@zenuml/core 3.45.4 → 3.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/dia-scoring/SKILL.md +139 -0
- package/.claude/skills/dia-scoring/agents/openai.yaml +7 -0
- package/.claude/skills/dia-scoring/references/selectors-and-keys.md +253 -0
- package/CLAUDE.md +1 -1
- package/bun.lock +25 -11
- package/cy/canonical-history.html +908 -0
- package/cy/compare-case.html +357 -0
- package/cy/compare-cases.js +824 -0
- package/cy/compare.html +35 -0
- package/cy/diff-algorithm.js +199 -0
- package/cy/element-report.html +705 -0
- package/cy/icons-test.html +29 -0
- package/cy/legacy-vs-html.html +291 -0
- package/cy/native-diff-ext/background.js +60 -0
- package/cy/native-diff-ext/bridge.js +26 -0
- package/cy/native-diff-ext/content.js +194 -0
- package/cy/parity-test.html +122 -0
- package/cy/return-in-nested-if.html +29 -0
- package/cy/svg-preview.html +56 -0
- package/cy/svg-test.html +21 -0
- package/cy/theme-default-test.html +28 -0
- package/dist/stats.html +1 -1
- package/dist/zenuml.esm.mjs +16352 -15223
- package/dist/zenuml.js +701 -575
- package/docs/superpowers/plans/2026-03-23-svg-parity-features.md +283 -0
- package/index.html +568 -73
- package/package.json +15 -4
- package/scripts/analyze-compare-case/collect-data.mjs +991 -0
- package/scripts/analyze-compare-case/config.mjs +102 -0
- package/scripts/analyze-compare-case/geometry.mjs +101 -0
- package/scripts/analyze-compare-case/native-diff.mjs +224 -0
- package/scripts/analyze-compare-case/output.mjs +74 -0
- package/scripts/analyze-compare-case/panel-diff.mjs +114 -0
- package/scripts/analyze-compare-case/report.mjs +157 -0
- package/scripts/analyze-compare-case/residual-scopes.mjs +325 -0
- package/scripts/analyze-compare-case/scoring.mjs +816 -0
- package/scripts/analyze-compare-case.mjs +149 -0
- package/scripts/snapshot-dual.js +34 -34
- package/skills/dia-scoring/SKILL.md +129 -0
- package/skills/dia-scoring/agents/openai.yaml +7 -0
- package/skills/dia-scoring/references/selectors-and-keys.md +253 -0
- package/test-setup.ts +8 -0
- package/types/index.d.ts +56 -0
- package/vite.config.ts +4 -0
- package/dist/10029-icon-service-Function-Apps-ObflOLuF.js +0 -5
- package/dist/Res_AWS-Identity-Access-Management_IAM-Access-Analyzer_48-BPq60XMY.js +0 -11
- package/dist/Res_AWS-Lambda_Lambda-Function_48-Co38UB_2.js +0 -12
- package/dist/Res_Amazon-EC2_Instance_48-CRaqbNUl.js +0 -12
- package/dist/Res_Amazon-Simple-Notification-Service_Topic_48-q13mxUeM.js +0 -11
- package/dist/Res_Amazon-Simple-Queue-Service_Queue_48-D2-8gbFw.js +0 -11
- package/dist/Robustness_Diagram_Boundary-nYnmTPs8.js +0 -10
- package/dist/Robustness_Diagram_Control-DLNLoMxd.js +0 -11
- package/dist/Robustness_Diagram_Entity-Be3kcbIE.js +0 -11
- package/dist/actor-BMj_HFpo.js +0 -11
- package/dist/database-BKHQQWQK.js +0 -8
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dia-scoring
|
|
3
|
+
description: Score HTML-vs-SVG diagram parity in compare-case pages, including message labels, fragment labels, sequence numbers, arrows, participant headers, icons, stereotypes, participant colors, participant groups, comments, and residual diff scopes. Use Playwright for page inspection and semantic attribution.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Dia Scoring
|
|
7
|
+
|
|
8
|
+
Use this skill when the task is to measure **message labels, fragment labels, sequence numbers, message arrows, participant labels, participant boxes, participant icons, stereotypes, participant colors, participant groups, inline comments, and residual diff hotspots** between the HTML renderer and the native SVG renderer on `compare-case.html`.
|
|
9
|
+
|
|
10
|
+
## Diff Source of Truth
|
|
11
|
+
|
|
12
|
+
The `native-diff-ext` Chrome extension is the absolute source of truth for pixel diff. The analyzer script (`scripts/analyze-compare-case.mjs`) uses the same CDP screenshot capture method and the same diff algorithm (`cy/diff-algorithm.js`), producing identical results when run against the same viewport.
|
|
13
|
+
|
|
14
|
+
- Use the **analyzer script** as the primary scoring tool (automated, reproducible, CLI-driven)
|
|
15
|
+
- Use the **extension** for live interactive inspection in the browser
|
|
16
|
+
- Both use CDP `Page.captureScreenshot` with `DOM.getBoxModel` border-box clip
|
|
17
|
+
- When calibrating the skill, verify against the extension's live `#diff-panel canvas`
|
|
18
|
+
|
|
19
|
+
The workflow:
|
|
20
|
+
|
|
21
|
+
1. Run `node scripts/analyze-compare-case.mjs --case <name> --json` for structured data.
|
|
22
|
+
2. Use `--output-dir <dir>` when you need saved `html.png`, `svg.png`, `diff.png`, and `report.json`.
|
|
23
|
+
3. For live browser inspection, navigate to `http://localhost:8080/cy/compare-case.html?case=<name>` and use the extension's `#diff-panel canvas`.
|
|
24
|
+
4. Use Playwright page inspection for semantic attribution (element positions, font metrics, DOM structure).
|
|
25
|
+
|
|
26
|
+
## Offset Anchor
|
|
27
|
+
|
|
28
|
+
All reported offsets must use the **outermost frame's top-left corner** as the anchor.
|
|
29
|
+
|
|
30
|
+
- HTML anchor: the compare-case HTML frame root
|
|
31
|
+
- SVG anchor: the compare-case SVG root / outer frame root
|
|
32
|
+
- Do not report alternate offset systems
|
|
33
|
+
- Do not anchor offsets to participant boxes, label boxes, stereotype boxes, or local containers
|
|
34
|
+
- If a local-container-relative reading differs from the frame-anchor reading, prefer the frame-anchor reading in all reporting
|
|
35
|
+
|
|
36
|
+
## Browser Requirement
|
|
37
|
+
|
|
38
|
+
Use **Playwright browser tools only** for browser interaction in this workflow.
|
|
39
|
+
|
|
40
|
+
- Preferred tools: `browser_navigate`, `browser_snapshot`, `browser_evaluate`, `browser_take_screenshot`, `browser_click`, `browser_wait_for`
|
|
41
|
+
- Do not use Chrome DevTools browser tools for scoring, DOM inspection, screenshot capture, or residual validation
|
|
42
|
+
- Do not build your own pixel diff from HTML/SVG screenshots. For pixel comparison, use only the extension-rendered `#diff-panel canvas`
|
|
43
|
+
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- Do not use `html-to-image` for capture.
|
|
47
|
+
- Use browser-native screenshots only.
|
|
48
|
+
- Use Playwright for browser-native screenshots and page inspection.
|
|
49
|
+
- All offset calculations must be anchored to the outermost frame's top-left corner.
|
|
50
|
+
- When recalibrating the skill itself, verify against the extension's live `#diff-panel canvas`.
|
|
51
|
+
- Do not use Chrome DevTools browser tools for this workflow.
|
|
52
|
+
- Scope:
|
|
53
|
+
- normal messages
|
|
54
|
+
- self messages
|
|
55
|
+
- returns
|
|
56
|
+
- creation messages (e.g., `«payload»`, `new Order()`)
|
|
57
|
+
- fragment conditions such as `[cond]`, `[else]`
|
|
58
|
+
- fragment section labels such as `catch`, `finally`
|
|
59
|
+
- participant label text and participant box geometry
|
|
60
|
+
- participant icons (actor, database, ec2, lambda, azurefunction, sqs, sns, iam, boundary, control, entity)
|
|
61
|
+
- participant stereotypes such as `«BFF»`, `«Interface»`
|
|
62
|
+
- participant background colors (`#FFEBE6`, `#0747A6`, etc.) and computed text contrast
|
|
63
|
+
- participant groups (dashed outline containers with title bar)
|
|
64
|
+
- inline comments (`// text`) above messages and fragments, including styled comments (`// [red] text`)
|
|
65
|
+
- residual `html-only` and `svg-only` diff clusters scoped back to nearby elements
|
|
66
|
+
- For each supported message, include:
|
|
67
|
+
- label text
|
|
68
|
+
- fragment condition / section label text when present
|
|
69
|
+
- sequence number text, including fragment sequence numbers when present
|
|
70
|
+
- arrow geometry keyed by sequence number
|
|
71
|
+
- normal/return arrow endpoint deltas: `left_dx`, `right_dx`, `width_dx`
|
|
72
|
+
- self-arrow loop geometry from the painted loop path plus arrowhead, not the outer `svg` viewport
|
|
73
|
+
- self-arrow vertical deltas: `top_dy`, `bottom_dy`, `height_dy`
|
|
74
|
+
- For participant icons, include:
|
|
75
|
+
- icon presence (HTML vs SVG)
|
|
76
|
+
- participant label text when the participant has an icon
|
|
77
|
+
- icon position relative to participant label
|
|
78
|
+
- icon visual match confirmation from diff image
|
|
79
|
+
- For participant stereotypes, include:
|
|
80
|
+
- stereotype text presence (HTML vs SVG), e.g. `«BFF»`
|
|
81
|
+
- stereotype position relative to participant label (above label, smaller font)
|
|
82
|
+
- stereotype offset must be measured with per-letter glyph-box comparison relative to the outermost frame anchor
|
|
83
|
+
- do not use participant-box-relative or other local-container-relative deltas in final reporting
|
|
84
|
+
- do not mark a stereotype as clean from glyph boxes alone; also check the live `#diff-panel canvas` in the stereotype row
|
|
85
|
+
- if glyph-box deltas are `0/0` but the panel still shows localized red/blue pixels overlapping the stereotype glyph union, report the stereotype as `ambiguous` or `paint-level residual`, not clean
|
|
86
|
+
- stereotype text color matching participant background contrast
|
|
87
|
+
- For participant colors, include:
|
|
88
|
+
- background fill color (hex value) on participant rect
|
|
89
|
+
- text color contrast (dark text on light bg, white text on dark bg)
|
|
90
|
+
- color application to both top and bottom participant boxes
|
|
91
|
+
- For participant groups, include:
|
|
92
|
+
- group name text presence and position (centered title bar)
|
|
93
|
+
- dashed outline rect enclosing grouped participants
|
|
94
|
+
- group bounds: leftmost to rightmost participant with margin
|
|
95
|
+
- group height extending to diagram bottom
|
|
96
|
+
- For inline comments, include:
|
|
97
|
+
- comment text presence and position (above the associated statement)
|
|
98
|
+
- comment Y offset from the message/fragment it belongs to
|
|
99
|
+
- fragment-level comments (e.g. `// comment 4` before `if(...)`) positioned above fragment header
|
|
100
|
+
- when all letters are `ambiguous` due to large positional offset (e.g. fragment comments at wrong X), the analyzer reports `box_dx` / `box_dy` from the bounding boxes instead of suppressing the measurement
|
|
101
|
+
- styled comment color application (e.g. `// [red] text`)
|
|
102
|
+
- For participant boxes, use the analyzer script output directly:
|
|
103
|
+
- Report `html_box`, `svg_box`, `dx`, `dy`, `dw`, `dh` from the script's `participant_boxes` section
|
|
104
|
+
- The script already applies stroke correction (`strokedElementOuterRect`) — do not re-measure with `browser_evaluate`
|
|
105
|
+
- For residual scopes, include:
|
|
106
|
+
- connected `html-only` and `svg-only` diff clusters from `#diff-panel canvas`
|
|
107
|
+
- cluster `size`, `bbox`, and `centroid`
|
|
108
|
+
- nearest scoped HTML and SVG targets at that position
|
|
109
|
+
- summaries that explain which element a remaining positional diff most likely belongs to
|
|
110
|
+
- live native diff panel confirmation before claiming a hotspot is real
|
|
111
|
+
- the largest confirmed live-panel `html-only` and `svg-only` clusters with approximate positions
|
|
112
|
+
- grouped summaries of where the panel's red and blue pixels are concentrated
|
|
113
|
+
- Do not report a residual hotspot as real if it is absent from the live `#diff-panel canvas`.
|
|
114
|
+
- Do not stop at totals like `HTML-only (44)` or `SVG-only (55)` when residuals matter; report where those pixels are.
|
|
115
|
+
- Each reported letter must be backed by:
|
|
116
|
+
- direct HTML-vs-SVG browser layout positions
|
|
117
|
+
- pixel-panel confirmation from `#diff-panel canvas`
|
|
118
|
+
- Participant stereotypes are first-class targets, not just part of `participant-box` or `participant-label`.
|
|
119
|
+
- If the evidence is weak or contradictory, keep the letter `ambiguous`.
|
|
120
|
+
|
|
121
|
+
## Known Analyzer Internals
|
|
122
|
+
|
|
123
|
+
### Arrow pairing by sequence number
|
|
124
|
+
|
|
125
|
+
The analyzer pairs arrows by sequence number (`text` field), not by label text (`pairText`). Calibrated on `repro-creation-return-arrow` (2026-03-24).
|
|
126
|
+
|
|
127
|
+
## Commands
|
|
128
|
+
|
|
129
|
+
Run from [../..](../..):
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
node scripts/analyze-compare-case.mjs --case async-2a
|
|
133
|
+
node scripts/analyze-compare-case.mjs --case async-2a --json
|
|
134
|
+
node scripts/analyze-compare-case.mjs --case async-2a --output-dir tmp/message-elements/async-2a
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## References
|
|
138
|
+
|
|
139
|
+
- Selector and pairing details: [references/selectors-and-keys.md](references/selectors-and-keys.md)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Dia Scoring"
|
|
3
|
+
short_description: "Diagram label, number, and arrow offsets"
|
|
4
|
+
default_prompt: "Use $dia-scoring to measure message label, sequence-number, and arrow parity for a compare-case page."
|
|
5
|
+
|
|
6
|
+
policy:
|
|
7
|
+
allow_implicit_invocation: true
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Selectors And Keys
|
|
2
|
+
|
|
3
|
+
The analyzer uses these roots:
|
|
4
|
+
|
|
5
|
+
- HTML root: `#html-output .frame`, fallback `#html-output .sequence-diagram`
|
|
6
|
+
- SVG root: `#svg-output > svg`
|
|
7
|
+
|
|
8
|
+
Offset anchor:
|
|
9
|
+
|
|
10
|
+
- All reported offsets use the outermost frame root's top-left corner
|
|
11
|
+
- HTML side: `#html-output .frame`, fallback `#html-output .sequence-diagram`
|
|
12
|
+
- SVG side: `#svg-output > svg`
|
|
13
|
+
- Do not emit final `dx` / `dy` values from participant-local or other nested-container anchors
|
|
14
|
+
|
|
15
|
+
HTML label extraction:
|
|
16
|
+
|
|
17
|
+
- Normal messages: iterate `.interaction`, skip `.return`, `.creation`, and self interactions, then read `.message .editable-span-base`
|
|
18
|
+
- Self messages: `.self-invocation .label .editable-span-base`
|
|
19
|
+
- Returns: `.interaction.return .message .editable-span-base`, fallback `.interaction.return .name`
|
|
20
|
+
- Fragment conditions: `.fragment .segment > .text-skin-fragment:not(.finally)`, using only visible child spans when conditional branches are stacked
|
|
21
|
+
- Fragment sections:
|
|
22
|
+
- `.fragment.fragment-tcf .segment > .header.inline-block.bg-skin-frame.opacity-65`
|
|
23
|
+
- `.fragment.fragment-tcf .segment > .header.finally`
|
|
24
|
+
|
|
25
|
+
SVG label extraction:
|
|
26
|
+
|
|
27
|
+
- Normal messages: `g.message:not(.self-call) > text.message-label`
|
|
28
|
+
- Self messages: `g.message.self-call > text.message-label`
|
|
29
|
+
- Returns: `g.return > text.return-label`
|
|
30
|
+
- Fragment conditions: `g.fragment > text.fragment-condition`
|
|
31
|
+
- Fragment condition / section groups: `g.fragment > g` containing `text.fragment-section-label`
|
|
32
|
+
- texts starting with `[` are treated as `fragment-condition`
|
|
33
|
+
- other texts are treated as `fragment-section`
|
|
34
|
+
|
|
35
|
+
Pairing key:
|
|
36
|
+
|
|
37
|
+
- Semantic grouping is by `kind + text`
|
|
38
|
+
- Duplicate labels are paired by top-to-bottom order within that group
|
|
39
|
+
- Output key is:
|
|
40
|
+
- `kind`
|
|
41
|
+
- `text`
|
|
42
|
+
- `y_order`
|
|
43
|
+
- Fragment labels also include `owner=<fragment header>` in the human-readable summary when available
|
|
44
|
+
|
|
45
|
+
Per-letter scoring:
|
|
46
|
+
|
|
47
|
+
- Grapheme segmentation uses `Intl.Segmenter`, fallback `Array.from`
|
|
48
|
+
- Glyph boxes come from browser layout ranges, not whole-word centroids
|
|
49
|
+
- Numeric `dx` and `dy` are only emitted when direct layout evidence and diff-image evidence agree
|
|
50
|
+
|
|
51
|
+
Arrow extraction:
|
|
52
|
+
|
|
53
|
+
- HTML normal/return messages:
|
|
54
|
+
- line: direct child `svg` line strip inside `.message`
|
|
55
|
+
- head: direct child arrowhead `svg` inside `.message`
|
|
56
|
+
- HTML self messages:
|
|
57
|
+
- loop: painted geometry inside `svg.arrow`
|
|
58
|
+
- parts: outer loop path plus nested arrowhead path
|
|
59
|
+
- SVG normal messages:
|
|
60
|
+
- line: `line.message-line`
|
|
61
|
+
- head: `svg.arrow-head`
|
|
62
|
+
- SVG returns:
|
|
63
|
+
- line: `line.return-line`
|
|
64
|
+
- head: `polyline.return-arrow`
|
|
65
|
+
- SVG self messages:
|
|
66
|
+
- loop: painted geometry inside the outer `svg` under `g.message.self-call`
|
|
67
|
+
- parts: outer loop path plus nested arrowhead path
|
|
68
|
+
|
|
69
|
+
Arrow scoring:
|
|
70
|
+
|
|
71
|
+
- Arrows are keyed by sequence number when numbering is available, for example `arrow:1.2.3`
|
|
72
|
+
- Normal and return arrows are measured as one combined geometry item:
|
|
73
|
+
- line + arrow head together
|
|
74
|
+
- Self arrows are measured as one loop geometry item
|
|
75
|
+
- Self arrows use the union of the painted loop path and arrowhead path, not the outer viewport box
|
|
76
|
+
- Arrow output is endpoint-based, not box-centroid-based
|
|
77
|
+
- For normal and return arrows, report:
|
|
78
|
+
- `left_dx`
|
|
79
|
+
- `right_dx`
|
|
80
|
+
- `width_dx`
|
|
81
|
+
- For self arrows, also report:
|
|
82
|
+
- `top_dy`
|
|
83
|
+
- `bottom_dy`
|
|
84
|
+
- `height_dy`
|
|
85
|
+
- Do not report `dy` for horizontal message arrows
|
|
86
|
+
|
|
87
|
+
HTML sequence number extraction:
|
|
88
|
+
|
|
89
|
+
- Normal messages: `.interaction:not(.return):not(.creation):not(.self-invocation):not(.self) > .message > .absolute.text-xs`
|
|
90
|
+
- Self messages: `.interaction.self-invocation > .message .absolute.text-xs`
|
|
91
|
+
- Returns: `.interaction.return > .message > .absolute.text-xs`
|
|
92
|
+
- Fragments: `.fragment > .header > .absolute.text-xs`
|
|
93
|
+
|
|
94
|
+
SVG sequence number extraction:
|
|
95
|
+
|
|
96
|
+
- Normal messages: `g.message:not(.self-call) > text.seq-number`
|
|
97
|
+
- Self messages: `g.message.self-call > text.seq-number`
|
|
98
|
+
- Returns: `g.return > text.seq-number`
|
|
99
|
+
- Fragments: `g.fragment > text.seq-number`
|
|
100
|
+
|
|
101
|
+
## Participant Icon Extraction
|
|
102
|
+
|
|
103
|
+
## Participant Header Extraction
|
|
104
|
+
|
|
105
|
+
HTML participant header extraction:
|
|
106
|
+
|
|
107
|
+
- Participant root: `.participant[data-participant-id]`
|
|
108
|
+
- Participant box: outer border box from the participant root element
|
|
109
|
+
- Participant stereotype: `label.interface`, when present
|
|
110
|
+
- Participant label: last `.name` descendant, measured by glyph boxes
|
|
111
|
+
|
|
112
|
+
SVG participant header extraction:
|
|
113
|
+
|
|
114
|
+
- Participant root: `g.participant[data-participant]`
|
|
115
|
+
- Skip `g.participant-bottom`
|
|
116
|
+
- Participant box element: `:scope > rect.participant-box`
|
|
117
|
+
- Participant box measurement must use the painted outer bounds of the stroked rect, not the inset rect geometry
|
|
118
|
+
- Participant stereotype:
|
|
119
|
+
- prefer `:scope > text.stereotype-label`
|
|
120
|
+
- fallback: top-most direct `text` child above `text.participant-label`
|
|
121
|
+
- Participant label: `:scope > text.participant-label`
|
|
122
|
+
|
|
123
|
+
Participant stereotype pairing and scoring:
|
|
124
|
+
|
|
125
|
+
- Pair by participant name
|
|
126
|
+
- Validate text equality, for example `«BFF»`
|
|
127
|
+
- Measure stereotype offset by per-letter glyph boxes relative to the outermost frame root, not by participant-local anchors or whole-word box centroids
|
|
128
|
+
- Report:
|
|
129
|
+
- `letter_deltas`
|
|
130
|
+
- concise aggregate only when the per-letter evidence agrees
|
|
131
|
+
- Do not mark the stereotype clean from glyph boxes alone
|
|
132
|
+
- Also check the live `#diff-panel canvas` over the union of the HTML and SVG stereotype glyph boxes
|
|
133
|
+
- If localized red or blue pixels persist in that stereotype region while glyph-box deltas are `0/0`, classify it as `ambiguous` or `paint-level residual`
|
|
134
|
+
|
|
135
|
+
Participant box pairing and scoring:
|
|
136
|
+
|
|
137
|
+
- Pair by participant name
|
|
138
|
+
- Report `html_box` and `svg_box` with `x`, `y`, `w`, `h`
|
|
139
|
+
- Box `x` / `y` values are frame-anchor-relative
|
|
140
|
+
- Report box deltas:
|
|
141
|
+
- `dx`
|
|
142
|
+
- `dy`
|
|
143
|
+
- `dw`
|
|
144
|
+
- `dh`
|
|
145
|
+
|
|
146
|
+
HTML icon extraction:
|
|
147
|
+
|
|
148
|
+
- Participant root: `.participant[data-participant-id]`
|
|
149
|
+
- Top-row participant only: keep the top-most entry for each participant id
|
|
150
|
+
- Icon host: first child inside the centered participant row when it is an async icon host
|
|
151
|
+
- `[aria-description]`
|
|
152
|
+
- or contains `svg`
|
|
153
|
+
- or has `h-6` sizing class from `AsyncIcon`
|
|
154
|
+
- Icon box: union of painted SVG shapes when available, fallback to the host box
|
|
155
|
+
- Participant label: last `.name` descendant, measured by glyph boxes
|
|
156
|
+
|
|
157
|
+
SVG icon extraction:
|
|
158
|
+
|
|
159
|
+
- Participant root: `g.participant[data-participant]`
|
|
160
|
+
- Skip `g.participant-bottom`
|
|
161
|
+
- Icon element: `:scope > g[transform]`
|
|
162
|
+
- Icon box: union of painted shapes within that transformed group
|
|
163
|
+
- Participant label: `:scope > text.participant-label`
|
|
164
|
+
|
|
165
|
+
Icon pairing:
|
|
166
|
+
|
|
167
|
+
- Pair by participant name
|
|
168
|
+
- Only report participant icon rows for participants where at least one side has an icon
|
|
169
|
+
- Participant labels for icon-bearing participants are paired by participant name, not raw label text
|
|
170
|
+
|
|
171
|
+
Icon scoring:
|
|
172
|
+
|
|
173
|
+
- Absolute icon drift:
|
|
174
|
+
- `icon_dx`
|
|
175
|
+
- `icon_dy`
|
|
176
|
+
- Absolute icon drift is measured from the outermost frame anchor
|
|
177
|
+
- Relative icon drift against the participant label anchor:
|
|
178
|
+
- `relative_dx`
|
|
179
|
+
- `relative_dy`
|
|
180
|
+
- If there is no participant label on one side, use the participant box center as the anchor
|
|
181
|
+
- Report presence mismatch if one renderer has an icon and the other does not
|
|
182
|
+
- Diff confirmation is taken from `#diff-panel canvas`, scoped to the union of the HTML and SVG icon boxes
|
|
183
|
+
|
|
184
|
+
## Residual Scope Attribution
|
|
185
|
+
|
|
186
|
+
Residual scope extraction:
|
|
187
|
+
|
|
188
|
+
- Build connected clusters from the live `#diff-panel canvas` colors:
|
|
189
|
+
- red = `html-only`
|
|
190
|
+
- blue = `svg-only`
|
|
191
|
+
- Ignore green `match` and magenta `color diff` pixels for positional scoping
|
|
192
|
+
- Each cluster reports:
|
|
193
|
+
- `size`
|
|
194
|
+
- `bbox`
|
|
195
|
+
- `centroid`
|
|
196
|
+
- These panel-derived clusters are the source of truth for residual hotspots.
|
|
197
|
+
|
|
198
|
+
Residual scope candidates:
|
|
199
|
+
|
|
200
|
+
- HTML side:
|
|
201
|
+
- labels
|
|
202
|
+
- numbers
|
|
203
|
+
- arrows
|
|
204
|
+
- participant stereotypes
|
|
205
|
+
- participant labels
|
|
206
|
+
- participant icons
|
|
207
|
+
- participant boxes
|
|
208
|
+
- diagram root fallback
|
|
209
|
+
- SVG side:
|
|
210
|
+
- labels
|
|
211
|
+
- numbers
|
|
212
|
+
- arrows
|
|
213
|
+
- participant stereotypes
|
|
214
|
+
- participant labels
|
|
215
|
+
- participant icons
|
|
216
|
+
- participant boxes
|
|
217
|
+
- `rect.frame-border-inner`, fallback `rect.frame-border` / `rect.frame-box`
|
|
218
|
+
- diagram root fallback
|
|
219
|
+
|
|
220
|
+
Residual scope attribution:
|
|
221
|
+
|
|
222
|
+
- Pick the closest candidate to the cluster centroid on each side
|
|
223
|
+
- Prefer targets that contain the centroid
|
|
224
|
+
- Prefer more specific categories over large containers:
|
|
225
|
+
- `participant-icon`
|
|
226
|
+
- `participant-stereotype`
|
|
227
|
+
- `label`, `number`, `participant-label`
|
|
228
|
+
- `arrow`
|
|
229
|
+
- `participant-box`
|
|
230
|
+
- `frame-border`
|
|
231
|
+
- `diagram-root`
|
|
232
|
+
- Use cluster/target overlap and centroid distance as tie-breakers
|
|
233
|
+
|
|
234
|
+
Residual scope output:
|
|
235
|
+
|
|
236
|
+
- `residual_scopes`: all attributed clusters
|
|
237
|
+
- `residual_scope_summary`: top 20 concise lines for terminal use
|
|
238
|
+
- `residual_scope_html_only_top`: top 10 `html-only` clusters
|
|
239
|
+
- `residual_scope_svg_only_top`: top 10 `svg-only` clusters
|
|
240
|
+
- When answering from the live panel, also report:
|
|
241
|
+
- the largest red clusters from `#diff-panel canvas`
|
|
242
|
+
- the largest blue clusters from `#diff-panel canvas`
|
|
243
|
+
- approximate diagram-space positions or bounding boxes
|
|
244
|
+
- attributed HTML and SVG targets for those clusters
|
|
245
|
+
- a short grouped summary of where the red and blue pixels are concentrated
|
|
246
|
+
|
|
247
|
+
Live panel validation:
|
|
248
|
+
|
|
249
|
+
- Source of truth for residual hotspots is `#diff-panel canvas`
|
|
250
|
+
- Confirm the hotspot by reading the panel's actual red and blue pixels at that area
|
|
251
|
+
- If the panel shows no red or blue pixels there, do not report that hotspot as a real residual diff
|
|
252
|
+
- If the panel shows non-zero red or blue totals, do not stop at the totals alone; locate the dominant clusters and report them
|
|
253
|
+
- Do not build or rely on a separate screenshot-to-screenshot diff for pixel comparison when `#diff-panel canvas` is available on the page
|
package/CLAUDE.md
CHANGED
|
@@ -7,7 +7,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
7
7
|
- **Start development server**: `bun dev` (runs on port 8080)
|
|
8
8
|
- **Build library**: `bun build` (builds library with vite.config.lib.ts)
|
|
9
9
|
- **Build site**: `bun build:site` (builds demo site with vite.config.ts)
|
|
10
|
-
- **Run tests**: `bun run test
|
|
10
|
+
- **Run tests**: `bun run test` (runs Vitest unit tests, excluding E2E). Do NOT use `bun test` — it picks up Playwright E2E files and reports false failures.
|
|
11
11
|
- **Run E2E tests**: `bun pw` (runs Playwright tests)
|
|
12
12
|
- **Run E2E tests (CI)**: `bun pw:ci` (runs with GitHub reporter for CI)
|
|
13
13
|
- **Open Playwright UI**: `bun pw:ui`
|
package/bun.lock
CHANGED
|
@@ -20,8 +20,6 @@
|
|
|
20
20
|
"marked": "^4.3.0",
|
|
21
21
|
"pako": "^2.1.0",
|
|
22
22
|
"pino": "^8.21.0",
|
|
23
|
-
"radash": "^12.1.1",
|
|
24
|
-
"ramda": "^0.28.0",
|
|
25
23
|
"react": "^19.2.3",
|
|
26
24
|
"react-dom": "^19.2.3",
|
|
27
25
|
"tailwind-merge": "^3.4.0",
|
|
@@ -30,6 +28,7 @@
|
|
|
30
28
|
"devDependencies": {
|
|
31
29
|
"@eslint/js": "^9.39.2",
|
|
32
30
|
"@happy-dom/global-registrator": "^18.0.1",
|
|
31
|
+
"@napi-rs/canvas": "^0.1.97",
|
|
33
32
|
"@playwright/test": "^1.57.0",
|
|
34
33
|
"@storybook/addon-docs": "^9.1.17",
|
|
35
34
|
"@storybook/addon-onboarding": "^9.1.17",
|
|
@@ -42,7 +41,6 @@
|
|
|
42
41
|
"@types/jsdom": "^21.1.7",
|
|
43
42
|
"@types/marked": "^4.3.2",
|
|
44
43
|
"@types/node": "^22.19.3",
|
|
45
|
-
"@types/ramda": "^0.28.25",
|
|
46
44
|
"@types/react": "^19.2.7",
|
|
47
45
|
"@types/react-dom": "^19.2.3",
|
|
48
46
|
"@vitejs/plugin-react": "^4.7.0",
|
|
@@ -297,6 +295,30 @@
|
|
|
297
295
|
|
|
298
296
|
"@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="],
|
|
299
297
|
|
|
298
|
+
"@napi-rs/canvas": ["@napi-rs/canvas@0.1.97", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.97", "@napi-rs/canvas-darwin-arm64": "0.1.97", "@napi-rs/canvas-darwin-x64": "0.1.97", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.97", "@napi-rs/canvas-linux-arm64-gnu": "0.1.97", "@napi-rs/canvas-linux-arm64-musl": "0.1.97", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-musl": "0.1.97", "@napi-rs/canvas-win32-arm64-msvc": "0.1.97", "@napi-rs/canvas-win32-x64-msvc": "0.1.97" } }, "sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ=="],
|
|
299
|
+
|
|
300
|
+
"@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.97", "", { "os": "android", "cpu": "arm64" }, "sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ=="],
|
|
301
|
+
|
|
302
|
+
"@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.97", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw=="],
|
|
303
|
+
|
|
304
|
+
"@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.97", "", { "os": "darwin", "cpu": "x64" }, "sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA=="],
|
|
305
|
+
|
|
306
|
+
"@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.97", "", { "os": "linux", "cpu": "arm" }, "sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw=="],
|
|
307
|
+
|
|
308
|
+
"@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w=="],
|
|
309
|
+
|
|
310
|
+
"@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ=="],
|
|
311
|
+
|
|
312
|
+
"@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.97", "", { "os": "linux", "cpu": "none" }, "sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA=="],
|
|
313
|
+
|
|
314
|
+
"@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg=="],
|
|
315
|
+
|
|
316
|
+
"@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA=="],
|
|
317
|
+
|
|
318
|
+
"@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.97", "", { "os": "win32", "cpu": "arm64" }, "sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A=="],
|
|
319
|
+
|
|
320
|
+
"@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.97", "", { "os": "win32", "cpu": "x64" }, "sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ=="],
|
|
321
|
+
|
|
300
322
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
|
301
323
|
|
|
302
324
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
|
@@ -497,8 +519,6 @@
|
|
|
497
519
|
|
|
498
520
|
"@types/node": ["@types/node@22.19.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA=="],
|
|
499
521
|
|
|
500
|
-
"@types/ramda": ["@types/ramda@0.28.25", "", { "dependencies": { "ts-toolbelt": "^6.15.1" } }, "sha512-HrQNqQAGcITpn9HAJFamDxm7iZeeXiP/95pN5OMbNniDjzCCeOHbBKNGmUy8NRi0fhYS+/cXeo91MFC+06gbow=="],
|
|
501
|
-
|
|
502
522
|
"@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="],
|
|
503
523
|
|
|
504
524
|
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
|
@@ -1077,10 +1097,6 @@
|
|
|
1077
1097
|
|
|
1078
1098
|
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
|
1079
1099
|
|
|
1080
|
-
"radash": ["radash@12.1.1", "", {}, "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA=="],
|
|
1081
|
-
|
|
1082
|
-
"ramda": ["ramda@0.28.0", "", {}, "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA=="],
|
|
1083
|
-
|
|
1084
1100
|
"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
|
|
1085
1101
|
|
|
1086
1102
|
"react-docgen": ["react-docgen@8.0.1", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.2", "@types/babel__core": "^7.20.5", "@types/babel__traverse": "^7.20.7", "@types/doctrine": "^0.0.9", "@types/resolve": "^1.20.2", "doctrine": "^3.0.0", "resolve": "^1.22.1", "strip-indent": "^4.0.0" } }, "sha512-kQKsqPLplY3Hx4jGnM3jpQcG3FQDt7ySz32uTHt3C9HAe45kNXG+3o16Eqn3Fw1GtMfHoN3b4J/z2e6cZJCmqQ=="],
|
|
@@ -1239,8 +1255,6 @@
|
|
|
1239
1255
|
|
|
1240
1256
|
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
|
1241
1257
|
|
|
1242
|
-
"ts-toolbelt": ["ts-toolbelt@6.15.5", "", {}, "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A=="],
|
|
1243
|
-
|
|
1244
1258
|
"tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="],
|
|
1245
1259
|
|
|
1246
1260
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|