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/contextlost-micro.mjs
// Micro-check: T4 context-lost wiring — is ctx2d actually nulled and restored,
// and can the loop keep "drawing" into a dead context (silent no-op = the
// "wind disappeared entirely" failure mode)?
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
import { join, extname } from 'node:path';
import { createRequire } from 'node:module';
const require = createRequire('/mnt/Mirror/Projects/Hermes/minesweeper-dx/');
const { chromium } = require('/mnt/Mirror/Projects/Hermes/minesweeper-dx/node_modules/playwright/index.js');
const dist = '/mnt/Mirror/Projects/Hermes/minesweeper-dx/dist';
const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css' };
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));
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1600, height: 1000 }, deviceScaleFactor: 2 });
await page.goto(`http://127.0.0.1:${server.address().port}/`, { waitUntil: 'load' });
await page.waitForFunction(() => window.minesweeperDx !== undefined);
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.evaluate(() => document.querySelector('#continueBtn')?.click());
}
await page.evaluate(() => { const m = document.querySelector('#shifterIntro'); if (m && !m.hidden) document.querySelector('#shifterIntroOk').click(); });
const r = await page.evaluate(() => new Promise((res) => {
  const fx = window.minesweeperDx.fx;
  const canvas = document.getElementById('fx');
  const before = { ctx2d: fx.ctx2d !== null, raf: fx.raf > 0, wind: fx.wind !== null };
  canvas.dispatchEvent(new Event('contextlost'));
  setTimeout(() => {
    const during = { ctx2d: fx.ctx2d !== null, raf: fx.raf > 0, wind: fx.wind !== null };
    canvas.dispatchEvent(new Event('contextrestored'));
    setTimeout(() => {
      const after = { ctx2d: fx.ctx2d !== null, raf: fx.raf > 0, wind: fx.wind !== null, w: canvas.width, h: canvas.height };
      res({ before, during, after });
    }, 1200);
  }, 1200);
}));
console.log(JSON.stringify(r));
await browser.close();
server.close();