@zenuml/core 3.33.0 → 3.34.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.
package/index.html CHANGED
@@ -8,20 +8,14 @@
8
8
  <link
9
9
  rel="preload stylesheet"
10
10
  as="style"
11
- href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@300;400;500;700&display=swap"
11
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap"
12
12
  />
13
13
  <style id="zenumlstyle">
14
- /* Prefix your CSS rules with `#diagram` */
15
- /*@import url('https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap');*/
16
- /*!*@import url("https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@300;400;500;700&display=swap");*!*/
17
-
18
- /*#diagram1 .sequence-diagram {*/
19
- /* font-family: "Kalam", serif;*/
20
- /*}*/
14
+ /* Custom styles for the diagram */
21
15
  </style>
22
16
  <link
23
17
  rel="stylesheet"
24
- href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/default.min.css"
18
+ href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github-dark.min.css"
25
19
  crossorigin="anonymous"
26
20
  />
27
21
  <script src="https://cdn.jsdelivr.net/npm/codemirror@5.65.1/lib/codemirror.min.js"></script>
@@ -32,61 +26,102 @@
32
26
  crossorigin="anonymous"
33
27
  referrerpolicy="no-referrer"
34
28
  />
35
- <title>ZenUML Core Demo</title>
29
+ <link
30
+ rel="stylesheet"
31
+ href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.1/theme/material-darker.min.css"
32
+ crossorigin="anonymous"
33
+ />
34
+ <title>ZenUML - Local Development</title>
36
35
  <style>
