react-super-mermaid 0.3.1 → 0.4.2

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
@@ -105,8 +105,13 @@ var RSM_CSS = `
105
105
  --rsm-accent: #2563eb;
106
106
  --rsm-hover: #f3f4f6;
107
107
  --rsm-surface: #ffffff;
108
+ /* --rsm-paper = \u756B\u5E03\u5E95\u8272,\u5C0D\u9F4A VS Code \u64F4\u5145\u5957\u4EF6\u7684 editor-background(\u4EAE)\u3002 */
109
+ --rsm-paper: #ffffff;
108
110
  --rsm-canvas-bg: transparent;
109
- --rsm-grid-dot: rgba(0, 0, 0, 0.08);
111
+ /* \u9EDE\u9663\u683C\u7DDA:\u5C0D\u9F4A VS Code \u7684 color-mix(foreground 9%) \u516C\u5F0F\u3002 */
112
+ --rsm-grid-dot: color-mix(in srgb, var(--rsm-fg) 9%, transparent);
113
+ /* \u7DB2\u683C\u7DDA:\u6BD4\u7DB2\u9EDE\u518D\u6DE1\u4E00\u9EDE,\u907F\u514D\u7DDA\u689D\u6436\u904E\u5716\u8868\u3002 */
114
+ --rsm-grid-line: color-mix(in srgb, var(--rsm-fg) 7%, transparent);
110
115
  --rsm-radius: 8px;
111
116
  display: flex;
112
117
  flex-direction: column;
@@ -168,6 +173,14 @@ var RSM_CSS = `
168
173
  }
169
174
  .rsm-btn:disabled { opacity: 0.6; cursor: default; }
170
175
 
