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,288 @@
|
|
|
1
|
+
import { t as Button } from "./button-Bqnnid5i.js";
|
|
2
|
+
import { t as Input } from "./input-C5r-hBix.js";
|
|
3
|
+
import { m as useModvizBundle } from "./modviz-data-CUyTorv0.js";
|
|
4
|
+
import { t as ModvizLayout } from "./modviz-layout-BAH2ogse.js";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { Check, Copy } from "lucide-react";
|
|
8
|
+
//#region src/routes/configure.tsx?tsr-split=component
|
|
9
|
+
var defaultCommandBuilderState = {
|
|
10
|
+
launchUi: true,
|
|
11
|
+
outputFile: "modviz.json",
|
|
12
|
+
barrelThreshold: 3,
|
|
13
|
+
excludePaths: "",
|
|
14
|
+
enableLlm: false,
|
|
15
|
+
enableAiAnalysis: false,
|
|
16
|
+
historyDir: "",
|
|
17
|
+
includePaths: "",
|
|
18
|
+
ignoreDynamic: false,
|
|
19
|
+
llmModel: "gpt-4.1-mini",
|
|
20
|
+
nodeModules: false,
|
|
21
|
+
packageQuery: "",
|
|
22
|
+
nodeQuery: "",
|
|
23
|
+
snapshotName: ""
|
|
24
|
+
};
|
|
25
|
+
var quoteCliValue = (value) => /\s/.test(value) ? JSON.stringify(value) : value;
|
|
26
|
+
var buildAnalyzeCommand = (entryFile, config) => {
|
|
27
|
+
const parts = [
|
|
28
|
+
"pnpm",
|
|
29
|
+
"exec",
|
|
30
|
+
"modviz",
|
|
31
|
+
"analyze",
|
|
32
|
+
quoteCliValue(entryFile)
|
|
33
|
+
];
|
|
34
|
+
if (config.outputFile && config.outputFile !== defaultCommandBuilderState.outputFile) parts.push(`--output-file=${quoteCliValue(config.outputFile)}`);
|
|
35
|
+
if (config.barrelThreshold !== defaultCommandBuilderState.barrelThreshold) parts.push(`--barrel-threshold=${config.barrelThreshold}`);
|
|
36
|
+
if (config.includePaths.trim()) parts.push(`--include=${quoteCliValue(config.includePaths.trim())}`);
|
|
37
|
+
if (config.excludePaths.trim()) parts.push(`--exclude=${quoteCliValue(config.excludePaths.trim())}`);
|
|
38
|
+
if (config.historyDir.trim()) parts.push(`--history-dir=${quoteCliValue(config.historyDir.trim())}`);
|
|
39
|
+
if (config.launchUi) parts.push("--ui");
|
|
40
|
+
if (config.enableLlm) parts.push("--llm");
|
|
41
|
+
if (config.enableAiAnalysis) parts.push("--llm-analyze");
|
|
42
|
+
if (config.enableAiAnalysis && config.llmModel.trim()) parts.push(`--llm-model=${quoteCliValue(config.llmModel.trim())}`);
|
|
43
|
+
if (config.ignoreDynamic) parts.push("--ignore-dynamic");
|
|
44
|
+
if (config.nodeModules) parts.push("--node-modules");
|
|
45
|
+
if (config.snapshotName.trim()) parts.push(`--snapshot-name=${quoteCliValue(config.snapshotName.trim())}`);
|
|
46
|
+
if (config.packageQuery.trim()) parts.push(`--package=${quoteCliValue(config.packageQuery.trim())}`);
|
|
47
|
+
if (config.nodeQuery.trim()) parts.push(`--node=${quoteCliValue(config.nodeQuery.trim())}`);
|
|
48
|
+
return parts.join(" ");
|
|
49
|
+
};
|
|
50
|
+
function ConfigureRoute() {
|
|
51
|
+
const bundle = useModvizBundle();
|
|
52
|
+
const [config, setConfig] = useState(defaultCommandBuilderState);
|
|
53
|
+
const [didCopy, setDidCopy] = useState(false);
|
|
54
|
+
const updateConfig = (key, value) => {
|
|
55
|
+
setConfig((current) => ({
|
|
56
|
+
...current,
|
|
57
|
+
[key]: value
|
|
58
|
+
}));
|
|
59
|
+
};
|
|
60
|
+
const copyToClipboard = async (text) => {
|
|
61
|
+
await navigator.clipboard.writeText(text);
|
|
62
|
+
setDidCopy(true);
|
|
63
|
+
window.setTimeout(() => setDidCopy(false), 2e3);
|
|
64
|
+
};
|
|
65
|
+
const cmd = useMemo(() => {
|
|
66
|
+
return buildAnalyzeCommand(bundle.graph?.metadata.entrypoints[0] || "./src/index.ts", config);
|
|
67
|
+
}, [bundle.graph?.metadata.entrypoints, config]);
|
|
68
|
+
const summary = bundle.summary;
|
|
69
|
+
return /* @__PURE__ */ jsx(ModvizLayout, {
|
|
70
|
+
projectTitle: bundle.projectTitle,
|
|
71
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
72
|
+
className: "max-w-3xl space-y-6",
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", {
|
|
75
|
+
className: "text-2xl font-bold text-slate-900 dark:text-slate-100",
|
|
76
|
+
children: "Command Configuration"
|
|
77
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
78
|
+
className: "mt-1 text-sm text-slate-600 dark:text-slate-400",
|
|
79
|
+
children: "Configure and run the analyzer from your terminal."
|
|
80
|
+
})] }),
|
|
81
|
+
/* @__PURE__ */ jsxs("section", {
|
|
82
|
+
className: "rounded-[16px] 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",
|
|
83
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
84
|
+
className: "text-sm font-semibold text-slate-900 dark:text-slate-100 mb-4",
|
|
85
|
+
children: "Build Command"
|
|
86
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
87
|
+
className: "space-y-4",
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
90
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
91
|
+
children: "Output File"
|
|
92
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
93
|
+
value: config.outputFile,
|
|
94
|
+
onChange: (e) => updateConfig("outputFile", e.target.value),
|
|
95
|
+
placeholder: "modviz.json",
|
|
96
|
+
className: "mt-1"
|
|
97
|
+
})] }),
|
|
98
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
99
|
+
/* @__PURE__ */ jsx("label", {
|
|
100
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
101
|
+
children: "Barrel threshold"
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ jsx(Input, {
|
|
104
|
+
type: "number",
|
|
105
|
+
min: "1",
|
|
106
|
+
value: String(config.barrelThreshold),
|
|
107
|
+
onChange: (e) => updateConfig("barrelThreshold", Math.max(1, Number(e.target.value) || defaultCommandBuilderState.barrelThreshold)),
|
|
108
|
+
className: "mt-1"
|
|
109
|
+
}),
|
|
110
|
+
/* @__PURE__ */ jsx("p", {
|
|
111
|
+
className: "mt-1 text-xs text-slate-500 dark:text-slate-400",
|
|
112
|
+
children: "Files exporting at least this many symbols are treated as barrel files."
|
|
113
|
+
})
|
|
114
|
+
] }),
|
|
115
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
116
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
117
|
+
children: "Snapshot name (optional)"
|
|
118
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
119
|
+
value: config.snapshotName,
|
|
120
|
+
onChange: (e) => updateConfig("snapshotName", e.target.value),
|
|
121
|
+
placeholder: "before-refactor",
|
|
122
|
+
className: "mt-1"
|
|
123
|
+
})] }),
|
|
124
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
125
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
126
|
+
children: "Include paths (optional)"
|
|
127
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
128
|
+
value: config.includePaths,
|
|
129
|
+
onChange: (e) => updateConfig("includePaths", e.target.value),
|
|
130
|
+
placeholder: "src/routes/**,src/components/**",
|
|
131
|
+
className: "mt-1"
|
|
132
|
+
})] }),
|
|
133
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
134
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
135
|
+
children: "Exclude paths (optional)"
|
|
136
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
137
|
+
value: config.excludePaths,
|
|
138
|
+
onChange: (e) => updateConfig("excludePaths", e.target.value),
|
|
139
|
+
placeholder: "**/*.test.ts,**/*.stories.tsx",
|
|
140
|
+
className: "mt-1"
|
|
141
|
+
})] }),
|
|
142
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
143
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
144
|
+
children: "History dir (optional)"
|
|
145
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
146
|
+
value: config.historyDir,
|
|
147
|
+
onChange: (e) => updateConfig("historyDir", e.target.value),
|
|
148
|
+
placeholder: ".modviz/history",
|
|
149
|
+
className: "mt-1"
|
|
150
|
+
})] }),
|
|
151
|
+
/* @__PURE__ */ jsx("div", {
|
|
152
|
+
className: "space-y-2",
|
|
153
|
+
children: [
|
|
154
|
+
{
|
|
155
|
+
key: "launchUi",
|
|
156
|
+
label: "Launch web UI after analyze (--ui)"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
key: "enableLlm",
|
|
160
|
+
label: "Enable LLM analysis (--llm)"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
key: "enableAiAnalysis",
|
|
164
|
+
label: "Generate AI summary (--llm-analyze)"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
key: "ignoreDynamic",
|
|
168
|
+
label: "Ignore dynamic imports (--ignore-dynamic)"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
key: "nodeModules",
|
|
172
|
+
label: "Include node_modules (--node-modules)"
|
|
173
|
+
}
|
|
174
|
+
].map((field) => /* @__PURE__ */ jsxs("label", {
|
|
175
|
+
className: "flex cursor-pointer items-center gap-2",
|
|
176
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
177
|
+
type: "checkbox",
|
|
178
|
+
checked: config[field.key],
|
|
179
|
+
onChange: (e) => updateConfig(field.key, e.target.checked),
|
|
180
|
+
className: "rounded"
|
|
181
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
182
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
183
|
+
children: field.label
|
|
184
|
+
})]
|
|
185
|
+
}, field.key))
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
188
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
189
|
+
children: "AI model (optional)"
|
|
190
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
191
|
+
value: config.llmModel,
|
|
192
|
+
onChange: (e) => updateConfig("llmModel", e.target.value),
|
|
193
|
+
placeholder: "gpt-4.1-mini",
|
|
194
|
+
className: "mt-1 text-xs",
|
|
195
|
+
disabled: !config.enableAiAnalysis
|
|
196
|
+
})] }),
|
|
197
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
198
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
199
|
+
children: "Filter package (optional)"
|
|
200
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
201
|
+
value: config.packageQuery,
|
|
202
|
+
onChange: (e) => updateConfig("packageQuery", e.target.value),
|
|
203
|
+
placeholder: "e.g., @namespace/package",
|
|
204
|
+
className: "mt-1 text-xs"
|
|
205
|
+
})] }),
|
|
206
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
207
|
+
className: "text-xs font-medium text-slate-700 dark:text-slate-300",
|
|
208
|
+
children: "Filter node (optional)"
|
|
209
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
210
|
+
value: config.nodeQuery,
|
|
211
|
+
onChange: (e) => updateConfig("nodeQuery", e.target.value),
|
|
212
|
+
placeholder: "e.g., src/utils",
|
|
213
|
+
className: "mt-1 text-xs"
|
|
214
|
+
})] }),
|
|
215
|
+
/* @__PURE__ */ jsxs("div", {
|
|
216
|
+
className: "pt-2 border-t border-slate-200 dark:border-slate-800",
|
|
217
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
218
|
+
className: "text-xs text-slate-600 dark:text-slate-400 mb-2",
|
|
219
|
+
children: "Run this command in your terminal:"
|
|
220
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
221
|
+
className: "flex items-center gap-2",
|
|
222
|
+
children: [/* @__PURE__ */ jsx("code", {
|
|
223
|
+
className: "flex-1 min-w-0 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 font-mono text-xs text-slate-800 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 overflow-x-auto",
|
|
224
|
+
children: cmd
|
|
225
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
226
|
+
size: "sm",
|
|
227
|
+
onClick: () => void copyToClipboard(cmd),
|
|
228
|
+
className: "shrink-0 gap-1.5",
|
|
229
|
+
title: "Copy command",
|
|
230
|
+
children: didCopy ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Check, { className: "size-3.5" }), "Copied"] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Copy, { className: "size-3.5" }), "Copy command"] })
|
|
231
|
+
})]
|
|
232
|
+
})]
|
|
233
|
+
})
|
|
234
|
+
]
|
|
235
|
+
})]
|
|
236
|
+
}),
|
|
237
|
+
/* @__PURE__ */ jsxs("section", {
|
|
238
|
+
className: "rounded-[16px] 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",
|
|
239
|
+
children: [
|
|
240
|
+
/* @__PURE__ */ jsx("h2", {
|
|
241
|
+
className: "text-sm font-semibold text-slate-900 dark:text-slate-100 mb-3",
|
|
242
|
+
children: "Current Dataset"
|
|
243
|
+
}),
|
|
244
|
+
/* @__PURE__ */ jsxs("div", {
|
|
245
|
+
className: "grid gap-3 grid-cols-2 sm:grid-cols-4 text-xs",
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
|
|
248
|
+
className: "font-semibold text-slate-500 dark:text-slate-400",
|
|
249
|
+
children: "Nodes"
|
|
250
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
251
|
+
className: "text-lg font-bold text-slate-900 dark:text-slate-100",
|
|
252
|
+
children: (summary?.overview.totalNodes ?? 0).toLocaleString()
|
|
253
|
+
})] }),
|
|
254
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
|
|
255
|
+
className: "font-semibold text-slate-500 dark:text-slate-400",
|
|
256
|
+
children: "Workspace"
|
|
257
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
258
|
+
className: "text-lg font-bold text-slate-900 dark:text-slate-100",
|
|
259
|
+
children: (summary?.overview.workspaceNodes ?? 0).toLocaleString()
|
|
260
|
+
})] }),
|
|
261
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
|
|
262
|
+
className: "font-semibold text-slate-500 dark:text-slate-400",
|
|
263
|
+
children: "Packages"
|
|
264
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
265
|
+
className: "text-lg font-bold text-slate-900 dark:text-slate-100",
|
|
266
|
+
children: (summary?.overview.workspacePackages ?? 0).toLocaleString()
|
|
267
|
+
})] }),
|
|
268
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
|
|
269
|
+
className: "font-semibold text-slate-500 dark:text-slate-400",
|
|
270
|
+
children: "LLM Report"
|
|
271
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
272
|
+
className: "text-lg font-bold text-slate-900 dark:text-slate-100",
|
|
273
|
+
children: summary?.hasLlm ? "✓" : "—"
|
|
274
|
+
})] })
|
|
275
|
+
]
|
|
276
|
+
}),
|
|
277
|
+
bundle.setup.status !== "ready" ? /* @__PURE__ */ jsx("p", {
|
|
278
|
+
className: "mt-4 text-sm text-slate-500 dark:text-slate-400",
|
|
279
|
+
children: bundle.setup.message
|
|
280
|
+
}) : null
|
|
281
|
+
]
|
|
282
|
+
})
|
|
283
|
+
]
|
|
284
|
+
})
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
//#endregion
|
|
288
|
+
export { ConfigureRoute as component };
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { t as Button } from "./button-Bqnnid5i.js";
|
|
2
|
+
import { t as Input } from "./input-C5r-hBix.js";
|
|
3
|
+
import { d as getWorkspacePackageNames, f as isModvizBundleReady, m as useModvizBundle, o as filterNodesByScope, u as getNodeScope } from "./modviz-data-CUyTorv0.js";
|
|
4
|
+
import { t as Route$1 } from "./explorer-CpSb0JTa.js";
|
|
5
|
+
import { t as ModvizLayout } from "./modviz-layout-BAH2ogse.js";
|
|
6
|
+
import { t as ImportDisplay } from "./import-display-BVIOWcsm.js";
|
|
7
|
+
import { t as SetupView } from "./setup-view-DjI49Iqr.js";
|
|
8
|
+
import { useMemo, useState } from "react";
|
|
9
|
+
import { Link } from "@tanstack/react-router";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { ChevronDown, ChevronRight, ExternalLink, FileCode2, Folder, FolderOpen } from "lucide-react";
|
|
12
|
+
//#region src/components/modviz/explorer-view.tsx
|
|
13
|
+
var defaultGraphSearch = {
|
|
14
|
+
adjustSizes: false,
|
|
15
|
+
cluster: "",
|
|
16
|
+
externalGrouping: "package",
|
|
17
|
+
focus: "",
|
|
18
|
+
gravity: 0,
|
|
19
|
+
hideClusterLabels: false,
|
|
20
|
+
iterations: 0,
|
|
21
|
+
linLogMode: false,
|
|
22
|
+
nodeSizeScale: 0,
|
|
23
|
+
outboundAttractionDistribution: true,
|
|
24
|
+
scalingRatio: 0,
|
|
25
|
+
scope: "all",
|
|
26
|
+
strongGravityMode: false
|
|
27
|
+
};
|
|
28
|
+
var defaultImportSearch = {
|
|
29
|
+
exclude: "",
|
|
30
|
+
include: "",
|
|
31
|
+
mode: "contains",
|
|
32
|
+
module: "",
|
|
33
|
+
preset: "",
|
|
34
|
+
scope: "all",
|
|
35
|
+
symbol: ""
|
|
36
|
+
};
|
|
37
|
+
function ExplorerView(props) {
|
|
38
|
+
const { graph } = props.bundle;
|
|
39
|
+
const workspacePackageNames = useMemo(() => getWorkspacePackageNames(graph), [graph]);
|
|
40
|
+
const scopedNodes = useMemo(() => filterNodesByScope(graph.nodes, workspacePackageNames, props.search.scope), [
|
|
41
|
+
graph.nodes,
|
|
42
|
+
props.search.scope,
|
|
43
|
+
workspacePackageNames
|
|
44
|
+
]);
|
|
45
|
+
const tree = useMemo(() => buildFileTree(scopedNodes), [scopedNodes]);
|
|
46
|
+
const selectedNode = useMemo(() => scopedNodes.find((node) => node.path === props.search.selected) ?? scopedNodes[0] ?? null, [props.search.selected, scopedNodes]);
|
|
47
|
+
const [manuallyExpandedPaths, setManuallyExpandedPaths] = useState(() => /* @__PURE__ */ new Set());
|
|
48
|
+
const expandedPaths = useMemo(() => new Set([...manuallyExpandedPaths, ...selectedNode ? getAncestorPaths(selectedNode.path) : []]), [manuallyExpandedPaths, selectedNode]);
|
|
49
|
+
const selectedNodeScope = selectedNode == null ? "workspace" : getNodeScope(selectedNode, workspacePackageNames);
|
|
50
|
+
const normalizedQuery = props.search.q.trim().toLowerCase();
|
|
51
|
+
const visibleTree = useMemo(() => filterTree(tree, normalizedQuery), [normalizedQuery, tree]);
|
|
52
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
53
|
+
className: "flex h-screen flex-col gap-6 lg:flex-row lg:h-auto",
|
|
54
|
+
children: [/* @__PURE__ */ jsxs("section", {
|
|
55
|
+
className: "flex flex-col 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 lg:w-[35%]",
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsx("div", {
|
|
58
|
+
className: "flex flex-wrap gap-2",
|
|
59
|
+
children: [
|
|
60
|
+
["all", "All files"],
|
|
61
|
+
["workspace", "Monorepo"],
|
|
62
|
+
["external", "node_modules"]
|
|
63
|
+
].map(([value, label]) => /* @__PURE__ */ jsx(Button, {
|
|
64
|
+
variant: props.search.scope === value ? "default" : "outline",
|
|
65
|
+
size: "sm",
|
|
66
|
+
onClick: () => props.onSearchChange({
|
|
67
|
+
scope: value,
|
|
68
|
+
selected: ""
|
|
69
|
+
}),
|
|
70
|
+
children: label
|
|
71
|
+
}, value))
|
|
72
|
+
}),
|
|
73
|
+
/* @__PURE__ */ jsxs("div", {
|
|
74
|
+
className: "mt-4 flex gap-3",
|
|
75
|
+
children: [
|
|
76
|
+
/* @__PURE__ */ jsx(Input, {
|
|
77
|
+
placeholder: "Search file or folder path",
|
|
78
|
+
value: props.search.q,
|
|
79
|
+
onChange: (event) => props.onSearchChange({ q: event.currentTarget.value })
|
|
80
|
+
}),
|
|
81
|
+
/* @__PURE__ */ jsx(Button, {
|
|
82
|
+
variant: "outline",
|
|
83
|
+
onClick: () => setManuallyExpandedPaths(/* @__PURE__ */ new Set()),
|
|
84
|
+
children: "Collapse"
|
|
85
|
+
}),
|
|
86
|
+
/* @__PURE__ */ jsx(Button, {
|
|
87
|
+
variant: "outline",
|
|
88
|
+
onClick: () => setManuallyExpandedPaths(new Set(flattenFolderPaths(visibleTree))),
|
|
89
|
+
children: "Expand"
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
}),
|
|
93
|
+
/* @__PURE__ */ jsx("div", {
|
|
94
|
+
className: "mt-4 flex-1 overflow-auto",
|
|
95
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
96
|
+
className: "space-y-1 pr-2",
|
|
97
|
+
children: visibleTree.children.map((child) => /* @__PURE__ */ jsx(FileTreeItem, {
|
|
98
|
+
node: child,
|
|
99
|
+
expandedPaths,
|
|
100
|
+
selectedPath: selectedNode?.path ?? "",
|
|
101
|
+
onToggle: (path) => {
|
|
102
|
+
setManuallyExpandedPaths((previous) => {
|
|
103
|
+
const next = new Set(previous);
|
|
104
|
+
if (next.has(path)) next.delete(path);
|
|
105
|
+
else next.add(path);
|
|
106
|
+
return next;
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
onSelect: (path) => props.onSearchChange({ selected: path })
|
|
110
|
+
}, child.id))
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
}), /* @__PURE__ */ jsx("section", {
|
|
115
|
+
className: "flex flex-col 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 lg:w-[65%] lg:overflow-y-auto",
|
|
116
|
+
children: selectedNode ? /* @__PURE__ */ jsxs("div", {
|
|
117
|
+
className: "space-y-6",
|
|
118
|
+
children: [
|
|
119
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
120
|
+
/* @__PURE__ */ jsx("p", {
|
|
121
|
+
className: "text-xs font-semibold uppercase tracking-[0.22em] text-sky-700 dark:text-sky-300",
|
|
122
|
+
children: "Selected file"
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ jsx("h2", {
|
|
125
|
+
className: "mt-2 break-words text-lg font-semibold text-slate-900 dark:text-slate-100 sm:text-xl",
|
|
126
|
+
children: selectedNode.path
|
|
127
|
+
}),
|
|
128
|
+
/* @__PURE__ */ jsxs("p", {
|
|
129
|
+
className: "mt-2 text-sm text-slate-500 dark:text-slate-400",
|
|
130
|
+
children: [selectedNode.package?.name ? `${selectedNode.package.name} • ` : "", selectedNode.cluster ?? selectedNode.type]
|
|
131
|
+
})
|
|
132
|
+
] }),
|
|
133
|
+
/* @__PURE__ */ jsxs("div", {
|
|
134
|
+
className: "flex flex-wrap gap-2",
|
|
135
|
+
children: [
|
|
136
|
+
/* @__PURE__ */ jsxs(Link, {
|
|
137
|
+
to: "/graph",
|
|
138
|
+
search: {
|
|
139
|
+
...defaultGraphSearch,
|
|
140
|
+
focus: selectedNode.path,
|
|
141
|
+
scope: selectedNodeScope
|
|
142
|
+
},
|
|
143
|
+
className: "inline-flex h-9 items-center gap-2 rounded-md border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 shadow-xs hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-100",
|
|
144
|
+
children: [/* @__PURE__ */ jsx(ExternalLink, { className: "size-4" }), "Open in graph"]
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ jsxs(Link, {
|
|
147
|
+
to: "/imports",
|
|
148
|
+
search: {
|
|
149
|
+
...defaultImportSearch,
|
|
150
|
+
include: selectedNode.path,
|
|
151
|
+
scope: selectedNodeScope
|
|
152
|
+
},
|
|
153
|
+
className: "inline-flex h-9 items-center gap-2 rounded-md border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 shadow-xs hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-100",
|
|
154
|
+
children: [/* @__PURE__ */ jsx(ExternalLink, { className: "size-4" }), "Use as importer filter"]
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ jsxs(Link, {
|
|
157
|
+
to: "/trace",
|
|
158
|
+
search: {
|
|
159
|
+
package: "",
|
|
160
|
+
node: selectedNode.path,
|
|
161
|
+
limit: 10
|
|
162
|
+
},
|
|
163
|
+
className: "inline-flex h-9 items-center gap-2 rounded-md border border-slate-200 bg-white px-4 text-sm font-medium text-slate-700 shadow-xs hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-100",
|
|
164
|
+
children: [/* @__PURE__ */ jsx(ExternalLink, { className: "size-4" }), "Trace origins"]
|
|
165
|
+
})
|
|
166
|
+
]
|
|
167
|
+
}),
|
|
168
|
+
/* @__PURE__ */ jsxs("div", {
|
|
169
|
+
className: "grid gap-4 md:grid-cols-3",
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ jsx(Metric, {
|
|
172
|
+
label: "Direct imports",
|
|
173
|
+
value: selectedNode.imports.length
|
|
174
|
+
}),
|
|
175
|
+
/* @__PURE__ */ jsx(Metric, {
|
|
176
|
+
label: "Imported by",
|
|
177
|
+
value: selectedNode.importedBy.length
|
|
178
|
+
}),
|
|
179
|
+
/* @__PURE__ */ jsx(Metric, {
|
|
180
|
+
label: "Exports",
|
|
181
|
+
value: selectedNode.exports.length
|
|
182
|
+
})
|
|
183
|
+
]
|
|
184
|
+
}),
|
|
185
|
+
/* @__PURE__ */ jsx(CollapsibleSection, {
|
|
186
|
+
title: "Imports",
|
|
187
|
+
description: "Grouped into import statements so scanning looks closer to the source file.",
|
|
188
|
+
children: /* @__PURE__ */ jsx(ImportDisplay, {
|
|
189
|
+
imports: selectedNode.imports,
|
|
190
|
+
emptyMessage: "No direct imports recorded for this file.",
|
|
191
|
+
showViewToggle: true
|
|
192
|
+
})
|
|
193
|
+
}),
|
|
194
|
+
/* @__PURE__ */ jsx(CollapsibleSection, {
|
|
195
|
+
title: "Imported by",
|
|
196
|
+
description: "Direct inbound edges into this file.",
|
|
197
|
+
children: /* @__PURE__ */ jsx(ImportedByCard, {
|
|
198
|
+
items: selectedNode.importedBy,
|
|
199
|
+
onSelect: (path) => props.onSearchChange({ selected: path })
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
]
|
|
203
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
204
|
+
className: "flex min-h-[24rem] items-center justify-center text-sm text-slate-500 dark:text-slate-400",
|
|
205
|
+
children: "No file matched the current explorer scope."
|
|
206
|
+
})
|
|
207
|
+
})]
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function Metric(props) {
|
|
211
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
212
|
+
className: "rounded-[20px] bg-slate-50/90 p-4 dark:bg-slate-900/70",
|
|
213
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
214
|
+
className: "text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400",
|
|
215
|
+
children: props.label
|
|
216
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
217
|
+
className: "mt-2 text-3xl font-semibold text-slate-900 dark:text-slate-100",
|
|
218
|
+
children: props.value
|
|
219
|
+
})]
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function CollapsibleSection(props) {
|
|
223
|
+
return /* @__PURE__ */ jsxs("details", {
|
|
224
|
+
open: true,
|
|
225
|
+
className: "group rounded-[24px] border border-slate-200/70 bg-slate-50/80 p-4 dark:border-slate-800 dark:bg-slate-900/70",
|
|
226
|
+
children: [/* @__PURE__ */ jsxs("summary", {
|
|
227
|
+
className: "flex cursor-pointer list-none items-center gap-3 text-left",
|
|
228
|
+
children: [/* @__PURE__ */ jsx(ChevronDown, { className: "size-5 shrink-0 -rotate-90 transition-transform group-open:rotate-0" }), /* @__PURE__ */ jsxs("div", {
|
|
229
|
+
className: "flex-1",
|
|
230
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
231
|
+
className: "text-base font-semibold text-slate-900 dark:text-slate-100",
|
|
232
|
+
children: props.title
|
|
233
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
234
|
+
className: "mt-1 text-sm text-slate-500 dark:text-slate-400",
|
|
235
|
+
children: props.description
|
|
236
|
+
})]
|
|
237
|
+
})]
|
|
238
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
239
|
+
className: "mt-4",
|
|
240
|
+
children: props.children
|
|
241
|
+
})]
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function ImportedByCard(props) {
|
|
245
|
+
return /* @__PURE__ */ jsx("div", {
|
|
246
|
+
className: "max-h-[28rem] space-y-2 overflow-auto pr-1",
|
|
247
|
+
children: props.items.length ? props.items.map((item) => /* @__PURE__ */ jsxs("button", {
|
|
248
|
+
type: "button",
|
|
249
|
+
onClick: () => props.onSelect(item),
|
|
250
|
+
className: "flex w-full items-center gap-2 rounded-2xl bg-white px-3 py-3 text-left text-sm text-slate-700 hover:bg-sky-50 hover:text-sky-800 dark:bg-slate-950/80 dark:text-slate-200 dark:hover:bg-sky-500/10 dark:hover:text-sky-200",
|
|
251
|
+
children: [/* @__PURE__ */ jsx(FileCode2, { className: "size-4 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
252
|
+
className: "truncate",
|
|
253
|
+
children: item
|
|
254
|
+
})]
|
|
255
|
+
}, item)) : /* @__PURE__ */ jsx("p", {
|
|
256
|
+
className: "rounded-2xl bg-white px-4 py-6 text-sm text-slate-500 dark:bg-slate-950/80 dark:text-slate-400",
|
|
257
|
+
children: "No inbound importers recorded for this file."
|
|
258
|
+
})
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
function FileTreeItem(props) {
|
|
262
|
+
const depth = props.depth ?? 0;
|
|
263
|
+
const isExpanded = props.expandedPaths.has(props.node.path);
|
|
264
|
+
if (props.node.kind === "file") return /* @__PURE__ */ jsxs("button", {
|
|
265
|
+
type: "button",
|
|
266
|
+
onClick: () => props.onSelect(props.node.path),
|
|
267
|
+
className: `flex w-full items-center gap-2 rounded-xl px-3 py-2 text-left text-sm ${props.selectedPath === props.node.path ? "bg-sky-100 text-sky-900 dark:bg-sky-500/20 dark:text-sky-100" : "text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-900"}`,
|
|
268
|
+
style: { paddingLeft: `${depth * 16 + 12}px` },
|
|
269
|
+
children: [/* @__PURE__ */ jsx(FileCode2, { className: "size-4 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
270
|
+
className: "truncate",
|
|
271
|
+
children: props.node.label
|
|
272
|
+
})]
|
|
273
|
+
});
|
|
274
|
+
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("button", {
|
|
275
|
+
type: "button",
|
|
276
|
+
onClick: () => props.onToggle(props.node.path),
|
|
277
|
+
className: "flex w-full items-center gap-2 rounded-xl px-3 py-2 text-left text-sm text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-900",
|
|
278
|
+
style: { paddingLeft: `${depth * 16 + 12}px` },
|
|
279
|
+
children: [
|
|
280
|
+
isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" }),
|
|
281
|
+
isExpanded ? /* @__PURE__ */ jsx(FolderOpen, { className: "size-4" }) : /* @__PURE__ */ jsx(Folder, { className: "size-4" }),
|
|
282
|
+
/* @__PURE__ */ jsx("span", {
|
|
283
|
+
className: "truncate",
|
|
284
|
+
children: props.node.label
|
|
285
|
+
})
|
|
286
|
+
]
|
|
287
|
+
}), isExpanded ? /* @__PURE__ */ jsx("div", {
|
|
288
|
+
className: "space-y-1",
|
|
289
|
+
children: props.node.children.map((child) => /* @__PURE__ */ jsx(FileTreeItem, {
|
|
290
|
+
node: child,
|
|
291
|
+
expandedPaths: props.expandedPaths,
|
|
292
|
+
selectedPath: props.selectedPath,
|
|
293
|
+
onToggle: props.onToggle,
|
|
294
|
+
onSelect: props.onSelect,
|
|
295
|
+
depth: depth + 1
|
|
296
|
+
}, child.id))
|
|
297
|
+
}) : null] });
|
|
298
|
+
}
|
|
299
|
+
function buildFileTree(nodes) {
|
|
300
|
+
const root = {
|
|
301
|
+
id: "root",
|
|
302
|
+
label: "root",
|
|
303
|
+
path: "",
|
|
304
|
+
kind: "folder",
|
|
305
|
+
children: []
|
|
306
|
+
};
|
|
307
|
+
for (const node of nodes) {
|
|
308
|
+
const segments = node.path.split("/").filter(Boolean);
|
|
309
|
+
let cursor = root;
|
|
310
|
+
let currentPath = "";
|
|
311
|
+
segments.forEach((segment, index) => {
|
|
312
|
+
currentPath = currentPath ? `${currentPath}/${segment}` : segment;
|
|
313
|
+
const isFile = index === segments.length - 1;
|
|
314
|
+
let child = cursor.children.find((existing) => existing.label === segment && existing.kind === (isFile ? "file" : "folder"));
|
|
315
|
+
if (!child) {
|
|
316
|
+
child = {
|
|
317
|
+
id: currentPath,
|
|
318
|
+
label: segment,
|
|
319
|
+
path: currentPath,
|
|
320
|
+
kind: isFile ? "file" : "folder",
|
|
321
|
+
children: [],
|
|
322
|
+
file: isFile ? node : void 0
|
|
323
|
+
};
|
|
324
|
+
cursor.children.push(child);
|
|
325
|
+
cursor.children.sort((left, right) => {
|
|
326
|
+
if (left.kind !== right.kind) return left.kind === "folder" ? -1 : 1;
|
|
327
|
+
return left.label.localeCompare(right.label);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
cursor = child;
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
return root;
|
|
334
|
+
}
|
|
335
|
+
function filterTree(node, query) {
|
|
336
|
+
if (!query) return node;
|
|
337
|
+
if (node.kind === "file") return node.path.toLowerCase().includes(query) ? node : {
|
|
338
|
+
...node,
|
|
339
|
+
children: []
|
|
340
|
+
};
|
|
341
|
+
const children = node.children.map((child) => filterTree(child, query)).filter((child) => child.kind === "file" ? child.path.toLowerCase().includes(query) : child.children.length || child.path.toLowerCase().includes(query));
|
|
342
|
+
return {
|
|
343
|
+
...node,
|
|
344
|
+
children
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function flattenFolderPaths(node) {
|
|
348
|
+
if (node.kind === "file") return [];
|
|
349
|
+
return [node.path, ...node.children.flatMap(flattenFolderPaths)];
|
|
350
|
+
}
|
|
351
|
+
function getAncestorPaths(path) {
|
|
352
|
+
const segments = path.split("/").filter(Boolean);
|
|
353
|
+
return segments.map((_, index) => segments.slice(0, index + 1).join("/"));
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/routes/explorer.tsx?tsr-split=component
|
|
357
|
+
function ExplorerRoute() {
|
|
358
|
+
const bundle = useModvizBundle();
|
|
359
|
+
const search = Route$1.useSearch();
|
|
360
|
+
const navigate = Route$1.useNavigate();
|
|
361
|
+
return /* @__PURE__ */ jsx(ModvizLayout, {
|
|
362
|
+
projectTitle: bundle.projectTitle,
|
|
363
|
+
title: "File Explorer",
|
|
364
|
+
description: "Browse the monorepo or node_modules tree, select any file, then inspect its direct imports and importers without opening the force-directed graph.",
|
|
365
|
+
children: isModvizBundleReady(bundle) ? /* @__PURE__ */ jsx(ExplorerView, {
|
|
366
|
+
bundle,
|
|
367
|
+
search,
|
|
368
|
+
onSearchChange: (patch) => navigate({
|
|
369
|
+
replace: true,
|
|
370
|
+
search: (previous) => ({
|
|
371
|
+
...previous,
|
|
372
|
+
...patch
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
}) : /* @__PURE__ */ jsx(SetupView, { bundle })
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
//#endregion
|
|
379
|
+
export { ExplorerRoute as component };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/routes/explorer.tsx
|
|
4
|
+
var $$splitComponentImporter = () => import("./explorer-C7dODpSv.js");
|
|
5
|
+
var explorerSearchSchema = z.object({
|
|
6
|
+
q: z.string().catch(""),
|
|
7
|
+
selected: z.string().catch(""),
|
|
8
|
+
scope: z.enum([
|
|
9
|
+
"all",
|
|
10
|
+
"workspace",
|
|
11
|
+
"external"
|
|
12
|
+
]).catch("workspace")
|
|
13
|
+
});
|
|
14
|
+
var Route = createFileRoute("/explorer")({
|
|
15
|
+
ssr: false,
|
|
16
|
+
validateSearch: (search) => explorerSearchSchema.parse(search),
|
|
17
|
+
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { Route as t };
|