@petrarca/sonnet-graph 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -182,15 +182,225 @@ var EMPTY_NORMALIZED = {
182
182
  edgeIds: []
183
183
  };
184
184
 
185
- // src/stores/Selection.ts
185
+ // src/graph/extractFromRows.ts
186
+ function isNodeCell(value) {
187
+ return typeof value === "object" && value !== null && "node" in value;
188
+ }
189
+ function isEdgeCell(value) {
190
+ return typeof value === "object" && value !== null && "edge" in value;
191
+ }
192
+ function extractGraphFromRows(rows) {
193
+ const nodesById = /* @__PURE__ */ new Map();
194
+ const edgesById = /* @__PURE__ */ new Map();
195
+ for (const row of rows) {
196
+ for (const cell of row) {
197
+ if (isNodeCell(cell) && cell.node.id_ !== void 0) {
198
+ nodesById.set(cell.node.id_, cell.node);
199
+ } else if (isEdgeCell(cell) && cell.edge.id_ !== void 0) {
200
+ edgesById.set(cell.edge.id_, cell.edge);
201
+ }
202
+ }
203
+ }
204
+ return {
205
+ nodes: [...nodesById.values()],
206
+ edges: [...edgesById.values()],
207
+ isGraph: nodesById.size > 0 || edgesById.size > 0
208
+ };
209
+ }
210
+
211
+ // src/query/QueryEditor.tsx
212
+ import { useRef, useCallback, useEffect, useState } from "react";
213
+ import {
214
+ Alert,
215
+ Button,
216
+ InputGroup,
217
+ InputGroupAddon,
218
+ InputGroupButton,
219
+ InputGroupTextarea,
220
+ SimpleTooltip
221
+ } from "@petrarca/sonnet-ui";
222
+ import { copyTextToClipboard } from "@petrarca/sonnet-core";
223
+ import { Play, Copy } from "lucide-react";
224
+ import { jsx, jsxs } from "react/jsx-runtime";
225
+ var LINE_HEIGHT = 24;
226
+ function CopyErrorButton({
227
+ error,
228
+ query
229
+ }) {
230
+ return /* @__PURE__ */ jsx(SimpleTooltip, { label: "Copy error and query", children: /* @__PURE__ */ jsx(
231
+ Button,
232
+ {
233
+ variant: "ghost",
234
+ size: "icon",
235
+ onClick: () => copyTextToClipboard(`Error: ${error}
236
+
237
+ Query:
238
+ ${query}`),
239
+ className: "ml-2",
240
+ children: /* @__PURE__ */ jsx(Copy, { size: 16 })
241
+ }
242
+ ) });
243
+ }
244
+ function QueryEditor({
245
+ value,
246
+ onExecute,
247
+ onChange,
248
+ isLoading = false,
249
+ error = null,
250
+ placeholder = "Example: MATCH (p)-[e]->(t) RETURN p,e,t LIMIT 10",
251
+ minRows = 1,
252
+ maxRows = 12,
253
+ className,
254
+ renderActions
255
+ }) {
256
+ const textareaRef = useRef(null);
257
+ const [localValue, setLocalValue] = useState(value);
258
+ const handleResize = useCallback(() => {
259
+ const textarea = textareaRef.current;
260
+ if (!textarea) return;
261
+ textarea.style.height = "auto";
262
+ const minH = minRows * LINE_HEIGHT;
263
+ const maxH = maxRows * LINE_HEIGHT;
264
+ const newHeight = Math.min(Math.max(textarea.scrollHeight, minH), maxH);
265
+ textarea.style.height = `${newHeight}px`;
266
+ textarea.style.overflowY = textarea.scrollHeight > maxH ? "auto" : "hidden";
267
+ }, [minRows, maxRows]);
268
+ useEffect(() => {
269
+ setLocalValue(value);
270
+ if (textareaRef.current && textareaRef.current.value !== value) {
271
+ textareaRef.current.value = value;
272
+ }
273
+ setTimeout(handleResize, 0);
274
+ }, [value, handleResize]);
275
+ const handleExecute = useCallback(() => {
276
+ const current = textareaRef.current?.value || "";
277
+ if (!current.trim()) return;
278
+ onExecute(current);
279
+ }, [onExecute]);
280
+ const handleKeyDown = useCallback(
281
+ (e) => {
282
+ if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
283
+ e.preventDefault();
284
+ handleExecute();
285
+ }
286
+ },
287
+ [handleExecute]
288
+ );
289
+ const textareaRefCallback = (element) => {
290
+ textareaRef.current = element;
291
+ if (element) setTimeout(handleResize, 0);
292
+ };
293
+ const handleChange = useCallback(
294
+ (e) => {
295
+ setLocalValue(e.target.value);
296
+ onChange?.(e.target.value);
297
+ setTimeout(handleResize, 0);
298
+ },
299
+ [onChange, handleResize]
300
+ );
301
+ return /* @__PURE__ */ jsxs("div", { className, children: [
302
+ /* @__PURE__ */ jsxs(InputGroup, { className: "!ring-0 !ring-offset-0 focus:!ring-0 has-[[data-slot=input-group-control]:focus-visible]:!ring-0", children: [
303
+ /* @__PURE__ */ jsx(
304
+ InputGroupTextarea,
305
+ {
306
+ ref: textareaRefCallback,
307
+ value: localValue,
308
+ onChange: handleChange,
309
+ onKeyDown: handleKeyDown,
310
+ placeholder,
311
+ className: "resize-none font-mono",
312
+ style: { minHeight: `${minRows * LINE_HEIGHT}px` },
313
+ rows: minRows
314
+ }
315
+ ),
316
+ /* @__PURE__ */ jsx(InputGroupAddon, { align: "inline-end", className: "gap-0", children: renderActions ? renderActions({ onExecute: handleExecute, isLoading }) : /* @__PURE__ */ jsx(
317
+ InputGroupButton,
318
+ {
319
+ onClick: handleExecute,
320
+ disabled: isLoading,
321
+ title: "Execute query (Ctrl+Enter)",
322
+ children: /* @__PURE__ */ jsx(Play, { size: 16 })
323
+ }
324
+ ) })
325
+ ] }),
326
+ error && /* @__PURE__ */ jsx(Alert, { variant: "destructive", className: "p-2 py-1 mt-1", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-row justify-between items-start", children: [
327
+ /* @__PURE__ */ jsx("pre", { className: "text-sm m-0 flex-1 whitespace-pre-wrap break-words md:text-sm", children: error }),
328
+ /* @__PURE__ */ jsx(CopyErrorButton, { error, query: localValue })
329
+ ] }) })
330
+ ] });
331
+ }
332
+
333
+ // src/query/history.ts
186
334
  import { create } from "zustand";
335
+ import { persist } from "zustand/middleware";
336
+ var DEFAULT_LIMIT = 50;
337
+ function createQueryHistoryStore(persistKey, limit = DEFAULT_LIMIT) {
338
+ return create()(
339
+ persist(
340
+ (set, get) => ({
341
+ queries: [],
342
+ add: (query) => {
343
+ const trimmed = query.trim();
344
+ if (!trimmed) return;
345
+ set({
346
+ queries: [
347
+ trimmed,
348
+ ...get().queries.filter((q) => q !== trimmed)
349
+ ].slice(0, limit)
350
+ });
351
+ },
352
+ clear: () => set({ queries: [] })
353
+ }),
354
+ { name: persistKey, version: 1 }
355
+ )
356
+ );
357
+ }
358
+
359
+ // src/query/QueryHistory.tsx
360
+ import { History as HistoryIcon } from "lucide-react";
361
+ import {
362
+ Button as Button2,
363
+ DropdownMenu,
364
+ DropdownMenuContent,
365
+ DropdownMenuItem,
366
+ DropdownMenuSeparator,
367
+ DropdownMenuTrigger
368
+ } from "@petrarca/sonnet-ui";
369
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
370
+ function QueryHistory({
371
+ store,
372
+ onSelect
373
+ }) {
374
+ const queries = store((s) => s.queries);
375
+ const clear = store((s) => s.clear);
376
+ return /* @__PURE__ */ jsxs2(DropdownMenu, { children: [
377
+ /* @__PURE__ */ jsx2(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(
378
+ Button2,
379
+ {
380
+ variant: "ghost",
381
+ size: "icon",
382
+ title: "Query history",
383
+ disabled: queries.length === 0,
384
+ children: /* @__PURE__ */ jsx2(HistoryIcon, { size: 16 })
385
+ }
386
+ ) }),
387
+ /* @__PURE__ */ jsxs2(DropdownMenuContent, { align: "end", className: "max-w-[560px]", children: [
388
+ queries.map((q) => /* @__PURE__ */ jsx2(DropdownMenuItem, { onClick: () => onSelect(q), children: /* @__PURE__ */ jsx2("span", { className: "truncate font-mono text-xs", children: q }) }, q)),
389
+ queries.length > 0 && /* @__PURE__ */ jsx2(DropdownMenuSeparator, {}),
390
+ /* @__PURE__ */ jsx2(DropdownMenuItem, { onClick: clear, disabled: queries.length === 0, children: "Clear history" })
391
+ ] })
392
+ ] });
393
+ }
394
+
395
+ // src/stores/Selection.ts
396
+ import { create as create2 } from "zustand";
187
397
  import { devLog } from "@petrarca/sonnet-core";
188
398
  var MAX_NODE_SELECTION = 2;
189
399
  function evictToMax(selected, id, max = MAX_NODE_SELECTION) {
190
400
  return [...selected.filter((existing) => existing !== id), id].slice(-max);
191
401
  }
