| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| Invoke | /jaspr-web:widgetbook-web |
| Category | cocode-web |
| Complexity | high |
| MCP Servers | playwright, marionette |
/jaspr-web widgetbook-web โ ์น ์ปดํฌ๋ํธ ์นดํ๋ก๊ทธ ์๋ ์ ๊ฒ#
ํ๋ง๋๋ก#
์น ๋ธ๋ผ์ฐ์ ์ ๋์ด ์ปดํฌ๋ํธ ๊ฒฌ๋ณธ ์นดํ๋ก๊ทธ(Widgetbook) ๋ฅผ ํ ์นธ์ฉ ์๋์ผ๋ก ๋๋ฉด์, ํ๋ฉด์ ์บก์ฒํ๊ณ ์ง์ ๋๋ฌ๋ณด๋ฉฐ ์ด์์ด ์๋์ง ์ ๊ฒํด ์ฃผ๋ ๋ช ๋ น์ ๋๋ค. ๋ง์น ๋งค์ฅ ์ง์์ด ์ง์ด๋ ๋ชจ๋ ์ํ์ ํ๋์ฉ ๊บผ๋ด ์๋์์ผ ๋ณด๊ณ ์ฌ์ง์ผ๋ก ๋จ๊ฒจ ๋๋ ๊ฒ๊ณผ ๊ฐ์์.
๋๊ฐยท์ธ์ ์ฐ๋์#
- ์น์์๋ง ์๊ธฐ๋ ๋ฌธ์ ๋ฅผ ์กฐ์ฌํ ๋ (์: ๋งฅ/๋ชจ๋ฐ์ผ์์๋ ๋ฉ์ฉกํ๋ฐ ๋ธ๋ผ์ฐ์ ์์๋ง ๊นจ์ง๋ ํ๋ฉด)
- ํ์ด์ง์ ๋ฐ๊นฅ ํ(๋ธ๋ผ์ฐ์ DOM) ๊ณผ ์์ชฝ ์ค์ ์ปดํฌ๋ํธ(Flutter ์บ๋ฒ์ค) ๋ฅผ ํ ๋ฒ์ ํจ๊ป ํ์ธํ๊ณ ์ถ์ ๋
- ๋งฅ์ด ์๊ณ ๋ธ๋ผ์ฐ์ ๋ง ์ธ ์ ์๋ ํ๊ฒฝ(์: CI ์๋ฒ)์์ ์ ๊ฒํด์ผ ํ ๋
๐ ํ์ ์ผ๋ฐ ์ ๊ฒ์ ๋งฅ์์ ๋๋ cc-flutter-marionette:sweep์ด ๋ ๋น ๋ฅด๊ณ ์์ ์ ์
๋๋ค. ์ด ๋ช
๋ น์ ์์ฒ๋ผ "์น์ด์ด์ผ๋ง ํ๋" ์ํฉ์ ์๋๋ค.
๋ฌด์์ ํด์ฃผ๋์#
์ ๊ฒ์ด ๋๋๋ฉด .claude/docs/jaspr-web/widgetbook-<์๊ฐ>/ ํด๋ ์์ ๊ฒฐ๊ณผ๊ฐ ์ ๋ฆฌ๋ฉ๋๋ค.
-
์ปดํฌ๋ํธ๋ณ ํ๋ฉด ์บก์ฒ โ ๊ฐ๋งํ ์์ ๋(
00_idle.png)์ ๋๋ฌ๋ณธ ๋ค(01_after_action.png)๋ฅผ ๋น๊ต - ์ ๊ฒํ ์ปดํฌ๋ํธ ๋ชฉ๋ก โ
routes.json - ์์ฝ ๋ณด๊ณ ์ โ
summary.md - ์ปดํฌ๋ํธ๋ง๋ค ๋ฌด์์ ํ์ธํ๋์ง ๋ฉ๋ชจ(
notes.md)์ ๋ด๋ถ ๋์ ๊ธฐ๋ก(interactive.json)
์ด๋ป๊ฒ ์ฐ๋์#
# 1) ํฐ๋ฏธ๋์์ Widgetbook์ ๋ธ๋ผ์ฐ์ ์ ๋์๋๋ค
cd packages/coui_widgetbook
flutter run -d chrome --web-port=8080 --print-dtd --dart-define=ENABLE_SEMANTICS=true
# 2) Claude Code์์ ์ ๊ฒ ๋ช
๋ น์ ์คํํฉ๋๋ค
/jaspr-web widgetbook-web --port=8080 --marionette-uri=ws://127.0.0.1:54321/abc/ws
์์ฃผ ์ฐ๋ ์ ํ ํญ๋ชฉ:
--portโ Widgetbook์ด ์ด๋ฆฐ ๋ธ๋ผ์ฐ์ ํฌํธ (๊ธฐ๋ณธ8080)--routesโ ํน์ ์ปดํฌ๋ํธ ๊ฒฝ๋ก๋ง ๊ณจ๋ผ์ ์ ๊ฒ--limitโ ์ ๊ฒํ ์ปดํฌ๋ํธ ๊ฐ์ ์ ํ--outputโ ๊ฒฐ๊ณผ ์ ์ฅ ํด๋ ์ง์
์์์ ๋ฌด์จ ์ผ์ด ๋ฒ์ด์ง๋์#
์ด ๋ช ๋ น์ ๋ ๋๊ตฌ(๋ธ๋ผ์ฐ์ ๋ฅผ ์กฐ์ข ํ๋ Playwright, ์ค์ ์์ ฏ์ ์กฐ์ข ํ๋ Marionette)๋ฅผ ๋์์ ๋ถ๋ ค์ ๊ฐ์ ์ํ๋ ์ผ์ ์ํต๋๋ค.
- ๋ธ๋ผ์ฐ์ ํ๋ฉด๊ณผ ์์ ฏ ์์ง์ ๋ชจ๋ ์ฐ๊ฒฐ๋๋์ง ๋จผ์ ํ์ธํฉ๋๋ค.
- ์นดํ๋ก๊ทธ์ ์ปดํฌ๋ํธ ๋ชฉ๋ก์ ์ฝ์ด์ต๋๋ค.
- ์ปดํฌ๋ํธ๋ฅผ ํ๋์ฉ ๋๋ฉด์: ํด๋น ํญ๋ชฉ์ ํด๋ฆญ โ ํ๋ฉด์ด ์์ ๋๊ธธ ๊ธฐ๋ค๋ฆผ โ ์บก์ฒ โ ์ค์ ๋ก ๋๋ฌ๋ณด๊ฑฐ๋ ์ ๋ ฅํด ๋ด โ ๋ค์ ์บก์ฒ โ ๋๋ฅด๊ธฐ ์ ํ ํ๋ฉด์ ๋น๊ตํฉ๋๋ค.
- ๋ชจ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์ ์์ฝ ๋ณด๊ณ ์๋ฅผ ์์ฑํฉ๋๋ค.
โ๏ธ ์์ธ ์ต์ ยท์คํ ๋ช ์ธ (๊ฐ๋ฐ์ / AI ์์ด์ ํธ์ฉ)
Parameters#
| Parameter | Required | Description | Default |
|---|---|---|---|
--port | โ | flutter run Chrome port (the URL Widgetbook is served at) | 8080 |
--marionette-uri | โ | DTD/VM Service URI; if omitted, derived from flutter run --print-dtd output | auto |
--routes | โ | Widgetbook routes / UseCase paths to walk | all visible |
--limit | โ | Cap on UseCases | unlimited |
--output | โ | Output dir | .claude/docs/jaspr-web/widgetbook-<ts>/ |
Prerequisites#
# In the Widgetbook package
flutter run \
-d chrome \
--web-port=8080 \
--print-dtd \
--dart-define=ENABLE_SEMANTICS=trueLook for two lines:
A Dart VM Service ... is available at: http://127.0.0.1:54321/abc/
The Dart Tooling Daemon is listening on ws://127.0.0.1:54322/xyzThe first is for Marionette; the second is for cc-flutter-pixel-loop if you also want pixel-loop (rare for web).
The
ENABLE_SEMANTICS=trueflag is a cocode convention โ guardSemanticsBinding.instance.ensureSemantics()behind it in the Widgetbook bootstrap so production sites stay unaffected.
Flow#
1. Confirm Playwright can reach http://localhost: < port >
2. Confirm Marionette can reach the VM Service URI
3. List Widgetbook UseCases by:
- Playwright: read the Widgetbook NavigationSidebar DOM
- OR Marionette: `get_interactive_elements` + filter sidebar entries
4. For each UseCase:
a. Playwright: click the sidebar entry (browser-level navigation)
b. Marionette: wait for hot reload to settle (`hot_reload` returns success)
c. Playwright: take a clipped screenshot of the canvas region
d. Marionette: `get_interactive_elements` inside the canvas; for each, run smoke action (tap / type)
e. Playwright: take post-action screenshot
f. Diff before/after at default tolerance
5. Write summaryEach layer does what it's best at:
| Step | Playwright | Marionette |
|---|---|---|
| Browser navigation | โ | โ |
| Sidebar click | โ | (fallback) |
| Widget tap inside canvas | (only if Semantics on) | โ (better) |
| TextField controller assertion | โ | โ (only Marionette can read it) |
| Screenshot | โ (DOM-aware clip) | โ (frame buffer) |
| Hot reload | โ | โ |
Example#
# In one terminal
cd packages/coui_widgetbook
flutter run -d chrome --web-port=8080 --print-dtd --dart-define=ENABLE_SEMANTICS=true
# In Claude Code
/jaspr-web widgetbook-web --port=8080 --marionette-uri=ws://127.0.0.1:54321/abc/wsArtifacts#
.claude/docs/jaspr-web/widgetbook- < ts > /
โโโ usecases/ < usecase-id > /
โ โโโ 00_idle.png
โ โโโ 01_after_action.png
โ โโโ interactive.json # what Marionette saw
โ โโโ notes.md
โโโ routes.json # list of UseCases walked
โโโ summary.mdWhen to prefer the native counterpart#
The native cc-flutter-marionette:sweep against a non-web Widgetbook (flutter run -d macos) is usually faster and less flaky. Use /jaspr-web widgetbook-web when:
- You're investigating a web-only regression.
- You want to verify both DOM (Jaspr Widgetbook chrome) and Canvas (Flutter UseCase) parts of the page.
- The browser is the only target you have available (e.g. on a CI agent without macOS).
Failure Modes#
| Symptom | Cause | Recovery |
|---|---|---|
Marionette: not connected | flutter run printed a stale URI from a previous run | Re-run flutter run --print-dtd; paste fresh URI |
flt-semantics-host empty | ENABLE_SEMANTICS was false | Restart with the dart-define; semantics must be enabled before main runs |
| Canvas screenshot is all-white | Playwright clipped before the first frame painted | Increase wait: --wait-for=flutter-view[data-flt-renderer] |
| Hot reload fails | Same as native | Switch to hot_restart; re-navigate via Playwright |