@zenuml/core 3.47.8 → 3.48.0

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.
Files changed (204) hide show
  1. package/dist/cloud-icons-eHuugVSv.js.map +1 -0
  2. package/dist/zenuml.esm.mjs +2153 -2156
  3. package/dist/zenuml.esm.mjs.map +1 -0
  4. package/dist/zenuml.js +82 -82
  5. package/dist/zenuml.js.map +1 -0
  6. package/package.json +11 -1
  7. package/src/cli/zenuml.ts +1164 -0
  8. package/.agents/skills/babysit-pr/SKILL.md +0 -223
  9. package/.agents/skills/babysit-pr/agents/openai.yaml +0 -7
  10. package/.agents/skills/dia-scoring/SKILL.md +0 -139
  11. package/.agents/skills/dia-scoring/agents/openai.yaml +0 -7
  12. package/.agents/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  13. package/.agents/skills/land-pr/SKILL.md +0 -120
  14. package/.agents/skills/propagate-core-release/SKILL.md +0 -205
  15. package/.agents/skills/propagate-core-release/agents/openai.yaml +0 -7
  16. package/.agents/skills/propagate-core-release/references/downstreams.md +0 -42
  17. package/.agents/skills/ship-branch/SKILL.md +0 -105
  18. package/.agents/skills/submit-branch/SKILL.md +0 -76
  19. package/.agents/skills/validate-branch/SKILL.md +0 -72
  20. package/.claude/commands/README.md +0 -162
  21. package/.claude/commands/analyze.md +0 -101
  22. package/.claude/commands/clarify.md +0 -158
  23. package/.claude/commands/code-review.md +0 -322
  24. package/.claude/commands/constitution.md +0 -73
  25. package/.claude/commands/create-docs.md +0 -309
  26. package/.claude/commands/full-context.md +0 -121
  27. package/.claude/commands/gemini-consult.md +0 -164
  28. package/.claude/commands/handoff.md +0 -146
  29. package/.claude/commands/implement.md +0 -56
  30. package/.claude/commands/plan.md +0 -43
  31. package/.claude/commands/refactor.md +0 -188
  32. package/.claude/commands/specify.md +0 -21
  33. package/.claude/commands/tasks.md +0 -62
  34. package/.claude/commands/update-docs.md +0 -314
  35. package/.claude/hooks/README.md +0 -270
  36. package/.claude/hooks/config/sensitive-patterns.json +0 -86
  37. package/.claude/hooks/gemini-context-injector.sh +0 -129
  38. package/.claude/hooks/mcp-security-scan.sh +0 -147
  39. package/.claude/hooks/notify.sh +0 -103
  40. package/.claude/hooks/setup/hook-setup.md +0 -96
  41. package/.claude/hooks/setup/settings.json.template +0 -63
  42. package/.claude/hooks/sounds/complete.wav +0 -0
  43. package/.claude/hooks/sounds/input-needed.wav +0 -0
  44. package/.claude/hooks/subagent-context-injector.sh +0 -65
  45. package/.claude/skills/babysit-pr/SKILL.md +0 -223
  46. package/.claude/skills/babysit-pr/agents/openai.yaml +0 -7
  47. package/.claude/skills/dia-scoring/SKILL.md +0 -139
  48. package/.claude/skills/dia-scoring/agents/openai.yaml +0 -7
  49. package/.claude/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  50. package/.claude/skills/emoji-eval/SKILL.md +0 -187
  51. package/.claude/skills/land-pr/SKILL.md +0 -120
  52. package/.claude/skills/propagate-core-release/SKILL.md +0 -205
  53. package/.claude/skills/propagate-core-release/agents/openai.yaml +0 -7
  54. package/.claude/skills/propagate-core-release/references/downstreams.md +0 -42
  55. package/.claude/skills/ship-branch/SKILL.md +0 -105
  56. package/.claude/skills/submit-branch/SKILL.md +0 -76
  57. package/.claude/skills/validate-branch/SKILL.md +0 -72
  58. package/.claude/skills/zenuml-ux-research/SKILL.md +0 -183
  59. package/.claude/skills/zenuml-ux-research/references/assertion-catalog.md +0 -261
  60. package/.claude/skills/zenuml-ux-research/references/best-practices-overview.md +0 -56
  61. package/.claude/skills/zenuml-ux-research/references/report-template.md +0 -89
  62. package/.claude/skills/zenuml-ux-research/references/scenarios/edit-message-label.md +0 -37
  63. package/.claude/skills/zenuml-ux-research/references/scenarios/insert-message.md +0 -36
  64. package/.claude/skills/zenuml-ux-research/references/scenarios/insert-participant.md +0 -31
  65. package/.claude/skills/zenuml-ux-research/references/scenarios/rename-participant.md +0 -33
  66. package/.claude/skills/zenuml-ux-research/references/scenarios/undo-insert.md +0 -35
  67. package/.devcontainer/devcontainer.json +0 -21
  68. package/.dockerignore +0 -19
  69. package/.eslintrc.js +0 -39
  70. package/.git-blame-ignore-revs +0 -6
  71. package/.kiro/hooks/README.md +0 -38
  72. package/.kiro/hooks/session-sound-notification.js +0 -44
  73. package/.kiro/hooks/session-sound-notification.json +0 -23
  74. package/.mcp.json.example +0 -17
  75. package/.nvmrc +0 -1
  76. package/.prettierignore +0 -4
  77. package/.prettierrc +0 -1
  78. package/.specify/memory/constitution.md +0 -33
  79. package/.specify/scripts/bash/check-prerequisites.sh +0 -166
  80. package/.specify/scripts/bash/common.sh +0 -113
  81. package/.specify/scripts/bash/create-new-feature.sh +0 -97
  82. package/.specify/scripts/bash/setup-plan.sh +0 -60
  83. package/.specify/scripts/bash/update-agent-context.sh +0 -728
  84. package/.specify/templates/agent-file-template.md +0 -23
  85. package/.specify/templates/plan-template.md +0 -219
  86. package/.specify/templates/spec-template.md +0 -116
  87. package/.specify/templates/tasks-template.md +0 -127
  88. package/.storybook/main.ts +0 -25
  89. package/.storybook/preview.ts +0 -29
  90. package/.watchmanconfig +0 -3
  91. package/AGENTS.md +0 -26
  92. package/CLAUDE.md +0 -124
  93. package/DEPLOYMENT.md +0 -62
  94. package/Dockerfile +0 -36
  95. package/IMPLEMENTATION_PLAN.md +0 -163
  96. package/Integration/vanilla-js/index.html +0 -42
  97. package/MCP-ASSISTANT-RULES.md +0 -85
  98. package/README_CN.md +0 -15
  99. package/TUTORIAL.md +0 -116
  100. package/antlr/antlr-4.11.1-complete.jar +0 -0
  101. package/bun.lock +0 -1544
  102. package/bunfig.toml +0 -52
  103. package/docs/UNICODE_SUPPORT.md +0 -179
  104. package/docs/ai-context/deployment-infrastructure.md +0 -21
  105. package/docs/ai-context/docs-overview.md +0 -89
  106. package/docs/ai-context/handoff.md +0 -174
  107. package/docs/ai-context/project-structure.md +0 -160
  108. package/docs/ai-context/system-integration.md +0 -21
  109. package/docs/asciidoc/contributor.adoc +0 -54
  110. package/docs/asciidoc/create-my-own-theme.adoc +0 -149
  111. package/docs/asciidoc/images/creation-component.png +0 -0
  112. package/docs/asciidoc/images/creation-rtl.png +0 -0
  113. package/docs/asciidoc/images/message-arrow-rtl.png +0 -0
  114. package/docs/asciidoc/images/occurrence.png +0 -0
  115. package/docs/asciidoc/images/return-message-conflict.png +0 -0
  116. package/docs/asciidoc/images/shift-up-half-the-height.png +0 -0
  117. package/docs/asciidoc/images/three-layer-info-arch.png +0 -0
  118. package/docs/asciidoc/images/vertical-alignment.svg +0 -1
  119. package/docs/asciidoc/images/vertically-aligning.png +0 -0
  120. package/docs/asciidoc/index.adoc +0 -277
  121. package/docs/asciidoc/theme-debug-web-app.png +0 -0
  122. package/docs/asciidoc/tutorial.adoc +0 -22
  123. package/docs/asciidoc/user-css.png +0 -0
  124. package/docs/async-vs-sync-parser-rules.md +0 -81
  125. package/docs/divider-parser-allow-spaces.md +0 -38
  126. package/docs/highlighting-messages.md +0 -52
  127. package/docs/images/editor-sample.png +0 -0
  128. package/docs/inherited-vs-provided-from.md +0 -64
  129. package/docs/parser/Assignment.md +0 -8
  130. package/docs/parser/PARSER_IMPROVEMENTS_CC.md +0 -425
  131. package/docs/parser/grammar_review_gemini.md +0 -116
  132. package/docs/participants-function.md +0 -25
  133. package/docs/responsive-participant-margin.md +0 -52
  134. package/docs/starter.md +0 -9
  135. package/docs/superpowers/plans/2026-03-27-e2e-test-reorg.md +0 -698
  136. package/docs/superpowers/plans/2026-03-30-emoji-support.md +0 -1220
  137. package/docs/superpowers/plans/2026-03-30-self-correcting-scoring.md +0 -206
  138. package/docs/superpowers/plans/2026-04-15-keyboard-editing-on-diagram.md +0 -1992
  139. package/docs/superpowers/plans/2026-04-15-zenuml-ux-research-skill.md +0 -1452
  140. package/docs/ux-research/.gitkeep +0 -0
  141. package/docs/ux-research/2026-04-15-rename-participant.md +0 -156
  142. package/docs/ux-research/2026-04-18-insert-participant.md +0 -151
  143. package/docs/width-translate-and-offsets.md +0 -62
  144. package/docs/xss.md +0 -59
  145. package/e2e/data/compare-cases.js +0 -1090
  146. package/e2e/data/diff-algorithm.js +0 -199
  147. package/e2e/fixtures/create-message.html +0 -26
  148. package/e2e/fixtures/editable-label.html +0 -35
  149. package/e2e/fixtures/editable-span.html +0 -122
  150. package/e2e/fixtures/empty-diagram.html +0 -23
  151. package/e2e/fixtures/fixture.html +0 -31
  152. package/e2e/fixtures/insert-participant.html +0 -23
  153. package/e2e/fixtures/reorder-cross-fragment.html +0 -31
  154. package/e2e/fixtures/reorder-fragment.html +0 -29
  155. package/e2e/fixtures/reorder-message.html +0 -27
  156. package/e2e/fixtures/svg-test.html +0 -21
  157. package/e2e/fixtures/type-switch.html +0 -29
  158. package/e2e/tools/canonical-history.html +0 -908
  159. package/e2e/tools/compare-case.html +0 -371
  160. package/e2e/tools/compare.html +0 -35
  161. package/e2e/tools/native-diff-ext/background.js +0 -60
  162. package/e2e/tools/native-diff-ext/bridge.js +0 -26
  163. package/e2e/tools/native-diff-ext/content.js +0 -194
  164. package/e2e/tools/svg-preview.html +0 -56
  165. package/embed.html +0 -193
  166. package/eslint.config.mjs +0 -35
  167. package/firebase-debug.log +0 -108
  168. package/iframe-container-demo/diagram.html +0 -124
  169. package/iframe-container-demo/host.html +0 -817
  170. package/index.html +0 -771
  171. package/mermaid-zenuml-async-spa-auth.png +0 -0
  172. package/mermaid-zenuml-async-spa-auth.snapshot.md +0 -96
  173. package/newsletter/unicode-support-announcement.md +0 -134
  174. package/playground/creation.html +0 -53
  175. package/playground/message.html +0 -63
  176. package/playwright.config.ts +0 -40
  177. package/renderer.html +0 -366
  178. package/scripts/analyze-compare-case/collect-data.mjs +0 -1134
  179. package/scripts/analyze-compare-case/config.mjs +0 -102
  180. package/scripts/analyze-compare-case/geometry.mjs +0 -101
  181. package/scripts/analyze-compare-case/native-diff.mjs +0 -224
  182. package/scripts/analyze-compare-case/output.mjs +0 -74
  183. package/scripts/analyze-compare-case/panel-diff.mjs +0 -114
  184. package/scripts/analyze-compare-case/report.mjs +0 -162
  185. package/scripts/analyze-compare-case/residual-scopes.mjs +0 -347
  186. package/scripts/analyze-compare-case/scoring.mjs +0 -829
  187. package/scripts/analyze-compare-case.mjs +0 -149
  188. package/scripts/bump-version.js +0 -117
  189. package/scripts/snapshot-dual.js +0 -173
  190. package/scripts/update-snapshots.js +0 -70
  191. package/skills/dia-scoring/SKILL.md +0 -129
  192. package/skills/dia-scoring/agents/openai.yaml +0 -7
  193. package/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  194. package/tailwind.config.js +0 -126
  195. package/test-compression.html +0 -274
  196. package/test-mermaid-zenuml.html +0 -57
  197. package/test-setup.ts +0 -124
  198. package/test-url-params.html +0 -192
  199. package/tsconfig.app.json +0 -31
  200. package/tsconfig.node.json +0 -24
  201. package/tsconfig.test.json +0 -9
  202. package/vite.config.lib.ts +0 -93
  203. package/vite.config.ts +0 -84
  204. package/wrangler.toml +0 -18
