diff --git a/.agents/skills/use-agent-browser-for-airi/SKILL.md b/.agents/skills/use-agent-browser-for-airi/SKILL.md new file mode 100644 index 000000000..df261d0b0 --- /dev/null +++ b/.agents/skills/use-agent-browser-for-airi/SKILL.md @@ -0,0 +1,64 @@ +--- +name: use-agent-browser-for-airi +description: Test AIRI display-model imports with agent-browser across stage-tamagotchi Electron, stage-web, and stage-pocket mobile web layouts. Use when uploading and verifying contributor-supplied Live2D ZIP, VRM, or MMD ZIP/PMX/PMD files through AIRI's model selector, including onboarding bypass, format-specific import behavior, renderer verification, and mobile-platform coverage decisions. +--- + +# Use Agent Browser for AIRI + +Invoke `$agent-browser` and `$use-agent-browser-with-input-file`. The input-file skill owns file-input discovery, temporary instrumentation, upload commands, and generic post-upload verification. Run `agent-browser skills get core --full` before browser commands. For Electron, also invoke `$agent-browser-electron` and run `agent-browser skills get electron --full`. + +Select the runtime guide: + +- Electron stage-tamagotchi: read [references/electron.md](references/electron.md). +- stage-web: read [references/web.md](references/web.md). +- stage-pocket and mobile coverage: read [references/mobile.md](references/mobile.md). + +## Model import contract + +Use an absolute input path. AIRI currently creates each format input with VueUse `useFileDialog` and keeps it detached from the DOM, so all three formats use the [detached-input method](../use-agent-browser-with-input-file/references/detached-input.md). Apply that method before clicking the format menu item, then use the matching selector and format-specific postcondition: + +| Format | Menu item | Input selector | Format-specific result | +| --- | --- | --- | --- | +| Live2D | `Live2D` | `input[data-agent-browser-upload][accept=".zip"]` | Inspect the validation report and click `Confirm` only when import is permitted. | +| VRM | `VRM` | `input[data-agent-browser-upload][accept=".vrm"]` | Wait for the imported card; no Live2D validation confirmation is expected. | +| MMD | `MMD` | `input[data-agent-browser-upload][accept=".zip,.pmx,.pmd"]` | Verify archive/model storage separately from textures, physics, dynamic imports, and rendering. | + +Resolve local Live2D and VRM fixtures from the paths documented by `$use-vishot-for-airi`. For MMD, use a licensed archive containing the model and its texture directories. Do not encode a contributor's private fixture name in tests or source comments. + +## Prepare AIRI state + +Use a fresh browser session or a dedicated Electron user-data directory. Mark onboarding complete before opening the model settings route: + +```bash +agent-browser eval 'localStorage.setItem("onboarding/completed", "true"); localStorage.setItem("onboarding/skipped", "false"); location.reload(); true' +``` + +Open `/settings/models`, click `Select model`, and snapshot again. + +## Import the model + +1. Click the first `Options for Display Models` button. +2. Re-snapshot and click the exact Live2D, VRM, or MMD menu item. +3. Upload with the matching selector from the table by following `$use-agent-browser-with-input-file`'s detached-input method. +4. Re-snapshot. If Live2D validation displays a report, inspect it and click `Confirm` only when the report permits import. +5. Wait for the exact basename to appear on an imported model card with its own `Pick` button. The detached input's displayed filename is not sufficient. +6. Click that imported card's `Pick` button. +7. Restore the detached-input bridge after the change handler completes when the page will remain open. + +## Verify the result + +Require all of these postconditions: + +1. `localStorage.getItem("settings/stage/model")` starts with `display-model-` after `Pick`. +2. The final stage shows the imported model rather than a preset, blank canvas, loading state, or import dialog. +3. A screenshot visually matches the format and fixture that was uploaded. +4. `agent-browser errors` contains no model-load failure. +5. `agent-browser console` contains no relevant Live2D, VRM, MMD, ZIP, texture, physics, or dynamic-import failure. + +Do not infer the selected display model from the AIRI card/profile dropdown in the header; it identifies the active character card and may retain a different label. + +Treat upload, persistence, and rendering as separate checkpoints. A visible imported card proves only that storage succeeded. + +## Close and report + +Close agent-browser sessions. Stop processes started for the test and remove only the dedicated temporary Electron user-data directory. Report each format and runtime separately as passed, failed, or not runnable, with the first failing checkpoint and relevant console error. diff --git a/.agents/skills/use-agent-browser-for-airi/agents/openai.yaml b/.agents/skills/use-agent-browser-for-airi/agents/openai.yaml new file mode 100644 index 000000000..163c46ee8 --- /dev/null +++ b/.agents/skills/use-agent-browser-for-airi/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Use Agent Browser for AIRI" + short_description: "Test AIRI model imports across desktop, web, and mobile" + default_prompt: "Use $use-agent-browser-for-airi to upload and verify a local display model in AIRI." diff --git a/.agents/skills/use-agent-browser-for-airi/references/electron.md b/.agents/skills/use-agent-browser-for-airi/references/electron.md new file mode 100644 index 000000000..341a4dcc2 --- /dev/null +++ b/.agents/skills/use-agent-browser-for-airi/references/electron.md @@ -0,0 +1,45 @@ +# Electron stage-tamagotchi + +Build or start stage-tamagotchi with a known CDP port. Prefer a dedicated user-data directory so imported fixtures and onboarding state do not affect the contributor's normal profile. + +```bash +APP_REMOTE_DEBUG=true \ +APP_REMOTE_DEBUG_PORT=9250 \ +APP_REMOTE_DEBUG_NO_OPEN=true \ +pnpm dev:tamagotchi +``` + +If using the built app, launch its Electron executable with `apps/stage-tamagotchi/out/main/index.js` and an explicit `--user-data-dir` under a newly created temporary directory. + +## Choose the renderer + +Enumerate raw targets first: + +```bash +curl -sS http://127.0.0.1:9250/json/list +agent-browser --session airi-electron --cdp 9250 tab +``` + +Identify the main `/#/` target, switch with its stable `tN` id, verify its URL, set onboarding storage, and close an existing `/onboarding` target. + +Expand the main controls and open settings: + +```bash +agent-browser --session airi-electron --cdp 9250 snapshot -i +# Replace @eN with the current snapshot ref for Expand. +agent-browser --session airi-electron --cdp 9250 click @eN +agent-browser --session airi-electron --cdp 9250 find role button click --name 'Open settings' +``` + +Enumerate targets again, switch to the new `/settings` target, and route it to model settings: + +```bash +agent-browser --session airi-electron --cdp 9250 eval 'location.hash = "/settings/models"; true' +agent-browser --session airi-electron --cdp 9250 snapshot -i +``` + +Keep `--session airi-electron --cdp 9250` on every command. Follow the shared import contract in `SKILL.md`. + +Live2D validation can open a report modal. If a visible `Confirm` button is covered in the accessibility click path, re-snapshot and resolve the covering dialog first. Use a DOM `.click()` on the exact visible Confirm button only as a diagnostic fallback, and record that ordinary pointer automation did not reach it. + +After `Pick`, switch back to the original main target, wait for its canvas, capture it, and inspect errors and console output. Repeat in a fresh temporary profile when isolation between formats matters. diff --git a/.agents/skills/use-agent-browser-for-airi/references/mobile.md b/.agents/skills/use-agent-browser-for-airi/references/mobile.md new file mode 100644 index 000000000..0eb27b389 --- /dev/null +++ b/.agents/skills/use-agent-browser-for-airi/references/mobile.md @@ -0,0 +1,39 @@ +# stage-pocket and mobile coverage + +## Browser-driven mobile layout + +Run stage-pocket's web surface on a dedicated HTTPS port: + +```bash +pnpm -F @proj-airi/stage-pocket dev:web --host 127.0.0.1 --port 5174 +agent-browser --session airi-pocket-web --ignore-https-errors open https://127.0.0.1:5174/settings/models +``` + +Set a desktop-width viewport while operating the settings page if controls are present in the DOM but visually outside the compact layout: + +```bash +agent-browser set viewport 768 1024 +``` + +Follow the shared import contract. After `Pick`, switch to the intended phone viewport before opening the stage and capturing evidence: + +```bash +agent-browser set viewport 390 844 +agent-browser open https://127.0.0.1:5174/ +agent-browser wait 'canvas' +agent-browser screenshot +``` + +This validates stage-pocket's shared Vue UI and mobile responsive renderer. It does not validate a native file picker, Android WebView, WKWebView, filesystem permission, or Capacitor bridge. + +## Android emulator + +Run a native Android test only when `adb devices` lists a booted emulator and an automation bridge can inspect its WebView or drive the native picker. Agent-browser requires a reachable Chromium CDP endpoint; do not claim coverage merely because the app launches. + +Use `pnpm dev:pocket:android` for the project runtime. Upload each format through the native document picker, return to AIRI, select the imported card, and verify the final renderer plus Logcat. Record “not runnable” when `adb` or a compatible WebView debugging endpoint is unavailable. + +## iOS Simulator + +Use `pnpm dev:pocket:ios` with a booted Simulator for manual or XCUITest/Appium coverage. WKWebView exposes Safari Web Inspector rather than Chromium CDP, so agent-browser cannot directly automate it. + +Do not substitute an agent-browser mobile viewport result for native iOS coverage. Report the browser-driven stage-pocket result and the native Simulator result as separate rows. diff --git a/.agents/skills/use-agent-browser-for-airi/references/web.md b/.agents/skills/use-agent-browser-for-airi/references/web.md new file mode 100644 index 000000000..0797ccab4 --- /dev/null +++ b/.agents/skills/use-agent-browser-for-airi/references/web.md @@ -0,0 +1,27 @@ +# stage-web + +Start stage-web on a known origin, then use one fresh agent-browser session per format: + +```bash +pnpm -F @proj-airi/stage-web dev --host 127.0.0.1 --port 5173 +agent-browser --session airi-web-live2d open http://127.0.0.1:5173/settings/models +``` + +Set onboarding storage, reload, wait for `Select model`, and follow the shared detached-input import contract from `SKILL.md`. + +After `Pick`: + +```bash +agent-browser eval 'localStorage.getItem("settings/stage/model")' +agent-browser open http://127.0.0.1:5173/ +agent-browser wait 'canvas' +agent-browser screenshot +agent-browser errors +agent-browser console +``` + +Use an explicit wait for renderer-specific readiness when the app exposes one. A canvas existing in the DOM is not sufficient evidence because it can remain blank after a loader failure. + +For MMD, inspect console output for texture, archive, physics, and Vite dynamic-import failures. Report an imported card plus blank stage as “storage passed, rendering failed,” not as a successful upload test. + +Close each session after evidence is collected. Fresh sessions avoid deleting unrelated IndexedDB models from a contributor's browser profile. diff --git a/.agents/skills/use-agent-browser-with-input-file/SKILL.md b/.agents/skills/use-agent-browser-with-input-file/SKILL.md new file mode 100644 index 000000000..5f83fc225 --- /dev/null +++ b/.agents/skills/use-agent-browser-with-input-file/SKILL.md @@ -0,0 +1,33 @@ +--- +name: use-agent-browser-with-input-file +description: Upload local files through ordinary, hidden, dynamically created, detached, multiple, or native-backed file inputs with agent-browser in web and Electron applications. Use when testing file selection, drag-or-choose flows, framework file-dialog composables, or post-upload application behavior. +--- + +# Use Agent Browser with an Input File + +Invoke `$agent-browser` first and run `agent-browser skills get agent-browser --full` before issuing browser commands. For Electron, also invoke `$agent-browser-electron` and attach to the intended renderer target. + +Resolve the input file to an absolute path and verify that it exists. Then inspect the DOM before choosing an upload method: + +- For an attached `input[type="file"]`, including a visually hidden input, read [references/attached-input.md](references/attached-input.md). +- For an input created on demand or kept detached from the DOM by application code, read [references/detached-input.md](references/detached-input.md). +- For a native file chooser or a flow where no HTML input can be addressed, read [references/native-file-chooser.md](references/native-file-chooser.md). +- For evidence requirements after the file is assigned, read [references/verify-upload.md](references/verify-upload.md). + +## Workflow + +1. Open the target route and inspect `snapshot -i` and the DOM around the upload control. +2. Determine whether the input already exists, appears after interaction, remains detached, or delegates to a native picker. +3. Apply the matching reference without changing application source code. +4. Re-snapshot after each interaction because element refs can become stale. +5. Verify application-specific processing, persistence, and final output separately. +6. Inspect browser errors and relevant console output. +7. Restore any temporary browser-session instrumentation and close the session. + +Keep the same `--session` and, for Electron, `--cdp ` options on every command. When Electron exposes several targets, use the discovery process from `$agent-browser-electron` and confirm the active target URL before uploading. + +## Handoff to Vishot + +Use Agent Browser to discover inputs, diagnose selectors, and prove the interaction. Once the flow is stable, encode it in a Playwright-backed Vishot scenario with `$use-vishot-with-input-file` when deterministic visual evidence is required. + +Do not treat a successful `upload` command as completion. File assignment, application import, persistence, and rendering are distinct checkpoints. diff --git a/.agents/skills/use-agent-browser-with-input-file/agents/openai.yaml b/.agents/skills/use-agent-browser-with-input-file/agents/openai.yaml new file mode 100644 index 000000000..3243d0f6e --- /dev/null +++ b/.agents/skills/use-agent-browser-with-input-file/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Use Agent Browser with Input Files" + short_description: "Automate browser file chooser workflows" + default_prompt: "Use $use-agent-browser-with-input-file to upload this local file through the application UI." diff --git a/.agents/skills/use-agent-browser-with-input-file/references/attached-input.md b/.agents/skills/use-agent-browser-with-input-file/references/attached-input.md new file mode 100644 index 000000000..cb059d584 --- /dev/null +++ b/.agents/skills/use-agent-browser-with-input-file/references/attached-input.md @@ -0,0 +1,21 @@ +# Attached HTML file input + +Use this method when the target `input[type="file"]` is attached to the document. CSS visibility does not matter: hidden inputs remain addressable by selector. + +Inspect candidate inputs and their constraints: + +```bash +agent-browser eval 'Array.from(document.querySelectorAll("input[type=file]")).map((input, index) => ({ index, accept: input.accept, multiple: input.multiple, disabled: input.disabled, connected: input.isConnected }))' +``` + +Trigger the application's upload control first when it creates or enables the input. Then upload with the narrowest stable selector: + +```bash +agent-browser upload 'input[type="file"][accept=".zip"]' '/absolute/path/to/archive.zip' +``` + +For `multiple` inputs, pass every supported absolute path in the same upload operation if the installed Agent Browser command contract permits multiple path arguments. Otherwise assign one complete selection through the command's documented multi-file form; do not upload sequentially when each assignment replaces the previous `FileList`. + +Prefer semantic attributes such as `accept`, a stable test attribute, or a label relationship. Avoid positional selectors unless DOM inspection proves that only ordering distinguishes the intended input. + +Continue with [verify-upload.md](verify-upload.md). diff --git a/.agents/skills/use-agent-browser-with-input-file/references/detached-input.md b/.agents/skills/use-agent-browser-with-input-file/references/detached-input.md new file mode 100644 index 000000000..816efb275 --- /dev/null +++ b/.agents/skills/use-agent-browser-with-input-file/references/detached-input.md @@ -0,0 +1,34 @@ +# Dynamically created or detached HTML file input + +Use this method when clicking the application control creates an input only briefly, or when a framework file-dialog helper calls `input.click()` without attaching the input to the document. Agent Browser cannot select a detached node with a CSS selector, so expose it temporarily within the browser test session. + +## Confirm the behavior + +Inspect the DOM before and after clicking the control. If no input remains connected, install the bridge before triggering the file dialog: + +```bash +agent-browser eval 'window.__agentBrowserOriginalFileInputClick ??= HTMLInputElement.prototype.click; HTMLInputElement.prototype.click = function () { if (this.type === "file") { this.dataset.agentBrowserUpload = "true"; document.body.append(this); return; } return window.__agentBrowserOriginalFileInputClick.call(this); }; true' +``` + +The bridge preserves ordinary clicks and intercepts only file inputs. It prevents the native chooser from opening, marks the exact input created by the application, and attaches that same node so its existing `change` listener remains intact. + +Trigger the application's choose/import action, inspect the captured input, and upload using both the marker and the expected constraints: + +```bash +agent-browser eval 'Array.from(document.querySelectorAll("input[data-agent-browser-upload]"), input => ({ accept: input.accept, multiple: input.multiple, connected: input.isConnected }))' +agent-browser upload 'input[data-agent-browser-upload][accept=".zip"]' '/absolute/path/to/archive.zip' +``` + +If several inputs were captured, narrow the selector by `accept`, `multiple`, or another application-owned attribute. Do not upload to all marked inputs. + +## Restore the page + +Wait until the application's `change` handler has consumed the file, then restore the prototype and remove only nodes created by this bridge: + +```bash +agent-browser eval 'if (window.__agentBrowserOriginalFileInputClick) { HTMLInputElement.prototype.click = window.__agentBrowserOriginalFileInputClick; delete window.__agentBrowserOriginalFileInputClick; } document.querySelectorAll("input[data-agent-browser-upload]").forEach(input => input.remove()); true' +``` + +Keep the bridge scoped to a disposable test session. It is runtime instrumentation for automation, not application code to commit. + +Continue with [verify-upload.md](verify-upload.md). diff --git a/.agents/skills/use-agent-browser-with-input-file/references/native-file-chooser.md b/.agents/skills/use-agent-browser-with-input-file/references/native-file-chooser.md new file mode 100644 index 000000000..58d089430 --- /dev/null +++ b/.agents/skills/use-agent-browser-with-input-file/references/native-file-chooser.md @@ -0,0 +1,12 @@ +# Native file chooser + +Use this path only after DOM inspection shows that the flow does not expose an addressable HTML file input. Examples include platform-native pickers, a native bridge that returns file handles, and controls implemented outside the inspected renderer. + +Agent Browser's `upload` command assigns files to an HTML input; it does not drive arbitrary operating-system picker windows. Choose the boundary that owns the picker: + +- In Electron, use `$agent-browser-electron` to identify the renderer and determine whether the application ultimately creates an HTML input there. If it does, return to the attached or detached method. +- For a genuine Electron main-process or operating-system dialog, use the platform automation mechanism supported by the test environment, then return to Agent Browser for renderer verification. +- For Android or iOS native document pickers, use emulator/simulator automation and report that separately from browser-layout coverage. +- If the picker cannot be controlled safely, report the native selection checkpoint as not runnable. Do not substitute a typed path, synthetic filename, or viewport emulation and call it covered. + +After native selection, use [verify-upload.md](verify-upload.md) to validate the application-visible result. diff --git a/.agents/skills/use-agent-browser-with-input-file/references/verify-upload.md b/.agents/skills/use-agent-browser-with-input-file/references/verify-upload.md new file mode 100644 index 000000000..5bc1a4a26 --- /dev/null +++ b/.agents/skills/use-agent-browser-with-input-file/references/verify-upload.md @@ -0,0 +1,21 @@ +# Verify an upload + +Verify the deepest observable result instead of stopping when `agent-browser upload` exits successfully. + +Treat these as separate checkpoints: + +1. **Assignment:** the intended input received the expected file or files. +2. **Processing:** the application emitted the expected filename, preview, validation result, progress state, or imported record. +3. **Persistence:** the application retained the new selection in its store, database, or subsequent route when persistence is part of the contract. +4. **Consumption:** the destination viewer, editor, renderer, or submission flow used the uploaded content. + +Re-snapshot after assignment and wait on an application-specific postcondition. A filename rendered by the browser's input control proves assignment only. Likewise, an imported card does not prove that a renderer can consume the file. + +Inspect diagnostics at the final checkpoint: + +```bash +agent-browser errors +agent-browser console +``` + +Capture evidence that distinguishes the uploaded file from default or previously cached data. Report the first failing checkpoint and the relevant error rather than collapsing the whole flow into a single pass/fail result. diff --git a/.agents/skills/use-vishot-for-airi/SKILL.md b/.agents/skills/use-vishot-for-airi/SKILL.md new file mode 100644 index 000000000..b51f2194f --- /dev/null +++ b/.agents/skills/use-vishot-for-airi/SKILL.md @@ -0,0 +1,47 @@ +--- +name: use-vishot-for-airi +description: Run AIRI-owned Vishot scenarios for stage-web or stage-tamagotchi, including skipping first-run onboarding and importing a local Live2D, VRM, or MMD display model before capture. Use when AIRI visual evidence must exercise a contributor-supplied avatar file instead of a preset model. +--- + +# Use Vishot for AIRI + +Invoke `$use-vishot`, `$use-vishot-with-electron`, and `$use-vishot-with-input-file`. Reuse the product helper at `packages/scenarios-stage-tamagotchi-electron/src/scenarios/display-model-from-file.ts`. + +## Local model capture + +Build Electron, then pass the model path and explicit format through the environment: + +```bash +pnpm build:tamagotchi + +AIRI_DISPLAY_MODEL_FORMAT=live2d \ +AIRI_DISPLAY_MODEL_PATH='/Users/neko/Pictures/Project AIRI 静态资产/形象 - Iru/Live2D/Iru v2.0 @ Project AIRI 运行文件.zip' \ +pnpm exec vishot capture \ + ./packages/scenarios-stage-tamagotchi-electron/src/scenarios/display-model-from-file.ts \ + --target electron \ + --app-entrypoint ./apps/stage-tamagotchi/out/main/index.js \ + --cwd . \ + --settle-ms 2500 \ + --output-dir ./.vishot/display-model/live2d +``` + +Use `AIRI_DISPLAY_MODEL_FORMAT=vrm` with a local file such as: + +```text +/Users/neko/Pictures/Project AIRI 静态资产/形象 - Iru/VRM/ReLU @ Project AIRI.vrm +``` + +Use `AIRI_DISPLAY_MODEL_FORMAT=mmd` with `.zip`, `.pmx`, or `.pmd`. A license-readable public candidate is [CG-CA Gene](https://github.com/mmdagent-ex/gene), whose repository provides PMX/PMD models and textures under CC BY 4.0. Prefer a ZIP containing the model and its texture directories. + +## Scenario contract + +The AIRI helper: + +- marks onboarding completed in the test profile and closes an already-open onboarding renderer; +- opens Settings → Models through product-owned window helpers; +- starts waiting for the Playwright file chooser before choosing Live2D, VRM, or MMD; +- correlates the import through the new IndexedDB model key; +- selects the imported card and waits for the main window selection to match; +- captures the main stage and removes the imported fixture afterward. + +Treat the capture as failed when it still shows a preset model, an import dialog, a blank stage, or a loading state. Inspect every generated image. diff --git a/.agents/skills/use-vishot-for-airi/agents/openai.yaml b/.agents/skills/use-vishot-for-airi/agents/openai.yaml new file mode 100644 index 000000000..a3d3db361 --- /dev/null +++ b/.agents/skills/use-vishot-for-airi/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Use Vishot for AIRI" + short_description: "Capture AIRI model-import scenarios" + default_prompt: "Use $use-vishot-for-airi to import a local avatar model and capture it in AIRI." diff --git a/.agents/skills/use-vishot-with-electron/SKILL.md b/.agents/skills/use-vishot-with-electron/SKILL.md index 108885d67..f485774d3 100644 --- a/.agents/skills/use-vishot-with-electron/SKILL.md +++ b/.agents/skills/use-vishot-with-electron/SKILL.md @@ -31,6 +31,34 @@ Keep product routes, selectors, window identities, and interaction scenarios in 9. Inspect every screenshot and confirm it depicts the intended window and state. 10. Return its stable ID, title, window identity, and absolute path to `$use-vishot` or the caller. +## Window Sizing + +Resize the native `BrowserWindow` through Playwright's Electron handle when a scenario needs a deterministic content size: + +```ts +const size = { width: 1200, height: 900 } +const browserWindow = await electronApp.browserWindow(page) + +try { + await browserWindow.evaluate((window, target) => { + window.setContentSize(target.width, target.height) + }, size) +} +finally { + await browserWindow.dispose() +} + +await page.waitForFunction(target => ( + globalThis.innerWidth === target.width + && globalThis.innerHeight === target.height +), size) +``` + +- Use `BrowserWindow.setContentSize()` when the requested dimensions describe the renderer content captured by `page.screenshot()`. +- Use `BrowserWindow.setSize()` only when the requested dimensions describe the complete native window, including its frame and title bar. +- Do not use `page.setViewportSize()` to resize an Electron window. It overrides renderer viewport metrics through Playwright/CDP but does not resize the native `BrowserWindow`, so the visible window and screenshot state can disagree. +- Treat screenshot dimensions as device pixels. On a display with `devicePixelRatio = 2`, a `1200 × 900` content area normally produces a `2400 × 1800` screenshot. + ## Electron Windows and UI State - Do not treat `electronApp.firstWindow()` or the next `window` event as semantic window selection. They reflect creation order, and startup may create Chromium DevTools, splash, onboarding, hidden, or auxiliary windows before the intended product window. diff --git a/.agents/skills/use-vishot-with-input-file/SKILL.md b/.agents/skills/use-vishot-with-input-file/SKILL.md new file mode 100644 index 000000000..68f66ecad --- /dev/null +++ b/.agents/skills/use-vishot-with-input-file/SKILL.md @@ -0,0 +1,41 @@ +--- +name: use-vishot-with-input-file +description: Drive a local file-input or file-chooser workflow inside a Vishot browser or Electron scenario, then capture the resulting deterministic UI state. Use when screenshot readiness depends on uploading, importing, opening, or selecting a local file rather than the application's default startup data. +--- + +# Use Vishot with an Input File + +Invoke `$use-vishot` and its runtime variant first. Put file interaction in a product-owned or disposable `defineScenario` module; Vishot performs the final capture. + +## File chooser pattern + +Pass the absolute input path through an environment variable. Do not embed a contributor-specific path in tests or reusable scenario source. + +```ts +const inputFile = process.env.VISHOT_INPUT_FILE +if (!inputFile) + throw new Error('VISHOT_INPUT_FILE is required') + +const [fileChooser] = await Promise.all([ + page.waitForEvent('filechooser'), + page.getByRole('menuitem', { name: 'Import' }).click(), +]) + +await fileChooser.setFiles(inputFile) +await page.getByText(inputFile.replaceAll('\\', '/').split('/').at(-1)!, { exact: true }) + .waitFor({ state: 'visible' }) +await capture('imported-file', page) +``` + +Use `locator.setInputFiles(inputFile)` when a stable `` already exists. Prefer `waitForEvent('filechooser')` when the application creates the input lazily or hides it behind a menu. + +## Readiness + +1. Verify the absolute file exists before launching the scenario. +2. Start waiting for `filechooser` before clicking the control that opens it. +3. Wait for a file-specific postcondition such as its displayed name, imported record ID, parsed preview, or selected renderer. +4. Select the imported item if upload alone does not activate it. +5. Wait for the destination surface, not merely for the chooser to close. +6. Capture with Vishot and inspect the image. A default model or unchanged startup view is a failed upload scenario. + +Keep secrets and private filenames out of scenario IDs, screenshot names, test names, and comments. Supply private local paths only through environment variables or command arguments. diff --git a/.agents/skills/use-vishot-with-input-file/agents/openai.yaml b/.agents/skills/use-vishot-with-input-file/agents/openai.yaml new file mode 100644 index 000000000..2cadc431c --- /dev/null +++ b/.agents/skills/use-vishot-with-input-file/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Use Vishot with Input Files" + short_description: "Capture file-upload states with Vishot" + default_prompt: "Use $use-vishot-with-input-file to upload this local file and capture the resulting UI state." diff --git a/.agents/skills/use-vishot-with-web/SKILL.md b/.agents/skills/use-vishot-with-web/SKILL.md index 7e681d3b6..519bf1670 100644 --- a/.agents/skills/use-vishot-with-web/SKILL.md +++ b/.agents/skills/use-vishot-with-web/SKILL.md @@ -30,6 +30,33 @@ Prefer product-owned Vishot capture roots when available. Otherwise capture a lo 8. Inspect each image for loading screens, stale data, iframe refusal, missing fonts, permission prompts, and animation instability. 9. Return its stable ID, title, viewport, and absolute path to `$use-vishot` or the caller. +## Viewport Sizing + +Prefer Vishot's browser dimensions when the size is known before launch: + +```bash +pnpm exec vishot render \ + --target browser \ + http://127.0.0.1:5173/settings \ + --width 1440 \ + --height 900 \ + --output-dir /absolute/output/settings +``` + +Use Playwright's viewport API when a browser scenario must change responsive states during the same run: + +```ts +await page.setViewportSize({ width: 390, height: 844 }) +await page.waitForFunction(size => ( + globalThis.innerWidth === size.width + && globalThis.innerHeight === size.height +), { width: 390, height: 844 }) +``` + +- Web pages do not have an Electron `BrowserWindow`; `page.setViewportSize()` is therefore the correct way to change the browser viewport in scenario code. +- Do not use DOM APIs such as `window.resizeTo()` to control Vishot output. Browser security and window-manager behavior can ignore them, and they do not define the Playwright capture viewport reliably. +- Treat `width` and `height` as CSS pixels. The output image can contain more device pixels when the browser context uses a device scale factor above `1`. + ## Scenario Readiness and Locators - Implement readiness for the specific route and UI state. A heading, button, or expected text from one page is not a reusable readiness condition for another page. diff --git a/.agents/skills/use-vishot/SKILL.md b/.agents/skills/use-vishot/SKILL.md index ec3df071b..da12bbdc6 100644 --- a/.agents/skills/use-vishot/SKILL.md +++ b/.agents/skills/use-vishot/SKILL.md @@ -16,6 +16,7 @@ Use Vishot as the common screenshot interface and delegate runtime-specific beha - Invoke `$use-vishot-with-electron` for Electron windows. - Invoke `$use-vishot-with-web` for browser routes. - Invoke `$use-vishot-with-capacitor` for Capacitor WebView or native-shell evidence. + - Also invoke `$use-vishot-with-input-file` when reaching the requested state requires a local file chooser or file input. 5. When developing or debugging the disposable automation, use a locally available `$agent-browser` to inspect web or Capacitor DOM and interaction paths. For Electron window discovery and interaction, also invoke `$agent-browser-electron`. Use these skills as development and validation aids; do not make their availability a prerequisite for Vishot capture. 6. Produce the final screenshot artifacts with Vishot whenever possible, even when agent-browser helped discover selectors or verify the interaction sequence. Treat a missing product helper as temporary automation work, not as proof that Vishot cannot capture the state; report a Vishot limitation only after reproducing a concrete runtime failure. 7. Give the variant an explicit output directory. Directory ownership belongs to the caller; Vishot only writes named capture artifacts into that directory.