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.cjs CHANGED
@@ -108,6 +108,7 @@ var RSM_CSS = `
108
108
  --rsm-hover: #f3f4f6;
109
109
  --rsm-surface: #ffffff;
110
110
  --rsm-canvas-bg: transparent;
111
+ --rsm-grid-dot: rgba(0, 0, 0, 0.08);
111
112
  --rsm-radius: 8px;
112
113
  display: flex;
113
114
  flex-direction: column;
@@ -245,6 +246,71 @@ var RSM_CSS = `
245
246
  --rsm-accent: #60a5fa;
246
247
  --rsm-hover: #1f2937;
247
248
  --rsm-surface: #111827;
249
+ --rsm-grid-dot: rgba(255, 255, 255, 0.10);
250
+ }
251
+
252
+ /* \u2500\u2500 \u80CC\u666F\u6A21\u5F0F \u2500\u2500 \u900F\u660E(\u9810\u8A2D,\u8DDF\u96A8\u9801\u9762) / \u7D14\u8272(surface) / \u9EDE\u9663\u683C\u7DDA\u3002 */
253
+ .rsm-root.rsm-bg-solid .rsm-canvas { background: var(--rsm-surface); }
254
+ .rsm-root.rsm-bg-grid .rsm-canvas {
255
+ background-color: var(--rsm-surface);
256
+ background-image: radial-gradient(var(--rsm-grid-dot) 1px, transparent 1px);
257
+ background-size: 18px 18px;
258
+ background-position: -9px -9px;
259
+ }
260
+
261
+ /* \u2500\u2500 \u5168\u87A2\u5E55\u8DF3\u7A97 \u2500\u2500 position:fixed \u8986\u84CB\u6574\u500B\u8996\u7A97,RWD \u53CB\u5584\u3002 */
262
+ .rsm-root.rsm-fullscreen {
263
+ position: fixed;
264
+ inset: 0;
265
+ width: 100vw;
266
+ width: 100dvw;
267
+ height: 100vh;
268
+ height: 100dvh;
269
+ max-width: 100vw;
270
+ max-height: 100dvh;
271
+ margin: 0;
272
+ z-index: 2147483000;
273
+ border: 0;
274
+ border-radius: 0;
275
+ animation: rsm-fs-in 0.16s ease-out;
276
+ }
277
+ @keyframes rsm-fs-in {
278
+ from { opacity: 0; }
279
+ to { opacity: 1; }
280
+ }
281
+
282
+ /* \u5168\u87A2\u5E55\u53F3\u4E0A\u89D2\u7684\u96E2\u958B\u9215(toolbar \u96B1\u85CF\u6642\u4E5F\u80FD\u95DC\u9589)\u3002 */
283
+ .rsm-fs-close {
284
+ position: absolute;
285
+ top: 10px;
286
+ right: 10px;
287
+ z-index: 5;
288
+ display: inline-flex;
289
+ align-items: center;
290
+ justify-content: center;
291
+ width: 34px;
292
+ height: 34px;
293
+ padding: 0;
294
+ font-size: 16px;
295
+ line-height: 1;
296
+ border: 1px solid var(--rsm-border);
297
+ border-radius: 8px;
298
+ background: var(--rsm-surface);
299
+ color: var(--rsm-fg);
300
+ cursor: pointer;
301
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
302
+ transition: background 0.12s ease, color 0.12s ease;
303
+ }
304
+ .rsm-fs-close:hover { background: var(--rsm-hover); }
305
+
306
+ /* \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 */
307
+ @media (max-width: 640px) {
308
+ .rsm-toolbar { gap: 6px; padding: 6px 8px; }
309
+ .rsm-btn { padding: 4px 8px; font-size: 12px; }
310
+ .rsm-label { font-size: 11px; }
311
+ .rsm-select { padding: 3px 6px; font-size: 12px; }
312
+ .rsm-zoom > button { padding: 4px 8px; font-size: 12px; }
313
+ .rsm-input { flex-basis: 150px; }
248
314
  }
