flowchart-sequence-designer 1.0.1 → 1.1.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/ui/index.cjs CHANGED
@@ -31,7 +31,7 @@ __export(ui_exports, {
31
31
  module.exports = __toCommonJS(ui_exports);
32
32
 
33
33
  // src/ui/DiagramEditor.tsx
34
- var import_react19 = require("react");
34
+ var import_react20 = require("react");
35
35
 
36
36
  // src/ui/Toolbar.tsx
37
37
  var import_react2 = require("react");
@@ -444,10 +444,10 @@ function Toolbar({ onExport, onImport, allowedExports, allowImport = true }) {
444
444
  ] }),
445
445
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: divider }),
446
446
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 4, alignItems: "center" }, children: [
447
- allowImport && onImport && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => setImportOpen(true), style: ghostBtn, children: "\u2191 Import" }),
447
+ allowImport && onImport && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => setImportOpen(true), "aria-label": "Import diagram", style: ghostBtn, children: "\u2191 Import" }),
448
448
  formats.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
449
449
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 11, color: darkTheme.inputText, margin: "0 4px" }, children: "Export \u2192" }),
450
- formats.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => onExport(f.key), style: exportBtn, children: f.label }, f.key))
450
+ formats.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => onExport(f.key), "aria-label": `Export as ${f.label}`, style: exportBtn, children: f.label }, f.key))
451
451
  ] })
452
452
  ] }),
453
453
  onImport && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -725,7 +725,7 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
725
725
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: { display: "block", fontSize: 10, fontWeight: 700, color: tt.labelText, marginBottom: 8, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Shape" }),
726
726
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6 }, children: SHAPES.map((s2) => {
727
727
  const active = (node.shape ?? "rectangle") === s2.key;
728
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { onClick: () => setShape(s2.key), style: {
728
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { onClick: () => setShape(s2.key), "aria-pressed": active, style: {
729
729
  display: "flex",
730
730
  flexDirection: "column",
731
731
  alignItems: "center",
@@ -772,7 +772,12 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
772
772
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: () => removeAnswer(ans), style: { background: "none", border: "none", color: tt.textMuted, cursor: "pointer", fontSize: 12, padding: "8px 10px", flexShrink: 0 }, title: "Remove", children: "\u2715" })
773
773
  ] }, ans + i);
774
774
  }),
775
- addingAnswer ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
775
+ addingAnswer ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "group", "aria-label": "Add answer form", onKeyDown: (e) => {
776
+ if (e.key === "Escape") {
777
+ setAddingAnswer(false);
778
+ setNewAnswer("");
779
+ }
780
+ }, style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
776
781
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { autoFocus: true, value: newAnswer, onChange: (e) => setNewAnswer(e.target.value), onKeyDown: (e) => e.key === "Enter" && addAnswer(), placeholder: "Answer text\u2026", style: { ...inputStyle, marginBottom: 8 } }),
777
782
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: 6 }, children: [
778
783
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: addAnswer, style: addBtnStyle, children: "Add Answer" }),
@@ -810,7 +815,9 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
810
815
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: () => removeEdge(edge.id), style: { background: "none", border: "none", color: tt.textMuted, cursor: "pointer", fontSize: 12, padding: "8px 10px", flexShrink: 0 }, title: "Remove", children: "\u2715" })
811
816
  ] }, edge.id);
812
817
  }),
813
- addingBranch ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
818
+ addingBranch ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "group", "aria-label": "Add branch form", onKeyDown: (e) => {
819
+ if (e.key === "Escape") setAddingBranch(false);
820
+ }, style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
814
821
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: 6, marginBottom: 10 }, children: ["new", "existing"].map((mode) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: () => setBranchMode(mode), style: {
815
822
  flex: 1,
816
823
  padding: "5px 0",
@@ -845,7 +852,7 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
845
852
  }
846
853
 
847
854
  // src/ui/SequenceEditor.tsx
848
- var import_react10 = require("react");
855
+ var import_react11 = require("react");
849
856
 
850
857
  // src/ui/SequenceCanvas.tsx
851
858
  var import_react4 = require("react");
@@ -1071,8 +1078,17 @@ function SequenceCanvas(props) {
1071
1078
  fontSize: 13,
1072
1079
  fontWeight: 700,
1073
1080
  fill: t.actorText,
1081
+ role: "button",
1082
+ tabIndex: 0,
1083
+ "aria-label": `Actor ${name} \u2014 press Enter or F2 to rename`,
1074
1084
  style: STYLE_SEQ_ACTOR_TEXT,
1075
1085
  onDoubleClick: () => setEditingId(name),
1086
+ onKeyDown: (e) => {
1087
+ if (e.key === "Enter" || e.key === "F2") {
1088
+ e.preventDefault();
1089
+ setEditingId(name);
1090
+ }
1091
+ },
1076
1092
  children: name
1077
1093
  }
1078
1094
  ),
@@ -1083,9 +1099,21 @@ function SequenceCanvas(props) {
1083
1099
  cy: HEADER_PAD + 14,
1084
1100
  r: 9,
1085
1101
  fill: "transparent",
1102
+ role: "button",
1103
+ tabIndex: 0,
1104
+ "aria-label": `Remove actor ${name}`,
1086
1105
  style: STYLE_SEQ_REMOVE_BTN,
1087
1106
  onClick: () => removeActor(name),
1088
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("title", { children: "Remove actor" })
1107
+ onKeyDown: (e) => {
1108
+ if (e.key === "Enter" || e.key === " ") {
1109
+ e.preventDefault();
1110
+ removeActor(name);
1111
+ }
1112
+ },
1113
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("title", { children: [
1114
+ "Remove actor ",
1115
+ name
1116
+ ] })
1089
1117
  }
1090
1118
  ),
1091
1119
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -1552,7 +1580,7 @@ async function toPNG(model) {
1552
1580
  }
1553
1581
 
1554
1582
  // src/ui/hooks/useExporters.ts
1555
- function useExporters(model, onExport, filename = "diagram") {
1583
+ function useExporters(model, onExport, filename = "diagram", onSuccess) {
1556
1584
  return (0, import_react7.useCallback)(async (format) => {
1557
1585
  let content;
1558
1586
  switch (format) {
@@ -1576,6 +1604,7 @@ function useExporters(model, onExport, filename = "diagram") {
1576
1604
  }
1577
1605
  if (onExport) {
1578
1606
  onExport(format, content);
1607
+ onSuccess?.(`Exported as ${format.toUpperCase()}`);
1579
1608
  return;
1580
1609
  }
1581
1610
  const url = content instanceof Blob ? URL.createObjectURL(content) : URL.createObjectURL(new Blob([content], { type: "text/plain" }));
@@ -1584,7 +1613,8 @@ function useExporters(model, onExport, filename = "diagram") {
1584
1613
  a.download = `${filename}.${format === "plantuml" ? "puml" : format}`;
1585
1614
  a.click();
1586
1615
  URL.revokeObjectURL(url);
1587
- }, [model, onExport, filename]);
1616
+ onSuccess?.(`Downloaded ${a.download}`);
1617
+ }, [model, onExport, filename, onSuccess]);
1588
1618
  }
1589
1619
 
1590
1620
  // src/ui/hooks/useImporter.ts
@@ -1882,19 +1912,91 @@ function fromJSON(json) {
1882
1912
 
1883
1913
  // src/ui/hooks/useImporter.ts
1884
1914
  function useImporter(applyAndPush, options = {}) {
1885
- const { expectedType, transform } = options;
1915
+ const { expectedType, transform, onSuccess, onError } = options;
1916
+ const reportError = onError ?? ((msg) => alert(msg));
1886
1917
  return (0, import_react8.useCallback)((text) => {
1887
1918
  try {
1888
1919
  const parsed = text.trim().startsWith("{") ? fromJSON(text).toJSON() : fromMermaid(text).toJSON();
1889
1920
  if (expectedType && parsed.type !== expectedType) {
1890
- alert(`Imported diagram is not a ${expectedType} diagram.`);
1921
+ reportError(`Imported diagram is not a ${expectedType} diagram.`);
1891
1922
  return;
1892
1923
  }
1893
1924
  applyAndPush(transform ? transform(parsed) : parsed);
1925
+ onSuccess?.("Diagram imported successfully");
1894
1926
  } catch (err) {
1895
- alert(`Import failed: ${err.message}`);
1927
+ reportError(`Import failed: ${err.message}`);
1896
1928
  }
1897
- }, [applyAndPush, expectedType, transform]);
1929
+ }, [applyAndPush, expectedType, transform, onSuccess, onError]);
1930
+ }
1931
+
1932
+ // src/ui/hooks/useToast.ts
1933
+ var import_react9 = require("react");
1934
+ var _toastSeq = 0;
1935
+ function useToast() {
1936
+ const [toasts, setToasts] = (0, import_react9.useState)([]);
1937
+ const showToast = (0, import_react9.useCallback)((message, type = "info") => {
1938
+ const id = ++_toastSeq;
1939
+ setToasts((prev) => [...prev, { id, message, type }]);
1940
+ setTimeout(() => {
1941
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1942
+ }, 3e3);
1943
+ }, []);
1944
+ const dismissToast = (0, import_react9.useCallback)((id) => {
1945
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1946
+ }, []);
1947
+ return { toasts, showToast, dismissToast };
1948
+ }
1949
+
1950
+ // src/ui/ToastContainer.tsx
1951
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1952
+ var TOAST_COLORS = {
1953
+ success: { bg: "#065f46", border: "#10b981", text: "#ecfdf5" },
1954
+ error: { bg: "#7f1d1d", border: "#ef4444", text: "#fef2f2" },
1955
+ info: { bg: "#1e3a5f", border: "#3b82f6", text: "#eff6ff" }
1956
+ };
1957
+ var containerStyle = {
1958
+ position: "absolute",
1959
+ top: 8,
1960
+ right: 8,
1961
+ display: "flex",
1962
+ flexDirection: "column",
1963
+ gap: 6,
1964
+ zIndex: 9999,
1965
+ pointerEvents: "none"
1966
+ };
1967
+ function ToastContainer({ toasts, onDismiss }) {
1968
+ if (toasts.length === 0) return null;
1969
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: containerStyle, children: toasts.map((t) => {
1970
+ const c = TOAST_COLORS[t.type];
1971
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1972
+ "div",
1973
+ {
1974
+ role: "alert",
1975
+ "aria-live": "polite",
1976
+ style: {
1977
+ background: c.bg,
1978
+ border: `1px solid ${c.border}`,
1979
+ color: c.text,
1980
+ padding: "8px 14px",
1981
+ borderRadius: 8,
1982
+ fontSize: 12,
1983
+ fontWeight: 500,
1984
+ fontFamily: "ui-sans-serif,system-ui,sans-serif",
1985
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
1986
+ pointerEvents: "auto",
1987
+ cursor: "pointer",
1988
+ maxWidth: 280
1989
+ },
1990
+ onClick: () => onDismiss(t.id),
1991
+ children: [
1992
+ t.type === "success" && "\u2713 ",
1993
+ t.type === "error" && "\u2717 ",
1994
+ t.message
1995
+ ]
1996
+ },
1997
+ t.id
1998
+ );
1999
+ }) });
1898
2000
  }
1899
2001
 
1900
2002
  // src/ui/presets.ts
@@ -1994,13 +2096,13 @@ function cloneModel(m) {
1994
2096
  }
1995
2097
 
1996
2098
  // src/ui/hooks/useEditorKeyboard.ts
