// 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(`
{@render row("users", "Go to artist", al.artist, act(() => push("artist", { name: al.artist })))} {:else if kind === "peerfile" && shown?.arg?.group}`, `
{#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 })))} {:else if kind === "peerfile" && shown?.arg?.group}`); save(); // ── the cards ── load('src/mobile/screens/Search.svelte'); rep(` openAlbum(a)} />`, ` openAlbum(a)} onmore={() => openHalf("catalogAlbum", { album: a })} />`); save(); load('src/mobile/screens/Home.svelte'); rep(` openRelease(r)} />`, ` 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');