@glyphjs/components 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -62,6 +62,34 @@ var calloutDefinition = {
62
62
  schema: calloutSchema,
63
63
  render: Callout
64
64
  };
65
+
66
+ // src/utils/inlineToText.ts
67
+ function inlineToText(content) {
68
+ if (typeof content === "string") {
69
+ return content;
70
+ }
71
+ return content.map((node) => {
72
+ switch (node.type) {
73
+ case "text":
74
+ return node.value;
75
+ case "strong":
76
+ case "emphasis":
77
+ case "delete":
78
+ case "link":
79
+ return inlineToText(node.children);
80
+ case "inlineCode":
81
+ return node.value;
82
+ case "image":
83
+ return node.alt ?? "";
84
+ case "break":
85
+ return "\n";
86
+ default:
87
+ return "";
88
+ }
89
+ }).join("");
90
+ }
91
+
92
+ // src/chart/render.ts
65
93
  var DEFAULT_WIDTH = 600;
66
94
  var DEFAULT_HEIGHT = 400;
67
95
  var MARGIN = { top: 20, right: 30, bottom: 50, left: 60 };
@@ -109,7 +137,7 @@ function renderAxes(g, xScale, yScale, xAxisConfig, yAxisConfig, innerWidth, inn
109
137
  }
110
138
  xAxisG.selectAll("text, line, path").attr("fill", "var(--glyph-text, #1a2035)").attr("stroke", "var(--glyph-grid, #1a2035)");
111
139
  if (xAxisConfig?.label) {
112
- g.append("text").attr("class", "x-label").attr("x", innerWidth / 2).attr("y", innerHeight + MARGIN.bottom - 6).attr("text-anchor", "middle").attr("fill", "var(--glyph-text, #1a2035)").attr("font-size", "12px").text(xAxisConfig.label);
140
+ g.append("text").attr("class", "x-label").attr("x", innerWidth / 2).attr("y", innerHeight + MARGIN.bottom - 6).attr("text-anchor", "middle").attr("fill", "var(--glyph-text, #1a2035)").attr("font-size", "12px").text(inlineToText(xAxisConfig.label));
113
141
  }
114
142
  const yAxisG = g.append("g").attr("class", "y-axis");
115
143
  yAxisG.call(
@@ -117,7 +145,7 @@ function renderAxes(g, xScale, yScale, xAxisConfig, yAxisConfig, innerWidth, inn
117
145
  );
118
146
  yAxisG.selectAll("text, line, path").attr("fill", "var(--glyph-text, #1a2035)").attr("stroke", "var(--glyph-grid, #1a2035)");
119
147
  if (yAxisConfig?.label) {
120
- g.append("text").attr("class", "y-label").attr("transform", "rotate(-90)").attr("x", -innerHeight / 2).attr("y", -MARGIN.left + 14).attr("text-anchor", "middle").attr("fill", "var(--glyph-text, #1a2035)").attr("font-size", "12px").text(yAxisConfig.label);
148
+ g.append("text").attr("class", "y-label").attr("transform", "rotate(-90)").attr("x", -innerHeight / 2).attr("y", -MARGIN.left + 14).attr("text-anchor", "middle").attr("fill", "var(--glyph-text, #1a2035)").attr("font-size", "12px").text(inlineToText(yAxisConfig.label));
121
149
  }
122
150
  }
123
151
  function renderGridLines(g, yScale, innerWidth) {
@@ -173,7 +201,7 @@ function renderLegend(sel, series, marginLeft, marginTop, fontSize = "12px") {
173
201
  const color3 = COLOR_SCHEME[i % COLOR_SCHEME.length] ?? "var(--glyph-text, #1a2035)";
174
202
  const row = legendG.append("g").attr("transform", `translate(0,${String(i * 20)})`);
175
203
  row.append("rect").attr("width", 14).attr("height", 14).attr("fill", color3).attr("rx", 2);
176
- row.append("text").attr("x", 20).attr("y", 11).attr("fill", "var(--glyph-text, #1a2035)").attr("font-size", fontSize).text(s.name);
204
+ row.append("text").attr("x", 20).attr("y", 11).attr("fill", "var(--glyph-text, #1a2035)").attr("font-size", fontSize).text(inlineToText(s.name));
177
205
  });
178
206
  }
