diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 4998bc8..bef894a 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -1497,6 +1497,11 @@ onchange={(e) => setPref("instantReplay", e.currentTarget.checked)} /> Instant replay: notable chronicle lines offer a 👁 that relives the turn + + setPref("handLeft", e.currentTarget.checked)} /> + Hand at the left: your cards stand in a column beside the board (wide screens) + setPref("cautions", e.currentTarget.checked)} /> @@ -1986,7 +1991,7 @@ {:else if view} - + {#if prefs.liveFp && view.you && !net.spectating} @@ -2249,7 +2254,6 @@ {/if} - {#if ambushVia} @@ -2558,6 +2562,7 @@ {/each} + {/if} {/if} @@ -3036,6 +3041,32 @@ min-height: 0; } .board-zone { display: flex; align-items: flex-start; justify-content: center; min-width: 0; } + /* Hand at the left: the cards stand in their own column beside the + * board while the hint strips and action buttons stay beneath it — + * the strip dissolves into the grid so its children place themselves. + * Phones keep the bottom hand; the one-column flow re-orders anyway. */ + @media (min-width: 901px) { + .game.hand-left { + grid-template-columns: minmax(9.6rem, 10.4rem) minmax(340px, 1fr) minmax(250px, 330px); + } + .game.hand-left .table-edge { display: contents; } + .game.hand-left .table-edge > * { grid-column: 2; min-width: 0; } + .game.hand-left .table-edge > .hand { + grid-column: 1; + grid-row: 1 / span 6; + flex-direction: column; + align-items: center; + align-self: start; + position: sticky; + top: 0.5rem; + max-height: calc(100dvh - 5rem); + overflow-y: auto; + overflow-x: visible; + padding: 0.3rem 0.3rem 0.9rem; + } + .game.hand-left .board-zone { grid-column: 2; grid-row: 1; } + .game.hand-left .paper-rail { grid-column: 3; grid-row: 1; } + } /* The first-person pane wears exactly the board's width, right above * it: the stack shrink-wraps to the board, and width:0 + min-width * keeps the pane out of the intrinsic sizing so the BOARD alone @@ -3220,6 +3251,7 @@ .moment-eye:hover { opacity: 1; } .table-edge { + grid-column: 1 / -1; margin-top: 0.9rem; display: flex; flex-direction: column; diff --git a/packages/web/src/prefs.svelte.ts b/packages/web/src/prefs.svelte.ts index d06ad1c..555e30c 100644 --- a/packages/web/src/prefs.svelte.ts +++ b/packages/web/src/prefs.svelte.ts @@ -22,6 +22,9 @@ export interface Prefs { instantReplay: boolean; /** The live first-person pane above the board during play. */ liveFp: boolean; + /** The hand of cards stands in a column at the board's left instead of + * lying along the bottom (wide screens only). */ + handLeft: boolean; } const KEY = "wizwar-prefs"; @@ -29,7 +32,7 @@ const KEY = "wizwar-prefs"; function load(): Prefs { const fallback: Prefs = { art: "photo", autoGrab: false, flourishes: true, wizardName: "", color: null, - cautions: true, botTier: "adept", instantReplay: true, liveFp: false, + cautions: true, botTier: "adept", instantReplay: true, liveFp: false, handLeft: false, }; try { const raw = localStorage.getItem(KEY); @@ -45,6 +48,7 @@ function load(): Prefs { botTier: p.botTier === "apprentice" || p.botTier === "archmage" ? p.botTier : "adept", instantReplay: p.instantReplay !== false, liveFp: p.liveFp === true, + handLeft: p.handLeft === true, }; } catch { return fallback;