From 85086e31b212a5e6dcab23feec5f14319aa0f83e Mon Sep 17 00:00:00 2001 From: Kevin Riehl Date: Sun, 9 Aug 2026 02:01:53 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20audit=20phase=206=20(T4)=20=E2=80=94=20?= =?UTF-8?q?node=20allocation,=20the=20Path=20of=20Exile=20payoff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- src/lib/components/BuildBar.svelte | 81 +++++++++++++++++++ src/lib/components/NodeModal.svelte | 43 ++++++++++- src/lib/components/SkillTree.svelte | 85 +++++++++++++++++--- src/lib/pixi/allocation.ts | 40 ++++++++++ src/lib/utils/allocation.test.ts | 116 ++++++++++++++++++++++++++++ src/lib/utils/allocation.ts | 93 ++++++++++++++++++++++ 6 files changed, 448 insertions(+), 10 deletions(-) create mode 100644 src/lib/components/BuildBar.svelte create mode 100644 src/lib/pixi/allocation.ts create mode 100644 src/lib/utils/allocation.test.ts create mode 100644 src/lib/utils/allocation.ts diff --git a/src/lib/components/BuildBar.svelte b/src/lib/components/BuildBar.svelte new file mode 100644 index 0000000..59740c9 --- /dev/null +++ b/src/lib/components/BuildBar.svelte @@ -0,0 +1,81 @@ + + +{#if count > 0} +
+ {count} allocated + + +
+{/if} + + diff --git a/src/lib/components/NodeModal.svelte b/src/lib/components/NodeModal.svelte index 10a3674..21821c8 100644 --- a/src/lib/components/NodeModal.svelte +++ b/src/lib/components/NodeModal.svelte @@ -6,8 +6,13 @@ node, visible, onClose, - onDismiss + onDismiss, + allocated = false, + onToggleAllocate }: { + allocated?: boolean; + /** Allocate/remove this node and its path. */ + onToggleAllocate?: (id: string) => void; node: TreeNode; visible: boolean; /** Explicit close (✕ button) — always immediate. */ @@ -84,6 +89,19 @@ > + {#if onToggleAllocate} + + + {/if} +