1997
- var import_react9 = require("react");
2099
+ var import_react10 = require("react");
1998
2100
  var isInput = (e) => {
1999
2101
  const tgt = e.target;
2000
2102
  return !!(tgt && (tgt.tagName === "INPUT" || tgt.tagName === "TEXTAREA" || tgt.isContentEditable));
2001
2103
  };
2002
2104
  function useEditorKeyboard(commands, deps) {
2003
- (0, import_react9.useEffect)(() => {
2105
+ (0, import_react10.useEffect)(() => {
2004
2106
  const onKey = (e) => {
2005
2107
  if (isInput(e)) return;
2006
2108
  for (const cmd of commands) {
@@ -2017,7 +2119,7 @@ function useEditorKeyboard(commands, deps) {
2017
2119
  }
2018
2120
 
2019
2121
  // src/ui/SequenceEditor.tsx
2020
- var import_jsx_runtime5 = require("react/jsx-runtime");
2122
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2021
2123
  var INDIGO2 = "#4f46e5";
2022
2124
  var INDIGO_SOFT2 = "#eef2ff";
2023
2125
  var lightTheme2 = {
@@ -2084,18 +2186,19 @@ function SequenceEditor({
2084
2186
  theme = "auto",
2085
2187
  themeOverrides
2086
2188
  }) {
2087
- const [model, setModel] = (0, import_react10.useState)(() => ensureSequenceModel(initialModel));
2088
- const [selected, setSelected] = (0, import_react10.useState)(null);
2089
- const [drag, setDrag] = (0, import_react10.useState)(null);
2090
- const [editingId, setEditingId] = (0, import_react10.useState)(null);
2091
- const [editLabel, setEditLabel] = (0, import_react10.useState)("");
2092
- const historyRef = (0, import_react10.useRef)([ensureSequenceModel(initialModel)]);
2093
- const historyIdxRef = (0, import_react10.useRef)(0);
2094
- const svgRef = (0, import_react10.useRef)(null);
2189
+ const [model, setModel] = (0, import_react11.useState)(() => ensureSequenceModel(initialModel));
2190
+ const { toasts, showToast, dismissToast } = useToast();
2191
+ const [selected, setSelected] = (0, import_react11.useState)(null);
2192
+ const [drag, setDrag] = (0, import_react11.useState)(null);
2193
+ const [editingId, setEditingId] = (0, import_react11.useState)(null);
2194
+ const [editLabel, setEditLabel] = (0, import_react11.useState)("");
2195
+ const historyRef = (0, import_react11.useRef)([ensureSequenceModel(initialModel)]);
2196
+ const historyIdxRef = (0, import_react11.useRef)(0);
2197
+ const svgRef = (0, import_react11.useRef)(null);
2095
2198
  const { t, isDark } = useEditorTheme(theme, themeOverrides, { light: lightTheme2, dark: darkTheme2 });
2096
2199
  const actors = model.actors ?? [];
2097
2200
  const messages = model.messages ?? [];
2098
- const colW = (0, import_react10.useMemo)(() => {
2201
+ const colW = (0, import_react11.useMemo)(() => {
2099
2202
  const longest = actors.reduce((m, a) => Math.max(m, a.length), 6);
2100
2203
  return Math.max(COL_MIN, longest * 9 + 40);
2101
2204
  }, [actors]);
@@ -2107,26 +2210,26 @@ function SequenceEditor({
2107
2210
  return SIDE_PAD2 + idx * colW + colW / 2;
2108
2211
  };
2109
2212
  const msgY = (idx) => HEADER_PAD2 + HEADER_H2 + 40 + idx * ROW_H2;
2110
- const pushHistory = (0, import_react10.useCallback)((m) => {
2213
+ const pushHistory = (0, import_react11.useCallback)((m) => {
2111
2214
  const stack = historyRef.current.slice(0, historyIdxRef.current + 1);
2112
2215
  stack.push(m);
2113
2216
  if (stack.length > 80) stack.shift();
2114
2217
  historyRef.current = stack;
2115
2218
  historyIdxRef.current = stack.length - 1;
2116
2219
  }, []);
2117
- const applyAndPush = (0, import_react10.useCallback)((m) => {
2220
+ const applyAndPush = (0, import_react11.useCallback)((m) => {
2118
2221
  setModel(m);
2119
2222
  onChange?.(m);
2120
2223
  pushHistory(m);
2121
2224
  }, [onChange, pushHistory]);
2122
- const undo = (0, import_react10.useCallback)(() => {
2225
+ const undo = (0, import_react11.useCallback)(() => {
2123
2226
  if (historyIdxRef.current <= 0) return;
2124
2227
  historyIdxRef.current--;
2125
2228
  const m = historyRef.current[historyIdxRef.current];
2126
2229
  setModel(m);
2127
2230
  onChange?.(m);
2128
2231
  }, [onChange]);
2129
- const redo = (0, import_react10.useCallback)(() => {
2232
+ const redo = (0, import_react11.useCallback)(() => {
2130
2233
  if (historyIdxRef.current >= historyRef.current.length - 1) return;
2131
2234
  historyIdxRef.current++;
2132
2235
  const m = historyRef.current[historyIdxRef.current];
@@ -2184,7 +2287,7 @@ function SequenceEditor({
2184
2287
  applyAndPush({ ...model, messages: messages.filter((m) => m.id !== id) });
2185
2288
  if (selected === id) setSelected(null);
2186
2289
  };
2187
- const reorderMessage = (0, import_react10.useCallback)((id, toIdx) => {
2290
+ const reorderMessage = (0, import_react11.useCallback)((id, toIdx) => {
2188
2291
  const fromIdx = messages.findIndex((m) => m.id === id);
2189
2292
  if (fromIdx < 0 || toIdx === fromIdx) return;
2190
2293
  const next = messages.slice();
@@ -2212,10 +2315,12 @@ function SequenceEditor({
2212
2315
  } }
2213
2316
  ];
2214
2317
  useEditorKeyboard(keyCommands, [undo, redo, selected]);
2215
- const handleExport = useExporters(model, onExport, "sequence");
2318
+ const handleExport = useExporters(model, onExport, "sequence", (msg) => showToast(msg, "success"));
2216
2319
  const handleImport = useImporter(applyAndPush, {
2217
2320
  expectedType: "sequence",
2218
- transform: ensureSequenceModel
2321
+ transform: ensureSequenceModel,
2322
+ onSuccess: (msg) => showToast(msg, "success"),
2323
+ onError: (msg) => showToast(msg, "error")
2219
2324
  });
2220
2325
  const onRowMouseDown = (e, id) => {
2221
2326
  const tag = e.target.tagName;
@@ -2226,7 +2331,7 @@ function SequenceEditor({
2226
2331
  setSelected(id);
2227
2332
  setDrag({ id, startY: e.clientY, originalIdx: idx, targetIdx: idx, active: false });
2228
2333
  };
2229
- (0, import_react10.useEffect)(() => {
2334
+ (0, import_react11.useEffect)(() => {
2230
2335
  if (!drag) return;
2231
2336
  const baseY = HEADER_PAD2 + HEADER_H2 + 40;
2232
2337
  const onMove = (ev) => {
@@ -2255,16 +2360,30 @@ function SequenceEditor({
2255
2360
  };
2256
2361
  }, [drag, messages.length, reorderMessage]);
2257
2362
  const selectedMsg = selected ? messages.find((m) => m.id === selected) : null;
2258
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
2363
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fsd-seq-editor", style: {
2259
2364
  display: "flex",
2260
2365
  flexDirection: "column",
2261
2366
  height,
2262
2367
  width: "100%",
2263
2368
  fontFamily: "ui-sans-serif,system-ui,sans-serif",
2264
- background: t.ctrlsBg
2369
+ background: t.ctrlsBg,
2370
+ position: "relative"
2265
2371
  }, children: [
2266
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
2267
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
2372
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToastContainer, { toasts, onDismiss: dismissToast }),
2373
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `
2374
+ .fsd-seq-editor [role="button"]:focus-visible {
2375
+ outline: 2px solid ${t.actorText};
2376
+ outline-offset: 2px;
2377
+ }
2378
+ .fsd-seq-editor button:focus-visible,
2379
+ .fsd-seq-editor input:focus-visible {
2380
+ outline: 2px solid ${t.actorText};
2381
+ outline-offset: 2px;
2382
+ border-radius: 4px;
2383
+ }
2384
+ ` }),
2385
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
2386
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
2268
2387
  display: "flex",
2269
2388
  gap: 8,
2270
2389
  padding: "7px 14px",
@@ -2273,12 +2392,12 @@ function SequenceEditor({
2273
2392
  alignItems: "center",
2274
2393
  flexWrap: "wrap"
2275
2394
  }, children: [
2276
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: addActor, style: primaryBtn(), children: "+ Actor" }),
2277
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: addMessage, style: primaryBtn(), children: "+ Message" }),
2278
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { width: 1, height: 18, background: t.ctrlsBorder, margin: "0 4px" } }),
2279
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: undo, style: ghostBtn2(t), title: "Undo (Ctrl+Z)", children: "\u21B6" }),
2280
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: redo, style: ghostBtn2(t), title: "Redo (Ctrl+Y)", children: "\u21B7" }),
2281
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
2395
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: addActor, style: primaryBtn(), children: "+ Actor" }),
2396
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: addMessage, style: primaryBtn(), children: "+ Message" }),
2397
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { width: 1, height: 18, background: t.ctrlsBorder, margin: "0 4px" } }),
2398
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: undo, style: ghostBtn2(t), title: "Undo (Ctrl+Z)", children: "\u21B6" }),
2399
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: redo, style: ghostBtn2(t), title: "Redo (Ctrl+Y)", children: "\u21B7" }),
2400
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
2282
2401
  actors.length,
2283
2402
  " actor",
2284
2403
  actors.length === 1 ? "" : "s",
@@ -2289,8 +2408,8 @@ function SequenceEditor({
2289
2408
  " \xB7 drag a row to reorder"
2290
2409
  ] })
2291
2410
  ] }),
2292
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
2293
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { flex: 1, overflow: "auto", background: t.canvas, position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2411
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
2412
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1, overflow: "auto", background: t.canvas, position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2294
2413
  SequenceCanvas,
2295
2414
  {
2296
2415
  model,
@@ -2313,17 +2432,18 @@ function SequenceEditor({
2313
2432
  svgRef
2314
2433
  }
2315
2434
  ) }),
2316
- selectedMsg && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
2435
+ selectedMsg && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
2317
2436
  width: 280,
2437
+ maxWidth: "40vw",
2318
2438
  flexShrink: 0,
2319
2439
  background: t.panelBg,
2320
2440
  borderLeft: `1px solid ${t.panelBorder}`,
2321
2441
  padding: "14px 16px",
2322
2442
  overflowY: "auto"
2323
2443
  }, children: [
2324
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.7, marginBottom: 10 }, children: "Message" }),
2325
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Label, { t, children: "Label" }),
2326
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2444
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.7, marginBottom: 10 }, children: "Message" }),
2445
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Label, { t, children: "Label" }),
2446
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2327
2447
  "input",
