system-canvas-standalone 0.1.0 → 0.1.1

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.62;
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,359 @@ 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 wrapInset = 4;
18185
+ const wrapWidth = Math.max(0, region.width - wrapInset * 2);
18186
+ const lines = wrapTextWithBreaks(displayValue, wrapWidth, fontSize, maxLines);
18187
+ if (lines.length === 0)
18188
+ return null;
18189
+ const baseY = region.y + fontSize;
18190
+ const clipId = `sc-text-clip-${safeId}`;
18191
+ 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)) }) })] });
18192
+ }
18193
+ function GradientDef({ id: id2, fill }) {
18194
+ const angle = fill.angle ?? 0;
18195
+ 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 })] });
18196
+ }
18197
+
18198
+ // ../react/dist/primitives/NodeStatusPill.js
18199
+ var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
18200
+ function NodeStatusPill({ region, value, theme, color: color2, textColor, fill }) {
18201
+ const label = value.toUpperCase();
18202
+ if (!label)
18203
+ return null;
18204
+ const h = Math.min(region.height, 18);
18205
+ const fontSize = Math.max(9, h * 0.62);
18206
+ const padX = 8;
18207
+ const w = Math.max(h * 1.6, label.length * fontSize * 0.62 + padX * 2);
18208
+ const x = region.x + region.width - w;
18209
+ const y = region.y + (region.height - h) / 2;
18210
+ const rx = h / 2;
18211
+ const cx = x + w / 2;
18212
+ const cy = y + h / 2;
18213
+ const tint = fill ?? toTint(color2);
18214
+ const fg = textColor ?? color2;
18215
+ 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 })] });
18216
+ }
18217
+ function toTint(color2) {
18218
+ if (color2.startsWith("#") && (color2.length === 7 || color2.length === 4)) {
18219
+ let r;
18220
+ let g;
18221
+ let b;
18222
+ if (color2.length === 7) {
18223
+ r = parseInt(color2.slice(1, 3), 16);
18224
+ g = parseInt(color2.slice(3, 5), 16);
18225
+ b = parseInt(color2.slice(5, 7), 16);
18226
+ } else {
18227
+ r = parseInt(color2[1] + color2[1], 16);
18228
+ g = parseInt(color2[2] + color2[2], 16);
18229
+ b = parseInt(color2[3] + color2[3], 16);
18230
+ }
18231
+ if (!Number.isNaN(r) && !Number.isNaN(g) && !Number.isNaN(b)) {
18232
+ return `rgba(${r}, ${g}, ${b}, 0.15)`;
18233
+ }
18234
+ }
18235
+ return "rgba(255, 255, 255, 0.08)";
18236
+ }
18237
+
18238
+ // ../react/dist/components/CategorySlotsLayer.js
18239
+ function CategorySlotsLayer({ node, theme, canvases, slots: slotsProp }) {
18240
+ const slots = slotsProp ?? getCategorySlots(node, theme);
18241
+ const regions = (0, import_react10.useMemo)(() => computeCategorySlotRegions(node, theme, slots), [node, theme, slots]);
18242
+ const reactId = (0, import_react10.useId)();
18243
+ const clipId = `sc-edge-clip-${reactId.replace(/:/g, "")}`;
18244
+ if (!slots)
18245
+ return null;
18246
+ const getSubCanvas = (ref) => canvases?.[ref];
18247
+ const entries = slotEntries(slots);
18248
+ if (entries.length === 0)
18249
+ return null;
18250
+ const edgeEntries = entries.filter(([p]) => isEdgePosition(p));
18251
+ const otherEntries = entries.filter(([p]) => !isEdgePosition(p));
18252
+ const renderEntry = ([position, spec]) => {
18253
+ const region = regions[position];
18254
+ const ctx = {
18255
+ node,
18256
+ theme,
18257
+ region,
18258
+ getSubCanvas,
18259
+ canvases,
18260
+ rollup: (predicate) => node.ref ? rollupNodes(getSubCanvas(node.ref), predicate) : { total: 0, matched: 0, fraction: 0 }
18261
+ };
18262
+ return (0, import_jsx_runtime9.jsx)(SlotView, { position, spec, ctx }, position);
18263
+ };
18264
+ 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)] });
18265
+ }
18266
+ function isEdgePosition(p) {
18267
+ return p === "topEdge" || p === "bottomEdge" || p === "leftEdge" || p === "rightEdge";
18268
+ }
18269
+ function SlotView({ position, spec, ctx }) {
18270
+ try {
18271
+ return renderSlot(position, spec, ctx);
18272
+ } catch (err) {
18273
+ const env = globalThis.process?.env?.NODE_ENV;
18274
+ if (env !== "production") {
18275
+ console.warn("[system-canvas] slot render failed", position, err);
18276
+ }
18277
+ return null;
18278
+ }
18279
+ }
18280
+ function renderSlot(position, spec, ctx) {
18281
+ const { theme, node, region } = ctx;
18282
+ const nodeColor = node.resolvedStroke;
18283
+ switch (spec.kind) {
18284
+ case "color": {
18285
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18286
+ const length = resolveAccessorOr(spec.length, 0.55, ctx);
18287
+ return (0, import_jsx_runtime9.jsx)(NodeColorFill, { region, color: color2, position, extent: spec.extent, length });
18288
+ }
18289
+ case "progress": {
18290
+ const value = resolveAccessor(spec.value, ctx);
18291
+ if (spec.hideWhenZero && (!Number.isFinite(value) || value <= 0)) {
18292
+ return null;
18293
+ }
18294
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18295
+ const bgColor = resolveAccessorOr(spec.bgColor, "rgba(255,255,255,0.08)", ctx);
18296
+ return (0, import_jsx_runtime9.jsx)(NodeProgressBar, { region, value, color: color2, bgColor });
18297
+ }
18298
+ case "count": {
18299
+ const raw = resolveAccessor(spec.value, ctx);
18300
+ const hideWhenEmpty = spec.hideWhenEmpty !== false;
18301
+ if (hideWhenEmpty) {
18302
+ if (raw === 0 || raw === "" || raw == null)
18303
+ return null;
18304
+ }
18305
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18306
+ const textColor = resolveAccessorOr(spec.textColor, theme.background, ctx);
18307
+ return (0, import_jsx_runtime9.jsx)(NodeCountBadge, { region, value: raw, theme, color: color2, textColor, position });
18308
+ }
18309
+ case "pill": {
18310
+ const value = resolveAccessor(spec.value, ctx);
18311
+ if (!value)
18312
+ return null;
18313
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18314
+ const textColor = spec.textColor ? resolveAccessor(spec.textColor, ctx) : void 0;
18315
+ const fill = spec.fill ? resolveAccessor(spec.fill, ctx) : void 0;
18316
+ return (0, import_jsx_runtime9.jsx)(NodeStatusPill, { region, value, theme, color: color2, textColor, fill });
18317
+ }
18318
+ case "text": {
18319
+ const value = resolveAccessor(spec.value, ctx);
18320
+ const color2 = resolveAccessorOr(spec.color, position === "body" ? theme.node.labelColor : theme.node.sublabelColor, ctx);
18321
+ const defaultAlign = position === "header" || position === "footer" || position === "body" ? "start" : "center";
18322
+ const align = spec.align ?? defaultAlign;
18323
+ const isHeader = position === "header";
18324
+ const isBody = position === "body";
18325
+ const defaultWeight = isHeader ? 700 : isBody ? 600 : 500;
18326
+ const defaultUppercase = isHeader;
18327
+ const defaultUseLabelFont = isHeader || isBody;
18328
+ const defaultFontSize = isBody ? Math.round(theme.node.fontSize * 1.35) : void 0;
18329
+ const fontSize = spec.fontSize !== void 0 ? resolveAccessor(spec.fontSize, ctx) : defaultFontSize;
18330
+ const fontWeight = spec.fontWeight !== void 0 ? resolveAccessor(spec.fontWeight, ctx) : defaultWeight;
18331
+ const fontFamily = spec.fontFamily !== void 0 ? resolveAccessor(spec.fontFamily, ctx) : void 0;
18332
+ const wrap = spec.wrap ?? isBody;
18333
+ const lineHeight = spec.lineHeight !== void 0 ? resolveAccessor(spec.lineHeight, ctx) : void 0;
18334
+ const fill = spec.fill !== void 0 ? resolveAccessor(spec.fill, ctx) : void 0;
18335
+ 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 });
18336
+ }
18337
+ case "dot": {
18338
+ const color2 = resolveAccessorOr(spec.color, nodeColor, ctx);
18339
+ return (0, import_jsx_runtime9.jsx)(NodeDot, { region, color: color2 });
18340
+ }
18341
+ case "custom": {
18342
+ return spec.render(ctx);
18343
+ }
18344
+ }
18345
+ }
18346
+
18347
+ // ../react/dist/components/refCorner.js
18348
+ function toKebabCorner(c) {
18349
+ switch (c) {
18350
+ case "topLeft":
18351
+ return "top-left";
18352
+ case "topRight":
18353
+ return "top-right";
18354
+ case "bottomLeft":
18355
+ return "bottom-left";
18356
+ case "bottomRight":
18357
+ return "bottom-right";
18358
+ }
18359
+ }
18360
+
17630
18361
  // ../react/dist/components/TextNode.js
17631
- function TextNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing }) {
18362
+ function TextNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "bottomRight" }) {
17632
18363
  const { x, y, width, height } = node;
17633
- const cx = x + width / 2;
18364
+ const contentX = x + reservedLeft;
18365
+ const contentY = y + reservedTop;
18366
+ const contentWidth = Math.max(0, width - reservedLeft - reservedRight);
18367
+ const contentHeight = Math.max(0, height - reservedTop - reservedBottom);
17634
18368
  const text = node.text ?? "";
17635
18369
  const lines = text.split("\n").filter(Boolean);
17636
18370
  const mainLabel = lines[0] ?? node.id;
17637
18371
  const sublabel = lines[1];
17638
- const lineHeight = theme.node.fontSize + 4;
18372
+ const hasBodySlot = slots?.body !== void 0;
18373
+ const hasHeader = reservedTop > 0;
18374
+ const labelFont = theme.node.labelFont ?? theme.node.fontFamily;
18375
+ const labelFontSize = theme.node.fontSize + (hasHeader ? 1 : 0);
18376
+ const lineHeight = labelFontSize + 4;
17639
18377
  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 })] });
