Skip to content

Commit f8d149c

Browse files
committed
Various little tweaks
1 parent c494182 commit f8d149c

7 files changed

Lines changed: 137 additions & 5 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ PUBLIC_CORS_PROXY_URL=https://cors.isomorphic-git.org
66
# PUBLIC_SHARED_CACHE_URL=https://your-proxy.workers.dev
77

88
# Optional Umami analytics. Both are required to enable tracking.
9-
# PUBLIC_ANALYTICS_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
109
# PUBLIC_ANALYTICS_URL=https://umami.yourdomain.com
10+
# PUBLIC_ANALYTICS_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

docs/deploying.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ This creates a Pages project named `git-strata`. The actual deployments happen f
7070

7171
## Step 5: Set up the custom domain
7272

73-
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Workers & Pages > git-strata > Custom
73+
1. In the [Cloudflare dashboard](https://dash.cloudflare.com/), go to **Compute > Workers & Pages > git-strata > Custom
7474
domains**.
7575
2. Add your domain (for example, `gitstrata.yourdomain.com`).
7676
3. Cloudflare will auto-create the DNS record if your domain's DNS is managed by Cloudflare.

docs/stra-git-raphy.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
The landing logo thing is a **state machine with 6 phases** managing the interactive hover behavior:
2+
3+
1. **`initial`**: the CSS animation is playing (tig→git on page load). No hover reactions.
4+
2. **`showing-git`**: default state after the initial animation. Waiting for interaction. The mouse must leave and
5+
re-enter to arm the hover trigger (`needsFreshEnter` flag).
6+
3. **`hover-pending`**: mouse is over "git", 1-sec timer running. If the mouse leaves before 1s, cancels and goes back
7+
to `showing-git`.
8+
4. **`animating-to-tig`**: git→tig reverse animation running (2s). All hover events ignored.
9+
5. **`showing-tig`**: text reads "Stratigraphy". Hovering cancels the revert timer; leaving starts/restarts the 5-sec
10+
revert timer.
11+
6. **`animating-to-git`**: tig→git forward animation running (2s). All hover events ignored. On finish, goes to
12+
`showing-git` with `needsFreshEnter = true` (mouse must leave and re-enter).
13+
14+
The subsequent animations use the **Web Animations API** (`element.animate()`) to interpolate the registered
15+
`@property --tig-swap`, so the same CSS `sin()`/`cos()` transforms drive the circular path in both directions.
16+
`prefers-reduced-motion` is respected (animations become near-instant).

knip.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const config: KnipConfig = {
44
entry: ['src/routes/**/+*.{ts,svelte}', 'src/lib/worker/analyzer.worker.ts'],
55
project: ['src/**/*.{ts,svelte}'],
66
ignore: ['src/app.d.ts'],
7+
ignoreBinaries: ['wrangler'],
78
ignoreDependencies: ['@vitest/coverage-v8', 'buffer', 'tailwindcss']
89
};
910

scripts/check/checks/frontend-svelte-check.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ func RunSvelteCheck(ctx *CheckContext) (CheckResult, error) {
1818
}
1919

2020
// Check for warnings in output
21-
if strings.Contains(output, " warning") && !strings.Contains(output, "0 warnings") {
21+
lower := strings.ToLower(output)
22+
if strings.Contains(lower, " warning") && !strings.Contains(lower, "0 warnings") {
2223
return CheckResult{}, fmt.Errorf("svelte-check found warnings\n%s", indentOutput(output))
2324
}
2425

src/lib/components/PipelineProgress.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ ${statBlock}`;
406406
};
407407
</script>
408408

409+
<!-- Click-outside/Escape handler for dismissing info popups, not an interactive control -->
410+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
409411
<div
410412
class="strata-card p-5"
411413
role="region"

src/routes/+page.svelte

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@
8888
}
8989
});
9090
91-
// Measure tig character widths after fonts load, so the swap animation is pixel-perfect
91+
// Measure tig character widths after fonts load, so the swap animation is pixel-perfect.
92+
// Also listens for animationend to hand off from the initial CSS animation to JS control.
9293
$effect(() => {
9394
if (!tigGroupEl || !tigTEl || !tigIEl || !tigGEl) return;
9495
const group = tigGroupEl;
@@ -104,6 +105,110 @@
104105
group.style.setProperty('--tig-dg', `${(wt + wi) / fontSize}em`);
105106
group.style.setProperty('--tig-di', `${(wg - wt) / fontSize}em`);
106107
});
108+
group.addEventListener('animationend', handleTigAnimationEnd);
109+
return () => group.removeEventListener('animationend', handleTigAnimationEnd);
110+
});
111+
112+
// --- Tig↔git interactive hover swap ---
113+
type SwapPhase =
114+
| 'initial'
115+
| 'showing-git'
116+
| 'hover-pending'
117+
| 'animating-to-tig'
118+
| 'showing-tig'
119+
| 'animating-to-git';
120+
121+
let swapPhase: SwapPhase = 'initial';
122+
let swapNeedsFreshEnter = true;
123+
let swapIsHovering = false;
124+
let swapHoverTimer: ReturnType<typeof setTimeout> | undefined;
125+
let swapRevertTimer: ReturnType<typeof setTimeout> | undefined;
126+
let swapAnim: Animation | null = null;
127+
128+
const swapAnimate = (from: number, to: number, onDone: () => void) => {
129+
swapAnim?.cancel();
130+
swapAnim = null;
131+
if (!tigGroupEl) return;
132+
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
133+
tigGroupEl.style.setProperty('--tig-swap', String(from));
134+
swapAnim = tigGroupEl.animate([{ '--tig-swap': String(from) }, { '--tig-swap': String(to) }], {
135+
duration: reducedMotion ? 1 : 2000,
136+
easing: 'linear',
137+
fill: 'forwards'
138+
});
139+
swapAnim.addEventListener(
140+
'finish',
141+
() => {
142+
tigGroupEl?.style.setProperty('--tig-swap', String(to));
143+
swapAnim?.cancel();
144+
swapAnim = null;
145+
onDone();
146+
},
147+
{ once: true }
148+
);
149+
};
150+
151+
const swapStartRevertTimer = () => {
152+
if (swapRevertTimer) clearTimeout(swapRevertTimer);
153+
swapRevertTimer = setTimeout(() => {
154+
swapRevertTimer = undefined;
155+
swapPhase = 'animating-to-git';
156+
swapAnimate(0, 1, () => {
157+
swapPhase = 'showing-git';
158+
swapNeedsFreshEnter = swapIsHovering;
159+
});
160+
}, 5000);
161+
};
162+
163+
const handleTigAnimationEnd = (e: AnimationEvent) => {
164+
if (e.animationName !== 'tig-swap-anim' || swapPhase !== 'initial') return;
165+
if (!tigGroupEl) return;
166+
tigGroupEl.style.animation = 'none';
167+
tigGroupEl.style.setProperty('--tig-swap', '1');
168+
swapPhase = 'showing-git';
169+
swapNeedsFreshEnter = swapIsHovering;
170+
};
171+
172+
const handleTigMouseEnter = () => {
173+
swapIsHovering = true;
174+
if (swapPhase === 'showing-git' && !swapNeedsFreshEnter) {
175+
swapPhase = 'hover-pending';
176+
swapHoverTimer = setTimeout(() => {
177+
swapHoverTimer = undefined;
178+
swapPhase = 'animating-to-tig';
179+
swapAnimate(1, 0, () => {
180+
swapPhase = 'showing-tig';
181+
if (!swapIsHovering) swapStartRevertTimer();
182+
});
183+
}, 500);
184+
} else if (swapPhase === 'showing-tig') {
185+
if (swapRevertTimer) {
186+
clearTimeout(swapRevertTimer);
187+
swapRevertTimer = undefined;
188+
}
189+
}
190+
};
191+
192+
const handleTigMouseLeave = () => {
193+
swapIsHovering = false;
194+
if (swapPhase === 'showing-git') {
195+
swapNeedsFreshEnter = false;
196+
} else if (swapPhase === 'hover-pending') {
197+
if (swapHoverTimer) clearTimeout(swapHoverTimer);
198+
swapHoverTimer = undefined;
199+
swapPhase = 'showing-git';
200+
} else if (swapPhase === 'showing-tig') {
201+
swapStartRevertTimer();
202+
}
203+
};
204+
205+
// Cleanup swap timers/animation on unmount
206+
$effect(() => {
207+
return () => {
208+
if (swapHoverTimer) clearTimeout(swapHoverTimer);
209+
if (swapRevertTimer) clearTimeout(swapRevertTimer);
210+
swapAnim?.cancel();
211+
};
107212
});
108213
109214
const startTimer = (kind: 'clone' | 'process') => {
@@ -366,11 +471,18 @@
366471
<div class="relative py-4 text-center sm:py-6">
367472
<div class="strata-hero-lines"></div>
368473
<div class="relative">
474+
<!-- Decorative tig↔git animation easter egg, not an interactive control -->
475+
<!-- svelte-ignore a11y_no_static_element_interactions -->
369476
<!-- prettier-ignore -->
370477
<h1
371478
class="text-3xl font-bold tracking-tight text-[var(--color-text)] sm:text-4xl lg:text-5xl"
372479
style="font-family: var(--font-sans); letter-spacing: -0.025em;"
373-
>Stra<span class="tig-group" bind:this={tigGroupEl}><span class="tig-char tig-t text-[var(--color-accent)]" bind:this={tigTEl}>t</span><span class="tig-char tig-i text-[var(--color-accent)]" bind:this={tigIEl}>i</span><span class="tig-char tig-g text-[var(--color-accent)]" bind:this={tigGEl}>g</span></span>raphy for your code</h1>
480+
>Stra<span
481+
class="tig-group"
482+
bind:this={tigGroupEl}
483+
onmouseenter={handleTigMouseEnter}
484+
onmouseleave={handleTigMouseLeave}
485+
><span class="tig-char tig-t text-[var(--color-accent)]" bind:this={tigTEl}>t</span><span class="tig-char tig-i text-[var(--color-accent)]" bind:this={tigIEl}>i</span><span class="tig-char tig-g text-[var(--color-accent)]" bind:this={tigGEl}>g</span></span>raphy for your code</h1>
374486
<p
375487
class="mx-auto mt-4 max-w-lg text-sm leading-relaxed text-[var(--color-text-secondary)] sm:text-base"
376488
style="font-family: var(--font-sans);"

0 commit comments

Comments
 (0)