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,578 @@
1
+ import { t as Button } from "./button-Bqnnid5i.js";
2
+ import { t as Input } from "./input-C5r-hBix.js";
3
+ import { f as isModvizBundleReady, m as useModvizBundle } from "./modviz-data-CUyTorv0.js";
4
+ import { t as Route$1 } from "./trace-ColKOf9g.js";
5
+ import { t as ModvizLayout } from "./modviz-layout-BAH2ogse.js";
6
+ import { t as SetupView } from "./setup-view-DjI49Iqr.js";
7
+ import { useDeferredValue, useEffect, useMemo, useState } from "react";
8
+ import { Link } from "@tanstack/react-router";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { Search, Sparkles } from "lucide-react";
11
+ //#region shared/modviz-trace.ts
12
+ var chainCache = /* @__PURE__ */ new WeakMap();
13
+ var packageReportCache = /* @__PURE__ */ new WeakMap();
14
+ var nodeReportCache = /* @__PURE__ */ new WeakMap();
15
+ var normalizeForSearch = (value) => value.trim().toLowerCase();
16
+ var isExternalPath = (value) => value.includes("node_modules");
17
+ var uniqueSorted = (values) => Array.from(new Set(values)).filter(Boolean).sort((left, right) => left.localeCompare(right));
18
+ var getTracePackageName = (node) => {
19
+ if (!node.path.includes("node_modules")) return node.package?.name ?? null;
20
+ if (node.package?.name && node.package.name !== "node_modules") return node.package.name;
21
+ const segments = node.path.split(/[\\/]/).filter(Boolean);
22
+ const nodeModulesIndex = segments.lastIndexOf("node_modules");
23
+ if (nodeModulesIndex === -1) return null;
24
+ const scopeOrName = segments[nodeModulesIndex + 1];
25
+ const maybeName = segments[nodeModulesIndex + 2];
26
+ if (!scopeOrName) return null;
27
+ return scopeOrName.startsWith("@") && maybeName ? `${scopeOrName}/${maybeName}` : scopeOrName;
28
+ };
29
+ var uniqueChains = (chains) => {
30
+ const seen = /* @__PURE__ */ new Set();
31
+ const nextChains = [];
32
+ for (const chain of chains) {
33
+ const key = chain.join(" -> ");
34
+ if (seen.has(key)) continue;
35
+ seen.add(key);
36
+ nextChains.push(chain);
37
+ }
38
+ return nextChains.sort((left, right) => {
39
+ const byLength = left.length - right.length;
40
+ return byLength !== 0 ? byLength : left.join("/").localeCompare(right.join("/"));
41
+ });
42
+ };
43
+ var buildUpstreamChains = (graph, targetPath, maxChains = 40, maxDepth = 28) => {
44
+ const cacheKey = `${targetPath}::${maxChains}::${maxDepth}`;
45
+ const cachedGraphChains = chainCache.get(graph);
46
+ if (cachedGraphChains?.has(cacheKey)) return cachedGraphChains.get(cacheKey);
47
+ const nodeByPath = new Map(graph.nodes.map((node) => [node.path, node]));
48
+ const entrypoints = new Set(graph.metadata.entrypoints);
49
+ const results = [];
50
+ const queue = [[targetPath]];
51
+ let queueIndex = 0;
52
+ while (queueIndex < queue.length && results.length < maxChains) {
53
+ const reverseChain = queue[queueIndex];
54
+ queueIndex += 1;
55
+ if (!reverseChain) continue;
56
+ const currentPath = reverseChain[0];
57
+ const currentNode = nodeByPath.get(currentPath);
58
+ const importers = uniqueSorted((currentNode?.importedBy ?? []).filter((importerPath) => !reverseChain.includes(importerPath) && nodeByPath.has(importerPath)));
59
+ if (!currentNode || importers.length === 0 || entrypoints.has(currentPath) || reverseChain.length >= maxDepth) {
60
+ results.push([...reverseChain].reverse());
61
+ continue;
62
+ }
63
+ importers.sort((left, right) => {
64
+ const leftExternal = isExternalPath(left);
65
+ if (leftExternal !== isExternalPath(right)) return leftExternal ? 1 : -1;
66
+ return left.localeCompare(right);
67
+ }).forEach((importerPath) => {
68
+ queue.push([importerPath, ...reverseChain]);
69
+ });
70
+ }
71
+ const resolvedChains = uniqueChains(results);
72
+ const nextCache = cachedGraphChains ?? /* @__PURE__ */ new Map();
73
+ nextCache.set(cacheKey, resolvedChains);
74
+ if (!cachedGraphChains) chainCache.set(graph, nextCache);
75
+ return resolvedChains;
76
+ };
77
+ var getWorkspaceOrigin = (chain) => chain.find((segment) => !isExternalPath(segment)) ?? chain[0] ?? null;
78
+ var getIntroducedThrough = (chain) => {
79
+ for (let index = 0; index < chain.length; index += 1) {
80
+ if (!isExternalPath(chain[index])) continue;
81
+ return chain[index - 1] ?? chain[index] ?? null;
82
+ }
83
+ return chain.at(-1) ?? null;
84
+ };
85
+ var createTraceMatch = (node, chains) => ({
86
+ path: node.path,
87
+ label: node.name,
88
+ packageName: getTracePackageName(node),
89
+ type: node.type,
90
+ targetPaths: [node.path],
91
+ directImporters: uniqueSorted(node.importedBy),
92
+ chains,
93
+ workspaceOrigins: uniqueSorted(chains.map((chain) => getWorkspaceOrigin(chain)).filter(Boolean)),
94
+ introducedThrough: uniqueSorted(chains.map((chain) => getIntroducedThrough(chain)).filter(Boolean))
95
+ });
96
+ var buildPackageTraceReport = (graph, packageQuery, options = {}) => {
97
+ const { maxChainsPerTarget = 40, maxDepth = 28, maxNodesPerPackage = Number.POSITIVE_INFINITY } = options;
98
+ const cacheSuffix = `::${maxChainsPerTarget}::${maxDepth}::${maxNodesPerPackage}`;
99
+ const normalizedQuery = normalizeForSearch(packageQuery);
100
+ const cacheKey = `${normalizedQuery}${cacheSuffix}`;
101
+ const cachedReports = packageReportCache.get(graph);
102
+ if (cachedReports?.has(cacheKey)) return cachedReports.get(cacheKey);
103
+ const groupedMatches = /* @__PURE__ */ new Map();
104
+ const tracedNodeCountByPackage = /* @__PURE__ */ new Map();
105
+ graph.nodes.filter((node) => node.path.includes("node_modules")).filter((node) => {
106
+ const packageName = getTracePackageName(node);
107
+ return packageName ? normalizeForSearch(packageName).includes(normalizedQuery) : false;
108
+ }).forEach((node) => {
109
+ const packageName = getTracePackageName(node) ?? node.path;
110
+ const tracedCount = tracedNodeCountByPackage.get(packageName) ?? 0;
111
+ const canTraceNode = tracedCount < maxNodesPerPackage;
112
+ const chains = canTraceNode ? buildUpstreamChains(graph, node.path, maxChainsPerTarget, maxDepth) : [];
113
+ if (canTraceNode) tracedNodeCountByPackage.set(packageName, tracedCount + 1);
114
+ const existing = groupedMatches.get(packageName);
115
+ if (!existing) {
116
+ groupedMatches.set(packageName, {
117
+ path: packageName,
118
+ label: packageName,
119
+ packageName,
120
+ type: "package",
121
+ targetPaths: [node.path],
122
+ directImporters: uniqueSorted(node.importedBy),
123
+ chains,
124
+ workspaceOrigins: uniqueSorted(chains.map((chain) => getWorkspaceOrigin(chain)).filter(Boolean)),
125
+ introducedThrough: uniqueSorted(chains.map((chain) => getIntroducedThrough(chain)).filter(Boolean))
126
+ });
127
+ return;
128
+ }
129
+ existing.targetPaths = uniqueSorted([...existing.targetPaths, node.path]);
130
+ existing.directImporters = uniqueSorted([...existing.directImporters, ...node.importedBy]);
131
+ existing.chains = uniqueChains([...existing.chains, ...chains]);
132
+ existing.workspaceOrigins = uniqueSorted([...existing.workspaceOrigins, ...chains.map((chain) => getWorkspaceOrigin(chain)).filter(Boolean)]);
133
+ existing.introducedThrough = uniqueSorted([...existing.introducedThrough, ...chains.map((chain) => getIntroducedThrough(chain)).filter(Boolean)]);
134
+ });
135
+ const matches = Array.from(groupedMatches.values()).sort((left, right) => left.label.localeCompare(right.label));
136
+ const report = {
137
+ kind: "package",
138
+ query: packageQuery,
139
+ matchedLabels: Array.from(new Set(matches.map((match) => match.packageName).filter(Boolean))).sort((left, right) => left.localeCompare(right)),
140
+ matches,
141
+ totalChains: matches.reduce((sum, match) => sum + match.chains.length, 0)
142
+ };
143
+ const nextCache = cachedReports ?? /* @__PURE__ */ new Map();
144
+ nextCache.set(cacheKey, report);
145
+ if (!cachedReports) packageReportCache.set(graph, nextCache);
146
+ return report;
147
+ };
148
+ var buildNodeTraceReport = (graph, nodeQuery, options = {}) => {
149
+ const { maxChainsPerTarget = 40, maxDepth = 28, maxNodeMatches = Number.POSITIVE_INFINITY } = options;
150
+ const cacheSuffix = `::${maxChainsPerTarget}::${maxDepth}::${maxNodeMatches}`;
151
+ const normalizedQuery = normalizeForSearch(nodeQuery);
152
+ const cacheKey = `${normalizedQuery}${cacheSuffix}`;
153
+ const cachedReports = nodeReportCache.get(graph);
154
+ if (cachedReports?.has(cacheKey)) return cachedReports.get(cacheKey);
155
+ const matches = graph.nodes.filter((node) => {
156
+ return [
157
+ node.path,
158
+ node.name,
159
+ node.package?.name,
160
+ node.cluster
161
+ ].filter(Boolean).some((value) => normalizeForSearch(String(value)).includes(normalizedQuery));
162
+ }).slice(0, maxNodeMatches).map((node) => createTraceMatch(node, buildUpstreamChains(graph, node.path, maxChainsPerTarget, maxDepth)));
163
+ const report = {
164
+ kind: "node",
165
+ query: nodeQuery,
166
+ matchedLabels: matches.map((match) => match.path),
167
+ matches,
168
+ totalChains: matches.reduce((sum, match) => sum + match.chains.length, 0)
169
+ };
170
+ const nextCache = cachedReports ?? /* @__PURE__ */ new Map();
171
+ nextCache.set(cacheKey, report);
172
+ if (!cachedReports) nodeReportCache.set(graph, nextCache);
173
+ return report;
174
+ };
175
+ //#endregion
176
+ //#region src/components/modviz/trace-view.tsx
177
+ var TRACE_RESULT_LIMIT_DEFAULT = 25;
178
+ var TRACE_RENDERED_MATCHES_LIMIT = 30;
179
+ var explorerSearchForPath = (path) => ({
180
+ q: "",
181
+ selected: path,
182
+ scope: path.includes("node_modules") ? "external" : "workspace"
183
+ });
184
+ function TracePathLink(props) {
185
+ return /* @__PURE__ */ jsx(Link, {
186
+ to: "/explorer",
187
+ search: explorerSearchForPath(props.path),
188
+ className: props.className ?? "rounded-full bg-white px-3 py-1 hover:text-sky-700 dark:bg-slate-950/80 dark:hover:text-sky-300",
189
+ children: props.path
190
+ });
191
+ }
192
+ var defaultGraphSearch = {
193
+ adjustSizes: false,
194
+ cluster: "",
195
+ externalGrouping: "package",
196
+ focus: "",
197
+ gravity: 0,
198
+ hideClusterLabels: false,
199
+ iterations: 0,
200
+ linLogMode: false,
201
+ nodeSizeScale: 0,
202
+ outboundAttractionDistribution: true,
203
+ scalingRatio: 0,
204
+ scope: "all",
205
+ strongGravityMode: false
206
+ };
207
+ function TraceView(props) {
208
+ const [draftSearch, setDraftSearch] = useState(props.search);
209
+ const deferredSearch = useDeferredValue(props.search);
210
+ useEffect(() => {
211
+ setDraftSearch(props.search);
212
+ }, [props.search]);
213
+ const applySearch = (next) => {
214
+ props.onSearchChange(next);
215
+ };
216
+ const renderedLimit = deferredSearch.limit || TRACE_RESULT_LIMIT_DEFAULT;
217
+ const report = useMemo(() => {
218
+ if (deferredSearch.package.trim()) return buildPackageTraceReport(props.bundle.graph, deferredSearch.package, {
219
+ maxChainsPerTarget: Math.max(14, renderedLimit),
220
+ maxDepth: 22,
221
+ maxNodesPerPackage: Math.max(80, renderedLimit * 4)
222
+ });
223
+ if (deferredSearch.node.trim()) return buildNodeTraceReport(props.bundle.graph, deferredSearch.node, {
224
+ maxChainsPerTarget: Math.max(14, renderedLimit),
225
+ maxDepth: 22,
226
+ maxNodeMatches: Math.max(160, renderedLimit * 6)
227
+ });
228
+ return null;
229
+ }, [
230
+ deferredSearch.node,
231
+ deferredSearch.package,
232
+ props.bundle.graph,
233
+ renderedLimit
234
+ ]);
235
+ const visibleMatches = useMemo(() => report?.matches.slice(0, TRACE_RENDERED_MATCHES_LIMIT) ?? [], [report]);
236
+ return /* @__PURE__ */ jsxs("div", {
237
+ className: "space-y-6",
238
+ children: [/* @__PURE__ */ jsxs("section", {
239
+ className: "rounded-[24px] border border-slate-200/70 bg-white/90 p-5 shadow-[0_16px_50px_-32px_rgba(15,23,42,0.55)] dark:border-slate-800 dark:bg-slate-950/70",
240
+ children: [
241
+ /* @__PURE__ */ jsxs("div", {
242
+ className: "grid gap-4 lg:grid-cols-3",
243
+ children: [
244
+ /* @__PURE__ */ jsxs("div", {
245
+ className: "space-y-2",
246
+ children: [/* @__PURE__ */ jsx("label", {
247
+ className: "text-sm font-medium text-slate-700 dark:text-slate-200",
248
+ children: "External package"
249
+ }), /* @__PURE__ */ jsx(Input, {
250
+ placeholder: "zod, react, lodash-es",
251
+ value: draftSearch.package,
252
+ onChange: (event) => {
253
+ const value = event.currentTarget.value;
254
+ setDraftSearch((previous) => ({
255
+ ...previous,
256
+ package: value,
257
+ node: ""
258
+ }));
259
+ },
260
+ onKeyDown: (event) => {
261
+ if (event.key === "Enter") {
262
+ const value = event.currentTarget.value;
263
+ applySearch({
264
+ ...draftSearch,
265
+ package: value,
266
+ node: ""
267
+ });
268
+ }
269
+ }
270
+ })]
271
+ }),
272
+ /* @__PURE__ */ jsxs("div", {
273
+ className: "space-y-2",
274
+ children: [/* @__PURE__ */ jsx("label", {
275
+ className: "text-sm font-medium text-slate-700 dark:text-slate-200",
276
+ children: "Node path or name"
277
+ }), /* @__PURE__ */ jsx(Input, {
278
+ placeholder: "src/routes/index.ts",
279
+ value: draftSearch.node,
280
+ onChange: (event) => {
281
+ const value = event.currentTarget.value;
282
+ setDraftSearch((previous) => ({
283
+ ...previous,
284
+ node: value,
285
+ package: ""
286
+ }));
287
+ },
288
+ onKeyDown: (event) => {
289
+ if (event.key === "Enter") {
290
+ const value = event.currentTarget.value;
291
+ applySearch({
292
+ ...draftSearch,
293
+ node: value,
294
+ package: ""
295
+ });
296
+ }
297
+ }
298
+ })]
299
+ }),
300
+ /* @__PURE__ */ jsxs("div", {
301
+ className: "space-y-2",
302
+ children: [/* @__PURE__ */ jsx("label", {
303
+ className: "text-sm font-medium text-slate-700 dark:text-slate-200",
304
+ children: "Chain limit"
305
+ }), /* @__PURE__ */ jsx(Input, {
306
+ type: "number",
307
+ min: "1",
308
+ max: "200",
309
+ value: String(draftSearch.limit),
310
+ onChange: (event) => {
311
+ setDraftSearch((previous) => ({
312
+ ...previous,
313
+ limit: Math.max(1, Number(event.currentTarget.value) || TRACE_RESULT_LIMIT_DEFAULT)
314
+ }));
315
+ },
316
+ onKeyDown: (event) => {
317
+ if (event.key === "Enter") applySearch(draftSearch);
318
+ }
319
+ })]
320
+ })
321
+ ]
322
+ }),
323
+ /* @__PURE__ */ jsxs("div", {
324
+ className: "mt-4 flex flex-wrap gap-2",
325
+ children: [
326
+ /* @__PURE__ */ jsx(Button, {
327
+ variant: "outline",
328
+ onClick: () => applySearch({
329
+ package: "react",
330
+ node: "",
331
+ limit: draftSearch.limit || TRACE_RESULT_LIMIT_DEFAULT
332
+ }),
333
+ children: "Trace React"
334
+ }),
335
+ /* @__PURE__ */ jsx(Button, {
336
+ variant: "outline",
337
+ onClick: () => applySearch({
338
+ package: "",
339
+ node: props.bundle.graph.metadata.entrypoints[0] ?? "",
340
+ limit: draftSearch.limit || TRACE_RESULT_LIMIT_DEFAULT
341
+ }),
342
+ children: "Trace entrypoint"
343
+ }),
344
+ /* @__PURE__ */ jsx(Button, {
345
+ variant: "outline",
346
+ onClick: () => {
347
+ const reset = {
348
+ package: "",
349
+ node: "",
350
+ limit: TRACE_RESULT_LIMIT_DEFAULT
351
+ };
352
+ setDraftSearch(reset);
353
+ applySearch(reset);
354
+ },
355
+ children: "Reset"
356
+ }),
357
+ /* @__PURE__ */ jsx(Button, {
358
+ onClick: () => applySearch(draftSearch),
359
+ children: "Apply"
360
+ })
361
+ ]
362
+ }),
363
+ /* @__PURE__ */ jsx("p", {
364
+ className: "mt-4 text-sm leading-6 text-slate-500 dark:text-slate-400",
365
+ children: "Import search finds matching import statements. Trace explains why a file or external package is reachable by walking upstream importers back toward workspace roots and entrypoints."
366
+ })
367
+ ]
368
+ }), report ? /* @__PURE__ */ jsxs("section", {
369
+ className: "space-y-4",
370
+ children: [/* @__PURE__ */ jsx("div", {
371
+ className: "rounded-[24px] border border-slate-200/70 bg-white/90 p-5 shadow-[0_16px_50px_-32px_rgba(15,23,42,0.55)] dark:border-slate-800 dark:bg-slate-950/70",
372
+ children: /* @__PURE__ */ jsxs("div", {
373
+ className: "flex items-center justify-between gap-4",
374
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h2", {
375
+ className: "text-lg font-semibold text-slate-900 dark:text-slate-100",
376
+ children: "Trace results"
377
+ }), /* @__PURE__ */ jsxs("p", {
378
+ className: "mt-1 text-sm text-slate-500 dark:text-slate-400",
379
+ children: [
380
+ "Matched ",
381
+ report.matches.length,
382
+ " result(s) across ",
383
+ report.totalChains,
384
+ " origin chain(s). Showing up to ",
385
+ renderedLimit,
386
+ " path(s) per section."
387
+ ]
388
+ })] }), /* @__PURE__ */ jsx("div", {
389
+ className: "rounded-full bg-slate-950 px-4 py-2 text-sm font-semibold text-white dark:bg-slate-100 dark:text-slate-950",
390
+ children: report.kind
391
+ })]
392
+ })
393
+ }), report.matches.length === 0 ? /* @__PURE__ */ jsx("div", {
394
+ className: "rounded-[24px] border border-slate-200/70 bg-white/90 p-8 text-sm text-slate-500 shadow-[0_16px_50px_-32px_rgba(15,23,42,0.55)] dark:border-slate-800 dark:bg-slate-950/70 dark:text-slate-400",
395
+ children: "No matching origin traces were found."
396
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [report.matches.length > TRACE_RENDERED_MATCHES_LIMIT ? /* @__PURE__ */ jsxs("div", {
397
+ className: "rounded-[20px] border border-amber-200/60 bg-amber-50/80 px-4 py-3 text-xs text-amber-800 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-200",
398
+ children: [
399
+ "Rendering the first ",
400
+ TRACE_RENDERED_MATCHES_LIMIT,
401
+ " matches out of",
402
+ " ",
403
+ report.matches.length,
404
+ ". Refine the query to narrow results."
405
+ ]
406
+ }) : null, visibleMatches.map((match) => /* @__PURE__ */ jsxs("article", {
407
+ className: "rounded-[24px] border border-slate-200/70 bg-white/90 p-5 shadow-[0_16px_50px_-32px_rgba(15,23,42,0.55)] dark:border-slate-800 dark:bg-slate-950/70",
408
+ children: [
409
+ /* @__PURE__ */ jsxs("div", {
410
+ className: "flex flex-wrap items-start justify-between gap-4",
411
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
412
+ className: "text-base font-semibold text-slate-900 dark:text-slate-100",
413
+ children: match.label
414
+ }), /* @__PURE__ */ jsxs("p", {
415
+ className: "mt-1 text-sm text-slate-500 dark:text-slate-400",
416
+ children: [
417
+ match.packageName ? `${match.packageName} • ` : "",
418
+ match.type,
419
+ " • ",
420
+ match.targetPaths.length,
421
+ " matching file(s) •",
422
+ " ",
423
+ match.workspaceOrigins.length,
424
+ " workspace origin(s)"
425
+ ]
426
+ })] }), /* @__PURE__ */ jsxs("div", {
427
+ className: "flex flex-wrap gap-2",
428
+ children: [/* @__PURE__ */ jsx(Link, {
429
+ to: "/graph",
430
+ search: {
431
+ ...defaultGraphSearch,
432
+ focus: match.introducedThrough[0] ?? match.workspaceOrigins[0] ?? match.targetPaths[0] ?? match.path,
433
+ scope: match.introducedThrough[0] || match.workspaceOrigins[0] ? "workspace" : match.path.includes("node_modules") ? "external" : "workspace"
434
+ },
435
+ className: "text-sm font-medium text-sky-700 dark:text-sky-300",
436
+ children: "Open in graph"
437
+ }), /* @__PURE__ */ jsx(Link, {
438
+ to: "/explorer",
439
+ search: {
440
+ selected: match.introducedThrough[0] ?? match.workspaceOrigins[0] ?? match.targetPaths[0] ?? match.path,
441
+ q: "",
442
+ scope: match.introducedThrough[0] || match.workspaceOrigins[0] ? "workspace" : match.path.includes("node_modules") ? "external" : "workspace"
443
+ },
444
+ className: "text-sm font-medium text-sky-700 dark:text-sky-300",
445
+ children: "Open in explorer"
446
+ })]
447
+ })]
448
+ }),
449
+ /* @__PURE__ */ jsxs("div", {
450
+ className: "mt-4 grid gap-3 lg:grid-cols-2",
451
+ children: [/* @__PURE__ */ jsxs("div", {
452
+ className: "rounded-2xl bg-slate-50/90 px-4 py-3 dark:bg-slate-900/70",
453
+ children: [
454
+ /* @__PURE__ */ jsx("p", {
455
+ className: "text-xs font-semibold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400",
456
+ children: "Introduced through"
457
+ }),
458
+ /* @__PURE__ */ jsx("p", {
459
+ className: "mt-1 text-xs text-slate-500 dark:text-slate-400",
460
+ children: "Workspace files that first cross into this external dependency chain."
461
+ }),
462
+ /* @__PURE__ */ jsx("div", {
463
+ className: "mt-2 flex flex-wrap gap-2 text-sm text-slate-700 dark:text-slate-200",
464
+ children: match.introducedThrough.length > 0 ? match.introducedThrough.slice(0, renderedLimit).map((segment) => /* @__PURE__ */ jsx(TracePathLink, { path: segment }, `${match.path}-introduced-${segment}`)) : /* @__PURE__ */ jsx("span", {
465
+ className: "text-slate-500 dark:text-slate-400",
466
+ children: "No workspace importer path found."
467
+ })
468
+ })
469
+ ]
470
+ }), /* @__PURE__ */ jsxs("div", {
471
+ className: "rounded-2xl bg-slate-50/90 px-4 py-3 dark:bg-slate-900/70",
472
+ children: [
473
+ /* @__PURE__ */ jsx("p", {
474
+ className: "text-xs font-semibold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400",
475
+ children: "Workspace origins"
476
+ }),
477
+ /* @__PURE__ */ jsx("p", {
478
+ className: "mt-1 text-xs text-slate-500 dark:text-slate-400",
479
+ children: "Workspace roots or entrypoint-side files that can eventually reach this result."
480
+ }),
481
+ /* @__PURE__ */ jsx("div", {
482
+ className: "mt-2 flex flex-wrap gap-2 text-sm text-slate-700 dark:text-slate-200",
483
+ children: match.workspaceOrigins.length > 0 ? match.workspaceOrigins.slice(0, renderedLimit).map((segment) => /* @__PURE__ */ jsx(TracePathLink, { path: segment }, `${match.path}-origin-${segment}`)) : /* @__PURE__ */ jsx("span", {
484
+ className: "text-slate-500 dark:text-slate-400",
485
+ children: "No upstream workspace root found."
486
+ })
487
+ })
488
+ ]
489
+ })]
490
+ }),
491
+ /* @__PURE__ */ jsxs("div", {
492
+ className: "mt-4 rounded-2xl bg-slate-50/90 px-4 py-3 dark:bg-slate-900/70",
493
+ children: [
494
+ /* @__PURE__ */ jsx("p", {
495
+ className: "text-xs font-semibold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400",
496
+ children: "Matched files"
497
+ }),
498
+ /* @__PURE__ */ jsx("p", {
499
+ className: "mt-1 text-xs text-slate-500 dark:text-slate-400",
500
+ children: "Concrete matching files inside the package or node search result."
501
+ }),
502
+ /* @__PURE__ */ jsx("div", {
503
+ className: "mt-2 space-y-2 text-xs text-slate-600 dark:text-slate-300",
504
+ children: match.targetPaths.slice(0, renderedLimit).map((targetPath) => /* @__PURE__ */ jsx("div", {
505
+ className: "font-mono",
506
+ children: /* @__PURE__ */ jsx(TracePathLink, {
507
+ path: targetPath,
508
+ className: "hover:text-sky-700 dark:hover:text-sky-300"
509
+ })
510
+ }, `${match.path}-target-${targetPath}`))
511
+ })
512
+ ]
513
+ }),
514
+ /* @__PURE__ */ jsx("div", {
515
+ className: "mt-4 space-y-3",
516
+ children: match.chains.slice(0, renderedLimit).map((chain, index) => /* @__PURE__ */ jsxs("div", {
517
+ className: "rounded-2xl bg-slate-50/90 px-4 py-3 dark:bg-slate-900/70",
518
+ children: [/* @__PURE__ */ jsxs("p", {
519
+ className: "text-xs font-semibold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400",
520
+ children: ["Origin chain ", index + 1]
521
+ }), /* @__PURE__ */ jsx("div", {
522
+ className: "mt-2 flex flex-wrap gap-2 text-sm text-slate-700 dark:text-slate-200",
523
+ children: chain.map((segment, segmentIndex) => /* @__PURE__ */ jsx(TracePathLink, { path: segment }, `${segment}-${segmentIndex}`))
524
+ })]
525
+ }, `${match.path}-${index}`))
526
+ })
527
+ ]
528
+ }, match.path))] })]
529
+ }) : /* @__PURE__ */ jsx("section", {
530
+ className: "rounded-[24px] border border-slate-200/70 bg-white/90 p-8 shadow-[0_16px_50px_-32px_rgba(15,23,42,0.55)] dark:border-slate-800 dark:bg-slate-950/70",
531
+ children: /* @__PURE__ */ jsxs("div", {
532
+ className: "flex items-start gap-4",
533
+ children: [/* @__PURE__ */ jsx("div", {
534
+ className: "rounded-2xl bg-sky-100 p-3 text-sky-700 dark:bg-sky-500/10 dark:text-sky-300",
535
+ children: /* @__PURE__ */ jsx(Search, { className: "size-5" })
536
+ }), /* @__PURE__ */ jsxs("div", { children: [
537
+ /* @__PURE__ */ jsx("h2", {
538
+ className: "text-lg font-semibold text-slate-900 dark:text-slate-100",
539
+ children: "Explain why something is here"
540
+ }),
541
+ /* @__PURE__ */ jsx("p", {
542
+ className: "mt-2 text-sm leading-6 text-slate-500 dark:text-slate-400",
543
+ children: "Enter a package name or a node path to inspect the origin chains captured in the graph snapshot."
544
+ }),
545
+ /* @__PURE__ */ jsxs("p", {
546
+ className: "mt-3 inline-flex items-center gap-2 rounded-full bg-slate-100 px-3 py-1 text-xs font-medium text-slate-700 dark:bg-slate-900 dark:text-slate-200",
547
+ children: [/* @__PURE__ */ jsx(Sparkles, { className: "size-3.5" }), "This uses stored import chains, not a fresh graph walk."]
548
+ })
549
+ ] })]
550
+ })
551
+ })]
552
+ });
553
+ }
554
+ //#endregion
555
+ //#region src/routes/trace.tsx?tsr-split=component
556
+ function TraceRoute() {
557
+ const bundle = useModvizBundle();
558
+ const search = Route$1.useSearch();
559
+ const navigate = Route$1.useNavigate();
560
+ return /* @__PURE__ */ jsx(ModvizLayout, {
561
+ projectTitle: bundle.projectTitle,
562
+ title: "Trace",
563
+ description: "Explain why a dependency or module is present by reading the stored origin chains in the current snapshot.",
564
+ children: isModvizBundleReady(bundle) ? /* @__PURE__ */ jsx(TraceView, {
565
+ bundle,
566
+ search,
567
+ onSearchChange: (patch) => navigate({
568
+ replace: true,
569
+ search: (previous) => ({
570
+ ...previous,
571
+ ...patch
572
+ })
573
+ })
574
+ }) : /* @__PURE__ */ jsx(SetupView, { bundle })
575
+ });
576
+ }
577
+ //#endregion
578
+ export { TraceRoute as component };
@@ -0,0 +1,17 @@
1
+ import { t as parseSearchParam } from "./search-params-BNApPgkX.js";
2
+ import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
3
+ //#region src/routes/treemap.tsx
4
+ var $$splitComponentImporter = () => import("./treemap-CrgWFoCF.js");
5
+ var validateTreemapSearch = (search) => ({
6
+ focus: parseSearchParam.string(search.focus, "root") || "root",
7
+ page: Math.max(0, Math.floor(parseSearchParam.number(search.page, 0))),
8
+ paginate: parseSearchParam.boolean(search.paginate, true),
9
+ selected: parseSearchParam.string(search.selected)
10
+ });
11
+ var Route = createFileRoute("/treemap")({
12
+ ssr: false,
13
+ validateSearch: validateTreemapSearch,
14
+ component: lazyRouteComponent($$splitComponentImporter, "component")
15
+ });
16
+ //#endregion
17
+ export { Route as t };