@zenuml/core 3.43.3 → 3.44.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.
@@ -9,9 +9,9 @@ This project is built with vue cli. It has two types of target: the web applicat
9
9
 
10
10
  ....
11
11
  # to build the library
12
- yarn build
12
+ bun run build
13
13
  # to build the web application
14
- yarn build:site
14
+ bun run build:site
15
15
  ....
16
16
 
17
17
  == Demo pages
@@ -42,11 +42,11 @@ We use antlr4 to parse the ZenUML DSL code. The grammar files are:
42
42
 
43
43
  === How to generate the parser and lexer
44
44
 
45
- Run `yarn antlr4` to generate the parser and lexer.
45
+ Run `bun run antlr` to generate the parser and lexer.
46
46
 
47
47
  === Setup local development environment
48
48
 
49
- Run `yarn antlr:setup` (`python3 -m pip install antlr4-tools`) to install the antlr4 command and the runtime.
49
+ Run `bun run antlr:setup` (`python3 -m pip install antlr4-tools`) to install the antlr4 command and the runtime.
50
50
 
51
51
  To test the setup, go to `src/g4-unit/hello-world` folder, and run:
52
52
 
@@ -1,4 +1,4 @@
1
- Error when running 'yarn build'
1
+ Error when running 'bun run build'
2
2
 
3
3
  ```shell
4
4
  ERROR Failed to compile with 1 error 10:15:37 AM
@@ -10,4 +10,4 @@ Missing binding /Users/pengxiao/workspaces/zenuml/vue-sequence/node_modules/node
10
10
  Node Sass could not find a binding for your current environment: OS X 64-bit with Node.js 14.x
11
11
  ```
12
12
 
13
- Remove 'node_modules' and re-run 'yarn build' under node 14.
13
+ Remove 'node_modules' and re-run 'bun run build' under node 14.
package/docs/watch.md CHANGED
@@ -1 +1 @@
1
- watch 'yarn antlr' src/g4
1
+ watch 'bun run antlr' src/g4
package/index.html CHANGED
@@ -15,7 +15,7 @@
15
15
  </style>
16
16
  <link
17
17
  rel="stylesheet"
18
- href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github-dark.min.css"
18
+ href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css"
19
19
  crossorigin="anonymous"
20
20
  />
21
21
  <script src="https://cdn.jsdelivr.net/npm/codemirror@5.65.1/lib/codemirror.min.js"></script>
@@ -34,11 +34,18 @@
34
34
  <title>ZenUML - Local Development</title>
35
35
  <style>
36
36
  * {
37
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
37
+ font-family:
38
+ "Inter",
39
+ -apple-system,
40
+ BlinkMacSystemFont,
41
+ "Segoe UI",
42
+ "Roboto",
43
+ sans-serif;
38
44
  }
39
45
 
