android: the room can measure the song, and a cold artist page stops waiting on one call
The tap-to-sync room draws the recording's own vocal band (lyrics_vocal_activity, the desktop's coarse envelope) as a strip under the head, and one button lays every line across the stretches it says are sung — the desktop's own generator, shared and unit-tested, which refuses rather than guesses when there is too little voice to read. The result is labelled an estimate everywhere it shows, the cursor goes back to the top so the taps correct it, and one more tap drops the whole thing back to an empty set, because a rough fill stamps every line at once and leaves Undo nothing to take back. The artist page now fires the hero seed and the overview beside the full page, the way the desktop stages them: a hero Kyle picked on this phone comes back off the file with no network at all, the discography paints from the overview's first page until the exhaustive pass lands with the classified one, and both lanes are dropped with the page when a hero changes. ANDROID.md's "Not done yet" is rewritten as the true map of the port — what is absent by design and why, what is waiting on Rust rather than on a file picker, and one bullet about the artwork viewer that had been done for rounds.
This commit is contained in:
1 parent
5738c2d7bf
commit
44eda8dff5
9 files changed
+374
-39
No files matched your search
+44
-2
@@ -251,6 +251,24 @@ const PROBE = `(function () {
|
||||
halfSub: ((document.querySelector('.halfsheet .sheet-head .m') || {}).textContent || '').trim(),
|
||||
halfSw: [].filter.call(document.querySelectorAll('.halfsheet .row'), function (r) { return !!r.querySelector('.sw.on'); }).map(function (r) { return ((r.querySelector('.t') || {}).textContent || '').trim(); }).join('|'),
|
||||
contactsLoaded: (function () { try { return __ss.contacts.loaded ? 'yes' : (__ss.contacts.error ? 'err' : 'no'); } catch (e) { return ''; } })(),
|
||||
syStrip: document.querySelectorAll('.sy-strip .sy-col').length,
|
||||
syTicks: document.querySelectorAll('.sy-strip .sy-tick').length,
|
||||
syEst: (document.querySelector('.sy-est') || {}).textContent || '',
|
||||
syHint: (document.querySelector('.sy-hint') || {}).textContent || '',
|
||||
disco: document.querySelectorAll('.grid2 .release').length,
|
||||
/* the artist page's three lanes at once — the page, the hero seed and
|
||||
the overview — so a step can say what painted and what had not
|
||||
landed yet: "<page>:<seed>[+art]:<overview>:<releases>" */
|
||||
artFast: (function () {
|
||||
try {
|
||||
var t = __ss.top();
|
||||
if (!t || t.n !== 'artist') return '';
|
||||
var a = __ss.catalog.artists[String(t.c.name || '').trim().toLowerCase()];
|
||||
if (!a) return 'no-entry';
|
||||
var h = __ss.catalog.heroSeeds[a.id] || {}, o = __ss.catalog.overviews[a.id] || {};
|
||||
return a.status + ':' + (h.status || '-') + (h.banner ? '+art' : '') + ':' + (o.status || '-') + ':' + ((o.albums || []).length);
|
||||
} catch (e) { return ''; }
|
||||
})(),
|
||||
/* the number this round exists to hold at zero, and who broke it */
|
||||
a11yAnon: a11yNone.length,
|
||||
a11yWho: a11yNone.slice(0, 3).map(whatis).join(' | '),
|
||||
@@ -376,6 +394,10 @@ const MOCK_STEPS = [
|
||||
/* the tap-to-sync room: taps become the timing, one undone, the room
|
||||
peeled by the back gesture and the set offered back, then saved */
|
||||
{ do: `__click('.halfsheet .row', 'Re-time by hand')`, name: "sync-room", wait: 800 },
|
||||
/* the envelope is measured on open; the estimate lays every line across
|
||||
the sung stretches, and dropping it puts the room back to the tap */
|
||||
{ do: `__click('.sy-est', null)`, name: "sync-estimated", wait: 700 },
|
||||
{ do: `__click('.sy-est', null)`, name: "sync-cleared", wait: 600 },
|
||||
{ do: `__click('.sy-pad', null)`, wait: 400 },
|
||||
{ do: `__click('.sy-pad', null)`, name: "sync-tapped", wait: 500 },
|
||||
{ do: `__click('.sy-undo', null)`, name: "sync-undone", wait: 500 },
|
||||
@@ -467,6 +489,11 @@ const MOCK_STEPS = [
|
||||
{ do: `__click('.halfsheet .row', 'Choose hero photo')`, name: "artist-hero-set", wait: 1400 },
|
||||
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "artist-more-custom", wait: 800 },
|
||||
{ do: `__click('.halfsheet .row', 'Remove custom hero')`, name: "artist-hero-cleared", wait: 1400 },
|
||||
/* an artist whose exhaustive page the mock keeps for four seconds: what
|
||||
is on screen a second and a half in can only be the two fast lanes */
|
||||
{ do: `__ss.push('artist', { name: 'Halcyon Drift' })`, name: "artist-lanes", wait: 1200 },
|
||||
{ name: "artist-lanes-page", wait: 4200 },
|
||||
{ do: `history.back()`, wait: 700 },
|
||||
{ do: `__click('.rail .acard', 'Justice')`, name: "cat-artist-related", wait: 1800 },
|
||||
{ do: `history.back()`, wait: 600 },
|
||||
{ do: `history.back()`, wait: 800 },
|
||||
@@ -800,7 +827,9 @@ const MOCK_EXPECT = {
|
||||
"lyrics-pasted": (i) => !i.half.includes("up") && i.lyrLines === 2,
|
||||
"lyrics-autosync-off": (i) => i.half.includes("up") && i.lyrAuto === "off",
|
||||
"lyrics-autosync-on": (i) => i.half.includes("up") && i.lyrAuto === "on",
|
||||
"sync-room": (i) => i.sync && i.syCount === "0 / 3" && !!i.syNow,
|
||||
"sync-room": (i) => i.sync && i.syCount === "0 / 3" && !!i.syNow && i.syStrip === 64 && /Estimate the timing/.test(i.syEst),
|
||||
"sync-estimated": (i) => i.sync && i.syCount === "3 / 3" && i.syTicks === 3 && /Estimated/.test(i.syHint) && /Clear the estimate/.test(i.syEst),
|
||||
"sync-cleared": (i) => i.sync && i.syCount === "0 / 3" && i.syTicks === 0 && /Estimate the timing/.test(i.syEst),
|
||||
"sync-tapped": (i) => i.sync && i.syCount === "2 / 3",
|
||||
"sync-undone": (i) => i.sync && i.syCount === "1 / 3",
|
||||
"sync-left": (i) => !i.sync && i.half.includes("up") && /1 of 3 lines timed/.test(i.halfText),
|
||||
@@ -843,6 +872,11 @@ const MOCK_EXPECT = {
|
||||
"artist-hero-set": (i) => !i.half.includes("up") && /3840×2160/.test(i.notice),
|
||||
"artist-more-custom": (i) => i.half.includes("up") && /Remove custom hero/.test(i.halfText) && i.heroPix,
|
||||
"artist-hero-cleared": (i) => !i.half.includes("up") && /photos are back/.test(i.notice),
|
||||
/* the exhaustive page has NOT landed; the banner and the first four
|
||||
releases have — three albums on the Albums tab, a single on its own */
|
||||
"artist-lanes": (i) => i.artFast === "loading:ready+art:ready:4" && i.heroPix && i.disco === 3 && i.tabs === 2,
|
||||
/* and the page wins when it does land: the classified six */
|
||||
"artist-lanes-page": (i) => /^ready:/.test(i.artFast) && i.heroPix && i.disco === 6 && i.tabs === 2,
|
||||
"net-playing": (i) => i.mini && i.playing.includes("One More Time") && /streaming from|copy/.test(i.live),
|
||||
"net-sheet": (i) => i.sheet.includes("up") && i.lane.includes("rising"),
|
||||
"net-arrived": (i) => i.sheet.includes("up") && !i.lane.includes("rising"),
|
||||
@@ -962,6 +996,9 @@ const PHONE_STEPS = [
|
||||
{ do: `(function () { var p = document.querySelector('.sy-pad'); if (p) p.click(); })()`, wait: 700 },
|
||||
{ do: `(function () { var p = document.querySelector('.sy-pad'); if (p) p.click(); })()`, name: "sync-tapped", wait: 700 },
|
||||
{ do: `(function () { var u = document.querySelector('.sy-undo'); if (u) u.click(); })()`, name: "sync-undone", wait: 700 },
|
||||
/* the real DSP on a real file: the envelope is a whole-file decode, so
|
||||
give it room. Estimating writes nothing — the room is discarded next */
|
||||
{ do: `(function () { var b = document.querySelector('.sy-est'); if (b && !b.disabled) b.click(); })()`, name: "sync-estimated", wait: 2500 },
|
||||
{ do: `(function () { var c = document.querySelector('.sy-close'); if (c) c.click(); })()`, wait: 500 },
|
||||
{ do: `(function () { var c = document.querySelector('.sy-close'); if (c) c.click(); })()`, name: "sync-discarded", wait: 800 },
|
||||
{ do: `document.querySelector('.scrim').click()`, wait: 500 },
|
||||
@@ -1108,6 +1145,10 @@ const PHONE_EXPECT = {
|
||||
tracks: (i) => i.title.startsWith("Your library"),
|
||||
"contacts-read": (i) => i.contactsLoaded === "yes" && !i.toast,
|
||||
"artist-cold": (i) => i.screen.includes("has-hero"),
|
||||
"sync-estimated": (i) => !i.toast && (!i.sync
|
||||
|| (/Clear the estimate/.test(i.syEst)
|
||||
? i.syTicks > 0 && i.syStrip === 64
|
||||
: /Estimate the timing|Reading the song|No estimate/.test(i.syEst))),
|
||||
you: (i) => i.title.startsWith("You") && !!i.norm && !!i.srcMax && !i.a11yAnon && !i.a11yGhost && !i.a11yState,
|
||||
"you-norm-off": (i) => i.norm === "off" && !i.toast,
|
||||
"you-norm-on": (i) => i.norm === "on" && !i.toast,
|
||||
@@ -1152,7 +1193,8 @@ const PHONE_EXPECT = {
|
||||
"import-folder-cancelled": (i) => i.title.startsWith("Your library") && !i.half.includes("up") && !i.toast,
|
||||
"share-import": (i) => i.title.startsWith("Import") && (i.imp > 0 || i.impErr),
|
||||
"share-cancel": (i) => i.title.startsWith("Your library"),
|
||||
artist: (i) => i.screen.includes("has-hero") && i.amb.includes("on:img") && i.tabs >= 1,
|
||||
artist: (i) => i.screen.includes("has-hero") && i.amb.includes("on:img") && i.tabs >= 1
|
||||
&& /^[a-z]+:(ready\+art|none):(ready|error):\d+$/.test(i.artFast),
|
||||
"artist-more": (i) => i.half.includes("up") && /Choose hero photo/.test(i.halfText),
|
||||
"catalog-album": (i) => i.screen.includes("tr-push") && !!i.title,
|
||||
end: (i) => /^(Good |Still)/.test(i.title),
|
||||
|
||||
Reference in new issue
Block a user