#34008 removed docs still linked from AGENTS.md (test anti-patterns, cross-platform support, git workflow, component fixtures) plus the macOS daemon TCC doc that cross-platform-support links to. Restore them in a dedicated folder whose name marks them as AGENTS.md inputs. Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
3.6 KiB
Component Fixtures
Render one renderer component in isolation and screenshot it headlessly in about a second, without booting Electron. Use it as the fast verify loop for presentational UI changes. Use the $electron skill when the change depends on real app state, IPC, terminals, or window chrome.
Writing a fixture
Put a *.fixture.tsx file next to the component. Each named export built with defineFixture is one fixture:
import { defineFixture } from '../../component-fixtures/component-fixture'
import { DeleteWorktreeDirtyChangeHint } from './DeleteWorktreeDirtyChangeHint'
export const dirty = defineFixture({
width: 360,
render: () => <DeleteWorktreeDirtyChangeHint changeCount={3} checkState="checking" />
})
Optional fields:
store: partialAppStatemerged into the store before render. The store starts from slice defaults plusgetDefaultSettings().setup(): runs before render, for example to override awindow.apimethod.interact(frame): runs after first paint, for example clicking a trigger so an open popover is captured. Content portaled to<body>(popovers, menus, dialogs) is included in the screenshot.width: frame width in CSS pixels. The height always fits the content.
Fixture ids are the file path under src/renderer/src/ with components/ and .fixture.tsx removed, followed by the export name. For example, sidebar/DeleteWorktreeDirtyChangeHint:dirty.
Every fixture renders inside I18nProvider, TooltipProvider, and the theme class. window.api is the web client's preload bridge (installWebPreloadApi) with no runtime paired. Calls that need a runtime reject, and namespaces without a web implementation return the web fallback defaults. The web-client flag is cleared, so components render their desktop branches.
Fixtures are dev-only. They load through import.meta.glob in the fixture explorer, which is not a production build input. Oxlint rejects static imports of *.fixture modules.
Screenshots
pnpm fixtures:shot sidebar/DeleteWorktreeDirtyChangeHint:dirty # one fixture
pnpm fixtures:shot sidebar/DeleteWorktreeDirtyChangeHint --theme both # every fixture in a file, both themes
pnpm fixtures:shot 'ui/*' --scale 2 # glob, at 2x
pnpm fixtures:shot --list
pnpm fixtures:shot --stop
PNGs go to .cache/fixtures/ (override with --out), and the command prints their paths. Read them with your image viewer tool. Render errors and page console errors are printed for each fixture, and any failure makes the command exit non-zero.
The first call starts a detached server: Vite (config/vite.fixtures.config.ts, which extends vite.web.config.ts) plus one headless Chromium. The first call takes 10–20 seconds. After that, each shot takes about 100 ms. The first shot after a source edit takes 1–2 seconds because the page reloads. The server restarts itself when the fixture scripts change and exits after 20 idle minutes.
Chromium resolution order: ORCA_FIXTURES_BROWSER (an executable path), then the Playwright-managed chromium-headless-shell, then an installed Chrome or Edge. Every option runs headless, so screenshots never open a window or take focus.
Browsing
pnpm dev:fixtures serves the explorer for humans. Open /fixtures for the index, or /fixtures?id=sidebar/DeleteWorktreeDirtyChangeHint:dirty&theme=light for a single fixture.
MCP
.mcp.json registers orca-component-fixtures, a stdio server at config/scripts/component-fixtures/fixtures-mcp-server.mjs. It has two tools:
list_fixtures({ filter? })screenshot_fixture({ id, theme?, scale? }): returns the PNGs inline (up to 6) along with their paths.