192
402
  function createSelectionStore() {
193
- return create()((set, get) => ({
403
+ return create2()((set, get) => ({
194
404
  selectedNodeIds: [],
195
405
  setSelectedNodeIds: (ids) => {
196
406
  const prev = get().selectedNodeIds;
@@ -217,10 +427,10 @@ function createSelectionStore() {
217
427
  var useSelection = createSelectionStore();
218
428
 
219
429
  // src/stores/GraphData.ts
220
- import { create as create2 } from "zustand";
430
+ import { create as create3 } from "zustand";
221
431
  import { devLog as devLog2 } from "@petrarca/sonnet-core";
222
432
  function createGraphDataStore() {
223
- return create2()((set, get) => ({
433
+ return create3()((set, get) => ({
224
434
  nodes: [],
225
435
  edges: [],
226
436
  stats: void 0,
@@ -328,7 +538,7 @@ function createGraphDataStore() {
328
538
  var useGraphData = createGraphDataStore();
329
539
 
330
540
  // src/stores/GraphFilter.ts
331
- import { create as create3 } from "zustand";
541
+ import { create as create4 } from "zustand";
332
542
  function calculateNodeLabelUpdate(affectedLabels, currentNodeFilters, allNodesDisabled) {
333
543
  if (affectedLabels.length === 0) return null;
334
544
  if (allNodesDisabled) {
@@ -366,7 +576,7 @@ function handleToggleFilter(label, current, exists, getNodeLabelUpdate, set) {
366
576
  set({ edgeLabelFilters: next, ...nodeUpdate });
367
577
  }
368
578
  function createGraphFilterStore() {
369
- return create3()((set, get) => ({
579
+ return create4()((set, get) => ({
370
580
  nodeLabelFilters: [],
371
581
  allLabelsDisabled: false,
372
582
  edgeLabelFilters: [],
@@ -442,7 +652,7 @@ function createGraphFilterStore() {
442
652
  var useGraphFilter = createGraphFilterStore();
443
653
 
444
654
  // src/stores/GraphExploration.ts
445
- import { create as create4 } from "zustand";
655
+ import { create as create5 } from "zustand";
446
656
  import { devLog as devLog3, errorLog } from "@petrarca/sonnet-core";
447
657
  import { toast } from "sonner";
448
658
  function collectReachable(startId, direction, allEdges, includeStart) {
@@ -569,7 +779,7 @@ function mergeExpandedLabelsIntoFilters(stores, enrichedNodes, nodeId2) {
569
779
  }
570
780
  function createGraphExplorationStore(deps) {
571
781
  const stores = deps;
572
- return create4()((set, get) => ({
782
+ return create5()((set, get) => ({
573
783
  focusedNodeIds: /* @__PURE__ */ new Set(),
574
784
  isFocusMode: false,
575
785
  hiddenElementIds: /* @__PURE__ */ new Set(),
@@ -978,7 +1188,7 @@ function createGraphExplorationStore(deps) {
978
1188
  }
979
1189
 
980
1190
  // src/stores/GraphFocus.ts
981
- import { create as create5 } from "zustand";
1191
+ import { create as create6 } from "zustand";
982
1192
  import { devLog as devLog4 } from "@petrarca/sonnet-core";
983
1193
  function buildStore() {
984
1194
  return (set, get) => ({
@@ -1012,13 +1222,13 @@ function buildStore() {
1012
1222
  });
1013
1223
  }
1014
1224
  function createGraphFocusStore() {
1015
- return create5()(buildStore());
1225
+ return create6()(buildStore());
1016
1226
  }
1017
1227
  var useGraphFocus = createGraphFocusStore();
1018
1228
  var GraphFocus_default = useGraphFocus;
1019
1229
 
1020
1230
  // src/stores/ActionPanelStore.ts
1021
- import { create as create6 } from "zustand";
1231
+ import { create as create7 } from "zustand";
1022
1232
  import { devLog as devLog5 } from "@petrarca/sonnet-core";
1023
1233
  function buildStore2() {
1024
1234
  return (set, get) => ({
@@ -1039,7 +1249,7 @@ function buildStore2() {
1039
1249
  });
1040
1250
  }
1041
1251
  function createActionPanelStore() {
1042
- return create6()(buildStore2());
1252
+ return create7()(buildStore2());
1043
1253
  }
1044
1254
  var useActionPanelStore = createActionPanelStore();
1045
1255
 
@@ -1072,18 +1282,18 @@ function useWorkspaceGraphFocus() {
1072
1282
  }
1073
1283
 
1074
1284
  // src/hooks/useStableRef.ts
1075
- import { useCallback, useRef } from "react";
1285
+ import { useCallback as useCallback2, useRef as useRef2 } from "react";
1076
1286
  function useStableRef(fn) {
1077
- const ref = useRef(fn);
1287
+ const ref = useRef2(fn);
1078
1288
  ref.current = fn;
1079
- return useCallback(
1289
+ return useCallback2(
1080
1290
  ((...args) => ref.current?.(...args)),
1081
1291
  []
1082
1292
  );
1083
1293
  }
1084
1294
 
1085
1295
  // src/hooks/useLiveGraphData.ts
1086
- import { useMemo, useEffect } from "react";
1296
+ import { useMemo, useEffect as useEffect2 } from "react";
1087
1297
  import { devLog as devLog6 } from "@petrarca/sonnet-core";
1088
1298
  function useLiveGraphData() {
1089
1299
  const useGraphFilter2 = useWorkspaceGraphFilter();
@@ -1111,7 +1321,7 @@ function useLiveGraphData() {
1111
1321
  focusedCount: focusedNodeIds.size,
1112
1322
  hiddenCount: hiddenElementIds.size
1113
1323
  });
1114
- useEffect(() => {
1324
+ useEffect2(() => {
1115
1325
  useGraphFocus2.getState().clearOverrides();
1116
1326
  }, [lastDataChangeTime, useGraphFocus2]);
1117
1327
  const allLabelCounts = useMemo(() => {
@@ -1309,7 +1519,7 @@ function useLiveGraphData() {
1309
1519
  edges: availableEdges.length
1310
1520
  };
1311
1521
  }, [stats, availableNodes.length, availableEdges.length]);
1312
- useEffect(() => {
1522
+ useEffect2(() => {
1313
1523
  const { clearOverrides } = useGraphFocus2.getState();
1314
1524
  clearOverrides();
1315
1525
  }, [
@@ -1448,30 +1658,30 @@ function useGraphRenderer() {
1448
1658
  }
1449
1659
 
1450
1660
  // src/hooks/graphRendererProvider.tsx
1451
- import { useState, useCallback as useCallback2 } from "react";
1452
- import { jsx } from "react/jsx-runtime";
1661
+ import { useState as useState2, useCallback as useCallback3 } from "react";
1662
+ import { jsx as jsx3 } from "react/jsx-runtime";
1453
1663
  function GraphRendererProvider({
1454
1664
  children,
1455
1665
  defaultRenderer = "cytoscape",
1456
1666
  onRendererChange
1457
1667
  }) {
1458
- const [renderer, setRendererState] = useState(defaultRenderer);
1459
- const setRenderer = useCallback2(
1668
+ const [renderer, setRendererState] = useState2(defaultRenderer);
1669
+ const setRenderer = useCallback3(
1460
1670
  (newRenderer) => {
1461
1671
  setRendererState(newRenderer);
1462
1672
  onRendererChange?.(newRenderer);
1463
1673
  },
1464
1674
  [onRendererChange]
1465
1675
  );
1466
- return /* @__PURE__ */ jsx(GraphRendererContext.Provider, { value: { renderer, setRenderer }, children });
1676
+ return /* @__PURE__ */ jsx3(GraphRendererContext.Provider, { value: { renderer, setRenderer }, children });
1467
1677
  }
1468
1678
 
1469
1679
  // src/components/rea/ReaGraph.tsx
1470
1680
  import {
1471
- useEffect as useEffect3,
1472
- useRef as useRef3,
1473
- useState as useState5,
1474
- useCallback as useCallback4
1681
+ useEffect as useEffect4,
1682
+ useRef as useRef4,
1683
+ useState as useState6,
1684
+ useCallback as useCallback5
1475
1685
  } from "react";
1476
1686
  import { devLog as devLog8 } from "@petrarca/sonnet-core";
1477
1687
  import {
@@ -1516,43 +1726,43 @@ import { cn } from "@petrarca/sonnet-core";
1516
1726
 
1517
1727
  // src/components/rea/LayoutAndCameraControls.tsx
1518
1728
  import {
1519
- Button as Button2,
1520
- DropdownMenu as DropdownMenu2,
1521
- DropdownMenuContent as DropdownMenuContent2,
1522
- DropdownMenuItem as DropdownMenuItem2,
1729
+ Button as Button4,
1730
+ DropdownMenu as DropdownMenu3,
1731
+ DropdownMenuContent as DropdownMenuContent3,
1732
+ DropdownMenuItem as DropdownMenuItem3,
1523
1733
  DropdownMenuLabel,
1524
- DropdownMenuSeparator,
1525
- DropdownMenuTrigger as DropdownMenuTrigger2,
1526
- SimpleTooltip as SimpleTooltip2
1734
+ DropdownMenuSeparator as DropdownMenuSeparator2,
1735
+ DropdownMenuTrigger as DropdownMenuTrigger3,
1736
+ SimpleTooltip as SimpleTooltip3
1527
1737
  } from "@petrarca/sonnet-ui";
1528
1738
  import { LayoutPanelTop, Maximize, Shrink, ArrowUp, Eye } from "lucide-react";
1529
- import { useState as useState2 } from "react";
1739
+ import { useState as useState3 } from "react";
1530
1740
 
1531
1741
  // src/components/shared/RendererDropdown.tsx
1532
1742
  import {
1533
- Button,
1534
- DropdownMenu,
1535
- DropdownMenuContent,
1536
- DropdownMenuItem,
1537
- DropdownMenuTrigger,
1538
- SimpleTooltip
1743
+ Button as Button3,
1744
+ DropdownMenu as DropdownMenu2,
1745
+ DropdownMenuContent as DropdownMenuContent2,
1746
+ DropdownMenuItem as DropdownMenuItem2,
1747
+ DropdownMenuTrigger as DropdownMenuTrigger2,
1748
+ SimpleTooltip as SimpleTooltip2
1539
1749
  } from "@petrarca/sonnet-ui";
1540
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1750
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1541
1751
  function RendererDropdown() {
1542
1752
  const { renderer, setRenderer } = useGraphRenderer();
1543
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1544
- /* @__PURE__ */ jsx2(SimpleTooltip, { label: "Renderer", side: "left", children: /* @__PURE__ */ jsx2(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(Button, { variant: "ghost", size: "icon", children: renderer === "reagraph" ? "RG" : "CY" }) }) }),
1545
- /* @__PURE__ */ jsxs(DropdownMenuContent, { side: "left", align: "start", children: [
1546
- /* @__PURE__ */ jsx2(
1547
- DropdownMenuItem,
1753
+ return /* @__PURE__ */ jsxs3(DropdownMenu2, { children: [
1754
+ /* @__PURE__ */ jsx4(SimpleTooltip2, { label: "Renderer", side: "left", children: /* @__PURE__ */ jsx4(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx4(Button3, { variant: "ghost", size: "icon", children: renderer === "reagraph" ? "RG" : "CY" }) }) }),
1755
+ /* @__PURE__ */ jsxs3(DropdownMenuContent2, { side: "left", align: "start", children: [
1756
+ /* @__PURE__ */ jsx4(
1757
+ DropdownMenuItem2,
1548
1758
  {
1549
1759
  onClick: () => setRenderer("reagraph"),
1550
1760
  className: renderer === "reagraph" ? "text-blue-600" : "",
1551
1761
  children: "Reagraph"
1552
1762
  }
1553
1763
  ),
1554
- /* @__PURE__ */ jsx2(
1555
- DropdownMenuItem,
1764
+ /* @__PURE__ */ jsx4(
1765
+ DropdownMenuItem2,
1556
1766
  {
1557
1767
  onClick: () => setRenderer("cytoscape"),
1558
1768
  className: renderer === "cytoscape" ? "text-blue-600" : "",
@@ -1564,46 +1774,46 @@ function RendererDropdown() {
1564
1774
  }
1565
1775
 
1566
1776
  // src/components/rea/LayoutAndCameraControls.tsx
1567
- import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1777
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1568
1778
  function PathSelectionDropdown({
1569
1779
  setPathSelectionType
1570
1780
  }) {
1571
- return /* @__PURE__ */ jsxs2(DropdownMenu2, { children: [
1572
- /* @__PURE__ */ jsx3(SimpleTooltip2, { label: "Path Selection Type", side: "left", children: /* @__PURE__ */ jsx3(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx3(ArrowUp, {}) }) }) }),
1573
- /* @__PURE__ */ jsxs2(DropdownMenuContent2, { side: "left", align: "start", children: [
1574
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => setPathSelectionType("direct"), children: "None" }),
1575
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => setPathSelectionType("all"), children: "All Paths" }),
1576
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => setPathSelectionType("in"), children: "Inbound" }),
1577
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => setPathSelectionType("out"), children: "Outbound" })
1781
+ return /* @__PURE__ */ jsxs4(DropdownMenu3, { children: [
1782
+ /* @__PURE__ */ jsx5(SimpleTooltip3, { label: "Path Selection Type", side: "left", children: /* @__PURE__ */ jsx5(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx5(Button4, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx5(ArrowUp, {}) }) }) }),
1783
+ /* @__PURE__ */ jsxs4(DropdownMenuContent3, { side: "left", align: "start", children: [
1784
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => setPathSelectionType("direct"), children: "None" }),
1785
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => setPathSelectionType("all"), children: "All Paths" }),
1786
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => setPathSelectionType("in"), children: "Inbound" }),
1787
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => setPathSelectionType("out"), children: "Outbound" })
1578
1788
  ] })
1579
1789
  ] });
1580
1790
  }
1581
1791
  function LayoutDropdown({ updateLayout }) {
1582
- const [showTooltip, setShowTooltip] = useState2(true);
1792
+ const [showTooltip, setShowTooltip] = useState3(true);
1583
1793
  const handleSelect = (layout) => {
1584
1794
  updateLayout(layout);
1585
1795
  setShowTooltip(false);
1586
1796
  setTimeout(() => setShowTooltip(true), 500);
1587
1797
  };
1588
- const trigger = /* @__PURE__ */ jsx3(DropdownMenuTrigger2, { asChild: true, children: /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx3(LayoutPanelTop, {}) }) });
1589
- return /* @__PURE__ */ jsxs2(DropdownMenu2, { children: [
1590
- showTooltip ? /* @__PURE__ */ jsx3(SimpleTooltip2, { label: "Layout", side: "left", children: trigger }) : trigger,
1591
- /* @__PURE__ */ jsxs2(DropdownMenuContent2, { side: "left", align: "start", children: [
1592
- /* @__PURE__ */ jsx3(DropdownMenuLabel, { children: "Force-directed Layouts" }),
1593
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("forceDirected2d"), children: "Force Directed" }),
1594
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("forceatlas2"), children: "Force Atlas 2" }),
1595
- /* @__PURE__ */ jsx3(DropdownMenuSeparator, {}),
1596
- /* @__PURE__ */ jsx3(DropdownMenuLabel, { children: "Tree Layouts" }),
1597
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("treeTd2d"), children: "Tree Top-Down" }),
1598
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("treeLr2d"), children: "Tree Left-Right" }),
1599
- /* @__PURE__ */ jsx3(DropdownMenuSeparator, {}),
1600
- /* @__PURE__ */ jsx3(DropdownMenuLabel, { children: "Circular & Radial Layouts" }),
1601
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("circular2d"), children: "Circular" }),
1602
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("radialOut2d"), children: "Radial Out" }),
1603
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("concentric2d"), children: "Concentric" }),
1604
- /* @__PURE__ */ jsx3(DropdownMenuSeparator, {}),
1605
- /* @__PURE__ */ jsx3(DropdownMenuLabel, { children: "Utility Layouts" }),
1606
- /* @__PURE__ */ jsx3(DropdownMenuItem2, { onClick: () => handleSelect("nooverlap"), children: "No Overlap" })
1798
+ const trigger = /* @__PURE__ */ jsx5(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx5(Button4, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx5(LayoutPanelTop, {}) }) });
1799
+ return /* @__PURE__ */ jsxs4(DropdownMenu3, { children: [
1800
+ showTooltip ? /* @__PURE__ */ jsx5(SimpleTooltip3, { label: "Layout", side: "left", children: trigger }) : trigger,
1801
+ /* @__PURE__ */ jsxs4(DropdownMenuContent3, { side: "left", align: "start", children: [
1802
+ /* @__PURE__ */ jsx5(DropdownMenuLabel, { children: "Force-directed Layouts" }),
1803
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("forceDirected2d"), children: "Force Directed" }),
1804
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("forceatlas2"), children: "Force Atlas 2" }),
1805
+ /* @__PURE__ */ jsx5(DropdownMenuSeparator2, {}),
1806
+ /* @__PURE__ */ jsx5(DropdownMenuLabel, { children: "Tree Layouts" }),
1807
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("treeTd2d"), children: "Tree Top-Down" }),
1808
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("treeLr2d"), children: "Tree Left-Right" }),
1809
+ /* @__PURE__ */ jsx5(DropdownMenuSeparator2, {}),
1810
+ /* @__PURE__ */ jsx5(DropdownMenuLabel, { children: "Circular & Radial Layouts" }),
1811
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("circular2d"), children: "Circular" }),
1812
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("radialOut2d"), children: "Radial Out" }),
1813
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("concentric2d"), children: "Concentric" }),
1814
+ /* @__PURE__ */ jsx5(DropdownMenuSeparator2, {}),
1815
+ /* @__PURE__ */ jsx5(DropdownMenuLabel, { children: "Utility Layouts" }),
1816
+ /* @__PURE__ */ jsx5(DropdownMenuItem3, { onClick: () => handleSelect("nooverlap"), children: "No Overlap" })
1607
1817
  ] })
1608
1818
  ] });
1609
1819
  }
@@ -1615,15 +1825,15 @@ function ViewControls({
1615
1825
  isFocusMode,
1616
1826
  onResetView
1617
1827
  }) {
1618
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
1619
- /* @__PURE__ */ jsx3(SimpleTooltip2, { label: "Fullscreen", side: "left", children: /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "icon", onClick: toggleFullscreen, children: !fullscreen ? /* @__PURE__ */ jsx3(Maximize, {}) : /* @__PURE__ */ jsx3(Shrink, {}) }) }),
1620
- /* @__PURE__ */ jsx3(SimpleTooltip2, { label: "Fit to screen", side: "left", children: /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "icon", onClick: fitToScreen, children: /* @__PURE__ */ jsx3(Shrink, {}) }) }),
1621
- (hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */ jsx3(
1622
- SimpleTooltip2,
1828
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
1829
+ /* @__PURE__ */ jsx5(SimpleTooltip3, { label: "Fullscreen", side: "left", children: /* @__PURE__ */ jsx5(Button4, { variant: "ghost", size: "icon", onClick: toggleFullscreen, children: !fullscreen ? /* @__PURE__ */ jsx5(Maximize, {}) : /* @__PURE__ */ jsx5(Shrink, {}) }) }),
1830
+ /* @__PURE__ */ jsx5(SimpleTooltip3, { label: "Fit to screen", side: "left", children: /* @__PURE__ */ jsx5(Button4, { variant: "ghost", size: "icon", onClick: fitToScreen, children: /* @__PURE__ */ jsx5(Shrink, {}) }) }),
1831
+ (hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */ jsx5(
1832
+ SimpleTooltip3,
1623
1833
  {
1624
1834
  label: isFocusMode ? "Reset Focus" : "Show All Hidden",
1625
1835
  side: "left",
1626
- children: /* @__PURE__ */ jsx3(Button2, { variant: "ghost", size: "icon", onClick: onResetView, children: /* @__PURE__ */ jsx3(Eye, {}) })
1836
+ children: /* @__PURE__ */ jsx5(Button4, { variant: "ghost", size: "icon", onClick: onResetView, children: /* @__PURE__ */ jsx5(Eye, {}) })
1627
1837
  }
1628
1838
  )
1629
1839
  ] });
@@ -1639,9 +1849,9 @@ function LayoutAndCameraControls({
1639
1849
  onResetView,
1640
1850
  extraToolbarContent
1641
1851
  }) {
1642
- return /* @__PURE__ */ jsxs2("div", { className: "absolute right-2 top-2 flex flex-col border", children: [
1643
- /* @__PURE__ */ jsx3(RendererDropdown, {}),
1644
- /* @__PURE__ */ jsx3(
1852
+ return /* @__PURE__ */ jsxs4("div", { className: "absolute right-2 top-2 flex flex-col border", children: [
1853
+ /* @__PURE__ */ jsx5(RendererDropdown, {}),
1854
+ /* @__PURE__ */ jsx5(
1645
1855
  ViewControls,
1646
1856
  {
1647
1857
  fullscreen,
@@ -1652,9 +1862,9 @@ function LayoutAndCameraControls({
1652
1862
  onResetView
1653
1863
  }
1654
1864
  ),
1655
- /* @__PURE__ */ jsx3(PathSelectionDropdown, { setPathSelectionType }),
1865
+ /* @__PURE__ */ jsx5(PathSelectionDropdown, { setPathSelectionType }),
1656
1866
  extraToolbarContent,
1657
- /* @__PURE__ */ jsx3(LayoutDropdown, { updateLayout })
1867
+ /* @__PURE__ */ jsx5(LayoutDropdown, { updateLayout })
1658
1868
  ] });
1659
1869
  }
1660
1870
  var LayoutAndCameraControls_default = LayoutAndCameraControls;
@@ -1664,10 +1874,10 @@ import { Card as Card2 } from "@petrarca/sonnet-ui";
1664
1874
 
1665
1875
  // src/components/rea/NodeHoverInfo.tsx
1666
1876
  import { Card, Separator } from "@petrarca/sonnet-ui";
1667
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1877
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1668
1878
  function NodeHoverInfo({ node }) {
1669
- return /* @__PURE__ */ jsx4(Card, { className: "absolute top-2 left-1/2 -translate-x-1/2 bg-white shadow-md p-2", children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-row items-center gap-2", children: [
1670
- /* @__PURE__ */ jsx4(
1879
+ return /* @__PURE__ */ jsx6(Card, { className: "absolute top-2 left-1/2 -translate-x-1/2 bg-white shadow-md p-2", children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-row items-center gap-2", children: [
1880
+ /* @__PURE__ */ jsx6(
1671
1881
  Separator,
1672
1882
  {
1673
1883
  orientation: "vertical",
@@ -1676,43 +1886,43 @@ function NodeHoverInfo({ node }) {
1676
1886
  style: { backgroundColor: node.fill }
1677
1887
  }
1678
1888
  ),
1679
- /* @__PURE__ */ jsxs3("div", { children: [
1680
- /* @__PURE__ */ jsxs3("p", { children: [
1681
- /* @__PURE__ */ jsxs3("strong", { children: [
1889
+ /* @__PURE__ */ jsxs5("div", { children: [
1890
+ /* @__PURE__ */ jsxs5("p", { children: [
1891
+ /* @__PURE__ */ jsxs5("strong", { children: [
1682
1892
  node.data.label_,
1683
1893
  ": "
1684
1894
  ] }),
1685
1895
  node.label
1686
1896
  ] }),
1687
- /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground", children: node.data.id_ })
1897
+ /* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: node.data.id_ })
1688
1898
  ] })
1689
1899
  ] }) });
1690
1900
  }
1691
1901
  var NodeHoverInfo_default = NodeHoverInfo;
1692
1902
 
1693
1903
  // src/components/rea/ReaContextMenu.tsx
1694
- import React3, { useState as useState4 } from "react";
1904
+ import React4, { useState as useState5 } from "react";
1695
1905
  import { devLog as devLog7 } from "@petrarca/sonnet-core";
1696
1906
 
1697
1907
  // src/components/rea/ExpandOptionsPanel.tsx
1698
- import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
1908
+ import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
1699
1909
  import { Checkbox, Input, Label } from "@petrarca/sonnet-ui";
1700
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1910
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1701
1911
  function ExpandOptionsPanel({
1702
1912
  nodeLabel,
1703
1913
  onExpand,
1704
1914
  onBack,
1705
1915
  getEdgeTypes
1706
1916
  }) {
1707
- const [direction, setDirection] = useState3("both");
1708
- const [limit, setLimit] = useState3(100);
1709
- const [edgeTypes, setEdgeTypes] = useState3([]);
1710
- const [selectedTypes, setSelectedTypes] = useState3([]);
1711
- const [loading, setLoading] = useState3(!!getEdgeTypes);
1712
- const [offset, setOffset] = useState3({ x: 0, y: 0 });
1713
- const expandBtnRef = useRef2(null);
1714
- const dragRef = useRef2(null);
1715
- const onPointerDown = useCallback3(
1917
+ const [direction, setDirection] = useState4("both");
1918
+ const [limit, setLimit] = useState4(100);
1919
+ const [edgeTypes, setEdgeTypes] = useState4([]);
1920
+ const [selectedTypes, setSelectedTypes] = useState4([]);
1921
+ const [loading, setLoading] = useState4(!!getEdgeTypes);
1922
+ const [offset, setOffset] = useState4({ x: 0, y: 0 });
1923
+ const expandBtnRef = useRef3(null);
1924
+ const dragRef = useRef3(null);
1925
+ const onPointerDown = useCallback4(
1716
1926
  (e) => {
1717
1927
  if (e.target.closest("button")) return;
1718
1928
  e.preventDefault();
@@ -1726,16 +1936,16 @@ function ExpandOptionsPanel({
1726
1936
  },
1727
1937
  [offset]
1728
1938
  );
1729
- const onPointerMove = useCallback3((e) => {
1939
+ const onPointerMove = useCallback4((e) => {
1730
1940
  if (!dragRef.current) return;
1731
1941
  const dx = e.clientX - dragRef.current.startX;
1732
1942
  const dy = e.clientY - dragRef.current.startY;
1733
1943
  setOffset({ x: dragRef.current.origX + dx, y: dragRef.current.origY + dy });
1734
1944
  }, []);
1735
- const onPointerUp = useCallback3(() => {
1945
+ const onPointerUp = useCallback4(() => {
1736
1946
  dragRef.current = null;
1737
1947
  }, []);
1738
- useEffect2(() => {
1948
+ useEffect3(() => {
1739
1949
  if (!getEdgeTypes) return;
1740
1950
  setLoading(true);
1741
1951
  getEdgeTypes().then((types) => {
@@ -1760,18 +1970,18 @@ function ExpandOptionsPanel({
1760
1970
  };
1761
1971
  const allSelected = selectedTypes.length === edgeTypes.length;
1762
1972
  const noneSelected = edgeTypes.length > 0 && selectedTypes.length === 0;
1763
- useEffect2(() => {
1973
+ useEffect3(() => {
1764
1974
  if (!loading && !noneSelected) {
1765
1975
  expandBtnRef.current?.focus();
1766
1976
  }
1767
1977
  }, [loading, noneSelected]);
1768
- return /* @__PURE__ */ jsxs4(
1978
+ return /* @__PURE__ */ jsxs6(
1769
1979
  "div",
1770
1980
  {
1771
1981
  className: "bg-background border border-border rounded-md shadow-md p-2.5 min-w-48 space-y-2",
1772
1982
  style: { transform: `translate(${offset.x}px, ${offset.y}px)` },
1773
1983
  children: [
1774
- /* @__PURE__ */ jsxs4(
1984
+ /* @__PURE__ */ jsxs6(
1775
1985
  "div",
1776
1986
  {
1777
1987
  className: "flex items-center justify-between gap-2 cursor-grab active:cursor-grabbing select-none",
@@ -1779,7 +1989,7 @@ function ExpandOptionsPanel({
1779
1989
  onPointerMove,
1780
1990
  onPointerUp,
1781
1991
  children: [
1782
- /* @__PURE__ */ jsx5(
1992
+ /* @__PURE__ */ jsx7(
1783
1993
  "button",
1784
1994
  {
1785
1995
  className: "text-[11px] text-muted-foreground hover:text-foreground cursor-pointer",
@@ -1787,13 +1997,13 @@ function ExpandOptionsPanel({
1787
1997
  children: "Back"
1788
1998
  }
1789
1999
  ),
1790
- /* @__PURE__ */ jsx5("span", { className: "text-[11px] font-medium text-foreground truncate max-w-32", children: nodeLabel })
2000
+ /* @__PURE__ */ jsx7("span", { className: "text-[11px] font-medium text-foreground truncate max-w-32", children: nodeLabel })
1791
2001
  ]
1792
2002
  }
1793
2003
  ),
1794
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
1795
- /* @__PURE__ */ jsx5(Label, { className: "text-[11px] text-muted-foreground", children: "Direction" }),
1796
- /* @__PURE__ */ jsx5("div", { className: "flex gap-0.5", children: ["both", "out", "in"].map((dir) => /* @__PURE__ */ jsx5(
2004
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
2005
+ /* @__PURE__ */ jsx7(Label, { className: "text-[11px] text-muted-foreground", children: "Direction" }),
2006
+ /* @__PURE__ */ jsx7("div", { className: "flex gap-0.5", children: ["both", "out", "in"].map((dir) => /* @__PURE__ */ jsx7(
1797
2007
  "button",
1798
2008
  {
1799
2009
  className: `flex-1 px-2 py-1 text-[11px] capitalize rounded border cursor-pointer ${direction === dir ? "bg-primary/10 text-primary border-primary/40 font-medium" : "bg-background text-muted-foreground border-border hover:bg-muted"}`,
@@ -1803,10 +2013,10 @@ function ExpandOptionsPanel({
1803
2013
  dir
1804
2014
  )) })
1805
2015
  ] }),
1806
- !loading && edgeTypes.length > 1 && /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
1807
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between", children: [
1808
- /* @__PURE__ */ jsx5(Label, { className: "text-[11px] text-muted-foreground", children: "Rel Types" }),
1809
- /* @__PURE__ */ jsx5(
2016
+ !loading && edgeTypes.length > 1 && /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
2017
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
2018
+ /* @__PURE__ */ jsx7(Label, { className: "text-[11px] text-muted-foreground", children: "Rel Types" }),
2019
+ /* @__PURE__ */ jsx7(
1810
2020
  "button",
1811
2021
  {
1812
2022
  className: "text-[10px] text-muted-foreground hover:text-foreground cursor-pointer",
@@ -1815,12 +2025,12 @@ function ExpandOptionsPanel({
1815
2025
  }
1816
2026
  )
1817
2027
  ] }),
1818
- /* @__PURE__ */ jsx5("div", { className: "space-y-0.5 max-h-32 overflow-y-auto", children: edgeTypes.map((type) => /* @__PURE__ */ jsxs4(
2028
+ /* @__PURE__ */ jsx7("div", { className: "space-y-0.5 max-h-32 overflow-y-auto", children: edgeTypes.map((type) => /* @__PURE__ */ jsxs6(
1819
2029
  "label",
1820
2030
  {
1821
2031
  className: "flex items-center gap-1.5 text-[11px] cursor-pointer",
1822
2032
  children: [
1823
- /* @__PURE__ */ jsx5(
2033
+ /* @__PURE__ */ jsx7(
1824
2034
  Checkbox,
1825
2035
  {
1826
2036
  checked: selectedTypes.includes(type),
@@ -1828,15 +2038,15 @@ function ExpandOptionsPanel({
1828
2038
  className: "h-3 w-3"
1829
2039
  }
1830
2040
  ),
1831
- /* @__PURE__ */ jsx5("span", { className: "truncate", children: type })
2041
+ /* @__PURE__ */ jsx7("span", { className: "truncate", children: type })
1832
2042
  ]
1833
2043
  },
1834
2044
  type
1835
2045
  )) })
1836
2046
  ] }),
1837
- /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
1838
- /* @__PURE__ */ jsx5(Label, { className: "text-[11px] text-muted-foreground", children: "Limit" }),
1839
- /* @__PURE__ */ jsx5(
2047
+ /* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
2048
+ /* @__PURE__ */ jsx7(Label, { className: "text-[11px] text-muted-foreground", children: "Limit" }),
2049
+ /* @__PURE__ */ jsx7(
1840
2050
  Input,
1841
2051
  {
1842
2052
  type: "number",
@@ -1851,7 +2061,7 @@ function ExpandOptionsPanel({
1851
2061
  }
1852
2062
  )
1853
2063
  ] }),
1854
- /* @__PURE__ */ jsx5(
2064
+ /* @__PURE__ */ jsx7(
1855
2065
  "button",
1856
2066
  {
1857
2067
  ref: expandBtnRef,
@@ -1869,7 +2079,7 @@ function ExpandOptionsPanel({
1869
2079
  var ExpandOptionsPanel_default = ExpandOptionsPanel;
1870
2080
 
1871
2081
  // src/components/rea/ReaContextMenu.tsx
1872
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2082
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1873
2083
  function ReaContextMenu({
1874
2084
  data,
1875
2085
  onClose,
@@ -1878,8 +2088,8 @@ function ReaContextMenu({
1878
2088
  onCopyNode,
1879
2089
  getNodeEdgeTypes
1880
2090
  }) {
1881
- const [showExpandPanel, setShowExpandPanel] = useState4(false);
1882
- React3.useEffect(() => {
2091
+ const [showExpandPanel, setShowExpandPanel] = useState5(false);
2092
+ React4.useEffect(() => {
1883
2093
  const handleKey = (e) => {
1884
2094
  if (e.key === "Escape") onClose();
1885
2095
  };
@@ -1989,7 +2199,7 @@ function ReaContextMenu({
1989
2199
  const menuItems = elementType === "node" ? nodeMenuItems : edgeMenuItems;
1990
2200
  if (showExpandPanel && elementType === "node") {
1991
2201
  const nodeLabel = data.label || data.id;
1992
- return /* @__PURE__ */ jsx6(
2202
+ return /* @__PURE__ */ jsx8(
1993
2203
  ExpandOptionsPanel_default,
1994
2204
  {
1995
2205
  nodeLabel: String(nodeLabel),
@@ -1999,9 +2209,9 @@ function ReaContextMenu({
1999
2209
  }
2000
2210
  );
2001
2211
  }
2002
- return /* @__PURE__ */ jsxs5("div", { className: "bg-background border border-border rounded-md shadow-lg py-1 min-w-48", children: [
2003
- menuItems.map((item, index) => /* @__PURE__ */ jsxs5(React3.Fragment, { children: [
2004
- /* @__PURE__ */ jsx6(
2212
+ return /* @__PURE__ */ jsxs7("div", { className: "bg-background border border-border rounded-md shadow-lg py-1 min-w-48", children: [
2213
+ menuItems.map((item, index) => /* @__PURE__ */ jsxs7(React4.Fragment, { children: [
2214
+ /* @__PURE__ */ jsx8(
2005
2215
  "button",
2006
2216
  {
2007
2217
  className: "w-full text-left px-3 py-2 text-sm hover:bg-muted/50 cursor-pointer",
@@ -2009,10 +2219,10 @@ function ReaContextMenu({
2009
2219
  children: item.label
2010
2220
  }
2011
2221
  ),
2012
- index === 1 && /* @__PURE__ */ jsx6("div", { className: "border-t border-border my-1" })
2222
+ index === 1 && /* @__PURE__ */ jsx8("div", { className: "border-t border-border my-1" })
2013
2223
  ] }, item.label)),
2014
- /* @__PURE__ */ jsx6("div", { className: "border-t border-border my-1" }),
2015
- /* @__PURE__ */ jsx6(
2224
+ /* @__PURE__ */ jsx8("div", { className: "border-t border-border my-1" }),
2225
+ /* @__PURE__ */ jsx8(
2016
2226
  "button",
2017
2227
  {
2018
2228
  className: "w-full text-left px-3 py-1.5 text-xs text-muted-foreground hover:bg-muted/50 cursor-pointer",
@@ -2026,7 +2236,7 @@ var ReaContextMenu_default = ReaContextMenu;
2026
2236
 
2027
2237
  // src/components/rea/ReaGraph.tsx
2028
2238
  import { toast as toast2 } from "sonner";
2029
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2239
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2030
2240
  function findElementInGraphData(elementId, elementType, nodes, edges) {
2031
2241
  if (elementType === "node") {
2032
2242
  const node = nodes.find((n) => n.id === elementId);
@@ -2093,21 +2303,21 @@ function ReaGraph({
2093
2303
  extraToolbarContent,
2094
2304
  getNodeEdgeTypes
2095
2305
  }) {
2096
- const isMountedRef = useRef3(false);
2306
+ const isMountedRef = useRef4(false);
2097
2307
  const { nodes, edges } = useReagraphData();
2098
2308
  const { normalized } = useLiveGraphData();
2099
- useEffect3(() => {
2309
+ useEffect4(() => {
2100
2310
  devLog8("[ReGraph] counts", { nodes: nodes.length, edges: edges.length });
2101
2311
  if (nodes.length > 0) devLog8("[ReGraph] sample node", nodes[0]);
2102
2312
  if (edges.length > 0) devLog8("[ReGraph] sample edge", edges[0]);
2103
2313
  }, [nodes, edges]);
2104
- const canvas = useRef3(null);
2105
- const [hoveredNode, setHoveredNode] = useState5(
2314
+ const canvas = useRef4(null);
2315
+ const [hoveredNode, setHoveredNode] = useState6(
2106
2316
  void 0
2107
2317
  );
2108
- const [layout, setLayout] = useState5("forceDirected2d");
2109
- const [fullscreen, setFullscreen] = useState5(false);
2110
- const [pathSelectionType, setPathSelectionType] = useState5("direct");
2318
+ const [layout, setLayout] = useState6("forceDirected2d");
2319
+ const [fullscreen, setFullscreen] = useState6(false);
2320
+ const [pathSelectionType, setPathSelectionType] = useState6("direct");
2111
2321
  const useGraphSelection = useWorkspaceSelection();
2112
2322
  const useGraphFilter2 = useWorkspaceGraphFilter();
2113
2323
  const useGraphExploration = useWorkspaceGraphExploration();
@@ -2134,7 +2344,7 @@ function ReaGraph({
2134
2344
  pathSelectionType,
2135
2345
  type: "multi"
2136
2346
  });
2137
- useEffect3(() => {
2347
+ useEffect4(() => {
2138
2348
  const unsubscribe = useGraphFocus2.subscribe((state) => {
2139
2349
  const focusAction = state.focusAction;
2140
2350
  if (!focusAction || !canvas.current) return;
@@ -2158,12 +2368,12 @@ function ReaGraph({
2158
2368
  });
2159
2369
  return unsubscribe;
2160
2370
  }, [nodes, edges, setSelectedNodeIds, setSelectedEdge, useGraphFocus2]);
2161
- useEffect3(() => {
2371
+ useEffect4(() => {
2162
2372
  if (!canvas.current) return;
2163
2373
  clearSelections();
2164
2374
  clearSelectionStore();
2165
2375
  }, [lastDataChangeTime, clearSelections, clearSelectionStore]);
2166
- const fitToScreen = useCallback4(
2376
+ const fitToScreen = useCallback5(
2167
2377
  (reason) => {
2168
2378
  if (!isMountedRef.current) return;
2169
2379
  if (nodes.length === 0) return;
@@ -2189,7 +2399,7 @@ function ReaGraph({
2189
2399
  },
2190
2400
  [nodes.length]
2191
2401
  );
2192
- useEffect3(() => {
2402
+ useEffect4(() => {
2193
2403
  isMountedRef.current = true;
2194
2404
  fitToScreen("mount");
2195
2405
  const state = useGraphSelection.getState();
@@ -2209,9 +2419,9 @@ function ReaGraph({
2209
2419
  isMountedRef.current = false;
2210
2420
  };
2211
2421
  }, [fitToScreen, setSelections, setSelectedEdge, useGraphSelection]);
2212
- const selectionsRef = useRef3(selections);
2422
+ const selectionsRef = useRef4(selections);
2213
2423
  selectionsRef.current = selections;
2214
- useEffect3(() => {
2424
+ useEffect4(() => {
2215
2425
  const inSync = (nodeIds) => {
2216
2426
  const cur = selectionsRef.current;
2217
2427
  return cur.length === nodeIds.length && nodeIds.every((id) => cur.includes(id));
@@ -2223,7 +2433,7 @@ function ReaGraph({
2223
2433
  });
2224
2434
  return unsubscribe;
2225
2435
  }, [useGraphSelection, setSelections]);
2226
- useEffect3(() => {
2436
+ useEffect4(() => {
2227
2437
  if (isMountedRef.current) fitToScreen("counts-changed");
2228
2438
  }, [nodes.length, edges.length, fitToScreen]);
2229
2439
  function updateLayout(next) {
@@ -2271,12 +2481,12 @@ function ReaGraph({
2271
2481
  setSelectedEdge(null);
2272
2482
  if (onCanvasClick) onCanvasClick(e);
2273
2483
  }
2274
- useEffect3(() => {
2484
+ useEffect4(() => {
2275
2485
  if (!isMountedRef.current) return;
2276
2486
  const selectedNodes = selections.map((id) => nodes.find((n) => n.id === id)).filter(Boolean);
2277
2487
  setSelectedNodeIds(selectedNodes.map((n) => n.id));
2278
2488
  }, [selections, nodes]);
2279
- useEffect3(() => {
2489
+ useEffect4(() => {
2280
2490
  if (!isMountedRef.current) return;
2281
2491
  const currentSelectedIds = useGraphSelection.getState().selectedNodeIds;
2282
2492
  if (!currentSelectedIds.length) return;
@@ -2295,7 +2505,7 @@ function ReaGraph({
2295
2505
  setSelectedEdge,
2296
2506
  useGraphSelection
2297
2507
  ]);
2298
- useEffect3(() => {
2508
+ useEffect4(() => {
2299
2509
  if (!isMountedRef.current) return;
2300
2510
  const currentSelectedIds = useGraphSelection.getState().selectedNodeIds;
2301
2511
  if (!currentSelectedIds.length) return;
@@ -2307,7 +2517,7 @@ function ReaGraph({
2307
2517
  if (stillPresentIds.length < 2) setSelectedEdge(null);
2308
2518
  }
2309
2519
  }, [nodes, setSelectedNodeIds, setSelectedEdge, useGraphSelection]);
2310
- useEffect3(() => {
2520
+ useEffect4(() => {
2311
2521
  if (!fullscreen) return;
2312
2522
  const prevOverflow = document.body.style.overflow;
2313
2523
  document.body.style.overflow = "hidden";
@@ -2321,7 +2531,7 @@ function ReaGraph({
2321
2531
  window.removeEventListener("keydown", handleKey);
2322
2532
  };
2323
2533
  }, [fullscreen, fitToScreen]);
2324
- useEffect3(() => {
2534
+ useEffect4(() => {
2325
2535
  if (!fullscreen) return;
2326
2536
  const id = setTimeout(() => {
2327
2537
  if (canvas.current) {
@@ -2330,7 +2540,7 @@ function ReaGraph({
2330
2540
  }, 50);
2331
2541
  return () => clearTimeout(id);
2332
2542
  }, [fullscreen, fitToScreen]);
2333
- useEffect3(() => {
2543
+ useEffect4(() => {
2334
2544
  if (!isMountedRef.current) return;
2335
2545
  const handleResize = () => {
2336
2546
  if (isMountedRef.current && canvas.current) {
@@ -2342,8 +2552,8 @@ function ReaGraph({
2342
2552
  window.removeEventListener("resize", handleResize);
2343
2553
  };
2344
2554
  }, [fitToScreen]);
2345
- const firstDataRef = useRef3(true);
2346
- useEffect3(() => {
2555
+ const firstDataRef = useRef4(true);
2556
+ useEffect4(() => {
2347
2557
  if (!isMountedRef.current) return;
2348
2558
  if (nodes.length > 0) {
2349
2559
  if (firstDataRef.current) {
@@ -2356,18 +2566,18 @@ function ReaGraph({
2356
2566
  }, [nodes.length, fitToScreen]);
2357
2567
  if (nodes.length === 0) {
2358
2568
  devLog8("[ReGraph] empty state render");
2359
- return /* @__PURE__ */ jsxs6(Card2, { className: "relative w-full h-full items-center justify-center text-slate-500 gap-2 shadow-xs p-4", children: [
2360
- /* @__PURE__ */ jsx7("div", { children: "No data to display." }),
2361
- /* @__PURE__ */ jsxs6("div", { children: [
2569
+ return /* @__PURE__ */ jsxs8(Card2, { className: "relative w-full h-full items-center justify-center text-slate-500 gap-2 shadow-xs p-4", children: [
2570
+ /* @__PURE__ */ jsx9("div", { children: "No data to display." }),
2571
+ /* @__PURE__ */ jsxs8("div", { children: [
2362
2572
  "Please",
2363
2573
  " ",
2364
- /* @__PURE__ */ jsx7("span", { className: "font-semibold", children: "run a query, check filters, or add a node" }),
2574
+ /* @__PURE__ */ jsx9("span", { className: "font-semibold", children: "run a query, check filters, or add a node" }),
2365
2575
  " ",
2366
2576
  "to the workbench to populate the graph."
2367
2577
  ] })
2368
2578
  ] });
2369
2579
  }
2370
- return /* @__PURE__ */ jsxs6(
2580
+ return /* @__PURE__ */ jsxs8(
2371
2581
  "div",
2372
2582
  {
2373
2583
  className: cn(
@@ -2375,7 +2585,7 @@ function ReaGraph({
2375
2585
  fullscreen ? "fixed inset-0 z-50 w-screen h-screen p-0 m-0" : "relative w-full h-full"
2376
2586
  ),
2377
2587
  children: [
2378
- /* @__PURE__ */ jsx7(Card2, { className: "h-full shadow-xs p-4", children: /* @__PURE__ */ jsx7(
2588
+ /* @__PURE__ */ jsx9(Card2, { className: "h-full shadow-xs p-4", children: /* @__PURE__ */ jsx9(
2379
2589
  GraphCanvas,
2380
2590
  {
2381
2591
  ref: canvas,
@@ -2400,7 +2610,7 @@ function ReaGraph({
2400
2610
  elementId: data.id,
2401
2611
  elementType
2402
2612
  });
2403
- return /* @__PURE__ */ jsx7(
2613
+ return /* @__PURE__ */ jsx9(
2404
2614
  ReaContextMenu_default,
2405
2615
  {
2406
2616
  data,
@@ -2414,8 +2624,8 @@ function ReaGraph({
2414
2624
  }
2415
2625
  }
2416
2626
  ) }),
2417
- hoveredNode && /* @__PURE__ */ jsx7(NodeHoverInfo_default, { node: hoveredNode }),
2418
- /* @__PURE__ */ jsx7(
2627
+ hoveredNode && /* @__PURE__ */ jsx9(NodeHoverInfo_default, { node: hoveredNode }),
2628
+ /* @__PURE__ */ jsx9(
2419
2629
  LayoutAndCameraControls_default,
2420
2630
  {
2421
2631
  fitToScreen,
@@ -2442,10 +2652,10 @@ var ReaGraph_default = ReaGraph;
2442
2652
 
2443
2653
  // src/components/cyto/CytoscapeGraph.tsx
2444
2654
  import {
2445
- useEffect as useEffect4,
2446
- useRef as useRef4,
2447
- useState as useState6,
2448
- useCallback as useCallback5
2655
+ useEffect as useEffect5,
2656
+ useRef as useRef5,
2657
+ useState as useState7,
2658
+ useCallback as useCallback6
2449
2659
  } from "react";
2450
2660
  import { devLog as devLog10, errorLog as errorLog3 } from "@petrarca/sonnet-core";
2451
2661
  import cytoscape from "cytoscape";
@@ -2459,17 +2669,17 @@ import cxtmenu from "cytoscape-cxtmenu";
2459
2669
 
2460
2670
  // src/components/cyto/CytoscapeControls.tsx
2461
2671
  import {
2462
- Button as Button3,
2463
- DropdownMenu as DropdownMenu3,
2464
- DropdownMenuContent as DropdownMenuContent3,
2465
- DropdownMenuItem as DropdownMenuItem3,
2672
+ Button as Button5,
2673
+ DropdownMenu as DropdownMenu4,
2674
+ DropdownMenuContent as DropdownMenuContent4,
2675
+ DropdownMenuItem as DropdownMenuItem4,
2466
2676
  DropdownMenuLabel as DropdownMenuLabel2,
2467
- DropdownMenuSeparator as DropdownMenuSeparator2,
2468
- DropdownMenuTrigger as DropdownMenuTrigger3,
2469
- SimpleTooltip as SimpleTooltip3
2677
+ DropdownMenuSeparator as DropdownMenuSeparator3,
2678
+ DropdownMenuTrigger as DropdownMenuTrigger4,
2679
+ SimpleTooltip as SimpleTooltip4
2470
2680
  } from "@petrarca/sonnet-ui";
2471
2681
  import { LayoutPanelTop as LayoutPanelTop2, Monitor, Frame, Eye as Eye2, Minus } from "lucide-react";
2472
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2682
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2473
2683
  function ViewControls2({
2474
2684
  zoomIn,
2475
2685
  zoomOut,
@@ -2480,17 +2690,17 @@ function ViewControls2({
2480
2690
  isFocusMode,
2481
2691
  onResetView
2482
2692
  }) {
2483
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
2484
- /* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Zoom In", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: zoomIn, children: /* @__PURE__ */ jsx8("span", { style: { fontWeight: 600 }, children: "+" }) }) }),
2485
- /* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Zoom Out", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: zoomOut, children: /* @__PURE__ */ jsx8(Minus, {}) }) }),
2486
- /* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Fullscreen", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: toggleFullscreen, children: !fullscreen ? /* @__PURE__ */ jsx8(Monitor, {}) : /* @__PURE__ */ jsx8(Frame, {}) }) }),
2487
- /* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Fit to screen", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: fitToScreen, children: /* @__PURE__ */ jsx8(Frame, {}) }) }),
2488
- (hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */ jsx8(
2489
- SimpleTooltip3,
2693
+ return /* @__PURE__ */ jsxs9(Fragment2, { children: [
2694
+ /* @__PURE__ */ jsx10(SimpleTooltip4, { label: "Zoom In", side: "left", children: /* @__PURE__ */ jsx10(Button5, { variant: "ghost", size: "icon", onClick: zoomIn, children: /* @__PURE__ */ jsx10("span", { style: { fontWeight: 600 }, children: "+" }) }) }),
2695
+ /* @__PURE__ */ jsx10(SimpleTooltip4, { label: "Zoom Out", side: "left", children: /* @__PURE__ */ jsx10(Button5, { variant: "ghost", size: "icon", onClick: zoomOut, children: /* @__PURE__ */ jsx10(Minus, {}) }) }),
2696
+ /* @__PURE__ */ jsx10(SimpleTooltip4, { label: "Fullscreen", side: "left", children: /* @__PURE__ */ jsx10(Button5, { variant: "ghost", size: "icon", onClick: toggleFullscreen, children: !fullscreen ? /* @__PURE__ */ jsx10(Monitor, {}) : /* @__PURE__ */ jsx10(Frame, {}) }) }),
2697
+ /* @__PURE__ */ jsx10(SimpleTooltip4, { label: "Fit to screen", side: "left", children: /* @__PURE__ */ jsx10(Button5, { variant: "ghost", size: "icon", onClick: fitToScreen, children: /* @__PURE__ */ jsx10(Frame, {}) }) }),
2698
+ (hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */ jsx10(
2699
+ SimpleTooltip4,
2490
2700
  {
2491
2701
  label: isFocusMode ? "Reset Focus" : "Show All Hidden",
2492
2702
  side: "left",
2493
- children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: onResetView, children: /* @__PURE__ */ jsx8(Eye2, {}) })
2703
+ children: /* @__PURE__ */ jsx10(Button5, { variant: "ghost", size: "icon", onClick: onResetView, children: /* @__PURE__ */ jsx10(Eye2, {}) })
2494
2704
  }
2495
2705
  )
2496
2706
  ] });
@@ -2525,13 +2735,13 @@ var LAYOUT_GROUPS = [
2525
2735
  }
2526
2736
  ];
2527
2737
  function LayoutDropdown2({ currentLayout, updateLayout }) {
2528
- return /* @__PURE__ */ jsxs7(DropdownMenu3, { children: [
2529
- /* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Layout", side: "left", children: /* @__PURE__ */ jsx8(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx8(LayoutPanelTop2, {}) }) }) }),
2530
- /* @__PURE__ */ jsx8(DropdownMenuContent3, { side: "left", align: "start", children: LAYOUT_GROUPS.map((group, groupIdx) => /* @__PURE__ */ jsxs7("div", { children: [
2531
- groupIdx > 0 && /* @__PURE__ */ jsx8(DropdownMenuSeparator2, {}),
2532
- /* @__PURE__ */ jsx8(DropdownMenuLabel2, { children: group.label }),
2533
- group.items.map((item) => /* @__PURE__ */ jsx8(
2534
- DropdownMenuItem3,
2738
+ return /* @__PURE__ */ jsxs9(DropdownMenu4, { children: [
2739
+ /* @__PURE__ */ jsx10(SimpleTooltip4, { label: "Layout", side: "left", children: /* @__PURE__ */ jsx10(DropdownMenuTrigger4, { asChild: true, children: /* @__PURE__ */ jsx10(Button5, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx10(LayoutPanelTop2, {}) }) }) }),
2740
+ /* @__PURE__ */ jsx10(DropdownMenuContent4, { side: "left", align: "start", children: LAYOUT_GROUPS.map((group, groupIdx) => /* @__PURE__ */ jsxs9("div", { children: [
2741
+ groupIdx > 0 && /* @__PURE__ */ jsx10(DropdownMenuSeparator3, {}),
2742
+ /* @__PURE__ */ jsx10(DropdownMenuLabel2, { children: group.label }),
2743
+ group.items.map((item) => /* @__PURE__ */ jsx10(
2744
+ DropdownMenuItem4,
2535
2745
  {
2536
2746
  onClick: () => updateLayout(item.layout),
2537
2747
  className: currentLayout === item.layout ? "text-blue-600" : "",
@@ -2555,9 +2765,9 @@ function CytoscapeControls({
2555
2765
  onResetView,
2556
2766
  extraToolbarContent
2557
2767
  }) {
2558
- return /* @__PURE__ */ jsxs7("div", { className: "absolute right-2 top-2 flex flex-col border bg-white rounded-md shadow-sm z-10", children: [
2559
- /* @__PURE__ */ jsx8(RendererDropdown, {}),
2560
- /* @__PURE__ */ jsx8(
2768
+ return /* @__PURE__ */ jsxs9("div", { className: "absolute right-2 top-2 flex flex-col border bg-white rounded-md shadow-sm z-10", children: [
2769
+ /* @__PURE__ */ jsx10(RendererDropdown, {}),
2770
+ /* @__PURE__ */ jsx10(
2561
2771
  ViewControls2,
2562
2772
  {
2563
2773
  zoomIn,
@@ -2571,7 +2781,7 @@ function CytoscapeControls({
2571
2781
  }
2572
2782
  ),
2573
2783
  extraToolbarContent,
2574
- /* @__PURE__ */ jsx8(
2784
+ /* @__PURE__ */ jsx10(
2575
2785
  LayoutDropdown2,
2576
2786
  {
2577
2787
  currentLayout,
@@ -3011,7 +3221,7 @@ var createEdgeContextMenu = ({
3011
3221
 
3012
3222
  // src/components/cyto/CytoscapeGraph.tsx
3013
3223
  import { toast as toast4 } from "sonner";
3014
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
3224
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3015
3225
  function CytoscapeExpandOverlay({
3016
3226
  expandRequest,
3017
3227
  containerRef,
@@ -3019,7 +3229,7 @@ function CytoscapeExpandOverlay({
3019
3229
  getNodeEdgeTypes,
3020
3230
  onClose
3021
3231
  }) {
3022
- return /* @__PURE__ */ jsx9(
3232
+ return /* @__PURE__ */ jsx11(
3023
3233
  "div",
3024
3234
  {
3025
3235
  className: "absolute z-50",
@@ -3034,7 +3244,7 @@ function CytoscapeExpandOverlay({
3034
3244
  ),
3035
3245
  transform: "translate(-50%, 12px)"
3036
3246
  },
3037
- children: /* @__PURE__ */ jsx9(
3247
+ children: /* @__PURE__ */ jsx11(
3038
3248
  ExpandOptionsPanel_default,
3039
3249
  {
3040
3250
  nodeLabel: expandRequest.nodeLabel,
@@ -3346,8 +3556,8 @@ function computeGraphViewState({
3346
3556
  };
3347
3557
  }
3348
3558
  function useFullscreen(fitToScreen, cyRef) {
3349
- const [fullscreen, setFullscreen] = useState6(false);
3350
- useEffect4(() => {
3559
+ const [fullscreen, setFullscreen] = useState7(false);
3560
+ useEffect5(() => {
3351
3561
  if (fullscreen) {
3352
3562
  const prevOverflow = document.body.style.overflow;
3353
3563
  document.body.style.overflow = "hidden";
@@ -3362,7 +3572,7 @@ function useFullscreen(fitToScreen, cyRef) {
3362
3572
  };
3363
3573
  }
3364
3574
  }, [fullscreen, fitToScreen]);
3365
- useEffect4(() => {
3575
+ useEffect5(() => {
3366
3576
  if (!fullscreen) return;
3367
3577
  const id = setTimeout(() => {
3368
3578
  if (cyRef.current) {
@@ -3375,7 +3585,7 @@ function useFullscreen(fitToScreen, cyRef) {
3375
3585
  return {
3376
3586
  fullscreen,
3377
3587
  setFullscreen,
3378
- toggleFullscreen: useCallback5(() => setFullscreen((f) => !f), [])
3588
+ toggleFullscreen: useCallback6(() => setFullscreen((f) => !f), [])
3379
3589
  };
3380
3590
  }
3381
3591
  function setupCytoscapeEventHandlers({
@@ -3489,16 +3699,16 @@ function CytoscapeGraph({
3489
3699
  extraToolbarContent,
3490
3700
  getNodeEdgeTypes
3491
3701
  }) {
3492
- const containerRef = useRef4(null);
3493
- const cyRef = useRef4(null);
3494
- const isInitializedRef = useRef4(false);
3495
- const [currentLayout, setCurrentLayout] = useState6(initialLayout);
3496
- const [expandRequest, setExpandRequest] = useState6(
3702
+ const containerRef = useRef5(null);
3703
+ const cyRef = useRef5(null);
3704
+ const isInitializedRef = useRef5(false);
3705
+ const [currentLayout, setCurrentLayout] = useState7(initialLayout);
3706
+ const [expandRequest, setExpandRequest] = useState7(
3497
3707
  null
3498
3708
  );
3499
- const nodesRef = useRef4([]);
3500
- const edgesRef = useRef4([]);
3501
- const pendingFocusActionRef = useRef4(null);
3709
+ const nodesRef = useRef5([]);
3710
+ const edgesRef = useRef5([]);
3711
+ const pendingFocusActionRef = useRef5(null);
3502
3712
  const cytoData = useCytoscapeData();
3503
3713
  const live = useLiveGraphData();
3504
3714
  const nodes = live.nodes;
@@ -3519,13 +3729,13 @@ function CytoscapeGraph({
3519
3729
  setSelectedEdge: setSelectedEdgeStore,
3520
3730
  clear: clearSelectionStore
3521
3731
  } = useSelection3.getState();
3522
- const selectedNodeIdsRef = useRef4(
3732
+ const selectedNodeIdsRef = useRef5(
3523
3733
  useSelection3.getState().selectedNodeIds
3524
3734
  );
3525
- const selectedEdgeRef = useRef4(
3735
+ const selectedEdgeRef = useRef5(
3526
3736
  useSelection3.getState().selectedEdge
3527
3737
  );
3528
- useEffect4(() => {
3738
+ useEffect5(() => {
3529
3739
  const applyStoreSelection = (nodeIds, edge) => {
3530
3740
  selectedNodeIdsRef.current = nodeIds;
3531
3741
  selectedEdgeRef.current = edge;
@@ -3565,13 +3775,13 @@ function CytoscapeGraph({
3565
3775
  });
3566
3776
  return unsubscribe;
3567
3777
  }, [useSelection3]);
3568
- useEffect4(() => {
3778
+ useEffect5(() => {
3569
3779
  nodesRef.current = nodes;
3570
3780
  }, [nodes]);
3571
- useEffect4(() => {
3781
+ useEffect5(() => {
3572
3782
  edgesRef.current = edges;
3573
3783
  }, [edges]);
3574
- useEffect4(() => {
3784
+ useEffect5(() => {
3575
3785
  if (!containerRef.current || cyRef.current) return;
3576
3786
  isInitializedRef.current = true;
3577
3787
  cyRef.current = cytoscape({
@@ -3734,10 +3944,10 @@ function CytoscapeGraph({
3734
3944
  cyRef.current = null;
3735
3945
  };
3736
3946
  }, []);
3737
- const elementIdsRef = useRef4("");
3738
- const lastLayoutRef = useRef4("");
3739
- const lastFitPaddingRef = useRef4(fitPadding);
3740
- useEffect4(() => {
3947
+ const elementIdsRef = useRef5("");
3948
+ const lastLayoutRef = useRef5("");
3949
+ const lastFitPaddingRef = useRef5(fitPadding);
3950
+ useEffect5(() => {
3741
3951
  const cy = cyRef.current;
3742
3952
  if (!cy || !isInitializedRef.current) return;
3743
3953
  const currentIds = nodes.map((n) => n.id_).sort().join(",") + "|" + edges.map((e) => e.id_).sort().join(",");
@@ -3811,12 +4021,12 @@ function CytoscapeGraph({
3811
4021
  });
3812
4022
  }
3813
4023
  }, [nodes, edges, currentLayout, fitPadding]);
3814
- useEffect4(() => {
4024
+ useEffect5(() => {
3815
4025
  if (!cyRef.current) return;
3816
4026
  cyRef.current.elements().unselect();
3817
4027
  clearSelectionStore();
3818
4028
  }, [lastDataChangeTime, clearSelectionStore]);
3819
- useEffect4(() => {
4029
+ useEffect5(() => {
3820
4030
  const unsubscribe = useGraphFocus2.subscribe((state) => {
3821
4031
  handleFocusStateChange({
3822
4032
  focusStore: useGraphFocus2,
@@ -3841,15 +4051,15 @@ function CytoscapeGraph({
3841
4051
  const updateLayout = (newLayout) => {
3842
4052
  setCurrentLayout(newLayout);
3843
4053
  };
3844
- const fitToScreen = useCallback5(() => {
4054
+ const fitToScreen = useCallback6(() => {
3845
4055
  if (cyRef.current) {
3846
4056
  cyRef.current.fit(void 0, fitPadding);
3847
4057
  }
3848
4058
  }, [fitPadding]);
3849
- useEffect4(() => {
4059
+ useEffect5(() => {
3850
4060
  fitToScreen();
3851
4061
  }, [nodes.length, fitToScreen]);
3852
- useEffect4(() => {
4062
+ useEffect5(() => {
3853
4063
  if (!isInitializedRef.current) return;
3854
4064
  const handleResize = () => cyRef.current?.resize();
3855
4065
  window.addEventListener("resize", handleResize);
@@ -3868,9 +4078,9 @@ function CytoscapeGraph({
3868
4078
  isFocusMode,
3869
4079
  fullscreen
3870
4080
  });
3871
- return /* @__PURE__ */ jsx9("div", { className: outerClass, children: /* @__PURE__ */ jsxs8("div", { className: innerClass, children: [
3872
- /* @__PURE__ */ jsx9("div", { ref: containerRef, className: "absolute inset-0" }),
3873
- expandRequest && /* @__PURE__ */ jsx9(
4081
+ return /* @__PURE__ */ jsx11("div", { className: outerClass, children: /* @__PURE__ */ jsxs10("div", { className: innerClass, children: [
4082
+ /* @__PURE__ */ jsx11("div", { ref: containerRef, className: "absolute inset-0" }),
4083
+ expandRequest && /* @__PURE__ */ jsx11(
3874
4084
  CytoscapeExpandOverlay,
3875
4085
  {
3876
4086
  expandRequest,
@@ -3880,9 +4090,9 @@ function CytoscapeGraph({
3880
4090
  onClose: () => setExpandRequest(null)
3881
4091
  }
3882
4092
  ),
3883
- showEmptyMessage && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center text-xs text-gray-500 pointer-events-none select-none", children: "No graph data to visualize (run a query or check the filters)" }),
3884
- showFilteredMessage && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center text-xs text-gray-500 pointer-events-none select-none", children: 'All elements are hidden or filtered (click "Reset Focus" to restore)' }),
3885
- showControls && /* @__PURE__ */ jsx9(
4093
+ showEmptyMessage && /* @__PURE__ */ jsx11("div", { className: "absolute inset-0 flex items-center justify-center text-xs text-gray-500 pointer-events-none select-none", children: "No graph data to visualize (run a query or check the filters)" }),
4094
+ showFilteredMessage && /* @__PURE__ */ jsx11("div", { className: "absolute inset-0 flex items-center justify-center text-xs text-gray-500 pointer-events-none select-none", children: 'All elements are hidden or filtered (click "Reset Focus" to restore)' }),
4095
+ showControls && /* @__PURE__ */ jsx11(
3886
4096
  CytoscapeControls_default,
3887
4097
  {
3888
4098
  updateLayout,
@@ -3930,15 +4140,15 @@ function CytoscapeGraph({
3930
4140
  var CytoscapeGraph_default = CytoscapeGraph;
3931
4141
 
3932
4142
  // src/components/GraphVisualizer.tsx
3933
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4143
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3934
4144
  function GraphVisualizer({
3935
4145
  onCopyNode,
3936
4146
  extraToolbarContent,
3937
4147
  getNodeEdgeTypes
3938
4148
  }) {
3939
4149
  const { renderer } = useGraphRenderer();
3940
- return /* @__PURE__ */ jsx10("div", { className: "relative w-full h-full flex flex-col overflow-hidden", children: /* @__PURE__ */ jsxs9("div", { className: "flex-1 h-full", children: [
3941
- renderer === "reagraph" && /* @__PURE__ */ jsx10(
4150
+ return /* @__PURE__ */ jsx12("div", { className: "relative w-full h-full flex flex-col overflow-hidden", children: /* @__PURE__ */ jsxs11("div", { className: "flex-1 h-full", children: [
4151
+ renderer === "reagraph" && /* @__PURE__ */ jsx12(
3942
4152
  ReaGraph_default,
3943
4153
  {
3944
4154
  onCopyNode,
@@ -3947,7 +4157,7 @@ function GraphVisualizer({
3947
4157
  },
3948
4158
  "reagraph"
3949
4159
  ),
3950
- renderer === "cytoscape" && /* @__PURE__ */ jsx10(
4160
+ renderer === "cytoscape" && /* @__PURE__ */ jsx12(
3951
4161
  CytoscapeGraph_default,
3952
4162
  {
3953
4163
  onCopyNode,
@@ -3964,7 +4174,7 @@ var GraphVisualizer_default = GraphVisualizer;
3964
4174
  import { Card as Card5 } from "@petrarca/sonnet-ui";
3965
4175
 
3966
4176
  // src/components/ActionPanel/NodeInfoCard.tsx
3967
- import { useState as useState7 } from "react";
4177
+ import { useState as useState8 } from "react";
3968
4178
  import {
3969
4179
  Card as Card3,
3970
4180
  Collapsible,
@@ -3972,11 +4182,11 @@ import {
3972
4182
  CollapsibleTrigger,
3973
4183
  PropertyList
3974
4184
  } from "@petrarca/sonnet-ui";
3975
- import { copyTextToClipboard } from "@petrarca/sonnet-core";
3976
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
4185
+ import { copyTextToClipboard as copyTextToClipboard2 } from "@petrarca/sonnet-core";
4186
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3977
4187
  function NodeInfoCard({ node }) {
3978
- const [showProperties, setShowProperties] = useState7(false);
3979
- return /* @__PURE__ */ jsxs10(
4188
+ const [showProperties, setShowProperties] = useState8(false);
4189
+ return /* @__PURE__ */ jsxs12(
3980
4190
  Card3,
3981
4191
  {
3982
4192
  className: "w-full rounded-lg overflow-hidden bg-muted/30 border shadow-none p-3",
@@ -3984,20 +4194,20 @@ function NodeInfoCard({ node }) {
3984
4194
  borderLeft: `4px solid ${node.visual.color}`
3985
4195
  },
3986
4196
  children: [
3987
- /* @__PURE__ */ jsx11("p", { className: "text-sm leading-tight font-medium mb-0", children: node.label_ }),
3988
- /* @__PURE__ */ jsx11("p", { className: "text-sm leading-tight mb-1", children: node.visual.displayName }),
3989
- /* @__PURE__ */ jsx11(
4197
+ /* @__PURE__ */ jsx13("p", { className: "text-sm leading-tight font-medium mb-0", children: node.label_ }),
4198
+ /* @__PURE__ */ jsx13("p", { className: "text-sm leading-tight mb-1", children: node.visual.displayName }),
4199
+ /* @__PURE__ */ jsx13(
3990
4200
  "p",
3991
4201
  {
3992
4202
  className: "text-xs text-muted-foreground cursor-pointer select-all mb-2",
3993
- onClick: () => copyTextToClipboard(String(node.id_)),
4203
+ onClick: () => copyTextToClipboard2(String(node.id_)),
3994
4204
  title: "Click to copy id",
3995
4205
  children: node.id_
3996
4206
  }
3997
4207
  ),
3998
- /* @__PURE__ */ jsxs10(Collapsible, { open: showProperties, onOpenChange: setShowProperties, children: [
3999
- /* @__PURE__ */ jsx11(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsx11("button", { className: "text-xs cursor-pointer", children: showProperties ? "Hide" : "Show more" }) }),
4000
- /* @__PURE__ */ jsx11(CollapsibleContent, { children: /* @__PURE__ */ jsx11("div", { className: "mt-2", children: /* @__PURE__ */ jsx11(PropertyList, { properties: node.properties_ || {}, showHeader: true }) }) })
4208
+ /* @__PURE__ */ jsxs12(Collapsible, { open: showProperties, onOpenChange: setShowProperties, children: [
4209
+ /* @__PURE__ */ jsx13(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsx13("button", { className: "text-xs cursor-pointer", children: showProperties ? "Hide" : "Show more" }) }),
4210
+ /* @__PURE__ */ jsx13(CollapsibleContent, { children: /* @__PURE__ */ jsx13("div", { className: "mt-2", children: /* @__PURE__ */ jsx13(PropertyList, { properties: node.properties_ || {}, showHeader: true }) }) })
4001
4211
  ] })
4002
4212
  ]
4003
4213
  }
@@ -4006,7 +4216,7 @@ function NodeInfoCard({ node }) {
4006
4216
  var NodeInfoCard_default = NodeInfoCard;
4007
4217
 
4008
4218
  // src/components/ActionPanel/EdgeInfoCard.tsx
4009
- import { useState as useState8 } from "react";
4219
+ import { useState as useState9 } from "react";
4010
4220
  import {
4011
4221
  Card as Card4,
4012
4222
  Collapsible as Collapsible2,
@@ -4015,11 +4225,11 @@ import {
4015
4225
  PropertyList as PropertyList2
4016
4226
  } from "@petrarca/sonnet-ui";
4017
4227
  import { ArrowDown } from "lucide-react";
4018
- import { copyTextToClipboard as copyTextToClipboard2 } from "@petrarca/sonnet-core";
4019
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
4228
+ import { copyTextToClipboard as copyTextToClipboard3 } from "@petrarca/sonnet-core";
4229
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
4020
4230
  function EdgeInfoCard({ edge }) {
4021
- const [showProperties, setShowProperties] = useState8(false);
4022
- return /* @__PURE__ */ jsxs11(
4231
+ const [showProperties, setShowProperties] = useState9(false);
4232
+ return /* @__PURE__ */ jsxs13(
4023
4233
  Card4,
4024
4234
  {
4025
4235
  className: "w-full rounded-lg overflow-hidden bg-muted/30 border shadow-none p-3",
@@ -4027,22 +4237,22 @@ function EdgeInfoCard({ edge }) {
4027
4237
  borderLeft: `4px solid ${edge.visual.color}`
4028
4238
  },
4029
4239
  children: [
4030
- /* @__PURE__ */ jsxs11("p", { className: "text-sm leading-tight mb-1", children: [
4031
- /* @__PURE__ */ jsx12(ArrowDown, { className: "inline-block mr-1 opacity-70 align-middle h-3.5 w-3.5" }),
4032
- /* @__PURE__ */ jsx12("strong", { className: "font-medium align-middle", children: edge.label_ })
4240
+ /* @__PURE__ */ jsxs13("p", { className: "text-sm leading-tight mb-1", children: [
4241
+ /* @__PURE__ */ jsx14(ArrowDown, { className: "inline-block mr-1 opacity-70 align-middle h-3.5 w-3.5" }),
4242
+ /* @__PURE__ */ jsx14("strong", { className: "font-medium align-middle", children: edge.label_ })
4033
4243
  ] }),
4034
- edge.id_ && /* @__PURE__ */ jsx12(
4244
+ edge.id_ && /* @__PURE__ */ jsx14(
4035
4245
  "p",
4036
4246
  {
4037
4247
  className: "text-xs text-muted-foreground select-all cursor-pointer mt-1 mb-2",
4038
4248
  title: "Click to copy edge id",
4039
- onClick: () => copyTextToClipboard2(String(edge.id_)),
4249
+ onClick: () => copyTextToClipboard3(String(edge.id_)),
4040
4250
  children: edge.id_
4041
4251
  }
4042
4252
  ),
4043
- edge.properties_ && Object.keys(edge.properties_).length > 0 && /* @__PURE__ */ jsxs11(Collapsible2, { open: showProperties, onOpenChange: setShowProperties, children: [
4044
- /* @__PURE__ */ jsx12(CollapsibleTrigger2, { asChild: true, children: /* @__PURE__ */ jsx12("button", { className: "text-xs cursor-pointer", children: showProperties ? "Hide" : "Show more" }) }),
4045
- /* @__PURE__ */ jsx12(CollapsibleContent2, { children: /* @__PURE__ */ jsx12("div", { className: "mt-2", children: /* @__PURE__ */ jsx12(PropertyList2, { properties: edge.properties_ || {}, showHeader: true }) }) })
4253
+ edge.properties_ && Object.keys(edge.properties_).length > 0 && /* @__PURE__ */ jsxs13(Collapsible2, { open: showProperties, onOpenChange: setShowProperties, children: [
4254
+ /* @__PURE__ */ jsx14(CollapsibleTrigger2, { asChild: true, children: /* @__PURE__ */ jsx14("button", { className: "text-xs cursor-pointer", children: showProperties ? "Hide" : "Show more" }) }),
4255
+ /* @__PURE__ */ jsx14(CollapsibleContent2, { children: /* @__PURE__ */ jsx14("div", { className: "mt-2", children: /* @__PURE__ */ jsx14(PropertyList2, { properties: edge.properties_ || {}, showHeader: true }) }) })
4046
4256
  ] })
4047
4257
  ]
4048
4258
  }
@@ -4054,7 +4264,7 @@ var EdgeInfoCard_default = EdgeInfoCard;
4054
4264
  import { Badge } from "@petrarca/sonnet-ui";
4055
4265
  import { formatNumber, devLog as devLog11 } from "@petrarca/sonnet-core";
4056
4266
  import { useMemo as useMemo4 } from "react";
4057
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
4267
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
4058
4268
  function computeEdgeToNodeLabels(nodesById, edgesById) {
4059
4269
  const mapping = {};
4060
4270
  for (const edge of Object.values(edgesById)) {
@@ -4087,7 +4297,7 @@ function AllToggleBadge({
4087
4297
  badgeKey
4088
4298
  }) {
4089
4299
  const bgColor = allDisabled ? colorPalette["faded"] : colorPalette["neutral"];
4090
- return /* @__PURE__ */ jsxs12(
4300
+ return /* @__PURE__ */ jsxs14(
4091
4301
  Badge,
4092
4302
  {
4093
4303
  className: "cursor-pointer text-xs px-2.5 py-0.5",
@@ -4115,7 +4325,7 @@ function NodeLabelBadges({
4115
4325
  }) {
4116
4326
  return labels.map((label) => {
4117
4327
  const bgColor = shown.has(label) ? colorPalette[label] : colorPalette["faded"];
4118
- return /* @__PURE__ */ jsxs12(
4328
+ return /* @__PURE__ */ jsxs14(
4119
4329
  Badge,
4120
4330
  {
4121
4331
  className: "cursor-pointer text-xs px-2.5 py-0.5",
@@ -4153,7 +4363,7 @@ function EdgeLabelBadges({
4153
4363
  colorPalette,
4154
4364
  "neutral"
4155
4365
  );
4156
- return /* @__PURE__ */ jsxs12(
4366
+ return /* @__PURE__ */ jsxs14(
4157
4367
  Badge,
4158
4368
  {
4159
4369
  className: "cursor-pointer text-xs px-2.5 py-0.5",
@@ -4256,9 +4466,9 @@ function GraphOverview({
4256
4466
  nodeLabelFilters
4257
4467
  );
4258
4468
  const handleEdgeToggle = toggleAllEdgeLabelsDisabled;
4259
- return /* @__PURE__ */ jsxs12(Fragment3, { children: [
4260
- /* @__PURE__ */ jsx13("p", { className: "mt-2 heading-component", children: "Overview" }),
4261
- allLabels.length > 0 && /* @__PURE__ */ jsx13("div", { children: buckets.map((bucket) => {
4469
+ return /* @__PURE__ */ jsxs14(Fragment3, { children: [
4470
+ /* @__PURE__ */ jsx15("p", { className: "mt-2 heading-component", children: "Overview" }),
4471
+ allLabels.length > 0 && /* @__PURE__ */ jsx15("div", { children: buckets.map((bucket) => {
4262
4472
  if (bucket.labels.length === 0) return null;
4263
4473
  const bucketAllDisabled = isBucketAllOff(
4264
4474
  bucket.labels,
@@ -4270,10 +4480,10 @@ function GraphOverview({
4270
4480
  shownNodeLabels,
4271
4481
  setNodeFilterState
4272
4482
  );
4273
- return /* @__PURE__ */ jsxs12("div", { className: "mb-2", children: [
4274
- /* @__PURE__ */ jsx13("p", { className: "heading-meta mb-2", children: bucket.title }),
4275
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-start gap-2", children: [
4276
- /* @__PURE__ */ jsx13(
4483
+ return /* @__PURE__ */ jsxs14("div", { className: "mb-2", children: [
4484
+ /* @__PURE__ */ jsx15("p", { className: "heading-meta mb-2", children: bucket.title }),
4485
+ /* @__PURE__ */ jsxs14("div", { className: "flex flex-col items-start gap-2", children: [
4486
+ /* @__PURE__ */ jsx15(
4277
4487
  AllToggleBadge,
4278
4488
  {
4279
4489
  allDisabled: bucketAllDisabled,
@@ -4283,7 +4493,7 @@ function GraphOverview({
4283
4493
  badgeKey: `all-${bucket.title}`
4284
4494
  }
4285
4495
  ),
4286
- /* @__PURE__ */ jsx13(
4496
+ /* @__PURE__ */ jsx15(
4287
4497
  NodeLabelBadges,
4288
4498
  {
4289
4499
  labels: bucket.labels,
@@ -4301,10 +4511,10 @@ function GraphOverview({
4301
4511
  ] })
4302
4512
  ] }, bucket.title);
4303
4513
  }) }),
4304
- allEdgeLabels.length > 0 && /* @__PURE__ */ jsxs12("div", { children: [
4305
- /* @__PURE__ */ jsx13("p", { className: "heading-meta mb-2", children: edgeSectionTitle }),
4306
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-start gap-2", children: [
4307
- /* @__PURE__ */ jsx13(
4514
+ allEdgeLabels.length > 0 && /* @__PURE__ */ jsxs14("div", { children: [
4515
+ /* @__PURE__ */ jsx15("p", { className: "heading-meta mb-2", children: edgeSectionTitle }),
4516
+ /* @__PURE__ */ jsxs14("div", { className: "flex flex-col items-start gap-2", children: [
4517
+ /* @__PURE__ */ jsx15(
4308
4518
  AllToggleBadge,
4309
4519
  {
4310
4520
  allDisabled: allEdgeLabelsDisabled,
@@ -4314,7 +4524,7 @@ function GraphOverview({
4314
4524
  badgeKey: "edge-all"
4315
4525
  }
4316
4526
  ),
4317
- /* @__PURE__ */ jsx13(
4527
+ /* @__PURE__ */ jsx15(
4318
4528
  EdgeLabelBadges,
4319
4529
  {
4320
4530
  labels: allEdgeLabels,
@@ -4340,16 +4550,16 @@ function Overview({ nodeBuckets, edgeSectionTitle } = {}) {
4340
4550
  );
4341
4551
  }, [baseNormalized]);
4342
4552
  if (stats && stats.values != null && stats.values > 0) {
4343
- return /* @__PURE__ */ jsxs12(Fragment3, { children: [
4344
- /* @__PURE__ */ jsx13("p", { className: "mt-2 heading-component", children: "Overview" }),
4345
- /* @__PURE__ */ jsxs12("p", { className: "text-sm text-muted-foreground", children: [
4553
+ return /* @__PURE__ */ jsxs14(Fragment3, { children: [
4554
+ /* @__PURE__ */ jsx15("p", { className: "mt-2 heading-component", children: "Overview" }),
4555
+ /* @__PURE__ */ jsxs14("p", { className: "text-sm text-muted-foreground", children: [
4346
4556
  formatNumber(stats.values),
4347
4557
  " Values"
4348
4558
  ] })
4349
4559
  ] });
4350
4560
  }
4351
4561
  if (stats && stats.nodes > 0 && colorPalette) {
4352
- return /* @__PURE__ */ jsx13(
4562
+ return /* @__PURE__ */ jsx15(
4353
4563
  GraphOverview,
4354
4564
  {
4355
4565
  stats,
@@ -4368,7 +4578,7 @@ var Overview_default = Overview;
4368
4578
 
4369
4579
  // src/components/ActionPanel/ActionPanel.tsx
4370
4580
  import { useMemo as useMemo5 } from "react";
4371
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
4581
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
4372
4582
  function ActionPanel({ actions }) {
4373
4583
  const useSelection3 = useWorkspaceSelection();
4374
4584
  const selectedNodeIds = useSelection3(
@@ -4384,18 +4594,18 @@ function ActionPanel({ actions }) {
4384
4594
  const secondNode = selectedNodes?.[1];
4385
4595
  const hasEdge = !!selectedEdge;
4386
4596
  const showOverview = !firstNode && !hasEdge;
4387
- return /* @__PURE__ */ jsxs13(
4597
+ return /* @__PURE__ */ jsxs15(
4388
4598
  Card5,
4389
4599
  {
4390
4600
  className: "h-full min-w-64 flex flex-col bg-card shadow-none p-4",
4391
4601
  style: { maxHeight: "100%", overflow: "hidden" },
4392
4602
  children: [
4393
- actions && /* @__PURE__ */ jsx14("div", { className: "flex-shrink-0 mb-2", children: actions }),
4394
- /* @__PURE__ */ jsx14("div", { className: "flex-1 overflow-auto pr-1", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-3", children: [
4395
- firstNode && /* @__PURE__ */ jsx14(NodeInfoCard_default, { node: firstNode }),
4396
- hasEdge && selectedEdge && /* @__PURE__ */ jsx14(EdgeInfoCard_default, { edge: selectedEdge }),
4397
- secondNode && /* @__PURE__ */ jsx14(NodeInfoCard_default, { node: secondNode }),
4398
- showOverview && /* @__PURE__ */ jsx14(Overview_default, {})
4603
+ actions && /* @__PURE__ */ jsx16("div", { className: "flex-shrink-0 mb-2", children: actions }),
4604
+ /* @__PURE__ */ jsx16("div", { className: "flex-1 overflow-auto pr-1", children: /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-3", children: [
4605
+ firstNode && /* @__PURE__ */ jsx16(NodeInfoCard_default, { node: firstNode }),
4606
+ hasEdge && selectedEdge && /* @__PURE__ */ jsx16(EdgeInfoCard_default, { edge: selectedEdge }),
4607
+ secondNode && /* @__PURE__ */ jsx16(NodeInfoCard_default, { node: secondNode }),
4608
+ showOverview && /* @__PURE__ */ jsx16(Overview_default, {})
4399
4609
  ] }) })
4400
4610
  ]
4401
4611
  }
@@ -4404,10 +4614,10 @@ function ActionPanel({ actions }) {
4404
4614
  var ActionPanel_default = ActionPanel;
4405
4615
 
4406
4616
  // src/components/GraphA11yList/GraphA11yList.tsx
4407
- import { useCallback as useCallback6, useMemo as useMemo6, useRef as useRef5 } from "react";
4617
+ import { useCallback as useCallback7, useMemo as useMemo6, useRef as useRef6 } from "react";
4408
4618
  import { useVirtualizer } from "@tanstack/react-virtual";
4409
4619
  import { cn as cn2 } from "@petrarca/sonnet-core";
4410
- import { jsx as jsx15 } from "react/jsx-runtime";
4620
+ import { jsx as jsx17 } from "react/jsx-runtime";
4411
4621
  var SR_ONLY = "absolute h-px w-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)] [clip-path:inset(50%)] -m-px";
4412
4622
  function GraphA11yList({
4413
4623
  visible = false,
@@ -4442,14 +4652,14 @@ function GraphA11yList({
4442
4652
  }));
4443
4653
  return [...nodeRows, ...edgeRows];
4444
4654
  }, [live.nodes, live.edges]);
4445
- const parentRef = useRef5(null);
4655
+ const parentRef = useRef6(null);
4446
4656
  const virtualizer = useVirtualizer({
4447
4657
  count: rows.length,
4448
4658
  getScrollElement: () => parentRef.current,
4449
4659
  estimateSize: () => rowHeight,
4450
4660
  overscan: 8
4451
4661
  });
4452
- const activateNode = useCallback6(
4662
+ const activateNode = useCallback7(
4453
4663
  (id, additive) => {
4454
4664
  setSelectedEdge(null);
4455
4665
  if (!additive) {
@@ -4464,7 +4674,7 @@ function GraphA11yList({
4464
4674
  },
4465
4675
  [selectedNodeIds, setSelectedNodeIds, setSelectedEdge]
4466
4676
  );
4467
- const selectRow = useCallback6(
4677
+ const selectRow = useCallback7(
4468
4678
  (row, e) => {
4469
4679
  if (row.kind === "node") {
4470
4680
  activateNode(row.id, e.metaKey || e.ctrlKey || e.shiftKey);
@@ -4476,19 +4686,19 @@ function GraphA11yList({
4476
4686
  [activateNode, setSelectedNodeIds, setSelectedEdge]
4477
4687
  );
4478
4688
  const isSelected = (row) => row.kind === "node" ? selectedNodeIds.includes(row.id) : selectedEdge?.id_ === row.edge.id_;
4479
- return /* @__PURE__ */ jsx15(
4689
+ return /* @__PURE__ */ jsx17(
4480
4690
  "div",
4481
4691
  {
4482
4692
  role: "listbox",
4483
4693
  "aria-label": "Graph elements",
4484
4694
  "aria-multiselectable": true,
4485
4695
  className: cn2(visible ? "rounded-md border" : SR_ONLY, className),
4486
- children: /* @__PURE__ */ jsx15(
4696
+ children: /* @__PURE__ */ jsx17(
4487
4697
  "div",
4488
4698
  {
4489
4699
  ref: parentRef,
4490
4700
  style: { maxHeight: visible ? maxHeight : void 0, overflow: "auto" },
4491
- children: /* @__PURE__ */ jsx15(
4701
+ children: /* @__PURE__ */ jsx17(
4492
4702
  "div",
4493
4703
  {
4494
4704
  style: {
@@ -4500,7 +4710,7 @@ function GraphA11yList({
4500
4710
  const row = rows[vi.index];
4501
4711
  if (!row) return null;
4502
4712
  const selected = isSelected(row);
4503
- return /* @__PURE__ */ jsx15(
4713
+ return /* @__PURE__ */ jsx17(
4504
4714
  "button",
4505
4715
  {
4506
4716
  type: "button",
@@ -4518,7 +4728,7 @@ function GraphA11yList({
4518
4728
  height: `${vi.size}px`,
4519
4729
  transform: `translateY(${vi.start}px)`
4520
4730
  },
4521
- children: /* @__PURE__ */ jsx15("span", { className: "truncate", children: row.label })
4731
+ children: /* @__PURE__ */ jsx17("span", { className: "truncate", children: row.label })
4522
4732
  },
4523
4733
  vi.key
4524
4734
  );
@@ -4656,16 +4866,16 @@ function reconcileFilter(current, model, baselineReset, showEdgeTypes) {
4656
4866
  }
4657
4867
 
4658
4868
  // src/graph-model/GraphModelEditor.tsx
4659
- import { useCallback as useCallback7, useEffect as useEffect5, useRef as useRef6, useState as useState13 } from "react";
4869
+ import { useCallback as useCallback8, useEffect as useEffect6, useRef as useRef7, useState as useState14 } from "react";
4660
4870
  import { ResizeHandle } from "@petrarca/sonnet-ui";
4661
4871
 
4662
4872
  // src/graph-model/ModelActionPanel.tsx
4663
- import { useMemo as useMemo8, useState as useState12 } from "react";
4664
- import { Button as Button6, IconButton as IconButton2 } from "@petrarca/sonnet-ui";
4873
+ import { useMemo as useMemo8, useState as useState13 } from "react";
4874
+ import { Button as Button8, IconButton as IconButton2 } from "@petrarca/sonnet-ui";
4665
4875
  import { GitBranchPlus, CirclePlus, Waypoints } from "lucide-react";
4666
4876
 
4667
4877
  // src/graph-model/TypeDetailPanel.tsx
4668
- import { useState as useState11 } from "react";
4878
+ import { useState as useState12 } from "react";
4669
4879
  import { Badge as Badge2, IconButton } from "@petrarca/sonnet-ui";
4670
4880
  import { Plus, Pencil, Trash2 } from "lucide-react";
4671
4881
 
@@ -4985,7 +5195,7 @@ import {
4985
5195
  SheetDescription
4986
5196
  } from "@petrarca/sonnet-ui";
4987
5197
  import { JsonSchemaFormRenderer } from "@petrarca/sonnet-forms";
4988
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
5198
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
4989
5199
  var TYPE_OPTIONS = [
4990
5200
  "string",
4991
5201
  "integer",
@@ -5075,18 +5285,18 @@ function PropertyFormSheet({
5075
5285
  const schema = useMemo7(() => buildSchema(widgets), [widgets]);
5076
5286
  const data = useMemo7(() => toFormData(property), [property]);
5077
5287
  const isEdit = !!property;
5078
- return /* @__PURE__ */ jsx16(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs14(SheetContent, { children: [
5079
- /* @__PURE__ */ jsxs14(SheetHeader, { children: [
5080
- /* @__PURE__ */ jsxs14(SheetTitle, { children: [
5288
+ return /* @__PURE__ */ jsx18(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs16(SheetContent, { children: [
5289
+ /* @__PURE__ */ jsxs16(SheetHeader, { children: [
5290
+ /* @__PURE__ */ jsxs16(SheetTitle, { children: [
5081
5291
  isEdit ? "Edit property" : "Add property",
5082
5292
  " ",
5083
5293
  "\u2014",
5084
5294
  " ",
5085
5295
  ownerLabel
5086
5296
  ] }),
5087
- /* @__PURE__ */ jsx16(SheetDescription, { children: "Define the property and how it is rendered." })
5297
+ /* @__PURE__ */ jsx18(SheetDescription, { children: "Define the property and how it is rendered." })
5088
5298
  ] }),
5089
- /* @__PURE__ */ jsx16(SheetBody, { children: /* @__PURE__ */ jsx16(
5299
+ /* @__PURE__ */ jsx18(SheetBody, { children: /* @__PURE__ */ jsx18(
5090
5300
  JsonSchemaFormRenderer,
5091
5301
  {
5092
5302
  schema,
@@ -5105,7 +5315,7 @@ function PropertyFormSheet({
5105
5315
  }
5106
5316
 
5107
5317
  // src/graph-model/RelationFormSheet.tsx
5108
- import { useState as useState9 } from "react";
5318
+ import { useState as useState10 } from "react";
5109
5319
  import {
5110
5320
  Sheet as Sheet2,
5111
5321
  SheetContent as SheetContent2,
@@ -5113,11 +5323,11 @@ import {
5113
5323
  SheetBody as SheetBody2,
5114
5324
  SheetTitle as SheetTitle2,
5115
5325
  SheetDescription as SheetDescription2,
5116
- Button as Button4
5326
+ Button as Button6
5117
5327
  } from "@petrarca/sonnet-ui";
5118
5328
  import { FormSelect } from "@petrarca/sonnet-forms";
5119
5329
  import { ArrowRight } from "lucide-react";
5120
- import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
5330
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
5121
5331
  var CARDINALITIES = ["ONE_TO_MANY", "ONE_TO_ONE"];
5122
5332
  function RelationTypeField({
5123
5333
  relType,
@@ -5130,7 +5340,7 @@ function RelationTypeField({
5130
5340
  );
5131
5341
  const options = labels.map((l) => ({ value: l, label: l }));
5132
5342
  const canCreate = edgeTypeMode === "edit";
5133
- return /* @__PURE__ */ jsx17(
5343
+ return /* @__PURE__ */ jsx19(
5134
5344
  FormSelect,
5135
5345
  {
5136
5346
  label: "Relation type",
@@ -5152,14 +5362,14 @@ function DirectionField({
5152
5362
  canSwap,
5153
5363
  onSwap
5154
5364
  }) {
5155
- return /* @__PURE__ */ jsxs15("div", { children: [
5156
- /* @__PURE__ */ jsx17("label", { className: "mb-1 block text-xs font-medium", children: "Direction" }),
5157
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2 text-sm", children: [
5158
- /* @__PURE__ */ jsx17("span", { className: "font-medium", children: source }),
5159
- /* @__PURE__ */ jsx17(ArrowRight, { className: "h-4 w-4 text-muted-foreground" }),
5160
- /* @__PURE__ */ jsx17("span", { className: "font-medium", children: target }),
5161
- canSwap && /* @__PURE__ */ jsx17(
5162
- Button4,
5365
+ return /* @__PURE__ */ jsxs17("div", { children: [
5366
+ /* @__PURE__ */ jsx19("label", { className: "mb-1 block text-xs font-medium", children: "Direction" }),
5367
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2 text-sm", children: [
5368
+ /* @__PURE__ */ jsx19("span", { className: "font-medium", children: source }),
5369
+ /* @__PURE__ */ jsx19(ArrowRight, { className: "h-4 w-4 text-muted-foreground" }),
5370
+ /* @__PURE__ */ jsx19("span", { className: "font-medium", children: target }),
5371
+ canSwap && /* @__PURE__ */ jsx19(
5372
+ Button6,
5163
5373
  {
5164
5374
  variant: "ghost",
5165
5375
  size: "sm",
@@ -5175,10 +5385,10 @@ function CardinalityField({
5175
5385
  cardinality,
5176
5386
  onChange
5177
5387
  }) {
5178
- return /* @__PURE__ */ jsxs15("div", { children: [
5179
- /* @__PURE__ */ jsx17("label", { className: "mb-1 block text-xs font-medium", children: "Cardinality" }),
5180
- /* @__PURE__ */ jsx17("div", { className: "flex gap-2", children: CARDINALITIES.map((c) => /* @__PURE__ */ jsx17(
5181
- Button4,
5388
+ return /* @__PURE__ */ jsxs17("div", { children: [
5389
+ /* @__PURE__ */ jsx19("label", { className: "mb-1 block text-xs font-medium", children: "Cardinality" }),
5390
+ /* @__PURE__ */ jsx19("div", { className: "flex gap-2", children: CARDINALITIES.map((c) => /* @__PURE__ */ jsx19(
5391
+ Button6,
5182
5392
  {
5183
5393
  variant: c === cardinality ? "default" : "outline",
5184
5394
  size: "sm",
@@ -5199,9 +5409,9 @@ function RelationFormSheet({
5199
5409
  edgeTypeMode = "edit",
5200
5410
  onSave
5201
5411
  }) {
5202
- const [relType, setRelType] = useState9(initial?.relType ?? "");
5203
- const [swapped, setSwapped] = useState9(false);
5204
- const [cardinality, setCardinality] = useState9(
5412
+ const [relType, setRelType] = useState10(initial?.relType ?? "");
5413
+ const [swapped, setSwapped] = useState10(false);
5414
+ const [cardinality, setCardinality] = useState10(
5205
5415
  initial?.cardinality ?? "ONE_TO_MANY"
5206
5416
  );
5207
5417
  const source = swapped ? nodeB : nodeA;
@@ -5217,13 +5427,13 @@ function RelationFormSheet({
5217
5427
  });
5218
5428
  onOpenChange(false);
5219
5429
  };
5220
- return /* @__PURE__ */ jsx17(Sheet2, { open, onOpenChange, children: /* @__PURE__ */ jsxs15(SheetContent2, { children: [
5221
- /* @__PURE__ */ jsxs15(SheetHeader2, { children: [
5222
- /* @__PURE__ */ jsx17(SheetTitle2, { children: isEdit ? "Edit relation" : "Create relation" }),
5223
- /* @__PURE__ */ jsx17(SheetDescription2, { children: "A relation is stored on the source node type." })
5430
+ return /* @__PURE__ */ jsx19(Sheet2, { open, onOpenChange, children: /* @__PURE__ */ jsxs17(SheetContent2, { children: [
5431
+ /* @__PURE__ */ jsxs17(SheetHeader2, { children: [
5432
+ /* @__PURE__ */ jsx19(SheetTitle2, { children: isEdit ? "Edit relation" : "Create relation" }),
5433
+ /* @__PURE__ */ jsx19(SheetDescription2, { children: "A relation is stored on the source node type." })
5224
5434
  ] }),
5225
- /* @__PURE__ */ jsxs15(SheetBody2, { className: "flex flex-col gap-4", children: [
5226
- /* @__PURE__ */ jsx17(
5435
+ /* @__PURE__ */ jsxs17(SheetBody2, { className: "flex flex-col gap-4", children: [
5436
+ /* @__PURE__ */ jsx19(
5227
5437
  RelationTypeField,
5228
5438
  {
5229
5439
  relType,
@@ -5232,7 +5442,7 @@ function RelationFormSheet({
5232
5442
  edgeTypeMode
5233
5443
  }
5234
5444
  ),
5235
- /* @__PURE__ */ jsx17(
5445
+ /* @__PURE__ */ jsx19(
5236
5446
  DirectionField,
5237
5447
  {
5238
5448
  source,
@@ -5241,23 +5451,23 @@ function RelationFormSheet({
5241
5451
  onSwap: () => setSwapped((s) => !s)
5242
5452
  }
5243
5453
  ),
5244
- /* @__PURE__ */ jsx17(
5454
+ /* @__PURE__ */ jsx19(
5245
5455
  CardinalityField,
5246
5456
  {
5247
5457
  cardinality,
5248
5458
  onChange: setCardinality
5249
5459
  }
5250
5460
  ),
5251
- /* @__PURE__ */ jsxs15("div", { className: "flex justify-end gap-2 pt-2", children: [
5252
- /* @__PURE__ */ jsx17(Button4, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
5253
- /* @__PURE__ */ jsx17(Button4, { disabled: !canSave, onClick: handleSave, children: isEdit ? "Save" : "Create relation" })
5461
+ /* @__PURE__ */ jsxs17("div", { className: "flex justify-end gap-2 pt-2", children: [
5462
+ /* @__PURE__ */ jsx19(Button6, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
5463
+ /* @__PURE__ */ jsx19(Button6, { disabled: !canSave, onClick: handleSave, children: isEdit ? "Save" : "Create relation" })
5254
5464
  ] })
5255
5465
  ] })
5256
5466
  ] }) });
5257
5467
  }
5258
5468
 
5259
5469
  // src/graph-model/NodeTypeNameSheet.tsx
5260
- import { useState as useState10 } from "react";
5470
+ import { useState as useState11 } from "react";
5261
5471
  import {
5262
5472
  Sheet as Sheet3,
5263
5473
  SheetContent as SheetContent3,
@@ -5265,10 +5475,10 @@ import {
5265
5475
  SheetBody as SheetBody3,
5266
5476
  SheetTitle as SheetTitle3,
5267
5477
  SheetDescription as SheetDescription3,
5268
- Button as Button5,
5478
+ Button as Button7,
5269
5479
  Input as Input2
5270
5480
  } from "@petrarca/sonnet-ui";
5271
- import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
5481
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
5272
5482
  function NodeTypeNameSheet({
5273
5483
  open,
5274
5484
  onOpenChange,
@@ -5277,21 +5487,21 @@ function NodeTypeNameSheet({
5277
5487
  noun = "node",
5278
5488
  onSave
5279
5489
  }) {
5280
- const [name, setName] = useState10(initial ?? "");
5490
+ const [name, setName] = useState11(initial ?? "");
5281
5491
  const isEdit = initial !== void 0;
5282
5492
  const trimmed = name.trim();
5283
5493
  const duplicate = trimmed !== initial && existing.includes(trimmed);
5284
5494
  const canSave = trimmed.length > 0 && !duplicate;
5285
5495
  const typeNoun = noun === "edge" ? "edge type" : "node type";
5286
- return /* @__PURE__ */ jsx18(Sheet3, { open, onOpenChange, children: /* @__PURE__ */ jsxs16(SheetContent3, { children: [
5287
- /* @__PURE__ */ jsxs16(SheetHeader3, { children: [
5288
- /* @__PURE__ */ jsx18(SheetTitle3, { children: isEdit ? `Rename ${typeNoun}` : `Add ${typeNoun}` }),
5289
- /* @__PURE__ */ jsx18(SheetDescription3, { children: "The label identifies this type and its instances." })
5496
+ return /* @__PURE__ */ jsx20(Sheet3, { open, onOpenChange, children: /* @__PURE__ */ jsxs18(SheetContent3, { children: [
5497
+ /* @__PURE__ */ jsxs18(SheetHeader3, { children: [
5498
+ /* @__PURE__ */ jsx20(SheetTitle3, { children: isEdit ? `Rename ${typeNoun}` : `Add ${typeNoun}` }),
5499
+ /* @__PURE__ */ jsx20(SheetDescription3, { children: "The label identifies this type and its instances." })
5290
5500
  ] }),
5291
- /* @__PURE__ */ jsxs16(SheetBody3, { className: "flex flex-col gap-4", children: [
5292
- /* @__PURE__ */ jsxs16("div", { children: [
5293
- /* @__PURE__ */ jsx18("label", { className: "mb-1 block text-xs font-medium", children: "Label" }),
5294
- /* @__PURE__ */ jsx18(
5501
+ /* @__PURE__ */ jsxs18(SheetBody3, { className: "flex flex-col gap-4", children: [
5502
+ /* @__PURE__ */ jsxs18("div", { children: [
5503
+ /* @__PURE__ */ jsx20("label", { className: "mb-1 block text-xs font-medium", children: "Label" }),
5504
+ /* @__PURE__ */ jsx20(
5295
5505
  Input2,
5296
5506
  {
5297
5507
  value: name,
@@ -5300,12 +5510,12 @@ function NodeTypeNameSheet({
5300
5510
  autoFocus: true
5301
5511
  }
5302
5512
  ),
5303
- duplicate && /* @__PURE__ */ jsx18("p", { className: "mt-1 text-xs text-destructive", children: "A type with this label already exists." })
5513
+ duplicate && /* @__PURE__ */ jsx20("p", { className: "mt-1 text-xs text-destructive", children: "A type with this label already exists." })
5304
5514
  ] }),
5305
- /* @__PURE__ */ jsxs16("div", { className: "flex justify-end gap-2", children: [
5306
- /* @__PURE__ */ jsx18(Button5, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
5307
- /* @__PURE__ */ jsx18(
5308
- Button5,
5515
+ /* @__PURE__ */ jsxs18("div", { className: "flex justify-end gap-2", children: [
5516
+ /* @__PURE__ */ jsx20(Button7, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
5517
+ /* @__PURE__ */ jsx20(
5518
+ Button7,
5309
5519
  {
5310
5520
  disabled: !canSave,
5311
5521
  onClick: () => {
@@ -5321,7 +5531,7 @@ function NodeTypeNameSheet({
5321
5531
  }
5322
5532
 
5323
5533
  // src/graph-model/TypeDetailPanel.tsx
5324
- import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
5534
+ import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
5325
5535
  function relationViewOf(view) {
5326
5536
  return {
5327
5537
  relType: view.relType,
@@ -5334,9 +5544,9 @@ function SectionTitle({
5334
5544
  children,
5335
5545
  action
5336
5546
  }) {
5337
- return /* @__PURE__ */ jsxs17("div", { className: "mt-4 mb-2 flex items-center gap-2", children: [
5338
- /* @__PURE__ */ jsx19("span", { className: "heading-meta", children }),
5339
- action && /* @__PURE__ */ jsx19("span", { className: "ml-auto", children: action })
5547
+ return /* @__PURE__ */ jsxs19("div", { className: "mt-4 mb-2 flex items-center gap-2", children: [
5548
+ /* @__PURE__ */ jsx21("span", { className: "heading-meta", children }),
5549
+ action && /* @__PURE__ */ jsx21("span", { className: "ml-auto", children: action })
5340
5550
  ] });
5341
5551
  }
5342
5552
  function PropertyRows({
@@ -5347,37 +5557,37 @@ function PropertyRows({
5347
5557
  onDelete
5348
5558
  }) {
5349
5559
  if (properties.length === 0) {
5350
- return /* @__PURE__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: emptyText });
5560
+ return /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: emptyText });
5351
5561
  }
5352
- return /* @__PURE__ */ jsx19("div", { className: "rounded-lg border divide-y", children: properties.map((p) => /* @__PURE__ */ jsxs17(
5562
+ return /* @__PURE__ */ jsx21("div", { className: "rounded-lg border divide-y", children: properties.map((p) => /* @__PURE__ */ jsxs19(
5353
5563
  "div",
5354
5564
  {
5355
5565
  className: "group/row flex items-center gap-2 px-3 py-2",
5356
5566
  children: [
5357
- /* @__PURE__ */ jsx19("span", { className: "text-sm font-medium min-w-24", children: p.name }),
5358
- /* @__PURE__ */ jsxs17("span", { className: "flex-1 font-mono text-xs text-muted-foreground", children: [
5567
+ /* @__PURE__ */ jsx21("span", { className: "text-sm font-medium min-w-24", children: p.name }),
5568
+ /* @__PURE__ */ jsxs19("span", { className: "flex-1 font-mono text-xs text-muted-foreground", children: [
5359
5569
  p.type,
5360
5570
  p.widget ? ` \xB7 ${p.widget}` : ""
5361
5571
  ] }),
5362
- p.required && /* @__PURE__ */ jsx19(Badge2, { badgeColor: "orange", children: "required" }),
5363
- editable && /* @__PURE__ */ jsxs17("span", { className: "flex gap-1", children: [
5364
- /* @__PURE__ */ jsx19(
5572
+ p.required && /* @__PURE__ */ jsx21(Badge2, { badgeColor: "orange", children: "required" }),
5573
+ editable && /* @__PURE__ */ jsxs19("span", { className: "flex gap-1", children: [
5574
+ /* @__PURE__ */ jsx21(
5365
5575
  IconButton,
5366
5576
  {
5367
5577
  label: "Edit property",
5368
5578
  revealOnHover: true,
5369
5579
  onClick: () => onEdit(p),
5370
- children: /* @__PURE__ */ jsx19(Pencil, {})
5580
+ children: /* @__PURE__ */ jsx21(Pencil, {})
5371
5581
  }
5372
5582
  ),
5373
- /* @__PURE__ */ jsx19(
5583
+ /* @__PURE__ */ jsx21(
5374
5584
  IconButton,
5375
5585
  {
5376
5586
  label: "Delete property",
5377
5587
  tone: "destructive",
5378
5588
  revealOnHover: true,
5379
5589
  onClick: () => onDelete(p),
5380
- children: /* @__PURE__ */ jsx19(Trash2, {})
5590
+ children: /* @__PURE__ */ jsx21(Trash2, {})
5381
5591
  }
5382
5592
  )
5383
5593
  ] })
@@ -5431,9 +5641,9 @@ function TypeDetailPanel({
5431
5641
  capabilities,
5432
5642
  widgetOptions
5433
5643
  }) {
5434
- const [sheet, setSheet] = useState11({ open: false });
5435
- const [relSheet, setRelSheet] = useState11(null);
5436
- const [renameOpen, setRenameOpen] = useState11(false);
5644
+ const [sheet, setSheet] = useState12({ open: false });
5645
+ const [relSheet, setRelSheet] = useState12(null);
5646
+ const [renameOpen, setRenameOpen] = useState12(false);
5437
5647
  const view = resolveView(model, selection);
5438
5648
  if (!view) return null;
5439
5649
  const editable = !readOnly && !!onChange;
@@ -5460,7 +5670,7 @@ function TypeDetailPanel({
5460
5670
  if (!onChange) return;
5461
5671
  onChange(updateRelation(model, source, prev.relType, prev.target, input));
5462
5672
  };
5463
- const propertiesSection = propsHidden ? null : /* @__PURE__ */ jsx19(
5673
+ const propertiesSection = propsHidden ? null : /* @__PURE__ */ jsx21(
5464
5674
  PropertiesSection,
5465
5675
  {
5466
5676
  view,
@@ -5470,8 +5680,8 @@ function TypeDetailPanel({
5470
5680
  onDelete: removeProperty
5471
5681
  }
5472
5682
  );
5473
- return /* @__PURE__ */ jsxs17("div", { children: [
5474
- view.kind === "node" ? /* @__PURE__ */ jsx19(
5683
+ return /* @__PURE__ */ jsxs19("div", { children: [
5684
+ view.kind === "node" ? /* @__PURE__ */ jsx21(
5475
5685
  NodeDetail,
5476
5686
  {
5477
5687
  view,
@@ -5483,7 +5693,7 @@ function TypeDetailPanel({
5483
5693
  onDeleteRelation: removeRelation,
5484
5694
  children: propertiesSection
5485
5695
  }
5486
- ) : view.kind === "edge" ? /* @__PURE__ */ jsx19(
5696
+ ) : view.kind === "edge" ? /* @__PURE__ */ jsx21(
5487
5697
  EdgeDetail,
5488
5698
  {
5489
5699
  view,
@@ -5492,7 +5702,7 @@ function TypeDetailPanel({
5492
5702
  onDelete: () => onChange?.(deleteEdgeType(model, view.label)),
5493
5703
  children: propertiesSection
5494
5704
  }
5495
- ) : /* @__PURE__ */ jsx19(
5705
+ ) : /* @__PURE__ */ jsx21(
5496
5706
  RelationDetail,
5497
5707
  {
5498
5708
  view,
@@ -5502,7 +5712,7 @@ function TypeDetailPanel({
5502
5712
  children: propertiesSection
5503
5713
  }
5504
5714
  ),
5505
- editable && /* @__PURE__ */ jsx19(
5715
+ editable && /* @__PURE__ */ jsx21(
5506
5716
  DetailSheets,
5507
5717
  {
5508
5718
  model,
@@ -5546,8 +5756,8 @@ function DetailSheets({
5546
5756
  onRename
5547
5757
  }) {
5548
5758
  const edgeTypeOptions = model.filter(isEdgeType).map(typeLabel);
5549
- return /* @__PURE__ */ jsxs17(Fragment4, { children: [
5550
- /* @__PURE__ */ jsx19(
5759
+ return /* @__PURE__ */ jsxs19(Fragment4, { children: [
5760
+ /* @__PURE__ */ jsx21(
5551
5761
  PropertyFormSheet,
5552
5762
  {
5553
5763
  open: sheet.open,
@@ -5558,7 +5768,7 @@ function DetailSheets({
5558
5768
  onSave: onSaveProperty
5559
5769
  }
5560
5770
  ),
5561
- relSheet && (view.kind === "node" || view.kind === "relation") && /* @__PURE__ */ jsx19(
5771
+ relSheet && (view.kind === "node" || view.kind === "relation") && /* @__PURE__ */ jsx21(
5562
5772
  RelationFormSheet,
5563
5773
  {
5564
5774
  open: !!relSheet,
@@ -5575,7 +5785,7 @@ function DetailSheets({
5575
5785
  },
5576
5786
  `${relSheet.relType}:${relSheet.target}`
5577
5787
  ),
5578
- (view.kind === "node" || view.kind === "edge") && renameOpen && /* @__PURE__ */ jsx19(
5788
+ (view.kind === "node" || view.kind === "edge") && renameOpen && /* @__PURE__ */ jsx21(
5579
5789
  NodeTypeNameSheet,
5580
5790
  {
5581
5791
  open: renameOpen,
@@ -5596,15 +5806,15 @@ function PropertiesSection({
5596
5806
  onEdit,
5597
5807
  onDelete
5598
5808
  }) {
5599
- return /* @__PURE__ */ jsxs17(Fragment4, { children: [
5600
- /* @__PURE__ */ jsx19(
5809
+ return /* @__PURE__ */ jsxs19(Fragment4, { children: [
5810
+ /* @__PURE__ */ jsx21(
5601
5811
  SectionTitle,
5602
5812
  {
5603
- action: editable ? /* @__PURE__ */ jsx19(IconButton, { label: "Add property", onClick: onAdd, children: /* @__PURE__ */ jsx19(Plus, {}) }) : void 0,
5813
+ action: editable ? /* @__PURE__ */ jsx21(IconButton, { label: "Add property", onClick: onAdd, children: /* @__PURE__ */ jsx21(Plus, {}) }) : void 0,
5604
5814
  children: "Properties"
5605
5815
  }
5606
5816
  ),
5607
- /* @__PURE__ */ jsx19(
5817
+ /* @__PURE__ */ jsx21(
5608
5818
  PropertyRows,
5609
5819
  {
5610
5820
  properties: view.properties,
@@ -5626,11 +5836,11 @@ function NodeDetail({
5626
5836
  onDeleteRelation,
5627
5837
  children
5628
5838
  }) {
5629
- return /* @__PURE__ */ jsxs17(Fragment4, { children: [
5630
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
5631
- /* @__PURE__ */ jsx19("span", { className: "text-base font-semibold", children: view.label }),
5632
- /* @__PURE__ */ jsx19(Badge2, { children: "NODE TYPE" }),
5633
- nodeTypeEditable && /* @__PURE__ */ jsx19(
5839
+ return /* @__PURE__ */ jsxs19(Fragment4, { children: [
5840
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", children: [
5841
+ /* @__PURE__ */ jsx21("span", { className: "text-base font-semibold", children: view.label }),
5842
+ /* @__PURE__ */ jsx21(Badge2, { children: "NODE TYPE" }),
5843
+ nodeTypeEditable && /* @__PURE__ */ jsx21(
5634
5844
  RenameDeleteActions,
5635
5845
  {
5636
5846
  noun: "node type",
@@ -5639,16 +5849,16 @@ function NodeDetail({
5639
5849
  }
5640
5850
  )
5641
5851
  ] }),
5642
- view.description && /* @__PURE__ */ jsx19("p", { className: "mt-1 text-sm text-muted-foreground", children: view.description }),
5852
+ view.description && /* @__PURE__ */ jsx21("p", { className: "mt-1 text-sm text-muted-foreground", children: view.description }),
5643
5853
  children,
5644
- /* @__PURE__ */ jsx19(SectionTitle, { children: "Relations" }),
5645
- view.relations.length === 0 ? /* @__PURE__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: "No relations." }) : /* @__PURE__ */ jsx19("div", { className: "rounded-lg border divide-y", children: view.relations.map((r) => /* @__PURE__ */ jsxs17(
5854
+ /* @__PURE__ */ jsx21(SectionTitle, { children: "Relations" }),
5855
+ view.relations.length === 0 ? /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: "No relations." }) : /* @__PURE__ */ jsx21("div", { className: "rounded-lg border divide-y", children: view.relations.map((r) => /* @__PURE__ */ jsxs19(
5646
5856
  "div",
5647
5857
  {
5648
5858
  className: "group/row flex items-center gap-2 px-3 py-2",
5649
5859
  children: [
5650
- /* @__PURE__ */ jsx19("span", { className: "text-sm font-medium min-w-24", children: r.relType }),
5651
- /* @__PURE__ */ jsxs17("span", { className: "flex-1 font-mono text-xs text-muted-foreground", children: [
5860
+ /* @__PURE__ */ jsx21("span", { className: "text-sm font-medium min-w-24", children: r.relType }),
5861
+ /* @__PURE__ */ jsxs19("span", { className: "flex-1 font-mono text-xs text-muted-foreground", children: [
5652
5862
  "\u2192",
5653
5863
  " ",
5654
5864
  r.target,
@@ -5657,24 +5867,24 @@ function NodeDetail({
5657
5867
  " ",
5658
5868
  cardinalityLabel(r.cardinality)
5659
5869
  ] }),
5660
- relationsEditable && /* @__PURE__ */ jsxs17("span", { className: "flex gap-1", children: [
5661
- /* @__PURE__ */ jsx19(
5870
+ relationsEditable && /* @__PURE__ */ jsxs19("span", { className: "flex gap-1", children: [
5871
+ /* @__PURE__ */ jsx21(
5662
5872
  IconButton,
5663
5873
  {
5664
5874
  label: "Edit relation",
5665
5875
  revealOnHover: true,
5666
5876
  onClick: () => onEditRelation(r),
5667
- children: /* @__PURE__ */ jsx19(Pencil, {})
5877
+ children: /* @__PURE__ */ jsx21(Pencil, {})
5668
5878
  }
5669
5879
  ),
5670
- /* @__PURE__ */ jsx19(
5880
+ /* @__PURE__ */ jsx21(
5671
5881
  IconButton,
5672
5882
  {
5673
5883
  label: "Delete relation",
5674
5884
  tone: "destructive",
5675
5885
  revealOnHover: true,
5676
5886
  onClick: () => onDeleteRelation(r),
5677
- children: /* @__PURE__ */ jsx19(Trash2, {})
5887
+ children: /* @__PURE__ */ jsx21(Trash2, {})
5678
5888
  }
5679
5889
  )
5680
5890
  ] })
@@ -5687,7 +5897,7 @@ function NodeDetail({
5687
5897
  function EdgeTypeNote({
5688
5898
  children
5689
5899
  }) {
5690
- return /* @__PURE__ */ jsx19("p", { className: "mt-3 rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground", children });
5900
+ return /* @__PURE__ */ jsx21("p", { className: "mt-3 rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground", children });
5691
5901
  }
5692
5902
  function RenameDeleteActions({
5693
5903
  noun,
@@ -5695,15 +5905,15 @@ function RenameDeleteActions({
5695
5905
  onRename,
5696
5906
  onDelete
5697
5907
  }) {
5698
- return /* @__PURE__ */ jsxs17("span", { className: "ml-auto flex gap-1", children: [
5699
- /* @__PURE__ */ jsx19(IconButton, { label: `${editVerb} ${noun}`, onClick: onRename, children: /* @__PURE__ */ jsx19(Pencil, {}) }),
5700
- /* @__PURE__ */ jsx19(
5908
+ return /* @__PURE__ */ jsxs19("span", { className: "ml-auto flex gap-1", children: [
5909
+ /* @__PURE__ */ jsx21(IconButton, { label: `${editVerb} ${noun}`, onClick: onRename, children: /* @__PURE__ */ jsx21(Pencil, {}) }),
5910
+ /* @__PURE__ */ jsx21(
5701
5911
  IconButton,
5702
5912
  {
5703
5913
  label: `Delete ${noun}`,
5704
5914
  tone: "destructive",
5705
5915
  onClick: onDelete,
5706
- children: /* @__PURE__ */ jsx19(Trash2, {})
5916
+ children: /* @__PURE__ */ jsx21(Trash2, {})
5707
5917
  }
5708
5918
  )
5709
5919
  ] });
@@ -5715,11 +5925,11 @@ function EdgeDetail({
5715
5925
  onDelete,
5716
5926
  children
5717
5927
  }) {
5718
- return /* @__PURE__ */ jsxs17(Fragment4, { children: [
5719
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
5720
- /* @__PURE__ */ jsx19("span", { className: "text-base font-semibold", children: view.label }),
5721
- /* @__PURE__ */ jsx19(Badge2, { children: "EDGE TYPE" }),
5722
- editable && /* @__PURE__ */ jsx19(
5928
+ return /* @__PURE__ */ jsxs19(Fragment4, { children: [
5929
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", children: [
5930
+ /* @__PURE__ */ jsx21("span", { className: "text-base font-semibold", children: view.label }),
5931
+ /* @__PURE__ */ jsx21(Badge2, { children: "EDGE TYPE" }),
5932
+ editable && /* @__PURE__ */ jsx21(
5723
5933
  RenameDeleteActions,
5724
5934
  {
5725
5935
  noun: "edge type",
@@ -5728,11 +5938,11 @@ function EdgeDetail({
5728
5938
  }
5729
5939
  )
5730
5940
  ] }),
5731
- view.description && /* @__PURE__ */ jsx19("p", { className: "mt-1 text-sm text-muted-foreground", children: view.description }),
5732
- /* @__PURE__ */ jsxs17(EdgeTypeNote, { children: [
5941
+ view.description && /* @__PURE__ */ jsx21("p", { className: "mt-1 text-sm text-muted-foreground", children: view.description }),
5942
+ /* @__PURE__ */ jsxs19(EdgeTypeNote, { children: [
5733
5943
  "These properties belong to the",
5734
5944
  " ",
5735
- /* @__PURE__ */ jsx19("span", { className: "font-medium", children: view.label }),
5945
+ /* @__PURE__ */ jsx21("span", { className: "font-medium", children: view.label }),
5736
5946
  " edge type and are shared by every relation of this type."
5737
5947
  ] }),
5738
5948
  children
@@ -5745,11 +5955,11 @@ function RelationDetail({
5745
5955
  onDelete,
5746
5956
  children
5747
5957
  }) {
5748
- return /* @__PURE__ */ jsxs17(Fragment4, { children: [
5749
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2", children: [
5750
- /* @__PURE__ */ jsx19("span", { className: "text-base font-semibold", children: view.relType }),
5751
- /* @__PURE__ */ jsx19(Badge2, { children: "RELATION" }),
5752
- editable && /* @__PURE__ */ jsx19(
5958
+ return /* @__PURE__ */ jsxs19(Fragment4, { children: [
5959
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", children: [
5960
+ /* @__PURE__ */ jsx21("span", { className: "text-base font-semibold", children: view.relType }),
5961
+ /* @__PURE__ */ jsx21(Badge2, { children: "RELATION" }),
5962
+ editable && /* @__PURE__ */ jsx21(
5753
5963
  RenameDeleteActions,
5754
5964
  {
5755
5965
  noun: "relation",
@@ -5759,17 +5969,17 @@ function RelationDetail({
5759
5969
  }
5760
5970
  )
5761
5971
  ] }),
5762
- /* @__PURE__ */ jsxs17("p", { className: "mt-1 text-sm text-muted-foreground", children: [
5763
- /* @__PURE__ */ jsx19("span", { className: "font-medium text-foreground", children: view.source }),
5972
+ /* @__PURE__ */ jsxs19("p", { className: "mt-1 text-sm text-muted-foreground", children: [
5973
+ /* @__PURE__ */ jsx21("span", { className: "font-medium text-foreground", children: view.source }),
5764
5974
  " \u2192 ",
5765
- /* @__PURE__ */ jsx19("span", { className: "font-medium text-foreground", children: view.target }),
5975
+ /* @__PURE__ */ jsx21("span", { className: "font-medium text-foreground", children: view.target }),
5766
5976
  " \xB7 ",
5767
5977
  cardinalityLabel(view.cardinality)
5768
5978
  ] }),
5769
- /* @__PURE__ */ jsxs17(EdgeTypeNote, { children: [
5979
+ /* @__PURE__ */ jsxs19(EdgeTypeNote, { children: [
5770
5980
  "Properties are defined on the",
5771
5981
  " ",
5772
- /* @__PURE__ */ jsx19("span", { className: "font-medium", children: view.relType }),
5982
+ /* @__PURE__ */ jsx21("span", { className: "font-medium", children: view.relType }),
5773
5983
  " edge type, shared by every relation of this type -- not on this single relation."
5774
5984
  ] }),
5775
5985
  children
@@ -5834,7 +6044,7 @@ function validateCapabilities(input) {
5834
6044
  }
5835
6045
 
5836
6046
  // src/graph-model/ModelActionPanel.tsx
5837
- import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
6047
+ import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
5838
6048
  function nodeTypeLabels(model) {
5839
6049
  return model.filter(isNodeType).map(typeLabel);
5840
6050
  }
@@ -5848,19 +6058,19 @@ function CreateRelationPrompt({
5848
6058
  edgeTypeMode,
5849
6059
  onChange
5850
6060
  }) {
5851
- const [open, setOpen] = useState12(false);
5852
- return /* @__PURE__ */ jsxs18("div", { children: [
5853
- /* @__PURE__ */ jsxs18("div", { className: "text-sm", children: [
5854
- /* @__PURE__ */ jsx20("span", { className: "font-medium", children: nodeA }),
6061
+ const [open, setOpen] = useState13(false);
6062
+ return /* @__PURE__ */ jsxs20("div", { children: [
6063
+ /* @__PURE__ */ jsxs20("div", { className: "text-sm", children: [
6064
+ /* @__PURE__ */ jsx22("span", { className: "font-medium", children: nodeA }),
5855
6065
  " and ",
5856
- /* @__PURE__ */ jsx20("span", { className: "font-medium", children: nodeB }),
6066
+ /* @__PURE__ */ jsx22("span", { className: "font-medium", children: nodeB }),
5857
6067
  " selected."
5858
6068
  ] }),
5859
- /* @__PURE__ */ jsxs18(Button6, { size: "sm", className: "mt-3 gap-1.5", onClick: () => setOpen(true), children: [
5860
- /* @__PURE__ */ jsx20(GitBranchPlus, { className: "h-4 w-4" }),
6069
+ /* @__PURE__ */ jsxs20(Button8, { size: "sm", className: "mt-3 gap-1.5", onClick: () => setOpen(true), children: [
6070
+ /* @__PURE__ */ jsx22(GitBranchPlus, { className: "h-4 w-4" }),
5861
6071
  "Create relation"
5862
6072
  ] }),
5863
- /* @__PURE__ */ jsx20(
6073
+ /* @__PURE__ */ jsx22(
5864
6074
  RelationFormSheet,
5865
6075
  {
5866
6076
  open,
@@ -5916,7 +6126,7 @@ function ModelActionPanel({
5916
6126
  const schema = findType(model, l);
5917
6127
  return schema ? isNodeType(schema) : false;
5918
6128
  }) : [];
5919
- return /* @__PURE__ */ jsx20("div", { className: "h-full overflow-auto p-4", children: /* @__PURE__ */ jsx20(
6129
+ return /* @__PURE__ */ jsx22("div", { className: "h-full overflow-auto p-4", children: /* @__PURE__ */ jsx22(
5920
6130
  PanelBody,
5921
6131
  {
5922
6132
  model,
@@ -5939,7 +6149,7 @@ function PanelBody({
5939
6149
  twoLabels
5940
6150
  }) {
5941
6151
  if (selection) {
5942
- return /* @__PURE__ */ jsx20(
6152
+ return /* @__PURE__ */ jsx22(
5943
6153
  TypeDetailPanel,
5944
6154
  {
5945
6155
  model,
@@ -5953,7 +6163,7 @@ function PanelBody({
5953
6163
  }
5954
6164
  const canEdit = !readOnly && !!onChange;
5955
6165
  if (twoLabels.length === 2 && canEdit && caps.relations === "edit") {
5956
- return /* @__PURE__ */ jsx20(
6166
+ return /* @__PURE__ */ jsx22(
5957
6167
  CreateRelationPrompt,
5958
6168
  {
5959
6169
  nodeA: twoLabels[0],
@@ -5964,8 +6174,8 @@ function PanelBody({
5964
6174
  }
5965
6175
  );
5966
6176
  }
5967
- return /* @__PURE__ */ jsxs18(Fragment5, { children: [
5968
- canEdit && /* @__PURE__ */ jsx20(
6177
+ return /* @__PURE__ */ jsxs20(Fragment5, { children: [
6178
+ canEdit && /* @__PURE__ */ jsx22(
5969
6179
  AddTypeToolbar,
5970
6180
  {
5971
6181
  model,
@@ -5974,7 +6184,7 @@ function PanelBody({
5974
6184
  canAddEdge: caps.edgeTypes === "edit"
5975
6185
  }
5976
6186
  ),
5977
- /* @__PURE__ */ jsx20(
6187
+ /* @__PURE__ */ jsx22(
5978
6188
  Overview_default,
5979
6189
  {
5980
6190
  nodeBuckets: [
@@ -5993,9 +6203,9 @@ function AddTypeIconButton({
5993
6203
  existing,
5994
6204
  onSave
5995
6205
  }) {
5996
- const [open, setOpen] = useState12(false);
5997
- return /* @__PURE__ */ jsxs18(Fragment5, { children: [
5998
- /* @__PURE__ */ jsx20(
6206
+ const [open, setOpen] = useState13(false);
6207
+ return /* @__PURE__ */ jsxs20(Fragment5, { children: [
6208
+ /* @__PURE__ */ jsx22(
5999
6209
  IconButton2,
6000
6210
  {
6001
6211
  label: tooltip,
@@ -6004,7 +6214,7 @@ function AddTypeIconButton({
6004
6214
  children: icon
6005
6215
  }
6006
6216
  ),
6007
- /* @__PURE__ */ jsx20(
6217
+ /* @__PURE__ */ jsx22(
6008
6218
  NodeTypeNameSheet,
6009
6219
  {
6010
6220
  open,
@@ -6024,22 +6234,22 @@ function AddTypeToolbar({
6024
6234
  }) {
6025
6235
  if (!canAddNode && !canAddEdge) return null;
6026
6236
  const existing = model.map(typeLabel);
6027
- return /* @__PURE__ */ jsxs18("div", { className: "mb-3 flex gap-1.5", children: [
6028
- canAddNode && /* @__PURE__ */ jsx20(
6237
+ return /* @__PURE__ */ jsxs20("div", { className: "mb-3 flex gap-1.5", children: [
6238
+ canAddNode && /* @__PURE__ */ jsx22(
6029
6239
  AddTypeIconButton,
6030
6240
  {
6031
6241
  tooltip: "Add node type",
6032
- icon: /* @__PURE__ */ jsx20(CirclePlus, {}),
6242
+ icon: /* @__PURE__ */ jsx22(CirclePlus, {}),
6033
6243
  noun: "node",
6034
6244
  existing,
6035
6245
  onSave: (name) => onChange(addNodeType(model, name))
6036
6246
  }
6037
6247
  ),
6038
- canAddEdge && /* @__PURE__ */ jsx20(
6248
+ canAddEdge && /* @__PURE__ */ jsx22(
6039
6249
  AddTypeIconButton,
6040
6250
  {
6041
6251
  tooltip: "Add edge type",
6042
- icon: /* @__PURE__ */ jsx20(Waypoints, {}),
6252
+ icon: /* @__PURE__ */ jsx22(Waypoints, {}),
6043
6253
  noun: "edge",
6044
6254
  existing,
6045
6255
  onSave: (name) => onChange(addEdgeType(model, name))
@@ -6049,7 +6259,7 @@ function AddTypeToolbar({
6049
6259
  }
6050
6260
 
6051
6261
  // src/graph-model/GraphModelEditor.tsx
6052
- import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
6262
+ import { Fragment as Fragment6, jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
6053
6263
  function createEditorStores() {
6054
6264
  const graphFilter = createGraphFilterStore();
6055
6265
  const graphData = createGraphDataStore();
@@ -6086,14 +6296,14 @@ function GraphModelEditor({
6086
6296
  defaultRenderer = "cytoscape",
6087
6297
  hideActionPanel = false
6088
6298
  }) {
6089
- const storesRef = useRef6(null);
6299
+ const storesRef = useRef7(null);
6090
6300
  if (!storesRef.current) storesRef.current = createEditorStores();
6091
6301
  const stores = storesRef.current;
6092
6302
  const caps = resolveCapabilities(capabilities, readOnly);
6093
6303
  const showEdgeTypes = caps.showEdgeTypes;
6094
- const firstLoadRef = useRef6(true);
6095
- const prevShowEdgeTypesRef = useRef6(showEdgeTypes);
6096
- useEffect5(() => {
6304
+ const firstLoadRef = useRef7(true);
6305
+ const prevShowEdgeTypesRef = useRef7(showEdgeTypes);
6306
+ useEffect6(() => {
6097
6307
  loadModel(stores, value);
6098
6308
  const filter = stores.graphFilter.getState();
6099
6309
  const baselineReset = firstLoadRef.current || prevShowEdgeTypesRef.current !== showEdgeTypes;
@@ -6107,9 +6317,9 @@ function GraphModelEditor({
6107
6317
  firstLoadRef.current = false;
6108
6318
  prevShowEdgeTypesRef.current = showEdgeTypes;
6109
6319
  }, [stores, value, showEdgeTypes]);
6110
- const rowRef = useRef6(null);
6111
- const [panelWidth, setPanelWidth] = useState13(380);
6112
- const startResize = useCallback7((e) => {
6320
+ const rowRef = useRef7(null);
6321
+ const [panelWidth, setPanelWidth] = useState14(380);
6322
+ const startResize = useCallback8((e) => {
6113
6323
  e.preventDefault();
6114
6324
  const target = e.currentTarget;
6115
6325
  target.setPointerCapture(e.pointerId);
@@ -6124,10 +6334,10 @@ function GraphModelEditor({
6124
6334
  window.addEventListener("pointermove", move);
6125
6335
  window.addEventListener("pointerup", up);
6126
6336
  }, []);
6127
- return /* @__PURE__ */ jsx21(GraphStoresContext.Provider, { value: stores, children: /* @__PURE__ */ jsx21(GraphRendererProvider, { defaultRenderer, children: /* @__PURE__ */ jsxs19("div", { ref: rowRef, className: "flex w-full h-full overflow-hidden", children: [
6128
- /* @__PURE__ */ jsx21("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx21(GraphVisualizer_default, {}) }),
6129
- !hideActionPanel && /* @__PURE__ */ jsxs19(Fragment6, { children: [
6130
- /* @__PURE__ */ jsx21(
6337
+ return /* @__PURE__ */ jsx23(GraphStoresContext.Provider, { value: stores, children: /* @__PURE__ */ jsx23(GraphRendererProvider, { defaultRenderer, children: /* @__PURE__ */ jsxs21("div", { ref: rowRef, className: "flex w-full h-full overflow-hidden", children: [
6338
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx23(GraphVisualizer_default, {}) }),
6339
+ !hideActionPanel && /* @__PURE__ */ jsxs21(Fragment6, { children: [
6340
+ /* @__PURE__ */ jsx23(
6131
6341
  ResizeHandle,
6132
6342
  {
6133
6343
  orientation: "vertical",
@@ -6138,12 +6348,12 @@ function GraphModelEditor({
6138
6348
  onDoubleClick: () => setPanelWidth(380)
6139
6349
  }
6140
6350
  ),
6141
- /* @__PURE__ */ jsx21(
6351
+ /* @__PURE__ */ jsx23(
6142
6352
  "div",
6143
6353
  {
6144
6354
  style: { width: panelWidth },
6145
6355
  className: "shrink-0 border-l overflow-hidden bg-card",
6146
- children: /* @__PURE__ */ jsx21(
6356
+ children: /* @__PURE__ */ jsx23(
6147
6357
  ModelActionPanel,
6148
6358
  {
6149
6359
  model: value,
@@ -6307,17 +6517,17 @@ function enrichSchemaWithRelations(baseSchema, relations) {
6307
6517
  }
6308
6518
 
6309
6519
  // src/graph-edit/GraphEditToolbar.tsx
6310
- import { useCallback as useCallback11, useMemo as useMemo11, useState as useState17 } from "react";
6520
+ import { useCallback as useCallback12, useMemo as useMemo11, useState as useState18 } from "react";
6311
6521
  import { IconButton as IconButton4 } from "@petrarca/sonnet-ui";
6312
6522
  import { Plus as Plus3, Edit, Link, Trash2 as Trash23 } from "lucide-react";
6313
6523
 
6314
6524
  // src/graph-edit/NodeFormSheet.tsx
6315
6525
  import {
6316
- useCallback as useCallback8,
6317
- useEffect as useEffect6,
6526
+ useCallback as useCallback9,
6527
+ useEffect as useEffect7,
6318
6528
  useMemo as useMemo9,
6319
- useRef as useRef7,
6320
- useState as useState14
6529
+ useRef as useRef8,
6530
+ useState as useState15
6321
6531
  } from "react";
6322
6532
  import {
6323
6533
  Sheet as Sheet4,
@@ -6326,7 +6536,7 @@ import {
6326
6536
  SheetBody as SheetBody4,
6327
6537
  SheetTitle as SheetTitle4,
6328
6538
  SheetDescription as SheetDescription4,
6329
- Alert
6539
+ Alert as Alert2
6330
6540
  } from "@petrarca/sonnet-ui";
6331
6541
  import {
6332
6542
  FormSelect as FormSelect2,
@@ -6334,7 +6544,7 @@ import {
6334
6544
  JsonSchemaFormRenderer as JsonSchemaFormRenderer2
6335
6545
  } from "@petrarca/sonnet-forms";
6336
6546
  import { errorLog as errorLog4 } from "@petrarca/sonnet-core";
6337
- import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
6547
+ import { Fragment as Fragment7, jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
6338
6548
  var EMPTY_RESOLVED = {
6339
6549
  schema: null,
6340
6550
  uiSchema: {},
@@ -6358,10 +6568,10 @@ function resolveBaseSchema(provided, node) {
6358
6568
  return { type: "object", properties: {} };
6359
6569
  }
6360
6570
  function useNodeSchema(schemaProvider, label, node) {
6361
- const [resolved, setResolved] = useState14(EMPTY_RESOLVED);
6362
- const nodeRef = useRef7(node);
6571
+ const [resolved, setResolved] = useState15(EMPTY_RESOLVED);
6572
+ const nodeRef = useRef8(node);
6363
6573
  nodeRef.current = node;
6364
- useEffect6(() => {
6574
+ useEffect7(() => {
6365
6575
  let cancelled = false;
6366
6576
  if (!label) {
6367
6577
  setResolved(EMPTY_RESOLVED);
@@ -6399,11 +6609,11 @@ function useNodeSchema(schemaProvider, label, node) {
6399
6609
  return resolved;
6400
6610
  }
6401
6611
  function useExistingFormData(port, node, relationTypes) {
6402
- const [data, setData] = useState14(null);
6612
+ const [data, setData] = useState15(null);
6403
6613
  const nodeId2 = node?.id_;
6404
- const propertiesRef = useRef7(node?.properties_);
6614
+ const propertiesRef = useRef8(node?.properties_);
6405
6615
  propertiesRef.current = node?.properties_;
6406
- useEffect6(() => {
6616
+ useEffect7(() => {
6407
6617
  let cancelled = false;
6408
6618
  if (nodeId2 == null) {
6409
6619
  setData(null);
@@ -6445,12 +6655,12 @@ function LabelField({
6445
6655
  onSelect
6446
6656
  }) {
6447
6657
  if (isEdit) {
6448
- return /* @__PURE__ */ jsxs20("div", { children: [
6449
- /* @__PURE__ */ jsx22("span", { className: "heading-meta block", children: "Label" }),
6450
- /* @__PURE__ */ jsx22("span", { className: "text-sm text-muted-foreground", children: selectedLabel })
6658
+ return /* @__PURE__ */ jsxs22("div", { children: [
6659
+ /* @__PURE__ */ jsx24("span", { className: "heading-meta block", children: "Label" }),
6660
+ /* @__PURE__ */ jsx24("span", { className: "text-sm text-muted-foreground", children: selectedLabel })
6451
6661
  ] });
6452
6662
  }
6453
- return /* @__PURE__ */ jsx22(
6663
+ return /* @__PURE__ */ jsx24(
6454
6664
  FormSelect2,
6455
6665
  {
6456
6666
  label: "Label",
@@ -6462,8 +6672,8 @@ function LabelField({
6462
6672
  );
6463
6673
  }
6464
6674
  function useCreateModeLabels(schemaProvider, open, isEdit) {
6465
- const [labels, setLabels] = useState14([]);
6466
- useEffect6(() => {
6675
+ const [labels, setLabels] = useState15([]);
6676
+ useEffect7(() => {
6467
6677
  let cancelled = false;
6468
6678
  if (!open || isEdit) return;
6469
6679
  void schemaProvider.listNodeLabels().then((ls) => {
@@ -6485,7 +6695,7 @@ function NodeForm({
6485
6695
  onSave,
6486
6696
  onCancel
6487
6697
  }) {
6488
- return /* @__PURE__ */ jsx22(
6698
+ return /* @__PURE__ */ jsx24(
6489
6699
  JsonSchemaFormRenderer2,
6490
6700
  {
6491
6701
  schema,
@@ -6519,13 +6729,13 @@ function NodeFormSheetBody({
6519
6729
  onSave,
6520
6730
  onCancel
6521
6731
  }) {
6522
- return /* @__PURE__ */ jsxs20(Fragment7, { children: [
6523
- /* @__PURE__ */ jsxs20(SheetHeader4, { children: [
6524
- /* @__PURE__ */ jsx22(SheetTitle4, { children: isEdit ? "Edit node" : "Add node" }),
6525
- /* @__PURE__ */ jsx22(SheetDescription4, { children: isEdit ? "Edit this node's properties and relations." : "Create a node and (optionally) its relations." })
6732
+ return /* @__PURE__ */ jsxs22(Fragment7, { children: [
6733
+ /* @__PURE__ */ jsxs22(SheetHeader4, { children: [
6734
+ /* @__PURE__ */ jsx24(SheetTitle4, { children: isEdit ? "Edit node" : "Add node" }),
6735
+ /* @__PURE__ */ jsx24(SheetDescription4, { children: isEdit ? "Edit this node's properties and relations." : "Create a node and (optionally) its relations." })
6526
6736
  ] }),
6527
- /* @__PURE__ */ jsxs20(SheetBody4, { className: "flex flex-col gap-4", children: [
6528
- /* @__PURE__ */ jsx22(
6737
+ /* @__PURE__ */ jsxs22(SheetBody4, { className: "flex flex-col gap-4", children: [
6738
+ /* @__PURE__ */ jsx24(
6529
6739
  LabelField,
6530
6740
  {
6531
6741
  isEdit,
@@ -6534,7 +6744,7 @@ function NodeFormSheetBody({
6534
6744
  onSelect: onSelectLabel
6535
6745
  }
6536
6746
  ),
6537
- schema && /* @__PURE__ */ jsx22(
6747
+ schema && /* @__PURE__ */ jsx24(
6538
6748
  NodeForm,
6539
6749
  {
6540
6750
  formKey,
@@ -6547,7 +6757,7 @@ function NodeFormSheetBody({
6547
6757
  onCancel
6548
6758
  }
6549
6759
  ),
6550
- !isEdit && /* @__PURE__ */ jsx22(
6760
+ !isEdit && /* @__PURE__ */ jsx24(
6551
6761
  FormCheckbox,
6552
6762
  {
6553
6763
  label: "Create another node",
@@ -6555,7 +6765,7 @@ function NodeFormSheetBody({
6555
6765
  onChange: (c) => onKeepOpenChange(c === true)
6556
6766
  }
6557
6767
  ),
6558
- error && /* @__PURE__ */ jsx22(Alert, { variant: "destructive", children: error })
6768
+ error && /* @__PURE__ */ jsx24(Alert2, { variant: "destructive", children: error })
6559
6769
  ] })
6560
6770
  ] });
6561
6771
  }
@@ -6569,14 +6779,14 @@ function NodeFormSheet({
6569
6779
  onSaved
6570
6780
  }) {
6571
6781
  const isEdit = !!node?.id_;
6572
- const [selectedLabel, setSelectedLabel] = useState14(
6782
+ const [selectedLabel, setSelectedLabel] = useState15(
6573
6783
  node?.label_ ?? null
6574
6784
  );
6575
- const [error, setError] = useState14("");
6576
- const [keepOpen, setKeepOpen] = useState14(false);
6577
- const [formNonce, setFormNonce] = useState14(0);
6785
+ const [error, setError] = useState15("");
6786
+ const [keepOpen, setKeepOpen] = useState15(false);
6787
+ const [formNonce, setFormNonce] = useState15(0);
6578
6788
  const labels = useCreateModeLabels(schemaProvider, open, isEdit);
6579
- useEffect6(() => {
6789
+ useEffect7(() => {
6580
6790
  if (open) {
6581
6791
  setSelectedLabel(node?.label_ ?? null);
6582
6792
  setError("");
@@ -6593,7 +6803,7 @@ function NodeFormSheet({
6593
6803
  const formData = useMemo9(() => existingData ?? {}, [existingData]);
6594
6804
  const stableOnSaved = useStableRef(onSaved);
6595
6805
  const stableOnOpenChange = useStableRef(onOpenChange);
6596
- const handleSave = useCallback8(
6806
+ const handleSave = useCallback9(
6597
6807
  (data) => {
6598
6808
  if (!selectedLabel) return;
6599
6809
  void (async () => {
@@ -6626,7 +6836,7 @@ function NodeFormSheet({
6626
6836
  stableOnOpenChange
6627
6837
  ]
6628
6838
  );
6629
- return /* @__PURE__ */ jsx22(Sheet4, { open, onOpenChange, children: /* @__PURE__ */ jsx22(SheetContent4, { children: /* @__PURE__ */ jsx22(
6839
+ return /* @__PURE__ */ jsx24(Sheet4, { open, onOpenChange, children: /* @__PURE__ */ jsx24(SheetContent4, { children: /* @__PURE__ */ jsx24(
6630
6840
  NodeFormSheetBody,
6631
6841
  {
6632
6842
  isEdit,
@@ -6683,7 +6893,7 @@ async function saveNode({
6683
6893
  }
6684
6894
 
6685
6895
  // src/graph-edit/EdgeFormSheet.tsx
6686
- import { useCallback as useCallback9, useEffect as useEffect7, useMemo as useMemo10, useState as useState15 } from "react";
6896
+ import { useCallback as useCallback10, useEffect as useEffect8, useMemo as useMemo10, useState as useState16 } from "react";
6687
6897
  import {
6688
6898
  Sheet as Sheet5,
6689
6899
  SheetContent as SheetContent5,
@@ -6697,20 +6907,20 @@ import {
6697
6907
  DialogTitle,
6698
6908
  DialogDescription,
6699
6909
  DialogFooter,
6700
- Button as Button7,
6910
+ Button as Button9,
6701
6911
  IconButton as IconButton3,
6702
- Alert as Alert2
6912
+ Alert as Alert3
6703
6913
  } from "@petrarca/sonnet-ui";
6704
6914
  import { Pencil as Pencil2, Plus as Plus2, Trash2 as Trash22, ArrowRight as ArrowRight2 } from "lucide-react";
6705
6915
  import {
6706
6916
  JsonSchemaFormRenderer as JsonSchemaFormRenderer3
6707
6917
  } from "@petrarca/sonnet-forms";
6708
6918
  import { errorLog as errorLog5 } from "@petrarca/sonnet-core";
6709
- import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
6919
+ import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
6710
6920
  function usePairState(port, schemaProvider, open, nodeAId, nodeALabel, nodeBId, nodeBLabel, reloadKey) {
6711
- const [active, setActive] = useState15([]);
6712
- const [available, setAvailable] = useState15([]);
6713
- useEffect7(() => {
6921
+ const [active, setActive] = useState16([]);
6922
+ const [available, setAvailable] = useState16([]);
6923
+ useEffect8(() => {
6714
6924
  let cancelled = false;
6715
6925
  if (!open || nodeAId == null || nodeBId == null) {
6716
6926
  setActive([]);
@@ -6775,9 +6985,9 @@ var EMPTY_EDGE_SCHEMA = {
6775
6985
  properties: {}
6776
6986
  };
6777
6987
  function useEdgeSchema(schemaProvider, draft) {
6778
- const [schema, setSchema] = useState15(null);
6988
+ const [schema, setSchema] = useState16(null);
6779
6989
  const label = useMemo10(() => draftLabel(draft), [draft]);
6780
- useEffect7(() => {
6990
+ useEffect8(() => {
6781
6991
  let cancelled = false;
6782
6992
  if (!label) {
6783
6993
  setSchema(null);
@@ -6797,12 +7007,12 @@ function useEdgeSchema(schemaProvider, draft) {
6797
7007
  return schema;
6798
7008
  }
6799
7009
  function useEditableEdgeTypes(schemaProvider, active) {
6800
- const [editable, setEditable] = useState15({});
7010
+ const [editable, setEditable] = useState16({});
6801
7011
  const labelsKey = useMemo10(
6802
7012
  () => Array.from(new Set(active.map((e) => e.label_))).sort().join("\0"),
6803
7013
  [active]
6804
7014
  );
6805
- useEffect7(() => {
7015
+ useEffect8(() => {
6806
7016
  let cancelled = false;
6807
7017
  const labels = labelsKey ? labelsKey.split("\0") : [];
6808
7018
  if (labels.length === 0) {
@@ -6831,7 +7041,7 @@ function useEditableEdgeTypes(schemaProvider, active) {
6831
7041
  function SectionTitle2({
6832
7042
  children
6833
7043
  }) {
6834
- return /* @__PURE__ */ jsx23("div", { className: "mb-2", children: /* @__PURE__ */ jsx23("span", { className: "heading-meta", children }) });
7044
+ return /* @__PURE__ */ jsx25("div", { className: "mb-2", children: /* @__PURE__ */ jsx25("span", { className: "heading-meta", children }) });
6835
7045
  }
6836
7046
  function EdgeRow({
6837
7047
  label,
@@ -6839,19 +7049,19 @@ function EdgeRow({
6839
7049
  onDelete,
6840
7050
  onAdd
6841
7051
  }) {
6842
- return /* @__PURE__ */ jsxs21("div", { className: "group/row flex items-center gap-2 px-3 py-2", children: [
6843
- /* @__PURE__ */ jsx23("span", { className: "flex-1 text-sm font-medium", children: label }),
6844
- /* @__PURE__ */ jsxs21("span", { className: "flex gap-1", children: [
6845
- onAdd && /* @__PURE__ */ jsx23(IconButton3, { label: "Create edge", revealOnHover: true, onClick: onAdd, children: /* @__PURE__ */ jsx23(Plus2, {}) }),
6846
- onEdit && /* @__PURE__ */ jsx23(IconButton3, { label: "Edit edge", revealOnHover: true, onClick: onEdit, children: /* @__PURE__ */ jsx23(Pencil2, {}) }),
6847
- onDelete && /* @__PURE__ */ jsx23(
7052
+ return /* @__PURE__ */ jsxs23("div", { className: "group/row flex items-center gap-2 px-3 py-2", children: [
7053
+ /* @__PURE__ */ jsx25("span", { className: "flex-1 text-sm font-medium", children: label }),
7054
+ /* @__PURE__ */ jsxs23("span", { className: "flex gap-1", children: [
7055
+ onAdd && /* @__PURE__ */ jsx25(IconButton3, { label: "Create edge", revealOnHover: true, onClick: onAdd, children: /* @__PURE__ */ jsx25(Plus2, {}) }),
7056
+ onEdit && /* @__PURE__ */ jsx25(IconButton3, { label: "Edit edge", revealOnHover: true, onClick: onEdit, children: /* @__PURE__ */ jsx25(Pencil2, {}) }),
7057
+ onDelete && /* @__PURE__ */ jsx25(
6848
7058
  IconButton3,
6849
7059
  {
6850
7060
  label: "Delete edge",
6851
7061
  tone: "destructive",
6852
7062
  revealOnHover: true,
6853
7063
  onClick: onDelete,
6854
- children: /* @__PURE__ */ jsx23(Trash22, {})
7064
+ children: /* @__PURE__ */ jsx25(Trash22, {})
6855
7065
  }
6856
7066
  )
6857
7067
  ] })
@@ -6879,17 +7089,17 @@ function PairLists({
6879
7089
  const to = entry.sourceId === nodeAId ? nodeBLabel : nodeALabel;
6880
7090
  return `${entry.label} (${from} -> ${to})`;
6881
7091
  };
6882
- return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-4", children: [
6883
- empty && /* @__PURE__ */ jsxs21("p", { className: "text-xs text-muted-foreground", children: [
7092
+ return /* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-4", children: [
7093
+ empty && /* @__PURE__ */ jsxs23("p", { className: "text-xs text-muted-foreground", children: [
6884
7094
  "No edges are defined between ",
6885
7095
  nodeALabel,
6886
7096
  " and ",
6887
7097
  nodeBLabel,
6888
7098
  "."
6889
7099
  ] }),
6890
- active.length > 0 && /* @__PURE__ */ jsxs21("div", { children: [
6891
- /* @__PURE__ */ jsx23(SectionTitle2, { children: "Active edges" }),
6892
- /* @__PURE__ */ jsx23("div", { className: "rounded-lg border divide-y", children: active.map((edge) => /* @__PURE__ */ jsx23(
7100
+ active.length > 0 && /* @__PURE__ */ jsxs23("div", { children: [
7101
+ /* @__PURE__ */ jsx25(SectionTitle2, { children: "Active edges" }),
7102
+ /* @__PURE__ */ jsx25("div", { className: "rounded-lg border divide-y", children: active.map((edge) => /* @__PURE__ */ jsx25(
6893
7103
  EdgeRow,
6894
7104
  {
6895
7105
  label: edge.label_,
@@ -6899,9 +7109,9 @@ function PairLists({
6899
7109
  edge.id_
6900
7110
  )) })
6901
7111
  ] }),
6902
- available.length > 0 && /* @__PURE__ */ jsxs21("div", { children: [
6903
- /* @__PURE__ */ jsx23(SectionTitle2, { children: "Available edges" }),
6904
- /* @__PURE__ */ jsx23("div", { className: "rounded-lg border divide-y", children: available.map((entry) => /* @__PURE__ */ jsx23(
7112
+ available.length > 0 && /* @__PURE__ */ jsxs23("div", { children: [
7113
+ /* @__PURE__ */ jsx25(SectionTitle2, { children: "Available edges" }),
7114
+ /* @__PURE__ */ jsx25("div", { className: "rounded-lg border divide-y", children: available.map((entry) => /* @__PURE__ */ jsx25(
6905
7115
  EdgeRow,
6906
7116
  {
6907
7117
  label: rowLabel(entry),
@@ -6921,13 +7131,13 @@ function DraftForm({
6921
7131
  }) {
6922
7132
  const label = draft.mode === "create" ? draft.entry.label : draft.edge.label_;
6923
7133
  const data = draft.mode === "edit" ? draft.edge.properties_ ?? {} : {};
6924
- return /* @__PURE__ */ jsxs21("div", { children: [
6925
- /* @__PURE__ */ jsxs21(SectionTitle2, { children: [
7134
+ return /* @__PURE__ */ jsxs23("div", { children: [
7135
+ /* @__PURE__ */ jsxs23(SectionTitle2, { children: [
6926
7136
  draft.mode === "create" ? "Add" : "Edit",
6927
7137
  " ",
6928
7138
  label
6929
7139
  ] }),
6930
- /* @__PURE__ */ jsx23(
7140
+ /* @__PURE__ */ jsx25(
6931
7141
  JsonSchemaFormRenderer3,
6932
7142
  {
6933
7143
  schema,
@@ -6955,10 +7165,10 @@ function EdgeFormSheet({
6955
7165
  widgets,
6956
7166
  onChanged
6957
7167
  }) {
6958
- const [reloadKey, setReloadKey] = useState15(0);
6959
- const [draft, setDraft] = useState15(null);
6960
- const [error, setError] = useState15("");
6961
- const [pendingDelete, setPendingDelete] = useState15(null);
7168
+ const [reloadKey, setReloadKey] = useState16(0);
7169
+ const [draft, setDraft] = useState16(null);
7170
+ const [error, setError] = useState16("");
7171
+ const [pendingDelete, setPendingDelete] = useState16(null);
6962
7172
  const { active, available } = usePairState(
6963
7173
  port,
6964
7174
  schemaProvider,
@@ -6973,18 +7183,18 @@ function EdgeFormSheet({
6973
7183
  const editableTypes = useEditableEdgeTypes(schemaProvider, active);
6974
7184
  const nodeAId = nodeA.id_;
6975
7185
  const nodeBId = nodeB.id_;
6976
- useEffect7(() => {
7186
+ useEffect8(() => {
6977
7187
  if (open) {
6978
7188
  setDraft(null);
6979
7189
  setError("");
6980
7190
  setPendingDelete(null);
6981
7191
  }
6982
7192
  }, [open, nodeAId, nodeBId]);
6983
- const reload = useCallback9(() => {
7193
+ const reload = useCallback10(() => {
6984
7194
  setReloadKey((k) => k + 1);
6985
7195
  onChanged?.();
6986
7196
  }, [onChanged]);
6987
- const handleSave = useCallback9(
7197
+ const handleSave = useCallback10(
6988
7198
  (formData) => {
6989
7199
  if (!draft) return;
6990
7200
  const { properties } = extractRelationFields(formData);
@@ -7016,7 +7226,7 @@ function EdgeFormSheet({
7016
7226
  },
7017
7227
  [draft, port, reload]
7018
7228
  );
7019
- const confirmDelete = useCallback9(async () => {
7229
+ const confirmDelete = useCallback10(async () => {
7020
7230
  const edge = pendingDelete;
7021
7231
  if (!edge || edge.id_ == null) return;
7022
7232
  try {
@@ -7029,19 +7239,19 @@ function EdgeFormSheet({
7029
7239
  setError(e instanceof Error ? e.message : "Failed to delete edge");
7030
7240
  }
7031
7241
  }, [pendingDelete, port, reload]);
7032
- return /* @__PURE__ */ jsxs21(Sheet5, { open, onOpenChange, children: [
7033
- /* @__PURE__ */ jsxs21(SheetContent5, { children: [
7034
- /* @__PURE__ */ jsxs21(SheetHeader5, { children: [
7035
- /* @__PURE__ */ jsx23(SheetTitle5, { children: "Edges" }),
7036
- /* @__PURE__ */ jsx23(SheetDescription5, { children: "Manage the edges between the two selected nodes." })
7242
+ return /* @__PURE__ */ jsxs23(Sheet5, { open, onOpenChange, children: [
7243
+ /* @__PURE__ */ jsxs23(SheetContent5, { children: [
7244
+ /* @__PURE__ */ jsxs23(SheetHeader5, { children: [
7245
+ /* @__PURE__ */ jsx25(SheetTitle5, { children: "Edges" }),
7246
+ /* @__PURE__ */ jsx25(SheetDescription5, { children: "Manage the edges between the two selected nodes." })
7037
7247
  ] }),
7038
- /* @__PURE__ */ jsxs21(SheetBody5, { className: "flex flex-col gap-4", children: [
7039
- /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2 text-sm", children: [
7040
- /* @__PURE__ */ jsx23("span", { className: "font-medium", children: extractDisplayLabel(nodeA) }),
7041
- /* @__PURE__ */ jsx23(ArrowRight2, { className: "h-4 w-4 text-muted-foreground" }),
7042
- /* @__PURE__ */ jsx23("span", { className: "font-medium", children: extractDisplayLabel(nodeB) })
7248
+ /* @__PURE__ */ jsxs23(SheetBody5, { className: "flex flex-col gap-4", children: [
7249
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2 text-sm", children: [
7250
+ /* @__PURE__ */ jsx25("span", { className: "font-medium", children: extractDisplayLabel(nodeA) }),
7251
+ /* @__PURE__ */ jsx25(ArrowRight2, { className: "h-4 w-4 text-muted-foreground" }),
7252
+ /* @__PURE__ */ jsx25("span", { className: "font-medium", children: extractDisplayLabel(nodeB) })
7043
7253
  ] }),
7044
- !draft && /* @__PURE__ */ jsx23(
7254
+ !draft && /* @__PURE__ */ jsx25(
7045
7255
  PairLists,
7046
7256
  {
7047
7257
  nodeALabel: nodeA.label_,
@@ -7055,7 +7265,7 @@ function EdgeFormSheet({
7055
7265
  onCreate: (entry) => setDraft({ mode: "create", entry })
7056
7266
  }
7057
7267
  ),
7058
- draft && schema && /* @__PURE__ */ jsx23(
7268
+ draft && schema && /* @__PURE__ */ jsx25(
7059
7269
  DraftForm,
7060
7270
  {
7061
7271
  draft,
@@ -7065,10 +7275,10 @@ function EdgeFormSheet({
7065
7275
  onCancel: () => setDraft(null)
7066
7276
  }
7067
7277
  ),
7068
- error && /* @__PURE__ */ jsx23(Alert2, { variant: "destructive", children: error })
7278
+ error && /* @__PURE__ */ jsx25(Alert3, { variant: "destructive", children: error })
7069
7279
  ] })
7070
7280
  ] }),
7071
- /* @__PURE__ */ jsx23(
7281
+ /* @__PURE__ */ jsx25(
7072
7282
  DeleteEdgeConfirm,
7073
7283
  {
7074
7284
  edge: pendingDelete,
@@ -7083,24 +7293,24 @@ function DeleteEdgeConfirm({
7083
7293
  onCancel,
7084
7294
  onConfirm
7085
7295
  }) {
7086
- return /* @__PURE__ */ jsx23(Dialog, { open: !!edge, onOpenChange: (o) => !o && onCancel(), children: /* @__PURE__ */ jsxs21(DialogContent, { children: [
7087
- /* @__PURE__ */ jsxs21(DialogHeader, { children: [
7088
- /* @__PURE__ */ jsx23(DialogTitle, { children: "Delete edge" }),
7089
- /* @__PURE__ */ jsxs21(DialogDescription, { children: [
7296
+ return /* @__PURE__ */ jsx25(Dialog, { open: !!edge, onOpenChange: (o) => !o && onCancel(), children: /* @__PURE__ */ jsxs23(DialogContent, { children: [
7297
+ /* @__PURE__ */ jsxs23(DialogHeader, { children: [
7298
+ /* @__PURE__ */ jsx25(DialogTitle, { children: "Delete edge" }),
7299
+ /* @__PURE__ */ jsxs23(DialogDescription, { children: [
7090
7300
  "Delete the edge ",
7091
- /* @__PURE__ */ jsx23("strong", { children: edge?.label_ }),
7301
+ /* @__PURE__ */ jsx25("strong", { children: edge?.label_ }),
7092
7302
  "? This removes the relationship between the two nodes."
7093
7303
  ] })
7094
7304
  ] }),
7095
- /* @__PURE__ */ jsxs21(DialogFooter, { children: [
7096
- /* @__PURE__ */ jsx23(Button7, { variant: "outline", onClick: onCancel, children: "Cancel" }),
7097
- /* @__PURE__ */ jsx23(Button7, { variant: "destructive", onClick: onConfirm, children: "Delete" })
7305
+ /* @__PURE__ */ jsxs23(DialogFooter, { children: [
7306
+ /* @__PURE__ */ jsx25(Button9, { variant: "outline", onClick: onCancel, children: "Cancel" }),
7307
+ /* @__PURE__ */ jsx25(Button9, { variant: "destructive", onClick: onConfirm, children: "Delete" })
7098
7308
  ] })
7099
7309
  ] }) });
7100
7310
  }
7101
7311
 
7102
7312
  // src/graph-edit/DeleteNodeDialog.tsx
7103
- import { useCallback as useCallback10, useEffect as useEffect8, useState as useState16 } from "react";
7313
+ import { useCallback as useCallback11, useEffect as useEffect9, useState as useState17 } from "react";
7104
7314
  import {
7105
7315
  Dialog as Dialog2,
7106
7316
  DialogContent as DialogContent2,
@@ -7108,19 +7318,19 @@ import {
7108
7318
  DialogTitle as DialogTitle2,
7109
7319
  DialogDescription as DialogDescription2,
7110
7320
  DialogFooter as DialogFooter2,
7111
- Button as Button8,
7112
- Alert as Alert3
7321
+ Button as Button10,
7322
+ Alert as Alert4
7113
7323
  } from "@petrarca/sonnet-ui";
7114
7324
  import { errorLog as errorLog6 } from "@petrarca/sonnet-core";
7115
- import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
7325
+ import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
7116
7326
  var EMPTY_DEPENDENT = {
7117
7327
  inbound: [],
7118
7328
  outbound: [],
7119
7329
  names: {}
7120
7330
  };
7121
7331
  function useDependentEdges(port, open, node) {
7122
- const [dependent, setDependent] = useState16(EMPTY_DEPENDENT);
7123
- useEffect8(() => {
7332
+ const [dependent, setDependent] = useState17(EMPTY_DEPENDENT);
7333
+ useEffect9(() => {
7124
7334
  if (!open) return;
7125
7335
  if (node.id_ == null) {
7126
7336
  setDependent(EMPTY_DEPENDENT);
@@ -7170,12 +7380,12 @@ function EdgeList({
7170
7380
  direction
7171
7381
  }) {
7172
7382
  if (edges.length === 0) return null;
7173
- return /* @__PURE__ */ jsxs22("div", { className: "mb-2", children: [
7174
- /* @__PURE__ */ jsx24("span", { className: "heading-meta block", children: title }),
7175
- /* @__PURE__ */ jsx24("ul", { className: "list-disc pl-6 text-sm", children: edges.map((edge) => {
7383
+ return /* @__PURE__ */ jsxs24("div", { className: "mb-2", children: [
7384
+ /* @__PURE__ */ jsx26("span", { className: "heading-meta block", children: title }),
7385
+ /* @__PURE__ */ jsx26("ul", { className: "list-disc pl-6 text-sm", children: edges.map((edge) => {
7176
7386
  const neighbour = direction === "inbound" ? names[edge.start_id_] : names[edge.end_id_];
7177
7387
  const text = direction === "inbound" ? `${neighbour ?? "?"} \u2192 ${edge.label_}` : `${edge.label_} \u2192 ${neighbour ?? "?"}`;
7178
- return /* @__PURE__ */ jsx24("li", { children: neighbour ? text : edge.label_ }, edge.id_);
7388
+ return /* @__PURE__ */ jsx26("li", { children: neighbour ? text : edge.label_ }, edge.id_);
7179
7389
  }) })
7180
7390
  ] });
7181
7391
  }
@@ -7187,9 +7397,9 @@ function DeleteNodeDialog({
7187
7397
  onDeleted
7188
7398
  }) {
7189
7399
  const { inbound, outbound, names } = useDependentEdges(port, open, node);
7190
- const [error, setError] = useState16("");
7400
+ const [error, setError] = useState17("");
7191
7401
  const hasEdges = inbound.length > 0 || outbound.length > 0;
7192
- const handleDelete = useCallback10(() => {
7402
+ const handleDelete = useCallback11(() => {
7193
7403
  if (node.id_ == null) return;
7194
7404
  void (async () => {
7195
7405
  try {
@@ -7202,17 +7412,17 @@ function DeleteNodeDialog({
7202
7412
  }
7203
7413
  })();
7204
7414
  }, [port, node.id_, onDeleted, onOpenChange]);
7205
- return /* @__PURE__ */ jsx24(Dialog2, { open, onOpenChange, children: /* @__PURE__ */ jsxs22(DialogContent2, { children: [
7206
- /* @__PURE__ */ jsxs22(DialogHeader2, { children: [
7207
- /* @__PURE__ */ jsx24(DialogTitle2, { children: "Delete node" }),
7208
- /* @__PURE__ */ jsxs22(DialogDescription2, { children: [
7415
+ return /* @__PURE__ */ jsx26(Dialog2, { open, onOpenChange, children: /* @__PURE__ */ jsxs24(DialogContent2, { children: [
7416
+ /* @__PURE__ */ jsxs24(DialogHeader2, { children: [
7417
+ /* @__PURE__ */ jsx26(DialogTitle2, { children: "Delete node" }),
7418
+ /* @__PURE__ */ jsxs24(DialogDescription2, { children: [
7209
7419
  "Delete the node ",
7210
- /* @__PURE__ */ jsx24("strong", { children: extractDisplayLabel(node) }),
7420
+ /* @__PURE__ */ jsx26("strong", { children: extractDisplayLabel(node) }),
7211
7421
  "?"
7212
7422
  ] })
7213
7423
  ] }),
7214
- !hasEdges && /* @__PURE__ */ jsx24("p", { className: "text-sm text-muted-foreground", children: "No connected edges." }),
7215
- /* @__PURE__ */ jsx24(
7424
+ !hasEdges && /* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "No connected edges." }),
7425
+ /* @__PURE__ */ jsx26(
7216
7426
  EdgeList,
7217
7427
  {
7218
7428
  title: "Inbound edges",
@@ -7221,7 +7431,7 @@ function DeleteNodeDialog({
7221
7431
  direction: "inbound"
7222
7432
  }
7223
7433
  ),
7224
- /* @__PURE__ */ jsx24(
7434
+ /* @__PURE__ */ jsx26(
7225
7435
  EdgeList,
7226
7436
  {
7227
7437
  title: "Outbound edges",
@@ -7230,16 +7440,16 @@ function DeleteNodeDialog({
7230
7440
  direction: "outbound"
7231
7441
  }
7232
7442
  ),
7233
- error && /* @__PURE__ */ jsx24(Alert3, { variant: "destructive", children: error }),
7234
- /* @__PURE__ */ jsxs22(DialogFooter2, { children: [
7235
- /* @__PURE__ */ jsx24(Button8, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
7236
- /* @__PURE__ */ jsx24(Button8, { variant: "destructive", onClick: handleDelete, children: "Delete" })
7443
+ error && /* @__PURE__ */ jsx26(Alert4, { variant: "destructive", children: error }),
7444
+ /* @__PURE__ */ jsxs24(DialogFooter2, { children: [
7445
+ /* @__PURE__ */ jsx26(Button10, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
7446
+ /* @__PURE__ */ jsx26(Button10, { variant: "destructive", onClick: handleDelete, children: "Delete" })
7237
7447
  ] })
7238
7448
  ] }) });
7239
7449
  }
7240
7450
 
7241
7451
  // src/graph-edit/GraphEditToolbar.tsx
7242
- import { Fragment as Fragment8, jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
7452
+ import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
7243
7453
  function toPerms(caps) {
7244
7454
  const canCreateNode = caps.createNode === "edit";
7245
7455
  const canUpdateNode = caps.updateNode === "edit";
@@ -7258,38 +7468,38 @@ function ToolbarButtons({
7258
7468
  count,
7259
7469
  onOpen
7260
7470
  }) {
7261
- return /* @__PURE__ */ jsxs23("div", { className: "flex gap-0.5", children: [
7262
- perms.canCreateNode && /* @__PURE__ */ jsx25(
7471
+ return /* @__PURE__ */ jsxs25("div", { className: "flex gap-0.5", children: [
7472
+ perms.canCreateNode && /* @__PURE__ */ jsx27(
7263
7473
  IconButton4,
7264
7474
  {
7265
7475
  label: "Add node",
7266
7476
  variant: "outline",
7267
7477
  disabled: count > 0,
7268
7478
  onClick: () => onOpen("node-create"),
7269
- children: /* @__PURE__ */ jsx25(Plus3, {})
7479
+ children: /* @__PURE__ */ jsx27(Plus3, {})
7270
7480
  }
7271
7481
  ),
7272
- perms.canUpdateNode && /* @__PURE__ */ jsx25(
7482
+ perms.canUpdateNode && /* @__PURE__ */ jsx27(
7273
7483
  IconButton4,
7274
7484
  {
7275
7485
  label: "Edit node",
7276
7486
  variant: "outline",
7277
7487
  disabled: count !== 1,
7278
7488
  onClick: () => onOpen("node-edit"),
7279
- children: /* @__PURE__ */ jsx25(Edit, {})
7489
+ children: /* @__PURE__ */ jsx27(Edit, {})
7280
7490
  }
7281
7491
  ),
7282
- perms.canEditEdges && /* @__PURE__ */ jsx25(
7492
+ perms.canEditEdges && /* @__PURE__ */ jsx27(
7283
7493
  IconButton4,
7284
7494
  {
7285
7495
  label: "Edges",
7286
7496
  variant: "outline",
7287
7497
  disabled: count !== 2,
7288
7498
  onClick: () => onOpen("edge"),
7289
- children: /* @__PURE__ */ jsx25(Link, {})
7499
+ children: /* @__PURE__ */ jsx27(Link, {})
7290
7500
  }
7291
7501
  ),
7292
- perms.canDeleteNode && /* @__PURE__ */ jsx25(
7502
+ perms.canDeleteNode && /* @__PURE__ */ jsx27(
7293
7503
  IconButton4,
7294
7504
  {
7295
7505
  label: "Delete node",
@@ -7297,7 +7507,7 @@ function ToolbarButtons({
7297
7507
  tone: "destructive",
7298
7508
  disabled: count !== 1,
7299
7509
  onClick: () => onOpen("delete"),
7300
- children: /* @__PURE__ */ jsx25(Trash23, {})
7510
+ children: /* @__PURE__ */ jsx27(Trash23, {})
7301
7511
  }
7302
7512
  )
7303
7513
  ] });
@@ -7313,14 +7523,14 @@ function ToolbarDialogs({
7313
7523
  onClose,
7314
7524
  onMutated
7315
7525
  }) {
7316
- const onChange = useCallback11(
7526
+ const onChange = useCallback12(
7317
7527
  (o) => {
7318
7528
  if (!o) onClose();
7319
7529
  },
7320
7530
  [onClose]
7321
7531
  );
7322
- return /* @__PURE__ */ jsxs23(Fragment8, { children: [
7323
- (perms.canCreateNode || perms.canUpdateNode) && /* @__PURE__ */ jsx25(
7532
+ return /* @__PURE__ */ jsxs25(Fragment8, { children: [
7533
+ (perms.canCreateNode || perms.canUpdateNode) && /* @__PURE__ */ jsx27(
7324
7534
  NodeFormSheet,
7325
7535
  {
7326
7536
  open: dialog === "node-create" || dialog === "node-edit",
@@ -7332,7 +7542,7 @@ function ToolbarDialogs({
7332
7542
  onSaved: onMutated
7333
7543
  }
7334
7544
  ),
7335
- perms.canEditEdges && firstNode && secondNode && /* @__PURE__ */ jsx25(
7545
+ perms.canEditEdges && firstNode && secondNode && /* @__PURE__ */ jsx27(
7336
7546
  EdgeFormSheet,
7337
7547
  {
7338
7548
  open: dialog === "edge",
@@ -7345,7 +7555,7 @@ function ToolbarDialogs({
7345
7555
  onChanged: onMutated
7346
7556
  }
7347
7557
  ),
7348
- perms.canDeleteNode && firstNode && /* @__PURE__ */ jsx25(
7558
+ perms.canDeleteNode && firstNode && /* @__PURE__ */ jsx27(
7349
7559
  DeleteNodeDialog,
7350
7560
  {
7351
7561
  open: dialog === "delete",
@@ -7370,7 +7580,7 @@ function GraphEditToolbar({
7370
7580
  (s) => s.selectedNodeIds
7371
7581
  );
7372
7582
  const live = useLiveGraphData();
7373
- const [dialog, setDialog] = useState17(null);
7583
+ const [dialog, setDialog] = useState18(null);
7374
7584
  const perms = useMemo11(
7375
7585
  () => toPerms(resolveGraphCapabilities(capabilities, readOnly)),
7376
7586
  [capabilities, readOnly]
@@ -7381,8 +7591,8 @@ function GraphEditToolbar({
7381
7591
  );
7382
7592
  const stableOnMutated = useStableRef(onMutated);
7383
7593
  if (!perms.anyEditable) return null;
7384
- return /* @__PURE__ */ jsxs23(Fragment8, { children: [
7385
- /* @__PURE__ */ jsx25(
7594
+ return /* @__PURE__ */ jsxs25(Fragment8, { children: [
7595
+ /* @__PURE__ */ jsx27(
7386
7596
  ToolbarButtons,
7387
7597
  {
7388
7598
  perms,
@@ -7390,7 +7600,7 @@ function GraphEditToolbar({
7390
7600
  onOpen: setDialog
7391
7601
  }
7392
7602
  ),
7393
- /* @__PURE__ */ jsx25(
7603
+ /* @__PURE__ */ jsx27(
7394
7604
  ToolbarDialogs,
7395
7605
  {
7396
7606
  perms,
@@ -7406,6 +7616,662 @@ function GraphEditToolbar({
7406
7616
  )
7407
7617
  ] });
7408
7618
  }
7619
+
7620
+ // src/finder/GraphFinder.tsx
7621
+ import { FormSelect as FormSelect3, FormTextarea } from "@petrarca/sonnet-forms";
7622
+ import {
7623
+ Alert as Alert5,
7624
+ AlertDescription,
7625
+ AlertTitle,
7626
+ Button as Button11,
7627
+ SimpleTooltip as SimpleTooltip5
7628
+ } from "@petrarca/sonnet-ui";
7629
+ import { Search } from "lucide-react";
7630
+
7631
+ // src/finder/useFinderState.ts
7632
+ import { useState as useState20, useEffect as useEffect11 } from "react";
7633
+ import mistql from "mistql";
7634
+
7635
+ // src/finder/flattenGraphData.ts
7636
+ function flattenGraphData(normalized) {
7637
+ const flattenedNodes = normalized.nodeIds.map((id) => {
7638
+ const node = normalized.nodesById[id];
7639
+ return {
7640
+ ...node.properties_,
7641
+ id_: node.id_,
7642
+ label_: node.label_,
7643
+ displayName: node.visual.displayName,
7644
+ color: node.visual.color
7645
+ };
7646
+ });
7647
+ const flattenedEdges = normalized.edgeIds.map((id) => {
7648
+ const edge = normalized.edgesById[id];
7649
+ return {
7650
+ ...edge.properties_ || {},
7651
+ id_: edge.id_,
7652
+ label_: edge.label_,
7653
+ start_id_: edge.start_id_,
7654
+ end_id_: edge.end_id_
7655
+ };
7656
+ });
7657
+ const nodesByLabel = groupByLabel(flattenedNodes);
7658
+ const edgesByLabel = groupByLabel(flattenedEdges);
7659
+ return {
7660
+ nodes: flattenedNodes,
7661
+ edges: flattenedEdges,
7662
+ graph: [...flattenedNodes, ...flattenedEdges],
7663
+ _labels: Object.keys(nodesByLabel),
7664
+ _edgeLabels: Object.keys(edgesByLabel),
7665
+ ...nodesByLabel,
7666
+ ...edgesByLabel
7667
+ };
7668
+ }
7669
+ function groupByLabel(items) {
7670
+ const byLabel = {};
7671
+ for (const item of items) {
7672
+ (byLabel[item.label_] ??= []).push(item);
7673
+ }
7674
+ return byLabel;
7675
+ }
7676
+
7677
+ // src/finder/useGraphSearch.ts
7678
+ import { useRef as useRef9, useState as useState19, useEffect as useEffect10 } from "react";
7679
+ import { errorLog as errorLog7 } from "@petrarca/sonnet-core";
7680
+
7681
+ // src/finder/OramaSearchService.ts
7682
+ import { create as create8, insert, search } from "@orama/orama";
7683
+
7684
+ // src/finder/oramaSchemaBuilder.ts
7685
+ function buildOramaSchema() {
7686
+ return {
7687
+ id_: "string",
7688
+ type: "string",
7689
+ label_: "string",
7690
+ displayName: "string",
7691
+ searchableText: "string",
7692
+ properties_json: "string",
7693
+ isVisible: "boolean",
7694
+ start_id_: "string",
7695
+ end_id_: "string"
7696
+ };
7697
+ }
7698
+ function getDefaultFieldBoosting() {
7699
+ return {
7700
+ displayName: 2,
7701
+ searchableText: 1.5,
7702
+ properties_json: 1
7703
+ };
7704
+ }
7705
+
7706
+ // src/finder/oramaTransformers.ts
7707
+ function transformNodeToOramaDoc(node, normalized) {
7708
+ return {
7709
+ id_: String(node.id_),
7710
+ type: "node",
7711
+ label_: node.label_,
7712
+ displayName: node.visual.displayName || node.label_,
7713
+ searchableText: buildSearchableText(node),
7714
+ properties_json: JSON.stringify(node.properties_ || {}),
7715
+ isVisible: normalized ? !normalized.hiddenNodeIds.has(String(node.id_)) : true
7716
+ };
7717
+ }
7718
+ function transformEdgeToOramaDoc(edge, normalized) {
7719
+ return {
7720
+ id_: String(edge.id_),
7721
+ type: "edge",
7722
+ label_: edge.label_,
7723
+ displayName: edge.label_,
7724
+ searchableText: buildSearchableTextEdge(edge),
7725
+ properties_json: JSON.stringify(edge.properties_ || {}),
7726
+ isVisible: normalized ? !normalized.hiddenEdgeIds.has(String(edge.id_)) : true,
7727
+ start_id_: String(edge.start_id_),
7728
+ end_id_: String(edge.end_id_)
7729
+ };
7730
+ }
7731
+ function buildSearchableText(node) {
7732
+ const parts = [node.label_, node.visual.displayName || ""];
7733
+ appendPropertyValues(parts, node.properties_);
7734
+ return parts.filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
7735
+ }
7736
+ function buildSearchableTextEdge(edge) {
7737
+ const parts = [edge.label_];
7738
+ appendPropertyValues(parts, edge.properties_);
7739
+ return parts.filter(Boolean).join(" ").replace(/\s+/g, " ").trim();
7740
+ }
7741
+ function appendPropertyValues(parts, properties) {
7742
+ if (!properties) return;
7743
+ for (const value of Object.values(properties)) {
7744
+ if (value == null) continue;
7745
+ if (Array.isArray(value)) {
7746
+ parts.push(...value.map(String));
7747
+ } else if (typeof value === "object") {
7748
+ parts.push(JSON.stringify(value));
7749
+ } else {
7750
+ parts.push(String(value));
7751
+ }
7752
+ }
7753
+ }
7754
+
7755
+ // src/finder/OramaSearchService.ts
7756
+ function buildWhereClause(options) {
7757
+ const where = {};
7758
+ const includeNodes = options.includeNodes ?? true;
7759
+ const includeEdges = options.includeEdges ?? true;
7760
+ if (includeNodes !== includeEdges) {
7761
+ where.type = includeNodes ? "node" : "edge";
7762
+ }
7763
+ const labels = options.labels;
7764
+ if (labels?.length) {
7765
+ where.label_ = labels.length === 1 ? labels[0] : { in: labels };
7766
+ }
7767
+ if (options.visibleOnly) {
7768
+ where.isVisible = true;
7769
+ }
7770
+ return where;
7771
+ }
7772
+ var OramaSearchService = class {
7773
+ db = null;
7774
+ config;
7775
+ isDirty = false;
7776
+ isInitialized = false;
7777
+ lastIndexBuild = null;
7778
+ stats = { totalIndexed: 0, nodesIndexed: 0, edgesIndexed: 0 };
7779
+ constructor(config = {}) {
7780
+ this.config = {
7781
+ debug: config.debug ?? false,
7782
+ maxNodes: config.maxNodes ?? 1e5,
7783
+ lazyInit: config.lazyInit ?? true
7784
+ };
7785
+ }
7786
+ // ---- lifecycle ----------------------------------------------------------
7787
+ /** Create the empty index with the finder schema (idempotent). */
7788
+ async initialize() {
7789
+ if (this.isInitialized) return;
7790
+ this.db = await create8({
7791
+ schema: buildOramaSchema(),
7792
+ components: {
7793
+ tokenizer: {
7794
+ stemming: true,
7795
+ stopWords: ["the", "a", "an", "and", "or", "but"]
7796
+ }
7797
+ }
7798
+ });
7799
+ this.isInitialized = true;
7800
+ this.isDirty = false;
7801
+ }
7802
+ /** Drop all documents (recreates the index; Orama has no bulk delete). */
7803
+ async clear() {
7804
+ if (!this.db) return;
7805
+ this.isInitialized = false;
7806
+ await this.initialize();
7807
+ this.stats = { totalIndexed: 0, nodesIndexed: 0, edgesIndexed: 0 };
7808
+ this.lastIndexBuild = null;
7809
+ this.isDirty = false;
7810
+ }
7811
+ /** Mark the index stale so the next search rebuilds it. */
7812
+ markDirty() {
7813
+ this.isDirty = true;
7814
+ }
7815
+ /** Whether the index needs a (re)build before searching. */
7816
+ needsRebuild() {
7817
+ return this.isDirty || !this.isInitialized;
7818
+ }
7819
+ // ---- indexing -----------------------------------------------------------
7820
+ /** Rebuild the whole index from the given nodes and edges. */
7821
+ async rebuildIndex(nodes, edges, normalized) {
7822
+ if (!this.isInitialized) await this.initialize();
7823
+ await this.clear();
7824
+ if (!this.db) throw new Error("Finder index not initialized");
7825
+ let indexedNodes = nodes;
7826
+ if (nodes.length > this.config.maxNodes) {
7827
+ if (this.config.debug) {
7828
+ console.warn(
7829
+ `[GraphFinder] node count (${nodes.length}) exceeds maxNodes (${this.config.maxNodes}); truncating`
7830
+ );
7831
+ }
7832
+ indexedNodes = nodes.slice(0, this.config.maxNodes);
7833
+ }
7834
+ for (const node of indexedNodes) {
7835
+ await insert(this.db, transformNodeToOramaDoc(node, normalized));
7836
+ }
7837
+ for (const edge of edges) {
7838
+ await insert(this.db, transformEdgeToOramaDoc(edge, normalized));
7839
+ }
7840
+ this.stats = {
7841
+ totalIndexed: indexedNodes.length + edges.length,
7842
+ nodesIndexed: indexedNodes.length,
7843
+ edgesIndexed: edges.length
7844
+ };
7845
+ this.lastIndexBuild = Date.now();
7846
+ this.isDirty = false;
7847
+ }
7848
+ // ---- search -------------------------------------------------------------
7849
+ /** Run a full-text query against the index. */
7850
+ async search(options) {
7851
+ if (!this.db || !this.isInitialized || this.isDirty) return [];
7852
+ const { term, limit = 50, boost } = options;
7853
+ const where = buildWhereClause(options);
7854
+ const results = await search(this.db, {
7855
+ term,
7856
+ where: Object.keys(where).length > 0 ? where : void 0,
7857
+ limit,
7858
+ boost: boost || getDefaultFieldBoosting(),
7859
+ tolerance: 1
7860
+ // allow a 1-character typo
7861
+ });
7862
+ return results.hits.map((hit) => ({
7863
+ document: hit.document,
7864
+ score: hit.score
7865
+ }));
7866
+ }
7867
+ // ---- status -------------------------------------------------------------
7868
+ /** Current index statistics. */
7869
+ getStats() {
7870
+ return {
7871
+ ...this.stats,
7872
+ lastIndexBuild: this.lastIndexBuild,
7873
+ isReady: this.isInitialized && !this.isDirty
7874
+ };
7875
+ }
7876
+ /** Whether the index is ready to serve searches. */
7877
+ isReady() {
7878
+ return this.isInitialized && !this.isDirty;
7879
+ }
7880
+ };
7881
+
7882
+ // src/finder/useGraphSearch.ts
7883
+ function useGraphSearch() {
7884
+ const { baseNormalized } = useLiveGraphData();
7885
+ const serviceRef = useRef9(null);
7886
+ const [isReady, setIsReady] = useState19(false);
7887
+ const [isIndexing, setIsIndexing] = useState19(false);
7888
+ const [error, setError] = useState19(null);
7889
+ const [stats, setStats] = useState19(null);
7890
+ const lastDataKeyRef = useRef9("");
7891
+ useEffect10(() => {
7892
+ const nodeCount = Object.keys(baseNormalized.nodesById).length;
7893
+ const edgeCount = Object.keys(baseNormalized.edgesById).length;
7894
+ const currentKey = `${nodeCount}-${edgeCount}`;
7895
+ if (!serviceRef.current) serviceRef.current = new OramaSearchService();
7896
+ const service = serviceRef.current;
7897
+ const changed = currentKey !== lastDataKeyRef.current;
7898
+ if (changed && lastDataKeyRef.current !== "") {
7899
+ lastDataKeyRef.current = currentKey;
7900
+ service.markDirty();
7901
+ setIsReady(false);
7902
+ setStats(service.getStats());
7903
+ } else if (lastDataKeyRef.current === "") {
7904
+ lastDataKeyRef.current = currentKey;
7905
+ }
7906
+ }, [baseNormalized]);
7907
+ const ensureIndexReady = async () => {
7908
+ const nodes = Object.values(baseNormalized.nodesById);
7909
+ const edges = Object.values(baseNormalized.edgesById);
7910
+ const service = serviceRef.current;
7911
+ if (!service) return false;
7912
+ if (service.isReady() && !service.needsRebuild()) return true;
7913
+ setIsIndexing(true);
7914
+ setError(null);
7915
+ try {
7916
+ if (!service.isReady()) await service.initialize();
7917
+ await service.rebuildIndex(nodes, edges);
7918
+ setIsReady(true);
7919
+ setStats(service.getStats());
7920
+ return true;
7921
+ } catch (err) {
7922
+ setError(err instanceof Error ? err : new Error(String(err)));
7923
+ setIsReady(false);
7924
+ return false;
7925
+ } finally {
7926
+ setIsIndexing(false);
7927
+ }
7928
+ };
7929
+ const search2 = async (query, searchOptions) => {
7930
+ const term = query.trim();
7931
+ if (!term) return [];
7932
+ try {
7933
+ const ready = await ensureIndexReady();
7934
+ if (!ready || !serviceRef.current) return [];
7935
+ return await serviceRef.current.search({ term, ...searchOptions });
7936
+ } catch (err) {
7937
+ errorLog7("[GraphFinder] search error:", err);
7938
+ setError(err instanceof Error ? err : new Error(String(err)));
7939
+ return [];
7940
+ }
7941
+ };
7942
+ return { search: search2, isReady, isIndexing, stats, error };
7943
+ }
7944
+
7945
+ // src/finder/useFinderState.ts
7946
+ function isNodeRow(item) {
7947
+ return "displayName" in item && !("start_id_" in item);
7948
+ }
7949
+ function isEdgeRow(item) {
7950
+ return "start_id_" in item && "end_id_" in item;
7951
+ }
7952
+ function deduplicateResults(items) {
7953
+ const seen = /* @__PURE__ */ new Set();
7954
+ const deduped = [];
7955
+ for (const raw of items) {
7956
+ if (raw && typeof raw === "object" && "id_" in raw) {
7957
+ const item = raw;
7958
+ const key = `${isEdgeRow(item) ? "edge" : "node"}:${String(item.id_)}`;
7959
+ if (!seen.has(key)) {
7960
+ seen.add(key);
7961
+ deduped.push(item);
7962
+ }
7963
+ } else {
7964
+ deduped.push(raw);
7965
+ }
7966
+ }
7967
+ return deduped;
7968
+ }
7969
+ function oramaHitToRow(r) {
7970
+ const doc = r.document;
7971
+ const properties = doc.properties_json ? JSON.parse(doc.properties_json) : {};
7972
+ return {
7973
+ id_: doc.id_,
7974
+ label_: doc.label_,
7975
+ displayName: doc.displayName,
7976
+ ...doc.type === "edge" ? { start_id_: doc.start_id_, end_id_: doc.end_id_ } : {},
7977
+ ...properties
7978
+ };
7979
+ }
7980
+ function useFinderState() {
7981
+ const useGraphFilter2 = useWorkspaceGraphFilter();
7982
+ const [searchMode, setSearchMode] = useState20("fulltext");
7983
+ const [expressionQuery, setExpressionQuery] = useState20("");
7984
+ const [fulltextQuery, setFulltextQuery] = useState20("");
7985
+ const [results, setResults] = useState20([]);
7986
+ const [error, setError] = useState20(null);
7987
+ const [hasSearched, setHasSearched] = useState20(false);
7988
+ const [searchInfo, setSearchInfo] = useState20("");
7989
+ const { baseNormalized, normalized } = useLiveGraphData();
7990
+ const lastDataChangeTime = useGraphFilter2((s) => s.lastDataChangeTime);
7991
+ const { search: fullTextSearch, isIndexing, stats } = useGraphSearch();
7992
+ useEffect11(() => {
7993
+ setExpressionQuery("");
7994
+ setFulltextQuery("");
7995
+ setResults([]);
7996
+ setError(null);
7997
+ setHasSearched(false);
7998
+ setSearchInfo("");
7999
+ }, [lastDataChangeTime]);
8000
+ const buildQueryData = () => {
8001
+ const all = flattenGraphData(baseNormalized);
8002
+ const visible = flattenGraphData(normalized);
8003
+ return {
8004
+ ...all,
8005
+ vnodes: visible.nodes,
8006
+ vedges: visible.edges,
8007
+ vgraph: visible.graph
8008
+ };
8009
+ };
8010
+ const runExpression = () => {
8011
+ const result = mistql.query(expressionQuery, buildQueryData());
8012
+ const arr = Array.isArray(result) ? result : [result];
8013
+ setResults(deduplicateResults(arr));
8014
+ setHasSearched(true);
8015
+ };
8016
+ const runFullText = async () => {
8017
+ const hits = await fullTextSearch(fulltextQuery);
8018
+ setResults(deduplicateResults(hits.map(oramaHitToRow)));
8019
+ setHasSearched(true);
8020
+ };
8021
+ const runBoth = async () => {
8022
+ const exprResult = expressionQuery.trim() ? mistql.query(expressionQuery, buildQueryData()) : buildQueryData().graph;
8023
+ const exprArray = Array.isArray(exprResult) ? exprResult : [exprResult];
8024
+ if (!fulltextQuery.trim()) {
8025
+ setResults(deduplicateResults(exprArray));
8026
+ setSearchInfo(`${exprArray.length} items after expression filter`);
8027
+ setHasSearched(true);
8028
+ return;
8029
+ }
8030
+ const exprIds = new Set(
8031
+ exprArray.filter((i) => i && typeof i === "object" && "id_" in i).map((i) => String(i.id_))
8032
+ );
8033
+ const hits = await fullTextSearch(fulltextQuery);
8034
+ const filtered = hits.filter((h) => exprIds.has(h.document.id_));
8035
+ const deduped = deduplicateResults(filtered.map(oramaHitToRow));
8036
+ setResults(deduped);
8037
+ setSearchInfo(
8038
+ `${exprArray.length} items after expression -> ${deduped.length} matches in full-text search`
8039
+ );
8040
+ setHasSearched(true);
8041
+ };
8042
+ const activeQuery = () => {
8043
+ if (searchMode === "expression") return expressionQuery;
8044
+ if (searchMode === "fulltext") return fulltextQuery;
8045
+ return expressionQuery || fulltextQuery;
8046
+ };
8047
+ const runActiveMode = async () => {
8048
+ if (searchMode === "expression") return runExpression();
8049
+ if (searchMode === "fulltext") return runFullText();
8050
+ return runBoth();
8051
+ };
8052
+ const handleSearch = async () => {
8053
+ if (!activeQuery().trim()) {
8054
+ setError(null);
8055
+ setResults([]);
8056
+ setHasSearched(false);
8057
+ setSearchInfo("");
8058
+ return;
8059
+ }
8060
+ try {
8061
+ setError(null);
8062
+ setSearchInfo("");
8063
+ await runActiveMode();
8064
+ } catch (err) {
8065
+ setError(err instanceof Error ? err.message : "Search execution failed");
8066
+ setResults([]);
8067
+ setHasSearched(true);
8068
+ setSearchInfo("");
8069
+ }
8070
+ };
8071
+ return {
8072
+ searchMode,
8073
+ setSearchMode,
8074
+ expressionQuery,
8075
+ setExpressionQuery,
8076
+ fulltextQuery,
8077
+ setFulltextQuery,
8078
+ results,
8079
+ error,
8080
+ hasSearched,
8081
+ searchInfo,
8082
+ isIndexing,
8083
+ statsLabel: stats && stats.isReady ? ` - ${stats.totalIndexed} indexed` : "",
8084
+ handleSearch
8085
+ };
8086
+ }
8087
+
8088
+ // src/finder/GraphFinder.tsx
8089
+ import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
8090
+ function GraphFinder() {
8091
+ const {
8092
+ searchMode,
8093
+ setSearchMode,
8094
+ expressionQuery,
8095
+ setExpressionQuery,
8096
+ fulltextQuery,
8097
+ setFulltextQuery,
8098
+ results,
8099
+ error,
8100
+ hasSearched,
8101
+ searchInfo,
8102
+ isIndexing,
8103
+ statsLabel,
8104
+ handleSearch
8105
+ } = useFinderState();
8106
+ const handleKeyDown = (e) => {
8107
+ if ((e.ctrlKey || e.metaKey) && e.key === "Enter") handleSearch();
8108
+ };
8109
+ const both = searchMode === "both";
8110
+ return /* @__PURE__ */ jsxs26("div", { className: "h-full flex flex-col p-4", children: [
8111
+ /* @__PURE__ */ jsx28("div", { className: "mb-3 flex-shrink-0", children: /* @__PURE__ */ jsx28(
8112
+ FormSelect3,
8113
+ {
8114
+ label: "Search Mode",
8115
+ value: searchMode,
8116
+ onChange: (value) => setSearchMode(value || "fulltext"),
8117
+ options: [
8118
+ { value: "fulltext", label: "Full-Text" },
8119
+ { value: "expression", label: "Expression" },
8120
+ { value: "both", label: "Both" }
8121
+ ],
8122
+ clearable: false
8123
+ }
8124
+ ) }),
8125
+ /* @__PURE__ */ jsxs26("div", { className: "mb-4 flex-shrink-0 space-y-3", children: [
8126
+ (searchMode === "expression" || both) && /* @__PURE__ */ jsx28(
8127
+ ExpressionInput,
8128
+ {
8129
+ value: expressionQuery,
8130
+ onChange: setExpressionQuery,
8131
+ onKeyDown: handleKeyDown,
8132
+ both
8133
+ }
8134
+ ),
8135
+ (searchMode === "fulltext" || both) && /* @__PURE__ */ jsx28(
8136
+ FullTextInput,
8137
+ {
8138
+ value: fulltextQuery,
8139
+ onChange: setFulltextQuery,
8140
+ onKeyDown: handleKeyDown,
8141
+ onSearch: handleSearch,
8142
+ both,
8143
+ isIndexing,
8144
+ statsLabel
8145
+ }
8146
+ )
8147
+ ] }),
8148
+ error && /* @__PURE__ */ jsxs26(Alert5, { variant: "destructive", className: "mb-4 flex-shrink-0", children: [
8149
+ /* @__PURE__ */ jsx28(AlertTitle, { children: "Query Error" }),
8150
+ /* @__PURE__ */ jsx28(AlertDescription, { children: error })
8151
+ ] }),
8152
+ hasSearched && !error && /* @__PURE__ */ jsx28(ResultsPanel, { results, searchInfo })
8153
+ ] });
8154
+ }
8155
+ function ExpressionInput({
8156
+ value,
8157
+ onChange,
8158
+ onKeyDown,
8159
+ both
8160
+ }) {
8161
+ return /* @__PURE__ */ jsxs26("div", { children: [
8162
+ both && /* @__PURE__ */ jsx28("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Expression filter (applied first):" }),
8163
+ /* @__PURE__ */ jsx28(
8164
+ FormTextarea,
8165
+ {
8166
+ placeholder: 'e.g., Person | filter name == "John"',
8167
+ value,
8168
+ onChange: (e) => onChange(e.target.value),
8169
+ onKeyDown,
8170
+ autosize: true,
8171
+ minRows: both ? 2 : 3
8172
+ }
8173
+ ),
8174
+ !both && /* @__PURE__ */ jsx28("div", { className: "text-xs text-muted-foreground mt-1", children: "Use pipes (|) to filter and transform. Available: nodes, edges, graph, and each label." })
8175
+ ] });
8176
+ }
8177
+ function FullTextInput({
8178
+ value,
8179
+ onChange,
8180
+ onKeyDown,
8181
+ onSearch,
8182
+ both,
8183
+ isIndexing,
8184
+ statsLabel
8185
+ }) {
8186
+ return /* @__PURE__ */ jsxs26("div", { children: [
8187
+ both && /* @__PURE__ */ jsx28("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Full-text search (on filtered results):" }),
8188
+ /* @__PURE__ */ jsx28(
8189
+ FormTextarea,
8190
+ {
8191
+ placeholder: "e.g., software engineer machine learning",
8192
+ value,
8193
+ onChange: (e) => onChange(e.target.value),
8194
+ onKeyDown,
8195
+ autosize: true,
8196
+ minRows: both ? 2 : 3,
8197
+ rightSection: /* @__PURE__ */ jsx28(SimpleTooltip5, { label: "Search", children: /* @__PURE__ */ jsx28(Button11, { variant: "ghost", size: "icon", onClick: onSearch, children: /* @__PURE__ */ jsx28(Search, { size: 16 }) }) }),
8198
+ rightSectionWidth: 48
8199
+ }
8200
+ ),
8201
+ !both && /* @__PURE__ */ jsxs26("div", { className: "text-xs text-muted-foreground mt-1", children: [
8202
+ "Natural language search with typo tolerance",
8203
+ isIndexing && " - building index...",
8204
+ statsLabel
8205
+ ] })
8206
+ ] });
8207
+ }
8208
+ function ResultsPanel({
8209
+ results,
8210
+ searchInfo
8211
+ }) {
8212
+ return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col flex-1 min-h-0", children: [
8213
+ /* @__PURE__ */ jsxs26("div", { className: "mb-2 flex-shrink-0", children: [
8214
+ /* @__PURE__ */ jsxs26("div", { className: "text-sm font-semibold", children: [
8215
+ "Results: ",
8216
+ results.length,
8217
+ " ",
8218
+ results.length === 1 ? "item" : "items"
8219
+ ] }),
8220
+ searchInfo && /* @__PURE__ */ jsx28("div", { className: "text-xs text-muted-foreground mt-1", children: searchInfo })
8221
+ ] }),
8222
+ results.length === 0 ? /* @__PURE__ */ jsx28("div", { className: "text-sm text-muted-foreground italic", children: "No results found" }) : /* @__PURE__ */ jsx28("div", { className: "space-y-2 overflow-y-auto flex-1", children: results.map((item, index) => /* @__PURE__ */ jsx28(ResultItem, { item }, resultKey(item, index))) })
8223
+ ] });
8224
+ }
8225
+ function resultKey(item, index) {
8226
+ if (item && typeof item === "object" && "id_" in item) {
8227
+ return `${isEdgeRow(item) ? "edge" : "node"}:${String(item.id_)}`;
8228
+ }
8229
+ return String(index);
8230
+ }
8231
+ function ResultItem({ item }) {
8232
+ const useGraphFocus2 = useWorkspaceGraphFocus();
8233
+ const requestFocus = useGraphFocus2((s) => s.requestFocus);
8234
+ if (typeof item !== "object" || item === null) {
8235
+ return /* @__PURE__ */ jsx28("div", { className: "p-3 bg-muted/40 rounded border border-border", children: /* @__PURE__ */ jsx28("div", { className: "text-sm font-medium", children: String(item) }) });
8236
+ }
8237
+ const isNodeItem = isNodeRow(item);
8238
+ const isEdgeItem = isEdgeRow(item);
8239
+ const isGraphElement = isNodeItem || isEdgeItem;
8240
+ const focus = (shouldSelect) => {
8241
+ if (!isGraphElement) return;
8242
+ requestFocus(String(item.id_), isNodeItem ? "node" : "edge", shouldSelect);
8243
+ };
8244
+ return /* @__PURE__ */ jsx28(
8245
+ "div",
8246
+ {
8247
+ className: `p-3 bg-background rounded border border-border transition-colors ${isGraphElement ? "hover:border-primary hover:bg-accent cursor-pointer" : ""}`,
8248
+ onClick: () => focus(false),
8249
+ onDoubleClick: () => focus(true),
8250
+ title: isGraphElement ? "Click to focus, double-click to focus and select" : void 0,
8251
+ children: /* @__PURE__ */ jsxs26("div", { className: "flex items-start justify-between", children: [
8252
+ /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
8253
+ isNodeItem && /* @__PURE__ */ jsxs26(Fragment9, { children: [
8254
+ /* @__PURE__ */ jsx28("span", { className: "text-xs font-semibold px-2 py-0.5 bg-accent text-accent-foreground rounded", children: item.label_ }),
8255
+ /* @__PURE__ */ jsx28("span", { className: "text-sm font-medium", children: item.displayName })
8256
+ ] }),
8257
+ isEdgeItem && /* @__PURE__ */ jsxs26(Fragment9, { children: [
8258
+ /* @__PURE__ */ jsx28("span", { className: "text-xs font-semibold px-2 py-0.5 bg-accent text-accent-foreground rounded", children: item.label_ }),
8259
+ /* @__PURE__ */ jsxs26("span", { className: "text-xs text-muted-foreground", children: [
8260
+ String(item.start_id_),
8261
+ " -> ",
8262
+ String(item.end_id_)
8263
+ ] })
8264
+ ] }),
8265
+ !isGraphElement && /* @__PURE__ */ jsx28("span", { className: "text-sm font-medium", children: "Object" })
8266
+ ] }),
8267
+ isGraphElement && /* @__PURE__ */ jsxs26("span", { className: "text-xs text-muted-foreground", children: [
8268
+ "ID: ",
8269
+ String(item.id_)
8270
+ ] })
8271
+ ] })
8272
+ }
8273
+ );
8274
+ }
7409
8275
  export {
7410
8276
  ActionPanel_default as ActionPanel,
7411
8277
  DeleteNodeDialog,
@@ -7414,6 +8280,7 @@ export {
7414
8280
  EdgeInfoCard_default as EdgeInfoCard,
7415
8281
  GraphA11yList,
7416
8282
  GraphEditToolbar,
8283
+ GraphFinder,
7417
8284
  GraphModelEditor,
7418
8285
  GraphRendererProvider,
7419
8286
  GraphStoresContext,
@@ -7422,8 +8289,11 @@ export {
7422
8289
  NodeFormSheet,
7423
8290
  NodeInfoCard_default as NodeInfoCard,
7424
8291
  NodeTypeNameSheet,
8292
+ OramaSearchService,
7425
8293
  Overview_default as Overview,
7426
8294
  PropertyFormSheet,
8295
+ QueryEditor,
8296
+ QueryHistory,
7427
8297
  RelationFormSheet,
7428
8298
  TypeDetailPanel,
7429
8299
  addEdgeType,
@@ -7438,6 +8308,7 @@ export {
7438
8308
  createGraphExplorationStore,
7439
8309
  createGraphFilterStore,
7440
8310
  createGraphFocusStore,
8311
+ createQueryHistoryStore,
7441
8312
  createSelectionStore,
7442
8313
  deleteEdgeType,
7443
8314
  deleteEdgeTypeProperty,
@@ -7453,9 +8324,11 @@ export {
7453
8324
  enrichNodes,
7454
8325
  enrichSchemaWithRelations,
7455
8326
  extractDisplayLabel,
8327
+ extractGraphFromRows,
7456
8328
  extractRelationFields,
7457
8329
  findEdgeType,
7458
8330
  findType,
8331
+ flattenGraphData,
7459
8332
  graphCapabilitiesSchema,
7460
8333
  groupEdgesByRelation,
7461
8334
  hasProperties,
@@ -7493,6 +8366,7 @@ export {
7493
8366
  useCytoscapeData,
7494
8367
  GraphFocus_default as useGraphFocus,
7495
8368
  useGraphRenderer,
8369
+ useGraphSearch,
7496
8370
  useLiveGraphData,
7497
8371
  useReagraphData,
7498
8372
  useStableRef,