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/CHANGELOG.md +14 -0
- package/README.md +68 -8
- package/dist/ui/index.cjs +420 -274
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +398 -252
- package/dist/ui/index.js.map +1 -1
- package/package.json +4 -1
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
|
|
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", {
|
|
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", {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
2088
|
-
const
|
|
2089
|
-
const [
|
|
2090
|
-
const [
|
|
2091
|
-
const [
|
|
2092
|
-
const
|
|
2093
|
-
const
|
|
2094
|
-
const
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
2267
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2277
|
-
/* @__PURE__ */ (0,
|
|
2278
|
-
/* @__PURE__ */ (0,
|
|
2279
|
-
/* @__PURE__ */ (0,
|
|
2280
|
-
/* @__PURE__ */ (0,
|
|
2281
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2293
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2325
|
-
/* @__PURE__ */ (0,
|
|
2326
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2343
|
-
/* @__PURE__ */ (0,
|
|
2344
|
-
/* @__PURE__ */ (0,
|
|
2345
|
-
/* @__PURE__ */ (0,
|
|
2346
|
-
/* @__PURE__ */ (0,
|
|
2347
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2368
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2507
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
|
|
2388
2508
|
actors.length,
|
|
2389
2509
|
" actors"
|
|
2390
2510
|
] }),
|
|
2391
|
-
/* @__PURE__ */ (0,
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
|
|
2392
2512
|
messages.length,
|
|
2393
2513
|
" messages"
|
|
2394
2514
|
] }),
|
|
2395
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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
|
|
2446
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
2520
|
-
/* @__PURE__ */ (0,
|
|
2521
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2541
|
-
/* @__PURE__ */ (0,
|
|
2542
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2564
|
-
filtered.length === 0 && /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
2607
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
2710
|
-
/* @__PURE__ */ (0,
|
|
2711
|
-
/* @__PURE__ */ (0,
|
|
2712
|
-
] }) : node.shape === "diamond" ? /* @__PURE__ */ (0,
|
|
2713
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2735
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2767
|
-
/* @__PURE__ */ (0,
|
|
2768
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2897
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2774
2898
|
glow,
|
|
2775
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2904
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2781
2905
|
glow,
|
|
2782
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2910
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2787
2911
|
glow,
|
|
2788
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2916
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2793
2917
|
glow,
|
|
2794
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2816
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2970
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2847
2971
|
glow,
|
|
2848
|
-
/* @__PURE__ */ (0,
|
|
2849
|
-
/* @__PURE__ */ (0,
|
|
2850
|
-
/* @__PURE__ */ (0,
|
|
2851
|
-
/* @__PURE__ */ (0,
|
|
2852
|
-
/* @__PURE__ */ (0,
|
|
2853
|
-
/* @__PURE__ */ (0,
|
|
2854
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2861
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2866
|
-
answers.length === 0 && /* @__PURE__ */ (0,
|
|
2867
|
-
/* @__PURE__ */ (0,
|
|
2868
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
2882
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
3016
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
3082
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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
|
|
3118
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
3261
|
-
var
|
|
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,
|
|
3291
|
-
const [pos, setPos] = (0,
|
|
3292
|
-
(0,
|
|
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,
|
|
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,
|
|
3338
|
-
return /* @__PURE__ */ (0,
|
|
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,
|
|
3357
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
3372
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
3379
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
3469
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
3486
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
3498
|
-
/* @__PURE__ */ (0,
|
|
3499
|
-
/* @__PURE__ */ (0,
|
|
3500
|
-
/* @__PURE__ */ (0,
|
|
3501
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
3504
|
-
/* @__PURE__ */ (0,
|
|
3505
|
-
model.edges.map((e) => /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
3577
|
-
isQuestion2 ? /* @__PURE__ */ (0,
|
|
3578
|
-
/* @__PURE__ */ (0,
|
|
3579
|
-
editingId === node.id ? /* @__PURE__ */ (0,
|
|
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,
|
|
3593
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
3641
|
-
/* @__PURE__ */ (0,
|
|
3642
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
3837
|
+
var import_react16 = require("react");
|
|
3702
3838
|
var MAX_HISTORY = 80;
|
|
3703
3839
|
function useHistory(initial, onChange) {
|
|
3704
|
-
const [state, setState] = (0,
|
|
3705
|
-
const stackRef = (0,
|
|
3706
|
-
const idxRef = (0,
|
|
3707
|
-
const [, setTick] = (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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
4034
|
+
var import_react19 = require("react");
|
|
3899
4035
|
function useElementSize(ref) {
|
|
3900
|
-
const [size, setSize] = (0,
|
|
3901
|
-
(0,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
4044
|
-
const [
|
|
4045
|
-
const [
|
|
4046
|
-
const [
|
|
4047
|
-
const [
|
|
4048
|
-
const [
|
|
4049
|
-
const [
|
|
4050
|
-
const [
|
|
4051
|
-
const [
|
|
4052
|
-
const
|
|
4053
|
-
const
|
|
4054
|
-
const
|
|
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,
|
|
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,
|
|
4209
|
+
const clearSelection = (0, import_react20.useCallback)(() => {
|
|
4073
4210
|
setSelected(null);
|
|
4074
4211
|
setSelectedSet(/* @__PURE__ */ new Set());
|
|
4075
4212
|
}, []);
|
|
4076
|
-
const [editingId, setEditingId] = (0,
|
|
4077
|
-
const [editLabel, setEditLabel] = (0,
|
|
4078
|
-
const [editingEdgeId, setEditingEdgeId] = (0,
|
|
4079
|
-
const [editEdgeLabel, setEditEdgeLabel] = (0,
|
|
4080
|
-
const [hoveredId, setHoveredId] = (0,
|
|
4081
|
-
const [ctxMenu, setCtxMenu] = (0,
|
|
4082
|
-
const [navOpen, setNavOpen] = (0,
|
|
4083
|
-
const [announcement, setAnnouncement] = (0,
|
|
4084
|
-
const svgRef = (0,
|
|
4085
|
-
const containerRef = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
4295
|
+
const duplicateNode = (0, import_react20.useCallback)((nodeId) => {
|
|
4159
4296
|
duplicateIds([nodeId]);
|
|
4160
4297
|
}, [duplicateIds]);
|
|
4161
|
-
(0,
|
|
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,
|
|
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,
|
|
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,
|
|
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, {
|
|
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,
|
|
4585
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
4611
|
-
/* @__PURE__ */ (0,
|
|
4612
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
4617
|
-
/* @__PURE__ */ (0,
|
|
4618
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
4630
|
-
/* @__PURE__ */ (0,
|
|
4631
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
4785
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
4937
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
|
|
4792
4938
|
model.edges.length,
|
|
4793
4939
|
" connections"
|
|
4794
4940
|
] }),
|
|
4795
|
-
/* @__PURE__ */ (0,
|
|
4941
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { children: [
|
|
4796
4942
|
Math.round(transform.scale * 100),
|
|
4797
4943
|
"% zoom"
|
|
4798
4944
|
] }),
|
|
4799
|
-
/* @__PURE__ */ (0,
|
|
4800
|
-
selected && /* @__PURE__ */ (0,
|
|
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
|
}
|