@haklex/rich-renderer-mermaid 0.0.40 → 0.0.42

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.
@@ -0,0 +1,566 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useColorScheme } from "@haklex/rich-editor";
3
+ import { useState, useId, useEffect } from "react";
4
+ var mermaidContainer = "_36yqie0";
5
+ var mermaidEditHint = "_36yqie1";
6
+ var zoomControls = "_36yqie2";
7
+ var zoomBtn = "_36yqie3";
8
+ var mermaidLoading = "_36yqie4";
9
+ var mermaidError = "_36yqie6";
10
+ var editorPopup = "_36yqie7";
11
+ var editorHeader = "_36yqie8";
12
+ var editorHeaderLeft = "_36yqie9";
13
+ var editorHeaderRight = "_36yqiea";
14
+ var editorSep = "_36yqieb";
15
+ var editorTitle = "_36yqiec";
16
+ var editorTplBtn = "_36yqied";
17
+ var editorViewToggle = "_36yqiee";
18
+ var editorViewItem = "_36yqief";
19
+ var editorViewItemActive = "_36yqieg";
20
+ var editorIconBtn = "_36yqieh";
21
+ var editorBody = "_36yqiei";
22
+ var editorPane = "_36yqiej";
23
+ var editorPaneHalf = "_36yqiek";
24
+ var editorPaneFull = "_36yqiel";
25
+ var editorPaneLabel = "_36yqiem";
26
+ var editorPreviewPane = "_36yqien";
27
+ var editorPreviewWrap = "_36yqieo";
28
+ var editorPreviewEmpty = "_36yqiep";
29
+ var editorPreviewErrorWrap = "_36yqieq";
30
+ var editorPreviewErrorIcon = "_36yqier";
31
+ var editorPreviewErrorTitle = "_36yqies";
32
+ var editorPreviewErrorMsg = "_36yqiet";
33
+ var codeEditor = "_36yqieu";
34
+ var codeGutter = "_36yqiev";
35
+ var codeGutterLine = "_36yqiew";
36
+ var codeArea = "_36yqiex";
37
+ var editorFooter = "_36yqiey";
38
+ var footerActions = "_36yqie12";
39
+ var footerBtnCancel = "_36yqie14 _36yqie13";
40
+ var footerBtnSave = "_36yqie15 _36yqie13";
41
+ const MIX = {
42
+ textSec: 60,
43
+ textMuted: 40,
44
+ textFaint: 25,
45
+ line: 50,
46
+ arrow: 85,
47
+ nodeFill: 3,
48
+ nodeStroke: 20,
49
+ groupHeader: 5,
50
+ innerStroke: 12
51
+ };
52
+ function parseHex(hex) {
53
+ return [
54
+ Number.parseInt(hex.slice(1, 3), 16),
55
+ Number.parseInt(hex.slice(3, 5), 16),
56
+ Number.parseInt(hex.slice(5, 7), 16)
57
+ ];
58
+ }
59
+ function toHex(r, g, b) {
60
+ const clamp = (v) => Math.round(Math.max(0, Math.min(255, v)));
61
+ return `#${clamp(r).toString(16).padStart(2, "0")}${clamp(g).toString(16).padStart(2, "0")}${clamp(b).toString(16).padStart(2, "0")}`;
62
+ }
63
+ function mix(fg, bg, percent) {
64
+ const [fR, fG, fB] = parseHex(fg);
65
+ const [bR, bG, bB] = parseHex(bg);
66
+ const p = percent / 100;
67
+ return toHex(
68
+ fR * p + bR * (1 - p),
69
+ fG * p + bG * (1 - p),
70
+ fB * p + bB * (1 - p)
71
+ );
72
+ }
73
+ function deriveTokens(bg, fg) {
74
+ return {
75
+ bg,
76
+ fg,
77
+ line: mix(fg, bg, MIX.line),
78
+ arrow: mix(fg, bg, MIX.arrow),
79
+ nodeFill: mix(fg, bg, MIX.nodeFill),
80
+ nodeStroke: mix(fg, bg, MIX.nodeStroke),
81
+ groupHeader: mix(fg, bg, MIX.groupHeader),
82
+ innerStroke: mix(fg, bg, MIX.innerStroke),
83
+ textSec: mix(fg, bg, MIX.textSec),
84
+ textMuted: mix(fg, bg, MIX.textMuted),
85
+ textFaint: mix(fg, bg, MIX.textFaint)
86
+ };
87
+ }
88
+ function buildTheme(bg, fg) {
89
+ const t = deriveTokens(bg, fg);
90
+ return {
91
+ theme: "base",
92
+ themeVariables: {
93
+ background: t.bg,
94
+ fontFamily: "system-ui, -apple-system, sans-serif",
95
+ fontSize: "14px",
96
+ primaryColor: t.nodeFill,
97
+ primaryTextColor: t.fg,
98
+ primaryBorderColor: t.nodeStroke,
99
+ secondaryColor: t.nodeFill,
100
+ secondaryTextColor: t.fg,
101
+ secondaryBorderColor: t.innerStroke,
102
+ tertiaryColor: t.groupHeader,
103
+ tertiaryTextColor: t.fg,
104
+ tertiaryBorderColor: t.innerStroke,
105
+ noteBkgColor: t.nodeFill,
106
+ noteTextColor: t.fg,
107
+ noteBorderColor: t.innerStroke,
108
+ lineColor: t.line,
109
+ textColor: t.fg,
110
+ mainBkg: t.nodeFill,
111
+ nodeBorder: t.nodeStroke,
112
+ nodeTextColor: t.fg,
113
+ clusterBkg: t.bg,
114
+ clusterBorder: t.nodeStroke,
115
+ titleColor: t.fg,
116
+ labelColor: t.fg,
117
+ altBackground: t.nodeFill,
118
+ fillType0: t.nodeFill,
119
+ fillType1: t.groupHeader,
120
+ fillType2: mix(fg, bg, 7),
121
+ fillType3: t.innerStroke,
122
+ fillType4: t.nodeStroke,
123
+ fillType5: t.groupHeader,
124
+ fillType6: t.nodeFill,
125
+ fillType7: mix(fg, bg, 7),
126
+ // Sequence diagram
127
+ actorBkg: t.nodeFill,
128
+ actorBorder: t.nodeStroke,
129
+ actorTextColor: t.fg,
130
+ actorLineColor: t.line,
131
+ signalColor: t.fg,
132
+ signalTextColor: t.fg,
133
+ labelBoxBkgColor: t.nodeFill,
134
+ labelBoxBorderColor: t.nodeStroke,
135
+ labelTextColor: t.fg,
136
+ loopTextColor: t.fg,
137
+ activationBorderColor: t.line,
138
+ activationBkgColor: t.innerStroke,
139
+ sequenceNumberColor: t.bg,
140
+ // Git graph
141
+ git0: t.fg,
142
+ git1: t.line,
143
+ git2: t.textMuted,
144
+ git3: t.textFaint,
145
+ git4: t.nodeStroke,
146
+ git5: t.innerStroke,
147
+ git6: t.groupHeader,
148
+ git7: t.nodeFill,
149
+ gitBranchLabel0: t.bg,
150
+ gitBranchLabel1: t.bg,
151
+ gitBranchLabel2: t.bg,
152
+ gitBranchLabel3: t.fg,
153
+ gitInv0: t.bg,
154
+ // Pie chart
155
+ pie1: t.fg,
156
+ pie2: mix(fg, bg, 80),
157
+ pie3: t.line,
158
+ pie4: t.textMuted,
159
+ pie5: t.nodeStroke,
160
+ pie6: t.innerStroke,
161
+ pie7: mix(fg, bg, 7),
162
+ pie8: t.groupHeader,
163
+ pie9: t.nodeFill,
164
+ pie10: t.bg,
165
+ pie11: t.textSec,
166
+ pie12: t.arrow,
167
+ pieTitleTextColor: t.fg,
168
+ pieSectionTextColor: t.bg,
169
+ pieLegendTextColor: t.fg,
170
+ pieLegendTextSize: "14px",
171
+ pieStrokeColor: t.bg,
172
+ pieStrokeWidth: "2px",
173
+ classText: t.fg,
174
+ // Gantt / timeline
175
+ sectionBkgColor: t.nodeFill,
176
+ altSectionBkgColor: t.groupHeader,
177
+ sectionBkgColor2: mix(fg, bg, 7),
178
+ taskBkgColor: t.innerStroke,
179
+ taskTextColor: t.fg,
180
+ taskTextLightColor: t.fg,
181
+ taskTextOutsideColor: t.fg,
182
+ activeTaskBkgColor: t.nodeStroke,
183
+ activeTaskBorderColor: t.line,
184
+ gridColor: t.innerStroke,
185
+ doneTaskBkgColor: t.nodeStroke,
186
+ doneTaskBorderColor: t.line,
187
+ critBkgColor: t.fg,
188
+ critBorderColor: t.fg,
189
+ todayLineColor: t.fg,
190
+ // Requirement diagram
191
+ requirementBackground: t.nodeFill,
192
+ requirementBorderColor: t.nodeStroke,
193
+ requirementBorderSize: "1px",
194
+ requirementTextColor: t.fg,
195
+ relationColor: t.line,
196
+ relationLabelBackground: t.bg,
197
+ relationLabelColor: t.fg,
198
+ edgeLabelBackground: t.bg,
199
+ // Color scales
200
+ cScale0: t.nodeFill,
201
+ cScale1: t.innerStroke,
202
+ cScale2: t.nodeStroke,
203
+ cScale3: t.textMuted,
204
+ cScale4: t.line,
205
+ cScale5: t.textSec,
206
+ cScale6: mix(fg, bg, 70),
207
+ cScale7: mix(fg, bg, 80),
208
+ cScale8: t.arrow,
209
+ cScale9: t.fg,
210
+ cScaleLabel0: t.fg,
211
+ cScaleLabel2: t.fg
212
+ }
213
+ };
214
+ }
215
+ const lightTheme = buildTheme("#ffffff", "#27272a");
216
+ const darkTheme = buildTheme("#18181b", "#fafafa");
217
+ const lightTokens = deriveTokens("#ffffff", "#27272a");
218
+ const darkTokens = deriveTokens("#18181b", "#fafafa");
219
+ const CORNER_RADIUS = {
220
+ node: 5,
221
+ classNode: 4,
222
+ entity: 5,
223
+ subgraphOuter: 7,
224
+ actor: 6,
225
+ activation: 4
226
+ };
227
+ function applyCornerRounding(doc) {
228
+ const applyRadius = (selector, radius) => {
229
+ const rects = doc.querySelectorAll(selector);
230
+ for (const rect of rects) {
231
+ rect.setAttribute("rx", String(radius));
232
+ rect.setAttribute("ry", String(radius));
233
+ }
234
+ };
235
+ applyRadius(".node > rect", CORNER_RADIUS.node);
236
+ applyRadius(".node > .basic", CORNER_RADIUS.node);
237
+ applyRadius(".classGroup > rect", CORNER_RADIUS.classNode);
238
+ applyRadius(".er > rect", CORNER_RADIUS.entity);
239
+ applyRadius(".cluster > rect", CORNER_RADIUS.subgraphOuter);
240
+ applyRadius(".actor", CORNER_RADIUS.actor);
241
+ applyRadius(
242
+ ".activation0, .activation1, .activation2",
243
+ CORNER_RADIUS.activation
244
+ );
245
+ }
246
+ function buildVisualCss(tokens) {
247
+ return `
248
+ /* beautiful-mermaid inspired visual overrides */
249
+
250
+ /* Edge styling: rounded linecaps for clean connections */
251
+ .edgePath path.path,
252
+ .flowchart-link,
253
+ line[class*="messageLine"] {
254
+ stroke-linecap: round !important;
255
+ stroke-linejoin: round !important;
256
+ }
257
+
258
+ /* Cluster/subgraph "soft" styling */
259
+ .cluster > rect {
260
+ fill: ${tokens.nodeFill} !important;
261
+ fill-opacity: 0.96 !important;
262
+ stroke: ${tokens.nodeStroke} !important;
263
+ stroke-width: 1px !important;
264
+ }
265
+
266
+ /* Cluster title */
267
+ .cluster text,
268
+ .cluster .nodeLabel {
269
+ fill: ${tokens.textSec} !important;
270
+ }
271
+
272
+ /* Node stroke consistency */
273
+ .node rect,
274
+ .node circle,
275
+ .node ellipse,
276
+ .node polygon,
277
+ .node .basic {
278
+ stroke: ${tokens.nodeStroke} !important;
279
+ stroke-width: 1px !important;
280
+ }
281
+
282
+ /* Node fill */
283
+ .node rect,
284
+ .node .basic {
285
+ fill: ${tokens.nodeFill} !important;
286
+ }
287
+
288
+ /* Edge label "subtle" styling */
289
+ .edgeLabel rect,
290
+ .labelBkg {
291
+ rx: 5 !important;
292
+ ry: 5 !important;
293
+ fill: ${tokens.bg} !important;
294
+ stroke: ${tokens.innerStroke} !important;
295
+ stroke-width: 1px !important;
296
+ }
297
+
298
+ .edgeLabel text,
299
+ .edgeLabel .edgeLabel {
300
+ fill: ${tokens.textMuted} !important;
301
+ }
302
+
303
+ /* Arrow heads */
304
+ marker path {
305
+ fill: ${tokens.arrow} !important;
306
+ stroke: ${tokens.arrow} !important;
307
+ }
308
+
309
+ /* Edge paths */
310
+ .edgePath path.path {
311
+ stroke: ${tokens.line} !important;
312
+ stroke-width: 1px !important;
313
+ }
314
+
315
+ /* Sequence diagram refinements */
316
+ .actor {
317
+ fill: ${tokens.nodeFill} !important;
318
+ stroke: ${tokens.nodeStroke} !important;
319
+ stroke-width: 1px !important;
320
+ }
321
+
322
+ .messageLine0,
323
+ .messageLine1 {
324
+ stroke: ${tokens.line} !important;
325
+ stroke-width: 1px !important;
326
+ }
327
+
328
+ .messageText {
329
+ fill: ${tokens.fg} !important;
330
+ }
331
+
332
+ .loopLine {
333
+ stroke: ${tokens.innerStroke} !important;
334
+ stroke-width: 1px !important;
335
+ }
336
+
337
+ .labelBox {
338
+ fill: ${tokens.nodeFill} !important;
339
+ stroke: ${tokens.nodeStroke} !important;
340
+ stroke-width: 1px !important;
341
+ }
342
+
343
+ .loopText tspan,
344
+ .loopText {
345
+ fill: ${tokens.textSec} !important;
346
+ }
347
+
348
+ /* Activation bars */
349
+ .activation0,
350
+ .activation1,
351
+ .activation2 {
352
+ fill: ${tokens.innerStroke} !important;
353
+ stroke: ${tokens.line} !important;
354
+ }
355
+
356
+ /* Note styling */
357
+ .note {
358
+ fill: ${tokens.nodeFill} !important;
359
+ stroke: ${tokens.innerStroke} !important;
360
+ }
361
+
362
+ .noteText {
363
+ fill: ${tokens.fg} !important;
364
+ }
365
+
366
+ /* Inner divider lines */
367
+ .divider {
368
+ stroke: ${tokens.innerStroke} !important;
369
+ stroke-width: 0.75px !important;
370
+ }
371
+
372
+ /* Class diagram */
373
+ .classGroup rect {
374
+ fill: ${tokens.nodeFill} !important;
375
+ stroke: ${tokens.nodeStroke} !important;
376
+ stroke-width: 1px !important;
377
+ }
378
+
379
+ .classGroup line {
380
+ stroke: ${tokens.innerStroke} !important;
381
+ stroke-width: 0.75px !important;
382
+ }
383
+
384
+ .classGroup text {
385
+ fill: ${tokens.fg} !important;
386
+ }
387
+
388
+ .classLabel .box {
389
+ fill: ${tokens.groupHeader} !important;
390
+ stroke: ${tokens.nodeStroke} !important;
391
+ }
392
+
393
+ .relation {
394
+ stroke: ${tokens.line} !important;
395
+ stroke-width: 1px !important;
396
+ }
397
+
398
+ /* State diagram */
399
+ .stateGroup rect,
400
+ .statediagram-state rect {
401
+ fill: ${tokens.nodeFill} !important;
402
+ stroke: ${tokens.nodeStroke} !important;
403
+ stroke-width: 1px !important;
404
+ rx: 5 !important;
405
+ ry: 5 !important;
406
+ }
407
+
408
+ .stateGroup text {
409
+ fill: ${tokens.fg} !important;
410
+ }
411
+
412
+ /* Git graph */
413
+ .commit-id text {
414
+ fill: ${tokens.textMuted} !important;
415
+ }
416
+
417
+ `;
418
+ }
419
+ function postProcessMermaidSvg(svg, tokens) {
420
+ const parser = new DOMParser();
421
+ const doc = parser.parseFromString(svg, "image/svg+xml");
422
+ const parseError = doc.querySelector("parsererror");
423
+ if (parseError) return svg;
424
+ const root = doc.documentElement;
425
+ if (!root || root.tagName.toLowerCase() !== "svg") return svg;
426
+ applyCornerRounding(doc);
427
+ const existingOverride = root.querySelector("style[data-visual-overrides]");
428
+ existingOverride?.remove();
429
+ const styleEl = doc.createElementNS("http://www.w3.org/2000/svg", "style");
430
+ styleEl.dataset.visualOverrides = "";
431
+ styleEl.textContent = buildVisualCss(tokens);
432
+ if (root.firstChild) {
433
+ root.insertBefore(styleEl, root.firstChild);
434
+ } else {
435
+ root.append(styleEl);
436
+ }
437
+ return new XMLSerializer().serializeToString(root);
438
+ }
439
+ function useMermaidRender(content, preferredColorScheme) {
440
+ const [loading, setLoading] = useState(true);
441
+ const [error, setError] = useState("");
442
+ const [svg, setSvg] = useState("");
443
+ const [width, setWidth] = useState();
444
+ const [height, setHeight] = useState();
445
+ const colorScheme = useColorScheme();
446
+ const effectiveColorScheme = preferredColorScheme ?? colorScheme;
447
+ const id = useId().split(":").join("");
448
+ useEffect(() => {
449
+ if (!content) return;
450
+ setError("");
451
+ setLoading(true);
452
+ let cancelled = false;
453
+ import("mermaid").then(async (mo) => {
454
+ const mermaid = mo.default;
455
+ const themeConfig = effectiveColorScheme === "dark" ? darkTheme : lightTheme;
456
+ const tokens = effectiveColorScheme === "dark" ? darkTokens : lightTokens;
457
+ mermaid.initialize({
458
+ startOnLoad: false,
459
+ theme: themeConfig.theme,
460
+ themeVariables: themeConfig.themeVariables,
461
+ darkMode: effectiveColorScheme === "dark",
462
+ flowchart: {
463
+ htmlLabels: true,
464
+ curve: "basis",
465
+ padding: 20,
466
+ nodeSpacing: 24,
467
+ rankSpacing: 48
468
+ },
469
+ sequence: {
470
+ actorMargin: 80,
471
+ messageMargin: 40
472
+ },
473
+ gantt: {
474
+ titleTopMargin: 16,
475
+ barHeight: 24,
476
+ barGap: 6
477
+ }
478
+ });
479
+ let result;
480
+ try {
481
+ result = await mermaid.render(`mermaid-${id}`, content);
482
+ } catch (err) {
483
+ document.getElementById(`dmermaid-${id}`)?.remove();
484
+ if (err instanceof Error) {
485
+ setError(err.message);
486
+ }
487
+ setSvg("");
488
+ setWidth(void 0);
489
+ setHeight(void 0);
490
+ }
491
+ if (cancelled) return;
492
+ if (result) {
493
+ const processedSvg = postProcessMermaidSvg(result.svg, tokens);
494
+ setSvg(processedSvg);
495
+ const match = processedSvg.match(/viewBox="[^"]*\s([\d.]+)\s([\d.]+)"/);
496
+ if (match?.[1] && match?.[2]) {
497
+ setWidth(Number.parseInt(match[1]));
498
+ setHeight(Number.parseInt(match[2]));
499
+ }
500
+ setError("");
501
+ }
502
+ setLoading(false);
503
+ });
504
+ return () => {
505
+ cancelled = true;
506
+ };
507
+ }, [id, content, effectiveColorScheme]);
508
+ let imgSrc = "";
509
+ if (svg) {
510
+ const encoder = new TextEncoder();
511
+ const data = encoder.encode(svg);
512
+ imgSrc = `data:image/svg+xml;base64,${btoa(String.fromCodePoint(...new Uint8Array(data)))}`;
513
+ }
514
+ return { loading, error, imgSrc, svg, width, height };
515
+ }
516
+ const MermaidRenderer = ({ content }) => {
517
+ const { loading, error, imgSrc, width, height } = useMermaidRender(content);
518
+ if (loading) {
519
+ return /* @__PURE__ */ jsx("pre", { className: mermaidLoading, children: /* @__PURE__ */ jsx("code", { children: content }) });
520
+ }
521
+ if (!imgSrc) {
522
+ return /* @__PURE__ */ jsx("div", { className: mermaidError, children: error || "Render failed" });
523
+ }
524
+ return /* @__PURE__ */ jsx("div", { className: mermaidContainer, style: { cursor: "default" }, children: /* @__PURE__ */ jsx("img", { src: imgSrc, alt: "Mermaid diagram", width, height }) });
525
+ };
526
+ export {
527
+ editorViewItem as A,
528
+ editorViewItemActive as B,
529
+ codeEditor as C,
530
+ codeGutter as D,
531
+ codeGutterLine as E,
532
+ codeArea as F,
533
+ editorPreviewWrap as G,
534
+ editorPreviewEmpty as H,
535
+ editorPreviewErrorWrap as I,
536
+ editorPreviewErrorIcon as J,
537
+ editorPreviewErrorTitle as K,
538
+ editorPreviewErrorMsg as L,
539
+ MermaidRenderer as M,
540
+ mermaidError as a,
541
+ mermaidContainer as b,
542
+ mermaidEditHint as c,
543
+ editorHeader as d,
544
+ editorPopup as e,
545
+ editorHeaderLeft as f,
546
+ editorTitle as g,
547
+ editorSep as h,
548
+ editorTplBtn as i,
549
+ editorHeaderRight as j,
550
+ editorViewToggle as k,
551
+ editorIconBtn as l,
552
+ mermaidLoading as m,
553
+ editorBody as n,
554
+ editorPane as o,
555
+ editorPaneFull as p,
556
+ editorPaneHalf as q,
557
+ editorPaneLabel as r,
558
+ editorPreviewPane as s,
559
+ editorFooter as t,
560
+ useMermaidRender as u,
561
+ footerActions as v,
562
+ footerBtnCancel as w,
563
+ footerBtnSave as x,
564
+ zoomBtn as y,
565
+ zoomControls as z
566
+ };
package/dist/index.mjs CHANGED
@@ -2,520 +2,10 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { useColorScheme } from "@haklex/rich-editor";
3
3
  import { usePortalTheme, presentDialog } from "@haklex/rich-editor-ui";
