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.
This commit is contained in:
1 parent
867fd42972
commit
9f0601027c
400 files changed
+26401
-3
No files matched your search
@@ -0,0 +1,141 @@
|
||||
// Round seven: the artwork viewer (the desktop's lightbox) from the
|
||||
// now-playing art and a record page's cover, one layer the back gesture
|
||||
// peels; and the doc's "not done yet" brought up to date.
|
||||
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); };
|
||||
|
||||
// ── nav: the viewer is one more layer the back gesture peels ──
|
||||
load('src/mobile/nav.svelte.js');
|
||||
rep(` /* the half sheet: { kind, arg } */
|
||||
half: null,
|
||||
});`, ` /* the half sheet: { kind, arg } */
|
||||
half: null,
|
||||
/* the artwork viewer, and the art key it shows */
|
||||
viewer: false,
|
||||
viewerKey: null,
|
||||
});`);
|
||||
rep(`function backables() {
|
||||
return (nav.sheet ? 1 : 0) + (nav.half ? 1 : 0)`, `function backables() {
|
||||
return (nav.viewer ? 1 : 0) + (nav.sheet ? 1 : 0) + (nav.half ? 1 : 0)`);
|
||||
rep(` depth = Math.max(0, depth - 1);
|
||||
if (nav.sheet) nav.sheet = false;
|
||||
else if (nav.half) nav.half = null;`, ` depth = Math.max(0, depth - 1);
|
||||
if (nav.viewer) nav.viewer = false;
|
||||
else if (nav.sheet) nav.sheet = false;
|
||||
else if (nav.half) nav.half = null;`);
|
||||
rep(`export function back() {
|
||||
if (nav.sheet) nav.sheet = false;
|
||||
else if (nav.half) nav.half = null;`, `export function back() {
|
||||
if (nav.viewer) nav.viewer = false;
|
||||
else if (nav.sheet) nav.sheet = false;
|
||||
else if (nav.half) nav.half = null;`);
|
||||
rep(`export function openHalf(kind, arg = null) {`, `export function openViewer(key) {
|
||||
if (!key) return;
|
||||
nav.viewerKey = key;
|
||||
nav.viewer = true;
|
||||
sync();
|
||||
}
|
||||
export function closeViewer() {
|
||||
if (!nav.viewer) return;
|
||||
nav.viewer = false;
|
||||
sync();
|
||||
}
|
||||
export function openHalf(kind, arg = null) {`);
|
||||
save();
|
||||
|
||||
// ── the shell renders it ──
|
||||
load('src/mobile/Mobile.svelte');
|
||||
rep(` import HalfSheet from "./HalfSheet.svelte";`, ` import HalfSheet from "./HalfSheet.svelte";
|
||||
import Viewer from "./Viewer.svelte";`);
|
||||
rep(` <HalfSheet />
|
||||
<Sheet />`, ` <HalfSheet />
|
||||
<Sheet />
|
||||
<Viewer />`);
|
||||
save();
|
||||
|
||||
// ── the now-playing art opens it ──
|
||||
load('src/mobile/Sheet.svelte');
|
||||
rep(` import { nav, closeSheet, morphSource, openHalf, push } from "./nav.svelte.js";`,
|
||||
` import { nav, closeSheet, morphSource, openHalf, openViewer, push } from "./nav.svelte.js";`);
|
||||
rep(` <div class="np-art" class:small>`, ` <div class="np-art" class:small onclick={() => openViewer(player.album?.art_path)} role="presentation">`);
|
||||
save();
|
||||
|
||||
// ── a library record's cover opens it ──
|
||||
load('src/mobile/screens/Album.svelte');
|
||||
rep(` import { push, openHalf } from "../nav.svelte.js";`, ` import { push, openHalf, openViewer } from "../nav.svelte.js";`);
|
||||
rep(` <div class="rec-cover"><Cover path={album.art_path} eager size={48} /></div>`,
|
||||
` <div class="rec-cover" onclick={() => openViewer(album.art_path)} role="presentation"><Cover path={album.art_path} eager size={48} /></div>`);
|
||||
save();
|
||||
|
||||
// ── CSS ──
|
||||
load('src/mobile/shell.css');
|
||||
s = s.replace(/\s*$/, NL) + `
|
||||
/* ── the artwork viewer: the cover alone, over everything ─────────────── */
|
||||
.viewer { position: fixed; inset: 0; z-index: 200; background: #000; display: grid; place-items: center; animation: viewer-in 160ms ease both; }
|
||||
.viewer img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
|
||||
@keyframes viewer-in { from { opacity: 0; } to { opacity: 1; } }
|
||||
`;
|
||||
save();
|
||||
|
||||
// ── the walks ──
|
||||
load('tests/mobile/smoke.mjs');
|
||||
rep(` qrows: document.querySelectorAll('.q-face .row').length,`,
|
||||
` qrows: document.querySelectorAll('.q-face .row').length,
|
||||
viewer: !!document.querySelector('.viewer img'),`);
|
||||
rep(` { do: \`document.querySelector('.mini').click()\`, name: "sheet", wait: 1100 },`,
|
||||
` { do: \`document.querySelector('.mini').click()\`, name: "sheet", wait: 1100 },
|
||||
/* the art opens the viewer; the back gesture closes it */
|
||||
{ do: \`document.querySelector('.np-art').click()\`, name: "viewer", wait: 900 },
|
||||
{ do: \`history.back()\`, name: "viewer-closed", wait: 700 },`);
|
||||
afterLine(' album: (i) => i.screen.includes("tr-push") && !i.mini,', ` viewer: (i) => i.viewer,
|
||||
"viewer-closed": (i) => !i.viewer && i.sheet.includes("up"),
|
||||
`);
|
||||
afterLine(' "album-playing": (i) => i.mini && !i.toast,', ` viewer: (i) => i.viewer,
|
||||
"viewer-closed": (i) => !i.viewer && i.sheet.includes("up"),
|
||||
`);
|
||||
rep(` { do: \`document.querySelector('.mini').click()\`, name: "sheet", wait: 1400 },`,
|
||||
` { do: \`document.querySelector('.mini').click()\`, name: "sheet", wait: 1400 },
|
||||
{ do: \`document.querySelector('.np-art').click()\`, name: "viewer", wait: 1200 },
|
||||
{ do: \`history.back()\`, name: "viewer-closed", wait: 800 },`);
|
||||
save();
|
||||
|
||||
// ── the doc: what is not done, today ──
|
||||
load('ANDROID.md');
|
||||
rep(`## Not done yet
|
||||
|
||||
- Import (the desktop's import flow) on the phone.
|
||||
- Save and Transfers exist (You → Transfers, fed by \`soulseek:download\`; a
|
||||
live row cancels on tap; a long press on a catalog row offers the track's
|
||||
own Save).
|
||||
- Community's private layer (Spaces, holdings, the sealed inbox, friends by
|
||||
key) — Freenet on the phone is a later experiment; the network side is in.
|
||||
- Follows and the play history are per library folder, so the phone's own
|
||||
folder starts empty of both; they arrive with a shared library.`,
|
||||
`## Not done yet
|
||||
|
||||
- Community's private layer (Spaces, holdings, the sealed inbox, friends by
|
||||
key, Who has it) — Freenet on the phone is a later experiment; the
|
||||
network side is in. Rooms and chat are built and mock-walked; a live room
|
||||
is Kyle's to join.
|
||||
- The desktop-only doors and tools: the file and folder playlist imports,
|
||||
the listener door and the lyric alignment models (a desktop runtime),
|
||||
playlist export and the cover picker, drag reorder, catalog track
|
||||
previews, the full discography page, the artist-artwork upscaler,
|
||||
updates and the account section (sign-in lives on Community).
|
||||
- Follows and the play history are per library folder, so the phone's own
|
||||
folder starts empty of both; they arrive with a shared library.
|
||||
- The artwork viewer shows the cover at full size; pinch zoom is not wired.`);
|
||||
save();
|
||||
console.log('round seven wired');
|
||||
Reference in new issue
Block a user