Files
wizwar6e/packages/web/src/TokenArt.svelte
T
Eric WagonerandClaude Fable 5 d2b40ec6f8 Credibility pass: the seams get sanded
Scoped to everything since the last pass (3308850). Three blind
reviews, every finding verified before touching anything.

Confirmed and fixed: two identical comment-splitting insertions left
doc comments orphaned from their fields (net and local alike); a
51-line CSS fossil of the pre-extraction inline effects survived in
Board.svelte; the rev-13 miss-roll test asserted tautologies while
its comment claimed a check the code never made — it now proves the
die was consumed, and the skeleton is no longer returned as trollId;
the sprite registry's `as never` silently disabled the completeness
its annotation advertised (now a mapped type, one cast at the
dispatch seam); a dead ternary guarded a union that doesn't exist;
Bolt carried a duplicate .fork rule from a color iteration; fxTtl
contradicted three sprites' real animation lengths; the permanence
sentinel was reinvented as a magic 9000 (the engine now exports
isPermanentDuration); CELL was declared thrice (fx.ts now imports
it); App and Replay ran two divergent fx schedulers (one scheduleFx
now, cancellable — stale flourishes can no longer fire after leaving
a game); TokenArt retried missing files forever; the anti-anti
escape guards merge with the gate asymmetry explained; wall-of-fire's
rev-12 carve-out is marked; overLimit ignored a displayed BRAINSTONE
(bots over-discarded by two); botRemark's header mis-stated its own
branches; deliverGold fired on any drop, not a home-base delivery;
escape and win banter never fired from the steps that carry them.

Rejected: "as a human would" (house voice); FxGallery's dev-harness
framing (trimmed one plea, kept the facts).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 12:12:03 -04:00

68 lines
2.3 KiB
Svelte

<script lang="ts" module>
import { SvelteMap } from "svelte/reactivity";
// SVG art must be INLINED to stay vector: an svg referenced through an
// <image> element is rasterized once at its layout size in user units and
// scales as a bitmap — blurry the moment the board zooms. Fetched files
// are cached per URL, with their ids namespaced so 45 tokens' worth of
// filter/pattern defs cannot collide in one document.
type Entry = { vb: string; inner: string };
const cache = new SvelteMap<string, Entry | "pending" | "failed">();
function load(href: string): void {
if (cache.has(href)) return;
cache.set(href, "pending");
fetch(href)
.then((r) => r.text())
.then((text) => {
const ns = href.replace(/[^a-zA-Z0-9]/g, "");
const t = text
.replace(/id="([^"]+)"/g, (_, id) => `id="${id}-${ns}"`)
.replace(/url\(#([^)]+)\)/g, (_, id) => `url(#${id}-${ns})`)
.replace(/href="#([^"]+)"/g, (_, id) => `href="#${id}-${ns}"`);
const m = /<svg([^>]*)>([\s\S]*)<\/svg>/i.exec(t);
const vb = m ? (/viewBox="([^"]*)"/.exec(m[1]!)?.[1] ?? "0 0 144 150") : "0 0 144 150";
cache.set(href, { vb, inner: m?.[2] ?? "" });
})
// A failed fetch stays failed: deleting would retrigger the effect
// and hammer a missing file forever.
.catch(() => cache.set(href, "failed"));
}
</script>
<script lang="ts">
let {
href, x, y, width, height, cls = "", title = null,
}: {
href: string;
x: number;
y: number;
width: number;
height: number;
cls?: string;
title?: string | null;
} = $props();
const isSvg = $derived(href.endsWith(".svg"));
$effect(() => {
if (isSvg) load(href);
});
const entry = $derived(isSvg ? cache.get(href) : undefined);
</script>
{#if isSvg}
{#if entry && entry !== "pending" && entry !== "failed"}
<svg {x} {y} {width} {height} viewBox={entry.vb}
preserveAspectRatio="xMidYMid slice" class={cls}>
{#if title}<title>{title}</title>{/if}
<!-- eslint-disable-next-line svelte/no-at-html-tags — our own art files -->
{@html entry.inner}
</svg>
{/if}
{:else}
<image {href} {x} {y} {width} {height}
preserveAspectRatio="xMidYMid slice" class={cls}>
{#if title}<title>{title}</title>{/if}
</image>
{/if}