The glass board is open to everyone: the gate and its word are gone, and the toggle shows under the flat boards at every table and on the told game

The flat boards stay the default; the glass board is a tap away.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx
This commit is contained in:
Eric WagonerandClaude Fable 5.1 committed 2026-10-27 12:54:54 -04:00
1 parent 9a43952ad1
commit 5099cf67e7
5 files changed
+5 -49

No files matched your search

+3 -3
View File
@@ -34,9 +34,9 @@ tables, the Peanut Gallery, the ledgers and the ops tools are the kit's;
takes the same taps as the flat boards. The figures in `static/figures/` takes the same taps as the flat boards. The figures in `static/figures/`
are glTF, one per kind, with a `livery` material the view recolours per are glTF, one per kind, with a `livery` material the view recolours per
side; `docs/figures-brief.md` is what they were made from. three.js and side; `docs/figures-brief.md` is what they were made from. three.js and
the figures load only when the view is opened. Until the keeper opens it the figures load only when the view is opened. The flat boards are the
to everyone, the view shows only to a browser let in through default; the glass board is offered under them at the table and on the
`src/lib/glass.svelte.ts`. told game.
- The guide's figures are screenshots in `static/guide/`, taken from the - The guide's figures are screenshots in `static/guide/`, taken from the
running site at 960 pixels wide. running site at 960 pixels wide.
+1 -4
View File
@@ -14,7 +14,6 @@
} from '$lib/game'; } from '$lib/game';
import type { Room } from '$lib/net/room.svelte'; import type { Room } from '$lib/net/room.svelte';
import { prefs, reducedMotion } from '$lib/prefs.svelte'; import { prefs, reducedMotion } from '$lib/prefs.svelte';
import { glass } from '$lib/glass.svelte';
let { room }: { room: Room } = $props(); let { room }: { room: Room } = $props();
@@ -218,9 +217,7 @@
{/if} {/if}
{#if room.error}<p class="warning">{room.error}</p>{/if} {#if room.error}<p class="warning">{room.error}</p>{/if}
{#if glass.open} <p class="muted small glass-way"><button type="button" class="link plain" disabled={fetching} aria-busy={fetching} onclick={() => seeGlass(!onGlass)}>{fetching ? 'Fetching the glass board…' : onGlass ? 'Back to the flat boards' : 'See the glass board'}</button></p>
<p class="muted small glass-way"><button type="button" class="link plain" disabled={fetching} aria-busy={fetching} onclick={() => seeGlass(!onGlass)}>{fetching ? 'Fetching the glass board…' : onGlass ? 'Back to the flat boards' : 'See the glass board'}</button></p>
{/if}
{#if onGlass && Glass} {#if onGlass && Glass}
<Glass {g} {mySide} {selected} {targets} {reach} {shownSquares} {shownCaptured} {checkSquare} {calm} {tap} /> <Glass {g} {mySide} {selected} {targets} {reach} {shownSquares} {shownCaptured} {checkSquare} {calm} {tap} />
{:else} {:else}
-1
View File
@@ -8,7 +8,6 @@
import TallyBoard from './Tally.svelte'; import TallyBoard from './Tally.svelte';
import Preferences from './Preferences.svelte'; import Preferences from './Preferences.svelte';
import { prefs, setPref } from '$lib/prefs.svelte'; import { prefs, setPref } from '$lib/prefs.svelte';
import '$lib/glass.svelte';
import { allSeats, doubtSeat, forgetSeat, rememberSeat, ServerError, trustSeat } from '$lib/net/client'; import { allSeats, doubtSeat, forgetSeat, rememberSeat, ServerError, trustSeat } from '$lib/net/client';
import { api, NAME_MAX, playerName, rememberName, Room } from '$lib/net/room.svelte'; import { api, NAME_MAX, playerName, rememberName, Room } from '$lib/net/room.svelte';
import { unreadTalk } from '$lib/net/talk'; import { unreadTalk } from '$lib/net/talk';
-37
View File
@@ -1,37 +0,0 @@
// The glass board, the three-dimensional view of a game, is shown only to
// a browser that has been let in. A visit to any page with ?glass=<word>
// opens it for that browser, if the word's hash is the one kept here; the
// word itself is nowhere in the site. Until the keeper decides the view is
// ready for everyone, this is the whole of the gate.
const KEY = 'wyrm:glass';
const GATE = 'eec81ab3bd646536b3464bb0411aea9b2e92e6387c6a04ef15743191ac4d142f';
function remembered(): boolean {
try {
return localStorage.getItem(KEY) === '1';
} catch {
return false;
}
}
/** Whether this browser may open the glass board. */
export const glass = $state({ open: typeof localStorage !== 'undefined' && remembered() });
/** Let the browser in if the address carries the word, and remember it. */
export async function claimGlass(): Promise<void> {
if (glass.open || typeof location === 'undefined') return;
const word = new URLSearchParams(location.search).get('glass');
if (!word) return;
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(word));
const hex = Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');
if (hex !== GATE) return;
glass.open = true;
try {
localStorage.setItem(KEY, '1');
} catch {
// Opened for this visit only.
}
}
if (typeof location !== 'undefined') void claimGlass();
+1 -4
View File
@@ -7,7 +7,6 @@
import { narrate } from '$lib/game/chronicle'; import { narrate } from '$lib/game/chronicle';
import { notation, kingSquare, inCheck, movesFrom, legalMovesFrom, sideOf } from '$lib/game'; import { notation, kingSquare, inCheck, movesFrom, legalMovesFrom, sideOf } from '$lib/game';
import { prefs, reducedMotion } from '$lib/prefs.svelte'; import { prefs, reducedMotion } from '$lib/prefs.svelte';
import { glass } from '$lib/glass.svelte';
/** The glass board, loaded the first time it is asked for. */ /** The glass board, loaded the first time it is asked for. */
let Glass = $state<typeof import('$lib/components/Glass.svelte').default | null>(null); let Glass = $state<typeof import('$lib/components/Glass.svelte').default | null>(null);
@@ -79,9 +78,7 @@
<section class="stage"> <section class="stage">
<div class="boards"> <div class="boards">
{#if glass.open} <p class="muted small"><button type="button" class="link plain" disabled={fetching} aria-busy={fetching} onclick={() => seeGlass(!onGlass)}>{fetching ? 'Fetching the glass board…' : onGlass ? 'Back to the flat boards' : 'See the glass board'}</button></p>
<p class="muted small"><button type="button" class="link plain" disabled={fetching} aria-busy={fetching} onclick={() => seeGlass(!onGlass)}>{fetching ? 'Fetching the glass board…' : onGlass ? 'Back to the flat boards' : 'See the glass board'}</button></p>
{/if}
{#if onGlass && Glass} {#if onGlass && Glass}
<Glass {g} {shownSquares} {shownCaptured} {checkSquare} selected={inspected} {reach} {calm} {tap} /> <Glass {g} {shownSquares} {shownCaptured} {checkSquare} selected={inspected} {reach} {calm} {tap} />
{:else} {:else}