176
+ /* \u5DE5\u5177\u5217\u63A7\u5236\u9805\u9AD8\u5EA6\u4E00\u81F4(\u6A23\u5F0F\u4E0B\u62C9 / \u4E00\u822C\u9215 / \u7E2E\u653E\u7FA4 / \u80CC\u666F\u9215),\u907F\u514D\u9AD8\u4F4E\u4E0D\u9F4A\u3002 */
177
+ .rsm-toolbar .rsm-btn,
178
+ .rsm-toolbar .rsm-select,
179
+ .rsm-toolbar .rsm-zoom { min-height: 30px; }
180
+ .rsm-toolbar .rsm-btn,
181
+ .rsm-toolbar .rsm-select { align-items: center; }
182
+ .rsm-toolbar .rsm-zoom > button { display: inline-flex; align-items: center; justify-content: center; }
183
+
171
184
  .rsm-zoom {
172
185
  display: inline-flex;
173
186
  align-items: stretch;
@@ -213,7 +226,8 @@ var RSM_CSS = `
213
226
  flex: 1 1 auto;
214
227
  min-height: 0;
215
228
  overflow: hidden;
216
- background: var(--rsm-canvas-bg);
229
+ /* \u5E95\u8272\u7531 --rsm-canvas-bg \u63A7\u5236(\u9810\u8A2D\u900F\u660E,\u8DDF\u96A8\u9801\u9762);\u5716\u6A23\u758A\u5728\u5176\u4E0A(background-image)\u3002 */
230
+ background-color: var(--rsm-canvas-bg, transparent);
217
231
  }
218
232
  .rsm-stage { width: 100%; height: 100%; }
219
233
  .rsm-root svg { cursor: grab; user-select: none; }
@@ -238,23 +252,172 @@ var RSM_CSS = `
238
252
  .rsm-root .rsm-hit { filter: drop-shadow(0 0 5px #f59e0b) drop-shadow(0 0 1.5px #f59e0b); }
239
253
 
240
254
  .rsm-root.rsm-dark {
241
- --rsm-border: #374151;
242
- --rsm-fg: #e5e7eb;
243
- --rsm-muted: #9ca3af;
244
- --rsm-accent: #60a5fa;
245
- --rsm-hover: #1f2937;
246
- --rsm-surface: #111827;
247
- --rsm-grid-dot: rgba(255, 255, 255, 0.10);
255
+ /* \u6697\u8272\u9762\u677F\u5C0D\u9F4A VS Code Dark+ / Dark Modern \u7684\u4E2D\u6027\u7070(\u975E\u85CD\u8ABF)\u3002 */
256
+ --rsm-border: #3c3c3c;
257
+ --rsm-fg: #cccccc;
258
+ --rsm-muted: #9d9d9d;
259
+ --rsm-accent: #3794ff;
260
+ --rsm-hover: #2a2d2e;
261
+ --rsm-surface: #252526;
262
+ /* \u756B\u5E03\u5E95\u8272 = VS Code editor-background(\u6697);grid-dot \u7531 --rsm-fg 9% \u81EA\u52D5\u63A8\u5C0E\u3002 */
263
+ --rsm-paper: #1e1e1e;
248
264
  }
249
265
 
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);
266
+ /* \u2500\u2500 \u80CC\u666F \u2500\u2500 \u5E95\u8272 + \u758A\u52A0\u5716\u6A23,\u5169\u8005\u7368\u7ACB\u3002
267
+ * \u5E95\u8272:--rsm-canvas-bg(\u7531\u8272\u7968 / \u81EA\u8A02\u8272 inline \u8986\u5BEB;\u672A\u8A2D = \u900F\u660E\u8DDF\u96A8\u9801\u9762)\u3002
268
+ * \u5716\u6A23:.rsm-pattern-dots(\u7DB2\u9EDE) / .rsm-pattern-grid(\u7DB2\u683C\u7DDA),\u758A\u5728\u5E95\u8272\u4E4B\u4E0A\u3002 */
269
+ .rsm-root.rsm-pattern-dots .rsm-canvas {
254
270
  background-image: radial-gradient(var(--rsm-grid-dot) 1px, transparent 1px);
255
271
  background-size: 18px 18px;
256
272
  background-position: -9px -9px;
257
273
  }
274
+ .rsm-root.rsm-pattern-grid .rsm-canvas {
275
+ background-image:
276
+ linear-gradient(to right, var(--rsm-grid-line) 1px, transparent 1px),
277
+ linear-gradient(to bottom, var(--rsm-grid-line) 1px, transparent 1px);
278
+ background-size: 22px 22px;
279
+ background-position: -1px -1px;
280
+ }
281
+
282
+ /* \u2500\u2500 \u80CC\u666F\u9078\u64C7\u5668(toolbar \u5167\u7684\u8272\u4E95\u6309\u9215 + \u5F48\u51FA\u9762\u677F)\u2500\u2500 */
283
+ .rsm-bg { position: relative; display: inline-flex; }
284
+
285
+ /* \u89F8\u767C\u9215\u5DE6\u5074\u7684\u300C\u8272\u4E95\u300D:\u53CD\u6620\u76EE\u524D\u5E95\u8272;\u900F\u660E / \u9810\u8A2D\u6642\u756B\u4E00\u9053\u659C\u7DDA\u8868\u793A\u300C\u4E0D\u8986\u5BEB\u300D\u3002 */
286
+ .rsm-bg-well {
287
+ width: 16px;
288
+ height: 16px;
289
+ border-radius: 4px;
290
+ border: 1px solid color-mix(in srgb, var(--rsm-fg) 28%, transparent);
291
+ background-color: var(--rsm-well-color, transparent);
292
+ }
293
+ .rsm-bg-well[data-empty="true"] {
294
+ background-color: var(--rsm-surface);
295
+ background-image: linear-gradient(
296
+ to top right,
297
+ transparent calc(50% - 1px),
298
+ #ef4444 calc(50% - 1px),
299
+ #ef4444 calc(50% + 1px),
300
+ transparent calc(50% + 1px)
301
+ );
302
+ }
303
+
304
+ /* \u5F48\u51FA\u9762\u677F:\u5361\u7247\u5F0F\u3001\u8F15\u9670\u5F71\u3001\u6DE1\u5165\u3002 */
305
+ .rsm-bg-pop {
306
+ position: absolute;
307
+ top: calc(100% + 8px);
308
+ left: 0;
309
+ z-index: 50;
310
+ display: flex;
311
+ flex-direction: column;
312
+ gap: 12px;
313
+ padding: 12px;
314
+ min-width: 260px;
315
+ border: 1px solid var(--rsm-border);
316
+ border-radius: 12px;
317
+ background: var(--rsm-surface);
318
+ color: var(--rsm-fg);
319
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.16), 0 2px 6px rgba(0, 0, 0, 0.08);
320
+ animation: rsm-pop-in 0.13s ease-out;
321
+ }
322
+ @keyframes rsm-pop-in {
323
+ from { opacity: 0; transform: translateY(-5px); }
324
+ to { opacity: 1; transform: none; }
325
+ }
326
+ .rsm-bg-section { display: flex; flex-direction: column; gap: 8px; }
327
+ .rsm-bg-section-label {
328
+ font-size: 11px;
329
+ font-weight: 600;
330
+ letter-spacing: 0.03em;
331
+ color: var(--rsm-muted);
332
+ }
333
+ .rsm-bg-swatches { display: flex; flex-wrap: wrap; gap: 8px; }
334
+
335
+ /* \u8272\u7968:\u5713\u89D2\u5C0F\u65B9\u584A;\u9078\u4E2D\u52A0\u540C\u8272\u5916\u74B0\u3002 */
336
+ .rsm-swatch {
337
+ position: relative;
338
+ width: 26px;
339
+ height: 26px;
340
+ padding: 0;
341
+ border: 1px solid color-mix(in srgb, var(--rsm-fg) 16%, transparent);
342
+ border-radius: 7px;
343
+ cursor: pointer;
344
+ transition: transform 0.1s ease, box-shadow 0.1s ease;
345
+ }
346
+ .rsm-swatch:hover { transform: scale(1.12); }
347
+ .rsm-swatch:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--rsm-accent); }
348
+ .rsm-swatch.rsm-selected { outline: 2px solid var(--rsm-accent); outline-offset: 2px; }
349
+ .rsm-swatch[data-empty="true"] {
350
+ background-color: var(--rsm-surface);
351
+ background-image: linear-gradient(
352
+ to top right,
353
+ transparent calc(50% - 1px),
354
+ #ef4444 calc(50% - 1px),
355
+ #ef4444 calc(50% + 1px),
356
+ transparent calc(50% + 1px)
357
+ );
358
+ }
359
+
360
+ /* \u81EA\u8A02\u8272\u7968:\u8986\u4E00\u500B\u96B1\u5F62\u7684\u539F\u751F color input,\u672A\u9078\u6642\u986F\u793A \u{1F3A8}\u3002 */
361
+ .rsm-swatch-custom {
362
+ display: inline-flex;
363
+ align-items: center;
364
+ justify-content: center;
365
+ overflow: hidden;
366
+ background:
367
+ conic-gradient(from 180deg, #f87171, #fbbf24, #34d399, #60a5fa, #a78bfa, #f87171);
368
+ }
369
+ .rsm-swatch-custom.rsm-has-color { background: none; }
370
+ .rsm-swatch-custom input[type="color"] {
371
+ position: absolute;
372
+ inset: 0;
373
+ width: 100%;
374
+ height: 100%;
375
+ margin: 0;
376
+ padding: 0;
377
+ border: 0;
378
+ opacity: 0;
379
+ cursor: pointer;
380
+ }
381
+ .rsm-swatch-custom-icon {
382
+ font-size: 12px;
383
+ line-height: 1;
384
+ pointer-events: none;
385
+ filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
386
+ }
387
+
388
+ /* \u5716\u6A23\u5207\u63DB:\u5206\u6BB5\u5F0F\u6309\u9215(\u7121 / \u7DB2\u9EDE / \u7DB2\u683C)\u3002 */
389
+ .rsm-seg {
390
+ display: inline-flex;
391
+ align-self: flex-start;
392
+ border: 1px solid var(--rsm-border);
393
+ border-radius: 8px;
394
+ overflow: hidden;
395
+ }
396
+ .rsm-seg > button {
397
+ flex: 1 1 0;
398
+ min-width: 58px;
399
+ display: inline-flex;
400
+ align-items: center;
401
+ justify-content: center;
402
+ gap: 5px;
403
+ border: 0;
404
+ background: var(--rsm-surface);
405
+ color: var(--rsm-fg);
406
+ padding: 6px 11px;
407
+ font-size: 12px;
408
+ line-height: 1.3;
409
+ white-space: nowrap;
410
+ cursor: pointer;
411
+ transition: background 0.1s ease, color 0.1s ease;
412
+ }
413
+ .rsm-seg > button + button { border-left: 1px solid var(--rsm-border); }
414
+ .rsm-seg > button:hover { background: var(--rsm-hover); }
415
+ .rsm-seg > button[aria-pressed="true"] {
416
+ background: color-mix(in srgb, var(--rsm-accent) 14%, transparent);
417
+ color: var(--rsm-accent);
418
+ font-weight: 600;
419
+ }
420
+ .rsm-seg-glyph { font-size: 13px; line-height: 1; }
258
421
 
259
422
  /* \u2500\u2500 \u5168\u87A2\u5E55\u8DF3\u7A97 \u2500\u2500 position:fixed \u8986\u84CB\u6574\u500B\u8996\u7A97,RWD \u53CB\u5584\u3002 */
260
423
  .rsm-root.rsm-fullscreen {
@@ -336,7 +499,11 @@ function ensureStyles() {
336
499
  if (typeof document === "undefined") {
337
500
  return;
338
501
  }
339
- if (document.getElementById(RSM_STYLE_ID)) {
502
+ const existing = document.getElementById(RSM_STYLE_ID);
503
+ if (existing) {
504
+ if (existing.textContent !== RSM_CSS) {
505
+ existing.textContent = RSM_CSS;
506
+ }
340
507
  return;
341
508
  }
342
509
  const style = document.createElement("style");
@@ -1447,7 +1614,7 @@ async function rasterizeToBlob(prepared, opts = {}) {
1447
1614
  throw new Error("\u53D6\u4E0D\u5230 Canvas 2D context\u3002");
1448
1615
  }
1449
1616
  if (!transparent || mime === "image/jpeg") {
1450
- ctx.fillStyle = opts.background ?? (opts.dark ? "#111827" : "#ffffff");
1617
+ ctx.fillStyle = opts.background ?? (opts.dark ? "#1e1e1e" : "#ffffff");
1451
1618
  ctx.fillRect(0, 0, canvas.width, canvas.height);
1452
1619
  }
1453
1620
  ctx.scale(scale, scale);
@@ -1688,11 +1855,19 @@ function useMermaidViewer(opts) {
1688
1855
  getSvg
1689
1856
  };
1690
1857
  }
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
- };
1858
+ var BACKGROUND_PRESETS = [
1859
+ { value: null, label: "\u9810\u8A2D / \u900F\u660E" },
1860
+ { value: "#FFFFFF", label: "\u767D" },
1861
+ { value: "#F3F4F6", label: "\u6DFA\u7070" },
1862
+ { value: "#EFF6FF", label: "\u6DFA\u85CD" },
1863
+ { value: "#FEFCE8", label: "\u6DFA\u9EC3" },
1864
+ { value: "#FDF2F8", label: "\u6DFA\u73AB\u7470" }
1865
+ ];
1866
+ var PATTERN_OPTIONS = [
1867
+ { value: "none", glyph: "\u25A2", label: "\u7121" },
1868
+ { value: "dots", glyph: "\u283F", label: "\u7DB2\u9EDE" },
1869
+ { value: "grid", glyph: "\u229E", label: "\u7DB2\u683C" }
1870
+ ];
1696
1871
  var DEFAULT_THEME_OPTIONS = [
1697
1872
  { value: "colorful", label: "Colorful" },
1698
1873
  { value: "sketch", label: "Excalidraw" },
@@ -1702,6 +1877,126 @@ var DEFAULT_THEME_OPTIONS = [
1702
1877
  { value: "neutral", label: "Neutral" },
1703
1878
  { value: "forest", label: "Forest" }
1704
1879
  ];
1880
+ var HEX6 = /^#[0-9a-fA-F]{6}$/;
1881
+ function isSameColor(a, b) {
1882
+ if (a === null || b === null) {
1883
+ return a === b;
1884
+ }
1885
+ return a.toLowerCase() === b.toLowerCase();
1886
+ }
1887
+ function BackgroundPicker(props) {
1888
+ const [open, setOpen] = useState(false);
1889
+ const rootRef = useRef(null);
1890
+ useEffect(() => {
1891
+ if (!open) {
1892
+ return void 0;
1893
+ }
1894
+ const onDocPointer = (e) => {
1895
+ if (rootRef.current && !rootRef.current.contains(e.target)) {
1896
+ setOpen(false);
1897
+ }
1898
+ };
1899
+ const onKey = (e) => {
1900
+ if (e.key === "Escape") {
1901
+ setOpen(false);
1902
+ }
1903
+ };
1904
+ document.addEventListener("mousedown", onDocPointer);
1905
+ document.addEventListener("keydown", onKey);
1906
+ return () => {
1907
+ document.removeEventListener("mousedown", onDocPointer);
1908
+ document.removeEventListener("keydown", onKey);
1909
+ };
1910
+ }, [open]);
1911
+ const isPreset = BACKGROUND_PRESETS.some((p) => isSameColor(p.value, props.surface));
1912
+ const customActive = props.surface !== null && !isPreset;
1913
+ const customInputValue = props.surface && HEX6.test(props.surface) ? props.surface : "#1e293b";
1914
+ return /* @__PURE__ */ jsxs("div", { className: "rsm-bg", ref: rootRef, children: [
1915
+ /* @__PURE__ */ jsxs(
1916
+ "button",
1917
+ {
1918
+ type: "button",
1919
+ className: "rsm-btn rsm-bg-trigger",
1920
+ "aria-expanded": open,
1921
+ "aria-haspopup": "dialog",
1922
+ onClick: () => setOpen((o) => !o),
1923
+ title: "\u756B\u5E03\u80CC\u666F\uFF08\u5E95\u8272 + \u7DB2\u9EDE / \u7DB2\u683C\uFF0CB\uFF09",
1924
+ children: [
1925
+ /* @__PURE__ */ jsx(
1926
+ "span",
1927
+ {
1928
+ className: "rsm-bg-well",
1929
+ "data-empty": props.surface === null ? "true" : void 0,
1930
+ style: props.surface ? { ["--rsm-well-color"]: props.surface } : void 0
1931
+ }
1932
+ ),
1933
+ "\u80CC\u666F"
1934
+ ]
1935
+ }
1936
+ ),
1937
+ open ? /* @__PURE__ */ jsxs("div", { className: "rsm-bg-pop", role: "dialog", "aria-label": "\u756B\u5E03\u80CC\u666F\u8A2D\u5B9A", children: [
1938
+ /* @__PURE__ */ jsxs("div", { className: "rsm-bg-section", children: [
1939
+ /* @__PURE__ */ jsx("div", { className: "rsm-bg-section-label", children: "\u5E95\u8272" }),
1940
+ /* @__PURE__ */ jsxs("div", { className: "rsm-bg-swatches", children: [
1941
+ BACKGROUND_PRESETS.map((preset) => {
1942
+ const selected = isSameColor(preset.value, props.surface);
1943
+ return /* @__PURE__ */ jsx(
1944
+ "button",
1945
+ {
1946
+ type: "button",
1947
+ className: `rsm-swatch${selected ? " rsm-selected" : ""}`,
1948
+ "data-empty": preset.value === null ? "true" : void 0,
1949
+ style: preset.value ? { backgroundColor: preset.value } : void 0,
1950
+ title: preset.label,
1951
+ "aria-label": `\u5E95\u8272\uFF1A${preset.label}`,
1952
+ "aria-pressed": selected,
1953
+ onClick: () => props.onSurfaceChange(preset.value)
1954
+ },
1955
+ preset.label
1956
+ );
1957
+ }),
1958
+ /* @__PURE__ */ jsxs(
1959
+ "span",
1960
+ {
1961
+ className: `rsm-swatch rsm-swatch-custom${customActive ? " rsm-has-color rsm-selected" : ""}`,
1962
+ style: customActive ? { backgroundColor: props.surface } : void 0,
1963
+ title: "\u81EA\u8A02\u984F\u8272",
1964
+ children: [
1965
+ !customActive ? /* @__PURE__ */ jsx("span", { className: "rsm-swatch-custom-icon", children: "\u{1F3A8}" }) : null,
1966
+ /* @__PURE__ */ jsx(
1967
+ "input",
1968
+ {
1969
+ type: "color",
1970
+ value: customInputValue,
1971
+ "aria-label": "\u81EA\u8A02\u5E95\u8272",
1972
+ onChange: (e) => props.onSurfaceChange(e.target.value)
1973
+ }
1974
+ )
1975
+ ]
1976
+ }
1977
+ )
1978
+ ] })
1979
+ ] }),
1980
+ /* @__PURE__ */ jsxs("div", { className: "rsm-bg-section", children: [
1981
+ /* @__PURE__ */ jsx("div", { className: "rsm-bg-section-label", children: "\u5716\u6A23" }),
1982
+ /* @__PURE__ */ jsx("div", { className: "rsm-seg", role: "group", "aria-label": "\u80CC\u666F\u5716\u6A23", children: PATTERN_OPTIONS.map((opt) => /* @__PURE__ */ jsxs(
1983
+ "button",
1984
+ {
1985
+ type: "button",
1986
+ "aria-pressed": props.pattern === opt.value,
1987
+ title: opt.label,
1988
+ onClick: () => props.onPatternChange(opt.value),
1989
+ children: [
1990
+ /* @__PURE__ */ jsx("span", { className: "rsm-seg-glyph", "aria-hidden": "true", children: opt.glyph }),
1991
+ opt.label
1992
+ ]
1993
+ },
1994
+ opt.value
1995
+ )) })
1996
+ ] })
1997
+ ] }) : null
1998
+ ] });
1999
+ }
1705
2000
  function Toolbar(props) {
1706
2001
  return /* @__PURE__ */ jsxs("div", { className: "rsm-toolbar", children: [
1707
2002
  /* @__PURE__ */ jsxs("label", { className: "rsm-label", children: [
@@ -1716,18 +2011,13 @@ function Toolbar(props) {
1716
2011
  }
1717
2012
  )
1718
2013
  ] }),
1719
- props.backgroundEnabled ? /* @__PURE__ */ jsxs(
1720
- "button",
2014
+ props.backgroundEnabled ? /* @__PURE__ */ jsx(
2015
+ BackgroundPicker,
1721
2016
  {
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
- ]
2017
+ surface: props.surface,
2018
+ onSurfaceChange: props.onSurfaceChange,
2019
+ pattern: props.pattern,
2020
+ onPatternChange: props.onPatternChange
1731
2021
  }
1732
2022
  ) : null,
1733
2023
  /* @__PURE__ */ jsx("div", { className: "rsm-toolbar-spacer" }),
@@ -1797,7 +2087,24 @@ function Toolbar(props) {
1797
2087
  ) : null
1798
2088
  ] });
1799
2089
  }
1800
- var BACKGROUND_CYCLE = ["transparent", "solid", "grid"];
2090
+ var PATTERN_CYCLE = ["none", "dots", "grid"];
2091
+ function defaultSolidColor(dark) {
2092
+ return dark ? "#1e1e1e" : "#ffffff";
2093
+ }
2094
+ function patternInk(surface, dark) {
2095
+ const m = surface ? /^#([0-9a-fA-F]{6})$/.exec(surface) : null;
2096
+ let lightCanvas;
2097
+ if (m) {
2098
+ const r = parseInt(m[1].slice(0, 2), 16);
2099
+ const g = parseInt(m[1].slice(2, 4), 16);
2100
+ const b = parseInt(m[1].slice(4, 6), 16);
2101
+ lightCanvas = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 > 0.5;
2102
+ } else {
2103
+ lightCanvas = !dark;
2104
+ }
2105
+ const ink = lightCanvas ? "15, 23, 42" : "226, 232, 240";
2106
+ return { dot: `rgba(${ink}, 0.34)`, line: `rgba(${ink}, 0.2)` };
2107
+ }
1801
2108
  function usePrefersDark(explicit) {
1802
2109
  const [autoDark, setAutoDark] = useState(false);
1803
2110
  useEffect(() => {
@@ -1847,14 +2154,18 @@ var MermaidViewer = forwardRef(
1847
2154
  const [query, setQuery] = useState("");
1848
2155
  const [matchInfo, setMatchInfo] = useState({ current: 0, total: 0 });
1849
2156
  const [exporting, setExporting] = useState(false);
1850
- const [background, setBackgroundState] = useState(
1851
- props.backgroundMode ?? "transparent"
1852
- );
2157
+ const [pattern, setPatternState] = useState(props.pattern ?? "dots");
2158
+ useEffect(() => {
2159
+ if (props.pattern) {
2160
+ setPatternState(props.pattern);
2161
+ }
2162
+ }, [props.pattern]);
2163
+ const [solidColor, setSolidColorState] = useState(props.solidColor ?? null);
1853
2164
  useEffect(() => {
1854
- if (props.backgroundMode) {
1855
- setBackgroundState(props.backgroundMode);
2165
+ if (props.solidColor !== void 0) {
2166
+ setSolidColorState(props.solidColor);
1856
2167
  }
1857
- }, [props.backgroundMode]);
2168
+ }, [props.solidColor]);
1858
2169
  const [isFullscreen, setIsFullscreen] = useState(false);
1859
2170
  const rootRef = useRef(null);
1860
2171
  const searchInputRef = useRef(null);
@@ -1911,21 +2222,27 @@ var MermaidViewer = forwardRef(
1911
2222
  const exportPng = useCallback(async () => {
1912
2223
  setExporting(true);
1913
2224
  try {
1914
- await vm.downloadPng("diagram.png", { scale: 2 });
2225
+ const paper = solidColor ?? defaultSolidColor(dark);
2226
+ const transparent = solidColor === null && pattern === "none";
2227
+ const bgOpt = transparent ? { transparent: true } : { background: paper };
2228
+ await vm.downloadPng("diagram.png", { scale: 2, ...bgOpt });
1915
2229
  } catch (e) {
1916
2230
  onError?.(e instanceof Error ? e : new Error(String(e)));
1917
2231
  } finally {
1918
2232
  setExporting(false);
1919
2233
  }
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];
2234
+ }, [vm, onError, pattern, solidColor, dark]);
2235
+ const cyclePattern = useCallback(() => {
2236
+ setPatternState((prev) => {
2237
+ const i = PATTERN_CYCLE.indexOf(prev);
2238
+ return PATTERN_CYCLE[(i + 1) % PATTERN_CYCLE.length];
1925
2239
  });
1926
2240
  }, []);
1927
- const setBackground = useCallback((mode) => {
1928
- setBackgroundState(mode);
2241
+ const setPattern = useCallback((next) => {
2242
+ setPatternState(next);
2243
+ }, []);
2244
+ const setSolidColor = useCallback((color) => {
2245
+ setSolidColorState(color);
1929
2246
  }, []);
1930
2247
  const enterFullscreen = useCallback(() => {
1931
2248
  setIsFullscreen((prev) => {
@@ -2045,7 +2362,7 @@ var MermaidViewer = forwardRef(
2045
2362
  toggleFullscreen();
2046
2363
  } else if (backgroundEnabled && (e.key === "b" || e.key === "B")) {
2047
2364
  e.preventDefault();
2048
- cycleBackground();
2365
+ cyclePattern();
2049
2366
  }
2050
2367
  };
2051
2368
  root.addEventListener("keydown", onKey);
@@ -2059,7 +2376,7 @@ var MermaidViewer = forwardRef(
2059
2376
  isFullscreen,
2060
2377
  exitFullscreen,
2061
2378
  toggleFullscreen,
2062
- cycleBackground,
2379
+ cyclePattern,
2063
2380
  fullscreenEnabled,
2064
2381
  backgroundEnabled
2065
2382
  ]);
@@ -2085,9 +2402,11 @@ var MermaidViewer = forwardRef(
2085
2402
  exitFullscreen,
2086
2403
  toggleFullscreen,
2087
2404
  isFullscreen: () => isFullscreen,
2088
- setBackground,
2089
- cycleBackground,
2090
- getBackground: () => background
2405
+ setPattern,
2406
+ cyclePattern,
2407
+ getPattern: () => pattern,
2408
+ setSolidColor,
2409
+ getSolidColor: () => solidColor
2091
2410
  }),
2092
2411
  [
2093
2412
  vm,
@@ -2095,9 +2414,11 @@ var MermaidViewer = forwardRef(
2095
2414
  exitFullscreen,
2096
2415
  toggleFullscreen,
2097
2416
  isFullscreen,
2098
- setBackground,
2099
- cycleBackground,
2100
- background
2417
+ setPattern,
2418
+ cyclePattern,
2419
+ pattern,
2420
+ setSolidColor,
2421
+ solidColor
2101
2422
  ]
2102
2423
  );
2103
2424
  let countText = "";
@@ -2109,16 +2430,23 @@ var MermaidViewer = forwardRef(
2109
2430
  const rootClassName = [
2110
2431
  "rsm-root",
2111
2432
  dark ? "rsm-dark" : "",
2112
- `rsm-bg-${background}`,
2433
+ `rsm-pattern-${pattern}`,
2113
2434
  isFullscreen ? "rsm-fullscreen" : "",
2114
2435
  className ?? ""
2115
2436
  ].filter(Boolean).join(" ");
2437
+ const ink = patternInk(solidColor, dark);
2438
+ const rootStyle = {
2439
+ ...style ?? {},
2440
+ ["--rsm-grid-dot"]: ink.dot,
2441
+ ["--rsm-grid-line"]: ink.line,
2442
+ ...solidColor ? { ["--rsm-canvas-bg"]: solidColor } : {}
2443
+ };
2116
2444
  return /* @__PURE__ */ jsxs(
2117
2445
  "div",
2118
2446
  {
2119
2447
  ref: rootRef,
2120
2448
  className: rootClassName,
2121
- style,
2449
+ style: rootStyle,
2122
2450
  tabIndex: keyboard ? 0 : void 0,
2123
2451
  children: [
2124
2452
  toolbar ? /* @__PURE__ */ jsx(
@@ -2140,8 +2468,10 @@ var MermaidViewer = forwardRef(
2140
2468
  onExportSvg: exportSvg,
2141
2469
  onExportPng: exportPng,
2142
2470
  backgroundEnabled,
2143
- background,
2144
- onCycleBackground: cycleBackground,
2471
+ surface: solidColor,
2472
+ onSurfaceChange: setSolidColor,
2473
+ pattern,
2474
+ onPatternChange: setPattern,
2145
2475
  fullscreenEnabled,
2146
2476
  fullscreen: isFullscreen,
2147
2477
  onToggleFullscreen: toggleFullscreen
@@ -2208,6 +2538,6 @@ var MermaidDiagram = forwardRef(
2208
2538
  }
2209
2539
  );
2210
2540
 
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 };
2541
+ export { BACKGROUND_PRESETS, DEFAULT_THEME_OPTIONS, DEFAULT_VIRGIL_FONT_URL, MermaidDiagram, MermaidViewer, PATTERN_OPTIONS, SKETCH_FONT, Toolbar, boostLegibility, colorizeDiagram, downloadBlob, ensureSketchFont, ensureStyles, loadMermaid, loadSvgPanZoom, prepareSvgElement, prepareSvgString, rasterizeToBlob, renderDiagram, resolveTheme, serializeLiveSvg, sketchifyDiagram, svgBlob, useMermaidViewer };
2212
2542
  //# sourceMappingURL=index.js.map
2213
2543
  //# sourceMappingURL=index.js.map