system-canvas-standalone 0.1.0 → 0.1.2

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.
@@ -12759,12 +12759,12 @@ var SystemCanvas = (() => {
12759
12759
  render: () => render,
12760
12760
  themes: () => themes
12761
12761
  });
12762
- var import_react19 = __toESM(require_react(), 1);
12762
+ var import_react21 = __toESM(require_react(), 1);
12763
12763
  var import_client = __toESM(require_client(), 1);
12764
12764
 
12765
12765
  // ../react/dist/components/SystemCanvas.js
12766
- var import_jsx_runtime20 = __toESM(require_jsx_runtime(), 1);
12767
- var import_react18 = __toESM(require_react(), 1);
12766
+ var import_jsx_runtime27 = __toESM(require_jsx_runtime(), 1);
12767
+ var import_react20 = __toESM(require_react(), 1);
12768
12768
 
12769
12769
  // ../core/dist/themes/dark.js
12770
12770
  var darkTheme = {
@@ -13723,20 +13723,28 @@ var SystemCanvas = (() => {
13723
13723
  return pos;
13724
13724
  const edge = options.edge ?? "start";
13725
13725
  const size = options.size ?? 0;
13726
- if (edge === "start") {
13727
- const containing = findLaneAt(pos, lanes);
13728
- if (containing)
13729
- return containing.start;
13730
- let best2 = lanes[0];
13731
- let bestDist2 = Math.abs(pos - best2.start);
13732
- for (const lane of lanes) {
13733
- const d = Math.abs(pos - lane.start);
13734
- if (d < bestDist2) {
13735
- best2 = lane;
13736
- bestDist2 = d;
13737
- }
13738
- }
13739
- return best2.start;
13726
+ if (edge === "start" || edge === "center") {
13727
+ const center = pos + size / 2;
13728
+ const containing = findLaneAt(center, lanes);
13729
+ let target;
13730
+ if (containing) {
13731
+ target = containing;
13732
+ } else {
13733
+ let best2 = lanes[0];
13734
+ let bestDist2 = Math.abs(center - (best2.start + best2.size / 2));
13735
+ for (const lane of lanes) {
13736
+ const d = Math.abs(center - (lane.start + lane.size / 2));
13737
+ if (d < bestDist2) {
13738
+ best2 = lane;
13739
+ bestDist2 = d;
13740
+ }
13741
+ }
13742
+ target = best2;
13743
+ }
13744
+ if (edge === "center") {
13745
+ return target.start + (target.size - size) / 2;
13746
+ }
13747
+ return target.start;
13740
13748
  }
13741
13749
  const boundaries = [];
13742
13750
  for (const lane of lanes)
@@ -13792,8 +13800,397 @@ var SystemCanvas = (() => {
13792
13800
  function filterActionsForNode(group, node) {
13793
13801
  return group.actions.filter((a) => !a.appliesTo || a.appliesTo(node));
13794
13802
  }
13803
+ function getNodeActionsForNode(node, theme) {
13804
+ if (node.category) {
13805
+ const def = theme.categories?.[node.category];
13806
+ if (def?.toolbar && def.toolbar.length > 0)
13807
+ return def.toolbar;
13808
+ }
13809
+ return getNodeActions(theme);
13810
+ }
13811
+
13812
+ // ../core/dist/rollup.js
13813
+ function rollupNodes(canvas, predicate) {
13814
+ const nodes = canvas?.nodes ?? [];
13815
+ const total = nodes.length;
13816
+ if (total === 0)
13817
+ return { total: 0, matched: 0, fraction: 0 };
13818
+ let matched = 0;
13819
+ for (const n of nodes) {
13820
+ if (predicate(n))
13821
+ matched++;
13822
+ }
13823
+ return { total, matched, fraction: matched / total };
13824
+ }
13825
+
13826
+ // ../core/dist/slots.js
13827
+ var CORNER_INSET = 8;
13828
+ var CORNER_EM = 1.25;
13829
+ var HEADER_EM = 1;
13830
+ var FOOTER_EM = 1;
13831
+ var HEADER_INSET_X = 14;
13832
+ var HEADER_INSET_Y = 10;
13833
+ var FOOTER_INSET_Y = 10;
13834
+ var TOP_EDGE_PX = 5;
13835
+ var RIGHT_EDGE_PX = 4;
13836
+ var LEFT_EDGE_PX = 4;
13837
+ var BOTTOM_EDGE_MIN_PX = 6;
13838
+ var BOTTOM_EDGE_HEIGHT_RATIO = 0.08;
13839
+ var TAB_BADGE_EM = 1.4;
13840
+ var TAB_BADGE_LIFT = 10;
13841
+ var TAB_BADGE_OVERHANG = 8;
13842
+ function computeCategorySlotRegions(node, theme, slots) {
13843
+ const { x, y, width, height } = node;
13844
+ const fs = theme.node.fontSize;
13845
+ const corner = CORNER_EM * fs;
13846
+ const header = HEADER_EM * fs;
13847
+ const footer = FOOTER_EM * fs;
13848
+ const tab = TAB_BADGE_EM * fs;
13849
+ const bottomEdge = Math.max(BOTTOM_EDGE_MIN_PX, Math.round(height * BOTTOM_EDGE_HEIGHT_RATIO));
13850
+ const bodyReservations = computeBodyReservations(node, theme, slots);
13851
+ const bodyX = x + bodyReservations.left;
13852
+ const bodyY = y + bodyReservations.top;
13853
+ const bodyWidth = Math.max(0, width - bodyReservations.left - bodyReservations.right);
13854
+ const bodyHeight = Math.max(0, height - bodyReservations.top - bodyReservations.bottom);
13855
+ return {
13856
+ topEdge: {
13857
+ x,
13858
+ y,
13859
+ width,
13860
+ height: TOP_EDGE_PX
13861
+ },
13862
+ bottomEdge: {
13863
+ x,
13864
+ y: y + height - bottomEdge,
13865
+ width,
13866
+ height: bottomEdge
13867
+ },
13868
+ leftEdge: {
13869
+ x,
13870
+ y,
13871
+ width: LEFT_EDGE_PX,
13872
+ height
13873
+ },
13874
+ rightEdge: {
13875
+ x: x + width - RIGHT_EDGE_PX,
13876
+ y,
13877
+ width: RIGHT_EDGE_PX,
13878
+ height
13879
+ },
13880
+ bodyTop: (() => {
13881
+ const hasHeader = slots?.header !== void 0;
13882
+ const bandHeight = Math.max(4, Math.round(fs * 0.35));
13883
+ const bandY = hasHeader ? (
13884
+ // Just below the header strip. Header height is ~fs + 4
13885
+ // and sits at HEADER_INSET_Y; pad another small gap so the
13886
+ // bar reads as decoration rather than a divider line.
13887
+ y + HEADER_INSET_Y + fs + 6
13888
+ ) : (
13889
+ // Legacy: under one line of body text.
13890
+ y + HEADER_INSET_Y + fs + Math.round(fs * 0.9)
13891
+ );
13892
+ return {
13893
+ x: x + HEADER_INSET_X,
13894
+ y: bandY,
13895
+ width: Math.max(0, width - HEADER_INSET_X * 2),
13896
+ height: bandHeight
13897
+ };
13898
+ })(),
13899
+ topRightOuter: {
13900
+ // Hangs off the top-right corner. The region extends past the
13901
+ // node's right edge so the badge reads as a notification tab
13902
+ // clinging to the outside of the corner rather than sitting on
13903
+ // top of the node.
13904
+ x: x + width - tab + TAB_BADGE_OVERHANG,
13905
+ y: y - TAB_BADGE_LIFT,
13906
+ width: tab,
13907
+ height: tab
13908
+ },
13909
+ topLeft: {
13910
+ x: x + CORNER_INSET,
13911
+ y: y + CORNER_INSET,
13912
+ width: corner,
13913
+ height: corner
13914
+ },
13915
+ topRight: {
13916
+ x: x + width - CORNER_INSET - corner,
13917
+ y: y + CORNER_INSET,
13918
+ width: corner,
13919
+ height: corner
13920
+ },
13921
+ bottomLeft: {
13922
+ x: x + CORNER_INSET,
13923
+ y: y + height - CORNER_INSET - corner,
13924
+ width: corner,
13925
+ height: corner
13926
+ },
13927
+ bottomRight: {
13928
+ x: x + width - CORNER_INSET - corner,
13929
+ y: y + height - CORNER_INSET - corner,
13930
+ width: corner,
13931
+ height: corner
13932
+ },
13933
+ header: {
13934
+ x: x + HEADER_INSET_X,
13935
+ y: y + HEADER_INSET_Y,
13936
+ width: Math.max(0, width - HEADER_INSET_X * 2),
13937
+ height: header
13938
+ },
13939
+ footer: {
13940
+ x: x + HEADER_INSET_X,
13941
+ y: y + height - FOOTER_INSET_Y - footer,
13942
+ width: Math.max(0, width - HEADER_INSET_X * 2),
13943
+ height: footer
13944
+ },
13945
+ body: {
13946
+ x: bodyX,
13947
+ y: bodyY,
13948
+ width: bodyWidth,
13949
+ height: bodyHeight
13950
+ }
13951
+ };
13952
+ }
13953
+ function computeBodyReservations(node, theme, slots) {
13954
+ if (!slots) {
13955
+ return {
13956
+ top: HEADER_INSET_Y,
13957
+ bottom: FOOTER_INSET_Y,
13958
+ left: HEADER_INSET_X,
13959
+ right: HEADER_INSET_X
13960
+ };
13961
+ }
13962
+ const r = computeReflowReservationsInternal(node, theme, slots);
13963
+ let top = Math.max(r.top, HEADER_INSET_Y);
13964
+ if (slots.bodyTop && slots.header) {
13965
+ const fs = theme.node.fontSize;
13966
+ const bandHeight = Math.max(4, Math.round(fs * 0.35));
13967
+ const bandBottomFromTop = HEADER_INSET_Y + fs + 6 + bandHeight + 6;
13968
+ top = Math.max(top, bandBottomFromTop);
13969
+ }
13970
+ return {
13971
+ top,
13972
+ bottom: Math.max(r.bottom, FOOTER_INSET_Y),
13973
+ left: Math.max(r.left, HEADER_INSET_X),
13974
+ right: Math.max(r.right, HEADER_INSET_X)
13975
+ };
13976
+ }
13977
+ function isFn(v) {
13978
+ return typeof v === "function";
13979
+ }
13980
+ function resolveAccessor(accessor, ctx) {
13981
+ return isFn(accessor) ? accessor(ctx) : accessor;
13982
+ }
13983
+ function resolveAccessorOr(accessor, fallback, ctx) {
13984
+ if (accessor === void 0)
13985
+ return fallback;
13986
+ return resolveAccessor(accessor, ctx);
13987
+ }
13988
+ function getCategorySlots(node, theme) {
13989
+ if (!node.category)
13990
+ return void 0;
13991
+ const def = theme.categories[node.category];
13992
+ return def?.slots;
13993
+ }
13994
+ function pickRefIndicatorCorner(defaultCorner, slots) {
13995
+ if (!slots)
13996
+ return defaultCorner;
13997
+ const occupied = (c) => {
13998
+ if (slots[c])
13999
+ return true;
14000
+ if ((c === "topLeft" || c === "topRight") && slots.header)
14001
+ return true;
14002
+ if ((c === "bottomLeft" || c === "bottomRight") && slots.footer)
14003
+ return true;
14004
+ if (c === "topRight" && slots.topRightOuter)
14005
+ return true;
14006
+ return false;
14007
+ };
14008
+ if (!occupied(defaultCorner))
14009
+ return defaultCorner;
14010
+ const order = {
14011
+ topLeft: ["bottomRight", "topRight", "bottomLeft"],
14012
+ topRight: ["bottomLeft", "bottomRight", "topLeft"],
14013
+ bottomLeft: ["topRight", "topLeft", "bottomRight"],
14014
+ bottomRight: ["topLeft", "bottomLeft", "topRight"]
14015
+ };
14016
+ for (const c of order[defaultCorner]) {
14017
+ if (!occupied(c))
14018
+ return c;
14019
+ }
14020
+ return defaultCorner;
14021
+ }
14022
+ function slotEntries(slots) {
14023
+ const positions = [
14024
+ "topEdge",
14025
+ "bottomEdge",
14026
+ "leftEdge",
14027
+ "rightEdge",
14028
+ "header",
14029
+ "footer",
14030
+ "body",
14031
+ "bodyTop",
14032
+ "topLeft",
14033
+ "topRight",
14034
+ "bottomLeft",
14035
+ "bottomRight",
14036
+ // Outer positions paint last so they visually sit above the node's
14037
+ // own stroke — a tab badge needs to clip into the node's corner.
14038
+ "topRightOuter"
14039
+ ];
14040
+ const out = [];
14041
+ for (const p of positions) {
14042
+ const s = slots[p];
14043
+ if (s)
14044
+ out.push([p, s]);
14045
+ }
14046
+ return out;
14047
+ }
14048
+ function computeReflowReservations(node, theme, slots) {
14049
+ return computeReflowReservationsInternal(node, theme, slots);
14050
+ }
14051
+ function computeReflowReservationsInternal(node, theme, slots) {
14052
+ if (!slots)
14053
+ return { top: 0, bottom: 0, left: 0, right: 0 };
14054
+ const regions = computeCategorySlotRegions(node, theme);
14055
+ let top = 0;
14056
+ let bottom = 0;
14057
+ let left = 0;
14058
+ let right = 0;
14059
+ if (slots.header)
14060
+ top = regions.header.height + HEADER_INSET_Y + 6;
14061
+ if (slots.footer)
14062
+ bottom = regions.footer.height + FOOTER_INSET_Y + 4;
14063
+ if (slots.leftEdge)
14064
+ left = regions.leftEdge.width + 4;
14065
+ if (slots.rightEdge)
14066
+ right = regions.rightEdge.width + 4;
14067
+ if (slots.topLeft && slots.topLeft.kind === "dot") {
14068
+ left = Math.max(left, regions.topLeft.x + regions.topLeft.width - node.x + 6);
14069
+ }
14070
+ const isDashboard = slots.header !== void 0 || slots.topRight !== void 0 || slots.bodyTop !== void 0 || slots.footer !== void 0 || slots.topLeft !== void 0 && slots.topLeft.kind === "dot";
14071
+ if (isDashboard) {
14072
+ if (!slots.header)
14073
+ top = Math.max(top, HEADER_INSET_Y);
14074
+ left = Math.max(left, HEADER_INSET_X);
14075
+ right = Math.max(right, HEADER_INSET_X);
14076
+ }
14077
+ if (slots.topRight && slots.topRight.kind === "pill") {
14078
+ right = Math.max(right, Math.round(theme.node.fontSize * 3.6));
14079
+ }
14080
+ const vertSpace = node.height - top - bottom;
14081
+ if (vertSpace < theme.node.fontSize) {
14082
+ top = 0;
14083
+ bottom = 0;
14084
+ }
14085
+ const horSpace = node.width - left - right;
14086
+ if (horSpace < theme.node.fontSize) {
14087
+ left = 0;
14088
+ right = 0;
14089
+ }
14090
+ return { top, bottom, left, right };
14091
+ }
14092
+
14093
+ // ../core/dist/paths.js
14094
+ function getAtPath(obj, path) {
14095
+ if (obj == null)
14096
+ return void 0;
14097
+ const parts = path.split(".");
14098
+ let cur = obj;
14099
+ for (const p of parts) {
14100
+ if (cur == null || typeof cur !== "object")
14101
+ return void 0;
14102
+ cur = cur[p];
14103
+ }
14104
+ return cur;
14105
+ }
14106
+ function setAtPath(obj, path, value) {
14107
+ const parts = path.split(".");
14108
+ const root2 = obj ? { ...obj } : {};
14109
+ let cur = root2;
14110
+ for (let i = 0; i < parts.length - 1; i++) {
14111
+ const key = parts[i];
14112
+ const next = cur[key];
14113
+ const cloned = next != null && typeof next === "object" && !Array.isArray(next) ? { ...next } : {};
14114
+ cur[key] = cloned;
14115
+ cur = cloned;
14116
+ }
14117
+ cur[parts[parts.length - 1]] = value;
14118
+ return root2;
14119
+ }
14120
+
14121
+ // ../core/dist/text.js
14122
+ var GLYPH_WIDTH_RATIO = 0.55;
14123
+ function measureTextWidth(text, fontSize) {
14124
+ if (!text)
14125
+ return 0;
14126
+ return text.length * fontSize * GLYPH_WIDTH_RATIO;
14127
+ }
14128
+ function wrapText(text, maxWidth, fontSize) {
14129
+ if (!text)
14130
+ return [""];
14131
+ if (maxWidth <= 0)
14132
+ return [text];
14133
+ const words = text.split(/\s+/).filter(Boolean);
14134
+ if (words.length === 0)
14135
+ return [""];
14136
+ const lines = [];
14137
+ let current = "";
14138
+ for (const word of words) {
14139
+ const candidate = current ? `${current} ${word}` : word;
14140
+ if (measureTextWidth(candidate, fontSize) > maxWidth && current) {
14141
+ lines.push(current);
14142
+ current = word;
14143
+ } else {
14144
+ current = candidate;
14145
+ }
14146
+ }
14147
+ if (current)
14148
+ lines.push(current);
14149
+ return lines.length > 0 ? lines : [""];
14150
+ }
14151
+ function wrapTextWithBreaks(text, maxWidth, fontSize, maxLines) {
14152
+ if (!text)
14153
+ return [];
14154
+ const paragraphs = text.split("\n");
14155
+ const out = [];
14156
+ for (const para of paragraphs) {
14157
+ if (para === "") {
14158
+ out.push("");
14159
+ continue;
14160
+ }
14161
+ out.push(...wrapText(para, maxWidth, fontSize));
14162
+ }
14163
+ if (maxLines !== void 0 && maxLines > 0 && out.length > maxLines) {
14164
+ const truncated = out.slice(0, maxLines);
14165
+ const last = truncated[truncated.length - 1];
14166
+ truncated[truncated.length - 1] = ellipsize(last, maxWidth, fontSize);
14167
+ return truncated;
14168
+ }
14169
+ return out;
14170
+ }
14171
+ function ellipsize(line, maxWidth, fontSize) {
14172
+ const ellipsis = "\u2026";
14173
+ if (measureTextWidth(line + ellipsis, fontSize) <= maxWidth) {
14174
+ return line + ellipsis;
14175
+ }
14176
+ const words = line.split(" ");
14177
+ while (words.length > 1) {
14178
+ words.pop();
14179
+ const candidate = words.join(" ") + ellipsis;
14180
+ if (measureTextWidth(candidate, fontSize) <= maxWidth)
14181
+ return candidate;
14182
+ }
14183
+ return line + ellipsis;
14184
+ }
13795
14185
 
13796
14186
  // ../core/dist/canvas.js
14187
+ function deepClone(value) {
14188
+ const g = globalThis;
14189
+ if (typeof g.structuredClone === "function") {
14190
+ return g.structuredClone(value);
14191
+ }
14192
+ return JSON.parse(JSON.stringify(value));
14193
+ }
13797
14194
  function resolveCanvas(canvas, theme) {
13798
14195
  const effectiveTheme = canvas.theme?.categories ? {
13799
14196
  ...theme,
@@ -13856,7 +14253,7 @@ var SystemCanvas = (() => {
13856
14253
  }));
13857
14254
  return [...categoryOptions, ...typeOptions];
13858
14255
  }
13859
- function createNodeFromOption(option, x, y, id2 = generateNodeId()) {
14256
+ function createNodeFromOption(option, x, y, id2 = generateNodeId(), theme) {
13860
14257
  const base = {
13861
14258
  id: id2,
13862
14259
  type: option.nodeType,
@@ -13865,6 +14262,10 @@ var SystemCanvas = (() => {
13865
14262
  };
13866
14263
  if (option.kind === "category") {
13867
14264
  base.category = option.value;
14265
+ const def = theme?.categories?.[option.value];
14266
+ if (def?.defaultCustomData) {
14267
+ base.customData = deepClone(def.defaultCustomData);
14268
+ }
13868
14269
  }
13869
14270
  switch (option.nodeType) {
13870
14271
  case "text":
@@ -14607,8 +15008,8 @@ var SystemCanvas = (() => {
14607
15008
  }
14608
15009
 
14609
15010
  // ../react/dist/components/Viewport.js
14610
- var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
14611
- var import_react13 = __toESM(require_react(), 1);
15011
+ var import_jsx_runtime22 = __toESM(require_jsx_runtime(), 1);
15012
+ var import_react15 = __toESM(require_react(), 1);
14612
15013
 
14613
15014
  // ../react/dist/hooks/useViewport.js
14614
15015
  var import_react7 = __toESM(require_react(), 1);
@@ -17442,13 +17843,13 @@ var SystemCanvas = (() => {
17442
17843
  if (options2?.targetZoom != null) {
17443
17844
  targetZoom = options2.targetZoom;
17444
17845
  } else {
17445
- const padding = 40;
17846
+ const padding = 8;
17446
17847
  const scaleX = rect.width / (node.width + padding * 2);
17447
17848
  const scaleY = rect.height / (node.height + padding * 2);
17448
- targetZoom = Math.min(scaleX, scaleY, 3);
17849
+ targetZoom = Math.min(scaleX, scaleY, 12);
17449
17850
  }
17450
17851
  const t = identity2.translate(rect.width / 2 - nodeCx * targetZoom, rect.height / 2 - nodeCy * targetZoom).scale(targetZoom);
17451
- select_default2(svg).transition().duration(options2?.durationMs ?? 500).call(zoomBehaviorRef.current.transform, t).on("end", () => {
17852
+ select_default2(svg).transition().duration(options2?.durationMs ?? 900).call(zoomBehaviorRef.current.transform, t).on("end", () => {
17452
17853
  onComplete?.();
17453
17854
  });
17454
17855
  }, []);
@@ -17464,10 +17865,10 @@ var SystemCanvas = (() => {
17464
17865
  }
17465
17866
 
17466
17867
  // ../react/dist/components/NodeRenderer.js
17467
- var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
17868
+ var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
17468
17869
 
17469
17870
  // ../react/dist/components/TextNode.js
17470
- var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
17871
+ var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
17471
17872
 
17472
17873
  // ../react/dist/components/NodeIcon.js
17473
17874
  var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
@@ -17582,7 +17983,8 @@ var SystemCanvas = (() => {
17582
17983
  const stopAll = (e) => e.stopPropagation();
17583
17984
  const right = nodeX + nodeWidth;
17584
17985
  const bottom = nodeY + nodeHeight;
17585
- const nodeRadius = corner === "top-right" ? theme.group.cornerRadius : node.resolvedCornerRadius;
17986
+ const isTopCorner = corner === "top-left" || corner === "top-right";
17987
+ const nodeRadius = isTopCorner ? theme.group.cornerRadius : node.resolvedCornerRadius;
17586
17988
  const innerR = Math.max(0, Math.min(nodeRadius * 0.64, size / 2));
17587
17989
  let squareX;
17588
17990
  let squareY;
@@ -17591,10 +17993,18 @@ var SystemCanvas = (() => {
17591
17993
  squareX = right - size;
17592
17994
  squareY = bottom - size;
17593
17995
  carvePath = `M ${right} ${squareY} L ${squareX + innerR} ${squareY} A ${innerR} ${innerR} 0 0 0 ${squareX} ${squareY + innerR} L ${squareX} ${bottom}`;
17594
- } else {
17996
+ } else if (corner === "top-right") {
17595
17997
  squareX = right - size;
17596
17998
  squareY = nodeY;
17597
17999
  carvePath = `M ${squareX} ${nodeY} L ${squareX} ${squareY + size - innerR} A ${innerR} ${innerR} 0 0 0 ${squareX + innerR} ${squareY + size} L ${right} ${squareY + size}`;
18000
+ } else if (corner === "bottom-left") {
18001
+ squareX = nodeX;
18002
+ squareY = bottom - size;
18003
+ carvePath = `M ${squareX + size} ${bottom} L ${squareX + size} ${squareY + innerR} A ${innerR} ${innerR} 0 0 0 ${squareX + size - innerR} ${squareY} L ${nodeX} ${squareY}`;
18004
+ } else {
18005
+ squareX = nodeX;
18006
+ squareY = nodeY;
18007
+ carvePath = `M ${squareX + size} ${nodeY} L ${squareX + size} ${squareY + size - innerR} A ${innerR} ${innerR} 0 0 1 ${squareX + size - innerR} ${squareY + size} L ${nodeX} ${squareY + size}`;
17598
18008
  }
17599
18009
  const cx = squareX + size / 2;
17600
18010
  const cy = squareY + size / 2;
@@ -17605,8 +18015,12 @@ var SystemCanvas = (() => {
17605
18015
  let hoverPath;
17606
18016
  if (corner === "bottom-right") {
17607
18017
  hoverPath = `M ${right} ${squareY} L ${right} ${bottom} L ${squareX} ${bottom} L ${squareX} ${squareY + innerR} A ${innerR} ${innerR} 0 0 1 ${squareX + innerR} ${squareY} Z`;
17608
- } else {
18018
+ } else if (corner === "top-right") {
17609
18019
  hoverPath = `M ${squareX} ${nodeY} L ${right} ${nodeY} L ${right} ${squareY + size} L ${squareX + innerR} ${squareY + size} A ${innerR} ${innerR} 0 0 1 ${squareX} ${squareY + size - innerR} Z`;
18020
+ } else if (corner === "bottom-left") {
18021
+ hoverPath = `M ${nodeX} ${squareY} L ${squareX + size - innerR} ${squareY} A ${innerR} ${innerR} 0 0 1 ${squareX + size} ${squareY + innerR} L ${squareX + size} ${bottom} L ${nodeX} ${bottom} Z`;
18022
+ } else {
18023
+ hoverPath = `M ${nodeX} ${nodeY} L ${squareX + size} ${nodeY} L ${squareX + size} ${squareY + size - innerR} A ${innerR} ${innerR} 0 0 1 ${squareX + size - innerR} ${squareY + size} L ${nodeX} ${squareY + size} Z`;
17610
18024
  }
17611
18025
  return (0, import_jsx_runtime2.jsxs)("g", { className: "system-canvas-ref-indicator", style: { cursor: "pointer" }, onClick: (e) => {
17612
18026
  e.stopPropagation();
@@ -17627,31 +18041,357 @@ var SystemCanvas = (() => {
17627
18041
  }
17628
18042
  }
17629
18043
 
18044
+ // ../react/dist/components/CategorySlotsLayer.js
18045
+ var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
18046
+ var import_react10 = __toESM(require_react(), 1);
18047
+
18048
+ // ../react/dist/primitives/NodeColorFill.js
18049
+ var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
18050
+ function NodeColorFill({ region, color: color2, position, extent, length = 0.55, cornerRadius, opacity = 1 }) {
18051
+ const isEdge = position === "topEdge" || position === "bottomEdge" || position === "leftEdge" || position === "rightEdge";
18052
+ const effectiveExtent = extent ?? (isEdge ? "short" : "full");
18053
+ if (isEdge && effectiveExtent === "short") {
18054
+ return (0, import_jsx_runtime3.jsx)(ShortEdgeStrip, { region, color: color2, position, length: Math.max(0.1, Math.min(1, length)), opacity });
18055
+ }
18056
+ const rx = cornerRadius ?? // On full-width edge strips, round a bit so the ends tuck under the
18057
+ // node's own rounded corners cleanly.
18058
+ (isEdge ? Math.min(region.height, region.width) / 2 : 0);
18059
+ return (0, import_jsx_runtime3.jsx)("rect", { x: region.x, y: region.y, width: region.width, height: region.height, rx, fill: color2, opacity, pointerEvents: "none" });
18060
+ }
18061
+ function ShortEdgeStrip({ region, color: color2, position, length, opacity }) {
18062
+ let x = region.x;
18063
+ let y = region.y;
18064
+ let w = region.width;
18065
+ let h = region.height;
18066
+ if (position === "topEdge" || position === "bottomEdge") {
18067
+ w = region.width * length;
18068
+ } else {
18069
+ h = region.height * length;
18070
+ }
18071
+ const shortDim = Math.min(w, h);
18072
+ const rx = shortDim / 2;
18073
+ return (0, import_jsx_runtime3.jsx)("rect", { x, y, width: w, height: h, rx, fill: color2, opacity, pointerEvents: "none" });
18074
+ }
18075
+
18076
+ // ../react/dist/primitives/NodeProgressBar.js
18077
+ var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
18078
+ function NodeProgressBar({ region, value, color: color2, bgColor, cornerRadius, trackAlpha = 0.12 }) {
18079
+ const v = Math.max(0, Math.min(1, value));
18080
+ const filled = Math.max(0, region.width * v);
18081
+ const rx = cornerRadius ?? Math.min(region.width, region.height) / 2;
18082
+ const track = bgColor ?? colorWithAlpha(color2, trackAlpha);
18083
+ return (0, import_jsx_runtime4.jsxs)("g", { pointerEvents: "none", children: [(0, import_jsx_runtime4.jsx)("rect", { x: region.x, y: region.y, width: region.width, height: region.height, rx, fill: track }), filled > 0 && (0, import_jsx_runtime4.jsx)("rect", { x: region.x, y: region.y, width: filled, height: region.height, rx, fill: color2 })] });
18084
+ }
18085
+ function colorWithAlpha(color2, alpha) {
18086
+ if (color2.startsWith("#")) {
18087
+ const h = color2.slice(1);
18088
+ let r;
18089
+ let g;
18090
+ let b;
18091
+ if (h.length === 3) {
18092
+ r = parseInt(h[0] + h[0], 16);
18093
+ g = parseInt(h[1] + h[1], 16);
18094
+ b = parseInt(h[2] + h[2], 16);
18095
+ } else if (h.length === 6) {
18096
+ r = parseInt(h.slice(0, 2), 16);
18097
+ g = parseInt(h.slice(2, 4), 16);
18098
+ b = parseInt(h.slice(4, 6), 16);
18099
+ } else {
18100
+ return `rgba(255, 255, 255, ${alpha})`;
18101
+ }
18102
+ if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
18103
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
18104
+ }
18105
+ }
18106
+ const match = color2.match(/^rgba?\(([^)]+)\)$/);
18107
+ if (match) {
18108
+ const parts = match[1].split(",").map((p) => p.trim());
18109
+ if (parts.length >= 3) {
18110
+ return `rgba(${parts[0]}, ${parts[1]}, ${parts[2]}, ${alpha})`;
18111
+ }
18112
+ }
18113
+ return `rgba(255, 255, 255, ${alpha})`;
18114
+ }
18115
+
18116
+ // ../react/dist/primitives/NodeCountBadge.js
18117
+ var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
18118
+ function NodeCountBadge({ region, value, theme, color: color2, textColor, position }) {
18119
+ const label = typeof value === "number" ? String(value) : value;
18120
+ if (label.length === 0)
18121
+ return null;
18122
+ const fill = color2 ?? theme.node.refIndicator.color;
18123
+ const text = textColor ?? theme.background;
18124
+ if (position === "topRightOuter") {
18125
+ return (0, import_jsx_runtime5.jsx)(TabBadge, { region, label, fill, text, theme });
18126
+ }
18127
+ return (0, import_jsx_runtime5.jsx)(PillBadge, { region, label, fill, text, theme });
18128
+ }
18129
+ function TabBadge({ region, label, fill, text, theme }) {
18130
+ const h = region.height;
18131
+ const minW = h;
18132
+ const fontSize = Math.max(9, h * 0.58);
18133
+ const estW = label.length <= 1 ? minW : Math.max(minW, fontSize * 0.7 * label.length + 10);
18134
+ const w = Math.min(estW, h * 3);
18135
+ const x = region.x + region.width - w;
18136
+ const y = region.y;
18137
+ const rx = Math.min(6, h / 3);
18138
+ const cx = x + w / 2;
18139
+ const cy = y + h / 2;
18140
+ const ringWidth = 2;
18141
+ return (0, import_jsx_runtime5.jsxs)("g", { pointerEvents: "none", children: [(0, import_jsx_runtime5.jsx)("rect", { x: x - ringWidth, y: y - ringWidth, width: w + ringWidth * 2, height: h + ringWidth * 2, rx: rx + ringWidth, fill: theme.background }), (0, import_jsx_runtime5.jsx)("rect", { x, y, width: w, height: h, rx, fill }), (0, import_jsx_runtime5.jsx)("text", { x: cx, y: cy + fontSize * 0.35, fill: text, fontSize, fontWeight: 700, fontFamily: theme.node.fontFamily, textAnchor: "middle", children: label })] });
18142
+ }
18143
+ function PillBadge({ region, label, fill, text, theme }) {
18144
+ const fontSize = Math.max(9, Math.min(region.height * 0.7, 14));
18145
+ const estWidth = Math.max(region.height, region.height * 0.65 + label.length * fontSize * 0.55);
18146
+ const width = Math.min(estWidth, region.width * 1.6);
18147
+ const height = region.height;
18148
+ const cx = region.x + region.width / 2;
18149
+ const cy = region.y + region.height / 2;
18150
+ const x = cx - width / 2;
18151
+ const y = cy - height / 2;
18152
+ return (0, import_jsx_runtime5.jsxs)("g", { pointerEvents: "none", children: [(0, import_jsx_runtime5.jsx)("rect", { x, y, width, height, rx: height / 2, fill }), (0, import_jsx_runtime5.jsx)("text", { x: cx, y: cy + fontSize * 0.35, fill: text, fontSize, fontWeight: 600, fontFamily: theme.node.fontFamily, textAnchor: "middle", children: label })] });
18153
+ }
18154
+
18155
+ // ../react/dist/primitives/NodeDot.js
18156
+ var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
18157
+ function NodeDot({ region, color: color2 }) {
18158
+ const cx = region.x + region.width / 2;
18159
+ const cy = region.y + region.height / 2;
18160
+ const r = Math.max(2, Math.min(region.width, region.height) * 0.2);
18161
+ return (0, import_jsx_runtime6.jsx)("circle", { cx, cy, r, fill: color2, pointerEvents: "none" });
18162
+ }
18163
+
18164
+ // ../react/dist/primitives/NodeText.js
18165
+ var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
18166
+ var import_react9 = __toESM(require_react(), 1);
18167
+ function NodeText({ region, value, theme, color: color2, fill, align = "start", fontWeight = 500, uppercase = false, useLabelFont = false, fontFamily, fontSize: fontSizeProp, wrap = false, maxLines, lineHeight: lineHeightProp }) {
18168
+ const reactId = (0, import_react9.useId)();
18169
+ const safeId = reactId.replace(/:/g, "");
18170
+ if (!value)
18171
+ return null;
18172
+ const fontSize = fontSizeProp ?? Math.max(9, Math.min(theme.node.fontSize - 2, region.height * 0.85));
18173
+ const lineHeight = lineHeightProp ?? Math.round(fontSize * 1.25);
18174
+ const anchor = align === "start" ? "start" : align === "center" ? "middle" : "end";
18175
+ const x = align === "start" ? region.x : align === "center" ? region.x + region.width / 2 : region.x + region.width;
18176
+ const font = fontFamily ?? (useLabelFont ? theme.node.labelFont ?? theme.node.fontFamily : theme.node.fontFamily);
18177
+ const gradId = fill ? `sc-text-grad-${safeId}` : void 0;
18178
+ const fillAttr = gradId ? `url(#${gradId})` : color2 ?? theme.node.sublabelColor;
18179
+ const displayValue = uppercase ? value.toUpperCase() : value;
18180
+ if (!wrap) {
18181
+ const y = region.y + region.height / 2 + fontSize * 0.36;
18182
+ return (0, import_jsx_runtime7.jsxs)("g", { pointerEvents: "none", children: [fill && (0, import_jsx_runtime7.jsx)(GradientDef, { id: gradId, fill }), (0, import_jsx_runtime7.jsx)("text", { x, y, fill: fillAttr, fontSize, fontWeight, fontFamily: font, textAnchor: anchor, letterSpacing: uppercase ? 0.8 : 0.2, pointerEvents: "none", children: displayValue })] });
18183
+ }
18184
+ const lines = wrapTextWithBreaks(displayValue, region.width, fontSize, maxLines);
18185
+ if (lines.length === 0)
18186
+ return null;
18187
+ const baseY = region.y + fontSize;
18188
+ const clipId = `sc-text-clip-${safeId}`;
18189
+ return (0, import_jsx_runtime7.jsxs)("g", { pointerEvents: "none", children: [(0, import_jsx_runtime7.jsxs)("defs", { children: [fill && (0, import_jsx_runtime7.jsx)(GradientDef, { id: gradId, fill }), (0, import_jsx_runtime7.jsx)("clipPath", { id: clipId, children: (0, import_jsx_runtime7.jsx)("rect", { x: region.x, y: region.y, width: region.width, height: region.height }) })] }), (0, import_jsx_runtime7.jsx)("g", { clipPath: `url(#${clipId})`, pointerEvents: "none", children: (0, import_jsx_runtime7.jsx)("text", { x, y: baseY, fill: fillAttr, fontSize, fontWeight, fontFamily: font, textAnchor: anchor, letterSpacing: uppercase ? 0.8 : 0.2, pointerEvents: "none", children: lines.map((line, i) => (0, import_jsx_runtime7.jsx)("tspan", { x, dy: i === 0 ? 0 : lineHeight, children: line || " " }, i)) }) })] });
18190
+ }
18191
+ function GradientDef({ id: id2, fill }) {
18192
+ const angle = fill.angle ?? 0;
18193
+ return (0, import_jsx_runtime7.jsxs)("linearGradient", { id: id2, x1: "0", y1: "0", x2: "1", y2: "0", gradientUnits: "objectBoundingBox", gradientTransform: angle ? `rotate(${angle} 0.5 0.5)` : void 0, children: [(0, import_jsx_runtime7.jsx)("stop", { offset: "0%", stopColor: fill.from }), (0, import_jsx_runtime7.jsx)("stop", { offset: "100%", stopColor: fill.to })] });
18194
+ }
18195
+
18196
+ // ../react/dist/primitives/NodeStatusPill.js
18197
+ var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
18198
+ function NodeStatusPill({ region, value, theme, color: color2, textColor, fill }) {
18199
+ const label = value.toUpperCase();
18200
+ if (!label)
18201
+ return null;
18202
+ const h = Math.min(region.height, 18);
18203
+ const fontSize = Math.max(9, h * 0.62);
18204
+ const padX = 8;
18205
+ const w = Math.max(h * 1.6, label.length * fontSize * 0.62 + padX * 2);
18206
+ const x = region.x + region.width - w;
18207
+ const y = region.y + (region.height - h) / 2;
18208
+ const rx = h / 2;
18209
+ const cx = x + w / 2;
18210
+ const cy = y + h / 2;
18211
+ const tint = fill ?? toTint(color2);
18212
+ const fg = textColor ?? color2;
18213
+ return (0, import_jsx_runtime8.jsxs)("g", { pointerEvents: "none", children: [(0, import_jsx_runtime8.jsx)("rect", { x, y, width: w, height: h, rx, fill: tint }), (0, import_jsx_runtime8.jsx)("text", { x: cx, y: cy + fontSize * 0.35, fill: fg, fontSize, fontWeight: 700, fontFamily: theme.node.fontFamily, textAnchor: "middle", letterSpacing: 0.6, children: label })] });
18214
+ }
18215
+ function toTint(color2) {
18216
+ if (color2.startsWith("#") && (color2.length === 7 || color2.length === 4)) {
18217
+ let r;
18218
+ let g;
18219
+ let b;
18220
+ if (color2.length === 7) {
18221
+ r = parseInt(color2.slice(1, 3), 16);
18222
+ g = parseInt(color2.slice(3, 5), 16);
18223
+ b = parseInt(color2.slice(5, 7), 16);
18224
+ } else {
18225
+ r = parseInt(color2[1] + color2[1], 16);
18226
+ g = parseInt(color2[2] + color2[2], 16);
18227
+ b = parseInt(color2[3] + color2[3], 16);
18228
+ }
18229
+ if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
18230
+ return `rgba(${r}, ${g}, ${b}, 0.15)`;
18231
+ }
18232
+ }
18233
+ return "rgba(255, 255, 255, 0.08)";
18234
+ }
18235
+
18236
+ // ../react/dist/components/CategorySlotsLayer.js
18237
+ function CategorySlotsLayer({ node, theme, canvases, slots: slotsProp }) {
18238
+ const slots = slotsProp ?? getCategorySlots(node, theme);
18239
+ const regions = (0, import_react10.useMemo)(() => computeCategorySlotRegions(node, theme, slots), [node, theme, slots]);
18240
+ const reactId = (0, import_react10.useId)();
18241
+ const clipId = `sc-edge-clip-${reactId.replace(/:/g, "")}`;
18242
+ if (!slots)
18243
+ return null;
18244
+ const getSubCanvas = (ref) => canvases?.[ref];
18245
+ const entries = slotEntries(slots);
18246
+ if (entries.length === 0)
18247
+ return null;
18248
+ const edgeEntries = entries.filter(([p]) => isEdgePosition(p));
18249
+ const otherEntries = entries.filter(([p]) => !isEdgePosition(p));
18250
+ const renderEntry = ([position, spec]) => {
18251
+ const region = regions[position];
18252
+ const ctx = {
18253
+ node,
18254
+ theme,
18255
+ region,
18256
+ getSubCanvas,
18257
+ canvases,
18258
+ rollup: (predicate) => node.ref ? rollupNodes(getSubCanvas(node.ref), predicate) : { total: 0, matched: 0, fraction: 0 }
18259
+ };
18260
+ return (0, import_jsx_runtime9.jsx)(SlotView, { position, spec, ctx }, position);
18261
+ };
18262
+ return (0, import_jsx_runtime9.jsxs)("g", { className: "system-canvas-slots", pointerEvents: "none", children: [edgeEntries.length > 0 && (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [(0, import_jsx_runtime9.jsx)("defs", { children: (0, import_jsx_runtime9.jsx)("clipPath", { id: clipId, children: (0, import_jsx_runtime9.jsx)("rect", { x: node.x, y: node.y, width: node.width, height: node.height, rx: node.resolvedCornerRadius }) }) }), (0, import_jsx_runtime9.jsx)("g", { clipPath: `url(#${clipId})`, children: edgeEntries.map(renderEntry) })] }), otherEntries.map(renderEntry)] });
18263
+ }
18264
+ function isEdgePosition(p) {
18265
+ return p === "topEdge" || p === "bottomEdge" || p === "leftEdge" || p === "rightEdge";
18266
+ }
18267
+ function SlotView({ position, spec, ctx }) {
18268
+ try {
18269
+ return renderSlot(position, spec, ctx);
18270
+ } catch (err) {
18271
+ const env = globalThis.process?.env?.NODE_ENV;
18272
+ if (env !== "production") {
18273
+ console.warn("[system-canvas] slot render failed", position, err);
18274
+ }
18275
+ return null;
18276
+ }
18277
+ }
18278
+ function renderSlot(position, spec, ctx) {
18279
+ const { theme, node, region } = ctx;
18280
+ const nodeColor = node.resolvedStroke;
18281
+ switch (spec.kind) {
18282
+ case "color": {
18283
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18284
+ const length = resolveAccessorOr(spec.length, 0.55, ctx);
18285
+ return (0, import_jsx_runtime9.jsx)(NodeColorFill, { region, color: color2, position, extent: spec.extent, length });
18286
+ }
18287
+ case "progress": {
18288
+ const value = resolveAccessor(spec.value, ctx);
18289
+ if (spec.hideWhenZero && (!Number.isFinite(value) || value <= 0)) {
18290
+ return null;
18291
+ }
18292
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18293
+ const bgColor = resolveAccessorOr(spec.bgColor, "rgba(255,255,255,0.08)", ctx);
18294
+ return (0, import_jsx_runtime9.jsx)(NodeProgressBar, { region, value, color: color2, bgColor });
18295
+ }
18296
+ case "count": {
18297
+ const raw = resolveAccessor(spec.value, ctx);
18298
+ const hideWhenEmpty = spec.hideWhenEmpty !== false;
18299
+ if (hideWhenEmpty) {
18300
+ if (raw === 0 || raw === "" || raw == null)
18301
+ return null;
18302
+ }
18303
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18304
+ const textColor = resolveAccessorOr(spec.textColor, theme.background, ctx);
18305
+ return (0, import_jsx_runtime9.jsx)(NodeCountBadge, { region, value: raw, theme, color: color2, textColor, position });
18306
+ }
18307
+ case "pill": {
18308
+ const value = resolveAccessor(spec.value, ctx);
18309
+ if (!value)
18310
+ return null;
18311
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18312
+ const textColor = spec.textColor ? resolveAccessor(spec.textColor, ctx) : void 0;
18313
+ const fill = spec.fill ? resolveAccessor(spec.fill, ctx) : void 0;
18314
+ return (0, import_jsx_runtime9.jsx)(NodeStatusPill, { region, value, theme, color: color2, textColor, fill });
18315
+ }
18316
+ case "text": {
18317
+ const value = resolveAccessor(spec.value, ctx);
18318
+ const color2 = resolveAccessorOr(spec.color, position === "body" ? theme.node.labelColor : theme.node.sublabelColor, ctx);
18319
+ const defaultAlign = position === "header" || position === "footer" || position === "body" ? "start" : "center";
18320
+ const align = spec.align ?? defaultAlign;
18321
+ const isHeader = position === "header";
18322
+ const isBody = position === "body";
18323
+ const defaultWeight = isHeader ? 700 : isBody ? 600 : 500;
18324
+ const defaultUppercase = isHeader;
18325
+ const defaultUseLabelFont = isHeader || isBody;
18326
+ const defaultFontSize = isBody ? Math.round(theme.node.fontSize * 1.35) : void 0;
18327
+ const fontSize = spec.fontSize !== void 0 ? resolveAccessor(spec.fontSize, ctx) : defaultFontSize;
18328
+ const fontWeight = spec.fontWeight !== void 0 ? resolveAccessor(spec.fontWeight, ctx) : defaultWeight;
18329
+ const fontFamily = spec.fontFamily !== void 0 ? resolveAccessor(spec.fontFamily, ctx) : void 0;
18330
+ const wrap = spec.wrap ?? isBody;
18331
+ const lineHeight = spec.lineHeight !== void 0 ? resolveAccessor(spec.lineHeight, ctx) : void 0;
18332
+ const fill = spec.fill !== void 0 ? resolveAccessor(spec.fill, ctx) : void 0;
18333
+ return (0, import_jsx_runtime9.jsx)(NodeText, { region, value, theme, color: color2, fill, align, fontWeight, uppercase: spec.uppercase ?? defaultUppercase, useLabelFont: spec.useLabelFont ?? defaultUseLabelFont, fontFamily, fontSize, wrap, maxLines: spec.maxLines, lineHeight });
18334
+ }
18335
+ case "dot": {
18336
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18337
+ return (0, import_jsx_runtime9.jsx)(NodeDot, { region, color: color2 });
18338
+ }
18339
+ case "custom": {
18340
+ return spec.render(ctx);
18341
+ }
18342
+ }
18343
+ }
18344
+
18345
+ // ../react/dist/components/refCorner.js
18346
+ function toKebabCorner(c) {
18347
+ switch (c) {
18348
+ case "topLeft":
18349
+ return "top-left";
18350
+ case "topRight":
18351
+ return "top-right";
18352
+ case "bottomLeft":
18353
+ return "bottom-left";
18354
+ case "bottomRight":
18355
+ return "bottom-right";
18356
+ }
18357
+ }
18358
+
17630
18359
  // ../react/dist/components/TextNode.js
17631
- function TextNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing }) {
18360
+ function TextNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "bottomRight" }) {
17632
18361
  const { x, y, width, height } = node;
17633
- const cx = x + width / 2;
18362
+ const contentX = x + reservedLeft;
18363
+ const contentY = y + reservedTop;
18364
+ const contentWidth = Math.max(0, width - reservedLeft - reservedRight);
18365
+ const contentHeight = Math.max(0, height - reservedTop - reservedBottom);
17634
18366
  const text = node.text ?? "";
17635
18367
  const lines = text.split("\n").filter(Boolean);
17636
18368
  const mainLabel = lines[0] ?? node.id;
17637
18369
  const sublabel = lines[1];
17638
- const lineHeight = theme.node.fontSize + 4;
18370
+ const hasBodySlot = slots?.body !== void 0;
18371
+ const hasHeader = reservedTop > 0;
18372
+ const labelFont = theme.node.labelFont ?? theme.node.fontFamily;
18373
+ const labelFontSize = theme.node.fontSize + (hasHeader ? 1 : 0);
18374
+ const lineHeight = labelFontSize + 4;
17639
18375
  const totalTextHeight = sublabel ? lineHeight + theme.node.sublabelFontSize + 4 : lineHeight;
17640
- const textStartY = y + (height - totalTextHeight) / 2 + theme.node.fontSize;
17641
- return (0, import_jsx_runtime3.jsxs)("g", { className: "system-canvas-node system-canvas-node--text", style: { cursor: onPointerDown ? "move" : node.isNavigable ? "pointer" : "default" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime3.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: theme.background }), (0, import_jsx_runtime3.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: node.resolvedFill, stroke: node.resolvedStroke, strokeWidth: theme.node.strokeWidth }), !isEditing && (0, import_jsx_runtime3.jsx)("text", { x: cx, y: textStartY, fill: theme.node.labelColor, fontSize: theme.node.fontSize, fontWeight: 600, fontFamily: theme.node.fontFamily, textAnchor: "middle", pointerEvents: "none", children: mainLabel }), !isEditing && sublabel && (0, import_jsx_runtime3.jsx)("text", { x: cx, y: textStartY + lineHeight, fill: theme.node.sublabelColor, fontSize: theme.node.sublabelFontSize, fontFamily: theme.node.fontFamily, textAnchor: "middle", pointerEvents: "none", children: sublabel }), node.isNavigable && (0, import_jsx_runtime3.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor: node.resolvedStroke, strokeWidth: theme.node.strokeWidth, onNavigate }), node.resolvedIcon && (0, import_jsx_runtime3.jsx)(NodeIcon, { icon: node.resolvedIcon, x: x + 8, y: y + height / 2 - 7, size: 14, color: node.resolvedStroke, opacity: 0.7, customIcons: theme.icons })] });
18376
+ const labelAnchor = hasHeader ? "start" : "middle";
18377
+ const labelX = hasHeader ? contentX : contentX + contentWidth / 2;
18378
+ const textStartY = hasHeader ? contentY + labelFontSize + 2 : contentY + (contentHeight - totalTextHeight) / 2 + labelFontSize;
18379
+ return (0, import_jsx_runtime10.jsxs)("g", { className: "system-canvas-node system-canvas-node--text", style: { cursor: onPointerDown ? "move" : node.isNavigable ? "pointer" : "default" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime10.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: theme.background }), (0, import_jsx_runtime10.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: node.resolvedFill, stroke: node.resolvedStroke, strokeWidth: theme.node.strokeWidth }), !isEditing && !hasBodySlot && (0, import_jsx_runtime10.jsx)("text", { x: labelX, y: textStartY, fill: theme.node.labelColor, fontSize: labelFontSize, fontWeight: 600, fontFamily: labelFont, textAnchor: labelAnchor, pointerEvents: "none", children: mainLabel }), !isEditing && !hasBodySlot && sublabel && (0, import_jsx_runtime10.jsx)("text", { x: labelX, y: textStartY + lineHeight, fill: theme.node.sublabelColor, fontSize: theme.node.sublabelFontSize, fontFamily: theme.node.fontFamily, textAnchor: labelAnchor, pointerEvents: "none", children: sublabel }), node.resolvedIcon && (0, import_jsx_runtime10.jsx)(NodeIcon, { icon: node.resolvedIcon, x: x + 8 + reservedLeft, y: contentY + contentHeight / 2 - 7, size: 14, color: node.resolvedStroke, opacity: 0.7, customIcons: theme.icons }), slots && (0, import_jsx_runtime10.jsx)(CategorySlotsLayer, { node, theme, canvases, slots }), node.isNavigable && (0, import_jsx_runtime10.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor: node.resolvedStroke, strokeWidth: theme.node.strokeWidth, corner: toKebabCorner(refCorner), onNavigate })] });
17642
18380
  }
17643
18381
 
17644
18382
  // ../react/dist/components/FileNode.js
17645
- var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
17646
- function FileNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing }) {
18383
+ var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
18384
+ function FileNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "bottomRight" }) {
17647
18385
  const { x, y, width, height } = node;
17648
18386
  const filePath = node.file ?? "";
17649
18387
  const fileName = filePath.split("/").pop() ?? filePath;
17650
18388
  const dirPath = filePath.includes("/") ? filePath.slice(0, filePath.lastIndexOf("/")) : "";
17651
18389
  const subpath = node.subpath ?? "";
17652
18390
  const fold = 10;
17653
- const textPadding = 10;
17654
- const maxTextWidth = width - textPadding * 2 - fold;
18391
+ const textPadding = 10 + reservedLeft;
18392
+ const maxTextWidth = width - textPadding - reservedRight - fold;
18393
+ const contentY = y + reservedTop;
18394
+ const contentHeight = Math.max(0, height - reservedTop - reservedBottom);
17655
18395
  const shapePath = [
17656
18396
  `M ${x + 2} ${y}`,
17657
18397
  `L ${x + width - fold} ${y}`,
@@ -17668,35 +18408,41 @@ var SystemCanvas = (() => {
17668
18408
  const strokeColor = node.resolvedStroke;
17669
18409
  const thinStroke = 0.75;
17670
18410
  const clipId = `file-clip-${node.id}`;
17671
- return (0, import_jsx_runtime4.jsxs)("g", { className: "system-canvas-node system-canvas-node--file", style: { cursor: onPointerDown ? "move" : node.isNavigable ? "pointer" : "default" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime4.jsx)("defs", { children: (0, import_jsx_runtime4.jsx)("clipPath", { id: clipId, children: (0, import_jsx_runtime4.jsx)("rect", { x: x + textPadding, y, width: maxTextWidth, height }) }) }), (0, import_jsx_runtime4.jsx)("path", { d: shapePath, fill: theme.background }), (0, import_jsx_runtime4.jsx)("path", { d: shapePath, fill: node.resolvedFill, stroke: strokeColor, strokeWidth: thinStroke }), (0, import_jsx_runtime4.jsx)("path", { d: foldPath, fill: "none", stroke: strokeColor, strokeWidth: thinStroke, opacity: 0.5 }), !isEditing && (0, import_jsx_runtime4.jsxs)("g", { clipPath: `url(#${clipId})`, children: [dirPath && (0, import_jsx_runtime4.jsxs)("text", { x: x + textPadding, y: y + 14, fill: theme.node.sublabelColor, fontSize: theme.node.sublabelFontSize - 1, fontFamily: theme.node.fontFamily, pointerEvents: "none", opacity: 0.6, children: [dirPath, "/"] }), (0, import_jsx_runtime4.jsx)("text", { x: x + textPadding, y: y + (dirPath ? 28 : height / 2 + (subpath ? -2 : 4)), fill: theme.node.labelColor, fontSize: theme.node.fontSize - 1, fontWeight: 500, fontFamily: theme.node.fontFamily, pointerEvents: "none", children: fileName }), subpath && (0, import_jsx_runtime4.jsx)("text", { x: x + textPadding, y: y + (dirPath ? 40 : height / 2 + theme.node.fontSize + 2), fill: theme.node.sublabelColor, fontSize: theme.node.sublabelFontSize, fontFamily: theme.node.fontFamily, pointerEvents: "none", children: subpath })] }), node.isNavigable && (0, import_jsx_runtime4.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor, strokeWidth: thinStroke, onNavigate })] });
18411
+ return (0, import_jsx_runtime11.jsxs)("g", { className: "system-canvas-node system-canvas-node--file", style: { cursor: onPointerDown ? "move" : node.isNavigable ? "pointer" : "default" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime11.jsx)("defs", { children: (0, import_jsx_runtime11.jsx)("clipPath", { id: clipId, children: (0, import_jsx_runtime11.jsx)("rect", { x: x + textPadding, y: contentY, width: maxTextWidth, height: contentHeight }) }) }), (0, import_jsx_runtime11.jsx)("path", { d: shapePath, fill: theme.background }), (0, import_jsx_runtime11.jsx)("path", { d: shapePath, fill: node.resolvedFill, stroke: strokeColor, strokeWidth: thinStroke }), (0, import_jsx_runtime11.jsx)("path", { d: foldPath, fill: "none", stroke: strokeColor, strokeWidth: thinStroke, opacity: 0.5 }), !isEditing && !slots?.body && (0, import_jsx_runtime11.jsxs)("g", { clipPath: `url(#${clipId})`, children: [dirPath && (0, import_jsx_runtime11.jsxs)("text", { x: x + textPadding, y: contentY + 14, fill: theme.node.sublabelColor, fontSize: theme.node.sublabelFontSize - 1, fontFamily: theme.node.fontFamily, pointerEvents: "none", opacity: 0.6, children: [dirPath, "/"] }), (0, import_jsx_runtime11.jsx)("text", { x: x + textPadding, y: contentY + (dirPath ? 28 : contentHeight / 2 + (subpath ? -2 : 4)), fill: theme.node.labelColor, fontSize: theme.node.fontSize - 1, fontWeight: 500, fontFamily: theme.node.fontFamily, pointerEvents: "none", children: fileName }), subpath && (0, import_jsx_runtime11.jsx)("text", { x: x + textPadding, y: contentY + (dirPath ? 40 : contentHeight / 2 + theme.node.fontSize + 2), fill: theme.node.sublabelColor, fontSize: theme.node.sublabelFontSize, fontFamily: theme.node.fontFamily, pointerEvents: "none", children: subpath })] }), slots && (0, import_jsx_runtime11.jsx)(CategorySlotsLayer, { node, theme, canvases, slots }), node.isNavigable && (0, import_jsx_runtime11.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor, strokeWidth: thinStroke, corner: toKebabCorner(refCorner), onNavigate })] });
17672
18412
  }
17673
18413
 
17674
18414
  // ../react/dist/components/LinkNode.js
17675
- var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
17676
- function LinkNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing }) {
18415
+ var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
18416
+ function LinkNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "bottomRight" }) {
17677
18417
  const { x, y, width, height } = node;
17678
- const cx = x + width / 2;
18418
+ const contentX = x + reservedLeft;
18419
+ const contentY = y + reservedTop;
18420
+ const contentWidth = Math.max(0, width - reservedLeft - reservedRight);
18421
+ const contentHeight = Math.max(0, height - reservedTop - reservedBottom);
18422
+ const cx = contentX + contentWidth / 2;
17679
18423
  let displayUrl = node.url ?? "";
17680
18424
  try {
17681
18425
  const url = new URL(displayUrl);
17682
18426
  displayUrl = url.hostname;
17683
18427
  } catch {
17684
18428
  }
17685
- return (0, import_jsx_runtime5.jsxs)("g", { className: "system-canvas-node system-canvas-node--link", style: { cursor: onPointerDown ? "move" : "pointer" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime5.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: theme.background }), (0, import_jsx_runtime5.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: node.resolvedFill, stroke: node.resolvedStroke, strokeWidth: theme.node.strokeWidth }), !isEditing && (0, import_jsx_runtime5.jsx)("text", { x: x + 12, y: y + height / 2 + 4, fill: node.resolvedStroke, fontSize: 12, fontFamily: theme.node.fontFamily, pointerEvents: "none", opacity: 0.6, children: "\u29C9" }), !isEditing && (0, import_jsx_runtime5.jsx)("text", { x: cx, y: y + height / 2 + 4, fill: theme.node.labelColor, fontSize: theme.node.fontSize, fontWeight: 600, fontFamily: theme.node.fontFamily, textAnchor: "middle", pointerEvents: "none", textDecoration: "underline", children: displayUrl }), node.isNavigable && (0, import_jsx_runtime5.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor: node.resolvedStroke, strokeWidth: theme.node.strokeWidth, onNavigate })] });
18429
+ return (0, import_jsx_runtime12.jsxs)("g", { className: "system-canvas-node system-canvas-node--link", style: { cursor: onPointerDown ? "move" : "pointer" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime12.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: theme.background }), (0, import_jsx_runtime12.jsx)("rect", { x, y, width, height, rx: node.resolvedCornerRadius, fill: node.resolvedFill, stroke: node.resolvedStroke, strokeWidth: theme.node.strokeWidth }), !isEditing && !slots?.body && (0, import_jsx_runtime12.jsx)("text", { x: contentX + 12, y: contentY + contentHeight / 2 + 4, fill: node.resolvedStroke, fontSize: 12, fontFamily: theme.node.fontFamily, pointerEvents: "none", opacity: 0.6, children: "\u29C9" }), !isEditing && !slots?.body && (0, import_jsx_runtime12.jsx)("text", { x: cx, y: contentY + contentHeight / 2 + 4, fill: theme.node.labelColor, fontSize: theme.node.fontSize, fontWeight: 600, fontFamily: theme.node.fontFamily, textAnchor: "middle", pointerEvents: "none", textDecoration: "underline", children: displayUrl }), slots && (0, import_jsx_runtime12.jsx)(CategorySlotsLayer, { node, theme, canvases, slots }), node.isNavigable && (0, import_jsx_runtime12.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor: node.resolvedStroke, strokeWidth: theme.node.strokeWidth, corner: toKebabCorner(refCorner), onNavigate })] });
17686
18430
  }