249
315
  `;
250
316
 
@@ -282,16 +348,88 @@ var NODE_PALETTE = [
282
348
  // violet
283
349
  ];
284
350
  var CLUSTER_PALETTE = [
285
- { fill: "rgba(59, 130, 246, 0.07)", stroke: "#93C5FD" },
286
- { fill: "rgba(34, 197, 94, 0.07)", stroke: "#86EFAC" },
287
- { fill: "rgba(249, 115, 22, 0.07)", stroke: "#FDBA74" },
288
- { fill: "rgba(168, 85, 247, 0.07)", stroke: "#D8B4FE" },
289
- { fill: "rgba(6, 182, 212, 0.07)", stroke: "#67E8F9" },
290
- { fill: "rgba(239, 68, 68, 0.07)", stroke: "#FCA5A5" }
351
+ { fill: "rgba(59, 130, 246, 0.16)", stroke: "#3B82F6" },
352
+ // blue
353
+ { fill: "rgba(34, 197, 94, 0.16)", stroke: "#22C55E" },
354
+ // green
355
+ { fill: "rgba(249, 115, 22, 0.16)", stroke: "#F97316" },
356
+ // orange
357
+ { fill: "rgba(168, 85, 247, 0.16)", stroke: "#A855F7" },
358
+ // purple
359
+ { fill: "rgba(239, 68, 68, 0.16)", stroke: "#EF4444" },
360
+ // red
361
+ { fill: "rgba(6, 182, 212, 0.16)", stroke: "#06B6D4" },
362
+ // cyan
363
+ { fill: "rgba(234, 179, 8, 0.16)", stroke: "#EAB308" },
364
+ // yellow
365
+ { fill: "rgba(139, 92, 246, 0.16)", stroke: "#8B5CF6" }
366
+ // violet
367
+ ];
368
+ var PIE_PALETTE = [
369
+ "#3B82F6",
370
+ // blue
371
+ "#22C55E",
372
+ // green
373
+ "#F59E0B",
374
+ // amber
375
+ "#A855F7",
376
+ // purple
377
+ "#EF4444",
378
+ // red
379
+ "#06B6D4",
380
+ // cyan
381
+ "#EC4899",
382
+ // pink
383
+ "#84CC16",
384
+ // lime
385
+ "#F97316",
386
+ // orange
387
+ "#14B8A6",
388
+ // teal
389
+ "#6366F1",
390
+ // indigo
391
+ "#EAB308"
392
+ // yellow
291
393
  ];
292
394
  var NODE_TEXT = "#1F2937";
293
395
  var SHADOW_FILTER_ID = "rsm-soft-shadow";
294
396
  var SVG_NS = "http://www.w3.org/2000/svg";
397
+ function resolveSvg(root) {
398
+ if (root instanceof Element && root.tagName.toLowerCase() === "svg") {
399
+ return root;
400
+ }
401
+ return root.querySelector("svg");
402
+ }
403
+ function canonColor(input) {
404
+ const s = (input || "").trim();
405
+ const hex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(s);
406
+ if (hex) {
407
+ let h = hex[1];
408
+ if (h.length === 3) {
409
+ h = h.split("").map((c) => c + c).join("");
410
+ }
411
+ const n = parseInt(h, 16);
412
+ return `${n >> 16 & 255},${n >> 8 & 255},${n & 255}`;
413
+ }
414
+ const rgb = /rgba?\(([^)]+)\)/i.exec(s);
415
+ if (rgb) {
416
+ const p = rgb[1].split(",").map((x) => Math.round(parseFloat(x)));
417
+ return `${p[0]},${p[1]},${p[2]}`;
418
+ }
419
+ return s.toLowerCase();
420
+ }
421
+ function readableTextOn(color) {
422
+ const m = /^#?([0-9a-f]{6})$/i.exec(color.trim());
423
+ if (!m) {
424
+ return "#FFFFFF";
425
+ }
426
+ const n = parseInt(m[1], 16);
427
+ const r = n >> 16 & 255;
428
+ const g = n >> 8 & 255;
429
+ const b = n & 255;
430
+ const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
431
+ return lum > 0.62 ? "#1F2937" : "#FFFFFF";
432
+ }
295
433
  function ensureShadowFilter(svg) {
296
434
  if (svg.querySelector(`#${SHADOW_FILTER_ID}`)) {
297
435
  return;
@@ -397,6 +535,17 @@ function styleEdgeLabels(svg) {
397
535
  rect.setAttribute("ry", "4");
398
536
  }
399
537
  }
