LogoSkills

jaspr-web:match

Dual Webโ†”Flutter Web embed pixel matching on cocode live-preview pages.

ํ•ญ๋ชฉ๋‚ด์šฉ
Invoke/jaspr-web:match
Categorycocode-web
Complexityhigh
MCP Serversplaywright

/jaspr-web match โ€” ์›นยทํ”Œ๋Ÿฌํ„ฐ ํ™”๋ฉด "๋‹ค๋ฅธ ๊ทธ๋ฆผ ์ฐพ๊ธฐ"#

ํ•œ๋งˆ๋””๋กœ#

๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์›น์œผ๋กœ ๊ทธ๋ฆฐ ํ™”๋ฉด๊ณผ ํ”Œ๋Ÿฌํ„ฐ๋กœ ๊ทธ๋ฆฐ ํ™”๋ฉด์„ ๋‚˜๋ž€ํžˆ ๋‘๊ณ , ํ”ฝ์…€ ๋‹จ์œ„๋กœ "์–ด๋””๊ฐ€ ๋‹ค๋ฅธ์ง€" ์ž๋™์œผ๋กœ ์ฐพ์•„์ฃผ๋Š” ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ํ‹€๋ฆฐ ๊ทธ๋ฆผ ์ฐพ๊ธฐ์ฒ˜๋Ÿผ ๋‘ ๊ทธ๋ฆผ์„ ๊ฒน์ณ ๋ณด๊ณ , ๋‹ค๋ฅธ ๋ถ€๋ถ„๋งŒ ๋นจ๊ฐ›๊ฒŒ ํ‘œ์‹œํ•œ ๋น„๊ต ์‚ฌ์ง„๊ณผ ๋ณด๊ณ ์„œ๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

๋ˆ„๊ฐ€ยท์–ธ์ œ ์“ฐ๋‚˜์š”#

  • ๊ฐ™์€ UI๋ฅผ ์›น๊ณผ ํ”Œ๋Ÿฌํ„ฐ ๋‘ ๋ฐฉ์‹์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํŽ˜์ด์ง€(live-preview ๋ฅ˜)๋ฅผ ์ ๊ฒ€ํ•  ๋•Œ
  • "์›น์—์„œ๋Š” ์ž˜ ๋ณด์ด๋Š”๋ฐ ํ”Œ๋Ÿฌํ„ฐ์—์„œ๋Š” ๊ธ€์”จ ๊ตต๊ธฐยท์ƒ‰ยท๋ฐฐ์น˜๊ฐ€ ๋‹ค๋ฅธ ๊ฒƒ ๊ฐ™๋‹ค"๋ฅผ ๋ˆˆ์ด ์•„๋‹ˆ๋ผ ์ˆ˜์น˜๋กœ ํ™•์ธํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • PR(์ฝ”๋“œ ๋ณ€๊ฒฝ ์š”์ฒญ)์„ ํ•ฉ์น˜๊ธฐ ์ „์— ์ž๋™ ๊ฒ€์‚ฌ ๊ด€๋ฌธ์œผ๋กœ ๊ฑธ์–ด, ์ง„์งœ ์ฐจ์ด๊ฐ€ ์ƒ๊ธฐ๋ฉด ํ†ต๊ณผ๋ฅผ ๋ง‰๊ณ  ์‹ถ์„ ๋•Œ

๐Ÿ‘‰ ์ด๋Ÿด ๋• ๋‹ค๋ฅธ ๋ช…๋ น์„ ์“ฐ์„ธ์š”: ํŽ˜์ด์ง€์— ์›นยทํ”Œ๋Ÿฌํ„ฐ ์ค‘ ํ•œ์ชฝ๋งŒ ์žˆ์œผ๋ฉด /jaspr-web regression, ํ”Œ๋Ÿฌํ„ฐ ์ชฝ์„ ์ง์ ‘ ๋งŒ์ ธ ๋ณด๋ฉฐ ํ™•์ธํ•˜๋ ค๋ฉด /jaspr-web widgetbook-web.

๋ฌด์—‡์„ ํ•ด์ฃผ๋‚˜์š”#