4
4
  import { FishSymbol, Copy, Download, RotateCcw, X, ZoomIn, ZoomOut, Maximize2, CircleAlert, Code2, Columns2, Eye } from "lucide-react";
5
- import { useState, useId, useEffect, useCallback, useRef } from "react";
5
+ import { useCallback, useState, useRef, useEffect } from "react";
6
6
  import { TransformWrapper, TransformComponent, useControls } from "react-zoom-pan-pinch";
7
- var mermaidContainer = "_36yqie0";
8
- var mermaidEditHint = "_36yqie1";
9
- var zoomControls = "_36yqie2";
10
- var zoomBtn = "_36yqie3";
11
- var mermaidLoading = "_36yqie4";
12
- var mermaidError = "_36yqie6";
13
- var editorPopup = "_36yqie7";
14
- var editorHeader = "_36yqie8";
15
- var editorHeaderLeft = "_36yqie9";
16
- var editorHeaderRight = "_36yqiea";
17
- var editorSep = "_36yqieb";
18
- var editorTitle = "_36yqiec";
19
- var editorTplBtn = "_36yqied";
20
- var editorViewToggle = "_36yqiee";
21
- var editorViewItem = "_36yqief";
22
- var editorViewItemActive = "_36yqieg";
23
- var editorIconBtn = "_36yqieh";
24
- var editorBody = "_36yqiei";
25
- var editorPane = "_36yqiej";
26
- var editorPaneHalf = "_36yqiek";
27
- var editorPaneFull = "_36yqiel";
28
- var editorPaneLabel = "_36yqiem";
29
- var editorPreviewPane = "_36yqien";
30
- var editorPreviewWrap = "_36yqieo";
31
- var editorPreviewEmpty = "_36yqiep";
32
- var editorPreviewErrorWrap = "_36yqieq";
33
- var editorPreviewErrorIcon = "_36yqier";
34
- var editorPreviewErrorTitle = "_36yqies";
35
- var editorPreviewErrorMsg = "_36yqiet";
36
- var codeEditor = "_36yqieu";
37
- var codeGutter = "_36yqiev";
38
- var codeGutterLine = "_36yqiew";
39
- var codeArea = "_36yqiex";
40
- var editorFooter = "_36yqiey";
41
- var footerActions = "_36yqie12";
42
- var footerBtnCancel = "_36yqie14 _36yqie13";
43
- var footerBtnSave = "_36yqie15 _36yqie13";
44
- const MIX = {
45
- textSec: 60,
46
- textMuted: 40,
47
- textFaint: 25,
48
- line: 50,
49
- arrow: 85,
50
- nodeFill: 3,
51
- nodeStroke: 20,
52
- groupHeader: 5,
53
- innerStroke: 12
54
- };
55
- function parseHex(hex) {
56
- return [
57
- Number.parseInt(hex.slice(1, 3), 16),
58
- Number.parseInt(hex.slice(3, 5), 16),
59
- Number.parseInt(hex.slice(5, 7), 16)
60
- ];
61
- }
62
- function toHex(r, g, b) {
63
- const clamp = (v) => Math.round(Math.max(0, Math.min(255, v)));
64
- return `#${clamp(r).toString(16).padStart(2, "0")}${clamp(g).toString(16).padStart(2, "0")}${clamp(b).toString(16).padStart(2, "0")}`;
65
- }
66
- function mix(fg, bg, percent) {
67
- const [fR, fG, fB] = parseHex(fg);
68
- const [bR, bG, bB] = parseHex(bg);
69
- const p = percent / 100;
70
- return toHex(
71
- fR * p + bR * (1 - p),
72
- fG * p + bG * (1 - p),
73
- fB * p + bB * (1 - p)
74
- );
75
- }
76
- function deriveTokens(bg, fg) {
77
- return {
78
- bg,
79
- fg,
80
- line: mix(fg, bg, MIX.line),
81
- arrow: mix(fg, bg, MIX.arrow),
82
- nodeFill: mix(fg, bg, MIX.nodeFill),
83
- nodeStroke: mix(fg, bg, MIX.nodeStroke),
84
- groupHeader: mix(fg, bg, MIX.groupHeader),
85
- innerStroke: mix(fg, bg, MIX.innerStroke),
86
- textSec: mix(fg, bg, MIX.textSec),
87
- textMuted: mix(fg, bg, MIX.textMuted),
88
- textFaint: mix(fg, bg, MIX.textFaint)
89
- };
90
- }
91
- function buildTheme(bg, fg) {
92
- const t = deriveTokens(bg, fg);
93
- return {
94
- theme: "base",
95
- themeVariables: {
96
- background: t.bg,
97
- fontFamily: "system-ui, -apple-system, sans-serif",
98
- fontSize: "14px",
99
- primaryColor: t.nodeFill,
100
- primaryTextColor: t.fg,
101
- primaryBorderColor: t.nodeStroke,
102
- secondaryColor: t.nodeFill,
103
- secondaryTextColor: t.fg,
104
- secondaryBorderColor: t.innerStroke,
105
- tertiaryColor: t.groupHeader,
106
- tertiaryTextColor: t.fg,
107
- tertiaryBorderColor: t.innerStroke,
108
- noteBkgColor: t.nodeFill,
109
- noteTextColor: t.fg,
110
- noteBorderColor: t.innerStroke,
111
- lineColor: t.line,
112
- textColor: t.fg,
113
- mainBkg: t.nodeFill,
114
- nodeBorder: t.nodeStroke,
115
- nodeTextColor: t.fg,
116
- clusterBkg: t.bg,
117
- clusterBorder: t.nodeStroke,
118
- titleColor: t.fg,
119
- labelColor: t.fg,
120
- altBackground: t.nodeFill,
121
- fillType0: t.nodeFill,
122
- fillType1: t.groupHeader,
123
- fillType2: mix(fg, bg, 7),
124
- fillType3: t.innerStroke,
125
- fillType4: t.nodeStroke,
126
- fillType5: t.groupHeader,
127
- fillType6: t.nodeFill,
128
- fillType7: mix(fg, bg, 7),
129
- // Sequence diagram
130
- actorBkg: t.nodeFill,
131
- actorBorder: t.nodeStroke,
132
- actorTextColor: t.fg,
133
- actorLineColor: t.line,
134
- signalColor: t.fg,
135
- signalTextColor: t.fg,
136
- labelBoxBkgColor: t.nodeFill,
137
- labelBoxBorderColor: t.nodeStroke,
138
- labelTextColor: t.fg,
139
- loopTextColor: t.fg,
140
- activationBorderColor: t.line,
141
- activationBkgColor: t.innerStroke,
142
- sequenceNumberColor: t.bg,
143
- // Git graph
144
- git0: t.fg,
145
- git1: t.line,
146
- git2: t.textMuted,
147
- git3: t.textFaint,
148
- git4: t.nodeStroke,
149
- git5: t.innerStroke,
150
- git6: t.groupHeader,
151
- git7: t.nodeFill,
152
- gitBranchLabel0: t.bg,
153
- gitBranchLabel1: t.bg,
154
- gitBranchLabel2: t.bg,
155
- gitBranchLabel3: t.fg,
156
- gitInv0: t.bg,
157
- // Pie chart
158
- pie1: t.fg,
159
- pie2: mix(fg, bg, 80),
160
- pie3: t.line,
161
- pie4: t.textMuted,
162
- pie5: t.nodeStroke,
163
- pie6: t.innerStroke,
164
- pie7: mix(fg, bg, 7),
165
- pie8: t.groupHeader,
166
- pie9: t.nodeFill,
167
- pie10: t.bg,
168
- pie11: t.textSec,
169
- pie12: t.arrow,
170
- pieTitleTextColor: t.fg,
171
- pieSectionTextColor: t.bg,
172
- pieLegendTextColor: t.fg,
173
- pieLegendTextSize: "14px",
174
- pieStrokeColor: t.bg,
175
- pieStrokeWidth: "2px",
176
- classText: t.fg,
177
- // Gantt / timeline
178
- sectionBkgColor: t.nodeFill,
179
- altSectionBkgColor: t.groupHeader,
180
- sectionBkgColor2: mix(fg, bg, 7),
181
- taskBkgColor: t.innerStroke,
182
- taskTextColor: t.fg,
183
- taskTextLightColor: t.fg,
184
- taskTextOutsideColor: t.fg,
185
- activeTaskBkgColor: t.nodeStroke,
186
- activeTaskBorderColor: t.line,
187
- gridColor: t.innerStroke,
188
- doneTaskBkgColor: t.nodeStroke,
189
- doneTaskBorderColor: t.line,
190
- critBkgColor: t.fg,
191
- critBorderColor: t.fg,
192
- todayLineColor: t.fg,
193
- // Requirement diagram
194
- requirementBackground: t.nodeFill,
195
- requirementBorderColor: t.nodeStroke,
196
- requirementBorderSize: "1px",
197
- requirementTextColor: t.fg,
198
- relationColor: t.line,
199
- relationLabelBackground: t.bg,
200
- relationLabelColor: t.fg,
201
- edgeLabelBackground: t.bg,
202
- // Color scales
203
- cScale0: t.nodeFill,
204
- cScale1: t.innerStroke,
205
- cScale2: t.nodeStroke,
206
- cScale3: t.textMuted,
207
- cScale4: t.line,
208
- cScale5: t.textSec,
209
- cScale6: mix(fg, bg, 70),
210
- cScale7: mix(fg, bg, 80),
211
- cScale8: t.arrow,
212
- cScale9: t.fg,
213
- cScaleLabel0: t.fg,
214
- cScaleLabel2: t.fg
215
- }
216
- };
217
- }
218
- const lightTheme = buildTheme("#ffffff", "#27272a");
219
- const darkTheme = buildTheme("#18181b", "#fafafa");
220
- const lightTokens = deriveTokens("#ffffff", "#27272a");
221
- const darkTokens = deriveTokens("#18181b", "#fafafa");
222
- const CORNER_RADIUS = {
223
- node: 5,
224
- classNode: 4,
225
- entity: 5,
226
- subgraphOuter: 7,
227
- actor: 6,
228
- activation: 4
229
- };
230
- function applyCornerRounding(doc) {
231
- const applyRadius = (selector, radius) => {
232
- const rects = doc.querySelectorAll(selector);
233
- for (const rect of rects) {
234
- rect.setAttribute("rx", String(radius));
235
- rect.setAttribute("ry", String(radius));
236
- }
237
- };
238
- applyRadius(".node > rect", CORNER_RADIUS.node);
239
- applyRadius(".node > .basic", CORNER_RADIUS.node);
240
- applyRadius(".classGroup > rect", CORNER_RADIUS.classNode);
241
- applyRadius(".er > rect", CORNER_RADIUS.entity);
242
- applyRadius(".cluster > rect", CORNER_RADIUS.subgraphOuter);
243
- applyRadius(".actor", CORNER_RADIUS.actor);
244
- applyRadius(
245
- ".activation0, .activation1, .activation2",
246
- CORNER_RADIUS.activation
247
- );
248
- }
249
- function buildVisualCss(tokens) {
250
- return `
251
- /* beautiful-mermaid inspired visual overrides */
252
-
253
- /* Edge styling: rounded linecaps for clean connections */
254
- .edgePath path.path,
255
- .flowchart-link,
256
- line[class*="messageLine"] {
257
- stroke-linecap: round !important;
258
- stroke-linejoin: round !important;
259
- }
260
-
261
- /* Cluster/subgraph "soft" styling */
262
- .cluster > rect {
263
- fill: ${tokens.nodeFill} !important;
264
- fill-opacity: 0.96 !important;
265
- stroke: ${tokens.nodeStroke} !important;
266
- stroke-width: 1px !important;
267
- }
268
-
269
- /* Cluster title */
270
- .cluster text,
271
- .cluster .nodeLabel {
272
- fill: ${tokens.textSec} !important;
273
- }
274
-
275
- /* Node stroke consistency */
276
- .node rect,
277
- .node circle,
278
- .node ellipse,
279
- .node polygon,
280
- .node .basic {
281
- stroke: ${tokens.nodeStroke} !important;
282
- stroke-width: 1px !important;
283
- }
284
-
285
- /* Node fill */
286
- .node rect,
287
- .node .basic {
288
- fill: ${tokens.nodeFill} !important;
289
- }
290
-
291
- /* Edge label "subtle" styling */
292
- .edgeLabel rect,
293
- .labelBkg {
294
- rx: 5 !important;
295
- ry: 5 !important;
296
- fill: ${tokens.bg} !important;
297
- stroke: ${tokens.innerStroke} !important;
298
- stroke-width: 1px !important;
299
- }
300
-
301
- .edgeLabel text,
302
- .edgeLabel .edgeLabel {
303
- fill: ${tokens.textMuted} !important;
304
- }
305
-
306
- /* Arrow heads */
307
- marker path {
308
- fill: ${tokens.arrow} !important;
309
- stroke: ${tokens.arrow} !important;
310
- }
311
-
312
- /* Edge paths */
313
- .edgePath path.path {
314
- stroke: ${tokens.line} !important;
315
- stroke-width: 1px !important;
316
- }
317
-
318
- /* Sequence diagram refinements */
319
- .actor {
320
- fill: ${tokens.nodeFill} !important;
321
- stroke: ${tokens.nodeStroke} !important;
322
- stroke-width: 1px !important;
323
- }
324
-
325
- .messageLine0,
326
- .messageLine1 {
327
- stroke: ${tokens.line} !important;
328
- stroke-width: 1px !important;
329
- }
330
-
331
- .messageText {
332
- fill: ${tokens.fg} !important;
333
- }
334
-
335
- .loopLine {
336
- stroke: ${tokens.innerStroke} !important;
337
- stroke-width: 1px !important;
338
- }
339
-
340
- .labelBox {
341
- fill: ${tokens.nodeFill} !important;
342
- stroke: ${tokens.nodeStroke} !important;
343
- stroke-width: 1px !important;
344
- }
345
-
346
- .loopText tspan,
347
- .loopText {
348
- fill: ${tokens.textSec} !important;
349
- }
350
-
351
- /* Activation bars */
352
- .activation0,
353
- .activation1,
354
- .activation2 {
355
- fill: ${tokens.innerStroke} !important;
356
- stroke: ${tokens.line} !important;
357
- }
358
-
359
- /* Note styling */
360
- .note {
361
- fill: ${tokens.nodeFill} !important;
362
- stroke: ${tokens.innerStroke} !important;
363
- }
364
-
365
- .noteText {
366
- fill: ${tokens.fg} !important;
367
- }
368
-
369
- /* Inner divider lines */
370
- .divider {
371
- stroke: ${tokens.innerStroke} !important;
372
- stroke-width: 0.75px !important;
373
- }
374
-
375
- /* Class diagram */
376
- .classGroup rect {
377
- fill: ${tokens.nodeFill} !important;
378
- stroke: ${tokens.nodeStroke} !important;
379
- stroke-width: 1px !important;
380
- }
381
-
382
- .classGroup line {
383
- stroke: ${tokens.innerStroke} !important;
384
- stroke-width: 0.75px !important;
385
- }
386
-
387
- .classGroup text {
388
- fill: ${tokens.fg} !important;
389
- }
390
-
391
- .classLabel .box {
392
- fill: ${tokens.groupHeader} !important;
393
- stroke: ${tokens.nodeStroke} !important;
394
- }
395
-
396
- .relation {
397
- stroke: ${tokens.line} !important;
398
- stroke-width: 1px !important;
399
- }
400
-
401
- /* State diagram */
402
- .stateGroup rect,
403
- .statediagram-state rect {
404
- fill: ${tokens.nodeFill} !important;
405
- stroke: ${tokens.nodeStroke} !important;
406
- stroke-width: 1px !important;
407
- rx: 5 !important;
408
- ry: 5 !important;
409
- }
410
-
411
- .stateGroup text {
412
- fill: ${tokens.fg} !important;
413
- }
414
-
415
- /* Git graph */
416
- .commit-id text {
417
- fill: ${tokens.textMuted} !important;
418
- }
419
-
420
- `;
421
- }
422
- function postProcessMermaidSvg(svg, tokens) {
423
- const parser = new DOMParser();
424
- const doc = parser.parseFromString(svg, "image/svg+xml");
425
- const parseError = doc.querySelector("parsererror");
426
- if (parseError) return svg;
427
- const root = doc.documentElement;
428
- if (!root || root.tagName.toLowerCase() !== "svg") return svg;
429
- applyCornerRounding(doc);
430
- const existingOverride = root.querySelector("style[data-visual-overrides]");
431
- existingOverride?.remove();
432
- const styleEl = doc.createElementNS("http://www.w3.org/2000/svg", "style");
433
- styleEl.dataset.visualOverrides = "";
434
- styleEl.textContent = buildVisualCss(tokens);
435
- if (root.firstChild) {
436
- root.insertBefore(styleEl, root.firstChild);
437
- } else {
438
- root.append(styleEl);
439
- }
440
- return new XMLSerializer().serializeToString(root);
441
- }
442
- function useMermaidRender(content, preferredColorScheme) {
443
- const [loading, setLoading] = useState(true);
444
- const [error, setError] = useState("");
445
- const [svg, setSvg] = useState("");
446
- const [width, setWidth] = useState();
447
- const [height, setHeight] = useState();
448
- const colorScheme = useColorScheme();
449
- const effectiveColorScheme = preferredColorScheme ?? colorScheme;
450
- const id = useId().split(":").join("");
451
- useEffect(() => {
452
- if (!content) return;
453
- setError("");
454
- setLoading(true);
455
- let cancelled = false;
456
- import("mermaid").then(async (mo) => {
457
- const mermaid = mo.default;
458
- const themeConfig = effectiveColorScheme === "dark" ? darkTheme : lightTheme;
459
- const tokens = effectiveColorScheme === "dark" ? darkTokens : lightTokens;
460
- mermaid.initialize({
461
- startOnLoad: false,
462
- theme: themeConfig.theme,
463
- themeVariables: themeConfig.themeVariables,
464
- darkMode: effectiveColorScheme === "dark",
465
- flowchart: {
466
- htmlLabels: true,
467
- curve: "basis",
468
- padding: 20,
469
- nodeSpacing: 24,
470
- rankSpacing: 48
471
- },
472
- sequence: {
473
- actorMargin: 80,
474
- messageMargin: 40
475
- },
476
- gantt: {
477
- titleTopMargin: 16,
478
- barHeight: 24,
479
- barGap: 6
480
- }
481
- });
482
- let result;
483
- try {
484
- result = await mermaid.render(`mermaid-${id}`, content);
485
- } catch (err) {
486
- document.getElementById(`dmermaid-${id}`)?.remove();
487
- if (err instanceof Error) {
488
- setError(err.message);
489
- }
490
- setSvg("");
491
- setWidth(void 0);
492
- setHeight(void 0);
493
- }
494
- if (cancelled) return;
495
- if (result) {
496
- const processedSvg = postProcessMermaidSvg(result.svg, tokens);
497
- setSvg(processedSvg);
498
- const match = processedSvg.match(/viewBox="[^"]*\s([\d.]+)\s([\d.]+)"/);
499
- if (match?.[1] && match?.[2]) {
500
- setWidth(Number.parseInt(match[1]));
501
- setHeight(Number.parseInt(match[2]));
502
- }
503
- setError("");
504
- }
505
- setLoading(false);
506
- });
507
- return () => {
508
- cancelled = true;
509
- };
510
- }, [id, content, effectiveColorScheme]);
511
- let imgSrc = "";
512
- if (svg) {
513
- const encoder = new TextEncoder();
514
- const data = encoder.encode(svg);
515
- imgSrc = `data:image/svg+xml;base64,${btoa(String.fromCodePoint(...new Uint8Array(data)))}`;
516
- }
517
- return { loading, error, imgSrc, svg, width, height };
518
- }
7
+ import { u as useMermaidRender, e as editorPopup, m as mermaidLoading, a as mermaidError, b as mermaidContainer, c as mermaidEditHint, d as editorHeader, f as editorHeaderLeft, g as editorTitle, h as editorSep, i as editorTplBtn, j as editorHeaderRight, k as editorViewToggle, l as editorIconBtn, n as editorBody, o as editorPane, p as editorPaneFull, q as editorPaneHalf, r as editorPaneLabel, s as editorPreviewPane, t as editorFooter, v as footerActions, w as footerBtnCancel, x as footerBtnSave, z as zoomControls, y as zoomBtn, A as editorViewItem, B as editorViewItemActive, C as codeEditor, D as codeGutter, E as codeGutterLine, F as codeArea, G as editorPreviewWrap, H as editorPreviewEmpty, I as editorPreviewErrorWrap, J as editorPreviewErrorIcon, K as editorPreviewErrorTitle, L as editorPreviewErrorMsg } from "./MermaidRenderer-CuJfxtJI.js";
8
+ import { M } from "./MermaidRenderer-CuJfxtJI.js";
519
9
  const TEMPLATES = [
520
10
  {
521
11
  label: "Flowchart",
@@ -864,17 +354,7 @@ const MermaidEditRenderer = ({
864
354
  ] })
865
355
  ] });