18378
+ const labelAnchor = hasHeader ? "start" : "middle";
18379
+ const labelX = hasHeader ? contentX : contentX + contentWidth / 2;
18380
+ const textStartY = hasHeader ? contentY + labelFontSize + 2 : contentY + (contentHeight - totalTextHeight) / 2 + labelFontSize;
18381
+ 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
18382
  }
17643
18383
 
17644
18384
  // ../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 }) {
18385
+ var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
18386
+ function FileNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "bottomRight" }) {
17647
18387
  const { x, y, width, height } = node;
17648
18388
  const filePath = node.file ?? "";
17649
18389
  const fileName = filePath.split("/").pop() ?? filePath;
17650
18390
  const dirPath = filePath.includes("/") ? filePath.slice(0, filePath.lastIndexOf("/")) : "";
17651
18391
  const subpath = node.subpath ?? "";
17652
18392
  const fold = 10;
17653
- const textPadding = 10;
17654
- const maxTextWidth = width - textPadding * 2 - fold;
18393
+ const textPadding = 10 + reservedLeft;
18394
+ const maxTextWidth = width - textPadding - reservedRight - fold;
18395
+ const contentY = y + reservedTop;
18396
+ const contentHeight = Math.max(0, height - reservedTop - reservedBottom);
17655
18397
  const shapePath = [
17656
18398
  `M ${x + 2} ${y}`,
17657
18399
  `L ${x + width - fold} ${y}`,
@@ -17668,35 +18410,41 @@ var SystemCanvas = (() => {
17668
18410
  const strokeColor = node.resolvedStroke;
17669
18411
  const thinStroke = 0.75;
17670
18412
  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 })] });
18413
+ 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
18414
  }
17673
18415
 
17674
18416
  // ../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 }) {
18417
+ var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
18418
+ function LinkNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "bottomRight" }) {
17677
18419
  const { x, y, width, height } = node;
17678
- const cx = x + width / 2;
18420
+ const contentX = x + reservedLeft;
18421
+ const contentY = y + reservedTop;
18422
+ const contentWidth = Math.max(0, width - reservedLeft - reservedRight);
18423
+ const contentHeight = Math.max(0, height - reservedTop - reservedBottom);
18424
+ const cx = contentX + contentWidth / 2;
17679
18425
  let displayUrl = node.url ?? "";
17680
18426
  try {
17681
18427
  const url = new URL(displayUrl);
17682
18428
  displayUrl = url.hostname;
17683
18429
  } catch {
17684
18430
  }
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 })] });
18431
+ 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
18432
  }
17687
18433
 
17688
18434
  // ../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 }) {
18435
+ var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
18436
+ function GroupNode({ node, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, isSelected, isEditing, slots, canvases, reservedTop = 0, reservedBottom = 0, reservedLeft = 0, reservedRight = 0, refCorner = "topRight" }) {
17691
18437
  const { x, y, width, height } = node;
17692
18438
  const stroke = node.color ? node.resolvedStroke : theme.group.stroke;
17693
18439
  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 })] });
18440
+ const labelX = x + 12 + reservedLeft;
18441
+ const labelY = y + reservedTop + theme.group.labelFontSize + 8;
18442
+ 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
18443
  }
17696
18444
 
17697
18445
  // ../react/dist/components/ResizeHandles.js
17698
- var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
17699
- var import_react9 = __toESM(require_react(), 1);
18446
+ var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
18447
+ var import_react11 = __toESM(require_react(), 1);
17700
18448
  var HANDLE_SIZE = 7;
17701
18449
  var CORNERS = [
17702
18450
  { corner: "nw", cursor: "nwse-resize", anchor: "nw" },
@@ -17707,7 +18455,7 @@ var SystemCanvas = (() => {
17707
18455
  var cornerInset = (cornerRadius) => Math.min(cornerRadius * 0.25, 3);
17708
18456
  function ResizeHandles({ node, theme, onHandlePointerDown }) {
17709
18457
  const { x, y, width, height } = node;
17710
- const [hoveredCorner, setHoveredCorner] = (0, import_react9.useState)(null);
18458
+ const [hoveredCorner, setHoveredCorner] = (0, import_react11.useState)(null);
17711
18459
  const handleColor = node.resolvedStroke ?? theme.node.labelColor;
17712
18460
  const i = cornerInset(node.resolvedCornerRadius);
17713
18461
  const anchorPos = (anchor) => {
@@ -17722,36 +18470,54 @@ var SystemCanvas = (() => {
17722
18470
  return { cx: x + width - i, cy: y + height - i };
17723
18471
  }
17724
18472
  };
17725
- return (0, import_jsx_runtime7.jsx)("g", { className: "system-canvas-resize-handles", pointerEvents: "all", children: CORNERS.map(({ corner, cursor, anchor }) => {
18473
+ return (0, import_jsx_runtime14.jsx)("g", { className: "system-canvas-resize-handles", pointerEvents: "all", children: CORNERS.map(({ corner, cursor, anchor }) => {
17726
18474
  const { cx, cy } = anchorPos(anchor);
17727
18475
  const isHovered = hoveredCorner === corner;
17728
18476
  const s = isHovered ? HANDLE_SIZE + 2 : HANDLE_SIZE;
17729
18477
  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);
18478
+ 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
18479
  }) });
17732
18480
  }
17733
18481
 
17734
18482
  // ../react/dist/components/NodeRenderer.js
17735
- function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedId, editingId, onResizeHandlePointerDown, only }) {
18483
+ function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedId, editingId, onResizeHandlePointerDown, canvases, only }) {
17736
18484
  const groups = nodes.filter((n) => n.type === "group");
17737
18485
  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
- });
18486
+ const common = (node) => {
18487
+ const slots = getCategorySlots(node, theme);
18488
+ const reservations = computeReflowReservations(node, theme, slots);
18489
+ const defaultCorner = node.type === "group" ? "topRight" : "bottomRight";
18490
+ const refCorner = pickRefIndicatorCorner(defaultCorner, slots);
18491
+ return {
18492
+ node,
18493
+ theme,
18494
+ onClick,
18495
+ onDoubleClick,
18496
+ onContextMenu,
18497
+ onNavigate,
18498
+ onPointerDown,
18499
+ isSelected: selectedId === node.id,
18500
+ isEditing: editingId === node.id,
18501
+ slots,
18502
+ canvases,
18503
+ reservedTop: reservations.top,
18504
+ reservedBottom: reservations.bottom,
18505
+ reservedLeft: reservations.left,
18506
+ reservedRight: reservations.right,
18507
+ refCorner
18508
+ };
18509
+ };
17749
18510
  const selectedNode = selectedId && editingId !== selectedId ? nodes.find((n) => n.id === selectedId) : void 0;
17750
18511
  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) => {
18512
+ 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
18513
  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 })] });
18514
+ return (0, import_jsx_runtime15.jsx)(Component, { ...common(node) }, node.id);
18515
+ }), renderResizeHandles && (0, import_jsx_runtime15.jsx)(ResizeHandles, { node: selectedNode, theme, onHandlePointerDown: onResizeHandlePointerDown }), renderResizeHandles && (() => {
18516
+ const slots = getCategorySlots(selectedNode, theme);
18517
+ if (!slots?.topRightOuter)
18518
+ return null;
18519
+ return (0, import_jsx_runtime15.jsx)(CategorySlotsLayer, { node: selectedNode, theme, canvases, slots: { topRightOuter: slots.topRightOuter } });
18520
+ })()] });
17755
18521
  }
17756
18522
  function getNodeComponent(type) {
17757
18523
  switch (type) {
@@ -17765,9 +18531,9 @@ var SystemCanvas = (() => {
17765
18531
  }
17766
18532
 
17767
18533
  // ../react/dist/components/EdgeRenderer.js
17768
- var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
18534
+ var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
17769
18535
  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) => {
18536
+ 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
18537
  const aSel = selectedId === a.edge.id ? 2 : 0;
17772
18538
  const bSel = selectedId === b.edge.id ? 2 : 0;
17773
18539
  const aCol = a.edge.color ? 1 : 0;
@@ -17792,20 +18558,36 @@ var SystemCanvas = (() => {
17792
18558
  const toEnd = edge.toEnd ?? "arrow";
17793
18559
  const fromEnd = edge.fromEnd ?? "none";
17794
18560
  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);
18561
+ 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
18562
  })] });
17797
18563
  }
17798
18564
 
17799
18565
  // ../react/dist/components/NodeEditor.js
