modviz 0.1.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/README.md +261 -0
- package/dist/client/_shell.html +0 -0
- package/dist/client/android-chrome-192x192.png +0 -0
- package/dist/client/android-chrome-512x512.png +0 -0
- package/dist/client/apple-touch-icon.png +0 -0
- package/dist/client/assets/app-Sjrldkrg.css +2 -0
- package/dist/client/assets/button-aOWckyNs.js +1 -0
- package/dist/client/assets/check-C0EQe2S8.js +1 -0
- package/dist/client/assets/chevron-down-DrspihmT.js +1 -0
- package/dist/client/assets/chevron-right-DIJHr8AN.js +1 -0
- package/dist/client/assets/colors-CQoWjU5E.js +1 -0
- package/dist/client/assets/command-kkF7_wdz.js +45 -0
- package/dist/client/assets/compare-K6jVFsiI.js +1 -0
- package/dist/client/assets/compare-TOnoe1EP.js +2 -0
- package/dist/client/assets/configure-DnlSnhtN.js +1 -0
- package/dist/client/assets/explorer-C7NclVKg.js +2 -0
- package/dist/client/assets/explorer-Xu2X6XXF.js +1 -0
- package/dist/client/assets/external-link-B9eNA-li.js +1 -0
- package/dist/client/assets/flamegraph-CRVZSAlj.js +13 -0
- package/dist/client/assets/floating-ui.dom-DLIT5tPE.js +1 -0
- package/dist/client/assets/formatting-CiC0SYI8.js +1 -0
- package/dist/client/assets/graph-6Vr74V1k.js +2 -0
- package/dist/client/assets/graph-CVzypIGU.js +2 -0
- package/dist/client/assets/graph-command-menu-D2MoVT2B.js +4 -0
- package/dist/client/assets/graph-command-menu-VWiiW3qy.css +1 -0
- package/dist/client/assets/hierarchy-C8xxGb_u.js +2 -0
- package/dist/client/assets/hierarchy-iO7d4oSK.js +2 -0
- package/dist/client/assets/import-display-D-jRyyjM.js +5 -0
- package/dist/client/assets/imports-CPggnrs-.js +2 -0
- package/dist/client/assets/imports-CodbPyUJ.js +1 -0
- package/dist/client/assets/index-Dj_rhLdR.js +12 -0
- package/dist/client/assets/input-BCFMF0aR.js +1 -0
- package/dist/client/assets/jsx-runtime-DWSWI4JT.js +1 -0
- package/dist/client/assets/lazyRouteComponent-PTSyFp1J.js +1 -0
- package/dist/client/assets/loading-state-CyC_hrTF.js +1 -0
- package/dist/client/assets/modviz-data-BiRqoDI5.js +1 -0
- package/dist/client/assets/modviz-layout-Do93E-IB.js +1 -0
- package/dist/client/assets/modviz-sigma-Xl8qHaxK.js +312 -0
- package/dist/client/assets/portal-BgAm3V3j.js +1 -0
- package/dist/client/assets/routes-DBtN8hrZ.js +1 -0
- package/dist/client/assets/schemas-B4zfTepZ.js +39 -0
- package/dist/client/assets/search-BYHxNrYn.js +1 -0
- package/dist/client/assets/search-params-BaZRBvGI.js +1 -0
- package/dist/client/assets/setup-view-j1o0TuZz.js +1 -0
- package/dist/client/assets/summary-D703Zh3x.js +1 -0
- package/dist/client/assets/tooltip-B1VDU9HG.js +1 -0
- package/dist/client/assets/trace-B67CM5s2.js +2 -0
- package/dist/client/assets/trace-Bwwdw3AM.js +1 -0
- package/dist/client/assets/treemap-BZf2shzY.js +5 -0
- package/dist/client/assets/treemap-Csroy8Gy.js +2 -0
- package/dist/client/assets/utils-DkkZd0ys.js +1 -0
- package/dist/client/favicon-16x16.png +0 -0
- package/dist/client/favicon-32x32.png +0 -0
- package/dist/client/favicon.ico +0 -0
- package/dist/client/favicon.png +0 -0
- package/dist/client/site.webmanifest +19 -0
- package/dist/mod/cli-options.js +225 -0
- package/dist/mod/cli.js +519 -0
- package/dist/mod/index.js +3 -0
- package/dist/mod/llm-analysis.js +29 -0
- package/dist/mod/llm-output.js +742 -0
- package/dist/mod/module-graph-plugins.js +60 -0
- package/dist/mod/production-server.js +103 -0
- package/dist/mod/runtime-host.js +217 -0
- package/dist/mod/snapshot-history.js +73 -0
- package/dist/mod/types.js +3 -0
- package/dist/server/assets/__23tanstack-start-plugin-adapters-3QxJs4a0.js +5 -0
- package/dist/server/assets/_tanstack-start-manifest_v-DMytuIue.js +188 -0
- package/dist/server/assets/button-Bqnnid5i.js +41 -0
- package/dist/server/assets/colors-DhAxrYua.js +100 -0
- package/dist/server/assets/command-SdxShIbL.js +138 -0
- package/dist/server/assets/compare-BFMiiUsB.js +562 -0
- package/dist/server/assets/compare-CpOqTpYu.js +10 -0
- package/dist/server/assets/configure-Bvd45DTI.js +288 -0
- package/dist/server/assets/explorer-C7dODpSv.js +379 -0
- package/dist/server/assets/explorer-CpSb0JTa.js +20 -0
- package/dist/server/assets/flamegraph-CdW-VG6I.js +198 -0
- package/dist/server/assets/formatting-iDlL4tA-.js +4 -0
- package/dist/server/assets/graph-C1G9H5O4.js +438 -0
- package/dist/server/assets/graph-DAGFGioS.js +45 -0
- package/dist/server/assets/graph-command-menu-BV5GtOWx.js +249 -0
- package/dist/server/assets/hierarchy-B4K-Zfn9.js +16 -0
- package/dist/server/assets/hierarchy-BGpWSG-f.js +104 -0
- package/dist/server/assets/import-display-BVIOWcsm.js +124 -0
- package/dist/server/assets/imports-B6JBDl_h.js +379 -0
- package/dist/server/assets/imports-BGe5tZJT.js +28 -0
- package/dist/server/assets/input-C5r-hBix.js +19 -0
- package/dist/server/assets/loading-state-CrvCWTtw.js +23 -0
- package/dist/server/assets/modviz-data-CUyTorv0.js +197 -0
- package/dist/server/assets/modviz-layout-BAH2ogse.js +253 -0
- package/dist/server/assets/modviz-server-DoMlAyFW.js +195 -0
- package/dist/server/assets/modviz-sigma-XYxARWqd.js +1441 -0
- package/dist/server/assets/rolldown-runtime-rSIU-vHC.js +13 -0
- package/dist/server/assets/router-DYJ-zDbU.js +353 -0
- package/dist/server/assets/routes-DInCacpY.js +244 -0
- package/dist/server/assets/search-params-BNApPgkX.js +26 -0
- package/dist/server/assets/setup-view-DjI49Iqr.js +91 -0
- package/dist/server/assets/start-Ba3KII43.js +4 -0
- package/dist/server/assets/summary-z3lXkLCQ.js +208 -0
- package/dist/server/assets/tooltip-Ck0DDfF7.js +24 -0
- package/dist/server/assets/trace-ColKOf9g.js +16 -0
- package/dist/server/assets/trace-eVs-hIZO.js +578 -0
- package/dist/server/assets/treemap-BbZ9M4GF.js +17 -0
- package/dist/server/assets/treemap-CrgWFoCF.js +912 -0
- package/dist/server/assets/utils-BQZm0uva.js +8 -0
- package/dist/server/server.js +5259 -0
- package/dist/shared/modviz-compare.js +120 -0
- package/dist/shared/modviz-trace.js +244 -0
- package/package.json +135 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { t as cn } from "./utils-BQZm0uva.js";
|
|
2
|
+
import { t as Button } from "./button-Bqnnid5i.js";
|
|
3
|
+
import { c as getExternalPackageName } from "./modviz-data-CUyTorv0.js";
|
|
4
|
+
import { a as CommandInput, c as CommandSeparator, n as CommandDialog, o as CommandItem, r as CommandEmpty, s as CommandList } from "./command-SdxShIbL.js";
|
|
5
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { ChevronsUpDown } from "lucide-react";
|
|
8
|
+
import { useAtom } from "@xstate/store/react";
|
|
9
|
+
import { createAtom } from "@xstate/store";
|
|
10
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
11
|
+
import { Command, defaultFilter } from "cmdk";
|
|
12
|
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
13
|
+
//#region src/components/graph/common/use-graph-atoms.ts
|
|
14
|
+
var currentNodeIdAtom = createAtom(null);
|
|
15
|
+
var isNodeDetailsOpenAtom = createAtom(false);
|
|
16
|
+
var highlightedNodeIdAtom = createAtom(null);
|
|
17
|
+
var hoveredClusterNameAtom = createAtom(null);
|
|
18
|
+
var selectedNodeIdsAtom = createAtom([]);
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/components/ui/popover.tsx
|
|
21
|
+
function Popover({ ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Root, {
|
|
23
|
+
"data-slot": "popover",
|
|
24
|
+
...props
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function PopoverTrigger({ ...props }) {
|
|
28
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Trigger, {
|
|
29
|
+
"data-slot": "popover-trigger",
|
|
30
|
+
...props
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function PopoverContent({ className, align = "center", sideOffset = 4, ...props }) {
|
|
34
|
+
return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(PopoverPrimitive.Content, {
|
|
35
|
+
"data-slot": "popover-content",
|
|
36
|
+
align,
|
|
37
|
+
sideOffset,
|
|
38
|
+
className: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", className),
|
|
39
|
+
...props
|
|
40
|
+
}) });
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/components/graph/graph-command-menu.tsx
|
|
44
|
+
var buildNodeKeywords = (node) => [
|
|
45
|
+
node.name,
|
|
46
|
+
node.package?.name,
|
|
47
|
+
node.cluster,
|
|
48
|
+
node.path,
|
|
49
|
+
...node.path.split("/")
|
|
50
|
+
].filter(Boolean);
|
|
51
|
+
var getFilteredNodeRows = (nodesByClusterMap, search) => {
|
|
52
|
+
const normalizedSearch = search.trim();
|
|
53
|
+
const filteredClusters = Array.from(nodesByClusterMap.entries()).map(([cluster, nodeList]) => {
|
|
54
|
+
const filteredNodes = nodeList.map((node) => {
|
|
55
|
+
const keywords = buildNodeKeywords(node);
|
|
56
|
+
return {
|
|
57
|
+
keywords,
|
|
58
|
+
node,
|
|
59
|
+
score: normalizedSearch ? defaultFilter(node.path, normalizedSearch, keywords) : 1
|
|
60
|
+
};
|
|
61
|
+
}).filter((entry) => entry.score > 0);
|
|
62
|
+
if (normalizedSearch) filteredNodes.sort((left, right) => right.score - left.score || left.node.path.localeCompare(right.node.path));
|
|
63
|
+
return {
|
|
64
|
+
cluster,
|
|
65
|
+
filteredNodes,
|
|
66
|
+
maxScore: Math.max(...filteredNodes.map((entry) => entry.score), 0)
|
|
67
|
+
};
|
|
68
|
+
}).filter((entry) => entry.filteredNodes.length > 0);
|
|
69
|
+
if (normalizedSearch) filteredClusters.sort((left, right) => right.maxScore - left.maxScore || left.cluster.localeCompare(right.cluster));
|
|
70
|
+
return filteredClusters.flatMap(({ cluster, filteredNodes }) => filteredNodes.map((entry, index) => ({
|
|
71
|
+
cluster,
|
|
72
|
+
keywords: entry.keywords,
|
|
73
|
+
node: entry.node,
|
|
74
|
+
showClusterHeading: index === 0,
|
|
75
|
+
showSeparator: index === filteredNodes.length - 1
|
|
76
|
+
})));
|
|
77
|
+
};
|
|
78
|
+
var getNextSelectedValue = ({ event, filteredRows, selectedValue }) => {
|
|
79
|
+
if (filteredRows.length === 0) return;
|
|
80
|
+
const currentIndex = filteredRows.findIndex((row) => row.node.path === selectedValue);
|
|
81
|
+
switch (event.key) {
|
|
82
|
+
case "ArrowDown": return filteredRows[currentIndex >= 0 ? (currentIndex + 1) % filteredRows.length : 0]?.node.path;
|
|
83
|
+
case "ArrowUp": return filteredRows[currentIndex >= 0 ? (currentIndex - 1 + filteredRows.length) % filteredRows.length : filteredRows.length - 1]?.node.path;
|
|
84
|
+
case "Home": return filteredRows[0]?.node.path;
|
|
85
|
+
case "End": return filteredRows.at(-1)?.node.path;
|
|
86
|
+
default: return;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function NodeCommandList(props) {
|
|
90
|
+
const nodesByClusterMap = useNodesByClusterMap(props.nodes);
|
|
91
|
+
const [search, setSearch] = useState("");
|
|
92
|
+
const [selectedValue, setSelectedValue] = useState(void 0);
|
|
93
|
+
const listRef = useRef(null);
|
|
94
|
+
const filteredRows = useMemo(() => getFilteredNodeRows(nodesByClusterMap, search), [nodesByClusterMap, search]);
|
|
95
|
+
const selectedIndex = useMemo(() => filteredRows.findIndex((row) => row.node.path === selectedValue), [filteredRows, selectedValue]);
|
|
96
|
+
const rowVirtualizer = useVirtualizer({
|
|
97
|
+
count: filteredRows.length,
|
|
98
|
+
estimateSize: () => 68,
|
|
99
|
+
getItemKey: (index) => filteredRows[index]?.node.path ?? index,
|
|
100
|
+
getScrollElement: () => listRef.current,
|
|
101
|
+
overscan: 10
|
|
102
|
+
});
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (filteredRows.length === 0) {
|
|
105
|
+
setSelectedValue(void 0);
|
|
106
|
+
props.onHighlight(void 0);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (selectedValue && filteredRows.some((row) => row.node.path === selectedValue)) return;
|
|
110
|
+
setSelectedValue(filteredRows[0]?.node.path);
|
|
111
|
+
}, [
|
|
112
|
+
filteredRows,
|
|
113
|
+
props.onHighlight,
|
|
114
|
+
selectedValue
|
|
115
|
+
]);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (selectedIndex < 0) return;
|
|
118
|
+
rowVirtualizer.scrollToIndex(selectedIndex, { align: "auto" });
|
|
119
|
+
}, [rowVirtualizer, selectedIndex]);
|
|
120
|
+
const virtualRows = rowVirtualizer.getVirtualItems();
|
|
121
|
+
return /* @__PURE__ */ jsxs(Command, {
|
|
122
|
+
loop: true,
|
|
123
|
+
shouldFilter: false,
|
|
124
|
+
value: selectedValue,
|
|
125
|
+
onValueChange: setSelectedValue,
|
|
126
|
+
children: [/* @__PURE__ */ jsx(CommandInput, {
|
|
127
|
+
placeholder: "Type a command or search...",
|
|
128
|
+
value: search,
|
|
129
|
+
onValueChange: setSearch,
|
|
130
|
+
onKeyDown: (event) => {
|
|
131
|
+
const nextSelectedValue = getNextSelectedValue({
|
|
132
|
+
event,
|
|
133
|
+
filteredRows,
|
|
134
|
+
selectedValue
|
|
135
|
+
});
|
|
136
|
+
if (!nextSelectedValue) return;
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
setSelectedValue(nextSelectedValue);
|
|
139
|
+
props.onHighlight(nextSelectedValue);
|
|
140
|
+
}
|
|
141
|
+
}), /* @__PURE__ */ jsxs(CommandList, {
|
|
142
|
+
ref: listRef,
|
|
143
|
+
children: [/* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }), filteredRows.length === 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
144
|
+
className: "relative",
|
|
145
|
+
style: { height: `${rowVirtualizer.getTotalSize()}px` },
|
|
146
|
+
children: virtualRows.map((virtualRow) => {
|
|
147
|
+
const row = filteredRows[virtualRow.index];
|
|
148
|
+
if (!row) return null;
|
|
149
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
150
|
+
"data-index": virtualRow.index,
|
|
151
|
+
ref: rowVirtualizer.measureElement,
|
|
152
|
+
className: "absolute left-0 top-0 w-full",
|
|
153
|
+
style: { transform: `translateY(${virtualRow.start}px)` },
|
|
154
|
+
children: [
|
|
155
|
+
row.showClusterHeading ? /* @__PURE__ */ jsx("div", {
|
|
156
|
+
className: "text-muted-foreground px-2 py-1.5 text-xs font-medium",
|
|
157
|
+
children: row.cluster
|
|
158
|
+
}) : null,
|
|
159
|
+
/* @__PURE__ */ jsx(CommandItem, {
|
|
160
|
+
value: row.node.path,
|
|
161
|
+
keywords: row.keywords,
|
|
162
|
+
onMouseEnter: () => {
|
|
163
|
+
setSelectedValue(row.node.path);
|
|
164
|
+
props.onHighlight(row.node.path);
|
|
165
|
+
},
|
|
166
|
+
onSelect: (value) => {
|
|
167
|
+
props.onSelect(value);
|
|
168
|
+
props.onClose?.();
|
|
169
|
+
},
|
|
170
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
171
|
+
className: "flex min-w-0 flex-col",
|
|
172
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
173
|
+
className: "truncate",
|
|
174
|
+
children: row.node.name
|
|
175
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
176
|
+
className: "truncate text-xs text-slate-500",
|
|
177
|
+
children: row.node.path
|
|
178
|
+
})]
|
|
179
|
+
})
|
|
180
|
+
}),
|
|
181
|
+
row.showSeparator ? /* @__PURE__ */ jsx(CommandSeparator, { alwaysRender: true }) : null
|
|
182
|
+
]
|
|
183
|
+
}, virtualRow.key);
|
|
184
|
+
})
|
|
185
|
+
})]
|
|
186
|
+
})]
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function GraphCommandMenuDialog(props) {
|
|
190
|
+
const [open, setOpen] = useState(false);
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
const down = (e) => {
|
|
193
|
+
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
setOpen((open) => !open);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
document.addEventListener("keydown", down);
|
|
199
|
+
return () => document.removeEventListener("keydown", down);
|
|
200
|
+
}, []);
|
|
201
|
+
if (!open) return null;
|
|
202
|
+
return /* @__PURE__ */ jsx(CommandDialog, {
|
|
203
|
+
open,
|
|
204
|
+
onOpenChange: setOpen,
|
|
205
|
+
children: /* @__PURE__ */ jsx(NodeCommandList, {
|
|
206
|
+
...props,
|
|
207
|
+
onClose: () => setOpen(false)
|
|
208
|
+
})
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function GraphCommandMenu(props) {
|
|
212
|
+
const [open, setOpen] = useState(false);
|
|
213
|
+
const currentNodeId = useAtom(currentNodeIdAtom);
|
|
214
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
215
|
+
open,
|
|
216
|
+
onOpenChange: setOpen,
|
|
217
|
+
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
218
|
+
asChild: true,
|
|
219
|
+
children: /* @__PURE__ */ jsxs(Button, {
|
|
220
|
+
variant: "outline",
|
|
221
|
+
role: "combobox",
|
|
222
|
+
"aria-expanded": open,
|
|
223
|
+
className: "max-w-100 w-full justify-between",
|
|
224
|
+
children: [props.nodes.find((node) => node.path === currentNodeId)?.name ?? "Find or change current node...", /* @__PURE__ */ jsx(ChevronsUpDown, { className: "opacity-50" })]
|
|
225
|
+
})
|
|
226
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
227
|
+
className: "w-full p-0",
|
|
228
|
+
children: /* @__PURE__ */ jsx(NodeCommandList, {
|
|
229
|
+
...props,
|
|
230
|
+
onClose: () => setOpen(false)
|
|
231
|
+
})
|
|
232
|
+
})]
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
var useNodesByClusterMap = (nodes) => {
|
|
236
|
+
return useMemo(() => {
|
|
237
|
+
const map = /* @__PURE__ */ new Map();
|
|
238
|
+
nodes.forEach((node) => {
|
|
239
|
+
const group = node.path.includes("node_modules") ? `node_modules/${getExternalPackageName(node)}` : node.cluster ?? node.package?.name ?? "workspace";
|
|
240
|
+
const existingNodes = map.get(group) ?? [];
|
|
241
|
+
existingNodes.push(node);
|
|
242
|
+
map.set(group, existingNodes);
|
|
243
|
+
});
|
|
244
|
+
for (const [, groupedNodes] of map) groupedNodes.sort((left, right) => left.path.localeCompare(right.path));
|
|
245
|
+
return new Map(Array.from(map.entries()).sort((left, right) => left[0].localeCompare(right[0])));
|
|
246
|
+
}, [nodes]);
|
|
247
|
+
};
|
|
248
|
+
//#endregion
|
|
249
|
+
export { PopoverTrigger as a, hoveredClusterNameAtom as c, PopoverContent as i, isNodeDetailsOpenAtom as l, GraphCommandMenuDialog as n, currentNodeIdAtom as o, Popover as r, highlightedNodeIdAtom as s, GraphCommandMenu as t, selectedNodeIdsAtom as u };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
|
|
2
|
+
//#region src/routes/hierarchy.tsx
|
|
3
|
+
var $$splitComponentImporter = () => import("./hierarchy-BGpWSG-f.js");
|
|
4
|
+
var validateHierarchySearch = (search) => ({
|
|
5
|
+
entryNodeId: typeof search.entryNodeId === "string" ? search.entryNodeId : "",
|
|
6
|
+
includeExternal: search.includeExternal === void 0 ? true : search.includeExternal === true || search.includeExternal === "true",
|
|
7
|
+
maxChildren: Number.isFinite(Number(search.maxChildren)) ? Math.round(Number(search.maxChildren)) : 24,
|
|
8
|
+
maxDepth: Number.isFinite(Number(search.maxDepth)) ? Math.round(Number(search.maxDepth)) : 6
|
|
9
|
+
});
|
|
10
|
+
var Route = createFileRoute("/hierarchy")({
|
|
11
|
+
ssr: false,
|
|
12
|
+
validateSearch: validateHierarchySearch,
|
|
13
|
+
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { Route as t };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { f as isModvizBundleReady, m as useModvizBundle } from "./modviz-data-CUyTorv0.js";
|
|
2
|
+
import { t as LoadingState } from "./loading-state-CrvCWTtw.js";
|
|
3
|
+
import { t as Route } from "./hierarchy-B4K-Zfn9.js";
|
|
4
|
+
import { t as ModvizLayout } from "./modviz-layout-BAH2ogse.js";
|
|
5
|
+
import { t as SetupView } from "./setup-view-DjI49Iqr.js";
|
|
6
|
+
import { Suspense, lazy } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/routes/hierarchy.tsx?tsr-split=component
|
|
9
|
+
var Flamegraph = lazy(() => import("./flamegraph-CdW-VG6I.js").then((n) => n.n).then((module) => ({ default: module.Flamegraph })));
|
|
10
|
+
function HierarchyRoute() {
|
|
11
|
+
const bundle = useModvizBundle();
|
|
12
|
+
const search = Route.useSearch();
|
|
13
|
+
const navigate = Route.useNavigate();
|
|
14
|
+
if (!isModvizBundleReady(bundle)) return /* @__PURE__ */ jsx(ModvizLayout, {
|
|
15
|
+
projectTitle: bundle.projectTitle,
|
|
16
|
+
title: "Hierarchy",
|
|
17
|
+
description: "Pruned flamegraph for reading dependency hierarchy without freezing the browser.",
|
|
18
|
+
children: /* @__PURE__ */ jsx(SetupView, { bundle })
|
|
19
|
+
});
|
|
20
|
+
const entryNodeId = search.entryNodeId || bundle.graph.metadata.entrypoints[0] || bundle.graph.nodes[0]?.path || "";
|
|
21
|
+
const updateSearch = (patch) => navigate({
|
|
22
|
+
replace: true,
|
|
23
|
+
search: (previous) => ({
|
|
24
|
+
...previous,
|
|
25
|
+
...patch
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
return /* @__PURE__ */ jsx(ModvizLayout, {
|
|
29
|
+
projectTitle: bundle.projectTitle,
|
|
30
|
+
title: "Hierarchy",
|
|
31
|
+
description: "Pruned flamegraph for reading dependency hierarchy without freezing the browser. Depth and child limits are URL-backed so you can tune them per investigation.",
|
|
32
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
33
|
+
className: "flex h-[calc(100vh-14rem)] min-h-[680px] flex-col gap-4",
|
|
34
|
+
children: [/* @__PURE__ */ jsxs("section", {
|
|
35
|
+
className: "rounded-[24px] border border-slate-200/70 bg-white/90 p-4 shadow-[0_16px_50px_-32px_rgba(15,23,42,0.55)] dark:border-slate-800 dark:bg-slate-950/70",
|
|
36
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
37
|
+
className: "grid gap-4 lg:grid-cols-4",
|
|
38
|
+
children: [
|
|
39
|
+
/* @__PURE__ */ jsxs("label", {
|
|
40
|
+
className: "space-y-2 text-sm font-medium text-slate-700 dark:text-slate-200 lg:col-span-2",
|
|
41
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Entrypoint or focus node" }), /* @__PURE__ */ jsx("select", {
|
|
42
|
+
className: "flex h-10 w-full rounded-md border border-input bg-transparent px-3 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
43
|
+
value: entryNodeId,
|
|
44
|
+
onChange: (event) => updateSearch({ entryNodeId: event.currentTarget.value }),
|
|
45
|
+
children: bundle.graph.metadata.entrypoints.map((entrypoint) => /* @__PURE__ */ jsx("option", {
|
|
46
|
+
value: entrypoint,
|
|
47
|
+
children: entrypoint
|
|
48
|
+
}, entrypoint))
|
|
49
|
+
})]
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ jsxs("label", {
|
|
52
|
+
className: "space-y-2 text-sm font-medium text-slate-700 dark:text-slate-200",
|
|
53
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Max depth" }), /* @__PURE__ */ jsx("input", {
|
|
54
|
+
type: "number",
|
|
55
|
+
min: 2,
|
|
56
|
+
max: 12,
|
|
57
|
+
value: search.maxDepth,
|
|
58
|
+
onChange: (event) => updateSearch({ maxDepth: Number(event.currentTarget.value) || 6 }),
|
|
59
|
+
className: "flex h-10 w-full rounded-md border border-input bg-transparent px-3 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
|
60
|
+
})]
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ jsxs("label", {
|
|
63
|
+
className: "space-y-2 text-sm font-medium text-slate-700 dark:text-slate-200",
|
|
64
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Max children per node" }), /* @__PURE__ */ jsx("input", {
|
|
65
|
+
type: "number",
|
|
66
|
+
min: 4,
|
|
67
|
+
max: 80,
|
|
68
|
+
value: search.maxChildren,
|
|
69
|
+
onChange: (event) => updateSearch({ maxChildren: Number(event.currentTarget.value) || 24 }),
|
|
70
|
+
className: "flex h-10 w-full rounded-md border border-input bg-transparent px-3 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
|
71
|
+
})]
|
|
72
|
+
})
|
|
73
|
+
]
|
|
74
|
+
}), /* @__PURE__ */ jsxs("label", {
|
|
75
|
+
className: "mt-4 inline-flex items-center gap-3 text-sm text-slate-700 dark:text-slate-200",
|
|
76
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
77
|
+
type: "checkbox",
|
|
78
|
+
checked: search.includeExternal,
|
|
79
|
+
onChange: (event) => updateSearch({ includeExternal: event.currentTarget.checked })
|
|
80
|
+
}), /* @__PURE__ */ jsx("span", { children: "Include node_modules in the hierarchy slice" })]
|
|
81
|
+
})]
|
|
82
|
+
}), /* @__PURE__ */ jsx("section", {
|
|
83
|
+
className: "min-h-0 flex-1 overflow-hidden rounded-[28px] border border-slate-200/70 bg-white/90 shadow-[0_20px_70px_-36px_rgba(15,23,42,0.55)] dark:border-slate-800 dark:bg-slate-950/70",
|
|
84
|
+
children: /* @__PURE__ */ jsx(Suspense, {
|
|
85
|
+
fallback: /* @__PURE__ */ jsx(LoadingState, {
|
|
86
|
+
label: "Loading hierarchy…",
|
|
87
|
+
description: "Building a pruned dependency slice for the selected entrypoint."
|
|
88
|
+
}),
|
|
89
|
+
children: /* @__PURE__ */ jsx(Flamegraph, {
|
|
90
|
+
output: bundle.graph,
|
|
91
|
+
entryNodeId,
|
|
92
|
+
options: {
|
|
93
|
+
includeExternal: search.includeExternal,
|
|
94
|
+
maxChildren: search.maxChildren,
|
|
95
|
+
maxDepth: search.maxDepth
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
})]
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { HierarchyRoute as component };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { t as Button } from "./button-Bqnnid5i.js";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Code2, List } from "lucide-react";
|
|
5
|
+
//#region src/components/modviz/import-display.tsx
|
|
6
|
+
function ImportDisplay(props) {
|
|
7
|
+
const [viewMode, setViewMode] = useState("code");
|
|
8
|
+
const isFormatted = Array.isArray(props.imports) && props.imports.length > 0 && "code" in props.imports[0];
|
|
9
|
+
const blocks = useMemo(() => {
|
|
10
|
+
if (isFormatted) return props.imports;
|
|
11
|
+
return formatImportBlocks(props.imports);
|
|
12
|
+
}, [props.imports, isFormatted]);
|
|
13
|
+
const tableData = useMemo(() => {
|
|
14
|
+
const rows = [];
|
|
15
|
+
for (const block of blocks) {
|
|
16
|
+
const names = extractNamesFromCode(block.code);
|
|
17
|
+
for (const name of names) rows.push({
|
|
18
|
+
module: block.module,
|
|
19
|
+
name
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return rows.sort((a, b) => a.module.localeCompare(b.module) || a.name.localeCompare(b.name));
|
|
23
|
+
}, [blocks]);
|
|
24
|
+
if (blocks.length === 0) return /* @__PURE__ */ jsx("p", {
|
|
25
|
+
className: "rounded-2xl bg-white px-4 py-6 text-sm text-slate-500 dark:bg-slate-950/80 dark:text-slate-400",
|
|
26
|
+
children: props.emptyMessage || "No imports found."
|
|
27
|
+
});
|
|
28
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
29
|
+
className: "space-y-3",
|
|
30
|
+
children: [props.showViewToggle !== false && /* @__PURE__ */ jsxs("div", {
|
|
31
|
+
className: "flex gap-2",
|
|
32
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
33
|
+
size: "sm",
|
|
34
|
+
variant: viewMode === "code" ? "default" : "outline",
|
|
35
|
+
onClick: () => setViewMode("code"),
|
|
36
|
+
className: "gap-2",
|
|
37
|
+
children: [/* @__PURE__ */ jsx(Code2, { className: "size-3" }), "Code"]
|
|
38
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
39
|
+
size: "sm",
|
|
40
|
+
variant: viewMode === "table" ? "default" : "outline",
|
|
41
|
+
onClick: () => setViewMode("table"),
|
|
42
|
+
className: "gap-2",
|
|
43
|
+
children: [/* @__PURE__ */ jsx(List, { className: "size-3" }), "Table"]
|
|
44
|
+
})]
|
|
45
|
+
}), viewMode === "code" ? /* @__PURE__ */ jsx("div", {
|
|
46
|
+
className: "space-y-3",
|
|
47
|
+
children: blocks.map((block, idx) => /* @__PURE__ */ jsxs("div", {
|
|
48
|
+
className: "rounded-2xl border border-slate-200/70 bg-white/80 p-4 dark:border-slate-800 dark:bg-slate-950/60",
|
|
49
|
+
children: [blocks.length > 1 && /* @__PURE__ */ jsx("p", {
|
|
50
|
+
className: "mb-2 text-xs font-medium text-slate-500 dark:text-slate-400",
|
|
51
|
+
children: block.module
|
|
52
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
53
|
+
className: "overflow-x-auto text-sm leading-6 text-slate-800 dark:text-slate-100",
|
|
54
|
+
children: /* @__PURE__ */ jsx("code", { children: block.code })
|
|
55
|
+
})]
|
|
56
|
+
}, idx))
|
|
57
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
58
|
+
className: "overflow-x-auto rounded-2xl border border-slate-200/70 dark:border-slate-800",
|
|
59
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
60
|
+
className: "w-full text-sm",
|
|
61
|
+
children: [/* @__PURE__ */ jsx("thead", {
|
|
62
|
+
className: "border-b border-slate-200/70 bg-slate-50/80 dark:border-slate-800 dark:bg-slate-900/80",
|
|
63
|
+
children: /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
|
|
64
|
+
className: "px-4 py-3 text-left font-semibold text-slate-700 dark:text-slate-300",
|
|
65
|
+
children: "Module"
|
|
66
|
+
}), /* @__PURE__ */ jsx("th", {
|
|
67
|
+
className: "px-4 py-3 text-left font-semibold text-slate-700 dark:text-slate-300",
|
|
68
|
+
children: "Name"
|
|
69
|
+
})] })
|
|
70
|
+
}), /* @__PURE__ */ jsx("tbody", {
|
|
71
|
+
className: "divide-y divide-slate-200/70 dark:divide-slate-800",
|
|
72
|
+
children: tableData.map((row, idx) => /* @__PURE__ */ jsxs("tr", {
|
|
73
|
+
className: "hover:bg-sky-50/30 dark:hover:bg-sky-500/10",
|
|
74
|
+
children: [/* @__PURE__ */ jsx("td", {
|
|
75
|
+
className: "px-4 py-3 font-mono text-slate-600 dark:text-slate-400",
|
|
76
|
+
children: row.module
|
|
77
|
+
}), /* @__PURE__ */ jsx("td", {
|
|
78
|
+
className: "px-4 py-3 font-mono text-slate-800 dark:text-slate-100",
|
|
79
|
+
children: row.name
|
|
80
|
+
})]
|
|
81
|
+
}, `${idx}`))
|
|
82
|
+
})]
|
|
83
|
+
})
|
|
84
|
+
})]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function formatImportBlocks(matches) {
|
|
88
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const match of matches) {
|
|
90
|
+
const current = grouped.get(match.module) ?? {
|
|
91
|
+
names: /* @__PURE__ */ new Set(),
|
|
92
|
+
hasBareImport: false
|
|
93
|
+
};
|
|
94
|
+
const importName = match.name || match.declaration;
|
|
95
|
+
if (importName) current.names.add(importName);
|
|
96
|
+
else current.hasBareImport = true;
|
|
97
|
+
grouped.set(match.module, current);
|
|
98
|
+
}
|
|
99
|
+
const importLines = [];
|
|
100
|
+
Array.from(grouped.entries()).sort(([a], [b]) => a.localeCompare(b)).forEach(([module, details]) => {
|
|
101
|
+
const names = Array.from(details.names).sort((left, right) => left.localeCompare(right));
|
|
102
|
+
if (!names.length) {
|
|
103
|
+
importLines.push(`import ${JSON.stringify(module)};`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const importBody = names.length === 1 ? `{ ${names[0]} }` : `{
|
|
107
|
+
${names.join(",\n ")}
|
|
108
|
+
}`;
|
|
109
|
+
importLines.push(`import ${importBody} from ${JSON.stringify(module)};`);
|
|
110
|
+
if (details.hasBareImport) importLines.push(`import ${JSON.stringify(module)};`);
|
|
111
|
+
});
|
|
112
|
+
return [{
|
|
113
|
+
module: "imports",
|
|
114
|
+
code: importLines.join("\n")
|
|
115
|
+
}];
|
|
116
|
+
}
|
|
117
|
+
function extractNamesFromCode(code) {
|
|
118
|
+
const names = [];
|
|
119
|
+
const importMatches = code.matchAll(/import\s+(?:\{([^}]+)\})?[^;]*;/g);
|
|
120
|
+
for (const match of importMatches) if (match[1]) names.push(...match[1].split(",").map((name) => name.trim()).filter(Boolean));
|
|
121
|
+
return [...new Set(names)];
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { ImportDisplay as t };
|