2328
2448
  {
2329
2449
  value: editLabel || selectedMsg.label,
@@ -2339,12 +2459,12 @@ function SequenceEditor({
2339
2459
  style: input(t)
2340
2460
  }
2341
2461
  ),
2342
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Label, { t, children: "From" }),
2343
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { value: selectedMsg.from, onChange: (e) => updateMessage(selectedMsg.id, { from: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: a, children: a }, a)) }),
2344
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Label, { t, children: "To" }),
2345
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("select", { value: selectedMsg.to, onChange: (e) => updateMessage(selectedMsg.id, { to: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: a, children: a }, a)) }),
2346
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Label, { t, children: "Style" }),
2347
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", gap: 6 }, children: ["solid", "dashed"].map((s2) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2462
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Label, { t, children: "From" }),
2463
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { value: selectedMsg.from, onChange: (e) => updateMessage(selectedMsg.id, { from: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: a, children: a }, a)) }),
2464
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Label, { t, children: "To" }),
2465
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { value: selectedMsg.to, onChange: (e) => updateMessage(selectedMsg.id, { to: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: a, children: a }, a)) }),
2466
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Label, { t, children: "Style" }),
2467
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 6 }, children: ["solid", "dashed"].map((s2) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2348
2468
  "button",
2349
2469
  {
2350
2470
  onClick: () => updateMessage(selectedMsg.id, { style: s2 }),
@@ -2364,8 +2484,8 @@ function SequenceEditor({
2364
2484
  },
2365
2485
  s2
2366
2486
  )) }),
2367
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { height: 14 } }),
2368
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2487
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { height: 14 } }),
2488
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2369
2489
  "button",
2370
2490
  {
2371
2491
  onClick: () => removeMessage(selectedMsg.id),
@@ -2375,7 +2495,7 @@ function SequenceEditor({
2375
2495
  )
2376
2496
  ] })
2377
2497
  ] }),
2378
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
2498
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
2379
2499
  padding: "4px 14px",
2380
2500
  fontSize: 11,
2381
2501
  color: t.textMuted,
@@ -2384,15 +2504,15 @@ function SequenceEditor({
2384
2504
  display: "flex",
2385
2505
  gap: 16
2386
2506
  }, children: [
2387
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
2507
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2388
2508
  actors.length,
2389
2509
  " actors"
2390
2510
  ] }),
2391
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
2511
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2392
2512
  messages.length,
2393
2513
  " messages"
2394
2514
  ] }),
2395
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { marginLeft: "auto" }, children: "double-click actor to rename \xB7 drag a row to reorder" })
2515
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { marginLeft: "auto" }, children: "double-click actor to rename \xB7 drag a row to reorder" })
2396
2516
  ] })
2397
2517
  ] });
2398
2518
  }
@@ -2438,12 +2558,12 @@ function input(t) {
2438
2558
  };
2439
2559
  }
2440
2560
  function Label({ t, children }) {
2441
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.6, marginBottom: 4 }, children });
2561
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.6, marginBottom: 4 }, children });
2442
2562
  }
2443
2563
 
2444
2564
  // src/ui/NodeNavigator.tsx
2445
- var import_react11 = require("react");
2446
- var import_jsx_runtime6 = require("react/jsx-runtime");
2565
+ var import_react12 = require("react");
2566
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2447
2567
  function NodeNavigator({
2448
2568
  model,
2449
2569
  selected,
@@ -2455,7 +2575,7 @@ function NodeNavigator({
2455
2575
  onToggle,
2456
2576
  onSelect
2457
2577
  }) {
2458
- const [search, setSearch] = (0, import_react11.useState)("");
2578
+ const [search, setSearch] = (0, import_react12.useState)("");
2459
2579
  const shapeIcon = (node) => {
2460
2580
  if (variant === "question") return "?";
2461
2581
  if (variant === "journey") return "\u2197";
@@ -2476,7 +2596,7 @@ function NodeNavigator({
2476
2596
  const inEdges = (id) => model.edges.filter((e) => e.to === id).length;
2477
2597
  const outEdges = (id) => model.edges.filter((e) => e.from === id).length;
2478
2598
  if (!open) {
2479
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
2599
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: {
2480
2600
  width: 36,
2481
2601
  flexShrink: 0,
2482
2602
  background: t.panelBg,
@@ -2487,19 +2607,21 @@ function NodeNavigator({
2487
2607
  paddingTop: 8,
2488
2608
  gap: 6
2489
2609
  }, children: [
2490
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2610
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2491
2611
  "button",
2492
2612
  {
2493
2613
  onClick: onToggle,
2494
2614
  title: "Open node list",
2615
+ "aria-expanded": false,
2616
+ "aria-label": "Open node list",
2495
2617
  style: { background: "none", border: "none", cursor: "pointer", color: t.textMuted, padding: 6, borderRadius: 6, fontSize: 14, lineHeight: 1 },
2496
2618
  children: "\u2630"
2497
2619
  }
2498
2620
  ),
2499
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 10, color: t.textMuted, fontWeight: 700, writingMode: "vertical-rl", transform: "rotate(180deg)", letterSpacing: 0.5 }, children: model.nodes.length })
2621
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 10, color: t.textMuted, fontWeight: 700, writingMode: "vertical-rl", transform: "rotate(180deg)", letterSpacing: 0.5 }, children: model.nodes.length })
2500
2622
  ] });
2501
2623
  }
2502
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
2624
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: {
2503
2625
  width: 216,
2504
2626
  flexShrink: 0,
2505
2627
  background: t.panelBg,
@@ -2508,7 +2630,7 @@ function NodeNavigator({
2508
2630
  flexDirection: "column",
2509
2631
  overflow: "hidden"
2510
2632
  }, children: [
2511
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
2633
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: {
2512
2634
  display: "flex",
2513
2635
  alignItems: "center",
2514
2636
  justifyContent: "space-between",
@@ -2516,9 +2638,9 @@ function NodeNavigator({
2516
2638
  borderBottom: `1px solid ${t.panelBorder}`,
2517
2639
  flexShrink: 0
2518
2640
  }, children: [
2519
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2520
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 11, fontWeight: 700, color: t.textSecondary, textTransform: "uppercase", letterSpacing: 0.7 }, children: variant === "question" ? "Questions" : variant === "journey" ? "Steps" : "Nodes" }),
2521
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: {
2641
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2642
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 11, fontWeight: 700, color: t.textSecondary, textTransform: "uppercase", letterSpacing: 0.7 }, children: variant === "question" ? "Questions" : variant === "journey" ? "Steps" : "Nodes" }),
2643
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: {
2522
2644
  fontSize: 10,
2523
2645
  fontWeight: 700,
2524
2646
  color: t.textMuted,
@@ -2527,19 +2649,21 @@ function NodeNavigator({
2527
2649
  borderRadius: 99
2528
2650
  }, children: model.nodes.length })
2529
2651
  ] }),
2530
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2652
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2531
2653
  "button",
2532
2654
  {
2533
2655
  onClick: onToggle,
2534
2656
  style: { background: "none", border: "none", cursor: "pointer", color: t.textMuted, padding: "2px 4px", borderRadius: 4, fontSize: 13, lineHeight: 1 },
2535
2657
  title: "Collapse",
2658
+ "aria-expanded": true,
2659
+ "aria-label": "Collapse node list",
2536
2660
  children: "\u2039"
2537
2661
  }
2538
2662
  )
2539
2663
  ] }),
2540
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { padding: "8px 10px", borderBottom: `1px solid ${t.sectionBorder}`, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { position: "relative" }, children: [
2541
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", fontSize: 11, color: t.textMuted, pointerEvents: "none" }, children: "\u2315" }),
2542
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2664
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { padding: "8px 10px", borderBottom: `1px solid ${t.sectionBorder}`, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { position: "relative" }, children: [
2665
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", fontSize: 11, color: t.textMuted, pointerEvents: "none" }, children: "\u2315" }),
2666
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2543
2667
  "input",
2544
2668
  {
2545
2669
  value: search,
@@ -2560,12 +2684,12 @@ function NodeNavigator({
2560
2684
  }
2561
2685
  )
2562
2686
  ] }) }),
2563
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, overflowY: "auto", padding: "6px 8px", display: "flex", flexDirection: "column", gap: 2 }, children: [
2564
- filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { textAlign: "center", padding: "20px 0", fontSize: 12, color: t.textMuted, fontStyle: "italic" }, children: model.nodes.length === 0 ? "No nodes yet" : "No matches" }),
2687
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { flex: 1, overflowY: "auto", padding: "6px 8px", display: "flex", flexDirection: "column", gap: 2 }, children: [
2688
+ filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { textAlign: "center", padding: "20px 0", fontSize: 12, color: t.textMuted, fontStyle: "italic" }, children: model.nodes.length === 0 ? "No nodes yet" : "No matches" }),
2565
2689
  filtered.map((node, idx) => {
2566
2690
  const isSelected = selected === node.id;
2567
2691
  const answers = node.metadata?.answers ?? [];
2568
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2692
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2569
2693
  "button",
2570
2694
  {
2571
2695
  onClick: () => onSelect(node.id),
@@ -2590,7 +2714,7 @@ function NodeNavigator({
2590
2714
  if (!isSelected) e.currentTarget.style.background = "transparent";
2591
2715
  },
2592
2716
  children: [
2593
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
2717
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
2594
2718
  width: 22,
2595
2719
  height: 22,
2596
2720
  borderRadius: 6,
@@ -2603,8 +2727,8 @@ function NodeNavigator({
2603
2727
  fontSize: variant === "journey" ? 9 : 11,
2604
2728
  fontWeight: 700
2605
2729
  }, children: variant === "journey" ? idx + 1 : shapeIcon(node) }),
2606
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
2607
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
2730
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
2731
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
2608
2732
  fontSize: 12,
2609
2733
  fontWeight: isSelected ? 600 : 400,
2610
2734
  color: isSelected ? acc.color : t.textPrimary,
@@ -2613,9 +2737,9 @@ function NodeNavigator({
2613
2737
  whiteSpace: "nowrap",
2614
2738
  lineHeight: 1.3
2615
2739
  }, children: node.label }),
2616
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 10, color: t.textMuted, lineHeight: 1.2, marginTop: 1 }, children: variant === "question" ? `${answers.length} answer${answers.length !== 1 ? "s" : ""}` : `${inEdges(node.id)}\u2193 ${outEdges(node.id)}\u2192` })
2740
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 10, color: t.textMuted, lineHeight: 1.2, marginTop: 1 }, children: variant === "question" ? `${answers.length} answer${answers.length !== 1 ? "s" : ""}` : `${inEdges(node.id)}\u2193 ${outEdges(node.id)}\u2192` })
2617
2741
  ] }),
2618
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 10, color: acc.color, flexShrink: 0 }, children: "\u25C9" })
2742
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 10, color: acc.color, flexShrink: 0 }, children: "\u25C9" })
2619
2743
  ]
2620
2744
  },
2621
2745
  node.id
@@ -2626,7 +2750,7 @@ function NodeNavigator({
2626
2750
  }
2627
2751
 
2628
2752
  // src/ui/render.tsx
2629
- var import_react12 = require("react");
2753
+ var import_react13 = require("react");
2630
2754
 
2631
2755
  // src/ui/layout.ts
2632
2756
  var NODE_H2 = 48;
@@ -2692,7 +2816,7 @@ function bezierPathVia(x1, y1, wx, wy, x2, y2) {
2692
2816
  }
2693
2817
 
2694
2818
  // src/ui/render.tsx
2695
- var import_jsx_runtime7 = require("react/jsx-runtime");
2819
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2696
2820
  var STYLE_LABEL = { pointerEvents: "none", userSelect: "none" };
2697
2821
  var STYLE_BLUR = { filter: "blur(4px)" };
2698
2822
  var STYLE_EDGE_HIT = { cursor: "pointer" };
@@ -2706,11 +2830,11 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2706
2830
  const stroke = selected ? acc.color : t.nodeStroke;
2707
2831
  const fill = selected ? t.nodeSelectedFill : t.nodeFill;
2708
2832
  const sw = selected ? 1.75 : 1.25;
2709
- const glow = selected && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: node.shape === "circle" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2710
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx, cy, r: NODE_H2 / 2 + 3, fill: "none", stroke: acc.color, strokeWidth: 6, opacity: 0.18, style: STYLE_BLUR }),
2711
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx, cy, r: NODE_H2 / 2 + 1.5, fill: "none", stroke: acc.color, strokeWidth: 1, opacity: 0.55 })
2712
- ] }) : node.shape === "diamond" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2713
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2833
+ const glow = selected && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: node.shape === "circle" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2834
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx, cy, r: NODE_H2 / 2 + 3, fill: "none", stroke: acc.color, strokeWidth: 6, opacity: 0.18, style: STYLE_BLUR }),
2835
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx, cy, r: NODE_H2 / 2 + 1.5, fill: "none", stroke: acc.color, strokeWidth: 1, opacity: 0.55 })
2836
+ ] }) : node.shape === "diamond" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2837
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2714
2838
  "polygon",
