Found by a docs/ staleness audit: - architecture.md: Claude no longer runs under tmux — it's driven over the stream-json control protocol with --resume (D-75/D-77/D-78); and the IPC socket server is implemented, not "currently unimplemented". - testing/README.md + claude-ui-workflow.md: drop the dissolved app/ two-package paths (D-56) — tests live at test/ and the web build at build/web/. - design/multitab-pane.md: the Claude pane spawns a stream-json session, not a tmux one; ClaudeSessionRef carries the session id. Frozen historical snapshots (initial-plan.md, the HISTORICAL pty docs, dated spikes/audits) left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.8 KiB
2.8 KiB
How Claude Code uses clide
Claude Code needs to actually use the app while building features. The pipeline:
- Flutter builds the app to WASM.
flutter build web --wasmships a CanvasKit/Skwasm bundle underbuild/web/. - A local server serves it.
tools/ui/serve.shstartshttp://localhost:4280in the background with a pidfile. - Playwright drives a headless Chromium. Instead of click-by-pixel
— which is brittle on a CanvasKit
<canvas>— the driver queries Flutter's semantic tree (flt-semantics[aria-label=…]), the same tree screen readers use. This is only reliable because every interactive widget in clide already shipsSemantics(label:, hint:)wrappers for a11y — the automation surface is a free side-benefit.
Claude's one-liners
# Bring the app up (builds wasm, starts server)
make ui-dev
# Run an ad-hoc probe
cd tools/ui && npx playwright test ...
# Everything in one shot (build + serve + smoke + stop)
make ui-smoke
# Bring it down
make ui-stop
Writing a driver script
import { test, expect } from '@playwright/test';
import { ClideDriver } from '../driver';
test('opens the theme picker and selects summer-night', async ({ page }) => {
const clide = new ClideDriver(page);
await clide.goto('/');
// Keyboard shortcut that invokes `theme.pick`:
await page.keyboard.press('Control+K');
// Pick via semantic label.
await clide.click('summer-night');
// Dump the whole tree to inspect state after an interaction.
const tree = await clide.dumpSemanticsTree();
console.log(JSON.stringify(tree, null, 2));
// Screenshot into out/ (Claude reads the PNG via the Read tool).
await clide.screenshot('out/theme-picker.png');
});
Prerequisites (one-time per machine)
cd tools/ui
npm install
npx playwright install chromium
Known quirks
- Labels are merged. Flutter web concatenates sibling Semantics
labels into a single
aria-labelseparated by newlines.byLabeluses substring match for that reason. When two Semantics nodes share a substring, narrow with.filter()on the returned locator. - The placeholder button. Flutter web ships semantics disabled by
default, behind an invisible
<flt-semantics-placeholder>button.ClideDriver.waitUntilReady()clicks it automatically. - No daemon on web. The WASM build has no unix socket; the status
indicator always says
disconnected. That's honest — a web-hosted clide has no local daemon to talk to. The Playwright flow is for UI-only verification.
When Playwright is overkill
If all I need is "does the extension register its contributions," a
widget test (test/builtin/*/widget_test.dart) is faster and cheaper.
Reach for Playwright when the question is about the real rendering
pipeline, keyboard behavior, or multi-widget interactions that mirror
a user workflow.