538
+ function styleLabelText(svg, dark) {
539
+ const color = dark ? "#E2E8F0" : NODE_TEXT;
540
+ for (const t of Array.from(
541
+ svg.querySelectorAll("text.messageText, .edgeLabel text, .edgeLabel tspan")
542
+ )) {
543
+ t.style.fill = color;
544
+ }
545
+ for (const t of Array.from(svg.querySelectorAll(".edgeLabel span, .edgeLabel p"))) {
546
+ t.style.color = color;
547
+ }
548
+ }
400
549
  function colorizeLegacyEr(svg) {
401
550
  const erGroups = [];
402
551
  for (const rect of Array.from(svg.querySelectorAll("rect.er.entityBox"))) {
@@ -508,12 +657,194 @@ function colorizeSequence(svg, dark) {
508
657
  text.style.fill = dark ? "#E2E8F0" : NODE_TEXT;
509
658
  }
510
659
  }
660
+ function stylePie(svg, dark) {
661
+ const slices = Array.from(svg.querySelectorAll("path.pieCircle"));
662
+ const swatches = Array.from(svg.querySelectorAll("g.legend rect"));
663
+ if (slices.length === 0 && swatches.length === 0) {
664
+ return;
665
+ }
666
+ const remap = /* @__PURE__ */ new Map();
667
+ let next = 0;
668
+ const newColorFor = (old) => {
669
+ const key = canonColor(old) || `#slot-${next}`;
670
+ let c = remap.get(key);
671
+ if (!c) {
672
+ c = PIE_PALETTE[next % PIE_PALETTE.length];
673
+ remap.set(key, c);
674
+ next += 1;
675
+ }
676
+ return c;
677
+ };
678
+ for (const slice of slices) {
679
+ const old = slice.style.fill || slice.getAttribute("fill") || "";
680
+ const c = newColorFor(old);
681
+ slice.style.fill = c;
682
+ slice.style.opacity = "1";
683
+ slice.style.stroke = dark ? "#0F172A" : "#FFFFFF";
684
+ slice.style.strokeWidth = "2px";
685
+ slice.style.strokeLinejoin = "round";
686
+ }
687
+ for (const sw of swatches) {
688
+ const old = sw.style.fill || sw.getAttribute("fill") || "";
689
+ const c = newColorFor(old);
690
+ sw.style.fill = c;
691
+ sw.style.stroke = c;
692
+ sw.setAttribute("rx", "3");
693
+ sw.setAttribute("ry", "3");
694
+ }
695
+ Array.from(svg.querySelectorAll("text.slice")).forEach((label, i) => {
696
+ const slice = slices[i];
697
+ const c = slice ? slice.style.fill || PIE_PALETTE[0] : PIE_PALETTE[0];
698
+ label.style.fill = readableTextOn(c);
699
+ label.style.fontWeight = "600";
700
+ });
701
+ for (const title of Array.from(svg.querySelectorAll("text.pieTitleText"))) {
702
+ title.style.fontWeight = "700";
703
+ title.style.fill = dark ? "#E2E8F0" : "#1F2937";
704
+ }
705
+ for (const t of Array.from(svg.querySelectorAll("g.legend text"))) {
706
+ t.style.fill = dark ? "#E2E8F0" : "#1F2937";
707
+ }
708
+ for (const oc of Array.from(svg.querySelectorAll("circle.pieOuterCircle"))) {
709
+ oc.style.stroke = dark ? "#334155" : "#CBD5E1";
710
+ }
711
+ }
712
+ function styleGantt(svg, dark) {
713
+ const tasks = Array.from(svg.querySelectorAll("rect.task"));
714
+ if (tasks.length === 0) {
715
+ return;
716
+ }
717
+ for (const task of tasks) {
718
+ const cls = task.getAttribute("class") ?? "";
719
+ if (/\b(done|active|crit|milestone)\d*\b/.test(cls)) {
720
+ continue;
721
+ }
722
+ const m = cls.match(/task(\d+)/);
723
+ if (!m) {
724
+ continue;
725
+ }
726
+ const entry = NODE_PALETTE[Number(m[1]) % NODE_PALETTE.length];
727
+ task.style.fill = entry.fill;
728
+ task.style.stroke = entry.stroke;
729
+ task.setAttribute("rx", "4");
730
+ task.setAttribute("ry", "4");
731
+ }
732
+ Array.from(svg.querySelectorAll("rect.section")).forEach((band) => {
733
+ const m = (band.getAttribute("class") ?? "").match(/section(\d+)/);
734
+ if (m) {
735
+ band.style.fill = CLUSTER_PALETTE[Number(m[1]) % CLUSTER_PALETTE.length].fill;
736
+ }
737
+ });
738
+ for (const inBar of Array.from(svg.querySelectorAll("text.taskText"))) {
739
+ if (!/Outside/.test(inBar.getAttribute("class") ?? "")) {
740
+ inBar.style.fill = NODE_TEXT;
741
+ }
742
+ }
743
+ for (const tick of Array.from(svg.querySelectorAll("g.grid g.tick line"))) {
744
+ tick.style.stroke = dark ? "#334155" : "#E2E8F0";
745
+ }
746
+ }
747
+ function styleTimeline(svg) {
748
+ const nodes = Array.from(svg.querySelectorAll('g[class*="timeline-node"]'));
749
+ nodes.forEach((node, i) => {
750
+ const m = (node.getAttribute("class") ?? "").match(/section-(-?\d+)/);
751
+ const section = m ? Number(m[1]) : i;
752
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
753
+ const backgrounds = Array.from(node.querySelectorAll(".node-bkg"));
754
+ if (backgrounds.length > 0) {
755
+ for (const bkg of backgrounds) {
756
+ bkg.style.fill = entry.fill;
757
+ bkg.style.stroke = entry.stroke;
758
+ bkg.style.strokeWidth = "1.4px";
759
+ }
760
+ } else {
761
+ paintShapes(node, entry);
762
+ }
763
+ darkenNodeText(node);
764
+ });
765
+ }
766
+ function styleMindmap(svg) {
767
+ const nodes = Array.from(svg.querySelectorAll("g.mindmap-node"));
768
+ if (nodes.length === 0) {
769
+ return;
770
+ }
771
+ for (const node of nodes) {
772
+ const m = (node.getAttribute("class") ?? "").match(/section-(-?\d+)/);
773
+ const section = m ? Number(m[1]) : 0;
774
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
775
+ for (const shape of Array.from(
776
+ node.querySelectorAll("path, rect, circle, ellipse")
777
+ )) {
778
+ if (shape.closest("g.children")) {
779
+ continue;
780
+ }
781
+ shape.style.fill = entry.fill;
782
+ shape.style.stroke = entry.stroke;
783
+ shape.style.strokeWidth = "1.4px";
784
+ }
785
+ darkenNodeText(node);
786
+ }
787
+ for (const edge of Array.from(svg.querySelectorAll('path[class*="edge"]'))) {
788
+ const m = (edge.getAttribute("class") ?? "").match(/section-edge-(-?\d+)/);
789
+ if (m) {
790
+ const section = Number(m[1]);
791
+ const entry = section < 0 ? NODE_PALETTE[7] : NODE_PALETTE[section % NODE_PALETTE.length];
792
+ edge.style.stroke = entry.stroke;
793
+ edge.style.strokeWidth = "2px";
794
+ edge.style.opacity = "0.6";
795
+ edge.style.fill = "none";
796
+ }
797
+ }
798
+ }
799
+ function styleJourney(svg) {
800
+ const tasks = Array.from(
801
+ svg.querySelectorAll('circle[class*="task-type"], rect[class*="task-type"]')
802
+ );
803
+ tasks.forEach((shape) => {
804
+ const m = (shape.getAttribute("class") ?? "").match(/task-type-(\d+)/);
805
+ if (m) {
806
+ const entry = NODE_PALETTE[Number(m[1]) % NODE_PALETTE.length];
807
+ shape.style.fill = entry.fill;
808
+ shape.style.stroke = entry.stroke;
809
+ }
810
+ });
811
+ Array.from(svg.querySelectorAll('rect[class*="section-type"]')).forEach((rect) => {
812
+ const m = (rect.getAttribute("class") ?? "").match(/section-type-(\d+)/);
813
+ if (m) {
814
+ const entry = CLUSTER_PALETTE[Number(m[1]) % CLUSTER_PALETTE.length];
815
+ rect.style.fill = entry.fill;
816
+ rect.style.stroke = entry.stroke;
817
+ }
818
+ });
819
+ }
820
+ function boostLegibility(root) {
821
+ const svg = resolveSvg(root);
822
+ if (!svg) {
823
+ return;
824
+ }
825
+ for (const el of Array.from(
826
+ svg.querySelectorAll(
827
+ 'g.node text, g.node tspan, g.mindmap-node text, g[class*="timeline-node"] text, text.actor'
828
+ )
829
+ )) {
830
+ el.style.fontWeight = "600";
831
+ }
832
+ for (const el of Array.from(svg.querySelectorAll(".nodeLabel, g.node span, g.node p"))) {
833
+ el.style.fontWeight = "600";
834
+ }
835
+ for (const el of Array.from(svg.querySelectorAll("text"))) {
836
+ if (!el.style.fontWeight) {
837
+ el.style.fontWeight = "500";
838
+ }
839
+ }
840
+ }
511
841
  function colorizeDiagram(root, opts = {}) {
512
- const svg = root instanceof Element && root.tagName.toLowerCase() === "svg" ? root : root.querySelector("svg");
842
+ const svg = resolveSvg(root);
513
843
  if (!svg) {
514
844
  return;
515
845
  }
516
846
  ensureShadowFilter(svg);
847
+ const dark = opts.dark === true;
517
848
  Array.from(svg.querySelectorAll("g.node")).forEach((node, i) => {
518
849
  paintShapes(node, NODE_PALETTE[i % NODE_PALETTE.length]);
519
850
  darkenNodeText(node);
@@ -523,14 +854,41 @@ function colorizeDiagram(root, opts = {}) {
523
854
  for (const rect of Array.from(cluster.querySelectorAll(":scope > rect"))) {
524
855
  rect.style.fill = entry.fill;
525
856
  rect.style.stroke = entry.stroke;
526
- rect.style.strokeWidth = "1.2px";
857
+ rect.style.strokeWidth = "1.5px";
527
858
  roundRect(rect, 10);
528
859
  }
860
+ const label = cluster.querySelector(":scope > .cluster-label");
861
+ if (label) {
862
+ for (const el of Array.from(label.querySelectorAll("text, tspan"))) {
863
+ el.style.fill = entry.stroke;
864
+ el.style.fontWeight = "700";
865
+ }
866
+ for (const el of Array.from(label.querySelectorAll(".nodeLabel, span, p"))) {
867
+ el.style.color = entry.stroke;
868
+ el.style.fontWeight = "700";
869
+ }
870
+ for (const lr of Array.from(label.querySelectorAll("rect"))) {
871
+ lr.style.fill = entry.fill;
872
+ }
873
+ }
529
874
  });
530
875
  colorizeLegacyEr(svg);
531
- colorizeSequence(svg, opts.dark === true);
532
- styleEdges(svg, opts.dark === true);
876
+ colorizeSequence(svg, dark);
877
+ styleEdges(svg, dark);
533
878
  styleEdgeLabels(svg);
879
+ styleLabelText(svg, dark);
880
+ const kind = svg.getAttribute("aria-roledescription") ?? "";
881
+ if (kind === "pie" || kind === "pieChart") {
882
+ stylePie(svg, dark);
883
+ } else if (kind === "gantt") {
884
+ styleGantt(svg, dark);
885
+ } else if (kind === "timeline") {
886
+ styleTimeline(svg);
887
+ } else if (kind === "mindmap") {
888
+ styleMindmap(svg);
889
+ } else if (kind === "journey") {
890
+ styleJourney(svg);
891
+ }
534
892
  }
535
893
 
536
894
  // src/core/themes/sketch.ts
@@ -814,6 +1172,7 @@ function applyPostProcess(svg, postProcess, opts) {
814
1172
  } else if (postProcess === "sketch") {
815
1173
  sketchifyDiagram(svg, { dark: opts.dark, seed: opts.seed });
816
1174
  }
1175
+ boostLegibility(svg);
817
1176
  }
818
1177
  async function renderDiagram(opts) {
819
1178
  assertBrowser("renderDiagram");
@@ -1312,6 +1671,11 @@ function useMermaidViewer(opts) {
1312
1671
  getSvg
1313
1672
  };
1314
1673
  }
1674
+ var BACKGROUND_LABELS = {
1675
+ transparent: { icon: "\u25A6", label: "\u900F\u660E" },
1676
+ solid: { icon: "\u25FB", label: "\u7D14\u8272" },
1677
+ grid: { icon: "\u229E", label: "\u683C\u7DDA" }
1678
+ };
1315
1679
  var DEFAULT_THEME_OPTIONS = [
1316
1680
  { value: "colorful", label: "Colorful" },
1317
1681
  { value: "sketch", label: "Excalidraw" },
@@ -1335,6 +1699,20 @@ function Toolbar(props) {
1335
1699
  }
1336
1700
  )
1337
1701
  ] }),
