From 61f434a32db89bdc1b4cac411730364a5eaa73be Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 01:34:07 +0000 Subject: [PATCH 01/56] scene: two quick sky changes build the last sky; the Night drift test is exact A rebuild read the sky choice before awaiting its key and acted on it after, so Day then Dusk in quick succession left the Day scene live (the old test only counted disposals and passed with the Dusk scene never built). A rebuild now stands down if the choice moved while it awaited; that change has its own rebuild. The test asserts the last sky built is the last one chosen, is the live scene (a login reaches it), and that every other scene was disposed. The NIGHT_SKY_LIGHTEST drift test compared with a 1e-6 tolerance, so 0.081641 passed; it is now exact equality. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/index.test.ts | 21 +++++++++++++++++---- src/app/features/scene/index.ts | 2 ++ src/app/features/scene/skyTheme.test.ts | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/app/features/scene/index.test.ts b/src/app/features/scene/index.test.ts index 5d0e6410..6a29b33f 100644 --- a/src/app/features/scene/index.test.ts +++ b/src/app/features/scene/index.test.ts @@ -4,6 +4,7 @@ import { baked } from '../../../sky/baked'; import type { Scene } from './Scene'; import { startBackgroundScene } from './index'; import { setSkyClock, setSkyState } from './skyState'; +import { ridgeSkyFor } from './skyForState'; const { createRidgeScene, createScene } = vi.hoisted(() => ({ createRidgeScene: vi.fn(), @@ -155,16 +156,28 @@ describe('startBackgroundScene follows the sky state', () => { expect(createRidgeScene).toHaveBeenCalledOnce(); }); - it('keeps only the latest of two quick changes', async () => { + it('keeps only the latest of two quick changes: the last sky is built, the others are not left running', async () => { const scenes = [fakeScene(), fakeScene(), fakeScene()]; scenes.forEach((s) => createRidgeScene.mockReturnValueOnce(s)); const scene = await startBackgroundScene(config, caps, { canvas }); setSkyState('day'); - setSkyState('night'); + setSkyState('dusk'); await flush(); - expect(scenes[1].dispose).toHaveBeenCalledOnce(); - expect(scenes[2].dispose).not.toHaveBeenCalled(); + // The sky last built is dusk's, and it is the live scene: a login reaches it and nothing else. + const built = createRidgeScene.mock.calls.length; + expect(hex(createRidgeScene.mock.calls[built - 1][0].sky.top)).toBe( + hex(ridgeSkyFor('dusk').top) + ); + const live = scenes[built - 1]; + scene?.login(); + expect(live.login).toHaveBeenCalledOnce(); + scenes.filter((s) => s !== live).forEach((s) => expect(s.login).not.toHaveBeenCalled()); + // Every scene but the live one was disposed, the live one not yet. + scenes.slice(0, built).forEach((s) => { + expect(s.dispose).toHaveBeenCalledTimes(s === live ? 0 : 1); + }); scene?.dispose(); + expect(live.dispose).toHaveBeenCalledOnce(); }); it('leaves the ray-march scene alone: it has one fixed dusk', async () => { diff --git a/src/app/features/scene/index.ts b/src/app/features/scene/index.ts index 55966188..73377440 100644 --- a/src/app/features/scene/index.ts +++ b/src/app/features/scene/index.ts @@ -95,8 +95,10 @@ export async function startBackgroundScene( let generation = 0; // A rebuild is for a different sky ('auto' crossing a boundary, a new choice), not for a repeat of the one drawn. const rebuild = async () => { + // The choice can move while the key is awaited; that change has its own rebuild, so this one stands down. const next = getSkyState(); const key = await skyKey(config, next); + if (getSkyState() !== next) return; if ((key !== null && key === built) || disposed) return; generation += 1; const mine = generation; diff --git a/src/app/features/scene/skyTheme.test.ts b/src/app/features/scene/skyTheme.test.ts index 39b74fdc..53cf2873 100644 --- a/src/app/features/scene/skyTheme.test.ts +++ b/src/app/features/scene/skyTheme.test.ts @@ -92,7 +92,7 @@ describe('ink of the theme a state picks, on bare sky', () => { }); it('keeps the lightest Night sky the link was fitted to equal to the Night backdrop (no drift)', () => { - expect(Math.abs(NIGHT_SKY_LIGHTEST - skyBackdrop('night').lightest)).toBeLessThan(1e-6); + expect(NIGHT_SKY_LIGHTEST).toBe(skyBackdrop('night').lightest); }); it.each([ From 098e45f122d57b7f5041994b650e5a313d6baa35 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 01:56:34 +0000 Subject: [PATCH 02/56] scene: test a sky change landing 10-12 microtasks into a build The stale build must be disposed and must not become live: removing the stale-build scene?.dispose() or the generation check now fails the test. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/index.test.ts | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/app/features/scene/index.test.ts b/src/app/features/scene/index.test.ts index 6a29b33f..75e7b289 100644 --- a/src/app/features/scene/index.test.ts +++ b/src/app/features/scene/index.test.ts @@ -20,6 +20,12 @@ const flush = () => new Promise((r) => { setTimeout(r, 0); }); +// Resolves after `n` further turns of the microtask queue. +const microtasks = (n: number) => + Array.from({ length: n }).reduce>( + (p) => p.then(() => undefined), + Promise.resolve() + ); const fakeScene = () => ({ login: vi.fn(), dispose: vi.fn() }); const hex = (c: readonly number[]) => `#${c.map((v) => v.toString(16).padStart(2, '0')).join('')}`; @@ -180,6 +186,35 @@ describe('startBackgroundScene follows the sky state', () => { expect(live.dispose).toHaveBeenCalledOnce(); }); + it.each([10, 11, 12])( + 'a sky change %i microtasks into a build leaves only the newer build live; the stale one is disposed', + async (ticks) => { + const [a, b, c] = [fakeScene(), fakeScene(), fakeScene()]; + createRidgeScene.mockReturnValueOnce(a).mockReturnValueOnce(b).mockReturnValueOnce(c); + const onActive = vi.fn(); + const scene = await startBackgroundScene(config, caps, { canvas, onActive }); + setSkyState('day'); + await microtasks(ticks); + setSkyState('dusk'); + await flush(); + const built = createRidgeScene.mock.calls.length; + expect(hex(createRidgeScene.mock.calls[built - 1][0].sky.top)).toBe( + hex(ridgeSkyFor('dusk').top) + ); + // The newest build is live, every earlier one disposed exactly once, and only the live one is shown. + const live = [a, b, c][built - 1]; + scene?.login(); + expect(live.login).toHaveBeenCalledOnce(); + [a, b, c].slice(0, built).forEach((s) => { + expect(s.dispose).toHaveBeenCalledTimes(s === live ? 0 : 1); + if (s !== live) expect(s.login).not.toHaveBeenCalled(); + }); + expect(onActive).toHaveBeenCalledOnce(); + scene?.dispose(); + expect(live.dispose).toHaveBeenCalledOnce(); + } + ); + it('leaves the ray-march scene alone: it has one fixed dusk', async () => { const inner = fakeScene() as unknown as Scene; createScene.mockReturnValue(inner); From 6a93664813b61cc1f20efd1688c7a84cc7a1f02c Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 02:07:11 +0000 Subject: [PATCH 03/56] scene: sweep the mid-build test over microtask offsets 0..20 The change is no longer pinned to 10-12 microtasks; the test requires at least one offset to land on a build that is found stale and dropped, so a shift in the awaits inside create/skyKey cannot make it vacuous. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/index.test.ts | 69 +++++++++++++++++----------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/app/features/scene/index.test.ts b/src/app/features/scene/index.test.ts index 75e7b289..f54e456f 100644 --- a/src/app/features/scene/index.test.ts +++ b/src/app/features/scene/index.test.ts @@ -186,34 +186,47 @@ describe('startBackgroundScene follows the sky state', () => { expect(live.dispose).toHaveBeenCalledOnce(); }); - it.each([10, 11, 12])( - 'a sky change %i microtasks into a build leaves only the newer build live; the stale one is disposed', - async (ticks) => { - const [a, b, c] = [fakeScene(), fakeScene(), fakeScene()]; - createRidgeScene.mockReturnValueOnce(a).mockReturnValueOnce(b).mockReturnValueOnce(c); - const onActive = vi.fn(); - const scene = await startBackgroundScene(config, caps, { canvas, onActive }); - setSkyState('day'); - await microtasks(ticks); - setSkyState('dusk'); - await flush(); - const built = createRidgeScene.mock.calls.length; - expect(hex(createRidgeScene.mock.calls[built - 1][0].sky.top)).toBe( - hex(ridgeSkyFor('dusk').top) - ); - // The newest build is live, every earlier one disposed exactly once, and only the live one is shown. - const live = [a, b, c][built - 1]; - scene?.login(); - expect(live.login).toHaveBeenCalledOnce(); - [a, b, c].slice(0, built).forEach((s) => { - expect(s.dispose).toHaveBeenCalledTimes(s === live ? 0 : 1); - if (s !== live) expect(s.login).not.toHaveBeenCalled(); - }); - expect(onActive).toHaveBeenCalledOnce(); - scene?.dispose(); - expect(live.dispose).toHaveBeenCalledOnce(); - } - ); + // A second sky change `ticks` microtasks after the first. True when it landed mid-build: the build for the first + // change finished, was found stale and dropped (never shown), and only the newest sky was announced. + const changeAfter = async (ticks: number) => { + createRidgeScene.mockReset(); + setSkyState('auto'); + const [a, b, c] = [fakeScene(), fakeScene(), fakeScene()]; + createRidgeScene.mockReturnValueOnce(a).mockReturnValueOnce(b).mockReturnValueOnce(c); + const onActive = vi.fn(); + const scene = await startBackgroundScene(config, caps, { canvas, onActive }); + setSkyState('day'); + await microtasks(ticks); + setSkyState('dusk'); + await flush(); + const at = `at ${ticks} microtasks`; + const built = createRidgeScene.mock.calls.length; + expect(hex(createRidgeScene.mock.calls[built - 1][0].sky.top), at).toBe( + hex(ridgeSkyFor('dusk').top) + ); + // The newest build is live, every earlier one disposed exactly once, and only the live one is shown. + const live = [a, b, c][built - 1]; + scene?.login(); + expect(live.login, at).toHaveBeenCalledOnce(); + [a, b, c].slice(0, built).forEach((x) => { + expect(x.dispose, at).toHaveBeenCalledTimes(x === live ? 0 : 1); + if (x !== live) expect(x.login, at).not.toHaveBeenCalled(); + }); + scene?.dispose(); + expect(live.dispose, at).toHaveBeenCalledOnce(); + return built === 3 && onActive.mock.calls.length === 1; + }; + + it('a sky change at any point of a build leaves only the newer build live; the stale one is disposed', async () => { + // The offsets are swept, not pinned, so a shift in the awaits inside the build cannot move the change off the + // build: at least one offset must land mid-build, or the sweep proves nothing. + const offsets = Array.from({ length: 21 }, (_, i) => i); + const landed = await offsets.reduce>( + (p, k) => p.then(async (acc) => ((await changeAfter(k)) ? [...acc, k] : acc)), + Promise.resolve([]) + ); + expect(landed.length).toBeGreaterThan(0); + }); it('leaves the ray-march scene alone: it has one fixed dusk', async () => { const inner = fakeScene() as unknown as Scene; From de504f82863fbc9172d10b499b883185bbebd263 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 02:24:13 +0000 Subject: [PATCH 04/56] scene: say what the sweep's mid-build assertion means when it fails Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/index.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/features/scene/index.test.ts b/src/app/features/scene/index.test.ts index f54e456f..23c6b297 100644 --- a/src/app/features/scene/index.test.ts +++ b/src/app/features/scene/index.test.ts @@ -225,7 +225,9 @@ describe('startBackgroundScene follows the sky state', () => { (p, k) => p.then(async (acc) => ((await changeAfter(k)) ? [...acc, k] : acc)), Promise.resolve([]) ); - expect(landed.length).toBeGreaterThan(0); + // This can fail after a correct refactor that changes the await count in create/skyKey and moves every offset + // off the build. Then widen the range of offsets until some land again; do not weaken the assertion. + expect(landed.length, 'at least one offset must land mid-build').toBeGreaterThan(0); }); it('leaves the ray-march scene alone: it has one fixed dusk', async () => { From 91e93e6887ecd777f0ac5fe3cd0dff4540be286b Mon Sep 17 00:00:00 2001 From: Nibu Date: Tue, 10 Nov 2026 01:18:45 +0000 Subject: [PATCH 05/56] scene: Dusk floor at the link bound with Day's pale-blue lift, so the dark link clears AA Dusk's floor moves from 0.317 to 0.4744 (the dark link's bound 0.4554 plus the 0.019 margin Day keeps over its text bound), and its lift from rose #eba0b9 (luma 0.463, below the new floor) to Day's #e2f4ff. The ridge dusk stops are shaped into the same band, since the unshaped twilight dipped to 0.336. skyBackdrop('dusk').darkest becomes 0.460352; Dusk text 4.60 -> 6.59, link 3.17 -> 4.54. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/ridge/skyColour.ts | 5 ++-- src/app/features/scene/skyBand.test.ts | 2 +- src/app/features/scene/skyBand.ts | 13 ++++++--- src/app/features/scene/skyForState.test.ts | 34 +++++++++++++++++----- src/app/features/scene/skyForState.ts | 27 +++++++++++++---- src/app/features/scene/skyState.ts | 2 +- src/app/features/scene/skyTheme.test.ts | 12 ++++---- src/app/features/scene/skyTheme.ts | 8 ++--- 8 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/app/features/scene/ridge/skyColour.ts b/src/app/features/scene/ridge/skyColour.ts index 3a5147a4..7917d89e 100644 --- a/src/app/features/scene/ridge/skyColour.ts +++ b/src/app/features/scene/ridge/skyColour.ts @@ -1,8 +1,7 @@ // The one place the sky's colours come from. Void's `src/sky/sun.js` (`light({ time, lat, lon, // cloud_cover, precipitation })`, PR #20) is the intended source; until it lands this returns a -// fixed twilight: lavender zenith, rose mid-sky, orange horizon. Its darkest blend stays above the dusk -// floor of ../skyBand.ts (the dark ink of Noonday Sky needs a backdrop of at least 0.2983 luminance), which -// skyForState.test.ts sweeps. Replace the body, keep the signature: the scene calls it once, at startup, +// fixed twilight: lavender zenith, rose mid-sky, orange horizon. These are the raw stops: the scene draws them +// shaped into the dusk band of ../skyBand.ts (`ridgeSkyFor`, skyForState.ts), whose floor the link of Noonday Sky needs. Replace the body, keep the signature: the scene calls it once, at startup, // and bakes the result. export type Srgb = readonly [number, number, number]; diff --git a/src/app/features/scene/skyBand.test.ts b/src/app/features/scene/skyBand.test.ts index 5963fb79..e38f676a 100644 --- a/src/app/features/scene/skyBand.test.ts +++ b/src/app/features/scene/skyBand.test.ts @@ -23,7 +23,7 @@ describe('shapeToBand', () => { it('lifts black to the tint scaled in, not to grey', () => { const c = shapeToBand([0, 0, 0], SKY_BAND.dusk); expect(near(luma(c), SKY_BAND.dusk.floor)).toBe(true); - expect(c[0]).toBeGreaterThan(c[2]); // the dusk tint is rose + expect(c[2]).toBeGreaterThan(c[0]); // the dusk tint is Day's pale blue }); it('leaves a sky already in the band as it is, and is continuous at the floor', () => { diff --git a/src/app/features/scene/skyBand.ts b/src/app/features/scene/skyBand.ts index ac311081..a74784b3 100644 --- a/src/app/features/scene/skyBand.ts +++ b/src/app/features/scene/skyBand.ts @@ -1,12 +1,14 @@ // The luminance band each fixed sky state's drawn sky is shaped into, so the state's theme ink // clears 4.5:1 on bare sky (WCAG relative luminance, the `luminance` of src/sky/skyTokens.js). // -// day, dusk Noonday Sky's dark text ink #342d25 (L 0.0274) needs a backdrop of at least 0.2983. +// day Noonday Sky's dark text ink #342d25 (L 0.0274) needs a backdrop of at least 0.2983. // The floor is that text bound (0.30 as drawn); there is no ceiling, dark ink on light is fine. +// dusk The same ink, and its link #2a4f27 (L 0.0623), which needs a backdrop of at least 0.4554. The floor is +// the link bound (0.4744 as drawn), so text clears 4.5:1 with room (6.6) and the link clears too. // night Midnight Sky's light text ink #aeccff (L 0.594) needs a backdrop of at most 0.0931. // The baked night sky (0.058 to 0.077) is kept; the ceiling is the bound. // -// Link ink is not part of the band: see skyTheme.ts. +// Night's link ink is not part of the band: see skyTheme.ts. // // The terrain sky is a physical scatter function, so it is shaped downstream, on its final colour: // `shapeToBand` here, and its GLSL twin (`SHAPE_SKY_GLSL`) in terrain/terrainShaders.ts, built from the @@ -56,7 +58,8 @@ export interface SkyBand { } /** - * The shaping targets, in linear luma. The floor sits 0.019 above the text bound (0.2983): the screen encoding is + * The shaping targets, in linear luma. Day's floor sits 0.019 above the text bound (0.2983), Dusk's 0.019 above the link + * bound (0.4554), which skyForState.test.ts derives from the ink and ties to these: the screen encoding is * gamma 2.2, which reads about 0.006 darker through the sRGB curve at this level, and 8-bit rounding with the sky * pass's dither at its worst (one step in every channel) takes about 0.007 more. The night ceiling is lower than the text bound * (0.0931) by more than that: 0.077394 is the luma at which one step up reads 0.08164, the lightest the baked night sky shows @@ -64,7 +67,9 @@ export interface SkyBand { */ export const SKY_BAND: Record = { day: { floor: 0.317, ceiling: 1, lift: hexToLinear('#e2f4ff') }, - dusk: { floor: 0.317, ceiling: 1, lift: hexToLinear('#eba0b9') }, + // Dusk lifts toward Day's pale blue rather than a rose: mixing a third of the sky toward rose washed the twilight pink, and + // a rose lighter than the floor shifts hues by 47 to 72 degrees where Day's tint shifts them by 18 (measured, PR body). + dusk: { floor: 0.4744, ceiling: 1, lift: hexToLinear('#e2f4ff') }, night: { floor: 0, ceiling: 0.077394, lift: hexToLinear('#5a6a9a') }, }; diff --git a/src/app/features/scene/skyForState.test.ts b/src/app/features/scene/skyForState.test.ts index 9d1dfd2d..a8199b1b 100644 --- a/src/app/features/scene/skyForState.test.ts +++ b/src/app/features/scene/skyForState.test.ts @@ -12,7 +12,7 @@ import { terrainDrawn, terrainSkyFor, } from './skyForState'; -import { displayedLuminance, SKY_BAND } from './skyBand'; +import { displayedLuminance, luma, shapeToBand, SKY_BAND } from './skyBand'; import type { FixedSkyState } from './skyBand'; import { setSkyClock, skyBackdrop } from './skyState'; import { view } from './terrain/terrainWorld'; @@ -24,14 +24,17 @@ const STATES = ['day', 'dusk', 'night'] as const; const SKY = { 'midnight-sky': baked.midnight, 'noonday-sky': baked.noonday } as const; /** - * The band each state's text ink needs, from the ink itself: dark ink needs a backdrop of at least - * AA * (L + 0.05) - 0.05, light ink at most (L + 0.05) / AA - 0.05. Day 0.2983, dusk 0.2983, night 0.0931. + * The band each state needs, from the ink itself: dark ink needs a backdrop of at least + * AA * (L + 0.05) - 0.05, light ink at most (L + 0.05) / AA - 0.05. Day 0.2983, night 0.0931; dusk is bound by its + * link (0.4554), the darker ink to clear, whose floor also holds the text. */ const needs = (state: FixedSkyState) => { - const l = luminance(SKY[skyThemeId(state)].tokens.Background.OnContainer); + const sky = SKY[skyThemeId(state)]; + const l = luminance(sky.tokens.Background.OnContainer); + const link = luminance(sky.link); return state === 'night' ? { floor: 0, ceiling: (l + 0.05) / AA - 0.05 } - : { floor: AA * (l + 0.05) - 0.05, ceiling: 1 }; + : { floor: AA * ((state === 'dusk' ? link : l) + 0.05) - 0.05, ceiling: 1 }; }; let search = ''; @@ -51,9 +54,26 @@ afterEach(() => { describe('the bands the text ink needs', () => { it('are the numbers the shaping was set from', () => { expect(needs('day').floor).toBeCloseTo(0.2983, 3); - expect(needs('dusk').floor).toBeCloseTo(0.2983, 3); + expect(needs('dusk').floor).toBeCloseTo(0.4554, 3); expect(needs('night').ceiling).toBeCloseTo(0.0931, 3); }); + + // The drawn floor and the lift are tied to those bounds, not to themselves: nudge either constant and this fails. + it('give Dusk a floor 0.019 above the link bound (as Day has above the text bound) and a lift Day shares', () => { + expect(SKY_BAND.dusk.floor).toBeCloseTo(needs('dusk').floor + 0.019, 4); + expect(SKY_BAND.day.floor).toBeCloseTo(needs('day').floor + 0.019, 3); + expect(SKY_BAND.dusk.lift).toEqual(SKY_BAND.day.lift); + expect(luma(SKY_BAND.dusk.lift)).toBeGreaterThan(SKY_BAND.dusk.floor); + }); + + it.each(['day', 'dusk'] as const)( + '%s: the darkest the shaping can show reads at the bound and under the floor', + (state) => { + const shown = displayedLuminance(shapeToBand([0, 0, 0], SKY_BAND[state]), 'low'); + expect(shown).toBeGreaterThanOrEqual(needs(state).floor); + expect(shown).toBeLessThan(SKY_BAND[state].floor); + } + ); }); describe('ridgeSkyFor', () => { @@ -68,7 +88,7 @@ describe('ridgeSkyFor', () => { it('draws a lavender, rose and orange twilight for dusk', () => { const d = ridgeSkyFor('dusk'); - expect(d).toEqual(skyColours()); + expect(d.ground).toEqual(skyColours().ground); const [t, m, h] = [d.top, d.mid, d.horizon]; expect(t[2]).toBeGreaterThan(t[1]); // lavender: blue over green expect(t[0]).toBeGreaterThan(t[1]); diff --git a/src/app/features/scene/skyForState.ts b/src/app/features/scene/skyForState.ts index 7da17476..71e07ca6 100644 --- a/src/app/features/scene/skyForState.ts +++ b/src/app/features/scene/skyForState.ts @@ -10,11 +10,13 @@ import type { SkyColours, Srgb } from './ridge/skyColour'; import { skyColours } from './ridge/skyColour'; import { resolveSkyState } from './skyState'; import type { SkyState } from './skyState'; -import { displayedLuminance, SKY_BAND } from './skyBand'; +import { displayedLuminance, hexToLinear, shapeToBand, SKY_BAND } from './skyBand'; import type { SkyBand } from './skyBand'; import { luminance } from '../../../sky/skyTokens.js'; import { CAMERA } from './terrain/terrainWorld'; +const rgbHex = (c: Srgb) => `#${c.map((v) => v.toString(16).padStart(2, '0')).join('')}`; + const rgb = (hex: string): Srgb => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16)) as unknown as Srgb; @@ -28,12 +30,27 @@ const fromGradient = (g: readonly string[], ground: Srgb): SkyColours => ({ /** Sun elevation in degrees for the terrain's dusk: just above the horizon, between the two bakes (as `?sun=` puts it). */ export const DUSK_SUN_ELEVATION = 2; -/** The ridge scene's `sky` option: the gradient of the state's sky ('auto': of the sky it resolves to). */ +const toByte = (v: number): number => + Math.round(255 * (v <= 0.0031308 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - 0.055)); + +/** A stop moved into the state's band, the way the terrain sky is (`shapeToBand`), through the sRGB curve the canvas draws with. */ +const inBand = (c: Srgb, band: SkyBand): Srgb => + shapeToBand(hexToLinear(rgbHex(c)), band).map(toByte) as unknown as Srgb; + +/** The ridge scene's `sky` option: the gradient of the state's sky ('auto': of the sky it resolves to). Dusk's twilight stops are shaped into its band. */ export function ridgeSkyFor(state: SkyState): SkyColours { const { ground } = skyColours(); switch (resolveSkyState(state)) { - case 'dusk': - return skyColours(); + case 'dusk': { + const twilight = skyColours(); + const band = SKY_BAND.dusk; + return { + ...twilight, + top: inBand(twilight.top, band), + mid: inBand(twilight.mid, band), + horizon: inBand(twilight.horizon, band), + }; + } case 'day': return fromGradient(baked.noonday.gradient, ground); default: @@ -67,8 +84,6 @@ export function terrainSkyFor(state: SkyState): { } } -const rgbHex = (c: Srgb) => `#${c.map((v) => v.toString(16).padStart(2, '0')).join('')}`; - /** Relative luminance (WCAG) of the darkest and lightest colour a scene can draw. */ export interface Range { readonly darkest: number; diff --git a/src/app/features/scene/skyState.ts b/src/app/features/scene/skyState.ts index 21117039..0b60d99b 100644 --- a/src/app/features/scene/skyState.ts +++ b/src/app/features/scene/skyState.ts @@ -131,7 +131,7 @@ export interface SkyBackdrop { // Bounds what the 8-bit screen shows too (one step each way for rounding and dither), rounded outward to six places. const BACKDROPS: Record = { day: { darkest: 0.461063, lightest: 0.901108 }, - dusk: { darkest: 0.30619, lightest: 0.968744 }, + dusk: { darkest: 0.460352, lightest: 0.968744 }, night: { darkest: 0.05987, lightest: 0.081642 }, }; diff --git a/src/app/features/scene/skyTheme.test.ts b/src/app/features/scene/skyTheme.test.ts index 53cf2873..fd9f32dd 100644 --- a/src/app/features/scene/skyTheme.test.ts +++ b/src/app/features/scene/skyTheme.test.ts @@ -59,13 +59,13 @@ describe('skyThemeId', () => { describe('ink of the theme a state picks, on bare sky', () => { // Per state: the picked theme, its text ink and link, and their WCAG ratios on the sky the scenes // draw (skyBackdrop, the worst case over its band). Text clears 4.5 in every state. Link clears - // on day and night and not on dusk: the links are derived in src/sky/skyTokens.js, outside the band, and these - // numbers are recorded as they are measured. Midnight's link is brightened in its own hue until it clears the - // lightest Night sky (NIGHT_SKY_LIGHTEST); Dusk's dark link would need a backdrop of about 0.455, and the dusk floor is lower. + // in every state: the links are derived in src/sky/skyTokens.js, outside the band, and these numbers are recorded as + // they are measured. Midnight's link is brightened in its own hue until it clears the lightest Night sky + // (NIGHT_SKY_LIGHTEST); Dusk's dark link needs a backdrop of about 0.4554, which the dusk floor is set above. // 'auto' resolves from the clock to one of these three and has the same numbers. const cells: [FixedSkyState, string, string, string, string, string][] = [ ['day', 'noonday-sky', '#342d25', '6.60', '#2a4f27', '4.55'], - ['dusk', 'noonday-sky', '#342d25', '4.60', '#2a4f27', '3.17'], + ['dusk', 'noonday-sky', '#342d25', '6.59', '#2a4f27', '4.54'], ['night', 'midnight-sky', '#aeccff', '4.89', '#8ad1bd', '4.53'], ]; @@ -84,10 +84,10 @@ describe('ink of the theme a state picks, on bare sky', () => { expect(ratio(ink, skyBackdrop(state))).toBeGreaterThanOrEqual(AA); }); - it('link clears AA on day and night and falls short on dusk', () => { + it('link clears AA in every state', () => { const link = (s: FixedSkyState, id: keyof typeof SKY) => ratio(SKY[id].link, skyBackdrop(s)); expect(link('day', 'noonday-sky')).toBeGreaterThanOrEqual(AA); - expect(link('dusk', 'noonday-sky')).toBeLessThan(AA); + expect(link('dusk', 'noonday-sky')).toBeGreaterThanOrEqual(AA); expect(link('night', 'midnight-sky')).toBeGreaterThanOrEqual(AA); }); diff --git a/src/app/features/scene/skyTheme.ts b/src/app/features/scene/skyTheme.ts index 4ab3f43a..f8ab8b12 100644 --- a/src/app/features/scene/skyTheme.ts +++ b/src/app/features/scene/skyTheme.ts @@ -4,10 +4,10 @@ // // Day and Dusk take the dark ink of Noonday Sky, Night the light ink of Midnight Sky. Each state's // drawn sky is shaped into a luminance band (skyBand.ts) in which that ink's TEXT clears 4.5:1 on -// bare sky: Day and Dusk at least 0.2983 (dark ink), Night at most 0.0931 (light ink). skyTheme.test.ts -// pins the measured text and link ratios. Link ink is not in the band: the Day and Dusk links are dark -// and fall short of 4.5 where the sky is below about 0.455 (Dusk: 3.17), because the themes' link colours are -// derived outside this code (src/sky/skyTokens.js). Midnight Sky's link is derived there against the lightest Night sky +// bare sky: Day at least 0.2983 (dark ink), Night at most 0.0931 (light ink). Dusk's floor is higher, the bound of +// the dark link (0.4554), so its link clears as well. skyTheme.test.ts pins the measured text and link ratios. Night's +// link ink is not in the band, and the themes' link colours are derived outside this code (src/sky/skyTokens.js). +// Midnight Sky's link is derived there against the lightest Night sky // (4.53 on it, 5.43 on the darkest). The plate (skyPlate.css.ts) is still in the shell, pending // its removal. import type { FixedSkyState, SkyState } from './skyState'; From ea238cf77d4af5c79113982ccccabd08bf3b8d34 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 03:18:49 +0000 Subject: [PATCH 06/56] auth: a blurred, tinted, soft-edged band behind the sign-in footer line The footer links sat on dark terrain in the sky's dark ink (contrast 1.0 to 2.9 at Dusk and Day). The footer now carries the plate's ink over a backdrop-blur band tinted with the plate's fill (FOOTER_TINT), faded at its edges; skyTheme.test.ts checks the ink over the darkest and lightest scene behind it. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/skyPlate.ts | 8 ++++++++ src/app/features/scene/skyTheme.test.ts | 19 ++++++++++++++++++- src/app/pages/auth/styles.css.ts | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/app/features/scene/skyPlate.ts b/src/app/features/scene/skyPlate.ts index 872e2fd6..54ff13f2 100644 --- a/src/app/features/scene/skyPlate.ts +++ b/src/app/features/scene/skyPlate.ts @@ -7,3 +7,11 @@ export const SKY_PLATE_FALLBACK = { text: '#b7c7d9', link: '#99c7ff', } as const; + +/** + * The sign-in footer's tint: how much of the plate's fill (--sky-bubble) is mixed over the blurred scene behind the + * footer line. The footer is a blurred, tinted band with soft edges, not a plate: the scene shows through it. The + * footer ink is the plate's, so the tint has to bring the scene behind it to where that ink clears 4.5:1 + * (skyTheme.test.ts). + */ +export const FOOTER_TINT = 0.75; diff --git a/src/app/features/scene/skyTheme.test.ts b/src/app/features/scene/skyTheme.test.ts index 53cf2873..dd46117b 100644 --- a/src/app/features/scene/skyTheme.test.ts +++ b/src/app/features/scene/skyTheme.test.ts @@ -7,7 +7,7 @@ import { PageContent, PageNavContent } from '../../components/page/style.css'; import { CallStatus } from '../call-status/styles.css'; import { MembersDrawer } from '../room/MembersDrawer.css'; import { RoomViewTyping } from '../room/RoomViewTyping.css'; -import { SKY_PLATE_FALLBACK } from './skyPlate'; +import { FOOTER_TINT, SKY_PLATE_FALLBACK } from './skyPlate'; import { SkyPlate, plateRegions } from './skyPlate.css'; import { getResolvedSkyState, setSkyClock, skyBackdrop } from './skyState'; import type { SkyBackdrop } from './skyState'; @@ -160,3 +160,20 @@ describe('regions that sit on the plate', () => { expect(plateRegions.some((r) => r.includes(':not(:has('))).toBe(true); }); }); + +describe('the sign-in footer band', () => { + // The footer line is the plate's ink on a band that mixes FOOTER_TINT of the plate's fill over the blurred scene. + // The scene behind it is the terrain: as dark as 0, as light as the sky state's lightest backdrop. Ink contrast is + // lowest at one of those two ends, so both are checked. + const tinted = (fill: string, behind: number) => + FOOTER_TINT * luminance(fill) + (1 - FOOTER_TINT) * behind; + it.each(fixed)( + '%s: the ink clears AA over the darkest and the lightest scene behind it', + (state, id) => { + const { Other, OtherText } = SKY[id].bubbles; + [0, skyBackdrop(state).lightest].forEach((behind) => { + expect(against(OtherText, tinted(Other, behind))).toBeGreaterThanOrEqual(AA); + }); + } + ); +}); diff --git a/src/app/pages/auth/styles.css.ts b/src/app/pages/auth/styles.css.ts index b13bf868..c1614a79 100644 --- a/src/app/pages/auth/styles.css.ts +++ b/src/app/pages/auth/styles.css.ts @@ -1,5 +1,6 @@ import { globalStyle, style } from '@vanilla-extract/css'; import { DefaultReset, color, config, toRem } from 'folds'; +import { FOOTER_TINT } from '../../features/scene/skyPlate'; export const AuthLayout = style({ minHeight: '100%', @@ -88,3 +89,25 @@ globalStyle(`${scene} ${AuthCardContent}`, { paddingBottom: config.space.S400, gap: config.space.S500, }); + +// The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 to 0.04 at dusk), where +// the sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's +// ink and link, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. +// The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts. +globalStyle(`${scene} ${AuthFooter}`, { + paddingTop: config.space.S500, + paddingBottom: config.space.S500, + backgroundColor: `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${ + FOOTER_TINT * 100 + }%, transparent)`, + backdropFilter: 'blur(12px)', + WebkitBackdropFilter: 'blur(12px)', + maskImage: + 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)', + WebkitMaskImage: + 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)', + color: 'var(--sky-bubble-text, #b7c7d9)', +}); +globalStyle(`${scene} ${AuthFooter} a`, { + color: 'var(--sky-bubble-text, #b7c7d9)', +}); From eb6efeee91f564b9a3521078197ab0f95a3ece7a Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 03:20:06 +0000 Subject: [PATCH 07/56] auth: footer tint 0.82; 0.75 measured 4.49 at Dusk on the drawn pixels Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/skyPlate.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/features/scene/skyPlate.ts b/src/app/features/scene/skyPlate.ts index 54ff13f2..2b9f4484 100644 --- a/src/app/features/scene/skyPlate.ts +++ b/src/app/features/scene/skyPlate.ts @@ -12,6 +12,7 @@ export const SKY_PLATE_FALLBACK = { * The sign-in footer's tint: how much of the plate's fill (--sky-bubble) is mixed over the blurred scene behind the * footer line. The footer is a blurred, tinted band with soft edges, not a plate: the scene shows through it. The * footer ink is the plate's, so the tint has to bring the scene behind it to where that ink clears 4.5:1 - * (skyTheme.test.ts). + * (skyTheme.test.ts). The browser mixes in sRGB, which gives less contrast over dark terrain than the linear mix the test + * computes, so the value carries margin: measured on the drawn pixels (Chrome, GTX 1070), 0.75 gave 4.49 at Dusk. */ -export const FOOTER_TINT = 0.75; +export const FOOTER_TINT = 0.82; From 56bc40abe600cc92612cd159c79bce6aaa6d7f7d Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 03:21:47 +0000 Subject: [PATCH 08/56] auth: fade the footer band at its sides too Co-Authored-By: Claude Sonnet 5.5 --- src/app/pages/auth/styles.css.ts | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/app/pages/auth/styles.css.ts b/src/app/pages/auth/styles.css.ts index c1614a79..d3e60dca 100644 --- a/src/app/pages/auth/styles.css.ts +++ b/src/app/pages/auth/styles.css.ts @@ -90,6 +90,43 @@ globalStyle(`${scene} ${AuthCardContent}`, { gap: config.space.S500, }); +// The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 to 0.04 at dusk), where +// the sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's +// ink and link, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. +// The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts. +const FADE_Y = + 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)'; +const FADE_X = + 'linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent)'; +globalStyle(`${scene} ${AuthFooter}`, { + paddingTop: config.space.S500, + paddingBottom: config.space.S500, + backgroundColor: `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${ + FOOTER_TINT * 100 + }%, transparent)`, + backdropFilter: 'blur(12px)', + WebkitBackdropFilter: 'blur(12px)', + maskImage: `${FADE_Y}, ${FADE_X}`, + WebkitMaskImage: `${FADE_Y}, ${FADE_X}`, + maskComposite: 'intersect', + WebkitMaskComposite: 'source-in', + color: 'var(--sky-bubble-text, #b7c7d9)', + borderRadius: config.radii.R500, + // A faint inner edge, so a pane this clear still reads as a surface. + borderColor: 'rgba(183, 199, 217, 0.14)', +}); +globalStyle(`${scene} body:not(.gamecult-theme) ${AuthCard}`, { + vars: { '--tc-link': '#99c7ff' }, +}); +globalStyle(`${scene} ${AuthHeader}`, { + backgroundColor: 'transparent', +}); +globalStyle(`${scene} ${AuthCardContent}`, { + paddingTop: config.space.S500, + paddingBottom: config.space.S400, + gap: config.space.S500, +}); + // The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 to 0.04 at dusk), where // the sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's // ink and link, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. From 2e6d4d042a326e38e793ae043832376dcf3ae78b Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 03:31:52 +0000 Subject: [PATCH 09/56] auth: footer band review fixes: one block, no pill or border, color-mix fallback, sRGB test The first footer block had spliced into the sign-in card's rule (a Y-only mask duplicate that overrode the side fade, a pill radius and border on the footer); the footer is now one rule. The test mixes in encoded sRGB as Chrome does. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/skyPlate.ts | 4 +-- src/app/features/scene/skyTheme.test.ts | 23 +++++++++---- src/app/pages/auth/styles.css.ts | 45 +++---------------------- 3 files changed, 23 insertions(+), 49 deletions(-) diff --git a/src/app/features/scene/skyPlate.ts b/src/app/features/scene/skyPlate.ts index 2b9f4484..86429ba5 100644 --- a/src/app/features/scene/skyPlate.ts +++ b/src/app/features/scene/skyPlate.ts @@ -12,7 +12,7 @@ export const SKY_PLATE_FALLBACK = { * The sign-in footer's tint: how much of the plate's fill (--sky-bubble) is mixed over the blurred scene behind the * footer line. The footer is a blurred, tinted band with soft edges, not a plate: the scene shows through it. The * footer ink is the plate's, so the tint has to bring the scene behind it to where that ink clears 4.5:1 - * (skyTheme.test.ts). The browser mixes in sRGB, which gives less contrast over dark terrain than the linear mix the test - * computes, so the value carries margin: measured on the drawn pixels (Chrome, GTX 1070), 0.75 gave 4.49 at Dusk. + * (skyTheme.test.ts). The browser mixes in sRGB, so the test does too (skyTheme.test.ts): over pure black 0.80 gives 4.36 and 0.82 gives 4.55, and + * the darkest terrain drawn is 0.0045, not black; measured on the drawn pixels the footer clears 6.2. */ export const FOOTER_TINT = 0.82; diff --git a/src/app/features/scene/skyTheme.test.ts b/src/app/features/scene/skyTheme.test.ts index dd46117b..3229cf36 100644 --- a/src/app/features/scene/skyTheme.test.ts +++ b/src/app/features/scene/skyTheme.test.ts @@ -162,17 +162,28 @@ describe('regions that sit on the plate', () => { }); describe('the sign-in footer band', () => { - // The footer line is the plate's ink on a band that mixes FOOTER_TINT of the plate's fill over the blurred scene. - // The scene behind it is the terrain: as dark as 0, as light as the sky state's lightest backdrop. Ink contrast is - // lowest at one of those two ends, so both are checked. - const tinted = (fill: string, behind: number) => - FOOTER_TINT * luminance(fill) + (1 - FOOTER_TINT) * behind; + // The footer line is the plate's ink on a band that mixes FOOTER_TINT of the plate's fill over the blurred scene, + // as the browser does: per channel, in encoded sRGB (not in linear light, which flatters dark scenes). The scene + // behind it is the terrain, taken as grey: as dark as 0, as light as the sky state's lightest backdrop. Ink + // contrast is lowest at one of those two ends, so both are checked. + const encode = (l: number) => (l <= 0.0031308 ? 12.92 * l : 1.055 * l ** (1 / 2.4) - 0.055); + const channels = (hex: string) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255); + const hexOf = (c: number[]) => + `#${c + .map((v) => + Math.round(v * 255) + .toString(16) + .padStart(2, '0') + ) + .join('')}`; + const bandOver = (fill: string, behind: number) => + hexOf(channels(fill).map((f) => FOOTER_TINT * f + (1 - FOOTER_TINT) * encode(behind))); it.each(fixed)( '%s: the ink clears AA over the darkest and the lightest scene behind it', (state, id) => { const { Other, OtherText } = SKY[id].bubbles; [0, skyBackdrop(state).lightest].forEach((behind) => { - expect(against(OtherText, tinted(Other, behind))).toBeGreaterThanOrEqual(AA); + expect(contrast(OtherText, bandOver(Other, behind))).toBeGreaterThanOrEqual(AA); }); } ); diff --git a/src/app/pages/auth/styles.css.ts b/src/app/pages/auth/styles.css.ts index d3e60dca..772f02a2 100644 --- a/src/app/pages/auth/styles.css.ts +++ b/src/app/pages/auth/styles.css.ts @@ -92,57 +92,20 @@ globalStyle(`${scene} ${AuthCardContent}`, { // The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 to 0.04 at dusk), where // the sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's -// ink and link, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. -// The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts. +// ink, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. A browser +// without color-mix gets the plate's opaque fill, as the chat bubbles do (message/layout/layout.css.ts). The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts. const FADE_Y = 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)'; const FADE_X = 'linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent)'; +const tint = `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${FOOTER_TINT * 100}%, transparent)`; globalStyle(`${scene} ${AuthFooter}`, { paddingTop: config.space.S500, paddingBottom: config.space.S500, - backgroundColor: `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${ - FOOTER_TINT * 100 - }%, transparent)`, + backgroundColor: ['var(--sky-bubble, #0d1a26)', tint], backdropFilter: 'blur(12px)', - WebkitBackdropFilter: 'blur(12px)', maskImage: `${FADE_Y}, ${FADE_X}`, - WebkitMaskImage: `${FADE_Y}, ${FADE_X}`, maskComposite: 'intersect', - WebkitMaskComposite: 'source-in', - color: 'var(--sky-bubble-text, #b7c7d9)', - borderRadius: config.radii.R500, - // A faint inner edge, so a pane this clear still reads as a surface. - borderColor: 'rgba(183, 199, 217, 0.14)', -}); -globalStyle(`${scene} body:not(.gamecult-theme) ${AuthCard}`, { - vars: { '--tc-link': '#99c7ff' }, -}); -globalStyle(`${scene} ${AuthHeader}`, { - backgroundColor: 'transparent', -}); -globalStyle(`${scene} ${AuthCardContent}`, { - paddingTop: config.space.S500, - paddingBottom: config.space.S400, - gap: config.space.S500, -}); - -// The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 to 0.04 at dusk), where -// the sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's -// ink and link, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. -// The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts. -globalStyle(`${scene} ${AuthFooter}`, { - paddingTop: config.space.S500, - paddingBottom: config.space.S500, - backgroundColor: `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${ - FOOTER_TINT * 100 - }%, transparent)`, - backdropFilter: 'blur(12px)', - WebkitBackdropFilter: 'blur(12px)', - maskImage: - 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)', - WebkitMaskImage: - 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)', color: 'var(--sky-bubble-text, #b7c7d9)', }); globalStyle(`${scene} ${AuthFooter} a`, { From 271729a5c03c933def22745deb0fe2156a656ea7 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 03:48:36 +0000 Subject: [PATCH 10/56] auth: test the emitted footer rule; wrap its comment; correct the terrain luminance figure footerBand.test.ts reads the CSS vanilla-extract emits for styles.css.ts (so @vanilla-extract/integration becomes a devDependency): one footer rule, the plate fill before the color-mix fill, a mask faded on both axes, no border or radius. skyPlate.ts no longer claims the darkest terrain is 0.0045 (that was Night's whole frame); it states the footer region's minima per state and the measured link contrast. Co-Authored-By: Claude Sonnet 5.5 --- package-lock.json | 2 ++ package.json | 1 + src/app/features/scene/skyPlate.ts | 8 +++-- src/app/pages/auth/footerBand.test.ts | 48 +++++++++++++++++++++++++++ src/app/pages/auth/styles.css.ts | 7 ++-- 5 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 src/app/pages/auth/footerBand.test.ts diff --git a/package-lock.json b/package-lock.json index 947eef2b..1915ec32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,6 +86,7 @@ "@types/ua-parser-js": "0.7.36", "@typescript-eslint/eslint-plugin": "5.46.1", "@typescript-eslint/parser": "5.46.1", + "@vanilla-extract/integration": "6.5.0", "@vitejs/plugin-react": "4.2.0", "buffer": "6.0.3", "cz-conventional-changelog": "3.3.0", @@ -5095,6 +5096,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/@vanilla-extract/integration/-/integration-6.5.0.tgz", "integrity": "sha512-E2YcfO8vA+vs+ua+gpvy1HRqvgWbI+MTlUpxA8FvatOvybuNcWAY0CKwQ/Gpj7rswYKtC6C7+xw33emM6/ImdQ==", + "license": "MIT", "dependencies": { "@babel/core": "^7.20.7", "@babel/plugin-syntax-typescript": "^7.20.0", diff --git a/package.json b/package.json index 1aebfd94..69f589c6 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "@types/ua-parser-js": "0.7.36", "@typescript-eslint/eslint-plugin": "5.46.1", "@typescript-eslint/parser": "5.46.1", + "@vanilla-extract/integration": "6.5.0", "@vitejs/plugin-react": "4.2.0", "buffer": "6.0.3", "cz-conventional-changelog": "3.3.0", diff --git a/src/app/features/scene/skyPlate.ts b/src/app/features/scene/skyPlate.ts index 86429ba5..353134ab 100644 --- a/src/app/features/scene/skyPlate.ts +++ b/src/app/features/scene/skyPlate.ts @@ -11,8 +11,10 @@ export const SKY_PLATE_FALLBACK = { /** * The sign-in footer's tint: how much of the plate's fill (--sky-bubble) is mixed over the blurred scene behind the * footer line. The footer is a blurred, tinted band with soft edges, not a plate: the scene shows through it. The - * footer ink is the plate's, so the tint has to bring the scene behind it to where that ink clears 4.5:1 - * (skyTheme.test.ts). The browser mixes in sRGB, so the test does too (skyTheme.test.ts): over pure black 0.80 gives 4.36 and 0.82 gives 4.55, and - * the darkest terrain drawn is 0.0045, not black; measured on the drawn pixels the footer clears 6.2. + * footer ink is the plate's, so the tint has to bring the scene behind it to where that ink clears 4.5:1. The browser + * mixes in sRGB, so the test does too (skyTheme.test.ts): over pure black 0.80 gives 4.36 and 0.82 gives 4.55. The + * terrain is not black: in the shots at 390x844 (terrain scene, tier low) the darkest pixel in the footer's region is + * 0.0203 at Dusk, 0.0936 at Day (dark ink) and 0.0052 at Night (light ink). Measured on the drawn pixels with the band + * on, the worst link contrast is 5.0 at Dusk, 5.7 at Day and 11.2 at Night. */ export const FOOTER_TINT = 0.82; diff --git a/src/app/pages/auth/footerBand.test.ts b/src/app/pages/auth/footerBand.test.ts new file mode 100644 index 00000000..26078d95 --- /dev/null +++ b/src/app/pages/auth/footerBand.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest'; +import zlib from 'zlib'; +import { compile, processVanillaFile } from '@vanilla-extract/integration'; +import { AuthFooter } from './styles.css'; + +// The CSS styles.css.ts emits for the sign-in footer, read back from vanilla-extract's own compile. +const emitted = async () => { + const filePath = `${process.cwd()}/src/app/pages/auth/styles.css.ts`; + const { source } = await compile({ filePath, cwd: process.cwd() }); + const code = await processVanillaFile({ source, filePath, identOption: 'debug' }); + const encoded = (code.match(/source=#([A-Za-z0-9+/=]+)/) as RegExpMatchArray)[1]; + return zlib.gunzipSync(Buffer.from(encoded, 'base64')).toString(); +}; +const declarations = (css: string, selector: string) => + [...css.matchAll(/^([^{}\n]+) \{\n([^}]*)\}/gm)] + .filter((m) => m[1] === selector) + .map((m) => + m[2] + .split('\n') + .map((l) => l.trim().replace(/;$/, '')) + .filter(Boolean) + ); + +describe('the sign-in footer band, as emitted', () => { + const over = `:root[data-scene] .${AuthFooter}`; + + it('is one rule over the scene, so no later rule can override its mask', async () => { + expect(declarations(await emitted(), over)).toHaveLength(1); + }); + + it('puts the plate fill before the color-mix tint, so a browser without color-mix keeps the fill', async () => { + const [rule] = declarations(await emitted(), over); + const fills = rule.filter((d) => d.startsWith('background-color:')); + expect(fills).toHaveLength(2); + expect(fills[0]).toBe('background-color: var(--sky-bubble, #0d1a26)'); + expect(fills[1]).toContain('color-mix('); + }); + + it('is a blurred band faded at all four edges, with no border or radius', async () => { + const [rule] = declarations(await emitted(), over); + const mask = rule.find((d) => d.startsWith('mask-image:')) ?? ''; + expect(mask).toContain('to bottom'); + expect(mask).toContain('to right'); + expect(rule).toContain('mask-composite: intersect'); + expect(rule.some((d) => d.startsWith('backdrop-filter: blur('))).toBe(true); + expect(rule.filter((d) => /^(border|border-radius)/.test(d))).toEqual([]); + }); +}); diff --git a/src/app/pages/auth/styles.css.ts b/src/app/pages/auth/styles.css.ts index 772f02a2..fc47dd79 100644 --- a/src/app/pages/auth/styles.css.ts +++ b/src/app/pages/auth/styles.css.ts @@ -90,10 +90,11 @@ globalStyle(`${scene} ${AuthCardContent}`, { gap: config.space.S500, }); -// The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 to 0.04 at dusk), where -// the sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's +// The footer line sits on the terrain, which is dark at its foot in every sky (luminance 0.02 at dusk), where the +// sky's dark ink is unreadable. Over the scene it gets a blurred band tinted with the plate's fill and the plate's // ink, faded out at its edges so it reads as haze and not as a bar: the scene shows through and round it. A browser -// without color-mix gets the plate's opaque fill, as the chat bubbles do (message/layout/layout.css.ts). The tint (FOOTER_TINT) is checked against the ink in skyTheme.test.ts. +// without color-mix gets the plate's opaque fill, as the chat bubbles do (message/layout/layout.css.ts). The tint +// (FOOTER_TINT) is checked against the ink in skyTheme.test.ts, the emitted rule in footerBand.test.ts. const FADE_Y = 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)'; const FADE_X = From b5e93510e86b424f4312838daa9a197679579480 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 03:58:04 +0000 Subject: [PATCH 11/56] auth: footerBand test: identOption for compile; kill blur(0), base-rule border, hard-stop survivors Co-Authored-By: Claude Sonnet 5.5 --- src/app/pages/auth/footerBand.test.ts | 36 ++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/app/pages/auth/footerBand.test.ts b/src/app/pages/auth/footerBand.test.ts index 26078d95..c3293c4d 100644 --- a/src/app/pages/auth/footerBand.test.ts +++ b/src/app/pages/auth/footerBand.test.ts @@ -6,7 +6,7 @@ import { AuthFooter } from './styles.css'; // The CSS styles.css.ts emits for the sign-in footer, read back from vanilla-extract's own compile. const emitted = async () => { const filePath = `${process.cwd()}/src/app/pages/auth/styles.css.ts`; - const { source } = await compile({ filePath, cwd: process.cwd() }); + const { source } = await compile({ filePath, cwd: process.cwd(), identOption: 'debug' }); const code = await processVanillaFile({ source, filePath, identOption: 'debug' }); const encoded = (code.match(/source=#([A-Za-z0-9+/=]+)/) as RegExpMatchArray)[1]; return zlib.gunzipSync(Buffer.from(encoded, 'base64')).toString(); @@ -36,13 +36,37 @@ describe('the sign-in footer band, as emitted', () => { expect(fills[1]).toContain('color-mix('); }); - it('is a blurred band faded at all four edges, with no border or radius', async () => { + it('blurs what is behind it by a real radius', async () => { + const [rule] = declarations(await emitted(), over); + const blur = rule.find((d) => d.startsWith('backdrop-filter:')) ?? ''; + const radius = Number(blur.match(/^backdrop-filter: blur\((\d+(?:\.\d+)?)px\)$/)?.[1]); + expect(radius).toBeGreaterThan(0); + }); + + it('fades along both axes over a soft ramp, not a hard edge', async () => { const [rule] = declarations(await emitted(), over); const mask = rule.find((d) => d.startsWith('mask-image:')) ?? ''; - expect(mask).toContain('to bottom'); - expect(mask).toContain('to right'); expect(rule).toContain('mask-composite: intersect'); - expect(rule.some((d) => d.startsWith('backdrop-filter: blur('))).toBe(true); - expect(rule.filter((d) => /^(border|border-radius)/.test(d))).toEqual([]); + ['bottom', 'right'].forEach((axis) => { + const ramp = mask.match( + new RegExp( + `linear-gradient\\(to ${axis}, transparent, #000 (\\d+)px, #000 calc\\(100% - (\\d+)px\\), transparent\\)` + ) + ); + expect(ramp).not.toBeNull(); + // Each end ramps from clear to opaque over at least 8px. + expect(Number(ramp?.[1])).toBeGreaterThanOrEqual(8); + expect(Number(ramp?.[2])).toBeGreaterThanOrEqual(8); + }); + }); + + it('has no border or radius, in the scene rule or in the base footer rule', async () => { + const css = await emitted(); + const [rule] = declarations(css, over); + const base = declarations(css, `.${AuthFooter}`); + expect(base).toHaveLength(1); + [rule, base[0]].forEach((r) => { + expect(r.filter((d) => /^(border|outline)/.test(d))).toEqual([]); + }); }); }); From 320aeecc2c076453e72f843c5fd025a4d2c12594 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 04:09:31 +0000 Subject: [PATCH 12/56] auth: footerBand test pins blur to 12px and the footer rule's exact property set Co-Authored-By: Claude Sonnet 5.5 --- src/app/pages/auth/footerBand.test.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/app/pages/auth/footerBand.test.ts b/src/app/pages/auth/footerBand.test.ts index c3293c4d..9d196200 100644 --- a/src/app/pages/auth/footerBand.test.ts +++ b/src/app/pages/auth/footerBand.test.ts @@ -36,11 +36,25 @@ describe('the sign-in footer band, as emitted', () => { expect(fills[1]).toContain('color-mix('); }); - it('blurs what is behind it by a real radius', async () => { + it('blurs what is behind it by 12px, the radius of the chat bubbles', async () => { const [rule] = declarations(await emitted(), over); - const blur = rule.find((d) => d.startsWith('backdrop-filter:')) ?? ''; - const radius = Number(blur.match(/^backdrop-filter: blur\((\d+(?:\.\d+)?)px\)$/)?.[1]); - expect(radius).toBeGreaterThan(0); + expect(rule.filter((d) => d.startsWith('backdrop-filter'))).toEqual([ + 'backdrop-filter: blur(12px)', + ]); + }); + + it('declares exactly these properties, so nothing else (a shadow, a prefixed override) rides along', async () => { + const [rule] = declarations(await emitted(), over); + expect(rule.map((d) => d.slice(0, d.indexOf(':'))).sort()).toEqual([ + 'backdrop-filter', + 'background-color', + 'background-color', + 'color', + 'mask-composite', + 'mask-image', + 'padding-bottom', + 'padding-top', + ]); }); it('fades along both axes over a soft ramp, not a hard edge', async () => { @@ -68,5 +82,7 @@ describe('the sign-in footer band, as emitted', () => { [rule, base[0]].forEach((r) => { expect(r.filter((d) => /^(border|outline)/.test(d))).toEqual([]); }); + // The base rule is only the footer's padding: nothing visible (a shadow, a fill) is hidden outside the scene rule. + expect(base[0].map((d) => d.slice(0, d.indexOf(':')))).toEqual(['padding']); }); }); From 5712068902f5c73a0bc5eab63a9f073a6f91f079 Mon Sep 17 00:00:00 2001 From: Eureka Self Date: Tue, 10 Nov 2026 13:18:59 +0000 Subject: [PATCH 13/56] nemesis: Meta's logo, chromeless plates and card, sky-led theme, denser terrain, scene style picker - Logo: Meta's white matte (2026-11-09) as the UI mark; icons are the mark on a night-sky tile. Dark ink on Noonday Sky. - Plates (title bars, lists, composer) and the sign-in card: blur with a light sky tint (PLATE_TINT 0.45), never opaque. - auto sky picks the theme from the resolved sky: day light mode, night dark mode, dusk the dark-ink crossover. - Terrain grid 768/2048 full, 512/1024 low, 256/1024 still. - Temporary Background group (Terrain, Ridges) in the sky menu, also on the sign-in card. Co-Authored-By: Claude Opus 5.5 --- public/favicon.ico | Bin 2338 -> 9308 bytes public/res/android/android-chrome-144x144.png | Bin 3096 -> 9669 bytes public/res/android/android-chrome-192x192.png | Bin 4140 -> 13156 bytes public/res/android/android-chrome-256x256.png | Bin 5684 -> 17958 bytes public/res/android/android-chrome-36x36.png | Bin 817 -> 2038 bytes public/res/android/android-chrome-384x384.png | Bin 9055 -> 27702 bytes public/res/android/android-chrome-48x48.png | Bin 1039 -> 2914 bytes public/res/android/android-chrome-512x512.png | Bin 12620 -> 37581 bytes public/res/android/android-chrome-72x72.png | Bin 1528 -> 4503 bytes public/res/android/android-chrome-96x96.png | Bin 2022 -> 6234 bytes public/res/apple/apple-touch-icon-114x114.png | Bin 2340 -> 7505 bytes public/res/apple/apple-touch-icon-120x120.png | Bin 2472 -> 7923 bytes public/res/apple/apple-touch-icon-144x144.png | Bin 3096 -> 9669 bytes public/res/apple/apple-touch-icon-152x152.png | Bin 3212 -> 10236 bytes public/res/apple/apple-touch-icon-167x167.png | Bin 3555 -> 11346 bytes public/res/apple/apple-touch-icon-180x180.png | Bin 3884 -> 12230 bytes public/res/apple/apple-touch-icon-57x57.png | Bin 1188 -> 3474 bytes public/res/apple/apple-touch-icon-60x60.png | Bin 1261 -> 3629 bytes public/res/apple/apple-touch-icon-72x72.png | Bin 1528 -> 4503 bytes public/res/apple/apple-touch-icon-76x76.png | Bin 1606 -> 4748 bytes public/res/svg/ratatoskr.svg | 16 +----- src/app/features/scene/BackgroundScene.tsx | 9 +++- src/app/features/scene/SkyStateControl.tsx | 51 +++++++++++++++++- src/app/features/scene/sceneStyle.ts | 28 ++++++++++ src/app/features/scene/skyPlate.css.ts | 6 ++- src/app/features/scene/skyPlate.ts | 8 +++ src/app/features/scene/skyTheme.ts | 4 +- .../features/scene/terrain/terrainWorld.ts | 6 +-- src/app/pages/auth/AuthLayout.tsx | 2 + src/app/pages/auth/styles.css.ts | 29 +++++++--- 30 files changed, 127 insertions(+), 32 deletions(-) create mode 100644 src/app/features/scene/sceneStyle.ts diff --git a/public/favicon.ico b/public/favicon.ico index cfce994377be2daa71cd95204142fc269980eb34..bda9855c959e04e31aef615055c68fa7df9e1805 100644 GIT binary patch literal 9308 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 delta 2245 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 diff --git a/public/res/android/android-chrome-144x144.png b/public/res/android/android-chrome-144x144.png index d76ed356c9aeb674b40950163dbd422d61b84ade..2dfde532d8801012b8a38a176b80aef5cc5239e7 100644 GIT binary patch literal 9669 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 Hu0mjfyJ31P literal 3096 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 Hu0mjfJf7~C delta 795 zcmV+$1LXYn53vT2BYy(;NklZL|2h`x@@(JZ^0 zliOyieaOR)-7_c4`7$$S&YX(MWmT}0RPnz7%0OkHB@MJ%rGEhR3I%m8n|)D%HW-u; zbuk%sucMks79eJ&0y^qcaNMZ|w^fxF==lLHgqzekZIXf3>6I|vrG=@ZI!;eKT4JaD z9=(8zqwpQW^r-#MnC7{nfY38y7{1VJG@y8=9wz)OOb0CR)N6*x9uu)4R7pS>B0j*h z-vG&zHu6l{#DAXhH)!)8 zy@@oyx6ugvN{qnIi5Lu@Y6H7LVp5BNK3{C60-+t}qdOVcRi_mIeY~-cz@#-kauqDB zaq&Wy)|5{VuY1iI;X)7nczK%xir?#(zU@ubikwN!*SoMi>&`-bxY7!@yqm}_EU6Ch zi(=xKk$(y^Kd>z)464~&ScT3DbbbdztwRFuL>yF)EId93Ut&FEd}QM)d4TXB%~^{E zcT-{biJZD0Drl=01XM|>)?cG;4k&fql>vmF;RB&2!%q2tu)gqtuzs5-ZrK=K5&VnJpUVv@-(RKLx7%dmqEw1;0hb1|MyO-p6DGl2c`%GEk}d Z4HWs9wKCfirC)VK(#! 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 literal 9055 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 P00000NkvXXu0mjf*S({W literal 12620 zcmeHt`8(9#`~P{)48}fX8I@(Usbpy*>kQKJDocrEr$X5(*%I?qNl{TLm24@yvhU2a z$QlaSjYMP{5o4Q~@6qf1d0*G}PxxHd=a=Dno^#*lKKHrT$2oUQjSmS6tQP6o z-~<2|_%8=HY2K<}jgi^;ype4BQkk>4vaiFHyfAKJ0{#3P5LB%;N& z(3{672Qa)p(A`D*j42dC^$qduoAV4+#doVYh=m9rS+~{P^arN=8Z#Xa%FSHjPOdaOp0$#vu&ttWF#sKnXTPtI zw7?8r9eP-r%~~vboIm`Q=NB}Z+izq59{KIg{-9CQ|Kn{OS)QtMSPlV{lSMoG)&ciB z&vnLf-0NtwIz^AcoSXgy2Nr6i?imr~kgfT=q*A10M(g9N4!L z5HR)Q#Z1N>V<2Pz0O7F&%Mn56>6e!#ms|PK-Kzq0dw=A~2mRsrJu#?&*2i5e z<^cu3yDC}AvMieCD!P-#WXQa1dn@+~dd%$>Wr3_be)yJVOGR1uN z^O=l$*vF$`@F1S+vET-HH~2AV8{)vfpN@Y5k@aSQ4v&Mlo={l1zofiCMV|DrimITA zw1`3-?M?2-njV~*43VE_morr6ns4rfj(fn&Sx8l$)LTVWpKA{N`$bFY`(KU?hs<&a<_?@S}FC zicGsIN?B**A$qs;uficEy>_ed{&dSd8u`ONy?+W7UaHReMh^UT{b$gw`$Z=E@8e@U)H!Ju^#RStD zxNoSRdtkt(d^F7ez3BJ4d)^V5r#?ITkg!QLAm+j4KnL4pPTwewqK-ig=3jXj7Bwlb zFMR-t*w3$pK>97TjP-ZBo;YwQE2^6lxWi;QIj_SlQ(Sth`x7Y^h<}S} zjEqc+uvIq?OuYQB!5jd!w+ovB{Kw?ZHdl)k5Vq>uPMp9a+b5R&UWx%o|19U+v@~m} z#*uj=`+6ExA-+98lycgqkS`?Npw?D1DZ*G*tAp_+Q_g>YKfRiWuLeMndGU%Fd^_Hl(md0Pml%rKN%Le$K^27VB@ z62htbzip=@@BB}c@Fm_QOonWShQQ4lYv%h(0j~IPslPXS{3uUz1UV zI?%nf(pJ;N)3QVARvF!V--SR2v;R^SbomsIy&Sz43f`=HV{AJxhDVHg{zd?20{D8) z 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 B{%rsN diff --git a/public/res/android/android-chrome-72x72.png b/public/res/android/android-chrome-72x72.png index 32d5d51646aed74aa40cb39a4af3161802fb74d2..7aa6caab1206aa1727999a4e983d1d909f52102c 100644 GIT binary patch literal 4503 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 delta 1511 zcmVw*C@BjlSb{c*nOwX)#W}H?)nIM^? zj0vgfIUvAc5hEk4jV)7)CTnC^BgE`GW&y0{fRIA6aLm!Pa=E-eSRed+dkOupvz*4d zDrl%JM&~-BG}2K@XWNQW*11{9906)f1eM`9fy5s8`+pr}G|_W2P42tJRzK~ErGWAj z5KP!9AZFqs^G3zO10z~^V9Wx<;jP84Rx)KkGSM@>yNrH)=?+5^dn+@pIVnhkHKjos z-;Z;((KzC>H3M5q-Kt+r?ICZX=}ID*jJku?k`n_!*r&- z!n+iMhkw|?0R$@?rr!q}jmWb4QKrfk4+R~=Vd_{@JokWLuDs|$N@aq>boGsfv`E4} zDx2sTYAdJi`Y?Unnd(*FbSCB=5O&5qKmfo41YLQpF70#JiJN~99!>@hpycJ{QGb6w zZQatC_C6wUP61&@%u6p3944!ATcb$YYqrG4z?0P)%iJhUtz z_>uMVW-n6ln(eDYv@W)U4sD8f4+y2%4M5Pv-W4gIH}`8YU@r94(AAH7=&$eIr+>yj zHT1_Q3k}T zVom_1nDx%(H1c#z%75T9JUG+mhbR$^`wI}H3JAjP8e4>ko=TXOO;lat;jxdX)xa)U8Bkxf zBRE#U*xlGUX9XxAXjbvCDsZL`uFDz>n$lc=aSVtz?tq`j!kK;N0lM&9Rn~wWsVT}C z97ljK!^yT`g{~aBTM5MFqZ4}h543D#1;pnusK zU!@qtJMQ5P&CKZ~3&*ad`19PcBqXFl$~E%^bN$2nYKcQ9A)4if~5eD?kCkY-m(4ew8WjvtPU_1qjCNOF$yC=2bKq zK)i8=SpexvKu~WTzc;f>HJK7G1Am8AG~WUec@cz}j}?;0q_O9b1q2;$jrtQ%K;fhn zhldQ%wPVe~@~#4CPkqF{fC3VRZH^{v5HULvjW<+M%#GoEP64Uj6*6FTMY({iyJtIH z{OI|d!Ep_c6G&s7#auw5xieF5d&~R)ZTxu&2=|^vfIfSy)L=MFG~cGrq+CRi=Ya5E=Rq=mdk6OpGeN>@Zr-l<6(A;j z-vR)Tau9B*+NBKMlUet_0_0a|KpKz+qycF_K9&Zg0ck)QkOt&q{R`aM)sM=ki4Fh& N002ovPDHLkV1h$`;%5K= diff --git a/public/res/android/android-chrome-96x96.png b/public/res/android/android-chrome-96x96.png index 5da68e4b8978211d33d8d3a21d5701e642b0932f..5ab44fe9df1da828a7a2aa029aa72cb745219a61 100644 GIT binary patch literal 6234 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 Eg34HaWdHyG delta 2009 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
    yNan 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 Hu0mjfyJ31P literal 3096 zcmb`Jc{~%28^`_DR&$g!XO5aRj2saniS{_8o z-v&N>;MteH)p2q0P8wtNtO*%EGWYM;ew1YX(f;~e02!7Qo_k->vib{>mS@|c02b`* z{8p41bM`P_p`_;=JuEiTJTZW!7MrFh`kwxfUfCP)wx6R)2L}vo2j1_vQG@SW2_Mi` zs@?3V-}G-8*EvNU<~Jj-2HExH;Vky57CS5Cs@INb`&nXr@49lEptvwusvV$$u!lVc z^3o~JBvGgWL_ZSbYE*&dpqAt`nl5uY^eVSu_i=r~kr{$1tqS;zlW z^hj+D_b%4+tYrQCO#5e(^wZ#zRAO&P$Mik5A+7x;Tm@PyItI!mlI>UNF(JShHhBUX zP=FZZIscRNYLn#8-H2Y7tm-`7)BUY*R9xoQ*L9D|;N0C-27=`NExoYGLcDL-q*t6Y zGJi8#*4CQ7J9)>_H9+5OeLtu#=&3luAhs0RIeT%c(h+}J8HAzhRpW$Yd9X60@E70$ zhY9BFMS~K02G831+=X8wJBO(#JxC&~zyeTcoa7;K>Ws=wfZj<{{e~HC^S?Cm5^oI| z$uAGCWWlXn&4H@Y7L?s*sbvnYc?468q?hF|(l@TdOjRNl&!4flYqF}B(;vbn*PgE? zpWnSkPkSX!e+?2+3H7cbylRzuiq^4p{R9$o$anJER=^EBdMp|-iNa@m_DvRz*qX;O z?$yL@*3?D^X|QZw&O7o>Tf^3ZZg;y`=9Tb>ir*rlC2oMyg?C_|y14XlWK@GDFO<#4HO&8Q3)GjX~4$Eg+`( z4ZuiLagd@aJON0wKKo^(3|a-Jyc(0`AwSUo*ucbBj386TCcn&hNZ&AkNsiA6j5^Sn zx_=&Tg?hfJJKMAb-w*bCuGw65anOD%3@N#wcwNYQ#VDLO`uo~~;t45zmd~%+HIr#Ts?~FEuTt)Zs0Z-A zt+&JODTa}u#A6SXSvBCqI`0UCgEoeXJNsLTH;T8-ssW{B~#H>#pQR) z$zqR=t+DZlIJZs3_cp|~uU~W8S`!l!Us`*W|6x&O*ejwUWa>VLYM3{0+cX9vQ>2U) zlY_t^i23>XmwOo-4v00{tcpBD>5>!iZe<|v!xvAWk4(X9Lw_lXfOrNRT2Sk6$mD-U z(gKJe%zD8!*w3jY7JbveK==)vB>7MV2=J!8XvCg0#UI%zcPAkMxlwKySU~k4biSl{ zNCreribxzhRJrPKU4u@#9i0n$_2Shs>P#)oN9apXq6B zhOthCJBDhEDeHW0F3JWj6qF(|)NO zp@#0zp;2OiH;;>Md1Cn>bvNO81(y*7jTl zVV;?8;KATye;aB-bM3}a!}$XSj}t4BlhUwOrzta>Ga;Fmr<>6n(NPG}S8Q}ZA$;=( zab$xF=MU?8yzZTO+06jf?SmEYcEQoh5;A|cJNgFOHGY~%$U=Fx9=traHN2FuFk)JS zsS^_)&0Q9rMY8^c+xZa1dcto{RmebQMMbUwKqB^1PmPI)Y*f{~ffWM@4mM$N((-+$ zV~^`~SjP2KJ6NW8H6!84jYr~-9Uxi|T^2P-2yn<>smNV;-E?2g<2_i28Nyx&cIPA{v)!G>T@S!QkRG6pj zPQc_dE0PI4tpBQy7waukcfn7I51sXZJiQw*i-noM|MD|i9NSA~b)MK7BprU&U%;tZ{KGIi)hgEPk<`|NG$(>1q!xNxZRtCOz*iX9&b)!q5hbiUdhRS zqU1M51-fI{T1)E&W<_OSZ2e`MAoZfg0K=i}K76*7lVIlx-8zj?nkitW<*CQsIF=dr zwsZwyJkt~AG{J*?&OmPkd_sllRGWCOb%n$pBd4((d%nIw4|$myS{U8jdysxeF8wW} zzQb+DZ!X~(AXyFdB1-I$&S=O}4U+Y;!h6-^DM7Vi3_%gH-iy5@IPfuy@o2cR^#t@V zQ_QP#cB5!fjYb@Xyt}ez=3V_M0Ex^m_OBv6jS%+V?A2>=w%7L z51vo7;mfh?;AWwB#j-yI&0+VS;sT8k1{iFlDVLSRrvW6hhO>k@0+M$X8975boBW;7 z;4e0``_&BmP=}&yF)3GyE{v8%Ep$hH)2z?j7*m!U)$DNAdqdt%2s+R>PxQkf+ zBIe2TmtCK`d(9RnNG53wLq$a@*F#7MvZsNCpXk^YkvNS3G*-FYE41KtIO3zKx$6!uu$BG zKR##J5oD8znBL)Y+1?l!H*~QHJ{8!`)S7zSf{Zp_th)tu(^mc=<)NkdyC;aBTopmE zyuEl;A*E)>rrz(~?|eoq+66>K1F_I#tT?k5-BjXx*Xq=aW=omPlL>7@iTYP6DYZN4 zbZv2V%mD=yUdrRmZ$0j|yCi()&JVKWa$L*b{8cZXw6I(?2jq!#7msuv42PJ8;1X7k zy{@m)!KG=&PYfzdL^$gO=BhhRB4D&O>_!qI5jtu)SPfHtI`gnR*AzQFf6EfsGJ+kTbZ%e z2>`X>LqEBT;SPUHr>R8Q+Va?SSD~dK#*lv1^@s67{VYS001Y$Nkls{r{iu&pCIUeF0fi7R3b@Ff~QYQ3S1QF>6e}e$CX!Tv@FYk)XYlDrQA?+ zDHY0nLq#P)HUSX@1Z3Zr*|&S|IiJt(k8|#224-LeW`=<|?|B^Y!GoDQ_ny~zudny( zBXq0@lvpsu1x)%c^9PA~GdZiIQ zRYuJ)%Kf-<i~e2*f7Nb$Qm^2ps;Iy&7dzA zkr9XmMq`YNk0#yZbp<5wTwUYHj1QJHU=)amSRe|Fi!o-ppdL+H&Wm*`-rlt1z1;@@ zD^oDmfS9boV|v>zKNQ0c#8L-g3`V;KaKVW8E`l)F{lcfwAO^5RS%L`0)tiiPzhm+D zy5(8)MwbT06g} zTK)FiJ;}}50|u3j;{}I~{vM+46s2;E>jc0$fPp5KZ5yp?F#2Z|tKS;GJ%2`bB*2vS z1551u0g-!^WnFEw1IDxdID7=6N!$Q!^@7OgarNRIOE) zva9ZV;h=LK5EZ`4=#&EixB_ctj;N4rbn?OS6>nVKTGwA%YoZnoIOlR@haU;R71-`T zrk&&!<*VL!w54XOv{W(n8hFkyZQ0W>I*KvMpOyv|ShLa+j6p;rs!_Sl$So`1n7?CX zxx+YTp+K3^O70a=Sw7n%09RlwXYbfIF-28%g{xgVTuUqdl+Lxq=V`Df%TyM5-`RH>U+RLaHbZ3!o$CMsx3_O zxbqSu_JQpU(6oq!V;EV!?4=pXi<5ecC|hAf2Om&idxtO4qHLwmW73QXB}T>=WAJvH zf|4k>_Tm!67=suY17%9`2Yz`#*j67Kk!%T;U<2!r9XSy(rZ(y5W9_gd!$eux7;S<* z#lSjd%@-re$_`s*n8K3bUS4m4{41~yOY{*1mJGKsa-1I&_cQM$na2fx?kX)hV1qGW zjQ8gbbb!D*^X3>i&NeVeGIvV$1OF>1A56v=9M{Ehyvx-ZqkY{AyqZOnU|SZp?Om3o z5Cm<~|2D8r?2HYvMWl~l=+60m^eTb&Uy9DVI>lCO0Qezk`Y>F3O{@AeWr% zIFWD&Z4Aj|ipr{5$}6kcvbBnBoBbxl10eR>x%@|5E_<;0^Ie%ufa8`O_}eTvD>inaQ4v^G@N*0FZo z78WjE$*j4Hm_GYs7B5*%eM2Kz@fgua*z$i}fpA80d2YWM-%oc0UabgFXOQC!1|s)Ydj&S&Az!J&)_Jy_n*nJOEtRMU-kW z%ynGr+2Cr8ZFvmx=U;h`>ufMSJZIa(7`->&o8<1f6pTxk9xrJb1~`U`Xl{1 zUg$LpJ#rxDoil>>XMN17HJgaXqv-UYV-GH+gvLaQBMv`=NAJCXgZuTu4%sA=De4*$ z)Ydmp-;kibAweRU#C0{6ZDHGXa~HTDUef2NZU6vWaM!E{ldF6CQ@F$)W*C{M6hiuQ|TH5+1WAXeYBLfrp#f`z`hJRw6CvU zX>eVQ0h9>Eg?WrT<|uj>7x2+1%h|N09M{zuPik2fmStt88r>6My@qyC4hG6n zq#Os^w)xjXzv0|bCuhRZz6^=G0-58u6c^>uuWvE`dGWu*ViE6V?&7@~ARGzdxGvku zt4TR7uB*{_IbJLlVcmwUOnB~H7A#speqJ_x4=N%O38DP&H`;5(#G(;Cf8x=cf8Oa- z*VL1nn?*rE4#q%DZ3ES{b+}p+iG&d)USZIka zG$xW17UVGU^T%@01!wVv5yv-s*wTeZ)6^lAa;U1VWBIBL%=};>uTP%E^w|s1#*mX8 z?^gMm^y?CUd6iUkO&t&4^9#Q9wUYe?TmY`tSW2;A;c~`YaNTahq==A6q!=`yFV8=D z3;p^Q;kqu#q(g0eBgsSxgTb;avf>dUkuZ+qVkt#58gB9RgZg}*^>@FzUx5ohC(>Her*|O>KV8L#3ziX&MahhC zcf1UQ!y(ph+{V9On!;J952sJ>LTuY2D;^~`Cr)lo7CG55B9Sn*Wf2b9t$rqH_Q;A= z8(6+-L+0lIIIfF`V(`GeT>Q;Z^eW0{>GE}~+qjKzD3tM(`z%EE8~UwI^*ahXuGyZ2sK-$<{be4cvfH=J<%5Rxee+iIb= z*Mi23F@{vi;rUmm@z_(7SiEdCjg3hP^0GN+)X7})y>BpRK%dMUHXTN*YwCII=}G+Y z?x%6J%f3{vQJuK}AfiYmU5X3yxbv1D`u#2TOJa7qH#g$*$1r$cKN=G$By)gC%a`kF zvf?qemRIww?~dia(?22{vQbK*waEZfS^*b8eM5q|^Ov%4$tpH(s-U{2o@FaG@Z`j| zn73eQrh>FAh4FiMxjAvJ{r)!?ct{@-3C9nu`(_D5`VTENoh2V-TNdRN)%^9gYq;`% zztpYzTzg_#Ym`!4^?!Hp^t11fpP!B1O<&CTo~*GkNjPLP_LrA&>9@xaiG z@%1mC#-aUtXZmKXh3EXdZ2q@&^iK4o9Y0fTG`6ysI%5GFH&;+tkmDu$vabNvNv~`W zj5hq@#}{GSRwjpGzb2Y{zyFZK`g@EB)7Di6@xfq`mz&M973-Kb>toIuIh;!`9PNig z*<~)5ZdVvgrlQ>O^EAdF2(H#V^3T@@heP{Z$HwlgQBZANJ*SU2jQ=uEBn)R7}O4CE*mVW1SDCk_OkLMAR zDF?@KK~x*Pm%+9yYHAud{@B6X_S-94Owf17>_~U=jWMiRvx!d@uVVJRPnq$-LKZAs zK_ZzV7L8C*RhLOi(iNoE+Fxq%fF6y835V=VZnxj$V8%c=WHaWh6ZfbxPQPKLWP2C7 zNvU2@Sxa3*Bihx(qG9s$vMDUgZ3bempy<42Zbels4T&W2XrxWNeao_`Z%9y(pTqdO zt|K=m3$3-^N89xS=H*`Azk7eyC%pF7Y-W70kQJ*oP*GWnHU`_aG9c+)oX<%o9LezG z4r9Q<2a%VX1wc(*1DiKj@Y%`@e6n~YpDbR*#?9qKA|c|jNT=m$9onW45t6ACeft(M z?3lrRwIgi?rqb&pyY~==~&;Ep&a~AXIvbAj7R8DPO1F4jQHinRG6OTve)4Pyk zj~>A2k`o#Ag%ilmindzD@Ab*o!0NS|Nv0gKvSP5Sd7lA0WKmn!NOo3~Cmy(gqlO&P zCa<3ExdA-)$}}E&;uYr3UrJ*lMKls391anSN2sq)5DwdnIC&@+e`6G*&KOQXe$H;+ z%ewVjne@&FjDPYq7A;v#Zce=2MN8Xqunt6+5=AoUaMa<4P>`Rq%ic~)AEsp~YU>(! zY0?ayed#^s%wI}*MGXLzAWuGiw=?VJzU#(VswxXi4hJlCl;o*C(<8#L!-X?IR$7xGGTgz{5{ReMN znS)Y_STu}?B9TavO1kv#SIoE1{~Q-xa26+@aAdR3Q~NE8^vR;>ZKiaKqkq3*uDIlj zT=?}8{_n>7c)IOrf0|a7(Que}EXw<{7V`B= z%6NMGjSM~V(7g_qX^<{mzLtfHR}qaycWMK8H`7pARZD(uHaGw3yZrd7Z+Prj-L_&U z?eUKqatQx^tPG<)pu}QPii>jTU7X)6Dl}ySE7fG^?Qcjqj!S(*g2qG=L5N1Z@EQqw ze*(ng5x#Qnsr>#gPZ5tr(H%(h>EKb3_vU){%5T>BZmr>{lyLWh&++@ecvJtPqFh|p zt9JJE*m{jYyDo(VIjrBbjqm*Mc3yqvR(ciXd83{^%>2_2^-GgxP*GDyaY1f|x}4rh zs;g^28^brhdOFu%dm%?2en_TgWwq0TrlUY>UO)Z$r9qqKtXY<@Zo^h4z5N02&G?v= zt2a_vUAG-zkq~j8GE`KU%R$BY3?9&z1&daC&0{b2ch4LwO)T7%PJ}TgvuSYZP*qjO$tN7a_19jQ>HlTg3wr?Sbf2oJk4#Hc?dS=cE~wn~;PX85+&gUA zT!B*Fl5f-Bq0$efuB*4VE_@nyRy^uew;hNHyR+DkUodt7$W7*0Lu=**Me-hP~ADJDGsUluQ2lNrI-woP?SJ=s|?e)p@( z_`&60Asn(ZlZ(9>kF~CM)^ZN}=DTzG<+$;DwqiZGIaw4HDr>m=!RLv^B3^j%J;rIL9K)Z+exGBG8qlSycTL+m_dWVDWq)`a%Tg2<=ixg3 z=8^1LtD{97KIz7td~fb(*jirQYLYyA-V$#6>oXJ<=K2Y|bl$j3$ZYXUo4Jsx>N@gr zv)bG~Y3jXhzk349S8b$MQ67!S6!rCuT=V_&DZBm>LLnQ+P3J&e3$8!i`)_W&^IzoW zWg{Xuj&44gZ~v&Zqx)bTOJ_00uyXaron$K0L+{)E`n0#Dp5@j%pQfgIQ74yKJE8V$2($(m;2C~dF4Ig4b2yTi-5jDhYhPtl=$u(Ym%s}28oX)2>jPVkQr6dD_oY%8xJWLcRxURTR<#-iai z=3N$V|Mx@wc+W(#;!z^uFi-sbdX7B&U=LiDwZ~xDUci~%f+H<{Y+Dv<*Kgs*TOZ@4 z*Jn_WpW_h@_rd45Py1lvC%n1YF`rrR3n%C$94TT8ZX<2Igqc^VHr{u=8x zY@@IsH?y(5KX8QYjwVeRz_Pt%+u#4?86N-pb-1o48(v(L$CBmii7JKBx(l~~JTa0( z`uA$J9QSe=Ln`HR?G5*_wW1o=)!g#y%NTXW@HXdMOy(QrK+Zm`uYTQ z^^K%bZZpomd);phO+N_0Gmg1XVIi0J% z^CjHfE$=Se}Y5%_vXygkLSEGpXb5HUiVK$k}d;SBH=LP`2O_j zmEU5MHC_5op7J60J@PW~m`7s#{cpd!+creHSC*zFy+3OaFTXL1sWU%er?2?!Zw??6bPwugmR^raE)O zOZ#n$L^8!?Ke~e#UYpKcxBY;_2KJ-6y1w(rD&pl|k!YANoOW!BJ|NAMxc#n)tX#91 zq~r3nFP+LcXML_!aJjC|DBetX;XO|K;?KDB2Y=$d86OjkhRM(O^jym;Yp8Eb`Xeiy z$@6-W$)L|&Zn{J%(ACt{HS*K{cRnLdI-38x@j)u9YO!sr)B0i}LNe*luTLS9pSz9h ztZ1_knVyf%obxFcTy{%Fn`_d&8CJ{EwG&#)xA%>kGp; z;;{bY<;2m(uz5=*Gv|KF3$M;##p+F+#(D3NNq&n7v{K~d#`)tt|Ka2lhA{G!qnY&f z+)iz}dh+23P8vRh?5t=D)M;-Xdiy;SK|D#y%P$_o;e-0NO0!l1|1JofZkjQYYb z9=!9%^e)cZ`NfZhv(FgL55D_lZoK6&o_*zgaK`haOO7m=46$8|MVUUCjC6m|%ZNTzsV!rN3< z)$*?gewo=9$n0+l%@!?;F%;(KaNnP=p}ew||4#py?5tRi@?RtuD1EhVvi9GJ?TJ(>MW5b<3^=5BvxSOu_?tC% z3A5%cB`Y4IvZ|JEoOjyxYA@}HjNj@=rWkSZQOypEXiJe|DX)Ka{Ln!R9@v-VD>o30 zgggA8t!~`OKX8x4OgS!n4k{o!JC*@TQ?}0YuT3YJbclpq4(?aX>8A|cX<0b^5ajzC z(%Q67Dag~uH-y7B#f7;n`D`61zr5`cIoJXE#=I3SO|PO{Z$@goRefVT`J{%%BvWTD zBp#1Y-;m&}FC0%ne)cZc+e|A{fHogtjY%IFaG*Zd-6KSH6-mdK?Hf)elTLX+TeM^i zt5$Cz77J5Xm*AvfLz*>SJBm@9mbh$aNU~*XH6h#H=b~^2Mx(X6smATF-#Gu16*M#^ z5D~&*o1sS@(n*!GH~ju=QRZly}6Zg+K5(wH92LQqdhio=%D)$bN%87e(N8e-D)HYx#h3jbYa^l2d z;SOgkq$xblJpUexm#*~}cHJJ7;vc{{m@%H@(}qnIEMB_S-^ejd4juJ2oxDYr2qB;E z)lnHkx*FZIrINcJdWo!fq+4jFc0ItPTW&jb$fYdrDC4)MezX&CG6X`|9yaM@M8{?M|0Ekl&s$hFCPrvgPY}`q}q<`m_hI{(TF*fwvMG8iOVv=ykW?N+~KfUQ;l1T><_}p=a zk&_+ERFoB!wQW@uXt5pP$vIg}dS@P2{p@d;wEDB@x!~#{S-XB4?R8;k+D^)GDZBk&+;Gd|M8Y9LmgQ-y zHhKM?`lWhO#_4tKvv}*h`F!P)TTqs8&RN5$uB~V7hHdQ&iTQF-_^Lniz~gU_ zpPPj!*{*XMTn93nqBXB$$j{4W)tW6_eck;WHsBzlkq8?$RkW#*pKhR9B0Tr%495NG z8Mc;JQ&gDisWn264~zShGTmLy-;Vp8^<^tIV%rwgH4VFkMw-W8SD)bKJ0|eN#CM5B z!{p|~n}92@PE?uRkE%=UF_7PMlNA)?XJa&I*C2t{ z>RFWO_OvlHke3&4vC*0)AX=7U!VA+F`^TrLYe+N?i2=5P5V^a__z{EqwVH2ftr_>{ zi9Gbg8)Roi$&N>ZkQh*=+n)4iWyKhA(vi*B6zR>Qn%V|_a^u6i`qo^E3VoHEpug1( z#838%#SjlbXPi2eqlWa)DCwm4BDYo4@V%e@jhSa8Mt|2yYo zWY&#Oo35y=<-6D3!@LD6C@M&UD?nB-Dr%!ymZ!pa-slr488I|7@)4lEA;DEYzn^&v zyh<_1w*n8=?nR?i%B832%^8_QDs{n6KX_LZ6RD#|70xJZz21tGFsp-~C-4M~O#9mu7nC0>Ko-}e4s z{%72I|4S6+XM3Fe03Sh!Y=3z4&K1Av(lMTVl&@BpNILxP&lA0_yeN#(L1+v*`Rx}( zX=qGxUijh$z;W&xH@Qb1)JUM`H2jXP>3^5Iog{+8iOsugy*NRdhHep3$nZBh`}JqKT!3; zG{r?Z9C27*1PIv{4Gl>qzBH3qG!jsp1M!o+0GJX|t|v&BJ~=}v;oa#AS-N5ac{#p7 zZ#&Kr3M}wo&5rbQU6zWB?%DgZKV?gKwSP!z0EQq3+uINpWT>X-X|-GMnb#f;|CV>!P+56XR&cp1<9ntvK1RyzoCL?B-G#1)@5|2lElxAvIf9$r15^u2EGZK8)H9Yge`_$DZuxu41_nk{D z5IN?I>((k|^*6>fJt5OgyKBo*I04_Q)7G>^EYaFsXDisKMD^Fk?Gd3e{RUk}@8PK( zfwcu;N|b=R(Kbp}h*BrgV^{>H$w6Gf?6I@>@eY%lXKgL2)ZHd?<4peQR-3#QyYQ|r_m6^e)fH+T?(Yfml%t_np& z>(rbDQ*NJ0S(#EL@1j7ALdILllAf-L9DMDaqPL|iQOI}@Q}Qm#qj@5nSn{(X$BHf% zkwRnKV0SC9JsA&Sj48L>#-rv;xpNyB&wG`YPEd2E+_@EF?zKWuF^C)Z{NQTW!DEPP zg`$En_s*Ge=ho8F2@1scUdsFP{1PrT?e2bfTI)`48K~aD!z>1VjfpMex0yr;{Ir*Oe7M*-6zqA_2&Hc2ch$I~2FIo1EPU zUtBnXO2&alnt*ly%m*3c^l_)(P!NhoCs}s*G2(+OD5?(JLeaa& z#$a$mp)A{VlGD=QDl6MHxY|KV_kK`GNts;};a=O0UhO)KXsv@jSr6!QDWxpSj^a3t z59B1c#!KdR53Y7@J(MXJYe3A2=lr5<#u2f^4#jP4 z+{u{KnR6!HG`_TSg5~+7ae7a7xY}$@8)_$<^SdjQaImQvNFZku?7)y{Ft)dA!YwigdbRT_#mxa zw39GK#1&6CPzB{?w^X#!MlfQGrR<0(W#Kx>O~!D4N;`l5=*@APF-90WRv0%<@9h@q z9$C{4?kS^gJxE9Rnlbuv5j8^DVYGHIZVF?xF$V36kfe!@k{)#i)>-CPfrv4_Fs%qm zM2T1-l(NxI5@Ymq5%s7_@Z!w3Z``!wz3qrK=e>Ctq_lLRYU)ByK6l&*lr>trdJH1N zlv25(EH8C524h@$imFd{I6gTg#y#X?C*nKfm~SeFizk@)}ORT#a$*y4}?0000iO(Bj-8m`D7E9$j*Qf zWuV2KmtPbzv%dCHM5qJRs3n^kHARz8Q+#~>oLNjR+Pi;rA|T0}K>qr>2Rn#OkCq+Z zkWIg~oWZ3WzsaOtI2*%HZjjl$e=YyC60+eR)xb;>dF+$RXnT(O0HOh`^iUBH5C*Fc z;J5jor#g1=$|R-AdtHvk?4X2Kivx9DxX_klr0@i72iLZARS%`pI%HYu)K;o}X&wH- z$zvhiY*CGx$Jo6X&0)~) zFWYusfWgkuIB28GP;T5hB88CD+^?zP{^2NZ{T-{0aL2d%)f+b*h5TAsg z7$MR9xEUU^mR7=H5TmIH+@2%i81dK{NXO32VO!eQHLua}Jhhz;#@(HDVzB>eL>J8I ze3`Z32BLf#e$&e~RlSX7%JJMi39R=`o@Hgr zz-E3$@tDOx03%XGu1+%OVtm87mcu(%8BX7TK?0VeB$WqqYC&b<1mKUGzqYj?#&zkj zNFj5~8y~Lhgo7`TnovE>DM}y_a#gKUqp{bDKowOuC8yp?}c_cfZ=J zygUt-QncwGP~vzr-*c(ZmsMYVcgy5mmqA$OPK$;n{h_{G(q@=vvVU>~@a6SrF<-}P z&2m<9nwO8UL32NUa=p4sYHlhWA~^_`tKa(xj?Lw1#Os^H`^@^IE0gcCcjX5ls^y?s z;5yk3w>g?Z>FJSJ!D34CWwa9uSASUgbAgzBE{*Hx7*C(%32#}9~Ij; zV7cJb=o?h-pbW(}kSO7D!83W)d;BTpF?GdGya@s?w zgwM6uzw>89MyKx>y^btIdf1buI1I98qm#wIC#}Z&0ND)o&s*59?8iA6?WbBK3sc_q z5wJGYA7BI;9KX8Z*SAC4ePo%D$V=)+UN0fH25TKLlWxC*w@q8`F{5ukDBHJNcu9Gy zAep%#aOoi}MNhuM^bH@k+0oiS=~(#SVXMr^UOyM)`^;rV)~6;rxGAsYkGxjNH8UZ8 zOntAHPL;SR0=EQT^cmqttje}dmT zX@=6ZR=0>?Mg(uo_>1nglS4Qkjlzwql1qiqo(MGkjLHUcoBJ+*bIbJb=GF3WvL+Wt z|F(m|GVo_o@g|e--`Lds$@)L_`o)p?bx^j3^EV0O`ZcHG^3-2Eppm|3c)M0MsK2Z_ z{?l7M)Zyylr4cr}mKG5EAhrTXY$}GBc9vk%u5fVoL%@uAkIhPi?*UU$@9LwW3!P^K z>KrrrW3WMtW{peO1xfYBRjFztJeH>PV=ZmC8Osk-o)6%wBdA zind2Nv{UXJ-yduFGVq_p956<8Xz6qNjiBHb|96Mf0FPHp{K^LDYUk8re^_u3H6WTY zf`@QyS0&{+Xuyjy!r#=&8j=Ac1MSp@E(goe-U%F$%7#fLmA!6b_f1gK=DZDGAxoDX z`y|uq5BVvdV{V1!;I3jjDIyJ^3a!Vt;5*vVIU{bv124{fX*xpmV2pkfPUYD$Y0*sZ z4#s@A&+{1R{@Oh#Uy=Yc@DW5UZU8IPzoz#iZRtcJ(JZ`%JiX03&V7)qn7$^Ph68%W~1dH zPpITe-3!fFmZgU)6vPuh`98qqp?Do~ne;vTlqpVwpEjD)4(r2(mhO1bz#=fWh5`t~ zALjvG!Oa(f6IRC+HIFI!c@q-vIf)_74G*2y=OWU?eNA(aAhr!9KAKWtjH~T#oa56& zrZ|3uB3Yz0B!c(U0d>|~k0L1GMV;r)efstkLQ^UTPIUh|BWTH+zR=TO5rYNX(U88u zAY04Os0(NBhZ{lAEMx6+Beim6``8?O&=G3#+wBe3;%C@rX1!%1q+fg|R6kkifDgW0l1(rUT9|xp)8EBKwiXxW z4~zSApol?8$*H6(B5%lVbhP)uAli8j-64 z=GLfbzm4MO)S$t(90<4Ss@V^>u`e2=*kGwB_Ktx`v|0n`Q))s{f4=+bC@5jO|XfQ&#fCOj>3MM|Xp6)WEFq zLdh7#IG0Ukb8vFCa_em>?C$8(389ZVhEGL zXn|LRlf`UUa5kfi3~dWA-%O^@V8r#NdUKO?E+nvK27r&Y^&eYm+f<$hFyqq$VmV#E z`_rnw%y!&BaQJyyM+XOn#H;yyJG-;5!BdzsHSDmL_49kDc}k~#jd9y#W!GO}mbp_y z5Varl{a|HZW7zKSfFSK~_GdTUnJogZrGtsebS)>yQ)^591r9Tk^wxQ!*cF8e&49f$ zsVi^e=6V`yykr}9tA=dhnQ}=x>8A*_kozE>xUF!}Ao}d=$I3tt;myV4mktJsAjIFl zH`E?9TtD8wbD$iQz3~+JkW_!X7lVC+RV(6KXA};86@Eo<{nTudCT8v^k=j~ z+08(5fzZhBr7L(({B`!dL^6kc~9giyI2VR3ICH-J1^$qJZU9< gX+-(|huX*NQ5iSl{}N?dpMDuEX#HDyWF0L2zxt0H3jhEB diff --git a/public/res/apple/apple-touch-icon-167x167.png b/public/res/apple/apple-touch-icon-167x167.png index 3f26f91b57890d8ad2c7327b6c3c2badd7970d2c..963c024d6a1bc63f6ea8168c590f84ed03a5c091 100644 GIT binary patch literal 11346 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 KLTbRSul@(HS$PEj diff --git a/public/res/apple/apple-touch-icon-57x57.png b/public/res/apple/apple-touch-icon-57x57.png index 2e2081f5f6c211a342eb4f6e27e6e25eeda7f0e3..5cad75b275f1666ab48515772539ca73305ce64a 100644 GIT binary patch delta 3473 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 delta 1169 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 delta 1242 zcmV<01SR{e9PJ5^BYy<6NklyjoN`h7~)u0VV z(wY_{+Mp&%Jn+a?u?8ch#e=q9LB$_RX{#WJkWw$udZD6GlxU1sZDUPU?5~e}C%((< z?EAj+PG)vzo6HYi!tDFboBix_yw7`#@?~bxNYW^`zkmnq0e^eI9?PfjkUK{@_&@10&kcRpu4N`xw_h% zr-2U@7=lGvJAdL<-)83ejswGzB3QjT=LeOn&0~|A zW$N8j5q~#FvoR~j2TWm)HWgDwMg~QrQEIEni)%|Nu&B^@xMh+lFg!s24l7D#l?IjE zcTr`IX5|ZO%Hlr9`zZ$&#jjDw;Kpgn47jhOie7eg(3=}S(tkZC>Fw=f+VgL#Bbv3X zmar)WHYPCC;TTs2`}*=h4b-SP)m56CXJ(F0Uw>ezXMIuZJ>&$8`~9ayT;OMW>bbDc zsh@XHX(5u|*wh0fA*ljC?x>8tcW!xzx>l44E&TYKnL=P_+LX!3OxT@h3@n?6Re>Q@SfhIb9)Fk2D6s{r42+DZT2_kGzjyACswxPv zUVj=?Ut`+__DNPssHV(d2M&KieOn_#f^`biGO%djuM)?9eK%VP4$~15meIPd0LvEt zvU-|jXfx1((>r3x3B|^f-Gd>3dfOFX2(_@)`55kOW?5d5fsD+cQFprnEOP66YeO_} zsGbY_Vt=g^w*S~V;e7k*{2r~(;;MXifPbeHh^Hr!mCb-ztt=9(Y-jyz3)p9I=ff;v z0Y(N3!2?_cmRT8vh0(QT7Dho~1`M}$6&S*%oGP>oSQfu}VAo=p36+;@%N8r6KX=X? z%+iPIZ3DZOrBz+qs0Uy2S4OgBAXE3R?T*3P0`@8FRqHJ-Dmcu5cdh*_eSob%_kV7# z;6lS?MX=AWAEmNTVR`}k-Y*}`j$y8~j<@XA9UGTB3hNlK?|mY}&Ik`26&m)juN(%(K{eXxdDKL2CGB*-Nm*K0}02U(3PbH z)VI4%+m*ca)okJMY1#sd#DismwNuZwlnC3MxX_YvU|eT_wRnfz>iCJmBFj4|urC10L>r2TWBkZ1(EZ6#xJL07*qoM6N<$ Eg6OnkE&u=k diff --git a/public/res/apple/apple-touch-icon-72x72.png b/public/res/apple/apple-touch-icon-72x72.png index 32d5d51646aed74aa40cb39a4af3161802fb74d2..7aa6caab1206aa1727999a4e983d1d909f52102c 100644 GIT binary patch literal 4503 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 delta 1511 zcmVw*C@BjlSb{c*nOwX)#W}H?)nIM^? zj0vgfIUvAc5hEk4jV)7)CTnC^BgE`GW&y0{fRIA6aLm!Pa=E-eSRed+dkOupvz*4d zDrl%JM&~-BG}2K@XWNQW*11{9906)f1eM`9fy5s8`+pr}G|_W2P42tJRzK~ErGWAj z5KP!9AZFqs^G3zO10z~^V9Wx<;jP84Rx)KkGSM@>yNrH)=?+5^dn+@pIVnhkHKjos z-;Z;((KzC>H3M5q-Kt+r?ICZX=}ID*jJku?k`n_!*r&- z!n+iMhkw|?0R$@?rr!q}jmWb4QKrfk4+R~=Vd_{@JokWLuDs|$N@aq>boGsfv`E4} zDx2sTYAdJi`Y?Unnd(*FbSCB=5O&5qKmfo41YLQpF70#JiJN~99!>@hpycJ{QGb6w zZQatC_C6wUP61&@%u6p3944!ATcb$YYqrG4z?0P)%iJhUtz z_>uMVW-n6ln(eDYv@W)U4sD8f4+y2%4M5Pv-W4gIH}`8YU@r94(AAH7=&$eIr+>yj zHT1_Q3k}T zVom_1nDx%(H1c#z%75T9JUG+mhbR$^`wI}H3JAjP8e4>ko=TXOO;lat;jxdX)xa)U8Bkxf zBRE#U*xlGUX9XxAXjbvCDsZL`uFDz>n$lc=aSVtz?tq`j!kK;N0lM&9Rn~wWsVT}C z97ljK!^yT`g{~aBTM5MFqZ4}h543D#1;pnusK zU!@qtJMQ5P&CKZ~3&*ad`19PcBqXFl$~E%^bN$2nYKcQ9A)4if~5eD?kCkY-m(4ew8WjvtPU_1qjCNOF$yC=2bKq zK)i8=SpexvKu~WTzc;f>HJK7G1Am8AG~WUec@cz}j}?;0q_O9b1q2;$jrtQ%K;fhn zhldQ%wPVe~@~#4CPkqF{fC3VRZH^{v5HULvjW<+M%#GoEP64Uj6*6FTMY({iyJtIH z{OI|d!Ep_c6G&s7#auw5xieF5d&~R)ZTxu&2=|^vfIfSy)L=MFG~cGrq+CRi=Ya5E=Rq=mdk6OpGeN>@Zr-l<6(A;j z-vR)Tau9B*+NBKMlUet_0_0a|KpKz+qycF_K9&Zg0ck)QkOt&q{R`aM)sM=ki4Fh& N002ovPDHLkV1h$`;%5K= diff --git a/public/res/apple/apple-touch-icon-76x76.png b/public/res/apple/apple-touch-icon-76x76.png index 1164b72d4d511247bb3685036e3858b7de7aa3cd..1448098d13759d4c902b5288fce71e367cf64e37 100644 GIT binary patch literal 4748 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 delta 1590 zcmV-62Fdx1CB_VpBYy@BNkl_ttFlQorT6j%lXnLSLs{Wag4hdF!q z?mg$;Jqvf2^Wr<;?qRQd_&uL<7Nw@7k(Rs`&G&yG(WHTCV1F8zr=)>tU>caGq=9K* z8knb~foWhGm}~j(Sh6sI)#Rnq^K1N$ex^Lj`OhR3u#dN7(_>YcQtM$^XE$aUTF}6t zO{E#a^{_0qenA3*WibE{07hF2p!{06rYC`|DM+V-_Xp_xj%>R6c#ffSZCSLp&QHP2 z*n+Pa*t-u|gMYwlc#mJ(P$;&@IRzHDa|sPK2WBBf6JWXpX9mWg@mY|?E%Q3rxHl)j z0PCGlmJk-x=bhOUDtG=DA8HIlfPesqLF3~VuS07C-T?-elZC}==e^F-y&2di9XWJ) zdp>>h#BzFjO92fu=F?Z*%jmOhxt35p0Sv#i8(6%HI)4QQP*-*YX{^7D#-CkbscTOc z(-)n2VbJCs7+ewV6$>y();;k{!=mg!nFX)_tU6P=0w!x+qfZsk*zQug_GDhvHOQ`_ z=fT}%LGc>hRYI5AbChOESHSqv!Ab!1>ybM8VJAl$G|Fc(}k7=I6N$-Td%dy4}C#L@oZ#!aZJFVx*~O%(lM~E>P%Z$a6v0r zJT$nfjB9Qz&yE=u^hT&~{(y;!)M(bl>$j7Q5$=f+#r&*etp(ChM!JGj=wD3rwKa6K zH80}*K$@Ds?--b<_PRN=K?n?**k2>GvX6R7q88^3d3``X4 zWneLxUDIr3PTX!_eXaov0zC8kyVmA;511%^nKga9KH@X@e$#2JI@x_6?cY{QR}VGQ z^?~Z-0|uG1CZ-!sfz4dlLH~Svf&Lx67^dGx2I$!4VyX?M(Ootpp@ag4Bw?=0*t5=e z1b>CFZe2PM0g4xMQ=cy-Ie?+eFlS8EdU#g%s-?EDZX9l~g#|qlT5kYL@hwgcV9-x{ zE5beptrseMQCTrD*dhc5?d)n-0!ukCWIZ>)&_>N>+McEK<1_Nr{a>HHY71-X@_EYk z=gc24Q61(67~GPTCF6VKClU~{;h!H4T7SZt_}~<+tgKK5%M~zDrH?A!4YaY{C5~b7 z`uXM6G=1(-BV*$0cl*{EZmklQD`2A0j!bJ5VPUU7K-ZrucMZ!d#6DH6P|UzKlw{Zf zLjZRQ3~f@ZkiPCKGm6uqfZAQ6n1Nw-rLsM`In<=YssJorRuvO4NVHSrB#ybUE`P); ztJhJtc@h}f7gcWQ1Qde#-(Q}Y&4S2|C|=q77&dwmm}u$>w?wHRf;DrZK^hkNLaWjN zJtPk;-fro7n@u#=m&uk>hij!^O`h6pTbu$Kn@*@4M=J#;8db|w{lDy6DFw`G5@8nV zuW^NA9Ob}d_9hX~?SS2Q@opiksejWQQvD*JdKwsn-5yaavm05<027UG!djuh7H1TaZ67FOO@OOk5*>L0F#B4Fu)MYL>V(0w8;ajlmdhMK3uJCloJ)s(OQQlq%OeBp%@h4` zhZPrKOrnVfukU;FZLJK!Tm-D7uqd&CrQXh-B5PwOt8wtn&dTg|hdM(oa{?>wz<6-5 zI0g@p@I4xM{`BFQ2lI`ytbbM~rbqWwgtbXC?;W8lFExd&eXA|YIF}!fTWoU*Oh$e8 zSf8`!g=6N%?CWT&?K+&gPI_Qgw>ZqgadOP=FNPw|@%|7$Q- zcZztyT9Y0agwxJj%QKDZE^sRd-{UoNT*ANcI$H+;l-*6Yqyv@+(l5X?Fb&L8(!ew@ o4a`&0z%(!o%u~|9G_d&UKc&>qycpq$2><{907*qoM6N<$f+NBE)&Kwi diff --git a/public/res/svg/ratatoskr.svg b/public/res/svg/ratatoskr.svg index 3ca98122..1092ffc4 100644 --- a/public/res/svg/ratatoskr.svg +++ b/public/res/svg/ratatoskr.svg @@ -1,15 +1 @@ - - - - - - - - - - - - - - - + diff --git a/src/app/features/scene/BackgroundScene.tsx b/src/app/features/scene/BackgroundScene.tsx index 10785a5f..0c8d0fb3 100644 --- a/src/app/features/scene/BackgroundScene.tsx +++ b/src/app/features/scene/BackgroundScene.tsx @@ -1,8 +1,9 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useMemo, useRef } from 'react'; import { useClientConfig } from '../../hooks/useClientConfig'; import { getFallbackSession } from '../../state/sessions'; import { mountBackgroundScene } from './host'; import { readCapabilities, resolveTier } from './tier'; +import { getSceneStyleOverride } from './sceneStyle'; import './sceneShell.css'; /** Set on while a scene runs, so signed-out screens can let it show through. */ @@ -16,7 +17,11 @@ export const SCENE_ACTIVE_ATTRIBUTE = 'data-scene'; * Tier 'off' renders nothing and loads nothing. */ export function BackgroundScene() { - const config = useClientConfig().backgroundScene; + const configured = useClientConfig().backgroundScene; + const config = useMemo(() => { + const style = getSceneStyleOverride(); + return configured && style ? { ...configured, style } : configured; + }, [configured]); const enabled = resolveTier(config, readCapabilities(navigator)) !== 'off'; const canvasRef = useRef(null); diff --git a/src/app/features/scene/SkyStateControl.tsx b/src/app/features/scene/SkyStateControl.tsx index 5a9d84d6..4e67bd81 100644 --- a/src/app/features/scene/SkyStateControl.tsx +++ b/src/app/features/scene/SkyStateControl.tsx @@ -1,9 +1,29 @@ import React, { MouseEventHandler, useState } from 'react'; -import { Box, config, Icon, IconButton, Icons, Menu, MenuItem, PopOut, RectCords, Text } from 'folds'; +import { + Box, + config, + Icon, + IconButton, + Icons, + Menu, + MenuItem, + PopOut, + RectCords, + Text, +} from 'folds'; import FocusTrap from 'focus-trap-react'; import { stopPropagation } from '../../utils/keyboard'; import { useSkyState } from '../../hooks/useSkyState'; import { SKY_STATES, setSkyState, type SkyState } from './skyState'; +import { useClientConfig } from '../../hooks/useClientConfig'; +import { + getSceneStyleOverride, + SERVED_SCENE_STYLES, + setSceneStyle, + type ServedSceneStyle, +} from './sceneStyle'; + +const STYLE_LABEL: Record = { terrain: 'Terrain', ridge: 'Ridges' }; const LABEL: Record = { auto: 'Auto', @@ -19,6 +39,9 @@ const LABEL: Record = { export function SkyStateControl() { const state = useSkyState(); const [cords, setCords] = useState(); + const configuredStyle = useClientConfig().backgroundScene?.style; + const sceneStyle: ServedSceneStyle = + getSceneStyleOverride() ?? (configuredStyle === 'terrain' ? 'terrain' : 'ridge'); const open: MouseEventHandler = (evt) => { setCords(evt.currentTarget.getBoundingClientRect()); @@ -62,6 +85,32 @@ export function SkyStateControl() { ))} + + Background + + {SERVED_SCENE_STYLES.map((st) => ( + { + setCords(undefined); + if (st !== sceneStyle) setSceneStyle(st); + }} + after={sceneStyle === st ? : undefined} + > + + {STYLE_LABEL[st]} + + + ))} diff --git a/src/app/features/scene/sceneStyle.ts b/src/app/features/scene/sceneStyle.ts new file mode 100644 index 00000000..7c31233e --- /dev/null +++ b/src/app/features/scene/sceneStyle.ts @@ -0,0 +1,28 @@ +// Temporary: which background the viewer picked (terrain or ridges), kept in one localStorage key so the two scenes we +// serve can be compared on any device. Absent, the client config's backgroundScene.style decides. +import type { SceneStyle } from './tier'; + +export type ServedSceneStyle = Extract; +export const SERVED_SCENE_STYLES: readonly ServedSceneStyle[] = ['terrain', 'ridge']; +export const SCENE_STYLE_KEY = 'ratatoskr.sceneStyle'; + +export function getSceneStyleOverride(): ServedSceneStyle | undefined { + try { + const v = localStorage.getItem(SCENE_STYLE_KEY); + return SERVED_SCENE_STYLES.includes(v as ServedSceneStyle) + ? (v as ServedSceneStyle) + : undefined; + } catch { + return undefined; + } +} + +/** Store the pick and reload, so the scene host mounts the other scene from a clean page. */ +export function setSceneStyle(s: ServedSceneStyle): void { + try { + localStorage.setItem(SCENE_STYLE_KEY, s); + } catch { + return; + } + window.location.reload(); +} diff --git a/src/app/features/scene/skyPlate.css.ts b/src/app/features/scene/skyPlate.css.ts index a3c0c567..db168251 100644 --- a/src/app/features/scene/skyPlate.css.ts +++ b/src/app/features/scene/skyPlate.css.ts @@ -7,7 +7,7 @@ import { MembersDrawer } from '../room/MembersDrawer.css'; import { CallStatus } from '../call-status/styles.css'; import { RoomViewFollowing } from '../room/RoomViewFollowing.css'; import { RoomViewTyping } from '../room/RoomViewTyping.css'; -import { SKY_PLATE_FALLBACK } from './skyPlate'; +import { PLATE_TINT, SKY_PLATE_FALLBACK } from './skyPlate'; // Over the scene (html[data-scene]) the signed-in shell is clear (sceneShell.css.ts), so text // outside a chat bubble would sit on bare sky, which a sky state can make light, dark or both @@ -47,7 +47,9 @@ const within = (ancestor: string) => plateRegions.map((r) => `${ancestor} ${r.startsWith('#root') ? '' : '#root '}${r}`).join(', '); globalStyle(within(scene), { - backgroundColor: fill, + // Blur with a light tint toward the sky, like the chat bubbles; never an opaque slab (PLATE_TINT). + backgroundColor: [fill, `color-mix(in srgb, ${fill} ${PLATE_TINT * 100}%, transparent)`], + backdropFilter: 'blur(12px)', color: text, // Themed ink was chosen for the clear shell; inside the plate every token reads the plate's. vars: { diff --git a/src/app/features/scene/skyPlate.ts b/src/app/features/scene/skyPlate.ts index 353134ab..b82a3026 100644 --- a/src/app/features/scene/skyPlate.ts +++ b/src/app/features/scene/skyPlate.ts @@ -18,3 +18,11 @@ export const SKY_PLATE_FALLBACK = { * on, the worst link contrast is 5.0 at Dusk, 5.7 at Day and 11.2 at Night. */ export const FOOTER_TINT = 0.82; + +/** + * How much of the plate's fill (--sky-bubble) is mixed over the blurred scene behind a plate (title bars, lists, the + * composer, the sign-in card). Meta, 2026-11-09: "The app shouldn't need chrome, and where it does help for readability it + * should be like the chat bubbles, blur with a bit of tint towards the background color. If that's not working, the fix + * is in the background, not covering it up." + */ +export const PLATE_TINT = 0.45; diff --git a/src/app/features/scene/skyTheme.ts b/src/app/features/scene/skyTheme.ts index 4ab3f43a..dbd46501 100644 --- a/src/app/features/scene/skyTheme.ts +++ b/src/app/features/scene/skyTheme.ts @@ -10,7 +10,7 @@ // derived outside this code (src/sky/skyTokens.js). Midnight Sky's link is derived there against the lightest Night sky // (4.53 on it, 5.43 on the darkest). The plate (skyPlate.css.ts) is still in the shell, pending // its removal. -import type { FixedSkyState, SkyState } from './skyState'; +import { resolveSkyState, type FixedSkyState, type SkyState } from './skyState'; export type SkyThemeId = 'midnight-sky' | 'noonday-sky'; @@ -21,4 +21,4 @@ const BY_STATE: Record = { }; export const skyThemeId = (state: SkyState): SkyThemeId | undefined => - state === 'auto' ? undefined : BY_STATE[state]; + BY_STATE[resolveSkyState(state)]; diff --git a/src/app/features/scene/terrain/terrainWorld.ts b/src/app/features/scene/terrain/terrainWorld.ts index 7296025b..64f18970 100644 --- a/src/app/features/scene/terrain/terrainWorld.ts +++ b/src/app/features/scene/terrain/terrainWorld.ts @@ -11,9 +11,9 @@ export const TERRAIN = { span: 3000, hmax: 256 }; * under shading read from the height map shows as hard-edged facets and stepped crests. */ export const GRID = { - still: { grid: 192, tex: 512 }, - low: { grid: 320, tex: 1024 }, - full: { grid: 384, tex: 1024 }, + still: { grid: 256, tex: 1024 }, + low: { grid: 512, tex: 1024 }, + full: { grid: 768, tex: 2048 }, } as const; /** The camera: at the patch centre, above the highest peak, turning slowly and looking a little down. */ diff --git a/src/app/pages/auth/AuthLayout.tsx b/src/app/pages/auth/AuthLayout.tsx index c53e5a65..0d5c9406 100644 --- a/src/app/pages/auth/AuthLayout.tsx +++ b/src/app/pages/auth/AuthLayout.tsx @@ -21,6 +21,7 @@ import { import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { LOGIN_PATH, REGISTER_PATH, RESET_PASSWORD_PATH } from '../paths'; import RatatoskrSVG from '../../../../public/res/svg/ratatoskr.svg'; +import { SkyStateControl } from '../../features/scene/SkyStateControl'; import { ServerPicker } from './ServerPicker'; import { AutoDiscoveryAction, autoDiscovery } from '../../cs-api'; import { SpecVersionsLoader } from '../../components/SpecVersionsLoader'; @@ -137,6 +138,7 @@ export function AuthLayout() { Ratatoskr logo Ratatoskr + diff --git a/src/app/pages/auth/styles.css.ts b/src/app/pages/auth/styles.css.ts index fc47dd79..f16617e8 100644 --- a/src/app/pages/auth/styles.css.ts +++ b/src/app/pages/auth/styles.css.ts @@ -1,6 +1,6 @@ import { globalStyle, style } from '@vanilla-extract/css'; import { DefaultReset, color, config, toRem } from 'folds'; -import { FOOTER_TINT } from '../../features/scene/skyPlate'; +import { FOOTER_TINT, PLATE_TINT } from '../../features/scene/skyPlate'; export const AuthLayout = style({ minHeight: '100%', @@ -27,13 +27,16 @@ export const AuthCard = style({ export const AuthLogo = style([ DefaultReset, { - width: toRem(26), - height: toRem(26), + width: toRem(32), + height: toRem(32), borderRadius: '50%', }, ]); +// The mark is white (Meta's matte); on the light sky theme it takes the dark ink instead. +globalStyle('body.noonday-sky img[src*="ratatoskr"]', { filter: 'brightness(0.18)' }); + export const AuthHeader = style({ padding: `0 ${config.space.S400}`, borderBottomWidth: config.borderWidth.B300, @@ -67,22 +70,35 @@ export const AuthFooter = style({ // made for their own grounds, not this card; Dark and Butter already use the same hsl(213 100% 80%). // The card is also made shorter so it leaves more of the scene visible on a phone. const scene = ':root[data-scene]'; +const tint = `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${FOOTER_TINT * 100}%, transparent)`; globalStyle(`${scene} ${AuthLayout}`, { backgroundColor: 'transparent', backgroundImage: 'none', }); globalStyle(`${scene} ${AuthCard}`, { - backgroundColor: 'var(--sky-bubble, #0d1a26)', + // Meta, 2026-11-09: no chrome; where readability needs help, the chat-bubble treatment (blur with a tint toward the + // background), and if that is not enough the fix goes in the background, not in a plate. Same tint as the footer band. + backgroundColor: [ + 'var(--sky-bubble, #0d1a26)', + `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${PLATE_TINT * 100}%, transparent)`, + ], + backdropFilter: 'blur(12px)', color: 'var(--sky-bubble-text, #b7c7d9)', borderRadius: config.radii.R500, - // A faint inner edge, so a pane this clear still reads as a surface. - borderColor: 'rgba(183, 199, 217, 0.14)', + border: 'none', + boxShadow: 'none', }); globalStyle(`${scene} body:not(.gamecult-theme) ${AuthCard}`, { vars: { '--tc-link': '#99c7ff' }, }); globalStyle(`${scene} ${AuthHeader}`, { backgroundColor: 'transparent', + borderBottomWidth: 0, + boxShadow: 'none', +}); +globalStyle(`${scene} #root header.${AuthHeader}`, { + backgroundColor: 'transparent', + backdropFilter: 'none', }); globalStyle(`${scene} ${AuthCardContent}`, { paddingTop: config.space.S500, @@ -99,7 +115,6 @@ const FADE_Y = 'linear-gradient(to bottom, transparent, #000 16px, #000 calc(100% - 16px), transparent)'; const FADE_X = 'linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent)'; -const tint = `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${FOOTER_TINT * 100}%, transparent)`; globalStyle(`${scene} ${AuthFooter}`, { paddingTop: config.space.S500, paddingBottom: config.space.S500, From fcabaee59ed17a552e198a06a5b01e9e10f319da Mon Sep 17 00:00:00 2001 From: Eureka Self Date: Tue, 10 Nov 2026 13:22:41 +0000 Subject: [PATCH 14/56] nemesis: chromeless buttons, chips and room list; plates only on the composer and plain messages Co-Authored-By: Claude Opus 5.5 --- src/app/components/nav/styles.css.ts | 9 +++---- src/app/features/scene/BackgroundScene.tsx | 1 + src/app/features/scene/sceneButtons.css.ts | 28 ++++++++++++++++++++++ src/app/features/scene/skyPlate.css.ts | 18 +++++++------- 4 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 src/app/features/scene/sceneButtons.css.ts diff --git a/src/app/components/nav/styles.css.ts b/src/app/components/nav/styles.css.ts index feee3276..e5db6c47 100644 --- a/src/app/components/nav/styles.css.ts +++ b/src/app/components/nav/styles.css.ts @@ -5,10 +5,11 @@ import { surfaceInk } from '../../features/scene/sceneShell.css'; // Wash strengths over the scene: rows and labels are faint, and hover, selected and pressed rows // are three distinct steps above them. -const SCRIM_LABEL = '12%'; -const SCRIM_ROW = '16%'; -const SCRIM_HOVER = '26%'; -const SCRIM_SELECTED = '36%'; +// Meta, 2026-11-09: "this UI needs no chrome". Rows and labels are bare ink on the scene; hover and selection keep a faint wash. +const SCRIM_LABEL = '0%'; +const SCRIM_ROW = '0%'; +const SCRIM_HOVER = '14%'; +const SCRIM_SELECTED = '22%'; const SCRIM_PRESSED = '48%'; export const NavCategory = style([ diff --git a/src/app/features/scene/BackgroundScene.tsx b/src/app/features/scene/BackgroundScene.tsx index 0c8d0fb3..c447b4f0 100644 --- a/src/app/features/scene/BackgroundScene.tsx +++ b/src/app/features/scene/BackgroundScene.tsx @@ -5,6 +5,7 @@ import { mountBackgroundScene } from './host'; import { readCapabilities, resolveTier } from './tier'; import { getSceneStyleOverride } from './sceneStyle'; import './sceneShell.css'; +import './sceneButtons.css'; /** Set on while a scene runs, so signed-out screens can let it show through. */ export const SCENE_ACTIVE_ATTRIBUTE = 'data-scene'; diff --git a/src/app/features/scene/sceneButtons.css.ts b/src/app/features/scene/sceneButtons.css.ts new file mode 100644 index 00000000..301d5671 --- /dev/null +++ b/src/app/features/scene/sceneButtons.css.ts @@ -0,0 +1,28 @@ +import { globalStyle } from '@vanilla-extract/css'; + +// Meta, 2026-11-10: "buttons are big and ugly, too, they don't need chrome either". Over the scene a folds Button or +// Chip is its label in the page's ink, with a faint wash on hover and focus. Menus and dialogs (#portalContainer) keep +// folds' look. The selectors are folds' hashed recipe base classes (Button epr39zd, Chip _1bv8u1w6). +const scene = ':root[data-scene] #root'; +const controls = [`${scene} .epr39zd`, `${scene} ._1bv8u1w6`].join(', '); +globalStyle(controls, { + backgroundColor: 'transparent', + boxShadow: 'none', + border: 'none', + outline: 'none', + color: 'inherit', +}); +globalStyle( + [ + `${scene} .epr39zd:hover`, + `${scene} .epr39zd:focus-visible`, + `${scene} ._1bv8u1w6:hover`, + `${scene} ._1bv8u1w6:focus-visible`, + ].join(', '), + { backgroundColor: 'color-mix(in srgb, currentColor 12%, transparent)' } +); +globalStyle(`${scene} .epr39zd`, { + fontWeight: 700, + textDecoration: 'underline', + textUnderlineOffset: '0.25em', +}); diff --git a/src/app/features/scene/skyPlate.css.ts b/src/app/features/scene/skyPlate.css.ts index db168251..4472b98e 100644 --- a/src/app/features/scene/skyPlate.css.ts +++ b/src/app/features/scene/skyPlate.css.ts @@ -24,20 +24,18 @@ export const SkyPlate = style({}); // A recipe's first class is its base style. const base = (recipe: () => string) => `.${recipe().split(' ')[0]}`; +// Meta, 2026-11-09: no chrome. Title bars, the room list, list pages, the drawer and the bars over the composer are bare +// ink on the sky (dusk is allowed to be low contrast: it is the crossover between the light and dark inks). +void PageNavContent; +void PageContent; +void MembersDrawer; +void RoomViewTyping; +void RoomViewFollowing; export const plateRegions = [ `.${SkyPlate}`, - // Title bars: the room header, the room list's header, every page header. - '#root header', - // The room list, and the pages that are lists (explore, inbox, search). - `.${PageNavContent}`, - `.${PageContent}`, - // The composer, the members drawer, and the call status bar. + // The composer and the call status bar. `.${Editor}`, - `.${MembersDrawer}`, `.${CallStatus}`, - // The bars over the composer. - `.${RoomViewTyping}`, - base(RoomViewFollowing), // A message that is not a bubble (Modern, Compact) is plain text on the scene. `${base(MessageBase)}:not(:has(.${BubbleContent}))`, ]; From c4689299ca191648a7694dab05d03182634deab0 Mon Sep 17 00:00:00 2001 From: Eureka Self Date: Tue, 10 Nov 2026 13:46:51 +0000 Subject: [PATCH 15/56] nemesis: tab icon and notification icon are the night-sky tile (unread and highlight add a dot), not the white UI mark Co-Authored-By: Claude Opus 5.5 --- public/res/svg/ratatoskr-favicon-highlight.svg | 1 + public/res/svg/ratatoskr-favicon-unread.svg | 1 + public/res/svg/ratatoskr-favicon.svg | 1 + public/res/svg/ratatoskr-highlight.svg | 17 ----------------- public/res/svg/ratatoskr-unread.svg | 17 ----------------- src/app/pages/client/ClientNonUIFeatures.tsx | 14 +++++++------- 6 files changed, 10 insertions(+), 41 deletions(-) create mode 100644 public/res/svg/ratatoskr-favicon-highlight.svg create mode 100644 public/res/svg/ratatoskr-favicon-unread.svg create mode 100644 public/res/svg/ratatoskr-favicon.svg delete mode 100644 public/res/svg/ratatoskr-highlight.svg delete mode 100644 public/res/svg/ratatoskr-unread.svg diff --git a/public/res/svg/ratatoskr-favicon-highlight.svg b/public/res/svg/ratatoskr-favicon-highlight.svg new file mode 100644 index 00000000..f9a95e82 --- /dev/null +++ b/public/res/svg/ratatoskr-favicon-highlight.svg @@ -0,0 +1 @@ + diff --git a/public/res/svg/ratatoskr-favicon-unread.svg b/public/res/svg/ratatoskr-favicon-unread.svg new file mode 100644 index 00000000..f5bcb7d8 --- /dev/null +++ b/public/res/svg/ratatoskr-favicon-unread.svg @@ -0,0 +1 @@ + diff --git a/public/res/svg/ratatoskr-favicon.svg b/public/res/svg/ratatoskr-favicon.svg new file mode 100644 index 00000000..0c7cc393 --- /dev/null +++ b/public/res/svg/ratatoskr-favicon.svg @@ -0,0 +1 @@ + diff --git a/public/res/svg/ratatoskr-highlight.svg b/public/res/svg/ratatoskr-highlight.svg deleted file mode 100644 index 661442d4..00000000 --- a/public/res/svg/ratatoskr-highlight.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/public/res/svg/ratatoskr-unread.svg b/public/res/svg/ratatoskr-unread.svg deleted file mode 100644 index 69a08c95..00000000 --- a/public/res/svg/ratatoskr-unread.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/app/pages/client/ClientNonUIFeatures.tsx b/src/app/pages/client/ClientNonUIFeatures.tsx index db3eefc2..62385769 100644 --- a/src/app/pages/client/ClientNonUIFeatures.tsx +++ b/src/app/pages/client/ClientNonUIFeatures.tsx @@ -3,9 +3,9 @@ import React, { ReactNode, useCallback, useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { RoomEvent, RoomEventHandlerMap } from 'matrix-js-sdk'; import { roomToUnreadAtom, unreadEqual, unreadInfoToUnread } from '../../state/room/roomToUnread'; -import LogoSVG from '../../../../public/res/svg/ratatoskr.svg'; -import LogoUnreadSVG from '../../../../public/res/svg/ratatoskr-unread.svg'; -import LogoHighlightSVG from '../../../../public/res/svg/ratatoskr-highlight.svg'; +import FaviconSVG from '../../../../public/res/svg/ratatoskr-favicon.svg'; +import FaviconUnreadSVG from '../../../../public/res/svg/ratatoskr-favicon-unread.svg'; +import FaviconHighlightSVG from '../../../../public/res/svg/ratatoskr-favicon-highlight.svg'; import NotificationSound from '../../../../public/sound/notification.ogg'; import InviteSound from '../../../../public/sound/invite.ogg'; import { notificationPermission, setFavicon } from '../../utils/dom'; @@ -67,9 +67,9 @@ function FaviconUpdater() { }); if (notification) { - setFavicon(highlight ? LogoHighlightSVG : LogoUnreadSVG); + setFavicon(highlight ? FaviconHighlightSVG : FaviconUnreadSVG); } else { - setFavicon(LogoSVG); + setFavicon(FaviconSVG); } }, [roomToUnread]); @@ -89,8 +89,8 @@ function InviteNotifications() { const notify = useCallback( (count: number) => { const noti = new window.Notification('Invitation', { - icon: LogoSVG, - badge: LogoSVG, + icon: FaviconSVG, + badge: FaviconSVG, body: `You have ${count} new invitation request.`, silent: true, }); From 0fe41efb9b2a4f0fe3326eb682d366877f471084 Mon Sep 17 00:00:00 2001 From: Eureka Self Date: Tue, 10 Nov 2026 13:51:26 +0000 Subject: [PATCH 16/56] nemesis: Persona poses on by default; Safari 14 polyfills (Array.at/findLast, Object.hasOwn, structuredClone) and -webkit-backdrop-filter Co-Authored-By: Claude Opus 5.5 --- src/app/features/scene/skyPlate.css.ts | 1 + src/app/pages/auth/styles.css.ts | 3 ++ src/app/state/settings.ts | 2 +- src/index.tsx | 1 + src/polyfills.ts | 72 ++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 src/polyfills.ts diff --git a/src/app/features/scene/skyPlate.css.ts b/src/app/features/scene/skyPlate.css.ts index 4472b98e..f97efe26 100644 --- a/src/app/features/scene/skyPlate.css.ts +++ b/src/app/features/scene/skyPlate.css.ts @@ -48,6 +48,7 @@ globalStyle(within(scene), { // Blur with a light tint toward the sky, like the chat bubbles; never an opaque slab (PLATE_TINT). backgroundColor: [fill, `color-mix(in srgb, ${fill} ${PLATE_TINT * 100}%, transparent)`], backdropFilter: 'blur(12px)', + WebkitBackdropFilter: 'blur(12px)', color: text, // Themed ink was chosen for the clear shell; inside the plate every token reads the plate's. vars: { diff --git a/src/app/pages/auth/styles.css.ts b/src/app/pages/auth/styles.css.ts index f16617e8..2341fe80 100644 --- a/src/app/pages/auth/styles.css.ts +++ b/src/app/pages/auth/styles.css.ts @@ -83,6 +83,7 @@ globalStyle(`${scene} ${AuthCard}`, { `color-mix(in srgb, var(--sky-bubble, #0d1a26) ${PLATE_TINT * 100}%, transparent)`, ], backdropFilter: 'blur(12px)', + WebkitBackdropFilter: 'blur(12px)', color: 'var(--sky-bubble-text, #b7c7d9)', borderRadius: config.radii.R500, border: 'none', @@ -99,6 +100,7 @@ globalStyle(`${scene} ${AuthHeader}`, { globalStyle(`${scene} #root header.${AuthHeader}`, { backgroundColor: 'transparent', backdropFilter: 'none', + WebkitBackdropFilter: 'none', }); globalStyle(`${scene} ${AuthCardContent}`, { paddingTop: config.space.S500, @@ -120,6 +122,7 @@ globalStyle(`${scene} ${AuthFooter}`, { paddingBottom: config.space.S500, backgroundColor: ['var(--sky-bubble, #0d1a26)', tint], backdropFilter: 'blur(12px)', + WebkitBackdropFilter: 'blur(12px)', maskImage: `${FADE_Y}, ${FADE_X}`, maskComposite: 'intersect', color: 'var(--sky-bubble-text, #b7c7d9)', diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts index 2b48fb59..1031141f 100644 --- a/src/app/state/settings.ts +++ b/src/app/state/settings.ts @@ -85,7 +85,7 @@ const defaultSettings: Settings = { encUrlPreview: false, showHiddenEvents: false, legacyUsernameColor: false, - poseBubbles: false, + poseBubbles: true, showNotifications: true, isNotificationSounds: true, diff --git a/src/index.tsx b/src/index.tsx index c2461fe8..6c7a3bc2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,5 @@ /* eslint-disable import/first */ +import './polyfills'; import './preview-demo/seed'; import React from 'react'; import { createRoot } from 'react-dom/client'; diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 00000000..2f018c4b --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,72 @@ +/* eslint-disable no-extend-native, func-names */ +// Safari 14 (iPadOS 14) lacks these; the bundle (ours and matrix-js-sdk) calls them at runtime. Safari 15.4 has all. +// Loaded first by src/index.tsx. Each is installed only when missing. + +function at(this: ArrayLike, n: number): T | undefined { + const len = this.length; + const i = Math.trunc(n) || 0; + const k = i < 0 ? len + i : i; + return k < 0 || k >= len ? undefined : this[k]; +} +if (!Array.prototype.at) Object.defineProperty(Array.prototype, 'at', { value: at, writable: true, configurable: true }); +if (!String.prototype.at) + Object.defineProperty(String.prototype, 'at', { value: at, writable: true, configurable: true }); + +if (!(Array.prototype as unknown as Record).findLast) + Object.defineProperty(Array.prototype, 'findLast', { + value: function (this: T[], fn: (v: T, i: number, a: T[]) => unknown, thisArg?: unknown) { + for (let i = this.length - 1; i >= 0; i -= 1) if (fn.call(thisArg, this[i], i, this)) return this[i]; + return undefined; + }, + writable: true, + configurable: true, + }); +if (!(Array.prototype as unknown as Record).findLastIndex) + Object.defineProperty(Array.prototype, 'findLastIndex', { + value: function (this: T[], fn: (v: T, i: number, a: T[]) => unknown, thisArg?: unknown) { + for (let i = this.length - 1; i >= 0; i -= 1) if (fn.call(thisArg, this[i], i, this)) return i; + return -1; + }, + writable: true, + configurable: true, + }); + +if (!(Object as unknown as Record).hasOwn) + Object.defineProperty(Object, 'hasOwn', { + value: (o: object, k: PropertyKey) => Object.prototype.hasOwnProperty.call(o, k), + writable: true, + configurable: true, + }); + +// Structured clone for the data the client clones: plain objects, arrays, Map, Set, Date, RegExp, typed arrays, with cycles. +if (typeof (globalThis as { structuredClone?: unknown }).structuredClone !== 'function') { + const clone = (v: unknown, seen: Map): unknown => { + if (v === null || typeof v !== 'object') return v; + if (seen.has(v)) return seen.get(v); + if (v instanceof Date) return new Date(v.getTime()); + if (v instanceof RegExp) return new RegExp(v.source, v.flags); + if (ArrayBuffer.isView(v)) return (v as unknown as { slice: () => unknown }).slice(); + if (v instanceof ArrayBuffer) return v.slice(0); + if (v instanceof Map) { + const m = new Map(); + seen.set(v, m); + v.forEach((val, key) => m.set(clone(key, seen), clone(val, seen))); + return m; + } + if (v instanceof Set) { + const s = new Set(); + seen.set(v, s); + v.forEach((val) => s.add(clone(val, seen))); + return s; + } + const out: Record | unknown[] = Array.isArray(v) ? [] : {}; + seen.set(v, out); + Object.keys(v).forEach((k) => { + (out as Record)[k] = clone((v as Record)[k], seen); + }); + return out; + }; + (globalThis as { structuredClone?: unknown }).structuredClone = (v: unknown) => clone(v, new Map()); +} + +export {}; From 9d3343bb316c4d8acc8b12acd51be1863cfcf643 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 13:53:22 +0000 Subject: [PATCH 17/56] nemesis/demo: tests follow the new numbers; Ridge retries a zero-size start skyTheme: auto resolves to the clock's theme; the plate pins now assert the chromeless shell (only composer, call bar, call view and plain messages are plates). terrainWorld: ceilings for the 512/768 grids; still grid 256 -> 320 so its quads stay within two height texels at the nearest ground (the test is unchanged). skyPlate.css: drop the void-import placeholders. Ridge: a scene started with no area no longer throws on drawImage or returns null; it builds on the first resize that gives it one. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/ridge/Ridge.test.ts | 24 ++++++++++++++ src/app/features/scene/ridge/Ridge.ts | 14 +++++++-- src/app/features/scene/skyPlate.css.ts | 9 ------ src/app/features/scene/skyTheme.test.ts | 31 +++++++++++++------ .../scene/terrain/terrainWorld.test.ts | 9 ++++-- .../features/scene/terrain/terrainWorld.ts | 2 +- 6 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/app/features/scene/ridge/Ridge.test.ts b/src/app/features/scene/ridge/Ridge.test.ts index 3aecad12..711054f6 100644 --- a/src/app/features/scene/ridge/Ridge.test.ts +++ b/src/app/features/scene/ridge/Ridge.test.ts @@ -437,6 +437,30 @@ describe('createRidgeScene', () => { expect(parent.children.some((c: any) => c === readout)).toBe(false); }); + it('a scene started at zero size paints nothing, then builds on the first non-zero resize', async () => { + const { parent, canvas } = host(); + canvas.clientWidth = 0; + canvas.clientHeight = 0; + (window as any).innerWidth = 0; + (window as any).innerHeight = 0; + const s = createRidgeScene({ canvas, tier: 'full', sky: DAY }); + expect(s).not.toBeNull(); + expect(parent.children).toEqual([canvas]); + expect(draws).toHaveLength(0); + // Still zero: a resize does not paint either. + window.dispatchEvent(new Event('resize')); + await new Promise((r) => setTimeout(r, 300)); + expect(parent.children).toEqual([canvas]); + (window as any).innerWidth = 400; + (window as any).innerHeight = 800; + window.dispatchEvent(new Event('resize')); + await new Promise((r) => setTimeout(r, 300)); + expect(parent.children.slice(1)).toHaveLength(LAYERS.length); + expect(draws.filter((d) => d === 'sky')).toHaveLength(1); + s!.dispose(); + expect(parent.children).toEqual([canvas]); + }); + it('dispose cancels the loop, the animations and the layers', () => { const { parent, canvas } = host(); const s = createRidgeScene({ canvas, tier: 'full', sky: DAY })!; diff --git a/src/app/features/scene/ridge/Ridge.ts b/src/app/features/scene/ridge/Ridge.ts index eae0df08..d2312aaf 100644 --- a/src/app/features/scene/ridge/Ridge.ts +++ b/src/app/features/scene/ridge/Ridge.ts @@ -180,9 +180,18 @@ export function createRidgeScene(opts: RidgeOptions): Scene | null { built = undefined; }; - /** Bake the sky, then the layers. Returns false when a 2D context is missing. */ + /** True while the host has no area to paint (a scene started in a hidden or collapsed container). */ + const hasNoArea = () => { + const { w, h, scale } = viewSize(); + const size = layerSize(w, h, scale); + return size.period < 1 || size.height < 1 || Math.round(h * scale) < 1; + }; + + /** Bake the sky, then the layers. Returns false when a 2D context is missing or there is no area yet. */ const build = (): boolean => { teardownLayers(); + // A zero-size canvas throws on drawImage; wait for the first resize that gives the host an area. + if (hasNoArea()) return false; const { w, h, scale } = viewSize(); const size = layerSize(w, h, scale); canvas.width = size.period; @@ -343,7 +352,8 @@ export function createRidgeScene(opts: RidgeOptions): Scene | null { raf = requestAnimationFrame(tick); }; - if (!build()) return null; + // No area yet is not a failure: the resize listener below builds once the host has one. + if (!build() && !hasNoArea()) return null; if (probe) { readout = document.createElement('div'); Object.assign(readout.style, { diff --git a/src/app/features/scene/skyPlate.css.ts b/src/app/features/scene/skyPlate.css.ts index 4472b98e..c231527c 100644 --- a/src/app/features/scene/skyPlate.css.ts +++ b/src/app/features/scene/skyPlate.css.ts @@ -1,12 +1,8 @@ import { globalStyle, style } from '@vanilla-extract/css'; import { color, config } from 'folds'; import { MessageBase, BubbleContent } from '../../components/message/layout/layout.css'; -import { PageContent, PageNavContent } from '../../components/page/style.css'; import { Editor } from '../../components/editor/Editor.css'; -import { MembersDrawer } from '../room/MembersDrawer.css'; import { CallStatus } from '../call-status/styles.css'; -import { RoomViewFollowing } from '../room/RoomViewFollowing.css'; -import { RoomViewTyping } from '../room/RoomViewTyping.css'; import { PLATE_TINT, SKY_PLATE_FALLBACK } from './skyPlate'; // Over the scene (html[data-scene]) the signed-in shell is clear (sceneShell.css.ts), so text @@ -26,11 +22,6 @@ const base = (recipe: () => string) => `.${recipe().split(' ')[0]}`; // Meta, 2026-11-09: no chrome. Title bars, the room list, list pages, the drawer and the bars over the composer are bare // ink on the sky (dusk is allowed to be low contrast: it is the crossover between the light and dark inks). -void PageNavContent; -void PageContent; -void MembersDrawer; -void RoomViewTyping; -void RoomViewFollowing; export const plateRegions = [ `.${SkyPlate}`, // The composer and the call status bar. diff --git a/src/app/features/scene/skyTheme.test.ts b/src/app/features/scene/skyTheme.test.ts index 3229cf36..80bc0423 100644 --- a/src/app/features/scene/skyTheme.test.ts +++ b/src/app/features/scene/skyTheme.test.ts @@ -39,11 +39,17 @@ describe('skyThemeId', () => { expect(skyThemeId('night')).toBe('midnight-sky'); }); - it('picks none for auto, at any hour: auto never overrides a signed-in theme', () => { + it('picks the theme of the state auto resolves to, at any hour', () => { [0, 6, 12, 18, 23].forEach((h) => { setSkyClock(() => atStandardTime(h, 10)); - expect(skyThemeId('auto')).toBeUndefined(); + const id = skyThemeId('auto'); + expect(id).toBeDefined(); + expect(id).toBe(skyThemeId(getResolvedSkyState())); }); + setSkyClock(() => atStandardTime(12)); + expect(skyThemeId('auto')).toBe('noonday-sky'); + setSkyClock(() => atStandardTime(0)); + expect(skyThemeId('auto')).toBe('midnight-sky'); setSkyClock(); }); @@ -145,19 +151,26 @@ describe('regions that sit on the plate', () => { // and link of that plate are pinned above; this pins who is on it. const on = (cls: string) => expect(plateRegions).toContain(cls); - it('plates the composer, the members drawer, the call status bar and the call view', () => { + it('plates the composer, the call status bar and the call view', () => { on(`.${Editor}`); - on(`.${MembersDrawer}`); on(`.${CallStatus}`); on(`.${SkyPlate}`); }); - it('plates the room list, list pages, typing and Following bars and every header', () => { - on(`.${PageNavContent}`); - on(`.${PageContent}`); - on(`.${RoomViewTyping}`); - on('#root header'); + it('plates a message that is not a bubble, and nothing else of the signed-in shell', () => { expect(plateRegions.some((r) => r.includes(':not(:has('))).toBe(true); + expect(plateRegions).toHaveLength(4); + }); + + // Meta, 2026-11-09: no chrome. These are bare ink on the sky, not plates. + it('leaves title bars, the room list, list pages, the drawer and the bars over the composer bare', () => { + [ + `.${MembersDrawer}`, + `.${PageNavContent}`, + `.${PageContent}`, + `.${RoomViewTyping}`, + '#root header', + ].forEach((cls) => expect(plateRegions).not.toContain(cls)); }); }); diff --git a/src/app/features/scene/terrain/terrainWorld.test.ts b/src/app/features/scene/terrain/terrainWorld.test.ts index 78ab2700..70a9d763 100644 --- a/src/app/features/scene/terrain/terrainWorld.test.ts +++ b/src/app/features/scene/terrain/terrainWorld.test.ts @@ -41,8 +41,13 @@ describe('grid', () => { }); it('each tier fits a 32-bit index and the cheap tiers stay cheap', () => { - // 205k at 320 quads: a guess at what a phone carries (SwiftShader only, no phone frame time), kept as a ceiling so a bigger grid is a deliberate change. - expect(triangleCount(GRID.low.grid)).toBeLessThan(210000); + // 524k at 512 quads (low), 1.18M at 768 (full): Meta asked for denser terrain on 2026-11-10, up from 205k at 320. No + // phone frame time is measured (SwiftShader only); kept as a ceiling so a bigger grid is a deliberate change. + expect(triangleCount(GRID.low.grid)).toBeLessThan(540000); + expect(triangleCount(GRID.full.grid)).toBeLessThan(1200000); + (['still', 'low', 'full'] as const).forEach((t) => + expect((GRID[t].grid + 1) ** 2).toBeLessThan(2 ** 32) + ); expect(GRID.full.grid).toBeGreaterThan(GRID.low.grid); expect(GRID.still.tex).toBeLessThanOrEqual(GRID.low.tex); }); diff --git a/src/app/features/scene/terrain/terrainWorld.ts b/src/app/features/scene/terrain/terrainWorld.ts index 64f18970..82a16bfb 100644 --- a/src/app/features/scene/terrain/terrainWorld.ts +++ b/src/app/features/scene/terrain/terrainWorld.ts @@ -11,7 +11,7 @@ export const TERRAIN = { span: 3000, hmax: 256 }; * under shading read from the height map shows as hard-edged facets and stepped crests. */ export const GRID = { - still: { grid: 256, tex: 1024 }, + still: { grid: 320, tex: 1024 }, low: { grid: 512, tex: 1024 }, full: { grid: 768, tex: 2048 }, } as const; From 384cd17dac344bc195a2d92ab7a881d575c51df2 Mon Sep 17 00:00:00 2001 From: Nibu Date: Tue, 10 Nov 2026 14:00:44 +0000 Subject: [PATCH 18/56] scene: terrain fine grain domain-warped so its plane waves stop reading as hatching The fine albedo octave is three plane waves per projection (about 5 m periods): a regular lattice, resolved (not aliased) at near-ground pixel size, so it reads as diagonal hatching. Switching the octave off in a SwiftShader render removes it; coarse grain off does not. The octave's coordinates are now pushed by a slower sine each, in terrainAlbedo.wavesWarped and the shader's warpq. Co-Authored-By: Claude Sonnet 5.5 --- .../scene/terrain/terrainAlbedo.test.ts | 2 ++ .../features/scene/terrain/terrainAlbedo.ts | 19 +++++++++++++++---- .../features/scene/terrain/terrainShaders.ts | 5 ++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/app/features/scene/terrain/terrainAlbedo.test.ts b/src/app/features/scene/terrain/terrainAlbedo.test.ts index 17363282..6afac79d 100644 --- a/src/app/features/scene/terrain/terrainAlbedo.test.ts +++ b/src/app/features/scene/terrain/terrainAlbedo.test.ts @@ -114,6 +114,8 @@ describe('shader agrees with the pure function on its numbers', () => { expect(GRID_FRAG).toContain(`#define ALB_WOBBLE_S ${ALBEDO.wobbleSlope}`); expect(GRID_FRAG).toContain(`#define ALB_GRAIN_COARSE ${ALBEDO.grainCoarse}`); expect(GRID_FRAG).toContain(`#define ALB_STRATA ${ALBEDO.strata}`); + expect(GRID_FRAG).toContain(`#define ALB_FINE_WARP ${ALBEDO.fineWarp.toFixed(1)}`); + expect(GRID_FRAG).toContain('waves3(warpq(qy),'); // the only fetch for colour-by-material would be a texture; the height map's texelFetch is the only other read, and the sky lookup is the baked scattering table's expect(GRID_FRAG.match(/texelFetch|texture\((?!uSky|uAP)/g)).toHaveLength(1); }); diff --git a/src/app/features/scene/terrain/terrainAlbedo.ts b/src/app/features/scene/terrain/terrainAlbedo.ts index fef204ff..48dc2b42 100644 --- a/src/app/features/scene/terrain/terrainAlbedo.ts +++ b/src/app/features/scene/terrain/terrainAlbedo.ts @@ -31,6 +31,8 @@ export const ALBEDO = { /** Albedo grain: relative swing of the two sine-product octaves (about 20 m and 5 m periods), and of the rock strata. */ grainCoarse: 0.35, grainFine: 0.3, + /** Metres the fine octave's coordinates are pushed by a slower sine, so its parallel stripes bend and do not read as hatching. */ + fineWarp: 7, /** The fine octave fades out between these distances (metres): beyond them it is under a pixel and only shimmers. */ fineNear: 250, fineFar: 700, @@ -132,6 +134,13 @@ const FINE = [ ] as const; const waves = (k: ReadonlyArray, a: number, b: number) => k.reduce((sum, [ka, kb]) => sum + Math.sin(a * ka + b * kb), 0) / 3; +/** `waves` of coordinates warped by a sine each (the shader's `warpq`): three plane waves alone make a regular lattice. */ +const wavesWarped = (k: ReadonlyArray, a: number, b: number) => + waves( + k, + a + ALBEDO.fineWarp * Math.sin(b * 0.55 + 1.3), + b + ALBEDO.fineWarp * Math.sin(a * 0.47 + 0.7) + ); /** * The multiplier on albedo that gives the surface grain and the rock its layers, at a surface point (metres) with unit @@ -148,10 +157,12 @@ export function grainAt( distance: number ): number { const w = triWeights(n); - const over = (k: ReadonlyArray) => - w[0] * waves(k, z, y) + w[1] * waves(k, x, z) + w[2] * waves(k, x, y); - const coarse = over(COARSE); - const fine = over(FINE) * (1 - smooth(ALBEDO.fineNear, ALBEDO.fineFar, distance)); + const over = ( + k: ReadonlyArray, + f: typeof waves + ) => w[0] * f(k, z, y) + w[1] * f(k, x, z) + w[2] * f(k, x, y); + const coarse = over(COARSE, waves); + const fine = over(FINE, wavesWarped) * (1 - smooth(ALBEDO.fineNear, ALBEDO.fineFar, distance)); const layers = Math.sin(y * 0.9 + wobbleAt(x, y, z, n) * 3); return Math.max( 0, diff --git a/src/app/features/scene/terrain/terrainShaders.ts b/src/app/features/scene/terrain/terrainShaders.ts index b79e2873..f6bcce37 100644 --- a/src/app/features/scene/terrain/terrainShaders.ts +++ b/src/app/features/scene/terrain/terrainShaders.ts @@ -133,6 +133,7 @@ const ALBEDO_DEFINES = ` #define ALB_WOBBLE_S ${lit(ALBEDO.wobbleSlope)} #define ALB_GRAIN_COARSE ${lit(ALBEDO.grainCoarse)} #define ALB_GRAIN_FINE ${lit(ALBEDO.grainFine)} +#define ALB_FINE_WARP ${lit(ALBEDO.fineWarp)} #define ALB_STRATA ${lit(ALBEDO.strata)} #define ALB_FINE_NEAR ${lit(ALBEDO.fineNear)} #define ALB_FINE_FAR ${lit(ALBEDO.fineFar)} @@ -175,6 +176,8 @@ ${FETCH} // Triplanar patterns: each projection plane's value, blended by the weights of the face's normal (terrainAlbedo.triWeights). float wob2(vec2 q) { return sin(q.x * 0.043 + q.y * 0.017) * sin(q.y * 0.051 - q.x * 0.023); } float waves3(vec2 q, vec2 a, vec2 b, vec2 c) { return (sin(dot(q, a)) + sin(dot(q, b)) + sin(dot(q, c))) / 3.0; } +// The fine octave's coordinates pushed by a slower sine each (terrainAlbedo wavesWarped): three plane waves alone make a regular lattice. +vec2 warpq(vec2 q) { return q + ALB_FINE_WARP * vec2(sin(q.y * 0.55 + 1.3), sin(q.x * 0.47 + 0.7)); } void main() { // Normal from the bilinear height at a continuous position, a stencil 1.5 texels each side: smooth across texel borders. vec2 p = v_uv * float(uTex - 1); @@ -195,7 +198,7 @@ void main() { vec3 albedo = mix(mix(ground, uSnow, snow), uRock, rockW); // Surface grain and rock strata, the same arithmetic as terrainAlbedo.grainAt(). float coarse = tw.x * waves3(qx, vec2(0.31, 0.27), vec2(-0.22, 0.35), vec2(0.38, -0.12)) + tw.y * waves3(qy, vec2(0.31, 0.27), vec2(-0.22, 0.35), vec2(0.38, -0.12)) + tw.z * waves3(qz, vec2(0.31, 0.27), vec2(-0.22, 0.35), vec2(0.38, -0.12)); - float fine = tw.x * waves3(qx, vec2(1.1, 0.7), vec2(-0.8, 1.2), vec2(1.3, -0.6)) + tw.y * waves3(qy, vec2(1.1, 0.7), vec2(-0.8, 1.2), vec2(1.3, -0.6)) + tw.z * waves3(qz, vec2(1.1, 0.7), vec2(-0.8, 1.2), vec2(1.3, -0.6)); + float fine = tw.x * waves3(warpq(qx), vec2(1.1, 0.7), vec2(-0.8, 1.2), vec2(1.3, -0.6)) + tw.y * waves3(warpq(qy), vec2(1.1, 0.7), vec2(-0.8, 1.2), vec2(1.3, -0.6)) + tw.z * waves3(warpq(qz), vec2(1.1, 0.7), vec2(-0.8, 1.2), vec2(1.3, -0.6)); float near = 1.0 - smoothstep(ALB_FINE_NEAR, ALB_FINE_FAR, length(v_world - uCamera)); float layers = sin(v_world.y * 0.9 + w * 3.0); albedo *= max(0.0, 1.0 + ALB_GRAIN_COARSE * coarse + ALB_GRAIN_FINE * near * fine + ALB_STRATA * rockW * layers); From c657d686152aa8c8cb470cc0181b4ffe827323aa Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 14:03:21 +0000 Subject: [PATCH 19/56] PR #65 steward ruling: user themes win over auto; focus ring; logo on light themes skyThemeId('auto') is undefined again as on dev. sceneThemeId follows the resolved sky for auto only while a scene is on and the person chose no theme (signed-out pages pass none); useResolvedSkyState changes as the clock crosses so theme classes re-render. Scene buttons and chips keep a focus-visible outline. The white mark takes the dark filter through a LogoMark class on every themed under any prism-light theme (favicon and notification icons are files and keep the white mark). Background picker is hidden without a backgroundScene config. Ridge comment states what hasNoArea covers. Co-Authored-By: Claude Sonnet 5.5 --- src/app/components/logo/logoMark.css.ts | 9 ++++ src/app/features/scene/SkyStateControl.tsx | 59 ++++++++++++---------- src/app/features/scene/ridge/Ridge.ts | 2 +- src/app/features/scene/sceneButtons.css.ts | 6 ++- src/app/features/scene/skyTheme.test.ts | 57 +++++++++++++++++---- src/app/features/scene/skyTheme.ts | 19 ++++++- src/app/features/settings/about/About.tsx | 2 + src/app/hooks/useSkyState.ts | 20 +++++++- src/app/hooks/useTheme.ts | 27 ++++++---- src/app/pages/auth/AuthLayout.tsx | 7 ++- src/app/pages/auth/styles.css.ts | 3 -- src/app/pages/client/WelcomePage.tsx | 11 +++- src/app/state/settings.ts | 4 +- 13 files changed, 167 insertions(+), 59 deletions(-) create mode 100644 src/app/components/logo/logoMark.css.ts diff --git a/src/app/components/logo/logoMark.css.ts b/src/app/components/logo/logoMark.css.ts new file mode 100644 index 00000000..4c967088 --- /dev/null +++ b/src/app/components/logo/logoMark.css.ts @@ -0,0 +1,9 @@ +import { globalStyle, style } from '@vanilla-extract/css'; + +/** + * The Ratatoskr mark is white on transparent (Meta's matte). On any light theme (they all carry `prism-light`) a class + * of this name darkens it to the dark ink; put it on every that shows the mark on a themed surface. The favicon and + * notification icons are files the browser draws and cannot take it. + */ +export const LogoMark = style({}); +globalStyle(`body.prism-light .${LogoMark}`, { filter: 'brightness(0.18)' }); diff --git a/src/app/features/scene/SkyStateControl.tsx b/src/app/features/scene/SkyStateControl.tsx index 4e67bd81..b664dc07 100644 --- a/src/app/features/scene/SkyStateControl.tsx +++ b/src/app/features/scene/SkyStateControl.tsx @@ -39,7 +39,8 @@ const LABEL: Record = { export function SkyStateControl() { const state = useSkyState(); const [cords, setCords] = useState(); - const configuredStyle = useClientConfig().backgroundScene?.style; + const backgroundScene = useClientConfig().backgroundScene; + const configuredStyle = backgroundScene?.style; const sceneStyle: ServedSceneStyle = getSceneStyleOverride() ?? (configuredStyle === 'terrain' ? 'terrain' : 'ridge'); @@ -85,32 +86,36 @@ export function SkyStateControl() { ))} - - Background - - {SERVED_SCENE_STYLES.map((st) => ( - { - setCords(undefined); - if (st !== sceneStyle) setSceneStyle(st); - }} - after={sceneStyle === st ? : undefined} - > - - {STYLE_LABEL[st]} - - - ))} + {backgroundScene && ( + <> + + Background + + {SERVED_SCENE_STYLES.map((st) => ( + { + setCords(undefined); + if (st !== sceneStyle) setSceneStyle(st); + }} + after={sceneStyle === st ? : undefined} + > + + {STYLE_LABEL[st]} + + + ))} + + )} diff --git a/src/app/features/scene/ridge/Ridge.ts b/src/app/features/scene/ridge/Ridge.ts index d2312aaf..69a88864 100644 --- a/src/app/features/scene/ridge/Ridge.ts +++ b/src/app/features/scene/ridge/Ridge.ts @@ -180,7 +180,7 @@ export function createRidgeScene(opts: RidgeOptions): Scene | null { built = undefined; }; - /** True while the host has no area to paint (a scene started in a hidden or collapsed container). */ + /** True while there is nothing to paint on: the canvas has no client size and the window itself is 0 x 0 (`viewSize` falls back to the window). */ const hasNoArea = () => { const { w, h, scale } = viewSize(); const size = layerSize(w, h, scale); diff --git a/src/app/features/scene/sceneButtons.css.ts b/src/app/features/scene/sceneButtons.css.ts index 301d5671..5c96e71b 100644 --- a/src/app/features/scene/sceneButtons.css.ts +++ b/src/app/features/scene/sceneButtons.css.ts @@ -9,7 +9,6 @@ globalStyle(controls, { backgroundColor: 'transparent', boxShadow: 'none', border: 'none', - outline: 'none', color: 'inherit', }); globalStyle( @@ -21,6 +20,11 @@ globalStyle( ].join(', '), { backgroundColor: 'color-mix(in srgb, currentColor 12%, transparent)' } ); +// A visible keyboard focus ring in the control's own ink, on top of the hover wash. +globalStyle([`${scene} .epr39zd:focus-visible`, `${scene} ._1bv8u1w6:focus-visible`].join(', '), { + outline: '2px solid currentColor', + outlineOffset: '2px', +}); globalStyle(`${scene} .epr39zd`, { fontWeight: 700, textDecoration: 'underline', diff --git a/src/app/features/scene/skyTheme.test.ts b/src/app/features/scene/skyTheme.test.ts index 80bc0423..18435ae6 100644 --- a/src/app/features/scene/skyTheme.test.ts +++ b/src/app/features/scene/skyTheme.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { atStandardTime } from './standardTime.testkit'; import { baked } from '../../../sky/baked'; import { AA, NIGHT_SKY_LIGHTEST, contrast, luminance } from '../../../sky/skyTokens.js'; @@ -9,10 +9,15 @@ import { MembersDrawer } from '../room/MembersDrawer.css'; import { RoomViewTyping } from '../room/RoomViewTyping.css'; import { FOOTER_TINT, SKY_PLATE_FALLBACK } from './skyPlate'; import { SkyPlate, plateRegions } from './skyPlate.css'; -import { getResolvedSkyState, setSkyClock, skyBackdrop } from './skyState'; +import { + getResolvedSkyState, + setSkyClock, + skyBackdrop, + subscribe as subscribeSky, +} from './skyState'; import type { SkyBackdrop } from './skyState'; import type { FixedSkyState } from './skyBand'; -import { skyThemeId } from './skyTheme'; +import { sceneThemeId, skyThemeId } from './skyTheme'; const SKY = { 'midnight-sky': baked.midnight, 'noonday-sky': baked.noonday } as const; const fixed = (['day', 'dusk', 'night'] as const).flatMap((s) => { @@ -39,17 +44,47 @@ describe('skyThemeId', () => { expect(skyThemeId('night')).toBe('midnight-sky'); }); - it('picks the theme of the state auto resolves to, at any hour', () => { + it('picks none for auto, at any hour: auto never overrides a signed-in theme', () => { [0, 6, 12, 18, 23].forEach((h) => { setSkyClock(() => atStandardTime(h, 10)); - const id = skyThemeId('auto'); - expect(id).toBeDefined(); - expect(id).toBe(skyThemeId(getResolvedSkyState())); + expect(skyThemeId('auto')).toBeUndefined(); }); - setSkyClock(() => atStandardTime(12)); - expect(skyThemeId('auto')).toBe('noonday-sky'); - setSkyClock(() => atStandardTime(0)); - expect(skyThemeId('auto')).toBe('midnight-sky'); + setSkyClock(); + }); + + describe('sceneThemeId, the theme the page wears', () => { + it('a fixed state always picks its theme, whatever the scene or the person chose', () => { + [true, false].forEach((scene) => + [true, false].forEach((chose) => { + expect(sceneThemeId('day', 'night', scene, chose)).toBe('noonday-sky'); + expect(sceneThemeId('night', 'day', scene, chose)).toBe('midnight-sky'); + }) + ); + }); + + it('auto follows the resolved sky only while a scene is on and the person chose no theme', () => { + expect(sceneThemeId('auto', 'day', true, false)).toBe('noonday-sky'); + expect(sceneThemeId('auto', 'dusk', true, false)).toBe('noonday-sky'); + expect(sceneThemeId('auto', 'night', true, false)).toBe('midnight-sky'); + expect(sceneThemeId('auto', 'day', false, false)).toBeUndefined(); + expect(sceneThemeId('auto', 'day', true, true)).toBeUndefined(); + }); + }); + + it('the resolved sky a hook subscribes to changes as the clock crosses, so theme classes follow', () => { + vi.useFakeTimers(); + let hour = 12; + setSkyClock(() => atStandardTime(hour, 10)); + const seen: string[] = []; + const off = subscribeSky((s) => seen.push(s)); + expect(getResolvedSkyState()).toBe('day'); + hour = 0; + setSkyClock(() => atStandardTime(hour, 10)); + vi.advanceTimersByTime(61_000); + expect(seen).toEqual(['night']); + expect(getResolvedSkyState()).toBe('night'); + off(); + vi.useRealTimers(); setSkyClock(); }); diff --git a/src/app/features/scene/skyTheme.ts b/src/app/features/scene/skyTheme.ts index dbd46501..3d427cb2 100644 --- a/src/app/features/scene/skyTheme.ts +++ b/src/app/features/scene/skyTheme.ts @@ -10,7 +10,7 @@ // derived outside this code (src/sky/skyTokens.js). Midnight Sky's link is derived there against the lightest Night sky // (4.53 on it, 5.43 on the darkest). The plate (skyPlate.css.ts) is still in the shell, pending // its removal. -import { resolveSkyState, type FixedSkyState, type SkyState } from './skyState'; +import type { FixedSkyState, SkyState } from './skyState'; export type SkyThemeId = 'midnight-sky' | 'noonday-sky'; @@ -21,4 +21,19 @@ const BY_STATE: Record = { }; export const skyThemeId = (state: SkyState): SkyThemeId | undefined => - BY_STATE[resolveSkyState(state)]; + state === 'auto' ? undefined : BY_STATE[state]; + +/** + * The theme the page wears: a fixed sky state always picks its theme; 'auto' follows the resolved sky (`resolved`, the + * clock's state) only while a scene is on and the person has not chosen a theme of their own, and otherwise picks none. + * Signed-out pages choose no theme, so they pass `userChoseTheme: false`. + */ +export const sceneThemeId = ( + state: SkyState, + resolved: FixedSkyState, + sceneOn: boolean, + userChoseTheme: boolean +): SkyThemeId | undefined => { + if (state !== 'auto') return BY_STATE[state]; + return sceneOn && !userChoseTheme ? BY_STATE[resolved] : undefined; +}; diff --git a/src/app/features/settings/about/About.tsx b/src/app/features/settings/about/About.tsx index 6b304965..9ec74305 100644 --- a/src/app/features/settings/about/About.tsx +++ b/src/app/features/settings/about/About.tsx @@ -4,6 +4,7 @@ import { Page, PageContent, PageHeader } from '../../../components/page'; import { SequenceCard } from '../../../components/sequence-card'; import { SequenceCardStyle } from '../styles.css'; import { SettingTile } from '../../../components/setting-tile'; +import { LogoMark } from '../../../components/logo/logoMark.css'; import RatatoskrSVG from '../../../../../public/res/svg/ratatoskr.svg'; import { clearCacheAndReload } from '../../../../client/initMatrix'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; @@ -37,6 +38,7 @@ export function About({ requestClose }: AboutProps) { Ratatoskr logo useSyncExternalStore(subscribe, getSkyState); + +/** The sky being drawn: day, dusk or night. Unlike the choice it changes as the clock crosses, so it re-renders. */ +export const useResolvedSkyState = (): FixedSkyState => + useSyncExternalStore(subscribe, getResolvedSkyState); + +/** Whether a background scene runs on this device and config (the tier the scene host will mount). */ +export const useSceneOn = (): boolean => + resolveTier(useClientConfig().backgroundScene, readCapabilities(navigator)) !== 'off'; diff --git a/src/app/hooks/useTheme.ts b/src/app/hooks/useTheme.ts index 00ac1ee9..60f89757 100644 --- a/src/app/hooks/useTheme.ts +++ b/src/app/hooks/useTheme.ts @@ -10,10 +10,10 @@ import { } from '../../colors.css'; import '../../gamecult.css'; import '../../sky.css'; -import { settingsAtom } from '../state/settings'; +import { defaultThemeId, settingsAtom } from '../state/settings'; import { useSetting } from '../state/hooks/settings'; -import { skyThemeId } from '../features/scene/skyTheme'; -import { useSkyState } from './useSkyState'; +import { sceneThemeId } from '../features/scene/skyTheme'; +import { useResolvedSkyState, useSceneOn, useSkyState } from './useSkyState'; export enum ThemeKind { Light = 'light', @@ -102,20 +102,29 @@ export const useSystemThemeKind = (): ThemeKind => { return themeKind; }; -/** The theme the sky state asks for, or undefined while it is 'auto': auto never overrides a person's theme. */ -export const useSkyTheme = (): Theme | undefined => { - const id = skyThemeId(useSkyState()); - return id === NoondaySkyTheme.id ? NoondaySkyTheme : id && MidnightSkyTheme; -}; +const toTheme = (id: ReturnType): Theme | undefined => + id === NoondaySkyTheme.id ? NoondaySkyTheme : id && MidnightSkyTheme; + +/** A theme the person chose: the system scheme, or one other than the default. A saved Midnight Sky is the default. */ +const chosenTheme = (useSystemTheme: boolean, themeId?: string) => + useSystemTheme || (themeId !== undefined && themeId !== defaultThemeId); + +/** The theme the sky asks for on a page with no theme of its own (signed out), or undefined for none. */ +export const useSkyTheme = (): Theme | undefined => + toTheme(sceneThemeId(useSkyState(), useResolvedSkyState(), useSceneOn(), false)); export const useActiveTheme = (): Theme => { - const skyTheme = useSkyTheme(); + const sky = useSkyState(); + const resolved = useResolvedSkyState(); + const sceneOn = useSceneOn(); const systemThemeKind = useSystemThemeKind(); const themes = useThemes(); const [systemTheme] = useSetting(settingsAtom, 'useSystemTheme'); const [themeId] = useSetting(settingsAtom, 'themeId'); const [lightThemeId] = useSetting(settingsAtom, 'lightThemeId'); const [darkThemeId] = useSetting(settingsAtom, 'darkThemeId'); + // A person's own theme wins over 'auto'; a fixed sky state is itself a choice and wins over the theme. + const skyTheme = toTheme(sceneThemeId(sky, resolved, sceneOn, chosenTheme(systemTheme, themeId))); if (skyTheme) return skyTheme; diff --git a/src/app/pages/auth/AuthLayout.tsx b/src/app/pages/auth/AuthLayout.tsx index 0d5c9406..49c7b774 100644 --- a/src/app/pages/auth/AuthLayout.tsx +++ b/src/app/pages/auth/AuthLayout.tsx @@ -21,6 +21,7 @@ import { import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { LOGIN_PATH, REGISTER_PATH, RESET_PASSWORD_PATH } from '../paths'; import RatatoskrSVG from '../../../../public/res/svg/ratatoskr.svg'; +import { LogoMark } from '../../components/logo/logoMark.css'; import { SkyStateControl } from '../../features/scene/SkyStateControl'; import { ServerPicker } from './ServerPicker'; import { AutoDiscoveryAction, autoDiscovery } from '../../cs-api'; @@ -135,7 +136,11 @@ export function AuthLayout() {
    - Ratatoskr logo + Ratatoskr logo Ratatoskr diff --git a/src/app/pages/auth/styles.css.ts b/src/app/pages/auth/styles.css.ts index f16617e8..56ee090f 100644 --- a/src/app/pages/auth/styles.css.ts +++ b/src/app/pages/auth/styles.css.ts @@ -34,9 +34,6 @@ export const AuthLogo = style([ }, ]); -// The mark is white (Meta's matte); on the light sky theme it takes the dark ink instead. -globalStyle('body.noonday-sky img[src*="ratatoskr"]', { filter: 'brightness(0.18)' }); - export const AuthHeader = style({ padding: `0 ${config.space.S400}`, borderBottomWidth: config.borderWidth.B300, diff --git a/src/app/pages/client/WelcomePage.tsx b/src/app/pages/client/WelcomePage.tsx index 74869c3f..8b63af73 100644 --- a/src/app/pages/client/WelcomePage.tsx +++ b/src/app/pages/client/WelcomePage.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Box, Button, Icon, Icons, Text, config, toRem } from 'folds'; import { Page, PageHero, PageHeroSection } from '../../components/page'; +import { LogoMark } from '../../components/logo/logoMark.css'; import RatatoskrSVG from '../../../../public/res/svg/ratatoskr.svg'; export function WelcomePage() { @@ -14,7 +15,15 @@ export function WelcomePage() { > } + icon={ + Ratatoskr logo + } title="Welcome to Ratatoskr" subTitle={ diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts index 2b48fb59..9ca2496f 100644 --- a/src/app/state/settings.ts +++ b/src/app/state/settings.ts @@ -58,8 +58,10 @@ export interface Settings { soundscapeVoiceCap: number; } +export const defaultThemeId = 'midnight-sky'; + const defaultSettings: Settings = { - themeId: 'midnight-sky', + themeId: defaultThemeId, useSystemTheme: false, lightThemeId: undefined, darkThemeId: undefined, From d2d6fbcc0012cf05bb43197dd16e8665633db405 Mon Sep 17 00:00:00 2001 From: Eureka Self Date: Tue, 10 Nov 2026 14:04:34 +0000 Subject: [PATCH 20/56] nemesis: lookbehind regexes degrade where the engine lacks lookbehind (Safari < 16.4 blanked at load); markdown rules share regex.ts patterns Co-Authored-By: Claude Opus 5.5 --- src/app/plugins/markdown/inline/rules.ts | 3 +-- src/app/utils/regex.ts | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/app/plugins/markdown/inline/rules.ts b/src/app/plugins/markdown/inline/rules.ts index 77bcbd57..2a7c6016 100644 --- a/src/app/plugins/markdown/inline/rules.ts +++ b/src/app/plugins/markdown/inline/rules.ts @@ -1,8 +1,7 @@ import { InlineMDRule } from './type'; +import { ESC_NEG_LB, URL_NEG_LB } from '../../../utils/regex'; const MIN_ANY = '(.+?)'; -const URL_NEG_LB = '(? unsafeText.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d'); -export const HTTP_URL_PATTERN = `https?:\\/\\/(?:www\\.)?(?:[^\\s)]*)(? { + try { + return new RegExp('(?<=a)b').test('ab'); + } catch { + return false; + } +})(); + +// Without lookbehind a URL keeps any trailing punctuation. +export const HTTP_URL_PATTERN = LOOKBEHIND + ? `https?:\\/\\/(?:www\\.)?(?:[^\\s)]*)(?()[\]\\.,;:\s@\\"]+(\.[^<>()[\]\\.,;:\s@\\"]+)*)|(\\".+\\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; -export const URL_NEG_LB = '(? Date: Tue, 10 Nov 2026 14:05:42 +0000 Subject: [PATCH 21/56] Diagnostic overlays (?probe=1, ?tierlabel=1) only where allowed; footer band test knows the Safari prefix debugOverlaysAllowed: a dev build, a VITE_PREVIEW_DEMO build, or backgroundScene.debugOverlays in the client config. A production visitor adding the query string gets neither the frame readout nor the tier label. footerBand.test pins -webkit-backdrop-filter (live commit 0fe41efb) and that it blurs as the standard one. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/scene/debugOverlays.test.ts | 49 +++++++++++++++++++ src/app/features/scene/debugOverlays.ts | 15 ++++++ src/app/features/scene/index.ts | 6 ++- src/app/features/scene/ridge/Ridge.test.ts | 13 ++++- src/app/features/scene/ridge/Ridge.ts | 7 ++- src/app/features/scene/terrain/Terrain.ts | 9 ++-- .../features/scene/terrain/tierLabel.test.ts | 12 +++-- src/app/features/scene/terrain/tierLabel.ts | 18 ++++--- src/app/features/scene/tier.ts | 2 + src/app/pages/auth/footerBand.test.ts | 10 +++- 10 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 src/app/features/scene/debugOverlays.test.ts create mode 100644 src/app/features/scene/debugOverlays.ts diff --git a/src/app/features/scene/debugOverlays.test.ts b/src/app/features/scene/debugOverlays.test.ts new file mode 100644 index 00000000..e1a4e61e --- /dev/null +++ b/src/app/features/scene/debugOverlays.test.ts @@ -0,0 +1,49 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { debugOverlaysAllowed, overlayRequested } from './debugOverlays'; + +let search = ''; +beforeEach(() => { + search = ''; + vi.stubGlobal('window', { + get location() { + return { search }; + }, + }); +}); +afterEach(() => { + vi.unstubAllGlobals(); + vi.unstubAllEnvs(); +}); + +describe('debugOverlaysAllowed', () => { + it('a production build allows nothing by default', () => { + vi.stubEnv('DEV', false); + vi.stubEnv('VITE_PREVIEW_DEMO', ''); + expect(debugOverlaysAllowed(undefined)).toBe(false); + expect(debugOverlaysAllowed({ tier: 'full' })).toBe(false); + expect(debugOverlaysAllowed({ tier: 'full', debugOverlays: false })).toBe(false); + }); + + it('a dev build, a demo preview build or an explicit config flag allows them', () => { + vi.stubEnv('DEV', true); + expect(debugOverlaysAllowed(undefined)).toBe(true); + vi.stubEnv('DEV', false); + vi.stubEnv('VITE_PREVIEW_DEMO', '1'); + expect(debugOverlaysAllowed(undefined)).toBe(true); + vi.stubEnv('VITE_PREVIEW_DEMO', ''); + expect(debugOverlaysAllowed({ debugOverlays: true })).toBe(true); + }); +}); + +describe('overlayRequested', () => { + it('needs both the query and the permission', () => { + search = '?probe=1'; + expect(overlayRequested('probe', true)).toBe(true); + expect(overlayRequested('probe', false)).toBe(false); + expect(overlayRequested('tierlabel', true)).toBe(false); + search = '?tierlabel=1'; + expect(overlayRequested('tierlabel', true)).toBe(true); + search = ''; + expect(overlayRequested('probe', true)).toBe(false); + }); +}); diff --git a/src/app/features/scene/debugOverlays.ts b/src/app/features/scene/debugOverlays.ts new file mode 100644 index 00000000..a02d9e92 --- /dev/null +++ b/src/app/features/scene/debugOverlays.ts @@ -0,0 +1,15 @@ +import type { BackgroundSceneConfig } from './tier'; + +/** + * Whether the page URL may switch on the scene's diagnostic overlays (`?probe=1` frame readout, `?tierlabel=1` path + * label). Only a dev build, a demo preview build (VITE_PREVIEW_DEMO=1), or a config that says `debugOverlays: true` + * allows it; a production visitor adding a query string gets nothing. + */ +export const debugOverlaysAllowed = (config: BackgroundSceneConfig | undefined): boolean => + import.meta.env?.DEV === true || + import.meta.env?.VITE_PREVIEW_DEMO === '1' || + config?.debugOverlays === true; + +/** The page URL asks for this overlay (`?name=1`), and the overlays are allowed. */ +export const overlayRequested = (name: 'probe' | 'tierlabel', allowed: boolean): boolean => + allowed && new URLSearchParams(window.location.search).get(name) === '1'; diff --git a/src/app/features/scene/index.ts b/src/app/features/scene/index.ts index 73377440..bc041672 100644 --- a/src/app/features/scene/index.ts +++ b/src/app/features/scene/index.ts @@ -1,5 +1,6 @@ import type { Scene, SceneOptions } from './Scene'; import { BackgroundSceneConfig, Capabilities, SceneTier, resolveTier } from './tier'; +import { debugOverlaysAllowed } from './debugOverlays'; import { getSkyState, subscribe } from './skyState'; import type { SkyState } from './skyState'; @@ -17,7 +18,7 @@ export type StartOptions = Omit { let createScene: (o: SceneOptions) => Scene | null; @@ -82,7 +83,8 @@ export async function startBackgroundScene( ): Promise { const tier = resolveTier(config, capabilities); if (tier === 'off') return null; - const build = (state: SkyState) => create(config, { ...options, tier }, state); + const debug = debugOverlaysAllowed(config); + const build = (state: SkyState) => create(config, { ...options, tier, debug }, state); const state = getSkyState(); const key0 = config?.style === 'raymarch' ? null : await skyKey(config, state); const first = await build(state); diff --git a/src/app/features/scene/ridge/Ridge.test.ts b/src/app/features/scene/ridge/Ridge.test.ts index 711054f6..adee2717 100644 --- a/src/app/features/scene/ridge/Ridge.test.ts +++ b/src/app/features/scene/ridge/Ridge.test.ts @@ -428,7 +428,7 @@ describe('createRidgeScene', () => { it('?probe=1 keeps the rAF loop alive and shows median and p95', () => { search = '?probe=1'; const { parent, canvas } = host(); - const s = createRidgeScene({ canvas, tier: 'full', sky: DAY })!; + const s = createRidgeScene({ canvas, tier: 'full', sky: DAY, debug: true })!; const readout = parent.children[parent.children.length - 1]; runFrames(16.7, 300); expect(rafQueue.length).toBe(1); // still sampling @@ -437,6 +437,17 @@ describe('createRidgeScene', () => { expect(parent.children.some((c: any) => c === readout)).toBe(false); }); + it('?probe=1 does nothing where the overlays are not allowed: no readout, the loop ends', () => { + search = '?probe=1'; + const { parent, canvas } = host(); + const before = parent.children.length; + const s = createRidgeScene({ canvas, tier: 'full', sky: DAY })!; + expect(parent.children.length).toBe(before + LAYERS.length); + runFrames(16.7, 300); + expect(rafQueue.length).toBe(0); + s.dispose(); + }); + it('a scene started at zero size paints nothing, then builds on the first non-zero resize', async () => { const { parent, canvas } = host(); canvas.clientWidth = 0; diff --git a/src/app/features/scene/ridge/Ridge.ts b/src/app/features/scene/ridge/Ridge.ts index 69a88864..fd40a967 100644 --- a/src/app/features/scene/ridge/Ridge.ts +++ b/src/app/features/scene/ridge/Ridge.ts @@ -1,4 +1,5 @@ import type { Scene, SceneOptions } from '../Scene'; +import { overlayRequested } from '../debugOverlays'; import { FrameSampler, FrameStats } from './probe'; import { skyColours, SkyColours, Srgb } from './skyColour'; import { activeAt, drawStars, drawTwinkle as paintTwinkle, makeStars, Star } from './stars'; @@ -14,8 +15,10 @@ import { } from './ridgeWorld'; export type RidgeOptions = Pick & { - /** Show the rAF median/p95 readout. Default: the page URL has `?probe=1`. */ + /** Show the rAF median/p95 readout. Default: the page URL has `?probe=1`, where `debug` allows it. */ probe?: boolean; + /** The diagnostic overlays may be asked for by URL (debugOverlays.ts). Default: no. */ + debug?: boolean; /** Each time the readout would update, and once with `still` when the fallback fires. */ onProbe?: (s: FrameStats & { still: boolean }) => void; /** The sky's colours; default `skyColours()`, the one function to replace with Void's sun.js. */ @@ -151,7 +154,7 @@ export function createRidgeScene(opts: RidgeOptions): Scene | null { const parent = canvas.parentElement; if (!parent) return null; const sky = opts.sky ?? skyColours(); - const probe = opts.probe ?? new URLSearchParams(window.location.search).get('probe') === '1'; + const probe = opts.probe ?? overlayRequested('probe', opts.debug === true); const reducedMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)'); let built: Built | undefined; diff --git a/src/app/features/scene/terrain/Terrain.ts b/src/app/features/scene/terrain/Terrain.ts index b697927b..a1807305 100644 --- a/src/app/features/scene/terrain/Terrain.ts +++ b/src/app/features/scene/terrain/Terrain.ts @@ -22,6 +22,7 @@ import { SKY_FRAG, } from './terrainShaders'; import { createTerrainStars } from './terrainStars'; +import { overlayRequested } from '../debugOverlays'; import { showTierLabel } from './tierLabel'; export type TerrainOptions = Pick< @@ -36,8 +37,10 @@ export type TerrainOptions = Pick< band?: SkyBand; /** Terrain seed: the height map is sampled at an offset taken from it. Default 0. */ seed?: number; - /** Show the rAF median/p95 readout (plus triangle count). Default: the page URL has `?probe=1`. */ + /** Show the rAF median/p95 readout (plus triangle count). Default: the page URL has `?probe=1`, where `debug` allows it. */ probe?: boolean; + /** The diagnostic overlays (readout, tier label) may be asked for by URL (debugOverlays.ts). Default: no. */ + debug?: boolean; onProbe?: (s: FrameStats & { still: boolean; triangles: number }) => void; }; @@ -89,7 +92,7 @@ export function createTerrainScene(opts: TerrainOptions): Scene | null { const tier = opts.tier ?? 'full'; if (tier === 'off') return null; const { canvas } = opts; - const probe = opts.probe ?? new URLSearchParams(window.location.search).get('probe') === '1'; + const probe = opts.probe ?? overlayRequested('probe', opts.debug === true); const gl = canvas.getContext('webgl2', { antialias: false, alpha: false, @@ -395,7 +398,7 @@ export function createTerrainScene(opts: TerrainOptions): Scene | null { }); canvas.parentElement.appendChild(readout); } - const removeLabel = showTierLabel(canvas, tier); + const removeLabel = showTierLabel(canvas, tier, opts.debug === true); if (still) settleToStill(); else wake(); if (!still && !moving()) draw(0); diff --git a/src/app/features/scene/terrain/tierLabel.test.ts b/src/app/features/scene/terrain/tierLabel.test.ts index 3e9f3a48..e3f18871 100644 --- a/src/app/features/scene/terrain/tierLabel.test.ts +++ b/src/app/features/scene/terrain/tierLabel.test.ts @@ -29,21 +29,27 @@ afterEach(() => vi.unstubAllGlobals()); describe('showTierLabel', () => { it('adds nothing in a normal build', () => { - expect(showTierLabel(canvas(true), 'full')).toBeNull(); + expect(showTierLabel(canvas(true), 'full', true)).toBeNull(); expect(appended).toHaveLength(0); }); it('names the path on screen when asked with ?tierlabel=1, and removes itself', () => { search = '?tierlabel=1'; - const remove = showTierLabel(canvas(true), 'sky-only'); + const remove = showTierLabel(canvas(true), 'sky-only', true); expect(appended).toHaveLength(1); expect(appended[0].textContent).toBe('terrain · sky-only'); remove!(); expect(appended[0].remove).toHaveBeenCalled(); }); + it('ignores ?tierlabel=1 where the overlays are not allowed (a production page)', () => { + search = '?tierlabel=1'; + expect(showTierLabel(canvas(true), 'full', false)).toBeNull(); + expect(appended).toHaveLength(0); + }); + it('has nowhere to go without a parent', () => { search = '?tierlabel=1'; - expect(showTierLabel(canvas(false), 'full')).toBeNull(); + expect(showTierLabel(canvas(false), 'full', true)).toBeNull(); }); }); diff --git a/src/app/features/scene/terrain/tierLabel.ts b/src/app/features/scene/terrain/tierLabel.ts index 0b2c79ec..4afc8a69 100644 --- a/src/app/features/scene/terrain/tierLabel.ts +++ b/src/app/features/scene/terrain/tierLabel.ts @@ -1,13 +1,19 @@ +import { overlayRequested } from '../debugOverlays'; + // A tiny label naming which terrain path is on screen ('full', 'low', 'still', or 'sky-only' where WebGL2 is missing), -// so a screenshot of a preview says what it shows. Built into preview builds only (VITE_PREVIEW_DEMO=1), or by `?tierlabel=1`. -const wanted = (): boolean => - import.meta.env?.VITE_PREVIEW_DEMO === '1' || - new URLSearchParams(window.location.search).get('tierlabel') === '1'; +// so a screenshot of a preview says what it shows. Built into preview builds (VITE_PREVIEW_DEMO=1), or by `?tierlabel=1` +// where the diagnostic overlays are allowed (debugOverlays.ts). +const wanted = (allowed: boolean): boolean => + import.meta.env?.VITE_PREVIEW_DEMO === '1' || overlayRequested('tierlabel', allowed); /** Adds the label to the canvas's parent; returns its remover, or null when no label is wanted. */ -export function showTierLabel(canvas: HTMLCanvasElement, text: string): (() => void) | null { +export function showTierLabel( + canvas: HTMLCanvasElement, + text: string, + allowed: boolean +): (() => void) | null { const parent = canvas.parentElement; - if (!parent || !wanted()) return null; + if (!parent || !wanted(allowed)) return null; const el = document.createElement('div'); el.textContent = `terrain · ${text}`; Object.assign(el.style, { diff --git a/src/app/features/scene/tier.ts b/src/app/features/scene/tier.ts index 4a976081..ba338f37 100644 --- a/src/app/features/scene/tier.ts +++ b/src/app/features/scene/tier.ts @@ -24,6 +24,8 @@ export type SceneStyle = 'ridge' | 'raymarch' | 'terrain'; export interface BackgroundSceneConfig { tier?: SceneTier; style?: SceneStyle; + /** Let the page URL switch on the diagnostic overlays (`?probe=1`, `?tierlabel=1`) in a production build. Default: no. */ + debugOverlays?: boolean; } export function readCapabilities(nav: Navigator): Capabilities { diff --git a/src/app/pages/auth/footerBand.test.ts b/src/app/pages/auth/footerBand.test.ts index 9d196200..97febbbb 100644 --- a/src/app/pages/auth/footerBand.test.ts +++ b/src/app/pages/auth/footerBand.test.ts @@ -43,9 +43,17 @@ describe('the sign-in footer band, as emitted', () => { ]); }); - it('declares exactly these properties, so nothing else (a shadow, a prefixed override) rides along', async () => { + it('declares exactly these properties, so nothing else (a shadow, a stray override) rides along', async () => { const [rule] = declarations(await emitted(), over); + // -webkit-backdrop-filter is the one prefixed property, for Safari 14; it must blur exactly as the standard one does. + const blur = (prop: string) => + rule + .find((d) => d.startsWith(`${prop}:`)) + ?.split(':')[1] + .trim(); + expect(blur('-webkit-backdrop-filter')).toBe(blur('backdrop-filter')); expect(rule.map((d) => d.slice(0, d.indexOf(':'))).sort()).toEqual([ + '-webkit-backdrop-filter', 'backdrop-filter', 'background-color', 'background-color', From 48d6d171daa7505127170aa1b40afbaca2e91909 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 14:09:00 +0000 Subject: [PATCH 22/56] Sky-only tier label follows the overlay gate; tests pin the door's debug forwarding Co-Authored-By: Claude Sonnet 5.5 --- .../scene/terrain/TerrainSky2D.test.ts | 36 +++++++++++++++++++ .../features/scene/terrain/TerrainSky2D.ts | 7 ++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/app/features/scene/terrain/TerrainSky2D.test.ts b/src/app/features/scene/terrain/TerrainSky2D.test.ts index c3a95d69..d85d8f01 100644 --- a/src/app/features/scene/terrain/TerrainSky2D.test.ts +++ b/src/app/features/scene/terrain/TerrainSky2D.test.ts @@ -168,6 +168,42 @@ describe('createTerrainSkyScene', () => { }); }); +describe('the sky-only tier label', () => { + const labels = (parentAppend: ReturnType) => + parentAppend.mock.calls.filter(([e]) => e?.textContent === 'terrain · sky-only'); + + it('shows for ?tierlabel=1 only where the overlays are allowed', () => { + search = '?tierlabel=1'; + const off = canvasWith(); + createTerrainSkyScene({ canvas: off.canvas, tier: 'low' })!.dispose(); + expect(labels(off.parentAppend)).toHaveLength(0); + const on = canvasWith(); + createTerrainSkyScene({ canvas: on.canvas, tier: 'low', debug: true })!.dispose(); + expect(labels(on.parentAppend)).toHaveLength(1); + }); + + it('the door allows it from the config flag, and not from a plain production config', async () => { + vi.stubEnv('DEV', false); + vi.stubEnv('VITE_PREVIEW_DEMO', ''); + search = '?tierlabel=1'; + const plain = canvasWith(); + ( + await startBackgroundScene({ tier: 'low', style: 'terrain' }, {}, { canvas: plain.canvas }) + )?.dispose(); + expect(labels(plain.parentAppend)).toHaveLength(0); + const flagged = canvasWith(); + ( + await startBackgroundScene( + { tier: 'low', style: 'terrain', debugOverlays: true }, + {}, + { canvas: flagged.canvas } + ) + )?.dispose(); + expect(labels(flagged.parentAppend)).toHaveLength(1); + vi.unstubAllEnvs(); + }); +}); + describe('startBackgroundScene, terrain style', () => { it('without WebGL2 mounts the sky on the same canvas instead of returning null', async () => { const { canvas, painted } = canvasWith({ gl: null }); diff --git a/src/app/features/scene/terrain/TerrainSky2D.ts b/src/app/features/scene/terrain/TerrainSky2D.ts index 71bc7a78..4653d470 100644 --- a/src/app/features/scene/terrain/TerrainSky2D.ts +++ b/src/app/features/scene/terrain/TerrainSky2D.ts @@ -9,7 +9,10 @@ import { CAMERA, view } from './terrainWorld'; export const SKY_WIDTH = 96; /** The sky of the terrain scene without WebGL2: the camera's first frame of `skyColour`, once, on a 2D canvas, with the star layer on top. */ -export type TerrainSkyOptions = Pick; +export type TerrainSkyOptions = Pick< + TerrainOptions, + 'canvas' | 'tier' | 'sky' | 'sun' | 'band' | 'debug' +>; /** * Same contract as `createTerrainScene`. Paints the terrain scene's sky (the CPU twin of its sky shader, from the @@ -69,7 +72,7 @@ export function createTerrainSkyScene(opts: TerrainSkyOptions): Scene | null { }; window.addEventListener('resize', onResize); paint(); - const removeLabel = showTierLabel(canvas, 'sky-only'); + const removeLabel = showTierLabel(canvas, 'sky-only', opts.debug === true); return { login() { From 9b9b495defbc193b6b7665662bfb6889818f12e9 Mon Sep 17 00:00:00 2001 From: Eureka Self Date: Tue, 10 Nov 2026 14:09:58 +0000 Subject: [PATCH 23/56] nemesis: drop the pose status strip; the portraits already show each Mind's mood (Meta: redundant, wastes vertical space) Co-Authored-By: Claude Opus 5.5 --- src/app/components/message/pose/poses.test.ts | 50 ------------ src/app/components/message/pose/poses.ts | 47 ----------- src/app/features/room/RoomView.tsx | 2 - .../features/room/RoomViewPoseStatus.css.ts | 40 ---------- src/app/features/room/RoomViewPoseStatus.tsx | 80 ------------------- 5 files changed, 219 deletions(-) delete mode 100644 src/app/features/room/RoomViewPoseStatus.css.ts delete mode 100644 src/app/features/room/RoomViewPoseStatus.tsx diff --git a/src/app/components/message/pose/poses.test.ts b/src/app/components/message/pose/poses.test.ts index 81c2f0c6..c6089f62 100644 --- a/src/app/components/message/pose/poses.test.ts +++ b/src/app/components/message/pose/poses.test.ts @@ -3,10 +3,7 @@ import catalogue from './catalogue.json'; import { mindOfSender, poseSrc, - poseStatusLine, - poseStatuses, resolvePose, - splitNewestStatus, } from './poses'; describe('resolvePose', () => { @@ -53,52 +50,5 @@ describe('poseSrc', () => { }); }); -describe('poseStatuses', () => { - const ev = (sender: string, ts: number, label: unknown) => ({ - sender, - localpart: sender, - ts, - label, - }); - it('reports the latest pose per Mind and when its unbroken run began', () => { - const out = poseStatuses([ - ev('aqua', 1, 'happy'), - ev('aqua', 2, 'sigh'), - ev('aqua', 3, 'sigh'), - ev('alice', 4, 'happy'), - ev('eureka', 5, 'probe'), - ]); - expect(out).toEqual([ - { mind: 'aqua', pose: 'sigh', since: 2 }, - { mind: 'eureka', pose: 'probe', since: 5 }, - ]); - }); - it('treats unknown labels as neutral runs', () => { - expect(poseStatuses([ev('void', 1, 'bogus'), ev('void', 2, undefined)])).toEqual([ - { mind: 'void', pose: 'neutral', since: 1 }, - ]); - }); -}); -describe('poseStatusLine', () => { - it('formats the client-status line', () => { - const since = new Date(2026, 10, 4, 9, 5).getTime(); - expect(poseStatusLine({ mind: 'void', pose: 'reading', since })).toBe( - 'Ratatoskr: Void is reading, since 09:05' - ); - }); -}); -describe('splitNewestStatus', () => { - const s = (mind: string, since: number) => ({ mind, pose: 'happy', since }); - - it('picks the status that changed last and keeps the others in their order', () => { - const { newest, rest } = splitNewestStatus([s('eureka', 100), s('void', 300), s('nibu', 200)]); - expect(newest?.mind).toBe('void'); - expect(rest.map((r) => r.mind)).toEqual(['eureka', 'nibu']); - }); - - it('has no newest for an empty list', () => { - expect(splitNewestStatus([])).toEqual({ newest: undefined, rest: [] }); - }); -}); diff --git a/src/app/components/message/pose/poses.ts b/src/app/components/message/pose/poses.ts index 7175196a..69429090 100644 --- a/src/app/components/message/pose/poses.ts +++ b/src/app/components/message/pose/poses.ts @@ -40,50 +40,3 @@ export const resolvePose = (mind: string, label: unknown): string => { export const poseSrc = (mind: string, pose: string, base = '/'): string => `${base}public/poses/${artPath(mind, pose) ?? artPath(mind, NEUTRAL)}`; -export type PoseStatus = { mind: string; pose: string; since: number }; -export type PoseEvent = { - sender: string; - localpart?: string; - name?: string; - ts: number; - label: unknown; -}; - -// Latest pose per Mind in a room's events (oldest first), and when that pose -// began: the first of its unbroken run of messages carrying the same pose. -export const poseStatuses = (events: PoseEvent[]): PoseStatus[] => { - const bySender = new Map(); - events.forEach((e) => { - const mind = mindOfSender(e.localpart, e.name); - if (!mind) return; - const entry = bySender.get(e.sender) ?? { mind, run: [] }; - entry.run.push({ pose: resolvePose(mind, e.label), ts: e.ts }); - bySender.set(e.sender, entry); - }); - return [...bySender.values()].map(({ mind, run }) => { - const last = run[run.length - 1]; - let i = run.length - 1; - while (i > 0 && run[i - 1].pose === last.pose) i -= 1; - return { mind, pose: last.pose, since: run[i].ts }; - }); -}; - -// The status that changed last, and the rest in their given order (phones show only the first). -export const splitNewestStatus = ( - statuses: PoseStatus[] -): { newest: PoseStatus | undefined; rest: PoseStatus[] } => { - let newest: PoseStatus | undefined; - statuses.forEach((st) => { - if (!newest || st.since > newest.since) newest = st; - }); - return { newest, rest: statuses.filter((st) => st !== newest) }; -}; - -const two = (n: number) => String(n).padStart(2, '0'); - -// Client-status text. Rendered locally only; it is never sent to a room. -export const poseStatusLine = ({ mind, pose, since }: PoseStatus): string => { - const d = new Date(since); - const name = mind.charAt(0).toUpperCase() + mind.slice(1); - return `Ratatoskr: ${name} is ${pose}, since ${two(d.getHours())}:${two(d.getMinutes())}`; -}; diff --git a/src/app/features/room/RoomView.tsx b/src/app/features/room/RoomView.tsx index cf91c638..3fbd81fa 100644 --- a/src/app/features/room/RoomView.tsx +++ b/src/app/features/room/RoomView.tsx @@ -11,7 +11,6 @@ import { useEditor } from '../../components/editor'; import { RoomInputPlaceholder } from './RoomInputPlaceholder'; import { RoomTimeline } from './RoomTimeline'; import { RoomViewTyping } from './RoomViewTyping'; -import { RoomViewPoseStatus } from './RoomViewPoseStatus'; import { RoomTombstone } from './RoomTombstone'; import { RoomInput } from './RoomInput'; import { RoomTrainingLine } from './RoomTrainingLine'; @@ -102,7 +101,6 @@ export function RoomView({ eventId }: { eventId?: string }) { roomInputRef={roomInputRef} editor={editor} /> - diff --git a/src/app/features/room/RoomViewPoseStatus.css.ts b/src/app/features/room/RoomViewPoseStatus.css.ts deleted file mode 100644 index d8319d0d..00000000 --- a/src/app/features/room/RoomViewPoseStatus.css.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { style } from '@vanilla-extract/css'; -import { DefaultReset, color, config, toRem } from 'folds'; - -const NARROW = 'screen and (max-width: 480px)'; - -export const StatusBlock = style([ - DefaultReset, - { - display: 'flex', - flexDirection: 'column', - flexShrink: 0, - padding: `0 ${toRem(16)}`, - // Phones: the newest mood and its "+N more" share one line. - '@media': { [NARROW]: { flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center' } }, - }, -]); - -// On phones only the newest mood shows until the strip is tapped; wide screens show every line. -export const StatusRow = style({ - '@media': { - [NARROW]: { - selectors: { - '&[data-newest="false"]': { display: 'none' }, - [`${StatusBlock}[data-open="true"] &`]: { display: 'block', flexBasis: '100%' }, - }, - }, - }, -}); - -export const StatusToggle = style([ - DefaultReset, - { - display: 'none', - minHeight: toRem(24), - padding: `0 ${config.space.S100}`, - color: color.Primary.Main, - cursor: 'pointer', - '@media': { [NARROW]: { display: 'block' } }, - }, -]); diff --git a/src/app/features/room/RoomViewPoseStatus.tsx b/src/app/features/room/RoomViewPoseStatus.tsx deleted file mode 100644 index e62f81c0..00000000 --- a/src/app/features/room/RoomViewPoseStatus.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Text } from 'folds'; -import { Room, RoomEvent } from 'matrix-js-sdk'; -import { settingsAtom } from '../../state/settings'; -import { useSetting } from '../../state/hooks/settings'; -import { getMxIdLocalPart } from '../../utils/matrix'; -import { getMemberDisplayName } from '../../utils/room'; -import { - POSE_KEY, - poseStatusLine, - poseStatuses, - splitNewestStatus, -} from '../../components/message/pose/poses'; -import * as css from './RoomViewPoseStatus.css'; - -// Client-status lines about each Mind's current pose. They are drawn from the -// timeline already in memory and are never sent as anyone's message. -export function RoomViewPoseStatus({ room }: { room: Room }) { - const [poseBubbles] = useSetting(settingsAtom, 'poseBubbles'); - const [, bump] = useState(0); - const [open, setOpen] = useState(false); - - useEffect(() => { - const onTimeline = () => bump((n) => n + 1); - room.on(RoomEvent.Timeline, onTimeline); - return () => { - room.removeListener(RoomEvent.Timeline, onTimeline); - }; - }, [room]); - - if (!poseBubbles) return null; - - const statuses = poseStatuses( - room - .getLiveTimeline() - .getEvents() - .filter((e) => e.getType() === 'm.room.message' && e.getSender()) - .map((e) => { - const sender = e.getSender() as string; - return { - sender, - localpart: getMxIdLocalPart(sender), - name: getMemberDisplayName(room, sender), - ts: e.getTs(), - label: e.getContent()[POSE_KEY], - }; - }) - ); - if (statuses.length === 0) return null; - - const { newest, rest } = splitNewestStatus(statuses); - - return ( -
    - {statuses.map((st) => ( - - {poseStatusLine(st)} - - ))} - {rest.length > 0 && ( - - )} -
    - ); -} From 3c92696507b4b9b9bfc6a46ea7e5cdea3d7014e0 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 14:15:47 +0000 Subject: [PATCH 24/56] Room list: groups of unjoined project rooms wait behind one Browse entry A steward group with no joined or invited room collapses into "Browse N more rooms" per tier, closed on every screen and remembered per device; a group the user is in anything of, loose estate rooms, and joined rooms stay up front. splitGroups is the pure rule, tested. Co-Authored-By: Claude Sonnet 5.5 --- src/app/features/estate/EstateNavigator.tsx | 52 ++++++++++++++++----- src/app/features/estate/buildEstate.test.ts | 39 +++++++++++++++- src/app/features/estate/buildEstate.ts | 14 ++++++ 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/src/app/features/estate/EstateNavigator.tsx b/src/app/features/estate/EstateNavigator.tsx index 2cbbfe4a..56b554fa 100644 --- a/src/app/features/estate/EstateNavigator.tsx +++ b/src/app/features/estate/EstateNavigator.tsx @@ -38,7 +38,7 @@ import { useClosedNavCategoriesAtom } from '../../state/hooks/closedNavCategorie import { makeNavCategoryId } from '../../state/closedNavCategories'; import { roomToUnreadAtom } from '../../state/room/roomToUnread'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; -import { EstateRoom, EstateTier } from './buildEstate'; +import { EstateGroup, EstateRoom, EstateTier, splitGroups } from './buildEstate'; import { activateEstateRoom, awaitMyJoin } from './joinEstateRoom'; import { useEstate, useEstateRootId } from './useEstate'; import * as css from './EstateNavigator.css'; @@ -167,25 +167,53 @@ function Collapsible({ categoryId, label, rooms, children }: CollapsibleProps) { ); } +function GroupSection({ tierId, group }: { tierId: string; group: EstateGroup }) { + return ( + + {group.rooms.map((r) => ( + + ))} + + ); +} + +/** + * Groups of rooms the user has not joined, behind one quiet entry. Closed until opened, on every screen; the opening is + * remembered per device like any other section. + */ +function Browse({ tierId, groups }: { tierId: string; groups: EstateGroup[] }) { + const [toggled, setToggled] = useAtom(useClosedNavCategoriesAtom()); + const key = makeNavCategoryId('estate', 'browse', tierId); + const open = toggled.has(key); + const count = groups.reduce((n, g) => n + g.rooms.length, 0); + + return ( + + + setToggled({ type: open ? 'DELETE' : 'PUT', categoryId: key })} + > + {`Browse ${count} more ${count === 1 ? 'room' : 'rooms'}`} + + + {open && groups.map((g) => )} + + ); +} + function Tier({ tier }: { tier: EstateTier }) { const allRooms = useMemo(() => [...tier.loose, ...tier.groups.flatMap((g) => g.rooms)], [tier]); + const { front, browse } = useMemo(() => splitGroups(tier), [tier]); return ( {tier.loose.map((r) => ( ))} - {tier.groups.map((g) => ( - - {g.rooms.map((r) => ( - - ))} - + {front.map((g) => ( + ))} + {browse.length > 0 && } ); } diff --git a/src/app/features/estate/buildEstate.test.ts b/src/app/features/estate/buildEstate.test.ts index 8f898a0f..7b63eac5 100644 --- a/src/app/features/estate/buildEstate.test.ts +++ b/src/app/features/estate/buildEstate.test.ts @@ -1,5 +1,11 @@ import { describe, expect, it } from 'vitest'; -import { buildEstate, EstateHierarchyRoom, EstateInput, stewardOfChild } from './buildEstate'; +import { + buildEstate, + EstateHierarchyRoom, + EstateInput, + splitGroups, + stewardOfChild, +} from './buildEstate'; const ROOT = '!root:gamecult.org'; const PROJECTS = '!projects:gamecult.org'; @@ -324,3 +330,34 @@ describe('direct invites', () => { ]); }); }); + +describe('splitGroups', () => { + const projects = (membership: EstateInput['membership']) => + buildEstate(input({ membership })).find((t) => t.id === PROJECTS)!; + const labels = (gs: { mind: string }[]) => gs.map((g) => g.mind); + + it('puts a group nobody has joined behind Browse, and keeps loose estate rooms out of it', () => { + const tier = projects(new Map()); + const { front, browse } = splitGroups(tier); + expect(labels(front)).toEqual([]); + expect(labels(browse).sort()).toEqual(['eureka', 'void']); + expect(tier.loose.map((r) => r.name)).toEqual(['Misc']); + }); + + it('keeps a group up front once any room in it is joined, its unjoined siblings with it', () => { + const { front, browse } = splitGroups(projects(new Map([['!cultlib:gamecult.org', 'join']]))); + expect(labels(front)).toEqual(['eureka']); + expect(labels(browse)).toEqual(['void']); + }); + + it('keeps a group up front for an invite, which is something to act on', () => { + const { front } = splitGroups(projects(new Map([['!aeth:gamecult.org', 'invite']]))); + expect(labels(front)).toEqual(['void']); + }); + + it('loses no group: front and browse together are the tier groups', () => { + const tier = projects(new Map([['!cultlib:gamecult.org', 'join']])); + const { front, browse } = splitGroups(tier); + expect([...front, ...browse].sort((a, b) => a.mind.localeCompare(b.mind))).toEqual(tier.groups); + }); +}); diff --git a/src/app/features/estate/buildEstate.ts b/src/app/features/estate/buildEstate.ts index b9232beb..ca656211 100644 --- a/src/app/features/estate/buildEstate.ts +++ b/src/app/features/estate/buildEstate.ts @@ -71,6 +71,20 @@ export type EstateInput = { mindLabels?: ReadonlyMap; }; +/** + * A tier's project-room groups split for the navigator: a group the user is in anything of (joined, or invited, which is + * something to act on) stays up front; a group of rooms nobody has joined waits behind one Browse entry, so the first + * screen is conversations, not a wall of Join buttons. Loose (estate) rooms are never grouped away. + */ +export const splitGroups = (tier: EstateTier): { front: EstateGroup[]; browse: EstateGroup[] } => { + const front: EstateGroup[] = []; + const browse: EstateGroup[] = []; + tier.groups.forEach((g) => + (g.rooms.some((r) => r.state !== 'joinable') ? front : browse).push(g) + ); + return { front, browse }; +}; + export const DIRECT_TIER_ID = 'direct'; export const ELSEWHERE_TIER_ID = 'elsewhere'; From 3c868254e549ffe999ad3b448409e5e68931c5ee Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 14:47:41 +0000 Subject: [PATCH 25/56] Category buttons expose aria-expanded RoomNavCategoryButton set open/closed only through its chevron. It now sets aria-expanded from `closed` (absent when the section does not toggle); tested by rendering to static markup. Co-Authored-By: Claude Sonnet 5.5 --- .../room-nav/RoomNavCategoryButton.test.tsx | 21 +++++++++++++++++++ .../room-nav/RoomNavCategoryButton.tsx | 1 + 2 files changed, 22 insertions(+) create mode 100644 src/app/features/room-nav/RoomNavCategoryButton.test.tsx diff --git a/src/app/features/room-nav/RoomNavCategoryButton.test.tsx b/src/app/features/room-nav/RoomNavCategoryButton.test.tsx new file mode 100644 index 00000000..a500f920 --- /dev/null +++ b/src/app/features/room-nav/RoomNavCategoryButton.test.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { describe, expect, it } from 'vitest'; +import { RoomNavCategoryButton } from './RoomNavCategoryButton'; + +const html = (closed?: boolean) => + renderToStaticMarkup(Browse); + +describe('RoomNavCategoryButton', () => { + it('says it is collapsed when closed, so assistive tech does not rely on the chevron', () => { + expect(html(true)).toContain('aria-expanded="false"'); + }); + + it('says it is expanded when open', () => { + expect(html(false)).toContain('aria-expanded="true"'); + }); + + it('claims neither when the section does not toggle', () => { + expect(html(undefined)).not.toContain('aria-expanded'); + }); +}); diff --git a/src/app/features/room-nav/RoomNavCategoryButton.tsx b/src/app/features/room-nav/RoomNavCategoryButton.tsx index 3af8aeac..e232b856 100644 --- a/src/app/features/room-nav/RoomNavCategoryButton.tsx +++ b/src/app/features/room-nav/RoomNavCategoryButton.tsx @@ -16,6 +16,7 @@ export const RoomNavCategoryButton = as<'button', { closed?: boolean }>( src={closed ? Icons.ChevronRight : Icons.ChevronBottom} /> } + aria-expanded={closed === undefined ? undefined : !closed} {...props} ref={ref} > From 9877154033f65a14002952a86e075f39169e4adc Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 17:03:17 +0000 Subject: [PATCH 26/56] dings: port the hook from #36 onto dev, wire Aqua's v3 set behind dings.enabled (default off) Hook code is #36 (b3eb9d01) verbatim plus the switch. Clips and manifest are AquaSynth PR #22 out/, Nibu on proposal 1. Nobody has heard v3. Co-Authored-By: Claude Sonnet 5.5 --- config.json | 4 + public/dings/README.md | 17 +++ public/dings/manifest.json | 58 ++++++++ public/dings/neutral/dm.ogg | Bin 0 -> 13525 bytes public/dings/neutral/invite.ogg | Bin 0 -> 11198 bytes public/dings/neutral/mention.ogg | Bin 0 -> 9051 bytes public/dings/neutral/message.ogg | Bin 0 -> 7274 bytes public/dings/senders/aqua/dm.ogg | Bin 0 -> 4687 bytes public/dings/senders/aqua/invite.ogg | Bin 0 -> 4863 bytes public/dings/senders/aqua/mention.ogg | Bin 0 -> 4538 bytes public/dings/senders/aqua/message.ogg | Bin 0 -> 4563 bytes public/dings/senders/eureka/dm.ogg | Bin 0 -> 4583 bytes public/dings/senders/eureka/invite.ogg | Bin 0 -> 4727 bytes public/dings/senders/eureka/mention.ogg | Bin 0 -> 4443 bytes public/dings/senders/eureka/message.ogg | Bin 0 -> 4425 bytes public/dings/senders/heimdall/dm.ogg | Bin 0 -> 5184 bytes public/dings/senders/heimdall/invite.ogg | Bin 0 -> 5476 bytes public/dings/senders/heimdall/mention.ogg | Bin 0 -> 5217 bytes public/dings/senders/heimdall/message.ogg | Bin 0 -> 4746 bytes public/dings/senders/kiko/dm.ogg | Bin 0 -> 5703 bytes public/dings/senders/kiko/invite.ogg | Bin 0 -> 5627 bytes public/dings/senders/kiko/mention.ogg | Bin 0 -> 5044 bytes public/dings/senders/kiko/message.ogg | Bin 0 -> 5090 bytes public/dings/senders/libby/dm.ogg | Bin 0 -> 5426 bytes public/dings/senders/libby/invite.ogg | Bin 0 -> 5613 bytes public/dings/senders/libby/mention.ogg | Bin 0 -> 5108 bytes public/dings/senders/libby/message.ogg | Bin 0 -> 5075 bytes public/dings/senders/nibu/dm.ogg | Bin 0 -> 6138 bytes public/dings/senders/nibu/invite.ogg | Bin 0 -> 6151 bytes public/dings/senders/nibu/mention.ogg | Bin 0 -> 5734 bytes public/dings/senders/nibu/message.ogg | Bin 0 -> 5354 bytes public/dings/senders/void/dm.ogg | Bin 0 -> 5402 bytes public/dings/senders/void/invite.ogg | Bin 0 -> 5667 bytes public/dings/senders/void/mention.ogg | Bin 0 -> 5022 bytes public/dings/senders/void/message.ogg | Bin 0 -> 5362 bytes public/dings/senders/weksa/dm.ogg | Bin 0 -> 5682 bytes public/dings/senders/weksa/invite.ogg | Bin 0 -> 6120 bytes public/dings/senders/weksa/mention.ogg | Bin 0 -> 5291 bytes public/dings/senders/weksa/message.ogg | Bin 0 -> 5272 bytes src/app/features/dings/dings.test.ts | 144 +++++++++++++++++++ src/app/features/dings/dings.ts | 100 +++++++++++++ src/app/features/dings/manifest.test.ts | 80 +++++++++++ src/app/features/dings/useDings.ts | 55 +++++++ src/app/hooks/useClientConfig.ts | 4 + src/app/pages/client/ClientNonUIFeatures.tsx | 28 +++- vite.config.js | 4 + 46 files changed, 491 insertions(+), 3 deletions(-) create mode 100644 public/dings/README.md create mode 100644 public/dings/manifest.json create mode 100644 public/dings/neutral/dm.ogg create mode 100644 public/dings/neutral/invite.ogg create mode 100644 public/dings/neutral/mention.ogg create mode 100644 public/dings/neutral/message.ogg create mode 100644 public/dings/senders/aqua/dm.ogg create mode 100644 public/dings/senders/aqua/invite.ogg create mode 100644 public/dings/senders/aqua/mention.ogg create mode 100644 public/dings/senders/aqua/message.ogg create mode 100644 public/dings/senders/eureka/dm.ogg create mode 100644 public/dings/senders/eureka/invite.ogg create mode 100644 public/dings/senders/eureka/mention.ogg create mode 100644 public/dings/senders/eureka/message.ogg create mode 100644 public/dings/senders/heimdall/dm.ogg create mode 100644 public/dings/senders/heimdall/invite.ogg create mode 100644 public/dings/senders/heimdall/mention.ogg create mode 100644 public/dings/senders/heimdall/message.ogg create mode 100644 public/dings/senders/kiko/dm.ogg create mode 100644 public/dings/senders/kiko/invite.ogg create mode 100644 public/dings/senders/kiko/mention.ogg create mode 100644 public/dings/senders/kiko/message.ogg create mode 100644 public/dings/senders/libby/dm.ogg create mode 100644 public/dings/senders/libby/invite.ogg create mode 100644 public/dings/senders/libby/mention.ogg create mode 100644 public/dings/senders/libby/message.ogg create mode 100644 public/dings/senders/nibu/dm.ogg create mode 100644 public/dings/senders/nibu/invite.ogg create mode 100644 public/dings/senders/nibu/mention.ogg create mode 100644 public/dings/senders/nibu/message.ogg create mode 100644 public/dings/senders/void/dm.ogg create mode 100644 public/dings/senders/void/invite.ogg create mode 100644 public/dings/senders/void/mention.ogg create mode 100644 public/dings/senders/void/message.ogg create mode 100644 public/dings/senders/weksa/dm.ogg create mode 100644 public/dings/senders/weksa/invite.ogg create mode 100644 public/dings/senders/weksa/mention.ogg create mode 100644 public/dings/senders/weksa/message.ogg create mode 100644 src/app/features/dings/dings.test.ts create mode 100644 src/app/features/dings/dings.ts create mode 100644 src/app/features/dings/manifest.test.ts create mode 100644 src/app/features/dings/useDings.ts diff --git a/config.json b/config.json index dd4c887b..ed1bca6b 100644 --- a/config.json +++ b/config.json @@ -20,5 +20,9 @@ "backgroundScene": { "tier": "off" + }, + + "dings": { + "enabled": false } } diff --git a/public/dings/README.md b/public/dings/README.md new file mode 100644 index 00000000..573a78f9 --- /dev/null +++ b/public/dings/README.md @@ -0,0 +1,17 @@ +# Dings + +Pre-rendered earcons, served at `public/dings/` and read at runtime by `src/app/features/dings`. +Aqua renders the clips; nothing here is synthesised in the browser. + + dings/manifest.json + dings/senders//.ogg + dings/neutral/.ogg + +`manifest.json` is `{ "neutral": { : "" }, "senders": { : { : "" } } }` +with motif one of `mention`, `dm`, `message`, `invite`; paths are relative to this folder, `.ogg` or `.mp3`. +A sender's own clip wins, else the neutral clip, else silence. Without a `manifest.json` the client keeps +its stock notification sounds; once one exists, a motif with no clip is silent. + +The set here is Aqua's v3 (AquaSynth PR #22, staged at minds.gamecult.org/aqua/dings-voices-70a9795), +Nibu on proposal 1 (static-filter square). Nobody has heard it. The client plays none of it unless the +client config sets `"dings": { "enabled": true }` (default off, as in `config.json`). diff --git a/public/dings/manifest.json b/public/dings/manifest.json new file mode 100644 index 00000000..7096d195 --- /dev/null +++ b/public/dings/manifest.json @@ -0,0 +1,58 @@ +{ + "neutral": { + "mention": "neutral/mention.ogg", + "dm": "neutral/dm.ogg", + "message": "neutral/message.ogg", + "invite": "neutral/invite.ogg" + }, + "senders": { + "aqua": { + "mention": "senders/aqua/mention.ogg", + "dm": "senders/aqua/dm.ogg", + "message": "senders/aqua/message.ogg", + "invite": "senders/aqua/invite.ogg" + }, + "eureka": { + "mention": "senders/eureka/mention.ogg", + "dm": "senders/eureka/dm.ogg", + "message": "senders/eureka/message.ogg", + "invite": "senders/eureka/invite.ogg" + }, + "heimdall": { + "mention": "senders/heimdall/mention.ogg", + "dm": "senders/heimdall/dm.ogg", + "message": "senders/heimdall/message.ogg", + "invite": "senders/heimdall/invite.ogg" + }, + "kiko": { + "mention": "senders/kiko/mention.ogg", + "dm": "senders/kiko/dm.ogg", + "message": "senders/kiko/message.ogg", + "invite": "senders/kiko/invite.ogg" + }, + "libby": { + "mention": "senders/libby/mention.ogg", + "dm": "senders/libby/dm.ogg", + "message": "senders/libby/message.ogg", + "invite": "senders/libby/invite.ogg" + }, + "nibu": { + "mention": "senders/nibu/mention.ogg", + "dm": "senders/nibu/dm.ogg", + "message": "senders/nibu/message.ogg", + "invite": "senders/nibu/invite.ogg" + }, + "void": { + "mention": "senders/void/mention.ogg", + "dm": "senders/void/dm.ogg", + "message": "senders/void/message.ogg", + "invite": "senders/void/invite.ogg" + }, + "weksa": { + "mention": "senders/weksa/mention.ogg", + "dm": "senders/weksa/dm.ogg", + "message": "senders/weksa/message.ogg", + "invite": "senders/weksa/invite.ogg" + } + } +} \ No newline at end of file diff --git a/public/dings/neutral/dm.ogg b/public/dings/neutral/dm.ogg new file mode 100644 index 0000000000000000000000000000000000000000..8f7a269e1e9a98304f53deadc1fa52808660e341 GIT binary patch literal 13525 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 literal 0 HcmV?d00001 diff --git a/public/dings/neutral/invite.ogg b/public/dings/neutral/invite.ogg new file mode 100644 index 0000000000000000000000000000000000000000..8def2b7708e49b8dd3234c4290e31326c3789070 GIT binary patch literal 11198 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 Tl^D;oeXtpvpr8MyD*C?w^;=*f literal 0 HcmV?d00001 diff --git a/public/dings/neutral/message.ogg b/public/dings/neutral/message.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f7e17fd46b51a2cdbf49f41360eea38bb2a28de5 GIT binary patch literal 7274 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 NdYfE+T-gMGe*>~s(Ifx> literal 0 HcmV?d00001 diff --git a/public/dings/senders/aqua/mention.ogg b/public/dings/senders/aqua/mention.ogg new file mode 100644 index 0000000000000000000000000000000000000000..1f9c3ffa422b5dbffcabc248e9ca864904513cdc GIT binary patch literal 4538 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/eureka/dm.ogg b/public/dings/senders/eureka/dm.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a64c22f60b4025f0ac3569c2edf5cbd8126740c5 GIT binary patch literal 4583 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/eureka/invite.ogg b/public/dings/senders/eureka/invite.ogg new file mode 100644 index 0000000000000000000000000000000000000000..708dd38d86c0c07f62d0c4142f3c36e36137a38a GIT binary patch literal 4727 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/heimdall/dm.ogg b/public/dings/senders/heimdall/dm.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f4844c021e4bca621f43c9cc89dec0f19c16c0d0 GIT binary patch literal 5184 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/heimdall/invite.ogg b/public/dings/senders/heimdall/invite.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6e27e760b4f9aa1387826ffc5633be9da21ec8c9 GIT binary patch literal 5476 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/heimdall/mention.ogg b/public/dings/senders/heimdall/mention.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e006414703fe25088289ba127c85f579645d7cf1 GIT binary patch literal 5217 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/kiko/invite.ogg b/public/dings/senders/kiko/invite.ogg new file mode 100644 index 0000000000000000000000000000000000000000..45d02d52bbdc736f2dbf40c8a789e530d3b575b2 GIT binary patch literal 5627 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/kiko/mention.ogg b/public/dings/senders/kiko/mention.ogg new file mode 100644 index 0000000000000000000000000000000000000000..388dbbabfff693398d68b15fdbf993f3a4d85163 GIT binary patch literal 5044 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/kiko/message.ogg b/public/dings/senders/kiko/message.ogg new file mode 100644 index 0000000000000000000000000000000000000000..8eae8e4e171a3b0544a0db81125821e0966916fa GIT binary patch literal 5090 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/libby/invite.ogg b/public/dings/senders/libby/invite.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f88e6d2877996eb88e609a02884f7b57fc8c18d2 GIT binary patch literal 5613 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/libby/message.ogg b/public/dings/senders/libby/message.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d923f60bdd2fad0bc6ecfdebc6e4de6b0f60685b GIT binary patch literal 5075 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 H3p)P;7?nxp literal 0 HcmV?d00001 diff --git a/public/dings/senders/nibu/invite.ogg b/public/dings/senders/nibu/invite.ogg new file mode 100644 index 0000000000000000000000000000000000000000..4111e533e78ec7f5df3280c261ac841de4053252 GIT binary patch literal 6151 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 literal 0 HcmV?d00001 diff --git a/public/dings/senders/nibu/mention.ogg b/public/dings/senders/nibu/mention.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a36a9325a50b4f17498b933135017b1471ea313f GIT binary patch literal 5734 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 wQtrK2xO{-9H*IsL{wbyt5)^A&A=yo6j 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 WAtHIBATkPT^ZJ(-lp7x@f`0?$4`aUo literal 0 HcmV?d00001 diff --git a/public/dings/senders/void/mention.ogg b/public/dings/senders/void/mention.ogg new file mode 100644 index 0000000000000000000000000000000000000000..80ec43e39c6f8d7d48bf87f1aabd5dd4340e4278 GIT binary patch literal 5022 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 QD~|%Z!A*gaE4{)00P0INDgXcg literal 0 HcmV?d00001 diff --git a/public/dings/senders/void/message.ogg b/public/dings/senders/void/message.ogg new file mode 100644 index 0000000000000000000000000000000000000000..5c8fd5c87a32a70d32a665803b05f212e3490cd3 GIT binary patch literal 5362 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 Q&q9wjK@#Ps{#Vrh2RQ}OrT_o{ literal 0 HcmV?d00001 diff --git a/public/dings/senders/weksa/invite.ogg b/public/dings/senders/weksa/invite.ogg new file mode 100644 index 0000000000000000000000000000000000000000..b9dd564347dba2d527ccc66b45b46e07d619e2cb GIT binary patch literal 6120 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 DQ{hdY literal 0 HcmV?d00001 diff --git a/src/app/features/dings/dings.test.ts b/src/app/features/dings/dings.test.ts new file mode 100644 index 00000000..d5c5e601 --- /dev/null +++ b/src/app/features/dings/dings.test.ts @@ -0,0 +1,144 @@ +import { describe, expect, it, vi } from 'vitest'; +import { DING_VOLUME, createDingPlayer, motifFor, parseManifest, pickDing } from './dings'; + +const manifest = parseManifest({ + neutral: { mention: 'neutral/mention.ogg', message: 'neutral/message.ogg' }, + senders: { + eureka: { mention: 'eureka/mention.ogg', message: 'eureka/message.ogg', dm: 'eureka/dm.ogg' }, + nibu: { message: 'nibu/message.ogg' }, + }, +})!; + +describe('dings manifest', () => { + it('is read defensively: junk is no manifest, bad paths are dropped', () => { + expect(parseManifest(undefined)).toBeUndefined(); + expect(parseManifest('x')).toBeUndefined(); + expect(parseManifest({ neutral: 3, senders: [] })).toBeUndefined(); + const m = parseManifest({ + neutral: { message: '../../etc/passwd.ogg', mention: 'https://evil.example/a.ogg' }, + senders: { void: { message: 'void/message.wav', dm: 'void/dm.ogg', mention: '/abs.ogg' } }, + })!; + expect(m.neutral).toEqual({}); + expect(m.senders.void).toEqual({ dm: 'void/dm.ogg' }); + }); +}); + +describe('manifest guards', () => { + it('rejects a dot segment in the middle of a path', () => { + const m = parseManifest({ + neutral: { message: 'a/../b.ogg', mention: 'a/b.ogg' }, + senders: {}, + })!; + expect(m.neutral).toEqual({ mention: 'a/b.ogg' }); + }); + + it('is not fooled by an array of senders', () => { + expect(parseManifest({ neutral: { message: 'a.ogg' }, senders: [] })).toBeUndefined(); + }); + + it('ignores motifs a clip set only inherits', () => { + const inherited = Object.create({ message: 'x/message.ogg' }); + const m = parseManifest({ neutral: inherited, senders: {} })!; + expect(m.neutral).toEqual({}); + }); + + it('gives object-prototype names no clip of their own', () => { + const m = parseManifest({ neutral: { message: 'n/message.ogg' }, senders: {} })!; + ['__proto__', 'constructor', 'toString', 'hasOwnProperty'].forEach((who) => { + expect(pickDing(m, who, 'message')?.path).toBe('n/message.ogg'); + }); + }); + + it('reports the motif volume in the pick itself', () => { + expect(pickDing(manifest, 'eureka', 'message')?.volume).toBe(DING_VOLUME.message); + expect(pickDing(manifest, 'stranger', 'mention')?.volume).toBe(1); + }); +}); + +describe('which ding plays', () => { + it('uses the sender instrument for the motif', () => { + expect(pickDing(manifest, 'eureka', 'mention')).toEqual({ + path: 'eureka/mention.ogg', + volume: DING_VOLUME.mention, + }); + }); + + it('falls back to the neutral ding for an unknown sender', () => { + expect(pickDing(manifest, 'stranger', 'message')?.path).toBe('neutral/message.ogg'); + }); + + it('falls back to neutral when the sender has no clip for that motif', () => { + expect(pickDing(manifest, 'nibu', 'mention')?.path).toBe('neutral/mention.ogg'); + }); + + it('is silent when neither the sender nor neutral has a clip', () => { + expect(pickDing(manifest, 'eureka', 'invite')).toBeUndefined(); + expect(pickDing(manifest, 'stranger', 'dm')).toBeUndefined(); + }); + + it('is loud for mentions and DMs and quiet for ordinary messages', () => { + expect(DING_VOLUME.mention).toBe(1); + expect(DING_VOLUME.dm).toBe(1); + expect(DING_VOLUME.message).toBeLessThan(DING_VOLUME.invite); + expect(DING_VOLUME.invite).toBeLessThan(DING_VOLUME.mention); + }); + + it('names the motif: invite over mention over DM over message', () => { + expect(motifFor({ invite: true, highlight: true, direct: true })).toBe('invite'); + expect(motifFor({ highlight: true, direct: true })).toBe('mention'); + expect(motifFor({ highlight: false, direct: true })).toBe('dm'); + expect(motifFor({ highlight: false, direct: false })).toBe('message'); + }); +}); + +describe('audio unlock', () => { + const make = () => { + const listeners = new Map void>(); + const target = { + addEventListener: (t: string, f: () => void) => listeners.set(t, f), + removeEventListener: (t: string) => listeners.delete(t), + }; + const played: Array<{ url: string; volume: number }> = []; + const player = createDingPlayer((url) => { + const clip = { volume: 1, play: vi.fn(async () => undefined) }; + played.push({ + url, + get volume() { + return clip.volume; + }, + } as never); + return clip; + }, target); + return { listeners, player, played }; + }; + + it('does not play before the first user gesture, and plays after it', () => { + const { listeners, player, played } = make(); + expect(player.play('a.ogg', 1)).toBe(false); + expect(played).toHaveLength(0); + listeners.get('pointerdown')!(); + expect(player.play('a.ogg', 0.3)).toBe(true); + expect(played).toHaveLength(1); + expect(played[0].volume).toBe(0.3); + }); + + it('stops listening once unlocked', () => { + const { listeners, player } = make(); + listeners.get('keydown')!(); + expect(listeners.size).toBe(0); + expect(player.unlocked()).toBe(true); + }); + + it('a clip the browser refuses does not throw', async () => { + const target = { + addEventListener: (_t: string, f: () => void) => f(), + removeEventListener: () => undefined, + }; + const player = createDingPlayer( + () => ({ volume: 1, play: () => Promise.reject(new Error('NotAllowedError')) }), + target + ); + expect(() => player.play('a.ogg', 1)).not.toThrow(); + await Promise.resolve(); + }); +}); diff --git a/src/app/features/dings/dings.ts b/src/app/features/dings/dings.ts new file mode 100644 index 00000000..562700f3 --- /dev/null +++ b/src/app/features/dings/dings.ts @@ -0,0 +1,100 @@ +// Dings: pre-rendered earcons shipped as static assets, `dings//.ogg` plus +// `dings/manifest.json` (docs/gamecult/ratatoskr-target.md, Agreements). The sender picks the +// instrument, the event picks the motif. This file is the whole decision; the client only asks it. + +export type Motif = 'mention' | 'dm' | 'message' | 'invite'; +type Clips = Partial>; +export type DingManifest = { neutral: Clips; senders: Record }; + +/** Client config `dings`. Off unless `enabled` is exactly true; nothing about dings loads otherwise. */ +export type DingsConfig = { enabled?: boolean }; +export const dingsEnabled = (config: DingsConfig | undefined): boolean => config?.enabled === true; + +const MOTIFS: Motif[] = ['mention', 'dm', 'message', 'invite']; + +// Mentions and DMs loud, an ordinary message quiet. +export const DING_VOLUME: Record = { + mention: 1, + dm: 1, + invite: 0.7, + message: 0.3, +}; + +const isRecord = (v: unknown): v is Record => + typeof v === 'object' && v !== null && !Array.isArray(v); + +// A clip path stays inside dings/: relative, ogg or mp3 (not wav), no dot segments, no scheme. +const isClipPath = (v: unknown): v is string => + typeof v === 'string' && + /^[A-Za-z0-9_-]+(\/[A-Za-z0-9_.-]+)*\.(ogg|mp3)$/.test(v) && + !v.split('/').includes('..'); + +const readClips = (v: unknown): Clips => { + const clips: Clips = {}; + if (!isRecord(v)) return clips; + MOTIFS.forEach((m) => { + if (Object.prototype.hasOwnProperty.call(v, m) && isClipPath(v[m])) clips[m] = v[m] as string; + }); + return clips; +}; + +/** The manifest is JSON from outside the typed world: read it defensively, undefined if unusable. */ +export const parseManifest = (json: unknown): DingManifest | undefined => { + if (!isRecord(json) || !isRecord(json.neutral) || !isRecord(json.senders)) return undefined; + const senders: Record = Object.create(null); + Object.keys(json.senders).forEach((localpart) => { + senders[localpart] = readClips( + json.senders && (json.senders as Record)[localpart] + ); + }); + return { neutral: readClips(json.neutral), senders }; +}; + +/** The sender's own clip, else the neutral one, else nothing: silence. */ +export const pickDing = ( + manifest: DingManifest, + localpart: string, + motif: Motif +): { path: string; volume: number } | undefined => { + const own = Object.prototype.hasOwnProperty.call(manifest.senders, localpart) + ? manifest.senders[localpart][motif] + : undefined; + const path = own ?? manifest.neutral[motif]; + return path === undefined ? undefined : { path, volume: DING_VOLUME[motif] }; +}; + +export const motifFor = (e: { invite?: boolean; highlight: boolean; direct: boolean }): Motif => { + if (e.invite) return 'invite'; + if (e.highlight) return 'mention'; + return e.direct ? 'dm' : 'message'; +}; + +type Clip = { volume: number; play: () => Promise }; +type GestureTarget = { + addEventListener: (type: string, listener: () => void) => void; + removeEventListener: (type: string, listener: () => void) => void; +}; +const GESTURES = ['pointerdown', 'keydown', 'touchend']; + +/** + * Browsers refuse audio until the first user gesture. The player stays silent until one has + * happened, then plays; a clip the browser still refuses is dropped, never thrown. + */ +export const createDingPlayer = (make: (url: string) => Clip, target: GestureTarget) => { + let unlocked = false; + const unlock = () => { + unlocked = true; + GESTURES.forEach((g) => target.removeEventListener(g, unlock)); + }; + GESTURES.forEach((g) => target.addEventListener(g, unlock)); + return { + unlocked: () => unlocked, + play(url: string, volume: number): boolean { + if (!unlocked) return false; + const clip = make(url); + clip.volume = volume; + clip.play().catch(() => undefined); + return true; + }, + }; +}; diff --git a/src/app/features/dings/manifest.test.ts b/src/app/features/dings/manifest.test.ts new file mode 100644 index 00000000..fb194cd1 --- /dev/null +++ b/src/app/features/dings/manifest.test.ts @@ -0,0 +1,80 @@ +import { readFileSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { DingManifest, Motif, dingsEnabled, parseManifest, pickDing } from './dings'; + +// The staged v3 set (Aqua, AquaSynth PR #22). Nobody has heard it; these tests check the manifest +// against this code, not the sound. +const DINGS = join(__dirname, '../../../../public/dings'); +const raw = JSON.parse(readFileSync(join(DINGS, 'manifest.json'), 'utf8')); +const manifest = parseManifest(raw) as DingManifest; +const MOTIFS: Motif[] = ['mention', 'dm', 'message', 'invite']; +const VOICES = ['aqua', 'eureka', 'heimdall', 'kiko', 'libby', 'nibu', 'void', 'weksa']; + +// Vorbis-in-Ogg duration: last page's granule position over the sample rate in the id header. +const oggMs = (file: string): number => { + const b = readFileSync(join(DINGS, file)); + const id = b.indexOf('\x01vorbis', 0, 'latin1'); + const rate = b.readUInt32LE(id + 12); + const last = b.lastIndexOf('OggS', b.length, 'latin1'); + return (Number(b.readBigUInt64LE(last + 6)) / rate) * 1000; +}; + +describe('dings switch', () => { + it('is off unless the config says exactly true', () => { + expect(dingsEnabled(undefined)).toBe(false); + expect(dingsEnabled({})).toBe(false); + expect(dingsEnabled({ enabled: false })).toBe(false); + expect(dingsEnabled({ enabled: 'true' as unknown as boolean })).toBe(false); + expect(dingsEnabled({ enabled: true })).toBe(true); + }); + + it('ships off in config.json', () => { + const config = JSON.parse(readFileSync(join(__dirname, '../../../../config.json'), 'utf8')); + expect(config.dings).toEqual({ enabled: false }); + expect(dingsEnabled(config.dings)).toBe(false); + }); +}); + +describe('v3 manifest against the parser', () => { + it('survives parseManifest whole: no path or motif is dropped', () => { + expect(manifest.neutral).toEqual(raw.neutral); + expect(Object.keys(manifest.senders).sort()).toEqual(VOICES); + VOICES.forEach((v) => expect(manifest.senders[v]).toEqual(raw.senders[v])); + MOTIFS.forEach((m) => expect(manifest.neutral[m]).toBeDefined()); + }); + + it('names a file that exists for every clip, and no file the manifest omits', () => { + const paths = [ + ...Object.values(manifest.neutral), + ...Object.values(manifest.senders).flatMap((c) => Object.values(c)), + ]; + expect(paths).toHaveLength(4 + VOICES.length * 4); + paths.forEach((p) => expect(existsSync(join(DINGS, p as string)), p).toBe(true)); + }); + + it('maps a sender localpart to that sender folder, per motif', () => { + VOICES.forEach((v) => + MOTIFS.forEach((m) => expect(pickDing(manifest, v, m)?.path).toBe(`senders/${v}/${m}.ogg`)) + ); + }); + + it('an unknown sender and an invite get the neutral clip', () => { + MOTIFS.forEach((m) => expect(pickDing(manifest, 'someone', m)?.path).toBe(`neutral/${m}.ogg`)); + }); + + it('Nibu is the static-filter square (pick 1): senders/nibu, never the proposals folder', () => { + MOTIFS.forEach((m) => { + const path = pickDing(manifest, 'nibu', m)?.path as string; + expect(path).toBe(`senders/nibu/${m}.ogg`); + expect(path).not.toContain('proposals'); + }); + }); + + it('keeps the clip lengths the design assumes: message under 400 ms, nothing over 600 ms', () => { + VOICES.forEach((v) => { + expect(oggMs(`senders/${v}/message.ogg`)).toBeLessThan(400); + MOTIFS.forEach((m) => expect(oggMs(`senders/${v}/${m}.ogg`)).toBeLessThan(601)); + }); + }); +}); diff --git a/src/app/features/dings/useDings.ts b/src/app/features/dings/useDings.ts new file mode 100644 index 00000000..31899996 --- /dev/null +++ b/src/app/features/dings/useDings.ts @@ -0,0 +1,55 @@ +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useClientConfig } from '../../hooks/useClientConfig'; +import { trimTrailingSlash } from '../../utils/common'; +import { getMxIdLocalPart } from '../../utils/matrix'; +import { + DingManifest, + Motif, + createDingPlayer, + dingsEnabled, + parseManifest, + pickDing, +} from './dings'; + +const DINGS_ROOT = `${trimTrailingSlash(import.meta.env.BASE_URL)}/public/dings`; + +/** + * Plays a ding for a sender and motif. The manifest is read once at runtime. Returns false when + * there is no manifest (nothing rendered yet), so the caller keeps the stock sound; once there is + * one, a sender or motif with no clip is silence, as the design says, and the return is true. + * The client config's `dings.enabled` is the switch (default off): off, nothing is fetched, no + * gesture listener is added, and the return is always false, so the stock sound plays. + */ +export const useDings = () => { + const enabled = dingsEnabled(useClientConfig().dings); + const manifestRef = useRef(); + const player = useMemo( + () => (enabled ? createDingPlayer((url) => new Audio(url), window) : undefined), + [enabled] + ); + + useEffect(() => { + if (!enabled) return undefined; + let live = true; + fetch(`${DINGS_ROOT}/manifest.json`) + .then((r) => (r.ok ? r.json() : undefined)) + .then((json) => { + if (live) manifestRef.current = parseManifest(json); + }) + .catch(() => undefined); + return () => { + live = false; + }; + }, [enabled]); + + return useCallback( + (sender: string, motif: Motif): boolean => { + const manifest = manifestRef.current; + if (!player || !manifest) return false; + const ding = pickDing(manifest, getMxIdLocalPart(sender) ?? sender, motif); + if (ding) player.play(`${DINGS_ROOT}/${ding.path}`, ding.volume); + return true; + }, + [player] + ); +}; diff --git a/src/app/hooks/useClientConfig.ts b/src/app/hooks/useClientConfig.ts index 6c793e65..759e0c59 100644 --- a/src/app/hooks/useClientConfig.ts +++ b/src/app/hooks/useClientConfig.ts @@ -1,4 +1,5 @@ import { createContext, useContext } from 'react'; +import type { DingsConfig } from '../features/dings/dings'; import type { BackgroundSceneConfig } from '../features/scene/tier'; export type HashRouterConfig = { @@ -22,6 +23,9 @@ export type ClientConfig = { backgroundScene?: BackgroundSceneConfig; + // Per-Mind notification earcons (src/app/features/dings). Off unless `enabled: true`. + dings?: DingsConfig; + // Dev preview only: the build answers a pretend homeserver in-page (src/preview-demo). previewDemo?: boolean; }; diff --git a/src/app/pages/client/ClientNonUIFeatures.tsx b/src/app/pages/client/ClientNonUIFeatures.tsx index 62385769..5bc97d65 100644 --- a/src/app/pages/client/ClientNonUIFeatures.tsx +++ b/src/app/pages/client/ClientNonUIFeatures.tsx @@ -26,6 +26,9 @@ import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; import { useSelectedRoom } from '../../hooks/router/useSelectedRoom'; import { useInboxNotificationsSelected } from '../../hooks/router/useInbox'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; +import { mDirectAtom } from '../../state/mDirectList'; +import { motifFor } from '../../features/dings/dings'; +import { useDings } from '../../features/dings/useDings'; function SystemEmojiFeature() { const [twitterEmoji] = useSetting(settingsAtom, 'twitterEmoji'); @@ -85,6 +88,7 @@ function InviteNotifications() { const navigate = useNavigate(); const [showNotifications] = useSetting(settingsAtom, 'showNotifications'); const [notificationSound] = useSetting(settingsAtom, 'isNotificationSounds'); + const playDing = useDings(); const notify = useCallback( (count: number) => { @@ -115,10 +119,20 @@ function InviteNotifications() { } if (notificationSound) { - playSound(); + // The sender of an invite is not in the invite list, so it gets the neutral invite ding. + if (!playDing('', 'invite')) playSound(); } } - }, [mx, invites, perviousInviteLen, showNotifications, notificationSound, notify, playSound]); + }, [ + mx, + invites, + perviousInviteLen, + showNotifications, + notificationSound, + notify, + playSound, + playDing, + ]); return ( // eslint-disable-next-line jsx-a11y/media-has-caption @@ -136,6 +150,8 @@ function MessageNotifications() { const useAuthentication = useMediaAuthentication(); const [showNotifications] = useSetting(settingsAtom, 'showNotifications'); const [notificationSound] = useSetting(settingsAtom, 'isNotificationSounds'); + const mDirects = useAtomValue(mDirectAtom); + const playDing = useDings(); const navigate = useNavigate(); const notificationSelected = useInboxNotificationsSelected(); @@ -227,7 +243,11 @@ function MessageNotifications() { } if (notificationSound) { - playSound(); + const motif = motifFor({ + highlight: unreadInfo.highlight > 0, + direct: mDirects.has(room.roomId), + }); + if (!playDing(sender, motif)) playSound(); } }; mx.on(RoomEvent.Timeline, handleTimelineEvent); @@ -240,6 +260,8 @@ function MessageNotifications() { notificationSelected, showNotifications, playSound, + playDing, + mDirects, notify, selectedRoomId, useAuthentication, diff --git a/vite.config.js b/vite.config.js index 891c8216..e9b67550 100644 --- a/vite.config.js +++ b/vite.config.js @@ -46,6 +46,10 @@ const copyFiles = { src: 'public/poses', dest: 'public/', }, + { + src: 'public/dings', + dest: 'public/', + }, ], }; From 1dea4b51e50892188b49ecf5b42e539e72249b72 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 17:25:27 +0000 Subject: [PATCH 27/56] dings: Nibu carries proposal 3 (8 kHz 5-bit smoothed sine) for every event Meta heard v3 and chose proposal 3; Nibu confirmed it as her voice for all events. Co-Authored-By: Claude Sonnet 5.5 --- public/dings/senders/nibu/dm.ogg | Bin 6138 -> 6557 bytes public/dings/senders/nibu/invite.ogg | Bin 6151 -> 6583 bytes public/dings/senders/nibu/mention.ogg | Bin 5734 -> 6257 bytes public/dings/senders/nibu/message.ogg | Bin 5354 -> 5663 bytes src/app/features/dings/manifest.test.ts | 16 +++++++++++++++- 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/public/dings/senders/nibu/dm.ogg b/public/dings/senders/nibu/dm.ogg index 918858f172e83f10c7177c500e3f1e883e11b938..ede0ec4511b51b44fb71bd1ebaa906235e150389 100644 GIT binary patch delta 2967 zcmXX|c|25Y8-9#^A4{^1DaP`q?1aIfY>g#bNo326CHg`|I3vr*{;>>BHKvDcc%B7Kh8X7?sKl|x$o<`?`M7~=c$X^5D4}F0sjBt87R5PSQ1`1 zP*cTE@i6Y0;QLCW$Eh_Azj-0X4~(ZR`N~eAP}hA4f!Fep^ z<#qY8CDV|8Y3sCc%oB=BJp!b_?;wL#j55|w)pfZN@yA~){7s9g-x;r>uG4GF6rZvR z7rs79U1Elx1E1d%Xh;Emcy;~qPkK=2+ikTYCup60|A;{o%fH6ajy~-KYubvVV11rs zL}_g56}5BmU0AhetXOQ|SIMiAhVe{2oi=K$*Vj(ph$~^bO=W<(DQMARrV7S`!t)Jn z?eW~8pT|a9j*%SLJPAlqN8m+VP43Yvl^JHOI`3Qw3#o|ZTPX{K#?6QX{aIn>`rf8G!p ziY^Ny#xmVj61qVu1#!hPqybJK$?=7 zV$K8oiE_H8^=jK`J?@<2(EInK-t zi)3$`gl=*m=HC8gb93JGR1&A-p!Kk8d^mFggAJA7I-dqPAM99`|Dv0A6g_P~0Cyvx zf1St;0Es>=Ry}vt;j|{cF5o`LR?x@kUKZwB-r>lSD{;YgP0{)xXQ!Hm7F`zg^kxU7 zdPS~ReOgQbCzs2f=J23Mi(&xGyi)?8<$}~U`<;r(pd=dIw5;f9rU)pslJqzU^Y8SD zliDFZv5bQ$E;-R@F1;y(`1E@N<;4wnL(6Tk+qn0HT?g*nW8maB9#dq));#gyW?#H@ z%~`$ef6Tn4m4G)M-lsG-Fp!s8pe%jg8ArE3$9Z}s{TTv{wGE2EBnLU%9BlZ#UyE2G zE-|yNo;J6tl9mwEd_xgCLUPmQg6=6QHwI;)BsQC*B8GJ;1}PE!9k#D1n6APd%&JS4 z1UVd3+QUGGY_Jc$Nq(d#?u-Ye(dhTmy*@IM**E2r1Kt*z%XZGE?^`I|R~3C*^C;PCZ#&w&0HX#SZFq3a(UN0`AIv1e zUL!HYcB8KRvg(T3w~{w(70??M9z=kU6TS>pXaT@0<+$a+2IhqABWp=oMp|Z3Q&BC# z|Dj~ji4NJaM8*6l=T>$KW((JS#y(;JNuH&qD;3-O&YSVItAH=CBDfr8m>{>4U43VA zQ3zU;sPVE=Jqd99vZ1iMQtX-Bk)L$uDju;Mr})d{WDvjDZ)bVls%G00Dva$kud$CM z#AtXI=9kKtN?1pVnrD`)S)B2cl(Pl-?BE9<08!K?mRs%b_bUssc9OQl{SqY_4pp6{ zXVJIhh%mu8@r%UYL6YkCOc%qT3U$Z=>9_D+`}`r+Q^>!4c<_txW!+a2S8>k%f<9W9 zUTF<}Rs&N!CivBJ0d5c(aA{u=uEH%=V8FRHK?Wf|-n?tymmeBv^fBiKwhR4{vT*re zYXvuB3SkR^l#d>9XRL6&kM~TFzpp!^ut4pVf*U*InofK4V@8Y_SKWrx z34ueT13MvHVxZtO>Y{m02ZzjHdilI^lDeLSm5IOBO@i+|M@oF9y7l2&o`|Ir;!7+i z*{xspSs(f(WPfy0*pvTbpx^2c|~XjA`_gGnEV`T&3_NFH8l% zzFON&_zT&xA)1ltT6KFvi%HP&#n{SO=zU6SOI&%&o3#ugp7o(gWemGwi?(p3Tq{2dc3iyzX5SZ)kPkWXv`t6$CjE-a}iIBSP)l zwI``^0Xi@JkhM3z{T2Iy%_bZvzoknLkT&KZi{b5Vp#!F>a#M?Ns~z8_I1Eyz9woDk zEkpzc8wATWj}%4P)S-DN?#{lr&wzE*thP&;4L1%+C@j=t0>P zuZFm%a}x~(__aDrk3Rgb<+{nL#@N8IFurb6(E-&0-tmUlr&|nj!b7gwG9@2la0GC{ zmnrgUk3GygFRE${k# zszN#oi4Z@Avqo0_+-g{8)7OO7^!z5;XC#p+NMBAL=HI6n7`NTwOFrRRj|ni?UmctO zA)Tbz^$G~{j1I!OfZSnuBZm+7(IuJG9aa6i=y&PDWM6;&2a8X1Yc#b~>dt>J3m2p$ z8h(&v)426k{DxMNvFFcbEzwgelcb>hkT9RfXy{M9lwth=%Iw9m37G0z)x6gy-K%-_ zD?8|ZSnLV&%#??tFVc&drlbp8t2kz=)&Ar0y`g@jwfU2liI})V8Z%Y(rJD>tUJg^y z>tjGObtx*k(SaVpca*4QbFK;4mcsrrfN@art3Bs{{^rhD?Cz+om9N!#8WS)L8-oua z5=6fsm2zyZxT8A#DNbIkn=bB4jmlhd&?t)~5Buj;&dJ7k+c9A;W<{T`+*V=Ewh}KN zt>x4&8>t;iN*cQR>oTNy@!UPsIG9f9{jqTe~G za)#Mb`by>QJc`rV<&Dm(%6vap2s_z#A?(v-IT;Zt318yhtghe;^3v_4WDn-IkRVU*g+Ap&>hoep zW~twde;c3$7TJJW0juA*`|c8lA%wuBe%E5;a_3KD;toxij1&Hf#w+{~n>GfM1pWmv zk?fOntpd%>ht0TW{l3rHleH8pE7IC%QbmPW=j$!U_`TnZHrju$mFKKO1F+7J-+(H) zmjQwBs`1CjDMQ4t=$~`*x#TN2R(M!}kj_ddA_snQvQ6sHJS}WX% zetK|j{H@-P9(3qc_GIbX-?1A4j>;8m1pW3%8RuG+MMld5cvPdPxeXcM*NaJ+7r8;k zwZVT*njI9wQ?KF*ar!WWV7Eg|EiLrPoaKHcH zKF_bmYe|jY|I$;(^QkCa7#{3uj0CU=rC=w2@vdxNdo2Pi0f2y^JYKnHw%l+A_LKd3$=VAsV zXSl`dB9LSf>F3I?1ET|hBC;4*!wfGH9`Y|d&ecXDDKmc^9> b0ty>F+V#!w%!-R^s7TfMERrf10(}1ifM4$i delta 2545 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 V8^1sPj zTj>xkPuUq0d3u!Tsmr!Vi&xj&VPV)&+Kk(Cp~Z+%?k&t_21dVj7ThILO9D(=e%23Y3=B6+!>@^)4W07w3Qns8Pypb>rCPJdKR^oL!aYGVbIoQ4DCO=pJM4Un|nRWc)TRqc3moel(%8uT}E@aM}JMmTnFrT5=BoH@hhbWG| z77nw<;4G70<-Ebf*q+7f37W}aU|`fgXT}FzAD|-VpIWy7pe%(gQ@Yj6FZiDCR;NU! z$6jwk%YI?7LodD+^~=AsUB2eY?D3Kq3}7#!m_~r}YGFkURGW+?ak(NAN9#Y@A2Hwq zxZDFOmPo0fDwu?J(|?is;|4|Tx&DJBD=R#inrEl*k9=5uHKYJAvj zoj0T=f8R*}xM&&-25aru+re3nwHF=V-Z*`~ZDKWq1B53<2U3x|DCU~j%h{f|4~bSN z`v+Z*vJ~#G3!FdyJ!ZAN6HPhCAs)}t&i^J1bwPrjom!joev~dLyhVeRN^l=(Zf#-9 zAw)%Io6=S`mf=F#j_mNeq31@KS z__C(f@Mqp&^*z@yD((nqf(EsCq$f5xhv2*C>ihO{e?t6QJZXy_5?(doeu5hVcdKQDvt Ai2wiq delta 2566 zcmX|Dd0bOh7XDsH0zrfbh>Ef(D1*r2f})6tbwNc$1r$LL#EQU=LMuqCki0rhDY)Rc zYXxbmV%ZZ1vC^0Ezz ziwlMb|NEKw&zvF*tp~K4{L|LMOqeq?UsJU3;kl5Gf$H2Z`d11c1h3X^WIY$2LFTYSib;ug}}k`V!N^^6pR16^#ud78C^9OJ!5`7e`;5Q zI#MrPvNtg&z=sy9!X;I4_}!{l5|2Uw6deu+(OvfgjvGxHgoX%)r8tX4_)-EqeO_XN z`kGtEoY64(_*}@*`zi8SZgIf#&dm*x4T&ec%SR&&=v^g=c3~zUVyAR*A79bWY`Na% zNvtc%tfBUaWZ2sxd?7&v(SP~K9t-wLr}Lk~VVj#e4;3@IuI~woSzi13icXIWB@gNF zHWZd2NcOY|9|g?fzBpT!8*|!Vb7dgj0C!A~5{&3@2FdhsxBWn*{O4XsII2RI4M0~^ z*Yyt1n2G04ZElKe8Kpjv5cs7GVsJqxMO|XvPC3(->x1O#yhgN*55*#+hC(jJ*Ms15 z2Fk&dzxmtsdoXTa(Xs`|QMI}=n9-EE-81I(<>vKqo$d`Wqs|<^EG7-|4r7_>VXPB2 zPh$Hqi&o+A2sQ?jm%t~KVHFZJ&I0c!gBe#}_CVgO*>B0ZWz~SgjDvpq{qo(uuDTP+ zewwSu!y?uSGi{Z9BIngRQ0*@28X8?H;x5`?Yy@~4@RXpGU+&t6z~p*- z$2{a$CNN5A{>PU3{ql3hfBI+ojxwq?a|6_Eolizb2T7xiOoZeV?;XoOu#>dy90lDW z5$b@2l^cs6Ay^G8$kPqEn|L5YUjcnoUP>H zxmTDrsf0HKqwHzqs~=oC{ZfWB_RiH!oINNzAt!*K*umgWO3@-8Al#&Ab&-TkT!3O1 zM0e8suuOjcQFYKm5tNldjWVF(!{Pa~Ow1Z+uQg&C=8Ir2t1aI;WowPrDPF5ie!qVH zS0`OPgiEvxY_m5`4DAp4qbvBz`Dj}R#~h+sBtLy|dC@|`X&&O&C<2>u?OR)r?2*!A zN}?D8E*Z5Aes^}eY_;+-`RJ<;s2cg>TDpqBH~~F8h8lD~mqGuR9j4Dh0mY-fpQp$T zF#`A2o}$L#U_?Z>O#Ur({F?pMT{ z_qj4?(nSR>Z%~!(MLuj#HrV$v?5Q=TE1X7#<`O`81Y_3J)qSH-EL}SuKYE`#<)vJs z7Nac!x;l})f4Qb!G`94uN)Z^S-BzoJ+HMyg^2B;M;kD$BX@s)c>Y6hemBwt}7B?eA z$_obrc)dTvilDoU0O5=!u7G3g#GjtTR@Z8c#L;EL$+(z7h*;H-cE^*^b#F*KwR)5P z_*RES2B+^6a@20LpzP$FX{ag}4`gQwAZP1LDB8DF6-XrJA=cDw&NiE~{F+1wosB>W zK4^tj&S0oxuu@A;DpLl@Ey|ZsGqILvAkxB)$KO?EI3 zA_A&>b!^^skIZ1htgY7beam2WnR-orVnDT_1LH?@~axattvt7jxxhNB8)y0`iWe3Ku0Fz-Uf&mD?! zDw9fqX4*8Ks?zVWwI~rX#a9-MgHk9}ya!fO=Xl_}Yf(m95)HYQO)IQ)ku(A~iKMyC z2n;W(o#`3vcH_9$zm%4OF!?h z(T@9Q;WjzovwniBCvBbzc_^)lw(QrOk6|UbJxXw=mX#`;gib|`sSI3Q=)hrQ= z?tOHSS_!)`KmD;LnR%_)k+?Q9v(R9X)EgsPN?g$EOtMHFCX%3@70U&UwzVr{;OVBI z%CaDjIy@o@78YlA-@0ef7yh|FN*tM@VMMksz3s4fd3?u#d0e!i=s{@Rl~ab|i;4(K zbz+Wi18Y}}v_r#TN5;!|GDw49(*;Q=)B?d8C!Uf82!D5Oii7jxdIm`ynQH=Rg!~Bh z7sb4Sd7Yeo#Oar^_9?yXO+CtlbKd2GSf`BMGJLZiqZBGAwoMr=n5G&Pk05k5d&H9W zOQ*NmZzCi4W9{@3VmjFa*(JRfPIt{xZfPxTwsu$j-0;bNRink~RPXYsTu-_Sn=J?p btLJ#f)Bl`@)~5Uj%ao0S3#xVC8e#rl7f*d8 diff --git a/public/dings/senders/nibu/mention.ogg b/public/dings/senders/nibu/mention.ogg index a36a9325a50b4f17498b933135017b1471ea313f..6a065e39d171638f3e009678c6c921e66bd82a59 100644 GIT binary patch delta 2673 zcmXX{2|QHm8~$d-GGjLwYZDnssi-8fjC6^cq=+oj*kiI4k>ZTFY8u3)vP)SKG9nZ* zgcfUtF0M5i%Pk4bOi2GT`k&wLJHPXt_ni0n-sgSa=Vfbu(^0bZ^*s%c;Q!YJDnp~`=zQ7*-HBrame5O_m_MF|1U~9epPAf@~c2L|Sts+5i{zkE~#wK2LF$fM61c3M#WBJMKll#~>^O%`DOzcMO)9cix4k#qE zABuls5Eeem4L0ub7!Y7IslJh*vH$U{-{C&JvHwPxbK4@b++^A3=hbv2K%9Y+-3?vSS9(j0mq8A4P{`@MM9xW|*LR_+gCT;Sw9-sb70QFRS zVCGnp$V@>5>gw#RhASmKwcX*DTWh}t*PyvOqa-lB50U~|HbRoj#bM6puWlB+E;OE&Q7?%nzvVdoe$$n+*#+0bJ4xF4DBu$X6c^s-ox|XtG8}5s}{Lh=8V+AKt`4qjJ{qnJ^ENpFH zaou7ErEIfAkhU%WKw!nRl1gT#Wf?9JP<^MIuFmwUbrRx(8ZVYaby7*5ve?sj(wT&# zmarX#-aV zF-XZB1zT^NYeINQ*>TJg^HNk56uk+&OSilKsA`6;c$?ZbT6D{qXPkeU;8$(!J^9Lb z(z(y#0n+fpQiyhwiPT(`NQS8E+nSvk0`tNkSdZ^c8eV`@7qDt`dyPZmty|36Z?YFq zup2gfyo6GSt$uKpk{vE!ch_2lr?2O;!_s~@CT!OYy8+D>+2pqVeLwd_OXB{7W@D0R zMZ9-vCvdN~NmvX9ZX4=WlA6{5LvVhGStvkylrv4A`j2DqrY9(F-(Q&?&VY(P)Zz@o zOz#$89yzvaExi8JgeA^#muI`@UEjwhry~vP#FsSt?l3&h&~vsKDfcJGykki}6fZUr zBXmC76R+YwKQejqh45BDoq>$u`mIL+lbbm!S&B0p|5mO(wp*{Q8USORYPfHiM&dw$ zu=Hx{0XeL?+@wtPMi;wEJy)i~d!W7~v$+8#N4VBft^ z!B1me9YV1})O7f$U)cyS7XD@R?aJCW<&SH7k6_+zM&b@R48mq6ZyRVowpd}KdkUj? zI9S7Uzf$gP62v-$kN2E8r;952Xp2+=^)a4sfZ$61ndij4+V#iF{7@T2 z1n)EK$4eHzjXAna$I2~pC@b?^O)R!BQw=VI+MQ+mVAgAZ0&~xgSEr+_gu-~e5em?4 zMQFq=QtzHNd85z4#;~rR5;Qa#XH81+j3{09W66Hoj`ps^h1N!9CB9z*KnBh}Ss4j( zEps}v6e(OxPY@wKNv}-~kk4Y2A~pLq`_och$Ki}kBuVZhq@ZI~AJs)dnwx=3ImpR+4^} z>!QnebfI9|7f*3pmEC50ZPmU42-R%2;Lbx~KTMW~ zxVL_(tS`8Eb^Pfir^!O4Nx5r)>VO5R{24mN{=Ay{#FuLTtBv(<8PJcffeknF1ju4i zQHQ5WG~O!c4b4{gLkAlAqPLlh9@e?qAFG%!_Hl|BHDhJIYzA<^yGlc{6uh2@nO@C*{;l@z7pfU0nkjyw!#Yyp*>=iz|E1&&q*a%`Ir709d1?SOSmmiJDRM<%&cG;_P_6 z1#waC&<<2|W-Fm=*e&mGU;nL&CoL`%dOxoJRzeU8x4{S3Tnu2{v!b<50M+fVz$imV zK&o+_%fbKTR385RQzoR++a?5_-_FQh+VM6xnm2}2th$Q_Z2))2FBuWiULm4|<`wzJ zthDa;Npo#%X`GfcX1V$oe(xxXR~zWCl5llGh%ogH0g>N}q?+uuEO>Mpwk{{ejr`U~ z8NTj=PJI*=@wo~z4#!EITuO-3jZL4geRRwGpXx_!O|;Kb(Yb-b@2@rR_Uud(-cB$%d6)bOJD3CAA4C((Urcmnnj&!D>dv`+~~pTYX7=FtTds5 G1^)ww!jS|3 delta 2146 zcmX|CYgAKL7XI$N2_b<!0W6oYgGyY0eJ_Fa<)sFSCbYzqW7L5U0tI|=lD2PbrT%6VO>)y4`{`UU%{`Njk zHTP#Lqt>pC1&#k7ub=Ga37^+p`G68-fTQqFk^di^TYHYj4a)Vpj5jw0gWy#bSSeGw zY2&o9u`w|T(c1VGE8^p{F)>lmF-zZH6cQHR)Ze+7O1Yah^^M=2{$m{lQ}c2wua;#` zzfjYh=b0OREI4lNG0l8%L!NQ;*lD_Bo5;Z6oS1kF?6AE@N8FhsHaKU|{5e-W!m8b2 zKLLXx)n~S^HpMXI>ANcDcpxDT@Q@Xnd`fI*_8l>w>Hdf=h}i(s<-spMk2Y?*+{+73yLwpOVwl$}c>+#d!C4+5ZY1Gr^Lk(AC- zu&_#m)Wx0lpd*xNJCdD2dFInFJV*0j)hTFysOXc`sshQ>?Jd~E7 zoTh!VwYW7^q;8lMKyi9t_ya8)CCQa^e;&k*XUV0nop0R}0v~2yJf-_UjQl&`7msJj z&unU9c>YF0us1x4&U=CKP)79CDK0ITcG;3wY+nD5Wl$03o4pO!&@zD|kZ+{e{#e6y zIq({8iVL=q>?&yZ(1-zvzc==^19rF9#?YF)+E^4*X-dn@sEiXW{j1KN=|12`^30$_ z;AP;x71gWcRu_Ra_d3sRG=Dv1^5(@VN+H7h2Cyl_P@CgV>3$U7c}(eU9;!h{=>7>v z37bh_%-wRY&N%ytYh2ca^hQFSTprUOr;TPRg7*EhMeMFyvX49V;?hM_AA?7L}e^%=@;EP(;aCL677Wq)<7-7)x4WPUXVNtwx@~? zeY?aYSDm7&%suOm`sC#5jzbQwGb6w1OHqrYdj+LS6l3yR0`vY z^+cvQi8NqWCiYSgEqna=k^ahe!}Lii-qlLF-aocXC#uiN#=M~rg!5$IsK)mq(b zOpeIYK3C^>oqE&x;bmg$yAl)i;Lj$hk5R)}uQ88`^Mlm~tUcIZu%Q4SvNp$#ds6pF zr3{beU3Ua!xzt+Y)Ejj2=&YnxhsxcJwWhz5X7aYTVV=q}9gCEDc6>gKS&976I(t5c zdj#%XM-+#^O2EmkQxzmC@O%90im3>iP*1b&X@^_?inMj5fIvKL`dzxT)X^08~@A`Za*+rc2TDA?PT&*iE-BHRs>WZ{mom)SvkrXlI zDn@7U@d$!FsS`M*mJf8p=q;L?0sb4Zt!M;bP1Y3=ZWe70XKtNF^&Y$9st6{7rQHc8dM_Qb@otxh`J*^;GokJWv zPA`9!JUQ)5X~4D7hJQ|*nHsr?^!4V6CZSsEHoD>LpM??!g00c0)3CrRVHU%Bpo) OnKyCRZq4>D=>GxfBJ)N7 diff --git a/public/dings/senders/nibu/message.ogg b/public/dings/senders/nibu/message.ogg index 13fae9acb26d8f6626594b154e58c41ee8ac1c39..c324158291c5f12871b24158787d21584dcbd83f 100644 GIT binary patch delta 2074 zcmXX{dpwiv8^1R*hel3GMhTNbsa_oj6ADc^{UYU%%(7zTSdwQ(Cc?t+{Rx?h2)&i_ zl9_rHDXLXU=Jd*>O{!(f@qHej-}A@yJlE%Y-`DrKuj_kV_vA*lx$Yqf#T%f(|DT%g zH;BmR`>IaYtGb(Uh>wE(dOjoXfsaH5TH}Ji03ZOe%Zs$;h1R;b)6_GeWHKc*I4YJJ zP9cYfY!um6K-^2H?oE*bWq>TD4}M75JQ6_7Y?)@@fbNlOS!PquRJ3Fu=-8=@aFl%I zprXx1KvzYg^P;7ie3Z|eJiKik?4LRIV=*k~P&EZ|yX&z&X1coTM~n@#Fd`*0cbI~QL)s~=eOcs9k>V2} z!x0Z}(h6Y7EJG!@eSk5ZY&Y2W(D;kbJ0VWN>Hflb?Y=l=8-wC6y1Voiv9qFEi=9gY ztBX`o41IeC{q#cSJ{|h%?N^~}gALnVAF4;&a<_x&k68G>bC5EWd=QU1SI5qsJO>$# z`y*8MiMLP*Wq3G)@EXV+UDMfc#9^<~ijx6wPJ>gG!X(y1GRzfof$zK&XUnDsI@6ZBYgY*FCziN(GH3NPN}cruw~J`g=;8D5v1D6UfI*6QHYK1}+>|EW`Ir^$zSROi z#5X%0`@<9>b{4?Jbv6?MwiE;>nu%mNfy^;wJtfr;IJ|7LVIm1c!gOlPNY! zWc#a&?IS}hS!H>t8%nfmS89W8CU6yUasXa08CpZgRHFaFtiZ20+n zl%@PHInW#e3)cVvPSHQZmvjy1obrstEUW|0D8KI>0cbv~Nw~J?m zI@s#?-?rP_Ds?u-WlZlmjNf+H_R!J&w&gE3Ym8SF8;4L&qvh#H*9;3#fE%CHuGz|d zHrvtL=7M8S9v+ya)0UXqo0Opma=8uw=Smnm^)IZeh_V~_r*7%>dk#$)SFF)j(~-C| za@;16qxe%GXlH;`(tow zxUP{C-eAwrRDbGE6=;`3<(fBWQUD&B>BpE^KP|lWID=D zZ#7;au=X4Gk1ltb#aFrAtMY2YvRga^u4MbrG3C0nQW0=Gw57TfV|X$@D}BkPL9A_l zkEQ^|Tf303irLRAK=V*b&gHpo&g}wiBK<1P8U}cRE)bG?H-B%WAI1HWQ{kfIIjeNC zI1W06U;j1nzm&V2+#8qnpYLF#1B2MxBo5iVu~@~bosz=S#e}ARKjp%+#8C?CKZ6(j#H&8(A6x5KPR$oYl7-a?v) zTXHuuD#IUmuR}YoSd1E6k9rbZKxluY1c#Z2l~(f9e2fXEIkbCE&n>3*Lpr|d@8Jny z%F=XL&YkRFGOrAW<hHT#)qlb_VAA2z$H|fl?SycQ$a9bG6yu-)+q3)e z^=^}~ReIxQgy~!!i-pZWo4}(E0ACDj1{+R~9Uy*BZplfWSw%vI9r0j6BMb~LxB?Tu zMt8plHVt89&6+&TS*Mz=kxz0zUQ9wWx9@X2M6Ww12rvBk4Z6V}E&tlPb;pCIc^%Br zh)Cfbb#p6^A>RYy#zpf3+!3WER?{|pk>dSka9um9L> G8T&7#v5AcU delta 1763 zcmXw3e^gWF8Gdel5W)|G7+OWAXhldsNUfGClBUxZ9b%#oP*I3(okvB1Ryj;G`KtC* z7w z`%IDdWx?v5JCne}%ST${+{}HpoSxVE=in$mE@o)_#oey_i^)U&hOjj+R&oihs;u8H zRXQ_8o05{eF)2m8K4shHjIFw~t*YcW)7|e2eXO>+w_a5AT#O3-wuo;$zOA(;yQ#5b z$5Z9d!OMHi)z&5#pdWiD)Rh&1!|_;wkgZ&a!BZsm?EjCHjBODkp@&E6jf{b~pvqwW zo>ns}tPZUoW|^)_k(vxBgZ0+1)02(H+Gb0q{~||+wA4&7JS{%#cm&D9>?%gefx=T1 z#@CzP{HPqq!r>O6@&I(PaJ>R}_x@E~g3szN)QOY1+o`S+svG5>Y^;6$iAkjOo@zC89%|I`=RZF&~-q31?qH;5H5_8190C`p; zc#sSe(-7jXdoH!pG6#0`axCM2<_}LGAFWax_eLw-*s(i(T3FqEdAs3g-;@N0NGZ2@ zECDBCFLy_gGuNf12B?6Esx2zD$Rp;q@5QNwzD?j>H=7hQ7s3%q#TNM^b2`E)zggDy z42taAqC_znFWwPIV3X?$qqqMr+j;l=lf~EgYtPA|7()AUm@|Ar;ZV%yyD)XLGboo) z9L^tbuA(Dn5#pKYRck&^Z!Pcpv~2v*_^umQVtP%d4tUqn22qR@5J{4OQKe>m%!LoK zA{kwVH+H^(s`>PXWY#)G%cY8x9@_Jv)#bs!lF;r<+6CIL5DO0(GhnNRUZ!k58aw)E zkJs(VLid6#HOAh+O%yEx?Dz;A(RB(MkjW7Shpxpijuw!IG_F@E+S30R?*(Frdl-9< zvb8_1eKC2D=fO1yw*>m--OUBR-7~WMsC(j0SKQABUdld00kO);y~lgz!=s#ohF4I6 zkdJi68Q&%R9@u2OT0VykU|xJVph~n+>1T6a2NYSfJ%4$X7S3gmE>m`u7mpgK@*khf zzWv{#4fK4`6g{^SQ!|hmCxY<#Fm&3T5*X}o&0+|hF0&t9v;jxTmga4fQxe<6`&9n? zbutDaaKBCcp#)Rj>750!1C2fEz=|HXVNrwTCAfeV&-?{e3eQ;s*d!1h1OX3*>n>Fb zK|a>@tfyAou4>d$uzJE;Oa*xlpNMZ*8^K`Hnm1nyq8Efgax08ZiSo;w112rmh82CTAwQF$X_JieKLUplJ*gXR0!~|AnqPyZT4j_$3xR! zN})cu47irzL6Pzs`!3l+C|vuNEfz!P%|<|1$Ok#Jmn!&rqC4#8b6<=68tOf*KU|YW xOUggI^*h|SC9X{3B(onp8NI3uaM|^xrvmX-X-ujwbo+n(PX;AB3y&NX{2w90gQWlf diff --git a/src/app/features/dings/manifest.test.ts b/src/app/features/dings/manifest.test.ts index fb194cd1..7017669a 100644 --- a/src/app/features/dings/manifest.test.ts +++ b/src/app/features/dings/manifest.test.ts @@ -1,4 +1,5 @@ import { readFileSync, existsSync } from 'node:fs'; +import { createHash } from 'node:crypto'; import { join } from 'node:path'; import { describe, expect, it } from 'vitest'; import { DingManifest, Motif, dingsEnabled, parseManifest, pickDing } from './dings'; @@ -63,7 +64,7 @@ describe('v3 manifest against the parser', () => { MOTIFS.forEach((m) => expect(pickDing(manifest, 'someone', m)?.path).toBe(`neutral/${m}.ogg`)); }); - it('Nibu is the static-filter square (pick 1): senders/nibu, never the proposals folder', () => { + it('Nibu carries pick 3 (8 kHz 5-bit smoothed sine) for every event: senders/nibu, never the proposals folder', () => { MOTIFS.forEach((m) => { const path = pickDing(manifest, 'nibu', m)?.path as string; expect(path).toBe(`senders/nibu/${m}.ogg`); @@ -71,6 +72,19 @@ describe('v3 manifest against the parser', () => { }); }); + it("Nibu's four files are AquaSynth's proposals/nibu3 (sha256 prefix)", () => { + const want: Record = { + mention: '3833f504903f', + dm: 'ad4839400f2c', + message: '6ec6fc893b94', + invite: 'aefb6ff79c42', + }; + MOTIFS.forEach((m) => { + const hash = createHash('sha256').update(readFileSync(join(DINGS, `senders/nibu/${m}.ogg`))); + expect(hash.digest('hex').slice(0, 12)).toBe(want[m]); + }); + }); + it('keeps the clip lengths the design assumes: message under 400 ms, nothing over 600 ms', () => { VOICES.forEach((v) => { expect(oggMs(`senders/${v}/message.ogg`)).toBeLessThan(400); From f1287a6f3aefb1fad9fbb56b98f87693fbdf6160 Mon Sep 17 00:00:00 2001 From: Libby Date: Tue, 10 Nov 2026 17:29:51 +0000 Subject: [PATCH 28/56] auth: newcomer-facing sign-in, server and sign-up messages that say what to do next Replaces Cinny's stock wording (Invalid Username or Password., Failed to find homeserver., Failed to connect..., the dead-end registration-disabled text) with plain messages in one module, authMessages.ts, used by the login form, the auth layout, the login page and the register page. Co-Authored-By: Claude Sonnet 5.5 --- src/app/pages/auth/AuthLayout.tsx | 15 ++-- src/app/pages/auth/authMessages.test.ts | 68 +++++++++++++++++ src/app/pages/auth/authMessages.ts | 74 +++++++++++++++++++ src/app/pages/auth/login/Login.tsx | 5 +- .../pages/auth/login/PasswordLoginForm.tsx | 15 ++-- src/app/pages/auth/register/Register.tsx | 9 ++- 6 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 src/app/pages/auth/authMessages.test.ts create mode 100644 src/app/pages/auth/authMessages.ts diff --git a/src/app/pages/auth/AuthLayout.tsx b/src/app/pages/auth/AuthLayout.tsx index 49c7b774..a2d652a3 100644 --- a/src/app/pages/auth/AuthLayout.tsx +++ b/src/app/pages/auth/AuthLayout.tsx @@ -32,6 +32,7 @@ import { AuthFlowsLoader } from '../../components/AuthFlowsLoader'; import { AuthFlowsProvider } from '../../hooks/useAuthFlows'; import { AuthServerProvider } from '../../hooks/useAuthServer'; import { tryDecodeURIComponent } from '../../utils/dom'; +import { serverTroubleMessage } from './authMessages'; const currentAuthPath = (pathname: string): string => { if (matchPath(LOGIN_PATH, pathname)) { @@ -158,18 +159,18 @@ export function AuthLayout() { />
    {discoveryState.status === AsyncStatus.Loading && ( - + )} {discoveryState.status === AsyncStatus.Error && ( - + )} {autoDiscoveryError?.action === AutoDiscoveryAction.FAIL_PROMPT && ( )} {autoDiscoveryError?.action === AutoDiscoveryAction.FAIL_ERROR && ( - + )} {discoveryState.status === AsyncStatus.Success && autoDiscoveryInfo && ( @@ -182,7 +183,7 @@ export function AuthLayout() { /> )} error={() => ( - + )} > {(specVersions) => ( @@ -192,7 +193,9 @@ export function AuthLayout() { )} error={() => ( - + )} > {(authFlows) => ( diff --git a/src/app/pages/auth/authMessages.test.ts b/src/app/pages/auth/authMessages.test.ts new file mode 100644 index 00000000..04b43ffb --- /dev/null +++ b/src/app/pages/auth/authMessages.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, it } from 'vitest'; +import { + loginErrorMessage, + noSignInMethodMessage, + registerTroubleMessage, + serverTroubleMessage, + type RegisterTrouble, + type ServerTrouble, +} from './authMessages'; +import { LoginError } from './login/loginUtil'; + +const stock = [ + 'Invalid Username or Password.', + 'Failed to find homeserver.', + 'Failed to connect', + 'Failed to login', +]; +const troubles: ServerTrouble[] = [ + 'not-found', + 'unusable-setup', + 'bad-address', + 'unreachable', + 'no-sign-in-info', +]; +const registerTroubles: RegisterTrouble[] = ['closed', 'rate-limited', 'invalid', 'unsupported']; + +describe('sign-in and sign-up messages', () => { + it('has a message for every sign-in error, each different, none the stock wording', () => { + const messages = Object.values(LoginError).map((e) => loginErrorMessage(e)); + expect(new Set(messages).size).toBe(messages.length); + messages.forEach((m) => stock.forEach((s) => expect(m).not.toContain(s))); + }); + + it('keeps wrong credentials apart from a missing server', () => { + expect(loginErrorMessage(LoginError.Forbidden)).toContain('username and password'); + expect(loginErrorMessage(LoginError.InvalidServer)).toContain('@name:server'); + }); + + it('names the server in every server message and says what to do', () => { + troubles.forEach((t) => { + const m = serverTroubleMessage(t, 'chat.example', 'bad.host'); + expect(m).toContain('chat.example'); + stock.forEach((s) => expect(m).not.toContain(s)); + }); + expect(serverTroubleMessage('unusable-setup', 'chat.example', 'bad.host')).toContain( + 'bad.host' + ); + expect(serverTroubleMessage('unreachable', 'chat.example')).toContain('reload'); + }); + + it('tells a person who cannot fix a broken server setup to tell its owner', () => { + expect(serverTroubleMessage('bad-address', 'chat.example')).toContain( + 'whoever runs the server' + ); + expect(noSignInMethodMessage('chat.example')).toContain('chat.example'); + }); + + it('says closed sign-up is closed and points to sign in or the inviter, without a Register link to nowhere', () => { + const m = registerTroubleMessage('closed', 'chat.example'); + expect(m).toContain('chat.example'); + expect(m).toContain('sign in'); + expect(m).toContain('invited you'); + expect(m).not.toContain('disabled'); + registerTroubles.forEach((t) => + expect(registerTroubleMessage(t, 'chat.example').length).toBeGreaterThan(20) + ); + }); +}); diff --git a/src/app/pages/auth/authMessages.ts b/src/app/pages/auth/authMessages.ts new file mode 100644 index 00000000..3cc858fe --- /dev/null +++ b/src/app/pages/auth/authMessages.ts @@ -0,0 +1,74 @@ +import { LoginError } from './login/loginUtil'; + +// What the sign-in and sign-up pages say when something goes wrong. Each message says what happened +// in plain words and what the person can do next; none is a status code or a Matrix term. + +export const loginErrorMessage = (error: LoginError): string => { + switch (error) { + case LoginError.ServerNotAllowed: + return 'This app only signs in to the servers in its Homeserver list. Pick one from the list above.'; + case LoginError.InvalidServer: + return 'We could not find the server in that username. A full username looks like @name:server; the part after the colon is the server.'; + case LoginError.Forbidden: + return 'That username and password did not match. Check both and try again, or use "Forget Password?" beside the password.'; + case LoginError.UserDeactivated: + return 'This account has been turned off. Ask whoever runs this server to turn it back on.'; + case LoginError.InvalidRequest: + return 'The server could not read that sign-in. Check the username and password for stray spaces and try again.'; + case LoginError.RateLimited: + return 'Too many tries in a short time. Wait a minute, then try again.'; + case LoginError.Unknown: + default: + return 'Sign-in did not work and the server gave no reason. Wait a minute and try again; if it keeps failing, tell whoever invited you.'; + } +}; + +export type ServerTrouble = + | 'not-found' + | 'unusable-setup' + | 'bad-address' + | 'unreachable' + | 'no-sign-in-info'; + +/** `server` is the name shown in the Homeserver box; `host` is the name the server's own setup gave. */ +export const serverTroubleMessage = ( + trouble: ServerTrouble, + server: string, + host?: string +): string => { + switch (trouble) { + case 'not-found': + return `We could not find a chat server called ${server}. Check the name for typos, or try again in a minute.`; + case 'unusable-setup': + return `We found ${server}, but the setup it points to (${ + host ?? 'unknown' + }) is not usable. You cannot fix this from here: tell whoever runs the server.`; + case 'bad-address': + return `We found ${server}, but the address it gives for chat is not valid. You cannot fix this from here: tell whoever runs the server.`; + case 'unreachable': + return `We found ${server} but could not connect to it. It may be down for a moment: wait a minute and reload this page. If it stays down, tell whoever invited you.`; + case 'no-sign-in-info': + return `Connected to ${server}, but it did not say how to sign in. Reload the page; if it happens again, tell whoever runs the server.`; + default: + return `We could not reach ${server}. Reload the page and try again.`; + } +}; + +export const noSignInMethodMessage = (server: string): string => + `${server} does not offer a way to sign in that this app can use. Pick another server from the Homeserver list, or tell whoever runs ${server}.`; + +export type RegisterTrouble = 'closed' | 'rate-limited' | 'invalid' | 'unsupported'; + +export const registerTroubleMessage = (trouble: RegisterTrouble, server: string): string => { + switch (trouble) { + case 'closed': + return `New accounts cannot be made on ${server} from this page yet. If you were given an account, sign in with it. If you were not, ask whoever invited you how to get one.`; + case 'rate-limited': + return 'Too many tries in a short time. Wait a minute, then try again.'; + case 'invalid': + return `${server} did not accept the request to make an account. Reload the page and try again; if it keeps failing, tell whoever invited you.`; + case 'unsupported': + default: + return `${server} asks for something this app cannot do when making an account. Tell whoever invited you.`; + } +}; diff --git a/src/app/pages/auth/login/Login.tsx b/src/app/pages/auth/login/Login.tsx index 9d890bef..bc3136f4 100644 --- a/src/app/pages/auth/login/Login.tsx +++ b/src/app/pages/auth/login/Login.tsx @@ -13,6 +13,7 @@ import { getLoginPath, getRegisterPath, withSearchParam } from '../../pathUtils' import { usePathWithOrigin } from '../../../hooks/usePathWithOrigin'; import { LoginPathSearchParams } from '../../paths'; import { useClientConfig } from '../../../hooks/useClientConfig'; +import { noSignInMethodMessage } from '../authMessages'; const getLoginTokenSearchParam = () => { // when using hasRouter query params in existing route @@ -85,9 +86,7 @@ export function Login() { )} {!parsedFlows.password && !parsedFlows.sso && ( <> - - {`This client does not support login on "${server}" homeserver. Password and SSO based login method not found.`} - + {noSignInMethodMessage(server)} )} diff --git a/src/app/pages/auth/login/PasswordLoginForm.tsx b/src/app/pages/auth/login/PasswordLoginForm.tsx index 3c8641d9..7934bc9f 100644 --- a/src/app/pages/auth/login/PasswordLoginForm.tsx +++ b/src/app/pages/auth/login/PasswordLoginForm.tsx @@ -35,6 +35,7 @@ import { } from './loginUtil'; import { PasswordInput } from '../../../components/password-input'; import { FieldError } from '../FiledError'; +import { loginErrorMessage } from '../authMessages'; import { getResetPasswordPath } from '../../pathUtils'; import { stopPropagation } from '../../../utils/keyboard'; @@ -215,10 +216,10 @@ export function PasswordLoginForm({ defaultUsername, defaultEmail }: PasswordLog {loginState.status === AsyncStatus.Error && ( <> {loginState.error.errcode === LoginError.ServerNotAllowed && ( - + )} {loginState.error.errcode === LoginError.InvalidServer && ( - + )} )} @@ -232,19 +233,19 @@ export function PasswordLoginForm({ defaultUsername, defaultEmail }: PasswordLog {loginState.status === AsyncStatus.Error && ( <> {loginState.error.errcode === LoginError.Forbidden && ( - + )} {loginState.error.errcode === LoginError.UserDeactivated && ( - + )} {loginState.error.errcode === LoginError.InvalidRequest && ( - + )} {loginState.error.errcode === LoginError.RateLimited && ( - + )} {loginState.error.errcode === LoginError.Unknown && ( - + )} )} diff --git a/src/app/pages/auth/register/Register.tsx b/src/app/pages/auth/register/Register.tsx index 7176489b..286fa5d3 100644 --- a/src/app/pages/auth/register/Register.tsx +++ b/src/app/pages/auth/register/Register.tsx @@ -12,6 +12,7 @@ import { SupportedUIAFlowsLoader } from '../../../components/SupportedUIAFlowsLo import { getLoginPath } from '../../pathUtils'; import { usePathWithOrigin } from '../../../hooks/usePathWithOrigin'; import { RegisterPathSearchParams } from '../../paths'; +import { registerTroubleMessage } from '../authMessages'; const useRegisterSearchParams = (searchParams: URLSearchParams): RegisterPathSearchParams => useMemo( @@ -40,17 +41,17 @@ export function Register() { {registerFlows.status === RegisterFlowStatus.RegistrationDisabled && !sso && ( - Registration has been disabled on this homeserver. + {registerTroubleMessage('closed', server)} )} {registerFlows.status === RegisterFlowStatus.RateLimited && !sso && ( - You have been rate-limited! Please try after some time. + {registerTroubleMessage('rate-limited', server)} )} {registerFlows.status === RegisterFlowStatus.InvalidRequest && !sso && ( - Invalid Request! Failed to get any registration options. + {registerTroubleMessage('invalid', server)} )} {registerFlows.status === RegisterFlowStatus.FlowRequired && ( @@ -62,7 +63,7 @@ export function Register() { {(supportedFlows) => supportedFlows.length === 0 ? ( - This application does not support registration on this homeserver. + {registerTroubleMessage('unsupported', server)} ) : ( Date: Tue, 10 Nov 2026 17:43:18 +0000 Subject: [PATCH 29/56] scene: one owner of ink per sky state (names, phone-dusk labels), New Messages as plain label ink Sender names over the terrain step to the state's ink: same OKLCH hue and chroma, darker by day and dusk, lighter at night, until 4.5:1 on the lightest measured terrain patch (capped at OKLCH L 0.9 at night). Sidebar labels get a darker dusk ink on phones. An explicit theme still wins. The unread divider keeps only its green line. Co-Authored-By: Claude Sonnet 5.5 --- src/app/components/nav/styles.css.ts | 3 +- src/app/features/room-nav/styles.css.ts | 3 +- src/app/features/room/RoomTimeline.tsx | 13 +- src/app/features/room/message/Message.tsx | 6 +- src/app/features/scene/sceneShell.css.ts | 7 + src/app/features/scene/skyInk.test.ts | 177 ++++++++++++++++++++++ src/app/features/scene/skyInk.ts | 95 ++++++++++++ src/app/hooks/useMemberPowerTag.ts | 15 +- src/app/hooks/useTheme.ts | 29 +++- src/app/pages/ThemeManager.tsx | 22 ++- src/util/colorMXID.js | 10 +- 11 files changed, 361 insertions(+), 19 deletions(-) create mode 100644 src/app/features/scene/skyInk.test.ts create mode 100644 src/app/features/scene/skyInk.ts diff --git a/src/app/components/nav/styles.css.ts b/src/app/components/nav/styles.css.ts index e5db6c47..6d1a6691 100644 --- a/src/app/components/nav/styles.css.ts +++ b/src/app/components/nav/styles.css.ts @@ -1,7 +1,7 @@ import { ComplexStyleRule, StyleRule, createVar, globalStyle, style } from '@vanilla-extract/css'; import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; import { ContainerColor, DefaultReset, Disabled, RadiiVariant, color, config, toRem } from 'folds'; -import { surfaceInk } from '../../features/scene/sceneShell.css'; +import { skyLabelInk, surfaceInk } from '../../features/scene/sceneShell.css'; // Wash strengths over the scene: rows and labels are faint, and hover, selected and pressed rows // are three distinct steps above them. @@ -222,6 +222,7 @@ phoneLabel(`.${NavItemBase}:has(.${NavLink}:active) ${label}`, { textDecorationLine: 'underline', }); phoneLabel(`:root[data-scene] ${label}`, { + color: skyLabelInk('inherit'), textShadow: `0 0 3px ${surfaceInk}, 0 0 8px ${surfaceInk}`, }); diff --git a/src/app/features/room-nav/styles.css.ts b/src/app/features/room-nav/styles.css.ts index b9655923..e5ab4f8e 100644 --- a/src/app/features/room-nav/styles.css.ts +++ b/src/app/features/room-nav/styles.css.ts @@ -1,6 +1,6 @@ import { style } from '@vanilla-extract/css'; import { config } from 'folds'; -import { surfaceInk } from '../scene/sceneShell.css'; +import { skyLabelInk, surfaceInk } from '../scene/sceneShell.css'; export const CategoryButton = style({ flexGrow: 1, @@ -15,6 +15,7 @@ export const CategoryButton = style({ borderRadius: 0, }, ':root[data-scene] &&': { + color: skyLabelInk('inherit'), textShadow: `0 0 3px ${surfaceInk}, 0 0 8px ${surfaceInk}`, filter: `drop-shadow(0 0 3px ${surfaceInk})`, }, diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx index c533528d..552fb04c 100644 --- a/src/app/features/room/RoomTimeline.tsx +++ b/src/app/features/room/RoomTimeline.tsx @@ -123,7 +123,8 @@ import { useSpaceOptionally } from '../../hooks/useSpace'; import { useRoomCreators } from '../../hooks/useRoomCreators'; import { useRoomPermissions } from '../../hooks/useRoomPermissions'; import { useAccessiblePowerTagColors, useGetMemberPowerTag } from '../../hooks/useMemberPowerTag'; -import { useTheme } from '../../hooks/useTheme'; +import { useSkyInk, useTheme } from '../../hooks/useTheme'; +import { skyLabelInk } from '../scene/sceneShell.css'; import { useRoomCreatorsTag } from '../../hooks/useRoomCreatorsTag'; import { usePowerLevelTags } from '../../hooks/usePowerLevelTags'; @@ -462,10 +463,12 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli const getMemberPowerTag = useGetMemberPowerTag(room, creators, powerLevels); const theme = useTheme(); + const skyInk = useSkyInk(); const accessiblePowerTagColors = useAccessiblePowerTagColors( theme.kind, creatorsTag, - powerLevelTags + powerLevelTags, + skyInk ); const permissions = useRoomPermissions(creators, powerLevels); @@ -1674,9 +1677,9 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli newDivider && eventJSX && eventSender !== mx.getUserId() ? ( - - New Messages - + + New Messages + ) : null; diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index 5c4421b6..62bb4dae 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -82,6 +82,8 @@ import { useRoomPinnedEvents } from '../../../hooks/useRoomPinnedEvents'; import { MemberPowerTag, StateEvent } from '../../../../types/matrix/room'; import { PowerIcon } from '../../../components/power'; import colorMXID from '../../../../util/colorMXID'; +import { useSkyInk } from '../../../hooks/useTheme'; +import { userColorInk } from '../../scene/skyInk'; import { getPowerTagIconSrc } from '../../../hooks/useMemberPowerTag'; export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void; @@ -723,6 +725,7 @@ export const Message = as<'div', MessageProps>( ref ) => { const mx = useMatrixClient(); + const skyInk = useSkyInk(); const useAuthentication = useMediaAuthentication(); const senderId = mEvent.getSender() ?? ''; @@ -743,7 +746,8 @@ export const Message = as<'div', MessageProps>( ? getPowerTagIconSrc(mx, useAuthentication, memberPowerTag.icon) : undefined; - const usernameColor = legacyUsernameColor ? colorMXID(senderId) : tagColor; + const hashColor = skyInk ? userColorInk(senderId, skyInk) : colorMXID(senderId); + const usernameColor = legacyUsernameColor ? hashColor : tagColor; const headerJSX = !collapse && ( by the theme manager (ThemeManager.tsx) only where it + * differs from the theme's; unset, `skyLabelInk(fallback)` is the fallback, so a person's own theme keeps its ink. + */ +export const SKY_LABEL_INK = '--sky-label-ink'; +export const skyLabelInk = (fallback: string) => `var(${SKY_LABEL_INK}, ${fallback})`; diff --git a/src/app/features/scene/skyInk.test.ts b/src/app/features/scene/skyInk.test.ts new file mode 100644 index 00000000..4cf2a6c4 --- /dev/null +++ b/src/app/features/scene/skyInk.test.ts @@ -0,0 +1,177 @@ +import chroma from 'chroma-js'; +import { describe, expect, it } from 'vitest'; +import { baked } from '../../../sky/baked'; +import { AA, NIGHT_SKY_LIGHTEST, luminance } from '../../../sky/skyTokens.js'; +import { indexColorMXID } from '../../../util/colorMXID'; +import type { FixedSkyState } from './skyBand'; +import { + LIGHTEST_PATCH, + NIGHT_INK_MAX_L, + inkForState, + labelInk, + sceneInk, + userColorInk, +} from './skyInk'; +import { sceneThemeId } from './skyTheme'; + +// Every name colour the client can show over the scene, per state and viewport: Aqua's key colour #22d3e6 as +// accessibleColor leaves it (Lab L capped to 50 on a light theme: #00889a; unchanged on a dark one: #22d3e6), and +// the eight hash hues of the theme the state wears (noonday for day and dusk, midnight for night). Columns: name, +// state, phone, colour in, ink out. Aqua's day and dusk rows are the values measured in +// ratatoskr-sidebar-contrast-20261110/results.md (T4): #05707f, #016a78, #055763, #03454f, and #43e5f8 at night. +const TABLE: Array<[string, FixedSkyState, boolean, string, string]> = [ + ['aqua', 'day', false, '#00889a', '#05707f'], + ['aqua', 'day', true, '#00889a', '#016a78'], + ['aqua', 'dusk', false, '#00889a', '#055763'], + ['aqua', 'dusk', true, '#00889a', '#03454f'], + ['aqua', 'night', false, '#22d3e6', '#43e5f8'], + ['aqua', 'night', true, '#22d3e6', '#87f1ff'], + ['hash1', 'day', false, '#2b4675', '#2b4675'], + ['hash1', 'day', true, '#2b4675', '#2b4675'], + ['hash1', 'dusk', false, '#2b4675', '#2b4675'], + ['hash1', 'dusk', true, '#2b4675', '#233e6c'], + ['hash1', 'night', false, '#4683ff', '#bcd3ff'], + ['hash1', 'night', true, '#4683ff', '#cbddfe'], + ['hash2', 'day', false, '#5d366d', '#5d366d'], + ['hash2', 'day', true, '#5d366d', '#5d366d'], + ['hash2', 'dusk', false, '#5d366d', '#5d366d'], + ['hash2', 'dusk', true, '#5d366d', '#562f66'], + ['hash2', 'night', false, '#986fff', '#d4cbfe'], + ['hash2', 'night', true, '#986fff', '#ded7fe'], + ['hash3', 'day', false, '#214e3e', '#214e3e'], + ['hash3', 'day', true, '#214e3e', '#214e3e'], + ['hash3', 'dusk', false, '#214e3e', '#214e3e'], + ['hash3', 'dusk', true, '#214e3e', '#184636'], + ['hash3', 'night', false, '#3893b1', '#86defe'], + ['hash3', 'night', true, '#3893b1', '#ace8ff'], + ['hash4', 'day', false, '#6d334a', '#6d334a'], + ['hash4', 'day', true, '#6d334a', '#6d334a'], + ['hash4', 'dusk', false, '#6d334a', '#6d334a'], + ['hash4', 'dusk', true, '#6d334a', '#652c43'], + ['hash4', 'night', false, '#b26adc', '#e8c7fe'], + ['hash4', 'night', true, '#b26adc', '#edd2ff'], + ['hash5', 'day', false, '#5f401d', '#5f401d'], + ['hash5', 'day', true, '#5f401d', '#5f401d'], + ['hash5', 'dusk', false, '#5f401d', '#5f401d'], + ['hash5', 'dusk', true, '#5f401d', '#563814'], + ['hash5', 'night', false, '#9f8365', '#ecceae'], + ['hash5', 'night', true, '#9f8365', '#f6d8b7'], + ['hash6', 'day', false, '#204c52', '#204c52'], + ['hash6', 'day', true, '#204c52', '#204c52'], + ['hash6', 'dusk', false, '#204c52', '#204c52'], + ['hash6', 'dusk', true, '#204c52', '#19454b'], + ['hash6', 'night', false, '#358dde', '#b1d7ff'], + ['hash6', 'night', true, '#358dde', '#c6e1fe'], + ['hash7', 'day', false, '#41426c', '#41426c'], + ['hash7', 'day', true, '#41426c', '#41426c'], + ['hash7', 'dusk', false, '#41426c', '#41426c'], + ['hash7', 'dusk', true, '#41426c', '#3b3b65'], + ['hash7', 'night', false, '#677dff', '#c4d1fe'], + ['hash7', 'night', true, '#677dff', '#d3ddfe'], + ['hash8', 'day', false, '#3f4a20', '#3f4a20'], + ['hash8', 'day', true, '#3f4a20', '#3f4a20'], + ['hash8', 'dusk', false, '#3f4a20', '#3f4a20'], + ['hash8', 'dusk', true, '#3f4a20', '#394319'], + ['hash8', 'night', false, '#6a926a', '#b2ddb1'], + ['hash8', 'night', true, '#6a926a', '#c0ebc0'], +]; + +const patchRatio = (hex: string, state: FixedSkyState, phone: boolean) => { + const p = LIGHTEST_PATCH[state][phone ? 'phone' : 'desktop']; + const l = luminance(hex); + return (Math.max(l, p) + 0.05) / (Math.min(l, p) + 0.05); +}; +const hueGap = (a: string, b: string) => { + const d = Math.abs(chroma(a).oklch()[2] - chroma(b).oklch()[2]) % 360; + return Math.min(d, 360 - d); +}; + +describe('inkForState', () => { + it.each(TABLE)('%s %s phone=%s: %s -> %s', (_name, state, phone, base, ink) => { + expect(inkForState(base, { state, phone })).toBe(ink); + }); + + it('clears 4.5:1 on the lightest patch, keeps the hue, and moves lightness the way the state needs', () => { + TABLE.forEach(([, state, phone, base, ink]) => { + const [l0] = chroma(base).oklch(); + const [l1] = chroma(ink).oklch(); + if (ink === base) { + expect(patchRatio(base, state, phone)).toBeGreaterThanOrEqual(AA); + return; + } + expect(state === 'night' ? l1 > l0 : l1 < l0).toBe(true); + expect(hueGap(base, ink)).toBeLessThan(2); + if (l1 < NIGHT_INK_MAX_L - 0.006) + expect(patchRatio(ink, state, phone)).toBeGreaterThanOrEqual(AA); + }); + }); + + it('stops lightening at NIGHT_INK_MAX_L: a phone-night name keeps its hue instead of reaching 4.5:1 on the one pale snow patch', () => { + const aqua = inkForState('#22d3e6', { state: 'night', phone: true }); + expect(aqua).toBe('#87f1ff'); + expect(chroma(aqua).oklch()[0]).toBeLessThanOrEqual(NIGHT_INK_MAX_L); + expect(chroma(aqua).oklch()[1]).toBeGreaterThan(0.08); + expect(patchRatio(aqua, 'night', true)).toBeGreaterThan(3.9); + // The one-patch figure is what the cap gives up; against the sky's own lightest (skyBackdrop) it clears AA. + expect((luminance(aqua) + 0.05) / (NIGHT_SKY_LIGHTEST + 0.05)).toBeGreaterThanOrEqual(AA); + }); + + it('leaves a colour that already clears its patch untouched, and a non-colour alone', () => { + expect(inkForState('#2b4675', { state: 'day', phone: false })).toBe('#2b4675'); + expect(inkForState('var(--mx-uc-1)', { state: 'day', phone: false })).toBe('var(--mx-uc-1)'); + }); +}); + +describe('userColorInk', () => { + it('takes the hash colour of the theme the state wears, then steps it', () => { + const id = '@aqua:gamecult.org'; + const n = indexColorMXID(id) - 1; + expect(userColorInk(id, { state: 'dusk', phone: true })).toBe( + inkForState(baked.noonday.userColors[n], { state: 'dusk', phone: true }) + ); + expect(userColorInk(id, { state: 'night', phone: false })).toBe( + inkForState(baked.midnight.userColors[n], { state: 'night', phone: false }) + ); + }); +}); + +describe('sceneInk: an explicit theme wins over the auto ink', () => { + it('is the sky state and viewport only while the sky theme is worn and a scene runs', () => { + // auto, scene on, no theme of the person's own: the sky theme is worn. + expect(sceneInk(sceneThemeId('auto', 'dusk', true, false), true, 'dusk', true)).toEqual({ + state: 'dusk', + phone: true, + }); + // auto, but the person chose a theme: it wins, the theme's own ink stands. + expect(sceneInk(sceneThemeId('auto', 'dusk', true, true), true, 'dusk', true)).toBeUndefined(); + // a fixed sky state is itself a choice and wins over the theme (skyTheme.ts), so its ink applies. + expect(sceneInk(sceneThemeId('night', 'night', true, true), true, 'night', false)).toEqual({ + state: 'night', + phone: false, + }); + // no scene behind the page: nothing for the ink to be measured against. + expect( + sceneInk(sceneThemeId('auto', 'day', false, false), false, 'day', false) + ).toBeUndefined(); + expect(sceneInk(sceneThemeId('day', 'day', true, false), false, 'day', false)).toBeUndefined(); + }); +}); + +describe('labelInk', () => { + it('differs from the theme only on a phone at dusk', () => { + const states: FixedSkyState[] = ['day', 'dusk', 'night']; + states.forEach((state) => + [false, true].forEach((phone) => { + expect(labelInk({ state, phone })).toBe(state === 'dusk' && phone ? '#29221a' : undefined); + }) + ); + }); + + it('is the shipped dark ink, same hue and chroma, a little darker', () => { + const dark = baked.noonday.tokens.Surface.OnContainer; + const ink = labelInk({ state: 'dusk', phone: true }) as string; + expect(dark).toBe('#342d25'); + expect(luminance(ink)).toBeLessThan(luminance(dark)); + expect(hueGap(dark, ink)).toBeLessThan(2); + }); +}); diff --git a/src/app/features/scene/skyInk.ts b/src/app/features/scene/skyInk.ts new file mode 100644 index 00000000..7219159c --- /dev/null +++ b/src/app/features/scene/skyInk.ts @@ -0,0 +1,95 @@ +// The one owner of ink for a sky state: which colour a sender name or a sidebar label wears over the scene, +// per fixed sky state (day, dusk, night) and viewport (phone or not). Pure: the hooks that read the resolved +// sky and the viewport (useSkyInk, hooks/useTheme.ts) only hand their values here. +// +// Names are bare ink over the terrain, so the theme's fitted colours (fitted against panel grounds, skyTokens.js) +// can land on the same luminance as the ridge behind them. The rule is measured, not tuned by eye: keep the +// colour's OKLCH hue and chroma and move its lightness (darker by day and dusk, lighter at night, in 0.005 steps) +// until it clears 4.5:1 (WCAG) against the LIGHTEST 24x12 patch of terrain under the timeline in that state +// and viewport. Measured 2026-11-10 on the demo's terrain scene at 1920x1080 and 390x844 (Chrome, SwiftShader, +// not a GPU; ratatoskr-sidebar-contrast-20261110/results.md, T3). These are the scene's own patches, not +// `skyBackdrop` (skyState.ts): that is the bare sky's extreme (day 0.90, dusk 0.97), which the terrain under the +// timeline never shows, and at night the terrain's snow (0.103, phone 0.149) is lighter than the sky (0.0816). +// Only a person whose theme is the sky's (no explicit theme of their own, sceneThemeId) wears this ink. +import chroma from 'chroma-js'; +import { AA, luminance } from '../../../sky/skyTokens.js'; +import { baked } from '../../../sky/baked'; +import { indexColorMXID } from '../../../util/colorMXID'; +import type { FixedSkyState } from './skyBand'; +import type { SkyThemeId } from './skyTheme'; + +export interface SkyInk { + readonly state: FixedSkyState; + /** At or under the repo's mobile breakpoint (ScreenSize.Mobile, hooks/useScreenSize.ts); tablets read as desktop. */ + readonly phone: boolean; +} + +/** + * The ink in force: only while the page wears the sky's theme (`themeId`, from sceneThemeId, so a person's explicit + * theme, which that function lets win over 'auto', keeps its own ink) and a scene runs behind it. Otherwise undefined. + */ +export const sceneInk = ( + themeId: SkyThemeId | undefined, + sceneOn: boolean, + state: FixedSkyState, + phone: boolean +): SkyInk | undefined => (themeId && sceneOn ? { state, phone } : undefined); + +/** WCAG relative luminance of the lightest 24x12 patch of terrain behind the timeline. */ +export const LIGHTEST_PATCH: Record = { + day: { desktop: 0.777, phone: 0.703 }, + dusk: { desktop: 0.531, phone: 0.4 }, + night: { desktop: 0.103, phone: 0.149 }, +}; + +/** Night lightening stops here (OKLCH L): above it every hue reads white, and the hue is what names the sender. */ +export const NIGHT_INK_MAX_L = 0.9; + +const STEP = 0.005; +const SKY_COLORS = { noonday: baked.noonday.userColors, midnight: baked.midnight.userColors }; + +const inGamut = (l: number, c: number, h: number): string | undefined => { + for (let k = c; k > 1e-4; k *= 0.97) { + const out = chroma.oklch(l, k, h); + if (!out.clipped()) return out.hex(); + } + return chroma.oklch(l, 0, 0).hex(); +}; + +/** The ink for a colour (#rrggbb) in this sky state and viewport: itself when it already clears the lightest patch. */ +export function inkForState(hex: string, { state, phone }: SkyInk): string { + if (!chroma.valid(hex)) return hex; + const patch = LIGHTEST_PATCH[state][phone ? 'phone' : 'desktop']; + const ratio = (h: string) => { + const l = luminance(h); + return (Math.max(l, patch) + 0.05) / (Math.min(l, patch) + 0.05); + }; + if (ratio(hex) >= AA) return hex; + const [l0, c0, h0] = chroma(hex).oklch(); + const hue = Number.isNaN(h0) ? 0 : h0; + const dir = state === 'night' ? 1 : -1; + let best = hex; + for (let k = 1; k < 200; k += 1) { + const l = l0 + dir * k * STEP; + if (l < 0 || l > (state === 'night' ? NIGHT_INK_MAX_L : 1)) break; + best = inGamut(l, c0, hue) ?? best; + if (ratio(best) >= AA) break; + } + return best; +} + +/** The ink of a user's hash colour (the --mx-uc-1..8 the theme would give) in this sky state and viewport. */ +export function userColorInk(userId: string, ink: SkyInk): string { + const palette = ink.state === 'night' ? SKY_COLORS.midnight : SKY_COLORS.noonday; + return inkForState(palette[indexColorMXID(userId) - 1], ink); +} + +/** + * The sidebar label ink where it differs from the theme's. Phone Dusk: the dark ink #342d25 reached a median of 3.98:1 + * on the glyph pixels (worst 1.66); the same hue and chroma at OKLCH L 0.257 (#29221a) reaches 4.53 (worst 1.92), + * measured over the shipped dusk phone shot, halo included. Nothing darker is searched for: the ridge's dusk ochre + * sits at the dark ink's own luminance, so the worst pixels stay below 4.5. Every other state keeps the theme's + * ink (undefined): desktop and night were measured as shipped. + */ +export const labelInk = ({ state, phone }: SkyInk): string | undefined => + state === 'dusk' && phone ? '#29221a' : undefined; diff --git a/src/app/hooks/useMemberPowerTag.ts b/src/app/hooks/useMemberPowerTag.ts index 31e52aa0..68051749 100644 --- a/src/app/hooks/useMemberPowerTag.ts +++ b/src/app/hooks/useMemberPowerTag.ts @@ -6,6 +6,7 @@ import { MemberPowerTag, MemberPowerTagIcon } from '../../types/matrix/room'; import { useRoomCreatorsTag } from './useRoomCreatorsTag'; import { ThemeKind } from './useTheme'; import { accessibleColor } from '../plugins/color'; +import { inkForState, type SkyInk } from '../features/scene/skyInk'; export type GetMemberPowerTag = (userId: string) => MemberPowerTag; @@ -44,23 +45,29 @@ export const getPowerTagIconSrc = ( export const useAccessiblePowerTagColors = ( themeKind: ThemeKind, creatorsTag: MemberPowerTag, - powerLevelTags: PowerLevelTags + powerLevelTags: PowerLevelTags, + skyInk?: SkyInk ): Map => { const accessibleColors: Map = useMemo(() => { const colors: Map = new Map(); + // Over the scene a name wears the sky state's ink (skyInk.ts) in place of the theme's accessible colour's own step. + const ink = (color: string) => { + const accessible = accessibleColor(themeKind, color); + return skyInk ? inkForState(accessible, skyInk) : accessible; + }; if (creatorsTag.color) { - colors.set(creatorsTag.color, accessibleColor(themeKind, creatorsTag.color)); + colors.set(creatorsTag.color, ink(creatorsTag.color)); } Object.values(powerLevelTags).forEach((tag) => { const { color } = tag; if (!color) return; - colors.set(color, accessibleColor(themeKind, color)); + colors.set(color, ink(color)); }); return colors; - }, [powerLevelTags, creatorsTag, themeKind]); + }, [powerLevelTags, creatorsTag, themeKind, skyInk]); return accessibleColors; }; diff --git a/src/app/hooks/useTheme.ts b/src/app/hooks/useTheme.ts index 60f89757..1ef0278d 100644 --- a/src/app/hooks/useTheme.ts +++ b/src/app/hooks/useTheme.ts @@ -13,7 +13,9 @@ import '../../sky.css'; import { defaultThemeId, settingsAtom } from '../state/settings'; import { useSetting } from '../state/hooks/settings'; import { sceneThemeId } from '../features/scene/skyTheme'; +import { sceneInk, type SkyInk } from '../features/scene/skyInk'; import { useResolvedSkyState, useSceneOn, useSkyState } from './useSkyState'; +import { ScreenSize, useScreenSizeContext } from './useScreenSize'; export enum ThemeKind { Light = 'light', @@ -113,18 +115,39 @@ const chosenTheme = (useSystemTheme: boolean, themeId?: string) => export const useSkyTheme = (): Theme | undefined => toTheme(sceneThemeId(useSkyState(), useResolvedSkyState(), useSceneOn(), false)); -export const useActiveTheme = (): Theme => { +/** The sky theme the signed-in page wears, or undefined when the person's own theme wins. */ +const useSceneSkyThemeId = (): ReturnType => { const sky = useSkyState(); const resolved = useResolvedSkyState(); const sceneOn = useSceneOn(); + const [systemTheme] = useSetting(settingsAtom, 'useSystemTheme'); + const [themeId] = useSetting(settingsAtom, 'themeId'); + // A person's own theme wins over 'auto'; a fixed sky state is itself a choice and wins over the theme. + return sceneThemeId(sky, resolved, sceneOn, chosenTheme(systemTheme, themeId)); +}; + +/** The ink the page wears over the scene (`sceneInk`, skyInk.ts), or undefined when the theme's own ink stands. */ +export const useSceneInk = (): SkyInk | undefined => { + const themeId = useSceneSkyThemeId(); + const state = useResolvedSkyState(); + const sceneOn = useSceneOn(); + const phone = useScreenSizeContext() === ScreenSize.Mobile; + return useMemo(() => sceneInk(themeId, sceneOn, state, phone), [themeId, sceneOn, state, phone]); +}; + +const SkyInkContext = createContext(undefined); +export const SkyInkContextProvider = SkyInkContext.Provider; +/** The ink for this sky state and viewport (provided once by the theme manager), or undefined for the theme's own. */ +export const useSkyInk = (): SkyInk | undefined => useContext(SkyInkContext); + +export const useActiveTheme = (): Theme => { const systemThemeKind = useSystemThemeKind(); const themes = useThemes(); const [systemTheme] = useSetting(settingsAtom, 'useSystemTheme'); const [themeId] = useSetting(settingsAtom, 'themeId'); const [lightThemeId] = useSetting(settingsAtom, 'lightThemeId'); const [darkThemeId] = useSetting(settingsAtom, 'darkThemeId'); - // A person's own theme wins over 'auto'; a fixed sky state is itself a choice and wins over the theme. - const skyTheme = toTheme(sceneThemeId(sky, resolved, sceneOn, chosenTheme(systemTheme, themeId))); + const skyTheme = toTheme(useSceneSkyThemeId()); if (skyTheme) return skyTheme; diff --git a/src/app/pages/ThemeManager.tsx b/src/app/pages/ThemeManager.tsx index b7c50d93..980d35b7 100644 --- a/src/app/pages/ThemeManager.tsx +++ b/src/app/pages/ThemeManager.tsx @@ -2,12 +2,16 @@ import React, { ReactNode, useEffect } from 'react'; import { configClass, varsClass } from 'folds'; import { MidnightSkyTheme, + SkyInkContextProvider, ThemeContextProvider, useActiveTheme, + useSceneInk, useSkyTheme, } from '../hooks/useTheme'; import { useSetting } from '../state/hooks/settings'; import { settingsAtom } from '../state/settings'; +import { labelInk } from '../features/scene/skyInk'; +import { SKY_LABEL_INK } from '../features/scene/sceneShell.css'; export function UnAuthRouteThemeManager() { // Signed-out pages are Midnight Sky unless the sky state picks otherwise; they ignore the @@ -26,6 +30,18 @@ export function UnAuthRouteThemeManager() { export function AuthRouteThemeManager({ children }: { children: ReactNode }) { const activeTheme = useActiveTheme(); const [monochromeMode] = useSetting(settingsAtom, 'monochromeMode'); + const skyInk = useSceneInk(); + const labels = skyInk && labelInk(skyInk); + + // The sky's label ink where it differs from the theme's (skyInk.ts); unset, the theme's ink stands. + useEffect(() => { + const root = document.documentElement; + if (labels) root.style.setProperty(SKY_LABEL_INK, labels); + else root.style.removeProperty(SKY_LABEL_INK); + return () => { + root.style.removeProperty(SKY_LABEL_INK); + }; + }, [labels]); useEffect(() => { document.body.className = ''; @@ -40,5 +56,9 @@ export function AuthRouteThemeManager({ children }: { children: ReactNode }) { } }, [activeTheme, monochromeMode]); - return {children}; + return ( + + {children} + + ); } diff --git a/src/util/colorMXID.js b/src/util/colorMXID.js index 95600d29..efebf90c 100644 --- a/src/util/colorMXID.js +++ b/src/util/colorMXID.js @@ -10,16 +10,20 @@ function hashCode(str) { for (i = 0; i < str.length; i += 1) { chr = str.charCodeAt(i); // eslint-disable-next-line no-bitwise - hash = ((hash << 5) - hash) + chr; + hash = (hash << 5) - hash + chr; // eslint-disable-next-line no-bitwise hash |= 0; } return Math.abs(hash); } +/** Which of the eight user colours (1..8) a user id takes. */ +export function indexColorMXID(userId) { + return (hashCode(userId) % 8) + 1; +} + export function cssColorMXID(userId) { - const colorNumber = hashCode(userId) % 8; - return `--mx-uc-${colorNumber + 1}`; + return `--mx-uc-${indexColorMXID(userId)}`; } export default function colorMXID(userId) { From de9955a24a241cfdc9a77a18ab485e9e81212300 Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 17:59:07 +0000 Subject: [PATCH 30/56] dings: Aqua's v4 set (Void single onset, Nibu proposal 3 everywhere), still behind dings.enabled Replaces the 32 sender clips with AquaSynth 21e83ec's staged render; manifest and neutral fallback are byte-identical to the old ones. The manifest test now pins every clip by sha256 prefix. Unheard. Co-Authored-By: Claude Sonnet 5.5 --- public/dings/README.md | 4 +- public/dings/senders/aqua/dm.ogg | Bin 4687 -> 4687 bytes public/dings/senders/aqua/invite.ogg | Bin 4863 -> 4863 bytes public/dings/senders/aqua/mention.ogg | Bin 4538 -> 4538 bytes public/dings/senders/aqua/message.ogg | Bin 4563 -> 4563 bytes public/dings/senders/eureka/dm.ogg | Bin 4583 -> 4583 bytes public/dings/senders/eureka/invite.ogg | Bin 4727 -> 4727 bytes public/dings/senders/eureka/mention.ogg | Bin 4443 -> 4443 bytes public/dings/senders/eureka/message.ogg | Bin 4425 -> 4425 bytes public/dings/senders/heimdall/dm.ogg | Bin 5184 -> 5184 bytes public/dings/senders/heimdall/invite.ogg | Bin 5476 -> 5477 bytes public/dings/senders/heimdall/mention.ogg | Bin 5217 -> 5217 bytes public/dings/senders/heimdall/message.ogg | Bin 4746 -> 4746 bytes public/dings/senders/kiko/dm.ogg | Bin 5703 -> 5703 bytes public/dings/senders/kiko/invite.ogg | Bin 5627 -> 5627 bytes public/dings/senders/kiko/mention.ogg | Bin 5044 -> 5044 bytes public/dings/senders/kiko/message.ogg | Bin 5090 -> 5091 bytes public/dings/senders/libby/dm.ogg | Bin 5426 -> 5425 bytes public/dings/senders/libby/invite.ogg | Bin 5613 -> 5613 bytes public/dings/senders/libby/mention.ogg | Bin 5108 -> 5108 bytes public/dings/senders/libby/message.ogg | Bin 5075 -> 5074 bytes public/dings/senders/nibu/dm.ogg | Bin 6557 -> 6556 bytes public/dings/senders/nibu/invite.ogg | Bin 6583 -> 6583 bytes public/dings/senders/nibu/mention.ogg | Bin 6257 -> 6257 bytes public/dings/senders/nibu/message.ogg | Bin 5663 -> 5662 bytes public/dings/senders/void/dm.ogg | Bin 5402 -> 4754 bytes public/dings/senders/void/invite.ogg | Bin 5667 -> 5035 bytes public/dings/senders/void/mention.ogg | Bin 5022 -> 4561 bytes public/dings/senders/void/message.ogg | Bin 5362 -> 4570 bytes public/dings/senders/weksa/dm.ogg | Bin 5682 -> 5683 bytes public/dings/senders/weksa/invite.ogg | Bin 6120 -> 6122 bytes public/dings/senders/weksa/mention.ogg | Bin 5291 -> 5292 bytes public/dings/senders/weksa/message.ogg | Bin 5272 -> 5272 bytes src/app/features/dings/manifest.test.ts | 55 +++++++++++++++++----- 34 files changed, 44 insertions(+), 15 deletions(-) diff --git a/public/dings/README.md b/public/dings/README.md index 573a78f9..b36e1fb7 100644 --- a/public/dings/README.md +++ b/public/dings/README.md @@ -12,6 +12,6 @@ with motif one of `mention`, `dm`, `message`, `invite`; paths are relative to th A sender's own clip wins, else the neutral clip, else silence. Without a `manifest.json` the client keeps its stock notification sounds; once one exists, a motif with no clip is silent. -The set here is Aqua's v3 (AquaSynth PR #22, staged at minds.gamecult.org/aqua/dings-voices-70a9795), -Nibu on proposal 1 (static-filter square). Nobody has heard it. The client plays none of it unless the +The set here is Aqua's v4 (AquaSynth merge 21e83ec, staged at minds.gamecult.org/aqua/dings-voices-dd9be66), +Nibu on proposal 3 (8 kHz 5-bit smoothed sine) for all four events. Nobody has heard it. The client plays none of it unless the client config sets `"dings": { "enabled": true }` (default off, as in `config.json`). diff --git a/public/dings/senders/aqua/dm.ogg b/public/dings/senders/aqua/dm.ogg index 0a2b98da6b7cd4b4836af63bbbdf70323b098fdb..5a23554656f200923f9ff45ff79b50f5a12a466b 100644 GIT binary patch delta 83 zcmX@Fa$ZHsKRrE|fr$YMmh4h@2Qk~YA1<0GrN;x}GXnW8XRjRI81jb~EC5o?UyxWo YIfm~LQ{4N>3jEKRDjsd-6mVk#0LAwi8vpeO2QlUNG6R0j6iG6R0j6l9n|I4czL;moB1wg8=FrMGC JIfgHe82~AC5w8FM diff --git a/public/dings/senders/aqua/message.ogg b/public/dings/senders/aqua/message.ogg index ce68c47aa5e6474202000332c852eaa000f12680..3704f55aa24d3b942ab7dc4639ea754a15452c18 100644 GIT binary patch delta 69 zcmcbtd|6q_KRrE|fr$YM<~aD+ftc0DqwY?W(&GW~8G-!BYisQ{hWz0L3xHI&Rjir4 JIfk#C831S76CD5m delta 69 zcmcbtd|6q_KRrE|fr$YMwk$j+4`QYs%+#7FrN;x}GXnX6b&eSuL;moB1wg8=^1eB@ JIfk#C832%&6r}(F diff --git a/public/dings/senders/eureka/dm.ogg b/public/dings/senders/eureka/dm.ogg index a64c22f60b4025f0ac3569c2edf5cbd8126740c5..57024fbe5d6d158ebb190f7096dee302840c9771 100644 GIT binary patch delta 69 zcmaE^{9IYeKRrE|fr$YMS|&7QfSAjCHpoqs(&GW~8G-y?kE4w@hWz0L3xHI=l)wFK Ja|~ZMGXR@76+8d{ delta 69 zcmaE^{9IYeKRrE|fr$YM=1j5W1~F&c{~$S0N{M{TM$hWz0L3xHIA6??R3 Ja}1v}3jn2Z6t@5X diff --git a/public/dings/senders/eureka/mention.ogg b/public/dings/senders/eureka/mention.ogg index 0025d7429d2c03ead0c324b27675bc9a4791ed65..c4085faead552f391c5ae999be696952dbec8541 100644 GIT binary patch delta 69 zcmcbubX!TvKRrE|fr$YMdQMN11~G5R>Y7cI(&GW~8G-yN?lQ5BA%A$m0wC2N)fE?P Jj^X>y1OQ@-69WJM delta 69 zcmcbubX!TvKRrE|fr$YMmh_3Lf|&c7JqjmE>G6R0j6nX$nTk&~hWz0L3xHIAj@Mqe JIfm~)698l36KVhe diff --git a/public/dings/senders/eureka/message.ogg b/public/dings/senders/eureka/message.ogg index beb2a203b9d9af6218fde08c56c7c07126c0d4e0..6cd340ce391adf6c38117879b4170fad7febede9 100644 GIT binary patch delta 69 zcmX@9bW%ymKRrE|fr$YMYP5XWK}@qQ9> delta 69 zcmX@9bW%ymKRrE|fr$YMrks}rvo1<1Zk#Bk#{=Rs0!41$Q;XXe@`o2J08*V3pr^k% IhVLyC08MBT0{{R3 diff --git a/public/dings/senders/heimdall/dm.ogg b/public/dings/senders/heimdall/dm.ogg index f4844c021e4bca621f43c9cc89dec0f19c16c0d0..145e885336ba8be55c558c981273ace4d1088bbc 100644 GIT binary patch delta 91 zcmX@0aX>@LKRrE|fr$YMQnuYn2Qj7mejS`BrN;x}GXnXM;ubSDhWz0L3xHH_l~MaJ gIfk!8@Z$nIMus=@_=3A_yj?$wT5)dv%ID1r0J`TKvj6}9 delta 91 zcmX@0aX>@LKRrE|fr$YMYU*-wK+FY)_U28L(&GW~8G-ympV=304Ee(g767T{ZGZB3 gatvRGU|qm`28J^K_LZ*lmM;3qHJ4@cS3Yl60K%#r=l}o! diff --git a/public/dings/senders/heimdall/invite.ogg b/public/dings/senders/heimdall/invite.ogg index 6e27e760b4f9aa1387826ffc5633be9da21ec8c9..98403c21128d08f0960988357323e9255b6b963d 100644 GIT binary patch delta 142 zcmaE&^;ApBKRrE|fr$YMLfp-zLCn)xJN8YK(&GW~8G-zzTPkO54Ee(g767T{bbDq! zIfhS?(Q$JbUp1@k-B00+JNDTs9M~@EVfUW@Yz>ROwHWGrHtUI)GXemWrz{=- diff --git a/public/dings/senders/heimdall/mention.ogg b/public/dings/senders/heimdall/mention.ogg index e006414703fe25088289ba127c85f579645d7cf1..5a39a98ce44d2b6673af338c344850af4fd23bbc 100644 GIT binary patch delta 123 zcmaE;@lZp`KRrE|fr$YMa$=8(ftVao|5r|w(&GW~8G-y1XE%k7A%A$m0wC49vQ<4c z$MB^v8q^E$W>{a?yKvbKCWb$OlSE^dgA8DJ@U<+YuW${z4W2c!wzz delta 123 zcmaE;@lZp`KRrE|fr$YMT2kV~K}@!*9XBUR>G6R0j6l9;j_Tu$A%A$m0wC3bXLerN z9K)BwXt2MN?~CkG6R0j6nWD{t0#)L;moB1wg80>*6~m Q$MA(PEtlE6iSGt80J)D8J^%m! delta 76 zcmeBD?NXKUPfrhKU}AuR65l=%5Hsfe#qx<#dORRLBanaFb>F^?A%A$m0wC4-+z%or Q$MA(PeV5z3iSGt80NaZdt^fc4 diff --git a/public/dings/senders/kiko/dm.ogg b/public/dings/senders/kiko/dm.ogg index 8b76726e0b934f2c741cd1844fec12b9ec8e6c67..7c35b072c4ed71a0c4942185fe976cd5266f1aba 100644 GIT binary patch delta 124 zcmX@Eb6iKtKRrE|fr$YMTy$PYf|x~9YJDe4>G6R0j6nX|TO}7ahWz0L3xHI=?&tl# zIficuo1u8Vf4=IMn*Wn0S8V>xT(q~#LzsaflfjRffkT{uA%LBM;Yd6qgTtE*+vGqd MKgir{C@RJX0LL37x&QzG delta 124 zcmX@Eb6iKtKRrE|fr$YMVs<RNwp za}1vXix~FM%d{&o&$N>=s UhYpa!1DawC3=zDO*Ngf90I0z%0{{R3 delta 134 zcmeyZ{aahgKRrE|fr$YMd?qIG6R0j6i-;O=jfAkUzX&0g!6b*>$d) zWB3$U#P}a5wdjG=0D=GEpPCK_Cmr~=a`Pmizf21G_a>LsT>bkse%;q}Q&$151TF>z T7m&gSBHRoNYuF~Q7xe)EzU3%P diff --git a/public/dings/senders/kiko/mention.ogg b/public/dings/senders/kiko/mention.ogg index 388dbbabfff693398d68b15fdbf993f3a4d85163..12cccf293a5cf9c59988f06a428a7e6ee674f0ac 100644 GIT binary patch delta 113 zcmdm@zC~TiKRrE|fr$YMeC+4iftV#b6FyIr(&GW~8G-!!b}uD2hWz0L3xHG$tQX$9 zIfm~xBh$Y(lPv{Qn0NjR-kc!tj!FEg2q!B;s4#~($Q%ZSbGbmGL7ic8s<1x*$f_Q8 delta 113 zcmdm@zC~TiKRrE|fr$YMQo5FefSA=4>vm0)(&GW~8G-y;2GdG6R0j6lAhi@D>*kUzZQAOVo-C!Gtn zNa<>&+1T5dSz4HGPUo{_(*Hk8Ns*PoL5g7kTMG}!7*-(G;AUX>vD7-7f#CrM1B2W) fn|$+w)1RID`Si`^D+0F|SzUpO95!zjGGGJ%NH!;= diff --git a/public/dings/senders/libby/dm.ogg b/public/dings/senders/libby/dm.ogg index 8b580a4bfc3c17780ebb3ed86552d970bdfe9825..1316c19600e1fda5f082ccbb6245736fd55b0058 100644 GIT binary patch delta 156 zcmdm_wNXpTKRrE|fr$YMR6ZrKgP2Z>X0DznrN;x}GXnYT#zHO|L;mn8fdoLRyR2XE zD@b{}xmsG9SXmh8n~SL_#d%4oiyIR;78YVC?2pEA( so_Cq?KqM!F!(NdC6~~wu8qR!A;{*yj6i=2HzQ>Zi&rW_bqlh6R0NWWYDF6Tf delta 156 zcmdm}wMk3LKRrE|fr$YMY@AZWK+G>EpEXUC(&GW~8G-ya2K?q5L;mn8fdoLR7X?jS zq9En%=4xqaVr5~VZ!V^$6zk>W=xDpSh_8ypc<1MuTmSdJzE|2==sWQO1A{;i1A`MA tP~(AOD+UW~MurvBnIz6fFfdH`c~1vqLk8buIpKRO+WYMEH;EWB0s!9vEI$AM diff --git a/public/dings/senders/libby/invite.ogg b/public/dings/senders/libby/invite.ogg index f88e6d2877996eb88e609a02884f7b57fc8c18d2..4b92a0b7d285989c60e106eabb1a72d3aeee91b6 100644 GIT binary patch delta 112 zcmaE>{Z?DbKRrE|fr$YML@r11f|%vkWj0Ne(&GW~8G(GoKlP>?L;moB1wg7@ekVWJ x9K&~?RrPLG7fU7+!xByghD4x*91x3eevqCQ&j(~NFgU!l-Id|IIYFeC5ddU)9bEtb delta 112 zcmaE>{Z?DbKRrE|fr$YMbdI`mf|yJWJ7^M*4_Kf&o1A*T)>wZ0A?mG0ssI2 diff --git a/public/dings/senders/libby/message.ogg b/public/dings/senders/libby/message.ogg index d923f60bdd2fad0bc6ecfdebc6e4de6b0f60685b..432f8623c5cd8de934c10644e03c39a185c8888c 100644 GIT binary patch delta 135 zcmcbteo0-*KRrE|fr$YMWKMjx0x>rzyV*^Y(&GW~8G-yqjVkgRL;mneg9JdT_3iWC zNz2NGdpX-!8e5qfx=2ZE&gQ$yB=Wy)W42?!m#H9A7#ilS)xG?vVsohADMk_Z)tW+5 W3=D_N85smXk_Eo(3nm*2F8~1fQzoDQ delta 136 zcmcblepy}0KRrE|fr$YMOe{B7fS89K$f-?~(&GW~8G-y2A2{kahWz1`1_^*vU;qA= zTUu5w+{@X<(%8z>&_zmeb2i^qCeix#jmrZaE`~#ltlGA%=g3E%%^`xP7)7F2YYIsL U%{FIb5CBy4TaPRzE)6Vzyew+Qplo=D88S1{>HWmB&SJ`oDrf=X6 zMwt_Km&AbjzsL)57*9-bG$?M}dvf(OYPfudAtoQ;%)c;xxK0@jJM>2IM57+c;?NQ#ZNH;0FlN-SpWb4 delta 231 zcmbPZJl9ytKRrE|fr$YMcrGr905Q|ohHRTCrN;x}GXnYR92ZaA81jb~EC5pN#kWFl zatxmYW7_6YzLiXj*_-7B)mXWl8Sb5bYub5U{@iANF=fX3V1~MHcTL5<{#16Hx^tPv z9|pb@`IjII{8F0Iw;UT)-ZZoHG0 zx-P2XWq%^`VIiJ#;pg@LZ(Fq}lv(x;ANftbBYuY)H_>G6R0j6nV_LxYnWL;moB1wg8^-ygiW JIfgHq8vubE6r2D6 diff --git a/public/dings/senders/nibu/mention.ogg b/public/dings/senders/nibu/mention.ogg index 6a065e39d171638f3e009678c6c921e66bd82a59..5f6d2dfc2d20d5310cf4b084dce302400d795bcf 100644 GIT binary patch delta 267 zcmexp@XjI!B3EvCiG zcYKL#J;S;#C3>n5(^qDO9kW2Dacur6xPXzdWb!Ow4Wp>5F`F7*>&ISQ((JwW+qApB zE1rm2wr#hXZ{{nQ-xMI0?O48g`%MX5eg*?Wpu@K*GHm`VoW{g4IrR0novVNR-JB>U GzytuTlWHdb delta 267 zcmexp@XG6R0j6i-~rI+2tkUzZQAOVo-M{kbk ziO84bWEG^Orlu!vPUov*=JefK`+eu;KmR%>zZ3Lhf5OJV;4qtcvWw7J=9>B)n?;1T zF~&zT?Ad>Oedq7~U|&OB1_z;|TUddnHP=@$97*sI_4(PmIgXL(pj=E5TXpW`6bnD*ylh delta 216 zcmbQIGhau_KRrE|fr$YM)_h#(0AgNSb;5I^lpYU=&j{pix!g2)W5^#~agYE=b&!wb z84>xioUDSB)YSBp&FOq~%$&_xzuy(7|NPrK`JJF2N6KcPx?Dbn$<9J&neW)gY!(*Y z#u(quP_ch|yzqDa<+GBa7#dW%GP!}u*#3WE=r}k-YsM$(^u0_hO}0Cp@O-Q3?1ypE!{O482%mmvp?1G|E;z0SMsH@=SMb=Keoc2sEO>^Fpows|6 z`8RhQS6*%VYQO!9_qDh6%#OW}TorS!7$md!$m7JV%=1NBSsCgy>omd{J8HkrZYy7L z@`^|}(Di>(s+bO_)UyJuIS?FO-*oTKYbkD%Af}1`{@7@*i%8w)-mY;pWw!opom$tI zuTy9HOD&8MjjElk&nRcPFG8GgiT&~X2eW%-J>p{6P+$1H7~*?|-_rh)3g@nr0J#&K zx^&moKDK0Kn8&eCYdOP{^PG1+w9YEz6o9H_V4Gi`&t?aV^d?T>>=%2qXR@4#&t3wO zjk{5+wHv78WA^87KrO5?4=$@hbUFMlXIml&VrRO3-SjbybA$N3_S%oT8D5?5w5a?I z3_?~035ZgLJzE)mv4hwP7NmqT2bjgB*M3~hu;M%CoISM*m(37kV5kSH{BO+MaAK8O z^i>OnQ|t3ULifz>&e8u9SZ!i{R~nHnH|&>T6fk-kc0Fz#9~(p5w?C)F_pK6M?EKfs z*U%Si5(5LWuKcjoBtseWtN z;`z7hm^Z?pG7g4@7iXQg7{N+3n#`}si3RLGv>cckp;lD_lN{SVsh3GCASE?gcLH~d ZU(vrJa+#sH-xiY43U2RLe!enL762&Cd7S_N delta 1643 zcmYLIdpy%?7=O16C6hF_)+Co)lG~J|Hup}EOXPkjx6|nGkwwOSKMEaPoGwaJTBjPy zrIankkcy+w$*r`x9E%QVg@|T5zg3^ldH#4m@AJIR^ZmZ>^L*d)1`HEz&)C>~01bYc z*yWc5*_JljcJG|_=9MxZh4{n^AzJr+#mX+SfGivCywiwZx7WnP0k5fRXn5b(z`#H+ z%F5F63M*S*SAWBXl|H$RI-<PFU`xK~w#<23(~N_10c=o=PYoTcZGv@?es4Hl*{m zC^t#OcI9A(NZn^v?gjBlk=q&JOnNLPLJbpB4k~TR8?hM3{sZ<;LgJGOL|G2%{ew7K9f7waf@fMI- zU`>5U*43h_8D^jzH@@7?MNe3S3ER44%4KHGq!PapOiGPckO0{?8foCC$pQenho`qA zLn06Kt3YhUR9A04JnowE5rio$ zk*b;DQv_uJ&6a5Ml<0tfKnSR292UUR>1OTSNPy5UZlvn+1~Q1rBCImj!^6_!```d< zyBv^t_oLO2Bnev#Sz)Ac^Isj7EIaEVQ0enLaT)%1(zM`J7J@dwaH5jJB+U z@su)mC2gCeY9O&4l-EE#60~A?hrAJrHHq~3>kNclX3%MjMdVi=a=G>9370Aa?_={i z_v#NC@q&U;w;E_oISDH$d8VL=jXa9L-eq=({MizPQ$La{qD3Hz?|=(%~^dpM*4$ z1(Nw}H-`gJ(guHp+`qS2bEYtaXi^M*L6qBgo|&My__k3}phxYA&Hpe?f6-OD5tW@lu@zSiTlMtaazIJB~ z%P1XyXw%Q_?kF(Rp%0q6GM0kq?r8RjCMgBW^C+b%h8s9%vFhv|R+jO<;1g6|Xu%sp z)Sx@G1tGJX5FdYUm98|i#_V@9mUibvc}}&jN7H5Nd*LoBA2^(??Av;UW8*5Oi`2Tv zSIx?>&S_nKAqNc~jy-F-f66K%T^GPpmo4gw5g`xuif^jVom3lR z@ovQIQv0C|cpOoc9QSbYfwd@5TqBOe6?5k_-6#Bs!T*02i$^%1Xjp#JMjcU1jaUOG zDVft`(r>T|4S%43Pz*Rx|84Z38X~H}8os^yoF8r2dCAekavh)F`RzxRm);>Iq5Li! F_z!jE#XA51 diff --git a/public/dings/senders/void/invite.ogg b/public/dings/senders/void/invite.ogg index 0b344456aa20b0f0bbc8807728512440a794c415..ed46cbb91d5dd46845c139731487738547de0926 100644 GIT binary patch delta 1433 zcmYjRX;4#F6h1F3hM+_R$`Zj;WfKKz1Cb?*;z&kNz&N5}SzJcNg@LGvRuT##7Eu%w z6a>^_L~KA5QIsXxvB7EyHi#Grp@5)*21;!xNnZLMFxv0ex%YhMoNvB6=l-KFk z&D=0cceuVBV?c@8s>p<4dEsiXWPa5#aeAqQ&nZxpi_@lQS8xKd%lcZji9Y_d_8v{a zE07-zO_CQETI96(n>kycedHGOBXipA?+5B4xnb6LHvo3i2h*_GGnc$95-_ahJQ#|k z3-1|Nn|pT7XEkEueIL^v;!Xj)WhrMu_mO`?+{VIt3Ba6Y zV9m22*~`4kW6K>Brjs*=DP92a8K}lwxyE}Dg0|~fw=Ttga5nkGuqnipQ7387uH^&h z29p&e3xKX#y{PHHL!(j>+m={kg-x6s2%V-t?QiegWvmIh;=HWX*FQEs)H;kUYq!+8PuTw#Hs(s2dq>Es)iaY2EgRh#`aqjUnSPs%2}G zu^WbrgofRs_-Z2~uBwHKFgsMEB0j>YR0XL280(RER06cJV$MLWEiQKaWByFaNOW{S z{R!@=b6rcBPyF3~JIrcQZ%inCky{V*CJ9bzJN$-z=TRiK&gV;5ZV1oVUJ z+3K?ZM2|kP*o_}alIq9zsPGher+(j%O{$Lfel)G>V(YiQy(AF~2Ow{iv-Xx@`P33N z`JeI{ZPy*Py1~;veq;THz*CEd#3fFL*3}0xF+5Q>iHxX0r`3E8MkX*fC+F~y3Fz@0 z{b16cDAGub8Oj7@kAUgi85_Bx95*-*#)eHv6GATxX4(2&b6oXddGaICNy48`8%HfRUYHvTS zL4+NuiYVmh(ys-=-4Wc)n2(H+T_%$SJzp&VX{e42@UWu#iZ!n#h}R4v^cq7_1kY0R z!JjIZtxhrnsK*#Mx+RL~Ablgp!bZhi+q+&bJ&{Jfs)CN39T+2b`I}<)rJ7yB9uvo~ zCInzTHjwzr@y6l(=|v{Mq%(eb0Dh$mT6b}91lNW+$%Jl2v^AWM%V>RikS9V~cOQW+ za|6T2BF>j@bdv=}Wqny>DVGP*quGh$y}l|M#I)-fJ|yaMzWVn{&Cld7`;yAs4s6oZ z<=2~wE=Lj2JNjY`;igu_Tav<~lzWJ=Rhz)YaBC<#Zgb0!E!)ol=UTmY^mfzJ6{^@1 zZHbj-3;)-z+8o#mwvE4zsx!fPaxX%fRM{}&J^dx;viQ8~RKJ@ckji$?(QIr(aMnPi X;72FyPw)^==95?Ts%7W-GF$i$2&YiB delta 2070 zcmYLKdpy&7AOCJ9LsFq!rX!NZhl7=a{Jh>L}IGVX+zj0!SB!v=&LqoZ& zSXr}qL@Ae@+?E=-L^;$-jk57<>3N>dU*FI7^Zs03pKFa_Iab>>Bm@uO;3v&$t|iN! zFBrR?2t` z(P*>*>i&U&mG=?>l7^6(133Bm0o+i=+^gkFO2A5sY`2_!g0mXm`$`(oL{X?(qaRUN*y5oQE+h@S>3#;&|K8E@?@*tslXYB!X}`F{wysYW0R?sI z&3eU-1)K~CItK9wW|b}f?vJQ=_?88+eHoX)s-Y#Qrc_oIppmP^i8{LjaLL=NFxy~Y zxkJo>g7#f#^j2slQP=!{t)eJ>av&|vu&7iWfR-ll0ql7;$&~IV?FD&|c39TGpB}=% zvntudzzR@rFJS#jF#xAswT!czl`9TeO`YV5kI=5d`aR*Bh8s&MNra6 zBd4B!LGJoyUC_c81b={RPU{LDRD8-BnYxRq1%Su#%mG(%G8YHG%sZqxpq!3mk#7Bt zH7-71P?b8L{V|q#htE>dVj)y;^{j~eQ%VdA?*SDu(vCrZJ(y!XxfrQ?Hp3Wz@xCRe zpJVs{T0*{w^LYw@|K$7xQgrP+PbCUQe#fR0AlQ9@OsATTF@dd)766pi?mUo-0RG8} zI(b387z^;yy^wiX8q+e9ebO8Db`7Z{7tiUG>8vJ98u&`HuD{gZwiI!cV5iL?U>-NH zQ6}{b*(q1^6v>a+_af(dOtl69P=;=R=Ddbvx=9Tj0t=_+u@6$(YRnZ*Xfeml$+vOaRF%IIIia+Gcz}~*RKqh zL;nEn3k#;ji$0&JfH6~=@K+0Cs6W#fa1DT+&AysD+1eOD@gAGSXU2xQ>-R*aq@`nT z*DL#Y8U;?>wP;GK?0FvgS@DR;UD{DkjhPv&as~S|v#8?3xq~Hvxt1yoyv&@$%U*f$rQpyT*c(zM1V?w0$J`n{-xzTW_F{a|?jrpH)b<{fGr zV~0Id@a5Z@Tf*)SnY7Lq+WlwN5r-`GPho zcvM_YAyw10JOSX28m{g%ZsRRi1@NF#x(W5}rrW3d3sdF@Cx;LRjouRTys0_OX~Sbm z0Gu+%o$zM-Sq-Puex_>CV6KPN4Zgzl)w;q28&9g%h3)&|ge_v06JBi}hclOE7N)nU zLuWKuTvij7svn(Cqi7Lc6JcbJ zhc6Zf2W`TmndxslYaNd~K7T36jA(;OBA<%P!O2C5HY3y@TR$WA$2Kt4h<{b0=gR9N zKnptS1|B?=FrCYCV@ietP6A4X1{=zHrVeJ`#f8B^&bP^q-$LHozyN>L#^c6F(*7Z! z{QO{9n6JaIe_ZEG*5^Zv@+{w*`SbJhM6#d@g)0c~+oL$3xo)O<*rK&16x`X>8@1%} zrl*Bk0lNNxMBS-+*tyE1O|i{03soLz_5mM8CD!s2v`dd|_Li|r?l z*M|U@Yep3gd-J6StWjJT2Crud63WQYI{!am;8qC&Agiv-h1gU(!nZVuO;6(yS3FrI zso%S=4<7&D2?Ls80PJ@Nwr}px1ya~;4+Nr=&n_iL%lE5G9j%fG3DzUuezH}zl3}LZ VV$*eh?8sGV!HY$aCi}St_z!*fiQ)hN diff --git a/public/dings/senders/void/mention.ogg b/public/dings/senders/void/mention.ogg index 80ec43e39c6f8d7d48bf87f1aabd5dd4340e4278..b6295e00cd637181fe9814c5369e34c7b224fa5b 100644 GIT binary patch delta 653 zcmbQIeo)R$u>G6R0j6i<$kEZh*L;mpEfdoLRb5Aj; ziA(uvX{xEHs3}LGkAL;VoIQJ{@tgg%^h!-Zjmk?u^Rf0Z2@h9@Tue;RdgN)IEsJT8rnjzto-phCippt8U_{ERJAAimMbvB==>qAzE zk{jz88zvN1miq%;&hYQppZ%!^|KGB$3%H*d5G)c0QPc2`lcBz$z;=DVG(!U$!@X@l zRf_*^#oDj9U$O$|F^H-O`RoiE_-u0)1KqQ4+aLR70`YIx>Ay0UVlzEz0*MQTM`DZ& zN^j5B0=Xblk{8-;t=<#3|0R;w*RV2h)St6t2l~7YXiB=H(C+&S>K2MFo5A*N_x&13 OAU0ICDL$3jr2zo&i0U{1 delta 1117 zcmcbpJWpN9KRrE|fr$YMSnf3mt);HE*;Z8*D4?vYs;H#M6V`se|9j(t8Q(-5fLb~s%+WwC zByXSF_G|U+b7%ao@7rG{YwC5o{{OAgD=QYfe;+?PaB;zWh9!0X|Jv-m=)eio3)KCb zp``MLOIV7l{(c$OElvOXPru6L*zxb}T(C4l@b9{~I-V(Y|9{T9S|2H;k>~FWk+^m; z^F`vE!WxE@x_W!Q%@-Y^=Dp{h(=WVfqUhc~o7m>m0ge0iR)=Ywz1>z8us$zSLoF*A zhB;OLf7gLE0W89uz1*kqahr{HSQ8~s@?Jtk^1 zIiBAVyl%^0)hSi&zg9dw@XKC};WI-+mto)a{xAD?yePdg`$3+`lIwGfa$nDvQTf%L zTX1smi3r2cVCzX@It-4tO=T8`D>D2#JAHSh+JTZezv{1Dwl-e2Z%^SDUv~M52dDm5 z7nFy`%{S5dx-0wQPc~=Y4F86>lDtu}T3uC>mxm`=PH9PTG z^ZDtOT+>yWx{hr0PxK6ZUOR2_E~)8FzLN|j0@jLzhcY&tvdPciuAqH&gMm}6vjFeI zWZf^fw2zn`STcKiedvM&z2sT_^Au{e)AJT`q;y<)*KjX4Jy z0~AGoA-dteKXb!`$i+9j8Rx8KX7GA)ZBoO|g3Mg0ur@s|2?@GG^STvau;J@xaa=Ye%FHdw{pyP>wgDuE(9urs5|>TQ4QH!1wHlCxiNUkT(Kh)p-*`56wx ztiQe%$p5$XkGS}g_}$m}Uu|bFb$!SRQL^KoK10GO?Ycjg85m4Jil&R6iQe6B{z^P6 zyekvp8iqIX85vT-Ozb2X80G zE+XZprKPH>qN1v%5@o8dudAV{s9+Lx{lDFrrUey`Lzx+XS`+kMe*I*1{YrpKjdWfyzf%%^kwk`92S2EYl_*=iI)9sDj!fJOW)1xK_ zK`vlmc#(fwfZ@WnKVIrTRv%^Dm;P5iilwoz#-Y~v>;yHi3WkRITO|w*77pK)*@}!~ z80yUb-d}V;{J8SMeeKRGBrmblLrhZe3uR(>Q-8^9)dU8HI`+T&Qyur;TFZQQx2(*p zSxK`XW-(M&F)$dOKel-a(3x`A|Ljja`2SXH{FQvGDcl3+fh^uFzb@2~jp-IXi=ZZHn1Zko|Hi_VVrLT7LL$yYD}@In!o6 zN>rZr|84v_bJZ8o!i)@Cg@Agd>IqeCJA32x!d=qV$)(fZWiQop+kE|DvDyi-!@GSN zldi7umD1D*b_?28tQYuA@7C)Nj;^WJ0{lJ5|2~cL*^! z|cu`e>3v81kAqC$P~vK>qefA;6|LG(7%sw=W0d%Qx30Y zm~P7^*YWpRnBD&J{bv66I|A#2eYipL3=9tYCJWjya=g^9`?y?4BYj@B7)Ufu{qOy( zhxS|9Tb#e2OAZM35eA6@&HHZJz{J2k&pz9W;kmpg@4aV#e-dJ-+xjP0^jv&3%ZhkVbQnRD9r!88pzvtn9amLGhIw26 fD04qwcQ<=M-NI!v*nYfoPl70#Fi-B@mD7v>K*lb4 delta 1603 zcmX|Adpy%?7=M3bZfojfpIjRiYJ@b(T-scsa*(4TmsyUiC?faRZ{4V*yD%kL;)n_% z$*iM}Ri8LwMcG!IlVf7!?(A~5^f}L8@Av(FpYQv8-sgEftow#D@T1WJ00R7o=_u(> z%40$>GT^d-^)e-f6u95N$G3Km1ie#vp;XgOp1XuK_O-BZGt@INH?Jd`nVIk3YHPK{ zP+#9@1Fn{CW`YUKX~T+DTy4iJfx?6OfnQfSoq`(5+bT#KvcGyxn+3n?lM+3i#f+Wz zoXcjG9Z^#!DIX)r{jzjWJ+!!VW`XXmjM18Q?L?=?Mi=$$wPdpVi?1NzA;6BQA8oo2)>$sjNfbAF zHX((?p1ftgASvZnCuT&c>^CjWL%=B(ByzoN?p^LNP9JqN8c3RKVa>jdo401B+NTl3 z)CX>Ogy);WRT}{>xA&I@H^5C$W4wglBqmQKO&qrjqR2DUjUS8TE(iD{eVdqhL%0jh z@%*ZUF{3$K?mv9ArG?KuN=qyi4(`=w4FGmeS)>X?F8ii%Ogp5s=DWDL(MidrJu1eI`pqD!{=A zqNIPVDWjF@LmRB@ctku_p^@GdE~t+hndZ{exa+3TiGF_AwWeVoI`hT{Ad0S3SY{$~ z#G+@MjF`+wW+?!$vMcB9)d2@ihtcH?A*Mo)U*Be-3?EdG=P8vjQPPTx0QgZr%*jb!zLf=k1=d|G0Q^_ z%|5?u;&ef^^w1rJI!)t7N0ST|Hk+Ouc+lL;u6C`OyI&MG_&H&kK<*FM#;BJSz%CYtVD+WK?c9I9+v3;0`+JJ0ZNZ=s&VvXP|e5I?zSK_Rd5(<+r(@(b5IK$q5W9e`AG~X-&dJ=YV7qm z@+FcQt{GS;^$&umZ5L5pi`vHa?jf9|!nAYRh3iZGT|NoDy3*fGz}zDIR8Nmh4K{Qa zPGR?R9FdL9V4YmKREGrzjh)$X8nc;BSd|$k&!9h6Ta6J`seWfo(*JhfJ`@ZxW8`$= zw42w9wu-Lr1fx2KXd`x1agM%4ycPYwnK2lyTr@AF{-IAZGK`9=NH2(^AKC{~k33)^&qonzMqk0G-(>#-Hq?=qK8_=*l~)f zSFQ_UGqtdhENy>Q(UzO;Cq($*(Iz~4hf#NrbA5~k`<(PBW_cCZs`~|TBt?erq*rTF&oIv7 z0liTobbY9St>%lN8N-C&m~#hz4AjIB1Q`4I{qbA?WDSy3i?8HkdcBGz>S16B9ZRDT MCZ6Qa=IbK=1Io+28UO$Q diff --git a/public/dings/senders/weksa/dm.ogg b/public/dings/senders/weksa/dm.ogg index 3eb3fa88c8d4444a1f853ac52e7ced92a8f87634..37b5f990d09f611b962c35b0e5984ebf1f168a91 100644 GIT binary patch delta 148 zcmdm_vsp*VKRrE|fr$YMjwnwS2QhV?ESWV?N{F?$3?&N6gC#A1k65^MpsGz8!uHBiElo%4?>tJnRVz9Z9?>(b_zp1Me7sCdi z&NB=TLHd8qW@LC^0%W{rVz4nc1=(Tn|M(;j-7x*x=7j>&Sp)3%?7aEf?b`MqoxyMG z_FtZ*w2&=dq nUtbq=mjEql>;*aX#T^D_pmfrX%u6ghKt@KbS|-Z@c@+i#JF?$3?&4_eC#A1k65^MpsGz8!uHBiElo%4?>tJnWqPMw`?>(d8`B_SetPBxA zqtYCt7)n4UEZ8r}z~INs!0F_TSI+ofyFI!fOpsk$Qs(!;hEmSXxdqFmSM1ft(1`IcFV%9guzn;(VCLD`!nD6 uW7eA;Eb;uiJa#VYFxhaC(_se>P^#nMGEZS3y%#8F#rH4!*o~x diff --git a/public/dings/senders/weksa/mention.ogg b/public/dings/senders/weksa/mention.ogg index 148a6aca49b402a19a33f1757f759a1e6c38d553..98fa0f7507bb4695057a2603e27a0fb8268b90a4 100644 GIT binary patch delta 301 zcmZ3jxkgjUKRrE|fr$YM&OCc41Y*AK`1@v}lpYU=&j{o;p6lz z^1q6nZgZ<+V|c*Fz>pwscuXRhC4qZ``ntJOH|O5omV3MH!SAMP&!n2(|K9ih_cmR| z12qw^-u`CVti~V4#Qslg?z;Sg{F9pnef9TcxjAt$Yydjt8xw~((DVh{fY#)*GBA8# zV@SyNTMM!=rfwTEh@LO8`M;n&qxl_ue#ZT3zjJx~{__2;GO?SsMpGz=p}}&;Zcc|C fAcgOR85rz{H3<`!D`+$Bwp|QDC*o+YX)8uaB delta 299 zcmZ3Zxmr`oKRrE|fr$YMo;WfkftVFHTpcG$>G6R0j6gnjd`soVkUzWvAOVnSg$L{R zNy@3^OitiSOW5_{%l7;0A4UexaqXY&Z})Yj|J{AIZcGdjK&{rM3cer{85kbvXWD;x zdMdZ~4-dluSq6p!^9+wkB(pd$emk;tbJ4ck+dw2x6Mx}WjPr-u``>HtZ&PLX;QirS zE#qc&{xBx?dt$Qs`3J=(w+Q;`+nFjWvVzU8W^CaG8XgcW%+RnM=&}lCh7IP+!$215 z+201ay}_SnlaM{5xy`n6#{I{B=RUde{=$0q$H^yWDFMxx(Y$*$1K5ZUc8m-Ul!5Mg VXRQp>*YH*vNc1pmE)zCm1OOHDWzGNq diff --git a/public/dings/senders/weksa/message.ogg b/public/dings/senders/weksa/message.ogg index 2e678ea6c91affc50dbba2b0c5ba43ab90215b08..f552996b8ee1c137cfe304f25badcdc6f2c1c34c 100644 GIT binary patch delta 202 zcmbQCIYU#*KRrE|fr$YM_L#2l1u^9+e`ZgV(&GW~8G-!E2WlHPhWz0L3xHH}m^|p2 z9K-inc{Y%rNSuM;fMCPp$Bzq^xUUCE_uQ^e2hk3x3A?y97x5c0%GOEs%~DciWrzT3 z5K@Q%N;Z@O_4NU{Nnw)@3M8n=2L`JwVPGgO*v=3Dl3u9Az#yZ-z>pv}! = { + 'aqua/mention': 'cedb63552008', + 'aqua/dm': '5b996d01c770', + 'aqua/message': '8be19ccd505a', + 'aqua/invite': '462e63687f0b', + 'eureka/mention': '7afe0bcc90d0', + 'eureka/dm': '3909d70b0d45', + 'eureka/message': '0b466cd4b437', + 'eureka/invite': '837e1bbbdacf', + 'heimdall/mention': '36051569eb2c', + 'heimdall/dm': '26ebc4158d51', + 'heimdall/message': 'c383d6ce3da0', + 'heimdall/invite': 'fd47e9a97bb2', + 'kiko/mention': 'e73d2d586f4e', + 'kiko/dm': 'b3e39946ab70', + 'kiko/message': '6730b9df5bfa', + 'kiko/invite': '1a3c1d118b9e', + 'libby/mention': 'eb9f3ac60b02', + 'libby/dm': '77dfd01cc3ca', + 'libby/message': '257b84a114a2', + 'libby/invite': '8ef7e91100b8', + 'nibu/mention': '56484d731df3', + 'nibu/dm': '573f5db4de4b', + 'nibu/message': '30f6a5c9ce54', + 'nibu/invite': 'bb36c0975f35', + 'void/mention': 'dee69f4af562', + 'void/dm': '92e3ebbc0b29', + 'void/message': 'fadc5556b681', + 'void/invite': 'd602121389d8', + 'weksa/mention': 'db46a36ab3c5', + 'weksa/dm': 'efe993382a46', + 'weksa/message': '00e323635949', + 'weksa/invite': '9bf03a36758b', +}; const MOTIFS: Motif[] = ['mention', 'dm', 'message', 'invite']; const VOICES = ['aqua', 'eureka', 'heimdall', 'kiko', 'libby', 'nibu', 'void', 'weksa']; @@ -37,7 +72,7 @@ describe('dings switch', () => { }); }); -describe('v3 manifest against the parser', () => { +describe('v4 manifest against the parser', () => { it('survives parseManifest whole: no path or motif is dropped', () => { expect(manifest.neutral).toEqual(raw.neutral); expect(Object.keys(manifest.senders).sort()).toEqual(VOICES); @@ -72,17 +107,11 @@ describe('v3 manifest against the parser', () => { }); }); - it("Nibu's four files are AquaSynth's proposals/nibu3 (sha256 prefix)", () => { - const want: Record = { - mention: '3833f504903f', - dm: 'ad4839400f2c', - message: '6ec6fc893b94', - invite: 'aefb6ff79c42', - }; - MOTIFS.forEach((m) => { - const hash = createHash('sha256').update(readFileSync(join(DINGS, `senders/nibu/${m}.ogg`))); - expect(hash.digest('hex').slice(0, 12)).toBe(want[m]); - }); + it("every voice file is Aqua's v4 render (sha256 prefix)", () => { + const hash = (file: string) => + createHash('sha256').update(readFileSync(join(DINGS, file))).digest('hex').slice(0, 12); + expect(Object.keys(V4_SHA).length).toBe(VOICES.length * MOTIFS.length); + Object.entries(V4_SHA).forEach(([k, want]) => expect(hash(`senders/${k}.ogg`)).toBe(want)); }); it('keeps the clip lengths the design assumes: message under 400 ms, nothing over 600 ms', () => { From 14ea0dbab0ddd0db10bf1db086f5c92a87c0a7ec Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 18:00:41 +0000 Subject: [PATCH 31/56] scene: skyLabelInk leaves sceneShell.css.ts (vanilla-extract refuses a function export; vite build failed on dev) #74 exported a function from a .css.ts file. vitest's plugin tolerates it, vite build's compile does not ("Invalid exports"), so dev did not build. The helper and its custom-property name move to skyLabelInk.ts; a test compiles the three .css.ts files that use it the build's way and fails on the old arrangement. Co-Authored-By: Claude Sonnet 5.5 --- src/app/components/nav/styles.css.ts | 3 ++- src/app/features/room-nav/styles.css.ts | 3 ++- src/app/features/room/RoomTimeline.tsx | 2 +- src/app/features/scene/cssModules.test.ts | 18 ++++++++++++++++++ src/app/features/scene/sceneShell.css.ts | 7 ------- src/app/features/scene/skyLabelInk.ts | 7 +++++++ src/app/pages/ThemeManager.tsx | 2 +- 7 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 src/app/features/scene/cssModules.test.ts create mode 100644 src/app/features/scene/skyLabelInk.ts diff --git a/src/app/components/nav/styles.css.ts b/src/app/components/nav/styles.css.ts index 6d1a6691..62e3928e 100644 --- a/src/app/components/nav/styles.css.ts +++ b/src/app/components/nav/styles.css.ts @@ -1,7 +1,8 @@ import { ComplexStyleRule, StyleRule, createVar, globalStyle, style } from '@vanilla-extract/css'; import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; import { ContainerColor, DefaultReset, Disabled, RadiiVariant, color, config, toRem } from 'folds'; -import { skyLabelInk, surfaceInk } from '../../features/scene/sceneShell.css'; +import { surfaceInk } from '../../features/scene/sceneShell.css'; +import { skyLabelInk } from '../../features/scene/skyLabelInk'; // Wash strengths over the scene: rows and labels are faint, and hover, selected and pressed rows // are three distinct steps above them. diff --git a/src/app/features/room-nav/styles.css.ts b/src/app/features/room-nav/styles.css.ts index e5ab4f8e..88b2c333 100644 --- a/src/app/features/room-nav/styles.css.ts +++ b/src/app/features/room-nav/styles.css.ts @@ -1,6 +1,7 @@ import { style } from '@vanilla-extract/css'; import { config } from 'folds'; -import { skyLabelInk, surfaceInk } from '../scene/sceneShell.css'; +import { surfaceInk } from '../scene/sceneShell.css'; +import { skyLabelInk } from '../scene/skyLabelInk'; export const CategoryButton = style({ flexGrow: 1, diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx index 552fb04c..6cce595e 100644 --- a/src/app/features/room/RoomTimeline.tsx +++ b/src/app/features/room/RoomTimeline.tsx @@ -124,7 +124,7 @@ import { useRoomCreators } from '../../hooks/useRoomCreators'; import { useRoomPermissions } from '../../hooks/useRoomPermissions'; import { useAccessiblePowerTagColors, useGetMemberPowerTag } from '../../hooks/useMemberPowerTag'; import { useSkyInk, useTheme } from '../../hooks/useTheme'; -import { skyLabelInk } from '../scene/sceneShell.css'; +import { skyLabelInk } from '../scene/skyLabelInk'; import { useRoomCreatorsTag } from '../../hooks/useRoomCreatorsTag'; import { usePowerLevelTags } from '../../hooks/usePowerLevelTags'; diff --git a/src/app/features/scene/cssModules.test.ts b/src/app/features/scene/cssModules.test.ts new file mode 100644 index 00000000..27fd4d4d --- /dev/null +++ b/src/app/features/scene/cssModules.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from 'vitest'; +import { compile, processVanillaFile } from '@vanilla-extract/integration'; + +// vitest imports a .css.ts through the vanilla plugin, which is lenient; `vite build` compiles it with +// vanilla-extract's integration, which refuses a function export. Compile the scene's .css.ts files the build's way. +const FILES = [ + 'src/app/features/scene/sceneShell.css.ts', + 'src/app/features/room-nav/styles.css.ts', + 'src/app/components/nav/styles.css.ts', +]; + +describe('scene .css.ts files, compiled as the production build compiles them', () => { + it.each(FILES)('%s compiles with exports vanilla-extract accepts', async (file) => { + const filePath = `${process.cwd()}/${file}`; + const { source } = await compile({ filePath, cwd: process.cwd(), identOption: 'debug' }); + await expect(processVanillaFile({ source, filePath, identOption: 'debug' })).resolves.toBeTypeOf('string'); + }); +}); diff --git a/src/app/features/scene/sceneShell.css.ts b/src/app/features/scene/sceneShell.css.ts index 3d220e81..18911a1b 100644 --- a/src/app/features/scene/sceneShell.css.ts +++ b/src/app/features/scene/sceneShell.css.ts @@ -28,10 +28,3 @@ globalStyle(':root[data-scene] #root', { const surfaceInkVar = createVar(); globalStyle(':root[data-scene] body', { vars: { [surfaceInkVar]: color.Surface.Container } }); export const surfaceInk = fallbackVar(surfaceInkVar, color.Surface.Container); - -/** - * The sky's label ink (skyInk.ts `labelInk`), set on by the theme manager (ThemeManager.tsx) only where it - * differs from the theme's; unset, `skyLabelInk(fallback)` is the fallback, so a person's own theme keeps its ink. - */ -export const SKY_LABEL_INK = '--sky-label-ink'; -export const skyLabelInk = (fallback: string) => `var(${SKY_LABEL_INK}, ${fallback})`; diff --git a/src/app/features/scene/skyLabelInk.ts b/src/app/features/scene/skyLabelInk.ts new file mode 100644 index 00000000..03b9e660 --- /dev/null +++ b/src/app/features/scene/skyLabelInk.ts @@ -0,0 +1,7 @@ +/** + * The sky's label ink (skyInk.ts `labelInk`), set on by the theme manager (ThemeManager.tsx) only where it + * differs from the theme's; unset, `skyLabelInk(fallback)` is the fallback, so a person's own theme keeps its ink. + * Not in sceneShell.css.ts: vanilla-extract refuses a function exported from a .css.ts file. + */ +export const SKY_LABEL_INK = '--sky-label-ink'; +export const skyLabelInk = (fallback: string) => `var(${SKY_LABEL_INK}, ${fallback})`; diff --git a/src/app/pages/ThemeManager.tsx b/src/app/pages/ThemeManager.tsx index 980d35b7..092ace6c 100644 --- a/src/app/pages/ThemeManager.tsx +++ b/src/app/pages/ThemeManager.tsx @@ -11,7 +11,7 @@ import { import { useSetting } from '../state/hooks/settings'; import { settingsAtom } from '../state/settings'; import { labelInk } from '../features/scene/skyInk'; -import { SKY_LABEL_INK } from '../features/scene/sceneShell.css'; +import { SKY_LABEL_INK } from '../features/scene/skyLabelInk'; export function UnAuthRouteThemeManager() { // Signed-out pages are Midnight Sky unless the sky state picks otherwise; they ignore the From 5def1bdfb8fc25cab55260c79647e487f7190ddf Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 7 Nov 2026 09:48:58 +0000 Subject: [PATCH 32/56] docs: Mind feedback pages spec (placement, close, hiding, receiver) Folds Kiko's spec with Heimdall's draft rulings on guests, erase and hiding. Design only; no code. Co-Authored-By: Claude Sonnet 5.5 --- docs/mind-feedback-pages.md | 186 ++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 docs/mind-feedback-pages.md diff --git a/docs/mind-feedback-pages.md b/docs/mind-feedback-pages.md new file mode 100644 index 00000000..562efad4 --- /dev/null +++ b/docs/mind-feedback-pages.md @@ -0,0 +1,186 @@ +# Mind feedback pages + +Design only. No code exists for this; nothing here is built. Drafted by Kiko Self (2026-11-07) with Heimdall's draft rulings +on guests and hiding; placement ruled for this repo by Void. Follow-up `eureka-body:follow_up:ratatoskr-mind-feedback-pages`. +Every number below is provisional. + +Each Mind's section in the estate navigator opens on that Mind's **feedback page**: the Mind's open questions with options, its +recommendation and what each waits on, answerable by the operator and anyone with an account. It replaces the old Fork Desk +(taken from the brief; no trace of it was found in this repo or in the Minds' state). + +## 1. Placement + +Each Mind's navigator section **opens on its feedback page**, rooms below it. A cross-Mind list of everything waiting on a person +is wanted later and is not part of this spec. On phones the page folds each question's options and the answer form behind its title, +so the section root stays a list of questions, not a wall of forms. + +## 2. Authority + +| Concern | Owner | The page's part | +|---|---|---| +| The question | a typed `question` document in the **owning Mind's own mind** | a view of it; the page stores and edits nothing | +| Which questions appear | the owning Mind, by a typed opt-in act carrying an `audience` | shows only what was opted in | +| An answer | its author, as an append-only record | submits one record per answer | +| The ruling | the owning Mind, recorded by her | **never writes one**; links to it once she has | +| Status (open, answered, closed) | derived in the Mind from its documents, never written | shows it | +| Who answered, and as what | the Bifrost account behind the Heimdall session, plus its role at the moment of the answer | shows it; never takes it from the client | +| Close | a request the owning Mind honours | offers Close only to a grant holder; **the page writes no ruling** | +| Hiding | the owning Mind alone, as a typed act | shows the labelled stub; hides nothing itself | + +An answer is input to a Mind, not a vote and not a decision. **The page never tallies**: no counts, no totals, no "leading" option, no +ordering by popularity, no count of reports or close requests. Only the Mind's ruling picks anything. + +## 3. Receiver and carriers + +- **Bifrost is the receiver** (Heimdall's side). It verifies the Heimdall session, reads the account, stamps `answerer`, `kind` and `time`, + appends the record, delivers it to the owning Mind, and serves the projection the page reads. The client writes no record that carries + identity; a Matrix sender is a different identity authority and never becomes `answerer`. +- **Huginn is RUDP-only.** The client never talks to Huginn. The Mind's questions reach the page as a projection Bifrost serves. +- **The Huginn kinds this needs are Eureka's:** the opt-in act with `audience`, `closeGrants` on a question, the close request, the hide and + unhide acts, and the redaction record. This spec names what it needs and proposes no kinds. `huginn-feedback-kinds-proposal.md` was not in + `/opt/commons/handoff` on 2026-11-07; the kinds wait on it. + +## 4. Mock + +``` +Kiko > Feedback [synced 2 min ago] +-------------------------------------------------------------------------------------- +Open questions [ Open ] [ Settled ] + +OPEN | asked 6 Nov 2026 09:12 | waiting 1 day +Which source feeds audio_spectrum? [ Details v ] +The visualizer contract assumes a host-side spectrum ... (3 lines, then "Show more") +Waits on: "Overlay-wide effect budget" (open, links to that question) + + ( ) A Overlay's own audio only + ( ) B OBS captured stream Kiko recommends this + ( ) C Loopback of the mix, microphone excluded + ( ) Something else: [ up to 2000 characters ] + Answers are visible to everyone who can see this question. + [ Send answer ] + [ Ask Kiko to close this question ] <- only if you hold the close grant on it + +Answers (every one, oldest first) + Meta operator B OBS captured stream 6 Nov 21:04 + Aqua Mind B OBS captured stream 6 Nov 21:40 + Nibu member C Loopback of the mix 7 Nov 08:12 + Heimdall Mind "Either, if latency is measured first." 7 Nov 09:02 + a guest guest A Overlay's own audio only 7 Nov 09:15 + (answer hidden) guest Hidden by Kiko, 7 Nov, spam + Earlier answers by the same person fold under their latest. +-------------------------------------------------------------------------------------- +SETTLED | Kiko ruled: "..." (link to the ruling) | 5 Nov +``` + +- Each row is the author's name and **kind**, the choice or text, and the time. Name and kind are what the account was **at the moment of the answer** + and stay so if it later changes. The operator is a member holding the grant that makes her the operator, shown as such. +- The recommendation is stated in words, not colour, and is never preselected. +- Answering a settled question is not offered. + +## 5. Data contract + +Typed, closed, versioned by name. Logical shapes; the typed interface and its fixtures come first (section 9). + +**Question** (`org.gamecult.feedback.question.v0`), projected read-only from the owning Mind's `question` document: + +| Field | Meaning | Source | +|---|---|---| +| `id` | `:question: