packages/vishot-runner-electron/README.md
Capture raw screenshots from the built stage-tamagotchi Electron app with TypeScript scenarios.
This package is the Electron capture runner. It provides:
src/index.tscapture CLI in src/cli/capture.tsdefineScenario() authoring helper for scenario modulesThis package stops at raw business screenshots. It does not own the scenario modules themselves; those live in @proj-airi/scenarios-stage-tamagotchi-electron.
pnpm -F @proj-airi/stage-tamagotchi build
pnpm -F @proj-airi/vishot-runner-electron capture -- ../../packages/scenarios-stage-tamagotchi-electron/src/scenarios/demo-controls-settings-chat-websocket.ts --output-dir ../../packages/scenarios-stage-tamagotchi-browser/artifacts/raw
To emit AVIF files instead of PNG files:
pnpm -F @proj-airi/vishot-runner-electron capture -- ../../packages/scenarios-stage-tamagotchi-electron/src/scenarios/demo-controls-settings-chat-websocket.ts --output-dir ../../packages/scenarios-stage-tamagotchi-browser/artifacts/raw --format avif
This writes the raw inputs consumed by the browser scene package:
packages/scenarios-stage-tamagotchi-browser/artifacts/raw/00-stage-tamagotchi.pngpackages/scenarios-stage-tamagotchi-browser/artifacts/raw/01-controls-island-expanded.pngpackages/scenarios-stage-tamagotchi-browser/artifacts/raw/02-settings-window.pngpackages/scenarios-stage-tamagotchi-browser/artifacts/raw/03-websocket-settings.pngIf you pass --format avif, the same capture names are emitted as .avif files instead.
Then export the composed browser assets:
pnpm -F @proj-airi/scenarios-stage-tamagotchi-browser capture
In this environment, the raw capture command worked end-to-end after running outside the sandbox because tsx pipe creation was denied inside the sandbox (listen EPERM on the temporary tsx IPC pipe).
vishot-runner-electron launches the built Electron app entrypoint. In local development this can resolve app.getPath('userData') to the Electron profile directory, which means plugin discovery happens under:
~/Library/Application Support/Electron/plugins/v1This may differ from dev:tamagotchi, which often uses:
~/Library/Application Support/@proj-airi/stage-tamagotchi/plugins/v1If plugin-host devtools shows Discovered 0, Plugin manifest not found, or module registration errors during Vishot scenarios, mirror your plugin symlink into the Electron profile plugins directory:
mkdir -p "$HOME/Library/Application Support/Electron/plugins/v1"
ln -sfn "/absolute/path/to/airi-plugin-game-chess/dist" "$HOME/Library/Application Support/Electron/plugins/v1/airi-plugin-game-chess"
To verify the controls-island hearing button specifically:
pnpm -F @proj-airi/stage-tamagotchi build
pnpm -F @proj-airi/vishot-runner-electron capture -- ../../packages/scenarios-stage-tamagotchi-electron/src/scenarios/demo-hearing-dialog.ts --output-dir ./artifacts/hearing-demo
Expected file:
packages/vishot-runner-electron/artifacts/hearing-demo/hearing-dialog.pngimport { defineScenario } from '@proj-airi/vishot-runner-electron'
export default defineScenario({
id: 'settings-connection',
async run({ controlsIsland, settingsWindow, stageWindows, capture }) {
const main = await stageWindows.waitFor('main')
await controlsIsland.expand(main.page)
const settings = await controlsIsland.openSettings(main.page)
const page = await settingsWindow.goToConnection(settings.page)
await capture('connection-settings', page)
},
})
For the controls-island hearing trigger, the runtime also provides:
controlsIsland.openHearing(page)For surfaces built with DialogRoot or DrawerRoot, the runtime now exposes:
dialogs.dismiss(page)drawers.swipeDown(page)drawers.dismiss(page)Example:
import { defineScenario } from '@proj-airi/vishot-runner-electron'
export default defineScenario({
id: 'dismiss-helpers',
async run({ dialogs, drawers, stageWindows }) {
const main = await stageWindows.waitFor('main')
await dialogs.dismiss(main.page)
await drawers.swipeDown(main.page)
await drawers.dismiss(main.page)
},
})
These are best-effort automation helpers. The current behavior is:
Escape, then overlay-corner click fallbackEscape, then overlay-corner click fallbackThey are intended for scenarios where you already opened the dialog or drawer and need a reusable close step.
The settingsWindow surface is navigation-only:
settingsWindow.waitFor(timeout?)settingsWindow.goToConnection(page)It does not open the settings window from the main window for you. The intended flow is:
stageWindows.waitFor('main')controlsIsland.expand(main.page)controlsIsland.openSettings(main.page)settingsWindow.goToConnection(settings.page)@proj-airi/vishot-runner-electron resolves to src/index.ts via the package export surface.packages/scenarios-stage-tamagotchi-browser/artifacts/final, not this package.image artifacts and can run transformer hooks before those raw files are handed to downstream consumers.--format png|avif; AVIF remains an opt-in post-processing step on top of the raw PNG screenshot primitive.