// Round twenty-eight: what a screen reader says. No new Tauri command and no // backend call at all, so src/lib/api.js and src/lib/mockLibrary.js are // deliberately untouched — nothing here can warn about an unmocked command. // Nothing visible changes either: every edit is an attribute, a role, one // live region clipped to a pixel, and the probe that holds it all at zero. const fs = require('fs'); const root = process.env.SS_ROOT || 'F:/Coding/SoulStream/'; const scratch = 'C:/Users/Kyle/AppData/Local/Temp/claude/F--Coding-SoulStream/5488514a-11e8-482a-b0a0-20cb511ee110/scratchpad/'; const ins = (name) => fs.readFileSync(scratch + name, 'utf8').replace(/\s+$/, ''); let p, s; const load = (file) => { p = file; s = fs.readFileSync(root + p, 'utf8'); }; const save = () => fs.writeFileSync(root + p, s); const once = (a) => { const i = s.indexOf(a); if (i < 0) throw new Error('missing in ' + p + ': ' + a.slice(0, 90)); if (s.indexOf(a, i + 1) >= 0) throw new Error('ambiguous in ' + p + ': ' + a.slice(0, 90)); return i; }; const rep = (a, b) => { once(a); s = s.replace(a, () => b); }; const NL = String.fromCharCode(10); /* Two mechanical sweeps below are regex replace-alls (aria-pressed on the choice chips, aria-checked on the switch rows) and would happily apply themselves a second time. Refuse a re-run before anything is written: a11yAnon is created by this round and exists nowhere in the tree today. */ if (fs.readFileSync(root + 'tests/mobile/smoke.mjs', 'utf8').includes('a11yAnon')) { throw new Error('round 28 is already applied in ' + root + ' — reset the tree rather than re-running'); } /* the thirteen choice chips: one uniform line, so one regex — with the count asserted first, so a chip added by a later round is noticed here rather than quietly skipped */ const CHIP = /{/if} {/if}`, ins('ins-r28-toasts.txt')); save(); // ── the look: the live region, and nothing else ───────────────────────── load('src/mobile/shell.css'); s = s.replace(/\s*$/, NL) + ` /* The one live region: never drawn, never laid out, always there — so an announcement is a change of text inside it rather than a node arriving with its words already in it (PreviewOverlay.svelte's .sr-announce, at phone scale) */ .sr-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } `; save(); // ── the player sheet: a dialog, with somewhere to land ────────────────── load('src/mobile/Sheet.svelte'); rep(`
`, `