179
207
  function ChartAccessibleTable({
@@ -204,10 +232,10 @@ function ChartAccessibleTable({
204
232
  " chart data"
205
233
  ] }),
206
234
  series.map((s, si) => /* @__PURE__ */ jsxs("tbody", { children: [
207
- /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("th", { colSpan: 2, children: s.name }) }),
235
+ /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("th", { colSpan: 2, children: inlineToText(s.name) }) }),
208
236
  /* @__PURE__ */ jsxs("tr", { children: [
209
- /* @__PURE__ */ jsx("th", { children: xLabel ?? xKey }),
210
- /* @__PURE__ */ jsx("th", { children: yLabel ?? yKey })
237
+ /* @__PURE__ */ jsx("th", { children: xLabel ? inlineToText(xLabel) : xKey }),
238
+ /* @__PURE__ */ jsx("th", { children: yLabel ? inlineToText(yLabel) : yKey })
211
239
  ] }),
212
240
  s.data.map((d, di) => /* @__PURE__ */ jsxs("tr", { children: [
213
241
  /* @__PURE__ */ jsx("td", { children: String(d[xKey] ?? "") }),
@@ -258,6 +286,7 @@ function renderAllSeries(g, type, series, scales, xKey, yKey, showTooltip, hideT
258
286
  const { xScale, xScalePoint, yScale, innerHeight } = scales;
259
287
  series.forEach((s, i) => {
260
288
  const color3 = COLOR_SCHEME[i % COLOR_SCHEME.length] ?? "#333";
289
+ const seriesName = inlineToText(s.name);
261
290
  switch (type) {
262
291
  case "line":
263
292
  renderLineSeries(
@@ -269,7 +298,7 @@ function renderAllSeries(g, type, series, scales, xKey, yKey, showTooltip, hideT
269
298
  xKey,
270
299
  color3,
271
300
  i,
272
- s.name,
301
+ seriesName,
273
302
  showTooltip,
274
303
  hideTooltip
275
304
  );
@@ -285,7 +314,7 @@ function renderAllSeries(g, type, series, scales, xKey, yKey, showTooltip, hideT
285
314
  innerHeight,
286
315
  color3,
287
316
  i,
288
- s.name,
317
+ seriesName,
289
318
  showTooltip,
290
319
  hideTooltip
291
320
  );
@@ -302,7 +331,7 @@ function renderAllSeries(g, type, series, scales, xKey, yKey, showTooltip, hideT
302
331
  i,
303
332
  series.length,
304
333
  innerHeight,
305
- s.name,
334
+ seriesName,
306
335
  showTooltip,
307
336
  hideTooltip
308
337
  );
@@ -315,7 +344,7 @@ function renderAllSeries(g, type, series, scales, xKey, yKey, showTooltip, hideT
315
344
  xScalePoint,
316
345
  yScale,
317
346
  scales.innerWidth,
318
- s.name,
347
+ seriesName,
319
348
  showTooltip,
320
349
  hideTooltip
321
350
  );
@@ -695,7 +724,7 @@ function TableHead({
695
724
  whiteSpace: "nowrap"
696
725
  },
697
726
  children: [
698
- col.label,
727
+ /* @__PURE__ */ jsx(RichText, { content: col.label }),
699
728
  col.sortable ? sortIndicator(direction) : ""
700
729
  ]
701
730
  },
@@ -1021,7 +1050,7 @@ function Tabs({ data, block, onInteraction }) {
1021
1050
  outline: "revert",
1022
1051
  outlineOffset: "2px"
1023
1052
  },
1024
- children: tab.label
1053
+ children: /* @__PURE__ */ jsx(RichText, { content: tab.label })
1025
1054
  },
1026
1055
  tabId
1027
1056
  );
@@ -1047,7 +1076,7 @@ function Tabs({ data, block, onInteraction }) {
1047
1076
  color: "var(--glyph-heading, #0a0e1a)",
1048
1077
  lineHeight: 1.6
1049
1078
  },
1050
- children: tab.content
1079
+ children: /* @__PURE__ */ jsx(RichText, { content: tab.content })
1051
1080
  },
1052
1081
  panelId
1053
1082
  );
@@ -1298,6 +1327,92 @@ var timelineDefinition = {
1298
1327
  schema: timelineSchema,
1299
1328
  render: Timeline
1300
1329
  };
1330
+
1331
+ // src/utils/measureText.ts
1332
+ var measurementCache = /* @__PURE__ */ new WeakMap();
1333
+ function measurePlainText(text, style) {
1334
+ const canvas = document.createElement("canvas");
1335
+ const ctx = canvas.getContext("2d");
1336
+ if (!ctx) {
1337
+ const avgCharWidth = parseInt(style.fontSize) * 0.6;
1338
+ return {
1339
+ width: text.length * avgCharWidth,
1340
+ height: parseInt(style.fontSize) * 1.2
1341
+ };
1342
+ }
1343
+ const fontWeight = style.fontWeight ?? "normal";
1344
+ ctx.font = `${fontWeight} ${style.fontSize} ${style.fontFamily}`;
1345
+ const metrics = ctx.measureText(text);
1346
+ const width = metrics.width;
1347
+ const height = parseInt(style.fontSize) * 1.2;
1348
+ return { width, height };
1349
+ }
1350
+ function measureHtmlText(content, style) {
1351
+ const cached = measurementCache.get(content);
1352
+ if (cached) {
1353
+ return cached;
1354
+ }
1355
+ const container = document.createElement("div");
1356
+ container.style.position = "absolute";
1357
+ container.style.visibility = "hidden";
1358
+ container.style.left = "-9999px";
1359
+ container.style.top = "-9999px";
1360
+ container.style.fontSize = style.fontSize;
1361
+ container.style.fontFamily = style.fontFamily;
1362
+ container.style.fontWeight = style.fontWeight ?? "normal";
1363
+ container.style.whiteSpace = "pre-wrap";
1364
+ container.style.wordBreak = "break-word";
1365
+ if (style.maxWidth) {
1366
+ container.style.maxWidth = `${style.maxWidth}px`;
1367
+ }
1368
+ container.innerHTML = inlineNodesToHtml(content);
1369
+ document.body.appendChild(container);
1370
+ const rect = container.getBoundingClientRect();
1371
+ const dimensions = {
1372
+ width: Math.ceil(rect.width),
1373
+ height: Math.ceil(rect.height)
1374
+ };
1375
+ document.body.removeChild(container);
1376
+ measurementCache.set(content, dimensions);
1377
+ return dimensions;
1378
+ }
1379
+ function inlineNodesToHtml(nodes) {
1380
+ return nodes.map((node) => {
1381
+ switch (node.type) {
1382
+ case "text":
1383
+ return escapeHtml(node.value);
1384
+ case "strong":
1385
+ return `<strong>${inlineNodesToHtml(node.children)}</strong>`;
1386
+ case "emphasis":
1387
+ return `<em>${inlineNodesToHtml(node.children)}</em>`;
1388
+ case "delete":
1389
+ return `<del>${inlineNodesToHtml(node.children)}</del>`;
1390
+ case "inlineCode":
1391
+ return `<code>${escapeHtml(node.value)}</code>`;
1392
+ case "link":
1393
+ return `<a href="${escapeHtml(node.url)}">${inlineNodesToHtml(node.children)}</a>`;
1394
+ case "image":
1395
+ return `<img src="${escapeHtml(node.src)}" alt="${escapeHtml(node.alt ?? "")}" />`;
1396
+ case "break":
1397
+ return "<br />";
1398
+ default:
1399
+ return "";
1400
+ }
1401
+ }).join("");
1402
+ }
1403
+ function escapeHtml(str) {
1404
+ const div = document.createElement("div");
1405
+ div.textContent = str;
1406
+ return div.innerHTML;
1407
+ }
1408
+ function measureText(content, style) {
1409
+ if (typeof content === "string") {
1410
+ return measurePlainText(content, style);
1411
+ }
1412
+ return measureHtmlText(content, style);
1413
+ }
1414
+
1415
+ // src/graph/layout.ts
1301
1416
  var RANKDIR_MAP = {
1302
1417
  "top-down": "TB",
1303
1418
  "left-right": "LR",
@@ -1320,10 +1435,17 @@ function computeDagreLayout(nodes, edges, direction = "top-down") {
1320
1435
  });
1321
1436
  g.setDefaultEdgeLabel(() => ({}));
1322
1437
  for (const node of nodes) {
1438
+ const labelDimensions = measureText(node.label, {
1439
+ fontSize: "13px",
1440
+ fontFamily: "Inter, system-ui, sans-serif",
1441
+ maxWidth: 200
1442
+ });
1443
+ const nodeWidth = Math.max(120, Math.min(250, labelDimensions.width + 40));
1444
+ const nodeHeight = Math.max(40, labelDimensions.height + 20);
1323
1445
  g.setNode(node.id, {
1324
1446
  label: node.label,
1325
- width: DEFAULT_NODE_WIDTH,
1326
- height: DEFAULT_NODE_HEIGHT
1447
+ width: nodeWidth,
1448
+ height: nodeHeight
1327
1449
  });
1328
1450
  }
1329
1451
  for (const edge of edges) {
@@ -1485,16 +1607,16 @@ function useZoomInteraction({
1485
1607
  };
1486
1608
  }, [interactionMode, svgRef]);
1487
1609
  const zoomBehavior = useMemo(() => {
1488
- const zoom3 = d32.zoom().scaleExtent([0.1, 4]);
1489
- if (typeof zoom3.filter === "function") {
1490
- zoom3.filter(filterFunction);
1610
+ const zoom2 = d32.zoom().scaleExtent([0.1, 4]);
1611
+ if (typeof zoom2.filter === "function") {
1612
+ zoom2.filter(filterFunction);
1491
1613
  }
1492
- zoom3.on("zoom", (event) => {
1614
+ zoom2.on("zoom", (event) => {
1493
1615
  if (rootRef.current) {
1494
1616
  d32.select(rootRef.current).attr("transform", event.transform.toString());
1495
1617
  }
1496
1618
  });
1497
- return zoom3;
1619
+ return zoom2;
1498
1620
  }, [filterFunction, rootRef]);
1499
1621
  const handleActivate = useCallback(() => {
1500
1622
  if (interactionMode === "click-to-activate") {
@@ -1826,7 +1948,7 @@ function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, z
1826
1948
  if (edge.label) {
1827
1949
  const mid = edge.points[Math.floor(edge.points.length / 2)];
1828
1950
  if (mid) {
1829
- edgeG.append("text").attr("x", mid.x).attr("y", mid.y - 8).attr("text-anchor", "middle").attr("font-size", "11px").attr("fill", "var(--glyph-edge-color, #6b7a94)").text(edge.label);
1951
+ edgeG.append("text").attr("x", mid.x).attr("y", mid.y - 8).attr("text-anchor", "middle").attr("font-size", "11px").attr("fill", "var(--glyph-edge-color, #6b7a94)").text(inlineToText(edge.label));
1830
1952
  }
1831
1953
  }
1832
1954
  }
@@ -1843,7 +1965,7 @@ function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, z
1843
1965
  } else {
1844
1966
  nodeG.append("rect").attr("x", nodeX).attr("y", nodeY).attr("width", node.width).attr("height", node.height).attr("rx", nodeRadius).attr("ry", nodeRadius).attr("fill", node.style?.["fill"] ?? color3).attr("stroke", node.style?.["stroke"] ?? defaultStroke).attr("stroke-width", node.style?.["stroke-width"] ?? nodeStrokeWidth).attr("opacity", nodeFillOpacity);
1845
1967
  }
1846
- nodeG.append("text").attr("x", node.x).attr("y", node.y).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-node-label-color, #fff)").attr("pointer-events", "none").text(node.label);
1968
+ nodeG.append("text").attr("x", node.x).attr("y", node.y).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-node-label-color, #fff)").attr("pointer-events", "none").text(inlineToText(node.label));
1847
1969
  if (isNavigable || onNodeClick) {
1848
1970
  nodeG.attr("cursor", "pointer");
1849
1971
  nodeG.on("click", () => {
@@ -1851,7 +1973,7 @@ function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, z
1851
1973
  const ref = refByAnchor.get(node.id);
1852
1974
  if (ref) onNavigate(ref);
1853
1975
  }
1854
- onNodeClick?.(node.id, node.label);
1976
+ onNodeClick?.(node.id, inlineToText(node.label));
1855
1977
  });
1856
1978
  }
1857
1979
  }
@@ -1943,7 +2065,7 @@ function Graph({
1943
2065
  return `${dir} ${target}${e.label ? ` (${e.label})` : ""}`;
1944
2066
  }).join(", ");
1945
2067
  return /* @__PURE__ */ jsxs("tr", { children: [
1946
- /* @__PURE__ */ jsx("td", { children: node.label }),
2068
+ /* @__PURE__ */ jsx("td", { children: inlineToText(node.label) }),
1947
2069
  /* @__PURE__ */ jsx("td", { children: node.group ?? "" }),
1948
2070
  /* @__PURE__ */ jsx("td", { children: connections })
1949
2071
  ] }, node.id);
@@ -1977,14 +2099,20 @@ var NODE_SEP2 = 60;
1977
2099
  var RANK_SEP2 = 80;
1978
2100
  var EDGE_SEP2 = 10;
1979
2101
  var LAYOUT_PADDING2 = 40;
1980
- var CHAR_WIDTH = 7.5;
1981
2102
  function computeEntitySize(entity) {
1982
2103
  const attrs = entity.attributes ?? [];
1983
2104
  const height = ENTITY_HEADER_HEIGHT + attrs.length * ENTITY_ATTR_HEIGHT + ENTITY_PADDING;
1984
- let maxTextWidth = entity.label.length * (CHAR_WIDTH + 1);
2105
+ const labelDimensions = measureText(entity.label, {
2106
+ fontSize: "14px",
2107
+ fontFamily: "Inter, system-ui, sans-serif"
2108
+ });
2109
+ let maxTextWidth = labelDimensions.width;
1985
2110
  for (const attr of attrs) {
1986
- const attrText = `${attr.name}: ${attr.type}`;
1987
- maxTextWidth = Math.max(maxTextWidth, attrText.length * CHAR_WIDTH);
2111
+ const attrNameDimensions = measureText(attr.name, {
2112
+ fontSize: "12px",
2113
+ fontFamily: "ui-monospace, monospace"
2114
+ });
2115
+ maxTextWidth = Math.max(maxTextWidth, attrNameDimensions.width + 100);
1988
2116
  }
1989
2117
  const width = Math.max(ENTITY_MIN_WIDTH, maxTextWidth + ENTITY_PADDING * 2 + 16);
1990
2118
  return { width, height };
@@ -2108,7 +2236,7 @@ function renderRelation(svgElement, layout, zoomBehavior) {
2108
2236
  if (rel.label) {
2109
2237
  const mid = rel.points[Math.floor(rel.points.length / 2)];
2110
2238
  if (mid) {
2111
- edgeG.append("text").attr("x", mid.x).attr("y", mid.y - 10).attr("text-anchor", "middle").attr("font-size", "11px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-relation-label, #6b7a94)").text(rel.label);
2239
+ edgeG.append("text").attr("x", mid.x).attr("y", mid.y - 10).attr("text-anchor", "middle").attr("font-size", "11px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-relation-label, #6b7a94)").text(inlineToText(rel.label));
2112
2240
  }
2113
2241
  }
2114
2242
  const pFirst = rel.points[0];
@@ -2136,7 +2264,7 @@ function renderRelation(svgElement, layout, zoomBehavior) {
2136
2264
  const headerHeight = ENTITY_HEADER_HEIGHT;
2137
2265
  entityG.append("rect").attr("x", x).attr("y", y).attr("width", entity.width).attr("height", headerHeight).attr("rx", 4).attr("ry", 4).attr("fill", "var(--glyph-relation-header-bg, #00d4aa)");
2138
2266
  entityG.append("rect").attr("x", x).attr("y", y + headerHeight - 4).attr("width", entity.width).attr("height", 4).attr("fill", "var(--glyph-relation-header-bg, #00d4aa)");
2139
- entityG.append("text").attr("x", entity.x).attr("y", y + headerHeight / 2).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-weight", "bold").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-relation-header-text, #fff)").text(entity.label);
2267
+ entityG.append("text").attr("x", entity.x).attr("y", y + headerHeight / 2).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-weight", "bold").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-relation-header-text, #fff)").text(inlineToText(entity.label));
2140
2268
  if (attrs.length > 0) {
2141
2269
  entityG.append("line").attr("x1", x).attr("y1", y + headerHeight).attr("x2", x + entity.width).attr("y2", y + headerHeight).attr("stroke", "var(--glyph-relation-entity-border, #a8b5c8)").attr("stroke-width", 1);
2142
2270
  }
@@ -2145,7 +2273,7 @@ function renderRelation(svgElement, layout, zoomBehavior) {
2145
2273
  if (!attr) continue;
2146
2274
  const attrY = y + headerHeight + i * ENTITY_ATTR_HEIGHT + ENTITY_ATTR_HEIGHT / 2 + 4;
2147
2275
  const textEl = entityG.append("text").attr("x", x + ENTITY_PADDING).attr("y", attrY).attr("dy", "0.35em").attr("font-size", "12px").attr("font-family", "system-ui, -apple-system, monospace").attr("fill", "var(--glyph-relation-attr-text, #1a2035)");
2148
- const nameSpan = textEl.append("tspan").text(attr.name);
2276
+ const nameSpan = textEl.append("tspan").text(inlineToText(attr.name));
2149
2277
  if (attr.primaryKey) {
2150
2278
  nameSpan.attr("font-weight", "bold").attr("text-decoration", "underline");
2151
2279
  }
@@ -2205,7 +2333,7 @@ function Relation({ data, block }) {
2205
2333
  return `${dir} ${target} [${r.cardinality}]${r.label ? ` (${r.label})` : ""}`;
2206
2334
  }).join(", ");
2207
2335
  return /* @__PURE__ */ jsxs("tr", { children: [
2208
- /* @__PURE__ */ jsx("td", { children: entity.label }),
2336
+ /* @__PURE__ */ jsx("td", { children: inlineToText(entity.label) }),
2209
2337
  /* @__PURE__ */ jsx("td", { children: attrs }),
2210
2338
  /* @__PURE__ */ jsx("td", { children: rels })
2211
2339
  ] }, entity.id);
@@ -2743,9 +2871,9 @@ function CodeDiff({ data, block }) {
2743
2871
  }
2744
2872
  return /* @__PURE__ */ jsxs("div", { id: baseId, role: "region", "aria-label": summary, style: containerStyle11, children: [
2745
2873
  (beforeLabel || afterLabel) && /* @__PURE__ */ jsxs("div", { style: labelBarStyle, children: [
2746
- beforeLabel && /* @__PURE__ */ jsx("span", { children: beforeLabel }),
2874
+ beforeLabel && /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(RichText, { content: beforeLabel }) }),
2747
2875
  beforeLabel && afterLabel && /* @__PURE__ */ jsx("span", { children: "\u2192" }),
2748
- afterLabel && /* @__PURE__ */ jsx("span", { children: afterLabel })
2876
+ afterLabel && /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(RichText, { content: afterLabel }) })
2749
2877
  ] }),
2750
2878
  /* @__PURE__ */ jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsx("table", { role: "grid", style: tableStyle2, children: /* @__PURE__ */ jsx("tbody", { children: diffLines.map((line6, i) => /* @__PURE__ */ jsxs(
2751
2879
  "tr",
@@ -2870,7 +2998,7 @@ function renderFlowchart(svgElement, layout, zoomBehavior) {
2870
2998
  if (edge.label) {
2871
2999
  const mid = edge.points[Math.floor(edge.points.length / 2)];
2872
3000
  if (mid) {
2873
- edgeG.append("text").attr("x", mid.x).attr("y", mid.y - 8).attr("text-anchor", "middle").attr("font-size", "11px").attr("fill", "var(--glyph-text-muted, #6b7a94)").text(edge.label);
3001
+ edgeG.append("text").attr("x", mid.x).attr("y", mid.y - 8).attr("text-anchor", "middle").attr("font-size", "11px").attr("fill", "var(--glyph-text-muted, #6b7a94)").text(inlineToText(edge.label));
2874
3002
  }
2875
3003
  }
2876
3004
  }
@@ -2878,7 +3006,7 @@ function renderFlowchart(svgElement, layout, zoomBehavior) {
2878
3006
  for (const node of layout.nodes) {
2879
3007
  const nodeG = nodeGroup.append("g").attr("class", "glyph-flowchart-node");
2880
3008
  renderNodeShape(nodeG, node, nodeFillOpacity, nodeStrokeWidth);
2881
- nodeG.append("text").attr("x", node.x).attr("y", node.y).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-node-label-color, #fff)").attr("pointer-events", "none").text(node.label);
3009
+ nodeG.append("text").attr("x", node.x).attr("y", node.y).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-node-label-color, #fff)").attr("pointer-events", "none").text(inlineToText(node.label));
2882
3010
  }
2883
3011
  }
2884
3012
  function Flowchart({
@@ -2905,7 +3033,7 @@ function Flowchart({
2905
3033
  }, [layoutResult, zoomBehavior]);
2906
3034
  const nodeCount = data.nodes.length;
2907
3035
  const edgeCount = data.edges.length;
2908
- const ariaLabel = data.title ? `${data.title}: flowchart with ${nodeCount} nodes and ${edgeCount} edges` : `Flowchart with ${nodeCount} nodes and ${edgeCount} edges`;
3036
+ const ariaLabel = data.title ? `${inlineToText(data.title)}: flowchart with ${nodeCount} nodes and ${edgeCount} edges` : `Flowchart with ${nodeCount} nodes and ${edgeCount} edges`;
2909
3037
  return /* @__PURE__ */ jsxs("div", { className: "glyph-flowchart-container", children: [
2910
3038
  data.title && /* @__PURE__ */ jsx(
2911
3039
  "div",
@@ -2917,7 +3045,7 @@ function Flowchart({
2917
3045
  color: "var(--glyph-heading, #edf0f5)",
2918
3046
  marginBottom: "0.5rem"
2919
3047
  },
2920
- children: data.title
3048
+ children: /* @__PURE__ */ jsx(RichText, { content: data.title })
2921
3049
  }
2922
3050
  ),
2923
3051
  /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
@@ -2950,10 +3078,10 @@ function Flowchart({
2950
3078
  const connections = data.edges.filter((e) => e.from === node.id || e.to === node.id).map((e) => {
2951
3079
  const target = e.from === node.id ? e.to : e.from;
2952
3080
  const dir = e.from === node.id ? "->" : "<-";
2953
- return `${dir} ${target}${e.label ? ` (${e.label})` : ""}`;
3081
+ return `${dir} ${target}${e.label ? ` (${inlineToText(e.label)})` : ""}`;
2954
3082
  }).join(", ");
2955
3083
  return /* @__PURE__ */ jsxs("tr", { children: [
2956
- /* @__PURE__ */ jsx("td", { children: node.label }),
3084
+ /* @__PURE__ */ jsx("td", { children: inlineToText(node.label) }),
2957
3085
  /* @__PURE__ */ jsx("td", { children: node.type }),
2958
3086
  /* @__PURE__ */ jsx("td", { children: connections })
2959
3087
  ] }, node.id);
@@ -3375,20 +3503,24 @@ function renderActorBox(actor, cx, y, keyPrefix, width = ACTOR_WIDTH, height = A
3375
3503
  strokeWidth: 1.5
3376
3504
  }
3377
3505
  ),
3378
- /* @__PURE__ */ jsx(
3379
- "text",
3506
+ /* @__PURE__ */ jsx("foreignObject", { x: cx - width / 2, y, width, height, children: /* @__PURE__ */ jsx(
3507
+ "div",
3380
3508
  {
3381
- x: cx,
3382
- y: y + height / 2,
3383
- dy: "0.35em",
3384
- textAnchor: "middle",
3385
- fontSize,
3386
- fontFamily: "Inter, system-ui, sans-serif",
3387
- fontWeight: 600,
3388
- fill: "var(--glyph-text, #d4dae3)",
3389
- children: actor.label
3509
+ style: {
3510
+ display: "flex",
3511
+ alignItems: "center",
3512
+ justifyContent: "center",
3513
+ width: "100%",
3514
+ height: "100%",
3515
+ fontSize,
3516
+ fontFamily: "Inter, system-ui, sans-serif",
3517
+ fontWeight: 600,
3518
+ color: "var(--glyph-text, #d4dae3)",
3519
+ textAlign: "center"
3520
+ },
3521
+ children: /* @__PURE__ */ jsx(RichText, { content: actor.label })
3390
3522
  }
3391
- )
3523
+ ) })
3392
3524
  ] }, `${keyPrefix}-${actor.id}`);
3393
3525
  }
3394
3526
  function renderSelfMessage(x, y, label, idx, fontSize = "12px") {
@@ -3403,18 +3535,19 @@ function renderSelfMessage(x, y, label, idx, fontSize = "12px") {
3403
3535
  markerEnd: "url(#seq-arrow-solid)"
3404
3536
  }
3405
3537
  ),
3406
- /* @__PURE__ */ jsx(
3407
- "text",
3538
+ /* @__PURE__ */ jsx("foreignObject", { x: x + SELF_ARC_WIDTH + 6, y, width: 150, height: SELF_ARC_HEIGHT, children: /* @__PURE__ */ jsx(
3539
+ "div",
3408
3540
  {
3409
- x: x + SELF_ARC_WIDTH + 6,
3410
- y: y + SELF_ARC_HEIGHT / 2,
3411
- dy: "0.35em",
3412
- fontSize,
3413
- fontFamily: "Inter, system-ui, sans-serif",
3414
- fill: "var(--glyph-text, #d4dae3)",
3415
- children: label
3541
+ style: {
3542
+ display: "flex",
3543
+ alignItems: "center",
3544
+ fontSize,
3545
+ fontFamily: "Inter, system-ui, sans-serif",
3546
+ color: "var(--glyph-text, #d4dae3)"
3547
+ },
3548
+ children: /* @__PURE__ */ jsx(RichText, { content: label })
3416
3549
  }
3417
- )
3550
+ ) })
3418
3551
  ] }, `msg-${idx}`);
3419
3552
  }
3420
3553
  function renderStandardMessage(fromX, toX, y, label, isDashed, idx, fontSize = "12px") {
@@ -3434,18 +3567,20 @@ function renderStandardMessage(fromX, toX, y, label, isDashed, idx, fontSize = "
3434
3567
  markerEnd: `url(#${markerId})`
3435
3568
  }
3436
3569
  ),
3437
- /* @__PURE__ */ jsx(
3438
- "text",
3570
+ /* @__PURE__ */ jsx("foreignObject", { x: midX - 75, y: y - 22, width: 150, height: 20, children: /* @__PURE__ */ jsx(
3571
+ "div",
3439
3572
  {
3440
- x: midX,
3441
- y: y - 8,
3442
- textAnchor: "middle",
3443
- fontSize,
3444
- fontFamily: "Inter, system-ui, sans-serif",
3445
- fill: "var(--glyph-text, #d4dae3)",
3446
- children: label
3573
+ style: {
3574
+ display: "flex",
3575
+ justifyContent: "center",
3576
+ fontSize,
3577
+ fontFamily: "Inter, system-ui, sans-serif",
3578
+ color: "var(--glyph-text, #d4dae3)",
3579
+ textAlign: "center"
3580
+ },
3581
+ children: /* @__PURE__ */ jsx(RichText, { content: label })
3447
3582
  }
3448
- )
3583
+ ) })
3449
3584
  ] }, `msg-${idx}`);
3450
3585
  }
3451
3586
  function Sequence({ data, container }) {
@@ -3470,7 +3605,7 @@ function Sequence({ data, container }) {
3470
3605
  const firstMsgY = lifelineStartY + MSG_SPACING;
3471
3606
  const lastMsgY = firstMsgY + (messageCount - 1) * MSG_SPACING;
3472
3607
  const svgHeight = lastMsgY + BOTTOM_PADDING + effectiveActorHeight;
3473
- const ariaLabel = data.title ? `${data.title}: sequence diagram with ${actorCount} actors and ${messageCount} messages` : `Sequence diagram with ${actorCount} actors and ${messageCount} messages`;
3608
+ const ariaLabel = data.title ? `${inlineToText(data.title)}: sequence diagram with ${actorCount} actors and ${messageCount} messages` : `Sequence diagram with ${actorCount} actors and ${messageCount} messages`;
3474
3609
  return /* @__PURE__ */ jsxs("div", { className: "glyph-sequence-container", children: [
3475
3610
  data.title && /* @__PURE__ */ jsx(
3476
3611
  "div",
@@ -3482,7 +3617,7 @@ function Sequence({ data, container }) {
3482
3617
  color: "var(--glyph-heading, #edf0f5)",
3483
3618
  marginBottom: "0.5rem"
3484
3619
  },
3485
- children: data.title
3620
+ children: /* @__PURE__ */ jsx(RichText, { content: data.title })
3486
3621
  }
3487
3622
  ),
3488
3623
  /* @__PURE__ */ jsxs(
@@ -3597,9 +3732,9 @@ function Sequence({ data, container }) {
3597
3732
  const toActor = data.actors.find((a) => a.id === msg.to);
3598
3733
  return /* @__PURE__ */ jsxs("tr", { children: [
3599
3734
  /* @__PURE__ */ jsx("td", { children: idx + 1 }),
3600
- /* @__PURE__ */ jsx("td", { children: fromActor?.label ?? msg.from }),
3601
- /* @__PURE__ */ jsx("td", { children: toActor?.label ?? msg.to }),
3602
- /* @__PURE__ */ jsx("td", { children: msg.label }),
3735
+ /* @__PURE__ */ jsx("td", { children: fromActor ? inlineToText(fromActor.label) : msg.from }),
3736
+ /* @__PURE__ */ jsx("td", { children: toActor ? inlineToText(toActor.label) : msg.to }),
3737
+ /* @__PURE__ */ jsx("td", { children: inlineToText(msg.label) }),
3603
3738
  /* @__PURE__ */ jsx("td", { children: msg.type })
3604
3739
  ] }, idx);
3605
3740
  }) })
@@ -3878,7 +4013,7 @@ function getThemeVar3(container, varName, fallback) {
3878
4013
  return getComputedStyle(container).getPropertyValue(varName).trim() || fallback;
3879
4014
  }
3880
4015
  var ARROW_MARKER_ID3 = "glyph-arch-arrowhead";
3881
- function renderArchitecture(svgElement, layout) {
4016
+ function renderArchitecture(svgElement, layout, rootRef, zoomBehavior) {
3882
4017
  const svg = d32.select(svgElement);
3883
4018
  svg.selectAll("*").remove();
3884
4019
  const width = Math.max(layout.width, 200);
@@ -3891,10 +4026,12 @@ function renderArchitecture(svgElement, layout) {
3891
4026
  const nodeStrokeWidth = getThemeVar3(container, "--glyph-node-stroke-width", "1.5");
3892
4027
  const nodeFillOpacity = getThemeVar3(container, "--glyph-node-fill-opacity", "0.85");
3893
4028
  const root = svg.append("g").attr("class", "glyph-architecture-root");
3894
- const zoomBehavior = d32.zoom().scaleExtent([0.1, 4]).on("zoom", (event) => {
3895
- root.attr("transform", event.transform.toString());
3896
- });
3897
- svg.call(zoomBehavior);
4029
+ if (rootRef) {
4030
+ rootRef.current = root.node();
4031
+ }
4032
+ if (zoomBehavior) {
4033
+ svg.call(zoomBehavior);
4034
+ }
3898
4035
  const sortedZones = [...layout.zones].sort((a, b) => a.depth - b.depth);
3899
4036
  const zoneGroup = root.append("g").attr("class", "glyph-architecture-zones");
3900
4037
  for (const zone of sortedZones) {
@@ -3962,10 +4099,19 @@ function renderArchitecture(svgElement, layout) {
3962
4099
  }
3963
4100
  function Architecture({
3964
4101
  data,
3965
- container
4102
+ container,
4103
+ block
3966
4104
  }) {
3967
4105
  const svgRef = useRef(null);
4106
+ const rootRef = useRef(null);
4107
+ const containerRef = useRef(null);
3968
4108
  const [layout, setLayout] = useState(null);
4109
+ const { overlayProps, zoomBehavior, zoomIn, zoomOut, resetZoom } = useZoomInteraction({
4110
+ svgRef,
4111
+ rootRef,
4112
+ interactionMode: data.interactionMode ?? "modifier-key",
4113
+ blockId: block.id
4114
+ });
3969
4115
  useEffect(() => {
3970
4116
  let cancelled = false;
3971
4117
  computeArchitectureLayout(data).then((result) => {
@@ -3977,62 +4123,72 @@ function Architecture({
3977
4123
  }, [data]);
3978
4124
  useEffect(() => {
3979
4125
  if (!svgRef.current || !layout) return;
3980
- renderArchitecture(svgRef.current, layout);
3981
- }, [layout]);
4126
+ renderArchitecture(svgRef.current, layout, rootRef, zoomBehavior);
4127
+ }, [layout, zoomBehavior]);
3982
4128
  const nodeCount = countLeafNodes(data.children);
3983
4129
  const edgeCount = data.edges.length;
3984
4130
  const ariaLabel = data.title ? `${data.title}: architecture diagram with ${nodeCount} nodes and ${edgeCount} connections` : `Architecture diagram with ${nodeCount} nodes and ${edgeCount} connections`;
3985
4131
  const flatNodes = flattenNodes(data.children);
3986
- return /* @__PURE__ */ jsxs("div", { className: "glyph-architecture-container", children: [
3987
- !layout && /* @__PURE__ */ jsx(
3988
- "div",
3989
- {
3990
- style: {
3991
- padding: "2rem",
3992
- textAlign: "center",
3993
- color: "var(--glyph-text-muted, #7a8599)",
3994
- fontFamily: "Inter, system-ui, sans-serif",
3995
- fontSize: "13px"
3996
- },
3997
- children: "Computing layout..."
3998
- }
3999
- ),
4000
- /* @__PURE__ */ jsx(
4001
- "svg",
4002
- {
4003
- ref: svgRef,
4004
- role: "img",
4005
- "aria-label": ariaLabel,
4006
- width: "100%",
4007
- height: "100%",
4008
- style: {
4009
- minHeight: container.tier === "compact" ? 200 : 300,
4010
- maxHeight: container.tier === "compact" ? 500 : 700,
4011
- display: layout ? "block" : "none"
4012
- }
4013
- }
4014
- ),
4015
- /* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Architecture data", style: SR_ONLY_STYLE6, children: [
4016
- /* @__PURE__ */ jsx("caption", { children: "Architecture nodes and connections" }),
4017
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
4018
- /* @__PURE__ */ jsx("th", { scope: "col", children: "Node" }),
4019
- /* @__PURE__ */ jsx("th", { scope: "col", children: "Zone" }),
4020
- /* @__PURE__ */ jsx("th", { scope: "col", children: "Connections" })
4021
- ] }) }),
4022
- /* @__PURE__ */ jsx("tbody", { children: flatNodes.map((node) => {
4023
- const connections = data.edges.filter((e) => e.from === node.id || e.to === node.id).map((e) => {
4024
- const target = e.from === node.id ? e.to : e.from;
4025
- const dir = e.from === node.id ? "->" : "<-";
4026
- return `${dir} ${target}${e.label ? ` (${e.label})` : ""}`;
4027
- }).join(", ");
4028
- return /* @__PURE__ */ jsxs("tr", { children: [
4029
- /* @__PURE__ */ jsx("td", { children: node.label }),
4030
- /* @__PURE__ */ jsx("td", { children: node.zone ?? "" }),
4031
- /* @__PURE__ */ jsx("td", { children: connections })
4032
- ] }, node.id);
4033
- }) })
4034
- ] })
4035
- ] });
4132
+ return /* @__PURE__ */ jsxs(
4133
+ "div",
4134
+ {
4135
+ ref: containerRef,
4136
+ className: "glyph-architecture-container",
4137
+ style: { position: "relative" },
4138
+ children: [
4139
+ !layout && /* @__PURE__ */ jsx(
4140
+ "div",
4141
+ {
4142
+ style: {
4143
+ padding: "2rem",
4144
+ textAlign: "center",
4145
+ color: "var(--glyph-text-muted, #7a8599)",
4146
+ fontFamily: "Inter, system-ui, sans-serif",
4147
+ fontSize: "13px"
4148
+ },
4149
+ children: "Computing layout..."
4150
+ }
4151
+ ),
4152
+ /* @__PURE__ */ jsx(
4153
+ "svg",
4154
+ {
4155
+ ref: svgRef,
4156
+ role: "img",
4157
+ "aria-label": ariaLabel,
4158
+ width: "100%",
4159
+ height: "100%",
4160
+ style: {
4161
+ minHeight: container.tier === "compact" ? 200 : 300,
4162
+ maxHeight: container.tier === "compact" ? 500 : 700,
4163
+ display: layout ? "block" : "none"
4164
+ }
4165
+ }
4166
+ ),
4167
+ layout && overlayProps && /* @__PURE__ */ jsx(InteractionOverlay, { ...overlayProps }),
4168
+ layout && data.showZoomControls !== false && /* @__PURE__ */ jsx(ZoomControls, { onZoomIn: zoomIn, onZoomOut: zoomOut, onReset: resetZoom }),
4169
+ /* @__PURE__ */ jsxs("table", { className: "sr-only", "aria-label": "Architecture data", style: SR_ONLY_STYLE6, children: [
4170
+ /* @__PURE__ */ jsx("caption", { children: "Architecture nodes and connections" }),
4171
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
4172
+ /* @__PURE__ */ jsx("th", { scope: "col", children: "Node" }),
4173
+ /* @__PURE__ */ jsx("th", { scope: "col", children: "Zone" }),
4174
+ /* @__PURE__ */ jsx("th", { scope: "col", children: "Connections" })
4175
+ ] }) }),
4176
+ /* @__PURE__ */ jsx("tbody", { children: flatNodes.map((node) => {
4177
+ const connections = data.edges.filter((e) => e.from === node.id || e.to === node.id).map((e) => {
4178
+ const target = e.from === node.id ? e.to : e.from;
4179
+ const dir = e.from === node.id ? "->" : "<-";
4180
+ return `${dir} ${target}${e.label ? ` (${e.label})` : ""}`;
4181
+ }).join(", ");
4182
+ return /* @__PURE__ */ jsxs("tr", { children: [
4183
+ /* @__PURE__ */ jsx("td", { children: node.label }),
4184
+ /* @__PURE__ */ jsx("td", { children: node.zone ?? "" }),
4185
+ /* @__PURE__ */ jsx("td", { children: connections })
4186
+ ] }, node.id);
4187
+ }) })
4188
+ ] })
4189
+ ]
4190
+ }
4191
+ );
4036
4192
  }
4037
4193
  function flattenNodes(children, zone) {
4038
4194
  const result = [];
@@ -4446,7 +4602,7 @@ function Equation({ data }) {
4446
4602
  const ariaLabel = `Equation: ${data.expression}`;
4447
4603
  return /* @__PURE__ */ jsxs("div", { style: containerStyle, role: "math", "aria-label": ariaLabel, children: [
4448
4604
  error || html === "" ? /* @__PURE__ */ jsx("code", { style: fallbackStyle, children: data.expression }) : /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: html } }),
4449
- data.label !== void 0 && /* @__PURE__ */ jsx("div", { style: labelStyle2, children: data.label })
4605
+ data.label !== void 0 && /* @__PURE__ */ jsx("div", { style: labelStyle2, children: /* @__PURE__ */ jsx(RichText, { content: data.label }) })
4450
4606
  ] });
4451
4607
  }
4452
4608
  if (data.steps !== void 0 && data.steps.length > 0) {
@@ -4457,10 +4613,10 @@ function Equation({ data }) {
4457
4613
  const { html, error } = renderLatex(step.expression);
4458
4614
  return /* @__PURE__ */ jsxs("div", { style: stepRowStyle, children: [
4459
4615
  error || html === "" ? /* @__PURE__ */ jsx("code", { style: fallbackStyle, children: step.expression }) : /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: html } }),
4460
- step.annotation !== void 0 && /* @__PURE__ */ jsx("span", { style: annotationStyle, children: step.annotation })
4616
+ step.annotation !== void 0 && /* @__PURE__ */ jsx("span", { style: annotationStyle, children: /* @__PURE__ */ jsx(RichText, { content: step.annotation }) })
4461
4617
  ] }, idx);