17800
- var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
17801
- var import_react10 = __toESM(require_react(), 1);
18566
+ var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
18567
+ var import_react12 = __toESM(require_react(), 1);
17802
18568
  function NodeEditor({ node, theme, onCommit, onCancel }) {
18569
+ const editableFields = useCategoryFields(node, theme);
18570
+ if (editableFields) {
18571
+ return (0, import_jsx_runtime17.jsx)(FormEditor, { node, theme, fields: editableFields, onCommit, onCancel });
18572
+ }
18573
+ return (0, import_jsx_runtime17.jsx)(SingleFieldEditor, { node, theme, onCommit, onCancel });
18574
+ }
18575
+ function useCategoryFields(node, theme) {
18576
+ if (!node.category)
18577
+ return null;
18578
+ const def = theme.categories[node.category];
18579
+ const fields = def?.editableFields;
18580
+ if (!fields || fields.length === 0)
18581
+ return null;
18582
+ return fields;
18583
+ }
18584
+ function SingleFieldEditor({ node, theme, onCommit, onCancel }) {
17803
18585
  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)(() => {
18586
+ const [value, setValue] = (0, import_react12.useState)(initial);
18587
+ const textareaRef = (0, import_react12.useRef)(null);
18588
+ const inputRef = (0, import_react12.useRef)(null);
18589
+ const committedRef = (0, import_react12.useRef)(false);
18590
+ (0, import_react12.useEffect)(() => {
17809
18591
  const el = textareaRef.current ?? inputRef.current;
17810
18592
  if (el) {
17811
18593
  el.focus();
@@ -17821,7 +18603,7 @@ var SystemCanvas = (() => {
17821
18603
  onCancel();
17822
18604
  return;
17823
18605
  }
17824
- onCommit(buildPatch(node, value));
18606
+ onCommit(buildSingleFieldPatch(node, value));
17825
18607
  };
17826
18608
  const cancel = () => {
17827
18609
  if (committedRef.current)
@@ -17850,7 +18632,7 @@ var SystemCanvas = (() => {
17850
18632
  resize: "none",
17851
18633
  textAlign: node.type === "text" ? "center" : "left"
17852
18634
  };
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) => {
18635
+ 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
18636
  if (e.key === "Enter" && !e.shiftKey) {
17855
18637
  e.preventDefault();
17856
18638
  commit();
@@ -17858,7 +18640,7 @@ var SystemCanvas = (() => {
17858
18640
  e.preventDefault();
17859
18641
  cancel();
17860
18642
  }
17861
- }, style: commonFieldStyle }) : (0, import_jsx_runtime10.jsx)("input", { ref: inputRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
18643
+ }, style: commonFieldStyle }) : (0, import_jsx_runtime17.jsx)("input", { ref: inputRef, value, onChange: (e) => setValue(e.target.value), onBlur: commit, onKeyDown: (e) => {
17862
18644
  if (e.key === "Enter") {
17863
18645
  e.preventDefault();
17864
18646
  commit();
@@ -17882,7 +18664,7 @@ var SystemCanvas = (() => {
17882
18664
  return "";
17883
18665
  }
17884
18666
  }
17885
- function buildPatch(node, value) {
18667
+ function buildSingleFieldPatch(node, value) {
17886
18668
  switch (node.type) {
17887
18669
  case "text":
17888
18670
  return { text: value };
@@ -17896,16 +18678,176 @@ var SystemCanvas = (() => {
17896
18678
  return {};
17897
18679
  }
17898
18680
  }
18681
+ function FormEditor({ node, theme, fields, onCommit, onCancel }) {
18682
+ const initial = (0, import_react12.useMemo)(() => readInitialValues(node, fields), [node, fields]);
18683
+ const [values, setValues] = (0, import_react12.useState)(initial);
18684
+ const committedRef = (0, import_react12.useRef)(false);
18685
+ const panelRef = (0, import_react12.useRef)(null);
18686
+ const width = Math.max(node.width, 240);
18687
+ const height = Math.max(node.height, 36 + fields.length * 44);
18688
+ const commit = () => {
18689
+ if (committedRef.current)
18690
+ return;
18691
+ committedRef.current = true;
18692
+ const patch = buildFormPatch(node, fields, initial, values);
18693
+ if (!patch) {
18694
+ onCancel();
18695
+ return;
18696
+ }
18697
+ onCommit(patch);
18698
+ };
18699
+ const cancel = () => {
18700
+ if (committedRef.current)
18701
+ return;
18702
+ committedRef.current = true;
18703
+ onCancel();
18704
+ };
18705
+ const onBlurPanel = (e) => {
18706
+ const next = e.relatedTarget;
18707
+ if (next && panelRef.current?.contains(next))
18708
+ return;
18709
+ commit();
18710
+ };
18711
+ const stopPointer = (e) => {
18712
+ e.stopPropagation();
18713
+ };
18714
+ (0, import_react12.useEffect)(() => {
18715
+ const el = panelRef.current;
18716
+ if (!el)
18717
+ return;
18718
+ const first = el.querySelector("input, textarea, select, button");
18719
+ first?.focus();
18720
+ if (first instanceof HTMLInputElement && first.type === "text") {
18721
+ const end = first.value.length;
18722
+ first.setSelectionRange(end, end);
18723
+ }
18724
+ }, []);
18725
+ const fieldLabelColor = theme.node.sublabelColor;
18726
+ const inputStyle = {
18727
+ width: "100%",
18728
+ boxSizing: "border-box",
18729
+ padding: "6px 8px",
18730
+ fontFamily: theme.node.fontFamily,
18731
+ fontSize: theme.node.fontSize - 1,
18732
+ background: theme.background,
18733
+ color: theme.node.labelColor,
18734
+ border: `1px solid ${theme.breadcrumbs.separatorColor}`,
18735
+ borderRadius: 6,
18736
+ outline: "none"
18737
+ };
18738
+ 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) => {
18739
+ if (e.key === "Escape") {
18740
+ e.preventDefault();
18741
+ cancel();
18742
+ } else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
18743
+ e.preventDefault();
18744
+ commit();
18745
+ } else if (e.key === "Enter" && !(e.target instanceof HTMLTextAreaElement)) {
18746
+ e.preventDefault();
18747
+ const panel = panelRef.current;
18748
+ if (!panel)
18749
+ return;
18750
+ const focusables = Array.from(panel.querySelectorAll("input, textarea, select"));
18751
+ const idx = focusables.indexOf(e.target);
18752
+ if (idx === -1 || idx === focusables.length - 1)
18753
+ commit();
18754
+ else
18755
+ focusables[idx + 1].focus();
18756
+ }
18757
+ }, style: {
18758
+ width: "100%",
18759
+ boxSizing: "border-box",
18760
+ display: "flex",
18761
+ flexDirection: "column",
18762
+ gap: 8,
18763
+ padding: 10,
18764
+ background: theme.breadcrumbs.background,
18765
+ color: theme.node.labelColor,
18766
+ border: `1.5px solid ${theme.node.labelColor}`,
18767
+ borderRadius: node.resolvedCornerRadius,
18768
+ boxShadow: "0 4px 12px rgba(0,0,0,0.25)",
18769
+ backdropFilter: "blur(8px)",
18770
+ fontFamily: theme.node.fontFamily
18771
+ }, children: fields.map((field) => {
18772
+ const v = values[field.path];
18773
+ const setV = (next) => setValues((prev) => ({ ...prev, [field.path]: next }));
18774
+ const label = field.label ?? field.path;
18775
+ return (0, import_jsx_runtime17.jsxs)("label", { style: {
18776
+ display: "flex",
18777
+ flexDirection: "column",
18778
+ gap: 3,
18779
+ fontSize: 10,
18780
+ color: fieldLabelColor,
18781
+ textTransform: "uppercase",
18782
+ letterSpacing: 0.5
18783
+ }, children: [(0, import_jsx_runtime17.jsx)("span", { children: label }), renderControl(field, v, setV, inputStyle, theme)] }, field.path);
18784
+ }) }) });
18785
+ }
18786
+ function renderControl(field, value, setValue, inputStyle, theme) {
18787
+ switch (field.kind) {
18788
+ case "textarea":
18789
+ 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 } });
18790
+ case "number":
18791
+ 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) => {
18792
+ const v = e.target.value;
18793
+ setValue(v === "" ? void 0 : Number(v));
18794
+ }, placeholder: field.placeholder, style: inputStyle });
18795
+ case "select":
18796
+ 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))] });
18797
+ case "boolean":
18798
+ return (0, import_jsx_runtime17.jsx)("input", { type: "checkbox", checked: !!value, onChange: (e) => setValue(e.target.checked), style: {
18799
+ alignSelf: "flex-start",
18800
+ width: 16,
18801
+ height: 16,
18802
+ accentColor: theme.node.labelColor
18803
+ } });
18804
+ case "text":
18805
+ default:
18806
+ return (0, import_jsx_runtime17.jsx)("input", { type: "text", value: value ?? "", onChange: (e) => setValue(e.target.value), placeholder: field.placeholder, style: inputStyle });
18807
+ }
18808
+ }
18809
+ function readInitialValues(node, fields) {
18810
+ const out = {};
18811
+ for (const f of fields) {
18812
+ out[f.path] = getAtPath(node, f.path);
18813
+ }
18814
+ return out;
18815
+ }
18816
+ function buildFormPatch(node, fields, initial, current) {
18817
+ let changed = false;
18818
+ for (const f of fields) {
18819
+ if (current[f.path] !== initial[f.path]) {
18820
+ changed = true;
18821
+ break;
18822
+ }
18823
+ }
18824
+ if (!changed)
18825
+ return null;
18826
+ let working = { ...node };
18827
+ for (const f of fields) {
18828
+ working = setAtPath(working, f.path, current[f.path]);
18829
+ }
18830
+ const patch = {};
18831
+ const topKeys = /* @__PURE__ */ new Set();
18832
+ for (const f of fields) {
18833
+ topKeys.add(f.path.split(".")[0]);
18834
+ }
18835
+ for (const k of topKeys) {
18836
+ ;
18837
+ patch[k] = working[k];
18838
+ }
18839
+ return patch;
18840
+ }
17899
18841
 
17900
18842
  // ../react/dist/components/EdgeLabelEditor.js
17901
- var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
17902
- var import_react11 = __toESM(require_react(), 1);
18843
+ var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
18844
+ var import_react13 = __toESM(require_react(), 1);
17903
18845
  var EDITOR_WIDTH = 110;