@@ -1,829 +0,0 @@
1
- /*
2
- * What this file does:
3
- * Compares extracted HTML and SVG geometry and converts it into scored sections.
4
- *
5
- * High-level flow:
6
- * - Pairs equivalent items across HTML and SVG by semantic keys.
7
- * - Scores per-letter drift, arrow geometry, participant labels, icons, and boxes.
8
- * - Uses the analyzer's local diff only as supporting evidence when deciding
9
- * whether a measured offset is strong enough to report.
10
- * - Returns structured sections ready for report assembly.
11
- *
12
- * This module answers "how far apart are the matched elements?" but does not
13
- * decide how to print results or where residual hotspots belong.
14
- *
15
- * Example input:
16
- * Extracted HTML/SVG geometry plus a local diff image.
17
- *
18
- * Example output:
19
- * `{ labels, numbers, arrows, participantLabels, participantIcons, participantBoxes }`
20
- * where each section contains status, deltas, evidence, and normalized boxes.
21
- */
22
- import { analyzeDiffSlot } from "./native-diff.mjs";
23
- import {
24
- iou,
25
- keyForLabel,
26
- normalizeOffset,
27
- rectBottom,
28
- rectCenter,
29
- rectRight,
30
- round,
31
- } from "./geometry.mjs";
32
-
33
- function enrichOrdering(labels) {
34
- const byKind = new Map();
35
- const byText = new Map();
36
- const ordered = [...labels].sort((a, b) => (a.box.y - b.box.y) || (a.box.x - b.box.x));
37
-
38
- for (const label of ordered) {
39
- const kindCount = byKind.get(label.kind) || 0;
40
- byKind.set(label.kind, kindCount + 1);
41
- label.yOrder = kindCount;
42
-
43
- const textKey = `${label.kind}\u0000${label.pairText ?? label.text}`;
44
- const textCount = byText.get(textKey) || 0;
45
- byText.set(textKey, textCount + 1);
46
- label.textOrder = textCount;
47
- }
48
-
49
- return ordered;
50
- }
51
-
52
- function pairLabels(htmlLabels, svgLabels) {
53
- const htmlOrdered = enrichOrdering(htmlLabels);
54
- const svgOrdered = enrichOrdering(svgLabels);
55
-
56
- const htmlMap = new Map(htmlOrdered.map((label) => [keyForLabel(label), label]));
57
- const svgMap = new Map(svgOrdered.map((label) => [keyForLabel(label), label]));
58
- const allKeys = Array.from(new Set([...htmlMap.keys(), ...svgMap.keys()]));
59
-
60
- return allKeys
61
- .map((key) => ({ key, html: htmlMap.get(key) || null, svg: svgMap.get(key) || null }))
62
- .sort((a, b) => {
63
- const ay = a.html?.box.y ?? a.svg?.box.y ?? 0;
64
- const by = b.html?.box.y ?? b.svg?.box.y ?? 0;
65
- return ay - by;
66
- });
67
- }
68
-
69
- function scoreLetter(htmlLetter, svgLetter, diffImage) {
70
- const directDx = svgLetter.box.x - htmlLetter.box.x;
71
- const directDy = svgLetter.box.y - htmlLetter.box.y;
72
- const slot = {
73
- x: Math.min(htmlLetter.box.x, svgLetter.box.x) - 2,
74
- y: Math.min(htmlLetter.box.y, svgLetter.box.y) - 2,
75
- w: Math.max(rectRight(htmlLetter.box), rectRight(svgLetter.box)) - Math.min(htmlLetter.box.x, svgLetter.box.x) + 4,
76
- h: Math.max(rectBottom(htmlLetter.box), rectBottom(svgLetter.box)) - Math.min(htmlLetter.box.y, svgLetter.box.y) + 4,
77
- };
78
- const diff = analyzeDiffSlot(diffImage, slot);
79
- const centroidDx = diff.redCentroid && diff.blueCentroid ? diff.blueCentroid.x - diff.redCentroid.x : null;
80
- const centroidDy = diff.redCentroid && diff.blueCentroid ? diff.blueCentroid.y - diff.redCentroid.y : null;
81
- const nearZero = Math.abs(directDx) < 0.75 && Math.abs(directDy) < 0.75;
82
- const enoughDiffPixels = diff.redCount >= 6 && diff.blueCount >= 6;
83
- const xConsistent = centroidDx === null || Math.abs(directDx) < 0.75 || Math.sign(centroidDx) === Math.sign(directDx);
84
- const yConsistent = centroidDy === null || Math.abs(directDy) < 0.75 || Math.sign(centroidDy) === Math.sign(directDy);
85
- const overlap = iou(htmlLetter.box, svgLetter.box);
86
-
87
- let status = "ambiguous";
88
- if (nearZero) {
89
- status = overlap >= 0.35 ? "ok" : "ambiguous";
90
- } else if (enoughDiffPixels && xConsistent && yConsistent) {
91
- status = "ok";
92
- }
93
-
94
- const diffConfidence = nearZero
95
- ? overlap
96
- : enoughDiffPixels
97
- ? 0.5 + Math.min(0.5, ((diff.redCount + diff.blueCount) / 80))
98
- : 0.15;
99
- const confidence = round(Math.min(1, overlap * 0.45 + diffConfidence * 0.55), 3);
100
-
101
- return {
102
- index: htmlLetter.index,
103
- grapheme: htmlLetter.grapheme,
104
- status,
105
- dx: normalizeOffset(directDx),
106
- dy: normalizeOffset(directDy),
107
- confidence,
108
- html_box: {
109
- x: round(htmlLetter.box.x),
110
- y: round(htmlLetter.box.y),
111
- w: round(htmlLetter.box.w),
112
- h: round(htmlLetter.box.h),
113
- },
114
- svg_box: {
115
- x: round(svgLetter.box.x),
116
- y: round(svgLetter.box.y),
117
- w: round(svgLetter.box.w),
118
- h: round(svgLetter.box.h),
119
- },
120
- evidence: {
121
- direct_dx: normalizeOffset(directDx),
122
- direct_dy: normalizeOffset(directDy),
123
- overlap: round(overlap, 3),
124
- diff_red: diff.redCount,
125
- diff_blue: diff.blueCount,
126
- diff_centroid_dx: centroidDx === null ? null : round(centroidDx),
127
- diff_centroid_dy: centroidDy === null ? null : round(centroidDy),
128
- },
129
- };
130
- }
131
-
132
- function buildSection(htmlItems, svgItems, diffImage) {
133
- const pairs = pairLabels(htmlItems, svgItems);
134
- const section = [];
135
-
136
- for (const pair of pairs) {
137
- const base = pair.html || pair.svg;
138
- const key = {
139
- kind: base?.kind ?? "message",
140
- text: base?.text ?? "",
141
- y_order: base?.yOrder ?? 0,
142
- };
143
-
144
- if (!pair.html || !pair.svg) {
145
- section.push({
146
- key,
147
- status: "ambiguous",
148
- html_text: pair.html?.text ?? null,
149
- svg_text: pair.svg?.text ?? null,
150
- owner_text: pair.html?.ownerText ?? pair.svg?.ownerText ?? null,
151
- html_box: pair.html ? pair.html.box : null,
152
- svg_box: pair.svg ? pair.svg.box : null,
153
- font: {
154
- html: pair.html?.font ?? null,
155
- svg: pair.svg?.font ?? null,
156
- },
157
- letters: [],
158
- reason: "item missing on one side",
159
- });
160
- continue;
161
- }
162
-
163
- const letterCount = Math.max(pair.html.letters.length, pair.svg.letters.length);
164
- const letters = [];
165
- for (let index = 0; index < letterCount; index++) {
166
- const htmlLetter = pair.html.letters[index];
167
- const svgLetter = pair.svg.letters[index];
168
- if (!htmlLetter || !svgLetter || htmlLetter.grapheme !== svgLetter.grapheme) {
169
- letters.push({
170
- index,
171
- grapheme: htmlLetter?.grapheme ?? svgLetter?.grapheme ?? "",
172
- status: "ambiguous",
173
- dx: null,
174
- dy: null,
175
- confidence: 0,
176
- html_box: htmlLetter ? htmlLetter.box : null,
177
- svg_box: svgLetter ? svgLetter.box : null,
178
- evidence: { reason: "letter mismatch or missing" },
179
- });
180
- continue;
181
- }
182
- letters.push(scoreLetter(htmlLetter, svgLetter, diffImage));
183
- }
184
-
185
- const okCount = letters.filter((letter) => letter.status === "ok").length;
186
- const status = okCount === letters.length ? "ok" : okCount > 0 ? "mixed" : "ambiguous";
187
- section.push({
188
- key,
189
- status,
190
- html_text: pair.html.text,
191
- svg_text: pair.svg.text,
192
- owner_text: pair.html.ownerText ?? pair.svg.ownerText ?? null,
193
- html_box: {
194
- x: round(pair.html.box.x),
195
- y: round(pair.html.box.y),
196
- w: round(pair.html.box.w),
197
- h: round(pair.html.box.h),
198
- },
199
- svg_box: {
200
- x: round(pair.svg.box.x),
201
- y: round(pair.svg.box.y),
202
- w: round(pair.svg.box.w),
203
- h: round(pair.svg.box.h),
204
- },
205
- font: {
206
- html: pair.html.font,
207
- svg: pair.svg.font,
208
- },
209
- letters,
210
- });
211
- }
212
-
213
- return section;
214
- }
215
-
216
- function scoreArrowGeometry(htmlArrow, svgArrow, diffImage, kind = htmlArrow.kind ?? svgArrow.kind ?? "message") {
217
- const leftDx = svgArrow.left_x - htmlArrow.left_x;
218
- const rightDx = svgArrow.right_x - htmlArrow.right_x;
219
- const widthDx = svgArrow.width - htmlArrow.width;
220
- const topDy = svgArrow.box.y - htmlArrow.box.y;
221
- const bottomDy = rectBottom(svgArrow.box) - rectBottom(htmlArrow.box);
222
- const heightDy = svgArrow.box.h - htmlArrow.box.h;
223
- const slot = {
224
- x: Math.min(htmlArrow.box.x, svgArrow.box.x) - 2,
225
- y: Math.min(htmlArrow.box.y, svgArrow.box.y) - 2,
226
- w: Math.max(rectRight(htmlArrow.box), rectRight(svgArrow.box)) - Math.min(htmlArrow.box.x, svgArrow.box.x) + 4,
227
- h: Math.max(rectBottom(htmlArrow.box), rectBottom(svgArrow.box)) - Math.min(htmlArrow.box.y, svgArrow.box.y) + 4,
228
- };
229
- const diff = analyzeDiffSlot(diffImage, slot);
230
- const centroidDx = diff.redCentroid && diff.blueCentroid ? diff.blueCentroid.x - diff.redCentroid.x : null;
231
- const centroidDy = diff.redCentroid && diff.blueCentroid ? diff.blueCentroid.y - diff.redCentroid.y : null;
232
- const nearZero = Math.abs(leftDx) < 0.75 && Math.abs(rightDx) < 0.75;
233
- const nearZeroSelf = nearZero && Math.abs(topDy) < 0.75 && Math.abs(bottomDy) < 0.75 && Math.abs(heightDy) < 0.75;
234
- const enoughDiffPixels = diff.redCount >= 6 && diff.blueCount >= 6;
235
- const dominantDx = Math.abs(rightDx) >= Math.abs(leftDx) ? rightDx : leftDx;
236
- const dominantDy = [topDy, bottomDy, heightDy].reduce((dominant, value) => (
237
- Math.abs(value) > Math.abs(dominant) ? value : dominant
238
- ), 0);
239
- const xConsistent = centroidDx === null || Math.abs(dominantDx) < 0.75 || Math.sign(centroidDx) === Math.sign(dominantDx);
240
- const yConsistent = centroidDy === null || Math.abs(dominantDy) < 0.75 || Math.sign(centroidDy) === Math.sign(dominantDy);
241
- const overlap = iou(htmlArrow.box, svgArrow.box);
242
- const status = kind === "self"
243
- ? (nearZeroSelf || (enoughDiffPixels && xConsistent && yConsistent) || Math.abs(dominantDx) >= 0.75 || Math.abs(dominantDy) >= 0.75 ? "ok" : "ambiguous")
244
- : (nearZero || (enoughDiffPixels && xConsistent) || Math.abs(dominantDx) >= 0.75 ? "ok" : "ambiguous");
245
- const confidence = round(Math.min(1, overlap * 0.45 + (enoughDiffPixels ? 0.55 : 0.2)), 3);
246
-
247
- return {
248
- status,
249
- left_dx: status === "ok" ? normalizeOffset(leftDx) : null,
250
- right_dx: status === "ok" ? normalizeOffset(rightDx) : null,
251
- width_dx: status === "ok" ? normalizeOffset(widthDx) : null,
252
- top_dy: kind === "self" && status === "ok" ? normalizeOffset(topDy) : null,
253
- bottom_dy: kind === "self" && status === "ok" ? normalizeOffset(bottomDy) : null,
254
- height_dy: kind === "self" && status === "ok" ? normalizeOffset(heightDy) : null,
255
- confidence,
256
- html_box: {
257
- x: round(htmlArrow.box.x),
258
- y: round(htmlArrow.box.y),
259
- w: round(htmlArrow.box.w),
260
- h: round(htmlArrow.box.h),
261
- },
262
- svg_box: {
263
- x: round(svgArrow.box.x),
264
- y: round(svgArrow.box.y),
265
- w: round(svgArrow.box.w),
266
- h: round(svgArrow.box.h),
267
- },
268
- evidence: {
269
- left_dx: normalizeOffset(leftDx),
270
- right_dx: normalizeOffset(rightDx),
271
- width_dx: normalizeOffset(widthDx),
272
- top_dy: normalizeOffset(topDy),
273
- bottom_dy: normalizeOffset(bottomDy),
274
- height_dy: normalizeOffset(heightDy),
275
- overlap: round(overlap, 3),
276
- diff_red: diff.redCount,
277
- diff_blue: diff.blueCount,
278
- diff_centroid_dx: centroidDx === null ? null : round(centroidDx),
279
- diff_centroid_dy: centroidDy === null ? null : round(centroidDy),
280
- },
281
- };
282
- }
283
-
284
- function buildArrowSection(htmlItems, svgItems, diffImage) {
285
- const htmlOrdered = enrichOrdering(htmlItems);
286
- const svgOrdered = enrichOrdering(svgItems);
287
- const htmlMap = new Map(htmlOrdered.map((item) => [keyForLabel(item), item]));
288
- const svgMap = new Map(svgOrdered.map((item) => [keyForLabel(item), item]));
289
- const allKeys = Array.from(new Set([...htmlMap.keys(), ...svgMap.keys()]));
290
- const arrows = [];
291
-
292
- for (const key of allKeys) {
293
- const html = htmlMap.get(key) || null;
294
- const svg = svgMap.get(key) || null;
295
- const base = html || svg;
296
- const arrow = {
297
- key: {
298
- kind: base?.kind ?? "message",
299
- text: base?.text ?? "",
300
- y_order: base?.yOrder ?? 0,
301
- },
302
- status: "ambiguous",
303
- };
304
-
305
- if (!html || !svg) {
306
- arrow.reason = "arrow missing on one side";
307
- arrows.push(arrow);
308
- continue;
309
- }
310
-
311
- const scored = scoreArrowGeometry(html, svg, diffImage, base?.kind);
312
- arrows.push({
313
- ...arrow,
314
- ...scored,
315
- label_text: base?.labelText ?? null,
316
- });
317
- }
318
-
319
- return arrows;
320
- }
321
-
322
- function participantsWithIcons(htmlParticipants, svgParticipants) {
323
- const htmlMap = new Map(htmlParticipants.map((participant) => [participant.name, participant]));
324
- const svgMap = new Map(svgParticipants.map((participant) => [participant.name, participant]));
325
- const byName = new Map();
326
- for (const participant of [...htmlParticipants, ...svgParticipants]) {
327
- if (!participant.name || !participant.iconBox) {
328
- continue;
329
- }
330
- const html = htmlMap.get(participant.name) || null;
331
- const svg = svgMap.get(participant.name) || null;
332
- const hasLabel = Boolean(html?.labelText || svg?.labelText);
333
- if (!hasLabel && participant.name === "_STARTER_") {
334
- continue;
335
- }
336
- byName.set(participant.name, true);
337
- }
338
- return Array.from(byName.keys()).sort((a, b) => a.localeCompare(b));
339
- }
340
-
341
- function buildParticipantLabelItems(participants, iconNames) {
342
- const include = new Set(iconNames);
343
- return participants
344
- .filter((participant) => include.has(participant.name) && participant.labelText && participant.labelBox)
345
- .map((participant) => ({
346
- side: participant.side,
347
- kind: "participant",
348
- text: participant.labelText,
349
- pairText: participant.name,
350
- ownerText: participant.name,
351
- box: participant.labelBox,
352
- font: participant.labelFont,
353
- letters: participant.labelLetters,
354
- }));
355
- }
356
-
357
- function buildParticipantStereotypeItems(participants) {
358
- return participants
359
- .filter((participant) => participant.stereotypeText && participant.stereotypeBox)
360
- .map((participant) => ({
361
- side: participant.side,
362
- kind: "participant-stereotype",
363
- text: participant.stereotypeText,
364
- pairText: participant.name,
365
- ownerText: participant.name,
366
- box: participant.stereotypeBox,
367
- font: participant.stereotypeFont,
368
- letters: participant.stereotypeLetters,
369
- }));
370
- }
371
-
372
- function scoreParticipantIcon(htmlParticipant, svgParticipant, diffImage) {
373
- const iconPresentHtml = Boolean(htmlParticipant?.iconBox);
374
- const iconPresentSvg = Boolean(svgParticipant?.iconBox);
375
- const base = htmlParticipant || svgParticipant;
376
- const participant = {
377
- name: base?.name ?? "",
378
- label_text: htmlParticipant?.labelText || svgParticipant?.labelText || null,
379
- emoji: htmlParticipant?.emojiText || svgParticipant?.emojiText || null,
380
- presence: {
381
- html: iconPresentHtml,
382
- svg: iconPresentSvg,
383
- },
384
- anchor_kind: htmlParticipant?.anchorKind || svgParticipant?.anchorKind || null,
385
- status: "ambiguous",
386
- };
387
-
388
- if (!iconPresentHtml || !iconPresentSvg) {
389
- participant.reason = "icon missing on one side";
390
- return participant;
391
- }
392
-
393
- const htmlIconCenter = rectCenter(htmlParticipant.iconBox);
394
- const svgIconCenter = rectCenter(svgParticipant.iconBox);
395
- const htmlAnchorCenter = rectCenter(htmlParticipant.anchorBox);
396
- const svgAnchorCenter = rectCenter(svgParticipant.anchorBox);
397
- const directDx = svgIconCenter.x - htmlIconCenter.x;
398
- const directDy = svgIconCenter.y - htmlIconCenter.y;
399
- const relativeDx = (svgIconCenter.x - svgAnchorCenter.x) - (htmlIconCenter.x - htmlAnchorCenter.x);
400
- const relativeDy = (svgIconCenter.y - svgAnchorCenter.y) - (htmlIconCenter.y - htmlAnchorCenter.y);
401
- const slot = {
402
- x: Math.min(htmlParticipant.iconBox.x, svgParticipant.iconBox.x) - 2,
403
- y: Math.min(htmlParticipant.iconBox.y, svgParticipant.iconBox.y) - 2,
404
- w: Math.max(rectRight(htmlParticipant.iconBox), rectRight(svgParticipant.iconBox)) - Math.min(htmlParticipant.iconBox.x, svgParticipant.iconBox.x) + 4,
405
- h: Math.max(rectBottom(htmlParticipant.iconBox), rectBottom(svgParticipant.iconBox)) - Math.min(htmlParticipant.iconBox.y, svgParticipant.iconBox.y) + 4,
406
- };
407
- const diff = analyzeDiffSlot(diffImage, slot);
408
- const centroidDx = diff.redCentroid && diff.blueCentroid ? diff.blueCentroid.x - diff.redCentroid.x : null;
409
- const centroidDy = diff.redCentroid && diff.blueCentroid ? diff.blueCentroid.y - diff.redCentroid.y : null;
410
- const nearZero = Math.abs(directDx) < 0.75
411
- && Math.abs(directDy) < 0.75
412
- && Math.abs(relativeDx) < 0.75
413
- && Math.abs(relativeDy) < 0.75;
414
- const enoughDiffPixels = diff.redCount >= 6 && diff.blueCount >= 6;
415
- const xConsistent = centroidDx === null || Math.abs(directDx) < 0.75 || Math.sign(centroidDx) === Math.sign(directDx);
416
- const yConsistent = centroidDy === null || Math.abs(directDy) < 0.75 || Math.sign(centroidDy) === Math.sign(directDy);
417
- const overlap = iou(htmlParticipant.iconBox, svgParticipant.iconBox);
418
- const status = nearZero
419
- ? (overlap >= 0.15 ? "ok" : "ambiguous")
420
- : ((enoughDiffPixels && xConsistent && yConsistent)
421
- || Math.abs(directDx) >= 0.75
422
- || Math.abs(directDy) >= 0.75
423
- || Math.abs(relativeDx) >= 0.75
424
- || Math.abs(relativeDy) >= 0.75
425
- ? "ok"
426
- : "ambiguous");
427
- const confidence = round(Math.min(1, overlap * 0.45 + (enoughDiffPixels ? 0.55 : 0.2)), 3);
428
-
429
- return {
430
- ...participant,
431
- status,
432
- icon_dx: status === "ok" ? normalizeOffset(directDx) : null,
433
- icon_dy: status === "ok" ? normalizeOffset(directDy) : null,
434
- relative_dx: status === "ok" ? normalizeOffset(relativeDx) : null,
435
- relative_dy: status === "ok" ? normalizeOffset(relativeDy) : null,
436
- confidence,
437
- html_icon_box: {
438
- x: round(htmlParticipant.iconBox.x),
439
- y: round(htmlParticipant.iconBox.y),
440
- w: round(htmlParticipant.iconBox.w),
441
- h: round(htmlParticipant.iconBox.h),
442
- },
443
- svg_icon_box: {
444
- x: round(svgParticipant.iconBox.x),
445
- y: round(svgParticipant.iconBox.y),
446
- w: round(svgParticipant.iconBox.w),
447
- h: round(svgParticipant.iconBox.h),
448
- },
449
- html_anchor_box: {
450
- x: round(htmlParticipant.anchorBox.x),
451
- y: round(htmlParticipant.anchorBox.y),
452
- w: round(htmlParticipant.anchorBox.w),
453
- h: round(htmlParticipant.anchorBox.h),
454
- },
455
- svg_anchor_box: {
456
- x: round(svgParticipant.anchorBox.x),
457
- y: round(svgParticipant.anchorBox.y),
458
- w: round(svgParticipant.anchorBox.w),
459
- h: round(svgParticipant.anchorBox.h),
460
- },
461
- evidence: {
462
- icon_dx: normalizeOffset(directDx),
463
- icon_dy: normalizeOffset(directDy),
464
- relative_dx: normalizeOffset(relativeDx),
465
- relative_dy: normalizeOffset(relativeDy),
466
- overlap: round(overlap, 3),
467
- diff_red: diff.redCount,
468
- diff_blue: diff.blueCount,
469
- diff_centroid_dx: centroidDx === null ? null : round(centroidDx),
470
- diff_centroid_dy: centroidDy === null ? null : round(centroidDy),
471
- },
472
- };
473
- }
474
-
475
- function buildParticipantIconSection(htmlParticipants, svgParticipants, diffImage) {
476
- const names = participantsWithIcons(htmlParticipants, svgParticipants);
477
- const htmlMap = new Map(htmlParticipants.map((participant) => [participant.name, participant]));
478
- const svgMap = new Map(svgParticipants.map((participant) => [participant.name, participant]));
479
- return names.map((name) => scoreParticipantIcon(htmlMap.get(name) || null, svgMap.get(name) || null, diffImage));
480
- }
481
-
482
- function participantNames(htmlParticipants, svgParticipants) {
483
- return Array.from(
484
- new Set(
485
- [...htmlParticipants, ...svgParticipants]
486
- .map((participant) => participant.name)
487
- .filter(Boolean),
488
- ),
489
- ).sort((a, b) => a.localeCompare(b));
490
- }
491
-
492
- function scoreParticipantBox(htmlParticipant, svgParticipant) {
493
- const base = htmlParticipant || svgParticipant;
494
- const item = {
495
- name: base?.name ?? "",
496
- status: "ambiguous",
497
- };
498
-
499
- if (!htmlParticipant?.participantBox || !svgParticipant?.participantBox) {
500
- item.reason = "participant box missing on one side";
501
- return item;
502
- }
503
-
504
- const dx = svgParticipant.participantBox.x - htmlParticipant.participantBox.x;
505
- const dy = svgParticipant.participantBox.y - htmlParticipant.participantBox.y;
506
- const dw = svgParticipant.participantBox.w - htmlParticipant.participantBox.w;
507
- const dh = svgParticipant.participantBox.h - htmlParticipant.participantBox.h;
508
-
509
- return {
510
- ...item,
511
- status: "ok",
512
- dx: normalizeOffset(dx),
513
- dy: normalizeOffset(dy),
514
- dw: normalizeOffset(dw),
515
- dh: normalizeOffset(dh),
516
- html_box: {
517
- x: round(htmlParticipant.participantBox.x),
518
- y: round(htmlParticipant.participantBox.y),
519
- w: round(htmlParticipant.participantBox.w),
520
- h: round(htmlParticipant.participantBox.h),
521
- },
522
- svg_box: {
523
- x: round(svgParticipant.participantBox.x),
524
- y: round(svgParticipant.participantBox.y),
525
- w: round(svgParticipant.participantBox.w),
526
- h: round(svgParticipant.participantBox.h),
527
- },
528
- };
529
- }
530
-
531
- function buildParticipantBoxSection(htmlParticipants, svgParticipants) {
532
- const names = participantNames(htmlParticipants, svgParticipants);
533
- const htmlMap = new Map(htmlParticipants.map((participant) => [participant.name, participant]));
534
- const svgMap = new Map(svgParticipants.map((participant) => [participant.name, participant]));
535
- return names.map((name) => scoreParticipantBox(htmlMap.get(name) || null, svgMap.get(name) || null));
536
- }
537
-
538
- function scoreParticipantColor(htmlParticipant, svgParticipant) {
539
- const base = htmlParticipant || svgParticipant;
540
- const item = {
541
- name: base?.name ?? "",
542
- status: "ambiguous",
543
- };
544
-
545
- if (!htmlParticipant || !svgParticipant) {
546
- item.reason = "participant missing on one side";
547
- return item;
548
- }
549
-
550
- return {
551
- ...item,
552
- status: "ok",
553
- html_background_color: htmlParticipant.backgroundColor ?? null,
554
- svg_background_color: svgParticipant.backgroundColor ?? null,
555
- html_text_color: htmlParticipant.textColor ?? null,
556
- svg_text_color: svgParticipant.textColor ?? null,
557
- html_stereotype_color: htmlParticipant.stereotypeColor ?? null,
558
- svg_stereotype_color: svgParticipant.stereotypeColor ?? null,
559
- background_match: (htmlParticipant.backgroundColor ?? null) === (svgParticipant.backgroundColor ?? null),
560
- text_match: (htmlParticipant.textColor ?? null) === (svgParticipant.textColor ?? null),
561
- stereotype_text_match: (htmlParticipant.stereotypeColor ?? null) === (svgParticipant.stereotypeColor ?? null),
562
- };
563
- }
564
-
565
- function buildParticipantColorSection(htmlParticipants, svgParticipants) {
566
- const names = participantNames(htmlParticipants, svgParticipants);
567
- const htmlMap = new Map(htmlParticipants.map((participant) => [participant.name, participant]));
568
- const svgMap = new Map(svgParticipants.map((participant) => [participant.name, participant]));
569
- return names.map((name) => scoreParticipantColor(htmlMap.get(name) || null, svgMap.get(name) || null));
570
- }
571
-
572
- function groupNames(htmlGroups, svgGroups) {
573
- return Array.from(
574
- new Set(
575
- [...htmlGroups, ...svgGroups]
576
- .map((group) => group.name)
577
- .filter(Boolean),
578
- ),
579
- ).sort((a, b) => a.localeCompare(b));
580
- }
581
-
582
- function scoreGroup(htmlGroup, svgGroup) {
583
- const base = htmlGroup || svgGroup;
584
- const item = {
585
- name: base?.name ?? "",
586
- status: "ambiguous",
587
- };
588
-
589
- if (!htmlGroup?.box || !svgGroup?.box) {
590
- item.reason = "group missing on one side";
591
- return item;
592
- }
593
-
594
- const dx = svgGroup.box.x - htmlGroup.box.x;
595
- const dy = svgGroup.box.y - htmlGroup.box.y;
596
- const dw = svgGroup.box.w - htmlGroup.box.w;
597
- const dh = svgGroup.box.h - htmlGroup.box.h;
598
- const nameDx = htmlGroup.nameBox && svgGroup.nameBox
599
- ? svgGroup.nameBox.x - htmlGroup.nameBox.x
600
- : null;
601
- const nameDy = htmlGroup.nameBox && svgGroup.nameBox
602
- ? svgGroup.nameBox.y - htmlGroup.nameBox.y
603
- : null;
604
-
605
- return {
606
- ...item,
607
- status: "ok",
608
- html_name: htmlGroup.name,
609
- svg_name: svgGroup.name,
610
- dx: normalizeOffset(dx),
611
- dy: normalizeOffset(dy),
612
- dw: normalizeOffset(dw),
613
- dh: normalizeOffset(dh),
614
- name_dx: nameDx === null ? null : normalizeOffset(nameDx),
615
- name_dy: nameDy === null ? null : normalizeOffset(nameDy),
616
- html_box: {
617
- x: round(htmlGroup.box.x),
618
- y: round(htmlGroup.box.y),
619
- w: round(htmlGroup.box.w),
620
- h: round(htmlGroup.box.h),
621
- },
622
- svg_box: {
623
- x: round(svgGroup.box.x),
624
- y: round(svgGroup.box.y),
625
- w: round(svgGroup.box.w),
626
- h: round(svgGroup.box.h),
627
- },
628
- html_name_box: htmlGroup.nameBox ? {
629
- x: round(htmlGroup.nameBox.x),
630
- y: round(htmlGroup.nameBox.y),
631
- w: round(htmlGroup.nameBox.w),
632
- h: round(htmlGroup.nameBox.h),
633
- } : null,
634
- svg_name_box: svgGroup.nameBox ? {
635
- x: round(svgGroup.nameBox.x),
636
- y: round(svgGroup.nameBox.y),
637
- w: round(svgGroup.nameBox.w),
638
- h: round(svgGroup.nameBox.h),
639
- } : null,
640
- };
641
- }
642
-
643
- function buildGroupSection(htmlGroups, svgGroups) {
644
- const names = groupNames(htmlGroups, svgGroups);
645
- const htmlMap = new Map(htmlGroups.map((group) => [group.name, group]));
646
- const svgMap = new Map(svgGroups.map((group) => [group.name, group]));
647
- return names.map((name) => scoreGroup(htmlMap.get(name) || null, svgMap.get(name) || null));
648
- }
649
-
650
- function scoreOccurrence(htmlOcc, svgOcc) {
651
- const base = htmlOcc || svgOcc;
652
- const item = {
653
- participant: base?.participant ?? "",
654
- idx: base?.idx ?? 0,
655
- status: "ambiguous",
656
- };
657
-
658
- if (!htmlOcc?.box || !svgOcc?.box) {
659
- item.reason = `occurrence missing on ${!htmlOcc ? "html" : "svg"} side`;
660
- return item;
661
- }
662
-
663
- const dx = svgOcc.box.x - htmlOcc.box.x;
664
- const dy = svgOcc.box.y - htmlOcc.box.y;
665
- const dw = svgOcc.box.w - htmlOcc.box.w;
666
- const dh = svgOcc.box.h - htmlOcc.box.h;
667
-
668
- return {
669
- ...item,
670
- status: "ok",
671
- dx: normalizeOffset(dx),
672
- dy: normalizeOffset(dy),
673
- dw: normalizeOffset(dw),
674
- dh: normalizeOffset(dh),
675
- html_box: {
676
- x: round(htmlOcc.box.x),
677
- y: round(htmlOcc.box.y),
678
- w: round(htmlOcc.box.w),
679
- h: round(htmlOcc.box.h),
680
- },
681
- svg_box: {
682
- x: round(svgOcc.box.x),
683
- y: round(svgOcc.box.y),
684
- w: round(svgOcc.box.w),
685
- h: round(svgOcc.box.h),
686
- },
687
- };
688
- }
689
-
690
- function buildOccurrenceSection(htmlOccurrences, svgOccurrences) {
691
- const maxLen = Math.max(htmlOccurrences.length, svgOccurrences.length);
692
- const results = [];
693
- for (let i = 0; i < maxLen; i++) {
694
- results.push(scoreOccurrence(htmlOccurrences[i] || null, svgOccurrences[i] || null));
695
- }
696
- return results;
697
- }
698
-
699
- function scoreFragmentDivider(htmlDiv, svgDiv) {
700
- const base = htmlDiv || svgDiv;
701
- const item = {
702
- idx: base?.idx ?? 0,
703
- label: base?.label ?? "",
704
- status: "ambiguous",
705
- };
706
-
707
- if (!htmlDiv || !svgDiv) {
708
- item.reason = `divider missing on ${!htmlDiv ? "html" : "svg"} side`;
709
- return item;
710
- }
711
-
712
- const dx = svgDiv.x - htmlDiv.x;
713
- const dy = svgDiv.y - htmlDiv.y;
714
- const dw = svgDiv.width - htmlDiv.width;
715
-
716
- return {
717
- ...item,
718
- status: "ok",
719
- dx: normalizeOffset(dx),
720
- dy: normalizeOffset(dy),
721
- dw: normalizeOffset(dw),
722
- html_y: round(htmlDiv.y),
723
- svg_y: round(svgDiv.y),
724
- html_x: round(htmlDiv.x),
725
- svg_x: round(svgDiv.x),
726
- html_width: round(htmlDiv.width),
727
- svg_width: round(svgDiv.width),
728
- };
729
- }
730
-
731
- function scoreDivider(htmlDiv, svgDiv) {
732
- const base = htmlDiv || svgDiv;
733
- const item = {
734
- idx: base?.idx ?? 0,
735
- label: base?.label ?? "",
736
- status: "ambiguous",
737
- };
738
-
739
- if (!htmlDiv || !svgDiv) {
740
- item.reason = `divider missing on ${!htmlDiv ? "html" : "svg"} side`;
741
- return item;
742
- }
743
-
744
- const dy = round(svgDiv.y - htmlDiv.y);
745
- return {
746
- ...item,
747
- status: dy === 0 ? "ok" : "ambiguous",
748
- dy,
749
- html_box: htmlDiv.box,
750
- svg_box: svgDiv.box,
751
- html_label_box: htmlDiv.label_box,
752
- svg_label_box: svgDiv.label_box,
753
- };
754
- }
755
-
756
- function buildDividerSection(htmlDividers, svgDividers) {
757
- const maxLen = Math.max(htmlDividers.length, svgDividers.length);
758
- const results = [];
759
- for (let i = 0; i < maxLen; i++) {
760
- results.push(scoreDivider(htmlDividers[i] || null, svgDividers[i] || null));
761
- }
762
- return results;
763
- }
764
-
765
- function buildFragmentDividerSection(htmlDividers, svgDividers) {
766
- const maxLen = Math.max(htmlDividers.length, svgDividers.length);
767
- const results = [];
768
- for (let i = 0; i < maxLen; i++) {
769
- results.push(scoreFragmentDivider(htmlDividers[i] || null, svgDividers[i] || null));
770
- }
771
- return results;
772
- }
773
-
774
- function buildTitleSection(htmlTitle, svgTitle, diffImage) {
775
- if (!htmlTitle && !svgTitle) return null;
776
- // Wrap as single-element arrays and reuse buildSection
777
- const htmlItems = htmlTitle ? [htmlTitle] : [];
778
- const svgItems = svgTitle ? [svgTitle] : [];
779
- const results = buildSection(htmlItems, svgItems, diffImage);
780
- return results.length > 0 ? results[0] : null;
781
- }
782
-
783
- export function buildScoredSections(extracted, diffImage) {
784
- const {
785
- htmlTitle,
786
- svgTitle,
787
- htmlLabels,
788
- svgLabels,
789
- htmlNumbers,
790
- svgNumbers,
791
- htmlArrows,
792
- svgArrows,
793
- htmlParticipants,
794
- svgParticipants,
795
- htmlComments,
796
- svgComments,
797
- htmlGroups,
798
- svgGroups,
799
- htmlOccurrences,
800
- svgOccurrences,
801
- htmlFragmentDividers,
802
- svgFragmentDividers,
803
- htmlDividers,
804
- svgDividers,
805
- } = extracted;
806
-
807
- const iconNames = participantsWithIcons(htmlParticipants, svgParticipants);
808
- const htmlParticipantLabels = buildParticipantLabelItems(htmlParticipants, iconNames);
809
- const svgParticipantLabels = buildParticipantLabelItems(svgParticipants, iconNames);
810
- const htmlParticipantStereotypes = buildParticipantStereotypeItems(htmlParticipants);
811
- const svgParticipantStereotypes = buildParticipantStereotypeItems(svgParticipants);
812
-
813
- return {
814
- title: buildTitleSection(htmlTitle || null, svgTitle || null, diffImage),
815
- labels: buildSection(htmlLabels, svgLabels, diffImage),
816
- numbers: buildSection(htmlNumbers, svgNumbers, diffImage),
817
- arrows: buildArrowSection(htmlArrows, svgArrows, diffImage),
818
- participantLabels: buildSection(htmlParticipantLabels, svgParticipantLabels, diffImage),
819
- participantStereotypes: buildSection(htmlParticipantStereotypes, svgParticipantStereotypes, diffImage),
820
- participantIcons: buildParticipantIconSection(htmlParticipants, svgParticipants, diffImage),
821
- participantBoxes: buildParticipantBoxSection(htmlParticipants, svgParticipants),
822
- participantColors: buildParticipantColorSection(htmlParticipants, svgParticipants),
823
- comments: buildSection(htmlComments, svgComments, diffImage),
824
- groups: buildGroupSection(htmlGroups, svgGroups),
825
- occurrences: buildOccurrenceSection(htmlOccurrences || [], svgOccurrences || []),
826
- fragmentDividers: buildFragmentDividerSection(htmlFragmentDividers || [], svgFragmentDividers || []),
827
- dividers: buildDividerSection(htmlDividers || [], svgDividers || []),
828
- };
829
- }