@petrarca/sonnet-graph 0.6.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/README.md +89 -118
- package/dist/index.d.ts +354 -33
- package/dist/index.js +1534 -636
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -182,11 +182,225 @@ var EMPTY_NORMALIZED = {
|
|
|
182
182
|
edgeIds: []
|
|
183
183
|
};
|
|
184
184
|
|
|
185
|
-
// src/
|
|
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";
|
|
398
|
+
var MAX_NODE_SELECTION = 2;
|
|
399
|
+
function evictToMax(selected, id, max = MAX_NODE_SELECTION) {
|
|
400
|
+
return [...selected.filter((existing) => existing !== id), id].slice(-max);
|
|
401
|
+
}
|
|
188
402
|
function createSelectionStore() {
|
|
189
|
-
return
|
|
403
|
+
return create2()((set, get) => ({
|
|
190
404
|
selectedNodeIds: [],
|
|
191
405
|
setSelectedNodeIds: (ids) => {
|
|
192
406
|
const prev = get().selectedNodeIds;
|
|
@@ -213,10 +427,10 @@ function createSelectionStore() {
|
|
|
213
427
|
var useSelection = createSelectionStore();
|
|
214
428
|
|
|
215
429
|
// src/stores/GraphData.ts
|
|
216
|
-
import { create as
|
|
430
|
+
import { create as create3 } from "zustand";
|
|
217
431
|
import { devLog as devLog2 } from "@petrarca/sonnet-core";
|
|
218
432
|
function createGraphDataStore() {
|
|
219
|
-
return
|
|
433
|
+
return create3()((set, get) => ({
|
|
220
434
|
nodes: [],
|
|
221
435
|
edges: [],
|
|
222
436
|
stats: void 0,
|
|
@@ -324,7 +538,7 @@ function createGraphDataStore() {
|
|
|
324
538
|
var useGraphData = createGraphDataStore();
|
|
325
539
|
|
|
326
540
|
// src/stores/GraphFilter.ts
|
|
327
|
-
import { create as
|
|
541
|
+
import { create as create4 } from "zustand";
|
|
328
542
|
function calculateNodeLabelUpdate(affectedLabels, currentNodeFilters, allNodesDisabled) {
|
|
329
543
|
if (affectedLabels.length === 0) return null;
|
|
330
544
|
if (allNodesDisabled) {
|
|
@@ -362,7 +576,7 @@ function handleToggleFilter(label, current, exists, getNodeLabelUpdate, set) {
|
|
|
362
576
|
set({ edgeLabelFilters: next, ...nodeUpdate });
|
|
363
577
|
}
|
|
364
578
|
function createGraphFilterStore() {
|
|
365
|
-
return
|
|
579
|
+
return create4()((set, get) => ({
|
|
366
580
|
nodeLabelFilters: [],
|
|
367
581
|
allLabelsDisabled: false,
|
|
368
582
|
edgeLabelFilters: [],
|
|
@@ -438,7 +652,7 @@ function createGraphFilterStore() {
|
|
|
438
652
|
var useGraphFilter = createGraphFilterStore();
|
|
439
653
|
|
|
440
654
|
// src/stores/GraphExploration.ts
|
|
441
|
-
import { create as
|
|
655
|
+
import { create as create5 } from "zustand";
|
|
442
656
|
import { devLog as devLog3, errorLog } from "@petrarca/sonnet-core";
|
|
443
657
|
import { toast } from "sonner";
|
|
444
658
|
function collectReachable(startId, direction, allEdges, includeStart) {
|
|
@@ -565,7 +779,7 @@ function mergeExpandedLabelsIntoFilters(stores, enrichedNodes, nodeId2) {
|
|
|
565
779
|
}
|
|
566
780
|
function createGraphExplorationStore(deps) {
|
|
567
781
|
const stores = deps;
|
|
568
|
-
return
|
|
782
|
+
return create5()((set, get) => ({
|
|
569
783
|
focusedNodeIds: /* @__PURE__ */ new Set(),
|
|
570
784
|
isFocusMode: false,
|
|
571
785
|
hiddenElementIds: /* @__PURE__ */ new Set(),
|
|
@@ -974,7 +1188,7 @@ function createGraphExplorationStore(deps) {
|
|
|
974
1188
|
}
|
|
975
1189
|
|
|
976
1190
|
// src/stores/GraphFocus.ts
|
|
977
|
-
import { create as
|
|
1191
|
+
import { create as create6 } from "zustand";
|
|
978
1192
|
import { devLog as devLog4 } from "@petrarca/sonnet-core";
|
|
979
1193
|
function buildStore() {
|
|
980
1194
|
return (set, get) => ({
|
|
@@ -1008,13 +1222,13 @@ function buildStore() {
|
|
|
1008
1222
|
});
|
|
1009
1223
|
}
|
|
1010
1224
|
function createGraphFocusStore() {
|
|
1011
|
-
return
|
|
1225
|
+
return create6()(buildStore());
|
|
1012
1226
|
}
|
|
1013
1227
|
var useGraphFocus = createGraphFocusStore();
|
|
1014
1228
|
var GraphFocus_default = useGraphFocus;
|
|
1015
1229
|
|
|
1016
1230
|
// src/stores/ActionPanelStore.ts
|
|
1017
|
-
import { create as
|
|
1231
|
+
import { create as create7 } from "zustand";
|
|
1018
1232
|
import { devLog as devLog5 } from "@petrarca/sonnet-core";
|
|
1019
1233
|
function buildStore2() {
|
|
1020
1234
|
return (set, get) => ({
|
|
@@ -1035,7 +1249,7 @@ function buildStore2() {
|
|
|
1035
1249
|
});
|
|
1036
1250
|
}
|
|
1037
1251
|
function createActionPanelStore() {
|
|
1038
|
-
return
|
|
1252
|
+
return create7()(buildStore2());
|
|
1039
1253
|
}
|
|
1040
1254
|
var useActionPanelStore = createActionPanelStore();
|
|
1041
1255
|
|
|
@@ -1046,7 +1260,7 @@ function useGraphStores() {
|
|
|
1046
1260
|
const ctx = useContext(GraphStoresContext);
|
|
1047
1261
|
if (!ctx) {
|
|
1048
1262
|
throw new Error(
|
|
1049
|
-
"useGraphStores must be used within a
|
|
1263
|
+
"useGraphStores must be used within a GraphStoresContext.Provider. Wrap your graph UI with <GraphStoresContext.Provider value={...}>."
|
|
1050
1264
|
);
|
|
1051
1265
|
}
|
|
1052
1266
|
return ctx;
|
|
@@ -1063,31 +1277,35 @@ function useWorkspaceGraphExploration() {
|
|
|
1063
1277
|
function useWorkspaceSelection() {
|
|
1064
1278
|
return useGraphStores().selection;
|
|
1065
1279
|
}
|
|
1280
|
+
function useWorkspaceGraphFocus() {
|
|
1281
|
+
return useGraphStores().graphFocus;
|
|
1282
|
+
}
|
|
1066
1283
|
|
|
1067
1284
|
// src/hooks/useStableRef.ts
|
|
1068
|
-
import { useCallback, useRef } from "react";
|
|
1285
|
+
import { useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
1069
1286
|
function useStableRef(fn) {
|
|
1070
|
-
const ref =
|
|
1287
|
+
const ref = useRef2(fn);
|
|
1071
1288
|
ref.current = fn;
|
|
1072
|
-
return
|
|
1289
|
+
return useCallback2(
|
|
1073
1290
|
((...args) => ref.current?.(...args)),
|
|
1074
1291
|
[]
|
|
1075
1292
|
);
|
|
1076
1293
|
}
|
|
1077
1294
|
|
|
1078
1295
|
// src/hooks/useLiveGraphData.ts
|
|
1079
|
-
import { useMemo, useEffect } from "react";
|
|
1296
|
+
import { useMemo, useEffect as useEffect2 } from "react";
|
|
1080
1297
|
import { devLog as devLog6 } from "@petrarca/sonnet-core";
|
|
1081
1298
|
function useLiveGraphData() {
|
|
1082
1299
|
const useGraphFilter2 = useWorkspaceGraphFilter();
|
|
1083
1300
|
const useGraphData2 = useWorkspaceGraphData();
|
|
1084
1301
|
const useGraphExploration = useWorkspaceGraphExploration();
|
|
1302
|
+
const useGraphFocus2 = useWorkspaceGraphFocus();
|
|
1085
1303
|
const lastDataChangeTime = useGraphFilter2((s) => s.lastDataChangeTime);
|
|
1086
1304
|
const filterLabels = useGraphFilter2((s) => s.nodeLabelFilters);
|
|
1087
1305
|
const allLabelsDisabled = useGraphFilter2((s) => s.allLabelsDisabled);
|
|
1088
1306
|
const edgeFilterLabels = useGraphFilter2((s) => s.edgeLabelFilters);
|
|
1089
1307
|
const allEdgeLabelsDisabled = useGraphFilter2((s) => s.allEdgeLabelsDisabled);
|
|
1090
|
-
const overrideElementIds =
|
|
1308
|
+
const overrideElementIds = useGraphFocus2((s) => s.overrideElementIds);
|
|
1091
1309
|
const isFocusMode = useGraphExploration((s) => s.isFocusMode);
|
|
1092
1310
|
const focusedNodeIds = useGraphExploration((s) => s.focusedNodeIds);
|
|
1093
1311
|
const hiddenElementIds = useGraphExploration((s) => s.hiddenElementIds);
|
|
@@ -1103,9 +1321,9 @@ function useLiveGraphData() {
|
|
|
1103
1321
|
focusedCount: focusedNodeIds.size,
|
|
1104
1322
|
hiddenCount: hiddenElementIds.size
|
|
1105
1323
|
});
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}, [lastDataChangeTime]);
|
|
1324
|
+
useEffect2(() => {
|
|
1325
|
+
useGraphFocus2.getState().clearOverrides();
|
|
1326
|
+
}, [lastDataChangeTime, useGraphFocus2]);
|
|
1109
1327
|
const allLabelCounts = useMemo(() => {
|
|
1110
1328
|
if (isValueSet) return {};
|
|
1111
1329
|
const counts = {};
|
|
@@ -1301,10 +1519,11 @@ function useLiveGraphData() {
|
|
|
1301
1519
|
edges: availableEdges.length
|
|
1302
1520
|
};
|
|
1303
1521
|
}, [stats, availableNodes.length, availableEdges.length]);
|
|
1304
|
-
|
|
1305
|
-
const { clearOverrides } =
|
|
1522
|
+
useEffect2(() => {
|
|
1523
|
+
const { clearOverrides } = useGraphFocus2.getState();
|
|
1306
1524
|
clearOverrides();
|
|
1307
1525
|
}, [
|
|
1526
|
+
useGraphFocus2,
|
|
1308
1527
|
filterLabels,
|
|
1309
1528
|
allLabelsDisabled,
|
|
1310
1529
|
edgeFilterLabels,
|
|
@@ -1439,30 +1658,30 @@ function useGraphRenderer() {
|
|
|
1439
1658
|
}
|
|
1440
1659
|
|
|
1441
1660
|
// src/hooks/graphRendererProvider.tsx
|
|
1442
|
-
import { useState, useCallback as
|
|
1443
|
-
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";
|
|
1444
1663
|
function GraphRendererProvider({
|
|
1445
1664
|
children,
|
|
1446
1665
|
defaultRenderer = "cytoscape",
|
|
1447
1666
|
onRendererChange
|
|
1448
1667
|
}) {
|
|
1449
|
-
const [renderer, setRendererState] =
|
|
1450
|
-
const setRenderer =
|
|
1668
|
+
const [renderer, setRendererState] = useState2(defaultRenderer);
|
|
1669
|
+
const setRenderer = useCallback3(
|
|
1451
1670
|
(newRenderer) => {
|
|
1452
1671
|
setRendererState(newRenderer);
|
|
1453
1672
|
onRendererChange?.(newRenderer);
|
|
1454
1673
|
},
|
|
1455
1674
|
[onRendererChange]
|
|
1456
1675
|
);
|
|
1457
|
-
return /* @__PURE__ */
|
|
1676
|
+
return /* @__PURE__ */ jsx3(GraphRendererContext.Provider, { value: { renderer, setRenderer }, children });
|
|
1458
1677
|
}
|
|
1459
1678
|
|
|
1460
1679
|
// src/components/rea/ReaGraph.tsx
|
|
1461
1680
|
import {
|
|
1462
|
-
useEffect as
|
|
1463
|
-
useRef as
|
|
1464
|
-
useState as
|
|
1465
|
-
useCallback as
|
|
1681
|
+
useEffect as useEffect4,
|
|
1682
|
+
useRef as useRef4,
|
|
1683
|
+
useState as useState6,
|
|
1684
|
+
useCallback as useCallback5
|
|
1466
1685
|
} from "react";
|
|
1467
1686
|
import { devLog as devLog8 } from "@petrarca/sonnet-core";
|
|
1468
1687
|
import {
|
|
@@ -1507,43 +1726,43 @@ import { cn } from "@petrarca/sonnet-core";
|
|
|
1507
1726
|
|
|
1508
1727
|
// src/components/rea/LayoutAndCameraControls.tsx
|
|
1509
1728
|
import {
|
|
1510
|
-
Button as
|
|
1511
|
-
DropdownMenu as
|
|
1512
|
-
DropdownMenuContent as
|
|
1513
|
-
DropdownMenuItem as
|
|
1729
|
+
Button as Button4,
|
|
1730
|
+
DropdownMenu as DropdownMenu3,
|
|
1731
|
+
DropdownMenuContent as DropdownMenuContent3,
|
|
1732
|
+
DropdownMenuItem as DropdownMenuItem3,
|
|
1514
1733
|
DropdownMenuLabel,
|
|
1515
|
-
DropdownMenuSeparator,
|
|
1516
|
-
DropdownMenuTrigger as
|
|
1517
|
-
SimpleTooltip as
|
|
1734
|
+
DropdownMenuSeparator as DropdownMenuSeparator2,
|
|
1735
|
+
DropdownMenuTrigger as DropdownMenuTrigger3,
|
|
1736
|
+
SimpleTooltip as SimpleTooltip3
|
|
1518
1737
|
} from "@petrarca/sonnet-ui";
|
|
1519
1738
|
import { LayoutPanelTop, Maximize, Shrink, ArrowUp, Eye } from "lucide-react";
|
|
1520
|
-
import { useState as
|
|
1739
|
+
import { useState as useState3 } from "react";
|
|
1521
1740
|
|
|
1522
1741
|
// src/components/shared/RendererDropdown.tsx
|
|
1523
1742
|
import {
|
|
1524
|
-
Button,
|
|
1525
|
-
DropdownMenu,
|
|
1526
|
-
DropdownMenuContent,
|
|
1527
|
-
DropdownMenuItem,
|
|
1528
|
-
DropdownMenuTrigger,
|
|
1529
|
-
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
|
|
1530
1749
|
} from "@petrarca/sonnet-ui";
|
|
1531
|
-
import { jsx as
|
|
1750
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1532
1751
|
function RendererDropdown() {
|
|
1533
1752
|
const { renderer, setRenderer } = useGraphRenderer();
|
|
1534
|
-
return /* @__PURE__ */
|
|
1535
|
-
/* @__PURE__ */
|
|
1536
|
-
/* @__PURE__ */
|
|
1537
|
-
/* @__PURE__ */
|
|
1538
|
-
|
|
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,
|
|
1539
1758
|
{
|
|
1540
1759
|
onClick: () => setRenderer("reagraph"),
|
|
1541
1760
|
className: renderer === "reagraph" ? "text-blue-600" : "",
|
|
1542
1761
|
children: "Reagraph"
|
|
1543
1762
|
}
|
|
1544
1763
|
),
|
|
1545
|
-
/* @__PURE__ */
|
|
1546
|
-
|
|
1764
|
+
/* @__PURE__ */ jsx4(
|
|
1765
|
+
DropdownMenuItem2,
|
|
1547
1766
|
{
|
|
1548
1767
|
onClick: () => setRenderer("cytoscape"),
|
|
1549
1768
|
className: renderer === "cytoscape" ? "text-blue-600" : "",
|
|
@@ -1555,46 +1774,46 @@ function RendererDropdown() {
|
|
|
1555
1774
|
}
|
|
1556
1775
|
|
|
1557
1776
|
// src/components/rea/LayoutAndCameraControls.tsx
|
|
1558
|
-
import { Fragment, jsx as
|
|
1777
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1559
1778
|
function PathSelectionDropdown({
|
|
1560
1779
|
setPathSelectionType
|
|
1561
1780
|
}) {
|
|
1562
|
-
return /* @__PURE__ */
|
|
1563
|
-
/* @__PURE__ */
|
|
1564
|
-
/* @__PURE__ */
|
|
1565
|
-
/* @__PURE__ */
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
/* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
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" })
|
|
1569
1788
|
] })
|
|
1570
1789
|
] });
|
|
1571
1790
|
}
|
|
1572
1791
|
function LayoutDropdown({ updateLayout }) {
|
|
1573
|
-
const [showTooltip, setShowTooltip] =
|
|
1792
|
+
const [showTooltip, setShowTooltip] = useState3(true);
|
|
1574
1793
|
const handleSelect = (layout) => {
|
|
1575
1794
|
updateLayout(layout);
|
|
1576
1795
|
setShowTooltip(false);
|
|
1577
1796
|
setTimeout(() => setShowTooltip(true), 500);
|
|
1578
1797
|
};
|
|
1579
|
-
const trigger = /* @__PURE__ */
|
|
1580
|
-
return /* @__PURE__ */
|
|
1581
|
-
showTooltip ? /* @__PURE__ */
|
|
1582
|
-
/* @__PURE__ */
|
|
1583
|
-
/* @__PURE__ */
|
|
1584
|
-
/* @__PURE__ */
|
|
1585
|
-
/* @__PURE__ */
|
|
1586
|
-
/* @__PURE__ */
|
|
1587
|
-
/* @__PURE__ */
|
|
1588
|
-
/* @__PURE__ */
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__PURE__ */
|
|
1591
|
-
/* @__PURE__ */
|
|
1592
|
-
/* @__PURE__ */
|
|
1593
|
-
/* @__PURE__ */
|
|
1594
|
-
/* @__PURE__ */
|
|
1595
|
-
/* @__PURE__ */
|
|
1596
|
-
/* @__PURE__ */
|
|
1597
|
-
/* @__PURE__ */
|
|
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" })
|
|
1598
1817
|
] })
|
|
1599
1818
|
] });
|
|
1600
1819
|
}
|
|
@@ -1606,15 +1825,15 @@ function ViewControls({
|
|
|
1606
1825
|
isFocusMode,
|
|
1607
1826
|
onResetView
|
|
1608
1827
|
}) {
|
|
1609
|
-
return /* @__PURE__ */
|
|
1610
|
-
/* @__PURE__ */
|
|
1611
|
-
/* @__PURE__ */
|
|
1612
|
-
(hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */
|
|
1613
|
-
|
|
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,
|
|
1614
1833
|
{
|
|
1615
1834
|
label: isFocusMode ? "Reset Focus" : "Show All Hidden",
|
|
1616
1835
|
side: "left",
|
|
1617
|
-
children: /* @__PURE__ */
|
|
1836
|
+
children: /* @__PURE__ */ jsx5(Button4, { variant: "ghost", size: "icon", onClick: onResetView, children: /* @__PURE__ */ jsx5(Eye, {}) })
|
|
1618
1837
|
}
|
|
1619
1838
|
)
|
|
1620
1839
|
] });
|
|
@@ -1630,9 +1849,9 @@ function LayoutAndCameraControls({
|
|
|
1630
1849
|
onResetView,
|
|
1631
1850
|
extraToolbarContent
|
|
1632
1851
|
}) {
|
|
1633
|
-
return /* @__PURE__ */
|
|
1634
|
-
/* @__PURE__ */
|
|
1635
|
-
/* @__PURE__ */
|
|
1852
|
+
return /* @__PURE__ */ jsxs4("div", { className: "absolute right-2 top-2 flex flex-col border", children: [
|
|
1853
|
+
/* @__PURE__ */ jsx5(RendererDropdown, {}),
|
|
1854
|
+
/* @__PURE__ */ jsx5(
|
|
1636
1855
|
ViewControls,
|
|
1637
1856
|
{
|
|
1638
1857
|
fullscreen,
|
|
@@ -1643,9 +1862,9 @@ function LayoutAndCameraControls({
|
|
|
1643
1862
|
onResetView
|
|
1644
1863
|
}
|
|
1645
1864
|
),
|
|
1646
|
-
/* @__PURE__ */
|
|
1865
|
+
/* @__PURE__ */ jsx5(PathSelectionDropdown, { setPathSelectionType }),
|
|
1647
1866
|
extraToolbarContent,
|
|
1648
|
-
/* @__PURE__ */
|
|
1867
|
+
/* @__PURE__ */ jsx5(LayoutDropdown, { updateLayout })
|
|
1649
1868
|
] });
|
|
1650
1869
|
}
|
|
1651
1870
|
var LayoutAndCameraControls_default = LayoutAndCameraControls;
|
|
@@ -1655,10 +1874,10 @@ import { Card as Card2 } from "@petrarca/sonnet-ui";
|
|
|
1655
1874
|
|
|
1656
1875
|
// src/components/rea/NodeHoverInfo.tsx
|
|
1657
1876
|
import { Card, Separator } from "@petrarca/sonnet-ui";
|
|
1658
|
-
import { jsx as
|
|
1877
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1659
1878
|
function NodeHoverInfo({ node }) {
|
|
1660
|
-
return /* @__PURE__ */
|
|
1661
|
-
/* @__PURE__ */
|
|
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(
|
|
1662
1881
|
Separator,
|
|
1663
1882
|
{
|
|
1664
1883
|
orientation: "vertical",
|
|
@@ -1667,43 +1886,43 @@ function NodeHoverInfo({ node }) {
|
|
|
1667
1886
|
style: { backgroundColor: node.fill }
|
|
1668
1887
|
}
|
|
1669
1888
|
),
|
|
1670
|
-
/* @__PURE__ */
|
|
1671
|
-
/* @__PURE__ */
|
|
1672
|
-
/* @__PURE__ */
|
|
1889
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1890
|
+
/* @__PURE__ */ jsxs5("p", { children: [
|
|
1891
|
+
/* @__PURE__ */ jsxs5("strong", { children: [
|
|
1673
1892
|
node.data.label_,
|
|
1674
1893
|
": "
|
|
1675
1894
|
] }),
|
|
1676
1895
|
node.label
|
|
1677
1896
|
] }),
|
|
1678
|
-
/* @__PURE__ */
|
|
1897
|
+
/* @__PURE__ */ jsx6("p", { className: "text-sm text-muted-foreground", children: node.data.id_ })
|
|
1679
1898
|
] })
|
|
1680
1899
|
] }) });
|
|
1681
1900
|
}
|
|
1682
1901
|
var NodeHoverInfo_default = NodeHoverInfo;
|
|
1683
1902
|
|
|
1684
1903
|
// src/components/rea/ReaContextMenu.tsx
|
|
1685
|
-
import
|
|
1904
|
+
import React4, { useState as useState5 } from "react";
|
|
1686
1905
|
import { devLog as devLog7 } from "@petrarca/sonnet-core";
|
|
1687
1906
|
|
|
1688
1907
|
// src/components/rea/ExpandOptionsPanel.tsx
|
|
1689
|
-
import { useCallback as
|
|
1908
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
|
|
1690
1909
|
import { Checkbox, Input, Label } from "@petrarca/sonnet-ui";
|
|
1691
|
-
import { jsx as
|
|
1910
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1692
1911
|
function ExpandOptionsPanel({
|
|
1693
1912
|
nodeLabel,
|
|
1694
1913
|
onExpand,
|
|
1695
1914
|
onBack,
|
|
1696
1915
|
getEdgeTypes
|
|
1697
1916
|
}) {
|
|
1698
|
-
const [direction, setDirection] =
|
|
1699
|
-
const [limit, setLimit] =
|
|
1700
|
-
const [edgeTypes, setEdgeTypes] =
|
|
1701
|
-
const [selectedTypes, setSelectedTypes] =
|
|
1702
|
-
const [loading, setLoading] =
|
|
1703
|
-
const [offset, setOffset] =
|
|
1704
|
-
const expandBtnRef =
|
|
1705
|
-
const dragRef =
|
|
1706
|
-
const onPointerDown =
|
|
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(
|
|
1707
1926
|
(e) => {
|
|
1708
1927
|
if (e.target.closest("button")) return;
|
|
1709
1928
|
e.preventDefault();
|
|
@@ -1717,16 +1936,16 @@ function ExpandOptionsPanel({
|
|
|
1717
1936
|
},
|
|
1718
1937
|
[offset]
|
|
1719
1938
|
);
|
|
1720
|
-
const onPointerMove =
|
|
1939
|
+
const onPointerMove = useCallback4((e) => {
|
|
1721
1940
|
if (!dragRef.current) return;
|
|
1722
1941
|
const dx = e.clientX - dragRef.current.startX;
|
|
1723
1942
|
const dy = e.clientY - dragRef.current.startY;
|
|
1724
1943
|
setOffset({ x: dragRef.current.origX + dx, y: dragRef.current.origY + dy });
|
|
1725
1944
|
}, []);
|
|
1726
|
-
const onPointerUp =
|
|
1945
|
+
const onPointerUp = useCallback4(() => {
|
|
1727
1946
|
dragRef.current = null;
|
|
1728
1947
|
}, []);
|
|
1729
|
-
|
|
1948
|
+
useEffect3(() => {
|
|
1730
1949
|
if (!getEdgeTypes) return;
|
|
1731
1950
|
setLoading(true);
|
|
1732
1951
|
getEdgeTypes().then((types) => {
|
|
@@ -1751,18 +1970,18 @@ function ExpandOptionsPanel({
|
|
|
1751
1970
|
};
|
|
1752
1971
|
const allSelected = selectedTypes.length === edgeTypes.length;
|
|
1753
1972
|
const noneSelected = edgeTypes.length > 0 && selectedTypes.length === 0;
|
|
1754
|
-
|
|
1973
|
+
useEffect3(() => {
|
|
1755
1974
|
if (!loading && !noneSelected) {
|
|
1756
1975
|
expandBtnRef.current?.focus();
|
|
1757
1976
|
}
|
|
1758
1977
|
}, [loading, noneSelected]);
|
|
1759
|
-
return /* @__PURE__ */
|
|
1978
|
+
return /* @__PURE__ */ jsxs6(
|
|
1760
1979
|
"div",
|
|
1761
1980
|
{
|
|
1762
1981
|
className: "bg-background border border-border rounded-md shadow-md p-2.5 min-w-48 space-y-2",
|
|
1763
1982
|
style: { transform: `translate(${offset.x}px, ${offset.y}px)` },
|
|
1764
1983
|
children: [
|
|
1765
|
-
/* @__PURE__ */
|
|
1984
|
+
/* @__PURE__ */ jsxs6(
|
|
1766
1985
|
"div",
|
|
1767
1986
|
{
|
|
1768
1987
|
className: "flex items-center justify-between gap-2 cursor-grab active:cursor-grabbing select-none",
|
|
@@ -1770,7 +1989,7 @@ function ExpandOptionsPanel({
|
|
|
1770
1989
|
onPointerMove,
|
|
1771
1990
|
onPointerUp,
|
|
1772
1991
|
children: [
|
|
1773
|
-
/* @__PURE__ */
|
|
1992
|
+
/* @__PURE__ */ jsx7(
|
|
1774
1993
|
"button",
|
|
1775
1994
|
{
|
|
1776
1995
|
className: "text-[11px] text-muted-foreground hover:text-foreground cursor-pointer",
|
|
@@ -1778,13 +1997,13 @@ function ExpandOptionsPanel({
|
|
|
1778
1997
|
children: "Back"
|
|
1779
1998
|
}
|
|
1780
1999
|
),
|
|
1781
|
-
/* @__PURE__ */
|
|
2000
|
+
/* @__PURE__ */ jsx7("span", { className: "text-[11px] font-medium text-foreground truncate max-w-32", children: nodeLabel })
|
|
1782
2001
|
]
|
|
1783
2002
|
}
|
|
1784
2003
|
),
|
|
1785
|
-
/* @__PURE__ */
|
|
1786
|
-
/* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
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(
|
|
1788
2007
|
"button",
|
|
1789
2008
|
{
|
|
1790
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"}`,
|
|
@@ -1794,10 +2013,10 @@ function ExpandOptionsPanel({
|
|
|
1794
2013
|
dir
|
|
1795
2014
|
)) })
|
|
1796
2015
|
] }),
|
|
1797
|
-
!loading && edgeTypes.length > 1 && /* @__PURE__ */
|
|
1798
|
-
/* @__PURE__ */
|
|
1799
|
-
/* @__PURE__ */
|
|
1800
|
-
/* @__PURE__ */
|
|
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(
|
|
1801
2020
|
"button",
|
|
1802
2021
|
{
|
|
1803
2022
|
className: "text-[10px] text-muted-foreground hover:text-foreground cursor-pointer",
|
|
@@ -1806,12 +2025,12 @@ function ExpandOptionsPanel({
|
|
|
1806
2025
|
}
|
|
1807
2026
|
)
|
|
1808
2027
|
] }),
|
|
1809
|
-
/* @__PURE__ */
|
|
2028
|
+
/* @__PURE__ */ jsx7("div", { className: "space-y-0.5 max-h-32 overflow-y-auto", children: edgeTypes.map((type) => /* @__PURE__ */ jsxs6(
|
|
1810
2029
|
"label",
|
|
1811
2030
|
{
|
|
1812
2031
|
className: "flex items-center gap-1.5 text-[11px] cursor-pointer",
|
|
1813
2032
|
children: [
|
|
1814
|
-
/* @__PURE__ */
|
|
2033
|
+
/* @__PURE__ */ jsx7(
|
|
1815
2034
|
Checkbox,
|
|
1816
2035
|
{
|
|
1817
2036
|
checked: selectedTypes.includes(type),
|
|
@@ -1819,15 +2038,15 @@ function ExpandOptionsPanel({
|
|
|
1819
2038
|
className: "h-3 w-3"
|
|
1820
2039
|
}
|
|
1821
2040
|
),
|
|
1822
|
-
/* @__PURE__ */
|
|
2041
|
+
/* @__PURE__ */ jsx7("span", { className: "truncate", children: type })
|
|
1823
2042
|
]
|
|
1824
2043
|
},
|
|
1825
2044
|
type
|
|
1826
2045
|
)) })
|
|
1827
2046
|
] }),
|
|
1828
|
-
/* @__PURE__ */
|
|
1829
|
-
/* @__PURE__ */
|
|
1830
|
-
/* @__PURE__ */
|
|
2047
|
+
/* @__PURE__ */ jsxs6("div", { className: "space-y-1", children: [
|
|
2048
|
+
/* @__PURE__ */ jsx7(Label, { className: "text-[11px] text-muted-foreground", children: "Limit" }),
|
|
2049
|
+
/* @__PURE__ */ jsx7(
|
|
1831
2050
|
Input,
|
|
1832
2051
|
{
|
|
1833
2052
|
type: "number",
|
|
@@ -1842,7 +2061,7 @@ function ExpandOptionsPanel({
|
|
|
1842
2061
|
}
|
|
1843
2062
|
)
|
|
1844
2063
|
] }),
|
|
1845
|
-
/* @__PURE__ */
|
|
2064
|
+
/* @__PURE__ */ jsx7(
|
|
1846
2065
|
"button",
|
|
1847
2066
|
{
|
|
1848
2067
|
ref: expandBtnRef,
|
|
@@ -1860,7 +2079,7 @@ function ExpandOptionsPanel({
|
|
|
1860
2079
|
var ExpandOptionsPanel_default = ExpandOptionsPanel;
|
|
1861
2080
|
|
|
1862
2081
|
// src/components/rea/ReaContextMenu.tsx
|
|
1863
|
-
import { jsx as
|
|
2082
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1864
2083
|
function ReaContextMenu({
|
|
1865
2084
|
data,
|
|
1866
2085
|
onClose,
|
|
@@ -1869,8 +2088,8 @@ function ReaContextMenu({
|
|
|
1869
2088
|
onCopyNode,
|
|
1870
2089
|
getNodeEdgeTypes
|
|
1871
2090
|
}) {
|
|
1872
|
-
const [showExpandPanel, setShowExpandPanel] =
|
|
1873
|
-
|
|
2091
|
+
const [showExpandPanel, setShowExpandPanel] = useState5(false);
|
|
2092
|
+
React4.useEffect(() => {
|
|
1874
2093
|
const handleKey = (e) => {
|
|
1875
2094
|
if (e.key === "Escape") onClose();
|
|
1876
2095
|
};
|
|
@@ -1980,7 +2199,7 @@ function ReaContextMenu({
|
|
|
1980
2199
|
const menuItems = elementType === "node" ? nodeMenuItems : edgeMenuItems;
|
|
1981
2200
|
if (showExpandPanel && elementType === "node") {
|
|
1982
2201
|
const nodeLabel = data.label || data.id;
|
|
1983
|
-
return /* @__PURE__ */
|
|
2202
|
+
return /* @__PURE__ */ jsx8(
|
|
1984
2203
|
ExpandOptionsPanel_default,
|
|
1985
2204
|
{
|
|
1986
2205
|
nodeLabel: String(nodeLabel),
|
|
@@ -1990,9 +2209,9 @@ function ReaContextMenu({
|
|
|
1990
2209
|
}
|
|
1991
2210
|
);
|
|
1992
2211
|
}
|
|
1993
|
-
return /* @__PURE__ */
|
|
1994
|
-
menuItems.map((item, index) => /* @__PURE__ */
|
|
1995
|
-
/* @__PURE__ */
|
|
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(
|
|
1996
2215
|
"button",
|
|
1997
2216
|
{
|
|
1998
2217
|
className: "w-full text-left px-3 py-2 text-sm hover:bg-muted/50 cursor-pointer",
|
|
@@ -2000,10 +2219,10 @@ function ReaContextMenu({
|
|
|
2000
2219
|
children: item.label
|
|
2001
2220
|
}
|
|
2002
2221
|
),
|
|
2003
|
-
index === 1 && /* @__PURE__ */
|
|
2222
|
+
index === 1 && /* @__PURE__ */ jsx8("div", { className: "border-t border-border my-1" })
|
|
2004
2223
|
] }, item.label)),
|
|
2005
|
-
/* @__PURE__ */
|
|
2006
|
-
/* @__PURE__ */
|
|
2224
|
+
/* @__PURE__ */ jsx8("div", { className: "border-t border-border my-1" }),
|
|
2225
|
+
/* @__PURE__ */ jsx8(
|
|
2007
2226
|
"button",
|
|
2008
2227
|
{
|
|
2009
2228
|
className: "w-full text-left px-3 py-1.5 text-xs text-muted-foreground hover:bg-muted/50 cursor-pointer",
|
|
@@ -2017,7 +2236,7 @@ var ReaContextMenu_default = ReaContextMenu;
|
|
|
2017
2236
|
|
|
2018
2237
|
// src/components/rea/ReaGraph.tsx
|
|
2019
2238
|
import { toast as toast2 } from "sonner";
|
|
2020
|
-
import { jsx as
|
|
2239
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2021
2240
|
function findElementInGraphData(elementId, elementType, nodes, edges) {
|
|
2022
2241
|
if (elementType === "node") {
|
|
2023
2242
|
const node = nodes.find((n) => n.id === elementId);
|
|
@@ -2084,24 +2303,25 @@ function ReaGraph({
|
|
|
2084
2303
|
extraToolbarContent,
|
|
2085
2304
|
getNodeEdgeTypes
|
|
2086
2305
|
}) {
|
|
2087
|
-
const isMountedRef =
|
|
2306
|
+
const isMountedRef = useRef4(false);
|
|
2088
2307
|
const { nodes, edges } = useReagraphData();
|
|
2089
2308
|
const { normalized } = useLiveGraphData();
|
|
2090
|
-
|
|
2309
|
+
useEffect4(() => {
|
|
2091
2310
|
devLog8("[ReGraph] counts", { nodes: nodes.length, edges: edges.length });
|
|
2092
2311
|
if (nodes.length > 0) devLog8("[ReGraph] sample node", nodes[0]);
|
|
2093
2312
|
if (edges.length > 0) devLog8("[ReGraph] sample edge", edges[0]);
|
|
2094
2313
|
}, [nodes, edges]);
|
|
2095
|
-
const canvas =
|
|
2096
|
-
const [hoveredNode, setHoveredNode] =
|
|
2314
|
+
const canvas = useRef4(null);
|
|
2315
|
+
const [hoveredNode, setHoveredNode] = useState6(
|
|
2097
2316
|
void 0
|
|
2098
2317
|
);
|
|
2099
|
-
const [layout, setLayout] =
|
|
2100
|
-
const [fullscreen, setFullscreen] =
|
|
2101
|
-
const [pathSelectionType, setPathSelectionType] =
|
|
2318
|
+
const [layout, setLayout] = useState6("forceDirected2d");
|
|
2319
|
+
const [fullscreen, setFullscreen] = useState6(false);
|
|
2320
|
+
const [pathSelectionType, setPathSelectionType] = useState6("direct");
|
|
2102
2321
|
const useGraphSelection = useWorkspaceSelection();
|
|
2103
2322
|
const useGraphFilter2 = useWorkspaceGraphFilter();
|
|
2104
2323
|
const useGraphExploration = useWorkspaceGraphExploration();
|
|
2324
|
+
const useGraphFocus2 = useWorkspaceGraphFocus();
|
|
2105
2325
|
const hiddenElementIds = useGraphExploration((s) => s.hiddenElementIds);
|
|
2106
2326
|
const isFocusMode = useGraphExploration((s) => s.isFocusMode);
|
|
2107
2327
|
const hasHiddenElements = hiddenElementIds.size > 0;
|
|
@@ -2124,8 +2344,8 @@ function ReaGraph({
|
|
|
2124
2344
|
pathSelectionType,
|
|
2125
2345
|
type: "multi"
|
|
2126
2346
|
});
|
|
2127
|
-
|
|
2128
|
-
const unsubscribe =
|
|
2347
|
+
useEffect4(() => {
|
|
2348
|
+
const unsubscribe = useGraphFocus2.subscribe((state) => {
|
|
2129
2349
|
const focusAction = state.focusAction;
|
|
2130
2350
|
if (!focusAction || !canvas.current) return;
|
|
2131
2351
|
devLog8("[ReGraph] Processing focus action", {
|
|
@@ -2143,17 +2363,17 @@ function ReaGraph({
|
|
|
2143
2363
|
setSelectedEdge
|
|
2144
2364
|
);
|
|
2145
2365
|
if (handled) {
|
|
2146
|
-
|
|
2366
|
+
useGraphFocus2.setState({ focusAction: null });
|
|
2147
2367
|
}
|
|
2148
2368
|
});
|
|
2149
2369
|
return unsubscribe;
|
|
2150
|
-
}, [nodes, edges, setSelectedNodeIds, setSelectedEdge]);
|
|
2151
|
-
|
|
2370
|
+
}, [nodes, edges, setSelectedNodeIds, setSelectedEdge, useGraphFocus2]);
|
|
2371
|
+
useEffect4(() => {
|
|
2152
2372
|
if (!canvas.current) return;
|
|
2153
2373
|
clearSelections();
|
|
2154
2374
|
clearSelectionStore();
|
|
2155
2375
|
}, [lastDataChangeTime, clearSelections, clearSelectionStore]);
|
|
2156
|
-
const fitToScreen =
|
|
2376
|
+
const fitToScreen = useCallback5(
|
|
2157
2377
|
(reason) => {
|
|
2158
2378
|
if (!isMountedRef.current) return;
|
|
2159
2379
|
if (nodes.length === 0) return;
|
|
@@ -2179,7 +2399,7 @@ function ReaGraph({
|
|
|
2179
2399
|
},
|
|
2180
2400
|
[nodes.length]
|
|
2181
2401
|
);
|
|
2182
|
-
|
|
2402
|
+
useEffect4(() => {
|
|
2183
2403
|
isMountedRef.current = true;
|
|
2184
2404
|
fitToScreen("mount");
|
|
2185
2405
|
const state = useGraphSelection.getState();
|
|
@@ -2199,9 +2419,9 @@ function ReaGraph({
|
|
|
2199
2419
|
isMountedRef.current = false;
|
|
2200
2420
|
};
|
|
2201
2421
|
}, [fitToScreen, setSelections, setSelectedEdge, useGraphSelection]);
|
|
2202
|
-
const selectionsRef =
|
|
2422
|
+
const selectionsRef = useRef4(selections);
|
|
2203
2423
|
selectionsRef.current = selections;
|
|
2204
|
-
|
|
2424
|
+
useEffect4(() => {
|
|
2205
2425
|
const inSync = (nodeIds) => {
|
|
2206
2426
|
const cur = selectionsRef.current;
|
|
2207
2427
|
return cur.length === nodeIds.length && nodeIds.every((id) => cur.includes(id));
|
|
@@ -2213,7 +2433,7 @@ function ReaGraph({
|
|
|
2213
2433
|
});
|
|
2214
2434
|
return unsubscribe;
|
|
2215
2435
|
}, [useGraphSelection, setSelections]);
|
|
2216
|
-
|
|
2436
|
+
useEffect4(() => {
|
|
2217
2437
|
if (isMountedRef.current) fitToScreen("counts-changed");
|
|
2218
2438
|
}, [nodes.length, edges.length, fitToScreen]);
|
|
2219
2439
|
function updateLayout(next) {
|
|
@@ -2222,7 +2442,13 @@ function ReaGraph({
|
|
|
2222
2442
|
}
|
|
2223
2443
|
function handleNodeClick(node) {
|
|
2224
2444
|
setSelectedEdge(null);
|
|
2225
|
-
if (
|
|
2445
|
+
if (!onNodeClick) return;
|
|
2446
|
+
const alreadySelected = selections.includes(node.id);
|
|
2447
|
+
if (alreadySelected || selections.length < MAX_NODE_SELECTION) {
|
|
2448
|
+
onNodeClick(node);
|
|
2449
|
+
return;
|
|
2450
|
+
}
|
|
2451
|
+
setSelections(evictToMax(selections, node.id));
|
|
2226
2452
|
}
|
|
2227
2453
|
function handleEdgeClick(edge) {
|
|
2228
2454
|
const startNode = normalized.nodesById[edge.data.start_id_ + ""];
|
|
@@ -2255,14 +2481,12 @@ function ReaGraph({
|
|
|
2255
2481
|
setSelectedEdge(null);
|
|
2256
2482
|
if (onCanvasClick) onCanvasClick(e);
|
|
2257
2483
|
}
|
|
2258
|
-
|
|
2484
|
+
useEffect4(() => {
|
|
2259
2485
|
if (!isMountedRef.current) return;
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
setSelectedNodeIds(selectedNodes.map((n) => n.id));
|
|
2263
|
-
}
|
|
2486
|
+
const selectedNodes = selections.map((id) => nodes.find((n) => n.id === id)).filter(Boolean);
|
|
2487
|
+
setSelectedNodeIds(selectedNodes.map((n) => n.id));
|
|
2264
2488
|
}, [selections, nodes]);
|
|
2265
|
-
|
|
2489
|
+
useEffect4(() => {
|
|
2266
2490
|
if (!isMountedRef.current) return;
|
|
2267
2491
|
const currentSelectedIds = useGraphSelection.getState().selectedNodeIds;
|
|
2268
2492
|
if (!currentSelectedIds.length) return;
|
|
@@ -2281,7 +2505,7 @@ function ReaGraph({
|
|
|
2281
2505
|
setSelectedEdge,
|
|
2282
2506
|
useGraphSelection
|
|
2283
2507
|
]);
|
|
2284
|
-
|
|
2508
|
+
useEffect4(() => {
|
|
2285
2509
|
if (!isMountedRef.current) return;
|
|
2286
2510
|
const currentSelectedIds = useGraphSelection.getState().selectedNodeIds;
|
|
2287
2511
|
if (!currentSelectedIds.length) return;
|
|
@@ -2293,7 +2517,7 @@ function ReaGraph({
|
|
|
2293
2517
|
if (stillPresentIds.length < 2) setSelectedEdge(null);
|
|
2294
2518
|
}
|
|
2295
2519
|
}, [nodes, setSelectedNodeIds, setSelectedEdge, useGraphSelection]);
|
|
2296
|
-
|
|
2520
|
+
useEffect4(() => {
|
|
2297
2521
|
if (!fullscreen) return;
|
|
2298
2522
|
const prevOverflow = document.body.style.overflow;
|
|
2299
2523
|
document.body.style.overflow = "hidden";
|
|
@@ -2307,7 +2531,7 @@ function ReaGraph({
|
|
|
2307
2531
|
window.removeEventListener("keydown", handleKey);
|
|
2308
2532
|
};
|
|
2309
2533
|
}, [fullscreen, fitToScreen]);
|
|
2310
|
-
|
|
2534
|
+
useEffect4(() => {
|
|
2311
2535
|
if (!fullscreen) return;
|
|
2312
2536
|
const id = setTimeout(() => {
|
|
2313
2537
|
if (canvas.current) {
|
|
@@ -2316,7 +2540,7 @@ function ReaGraph({
|
|
|
2316
2540
|
}, 50);
|
|
2317
2541
|
return () => clearTimeout(id);
|
|
2318
2542
|
}, [fullscreen, fitToScreen]);
|
|
2319
|
-
|
|
2543
|
+
useEffect4(() => {
|
|
2320
2544
|
if (!isMountedRef.current) return;
|
|
2321
2545
|
const handleResize = () => {
|
|
2322
2546
|
if (isMountedRef.current && canvas.current) {
|
|
@@ -2328,8 +2552,8 @@ function ReaGraph({
|
|
|
2328
2552
|
window.removeEventListener("resize", handleResize);
|
|
2329
2553
|
};
|
|
2330
2554
|
}, [fitToScreen]);
|
|
2331
|
-
const firstDataRef =
|
|
2332
|
-
|
|
2555
|
+
const firstDataRef = useRef4(true);
|
|
2556
|
+
useEffect4(() => {
|
|
2333
2557
|
if (!isMountedRef.current) return;
|
|
2334
2558
|
if (nodes.length > 0) {
|
|
2335
2559
|
if (firstDataRef.current) {
|
|
@@ -2342,18 +2566,18 @@ function ReaGraph({
|
|
|
2342
2566
|
}, [nodes.length, fitToScreen]);
|
|
2343
2567
|
if (nodes.length === 0) {
|
|
2344
2568
|
devLog8("[ReGraph] empty state render");
|
|
2345
|
-
return /* @__PURE__ */
|
|
2346
|
-
/* @__PURE__ */
|
|
2347
|
-
/* @__PURE__ */
|
|
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: [
|
|
2348
2572
|
"Please",
|
|
2349
2573
|
" ",
|
|
2350
|
-
/* @__PURE__ */
|
|
2574
|
+
/* @__PURE__ */ jsx9("span", { className: "font-semibold", children: "run a query, check filters, or add a node" }),
|
|
2351
2575
|
" ",
|
|
2352
2576
|
"to the workbench to populate the graph."
|
|
2353
2577
|
] })
|
|
2354
2578
|
] });
|
|
2355
2579
|
}
|
|
2356
|
-
return /* @__PURE__ */
|
|
2580
|
+
return /* @__PURE__ */ jsxs8(
|
|
2357
2581
|
"div",
|
|
2358
2582
|
{
|
|
2359
2583
|
className: cn(
|
|
@@ -2361,7 +2585,7 @@ function ReaGraph({
|
|
|
2361
2585
|
fullscreen ? "fixed inset-0 z-50 w-screen h-screen p-0 m-0" : "relative w-full h-full"
|
|
2362
2586
|
),
|
|
2363
2587
|
children: [
|
|
2364
|
-
/* @__PURE__ */
|
|
2588
|
+
/* @__PURE__ */ jsx9(Card2, { className: "h-full shadow-xs p-4", children: /* @__PURE__ */ jsx9(
|
|
2365
2589
|
GraphCanvas,
|
|
2366
2590
|
{
|
|
2367
2591
|
ref: canvas,
|
|
@@ -2386,7 +2610,7 @@ function ReaGraph({
|
|
|
2386
2610
|
elementId: data.id,
|
|
2387
2611
|
elementType
|
|
2388
2612
|
});
|
|
2389
|
-
return /* @__PURE__ */
|
|
2613
|
+
return /* @__PURE__ */ jsx9(
|
|
2390
2614
|
ReaContextMenu_default,
|
|
2391
2615
|
{
|
|
2392
2616
|
data,
|
|
@@ -2400,8 +2624,8 @@ function ReaGraph({
|
|
|
2400
2624
|
}
|
|
2401
2625
|
}
|
|
2402
2626
|
) }),
|
|
2403
|
-
hoveredNode && /* @__PURE__ */
|
|
2404
|
-
/* @__PURE__ */
|
|
2627
|
+
hoveredNode && /* @__PURE__ */ jsx9(NodeHoverInfo_default, { node: hoveredNode }),
|
|
2628
|
+
/* @__PURE__ */ jsx9(
|
|
2405
2629
|
LayoutAndCameraControls_default,
|
|
2406
2630
|
{
|
|
2407
2631
|
fitToScreen,
|
|
@@ -2428,10 +2652,10 @@ var ReaGraph_default = ReaGraph;
|
|
|
2428
2652
|
|
|
2429
2653
|
// src/components/cyto/CytoscapeGraph.tsx
|
|
2430
2654
|
import {
|
|
2431
|
-
useEffect as
|
|
2432
|
-
useRef as
|
|
2433
|
-
useState as
|
|
2434
|
-
useCallback as
|
|
2655
|
+
useEffect as useEffect5,
|
|
2656
|
+
useRef as useRef5,
|
|
2657
|
+
useState as useState7,
|
|
2658
|
+
useCallback as useCallback6
|
|
2435
2659
|
} from "react";
|
|
2436
2660
|
import { devLog as devLog10, errorLog as errorLog3 } from "@petrarca/sonnet-core";
|
|
2437
2661
|
import cytoscape from "cytoscape";
|
|
@@ -2445,17 +2669,17 @@ import cxtmenu from "cytoscape-cxtmenu";
|
|
|
2445
2669
|
|
|
2446
2670
|
// src/components/cyto/CytoscapeControls.tsx
|
|
2447
2671
|
import {
|
|
2448
|
-
Button as
|
|
2449
|
-
DropdownMenu as
|
|
2450
|
-
DropdownMenuContent as
|
|
2451
|
-
DropdownMenuItem as
|
|
2672
|
+
Button as Button5,
|
|
2673
|
+
DropdownMenu as DropdownMenu4,
|
|
2674
|
+
DropdownMenuContent as DropdownMenuContent4,
|
|
2675
|
+
DropdownMenuItem as DropdownMenuItem4,
|
|
2452
2676
|
DropdownMenuLabel as DropdownMenuLabel2,
|
|
2453
|
-
DropdownMenuSeparator as
|
|
2454
|
-
DropdownMenuTrigger as
|
|
2455
|
-
SimpleTooltip as
|
|
2677
|
+
DropdownMenuSeparator as DropdownMenuSeparator3,
|
|
2678
|
+
DropdownMenuTrigger as DropdownMenuTrigger4,
|
|
2679
|
+
SimpleTooltip as SimpleTooltip4
|
|
2456
2680
|
} from "@petrarca/sonnet-ui";
|
|
2457
2681
|
import { LayoutPanelTop as LayoutPanelTop2, Monitor, Frame, Eye as Eye2, Minus } from "lucide-react";
|
|
2458
|
-
import { Fragment as Fragment2, jsx as
|
|
2682
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2459
2683
|
function ViewControls2({
|
|
2460
2684
|
zoomIn,
|
|
2461
2685
|
zoomOut,
|
|
@@ -2466,17 +2690,17 @@ function ViewControls2({
|
|
|
2466
2690
|
isFocusMode,
|
|
2467
2691
|
onResetView
|
|
2468
2692
|
}) {
|
|
2469
|
-
return /* @__PURE__ */
|
|
2470
|
-
/* @__PURE__ */
|
|
2471
|
-
/* @__PURE__ */
|
|
2472
|
-
/* @__PURE__ */
|
|
2473
|
-
/* @__PURE__ */
|
|
2474
|
-
(hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */
|
|
2475
|
-
|
|
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,
|
|
2476
2700
|
{
|
|
2477
2701
|
label: isFocusMode ? "Reset Focus" : "Show All Hidden",
|
|
2478
2702
|
side: "left",
|
|
2479
|
-
children: /* @__PURE__ */
|
|
2703
|
+
children: /* @__PURE__ */ jsx10(Button5, { variant: "ghost", size: "icon", onClick: onResetView, children: /* @__PURE__ */ jsx10(Eye2, {}) })
|
|
2480
2704
|
}
|
|
2481
2705
|
)
|
|
2482
2706
|
] });
|
|
@@ -2511,13 +2735,13 @@ var LAYOUT_GROUPS = [
|
|
|
2511
2735
|
}
|
|
2512
2736
|
];
|
|
2513
2737
|
function LayoutDropdown2({ currentLayout, updateLayout }) {
|
|
2514
|
-
return /* @__PURE__ */
|
|
2515
|
-
/* @__PURE__ */
|
|
2516
|
-
/* @__PURE__ */
|
|
2517
|
-
groupIdx > 0 && /* @__PURE__ */
|
|
2518
|
-
/* @__PURE__ */
|
|
2519
|
-
group.items.map((item) => /* @__PURE__ */
|
|
2520
|
-
|
|
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,
|
|
2521
2745
|
{
|
|
2522
2746
|
onClick: () => updateLayout(item.layout),
|
|
2523
2747
|
className: currentLayout === item.layout ? "text-blue-600" : "",
|
|
@@ -2541,9 +2765,9 @@ function CytoscapeControls({
|
|
|
2541
2765
|
onResetView,
|
|
2542
2766
|
extraToolbarContent
|
|
2543
2767
|
}) {
|
|
2544
|
-
return /* @__PURE__ */
|
|
2545
|
-
/* @__PURE__ */
|
|
2546
|
-
/* @__PURE__ */
|
|
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(
|
|
2547
2771
|
ViewControls2,
|
|
2548
2772
|
{
|
|
2549
2773
|
zoomIn,
|
|
@@ -2557,7 +2781,7 @@ function CytoscapeControls({
|
|
|
2557
2781
|
}
|
|
2558
2782
|
),
|
|
2559
2783
|
extraToolbarContent,
|
|
2560
|
-
/* @__PURE__ */
|
|
2784
|
+
/* @__PURE__ */ jsx10(
|
|
2561
2785
|
LayoutDropdown2,
|
|
2562
2786
|
{
|
|
2563
2787
|
currentLayout,
|
|
@@ -2997,7 +3221,7 @@ var createEdgeContextMenu = ({
|
|
|
2997
3221
|
|
|
2998
3222
|
// src/components/cyto/CytoscapeGraph.tsx
|
|
2999
3223
|
import { toast as toast4 } from "sonner";
|
|
3000
|
-
import { jsx as
|
|
3224
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3001
3225
|
function CytoscapeExpandOverlay({
|
|
3002
3226
|
expandRequest,
|
|
3003
3227
|
containerRef,
|
|
@@ -3005,7 +3229,7 @@ function CytoscapeExpandOverlay({
|
|
|
3005
3229
|
getNodeEdgeTypes,
|
|
3006
3230
|
onClose
|
|
3007
3231
|
}) {
|
|
3008
|
-
return /* @__PURE__ */
|
|
3232
|
+
return /* @__PURE__ */ jsx11(
|
|
3009
3233
|
"div",
|
|
3010
3234
|
{
|
|
3011
3235
|
className: "absolute z-50",
|
|
@@ -3020,7 +3244,7 @@ function CytoscapeExpandOverlay({
|
|
|
3020
3244
|
),
|
|
3021
3245
|
transform: "translate(-50%, 12px)"
|
|
3022
3246
|
},
|
|
3023
|
-
children: /* @__PURE__ */
|
|
3247
|
+
children: /* @__PURE__ */ jsx11(
|
|
3024
3248
|
ExpandOptionsPanel_default,
|
|
3025
3249
|
{
|
|
3026
3250
|
nodeLabel: expandRequest.nodeLabel,
|
|
@@ -3133,6 +3357,7 @@ function applyLayout({
|
|
|
3133
3357
|
}
|
|
3134
3358
|
}
|
|
3135
3359
|
function processPendingFocus({
|
|
3360
|
+
focusStore,
|
|
3136
3361
|
cy,
|
|
3137
3362
|
pendingFocus,
|
|
3138
3363
|
edgesRef,
|
|
@@ -3157,7 +3382,7 @@ function processPendingFocus({
|
|
|
3157
3382
|
}
|
|
3158
3383
|
);
|
|
3159
3384
|
pendingFocusActionRef.current = null;
|
|
3160
|
-
|
|
3385
|
+
focusStore.setState({ focusAction: null });
|
|
3161
3386
|
return;
|
|
3162
3387
|
}
|
|
3163
3388
|
cy.animate(
|
|
@@ -3177,7 +3402,7 @@ function processPendingFocus({
|
|
|
3177
3402
|
});
|
|
3178
3403
|
}
|
|
3179
3404
|
pendingFocusActionRef.current = null;
|
|
3180
|
-
|
|
3405
|
+
focusStore.setState({ focusAction: null });
|
|
3181
3406
|
}, 100);
|
|
3182
3407
|
}
|
|
3183
3408
|
function selectFocusedElement({
|
|
@@ -3220,6 +3445,7 @@ function selectFocusedElement({
|
|
|
3220
3445
|
}
|
|
3221
3446
|
}
|
|
3222
3447
|
function handleFocusStateChange({
|
|
3448
|
+
focusStore,
|
|
3223
3449
|
focusAction,
|
|
3224
3450
|
cyRef,
|
|
3225
3451
|
edgesRef,
|
|
@@ -3240,6 +3466,7 @@ function handleFocusStateChange({
|
|
|
3240
3466
|
const element = cy.$id(elementId);
|
|
3241
3467
|
if (element.length > 0) {
|
|
3242
3468
|
handleImmediateFocus({
|
|
3469
|
+
focusStore,
|
|
3243
3470
|
cy,
|
|
3244
3471
|
element,
|
|
3245
3472
|
focusAction,
|
|
@@ -3256,7 +3483,7 @@ function handleFocusStateChange({
|
|
|
3256
3483
|
elementType: focusAction.elementType
|
|
3257
3484
|
});
|
|
3258
3485
|
pendingFocusActionRef.current = focusAction;
|
|
3259
|
-
|
|
3486
|
+
focusStore.setState((state) => {
|
|
3260
3487
|
const overrides = new Set(state.overrideElementIds);
|
|
3261
3488
|
overrides.add(elementId);
|
|
3262
3489
|
if (focusAction.elementType === "edge") {
|
|
@@ -3280,6 +3507,7 @@ function handleFocusStateChange({
|
|
|
3280
3507
|
});
|
|
3281
3508
|
}
|
|
3282
3509
|
function handleImmediateFocus({
|
|
3510
|
+
focusStore,
|
|
3283
3511
|
cy,
|
|
3284
3512
|
element,
|
|
3285
3513
|
focusAction,
|
|
@@ -3310,7 +3538,7 @@ function handleImmediateFocus({
|
|
|
3310
3538
|
selectedEdgeRef
|
|
3311
3539
|
});
|
|
3312
3540
|
}
|
|
3313
|
-
|
|
3541
|
+
focusStore.setState({ focusAction: null });
|
|
3314
3542
|
}
|
|
3315
3543
|
function computeGraphViewState({
|
|
3316
3544
|
hasNodes,
|
|
@@ -3328,8 +3556,8 @@ function computeGraphViewState({
|
|
|
3328
3556
|
};
|
|
3329
3557
|
}
|
|
3330
3558
|
function useFullscreen(fitToScreen, cyRef) {
|
|
3331
|
-
const [fullscreen, setFullscreen] =
|
|
3332
|
-
|
|
3559
|
+
const [fullscreen, setFullscreen] = useState7(false);
|
|
3560
|
+
useEffect5(() => {
|
|
3333
3561
|
if (fullscreen) {
|
|
3334
3562
|
const prevOverflow = document.body.style.overflow;
|
|
3335
3563
|
document.body.style.overflow = "hidden";
|
|
@@ -3344,7 +3572,7 @@ function useFullscreen(fitToScreen, cyRef) {
|
|
|
3344
3572
|
};
|
|
3345
3573
|
}
|
|
3346
3574
|
}, [fullscreen, fitToScreen]);
|
|
3347
|
-
|
|
3575
|
+
useEffect5(() => {
|
|
3348
3576
|
if (!fullscreen) return;
|
|
3349
3577
|
const id = setTimeout(() => {
|
|
3350
3578
|
if (cyRef.current) {
|
|
@@ -3357,7 +3585,7 @@ function useFullscreen(fitToScreen, cyRef) {
|
|
|
3357
3585
|
return {
|
|
3358
3586
|
fullscreen,
|
|
3359
3587
|
setFullscreen,
|
|
3360
|
-
toggleFullscreen:
|
|
3588
|
+
toggleFullscreen: useCallback6(() => setFullscreen((f) => !f), [])
|
|
3361
3589
|
};
|
|
3362
3590
|
}
|
|
3363
3591
|
function setupCytoscapeEventHandlers({
|
|
@@ -3400,16 +3628,14 @@ function setupCytoscapeEventHandlers({
|
|
|
3400
3628
|
updateSelectedEdge(null);
|
|
3401
3629
|
}
|
|
3402
3630
|
let selectedIds = evtCy.$("node:selected").map((e) => e.id());
|
|
3403
|
-
if (selectedIds.length >
|
|
3404
|
-
const
|
|
3405
|
-
const updatedOrder = [...currentOrder.filter((cid) => cid !== id), id];
|
|
3406
|
-
const keep = updatedOrder.slice(-2);
|
|
3631
|
+
if (selectedIds.length > MAX_NODE_SELECTION) {
|
|
3632
|
+
const keep = evictToMax(selectedNodeIdsRef.current, id);
|
|
3407
3633
|
evtCy.batch(() => {
|
|
3408
3634
|
evtCy.$("node:selected").unselect();
|
|
3409
3635
|
keep.forEach((kid) => evtCy.$id(kid).select());
|
|
3410
3636
|
});
|
|
3411
3637
|
selectedIds = keep;
|
|
3412
|
-
devLog10("[CytoscapeGraph] enforced max
|
|
3638
|
+
devLog10("[CytoscapeGraph] enforced max selection after select", { keep });
|
|
3413
3639
|
}
|
|
3414
3640
|
updateSelectedNodeIds(selectedIds);
|
|
3415
3641
|
});
|
|
@@ -3473,16 +3699,16 @@ function CytoscapeGraph({
|
|
|
3473
3699
|
extraToolbarContent,
|
|
3474
3700
|
getNodeEdgeTypes
|
|
3475
3701
|
}) {
|
|
3476
|
-
const containerRef =
|
|
3477
|
-
const cyRef =
|
|
3478
|
-
const isInitializedRef =
|
|
3479
|
-
const [currentLayout, setCurrentLayout] =
|
|
3480
|
-
const [expandRequest, setExpandRequest] =
|
|
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(
|
|
3481
3707
|
null
|
|
3482
3708
|
);
|
|
3483
|
-
const nodesRef =
|
|
3484
|
-
const edgesRef =
|
|
3485
|
-
const pendingFocusActionRef =
|
|
3709
|
+
const nodesRef = useRef5([]);
|
|
3710
|
+
const edgesRef = useRef5([]);
|
|
3711
|
+
const pendingFocusActionRef = useRef5(null);
|
|
3486
3712
|
const cytoData = useCytoscapeData();
|
|
3487
3713
|
const live = useLiveGraphData();
|
|
3488
3714
|
const nodes = live.nodes;
|
|
@@ -3491,6 +3717,7 @@ function CytoscapeGraph({
|
|
|
3491
3717
|
const useGraphFilter2 = useWorkspaceGraphFilter();
|
|
3492
3718
|
const useGraphExploration = useWorkspaceGraphExploration();
|
|
3493
3719
|
const useSelection3 = useWorkspaceSelection();
|
|
3720
|
+
const useGraphFocus2 = useWorkspaceGraphFocus();
|
|
3494
3721
|
const lastDataChangeTime = useGraphFilter2((s) => s.lastDataChangeTime);
|
|
3495
3722
|
const resetView = useGraphExploration((s) => s.resetView);
|
|
3496
3723
|
const hasHiddenElements = useGraphExploration(
|
|
@@ -3502,13 +3729,13 @@ function CytoscapeGraph({
|
|
|
3502
3729
|
setSelectedEdge: setSelectedEdgeStore,
|
|
3503
3730
|
clear: clearSelectionStore
|
|
3504
3731
|
} = useSelection3.getState();
|
|
3505
|
-
const selectedNodeIdsRef =
|
|
3732
|
+
const selectedNodeIdsRef = useRef5(
|
|
3506
3733
|
useSelection3.getState().selectedNodeIds
|
|
3507
3734
|
);
|
|
3508
|
-
const selectedEdgeRef =
|
|
3735
|
+
const selectedEdgeRef = useRef5(
|
|
3509
3736
|
useSelection3.getState().selectedEdge
|
|
3510
3737
|
);
|
|
3511
|
-
|
|
3738
|
+
useEffect5(() => {
|
|
3512
3739
|
const applyStoreSelection = (nodeIds, edge) => {
|
|
3513
3740
|
selectedNodeIdsRef.current = nodeIds;
|
|
3514
3741
|
selectedEdgeRef.current = edge;
|
|
@@ -3548,13 +3775,13 @@ function CytoscapeGraph({
|
|
|
3548
3775
|
});
|
|
3549
3776
|
return unsubscribe;
|
|
3550
3777
|
}, [useSelection3]);
|
|
3551
|
-
|
|
3778
|
+
useEffect5(() => {
|
|
3552
3779
|
nodesRef.current = nodes;
|
|
3553
3780
|
}, [nodes]);
|
|
3554
|
-
|
|
3781
|
+
useEffect5(() => {
|
|
3555
3782
|
edgesRef.current = edges;
|
|
3556
3783
|
}, [edges]);
|
|
3557
|
-
|
|
3784
|
+
useEffect5(() => {
|
|
3558
3785
|
if (!containerRef.current || cyRef.current) return;
|
|
3559
3786
|
isInitializedRef.current = true;
|
|
3560
3787
|
cyRef.current = cytoscape({
|
|
@@ -3717,10 +3944,10 @@ function CytoscapeGraph({
|
|
|
3717
3944
|
cyRef.current = null;
|
|
3718
3945
|
};
|
|
3719
3946
|
}, []);
|
|
3720
|
-
const elementIdsRef =
|
|
3721
|
-
const lastLayoutRef =
|
|
3722
|
-
const lastFitPaddingRef =
|
|
3723
|
-
|
|
3947
|
+
const elementIdsRef = useRef5("");
|
|
3948
|
+
const lastLayoutRef = useRef5("");
|
|
3949
|
+
const lastFitPaddingRef = useRef5(fitPadding);
|
|
3950
|
+
useEffect5(() => {
|
|
3724
3951
|
const cy = cyRef.current;
|
|
3725
3952
|
if (!cy || !isInitializedRef.current) return;
|
|
3726
3953
|
const currentIds = nodes.map((n) => n.id_).sort().join(",") + "|" + edges.map((e) => e.id_).sort().join(",");
|
|
@@ -3782,6 +4009,7 @@ function CytoscapeGraph({
|
|
|
3782
4009
|
const pendingFocus = pendingFocusActionRef.current;
|
|
3783
4010
|
if (pendingFocus) {
|
|
3784
4011
|
processPendingFocus({
|
|
4012
|
+
focusStore: useGraphFocus2,
|
|
3785
4013
|
cy,
|
|
3786
4014
|
pendingFocus,
|
|
3787
4015
|
edgesRef,
|
|
@@ -3793,14 +4021,15 @@ function CytoscapeGraph({
|
|
|
3793
4021
|
});
|
|
3794
4022
|
}
|
|
3795
4023
|
}, [nodes, edges, currentLayout, fitPadding]);
|
|
3796
|
-
|
|
4024
|
+
useEffect5(() => {
|
|
3797
4025
|
if (!cyRef.current) return;
|
|
3798
4026
|
cyRef.current.elements().unselect();
|
|
3799
4027
|
clearSelectionStore();
|
|
3800
4028
|
}, [lastDataChangeTime, clearSelectionStore]);
|
|
3801
|
-
|
|
3802
|
-
const unsubscribe =
|
|
4029
|
+
useEffect5(() => {
|
|
4030
|
+
const unsubscribe = useGraphFocus2.subscribe((state) => {
|
|
3803
4031
|
handleFocusStateChange({
|
|
4032
|
+
focusStore: useGraphFocus2,
|
|
3804
4033
|
focusAction: state.focusAction,
|
|
3805
4034
|
cyRef,
|
|
3806
4035
|
edgesRef,
|
|
@@ -3813,19 +4042,24 @@ function CytoscapeGraph({
|
|
|
3813
4042
|
});
|
|
3814
4043
|
});
|
|
3815
4044
|
return unsubscribe;
|
|
3816
|
-
}, [
|
|
4045
|
+
}, [
|
|
4046
|
+
baseNormalized,
|
|
4047
|
+
setSelectedEdgeStore,
|
|
4048
|
+
setSelectedNodeIdsStore,
|
|
4049
|
+
useGraphFocus2
|
|
4050
|
+
]);
|
|
3817
4051
|
const updateLayout = (newLayout) => {
|
|
3818
4052
|
setCurrentLayout(newLayout);
|
|
3819
4053
|
};
|
|
3820
|
-
const fitToScreen =
|
|
4054
|
+
const fitToScreen = useCallback6(() => {
|
|
3821
4055
|
if (cyRef.current) {
|
|
3822
4056
|
cyRef.current.fit(void 0, fitPadding);
|
|
3823
4057
|
}
|
|
3824
4058
|
}, [fitPadding]);
|
|
3825
|
-
|
|
4059
|
+
useEffect5(() => {
|
|
3826
4060
|
fitToScreen();
|
|
3827
4061
|
}, [nodes.length, fitToScreen]);
|
|
3828
|
-
|
|
4062
|
+
useEffect5(() => {
|
|
3829
4063
|
if (!isInitializedRef.current) return;
|
|
3830
4064
|
const handleResize = () => cyRef.current?.resize();
|
|
3831
4065
|
window.addEventListener("resize", handleResize);
|
|
@@ -3844,9 +4078,9 @@ function CytoscapeGraph({
|
|
|
3844
4078
|
isFocusMode,
|
|
3845
4079
|
fullscreen
|
|
3846
4080
|
});
|
|
3847
|
-
return /* @__PURE__ */
|
|
3848
|
-
/* @__PURE__ */
|
|
3849
|
-
expandRequest && /* @__PURE__ */
|
|
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(
|
|
3850
4084
|
CytoscapeExpandOverlay,
|
|
3851
4085
|
{
|
|
3852
4086
|
expandRequest,
|
|
@@ -3856,9 +4090,9 @@ function CytoscapeGraph({
|
|
|
3856
4090
|
onClose: () => setExpandRequest(null)
|
|
3857
4091
|
}
|
|
3858
4092
|
),
|
|
3859
|
-
showEmptyMessage && /* @__PURE__ */
|
|
3860
|
-
showFilteredMessage && /* @__PURE__ */
|
|
3861
|
-
showControls && /* @__PURE__ */
|
|
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(
|
|
3862
4096
|
CytoscapeControls_default,
|
|
3863
4097
|
{
|
|
3864
4098
|
updateLayout,
|
|
@@ -3906,15 +4140,15 @@ function CytoscapeGraph({
|
|
|
3906
4140
|
var CytoscapeGraph_default = CytoscapeGraph;
|
|
3907
4141
|
|
|
3908
4142
|
// src/components/GraphVisualizer.tsx
|
|
3909
|
-
import { jsx as
|
|
4143
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3910
4144
|
function GraphVisualizer({
|
|
3911
4145
|
onCopyNode,
|
|
3912
4146
|
extraToolbarContent,
|
|
3913
4147
|
getNodeEdgeTypes
|
|
3914
4148
|
}) {
|
|
3915
4149
|
const { renderer } = useGraphRenderer();
|
|
3916
|
-
return /* @__PURE__ */
|
|
3917
|
-
renderer === "reagraph" && /* @__PURE__ */
|
|
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(
|
|
3918
4152
|
ReaGraph_default,
|
|
3919
4153
|
{
|
|
3920
4154
|
onCopyNode,
|
|
@@ -3923,7 +4157,7 @@ function GraphVisualizer({
|
|
|
3923
4157
|
},
|
|
3924
4158
|
"reagraph"
|
|
3925
4159
|
),
|
|
3926
|
-
renderer === "cytoscape" && /* @__PURE__ */
|
|
4160
|
+
renderer === "cytoscape" && /* @__PURE__ */ jsx12(
|
|
3927
4161
|
CytoscapeGraph_default,
|
|
3928
4162
|
{
|
|
3929
4163
|
onCopyNode,
|
|
@@ -3940,7 +4174,7 @@ var GraphVisualizer_default = GraphVisualizer;
|
|
|
3940
4174
|
import { Card as Card5 } from "@petrarca/sonnet-ui";
|
|
3941
4175
|
|
|
3942
4176
|
// src/components/ActionPanel/NodeInfoCard.tsx
|
|
3943
|
-
import { useState as
|
|
4177
|
+
import { useState as useState8 } from "react";
|
|
3944
4178
|
import {
|
|
3945
4179
|
Card as Card3,
|
|
3946
4180
|
Collapsible,
|
|
@@ -3948,11 +4182,11 @@ import {
|
|
|
3948
4182
|
CollapsibleTrigger,
|
|
3949
4183
|
PropertyList
|
|
3950
4184
|
} from "@petrarca/sonnet-ui";
|
|
3951
|
-
import { copyTextToClipboard } from "@petrarca/sonnet-core";
|
|
3952
|
-
import { jsx as
|
|
4185
|
+
import { copyTextToClipboard as copyTextToClipboard2 } from "@petrarca/sonnet-core";
|
|
4186
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3953
4187
|
function NodeInfoCard({ node }) {
|
|
3954
|
-
const [showProperties, setShowProperties] =
|
|
3955
|
-
return /* @__PURE__ */
|
|
4188
|
+
const [showProperties, setShowProperties] = useState8(false);
|
|
4189
|
+
return /* @__PURE__ */ jsxs12(
|
|
3956
4190
|
Card3,
|
|
3957
4191
|
{
|
|
3958
4192
|
className: "w-full rounded-lg overflow-hidden bg-muted/30 border shadow-none p-3",
|
|
@@ -3960,20 +4194,20 @@ function NodeInfoCard({ node }) {
|
|
|
3960
4194
|
borderLeft: `4px solid ${node.visual.color}`
|
|
3961
4195
|
},
|
|
3962
4196
|
children: [
|
|
3963
|
-
/* @__PURE__ */
|
|
3964
|
-
/* @__PURE__ */
|
|
3965
|
-
/* @__PURE__ */
|
|
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(
|
|
3966
4200
|
"p",
|
|
3967
4201
|
{
|
|
3968
4202
|
className: "text-xs text-muted-foreground cursor-pointer select-all mb-2",
|
|
3969
|
-
onClick: () =>
|
|
4203
|
+
onClick: () => copyTextToClipboard2(String(node.id_)),
|
|
3970
4204
|
title: "Click to copy id",
|
|
3971
4205
|
children: node.id_
|
|
3972
4206
|
}
|
|
3973
4207
|
),
|
|
3974
|
-
/* @__PURE__ */
|
|
3975
|
-
/* @__PURE__ */
|
|
3976
|
-
/* @__PURE__ */
|
|
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 }) }) })
|
|
3977
4211
|
] })
|
|
3978
4212
|
]
|
|
3979
4213
|
}
|
|
@@ -3982,7 +4216,7 @@ function NodeInfoCard({ node }) {
|
|
|
3982
4216
|
var NodeInfoCard_default = NodeInfoCard;
|
|
3983
4217
|
|
|
3984
4218
|
// src/components/ActionPanel/EdgeInfoCard.tsx
|
|
3985
|
-
import { useState as
|
|
4219
|
+
import { useState as useState9 } from "react";
|
|
3986
4220
|
import {
|
|
3987
4221
|
Card as Card4,
|
|
3988
4222
|
Collapsible as Collapsible2,
|
|
@@ -3991,11 +4225,11 @@ import {
|
|
|
3991
4225
|
PropertyList as PropertyList2
|
|
3992
4226
|
} from "@petrarca/sonnet-ui";
|
|
3993
4227
|
import { ArrowDown } from "lucide-react";
|
|
3994
|
-
import { copyTextToClipboard as
|
|
3995
|
-
import { jsx as
|
|
4228
|
+
import { copyTextToClipboard as copyTextToClipboard3 } from "@petrarca/sonnet-core";
|
|
4229
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3996
4230
|
function EdgeInfoCard({ edge }) {
|
|
3997
|
-
const [showProperties, setShowProperties] =
|
|
3998
|
-
return /* @__PURE__ */
|
|
4231
|
+
const [showProperties, setShowProperties] = useState9(false);
|
|
4232
|
+
return /* @__PURE__ */ jsxs13(
|
|
3999
4233
|
Card4,
|
|
4000
4234
|
{
|
|
4001
4235
|
className: "w-full rounded-lg overflow-hidden bg-muted/30 border shadow-none p-3",
|
|
@@ -4003,22 +4237,22 @@ function EdgeInfoCard({ edge }) {
|
|
|
4003
4237
|
borderLeft: `4px solid ${edge.visual.color}`
|
|
4004
4238
|
},
|
|
4005
4239
|
children: [
|
|
4006
|
-
/* @__PURE__ */
|
|
4007
|
-
/* @__PURE__ */
|
|
4008
|
-
/* @__PURE__ */
|
|
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_ })
|
|
4009
4243
|
] }),
|
|
4010
|
-
edge.id_ && /* @__PURE__ */
|
|
4244
|
+
edge.id_ && /* @__PURE__ */ jsx14(
|
|
4011
4245
|
"p",
|
|
4012
4246
|
{
|
|
4013
4247
|
className: "text-xs text-muted-foreground select-all cursor-pointer mt-1 mb-2",
|
|
4014
4248
|
title: "Click to copy edge id",
|
|
4015
|
-
onClick: () =>
|
|
4249
|
+
onClick: () => copyTextToClipboard3(String(edge.id_)),
|
|
4016
4250
|
children: edge.id_
|
|
4017
4251
|
}
|
|
4018
4252
|
),
|
|
4019
|
-
edge.properties_ && Object.keys(edge.properties_).length > 0 && /* @__PURE__ */
|
|
4020
|
-
/* @__PURE__ */
|
|
4021
|
-
/* @__PURE__ */
|
|
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 }) }) })
|
|
4022
4256
|
] })
|
|
4023
4257
|
]
|
|
4024
4258
|
}
|
|
@@ -4030,7 +4264,7 @@ var EdgeInfoCard_default = EdgeInfoCard;
|
|
|
4030
4264
|
import { Badge } from "@petrarca/sonnet-ui";
|
|
4031
4265
|
import { formatNumber, devLog as devLog11 } from "@petrarca/sonnet-core";
|
|
4032
4266
|
import { useMemo as useMemo4 } from "react";
|
|
4033
|
-
import { Fragment as Fragment3, jsx as
|
|
4267
|
+
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4034
4268
|
function computeEdgeToNodeLabels(nodesById, edgesById) {
|
|
4035
4269
|
const mapping = {};
|
|
4036
4270
|
for (const edge of Object.values(edgesById)) {
|
|
@@ -4063,7 +4297,7 @@ function AllToggleBadge({
|
|
|
4063
4297
|
badgeKey
|
|
4064
4298
|
}) {
|
|
4065
4299
|
const bgColor = allDisabled ? colorPalette["faded"] : colorPalette["neutral"];
|
|
4066
|
-
return /* @__PURE__ */
|
|
4300
|
+
return /* @__PURE__ */ jsxs14(
|
|
4067
4301
|
Badge,
|
|
4068
4302
|
{
|
|
4069
4303
|
className: "cursor-pointer text-xs px-2.5 py-0.5",
|
|
@@ -4091,7 +4325,7 @@ function NodeLabelBadges({
|
|
|
4091
4325
|
}) {
|
|
4092
4326
|
return labels.map((label) => {
|
|
4093
4327
|
const bgColor = shown.has(label) ? colorPalette[label] : colorPalette["faded"];
|
|
4094
|
-
return /* @__PURE__ */
|
|
4328
|
+
return /* @__PURE__ */ jsxs14(
|
|
4095
4329
|
Badge,
|
|
4096
4330
|
{
|
|
4097
4331
|
className: "cursor-pointer text-xs px-2.5 py-0.5",
|
|
@@ -4129,7 +4363,7 @@ function EdgeLabelBadges({
|
|
|
4129
4363
|
colorPalette,
|
|
4130
4364
|
"neutral"
|
|
4131
4365
|
);
|
|
4132
|
-
return /* @__PURE__ */
|
|
4366
|
+
return /* @__PURE__ */ jsxs14(
|
|
4133
4367
|
Badge,
|
|
4134
4368
|
{
|
|
4135
4369
|
className: "cursor-pointer text-xs px-2.5 py-0.5",
|
|
@@ -4232,9 +4466,9 @@ function GraphOverview({
|
|
|
4232
4466
|
nodeLabelFilters
|
|
4233
4467
|
);
|
|
4234
4468
|
const handleEdgeToggle = toggleAllEdgeLabelsDisabled;
|
|
4235
|
-
return /* @__PURE__ */
|
|
4236
|
-
/* @__PURE__ */
|
|
4237
|
-
allLabels.length > 0 && /* @__PURE__ */
|
|
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) => {
|
|
4238
4472
|
if (bucket.labels.length === 0) return null;
|
|
4239
4473
|
const bucketAllDisabled = isBucketAllOff(
|
|
4240
4474
|
bucket.labels,
|
|
@@ -4246,10 +4480,10 @@ function GraphOverview({
|
|
|
4246
4480
|
shownNodeLabels,
|
|
4247
4481
|
setNodeFilterState
|
|
4248
4482
|
);
|
|
4249
|
-
return /* @__PURE__ */
|
|
4250
|
-
/* @__PURE__ */
|
|
4251
|
-
/* @__PURE__ */
|
|
4252
|
-
/* @__PURE__ */
|
|
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(
|
|
4253
4487
|
AllToggleBadge,
|
|
4254
4488
|
{
|
|
4255
4489
|
allDisabled: bucketAllDisabled,
|
|
@@ -4259,7 +4493,7 @@ function GraphOverview({
|
|
|
4259
4493
|
badgeKey: `all-${bucket.title}`
|
|
4260
4494
|
}
|
|
4261
4495
|
),
|
|
4262
|
-
/* @__PURE__ */
|
|
4496
|
+
/* @__PURE__ */ jsx15(
|
|
4263
4497
|
NodeLabelBadges,
|
|
4264
4498
|
{
|
|
4265
4499
|
labels: bucket.labels,
|
|
@@ -4277,10 +4511,10 @@ function GraphOverview({
|
|
|
4277
4511
|
] })
|
|
4278
4512
|
] }, bucket.title);
|
|
4279
4513
|
}) }),
|
|
4280
|
-
allEdgeLabels.length > 0 && /* @__PURE__ */
|
|
4281
|
-
/* @__PURE__ */
|
|
4282
|
-
/* @__PURE__ */
|
|
4283
|
-
/* @__PURE__ */
|
|
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(
|
|
4284
4518
|
AllToggleBadge,
|
|
4285
4519
|
{
|
|
4286
4520
|
allDisabled: allEdgeLabelsDisabled,
|
|
@@ -4290,7 +4524,7 @@ function GraphOverview({
|
|
|
4290
4524
|
badgeKey: "edge-all"
|
|
4291
4525
|
}
|
|
4292
4526
|
),
|
|
4293
|
-
/* @__PURE__ */
|
|
4527
|
+
/* @__PURE__ */ jsx15(
|
|
4294
4528
|
EdgeLabelBadges,
|
|
4295
4529
|
{
|
|
4296
4530
|
labels: allEdgeLabels,
|
|
@@ -4316,16 +4550,16 @@ function Overview({ nodeBuckets, edgeSectionTitle } = {}) {
|
|
|
4316
4550
|
);
|
|
4317
4551
|
}, [baseNormalized]);
|
|
4318
4552
|
if (stats && stats.values != null && stats.values > 0) {
|
|
4319
|
-
return /* @__PURE__ */
|
|
4320
|
-
/* @__PURE__ */
|
|
4321
|
-
/* @__PURE__ */
|
|
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: [
|
|
4322
4556
|
formatNumber(stats.values),
|
|
4323
4557
|
" Values"
|
|
4324
4558
|
] })
|
|
4325
4559
|
] });
|
|
4326
4560
|
}
|
|
4327
4561
|
if (stats && stats.nodes > 0 && colorPalette) {
|
|
4328
|
-
return /* @__PURE__ */
|
|
4562
|
+
return /* @__PURE__ */ jsx15(
|
|
4329
4563
|
GraphOverview,
|
|
4330
4564
|
{
|
|
4331
4565
|
stats,
|
|
@@ -4344,7 +4578,7 @@ var Overview_default = Overview;
|
|
|
4344
4578
|
|
|
4345
4579
|
// src/components/ActionPanel/ActionPanel.tsx
|
|
4346
4580
|
import { useMemo as useMemo5 } from "react";
|
|
4347
|
-
import { jsx as
|
|
4581
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4348
4582
|
function ActionPanel({ actions }) {
|
|
4349
4583
|
const useSelection3 = useWorkspaceSelection();
|
|
4350
4584
|
const selectedNodeIds = useSelection3(
|
|
@@ -4360,18 +4594,18 @@ function ActionPanel({ actions }) {
|
|
|
4360
4594
|
const secondNode = selectedNodes?.[1];
|
|
4361
4595
|
const hasEdge = !!selectedEdge;
|
|
4362
4596
|
const showOverview = !firstNode && !hasEdge;
|
|
4363
|
-
return /* @__PURE__ */
|
|
4597
|
+
return /* @__PURE__ */ jsxs15(
|
|
4364
4598
|
Card5,
|
|
4365
4599
|
{
|
|
4366
4600
|
className: "h-full min-w-64 flex flex-col bg-card shadow-none p-4",
|
|
4367
4601
|
style: { maxHeight: "100%", overflow: "hidden" },
|
|
4368
4602
|
children: [
|
|
4369
|
-
actions && /* @__PURE__ */
|
|
4370
|
-
/* @__PURE__ */
|
|
4371
|
-
firstNode && /* @__PURE__ */
|
|
4372
|
-
hasEdge && selectedEdge && /* @__PURE__ */
|
|
4373
|
-
secondNode && /* @__PURE__ */
|
|
4374
|
-
showOverview && /* @__PURE__ */
|
|
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, {})
|
|
4375
4609
|
] }) })
|
|
4376
4610
|
]
|
|
4377
4611
|
}
|
|
@@ -4380,12 +4614,11 @@ function ActionPanel({ actions }) {
|
|
|
4380
4614
|
var ActionPanel_default = ActionPanel;
|
|
4381
4615
|
|
|
4382
4616
|
// src/components/GraphA11yList/GraphA11yList.tsx
|
|
4383
|
-
import { useCallback as
|
|
4617
|
+
import { useCallback as useCallback7, useMemo as useMemo6, useRef as useRef6 } from "react";
|
|
4384
4618
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
4385
4619
|
import { cn as cn2 } from "@petrarca/sonnet-core";
|
|
4386
|
-
import { jsx as
|
|
4620
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
4387
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";
|
|
4388
|
-
var MAX_NODE_SELECTION = 2;
|
|
4389
4622
|
function GraphA11yList({
|
|
4390
4623
|
visible = false,
|
|
4391
4624
|
rowHeight = 28,
|
|
@@ -4419,14 +4652,14 @@ function GraphA11yList({
|
|
|
4419
4652
|
}));
|
|
4420
4653
|
return [...nodeRows, ...edgeRows];
|
|
4421
4654
|
}, [live.nodes, live.edges]);
|
|
4422
|
-
const parentRef =
|
|
4655
|
+
const parentRef = useRef6(null);
|
|
4423
4656
|
const virtualizer = useVirtualizer({
|
|
4424
4657
|
count: rows.length,
|
|
4425
4658
|
getScrollElement: () => parentRef.current,
|
|
4426
4659
|
estimateSize: () => rowHeight,
|
|
4427
4660
|
overscan: 8
|
|
4428
4661
|
});
|
|
4429
|
-
const activateNode =
|
|
4662
|
+
const activateNode = useCallback7(
|
|
4430
4663
|
(id, additive) => {
|
|
4431
4664
|
setSelectedEdge(null);
|
|
4432
4665
|
if (!additive) {
|
|
@@ -4437,11 +4670,11 @@ function GraphA11yList({
|
|
|
4437
4670
|
setSelectedNodeIds(selectedNodeIds.filter((x) => x !== id));
|
|
4438
4671
|
return;
|
|
4439
4672
|
}
|
|
4440
|
-
setSelectedNodeIds(
|
|
4673
|
+
setSelectedNodeIds(evictToMax(selectedNodeIds, id));
|
|
4441
4674
|
},
|
|
4442
4675
|
[selectedNodeIds, setSelectedNodeIds, setSelectedEdge]
|
|
4443
4676
|
);
|
|
4444
|
-
const selectRow =
|
|
4677
|
+
const selectRow = useCallback7(
|
|
4445
4678
|
(row, e) => {
|
|
4446
4679
|
if (row.kind === "node") {
|
|
4447
4680
|
activateNode(row.id, e.metaKey || e.ctrlKey || e.shiftKey);
|
|
@@ -4453,19 +4686,19 @@ function GraphA11yList({
|
|
|
4453
4686
|
[activateNode, setSelectedNodeIds, setSelectedEdge]
|
|
4454
4687
|
);
|
|
4455
4688
|
const isSelected = (row) => row.kind === "node" ? selectedNodeIds.includes(row.id) : selectedEdge?.id_ === row.edge.id_;
|
|
4456
|
-
return /* @__PURE__ */
|
|
4689
|
+
return /* @__PURE__ */ jsx17(
|
|
4457
4690
|
"div",
|
|
4458
4691
|
{
|
|
4459
4692
|
role: "listbox",
|
|
4460
4693
|
"aria-label": "Graph elements",
|
|
4461
4694
|
"aria-multiselectable": true,
|
|
4462
4695
|
className: cn2(visible ? "rounded-md border" : SR_ONLY, className),
|
|
4463
|
-
children: /* @__PURE__ */
|
|
4696
|
+
children: /* @__PURE__ */ jsx17(
|
|
4464
4697
|
"div",
|
|
4465
4698
|
{
|
|
4466
4699
|
ref: parentRef,
|
|
4467
4700
|
style: { maxHeight: visible ? maxHeight : void 0, overflow: "auto" },
|
|
4468
|
-
children: /* @__PURE__ */
|
|
4701
|
+
children: /* @__PURE__ */ jsx17(
|
|
4469
4702
|
"div",
|
|
4470
4703
|
{
|
|
4471
4704
|
style: {
|
|
@@ -4477,7 +4710,7 @@ function GraphA11yList({
|
|
|
4477
4710
|
const row = rows[vi.index];
|
|
4478
4711
|
if (!row) return null;
|
|
4479
4712
|
const selected = isSelected(row);
|
|
4480
|
-
return /* @__PURE__ */
|
|
4713
|
+
return /* @__PURE__ */ jsx17(
|
|
4481
4714
|
"button",
|
|
4482
4715
|
{
|
|
4483
4716
|
type: "button",
|
|
@@ -4495,7 +4728,7 @@ function GraphA11yList({
|
|
|
4495
4728
|
height: `${vi.size}px`,
|
|
4496
4729
|
transform: `translateY(${vi.start}px)`
|
|
4497
4730
|
},
|
|
4498
|
-
children: /* @__PURE__ */
|
|
4731
|
+
children: /* @__PURE__ */ jsx17("span", { className: "truncate", children: row.label })
|
|
4499
4732
|
},
|
|
4500
4733
|
vi.key
|
|
4501
4734
|
);
|
|
@@ -4633,16 +4866,16 @@ function reconcileFilter(current, model, baselineReset, showEdgeTypes) {
|
|
|
4633
4866
|
}
|
|
4634
4867
|
|
|
4635
4868
|
// src/graph-model/GraphModelEditor.tsx
|
|
4636
|
-
import { useCallback as
|
|
4869
|
+
import { useCallback as useCallback8, useEffect as useEffect6, useRef as useRef7, useState as useState14 } from "react";
|
|
4637
4870
|
import { ResizeHandle } from "@petrarca/sonnet-ui";
|
|
4638
4871
|
|
|
4639
4872
|
// src/graph-model/ModelActionPanel.tsx
|
|
4640
|
-
import { useMemo as useMemo8, useState as
|
|
4641
|
-
import { Button as
|
|
4873
|
+
import { useMemo as useMemo8, useState as useState13 } from "react";
|
|
4874
|
+
import { Button as Button8, IconButton as IconButton2 } from "@petrarca/sonnet-ui";
|
|
4642
4875
|
import { GitBranchPlus, CirclePlus, Waypoints } from "lucide-react";
|
|
4643
4876
|
|
|
4644
4877
|
// src/graph-model/TypeDetailPanel.tsx
|
|
4645
|
-
import { useState as
|
|
4878
|
+
import { useState as useState12 } from "react";
|
|
4646
4879
|
import { Badge as Badge2, IconButton } from "@petrarca/sonnet-ui";
|
|
4647
4880
|
import { Plus, Pencil, Trash2 } from "lucide-react";
|
|
4648
4881
|
|
|
@@ -4962,7 +5195,7 @@ import {
|
|
|
4962
5195
|
SheetDescription
|
|
4963
5196
|
} from "@petrarca/sonnet-ui";
|
|
4964
5197
|
import { JsonSchemaFormRenderer } from "@petrarca/sonnet-forms";
|
|
4965
|
-
import { jsx as
|
|
5198
|
+
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
4966
5199
|
var TYPE_OPTIONS = [
|
|
4967
5200
|
"string",
|
|
4968
5201
|
"integer",
|
|
@@ -5052,18 +5285,18 @@ function PropertyFormSheet({
|
|
|
5052
5285
|
const schema = useMemo7(() => buildSchema(widgets), [widgets]);
|
|
5053
5286
|
const data = useMemo7(() => toFormData(property), [property]);
|
|
5054
5287
|
const isEdit = !!property;
|
|
5055
|
-
return /* @__PURE__ */
|
|
5056
|
-
/* @__PURE__ */
|
|
5057
|
-
/* @__PURE__ */
|
|
5288
|
+
return /* @__PURE__ */ jsx18(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs16(SheetContent, { children: [
|
|
5289
|
+
/* @__PURE__ */ jsxs16(SheetHeader, { children: [
|
|
5290
|
+
/* @__PURE__ */ jsxs16(SheetTitle, { children: [
|
|
5058
5291
|
isEdit ? "Edit property" : "Add property",
|
|
5059
5292
|
" ",
|
|
5060
5293
|
"\u2014",
|
|
5061
5294
|
" ",
|
|
5062
5295
|
ownerLabel
|
|
5063
5296
|
] }),
|
|
5064
|
-
/* @__PURE__ */
|
|
5297
|
+
/* @__PURE__ */ jsx18(SheetDescription, { children: "Define the property and how it is rendered." })
|
|
5065
5298
|
] }),
|
|
5066
|
-
/* @__PURE__ */
|
|
5299
|
+
/* @__PURE__ */ jsx18(SheetBody, { children: /* @__PURE__ */ jsx18(
|
|
5067
5300
|
JsonSchemaFormRenderer,
|
|
5068
5301
|
{
|
|
5069
5302
|
schema,
|
|
@@ -5082,7 +5315,7 @@ function PropertyFormSheet({
|
|
|
5082
5315
|
}
|
|
5083
5316
|
|
|
5084
5317
|
// src/graph-model/RelationFormSheet.tsx
|
|
5085
|
-
import { useState as
|
|
5318
|
+
import { useState as useState10 } from "react";
|
|
5086
5319
|
import {
|
|
5087
5320
|
Sheet as Sheet2,
|
|
5088
5321
|
SheetContent as SheetContent2,
|
|
@@ -5090,11 +5323,11 @@ import {
|
|
|
5090
5323
|
SheetBody as SheetBody2,
|
|
5091
5324
|
SheetTitle as SheetTitle2,
|
|
5092
5325
|
SheetDescription as SheetDescription2,
|
|
5093
|
-
Button as
|
|
5326
|
+
Button as Button6
|
|
5094
5327
|
} from "@petrarca/sonnet-ui";
|
|
5095
5328
|
import { FormSelect } from "@petrarca/sonnet-forms";
|
|
5096
5329
|
import { ArrowRight } from "lucide-react";
|
|
5097
|
-
import { jsx as
|
|
5330
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
5098
5331
|
var CARDINALITIES = ["ONE_TO_MANY", "ONE_TO_ONE"];
|
|
5099
5332
|
function RelationTypeField({
|
|
5100
5333
|
relType,
|
|
@@ -5107,7 +5340,7 @@ function RelationTypeField({
|
|
|
5107
5340
|
);
|
|
5108
5341
|
const options = labels.map((l) => ({ value: l, label: l }));
|
|
5109
5342
|
const canCreate = edgeTypeMode === "edit";
|
|
5110
|
-
return /* @__PURE__ */
|
|
5343
|
+
return /* @__PURE__ */ jsx19(
|
|
5111
5344
|
FormSelect,
|
|
5112
5345
|
{
|
|
5113
5346
|
label: "Relation type",
|
|
@@ -5129,14 +5362,14 @@ function DirectionField({
|
|
|
5129
5362
|
canSwap,
|
|
5130
5363
|
onSwap
|
|
5131
5364
|
}) {
|
|
5132
|
-
return /* @__PURE__ */
|
|
5133
|
-
/* @__PURE__ */
|
|
5134
|
-
/* @__PURE__ */
|
|
5135
|
-
/* @__PURE__ */
|
|
5136
|
-
/* @__PURE__ */
|
|
5137
|
-
/* @__PURE__ */
|
|
5138
|
-
canSwap && /* @__PURE__ */
|
|
5139
|
-
|
|
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,
|
|
5140
5373
|
{
|
|
5141
5374
|
variant: "ghost",
|
|
5142
5375
|
size: "sm",
|
|
@@ -5152,10 +5385,10 @@ function CardinalityField({
|
|
|
5152
5385
|
cardinality,
|
|
5153
5386
|
onChange
|
|
5154
5387
|
}) {
|
|
5155
|
-
return /* @__PURE__ */
|
|
5156
|
-
/* @__PURE__ */
|
|
5157
|
-
/* @__PURE__ */
|
|
5158
|
-
|
|
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,
|
|
5159
5392
|
{
|
|
5160
5393
|
variant: c === cardinality ? "default" : "outline",
|
|
5161
5394
|
size: "sm",
|
|
@@ -5176,9 +5409,9 @@ function RelationFormSheet({
|
|
|
5176
5409
|
edgeTypeMode = "edit",
|
|
5177
5410
|
onSave
|
|
5178
5411
|
}) {
|
|
5179
|
-
const [relType, setRelType] =
|
|
5180
|
-
const [swapped, setSwapped] =
|
|
5181
|
-
const [cardinality, setCardinality] =
|
|
5412
|
+
const [relType, setRelType] = useState10(initial?.relType ?? "");
|
|
5413
|
+
const [swapped, setSwapped] = useState10(false);
|
|
5414
|
+
const [cardinality, setCardinality] = useState10(
|
|
5182
5415
|
initial?.cardinality ?? "ONE_TO_MANY"
|
|
5183
5416
|
);
|
|
5184
5417
|
const source = swapped ? nodeB : nodeA;
|
|
@@ -5194,13 +5427,13 @@ function RelationFormSheet({
|
|
|
5194
5427
|
});
|
|
5195
5428
|
onOpenChange(false);
|
|
5196
5429
|
};
|
|
5197
|
-
return /* @__PURE__ */
|
|
5198
|
-
/* @__PURE__ */
|
|
5199
|
-
/* @__PURE__ */
|
|
5200
|
-
/* @__PURE__ */
|
|
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." })
|
|
5201
5434
|
] }),
|
|
5202
|
-
/* @__PURE__ */
|
|
5203
|
-
/* @__PURE__ */
|
|
5435
|
+
/* @__PURE__ */ jsxs17(SheetBody2, { className: "flex flex-col gap-4", children: [
|
|
5436
|
+
/* @__PURE__ */ jsx19(
|
|
5204
5437
|
RelationTypeField,
|
|
5205
5438
|
{
|
|
5206
5439
|
relType,
|
|
@@ -5209,7 +5442,7 @@ function RelationFormSheet({
|
|
|
5209
5442
|
edgeTypeMode
|
|
5210
5443
|
}
|
|
5211
5444
|
),
|
|
5212
|
-
/* @__PURE__ */
|
|
5445
|
+
/* @__PURE__ */ jsx19(
|
|
5213
5446
|
DirectionField,
|
|
5214
5447
|
{
|
|
5215
5448
|
source,
|
|
@@ -5218,23 +5451,23 @@ function RelationFormSheet({
|
|
|
5218
5451
|
onSwap: () => setSwapped((s) => !s)
|
|
5219
5452
|
}
|
|
5220
5453
|
),
|
|
5221
|
-
/* @__PURE__ */
|
|
5454
|
+
/* @__PURE__ */ jsx19(
|
|
5222
5455
|
CardinalityField,
|
|
5223
5456
|
{
|
|
5224
5457
|
cardinality,
|
|
5225
5458
|
onChange: setCardinality
|
|
5226
5459
|
}
|
|
5227
5460
|
),
|
|
5228
|
-
/* @__PURE__ */
|
|
5229
|
-
/* @__PURE__ */
|
|
5230
|
-
/* @__PURE__ */
|
|
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" })
|
|
5231
5464
|
] })
|
|
5232
5465
|
] })
|
|
5233
5466
|
] }) });
|
|
5234
5467
|
}
|
|
5235
5468
|
|
|
5236
5469
|
// src/graph-model/NodeTypeNameSheet.tsx
|
|
5237
|
-
import { useState as
|
|
5470
|
+
import { useState as useState11 } from "react";
|
|
5238
5471
|
import {
|
|
5239
5472
|
Sheet as Sheet3,
|
|
5240
5473
|
SheetContent as SheetContent3,
|
|
@@ -5242,10 +5475,10 @@ import {
|
|
|
5242
5475
|
SheetBody as SheetBody3,
|
|
5243
5476
|
SheetTitle as SheetTitle3,
|
|
5244
5477
|
SheetDescription as SheetDescription3,
|
|
5245
|
-
Button as
|
|
5478
|
+
Button as Button7,
|
|
5246
5479
|
Input as Input2
|
|
5247
5480
|
} from "@petrarca/sonnet-ui";
|
|
5248
|
-
import { jsx as
|
|
5481
|
+
import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
5249
5482
|
function NodeTypeNameSheet({
|
|
5250
5483
|
open,
|
|
5251
5484
|
onOpenChange,
|
|
@@ -5254,21 +5487,21 @@ function NodeTypeNameSheet({
|
|
|
5254
5487
|
noun = "node",
|
|
5255
5488
|
onSave
|
|
5256
5489
|
}) {
|
|
5257
|
-
const [name, setName] =
|
|
5490
|
+
const [name, setName] = useState11(initial ?? "");
|
|
5258
5491
|
const isEdit = initial !== void 0;
|
|
5259
5492
|
const trimmed = name.trim();
|
|
5260
5493
|
const duplicate = trimmed !== initial && existing.includes(trimmed);
|
|
5261
5494
|
const canSave = trimmed.length > 0 && !duplicate;
|
|
5262
5495
|
const typeNoun = noun === "edge" ? "edge type" : "node type";
|
|
5263
|
-
return /* @__PURE__ */
|
|
5264
|
-
/* @__PURE__ */
|
|
5265
|
-
/* @__PURE__ */
|
|
5266
|
-
/* @__PURE__ */
|
|
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." })
|
|
5267
5500
|
] }),
|
|
5268
|
-
/* @__PURE__ */
|
|
5269
|
-
/* @__PURE__ */
|
|
5270
|
-
/* @__PURE__ */
|
|
5271
|
-
/* @__PURE__ */
|
|
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(
|
|
5272
5505
|
Input2,
|
|
5273
5506
|
{
|
|
5274
5507
|
value: name,
|
|
@@ -5277,12 +5510,12 @@ function NodeTypeNameSheet({
|
|
|
5277
5510
|
autoFocus: true
|
|
5278
5511
|
}
|
|
5279
5512
|
),
|
|
5280
|
-
duplicate && /* @__PURE__ */
|
|
5513
|
+
duplicate && /* @__PURE__ */ jsx20("p", { className: "mt-1 text-xs text-destructive", children: "A type with this label already exists." })
|
|
5281
5514
|
] }),
|
|
5282
|
-
/* @__PURE__ */
|
|
5283
|
-
/* @__PURE__ */
|
|
5284
|
-
/* @__PURE__ */
|
|
5285
|
-
|
|
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,
|
|
5286
5519
|
{
|
|
5287
5520
|
disabled: !canSave,
|
|
5288
5521
|
onClick: () => {
|
|
@@ -5298,7 +5531,7 @@ function NodeTypeNameSheet({
|
|
|
5298
5531
|
}
|
|
5299
5532
|
|
|
5300
5533
|
// src/graph-model/TypeDetailPanel.tsx
|
|
5301
|
-
import { Fragment as Fragment4, jsx as
|
|
5534
|
+
import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
5302
5535
|
function relationViewOf(view) {
|
|
5303
5536
|
return {
|
|
5304
5537
|
relType: view.relType,
|
|
@@ -5311,9 +5544,9 @@ function SectionTitle({
|
|
|
5311
5544
|
children,
|
|
5312
5545
|
action
|
|
5313
5546
|
}) {
|
|
5314
|
-
return /* @__PURE__ */
|
|
5315
|
-
/* @__PURE__ */
|
|
5316
|
-
action && /* @__PURE__ */
|
|
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 })
|
|
5317
5550
|
] });
|
|
5318
5551
|
}
|
|
5319
5552
|
function PropertyRows({
|
|
@@ -5324,37 +5557,37 @@ function PropertyRows({
|
|
|
5324
5557
|
onDelete
|
|
5325
5558
|
}) {
|
|
5326
5559
|
if (properties.length === 0) {
|
|
5327
|
-
return /* @__PURE__ */
|
|
5560
|
+
return /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: emptyText });
|
|
5328
5561
|
}
|
|
5329
|
-
return /* @__PURE__ */
|
|
5562
|
+
return /* @__PURE__ */ jsx21("div", { className: "rounded-lg border divide-y", children: properties.map((p) => /* @__PURE__ */ jsxs19(
|
|
5330
5563
|
"div",
|
|
5331
5564
|
{
|
|
5332
5565
|
className: "group/row flex items-center gap-2 px-3 py-2",
|
|
5333
5566
|
children: [
|
|
5334
|
-
/* @__PURE__ */
|
|
5335
|
-
/* @__PURE__ */
|
|
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: [
|
|
5336
5569
|
p.type,
|
|
5337
5570
|
p.widget ? ` \xB7 ${p.widget}` : ""
|
|
5338
5571
|
] }),
|
|
5339
|
-
p.required && /* @__PURE__ */
|
|
5340
|
-
editable && /* @__PURE__ */
|
|
5341
|
-
/* @__PURE__ */
|
|
5572
|
+
p.required && /* @__PURE__ */ jsx21(Badge2, { badgeColor: "orange", children: "required" }),
|
|
5573
|
+
editable && /* @__PURE__ */ jsxs19("span", { className: "flex gap-1", children: [
|
|
5574
|
+
/* @__PURE__ */ jsx21(
|
|
5342
5575
|
IconButton,
|
|
5343
5576
|
{
|
|
5344
5577
|
label: "Edit property",
|
|
5345
5578
|
revealOnHover: true,
|
|
5346
5579
|
onClick: () => onEdit(p),
|
|
5347
|
-
children: /* @__PURE__ */
|
|
5580
|
+
children: /* @__PURE__ */ jsx21(Pencil, {})
|
|
5348
5581
|
}
|
|
5349
5582
|
),
|
|
5350
|
-
/* @__PURE__ */
|
|
5583
|
+
/* @__PURE__ */ jsx21(
|
|
5351
5584
|
IconButton,
|
|
5352
5585
|
{
|
|
5353
5586
|
label: "Delete property",
|
|
5354
5587
|
tone: "destructive",
|
|
5355
5588
|
revealOnHover: true,
|
|
5356
5589
|
onClick: () => onDelete(p),
|
|
5357
|
-
children: /* @__PURE__ */
|
|
5590
|
+
children: /* @__PURE__ */ jsx21(Trash2, {})
|
|
5358
5591
|
}
|
|
5359
5592
|
)
|
|
5360
5593
|
] })
|
|
@@ -5408,9 +5641,9 @@ function TypeDetailPanel({
|
|
|
5408
5641
|
capabilities,
|
|
5409
5642
|
widgetOptions
|
|
5410
5643
|
}) {
|
|
5411
|
-
const [sheet, setSheet] =
|
|
5412
|
-
const [relSheet, setRelSheet] =
|
|
5413
|
-
const [renameOpen, setRenameOpen] =
|
|
5644
|
+
const [sheet, setSheet] = useState12({ open: false });
|
|
5645
|
+
const [relSheet, setRelSheet] = useState12(null);
|
|
5646
|
+
const [renameOpen, setRenameOpen] = useState12(false);
|
|
5414
5647
|
const view = resolveView(model, selection);
|
|
5415
5648
|
if (!view) return null;
|
|
5416
5649
|
const editable = !readOnly && !!onChange;
|
|
@@ -5437,7 +5670,7 @@ function TypeDetailPanel({
|
|
|
5437
5670
|
if (!onChange) return;
|
|
5438
5671
|
onChange(updateRelation(model, source, prev.relType, prev.target, input));
|
|
5439
5672
|
};
|
|
5440
|
-
const propertiesSection = propsHidden ? null : /* @__PURE__ */
|
|
5673
|
+
const propertiesSection = propsHidden ? null : /* @__PURE__ */ jsx21(
|
|
5441
5674
|
PropertiesSection,
|
|
5442
5675
|
{
|
|
5443
5676
|
view,
|
|
@@ -5447,8 +5680,8 @@ function TypeDetailPanel({
|
|
|
5447
5680
|
onDelete: removeProperty
|
|
5448
5681
|
}
|
|
5449
5682
|
);
|
|
5450
|
-
return /* @__PURE__ */
|
|
5451
|
-
view.kind === "node" ? /* @__PURE__ */
|
|
5683
|
+
return /* @__PURE__ */ jsxs19("div", { children: [
|
|
5684
|
+
view.kind === "node" ? /* @__PURE__ */ jsx21(
|
|
5452
5685
|
NodeDetail,
|
|
5453
5686
|
{
|
|
5454
5687
|
view,
|
|
@@ -5460,7 +5693,7 @@ function TypeDetailPanel({
|
|
|
5460
5693
|
onDeleteRelation: removeRelation,
|
|
5461
5694
|
children: propertiesSection
|
|
5462
5695
|
}
|
|
5463
|
-
) : view.kind === "edge" ? /* @__PURE__ */
|
|
5696
|
+
) : view.kind === "edge" ? /* @__PURE__ */ jsx21(
|
|
5464
5697
|
EdgeDetail,
|
|
5465
5698
|
{
|
|
5466
5699
|
view,
|
|
@@ -5469,7 +5702,7 @@ function TypeDetailPanel({
|
|
|
5469
5702
|
onDelete: () => onChange?.(deleteEdgeType(model, view.label)),
|
|
5470
5703
|
children: propertiesSection
|
|
5471
5704
|
}
|
|
5472
|
-
) : /* @__PURE__ */
|
|
5705
|
+
) : /* @__PURE__ */ jsx21(
|
|
5473
5706
|
RelationDetail,
|
|
5474
5707
|
{
|
|
5475
5708
|
view,
|
|
@@ -5479,7 +5712,7 @@ function TypeDetailPanel({
|
|
|
5479
5712
|
children: propertiesSection
|
|
5480
5713
|
}
|
|
5481
5714
|
),
|
|
5482
|
-
editable && /* @__PURE__ */
|
|
5715
|
+
editable && /* @__PURE__ */ jsx21(
|
|
5483
5716
|
DetailSheets,
|
|
5484
5717
|
{
|
|
5485
5718
|
model,
|
|
@@ -5523,8 +5756,8 @@ function DetailSheets({
|
|
|
5523
5756
|
onRename
|
|
5524
5757
|
}) {
|
|
5525
5758
|
const edgeTypeOptions = model.filter(isEdgeType).map(typeLabel);
|
|
5526
|
-
return /* @__PURE__ */
|
|
5527
|
-
/* @__PURE__ */
|
|
5759
|
+
return /* @__PURE__ */ jsxs19(Fragment4, { children: [
|
|
5760
|
+
/* @__PURE__ */ jsx21(
|
|
5528
5761
|
PropertyFormSheet,
|
|
5529
5762
|
{
|
|
5530
5763
|
open: sheet.open,
|
|
@@ -5535,7 +5768,7 @@ function DetailSheets({
|
|
|
5535
5768
|
onSave: onSaveProperty
|
|
5536
5769
|
}
|
|
5537
5770
|
),
|
|
5538
|
-
relSheet && (view.kind === "node" || view.kind === "relation") && /* @__PURE__ */
|
|
5771
|
+
relSheet && (view.kind === "node" || view.kind === "relation") && /* @__PURE__ */ jsx21(
|
|
5539
5772
|
RelationFormSheet,
|
|
5540
5773
|
{
|
|
5541
5774
|
open: !!relSheet,
|
|
@@ -5552,7 +5785,7 @@ function DetailSheets({
|
|
|
5552
5785
|
},
|
|
5553
5786
|
`${relSheet.relType}:${relSheet.target}`
|
|
5554
5787
|
),
|
|
5555
|
-
(view.kind === "node" || view.kind === "edge") && renameOpen && /* @__PURE__ */
|
|
5788
|
+
(view.kind === "node" || view.kind === "edge") && renameOpen && /* @__PURE__ */ jsx21(
|
|
5556
5789
|
NodeTypeNameSheet,
|
|
5557
5790
|
{
|
|
5558
5791
|
open: renameOpen,
|
|
@@ -5573,15 +5806,15 @@ function PropertiesSection({
|
|
|
5573
5806
|
onEdit,
|
|
5574
5807
|
onDelete
|
|
5575
5808
|
}) {
|
|
5576
|
-
return /* @__PURE__ */
|
|
5577
|
-
/* @__PURE__ */
|
|
5809
|
+
return /* @__PURE__ */ jsxs19(Fragment4, { children: [
|
|
5810
|
+
/* @__PURE__ */ jsx21(
|
|
5578
5811
|
SectionTitle,
|
|
5579
5812
|
{
|
|
5580
|
-
action: editable ? /* @__PURE__ */
|
|
5813
|
+
action: editable ? /* @__PURE__ */ jsx21(IconButton, { label: "Add property", onClick: onAdd, children: /* @__PURE__ */ jsx21(Plus, {}) }) : void 0,
|
|
5581
5814
|
children: "Properties"
|
|
5582
5815
|
}
|
|
5583
5816
|
),
|
|
5584
|
-
/* @__PURE__ */
|
|
5817
|
+
/* @__PURE__ */ jsx21(
|
|
5585
5818
|
PropertyRows,
|
|
5586
5819
|
{
|
|
5587
5820
|
properties: view.properties,
|
|
@@ -5603,11 +5836,11 @@ function NodeDetail({
|
|
|
5603
5836
|
onDeleteRelation,
|
|
5604
5837
|
children
|
|
5605
5838
|
}) {
|
|
5606
|
-
return /* @__PURE__ */
|
|
5607
|
-
/* @__PURE__ */
|
|
5608
|
-
/* @__PURE__ */
|
|
5609
|
-
/* @__PURE__ */
|
|
5610
|
-
nodeTypeEditable && /* @__PURE__ */
|
|
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(
|
|
5611
5844
|
RenameDeleteActions,
|
|
5612
5845
|
{
|
|
5613
5846
|
noun: "node type",
|
|
@@ -5616,16 +5849,16 @@ function NodeDetail({
|
|
|
5616
5849
|
}
|
|
5617
5850
|
)
|
|
5618
5851
|
] }),
|
|
5619
|
-
view.description && /* @__PURE__ */
|
|
5852
|
+
view.description && /* @__PURE__ */ jsx21("p", { className: "mt-1 text-sm text-muted-foreground", children: view.description }),
|
|
5620
5853
|
children,
|
|
5621
|
-
/* @__PURE__ */
|
|
5622
|
-
view.relations.length === 0 ? /* @__PURE__ */
|
|
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(
|
|
5623
5856
|
"div",
|
|
5624
5857
|
{
|
|
5625
5858
|
className: "group/row flex items-center gap-2 px-3 py-2",
|
|
5626
5859
|
children: [
|
|
5627
|
-
/* @__PURE__ */
|
|
5628
|
-
/* @__PURE__ */
|
|
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: [
|
|
5629
5862
|
"\u2192",
|
|
5630
5863
|
" ",
|
|
5631
5864
|
r.target,
|
|
@@ -5634,24 +5867,24 @@ function NodeDetail({
|
|
|
5634
5867
|
" ",
|
|
5635
5868
|
cardinalityLabel(r.cardinality)
|
|
5636
5869
|
] }),
|
|
5637
|
-
relationsEditable && /* @__PURE__ */
|
|
5638
|
-
/* @__PURE__ */
|
|
5870
|
+
relationsEditable && /* @__PURE__ */ jsxs19("span", { className: "flex gap-1", children: [
|
|
5871
|
+
/* @__PURE__ */ jsx21(
|
|
5639
5872
|
IconButton,
|
|
5640
5873
|
{
|
|
5641
5874
|
label: "Edit relation",
|
|
5642
5875
|
revealOnHover: true,
|
|
5643
5876
|
onClick: () => onEditRelation(r),
|
|
5644
|
-
children: /* @__PURE__ */
|
|
5877
|
+
children: /* @__PURE__ */ jsx21(Pencil, {})
|
|
5645
5878
|
}
|
|
5646
5879
|
),
|
|
5647
|
-
/* @__PURE__ */
|
|
5880
|
+
/* @__PURE__ */ jsx21(
|
|
5648
5881
|
IconButton,
|
|
5649
5882
|
{
|
|
5650
5883
|
label: "Delete relation",
|
|
5651
5884
|
tone: "destructive",
|
|
5652
5885
|
revealOnHover: true,
|
|
5653
5886
|
onClick: () => onDeleteRelation(r),
|
|
5654
|
-
children: /* @__PURE__ */
|
|
5887
|
+
children: /* @__PURE__ */ jsx21(Trash2, {})
|
|
5655
5888
|
}
|
|
5656
5889
|
)
|
|
5657
5890
|
] })
|
|
@@ -5664,7 +5897,7 @@ function NodeDetail({
|
|
|
5664
5897
|
function EdgeTypeNote({
|
|
5665
5898
|
children
|
|
5666
5899
|
}) {
|
|
5667
|
-
return /* @__PURE__ */
|
|
5900
|
+
return /* @__PURE__ */ jsx21("p", { className: "mt-3 rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground", children });
|
|
5668
5901
|
}
|
|
5669
5902
|
function RenameDeleteActions({
|
|
5670
5903
|
noun,
|
|
@@ -5672,15 +5905,15 @@ function RenameDeleteActions({
|
|
|
5672
5905
|
onRename,
|
|
5673
5906
|
onDelete
|
|
5674
5907
|
}) {
|
|
5675
|
-
return /* @__PURE__ */
|
|
5676
|
-
/* @__PURE__ */
|
|
5677
|
-
/* @__PURE__ */
|
|
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(
|
|
5678
5911
|
IconButton,
|
|
5679
5912
|
{
|
|
5680
5913
|
label: `Delete ${noun}`,
|
|
5681
5914
|
tone: "destructive",
|
|
5682
5915
|
onClick: onDelete,
|
|
5683
|
-
children: /* @__PURE__ */
|
|
5916
|
+
children: /* @__PURE__ */ jsx21(Trash2, {})
|
|
5684
5917
|
}
|
|
5685
5918
|
)
|
|
5686
5919
|
] });
|
|
@@ -5692,11 +5925,11 @@ function EdgeDetail({
|
|
|
5692
5925
|
onDelete,
|
|
5693
5926
|
children
|
|
5694
5927
|
}) {
|
|
5695
|
-
return /* @__PURE__ */
|
|
5696
|
-
/* @__PURE__ */
|
|
5697
|
-
/* @__PURE__ */
|
|
5698
|
-
/* @__PURE__ */
|
|
5699
|
-
editable && /* @__PURE__ */
|
|
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(
|
|
5700
5933
|
RenameDeleteActions,
|
|
5701
5934
|
{
|
|
5702
5935
|
noun: "edge type",
|
|
@@ -5705,11 +5938,11 @@ function EdgeDetail({
|
|
|
5705
5938
|
}
|
|
5706
5939
|
)
|
|
5707
5940
|
] }),
|
|
5708
|
-
view.description && /* @__PURE__ */
|
|
5709
|
-
/* @__PURE__ */
|
|
5941
|
+
view.description && /* @__PURE__ */ jsx21("p", { className: "mt-1 text-sm text-muted-foreground", children: view.description }),
|
|
5942
|
+
/* @__PURE__ */ jsxs19(EdgeTypeNote, { children: [
|
|
5710
5943
|
"These properties belong to the",
|
|
5711
5944
|
" ",
|
|
5712
|
-
/* @__PURE__ */
|
|
5945
|
+
/* @__PURE__ */ jsx21("span", { className: "font-medium", children: view.label }),
|
|
5713
5946
|
" edge type and are shared by every relation of this type."
|
|
5714
5947
|
] }),
|
|
5715
5948
|
children
|
|
@@ -5722,11 +5955,11 @@ function RelationDetail({
|
|
|
5722
5955
|
onDelete,
|
|
5723
5956
|
children
|
|
5724
5957
|
}) {
|
|
5725
|
-
return /* @__PURE__ */
|
|
5726
|
-
/* @__PURE__ */
|
|
5727
|
-
/* @__PURE__ */
|
|
5728
|
-
/* @__PURE__ */
|
|
5729
|
-
editable && /* @__PURE__ */
|
|
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(
|
|
5730
5963
|
RenameDeleteActions,
|
|
5731
5964
|
{
|
|
5732
5965
|
noun: "relation",
|
|
@@ -5736,17 +5969,17 @@ function RelationDetail({
|
|
|
5736
5969
|
}
|
|
5737
5970
|
)
|
|
5738
5971
|
] }),
|
|
5739
|
-
/* @__PURE__ */
|
|
5740
|
-
/* @__PURE__ */
|
|
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 }),
|
|
5741
5974
|
" \u2192 ",
|
|
5742
|
-
/* @__PURE__ */
|
|
5975
|
+
/* @__PURE__ */ jsx21("span", { className: "font-medium text-foreground", children: view.target }),
|
|
5743
5976
|
" \xB7 ",
|
|
5744
5977
|
cardinalityLabel(view.cardinality)
|
|
5745
5978
|
] }),
|
|
5746
|
-
/* @__PURE__ */
|
|
5979
|
+
/* @__PURE__ */ jsxs19(EdgeTypeNote, { children: [
|
|
5747
5980
|
"Properties are defined on the",
|
|
5748
5981
|
" ",
|
|
5749
|
-
/* @__PURE__ */
|
|
5982
|
+
/* @__PURE__ */ jsx21("span", { className: "font-medium", children: view.relType }),
|
|
5750
5983
|
" edge type, shared by every relation of this type -- not on this single relation."
|
|
5751
5984
|
] }),
|
|
5752
5985
|
children
|
|
@@ -5811,7 +6044,7 @@ function validateCapabilities(input) {
|
|
|
5811
6044
|
}
|
|
5812
6045
|
|
|
5813
6046
|
// src/graph-model/ModelActionPanel.tsx
|
|
5814
|
-
import { Fragment as Fragment5, jsx as
|
|
6047
|
+
import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
5815
6048
|
function nodeTypeLabels(model) {
|
|
5816
6049
|
return model.filter(isNodeType).map(typeLabel);
|
|
5817
6050
|
}
|
|
@@ -5825,19 +6058,19 @@ function CreateRelationPrompt({
|
|
|
5825
6058
|
edgeTypeMode,
|
|
5826
6059
|
onChange
|
|
5827
6060
|
}) {
|
|
5828
|
-
const [open, setOpen] =
|
|
5829
|
-
return /* @__PURE__ */
|
|
5830
|
-
/* @__PURE__ */
|
|
5831
|
-
/* @__PURE__ */
|
|
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 }),
|
|
5832
6065
|
" and ",
|
|
5833
|
-
/* @__PURE__ */
|
|
6066
|
+
/* @__PURE__ */ jsx22("span", { className: "font-medium", children: nodeB }),
|
|
5834
6067
|
" selected."
|
|
5835
6068
|
] }),
|
|
5836
|
-
/* @__PURE__ */
|
|
5837
|
-
/* @__PURE__ */
|
|
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" }),
|
|
5838
6071
|
"Create relation"
|
|
5839
6072
|
] }),
|
|
5840
|
-
/* @__PURE__ */
|
|
6073
|
+
/* @__PURE__ */ jsx22(
|
|
5841
6074
|
RelationFormSheet,
|
|
5842
6075
|
{
|
|
5843
6076
|
open,
|
|
@@ -5893,7 +6126,7 @@ function ModelActionPanel({
|
|
|
5893
6126
|
const schema = findType(model, l);
|
|
5894
6127
|
return schema ? isNodeType(schema) : false;
|
|
5895
6128
|
}) : [];
|
|
5896
|
-
return /* @__PURE__ */
|
|
6129
|
+
return /* @__PURE__ */ jsx22("div", { className: "h-full overflow-auto p-4", children: /* @__PURE__ */ jsx22(
|
|
5897
6130
|
PanelBody,
|
|
5898
6131
|
{
|
|
5899
6132
|
model,
|
|
@@ -5916,7 +6149,7 @@ function PanelBody({
|
|
|
5916
6149
|
twoLabels
|
|
5917
6150
|
}) {
|
|
5918
6151
|
if (selection) {
|
|
5919
|
-
return /* @__PURE__ */
|
|
6152
|
+
return /* @__PURE__ */ jsx22(
|
|
5920
6153
|
TypeDetailPanel,
|
|
5921
6154
|
{
|
|
5922
6155
|
model,
|
|
@@ -5930,7 +6163,7 @@ function PanelBody({
|
|
|
5930
6163
|
}
|
|
5931
6164
|
const canEdit = !readOnly && !!onChange;
|
|
5932
6165
|
if (twoLabels.length === 2 && canEdit && caps.relations === "edit") {
|
|
5933
|
-
return /* @__PURE__ */
|
|
6166
|
+
return /* @__PURE__ */ jsx22(
|
|
5934
6167
|
CreateRelationPrompt,
|
|
5935
6168
|
{
|
|
5936
6169
|
nodeA: twoLabels[0],
|
|
@@ -5941,8 +6174,8 @@ function PanelBody({
|
|
|
5941
6174
|
}
|
|
5942
6175
|
);
|
|
5943
6176
|
}
|
|
5944
|
-
return /* @__PURE__ */
|
|
5945
|
-
canEdit && /* @__PURE__ */
|
|
6177
|
+
return /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
6178
|
+
canEdit && /* @__PURE__ */ jsx22(
|
|
5946
6179
|
AddTypeToolbar,
|
|
5947
6180
|
{
|
|
5948
6181
|
model,
|
|
@@ -5951,7 +6184,7 @@ function PanelBody({
|
|
|
5951
6184
|
canAddEdge: caps.edgeTypes === "edit"
|
|
5952
6185
|
}
|
|
5953
6186
|
),
|
|
5954
|
-
/* @__PURE__ */
|
|
6187
|
+
/* @__PURE__ */ jsx22(
|
|
5955
6188
|
Overview_default,
|
|
5956
6189
|
{
|
|
5957
6190
|
nodeBuckets: [
|
|
@@ -5970,9 +6203,9 @@ function AddTypeIconButton({
|
|
|
5970
6203
|
existing,
|
|
5971
6204
|
onSave
|
|
5972
6205
|
}) {
|
|
5973
|
-
const [open, setOpen] =
|
|
5974
|
-
return /* @__PURE__ */
|
|
5975
|
-
/* @__PURE__ */
|
|
6206
|
+
const [open, setOpen] = useState13(false);
|
|
6207
|
+
return /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
6208
|
+
/* @__PURE__ */ jsx22(
|
|
5976
6209
|
IconButton2,
|
|
5977
6210
|
{
|
|
5978
6211
|
label: tooltip,
|
|
@@ -5981,7 +6214,7 @@ function AddTypeIconButton({
|
|
|
5981
6214
|
children: icon
|
|
5982
6215
|
}
|
|
5983
6216
|
),
|
|
5984
|
-
/* @__PURE__ */
|
|
6217
|
+
/* @__PURE__ */ jsx22(
|
|
5985
6218
|
NodeTypeNameSheet,
|
|
5986
6219
|
{
|
|
5987
6220
|
open,
|
|
@@ -6001,22 +6234,22 @@ function AddTypeToolbar({
|
|
|
6001
6234
|
}) {
|
|
6002
6235
|
if (!canAddNode && !canAddEdge) return null;
|
|
6003
6236
|
const existing = model.map(typeLabel);
|
|
6004
|
-
return /* @__PURE__ */
|
|
6005
|
-
canAddNode && /* @__PURE__ */
|
|
6237
|
+
return /* @__PURE__ */ jsxs20("div", { className: "mb-3 flex gap-1.5", children: [
|
|
6238
|
+
canAddNode && /* @__PURE__ */ jsx22(
|
|
6006
6239
|
AddTypeIconButton,
|
|
6007
6240
|
{
|
|
6008
6241
|
tooltip: "Add node type",
|
|
6009
|
-
icon: /* @__PURE__ */
|
|
6242
|
+
icon: /* @__PURE__ */ jsx22(CirclePlus, {}),
|
|
6010
6243
|
noun: "node",
|
|
6011
6244
|
existing,
|
|
6012
6245
|
onSave: (name) => onChange(addNodeType(model, name))
|
|
6013
6246
|
}
|
|
6014
6247
|
),
|
|
6015
|
-
canAddEdge && /* @__PURE__ */
|
|
6248
|
+
canAddEdge && /* @__PURE__ */ jsx22(
|
|
6016
6249
|
AddTypeIconButton,
|
|
6017
6250
|
{
|
|
6018
6251
|
tooltip: "Add edge type",
|
|
6019
|
-
icon: /* @__PURE__ */
|
|
6252
|
+
icon: /* @__PURE__ */ jsx22(Waypoints, {}),
|
|
6020
6253
|
noun: "edge",
|
|
6021
6254
|
existing,
|
|
6022
6255
|
onSave: (name) => onChange(addEdgeType(model, name))
|
|
@@ -6026,7 +6259,7 @@ function AddTypeToolbar({
|
|
|
6026
6259
|
}
|
|
6027
6260
|
|
|
6028
6261
|
// src/graph-model/GraphModelEditor.tsx
|
|
6029
|
-
import { Fragment as Fragment6, jsx as
|
|
6262
|
+
import { Fragment as Fragment6, jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
6030
6263
|
function createEditorStores() {
|
|
6031
6264
|
const graphFilter = createGraphFilterStore();
|
|
6032
6265
|
const graphData = createGraphDataStore();
|
|
@@ -6040,7 +6273,7 @@ function createEditorStores() {
|
|
|
6040
6273
|
expandNode: async () => ({ nodes: [], edges: [], has_more: false }),
|
|
6041
6274
|
getNodes: async () => []
|
|
6042
6275
|
});
|
|
6043
|
-
return { graphFilter, graphData, graphExploration, selection };
|
|
6276
|
+
return { graphFilter, graphData, graphExploration, selection, graphFocus };
|
|
6044
6277
|
}
|
|
6045
6278
|
function loadModel(stores, model) {
|
|
6046
6279
|
const { nodes: rawNodes, edges: rawEdges } = modelToGraph(model);
|
|
@@ -6063,14 +6296,14 @@ function GraphModelEditor({
|
|
|
6063
6296
|
defaultRenderer = "cytoscape",
|
|
6064
6297
|
hideActionPanel = false
|
|
6065
6298
|
}) {
|
|
6066
|
-
const storesRef =
|
|
6299
|
+
const storesRef = useRef7(null);
|
|
6067
6300
|
if (!storesRef.current) storesRef.current = createEditorStores();
|
|
6068
6301
|
const stores = storesRef.current;
|
|
6069
6302
|
const caps = resolveCapabilities(capabilities, readOnly);
|
|
6070
6303
|
const showEdgeTypes = caps.showEdgeTypes;
|
|
6071
|
-
const firstLoadRef =
|
|
6072
|
-
const prevShowEdgeTypesRef =
|
|
6073
|
-
|
|
6304
|
+
const firstLoadRef = useRef7(true);
|
|
6305
|
+
const prevShowEdgeTypesRef = useRef7(showEdgeTypes);
|
|
6306
|
+
useEffect6(() => {
|
|
6074
6307
|
loadModel(stores, value);
|
|
6075
6308
|
const filter = stores.graphFilter.getState();
|
|
6076
6309
|
const baselineReset = firstLoadRef.current || prevShowEdgeTypesRef.current !== showEdgeTypes;
|
|
@@ -6084,9 +6317,9 @@ function GraphModelEditor({
|
|
|
6084
6317
|
firstLoadRef.current = false;
|
|
6085
6318
|
prevShowEdgeTypesRef.current = showEdgeTypes;
|
|
6086
6319
|
}, [stores, value, showEdgeTypes]);
|
|
6087
|
-
const rowRef =
|
|
6088
|
-
const [panelWidth, setPanelWidth] =
|
|
6089
|
-
const startResize =
|
|
6320
|
+
const rowRef = useRef7(null);
|
|
6321
|
+
const [panelWidth, setPanelWidth] = useState14(380);
|
|
6322
|
+
const startResize = useCallback8((e) => {
|
|
6090
6323
|
e.preventDefault();
|
|
6091
6324
|
const target = e.currentTarget;
|
|
6092
6325
|
target.setPointerCapture(e.pointerId);
|
|
@@ -6101,10 +6334,10 @@ function GraphModelEditor({
|
|
|
6101
6334
|
window.addEventListener("pointermove", move);
|
|
6102
6335
|
window.addEventListener("pointerup", up);
|
|
6103
6336
|
}, []);
|
|
6104
|
-
return /* @__PURE__ */
|
|
6105
|
-
/* @__PURE__ */
|
|
6106
|
-
!hideActionPanel && /* @__PURE__ */
|
|
6107
|
-
/* @__PURE__ */
|
|
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(
|
|
6108
6341
|
ResizeHandle,
|
|
6109
6342
|
{
|
|
6110
6343
|
orientation: "vertical",
|
|
@@ -6115,12 +6348,12 @@ function GraphModelEditor({
|
|
|
6115
6348
|
onDoubleClick: () => setPanelWidth(380)
|
|
6116
6349
|
}
|
|
6117
6350
|
),
|
|
6118
|
-
/* @__PURE__ */
|
|
6351
|
+
/* @__PURE__ */ jsx23(
|
|
6119
6352
|
"div",
|
|
6120
6353
|
{
|
|
6121
6354
|
style: { width: panelWidth },
|
|
6122
6355
|
className: "shrink-0 border-l overflow-hidden bg-card",
|
|
6123
|
-
children: /* @__PURE__ */
|
|
6356
|
+
children: /* @__PURE__ */ jsx23(
|
|
6124
6357
|
ModelActionPanel,
|
|
6125
6358
|
{
|
|
6126
6359
|
model: value,
|
|
@@ -6284,17 +6517,17 @@ function enrichSchemaWithRelations(baseSchema, relations) {
|
|
|
6284
6517
|
}
|
|
6285
6518
|
|
|
6286
6519
|
// src/graph-edit/GraphEditToolbar.tsx
|
|
6287
|
-
import { useCallback as
|
|
6520
|
+
import { useCallback as useCallback12, useMemo as useMemo11, useState as useState18 } from "react";
|
|
6288
6521
|
import { IconButton as IconButton4 } from "@petrarca/sonnet-ui";
|
|
6289
6522
|
import { Plus as Plus3, Edit, Link, Trash2 as Trash23 } from "lucide-react";
|
|
6290
6523
|
|
|
6291
6524
|
// src/graph-edit/NodeFormSheet.tsx
|
|
6292
6525
|
import {
|
|
6293
|
-
useCallback as
|
|
6294
|
-
useEffect as
|
|
6526
|
+
useCallback as useCallback9,
|
|
6527
|
+
useEffect as useEffect7,
|
|
6295
6528
|
useMemo as useMemo9,
|
|
6296
|
-
useRef as
|
|
6297
|
-
useState as
|
|
6529
|
+
useRef as useRef8,
|
|
6530
|
+
useState as useState15
|
|
6298
6531
|
} from "react";
|
|
6299
6532
|
import {
|
|
6300
6533
|
Sheet as Sheet4,
|
|
@@ -6303,7 +6536,7 @@ import {
|
|
|
6303
6536
|
SheetBody as SheetBody4,
|
|
6304
6537
|
SheetTitle as SheetTitle4,
|
|
6305
6538
|
SheetDescription as SheetDescription4,
|
|
6306
|
-
Alert
|
|
6539
|
+
Alert as Alert2
|
|
6307
6540
|
} from "@petrarca/sonnet-ui";
|
|
6308
6541
|
import {
|
|
6309
6542
|
FormSelect as FormSelect2,
|
|
@@ -6311,7 +6544,7 @@ import {
|
|
|
6311
6544
|
JsonSchemaFormRenderer as JsonSchemaFormRenderer2
|
|
6312
6545
|
} from "@petrarca/sonnet-forms";
|
|
6313
6546
|
import { errorLog as errorLog4 } from "@petrarca/sonnet-core";
|
|
6314
|
-
import { Fragment as Fragment7, jsx as
|
|
6547
|
+
import { Fragment as Fragment7, jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
6315
6548
|
var EMPTY_RESOLVED = {
|
|
6316
6549
|
schema: null,
|
|
6317
6550
|
uiSchema: {},
|
|
@@ -6335,10 +6568,10 @@ function resolveBaseSchema(provided, node) {
|
|
|
6335
6568
|
return { type: "object", properties: {} };
|
|
6336
6569
|
}
|
|
6337
6570
|
function useNodeSchema(schemaProvider, label, node) {
|
|
6338
|
-
const [resolved, setResolved] =
|
|
6339
|
-
const nodeRef =
|
|
6571
|
+
const [resolved, setResolved] = useState15(EMPTY_RESOLVED);
|
|
6572
|
+
const nodeRef = useRef8(node);
|
|
6340
6573
|
nodeRef.current = node;
|
|
6341
|
-
|
|
6574
|
+
useEffect7(() => {
|
|
6342
6575
|
let cancelled = false;
|
|
6343
6576
|
if (!label) {
|
|
6344
6577
|
setResolved(EMPTY_RESOLVED);
|
|
@@ -6376,11 +6609,11 @@ function useNodeSchema(schemaProvider, label, node) {
|
|
|
6376
6609
|
return resolved;
|
|
6377
6610
|
}
|
|
6378
6611
|
function useExistingFormData(port, node, relationTypes) {
|
|
6379
|
-
const [data, setData] =
|
|
6612
|
+
const [data, setData] = useState15(null);
|
|
6380
6613
|
const nodeId2 = node?.id_;
|
|
6381
|
-
const propertiesRef =
|
|
6614
|
+
const propertiesRef = useRef8(node?.properties_);
|
|
6382
6615
|
propertiesRef.current = node?.properties_;
|
|
6383
|
-
|
|
6616
|
+
useEffect7(() => {
|
|
6384
6617
|
let cancelled = false;
|
|
6385
6618
|
if (nodeId2 == null) {
|
|
6386
6619
|
setData(null);
|
|
@@ -6422,12 +6655,12 @@ function LabelField({
|
|
|
6422
6655
|
onSelect
|
|
6423
6656
|
}) {
|
|
6424
6657
|
if (isEdit) {
|
|
6425
|
-
return /* @__PURE__ */
|
|
6426
|
-
/* @__PURE__ */
|
|
6427
|
-
/* @__PURE__ */
|
|
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 })
|
|
6428
6661
|
] });
|
|
6429
6662
|
}
|
|
6430
|
-
return /* @__PURE__ */
|
|
6663
|
+
return /* @__PURE__ */ jsx24(
|
|
6431
6664
|
FormSelect2,
|
|
6432
6665
|
{
|
|
6433
6666
|
label: "Label",
|
|
@@ -6439,8 +6672,8 @@ function LabelField({
|
|
|
6439
6672
|
);
|
|
6440
6673
|
}
|
|
6441
6674
|
function useCreateModeLabels(schemaProvider, open, isEdit) {
|
|
6442
|
-
const [labels, setLabels] =
|
|
6443
|
-
|
|
6675
|
+
const [labels, setLabels] = useState15([]);
|
|
6676
|
+
useEffect7(() => {
|
|
6444
6677
|
let cancelled = false;
|
|
6445
6678
|
if (!open || isEdit) return;
|
|
6446
6679
|
void schemaProvider.listNodeLabels().then((ls) => {
|
|
@@ -6462,7 +6695,7 @@ function NodeForm({
|
|
|
6462
6695
|
onSave,
|
|
6463
6696
|
onCancel
|
|
6464
6697
|
}) {
|
|
6465
|
-
return /* @__PURE__ */
|
|
6698
|
+
return /* @__PURE__ */ jsx24(
|
|
6466
6699
|
JsonSchemaFormRenderer2,
|
|
6467
6700
|
{
|
|
6468
6701
|
schema,
|
|
@@ -6496,13 +6729,13 @@ function NodeFormSheetBody({
|
|
|
6496
6729
|
onSave,
|
|
6497
6730
|
onCancel
|
|
6498
6731
|
}) {
|
|
6499
|
-
return /* @__PURE__ */
|
|
6500
|
-
/* @__PURE__ */
|
|
6501
|
-
/* @__PURE__ */
|
|
6502
|
-
/* @__PURE__ */
|
|
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." })
|
|
6503
6736
|
] }),
|
|
6504
|
-
/* @__PURE__ */
|
|
6505
|
-
/* @__PURE__ */
|
|
6737
|
+
/* @__PURE__ */ jsxs22(SheetBody4, { className: "flex flex-col gap-4", children: [
|
|
6738
|
+
/* @__PURE__ */ jsx24(
|
|
6506
6739
|
LabelField,
|
|
6507
6740
|
{
|
|
6508
6741
|
isEdit,
|
|
@@ -6511,7 +6744,7 @@ function NodeFormSheetBody({
|
|
|
6511
6744
|
onSelect: onSelectLabel
|
|
6512
6745
|
}
|
|
6513
6746
|
),
|
|
6514
|
-
schema && /* @__PURE__ */
|
|
6747
|
+
schema && /* @__PURE__ */ jsx24(
|
|
6515
6748
|
NodeForm,
|
|
6516
6749
|
{
|
|
6517
6750
|
formKey,
|
|
@@ -6524,7 +6757,7 @@ function NodeFormSheetBody({
|
|
|
6524
6757
|
onCancel
|
|
6525
6758
|
}
|
|
6526
6759
|
),
|
|
6527
|
-
!isEdit && /* @__PURE__ */
|
|
6760
|
+
!isEdit && /* @__PURE__ */ jsx24(
|
|
6528
6761
|
FormCheckbox,
|
|
6529
6762
|
{
|
|
6530
6763
|
label: "Create another node",
|
|
@@ -6532,7 +6765,7 @@ function NodeFormSheetBody({
|
|
|
6532
6765
|
onChange: (c) => onKeepOpenChange(c === true)
|
|
6533
6766
|
}
|
|
6534
6767
|
),
|
|
6535
|
-
error && /* @__PURE__ */
|
|
6768
|
+
error && /* @__PURE__ */ jsx24(Alert2, { variant: "destructive", children: error })
|
|
6536
6769
|
] })
|
|
6537
6770
|
] });
|
|
6538
6771
|
}
|
|
@@ -6546,14 +6779,14 @@ function NodeFormSheet({
|
|
|
6546
6779
|
onSaved
|
|
6547
6780
|
}) {
|
|
6548
6781
|
const isEdit = !!node?.id_;
|
|
6549
|
-
const [selectedLabel, setSelectedLabel] =
|
|
6782
|
+
const [selectedLabel, setSelectedLabel] = useState15(
|
|
6550
6783
|
node?.label_ ?? null
|
|
6551
6784
|
);
|
|
6552
|
-
const [error, setError] =
|
|
6553
|
-
const [keepOpen, setKeepOpen] =
|
|
6554
|
-
const [formNonce, setFormNonce] =
|
|
6785
|
+
const [error, setError] = useState15("");
|
|
6786
|
+
const [keepOpen, setKeepOpen] = useState15(false);
|
|
6787
|
+
const [formNonce, setFormNonce] = useState15(0);
|
|
6555
6788
|
const labels = useCreateModeLabels(schemaProvider, open, isEdit);
|
|
6556
|
-
|
|
6789
|
+
useEffect7(() => {
|
|
6557
6790
|
if (open) {
|
|
6558
6791
|
setSelectedLabel(node?.label_ ?? null);
|
|
6559
6792
|
setError("");
|
|
@@ -6570,7 +6803,7 @@ function NodeFormSheet({
|
|
|
6570
6803
|
const formData = useMemo9(() => existingData ?? {}, [existingData]);
|
|
6571
6804
|
const stableOnSaved = useStableRef(onSaved);
|
|
6572
6805
|
const stableOnOpenChange = useStableRef(onOpenChange);
|
|
6573
|
-
const handleSave =
|
|
6806
|
+
const handleSave = useCallback9(
|
|
6574
6807
|
(data) => {
|
|
6575
6808
|
if (!selectedLabel) return;
|
|
6576
6809
|
void (async () => {
|
|
@@ -6603,7 +6836,7 @@ function NodeFormSheet({
|
|
|
6603
6836
|
stableOnOpenChange
|
|
6604
6837
|
]
|
|
6605
6838
|
);
|
|
6606
|
-
return /* @__PURE__ */
|
|
6839
|
+
return /* @__PURE__ */ jsx24(Sheet4, { open, onOpenChange, children: /* @__PURE__ */ jsx24(SheetContent4, { children: /* @__PURE__ */ jsx24(
|
|
6607
6840
|
NodeFormSheetBody,
|
|
6608
6841
|
{
|
|
6609
6842
|
isEdit,
|
|
@@ -6660,7 +6893,7 @@ async function saveNode({
|
|
|
6660
6893
|
}
|
|
6661
6894
|
|
|
6662
6895
|
// src/graph-edit/EdgeFormSheet.tsx
|
|
6663
|
-
import { useCallback as
|
|
6896
|
+
import { useCallback as useCallback10, useEffect as useEffect8, useMemo as useMemo10, useState as useState16 } from "react";
|
|
6664
6897
|
import {
|
|
6665
6898
|
Sheet as Sheet5,
|
|
6666
6899
|
SheetContent as SheetContent5,
|
|
@@ -6674,20 +6907,20 @@ import {
|
|
|
6674
6907
|
DialogTitle,
|
|
6675
6908
|
DialogDescription,
|
|
6676
6909
|
DialogFooter,
|
|
6677
|
-
Button as
|
|
6910
|
+
Button as Button9,
|
|
6678
6911
|
IconButton as IconButton3,
|
|
6679
|
-
Alert as
|
|
6912
|
+
Alert as Alert3
|
|
6680
6913
|
} from "@petrarca/sonnet-ui";
|
|
6681
6914
|
import { Pencil as Pencil2, Plus as Plus2, Trash2 as Trash22, ArrowRight as ArrowRight2 } from "lucide-react";
|
|
6682
6915
|
import {
|
|
6683
6916
|
JsonSchemaFormRenderer as JsonSchemaFormRenderer3
|
|
6684
6917
|
} from "@petrarca/sonnet-forms";
|
|
6685
6918
|
import { errorLog as errorLog5 } from "@petrarca/sonnet-core";
|
|
6686
|
-
import { jsx as
|
|
6919
|
+
import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
6687
6920
|
function usePairState(port, schemaProvider, open, nodeAId, nodeALabel, nodeBId, nodeBLabel, reloadKey) {
|
|
6688
|
-
const [active, setActive] =
|
|
6689
|
-
const [available, setAvailable] =
|
|
6690
|
-
|
|
6921
|
+
const [active, setActive] = useState16([]);
|
|
6922
|
+
const [available, setAvailable] = useState16([]);
|
|
6923
|
+
useEffect8(() => {
|
|
6691
6924
|
let cancelled = false;
|
|
6692
6925
|
if (!open || nodeAId == null || nodeBId == null) {
|
|
6693
6926
|
setActive([]);
|
|
@@ -6752,9 +6985,9 @@ var EMPTY_EDGE_SCHEMA = {
|
|
|
6752
6985
|
properties: {}
|
|
6753
6986
|
};
|
|
6754
6987
|
function useEdgeSchema(schemaProvider, draft) {
|
|
6755
|
-
const [schema, setSchema] =
|
|
6988
|
+
const [schema, setSchema] = useState16(null);
|
|
6756
6989
|
const label = useMemo10(() => draftLabel(draft), [draft]);
|
|
6757
|
-
|
|
6990
|
+
useEffect8(() => {
|
|
6758
6991
|
let cancelled = false;
|
|
6759
6992
|
if (!label) {
|
|
6760
6993
|
setSchema(null);
|
|
@@ -6774,12 +7007,12 @@ function useEdgeSchema(schemaProvider, draft) {
|
|
|
6774
7007
|
return schema;
|
|
6775
7008
|
}
|
|
6776
7009
|
function useEditableEdgeTypes(schemaProvider, active) {
|
|
6777
|
-
const [editable, setEditable] =
|
|
7010
|
+
const [editable, setEditable] = useState16({});
|
|
6778
7011
|
const labelsKey = useMemo10(
|
|
6779
7012
|
() => Array.from(new Set(active.map((e) => e.label_))).sort().join("\0"),
|
|
6780
7013
|
[active]
|
|
6781
7014
|
);
|
|
6782
|
-
|
|
7015
|
+
useEffect8(() => {
|
|
6783
7016
|
let cancelled = false;
|
|
6784
7017
|
const labels = labelsKey ? labelsKey.split("\0") : [];
|
|
6785
7018
|
if (labels.length === 0) {
|
|
@@ -6808,7 +7041,7 @@ function useEditableEdgeTypes(schemaProvider, active) {
|
|
|
6808
7041
|
function SectionTitle2({
|
|
6809
7042
|
children
|
|
6810
7043
|
}) {
|
|
6811
|
-
return /* @__PURE__ */
|
|
7044
|
+
return /* @__PURE__ */ jsx25("div", { className: "mb-2", children: /* @__PURE__ */ jsx25("span", { className: "heading-meta", children }) });
|
|
6812
7045
|
}
|
|
6813
7046
|
function EdgeRow({
|
|
6814
7047
|
label,
|
|
@@ -6816,19 +7049,19 @@ function EdgeRow({
|
|
|
6816
7049
|
onDelete,
|
|
6817
7050
|
onAdd
|
|
6818
7051
|
}) {
|
|
6819
|
-
return /* @__PURE__ */
|
|
6820
|
-
/* @__PURE__ */
|
|
6821
|
-
/* @__PURE__ */
|
|
6822
|
-
onAdd && /* @__PURE__ */
|
|
6823
|
-
onEdit && /* @__PURE__ */
|
|
6824
|
-
onDelete && /* @__PURE__ */
|
|
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(
|
|
6825
7058
|
IconButton3,
|
|
6826
7059
|
{
|
|
6827
7060
|
label: "Delete edge",
|
|
6828
7061
|
tone: "destructive",
|
|
6829
7062
|
revealOnHover: true,
|
|
6830
7063
|
onClick: onDelete,
|
|
6831
|
-
children: /* @__PURE__ */
|
|
7064
|
+
children: /* @__PURE__ */ jsx25(Trash22, {})
|
|
6832
7065
|
}
|
|
6833
7066
|
)
|
|
6834
7067
|
] })
|
|
@@ -6856,17 +7089,17 @@ function PairLists({
|
|
|
6856
7089
|
const to = entry.sourceId === nodeAId ? nodeBLabel : nodeALabel;
|
|
6857
7090
|
return `${entry.label} (${from} -> ${to})`;
|
|
6858
7091
|
};
|
|
6859
|
-
return /* @__PURE__ */
|
|
6860
|
-
empty && /* @__PURE__ */
|
|
7092
|
+
return /* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-4", children: [
|
|
7093
|
+
empty && /* @__PURE__ */ jsxs23("p", { className: "text-xs text-muted-foreground", children: [
|
|
6861
7094
|
"No edges are defined between ",
|
|
6862
7095
|
nodeALabel,
|
|
6863
7096
|
" and ",
|
|
6864
7097
|
nodeBLabel,
|
|
6865
7098
|
"."
|
|
6866
7099
|
] }),
|
|
6867
|
-
active.length > 0 && /* @__PURE__ */
|
|
6868
|
-
/* @__PURE__ */
|
|
6869
|
-
/* @__PURE__ */
|
|
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(
|
|
6870
7103
|
EdgeRow,
|
|
6871
7104
|
{
|
|
6872
7105
|
label: edge.label_,
|
|
@@ -6876,9 +7109,9 @@ function PairLists({
|
|
|
6876
7109
|
edge.id_
|
|
6877
7110
|
)) })
|
|
6878
7111
|
] }),
|
|
6879
|
-
available.length > 0 && /* @__PURE__ */
|
|
6880
|
-
/* @__PURE__ */
|
|
6881
|
-
/* @__PURE__ */
|
|
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(
|
|
6882
7115
|
EdgeRow,
|
|
6883
7116
|
{
|
|
6884
7117
|
label: rowLabel(entry),
|
|
@@ -6898,13 +7131,13 @@ function DraftForm({
|
|
|
6898
7131
|
}) {
|
|
6899
7132
|
const label = draft.mode === "create" ? draft.entry.label : draft.edge.label_;
|
|
6900
7133
|
const data = draft.mode === "edit" ? draft.edge.properties_ ?? {} : {};
|
|
6901
|
-
return /* @__PURE__ */
|
|
6902
|
-
/* @__PURE__ */
|
|
7134
|
+
return /* @__PURE__ */ jsxs23("div", { children: [
|
|
7135
|
+
/* @__PURE__ */ jsxs23(SectionTitle2, { children: [
|
|
6903
7136
|
draft.mode === "create" ? "Add" : "Edit",
|
|
6904
7137
|
" ",
|
|
6905
7138
|
label
|
|
6906
7139
|
] }),
|
|
6907
|
-
/* @__PURE__ */
|
|
7140
|
+
/* @__PURE__ */ jsx25(
|
|
6908
7141
|
JsonSchemaFormRenderer3,
|
|
6909
7142
|
{
|
|
6910
7143
|
schema,
|
|
@@ -6932,10 +7165,10 @@ function EdgeFormSheet({
|
|
|
6932
7165
|
widgets,
|
|
6933
7166
|
onChanged
|
|
6934
7167
|
}) {
|
|
6935
|
-
const [reloadKey, setReloadKey] =
|
|
6936
|
-
const [draft, setDraft] =
|
|
6937
|
-
const [error, setError] =
|
|
6938
|
-
const [pendingDelete, setPendingDelete] =
|
|
7168
|
+
const [reloadKey, setReloadKey] = useState16(0);
|
|
7169
|
+
const [draft, setDraft] = useState16(null);
|
|
7170
|
+
const [error, setError] = useState16("");
|
|
7171
|
+
const [pendingDelete, setPendingDelete] = useState16(null);
|
|
6939
7172
|
const { active, available } = usePairState(
|
|
6940
7173
|
port,
|
|
6941
7174
|
schemaProvider,
|
|
@@ -6950,18 +7183,18 @@ function EdgeFormSheet({
|
|
|
6950
7183
|
const editableTypes = useEditableEdgeTypes(schemaProvider, active);
|
|
6951
7184
|
const nodeAId = nodeA.id_;
|
|
6952
7185
|
const nodeBId = nodeB.id_;
|
|
6953
|
-
|
|
7186
|
+
useEffect8(() => {
|
|
6954
7187
|
if (open) {
|
|
6955
7188
|
setDraft(null);
|
|
6956
7189
|
setError("");
|
|
6957
7190
|
setPendingDelete(null);
|
|
6958
7191
|
}
|
|
6959
7192
|
}, [open, nodeAId, nodeBId]);
|
|
6960
|
-
const reload =
|
|
7193
|
+
const reload = useCallback10(() => {
|
|
6961
7194
|
setReloadKey((k) => k + 1);
|
|
6962
7195
|
onChanged?.();
|
|
6963
7196
|
}, [onChanged]);
|
|
6964
|
-
const handleSave =
|
|
7197
|
+
const handleSave = useCallback10(
|
|
6965
7198
|
(formData) => {
|
|
6966
7199
|
if (!draft) return;
|
|
6967
7200
|
const { properties } = extractRelationFields(formData);
|
|
@@ -6993,7 +7226,7 @@ function EdgeFormSheet({
|
|
|
6993
7226
|
},
|
|
6994
7227
|
[draft, port, reload]
|
|
6995
7228
|
);
|
|
6996
|
-
const confirmDelete =
|
|
7229
|
+
const confirmDelete = useCallback10(async () => {
|
|
6997
7230
|
const edge = pendingDelete;
|
|
6998
7231
|
if (!edge || edge.id_ == null) return;
|
|
6999
7232
|
try {
|
|
@@ -7006,19 +7239,19 @@ function EdgeFormSheet({
|
|
|
7006
7239
|
setError(e instanceof Error ? e.message : "Failed to delete edge");
|
|
7007
7240
|
}
|
|
7008
7241
|
}, [pendingDelete, port, reload]);
|
|
7009
|
-
return /* @__PURE__ */
|
|
7010
|
-
/* @__PURE__ */
|
|
7011
|
-
/* @__PURE__ */
|
|
7012
|
-
/* @__PURE__ */
|
|
7013
|
-
/* @__PURE__ */
|
|
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." })
|
|
7014
7247
|
] }),
|
|
7015
|
-
/* @__PURE__ */
|
|
7016
|
-
/* @__PURE__ */
|
|
7017
|
-
/* @__PURE__ */
|
|
7018
|
-
/* @__PURE__ */
|
|
7019
|
-
/* @__PURE__ */
|
|
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) })
|
|
7020
7253
|
] }),
|
|
7021
|
-
!draft && /* @__PURE__ */
|
|
7254
|
+
!draft && /* @__PURE__ */ jsx25(
|
|
7022
7255
|
PairLists,
|
|
7023
7256
|
{
|
|
7024
7257
|
nodeALabel: nodeA.label_,
|
|
@@ -7032,7 +7265,7 @@ function EdgeFormSheet({
|
|
|
7032
7265
|
onCreate: (entry) => setDraft({ mode: "create", entry })
|
|
7033
7266
|
}
|
|
7034
7267
|
),
|
|
7035
|
-
draft && schema && /* @__PURE__ */
|
|
7268
|
+
draft && schema && /* @__PURE__ */ jsx25(
|
|
7036
7269
|
DraftForm,
|
|
7037
7270
|
{
|
|
7038
7271
|
draft,
|
|
@@ -7042,10 +7275,10 @@ function EdgeFormSheet({
|
|
|
7042
7275
|
onCancel: () => setDraft(null)
|
|
7043
7276
|
}
|
|
7044
7277
|
),
|
|
7045
|
-
error && /* @__PURE__ */
|
|
7278
|
+
error && /* @__PURE__ */ jsx25(Alert3, { variant: "destructive", children: error })
|
|
7046
7279
|
] })
|
|
7047
7280
|
] }),
|
|
7048
|
-
/* @__PURE__ */
|
|
7281
|
+
/* @__PURE__ */ jsx25(
|
|
7049
7282
|
DeleteEdgeConfirm,
|
|
7050
7283
|
{
|
|
7051
7284
|
edge: pendingDelete,
|
|
@@ -7060,24 +7293,24 @@ function DeleteEdgeConfirm({
|
|
|
7060
7293
|
onCancel,
|
|
7061
7294
|
onConfirm
|
|
7062
7295
|
}) {
|
|
7063
|
-
return /* @__PURE__ */
|
|
7064
|
-
/* @__PURE__ */
|
|
7065
|
-
/* @__PURE__ */
|
|
7066
|
-
/* @__PURE__ */
|
|
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: [
|
|
7067
7300
|
"Delete the edge ",
|
|
7068
|
-
/* @__PURE__ */
|
|
7301
|
+
/* @__PURE__ */ jsx25("strong", { children: edge?.label_ }),
|
|
7069
7302
|
"? This removes the relationship between the two nodes."
|
|
7070
7303
|
] })
|
|
7071
7304
|
] }),
|
|
7072
|
-
/* @__PURE__ */
|
|
7073
|
-
/* @__PURE__ */
|
|
7074
|
-
/* @__PURE__ */
|
|
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" })
|
|
7075
7308
|
] })
|
|
7076
7309
|
] }) });
|
|
7077
7310
|
}
|
|
7078
7311
|
|
|
7079
7312
|
// src/graph-edit/DeleteNodeDialog.tsx
|
|
7080
|
-
import { useCallback as
|
|
7313
|
+
import { useCallback as useCallback11, useEffect as useEffect9, useState as useState17 } from "react";
|
|
7081
7314
|
import {
|
|
7082
7315
|
Dialog as Dialog2,
|
|
7083
7316
|
DialogContent as DialogContent2,
|
|
@@ -7085,19 +7318,19 @@ import {
|
|
|
7085
7318
|
DialogTitle as DialogTitle2,
|
|
7086
7319
|
DialogDescription as DialogDescription2,
|
|
7087
7320
|
DialogFooter as DialogFooter2,
|
|
7088
|
-
Button as
|
|
7089
|
-
Alert as
|
|
7321
|
+
Button as Button10,
|
|
7322
|
+
Alert as Alert4
|
|
7090
7323
|
} from "@petrarca/sonnet-ui";
|
|
7091
7324
|
import { errorLog as errorLog6 } from "@petrarca/sonnet-core";
|
|
7092
|
-
import { jsx as
|
|
7325
|
+
import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
7093
7326
|
var EMPTY_DEPENDENT = {
|
|
7094
7327
|
inbound: [],
|
|
7095
7328
|
outbound: [],
|
|
7096
7329
|
names: {}
|
|
7097
7330
|
};
|
|
7098
7331
|
function useDependentEdges(port, open, node) {
|
|
7099
|
-
const [dependent, setDependent] =
|
|
7100
|
-
|
|
7332
|
+
const [dependent, setDependent] = useState17(EMPTY_DEPENDENT);
|
|
7333
|
+
useEffect9(() => {
|
|
7101
7334
|
if (!open) return;
|
|
7102
7335
|
if (node.id_ == null) {
|
|
7103
7336
|
setDependent(EMPTY_DEPENDENT);
|
|
@@ -7147,12 +7380,12 @@ function EdgeList({
|
|
|
7147
7380
|
direction
|
|
7148
7381
|
}) {
|
|
7149
7382
|
if (edges.length === 0) return null;
|
|
7150
|
-
return /* @__PURE__ */
|
|
7151
|
-
/* @__PURE__ */
|
|
7152
|
-
/* @__PURE__ */
|
|
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) => {
|
|
7153
7386
|
const neighbour = direction === "inbound" ? names[edge.start_id_] : names[edge.end_id_];
|
|
7154
7387
|
const text = direction === "inbound" ? `${neighbour ?? "?"} \u2192 ${edge.label_}` : `${edge.label_} \u2192 ${neighbour ?? "?"}`;
|
|
7155
|
-
return /* @__PURE__ */
|
|
7388
|
+
return /* @__PURE__ */ jsx26("li", { children: neighbour ? text : edge.label_ }, edge.id_);
|
|
7156
7389
|
}) })
|
|
7157
7390
|
] });
|
|
7158
7391
|
}
|
|
@@ -7164,9 +7397,9 @@ function DeleteNodeDialog({
|
|
|
7164
7397
|
onDeleted
|
|
7165
7398
|
}) {
|
|
7166
7399
|
const { inbound, outbound, names } = useDependentEdges(port, open, node);
|
|
7167
|
-
const [error, setError] =
|
|
7400
|
+
const [error, setError] = useState17("");
|
|
7168
7401
|
const hasEdges = inbound.length > 0 || outbound.length > 0;
|
|
7169
|
-
const handleDelete =
|
|
7402
|
+
const handleDelete = useCallback11(() => {
|
|
7170
7403
|
if (node.id_ == null) return;
|
|
7171
7404
|
void (async () => {
|
|
7172
7405
|
try {
|
|
@@ -7179,17 +7412,17 @@ function DeleteNodeDialog({
|
|
|
7179
7412
|
}
|
|
7180
7413
|
})();
|
|
7181
7414
|
}, [port, node.id_, onDeleted, onOpenChange]);
|
|
7182
|
-
return /* @__PURE__ */
|
|
7183
|
-
/* @__PURE__ */
|
|
7184
|
-
/* @__PURE__ */
|
|
7185
|
-
/* @__PURE__ */
|
|
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: [
|
|
7186
7419
|
"Delete the node ",
|
|
7187
|
-
/* @__PURE__ */
|
|
7420
|
+
/* @__PURE__ */ jsx26("strong", { children: extractDisplayLabel(node) }),
|
|
7188
7421
|
"?"
|
|
7189
7422
|
] })
|
|
7190
7423
|
] }),
|
|
7191
|
-
!hasEdges && /* @__PURE__ */
|
|
7192
|
-
/* @__PURE__ */
|
|
7424
|
+
!hasEdges && /* @__PURE__ */ jsx26("p", { className: "text-sm text-muted-foreground", children: "No connected edges." }),
|
|
7425
|
+
/* @__PURE__ */ jsx26(
|
|
7193
7426
|
EdgeList,
|
|
7194
7427
|
{
|
|
7195
7428
|
title: "Inbound edges",
|
|
@@ -7198,7 +7431,7 @@ function DeleteNodeDialog({
|
|
|
7198
7431
|
direction: "inbound"
|
|
7199
7432
|
}
|
|
7200
7433
|
),
|
|
7201
|
-
/* @__PURE__ */
|
|
7434
|
+
/* @__PURE__ */ jsx26(
|
|
7202
7435
|
EdgeList,
|
|
7203
7436
|
{
|
|
7204
7437
|
title: "Outbound edges",
|
|
@@ -7207,16 +7440,16 @@ function DeleteNodeDialog({
|
|
|
7207
7440
|
direction: "outbound"
|
|
7208
7441
|
}
|
|
7209
7442
|
),
|
|
7210
|
-
error && /* @__PURE__ */
|
|
7211
|
-
/* @__PURE__ */
|
|
7212
|
-
/* @__PURE__ */
|
|
7213
|
-
/* @__PURE__ */
|
|
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" })
|
|
7214
7447
|
] })
|
|
7215
7448
|
] }) });
|
|
7216
7449
|
}
|
|
7217
7450
|
|
|
7218
7451
|
// src/graph-edit/GraphEditToolbar.tsx
|
|
7219
|
-
import { Fragment as Fragment8, jsx as
|
|
7452
|
+
import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
7220
7453
|
function toPerms(caps) {
|
|
7221
7454
|
const canCreateNode = caps.createNode === "edit";
|
|
7222
7455
|
const canUpdateNode = caps.updateNode === "edit";
|
|
@@ -7235,38 +7468,38 @@ function ToolbarButtons({
|
|
|
7235
7468
|
count,
|
|
7236
7469
|
onOpen
|
|
7237
7470
|
}) {
|
|
7238
|
-
return /* @__PURE__ */
|
|
7239
|
-
perms.canCreateNode && /* @__PURE__ */
|
|
7471
|
+
return /* @__PURE__ */ jsxs25("div", { className: "flex gap-0.5", children: [
|
|
7472
|
+
perms.canCreateNode && /* @__PURE__ */ jsx27(
|
|
7240
7473
|
IconButton4,
|
|
7241
7474
|
{
|
|
7242
7475
|
label: "Add node",
|
|
7243
7476
|
variant: "outline",
|
|
7244
7477
|
disabled: count > 0,
|
|
7245
7478
|
onClick: () => onOpen("node-create"),
|
|
7246
|
-
children: /* @__PURE__ */
|
|
7479
|
+
children: /* @__PURE__ */ jsx27(Plus3, {})
|
|
7247
7480
|
}
|
|
7248
7481
|
),
|
|
7249
|
-
perms.canUpdateNode && /* @__PURE__ */
|
|
7482
|
+
perms.canUpdateNode && /* @__PURE__ */ jsx27(
|
|
7250
7483
|
IconButton4,
|
|
7251
7484
|
{
|
|
7252
7485
|
label: "Edit node",
|
|
7253
7486
|
variant: "outline",
|
|
7254
7487
|
disabled: count !== 1,
|
|
7255
7488
|
onClick: () => onOpen("node-edit"),
|
|
7256
|
-
children: /* @__PURE__ */
|
|
7489
|
+
children: /* @__PURE__ */ jsx27(Edit, {})
|
|
7257
7490
|
}
|
|
7258
7491
|
),
|
|
7259
|
-
perms.canEditEdges && /* @__PURE__ */
|
|
7492
|
+
perms.canEditEdges && /* @__PURE__ */ jsx27(
|
|
7260
7493
|
IconButton4,
|
|
7261
7494
|
{
|
|
7262
7495
|
label: "Edges",
|
|
7263
7496
|
variant: "outline",
|
|
7264
7497
|
disabled: count !== 2,
|
|
7265
7498
|
onClick: () => onOpen("edge"),
|
|
7266
|
-
children: /* @__PURE__ */
|
|
7499
|
+
children: /* @__PURE__ */ jsx27(Link, {})
|
|
7267
7500
|
}
|
|
7268
7501
|
),
|
|
7269
|
-
perms.canDeleteNode && /* @__PURE__ */
|
|
7502
|
+
perms.canDeleteNode && /* @__PURE__ */ jsx27(
|
|
7270
7503
|
IconButton4,
|
|
7271
7504
|
{
|
|
7272
7505
|
label: "Delete node",
|
|
@@ -7274,7 +7507,7 @@ function ToolbarButtons({
|
|
|
7274
7507
|
tone: "destructive",
|
|
7275
7508
|
disabled: count !== 1,
|
|
7276
7509
|
onClick: () => onOpen("delete"),
|
|
7277
|
-
children: /* @__PURE__ */
|
|
7510
|
+
children: /* @__PURE__ */ jsx27(Trash23, {})
|
|
7278
7511
|
}
|
|
7279
7512
|
)
|
|
7280
7513
|
] });
|
|
@@ -7290,14 +7523,14 @@ function ToolbarDialogs({
|
|
|
7290
7523
|
onClose,
|
|
7291
7524
|
onMutated
|
|
7292
7525
|
}) {
|
|
7293
|
-
const onChange =
|
|
7526
|
+
const onChange = useCallback12(
|
|
7294
7527
|
(o) => {
|
|
7295
7528
|
if (!o) onClose();
|
|
7296
7529
|
},
|
|
7297
7530
|
[onClose]
|
|
7298
7531
|
);
|
|
7299
|
-
return /* @__PURE__ */
|
|
7300
|
-
(perms.canCreateNode || perms.canUpdateNode) && /* @__PURE__ */
|
|
7532
|
+
return /* @__PURE__ */ jsxs25(Fragment8, { children: [
|
|
7533
|
+
(perms.canCreateNode || perms.canUpdateNode) && /* @__PURE__ */ jsx27(
|
|
7301
7534
|
NodeFormSheet,
|
|
7302
7535
|
{
|
|
7303
7536
|
open: dialog === "node-create" || dialog === "node-edit",
|
|
@@ -7309,7 +7542,7 @@ function ToolbarDialogs({
|
|
|
7309
7542
|
onSaved: onMutated
|
|
7310
7543
|
}
|
|
7311
7544
|
),
|
|
7312
|
-
perms.canEditEdges && firstNode && secondNode && /* @__PURE__ */
|
|
7545
|
+
perms.canEditEdges && firstNode && secondNode && /* @__PURE__ */ jsx27(
|
|
7313
7546
|
EdgeFormSheet,
|
|
7314
7547
|
{
|
|
7315
7548
|
open: dialog === "edge",
|
|
@@ -7322,7 +7555,7 @@ function ToolbarDialogs({
|
|
|
7322
7555
|
onChanged: onMutated
|
|
7323
7556
|
}
|
|
7324
7557
|
),
|
|
7325
|
-
perms.canDeleteNode && firstNode && /* @__PURE__ */
|
|
7558
|
+
perms.canDeleteNode && firstNode && /* @__PURE__ */ jsx27(
|
|
7326
7559
|
DeleteNodeDialog,
|
|
7327
7560
|
{
|
|
7328
7561
|
open: dialog === "delete",
|
|
@@ -7347,7 +7580,7 @@ function GraphEditToolbar({
|
|
|
7347
7580
|
(s) => s.selectedNodeIds
|
|
7348
7581
|
);
|
|
7349
7582
|
const live = useLiveGraphData();
|
|
7350
|
-
const [dialog, setDialog] =
|
|
7583
|
+
const [dialog, setDialog] = useState18(null);
|
|
7351
7584
|
const perms = useMemo11(
|
|
7352
7585
|
() => toPerms(resolveGraphCapabilities(capabilities, readOnly)),
|
|
7353
7586
|
[capabilities, readOnly]
|
|
@@ -7358,8 +7591,8 @@ function GraphEditToolbar({
|
|
|
7358
7591
|
);
|
|
7359
7592
|
const stableOnMutated = useStableRef(onMutated);
|
|
7360
7593
|
if (!perms.anyEditable) return null;
|
|
7361
|
-
return /* @__PURE__ */
|
|
7362
|
-
/* @__PURE__ */
|
|
7594
|
+
return /* @__PURE__ */ jsxs25(Fragment8, { children: [
|
|
7595
|
+
/* @__PURE__ */ jsx27(
|
|
7363
7596
|
ToolbarButtons,
|
|
7364
7597
|
{
|
|
7365
7598
|
perms,
|
|
@@ -7367,7 +7600,7 @@ function GraphEditToolbar({
|
|
|
7367
7600
|
onOpen: setDialog
|
|
7368
7601
|
}
|
|
7369
7602
|
),
|
|
7370
|
-
/* @__PURE__ */
|
|
7603
|
+
/* @__PURE__ */ jsx27(
|
|
7371
7604
|
ToolbarDialogs,
|
|
7372
7605
|
{
|
|
7373
7606
|
perms,
|
|
@@ -7383,6 +7616,662 @@ function GraphEditToolbar({
|
|
|
7383
7616
|
)
|
|
7384
7617
|
] });
|
|
7385
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
|
+
}
|
|
7386
8275
|
export {
|
|
7387
8276
|
ActionPanel_default as ActionPanel,
|
|
7388
8277
|
DeleteNodeDialog,
|
|
@@ -7391,6 +8280,7 @@ export {
|
|
|
7391
8280
|
EdgeInfoCard_default as EdgeInfoCard,
|
|
7392
8281
|
GraphA11yList,
|
|
7393
8282
|
GraphEditToolbar,
|
|
8283
|
+
GraphFinder,
|
|
7394
8284
|
GraphModelEditor,
|
|
7395
8285
|
GraphRendererProvider,
|
|
7396
8286
|
GraphStoresContext,
|
|
@@ -7399,8 +8289,11 @@ export {
|
|
|
7399
8289
|
NodeFormSheet,
|
|
7400
8290
|
NodeInfoCard_default as NodeInfoCard,
|
|
7401
8291
|
NodeTypeNameSheet,
|
|
8292
|
+
OramaSearchService,
|
|
7402
8293
|
Overview_default as Overview,
|
|
7403
8294
|
PropertyFormSheet,
|
|
8295
|
+
QueryEditor,
|
|
8296
|
+
QueryHistory,
|
|
7404
8297
|
RelationFormSheet,
|
|
7405
8298
|
TypeDetailPanel,
|
|
7406
8299
|
addEdgeType,
|
|
@@ -7415,6 +8308,7 @@ export {
|
|
|
7415
8308
|
createGraphExplorationStore,
|
|
7416
8309
|
createGraphFilterStore,
|
|
7417
8310
|
createGraphFocusStore,
|
|
8311
|
+
createQueryHistoryStore,
|
|
7418
8312
|
createSelectionStore,
|
|
7419
8313
|
deleteEdgeType,
|
|
7420
8314
|
deleteEdgeTypeProperty,
|
|
@@ -7430,9 +8324,11 @@ export {
|
|
|
7430
8324
|
enrichNodes,
|
|
7431
8325
|
enrichSchemaWithRelations,
|
|
7432
8326
|
extractDisplayLabel,
|
|
8327
|
+
extractGraphFromRows,
|
|
7433
8328
|
extractRelationFields,
|
|
7434
8329
|
findEdgeType,
|
|
7435
8330
|
findType,
|
|
8331
|
+
flattenGraphData,
|
|
7436
8332
|
graphCapabilitiesSchema,
|
|
7437
8333
|
groupEdgesByRelation,
|
|
7438
8334
|
hasProperties,
|
|
@@ -7470,12 +8366,14 @@ export {
|
|
|
7470
8366
|
useCytoscapeData,
|
|
7471
8367
|
GraphFocus_default as useGraphFocus,
|
|
7472
8368
|
useGraphRenderer,
|
|
8369
|
+
useGraphSearch,
|
|
7473
8370
|
useLiveGraphData,
|
|
7474
8371
|
useReagraphData,
|
|
7475
8372
|
useStableRef,
|
|
7476
8373
|
useWorkspaceGraphData,
|
|
7477
8374
|
useWorkspaceGraphExploration,
|
|
7478
8375
|
useWorkspaceGraphFilter,
|
|
8376
|
+
useWorkspaceGraphFocus,
|
|
7479
8377
|
useWorkspaceSelection,
|
|
7480
8378
|
validateCapabilities,
|
|
7481
8379
|
validateGraphCapabilities
|