@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,149 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /*
4
- * What this file does:
5
- * Runs the compare-case analyzer end to end from the command line.
6
- *
7
- * High-level flow:
8
- * 1. Parse CLI flags such as case name and diff tolerances.
9
- * 2. Open compare-case.html in Playwright.
10
- * 3. Extract semantic geometry from the live HTML and SVG renderers.
11
- * 4. Capture native screenshots of both sides and build the analyzer's local diff.
12
- * 5. Build a structured report and optionally write artifacts to disk.
13
- * 6. Print either JSON, summaries, or both.
14
- *
15
- * This file is intentionally thin. The detailed work lives in focused modules:
16
- * config, browser extraction, diffing, scoring, residual attribution, and output.
17
- *
18
- * Example input:
19
- * `node scripts/analyze-compare-case.mjs --case async-2a --user-data-dir "/Users/pengxiao/Library/Application Support/Google/Chrome" --profile-directory "Profile 8" --channel chrome --headed --json`
20
- *
21
- * Example output:
22
- * A report object printed as JSON, with top-level sections such as `labels`,
23
- * `numbers`, `arrows`, `participant_labels`, `participant_icons`,
24
- * `participant_boxes`, `residual_scopes`, `diff`, and `capture`.
25
- */
26
-
27
- import process from "node:process";
28
- import { resolve } from "node:path";
29
- import { pathToFileURL } from "node:url";
30
-
31
- import { chromium } from "playwright";
32
-
33
- import { parseArgs } from "./analyze-compare-case/config.mjs";
34
- import { collectLabelData } from "./analyze-compare-case/collect-data.mjs";
35
- import { maybeWriteArtifacts, writeReportOutput } from "./analyze-compare-case/output.mjs";
36
- import { renderAndReadDiffPanel } from "./analyze-compare-case/panel-diff.mjs";
37
- import { buildReport } from "./analyze-compare-case/report.mjs";
38
-
39
- export async function main(argv = process.argv.slice(2), stdout = process.stdout) {
40
- const args = parseArgs(argv);
41
- if (args.profileDirectory && !args.userDataDir) {
42
- throw new Error("--profile-directory requires --user-data-dir");
43
- }
44
- const compareUrl = `${args.baseUrl.replace(/\/$/, "")}/e2e/tools/compare-case.html?case=${encodeURIComponent(args.caseName)}`;
45
- const chromiumArgs = args.profileDirectory
46
- ? [`--profile-directory=${args.profileDirectory}`]
47
- : [];
48
- const launchOptions = {
49
- channel: args.browserChannel || undefined,
50
- headless: args.headless,
51
- viewport: args.viewport,
52
- deviceScaleFactor: 2,
53
- args: chromiumArgs,
54
- };
55
- const persistentContext = args.userDataDir
56
- ? await chromium.launchPersistentContext(args.userDataDir, launchOptions)
57
- : null;
58
- const browser = persistentContext ? null : await chromium.launch({
59
- channel: args.browserChannel || undefined,
60
- headless: args.headless,
61
- args: chromiumArgs,
62
- });
63
- const context = persistentContext || await browser.newContext({
64
- viewport: args.viewport,
65
- deviceScaleFactor: 2,
66
- });
67
- const page = persistentContext
68
- ? context.pages()[0] || await context.newPage()
69
- : await context.newPage();
70
-
71
- try {
72
- await page.goto(compareUrl, { waitUntil: "networkidle" });
73
- await page.waitForSelector("#html-output .interaction, #html-output .frame, #html-output .sequence-diagram");
74
- await page.waitForSelector("#svg-output svg");
75
-
76
- const extracted = await collectLabelData(page);
77
-
78
- // Use CDP screenshots to match native-diff-ext (source of truth).
79
- // The extension uses DOM.getBoxModel border-box + Page.captureScreenshot
80
- // with clip and scale:1. Playwright's locator.screenshot() differs subtly
81
- // in how it clips elements, so we replicate the extension's exact logic.
82
- const cdpSession = await page.context().newCDPSession(page);
83
- async function cdpScreenshotElement(selector) {
84
- const { root } = await cdpSession.send("DOM.getDocument", {});
85
- const { nodeId } = await cdpSession.send("DOM.querySelector", {
86
- nodeId: root.nodeId,
87
- selector,
88
- });
89
- if (!nodeId) throw new Error(`Element not found: ${selector}`);
90
- const { model } = await cdpSession.send("DOM.getBoxModel", { nodeId });
91
- const border = model.border;
92
- const x = border[0];
93
- const y = border[1];
94
- const width = Math.ceil(border[2] - border[0]);
95
- const height = Math.ceil(border[5] - border[1]);
96
- const { data } = await cdpSession.send("Page.captureScreenshot", {
97
- format: "png",
98
- clip: { x, y, width, height, scale: 1 },
99
- captureBeyondViewport: true,
100
- });
101
- return Buffer.from(data, "base64");
102
- }
103
-
104
- const htmlBuffer = await cdpScreenshotElement(extracted.htmlRootSelector);
105
- const svgBuffer = await cdpScreenshotElement(extracted.svgRootSelector);
106
- await cdpSession.detach();
107
-
108
- await page.evaluate(() => {
109
- if (typeof window.restoreHtmlAfterCapture === "function") {
110
- window.restoreHtmlAfterCapture();
111
- }
112
- });
113
-
114
- const diffImage = await renderAndReadDiffPanel(page, htmlBuffer, svgBuffer);
115
- const report = buildReport(extracted.caseName || args.caseName, extracted, diffImage);
116
- report.diff = diffImage.stats;
117
- report.capture = {
118
- url: compareUrl,
119
- html_root: extracted.htmlRoot,
120
- svg_root: extracted.svgRoot,
121
- diff_badge: diffImage.badgeText,
122
- panel_stats: diffImage.panelStats,
123
- };
124
-
125
- const artifactPaths = await maybeWriteArtifacts(args.outputDir, htmlBuffer, svgBuffer, diffImage, report);
126
- if (artifactPaths) {
127
- report.artifacts = artifactPaths;
128
- }
129
-
130
- writeReportOutput(stdout, report, args);
131
- return report;
132
- } finally {
133
- await context.close();
134
- if (browser) {
135
- await browser.close();
136
- }
137
- }
138
- }
139
-
140
- const isDirectRun = process.argv[1]
141
- ? pathToFileURL(resolve(process.argv[1])).href === import.meta.url
142
- : false;
143
-
144
- if (isDirectRun) {
145
- main().catch((error) => {
146
- console.error(error.stack || error.message || String(error));
147
- process.exit(1);
148
- });
149
- }
@@ -1,117 +0,0 @@
1
- #!/usr/bin/env node
2
- const fs = require("fs");
3
- const { execSync } = require("child_process");
4
- const path = require("path");
5
-
6
- const getCurrentVersion = () => {
7
- try {
8
- const pkg = require("../package.json");
9
- const npmVersion = execSync(`npm view ${pkg.name} version`, {
10
- stdio: ["pipe", "pipe", "pipe"],
11
- })
12
- .toString()
13
- .trim();
14
- console.log(`Latest version on npm: ${npmVersion}`);
15
- return npmVersion;
16
- } catch (error) {
17
- console.error(
18
- "Failed to get npm version. Please ensure you have npm registry access.",
19
- );
20
- console.error("Error:", error.message);
21
- process.exit(1);
22
- }
23
- };
24
-
25
- const getCommitMessages = (currentVersion) => {
26
- try {
27
- // Try to fetch tags first
28
- try {
29
- execSync("git fetch --tags");
30
- } catch (e) {
31
- console.log("Warning: Could not fetch tags:", e.message);
32
- }
33
-
34
- // Get commits since the last version tag
35
- const messages = execSync(
36
- `git log v${currentVersion}..HEAD --pretty=format:%s%n%b`,
37
- ).toString();
38
- console.log("\nCommit messages since", `v${currentVersion}:`);
39
- console.log(messages || "(no commits)");
40
- return messages;
41
- } catch (e) {
42
- console.log(
43
- "Warning: Could not get commits since last version, falling back to recent commits",
44
- );
45
- // Fallback to recent commits if tag not found
46
- const messages = execSync("git log -10 --pretty=format:%s%n%b").toString();
47
- console.log("\nRecent commit messages:");
48
- console.log(messages || "(no commits)");
49
- return messages;
50
- }
51
- };
52
-
53
- const determineVersionBump = (messages) => {
54
- const lines = messages.split("\n").filter(Boolean);
55
- if (
56
- lines.some((msg) => msg.includes("BREAKING CHANGE") || msg.includes("!:"))
57
- ) {
58
- return "major";
59
- }
60
- if (lines.some((msg) => msg.toLowerCase().startsWith("feat"))) {
61
- return "minor";
62
- }
63
- return "patch";
64
- };
65
-
66
- const getNewVersion = (currentVersion, bump) => {
67
- const [major, minor, patch] = currentVersion.split(".").map(Number);
68
- switch (bump) {
69
- case "major":
70
- return `${major + 1}.0.0`;
71
- case "minor":
72
- return `${major}.${minor + 1}.0`;
73
- case "patch":
74
- return `${major}.${minor}.${patch + 1}`;
75
- default:
76
- throw new Error(`Invalid version bump type: ${bump}`);
77
- }
78
- };
79
-
80
- const run = async () => {
81
- const dryRun = process.argv.includes("--dry-run");
82
- if (dryRun) {
83
- console.log("DRY RUN: No changes will be made\n");
84
- }
85
-
86
- const currentVersion = getCurrentVersion();
87
- console.log(`Current version: ${currentVersion}`);
88
-
89
- const messages = getCommitMessages(currentVersion);
90
- const versionBump = determineVersionBump(messages);
91
- console.log(`\nDetermined version bump: ${versionBump}`);
92
-
93
- const newVersion = getNewVersion(currentVersion, versionBump);
94
- console.log(`New version will be: ${newVersion}`);
95
-
96
- if (!dryRun) {
97
- // Update package.json
98
- const pkg = require("../package.json");
99
- pkg.version = newVersion;
100
- fs.writeFileSync(
101
- path.join(__dirname, "../package.json"),
102
- JSON.stringify(pkg, null, 2) + "\n",
103
- );
104
-
105
- // Set output for GitHub Actions
106
- if (process.env.GITHUB_ACTIONS) {
107
- fs.appendFileSync(process.env.GITHUB_OUTPUT, `version=${newVersion}\n`);
108
- }
109
- } else {
110
- console.log("\nDRY RUN: No changes were made");
111
- }
112
- };
113
-
114
- run().catch((err) => {
115
- console.error("Error:", err.message);
116
- process.exit(1);
117
- });
@@ -1,173 +0,0 @@
1
- #!/usr/bin/env node
2
- // Generate two sets of Playwright snapshots (html & legacy modes) and a diff overlay.
3
- // Usage: node scripts/snapshot-dual.js -- tests/creation.spec.ts
4
-
5
- const { spawnSync } = require("node:child_process");
6
- const fs = require("node:fs/promises");
7
- const path = require("node:path");
8
- const { PNG } = require("pngjs");
9
- const pixelmatchModule = require("pixelmatch");
10
- const pixelmatch = pixelmatchModule.default || pixelmatchModule;
11
-
12
- const repoRoot = path.resolve(__dirname, "..");
13
- const testsArg = process.argv.slice(2);
14
- const tmpRoot = path.join(repoRoot, "tmp", "snapshots-dual");
15
- const paths = {
16
- original: path.join(tmpRoot, "original"),
17
- html: path.join(tmpRoot, "html"),
18
- legacy: path.join(tmpRoot, "legacy"),
19
- diff: path.join(tmpRoot, "diff"),
20
- };
21
-
22
- function snapshotsDirToSpecPath(dirName) {
23
- return path.join("tests", dirName.replace(/-snapshots$/, ""));
24
- }
25
-
26
- function fileNameToTestName(file) {
27
- return file.replace(/\.png$/, "").replace(/-chromium.*$/, "");
28
- }
29
-
30
- async function ensureDir(dir) {
31
- await fs.mkdir(dir, { recursive: true });
32
- }
33
-
34
- async function listSnapshotDirs() {
35
- const testsDir = path.join(repoRoot, "tests");
36
- const entries = await fs.readdir(testsDir, { withFileTypes: true });
37
- return entries
38
- .filter((e) => e.isDirectory() && e.name.endsWith("-snapshots"))
39
- .map((e) => path.join(testsDir, e.name));
40
- }
41
-
42
- async function copySnapshotDirs(destRoot) {
43
- const dirs = await listSnapshotDirs();
44
- await ensureDir(destRoot);
45
- for (const dir of dirs) {
46
- const target = path.join(destRoot, path.basename(dir));
47
- await fs.rm(target, { recursive: true, force: true });
48
- await fs.cp(dir, target, { recursive: true });
49
- }
50
- }
51
-
52
- function runPlaywright(mode) {
53
- const env = { ...process.env, VERTICAL_MODE: mode };
54
- const args = ["playwright", "test", "--update-snapshots", ...testsArg];
55
- const result = spawnSync("npx", args, {
56
- cwd: repoRoot,
57
- env,
58
- stdio: "inherit",
59
- });
60
- if (result.status !== 0) {
61
- throw new Error(`Playwright failed in "${mode}" mode`);
62
- }
63
- }
64
-
65
- async function diffImages() {
66
- const differences = [];
67
- await ensureDir(paths.diff);
68
- const htmlDirs = await fs.readdir(paths.html, { withFileTypes: true });
69
- for (const dirent of htmlDirs) {
70
- if (!dirent.isDirectory()) continue;
71
- const baseName = dirent.name;
72
- const htmlDir = path.join(paths.html, baseName);
73
- const legacyDir = path.join(paths.legacy, baseName);
74
- const files = await fs.readdir(htmlDir);
75
- for (const file of files) {
76
- if (!file.endsWith(".png")) continue;
77
- const htmlPngPath = path.join(htmlDir, file);
78
- const legacyPngPath = path.join(legacyDir, file);
79
- try {
80
- const [htmlBuf, legacyBuf] = await Promise.all([
81
- fs.readFile(htmlPngPath),
82
- fs.readFile(legacyPngPath),
83
- ]);
84
- const htmlImg = PNG.sync.read(htmlBuf);
85
- const legacyImg = PNG.sync.read(legacyBuf);
86
- if (
87
- htmlImg.width !== legacyImg.width ||
88
- htmlImg.height !== legacyImg.height
89
- ) {
90
- console.warn(`Skipping ${file}: dimension mismatch`);
91
- continue;
92
- }
93
- const diff = new PNG({
94
- width: htmlImg.width,
95
- height: htmlImg.height,
96
- });
97
- const diffPixels = pixelmatch(
98
- htmlImg.data,
99
- legacyImg.data,
100
- diff.data,
101
- htmlImg.width,
102
- htmlImg.height,
103
- { threshold: 0.1 },
104
- );
105
- const outDir = path.join(paths.diff, baseName);
106
- await ensureDir(outDir);
107
- const diffFileName = file.replace(/\.png$/, ".diff.png");
108
- await fs.writeFile(
109
- path.join(outDir, diffFileName),
110
- PNG.sync.write(diff),
111
- );
112
- if (diffPixels > 0) {
113
- differences.push({
114
- snapshotDir: baseName,
115
- file,
116
- diffPixels,
117
- specPath: snapshotsDirToSpecPath(baseName),
118
- testName: fileNameToTestName(file),
119
- diffPath: path.join(paths.diff, baseName, diffFileName),
120
- });
121
- }
122
- } catch (err) {
123
- console.warn(`Diff failed for ${baseName}/${file}: ${err.message}`);
124
- }
125
- }
126
- }
127
- return differences;
128
- }
129
-
130
- async function main() {
131
- await ensureDir(tmpRoot);
132
- // const snapshotDirs = await listSnapshotDirs();
133
- // await fs.rm(paths.original, { recursive: true, force: true });
134
- // await ensureDir(paths.original);
135
- // backup existing snapshots
136
- // await copySnapshotDirs(paths.original);
137
-
138
- // legacy mode
139
- runPlaywright("legacy");
140
- await fs.rm(paths.legacy, { recursive: true, force: true });
141
- await copySnapshotDirs(paths.legacy);
142
-
143
- // html mode
144
- runPlaywright("html");
145
- await fs.rm(paths.html, { recursive: true, force: true });
146
- await copySnapshotDirs(paths.html);
147
-
148
- // generate diffs
149
- const differences = await diffImages();
150
- console.log(`Snapshots saved under ${paths.html} and ${paths.legacy}`);
151
- console.log(`Diff overlays saved under ${paths.diff}`);
152
- if (differences.length === 0) {
153
- console.log("All snapshots match between html and legacy modes.");
154
- } else {
155
- console.log("Snapshots with differences:");
156
- const sorted = differences.sort((a, b) => {
157
- if (a.specPath === b.specPath)
158
- return a.testName.localeCompare(b.testName);
159
- return a.specPath.localeCompare(b.specPath);
160
- });
161
- for (const diff of sorted) {
162
- const relDiffPath = path.relative(repoRoot, diff.diffPath);
163
- console.log(
164
- `- ${diff.specPath} (test \"${diff.testName}\"): ${diff.snapshotDir}/${diff.file} -> ${diff.diffPixels} differing pixels [${relDiffPath}]`,
165
- );
166
- }
167
- }
168
- }
169
-
170
- main().catch((err) => {
171
- console.error(err);
172
- process.exit(1);
173
- });
@@ -1,70 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Script to update Cypress image snapshots
5
- *
6
- * This script:
7
- * 1. Removes all .diff.png files
8
- * 2. Replaces .png files with their .actual.png counterparts
9
- * 3. Removes all .actual.png files
10
- */
11
-
12
- const fs = require("fs");
13
- const path = require("path");
14
- const { promisify } = require("util");
15
- const { exec } = require("child_process");
16
-
17
- const execAsync = promisify(exec);
18
-
19
- // Path to the image snapshots directory
20
- const SNAPSHOTS_DIR = path.resolve(
21
- __dirname,
22
- "../cypress/e2e/__image_snapshots__",
23
- );
24
-
25
- async function updateSnapshots() {
26
- try {
27
- console.log("Starting snapshot update process...");
28
-
29
- // Check if the snapshots directory exists
30
- if (!fs.existsSync(SNAPSHOTS_DIR)) {
31
- console.error(`Error: Snapshots directory not found at ${SNAPSHOTS_DIR}`);
32
- process.exit(1);
33
- }
34
-
35
- console.log("1. Removing .diff.png files...");
36
- await execAsync(`find "${SNAPSHOTS_DIR}" -name "*.diff.png" -delete`);
37
-
38
- console.log(
39
- "2. Replacing .png files with their .actual.png counterparts...",
40
- );
41
- // Find all .actual.png files and replace their corresponding .png files
42
- const files = await execAsync(
43
- `find "${SNAPSHOTS_DIR}" -name "*.actual.png"`,
44
- );
45
- const actualFiles = files.stdout.trim().split("\n").filter(Boolean);
46
-
47
- for (const actualFile of actualFiles) {
48
- const targetFile = actualFile.replace(".actual.png", ".png");
49
- console.log(
50
- ` Replacing ${path.basename(targetFile)} with ${path.basename(
51
- actualFile,
52
- )}`,
53
- );
54
- fs.copyFileSync(actualFile, targetFile);
55
- }
56
-
57
- console.log("3. Removing .actual.png files...");
58
- await execAsync(`find "${SNAPSHOTS_DIR}" -name "*.actual.png" -delete`);
59
-
60
- console.log("Snapshot update completed successfully!");
61
- console.log(
62
- "Remember to commit and push these changes to update the reference snapshots.",
63
- );
64
- } catch (error) {
65
- console.error("Error updating snapshots:", error);
66
- process.exit(1);
67
- }
68
- }
69
-
70
- updateSnapshots();
@@ -1,129 +0,0 @@
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; use the live `#diff-panel canvas` as the sole pixel-diff source of truth.
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`. Use Playwright page inspection only to inspect the page and semantically attribute diffs to letters or elements. Use the live `#diff-panel canvas` as the sole pixel-diff source of truth.
9
-
10
- The workflow is browser-native:
11
-
12
- 1. Open `http://localhost:8080/e2e/tools/compare-case.html?case=<name>`.
13
- 2. Treat the `native-diff-ext` extension as required for pixel diff work: it generates the live `#diff-panel canvas` on page load.
14
- 3. Use the analyzer script at [../../scripts/analyze-compare-case.mjs](../../scripts/analyze-compare-case.mjs).
15
- 4. Prefer `--json` when the next step is automated processing.
16
- 5. Prefer `--output-dir <dir>` when you need saved `html.png`, `svg.png`, `diff.png`, and `report.json`.
17
- 6. Treat all pixel-diff comparison and residual scoping as live-panel work sourced from `#diff-panel canvas`.
18
- 7. When recalibrating or correcting this skill itself, use the live `#diff-panel canvas` to calibrate the skill's measurement rules and reporting language.
19
-
20
- ## Offset Anchor
21
-
22
- All reported offsets must use the **outermost frame's top-left corner** as the anchor.
23
-
24
- - HTML anchor: the compare-case HTML frame root
25
- - SVG anchor: the compare-case SVG root / outer frame root
26
- - Do not report alternate offset systems
27
- - Do not anchor offsets to participant boxes, label boxes, stereotype boxes, or local containers
28
- - If a local-container-relative reading differs from the frame-anchor reading, prefer the frame-anchor reading in all reporting
29
-
30
- ## Browser Requirement
31
-
32
- Use **Playwright browser tools only** for browser interaction in this workflow.
33
-
34
- - Preferred tools: `browser_navigate`, `browser_snapshot`, `browser_evaluate`, `browser_take_screenshot`, `browser_click`, `browser_wait_for`
35
- - Do not use Chrome DevTools browser tools for scoring, DOM inspection, screenshot capture, or residual validation
36
- - Do not build your own pixel diff from HTML/SVG screenshots. For pixel comparison, use only the extension-rendered `#diff-panel canvas`
37
-
38
- ## Rules
39
-
40
- - Do not use `html-to-image` for capture.
41
- - Use browser-native screenshots only.
42
- - Use Playwright for browser-native screenshots and page inspection.
43
- - Use the extension-generated live `#diff-panel canvas` as the sole source for pixel diff comparison and residual validation.
44
- - All offset calculations must be anchored to the outermost frame's top-left corner.
45
- - When re-checking, recalibrating, or correcting `dia-scoring` itself, calibrate the skill against the live `#diff-panel canvas`, not against a separately-built diff or memory of prior results.
46
- - If `#diff-panel canvas` is absent, do not recalibrate or correct `dia-scoring` itself.
47
- - Never build or trust a local screenshot-to-screenshot pixel diff when `#diff-panel canvas` is the question.
48
- - Do not use Chrome DevTools browser tools for this workflow.
49
- - Scope:
50
- - normal messages
51
- - self messages
52
- - returns
53
- - fragment conditions such as `[cond]`, `[else]`
54
- - fragment section labels such as `catch`, `finally`
55
- - participant label text and participant box geometry
56
- - participant icons (actor, database, ec2, lambda, azurefunction, sqs, sns, iam, boundary, control, entity)
57
- - participant stereotypes such as `«BFF»`, `«Interface»`
58
- - participant background colors (`#FFEBE6`, `#0747A6`, etc.) and computed text contrast
59
- - participant groups (dashed outline containers with title bar)
60
- - inline comments (`// text`) above messages and fragments, including styled comments (`// [red] text`)
61
- - residual `html-only` and `svg-only` diff clusters scoped back to nearby elements
62
- - For each supported message, include:
63
- - label text
64
- - fragment condition / section label text when present
65
- - sequence number text, including fragment sequence numbers when present
66
- - arrow geometry keyed by sequence number
67
- - normal/return arrow endpoint deltas: `left_dx`, `right_dx`, `width_dx`
68
- - self-arrow loop geometry from the painted loop path plus arrowhead, not the outer `svg` viewport
69
- - self-arrow vertical deltas: `top_dy`, `bottom_dy`, `height_dy`
70
- - For participant icons, include:
71
- - icon presence (HTML vs SVG)
72
- - participant label text when the participant has an icon
73
- - icon position relative to participant label
74
- - icon visual match confirmation from diff image
75
- - For participant stereotypes, include:
76
- - stereotype text presence (HTML vs SVG), e.g. `«BFF»`
77
- - stereotype position relative to participant label (above label, smaller font)
78
- - stereotype offset must be measured with per-letter glyph-box comparison relative to the outermost frame anchor
79
- - do not use participant-box-relative or other local-container-relative deltas in final reporting
80
- - do not mark a stereotype as clean from glyph boxes alone; also check the live `#diff-panel canvas` in the stereotype row
81
- - 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
82
- - stereotype text color matching participant background contrast
83
- - For participant colors, include:
84
- - background fill color (hex value) on participant rect
85
- - text color contrast (dark text on light bg, white text on dark bg)
86
- - color application to both top and bottom participant boxes
87
- - For participant groups, include:
88
- - group name text presence and position (centered title bar)
89
- - dashed outline rect enclosing grouped participants
90
- - group bounds: leftmost to rightmost participant with margin
91
- - group height extending to diagram bottom
92
- - For inline comments, include:
93
- - comment text presence and position (above the associated statement)
94
- - comment Y offset from the message/fragment it belongs to
95
- - fragment-level comments (e.g. `// comment 4` before `if(...)`) positioned above fragment header
96
- - styled comment color application (e.g. `// [red] text`)
97
- - For participant boxes, include:
98
- - `html_box` and `svg_box` with `x`, `y`, `w`, `h`
99
- - box deltas `dx`, `dy`, `dw`, `dh`
100
- - SVG measurement based on the painted outer bounds of the stroked box, not the inset rect geometry
101
- - For residual scopes, include:
102
- - connected `html-only` and `svg-only` diff clusters from `#diff-panel canvas`
103
- - cluster `size`, `bbox`, and `centroid`
104
- - nearest scoped HTML and SVG targets at that position
105
- - summaries that explain which element a remaining positional diff most likely belongs to
106
- - live native diff panel confirmation before claiming a hotspot is real
107
- - the largest confirmed live-panel `html-only` and `svg-only` clusters with approximate positions
108
- - grouped summaries of where the panel's red and blue pixels are concentrated
109
- - Do not report a residual hotspot as real if it is absent from the live `#diff-panel canvas`.
110
- - Do not stop at totals like `HTML-only (44)` or `SVG-only (55)` when residuals matter; report where those pixels are.
111
- - Each reported letter must be backed by:
112
- - direct HTML-vs-SVG browser layout positions
113
- - pixel-panel confirmation from `#diff-panel canvas`
114
- - Participant stereotypes are first-class targets, not just part of `participant-box` or `participant-label`.
115
- - If the evidence is weak or contradictory, keep the letter `ambiguous`.
116
-
117
- ## Commands
118
-
119
- Run from [../..](../..):
120
-
121
- ```bash
122
- node scripts/analyze-compare-case.mjs --case async-2a
123
- node scripts/analyze-compare-case.mjs --case async-2a --json
124
- node scripts/analyze-compare-case.mjs --case async-2a --output-dir tmp/message-elements/async-2a
125
- ```
126
-
127
- ## References
128
-
129
- - Selector and pairing details: [references/selectors-and-keys.md](references/selectors-and-keys.md)
@@ -1,7 +0,0 @@
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