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.
118 lines
6.7 KiB
JavaScript
118 lines
6.7 KiB
JavaScript
// Round twenty-seven: the phone turned sideways. The UI was drawn
|
||
// portrait-first and had never been looked at in landscape; the player sheet
|
||
// was the casualty (a cover sized by WIDTH inside a sheet that does not
|
||
// scroll, so at 915x411 the transport sat 687px below the fold). Two media
|
||
// queries answer it, the shell reads the side insets the cutout and the
|
||
// gesture pill take in landscape, and the walk turns the phone over itself:
|
||
// `{ view: [w, h] }` is a step kind now.
|
||
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);
|
||
|
||
/* The shell.css block is an APPEND with no anchor, so `once` cannot see a
|
||
second run coming: it would simply be written twice. Refuse one here,
|
||
before anything is touched. That string exists nowhere but in the block
|
||
this round adds. */
|
||
if (fs.readFileSync(root + 'src/mobile/shell.css', 'utf8').includes('a phone turned sideways')) {
|
||
throw new Error('round 27 is already applied in ' + root + ' — reset the tree rather than re-running');
|
||
}
|
||
|
||
// ── two more insets: sideways, the cutout and the pill are at the sides ──
|
||
load('src/mobile/mobile.css');
|
||
rep(` --inset-bottom: env(safe-area-inset-bottom, 0px);`,
|
||
` --inset-bottom: env(safe-area-inset-bottom, 0px);
|
||
/* landscape moves the cutout and the gesture pill to the SIDES, and
|
||
viewport-fit=cover puts our content under both */
|
||
--inset-left: env(safe-area-inset-left, 0px);
|
||
--inset-right: env(safe-area-inset-right, 0px);`);
|
||
save();
|
||
|
||
// ── the look. Appended, and the order is load-bearing: the side-inset
|
||
// longhands must come after .gbar's padding shorthand, .mini's left/right
|
||
// and .toast's left/right to win at equal specificity.
|
||
load('src/mobile/shell.css');
|
||
s = s.replace(/\s*$/, NL) + NL + ins('ins-r27-css.txt') + NL;
|
||
save();
|
||
|
||
// ── the sheet says which shape it is in: `small` is already the derived the
|
||
// artwork uses, and the landscape grid needs it on .np as a plain class
|
||
// rather than a :has()
|
||
load('src/mobile/Sheet.svelte');
|
||
rep(` <div class="np">`, ` <div class="np" class:small>`);
|
||
save();
|
||
|
||
// ── the walk ──
|
||
load('tests/mobile/smoke.mjs');
|
||
// the geometry the orientation steps read back
|
||
rep(`const PROBE = \`(function () {
|
||
var e = document.querySelector('.screen');
|
||
return {`,
|
||
`const PROBE = \`(function () {
|
||
var e = document.querySelector('.screen');
|
||
/* geometry, for the orientation steps: box() is the rect or null, and
|
||
inside() asks whether every pixel of it is within the viewport —
|
||
which, sideways, is the whole question */
|
||
function box(sel) { var x = document.querySelector(sel); return x ? x.getBoundingClientRect() : null; }
|
||
function inside(b) { return !!b && b.height > 0 && b.top >= -1 && b.bottom <= innerHeight + 1 && b.left >= -1 && b.right <= innerWidth + 1; }
|
||
return {`);
|
||
rep(` contactsLoaded: (function () { try { return __ss.contacts.loaded ? 'yes' : (__ss.contacts.error ? 'err' : 'no'); } catch (e) { return ''; } })(),`,
|
||
` contactsLoaded: (function () { try { return __ss.contacts.loaded ? 'yes' : (__ss.contacts.error ? 'err' : 'no'); } catch (e) { return ''; } })(),` + NL + ins('ins-r27-probe.txt'));
|
||
// the new step kind, beside the adb one
|
||
rep(` /* a phone step can be an adb command — an intent, a key */
|
||
if (s.adb) {
|
||
try { adb(...s.adb); } catch (e) { problems.push("ADB " + (s.name || s.adb.join(" ")) + ": " + e.message); }
|
||
}`,
|
||
` /* a phone step can be an adb command — an intent, a key */
|
||
if (s.adb) {
|
||
try { adb(...s.adb); } catch (e) { problems.push("ADB " + (s.name || s.adb.join(" ")) + ": " + e.message); }
|
||
}
|
||
/* a mock step can turn the phone over: the viewport, through the
|
||
same override the walk boots with. A real device's orientation
|
||
is not the harness's to set, so a view step on the phone walk
|
||
is a failure rather than a no-op. send() resolves with the
|
||
whole reply and does not reject on a protocol error, so a
|
||
refused override has to be read out of it. */
|
||
if (s.view) {
|
||
if (PHONE) problems.push("VIEW " + (s.name || s.view.join("x")) + ": the phone walk must not set the viewport");
|
||
else {
|
||
try {
|
||
const r = await cdp.send("Emulation.setDeviceMetricsOverride", { width: s.view[0], height: s.view[1], deviceScaleFactor: 2, mobile: true });
|
||
if (r.error) throw new Error(r.error.message || JSON.stringify(r.error));
|
||
} catch (e) { problems.push("VIEW " + (s.name || s.view.join("x")) + ": " + e.message); }
|
||
}
|
||
}`);
|
||
rep(` { do: \`__click('.halfsheet .chip', '30 min')\`, name: "sleep-set", wait: 700 },`,
|
||
` { do: \`__click('.halfsheet .chip', '30 min')\`, name: "sleep-set", wait: 700 },` + NL + ins('ins-r27-steps.txt'));
|
||
rep(` "sleep-set": (i) => !i.half.includes("up"),`,
|
||
` "sleep-set": (i) => !i.half.includes("up"),` + NL + ins('ins-r27-expect.txt'));
|
||
save();
|
||
|
||
// ── the doc ──
|
||
load('ANDROID.md');
|
||
rep("`linear()`. Reduce motion (You → Appearance, or the OS setting) turns it off." + NL + NL + `## Search and the resolver`,
|
||
"`linear()`. Reduce motion (You → Appearance, or the OS setting) turns it off." + NL + NL + ins('ins-r27-doc.txt') + NL + NL + `## Search and the resolver`);
|
||
rep(`the sleep timer, asserts what each step should show,`,
|
||
`the sleep timer → the phone turned sideways (the player's three faces, a
|
||
record with a long sheet over it, an artist page, and the side insets faked,
|
||
all at 915×411) and back, asserts what each step should show,`);
|
||
rep(`page after a confirmed delete was found).`,
|
||
`page after a confirmed delete was found). A mock step can also set the
|
||
viewport — \`{ view: [915, 411] }\` sends \`Emulation.setDeviceMetricsOverride\`
|
||
through the same client, which is how the walk turns the phone over and back;
|
||
the phone walk refuses one, because a real device's orientation is not the
|
||
harness's to set.`);
|
||
save();
|
||
console.log('round 27 wired');
|