react-super-mermaid 0.3.0 → 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 {
@@ -310,6 +473,25 @@ var RSM_CSS = `
310
473
  .rsm-zoom > button { padding: 4px 8px; font-size: 12px; }
311
474
  .rsm-input { flex-basis: 150px; }
312
475
  }
476
+
477
+ /* \u2500\u2500 \u6A19\u7C64\u5B57\u91CD(\u91CF\u6E2C\u968E\u6BB5\u5C31\u751F\u6548)\u2500\u2500
478
+ * 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
479
+ * \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
480
+ * \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
481
+ * \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
482
+ * \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
483
+ * \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
484
+ * \u53EA scope \u6211\u5011\u81EA\u5DF1\u6E32\u67D3\u51FA\u7684 svg,\u6545\u4E0D\u6703\u6C61\u67D3 host \u9801\u9762\u5176\u5B83 mermaid\u3002 */
485
+ svg[id^="rsm-"] g.node text,
486
+ svg[id^="rsm-"] g.node tspan,
487
+ svg[id^="rsm-"] g.node .nodeLabel,
488
+ svg[id^="rsm-"] g.mindmap-node text,
489
+ svg[id^="rsm-"] g.mindmap-node .nodeLabel,
490
+ svg[id^="rsm-"] g[class*="timeline-node"] text,
491
+ svg[id^="rsm-"] text.actor { font-weight: 600 !important; }
492
+ svg[id^="rsm-"] .cluster-label text,
493
+ svg[id^="rsm-"] .cluster-label .nodeLabel,
494
+ svg[id^="rsm-"] text.pieTitleText { font-weight: 700 !important; }
313
495
  `;
314
496
 
315
497
  // src/core/ensure-styles.ts
@@ -317,7 +499,11 @@ function ensureStyles() {
317
499
  if (typeof document === "undefined") {
318
500
  return;
319
501
  }
320
- 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
+ }
321
507
  return;
322
508
  }
323
509
  const style = document.createElement("style");
@@ -1428,7 +1614,7 @@ async function rasterizeToBlob(prepared, opts = {}) {
1428
1614
  throw new Error("\u53D6\u4E0D\u5230 Canvas 2D context\u3002");
1429
1615
  }
1430
1616
  if (!transparent || mime === "image/jpeg") {
1431
- ctx.fillStyle = opts.background ?? (opts.dark ? "#111827" : "#ffffff");
1617
+ ctx.fillStyle = opts.background ?? (opts.dark ? "#1e1e1e" : "#ffffff");
1432
1618
  ctx.fillRect(0, 0, canvas.width, canvas.height);
1433
1619
  }
1434
1620
  ctx.scale(scale, scale);
@@ -1669,11 +1855,19 @@ function useMermaidViewer(opts) {
1669
1855
  getSvg
1670
1856
  };
1671
1857
  }
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
- };
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
+ ];
1677
1871
  var DEFAULT_THEME_OPTIONS = [
1678
1872
  { value: "colorful", label: "Colorful" },
1679
1873
  { value: "sketch", label: "Excalidraw" },
@@ -1683,6 +1877,126 @@ var DEFAULT_THEME_OPTIONS = [
1683
1877
  { value: "neutral", label: "Neutral" },
1684
1878
  { value: "forest", label: "Forest" }
1685
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
+ }
1686
2000
  function Toolbar(props) {
1687
2001
  return /* @__PURE__ */ jsxs("div", { className: "rsm-toolbar", children: [
1688
2002
  /* @__PURE__ */ jsxs("label", { className: "rsm-label", children: [
@@ -1697,18 +2011,13 @@ function Toolbar(props) {
1697
2011
  }
1698
2012
  )
1699
2013
  ] }),
1700
- props.backgroundEnabled ? /* @__PURE__ */ jsxs(
1701
- "button",
2014
+ props.backgroundEnabled ? /* @__PURE__ */ jsx(
2015
+ BackgroundPicker,
1702
2016
  {
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
- ]
2017
+ surface: props.surface,
2018
+ onSurfaceChange: props.onSurfaceChange,
2019
+ pattern: props.pattern,
2020
+ onPatternChange: props.onPatternChange
1712
2021
  }
1713
2022
  ) : null,
1714
2023
  /* @__PURE__ */ jsx("div", { className: "rsm-toolbar-spacer" }),
@@ -1778,7 +2087,24 @@ function Toolbar(props) {
1778
2087
  ) : null
1779
2088
  ] });
1780
2089
  }
