Skip to content

Commit 167d06a

Browse files
committed
more contgrollers and terminal transitions
1 parent 5c477ba commit 167d06a

17 files changed

Lines changed: 693 additions & 108 deletions

src/app.css

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,6 @@
1111
@theme {
1212
--font-sans: "JetBrains Mono", sans-serif;
1313
--font-hebrew: "Noto Sans Hebrew", sans-serif;
14-
--animate-infinite-pan: infinite-pan 60s linear infinite;
15-
@keyframes infinite-pan {
16-
0% {
17-
pattern-transform: translate(0, 0);
18-
}
19-
100% {
20-
pattern-transform: translate(250px, 250px);
21-
}
22-
}
2314
}
2415
* {
2516
box-sizing: border-box;

src/lib/components/features/SkillsSection.svelte

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import WiiContriller from "$lib/icons/ryan-beck-wii-controller.svg?component";
1111
import NESController from "$lib/icons/ryan-beck-nes-controller.svg?component";
1212
import NSFController from "$lib/icons/ryan-beck-n64-controller.svg?component";
13+
import PSVita from "$lib/icons/ryan-beck-psvita-controller.svg?component";
14+
import GBSP from "$lib/icons/ryan-beck-sp-controller.svg?component";
1315
// Logos
1416
import GodotLogoLight from "$lib/assets/godot_logo_vertical_color_light.svg?component";
1517
import GodotLogoDark from "$lib/assets/godot_logo_vertical_color_dark.svg?component";
@@ -23,6 +25,8 @@
2325
WiiContriller,
2426
NESController,
2527
NSFController,
28+
PSVita,
29+
GBSP,
2630
];
2731
</script>
2832

@@ -36,19 +40,19 @@
3640
<SkillCard title={$t("skills.engines")} theme="mauve">
3741
<DecoBG
3842
icons={controllerIcons}
39-
scale={40}
40-
spacing={40}
43+
scale={60}
44+
spacing={30}
4145
rotation={-15} />
4246
<div class="flex flex-wrap items-center justify-center gap-4 p-4">
4347
<SkillIcon
44-
lightIcon={UnityLogoLight}
45-
darkIcon={UnityLogoDark}
48+
darkIcon={UnityLogoLight}
49+
lightIcon={UnityLogoDark}
4650
alt="Unity Logo"
4751
delay={300} />
4852

4953
<SkillIcon
50-
lightIcon={GodotLogoLight}
51-
darkIcon={GodotLogoDark}
54+
darkIcon={GodotLogoLight}
55+
lightIcon={GodotLogoDark}
5256
alt="Godot Logo" />
5357
</div>
5458
</SkillCard>

src/lib/components/features/terminal/ProjectView.svelte

Lines changed: 106 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,63 +2,126 @@
22
import type { Project } from "$lib/data/projects";
33
import { closeProject } from "$lib/stores/terminalStore";
44
import GlitchText from "../../ui/GlitchText.svelte";
5-
import { fade } from "svelte/transition";
6-
75
export let project: Project;
86
</script>
97

10-
<div transition:fade>
11-
<header class="mb-4 pb-2">
8+
<div class="project-view flex flex-col h-full max-h-full min-h-0">
9+
<header class="mb-4 pb-2 flex-shrink-0">
1210
<p>> cat {project.title}.{project.extension}</p>
1311
<div class="ascii-hr mt-2"></div>
1412
</header>
1513

