react-super-mermaid 0.1.0 → 0.3.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/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,6 +244,71 @@ 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);
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; }
246
312
  }
247
313
  `;
248
314
 
@@ -280,16 +346,88 @@ var NODE_PALETTE = [
280
346
  // violet
281
347
  ];
282
348
  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" }
349
+ { fill: "rgba(59, 130, 246, 0.16)", stroke: "#3B82F6" },
350
+ // blue
351
+ { fill: "rgba(34, 197, 94, 0.16)", stroke: "#22C55E" },
352
+ // green
353
+ { fill: "rgba(249, 115, 22, 0.16)", stroke: "#F97316" },
354
+ // orange
355
+ { fill: "rgba(168, 85, 247, 0.16)", stroke: "#A855F7" },
356
+ // purple
357
+ { fill: "rgba(239, 68, 68, 0.16)", stroke: "#EF4444" },
358
+ // red
359
+ { fill: "rgba(6, 182, 212, 0.16)", stroke: "#06B6D4" },
360
+ // cyan
361
+ { fill: "rgba(234, 179, 8, 0.16)", stroke: "#EAB308" },
362
+ // yellow
363
+ { fill: "rgba(139, 92, 246, 0.16)", stroke: "#8B5CF6" }
364
+ // violet
365
+ ];
366
+ var PIE_PALETTE = [
367
+ "#3B82F6",
368
+ // blue
369
+ "#22C55E",
370
+ // green
371
+ "#F59E0B",
372
+ // amber
373
+ "#A855F7",
374
+ // purple
375
+ "#EF4444",
376
+ // red
377
+ "#06B6D4",
378
+ // cyan
379
+ "#EC4899",
380
+ // pink
381
+ "#84CC16",
382
+ // lime
383
+ "#F97316",
384
+ // orange
385
+ "#14B8A6",
386
+ // teal
387
+ "#6366F1",
388
+ // indigo
389
+ "#EAB308"
390
+ // yellow
289
391
  ];
290
392
  var NODE_TEXT = "#1F2937";
291
393
  var SHADOW_FILTER_ID = "rsm-soft-shadow";
292
394
  var SVG_NS = "http://www.w3.org/2000/svg";
395
+ function resolveSvg(root) {
396
+ if (root instanceof Element && root.tagName.toLowerCase() === "svg") {
397
+ return root;
398
+ }
399
+ return root.querySelector("svg");
400
+ }
401
+ function canonColor(input) {
402
+ const s = (input || "").trim();
403
+ const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(s);
404
+ if (hex) {
405
+ let h = hex[1];
406
+ if (h.length === 3) {
407
+ h = h.split("").map((c) => c + c).join("");
408
+ }
409
+ const n = parseInt(h, 16);
410
+ return `${n >> 16 & 255},${n >> 8 & 255},${n & 255}`;
411
+ }
412
+ const rgb = /rgba?\(([^)]+)\)/i.exec(s);
413
+ if (rgb) {
414
+ const p = rgb[1].split(",").map((x) => Math.round(parseFloat(x)));
415
+ return `${p[0]},${p[1]},${p[2]}`;
416
+ }
417
+ return s.toLowerCase();
418
+ }
419
+ function readableTextOn(color) {
420
+ const m = /^#?([0-9a-f]{6})$/i.exec(color.trim());
421
+ if (!m) {
422
+ return "#FFFFFF";
423
+ }
424
+ const n = parseInt(m[1], 16);
425
+ const r = n >> 16 & 255;
426
+ const g = n >> 8 & 255;
427
+ const b = n & 255;
428
+ const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
429
+ return lum > 0.62 ? "#1F2937" : "#FFFFFF";
430
+ }
293
431
  function ensureShadowFilter(svg) {
294
432
  if (svg.querySelector(`#${SHADOW_FILTER_ID}`)) {
295
433
  return;
@@ -395,6 +533,17 @@ function styleEdgeLabels(svg) {
395
533
  rect.setAttribute("ry", "4");
396
534
  }
397
535
  }
