@diagrammo/dgmo 0.44.1 → 0.46.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/auto.cjs CHANGED
@@ -5052,10 +5052,7 @@ function renderLegendD3(container, config, state, palette, isDark, callbacks, co
5052
5052
  legendG.selectAll("*").remove();
5053
5053
  if (currentLayout.height === 0) return;
5054
5054
  if (currentState.activeGroup) {
5055
- legendG.attr(
5056
- "data-legend-active",
5057
- currentState.activeGroup.toLowerCase()
5058
- );
5055
+ legendG.attr("data-legend-active", tagAttrKey(currentState.activeGroup));
5059
5056
  } else {
5060
5057
  legendG.attr("data-legend-active", null);
5061
5058
  }
@@ -5862,7 +5859,7 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
5862
5859
  if (currentActiveGroup) {
5863
5860
  legendContainer.attr(
5864
5861
  "data-legend-active",
5865
- currentActiveGroup.toLowerCase()
5862
+ tagAttrKey(currentActiveGroup)
5866
5863
  );
5867
5864
  }
5868
5865
  const iconAddon = viewMode ? 0 : LG_ICON_W;
@@ -25559,7 +25556,7 @@ function renderLegendSvg(groups, options) {
25559
25556
  track(pill.x, pill.y, pill.width, pill.height);
25560
25557
  }
25561
25558
  const classAttr = className ? ` class="${esc(className)}"` : "";
25562
- const activeAttr = activeGroup ? ` data-legend-active="${esc(activeGroup.toLowerCase())}"` : "";
25559
+ const activeAttr = activeGroup ? ` data-legend-active="${esc(tagAttrKey(activeGroup))}"` : "";
25563
25560
  const svg = `<g${classAttr}${activeAttr}>${parts.join("")}</g>`;
25564
25561
  return {
25565
25562
  svg,
@@ -28040,7 +28037,7 @@ var init_renderer2 = __esm({
28040
28037
  COLLAPSED = "dgmo-evt-collapsed";
28041
28038
  OFF = "dgmo-evt-off";
28042
28039
  HIDDEN_ATTR = "data-evt-hidden";
28043
- HOVER_CSS = `.dgmo-event-dot{transition:transform .12s ease;transform-box:fill-box;transform-origin:center}.dgmo-event-leader,.dgmo-event-card,.dgmo-event-era{transition:opacity .12s ease,filter .12s ease}.dgmo-event-dot,.dgmo-event-card,.dgmo-event-era{cursor:default}[data-legend-entry]{cursor:pointer}.${DIM}{opacity:.2}.dgmo-event-dot.${HL}{transform:scale(1.55)}.dgmo-event-leader.${HL}{stroke-opacity:1;stroke-width:2.5}.dgmo-event-card.${HL}{filter:drop-shadow(0 2px 5px rgba(0,0,0,.22))}.dgmo-event-era.${ERA_HL}{filter:drop-shadow(0 1px 2px rgba(0,0,0,.28))}.dgmo-event-card.${COLLAPSED},.dgmo-event-leader.${COLLAPSED}{opacity:0;pointer-events:none}.dgmo-event-card.${COLLAPSED}.${HL},.dgmo-event-leader.${COLLAPSED}.${HL}{opacity:1;pointer-events:auto}[data-legend-entry].${OFF} text{text-decoration:line-through;opacity:.5}`;
28040
+ HOVER_CSS = `.dgmo-event-dot{transition:transform .12s ease;transform-box:fill-box;transform-origin:center}.dgmo-event-leader,.dgmo-event-card,.dgmo-event-era{transition:opacity .12s ease,filter .12s ease}.dgmo-event-dot,.dgmo-event-card,.dgmo-event-era{cursor:default}[data-legend-entry]{cursor:pointer}.${DIM}{opacity:.2}.dgmo-event-dot.${HL}{transform:scale(1.55)}.dgmo-event-leader.${HL}{stroke-opacity:1;stroke-width:2.5}.dgmo-event-card.${HL}{filter:drop-shadow(0 2px 5px rgba(0,0,0,.22))}.dgmo-event-era.${ERA_HL}{filter:drop-shadow(0 1px 2px rgba(0,0,0,.28))}.dgmo-event-card.${COLLAPSED},.dgmo-event-leader.${COLLAPSED}{opacity:0;pointer-events:none}.dgmo-event-card.${COLLAPSED}.${HL},.dgmo-event-leader.${COLLAPSED}.${HL}{opacity:1;pointer-events:auto}[data-legend-entry].${OFF} text{opacity:.5}`;
28044
28041
  DIMMABLE_SEL = ".dgmo-event-dot,.dgmo-event-leader,.dgmo-event-card,.dgmo-event-era";
28045
28042
  PIN_ATTR = "data-evt-pin";
28046
28043
  }
@@ -30874,7 +30871,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
30874
30871
  if (fixedLegend) {
30875
30872
  const legendParent = svg.append("g").attr("class", "sitemap-legend-fixed").attr("transform", `translate(0, ${sDiagramPadding + fixedTitleH})`);
30876
30873
  if (activeTagGroup) {
30877
- legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
30874
+ legendParent.attr("data-legend-active", tagAttrKey(activeTagGroup));
30878
30875
  }
30879
30876
  renderLegend(
30880
30877
  legendParent,
@@ -37268,7 +37265,7 @@ function renderSwimlaneForExport(container, parsed, layout, palette, isDark, opt
37268
37265
  const titleOffset = parsed.title ? 40 : 0;
37269
37266
  const width = opts.exportDims?.width ?? layout.width;
37270
37267
  const height = (opts.exportDims?.height ?? layout.height) + 0;
37271
- const svg = d3Selection14.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).style("font-family", FONT_FAMILY);
37268
+ const svg = d3Selection14.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY);
37272
37269
  const defs = svg.append("defs");
37273
37270
  const baseBg = themeBaseBg(palette, isDark);
37274
37271
  const ev = {
@@ -41296,7 +41293,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
41296
41293
  if (hasLegend) {
41297
41294
  const legendParent = fixedLegend ? svg.append("g").attr("class", "c4-legend-fixed").attr("transform", `translate(0, ${DIAGRAM_PADDING8 + titleHeight})`) : contentG.append("g").attr("class", "c4-legend");
41298
41295
  if (activeTagGroup) {
41299
- legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
41296
+ legendParent.attr("data-legend-active", tagAttrKey(activeTagGroup));
41300
41297
  }
41301
41298
  renderLegend2(
41302
41299
  legendParent,
@@ -41911,7 +41908,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
41911
41908
  if (hasLegend) {
41912
41909
  const legendParent = fixedLegend ? svg.append("g").attr("class", "c4-legend-fixed").attr("transform", `translate(0, ${DIAGRAM_PADDING8 + titleHeight})`) : contentG.append("g").attr("class", "c4-legend");
41913
41910
  if (activeTagGroup) {
41914
- legendParent.attr("data-legend-active", activeTagGroup.toLowerCase());
41911
+ legendParent.attr("data-legend-active", tagAttrKey(activeTagGroup));
41915
41912
  }
41916
41913
  renderLegend2(
41917
41914
  legendParent,
@@ -45584,7 +45581,7 @@ function renderLegend3(rootSvg, legendGroups, totalWidth, legendY, palette, isDa
45584
45581
  const appHostedPlayback = controlsHost === "app" && !!playback;
45585
45582
  const legendG = rootSvg.append("g").attr("transform", `translate(0, ${legendY})`);
45586
45583
  if (activeGroup) {
45587
- legendG.attr("data-legend-active", activeGroup.toLowerCase());
45584
+ legendG.attr("data-legend-active", tagAttrKey(activeGroup));
45588
45585
  }
45589
45586
  const allGroups = legendGroups.map((g) => ({
45590
45587
  name: g.name,
@@ -68658,6 +68655,7 @@ async function exportVisualization(ctx) {
68658
68655
  init_charts_d3();
68659
68656
 
68660
68657
  // src/utils/hover-styles.ts
68658
+ init_tag_groups();
68661
68659
  var MAX_HOVER_GROUPS = 40;
68662
68660
  var DEFAULT_DIM_OPACITY = 0.4;
68663
68661
  var LIFT_DECL = "filter:saturate(1.4) brightness(1.06)";
@@ -69052,7 +69050,7 @@ function deriveFromSvg(svg, spec) {
69052
69050
  if (spec.groupAttrMode === "tag-active") {
69053
69051
  const slug = root.querySelector("[data-legend-active]")?.getAttribute("data-legend-active");
69054
69052
  if (slug) {
69055
- groupAttr = `data-tag-${slug}`;
69053
+ groupAttr = `data-tag-${tagAttrKey(slug)}`;
69056
69054
  } else {
69057
69055
  const names = /* @__PURE__ */ new Set();
69058
69056
  root.querySelectorAll(spec.markSelector).forEach((el) => {
@@ -69107,10 +69105,16 @@ var DOM_GLOBALS = [
69107
69105
  "HTMLElement",
69108
69106
  "SVGElement"
69109
69107
  ];
69110
- var domRefCount = 0;
69111
- var domInstallPromise = null;
69112
- var domInstalledByUs = false;
69113
- async function installDom() {
69108
+ var DOM_STATE_KEY = /* @__PURE__ */ Symbol.for("diagrammo.dgmo.dom-globals");
69109
+ function domState() {
69110
+ const g = globalThis;
69111
+ return g[DOM_STATE_KEY] ??= {
69112
+ refCount: 0,
69113
+ installPromise: null,
69114
+ installedByUs: false
69115
+ };
69116
+ }
69117
+ async function installDom(state) {
69114
69118
  const { JSDOM } = await loadJsdom();
69115
69119
  const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
69116
69120
  url: "http://localhost/"
@@ -69129,23 +69133,25 @@ async function installDom() {
69129
69133
  configurable: true
69130
69134
  });
69131
69135
  }
69132
- domInstalledByUs = true;
69136
+ state.installedByUs = true;
69133
69137
  }
69134
69138
  async function acquireDom() {
69135
- if (typeof document !== "undefined" && !domInstalledByUs) return;
69136
- domRefCount++;
69137
- if (!domInstallPromise) domInstallPromise = installDom();
69138
- await domInstallPromise;
69139
+ const state = domState();
69140
+ if (typeof document !== "undefined" && !state.installedByUs) return;
69141
+ state.refCount++;
69142
+ if (!state.installPromise) state.installPromise = installDom(state);
69143
+ await state.installPromise;
69139
69144
  }
69140
69145
  function releaseDom() {
69141
- if (!domInstalledByUs) return;
69142
- if (--domRefCount > 0) return;
69146
+ const state = domState();
69147
+ if (!state.installedByUs) return;
69148
+ if (--state.refCount > 0) return;
69143
69149
  for (const key of DOM_GLOBALS) {
69144
69150
  delete globalThis[key];
69145
69151
  }
69146
- domInstalledByUs = false;
69147
- domInstallPromise = null;
69148
- domRefCount = 0;
69152
+ state.installedByUs = false;
69153
+ state.installPromise = null;
69154
+ state.refCount = 0;
69149
69155
  }
69150
69156
  async function loadJsdom() {
69151
69157
  const spec = ["js", "dom"].join("");
@@ -69270,6 +69276,9 @@ function encodeDiagramUrl(dsl, options) {
69270
69276
  return { url: `${baseUrl}?${hash}#${hash}` };
69271
69277
  }
69272
69278
 
69279
+ // src/embed/index.ts
69280
+ init_palettes();
69281
+
69273
69282
  // src/editor/dgmo.grammar.js
69274
69283
  var import_lr = require("@lezer/lr");
69275
69284
 
@@ -69652,230 +69661,426 @@ function markTokensInRange(tokens, from, to, role) {
69652
69661
  }
69653
69662
  }
69654
69663
 
69655
- // src/auto/styles.ts
69656
- var CSS = `
69657
- /* anti-flash: hide source elements until either render swaps them out
69658
- or the error path explicitly un-hides them */
69659
- pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
69660
- .dgmo:not(svg *):not(.dgmo-rendered):not(.dgmo-rendered *) {
69661
- visibility: hidden;
69664
+ // src/embed/escape.ts
69665
+ var HTML_ENTITIES = {
69666
+ "&": "&amp;",
69667
+ "<": "&lt;",
69668
+ ">": "&gt;",
69669
+ '"': "&quot;",
69670
+ "'": "&#39;"
69671
+ };
69672
+ function escapeHtml3(s) {
69673
+ return s.replace(/[&<>"']/g, (ch) => HTML_ENTITIES[ch] ?? ch);
69662
69674
  }
69663
- .dgmo-rendered, .dgmo-rendered *,
69664
- .dgmo-error-banner, .dgmo-error-banner * {
69665
- visibility: visible !important;
69675
+ function escapeAttr(s) {
69676
+ return s.replace(/[&<>"']/g, (ch) => HTML_ENTITIES[ch] ?? ch);
69666
69677
  }
69667
69678
 
69668
- .dgmo-rendered {
69679
+ // src/embed/css.ts
69680
+ var BLOCK_CSS = `
69681
+ /* @diagrammo/dgmo block \u2014 standard embed chrome.
69682
+ * Override the [data-theme="dark"] selectors if your site uses .dark or
69683
+ * another color-mode convention. */
69684
+
69685
+ /* === Color-mode visibility (dual-render) === */
69686
+ .dgmo-dark {
69687
+ display: none;
69688
+ }
69689
+ [data-theme="dark"] .dgmo-light {
69690
+ display: none;
69691
+ }
69692
+ [data-theme="dark"] .dgmo-dark {
69669
69693
  display: block;
69670
- position: relative;
69671
- margin: 1em 0;
69672
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
69673
- Oxygen, Ubuntu, Cantarell, sans-serif;
69674
- font-size: 14px;
69675
- line-height: 1.4;
69676
- color-scheme: light dark;
69677
69694
  }
69678
- .dgmo-rendered svg {
69695
+
69696
+ /* === Diagram sizing ===
69697
+ * SVGs are emitted with viewBox only (no fixed width/height). Force them to
69698
+ * fill their wrapper so light and dark variants render at identical sizes. */
69699
+ .dgmo-light > svg,
69700
+ .dgmo-dark > svg,
69701
+ .dgmo-svg > svg {
69679
69702
  display: block;
69680
- max-width: 100%;
69703
+ width: 100%;
69681
69704
  height: auto;
69705
+ max-width: 100%;
69682
69706
  }
69683
69707
 
69684
- .dgmo-source-panel {
69685
- margin-top: 4px;
69686
- position: relative;
69708
+ /* === Toolbar ===
69709
+ * The diagram is the star: the toolbar is a slim reserved row below the SVG
69710
+ * (height always in flow \u2014 no layout shift) that stays fully transparent
69711
+ * until the block is hovered or focused, or the source panel is open. It is
69712
+ * the <summary> of the source <details>, so toggling works without any JS.
69713
+ * No text labels, no disclosure triangle \u2014 three wordless icon buttons. */
69714
+ .dgmo-source-wrap {
69715
+ margin: 0;
69716
+ }
69717
+ .dgmo-toolbar {
69718
+ display: flex;
69719
+ align-items: center;
69720
+ justify-content: flex-end;
69721
+ gap: 0.25em;
69722
+ min-height: 1.9em;
69723
+ padding: 0.15em 0;
69724
+ opacity: 0;
69725
+ transition: opacity 0.15s ease;
69726
+ cursor: pointer;
69727
+ list-style: none;
69728
+ user-select: none;
69729
+ color: rgba(127, 127, 127, 0.9);
69730
+ }
69731
+ .dgmo-toolbar::-webkit-details-marker {
69732
+ display: none;
69733
+ }
69734
+ /* Reveal is keyed to the diagram IMAGE (the svg wrappers), not the whole
69735
+ * figure \u2014 hovering the open code panel keeps the page quiet. The toolbar's
69736
+ * own :hover keeps it alive while the pointer travels down from the image
69737
+ * (they're adjacent, so there's no dead gap). focus-within covers keyboard
69738
+ * and touch-tap. */
69739
+ .dgmo-light:hover ~ .dgmo-source-wrap > .dgmo-toolbar,
69740
+ .dgmo-dark:hover ~ .dgmo-source-wrap > .dgmo-toolbar,
69741
+ .dgmo-svg:hover ~ .dgmo-source-wrap > .dgmo-toolbar,
69742
+ .dgmo-toolbar:hover,
69743
+ .dgmo:focus-within .dgmo-toolbar {
69744
+ opacity: 1;
69745
+ }
69746
+ /* Touch devices have no hover: keep the icons faintly visible so the
69747
+ * affordance is discoverable at all. */
69748
+ @media (hover: none) {
69749
+ .dgmo-toolbar {
69750
+ opacity: 0.5;
69751
+ }
69687
69752
  }
69688
69753
 
69689
- .dgmo-source-toggle {
69754
+ .dgmo-toggle,
69755
+ .dgmo-toolbar-btn {
69690
69756
  display: inline-flex;
69691
69757
  align-items: center;
69692
- gap: 4px;
69693
- /* Visual size is small; the 28px hit area + padding still meets
69694
- the 44 \xD7 44 minimum touch target across most stylesheets, and
69695
- we widen padding on coarse pointers below. */
69696
- min-height: 28px;
69697
- padding: 4px 6px;
69698
- background: transparent;
69758
+ justify-content: center;
69759
+ width: 1.9em;
69760
+ height: 1.9em;
69761
+ border-radius: 0.35em;
69762
+ background: none;
69699
69763
  border: 0;
69700
- font: inherit;
69701
- font-size: 11px;
69702
- font-weight: 500;
69703
- letter-spacing: 0.02em;
69704
- text-align: left;
69705
- cursor: pointer;
69764
+ padding: 0;
69706
69765
  color: inherit;
69707
- opacity: 0.45;
69708
- border-radius: 3px;
69709
- transition: opacity 120ms ease, background 120ms ease;
69710
- }
69711
- .dgmo-source-toggle:hover {
69712
- opacity: 0.85;
69713
- background: rgba(127, 127, 127, 0.08);
69766
+ cursor: pointer;
69767
+ opacity: 0.75;
69768
+ transition:
69769
+ opacity 0.15s ease,
69770
+ background-color 0.15s ease;
69714
69771
  }
69715
- .dgmo-source-toggle:focus-visible {
69772
+ .dgmo-toggle:hover,
69773
+ .dgmo-toolbar-btn:hover {
69716
69774
  opacity: 1;
69717
- outline: 2px solid currentColor;
69718
- outline-offset: 1px;
69775
+ background: rgba(127, 127, 127, 0.12);
69719
69776
  }
69720
- .dgmo-source-toggle[aria-expanded="true"] {
69721
- opacity: 0.7;
69777
+ .dgmo-toggle svg,
69778
+ .dgmo-toolbar-btn svg {
69779
+ width: 1em;
69780
+ height: 1em;
69722
69781
  }
69723
- .dgmo-source-toggle .dgmo-chevron {
69724
- display: inline-block;
69725
- width: 8px;
69726
- height: 8px;
69727
- font-size: 8px;
69728
- line-height: 1;
69729
- transition: transform 150ms ease-out;
69782
+ /* Active state while the source panel is open. */
69783
+ .dgmo-source-wrap[open] > .dgmo-toolbar .dgmo-toggle {
69784
+ opacity: 1;
69785
+ background: rgba(127, 127, 127, 0.15);
69730
69786
  }
69731
- .dgmo-source-toggle[aria-expanded="true"] .dgmo-chevron {
69732
- transform: rotate(90deg);
69787
+ .dgmo-copy--success {
69788
+ color: #5cba7a;
69789
+ opacity: 1;
69733
69790
  }
69734
- @media (pointer: coarse) {
69735
- /* Larger hit area on touch devices without making the visible
69736
- toggle bigger on the desktop. */
69737
- .dgmo-source-toggle {
69738
- min-height: 44px;
69739
- padding: 12px 10px;
69740
- }
69791
+
69792
+ /* === Open state: frame the whole block ===
69793
+ * When the source panel is open, diagram + toolbar + code get one shared
69794
+ * border so the code visibly belongs to the chart above it, not to the
69795
+ * surrounding prose. Closed blocks stay frameless. */
69796
+ .dgmo:has(> .dgmo-source-wrap[open]) {
69797
+ border: 1px solid rgba(127, 127, 127, 0.3);
69798
+ border-radius: 0.6em;
69799
+ padding: 0.75em 0.75em 0.5em;
69741
69800
  }
69742
69801
 
69743
- .dgmo-source-body {
69802
+ /* === Source panel (hidden until toggled) === */
69803
+ .dgmo-source-inner {
69804
+ border-top: 1px solid rgba(127, 127, 127, 0.25);
69805
+ background: rgba(127, 127, 127, 0.06);
69744
69806
  overflow: hidden;
69745
- max-height: 0;
69746
- transition: max-height 150ms ease-out;
69747
- position: relative;
69807
+ margin: 0 -0.75em -0.5em;
69808
+ border-radius: 0 0 0.6em 0.6em;
69809
+ }
69810
+ .dgmo-pre {
69811
+ white-space: pre;
69812
+ overflow-x: auto;
69813
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
69814
+ font-size: 0.85em;
69815
+ line-height: 1.5;
69816
+ margin: 0;
69817
+ padding: 1em;
69748
69818
  }
69749
- .dgmo-source-body.dgmo-open {
69750
- max-height: 60vh;
69751
- overflow: auto;
69752
- margin-top: 2px;
69753
- border-radius: 4px;
69754
- background: rgba(127, 127, 127, 0.04);
69819
+ .dgmo-code {
69820
+ display: block;
69755
69821
  }
69756
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-body.dgmo-open {
69757
- background: rgba(255, 255, 255, 0.03);
69822
+
69823
+ /* === Error card === */
69824
+ .dgmo--error {
69825
+ border: 1px solid rgba(220, 80, 80, 0.5);
69826
+ border-radius: 0.5em;
69827
+ padding: 0.75em 1em;
69758
69828
  }
69759
- .dgmo-source-pre {
69760
- margin: 0;
69761
- /* Right padding leaves room for the floating action icons. */
69762
- padding: 10px 56px 10px 14px;
69763
- font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
69764
- font-size: 12px;
69765
- line-height: 1.45;
69829
+ .dgmo--error pre {
69766
69830
  white-space: pre;
69767
- overflow: auto;
69768
- background: transparent;
69769
- color: inherit;
69831
+ overflow-x: auto;
69832
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
69833
+ font-size: 0.85em;
69834
+ margin: 0.5em 0 0;
69770
69835
  }
69771
69836
 
69772
- .dgmo-source-actions {
69773
- position: absolute;
69774
- top: 6px;
69775
- right: 6px;
69776
- display: flex;
69777
- gap: 2px;
69778
- /* Inherit visibility:hidden when collapsed; pointer-events:none keeps
69779
- them off the tab order until the panel opens. */
69780
- pointer-events: none;
69837
+ /* === Source highlight roles ===
69838
+ * Light values mirror LIGHT_ROLE_STYLES; dark overrides mirror
69839
+ * NORD_ROLE_STYLES. Guarded by the parity test in tests/block.test.ts. */
69840
+ .dgmo-tok-keyword {
69841
+ color: #3b6ea5;
69842
+ font-weight: bold;
69781
69843
  }
69782
- .dgmo-source-body.dgmo-open .dgmo-source-actions {
69783
- pointer-events: auto;
69844
+ .dgmo-tok-controlKeyword {
69845
+ color: #7d5ba6;
69846
+ font-weight: bold;
69784
69847
  }
69785
- .dgmo-btn {
69786
- display: inline-flex;
69787
- align-items: center;
69788
- justify-content: center;
69789
- width: 26px;
69790
- height: 26px;
69791
- padding: 0;
69792
- background: transparent;
69793
- color: inherit;
69794
- border: 0;
69795
- border-radius: 3px;
69796
- cursor: pointer;
69797
- text-decoration: none;
69798
- opacity: 0.4;
69799
- transition: opacity 120ms ease, background 120ms ease, color 120ms ease;
69848
+ .dgmo-tok-definitionKeyword {
69849
+ color: #2f5a86;
69850
+ font-weight: bold;
69800
69851
  }
69801
- .dgmo-btn:hover {
69802
- opacity: 1;
69803
- background: rgba(127, 127, 127, 0.15);
69852
+ .dgmo-tok-modifier {
69853
+ color: #7d5ba6;
69804
69854
  }
69805
- .dgmo-btn:focus-visible {
69806
- opacity: 1;
69807
- outline: 2px solid currentColor;
69808
- outline-offset: 1px;
69855
+ .dgmo-tok-chartType {
69856
+ color: #b8691f;
69857
+ font-weight: bold;
69809
69858
  }
69810
- .dgmo-btn[aria-disabled="true"] {
69811
- opacity: 0.25;
69812
- cursor: not-allowed;
69859
+ .dgmo-tok-operator {
69860
+ color: #c0504d;
69861
+ font-weight: bold;
69813
69862
  }
69814
- .dgmo-btn[aria-disabled="true"]:hover {
69815
- background: transparent;
69863
+ .dgmo-tok-number {
69864
+ color: #7d5ba6;
69816
69865
  }
69817
- .dgmo-btn-copied {
69818
- opacity: 1 !important;
69819
- color: rgb(120, 200, 120);
69866
+ .dgmo-tok-comment {
69867
+ color: #677483;
69868
+ font-style: italic;
69820
69869
  }
69821
- .dgmo-btn svg {
69822
- width: 14px;
69823
- height: 14px;
69824
- display: block;
69870
+ .dgmo-tok-heading {
69871
+ color: #b8691f;
69872
+ font-weight: bold;
69873
+ }
69874
+ .dgmo-tok-bracket {
69875
+ color: #5b6672;
69876
+ }
69877
+ .dgmo-tok-separator {
69878
+ color: #2d7268;
69879
+ }
69880
+ .dgmo-tok-deprecatedSyntax {
69881
+ color: #c0504d;
69882
+ text-decoration: line-through;
69883
+ }
69884
+ .dgmo-tok-url {
69885
+ color: #3b6ea5;
69886
+ text-decoration: underline;
69887
+ }
69888
+ .dgmo-tok-colorAnnotation {
69889
+ color: #b8691f;
69890
+ font-style: italic;
69891
+ }
69892
+ .dgmo-tok-punctuation {
69893
+ color: #677483;
69894
+ }
69895
+ .dgmo-tok-propertyName {
69896
+ color: #2d7268;
69897
+ }
69898
+ .dgmo-tok-string {
69899
+ color: #3f7a3b;
69900
+ }
69901
+ .dgmo-tok-noteContent {
69902
+ color: #677483;
69903
+ font-style: italic;
69904
+ }
69905
+
69906
+ [data-theme="dark"] .dgmo-tok-keyword {
69907
+ color: #81a1c1;
69908
+ font-weight: bold;
69909
+ }
69910
+ [data-theme="dark"] .dgmo-tok-controlKeyword {
69911
+ color: #b48ead;
69912
+ font-weight: bold;
69913
+ }
69914
+ [data-theme="dark"] .dgmo-tok-definitionKeyword {
69915
+ color: #5e81ac;
69916
+ font-weight: bold;
69917
+ }
69918
+ [data-theme="dark"] .dgmo-tok-modifier {
69919
+ color: #b48ead;
69920
+ }
69921
+ [data-theme="dark"] .dgmo-tok-chartType {
69922
+ color: #d08770;
69923
+ font-weight: bold;
69924
+ }
69925
+ [data-theme="dark"] .dgmo-tok-operator {
69926
+ color: #bf616a;
69927
+ font-weight: bold;
69928
+ }
69929
+ [data-theme="dark"] .dgmo-tok-number {
69930
+ color: #b48ead;
69931
+ }
69932
+ [data-theme="dark"] .dgmo-tok-comment {
69933
+ color: #616e88;
69934
+ font-style: italic;
69935
+ }
69936
+ [data-theme="dark"] .dgmo-tok-heading {
69937
+ color: #d08770;
69938
+ font-weight: bold;
69939
+ }
69940
+ [data-theme="dark"] .dgmo-tok-bracket {
69941
+ color: #5e81ac;
69942
+ }
69943
+ [data-theme="dark"] .dgmo-tok-separator {
69944
+ color: #88c0d0;
69945
+ }
69946
+ [data-theme="dark"] .dgmo-tok-deprecatedSyntax {
69947
+ color: #bf616a;
69948
+ text-decoration: line-through;
69949
+ }
69950
+ [data-theme="dark"] .dgmo-tok-url {
69951
+ color: #88c0d0;
69952
+ text-decoration: underline;
69953
+ }
69954
+ [data-theme="dark"] .dgmo-tok-colorAnnotation {
69955
+ color: #d08770;
69956
+ font-style: italic;
69957
+ }
69958
+ [data-theme="dark"] .dgmo-tok-punctuation {
69959
+ color: #616e88;
69960
+ }
69961
+ [data-theme="dark"] .dgmo-tok-propertyName {
69962
+ color: #88c0d0;
69963
+ }
69964
+ [data-theme="dark"] .dgmo-tok-string {
69965
+ color: #a3be8c;
69966
+ }
69967
+ [data-theme="dark"] .dgmo-tok-noteContent {
69968
+ color: #616e88;
69969
+ font-style: italic;
69970
+ }
69971
+ `;
69972
+
69973
+ // src/embed/index.ts
69974
+ var EDITOR_BASE_URL = "https://online.diagrammo.app";
69975
+ function resolveBlockOptions(opts) {
69976
+ const mode = opts.mode ?? "diagram";
69977
+ const showcase = mode === "showcase";
69978
+ return {
69979
+ mode,
69980
+ palette: opts.palette ?? "slate",
69981
+ colorMode: opts.colorMode ?? "auto",
69982
+ showSource: opts.showSource ?? showcase,
69983
+ showCopy: opts.showCopy ?? showcase,
69984
+ showOpenInEditor: opts.showOpenInEditor ?? showcase,
69985
+ editorBaseUrl: opts.editorBaseUrl ?? EDITOR_BASE_URL,
69986
+ wrapper: opts.wrapper ?? "figure",
69987
+ className: opts.className ?? "dgmo",
69988
+ legacyClassNames: opts.legacyClassNames ?? [],
69989
+ title: opts.title,
69990
+ onWarn: opts.onWarn ?? ((message) => console.warn(message))
69991
+ };
69992
+ }
69993
+ function buildDgmoBlockHtml(source, svgsHtml, options = {}) {
69994
+ return assembleBlock(source.trim(), svgsHtml, resolveBlockOptions(options));
69995
+ }
69996
+ function errorBlockHtml(err, source, options = {}) {
69997
+ const msg = err instanceof Error ? err.message : "Failed to render dgmo block.";
69998
+ const base = options.className ?? "dgmo";
69999
+ const legacy = (options.legacyClassNames ?? []).join(" ");
70000
+ const cls = legacy ? `${base} ${legacy} ${base}--error` : `${base} ${base}--error`;
70001
+ return `<div class="${escapeAttr(cls)}" role="alert"><strong>dgmo render error:</strong> ${escapeHtml3(msg)}<pre>${escapeHtml3(source)}</pre></div>`;
70002
+ }
70003
+ function wrapperClasses(opts, variant) {
70004
+ const base = `${opts.className} ${opts.className}--${variant}`;
70005
+ const legacy = opts.legacyClassNames.join(" ");
70006
+ return legacy ? `${base} ${legacy}` : base;
70007
+ }
70008
+ function assembleBlock(source, svgsHtml, opts) {
70009
+ const Wrapper = opts.wrapper;
70010
+ const showcase = opts.mode === "showcase";
70011
+ const variant = showcase ? "showcase" : "diagram";
70012
+ const ariaAttr = opts.title ? ` aria-label="${escapeAttr(opts.title)}"` : "";
70013
+ const sourceHtml = showcase ? sourceDisclosure(source, opts) : "";
70014
+ return `<${Wrapper} class="${escapeAttr(wrapperClasses(opts, variant))}"${ariaAttr}>` + svgsHtml + sourceHtml + `</${Wrapper}>`;
70015
+ }
70016
+ var CODE_ICON = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m10 4.5 4 3.5-4 3.5"/><path d="m6 4.5-4 3.5 4 3.5"/></svg>`;
70017
+ var COPY_ICON = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3a1.5 1.5 0 0 0-1.5-1.5H3A1.5 1.5 0 0 0 1.5 3v6A1.5 1.5 0 0 0 3 10.5h2.5"/></svg>`;
70018
+ var EXTERNAL_ICON = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.5 2.5h4v4"/><path d="M13.5 2.5 7 9"/><path d="M12.5 9.5v3a1 1 0 0 1-1 1h-8a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h3"/></svg>`;
70019
+ function sourceDisclosure(source, opts) {
70020
+ if (!opts.showSource) return "";
70021
+ const toggle = `<span class="dgmo-toggle" title="View DGMO source">${CODE_ICON}</span>`;
70022
+ const copyButton = opts.showCopy ? `<button type="button" class="dgmo-toolbar-btn dgmo-copy" aria-label="Copy DGMO source" title="Copy source" data-dgmo-source="${escapeAttr(source)}">${COPY_ICON}</button>` : "";
70023
+ let openButton = "";
70024
+ if (opts.showOpenInEditor) {
70025
+ const encoded = encodeDiagramUrl(source, { baseUrl: opts.editorBaseUrl });
70026
+ if (encoded.url) {
70027
+ openButton = `<a href="${escapeAttr(encoded.url)}" target="_blank" rel="noopener noreferrer" class="dgmo-toolbar-btn dgmo-open" aria-label="Open in online editor" title="Open in online editor">${EXTERNAL_ICON}</a>`;
70028
+ }
70029
+ }
70030
+ const sourceHtml = highlightedSource(source);
70031
+ return `<details class="dgmo-source-wrap"><summary class="dgmo-toolbar" aria-label="View DGMO source">${toggle}${copyButton}${openButton}</summary><div class="dgmo-source-inner">${sourceHtml}</div></details>`;
70032
+ }
70033
+ function highlightedSource(source) {
70034
+ const tokens = highlightDgmo(source);
70035
+ const inner = tokens.map((t) => {
70036
+ const text = escapeHtml3(t.text);
70037
+ if (!t.role || t.role === "default") return text;
70038
+ return `<span class="dgmo-tok-${escapeAttr(t.role)}">${text}</span>`;
70039
+ }).join("");
70040
+ return `<pre class="dgmo-pre"><span class="dgmo-code">${inner}</span></pre>`;
70041
+ }
70042
+
70043
+ // src/auto/styles.ts
70044
+ var AUTO_BASE_CSS = `
70045
+ /* anti-flash: hide source elements until either render swaps them out
70046
+ or the error path replaces them with the standard error card */
70047
+ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
70048
+ .dgmo:not(svg *):not(.dgmo-rendered):not(.dgmo-rendered *):not(.dgmo--error):not(.dgmo--error *) {
70049
+ visibility: hidden;
70050
+ }
70051
+ .dgmo-rendered, .dgmo-rendered *,
70052
+ .dgmo--error, .dgmo--error * {
70053
+ visibility: visible !important;
69825
70054
  }
69826
70055
 
69827
- .dgmo-error-banner {
70056
+ .dgmo-rendered {
69828
70057
  display: block;
69829
- margin: 0.5em 0;
69830
- padding: 10px 14px;
69831
- border-left: 4px solid #c44;
69832
- background: rgba(204, 68, 68, 0.08);
69833
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
69834
- font-size: 13px;
70058
+ position: relative;
70059
+ margin: 1em 0;
70060
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
70061
+ Oxygen, Ubuntu, Cantarell, sans-serif;
70062
+ font-size: 14px;
69835
70063
  line-height: 1.4;
69836
- color: inherit;
69837
- border-radius: 0 4px 4px 0;
69838
- }
69839
- .dgmo-error-banner-title {
69840
- font-weight: 600;
69841
- margin-bottom: 2px;
70064
+ color-scheme: light dark;
69842
70065
  }
69843
- .dgmo-error-banner-loc {
69844
- opacity: 0.7;
70066
+
70067
+ .dgmo--loading {
70068
+ opacity: 0.6;
69845
70069
  font-size: 12px;
69846
70070
  font-family: ui-monospace, monospace;
69847
70071
  }
69848
-
69849
- /* Token roles \u2014 fallback colors used when theme classes don't override */
69850
- .dgmo-source-pre .dgmo-tok-keyword { color: #5e81ac; font-weight: 600; }
69851
- .dgmo-source-pre .dgmo-tok-controlKeyword { color: #b48ead; font-weight: 600; }
69852
- .dgmo-source-pre .dgmo-tok-definitionKeyword { color: #5e81ac; font-weight: 600; }
69853
- .dgmo-source-pre .dgmo-tok-modifier { color: #b48ead; }
69854
- .dgmo-source-pre .dgmo-tok-chartType { color: #d08770; font-weight: 600; }
69855
- .dgmo-source-pre .dgmo-tok-operator { color: #bf616a; font-weight: 600; }
69856
- .dgmo-source-pre .dgmo-tok-number { color: #b48ead; }
69857
- .dgmo-source-pre .dgmo-tok-comment { color: #6c7a96; font-style: italic; }
69858
- .dgmo-source-pre .dgmo-tok-heading { color: #d08770; font-weight: 600; }
69859
- .dgmo-source-pre .dgmo-tok-bracket { color: #5e81ac; }
69860
- .dgmo-source-pre .dgmo-tok-separator { color: #88c0d0; }
69861
- .dgmo-source-pre .dgmo-tok-url { color: #88c0d0; text-decoration: underline; }
69862
- .dgmo-source-pre .dgmo-tok-colorAnnotation { color: #d08770; font-style: italic; }
69863
- .dgmo-source-pre .dgmo-tok-punctuation { color: #6c7a96; }
69864
- .dgmo-source-pre .dgmo-tok-noteContent { color: #6c7a96; font-style: italic; }
69865
-
69866
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-keyword,
69867
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-definitionKeyword { color: #81a1c1; }
69868
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-comment,
69869
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-noteContent,
69870
- .dgmo-rendered.dgmo-theme-dark .dgmo-source-pre .dgmo-tok-punctuation { color: #616e88; }
69871
70072
  `;
70073
+ function darkScopedBlockCss(css) {
70074
+ const rules = css.match(/\[data-theme="dark"\][^{]*\{[^}]*\}/g) ?? [];
70075
+ return rules.map((rule) => rule.replace(/\[data-theme="dark"\]/g, ".dgmo-theme-dark")).join("\n");
70076
+ }
70077
+ var CSS = AUTO_BASE_CSS + "\n" + BLOCK_CSS + "\n" + darkScopedBlockCss(BLOCK_CSS) + "\n";
69872
70078
 
69873
70079
  // src/auto/shared.ts
69874
70080
  init_safe_href();
69875
- var VERSION = "0.44.1";
69876
- var EDITOR_BASE_URL = "https://online.diagrammo.app";
70081
+ var VERSION = "0.46.0";
69877
70082
  var ARIA_LABEL_MAX = 200;
69878
- var COPIED_INTERACTION_MS = 1200;
70083
+ var COPIED_INTERACTION_MS = 1500;
69879
70084
  var LOG_PREFIX = "[dgmo]";
69880
70085
  var ARIA_STRIP_RE = /[\x00-\x1F‪-‮⁦-⁩]/g;
69881
70086
  var STYLE_FLAG = "dgmoAutoStyles";
@@ -69944,139 +70149,98 @@ function deriveAriaLabel(source) {
69944
70149
  if (!firstLine) return "DGMO diagram";
69945
70150
  return firstLine.replace(ARIA_STRIP_RE, "").slice(0, ARIA_LABEL_MAX);
69946
70151
  }
69947
- function buildHighlightedSource(source) {
69948
- const frag = document.createDocumentFragment();
69949
- let tokens;
69950
- try {
69951
- tokens = highlightDgmo(source);
69952
- } catch {
69953
- frag.appendChild(document.createTextNode(source));
69954
- return frag;
69955
- }
69956
- for (const tok of tokens) {
69957
- if (!tok.text) continue;
69958
- const span = document.createElement("span");
69959
- span.className = `dgmo-tok-${tok.role}`;
69960
- span.textContent = tok.text;
69961
- frag.appendChild(span);
69962
- }
69963
- return frag;
69964
- }
69965
- var COPY_ICON_SVG = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.5" y="5.5" width="8" height="8" rx="1.5"/><path d="M10.5 5.5V3a1.5 1.5 0 0 0-1.5-1.5H3A1.5 1.5 0 0 0 1.5 3v6A1.5 1.5 0 0 0 3 10.5h2.5"/></svg>';
69966
- var CHECK_ICON_SVG = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 8.5 6.5 12 13 4.5"/></svg>';
69967
- var EXTERNAL_ICON_SVG = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 2h5v5"/><path d="M14 2L7 9"/><path d="M13 9v4a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h4"/></svg>';
69968
- function setIcon(el, svg) {
69969
- el.innerHTML = svg;
69970
- }
69971
- function buildSourcePanel(source, shareUrl, showEditorLink) {
69972
- const panel = document.createElement("div");
69973
- panel.className = "dgmo-source-panel";
69974
- const toggle = document.createElement("button");
69975
- toggle.type = "button";
69976
- toggle.className = "dgmo-source-toggle";
69977
- toggle.setAttribute("aria-expanded", "false");
69978
- const chevron = document.createElement("span");
69979
- chevron.className = "dgmo-chevron";
69980
- chevron.setAttribute("aria-hidden", "true");
69981
- chevron.textContent = "\u25B8";
69982
- toggle.appendChild(chevron);
69983
- const label = document.createElement("span");
69984
- label.textContent = "DGMO source";
69985
- toggle.appendChild(label);
69986
- const body = document.createElement("div");
69987
- body.className = "dgmo-source-body";
69988
- const pre = document.createElement("pre");
69989
- pre.className = "dgmo-source-pre";
69990
- pre.appendChild(buildHighlightedSource(source));
69991
- body.appendChild(pre);
69992
- const actions = document.createElement("div");
69993
- actions.className = "dgmo-source-actions";
69994
- const copyBtn = document.createElement("button");
69995
- copyBtn.type = "button";
69996
- copyBtn.className = "dgmo-btn dgmo-btn-copy";
69997
- copyBtn.setAttribute("aria-label", "Copy DGMO source");
69998
- copyBtn.title = "Copy source";
69999
- setIcon(copyBtn, COPY_ICON_SVG);
70000
- copyBtn.addEventListener("click", () => {
70001
- void copySource(source, copyBtn);
70152
+ function buildRenderedBlock(opts) {
70153
+ const html = buildDgmoBlockHtml(opts.source, '<div class="dgmo-svg"></div>', {
70154
+ mode: opts.showSource ? "showcase" : "diagram",
70155
+ showOpenInEditor: opts.showSource && opts.showEditorLink,
70156
+ // Keep the legacy `.dgmo-rendered` + theme hooks so the anti-flash CSS,
70157
+ // idempotency filters, and theme-scoped token colors keep working.
70158
+ legacyClassNames: ["dgmo-rendered", opts.themeClass]
70002
70159
  });
70003
- actions.appendChild(copyBtn);
70004
- if (showEditorLink) {
70005
- const editorBtn = document.createElement("a");
70006
- editorBtn.className = "dgmo-btn dgmo-btn-editor";
70007
- editorBtn.target = "_blank";
70008
- editorBtn.rel = "noopener noreferrer";
70009
- editorBtn.setAttribute("aria-label", "Open in editor");
70010
- setIcon(editorBtn, EXTERNAL_ICON_SVG);
70011
- if (shareUrl) {
70012
- editorBtn.href = shareUrl;
70013
- editorBtn.title = "Open in editor";
70014
- } else {
70015
- editorBtn.setAttribute("aria-disabled", "true");
70016
- editorBtn.title = "Diagram too large for share link; copy source and paste into editor";
70017
- editorBtn.addEventListener("click", (e) => e.preventDefault());
70018
- }
70019
- actions.appendChild(editorBtn);
70020
- }
70021
- body.appendChild(actions);
70022
- panel.appendChild(toggle);
70023
- panel.appendChild(body);
70024
- toggle.addEventListener("click", () => {
70025
- const open = body.classList.toggle("dgmo-open");
70026
- toggle.setAttribute("aria-expanded", open ? "true" : "false");
70160
+ const holder = document.createElement("div");
70161
+ holder.innerHTML = html;
70162
+ const wrapper = holder.firstElementChild;
70163
+ wrapper.querySelector(".dgmo-svg")?.appendChild(opts.svgEl);
70164
+ const open = wrapper.querySelector("a.dgmo-open");
70165
+ if (open) {
70166
+ if (opts.shareUrl) open.href = opts.shareUrl;
70167
+ else open.remove();
70168
+ }
70169
+ bindBlockToolbar(wrapper);
70170
+ return wrapper;
70171
+ }
70172
+ function bindBlockToolbar(wrapper) {
70173
+ wrapper.addEventListener("click", (e) => {
70174
+ void handleToolbarClick(e);
70027
70175
  });
70028
- return panel;
70029
70176
  }
70030
- async function copySource(source, btn) {
70031
- let copied = false;
70177
+ async function handleToolbarClick(e) {
70178
+ const target = e.target;
70179
+ if (!target || typeof target.closest !== "function") return;
70180
+ const btn = target.closest(".dgmo-toolbar-btn");
70181
+ if (!btn) return;
70182
+ const insideSummary = !!btn.closest("summary");
70183
+ if (insideSummary) e.preventDefault();
70184
+ if (btn.matches("button.dgmo-copy")) {
70185
+ const copied = await copyText(btn.dataset["dgmoSource"] ?? "");
70186
+ if (copied) {
70187
+ btn.classList.add("dgmo-copy--success");
70188
+ setTimeout(
70189
+ () => btn.classList.remove("dgmo-copy--success"),
70190
+ COPIED_INTERACTION_MS
70191
+ );
70192
+ } else {
70193
+ sharedWarn("clipboard write failed");
70194
+ }
70195
+ return;
70196
+ }
70197
+ if (insideSummary && btn.matches("a.dgmo-open")) {
70198
+ const anchor = btn;
70199
+ if (anchor.href) {
70200
+ window.open(
70201
+ anchor.href,
70202
+ anchor.target || "_blank",
70203
+ "noopener,noreferrer"
70204
+ );
70205
+ }
70206
+ }
70207
+ }
70208
+ async function copyText(text) {
70032
70209
  try {
70033
70210
  if (navigator?.clipboard?.writeText) {
70034
- await navigator.clipboard.writeText(source);
70035
- copied = true;
70211
+ await navigator.clipboard.writeText(text);
70212
+ return true;
70036
70213
  }
70037
70214
  } catch {
70038
70215
  }
70039
- if (!copied && typeof document !== "undefined") {
70216
+ if (typeof document !== "undefined") {
70040
70217
  try {
70041
70218
  const ta = document.createElement("textarea");
70042
- ta.value = source;
70219
+ ta.value = text;
70043
70220
  ta.setAttribute("readonly", "");
70044
70221
  ta.style.position = "absolute";
70045
70222
  ta.style.left = "-9999px";
70046
70223
  document.body.appendChild(ta);
70047
70224
  ta.select();
70048
- copied = document.execCommand("copy");
70225
+ const copied = document.execCommand("copy");
70049
70226
  document.body.removeChild(ta);
70227
+ return copied;
70050
70228
  } catch {
70051
- copied = false;
70229
+ return false;
70052
70230
  }
70053
70231
  }
70054
- if (copied) {
70055
- setIcon(btn, CHECK_ICON_SVG);
70056
- btn.classList.add("dgmo-btn-copied");
70057
- setTimeout(() => {
70058
- setIcon(btn, COPY_ICON_SVG);
70059
- btn.classList.remove("dgmo-btn-copied");
70060
- }, COPIED_INTERACTION_MS);
70061
- } else {
70062
- sharedWarn("clipboard write failed");
70063
- }
70232
+ return false;
70064
70233
  }
70065
- function buildErrorBanner(opts) {
70066
- const banner = document.createElement("div");
70067
- banner.className = "dgmo-error-banner";
70068
- banner.setAttribute("role", "alert");
70069
- const title = document.createElement("div");
70070
- title.className = "dgmo-error-banner-title";
70071
- title.textContent = `${opts.severity ?? "error"}: ${opts.message}`;
70072
- banner.appendChild(title);
70234
+ function buildErrorBlock(opts) {
70235
+ let message = opts.severity && opts.severity !== "error" ? `${opts.severity}: ${opts.message}` : opts.message;
70073
70236
  if (opts.line !== void 0 && opts.line > 0) {
70074
- const loc = document.createElement("div");
70075
- loc.className = "dgmo-error-banner-loc";
70076
- loc.textContent = opts.column !== void 0 ? `at ${opts.line}:${opts.column}` : `at line ${opts.line}`;
70077
- banner.appendChild(loc);
70237
+ message += opts.column !== void 0 ? ` (at ${opts.line}:${opts.column})` : ` (at line ${opts.line})`;
70078
70238
  }
70079
- return banner;
70239
+ const holder = document.createElement("div");
70240
+ holder.innerHTML = errorBlockHtml(new Error(message), opts.source);
70241
+ const card = holder.firstElementChild;
70242
+ card.setAttribute("data-dgmo-processed", "true");
70243
+ return card;
70080
70244
  }
70081
70245
  function buildShareUrl(source, opts) {
70082
70246
  const result = encodeDiagramUrl(source, {
@@ -70216,6 +70380,10 @@ function determineReplaceTarget(matched) {
70216
70380
  }
70217
70381
  return matched;
70218
70382
  }
70383
+ function replaceWithErrorCard(el, source, opts) {
70384
+ const card = buildErrorBlock({ ...opts, source });
70385
+ determineReplaceTarget(el).replaceWith(card);
70386
+ }
70219
70387
  async function processElement(el) {
70220
70388
  if (!(el instanceof HTMLElement)) return {};
70221
70389
  if (el.dataset["dgmoProcessed"] === "true") return {};
@@ -70223,12 +70391,10 @@ async function processElement(el) {
70223
70391
  const source = el.textContent || "";
70224
70392
  const sourceBytes = new TextEncoder().encode(source).byteLength;
70225
70393
  if (sourceBytes > SOURCE_BYTE_CAP) {
70226
- el.style.visibility = "";
70227
- const banner = buildErrorBanner({
70394
+ replaceWithErrorCard(el, source, {
70228
70395
  message: `DGMO source too large to render \u2014 ${SOURCE_BYTE_CAP / 1024} KB max`,
70229
70396
  severity: "error"
70230
70397
  });
70231
- el.parentElement?.insertBefore(banner, el);
70232
70398
  warn("source exceeds 256 KB cap");
70233
70399
  return {};
70234
70400
  }
@@ -70254,69 +70420,62 @@ async function processElement(el) {
70254
70420
  palette: cfg.palette
70255
70421
  });
70256
70422
  } catch (err) {
70257
- el.style.visibility = "";
70258
70423
  const message = err instanceof Error ? err.message : "Render failed unexpectedly";
70259
- const banner = buildErrorBanner({ message, severity: "error" });
70260
- el.parentElement?.insertBefore(banner, el);
70424
+ replaceWithErrorCard(el, source, { message, severity: "error" });
70261
70425
  warn("render() rejected:", err);
70262
70426
  return {};
70263
70427
  }
70264
70428
  if (result.diagnostics && result.diagnostics.length > 0) {
70265
- el.style.visibility = "";
70266
70429
  const d = result.diagnostics[0];
70267
- const banner = buildErrorBanner({
70430
+ replaceWithErrorCard(el, source, {
70268
70431
  message: d.message,
70269
70432
  severity: d.severity,
70270
70433
  line: d.line,
70271
70434
  ...d.column !== void 0 && { column: d.column }
70272
70435
  });
70273
- el.parentElement?.insertBefore(banner, el);
70274
70436
  warn("diagnostic:", d.message, d.line, d.column);
70275
70437
  return {};
70276
70438
  }
70277
70439
  if (!result.svg) {
70278
- el.style.visibility = "";
70279
- const banner = buildErrorBanner({
70440
+ replaceWithErrorCard(el, source, {
70280
70441
  message: "Empty SVG returned from renderer",
70281
70442
  severity: "error"
70282
70443
  });
70283
- el.parentElement?.insertBefore(banner, el);
70284
70444
  return {};
70285
70445
  }
70286
- const wrapper = document.createElement("div");
70287
70446
  const themeClass = resolvedTheme === "dark" ? "dgmo-theme-dark" : resolvedTheme === "transparent" ? "dgmo-theme-transparent" : "dgmo-theme-light";
70288
- wrapper.className = `dgmo-rendered ${themeClass}`;
70289
- wrapper.dataset["dgmoProcessed"] = "true";
70290
70447
  const svgHolder = document.createElement("div");
70291
70448
  svgHolder.innerHTML = result.svg;
70292
70449
  const svgEl = svgHolder.querySelector("svg");
70293
70450
  if (!svgEl) {
70294
- el.style.visibility = "";
70295
- const banner = buildErrorBanner({
70451
+ replaceWithErrorCard(el, source, {
70296
70452
  message: "Empty SVG returned from renderer",
70297
70453
  severity: "error"
70298
70454
  });
70299
- el.parentElement?.insertBefore(banner, el);
70300
70455
  return {};
70301
70456
  }
70302
70457
  sanitizeSvgInPlace(svgEl);
70303
70458
  svgEl.setAttribute("role", "img");
70304
70459
  svgEl.setAttribute("aria-label", ariaLabel);
70305
- wrapper.appendChild(svgEl);
70306
- if (showSource) {
70307
- let shareUrl = null;
70308
- if (cfg.showEditorLink) {
70309
- shareUrl = buildShareUrl(source, {
70310
- palette: cfg.palette,
70311
- theme: resolvedTheme === "dark" ? "dark" : "light",
70312
- editorBase: EDITOR_BASE_URL,
70313
- campaign: VERSION,
70314
- utmSource: "auto-embed"
70315
- });
70316
- }
70317
- const panel = buildSourcePanel(source, shareUrl, cfg.showEditorLink);
70318
- wrapper.appendChild(panel);
70460
+ let shareUrl = null;
70461
+ if (showSource && cfg.showEditorLink) {
70462
+ shareUrl = buildShareUrl(source, {
70463
+ palette: cfg.palette,
70464
+ theme: resolvedTheme === "dark" ? "dark" : "light",
70465
+ editorBase: EDITOR_BASE_URL,
70466
+ campaign: VERSION,
70467
+ utmSource: "auto-embed"
70468
+ });
70319
70469
  }
70470
+ const wrapper = buildRenderedBlock({
70471
+ source,
70472
+ svgEl,
70473
+ themeClass,
70474
+ showSource,
70475
+ showEditorLink: cfg.showEditorLink,
70476
+ shareUrl
70477
+ });
70478
+ wrapper.dataset["dgmoProcessed"] = "true";
70320
70479
  const trackedShowSource = perElementShowSource === "true" ? true : perElementShowSource === "false" ? false : null;
70321
70480
  const tracked = {
70322
70481
  wrapper,