1781
- 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
+ }
1782
2108
  function usePrefersDark(explicit) {
1783
2109
  const [autoDark, setAutoDark] = useState(false);
1784
2110
  useEffect(() => {
@@ -1828,14 +2154,18 @@ var MermaidViewer = forwardRef(
1828
2154
  const [query, setQuery] = useState("");
1829
2155
  const [matchInfo, setMatchInfo] = useState({ current: 0, total: 0 });
1830
2156
  const [exporting, setExporting] = useState(false);
1831
- const [background, setBackgroundState] = useState(
1832
- props.backgroundMode ?? "transparent"
1833
- );
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);
1834
2164
  useEffect(() => {
1835
- if (props.backgroundMode) {
1836
- setBackgroundState(props.backgroundMode);
2165
+ if (props.solidColor !== void 0) {
2166
+ setSolidColorState(props.solidColor);
1837
2167
  }
1838
- }, [props.backgroundMode]);
2168
+ }, [props.solidColor]);
1839
2169
  const [isFullscreen, setIsFullscreen] = useState(false);
1840
2170
  const rootRef = useRef(null);
1841
2171
  const searchInputRef = useRef(null);
@@ -1892,21 +2222,27 @@ var MermaidViewer = forwardRef(
1892
2222
  const exportPng = useCallback(async () => {
1893
2223
  setExporting(true);
1894
2224
  try {
1895
- 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 });
1896
2229
  } catch (e) {
1897
2230
  onError?.(e instanceof Error ? e : new Error(String(e)));
1898
2231
  } finally {
1899
2232
  setExporting(false);
1900
2233
  }
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];
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];
1906
2239
  });
1907
2240
  }, []);
1908
- const setBackground = useCallback((mode) => {
1909
- setBackgroundState(mode);
2241
+ const setPattern = useCallback((next) => {
2242
+ setPatternState(next);
2243
+ }, []);
2244
+ const setSolidColor = useCallback((color) => {
2245
+ setSolidColorState(color);
1910
2246
  }, []);
1911
2247
  const enterFullscreen = useCallback(() => {
1912
2248
  setIsFullscreen((prev) => {
@@ -2026,7 +2362,7 @@ var MermaidViewer = forwardRef(
2026
2362
  toggleFullscreen();
2027
2363
  } else if (backgroundEnabled && (e.key === "b" || e.key === "B")) {
2028
2364
  e.preventDefault();
2029
- cycleBackground();
2365
+ cyclePattern();
2030
2366
  }
2031
2367
  };
2032
2368
  root.addEventListener("keydown", onKey);
@@ -2040,7 +2376,7 @@ var MermaidViewer = forwardRef(
2040
2376
  isFullscreen,
2041
2377
  exitFullscreen,
2042
2378
  toggleFullscreen,
2043
- cycleBackground,
2379
+ cyclePattern,
2044
2380
  fullscreenEnabled,
2045
2381
  backgroundEnabled
2046
2382
  ]);
@@ -2066,9 +2402,11 @@ var MermaidViewer = forwardRef(
2066
2402
  exitFullscreen,
2067
2403
  toggleFullscreen,
2068
2404
  isFullscreen: () => isFullscreen,
2069
- setBackground,
2070
- cycleBackground,
2071
- getBackground: () => background
2405
+ setPattern,
2406
+ cyclePattern,
2407
+ getPattern: () => pattern,
2408
+ setSolidColor,
2409
+ getSolidColor: () => solidColor
2072
2410
  }),
2073
2411
  [
2074
2412
  vm,
@@ -2076,9 +2414,11 @@ var MermaidViewer = forwardRef(
2076
2414
  exitFullscreen,
2077
2415
  toggleFullscreen,
2078
2416
  isFullscreen,
2079
- setBackground,
2080
- cycleBackground,
2081
- background
2417
+ setPattern,
2418
+ cyclePattern,
2419
+ pattern,
2420
+ setSolidColor,
2421
+ solidColor
2082
2422
  ]
2083
2423
  );
2084
2424
  let countText = "";
@@ -2090,16 +2430,23 @@ var MermaidViewer = forwardRef(
2090
2430
  const rootClassName = [
2091
2431
  "rsm-root",
2092
2432
  dark ? "rsm-dark" : "",
2093
- `rsm-bg-${background}`,
2433
+ `rsm-pattern-${pattern}`,
2094
2434
  isFullscreen ? "rsm-fullscreen" : "",
2095
2435
  className ?? ""
2096
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
+ };
2097
2444
  return /* @__PURE__ */ jsxs(
2098
2445
  "div",
2099
2446
  {
2100
2447
  ref: rootRef,
2101
2448
  className: rootClassName,
2102
- style,
2449
+ style: rootStyle,
2103
2450
  tabIndex: keyboard ? 0 : void 0,
2104
2451
  children: [
2105
2452
  toolbar ? /* @__PURE__ */ jsx(
@@ -2121,8 +2468,10 @@ var MermaidViewer = forwardRef(
2121
2468
  onExportSvg: exportSvg,
2122
2469
  onExportPng: exportPng,
2123
2470
  backgroundEnabled,
2124
- background,
2125
- onCycleBackground: cycleBackground,
2471
+ surface: solidColor,
2472
+ onSurfaceChange: setSolidColor,
2473
+ pattern,
2474
+ onPatternChange: setPattern,
2126
2475
  fullscreenEnabled,
2127
2476
  fullscreen: isFullscreen,
2128
2477
  onToggleFullscreen: toggleFullscreen
@@ -2189,6 +2538,6 @@ var MermaidDiagram = forwardRef(
2189
2538
  }
2190
2539
  );
2191
2540
 
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 };
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 };
2193
2542
  //# sourceMappingURL=index.js.map
2194
2543
  //# sourceMappingURL=index.js.map