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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/ui/DiagramEditor.tsx
2
- import { useCallback as useCallback7, useEffect as useEffect10, useRef as useRef7, useState as useState11 } from "react";
2
+ import { useCallback as useCallback8, useEffect as useEffect10, useRef as useRef7, useState as useState12 } from "react";
3
3
 
4
4
  // src/ui/Toolbar.tsx
5
5
  import { useState as useState2 } from "react";
@@ -412,10 +412,10 @@ function Toolbar({ onExport, onImport, allowedExports, allowImport = true }) {
412
412
  ] }),
413
413
  /* @__PURE__ */ jsx2("div", { style: divider }),
414
414
  /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 4, alignItems: "center" }, children: [
415
- allowImport && onImport && /* @__PURE__ */ jsx2("button", { onClick: () => setImportOpen(true), style: ghostBtn, children: "\u2191 Import" }),
415
+ allowImport && onImport && /* @__PURE__ */ jsx2("button", { onClick: () => setImportOpen(true), "aria-label": "Import diagram", style: ghostBtn, children: "\u2191 Import" }),
416
416
  formats.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
417
417
  /* @__PURE__ */ jsx2("span", { style: { fontSize: 11, color: darkTheme.inputText, margin: "0 4px" }, children: "Export \u2192" }),
418
- formats.map((f) => /* @__PURE__ */ jsx2("button", { onClick: () => onExport(f.key), style: exportBtn, children: f.label }, f.key))
418
+ formats.map((f) => /* @__PURE__ */ jsx2("button", { onClick: () => onExport(f.key), "aria-label": `Export as ${f.label}`, style: exportBtn, children: f.label }, f.key))
419
419
  ] })
420
420
  ] }),
