react-super-mermaid 0.1.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -106,6 +106,7 @@ var RSM_CSS = `
106
106
  --rsm-hover: #f3f4f6;
107
107
  --rsm-surface: #ffffff;
108
108
  --rsm-canvas-bg: transparent;
109
+ --rsm-grid-dot: rgba(0, 0, 0, 0.08);
109
110
  --rsm-radius: 8px;
110
111
  display: flex;
111
112
  flex-direction: column;
@@ -243,7 +244,91 @@ var RSM_CSS = `
243
244
  --rsm-accent: #60a5fa;
244
245
  --rsm-hover: #1f2937;
245
246
  --rsm-surface: #111827;
247
+ --rsm-grid-dot: rgba(255, 255, 255, 0.10);
246
248
  }
249
+
250
+ /* \u2500\u2500 \u80CC\u666F\u6A21\u5F0F \u2500\u2500 \u900F\u660E(\u9810\u8A2D,\u8DDF\u96A8\u9801\u9762) / \u7D14\u8272(surface) / \u9EDE\u9663\u683C\u7DDA\u3002 */
251
+ .rsm-root.rsm-bg-solid .rsm-canvas { background: var(--rsm-surface); }
252
+ .rsm-root.rsm-bg-grid .rsm-canvas {
253
+ background-color: var(--rsm-surface);
254
+ background-image: radial-gradient(var(--rsm-grid-dot) 1px, transparent 1px);
255
+ background-size: 18px 18px;
256
+ background-position: -9px -9px;
257
+ }
258
+
259
+ /* \u2500\u2500 \u5168\u87A2\u5E55\u8DF3\u7A97 \u2500\u2500 position:fixed \u8986\u84CB\u6574\u500B\u8996\u7A97,RWD \u53CB\u5584\u3002 */
260
+ .rsm-root.rsm-fullscreen {
261
+ position: fixed;
262
+ inset: 0;
263
+ width: 100vw;
264
+ width: 100dvw;
265
+ height: 100vh;
266
+ height: 100dvh;
267
+ max-width: 100vw;
268
+ max-height: 100dvh;
269
+ margin: 0;
270
+ z-index: 2147483000;
271
+ border: 0;
272
+ border-radius: 0;
273
+ animation: rsm-fs-in 0.16s ease-out;
274
+ }
275
+ @keyframes rsm-fs-in {
276
+ from { opacity: 0; }
277
+ to { opacity: 1; }
278
+ }
279
+
280
+ /* \u5168\u87A2\u5E55\u53F3\u4E0A\u89D2\u7684\u96E2\u958B\u9215(toolbar \u96B1\u85CF\u6642\u4E5F\u80FD\u95DC\u9589)\u3002 */
281
+ .rsm-fs-close {
282
+ position: absolute;
283
+ top: 10px;
284
+ right: 10px;
285
+ z-index: 5;
286
+ display: inline-flex;
287
+ align-items: center;
288
+ justify-content: center;
289
+ width: 34px;
290
+ height: 34px;
291
+ padding: 0;
292
+ font-size: 16px;
293
+ line-height: 1;
294
+ border: 1px solid var(--rsm-border);
295
+ border-radius: 8px;
296
+ background: var(--rsm-surface);
297
+ color: var(--rsm-fg);
298
+ cursor: pointer;
299
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
300
+ transition: background 0.12s ease, color 0.12s ease;
301
+ }
302
+ .rsm-fs-close:hover { background: var(--rsm-hover); }
303
+
304
+ /* \u2500\u2500 RWD \u2500\u2500 \u5C0F\u87A2\u5E55\u6536\u7DCA toolbar\u3001\u7E2E\u77ED\u641C\u5C0B\u6846,\u907F\u514D\u63DB\u884C\u64E0\u58D3\u756B\u5E03\u3002 */
305
+ @media (max-width: 640px) {
306
+ .rsm-toolbar { gap: 6px; padding: 6px 8px; }
307
+ .rsm-btn { padding: 4px 8px; font-size: 12px; }
308
+ .rsm-label { font-size: 11px; }
309
+ .rsm-select { padding: 3px 6px; font-size: 12px; }
310
+ .rsm-zoom > button { padding: 4px 8px; font-size: 12px; }
311
+ .rsm-input { flex-basis: 150px; }
312
+ }
313
+
314
+ /* \u2500\u2500 \u6A19\u7C64\u5B57\u91CD(\u91CF\u6E2C\u968E\u6BB5\u5C31\u751F\u6548)\u2500\u2500
315
+ * boostLegibility \u5728\u300C\u6E32\u67D3\u5F8C\u300D\u624D\u628A\u6A19\u7C64\u5B57\u91CD\u52A0\u5230 600/700,\u4F46 mermaid \u662F\u5728\u300C\u6E32\u67D3\u4E2D\u300D
316
+ * \u91CF\u6E2C\u6587\u5B57\u5BEC\u5EA6\u4F86\u6C7A\u5B9A foreignObject / \u7BC0\u9EDE\u5916\u6846\u7684\u5927\u5C0F\u3002\u82E5\u53EA\u5728\u4E8B\u5F8C\u52A0\u7C97,\u7C97\u9AD4\u5B57\u6703\u6BD4\u5DF2\u91CF\u597D\u7684
317
+ * \u6846\u66F4\u5BEC \u2192 foreignObject \u628A\u5C3E\u5B57\u88C1\u6389(\u5FC3\u667A\u5716\u7BC0\u9EDE\u300Creact-super-mermaid\u300D\u5C3E\u5DF4\u7684 d \u4E0D\u898B\u5C31\u662F\u9019\u500B)\u3002
318
+ * \u89E3\u6CD5:\u628A\u540C\u6A23\u7684\u5B57\u91CD\u7528 CSS \u63D0\u524D\u5BA3\u544A,\u4E14\u300C\u4E0D\u300Dscope \u5728 .rsm-root \u4E4B\u4E0B,\u800C\u662F scope \u5728 mermaid
319
+ * \u7684\u6E32\u67D3 id(svg[id^="rsm-"])\u2014\u2014\u56E0\u70BA\u91CF\u6E2C\u6642\u90A3\u9846\u66AB\u6642\u7684 svg \u9084\u5728 <body> \u4E0B\u3001\u5C1A\u672A\u639B\u9032 .rsm-root\u3002
320
+ * \u9019\u6A23 mermaid \u91CF\u5230\u7684\u5C31\u662F\u7C97\u9AD4\u5BEC\u5EA6,\u6846\u6703\u525B\u597D\u5BB9\u7D0D,\u4E8B\u5F8C boostLegibility \u8A2D\u540C\u503C\u4E0D\u518D\u6490\u7834\u3002
321
+ * \u53EA scope \u6211\u5011\u81EA\u5DF1\u6E32\u67D3\u51FA\u7684 svg,\u6545\u4E0D\u6703\u6C61\u67D3 host \u9801\u9762\u5176\u5B83 mermaid\u3002 */
322
+ svg[id^="rsm-"] g.node text,
323
+ svg[id^="rsm-"] g.node tspan,
324
+ svg[id^="rsm-"] g.node .nodeLabel,
325
+ svg[id^="rsm-"] g.mindmap-node text,
326
+ svg[id^="rsm-"] g.mindmap-node .nodeLabel,
327
+ svg[id^="rsm-"] g[class*="timeline-node"] text,
328
+ svg[id^="rsm-"] text.actor { font-weight: 600 !important; }
329
+ svg[id^="rsm-"] .cluster-label text,
330
+ svg[id^="rsm-"] .cluster-label .nodeLabel,
331
+ svg[id^="rsm-"] text.pieTitleText { font-weight: 700 !important; }
247
332
  `;
248
333
 
249
334
  // src/core/ensure-styles.ts
@@ -280,16 +365,88 @@ var NODE_PALETTE = [
280
365
  // violet
281
366
  ];
282
367
  var CLUSTER_PALETTE = [
283
- { fill: "rgba(59, 130, 246, 0.07)", stroke: "#93C5FD" },
284
- { fill: "rgba(34, 197, 94, 0.07)", stroke: "#86EFAC" },
285
- { fill: "rgba(249, 115, 22, 0.07)", stroke: "#FDBA74" },
286
- { fill: "rgba(168, 85, 247, 0.07)", stroke: "#D8B4FE" },
287
- { fill: "rgba(6, 182, 212, 0.07)", stroke: "#67E8F9" },
288
- { fill: "rgba(239, 68, 68, 0.07)", stroke: "#FCA5A5" }
368
+ { fill: "rgba(59, 130, 246, 0.16)", stroke: "#3B82F6" },
369
+ // blue
370
+ { fill: "rgba(34, 197, 94, 0.16)", stroke: "#22C55E" },
371
+ // green
372
+ { fill: "rgba(249, 115, 22, 0.16)", stroke: "#F97316" },
373
+ // orange
374
+ { fill: "rgba(168, 85, 247, 0.16)", stroke: "#A855F7" },
375
+ // purple
376
+ { fill: "rgba(239, 68, 68, 0.16)", stroke: "#EF4444" },
377
+ // red
378
+ { fill: "rgba(6, 182, 212, 0.16)", stroke: "#06B6D4" },
379
+ // cyan
380
+ { fill: "rgba(234, 179, 8, 0.16)", stroke: "#EAB308" },
381
+ // yellow
382
+ { fill: "rgba(139, 92, 246, 0.16)", stroke: "#8B5CF6" }
383
+ // violet
384
+ ];
385
+ var PIE_PALETTE = [
386
+ "#3B82F6",
387
+ // blue
388
+ "#22C55E",
389
+ // green
390
+ "#F59E0B",
391
+ // amber
392
+ "#A855F7",
393
+ // purple
394
+ "#EF4444",
395
+ // red
396
+ "#06B6D4",
397
+ // cyan
398
+ "#EC4899",
399
+ // pink
400
+ "#84CC16",
401
+ // lime
402
+ "#F97316",
403
+ // orange
404
+ "#14B8A6",
405
+ // teal
406
+ "#6366F1",
407
+ // indigo
408
+ "#EAB308"
409
+ // yellow
289
410
  ];
290
411
  var NODE_TEXT = "#1F2937";
291
412
  var SHADOW_FILTER_ID = "rsm-soft-shadow";
292
413
  var SVG_NS = "http://www.w3.org/2000/svg";
414
+ function resolveSvg(root) {
415
+ if (root instanceof Element && root.tagName.toLowerCase() === "svg") {
416
+ return root;
417
+ }
418
+ return root.querySelector("svg");
419
+ }
420
+ function canonColor(input) {
421
+ const s = (input || "").trim();
422
+ const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(s);
423
+ if (hex) {
424
+ let h = hex[1];
425
+ if (h.length === 3) {
426
+ h = h.split("").map((c) => c + c).join("");
427
+ }
428
+ const n = parseInt(h, 16);
429
+ return `${n >> 16 & 255},${n >> 8 & 255},${n & 255}`;
430
+ }
431
+ const rgb = /rgba?\(([^)]+)\)/i.exec(s);
432
+ if (rgb) {
433
+ const p = rgb[1].split(",").map((x) => Math.round(parseFloat(x)));
434
+ return `${p[0]},${p[1]},${p[2]}`;
435
+ }
436
+ return s.toLowerCase();
437
+ }
438
+ function readableTextOn(color) {
439
+ const m = /^#?([0-9a-f]{6})$/i.exec(color.trim());
440
+ if (!m) {
441
+ return "#FFFFFF";
442
+ }
443
+ const n = parseInt(m[1], 16);
444
+ const r = n >> 16 & 255;
445
+ const g = n >> 8 & 255;
446
+ const b = n & 255;
447
+ const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
448
+ return lum > 0.62 ? "#1F2937" : "#FFFFFF";
449
+ }
293
450
  function ensureShadowFilter(svg) {
294
451
  if (svg.querySelector(`#${SHADOW_FILTER_ID}`)) {