16-
<div class="grid grid-cols-1 md:grid-cols-[1fr_auto_1fr] md:gap-x-4">
17-
<div class="visual-container mb-8 md:mb-0">
18-
{#if project.visual.type === "image"}
19-
<img
20-
src={project.visual.src}
21-
alt={project.title}
22-
class="w-full h-auto border-2 border-ctp-surface1 p-1" />
23-
{:else if project.visual.type === "video"}
24-
<video
25-
controls
26-
class="w-full h-auto border-2 border-ctp-surface1 p-1">
27-
<source src={project.visual.src} type="video/mp4" />
28-
<track
29-
kind="captions"
30-
src={project.visual.src.replace(".mp4", ".vtt")}
31-
srclang="en"
32-
label="English" />
33-
Your browser does not support the video tag.
34-
</video>
35-
{/if}
36-
</div>
14+
<main class="flex-grow overflow-auto min-h-0">
15+
<div class="grid grid-cols-1 md:grid-cols-[1fr_auto_1fr] md:gap-x-4">
16+
<div class="visual-container mb-8 md:mb-0">
17+
{#if project.visual && project.visual.src}
18+
{#if project.visual.type === "image"}
19+
<img
20+
src={project.visual.src}
21+
alt={project.title}
22+
class="w-full h-auto border-2 border-ctp-surface1 p-1"
23+
on:error={(e) => {
24+
(
25+
e.currentTarget as HTMLImageElement
26+
).style.display = "none";
27+
console.warn(
28+
`Image failed to load: ${project.visual.src}`,
29+
);
30+
}} />
31+
{:else if project.visual.type === "video"}
32+
<video
33+
controls
34+
class="w-full h-auto border-2 border-ctp-surface1 p-1"
35+
on:error={() =>
36+
console.warn(
37+
`Video failed to load: ${project.visual.src}`,
38+
)}>
39+
<source src={project.visual.src} type="video/mp4" />
40+
<track
41+
kind="captions"
42+
src={project.visual.src.replace(".mp4", ".vtt")}
43+
srclang="en"
44+
label="English" />
45+
Your browser does not support the video tag.
46+
</video>
47+
{:else if project.visual.type === "embed"}
48+
<div
49+
class="aspect-video w-full border-2 border-ctp-surface1 p-1">
50+
<iframe
51+
src={project.visual.src}
52+
class="w-full h-full"
53+
frameborder="0"
54+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
55+
allowfullscreen
56+
title={project.title}
57+
on:load={() =>
58+
console.log("Embed iframe loaded")}
59+
on:error={() =>
60+
console.warn(
61+
`Embed failed to load: ${project.visual.src}`,
62+
)}></iframe>
63+
</div>
64+
{:else}
65+
<p class="text-sm italic text-ctp-subtext0">
66+
Unknown visual type: {project.visual.type}
67+
</p>
68+
{/if}
69+
{:else}
70+
<p class="text-sm italic text-ctp-subtext0">
71+
No visual available for this project.
72+
</p>
73+
{/if}
74+
</div>
3775

38-
<div class="hidden md:flex flex-col items-center text-ctp-surface1">
39-
<span>|</span><span>|</span><span>|</span><span>|</span><span
40-
>|</span
41-
><span>|</span><span>|</span><span>|</span><span>|</span><span
42-
>|</span
43-
><span>|</span><span>|</span><span>|</span><span>|</span><span
44-
>|</span>
45-
</div>
76+
<div class="hidden md:flex flex-col items-center text-ctp-surface1">
77+
<span>|</span><span>|</span><span>|</span><span>|</span><span
78+
>|</span
79+
><span>|</span><span>|</span><span>|</span><span>|</span><span
80+
>|</span
81+
><span>|</span><span>|</span><span>|</span><span>|</span><span
82+
>|</span>
83+
</div>
4684

47-
<div class="details-container">
48-
<h3 class="text-2xl mb-2">
49-
<GlitchText text={project.title} color="sky" size="2xl" />
50-
</h3>
51-
<p class="text-ctp-text mb-4 whitespace-pre-wrap">
52-
{project.description}
53-
</p>
85+
<div class="details-container">
86+
<h3 class="text-2xl mb-2">
87+
<GlitchText text={project.title} color="sky" size="2xl" />
88+
</h3>
89+
<p class="text-ctp-text mb-4 whitespace-pre-wrap">
90+
{project.description}
91+
</p>
92+
</div>
5493
</div>
55-
</div>
94+
</main>
5695

57-
<footer class="mt-8 pt-4">
58-
<div class="ascii-hr"></div>
96+
<footer class="mt-4 pt-4 flex-shrink-0 border-t border-ctp-surface1">
5997
<button
6098
on:click={closeProject}
6199
class="text-ctp-red hover:bg-ctp-red hover:text-ctp-base p-1"
62100
>[EXIT]</button>
63101
</footer>
64102
</div>
103+
104+
<style>
105+
/* Ensure the project-view container fills parent and sets up flex layout */
106+
.project-view {
107+
/* height handled by parent container (set h-full on parent) */
108+
display: flex;
109+
flex-direction: column;
110+
/* min-h-0 required to allow flex children with overflow to shrink */
111+
min-height: 0;
112+
max-height: 100%;
113+
}
114+
115+
main {
116+
/* This allows main to grow and scroll inside flex container */
117+
flex-grow: 1;
118+
overflow-y: auto;
119+
min-height: 0; /* essential for correct overflow inside flex */
120+
}
121+
122+
footer {
123+
/* fixed height footer, flex-shrink: 0 keeps it visible */
124+
flex-shrink: 0;
125+
border-top: 1px solid var(--ctp-surface1);
126+
}
127+
</style>

src/lib/components/features/terminal/Terminal.svelte

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import BootSequence from "./BootSequence.svelte";
55
import FileExplorer from "./FileExplorer.svelte";
66
import ProjectView from "./ProjectView.svelte";
7+
import TerminalReveal from "./TerminalReveal.svelte";
8+
79
import { projects } from "$lib/data/projects";
810
911
let terminalElement: HTMLDivElement;
@@ -33,8 +35,12 @@
3335
{:else if $terminalStore.state === "booting"}
3436
<BootSequence />
3537
{:else if $terminalStore.state === "listing_files"}
36-
<FileExplorer {projects} />
38+
<TerminalReveal>
39+
<FileExplorer {projects} />
40+
</TerminalReveal>
3741
{:else if $terminalStore.state === "viewing_project" && $terminalStore.activeProject}
38-
<ProjectView project={$terminalStore.activeProject} />
42+
<TerminalReveal>
43+
<ProjectView project={$terminalStore.activeProject} />
44+
</TerminalReveal>
3945
{/if}
4046
</div>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script lang="ts">
2+
export let lineHeight = 24; // height of one "line", adjust to match your font
3+
export let lines = 20; // number of visual lines (used only for animation steps)
4+
export let duration = 1000; // total animation time
5+
</script>
6+
7+
<div
8+
class="flex flex-col overflow-hidden relative h-full w-full"
9+
style="
10+
--lineHeight: {lineHeight}px;
11+
--lines: {lines};
12+
--duration: {duration}ms;
13+
">
14+
<div class="scrolling-content overflow-auto flex-grow">
15+
<slot />
16+
</div>
17+
</div>
18+
19+
<style>
20+
.scrolling-content {
21+
animation: scrollUp var(--duration) steps(var(--lines)) forwards;
22+
will-change: transform;
23+
height: 100%;
24+
}
25+
26+
@keyframes scrollUp {
27+
from {
28+
transform: translateY(calc(var(--lineHeight) * var(--lines)));
29+
}
30+
to {
31+
transform: translateY(0);
32+
}
33+
}
34+
</style>

src/lib/components/ui/SVGComponentsPattern.svelte

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,65 @@
22
import type { Component } from "svelte";
33
import type { SVGAttributes } from "svelte/elements";
44
5-
// --- TYPE DEFINITIONS ---
6-
// This is the correct, specific type for a Svelte component generated by an SVG plugin.
75
type SvgComponent = Component<SVGAttributes<SVGSVGElement>>;
8-
// --- PROPS ---
9-
/** An array of imported Svelte SVG components for the pattern. */
6+
107
export let icons: SvgComponent[] = [];
11-
/** The size of each icon in the pattern. */
12-
export let scale: number = 50;
13-
/** The gap between pattern tiles. A larger value means lower density. */
14-
export let spacing: number = 100;
15-
/** The Tailwind CSS TEXT color class for the icons. */
16-
export let color: string = "text-ctp-surface1 dark:text-ctp-surface2";
17-
/** The overall opacity of the pattern. */
18-
/** The rotation angle for each icon in degrees. */
19-
export let rotation: number = -15;
8+
export let scale = 50;
9+
export let spacing = 100;
10+
export let rotation = -15;
11+
export let color = "text-ctp-surface2 dark:text-ctp-surface0";
12+
13+
// Unique ID for pattern
14+
const patternId = `pattern-${Math.random().toString(36).slice(2, 9)}`;
2015
21-
// --- CALCULATIONS ---
22-
// A unique ID for the pattern to prevent conflicts if you use it multiple times.
23-
const patternId = `master-pattern-${Math.random().toString(36).substring(7)}`;
24-
// The full size of one pattern tile.
25-
$: patternSize = scale + spacing;
16+
$: count = icons.length;
17+
$: cols = Math.ceil(Math.sqrt(count));
18+
$: rows = Math.ceil(count / cols);
19+
$: cells = rows * cols;
20+
21+
// Pattern tile dimensions
22+
$: tileWidth = cols * (scale + spacing);
23+
$: tileHeight = rows * (scale + spacing);
2624
</script>
2725

2826
{#if icons.length > 0}
29-
<div class="absolute inset-0 z-0 {color}" aria-hidden="true">
30-
<svg width="100%" height="100%">
27+
<div
28+
class="absolute inset-0 pointer-events-none {color}"
29+
aria-hidden="true">
30+
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
3131
<defs>
32-
{#each icons as icon, i}
33-
<symbol id="pattern-icon-{patternId}-{i}">
34-
<svelte:component this={icon} />
32+
{#each icons as Icon, i}
33+
<symbol
34+
id="icon-{patternId}-{i}"
35+
viewBox="0 0 24 24"
36+
preserveAspectRatio="xMidYMid meet">
37+
<Icon />
3538
</symbol>
3639
{/each}
3740

3841
<pattern
3942
id={patternId}
4043
patternUnits="userSpaceOnUse"
41-
width={patternSize}
42-
height={patternSize}>
44+
width={tileWidth}
45+
height={tileHeight}>
4346
<animateTransform
44-
attributeType="xml"
4547
attributeName="patternTransform"
4648
type="translate"
4749
from="0 0"
48-
to="{patternSize} {patternSize}"
49-
begin="0"
50-
dur="10s"
50+
to="{tileWidth} {tileHeight}"
51+
dur="20s"
5152
repeatCount="indefinite" />
52-
{#each icons as _, i}
53+
54+
{#each Array(cells) as _, i}
5355
<use
54-
href="#pattern-icon-{patternId}-{i}"
55-
x={(i % 2) * (patternSize / 2)}
56-
y={Math.floor(i / 2) * (patternSize / 2)}
56+
href="#icon-{patternId}-{i % icons.length}"
57+
x={(i % cols) * (scale + spacing)}
58+
y={Math.floor(i / cols) * (scale + spacing)}
5759
width={scale}
5860
height={scale}
59-
transform="rotate({rotation}, {(i % 2) *
60-
(patternSize / 2) +
61-
scale / 2}, {Math.floor(i / 2) *
62-
(patternSize / 2) +
63-
scale / 2})"
61+
transform="rotate({rotation},
62+
{(i % cols) * (scale + spacing) + scale / 2},
63+
{Math.floor(i / cols) * (scale + spacing) + scale / 2})"
6464
fill="currentColor" />
6565
{/each}
6666
</pattern>
@@ -72,5 +72,5 @@
7272
{/if}
7373

7474
<div class="relative z-10">
75-
<slot></slot>
75+
<slot />
7676
</div>

0 commit comments

Comments
 (0)