17904
18846
  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)(() => {
18847
+ const [value, setValue] = (0, import_react13.useState)(initialLabel);
18848
+ const inputRef = (0, import_react13.useRef)(null);
18849
+ const committedRef = (0, import_react13.useRef)(false);
18850
+ (0, import_react13.useEffect)(() => {
17909
18851
  const el = inputRef.current;
17910
18852
  if (el) {
17911
18853
  el.focus();
@@ -17934,7 +18876,7 @@ var SystemCanvas = (() => {
17934
18876
  };
17935
18877
  const fontSize = theme.edge.labelFontSize;
17936
18878
  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) => {
18879
+ 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
18880
  if (e.key === "Enter") {
17939
18881
  e.preventDefault();
17940
18882
  commit();
@@ -17959,8 +18901,8 @@ var SystemCanvas = (() => {
17959
18901
  }
17960
18902
 
17961
18903
  // ../react/dist/components/ConnectionHandles.js
17962
- var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
17963
- var import_react12 = __toESM(require_react(), 1);
18904
+ var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
18905
+ var import_react14 = __toESM(require_react(), 1);
17964
18906
  var SIDES = ["top", "right", "bottom", "left"];
17965
18907
  var HANDLE_RADIUS = 4;
17966
18908
  var HANDLE_HIT_RADIUS = 10;
@@ -17969,9 +18911,9 @@ var SystemCanvas = (() => {
17969
18911
  var HOVER_SCALE = 1.42;
17970
18912
  var HOVER_TRANSITION_MS = 120;
17971
18913
  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)(() => {
18914
+ const [visible, setVisible] = (0, import_react14.useState)(!!immediate);
18915
+ const [hoveredSide, setHoveredSide] = (0, import_react14.useState)(null);
18916
+ (0, import_react14.useEffect)(() => {
17975
18917
  if (immediate) {
17976
18918
  setVisible(true);
17977
18919
  return;
@@ -17982,16 +18924,16 @@ var SystemCanvas = (() => {
17982
18924
  }, [node.id, immediate]);
17983
18925
  const handleColor = node.resolvedStroke ?? theme.node.labelColor;
17984
18926
  const sidesToRender = immediate ? SIDES : activeSide ? [activeSide] : [];
17985
- return (0, import_jsx_runtime12.jsx)("g", { className: "system-canvas-connection-handles", pointerEvents: "auto", style: {
18927
+ return (0, import_jsx_runtime19.jsx)("g", { className: "system-canvas-connection-handles", pointerEvents: "auto", style: {
17986
18928
  opacity: visible ? 1 : 0,
17987
18929
  transition: `opacity ${FADE_DURATION_MS}ms ease-out`
17988
18930
  }, children: sidesToRender.map((side) => {
17989
18931
  const { x, y } = computeAnchorPoint(node, side);
17990
18932
  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) => {
18933
+ return (0, import_jsx_runtime19.jsxs)("g", { style: { cursor: "crosshair" }, onPointerEnter: () => setHoveredSide(side), onPointerLeave: () => setHoveredSide((s) => s === side ? null : s), onPointerDown: (e) => {
17992
18934
  e.stopPropagation();
17993
18935
  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: {
18936
+ }, 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
18937
  // Scale around the handle's own center. CSS transforms on
17996
18938
  // SVG elements use the element's user-space origin by
17997
18939
  // default, so we set transform-origin explicitly.
@@ -18003,7 +18945,7 @@ var SystemCanvas = (() => {
18003
18945
  }
18004
18946
 
18005
18947
  // ../react/dist/components/PendingEdgeRenderer.js
18006
- var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
18948
+ var import_jsx_runtime20 = __toESM(require_jsx_runtime(), 1);
18007
18949
  function PendingEdgeRenderer({ sourceNode, sourceSide, cursor, targetNode, theme, defaultEdgeStyle }) {
18008
18950
  const endNode = targetNode ?? {
18009
18951
  id: "__pending__",
@@ -18025,11 +18967,11 @@ var SystemCanvas = (() => {
18025
18967
  toNode: endNode.id
18026
18968
  };
18027
18969
  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 })] });
18970
+ 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
18971
  }
18030
18972
 
18031
18973
  // ../react/dist/components/LanesBackground.js
18032
- var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
18974
+ var import_jsx_runtime21 = __toESM(require_jsx_runtime(), 1);
18033
18975
  var LANE_EXTENT = 1e5;
18034
18976
  function LanesBackground({ columns, rows, theme }) {
18035
18977
  const hasColumns = columns && columns.length > 0;
@@ -18044,31 +18986,31 @@ var SystemCanvas = (() => {
18044
18986
  }
18045
18987
  return index % 2 === 0 ? lanesTheme.bandFillEven : lanesTheme.bandFillOdd;
18046
18988
  };
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) => {
18989
+ 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
18990
  if (i === 0)
18049
18991
  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}`);
18992
+ 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
18993
  }), hasColumns && columns.map((col, i) => {
18052
18994
  if (i === 0)
18053
18995
  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}`);
18996
+ 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
18997
  })] });
18056
18998
  }
18057
18999
 
18058
19000
  // ../react/dist/components/Viewport.js
18059
19001
  var HOVER_PADDING = 10;
18060
19002
  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) {
19003
+ 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
19004
  const { svgRef, groupRef, viewport, fitToContent, zoomToNode, setTransform } = useViewport({
18063
19005
  minZoom,
18064
19006
  maxZoom,
18065
19007
  defaultViewport,
18066
19008
  onViewportChange
18067
19009
  });
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) => {
19010
+ const navigatingRef = (0, import_react15.useRef)(false);
19011
+ const fadeRafRef = (0, import_react15.useRef)(null);
19012
+ const fadeTimeoutRef = (0, import_react15.useRef)(null);
19013
+ const triggerFade = (0, import_react15.useCallback)((durationMs) => {
18072
19014
  if (durationMs <= 0)
18073
19015
  return;
18074
19016
  const g = groupRef.current;
@@ -18091,7 +19033,7 @@ var SystemCanvas = (() => {
18091
19033
  }, durationMs + 16);
18092
19034
  });
18093
19035
  }, []);
18094
- (0, import_react13.useEffect)(() => {
19036
+ (0, import_react15.useEffect)(() => {
18095
19037
  return () => {
18096
19038
  if (fadeRafRef.current !== null)
18097
19039
  cancelAnimationFrame(fadeRafRef.current);
@@ -18099,9 +19041,9 @@ var SystemCanvas = (() => {
18099
19041
  clearTimeout(fadeTimeoutRef.current);
18100
19042
  };
18101
19043
  }, []);
18102
- const [hoveredNodeId, setHoveredNodeId] = (0, import_react13.useState)(null);
18103
- const [hoveredSide, setHoveredSide] = (0, import_react13.useState)(null);
18104
- (0, import_react13.useImperativeHandle)(ref, () => ({
19044
+ const [hoveredNodeId, setHoveredNodeId] = (0, import_react15.useState)(null);
19045
+ const [hoveredSide, setHoveredSide] = (0, import_react15.useState)(null);
19046
+ (0, import_react15.useImperativeHandle)(ref, () => ({
18105
19047
  zoomToNode: (node, onComplete, options) => {
18106
19048
  navigatingRef.current = true;
18107
19049
  zoomToNode(node, onComplete, options);
@@ -18111,7 +19053,7 @@ var SystemCanvas = (() => {
18111
19053
  getSvgElement: () => svgRef.current,
18112
19054
  getViewport: () => viewport.current ?? { x: 0, y: 0, zoom: 1 }
18113
19055
  }));
18114
- const renderNodes = (0, import_react13.useMemo)(() => {
19056
+ const renderNodes = (0, import_react15.useMemo)(() => {
18115
19057
  const hasDrag = dragOverrides && dragOverrides.size > 0;
18116
19058
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
18117
19059
  if (!hasDrag && !hasResize)
@@ -18124,7 +19066,7 @@ var SystemCanvas = (() => {
18124
19066
  return d ? { ...n, x: d.x, y: d.y } : n;
18125
19067
  });
18126
19068
  }, [nodes, dragOverrides, resizeOverrides]);
18127
- const renderNodeMap = (0, import_react13.useMemo)(() => {
19069
+ const renderNodeMap = (0, import_react15.useMemo)(() => {
18128
19070
  const hasDrag = dragOverrides && dragOverrides.size > 0;
18129
19071
  const hasResize = resizeOverrides && resizeOverrides.size > 0;
18130
19072
  if (!hasDrag && !hasResize)
@@ -18135,11 +19077,11 @@ var SystemCanvas = (() => {
18135
19077
  }
18136
19078
  return m;
18137
19079
  }, [renderNodes, nodeMap, dragOverrides, resizeOverrides]);
18138
- const latestNodesRef = (0, import_react13.useRef)(nodes);
18139
- (0, import_react13.useEffect)(() => {
19080
+ const latestNodesRef = (0, import_react15.useRef)(nodes);
19081
+ (0, import_react15.useEffect)(() => {
18140
19082
  latestNodesRef.current = nodes;
18141
19083
  }, [nodes]);
18142
- const fitNow = (0, import_react13.useCallback)(() => {
19084
+ const fitNow = (0, import_react15.useCallback)(() => {
18143
19085
  const current = latestNodesRef.current;
18144
19086
  if (current.length === 0)
18145
19087
  return;
@@ -18149,7 +19091,7 @@ var SystemCanvas = (() => {
18149
19091
  fitToContent(current, animate);
18150
19092
  });
18151
19093
  }, [fitToContent]);
18152
- (0, import_react13.useEffect)(() => {
19094
+ (0, import_react15.useEffect)(() => {
18153
19095
  if (defaultViewport)
18154
19096
  return;
18155
19097
  if (autoFit !== "always")
@@ -18158,8 +19100,8 @@ var SystemCanvas = (() => {
18158
19100
  return;
18159
19101
  fitNow();
18160
19102
  }, [nodes, autoFit, defaultViewport, fitNow]);
18161
- const fittedForRef = (0, import_react13.useRef)(null);
18162
- (0, import_react13.useEffect)(() => {
19103
+ const fittedForRef = (0, import_react15.useRef)(null);
19104
+ (0, import_react15.useEffect)(() => {
18163
19105
  if (defaultViewport)
18164
19106
  return;
18165
19107
  if (autoFit !== "canvas-change" && autoFit !== "initial")
@@ -18191,7 +19133,7 @@ var SystemCanvas = (() => {
18191
19133
  triggerFade
18192
19134
  ]);
18193
19135
  const editingNode = editingId ? renderNodes.find((n) => n.id === editingId) ?? null : null;
18194
- const handleSvgPointerMove = (0, import_react13.useCallback)((event) => {
19136
+ const handleSvgPointerMove = (0, import_react15.useCallback)((event) => {
18195
19137
  if (!edgeCreateEnabled)
18196
19138
  return;
18197
19139
  const svg = svgRef.current;
@@ -18240,7 +19182,7 @@ var SystemCanvas = (() => {
18240
19182
  setHoveredSide((prev) => prev === null ? prev : null);
18241
19183
  }
18242
19184
  }, [edgeCreateEnabled, renderNodes, svgRef, viewport]);
18243
- const handleSvgPointerLeave = (0, import_react13.useCallback)(() => {
19185
+ const handleSvgPointerLeave = (0, import_react15.useCallback)(() => {
18244
19186
  setHoveredNodeId(null);
18245
19187
  setHoveredSide(null);
18246
19188
  }, []);
@@ -18258,7 +19200,7 @@ var SystemCanvas = (() => {
18258
19200
  return null;
18259
19201
  return computeEdgeMidpoint(editingEdge, from, to);
18260
19202
  })();
18261
- return (0, import_jsx_runtime15.jsxs)("svg", { ref: svgRef, className: "system-canvas-viewport", style: {
19203
+ return (0, import_jsx_runtime22.jsxs)("svg", { ref: svgRef, className: "system-canvas-viewport", style: {
18262
19204
  width: "100%",
18263
19205
  height: "100%",
18264
19206
  display: "block",
@@ -18267,16 +19209,16 @@ var SystemCanvas = (() => {
18267
19209
  WebkitUserSelect: "none",
18268
19210
  MozUserSelect: "none",
18269
19211
  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 })] })] });
19212
+ }, 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
19213
  });
18272
19214
 
18273
19215
  // ../react/dist/components/Breadcrumbs.js
18274
- var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
18275
- var import_react14 = __toESM(require_react(), 1);
19216
+ var import_jsx_runtime23 = __toESM(require_jsx_runtime(), 1);
19217
+ var import_react16 = __toESM(require_react(), 1);
18276
19218
  function Breadcrumbs({ breadcrumbs, theme, onNavigate }) {
18277
19219
  if (breadcrumbs.length <= 1)
18278
19220
  return null;
18279
- return (0, import_jsx_runtime16.jsx)("div", { className: "system-canvas-breadcrumbs", style: {
19221
+ return (0, import_jsx_runtime23.jsx)("div", { className: "system-canvas-breadcrumbs", style: {
18280
19222
  position: "absolute",
18281
19223
  top: 12,
18282
19224
  left: 12,
@@ -18293,10 +19235,10 @@ var SystemCanvas = (() => {
18293
19235
  backdropFilter: "blur(8px)"
18294
19236
  }, children: breadcrumbs.map((crumb, index) => {
18295
19237
  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: {
19238
+ return (0, import_jsx_runtime23.jsxs)(import_react16.default.Fragment, { children: [index > 0 && (0, import_jsx_runtime23.jsx)("span", { style: {
18297
19239
  color: theme.separatorColor,
18298
19240
  margin: "0 2px"
18299
- }, children: "/" }), (0, import_jsx_runtime16.jsx)("span", { onClick: isLast ? void 0 : () => onNavigate(index), style: {
19241
+ }, children: "/" }), (0, import_jsx_runtime23.jsx)("span", { onClick: isLast ? void 0 : () => onNavigate(index), style: {
18300
19242
  color: isLast ? theme.activeColor : theme.textColor,
18301
19243
  cursor: isLast ? "default" : "pointer",
18302
19244
  fontWeight: isLast ? 600 : 400,
@@ -18316,12 +19258,12 @@ var SystemCanvas = (() => {
18316
19258
  }
18317
19259
 
18318
19260
  // ../react/dist/components/AddNodeButton.js
18319
- var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
18320
- var import_react15 = __toESM(require_react(), 1);
19261
+ var import_jsx_runtime24 = __toESM(require_jsx_runtime(), 1);
19262
+ var import_react17 = __toESM(require_react(), 1);
18321
19263
  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)(() => {
19264
+ const [open, setOpen] = (0, import_react17.useState)(false);
19265
+ const rootRef = (0, import_react17.useRef)(null);
19266
+ (0, import_react17.useEffect)(() => {
18325
19267
  if (!open)
18326
19268
  return;
18327
19269
  function onDocClick(e) {
@@ -18344,7 +19286,7 @@ var SystemCanvas = (() => {
18344
19286
  }, [open]);
18345
19287
  const categoryOptions = options.filter((o) => o.kind === "category");
18346
19288
  const typeOptions = options.filter((o) => o.kind === "type");
18347
- return (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, className: "system-canvas-add-node", style: {
19289
+ return (0, import_jsx_runtime24.jsxs)("div", { ref: rootRef, className: "system-canvas-add-node", style: {
18348
19290
  position: "absolute",
18349
19291
  bottom: 16,
18350
19292
  right: 16,
@@ -18352,7 +19294,7 @@ var SystemCanvas = (() => {
18352
19294
  fontFamily: theme.breadcrumbs.fontFamily,
18353
19295
  fontSize: theme.breadcrumbs.fontSize,
18354
19296
  userSelect: "none"
18355
- }, children: [open && (0, import_jsx_runtime17.jsxs)("div", { style: {
19297
+ }, children: [open && (0, import_jsx_runtime24.jsxs)("div", { style: {
18356
19298
  position: "absolute",
18357
19299
  bottom: 52,
18358
19300
  right: 0,
@@ -18365,13 +19307,13 @@ var SystemCanvas = (() => {
18365
19307
  borderRadius: 10,
18366
19308
  boxShadow: "0 8px 24px rgba(0,0,0,0.35)",
18367
19309
  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: () => {
19310
+ }, 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
19311
  addNode2(opt);
18370
19312
  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: () => {
19313
+ } }, `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
19314
  addNode2(opt);
18373
19315
  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: {
19316
+ } }, `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
19317
  width: 44,
18376
19318
  height: 44,
18377
19319
  borderRadius: 22,
@@ -18392,7 +19334,7 @@ var SystemCanvas = (() => {
18392
19334
  }, children: "+" })] });
18393
19335
  }
18394
19336
  function SectionLabel({ theme, children: children2 }) {
18395
- return (0, import_jsx_runtime17.jsx)("div", { style: {
19337
+ return (0, import_jsx_runtime24.jsx)("div", { style: {
18396
19338
  padding: "4px 8px",
18397
19339
  fontSize: theme.breadcrumbs.fontSize - 2,
18398
19340
  color: theme.breadcrumbs.textColor,
@@ -18402,7 +19344,7 @@ var SystemCanvas = (() => {
18402
19344
  }, children: children2 });
18403
19345
  }
18404
19346
  function Divider({ theme }) {
18405
- return (0, import_jsx_runtime17.jsx)("div", { style: {
19347
+ return (0, import_jsx_runtime24.jsx)("div", { style: {
18406
19348
  height: 1,
18407
19349
  margin: "4px 0",
18408
19350
  background: theme.breadcrumbs.separatorColor,
@@ -18410,9 +19352,9 @@ var SystemCanvas = (() => {
18410
19352
  } });
18411
19353
  }
18412
19354
  function MenuRow({ theme, option, onClick }) {
18413
- const [hover, setHover] = (0, import_react15.useState)(false);
19355
+ const [hover, setHover] = (0, import_react17.useState)(false);
18414
19356
  const swatchSize = 18;
18415
- return (0, import_jsx_runtime17.jsxs)("div", { role: "button", onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: {
19357
+ return (0, import_jsx_runtime24.jsxs)("div", { role: "button", onClick, onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), style: {
18416
19358
  display: "flex",
18417
19359
  alignItems: "center",
18418
19360
  gap: 8,
@@ -18421,7 +19363,7 @@ var SystemCanvas = (() => {
18421
19363
  cursor: "pointer",
18422
19364
  background: hover ? "rgba(255,255,255,0.06)" : "transparent",
18423
19365
  color: theme.breadcrumbs.activeColor
18424
- }, children: [option.kind === "category" ? (0, import_jsx_runtime17.jsx)("span", { style: {
19366
+ }, children: [option.kind === "category" ? (0, import_jsx_runtime24.jsx)("span", { style: {
18425
19367
  width: swatchSize,
18426
19368
  height: swatchSize,
18427
19369
  borderRadius: 4,
@@ -18431,13 +19373,13 @@ var SystemCanvas = (() => {
18431
19373
  alignItems: "center",
18432
19374
  justifyContent: "center",
18433
19375
  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: {
19376
+ }, 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
19377
  width: swatchSize,
18436
19378
  height: swatchSize,
18437
19379
  borderRadius: 4,
18438
19380
  border: `1px dashed ${theme.breadcrumbs.separatorColor}`,
18439
19381
  flexShrink: 0
18440
- } }), (0, import_jsx_runtime17.jsx)("span", { style: { textTransform: "capitalize" }, children: option.label }), (0, import_jsx_runtime17.jsx)("span", { style: {
19382
+ } }), (0, import_jsx_runtime24.jsx)("span", { style: { textTransform: "capitalize" }, children: option.label }), (0, import_jsx_runtime24.jsx)("span", { style: {
18441
19383
  marginLeft: "auto",
18442
19384
  fontSize: theme.breadcrumbs.fontSize - 2,
18443
19385
  opacity: 0.5
@@ -18445,13 +19387,13 @@ var SystemCanvas = (() => {
18445
19387
  }
18446
19388
 
18447
19389
  // ../react/dist/components/LaneHeaders.js
18448
- var import_jsx_runtime18 = __toESM(require_jsx_runtime(), 1);
18449
- var import_react16 = __toESM(require_react(), 1);
19390
+ var import_jsx_runtime25 = __toESM(require_jsx_runtime(), 1);
19391
+ var import_react18 = __toESM(require_react(), 1);
18450
19392
  function LaneHeaders({ columns, rows, theme, getViewport, width, height, pinned = true }) {
18451
19393
  const hasColumns = columns && columns.length > 0;
18452
19394
  const hasRows = rows && rows.length > 0;
18453
- const [viewport, setViewport] = (0, import_react16.useState)(() => getViewport());
18454
- (0, import_react16.useEffect)(() => {
19395
+ const [viewport, setViewport] = (0, import_react18.useState)(() => getViewport());
19396
+ (0, import_react18.useEffect)(() => {
18455
19397
  if (!hasColumns && !hasRows)
18456
19398
  return;
18457
19399
  let raf = 0;
@@ -18478,14 +19420,14 @@ var SystemCanvas = (() => {
18478
19420
  const pad = lanesTheme.headerPadding;
18479
19421
  const colsOffsetLeft = hasRows && pinned ? headerSize : 0;
18480
19422
  const rowsOffsetTop = hasColumns && pinned ? headerSize : 0;
18481
- return (0, import_jsx_runtime18.jsxs)("svg", { className: "system-canvas-lane-headers", style: {
19423
+ return (0, import_jsx_runtime25.jsxs)("svg", { className: "system-canvas-lane-headers", style: {
18482
19424
  position: "absolute",
18483
19425
  top: 0,
18484
19426
  left: 0,
18485
19427
  width,
18486
19428
  height,
18487
19429
  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) => {
19430
+ }, 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
19431
  const startScreen = canvasToScreen(col.start, 0, viewport).x;
18490
19432
  const endScreen = canvasToScreen(col.start + col.size, 0, viewport).x;
18491
19433
  const w = endScreen - startScreen;
@@ -18496,8 +19438,8 @@ var SystemCanvas = (() => {
18496
19438
  const cx = visibleLeft + visibleW / 2;
18497
19439
  if (endScreen <= colsOffsetLeft || startScreen >= width)
18498
19440
  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) => {
19441
+ 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}`);
19442
+ }), 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
19443
  const startScreen = canvasToScreen(0, row.start, viewport).y;
18502
19444
  const endScreen = canvasToScreen(0, row.start + row.size, viewport).y;
18503
19445
  const h = endScreen - startScreen;
@@ -18508,8 +19450,8 @@ var SystemCanvas = (() => {
18508
19450
  const cy = visibleTop + visibleH / 2;
18509
19451
  if (endScreen <= rowsOffsetTop || startScreen >= height)
18510
19452
  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 })] });
19453
+ 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}`);
19454
+ }), 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
19455
  }
18514
19456
  function truncateToWidth(label, availablePx, fontSize) {
18515
19457
  if (availablePx <= 0)
@@ -18524,8 +19466,8 @@ var SystemCanvas = (() => {
18524
19466
  }
18525
19467
 
18526
19468
  // ../react/dist/components/NodeToolbar.js
18527
- var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
18528
- var import_react17 = __toESM(require_react(), 1);
19469
+ var import_jsx_runtime26 = __toESM(require_jsx_runtime(), 1);
19470
+ var import_react19 = __toESM(require_react(), 1);
18529
19471
  var NODE_GAP = 10;
18530
19472
  var FLIP_MARGIN = 8;
18531
19473
  var PADDING = 6;
@@ -18534,8 +19476,8 @@ var SystemCanvas = (() => {
18534
19476
  var BUTTON_SIZE = 28;
18535
19477
  var DELETE_SIZE = 14;
18536
19478
  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)(() => {
19479
+ const [viewport, setViewport] = (0, import_react19.useState)(() => getViewport());
19480
+ (0, import_react19.useEffect)(() => {
18539
19481
  let raf = 0;
18540
19482
  let lastX = -Infinity;
18541
19483
  let lastY = -Infinity;
@@ -18555,9 +19497,9 @@ var SystemCanvas = (() => {
18555
19497
  }, [getViewport]);
18556
19498
  const topCenter = canvasToScreen(node.x + node.width / 2, node.y, viewport);
18557
19499
  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)(() => {
19500
+ const toolbarRef = (0, import_react19.useRef)(null);
19501
+ const [size, setSize] = (0, import_react19.useState)({ width: 0, height: 0 });
19502
+ (0, import_react19.useEffect)(() => {
18561
19503
  const el = toolbarRef.current;
18562
19504
  if (!el)
18563
19505
  return;
@@ -18578,7 +19520,7 @@ var SystemCanvas = (() => {
18578
19520
  left = Math.max(FLIP_MARGIN, Math.min(left, containerWidth - size.width - FLIP_MARGIN));
18579
19521
  const patch = (update) => onPatch(update);
18580
19522
  const deleteNode = () => onDelete();
18581
- return (0, import_jsx_runtime19.jsx)("div", {
19523
+ return (0, import_jsx_runtime26.jsx)("div", {
18582
19524
  ref: toolbarRef,
18583
19525
  className: "system-canvas-node-toolbar",
18584
19526
  // Stop pointer events from bubbling to the canvas (which would
@@ -18607,15 +19549,16 @@ var SystemCanvas = (() => {
18607
19549
  userSelect: "none",
18608
19550
  whiteSpace: "nowrap"
18609
19551
  },
18610
- children: render2 ? render2({ node, theme, patch, deleteNode }) : (0, import_jsx_runtime19.jsx)(DefaultToolbarContent, { node, theme, onPatch: patch, onDelete: deleteNode })
19552
+ children: render2 ? render2({ node, theme, patch, deleteNode }) : (0, import_jsx_runtime26.jsx)(DefaultToolbarContent, { node, theme, onPatch: patch, onDelete: deleteNode })
18611
19553
  });
18612
19554
  }
18613
19555
  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 })] });
19556
+ const groups = (0, import_react19.useMemo)(() => getNodeActionsForNode(node, theme), [node, theme]);
19557
+ const showDelete = !theme.hideToolbarDelete;
19558
+ 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
19559
  }
18617
19560
  function Divider2({ theme }) {
18618
- return (0, import_jsx_runtime19.jsx)("div", { style: {
19561
+ return (0, import_jsx_runtime26.jsx)("div", { style: {
18619
19562
  width: 1,
18620
19563
  alignSelf: "stretch",
18621
19564
  background: theme.breadcrumbs.separatorColor,
@@ -18628,23 +19571,23 @@ var SystemCanvas = (() => {
18628
19571
  return null;
18629
19572
  const kind = group.kind ?? "buttons";
18630
19573
  if (kind === "menu") {
18631
- return (0, import_jsx_runtime19.jsx)(MenuGroup, { group, actions, node, theme, onPatch });
19574
+ return (0, import_jsx_runtime26.jsx)(MenuGroup, { group, actions, node, theme, onPatch });
18632
19575
  }
18633
- return (0, import_jsx_runtime19.jsx)("div", { title: group.label, style: { display: "flex", alignItems: "center", gap: BUTTON_GAP }, children: actions.map((action) => {
19576
+ return (0, import_jsx_runtime26.jsx)("div", { title: group.label, style: { display: "flex", alignItems: "center", gap: BUTTON_GAP }, children: actions.map((action) => {
18634
19577
  const handleClick = () => {
18635
19578
  const patch = resolveActionPatch(action, node);
18636
19579
  onPatch(patch);
18637
19580
  };
18638
19581
  const active = action.isActive?.(node) ?? false;
18639
19582
  if (kind === "swatches") {
18640
- return (0, import_jsx_runtime19.jsx)(SwatchButton, { action, active, theme, onClick: handleClick }, action.id);
19583
+ return (0, import_jsx_runtime26.jsx)(SwatchButton, { action, active, theme, onClick: handleClick }, action.id);
18641
19584
  }
18642
- return (0, import_jsx_runtime19.jsx)(IconButton, { action, active, theme, onClick: handleClick }, action.id);
19585
+ return (0, import_jsx_runtime26.jsx)(IconButton, { action, active, theme, onClick: handleClick }, action.id);
18643
19586
  }) });
18644
19587
  }
18645
19588
  function SwatchButton({ action, active, theme, onClick }) {
18646
19589
  const color2 = action.swatch ?? theme.node.labelColor;
18647
- return (0, import_jsx_runtime19.jsx)("button", { type: "button", title: action.label, onClick, style: {
19590
+ return (0, import_jsx_runtime26.jsx)("button", { type: "button", title: action.label, onClick, style: {
18648
19591
  width: SWATCH_SIZE,
18649
19592
  height: SWATCH_SIZE,
18650
19593
  borderRadius: "50%",
@@ -18658,7 +19601,7 @@ var SystemCanvas = (() => {
18658
19601
  }, onMouseDown: (e) => e.preventDefault() });
18659
19602
  }
18660
19603
  function IconButton({ action, active, theme, onClick }) {
18661
- return (0, import_jsx_runtime19.jsx)("button", { type: "button", title: action.label, onClick, style: {
19604
+ return (0, import_jsx_runtime26.jsx)("button", { type: "button", title: action.label, onClick, style: {
18662
19605
  width: BUTTON_SIZE,
18663
19606
  height: BUTTON_SIZE,
18664
19607
  display: "inline-flex",
@@ -18671,17 +19614,17 @@ var SystemCanvas = (() => {
18671
19614
  cursor: "pointer",
18672
19615
  padding: 0,
18673
19616
  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: {
19617
+ }, 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
19618
  width: 10,
18676
19619
  height: 10,
18677
19620
  borderRadius: "50%",
18678
19621
  background: action.swatch
18679
- } }) : (0, import_jsx_runtime19.jsx)("span", { style: { fontSize: 10 }, children: action.label.slice(0, 2) }) });
19622
+ } }) : (0, import_jsx_runtime26.jsx)("span", { style: { fontSize: 10 }, children: action.label.slice(0, 2) }) });
18680
19623
  }
18681
19624
  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)(() => {
19625
+ const [open, setOpen] = (0, import_react19.useState)(false);
19626
+ const wrapRef = (0, import_react19.useRef)(null);
19627
+ (0, import_react19.useEffect)(() => {
18685
19628
  if (!open)
18686
19629
  return;
18687
19630
  const onDown = (e) => {
@@ -18694,7 +19637,7 @@ var SystemCanvas = (() => {
18694
19637
  const active = actions.find((a) => a.isActive?.(node));
18695
19638
  const triggerLabel = active?.label ?? group.label ?? "Menu";
18696
19639
  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: {
19640
+ 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
19641
  height: BUTTON_SIZE,
18699
19642
  display: "inline-flex",
18700
19643
  alignItems: "center",
@@ -18708,7 +19651,7 @@ var SystemCanvas = (() => {
18708
19651
  fontFamily: "inherit",
18709
19652
  fontSize: "inherit",
18710
19653
  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: {
19654
+ }, 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
19655
  position: "absolute",
18713
19656
  top: "100%",
18714
19657
  left: 0,
@@ -18723,7 +19666,7 @@ var SystemCanvas = (() => {
18723
19666
  zIndex: 1
18724
19667
  }, children: actions.map((action) => {
18725
19668
  const isActive = action.isActive?.(node) ?? false;
18726
- return (0, import_jsx_runtime19.jsxs)("button", { type: "button", onClick: () => {
19669
+ return (0, import_jsx_runtime26.jsxs)("button", { type: "button", onClick: () => {
18727
19670
  onPatch(resolveActionPatch(action, node));
18728
19671
  setOpen(false);
18729
19672
  }, style: {
@@ -18740,17 +19683,17 @@ var SystemCanvas = (() => {
18740
19683
  fontFamily: "inherit",
18741
19684
  fontSize: "inherit",
18742
19685
  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: {
19686
+ }, 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
19687
  width: 10,
18745
19688
  height: 10,
18746
19689
  borderRadius: "50%",
18747
19690
  background: action.swatch,
18748
19691
  flexShrink: 0
18749
- } }), (0, import_jsx_runtime19.jsx)("span", { children: action.label })] }, action.id);
19692
+ } }), (0, import_jsx_runtime26.jsx)("span", { children: action.label })] }, action.id);
18750
19693
  }) })] });
18751
19694
  }
18752
19695
  function DeleteButton({ theme, onDelete }) {
18753
- return (0, import_jsx_runtime19.jsx)("button", { type: "button", title: "Delete", onClick: onDelete, onMouseDown: (e) => e.preventDefault(), style: {
19696
+ return (0, import_jsx_runtime26.jsx)("button", { type: "button", title: "Delete", onClick: onDelete, onMouseDown: (e) => e.preventDefault(), style: {
18754
19697
  width: BUTTON_SIZE,
18755
19698
  height: BUTTON_SIZE,
18756
19699
  display: "inline-flex",
@@ -18763,21 +19706,21 @@ var SystemCanvas = (() => {
18763
19706
  cursor: "pointer",
18764
19707
  padding: 0,
18765
19708
  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" }) }) });
19709
+ }, 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
19710
  }
18768
19711
 
18769
19712
  // ../react/dist/components/SystemCanvas.js
18770
19713
  var CASCADE_WINDOW_MS = 1500;
18771
19714
  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)(() => {
19715
+ 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) {
19716
+ const zoomNavConfig = (0, import_react20.useMemo)(() => {
18774
19717
  const defaults = {
18775
19718
  enterThreshold: 0.66,
18776
19719
  exitThreshold: 0.33,
18777
19720
  prefetchThreshold: 0.4,
18778
19721
  landingScale: 1.2,
18779
19722
  landingPadding: 0.08,
18780
- fadeDuration: 200
19723
+ fadeDuration: 216
18781
19724
  };
18782
19725
  if (!zoomNavigation)
18783
19726
  return { enabled: false, ...defaults };
@@ -18795,16 +19738,16 @@ var SystemCanvas = (() => {
18795
19738
  }, [zoomNavigation]);
18796
19739
  const effectiveMaxZoom = maxZoom ?? (zoomNavConfig.enabled ? 16 : 4);
18797
19740
  const effectiveMinZoom = minZoomProp ?? (zoomNavConfig.enabled ? 0.01 : 0.1);
18798
- (0, import_react18.useEffect)(() => {
19741
+ (0, import_react20.useEffect)(() => {
18799
19742
  const env = globalThis.process?.env?.NODE_ENV;
18800
19743
  if (editable && !canvases && env !== "production") {
18801
19744
  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
19745
  }
18803
19746
  }, [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) => {
19747
+ const [parentFrames, setParentFrames] = (0, import_react20.useState)([]);
19748
+ const [pendingHandoff, setPendingHandoff] = (0, import_react20.useState)(null);
19749
+ const suppressNextHandoffClearRef = (0, import_react20.useRef)(false);
19750
+ const handleBreadcrumbClick = (0, import_react20.useCallback)((index) => {
18808
19751
  setParentFrames((prev) => prev.slice(0, index));
18809
19752
  if (suppressNextHandoffClearRef.current) {
18810
19753
  suppressNextHandoffClearRef.current = false;
@@ -18821,7 +19764,7 @@ var SystemCanvas = (() => {
18821
19764
  onNavigate,
18822
19765
  onBreadcrumbClick: handleBreadcrumbClick
18823
19766
  });
18824
- const theme = (0, import_react18.useMemo)(() => {
19767
+ const theme = (0, import_react20.useMemo)(() => {
18825
19768
  const registry = { ...themes, ...customThemes };
18826
19769
  const resolveByName = (name) => name && registry[name] ? registry[name] : null;
18827
19770
  if (themeProp) {
@@ -18832,22 +19775,22 @@ var SystemCanvas = (() => {
18832
19775
  }
18833
19776
  return resolveByName(currentCanvas.theme?.base) ?? resolveByName(canvas.theme?.base) ?? darkTheme;
18834
19777
  }, [themeProp, customThemes, currentCanvas.theme?.base, canvas.theme?.base]);
18835
- const { nodes, edges, nodeMap } = (0, import_react18.useMemo)(() => {
19778
+ const { nodes, edges, nodeMap } = (0, import_react20.useMemo)(() => {
18836
19779
  const resolved = resolveCanvas(currentCanvas, theme);
18837
19780
  const map = buildNodeMap(resolved.nodes);
18838
19781
  return { nodes: resolved.nodes, edges: resolved.edges, nodeMap: map };
18839
19782
  }, [currentCanvas, theme]);
18840
- const nodesRef = (0, import_react18.useRef)(nodes);
19783
+ const nodesRef = (0, import_react20.useRef)(nodes);
18841
19784
  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);
19785
+ const viewportStateRef = (0, import_react20.useRef)(defaultViewport ?? { x: 0, y: 0, zoom: 1 });
19786
+ const viewportHandleRef = (0, import_react20.useRef)(null);
19787
+ const navigateToRefRef = (0, import_react20.useRef)(navigateToRef);
18845
19788
  navigateToRefRef.current = navigateToRef;
18846
- const navigateToBreadcrumbRef = (0, import_react18.useRef)(navigateToBreadcrumb);
19789
+ const navigateToBreadcrumbRef = (0, import_react20.useRef)(navigateToBreadcrumb);
18847
19790
  navigateToBreadcrumbRef.current = navigateToBreadcrumb;
18848
- const breadcrumbsRef = (0, import_react18.useRef)(breadcrumbs);
19791
+ const breadcrumbsRef = (0, import_react20.useRef)(breadcrumbs);
18849
19792
  breadcrumbsRef.current = breadcrumbs;
18850
- (0, import_react18.useImperativeHandle)(forwardedRef, () => ({
19793
+ (0, import_react20.useImperativeHandle)(forwardedRef, () => ({
18851
19794
  zoomIntoNode: (nodeId, options) => {
18852
19795
  return new Promise((resolve) => {
18853
19796
  const node = nodesRef.current.find((n) => n.id === nodeId);
@@ -18879,19 +19822,19 @@ var SystemCanvas = (() => {
18879
19822
  navigateToBreadcrumbRef.current(0);
18880
19823
  }
18881
19824
  }), [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)(() => {
19825
+ const [selectedId, setSelectedId] = (0, import_react20.useState)(null);
19826
+ const [editingId, setEditingId] = (0, import_react20.useState)(null);
19827
+ const [selectedEdgeId, setSelectedEdgeId] = (0, import_react20.useState)(null);
19828
+ const [editingEdgeId, setEditingEdgeId] = (0, import_react20.useState)(null);
19829
+ (0, import_react20.useEffect)(() => {
18887
19830
  setSelectedId(null);
18888
19831
  setEditingId(null);
18889
19832
  setSelectedEdgeId(null);
18890
19833
  setEditingEdgeId(null);
18891
19834
  }, [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)(() => {
19835
+ const containerRef = (0, import_react20.useRef)(null);
19836
+ const [containerSize, setContainerSize] = (0, import_react20.useState)({ width: 0, height: 0 });
19837
+ (0, import_react20.useEffect)(() => {
18895
19838
  const el = containerRef.current;
18896
19839
  if (!el)
18897
19840
  return;
@@ -18906,19 +19849,28 @@ var SystemCanvas = (() => {
18906
19849
  }, []);
18907
19850
  const hasLanes = currentCanvas.columns && currentCanvas.columns.length > 0 || currentCanvas.rows && currentCanvas.rows.length > 0;
18908
19851
  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) => {
19852
+ const getViewportState = (0, import_react20.useCallback)(() => viewportStateRef.current ?? { x: 0, y: 0, zoom: 1 }, []);
19853
+ const commitDrag = (0, import_react20.useCallback)((id2, patch) => {
18911
19854
  let final = patch;
18912
19855
  if (snapToLanes) {
18913
19856
  const cols = currentCanvas.columns;
18914
19857
  const rows = currentCanvas.rows;
19858
+ const node = nodesRef.current.find((n) => n.id === id2);
18915
19859
  const nx = patch.x;
18916
19860
  const ny = patch.y;
18917
19861
  if (cols && cols.length > 0 && nx != null) {
18918
- final = { ...final, x: Math.round(snapToLane(nx, cols)) };
19862
+ const snapped = snapToLane(nx, cols, {
19863
+ edge: "center",
19864
+ size: node?.width ?? 0
19865
+ });
19866
+ final = { ...final, x: Math.round(snapped) };
18919
19867
  }
18920
19868
  if (rows && rows.length > 0 && ny != null) {
18921
- final = { ...final, y: Math.round(snapToLane(ny, rows)) };
19869
+ const snapped = snapToLane(ny, rows, {
19870
+ edge: "center",
19871
+ size: node?.height ?? 0
19872
+ });
19873
+ final = { ...final, y: Math.round(snapped) };
18922
19874
  }
18923
19875
  }
18924
19876
  onNodeUpdate?.(id2, final, currentCanvasRef);
@@ -18932,7 +19884,7 @@ var SystemCanvas = (() => {
18932
19884
  viewport: viewportStateRef,
18933
19885
  onCommit: commitDrag
18934
19886
  });
18935
- const selectedResolvedNode = (0, import_react18.useMemo)(() => {
19887
+ const selectedResolvedNode = (0, import_react20.useMemo)(() => {
18936
19888
  if (!selectedId)
18937
19889
  return null;
18938
19890
  const base = nodeMap.get(selectedId);
@@ -18948,9 +19900,9 @@ var SystemCanvas = (() => {
18948
19900
  }
18949
19901
  return base;
18950
19902
  }, [selectedId, nodeMap, dragOverrides, resizeOverrides]);
18951
- const svgProxyRef = (0, import_react18.useRef)(null);
19903
+ const svgProxyRef = (0, import_react20.useRef)(null);
18952
19904
  svgProxyRef.current = viewportHandleRef.current?.getSvgElement() ?? null;
18953
- const handleEdgeCreated = (0, import_react18.useCallback)((edge) => {
19905
+ const handleEdgeCreated = (0, import_react20.useCallback)((edge) => {
18954
19906
  onEdgeAdd?.(edge, currentCanvasRef);
18955
19907
  }, [onEdgeAdd, currentCanvasRef]);
18956
19908
  const { pending: pendingEdge, onHandlePointerDown: onConnectionHandlePointerDown } = useEdgeCreate({
@@ -18959,7 +19911,7 @@ var SystemCanvas = (() => {
18959
19911
  nodesRef,
18960
19912
  onCreate: handleEdgeCreated
18961
19913
  });
18962
- const handleNavigableNodeClick = (0, import_react18.useCallback)((node) => {
19914
+ const handleNavigableNodeClick = (0, import_react20.useCallback)((node) => {
18963
19915
  const frame2 = {
18964
19916
  parentCanvasRef: currentCanvasRef,
18965
19917
  parentNodeRect: {
@@ -18970,16 +19922,23 @@ var SystemCanvas = (() => {
18970
19922
  }
18971
19923
  };
18972
19924
  setParentFrames((prev) => [...prev, frame2]);
19925
+ if (!zoomNavConfig.enabled) {
19926
+ navigateToRef(node);
19927
+ return;
19928
+ }
18973
19929
  const handle = viewportHandleRef.current;
18974
19930
  if (handle) {
18975
19931
  handle.zoomToNode(node, () => {
19932
+ const finalVp = viewportStateRef.current;
19933
+ if (finalVp)
19934
+ setPendingHandoff({ ...finalVp });
18976
19935
  navigateToRef(node);
18977
19936
  });
18978
19937
  } else {
18979
19938
  navigateToRef(node);
18980
19939
  }
18981
- }, [navigateToRef, currentCanvasRef]);
18982
- const handleZoomEnter = (0, import_react18.useCallback)((node, targetTransform) => {
19940
+ }, [navigateToRef, currentCanvasRef, zoomNavConfig.enabled]);
19941
+ const handleZoomEnter = (0, import_react20.useCallback)((node, targetTransform) => {
18983
19942
  const frame2 = {
18984
19943
  parentCanvasRef: currentCanvasRef,
18985
19944
  parentNodeRect: {
@@ -18993,7 +19952,7 @@ var SystemCanvas = (() => {
18993
19952
  setPendingHandoff(targetTransform);
18994
19953
  navigateToRef(node);
18995
19954
  }, [currentCanvasRef, navigateToRef]);
18996
- const handleZoomExit = (0, import_react18.useCallback)((targetTransform) => {
19955
+ const handleZoomExit = (0, import_react20.useCallback)((targetTransform) => {
18997
19956
  setPendingHandoff(targetTransform);
18998
19957
  suppressNextHandoffClearRef.current = true;
18999
19958
  navigateToBreadcrumb(breadcrumbs.length - 2);
@@ -19019,19 +19978,19 @@ var SystemCanvas = (() => {
19019
19978
  onEnter: handleZoomEnter,
19020
19979
  onExit: handleZoomExit
19021
19980
  });
19022
- const handleViewportChange = (0, import_react18.useCallback)((vp) => {
19981
+ const handleViewportChange = (0, import_react20.useCallback)((vp) => {
19023
19982
  viewportStateRef.current = vp;
19024
19983
  handleZoomNavViewportChange(vp);
19025
19984
  onViewportChange?.(vp);
19026
19985
  }, [handleZoomNavViewportChange, onViewportChange]);
19027
- const handleHandoffApplied = (0, import_react18.useCallback)(() => {
19986
+ const handleHandoffApplied = (0, import_react20.useCallback)(() => {
19028
19987
  setPendingHandoff(null);
19029
19988
  clearZoomNavCommitting();
19030
19989
  }, [clearZoomNavCommitting]);
19031
- const handleBeginEdit = (0, import_react18.useCallback)((node) => {
19990
+ const handleBeginEdit = (0, import_react20.useCallback)((node) => {
19032
19991
  setEditingId(node.id);
19033
19992
  }, []);
19034
- const handleBeginEditEdge = (0, import_react18.useCallback)((edge) => {
19993
+ const handleBeginEditEdge = (0, import_react20.useCallback)((edge) => {
19035
19994
  setEditingEdgeId(edge.id);
19036
19995
  }, []);
19037
19996
  const { handleNodeClick, handleNodeDoubleClick, handleNodeNavigate, handleEdgeClick, handleEdgeDoubleClick, handleCanvasClick, handleCanvasContextMenu, handleNodeContextMenu, handleEdgeContextMenu } = useCanvasInteraction({
@@ -19048,27 +20007,27 @@ var SystemCanvas = (() => {
19048
20007
  onSelectEdge: setSelectedEdgeId,
19049
20008
  onBeginEditEdge: handleBeginEditEdge
19050
20009
  });
19051
- const handleEditorCommit = (0, import_react18.useCallback)((patch) => {
20010
+ const handleEditorCommit = (0, import_react20.useCallback)((patch) => {
19052
20011
  if (editingId) {
19053
20012
  onNodeUpdate?.(editingId, patch, currentCanvasRef);
19054
20013
  }
19055
20014
  setEditingId(null);
19056
20015
  }, [editingId, onNodeUpdate, currentCanvasRef]);
19057
- const handleEditorCancel = (0, import_react18.useCallback)(() => {
20016
+ const handleEditorCancel = (0, import_react20.useCallback)(() => {
19058
20017
  setEditingId(null);
19059
20018
  }, []);
19060
- const handleEdgeEditorCommit = (0, import_react18.useCallback)((patch) => {
20019
+ const handleEdgeEditorCommit = (0, import_react20.useCallback)((patch) => {
19061
20020
  if (editingEdgeId) {
19062
20021
  onEdgeUpdate?.(editingEdgeId, patch, currentCanvasRef);
19063
20022
  }
19064
20023
  setEditingEdgeId(null);
19065
20024
  }, [editingEdgeId, onEdgeUpdate, currentCanvasRef]);
19066
- const handleEdgeEditorCancel = (0, import_react18.useCallback)(() => {
20025
+ const handleEdgeEditorCancel = (0, import_react20.useCallback)(() => {
19067
20026
  setEditingEdgeId(null);
19068
20027
  }, []);
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) => {
20028
+ const lastAddRef = (0, import_react20.useRef)(null);
20029
+ const menuOptions = (0, import_react20.useMemo)(() => getNodeMenuOptions(currentCanvas, theme), [currentCanvas, theme]);
20030
+ const addNode2 = (0, import_react20.useCallback)((option, position) => {
19072
20031
  let x, y;
19073
20032
  if (position) {
19074
20033
  x = position.x;
@@ -19097,10 +20056,10 @@ var SystemCanvas = (() => {
19097
20056
  y += nextOffset;
19098
20057
  lastAddRef.current = { t: now2, offset: nextOffset };
19099
20058
  }
19100
- const node = createNodeFromOption(option, Math.round(x), Math.round(y));
20059
+ const node = createNodeFromOption(option, Math.round(x), Math.round(y), void 0, theme);
19101
20060
  onNodeAdd?.(node, currentCanvasRef);
19102
- }, [onNodeAdd, currentCanvasRef]);
19103
- const handleKeyDown = (0, import_react18.useCallback)((e) => {
20061
+ }, [onNodeAdd, currentCanvasRef, theme]);
20062
+ const handleKeyDown = (0, import_react20.useCallback)((e) => {
19104
20063
  if (!editable)
19105
20064
  return;
19106
20065
  if (e.key === "Escape") {
@@ -19134,14 +20093,14 @@ var SystemCanvas = (() => {
19134
20093
  currentCanvasRef
19135
20094
  ]);
19136
20095
  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: {
20096
+ return (0, import_jsx_runtime27.jsxs)("div", { ref: containerRef, className: `system-canvas ${className ?? ""}`, tabIndex: editable ? 0 : -1, onKeyDown: handleKeyDown, style: {
19138
20097
  position: "relative",
19139
20098
  width: "100%",
19140
20099
  height: "100%",
19141
20100
  overflow: "hidden",
19142
20101
  outline: "none",
19143
20102
  ...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: {
20103
+ }, 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
20104
  position: "absolute",
19146
20105
  top: 12,
19147
20106
  right: 12,
@@ -19153,12 +20112,12 @@ var SystemCanvas = (() => {
19153
20112
  fontFamily: theme.node.fontFamily,
19154
20113
  fontSize: 12,
19155
20114
  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) => {
20115
+ }, 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
20116
  onNodeUpdate?.(selectedResolvedNode.id, update, currentCanvasRef);
19158
20117
  }, onDelete: () => {
19159
20118
  onNodeDelete?.(selectedResolvedNode.id, currentCanvasRef);
19160
20119
  setSelectedId(null);
19161
- }, getViewport: getViewportState, containerWidth: containerSize.width, containerHeight: containerSize.height, render: renderNodeToolbar }), editable && (renderAddNodeButton ? renderAddNodeButton(renderProps) : (0, import_jsx_runtime20.jsx)(AddNodeButton, { ...renderProps }))] });
20120
+ }, getViewport: getViewportState, containerWidth: containerSize.width, containerHeight: containerSize.height, render: renderNodeToolbar }), editable && (renderAddNodeButton ? renderAddNodeButton(renderProps) : (0, import_jsx_runtime27.jsx)(AddNodeButton, { ...renderProps }))] });
19162
20121
  });
19163
20122
 
19164
20123
  // src/index.tsx
@@ -19269,7 +20228,7 @@ var SystemCanvas = (() => {
19269
20228
  onEdgeUpdate: handleEdgeUpdate,
19270
20229
  onEdgeDelete: handleEdgeDelete
19271
20230
  };
19272
- root2.render(import_react19.default.createElement(SystemCanvas, props));
20231
+ root2.render(import_react21.default.createElement(SystemCanvas, props));
19273
20232
  };
19274
20233
  doRender();
19275
20234
  return {