295
452
  return;
@@ -395,6 +552,17 @@ function styleEdgeLabels(svg) {
395
552
  rect.setAttribute("ry", "4");
396
553
  }
397
554
  }
555
+ function styleLabelText(svg, dark) {
556
+ const color = dark ? "#E2E8F0" : NODE_TEXT;
557
+ for (const t of Array.from(
558
+ svg.querySelectorAll("text.messageText, .edgeLabel text, .edgeLabel tspan")
559
+ )) {
560
+ t.style.fill = color;
561
+ }
562
+ for (const t of Array.from(svg.querySelectorAll(".edgeLabel span, .edgeLabel p"))) {
563
+ t.style.color = color;
564
+ }
565
+ }
398
566
  function colorizeLegacyEr(svg) {
399
567
  const erGroups = [];
400
568
  for (const rect of Array.from(svg.querySelectorAll("rect.er.entityBox"))) {
@@ -506,12 +674,194 @@ function colorizeSequence(svg, dark) {
506
674
  text.style.fill = dark ? "#E2E8F0" : NODE_TEXT;
507
675
  }
508
676
  }
677
+ function stylePie(svg, dark) {
678
+ const slices = Array.from(svg.querySelectorAll("path.pieCircle"));
679
+ const swatches = Array.from(svg.querySelectorAll("g.legend rect"));
680
+ if (slices.length === 0 && swatches.length === 0) {
681
+ return;
682
+ }
683
+ const remap = /* @__PURE__ */ new Map();
684
+ let next = 0;
685
+ const newColorFor = (old) => {
686
+ const key = canonColor(old) || `#slot-${next}`;
687
+ let c = remap.get(key);
688
+ if (!c) {
689
+ c = PIE_PALETTE[next % PIE_PALETTE.length];
690
+ remap.set(key, c);
691
+ next += 1;
692
+ }
693
+ return c;
694
+ };
695
+ for (const slice of slices) {
696
+ const old = slice.style.fill || slice.getAttribute("fill") || "";
697
+ const c = newColorFor(old);
698
+ slice.style.fill = c;
699
+ slice.style.opacity = "1";
700
+ slice.style.stroke = dark ? "#0F172A" : "#FFFFFF";
701
+ slice.style.strokeWidth = "2px";
702
+ slice.style.strokeLinejoin = "round";
703
+ }
704
+ for (const sw of swatches) {
705
+ const old = sw.style.fill || sw.getAttribute("fill") || "";
706
+ const c = newColorFor(old);
707
+ sw.style.fill = c;
708
+ sw.style.stroke = c;
709
+ sw.setAttribute("rx", "3");
710
+ sw.setAttribute("ry", "3");
711
+ }
712
+ Array.from(svg.querySelectorAll("text.slice")).forEach((label, i) => {
713
+ const slice = slices[i];
714
+ const c = slice ? slice.style.fill || PIE_PALETTE[0] : PIE_PALETTE[0];
715
+ label.style.fill = readableTextOn(c);
716
+ label.style.fontWeight = "600";
717
+ });
718
+ for (const title of Array.from(svg.querySelectorAll("text.pieTitleText"))) {
719
+ title.style.fontWeight = "700";
720
+ title.style.fill = dark ? "#E2E8F0" : "#1F2937";
721
+ }
722
+ for (const t of Array.from(svg.querySelectorAll("g.legend text"))) {
723
+ t.style.fill = dark ? "#E2E8F0" : "#1F2937";
724
+ }
725
+ for (const oc of Array.from(svg.querySelectorAll("circle.pieOuterCircle"))) {
726
+ oc.style.stroke = dark ? "#334155" : "#CBD5E1";
727
+ }
728
+ }
729
+ function styleGantt(svg, dark) {
730
+ const tasks = Array.from(svg.querySelectorAll("rect.task"));
731
+ if (tasks.length === 0) {
732
+ return;
733
+ }
734
+ for (const task of tasks) {
735
+ const cls = task.getAttribute("class") ?? "";
736
+ if (/\b(done|active|crit|milestone)\d*\b/.test(cls)) {
737
+ continue;
738
+ }
739
+ const m = cls.match(/task(\d+)/);
740
+ if (!m) {
741
+ continue;
742
+ }
743
+ const entry = NODE_PALETTE[Number(m[1]) % NODE_PALETTE.length];
744
+ task.style.fill = entry.fill;
745
+ task.style.stroke = entry.stroke;
746
+ task.setAttribute("rx", "4");
747
+ task.setAttribute("ry", "4");
748
+ }
749
+ Array.from(svg.querySelectorAll("rect.section")).forEach((band) => {
750
+ const m = (band.getAttribute("class") ?? "").match(/section(\d+)/);
751
+ if (m) {
752
+ band.style.fill = CLUSTER_PALETTE[Number(m[1]) % CLUSTER_PALETTE.length].fill;
753
+ }
754
+ });
755
+ for (const inBar of Array.from(svg.querySelectorAll("text.taskText"))) {
756
+ if (!/Outside/.test(inBar.getAttribute("class") ?? "")) {
757
+ inBar.style.fill = NODE_TEXT;
758
+ }
759
+ }
760
+ for (const tick of Array.from(svg.querySelectorAll("g.grid g.tick line"))) {
761
+ tick.style.stroke = dark ? "#334155" : "#E2E8F0";
762
+ }
763
+ }
764
+ function styleTimeline(svg) {
765
+ const nodes = Array.from(svg.querySelectorAll('g[class*="timeline-node"]'));
766
+ nodes.forEach((node, i) => {
767
+ const m = (node.getAttribute("class") ?? "").match(/section-(-?\d+)/);
768
+ const section = m ? Number(m[1]) : i;
769
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
770
+ const backgrounds = Array.from(node.querySelectorAll(".node-bkg"));
771
+ if (backgrounds.length > 0) {
772
+ for (const bkg of backgrounds) {
773
+ bkg.style.fill = entry.fill;
774
+ bkg.style.stroke = entry.stroke;
775
+ bkg.style.strokeWidth = "1.4px";
776
+ }
777
+ } else {
778
+ paintShapes(node, entry);
779
+ }
780
+ darkenNodeText(node);
781
+ });
782
+ }
783
+ function styleMindmap(svg) {
784
+ const nodes = Array.from(svg.querySelectorAll("g.mindmap-node"));
785
+ if (nodes.length === 0) {
786
+ return;
787
+ }
788
+ for (const node of nodes) {
789
+ const m = (node.getAttribute("class") ?? "").match(/section-(-?\d+)/);
790
+ const section = m ? Number(m[1]) : 0;
791
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
792
+ for (const shape of Array.from(
793
+ node.querySelectorAll("path, rect, circle, ellipse")
794
+ )) {
795
+ if (shape.closest("g.children")) {
796
+ continue;
797
+ }
798
+ shape.style.fill = entry.fill;
799
+ shape.style.stroke = entry.stroke;
800
+ shape.style.strokeWidth = "1.4px";
801
+ }
802
+ darkenNodeText(node);
803
+ }
804
+ for (const edge of Array.from(svg.querySelectorAll('path[class*="edge"]'))) {
805
+ const m = (edge.getAttribute("class") ?? "").match(/section-edge-(-?\d+)/);
806
+ if (m) {
807
+ const section = Number(m[1]);
808
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
809
+ edge.style.stroke = entry.stroke;
810
+ edge.style.strokeWidth = "2px";
811
+ edge.style.opacity = "0.6";
812
+ edge.style.fill = "none";
813
+ }
814
+ }
815
+ }
816
+ function styleJourney(svg) {
817
+ const tasks = Array.from(
818
+ svg.querySelectorAll('circle[class*="task-type"], rect[class*="task-type"]')
819
+ );
820
+ tasks.forEach((shape) => {
821
+ const m = (shape.getAttribute("class") ?? "").match(/task-type-(\d+)/);
822
+ if (m) {
823
+ const entry = NODE_PALETTE[Number(m[1]) % NODE_PALETTE.length];
824
+ shape.style.fill = entry.fill;
825
+ shape.style.stroke = entry.stroke;
826
+ }
827
+ });
828
+ Array.from(svg.querySelectorAll('rect[class*="section-type"]')).forEach((rect) => {
829
+ const m = (rect.getAttribute("class") ?? "").match(/section-type-(\d+)/);
830
+ if (m) {
831
+ const entry = CLUSTER_PALETTE[Number(m[1]) % CLUSTER_PALETTE.length];
832
+ rect.style.fill = entry.fill;
833
+ rect.style.stroke = entry.stroke;
834
+ }
835
+ });
836
+ }
837
+ function boostLegibility(root) {
838
+ const svg = resolveSvg(root);
839
+ if (!svg) {
840
+ return;
841
+ }
842
+ for (const el of Array.from(
843
+ svg.querySelectorAll(
844
+ 'g.node text, g.node tspan, g.mindmap-node text, g[class*="timeline-node"] text, text.actor'
845
+ )
846
+ )) {
847
+ el.style.fontWeight = "600";
848
+ }
849
+ for (const el of Array.from(svg.querySelectorAll(".nodeLabel, g.node span, g.node p"))) {
850
+ el.style.fontWeight = "600";
851
+ }
852
+ for (const el of Array.from(svg.querySelectorAll("text"))) {
853
+ if (!el.style.fontWeight) {
854
+ el.style.fontWeight = "500";
855
+ }
856
+ }
857
+ }
509
858
  function colorizeDiagram(root, opts = {}) {
510
- const svg = root instanceof Element && root.tagName.toLowerCase() === "svg" ? root : root.querySelector("svg");
859
+ const svg = resolveSvg(root);
511
860
  if (!svg) {
512
861
  return;
513
862
  }
514
863
  ensureShadowFilter(svg);
864
+ const dark = opts.dark === true;
515
865
  Array.from(svg.querySelectorAll("g.node")).forEach((node, i) => {
516
866
  paintShapes(node, NODE_PALETTE[i % NODE_PALETTE.length]);
517
867
  darkenNodeText(node);
@@ -521,14 +871,41 @@ function colorizeDiagram(root, opts = {}) {
521
871
  for (const rect of Array.from(cluster.querySelectorAll(":scope > rect"))) {
522
872
  rect.style.fill = entry.fill;
523
873
  rect.style.stroke = entry.stroke;
524
- rect.style.strokeWidth = "1.2px";
874
+ rect.style.strokeWidth = "1.5px";
525
875
  roundRect(rect, 10);
526
876
  }
877
+ const label = cluster.querySelector(":scope > .cluster-label");
878
+ if (label) {
879
+ for (const el of Array.from(label.querySelectorAll("text, tspan"))) {
880
+ el.style.fill = entry.stroke;
881
+ el.style.fontWeight = "700";
882
+ }
883
+ for (const el of Array.from(label.querySelectorAll(".nodeLabel, span, p"))) {
884
+ el.style.color = entry.stroke;
885
+ el.style.fontWeight = "700";
886
+ }
887
+ for (const lr of Array.from(label.querySelectorAll("rect"))) {
888
+ lr.style.fill = entry.fill;
889
+ }
890
+ }
527
891
  });
528
892
  colorizeLegacyEr(svg);
529
- colorizeSequence(svg, opts.dark === true);
530
- styleEdges(svg, opts.dark === true);
893
+ colorizeSequence(svg, dark);
894
+ styleEdges(svg, dark);
531
895
  styleEdgeLabels(svg);
896
+ styleLabelText(svg, dark);
897
+ const kind = svg.getAttribute("aria-roledescription") ?? "";
898
+ if (kind === "pie" || kind === "pieChart") {
899
+ stylePie(svg, dark);
900
+ } else if (kind === "gantt") {
901
+ styleGantt(svg, dark);
902
+ } else if (kind === "timeline") {
903
+ styleTimeline(svg);
904
+ } else if (kind === "mindmap") {
905
+ styleMindmap(svg);
906
+ } else if (kind === "journey") {
907
+ styleJourney(svg);
908
+ }
532
909
  }
533
910
 
534
911
  // src/core/themes/sketch.ts
@@ -812,6 +1189,7 @@ function applyPostProcess(svg, postProcess, opts) {
812
1189
  } else if (postProcess === "sketch") {
813
1190
  sketchifyDiagram(svg, { dark: opts.dark, seed: opts.seed });
814
1191
  }
1192
+ boostLegibility(svg);
815
1193
  }
816
1194
  async function renderDiagram(opts) {
817
1195
  assertBrowser("renderDiagram");
@@ -1310,6 +1688,11 @@ function useMermaidViewer(opts) {
1310
1688
  getSvg
1311
1689
  };
1312
1690
  }
1691
+ var BACKGROUND_LABELS = {
1692
+ transparent: { icon: "\u25A6", label: "\u900F\u660E" },
1693
+ solid: { icon: "\u25FB", label: "\u7D14\u8272" },
1694
+ grid: { icon: "\u229E", label: "\u683C\u7DDA" }
1695
+ };
1313
1696
  var DEFAULT_THEME_OPTIONS = [
1314
1697
  { value: "colorful", label: "Colorful" },
1315
1698
  { value: "sketch", label: "Excalidraw" },
@@ -1333,6 +1716,20 @@ function Toolbar(props) {
1333
1716
  }
1334
1717
  )
1335
1718
  ] }),
