From 313837eb21503a9fbefdf609ce6bb9f0e8b08aa6 Mon Sep 17 00:00:00 2001 From: "claude (blind_chess)" Date: Mon, 18 May 2026 20:45:40 -0400 Subject: [PATCH] feat(client): wire the phantom opponent-model layer into the game view Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/client/src/lib/Game.svelte | 67 ++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/packages/client/src/lib/Game.svelte b/packages/client/src/lib/Game.svelte index 7b2392d..6697d1b 100644 --- a/packages/client/src/lib/Game.svelte +++ b/packages/client/src/lib/Game.svelte @@ -5,6 +5,10 @@ import ModeratorPanel from './ModeratorPanel.svelte'; import CaptureTally from './CaptureTally.svelte'; import PromotionDialog from './PromotionDialog.svelte'; + import PhantomPalette from './PhantomPalette.svelte'; + import { pieceGlyph } from './pieces.js'; + import { phantoms } from './stores/phantoms.svelte.js'; + import { phantomDrag } from './stores/phantom-drag.svelte.js'; import type { PromotionType, Square } from '@blind-chess/shared'; interface Props { gameId: string; } @@ -92,6 +96,37 @@ } return 'Opponent thinking'; }); + + const oppColor = $derived<'w' | 'b'>(game.state.you === 'w' ? 'b' : 'w'); + + // Phantom layer is blind-mode-only and shown only during active play. + const phantomLayerEnabled = $derived( + game.state.mode === 'blind' && game.state.gameStatus === 'active', + ); + + // The piece type currently being dragged (for the floating ghost), or null. + const dragGhost = $derived.by(() => { + const a = phantomDrag.state.active; + return a && phantomDrag.state.moved ? a.type : null; + }); + + // Load the phantom layer once `you` is known (blind games only). + let phantomsLoaded = $state(false); + $effect(() => { + if (phantomsLoaded) return; + const you = game.state.you; + if (you && game.state.mode === 'blind') { + untrack(() => phantoms.loadForGame(gameId, you)); + phantomsLoaded = true; + } + }); + + // Drop the phantom layer when the game ends. + $effect(() => { + if (game.state.gameStatus === 'finished') { + untrack(() => phantoms.clearForGame(gameId)); + } + });
@@ -129,11 +164,16 @@ toMove={game.state.view.toMove} mode={game.state.mode ?? 'blind'} highlightingEnabled={game.state.highlightingEnabled} + phantoms={phantomLayerEnabled ? phantoms.state.phantoms : {}} + phantomsEnabled={phantomLayerEnabled} armedSquare={armedSquare} touchedSquare={game.state.touchedPiece} {onArm} {onCommit} /> + {#if phantomLayerEnabled} + + {/if}