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.
Files changed (109) hide show
  1. package/README.md +261 -0
  2. package/dist/client/_shell.html +0 -0
  3. package/dist/client/android-chrome-192x192.png +0 -0
  4. package/dist/client/android-chrome-512x512.png +0 -0
  5. package/dist/client/apple-touch-icon.png +0 -0
  6. package/dist/client/assets/app-Sjrldkrg.css +2 -0
  7. package/dist/client/assets/button-aOWckyNs.js +1 -0
  8. package/dist/client/assets/check-C0EQe2S8.js +1 -0
  9. package/dist/client/assets/chevron-down-DrspihmT.js +1 -0
  10. package/dist/client/assets/chevron-right-DIJHr8AN.js +1 -0
  11. package/dist/client/assets/colors-CQoWjU5E.js +1 -0
  12. package/dist/client/assets/command-kkF7_wdz.js +45 -0
  13. package/dist/client/assets/compare-K6jVFsiI.js +1 -0
  14. package/dist/client/assets/compare-TOnoe1EP.js +2 -0
  15. package/dist/client/assets/configure-DnlSnhtN.js +1 -0
  16. package/dist/client/assets/explorer-C7NclVKg.js +2 -0
  17. package/dist/client/assets/explorer-Xu2X6XXF.js +1 -0
  18. package/dist/client/assets/external-link-B9eNA-li.js +1 -0
  19. package/dist/client/assets/flamegraph-CRVZSAlj.js +13 -0
  20. package/dist/client/assets/floating-ui.dom-DLIT5tPE.js +1 -0
  21. package/dist/client/assets/formatting-CiC0SYI8.js +1 -0
  22. package/dist/client/assets/graph-6Vr74V1k.js +2 -0
  23. package/dist/client/assets/graph-CVzypIGU.js +2 -0
  24. package/dist/client/assets/graph-command-menu-D2MoVT2B.js +4 -0
  25. package/dist/client/assets/graph-command-menu-VWiiW3qy.css +1 -0
  26. package/dist/client/assets/hierarchy-C8xxGb_u.js +2 -0
  27. package/dist/client/assets/hierarchy-iO7d4oSK.js +2 -0
  28. package/dist/client/assets/import-display-D-jRyyjM.js +5 -0
  29. package/dist/client/assets/imports-CPggnrs-.js +2 -0
  30. package/dist/client/assets/imports-CodbPyUJ.js +1 -0
  31. package/dist/client/assets/index-Dj_rhLdR.js +12 -0
  32. package/dist/client/assets/input-BCFMF0aR.js +1 -0
  33. package/dist/client/assets/jsx-runtime-DWSWI4JT.js +1 -0
  34. package/dist/client/assets/lazyRouteComponent-PTSyFp1J.js +1 -0
  35. package/dist/client/assets/loading-state-CyC_hrTF.js +1 -0
  36. package/dist/client/assets/modviz-data-BiRqoDI5.js +1 -0
  37. package/dist/client/assets/modviz-layout-Do93E-IB.js +1 -0
  38. package/dist/client/assets/modviz-sigma-Xl8qHaxK.js +312 -0
  39. package/dist/client/assets/portal-BgAm3V3j.js +1 -0
  40. package/dist/client/assets/routes-DBtN8hrZ.js +1 -0
  41. package/dist/client/assets/schemas-B4zfTepZ.js +39 -0
  42. package/dist/client/assets/search-BYHxNrYn.js +1 -0
  43. package/dist/client/assets/search-params-BaZRBvGI.js +1 -0
  44. package/dist/client/assets/setup-view-j1o0TuZz.js +1 -0
  45. package/dist/client/assets/summary-D703Zh3x.js +1 -0
  46. package/dist/client/assets/tooltip-B1VDU9HG.js +1 -0
  47. package/dist/client/assets/trace-B67CM5s2.js +2 -0
  48. package/dist/client/assets/trace-Bwwdw3AM.js +1 -0
  49. package/dist/client/assets/treemap-BZf2shzY.js +5 -0
  50. package/dist/client/assets/treemap-Csroy8Gy.js +2 -0
  51. package/dist/client/assets/utils-DkkZd0ys.js +1 -0
  52. package/dist/client/favicon-16x16.png +0 -0
  53. package/dist/client/favicon-32x32.png +0 -0
  54. package/dist/client/favicon.ico +0 -0
  55. package/dist/client/favicon.png +0 -0
  56. package/dist/client/site.webmanifest +19 -0
  57. package/dist/mod/cli-options.js +225 -0
  58. package/dist/mod/cli.js +519 -0
  59. package/dist/mod/index.js +3 -0
  60. package/dist/mod/llm-analysis.js +29 -0
  61. package/dist/mod/llm-output.js +742 -0
  62. package/dist/mod/module-graph-plugins.js +60 -0
  63. package/dist/mod/production-server.js +103 -0
  64. package/dist/mod/runtime-host.js +217 -0
  65. package/dist/mod/snapshot-history.js +73 -0
  66. package/dist/mod/types.js +3 -0
  67. package/dist/server/assets/__23tanstack-start-plugin-adapters-3QxJs4a0.js +5 -0
  68. package/dist/server/assets/_tanstack-start-manifest_v-DMytuIue.js +188 -0
  69. package/dist/server/assets/button-Bqnnid5i.js +41 -0
  70. package/dist/server/assets/colors-DhAxrYua.js +100 -0
  71. package/dist/server/assets/command-SdxShIbL.js +138 -0
  72. package/dist/server/assets/compare-BFMiiUsB.js +562 -0
  73. package/dist/server/assets/compare-CpOqTpYu.js +10 -0
  74. package/dist/server/assets/configure-Bvd45DTI.js +288 -0
  75. package/dist/server/assets/explorer-C7dODpSv.js +379 -0
  76. package/dist/server/assets/explorer-CpSb0JTa.js +20 -0
  77. package/dist/server/assets/flamegraph-CdW-VG6I.js +198 -0
  78. package/dist/server/assets/formatting-iDlL4tA-.js +4 -0
  79. package/dist/server/assets/graph-C1G9H5O4.js +438 -0
  80. package/dist/server/assets/graph-DAGFGioS.js +45 -0
  81. package/dist/server/assets/graph-command-menu-BV5GtOWx.js +249 -0
  82. package/dist/server/assets/hierarchy-B4K-Zfn9.js +16 -0
  83. package/dist/server/assets/hierarchy-BGpWSG-f.js +104 -0
  84. package/dist/server/assets/import-display-BVIOWcsm.js +124 -0
  85. package/dist/server/assets/imports-B6JBDl_h.js +379 -0
  86. package/dist/server/assets/imports-BGe5tZJT.js +28 -0
  87. package/dist/server/assets/input-C5r-hBix.js +19 -0
  88. package/dist/server/assets/loading-state-CrvCWTtw.js +23 -0
  89. package/dist/server/assets/modviz-data-CUyTorv0.js +197 -0
  90. package/dist/server/assets/modviz-layout-BAH2ogse.js +253 -0
  91. package/dist/server/assets/modviz-server-DoMlAyFW.js +195 -0
  92. package/dist/server/assets/modviz-sigma-XYxARWqd.js +1441 -0
  93. package/dist/server/assets/rolldown-runtime-rSIU-vHC.js +13 -0
  94. package/dist/server/assets/router-DYJ-zDbU.js +353 -0
  95. package/dist/server/assets/routes-DInCacpY.js +244 -0
  96. package/dist/server/assets/search-params-BNApPgkX.js +26 -0
  97. package/dist/server/assets/setup-view-DjI49Iqr.js +91 -0
  98. package/dist/server/assets/start-Ba3KII43.js +4 -0
  99. package/dist/server/assets/summary-z3lXkLCQ.js +208 -0
  100. package/dist/server/assets/tooltip-Ck0DDfF7.js +24 -0
  101. package/dist/server/assets/trace-ColKOf9g.js +16 -0
  102. package/dist/server/assets/trace-eVs-hIZO.js +578 -0
  103. package/dist/server/assets/treemap-BbZ9M4GF.js +17 -0
  104. package/dist/server/assets/treemap-CrgWFoCF.js +912 -0
  105. package/dist/server/assets/utils-BQZm0uva.js +8 -0
  106. package/dist/server/server.js +5259 -0
  107. package/dist/shared/modviz-compare.js +120 -0
  108. package/dist/shared/modviz-trace.js +244 -0
  109. 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 };