1719
+ props.backgroundEnabled ? /* @__PURE__ */ jsxs(
1720
+ "button",
1721
+ {
1722
+ type: "button",
1723
+ className: "rsm-btn",
1724
+ onClick: props.onCycleBackground,
1725
+ title: "\u5207\u63DB\u756B\u5E03\u80CC\u666F\uFF08\u900F\u660E / \u7D14\u8272 / \u683C\u7DDA\uFF0CB\uFF09",
1726
+ children: [
1727
+ BACKGROUND_LABELS[props.background].icon,
1728
+ " \u80CC\u666F\uFF1A",
1729
+ BACKGROUND_LABELS[props.background].label
1730
+ ]
1731
+ }
1732
+ ) : null,
1336
1733
  /* @__PURE__ */ jsx("div", { className: "rsm-toolbar-spacer" }),
1337
1734
  props.searchEnabled ? /* @__PURE__ */ jsx(
1338
1735
  "button",
@@ -1386,9 +1783,21 @@ function Toolbar(props) {
1386
1783
  ),
1387
1784
  /* @__PURE__ */ jsx("button", { type: "button", onClick: props.onZoomIn, title: "\u653E\u5927\uFF08+\uFF09", children: "\uFF0B" }),
1388
1785
  /* @__PURE__ */ jsx("button", { type: "button", onClick: props.onReset, title: "\u7B26\u5408\u8996\u7A97\uFF080\uFF09", children: "\u2922" })
1389
- ] })
1786
+ ] }),
1787
+ props.fullscreenEnabled ? /* @__PURE__ */ jsx(
1788
+ "button",
1789
+ {
1790
+ type: "button",
1791
+ className: "rsm-btn rsm-btn-fullscreen",
1792
+ "aria-pressed": props.fullscreen ? "true" : "false",
1793
+ onClick: props.onToggleFullscreen,
1794
+ title: props.fullscreen ? "\u96E2\u958B\u5168\u87A2\u5E55\uFF08Esc\uFF09" : "\u5168\u87A2\u5E55\u6AA2\u8996\uFF08F\uFF09",
1795
+ children: props.fullscreen ? "\u2715 \u96E2\u958B\u5168\u87A2\u5E55" : "\u26F6 \u5168\u87A2\u5E55"
1796
+ }
1797
+ ) : null
1390
1798
  ] });
