From bde68aa85cb15b8d23822719c66b7287f0c56d18 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Sun, 23 Aug 2026 10:49:35 -0400 Subject: [PATCH] Telepath's glimpse gets held up to the light MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The engine sent the caster a private handRevealedPrivate event carrying the victim's cards; the client logged the public line and dropped the private one on the floor. The stolen mind is now a modal — the cards fanned out, once, until dismissed — in the trap-notice pattern. Co-Authored-By: Claude Fable 5 --- packages/web/src/App.svelte | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 882e32d..efabcd3 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -54,10 +54,16 @@ let trapNotice = $state(null); /** A Ward sprung on YOU earns the same courtesy. */ let wardBite = $state<{ owner: string; amount: number } | null>(null); + /** A mind YOU read: the stolen glimpse arrives once, in an event — hold + * it up until dismissed, or it is lost to the scrollback. */ + let mindRead = $state<{ player: string; cards: CardInstance[] } | null>(null); function playFx(events: Parameters[0]) { if (!view) return; for (const e of events) { if (e.type === "trapSprung" && e.player === view.you && e.cardId) trapNotice = e.cardId; + if (e.type === "handRevealedPrivate" && e.visibleTo === view.you) { + mindRead = { player: e.player, cards: e.cards }; + } if (e.type === "wardSprung" && e.victim === view.you) { const dmg = events.find((d) => d.type === "damaged" && d.player === e.victim && d.source === "warded treasure"); wardBite = { owner: e.owner, amount: dmg?.type === "damaged" ? dmg.amount : 0 }; @@ -1332,6 +1338,28 @@ {/if} + {#if mindRead} +
+
+
{mindRead.player}'s mind, laid bare
+
+ {#each mindRead.cards as card (card.instanceId)} + (faqCardId = id)} /> + {/each} + {#if mindRead.cards.length === 0} +
Nothing — their thoughts are empty-handed.
+ {/if} +
+
+ {mindRead.cards.length > 0 + ? `${mindRead.cards.length} card${mindRead.cards.length === 1 ? "" : "s"} — commit them to memory; the glimpse does not linger.` + : ""} +
+ +
+
+ {/if} + {#if wardBite}
@@ -2260,6 +2288,16 @@ .mast-room { margin-left: auto; font-size: 0.85rem; color: #a49c86; } .mast-room b { color: #e9e1cb; letter-spacing: 0.12em; } .mast-audience { font-size: 0.85rem; color: #a49c86; white-space: nowrap; } + .mind-read { max-width: min(92vw, 46rem); } + .mind-cards { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + justify-content: center; + max-height: 60vh; + overflow-y: auto; + padding: 0.4rem 0; + } .gallery-note { font-size: 0.85rem; color: #8d8672; font-style: italic; padding: 0.4rem 0; } .gallery-row { margin-top: 0.35rem; text-align: center; } .gallery-row button:disabled { opacity: 0.4; cursor: default; }