2715
2839
  {
2716
2840
  points: `${cx},${-5} ${w + 5},${cy} ${cx},${NODE_H2 + 5} ${-5},${cy}`,
@@ -2721,7 +2845,7 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2721
2845
  style: STYLE_BLUR
2722
2846
  }
2723
2847
  ),
2724
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2848
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2725
2849
  "polygon",
2726
2850
  {
2727
2851
  points: `${cx},${-2} ${w + 2},${cy} ${cx},${NODE_H2 + 2} ${-2},${cy}`,
@@ -2731,8 +2855,8 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2731
2855
  opacity: 0.55
2732
2856
  }
2733
2857
  )
2734
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2735
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2858
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2859
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2736
2860
  "rect",
2737
2861
  {
2738
2862
  x: -4,
@@ -2747,7 +2871,7 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2747
2871
  style: STYLE_BLUR
2748
2872
  }
2749
2873
  ),
2750
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2874
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2751
2875
  "rect",
2752
2876
  {
2753
2877
  x: -1.5,
@@ -2763,35 +2887,35 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2763
2887
  )
2764
2888
  ] }) });
2765
2889
  const badgeColor = isDark ? ACCENT.emeraldDark : ACCENT.emerald;
2766
- const badge = variant === "journey" && stepNumber !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2767
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 14, cy: 14, r: 10, fill: badgeColor }),
2768
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 14, y: 18, textAnchor: "middle", fontSize: 9, fill: "white", fontWeight: "700", style: STYLE_LABEL, children: stepNumber })
2890
+ const badge = variant === "journey" && stepNumber !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2891
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: 14, cy: 14, r: 10, fill: badgeColor }),
2892
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("text", { x: 14, y: 18, textAnchor: "middle", fontSize: 9, fill: "white", fontWeight: "700", style: STYLE_LABEL, children: stepNumber })
2769
2893
  ] });
2770
2894
  switch (node.shape) {
2771
2895
  case "diamond": {
2772
2896
  const pts = `${cx},0 ${w},${cy} ${cx},${NODE_H2} 0,${cy}`;
2773
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2897
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2774
2898
  glow,
2775
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: pts, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2899
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polygon", { points: pts, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2776
2900
  badge
2777
2901
  ] });
2778
2902
  }
2779
2903
  case "circle":
2780
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2904
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2781
2905
  glow,
2782
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx, cy, r: NODE_H2 / 2 - 1, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2906
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx, cy, r: NODE_H2 / 2 - 1, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2783
2907
  badge
2784
2908
  ] });
2785
2909
  case "parallelogram":
2786
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2910
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2787
2911
  glow,
2788
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `14,0 ${w},0 ${w - 14},${NODE_H2} 0,${NODE_H2}`, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2912
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polygon", { points: `14,0 ${w},0 ${w - 14},${NODE_H2} 0,${NODE_H2}`, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2789
2913
  badge
2790
2914
  ] });
2791
2915
  default:
2792
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2916
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2793
2917
  glow,
2794
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { width: w, height: NODE_H2, rx: 14, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2918
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { width: w, height: NODE_H2, rx: 14, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2795
2919
  badge
2796
2920
  ] });
2797
2921
  }
@@ -2812,8 +2936,8 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2812
2936
  const textSub = isDark ? "#64748b" : "#94a3b8";
2813
2937
  const textAns = isDark ? "#cbd5e1" : "#374151";
2814
2938
  const portRowY = Q_BASE_H2 + Q_ANS_ROW_H2 - 8;
2815
- const glow = selected && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2816
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2939
+ const glow = selected && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2940
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2817
2941
  "rect",
2818
2942
  {
2819
2943
  x: -4,
@@ -2828,7 +2952,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2828
2952
  style: STYLE_BLUR
2829
2953
  }
2830
2954
  ),
2831
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2955
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2832
2956
  "rect",
2833
2957
  {
2834
2958
  x: -1.5,
@@ -2843,29 +2967,29 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2843
2967
  }
2844
2968
  )
2845
2969
  ] });
2846
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2970
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2847
2971
  glow,
2848
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { width: qW, height: totalH, rx: 14, fill: nodeBg, stroke: nodeBorder, strokeWidth: selected ? 2 : 1.5, filter: "url(#nodeShadow)" }),
2849
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("clipPath", { id: `qhdr-${node.id}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { width: qW, height: Q_BASE_H2, rx: 14 }) }),
2850
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { width: qW, height: Q_BASE_H2, fill: amberSoft, clipPath: `url(#qhdr-${node.id})` }),
2851
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 0, y: 0, width: 4, height: Q_BASE_H2, rx: 2, fill: amber }),
2852
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 12, y: 14, width: 28, height: 28, rx: 8, fill: amber }),
2853
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 26, y: 33, textAnchor: "middle", fontSize: 15, fontWeight: "900", fill: "white", style: STYLE_LABEL, children: "?" }),
2854
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2972
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { width: qW, height: totalH, rx: 14, fill: nodeBg, stroke: nodeBorder, strokeWidth: selected ? 2 : 1.5, filter: "url(#nodeShadow)" }),
2973
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("clipPath", { id: `qhdr-${node.id}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { width: qW, height: Q_BASE_H2, rx: 14 }) }),
2974
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { width: qW, height: Q_BASE_H2, fill: amberSoft, clipPath: `url(#qhdr-${node.id})` }),
2975
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: 0, y: 0, width: 4, height: Q_BASE_H2, rx: 2, fill: amber }),
2976
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: 12, y: 14, width: 28, height: 28, rx: 8, fill: amber }),
2977
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("text", { x: 26, y: 33, textAnchor: "middle", fontSize: 15, fontWeight: "900", fill: "white", style: STYLE_LABEL, children: "?" }),
2978
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2855
2979
  "text",
2856
2980
  {
2857
2981
  style: STYLE_LABEL,
2858
2982
  fontFamily: "ui-sans-serif,system-ui,sans-serif",
2859
2983
  children: [
2860
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tspan", { x: 50, y: 27, fontSize: 9, fontWeight: 700, fill: textSub, letterSpacing: 0.6, textAnchor: "start", children: "QUESTION" }),
2861
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tspan", { x: 50, dy: 15, fontSize: 13, fontWeight: 700, fill: selected ? amber : textMain, textAnchor: "start", children: node.label })
2984
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tspan", { x: 50, y: 27, fontSize: 9, fontWeight: 700, fill: textSub, letterSpacing: 0.6, textAnchor: "start", children: "QUESTION" }),
2985
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tspan", { x: 50, dy: 15, fontSize: 13, fontWeight: 700, fill: selected ? amber : textMain, textAnchor: "start", children: node.label })
2862
2986
  ]
2863
2987
  }
2864
2988
  ),
2865
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: Q_BASE_H2, x2: qW, y2: Q_BASE_H2, stroke: amberLine, strokeWidth: 1 }),
2866
- answers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2867
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: qW / 2, y: Q_BASE_H2 + 22, textAnchor: "middle", fontSize: 10, fill: amber, opacity: 0.4, fontWeight: 600, style: STYLE_LABEL, children: "No answers yet" }),
2868
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: qW / 2, y: Q_BASE_H2 + 36, textAnchor: "middle", fontSize: 9, fill: textSub, opacity: 0.7, style: STYLE_LABEL, children: "Open panel \u2192 Add Answer" })
2989
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: 0, y1: Q_BASE_H2, x2: qW, y2: Q_BASE_H2, stroke: amberLine, strokeWidth: 1 }),
2990
+ answers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2991
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("text", { x: qW / 2, y: Q_BASE_H2 + 22, textAnchor: "middle", fontSize: 10, fill: amber, opacity: 0.4, fontWeight: 600, style: STYLE_LABEL, children: "No answers yet" }),
2992
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("text", { x: qW / 2, y: Q_BASE_H2 + 36, textAnchor: "middle", fontSize: 9, fill: textSub, opacity: 0.7, style: STYLE_LABEL, children: "Open panel \u2192 Add Answer" })
2869
2993
  ] }),
2870
2994
  answers.map((ans, i) => {
2871
2995
  const prevW = answers.slice(0, i).reduce((s2, a) => s2 + answerCardW2(a) + Q_CARD_PAD2, 0);
@@ -2878,8 +3002,8 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2878
3002
  const letter = i < 26 ? ANSWER_LETTERS[i] : `${i + 1}`;
2879
3003
  const maxChars = Math.max(2, Math.floor((cW - 20) / 7.5));
2880
3004
  const displayAns = ans.length > maxChars ? ans.slice(0, maxChars - 1) + "\u2026" : ans;
2881
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2882
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3005
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("g", { children: [
3006
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2883
3007
  "rect",
2884
3008
  {
2885
3009
  x: cardX,
@@ -2892,7 +3016,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2892
3016
  strokeWidth: connected ? 1.5 : 1
2893
3017
  }
2894
3018
  ),
2895
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3019
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2896
3020
  "rect",
2897
3021
  {
2898
3022
  x: cx - 11,
@@ -2903,7 +3027,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2903
3027
  fill: connected ? amber : isDark ? "#1e293b" : "#fef3c7"
2904
3028
  }
2905
3029
  ),
2906
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3030
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2907
3031
  "text",
2908
3032
  {
2909
3033
  x: cx,
@@ -2916,7 +3040,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2916
3040
  children: letter
2917
3041
  }
2918
3042
  ),
2919
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3043
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2920
3044
  "text",
2921
3045
  {
2922
3046
  x: cx,
@@ -2930,7 +3054,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2930
3054
  children: displayAns
2931
3055
  }
2932
3056
  ),
2933
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3057
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2934
3058
  "circle",
2935
3059
  {
2936
3060
  cx,
@@ -2943,7 +3067,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2943
3067
  onMouseDown: (e) => onAnswerPortDown(e, node.id, ans, cx, portRowY)
2944
3068
  }
2945
3069
  ),
2946
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3070
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2947
3071
  "path",
2948
3072
  {
2949
3073
  d: `M ${cx - 3} ${portRowY - 2} L ${cx} ${portRowY + 2} L ${cx + 3} ${portRowY - 2}`,
@@ -2960,7 +3084,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2960
3084
  ] });
2961
3085
  }