4462
4618
  }) }),
4463
- data.label !== void 0 && /* @__PURE__ */ jsx("div", { style: labelStyle2, children: data.label })
4619
+ data.label !== void 0 && /* @__PURE__ */ jsx("div", { style: labelStyle2, children: /* @__PURE__ */ jsx(RichText, { content: data.label }) })
4464
4620
  ] });
4465
4621
  }
4466
4622
  return /* @__PURE__ */ jsx("div", { style: containerStyle, role: "math", "aria-label": "Empty equation", children: /* @__PURE__ */ jsx("span", { style: { color: "var(--glyph-text-muted, #6b7a94)" }, children: "No equation provided" }) });
@@ -6886,8 +7042,8 @@ function Kanban({
6886
7042
  style: cardStyle(isGrabbed, card.priority),
6887
7043
  onKeyDown: (e) => handleCardKeyDown(e, card.id, col.id, cardIndex),
6888
7044
  children: [
6889
- /* @__PURE__ */ jsx("div", { style: cardTitleStyle, children: card.title }),
6890
- card.description && /* @__PURE__ */ jsx("div", { style: cardDescStyle, children: card.description }),
7045
+ /* @__PURE__ */ jsx("div", { style: cardTitleStyle, children: /* @__PURE__ */ jsx(RichText, { content: card.title }) }),
7046
+ card.description && /* @__PURE__ */ jsx("div", { style: cardDescStyle, children: /* @__PURE__ */ jsx(RichText, { content: card.description }) }),
6891
7047
  card.tags && card.tags.length > 0 && /* @__PURE__ */ jsx("div", { style: tagContainerStyle, children: card.tags.map((tag) => /* @__PURE__ */ jsx("span", { style: tagStyle, children: tag }, tag)) })
6892
7048
  ]
6893
7049
  },