LogoSkills

jaspr-web:widgetbook-web

Drive a local CoUI Widgetbook running on Flutter Web debug mode (Playwright + Marionette VM Service).

ํ•ญ๋ชฉ๋‚ด์šฉ
Invoke/jaspr-web:widgetbook-web
Categorycocode-web
Complexityhigh
MCP Serversplaywright, 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)๋ฅผ ๋™์‹œ์— ๋ถ€๋ ค์„œ ๊ฐ์ž ์ž˜ํ•˜๋Š” ์ผ์„ ์‹œํ‚ต๋‹ˆ๋‹ค.

  1. ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด๊ณผ ์œ„์ ฏ ์—”์ง„์— ๋ชจ๋‘ ์—ฐ๊ฒฐ๋˜๋Š”์ง€ ๋จผ์ € ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. ์นดํƒˆ๋กœ๊ทธ์˜ ์ปดํฌ๋„ŒํŠธ ๋ชฉ๋ก์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.
  3. ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‚˜์”ฉ ๋Œ๋ฉด์„œ: ํ•ด๋‹น ํ•ญ๋ชฉ์„ ํด๋ฆญ โ†’ ํ™”๋ฉด์ด ์•ˆ์ •๋˜๊ธธ ๊ธฐ๋‹ค๋ฆผ โ†’ ์บก์ฒ˜ โ†’ ์‹ค์ œ๋กœ ๋ˆŒ๋Ÿฌ๋ณด๊ฑฐ๋‚˜ ์ž…๋ ฅํ•ด ๋ด„ โ†’ ๋‹ค์‹œ ์บก์ฒ˜ โ†’ ๋ˆ„๋ฅด๊ธฐ ์ „ํ›„ ํ™”๋ฉด์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.
  4. ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์•„ ์š”์•ฝ ๋ณด๊ณ ์„œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

โš™๏ธ ์ƒ์„ธ ์˜ต์…˜ยท์‹คํ–‰ ๋ช…์„ธ (๊ฐœ๋ฐœ์ž / AI ์—์ด์ „ํŠธ์šฉ)

Parameters#

ParameterRequiredDescriptionDefault
--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 outputauto
--routesโŒWidgetbook routes / UseCase paths to walkall visible
--limitโŒCap on UseCasesunlimited
--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=true

Look 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/xyz

The first is for Marionette; the second is for cc-flutter-pixel-loop if you also want pixel-loop (rare for web).

The ENABLE_SEMANTICS=true flag is a cocode convention โ€” guard SemanticsBinding.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 summary

Each layer does what it's best at:

StepPlaywrightMarionette
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/ws

Artifacts#

.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.md

When 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#

SymptomCauseRecovery
Marionette: not connectedflutter run printed a stale URI from a previous runRe-run flutter run --print-dtd; paste fresh URI
flt-semantics-host emptyENABLE_SEMANTICS was falseRestart with the dart-define; semantics must be enabled before main runs
Canvas screenshot is all-whitePlaywright clipped before the first frame paintedIncrease wait: --wait-for=flutter-view[data-flt-renderer]
Hot reload failsSame as nativeSwitch to hot_restart; re-navigate via Playwright