์ง€์ •ํ•œ ํŽ˜์ด์ง€์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์นด๋“œ๋งˆ๋‹ค ๋น„๊ต ๊ฒฐ๊ณผ๋ฅผ ์ •๋ฆฌํ•ด, .claude/docs/jaspr-web/match-<์‹œ๊ฐ>/ ํด๋” ํ•˜๋‚˜๋กœ ๋‚จ๊น๋‹ˆ๋‹ค. ๊ทธ ์•ˆ์—๋Š”:

  • ๋ณ€ํ˜•๋ณ„ ๋น„๊ต ์‚ฌ์ง„ โ€” ์›น ํ™”๋ฉด(A.png), ํ”Œ๋Ÿฌํ„ฐ ํ™”๋ฉด(B.png), ๊ทธ๋ฆฌ๊ณ  ๋‘˜์„ ๊ฒน์ณ ์ฐจ์ด๋ฅผ ๋นจ๊ฐ›๊ฒŒ ์น ํ•œ ์‚ฌ์ง„(diff.png)
  • ์ฐจ์ด ๋ถ„๋ฅ˜ ๊ธฐ๋ก(classification.yaml) โ€” ์ฐจ์ด๊ฐ€ ๊ธ€์”จ(typography)ยท์ƒ‰(color)ยท๋ฐฐ์น˜(layout)ยท์žก์Œ(noise) ์ค‘ ๋ฌด์—‡ ๋•Œ๋ฌธ์ธ์ง€
  • ์ข…ํ•ฉ ํŒ์ •์„œ(verdict.md) โ€” "8๊ฐœ ์ค‘ 6๊ฐœ ํ†ต๊ณผ, 1๊ฐœ ๊ธ€์”จ ์ฐจ์ด, 1๊ฐœ ์ƒ‰ ์ฐจ์ด" ์‹์˜ ์š”์•ฝ
  • ์‚ฌ์šฉํ•œ ๊ธฐ์ค€ ๊ธฐ๋ก(selectors-used.yaml) โ€” ๊ฐ™์€ ๊ฒ€์‚ฌ๋ฅผ ๋˜‘๊ฐ™์ด ๋‹ค์‹œ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋„๋ก

์–ด๋–ป๊ฒŒ ์“ฐ๋‚˜์š”#

# ๊ธฐ๋ณธ ์‚ฌ์šฉ - ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํŽ˜์ด์ง€ ์ฃผ์†Œ๋งŒ ์ฃผ๋ฉด ๋จ
/jaspr-web match \
  --url=https://coui.cocode.im/live-preview-poc \
  --tolerance-preset=default
  • --url (ํ•„์ˆ˜): ์›นยทํ”Œ๋Ÿฌํ„ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ๊ฐ™์ด ์žˆ๋Š” ํŽ˜์ด์ง€ ์ฃผ์†Œ
  • --variants: ํŠน์ • ๋ณ€ํ˜•๋งŒ ๋น„๊ตํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์ด๋ฆ„์„ ์‰ผํ‘œ๋กœ ๋‚˜์—ด (๋น„์šฐ๋ฉด ๋ณด์ด๋Š” ์ „๋ถ€)
  • --tolerance-preset: ์–ผ๋งˆ๋‚˜ ๊น๊นํ•˜๊ฒŒ ๋ณผ์ง€ โ€” strict(์—„๊ฒฉ) / default(๊ธฐ๋ณธ) / loose(๋А์Šจ)
  • --mask: ๋น„๊ต์—์„œ ์ œ์™ธํ•˜๊ณ  ์‹ถ์€ ์˜์—ญ ์ง€์ •
  • --output: ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  ํด๋” ์œ„์น˜

PR ์ž๋™ ๊ฒ€์‚ฌ๋กœ ๊ฑธ ๋•Œ๋Š” ๊ฒฐ๊ณผ ํด๋”๋ฅผ ๋”ฐ๋กœ ์ง€์ •ํ•˜๊ณ , ๊ธ€์”จยท์ƒ‰ยท๋ฐฐ์น˜ ์ฐจ์ด๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ๋‚˜์˜ค๋ฉด ๊ฒ€์‚ฌ๋ฅผ ์‹คํŒจ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค(์žก์Œยทํ†ต๊ณผ๋Š” ๋ง‰์ง€ ์•Š์Œ).

์•ˆ์—์„œ ๋ฌด์Šจ ์ผ์ด ๋ฒŒ์–ด์ง€๋‚˜์š”#

ํŽ˜์ด์ง€๋ฅผ ์—ด์–ด ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์นด๋“œ๋ฅผ ํ•˜๋‚˜์”ฉ ๋Œ๋ฉฐ ๋‘ ํ™”๋ฉด์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

  1. ํŽ˜์ด์ง€ ์—ด๊ธฐ โ€” ์ฃผ์†Œ๋กœ ์ด๋™ํ•ด ํ™”๋ฉด์ด ๋‹ค ๋œฐ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.
  2. ์นด๋“œ ์ฐพ๊ธฐ โ€” ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์นด๋“œ๋ฅผ ์ „๋ถ€ ์ฐพ์•„, ์นด๋“œ๋งˆ๋‹ค ์›น ํŒจ๋„๊ณผ ํ”Œ๋Ÿฌํ„ฐ ํŒจ๋„์„ ์งš์Šต๋‹ˆ๋‹ค.
  3. ์ค€๋น„ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ โ€” ์นด๋“œ๋ฅผ ํ™”๋ฉด์— ๋„์šฐ๊ณ , ํ”Œ๋Ÿฌํ„ฐ ์ชฝ "Loading Flutter..." ๋ฌธ๊ตฌ๊ฐ€ ์‚ฌ๋ผ์ ธ ๊ทธ๋ฆด ์ค€๋น„๊ฐ€ ๋  ๋•Œ๊นŒ์ง€(์ตœ๋Œ€ 15์ดˆ) ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.
  4. ์‚ฌ์ง„ ์ฐ๊ธฐ โ€” ์›น ํ™”๋ฉด๊ณผ ํ”Œ๋Ÿฌํ„ฐ ํ™”๋ฉด์„ ๊ฐ๊ฐ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค(A.png, B.png).
  5. ์ฐจ์ด ๋น„๊ตยท๋ถ„๋ฅ˜ โ€” ๋‘ ์‚ฌ์ง„์„ ๊ธฐ์ค€๊ฐ’์œผ๋กœ ๋น„๊ตํ•˜๊ณ , ์ฐจ์ด๋ฅผ ๊ธ€์”จยท์ƒ‰ยท๋ฐฐ์น˜ยท์žก์Œ์œผ๋กœ ๋ถ„๋ฅ˜ํ•œ ๋’ค ๊ฒน์นœ ๋น„๊ต ์‚ฌ์ง„์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  6. ์ข…ํ•ฉ ์ •๋ฆฌ โ€” ๋ชจ๋“  ์นด๋“œ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์•„ ํŒ์ •์„œ(verdict.md)๋กœ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

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