536
+ function styleLabelText(svg, dark) {
537
+ const color = dark ? "#E2E8F0" : NODE_TEXT;
538
+ for (const t of Array.from(
539
+ svg.querySelectorAll("text.messageText, .edgeLabel text, .edgeLabel tspan")
540
+ )) {
541
+ t.style.fill = color;
542
+ }
543
+ for (const t of Array.from(svg.querySelectorAll(".edgeLabel span, .edgeLabel p"))) {
544
+ t.style.color = color;
545
+ }
546
+ }
398
547
  function colorizeLegacyEr(svg) {
399
548
  const erGroups = [];
400
549
  for (const rect of Array.from(svg.querySelectorAll("rect.er.entityBox"))) {
@@ -506,12 +655,194 @@ function colorizeSequence(svg, dark) {
506
655
  text.style.fill = dark ? "#E2E8F0" : NODE_TEXT;
507
656
  }
508
657
  }
658
+ function stylePie(svg, dark) {
659
+ const slices = Array.from(svg.querySelectorAll("path.pieCircle"));
660
+ const swatches = Array.from(svg.querySelectorAll("g.legend rect"));
661
+ if (slices.length === 0 && swatches.length === 0) {
662
+ return;
663
+ }
664
+ const remap = /* @__PURE__ */ new Map();
665
+ let next = 0;
666
+ const newColorFor = (old) => {
667
+ const key = canonColor(old) || `#slot-${next}`;
668
+ let c = remap.get(key);
669
+ if (!c) {
670
+ c = PIE_PALETTE[next % PIE_PALETTE.length];
671
+ remap.set(key, c);
672
+ next += 1;
673
+ }
674
+ return c;
675
+ };
676
+ for (const slice of slices) {
677
+ const old = slice.style.fill || slice.getAttribute("fill") || "";
678
+ const c = newColorFor(old);
679
+ slice.style.fill = c;
680
+ slice.style.opacity = "1";
681
+ slice.style.stroke = dark ? "#0F172A" : "#FFFFFF";
682
+ slice.style.strokeWidth = "2px";
683
+ slice.style.strokeLinejoin = "round";
684
+ }
685
+ for (const sw of swatches) {
686
+ const old = sw.style.fill || sw.getAttribute("fill") || "";
687
+ const c = newColorFor(old);
688
+ sw.style.fill = c;
689
+ sw.style.stroke = c;
690
+ sw.setAttribute("rx", "3");
691
+ sw.setAttribute("ry", "3");
692
+ }
693
+ Array.from(svg.querySelectorAll("text.slice")).forEach((label, i) => {
694
+ const slice = slices[i];
695
+ const c = slice ? slice.style.fill || PIE_PALETTE[0] : PIE_PALETTE[0];
696
+ label.style.fill = readableTextOn(c);
697
+ label.style.fontWeight = "600";
698
+ });
699
+ for (const title of Array.from(svg.querySelectorAll("text.pieTitleText"))) {
700
+ title.style.fontWeight = "700";
701
+ title.style.fill = dark ? "#E2E8F0" : "#1F2937";
702
+ }
703
+ for (const t of Array.from(svg.querySelectorAll("g.legend text"))) {
704
+ t.style.fill = dark ? "#E2E8F0" : "#1F2937";
705
+ }
706
+ for (const oc of Array.from(svg.querySelectorAll("circle.pieOuterCircle"))) {
707
+ oc.style.stroke = dark ? "#334155" : "#CBD5E1";
708
+ }
709
+ }
710
+ function styleGantt(svg, dark) {
711
+ const tasks = Array.from(svg.querySelectorAll("rect.task"));
712
+ if (tasks.length === 0) {
713
+ return;
714
+ }
715
+ for (const task of tasks) {
716
+ const cls = task.getAttribute("class") ?? "";
717
+ if (/\b(done|active|crit|milestone)\d*\b/.test(cls)) {
718
+ continue;
719
+ }
720
+ const m = cls.match(/task(\d+)/);
721
+ if (!m) {
722
+ continue;
723
+ }
724
+ const entry = NODE_PALETTE[Number(m[1]) % NODE_PALETTE.length];
725
+ task.style.fill = entry.fill;
726
+ task.style.stroke = entry.stroke;
727
+ task.setAttribute("rx", "4");
728
+ task.setAttribute("ry", "4");
729
+ }
730
+ Array.from(svg.querySelectorAll("rect.section")).forEach((band) => {
731
+ const m = (band.getAttribute("class") ?? "").match(/section(\d+)/);
732
+ if (m) {
733
+ band.style.fill = CLUSTER_PALETTE[Number(m[1]) % CLUSTER_PALETTE.length].fill;
734
+ }
735
+ });
736
+ for (const inBar of Array.from(svg.querySelectorAll("text.taskText"))) {
737
+ if (!/Outside/.test(inBar.getAttribute("class") ?? "")) {
738
+ inBar.style.fill = NODE_TEXT;
739
+ }
740
+ }
741
+ for (const tick of Array.from(svg.querySelectorAll("g.grid g.tick line"))) {
742
+ tick.style.stroke = dark ? "#334155" : "#E2E8F0";
743
+ }
744
+ }
745
+ function styleTimeline(svg) {
746
+ const nodes = Array.from(svg.querySelectorAll('g[class*="timeline-node"]'));
747
+ nodes.forEach((node, i) => {
748
+ const m = (node.getAttribute("class") ?? "").match(/section-(-?\d+)/);
749
+ const section = m ? Number(m[1]) : i;
750
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
751
+ const backgrounds = Array.from(node.querySelectorAll(".node-bkg"));
752
+ if (backgrounds.length > 0) {
753
+ for (const bkg of backgrounds) {
754
+ bkg.style.fill = entry.fill;
755
+ bkg.style.stroke = entry.stroke;
756
+ bkg.style.strokeWidth = "1.4px";
757
+ }
758
+ } else {
759
+ paintShapes(node, entry);
760
+ }
761
+ darkenNodeText(node);
762
+ });
763
+ }
764
+ function styleMindmap(svg) {
765
+ const nodes = Array.from(svg.querySelectorAll("g.mindmap-node"));
766
+ if (nodes.length === 0) {
767
+ return;
768
+ }
769
+ for (const node of nodes) {
770
+ const m = (node.getAttribute("class") ?? "").match(/section-(-?\d+)/);
771
+ const section = m ? Number(m[1]) : 0;
772
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
773
+ for (const shape of Array.from(
774
+ node.querySelectorAll("path, rect, circle, ellipse")
775
+ )) {
776
+ if (shape.closest("g.children")) {
777
+ continue;
778
+ }
779
+ shape.style.fill = entry.fill;
780
+ shape.style.stroke = entry.stroke;
781
+ shape.style.strokeWidth = "1.4px";
782
+ }
783
+ darkenNodeText(node);
784
+ }
785
+ for (const edge of Array.from(svg.querySelectorAll('path[class*="edge"]'))) {
786
+ const m = (edge.getAttribute("class") ?? "").match(/section-edge-(-?\d+)/);
787
+ if (m) {
788
+ const section = Number(m[1]);
789
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
790
+ edge.style.stroke = entry.stroke;
791
+ edge.style.strokeWidth = "2px";
792
+ edge.style.opacity = "0.6";
793
+ edge.style.fill = "none";
794
+ }
795
+ }
796
+ }
797
+ function styleJourney(svg) {
798
+ const tasks = Array.from(
799
+ svg.querySelectorAll('circle[class*="task-type"], rect[class*="task-type"]')
800
+ );
801
+ tasks.forEach((shape) => {
802
+ const m = (shape.getAttribute("class") ?? "").match(/task-type-(\d+)/);
803
+ if (m) {
804
+ const entry = NODE_PALETTE[Number(m[1]) % NODE_PALETTE.length];
805
+ shape.style.fill = entry.fill;
806
+ shape.style.stroke = entry.stroke;
807
+ }
808
+ });
809
+ Array.from(svg.querySelectorAll('rect[class*="section-type"]')).forEach((rect) => {
810
+ const m = (rect.getAttribute("class") ?? "").match(/section-type-(\d+)/);
811
+ if (m) {
812
+ const entry = CLUSTER_PALETTE[Number(m[1]) % CLUSTER_PALETTE.length];
813
+ rect.style.fill = entry.fill;
814
+ rect.style.stroke = entry.stroke;
815
+ }
816
+ });
817
+ }
818
+ function boostLegibility(root) {
819
+ const svg = resolveSvg(root);
820
+ if (!svg) {
821
+ return;
822
+ }
823
+ for (const el of Array.from(
824
+ svg.querySelectorAll(
825
+ 'g.node text, g.node tspan, g.mindmap-node text, g[class*="timeline-node"] text, text.actor'
826
+ )
827
+ )) {
828
+ el.style.fontWeight = "600";
829
+ }
830
+ for (const el of Array.from(svg.querySelectorAll(".nodeLabel, g.node span, g.node p"))) {
831
+ el.style.fontWeight = "600";
832
+ }
833
+ for (const el of Array.from(svg.querySelectorAll("text"))) {
834
+ if (!el.style.fontWeight) {
835
+ el.style.fontWeight = "500";
836
+ }
837
+ }
838
+ }
509
839
  function colorizeDiagram(root, opts = {}) {
510
- const svg = root instanceof Element && root.tagName.toLowerCase() === "svg" ? root : root.querySelector("svg");
840
+ const svg = resolveSvg(root);
511
841
  if (!svg) {
512
842
  return;
513
843
  }
514
844
  ensureShadowFilter(svg);
845
+ const dark = opts.dark === true;
515
846
  Array.from(svg.querySelectorAll("g.node")).forEach((node, i) => {
516
847
  paintShapes(node, NODE_PALETTE[i % NODE_PALETTE.length]);
517
848
  darkenNodeText(node);
@@ -521,14 +852,41 @@ function colorizeDiagram(root, opts = {}) {
521
852
  for (const rect of Array.from(cluster.querySelectorAll(":scope > rect"))) {
522
853
  rect.style.fill = entry.fill;
523
854
  rect.style.stroke = entry.stroke;
524
- rect.style.strokeWidth = "1.2px";
855
+ rect.style.strokeWidth = "1.5px";
525
856
  roundRect(rect, 10);
526
857
  }
858
+ const label = cluster.querySelector(":scope > .cluster-label");
859
+ if (label) {
860
+ for (const el of Array.from(label.querySelectorAll("text, tspan"))) {
861
+ el.style.fill = entry.stroke;
862
+ el.style.fontWeight = "700";
863
+ }
864
+ for (const el of Array.from(label.querySelectorAll(".nodeLabel, span, p"))) {
865
+ el.style.color = entry.stroke;
866
+ el.style.fontWeight = "700";
867
+ }
868
+ for (const lr of Array.from(label.querySelectorAll("rect"))) {
869
+ lr.style.fill = entry.fill;
870
+ }
871
+ }
527
872
  });
528
873
  colorizeLegacyEr(svg);
529
- colorizeSequence(svg, opts.dark === true);
530
- styleEdges(svg, opts.dark === true);
874
+ colorizeSequence(svg, dark);
875
+ styleEdges(svg, dark);
531
876
  styleEdgeLabels(svg);
877
+ styleLabelText(svg, dark);
878
+ const kind = svg.getAttribute("aria-roledescription") ?? "";
879
+ if (kind === "pie" || kind === "pieChart") {
880
+ stylePie(svg, dark);
881
+ } else if (kind === "gantt") {
882
+ styleGantt(svg, dark);
883
+ } else if (kind === "timeline") {
884
+ styleTimeline(svg);
885
+ } else if (kind === "mindmap") {
886
+ styleMindmap(svg);
887
+ } else if (kind === "journey") {
888
+ styleJourney(svg);
889
+ }
532
890
  }
533
891
 
534
892
  // src/core/themes/sketch.ts
@@ -812,6 +1170,7 @@ function applyPostProcess(svg, postProcess, opts) {
812
1170
  } else if (postProcess === "sketch") {
813
1171
  sketchifyDiagram(svg, { dark: opts.dark, seed: opts.seed });
814
1172
  }
1173
+ boostLegibility(svg);
815
1174
  }
816
1175
  async function renderDiagram(opts) {
817
1176
  assertBrowser("renderDiagram");
@@ -1310,6 +1669,11 @@ function useMermaidViewer(opts) {
1310
1669
  getSvg
1311
1670
  };
1312
1671
  }
1672
+ var BACKGROUND_LABELS = {
1673
+ transparent: { icon: "\u25A6", label: "\u900F\u660E" },
1674
+ solid: { icon: "\u25FB", label: "\u7D14\u8272" },
1675
+ grid: { icon: "\u229E", label: "\u683C\u7DDA" }
1676
+ };
1313
1677
  var DEFAULT_THEME_OPTIONS = [
1314
1678
  { value: "colorful", label: "Colorful" },
1315
1679
  { value: "sketch", label: "Excalidraw" },
@@ -1333,6 +1697,20 @@ function Toolbar(props) {
1333
1697
  }
1334
1698
  )
1335
1699
  ] }),
