system-canvas-standalone 0.2.36 → 0.2.38

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.
@@ -15107,6 +15107,9 @@ var SystemCanvas = (() => {
15107
15107
  }
15108
15108
  if (editingId || editingEdgeId)
15109
15109
  return;
15110
+ const tag = e.target?.tagName;
15111
+ if (tag === "INPUT" || tag === "TEXTAREA")
15112
+ return;
15110
15113
  if ((meta || ctrl) && !shift && key === "z") {
15111
15114
  e.preventDefault();
15112
15115
  undo();
@@ -20214,7 +20217,7 @@ var SystemCanvas = (() => {
20214
20217
  }
20215
20218
 
20216
20219
  // ../react/dist/components/NodeRenderer.js
20217
- function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedIds, editingId, onResizeHandlePointerDown, canvases, only, dimmedNodeIds, highlightedNodeIds }) {
20220
+ function NodeRenderer({ nodes, theme, onClick, onDoubleClick, onContextMenu, onNavigate, onPointerDown, selectedIds, editingId, onResizeHandlePointerDown, canvases, only, dimmedNodeIds, highlightedNodeIds, activeMatchNodeId }) {
20218
20221
  const groups = nodes.filter((n) => n.type === "group");
20219
20222
  const others = nodes.filter((n) => n.type !== "group");
20220
20223
  const common = (node) => {
@@ -20248,12 +20251,14 @@ var SystemCanvas = (() => {
20248
20251
  return (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [only !== "non-groups" && groups.map((node) => {
20249
20252
  const isDimmed = dimmedNodeIds?.has(node.id) ?? false;
20250
20253
  const isHighlighted = highlightedNodeIds?.has(node.id) ?? false;
20251
- return (0, import_jsx_runtime15.jsxs)("g", { opacity: isDimmed ? 0.15 : 1, children: [isHighlighted && (0, import_jsx_runtime15.jsx)("rect", { x: node.x - 4, y: node.y - 4, width: node.width + 8, height: node.height + 8, rx: 6, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, opacity: 0.6, pointerEvents: "none" }), (0, import_jsx_runtime15.jsx)(GroupNode, { ...common(node) })] }, node.id);
20254
+ const isActiveMatch = node.id === activeMatchNodeId;
20255
+ return (0, import_jsx_runtime15.jsxs)("g", { opacity: isDimmed ? 0.15 : 1, children: [isHighlighted && (0, import_jsx_runtime15.jsx)("rect", { x: node.x - 4, y: node.y - 4, width: node.width + 8, height: node.height + 8, rx: 6, fill: "none", stroke: theme.node.labelColor, strokeWidth: isActiveMatch ? 3.5 : 2, opacity: isActiveMatch ? 1 : 0.6, pointerEvents: "none" }), (0, import_jsx_runtime15.jsx)(GroupNode, { ...common(node) })] }, node.id);
20252
20256
  }), only !== "groups" && others.map((node) => {
20253
20257
  const Component = getNodeComponent(node.type);
20254
20258
  const isDimmed = dimmedNodeIds?.has(node.id) ?? false;
20255
20259
  const isHighlighted = highlightedNodeIds?.has(node.id) ?? false;
20256
- return (0, import_jsx_runtime15.jsxs)("g", { opacity: isDimmed ? 0.15 : 1, children: [isHighlighted && (0, import_jsx_runtime15.jsx)("rect", { x: node.x - 4, y: node.y - 4, width: node.width + 8, height: node.height + 8, rx: 6, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, opacity: 0.6, pointerEvents: "none" }), (0, import_jsx_runtime15.jsx)(Component, { ...common(node) })] }, node.id);
20260
+ const isActiveMatch = node.id === activeMatchNodeId;
20261
+ return (0, import_jsx_runtime15.jsxs)("g", { opacity: isDimmed ? 0.15 : 1, children: [isHighlighted && (0, import_jsx_runtime15.jsx)("rect", { x: node.x - 4, y: node.y - 4, width: node.width + 8, height: node.height + 8, rx: 6, fill: "none", stroke: theme.node.labelColor, strokeWidth: isActiveMatch ? 3.5 : 2, opacity: isActiveMatch ? 1 : 0.6, pointerEvents: "none" }), (0, import_jsx_runtime15.jsx)(Component, { ...common(node) })] }, node.id);
20257
20262
  }), renderResizeHandles && (0, import_jsx_runtime15.jsx)(ResizeHandles, { node: selectedNode, theme, onHandlePointerDown: onResizeHandlePointerDown }), renderResizeHandles && (() => {
20258
20263
  const slots = getCategorySlots(selectedNode, theme);
20259
20264
  if (!slots?.topRightOuter)
@@ -21156,7 +21161,7 @@ var SystemCanvas = (() => {
21156
21161
  // ../react/dist/components/Viewport.js
21157
21162
  var HOVER_PADDING = 10;
21158
21163
  var EDGE_PROXIMITY = 16;
21159
- var Viewport = (0, import_react22.forwardRef)(function Viewport2({ nodes, edges, nodeMap, theme, edgeStyle, columns, rows, canvases, minZoom, maxZoom, defaultViewport, panMode, onViewportChange, onNodeClick, onNodeDoubleClick, onNodeNavigate, onEdgeClick, onEdgeDoubleClick, onCanvasClick, onCanvasContextMenu, onNodeContextMenu, onEdgeContextMenu, onNodePointerDown, selectedIds, editingId, selectedEdgeId, editingEdgeId, dragOverrides, dropTargetId, marqueeRect, marqueeActiveRef, resizeOverrides, onResizeHandlePointerDown, onEditorCommit, onEditorCancel, onEdgeEditorCommit, onEdgeEditorCancel, onEdgeWaypointUpdate, pendingEdge, onConnectionHandlePointerDown, edgeCreateEnabled, alignmentGuides, dimmedNodeIds, highlightedNodeIds, viewportState, autoFit = "canvas-change", canvasRef, handoffTransform, onHandoffApplied, handoffFadeMs = 0 }, ref) {
21164
+ var Viewport = (0, import_react22.forwardRef)(function Viewport2({ nodes, edges, nodeMap, theme, edgeStyle, columns, rows, canvases, minZoom, maxZoom, defaultViewport, panMode, onViewportChange, onNodeClick, onNodeDoubleClick, onNodeNavigate, onEdgeClick, onEdgeDoubleClick, onCanvasClick, onCanvasContextMenu, onNodeContextMenu, onEdgeContextMenu, onNodePointerDown, selectedIds, editingId, selectedEdgeId, editingEdgeId, dragOverrides, dropTargetId, marqueeRect, marqueeActiveRef, resizeOverrides, onResizeHandlePointerDown, onEditorCommit, onEditorCancel, onEdgeEditorCommit, onEdgeEditorCancel, onEdgeWaypointUpdate, pendingEdge, onConnectionHandlePointerDown, edgeCreateEnabled, alignmentGuides, dimmedNodeIds, highlightedNodeIds, activeMatchNodeId, viewportState, autoFit = "canvas-change", canvasRef, handoffTransform, onHandoffApplied, handoffFadeMs = 0 }, ref) {
21160
21165
  const { svgRef, groupRef, viewport, fitToContent, zoomToNode, setTransform } = useViewport({
21161
21166
  minZoom,
21162
21167
  maxZoom,
@@ -21399,7 +21404,7 @@ var SystemCanvas = (() => {
21399
21404
  WebkitUserSelect: "none",
21400
21405
  MozUserSelect: "none",
21401
21406
  msUserSelect: "none"
21402
- }, onClick: onCanvasClick, onContextMenu: onCanvasContextMenu, onPointerMove: handleSvgPointerMove, onPointerLeave: handleSvgPointerLeave, children: [(0, import_jsx_runtime24.jsx)("defs", { children: (0, import_jsx_runtime24.jsx)("pattern", { id: "system-canvas-grid", width: theme.grid.size, height: theme.grid.size, patternUnits: "userSpaceOnUse", children: (0, import_jsx_runtime24.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_runtime24.jsx)("rect", { x: "-50000", y: "-50000", width: "100000", height: "100000", fill: "url(#system-canvas-grid)" }), (0, import_jsx_runtime24.jsxs)("g", { ref: groupRef, children: [(0, import_jsx_runtime24.jsx)(LanesBackground, { columns, rows, theme }), (0, import_jsx_runtime24.jsx)(NodeRenderer, { nodes: visibleNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedIds, editingId, canvases, only: "groups", dimmedNodeIds, highlightedNodeIds }), (0, import_jsx_runtime24.jsx)(EdgeRenderer, { edges, nodeMap: renderNodeMap, theme, defaultEdgeStyle: edgeStyle, onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, selectedId: selectedEdgeId, editingId: editingEdgeId, dimmedNodeIds, parallelGroups, editable: !!onNodePointerDown, onWaypointCommit: onEdgeWaypointUpdate ? (id2, wps) => onEdgeWaypointUpdate(id2, { waypoints: wps }) : void 0, viewportRef: viewport }), (0, import_jsx_runtime24.jsx)(NodeRenderer, { nodes: visibleNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedIds, editingId, onResizeHandlePointerDown, canvases, only: "non-groups", dimmedNodeIds, highlightedNodeIds }), (0, import_jsx_runtime24.jsx)(RevealsLayer, { nodes: visibleNodes, theme, canvases, getViewport }), pendingTargetNode && (0, import_jsx_runtime24.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" }), dropTargetNode && (0, import_jsx_runtime24.jsx)("rect", { className: "system-canvas-drop-target system-canvas-node-drop-target", x: dropTargetNode.x - 4, y: dropTargetNode.y - 4, width: dropTargetNode.width + 8, height: dropTargetNode.height + 8, rx: dropTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, strokeDasharray: "6 4", opacity: 0.9, pointerEvents: "none" }), selectedIds && selectedIds.size > 0 && Array.from(selectedIds).map((id2) => {
21407
+ }, onClick: onCanvasClick, onContextMenu: onCanvasContextMenu, onPointerMove: handleSvgPointerMove, onPointerLeave: handleSvgPointerLeave, children: [(0, import_jsx_runtime24.jsx)("defs", { children: (0, import_jsx_runtime24.jsx)("pattern", { id: "system-canvas-grid", width: theme.grid.size, height: theme.grid.size, patternUnits: "userSpaceOnUse", children: (0, import_jsx_runtime24.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_runtime24.jsx)("rect", { x: "-50000", y: "-50000", width: "100000", height: "100000", fill: "url(#system-canvas-grid)" }), (0, import_jsx_runtime24.jsxs)("g", { ref: groupRef, children: [(0, import_jsx_runtime24.jsx)(LanesBackground, { columns, rows, theme }), (0, import_jsx_runtime24.jsx)(NodeRenderer, { nodes: visibleNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedIds, editingId, canvases, only: "groups", dimmedNodeIds, highlightedNodeIds, activeMatchNodeId }), (0, import_jsx_runtime24.jsx)(EdgeRenderer, { edges, nodeMap: renderNodeMap, theme, defaultEdgeStyle: edgeStyle, onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, selectedId: selectedEdgeId, editingId: editingEdgeId, dimmedNodeIds, parallelGroups, editable: !!onNodePointerDown, onWaypointCommit: onEdgeWaypointUpdate ? (id2, wps) => onEdgeWaypointUpdate(id2, { waypoints: wps }) : void 0, viewportRef: viewport }), (0, import_jsx_runtime24.jsx)(NodeRenderer, { nodes: visibleNodes, theme, onClick: onNodeClick, onDoubleClick: onNodeDoubleClick, onContextMenu: onNodeContextMenu, onNavigate: onNodeNavigate, onPointerDown: onNodePointerDown, selectedIds, editingId, onResizeHandlePointerDown, canvases, only: "non-groups", dimmedNodeIds, highlightedNodeIds, activeMatchNodeId }), (0, import_jsx_runtime24.jsx)(RevealsLayer, { nodes: visibleNodes, theme, canvases, getViewport }), pendingTargetNode && (0, import_jsx_runtime24.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" }), dropTargetNode && (0, import_jsx_runtime24.jsx)("rect", { className: "system-canvas-drop-target system-canvas-node-drop-target", x: dropTargetNode.x - 4, y: dropTargetNode.y - 4, width: dropTargetNode.width + 8, height: dropTargetNode.height + 8, rx: dropTargetNode.resolvedCornerRadius + 4, fill: "none", stroke: theme.node.labelColor, strokeWidth: 2, strokeDasharray: "6 4", opacity: 0.9, pointerEvents: "none" }), selectedIds && selectedIds.size > 0 && Array.from(selectedIds).map((id2) => {
21403
21408
  const node = renderNodeMap.get(id2);
21404
21409
  if (!node)
21405
21410
  return null;
@@ -22247,7 +22252,7 @@ var SystemCanvas = (() => {
22247
22252
  // ../react/dist/components/SearchOverlay.js
22248
22253
  var import_jsx_runtime31 = __toESM(require_jsx_runtime(), 1);
22249
22254
  var import_react30 = __toESM(require_react(), 1);
22250
- function SearchOverlay({ open, query, onQueryChange, theme, hiddenCategories, onToggleCategory, matchCount, totalCount, onClose, onPanToMatch }) {
22255
+ function SearchOverlay({ open, query, onQueryChange, theme, hiddenCategories, onToggleCategory, matchCount, matchIndex, onNext, onPrev, onClose }) {
22251
22256
  const inputRef = (0, import_react30.useRef)(null);
22252
22257
  (0, import_react30.useEffect)(() => {
22253
22258
  if (open) {
@@ -22265,9 +22270,12 @@ var SystemCanvas = (() => {
22265
22270
  if (e.key === "Escape") {
22266
22271
  e.preventDefault();
22267
22272
  onClose();
22268
- } else if (e.key === "Enter") {
22273
+ } else if (e.key === "Enter" || e.key === "ArrowDown") {
22274
+ e.preventDefault();
22275
+ onNext();
22276
+ } else if (e.key === "ArrowUp") {
22269
22277
  e.preventDefault();
22270
- onPanToMatch();
22278
+ onPrev();
22271
22279
  }
22272
22280
  };
22273
22281
  return (0, import_jsx_runtime31.jsxs)("div", { style: {
@@ -22301,14 +22309,41 @@ var SystemCanvas = (() => {
22301
22309
  fontSize: 13,
22302
22310
  width: 160,
22303
22311
  caretColor: textColor
22304
- } }), query.length > 0 && (0, import_jsx_runtime31.jsxs)("span", { style: {
22312
+ } }), query.length > 0 && matchCount > 0 && (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [(0, import_jsx_runtime31.jsx)("button", { onClick: onPrev, style: {
22313
+ background: "transparent",
22314
+ border: "none",
22315
+ cursor: "pointer",
22316
+ padding: "2px 3px",
22317
+ display: "flex",
22318
+ alignItems: "center",
22319
+ color: textColor,
22320
+ opacity: 0.7,
22321
+ flexShrink: 0
22322
+ }, title: "Previous match (\u2191)", children: (0, import_jsx_runtime31.jsx)("svg", { width: 10, height: 10, viewBox: "0 0 10 10", fill: "none", stroke: "currentColor", strokeWidth: 1.5, children: (0, import_jsx_runtime31.jsx)("polyline", { points: "2,7 5,3 8,7" }) }) }), (0, import_jsx_runtime31.jsxs)("span", { style: {
22305
22323
  fontSize: 11,
22306
22324
  fontFamily,
22307
22325
  color: textColor,
22308
22326
  opacity: 0.7,
22309
22327
  whiteSpace: "nowrap",
22310
22328
  flexShrink: 0
22311
- }, children: [matchCount, " of ", totalCount] }), (0, import_jsx_runtime31.jsx)("button", { onClick: onClose, style: {
22329
+ }, children: [matchIndex + 1, "/", matchCount] }), (0, import_jsx_runtime31.jsx)("button", { onClick: onNext, style: {
22330
+ background: "transparent",
22331
+ border: "none",
22332
+ cursor: "pointer",
22333
+ padding: "2px 3px",
22334
+ display: "flex",
22335
+ alignItems: "center",
22336
+ color: textColor,
22337
+ opacity: 0.7,
22338
+ flexShrink: 0
22339
+ }, title: "Next match (\u2193)", children: (0, import_jsx_runtime31.jsx)("svg", { width: 10, height: 10, viewBox: "0 0 10 10", fill: "none", stroke: "currentColor", strokeWidth: 1.5, children: (0, import_jsx_runtime31.jsx)("polyline", { points: "2,3 5,7 8,3" }) }) })] }), query.length > 0 && matchCount === 0 && (0, import_jsx_runtime31.jsx)("span", { style: {
22340
+ fontSize: 11,
22341
+ fontFamily,
22342
+ color: textColor,
22343
+ opacity: 0.5,
22344
+ whiteSpace: "nowrap",
22345
+ flexShrink: 0
22346
+ }, children: "No matches" }), (0, import_jsx_runtime31.jsx)("button", { onClick: onClose, style: {
22312
22347
  background: "transparent",
22313
22348
  border: "none",
22314
22349
  cursor: "pointer",
@@ -22431,21 +22466,45 @@ var SystemCanvas = (() => {
22431
22466
  top: screen.y,
22432
22467
  transform: "translate(0, 0)",
22433
22468
  userSelect: "none"
22434
- }, children: [(0, import_jsx_runtime32.jsx)("svg", { width: "16", height: "20", viewBox: "0 0 16 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: (0, import_jsx_runtime32.jsx)("path", { d: "M1 1L1 15L5 11L8 18L10 17L7 10L13 10L1 1Z", fill: collab.color, stroke: "#fff", strokeWidth: "1.5", strokeLinejoin: "round" }) }), (0, import_jsx_runtime32.jsx)("div", { style: {
22469
+ }, children: [(0, import_jsx_runtime32.jsx)("svg", { width: "16", height: "20", viewBox: "0 0 16 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: (0, import_jsx_runtime32.jsx)("path", { d: "M1 1L1 15L5 11L8 18L10 17L7 10L13 10L1 1Z", fill: collab.color, stroke: "#fff", strokeWidth: "1.5", strokeLinejoin: "round" }) }), (0, import_jsx_runtime32.jsxs)("div", { style: {
22435
22470
  position: "absolute",
22436
22471
  top: 18,
22437
22472
  left: 8,
22438
- fontSize: 11,
22439
- lineHeight: "16px",
22440
- padding: "1px 6px",
22473
+ display: "flex",
22474
+ alignItems: "center",
22475
+ gap: 4,
22476
+ background: collab.color,
22441
22477
  borderRadius: 8,
22478
+ padding: "1px 4px 1px 2px",
22479
+ boxShadow: "0 1px 3px rgba(0,0,0,0.3)",
22480
+ whiteSpace: "nowrap",
22481
+ fontFamily: "sans-serif"
22482
+ }, children: [(0, import_jsx_runtime32.jsx)("div", { style: {
22483
+ width: 16,
22484
+ height: 16,
22485
+ borderRadius: "50%",
22486
+ overflow: "hidden",
22487
+ flexShrink: 0,
22488
+ display: "flex",
22489
+ alignItems: "center",
22490
+ justifyContent: "center",
22491
+ background: collab.image ? "transparent" : collab.color
22492
+ }, children: collab.image ? (0, import_jsx_runtime32.jsx)("img", { src: collab.image, width: 16, height: 16, style: { objectFit: "cover", borderRadius: "50%" } }) : (0, import_jsx_runtime32.jsx)("div", { style: {
22493
+ width: 16,
22494
+ height: 16,
22495
+ display: "flex",
22496
+ alignItems: "center",
22497
+ justifyContent: "center",
22442
22498
  background: collab.color,
22443
22499
  color: "#fff",
22444
- whiteSpace: "nowrap",
22445
- fontFamily: "sans-serif",
22446
- fontWeight: 500,
22447
- boxShadow: "0 1px 3px rgba(0,0,0,0.3)"
22448
- }, children: collab.name })] }, `cursor-${collab.id}`);
22500
+ fontSize: 9,
22501
+ fontWeight: 700
22502
+ }, children: collab.name ? collab.name[0].toUpperCase() : "?" }) }), (0, import_jsx_runtime32.jsx)("span", { style: {
22503
+ fontSize: 11,
22504
+ lineHeight: "16px",
22505
+ color: "#fff",
22506
+ fontWeight: 500
22507
+ }, children: collab.name })] })] }, `cursor-${collab.id}`);
22449
22508
  }), Array.from(flashNodeIds.keys()).map((nodeId) => {
22450
22509
  const node = nodeMap.get(nodeId);
22451
22510
  if (!node)
@@ -22861,6 +22920,7 @@ var SystemCanvas = (() => {
22861
22920
  const [searchOpen, setSearchOpen] = (0, import_react33.useState)(false);
22862
22921
  const [searchQuery, setSearchQuery] = (0, import_react33.useState)("");
22863
22922
  const [hiddenCategories, setHiddenCategories] = (0, import_react33.useState)(/* @__PURE__ */ new Set());
22923
+ const [searchIndex, setSearchIndex] = (0, import_react33.useState)(0);
22864
22924
  const [importError, setImportError] = (0, import_react33.useState)(null);
22865
22925
  const svgProxyRef = (0, import_react33.useRef)(null);
22866
22926
  const containerRef = (0, import_react33.useRef)(null);
@@ -22921,13 +22981,7 @@ var SystemCanvas = (() => {
22921
22981
  const onKey = (e) => {
22922
22982
  if ((e.metaKey || e.ctrlKey) && e.key === "f") {
22923
22983
  e.preventDefault();
22924
- setSearchOpen((prev) => {
22925
- if (prev) {
22926
- setSearchQuery("");
22927
- setHiddenCategories(/* @__PURE__ */ new Set());
22928
- }
22929
- return !prev;
22930
- });
22984
+ setSearchOpen((prev) => !prev);
22931
22985
  }
22932
22986
  };
22933
22987
  window.addEventListener("keydown", onKey);
@@ -23012,11 +23066,16 @@ var SystemCanvas = (() => {
23012
23066
  const showLaneHeaders = hasLanes && laneHeaders !== "none";
23013
23067
  const getViewportState = (0, import_react33.useCallback)(() => viewportStateRef.current ?? { x: 0, y: 0, zoom: 1 }, []);
23014
23068
  const { matchingIds, dimmedIds } = (0, import_react33.useMemo)(() => computeNodeFilter(nodes, searchOpen ? searchQuery : "", hiddenCategories), [nodes, searchQuery, searchOpen, hiddenCategories]);
23015
- const panToFirstMatch = (0, import_react33.useCallback)(() => {
23016
- if (matchingIds.size === 0)
23069
+ const matchingIdsArray = (0, import_react33.useMemo)(() => Array.from(matchingIds), [matchingIds]);
23070
+ const activeMatchId = matchingIdsArray[searchIndex];
23071
+ (0, import_react33.useEffect)(() => {
23072
+ setSearchIndex(0);
23073
+ }, [matchingIds]);
23074
+ const panToMatch = (0, import_react33.useCallback)((index) => {
23075
+ const id2 = matchingIdsArray[index];
23076
+ if (!id2)
23017
23077
  return;
23018
- const firstId = matchingIds.values().next().value;
23019
- const node = nodesRef.current.find((n) => n.id === firstId);
23078
+ const node = nodesRef.current.find((n) => n.id === id2);
23020
23079
  const handle = viewportHandleRef.current;
23021
23080
  if (!node || !handle)
23022
23081
  return;
@@ -23025,7 +23084,21 @@ var SystemCanvas = (() => {
23025
23084
  targetZoom: Math.max(currentZoom, 1.5),
23026
23085
  durationMs: 400
23027
23086
  });
23028
- }, [matchingIds]);
23087
+ }, [matchingIdsArray]);
23088
+ const goNextMatch = (0, import_react33.useCallback)(() => {
23089
+ if (matchingIdsArray.length === 0)
23090
+ return;
23091
+ const next = (searchIndex + 1) % matchingIdsArray.length;
23092
+ setSearchIndex(next);
23093
+ panToMatch(next);
23094
+ }, [matchingIdsArray, searchIndex, panToMatch]);
23095
+ const goPrevMatch = (0, import_react33.useCallback)(() => {
23096
+ if (matchingIdsArray.length === 0)
23097
+ return;
23098
+ const prev = (searchIndex - 1 + matchingIdsArray.length) % matchingIdsArray.length;
23099
+ setSearchIndex(prev);
23100
+ panToMatch(prev);
23101
+ }, [matchingIdsArray, searchIndex, panToMatch]);
23029
23102
  const applyLaneSnap = (0, import_react33.useCallback)((id2, patch) => {
23030
23103
  if (!snapToLanes)
23031
23104
  return patch;
@@ -23563,7 +23636,7 @@ var SystemCanvas = (() => {
23563
23636
  fontFamily: theme.node.fontFamily,
23564
23637
  fontSize: 12,
23565
23638
  backdropFilter: "blur(8px)"
23566
- }, children: "Loading..." }), (0, import_jsx_runtime34.jsx)(Viewport, { ref: viewportHandleRef, nodes, edges, nodeMap, theme, edgeStyle, columns: currentCanvas.columns, rows: currentCanvas.rows, canvases, minZoom: effectiveMinZoom, maxZoom: effectiveMaxZoom, defaultViewport, panMode, 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, selectedIds: editable ? selectedIds : void 0, marqueeRect: editable ? marqueeRect : null, marqueeActiveRef: editable ? marqueeActiveRef : void 0, editingId: editable ? editingId : null, selectedEdgeId: editable ? selectedEdgeId : null, editingEdgeId: editable ? editingEdgeId : null, dragOverrides, dropTargetId, resizeOverrides, onResizeHandlePointerDown: editable ? onResizeHandlePointerDown : void 0, onEditorCommit: handleEditorCommit, onEditorCancel: handleEditorCancel, onEdgeEditorCommit: handleEdgeEditorCommit, onEdgeEditorCancel: handleEdgeEditorCancel, onEdgeWaypointUpdate: editable ? handleEdgeWaypointUpdate : void 0, pendingEdge: editable ? pendingEdge : null, onConnectionHandlePointerDown: editable ? onConnectionHandlePointerDown : void 0, edgeCreateEnabled: editable, alignmentGuides: editable ? alignmentGuides : void 0, dimmedNodeIds: dimmedIds, highlightedNodeIds: matchingIds, viewportState: collaboratorViewport }), collaborators.length > 0 || flashNodeIds.size > 0 ? (0, import_jsx_runtime34.jsx)(CollaboratorsOverlay, { collaborators, viewport: collaboratorViewport, nodeMap, flashNodeIds }) : null, showLaneHeaders && (0, import_jsx_runtime34.jsx)(LaneHeaders, { columns: currentCanvas.columns, rows: currentCanvas.rows, theme, getViewport: getViewportState, width: containerSize.width, height: containerSize.height, pinned: laneHeaders === "pinned" }), editable && showNodeToolbar && selectedIds.size === 1 && selectedResolvedNode && !editingId && (0, import_jsx_runtime34.jsx)(NodeToolbar, { node: selectedResolvedNode, theme, onPatch: (update) => {
23639
+ }, children: "Loading..." }), (0, import_jsx_runtime34.jsx)(Viewport, { ref: viewportHandleRef, nodes, edges, nodeMap, theme, edgeStyle, columns: currentCanvas.columns, rows: currentCanvas.rows, canvases, minZoom: effectiveMinZoom, maxZoom: effectiveMaxZoom, defaultViewport, panMode, 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, selectedIds: editable ? selectedIds : void 0, marqueeRect: editable ? marqueeRect : null, marqueeActiveRef: editable ? marqueeActiveRef : void 0, editingId: editable ? editingId : null, selectedEdgeId: editable ? selectedEdgeId : null, editingEdgeId: editable ? editingEdgeId : null, dragOverrides, dropTargetId, resizeOverrides, onResizeHandlePointerDown: editable ? onResizeHandlePointerDown : void 0, onEditorCommit: handleEditorCommit, onEditorCancel: handleEditorCancel, onEdgeEditorCommit: handleEdgeEditorCommit, onEdgeEditorCancel: handleEdgeEditorCancel, onEdgeWaypointUpdate: editable ? handleEdgeWaypointUpdate : void 0, pendingEdge: editable ? pendingEdge : null, onConnectionHandlePointerDown: editable ? onConnectionHandlePointerDown : void 0, edgeCreateEnabled: editable, alignmentGuides: editable ? alignmentGuides : void 0, dimmedNodeIds: dimmedIds, highlightedNodeIds: matchingIds, activeMatchNodeId: activeMatchId, viewportState: collaboratorViewport }), collaborators.length > 0 || flashNodeIds.size > 0 ? (0, import_jsx_runtime34.jsx)(CollaboratorsOverlay, { collaborators, viewport: collaboratorViewport, nodeMap, flashNodeIds }) : null, showLaneHeaders && (0, import_jsx_runtime34.jsx)(LaneHeaders, { columns: currentCanvas.columns, rows: currentCanvas.rows, theme, getViewport: getViewportState, width: containerSize.width, height: containerSize.height, pinned: laneHeaders === "pinned" }), editable && showNodeToolbar && selectedIds.size === 1 && selectedResolvedNode && !editingId && (0, import_jsx_runtime34.jsx)(NodeToolbar, { node: selectedResolvedNode, theme, onPatch: (update) => {
23567
23640
  wrappedOnNodeUpdate(selectedResolvedNode.id, update, currentCanvasRef);
23568
23641
  }, onDelete: () => {
23569
23642
  wrappedOnNodeDelete(selectedResolvedNode.id, currentCanvasRef);
@@ -23595,11 +23668,7 @@ var SystemCanvas = (() => {
23595
23668
  const next = new Set(prev);
23596
23669
  next.has(cat) ? next.delete(cat) : next.add(cat);
23597
23670
  return next;
23598
- }), matchCount: matchingIds.size, totalCount: nodes.length, onClose: () => {
23599
- setSearchOpen(false);
23600
- setSearchQuery("");
23601
- setHiddenCategories(/* @__PURE__ */ new Set());
23602
- }, onPanToMatch: panToFirstMatch })] });
23671
+ }), matchCount: matchingIds.size, matchIndex: searchIndex, onNext: goNextMatch, onPrev: goPrevMatch, onClose: () => setSearchOpen(false) })] });
23603
23672
  });
23604
23673
 
23605
23674
  // src/index.tsx