866
356
  };
867
- const MermaidRenderer = ({ content }) => {
868
- const { loading, error, imgSrc, width, height } = useMermaidRender(content);
869
- if (loading) {
870
- return /* @__PURE__ */ jsx("pre", { className: mermaidLoading, children: /* @__PURE__ */ jsx("code", { children: content }) });
871
- }
872
- if (!imgSrc) {
873
- return /* @__PURE__ */ jsx("div", { className: mermaidError, children: error || "Render failed" });
874
- }
875
- return /* @__PURE__ */ jsx("div", { className: mermaidContainer, style: { cursor: "default" }, children: /* @__PURE__ */ jsx("img", { src: imgSrc, alt: "Mermaid diagram", width, height }) });
876
- };
877
357
  export {
878
358
  MermaidEditRenderer,
879
- MermaidRenderer
359
+ M as MermaidRenderer
880
360
  };
@@ -0,0 +1,4 @@
1
+ import { M } from "./MermaidRenderer-CuJfxtJI.js";
2
+ export {
3
+ M as MermaidRenderer
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-renderer-mermaid",
3
- "version": "0.0.40",
3
+ "version": "0.0.42",
4
4
  "description": "Mermaid diagram renderer",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,9 +23,9 @@
23
23
  "lucide-react": "^0.574.0",
24
24
  "mermaid": "^11.12.3",
25
25
  "react-zoom-pan-pinch": "^3.7.0",
26
- "@haklex/rich-editor": "0.0.40",
27
- "@haklex/rich-editor-ui": "0.0.40",
28
- "@haklex/rich-style-token": "0.0.40"
26
+ "@haklex/rich-style-token": "0.0.42",
27
+ "@haklex/rich-editor-ui": "0.0.42",
28
+ "@haklex/rich-editor": "0.0.42"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/react": "^19.2.14",