1702
+ props.backgroundEnabled ? /* @__PURE__ */ jsxRuntime.jsxs(
1703
+ "button",
1704
+ {
1705
+ type: "button",
1706
+ className: "rsm-btn",
1707
+ onClick: props.onCycleBackground,
1708
+ title: "\u5207\u63DB\u756B\u5E03\u80CC\u666F\uFF08\u900F\u660E / \u7D14\u8272 / \u683C\u7DDA\uFF0CB\uFF09",
1709
+ children: [
1710
+ BACKGROUND_LABELS[props.background].icon,
1711
+ " \u80CC\u666F\uFF1A",
1712
+ BACKGROUND_LABELS[props.background].label
1713
+ ]
1714
+ }
1715
+ ) : null,
1338
1716
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rsm-toolbar-spacer" }),
1339
1717
  props.searchEnabled ? /* @__PURE__ */ jsxRuntime.jsx(
1340
1718
  "button",
@@ -1388,9 +1766,21 @@ function Toolbar(props) {
1388
1766
  ),
1389
1767
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: props.onZoomIn, title: "\u653E\u5927\uFF08+\uFF09", children: "\uFF0B" }),
1390
1768
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: props.onReset, title: "\u7B26\u5408\u8996\u7A97\uFF080\uFF09", children: "\u2922" })
1391
- ] })
1769
+ ] }),
1770
+ props.fullscreenEnabled ? /* @__PURE__ */ jsxRuntime.jsx(
1771
+ "button",
1772
+ {
1773
+ type: "button",
1774
+ className: "rsm-btn rsm-btn-fullscreen",
1775
+ "aria-pressed": props.fullscreen ? "true" : "false",
1776
+ onClick: props.onToggleFullscreen,
1777
+ title: props.fullscreen ? "\u96E2\u958B\u5168\u87A2\u5E55\uFF08Esc\uFF09" : "\u5168\u87A2\u5E55\u6AA2\u8996\uFF08F\uFF09",
1778
+ children: props.fullscreen ? "\u2715 \u96E2\u958B\u5168\u87A2\u5E55" : "\u26F6 \u5168\u87A2\u5E55"
1779
+ }
1780
+ ) : null
1392
1781
  ] });