2962
3086
  function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, onEditChange, onEditCommit, onEditCancel, onDoubleClick, onContextMenu, onWaypointDown }) {
2963
- const [hovered, setHovered] = (0, import_react12.useState)(false);
3087
+ const [hovered, setHovered] = (0, import_react13.useState)(false);
2964
3088
  const from = nodes.find((n) => n.id === edge.from);
2965
3089
  const to = nodes.find((n) => n.id === edge.to);
2966
3090
  if (!from || !to) return null;
@@ -2999,7 +3123,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
2999
3123
  const labelW = edge.label ? Math.max(60, Math.ceil(estimateTextW2(edge.label, 7) + 18)) : 60;
3000
3124
  const showHandle = !!onWaypointDown && (hovered || !!wp);
3001
3125
  const flowClass = dash ? void 0 : isAmber ? "edge-flow-amber" : "edge-flow";
3002
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3126
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3003
3127
  "g",
3004
3128
  {
3005
3129
  onDoubleClick: (e) => {
@@ -3012,8 +3136,8 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3012
3136
  onMouseEnter: () => setHovered(true),
3013
3137
  onMouseLeave: () => setHovered(false),
3014
3138
  children: [
3015
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d, fill: "none", stroke: "transparent", strokeWidth: 14, style: STYLE_EDGE_HIT }),
3016
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3139
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d, fill: "none", stroke: "transparent", strokeWidth: 14, style: STYLE_EDGE_HIT }),
3140
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3017
3141
  "path",
3018
3142
  {
3019
3143
  d,
@@ -3028,7 +3152,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3028
3152
  style: STYLE_NO_EVENTS
3029
3153
  }
3030
3154
  ),
3031
- showHandle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3155
+ showHandle && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3032
3156
  "circle",
3033
3157
  {
3034
3158
  cx: hx,
@@ -3044,7 +3168,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3044
3168
  }
3045
3169
  }
3046
3170
  ),
3047
- editing && !isAmber ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("foreignObject", { x: mx - labelW / 2, y: my - 12, width: labelW, height: 22, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3171
+ editing && !isAmber ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("foreignObject", { x: mx - labelW / 2, y: my - 12, width: labelW, height: 22, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3048
3172
  "input",
3049
3173
  {
3050
3174
  autoFocus: true,
@@ -3078,8 +3202,8 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3078
3202
  fontFamily: "inherit"
3079
3203
  }
3080
3204
  }
3081
- ) }) : edge.label && !isAmber ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3082
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3205
+ ) }) : edge.label && !isAmber ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3206
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3083
3207
  "rect",
3084
3208
  {
3085
3209
  x: mx - labelW / 2,
@@ -3093,7 +3217,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3093
3217
  style: STYLE_EDGE_LABEL_HIT
3094
3218
  }
3095
3219
  ),
3096
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3220
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3097
3221
  "text",
3098
3222
  {
3099
3223
  x: mx,
@@ -3114,8 +3238,8 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3114
3238
  }
3115
3239
 
3116
3240
  // src/ui/Minimap.tsx
3117
- var import_react13 = require("react");
3118
- var import_jsx_runtime8 = require("react/jsx-runtime");
3241
+ var import_react14 = require("react");
3242
+ var import_jsx_runtime9 = require("react/jsx-runtime");
3119
3243
  var W = 168;
3120
3244
  var H = 112;
3121
3245
  var PAD = 18;