Parameters#

ParameterRequiredDescriptionDefault
--urlโœ…Page URL with dual previewsโ€”
--variantsโŒComma-separated variant names; omit for "all visible"all
--tolerance-presetโŒstrict / default / loosedefault
--selectorsโŒPath to selector override YAMLbundled defaults
--maskโŒCSS selector list to mask out before diffingnone
--outputโŒOutput dir.claude/docs/jaspr-web/match-<ts>/

Default selectors#

variant_card:       ' [data-co= " preview-card " ] ' 
 preview_web_panel:  ' [data-co= " preview-web " ] ' 
 preview_flutter_panel:  ' [data-co= " preview-flutter " ] ' 
 flutter_ready_indicator:  ' flutter-view[data-flt-renderer] ' 
 flutter_loading_text:  ' text= " Loading Flutter... " ' 
 variant_label:  ' [data-co= " preview-variant-label " ] '

If coui.cocode.im/live-preview-poc uses different markers, override via --selectors. The skill stops with a clear error if the catalogue doesn't resolve.

Loop#

1. Navigate to  < url > 
 2. Wait for networkidle
3. Find all [data-co= " preview-card " ]; for each:
   a. Find Web + Flutter panels
   b. Scroll card into view; wait for Flutter panel ' s ready indicator
      (re-poll until  " Loading Flutter... "   text disappears, max 15s)
   c. takeScreenshot --clip= < web-rect >     โ†’ A.png
   d. takeScreenshot --clip= < flutter-rect >   โ†’ B.png
   e. Diff A vs B with tolerance preset
   f. Classify divergence (typography / color / layout / noise)
   g. Write 3-up image (A | B | diff overlay)
4. Aggregate into verdict.md

Example#

/jaspr-web match \
  --url=https://coui.cocode.im/live-preview-poc \
  --tolerance-preset=default

Console transcript (excerpt):

[Card 1/8] Button โ€” Primary
  โœ“ Web rendered            (24x32+312x28)
  โœ“ Flutter rendered        (24x32+312x28) โ€” first frame at 412ms
  โœ“ Diff: 0.4% pixels, 0.0 ฮ”E โ€” pass
[Card 2/8] Button โ€” Outline
  โœ“ Web rendered
  โœ“ Flutter rendered
  โœ— Diff: 2.1% pixels, 3.2 ฮ”E โ€” typography divergence (font-weight: 500 vs 600)
[Card 3/8] Button โ€” Ghost  โ†’ pass
...
Summary: 6 pass / 1 typography / 1 color
Report: .claude/docs/jaspr-web/match-20260512-1130/verdict.md

Artifacts#

.claude/docs/jaspr-web/match- < ts > /
โ”œโ”€โ”€ variants/
โ”‚   โ”œโ”€โ”€ button_primary/
โ”‚   โ”‚   โ”œโ”€โ”€ A.png            # Web panel clip
โ”‚   โ”‚   โ”œโ”€โ”€ B.png            # Flutter panel clip
โ”‚   โ”‚   โ”œโ”€โ”€ diff.png         # 3-up overlay
โ”‚   โ”‚   โ””โ”€โ”€ classification.yaml
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ verdict.md
โ””โ”€โ”€ selectors-used.yaml      # for repeatability

CI Wiring#

For a PR gate, fail the job when any classification is typography, color, or layout. noise and pass are non-blocking.

/jaspr-web match \
  --url=$DEPLOY_URL/live-preview-poc \
  --tolerance-preset=default \
  --output=$GITHUB_WORKSPACE/artifacts/match/
# parse classification.yaml; exit non-zero on real divergence

When not to use this#

  • The page renders only one of (Web, Flutter) โ€” use /jaspr-web regression instead.
  • The Flutter side is in debug mode and you want to drive it interactively โ€” use /jaspr-web widgetbook-web.