421
421
  onImport && /* @__PURE__ */ jsx2(
@@ -693,7 +693,7 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
693
693
  /* @__PURE__ */ jsx3("label", { style: { display: "block", fontSize: 10, fontWeight: 700, color: tt.labelText, marginBottom: 8, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Shape" }),
694
694
  /* @__PURE__ */ jsx3("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6 }, children: SHAPES.map((s2) => {
695
695
  const active = (node.shape ?? "rectangle") === s2.key;
696
- return /* @__PURE__ */ jsxs3("button", { onClick: () => setShape(s2.key), style: {
696
+ return /* @__PURE__ */ jsxs3("button", { onClick: () => setShape(s2.key), "aria-pressed": active, style: {
697
697
  display: "flex",
698
698
  flexDirection: "column",
699
699
  alignItems: "center",
@@ -740,7 +740,12 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
740
740
  /* @__PURE__ */ jsx3("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" })
741
741
  ] }, ans + i);
742
742
  }),
743
- addingAnswer ? /* @__PURE__ */ jsxs3("div", { style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
743
+ addingAnswer ? /* @__PURE__ */ jsxs3("div", { role: "group", "aria-label": "Add answer form", onKeyDown: (e) => {
744
+ if (e.key === "Escape") {
745
+ setAddingAnswer(false);
746
+ setNewAnswer("");
747
+ }
748
+ }, style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
744
749
  /* @__PURE__ */ jsx3("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 } }),
745
750
  /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 6 }, children: [
746
751
  /* @__PURE__ */ jsx3("button", { onClick: addAnswer, style: addBtnStyle, children: "Add Answer" }),
@@ -778,7 +783,9 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
778
783
  /* @__PURE__ */ jsx3("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" })
779
784
  ] }, edge.id);
780
785
  }),
781
- addingBranch ? /* @__PURE__ */ jsxs3("div", { style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
786
+ addingBranch ? /* @__PURE__ */ jsxs3("div", { role: "group", "aria-label": "Add branch form", onKeyDown: (e) => {
787
+ if (e.key === "Escape") setAddingBranch(false);
788
+ }, style: { marginTop: 10, background: tt.addFormBg, borderRadius: 10, padding: 12, border: `1.5px solid ${accentBorder}` }, children: [
782
789
  /* @__PURE__ */ jsx3("div", { style: { display: "flex", gap: 6, marginBottom: 10 }, children: ["new", "existing"].map((mode) => /* @__PURE__ */ jsx3("button", { onClick: () => setBranchMode(mode), style: {
783
790
  flex: 1,
784
791
  padding: "5px 0",
@@ -813,7 +820,7 @@ function StepEditor({ nodeId, model, onModelChange, variant = "flowchart", isDar
813
820
  }
814
821
 
815
822
  // src/ui/SequenceEditor.tsx
816
- import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef3, useState as useState5 } from "react";
823
+ import { useCallback as useCallback5, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef3, useState as useState6 } from "react";
817
824
 
818
825
  // src/ui/SequenceCanvas.tsx
819
826
  import { useMemo } from "react";
@@ -1039,8 +1046,17 @@ function SequenceCanvas(props) {
1039
1046
  fontSize: 13,
1040
1047
  fontWeight: 700,
1041
1048
  fill: t.actorText,
1049
+ role: "button",
1050
+ tabIndex: 0,
1051
+ "aria-label": `Actor ${name} \u2014 press Enter or F2 to rename`,
1042
1052
  style: STYLE_SEQ_ACTOR_TEXT,
1043
1053
  onDoubleClick: () => setEditingId(name),
1054
+ onKeyDown: (e) => {
1055
+ if (e.key === "Enter" || e.key === "F2") {
1056
+ e.preventDefault();
1057
+ setEditingId(name);
1058
+ }
1059
+ },
1044
1060
  children: name
1045
1061
  }
1046
1062
  ),
@@ -1051,9 +1067,21 @@ function SequenceCanvas(props) {
1051
1067
  cy: HEADER_PAD + 14,
1052
1068
  r: 9,
1053
1069
  fill: "transparent",
1070
+ role: "button",
1071
+ tabIndex: 0,
1072
+ "aria-label": `Remove actor ${name}`,
1054
1073
  style: STYLE_SEQ_REMOVE_BTN,
1055
1074
  onClick: () => removeActor(name),
1056
- children: /* @__PURE__ */ jsx4("title", { children: "Remove actor" })
1075
+ onKeyDown: (e) => {
1076
+ if (e.key === "Enter" || e.key === " ") {
1077
+ e.preventDefault();
1078
+ removeActor(name);
1079
+ }
1080
+ },
1081
+ children: /* @__PURE__ */ jsxs4("title", { children: [
1082
+ "Remove actor ",
1083
+ name
1084
+ ] })
1057
1085
  }
1058
1086
  ),
1059
1087
  /* @__PURE__ */ jsx4(
@@ -1520,7 +1548,7 @@ async function toPNG(model) {
1520
1548
  }
1521
1549
 
1522
1550
  // src/ui/hooks/useExporters.ts
1523
- function useExporters(model, onExport, filename = "diagram") {
1551
+ function useExporters(model, onExport, filename = "diagram", onSuccess) {
1524
1552
  return useCallback2(async (format) => {
1525
1553
  let content;
1526
1554
  switch (format) {
@@ -1544,6 +1572,7 @@ function useExporters(model, onExport, filename = "diagram") {
1544
1572
  }
1545
1573
  if (onExport) {
1546
1574
  onExport(format, content);
1575
+ onSuccess?.(`Exported as ${format.toUpperCase()}`);
1547
1576
  return;
1548
1577
  }
1549
1578
  const url = content instanceof Blob ? URL.createObjectURL(content) : URL.createObjectURL(new Blob([content], { type: "text/plain" }));
@@ -1552,7 +1581,8 @@ function useExporters(model, onExport, filename = "diagram") {
1552
1581
  a.download = `${filename}.${format === "plantuml" ? "puml" : format}`;
1553
1582
  a.click();
1554
1583
  URL.revokeObjectURL(url);
1555
- }, [model, onExport, filename]);
1584
+ onSuccess?.(`Downloaded ${a.download}`);
1585
+ }, [model, onExport, filename, onSuccess]);
1556
1586
  }
1557
1587
 
1558
1588
  // src/ui/hooks/useImporter.ts
@@ -1850,19 +1880,91 @@ function fromJSON(json) {
1850
1880
 
1851
1881
  // src/ui/hooks/useImporter.ts
1852
1882
  function useImporter(applyAndPush, options = {}) {
1853
- const { expectedType, transform } = options;
1883
+ const { expectedType, transform, onSuccess, onError } = options;
1884
+ const reportError = onError ?? ((msg) => alert(msg));
1854
1885
  return useCallback3((text) => {
1855
1886
  try {
1856
1887
  const parsed = text.trim().startsWith("{") ? fromJSON(text).toJSON() : fromMermaid(text).toJSON();
1857
1888
  if (expectedType && parsed.type !== expectedType) {
1858
- alert(`Imported diagram is not a ${expectedType} diagram.`);
1889
+ reportError(`Imported diagram is not a ${expectedType} diagram.`);
1859
1890
  return;
1860
1891
  }
1861
1892
  applyAndPush(transform ? transform(parsed) : parsed);
1893
+ onSuccess?.("Diagram imported successfully");
1862
1894
  } catch (err) {
1863
- alert(`Import failed: ${err.message}`);
1895
+ reportError(`Import failed: ${err.message}`);
1864
1896
  }
1865
- }, [applyAndPush, expectedType, transform]);
1897
+ }, [applyAndPush, expectedType, transform, onSuccess, onError]);
1898
+ }
1899
+
1900
+ // src/ui/hooks/useToast.ts
1901
+ import { useCallback as useCallback4, useState as useState5 } from "react";
1902
+ var _toastSeq = 0;
1903
+ function useToast() {
1904
+ const [toasts, setToasts] = useState5([]);
1905
+ const showToast = useCallback4((message, type = "info") => {
1906
+ const id = ++_toastSeq;
1907
+ setToasts((prev) => [...prev, { id, message, type }]);
1908
+ setTimeout(() => {
1909
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1910
+ }, 3e3);
1911
+ }, []);
1912
+ const dismissToast = useCallback4((id) => {
1913
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1914
+ }, []);
1915
+ return { toasts, showToast, dismissToast };
1916
+ }
1917
+
1918
+ // src/ui/ToastContainer.tsx
1919
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1920
+ var TOAST_COLORS = {
1921
+ success: { bg: "#065f46", border: "#10b981", text: "#ecfdf5" },
1922
+ error: { bg: "#7f1d1d", border: "#ef4444", text: "#fef2f2" },
1923
+ info: { bg: "#1e3a5f", border: "#3b82f6", text: "#eff6ff" }
1924
+ };
1925
+ var containerStyle = {
1926
+ position: "absolute",
1927
+ top: 8,
1928
+ right: 8,
1929
+ display: "flex",
1930
+ flexDirection: "column",
1931
+ gap: 6,
1932
+ zIndex: 9999,
1933
+ pointerEvents: "none"
1934
+ };
1935
+ function ToastContainer({ toasts, onDismiss }) {
1936
+ if (toasts.length === 0) return null;
1937
+ return /* @__PURE__ */ jsx5("div", { style: containerStyle, children: toasts.map((t) => {
1938
+ const c = TOAST_COLORS[t.type];
1939
+ return /* @__PURE__ */ jsxs5(
1940
+ "div",
1941
+ {
1942
+ role: "alert",
1943
+ "aria-live": "polite",
1944
+ style: {
1945
+ background: c.bg,
1946
+ border: `1px solid ${c.border}`,
1947
+ color: c.text,
1948
+ padding: "8px 14px",
1949
+ borderRadius: 8,
1950
+ fontSize: 12,
1951
+ fontWeight: 500,
1952
+ fontFamily: "ui-sans-serif,system-ui,sans-serif",
1953
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
1954
+ pointerEvents: "auto",
1955
+ cursor: "pointer",
1956
+ maxWidth: 280
1957
+ },
1958
+ onClick: () => onDismiss(t.id),
1959
+ children: [
1960
+ t.type === "success" && "\u2713 ",
1961
+ t.type === "error" && "\u2717 ",
1962
+ t.message
1963
+ ]
1964
+ },
1965
+ t.id
1966
+ );
1967
+ }) });
1866
1968
  }
1867
1969
 
1868
1970
  // src/ui/presets.ts
@@ -1985,7 +2087,7 @@ function useEditorKeyboard(commands, deps) {
1985
2087
  }
1986
2088
 
1987
2089
  // src/ui/SequenceEditor.tsx
1988
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2090
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1989
2091
  var INDIGO2 = "#4f46e5";
1990
2092
  var INDIGO_SOFT2 = "#eef2ff";
1991
2093
  var lightTheme2 = {
@@ -2052,11 +2154,12 @@ function SequenceEditor({
2052
2154
  theme = "auto",
2053
2155
  themeOverrides
2054
2156
  }) {
2055
- const [model, setModel] = useState5(() => ensureSequenceModel(initialModel));
2056
- const [selected, setSelected] = useState5(null);
2057
- const [drag, setDrag] = useState5(null);
2058
- const [editingId, setEditingId] = useState5(null);
2059
- const [editLabel, setEditLabel] = useState5("");
2157
+ const [model, setModel] = useState6(() => ensureSequenceModel(initialModel));
2158
+ const { toasts, showToast, dismissToast } = useToast();
2159
+ const [selected, setSelected] = useState6(null);
2160
+ const [drag, setDrag] = useState6(null);
2161
+ const [editingId, setEditingId] = useState6(null);
2162
+ const [editLabel, setEditLabel] = useState6("");
2060
2163
  const historyRef = useRef3([ensureSequenceModel(initialModel)]);
2061
2164
  const historyIdxRef = useRef3(0);
2062
2165
  const svgRef = useRef3(null);
@@ -2075,26 +2178,26 @@ function SequenceEditor({
2075
2178
  return SIDE_PAD2 + idx * colW + colW / 2;
2076
2179
  };
2077
2180
  const msgY = (idx) => HEADER_PAD2 + HEADER_H2 + 40 + idx * ROW_H2;
2078
- const pushHistory = useCallback4((m) => {
2181
+ const pushHistory = useCallback5((m) => {
2079
2182
  const stack = historyRef.current.slice(0, historyIdxRef.current + 1);
2080
2183
  stack.push(m);
2081
2184
  if (stack.length > 80) stack.shift();
2082
2185
  historyRef.current = stack;
2083
2186
  historyIdxRef.current = stack.length - 1;
2084
2187
  }, []);
2085
- const applyAndPush = useCallback4((m) => {
2188
+ const applyAndPush = useCallback5((m) => {
2086
2189
  setModel(m);
2087
2190
  onChange?.(m);
2088
2191
  pushHistory(m);
2089
2192
  }, [onChange, pushHistory]);
2090
- const undo = useCallback4(() => {
2193
+ const undo = useCallback5(() => {
2091
2194
  if (historyIdxRef.current <= 0) return;
2092
2195
  historyIdxRef.current--;
2093
2196
  const m = historyRef.current[historyIdxRef.current];
2094
2197
  setModel(m);
2095
2198
  onChange?.(m);
2096
2199
  }, [onChange]);
2097
- const redo = useCallback4(() => {
2200
+ const redo = useCallback5(() => {
2098
2201
  if (historyIdxRef.current >= historyRef.current.length - 1) return;
2099
2202
  historyIdxRef.current++;
2100
2203
  const m = historyRef.current[historyIdxRef.current];
@@ -2152,7 +2255,7 @@ function SequenceEditor({
2152
2255
  applyAndPush({ ...model, messages: messages.filter((m) => m.id !== id) });
2153
2256
  if (selected === id) setSelected(null);
2154
2257
  };
2155
- const reorderMessage = useCallback4((id, toIdx) => {
2258
+ const reorderMessage = useCallback5((id, toIdx) => {
2156
2259
  const fromIdx = messages.findIndex((m) => m.id === id);
2157
2260
  if (fromIdx < 0 || toIdx === fromIdx) return;
2158
2261
  const next = messages.slice();
@@ -2180,10 +2283,12 @@ function SequenceEditor({
2180
2283
  } }
2181
2284
  ];
2182
2285
  useEditorKeyboard(keyCommands, [undo, redo, selected]);
2183
- const handleExport = useExporters(model, onExport, "sequence");
2286
+ const handleExport = useExporters(model, onExport, "sequence", (msg) => showToast(msg, "success"));
2184
2287
  const handleImport = useImporter(applyAndPush, {
2185
2288
  expectedType: "sequence",
2186
- transform: ensureSequenceModel
2289
+ transform: ensureSequenceModel,
2290
+ onSuccess: (msg) => showToast(msg, "success"),
2291
+ onError: (msg) => showToast(msg, "error")
2187
2292
  });
2188
2293
  const onRowMouseDown = (e, id) => {
2189
2294
  const tag = e.target.tagName;
@@ -2223,16 +2328,30 @@ function SequenceEditor({
2223
2328
  };
2224
2329
  }, [drag, messages.length, reorderMessage]);
2225
2330
  const selectedMsg = selected ? messages.find((m) => m.id === selected) : null;
2226
- return /* @__PURE__ */ jsxs5("div", { style: {
2331
+ return /* @__PURE__ */ jsxs6("div", { className: "fsd-seq-editor", style: {
2227
2332
  display: "flex",
2228
2333
  flexDirection: "column",
2229
2334
  height,
2230
2335
  width: "100%",
2231
2336
  fontFamily: "ui-sans-serif,system-ui,sans-serif",
2232
- background: t.ctrlsBg
2337
+ background: t.ctrlsBg,
2338
+ position: "relative"
2233
2339
  }, children: [
2234
- /* @__PURE__ */ jsx5(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
2235
- /* @__PURE__ */ jsxs5("div", { style: {
2340
+ /* @__PURE__ */ jsx6(ToastContainer, { toasts, onDismiss: dismissToast }),
2341
+ /* @__PURE__ */ jsx6("style", { children: `
2342
+ .fsd-seq-editor [role="button"]:focus-visible {
2343
+ outline: 2px solid ${t.actorText};
2344
+ outline-offset: 2px;
2345
+ }
2346
+ .fsd-seq-editor button:focus-visible,
2347
+ .fsd-seq-editor input:focus-visible {
2348
+ outline: 2px solid ${t.actorText};
2349
+ outline-offset: 2px;
2350
+ border-radius: 4px;
2351
+ }
2352
+ ` }),
2353
+ /* @__PURE__ */ jsx6(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
2354
+ /* @__PURE__ */ jsxs6("div", { style: {
2236
2355
  display: "flex",
2237
2356
  gap: 8,
2238
2357
  padding: "7px 14px",
@@ -2241,12 +2360,12 @@ function SequenceEditor({
2241
2360
  alignItems: "center",
2242
2361
  flexWrap: "wrap"
2243
2362
  }, children: [
2244
- /* @__PURE__ */ jsx5("button", { onClick: addActor, style: primaryBtn(), children: "+ Actor" }),
2245
- /* @__PURE__ */ jsx5("button", { onClick: addMessage, style: primaryBtn(), children: "+ Message" }),
2246
- /* @__PURE__ */ jsx5("div", { style: { width: 1, height: 18, background: t.ctrlsBorder, margin: "0 4px" } }),
2247
- /* @__PURE__ */ jsx5("button", { onClick: undo, style: ghostBtn2(t), title: "Undo (Ctrl+Z)", children: "\u21B6" }),
2248
- /* @__PURE__ */ jsx5("button", { onClick: redo, style: ghostBtn2(t), title: "Redo (Ctrl+Y)", children: "\u21B7" }),
2249
- /* @__PURE__ */ jsxs5("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
2363
+ /* @__PURE__ */ jsx6("button", { onClick: addActor, style: primaryBtn(), children: "+ Actor" }),
2364
+ /* @__PURE__ */ jsx6("button", { onClick: addMessage, style: primaryBtn(), children: "+ Message" }),
2365
+ /* @__PURE__ */ jsx6("div", { style: { width: 1, height: 18, background: t.ctrlsBorder, margin: "0 4px" } }),
2366
+ /* @__PURE__ */ jsx6("button", { onClick: undo, style: ghostBtn2(t), title: "Undo (Ctrl+Z)", children: "\u21B6" }),
2367
+ /* @__PURE__ */ jsx6("button", { onClick: redo, style: ghostBtn2(t), title: "Redo (Ctrl+Y)", children: "\u21B7" }),
2368
+ /* @__PURE__ */ jsxs6("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
2250
2369
  actors.length,
2251
2370
  " actor",
2252
2371
  actors.length === 1 ? "" : "s",
@@ -2257,8 +2376,8 @@ function SequenceEditor({
2257
2376
  " \xB7 drag a row to reorder"
2258
2377
  ] })
2259
2378
  ] }),
2260
- /* @__PURE__ */ jsxs5("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
2261
- /* @__PURE__ */ jsx5("div", { style: { flex: 1, overflow: "auto", background: t.canvas, position: "relative" }, children: /* @__PURE__ */ jsx5(
2379
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
2380
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1, overflow: "auto", background: t.canvas, position: "relative" }, children: /* @__PURE__ */ jsx6(
2262
2381
  SequenceCanvas,
2263
2382
  {
2264
2383
  model,
@@ -2281,17 +2400,18 @@ function SequenceEditor({
2281
2400
  svgRef
2282
2401
  }
2283
2402
  ) }),
2284
- selectedMsg && /* @__PURE__ */ jsxs5("div", { style: {
2403
+ selectedMsg && /* @__PURE__ */ jsxs6("div", { style: {
2285
2404
  width: 280,
2405
+ maxWidth: "40vw",
2286
2406
  flexShrink: 0,
2287
2407
  background: t.panelBg,
2288
2408
  borderLeft: `1px solid ${t.panelBorder}`,
2289
2409
  padding: "14px 16px",
2290
2410
  overflowY: "auto"
2291
2411
  }, children: [
2292
- /* @__PURE__ */ jsx5("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.7, marginBottom: 10 }, children: "Message" }),
2293
- /* @__PURE__ */ jsx5(Label, { t, children: "Label" }),
2294
- /* @__PURE__ */ jsx5(
2412
+ /* @__PURE__ */ jsx6("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.7, marginBottom: 10 }, children: "Message" }),
2413
+ /* @__PURE__ */ jsx6(Label, { t, children: "Label" }),
2414
+ /* @__PURE__ */ jsx6(
2295
2415
  "input",
2296
2416
  {
2297
2417
  value: editLabel || selectedMsg.label,
@@ -2307,12 +2427,12 @@ function SequenceEditor({
2307
2427
  style: input(t)
2308
2428
  }
2309
2429
  ),
2310
- /* @__PURE__ */ jsx5(Label, { t, children: "From" }),
2311
- /* @__PURE__ */ jsx5("select", { value: selectedMsg.from, onChange: (e) => updateMessage(selectedMsg.id, { from: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ jsx5("option", { value: a, children: a }, a)) }),
2312
- /* @__PURE__ */ jsx5(Label, { t, children: "To" }),
2313
- /* @__PURE__ */ jsx5("select", { value: selectedMsg.to, onChange: (e) => updateMessage(selectedMsg.id, { to: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ jsx5("option", { value: a, children: a }, a)) }),
2314
- /* @__PURE__ */ jsx5(Label, { t, children: "Style" }),
2315
- /* @__PURE__ */ jsx5("div", { style: { display: "flex", gap: 6 }, children: ["solid", "dashed"].map((s2) => /* @__PURE__ */ jsx5(
2430
+ /* @__PURE__ */ jsx6(Label, { t, children: "From" }),
2431
+ /* @__PURE__ */ jsx6("select", { value: selectedMsg.from, onChange: (e) => updateMessage(selectedMsg.id, { from: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ jsx6("option", { value: a, children: a }, a)) }),
2432
+ /* @__PURE__ */ jsx6(Label, { t, children: "To" }),
2433
+ /* @__PURE__ */ jsx6("select", { value: selectedMsg.to, onChange: (e) => updateMessage(selectedMsg.id, { to: e.target.value }), style: input(t), children: actors.map((a) => /* @__PURE__ */ jsx6("option", { value: a, children: a }, a)) }),
2434
+ /* @__PURE__ */ jsx6(Label, { t, children: "Style" }),
2435
+ /* @__PURE__ */ jsx6("div", { style: { display: "flex", gap: 6 }, children: ["solid", "dashed"].map((s2) => /* @__PURE__ */ jsx6(
2316
2436
  "button",
2317
2437
  {
2318
2438
  onClick: () => updateMessage(selectedMsg.id, { style: s2 }),
@@ -2332,8 +2452,8 @@ function SequenceEditor({
2332
2452
  },
2333
2453
  s2
2334
2454
  )) }),
2335
- /* @__PURE__ */ jsx5("div", { style: { height: 14 } }),
2336
- /* @__PURE__ */ jsx5(
2455
+ /* @__PURE__ */ jsx6("div", { style: { height: 14 } }),
2456
+ /* @__PURE__ */ jsx6(
2337
2457
  "button",
2338
2458
  {
2339
2459
  onClick: () => removeMessage(selectedMsg.id),
@@ -2343,7 +2463,7 @@ function SequenceEditor({
2343
2463
  )
2344
2464
  ] })
2345
2465
  ] }),
2346
- /* @__PURE__ */ jsxs5("div", { style: {
2466
+ /* @__PURE__ */ jsxs6("div", { style: {
2347
2467
  padding: "4px 14px",
2348
2468
  fontSize: 11,
2349
2469
  color: t.textMuted,
@@ -2352,15 +2472,15 @@ function SequenceEditor({
2352
2472
  display: "flex",
2353
2473
  gap: 16
2354
2474
  }, children: [
2355
- /* @__PURE__ */ jsxs5("span", { children: [
2475
+ /* @__PURE__ */ jsxs6("span", { children: [
2356
2476
  actors.length,
2357
2477
  " actors"
2358
2478
  ] }),
2359
- /* @__PURE__ */ jsxs5("span", { children: [
2479
+ /* @__PURE__ */ jsxs6("span", { children: [
2360
2480
  messages.length,
2361
2481
  " messages"
2362
2482
  ] }),
2363
- /* @__PURE__ */ jsx5("span", { style: { marginLeft: "auto" }, children: "double-click actor to rename \xB7 drag a row to reorder" })
2483
+ /* @__PURE__ */ jsx6("span", { style: { marginLeft: "auto" }, children: "double-click actor to rename \xB7 drag a row to reorder" })
2364
2484
  ] })
2365
2485
  ] });
2366
2486
  }
@@ -2406,12 +2526,12 @@ function input(t) {
2406
2526
  };
2407
2527
  }
2408
2528
  function Label({ t, children }) {
2409
- return /* @__PURE__ */ jsx5("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.6, marginBottom: 4 }, children });
2529
+ return /* @__PURE__ */ jsx6("div", { style: { fontSize: 10, fontWeight: 700, color: t.textMuted, textTransform: "uppercase", letterSpacing: 0.6, marginBottom: 4 }, children });
2410
2530
  }
2411
2531
 
2412
2532
  // src/ui/NodeNavigator.tsx
2413
- import { useState as useState6 } from "react";
2414
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2533
+ import { useState as useState7 } from "react";
2534
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2415
2535
  function NodeNavigator({
2416
2536
  model,
2417
2537
  selected,
@@ -2423,7 +2543,7 @@ function NodeNavigator({
2423
2543
  onToggle,
2424
2544
  onSelect
2425
2545
  }) {
2426
- const [search, setSearch] = useState6("");
2546
+ const [search, setSearch] = useState7("");
2427
2547
  const shapeIcon = (node) => {
2428
2548
  if (variant === "question") return "?";
2429
2549
  if (variant === "journey") return "\u2197";
@@ -2444,7 +2564,7 @@ function NodeNavigator({
2444
2564
  const inEdges = (id) => model.edges.filter((e) => e.to === id).length;
2445
2565
  const outEdges = (id) => model.edges.filter((e) => e.from === id).length;
2446
2566
  if (!open) {
2447
- return /* @__PURE__ */ jsxs6("div", { style: {
2567
+ return /* @__PURE__ */ jsxs7("div", { style: {
2448
2568
  width: 36,
2449
2569
  flexShrink: 0,
2450
2570
  background: t.panelBg,
@@ -2455,19 +2575,21 @@ function NodeNavigator({
2455
2575
  paddingTop: 8,
2456
2576
  gap: 6
2457
2577
  }, children: [
2458
- /* @__PURE__ */ jsx6(
2578
+ /* @__PURE__ */ jsx7(
2459
2579
  "button",
2460
2580
  {
2461
2581
  onClick: onToggle,
2462
2582
  title: "Open node list",
2583
+ "aria-expanded": false,
2584
+ "aria-label": "Open node list",
2463
2585
  style: { background: "none", border: "none", cursor: "pointer", color: t.textMuted, padding: 6, borderRadius: 6, fontSize: 14, lineHeight: 1 },
2464
2586
  children: "\u2630"
2465
2587
  }
2466
2588
  ),
2467
- /* @__PURE__ */ jsx6("div", { style: { fontSize: 10, color: t.textMuted, fontWeight: 700, writingMode: "vertical-rl", transform: "rotate(180deg)", letterSpacing: 0.5 }, children: model.nodes.length })
2589
+ /* @__PURE__ */ jsx7("div", { style: { fontSize: 10, color: t.textMuted, fontWeight: 700, writingMode: "vertical-rl", transform: "rotate(180deg)", letterSpacing: 0.5 }, children: model.nodes.length })
2468
2590
  ] });
2469
2591
  }
2470
- return /* @__PURE__ */ jsxs6("div", { style: {
2592
+ return /* @__PURE__ */ jsxs7("div", { style: {
2471
2593
  width: 216,
2472
2594
  flexShrink: 0,
2473
2595
  background: t.panelBg,
@@ -2476,7 +2598,7 @@ function NodeNavigator({
2476
2598
  flexDirection: "column",
2477
2599
  overflow: "hidden"
2478
2600
  }, children: [
2479
- /* @__PURE__ */ jsxs6("div", { style: {
2601
+ /* @__PURE__ */ jsxs7("div", { style: {
2480
2602
  display: "flex",
2481
2603
  alignItems: "center",
2482
2604
  justifyContent: "space-between",
@@ -2484,9 +2606,9 @@ function NodeNavigator({
2484
2606
  borderBottom: `1px solid ${t.panelBorder}`,
2485
2607
  flexShrink: 0
2486
2608
  }, children: [
2487
- /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2488
- /* @__PURE__ */ jsx6("span", { style: { fontSize: 11, fontWeight: 700, color: t.textSecondary, textTransform: "uppercase", letterSpacing: 0.7 }, children: variant === "question" ? "Questions" : variant === "journey" ? "Steps" : "Nodes" }),
2489
- /* @__PURE__ */ jsx6("span", { style: {
2609
+ /* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2610
+ /* @__PURE__ */ jsx7("span", { style: { fontSize: 11, fontWeight: 700, color: t.textSecondary, textTransform: "uppercase", letterSpacing: 0.7 }, children: variant === "question" ? "Questions" : variant === "journey" ? "Steps" : "Nodes" }),
2611
+ /* @__PURE__ */ jsx7("span", { style: {
2490
2612
  fontSize: 10,
2491
2613
  fontWeight: 700,
2492
2614
  color: t.textMuted,
@@ -2495,19 +2617,21 @@ function NodeNavigator({
2495
2617
  borderRadius: 99
2496
2618
  }, children: model.nodes.length })
2497
2619
  ] }),
2498
- /* @__PURE__ */ jsx6(
2620
+ /* @__PURE__ */ jsx7(
2499
2621
  "button",
2500
2622
  {
2501
2623
  onClick: onToggle,
2502
2624
  style: { background: "none", border: "none", cursor: "pointer", color: t.textMuted, padding: "2px 4px", borderRadius: 4, fontSize: 13, lineHeight: 1 },
2503
2625
  title: "Collapse",
2626
+ "aria-expanded": true,
2627
+ "aria-label": "Collapse node list",
2504
2628
  children: "\u2039"
2505
2629
  }
2506
2630
  )
2507
2631
  ] }),
2508
- /* @__PURE__ */ jsx6("div", { style: { padding: "8px 10px", borderBottom: `1px solid ${t.sectionBorder}`, flexShrink: 0 }, children: /* @__PURE__ */ jsxs6("div", { style: { position: "relative" }, children: [
2509
- /* @__PURE__ */ jsx6("span", { style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", fontSize: 11, color: t.textMuted, pointerEvents: "none" }, children: "\u2315" }),
2510
- /* @__PURE__ */ jsx6(
2632
+ /* @__PURE__ */ jsx7("div", { style: { padding: "8px 10px", borderBottom: `1px solid ${t.sectionBorder}`, flexShrink: 0 }, children: /* @__PURE__ */ jsxs7("div", { style: { position: "relative" }, children: [
2633
+ /* @__PURE__ */ jsx7("span", { style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", fontSize: 11, color: t.textMuted, pointerEvents: "none" }, children: "\u2315" }),
2634
+ /* @__PURE__ */ jsx7(
2511
2635
  "input",
2512
2636
  {
2513
2637
  value: search,
@@ -2528,12 +2652,12 @@ function NodeNavigator({
2528
2652
  }
2529
2653
  )
2530
2654
  ] }) }),
2531
- /* @__PURE__ */ jsxs6("div", { style: { flex: 1, overflowY: "auto", padding: "6px 8px", display: "flex", flexDirection: "column", gap: 2 }, children: [
2532
- filtered.length === 0 && /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", padding: "20px 0", fontSize: 12, color: t.textMuted, fontStyle: "italic" }, children: model.nodes.length === 0 ? "No nodes yet" : "No matches" }),
2655
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1, overflowY: "auto", padding: "6px 8px", display: "flex", flexDirection: "column", gap: 2 }, children: [
2656
+ filtered.length === 0 && /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", padding: "20px 0", fontSize: 12, color: t.textMuted, fontStyle: "italic" }, children: model.nodes.length === 0 ? "No nodes yet" : "No matches" }),
2533
2657
  filtered.map((node, idx) => {
2534
2658
  const isSelected = selected === node.id;
2535
2659
  const answers = node.metadata?.answers ?? [];
2536
- return /* @__PURE__ */ jsxs6(
2660
+ return /* @__PURE__ */ jsxs7(
2537
2661
  "button",
2538
2662
  {
2539
2663
  onClick: () => onSelect(node.id),
@@ -2558,7 +2682,7 @@ function NodeNavigator({
2558
2682
  if (!isSelected) e.currentTarget.style.background = "transparent";
2559
2683
  },
2560
2684
  children: [
2561
- /* @__PURE__ */ jsx6("div", { style: {
2685
+ /* @__PURE__ */ jsx7("div", { style: {
2562
2686
  width: 22,
2563
2687
  height: 22,
2564
2688
  borderRadius: 6,
@@ -2571,8 +2695,8 @@ function NodeNavigator({
2571
2695
  fontSize: variant === "journey" ? 9 : 11,
2572
2696
  fontWeight: 700
2573
2697
  }, children: variant === "journey" ? idx + 1 : shapeIcon(node) }),
2574
- /* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
2575
- /* @__PURE__ */ jsx6("div", { style: {
2698
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1, minWidth: 0 }, children: [
2699
+ /* @__PURE__ */ jsx7("div", { style: {
2576
2700
  fontSize: 12,
2577
2701
  fontWeight: isSelected ? 600 : 400,
2578
2702
  color: isSelected ? acc.color : t.textPrimary,
@@ -2581,9 +2705,9 @@ function NodeNavigator({
2581
2705
  whiteSpace: "nowrap",
2582
2706
  lineHeight: 1.3
2583
2707
  }, children: node.label }),
2584
- /* @__PURE__ */ jsx6("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` })
2708
+ /* @__PURE__ */ jsx7("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` })
2585
2709
  ] }),
2586
- isSelected && /* @__PURE__ */ jsx6("span", { style: { fontSize: 10, color: acc.color, flexShrink: 0 }, children: "\u25C9" })
2710
+ isSelected && /* @__PURE__ */ jsx7("span", { style: { fontSize: 10, color: acc.color, flexShrink: 0 }, children: "\u25C9" })
2587
2711
  ]
2588
2712
  },
2589
2713
  node.id
@@ -2594,7 +2718,7 @@ function NodeNavigator({
2594
2718
  }
2595
2719
 
2596
2720
  // src/ui/render.tsx
2597
- import { useState as useState7 } from "react";
2721
+ import { useState as useState8 } from "react";
2598
2722
 
2599
2723
  // src/ui/layout.ts
2600
2724
  var NODE_H2 = 48;
@@ -2660,7 +2784,7 @@ function bezierPathVia(x1, y1, wx, wy, x2, y2) {
2660
2784
  }
2661
2785
 
2662
2786
  // src/ui/render.tsx
2663
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2787
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2664
2788
  var STYLE_LABEL = { pointerEvents: "none", userSelect: "none" };
2665
2789
  var STYLE_BLUR = { filter: "blur(4px)" };
2666
2790
  var STYLE_EDGE_HIT = { cursor: "pointer" };
@@ -2674,11 +2798,11 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2674
2798
  const stroke = selected ? acc.color : t.nodeStroke;
2675
2799
  const fill = selected ? t.nodeSelectedFill : t.nodeFill;
2676
2800
  const sw = selected ? 1.75 : 1.25;
2677
- const glow = selected && /* @__PURE__ */ jsx7(Fragment2, { children: node.shape === "circle" ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
2678
- /* @__PURE__ */ jsx7("circle", { cx, cy, r: NODE_H2 / 2 + 3, fill: "none", stroke: acc.color, strokeWidth: 6, opacity: 0.18, style: STYLE_BLUR }),
2679
- /* @__PURE__ */ jsx7("circle", { cx, cy, r: NODE_H2 / 2 + 1.5, fill: "none", stroke: acc.color, strokeWidth: 1, opacity: 0.55 })
2680
- ] }) : node.shape === "diamond" ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
2681
- /* @__PURE__ */ jsx7(
2801
+ const glow = selected && /* @__PURE__ */ jsx8(Fragment2, { children: node.shape === "circle" ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
2802
+ /* @__PURE__ */ jsx8("circle", { cx, cy, r: NODE_H2 / 2 + 3, fill: "none", stroke: acc.color, strokeWidth: 6, opacity: 0.18, style: STYLE_BLUR }),
2803
+ /* @__PURE__ */ jsx8("circle", { cx, cy, r: NODE_H2 / 2 + 1.5, fill: "none", stroke: acc.color, strokeWidth: 1, opacity: 0.55 })
2804
+ ] }) : node.shape === "diamond" ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
2805
+ /* @__PURE__ */ jsx8(
2682
2806
  "polygon",
2683
2807
  {
2684
2808
  points: `${cx},${-5} ${w + 5},${cy} ${cx},${NODE_H2 + 5} ${-5},${cy}`,
@@ -2689,7 +2813,7 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2689
2813
  style: STYLE_BLUR
2690
2814
  }
2691
2815
  ),
2692
- /* @__PURE__ */ jsx7(
2816
+ /* @__PURE__ */ jsx8(
2693
2817
  "polygon",
2694
2818
  {
2695
2819
  points: `${cx},${-2} ${w + 2},${cy} ${cx},${NODE_H2 + 2} ${-2},${cy}`,
@@ -2699,8 +2823,8 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2699
2823
  opacity: 0.55
2700
2824
  }
2701
2825
  )
2702
- ] }) : /* @__PURE__ */ jsxs7(Fragment2, { children: [
2703
- /* @__PURE__ */ jsx7(
2826
+ ] }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
2827
+ /* @__PURE__ */ jsx8(
2704
2828
  "rect",
2705
2829
  {
2706
2830
  x: -4,
@@ -2715,7 +2839,7 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2715
2839
  style: STYLE_BLUR
2716
2840
  }
2717
2841
  ),
2718
- /* @__PURE__ */ jsx7(
2842
+ /* @__PURE__ */ jsx8(
2719
2843
  "rect",
2720
2844
  {
2721
2845
  x: -1.5,
@@ -2731,35 +2855,35 @@ function NodeShape({ node, selected, variant, stepNumber, t, isDark, w }) {
2731
2855
  )
2732
2856
  ] }) });
2733
2857
  const badgeColor = isDark ? ACCENT.emeraldDark : ACCENT.emerald;
2734
- const badge = variant === "journey" && stepNumber !== void 0 && /* @__PURE__ */ jsxs7(Fragment2, { children: [
2735
- /* @__PURE__ */ jsx7("circle", { cx: 14, cy: 14, r: 10, fill: badgeColor }),
2736
- /* @__PURE__ */ jsx7("text", { x: 14, y: 18, textAnchor: "middle", fontSize: 9, fill: "white", fontWeight: "700", style: STYLE_LABEL, children: stepNumber })
2858
+ const badge = variant === "journey" && stepNumber !== void 0 && /* @__PURE__ */ jsxs8(Fragment2, { children: [
2859
+ /* @__PURE__ */ jsx8("circle", { cx: 14, cy: 14, r: 10, fill: badgeColor }),
2860
+ /* @__PURE__ */ jsx8("text", { x: 14, y: 18, textAnchor: "middle", fontSize: 9, fill: "white", fontWeight: "700", style: STYLE_LABEL, children: stepNumber })
2737
2861
  ] });
2738
2862
  switch (node.shape) {
2739
2863
  case "diamond": {
2740
2864
  const pts = `${cx},0 ${w},${cy} ${cx},${NODE_H2} 0,${cy}`;
2741
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
2865
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2742
2866
  glow,
2743
- /* @__PURE__ */ jsx7("polygon", { points: pts, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2867
+ /* @__PURE__ */ jsx8("polygon", { points: pts, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2744
2868
  badge
2745
2869
  ] });
2746
2870
  }
2747
2871
  case "circle":
2748
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
2872
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2749
2873
  glow,
2750
- /* @__PURE__ */ jsx7("circle", { cx, cy, r: NODE_H2 / 2 - 1, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2874
+ /* @__PURE__ */ jsx8("circle", { cx, cy, r: NODE_H2 / 2 - 1, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2751
2875
  badge
2752
2876
  ] });
2753
2877
  case "parallelogram":
2754
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
2878
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2755
2879
  glow,
2756
- /* @__PURE__ */ jsx7("polygon", { points: `14,0 ${w},0 ${w - 14},${NODE_H2} 0,${NODE_H2}`, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2880
+ /* @__PURE__ */ jsx8("polygon", { points: `14,0 ${w},0 ${w - 14},${NODE_H2} 0,${NODE_H2}`, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2757
2881
  badge
2758
2882
  ] });
2759
2883
  default:
2760
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
2884
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2761
2885
  glow,
2762
- /* @__PURE__ */ jsx7("rect", { width: w, height: NODE_H2, rx: 14, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2886
+ /* @__PURE__ */ jsx8("rect", { width: w, height: NODE_H2, rx: 14, fill, stroke, strokeWidth: sw, filter: "url(#nodeShadow)" }),
2763
2887
  badge
2764
2888
  ] });
2765
2889
  }
@@ -2780,8 +2904,8 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2780
2904
  const textSub = isDark ? "#64748b" : "#94a3b8";
2781
2905
  const textAns = isDark ? "#cbd5e1" : "#374151";
2782
2906
  const portRowY = Q_BASE_H2 + Q_ANS_ROW_H2 - 8;
2783
- const glow = selected && /* @__PURE__ */ jsxs7(Fragment2, { children: [
2784
- /* @__PURE__ */ jsx7(
2907
+ const glow = selected && /* @__PURE__ */ jsxs8(Fragment2, { children: [
2908
+ /* @__PURE__ */ jsx8(
2785
2909
  "rect",
2786
2910
  {
2787
2911
  x: -4,
@@ -2796,7 +2920,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2796
2920
  style: STYLE_BLUR
2797
2921
  }
2798
2922
  ),
2799
- /* @__PURE__ */ jsx7(
2923
+ /* @__PURE__ */ jsx8(
2800
2924
  "rect",
2801
2925
  {
2802
2926
  x: -1.5,
@@ -2811,29 +2935,29 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2811
2935
  }
2812
2936
  )
2813
2937
  ] });
2814
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
2938
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2815
2939
  glow,
2816
- /* @__PURE__ */ jsx7("rect", { width: qW, height: totalH, rx: 14, fill: nodeBg, stroke: nodeBorder, strokeWidth: selected ? 2 : 1.5, filter: "url(#nodeShadow)" }),
2817
- /* @__PURE__ */ jsx7("clipPath", { id: `qhdr-${node.id}`, children: /* @__PURE__ */ jsx7("rect", { width: qW, height: Q_BASE_H2, rx: 14 }) }),
2818
- /* @__PURE__ */ jsx7("rect", { width: qW, height: Q_BASE_H2, fill: amberSoft, clipPath: `url(#qhdr-${node.id})` }),
2819
- /* @__PURE__ */ jsx7("rect", { x: 0, y: 0, width: 4, height: Q_BASE_H2, rx: 2, fill: amber }),
2820
- /* @__PURE__ */ jsx7("rect", { x: 12, y: 14, width: 28, height: 28, rx: 8, fill: amber }),
2821
- /* @__PURE__ */ jsx7("text", { x: 26, y: 33, textAnchor: "middle", fontSize: 15, fontWeight: "900", fill: "white", style: STYLE_LABEL, children: "?" }),
2822
- /* @__PURE__ */ jsxs7(
2940
+ /* @__PURE__ */ jsx8("rect", { width: qW, height: totalH, rx: 14, fill: nodeBg, stroke: nodeBorder, strokeWidth: selected ? 2 : 1.5, filter: "url(#nodeShadow)" }),
2941
+ /* @__PURE__ */ jsx8("clipPath", { id: `qhdr-${node.id}`, children: /* @__PURE__ */ jsx8("rect", { width: qW, height: Q_BASE_H2, rx: 14 }) }),
2942
+ /* @__PURE__ */ jsx8("rect", { width: qW, height: Q_BASE_H2, fill: amberSoft, clipPath: `url(#qhdr-${node.id})` }),
2943
+ /* @__PURE__ */ jsx8("rect", { x: 0, y: 0, width: 4, height: Q_BASE_H2, rx: 2, fill: amber }),
2944
+ /* @__PURE__ */ jsx8("rect", { x: 12, y: 14, width: 28, height: 28, rx: 8, fill: amber }),
2945
+ /* @__PURE__ */ jsx8("text", { x: 26, y: 33, textAnchor: "middle", fontSize: 15, fontWeight: "900", fill: "white", style: STYLE_LABEL, children: "?" }),
2946
+ /* @__PURE__ */ jsxs8(
2823
2947
  "text",
2824
2948
  {
2825
2949
  style: STYLE_LABEL,
2826
2950
  fontFamily: "ui-sans-serif,system-ui,sans-serif",
2827
2951
  children: [
2828
- /* @__PURE__ */ jsx7("tspan", { x: 50, y: 27, fontSize: 9, fontWeight: 700, fill: textSub, letterSpacing: 0.6, textAnchor: "start", children: "QUESTION" }),
2829
- /* @__PURE__ */ jsx7("tspan", { x: 50, dy: 15, fontSize: 13, fontWeight: 700, fill: selected ? amber : textMain, textAnchor: "start", children: node.label })
2952
+ /* @__PURE__ */ jsx8("tspan", { x: 50, y: 27, fontSize: 9, fontWeight: 700, fill: textSub, letterSpacing: 0.6, textAnchor: "start", children: "QUESTION" }),
2953
+ /* @__PURE__ */ jsx8("tspan", { x: 50, dy: 15, fontSize: 13, fontWeight: 700, fill: selected ? amber : textMain, textAnchor: "start", children: node.label })
2830
2954
  ]
2831
2955
  }
2832
2956
  ),
2833
- /* @__PURE__ */ jsx7("line", { x1: 0, y1: Q_BASE_H2, x2: qW, y2: Q_BASE_H2, stroke: amberLine, strokeWidth: 1 }),
2834
- answers.length === 0 && /* @__PURE__ */ jsxs7(Fragment2, { children: [
2835
- /* @__PURE__ */ jsx7("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" }),
2836
- /* @__PURE__ */ jsx7("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" })
2957
+ /* @__PURE__ */ jsx8("line", { x1: 0, y1: Q_BASE_H2, x2: qW, y2: Q_BASE_H2, stroke: amberLine, strokeWidth: 1 }),
2958
+ answers.length === 0 && /* @__PURE__ */ jsxs8(Fragment2, { children: [
2959
+ /* @__PURE__ */ jsx8("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" }),
2960
+ /* @__PURE__ */ jsx8("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" })
2837
2961
  ] }),
2838
2962
  answers.map((ans, i) => {
2839
2963
  const prevW = answers.slice(0, i).reduce((s2, a) => s2 + answerCardW2(a) + Q_CARD_PAD2, 0);
@@ -2846,8 +2970,8 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2846
2970
  const letter = i < 26 ? ANSWER_LETTERS[i] : `${i + 1}`;
2847
2971
  const maxChars = Math.max(2, Math.floor((cW - 20) / 7.5));
2848
2972
  const displayAns = ans.length > maxChars ? ans.slice(0, maxChars - 1) + "\u2026" : ans;
2849
- return /* @__PURE__ */ jsxs7("g", { children: [
2850
- /* @__PURE__ */ jsx7(
2973
+ return /* @__PURE__ */ jsxs8("g", { children: [
2974
+ /* @__PURE__ */ jsx8(
2851
2975
  "rect",
2852
2976
  {
2853
2977
  x: cardX,
@@ -2860,7 +2984,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2860
2984
  strokeWidth: connected ? 1.5 : 1
2861
2985
  }
2862
2986
  ),
2863
- /* @__PURE__ */ jsx7(
2987
+ /* @__PURE__ */ jsx8(
2864
2988
  "rect",
2865
2989
  {
2866
2990
  x: cx - 11,
@@ -2871,7 +2995,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2871
2995
  fill: connected ? amber : isDark ? "#1e293b" : "#fef3c7"
2872
2996
  }
2873
2997
  ),
2874
- /* @__PURE__ */ jsx7(
2998
+ /* @__PURE__ */ jsx8(
2875
2999
  "text",
2876
3000
  {
2877
3001
  x: cx,
@@ -2884,7 +3008,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2884
3008
  children: letter
2885
3009
  }
2886
3010
  ),
2887
- /* @__PURE__ */ jsx7(
3011
+ /* @__PURE__ */ jsx8(
2888
3012
  "text",
2889
3013
  {
2890
3014
  x: cx,
@@ -2898,7 +3022,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2898
3022
  children: displayAns
2899
3023
  }
2900
3024
  ),
2901
- /* @__PURE__ */ jsx7(
3025
+ /* @__PURE__ */ jsx8(
2902
3026
  "circle",
2903
3027
  {
2904
3028
  cx,
@@ -2911,7 +3035,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2911
3035
  onMouseDown: (e) => onAnswerPortDown(e, node.id, ans, cx, portRowY)
2912
3036
  }
2913
3037
  ),
2914
- /* @__PURE__ */ jsx7(
3038
+ /* @__PURE__ */ jsx8(
2915
3039
  "path",
2916
3040
  {
2917
3041
  d: `M ${cx - 3} ${portRowY - 2} L ${cx} ${portRowY + 2} L ${cx + 3} ${portRowY - 2}`,
@@ -2928,7 +3052,7 @@ function QuestionNode({ node, selected, edges, isDark, onAnswerPortDown, qW }) {
2928
3052
  ] });
2929
3053
  }
2930
3054
  function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, onEditChange, onEditCommit, onEditCancel, onDoubleClick, onContextMenu, onWaypointDown }) {
2931
- const [hovered, setHovered] = useState7(false);
3055
+ const [hovered, setHovered] = useState8(false);
2932
3056
  const from = nodes.find((n) => n.id === edge.from);
2933
3057
  const to = nodes.find((n) => n.id === edge.to);
2934
3058
  if (!from || !to) return null;
@@ -2967,7 +3091,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
2967
3091
  const labelW = edge.label ? Math.max(60, Math.ceil(estimateTextW2(edge.label, 7) + 18)) : 60;
2968
3092
  const showHandle = !!onWaypointDown && (hovered || !!wp);
2969
3093
  const flowClass = dash ? void 0 : isAmber ? "edge-flow-amber" : "edge-flow";
2970
- return /* @__PURE__ */ jsxs7(
3094
+ return /* @__PURE__ */ jsxs8(
2971
3095
  "g",
2972
3096
  {
2973
3097
  onDoubleClick: (e) => {
@@ -2980,8 +3104,8 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
2980
3104
  onMouseEnter: () => setHovered(true),
2981
3105
  onMouseLeave: () => setHovered(false),
2982
3106
  children: [
2983
- /* @__PURE__ */ jsx7("path", { d, fill: "none", stroke: "transparent", strokeWidth: 14, style: STYLE_EDGE_HIT }),
2984
- /* @__PURE__ */ jsx7(
3107
+ /* @__PURE__ */ jsx8("path", { d, fill: "none", stroke: "transparent", strokeWidth: 14, style: STYLE_EDGE_HIT }),
3108
+ /* @__PURE__ */ jsx8(
2985
3109
  "path",
2986
3110
  {
2987
3111
  d,
@@ -2996,7 +3120,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
2996
3120
  style: STYLE_NO_EVENTS
2997
3121
  }
2998
3122
  ),
2999
- showHandle && /* @__PURE__ */ jsx7(
3123
+ showHandle && /* @__PURE__ */ jsx8(
3000
3124
  "circle",
3001
3125
  {
3002
3126
  cx: hx,
@@ -3012,7 +3136,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3012
3136
  }
3013
3137
  }
3014
3138
  ),
3015
- editing && !isAmber ? /* @__PURE__ */ jsx7("foreignObject", { x: mx - labelW / 2, y: my - 12, width: labelW, height: 22, children: /* @__PURE__ */ jsx7(
3139
+ editing && !isAmber ? /* @__PURE__ */ jsx8("foreignObject", { x: mx - labelW / 2, y: my - 12, width: labelW, height: 22, children: /* @__PURE__ */ jsx8(
3016
3140
  "input",
3017
3141
  {
3018
3142
  autoFocus: true,
@@ -3046,8 +3170,8 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3046
3170
  fontFamily: "inherit"
3047
3171
  }
3048
3172
  }
3049
- ) }) : edge.label && !isAmber ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
3050
- /* @__PURE__ */ jsx7(
3173
+ ) }) : edge.label && !isAmber ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
3174
+ /* @__PURE__ */ jsx8(
3051
3175
  "rect",
3052
3176
  {
3053
3177
  x: mx - labelW / 2,
@@ -3061,7 +3185,7 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3061
3185
  style: STYLE_EDGE_LABEL_HIT
3062
3186
  }
3063
3187
  ),
3064
- /* @__PURE__ */ jsx7(
3188
+ /* @__PURE__ */ jsx8(
3065
3189
  "text",
3066
3190
  {
3067
3191
  x: mx,
@@ -3082,8 +3206,8 @@ function EdgeLine({ edge, nodes, variant, t, isDark, acc, editing, editValue, on
3082
3206
  }
3083
3207
 
3084
3208
  // src/ui/Minimap.tsx
3085
- import { useCallback as useCallback5, useRef as useRef4 } from "react";
3086
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
3209
+ import { useCallback as useCallback6, useRef as useRef4 } from "react";
3210
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3087
3211
  var W = 168;
3088
3212
  var H = 112;
3089
3213
  var PAD = 18;
@@ -3131,7 +3255,7 @@ function Minimap({
3131
3255
  x: (mx - offsetX) / scale,
3132
3256
  y: (my - offsetY) / scale
3133
3257
  });
3134
- const panTo = useCallback5((e) => {
3258
+ const panTo = useCallback6((e) => {
3135
3259
  const rect = e.currentTarget.getBoundingClientRect();
3136
3260
  const mx = e.clientX - rect.left;
3137
3261
  const my = e.clientY - rect.top;
@@ -3163,9 +3287,11 @@ function Minimap({
3163
3287
  w: Math.max(2, Math.min(W, vp2.x) - Math.max(0, vp1.x)),
3164
3288
  h: Math.max(2, Math.min(H, vp2.y) - Math.max(0, vp1.y))
3165
3289
  };
3166
- return /* @__PURE__ */ jsx8(
3290
+ return /* @__PURE__ */ jsx9(
3167
3291
  "div",
3168
3292
  {
3293
+ "aria-label": "Minimap \u2014 click to re-center the viewport",
3294
+ role: "img",
3169
3295
  style: {
3170
3296
  position: "absolute",
3171
3297
  bottom: 14,
@@ -3177,7 +3303,7 @@ function Minimap({
3177
3303
  boxShadow: isDark ? "0 8px 20px rgba(0,0,0,0.45)" : "0 6px 18px rgba(15,23,42,0.08)",
3178
3304
  backdropFilter: "blur(6px)"
3179
3305
  },
3180
- children: /* @__PURE__ */ jsxs8(
3306
+ children: /* @__PURE__ */ jsxs9(
3181
3307
  "svg",
3182
3308
  {
3183
3309
  width: W,
@@ -3188,10 +3314,10 @@ function Minimap({
3188
3314
  onMouseUp,
3189
3315
  onMouseLeave: onMouseUp,
3190
3316
  children: [
3191
- /* @__PURE__ */ jsx8("rect", { width: W, height: H, rx: 6, fill: isDark ? "#0f172a" : "#fafbfc" }),
3317
+ /* @__PURE__ */ jsx9("rect", { width: W, height: H, rx: 6, fill: isDark ? "#0f172a" : "#fafbfc" }),
3192
3318
  boxes.map((b) => {
3193
3319
  const p = project(b.x, b.y);
3194
- return /* @__PURE__ */ jsx8(
3320
+ return /* @__PURE__ */ jsx9(
3195
3321
  "rect",
3196
3322
  {
3197
3323
  x: p.x,
@@ -3204,7 +3330,7 @@ function Minimap({
3204
3330
  b.id
3205
3331
  );
3206
3332
  }),
3207
- /* @__PURE__ */ jsx8(
3333
+ /* @__PURE__ */ jsx9(
3208
3334
  "rect",
3209
3335
  {
3210
3336
  x: vpRect.x,
@@ -3225,8 +3351,8 @@ function Minimap({
3225
3351
  }
3226
3352
 
3227
3353
  // src/ui/ContextMenu.tsx
3228
- import { useEffect as useEffect6, useRef as useRef5, useState as useState8 } from "react";
3229
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3354
+ import { useEffect as useEffect6, useRef as useRef5, useState as useState9 } from "react";
3355
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3230
3356
  function ContextMenu({
3231
3357
  x,
3232
3358
  y,
@@ -3256,7 +3382,7 @@ function ContextMenu({
3256
3382
  containerRef
3257
3383
  }) {
3258
3384
  const menuRef = useRef5(null);
3259
- const [pos, setPos] = useState8({ x, y });
3385
+ const [pos, setPos] = useState9({ x, y });
3260
3386
  useEffect6(() => {
3261
3387
  if (!menuRef.current || !containerRef.current) return;
3262
3388
  const m = menuRef.current.getBoundingClientRect();
@@ -3272,7 +3398,7 @@ function ContextMenu({
3272
3398
  const dividerColor = isDark ? "#334155" : "#f1f5f9";
3273
3399
  const text = t.textPrimary;
3274
3400
  const muted = t.textMuted;
3275
- const item = (label, onClick, color, disabled) => /* @__PURE__ */ jsx9(
3401
+ const item = (label, onClick, color, disabled) => /* @__PURE__ */ jsx10(
3276
3402
  "button",
3277
3403
  {
3278
3404
  onClick: disabled ? void 0 : onClick,
@@ -3302,8 +3428,8 @@ function ContextMenu({
3302
3428
  },
3303
3429
  label
3304
3430
  );
3305
- const divider2 = /* @__PURE__ */ jsx9("div", { style: { height: 1, background: dividerColor, margin: "4px 0" } });
3306
- return /* @__PURE__ */ jsx9(
3431
+ const divider2 = /* @__PURE__ */ jsx10("div", { style: { height: 1, background: dividerColor, margin: "4px 0" } });
3432
+ return /* @__PURE__ */ jsx10(
3307
3433
  "div",
3308
3434
  {
3309
3435
  ref: menuRef,
@@ -3321,30 +3447,30 @@ function ContextMenu({
3321
3447
  boxShadow: isDark ? "0 8px 32px rgba(0,0,0,0.5)" : "0 8px 32px rgba(0,0,0,0.12)",
3322
3448
  fontFamily: "ui-sans-serif,system-ui,sans-serif"
3323
3449
  },
3324
- children: edgeId ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
3325
- /* @__PURE__ */ jsx9("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Edge" }),
3450
+ children: edgeId ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
3451
+ /* @__PURE__ */ jsx10("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Edge" }),
3326
3452
  item("Rename label (dbl-click)", () => onEdgeRename?.()),
3327
3453
  divider2,
3328
- /* @__PURE__ */ jsx9("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Style" }),
3454
+ /* @__PURE__ */ jsx10("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Style" }),
3329
3455
  item(`Solid${currentEdgeStyle === "solid" || !currentEdgeStyle ? " \u2713" : ""}`, () => onEdgeStyle?.("solid")),
3330
3456
  item(`Dashed${currentEdgeStyle === "dashed" ? " \u2713" : ""}`, () => onEdgeStyle?.("dashed")),
3331
3457
  item(`Dotted${currentEdgeStyle === "dotted" ? " \u2713" : ""}`, () => onEdgeStyle?.("dotted")),
3332
3458
  divider2,
3333
- /* @__PURE__ */ jsx9("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Arrowhead" }),
3459
+ /* @__PURE__ */ jsx10("div", { style: { padding: "4px 14px 2px", fontSize: 9, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Arrowhead" }),
3334
3460
  item(`Arrow${currentEdgeArrow !== "none" ? " \u2713" : ""}`, () => onEdgeArrowhead?.("arrow")),
3335
3461
  item(`None${currentEdgeArrow === "none" ? " \u2713" : ""}`, () => onEdgeArrowhead?.("none")),
3336
3462
  divider2,
3337
3463
  item("Reset routing", () => onEdgeResetRouting?.(), void 0, !edgeHasWaypoint),
3338
3464
  item("Delete edge", () => onEdgeDelete?.(), "#ef4444")
3339
- ] }) : nodeId ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
3340
- /* @__PURE__ */ jsx9("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Node" }),
3465
+ ] }) : nodeId ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
3466
+ /* @__PURE__ */ jsx10("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Node" }),
3341
3467
  item("Rename (dbl-click)", onRename),
3342
3468
  item("Duplicate", onDuplicate),
3343
3469
  item("Disconnect all edges", onDisconnect),
3344
3470
  divider2,
3345
3471
  item("Delete node", onDelete, "#ef4444")
3346
- ] }) : /* @__PURE__ */ jsxs9(Fragment3, { children: [
3347
- /* @__PURE__ */ jsx9("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Canvas" }),
3472
+ ] }) : /* @__PURE__ */ jsxs10(Fragment3, { children: [
3473
+ /* @__PURE__ */ jsx10("div", { style: { padding: "4px 14px 6px", fontSize: 10, fontWeight: 700, color: muted, textTransform: "uppercase", letterSpacing: 0.8 }, children: "Canvas" }),
3348
3474
  item("Add node here", onAddNode, acc.color),
3349
3475
  item("Re-center (Ctrl+0)", onReCenter),
3350
3476
  divider2,
@@ -3356,7 +3482,7 @@ function ContextMenu({
3356
3482
  }
3357
3483
 
3358
3484
  // src/ui/DiagramCanvas.tsx
3359
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3485
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3360
3486
  var STYLE_LABEL2 = { pointerEvents: "none", userSelect: "none" };
3361
3487
  var STYLE_LIVE_PORT = { opacity: 0.85, pointerEvents: "none" };
3362
3488
  var STYLE_NODE_GRAB = { cursor: "grab" };
@@ -3433,8 +3559,8 @@ function DiagramCanvas(props) {
3433
3559
  onCtxEdgeDelete,
3434
3560
  onCtxEdgeResetRouting
3435
3561
  } = props;
3436
- return /* @__PURE__ */ jsxs10("div", { ref: containerRef, style: { flex: 1, overflow: "hidden", position: "relative", background: t.canvas }, children: [
3437
- /* @__PURE__ */ jsxs10(
3562
+ return /* @__PURE__ */ jsxs11("div", { ref: containerRef, style: { flex: 1, overflow: "hidden", position: "relative", background: t.canvas }, children: [
3563
+ /* @__PURE__ */ jsxs11(
3438
3564
  "svg",
3439
3565
  {
3440
3566
  ref: svgRef,
@@ -3450,8 +3576,8 @@ function DiagramCanvas(props) {
3450
3576
  onMouseLeave: onMouseUp,
3451
3577
  onContextMenu: onSvgContextMenu,
3452
3578
  children: [
3453
- /* @__PURE__ */ jsxs10("defs", { children: [
3454
- /* @__PURE__ */ jsx10("style", { children: reducedMotion ? `
3579
+ /* @__PURE__ */ jsxs11("defs", { children: [
3580
+ /* @__PURE__ */ jsx11("style", { children: reducedMotion ? `
3455
3581
  .edge-flow { stroke-dasharray: 0; }
3456
3582
  .edge-flow-amber { stroke-dasharray: 0; }
3457
3583
  .edge-live { stroke-dasharray: 4 4; }
@@ -3462,15 +3588,15 @@ function DiagramCanvas(props) {
3462
3588
  .edge-flow-amber { stroke-dasharray: 6 4; animation: edgeFlowFast 0.65s linear infinite; }
3463
3589
  .edge-live { stroke-dasharray: 7 5; animation: edgeFlow 0.55s linear infinite; }
3464
3590
  ` }),
3465
- /* @__PURE__ */ jsx10("pattern", { id: "dots", width: GRID, height: GRID, patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx10("circle", { cx: GRID / 2, cy: GRID / 2, r: 1.1, fill: t.dot }) }),
3466
- /* @__PURE__ */ jsx10("filter", { id: "nodeShadow", x: "-25%", y: "-25%", width: "150%", height: "160%", children: /* @__PURE__ */ jsx10("feDropShadow", { dx: "0", dy: "3", stdDeviation: "5", floodColor: shadowClr, floodOpacity: "1" }) }),
3467
- /* @__PURE__ */ jsx10("marker", { id: "arrowhead", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ jsx10("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: arrowClr }) }),
3468
- /* @__PURE__ */ jsx10("marker", { id: "arrowAmber", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ jsx10("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: amberArrow }) }),
3469
- /* @__PURE__ */ jsx10("marker", { id: "arrowLive", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ jsx10("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: acc.color }) })
3591
+ /* @__PURE__ */ jsx11("pattern", { id: "dots", width: GRID, height: GRID, patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx11("circle", { cx: GRID / 2, cy: GRID / 2, r: 1.1, fill: t.dot }) }),
3592
+ /* @__PURE__ */ jsx11("filter", { id: "nodeShadow", x: "-25%", y: "-25%", width: "150%", height: "160%", children: /* @__PURE__ */ jsx11("feDropShadow", { dx: "0", dy: "3", stdDeviation: "5", floodColor: shadowClr, floodOpacity: "1" }) }),
3593
+ /* @__PURE__ */ jsx11("marker", { id: "arrowhead", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ jsx11("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: arrowClr }) }),
3594
+ /* @__PURE__ */ jsx11("marker", { id: "arrowAmber", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ jsx11("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: amberArrow }) }),
3595
+ /* @__PURE__ */ jsx11("marker", { id: "arrowLive", markerWidth: "9", markerHeight: "7", refX: "8", refY: "3.5", orient: "auto", markerUnits: "strokeWidth", children: /* @__PURE__ */ jsx11("path", { d: "M0,0.5 L9,3.5 L0,6.5 L2.2,3.5 Z", fill: acc.color }) })
3470
3596
  ] }),
3471
- /* @__PURE__ */ jsx10("rect", { width: "100%", height: "100%", fill: "url(#dots)", "data-bg": "1" }),
3472
- /* @__PURE__ */ jsxs10("g", { transform: `translate(${transform.x},${transform.y}) scale(${transform.scale})`, children: [
3473
- model.edges.map((e) => /* @__PURE__ */ jsx10(
3597
+ /* @__PURE__ */ jsx11("rect", { width: "100%", height: "100%", fill: "url(#dots)", "data-bg": "1" }),
3598
+ /* @__PURE__ */ jsxs11("g", { transform: `translate(${transform.x},${transform.y}) scale(${transform.scale})`, children: [
3599
+ model.edges.map((e) => /* @__PURE__ */ jsx11(
3474
3600
  EdgeLine,
3475
3601
  {
3476
3602
  edge: e,
@@ -3492,9 +3618,9 @@ function DiagramCanvas(props) {
3492
3618
  )),
3493
3619
  liveEdge && (() => {
3494
3620
  const d = bezierPath2(liveEdge.fromX, liveEdge.fromY, liveEdge.toX, liveEdge.toY, liveEdge.exitDir);
3495
- return /* @__PURE__ */ jsx10("path", { d, fill: "none", stroke: acc.color, strokeWidth: 2, strokeLinecap: "round", className: "edge-live", opacity: 0.8, markerEnd: "url(#arrowLive)" });
3621
+ return /* @__PURE__ */ jsx11("path", { d, fill: "none", stroke: acc.color, strokeWidth: 2, strokeLinecap: "round", className: "edge-live", opacity: 0.8, markerEnd: "url(#arrowLive)" });
3496
3622
  })(),
3497
- alignGuides?.x && /* @__PURE__ */ jsx10(
3623
+ alignGuides?.x && /* @__PURE__ */ jsx11(
3498
3624
  "line",
3499
3625
  {
3500
3626
  x1: alignGuides.x.pos,
@@ -3508,7 +3634,7 @@ function DiagramCanvas(props) {
3508
3634
  pointerEvents: "none"
3509
3635
  }
3510
3636
  ),
3511
- alignGuides?.y && /* @__PURE__ */ jsx10(
3637
+ alignGuides?.y && /* @__PURE__ */ jsx11(
3512
3638
  "line",
3513
3639
  {
3514
3640
  y1: alignGuides.y.pos,
@@ -3527,11 +3653,12 @@ function DiagramCanvas(props) {
3527
3653
  const isQuestion2 = variant === "question";
3528
3654
  const { w: nW } = nodeDims(node, variant);
3529
3655
  const isSelected = selectedSet.has(node.id);
3530
- return /* @__PURE__ */ jsxs10(
3656
+ return /* @__PURE__ */ jsxs11(
3531
3657
  "g",
3532
3658
  {
3533
3659
  transform: `translate(${node.x ?? 0},${node.y ?? 0})`,
3534
3660
  role: "button",
3661
+ tabIndex: 0,
3535
3662
  "aria-label": `${variantLabel} ${variant === "journey" ? idx + 1 + ": " : ""}${node.label}${isSelected ? ", selected" : ""}`,
3536
3663
  style: drag?.nodeId === node.id ? STYLE_NODE_GRABBING : STYLE_NODE_GRAB,
3537
3664
  onMouseDown: (e) => onNodeMouseDown(e, node.id),
@@ -3540,11 +3667,20 @@ function DiagramCanvas(props) {
3540
3667
  onContextMenu: (e) => onNodeContextMenu(e, node.id),
3541
3668
  onMouseEnter: () => setHoveredId(node.id),
3542
3669
  onMouseLeave: () => setHoveredId(null),
3670
+ onFocus: () => setHoveredId(node.id),
3671
+ onBlur: () => setHoveredId(null),
3672
+ onKeyDown: (e) => {
3673
+ if (e.key === "F2" || e.key === "Enter" && !e.ctrlKey && !e.metaKey) {
3674
+ e.preventDefault();
3675
+ setEditingId(node.id);
3676
+ setEditLabel(node.label);
3677
+ }
3678
+ },
3543
3679
  children: [
3544
- /* @__PURE__ */ jsx10("title", { children: `${variantLabel}: ${node.label}` }),
3545
- isQuestion2 ? /* @__PURE__ */ jsx10(QuestionNode, { node, selected: isSelected, edges: model.edges, isDark, onAnswerPortDown, qW: nW }) : /* @__PURE__ */ jsxs10(Fragment4, { children: [
3546
- /* @__PURE__ */ jsx10(NodeShape, { node, selected: isSelected, variant, stepNumber: variant === "journey" ? idx + 1 : void 0, t, isDark, w: nW }),
3547
- editingId === node.id ? /* @__PURE__ */ jsx10("foreignObject", { x: 6, y: 6, width: nW - 12, height: NODE_H2 - 12, children: /* @__PURE__ */ jsx10(
3680
+ /* @__PURE__ */ jsx11("title", { children: `${variantLabel}: ${node.label}` }),
3681
+ isQuestion2 ? /* @__PURE__ */ jsx11(QuestionNode, { node, selected: isSelected, edges: model.edges, isDark, onAnswerPortDown, qW: nW }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
3682
+ /* @__PURE__ */ jsx11(NodeShape, { node, selected: isSelected, variant, stepNumber: variant === "journey" ? idx + 1 : void 0, t, isDark, w: nW }),
3683
+ editingId === node.id ? /* @__PURE__ */ jsx11("foreignObject", { x: 6, y: 6, width: nW - 12, height: NODE_H2 - 12, children: /* @__PURE__ */ jsx11(
3548
3684
  "input",
3549
3685
  {
3550
3686
  autoFocus: true,
@@ -3557,8 +3693,8 @@ function DiagramCanvas(props) {
3557
3693
  },
3558
3694
  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 }
3559
3695
  }
3560
- ) }) : /* @__PURE__ */ jsx10("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 }),
3561
- /* @__PURE__ */ jsx10(
3696
+ ) }) : /* @__PURE__ */ jsx11("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 }),
3697
+ /* @__PURE__ */ jsx11(
3562
3698
  "circle",
3563
3699
  {
3564
3700
  cx: nW / 2,
@@ -3572,7 +3708,7 @@ function DiagramCanvas(props) {
3572
3708
  }
3573
3709
  )
3574
3710
  ] }),
3575
- liveEdge && liveEdge.fromId !== node.id && /* @__PURE__ */ jsx10("circle", { cx: nW / 2, cy: -1, r: portR, fill: acc.color, stroke: isDark ? "#0f172a" : "white", strokeWidth: 2, style: STYLE_LIVE_PORT })
3711
+ liveEdge && liveEdge.fromId !== node.id && /* @__PURE__ */ jsx11("circle", { cx: nW / 2, cy: -1, r: portR, fill: acc.color, stroke: isDark ? "#0f172a" : "white", strokeWidth: 2, style: STYLE_LIVE_PORT })
3576
3712
  ]
3577
3713
  },
3578
3714
  node.id
@@ -3588,7 +3724,7 @@ function DiagramCanvas(props) {
3588
3724
  const top = Math.min(boxSel.sy, boxSel.cy) - rect.top;
3589
3725
  const w = Math.abs(boxSel.cx - boxSel.sx);
3590
3726
  const h = Math.abs(boxSel.cy - boxSel.sy);
3591
- return /* @__PURE__ */ jsx10(
3727
+ return /* @__PURE__ */ jsx11(
3592
3728
  "div",
3593
3729
  {
3594
3730
  style: {
@@ -3605,18 +3741,18 @@ function DiagramCanvas(props) {
3605
3741
  }
3606
3742
  );
3607
3743
  })(),
3608
- model.nodes.length === 0 && /* @__PURE__ */ jsxs10("div", { style: { position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", pointerEvents: "none", gap: 8 }, children: [
3609
- /* @__PURE__ */ jsx10("div", { style: { fontSize: 36, opacity: 0.1, color: t.textPrimary }, children: variant === "question" ? "?" : variant === "journey" ? "\u2197" : "\u2B21" }),
3610
- /* @__PURE__ */ jsxs10("div", { style: { fontSize: 13, color: t.textMuted, fontWeight: 500 }, children: [
3744
+ model.nodes.length === 0 && /* @__PURE__ */ jsxs11("div", { style: { position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", pointerEvents: "none", gap: 8 }, children: [
3745
+ /* @__PURE__ */ jsx11("div", { style: { fontSize: 36, opacity: 0.1, color: t.textPrimary }, children: variant === "question" ? "?" : variant === "journey" ? "\u2197" : "\u2B21" }),
3746
+ /* @__PURE__ */ jsxs11("div", { style: { fontSize: 13, color: t.textMuted, fontWeight: 500 }, children: [
3611
3747
  "Click ",
3612
- /* @__PURE__ */ jsxs10("strong", { style: { color: acc.color }, children: [
3748
+ /* @__PURE__ */ jsxs11("strong", { style: { color: acc.color }, children: [
3613
3749
  "+ ",
3614
3750
  variantLabel
3615
3751
  ] }),
3616
3752
  " to start"
3617
3753
  ] })
3618
3754
  ] }),
3619
- model.nodes.length > 0 && viewport.w > 0 && /* @__PURE__ */ jsx10(
3755
+ model.nodes.length > 0 && viewport.w > 0 && /* @__PURE__ */ jsx11(
3620
3756
  Minimap,
3621
3757
  {
3622
3758
  model,
@@ -3631,7 +3767,7 @@ function DiagramCanvas(props) {
3631
3767
  }
3632
3768
  }
3633
3769
  ),
3634
- ctxMenu && /* @__PURE__ */ jsx10(
3770
+ ctxMenu && /* @__PURE__ */ jsx11(
3635
3771
  ContextMenu,
3636
3772
  {
3637
3773
  x: ctxMenu.x,
@@ -3666,22 +3802,22 @@ function DiagramCanvas(props) {
3666
3802
  }
3667
3803
 
3668
3804
  // src/ui/hooks/useHistory.ts
3669
- import { useCallback as useCallback6, useRef as useRef6, useState as useState9 } from "react";
3805
+ import { useCallback as useCallback7, useRef as useRef6, useState as useState10 } from "react";
3670
3806
  var MAX_HISTORY = 80;
3671
3807
  function useHistory(initial, onChange) {
3672
- const [state, setState] = useState9(initial);
3808
+ const [state, setState] = useState10(initial);
3673
3809
  const stackRef = useRef6([initial]);
3674
3810
  const idxRef = useRef6(0);
3675
- const [, setTick] = useState9(0);
3811
+ const [, setTick] = useState10(0);
3676
3812
  const bump = () => setTick((n) => n + 1);
3677
- const apply = useCallback6(
3813
+ const apply = useCallback7(
3678
3814
  (next) => {
3679
3815
  setState(next);
3680
3816
  onChange?.(next);
3681
3817
  },
3682
3818
  [onChange]
3683
3819
  );
3684
- const applyAndPush = useCallback6(
3820
+ const applyAndPush = useCallback7(
3685
3821
  (next) => {
3686
3822
  const stack = stackRef.current.slice(0, idxRef.current + 1);
3687
3823
  stack.push(next);
@@ -3694,7 +3830,7 @@ function useHistory(initial, onChange) {
3694
3830
  },
3695
3831
  [onChange]
3696
3832
  );
3697
- const undo = useCallback6(() => {
3833
+ const undo = useCallback7(() => {
3698
3834
  if (idxRef.current <= 0) return;
3699
3835
  idxRef.current--;
3700
3836
  const next = stackRef.current[idxRef.current];
@@ -3702,7 +3838,7 @@ function useHistory(initial, onChange) {
3702
3838
  onChange?.(next);
3703
3839
  bump();
3704
3840
  }, [onChange]);
3705
- const redo = useCallback6(() => {
3841
+ const redo = useCallback7(() => {
3706
3842
  if (idxRef.current >= stackRef.current.length - 1) return;
3707
3843
  idxRef.current++;
3708
3844
  const next = stackRef.current[idxRef.current];
@@ -3863,9 +3999,9 @@ function useCanvasTouch(ref, {
3863
3999
  }
3864
4000
 
3865
4001
  // src/ui/hooks/useElementSize.ts
3866
- import { useEffect as useEffect9, useState as useState10 } from "react";
4002
+ import { useEffect as useEffect9, useState as useState11 } from "react";
3867
4003
  function useElementSize(ref) {
3868
- const [size, setSize] = useState10({ w: 0, h: 0 });
4004
+ const [size, setSize] = useState11({ w: 0, h: 0 });
3869
4005
  useEffect9(() => {
3870
4006
  const el = ref.current;
3871
4007
  if (!el || typeof ResizeObserver === "undefined") return;
@@ -3972,12 +4108,12 @@ function nearestInDirection(fromX, fromY, dir, candidates) {
3972
4108
  }
3973
4109
 
3974
4110
  // src/ui/DiagramEditor.tsx
3975
- import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
4111
+ import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
3976
4112
  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 };
3977
4113
  var STYLE_FLEX_ROW = { flex: 1, display: "flex", overflow: "hidden" };
3978
4114
  function DiagramEditor(props) {
3979
4115
  if (props.initialModel?.type === "sequence") {
3980
- return /* @__PURE__ */ jsx11(
4116
+ return /* @__PURE__ */ jsx12(
3981
4117
  SequenceEditor,
3982
4118
  {
3983
4119
  initialModel: props.initialModel,
@@ -3991,7 +4127,7 @@ function DiagramEditor(props) {
3991
4127
  }
3992
4128
  );
3993
4129
  }
3994
- return /* @__PURE__ */ jsx11(FlowchartEditor, { ...props });
4130
+ return /* @__PURE__ */ jsx12(FlowchartEditor, { ...props });
3995
4131
  }
3996
4132
  function FlowchartEditor({
3997
4133
  initialModel,
@@ -4005,25 +4141,26 @@ function FlowchartEditor({
4005
4141
  themeOverrides
4006
4142
  }) {
4007
4143
  const base = initialModel ? { ...initialModel, variant: initialModel.variant ?? variant } : presetFlowchartModel(variant);
4008
- const notify = useCallback7((m) => onChange?.(m), [onChange]);
4144
+ const notify = useCallback8((m) => onChange?.(m), [onChange]);
4009
4145
  const history = useHistory(base, notify);
4010
4146
  const { state: model, apply: applyModel, applyAndPush, undo, redo } = history;
4011
- const [transform, setTransform] = useState11({ x: 60, y: 60, scale: 1 });
4012
- const [selected, setSelected] = useState11(null);
4013
- const [selectedSet, setSelectedSet] = useState11(() => /* @__PURE__ */ new Set());
4014
- const [drag, setDrag] = useState11(null);
4015
- const [pan, setPan] = useState11(null);
4016
- const [boxSel, setBoxSel] = useState11(null);
4017
- const [liveEdge, setLiveEdge] = useState11(null);
4018
- const [alignGuides, setAlignGuides] = useState11(null);
4019
- const [waypointDrag, setWaypointDrag] = useState11(null);
4147
+ const { toasts, showToast, dismissToast } = useToast();
4148
+ const [transform, setTransform] = useState12({ x: 60, y: 60, scale: 1 });
4149
+ const [selected, setSelected] = useState12(null);
4150
+ const [selectedSet, setSelectedSet] = useState12(() => /* @__PURE__ */ new Set());
4151
+ const [drag, setDrag] = useState12(null);
4152
+ const [pan, setPan] = useState12(null);
4153
+ const [boxSel, setBoxSel] = useState12(null);
4154
+ const [liveEdge, setLiveEdge] = useState12(null);
4155
+ const [alignGuides, setAlignGuides] = useState12(null);
4156
+ const [waypointDrag, setWaypointDrag] = useState12(null);
4020
4157
  const groupDragOriginsRef = useRef7(null);
4021
4158
  const clipboardRef = useRef7(null);
4022
- const selectOne = useCallback7((id) => {
4159
+ const selectOne = useCallback8((id) => {
4023
4160
  setSelected(id);
4024
4161
  setSelectedSet(id ? /* @__PURE__ */ new Set([id]) : /* @__PURE__ */ new Set());
4025
4162
  }, []);
4026
- const toggleSelect = useCallback7((id) => {
4163
+ const toggleSelect = useCallback8((id) => {
4027
4164
  setSelectedSet((prev) => {
4028
4165
  const next = new Set(prev);
4029
4166
  if (next.has(id)) {
@@ -4037,18 +4174,18 @@ function FlowchartEditor({
4037
4174
  return next;
4038
4175
  });
4039
4176
  }, []);
4040
- const clearSelection = useCallback7(() => {
4177
+ const clearSelection = useCallback8(() => {
4041
4178
  setSelected(null);
4042
4179
  setSelectedSet(/* @__PURE__ */ new Set());
4043
4180
  }, []);
4044
- const [editingId, setEditingId] = useState11(null);
4045
- const [editLabel, setEditLabel] = useState11("");
4046
- const [editingEdgeId, setEditingEdgeId] = useState11(null);
4047
- const [editEdgeLabel, setEditEdgeLabel] = useState11("");
4048
- const [hoveredId, setHoveredId] = useState11(null);
4049
- const [ctxMenu, setCtxMenu] = useState11(null);
4050
- const [navOpen, setNavOpen] = useState11(true);
4051
- const [announcement, setAnnouncement] = useState11("");
4181
+ const [editingId, setEditingId] = useState12(null);
4182
+ const [editLabel, setEditLabel] = useState12("");
4183
+ const [editingEdgeId, setEditingEdgeId] = useState12(null);
4184
+ const [editEdgeLabel, setEditEdgeLabel] = useState12("");
4185
+ const [hoveredId, setHoveredId] = useState12(null);
4186
+ const [ctxMenu, setCtxMenu] = useState12(null);
4187
+ const [navOpen, setNavOpen] = useState12(true);
4188
+ const [announcement, setAnnouncement] = useState12("");
4052
4189
  const svgRef = useRef7(null);
4053
4190
  const containerRef = useRef7(null);
4054
4191
  const reducedMotion = usePrefersReducedMotion();
@@ -4056,7 +4193,7 @@ function FlowchartEditor({
4056
4193
  const isCoarse = useIsCoarsePointer();
4057
4194
  const portR = isCoarse ? 9 : 6;
4058
4195
  const viewport = useElementSize(svgRef);
4059
- const reCenter = useCallback7(() => {
4196
+ const reCenter = useCallback8(() => {
4060
4197
  if (!svgRef.current) return;
4061
4198
  const rect = svgRef.current.getBoundingClientRect();
4062
4199
  const W2 = rect.width, H2 = rect.height;
@@ -4080,7 +4217,7 @@ function FlowchartEditor({
4080
4217
  const cx = (minX + maxX) / 2, cy = (minY + maxY) / 2;
4081
4218
  setTransform({ scale, x: W2 / 2 - cx * scale, y: H2 / 2 - cy * scale });
4082
4219
  }, [model.nodes, variant]);
4083
- const jumpToNode = useCallback7((nodeId) => {
4220
+ const jumpToNode = useCallback8((nodeId) => {
4084
4221
  const node = model.nodes.find((n) => n.id === nodeId);
4085
4222
  if (!node || !svgRef.current) return;
4086
4223
  const rect = svgRef.current.getBoundingClientRect();
@@ -4091,7 +4228,7 @@ function FlowchartEditor({
4091
4228
  setTransform({ scale, x: rect.width / 2 - cx * scale, y: rect.height / 2 - cy * scale });
4092
4229
  selectOne(nodeId);
4093
4230
  }, [model.nodes, variant, transform.scale, selectOne]);
4094
- const duplicateIds = useCallback7((ids) => {
4231
+ const duplicateIds = useCallback8((ids) => {
4095
4232
  if (ids.length === 0) return;
4096
4233
  const idSet = new Set(ids);
4097
4234
  const idMap = /* @__PURE__ */ new Map();
@@ -4123,7 +4260,7 @@ function FlowchartEditor({
4123
4260
  setSelected(newIds[newIds.length - 1] ?? null);
4124
4261
  setSelectedSet(new Set(newIds));
4125
4262
  }, [model, applyAndPush]);
4126
- const duplicateNode = useCallback7((nodeId) => {
4263
+ const duplicateNode = useCallback8((nodeId) => {
4127
4264
  duplicateIds([nodeId]);
4128
4265
  }, [duplicateIds]);
4129
4266
  useEffect10(() => {
@@ -4258,12 +4395,12 @@ function FlowchartEditor({
4258
4395
  }
4259
4396
  ];
4260
4397
  useEditorKeyboard(keyCommands, [undo, redo, reCenter, selected, selectedSet, ctxMenu, liveEdge, editingId, boxSel, model, applyAndPush, duplicateNode, clearSelection]);
4261
- const toCanvas = useCallback7((clientX, clientY) => {
4398
+ const toCanvas = useCallback8((clientX, clientY) => {
4262
4399
  const rect = svgRef.current.getBoundingClientRect();
4263
4400
  return { x: (clientX - rect.left - transform.x) / transform.scale, y: (clientY - rect.top - transform.y) / transform.scale };
4264
4401
  }, [transform]);
4265
4402
  useCanvasWheel(svgRef, setTransform);
4266
- const onCanvasLongPress = useCallback7((x, y) => {
4403
+ const onCanvasLongPress = useCallback8((x, y) => {
4267
4404
  setCtxMenu({ x, y, nodeId: null });
4268
4405
  }, []);
4269
4406
  useCanvasTouch(svgRef, { transform, setTransform, onLongPress: onCanvasLongPress });
@@ -4534,8 +4671,8 @@ function FlowchartEditor({
4534
4671
  };
4535
4672
  applyAndPush(updated);
4536
4673
  };
4537
- const handleExport = useExporters(model, onExport, "diagram");
4538
- const positionFlowchartNodes = useCallback7((m) => ({
4674
+ const handleExport = useExporters(model, onExport, "diagram", (msg) => showToast(msg, "success"));
4675
+ const positionFlowchartNodes = useCallback8((m) => ({
4539
4676
  ...m,
4540
4677
  nodes: m.nodes.map((n, i) => ({
4541
4678
  ...n,
@@ -4543,14 +4680,19 @@ function FlowchartEditor({
4543
4680
  y: n.y ?? snap(80 + Math.floor(i / 4) * 140)
4544
4681
  }))
4545
4682
  }), []);
4546
- const handleImport = useImporter(applyAndPush, { transform: positionFlowchartNodes });
4683
+ const handleImport = useImporter(applyAndPush, {
4684
+ transform: positionFlowchartNodes,
4685
+ onSuccess: (msg) => showToast(msg, "success"),
4686
+ onError: (msg) => showToast(msg, "error")
4687
+ });
4547
4688
  const acc = variantAccent(variant, isDark);
4548
4689
  const variantLabel = variant === "question" ? "Question" : variant === "journey" ? "Step" : "Node";
4549
4690
  const shadowClr = shadowColor(isDark);
4550
4691
  const arrowClr = arrowColor(isDark);
4551
4692
  const amberArrow = isDark ? ACCENT.amberDark : ACCENT.amber;
4552
- return /* @__PURE__ */ jsxs11("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: [
4553
- /* @__PURE__ */ jsx11("style", { children: `
4693
+ return /* @__PURE__ */ jsxs12("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: [
4694
+ /* @__PURE__ */ jsx12(ToastContainer, { toasts, onDismiss: dismissToast }),
4695
+ /* @__PURE__ */ jsx12("style", { children: `
4554
4696
  .fsd-editor button:focus-visible,
4555
4697
  .fsd-editor input:focus-visible,
4556
4698
  .fsd-editor textarea:focus-visible,
@@ -4560,12 +4702,16 @@ function FlowchartEditor({
4560
4702
  outline-offset: 2px;
4561
4703
  border-radius: 6px;
4562
4704
  }
4705
+ .fsd-editor svg [role="button"]:focus-visible {
4706
+ outline: 2px solid ${acc.color};
4707
+ outline-offset: 3px;
4708
+ }
4563
4709
  .fsd-editor svg[role="application"]:focus-visible {
4564
4710
  outline: 2px solid ${acc.color};
4565
4711
  outline-offset: -2px;
4566
4712
  }
4567
4713
  ` }),
4568
- /* @__PURE__ */ jsx11(
4714
+ /* @__PURE__ */ jsx12(
4569
4715
  "div",
4570
4716
  {
4571
4717
  role: "status",
@@ -4575,28 +4721,28 @@ function FlowchartEditor({
4575
4721
  children: announcement
4576
4722
  }
4577
4723
  ),
4578
- /* @__PURE__ */ jsx11(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
4579
- /* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 6, padding: "7px 14px", background: t.ctrlsBg, borderBottom: `1px solid ${t.ctrlsBorder}`, alignItems: "center", flexWrap: "wrap" }, children: [
4580
- /* @__PURE__ */ jsxs11("button", { onClick: () => addNode(), style: ctrlBtn(acc.color, isDark), children: [
4724
+ /* @__PURE__ */ jsx12(Toolbar, { onExport: handleExport, onImport: allowImport ? handleImport : void 0, allowedExports, allowImport }),
4725
+ /* @__PURE__ */ jsxs12("div", { style: { display: "flex", gap: 6, padding: "7px 14px", background: t.ctrlsBg, borderBottom: `1px solid ${t.ctrlsBorder}`, alignItems: "center", flexWrap: "wrap" }, children: [
4726
+ /* @__PURE__ */ jsxs12("button", { onClick: () => addNode(), style: ctrlBtn(acc.color, isDark), children: [
4581
4727
  "+ ",
4582
4728
  variantLabel
4583
4729
  ] }),
4584
- selectedSet.size > 0 && /* @__PURE__ */ jsxs11(Fragment5, { children: [
4585
- /* @__PURE__ */ jsx11("div", { style: { width: 1, height: 20, background: t.ctrlsBorder, margin: "0 2px" } }),
4586
- /* @__PURE__ */ jsx11("button", { onClick: deleteSelected, style: { ...ctrlBtn("transparent", isDark), color: "#ef4444", border: `1px solid ${isDark ? "#7f1d1d" : "#fca5a5"}` }, children: selectedSet.size > 1 ? `Delete (${selectedSet.size})` : "Delete" })
4730
+ selectedSet.size > 0 && /* @__PURE__ */ jsxs12(Fragment5, { children: [
4731
+ /* @__PURE__ */ jsx12("div", { style: { width: 1, height: 20, background: t.ctrlsBorder, margin: "0 2px" } }),
4732
+ /* @__PURE__ */ jsx12("button", { onClick: deleteSelected, style: { ...ctrlBtn("transparent", isDark), color: "#ef4444", border: `1px solid ${isDark ? "#7f1d1d" : "#fca5a5"}` }, children: selectedSet.size > 1 ? `Delete (${selectedSet.size})` : "Delete" })
4587
4733
  ] }),
4588
- liveEdge && /* @__PURE__ */ jsxs11("span", { style: { fontSize: 11, color: acc.color, fontWeight: 600, marginLeft: 6 }, children: [
4734
+ liveEdge && /* @__PURE__ */ jsxs12("span", { style: { fontSize: 11, color: acc.color, fontWeight: 600, marginLeft: 6 }, children: [
4589
4735
  liveEdge.answerLabel ? `Routing "${liveEdge.answerLabel}" \u2192` : "Drop on a node to connect",
4590
- /* @__PURE__ */ jsx11("span", { style: { fontWeight: 400, color: t.textMuted, marginLeft: 6 }, children: "release to cancel" })
4736
+ /* @__PURE__ */ jsx12("span", { style: { fontWeight: 400, color: t.textMuted, marginLeft: 6 }, children: "release to cancel" })
4591
4737
  ] }),
4592
- /* @__PURE__ */ jsxs11("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
4738
+ /* @__PURE__ */ jsxs12("span", { style: { marginLeft: "auto", fontSize: 11, color: t.textMuted }, children: [
4593
4739
  variant === "question" ? "drag answer port to connect \xB7 " : "drag port dot \xB7 ",
4594
4740
  "scroll to zoom \xB7 drag to pan"
4595
4741
  ] })
4596
4742
  ] }),
4597
- variant !== "flowchart" && /* @__PURE__ */ jsx11("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" }),
4598
- /* @__PURE__ */ jsxs11("div", { style: STYLE_FLEX_ROW, children: [
4599
- /* @__PURE__ */ jsx11(
4743
+ variant !== "flowchart" && /* @__PURE__ */ jsx12("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" }),
4744
+ /* @__PURE__ */ jsxs12("div", { style: STYLE_FLEX_ROW, children: [
4745
+ /* @__PURE__ */ jsx12(
4600
4746
  NodeNavigator,
4601
4747
  {
4602
4748
  model,
@@ -4610,7 +4756,7 @@ function FlowchartEditor({
4610
4756
  onSelect: jumpToNode
4611
4757
  }
4612
4758
  ),
4613
- /* @__PURE__ */ jsx11(
4759
+ /* @__PURE__ */ jsx12(
4614
4760
  DiagramCanvas,
4615
4761
  {
4616
4762
  model,
@@ -4745,27 +4891,27 @@ function FlowchartEditor({
4745
4891
  }
4746
4892
  }
4747
4893
  ),
4748
- selected && /* @__PURE__ */ jsx11(StepEditor, { nodeId: selected, model, onModelChange: (m) => {
4894
+ selected && /* @__PURE__ */ jsx12(StepEditor, { nodeId: selected, model, onModelChange: (m) => {
4749
4895
  applyAndPush(m);
4750
4896
  }, variant, isDark, t, acc }, selected)
4751
4897
  ] }),
4752
- /* @__PURE__ */ jsxs11("div", { style: { padding: "4px 14px", fontSize: 11, color: t.textMuted, background: t.statusBg, borderTop: `1px solid ${t.ctrlsBorder}`, display: "flex", gap: 16 }, children: [
4753
- /* @__PURE__ */ jsxs11("span", { children: [
4898
+ /* @__PURE__ */ jsxs12("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: [
4899
+ /* @__PURE__ */ jsxs12("span", { children: [
4754
4900
  model.nodes.length,
4755
4901
  " ",
4756
4902
  variantLabel.toLowerCase(),
4757
4903
  "s"
4758
4904
  ] }),
4759
- /* @__PURE__ */ jsxs11("span", { children: [
4905
+ /* @__PURE__ */ jsxs12("span", { children: [
4760
4906
  model.edges.length,
4761
4907
  " connections"
4762
4908
  ] }),
4763
- /* @__PURE__ */ jsxs11("span", { children: [
4909
+ /* @__PURE__ */ jsxs12("span", { children: [
4764
4910
  Math.round(transform.scale * 100),
4765
4911
  "% zoom"
4766
4912
  ] }),
4767
- /* @__PURE__ */ jsx11("span", { style: { marginLeft: "auto" }, children: "Ctrl+Z undo \xB7 Ctrl+Y redo \xB7 Ctrl+0 fit \xB7 Alt+Arrow traverse" }),
4768
- selected && /* @__PURE__ */ jsx11("span", { style: { color: acc.color }, children: model.nodes.find((n) => n.id === selected)?.label })
4913
+ /* @__PURE__ */ jsx12("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" }),
4914
+ selected && /* @__PURE__ */ jsx12("span", { style: { color: acc.color }, children: model.nodes.find((n) => n.id === selected)?.label })
4769
4915
  ] })
4770
4916
  ] });
4771
4917
  }