40
- code, .CodeMirror {
41
- font-family: 'JetBrains Mono', 'Monaco', 'Consolas', monospace;
46
+ code,
47
+ .CodeMirror {
48
+ font-family: "JetBrains Mono", "Monaco", "Consolas", monospace;
42
49
  }
43
50
 
44
51
  .CodeMirror {
@@ -63,9 +70,10 @@
63
70
  background-clip: text;
64
71
  }
65
72
 
66
-
67
73
  .editor-shadow {
68
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
74
+ box-shadow:
75
+ 0 20px 25px -5px rgba(0, 0, 0, 0.1),
76
+ 0 10px 10px -5px rgba(0, 0, 0, 0.04);
69
77
  }
70
78
  </style>
71
79
  <script src="https://cdn.tailwindcss.com"></script>
@@ -73,44 +81,61 @@
73
81
  <body class="bg-gray-50">
74
82
  <!-- Main Editor Section -->
75
83
  <div class="w-full h-screen p-4">
76
- <div class="grid grid-cols-2 gap-4 h-full" id="diagram1">
84
+ <div class="grid grid-cols-3 gap-4 h-full" id="diagram1">
77
85
  <!-- Editor Panel -->
78
- <div class="bg-white rounded-lg shadow-lg editor-shadow overflow-hidden h-full">
79
- <div class="bg-gray-800 text-white px-4 py-3 flex items-center justify-between">
86
+ <div
87
+ class="bg-white rounded-lg shadow-lg editor-shadow overflow-hidden h-full"
88
+ >
89
+ <div
90
+ class="bg-gray-800 text-white px-4 py-3 flex items-center justify-between"
91
+ >
80
92
  <h3 class="font-medium">Editor</h3>
81
93
  <div class="flex space-x-2">
82
- <button onclick="loadExample('basic')" class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors">
94
+ <button
95
+ onclick="loadExample('basic')"
96
+ class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors"
97
+ >
83
98
  Basic
84
99
  </button>
85
- <button onclick="loadExample('advanced')" class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors">
100
+ <button
101
+ onclick="loadExample('advanced')"
102
+ class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors"
103
+ >
86
104
  Advanced
87
105
  </button>
88
- <button onclick="clearEditor()" class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors">
106
+ <button
107
+ onclick="clearEditor()"
108
+ class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors"
109
+ >
89
110
  Clear
90
111
  </button>
91
- <button onclick="exportDiagram()" class="text-sm bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded transition-colors">
112
+ <button
113
+ onclick="exportDiagram()"
114
+ class="text-sm bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded transition-colors"
115
+ >
92
116
  Export PNG
93
117
  </button>
94
118
  </div>
95
119
  </div>
96
- <div style="height: calc(100% - 52px);">
97
- <textarea id="text" style="display: none;"></textarea>
120
+ <div style="height: calc(100% - 52px)">
121
+ <textarea id="text" style="display: none"></textarea>
98
122
  </div>
99
123
  </div>
100
124
 
101
125
  <!-- Preview Panel -->
102
- <div class="bg-white rounded-lg shadow-lg editor-shadow overflow-hidden h-full">
126
+ <div
127
+ class="bg-white rounded-lg shadow-lg editor-shadow overflow-hidden h-full col-span-2"
128
+ >
103
129
  <div class="bg-gray-800 text-white px-4 py-3">
104
130
  <h3 class="font-medium">Preview</h3>
105
131
  </div>
106
- <div style="height: calc(100% - 52px); overflow: auto;" class="p-4">
132
+ <div style="height: calc(100% - 52px); overflow: auto" class="p-4">
107
133
  <pre class="zenuml" style="margin: 0"></pre>
108
134
  </div>
109
135
  </div>
110
136
  </div>
111
137
  </div>
112
138
 
113
-
114
139
  <script type="module">
115
140
  import { waitUntil, debounce } from "./src/utils.ts";
116
141
  import { createConfig } from "./src/config.ts";
@@ -172,29 +197,29 @@ WebApp -> PaymentService: Process payment
172
197
  PaymentService --> WebApp: Payment confirmed
173
198
  WebApp -> Database: Create order
174
199
  Database --> WebApp: Order created
175
- WebApp --> Customer: Order confirmation`
200
+ WebApp --> Customer: Order confirmation`,
176
201
  };
177
202
 
178
203
  // Global functions