1391
1799
  }
1800
+ var BACKGROUND_CYCLE = ["transparent", "solid", "grid"];
1392
1801
  function usePrefersDark(explicit) {
1393
1802
  const [autoDark, setAutoDark] = useState(false);
1394
1803
  useEffect(() => {
@@ -1412,6 +1821,9 @@ var MermaidViewer = forwardRef(
1412
1821
  panZoom = true,
1413
1822
  search: searchEnabled = true,
1414
1823
  exportable = true,
1824
+ background: backgroundEnabled = true,
1825
+ fullscreen: fullscreenEnabled = true,
1826
+ onFullscreenChange,
1415
1827
  keyboard = true,
1416
1828
  seed = 42,
1417
1829
  fontUrl,
@@ -1435,6 +1847,15 @@ var MermaidViewer = forwardRef(
1435
1847
  const [query, setQuery] = useState("");
1436
1848
  const [matchInfo, setMatchInfo] = useState({ current: 0, total: 0 });
1437
1849
  const [exporting, setExporting] = useState(false);
1850
+ const [background, setBackgroundState] = useState(
1851
+ props.backgroundMode ?? "transparent"
1852
+ );
1853
+ useEffect(() => {
1854
+ if (props.backgroundMode) {
1855
+ setBackgroundState(props.backgroundMode);
1856
+ }
1857
+ }, [props.backgroundMode]);
1858
+ const [isFullscreen, setIsFullscreen] = useState(false);
1438
1859
  const rootRef = useRef(null);
1439
1860
  const searchInputRef = useRef(null);
1440
1861
  const vm = useMermaidViewer({
@@ -1497,6 +1918,85 @@ var MermaidViewer = forwardRef(
1497
1918
  setExporting(false);
1498
1919
  }
1499
1920
  }, [vm, onError]);
1921
+ const cycleBackground = useCallback(() => {
1922
+ setBackgroundState((prev) => {
1923
+ const i = BACKGROUND_CYCLE.indexOf(prev);
1924
+ return BACKGROUND_CYCLE[(i + 1) % BACKGROUND_CYCLE.length];
1925
+ });
1926
+ }, []);
1927
+ const setBackground = useCallback((mode) => {
1928
+ setBackgroundState(mode);
1929
+ }, []);
1930
+ const enterFullscreen = useCallback(() => {
1931
+ setIsFullscreen((prev) => {
1932
+ if (!prev) {
1933
+ onFullscreenChange?.(true);
1934
+ }
1935
+ return true;
1936
+ });
1937
+ }, [onFullscreenChange]);
1938
+ const exitFullscreen = useCallback(() => {
1939
+ setIsFullscreen((prev) => {
1940
+ if (prev) {
1941
+ onFullscreenChange?.(false);
1942
+ }
1943
+ return false;
1944
+ });
1945
+ }, [onFullscreenChange]);
1946
+ const toggleFullscreen = useCallback(() => {
1947
+ setIsFullscreen((prev) => {
1948
+ onFullscreenChange?.(!prev);
1949
+ return !prev;
1950
+ });
1951
+ }, [onFullscreenChange]);
1952
+ useEffect(() => {
1953
+ if (!isFullscreen) {
1954
+ return void 0;
1955
+ }
1956
+ const body = typeof document !== "undefined" ? document.body : null;
1957
+ const prevOverflow = body?.style.overflow ?? "";
1958
+ if (body) {
1959
+ body.style.overflow = "hidden";
1960
+ }
1961
+ const onWinKey = (e) => {
1962
+ if (e.key === "Escape") {
1963
+ e.preventDefault();
1964
+ exitFullscreen();
1965
+ }
1966
+ };
1967
+ window.addEventListener("keydown", onWinKey);
1968
+ const fitId = window.setTimeout(() => vm.reset(), 60);
1969
+ let resizeId = 0;
1970
+ const onResize = () => {
1971
+ window.clearTimeout(resizeId);
1972
+ resizeId = window.setTimeout(() => vm.reset(), 150);
1973
+ };
1974
+ window.addEventListener("resize", onResize);
1975
+ window.addEventListener("orientationchange", onResize);
1976
+ rootRef.current?.focus();
1977
+ return () => {
1978
+ window.removeEventListener("keydown", onWinKey);
1979
+ window.removeEventListener("resize", onResize);
1980
+ window.removeEventListener("orientationchange", onResize);
1981
+ window.clearTimeout(fitId);
1982
+ window.clearTimeout(resizeId);
1983
+ if (body) {
1984
+ body.style.overflow = prevOverflow;
1985
+ }
1986
+ };
1987
+ }, [isFullscreen, exitFullscreen]);
1988
+ const fsMountedRef = useRef(false);
1989
+ useEffect(() => {
1990
+ if (!fsMountedRef.current) {
1991
+ fsMountedRef.current = true;
1992
+ return void 0;
1993
+ }
1994
+ if (isFullscreen) {
1995
+ return void 0;
1996
+ }
1997
+ const id = window.setTimeout(() => vm.reset(), 60);
1998
+ return () => window.clearTimeout(id);
1999
+ }, [isFullscreen]);
1500
2000
  useEffect(() => {
1501
2001
  if (!keyboard) {
1502
2002
  return void 0;
@@ -1522,6 +2022,11 @@ var MermaidViewer = forwardRef(
1522
2022
  closeSearch();
1523
2023
  return;
1524
2024
  }
2025
+ if (e.key === "Escape" && isFullscreen) {
2026
+ e.preventDefault();
2027
+ exitFullscreen();
2028
+ return;
2029
+ }
1525
2030
  if (typing) {
1526
2031
  return;
1527
2032
  }
@@ -1535,11 +2040,29 @@ var MermaidViewer = forwardRef(
1535
2040
  vm.actualSize();
1536
2041
  } else if (e.key === "w" || e.key === "W") {
1537
2042
  vm.fit();
2043
+ } else if (fullscreenEnabled && (e.key === "f" || e.key === "F")) {
2044
+ e.preventDefault();
2045
+ toggleFullscreen();
2046
+ } else if (backgroundEnabled && (e.key === "b" || e.key === "B")) {
2047
+ e.preventDefault();
2048
+ cycleBackground();
1538
2049
  }
1539
2050
  };
1540
2051
  root.addEventListener("keydown", onKey);
1541
2052
  return () => root.removeEventListener("keydown", onKey);
1542
- }, [keyboard, searchOpen, openSearch, closeSearch, vm]);
2053
+ }, [
2054
+ keyboard,
2055
+ searchOpen,
2056
+ openSearch,
2057
+ closeSearch,
2058
+ vm,
2059
+ isFullscreen,
2060
+ exitFullscreen,
2061
+ toggleFullscreen,
2062
+ cycleBackground,
2063
+ fullscreenEnabled,
2064
+ backgroundEnabled
2065
+ ]);
1543
2066
  useImperativeHandle(
1544
2067
  ref,
1545
2068
  () => ({
@@ -1557,9 +2080,25 @@ var MermaidViewer = forwardRef(
1557
2080
  exportPng: vm.exportPng,
1558
2081
  downloadSvg: vm.downloadSvg,
1559
2082
  downloadPng: vm.downloadPng,
1560
- getSvg: vm.getSvg
2083
+ getSvg: vm.getSvg,
2084
+ enterFullscreen,
2085
+ exitFullscreen,
2086
+ toggleFullscreen,
2087
+ isFullscreen: () => isFullscreen,
2088
+ setBackground,
2089
+ cycleBackground,
2090
+ getBackground: () => background
1561
2091
  }),
1562
- [vm]
2092
+ [
2093
+ vm,
2094
+ enterFullscreen,
2095
+ exitFullscreen,
2096
+ toggleFullscreen,
2097
+ isFullscreen,
2098
+ setBackground,
2099
+ cycleBackground,
2100
+ background
2101
+ ]
1563
2102
  );
1564
2103
  let countText = "";
1565
2104
  if (matchInfo.total > 0) {
@@ -1567,7 +2106,13 @@ var MermaidViewer = forwardRef(
1567
2106
  } else if (query.trim()) {
1568
2107
  countText = "0";
1569
2108
  }
1570
- const rootClassName = ["rsm-root", dark ? "rsm-dark" : "", className ?? ""].filter(Boolean).join(" ");
2109
+ const rootClassName = [
2110
+ "rsm-root",
2111
+ dark ? "rsm-dark" : "",
2112
+ `rsm-bg-${background}`,
2113
+ isFullscreen ? "rsm-fullscreen" : "",
2114
+ className ?? ""
2115
+ ].filter(Boolean).join(" ");
1571
2116
  return /* @__PURE__ */ jsxs(
1572
2117
  "div",
1573
2118
  {
@@ -1593,7 +2138,13 @@ var MermaidViewer = forwardRef(
1593
2138
  exportEnabled: exportable,
1594
2139
  exporting,
1595
2140
  onExportSvg: exportSvg,
1596
- onExportPng: exportPng
2141
+ onExportPng: exportPng,
2142
+ backgroundEnabled,
2143
+ background,
2144
+ onCycleBackground: cycleBackground,
2145
+ fullscreenEnabled,
2146
+ fullscreen: isFullscreen,
2147
+ onToggleFullscreen: toggleFullscreen
1597
2148
  }
1598
2149
  ) : null,
1599
2150
  toolbar && searchEnabled && searchOpen ? /* @__PURE__ */ jsxs("div", { className: "rsm-searchbar", children: [
@@ -1633,6 +2184,17 @@ var MermaidViewer = forwardRef(
1633
2184
  "\u5716\u8868\u8F09\u5165\u5931\u6557\uFF1A",
1634
2185
  vm.error
1635
2186
  ] }) : null,
2187
+ isFullscreen ? /* @__PURE__ */ jsx(
2188
+ "button",
2189
+ {
2190
+ type: "button",
2191
+ className: "rsm-fs-close",
2192
+ onClick: exitFullscreen,
2193
+ title: "\u96E2\u958B\u5168\u87A2\u5E55\uFF08Esc\uFF09",
2194
+ "aria-label": "\u96E2\u958B\u5168\u87A2\u5E55",
2195
+ children: "\u2715"
2196
+ }
2197
+ ) : null,
1636
2198
  /* @__PURE__ */ jsx("div", { ref: vm.stageRef, className: "rsm-stage" })
1637
2199
  ] })
1638
2200
  ]
@@ -1646,6 +2208,6 @@ var MermaidDiagram = forwardRef(
1646
2208
  }
1647
2209
  );
1648
2210
 
1649
- export { DEFAULT_THEME_OPTIONS, DEFAULT_VIRGIL_FONT_URL, MermaidDiagram, MermaidViewer, SKETCH_FONT, Toolbar, colorizeDiagram, downloadBlob, ensureSketchFont, ensureStyles, loadMermaid, loadSvgPanZoom, prepareSvgElement, prepareSvgString, rasterizeToBlob, renderDiagram, resolveTheme, serializeLiveSvg, sketchifyDiagram, svgBlob, useMermaidViewer };
2211
+ export { DEFAULT_THEME_OPTIONS, DEFAULT_VIRGIL_FONT_URL, MermaidDiagram, MermaidViewer, SKETCH_FONT, Toolbar, boostLegibility, colorizeDiagram, downloadBlob, ensureSketchFont, ensureStyles, loadMermaid, loadSvgPanZoom, prepareSvgElement, prepareSvgString, rasterizeToBlob, renderDiagram, resolveTheme, serializeLiveSvg, sketchifyDiagram, svgBlob, useMermaidViewer };
1650
2212
  //# sourceMappingURL=index.js.map
1651
2213
  //# sourceMappingURL=index.js.map