From 91e93e6887ecd777f0ac5fe3cd0dff4540be286b Mon Sep 17 00:00:00 2001 From: Nibu Date: Tue, 10 Nov 2026 01:18:45 +0000 Subject: [PATCH 01/32] 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 9877154033f65a14002952a86e075f39169e4adc Mon Sep 17 00:00:00 2001 From: Void Date: Tue, 10 Nov 2026 17:03:17 +0000 Subject: [PATCH 02/32] 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 zcmb7Id03Oj)}J7|h*1!MA_hbyTo8gp!AhlpC`8skK){F)QiKq#gt+m6Q33^uh!_w; zRImiZOGLc{MT>+giwI#!6Lygz;J#e1EY+GjZ=!wd_s4hdH_tFR@0mGgX3m`7nKJ~$ z#RUTm@Tr7+H4ui7D{bqyqbyMe_9ex{vk(S~a|`(a;B^2>^?L&qfJpvX5J{xR-)rNy zizAd@mQxGVdR$=lj`;8QtP4nr!z9MXgsQY-d@#;V&Q4Anom?=sWO^){7QZ(RL)?2H zK55@xCVg)*7E!wt{t^8>1H6c2cPPr*8~a^+A|0ir*1U$`8HxhB0C22*P49Ag@+knI z0Vu24Sd`MSev&cd$lF|-s6Sb6YpI3`4Hf-!{T9N zPYmANc6xKS5x!gI(q-fq)*-SyH!YL88=bLco$@J4#y0N_zM#sgD6) zsHP4tE!7cK_d%Z7w7{^{EE6*q*a^{JGyq}96w z(q^}DD3Lu>%4pD|LTWx%@Ulw};lX)7HF_%d@qp(RJz&~;_BbXrPdmogs?zGTW%5bG z(`C4JY=z~Cysf0_^gy!NT4C~W)y^>haM53Su~nvNOqNxZAW7fuVnsoYU5D*II-T#^ zBTda6>858_?w~ylaCLnod%Qf9c0xRLj&<41n;{e{BK{_X4vrP75d{ww6Py37wwVea znZ-#E6=lcuzdX3tHr-2B^nOvh-&jCrR{OKcnJHlhnBiyG!`CyY1MIQ>)Uo~(QOh&w z;YEL`>aD|_00d4dIPzk0aczQhe}bO+fIlH;+kWG=Qx^A5*=)aN71*X;e?s`u#L3!_6aaru`A0DvUlU^@~@Y=X!Raxz{{cA$i~ zQ~pztBhZ3|EmTF?MVsc#Q|=YtElAmuOE5U{teBN|q$}6DegAXx`mDnX3s-3#K2hS8 zwI3Rd2+%Ngnxn{pl0=!TFt!pLBCwpV31WATip@1f}s^qac+$II-eLmF!R-YU8 z6dCzUlKMOmgh<`6XSwjA)3AVl_FM=RO|V}B7`DCH>?89E>T?PDmv1lIy*HWaBl8V5 zB2wgDK@`6*F|#*>9@*z2`xSoTgF+}lc;;XTWq8I{R^-<&X9iK&k@d{zL(Jh>cUc|$ zWR50NBM&kMXX%msC$_0ng+Wzpzc4v7n9Zab5&M@>!itFULV8#cJF1XA+V9oNCdzr# zAT~X+kUDmN9zN^W|Bg*5%7DG&*V$Cly<;2C!HJ?^Zy0@W`QFhDX~VM_ajI4IQt-VY zR2hX0=N(ocs=~x{$QdkTj)hSNXYfJE)LsfbJed+6N*|o1hR?8rD5;T!bfkBdIkr4= zP?0*GOr;{4L&IlMhx-dB73}djUs*leAX~;x9h+gtm9VLE#Qt}wl%iCcm>xBkF;@M) z^-b=jOV2L7&~LK4|I3wyx89E2cI2^#=a{2$Beze!nfTCpiZT2P?pZx+9{!%#*%0(1Q+z_=Z!tfwB2DYmj9GcB7LF zaavYn0~im4h6K_Q8rpffBDT{@^OeUAWRkWI^D44ydnbDA!BL8Lg8H#QoKkU z%1Cffph=8!o~b9+p;D3HE0UYzw5?eFq$vT6Zm>h)Jbf|Qko*(_uk(bf6~?ql0ncEQ z-3l&`WQQx{#TZoI4JGG@D?(_DtCJ=)X*thiyWLgT!Ng6P(mLIc$pz}k6>u$~&_SUp zH`{8c3X5&&bn}6#Ha)h5621;CCZuc;RE4c>fvOx@c6jp^8FmY2BpC-pDpehH&5bRJ*E;on!Pz*p@^+Q$F zEqsQwxviKM;GnReb-K3{F}fON`H}e$>ISaxLMjf_hOpIB!Io>=V#?LFY89OB!Q3ro zk?mRq>G`CVViuazAxIA-kxC%tgXI;FNTLifiBv6yu+>Lko7ltKVNr$z9S=LeY2I&t z|E*ULo<4ZhrrU@pgQz@!aC%)R@_Gj~2Rbmq38Bc@>`@~mG3tk&KdTzUhH&tcB_o{S znN*nzrJqfQaXy-@;wYH39SCQzo=J7vI|>o8JTtm@=;q7!w;c`IUi&Tp5+e)1(NEm5 zV!UFH{T0=!v;l}VAZS}<7!Y7q$Rl`TR|BwHf&v%`*Ew)cI|*Q5rir;5!4SdAGNu|f zkjf;fKL4*qb*uirn`Pi}*f@Bo+X>nIxh}eY}N2MZ}L+llV@A3=sg;BFdb3&=m>T6Z#DHk?t!)QG2GcH%BJp^5O*BdpiRV5t~p-+`i* z9gNFvI^!uQFtW%Hp!GILuM{MQx|;7)naKulJhE`D0+t~Juonpj)u{4eZ|o-?H~$l_ zoJV$6^H%c$Aw-dJkVm?wmPL%VBKZ>!D@61my;vbkx^30j3J>;vqL%(ikw1^CAd@~x zKruZll`PVfa}g~Fh0Lp>?1aVueE$$$iMut=#)gD*PpsK_2&fz%rpg#;L?U)8V(83v z%w0^TKDI?5Ho&G^RQh7Myh151GwZZTt~b2S1l6|Mf2L%n#=d=`L*=5}J>sQY1uV5|DDGwp`e#`EXkELD<1b zA8=&O@8rE%rOkyuo;tZ-NFM&9sqPKvd~1=5YXwI#1t_3|$-HWzXj*QB;~C}iTJg*Z z=+N)>h+0xD+D_4OaZhopdZxiXSODiA^Yin2!0zs5>EtB)+`DeCR}oPF>>3cAoV?W) z?Jg2|OPj>+mt3u~E(ec~3Q&E}Fy(*_qaKhfl>B(2&b%4D;l| zG2%zLk$5g3ZB8^=d--=`JuCof(I`X|0ICKM6oH4AtB{X6c{~I9#BRH7#u{mHqVB~H z{+?L-)uO2R-s>-@js0Ip&mzM?pa$Tfz|#!~9sBMZCj2R`FAM{gYelsyo0nsv1Y_Lk z;HLS32jj2!rvky;AD2DM!IdU6v@;?C1lJs3Ta%;#G;t_!dnU1aU-M###muWdWz~o0 ze;i1CkdVXG*nPA~s{#emv~;x|tKC8JAq_ef$JG!>@R0bXzNXu;d3mwx^MHx56zM!Q zrO5*kl!W0mpx|u{U9CN;`M`~P&`dz$j{@EL09e-Vd>H`cUOgXsK7BC%ZTKA}-`YwG zz%`a>a}5v?#7MRQ^O-5>{N(dm!M?B3E@7AIgZ|QEF7Rzh zP_OU242p49YP?fd}xAx_Xd>0^rxB!6nRLSR} z6@#(|D6pC9ln3YQv|e2M@gq!mKWv^=Ms-J)k6*~pxYUkHGe-mfaE=dbo`;xlpZ$1V zk9S=NTmWUyC<97s33oQ8ZC{X}J~A+%4X$=6T^)ddyG*%k z`bp30j(vLHz96XtIrX~Wg-n^U?uEwAcxAvA|FLY*>adTj$ODV`fRobJu;$y5gk0sN zo&JU&J~sUTJ<&$%<0ep7qP)H(=HyrVIKNG{xcj{qEgY@XLA8Q&;^j=zeYRzW|fc08{GfzZuW>sOTOzRig=ox7I zfzb&fJa`WP(!%hybBuJ#tw&8q+WCVm3Q?8|3gl;Qgytfkz*!iCZ(4$1>yd>*vp;#r zX0L+b7xh3JUpwEntbgRu(W;TsZ+_E?mW!X>|j7$0>G831)OwUZTJxETiZSdInK4x#b2oDzUY!PdvGJd|_7O zQ1ORf+QMpAuK%62MQ)53hKpF@x&~;jJx_o`m59HOeAx3de@4IeTvX6VPDHScceP2-Ben-^Z`^PmUvOPlyhdODFgc9PAwI?AF;i5-h2VSawXpjyQtXj?{$Y zojaI}9VtXi>=O8SZT9f@^rE@}D{DvWcM01WI2DEDmF^zFIHZmcXPK{28>g?BhLA2o zRrTx2GP$N5sw*ki@IK~MO3^@`*%_h;8< z%#6nu2bfrOVo*-L9oIO=t#l}S(){Rs_h)VsX6lQ=R1jE=tZtsIPxUs>`8*K$<$= zAi!P#87oI(k`?k4Y7r0zOo4ok=-3V3{U%o@Jb`l5291@J5Vx1>>C-DW6@UQt0;6%P6qrjMAQMF(5OUF%F^e#5-|DyN4!b31C;#nTdw2)M0*fI8SQx5$eTPDqvNe@LX&1Hm@ z{jDoh2Tc%`PV#i*Mi<(5@(yfv*HAckK65ti((gQizj?%B(@z$DcdUC}JNmzI?AP;x za0xV^_ZzSW3>krI=>txT0UAS`${1{9L^NfFHD=0YzUWf+mze`;vy4>q(EIP1Q}nxY z(Q=;>i?EWF^316_e(Gu6>Hn2ERq+M2`^r*K)gN1nsyA_*m#j_X^I}BiXSl3eq{qAicYNx2tUDy) z5i0hWEb@8E3lfotC$r&2r-X-}bvC4mGTNntv^!t9`V4pm+;I%})3=}P)}KQ68So9% z^P-78189CB0%m^@BjS$Zz+HHB3J9VFI57uY|EEEZ|A&kMLJ4V)K4bSAn$#>OHbLtPG z575}K?yv+C6(V2&X0Vhg3!x8AI|ZcB`)Q1@6k1p?V{nEZHq8#8Wk!@Tu-O@=Y-#SG zB=bQEosLNk4x7##mX;ZPBY?>W+oK2tglD^HPm1V{V7?HC%vby&j zuL{qdn>_bi^M=*!UoS4a_U3+zEtfq!%N&Wj-*WiX!w(%tl7@e^pKN5^Z#i#$RzTSEh{SR zl(g~CgXzp$+b1`CwYTzh_tits-*+UxcbV8-B%0FFfPlhc!>(FO-{hxdC{GV<(@oa4 zS23q*K|SDWyHgg&=sHZ)#CH2=@M{byJ~q)>?&Ia$ZW6JYTjox*$ypMIM@Mhl#T(+SF$Rc%3I+s?m>m%;Rdg zSea&XDOUES+-R-3wqR<3pe86L>C$7v7*RFXaFf*~m|)sJHj3#cVTmXvsD+%TqB@K};0x?U?u z^t<$UD7xEi+$grESxv_s6g2c8+BZrPnfBxO`NfG65M?77f(iNJViV9qat~}J1yObF z6-lDj&hi+48woz9+pWDUsi%3SBBB^lUCS9>Nab5pA#F9}X`5}?Zj`NR-oZQCM`$Z& zQLQ?72Z|}}`l5VSst84CxvQGzXt~3wkn8SET!GLwAr29Q*$!LcyC{eNCrv_Icv1trETijS z04SFtfBe4&`CIk>Js6-TArH{lr)|GIQ(xs3vo9yNUK#xg=jj-TQJ1F{HFxbE8)Z|e zKK#-R_7end36;`76Zv@a`I7L1my$Pw7X*e8g%FJ4i8dWZF@Hdmh9}trWXb0n#njtN z!aaQI1V%B1PSS8HkbpMLGbPl6_8nW3UNj$*MAr+(;dJ6AFpOz%nz+xJ!iYrzIDKzv zXb(*~Pf~l3fkXEfX(L^YwW5n9DZ$PrTjgf5Kpb+OeFu-F4GHW?u?I2o^RPE@9+zD_ zkE`ZV9Td0~+%1rz7#-j!Hx;~?(M(h3aYQ~Q51S?O;m~cVIIVDDb{<;t7exI`S3{-D za{!x$xttdps@r``o;vh7k)57Qks%7dtvB?NhR}E5;!;bqy*aNA0Wj z$X3_=Q7F7}~xN+aWW z`yFX|3tKhTxxUI>b$?9xKSURP)a3QupzJ6BI&yNT`N{ zQA(bkj-CAvUg4H3b8x3(S1?6WR#u$)v1_BUviu&XT(m>YVmomEJS(7W1` zBL_bXx!(9~>M4Ew(vOcSez&g5NRDj^L>$solM2}+#7V({D&xW@lIO0qxPeygzRKuH z66|i9h~_BQvITv6@8obGnIjxZf7n;KlU?+==jTeDcqBlg{L%uap;0Kh7P^EGaR9o-EK1{#?;2$di~zKE?sz_;`9_kHMkE9 z4T2UBIS0USAKZ^na`Njvv$r__jbZA1yK&^jx>lV(e_n6fb33AFliw~wG-Zqki^e<@ ztUzdB@OC$7GPNF|t*M6qQDuVAsG-~RwXuco24%+{y)Io|2dX0zjBhCrV9nCjR;LyY zacGST66QaLa)q)azinl zjPHZ>!7-ifvh{~*cdqD+OvEf}jL1fq?V)i#O)H~uQ>dTp=!5IX^1?J7^(*&87!~5IE300F9Nl)#RW9lOI&n7D z^sYIe$5xJ5UtJZ+ToIB!XLydhZ&!zL?NVP_<%b*oyr+GI9bF!5-(Qygk*tD!pg{gF z_2(8u(l{F7_t+O?`+I-5dGc-LO!~XTgEg3ulYNVhz;( zzK0qjK+r#~m{wNqm#t52X=<#@5{bxL0xjTE5Nw3AMM#6A8*F?s7|nhQfV2wi>1|=j|)wl%5PG?|GMQLwuuFDPdMA+ z6bczlA`a$%6c`Z+sTs5YvTgfe*w?n0&WPKQt7^si8kqN|bPxd}6S7p%_z4UeHG_@j z0{PaLKpvR7SMcF&!?St$T}lY^C3^A{A-KvRVfDjKs2(`8#`~iel(J5wz8*Lqc_9o^ zU%s7La3j{}3(;y*_#exg#J&TXHU2Wvq?9ETnl{CKe77ec(4qz+>DNp}uB3?&Sza+# zK82S96p1Sh`*q4QI)?|O|NY{wV%y(iwGJ8{hnz4jt#E0XT=0k87xrI}p1PFOa`mgI eI^vRCrzISC_rgb?J&{X;RdC4lN0}@Up8p3Q<5IH# 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 zcmb6-dpy)x`)Azmrbu|vD9VT!OJS+WrI=hY8P|}@B$rVxQ@Y4(hNX~%m4?_{(k9w> zM`??xb*o&{j9hXlp>$C;+D-hP?`Zea{{HyA@9%uhobPj<^E}Tv&pFR?p685fXy`5^ zg1(eCOMf~BirigOgww(u+#eGZ7KbX-rTNGKl%W0u>{kQ6$ptT7E!7oX2j;@?Kv&A z@nvr6`YjlgoMxP(mTaBd?=z)+cF^Xf^@Ns`ytg=P5+X}!vz4h1+R1-4#IpRLlf?4; zp!>uMjo3C)3n59ta%{4G8(G5$ZvJ zwE!|ih{Pl>$y-v3fLO2<$nlKwgKHY?-5TxhG&+~*x-Hl3HZkm>n(g|=n&xiLcc*#M z{QM3v_y-yMJVvjF5b$7l*D?nFS%s<-aHx2VMfMtE_G)7GGxKaQcFGkI6oPF@vEIpN z=9yy-nYv-wdk^P@rWIT|S1?&wBqJn2I3U5wNc_o(_{`A5*}}Gw`hm|M`eIX+MDe2;sbGm&R}~@0UheXg*{(x4_4$&4nij37HZ>%G%}HXSL!^ zE1WGRwGvCUGBUSODwB3n)z+28oe*kHK!}b1+KRPPRD)8rD>yMS`j<+xPwL;-`*)kk z0|TtN@>_@HRLyUATRGA%4=Dy_e2?G^?s!q`RSSoRGOoa9eHj=yv24ks;7|!6Sz9I4 zg#M9|9|K-dd_v~ygPYdrT&CImRqcGI0mi&!&q8CSc^+hV|G*r$p6vISIoz8t+?x@w zGKKD)_m8@yI%tA0cM`f|PEb0jg_GoBBfWIMUp!}fv~tTCjeBQwcKod4{LrBNy{YSr zX}6L+n9Hsee%&jWJu38F8~u9B=sh$#{~*1umhO8!!MiqLc&hxW@^URWt*4=PB#M!U>`T!8(Wy$Tpt`Z$Lsv)0h z#-|$6+^uQE?#NVrZ03>n^wq7=ukhcb9+u5r zBX&6BoLy=(Y??j7!`-Qxq+#2pJ;5n;p^kAbK6DG`#O7F1G|QAD?sG4?2kqc)?681t zI`uyHmwq=2w_IHoE`tN`tHI02doM$FIE0tdF3X}i5XU(&3dAx_aEEqzPV9Y)=bWU6 zj+LC)hZe*$RID4*;rN2%-eEy3WJ916(SV-;8(c*UMT;PXmUmW;J$7ynP2GNX>SkJZ z$N4$-IPFrhr}6FFXwIHoMz=fN_n~ReBj}pBxzpUt7=7-vfmx@XJm+3M!;QxDtz`tp zGY00Ydwzi~<4K&K?;%Ft9No7!W4ln4Cx~J?d-55(m<&H9``#5a&pdm6F5NSa8IVhV z(reevwCCsexiRU!xqib3>E3hBy&ssgykr<1z0UMgjT+um1QP|os3*N|Wz>_6#|P$; zLxrpArkQoS`}NS6Fz6XnUJ1Ew#RG3@ErH*4k==hsc6d&kkdJ?MRNe%`Z8H(G*k zE*%@4V+^lM=@TT3#`*bSl0Cd<69#&7p9`3yADwz?K|y8@Ghuj^8G4TC_tC!hLjo-? zA(%@K_?SFg`KkF``sK@0mtV^?=s&nyBm3*jV7*ZebKoQ6N$6nx>37fOo6kfH+$Bxb z#tznBHE5Xm<~ZH!NO{;m`P82`U)-!Wnz~6Etglw4@q1~$8&mkX$y#CZ2^T~4JP-e%poHQ5|W*f#Oa?0vI zuSTg#Xb(O{fh4X?m4o}BOu>e-EEsPxQ5w?bC|y>nLUA+MVh5|e#q9jMvmS|Wm2zRRDtCJ+OV_hiY9Jt+JKwvU)DNcdi8wtILaF3}+sa^W8`+iP zrBRGsI%Ivsx)W{iV8+hPjNW%mr&Gzk2fXqihUqq?@oNp2FkrwK#+}AzGM^}6iBT_D z{+y783GU$B6Nh02W)pf$X}wH3-1C7LRpFc7lsybbzDWTjIqwO*8Hbj#!4t!$yf^kaX)UWh*%_ z`UP74H$?r5u9Qmo!U1g3+CpBeDPdz$FbJDh2@knU+ zkSY^8d?Jb4u#HY>B{ULJWQa{1t{gE*quhze&dKc{rKFx!@s(X63fU|kOPxf%6VWry z!XZg1X(}QIeph14mcn1;@owDs1ztNgAt?@a1`=t~e6kx!6B%qU9up;}G;ix~5|2|G zIkDl5CEf;q0^j%)$vE)tNN=)wm$GhfM6B>fs-*A<2N$LyLM;;bW!)Ovzi*LrNNtsJ zbc75Fp)SOeM7%%|J3G^s?$LL+<*Q9>sMr&1sfdX!W&PN*QA|wu4iqz@FO6aEKn#y> zIQCX&&Dt`P+@A8`_*NECk%@Y1l8K z>%nwx*OEr6z^<_6?DLhglRq+Md-O$LeYfS0;i1>{#tPhO@eM54N+UJ(j0)O%zVxZ4 zi}l++efvI264!s7Iqpkl@ft~qJ8r(8^=t_E?Wet*Ce=;Xw|uDlcK@)JC}Jfg$|GJO zOAQr;Z@|_|p0|2(ixzNv`8f;pr-np|8KoK9-KgjoFz+`ZR>VkaVn?`u_E4#XE`yp(z(~z9iz8A6E)49EB}&4UfjqsMe&JX5D0xb z3i1>Xs<{O=h6wT^EdPMc*W4(qhrjZv7mh@0o#v`lCssYKH(LARkD&$2NB*LZl%+vq z8H9|@2=ZvN7A9%>+;YV8gqUfbEeGBBpY_qg<>}wnuffcsXE7Kfs-NXV?Y~n!v03*0 zVWVn=OMXgz&Szk;S77;7xG>ZO?1;5+Sqx3T0f>#UvSg64#(Z3)A#?~%31*uS+T6_P z{DBo2}#9{*WgMhW4(y0v>16=>OSnl9}Lu^Gvz5xWR&xQzzBaQPDc&t~ltN=a(E1@Zu zMk=+p9{t3U+B&juXVSYNH}Qb)BW1<7gyT~JJHOr=7sEwH_D~SY$K3^Tgywsez0CTv zm&so!!~SY`Oa4%FdX3ZxkP7pmXQEctzbxlk(m=#L#&ncUG{#3A|Elzy; z^^f%n0q_6y)AnnUlj?X8G4BtkWm6Wy%#cN2!vlM~<1Z}@DcZNfe!&g>a@ zF}COJ(la>!UhI&4;v^ks*_u&Z#k#PFo4O>wS=PT@aMMtZPe)YOq!R4*_lat%K5K261R3l;Zbe z4-Q-KRZ*Ti&;KanZT~|m_BN743qNRH@WeInShs&s44a>my*bV7WABW5v%7floriP@ zYFIR9Wr-juxOSj;br8_*f2$)FWi?n5107I6tgaWMOyMAkf4y-N1{H;BuDWpd?eai$}mrps+UJQ0aCVbU{k1{}%SjiPx({UvA4bYZ7I_KOE1@752PUvB4!EDS1S0tzHs{qyqgs zEqakw-UrvCZ+!&V+Y)b#oo$PYBx^I~1M`cYedR8`iPvT6Zk~!3u0q|^J8_{Q(P4k8 z2vPPW$4%2Q4Ks6uzOOiCQuSWVSi%4Z60K@orP#k0XSROhAR;tXR8Kj6hZdH1PDY28 z_)hPiJ9Y%uzx@Oo zI6m=uw|`6e_m=D7Pb?f-sNh$>`02jcM$L#`>zLM7#WbN=)BvSBVy^^Sf*2`2+XD$0 zlv)%ITJwH|(PO4^fE$@L#_&7+J$-TLr?snmv0hEhORh+<1Y!-hCu5sb6q@^XX1%5$ z%=z>q-OTi3nkC0ivPH-j`MaU9MwUgicsmlh)d z|AcvUJtl9m!Q4?i3#0t_`FRu;P&aSlSFgLUETFLb*22_s#lFu|yg!M{Wzd$wJs?95 z2f+k$mlB?fB*uo&jFmXg6~2vCppM4~*af*2lwvm)9>iuKAD*z3$LQi+>xGLNlHgYd z9HR~~e~kG&8DmxRX@<#HmKJACDT5As{*?t0s${bc2*H@p$Wb>m4ChV;*qtZz)AS=n zXEDjVm0KXjSq+0{_zNCYH{7bTX0Dm?oo}o>*f1RCd}b$_q(F2)>mX!(eDOw%{Q#^- z+!A92X3kXv;Q+nFI@3k+c*uew{=lG=U|15?@1<313Noo2b;2Rn)33r!?rL>Iq>wd2 zWVnynq2T2ZAXwF~L>~uPj2VqQcNaxYiB)wt#BxI@>&3hp8o`a2!jM*NdguP3W`FUq zkNiWCW1pWEtbQnl97I_S7jSnyMGzZ{dqdbh%>=A?jJYui^htUL9|Lp&)Yq!L(rGj_ zeVc-RFd!O5`tt~1`OI1b@#>bREZZp+=!DZ8h%i9;f7t4ZqG?-9Mh!^Rs(p1@aj1Tl zqBj_zc%^T$;q6~@UzK4yU{o$xGjSR5UD>THF;>b=SnC@uLkRHalM~T&|jR9J<^B1h&g}=HX7NB_3p{_jDw5D5{d;?Q0 Wb+p`33BJV*oY{$OA|r~+(f8+L2!cb;e3KJD+1@5}u>bDwk1z2}^J&OPTm=Z;%ws3(#^ z@7rS=nP3czd*EP0&?Ou>7#$qO!4L%APuL5gSvLayzD{t%C_gle5}Q#DOgg&M|J}#! z%pxftGnn=bJ9J=!TXZNXGA!5|=T34anVOiIm~1gIBN@PT4h!6-H> zDu#@)T@F8oJ$7#P4m2wuAN}qOo+huCpLo@f>WJULK==Gxe&>4(a zcJC2(Z!Wvf8wb4EepT$je?GYCBmyc}W0AdvoV}WyJ+?JlHYue{28CcIDKJPI+nV{< zDRXUD_UA`)Lr>*jI-mcf^r8~J1QB2f)<)veCgU5@Z8>f zc6*1Kc}I^~o0_YKnQPB~@p!7A!2Jaj(fj7}+;E=4rmP^`EIeSVS*Tz~luCjHsfCY8 z`JMSM+bmS_#|ui;FZ9%(^186TrZn#Yr`=*w5ZS(JOu%U;x14V*INzDiX?G^(3#HJ} zVJg>~o4m-hPttzT#U#HAW_fKKr*tQXaIN%`SSIqjwU1nu<} zqd;+L;6g-=A_Jg5LIls-6fqpgecvhK?qdz-7W6=~>6Uza`XLVyvtDG&M z+#wg~W@PT7mY#H{ty(XP`4ZQhgiw;|$6ls=TdgN2L7NO*Jy_^=4*>CN6TWSU5!pMH2tdgy7(0p){i4P)Q+KHgc=*_(;i% zhNvj`Qt9J^o7L}FrqlJ&)A2$ZoLTD6xHHpzjCI|~BJRJGK>6B){$vw7;(gS`UIgW?bo6l(9J)^(-2YuIWqqg77++Lb>sX2hV zc&=b{t>E@(usk<2ddyipbXM;XR(};MpgO^?Dq(p3qoQ z2pD)AX!;KSh#WCR+YW-~2P4;)QXQ?jAUni3to--@5E5q*cVnqU--Bk{OEd4K8PmP2 z=>MtpVxa|w&A~J663Ydx_`7)x+3^R`ZHdRG@;I5t+R|6wIs8m@Q|eLWoHepXGtS$m z9tNX1AXUhnnkTjGTJbjjgbO%)ORA1gJ)%YQy;tyV(dKpw7^c%&L_ZFA zP({UB%8(2W!LL$PX^G!5WKTe<3VOmUniF|K5UoNM3YhJB#e7bS#kk;Pw{xk0(``XM zL&JvA?asdnyxJ|u-z33@PDUGkMoADVvgoi3QfYp%-MPo!qub2mcb6`%RaXqdxyQv* z&4J!)??HF<5wW|xSOMK;J@;W~?%_rEFlYCB(Ff;TdU9R+df6UyZa@_~D2_ciZ`Jc7 z4A~E37y+^D{&`kFUxp*j$_H3+U444lo?JFV&7p4v-6z+fH;3hu%MHw7J?yjZ;yU#5 z86I3#Kn`R02+MEYweJ;|o|_D_qt#r7X4LSOiy$ZvW_?)wE2AE6P8^(14#juXMK|yA zV)W3tAa_uLvGNhI0Mnnt9`<4M&zXC~FuLe0zZkloH>-c1;Wx+ipeF?6u&~*A_VCJ- zeo4Y;41VR()kdY;QzaOiuLK+jEJidcaQ$-||u zn_i?}zC3k#TB*+P-p`fFKfN5P-N@$-F0dbl4%ME1G4`hEOvK>Nl&LDtQ0*0?x|f?0 zS-!`L!v>3|{=D()joOVDq|O`{@QId& zI!S!0Ati?&Oe}5irlpCByqFPJ$2FMkg?x?OhF4*NO&Ql@wpw7q1ybP>*o!wfD6q0H zC7{OW`TvqLm4iDbbnGoK%j1))yd3Lm6AcR2`KMLcYi3>r0)ZXFEv^c^sNy zli*|)wK0#QN^KFGbf;3!1LRERXH%)=$uugpR0LtGgoSNnC*N1Q082X7hKN^x*y!F% zdk=F~|5g1CHHRLE$|D%2%Z%PzWh}*j17jF3dM}szPz@`L`oQz&@ikls2iKk$3^O>F z&|^mLUS%k!D+K8tV>8mqZ zbik+*-<2FfK}1_Qy=0;-RE2z7C-Pc^_KOLKBu+AQKX8YNNKk2#8vG;5;bR$G3LQWx zRDAgFI{d5pKX)t86Q5Bu@^1g_rwZ#Gm?x4`%4E^cP){?#M=gKVFS>6su9-xmISX@k zQ6@?JY#Ozk-tO!q6iWQRz8bv;PC=v*kPgo1A8y>F$$SQ0>K{P?$UrF6WR_7R{&vo# zB28wxd7nQGmVh=j^VzhoDNTDLp4Xg{1eb{>pgOSi|c+E2YJZrW5lXM;2oIuvxMYif#jGwZezl_t5eW5bf`EMKtPr3LsO| z!)dXuFbU&=L6}?|G8C#KbZ7)ViTh>03Gwnv%2>BI4&jaur824AKa#u)Gjz%wQUfVP ziQFg<5y>aDi(SY`{G4`5O6pmS0Ob{npqf?1N++2UG6p7D1SGGZNJG{^CkpM76jZ+# zi?3h*5pFxSAt_GwMslYVd#ybvI>=~)$>T-VDNVZu8W+c`8u@a=b4yiQ)h|^W9w8+s zalPV}?JrW+4~@uv^BqkdzrsPnT#8V$oa(YJZO5;g<(*RRC^$Pq1%*&263ZiTkc_>( zS##Iu3&Qf%rnWTf3MOr`veK#_yEe+o;`cz=Nk8cvb`Qkxq>+l+Rl4gUJ$Adf?56Ir zx3{*l*|}qz<>t+%n@r4x{D$8Q*F_BPT;Sd}pN&?+X%`;5{G29O6GxEE-1*1MPt(d{ z?yvnvE)xhc=psd{vFA~VWzf~Q`+WzFJN`0FLjDV{-a1nt5Idr@(9u}i#@8l4#4m=_$1{)^w3A{gxxKpl};KHMi4#G?K z68p4U{mQbfU)$`M=?(mJoOYSKTnq6a^C0Vd=%CFQ63`nN#9NNUBJ@_tt@-kL;2odKK;DEu(p0G({!s`cp2=7p7m&5F?9)r@mqu>;+UCt4&)-?{9czl64j|+W z!j8*3E#l3GV~TXvX(6=d9MDy%z)<`yl2F;45&A+0o4gkN-AZ^RhL&SmJ{t;kjz3;q zx8p9QGkK344OXGrTVUvD0N#dS#BZ)4_iwky@5k-cx^JThHn-1ldLB0Q zY#`J*uC}jEcia>>=`F@Nz20oCg`VkSoaWv^3etkgA`UY@npG!Fy*&&7uj|z-xN_>Q z*Qq#I|LHT!+xi!0ew*@$cfJ0B%N`VlW-%VT9y$RL<)JcRV8h%c)-O|IfF)9&uF*v3 z7Y&__2Qi(#&1!h5`=`i#+d5y@Gg;B75+cY;rW@_YB%{CT5E=r*RDOj8RugOVRJ#$5|7-Gu&CHbBwB`=;HScwaCnQ15jNAj%5U(02;})UHEhS)jgf{FP z#H_<}Bceo!preErcxc63{IuLn_`wp(!IQB~qbOqnx`}N9lRL6l{HTh8 zu~}#N9jK^VHFWwbUSQ|Vos`tteLBgL6*Mq|3@TcTR58aasH0-J>I=7DHUK#(?}k4h zc}HT+kcRYGr%Y0&?mcFu&pdaK#m5VGs60vD}j3N z#-)V1E3b|1uO-S?WM2Bq`3Kwp6C=l!(a%`qE&xU4Iq+4F_IAxBYt{-Ch+- zsB3!7etdtXv%+zVc^B6_@8nMx5N`b09RxJ82F&z)+Zxy|Z~7DH(y=WMMODZo+H@u= zcU>7C^^p$`3{OkyKF*}02P4l2*5DjS{Lm94fz|Lm0q7Dzx9aA;4Kj%*#%jH9O~pRx z<2?89)GtDabEpcE7%ck*)?ZzMkoI&T<{xZCxN z`doGj_2=gQT6VI!V&^+@DS0^!@hq;xW}v0Px8qOv-c&X@ zVy8a+DQxP^jKR&7qKeh8UdO1%{a#q?5e3W8D=ArS-uVKuVE6d^{G5Em>M6(~|f#IT4_ zp#?D&6stk8MMBHs!j4HmK(H3D(kgC1OND#hH@1(xf86i8?=zV>b7sytGiT0kmiWfR z1Rxdk{(F$n&%wyMJz`6ODPeDXLUb$-V-N)2V+BIvz689yN$|xa|M@UUti{!>ow*=v z_QUVw9HkyRXby?pz2j5ggc#D!*ytc!JBdoNwXwCaS!-iQvY@lK^I5UGVn`ml_Qoc} z@8Ys|C6Y0^;zwt zsnN(ne`CvLj7mFZBQ#2L&U+X(W_GII^_laismA=!xd^=GqG6UwqibixfaSjK>R1#O&$pq?;?V{(cnivo4zRxuA@Nt24i|R3KI(vkaoUIm3G`nDS$~5tW z&Z%O`9dfDZ@$5}B@lijz(aN&KBlyfo2&L+N7{zAkhS3>j<)Q>F%ZsJCnU?n~uC>_S zx5BcDpV}P9s(#XS)23YSqFLF?f>_5TBjd2sTBq~L#u9DvPIhwLX_|3*&s z&+D?6QFF~hb64QV5uZNytN82xg`DCYndOC<OTZy?p-C9Psm(i^m zfzFKo^vN)2fn)RVop!$Cl<~p4g$=m}cVxM0Cyf>IvXfe~mfqR(o9^n21M~8hsUA3f z+C5_rIL!m;!rp0k)WogDY*bXbh5n1^n=b!25l`qyTT({k+>?A07`<7trp*DWS@e67 z>ks{Dk_*Q3U^Cbazbaj=`JoGuI|1oxn3d(wy~rb?1RZjjh}C9xLCCx3Ff2OSMHP#9 zT@K`vbgY`tM*US3*ycb!n+gY=iV6IzQUNNeXpaigX@2QM?RNL?vh#oA-N|?EOk`5K zy#w?-7&3Q%hEK4B+Zo6X@3QND09AYcK!(3Pw>OZ{H{so#@1u}${Tcl5YHrkiZr`MH z_jRap2NId#`?$T6>~O_#Ph3?nsN(ws%eVo2E>q7#v6vB@?;*=$2j}x6^4J3k_fEct zOvv=-v%~Y4gL~PblRk>Kd`5m6v_8JUXBzGrTzd`-ML=sXyVqdXz?zi4$+Q@JRGkd_ z&Ol~2gAaE5-F_ktb zo^E=X_3gJ~-~O&uXL8xg_o1`TrC(Tj;X^k*bE&f~6@5?R9{ zp|+#tl2jqhl9DHk))qGe(K98bfh^9|VSQFxiBNyDX7NO0{Z9!6I(YJ z9_Mp)_+fO5^N8X0)*AH%t}sD;H!`JK9>=AOn7lVmmV+v5`9o;HjmwN-9`YXWl>$_W z8;dw?_05GWUu(GutHrr7pVL}1SrncFP}g1u2U4M*8o*W)hi$4wqhYF=S(E5g2dSZu zN4IPe9nGON7V>my_e4kiXtdKnQOUww8m%&oPNRt>09!2#wmUn7AtuGxrsHCX1oeBY z?!R{Tw`ccWHE-AR=mu2o#Ww^wO5k9*HSQauwS(ApWY zRmR9n9tcW8(NH%(GQ7^e~r%V;rV zK&8|0>i?DDr|SPZTa2CtKSo2dkv~1tSn0t!oR(gpihhLqGz((X@mKTG2R6fosdPHE zEN>HKlqAfh(<&KlRIjo!dDvH16SlxANc6+AAQ;2qteXs3zd@9SaVS7pl$9B>Dk$y}-$jY);gwdf3%%)~Cm;M!{X)EV>O@TbRLNWrk6K9D&tI~e7pEr(mZj-?6+pn@Q zgmc+J{qaEp>YuNJbT!wsRpllItux+=yU830Trwz4BAyNa*qufJHTZqli~OFKn)9Al zBBa|YdCP@<08y+T@Mw3HvY6ABB);d7%P@UdE4d6V-4@E*iV#}gQ;Ytg=zsSsrPJO^ zfS9HkE{kO)shAdw!tCOdWf>8n-9vCBMyj6O9vGT>e8txN2oHR?DwEp6c9J(?j!wTr zY9OU+ksC!4ZSql*3*O{ZVO|?0J>!&q_`Jn);5Ms^U7b`8t613N5RkftCLOs%9WHT8 z)zJM$D!qC0I=psVx2Jo#Td5t>l)3m*mLRK7ZGM^KlHRoGVdLCHqoE_8K6li0)jguy z@H5i#lGbakb$Xe;vVTbR?ALU4{0Rpe<^qJ8)pQqjns|QMtnQU@M}ta*8x%qvNUDyc zQ7Z24cFmoSUlJBAwRNRqPcY@Ds;a#8W6wraRs0>O`iD{yVedeUKe}wU{zsr2;{=~anY4P z40TBGC>w&9vS}`=is7{GYJCBy$6iY5;}f3~(1x48pIA`8pkm!M^B0rPuAGolB7IJF zAXEYRrejf;(i=C$DrhJeTuV2AOVvWblP6DhmwH~ooRQUcCQ+FF zv~ElIM+l_^FZuZ6BZI{?tBAwzw!Is^eraofIXtsJfwynDS}8%=jQe$3Z#fcNf%O}I zp$)=Qw=B`jv&;o+bfAJR9Fk(5nbAL6y{g{ei?b_LUw%;#Z2Qpmr0nlu^=P^nqeN^1 z#dVeh^uv0fqOlnA^GAj{DVM*$3?2~J@SAP|@7&1NwgN$&{9WvuM@J*RdS&m#$xJ1p zNH_@!39+aH&T1r>kpWJOfhd3)66r{J(>~awd!|`;p)jo$wHF8mufL1^?A6;b{}kiw zqNQeYQ3%*vM8ZZZ*atq&hH7*WiY)kY7#my9&lDyU$Yl)y6l~2d7Yug1TXKqCHzSOQ z!-n881rHs#Mxya>I8V@2$;q|Y%2oYv9*U}D&=y&Q+3w$VStZ4#D}~c3jySaIWB&m2 zQiOQBZy-PDEhw=>$iy7_nil$CbD1(m#`@Jid>meH&~Rg;>x*{9VEp>H%xf+Re)5>y z9W>W}gi9i6fp4;p%#=DbriwJ`-&~u7KN_-o)+Kv}8{YrU3K9}zsh~&=qI2`?iJRxQ zMG6$poqZxf!@&a{j9;B$zPF|;8KmU zA@GM+NsUqHjZ6GW9X7c)N%BD#KT_OC1F(<96>RqlWFcHJ6#qcxuikl^GaA;U}Mpv7N? zw8M+i>KYd%JslW&cYbJf|H$vZ+3uNr{psw{Mp@;4w7xFsZQQw0hvF?#Z8U@}p&&^^ z9UIGzu7?k~PHNLrbuXqz76JU=Tv29w;)hs{0*J55+Sj*hYim6a+O*+K>*HmcH^Uoj zEQrbEsw2~j!y9H#46O|@oq4Bt6kqp1lxx4Ngn)XG^!02NM1)`_(Fe?-LRemeex93B zhy}dMP6NMHX~eFL1E*IKiXE^fX?J|QTm}APN(p9cn2E37m*McX`O8Ns_P6|6r4WvbD@VkE#vf;JFL ztIapgdP-lssl9NmZibZfR6*1vBNsOB|m! z-p6Ub_}AG8>f($UKThV6Y#U6#4-GBojE9L1eRNqRa&Q@-?ynbCKRnQRu<8L3TQl@O 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 03/32] 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 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 b0ty>F+V#!w%!-R^s7TfMERrf10(}1ifM4$i delta 2545 zcmX|C2~<=^7QNNo(10u|h=^=SR76BbL{S2y6E&iu$krHH{2nwMaa7zdhCya<_(58(+)?ljyM7DX|daU!i&#$WYZ{2s_QdtvUdpm|E zCPqMlx5LRPYqH_h)p6yO?;Iys8s?1V_7Z8v*~nfi(Xe2!Ugc-K!o@)u9vKx878X4> zJVc27LKzdYaA9mzbewX*0%b4@2z0IP_}nzMK7PCb5^jOJbC2~*f&YT6jswXSQ}0#9 zq}jyTcy3Sn*3;4&x<2_2yrKXdUfddH!|4N!Yrn1et97^R_(BgRG{+#hTO1076PlP? zPvzOw;e#~2Yf@jNAU2FRW;d5NsrGK&5gDgmSroh|BGy2&H^D@=*+g$8eC5k&N|>L7 zne+xt65O%R1p9%#>mba8#tPu2&93ffth25f1lOmjfHdAheK&OR#B=!>O)+in|FmZA zYI9Le5TW^aZK)}c{o_+djfWAnZ_gM$pq2SJ=e~z3l?2NqIB9|F!vNi#YaZ~apXfb? zDN6Dj8T!rRTS|%&F3;GzWy{&vh}h4VJvbRI#YH!(TvH=Q-@E8sq+%gVFH=Z_V(=Ko z&(TihMsd81Z<=|^559#hb8s^z&ji_7qZx?mew)S750*xCdTnr8O-u)oM*}KZTO_&= zv)8o9$g|Dcn%AqG_+i{(@b!ddF5{;p(9-5)-*M1=CSJaaTvb>xaxJyyL47+0|N8Ms z^@NQ5_v{WpL2`=6$K{4X%ZRIBMAX$<$^)uRbxZheiNZmGQ6q7G9OgR%LT2^>CJ4{H zS0H&n2$2jX*Mn}V277+|;Z61Cz4wy7odY+Z|Jcx~MC``cVCh&RqQ2Eyx-Jge*?*=p zxBU``7C5Se!k>h{5HH3L!Gc4>W}9o8cG<#G+Z|V3Qnaf*$>H3_=Poa533CB~oCc=M z;u*eNW78g(n%TQ$=BtR1-AQj0D3YMJV+1Z9 zm2!mx;qz`gbN=ia6Lv67G$l|KRfRAH!DsIVy-)SocDQHLB$0%BY+XnjNU?%?46@DI z^_4j-GVP2svHTl{NpF9t#D zrEQRc9}?rKUTEC#l46l9`n{2ER;a(dyhZkK#%A&F+3MwQIEzF&poHZ%DvgX)@e#xN@~N|S5WUrB=TWP0sGe?w#Y-9bnWk2TAoKXjoptw~*Uh@qGxF5V z@7)SLCNL_k1q>UgZl=odPa|j7_2yiqW;}>tE;*k&jXGQfg2mQLOWgB@kn;2Vquham zx9gUZ+(=_wBy4R%reBTu{aNYawTj@Vg@zu;L6l=xnexA;B6zEzm$!BOF4L?}V!S}2 zsH29Xr=QN5-}t$;VNbOgaJaTlNznS|NBs@v{c;2`BW?GrCt_DvX~m|tgdKM8gig+4 zsFZQpVuPnWJ;`{rW+(zaz|l|!uBSCQGK#xz`h`oO**+`D3oyH*B+m3a9YU%Q^z|ck zr=Qe@w7fj>OWCRBb0I9K6g}K)Byghrfr_$( z7S9jEt0^dxQmr(971-UrHoX2>z|(*KEA%bsFz8g?Xj&m|GlZnA!W2c-V^ElYleUx` zmIsBZpQ4x3ZMNb}3+eju;m=8AMlG(M@p|Y-LVY8ya%W)ilaenkH2VE^=~CIeck!#L zk?@=Eq!3*te9vR#+9RE0R_vleZ!&c|_|$ZdJ4qTT*5A8Jj`*)CpmU*Xr4Z9i9dy#U zLGJnYqi2_&_kP~1JhApou_X8R9T|mex)EHi8rXDpS)f;o`TgX&u#i+t!<7*Knmfm^aNoXP%_ku2)06J_n57pXMaoH%K!OQzN8R)N6PB(m|c7c-4 zEVDxV#dYm|o;{tK)NzelLjV4*xNfjC36~$hE~TW8ctdotegkVGHriok`jf8KLG&+yqD8J1+>s3Bmt)@(jGFfSKlgIk_-DuV5FRdU%~tf?-m#{^z)Pz16ATo{L}Ee-=YQHp=jPXK%CJGlVk1{BJzV1Itr@+=XVo^F%4hM~RW@&UjOg zcBweuLpUkUeb@5(Sl)i;u|Q4oyXh=nr5vt2T%8kQ&==exY~wPVIiq-ng8- z4`Bz)NCtn{wRS*2G!T9rUwiX~2-c^5zl;dX=e}p6wcqu3#f?(+tLq&XtH&t{GyUq( zsrf0%eC*+i90)dgR97&X5CUjOIH2tnLO?hvQ*&1Ib8EgU982T2eE&2*2*eVSg8mSq zz^afL;)+(bBP)Ay+jAlDoawEc^T|c_W)E*IJH#dnb1je0W**+TuD=vCYB%RUT@30@G=6uNLXjGQ50Z2sw7~y2?zIa_?V@a!GdLtw8KTdd=&xjaGxKS8N zqCNN6^GZ81g&{k+=pyN&5@m6PY0^axJ)E|?D&;|2!9SNnZqEElwT{Xogz8L$AD~H| zbM8Kb!87M^NP|vG0w5R{pujo68Fau`L6ZyCcGFOrPC#dxBT)q<)~F<1atkXA}JRmQ&!rhxe|E_6uwR zTf_liy+(n!)?ag0^N-hpSR3NiPVe(1L5V<79Rk4q&X4B1v(BD-h_h5WGo?LfoOr|X z!eox^P&HerFEb}{zd%cG-tQKpYZkCu`NUC$~A{DPrIL~V_N+n!v_axXSYL*yUS66v& zY$qoivQ4H-tL0OhY~DB+FRIXgI=2_>19C`~GKj;-7)k*bni!wd8sIW`Q$!E38wI>b z{QNut%Ed+rzHPcUt0gu5*1a;;?MBtLx85RiLmAAJAWZK1sK-W|gE8H4m0Iapr#Go! zMEPW4p00-}C2-F>7m5+2<3(p->(knDr_SO7`4K$Y)-zkWY!3-BUf=qr|n!9rg@`pFdRR!?MyJ%H678`!b$%5uKDI zwt^n_=pu@3Ida7!HLPFGkfgTthepii#h+n?b^?HAk0gFUq`@<_AS+)t6T~HTZ zxN^*_<%^zIFQq7a>RT_;L6i-cmMniP;M8t=8G`Ek>N$S8kIQwWUYeNgSS8wD3x&JH zJN!NYGu5mESO65h47pR!)@I0X>&39FiU_)>wFqORUz^l+y->!@zf#>ot8w&qzxjuu z2v)b}RlaFeHx2l{&oU(;E$MnC09PN5teNGQzXEaCr7t&KjIYjwMRO&}1RaZc4>n(n ziIPPdB9rPYF%!|NWPi zj=A%h15(|RbYgYRSS-OMppRt26Ph*^0<`rVFldyTW@&-^4PAKySXFpY+6B*^N&TE2#`gyG=)%5AvnRbw>@%jC?}qzW>Eaj(XZfdDU(93lx=GHx-ePX2UR~` z`HN0;EV1=fdX))qu_)jyjbh^g=XFqv70_FrY{V?f>zhnCorGw%fLW)otqKBD|H(sm zf!h-U!Qg~cG^Fv#(UD)aDZ7NA;Sfvrk?wtzPy#u|jJ_;g@f%btXb>O(BHGCU{S-;s};hj+#*<02d{)M6o8yg@2H zdx<%Qgx}um!$>r{~}2KDx@pn0FzzItJKLjGuCa9(F|x!xs0&zHHNJJ>t9rjVeb;0#Qll^)v~(d8t3 zl4h<0n0U~D2AutszHfCp#^MNZoSdVxM@@Mn{r0)biHOJnjnV7X{;T)) 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 04/32] 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 05/32] 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 06/32] 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 07/32] 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 08/32] 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: