| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| Invoke | /jaspr-web:match |
| Category | cocode-web |
| Complexity | high |
| MCP Servers | playwright |
/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 ์๋ ๊ฒ์ฌ๋ก ๊ฑธ ๋๋ ๊ฒฐ๊ณผ ํด๋๋ฅผ ๋ฐ๋ก ์ง์ ํ๊ณ , ๊ธ์จยท์ยท๋ฐฐ์น ์ฐจ์ด๊ฐ ํ๋๋ผ๋ ๋์ค๋ฉด ๊ฒ์ฌ๋ฅผ ์คํจ ์ฒ๋ฆฌํฉ๋๋ค(์ก์ยทํต๊ณผ๋ ๋ง์ง ์์).
์์์ ๋ฌด์จ ์ผ์ด ๋ฒ์ด์ง๋์#
ํ์ด์ง๋ฅผ ์ด์ด ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์นด๋๋ฅผ ํ๋์ฉ ๋๋ฉฐ ๋ ํ๋ฉด์ ๋น๊ตํฉ๋๋ค.
- ํ์ด์ง ์ด๊ธฐ โ ์ฃผ์๋ก ์ด๋ํด ํ๋ฉด์ด ๋ค ๋ฐ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค.
- ์นด๋ ์ฐพ๊ธฐ โ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์นด๋๋ฅผ ์ ๋ถ ์ฐพ์, ์นด๋๋ง๋ค ์น ํจ๋๊ณผ ํ๋ฌํฐ ํจ๋์ ์ง์ต๋๋ค.
- ์ค๋น ๊ธฐ๋ค๋ฆฌ๊ธฐ โ ์นด๋๋ฅผ ํ๋ฉด์ ๋์ฐ๊ณ , ํ๋ฌํฐ ์ชฝ "Loading Flutter..." ๋ฌธ๊ตฌ๊ฐ ์ฌ๋ผ์ ธ ๊ทธ๋ฆด ์ค๋น๊ฐ ๋ ๋๊น์ง(์ต๋ 15์ด) ๊ธฐ๋ค๋ฆฝ๋๋ค.
- ์ฌ์ง ์ฐ๊ธฐ โ ์น ํ๋ฉด๊ณผ ํ๋ฌํฐ ํ๋ฉด์ ๊ฐ๊ฐ ์บก์ฒํฉ๋๋ค(
A.png,B.png). - ์ฐจ์ด ๋น๊ตยท๋ถ๋ฅ โ ๋ ์ฌ์ง์ ๊ธฐ์ค๊ฐ์ผ๋ก ๋น๊ตํ๊ณ , ์ฐจ์ด๋ฅผ ๊ธ์จยท์ยท๋ฐฐ์นยท์ก์์ผ๋ก ๋ถ๋ฅํ ๋ค ๊ฒน์น ๋น๊ต ์ฌ์ง์ ๋ง๋ญ๋๋ค.
- ์ข
ํฉ ์ ๋ฆฌ โ ๋ชจ๋ ์นด๋ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์ ํ์ ์(
verdict.md)๋ก ์ ๋ฆฌํฉ๋๋ค.
โ๏ธ ์์ธ ์ต์ ยท์คํ ๋ช ์ธ (๊ฐ๋ฐ์ / AI ์์ด์ ํธ์ฉ)
Parameters#
| Parameter | Required | Description | Default |
|---|---|---|---|
--url | โ | Page URL with dual previews | โ |
--variants | โ | Comma-separated variant names; omit for "all visible" | all |
--tolerance-preset | โ | strict / default / loose | default |
--selectors | โ | Path to selector override YAML | bundled defaults |
--mask | โ | CSS selector list to mask out before diffing | none |
--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.mdExample#
/jaspr-web match \
--url=https://coui.cocode.im/live-preview-poc \
--tolerance-preset=defaultConsole 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.mdArtifacts#
.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 repeatabilityCI 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 divergenceWhen not to use this#
- The page renders only one of (Web, Flutter) โ use
/jaspr-web regressioninstead. - The Flutter side is in debug mode and you want to drive it interactively โ use
/jaspr-web widgetbook-web.