@petrarca/sonnet-graph 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +20 -7
- package/dist/index.js +508 -247
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -436,7 +436,8 @@ var useGraphFilter = createGraphFilterStore();
|
|
|
436
436
|
|
|
437
437
|
// src/stores/GraphExploration.ts
|
|
438
438
|
import { create as create4 } from "zustand";
|
|
439
|
-
import { devLog as devLog3 } from "@petrarca/sonnet-core";
|
|
439
|
+
import { devLog as devLog3, errorLog } from "@petrarca/sonnet-core";
|
|
440
|
+
import { toast } from "sonner";
|
|
440
441
|
function collectReachable(startId, direction, allEdges, includeStart) {
|
|
441
442
|
const nodes = /* @__PURE__ */ new Set();
|
|
442
443
|
const edges = /* @__PURE__ */ new Set();
|
|
@@ -542,6 +543,23 @@ function ensureNodeLabelInFilters(enrichedNodes, nodeId, graphFilterStore) {
|
|
|
542
543
|
graphFilterStore.getState().setNodeLabelFilters([...currentFilters, nodeLabel]);
|
|
543
544
|
}
|
|
544
545
|
}
|
|
546
|
+
function mergeExpandedLabelsIntoFilters(stores, enrichedNodes, nodeId) {
|
|
547
|
+
const graphFilterState = stores.graphFilter.getState();
|
|
548
|
+
const currentFilters = graphFilterState.nodeLabelFilters;
|
|
549
|
+
if (currentFilters.length === 0 || enrichedNodes.length === 0) return;
|
|
550
|
+
const newLabels = new Set(enrichedNodes.map((n) => n.label_));
|
|
551
|
+
const labelsToAdd = Array.from(newLabels).filter(
|
|
552
|
+
(label) => !currentFilters.includes(label)
|
|
553
|
+
);
|
|
554
|
+
if (labelsToAdd.length > 0) {
|
|
555
|
+
devLog3("[GraphExploration] Adding expanded node labels to active filters", {
|
|
556
|
+
nodeId,
|
|
557
|
+
labelsToAdd,
|
|
558
|
+
currentFilters
|
|
559
|
+
});
|
|
560
|
+
stores.graphFilter.getState().setNodeLabelFilters([...currentFilters, ...labelsToAdd]);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
545
563
|
function createGraphExplorationStore(deps) {
|
|
546
564
|
const stores = deps;
|
|
547
565
|
return create4()((set, get) => ({
|
|
@@ -841,10 +859,10 @@ function createGraphExplorationStore(deps) {
|
|
|
841
859
|
isNodeExpanded: (nodeId) => {
|
|
842
860
|
return get().expandedNodeIds.has(nodeId);
|
|
843
861
|
},
|
|
844
|
-
expandNode: async (nodeId) => {
|
|
845
|
-
devLog3("[GraphExploration] expandNode", { nodeId });
|
|
862
|
+
expandNode: async (nodeId, options) => {
|
|
863
|
+
devLog3("[GraphExploration] expandNode", { nodeId, options });
|
|
846
864
|
try {
|
|
847
|
-
const result = await stores.expandNode(Number(nodeId));
|
|
865
|
+
const result = await stores.expandNode(Number(nodeId), options);
|
|
848
866
|
devLog3("[GraphExploration] Expansion API result", {
|
|
849
867
|
nodeId,
|
|
850
868
|
newNodes: result.nodes.length,
|
|
@@ -866,25 +884,7 @@ function createGraphExplorationStore(deps) {
|
|
|
866
884
|
);
|
|
867
885
|
const enrichedEdges = enrichEdges(result.edges);
|
|
868
886
|
stores.graphData.getState().mergeExpandedData(enrichedNodes, enrichedEdges);
|
|
869
|
-
|
|
870
|
-
const currentFilters = graphFilterState.nodeLabelFilters;
|
|
871
|
-
if (currentFilters.length > 0 && enrichedNodes.length > 0) {
|
|
872
|
-
const newLabels = new Set(enrichedNodes.map((n) => n.label_));
|
|
873
|
-
const labelsToAdd = Array.from(newLabels).filter(
|
|
874
|
-
(label) => !currentFilters.includes(label)
|
|
875
|
-
);
|
|
876
|
-
if (labelsToAdd.length > 0) {
|
|
877
|
-
devLog3(
|
|
878
|
-
"[GraphExploration] Adding expanded node labels to active filters",
|
|
879
|
-
{
|
|
880
|
-
nodeId,
|
|
881
|
-
labelsToAdd,
|
|
882
|
-
currentFilters
|
|
883
|
-
}
|
|
884
|
-
);
|
|
885
|
-
stores.graphFilter.getState().setNodeLabelFilters([...currentFilters, ...labelsToAdd]);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
887
|
+
mergeExpandedLabelsIntoFilters(stores, enrichedNodes, nodeId);
|
|
888
888
|
get().markNodeExpanded(nodeId);
|
|
889
889
|
const currentState = get();
|
|
890
890
|
if (currentState.isFocusMode) {
|
|
@@ -900,13 +900,19 @@ function createGraphExplorationStore(deps) {
|
|
|
900
900
|
});
|
|
901
901
|
set({ focusedNodeIds: newFocusedIds });
|
|
902
902
|
}
|
|
903
|
+
if (result.has_more) {
|
|
904
|
+
toast.info(
|
|
905
|
+
`Showing first ${enrichedNodes.length} neighbors \u2014 more exist. Refine filters or increase the limit.`
|
|
906
|
+
);
|
|
907
|
+
}
|
|
903
908
|
devLog3("[GraphExploration] Expansion complete", {
|
|
904
909
|
nodeId,
|
|
905
910
|
addedNodes: enrichedNodes.length,
|
|
906
|
-
addedEdges: enrichedEdges.length
|
|
911
|
+
addedEdges: enrichedEdges.length,
|
|
912
|
+
hasMore: result.has_more
|
|
907
913
|
});
|
|
908
914
|
} catch (error) {
|
|
909
|
-
|
|
915
|
+
errorLog("[GraphExploration] Expansion failed", { nodeId, error });
|
|
910
916
|
throw error;
|
|
911
917
|
}
|
|
912
918
|
},
|
|
@@ -1439,10 +1445,10 @@ function GraphRendererProvider({
|
|
|
1439
1445
|
|
|
1440
1446
|
// src/components/rea/ReaGraph.tsx
|
|
1441
1447
|
import {
|
|
1442
|
-
useEffect as
|
|
1443
|
-
useRef,
|
|
1444
|
-
useState as
|
|
1445
|
-
useCallback as
|
|
1448
|
+
useEffect as useEffect3,
|
|
1449
|
+
useRef as useRef2,
|
|
1450
|
+
useState as useState5,
|
|
1451
|
+
useCallback as useCallback3
|
|
1446
1452
|
} from "react";
|
|
1447
1453
|
import { devLog as devLog8 } from "@petrarca/sonnet-core";
|
|
1448
1454
|
import {
|
|
@@ -1662,16 +1668,201 @@ function NodeHoverInfo({ node }) {
|
|
|
1662
1668
|
var NodeHoverInfo_default = NodeHoverInfo;
|
|
1663
1669
|
|
|
1664
1670
|
// src/components/rea/ReaContextMenu.tsx
|
|
1665
|
-
import
|
|
1671
|
+
import React3, { useState as useState4 } from "react";
|
|
1666
1672
|
import { devLog as devLog7 } from "@petrarca/sonnet-core";
|
|
1673
|
+
|
|
1674
|
+
// src/components/rea/ExpandOptionsPanel.tsx
|
|
1675
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef, useState as useState3 } from "react";
|
|
1676
|
+
import { Checkbox, Input, Label } from "@petrarca/sonnet-ui";
|
|
1667
1677
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1678
|
+
function ExpandOptionsPanel({
|
|
1679
|
+
nodeLabel,
|
|
1680
|
+
onExpand,
|
|
1681
|
+
onBack,
|
|
1682
|
+
getEdgeTypes
|
|
1683
|
+
}) {
|
|
1684
|
+
const [direction, setDirection] = useState3("both");
|
|
1685
|
+
const [limit, setLimit] = useState3(100);
|
|
1686
|
+
const [edgeTypes, setEdgeTypes] = useState3([]);
|
|
1687
|
+
const [selectedTypes, setSelectedTypes] = useState3([]);
|
|
1688
|
+
const [loading, setLoading] = useState3(!!getEdgeTypes);
|
|
1689
|
+
const [offset, setOffset] = useState3({ x: 0, y: 0 });
|
|
1690
|
+
const expandBtnRef = useRef(null);
|
|
1691
|
+
const dragRef = useRef(null);
|
|
1692
|
+
const onPointerDown = useCallback2(
|
|
1693
|
+
(e) => {
|
|
1694
|
+
if (e.target.closest("button")) return;
|
|
1695
|
+
e.preventDefault();
|
|
1696
|
+
e.target.setPointerCapture(e.pointerId);
|
|
1697
|
+
dragRef.current = {
|
|
1698
|
+
startX: e.clientX,
|
|
1699
|
+
startY: e.clientY,
|
|
1700
|
+
origX: offset.x,
|
|
1701
|
+
origY: offset.y
|
|
1702
|
+
};
|
|
1703
|
+
},
|
|
1704
|
+
[offset]
|
|
1705
|
+
);
|
|
1706
|
+
const onPointerMove = useCallback2((e) => {
|
|
1707
|
+
if (!dragRef.current) return;
|
|
1708
|
+
const dx = e.clientX - dragRef.current.startX;
|
|
1709
|
+
const dy = e.clientY - dragRef.current.startY;
|
|
1710
|
+
setOffset({ x: dragRef.current.origX + dx, y: dragRef.current.origY + dy });
|
|
1711
|
+
}, []);
|
|
1712
|
+
const onPointerUp = useCallback2(() => {
|
|
1713
|
+
dragRef.current = null;
|
|
1714
|
+
}, []);
|
|
1715
|
+
useEffect2(() => {
|
|
1716
|
+
if (!getEdgeTypes) return;
|
|
1717
|
+
setLoading(true);
|
|
1718
|
+
getEdgeTypes().then((types) => {
|
|
1719
|
+
setEdgeTypes(types);
|
|
1720
|
+
setSelectedTypes(types);
|
|
1721
|
+
}).finally(() => setLoading(false));
|
|
1722
|
+
}, [getEdgeTypes]);
|
|
1723
|
+
const handleToggleType = (type) => {
|
|
1724
|
+
setSelectedTypes(
|
|
1725
|
+
(prev) => prev.includes(type) ? prev.filter((t) => t !== type) : [...prev, type]
|
|
1726
|
+
);
|
|
1727
|
+
};
|
|
1728
|
+
const handleExpand = () => {
|
|
1729
|
+
const options = {};
|
|
1730
|
+
if (direction !== "both") options.direction = direction;
|
|
1731
|
+
const clampedLimit = Math.max(1, Math.min(1e4, limit));
|
|
1732
|
+
if (clampedLimit !== 100) options.limit = clampedLimit;
|
|
1733
|
+
if (selectedTypes.length > 0 && selectedTypes.length < edgeTypes.length) {
|
|
1734
|
+
options.relTypes = selectedTypes;
|
|
1735
|
+
}
|
|
1736
|
+
onExpand(options);
|
|
1737
|
+
};
|
|
1738
|
+
const allSelected = selectedTypes.length === edgeTypes.length;
|
|
1739
|
+
const noneSelected = edgeTypes.length > 0 && selectedTypes.length === 0;
|
|
1740
|
+
useEffect2(() => {
|
|
1741
|
+
if (!loading && !noneSelected) {
|
|
1742
|
+
expandBtnRef.current?.focus();
|
|
1743
|
+
}
|
|
1744
|
+
}, [loading, noneSelected]);
|
|
1745
|
+
return /* @__PURE__ */ jsxs4(
|
|
1746
|
+
"div",
|
|
1747
|
+
{
|
|
1748
|
+
className: "bg-background border border-border rounded-md shadow-md p-2.5 min-w-48 space-y-2",
|
|
1749
|
+
style: { transform: `translate(${offset.x}px, ${offset.y}px)` },
|
|
1750
|
+
children: [
|
|
1751
|
+
/* @__PURE__ */ jsxs4(
|
|
1752
|
+
"div",
|
|
1753
|
+
{
|
|
1754
|
+
className: "flex items-center justify-between gap-2 cursor-grab active:cursor-grabbing select-none",
|
|
1755
|
+
onPointerDown,
|
|
1756
|
+
onPointerMove,
|
|
1757
|
+
onPointerUp,
|
|
1758
|
+
children: [
|
|
1759
|
+
/* @__PURE__ */ jsx5(
|
|
1760
|
+
"button",
|
|
1761
|
+
{
|
|
1762
|
+
className: "text-[11px] text-muted-foreground hover:text-foreground cursor-pointer",
|
|
1763
|
+
onClick: onBack,
|
|
1764
|
+
children: "Back"
|
|
1765
|
+
}
|
|
1766
|
+
),
|
|
1767
|
+
/* @__PURE__ */ jsx5("span", { className: "text-[11px] font-medium text-foreground truncate max-w-32", children: nodeLabel })
|
|
1768
|
+
]
|
|
1769
|
+
}
|
|
1770
|
+
),
|
|
1771
|
+
/* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
|
|
1772
|
+
/* @__PURE__ */ jsx5(Label, { className: "text-[11px] text-muted-foreground", children: "Direction" }),
|
|
1773
|
+
/* @__PURE__ */ jsx5("div", { className: "flex gap-0.5", children: ["both", "out", "in"].map((dir) => /* @__PURE__ */ jsx5(
|
|
1774
|
+
"button",
|
|
1775
|
+
{
|
|
1776
|
+
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"}`,
|
|
1777
|
+
onClick: () => setDirection(dir),
|
|
1778
|
+
children: dir
|
|
1779
|
+
},
|
|
1780
|
+
dir
|
|
1781
|
+
)) })
|
|
1782
|
+
] }),
|
|
1783
|
+
!loading && edgeTypes.length > 1 && /* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
|
|
1784
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between", children: [
|
|
1785
|
+
/* @__PURE__ */ jsx5(Label, { className: "text-[11px] text-muted-foreground", children: "Rel Types" }),
|
|
1786
|
+
/* @__PURE__ */ jsx5(
|
|
1787
|
+
"button",
|
|
1788
|
+
{
|
|
1789
|
+
className: "text-[10px] text-muted-foreground hover:text-foreground cursor-pointer",
|
|
1790
|
+
onClick: () => setSelectedTypes(allSelected ? [] : [...edgeTypes]),
|
|
1791
|
+
children: allSelected ? "None" : "All"
|
|
1792
|
+
}
|
|
1793
|
+
)
|
|
1794
|
+
] }),
|
|
1795
|
+
/* @__PURE__ */ jsx5("div", { className: "space-y-0.5 max-h-32 overflow-y-auto", children: edgeTypes.map((type) => /* @__PURE__ */ jsxs4(
|
|
1796
|
+
"label",
|
|
1797
|
+
{
|
|
1798
|
+
className: "flex items-center gap-1.5 text-[11px] cursor-pointer",
|
|
1799
|
+
children: [
|
|
1800
|
+
/* @__PURE__ */ jsx5(
|
|
1801
|
+
Checkbox,
|
|
1802
|
+
{
|
|
1803
|
+
checked: selectedTypes.includes(type),
|
|
1804
|
+
onCheckedChange: () => handleToggleType(type),
|
|
1805
|
+
className: "h-3 w-3"
|
|
1806
|
+
}
|
|
1807
|
+
),
|
|
1808
|
+
/* @__PURE__ */ jsx5("span", { className: "truncate", children: type })
|
|
1809
|
+
]
|
|
1810
|
+
},
|
|
1811
|
+
type
|
|
1812
|
+
)) })
|
|
1813
|
+
] }),
|
|
1814
|
+
/* @__PURE__ */ jsxs4("div", { className: "space-y-1", children: [
|
|
1815
|
+
/* @__PURE__ */ jsx5(Label, { className: "text-[11px] text-muted-foreground", children: "Limit" }),
|
|
1816
|
+
/* @__PURE__ */ jsx5(
|
|
1817
|
+
Input,
|
|
1818
|
+
{
|
|
1819
|
+
type: "number",
|
|
1820
|
+
min: 1,
|
|
1821
|
+
max: 1e4,
|
|
1822
|
+
value: limit,
|
|
1823
|
+
onChange: (e) => {
|
|
1824
|
+
const v = Number(e.target.value);
|
|
1825
|
+
if (!Number.isNaN(v)) setLimit(Math.max(1, Math.min(1e4, v)));
|
|
1826
|
+
},
|
|
1827
|
+
className: "h-6 text-[11px]"
|
|
1828
|
+
}
|
|
1829
|
+
)
|
|
1830
|
+
] }),
|
|
1831
|
+
/* @__PURE__ */ jsx5(
|
|
1832
|
+
"button",
|
|
1833
|
+
{
|
|
1834
|
+
ref: expandBtnRef,
|
|
1835
|
+
className: "w-full py-1.5 text-[11px] font-medium rounded bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer",
|
|
1836
|
+
onClick: handleExpand,
|
|
1837
|
+
disabled: loading || noneSelected,
|
|
1838
|
+
title: noneSelected ? "Select at least one relationship type" : void 0,
|
|
1839
|
+
children: "Expand"
|
|
1840
|
+
}
|
|
1841
|
+
)
|
|
1842
|
+
]
|
|
1843
|
+
}
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
var ExpandOptionsPanel_default = ExpandOptionsPanel;
|
|
1847
|
+
|
|
1848
|
+
// src/components/rea/ReaContextMenu.tsx
|
|
1849
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1668
1850
|
function ReaContextMenu({
|
|
1669
1851
|
data,
|
|
1670
1852
|
onClose,
|
|
1671
1853
|
elementType,
|
|
1672
1854
|
useGraphExploration,
|
|
1673
|
-
onCopyNode
|
|
1855
|
+
onCopyNode,
|
|
1856
|
+
getNodeEdgeTypes
|
|
1674
1857
|
}) {
|
|
1858
|
+
const [showExpandPanel, setShowExpandPanel] = useState4(false);
|
|
1859
|
+
React3.useEffect(() => {
|
|
1860
|
+
const handleKey = (e) => {
|
|
1861
|
+
if (e.key === "Escape") onClose();
|
|
1862
|
+
};
|
|
1863
|
+
document.addEventListener("keydown", handleKey);
|
|
1864
|
+
return () => document.removeEventListener("keydown", handleKey);
|
|
1865
|
+
}, [onClose]);
|
|
1675
1866
|
const handleFocus = () => {
|
|
1676
1867
|
if (elementType === "node") {
|
|
1677
1868
|
const nodeId = data.id;
|
|
@@ -1724,11 +1915,11 @@ function ReaContextMenu({
|
|
|
1724
1915
|
}
|
|
1725
1916
|
onClose();
|
|
1726
1917
|
};
|
|
1727
|
-
const
|
|
1918
|
+
const handleExpandWithOptions = (options) => {
|
|
1728
1919
|
if (elementType === "node") {
|
|
1729
1920
|
const nodeId = data.id;
|
|
1730
|
-
devLog7("[ReaContextMenu] Expand node:", nodeId);
|
|
1731
|
-
useGraphExploration.getState().expandNode(nodeId);
|
|
1921
|
+
devLog7("[ReaContextMenu] Expand node with options:", nodeId, options);
|
|
1922
|
+
useGraphExploration.getState().expandNode(nodeId, options);
|
|
1732
1923
|
}
|
|
1733
1924
|
onClose();
|
|
1734
1925
|
};
|
|
@@ -1763,7 +1954,7 @@ function ReaContextMenu({
|
|
|
1763
1954
|
{ label: "Hide Node", action: handleHideNode },
|
|
1764
1955
|
{ label: "Hide Incoming", action: handleHideIncoming },
|
|
1765
1956
|
{ label: "Hide Outgoing", action: handleHideOutgoing },
|
|
1766
|
-
{ label: "Expand", action:
|
|
1957
|
+
{ label: "Expand...", action: () => setShowExpandPanel(true) }
|
|
1767
1958
|
];
|
|
1768
1959
|
const edgeMenuItems = [
|
|
1769
1960
|
{ label: "Focus", action: handleFocus },
|
|
@@ -1773,23 +1964,46 @@ function ReaContextMenu({
|
|
|
1773
1964
|
{ label: "Hide Reverse Path", action: handleHideReversePath }
|
|
1774
1965
|
];
|
|
1775
1966
|
const menuItems = elementType === "node" ? nodeMenuItems : edgeMenuItems;
|
|
1776
|
-
|
|
1777
|
-
|
|
1967
|
+
if (showExpandPanel && elementType === "node") {
|
|
1968
|
+
const nodeLabel = data.label || data.id;
|
|
1969
|
+
return /* @__PURE__ */ jsx6(
|
|
1970
|
+
ExpandOptionsPanel_default,
|
|
1971
|
+
{
|
|
1972
|
+
nodeLabel: String(nodeLabel),
|
|
1973
|
+
onExpand: handleExpandWithOptions,
|
|
1974
|
+
onBack: () => setShowExpandPanel(false),
|
|
1975
|
+
getEdgeTypes: getNodeEdgeTypes ? () => getNodeEdgeTypes(Number(data.id)) : void 0
|
|
1976
|
+
}
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
return /* @__PURE__ */ jsxs5("div", { className: "bg-background border border-border rounded-md shadow-lg py-1 min-w-48", children: [
|
|
1980
|
+
menuItems.map((item, index) => /* @__PURE__ */ jsxs5(React3.Fragment, { children: [
|
|
1981
|
+
/* @__PURE__ */ jsx6(
|
|
1982
|
+
"button",
|
|
1983
|
+
{
|
|
1984
|
+
className: "w-full text-left px-3 py-2 text-sm hover:bg-muted/50 cursor-pointer",
|
|
1985
|
+
onClick: item.action,
|
|
1986
|
+
children: item.label
|
|
1987
|
+
}
|
|
1988
|
+
),
|
|
1989
|
+
index === 1 && /* @__PURE__ */ jsx6("div", { className: "border-t border-border my-1" })
|
|
1990
|
+
] }, item.label)),
|
|
1991
|
+
/* @__PURE__ */ jsx6("div", { className: "border-t border-border my-1" }),
|
|
1992
|
+
/* @__PURE__ */ jsx6(
|
|
1778
1993
|
"button",
|
|
1779
1994
|
{
|
|
1780
|
-
className: "w-full text-left px-3 py-
|
|
1781
|
-
onClick:
|
|
1782
|
-
children:
|
|
1995
|
+
className: "w-full text-left px-3 py-1.5 text-xs text-muted-foreground hover:bg-muted/50 cursor-pointer",
|
|
1996
|
+
onClick: onClose,
|
|
1997
|
+
children: "Cancel"
|
|
1783
1998
|
}
|
|
1784
|
-
)
|
|
1785
|
-
|
|
1786
|
-
] }, item.label)) });
|
|
1999
|
+
)
|
|
2000
|
+
] });
|
|
1787
2001
|
}
|
|
1788
2002
|
var ReaContextMenu_default = ReaContextMenu;
|
|
1789
2003
|
|
|
1790
2004
|
// src/components/rea/ReaGraph.tsx
|
|
1791
|
-
import { toast } from "sonner";
|
|
1792
|
-
import { jsx as
|
|
2005
|
+
import { toast as toast2 } from "sonner";
|
|
2006
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1793
2007
|
function findElementInGraphData(elementId, elementType, nodes, edges) {
|
|
1794
2008
|
if (elementType === "node") {
|
|
1795
2009
|
const node = nodes.find((n) => n.id === elementId);
|
|
@@ -1851,22 +2065,26 @@ function processFocusAction(focusAction, canvasRef, nodes, edges, setSelectedNod
|
|
|
1851
2065
|
}
|
|
1852
2066
|
return true;
|
|
1853
2067
|
}
|
|
1854
|
-
function ReaGraph({
|
|
1855
|
-
|
|
2068
|
+
function ReaGraph({
|
|
2069
|
+
onCopyNode,
|
|
2070
|
+
extraToolbarContent,
|
|
2071
|
+
getNodeEdgeTypes
|
|
2072
|
+
}) {
|
|
2073
|
+
const isMountedRef = useRef2(false);
|
|
1856
2074
|
const { nodes, edges } = useReagraphData();
|
|
1857
2075
|
const { normalized } = useLiveGraphData();
|
|
1858
|
-
|
|
2076
|
+
useEffect3(() => {
|
|
1859
2077
|
devLog8("[ReGraph] counts", { nodes: nodes.length, edges: edges.length });
|
|
1860
2078
|
if (nodes.length > 0) devLog8("[ReGraph] sample node", nodes[0]);
|
|
1861
2079
|
if (edges.length > 0) devLog8("[ReGraph] sample edge", edges[0]);
|
|
1862
2080
|
}, [nodes, edges]);
|
|
1863
|
-
const canvas =
|
|
1864
|
-
const [hoveredNode, setHoveredNode] =
|
|
2081
|
+
const canvas = useRef2(null);
|
|
2082
|
+
const [hoveredNode, setHoveredNode] = useState5(
|
|
1865
2083
|
void 0
|
|
1866
2084
|
);
|
|
1867
|
-
const [layout, setLayout] =
|
|
1868
|
-
const [fullscreen, setFullscreen] =
|
|
1869
|
-
const [pathSelectionType, setPathSelectionType] =
|
|
2085
|
+
const [layout, setLayout] = useState5("forceDirected2d");
|
|
2086
|
+
const [fullscreen, setFullscreen] = useState5(false);
|
|
2087
|
+
const [pathSelectionType, setPathSelectionType] = useState5("direct");
|
|
1870
2088
|
const useGraphSelection = useWorkspaceSelection();
|
|
1871
2089
|
const useGraphFilter2 = useWorkspaceGraphFilter();
|
|
1872
2090
|
const useGraphExploration = useWorkspaceGraphExploration();
|
|
@@ -1892,7 +2110,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
1892
2110
|
pathSelectionType,
|
|
1893
2111
|
type: "multi"
|
|
1894
2112
|
});
|
|
1895
|
-
|
|
2113
|
+
useEffect3(() => {
|
|
1896
2114
|
const unsubscribe = GraphFocus_default.subscribe((state) => {
|
|
1897
2115
|
const focusAction = state.focusAction;
|
|
1898
2116
|
if (!focusAction || !canvas.current) return;
|
|
@@ -1916,12 +2134,12 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
1916
2134
|
});
|
|
1917
2135
|
return unsubscribe;
|
|
1918
2136
|
}, [nodes, edges, setSelectedNodeIds, setSelectedEdge]);
|
|
1919
|
-
|
|
2137
|
+
useEffect3(() => {
|
|
1920
2138
|
if (!canvas.current) return;
|
|
1921
2139
|
clearSelections();
|
|
1922
2140
|
clearSelectionStore();
|
|
1923
2141
|
}, [lastDataChangeTime, clearSelections, clearSelectionStore]);
|
|
1924
|
-
const fitToScreen =
|
|
2142
|
+
const fitToScreen = useCallback3(
|
|
1925
2143
|
(reason) => {
|
|
1926
2144
|
if (!isMountedRef.current) return;
|
|
1927
2145
|
if (nodes.length === 0) return;
|
|
@@ -1947,7 +2165,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
1947
2165
|
},
|
|
1948
2166
|
[nodes.length]
|
|
1949
2167
|
);
|
|
1950
|
-
|
|
2168
|
+
useEffect3(() => {
|
|
1951
2169
|
isMountedRef.current = true;
|
|
1952
2170
|
fitToScreen("mount");
|
|
1953
2171
|
const state = useGraphSelection.getState();
|
|
@@ -1967,7 +2185,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
1967
2185
|
isMountedRef.current = false;
|
|
1968
2186
|
};
|
|
1969
2187
|
}, [fitToScreen, setSelections, setSelectedEdge, useGraphSelection]);
|
|
1970
|
-
|
|
2188
|
+
useEffect3(() => {
|
|
1971
2189
|
if (isMountedRef.current) fitToScreen("counts-changed");
|
|
1972
2190
|
}, [nodes.length, edges.length, fitToScreen]);
|
|
1973
2191
|
function updateLayout(next) {
|
|
@@ -2009,14 +2227,14 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2009
2227
|
setSelectedEdge(null);
|
|
2010
2228
|
if (onCanvasClick) onCanvasClick(e);
|
|
2011
2229
|
}
|
|
2012
|
-
|
|
2230
|
+
useEffect3(() => {
|
|
2013
2231
|
if (!isMountedRef.current) return;
|
|
2014
2232
|
if (selections.length > 0 && selections.length <= 2) {
|
|
2015
2233
|
const selectedNodes = selections.map((id) => nodes.find((n) => n.id === id)).filter(Boolean);
|
|
2016
2234
|
setSelectedNodeIds(selectedNodes.map((n) => n.id));
|
|
2017
2235
|
}
|
|
2018
2236
|
}, [selections, nodes]);
|
|
2019
|
-
|
|
2237
|
+
useEffect3(() => {
|
|
2020
2238
|
if (!isMountedRef.current) return;
|
|
2021
2239
|
const currentSelectedIds = useGraphSelection.getState().selectedNodeIds;
|
|
2022
2240
|
if (!currentSelectedIds.length) return;
|
|
@@ -2035,7 +2253,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2035
2253
|
setSelectedEdge,
|
|
2036
2254
|
useGraphSelection
|
|
2037
2255
|
]);
|
|
2038
|
-
|
|
2256
|
+
useEffect3(() => {
|
|
2039
2257
|
if (!isMountedRef.current) return;
|
|
2040
2258
|
const currentSelectedIds = useGraphSelection.getState().selectedNodeIds;
|
|
2041
2259
|
if (!currentSelectedIds.length) return;
|
|
@@ -2047,7 +2265,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2047
2265
|
if (stillPresentIds.length < 2) setSelectedEdge(null);
|
|
2048
2266
|
}
|
|
2049
2267
|
}, [nodes, setSelectedNodeIds, setSelectedEdge, useGraphSelection]);
|
|
2050
|
-
|
|
2268
|
+
useEffect3(() => {
|
|
2051
2269
|
if (!fullscreen) return;
|
|
2052
2270
|
const prevOverflow = document.body.style.overflow;
|
|
2053
2271
|
document.body.style.overflow = "hidden";
|
|
@@ -2061,7 +2279,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2061
2279
|
window.removeEventListener("keydown", handleKey);
|
|
2062
2280
|
};
|
|
2063
2281
|
}, [fullscreen, fitToScreen]);
|
|
2064
|
-
|
|
2282
|
+
useEffect3(() => {
|
|
2065
2283
|
if (!fullscreen) return;
|
|
2066
2284
|
const id = setTimeout(() => {
|
|
2067
2285
|
if (canvas.current) {
|
|
@@ -2070,7 +2288,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2070
2288
|
}, 50);
|
|
2071
2289
|
return () => clearTimeout(id);
|
|
2072
2290
|
}, [fullscreen, fitToScreen]);
|
|
2073
|
-
|
|
2291
|
+
useEffect3(() => {
|
|
2074
2292
|
if (!isMountedRef.current) return;
|
|
2075
2293
|
const handleResize = () => {
|
|
2076
2294
|
if (isMountedRef.current && canvas.current) {
|
|
@@ -2082,8 +2300,8 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2082
2300
|
window.removeEventListener("resize", handleResize);
|
|
2083
2301
|
};
|
|
2084
2302
|
}, [fitToScreen]);
|
|
2085
|
-
const firstDataRef =
|
|
2086
|
-
|
|
2303
|
+
const firstDataRef = useRef2(true);
|
|
2304
|
+
useEffect3(() => {
|
|
2087
2305
|
if (!isMountedRef.current) return;
|
|
2088
2306
|
if (nodes.length > 0) {
|
|
2089
2307
|
if (firstDataRef.current) {
|
|
@@ -2096,18 +2314,18 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2096
2314
|
}, [nodes.length, fitToScreen]);
|
|
2097
2315
|
if (nodes.length === 0) {
|
|
2098
2316
|
devLog8("[ReGraph] empty state render");
|
|
2099
|
-
return /* @__PURE__ */
|
|
2100
|
-
/* @__PURE__ */
|
|
2101
|
-
/* @__PURE__ */
|
|
2317
|
+
return /* @__PURE__ */ jsxs6(Card2, { className: "relative w-full h-full items-center justify-center text-slate-500 gap-2 shadow-xs p-4", children: [
|
|
2318
|
+
/* @__PURE__ */ jsx7("div", { children: "No data to display." }),
|
|
2319
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2102
2320
|
"Please",
|
|
2103
2321
|
" ",
|
|
2104
|
-
/* @__PURE__ */
|
|
2322
|
+
/* @__PURE__ */ jsx7("span", { className: "font-semibold", children: "run a query, check filters, or add a node" }),
|
|
2105
2323
|
" ",
|
|
2106
2324
|
"to the workbench to populate the graph."
|
|
2107
2325
|
] })
|
|
2108
2326
|
] });
|
|
2109
2327
|
}
|
|
2110
|
-
return /* @__PURE__ */
|
|
2328
|
+
return /* @__PURE__ */ jsxs6(
|
|
2111
2329
|
"div",
|
|
2112
2330
|
{
|
|
2113
2331
|
className: cn(
|
|
@@ -2115,7 +2333,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2115
2333
|
fullscreen ? "fixed inset-0 z-50 w-screen h-screen p-0 m-0" : "relative w-full h-full"
|
|
2116
2334
|
),
|
|
2117
2335
|
children: [
|
|
2118
|
-
/* @__PURE__ */
|
|
2336
|
+
/* @__PURE__ */ jsx7(Card2, { className: "h-full shadow-xs p-4", children: /* @__PURE__ */ jsx7(
|
|
2119
2337
|
GraphCanvas,
|
|
2120
2338
|
{
|
|
2121
2339
|
ref: canvas,
|
|
@@ -2140,21 +2358,22 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2140
2358
|
elementId: data.id,
|
|
2141
2359
|
elementType
|
|
2142
2360
|
});
|
|
2143
|
-
return /* @__PURE__ */
|
|
2361
|
+
return /* @__PURE__ */ jsx7(
|
|
2144
2362
|
ReaContextMenu_default,
|
|
2145
2363
|
{
|
|
2146
2364
|
data,
|
|
2147
2365
|
onClose,
|
|
2148
2366
|
elementType,
|
|
2149
2367
|
useGraphExploration,
|
|
2150
|
-
onCopyNode
|
|
2368
|
+
onCopyNode,
|
|
2369
|
+
getNodeEdgeTypes
|
|
2151
2370
|
}
|
|
2152
2371
|
);
|
|
2153
2372
|
}
|
|
2154
2373
|
}
|
|
2155
2374
|
) }),
|
|
2156
|
-
hoveredNode && /* @__PURE__ */
|
|
2157
|
-
/* @__PURE__ */
|
|
2375
|
+
hoveredNode && /* @__PURE__ */ jsx7(NodeHoverInfo_default, { node: hoveredNode }),
|
|
2376
|
+
/* @__PURE__ */ jsx7(
|
|
2158
2377
|
LayoutAndCameraControls_default,
|
|
2159
2378
|
{
|
|
2160
2379
|
fitToScreen,
|
|
@@ -2166,7 +2385,7 @@ function ReaGraph({ onCopyNode, extraToolbarContent }) {
|
|
|
2166
2385
|
isFocusMode,
|
|
2167
2386
|
onResetView: () => {
|
|
2168
2387
|
useGraphExploration.getState().resetView();
|
|
2169
|
-
|
|
2388
|
+
toast2.success(
|
|
2170
2389
|
isFocusMode ? "Focus mode cleared" : "Hidden elements restored"
|
|
2171
2390
|
);
|
|
2172
2391
|
},
|
|
@@ -2181,12 +2400,12 @@ var ReaGraph_default = ReaGraph;
|
|
|
2181
2400
|
|
|
2182
2401
|
// src/components/cyto/CytoscapeGraph.tsx
|
|
2183
2402
|
import {
|
|
2184
|
-
useEffect as
|
|
2185
|
-
useRef as
|
|
2186
|
-
useState as
|
|
2187
|
-
useCallback as
|
|
2403
|
+
useEffect as useEffect4,
|
|
2404
|
+
useRef as useRef3,
|
|
2405
|
+
useState as useState6,
|
|
2406
|
+
useCallback as useCallback4
|
|
2188
2407
|
} from "react";
|
|
2189
|
-
import { devLog as devLog10, errorLog as
|
|
2408
|
+
import { devLog as devLog10, errorLog as errorLog3 } from "@petrarca/sonnet-core";
|
|
2190
2409
|
import cytoscape from "cytoscape";
|
|
2191
2410
|
import fcose from "cytoscape-fcose";
|
|
2192
2411
|
import cola from "cytoscape-cola";
|
|
@@ -2208,7 +2427,7 @@ import {
|
|
|
2208
2427
|
SimpleTooltip as SimpleTooltip3
|
|
2209
2428
|
} from "@petrarca/sonnet-ui";
|
|
2210
2429
|
import { LayoutPanelTop as LayoutPanelTop2, Monitor, Frame, Eye as Eye2, Minus } from "lucide-react";
|
|
2211
|
-
import { Fragment as Fragment2, jsx as
|
|
2430
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2212
2431
|
function ViewControls2({
|
|
2213
2432
|
zoomIn,
|
|
2214
2433
|
zoomOut,
|
|
@@ -2219,17 +2438,17 @@ function ViewControls2({
|
|
|
2219
2438
|
isFocusMode,
|
|
2220
2439
|
onResetView
|
|
2221
2440
|
}) {
|
|
2222
|
-
return /* @__PURE__ */
|
|
2223
|
-
/* @__PURE__ */
|
|
2224
|
-
/* @__PURE__ */
|
|
2225
|
-
/* @__PURE__ */
|
|
2226
|
-
/* @__PURE__ */
|
|
2227
|
-
(hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */
|
|
2441
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
2442
|
+
/* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Zoom In", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: zoomIn, children: /* @__PURE__ */ jsx8("span", { style: { fontWeight: 600 }, children: "+" }) }) }),
|
|
2443
|
+
/* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Zoom Out", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: zoomOut, children: /* @__PURE__ */ jsx8(Minus, {}) }) }),
|
|
2444
|
+
/* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Fullscreen", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: toggleFullscreen, children: !fullscreen ? /* @__PURE__ */ jsx8(Monitor, {}) : /* @__PURE__ */ jsx8(Frame, {}) }) }),
|
|
2445
|
+
/* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Fit to screen", side: "left", children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: fitToScreen, children: /* @__PURE__ */ jsx8(Frame, {}) }) }),
|
|
2446
|
+
(hasHiddenElements || isFocusMode) && onResetView && /* @__PURE__ */ jsx8(
|
|
2228
2447
|
SimpleTooltip3,
|
|
2229
2448
|
{
|
|
2230
2449
|
label: isFocusMode ? "Reset Focus" : "Show All Hidden",
|
|
2231
2450
|
side: "left",
|
|
2232
|
-
children: /* @__PURE__ */
|
|
2451
|
+
children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", onClick: onResetView, children: /* @__PURE__ */ jsx8(Eye2, {}) })
|
|
2233
2452
|
}
|
|
2234
2453
|
)
|
|
2235
2454
|
] });
|
|
@@ -2264,12 +2483,12 @@ var LAYOUT_GROUPS = [
|
|
|
2264
2483
|
}
|
|
2265
2484
|
];
|
|
2266
2485
|
function LayoutDropdown2({ currentLayout, updateLayout }) {
|
|
2267
|
-
return /* @__PURE__ */
|
|
2268
|
-
/* @__PURE__ */
|
|
2269
|
-
/* @__PURE__ */
|
|
2270
|
-
groupIdx > 0 && /* @__PURE__ */
|
|
2271
|
-
/* @__PURE__ */
|
|
2272
|
-
group.items.map((item) => /* @__PURE__ */
|
|
2486
|
+
return /* @__PURE__ */ jsxs7(DropdownMenu3, { children: [
|
|
2487
|
+
/* @__PURE__ */ jsx8(SimpleTooltip3, { label: "Layout", side: "left", children: /* @__PURE__ */ jsx8(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx8(Button3, { variant: "ghost", size: "icon", children: /* @__PURE__ */ jsx8(LayoutPanelTop2, {}) }) }) }),
|
|
2488
|
+
/* @__PURE__ */ jsx8(DropdownMenuContent3, { side: "left", align: "start", children: LAYOUT_GROUPS.map((group, groupIdx) => /* @__PURE__ */ jsxs7("div", { children: [
|
|
2489
|
+
groupIdx > 0 && /* @__PURE__ */ jsx8(DropdownMenuSeparator2, {}),
|
|
2490
|
+
/* @__PURE__ */ jsx8(DropdownMenuLabel2, { children: group.label }),
|
|
2491
|
+
group.items.map((item) => /* @__PURE__ */ jsx8(
|
|
2273
2492
|
DropdownMenuItem3,
|
|
2274
2493
|
{
|
|
2275
2494
|
onClick: () => updateLayout(item.layout),
|
|
@@ -2294,9 +2513,9 @@ function CytoscapeControls({
|
|
|
2294
2513
|
onResetView,
|
|
2295
2514
|
extraToolbarContent
|
|
2296
2515
|
}) {
|
|
2297
|
-
return /* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2299
|
-
/* @__PURE__ */
|
|
2516
|
+
return /* @__PURE__ */ jsxs7("div", { className: "absolute right-2 top-2 flex flex-col border bg-white rounded-md shadow-sm z-10", children: [
|
|
2517
|
+
/* @__PURE__ */ jsx8(RendererDropdown, {}),
|
|
2518
|
+
/* @__PURE__ */ jsx8(
|
|
2300
2519
|
ViewControls2,
|
|
2301
2520
|
{
|
|
2302
2521
|
zoomIn,
|
|
@@ -2310,7 +2529,7 @@ function CytoscapeControls({
|
|
|
2310
2529
|
}
|
|
2311
2530
|
),
|
|
2312
2531
|
extraToolbarContent,
|
|
2313
|
-
/* @__PURE__ */
|
|
2532
|
+
/* @__PURE__ */ jsx8(
|
|
2314
2533
|
LayoutDropdown2,
|
|
2315
2534
|
{
|
|
2316
2535
|
currentLayout,
|
|
@@ -2590,12 +2809,13 @@ function getLayoutPreset(options) {
|
|
|
2590
2809
|
}
|
|
2591
2810
|
|
|
2592
2811
|
// src/components/cyto/contextMenuConfig.ts
|
|
2593
|
-
import { devLog as devLog9, errorLog } from "@petrarca/sonnet-core";
|
|
2594
|
-
import { toast as
|
|
2812
|
+
import { devLog as devLog9, errorLog as errorLog2 } from "@petrarca/sonnet-core";
|
|
2813
|
+
import { toast as toast3 } from "sonner";
|
|
2595
2814
|
var createNodeContextMenu = ({
|
|
2596
2815
|
cy,
|
|
2597
2816
|
useGraphExploration,
|
|
2598
|
-
onCopyNode
|
|
2817
|
+
onCopyNode,
|
|
2818
|
+
onExpandRequest
|
|
2599
2819
|
}) => {
|
|
2600
2820
|
devLog9("[CxtMenu] Initializing node context menu");
|
|
2601
2821
|
return cy.cxtmenu({
|
|
@@ -2617,7 +2837,7 @@ var createNodeContextMenu = ({
|
|
|
2617
2837
|
if (visualGraph) {
|
|
2618
2838
|
onCopyNode?.(visualGraph);
|
|
2619
2839
|
} else {
|
|
2620
|
-
|
|
2840
|
+
errorLog2("[CxtMenu] visualGraph not found in element data");
|
|
2621
2841
|
}
|
|
2622
2842
|
}
|
|
2623
2843
|
},
|
|
@@ -2647,21 +2867,23 @@ var createNodeContextMenu = ({
|
|
|
2647
2867
|
},
|
|
2648
2868
|
{
|
|
2649
2869
|
content: "Expand",
|
|
2650
|
-
select:
|
|
2870
|
+
select: (ele) => {
|
|
2651
2871
|
const nodeId = ele.id();
|
|
2652
|
-
devLog9("[CxtMenu]
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2872
|
+
devLog9("[CxtMenu] Expand request for node:", nodeId);
|
|
2873
|
+
if (onExpandRequest) {
|
|
2874
|
+
const pos = ele.renderedPosition();
|
|
2875
|
+
const label = ele.data("label") || nodeId;
|
|
2876
|
+
onExpandRequest({ nodeId, nodeLabel: label, x: pos.x, y: pos.y });
|
|
2877
|
+
} else {
|
|
2878
|
+
useGraphExploration.getState().expandNode(nodeId).catch((error) => {
|
|
2879
|
+
errorLog2("[CxtMenu] Expansion failed:", error);
|
|
2880
|
+
toast3.error("Failed to expand node");
|
|
2881
|
+
});
|
|
2659
2882
|
}
|
|
2660
2883
|
}
|
|
2661
2884
|
}
|
|
2662
2885
|
],
|
|
2663
2886
|
menuRadius: 60,
|
|
2664
|
-
// Increased for more items (default is 100)
|
|
2665
2887
|
fillColor: "rgba(0, 0, 0, 0.75)",
|
|
2666
2888
|
activeFillColor: "rgba(59, 130, 246, 0.75)",
|
|
2667
2889
|
// blue-500
|
|
@@ -2746,8 +2968,49 @@ var createEdgeContextMenu = ({
|
|
|
2746
2968
|
};
|
|
2747
2969
|
|
|
2748
2970
|
// src/components/cyto/CytoscapeGraph.tsx
|
|
2749
|
-
import { toast as
|
|
2750
|
-
import { jsx as
|
|
2971
|
+
import { toast as toast4 } from "sonner";
|
|
2972
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2973
|
+
function CytoscapeExpandOverlay({
|
|
2974
|
+
expandRequest,
|
|
2975
|
+
containerRef,
|
|
2976
|
+
useGraphExploration,
|
|
2977
|
+
getNodeEdgeTypes,
|
|
2978
|
+
onClose
|
|
2979
|
+
}) {
|
|
2980
|
+
return /* @__PURE__ */ jsx9(
|
|
2981
|
+
"div",
|
|
2982
|
+
{
|
|
2983
|
+
className: "absolute z-50",
|
|
2984
|
+
style: {
|
|
2985
|
+
left: Math.min(
|
|
2986
|
+
Math.max(expandRequest.x, 120),
|
|
2987
|
+
(containerRef.current?.clientWidth ?? 800) - 120
|
|
2988
|
+
),
|
|
2989
|
+
top: Math.min(
|
|
2990
|
+
Math.max(expandRequest.y, 10),
|
|
2991
|
+
(containerRef.current?.clientHeight ?? 600) - 260
|
|
2992
|
+
),
|
|
2993
|
+
transform: "translate(-50%, 12px)"
|
|
2994
|
+
},
|
|
2995
|
+
children: /* @__PURE__ */ jsx9(
|
|
2996
|
+
ExpandOptionsPanel_default,
|
|
2997
|
+
{
|
|
2998
|
+
nodeLabel: expandRequest.nodeLabel,
|
|
2999
|
+
onExpand: (options) => {
|
|
3000
|
+
const { nodeId } = expandRequest;
|
|
3001
|
+
onClose();
|
|
3002
|
+
useGraphExploration.getState().expandNode(nodeId, options).catch((err) => {
|
|
3003
|
+
errorLog3("[CxtMenu] Expansion failed:", err);
|
|
3004
|
+
toast4.error("Failed to expand node");
|
|
3005
|
+
});
|
|
3006
|
+
},
|
|
3007
|
+
onBack: onClose,
|
|
3008
|
+
getEdgeTypes: getNodeEdgeTypes ? () => getNodeEdgeTypes(Number(expandRequest.nodeId)) : void 0
|
|
3009
|
+
}
|
|
3010
|
+
)
|
|
3011
|
+
}
|
|
3012
|
+
);
|
|
3013
|
+
}
|
|
2751
3014
|
try {
|
|
2752
3015
|
cytoscape.use(fcose);
|
|
2753
3016
|
cytoscape.use(cola);
|
|
@@ -2825,12 +3088,12 @@ function applyLayout({
|
|
|
2825
3088
|
try {
|
|
2826
3089
|
cy.layout(layoutOpts).run();
|
|
2827
3090
|
} catch (e) {
|
|
2828
|
-
|
|
3091
|
+
errorLog3("Layout error, falling back to dagre:", e);
|
|
2829
3092
|
if (chosen === "klay") {
|
|
2830
3093
|
try {
|
|
2831
3094
|
cy.layout(getPreset("dagre")).run();
|
|
2832
3095
|
} catch (inner) {
|
|
2833
|
-
|
|
3096
|
+
errorLog3("Fallback layout also failed, using grid:", inner);
|
|
2834
3097
|
cy.layout(getPreset("grid")).run();
|
|
2835
3098
|
}
|
|
2836
3099
|
} else {
|
|
@@ -3037,8 +3300,8 @@ function computeGraphViewState({
|
|
|
3037
3300
|
};
|
|
3038
3301
|
}
|
|
3039
3302
|
function useFullscreen(fitToScreen, cyRef) {
|
|
3040
|
-
const [fullscreen, setFullscreen] =
|
|
3041
|
-
|
|
3303
|
+
const [fullscreen, setFullscreen] = useState6(false);
|
|
3304
|
+
useEffect4(() => {
|
|
3042
3305
|
if (fullscreen) {
|
|
3043
3306
|
const prevOverflow = document.body.style.overflow;
|
|
3044
3307
|
document.body.style.overflow = "hidden";
|
|
@@ -3053,7 +3316,7 @@ function useFullscreen(fitToScreen, cyRef) {
|
|
|
3053
3316
|
};
|
|
3054
3317
|
}
|
|
3055
3318
|
}, [fullscreen, fitToScreen]);
|
|
3056
|
-
|
|
3319
|
+
useEffect4(() => {
|
|
3057
3320
|
if (!fullscreen) return;
|
|
3058
3321
|
const id = setTimeout(() => {
|
|
3059
3322
|
if (cyRef.current) {
|
|
@@ -3066,7 +3329,7 @@ function useFullscreen(fitToScreen, cyRef) {
|
|
|
3066
3329
|
return {
|
|
3067
3330
|
fullscreen,
|
|
3068
3331
|
setFullscreen,
|
|
3069
|
-
toggleFullscreen:
|
|
3332
|
+
toggleFullscreen: useCallback4(() => setFullscreen((f) => !f), [])
|
|
3070
3333
|
};
|
|
3071
3334
|
}
|
|
3072
3335
|
function setupCytoscapeEventHandlers({
|
|
@@ -3078,7 +3341,8 @@ function setupCytoscapeEventHandlers({
|
|
|
3078
3341
|
setSelectedEdgeStore,
|
|
3079
3342
|
clearSelectionStore,
|
|
3080
3343
|
useGraphExploration,
|
|
3081
|
-
onCopyNode
|
|
3344
|
+
onCopyNode,
|
|
3345
|
+
onExpandRequest
|
|
3082
3346
|
}) {
|
|
3083
3347
|
const updateSelectedNodeIds = (nodeIds) => {
|
|
3084
3348
|
setSelectedNodeIdsStore(nodeIds);
|
|
@@ -3166,22 +3430,31 @@ function setupCytoscapeEventHandlers({
|
|
|
3166
3430
|
cy.on("tap", (evt) => {
|
|
3167
3431
|
if (evt.target === cy) clearAll();
|
|
3168
3432
|
});
|
|
3169
|
-
createNodeContextMenu({
|
|
3433
|
+
createNodeContextMenu({
|
|
3434
|
+
cy,
|
|
3435
|
+
useGraphExploration,
|
|
3436
|
+
onCopyNode,
|
|
3437
|
+
onExpandRequest
|
|
3438
|
+
});
|
|
3170
3439
|
createEdgeContextMenu({ cy, useGraphExploration });
|
|
3171
3440
|
}
|
|
3172
3441
|
function CytoscapeGraph({
|
|
3173
3442
|
layout: initialLayout = "auto",
|
|
3174
3443
|
fitPadding = 30,
|
|
3175
3444
|
onCopyNode,
|
|
3176
|
-
extraToolbarContent
|
|
3445
|
+
extraToolbarContent,
|
|
3446
|
+
getNodeEdgeTypes
|
|
3177
3447
|
}) {
|
|
3178
|
-
const containerRef =
|
|
3179
|
-
const cyRef =
|
|
3180
|
-
const isInitializedRef =
|
|
3181
|
-
const [currentLayout, setCurrentLayout] =
|
|
3182
|
-
const
|
|
3183
|
-
|
|
3184
|
-
|
|
3448
|
+
const containerRef = useRef3(null);
|
|
3449
|
+
const cyRef = useRef3(null);
|
|
3450
|
+
const isInitializedRef = useRef3(false);
|
|
3451
|
+
const [currentLayout, setCurrentLayout] = useState6(initialLayout);
|
|
3452
|
+
const [expandRequest, setExpandRequest] = useState6(
|
|
3453
|
+
null
|
|
3454
|
+
);
|
|
3455
|
+
const nodesRef = useRef3([]);
|
|
3456
|
+
const edgesRef = useRef3([]);
|
|
3457
|
+
const pendingFocusActionRef = useRef3(null);
|
|
3185
3458
|
const cytoData = useCytoscapeData();
|
|
3186
3459
|
const live = useLiveGraphData();
|
|
3187
3460
|
const nodes = live.nodes;
|
|
@@ -3201,13 +3474,13 @@ function CytoscapeGraph({
|
|
|
3201
3474
|
setSelectedEdge: setSelectedEdgeStore,
|
|
3202
3475
|
clear: clearSelectionStore
|
|
3203
3476
|
} = useSelection3.getState();
|
|
3204
|
-
const selectedNodeIdsRef =
|
|
3477
|
+
const selectedNodeIdsRef = useRef3(
|
|
3205
3478
|
useSelection3.getState().selectedNodeIds
|
|
3206
3479
|
);
|
|
3207
|
-
const selectedEdgeRef =
|
|
3480
|
+
const selectedEdgeRef = useRef3(
|
|
3208
3481
|
useSelection3.getState().selectedEdge
|
|
3209
3482
|
);
|
|
3210
|
-
|
|
3483
|
+
useEffect4(() => {
|
|
3211
3484
|
const state = useSelection3.getState();
|
|
3212
3485
|
selectedNodeIdsRef.current = state.selectedNodeIds;
|
|
3213
3486
|
selectedEdgeRef.current = state.selectedEdge;
|
|
@@ -3227,13 +3500,13 @@ function CytoscapeGraph({
|
|
|
3227
3500
|
});
|
|
3228
3501
|
}
|
|
3229
3502
|
}, [useSelection3]);
|
|
3230
|
-
|
|
3503
|
+
useEffect4(() => {
|
|
3231
3504
|
nodesRef.current = nodes;
|
|
3232
3505
|
}, [nodes]);
|
|
3233
|
-
|
|
3506
|
+
useEffect4(() => {
|
|
3234
3507
|
edgesRef.current = edges;
|
|
3235
3508
|
}, [edges]);
|
|
3236
|
-
|
|
3509
|
+
useEffect4(() => {
|
|
3237
3510
|
if (!containerRef.current || cyRef.current) return;
|
|
3238
3511
|
isInitializedRef.current = true;
|
|
3239
3512
|
cyRef.current = cytoscape({
|
|
@@ -3384,7 +3657,8 @@ function CytoscapeGraph({
|
|
|
3384
3657
|
setSelectedEdgeStore,
|
|
3385
3658
|
clearSelectionStore,
|
|
3386
3659
|
useGraphExploration,
|
|
3387
|
-
onCopyNode
|
|
3660
|
+
onCopyNode,
|
|
3661
|
+
onExpandRequest: setExpandRequest
|
|
3388
3662
|
});
|
|
3389
3663
|
}
|
|
3390
3664
|
return () => {
|
|
@@ -3392,10 +3666,10 @@ function CytoscapeGraph({
|
|
|
3392
3666
|
cyRef.current = null;
|
|
3393
3667
|
};
|
|
3394
3668
|
}, []);
|
|
3395
|
-
const elementIdsRef =
|
|
3396
|
-
const lastLayoutRef =
|
|
3397
|
-
const lastFitPaddingRef =
|
|
3398
|
-
|
|
3669
|
+
const elementIdsRef = useRef3("");
|
|
3670
|
+
const lastLayoutRef = useRef3("");
|
|
3671
|
+
const lastFitPaddingRef = useRef3(fitPadding);
|
|
3672
|
+
useEffect4(() => {
|
|
3399
3673
|
const cy = cyRef.current;
|
|
3400
3674
|
if (!cy || !isInitializedRef.current) return;
|
|
3401
3675
|
const currentIds = nodes.map((n) => n.id_).sort().join(",") + "|" + edges.map((e) => e.id_).sort().join(",");
|
|
@@ -3468,12 +3742,12 @@ function CytoscapeGraph({
|
|
|
3468
3742
|
});
|
|
3469
3743
|
}
|
|
3470
3744
|
}, [nodes, edges, currentLayout, fitPadding]);
|
|
3471
|
-
|
|
3745
|
+
useEffect4(() => {
|
|
3472
3746
|
if (!cyRef.current) return;
|
|
3473
3747
|
cyRef.current.elements().unselect();
|
|
3474
3748
|
clearSelectionStore();
|
|
3475
3749
|
}, [lastDataChangeTime, clearSelectionStore]);
|
|
3476
|
-
|
|
3750
|
+
useEffect4(() => {
|
|
3477
3751
|
const unsubscribe = GraphFocus_default.subscribe((state) => {
|
|
3478
3752
|
handleFocusStateChange({
|
|
3479
3753
|
focusAction: state.focusAction,
|
|
@@ -3492,15 +3766,15 @@ function CytoscapeGraph({
|
|
|
3492
3766
|
const updateLayout = (newLayout) => {
|
|
3493
3767
|
setCurrentLayout(newLayout);
|
|
3494
3768
|
};
|
|
3495
|
-
const fitToScreen =
|
|
3769
|
+
const fitToScreen = useCallback4(() => {
|
|
3496
3770
|
if (cyRef.current) {
|
|
3497
3771
|
cyRef.current.fit(void 0, fitPadding);
|
|
3498
3772
|
}
|
|
3499
3773
|
}, [fitPadding]);
|
|
3500
|
-
|
|
3774
|
+
useEffect4(() => {
|
|
3501
3775
|
fitToScreen();
|
|
3502
3776
|
}, [nodes.length, fitToScreen]);
|
|
3503
|
-
|
|
3777
|
+
useEffect4(() => {
|
|
3504
3778
|
if (!isInitializedRef.current) return;
|
|
3505
3779
|
const handleResize = () => cyRef.current?.resize();
|
|
3506
3780
|
window.addEventListener("resize", handleResize);
|
|
@@ -3519,18 +3793,28 @@ function CytoscapeGraph({
|
|
|
3519
3793
|
isFocusMode,
|
|
3520
3794
|
fullscreen
|
|
3521
3795
|
});
|
|
3522
|
-
return /* @__PURE__ */
|
|
3523
|
-
/* @__PURE__ */
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3796
|
+
return /* @__PURE__ */ jsx9("div", { className: outerClass, children: /* @__PURE__ */ jsxs8("div", { className: innerClass, children: [
|
|
3797
|
+
/* @__PURE__ */ jsx9("div", { ref: containerRef, className: "absolute inset-0" }),
|
|
3798
|
+
expandRequest && /* @__PURE__ */ jsx9(
|
|
3799
|
+
CytoscapeExpandOverlay,
|
|
3800
|
+
{
|
|
3801
|
+
expandRequest,
|
|
3802
|
+
containerRef,
|
|
3803
|
+
useGraphExploration,
|
|
3804
|
+
getNodeEdgeTypes,
|
|
3805
|
+
onClose: () => setExpandRequest(null)
|
|
3806
|
+
}
|
|
3807
|
+
),
|
|
3808
|
+
showEmptyMessage && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center text-xs text-gray-500 pointer-events-none select-none", children: "No graph data to visualize (run a query or check the filters)" }),
|
|
3809
|
+
showFilteredMessage && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center text-xs text-gray-500 pointer-events-none select-none", children: 'All elements are hidden or filtered (click "Reset Focus" to restore)' }),
|
|
3810
|
+
showControls && /* @__PURE__ */ jsx9(
|
|
3527
3811
|
CytoscapeControls_default,
|
|
3528
3812
|
{
|
|
3529
3813
|
updateLayout,
|
|
3530
3814
|
fitToScreen,
|
|
3531
3815
|
onResetView: () => {
|
|
3532
3816
|
resetView();
|
|
3533
|
-
|
|
3817
|
+
toast4.success(
|
|
3534
3818
|
isFocusMode ? "Focus mode cleared" : "Hidden elements restored"
|
|
3535
3819
|
);
|
|
3536
3820
|
},
|
|
@@ -3571,26 +3855,29 @@ function CytoscapeGraph({
|
|
|
3571
3855
|
var CytoscapeGraph_default = CytoscapeGraph;
|
|
3572
3856
|
|
|
3573
3857
|
// src/components/GraphVisualizer.tsx
|
|
3574
|
-
import { jsx as
|
|
3858
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
3575
3859
|
function GraphVisualizer({
|
|
3576
3860
|
onCopyNode,
|
|
3577
|
-
extraToolbarContent
|
|
3861
|
+
extraToolbarContent,
|
|
3862
|
+
getNodeEdgeTypes
|
|
3578
3863
|
}) {
|
|
3579
3864
|
const { renderer } = useGraphRenderer();
|
|
3580
|
-
return /* @__PURE__ */
|
|
3581
|
-
renderer === "reagraph" && /* @__PURE__ */
|
|
3865
|
+
return /* @__PURE__ */ jsx10("div", { className: "relative w-full h-full flex flex-col overflow-hidden", children: /* @__PURE__ */ jsxs9("div", { className: "flex-1 h-full", children: [
|
|
3866
|
+
renderer === "reagraph" && /* @__PURE__ */ jsx10(
|
|
3582
3867
|
ReaGraph_default,
|
|
3583
3868
|
{
|
|
3584
3869
|
onCopyNode,
|
|
3585
|
-
extraToolbarContent
|
|
3870
|
+
extraToolbarContent,
|
|
3871
|
+
getNodeEdgeTypes
|
|
3586
3872
|
},
|
|
3587
3873
|
"reagraph"
|
|
3588
3874
|
),
|
|
3589
|
-
renderer === "cytoscape" && /* @__PURE__ */
|
|
3875
|
+
renderer === "cytoscape" && /* @__PURE__ */ jsx10(
|
|
3590
3876
|
CytoscapeGraph_default,
|
|
3591
3877
|
{
|
|
3592
3878
|
onCopyNode,
|
|
3593
|
-
extraToolbarContent
|
|
3879
|
+
extraToolbarContent,
|
|
3880
|
+
getNodeEdgeTypes
|
|
3594
3881
|
},
|
|
3595
3882
|
"cytoscape"
|
|
3596
3883
|
)
|
|
@@ -3602,18 +3889,19 @@ var GraphVisualizer_default = GraphVisualizer;
|
|
|
3602
3889
|
import { Card as Card5 } from "@petrarca/sonnet-ui";
|
|
3603
3890
|
|
|
3604
3891
|
// src/components/ActionPanel/NodeInfoCard.tsx
|
|
3605
|
-
import { useState as
|
|
3892
|
+
import { useState as useState7 } from "react";
|
|
3606
3893
|
import {
|
|
3607
3894
|
Card as Card3,
|
|
3608
3895
|
Collapsible,
|
|
3609
3896
|
CollapsibleContent,
|
|
3610
|
-
CollapsibleTrigger
|
|
3897
|
+
CollapsibleTrigger,
|
|
3898
|
+
PropertyList
|
|
3611
3899
|
} from "@petrarca/sonnet-ui";
|
|
3612
3900
|
import { copyTextToClipboard } from "@petrarca/sonnet-core";
|
|
3613
|
-
import { jsx as
|
|
3901
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3614
3902
|
function NodeInfoCard({ node }) {
|
|
3615
|
-
const [showProperties, setShowProperties] =
|
|
3616
|
-
return /* @__PURE__ */
|
|
3903
|
+
const [showProperties, setShowProperties] = useState7(false);
|
|
3904
|
+
return /* @__PURE__ */ jsxs10(
|
|
3617
3905
|
Card3,
|
|
3618
3906
|
{
|
|
3619
3907
|
className: "w-full rounded-lg overflow-hidden bg-muted/30 border shadow-none p-3",
|
|
@@ -3621,9 +3909,9 @@ function NodeInfoCard({ node }) {
|
|
|
3621
3909
|
borderLeft: `4px solid ${node.visual.color}`
|
|
3622
3910
|
},
|
|
3623
3911
|
children: [
|
|
3624
|
-
/* @__PURE__ */
|
|
3625
|
-
/* @__PURE__ */
|
|
3626
|
-
/* @__PURE__ */
|
|
3912
|
+
/* @__PURE__ */ jsx11("p", { className: "text-sm leading-tight font-medium mb-0", children: node.label_ }),
|
|
3913
|
+
/* @__PURE__ */ jsx11("p", { className: "text-sm leading-tight mb-1", children: node.visual.displayName }),
|
|
3914
|
+
/* @__PURE__ */ jsx11(
|
|
3627
3915
|
"p",
|
|
3628
3916
|
{
|
|
3629
3917
|
className: "text-xs text-muted-foreground cursor-pointer select-all mb-2",
|
|
@@ -3632,23 +3920,9 @@ function NodeInfoCard({ node }) {
|
|
|
3632
3920
|
children: node.id_
|
|
3633
3921
|
}
|
|
3634
3922
|
),
|
|
3635
|
-
/* @__PURE__ */
|
|
3636
|
-
/* @__PURE__ */
|
|
3637
|
-
/* @__PURE__ */
|
|
3638
|
-
/* @__PURE__ */ jsx10("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1", children: "Properties" }),
|
|
3639
|
-
Object.entries(node.properties_ || {}).map(([key, value]) => /* @__PURE__ */ jsxs9("li", { className: "mb-2 leading-tight", children: [
|
|
3640
|
-
/* @__PURE__ */ jsx10("p", { className: "text-xs text-muted-foreground uppercase tracking-wide", children: key }),
|
|
3641
|
-
/* @__PURE__ */ jsx10(
|
|
3642
|
-
"p",
|
|
3643
|
-
{
|
|
3644
|
-
className: "text-sm truncate max-w-[190px] cursor-pointer",
|
|
3645
|
-
onClick: () => copyTextToClipboard(String(value)),
|
|
3646
|
-
title: "Click to copy value",
|
|
3647
|
-
children: String(value)
|
|
3648
|
-
}
|
|
3649
|
-
)
|
|
3650
|
-
] }, key))
|
|
3651
|
-
] }) })
|
|
3923
|
+
/* @__PURE__ */ jsxs10(Collapsible, { open: showProperties, onOpenChange: setShowProperties, children: [
|
|
3924
|
+
/* @__PURE__ */ jsx11(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsx11("button", { className: "text-xs cursor-pointer", children: showProperties ? "Hide" : "Show more" }) }),
|
|
3925
|
+
/* @__PURE__ */ jsx11(CollapsibleContent, { children: /* @__PURE__ */ jsx11("div", { className: "mt-2", children: /* @__PURE__ */ jsx11(PropertyList, { properties: node.properties_ || {}, showHeader: true }) }) })
|
|
3652
3926
|
] })
|
|
3653
3927
|
]
|
|
3654
3928
|
}
|
|
@@ -3657,19 +3931,20 @@ function NodeInfoCard({ node }) {
|
|
|
3657
3931
|
var NodeInfoCard_default = NodeInfoCard;
|
|
3658
3932
|
|
|
3659
3933
|
// src/components/ActionPanel/EdgeInfoCard.tsx
|
|
3660
|
-
import { useState as
|
|
3934
|
+
import { useState as useState8 } from "react";
|
|
3661
3935
|
import {
|
|
3662
3936
|
Card as Card4,
|
|
3663
3937
|
Collapsible as Collapsible2,
|
|
3664
3938
|
CollapsibleContent as CollapsibleContent2,
|
|
3665
|
-
CollapsibleTrigger as CollapsibleTrigger2
|
|
3939
|
+
CollapsibleTrigger as CollapsibleTrigger2,
|
|
3940
|
+
PropertyList as PropertyList2
|
|
3666
3941
|
} from "@petrarca/sonnet-ui";
|
|
3667
3942
|
import { ArrowDown } from "lucide-react";
|
|
3668
3943
|
import { copyTextToClipboard as copyTextToClipboard2 } from "@petrarca/sonnet-core";
|
|
3669
|
-
import { jsx as
|
|
3944
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3670
3945
|
function EdgeInfoCard({ edge }) {
|
|
3671
|
-
const [showProperties, setShowProperties] =
|
|
3672
|
-
return /* @__PURE__ */
|
|
3946
|
+
const [showProperties, setShowProperties] = useState8(false);
|
|
3947
|
+
return /* @__PURE__ */ jsxs11(
|
|
3673
3948
|
Card4,
|
|
3674
3949
|
{
|
|
3675
3950
|
className: "w-full rounded-lg overflow-hidden bg-muted/30 border shadow-none p-3",
|
|
@@ -3677,11 +3952,11 @@ function EdgeInfoCard({ edge }) {
|
|
|
3677
3952
|
borderLeft: `4px solid ${edge.visual.color}`
|
|
3678
3953
|
},
|
|
3679
3954
|
children: [
|
|
3680
|
-
/* @__PURE__ */
|
|
3681
|
-
/* @__PURE__ */
|
|
3682
|
-
/* @__PURE__ */
|
|
3955
|
+
/* @__PURE__ */ jsxs11("p", { className: "text-sm leading-tight mb-1", children: [
|
|
3956
|
+
/* @__PURE__ */ jsx12(ArrowDown, { className: "inline-block mr-1 opacity-70 align-middle h-3.5 w-3.5" }),
|
|
3957
|
+
/* @__PURE__ */ jsx12("strong", { className: "font-medium align-middle", children: edge.label_ })
|
|
3683
3958
|
] }),
|
|
3684
|
-
edge.id_ && /* @__PURE__ */
|
|
3959
|
+
edge.id_ && /* @__PURE__ */ jsx12(
|
|
3685
3960
|
"p",
|
|
3686
3961
|
{
|
|
3687
3962
|
className: "text-xs text-muted-foreground select-all cursor-pointer mt-1 mb-2",
|
|
@@ -3690,23 +3965,9 @@ function EdgeInfoCard({ edge }) {
|
|
|
3690
3965
|
children: edge.id_
|
|
3691
3966
|
}
|
|
3692
3967
|
),
|
|
3693
|
-
edge.properties_ && Object.keys(edge.properties_).length > 0 && /* @__PURE__ */
|
|
3694
|
-
/* @__PURE__ */
|
|
3695
|
-
/* @__PURE__ */
|
|
3696
|
-
/* @__PURE__ */ jsx11("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide mb-1", children: "Properties" }),
|
|
3697
|
-
Object.entries(edge.properties_ || {}).map(([key, value]) => /* @__PURE__ */ jsxs10("li", { className: "mb-2 leading-tight", children: [
|
|
3698
|
-
/* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground uppercase tracking-wide", children: key }),
|
|
3699
|
-
/* @__PURE__ */ jsx11(
|
|
3700
|
-
"p",
|
|
3701
|
-
{
|
|
3702
|
-
className: "text-sm truncate max-w-[190px] cursor-pointer",
|
|
3703
|
-
onClick: () => copyTextToClipboard2(String(value)),
|
|
3704
|
-
title: "Click to copy value",
|
|
3705
|
-
children: String(value)
|
|
3706
|
-
}
|
|
3707
|
-
)
|
|
3708
|
-
] }, key))
|
|
3709
|
-
] }) })
|
|
3968
|
+
edge.properties_ && Object.keys(edge.properties_).length > 0 && /* @__PURE__ */ jsxs11(Collapsible2, { open: showProperties, onOpenChange: setShowProperties, children: [
|
|
3969
|
+
/* @__PURE__ */ jsx12(CollapsibleTrigger2, { asChild: true, children: /* @__PURE__ */ jsx12("button", { className: "text-xs cursor-pointer", children: showProperties ? "Hide" : "Show more" }) }),
|
|
3970
|
+
/* @__PURE__ */ jsx12(CollapsibleContent2, { children: /* @__PURE__ */ jsx12("div", { className: "mt-2", children: /* @__PURE__ */ jsx12(PropertyList2, { properties: edge.properties_ || {}, showHeader: true }) }) })
|
|
3710
3971
|
] })
|
|
3711
3972
|
]
|
|
3712
3973
|
}
|
|
@@ -3718,7 +3979,7 @@ var EdgeInfoCard_default = EdgeInfoCard;
|
|
|
3718
3979
|
import { Badge } from "@petrarca/sonnet-ui";
|
|
3719
3980
|
import { formatNumber, devLog as devLog11 } from "@petrarca/sonnet-core";
|
|
3720
3981
|
import { useMemo as useMemo4 } from "react";
|
|
3721
|
-
import { Fragment as Fragment3, jsx as
|
|
3982
|
+
import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3722
3983
|
function computeEdgeToNodeLabels(nodesById, edgesById) {
|
|
3723
3984
|
const mapping = {};
|
|
3724
3985
|
for (const edge of Object.values(edgesById)) {
|
|
@@ -3751,7 +4012,7 @@ function AllToggleBadge({
|
|
|
3751
4012
|
badgeKey
|
|
3752
4013
|
}) {
|
|
3753
4014
|
const bgColor = allDisabled ? colorPalette["faded"] : colorPalette["neutral"];
|
|
3754
|
-
return /* @__PURE__ */
|
|
4015
|
+
return /* @__PURE__ */ jsxs12(
|
|
3755
4016
|
Badge,
|
|
3756
4017
|
{
|
|
3757
4018
|
className: "cursor-pointer text-xs px-2.5 py-0.5",
|
|
@@ -3786,7 +4047,7 @@ function NodeLabelBadges({
|
|
|
3786
4047
|
colorPalette,
|
|
3787
4048
|
label
|
|
3788
4049
|
);
|
|
3789
|
-
return /* @__PURE__ */
|
|
4050
|
+
return /* @__PURE__ */ jsxs12(
|
|
3790
4051
|
Badge,
|
|
3791
4052
|
{
|
|
3792
4053
|
className: "cursor-pointer text-xs px-2.5 py-0.5",
|
|
@@ -3824,7 +4085,7 @@ function EdgeLabelBadges({
|
|
|
3824
4085
|
colorPalette,
|
|
3825
4086
|
"neutral"
|
|
3826
4087
|
);
|
|
3827
|
-
return /* @__PURE__ */
|
|
4088
|
+
return /* @__PURE__ */ jsxs12(
|
|
3828
4089
|
Badge,
|
|
3829
4090
|
{
|
|
3830
4091
|
className: "cursor-pointer text-xs px-2.5 py-0.5",
|
|
@@ -3901,12 +4162,12 @@ function GraphOverview({
|
|
|
3901
4162
|
toggleAllEdgeLabelsDisabled();
|
|
3902
4163
|
}
|
|
3903
4164
|
};
|
|
3904
|
-
return /* @__PURE__ */
|
|
3905
|
-
/* @__PURE__ */
|
|
3906
|
-
allLabels.length > 0 && /* @__PURE__ */
|
|
3907
|
-
/* @__PURE__ */
|
|
3908
|
-
/* @__PURE__ */
|
|
3909
|
-
/* @__PURE__ */
|
|
4165
|
+
return /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
4166
|
+
/* @__PURE__ */ jsx13("p", { className: "mt-2 text-sm font-semibold", children: "Overview" }),
|
|
4167
|
+
allLabels.length > 0 && /* @__PURE__ */ jsxs12("div", { children: [
|
|
4168
|
+
/* @__PURE__ */ jsx13("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2", children: "Node Labels" }),
|
|
4169
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-start gap-2", children: [
|
|
4170
|
+
/* @__PURE__ */ jsx13(
|
|
3910
4171
|
AllToggleBadge,
|
|
3911
4172
|
{
|
|
3912
4173
|
allDisabled: allLabelsDisabled,
|
|
@@ -3916,7 +4177,7 @@ function GraphOverview({
|
|
|
3916
4177
|
badgeKey: "all"
|
|
3917
4178
|
}
|
|
3918
4179
|
),
|
|
3919
|
-
/* @__PURE__ */
|
|
4180
|
+
/* @__PURE__ */ jsx13(
|
|
3920
4181
|
NodeLabelBadges,
|
|
3921
4182
|
{
|
|
3922
4183
|
labels: allLabels,
|
|
@@ -3929,10 +4190,10 @@ function GraphOverview({
|
|
|
3929
4190
|
)
|
|
3930
4191
|
] })
|
|
3931
4192
|
] }),
|
|
3932
|
-
allEdgeLabels.length > 0 && /* @__PURE__ */
|
|
3933
|
-
/* @__PURE__ */
|
|
3934
|
-
/* @__PURE__ */
|
|
3935
|
-
/* @__PURE__ */
|
|
4193
|
+
allEdgeLabels.length > 0 && /* @__PURE__ */ jsxs12("div", { children: [
|
|
4194
|
+
/* @__PURE__ */ jsx13("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2", children: "Edge Types" }),
|
|
4195
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-start gap-2", children: [
|
|
4196
|
+
/* @__PURE__ */ jsx13(
|
|
3936
4197
|
AllToggleBadge,
|
|
3937
4198
|
{
|
|
3938
4199
|
allDisabled: allEdgeLabelsDisabled,
|
|
@@ -3942,7 +4203,7 @@ function GraphOverview({
|
|
|
3942
4203
|
badgeKey: "edge-all"
|
|
3943
4204
|
}
|
|
3944
4205
|
),
|
|
3945
|
-
/* @__PURE__ */
|
|
4206
|
+
/* @__PURE__ */ jsx13(
|
|
3946
4207
|
EdgeLabelBadges,
|
|
3947
4208
|
{
|
|
3948
4209
|
labels: allEdgeLabels,
|
|
@@ -3968,16 +4229,16 @@ function Overview() {
|
|
|
3968
4229
|
);
|
|
3969
4230
|
}, [baseNormalized]);
|
|
3970
4231
|
if (stats && stats.values != null && stats.values > 0) {
|
|
3971
|
-
return /* @__PURE__ */
|
|
3972
|
-
/* @__PURE__ */
|
|
3973
|
-
/* @__PURE__ */
|
|
4232
|
+
return /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
4233
|
+
/* @__PURE__ */ jsx13("p", { className: "mt-2 text-sm font-semibold", children: "Overview" }),
|
|
4234
|
+
/* @__PURE__ */ jsxs12("p", { className: "text-sm text-muted-foreground", children: [
|
|
3974
4235
|
formatNumber(stats.values),
|
|
3975
4236
|
" Values"
|
|
3976
4237
|
] })
|
|
3977
4238
|
] });
|
|
3978
4239
|
}
|
|
3979
4240
|
if (stats && stats.nodes > 0 && colorPalette) {
|
|
3980
|
-
return /* @__PURE__ */
|
|
4241
|
+
return /* @__PURE__ */ jsx13(
|
|
3981
4242
|
GraphOverview,
|
|
3982
4243
|
{
|
|
3983
4244
|
stats,
|
|
@@ -3994,7 +4255,7 @@ var Overview_default = Overview;
|
|
|
3994
4255
|
|
|
3995
4256
|
// src/components/ActionPanel/ActionPanel.tsx
|
|
3996
4257
|
import { useMemo as useMemo5 } from "react";
|
|
3997
|
-
import { jsx as
|
|
4258
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3998
4259
|
function ActionPanel({ actions }) {
|
|
3999
4260
|
const useSelection3 = useWorkspaceSelection();
|
|
4000
4261
|
const selectedNodeIds = useSelection3(
|
|
@@ -4010,18 +4271,18 @@ function ActionPanel({ actions }) {
|
|
|
4010
4271
|
const secondNode = selectedNodes?.[1];
|
|
4011
4272
|
const hasEdge = !!selectedEdge;
|
|
4012
4273
|
const showOverview = !firstNode && !hasEdge;
|
|
4013
|
-
return /* @__PURE__ */
|
|
4274
|
+
return /* @__PURE__ */ jsxs13(
|
|
4014
4275
|
Card5,
|
|
4015
4276
|
{
|
|
4016
4277
|
className: "h-full min-w-64 flex flex-col bg-card shadow-none p-4",
|
|
4017
4278
|
style: { maxHeight: "100%", overflow: "hidden" },
|
|
4018
4279
|
children: [
|
|
4019
|
-
actions && /* @__PURE__ */
|
|
4020
|
-
/* @__PURE__ */
|
|
4021
|
-
firstNode && /* @__PURE__ */
|
|
4022
|
-
hasEdge && selectedEdge && /* @__PURE__ */
|
|
4023
|
-
secondNode && /* @__PURE__ */
|
|
4024
|
-
showOverview && /* @__PURE__ */
|
|
4280
|
+
actions && /* @__PURE__ */ jsx14("div", { className: "flex-shrink-0 mb-2", children: actions }),
|
|
4281
|
+
/* @__PURE__ */ jsx14("div", { className: "flex-1 overflow-auto pr-1", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-3", children: [
|
|
4282
|
+
firstNode && /* @__PURE__ */ jsx14(NodeInfoCard_default, { node: firstNode }),
|
|
4283
|
+
hasEdge && selectedEdge && /* @__PURE__ */ jsx14(EdgeInfoCard_default, { edge: selectedEdge }),
|
|
4284
|
+
secondNode && /* @__PURE__ */ jsx14(NodeInfoCard_default, { node: secondNode }),
|
|
4285
|
+
showOverview && /* @__PURE__ */ jsx14(Overview_default, {})
|
|
4025
4286
|
] }) })
|
|
4026
4287
|
]
|
|
4027
4288
|
}
|
|
@@ -4030,10 +4291,10 @@ function ActionPanel({ actions }) {
|
|
|
4030
4291
|
var ActionPanel_default = ActionPanel;
|
|
4031
4292
|
|
|
4032
4293
|
// src/components/GraphA11yList/GraphA11yList.tsx
|
|
4033
|
-
import { useMemo as useMemo6, useRef as
|
|
4294
|
+
import { useMemo as useMemo6, useRef as useRef4 } from "react";
|
|
4034
4295
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
4035
4296
|
import { cn as cn2 } from "@petrarca/sonnet-core";
|
|
4036
|
-
import { jsx as
|
|
4297
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
4037
4298
|
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";
|
|
4038
4299
|
function GraphA11yList({
|
|
4039
4300
|
visible = false,
|
|
@@ -4068,7 +4329,7 @@ function GraphA11yList({
|
|
|
4068
4329
|
}));
|
|
4069
4330
|
return [...nodeRows, ...edgeRows];
|
|
4070
4331
|
}, [live.nodes, live.edges]);
|
|
4071
|
-
const parentRef =
|
|
4332
|
+
const parentRef = useRef4(null);
|
|
4072
4333
|
const virtualizer = useVirtualizer({
|
|
4073
4334
|
count: rows.length,
|
|
4074
4335
|
getScrollElement: () => parentRef.current,
|
|
@@ -4085,18 +4346,18 @@ function GraphA11yList({
|
|
|
4085
4346
|
}
|
|
4086
4347
|
};
|
|
4087
4348
|
const isSelected = (row) => row.kind === "node" ? selectedNodeIds.includes(row.id) : selectedEdge?.id_ === row.edge.id_;
|
|
4088
|
-
return /* @__PURE__ */
|
|
4349
|
+
return /* @__PURE__ */ jsx15(
|
|
4089
4350
|
"div",
|
|
4090
4351
|
{
|
|
4091
4352
|
role: "listbox",
|
|
4092
4353
|
"aria-label": "Graph elements",
|
|
4093
4354
|
className: cn2(visible ? "rounded-md border" : SR_ONLY, className),
|
|
4094
|
-
children: /* @__PURE__ */
|
|
4355
|
+
children: /* @__PURE__ */ jsx15(
|
|
4095
4356
|
"div",
|
|
4096
4357
|
{
|
|
4097
4358
|
ref: parentRef,
|
|
4098
4359
|
style: { maxHeight: visible ? maxHeight : void 0, overflow: "auto" },
|
|
4099
|
-
children: /* @__PURE__ */
|
|
4360
|
+
children: /* @__PURE__ */ jsx15(
|
|
4100
4361
|
"div",
|
|
4101
4362
|
{
|
|
4102
4363
|
style: {
|
|
@@ -4108,7 +4369,7 @@ function GraphA11yList({
|
|
|
4108
4369
|
const row = rows[vi.index];
|
|
4109
4370
|
if (!row) return null;
|
|
4110
4371
|
const selected = isSelected(row);
|
|
4111
|
-
return /* @__PURE__ */
|
|
4372
|
+
return /* @__PURE__ */ jsx15(
|
|
4112
4373
|
"button",
|
|
4113
4374
|
{
|
|
4114
4375
|
type: "button",
|
|
@@ -4126,7 +4387,7 @@ function GraphA11yList({
|
|
|
4126
4387
|
height: `${vi.size}px`,
|
|
4127
4388
|
transform: `translateY(${vi.start}px)`
|
|
4128
4389
|
},
|
|
4129
|
-
children: /* @__PURE__ */
|
|
4390
|
+
children: /* @__PURE__ */ jsx15("span", { className: "truncate", children: row.label })
|
|
4130
4391
|
},
|
|
4131
4392
|
vi.key
|
|
4132
4393
|
);
|