179
- window.loadExample = function(type) {
204
+ window.loadExample = function (type) {
180
205
  editor.setValue(examples[type] || examples.basic);
181
206
  };
182
207
 
183
- window.clearEditor = function() {
184
- editor.setValue('');
208
+ window.clearEditor = function () {
209
+ editor.setValue("");
185
210
  };
186
211
 
187
- window.exportDiagram = async function() {
188
- const element = document.querySelector('.zenuml');
212
+ window.exportDiagram = async function () {
213
+ const element = document.querySelector(".zenuml");
189
214
  if (element) {
190
215
  try {
191
216
  const dataUrl = await toPng(element);
192
- const link = document.createElement('a');
193
- link.download = 'sequence-diagram.png';
217
+ const link = document.createElement("a");
218
+ link.download = "sequence-diagram.png";
194
219
  link.href = dataUrl;
195
220
  link.click();
196
221
  } catch (error) {
197
- console.error('Failed to export diagram:', error);
222
+ console.error("Failed to export diagram:", error);
198
223
  }
199
224
  }
200
225
  };
@@ -209,4 +234,4 @@ WebApp --> Customer: Order confirmation`
209
234
  </script>
210
235
  <script type="module" src="/src/main.tsx"></script>
211
236
  </body>
212
- </html>
237
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenuml/core",
3
- "version": "3.43.3",
3
+ "version": "3.44.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,9 +13,11 @@
13
13
  "build:gh-pages": "bun run --bun vite build --mode gh-pages",
14
14
  "build": "bun run --bun vite build -c vite.config.lib.ts",
15
15
  "test": "bun test src test/unit",
16
- "pw": "playwright test",
16
+ "pw": "playwright test --reporter=list",
17
17
  "pw:ci": "playwright test",
18
- "pw:update": "playwright test --update-snapshots",
18
+ "measure:layout": "COLLECT_LAYOUT_METRICS=1 playwright test tests/layout-metrics.spec.ts --reporter=line --project=chromium",
19
+ "snapshots:dual": "node scripts/snapshot-dual.js",
20
+ "pw:update": "playwright test --update-snapshots --reporter=list",
19
21
  "pw:update-ci": "playwright test --update-snapshots --reporter=github",
20
22
  "pw:ui": "playwright test --ui",
21
23
  "pw:smoke": "playwright test smoke",
@@ -53,12 +55,6 @@
53
55
  "autoprefixer": {}
54
56
  }
55
57
  },
56
- "pnpm": {
57
- "overrides": {
58
- "react": "^19.0.0",
59
- "react-dom": "^19.0.0"
60
- }
61
- },
62
58
  "dependencies": {
63
59
  "@floating-ui/react": "^0.27.8",
64
60
  "@headlessui/react": "^2.2.1",
@@ -112,6 +108,8 @@
112
108
  "happy-dom": "^18.0.1",
113
109
  "jsdom": "^26.1.0",
114
110
  "less": "^4.3.0",
111
+ "pixelmatch": "^7.1.0",
112
+ "pngjs": "^7.0.0",
115
113
  "postcss": "^8.5.3",
116
114
  "prettier": "3.5.3",
117
115
  "rollup-plugin-visualizer": "^6.0.5",
@@ -1,5 +1,8 @@
1
1
  import { defineConfig, devices } from "@playwright/test";
2
2
 
3
+ const PORT = Number(process.env.PORT) || 8080;
4
+ const BASE_URL = `http://127.0.0.1:${PORT}`;
5
+
3
6
  export default defineConfig({
4
7
  testDir: "./tests",
5
8
  fullyParallel: true,
@@ -11,7 +14,7 @@ export default defineConfig({
11
14
  ? [["github"], ["html", { open: "never", outputFolder: "playwright-report" }]]
12
15
  : [["html", { outputFolder: "playwright-report" }]],
13
16
  use: {
14
- baseURL: "http://127.0.0.1:8080",
17
+ baseURL: BASE_URL,
15
18
  trace: "on-first-retry",
16
19
  screenshot: "only-on-failure",
17
20
  },
@@ -29,8 +32,8 @@ export default defineConfig({
29
32
  },
30
33
  ],
31
34
  webServer: {
32
- command: "bun run dev",
33
- url: "http://127.0.0.1:8080",
35
+ command: `bun run dev -- --port=${PORT} --strictPort`,
36
+ url: BASE_URL,
34
37
  reuseExistingServer: !process.env.CI,
35
38
  timeout: 120 * 1000,
36
39
  },
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ // Generate two sets of Playwright snapshots (server & browser 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
+ server: path.join(tmpRoot, "server"),
18
+ browser: path.join(tmpRoot, "browser"),
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 serverDirs = await fs.readdir(paths.server, { withFileTypes: true });
69
+ for (const dirent of serverDirs) {
70
+ if (!dirent.isDirectory()) continue;
71
+ const baseName = dirent.name;
72
+ const serverDir = path.join(paths.server, baseName);
73
+ const browserDir = path.join(paths.browser, baseName);
74
+ const files = await fs.readdir(serverDir);
75
+ for (const file of files) {
76
+ if (!file.endsWith(".png")) continue;
77
+ const serverPngPath = path.join(serverDir, file);
78
+ const browserPngPath = path.join(browserDir, file);
79
+ try {
80
+ const [serverBuf, browserBuf] = await Promise.all([
81
+ fs.readFile(serverPngPath),
82
+ fs.readFile(browserPngPath),
83
+ ]);
84
+ const serverImg = PNG.sync.read(serverBuf);
85
+ const browserImg = PNG.sync.read(browserBuf);
86
+ if (
87
+ serverImg.width !== browserImg.width ||
88
+ serverImg.height !== browserImg.height
89
+ ) {
90
+ console.warn(`Skipping ${file}: dimension mismatch`);
91
+ continue;
92
+ }
93
+ const diff = new PNG({
94
+ width: serverImg.width,
95
+ height: serverImg.height,
96
+ });
97
+ const diffPixels = pixelmatch(
98
+ serverImg.data,
99
+ browserImg.data,
100
+ diff.data,
101
+ serverImg.width,
102
+ serverImg.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
+ // browser mode
139
+ runPlaywright("browser");
140
+ await fs.rm(paths.browser, { recursive: true, force: true });
141
+ await copySnapshotDirs(paths.browser);
142
+
143
+ // server mode
144
+ runPlaywright("server");
145
+ await fs.rm(paths.server, { recursive: true, force: true });
146
+ await copySnapshotDirs(paths.server);
147
+
148
+ // generate diffs
149
+ const differences = await diffImages();
150
+ console.log(`Snapshots saved under ${paths.server} and ${paths.browser}`);
151
+ console.log(`Diff overlays saved under ${paths.diff}`);
152
+ if (differences.length === 0) {
153
+ console.log("All snapshots match between server and browser 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
+ });