The site borrowed PoE's visual language — passive-tree layout, plasma connections, an RPG stats panel — without any of its mechanic. Nodes were purely decorative. This is the interaction the concept was promising. - New utils/allocation.ts: path-to-root walking, allocate/deallocate, edge state, and URL encode/decode. Pure, 17 unit tests. - Allocation is path-connected like the game: allocating a leaf brings its whole path, and deallocating a node removes its descendants so nothing is ever orphaned. pathToRoot is cycle-safe, and a test asserts every real node reaches the origin. - New pixi/allocation.ts: the path reads as "lit" by dimming everything else rather than brightening the selection — the tree is already near its brightness ceiling, so recession is the only headroom. Nodes dim via alpha (nothing tweens it after the entrance); connections dim via tint, because their alpha is driven by the permanent plasma pulse and writing to it would fight the animation every frame. - Shareable via ?build=. Encoded as node ids, not indices: a bitset would be shorter, but any edit to data.ts would silently re-point every previously shared link at the wrong nodes. Decoding re-derives each path, so a hand-edited or partially-stale link can't produce a broken build, and unknown ids are dropped rather than throwing. - Allocation hangs off a button in the modal rather than the node click, which already opens that modal. BuildBar shows the count with share/reset; share falls back to rewriting the address bar when the clipboard is blocked. Two bugs found while verifying: - Both deep links (?build and ?tour) had ended up nested inside the first-visit coach-hint setTimeout, so a shared link did nothing at all for a returning visitor and waited out the whole intro for everyone else — backwards, since a shared link is precisely the cold-arrival case. Hoisted to run immediately, unconditionally. A ?tour link now also skips the intro. - A restored ?build= was wiped by the entrance, which tweens every node's alpha to 1 after the deep link applies. The result was a full-brightness tree with a counter claiming 4 allocated. Now re-applied once the entrance finishes. Verified in the production build: allocating TypeScript lights origin -> Skills -> Languages -> TypeScript and dims the rest; the counter reads 4; the generated link restores exactly that state on a cold load; Reset clears it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
82 lines
1.7 KiB
Svelte
82 lines
1.7 KiB
Svelte
<script lang="ts">
|
|
// Controls for the allocated path: how many nodes, share, reset.
|
|
const {
|
|
count = 0,
|
|
onReset,
|
|
onShare
|
|
}: {
|
|
count?: number;
|
|
onReset: () => void;
|
|
onShare: () => Promise<boolean> | boolean;
|
|
} = $props();
|
|
|
|
let copied = $state(false);
|
|
|
|
async function share() {
|
|
copied = await onShare();
|
|
if (copied) setTimeout(() => (copied = false), 2000);
|
|
}
|
|
</script>
|
|
|
|
{#if count > 0}
|
|
<div class="build-bar" role="status" aria-live="polite">
|
|
<span class="count"><strong>{count}</strong> allocated</span>
|
|
<button onclick={share}>{copied ? '✓ Link copied' : 'Share build'}</button>
|
|
<button class="reset" onclick={onReset}>Reset</button>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.build-bar {
|
|
position: fixed;
|
|
top: var(--space-5);
|
|
right: var(--space-5);
|
|
z-index: var(--z-panel-toggle);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
padding: var(--space-2) var(--space-3);
|
|
border: 1px solid rgba(255, 215, 0, 0.4);
|
|
border-radius: var(--radius-pill);
|
|
background: rgba(10, 10, 20, 0.85);
|
|
font-size: 0.78rem;
|
|
}
|
|
|
|
.count {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.count strong {
|
|
color: var(--color-gold);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
button {
|
|
padding: 0.25rem 0.6rem;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
border-radius: var(--radius-pill);
|
|
background: transparent;
|
|
color: var(--text-primary);
|
|
font: inherit;
|
|
font-size: 0.72rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
button:hover {
|
|
border-color: var(--color-gold);
|
|
}
|
|
|
|
.reset {
|
|
border-color: transparent;
|
|
color: var(--text-subtle);
|
|
}
|
|
|
|
@media (max-width: 767px) {
|
|
.build-bar {
|
|
top: auto;
|
|
bottom: calc(var(--space-5) + 3.2rem);
|
|
right: var(--space-2);
|
|
}
|
|
}
|
|
</style>
|