SOURCE / PINNED RELEASE
Made of little things.
Napplet Machines V8
- Release
- a48ffab2ae1e…
- Author-recorded commit
- 2a817e4d389e…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b5572362d4a
Archive hash verified: ecd5161370161eeb…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { mkdir } from 'node:fs/promises';
export default async ({ players, check }) => {
const { page, frame } = players[0], element = await frame.frameElement();
await mkdir('.napplet-space/promotion', { recursive: true });
const resize = async (w, h) => {
await page.setViewportSize({ width: Math.max(320, w), height: Math.max(300, h) });
await element.evaluate((el, { w, h }) => Object.assign(el.style, {
position: 'fixed', top: '0', left: '0', width: `${w}px`, height: `${h}px`,
minHeight: '0', minWidth: '0', maxWidth: 'none', border: '0', zIndex: '9999',
}), { w, h });
await frame.waitForFunction(({ w, h }) => innerWidth === w && innerHeight === h, { w, h });
};
const facts = () => frame.locator('#scene').evaluate(el => ({ ...el.dataset }));
const start = async () => {
await frame.locator('#raceButton').click();
await frame.waitForFunction(() => document.querySelector('#scene').dataset.countdown === '0');
};
const inside = async () => frame.locator('#scene').evaluate(el => {
const s = el.dataset, r = el.getBoundingClientRect();
return [1, 2].every(i => Number(s[`player${i}ScreenX`]) > r.width * 0.08 &&
Number(s[`player${i}ScreenX`]) < r.width * 0.92 && Number(s[`player${i}ScreenY`]) > r.height * 0.2 &&
Number(s[`player${i}ScreenY`]) < r.height * 0.8);
});
await resize(900, 600);
await frame.locator('#addPlayer').click();
check('adding player creates separate keyboard schemes',
await frame.locator('#inputPlayer1').inputValue() === 'wasd' && await frame.locator('#inputPlayer2').inputValue() === 'arrows');
check('a keyboard scheme cannot be selected by both players',
await frame.locator('#inputPlayer2 option[value=wasd]').evaluate(el => el.disabled));
await frame.locator('#carPlayer2').selectOption('2');
await frame.locator('#gridSize').selectOption('2');
// Use the level, walled course to make boost/recovery measurements repeatable.
await frame.locator('#trackSelect').selectOption('1');
await frame.locator('#trackSelect').dispatchEvent('change');
await start();
check('two humans replace AI in the two-car grid', (await frame.locator('#raceOrder').innerText()).includes('Player 2') && await frame.locator('#raceOrder li').count() === 2);
check('both players have a HUD and boost meter', await frame.locator('#playerTwoHud').isVisible() && await frame.locator('#boostMeterTwo').getAttribute('value') === '1');
await page.waitForTimeout(900);
const initialSpan = Number((await facts()).cameraSpan);
await page.keyboard.down('w'); await page.keyboard.down('a'); await page.keyboard.down('ShiftLeft');
await page.keyboard.down('ArrowDown'); await page.keyboard.down('ArrowRight');
await page.waitForTimeout(450);
let s = await facts();
check('WASD and arrows drive different cars simultaneously', Number(s.inputThrottle) === 1 && Number(s.inputTwoThrottle) === -1 && Number(s.inputSteer) === 1 && Number(s.inputTwoSteer) === -1);
check('left Shift drains only player one boost', Number(s.localBoost) < 0.95 && Number(s.playerTwoBoost) > 0.999 && s.inputBoost === 'true' && s.inputTwoBoost === 'false');
await page.keyboard.up('a'); await page.keyboard.up('ArrowRight');
await page.waitForTimeout(1500);
s = await facts();
check('shared camera zooms out as players separate', s.cameraMode === 'shared' && Number(s.cameraSpan) > initialSpan + 2);
check('separated players stay within padded screen bounds', await inside());
await element.screenshot({ path: '.napplet-space/promotion/local-two-keyboards.png' });
await page.keyboard.up('ShiftLeft'); await page.keyboard.up('w'); await page.keyboard.up('ArrowDown');
const empty = Number((await facts()).localBoost);
await frame.waitForFunction(empty => Number(document.querySelector('#scene').dataset.localBoost) > empty + 0.1, empty);
check('released boost recharges', Number((await facts()).localBoost) > empty + 0.1);
await page.keyboard.down('ArrowUp'); await page.keyboard.down('ShiftRight'); await page.keyboard.down('Enter');
await page.waitForTimeout(100);
s = await facts();
check('arrow player drift and boost buttons do not affect WASD player', s.inputTwoBrake === 'true' && s.inputTwoBoost === 'true' && s.inputBrake === 'false' && s.inputBoost === 'false');
await page.keyboard.up('Enter'); await page.keyboard.up('ShiftRight'); await page.keyboard.up('ArrowUp');
await page.keyboard.press('Escape');
check('Escape pauses both cars', await frame.locator('#pauseButton').innerText() === 'Resume');
check('pause clears both input assignments', Number((await facts()).inputThrottle) === 0 && Number((await facts()).inputTwoThrottle) === 0);
await frame.locator('#exitRace').click();
// Standard gamepad samples are synthetic; this does not certify physical hardware.
await frame.locator('#inputPlayer2').selectOption('controller');
await frame.locator('#scene').focus();
await frame.evaluate(() => {
window.__pads = [];
window.__pad = index => ({ index, id: `Local test pad ${index}`, mapping: 'standard', connected: true,
axes: [0, 0], buttons: Array.from({ length: 17 }, () => ({ value: 0, pressed: false })) });
window.__pads[3] = window.__pad(3);
Object.defineProperty(navigator, 'getGamepads', { configurable: true, value: () => window.__pads });
});
const padButton = async (slot, button, value) => {
await frame.evaluate(({ slot, button, value }) => { window.__pads[slot].buttons[button].value = value; }, { slot, button, value });
await page.waitForTimeout(80);
};
const tap = async (slot, button) => { await padButton(slot, button, 1); await padButton(slot, button, 0); };
await page.waitForTimeout(100); await tap(3, 0);
check('controller joins only the player assigned to controller', await frame.locator('#inputPlayer1').inputValue() === 'wasd' && (await frame.locator('.local-player[data-player="2"]').innerText()).includes('Pad 4 connected'));
await start();
await padButton(3, 7, 0.75); await padButton(3, 2, 1);
await page.keyboard.down('w');
await page.waitForTimeout(300);
s = await facts();
check('keyboard and controller race simultaneously with separate boost', Number(s.inputThrottle) === 1 && Math.abs(Number(s.inputTwoThrottle) - 0.75) < 0.01 && s.inputBoost === 'false' && s.inputTwoBoost === 'true' && Number(s.playerTwoBoost) < 1);
await page.keyboard.up('w');
await frame.evaluate(() => { window.__pads[3] = null; });
await page.waitForTimeout(100);
check('controller loss pauses the shared race and clears both cars', await frame.locator('#pauseButton').innerText() === 'Resume' && Number((await facts()).inputTwoThrottle) === 0 && Number((await facts()).inputThrottle) === 0);
await frame.evaluate(() => { window.__pads[3] = window.__pad(3); window.__pads[3].buttons[0].value = 1; });
await page.waitForTimeout(120);
check('replacement controller held button cannot reclaim the car', !(await frame.locator('.local-player[data-player="2"]').innerText()).includes('connected'));
await padButton(3, 0, 0); await tap(3, 0);
check('fresh press explicitly rejoins the same player', (await frame.locator('.local-player[data-player="2"]').innerText()).includes('Pad 4 connected'));
await tap(3, 9);
check('rejoined controller can resume both cars', await frame.locator('#pauseButton').innerText() === 'Pause');
await frame.locator('#exitRace').click();
await frame.locator('#inputPlayer1').selectOption('controller');
await frame.locator('#scene').focus();
await frame.evaluate(() => { window.__pads[7] = window.__pad(7); });
await page.waitForTimeout(100); await tap(7, 0);
await start();
await padButton(7, 7, 0.6); await padButton(3, 7, 0.9);
s = await facts();
check('two controllers use distinct non-contiguous slots', Math.abs(Number(s.inputThrottle) - 0.6) < 0.01 && Math.abs(Number(s.inputTwoThrottle) - 0.9) < 0.01);
await padButton(7, 7, 0); await padButton(3, 7, 0);
await frame.locator('#exitRace').click();
await frame.locator('#inputPlayer1').selectOption('wasd');
await frame.locator('#inputPlayer2').selectOption('arrows');
for (const [theme, colors] of Object.entries({ light: { background: '#fafafa', text: '#111111' }, dark: { background: '#111111', text: '#fafafa' } })) {
await element.evaluate((el, colors) => el.contentWindow.postMessage({ type: 'theme.changed', theme: { colors } }, '*'), colors);
for (const [w, h] of [[200, 160], [320, 560], [900, 600], [2400, 1200]]) {
await resize(w, h);
const overflow = await frame.evaluate(() => ({ width: innerWidth, scroll: document.documentElement.scrollWidth, elements: [...document.querySelectorAll('body *')].filter(el => el.getBoundingClientRect().right > innerWidth + 1).map(el => ({ id: el.id, tag: el.tagName, class: el.className, right: el.getBoundingClientRect().right })).slice(0, 15) }));
if (overflow.scroll > overflow.width) { console.log('Overflow', overflow); await element.screenshot({ path: '.napplet-space/promotion/local-overflow.png' }); }
check(`${theme} ${w}×${h} setup fits exact frame`, await frame.evaluate(() => document.documentElement.scrollWidth === innerWidth));
if (w < 280) {
check(`${theme} tiny frame shows resize notice`, await frame.locator('.too-small').isVisible());
continue;
}
await frame.locator('#inputPlayer2').scrollIntoViewIfNeeded();
check(`${theme} ${w}×${h} second player's setup is reachable`, await frame.locator('#inputPlayer2').isVisible());
await start(); await page.waitForTimeout(400);
check(`${theme} ${w}×${h} both HUDs fit without overlapping`, await frame.evaluate(() => {
const a = document.querySelector('.hud-primary').getBoundingClientRect(), b = document.querySelector('#playerTwoHud').getBoundingClientRect();
return a.left >= 0 && b.right <= innerWidth && a.right + 4 <= b.left;
}));
check(`${theme} ${w}×${h} shared camera keeps both cars visible`, await inside());
check(`${theme} theme leaves the app-owned palette intact`, await frame.evaluate(() => getComputedStyle(document.documentElement).getPropertyValue('--bg').trim() === '#282044'));
await element.screenshot({ path: `.napplet-space/promotion/local-${theme}-${w}x${h}.png` });
await frame.locator('#exitRace').click();
}
}
await resize(900, 600);
await frame.locator('#removePlayer').click();
check('removing player returns to solo setup', await frame.locator('.local-player').count() === 1 && !(await frame.locator('#addPlayer').isDisabled()));
await frame.locator('#inputPlayer1').selectOption('touch');
const cdp = await page.context().newCDPSession(page);
await cdp.send('Emulation.setTouchEmulationEnabled', { enabled: true, maxTouchPoints: 5 });
for (const [w, h] of [[390, 844], [360, 640], [844, 390]]) {
await resize(w, h); await start();
const boost = await frame.locator('#touchBoost').boundingBox(), steering = await frame.locator('#steeringPad').boundingBox();
check(`touch boost has a comfortable target ${w}×${h}`, boost.width >= 44 && boost.height >= 44);
await cdp.send('Input.dispatchTouchEvent', { type: 'touchStart', touchPoints: [
{ x: boost.x + boost.width / 2, y: boost.y + boost.height / 2, id: 0 },
{ x: steering.x + steering.width * 0.7, y: steering.y + steering.height * 0.6, id: 1 },
] });
await page.waitForTimeout(250);
s = await facts();
check(`two thumbs steer and boost with forward gas ${w}×${h}`, Number(s.inputThrottle) === 1 && Number(s.inputSteer) < -0.1 && s.inputBoost === 'true' && Number(s.localBoost) < 1);
await cdp.send('Input.dispatchTouchEvent', { type: 'touchCancel', touchPoints: [] });
await page.waitForTimeout(100);
check(`touch cancellation clears boost and steering ${w}×${h}`, (await facts()).inputBoost === 'false' && Number((await facts()).inputSteer) === 0 && await frame.locator('#touchBoost').getAttribute('aria-pressed') === 'false');
await element.screenshot({ path: `.napplet-space/promotion/local-touch-${w}x${h}.png` });
await frame.locator('#exitRace').click();
}
await cdp.detach();
};
