Back to Shiftfield
SOURCE / PINNED RELEASE

Made of little things.

Shiftfield

Release
5a170c505f6b…
Author-recorded commit
a2e23e0171aa…
License
LICENSE
Author’s source reference
nostr://npub16nzc4ql748r2kv9202rgzrt0fmtk4n3tzsjl86yf6ty9agqe9u0qjcc7hc/wss%3A%2F%2Fgit.napplet.soy%2F/n-e4fb6341ccd

Archive hash verified: 66de7623efd91da1…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tests/telegraph-paint-probe.mjs
// Probe 2: telegraph paint/cadence audit at depth 6.
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
import { join, extname } from 'node:path';
import { chromium } from 'playwright';

const dist = '/mnt/Mirror/Projects/Hermes/minesweeper-dx/dist';
const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css' };
async function serveDist() {
  const server = createServer(async (req, res) => {
    const p = req.url === '/' ? '/index.html' : req.url.split('?')[0];
    try {
      const body = await readFile(join(dist, p.slice(1)));
      res.writeHead(200, { 'content-type': MIME[extname(p)] ?? 'application/octet-stream' });
      res.end(body);
    } catch { res.writeHead(403).end('denied'); }
  });
  await new Promise((ok) => server.listen(0, '127.0.0.1', ok));
  return { url: `http://127.0.0.1:${server.address().port}/`, close: () => server.close() };
}
const svc = await serveDist();
const browser = await chromium.launch();

async function run(tag, dpr, cpuRate, hiRes) {
  const page = await browser.newPage({
    viewport: hiRes ? { width: 1600, height: 1000 } : { width: 900, height: 600 },
    deviceScaleFactor: dpr,
  });
  await page.goto(svc.url, { waitUntil: 'load' });
  await page.waitForFunction(() => window.minesweeperDx !== undefined);
  await page.evaluate(() => {
    const app = window.minesweeperDx;
    const fx = app.fx;
    window.__p = { anims: [], events: [] };
    const oe = fx.event.bind(fx);
    fx.event = (ev) => { window.__p.events.push(ev.kind); return oe(ev); };
  });
  await page.click('#startRunBtn');
  for (let d = 0; d < 6; d++) {
    await page.evaluate(() => {
      const g = window.minesweeperDx; const eng = g.board.game;
      for (const t of eng.world.tiles) if (!t.hasMine && t.state === 'HIDDEN') eng.reveal(t.coord[0], t.coord[1]);
      g.board.refresh(); g.sync(eng);
    });
    await page.click('#continueBtn');
  }
  await page.evaluate(() => { const m = document.querySelector('#shifterIntro'); if (m && !m.hidden) document.querySelector('#shifterIntroOk').click(); });
  await page.waitForTimeout(400);

  const cdp = await page.context().newCDPSession(page);
  if (cpuRate > 1) await cdp.send('Emulation.setCPUThrottlingRate', { rate: cpuRate });

  async function trace(ms, action) {
    const chunks = [];
    cdp.on('Tracing.dataCollected', (e) => chunks.push(...e.value));
    await cdp.send('Tracing.start', {
      includedCategories: ['devtools.timeline', 'disabled-by-default-devtools.timeline', 'cc'],
      transferMode: 'ReportEvents',
    });
    // rAF cadence recorder inside the page
    await page.evaluate(() => {
      window.__dt = []; let last = performance.now();
      const tick = (t) => { window.__dt.push(t - last); last = t; if (window.__dtOn) requestAnimationFrame(tick); };
      window.__dtOn = true; requestAnimationFrame(tick);
    });
    if (action) await action();
    await page.waitForTimeout(ms);
    await page.evaluate(() => { window.__dtOn = false; });
    const done = new Promise((ok) => cdp.once('Tracing.tracingComplete', ok));
    await cdp.send('Tracing.end');
    await done;
    cdp.removeAllListeners('Tracing.dataCollected');
    const counts = {};
    let paintDur = 0, layoutDur = 0, recalcDur = 0;
    for (const e of chunks) {
      if (e.ph === 'X' || e.ph === 'B') counts[e.name] = (counts[e.name] ?? 0) + 1;
      if (e.ph === 'X') {
        if (e.name === 'Paint') paintDur += e.dur ?? 0;
        if (e.name === 'Layout') layoutDur += e.dur ?? 0;
        if (e.name === 'Update Rendering Statistics' || e.name === 'Style recalculation') recalcDur += e.dur ?? 0;
      }
    }
    const dt = await page.evaluate(() => {
      const a = window.__dt.slice(1).sort((x, y) => x - y);
      const p = (q) => a[Math.floor(a.length * q)] ?? 0;
      return { frames: a.length, med: +p(0.5).toFixed(1), p95: +p(0.95).toFixed(1), over33: a.filter((x) => x > 33).length };
    });
    const anims = await page.evaluate(() => {
      const all = document.getAnimations();
      const by = {};
      for (const an of all) { const n = an.animationName || 'css'; by[n] = (by[n] ?? 0) + 1; }
      return by;
    });
    const pick = (n) => counts[n] ?? 0;
    console.log(`[leg ${tag}]`, JSON.stringify({
      Paint: pick('Paint'), RasterTask: pick('RasterTask'), Layout: pick('Layout'),
      PrePaint: pick('PrePaint2'), PaintImage: pick('PaintImage'),
      produceCanvas: pick('ProduceCanvasResource'), commit: pick('Commit'),
      paintMs: +(paintDur / 1000).toFixed(1), layoutMs: +(layoutDur / 1000).toFixed(1),
      styleRecalcMs: +(recalcDur / 1000).toFixed(1),
      rAF: dt, anims,
    }));
  }

  // leg A: wind-only idle at depth 6 (countdown far from shift)
  await trace(2500, null);
  // leg B: 24 flag turns — covers warning beats (3 turns) + a shift landing
  const act = async () => {
    const a = page.locator('.cell[data-x="0"][data-y="0"]');
    const b = page.locator('.cell[data-x="1"][data-y="0"]');
    for (let i = 0; i < 24; i++) await (i % 2 ? a : b).click({ button: 'right' });
    // sample animations mid-pulse
    const an = await page.evaluate(() => {
      const all = document.getAnimations();
      const by = {};
      for (const an2 of all) { const n = an2.animationName || 'css'; by[n] = (by[n] ?? 0) + 1; }
      return { midPulse: by, pulses: document.querySelectorAll('.cell-count-pulse').length };
    });
    console.log(`[leg ${tag} mid-pulse]`, JSON.stringify(an));
  };
  await trace(3000, act);
  await page.close();
}

await run('d6 dpr1 cpu1', 1, 1, false);
await run('d6 dpr2 cpu1', 2, 1, true);
await run('d6 dpr1 cpu4', 1, 4, false);
await browser.close();
svc.close();