1393
1782
  }
1783
+ var BACKGROUND_CYCLE = ["transparent", "solid", "grid"];
1394
1784
  function usePrefersDark(explicit) {
1395
1785
  const [autoDark, setAutoDark] = react.useState(false);
1396
1786
  react.useEffect(() => {
@@ -1414,6 +1804,9 @@ var MermaidViewer = react.forwardRef(
1414
1804
  panZoom = true,
1415
1805
  search: searchEnabled = true,
1416
1806
  exportable = true,
1807
+ background: backgroundEnabled = true,
1808
+ fullscreen: fullscreenEnabled = true,
1809
+ onFullscreenChange,
1417
1810
  keyboard = true,
1418
1811
  seed = 42,
1419
1812
  fontUrl,
@@ -1437,6 +1830,15 @@ var MermaidViewer = react.forwardRef(
1437
1830
  const [query, setQuery] = react.useState("");
1438
1831
  const [matchInfo, setMatchInfo] = react.useState({ current: 0, total: 0 });
1439
1832
  const [exporting, setExporting] = react.useState(false);
1833
+ const [background, setBackgroundState] = react.useState(
1834
+ props.backgroundMode ?? "transparent"
1835
+ );
1836
+ react.useEffect(() => {
1837
+ if (props.backgroundMode) {
1838
+ setBackgroundState(props.backgroundMode);
1839
+ }
1840
+ }, [props.backgroundMode]);
1841
+ const [isFullscreen, setIsFullscreen] = react.useState(false);
1440
1842
  const rootRef = react.useRef(null);
1441
1843
  const searchInputRef = react.useRef(null);
1442
1844
  const vm = useMermaidViewer({
@@ -1499,6 +1901,85 @@ var MermaidViewer = react.forwardRef(
1499
1901
  setExporting(false);
1500
1902
  }
1501
1903
  }, [vm, onError]);
1904
+ const cycleBackground = react.useCallback(() => {
1905
+ setBackgroundState((prev) => {
1906
+ const i = BACKGROUND_CYCLE.indexOf(prev);
1907
+ return BACKGROUND_CYCLE[(i + 1) % BACKGROUND_CYCLE.length];
1908
+ });
1909
+ }, []);
1910
+ const setBackground = react.useCallback((mode) => {
1911
+ setBackgroundState(mode);
1912
+ }, []);
1913
+ const enterFullscreen = react.useCallback(() => {
1914
+ setIsFullscreen((prev) => {
1915
+ if (!prev) {
1916
+ onFullscreenChange?.(true);
1917
+ }
1918
+ return true;
1919
+ });
1920
+ }, [onFullscreenChange]);
1921
+ const exitFullscreen = react.useCallback(() => {
1922
+ setIsFullscreen((prev) => {
1923
+ if (prev) {
1924
+ onFullscreenChange?.(false);
1925
+ }
1926
+ return false;
1927
+ });
1928
+ }, [onFullscreenChange]);
1929
+ const toggleFullscreen = react.useCallback(() => {
1930
+ setIsFullscreen((prev) => {
1931
+ onFullscreenChange?.(!prev);
1932
+ return !prev;
1933
+ });
1934
+ }, [onFullscreenChange]);
1935
+ react.useEffect(() => {
1936
+ if (!isFullscreen) {
1937
+ return void 0;
1938
+ }
1939
+ const body = typeof document !== "undefined" ? document.body : null;
1940
+ const prevOverflow = body?.style.overflow ?? "";
1941
+ if (body) {
1942
+ body.style.overflow = "hidden";
1943
+ }
1944
+ const onWinKey = (e) => {
1945
+ if (e.key === "Escape") {
1946
+ e.preventDefault();
1947
+ exitFullscreen();
1948
+ }
1949
+ };
1950
+ window.addEventListener("keydown", onWinKey);
1951
+ const fitId = window.setTimeout(() => vm.reset(), 60);
1952
+ let resizeId = 0;
1953
+ const onResize = () => {
1954
+ window.clearTimeout(resizeId);
1955
+ resizeId = window.setTimeout(() => vm.reset(), 150);
1956
+ };
1957
+ window.addEventListener("resize", onResize);
1958
+ window.addEventListener("orientationchange", onResize);
1959
+ rootRef.current?.focus();
1960
+ return () => {
1961
+ window.removeEventListener("keydown", onWinKey);
1962
+ window.removeEventListener("resize", onResize);
1963
+ window.removeEventListener("orientationchange", onResize);
1964
+ window.clearTimeout(fitId);
1965
+ window.clearTimeout(resizeId);
1966
+ if (body) {
1967
+ body.style.overflow = prevOverflow;
1968
+ }
1969
+ };
1970
+ }, [isFullscreen, exitFullscreen]);
1971
+ const fsMountedRef = react.useRef(false);
1972
+ react.useEffect(() => {
1973
+ if (!fsMountedRef.current) {
1974
+ fsMountedRef.current = true;
1975
+ return void 0;
1976
+ }
1977
+ if (isFullscreen) {
1978
+ return void 0;
1979
+ }
1980
+ const id = window.setTimeout(() => vm.reset(), 60);
1981
+ return () => window.clearTimeout(id);
1982
+ }, [isFullscreen]);
1502
1983
  react.useEffect(() => {
1503
1984
  if (!keyboard) {
1504
1985
  return void 0;
@@ -1524,6 +2005,11 @@ var MermaidViewer = react.forwardRef(
1524
2005
  closeSearch();
1525
2006
  return;
1526
2007
  }
2008
+ if (e.key === "Escape" && isFullscreen) {
2009
+ e.preventDefault();
2010
+ exitFullscreen();
2011
+ return;
2012
+ }
1527
2013
  if (typing) {
1528
2014
  return;
1529
2015
  }
@@ -1537,11 +2023,29 @@ var MermaidViewer = react.forwardRef(
1537
2023
  vm.actualSize();
1538
2024
  } else if (e.key === "w" || e.key === "W") {
1539
2025
  vm.fit();
2026
+ } else if (fullscreenEnabled && (e.key === "f" || e.key === "F")) {
2027
+ e.preventDefault();
2028
+ toggleFullscreen();
2029
+ } else if (backgroundEnabled && (e.key === "b" || e.key === "B")) {
2030
+ e.preventDefault();
2031
+ cycleBackground();
1540
2032
  }
1541
2033
  };
1542
2034
  root.addEventListener("keydown", onKey);
1543
2035
  return () => root.removeEventListener("keydown", onKey);
1544
- }, [keyboard, searchOpen, openSearch, closeSearch, vm]);
2036
+ }, [
2037
+ keyboard,
2038
+ searchOpen,
2039
+ openSearch,
2040
+ closeSearch,
2041
+ vm,
2042
+ isFullscreen,
2043
+ exitFullscreen,
2044
+ toggleFullscreen,
2045
+ cycleBackground,
2046
+ fullscreenEnabled,
2047
+ backgroundEnabled
2048
+ ]);
1545
2049
  react.useImperativeHandle(
1546
2050
  ref,
1547
2051
  () => ({
@@ -1559,9 +2063,25 @@ var MermaidViewer = react.forwardRef(
1559
2063
  exportPng: vm.exportPng,
1560
2064
  downloadSvg: vm.downloadSvg,
1561
2065
  downloadPng: vm.downloadPng,
1562
- getSvg: vm.getSvg
2066
+ getSvg: vm.getSvg,
2067
+ enterFullscreen,
2068
+ exitFullscreen,
2069
+ toggleFullscreen,
2070
+ isFullscreen: () => isFullscreen,
2071
+ setBackground,
2072
+ cycleBackground,
2073
+ getBackground: () => background
1563
2074
  }),
1564
- [vm]
2075
+ [
2076
+ vm,
2077
+ enterFullscreen,
2078
+ exitFullscreen,
2079
+ toggleFullscreen,
2080
+ isFullscreen,
2081
+ setBackground,
2082
+ cycleBackground,
2083
+ background
2084
+ ]
1565
2085
  );
1566
2086
  let countText = "";
1567
2087
  if (matchInfo.total > 0) {
@@ -1569,7 +2089,13 @@ var MermaidViewer = react.forwardRef(
1569
2089
  } else if (query.trim()) {
1570
2090
  countText = "0";
1571
2091
  }
1572
- const rootClassName = ["rsm-root", dark ? "rsm-dark" : "", className ?? ""].filter(Boolean).join(" ");
2092
+ const rootClassName = [
2093
+ "rsm-root",
2094
+ dark ? "rsm-dark" : "",
2095
+ `rsm-bg-${background}`,
2096
+ isFullscreen ? "rsm-fullscreen" : "",
2097
+ className ?? ""
2098
+ ].filter(Boolean).join(" ");
1573
2099
  return /* @__PURE__ */ jsxRuntime.jsxs(
1574
2100
  "div",
1575
2101
  {
@@ -1595,7 +2121,13 @@ var MermaidViewer = react.forwardRef(
1595
2121
  exportEnabled: exportable,
1596
2122
  exporting,
1597
2123
  onExportSvg: exportSvg,
1598
- onExportPng: exportPng
2124
+ onExportPng: exportPng,
2125
+ backgroundEnabled,
2126
+ background,
2127
+ onCycleBackground: cycleBackground,
2128
+ fullscreenEnabled,
2129
+ fullscreen: isFullscreen,
2130
+ onToggleFullscreen: toggleFullscreen
1599
2131
  }
1600
2132
  ) : null,
1601
2133
  toolbar && searchEnabled && searchOpen ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rsm-searchbar", children: [
@@ -1635,6 +2167,17 @@ var MermaidViewer = react.forwardRef(
1635
2167
  "\u5716\u8868\u8F09\u5165\u5931\u6557\uFF1A",
1636
2168
  vm.error
1637
2169
  ] }) : null,
2170
+ isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx(
2171
+ "button",
2172
+ {
2173
+ type: "button",
2174
+ className: "rsm-fs-close",
2175
+ onClick: exitFullscreen,
2176
+ title: "\u96E2\u958B\u5168\u87A2\u5E55\uFF08Esc\uFF09",
2177
+ "aria-label": "\u96E2\u958B\u5168\u87A2\u5E55",
2178
+ children: "\u2715"
2179
+ }
2180
+ ) : null,
1638
2181
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: vm.stageRef, className: "rsm-stage" })
1639
2182
  ] })
1640
2183
  ]
@@ -1654,6 +2197,7 @@ exports.MermaidDiagram = MermaidDiagram;
1654
2197
  exports.MermaidViewer = MermaidViewer;
1655
2198
  exports.SKETCH_FONT = SKETCH_FONT;
1656
2199
  exports.Toolbar = Toolbar;
2200
+ exports.boostLegibility = boostLegibility;
1657
2201
  exports.colorizeDiagram = colorizeDiagram;
1658
2202
  exports.downloadBlob = downloadBlob;
1659
2203
  exports.ensureSketchFont = ensureSketchFont;