Files
SoulStream/handoff/rounds/patch-round8.cjs
T
KeelrLeek 9f0601027c android: the handoff — the pending rounds, the pipeline, and what is unverified
Everything written this session that has not been through the device: rounds 43
to 48 (each patch adversarially verified, 43's transient-notice expectation
fixed), a partial round 49, the pipeline scripts, the Android Auto design and
the icon comparison mock. Also the two harness changes that were never A/B'd —
adaptive waits, which may only shorten a declared wait and never lengthen it,
and a ship that runs the mock walk and the deploy at the same time. The A/B and
its 255-step baseline are in handoff/tools; main stays at what the phone has
actually walked.
2026-09-14 07:57:41 -05:00

104 lines
7.1 KiB
JavaScript

// Round eight: the catalog record's sheet from any catalog card (a long
// press on the search results' Records rail, Home's feed, the Gaps
// shelf), and the viewer's pinch zoom (CSS side; the component carries the
// gesture).
const fs = require('fs');
const root = 'F:/Coding/SoulStream/';
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);
const afterLine = (anchor, text) => { const i = once(anchor); const nl = s.indexOf(NL, i); s = s.slice(0, nl + 1) + text + s.slice(nl + 1); };
// ── the sheet accepts a card's album (tracks from the store when the page was read) ──
load('src/mobile/HalfSheet.svelte');
rep(` const catPage = $derived(kind === "catalogAlbum-more" ? (catalog.albums[top().c.id]?.page ?? null) : null);`,
` const catPage = $derived.by(() => {
if (kind === "catalogAlbum-more") return catalog.albums[top().c.id]?.page ?? null;
if (kind === "catalogAlbum" && shown?.arg?.album) {
const a = shown.arg.album;
return { album: { ...a, artist: a.artist || a.artistName || "" }, tracks: catalog.albums[a.id]?.page?.tracks ?? [] };
}
return null;
});`);
rep(` {:else if kind === "catalogAlbum-more" && catPage}`, ` {:else if (kind === "catalogAlbum-more" || kind === "catalogAlbum") && catPage}`);
rep(` <div class="sheet-hair"></div>
{@render row("users", "Go to artist", al.artist, act(() => push("artist", { name: al.artist })))}
</div>
{:else if kind === "peerfile" && shown?.arg?.group}`,
` <div class="sheet-hair"></div>
{#if !(top().n === "catalogAlbum" && top().c.id === al.id)}
{@render row("disc", "Go to record", al.title, act(() => push("catalogAlbum", { id: al.id, artist: al.artist, title: al.title })))}
{/if}
{@render row("users", "Go to artist", al.artist, act(() => push("artist", { name: al.artist })))}
</div>
{:else if kind === "peerfile" && shown?.arg?.group}`);
save();
// ── the cards ──
load('src/mobile/screens/Search.svelte');
rep(` <Release title={a.title} sub={[a.artist, yearOf(a.release_date)].filter(Boolean).join(" · ")} url={a.cover} onclick={() => openAlbum(a)} />`,
` <Release title={a.title} sub={[a.artist, yearOf(a.release_date)].filter(Boolean).join(" · ")} url={a.cover} onclick={() => openAlbum(a)} onmore={() => openHalf("catalogAlbum", { album: a })} />`);
save();
load('src/mobile/screens/Home.svelte');
rep(` <Release title={r.title} sub={r.artistName} url={r.cover} badge={age(r.release_date)} onclick={() => openRelease(r)} />`,
` <Release title={r.title} sub={r.artistName} url={r.cover} badge={age(r.release_date)} onclick={() => openRelease(r)} onmore={() => { if (!r.mbOnly && r.id != null) openHalf("catalogAlbum", { album: r }); }} />`);
{
const a = 'badge={r.upcoming ? inDays(r.release_date) : age(r.release_date)} onclick={() => openRele';
const i = once(a);
const lineEnd = s.indexOf(NL, i);
const line = s.slice(s.lastIndexOf(NL, i) + 1, lineEnd);
if (!line.endsWith(' />')) throw new Error('feed card line shape: ' + line.slice(-40));
const fixed = line.slice(0, -3) + ' onmore={() => { if (!r.mbOnly && r.id != null) openHalf("catalogAlbum", { album: r }); }} />';
s = s.slice(0, s.lastIndexOf(NL, i) + 1) + fixed + s.slice(lineEnd);
}
if (!/import \{[^}]*openHalf[^}]*\} from "\.\.\/nav\.svelte\.js"/.test(s)) {
s = s.replace(/import \{ ([^}]*) \} from "\.\.\/nav\.svelte\.js";/, (m, names) => `import { ${names}, openHalf } from "../nav.svelte.js";`);
}
save();
load('src/mobile/screens/Library.svelte');
rep(`url={r.cover} onclick={() => push("catalogAlbum", { id: r.id, artist: ar.name, title: r.title }, screenEl)} />`,
`url={r.cover} onclick={() => push("catalogAlbum", { id: r.id, artist: ar.name, title: r.title }, screenEl)} onmore={() => openHalf("catalogAlbum", { album: { ...r, artist: r.artist || ar.name } })} />`);
save();
// ── the viewer's gesture needs the browser's own touch handling out of the way ──
load('src/mobile/shell.css');
rep(`.viewer { position: fixed; inset: 0; z-index: 200; background: #000; display: grid; place-items: center; animation: viewer-in 160ms ease both; }`,
`.viewer { position: fixed; inset: 0; z-index: 200; background: #000; display: grid; place-items: center; animation: viewer-in 160ms ease both; touch-action: none; overflow: hidden; }`);
rep(`.viewer img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }`,
`.viewer img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; will-change: transform; transform-origin: center; }`);
save();
// ── the walk: a Records-rail card's sheet ──
load('tests/mobile/smoke.mjs');
rep(` { do: \`document.querySelector('.cat-song').dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true }))\`, name: "cat-song-sheet", wait: 600 },`,
` { do: \`document.querySelector('.cat-song').dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true }))\`, name: "cat-song-sheet", wait: 600 },
{ do: \`document.querySelector('.scrim').click()\`, wait: 500 },
/* a Records-rail card's long press: the record's sheet */
{ do: \`(function () { var sec = [].slice.call(document.querySelectorAll('.sec')).find(function (x) { return /^\\\\s*Records/.test(x.textContent); }); var rail = sec && sec.nextElementSibling; rail.querySelector('.release').dispatchEvent(new MouseEvent('contextmenu', { bubbles: true, cancelable: true })); })()\`, name: "cat-record-sheet", wait: 600 },`);
rep(` "cat-song-sheet": (i) => i.half.includes("up") && /Save to library/.test(i.halfText) && /Add to playlist/.test(i.halfText) && /Go to record/.test(i.halfText),`,
` "cat-song-sheet": (i) => i.half.includes("up") && /Save to library/.test(i.halfText) && /Add to playlist/.test(i.halfText) && /Go to record/.test(i.halfText),
"cat-record-sheet": (i) => i.half.includes("up") && /Save to library/.test(i.halfText) && /Go to record/.test(i.halfText) && /Go to artist/.test(i.halfText),`);
save();
// ── the doc ──
load('ANDROID.md');
rep(`- The artwork viewer shows the cover at full size; pinch zoom is not wired.`,
`- The artwork viewer: the cover at full size, pinch to zoom, one finger
pans while zoomed, a tap on the unzoomed cover or the back gesture closes.`);
rep(`the artist. The now-playing sheet's ⋯ on a streamed track`,
`the artist — and the same sheet is a long press on any catalog card (the
results' Records rail, Home's feed, the Gaps shelf). The now-playing
sheet's ⋯ on a streamed track`);
save();
console.log('round eight wired');