37
- body {
38
- margin: 0;
36
+ * {
37
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
39
38
  }
40
- .CodeMirror {
41
- /* Set height, width, borders, and global font properties here */
42
- font-family: monospace;
43
- font-size: 13px;
44
- height: 100vh;
39
+
40
+ code, .CodeMirror {
41
+ font-family: 'JetBrains Mono', 'Monaco', 'Consolas', monospace;
45
42
  }
46
43
 
47
- .zenuml .CodeMirror .CodeMirror-cursor {
48
- border-color: #000;
49
- border-left-width: 1px;
44
+ .CodeMirror {
45
+ font-size: 14px;
46
+ height: 100%;
47
+ overflow: hidden;
50
48
  }
51
49
 
52
- .grid {
53
- display: grid;
50
+ .CodeMirror-scroll {
51
+ padding: 1rem;
54
52
  }
55
53
 
56
- .grid-cols-6 {
57
- grid-template-columns: repeat(6, minmax(0, 1fr));
54
+ .zenuml .CodeMirror .CodeMirror-cursor {
55
+ border-color: #fff;
56
+ border-left-width: 2px;
58
57
  }
59
58
 
60
- .col-span-2 {
61
- grid-column: span 2 / span 2;
59
+ .gradient-text {
60
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
61
+ -webkit-background-clip: text;
62
+ -webkit-text-fill-color: transparent;
63
+ background-clip: text;
62
64
  }
63
65
 
64
- .col-span-4 {
65
- grid-column: span 4 / span 4;
66
+
67
+ .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);
66
69
  }
67
70
  </style>
71
+ <script src="https://cdn.tailwindcss.com"></script>
68
72
  </head>
69
- <body>
70
- <div class="m-1 grid grid-cols-6" id="diagram1">
71
- <div class="col-span-2">
72
- <textarea
73
- id="text"
74
- class="col-span-1 m-1 border-2"
75
- cols="30"
76
- rows="200"
77
- ></textarea>
78
- </div>
79
- <div class="col-span-4">
80
- <pre class="zenuml" style="margin: 0"></pre>
73
+ <body class="bg-gray-50">
74
+ <!-- Main Editor Section -->
75
+ <div class="w-full h-screen p-4">
76
+ <div class="grid grid-cols-2 gap-4 h-full" id="diagram1">
77
+ <!-- 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">
80
+ <h3 class="font-medium">Editor</h3>
81
+ <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">
83
+ Basic
84
+ </button>
85
+ <button onclick="loadExample('advanced')" class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors">
86
+ Advanced
87
+ </button>
88
+ <button onclick="clearEditor()" class="text-sm bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded transition-colors">
89
+ Clear
90
+ </button>
91
+ <button onclick="exportDiagram()" class="text-sm bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded transition-colors">
92
+ Export PNG
93
+ </button>
94
+ </div>
95
+ </div>
96
+ <div style="height: calc(100% - 52px);">
97
+ <textarea id="text" style="display: none;"></textarea>
98
+ </div>
99
+ </div>
100
+
101
+ <!-- Preview Panel -->
102
+ <div class="bg-white rounded-lg shadow-lg editor-shadow overflow-hidden h-full">
103
+ <div class="bg-gray-800 text-white px-4 py-3">
104
+ <h3 class="font-medium">Preview</h3>
105
+ </div>
106
+ <div style="height: calc(100% - 52px); overflow: auto;" class="p-4">
107
+ <pre class="zenuml" style="margin: 0"></pre>
108
+ </div>
109
+ </div>
81
110
  </div>
82
111
  </div>
112
+
113
+
83
114
  <script type="module">
84
115
  import { waitUntil, debounce } from "./src/utils.ts";
85
116
  import { createConfig } from "./src/config.ts";
117
+ import { toPng } from "html-to-image";
86
118
 
87
119
  const editor = CodeMirror.fromTextArea(document.getElementById("text"), {
88
120
  lineNumbers: true,
89
121
  singleCursorHeightPerLine: false,
122
+ theme: "material-darker",
123
+ mode: "text/plain",
124
+ autofocus: true,
90
125
  });
91
126
 
92
127
  const updateDiagram = debounce((content) => {
@@ -112,12 +147,66 @@
112
147
  );
113
148
  });
114
149
 
115
- // Load saved code from localStorage
150
+ // Example data
151
+ const examples = {
152
+ basic: `Alice -> Bob: Hello Bob!
153
+ Bob -> Alice: Hello Alice!`,
154
+ advanced: `title Online Shopping
155
+ participant Customer
156
+ participant WebApp
157
+ participant PaymentService
158
+ participant Database
159
+
160
+ Customer -> WebApp: Browse products
161
+ WebApp -> Database: Query products
162
+ Database --> WebApp: Return products
163
+ WebApp --> Customer: Display products
164
+
165
+ Customer -> WebApp: Add to cart
166
+ WebApp -> Database: Update cart
167
+ Database --> WebApp: Cart updated
168
+ WebApp --> Customer: Show cart
169
+
170
+ Customer -> WebApp: Checkout
171
+ WebApp -> PaymentService: Process payment
172
+ PaymentService --> WebApp: Payment confirmed
173
+ WebApp -> Database: Create order
174
+ Database --> WebApp: Order created
175
+ WebApp --> Customer: Order confirmation`
176
+ };
177
+
178
+ // Global functions
179
+ window.loadExample = function(type) {
180
+ editor.setValue(examples[type] || examples.basic);
181
+ };
182
+
183
+ window.clearEditor = function() {
184
+ editor.setValue('');
185
+ };
186
+
187
+ window.exportDiagram = async function() {
188
+ const element = document.querySelector('.zenuml');
189
+ if (element) {
190
+ try {
191
+ const dataUrl = await toPng(element);
192
+ const link = document.createElement('a');
193
+ link.download = 'sequence-diagram.png';
194
+ link.href = dataUrl;
195
+ link.click();
196
+ } catch (error) {
197
+ console.error('Failed to export diagram:', error);
198
+ }
199
+ }
200
+ };
201
+
202
+ // Load saved code from localStorage or default example
116
203
  const savedCode = localStorage.getItem("zenuml-cm-code");
117
204
  if (savedCode) {
118
205
  editor.setValue(savedCode);
206
+ } else {
207
+ editor.setValue(examples.basic);
119
208
  }
120
209
  </script>
121
210
  <script type="module" src="/src/main.tsx"></script>
122
211
  </body>
123
- </html>
212
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenuml/core",
3
- "version": "3.33.0",
3
+ "version": "3.34.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,7 +36,10 @@
36
36
  "git:forget": "git rm -r --cached . && git add . && git commit -m \"Forget all ignored files\"",
37
37
  "test:specs": "echo \"Error: test:specs is not supported\"",
38
38
  "storybook": "storybook dev -p 6006",
39
- "build-storybook": "storybook build"
39
+ "build-storybook": "storybook build",
40
+ "worker:dev": "pnpm build:site && wrangler dev",
41
+ "worker:deploy": "pnpm build:site && wrangler deploy",
42
+ "worker:deploy:staging": "pnpm build:site && wrangler deploy --env staging"
40
43
  },
41
44
  "main": "./dist/zenuml.js",
42
45
  "module": "./dist/zenuml.esm.mjs",
@@ -70,6 +73,7 @@
70
73
  "immer": "^10.1.1",
71
74
  "jotai": "^2.12.2",
72
75
  "marked": "^4.3.0",
76
+ "pako": "^2.1.0",
73
77
  "pino": "^8.8.0",
74
78
  "radash": "^12.1.0",
75
79
  "ramda": "^0.28.0",
@@ -115,6 +119,7 @@
115
119
  "vite-plugin-css-injected-by-js": "^3.5.2",
116
120
  "vite-plugin-svgr": "^4.3.0",
117
121
  "vite-svg-loader": "^5.1.0",
118
- "vitest": "^3.1.1"
122
+ "vitest": "^3.1.1",
123
+ "wrangler": "^4.25.0"
119
124
  }
120
125
  }
package/renderer.html ADDED
@@ -0,0 +1,366 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" style="height: 100%; width: 100%">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
7
+ <title>ZenUML Web Renderer</title>
8
+ <meta name="description" content="ZenUML Web Renderer - Render sequence diagrams from URL parameters" />
9
+ <meta name="keywords" content="zenuml, sequence diagram, uml, renderer, web" />
10
+
11
+ <!-- Favicon -->
12
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
13
+
14
+ <!-- Fonts -->
15
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
16
+ <link
17
+ rel="preload stylesheet"
18
+ as="style"
19
+ href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@300;400;500;700&display=swap"
20
+ />
21
+
22
+ <!-- Basic styles for the renderer -->
23
+ <style>
24
+ * {
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ body {
29
+ margin: 0;
30
+ padding: 0;
31
+ font-family: 'Roboto Slab', serif, system-ui, -apple-system, sans-serif;
32
+ background-color: white;
33
+ height: 100vh;
34
+ width: 100vw;
35
+ overflow: auto;
36
+ }
37
+
38
+ .zenuml {
39
+ width: 100%;
40
+ height: 100%;
41
+ margin: 0;
42
+ padding: 0;
43
+ overflow: auto;
44
+ text-align: center;
45
+ }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <!-- ZenUML diagram container -->
50
+ <pre class="zenuml" id="zenuml-diagram"></pre>
51
+
52
+ <!-- Initialize the renderer -->
53
+ <script type="module">
54
+ import pako from 'pako';
55
+ // ZenUML Web Renderer initialization
56
+ console.log('ZenUML Web Renderer initializing...');
57
+
58
+ // Default empty ZenUML code to display when no URL parameters are provided
59
+ const DEFAULT_ZENUML_CODE = `title ZenUML Web Renderer
60
+ A.method() {
61
+ B.process()
62
+ return result
63
+ }`;
64
+
65
+ // URL parameter extraction functionality
66
+ function extractCodeFromURL() {
67
+ try {
68
+ console.log('Extracting code parameter from URL...');
69
+
70
+ // Get current URL
71
+ const currentUrl = new URL(window.location.href);
72
+ console.log('Current URL:', currentUrl.href);
73
+
74
+ // Extract the 'code' parameter
75
+ const codeParam = currentUrl.searchParams.get('code');
76
+
77
+ if (codeParam) {
78
+ console.log('Code parameter found:', codeParam.substring(0, 50) + '...');
79
+ return codeParam;
80
+ } else {
81
+ console.log('No code parameter found in URL');
82
+ return null;
83
+ }
84
+ } catch (error) {
85
+ console.error('Error extracting code from URL:', error);
86
+ return null;
87
+ }
88
+ }
89
+
90
+ // Check if URL parameter exists
91
+ function hasCodeParameter() {
92
+ try {
93
+ const currentUrl = new URL(window.location.href);
94
+ return currentUrl.searchParams.has('code');
95
+ } catch (error) {
96
+ console.error('Error checking for code parameter:', error);
97
+ return false;
98
+ }
99
+ }
100
+
101
+ // Base64 decoding functionality
102
+ function decodeBase64(encodedString) {
103
+ try {
104
+ console.log('Attempting to decode Base64 string...');
105
+
106
+ // Check if the string is valid Base64
107
+ if (!encodedString || typeof encodedString !== 'string') {
108
+ throw new Error('Invalid input: string is null, undefined, or not a string');
109
+ }
110
+
111
+ // Remove any whitespace
112
+ const cleanedString = encodedString.trim();
113
+
114
+ // Check if string looks like Base64 (basic validation)
115
+ const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
116
+ if (!base64Regex.test(cleanedString)) {
117
+ throw new Error('Invalid Base64 format');
118
+ }
119
+
120
+ // Attempt to decode
121
+ const decodedString = atob(cleanedString);
122
+ console.log('Base64 decoding successful');
123
+
124
+ return decodedString;
125
+ } catch (error) {
126
+ console.error('Base64 decoding failed:', error);
127
+ throw new Error(`Base64 decoding failed: ${error.message}`);
128
+ }
129
+ }
130
+
131
+ // Gzip decompression functionality
132
+ function decompressGzip(compressedData) {
133
+ try {
134
+ console.log('Attempting to decompress gzip data...');
135
+
136
+ // Convert base64 decoded string to Uint8Array
137
+ const binaryString = compressedData;
138
+ const bytes = new Uint8Array(binaryString.length);
139
+ for (let i = 0; i < binaryString.length; i++) {
140
+ bytes[i] = binaryString.charCodeAt(i);
141
+ }
142
+
143
+ // Decompress using pako
144
+ const decompressed = pako.ungzip(bytes, { to: 'string' });
145
+ console.log('Gzip decompression successful');
146
+
147
+ return decompressed;
148
+ } catch (error) {
149
+ console.error('Gzip decompression failed:', error);
150
+ throw new Error(`Gzip decompression failed: ${error.message}`);
151
+ }
152
+ }
153
+
154
+ // Process URL code parameter with Base64 decoding and optional gzip decompression
155
+ function processURLCodeParameter(encodedCode) {
156
+ try {
157
+ console.log('Processing URL code parameter...');
158
+
159
+ if (!encodedCode) {
160
+ console.log('No encoded code provided');
161
+ return null;
162
+ }
163
+
164
+ let processedCode = encodedCode;
165
+
166
+ // Step 1: Try to decode as Base64
167
+ try {
168
+ const base64Decoded = decodeBase64(encodedCode);
169
+ console.log('Base64 decoding successful');
170
+ processedCode = base64Decoded;
171
+
172
+ // Step 2: Check if the decoded data is gzipped and decompress if needed
173
+ // Gzip files start with magic numbers: 1f 8b
174
+ if (processedCode.length >= 2 &&
175
+ processedCode.charCodeAt(0) === 0x1f &&
176
+ processedCode.charCodeAt(1) === 0x8b) {
177
+ console.log('Gzip signature detected, attempting decompression...');
178
+ processedCode = decompressGzip(processedCode);
179
+ console.log('Successfully decompressed gzipped data');
180
+ } else {
181
+ console.log('No gzip signature found, using Base64 decoded data');
182
+ }
183
+ } catch (base64Error) {
184
+ console.warn('Base64 decoding failed, trying direct gzip decompression:', base64Error);
185
+
186
+ // If Base64 fails, maybe it's already raw text or needs different handling
187
+ // Try to see if it's URL encoded
188
+ try {
189
+ const urlDecoded = decodeURIComponent(encodedCode);
190
+ if (urlDecoded !== encodedCode) {
191
+ console.log('URL decoding applied');
192
+ processedCode = urlDecoded;
193
+ }
194
+ } catch (urlError) {
195
+ console.warn('URL decoding failed:', urlError);
196
+ }
197
+ }
198
+
199
+ return processedCode;
200
+ } catch (error) {
201
+ console.error('Failed to process URL code parameter:', error);
202
+
203
+ // Return the original code as fallback (might be plain text)
204
+ console.log('All decoding attempts failed, using raw parameter value');
205
+ return encodedCode;
206
+ }
207
+ }
208
+
209
+ // Initialize the renderer
210
+ async function initRenderer() {
211
+ try {
212
+ console.log('Waiting for ZenUML core to load...');
213
+
214
+ // Wait for ZenUML to be available
215
+ await waitForZenUML();
216
+
217
+ console.log('ZenUML core loaded successfully');
218
+
219
+ // Get the ZenUML instance that was created by main.ts
220
+ const zenUmlInstance = window.zenUml;
221
+
222
+ if (!zenUmlInstance) {
223
+ throw new Error('ZenUML instance not found');
224
+ }
225
+
226
+ // Verify ZenUML version
227
+ console.log('ZenUML Version:', window.ZENUML_VERSION);
228
+
229
+ // Extract and process code from URL parameters
230
+ const urlCodeParam = extractCodeFromURL();
231
+ let codeToRender = DEFAULT_ZENUML_CODE;
232
+
233
+ if (urlCodeParam) {
234
+ console.log('URL code parameter detected, processing...');
235
+
236
+ try {
237
+ // Process the URL code parameter (includes Base64 decoding)
238
+ const processedCode = processURLCodeParameter(urlCodeParam);
239
+
240
+ if (processedCode) {
241
+ codeToRender = processedCode;
242
+ addStatusIndicator('success', 'Code decoded and ready to render');
243
+ } else {
244
+ console.log('Processed code is empty, using default diagram');
245
+ addStatusIndicator('warning', 'Empty code parameter, using default');
246
+ }
247
+ } catch (error) {
248
+ console.error('Failed to process URL code parameter:', error);
249
+ addStatusIndicator('error', 'Failed to decode code parameter');
250
+ // Keep using default code
251
+ }
252
+ } else {
253
+ console.log('No URL code parameter, using default diagram');
254
+ addStatusIndicator('info', 'Using default diagram');
255
+ }
256
+
257
+ // Render the diagram
258
+ console.log('Rendering diagram...');
259
+ await zenUmlInstance.render(codeToRender);
260
+
261
+ console.log('Diagram rendered successfully');
262
+
263
+ console.log('ZenUML Web Renderer initialized successfully');
264
+
265
+ // Add success indicator to the page
266
+ addStatusIndicator('success', 'ZenUML Web Renderer Ready');
267
+
268
+ } catch (error) {
269
+ console.error('Failed to initialize renderer:', error);
270
+ addStatusIndicator('error', `Initialization failed: ${error.message}`);
271
+
272
+ // Show error in the diagram container
273
+ const diagramContainer = document.getElementById('zenuml-diagram');
274
+ if (diagramContainer) {
275
+ diagramContainer.innerHTML = `
276
+ <div style="padding: 20px; color: #dc2626; text-align: center;">
277
+ <h3>ZenUML Renderer Error</h3>
278
+ <p>Failed to initialize: ${error.message}</p>
279
+ <button onclick="location.reload()" style="margin-top: 10px; padding: 8px 16px; background: #dc2626; color: white; border: none; border-radius: 4px; cursor: pointer;">
280
+ Retry
281
+ </button>
282
+ </div>
283
+ `;
284
+ }
285
+ }
286
+ }
287
+
288
+ // Wait for ZenUML to be available with timeout
289
+ function waitForZenUML(timeout = 10000) {
290
+ return new Promise((resolve, reject) => {
291
+ const startTime = Date.now();
292
+
293
+ const checkZenUml = () => {
294
+ if (typeof window.zenUml !== 'undefined') {
295
+ resolve();
296
+ } else if (Date.now() - startTime > timeout) {
297
+ reject(new Error('ZenUML loading timeout'));
298
+ } else {
299
+ setTimeout(checkZenUml, 100);
300
+ }
301
+ };
302
+
303
+ checkZenUml();
304
+ });
305
+ }
306
+
307
+ // Add status indicator to show initialization status
308
+ function addStatusIndicator(type, message) {
309
+ const indicator = document.createElement('div');
310
+ indicator.id = 'status-indicator';
311
+
312
+ let backgroundColor;
313
+ switch (type) {
314
+ case 'success':
315
+ backgroundColor = '#10b981';
316
+ break;
317
+ case 'error':
318
+ backgroundColor = '#dc2626';
319
+ break;
320
+ case 'info':
321
+ backgroundColor = '#3b82f6';
322
+ break;
323
+ case 'warning':
324
+ backgroundColor = '#f59e0b';
325
+ break;
326
+ default:
327
+ backgroundColor = '#6b7280';
328
+ }
329
+
330
+ indicator.style.cssText = `
331
+ position: fixed;
332
+ top: 10px;
333
+ right: 10px;
334
+ padding: 8px 12px;
335
+ border-radius: 4px;
336
+ font-size: 12px;
337
+ font-weight: 500;
338
+ z-index: 1000;
339
+ background: ${backgroundColor};
340
+ color: white;
341
+ `;
342
+ indicator.textContent = message;
343
+ document.body.appendChild(indicator);
344
+
345
+ // Auto-hide success and info messages after 3 seconds
346
+ if (type === 'success' || type === 'info') {
347
+ setTimeout(() => {
348
+ if (indicator.parentNode) {
349
+ indicator.parentNode.removeChild(indicator);
350
+ }
351
+ }, 3000);
352
+ }
353
+ }
354
+
355
+ // Start initialization when DOM is ready
356
+ if (document.readyState === 'loading') {
357
+ document.addEventListener('DOMContentLoaded', initRenderer);
358
+ } else {
359
+ initRenderer();
360
+ }
361
+ </script>
362
+
363
+ <!-- Load ZenUML core -->
364
+ <script type="module" src="/src/main.ts"></script>
365
+ </body>
366
+ </html>