1700
+ props.backgroundEnabled ? /* @__PURE__ */ jsxs(
1701
+ "button",
1702
+ {
1703
+ type: "button",
1704
+ className: "rsm-btn",
1705
+ onClick: props.onCycleBackground,
1706
+ title: "\u5207\u63DB\u756B\u5E03\u80CC\u666F\uFF08\u900F\u660E / \u7D14\u8272 / \u683C\u7DDA\uFF0CB\uFF09",
1707
+ children: [
1708
+ BACKGROUND_LABELS[props.background].icon,
1709
+ " \u80CC\u666F\uFF1A",
1710
+ BACKGROUND_LABELS[props.background].label
1711
+ ]
1712
+ }
1713
+ ) : null,
1336
1714
  /* @__PURE__ */ jsx("div", { className: "rsm-toolbar-spacer" }),
1337
1715
  props.searchEnabled ? /* @__PURE__ */ jsx(
1338
1716
  "button",
@@ -1386,9 +1764,21 @@ function Toolbar(props) {
1386
1764
  ),
1387
1765
  /* @__PURE__ */ jsx("button", { type: "button", onClick: props.onZoomIn, title: "\u653E\u5927\uFF08+\uFF09", children: "\uFF0B" }),
1388
1766
  /* @__PURE__ */ jsx("button", { type: "button", onClick: props.onReset, title: "\u7B26\u5408\u8996\u7A97\uFF080\uFF09", children: "\u2922" })
1389
- ] })
1767
+ ] }),
1768
+ props.fullscreenEnabled ? /* @__PURE__ */ jsx(
1769
+ "button",
1770
+ {
1771
+ type: "button",
1772
+ className: "rsm-btn rsm-btn-fullscreen",
1773
+ "aria-pressed": props.fullscreen ? "true" : "false",
1774
+ onClick: props.onToggleFullscreen,
1775
+ title: props.fullscreen ? "\u96E2\u958B\u5168\u87A2\u5E55\uFF08Esc\uFF09" : "\u5168\u87A2\u5E55\u6AA2\u8996\uFF08F\uFF09",
1776
+ children: props.fullscreen ? "\u2715 \u96E2\u958B\u5168\u87A2\u5E55" : "\u26F6 \u5168\u87A2\u5E55"
1777
+ }
1778
+ ) : null
1390
1779
  ] });
