SOURCE / PINNED RELEASE
Made of little things.
Napcraft
- Release
- ee47ac6a25df…
- Author-recorded commit
- 410dea87e109…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-52f9e22f5ce
Archive hash verified: 9e6e0cabb18d0d46…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import {
chromium,
executable,
frameOf,
fitFrame,
connectTestIdentity,
approveLocalPrompts,
} from './browser-support.mjs';
import { startLocalRelay, useLocalRelay, browserArgs } from './local-relay.mjs';
import { mkdir, writeFile, readFile } from 'node:fs/promises';
import { createHash } from 'node:crypto';
import { blockPoint } from './direct-building.mjs';
const artifactHash = createHash('sha256')
.update(await readFile('dist/index.html'))
.digest('hex');
const dir = '.napplet-space/promotion';
await mkdir(dir, { recursive: true });
const relay = await startLocalRelay();
let browser;
try {
browser = await chromium.launch({
executablePath: executable,
headless: true,
args: browserArgs,
});
const players = [];
for (let i = 0; i < 2; i++) {
const ctx = await browser.newContext({
viewport: { width: 960, height: 600 },
deviceScaleFactor: 1,
...(i === 0
? {
recordVideo: {
dir: `${dir}/raw`,
size: { width: 960, height: 600 },
},
}
: {}),
});
await useLocalRelay(ctx, relay.config, 0, 0);
const page = await ctx.newPage();
await page.goto(process.env.NAPCRAFT_URL || 'http://127.0.0.1:51696/');
const stop = approveLocalPrompts(page);
players.push({ page, frame: await frameOf(page), ctx, stop });
}
const [a, b] = players;
await connectTestIdentity(a.page);
await a.frame.locator('#create').click();
await a.frame.locator('#name').fill('Cloudberry Club');
await a.frame.locator('#confirmCreate').click();
await a.frame.locator('#share').waitFor({ state: 'visible' });
await a.frame.locator('#share').click();
const code = await a.frame.locator('#shareCode').inputValue();
await a.frame.locator('#close').click();
await b.frame.locator('#join').click();
await b.frame.locator('#code').fill(code);
await b.frame.locator('#confirmJoin').click();
await b.frame.locator('#share').waitFor({ state: 'visible' });
await a.frame.waitForFunction(
() => document.querySelector('canvas').dataset.peerCount === '1',
{},
{ timeout: 30000 },
);
await b.frame.locator('canvas').focus();
await b.page.keyboard.down('ArrowLeft');
await b.page.waitForTimeout(650);
await b.page.keyboard.up('ArrowLeft');
await a.frame.getByRole('button', { name: 'Coral', exact: true }).click();
await a.frame.locator('canvas').click({position:await blockPoint(a.frame,7,11)});
await a.page.waitForTimeout(850);
await a.frame.locator('canvas').click({position:await blockPoint(a.frame,7,11,4)});
await a.page.waitForTimeout(900);
await fitFrame(a.page, 1200, 750);
await a.page.waitForTimeout(500);
await a.page
.locator('#stage iframe')
.screenshot({ path: `${dir}/napcraft-cover.png` });
await fitFrame(a.page, 390, 844);
const touch = await a.ctx.newCDPSession(a.page);
await touch.send('Emulation.setTouchEmulationEnabled', { enabled: true });
await a.page.waitForTimeout(600);
await a.page
.locator('#stage iframe')
.screenshot({ path: `${dir}/napcraft-portrait.png` });
await touch.send('Emulation.setTouchEmulationEnabled', { enabled: false });
await touch.detach();
await fitFrame(a.page, 960, 600);
await a.page.waitForTimeout(300);
// The final seven seconds are the published clip candidate, all real gameplay.
const start = Date.now();
await a.frame.locator('canvas').focus();
await a.page.keyboard.down('ArrowDown');
await a.page.waitForTimeout(420);
await a.page.keyboard.up('ArrowDown');
await a.frame.getByRole('button', { name: 'Butter', exact: true }).click();
await a.frame.locator('canvas').click({position:await blockPoint(a.frame,6,10)});
await a.page.waitForTimeout(900);
await a.frame.locator('canvas').click({position:await blockPoint(a.frame,6,10,4)});
await a.page.waitForTimeout(900);
await a.frame.getByRole('button', { name: 'Glacier', exact: true }).click();
await a.frame.locator('canvas').click({position:await blockPoint(a.frame,6,10,5)});
await b.frame.locator('canvas').focus();
await b.page.keyboard.down('ArrowDown');
await b.page.waitForTimeout(500);
await b.page.keyboard.up('ArrowDown');
await b.frame.getByRole('button', { name: 'Coral', exact: true }).click();
await b.frame.locator('#place').click();
await a.page.waitForTimeout(900);
await a.frame.locator('canvas').click({position:await blockPoint(a.frame,6,10,6),button:'right'});
await a.page.waitForTimeout(Math.max(0, 7000 - (Date.now() - start)));
if (
createHash('sha256')
.update(await readFile('dist/index.html'))
.digest('hex') !== artifactHash
)
throw Error('Build changed during capture');
const video = a.page.video();
for (const p of players) p.stop();
await a.ctx.close();
const raw = await video.path();
await writeFile(
`${dir}/capture.json`,
JSON.stringify(
{
raw,
artifactHash,
trimLastSeconds: 7,
viewport: [960, 600],
world: 'Cloudberry Club',
players: 2,
network: 'local authenticated TURN, real soyLI host',
caption:
'A little island for big ideas. Wander, stack, and build together.',
},
null,
2,
),
);
console.log(
'Captured landscape cover, portrait image and raw two-player gameplay',
raw,
);
} finally {
await browser?.close();
await relay.close();
}