17687
18431
 
17688
18432
  // ../react/dist/components/GroupNode.js
17689
- var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
17690
- function GroupNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing }) {
18433
+ var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
18434
+ function GroupNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "topRight" }) {
17691
18435
  const { x, y, width, height } = node;
17692
18436
  const stroke = node.color ? node.resolvedStroke : theme.group.stroke;
17693
18437
  const fill = node.color ? node.resolvedFill : theme.group.fill;
17694
- return (0, import_jsx_runtime6.jsxs)("g", { className: "system-canvas-node system-canvas-node--group", style: { cursor: onPointerDown ? "move" : node.isNavigable ? "pointer" : "default" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime6.jsx)("rect", { x, y, width, height, rx: theme.group.cornerRadius, fill, stroke, strokeWidth: theme.group.strokeWidth, strokeDasharray: theme.group.strokeDasharray }), !isEditing && node.label && (0, import_jsx_runtime6.jsx)("text", { x: x + 12, y: y + theme.group.labelFontSize + 8, fill: node.color ? stroke : theme.group.labelColor, fontSize: theme.group.labelFontSize, fontWeight: 600, fontFamily: theme.node.fontFamily, pointerEvents: "none", children: node.label }), node.isNavigable && (0, import_jsx_runtime6.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor: stroke, strokeWidth: theme.group.strokeWidth, corner: "top-right", onNavigate })] });
18438
+ const labelX = x + 12 + reservedLeft;
18439
+ const labelY = y + reservedTop + theme.group.labelFontSize + 8;
18440
+ return (0, import_jsx_runtime13.jsxs)("g", { className: "system-canvas-node system-canvas-node--group", style: { cursor: onPointerDown ? "move" : node.isNavigable ? "pointer" : "default" }, onClick: (e) => onClick(node, e), onDoubleClick: (e) => onDoubleClick(node, e), onContextMenu: (e) => onContextMenu(node, e), onPointerDown: onPointerDown ? (e) => onPointerDown(node, e) : void 0, children: [(0, import_jsx_runtime13.jsx)("rect", { x, y, width, height, rx: theme.group.cornerRadius, fill, stroke, strokeWidth: theme.group.strokeWidth, strokeDasharray: theme.group.strokeDasharray }), !isEditing && !slots?.body && node.label && (0, import_jsx_runtime13.jsx)("text", { x: labelX, y: labelY, fill: node.color ? stroke : theme.group.labelColor, fontSize: theme.group.labelFontSize, fontWeight: 600, fontFamily: theme.node.fontFamily, pointerEvents: "none", children: node.label }), slots && (0, import_jsx_runtime13.jsx)(CategorySlotsLayer, { node, theme, canvases, slots }), node.isNavigable && (0, import_jsx_runtime13.jsx)(RefIndicator, { node, theme, nodeX: x, nodeY: y, nodeWidth: width, nodeHeight: height, strokeColor: stroke, strokeWidth: theme.group.strokeWidth, corner: toKebabCorner(refCorner), onNavigate })] });
17695
18441
  }