1391
1780
  }
1781
+ var BACKGROUND_CYCLE = ["transparent", "solid", "grid"];
1392
1782
  function usePrefersDark(explicit) {
1393
1783
  const [autoDark, setAutoDark] = useState(false);
1394
1784
  useEffect(() => {
@@ -1412,6 +1802,9 @@ var MermaidViewer = forwardRef(
1412
1802
  panZoom = true,
1413
1803
  search: searchEnabled = true,
1414
1804
  exportable = true,
1805
+ background: backgroundEnabled = true,
1806
+ fullscreen: fullscreenEnabled = true,
1807
+ onFullscreenChange,
1415
1808
  keyboard = true,
1416
1809
  seed = 42,
1417
1810
  fontUrl,
@@ -1435,6 +1828,15 @@ var MermaidViewer = forwardRef(
1435
1828
  const [query, setQuery] = useState("");
1436
1829
  const [matchInfo, setMatchInfo] = useState({ current: 0, total: 0 });
1437
1830
  const [exporting, setExporting] = useState(false);
1831
+ const [background, setBackgroundState] = useState(
1832
+ props.backgroundMode ?? "transparent"
1833
+ );
1834
+ useEffect(() => {
1835
+ if (props.backgroundMode) {
1836
+ setBackgroundState(props.backgroundMode);
1837
+ }
1838
+ }, [props.backgroundMode]);
1839
+ const [isFullscreen, setIsFullscreen] = useState(false);
1438
1840
  const rootRef = useRef(null);
1439
1841
  const searchInputRef = useRef(null);
1440
1842
  const vm = useMermaidViewer({
@@ -1497,6 +1899,85 @@ var MermaidViewer = forwardRef(
1497
1899
  setExporting(false);
1498
1900
  }
1499
1901
  }, [vm, onError]);
1902
+ const cycleBackground = useCallback(() => {
1903
+ setBackgroundState((prev) => {
1904
+ const i = BACKGROUND_CYCLE.indexOf(prev);
1905
+ return BACKGROUND_CYCLE[(i + 1) % BACKGROUND_CYCLE.length];
1906
+ });
1907
+ }, []);
1908
+ const setBackground = useCallback((mode) => {
1909
+ setBackgroundState(mode);
1910
+ }, []);
1911
+ const enterFullscreen = useCallback(() => {
1912
+ setIsFullscreen((prev) => {
1913
+ if (!prev) {
1914
+ onFullscreenChange?.(true);
1915
+ }
1916
+ return true;
1917
+ });
1918
+ }, [onFullscreenChange]);
1919
+ const exitFullscreen = useCallback(() => {
1920
+ setIsFullscreen((prev) => {
1921
+ if (prev) {
1922
+ onFullscreenChange?.(false);
1923
+ }
1924
+ return false;
1925
+ });
1926
+ }, [onFullscreenChange]);
1927
+ const toggleFullscreen = useCallback(() => {
1928
+ setIsFullscreen((prev) => {
1929
+ onFullscreenChange?.(!prev);
1930
+ return !prev;
1931
+ });
1932
+ }, [onFullscreenChange]);
1933
+ useEffect(() => {
1934
+ if (!isFullscreen) {
1935
+ return void 0;
1936
+ }
1937
+ const body = typeof document !== "undefined" ? document.body : null;
1938
+ const prevOverflow = body?.style.overflow ?? "";
1939
+ if (body) {
1940
+ body.style.overflow = "hidden";
1941
+ }
1942
+ const onWinKey = (e) => {
1943
+ if (e.key === "Escape") {
1944
+ e.preventDefault();
1945
+ exitFullscreen();
1946
+ }
1947
+ };
1948
+ window.addEventListener("keydown", onWinKey);
1949
+ const fitId = window.setTimeout(() => vm.reset(), 60);
1950
+ let resizeId = 0;
1951
+ const onResize = () => {
1952
+ window.clearTimeout(resizeId);
1953
+ resizeId = window.setTimeout(() => vm.reset(), 150);
1954
+ };
1955
+ window.addEventListener("resize", onResize);
1956
+ window.addEventListener("orientationchange", onResize);
1957
+ rootRef.current?.focus();
1958
+ return () => {
1959
+ window.removeEventListener("keydown", onWinKey);
1960
+ window.removeEventListener("resize", onResize);
1961
+ window.removeEventListener("orientationchange", onResize);
1962
+ window.clearTimeout(fitId);
1963
+ window.clearTimeout(resizeId);
1964
+ if (body) {
1965
+ body.style.overflow = prevOverflow;
1966
+ }
1967
+ };
1968
+ }, [isFullscreen, exitFullscreen]);
1969
+ const fsMountedRef = useRef(false);
1970
+ useEffect(() => {
1971
+ if (!fsMountedRef.current) {
1972
+ fsMountedRef.current = true;
1973
+ return void 0;
1974
+ }
1975
+ if (isFullscreen) {
1976
+ return void 0;
1977
+ }
1978
+ const id = window.setTimeout(() => vm.reset(), 60);
1979
+ return () => window.clearTimeout(id);
1980
+ }, [isFullscreen]);
1500
1981
  useEffect(() => {
1501
1982
  if (!keyboard) {
1502
1983
  return void 0;
@@ -1522,6 +2003,11 @@ var MermaidViewer = forwardRef(
1522
2003
  closeSearch();
1523
2004
  return;
1524
2005
  }
2006
+ if (e.key === "Escape" && isFullscreen) {
2007
+ e.preventDefault();
2008
+ exitFullscreen();
2009
+ return;
2010
+ }
1525
2011
  if (typing) {
1526
2012
  return;
1527
2013
  }
@@ -1535,11 +2021,29 @@ var MermaidViewer = forwardRef(
1535
2021
  vm.actualSize();
1536
2022
  } else if (e.key === "w" || e.key === "W") {
1537
2023
  vm.fit();
2024
+ } else if (fullscreenEnabled && (e.key === "f" || e.key === "F")) {
2025
+ e.preventDefault();
2026
+ toggleFullscreen();
2027
+ } else if (backgroundEnabled && (e.key === "b" || e.key === "B")) {
2028
+ e.preventDefault();
2029
+ cycleBackground();
1538
2030
  }
1539
2031
  };
1540
2032
  root.addEventListener("keydown", onKey);
1541
2033
  return () => root.removeEventListener("keydown", onKey);
1542
- }, [keyboard, searchOpen, openSearch, closeSearch, vm]);
2034
+ }, [
2035
+ keyboard,
2036
+ searchOpen,
2037
+ openSearch,
2038
+ closeSearch,
2039
+ vm,
2040
+ isFullscreen,
2041
+ exitFullscreen,
2042
+ toggleFullscreen,
2043
+ cycleBackground,
2044
+ fullscreenEnabled,
2045
+ backgroundEnabled
2046
+ ]);
1543
2047
  useImperativeHandle(
1544
2048
  ref,
1545
2049
  () => ({
@@ -1557,9 +2061,25 @@ var MermaidViewer = forwardRef(
1557
2061
  exportPng: vm.exportPng,
1558
2062
  downloadSvg: vm.downloadSvg,
1559
2063
  downloadPng: vm.downloadPng,
1560
- getSvg: vm.getSvg
2064
+ getSvg: vm.getSvg,
2065
+ enterFullscreen,
2066
+ exitFullscreen,
2067
+ toggleFullscreen,
2068
+ isFullscreen: () => isFullscreen,
2069
+ setBackground,
2070
+ cycleBackground,
2071
+ getBackground: () => background
1561
2072
  }),
1562
- [vm]
2073
+ [
2074
+ vm,
2075
+ enterFullscreen,
2076
+ exitFullscreen,
2077
+ toggleFullscreen,
2078
+ isFullscreen,
2079
+ setBackground,
2080
+ cycleBackground,
2081
+ background
2082
+ ]
1563
2083
  );
1564
2084
  let countText = "";
1565
2085
  if (matchInfo.total > 0) {
@@ -1567,7 +2087,13 @@ var MermaidViewer = forwardRef(
1567
2087
  } else if (query.trim()) {
1568
2088
  countText = "0";
1569
2089
  }
1570
- const rootClassName = ["rsm-root", dark ? "rsm-dark" : "", className ?? ""].filter(Boolean).join(" ");
2090
+ const rootClassName = [
2091
+ "rsm-root",
2092
+ dark ? "rsm-dark" : "",
2093
+ `rsm-bg-${background}`,
2094
+ isFullscreen ? "rsm-fullscreen" : "",
2095
+ className ?? ""
2096
+ ].filter(Boolean).join(" ");
1571
2097
  return /* @__PURE__ */ jsxs(
1572
2098
  "div",
1573
2099
  {
@@ -1593,7 +2119,13 @@ var MermaidViewer = forwardRef(
1593
2119
  exportEnabled: exportable,
1594
2120
  exporting,
1595
2121
  onExportSvg: exportSvg,
1596
- onExportPng: exportPng
2122
+ onExportPng: exportPng,
2123
+ backgroundEnabled,
2124
+ background,
2125
+ onCycleBackground: cycleBackground,
2126
+ fullscreenEnabled,
2127
+ fullscreen: isFullscreen,
2128
+ onToggleFullscreen: toggleFullscreen
1597
2129
  }
1598
2130
  ) : null,
1599
2131
  toolbar && searchEnabled && searchOpen ? /* @__PURE__ */ jsxs("div", { className: "rsm-searchbar", children: [
@@ -1633,6 +2165,17 @@ var MermaidViewer = forwardRef(
1633
2165
  "\u5716\u8868\u8F09\u5165\u5931\u6557\uFF1A",
1634
2166
  vm.error
1635
2167
  ] }) : null,
2168
+ isFullscreen ? /* @__PURE__ */ jsx(
2169
+ "button",
2170
+ {
2171
+ type: "button",
2172
+ className: "rsm-fs-close",
2173
+ onClick: exitFullscreen,
2174
+ title: "\u96E2\u958B\u5168\u87A2\u5E55\uFF08Esc\uFF09",
2175
+ "aria-label": "\u96E2\u958B\u5168\u87A2\u5E55",
2176
+ children: "\u2715"
2177
+ }
2178
+ ) : null,
1636
2179
  /* @__PURE__ */ jsx("div", { ref: vm.stageRef, className: "rsm-stage" })
1637
2180
  ] })
1638
2181
  ]
@@ -1646,6 +2189,6 @@ var MermaidDiagram = forwardRef(
1646
2189
  }
1647
2190
  );
1648
2191
 
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 };
2192
+ 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
2193
  //# sourceMappingURL=index.js.map
1651
2194
  //# sourceMappingURL=index.js.map