@@ -3129,7 +3253,7 @@ function Minimap({
3129
3253
  isDark,
3130
3254
  accentColor
3131
3255
  }) {
3132
- const dragRef = (0, import_react13.useRef)(null);
3256
+ const dragRef = (0, import_react14.useRef)(null);
3133
3257
  const boxes = model.nodes.map((n) => {
3134
3258
  const { w, h } = measureNode(n);
3135
3259
  return { id: n.id, x: n.x ?? 0, y: n.y ?? 0, w, h };
@@ -3163,7 +3287,7 @@ function Minimap({
3163
3287
  x: (mx - offsetX) / scale,
3164
3288
  y: (my - offsetY) / scale
3165
3289
  });
3166
- const panTo = (0, import_react13.useCallback)((e) => {
3290
+ const panTo = (0, import_react14.useCallback)((e) => {
3167
3291
  const rect = e.currentTarget.getBoundingClientRect();
3168
3292
  const mx = e.clientX - rect.left;
3169
3293
  const my = e.clientY - rect.top;
@@ -3195,9 +3319,11 @@ function Minimap({
3195
3319
  w: Math.max(2, Math.min(W, vp2.x) - Math.max(0, vp1.x)),
3196
3320
  h: Math.max(2, Math.min(H, vp2.y) - Math.max(0, vp1.y))
3197
3321
  };
3198
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3322
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3199
3323
  "div",
3200
3324
  {
3325
+ "aria-label": "Minimap \u2014 click to re-center the viewport",
3326
+ role: "img",
3201
3327
  style: {
3202
3328
  position: "absolute",
3203
3329
  bottom: 14,
@@ -3209,7 +3335,7 @@ function Minimap({
3209
3335
  boxShadow: isDark ? "0 8px 20px rgba(0,0,0,0.45)" : "0 6px 18px rgba(15,23,42,0.08)",
3210
3336
  backdropFilter: "blur(6px)"
3211
3337
  },
3212
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3338
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3213
3339
  "svg",
3214
3340
  {
3215
3341
  width: W,
@@ -3220,10 +3346,10 @@ function Minimap({
3220
3346
  onMouseUp,
3221
3347
  onMouseLeave: onMouseUp,
3222
3348
  children: [
3223
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { width: W, height: H, rx: 6, fill: isDark ? "#0f172a" : "#fafbfc" }),
3349
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { width: W, height: H, rx: 6, fill: isDark ? "#0f172a" : "#fafbfc" }),
3224
3350
  boxes.map((b) => {
3225
3351
  const p = project(b.x, b.y);
3226
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3352
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3227
3353
  "rect",
3228
3354
  {
3229
3355
  x: p.x,
@@ -3236,7 +3362,7 @@ function Minimap({
3236
3362
  b.id
3237
3363
  );
3238
3364
  }),
3239
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3365
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3240
3366
  "rect",
3241
3367
  {
3242
3368
  x: vpRect.x,
@@ -3257,8 +3383,8 @@ function Minimap({
3257
3383
  }
3258
3384
 
3259
3385
  // src/ui/ContextMenu.tsx
3260
- var import_react14 = require("react");
3261
- var import_jsx_runtime9 = require("react/jsx-runtime");
3386
+ var import_react15 = require("react");
3387
+ var import_jsx_runtime10 = require("react/jsx-runtime");
3262
3388
  function ContextMenu({
3263
3389
  x,
3264
3390
  y,
@@ -3287,9 +3413,9 @@ function ContextMenu({
3287
3413
  edgeHasWaypoint,
3288
3414
  containerRef
3289
3415
  }) {
3290
- const menuRef = (0, import_react14.useRef)(null);
3291
- const [pos, setPos] = (0, import_react14.useState)({ x, y });
3292
- (0, import_react14.useEffect)(() => {
3416
+ const menuRef = (0, import_react15.useRef)(null);
3417
+ const [pos, setPos] = (0, import_react15.useState)({ x, y });
3418
+ (0, import_react15.useEffect)(() => {
3293
3419
  if (!menuRef.current || !containerRef.current) return;
3294
3420
  const m = menuRef.current.getBoundingClientRect();
3295
3421
  const c = containerRef.current.getBoundingClientRect();
@@ -3304,7 +3430,7 @@ function ContextMenu({
3304
3430
  const dividerColor = isDark ? "#334155" : "#f1f5f9";
3305
3431
  const text = t.textPrimary;
3306
3432
  const muted = t.textMuted;
3307
- const item = (label, onClick, color, disabled) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3433
+ const item = (label, onClick, color, disabled) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3308
3434
  "button",
3309
3435
  {
3310
3436
  onClick: disabled ? void 0 : onClick,
@@ -3334,8 +3460,8 @@ function ContextMenu({
3334
3460
  },
3335
3461
  label
3336
3462
  );
3337
- const divider2 = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { height: 1, background: dividerColor, margin: "4px 0" } });
3338
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3463
+ const divider2 = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { height: 1, background: dividerColor, margin: "4px 0" } });
3464
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3339
3465
  "div",
3340
3466
  {
3341
3467
  ref: menuRef,
@@ -3353,30 +3479,30 @@ function ContextMenu({
3353
3479
  boxShadow: isDark ? "0 8px 32px rgba(0,0,0,0.5)" : "0 8px 32px rgba(0,0,0,0.12)",
3354
3480
  fontFamily: "ui-sans-serif,system-ui,sans-serif"
3355
3481
  },
3356
- children: edgeId ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
3357
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Edge" }),
3482
+ children: edgeId ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3483
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Edge" }),
3358
3484
  item("Rename label (dbl-click)", () => onEdgeRename?.()),
3359
3485
  divider2,
3360
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Style" }),
3486
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Style" }),
3361
3487
  item(`Solid${currentEdgeStyle === "solid" || !currentEdgeStyle ? " \u2713" : ""}`, () => onEdgeStyle?.("solid")),
3362
3488
  item(`Dashed${currentEdgeStyle === "dashed" ? " \u2713" : ""}`, () => onEdgeStyle?.("dashed")),
3363
3489
  item(`Dotted${currentEdgeStyle === "dotted" ? " \u2713" : ""}`, () => onEdgeStyle?.("dotted")),
3364
3490
  divider2,
3365
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Arrowhead" }),
3491
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Arrowhead" }),
3366
3492
  item(`Arrow${currentEdgeArrow !== "none" ? " \u2713" : ""}`, () => onEdgeArrowhead?.("arrow")),
3367
3493
  item(`None${currentEdgeArrow === "none" ? " \u2713" : ""}`, () => onEdgeArrowhead?.("none")),
3368
3494
  divider2,
3369
3495
  item("Reset routing", () => onEdgeResetRouting?.(), void 0, !edgeHasWaypoint),
3370
3496
  item("Delete edge", () => onEdgeDelete?.(), "#ef4444")
3371
- ] }) : nodeId ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
3372
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Node" }),
3497
+ ] }) : nodeId ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3498
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Node" }),
3373
3499
  item("Rename (dbl-click)", onRename),
3374
3500
  item("Duplicate", onDuplicate),
3375
3501
  item("Disconnect all edges", onDisconnect),
3376
3502
  divider2,
3377
3503
  item("Delete node", onDelete, "#ef4444")
3378
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
3379
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Canvas" }),
3504
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3505
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Canvas" }),
3380
3506
  item("Add node here", onAddNode, acc.color),
3381
3507
  item("Re-center (Ctrl+0)", onReCenter),
3382
3508
  divider2,
@@ -3388,7 +3514,7 @@ function ContextMenu({
3388
3514
  }
3389
3515
 
3390
3516
  // src/ui/DiagramCanvas.tsx
3391
- var import_jsx_runtime10 = require("react/jsx-runtime");
3517
+ var import_jsx_runtime11 = require("react/jsx-runtime");
3392
3518
  var STYLE_LABEL2 = { pointerEvents: "none", userSelect: "none" };
3393
3519
  var STYLE_LIVE_PORT = { opacity: 0.85, pointerEvents: "none" };
3394
3520
  var STYLE_NODE_GRAB = { cursor: "grab" };
@@ -3465,8 +3591,8 @@ function DiagramCanvas(props) {
3465
3591
  onCtxEdgeDelete,
3466
3592
  onCtxEdgeResetRouting
3467
3593
  } = props;
3468
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { ref: containerRef, style: { flex: 1, overflow: "hidden", position: "relative", background: t.canvas }, children: [
3469
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3594
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { ref: containerRef, style: { flex: 1, overflow: "hidden", position: "relative", background: t.canvas }, children: [
3595
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3470
3596
  "svg",
3471
3597
  {
3472
3598
  ref: svgRef,
@@ -3482,8 +3608,8 @@ function DiagramCanvas(props) {
3482
3608
  onMouseLeave: onMouseUp,
3483
3609
  onContextMenu: onSvgContextMenu,
3484
3610
  children: [
3485
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("defs", { children: [
3486
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("style", { children: reducedMotion ? `
3611
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("defs", { children: [
3612
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: reducedMotion ? `
3487
3613
  .edge-flow { stroke-dasharray: 0; }
3488
3614
  .edge-flow-amber { stroke-dasharray: 0; }
3489
3615
  .edge-live { stroke-dasharray: 4 4; }
@@ -3494,15 +3620,15 @@ function DiagramCanvas(props) {
3494
3620
  .edge-flow-amber { stroke-dasharray: 6 4; animation: edgeFlowFast 0.65s linear infinite; }
3495
3621
  .edge-live { stroke-dasharray: 7 5; animation: edgeFlow 0.55s linear infinite; }
3496
3622
  ` }),
3497
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("pattern", { id: "dots", width: GRID, height: GRID, patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: GRID / 2, cy: GRID / 2, r: 1.1, fill: t.dot }) }),
3498
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("filter", { id: "nodeShadow", x: "-25%", y: "-25%", width: "150%", height: "160%", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("feDropShadow", { dx: "0", dy: "3", stdDeviation: "5", floodColor: shadowClr, floodOpacity: "1" }) }),
3499
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("marker", { id: "arrowhead", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: arrowClr }) }),
3500
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("marker", { id: "arrowAmber", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: amberArrow }) }),
3501
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("marker", { id: "arrowLive", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: acc.color }) })
3623
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("pattern", { id: "dots", width: GRID, height: GRID, patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: GRID / 2, cy: GRID / 2, r: 1.1, fill: t.dot }) }),
3624
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("filter", { id: "nodeShadow", x: "-25%", y: "-25%", width: "150%", height: "160%", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("feDropShadow", { dx: "0", dy: "3", stdDeviation: "5", floodColor: shadowClr, floodOpacity: "1" }) }),
3625
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("marker", { id: "arrowhead", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: arrowClr }) }),
3626
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("marker", { id: "arrowAmber", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: amberArrow }) }),
3627
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("marker", { id: "arrowLive", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: acc.color }) })
3502
3628
  ] }),
3503
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { width: "100%", height: "100%", fill: "url(#dots)", "data-bg": "1" }),
3504
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${transform.x},${transform.y}) scale(${transform.scale})`, children: [
3505
- model.edges.map((e) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3629
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { width: "100%", height: "100%", fill: "url(#dots)", "data-bg": "1" }),
3630
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${transform.x},${transform.y}) scale(${transform.scale})`, children: [
3631
+ model.edges.map((e) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3506
3632
  EdgeLine,
3507
3633
  {
3508
3634
  edge: e,
@@ -3524,9 +3650,9 @@ function DiagramCanvas(props) {
3524
3650
  )),
3525
3651
  liveEdge && (() => {
3526
3652
  const d = bezierPath2(liveEdge.fromX, liveEdge.fromY, liveEdge.toX, liveEdge.toY, liveEdge.exitDir);
3527
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d, fill: "none", stroke: acc.color, strokeWidth: 2, strokeLinecap: "round", className: "edge-live", opacity: 0.8, markerEnd: "url(#arrowLive)" });
3653
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d, fill: "none", stroke: acc.color, strokeWidth: 2, strokeLinecap: "round", className: "edge-live", opacity: 0.8, markerEnd: "url(#arrowLive)" });
3528
3654
  })(),
3529
- alignGuides?.x && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3655
+ alignGuides?.x && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3530
3656
  "line",
3531
3657
  {
3532
3658
  x1: alignGuides.x.pos,
@@ -3540,7 +3666,7 @@ function DiagramCanvas(props) {
3540
3666
  pointerEvents: "none"
3541
3667
  }
3542
3668
  ),
3543
- alignGuides?.y && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3669
+ alignGuides?.y && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3544
3670
  "line",
3545
3671
  {
3546
3672
  y1: alignGuides.y.pos,
@@ -3559,11 +3685,12 @@ function DiagramCanvas(props) {
3559
3685
  const isQuestion2 = variant === "question";
3560
3686
  const { w: nW } = nodeDims(node, variant);
3561
3687
  const isSelected = selectedSet.has(node.id);
3562
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3688
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3563
3689
  "g",
3564
3690
  {
3565
3691
  transform: `translate(${node.x ?? 0},${node.y ?? 0})`,
3566
3692
  role: "button",
3693
+ tabIndex: 0,
3567
3694
  "aria-label": `${variantLabel} ${variant === "journey" ? idx + 1 + ": " : ""}${node.label}${isSelected ? ", selected" : ""}`,
3568
3695
  style: drag?.nodeId === node.id ? STYLE_NODE_GRABBING : STYLE_NODE_GRAB,
3569
3696
  onMouseDown: (e) => onNodeMouseDown(e, node.id),
@@ -3572,11 +3699,20 @@ function DiagramCanvas(props) {
3572
3699
  onContextMenu: (e) => onNodeContextMenu(e, node.id),
3573
3700
  onMouseEnter: () => setHoveredId(node.id),
3574
3701
  onMouseLeave: () => setHoveredId(null),
3702
+ onFocus: () => setHoveredId(node.id),
3703
+ onBlur: () => setHoveredId(null),
3704
+ onKeyDown: (e) => {
3705
+ if (e.key === "F2" || e.key === "Enter" && !e.ctrlKey && !e.metaKey) {
3706
+ e.preventDefault();
3707
+ setEditingId(node.id);
3708
+ setEditLabel(node.label);
3709
+ }
3710
+ },
3575
3711
  children: [
3576
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("title", { children: `${variantLabel}: ${node.label}` }),
3577
- isQuestion2 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(QuestionNode, { node, selected: isSelected, edges: model.edges, isDark, onAnswerPortDown, qW: nW }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
3578
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NodeShape, { node, selected: isSelected, variant, stepNumber: variant === "journey" ? idx + 1 : void 0, t, isDark, w: nW }),
3579
- editingId === node.id ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("foreignObject", { x: 6, y: 6, width: nW - 12, height: NODE_H2 - 12, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3712
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("title", { children: `${variantLabel}: ${node.label}` }),
3713
+ isQuestion2 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(QuestionNode, { node, selected: isSelected, edges: model.edges, isDark, onAnswerPortDown, qW: nW }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
3714
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(NodeShape, { node, selected: isSelected, variant, stepNumber: variant === "journey" ? idx + 1 : void 0, t, isDark, w: nW }),
3715
+ editingId === node.id ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("foreignObject", { x: 6, y: 6, width: nW - 12, height: NODE_H2 - 12, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3580
3716
  "input",
3581
3717
  {
3582
3718
  autoFocus: true,
@@ -3589,8 +3725,8 @@ function DiagramCanvas(props) {
3589
3725
  },
3590
3726
  style: { width: "100%", height: "100%", border: "none", borderRadius: 6, outline: `2px solid ${acc.color}`, textAlign: "center", fontSize: 13, fontWeight: 500, background: t.inputBg, boxSizing: "border-box", padding: "0 6px", fontFamily: "inherit", color: t.inputText }
3591
3727
  }
3592
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: nW / 2, y: NODE_H2 / 2 + 5, textAnchor: "middle", fontSize: 13, fontWeight: "500", fontFamily: "ui-sans-serif,system-ui,sans-serif", fill: isSelected ? acc.color : t.textPrimary, style: STYLE_LABEL2, children: node.label }),
3593
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3728
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: nW / 2, y: NODE_H2 / 2 + 5, textAnchor: "middle", fontSize: 13, fontWeight: "500", fontFamily: "ui-sans-serif,system-ui,sans-serif", fill: isSelected ? acc.color : t.textPrimary, style: STYLE_LABEL2, children: node.label }),
3729
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3594
3730
  "circle",
3595
3731
  {
3596
3732
  cx: nW / 2,
@@ -3604,7 +3740,7 @@ function DiagramCanvas(props) {
3604
3740
  }
3605
3741
  )
3606
3742
  ] }),
3607
- liveEdge && liveEdge.fromId !== node.id && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: nW / 2, cy: -1, r: portR, fill: acc.color, stroke: isDark ? "#0f172a" : "white", strokeWidth: 2, style: STYLE_LIVE_PORT })
3743
+ liveEdge && liveEdge.fromId !== node.id && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: nW / 2, cy: -1, r: portR, fill: acc.color, stroke: isDark ? "#0f172a" : "white", strokeWidth: 2, style: STYLE_LIVE_PORT })
3608
3744
  ]
3609
3745
  },
3610
3746
  node.id
@@ -3620,7 +3756,7 @@ function DiagramCanvas(props) {
3620
3756
  const top = Math.min(boxSel.sy, boxSel.cy) - rect.top;
3621
3757
  const w = Math.abs(boxSel.cx - boxSel.sx);
3622
3758
  const h = Math.abs(boxSel.cy - boxSel.sy);
3623
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3759
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3624
3760
  "div",
3625
3761
  {
3626
3762
  style: {
@@ -3637,18 +3773,18 @@ function DiagramCanvas(props) {
3637
3773
  }
3638
3774
  );
3639
3775
  })(),
3640
- model.nodes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", pointerEvents: "none", gap: 8 }, children: [
3641
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { fontSize: 36, opacity: 0.1, color: t.textPrimary }, children: variant === "question" ? "?" : variant === "journey" ? "\u2197" : "\u2B21" }),
3642
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { fontSize: 13, color: t.textMuted, fontWeight: 500 }, children: [
3776
+ model.nodes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", pointerEvents: "none", gap: 8 }, children: [
3777
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 36, opacity: 0.1, color: t.textPrimary }, children: variant === "question" ? "?" : variant === "journey" ? "\u2197" : "\u2B21" }),
3778
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { fontSize: 13, color: t.textMuted, fontWeight: 500 }, children: [
3643
3779
  "Click ",
3644
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("strong", { style: { color: acc.color }, children: [
3780
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("strong", { style: { color: acc.color }, children: [
3645
3781
  "+ ",
3646
3782
  variantLabel
3647
3783
  ] }),
3648
3784
  " to start"
3649
3785
  ] })
3650
3786
  ] }),
3651
- model.nodes.length > 0 && viewport.w > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3787
+ model.nodes.length > 0 && viewport.w > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3652
3788
  Minimap,
3653
3789
  {
3654
3790
  model,
@@ -3663,7 +3799,7 @@ function DiagramCanvas(props) {
3663
3799
  }
3664
3800
  }
3665
3801
  ),
3666
- ctxMenu && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3802
+ ctxMenu && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3667
3803
  ContextMenu,
3668
3804
  {
3669
3805
  x: ctxMenu.x,
@@ -3698,22 +3834,22 @@ function DiagramCanvas(props) {
3698
3834
  }
3699
3835
 
3700
3836
  // src/ui/hooks/useHistory.ts
3701
- var import_react15 = require("react");
3837
+ var import_react16 = require("react");
3702
3838
  var MAX_HISTORY = 80;
3703
3839
  function useHistory(initial, onChange) {
3704
- const [state, setState] = (0, import_react15.useState)(initial);
3705
- const stackRef = (0, import_react15.useRef)([initial]);
3706
- const idxRef = (0, import_react15.useRef)(0);
3707
- const [, setTick] = (0, import_react15.useState)(0);
3840
+ const [state, setState] = (0, import_react16.useState)(initial);
3841
+ const stackRef = (0, import_react16.useRef)([initial]);
3842
+ const idxRef = (0, import_react16.useRef)(0);
3843
+ const [, setTick] = (0, import_react16.useState)(0);
3708
3844
  const bump = () => setTick((n) => n + 1);
3709
- const apply = (0, import_react15.useCallback)(
3845
+ const apply = (0, import_react16.useCallback)(
3710
3846
  (next) => {
3711
3847
  setState(next);
3712
3848
  onChange?.(next);
3713
3849
  },
3714
3850
  [onChange]
3715
3851
  );
3716
- const applyAndPush = (0, import_react15.useCallback)(
3852
+ const applyAndPush = (0, import_react16.useCallback)(
3717
3853
  (next) => {
3718
3854
  const stack = stackRef.current.slice(0, idxRef.current + 1);
3719
3855
  stack.push(next);
@@ -3726,7 +3862,7 @@ function useHistory(initial, onChange) {
3726
3862
  },
3727
3863
  [onChange]
3728
3864
  );
3729
- const undo = (0, import_react15.useCallback)(() => {
3865
+ const undo = (0, import_react16.useCallback)(() => {
3730
3866
  if (idxRef.current <= 0) return;
3731
3867
  idxRef.current--;
3732
3868
  const next = stackRef.current[idxRef.current];
@@ -3734,7 +3870,7 @@ function useHistory(initial, onChange) {
3734
3870
  onChange?.(next);
3735
3871
  bump();
3736
3872
  }, [onChange]);
3737
- const redo = (0, import_react15.useCallback)(() => {
3873
+ const redo = (0, import_react16.useCallback)(() => {
3738
3874
  if (idxRef.current >= stackRef.current.length - 1) return;
3739
3875
  idxRef.current++;
3740
3876
  const next = stackRef.current[idxRef.current];
@@ -3754,10 +3890,10 @@ function useHistory(initial, onChange) {
3754
3890
  }
3755
3891
 
3756
3892
  // src/ui/hooks/useCanvasWheel.ts
3757
- var import_react16 = require("react");
3893
+ var import_react17 = require("react");
3758
3894
  function useCanvasWheel(ref, setTransform, options = {}) {
3759
3895
  const { min = 0.15, max = 3, factor = 0.1 } = options;
3760
- (0, import_react16.useEffect)(() => {
3896
+ (0, import_react17.useEffect)(() => {
3761
3897
  const el = ref.current;
3762
3898
  if (!el) return;
3763
3899
  const onWheel = (e) => {
@@ -3781,7 +3917,7 @@ function useCanvasWheel(ref, setTransform, options = {}) {
3781
3917
  }
3782
3918
 
3783
3919
  // src/ui/hooks/useCanvasTouch.ts
3784
- var import_react17 = require("react");
3920
+ var import_react18 = require("react");
3785
3921
  function useCanvasTouch(ref, {
3786
3922
  transform,
3787
3923
  setTransform,
@@ -3791,7 +3927,7 @@ function useCanvasTouch(ref, {
3791
3927
  longPressMs = 550,
3792
3928
  longPressSlop = 8
3793
3929
  }) {
3794
- (0, import_react17.useEffect)(() => {
3930
+ (0, import_react18.useEffect)(() => {
3795
3931
  const el = ref.current;
3796
3932
  if (!el) return;
3797
3933
  let touchPan = null;
@@ -3895,10 +4031,10 @@ function useCanvasTouch(ref, {
3895
4031
  }
3896
4032
 
3897
4033
  // src/ui/hooks/useElementSize.ts
3898
- var import_react18 = require("react");
4034
+ var import_react19 = require("react");
3899
4035
  function useElementSize(ref) {
3900
- const [size, setSize] = (0, import_react18.useState)({ w: 0, h: 0 });
3901
- (0, import_react18.useEffect)(() => {
4036
+ const [size, setSize] = (0, import_react19.useState)({ w: 0, h: 0 });
4037
+ (0, import_react19.useEffect)(() => {
3902
4038
  const el = ref.current;
3903
4039
  if (!el || typeof ResizeObserver === "undefined") return;
3904
4040
  const measure = () => {
@@ -4004,12 +4140,12 @@ function nearestInDirection(fromX, fromY, dir, candidates) {
4004
4140
  }
4005
4141
 
4006
4142
  // src/ui/DiagramEditor.tsx
4007
- var import_jsx_runtime11 = require("react/jsx-runtime");
4143
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4008
4144
  var STYLE_SR_ONLY = { position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap", border: 0 };
4009
4145
  var STYLE_FLEX_ROW = { flex: 1, display: "flex", overflow: "hidden" };
4010
4146
  function DiagramEditor(props) {
4011
4147
  if (props.initialModel?.type === "sequence") {
4012
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4148
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4013
4149
  SequenceEditor,
4014
4150
  {
4015
4151
  initialModel: props.initialModel,
@@ -4023,7 +4159,7 @@ function DiagramEditor(props) {
4023
4159
  }
4024
4160
  );
4025
4161
  }
4026
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(FlowchartEditor, { ...props });
4162
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FlowchartEditor, { ...props });
4027
4163
  }
4028
4164
  function FlowchartEditor({
4029
4165
  initialModel,
@@ -4037,25 +4173,26 @@ function FlowchartEditor({
4037
4173
  themeOverrides
4038
4174
  }) {
4039
4175
  const base = initialModel ? { ...initialModel, variant: initialModel.variant ?? variant } : presetFlowchartModel(variant);
4040
- const notify = (0, import_react19.useCallback)((m) => onChange?.(m), [onChange]);
4176
+ const notify = (0, import_react20.useCallback)((m) => onChange?.(m), [onChange]);
4041
4177
  const history = useHistory(base, notify);
4042
4178
  const { state: model, apply: applyModel, applyAndPush, undo, redo } = history;
4043
- const [transform, setTransform] = (0, import_react19.useState)({ x: 60, y: 60, scale: 1 });
4044
- const [selected, setSelected] = (0, import_react19.useState)(null);
4045
- const [selectedSet, setSelectedSet] = (0, import_react19.useState)(() => /* @__PURE__ */ new Set());
4046
- const [drag, setDrag] = (0, import_react19.useState)(null);
4047
- const [pan, setPan] = (0, import_react19.useState)(null);
4048
- const [boxSel, setBoxSel] = (0, import_react19.useState)(null);
4049
- const [liveEdge, setLiveEdge] = (0, import_react19.useState)(null);
4050
- const [alignGuides, setAlignGuides] = (0, import_react19.useState)(null);
4051
- const [waypointDrag, setWaypointDrag] = (0, import_react19.useState)(null);
4052
- const groupDragOriginsRef = (0, import_react19.useRef)(null);
4053
- const clipboardRef = (0, import_react19.useRef)(null);
4054
- const selectOne = (0, import_react19.useCallback)((id) => {
4179
+ const { toasts, showToast, dismissToast } = useToast();
4180
+ const [transform, setTransform] = (0, import_react20.useState)({ x: 60, y: 60, scale: 1 });
4181
+ const [selected, setSelected] = (0, import_react20.useState)(null);
4182
+ const [selectedSet, setSelectedSet] = (0, import_react20.useState)(() => /* @__PURE__ */ new Set());
4183
+ const [drag, setDrag] = (0, import_react20.useState)(null);
4184
+ const [pan, setPan] = (0, import_react20.useState)(null);
4185
+ const [boxSel, setBoxSel] = (0, import_react20.useState)(null);
4186
+ const [liveEdge, setLiveEdge] = (0, import_react20.useState)(null);
4187
+ const [alignGuides, setAlignGuides] = (0, import_react20.useState)(null);
4188
+ const [waypointDrag, setWaypointDrag] = (0, import_react20.useState)(null);
4189
+ const groupDragOriginsRef = (0, import_react20.useRef)(null);
4190
+ const clipboardRef = (0, import_react20.useRef)(null);
4191
+ const selectOne = (0, import_react20.useCallback)((id) => {
4055
4192
  setSelected(id);
4056
4193
  setSelectedSet(id ? /* @__PURE__ */ new Set([id]) : /* @__PURE__ */ new Set());
4057
4194
  }, []);
4058
- const toggleSelect = (0, import_react19.useCallback)((id) => {
4195
+ const toggleSelect = (0, import_react20.useCallback)((id) => {
4059
4196
  setSelectedSet((prev) => {
4060
4197
  const next = new Set(prev);
4061
4198
  if (next.has(id)) {
@@ -4069,26 +4206,26 @@ function FlowchartEditor({
4069
4206
  return next;
4070
4207
  });
4071
4208
  }, []);
4072
- const clearSelection = (0, import_react19.useCallback)(() => {
4209
+ const clearSelection = (0, import_react20.useCallback)(() => {
4073
4210
  setSelected(null);
4074
4211
  setSelectedSet(/* @__PURE__ */ new Set());
4075
4212
  }, []);
4076
- const [editingId, setEditingId] = (0, import_react19.useState)(null);
4077
- const [editLabel, setEditLabel] = (0, import_react19.useState)("");
4078
- const [editingEdgeId, setEditingEdgeId] = (0, import_react19.useState)(null);
4079
- const [editEdgeLabel, setEditEdgeLabel] = (0, import_react19.useState)("");
4080
- const [hoveredId, setHoveredId] = (0, import_react19.useState)(null);
4081
- const [ctxMenu, setCtxMenu] = (0, import_react19.useState)(null);
4082
- const [navOpen, setNavOpen] = (0, import_react19.useState)(true);
4083
- const [announcement, setAnnouncement] = (0, import_react19.useState)("");
4084
- const svgRef = (0, import_react19.useRef)(null);
4085
- const containerRef = (0, import_react19.useRef)(null);
4213
+ const [editingId, setEditingId] = (0, import_react20.useState)(null);
4214
+ const [editLabel, setEditLabel] = (0, import_react20.useState)("");
4215
+ const [editingEdgeId, setEditingEdgeId] = (0, import_react20.useState)(null);
4216
+ const [editEdgeLabel, setEditEdgeLabel] = (0, import_react20.useState)("");
4217
+ const [hoveredId, setHoveredId] = (0, import_react20.useState)(null);
4218
+ const [ctxMenu, setCtxMenu] = (0, import_react20.useState)(null);
4219
+ const [navOpen, setNavOpen] = (0, import_react20.useState)(true);
4220
+ const [announcement, setAnnouncement] = (0, import_react20.useState)("");
4221
+ const svgRef = (0, import_react20.useRef)(null);
4222
+ const containerRef = (0, import_react20.useRef)(null);
4086
4223
  const reducedMotion = usePrefersReducedMotion();
4087
4224
  const { t, isDark } = useEditorTheme(theme, themeOverrides, { light: lightTheme, dark: darkTheme });
4088
4225
  const isCoarse = useIsCoarsePointer();
4089
4226
  const portR = isCoarse ? 9 : 6;
4090
4227
  const viewport = useElementSize(svgRef);
4091
- const reCenter = (0, import_react19.useCallback)(() => {
4228
+ const reCenter = (0, import_react20.useCallback)(() => {
4092
4229
  if (!svgRef.current) return;
4093
4230
  const rect = svgRef.current.getBoundingClientRect();
4094
4231
  const W2 = rect.width, H2 = rect.height;
@@ -4112,7 +4249,7 @@ function FlowchartEditor({
4112
4249
  const cx = (minX + maxX) / 2, cy = (minY + maxY) / 2;
4113
4250
  setTransform({ scale, x: W2 / 2 - cx * scale, y: H2 / 2 - cy * scale });
4114
4251
  }, [model.nodes, variant]);
4115
- const jumpToNode = (0, import_react19.useCallback)((nodeId) => {
4252
+ const jumpToNode = (0, import_react20.useCallback)((nodeId) => {
4116
4253
  const node = model.nodes.find((n) => n.id === nodeId);
4117
4254
  if (!node || !svgRef.current) return;
4118
4255
  const rect = svgRef.current.getBoundingClientRect();
@@ -4123,7 +4260,7 @@ function FlowchartEditor({
4123
4260
  setTransform({ scale, x: rect.width / 2 - cx * scale, y: rect.height / 2 - cy * scale });
4124
4261
  selectOne(nodeId);
4125
4262
  }, [model.nodes, variant, transform.scale, selectOne]);
4126
- const duplicateIds = (0, import_react19.useCallback)((ids) => {
4263
+ const duplicateIds = (0, import_react20.useCallback)((ids) => {
4127
4264
  if (ids.length === 0) return;
4128
4265
  const idSet = new Set(ids);
4129
4266
  const idMap = /* @__PURE__ */ new Map();
@@ -4155,10 +4292,10 @@ function FlowchartEditor({
4155
4292
  setSelected(newIds[newIds.length - 1] ?? null);
4156
4293
  setSelectedSet(new Set(newIds));
4157
4294
  }, [model, applyAndPush]);
4158
- const duplicateNode = (0, import_react19.useCallback)((nodeId) => {
4295
+ const duplicateNode = (0, import_react20.useCallback)((nodeId) => {
4159
4296
  duplicateIds([nodeId]);
4160
4297
  }, [duplicateIds]);
4161
- (0, import_react19.useEffect)(() => {
4298
+ (0, import_react20.useEffect)(() => {
4162
4299
  if (!ctxMenu) return;
4163
4300
  const close = () => setCtxMenu(null);
4164
4301
  window.addEventListener("mousedown", close);
@@ -4290,12 +4427,12 @@ function FlowchartEditor({
4290
4427
  }
4291
4428
  ];
4292
4429
  useEditorKeyboard(keyCommands, [undo, redo, reCenter, selected, selectedSet, ctxMenu, liveEdge, editingId, boxSel, model, applyAndPush, duplicateNode, clearSelection]);
4293
- const toCanvas = (0, import_react19.useCallback)((clientX, clientY) => {
4430
+ const toCanvas = (0, import_react20.useCallback)((clientX, clientY) => {
4294
4431
  const rect = svgRef.current.getBoundingClientRect();
4295
4432
  return { x: (clientX - rect.left - transform.x) / transform.scale, y: (clientY - rect.top - transform.y) / transform.scale };
4296
4433
  }, [transform]);
4297
4434
  useCanvasWheel(svgRef, setTransform);
4298
- const onCanvasLongPress = (0, import_react19.useCallback)((x, y) => {
4435
+ const onCanvasLongPress = (0, import_react20.useCallback)((x, y) => {
4299
4436
  setCtxMenu({ x, y, nodeId: null });
4300
4437
  }, []);
4301
4438
  useCanvasTouch(svgRef, { transform, setTransform, onLongPress: onCanvasLongPress });
@@ -4566,8 +4703,8 @@ function FlowchartEditor({
4566
4703
  };
4567
4704
  applyAndPush(updated);
4568
4705
  };
4569
- const handleExport = useExporters(model, onExport, "diagram");
4570
- const positionFlowchartNodes = (0, import_react19.useCallback)((m) => ({
4706
+ const handleExport = useExporters(model, onExport, "diagram", (msg) => showToast(msg, "success"));
4707
+ const positionFlowchartNodes = (0, import_react20.useCallback)((m) => ({
4571
4708
  ...m,
4572
4709
  nodes: m.nodes.map((n, i) => ({
4573
4710
  ...n,
@@ -4575,14 +4712,19 @@ function FlowchartEditor({
4575
4712
  y: n.y ?? snap(80 + Math.floor(i / 4) * 140)
4576
4713
  }))
4577
4714
  }), []);
4578
- const handleImport = useImporter(applyAndPush, { transform: positionFlowchartNodes });
4715
+ const handleImport = useImporter(applyAndPush, {
4716
+ transform: positionFlowchartNodes,
4717
+ onSuccess: (msg) => showToast(msg, "success"),
4718
+ onError: (msg) => showToast(msg, "error")
4719
+ });
4579
4720
  const acc = variantAccent(variant, isDark);
4580
4721
  const variantLabel = variant === "question" ? "Question" : variant === "journey" ? "Step" : "Node";
4581
4722
  const shadowClr = shadowColor(isDark);
4582
4723
  const arrowClr = arrowColor(isDark);
4583
4724
  const amberArrow = isDark ? ACCENT.amberDark : ACCENT.amber;
4584
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fsd-editor", style: { display: "flex", flexDirection: "column", height, width: "100%", fontFamily: "ui-sans-serif,system-ui,sans-serif", boxSizing: "border-box", background: t.ctrlsBg }, children: [
4585
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: `
4725
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "fsd-editor", style: { display: "flex", flexDirection: "column", height, width: "100%", fontFamily: "ui-sans-serif,system-ui,sans-serif", boxSizing: "border-box", background: t.ctrlsBg, position: "relative" }, children: [
4726
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ToastContainer, { toasts, onDismiss: dismissToast }),
4727
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("style", { children: `
4586
4728
  .fsd-editor button:focus-visible,
4587
4729
  .fsd-editor input:focus-visible,
4588
4730
  .fsd-editor textarea:focus-visible,
@@ -4592,12 +4734,16 @@ function FlowchartEditor({
4592
4734
  outline-offset: 2px;
4593
4735
  border-radius: 6px;
4594
4736
  }
4737
+ .fsd-editor svg [role="button"]:focus-visible {
4738
+ outline: 2px solid ${acc.color};
4739
+ outline-offset: 3px;
4740
+ }
4595
4741
  .fsd-editor svg[role="application"]:focus-visible {
4596
4742
  outline: 2px solid ${acc.color};
4597
4743
  outline-offset: -2px;
4598
4744
  }
4599
4745
  ` }),
4600
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4746
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4601
4747
  "div",
4602
4748
  {
4603
4749
  role: "status",
@@ -4607,28 +4753,28 @@ function FlowchartEditor({
4607
4753
  children: announcement
4608
4754
  }
4609
4755
  ),
4610
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
4611
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", gap: 6, padding: "7px 14px", background: t.ctrlsBg, borderBottom: `1px solid ${t.ctrlsBorder}`, alignItems: "center", flexWrap: "wrap" }, children: [
4612
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { onClick: () => addNode(), style: ctrlBtn(acc.color, isDark), children: [
4756
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
4757
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", gap: 6, padding: "7px 14px", background: t.ctrlsBg, borderBottom: `1px solid ${t.ctrlsBorder}`, alignItems: "center", flexWrap: "wrap" }, children: [
4758
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { onClick: () => addNode(), style: ctrlBtn(acc.color, isDark), children: [
4613
4759
  "+ ",
4614
4760
  variantLabel
4615
4761
  ] }),
4616
- selectedSet.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4617
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { width: 1, height: 20, background: t.ctrlsBorder, margin: "0 2px" } }),
4618
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: deleteSelected, style: { ...ctrlBtn("transparent", isDark), color: "#ef4444", border: `1px solid ${isDark ? "#7f1d1d" : "#fca5a5"}` }, children: selectedSet.size > 1 ? `Delete (${selectedSet.size})` : "Delete" })
4762
+ selectedSet.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
4763
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { width: 1, height: 20, background: t.ctrlsBorder, margin: "0 2px" } }),
4764
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { onClick: deleteSelected, style: { ...ctrlBtn("transparent", isDark), color: "#ef4444", border: `1px solid ${isDark ? "#7f1d1d" : "#fca5a5"}` }, children: selectedSet.size > 1 ? `Delete (${selectedSet.size})` : "Delete" })
4619
4765
  ] }),
4620
- liveEdge && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { style: { fontSize: 11, color: acc.color, fontWeight: 600, marginLeft: 6 }, children: [
4766
+ liveEdge && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { style: { fontSize: 11, color: acc.color, fontWeight: 600, marginLeft: 6 }, children: [
4621
4767
  liveEdge.answerLabel ? `Routing "${liveEdge.answerLabel}" \u2192` : "Drop on a node to connect",
4622
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontWeight: 400, color: t.textMuted, marginLeft: 6 }, children: "release to cancel" })
4768
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { fontWeight: 400, color: t.textMuted, marginLeft: 6 }, children: "release to cancel" })
4623
4769
  ] }),
4624
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
4770
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
4625
4771
  variant === "question" ? "drag answer port to connect \xB7 " : "drag port dot \xB7 ",
4626
4772
  "scroll to zoom \xB7 drag to pan"
4627
4773
  ] })
4628
4774
  ] }),
4629
- variant !== "flowchart" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { padding: "3px 14px", background: acc.fill, borderBottom: `1px solid ${acc.border}`, fontSize: 11, color: acc.color, fontWeight: 600 }, children: variant === "question" ? "? Question Flow \u2014 add answers in the panel, drag their port to connect" : "\u2197 Journey Map \u2014 numbered steps, drag port to sequence" }),
4630
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: STYLE_FLEX_ROW, children: [
4631
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4775
+ variant !== "flowchart" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { padding: "3px 14px", background: acc.fill, borderBottom: `1px solid ${acc.border}`, fontSize: 11, color: acc.color, fontWeight: 600 }, children: variant === "question" ? "? Question Flow \u2014 add answers in the panel, drag their port to connect" : "\u2197 Journey Map \u2014 numbered steps, drag port to sequence" }),
4776
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: STYLE_FLEX_ROW, children: [
4777
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4632
4778
  NodeNavigator,
4633
4779
  {
4634
4780
  model,
@@ -4642,7 +4788,7 @@ function FlowchartEditor({
4642
4788
  onSelect: jumpToNode
4643
4789
  }
4644
4790
  ),
4645
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4791
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4646
4792
  DiagramCanvas,
4647
4793
  {
4648
4794
  model,
@@ -4777,27 +4923,27 @@ function FlowchartEditor({
4777
4923
  }
4778
4924
  }
4779
4925
  ),
4780
- selected && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StepEditor, { nodeId: selected, model, onModelChange: (m) => {
4926
+ selected && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(StepEditor, { nodeId: selected, model, onModelChange: (m) => {
4781
4927
  applyAndPush(m);
4782
4928
  }, variant, isDark, t, acc }, selected)
4783
4929
  ] }),
4784
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { padding: "4px 14px", fontSize: 11, color: t.textMuted, background: t.statusBg, borderTop: `1px solid ${t.ctrlsBorder}`, display: "flex", gap: 16 }, children: [
4785
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
4930
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { padding: "4px 14px", fontSize: 11, color: t.textMuted, background: t.statusBg, borderTop: `1px solid ${t.ctrlsBorder}`, display: "flex", gap: 16, flexWrap: "wrap", overflow: "hidden", maxHeight: 28 }, children: [
4931
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
4786
4932
  model.nodes.length,
4787
4933
  " ",
4788
4934
  variantLabel.toLowerCase(),
4789
4935
  "s"
4790
4936
  ] }),
4791
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
4937
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
4792
4938
  model.edges.length,
4793
4939
  " connections"
4794
4940
  ] }),
4795
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
4941
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
4796
4942
  Math.round(transform.scale * 100),
4797
4943
  "% zoom"
4798
4944
  ] }),
4799
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { marginLeft: "auto" }, children: "Ctrl+Z undo \xB7 Ctrl+Y redo \xB7 Ctrl+0 fit \xB7 Alt+Arrow traverse" }),
4800
- selected && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { color: acc.color }, children: model.nodes.find((n) => n.id === selected)?.label })
4945
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { marginLeft: "auto", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: "Ctrl+Z undo \xB7 Ctrl+Y redo \xB7 Ctrl+0 fit \xB7 Alt+Arrow traverse" }),
4946
+ selected && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { color: acc.color }, children: model.nodes.find((n) => n.id === selected)?.label })
4801
4947
  ] })
4802
4948
  ] });
4803
4949
  }