17696
18442
 
17697
18443
  // ../react/dist/components/ResizeHandles.js
17698
- var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
17699
- var import_react9 = __toESM(require_react(), 1);
18444
+ var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
18445
+ var import_react11 = __toESM(require_react(), 1);
17700
18446
  var HANDLE_SIZE = 7;
17701
18447
  var CORNERS = [
17702
18448
  { corner: "nw", cursor: "nwse-resize", anchor: "nw" },
@@ -17707,7 +18453,7 @@ var SystemCanvas = (() => {
17707
18453
  var cornerInset = (cornerRadius) => Math.min(cornerRadius * 0.25, 3);
17708
18454
  function ResizeHandles({ node, theme, onHandlePointerDown }) {
17709
18455
  const { x, y, width, height } = node;
17710
- const [hoveredCorner, setHoveredCorner] = (0, import_react9.useState)(null);
18456
+ const [hoveredCorner, setHoveredCorner] = (0, import_react11.useState)(null);
17711
18457
  const handleColor = node.resolvedStroke ?? theme.node.labelColor;
17712
18458
  const i = cornerInset(node.resolvedCornerRadius);
17713
18459
  const anchorPos = (anchor) => {
@@ -17722,36 +18468,54 @@ var SystemCanvas = (() => {
17722
18468
  return { cx: x + width - i, cy: y + height - i };
17723
18469
  }
17724
18470
  };
17725
- return (0, import_jsx_runtime7.jsx)("g", { className: "system-canvas-resize-handles", pointerEvents: "all", children: CORNERS.map(({ corner, cursor, anchor }) => {
18471
+ return (0, import_jsx_runtime14.jsx)("g", { className: "system-canvas-resize-handles", pointerEvents: "all", children: CORNERS.map(({ corner, cursor, anchor }) => {
17726
18472
  const { cx, cy } = anchorPos(anchor);
17727
18473
  const isHovered = hoveredCorner === corner;
17728
18474
  const s = isHovered ? HANDLE_SIZE + 2 : HANDLE_SIZE;
17729
18475
  const half = s / 2;
17730
- return (0, import_jsx_runtime7.jsx)("rect", { x: cx - half, y: cy - half, width: s, height: s, fill: handleColor, style: { cursor }, onPointerEnter: () => setHoveredCorner(corner), onPointerLeave: () => setHoveredCorner((c) => c === corner ? null : c), onPointerDown: (e) => onHandlePointerDown(node, corner, e), onClick: (e) => e.stopPropagation(), onDoubleClick: (e) => e.stopPropagation() }, corner);
18476
+ return (0, import_jsx_runtime14.jsx)("rect", { x: cx - half, y: cy - half, width: s, height: s, fill: handleColor, style: { cursor }, onPointerEnter: () => setHoveredCorner(corner), onPointerLeave: () => setHoveredCorner((c) => c === corner ? null : c), onPointerDown: (e) => onHandlePointerDown(node, corner, e), onClick: (e) => e.stopPropagation(), onDoubleClick: (e) => e.stopPropagation() }, corner);
17731
18477
  }) });
17732
18478
  }
17733
18479
 
17734
18480
  // ../react/dist/components/NodeRenderer.js
17735
- function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedId, editingId, onResizeHandlePointerDown, only }) {
18481
+ function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedId, editingId, onResizeHandlePointerDown, canvases, only }) {
17736
18482
  const groups = nodes.filter((n) => n.type === "group");
17737
18483
  const others = nodes.filter((n) => n.type !== "group");
17738
- const common = (node) => ({
17739
- node,
17740
- theme,
17741
- onClick,
17742
- onDoubleClick,
17743
- onContextMenu,
17744
- onNavigate,
17745
- onPointerDown,
17746
- isSelected: selectedId === node.id,
17747
- isEditing: editingId === node.id
17748
- });
18484
+ const common = (node) => {
18485
+ const slots = getCategorySlots(node, theme);
18486
+ const reservations = computeReflowReservations(node, theme, slots);
18487
+ const defaultCorner = node.type === "group" ? "topRight" : "bottomRight";
18488
+ const refCorner = pickRefIndicatorCorner(defaultCorner, slots);
18489
+ return {
18490
+ node,
18491
+ theme,
18492
+ onClick,
18493
+ onDoubleClick,
18494
+ onContextMenu,
18495
+ onNavigate,
18496
+ onPointerDown,
18497
+ isSelected: selectedId === node.id,
18498
+ isEditing: editingId === node.id,
18499
+ slots,
18500
+ canvases,
18501
+ reservedTop: reservations.top,
18502
+ reservedBottom: reservations.bottom,
18503
+ reservedLeft: reservations.left,
18504
+ reservedRight: reservations.right,
18505
+ refCorner
18506
+ };
18507
+ };
17749
18508
  const selectedNode = selectedId && editingId !== selectedId ? nodes.find((n) => n.id === selectedId) : void 0;
17750
18509
  const renderResizeHandles = only !== "groups" && selectedNode && onResizeHandlePointerDown;
17751
- return (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [only !== "non-groups" && groups.map((node) => (0, import_jsx_runtime8.jsx)(GroupNode, { ...common(node) }, node.id)), only !== "groups" && others.map((node) => {
18510
+ return (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [only !== "non-groups" && groups.map((node) => (0, import_jsx_runtime15.jsx)(GroupNode, { ...common(node) }, node.id)), only !== "groups" && others.map((node) => {
17752
18511
  const Component = getNodeComponent(node.type);
17753
- return (0, import_jsx_runtime8.jsx)(Component, { ...common(node) }, node.id);
17754
- }), renderResizeHandles && (0, import_jsx_runtime8.jsx)(ResizeHandles, { node: selectedNode, theme, onHandlePointerDown: onResizeHandlePointerDown })] });
18512
+ return (0, import_jsx_runtime15.jsx)(Component, { ...common(node) }, node.id);
18513
+ }), renderResizeHandles && (0, import_jsx_runtime15.jsx)(ResizeHandles, { node: selectedNode, theme, onHandlePointerDown: onResizeHandlePointerDown }), renderResizeHandles && (() => {
18514
+ const slots = getCategorySlots(selectedNode, theme);
18515
+ if (!slots?.topRightOuter)
18516
+ return null;
18517
+ return (0, import_jsx_runtime15.jsx)(CategorySlotsLayer, { node: selectedNode, theme, canvases, slots: { topRightOuter: slots.topRightOuter } });
18518
+ })()] });
17755
18519
  }
17756
18520
  function getNodeComponent(type) {
17757
18521
  switch (type) {
@@ -17765,9 +18529,9 @@ var SystemCanvas = (() => {
17765
18529
  }
17766
18530
 
17767
18531
  // ../react/dist/components/EdgeRenderer.js
17768
- var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
18532
+ var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
17769
18533
  function EdgeRenderer({ edges, nodeMap, theme, defaultEdgeStyle, onClick, onDoubleClick, onContextMenu, selectedId, editingId }) {
17770
- return (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [(0, import_jsx_runtime9.jsx)("defs", { children: (0, import_jsx_runtime9.jsx)("marker", { id: "system-canvas-arrowhead", markerWidth: theme.edge.arrowSize, markerHeight: theme.edge.arrowSize * 0.7, refX: theme.edge.arrowSize - 1, refY: theme.edge.arrowSize * 0.35, orient: "auto", markerUnits: "userSpaceOnUse", children: (0, import_jsx_runtime9.jsx)("polygon", { points: `0 0, ${theme.edge.arrowSize} ${theme.edge.arrowSize * 0.35}, 0 ${theme.edge.arrowSize * 0.7}`, fill: "context-stroke" }) }) }), [...edges].map((edge, i) => ({ edge, i })).sort((a, b) => {
18534
+ return (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [(0, import_jsx_runtime16.jsx)("defs", { children: (0, import_jsx_runtime16.jsx)("marker", { id: "system-canvas-arrowhead", markerWidth: theme.edge.arrowSize, markerHeight: theme.edge.arrowSize * 0.7, refX: theme.edge.arrowSize - 1, refY: theme.edge.arrowSize * 0.35, orient: "auto", markerUnits: "userSpaceOnUse", children: (0, import_jsx_runtime16.jsx)("polygon", { points: `0 0, ${theme.edge.arrowSize} ${theme.edge.arrowSize * 0.35}, 0 ${theme.edge.arrowSize * 0.7}`, fill: "context-stroke" }) }) }), [...edges].map((edge, i) => ({ edge, i })).sort((a, b) => {
17771
18535
  const aSel = selectedId === a.edge.id ? 2 : 0;
17772
18536
  const bSel = selectedId === b.edge.id ? 2 : 0;
17773
18537
  const aCol = a.edge.color ? 1 : 0;
@@ -17792,20 +18556,36 @@ var SystemCanvas = (() => {
17792
18556
  const toEnd = edge.toEnd ?? "arrow";
17793
18557
  const fromEnd = edge.fromEnd ?? "none";
17794
18558
  const arrowId = "system-canvas-arrowhead";
17795
- return (0, import_jsx_runtime9.jsxs)("g", { className: "system-canvas-edge", style: { cursor: "pointer" }, onClick: (e) => onClick(edge, e), onDoubleClick: (e) => onDoubleClick?.(edge, e), onContextMenu: (e) => onContextMenu(edge, e), children: [(0, import_jsx_runtime9.jsx)("path", { d: pathD, fill: "none", stroke: "transparent", strokeWidth: 12 }), (0, import_jsx_runtime9.jsx)("path", { d: pathD, fill: "none", stroke: edgeColor, strokeWidth, markerEnd: toEnd === "arrow" ? `url(#${arrowId})` : void 0, markerStart: fromEnd === "arrow" ? `url(#${arrowId})` : void 0 }), edge.label && !isEditing && (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [(0, import_jsx_runtime9.jsx)("rect", { x: midpoint.x - edge.label.length * 3 - 4, y: midpoint.y - theme.edge.labelFontSize - 2, width: edge.label.length * 6 + 8, height: theme.edge.labelFontSize + 6, rx: 3, fill: theme.background, opacity: 0.8 }), (0, import_jsx_runtime9.jsx)("text", { x: midpoint.x, y: midpoint.y, fill: isSelected ? theme.node.labelColor : theme.edge.labelColor, fontSize: theme.edge.labelFontSize, fontFamily: theme.node.fontFamily, textAnchor: "middle", pointerEvents: "none", children: edge.label })] })] }, edge.id);
18559
+ return (0, import_jsx_runtime16.jsxs)("g", { className: "system-canvas-edge", style: { cursor: "pointer" }, onClick: (e) => onClick(edge, e), onDoubleClick: (e) => onDoubleClick?.(edge, e), onContextMenu: (e) => onContextMenu(edge, e), children: [(0, import_jsx_runtime16.jsx)("path", { d: pathD, fill: "none", stroke: "transparent", strokeWidth: 12 }), (0, import_jsx_runtime16.jsx)("path", { d: pathD, fill: "none", stroke: edgeColor, strokeWidth, markerEnd: toEnd === "arrow" ? `url(#${arrowId})` : void 0, markerStart: fromEnd === "arrow" ? `url(#${arrowId})` : void 0 }), edge.label && !isEditing && (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [(0, import_jsx_runtime16.jsx)("rect", { x: midpoint.x - edge.label.length * 3 - 4, y: midpoint.y - theme.edge.labelFontSize - 2, width: edge.label.length * 6 + 8, height: theme.edge.labelFontSize + 6, rx: 3, fill: theme.background, opacity: 0.8 }), (0, import_jsx_runtime16.jsx)("text", { x: midpoint.x, y: midpoint.y, fill: isSelected ? theme.node.labelColor : theme.edge.labelColor, fontSize: theme.edge.labelFontSize, fontFamily: theme.node.fontFamily, textAnchor: "middle", pointerEvents: "none", children: edge.label })] })] }, edge.id);
17796
18560
  })] });
17797
18561
  }
17798
18562
 
17799
18563
  // ../react/dist/components/NodeEditor.js
17800
- var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
17801
- var import_react10 = __toESM(require_react(), 1);
18564
+ var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
18565
+ var import_react12 = __toESM(require_react(), 1);
17802
18566
  function NodeEditor({ node, theme, onCommit, onCancel }) {
18567
+ const editableFields = useCategoryFields(node, theme);
18568
+ if (editableFields) {
18569
+ return (0, import_jsx_runtime17.jsx)(FormEditor, { node, theme, fields: editableFields, onCommit, onCancel });
18570
+ }
18571
+ return (0, import_jsx_runtime17.jsx)(SingleFieldEditor, { node, theme, onCommit, onCancel });
18572
+ }
18573
+ function useCategoryFields(node, theme) {
18574
+ if (!node.category)
18575
+ return null;
18576
+ const def = theme.categories[node.category];
18577
+ const fields = def?.editableFields;
18578
+ if (!fields || fields.length === 0)
18579
+ return null;
18580
+ return fields;
18581
+ }
18582
+ function SingleFieldEditor({ node, theme, onCommit, onCancel }) {
17803
18583
  const initial = getInitialValue(node);
17804
- const [value, setValue] = (0, import_react10.useState)(initial);
17805
- const textareaRef = (0, import_react10.useRef)(null);
17806
- const inputRef = (0, import_react10.useRef)(null);
17807
- const committedRef = (0, import_react10.useRef)(false);
17808
- (0, import_react10.useEffect)(() => {
18584
+ const [value, setValue] = (0, import_react12.useState)(initial);
18585
+ const textareaRef = (0, import_react12.useRef)(null);
18586
+ const inputRef = (0, import_react12.useRef)(null);
18587
+ const committedRef = (0, import_react12.useRef)(false);
18588
+ (0, import_react12.useEffect)(() => {
17809
18589
  const el = textareaRef.current ?? inputRef.current;
17810
18590
  if (el) {
17811
18591
  el.focus();
@@ -17821,7 +18601,7 @@ var SystemCanvas = (() => {
17821
18601
  onCancel();
17822
18602
  return;
17823
18603
  }
17824
- onCommit(buildPatch(node, value));
18604
+ onCommit(buildSingleFieldPatch(node, value));
17825
18605
  };
17826
18606
  const cancel = () => {
17827
18607
  if (committedRef.current)
@@ -17850,7 +18630,7 @@ var SystemCanvas = (() => {
17850
18630
  resize: "none",
17851
18631
  textAlign: node.type === "text" ? "center" : "left"
17852
18632
  };
17853
- return (0, import_jsx_runtime10.jsx)("foreignObject", { x: node.x, y: node.y, width: node.width, height: node.height, onPointerDown: stopPointer, onMouseDown: stopPointer, onClick: stopPointer, onDoubleClick: stopPointer, children: node.type === "text" ? (0, import_jsx_runtime10.jsx)("textarea", { ref: textareaRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
18633
+ return (0, import_jsx_runtime17.jsx)("foreignObject", { x: node.x, y: node.y, width: node.width, height: node.height, onPointerDown: stopPointer, onMouseDown: stopPointer, onClick: stopPointer, onDoubleClick: stopPointer, children: node.type === "text" ? (0, import_jsx_runtime17.jsx)("textarea", { ref: textareaRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
17854
18634
  if (e.key === "Enter" && !e.shiftKey) {
17855
18635
  e.preventDefault();
17856
18636
  commit();
@@ -17858,7 +18638,7 @@ var SystemCanvas = (() => {
17858
18638
  e.preventDefault();
17859
18639
  cancel();
17860
18640
  }
17861
- }, style: commonFieldStyle }) : (0, import_jsx_runtime10.jsx)("input", { ref: inputRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
18641
+ }, style: commonFieldStyle }) : (0, import_jsx_runtime17.jsx)("input", { ref: inputRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
17862
18642
  if (e.key === "Enter") {
17863
18643
  e.preventDefault();
17864
18644
  commit();
@@ -17882,7 +18662,7 @@ var SystemCanvas = (() => {
17882
18662
  return "";
17883
18663
  }
17884
18664
  }
17885
- function buildPatch(node, value) {
18665
+ function buildSingleFieldPatch(node, value) {
17886
18666
  switch (node.type) {
17887
18667
  case "text":
17888
18668
  return { text: value };
@@ -17896,16 +18676,176 @@ var SystemCanvas = (() => {
17896
18676
  return {};
17897
18677
  }
17898
18678
  }
18679
+ function FormEditor({ node, theme, fields, onCommit, onCancel }) {
18680
+ const initial = (0, import_react12.useMemo)(() => readInitialValues(node, fields), [node, fields]);
18681
+ const [values, setValues] = (0, import_react12.useState)(initial);
18682
+ const committedRef = (0, import_react12.useRef)(false);
18683
+ const panelRef = (0, import_react12.useRef)(null);
18684
+ const width = Math.max(node.width, 240);
18685
+ const height = Math.max(node.height, 36 + fields.length * 44);
18686
+ const commit = () => {
18687
+ if (committedRef.current)
18688
+ return;
18689
+ committedRef.current = true;
18690
+ const patch = buildFormPatch(node, fields, initial, values);
18691
+ if (!patch) {
18692
+ onCancel();
18693
+ return;
18694
+ }
18695
+ onCommit(patch);
18696
+ };
18697
+ const cancel = () => {
18698
+ if (committedRef.current)
18699
+ return;
18700
+ committedRef.current = true;
18701
+ onCancel();
18702
+ };
18703
+ const onBlurPanel = (e) => {
18704
+ const next = e.relatedTarget;
18705
+ if (next && panelRef.current?.contains(next))
18706
+ return;
18707
+ commit();
18708
+ };
18709
+ const stopPointer = (e) => {
18710
+ e.stopPropagation();
18711
+ };
18712
+ (0, import_react12.useEffect)(() => {
18713
+ const el = panelRef.current;
18714
+ if (!el)
18715
+ return;
18716
+ const first = el.querySelector("input, textarea, select, button");
18717
+ first?.focus();
18718
+ if (first instanceof HTMLInputElement && first.type === "text") {
18719
+ const end = first.value.length;
18720
+ first.setSelectionRange(end, end);
18721
+ }
18722
+ }, []);
18723
+ const fieldLabelColor = theme.node.sublabelColor;
18724
+ const inputStyle = {
18725
+ width: "100%",
18726
+ boxSizing: "border-box",
18727
+ padding: "6px 8px",
18728
+ fontFamily: theme.node.fontFamily,
18729
+ fontSize: theme.node.fontSize - 1,
18730
+ background: theme.background,
18731
+ color: theme.node.labelColor,
18732
+ border: `1px solid ${theme.breadcrumbs.separatorColor}`,
18733
+ borderRadius: 6,
18734
+ outline: "none"
18735
+ };
18736
+ return (0, import_jsx_runtime17.jsx)("foreignObject", { x: node.x, y: node.y, width, height, onPointerDown: stopPointer, onMouseDown: stopPointer, onClick: stopPointer, onDoubleClick: stopPointer, children: (0, import_jsx_runtime17.jsx)("div", { ref: panelRef, onBlur: onBlurPanel, onKeyDown: (e) => {
18737
+ if (e.key === "Escape") {
18738
+ e.preventDefault();
18739
+ cancel();
18740
+ } else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
18741
+ e.preventDefault();
18742
+ commit();
18743
+ } else if (e.key === "Enter" && !(e.target instanceof HTMLTextAreaElement)) {
18744
+ e.preventDefault();
18745
+ const panel = panelRef.current;
18746
+ if (!panel)
18747
+ return;
18748
+ const focusables = Array.from(panel.querySelectorAll("input, textarea, select"));
18749
+ const idx = focusables.indexOf(e.target);
18750
+ if (idx === -1 || idx === focusables.length - 1)
18751
+ commit();
18752
+ else
18753
+ focusables[idx + 1].focus();
18754
+ }
18755
+ }, style: {
18756
+ width: "100%",
18757
+ boxSizing: "border-box",
18758
+ display: "flex",
18759
+ flexDirection: "column",
18760
+ gap: 8,
18761
+ padding: 10,
18762
+ background: theme.breadcrumbs.background,
18763
+ color: theme.node.labelColor,
18764
+ border: `1.5px solid ${theme.node.labelColor}`,
18765
+ borderRadius: node.resolvedCornerRadius,
18766
+ boxShadow: "0 4px 12px rgba(0,0,0,0.25)",
18767
+ backdropFilter: "blur(8px)",
18768
+ fontFamily: theme.node.fontFamily
18769
+ }, children: fields.map((field) => {
18770
+ const v = values[field.path];
18771
+ const setV = (next) => setValues((prev) => ({ ...prev, [field.path]: next }));
18772
+ const label = field.label ?? field.path;
18773
+ return (0, import_jsx_runtime17.jsxs)("label", { style: {
18774
+ display: "flex",
18775
+ flexDirection: "column",
18776
+ gap: 3,
18777
+ fontSize: 10,
18778
+ color: fieldLabelColor,
18779
+ textTransform: "uppercase",
18780
+ letterSpacing: 0.5
18781
+ }, children: [(0, import_jsx_runtime17.jsx)("span", { children: label }), renderControl(field, v, setV, inputStyle, theme)] }, field.path);
18782
+ }) }) });
18783
+ }
18784
+ function renderControl(field, value, setValue, inputStyle, theme) {
18785
+ switch (field.kind) {
18786
+ case "textarea":
18787
+ return (0, import_jsx_runtime17.jsx)("textarea", { value: value ?? "", onChange: (e) => setValue(e.target.value), placeholder: field.placeholder, rows: 3, style: { ...inputStyle, resize: "vertical", minHeight: 60 } });
18788
+ case "number":
18789
+ return (0, import_jsx_runtime17.jsx)("input", { type: "number", value: value == null ? "" : String(value), min: field.min, max: field.max, step: field.step, onChange: (e) => {
18790
+ const v = e.target.value;
18791
+ setValue(v === "" ? void 0 : Number(v));
18792
+ }, placeholder: field.placeholder, style: inputStyle });
18793
+ case "select":
18794
+ return (0, import_jsx_runtime17.jsxs)("select", { value: value ?? "", onChange: (e) => setValue(e.target.value), style: inputStyle, children: [!field.options?.some((o) => o.value === value) && (0, import_jsx_runtime17.jsx)("option", { value: "", children: "\u2014" }), field.options?.map((o) => (0, import_jsx_runtime17.jsx)("option", { value: o.value, children: o.label ?? o.value }, o.value))] });
18795
+ case "boolean":
18796
+ return (0, import_jsx_runtime17.jsx)("input", { type: "checkbox", checked: !!value, onChange: (e) => setValue(e.target.checked), style: {
18797
+ alignSelf: "flex-start",
18798
+ width: 16,
18799
+ height: 16,
18800
+ accentColor: theme.node.labelColor
18801
+ } });
18802
+ case "text":
18803
+ default:
18804
+ return (0, import_jsx_runtime17.jsx)("input", { type: "text", value: value ?? "", onChange: (e) => setValue(e.target.value), placeholder: field.placeholder, style: inputStyle });
18805
+ }
18806
+ }
18807
+ function readInitialValues(node, fields) {
18808
+ const out = {};
18809
+ for (const f of fields) {
18810
+ out[f.path] = getAtPath(node, f.path);
18811
+ }
18812
+ return out;
18813
+ }
18814
+ function buildFormPatch(node, fields, initial, current) {
18815
+ let changed = false;
18816
+ for (const f of fields) {
18817
+ if (current[f.path] !== initial[f.path]) {
18818
+ changed = true;
18819
+ break;
18820
+ }
18821
+ }
18822
+ if (!changed)
18823
+ return null;
18824
+ let working = { ...node };
18825
+ for (const f of fields) {
18826
+ working = setAtPath(working, f.path, current[f.path]);
18827
+ }
18828
+ const patch = {};
18829
+ const topKeys = /* @__PURE__ */ new Set();
18830
+ for (const f of fields) {
18831
+ topKeys.add(f.path.split(".")[0]);
18832
+ }
18833
+ for (const k of topKeys) {
18834
+ ;
18835
+ patch[k] = working[k];
18836
+ }
18837
+ return patch;
18838
+ }
17899
18839
 
17900
18840
  // ../react/dist/components/EdgeLabelEditor.js
17901
- var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
17902
- var import_react11 = __toESM(require_react(), 1);
18841
+ var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
18842
+ var import_react13 = __toESM(require_react(), 1);
17903
18843
  var EDITOR_WIDTH = 110;
17904
18844
  function EdgeLabelEditor({ initialLabel, midpoint, theme, onCommit, onCancel }) {
17905
- const [value, setValue] = (0, import_react11.useState)(initialLabel);
17906
- const inputRef = (0, import_react11.useRef)(null);
17907
- const committedRef = (0, import_react11.useRef)(false);
17908
- (0, import_react11.useEffect)(() => {
18845
+ const [value, setValue] = (0, import_react13.useState)(initialLabel);
18846
+ const inputRef = (0, import_react13.useRef)(null);
18847
+ const committedRef = (0, import_react13.useRef)(false);
18848
+ (0, import_react13.useEffect)(() => {
17909
18849
  const el = inputRef.current;
17910
18850
  if (el) {
17911
18851
  el.focus();
@@ -17934,7 +18874,7 @@ var SystemCanvas = (() => {
17934
18874
  };
17935
18875
  const fontSize = theme.edge.labelFontSize;
17936
18876
  const height = fontSize + 14;
17937
- return (0, import_jsx_runtime11.jsx)("foreignObject", { x: midpoint.x - EDITOR_WIDTH / 2, y: midpoint.y - height / 2, width: EDITOR_WIDTH, height, onPointerDown: stopPointer, onMouseDown: stopPointer, onClick: stopPointer, onDoubleClick: stopPointer, children: (0, import_jsx_runtime11.jsx)("input", { ref: inputRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
18877
+ return (0, import_jsx_runtime18.jsx)("foreignObject", { x: midpoint.x - EDITOR_WIDTH / 2, y: midpoint.y - height / 2, width: EDITOR_WIDTH, height, onPointerDown: stopPointer, onMouseDown: stopPointer, onClick: stopPointer, onDoubleClick: stopPointer, children: (0, import_jsx_runtime18.jsx)("input", { ref: inputRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
17938
18878
  if (e.key === "Enter") {
17939
18879
  e.preventDefault();
17940
18880
  commit();
@@ -17959,8 +18899,8 @@ var SystemCanvas = (() => {
17959
18899
  }
17960
18900
 
17961
18901
  // ../react/dist/components/ConnectionHandles.js
17962
- var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
17963
- var import_react12 = __toESM(require_react(), 1);
18902
+ var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
18903
+ var import_react14 = __toESM(require_react(), 1);
17964
18904
  var SIDES = ["top", "right", "bottom", "left"];
17965
18905
  var HANDLE_RADIUS = 4;
17966
18906
  var HANDLE_HIT_RADIUS = 10;
@@ -17969,9 +18909,9 @@ var SystemCanvas = (() => {
17969
18909
  var HOVER_SCALE = 1.42;
17970
18910
  var HOVER_TRANSITION_MS = 120;
17971
18911
  function ConnectionHandles({ node, theme, onHandlePointerDown, immediate, activeSide }) {
17972
- const [visible, setVisible] = (0, import_react12.useState)(!!immediate);
17973
- const [hoveredSide, setHoveredSide] = (0, import_react12.useState)(null);
17974
- (0, import_react12.useEffect)(() => {
18912
+ const [visible, setVisible] = (0, import_react14.useState)(!!immediate);
18913
+ const [hoveredSide, setHoveredSide] = (0, import_react14.useState)(null);
18914
+ (0, import_react14.useEffect)(() => {
17975
18915
  if (immediate) {
17976
18916
  setVisible(true);
17977
18917
  return;
@@ -17982,16 +18922,16 @@ var SystemCanvas = (() => {
17982
18922
  }, [node.id, immediate]);
17983
18923
  const handleColor = node.resolvedStroke ?? theme.node.labelColor;
17984
18924
  const sidesToRender = immediate ? SIDES : activeSide ? [activeSide] : [];
17985
- return (0, import_jsx_runtime12.jsx)("g", { className: "system-canvas-connection-handles", pointerEvents: "auto", style: {
18925
+ return (0, import_jsx_runtime19.jsx)("g", { className: "system-canvas-connection-handles", pointerEvents: "auto", style: {
17986
18926
  opacity: visible ? 1 : 0,
17987
18927
  transition: `opacity ${FADE_DURATION_MS}ms ease-out`
17988
18928
  }, children: sidesToRender.map((side) => {
17989
18929
  const { x, y } = computeAnchorPoint(node, side);
17990
18930
  const isHovered = hoveredSide === side;
17991
- return (0, import_jsx_runtime12.jsxs)("g", { style: { cursor: "crosshair" }, onPointerEnter: () => setHoveredSide(side), onPointerLeave: () => setHoveredSide((s) => s === side ? null : s), onPointerDown: (e) => {
18931
+ return (0, import_jsx_runtime19.jsxs)("g", { style: { cursor: "crosshair" }, onPointerEnter: () => setHoveredSide(side), onPointerLeave: () => setHoveredSide((s) => s === side ? null : s), onPointerDown: (e) => {
17992
18932
  e.stopPropagation();
17993
18933
  onHandlePointerDown(node, side, e);
17994
- }, children: [(0, import_jsx_runtime12.jsx)("circle", { cx: x, cy: y, r: HANDLE_HIT_RADIUS, fill: "transparent" }), (0, import_jsx_runtime12.jsx)("circle", { cx: x, cy: y, r: HANDLE_RADIUS, fill: handleColor, pointerEvents: "none", style: {
18934
+ }, children: [(0, import_jsx_runtime19.jsx)("circle", { cx: x, cy: y, r: HANDLE_HIT_RADIUS, fill: "transparent" }), (0, import_jsx_runtime19.jsx)("circle", { cx: x, cy: y, r: HANDLE_RADIUS, fill: handleColor, pointerEvents: "none", style: {
17995
18935
  // Scale around the handle's own center. CSS transforms on
17996
18936
  // SVG elements use the element's user-space origin by
17997
18937
  // default, so we set transform-origin explicitly.
@@ -18003,7 +18943,7 @@ var SystemCanvas = (() => {
18003
18943
  }
18004
18944
 
18005
18945
  // ../react/dist/components/PendingEdgeRenderer.js
18006
- var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
18946
+ var import_jsx_runtime20 = __toESM(require_jsx_runtime(), 1);
18007
18947
  function PendingEdgeRenderer({ sourceNode, sourceSide, cursor, targetNode, theme, defaultEdgeStyle }) {
18008
18948
  const endNode = targetNode ?? {
18009
18949
  id: "__pending__",
@@ -18025,11 +18965,11 @@ var SystemCanvas = (() => {
18025
18965
  toNode: endNode.id
18026
18966
  };
18027
18967
  const pathD = computeEdgePath(pendingEdge, sourceNode, endNode, defaultEdgeStyle);
18028
- return (0, import_jsx_runtime13.jsxs)("g", { className: "system-canvas-pending-edge", pointerEvents: "none", children: [(0, import_jsx_runtime13.jsx)("path", { d: pathD, fill: "none", stroke: theme.node.labelColor, strokeWidth: theme.edge.strokeWidth * 1.25, strokeDasharray: "5,4", opacity: 0.85 }), !targetNode && (0, import_jsx_runtime13.jsx)("circle", { cx: cursor.x, cy: cursor.y, r: 4, fill: theme.background, stroke: theme.node.labelColor, strokeWidth: 1.5 })] });
18968
+ return (0, import_jsx_runtime20.jsxs)("g", { className: "system-canvas-pending-edge", pointerEvents: "none", children: [(0, import_jsx_runtime20.jsx)("path", { d: pathD, fill: "none", stroke: theme.node.labelColor, strokeWidth: theme.edge.strokeWidth * 1.25, strokeDasharray: "5,4", opacity: 0.85 }), !targetNode && (0, import_jsx_runtime20.jsx)("circle", { cx: cursor.x, cy: cursor.y, r: 4, fill: theme.background, stroke: theme.node.labelColor, strokeWidth: 1.5 })] });
18029
18969
  }
18030
18970
 
18031
18971
  // ../react/dist/components/LanesBackground.js
18032
- var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
18972
+ var import_jsx_runtime21 = __toESM(require_jsx_runtime(), 1);
18033
18973
  var LANE_EXTENT = 1e5;
18034
18974
  function LanesBackground({ columns, rows, theme }) {
18035
18975
  const hasColumns = columns && columns.length > 0;
@@ -18044,31 +18984,31 @@ var SystemCanvas = (() => {
18044
18984
  }
18045
18985
  return index % 2 === 0 ? lanesTheme.bandFillEven : lanesTheme.bandFillOdd;
18046
18986
  };
18047
- return (0, import_jsx_runtime14.jsxs)("g", { className: "system-canvas-lanes", pointerEvents: "none", children: [hasRows && rows.map((row, i) => (0, import_jsx_runtime14.jsx)("rect", { x: -LANE_EXTENT, y: row.start, width: LANE_EXTENT * 2, height: row.size, fill: fillForLane(row, i) }, `row-${row.id}`)), hasColumns && columns.map((col, i) => (0, import_jsx_runtime14.jsx)("rect", { x: col.start, y: -LANE_EXTENT, width: col.size, height: LANE_EXTENT * 2, fill: fillForLane(col, i) }, `col-${col.id}`)), hasRows && rows.map((row, i) => {
18987
+ return (0, import_jsx_runtime21.jsxs)("g", { className: "system-canvas-lanes", pointerEvents: "none", children: [hasRows && rows.map((row, i) => (0, import_jsx_runtime21.jsx)("rect", { x: -LANE_EXTENT, y: row.start, width: LANE_EXTENT * 2, height: row.size, fill: fillForLane(row, i) }, `row-${row.id}`)), hasColumns && columns.map((col, i) => (0, import_jsx_runtime21.jsx)("rect", { x: col.start, y: -LANE_EXTENT, width: col.size, height: LANE_EXTENT * 2, fill: fillForLane(col, i) }, `col-${col.id}`)), hasRows && rows.map((row, i) => {
18048
18988
  if (i === 0)
18049
18989
  return null;
18050
- return (0, import_jsx_runtime14.jsx)("line", { x1: -LANE_EXTENT, y1: row.start, x2: LANE_EXTENT, y2: row.start, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth, vectorEffect: "non-scaling-stroke" }, `rowdiv-${row.id}`);
18990
+ return (0, import_jsx_runtime21.jsx)("line", { x1: -LANE_EXTENT, y1: row.start, x2: LANE_EXTENT, y2: row.start, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth, vectorEffect: "non-scaling-stroke" }, `rowdiv-${row.id}`);
18051
18991
  }), hasColumns && columns.map((col, i) => {
18052
18992
  if (i === 0)
18053
18993
  return null;
18054
- return (0, import_jsx_runtime14.jsx)("line", { x1: col.start, y1: -LANE_EXTENT, x2: col.start, y2: LANE_EXTENT, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth, vectorEffect: "non-scaling-stroke" }, `coldiv-${col.id}`);
18994
+ return (0, import_jsx_runtime21.jsx)("line", { x1: col.start, y1: -LANE_EXTENT, x2: col.start, y2: LANE_EXTENT, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth, vectorEffect: "non-scaling-stroke" }, `coldiv-${col.id}`);
18055
18995
  })] });
18056
18996
  }
18057
18997
 
18058
18998
  // ../react/dist/components/Viewport.js
18059
18999
  var HOVER_PADDING = 10;
18060
19000
  var EDGE_PROXIMITY = 16;
18061
- var Viewport = (0, import_react13.forwardRef)(function Viewport2({ nodes, edges, nodeMap, theme, edgeStyle, columns, rows, minZoom, maxZoom, defaultViewport, onViewportChange, onNodeClick, onNodeDoubleClick, onNodeNavigate, onEdgeClick, onEdgeDoubleClick, onCanvasClick, onCanvasContextMenu, onNodeContextMenu, onEdgeContextMenu, onNodePointerDown, selectedId, editingId, selectedEdgeId, editingEdgeId, dragOverrides, resizeOverrides, onResizeHandlePointerDown, onEditorCommit, onEditorCancel, onEdgeEditorCommit, onEdgeEditorCancel, pendingEdge, onConnectionHandlePointerDown, edgeCreateEnabled, autoFit = "canvas-change", canvasRef, handoffTransform, onHandoffApplied, handoffFadeMs = 0 }, ref) {
19001
+ var Viewport = (0, import_react15.forwardRef)(function Viewport2({ nodes, edges, nodeMap, theme, edgeStyle, columns, rows, canvases, minZoom, maxZoom, defaultViewport, onViewportChange, onNodeClick, onNodeDoubleClick, onNodeNavigate, onEdgeClick, onEdgeDoubleClick, onCanvasClick, onCanvasContextMenu, onNodeContextMenu, onEdgeContextMenu, onNodePointerDown, selectedId, editingId, selectedEdgeId, editingEdgeId, dragOverrides, resizeOverrides, onResizeHandlePointerDown, onEditorCommit, onEditorCancel, onEdgeEditorCommit, onEdgeEditorCancel, pendingEdge, onConnectionHandlePointerDown, edgeCreateEnabled, autoFit = "canvas-change", canvasRef, handoffTransform, onHandoffApplied, handoffFadeMs = 0 }, ref) {
18062
19002
  const { svgRef, groupRef, viewport, fitToContent, zoomToNode, setTransform } = useViewport({
18063
19003
  minZoom,
18064
19004
  maxZoom,
18065
19005
  defaultViewport,
18066
19006
  onViewportChange
18067
19007
  });
18068
- const navigatingRef = (0, import_react13.useRef)(false);
18069
- const fadeRafRef = (0, import_react13.useRef)(null);
18070
- const fadeTimeoutRef = (0, import_react13.useRef)(null);
18071
- const triggerFade = (0, import_react13.useCallback)((durationMs) => {
19008
+ const navigatingRef = (0, import_react15.useRef)(false);
19009
+ const fadeRafRef = (0, import_react15.useRef)(null);
19010
+ const fadeTimeoutRef = (0, import_react15.useRef)(null);
19011
+ const triggerFade = (0, import_react15.useCallback)((durationMs) => {
18072
19012
  if (durationMs <= 0)
18073
19013
  return;
18074
19014
  const g = groupRef.current;
@@ -18091,7 +19031,7 @@ var SystemCanvas = (() => {
18091
19031
  }, durationMs + 16);
18092
19032
  });
18093
19033
  }, []);
18094
- (0, import_react13.useEffect)(() => {
19034
+ (0, import_react15.useEffect)(() => {
18095
19035
  return () => {
18096
19036
  if (fadeRafRef.current !== null)
18097
19037
  cancelAnimationFrame(fadeRafRef.current);
@@ -18099,9 +19039,9 @@ var SystemCanvas = (() => {
18099
19039
  clearTimeout(fadeTimeoutRef.current);
18100
19040
  };
18101
19041
  }, []);
18102
- const [hoveredNodeId, setHoveredNodeId] = (0, import_react13.useState)(null);
18103
- const [hoveredSide, setHoveredSide] = (0, import_react13.useState)(null);
18104
- (0, import_react13.useImperativeHandle)(ref, () => ({
19042
+ const [hoveredNodeId, setHoveredNodeId] = (0, import_react15.useState)(null);
19043
+ const [hoveredSide, setHoveredSide] = (0, import_react15.useState)(null);
19044
+ (0, import_react15.useImperativeHandle)(ref, () => ({
18105
19045
  zoomToNode: (node, onComplete, options) => {
18106
19046
  navigatingRef.current = true;
18107
19047
  zoomToNode(node, onComplete, options);
@@ -18111,7 +19051,7 @@ var SystemCanvas = (() => {
18111
19051
  getSvgElement: () => svgRef.current,
18112
19052
  getViewport: () => viewport.current ?? { x: 0, y: 0, zoom: 1 }
18113
19053
  }));
18114
- const renderNodes = (0, import_react13.useMemo)(() => {
19054
+ const renderNodes = (0, import_react15.useMemo)(() => {
18115
19055
  const hasDrag = dragOverrides && dragOverrides.size > 0;
18116
19056
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
18117
19057
  if (!hasDrag && !hasResize)
@@ -18124,7 +19064,7 @@ var SystemCanvas = (() => {
18124
19064
  return d ? { ...n, x: d.x, y: d.y } : n;
18125
19065
  });
18126
19066
  }, [nodes, dragOverrides, resizeOverrides]);
18127
- const renderNodeMap = (0, import_react13.useMemo)(() => {
19067
+ const renderNodeMap = (0, import_react15.useMemo)(() => {
18128
19068
  const hasDrag = dragOverrides && dragOverrides.size > 0;
18129
19069
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
18130
19070
  if (!hasDrag && !hasResize)
@@ -18135,11 +19075,11 @@ var SystemCanvas = (() => {
18135
19075
  }
18136
19076
  return m;
18137
19077
  }, [renderNodes, nodeMap, dragOverrides, resizeOverrides]);
18138
- const latestNodesRef = (0, import_react13.useRef)(nodes);
18139
- (0, import_react13.useEffect)(() => {
19078
+ const latestNodesRef = (0, import_react15.useRef)(nodes);
19079
+ (0, import_react15.useEffect)(() => {
18140
19080
  latestNodesRef.current = nodes;
18141
19081
  }, [nodes]);
18142
- const fitNow = (0, import_react13.useCallback)(() => {
19082
+ const fitNow = (0, import_react15.useCallback)(() => {
18143
19083
  const current = latestNodesRef.current;
18144
19084
  if (current.length === 0)
18145
19085
  return;
@@ -18149,7 +19089,7 @@ var SystemCanvas = (() => {
18149
19089
  fitToContent(current, animate);
18150
19090
  });
18151
19091
  }, [fitToContent]);
18152
- (0, import_react13.useEffect)(() => {
19092
+ (0, import_react15.useEffect)(() => {
18153
19093
  if (defaultViewport)
18154
19094
  return;
18155
19095
  if (autoFit !== "always")
@@ -18158,8 +19098,8 @@ var SystemCanvas = (() => {
18158
19098
  return;
18159
19099
  fitNow();
18160
19100
  }, [nodes, autoFit, defaultViewport, fitNow]);
18161
- const fittedForRef = (0, import_react13.useRef)(null);
18162
- (0, import_react13.useEffect)(() => {
19101
+ const fittedForRef = (0, import_react15.useRef)(null);
19102
+ (0, import_react15.useEffect)(() => {
18163
19103
  if (defaultViewport)
18164
19104
  return;
18165
19105
  if (autoFit !== "canvas-change" && autoFit !== "initial")
@@ -18191,7 +19131,7 @@ var SystemCanvas = (() => {
18191
19131
  triggerFade
18192
19132
  ]);
18193
19133
  const editingNode = editingId ? renderNodes.find((n) => n.id === editingId) ?? null : null;
18194
- const handleSvgPointerMove = (0, import_react13.useCallback)((event) => {
19134
+ const handleSvgPointerMove = (0, import_react15.useCallback)((event) => {
18195
19135
  if (!edgeCreateEnabled)
18196
19136
  return;
18197
19137
  const svg = svgRef.current;
@@ -18240,7 +19180,7 @@ var SystemCanvas = (() => {
18240
19180
  setHoveredSide((prev) => prev === null ? prev : null);
18241
19181
  }
18242
19182
  }, [edgeCreateEnabled, renderNodes, svgRef, viewport]);
18243
- const handleSvgPointerLeave = (0, import_react13.useCallback)(() => {
19183
+ const handleSvgPointerLeave = (0, import_react15.useCallback)(() => {
18244
19184
  setHoveredNodeId(null);
18245
19185
  setHoveredSide(null);
18246
19186
  }, []);
@@ -18258,7 +19198,7 @@ var SystemCanvas = (() => {
18258
19198
  return null;
18259
19199
  return computeEdgeMidpoint(editingEdge, from, to);
18260
19200
  })();
18261
- return (0, import_jsx_runtime15.jsxs)("svg", { ref: svgRef, className: "system-canvas-viewport", style: {
19201
+ return (0, import_jsx_runtime22.jsxs)("svg", { ref: svgRef, className: "system-canvas-viewport", style: {
18262
19202
  width: "100%",
18263
19203
  height: "100%",
18264
19204
  display: "block",
@@ -18267,16 +19207,16 @@ var SystemCanvas = (() => {
18267
19207
  WebkitUserSelect: "none",
18268
19208
  MozUserSelect: "none",
18269
19209
  msUserSelect: "none"
18270
- }, onClick: onCanvasClick, onContextMenu: onCanvasContextMenu, onPointerMove: handleSvgPointerMove, onPointerLeave: handleSvgPointerLeave, children: [(0, import_jsx_runtime15.jsx)("defs", { children: (0, import_jsx_runtime15.jsx)("pattern", { id: "system-canvas-grid", width: theme.grid.size, height: theme.grid.size, patternUnits: "userSpaceOnUse", children: (0, import_jsx_runtime15.jsx)("path", { d: `M ${theme.grid.size} 0 L 0 0 0 ${theme.grid.size}`, fill: "none", stroke: theme.grid.color, strokeWidth: theme.grid.strokeWidth }) }) }), (0, import_jsx_runtime15.jsx)("rect", { x: "-50000", y: "-50000", width: "100000", height: "100000", fill: "url(#system-canvas-grid)" }), (0, import_jsx_runtime15.jsxs)("g", { ref: groupRef, children: [(0, import_jsx_runtime15.jsx)(LanesBackground, { columns, rows, theme }), (0, import_jsx_runtime15.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedId, editingId, only: "groups" }), (0, import_jsx_runtime15.jsx)(EdgeRenderer, { edges, nodeMap: renderNodeMap, theme, defaultEdgeStyle: edgeStyle, onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, selectedId: selectedEdgeId, editingId: editingEdgeId }), (0, import_jsx_runtime15.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedId, editingId, onResizeHandlePointerDown, only: "non-groups" }), pendingTargetNode && (0, import_jsx_runtime15.jsx)("rect", { className: "system-canvas-drop-target", x: pendingTargetNode.x - 4, y: pendingTargetNode.y - 4, width: pendingTargetNode.width + 8, height: pendingTargetNode.height + 8, rx: pendingTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, opacity: 0.85, pointerEvents: "none" }), pendingEdge && pendingSourceNode && (0, import_jsx_runtime15.jsx)(PendingEdgeRenderer, { sourceNode: pendingSourceNode, sourceSide: pendingEdge.sourceSide, cursor: pendingEdge.cursor, targetNode: pendingTargetNode, theme, defaultEdgeStyle: edgeStyle }), handlesNode && onConnectionHandlePointerDown && (0, import_jsx_runtime15.jsx)(ConnectionHandles, { node: handlesNode, theme, onHandlePointerDown: onConnectionHandlePointerDown, immediate: !!pendingEdge, activeSide: hoveredSide }), editingNode && onEditorCommit && onEditorCancel && (0, import_jsx_runtime15.jsx)(NodeEditor, { node: editingNode, theme, onCommit: onEditorCommit, onCancel: onEditorCancel }), editingEdge && editingEdgeMidpoint && onEdgeEditorCommit && onEdgeEditorCancel && (0, import_jsx_runtime15.jsx)(EdgeLabelEditor, { initialLabel: editingEdge.label ?? "", midpoint: editingEdgeMidpoint, theme, onCommit: onEdgeEditorCommit, onCancel: onEdgeEditorCancel })] })] });
19210
+ }, onClick: onCanvasClick, onContextMenu: onCanvasContextMenu, onPointerMove: handleSvgPointerMove, onPointerLeave: handleSvgPointerLeave, children: [(0, import_jsx_runtime22.jsx)("defs", { children: (0, import_jsx_runtime22.jsx)("pattern", { id: "system-canvas-grid", width: theme.grid.size, height: theme.grid.size, patternUnits: "userSpaceOnUse", children: (0, import_jsx_runtime22.jsx)("path", { d: `M ${theme.grid.size} 0 L 0 0 0 ${theme.grid.size}`, fill: "none", stroke: theme.grid.color, strokeWidth: theme.grid.strokeWidth }) }) }), (0, import_jsx_runtime22.jsx)("rect", { x: "-50000", y: "-50000", width: "100000", height: "100000", fill: "url(#system-canvas-grid)" }), (0, import_jsx_runtime22.jsxs)("g", { ref: groupRef, children: [(0, import_jsx_runtime22.jsx)(LanesBackground, { columns, rows, theme }), (0, import_jsx_runtime22.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedId, editingId, canvases, only: "groups" }), (0, import_jsx_runtime22.jsx)(EdgeRenderer, { edges, nodeMap: renderNodeMap, theme, defaultEdgeStyle: edgeStyle, onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, selectedId: selectedEdgeId, editingId: editingEdgeId }), (0, import_jsx_runtime22.jsx)(NodeRenderer, { nodes: renderNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedId, editingId, onResizeHandlePointerDown, canvases, only: "non-groups" }), pendingTargetNode && (0, import_jsx_runtime22.jsx)("rect", { className: "system-canvas-drop-target", x: pendingTargetNode.x - 4, y: pendingTargetNode.y - 4, width: pendingTargetNode.width + 8, height: pendingTargetNode.height + 8, rx: pendingTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, opacity: 0.85, pointerEvents: "none" }), pendingEdge && pendingSourceNode && (0, import_jsx_runtime22.jsx)(PendingEdgeRenderer, { sourceNode: pendingSourceNode, sourceSide: pendingEdge.sourceSide, cursor: pendingEdge.cursor, targetNode: pendingTargetNode, theme, defaultEdgeStyle: edgeStyle }), handlesNode && onConnectionHandlePointerDown && (0, import_jsx_runtime22.jsx)(ConnectionHandles, { node: handlesNode, theme, onHandlePointerDown: onConnectionHandlePointerDown, immediate: !!pendingEdge, activeSide: hoveredSide }), editingNode && onEditorCommit && onEditorCancel && (0, import_jsx_runtime22.jsx)(NodeEditor, { node: editingNode, theme, onCommit: onEditorCommit, onCancel: onEditorCancel }), editingEdge && editingEdgeMidpoint && onEdgeEditorCommit && onEdgeEditorCancel && (0, import_jsx_runtime22.jsx)(EdgeLabelEditor, { initialLabel: editingEdge.label ?? "", midpoint: editingEdgeMidpoint, theme, onCommit: onEdgeEditorCommit, onCancel: onEdgeEditorCancel })] })] });
18271
19211
  });
18272
19212
 
18273
19213
  // ../react/dist/components/Breadcrumbs.js
18274
- var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
18275
- var import_react14 = __toESM(require_react(), 1);
19214
+ var import_jsx_runtime23 = __toESM(require_jsx_runtime(), 1);
19215
+ var import_react16 = __toESM(require_react(), 1);
18276
19216
  function Breadcrumbs({ breadcrumbs, theme, onNavigate }) {
18277
19217
  if (breadcrumbs.length <= 1)
18278
19218
  return null;
18279
- return (0, import_jsx_runtime16.jsx)("div", { className: "system-canvas-breadcrumbs", style: {
19219
+ return (0, import_jsx_runtime23.jsx)("div", { className: "system-canvas-breadcrumbs", style: {
18280
19220
  position: "absolute",
18281
19221
  top: 12,
18282
19222
  left: 12,
@@ -18293,10 +19233,10 @@ var SystemCanvas = (() => {
18293
19233
  backdropFilter: "blur(8px)"
18294
19234
  }, children: breadcrumbs.map((crumb, index) => {
18295
19235
  const isLast = index === breadcrumbs.length - 1;
18296
- return (0, import_jsx_runtime16.jsxs)(import_react14.default.Fragment, { children: [index > 0 && (0, import_jsx_runtime16.jsx)("span", { style: {
19236
+ return (0, import_jsx_runtime23.jsxs)(import_react16.default.Fragment, { children: [index > 0 && (0, import_jsx_runtime23.jsx)("span", { style: {
18297
19237
  color: theme.separatorColor,
18298
19238
  margin: "0 2px"
18299
- }, children: "/" }), (0, import_jsx_runtime16.jsx)("span", { onClick: isLast ? void 0 : () => onNavigate(index), style: {
19239
+ }, children: "/" }), (0, import_jsx_runtime23.jsx)("span", { onClick: isLast ? void 0 : () => onNavigate(index), style: {
18300
19240
  color: isLast ? theme.activeColor : theme.textColor,
18301
19241
  cursor: isLast ? "default" : "pointer",
18302
19242
  fontWeight: isLast ? 600 : 400,
@@ -18316,12 +19256,12 @@ var SystemCanvas = (() => {
18316
19256
  }
18317
19257
 
18318
19258
  // ../react/dist/components/AddNodeButton.js
18319
- var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
18320
- var import_react15 = __toESM(require_react(), 1);
19259
+ var import_jsx_runtime24 = __toESM(require_jsx_runtime(), 1);
19260
+ var import_react17 = __toESM(require_react(), 1);
18321
19261
  function AddNodeButton({ options, addNode: addNode2, theme }) {
18322
- const [open, setOpen] = (0, import_react15.useState)(false);
18323
- const rootRef = (0, import_react15.useRef)(null);
18324
- (0, import_react15.useEffect)(() => {
19262
+ const [open, setOpen] = (0, import_react17.useState)(false);
19263
+ const rootRef = (0, import_react17.useRef)(null);
19264
+ (0, import_react17.useEffect)(() => {
18325
19265
  if (!open)
18326
19266
  return;
18327
19267
  function onDocClick(e) {
@@ -18344,7 +19284,7 @@ var SystemCanvas = (() => {
18344
19284
  }, [open]);
18345
19285
  const categoryOptions = options.filter((o) => o.kind === "category");
18346
19286
  const typeOptions = options.filter((o) => o.kind === "type");
18347
- return (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "system-canvas-add-node", style: {
19287
+ return (0, import_jsx_runtime24.jsxs)("div", { ref: rootRef, className: "system-canvas-add-node", style: {
18348
19288
  position: "absolute",
18349
19289
  bottom: 16,
18350
19290
  right: 16,
@@ -18352,7 +19292,7 @@ var SystemCanvas = (() => {
18352
19292
  fontFamily: theme.breadcrumbs.fontFamily,
18353
19293
  fontSize: theme.breadcrumbs.fontSize,
18354
19294
  userSelect: "none"
18355
- }, children: [open && (0, import_jsx_runtime17.jsxs)("div", { style: {
19295
+ }, children: [open && (0, import_jsx_runtime24.jsxs)("div", { style: {
18356
19296
  position: "absolute",
18357
19297
  bottom: 52,
18358
19298
  right: 0,
@@ -18365,13 +19305,13 @@ var SystemCanvas = (() => {
18365
19305
  borderRadius: 10,
18366
19306
  boxShadow: "0 8px 24px rgba(0,0,0,0.35)",
18367
19307
  backdropFilter: "blur(10px)"
18368
- }, children: [categoryOptions.length > 0 && (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [(0, import_jsx_runtime17.jsx)(SectionLabel, { theme, children: "Categories" }), categoryOptions.map((opt) => (0, import_jsx_runtime17.jsx)(MenuRow, { theme, option: opt, onClick: () => {
19308
+ }, children: [categoryOptions.length > 0 && (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [(0, import_jsx_runtime24.jsx)(SectionLabel, { theme, children: "Categories" }), categoryOptions.map((opt) => (0, import_jsx_runtime24.jsx)(MenuRow, { theme, option: opt, onClick: () => {
18369
19309
  addNode2(opt);
18370
19310
  setOpen(false);
18371
- } }, `cat-${opt.value}`)), (0, import_jsx_runtime17.jsx)(Divider, { theme })] }), (0, import_jsx_runtime17.jsx)(SectionLabel, { theme, children: "Basic" }), typeOptions.map((opt) => (0, import_jsx_runtime17.jsx)(MenuRow, { theme, option: opt, onClick: () => {
19311
+ } }, `cat-${opt.value}`)), (0, import_jsx_runtime24.jsx)(Divider, { theme })] }), (0, import_jsx_runtime24.jsx)(SectionLabel, { theme, children: "Basic" }), typeOptions.map((opt) => (0, import_jsx_runtime24.jsx)(MenuRow, { theme, option: opt, onClick: () => {
18372
19312
  addNode2(opt);
18373
19313
  setOpen(false);
18374
- } }, `type-${opt.value}`))] }), (0, import_jsx_runtime17.jsx)("button", { type: "button", "aria-label": open ? "Close add node menu" : "Add node", onClick: () => setOpen((v) => !v), style: {
19314
+ } }, `type-${opt.value}`))] }), (0, import_jsx_runtime24.jsx)("button", { type: "button", "aria-label": open ? "Close add node menu" : "Add node", onClick: () => setOpen((v) => !v), style: {
18375
19315
  width: 44,
18376
19316
  height: 44,
18377
19317
  borderRadius: 22,
@@ -18392,7 +19332,7 @@ var SystemCanvas = (() => {
18392
19332
  }, children: "+" })] });
18393
19333
  }
18394
19334
  function SectionLabel({ theme, children: children2 }) {
18395
- return (0, import_jsx_runtime17.jsx)("div", { style: {
19335
+ return (0, import_jsx_runtime24.jsx)("div", { style: {
18396
19336
  padding: "4px 8px",
18397
19337
  fontSize: theme.breadcrumbs.fontSize - 2,
18398
19338
  color: theme.breadcrumbs.textColor,
@@ -18402,7 +19342,7 @@ var SystemCanvas = (() => {
18402
19342
  }, children: children2 });
18403
19343
  }
18404
19344
  function Divider({ theme }) {
18405
- return (0, import_jsx_runtime17.jsx)("div", { style: {
19345
+ return (0, import_jsx_runtime24.jsx)("div", { style: {
18406
19346
  height: 1,
18407
19347
  margin: "4px 0",
18408
19348
  background: theme.breadcrumbs.separatorColor,
@@ -18410,9 +19350,9 @@ var SystemCanvas = (() => {
18410
19350
  } });
18411
19351
  }
18412
19352
  function MenuRow({ theme, option, onClick }) {
18413
- const [hover, setHover] = (0, import_react15.useState)(false);
19353
+ const [hover, setHover] = (0, import_react17.useState)(false);
18414
19354
  const swatchSize = 18;
18415
- return (0, import_jsx_runtime17.jsxs)("div", { role: "button", onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: {
19355
+ return (0, import_jsx_runtime24.jsxs)("div", { role: "button", onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: {
18416
19356
  display: "flex",
18417
19357
  alignItems: "center",
18418
19358
  gap: 8,
@@ -18421,7 +19361,7 @@ var SystemCanvas = (() => {
18421
19361
  cursor: "pointer",
18422
19362
  background: hover ? "rgba(255,255,255,0.06)" : "transparent",
18423
19363
  color: theme.breadcrumbs.activeColor
18424
- }, children: [option.kind === "category" ? (0, import_jsx_runtime17.jsx)("span", { style: {
19364
+ }, children: [option.kind === "category" ? (0, import_jsx_runtime24.jsx)("span", { style: {
18425
19365
  width: swatchSize,
18426
19366
  height: swatchSize,
18427
19367
  borderRadius: 4,
@@ -18431,13 +19371,13 @@ var SystemCanvas = (() => {
18431
19371
  alignItems: "center",
18432
19372
  justifyContent: "center",
18433
19373
  flexShrink: 0
18434
- }, children: option.icon && (0, import_jsx_runtime17.jsx)("svg", { width: 12, height: 12, viewBox: "0 0 14 14", style: { overflow: "visible" }, children: (0, import_jsx_runtime17.jsx)("g", { transform: "translate(-1, -1)", children: (0, import_jsx_runtime17.jsx)(NodeIcon, { icon: option.icon, x: 0, y: 0, size: 14, color: option.stroke ?? theme.breadcrumbs.activeColor, opacity: 0.9, customIcons: theme.icons }) }) }) }) : (0, import_jsx_runtime17.jsx)("span", { style: {
19374
+ }, children: option.icon && (0, import_jsx_runtime24.jsx)("svg", { width: 12, height: 12, viewBox: "0 0 14 14", style: { overflow: "visible" }, children: (0, import_jsx_runtime24.jsx)("g", { transform: "translate(-1, -1)", children: (0, import_jsx_runtime24.jsx)(NodeIcon, { icon: option.icon, x: 0, y: 0, size: 14, color: option.stroke ?? theme.breadcrumbs.activeColor, opacity: 0.9, customIcons: theme.icons }) }) }) }) : (0, import_jsx_runtime24.jsx)("span", { style: {
18435
19375
  width: swatchSize,
18436
19376
  height: swatchSize,
18437
19377
  borderRadius: 4,
18438
19378
  border: `1px dashed ${theme.breadcrumbs.separatorColor}`,
18439
19379
  flexShrink: 0
18440
- } }), (0, import_jsx_runtime17.jsx)("span", { style: { textTransform: "capitalize" }, children: option.label }), (0, import_jsx_runtime17.jsx)("span", { style: {
19380
+ } }), (0, import_jsx_runtime24.jsx)("span", { style: { textTransform: "capitalize" }, children: option.label }), (0, import_jsx_runtime24.jsx)("span", { style: {
18441
19381
  marginLeft: "auto",
18442
19382
  fontSize: theme.breadcrumbs.fontSize - 2,
18443
19383
  opacity: 0.5
@@ -18445,13 +19385,13 @@ var SystemCanvas = (() => {
18445
19385
  }
18446
19386
 
18447
19387
  // ../react/dist/components/LaneHeaders.js
18448
- var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
18449
- var import_react16 = __toESM(require_react(), 1);
19388
+ var import_jsx_runtime25 = __toESM(require_jsx_runtime(), 1);
19389
+ var import_react18 = __toESM(require_react(), 1);
18450
19390
  function LaneHeaders({ columns, rows, theme, getViewport, width, height, pinned = true }) {
18451
19391
  const hasColumns = columns && columns.length > 0;
18452
19392
  const hasRows = rows && rows.length > 0;
18453
- const [viewport, setViewport] = (0, import_react16.useState)(() => getViewport());
18454
- (0, import_react16.useEffect)(() => {
19393
+ const [viewport, setViewport] = (0, import_react18.useState)(() => getViewport());
19394
+ (0, import_react18.useEffect)(() => {
18455
19395
  if (!hasColumns && !hasRows)
18456
19396
  return;
18457
19397
  let raf = 0;
@@ -18478,14 +19418,14 @@ var SystemCanvas = (() => {
18478
19418
  const pad = lanesTheme.headerPadding;
18479
19419
  const colsOffsetLeft = hasRows && pinned ? headerSize : 0;
18480
19420
  const rowsOffsetTop = hasColumns && pinned ? headerSize : 0;
18481
- return (0, import_jsx_runtime18.jsxs)("svg", { className: "system-canvas-lane-headers", style: {
19421
+ return (0, import_jsx_runtime25.jsxs)("svg", { className: "system-canvas-lane-headers", style: {
18482
19422
  position: "absolute",
18483
19423
  top: 0,
18484
19424
  left: 0,
18485
19425
  width,
18486
19426
  height,
18487
19427
  pointerEvents: "none"
18488
- }, children: [hasColumns && (0, import_jsx_runtime18.jsxs)("g", { children: [pinned && (0, import_jsx_runtime18.jsx)("rect", { x: colsOffsetLeft, y: 0, width: width - colsOffsetLeft, height: headerSize, fill: lanesTheme.headerBackground }), columns.map((col) => {
19428
+ }, children: [hasColumns && (0, import_jsx_runtime25.jsxs)("g", { children: [pinned && (0, import_jsx_runtime25.jsx)("rect", { x: colsOffsetLeft, y: 0, width: width - colsOffsetLeft, height: headerSize, fill: lanesTheme.headerBackground }), columns.map((col) => {
18489
19429
  const startScreen = canvasToScreen(col.start, 0, viewport).x;
18490
19430
  const endScreen = canvasToScreen(col.start + col.size, 0, viewport).x;
18491
19431
  const w = endScreen - startScreen;
@@ -18496,8 +19436,8 @@ var SystemCanvas = (() => {
18496
19436
  const cx = visibleLeft + visibleW / 2;
18497
19437
  if (endScreen <= colsOffsetLeft || startScreen >= width)
18498
19438
  return null;
18499
- return (0, import_jsx_runtime18.jsxs)("g", { children: [(0, import_jsx_runtime18.jsx)("line", { x1: startScreen, y1: pinned ? headerSize : y + headerSize, x2: startScreen, y2: pinned ? 0 : y, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth }), (0, import_jsx_runtime18.jsx)("text", { x: cx, y: (pinned ? 0 : y) + headerSize / 2, textAnchor: "middle", dominantBaseline: "middle", fill: lanesTheme.headerTextColor, fontFamily: lanesTheme.headerFontFamily, fontSize: lanesTheme.headerFontSize, style: { userSelect: "none" }, children: truncateToWidth(col.label, visibleW - pad * 2, lanesTheme.headerFontSize) })] }, `colh-${col.id}`);
18500
- }), pinned && (0, import_jsx_runtime18.jsx)("line", { x1: colsOffsetLeft, y1: headerSize, x2: width, y2: headerSize, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth })] }), hasRows && (0, import_jsx_runtime18.jsxs)("g", { children: [pinned && (0, import_jsx_runtime18.jsx)("rect", { x: 0, y: rowsOffsetTop, width: headerSize, height: height - rowsOffsetTop, fill: lanesTheme.headerBackground }), rows.map((row) => {
19439
+ return (0, import_jsx_runtime25.jsxs)("g", { children: [(0, import_jsx_runtime25.jsx)("line", { x1: startScreen, y1: pinned ? headerSize : y + headerSize, x2: startScreen, y2: pinned ? 0 : y, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth }), (0, import_jsx_runtime25.jsx)("text", { x: cx, y: (pinned ? 0 : y) + headerSize / 2, textAnchor: "middle", dominantBaseline: "middle", fill: lanesTheme.headerTextColor, fontFamily: lanesTheme.headerFontFamily, fontSize: lanesTheme.headerFontSize, style: { userSelect: "none" }, children: truncateToWidth(col.label, visibleW - pad * 2, lanesTheme.headerFontSize) })] }, `colh-${col.id}`);
19440
+ }), pinned && (0, import_jsx_runtime25.jsx)("line", { x1: colsOffsetLeft, y1: headerSize, x2: width, y2: headerSize, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth })] }), hasRows && (0, import_jsx_runtime25.jsxs)("g", { children: [pinned && (0, import_jsx_runtime25.jsx)("rect", { x: 0, y: rowsOffsetTop, width: headerSize, height: height - rowsOffsetTop, fill: lanesTheme.headerBackground }), rows.map((row) => {
18501
19441
  const startScreen = canvasToScreen(0, row.start, viewport).y;
18502
19442
  const endScreen = canvasToScreen(0, row.start + row.size, viewport).y;
18503
19443
  const h = endScreen - startScreen;
@@ -18508,8 +19448,8 @@ var SystemCanvas = (() => {
18508
19448
  const cy = visibleTop + visibleH / 2;
18509
19449
  if (endScreen <= rowsOffsetTop || startScreen >= height)
18510
19450
  return null;
18511
- return (0, import_jsx_runtime18.jsxs)("g", { children: [(0, import_jsx_runtime18.jsx)("line", { x1: pinned ? 0 : x, y1: startScreen, x2: pinned ? headerSize : x + headerSize, y2: startScreen, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth }), (0, import_jsx_runtime18.jsx)("text", { x: (pinned ? 0 : x) + headerSize / 2, y: cy, textAnchor: "middle", dominantBaseline: "middle", fill: lanesTheme.headerTextColor, fontFamily: lanesTheme.headerFontFamily, fontSize: lanesTheme.headerFontSize, transform: `rotate(-90 ${(pinned ? 0 : x) + headerSize / 2} ${cy})`, style: { userSelect: "none" }, children: truncateToWidth(row.label, visibleH - pad * 2, lanesTheme.headerFontSize) })] }, `rowh-${row.id}`);
18512
- }), pinned && (0, import_jsx_runtime18.jsx)("line", { x1: headerSize, y1: rowsOffsetTop, x2: headerSize, y2: height, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth })] }), hasColumns && hasRows && pinned && (0, import_jsx_runtime18.jsx)("rect", { x: 0, y: 0, width: headerSize, height: headerSize, fill: lanesTheme.headerBackground })] });
19451
+ return (0, import_jsx_runtime25.jsxs)("g", { children: [(0, import_jsx_runtime25.jsx)("line", { x1: pinned ? 0 : x, y1: startScreen, x2: pinned ? headerSize : x + headerSize, y2: startScreen, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth }), (0, import_jsx_runtime25.jsx)("text", { x: (pinned ? 0 : x) + headerSize / 2, y: cy, textAnchor: "middle", dominantBaseline: "middle", fill: lanesTheme.headerTextColor, fontFamily: lanesTheme.headerFontFamily, fontSize: lanesTheme.headerFontSize, transform: `rotate(-90 ${(pinned ? 0 : x) + headerSize / 2} ${cy})`, style: { userSelect: "none" }, children: truncateToWidth(row.label, visibleH - pad * 2, lanesTheme.headerFontSize) })] }, `rowh-${row.id}`);
19452
+ }), pinned && (0, import_jsx_runtime25.jsx)("line", { x1: headerSize, y1: rowsOffsetTop, x2: headerSize, y2: height, stroke: lanesTheme.dividerColor, strokeWidth: lanesTheme.dividerWidth })] }), hasColumns && hasRows && pinned && (0, import_jsx_runtime25.jsx)("rect", { x: 0, y: 0, width: headerSize, height: headerSize, fill: lanesTheme.headerBackground })] });
18513
19453
  }
18514
19454
  function truncateToWidth(label, availablePx, fontSize) {
18515
19455
  if (availablePx <= 0)
@@ -18524,8 +19464,8 @@ var SystemCanvas = (() => {
18524
19464
  }
18525
19465
 
18526
19466
  // ../react/dist/components/NodeToolbar.js
18527
- var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
18528
- var import_react17 = __toESM(require_react(), 1);
19467
+ var import_jsx_runtime26 = __toESM(require_jsx_runtime(), 1);
19468
+ var import_react19 = __toESM(require_react(), 1);
18529
19469
  var NODE_GAP = 10;
18530
19470
  var FLIP_MARGIN = 8;
18531
19471
  var PADDING = 6;
@@ -18534,8 +19474,8 @@ var SystemCanvas = (() => {
18534
19474
  var BUTTON_SIZE = 28;
18535
19475
  var DELETE_SIZE = 14;
18536
19476
  function NodeToolbar({ node, theme, onPatch, onDelete, getViewport, containerWidth, containerHeight, render: render2 }) {
18537
- const [viewport, setViewport] = (0, import_react17.useState)(() => getViewport());
18538
- (0, import_react17.useEffect)(() => {
19477
+ const [viewport, setViewport] = (0, import_react19.useState)(() => getViewport());
19478
+ (0, import_react19.useEffect)(() => {
18539
19479
  let raf = 0;
18540
19480
  let lastX = -Infinity;
18541
19481
  let lastY = -Infinity;
@@ -18555,9 +19495,9 @@ var SystemCanvas = (() => {
18555
19495
  }, [getViewport]);
18556
19496
  const topCenter = canvasToScreen(node.x + node.width / 2, node.y, viewport);
18557
19497
  const bottomCenter = canvasToScreen(node.x + node.width / 2, node.y + node.height, viewport);
18558
- const toolbarRef = (0, import_react17.useRef)(null);
18559
- const [size, setSize] = (0, import_react17.useState)({ width: 0, height: 0 });
18560
- (0, import_react17.useEffect)(() => {
19498
+ const toolbarRef = (0, import_react19.useRef)(null);
19499
+ const [size, setSize] = (0, import_react19.useState)({ width: 0, height: 0 });
19500
+ (0, import_react19.useEffect)(() => {
18561
19501
  const el = toolbarRef.current;
18562
19502
  if (!el)
18563
19503
  return;
@@ -18578,7 +19518,7 @@ var SystemCanvas = (() => {
18578
19518
  left = Math.max(FLIP_MARGIN, Math.min(left, containerWidth - size.width - FLIP_MARGIN));
18579
19519
  const patch = (update) => onPatch(update);
18580
19520
  const deleteNode = () => onDelete();
18581
- return (0, import_jsx_runtime19.jsx)("div", {
19521
+ return (0, import_jsx_runtime26.jsx)("div", {
18582
19522
  ref: toolbarRef,
18583
19523
  className: "system-canvas-node-toolbar",
18584
19524
  // Stop pointer events from bubbling to the canvas (which would
@@ -18607,15 +19547,16 @@ var SystemCanvas = (() => {
18607
19547
  userSelect: "none",
18608
19548
  whiteSpace: "nowrap"
18609
19549
  },
18610
- children: render2 ? render2({ node, theme, patch, deleteNode }) : (0, import_jsx_runtime19.jsx)(DefaultToolbarContent, { node, theme, onPatch: patch, onDelete: deleteNode })
19550
+ children: render2 ? render2({ node, theme, patch, deleteNode }) : (0, import_jsx_runtime26.jsx)(DefaultToolbarContent, { node, theme, onPatch: patch, onDelete: deleteNode })
18611
19551
  });
18612
19552
  }
18613
19553
  function DefaultToolbarContent({ node, theme, onPatch, onDelete }) {
18614
- const groups = (0, import_react17.useMemo)(() => getNodeActions(theme), [theme]);
18615
- return (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [groups.map((group, i) => (0, import_jsx_runtime19.jsxs)(import_react17.default.Fragment, { children: [i > 0 && (0, import_jsx_runtime19.jsx)(Divider2, { theme }), (0, import_jsx_runtime19.jsx)(ActionGroupView, { group, node, theme, onPatch })] }, group.id)), (0, import_jsx_runtime19.jsx)(Divider2, { theme }), (0, import_jsx_runtime19.jsx)(DeleteButton, { theme, onDelete })] });
19554
+ const groups = (0, import_react19.useMemo)(() => getNodeActionsForNode(node, theme), [node, theme]);
19555
+ const showDelete = !theme.hideToolbarDelete;
19556
+ return (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [groups.map((group, i) => (0, import_jsx_runtime26.jsxs)(import_react19.default.Fragment, { children: [i > 0 && (0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)(ActionGroupView, { group, node, theme, onPatch })] }, group.id)), showDelete && (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [(0, import_jsx_runtime26.jsx)(Divider2, { theme }), (0, import_jsx_runtime26.jsx)(DeleteButton, { theme, onDelete })] })] });
18616
19557
  }
18617
19558
  function Divider2({ theme }) {
18618
- return (0, import_jsx_runtime19.jsx)("div", { style: {
19559
+ return (0, import_jsx_runtime26.jsx)("div", { style: {
18619
19560
  width: 1,
18620
19561
  alignSelf: "stretch",
18621
19562
  background: theme.breadcrumbs.separatorColor,
@@ -18628,23 +19569,23 @@ var SystemCanvas = (() => {
18628
19569
  return null;
18629
19570
  const kind = group.kind ?? "buttons";
18630
19571
  if (kind === "menu") {
18631
- return (0, import_jsx_runtime19.jsx)(MenuGroup, { group, actions, node, theme, onPatch });
19572
+ return (0, import_jsx_runtime26.jsx)(MenuGroup, { group, actions, node, theme, onPatch });
18632
19573
  }
18633
- return (0, import_jsx_runtime19.jsx)("div", { title: group.label, style: { display: "flex", alignItems: "center", gap: BUTTON_GAP }, children: actions.map((action) => {
19574
+ return (0, import_jsx_runtime26.jsx)("div", { title: group.label, style: { display: "flex", alignItems: "center", gap: BUTTON_GAP }, children: actions.map((action) => {
18634
19575
  const handleClick = () => {
18635
19576
  const patch = resolveActionPatch(action, node);
18636
19577
  onPatch(patch);
18637
19578
  };
18638
19579
  const active = action.isActive?.(node) ?? false;
18639
19580
  if (kind === "swatches") {
18640
- return (0, import_jsx_runtime19.jsx)(SwatchButton, { action, active, theme, onClick: handleClick }, action.id);
19581
+ return (0, import_jsx_runtime26.jsx)(SwatchButton, { action, active, theme, onClick: handleClick }, action.id);
18641
19582
  }
18642
- return (0, import_jsx_runtime19.jsx)(IconButton, { action, active, theme, onClick: handleClick }, action.id);
19583
+ return (0, import_jsx_runtime26.jsx)(IconButton, { action, active, theme, onClick: handleClick }, action.id);
18643
19584
  }) });
18644
19585
  }
18645
19586
  function SwatchButton({ action, active, theme, onClick }) {
18646
19587
  const color2 = action.swatch ?? theme.node.labelColor;
18647
- return (0, import_jsx_runtime19.jsx)("button", { type: "button", title: action.label, onClick, style: {
19588
+ return (0, import_jsx_runtime26.jsx)("button", { type: "button", title: action.label, onClick, style: {
18648
19589
  width: SWATCH_SIZE,
18649
19590
  height: SWATCH_SIZE,
18650
19591
  borderRadius: "50%",
@@ -18658,7 +19599,7 @@ var SystemCanvas = (() => {
18658
19599
  }, onMouseDown: (e) => e.preventDefault() });
18659
19600
  }
18660
19601
  function IconButton({ action, active, theme, onClick }) {
18661
- return (0, import_jsx_runtime19.jsx)("button", { type: "button", title: action.label, onClick, style: {
19602
+ return (0, import_jsx_runtime26.jsx)("button", { type: "button", title: action.label, onClick, style: {
18662
19603
  width: BUTTON_SIZE,
18663
19604
  height: BUTTON_SIZE,
18664
19605
  display: "inline-flex",
@@ -18671,17 +19612,17 @@ var SystemCanvas = (() => {
18671
19612
  cursor: "pointer",
18672
19613
  padding: 0,
18673
19614
  outline: "none"
18674
- }, onMouseDown: (e) => e.preventDefault(), children: action.icon ? (0, import_jsx_runtime19.jsx)("svg", { width: 16, height: 16, viewBox: "0 0 16 16", children: (0, import_jsx_runtime19.jsx)(NodeIcon, { icon: action.icon, x: 0, y: 0, size: 16, color: active ? theme.breadcrumbs.activeColor : theme.breadcrumbs.textColor, opacity: 1, customIcons: theme.icons }) }) : action.swatch ? (0, import_jsx_runtime19.jsx)("span", { style: {
19615
+ }, onMouseDown: (e) => e.preventDefault(), children: action.icon ? (0, import_jsx_runtime26.jsx)("svg", { width: 16, height: 16, viewBox: "0 0 16 16", children: (0, import_jsx_runtime26.jsx)(NodeIcon, { icon: action.icon, x: 0, y: 0, size: 16, color: active ? theme.breadcrumbs.activeColor : theme.breadcrumbs.textColor, opacity: 1, customIcons: theme.icons }) }) : action.swatch ? (0, import_jsx_runtime26.jsx)("span", { style: {
18675
19616
  width: 10,
18676
19617
  height: 10,
18677
19618
  borderRadius: "50%",
18678
19619
  background: action.swatch
18679
- } }) : (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 10 }, children: action.label.slice(0, 2) }) });
19620
+ } }) : (0, import_jsx_runtime26.jsx)("span", { style: { fontSize: 10 }, children: action.label.slice(0, 2) }) });
18680
19621
  }
18681
19622
  function MenuGroup({ group, actions, node, theme, onPatch }) {
18682
- const [open, setOpen] = (0, import_react17.useState)(false);
18683
- const wrapRef = (0, import_react17.useRef)(null);
18684
- (0, import_react17.useEffect)(() => {
19623
+ const [open, setOpen] = (0, import_react19.useState)(false);
19624
+ const wrapRef = (0, import_react19.useRef)(null);
19625
+ (0, import_react19.useEffect)(() => {
18685
19626
  if (!open)
18686
19627
  return;
18687
19628
  const onDown = (e) => {
@@ -18694,7 +19635,7 @@ var SystemCanvas = (() => {
18694
19635
  const active = actions.find((a) => a.isActive?.(node));
18695
19636
  const triggerLabel = active?.label ?? group.label ?? "Menu";
18696
19637
  const triggerIcon = active?.icon ?? void 0;
18697
- return (0, import_jsx_runtime19.jsxs)("div", { ref: wrapRef, style: { position: "relative" }, children: [(0, import_jsx_runtime19.jsxs)("button", { type: "button", title: group.label, onClick: () => setOpen((v) => !v), style: {
19638
+ return (0, import_jsx_runtime26.jsxs)("div", { ref: wrapRef, style: { position: "relative" }, children: [(0, import_jsx_runtime26.jsxs)("button", { type: "button", title: group.label, onClick: () => setOpen((v) => !v), style: {
18698
19639
  height: BUTTON_SIZE,
18699
19640
  display: "inline-flex",
18700
19641
  alignItems: "center",
@@ -18708,7 +19649,7 @@ var SystemCanvas = (() => {
18708
19649
  fontFamily: "inherit",
18709
19650
  fontSize: "inherit",
18710
19651
  outline: "none"
18711
- }, onMouseDown: (e) => e.preventDefault(), children: [triggerIcon && (0, import_jsx_runtime19.jsx)("svg", { width: 14, height: 14, viewBox: "0 0 16 16", children: (0, import_jsx_runtime19.jsx)(NodeIcon, { icon: triggerIcon, x: 0, y: 0, size: 14, color: theme.breadcrumbs.textColor, opacity: 1, customIcons: theme.icons }) }), (0, import_jsx_runtime19.jsx)("span", { children: triggerLabel }), (0, import_jsx_runtime19.jsx)("span", { style: { opacity: 0.6, fontSize: 8 }, children: "\u25BE" })] }), open && (0, import_jsx_runtime19.jsx)("div", { style: {
19652
+ }, onMouseDown: (e) => e.preventDefault(), children: [triggerIcon && (0, import_jsx_runtime26.jsx)("svg", { width: 14, height: 14, viewBox: "0 0 16 16", children: (0, import_jsx_runtime26.jsx)(NodeIcon, { icon: triggerIcon, x: 0, y: 0, size: 14, color: theme.breadcrumbs.textColor, opacity: 1, customIcons: theme.icons }) }), (0, import_jsx_runtime26.jsx)("span", { children: triggerLabel }), (0, import_jsx_runtime26.jsx)("span", { style: { opacity: 0.6, fontSize: 8 }, children: "\u25BE" })] }), open && (0, import_jsx_runtime26.jsx)("div", { style: {
18712
19653
  position: "absolute",
18713
19654
  top: "100%",
18714
19655
  left: 0,
@@ -18723,7 +19664,7 @@ var SystemCanvas = (() => {
18723
19664
  zIndex: 1
18724
19665
  }, children: actions.map((action) => {
18725
19666
  const isActive = action.isActive?.(node) ?? false;
18726
- return (0, import_jsx_runtime19.jsxs)("button", { type: "button", onClick: () => {
19667
+ return (0, import_jsx_runtime26.jsxs)("button", { type: "button", onClick: () => {
18727
19668
  onPatch(resolveActionPatch(action, node));
18728
19669
  setOpen(false);
18729
19670
  }, style: {
@@ -18740,17 +19681,17 @@ var SystemCanvas = (() => {
18740
19681
  fontFamily: "inherit",
18741
19682
  fontSize: "inherit",
18742
19683
  textAlign: "left"
18743
- }, onMouseDown: (e) => e.preventDefault(), children: [action.icon && (0, import_jsx_runtime19.jsx)("svg", { width: 14, height: 14, viewBox: "0 0 16 16", children: (0, import_jsx_runtime19.jsx)(NodeIcon, { icon: action.icon, x: 0, y: 0, size: 14, color: isActive ? theme.breadcrumbs.activeColor : theme.breadcrumbs.textColor, opacity: 1, customIcons: theme.icons }) }), action.swatch && (0, import_jsx_runtime19.jsx)("span", { style: {
19684
+ }, onMouseDown: (e) => e.preventDefault(), children: [action.icon && (0, import_jsx_runtime26.jsx)("svg", { width: 14, height: 14, viewBox: "0 0 16 16", children: (0, import_jsx_runtime26.jsx)(NodeIcon, { icon: action.icon, x: 0, y: 0, size: 14, color: isActive ? theme.breadcrumbs.activeColor : theme.breadcrumbs.textColor, opacity: 1, customIcons: theme.icons }) }), action.swatch && (0, import_jsx_runtime26.jsx)("span", { style: {
18744
19685
  width: 10,
18745
19686
  height: 10,
18746
19687
  borderRadius: "50%",
18747
19688
  background: action.swatch,
18748
19689
  flexShrink: 0
18749
- } }), (0, import_jsx_runtime19.jsx)("span", { children: action.label })] }, action.id);
19690
+ } }), (0, import_jsx_runtime26.jsx)("span", { children: action.label })] }, action.id);
18750
19691
  }) })] });
18751
19692
  }
18752
19693
  function DeleteButton({ theme, onDelete }) {
18753
- return (0, import_jsx_runtime19.jsx)("button", { type: "button", title: "Delete", onClick: onDelete, onMouseDown: (e) => e.preventDefault(), style: {
19694
+ return (0, import_jsx_runtime26.jsx)("button", { type: "button", title: "Delete", onClick: onDelete, onMouseDown: (e) => e.preventDefault(), style: {
18754
19695
  width: BUTTON_SIZE,
18755
19696
  height: BUTTON_SIZE,
18756
19697
  display: "inline-flex",
@@ -18763,21 +19704,21 @@ var SystemCanvas = (() => {
18763
19704
  cursor: "pointer",
18764
19705
  padding: 0,
18765
19706
  outline: "none"
18766
- }, children: (0, import_jsx_runtime19.jsx)("svg", { width: DELETE_SIZE, height: DELETE_SIZE, viewBox: "0 0 16 16", children: (0, import_jsx_runtime19.jsx)("path", { d: "M 3 5 L 13 5 M 6 5 L 6 3 L 10 3 L 10 5 M 5 5 L 5.5 14 L 10.5 14 L 11 5 M 7 7 L 7 12 M 9 7 L 9 12", fill: "none", stroke: "currentColor", strokeWidth: 1.2, strokeLinecap: "round", strokeLinejoin: "round" }) }) });
19707
+ }, children: (0, import_jsx_runtime26.jsx)("svg", { width: DELETE_SIZE, height: DELETE_SIZE, viewBox: "0 0 16 16", children: (0, import_jsx_runtime26.jsx)("path", { d: "M 3 5 L 13 5 M 6 5 L 6 3 L 10 3 L 10 5 M 5 5 L 5.5 14 L 10.5 14 L 11 5 M 7 7 L 7 12 M 9 7 L 9 12", fill: "none", stroke: "currentColor", strokeWidth: 1.2, strokeLinecap: "round", strokeLinejoin: "round" }) }) });
18767
19708
  }
18768
19709
 
18769
19710
  // ../react/dist/components/SystemCanvas.js
18770
19711
  var CASCADE_WINDOW_MS = 1500;
18771
19712
  var CASCADE_OFFSET = 20;
18772
- var SystemCanvas = (0, import_react18.forwardRef)(function SystemCanvas2({ canvas, onResolveCanvas, canvases, rootLabel = "Home", onNavigate, onBreadcrumbClick, onNodeClick, onNodeDoubleClick, onEdgeClick, onEdgeDoubleClick, onContextMenu, editable = false, onNodeAdd, onNodeUpdate, onNodeDelete, onEdgeUpdate, onEdgeDelete, onEdgeAdd, renderAddNodeButton, showNodeToolbar = true, renderNodeToolbar, theme: themeProp, themes: customThemes, edgeStyle = "bezier", defaultViewport, minZoom: minZoomProp, maxZoom, onViewportChange, autoFit = "canvas-change", laneHeaders = "pinned", snapToLanes = false, zoomNavigation = false, className, style }, forwardedRef) {
18773
- const zoomNavConfig = (0, import_react18.useMemo)(() => {
19713
+ var SystemCanvas = (0, import_react20.forwardRef)(function SystemCanvas2({ canvas, onResolveCanvas, canvases, rootLabel = "Home", onNavigate, onBreadcrumbClick, onNodeClick, onNodeDoubleClick, onEdgeClick, onEdgeDoubleClick, onContextMenu, editable = false, onNodeAdd, onNodeUpdate, onNodeDelete, onEdgeUpdate, onEdgeDelete, onEdgeAdd, renderAddNodeButton, showNodeToolbar = true, renderNodeToolbar, theme: themeProp, themes: customThemes, edgeStyle = "bezier", defaultViewport, minZoom: minZoomProp, maxZoom, onViewportChange, autoFit = "canvas-change", laneHeaders = "pinned", snapToLanes = false, zoomNavigation = false, className, style }, forwardedRef) {
19714
+ const zoomNavConfig = (0, import_react20.useMemo)(() => {
18774
19715
  const defaults = {
18775
19716
  enterThreshold: 0.66,
18776
19717
  exitThreshold: 0.33,
18777
19718
  prefetchThreshold: 0.4,
18778
19719
  landingScale: 1.2,
18779
19720
  landingPadding: 0.08,
18780
- fadeDuration: 200
19721
+ fadeDuration: 216
18781
19722
  };
18782
19723
  if (!zoomNavigation)
18783
19724
  return { enabled: false, ...defaults };
@@ -18795,16 +19736,16 @@ var SystemCanvas = (() => {
18795
19736
  }, [zoomNavigation]);
18796
19737
  const effectiveMaxZoom = maxZoom ?? (zoomNavConfig.enabled ? 16 : 4);
18797
19738
  const effectiveMinZoom = minZoomProp ?? (zoomNavConfig.enabled ? 0.01 : 0.1);
18798
- (0, import_react18.useEffect)(() => {
19739
+ (0, import_react20.useEffect)(() => {
18799
19740
  const env = globalThis.process?.env?.NODE_ENV;
18800
19741
  if (editable && !canvases && env !== "production") {
18801
19742
  console.warn("[system-canvas] `editable` is enabled but `canvases` prop is missing. Edits to sub-canvases will not be reflected without a synchronous ref \u2192 CanvasData map.");
18802
19743
  }
18803
19744
  }, [editable, canvases]);
18804
- const [parentFrames, setParentFrames] = (0, import_react18.useState)([]);
18805
- const [pendingHandoff, setPendingHandoff] = (0, import_react18.useState)(null);
18806
- const suppressNextHandoffClearRef = (0, import_react18.useRef)(false);
18807
- const handleBreadcrumbClick = (0, import_react18.useCallback)((index) => {
19745
+ const [parentFrames, setParentFrames] = (0, import_react20.useState)([]);
19746
+ const [pendingHandoff, setPendingHandoff] = (0, import_react20.useState)(null);
19747
+ const suppressNextHandoffClearRef = (0, import_react20.useRef)(false);
19748
+ const handleBreadcrumbClick = (0, import_react20.useCallback)((index) => {
18808
19749
  setParentFrames((prev) => prev.slice(0, index));
18809
19750
  if (suppressNextHandoffClearRef.current) {
18810
19751
  suppressNextHandoffClearRef.current = false;
@@ -18821,7 +19762,7 @@ var SystemCanvas = (() => {
18821
19762
  onNavigate,
18822
19763
  onBreadcrumbClick: handleBreadcrumbClick
18823
19764
  });
18824
- const theme = (0, import_react18.useMemo)(() => {
19765
+ const theme = (0, import_react20.useMemo)(() => {
18825
19766
  const registry = { ...themes, ...customThemes };
18826
19767
  const resolveByName = (name) => name && registry[name] ? registry[name] : null;
18827
19768
  if (themeProp) {
@@ -18832,22 +19773,22 @@ var SystemCanvas = (() => {
18832
19773
  }
18833
19774
  return resolveByName(currentCanvas.theme?.base) ?? resolveByName(canvas.theme?.base) ?? darkTheme;
18834
19775
  }, [themeProp, customThemes, currentCanvas.theme?.base, canvas.theme?.base]);
18835
- const { nodes, edges, nodeMap } = (0, import_react18.useMemo)(() => {
19776
+ const { nodes, edges, nodeMap } = (0, import_react20.useMemo)(() => {
18836
19777
  const resolved = resolveCanvas(currentCanvas, theme);
18837
19778
  const map = buildNodeMap(resolved.nodes);
18838
19779
  return { nodes: resolved.nodes, edges: resolved.edges, nodeMap: map };
18839
19780
  }, [currentCanvas, theme]);
18840
- const nodesRef = (0, import_react18.useRef)(nodes);
19781
+ const nodesRef = (0, import_react20.useRef)(nodes);
18841
19782
  nodesRef.current = nodes;
18842
- const viewportStateRef = (0, import_react18.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
18843
- const viewportHandleRef = (0, import_react18.useRef)(null);
18844
- const navigateToRefRef = (0, import_react18.useRef)(navigateToRef);
19783
+ const viewportStateRef = (0, import_react20.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
19784
+ const viewportHandleRef = (0, import_react20.useRef)(null);
19785
+ const navigateToRefRef = (0, import_react20.useRef)(navigateToRef);
18845
19786
  navigateToRefRef.current = navigateToRef;
18846
- const navigateToBreadcrumbRef = (0, import_react18.useRef)(navigateToBreadcrumb);
19787
+ const navigateToBreadcrumbRef = (0, import_react20.useRef)(navigateToBreadcrumb);
18847
19788
  navigateToBreadcrumbRef.current = navigateToBreadcrumb;
18848
- const breadcrumbsRef = (0, import_react18.useRef)(breadcrumbs);
19789
+ const breadcrumbsRef = (0, import_react20.useRef)(breadcrumbs);
18849
19790
  breadcrumbsRef.current = breadcrumbs;
18850
- (0, import_react18.useImperativeHandle)(forwardedRef, () => ({
19791
+ (0, import_react20.useImperativeHandle)(forwardedRef, () => ({
18851
19792
  zoomIntoNode: (nodeId, options) => {
18852
19793
  return new Promise((resolve) => {
18853
19794
  const node = nodesRef.current.find((n) => n.id === nodeId);
@@ -18879,19 +19820,19 @@ var SystemCanvas = (() => {
18879
19820
  navigateToBreadcrumbRef.current(0);
18880
19821
  }
18881
19822
  }), [forwardedRef]);
18882
- const [selectedId, setSelectedId] = (0, import_react18.useState)(null);
18883
- const [editingId, setEditingId] = (0, import_react18.useState)(null);
18884
- const [selectedEdgeId, setSelectedEdgeId] = (0, import_react18.useState)(null);
18885
- const [editingEdgeId, setEditingEdgeId] = (0, import_react18.useState)(null);
18886
- (0, import_react18.useEffect)(() => {
19823
+ const [selectedId, setSelectedId] = (0, import_react20.useState)(null);
19824
+ const [editingId, setEditingId] = (0, import_react20.useState)(null);
19825
+ const [selectedEdgeId, setSelectedEdgeId] = (0, import_react20.useState)(null);
19826
+ const [editingEdgeId, setEditingEdgeId] = (0, import_react20.useState)(null);
19827
+ (0, import_react20.useEffect)(() => {
18887
19828
  setSelectedId(null);
18888
19829
  setEditingId(null);
18889
19830
  setSelectedEdgeId(null);
18890
19831
  setEditingEdgeId(null);
18891
19832
  }, [currentCanvasRef]);
18892
- const containerRef = (0, import_react18.useRef)(null);
18893
- const [containerSize, setContainerSize] = (0, import_react18.useState)({ width: 0, height: 0 });
18894
- (0, import_react18.useEffect)(() => {
19833
+ const containerRef = (0, import_react20.useRef)(null);
19834
+ const [containerSize, setContainerSize] = (0, import_react20.useState)({ width: 0, height: 0 });
19835
+ (0, import_react20.useEffect)(() => {
18895
19836
  const el = containerRef.current;
18896
19837
  if (!el)
18897
19838
  return;
@@ -18906,19 +19847,28 @@ var SystemCanvas = (() => {
18906
19847
  }, []);
18907
19848
  const hasLanes = currentCanvas.columns && currentCanvas.columns.length > 0 || currentCanvas.rows && currentCanvas.rows.length > 0;
18908
19849
  const showLaneHeaders = hasLanes && laneHeaders !== "none";
18909
- const getViewportState = (0, import_react18.useCallback)(() => viewportStateRef.current ?? { x: 0, y: 0, zoom: 1 }, []);
18910
- const commitDrag = (0, import_react18.useCallback)((id2, patch) => {
19850
+ const getViewportState = (0, import_react20.useCallback)(() => viewportStateRef.current ?? { x: 0, y: 0, zoom: 1 }, []);
19851
+ const commitDrag = (0, import_react20.useCallback)((id2, patch) => {
18911
19852
  let final = patch;
18912
19853
  if (snapToLanes) {
18913
19854
  const cols = currentCanvas.columns;
18914
19855
  const rows = currentCanvas.rows;
19856
+ const node = nodesRef.current.find((n) => n.id === id2);
18915
19857
  const nx = patch.x;
18916
19858
  const ny = patch.y;
18917
19859
  if (cols && cols.length > 0 && nx != null) {
18918
- final = { ...final, x: Math.round(snapToLane(nx, cols)) };
19860
+ const snapped = snapToLane(nx, cols, {
19861
+ edge: "center",
19862
+ size: node?.width ?? 0
19863
+ });
19864
+ final = { ...final, x: Math.round(snapped) };
18919
19865
  }
18920
19866
  if (rows && rows.length > 0 && ny != null) {
18921
- final = { ...final, y: Math.round(snapToLane(ny, rows)) };
19867
+ const snapped = snapToLane(ny, rows, {
19868
+ edge: "center",
19869
+ size: node?.height ?? 0
19870
+ });
19871
+ final = { ...final, y: Math.round(snapped) };
18922
19872
  }
18923
19873
  }
18924
19874
  onNodeUpdate?.(id2, final, currentCanvasRef);
@@ -18932,7 +19882,7 @@ var SystemCanvas = (() => {
18932
19882
  viewport: viewportStateRef,
18933
19883
  onCommit: commitDrag
18934
19884
  });
18935
- const selectedResolvedNode = (0, import_react18.useMemo)(() => {
19885
+ const selectedResolvedNode = (0, import_react20.useMemo)(() => {
18936
19886
  if (!selectedId)
18937
19887
  return null;
18938
19888
  const base = nodeMap.get(selectedId);
@@ -18948,9 +19898,9 @@ var SystemCanvas = (() => {
18948
19898
  }
18949
19899
  return base;
18950
19900
  }, [selectedId, nodeMap, dragOverrides, resizeOverrides]);
18951
- const svgProxyRef = (0, import_react18.useRef)(null);
19901
+ const svgProxyRef = (0, import_react20.useRef)(null);
18952
19902
  svgProxyRef.current = viewportHandleRef.current?.getSvgElement() ?? null;
18953
- const handleEdgeCreated = (0, import_react18.useCallback)((edge) => {
19903
+ const handleEdgeCreated = (0, import_react20.useCallback)((edge) => {
18954
19904
  onEdgeAdd?.(edge, currentCanvasRef);
18955
19905
  }, [onEdgeAdd, currentCanvasRef]);
18956
19906
  const { pending: pendingEdge, onHandlePointerDown: onConnectionHandlePointerDown } = useEdgeCreate({
@@ -18959,7 +19909,7 @@ var SystemCanvas = (() => {
18959
19909
  nodesRef,
18960
19910
  onCreate: handleEdgeCreated
18961
19911
  });
18962
- const handleNavigableNodeClick = (0, import_react18.useCallback)((node) => {
19912
+ const handleNavigableNodeClick = (0, import_react20.useCallback)((node) => {
18963
19913
  const frame2 = {
18964
19914
  parentCanvasRef: currentCanvasRef,
18965
19915
  parentNodeRect: {
@@ -18970,16 +19920,23 @@ var SystemCanvas = (() => {
18970
19920
  }
18971
19921
  };
18972
19922
  setParentFrames((prev) => [...prev, frame2]);
19923
+ if (!zoomNavConfig.enabled) {
19924
+ navigateToRef(node);
19925
+ return;
19926
+ }
18973
19927
  const handle = viewportHandleRef.current;
18974
19928
  if (handle) {
18975
19929
  handle.zoomToNode(node, () => {
19930
+ const finalVp = viewportStateRef.current;
19931
+ if (finalVp)
19932
+ setPendingHandoff({ ...finalVp });
18976
19933
  navigateToRef(node);
18977
19934
  });
18978
19935
  } else {
18979
19936
  navigateToRef(node);
18980
19937
  }
18981
- }, [navigateToRef, currentCanvasRef]);
18982
- const handleZoomEnter = (0, import_react18.useCallback)((node, targetTransform) => {
19938
+ }, [navigateToRef, currentCanvasRef, zoomNavConfig.enabled]);
19939
+ const handleZoomEnter = (0, import_react20.useCallback)((node, targetTransform) => {
18983
19940
  const frame2 = {
18984
19941
  parentCanvasRef: currentCanvasRef,
18985
19942
  parentNodeRect: {
@@ -18993,7 +19950,7 @@ var SystemCanvas = (() => {
18993
19950
  setPendingHandoff(targetTransform);
18994
19951
  navigateToRef(node);
18995
19952
  }, [currentCanvasRef, navigateToRef]);
18996
- const handleZoomExit = (0, import_react18.useCallback)((targetTransform) => {
19953
+ const handleZoomExit = (0, import_react20.useCallback)((targetTransform) => {
18997
19954
  setPendingHandoff(targetTransform);
18998
19955
  suppressNextHandoffClearRef.current = true;
18999
19956
  navigateToBreadcrumb(breadcrumbs.length - 2);
@@ -19019,19 +19976,19 @@ var SystemCanvas = (() => {
19019
19976
  onEnter: handleZoomEnter,
19020
19977
  onExit: handleZoomExit
19021
19978
  });
19022
- const handleViewportChange = (0, import_react18.useCallback)((vp) => {
19979
+ const handleViewportChange = (0, import_react20.useCallback)((vp) => {
19023
19980
  viewportStateRef.current = vp;
19024
19981
  handleZoomNavViewportChange(vp);
19025
19982
  onViewportChange?.(vp);
19026
19983
  }, [handleZoomNavViewportChange, onViewportChange]);
19027
- const handleHandoffApplied = (0, import_react18.useCallback)(() => {
19984
+ const handleHandoffApplied = (0, import_react20.useCallback)(() => {
19028
19985
  setPendingHandoff(null);
19029
19986
  clearZoomNavCommitting();
19030
19987
  }, [clearZoomNavCommitting]);
19031
- const handleBeginEdit = (0, import_react18.useCallback)((node) => {
19988
+ const handleBeginEdit = (0, import_react20.useCallback)((node) => {
19032
19989
  setEditingId(node.id);
19033
19990
  }, []);
19034
- const handleBeginEditEdge = (0, import_react18.useCallback)((edge) => {
19991
+ const handleBeginEditEdge = (0, import_react20.useCallback)((edge) => {
19035
19992
  setEditingEdgeId(edge.id);
19036
19993
  }, []);
19037
19994
  const { handleNodeClick, handleNodeDoubleClick, handleNodeNavigate, handleEdgeClick, handleEdgeDoubleClick, handleCanvasClick, handleCanvasContextMenu, handleNodeContextMenu, handleEdgeContextMenu } = useCanvasInteraction({
@@ -19048,27 +20005,27 @@ var SystemCanvas = (() => {
19048
20005
  onSelectEdge: setSelectedEdgeId,
19049
20006
  onBeginEditEdge: handleBeginEditEdge
19050
20007
  });
19051
- const handleEditorCommit = (0, import_react18.useCallback)((patch) => {
20008
+ const handleEditorCommit = (0, import_react20.useCallback)((patch) => {
19052
20009
  if (editingId) {
19053
20010
  onNodeUpdate?.(editingId, patch, currentCanvasRef);
19054
20011
  }
19055
20012
  setEditingId(null);
19056
20013
  }, [editingId, onNodeUpdate, currentCanvasRef]);
19057
- const handleEditorCancel = (0, import_react18.useCallback)(() => {
20014
+ const handleEditorCancel = (0, import_react20.useCallback)(() => {
19058
20015
  setEditingId(null);
19059
20016
  }, []);
19060
- const handleEdgeEditorCommit = (0, import_react18.useCallback)((patch) => {
20017
+ const handleEdgeEditorCommit = (0, import_react20.useCallback)((patch) => {
19061
20018
  if (editingEdgeId) {
19062
20019
  onEdgeUpdate?.(editingEdgeId, patch, currentCanvasRef);
19063
20020
  }
19064
20021
  setEditingEdgeId(null);
19065
20022
  }, [editingEdgeId, onEdgeUpdate, currentCanvasRef]);
19066
- const handleEdgeEditorCancel = (0, import_react18.useCallback)(() => {
20023
+ const handleEdgeEditorCancel = (0, import_react20.useCallback)(() => {
19067
20024
  setEditingEdgeId(null);
19068
20025
  }, []);
19069
- const lastAddRef = (0, import_react18.useRef)(null);
19070
- const menuOptions = (0, import_react18.useMemo)(() => getNodeMenuOptions(currentCanvas, theme), [currentCanvas, theme]);
19071
- const addNode2 = (0, import_react18.useCallback)((option, position) => {
20026
+ const lastAddRef = (0, import_react20.useRef)(null);
20027
+ const menuOptions = (0, import_react20.useMemo)(() => getNodeMenuOptions(currentCanvas, theme), [currentCanvas, theme]);
20028
+ const addNode2 = (0, import_react20.useCallback)((option, position) => {
19072
20029
  let x, y;
19073
20030
  if (position) {
19074
20031
  x = position.x;
@@ -19097,10 +20054,10 @@ var SystemCanvas = (() => {
19097
20054
  y += nextOffset;
19098
20055
  lastAddRef.current = { t: now2, offset: nextOffset };
19099
20056
  }
19100
- const node = createNodeFromOption(option, Math.round(x), Math.round(y));
20057
+ const node = createNodeFromOption(option, Math.round(x), Math.round(y), void 0, theme);
19101
20058
  onNodeAdd?.(node, currentCanvasRef);
19102
- }, [onNodeAdd, currentCanvasRef]);
19103
- const handleKeyDown = (0, import_react18.useCallback)((e) => {
20059
+ }, [onNodeAdd, currentCanvasRef, theme]);
20060
+ const handleKeyDown = (0, import_react20.useCallback)((e) => {
19104
20061
  if (!editable)
19105
20062
  return;
19106
20063
  if (e.key === "Escape") {
@@ -19134,14 +20091,14 @@ var SystemCanvas = (() => {
19134
20091
  currentCanvasRef
19135
20092
  ]);
19136
20093
  const renderProps = { options: menuOptions, addNode: addNode2, theme };
19137
- return (0, import_jsx_runtime20.jsxs)("div", { ref: containerRef, className: `system-canvas ${className ?? ""}`, tabIndex: editable ? 0 : -1, onKeyDown: handleKeyDown, style: {
20094
+ return (0, import_jsx_runtime27.jsxs)("div", { ref: containerRef, className: `system-canvas ${className ?? ""}`, tabIndex: editable ? 0 : -1, onKeyDown: handleKeyDown, style: {
19138
20095
  position: "relative",
19139
20096
  width: "100%",
19140
20097
  height: "100%",
19141
20098
  overflow: "hidden",
19142
20099
  outline: "none",
19143
20100
  ...style
19144
- }, children: [(0, import_jsx_runtime20.jsx)(Breadcrumbs, { breadcrumbs, theme: theme.breadcrumbs, onNavigate: navigateToBreadcrumb }), isLoading && (0, import_jsx_runtime20.jsx)("div", { className: "system-canvas-loading", style: {
20101
+ }, children: [(0, import_jsx_runtime27.jsx)(Breadcrumbs, { breadcrumbs, theme: theme.breadcrumbs, onNavigate: navigateToBreadcrumb }), isLoading && (0, import_jsx_runtime27.jsx)("div", { className: "system-canvas-loading", style: {
19145
20102
  position: "absolute",
19146
20103
  top: 12,
19147
20104
  right: 12,
@@ -19153,12 +20110,12 @@ var SystemCanvas = (() => {
19153
20110
  fontFamily: theme.node.fontFamily,
19154
20111
  fontSize: 12,
19155
20112
  backdropFilter: "blur(8px)"
19156
- }, children: "Loading..." }), (0, import_jsx_runtime20.jsx)(Viewport, { ref: viewportHandleRef, nodes, edges, nodeMap, theme, edgeStyle, columns: currentCanvas.columns, rows: currentCanvas.rows, minZoom: effectiveMinZoom, maxZoom: effectiveMaxZoom, defaultViewport, autoFit, canvasRef: currentCanvasRef, handoffTransform: pendingHandoff, onHandoffApplied: handleHandoffApplied, handoffFadeMs: zoomNavConfig.fadeDuration, onViewportChange: handleViewportChange, onNodeClick: handleNodeClick, onNodeDoubleClick: handleNodeDoubleClick, onNodeNavigate: handleNodeNavigate, onEdgeClick: handleEdgeClick, onEdgeDoubleClick: handleEdgeDoubleClick, onCanvasClick: editable ? handleCanvasClick : void 0, onCanvasContextMenu: handleCanvasContextMenu, onNodeContextMenu: handleNodeContextMenu, onEdgeContextMenu: handleEdgeContextMenu, onNodePointerDown: editable ? onNodePointerDown : void 0, selectedId: editable ? selectedId : null, editingId: editable ? editingId : null, selectedEdgeId: editable ? selectedEdgeId : null, editingEdgeId: editable ? editingEdgeId : null, dragOverrides, resizeOverrides, onResizeHandlePointerDown: editable ? onResizeHandlePointerDown : void 0, onEditorCommit: handleEditorCommit, onEditorCancel: handleEditorCancel, onEdgeEditorCommit: handleEdgeEditorCommit, onEdgeEditorCancel: handleEdgeEditorCancel, pendingEdge: editable ? pendingEdge : null, onConnectionHandlePointerDown: editable ? onConnectionHandlePointerDown : void 0, edgeCreateEnabled: editable }), showLaneHeaders && (0, import_jsx_runtime20.jsx)(LaneHeaders, { columns: currentCanvas.columns, rows: currentCanvas.rows, theme, getViewport: getViewportState, width: containerSize.width, height: containerSize.height, pinned: laneHeaders === "pinned" }), editable && showNodeToolbar && selectedResolvedNode && !editingId && (0, import_jsx_runtime20.jsx)(NodeToolbar, { node: selectedResolvedNode, theme, onPatch: (update) => {
20113
+ }, children: "Loading..." }), (0, import_jsx_runtime27.jsx)(Viewport, { ref: viewportHandleRef, nodes, edges, nodeMap, theme, edgeStyle, columns: currentCanvas.columns, rows: currentCanvas.rows, canvases, minZoom: effectiveMinZoom, maxZoom: effectiveMaxZoom, defaultViewport, autoFit, canvasRef: currentCanvasRef, handoffTransform: pendingHandoff, onHandoffApplied: handleHandoffApplied, handoffFadeMs: zoomNavConfig.fadeDuration, onViewportChange: handleViewportChange, onNodeClick: handleNodeClick, onNodeDoubleClick: handleNodeDoubleClick, onNodeNavigate: handleNodeNavigate, onEdgeClick: handleEdgeClick, onEdgeDoubleClick: handleEdgeDoubleClick, onCanvasClick: editable ? handleCanvasClick : void 0, onCanvasContextMenu: handleCanvasContextMenu, onNodeContextMenu: handleNodeContextMenu, onEdgeContextMenu: handleEdgeContextMenu, onNodePointerDown: editable ? onNodePointerDown : void 0, selectedId: editable ? selectedId : null, editingId: editable ? editingId : null, selectedEdgeId: editable ? selectedEdgeId : null, editingEdgeId: editable ? editingEdgeId : null, dragOverrides, resizeOverrides, onResizeHandlePointerDown: editable ? onResizeHandlePointerDown : void 0, onEditorCommit: handleEditorCommit, onEditorCancel: handleEditorCancel, onEdgeEditorCommit: handleEdgeEditorCommit, onEdgeEditorCancel: handleEdgeEditorCancel, pendingEdge: editable ? pendingEdge : null, onConnectionHandlePointerDown: editable ? onConnectionHandlePointerDown : void 0, edgeCreateEnabled: editable }), showLaneHeaders && (0, import_jsx_runtime27.jsx)(LaneHeaders, { columns: currentCanvas.columns, rows: currentCanvas.rows, theme, getViewport: getViewportState, width: containerSize.width, height: containerSize.height, pinned: laneHeaders === "pinned" }), editable && showNodeToolbar && selectedResolvedNode && !editingId && (0, import_jsx_runtime27.jsx)(NodeToolbar, { node: selectedResolvedNode, theme, onPatch: (update) => {
19157
20114
  onNodeUpdate?.(selectedResolvedNode.id, update, currentCanvasRef);
19158
20115
  }, onDelete: () => {
19159
20116
  onNodeDelete?.(selectedResolvedNode.id, currentCanvasRef);
19160
20117
  setSelectedId(null);
19161
- }, getViewport: getViewportState, containerWidth: containerSize.width, containerHeight: containerSize.height, render: renderNodeToolbar }), editable && (renderAddNodeButton ? renderAddNodeButton(renderProps) : (0, import_jsx_runtime20.jsx)(AddNodeButton, { ...renderProps }))] });
20118
+ }, getViewport: getViewportState, containerWidth: containerSize.width, containerHeight: containerSize.height, render: renderNodeToolbar }), editable && (renderAddNodeButton ? renderAddNodeButton(renderProps) : (0, import_jsx_runtime27.jsx)(AddNodeButton, { ...renderProps }))] });
19162
20119
  });
19163
20120
 
19164
20121
  // src/index.tsx
@@ -19269,7 +20226,7 @@ var SystemCanvas = (() => {
19269
20226
  onEdgeUpdate: handleEdgeUpdate,
19270
20227
  onEdgeDelete: handleEdgeDelete
19271
20228
  };
19272
- root2.render(import_react19.default.createElement(SystemCanvas, props));
20229
+ root2.render(import_react21.default.createElement(SystemCanvas, props));
19273
20230
  };
19274
20231
  doRender();
19275
20232
  return {