@zenuml/core 3.47.9 → 3.48.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.
Files changed (205) hide show
  1. package/dist/cli/zenuml.mjs +13529 -0
  2. package/dist/cli/zenuml.mjs.map +1 -0
  3. package/dist/cloud-icons-eHuugVSv.js.map +1 -0
  4. package/dist/zenuml.esm.mjs +2153 -2156
  5. package/dist/zenuml.esm.mjs.map +1 -0
  6. package/dist/zenuml.js +82 -82
  7. package/dist/zenuml.js.map +1 -0
  8. package/package.json +18 -5
  9. package/.agents/skills/babysit-pr/SKILL.md +0 -223
  10. package/.agents/skills/babysit-pr/agents/openai.yaml +0 -7
  11. package/.agents/skills/dia-scoring/SKILL.md +0 -139
  12. package/.agents/skills/dia-scoring/agents/openai.yaml +0 -7
  13. package/.agents/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  14. package/.agents/skills/land-pr/SKILL.md +0 -120
  15. package/.agents/skills/propagate-core-release/SKILL.md +0 -205
  16. package/.agents/skills/propagate-core-release/agents/openai.yaml +0 -7
  17. package/.agents/skills/propagate-core-release/references/downstreams.md +0 -42
  18. package/.agents/skills/ship-branch/SKILL.md +0 -105
  19. package/.agents/skills/submit-branch/SKILL.md +0 -76
  20. package/.agents/skills/validate-branch/SKILL.md +0 -72
  21. package/.claude/commands/README.md +0 -162
  22. package/.claude/commands/analyze.md +0 -101
  23. package/.claude/commands/clarify.md +0 -158
  24. package/.claude/commands/code-review.md +0 -322
  25. package/.claude/commands/constitution.md +0 -73
  26. package/.claude/commands/create-docs.md +0 -309
  27. package/.claude/commands/full-context.md +0 -121
  28. package/.claude/commands/gemini-consult.md +0 -164
  29. package/.claude/commands/handoff.md +0 -146
  30. package/.claude/commands/implement.md +0 -56
  31. package/.claude/commands/plan.md +0 -43
  32. package/.claude/commands/refactor.md +0 -188
  33. package/.claude/commands/specify.md +0 -21
  34. package/.claude/commands/tasks.md +0 -62
  35. package/.claude/commands/update-docs.md +0 -314
  36. package/.claude/hooks/README.md +0 -270
  37. package/.claude/hooks/config/sensitive-patterns.json +0 -86
  38. package/.claude/hooks/gemini-context-injector.sh +0 -129
  39. package/.claude/hooks/mcp-security-scan.sh +0 -147
  40. package/.claude/hooks/notify.sh +0 -103
  41. package/.claude/hooks/setup/hook-setup.md +0 -96
  42. package/.claude/hooks/setup/settings.json.template +0 -63
  43. package/.claude/hooks/sounds/complete.wav +0 -0
  44. package/.claude/hooks/sounds/input-needed.wav +0 -0
  45. package/.claude/hooks/subagent-context-injector.sh +0 -65
  46. package/.claude/skills/babysit-pr/SKILL.md +0 -223
  47. package/.claude/skills/babysit-pr/agents/openai.yaml +0 -7
  48. package/.claude/skills/dia-scoring/SKILL.md +0 -139
  49. package/.claude/skills/dia-scoring/agents/openai.yaml +0 -7
  50. package/.claude/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  51. package/.claude/skills/emoji-eval/SKILL.md +0 -187
  52. package/.claude/skills/land-pr/SKILL.md +0 -120
  53. package/.claude/skills/propagate-core-release/SKILL.md +0 -205
  54. package/.claude/skills/propagate-core-release/agents/openai.yaml +0 -7
  55. package/.claude/skills/propagate-core-release/references/downstreams.md +0 -42
  56. package/.claude/skills/ship-branch/SKILL.md +0 -105
  57. package/.claude/skills/submit-branch/SKILL.md +0 -76
  58. package/.claude/skills/validate-branch/SKILL.md +0 -72
  59. package/.claude/skills/zenuml-ux-research/SKILL.md +0 -183
  60. package/.claude/skills/zenuml-ux-research/references/assertion-catalog.md +0 -261
  61. package/.claude/skills/zenuml-ux-research/references/best-practices-overview.md +0 -56
  62. package/.claude/skills/zenuml-ux-research/references/report-template.md +0 -89
  63. package/.claude/skills/zenuml-ux-research/references/scenarios/edit-message-label.md +0 -37
  64. package/.claude/skills/zenuml-ux-research/references/scenarios/insert-message.md +0 -36
  65. package/.claude/skills/zenuml-ux-research/references/scenarios/insert-participant.md +0 -31
  66. package/.claude/skills/zenuml-ux-research/references/scenarios/rename-participant.md +0 -33
  67. package/.claude/skills/zenuml-ux-research/references/scenarios/undo-insert.md +0 -35
  68. package/.devcontainer/devcontainer.json +0 -21
  69. package/.dockerignore +0 -19
  70. package/.eslintrc.js +0 -39
  71. package/.git-blame-ignore-revs +0 -6
  72. package/.kiro/hooks/README.md +0 -38
  73. package/.kiro/hooks/session-sound-notification.js +0 -44
  74. package/.kiro/hooks/session-sound-notification.json +0 -23
  75. package/.mcp.json.example +0 -17
  76. package/.nvmrc +0 -1
  77. package/.prettierignore +0 -4
  78. package/.prettierrc +0 -1
  79. package/.specify/memory/constitution.md +0 -33
  80. package/.specify/scripts/bash/check-prerequisites.sh +0 -166
  81. package/.specify/scripts/bash/common.sh +0 -113
  82. package/.specify/scripts/bash/create-new-feature.sh +0 -97
  83. package/.specify/scripts/bash/setup-plan.sh +0 -60
  84. package/.specify/scripts/bash/update-agent-context.sh +0 -728
  85. package/.specify/templates/agent-file-template.md +0 -23
  86. package/.specify/templates/plan-template.md +0 -219
  87. package/.specify/templates/spec-template.md +0 -116
  88. package/.specify/templates/tasks-template.md +0 -127
  89. package/.storybook/main.ts +0 -25
  90. package/.storybook/preview.ts +0 -29
  91. package/.watchmanconfig +0 -3
  92. package/AGENTS.md +0 -26
  93. package/CLAUDE.md +0 -124
  94. package/DEPLOYMENT.md +0 -62
  95. package/Dockerfile +0 -36
  96. package/IMPLEMENTATION_PLAN.md +0 -163
  97. package/Integration/vanilla-js/index.html +0 -42
  98. package/MCP-ASSISTANT-RULES.md +0 -85
  99. package/README_CN.md +0 -15
  100. package/TUTORIAL.md +0 -116
  101. package/antlr/antlr-4.11.1-complete.jar +0 -0
  102. package/bun.lock +0 -1544
  103. package/bunfig.toml +0 -52
  104. package/docs/UNICODE_SUPPORT.md +0 -179
  105. package/docs/ai-context/deployment-infrastructure.md +0 -21
  106. package/docs/ai-context/docs-overview.md +0 -89
  107. package/docs/ai-context/handoff.md +0 -174
  108. package/docs/ai-context/project-structure.md +0 -160
  109. package/docs/ai-context/system-integration.md +0 -21
  110. package/docs/asciidoc/contributor.adoc +0 -54
  111. package/docs/asciidoc/create-my-own-theme.adoc +0 -149
  112. package/docs/asciidoc/images/creation-component.png +0 -0
  113. package/docs/asciidoc/images/creation-rtl.png +0 -0
  114. package/docs/asciidoc/images/message-arrow-rtl.png +0 -0
  115. package/docs/asciidoc/images/occurrence.png +0 -0
  116. package/docs/asciidoc/images/return-message-conflict.png +0 -0
  117. package/docs/asciidoc/images/shift-up-half-the-height.png +0 -0
  118. package/docs/asciidoc/images/three-layer-info-arch.png +0 -0
  119. package/docs/asciidoc/images/vertical-alignment.svg +0 -1
  120. package/docs/asciidoc/images/vertically-aligning.png +0 -0
  121. package/docs/asciidoc/index.adoc +0 -277
  122. package/docs/asciidoc/theme-debug-web-app.png +0 -0
  123. package/docs/asciidoc/tutorial.adoc +0 -22
  124. package/docs/asciidoc/user-css.png +0 -0
  125. package/docs/async-vs-sync-parser-rules.md +0 -81
  126. package/docs/divider-parser-allow-spaces.md +0 -38
  127. package/docs/highlighting-messages.md +0 -52
  128. package/docs/images/editor-sample.png +0 -0
  129. package/docs/inherited-vs-provided-from.md +0 -64
  130. package/docs/parser/Assignment.md +0 -8
  131. package/docs/parser/PARSER_IMPROVEMENTS_CC.md +0 -425
  132. package/docs/parser/grammar_review_gemini.md +0 -116
  133. package/docs/participants-function.md +0 -25
  134. package/docs/responsive-participant-margin.md +0 -52
  135. package/docs/starter.md +0 -9
  136. package/docs/superpowers/plans/2026-03-27-e2e-test-reorg.md +0 -698
  137. package/docs/superpowers/plans/2026-03-30-emoji-support.md +0 -1220
  138. package/docs/superpowers/plans/2026-03-30-self-correcting-scoring.md +0 -206
  139. package/docs/superpowers/plans/2026-04-15-keyboard-editing-on-diagram.md +0 -1992
  140. package/docs/superpowers/plans/2026-04-15-zenuml-ux-research-skill.md +0 -1452
  141. package/docs/ux-research/.gitkeep +0 -0
  142. package/docs/ux-research/2026-04-15-rename-participant.md +0 -156
  143. package/docs/ux-research/2026-04-18-insert-participant.md +0 -151
  144. package/docs/width-translate-and-offsets.md +0 -62
  145. package/docs/xss.md +0 -59
  146. package/e2e/data/compare-cases.js +0 -1090
  147. package/e2e/data/diff-algorithm.js +0 -199
  148. package/e2e/fixtures/create-message.html +0 -26
  149. package/e2e/fixtures/editable-label.html +0 -35
  150. package/e2e/fixtures/editable-span.html +0 -122
  151. package/e2e/fixtures/empty-diagram.html +0 -23
  152. package/e2e/fixtures/fixture.html +0 -31
  153. package/e2e/fixtures/insert-participant.html +0 -23
  154. package/e2e/fixtures/reorder-cross-fragment.html +0 -31
  155. package/e2e/fixtures/reorder-fragment.html +0 -29
  156. package/e2e/fixtures/reorder-message.html +0 -27
  157. package/e2e/fixtures/svg-test.html +0 -21
  158. package/e2e/fixtures/type-switch.html +0 -29
  159. package/e2e/tools/canonical-history.html +0 -908
  160. package/e2e/tools/compare-case.html +0 -371
  161. package/e2e/tools/compare.html +0 -35
  162. package/e2e/tools/native-diff-ext/background.js +0 -60
  163. package/e2e/tools/native-diff-ext/bridge.js +0 -26
  164. package/e2e/tools/native-diff-ext/content.js +0 -194
  165. package/e2e/tools/svg-preview.html +0 -56
  166. package/embed.html +0 -193
  167. package/eslint.config.mjs +0 -35
  168. package/firebase-debug.log +0 -108
  169. package/iframe-container-demo/diagram.html +0 -124
  170. package/iframe-container-demo/host.html +0 -817
  171. package/index.html +0 -771
  172. package/mermaid-zenuml-async-spa-auth.png +0 -0
  173. package/mermaid-zenuml-async-spa-auth.snapshot.md +0 -96
  174. package/newsletter/unicode-support-announcement.md +0 -134
  175. package/playground/creation.html +0 -53
  176. package/playground/message.html +0 -63
  177. package/playwright.config.ts +0 -40
  178. package/renderer.html +0 -366
  179. package/scripts/analyze-compare-case/collect-data.mjs +0 -1134
  180. package/scripts/analyze-compare-case/config.mjs +0 -102
  181. package/scripts/analyze-compare-case/geometry.mjs +0 -101
  182. package/scripts/analyze-compare-case/native-diff.mjs +0 -224
  183. package/scripts/analyze-compare-case/output.mjs +0 -74
  184. package/scripts/analyze-compare-case/panel-diff.mjs +0 -114
  185. package/scripts/analyze-compare-case/report.mjs +0 -162
  186. package/scripts/analyze-compare-case/residual-scopes.mjs +0 -347
  187. package/scripts/analyze-compare-case/scoring.mjs +0 -829
  188. package/scripts/analyze-compare-case.mjs +0 -149
  189. package/scripts/bump-version.js +0 -117
  190. package/scripts/snapshot-dual.js +0 -173
  191. package/scripts/update-snapshots.js +0 -70
  192. package/skills/dia-scoring/SKILL.md +0 -129
  193. package/skills/dia-scoring/agents/openai.yaml +0 -7
  194. package/skills/dia-scoring/references/selectors-and-keys.md +0 -253
  195. package/tailwind.config.js +0 -126
  196. package/test-compression.html +0 -274
  197. package/test-mermaid-zenuml.html +0 -57
  198. package/test-setup.ts +0 -124
  199. package/test-url-params.html +0 -192
  200. package/tsconfig.app.json +0 -31
  201. package/tsconfig.node.json +0 -24
  202. package/tsconfig.test.json +0 -9
  203. package/vite.config.lib.ts +0 -93
  204. package/vite.config.ts +0 -84
  205. 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
- }