react-dev-panel 0.1.0

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/dist/adapters/next.cjs +95 -0
  4. package/dist/adapters/next.cjs.map +1 -0
  5. package/dist/adapters/next.d.cts +17 -0
  6. package/dist/adapters/next.d.ts +17 -0
  7. package/dist/adapters/next.js +65 -0
  8. package/dist/adapters/next.js.map +1 -0
  9. package/dist/adapters/server.cjs +72 -0
  10. package/dist/adapters/server.cjs.map +1 -0
  11. package/dist/adapters/server.d.cts +24 -0
  12. package/dist/adapters/server.d.ts +24 -0
  13. package/dist/adapters/server.js +4 -0
  14. package/dist/adapters/server.js.map +1 -0
  15. package/dist/adapters/vite.cjs +301 -0
  16. package/dist/adapters/vite.cjs.map +1 -0
  17. package/dist/adapters/vite.d.cts +35 -0
  18. package/dist/adapters/vite.d.ts +35 -0
  19. package/dist/adapters/vite.js +31 -0
  20. package/dist/adapters/vite.js.map +1 -0
  21. package/dist/chunk-2ZAPVMUL.js +25 -0
  22. package/dist/chunk-2ZAPVMUL.js.map +1 -0
  23. package/dist/chunk-MAYMGQIM.js +64 -0
  24. package/dist/chunk-MAYMGQIM.js.map +1 -0
  25. package/dist/chunk-XZ4DPO52.js +190 -0
  26. package/dist/chunk-XZ4DPO52.js.map +1 -0
  27. package/dist/cli/index.cjs +221 -0
  28. package/dist/cli/index.cjs.map +1 -0
  29. package/dist/cli/index.d.cts +1 -0
  30. package/dist/cli/index.d.ts +1 -0
  31. package/dist/cli/index.js +35 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/generate-UK65K5BU.js +3 -0
  34. package/dist/generate-UK65K5BU.js.map +1 -0
  35. package/dist/index.cjs +1745 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +53 -0
  38. package/dist/index.d.ts +53 -0
  39. package/dist/index.js +1711 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/server-open-B4FK0jQF.d.cts +92 -0
  42. package/dist/server-open-B4FK0jQF.d.ts +92 -0
  43. package/package.json +77 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,1745 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var webVitals = require('web-vitals');
6
+
7
+ // src/tools/dev-logs/index.tsx
8
+
9
+ // src/core/styles.ts
10
+ var RDP_STYLE_ID = "react-dev-panel-styles";
11
+ var RDP_Z = 2147483e3;
12
+ var CSS = `
13
+ .rdp-root, .rdp-root * { box-sizing: border-box; }
14
+ .rdp-root {
15
+ --rdp-bg: #11161d;
16
+ --rdp-bg-elev: #1a212b;
17
+ --rdp-bg-soft: rgba(148,163,184,0.06);
18
+ --rdp-border: rgba(255,255,255,0.09);
19
+ --rdp-text: #e6e9ee;
20
+ --rdp-text-dim: #9aa4b2;
21
+ --rdp-text-faint: #6b7585;
22
+ --rdp-accent: #6950E8;
23
+ --rdp-accent-contrast: #ffffff;
24
+ --rdp-success: #3ddc84;
25
+ --rdp-warning: #e0a82e;
26
+ --rdp-error: #e5575c;
27
+ --rdp-info: #56a6e8;
28
+ --rdp-radius: 10px;
29
+ --rdp-shadow: 0 8px 28px rgba(0,0,0,0.5);
30
+ --rdp-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
31
+ --rdp-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
32
+ color: var(--rdp-text);
33
+ font-family: var(--rdp-sans);
34
+ font-size: 13px;
35
+ line-height: 1.4;
36
+ }
37
+
38
+ /* Launcher FAB */
39
+ .rdp-fab {
40
+ position: fixed; width: 52px; height: 52px; border-radius: 50%;
41
+ display: grid; place-items: center; cursor: grab; border: none;
42
+ color: var(--rdp-accent-contrast);
43
+ background: linear-gradient(135deg, var(--rdp-accent), #4a36b8);
44
+ box-shadow: 0 4px 12px rgba(105,80,232,0.35), 0 8px 24px rgba(0,0,0,0.5);
45
+ z-index: ${RDP_Z};
46
+ }
47
+ .rdp-fab:active { cursor: grabbing; }
48
+ .rdp-fab-badge {
49
+ position: absolute; bottom: -3px; right: -3px; min-width: 18px; height: 18px;
50
+ padding: 0 5px; border-radius: 999px; background: var(--rdp-error); color: #fff;
51
+ font-size: 10px; font-weight: 700; display: grid; place-items: center;
52
+ border: 2px solid var(--rdp-bg);
53
+ }
54
+
55
+ /* Floating surfaces */
56
+ .rdp-surface {
57
+ position: fixed; background: var(--rdp-bg); border: 1px solid var(--rdp-border);
58
+ border-radius: var(--rdp-radius); box-shadow: var(--rdp-shadow); overflow: hidden;
59
+ display: flex; flex-direction: column; z-index: ${RDP_Z};
60
+ }
61
+ .rdp-menu { width: 320px; }
62
+ .rdp-panel { width: min(460px, calc(100vw - 32px)); max-height: min(78vh, 720px); }
63
+
64
+ .rdp-header {
65
+ display: flex; align-items: center; gap: 10px; padding: 10px 14px;
66
+ border-bottom: 1px solid var(--rdp-border); flex-shrink: 0;
67
+ }
68
+ .rdp-menu-head {
69
+ background: linear-gradient(135deg, rgba(105,80,232,0.18), rgba(105,80,232,0.04));
70
+ }
71
+ .rdp-title { font-weight: 700; font-size: 13px; }
72
+ .rdp-sub { color: var(--rdp-text-dim); font-size: 12px; }
73
+ .rdp-body { flex: 1; overflow-y: auto; padding: 12px; }
74
+
75
+ /* Menu rows */
76
+ .rdp-row {
77
+ display: flex; align-items: flex-start; gap: 12px; padding: 10px 12px;
78
+ border-radius: 8px; cursor: pointer; width: 100%; text-align: left; border: none;
79
+ background: transparent; color: inherit; font: inherit;
80
+ }
81
+ .rdp-row:hover { background: rgba(255,255,255,0.05); }
82
+ .rdp-tile {
83
+ width: 38px; height: 38px; flex-shrink: 0; border-radius: 9px;
84
+ display: grid; place-items: center;
85
+ }
86
+
87
+ /* Primitives */
88
+ .rdp-chip {
89
+ display: inline-flex; align-items: center; height: 18px; padding: 0 7px;
90
+ border-radius: 999px; font-size: 11px; font-weight: 700; line-height: 1;
91
+ }
92
+ .rdp-btn {
93
+ display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px;
94
+ border-radius: 8px; border: 1px solid var(--rdp-border); background: transparent;
95
+ color: var(--rdp-text); font: inherit; font-size: 12px; cursor: pointer;
96
+ }
97
+ .rdp-btn:hover { background: rgba(255,255,255,0.05); }
98
+ .rdp-btn-primary {
99
+ background: var(--rdp-accent); color: var(--rdp-accent-contrast); border-color: transparent;
100
+ font-weight: 600; justify-content: center; width: 100%;
101
+ }
102
+ .rdp-btn-primary:hover { filter: brightness(1.08); }
103
+ .rdp-btn-sm { padding: 2px 7px; font-size: 11px; border-radius: 6px; }
104
+ .rdp-iconbtn {
105
+ display: grid; place-items: center; width: 28px; height: 28px; border-radius: 7px;
106
+ border: 1px solid var(--rdp-border); background: transparent; color: var(--rdp-text);
107
+ cursor: pointer;
108
+ }
109
+ .rdp-iconbtn:hover { background: rgba(255,255,255,0.06); }
110
+ .rdp-iconbtn-bare { border: none; width: 22px; height: 22px; color: var(--rdp-text-dim); }
111
+ .rdp-iconbtn-bare:hover { color: var(--rdp-text); background: rgba(255,255,255,0.06); }
112
+
113
+ .rdp-tabs { display: flex; gap: 4px; background: var(--rdp-bg-soft); padding: 3px; border-radius: 9px; }
114
+ .rdp-tab {
115
+ flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px;
116
+ padding: 6px 4px; border-radius: 7px; border: none; background: transparent;
117
+ color: var(--rdp-text-dim); font: inherit; font-size: 12px; cursor: pointer;
118
+ }
119
+ .rdp-tab[aria-selected="true"] { background: var(--rdp-bg-elev); color: var(--rdp-text); box-shadow: 0 1px 2px rgba(0,0,0,0.3); }
120
+
121
+ .rdp-input {
122
+ display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 9px;
123
+ border: 1px solid var(--rdp-border); background: var(--rdp-bg-soft);
124
+ }
125
+ .rdp-input input {
126
+ flex: 1; background: transparent; border: none; outline: none; color: var(--rdp-text);
127
+ font: inherit; font-size: 13px;
128
+ }
129
+ .rdp-mono { font-family: var(--rdp-mono); }
130
+ .rdp-section-label {
131
+ display: block; margin: 8px 0 2px; color: var(--rdp-text-faint); font-weight: 700;
132
+ text-transform: uppercase; letter-spacing: 0.5px; font-size: 10px;
133
+ }
134
+
135
+ /* Inspector overlay bits */
136
+ .rdp-overlay { position: fixed; inset: 0; pointer-events: none; z-index: ${RDP_Z - 1}; }
137
+ .rdp-hl {
138
+ position: fixed; border: 1px solid var(--rdp-accent); border-radius: 3px;
139
+ background: rgba(105,80,232,0.14); box-shadow: 0 0 0 1px rgba(105,80,232,0.4);
140
+ transition: all 60ms linear; pointer-events: none;
141
+ }
142
+ .rdp-tooltip {
143
+ position: fixed; max-width: 360px; padding: 8px 10px; border-radius: 9px;
144
+ background: rgba(15,18,24,0.86); backdrop-filter: blur(8px);
145
+ border: 1px solid rgba(255,255,255,0.12); box-shadow: var(--rdp-shadow); color: #fff;
146
+ pointer-events: none;
147
+ }
148
+ .rdp-toast {
149
+ position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
150
+ padding: 7px 14px; border-radius: 9px; background: rgba(15,18,24,0.92);
151
+ border: 1px solid rgba(255,255,255,0.12); box-shadow: var(--rdp-shadow);
152
+ font-size: 12px; font-weight: 600; z-index: ${RDP_Z};
153
+ }
154
+
155
+ .rdp-body::-webkit-scrollbar, .rdp-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
156
+ .rdp-body::-webkit-scrollbar-thumb, .rdp-scroll::-webkit-scrollbar-thumb {
157
+ background: rgba(255,255,255,0.14); border-radius: 8px;
158
+ }
159
+ .rdp-kbd {
160
+ padding: 1px 5px; border-radius: 5px; border: 1px solid var(--rdp-border);
161
+ background: rgba(255,255,255,0.04); font-family: var(--rdp-mono); font-size: 11px;
162
+ color: var(--rdp-text-dim);
163
+ }
164
+ `;
165
+ var injected = false;
166
+ function injectBaseStyles() {
167
+ if (injected || typeof document === "undefined") return;
168
+ if (document.getElementById(RDP_STYLE_ID)) {
169
+ injected = true;
170
+ return;
171
+ }
172
+ const style = document.createElement("style");
173
+ style.id = RDP_STYLE_ID;
174
+ style.textContent = CSS;
175
+ document.head.appendChild(style);
176
+ injected = true;
177
+ }
178
+ function colorVar(key) {
179
+ switch (key) {
180
+ case "info":
181
+ return "var(--rdp-info)";
182
+ case "warning":
183
+ return "var(--rdp-warning)";
184
+ case "success":
185
+ return "var(--rdp-success)";
186
+ case "error":
187
+ return "var(--rdp-error)";
188
+ default:
189
+ return "var(--rdp-accent)";
190
+ }
191
+ }
192
+ function cx(...parts) {
193
+ return parts.filter(Boolean).join(" ");
194
+ }
195
+
196
+ // src/core/registry.ts
197
+ var tools = /* @__PURE__ */ new Map();
198
+ function registerTool(def) {
199
+ tools.set(def.id, def);
200
+ }
201
+ function getRegisteredTools() {
202
+ return [...tools.values()];
203
+ }
204
+ function resolveTools(ids) {
205
+ if (!ids || ids.length === 0) return getRegisteredTools();
206
+ return ids.map((id) => tools.get(id)).filter((t) => Boolean(t));
207
+ }
208
+ function Svg({ children, size = 16 }) {
209
+ return /* @__PURE__ */ jsxRuntime.jsx(
210
+ "svg",
211
+ {
212
+ width: size,
213
+ height: size,
214
+ viewBox: "0 0 24 24",
215
+ fill: "none",
216
+ stroke: "currentColor",
217
+ strokeWidth: 2,
218
+ strokeLinecap: "round",
219
+ strokeLinejoin: "round",
220
+ "aria-hidden": "true",
221
+ children
222
+ }
223
+ );
224
+ }
225
+ var IconWrench = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Svg, { size, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14.7 6.3a4 4 0 0 0-5.4 5.3L3 18v3h3l6.4-6.3a4 4 0 0 0 5.3-5.4l-2.5 2.5-2.1-2.1z" }) });
226
+ var IconBug = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Svg, { size, children: [
227
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "8", y: "6", width: "8", height: "14", rx: "4" }),
228
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 6a3 3 0 0 1 6 0M3 13h5M16 13h5M4 18l4-2M20 18l-4-2M4 8l4 2M20 8l-4 2" })
229
+ ] });
230
+ var IconGauge = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Svg, { size, children: [
231
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 14l3-3" }),
232
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.5 18a9 9 0 1 1 17 0" })
233
+ ] });
234
+ var IconGraph = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Svg, { size, children: [
235
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1.5" }),
236
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1.5" }),
237
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 6.5h4a3 3 0 0 1 3 3V14" })
238
+ ] });
239
+ var IconX = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Svg, { size, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 6l12 12M18 6L6 18" }) });
240
+ var IconChevronRight = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Svg, { size, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 6l6 6-6 6" }) });
241
+ var IconChevronDown = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Svg, { size, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 9l6 6 6-6" }) });
242
+ var IconSearch = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Svg, { size, children: [
243
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "7" }),
244
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 21l-4-4" })
245
+ ] });
246
+ var IconCopy = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Svg, { size, children: [
247
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
248
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15V5a2 2 0 0 1 2-2h10" })
249
+ ] });
250
+ var IconFileCode = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Svg, { size, children: [
251
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" }),
252
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 3v5h5M10 12l-2 2 2 2M14 12l2 2-2 2" })
253
+ ] });
254
+ var IconRefresh = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs(Svg, { size, children: [
255
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-3-6.7L21 8" }),
256
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 3v5h-5" })
257
+ ] });
258
+ var IconPointer = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Svg, { size, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 3l16 7-7 2-2 7z" }) });
259
+ var IconLayers = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Svg, { size, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5" }) });
260
+ var IconArrowUp = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx(Svg, { size, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 19V5M6 11l6-6 6 6" }) });
261
+ var buffer = [];
262
+ var nextId = 1;
263
+ var listeners = /* @__PURE__ */ new Set();
264
+ function emit() {
265
+ listeners.forEach((l) => l());
266
+ }
267
+ function push(level, message, meta) {
268
+ buffer = [...buffer.slice(-499), { id: nextId++, level, time: Date.now(), message, meta }];
269
+ emit();
270
+ }
271
+ function stringify(args) {
272
+ return args.map((a) => {
273
+ if (typeof a === "string") return a;
274
+ try {
275
+ return JSON.stringify(a);
276
+ } catch {
277
+ return String(a);
278
+ }
279
+ }).join(" ").slice(0, 2e3);
280
+ }
281
+ var installed = false;
282
+ function installCapture() {
283
+ if (installed || typeof window === "undefined") return;
284
+ installed = true;
285
+ ["log", "info", "warn", "error"].forEach((level) => {
286
+ const original = console[level].bind(console);
287
+ console[level] = (...args) => {
288
+ push(level, stringify(args));
289
+ original(...args);
290
+ };
291
+ });
292
+ if (typeof window.fetch === "function") {
293
+ const orig = window.fetch.bind(window);
294
+ window.fetch = async (input, init) => {
295
+ const method = (init?.method ?? "GET").toUpperCase();
296
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
297
+ const start = Date.now();
298
+ try {
299
+ const res = await orig(input, init);
300
+ push(res.ok ? "network" : "error", `${method} ${url}`, `${res.status} \xB7 ${Date.now() - start}ms`);
301
+ return res;
302
+ } catch (err) {
303
+ push("error", `${method} ${url}`, `failed \xB7 ${String(err)}`);
304
+ throw err;
305
+ }
306
+ };
307
+ }
308
+ }
309
+ function subscribeLogs(listener) {
310
+ listeners.add(listener);
311
+ return () => listeners.delete(listener);
312
+ }
313
+ function getLogs() {
314
+ return buffer;
315
+ }
316
+ var EMPTY = [];
317
+ function getServerLogs() {
318
+ return EMPTY;
319
+ }
320
+ function clearLogs() {
321
+ buffer = [];
322
+ emit();
323
+ }
324
+ var LEVELS = ["all", "log", "warn", "error", "network"];
325
+ var LEVEL_COLOR = {
326
+ log: "var(--rdp-text-dim)",
327
+ info: "var(--rdp-info)",
328
+ warn: "var(--rdp-warning)",
329
+ error: "var(--rdp-error)",
330
+ network: "var(--rdp-info)"
331
+ };
332
+ function DevLogsPanel({ onClose }) {
333
+ const logs = react.useSyncExternalStore(subscribeLogs, getLogs, getServerLogs);
334
+ const [filter, setFilter] = react.useState("all");
335
+ const shown = filter === "all" ? logs : logs.filter((l) => l.level === filter);
336
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-surface rdp-panel", style: { bottom: 88, right: 20 }, children: [
337
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-header", children: [
338
+ /* @__PURE__ */ jsxRuntime.jsx(IconBug, { size: 16 }),
339
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-title", style: { flex: 1 }, children: "Developer Logs" }),
340
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: clearLogs, title: "Clear", style: { width: "auto", padding: "0 8px" }, children: "Clear" }),
341
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { size: 16 }) })
342
+ ] }),
343
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px 12px 0", display: "flex", gap: 4, flexWrap: "wrap" }, children: LEVELS.map((lvl) => /* @__PURE__ */ jsxRuntime.jsx(
344
+ "button",
345
+ {
346
+ type: "button",
347
+ className: cx("rdp-btn", "rdp-btn-sm"),
348
+ style: filter === lvl ? { borderColor: "var(--rdp-accent)", color: "var(--rdp-accent)" } : void 0,
349
+ onClick: () => setFilter(lvl),
350
+ children: lvl
351
+ },
352
+ lvl
353
+ )) }),
354
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rdp-body rdp-mono", style: { fontSize: 12 }, children: shown.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--rdp-text-faint)" }, children: "No log entries captured yet." }) : shown.slice().reverse().map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "3px 0", borderBottom: "1px solid var(--rdp-border)" }, children: [
355
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: LEVEL_COLOR[entry.level], fontWeight: 700, marginRight: 6 }, children: entry.level === "network" ? "net" : entry.level }),
356
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { wordBreak: "break-word" }, children: entry.message }),
357
+ entry.meta && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--rdp-text-faint)", marginLeft: 6 }, children: entry.meta })
358
+ ] }, entry.id)) })
359
+ ] });
360
+ }
361
+ function useBadge() {
362
+ const logs = react.useSyncExternalStore(subscribeLogs, getLogs, getServerLogs);
363
+ const errors = logs.filter((l) => l.level === "error").length;
364
+ if (logs.length === 0) return null;
365
+ return errors > 0 ? { label: String(errors), tone: "error" } : { label: String(logs.length), tone: "neutral" };
366
+ }
367
+ var devLogsTool = {
368
+ id: "logs",
369
+ title: "Developer Logs",
370
+ subtitle: "Client & network activity",
371
+ color: "info",
372
+ icon: /* @__PURE__ */ jsxRuntime.jsx(IconBug, { size: 19 }),
373
+ Panel: DevLogsPanel,
374
+ useBadge,
375
+ init: installCapture
376
+ };
377
+ function registerDevLogs() {
378
+ registerTool(devLogsTool);
379
+ }
380
+ var HINTS = {
381
+ LCP: "Largest Contentful Paint \u2014 optimize hero image/font, reduce server time.",
382
+ CLS: "Cumulative Layout Shift \u2014 set sizes on images/embeds, reserve space.",
383
+ INP: "Interaction to Next Paint \u2014 break up long tasks, defer work.",
384
+ FCP: "First Contentful Paint \u2014 reduce render-blocking CSS/JS.",
385
+ TTFB: "Time to First Byte \u2014 server/edge latency, caching."
386
+ };
387
+ var metrics = {};
388
+ var listeners2 = /* @__PURE__ */ new Set();
389
+ function emit2() {
390
+ listeners2.forEach((l) => l());
391
+ }
392
+ function record(m) {
393
+ metrics = {
394
+ ...metrics,
395
+ [m.name]: {
396
+ name: m.name,
397
+ value: m.value,
398
+ rating: m.rating,
399
+ unit: m.name === "CLS" ? "" : "ms"
400
+ }
401
+ };
402
+ emit2();
403
+ }
404
+ var installed2 = false;
405
+ function installPerf() {
406
+ if (installed2 || typeof window === "undefined") return;
407
+ installed2 = true;
408
+ webVitals.onLCP(record);
409
+ webVitals.onCLS(record);
410
+ webVitals.onINP(record);
411
+ webVitals.onFCP(record);
412
+ webVitals.onTTFB(record);
413
+ }
414
+ function subscribe(l) {
415
+ listeners2.add(l);
416
+ return () => listeners2.delete(l);
417
+ }
418
+ function snapshot() {
419
+ return metrics;
420
+ }
421
+ var EMPTY2 = {};
422
+ function serverSnapshot() {
423
+ return EMPTY2;
424
+ }
425
+ var RATING_COLOR = {
426
+ good: "var(--rdp-success)",
427
+ "needs-improvement": "var(--rdp-warning)",
428
+ poor: "var(--rdp-error)"
429
+ };
430
+ function PagePerformancePanel({ onClose }) {
431
+ const data = react.useSyncExternalStore(subscribe, snapshot, serverSnapshot);
432
+ const order = ["LCP", "INP", "CLS", "FCP", "TTFB"];
433
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-surface rdp-panel", style: { bottom: 88, right: 20 }, children: [
434
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-header", children: [
435
+ /* @__PURE__ */ jsxRuntime.jsx(IconGauge, { size: 16 }),
436
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-title", style: { flex: 1 }, children: "Page Performance" }),
437
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { size: 16 }) })
438
+ ] }),
439
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-body", children: [
440
+ order.every((k) => !data[k]) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--rdp-text-faint)" }, children: "Collecting Web Vitals\u2026 interact with the page (INP needs an interaction)." }),
441
+ order.map((key) => {
442
+ const m = data[key];
443
+ if (!m) return null;
444
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "8px 0", borderBottom: "1px solid var(--rdp-border)" }, children: [
445
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
446
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-chip", style: { background: RATING_COLOR[m.rating], color: "#06210f" }, children: m.name }),
447
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rdp-mono", style: { fontWeight: 700 }, children: [
448
+ m.unit === "ms" ? Math.round(m.value) : m.value.toFixed(3),
449
+ m.unit
450
+ ] }),
451
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: RATING_COLOR[m.rating], fontSize: 11, marginLeft: "auto" }, children: m.rating })
452
+ ] }),
453
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rdp-sub", style: { marginTop: 2 }, children: HINTS[key] })
454
+ ] }, key);
455
+ })
456
+ ] })
457
+ ] });
458
+ }
459
+ var pagePerformanceTool = {
460
+ id: "perf",
461
+ title: "Page Performance",
462
+ subtitle: "Web Vitals & fix suggestions",
463
+ color: "warning",
464
+ icon: /* @__PURE__ */ jsxRuntime.jsx(IconGauge, { size: 19 }),
465
+ Panel: PagePerformancePanel,
466
+ init: installPerf
467
+ };
468
+ function registerPagePerformance() {
469
+ registerTool(pagePerformanceTool);
470
+ }
471
+ function buildProtocolUrl(editor, loc) {
472
+ const line = loc.line ?? 1;
473
+ const col = loc.column ?? 1;
474
+ switch (editor) {
475
+ case "cursor":
476
+ return `cursor://file/${loc.file}:${line}:${col}`;
477
+ case "webstorm":
478
+ return `webstorm://open?file=${encodeURIComponent(loc.file)}&line=${line}&column=${col}`;
479
+ case "zed":
480
+ return `zed://file/${loc.file}:${line}:${col}`;
481
+ case "vscode":
482
+ case "auto":
483
+ default:
484
+ return `vscode://file/${loc.file}:${line}:${col}`;
485
+ }
486
+ }
487
+ var defaultOpenInEditor = async (loc, editor = "auto") => {
488
+ const url = buildProtocolUrl(editor, loc);
489
+ if (url && typeof window !== "undefined") {
490
+ try {
491
+ window.location.assign(url);
492
+ return true;
493
+ } catch {
494
+ }
495
+ }
496
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
497
+ await navigator.clipboard.writeText(`${loc.file}${loc.line ? `:${loc.line}` : ""}`).catch(() => void 0);
498
+ }
499
+ return false;
500
+ };
501
+ var DevPanelContext = react.createContext(null);
502
+ function resolve(config) {
503
+ const enabled = config.enabled ?? (typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true);
504
+ return {
505
+ enabled,
506
+ editor: config.editor ?? "auto",
507
+ getRoute: config.getRoute ?? (() => typeof location !== "undefined" ? location.pathname : void 0),
508
+ openInEditor: config.openInEditor ?? defaultOpenInEditor,
509
+ graphEndpoint: config.graphEndpoint,
510
+ theme: config.theme ?? {},
511
+ tools: config.tools
512
+ };
513
+ }
514
+ function DevPanelConfigProvider({
515
+ config,
516
+ children
517
+ }) {
518
+ const value = react.useMemo(() => resolve(config), [config]);
519
+ return /* @__PURE__ */ jsxRuntime.jsx(DevPanelContext.Provider, { value, children });
520
+ }
521
+ function useDevPanelConfig() {
522
+ const ctx = react.useContext(DevPanelContext);
523
+ if (!ctx) throw new Error("useDevPanelConfig must be used within <DevPanel> / DevPanelConfigProvider");
524
+ return ctx;
525
+ }
526
+ function GraphSearch({
527
+ value,
528
+ onChange,
529
+ placeholder = "Search components\u2026"
530
+ }) {
531
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-input", children: [
532
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--rdp-text-faint)", display: "grid", placeItems: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(IconSearch, { size: 15 }) }),
533
+ /* @__PURE__ */ jsxRuntime.jsx("input", { value, onChange: (e) => onChange(e.target.value), placeholder, "aria-label": "Search components" }),
534
+ value && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: () => onChange(""), "aria-label": "Clear", children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { size: 14 }) })
535
+ ] });
536
+ }
537
+ function Chips({ names, onSelect }) {
538
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "flex", flexWrap: "wrap", gap: 4 }, children: names.map((n) => /* @__PURE__ */ jsxRuntime.jsx(
539
+ "button",
540
+ {
541
+ type: "button",
542
+ className: "rdp-chip rdp-mono",
543
+ style: { border: "1px solid var(--rdp-border)", background: "transparent", color: "var(--rdp-text)", cursor: "pointer" },
544
+ onClick: () => onSelect(n),
545
+ children: n
546
+ },
547
+ n
548
+ )) });
549
+ }
550
+ function Row({ label, children }) {
551
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, marginTop: 5 }, children: [
552
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { minWidth: 52, color: "var(--rdp-text-faint)", fontWeight: 600, flexShrink: 0, fontSize: 11 }, children: label }),
553
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { minWidth: 0, flex: 1 }, children })
554
+ ] });
555
+ }
556
+ function NodeDetails({
557
+ selected,
558
+ onOpen,
559
+ onCopyInfo,
560
+ onCopyPath,
561
+ onSelectName
562
+ }) {
563
+ if (!selected) {
564
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--rdp-text-faint)", padding: "6px 2px" }, children: "Nothing selected. Enable inspect mode and click a component, or pick one from the tree." });
565
+ }
566
+ return /* @__PURE__ */ jsxRuntime.jsxs(
567
+ "div",
568
+ {
569
+ style: {
570
+ padding: 10,
571
+ borderRadius: 9,
572
+ border: "1px solid var(--rdp-border)",
573
+ background: "var(--rdp-bg-soft)"
574
+ },
575
+ children: [
576
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
577
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-chip", style: { background: "rgba(105,80,232,0.22)", color: "var(--rdp-accent)" }, children: selected.componentName }),
578
+ selected.domTag && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-mono", style: { color: "var(--rdp-text-faint)", fontSize: 11 }, children: `<${selected.domTag}>` })
579
+ ] }),
580
+ selected.filePath ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-mono", style: { marginTop: 6, fontSize: 11.5, color: "var(--rdp-text-dim)", wordBreak: "break-all" }, children: [
581
+ selected.filePath,
582
+ selected.line ? `:${selected.line}` : "",
583
+ selected.line && selected.column ? `:${selected.column}` : ""
584
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 6, fontSize: 11.5, color: "var(--rdp-warning)" }, children: "No source path \u2014 run the graph generator (npx dev-panel-graph) or mount an adapter." }),
585
+ selected.route && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Route", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-mono", style: { fontSize: 12 }, children: selected.route }) }),
586
+ selected.parent && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Parent", children: /* @__PURE__ */ jsxRuntime.jsx(Chips, { names: [selected.parent], onSelect: onSelectName }) }),
587
+ selected.children.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Renders", children: /* @__PURE__ */ jsxRuntime.jsx(Chips, { names: selected.children, onSelect: onSelectName }) }),
588
+ selected.imports.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Imports", children: /* @__PURE__ */ jsxRuntime.jsx(Chips, { names: selected.imports, onSelect: onSelectName }) }),
589
+ selected.props && selected.props.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Props", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-mono", style: { fontSize: 11, color: "var(--rdp-text-dim)" }, children: selected.props.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { marginRight: 8, display: "inline-block" }, children: [
590
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--rdp-accent)" }, children: p.name }),
591
+ "=",
592
+ p.value
593
+ ] }, p.name)) }) }),
594
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: cx("rdp-btn", "rdp-btn-primary"), style: { marginTop: 12 }, onClick: onOpen, children: [
595
+ /* @__PURE__ */ jsxRuntime.jsx(IconFileCode, { size: 14 }),
596
+ " Open in editor"
597
+ ] }),
598
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, marginTop: 8 }, children: [
599
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn", onClick: onCopyInfo, title: "Copy component info", children: /* @__PURE__ */ jsxRuntime.jsx(IconCopy, { size: 15 }) }),
600
+ /* @__PURE__ */ jsxRuntime.jsx(
601
+ "button",
602
+ {
603
+ type: "button",
604
+ className: "rdp-iconbtn",
605
+ onClick: onCopyPath,
606
+ disabled: !selected.filePath && !selected.absFilePath,
607
+ title: "Copy file path",
608
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconFileCode, { size: 15 })
609
+ }
610
+ )
611
+ ] })
612
+ ]
613
+ }
614
+ );
615
+ }
616
+
617
+ // src/tools/component-graph/graph-utils.ts
618
+ var indexCache = /* @__PURE__ */ new WeakMap();
619
+ function pushUnique(map, k, v) {
620
+ const a = map.get(k);
621
+ if (a) {
622
+ if (!a.includes(v)) a.push(v);
623
+ } else map.set(k, [v]);
624
+ }
625
+ function getIndex(graph) {
626
+ if (!graph) return null;
627
+ let idx = indexCache.get(graph);
628
+ if (idx) return idx;
629
+ idx = { byName: /* @__PURE__ */ new Map(), childrenOf: /* @__PURE__ */ new Map(), parentsOf: /* @__PURE__ */ new Map(), importsOf: /* @__PURE__ */ new Map() };
630
+ for (const n of graph.nodes) {
631
+ idx.byName.set(n.id, n);
632
+ idx.byName.set(n.name, n);
633
+ }
634
+ for (const e of graph.edges) {
635
+ if (e.type === "renders") {
636
+ pushUnique(idx.childrenOf, e.from, e.to);
637
+ pushUnique(idx.parentsOf, e.to, e.from);
638
+ } else if (e.type === "route") {
639
+ pushUnique(idx.parentsOf, e.to, e.from);
640
+ } else if (e.type === "imports") {
641
+ pushUnique(idx.importsOf, e.from, e.to);
642
+ }
643
+ }
644
+ indexCache.set(graph, idx);
645
+ return idx;
646
+ }
647
+ function findNode(graph, name) {
648
+ return getIndex(graph)?.byName.get(name) ?? null;
649
+ }
650
+ function getParents(graph, id) {
651
+ return getIndex(graph)?.parentsOf.get(id) ?? [];
652
+ }
653
+ function getChildren(graph, id) {
654
+ return getIndex(graph)?.childrenOf.get(id) ?? [];
655
+ }
656
+ function getImports(graph, id) {
657
+ return getIndex(graph)?.importsOf.get(id) ?? [];
658
+ }
659
+ function searchNodes(graph, query, limit = 40) {
660
+ if (!graph) return [];
661
+ const q = query.trim().toLowerCase();
662
+ const matches = q ? graph.nodes.filter((n) => n.name.toLowerCase().includes(q) || n.filePath.toLowerCase().includes(q)) : graph.nodes.filter((n) => n.type === "component");
663
+ matches.sort((a, b) => a.type !== b.type ? a.type === "component" ? -1 : 1 : a.name.localeCompare(b.name));
664
+ return matches.slice(0, limit);
665
+ }
666
+ function isAbs(p) {
667
+ return p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p);
668
+ }
669
+ function toDisplayPath(graph, file) {
670
+ const root = graph?.root;
671
+ if (root && file.startsWith(root)) return file.slice(root.length).replace(/^[/\\]/, "");
672
+ return file;
673
+ }
674
+ function toAbsPath(graph, file) {
675
+ if (isAbs(file)) return file;
676
+ return graph?.root ? `${graph.root}/${file}` : void 0;
677
+ }
678
+ function getFiber(node) {
679
+ const key = Object.keys(node).find(
680
+ (k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$")
681
+ );
682
+ return key ? node[key] ?? null : null;
683
+ }
684
+ function getDisplayName(type) {
685
+ if (!type || typeof type === "string") return null;
686
+ if (typeof type === "function") {
687
+ const fn = type;
688
+ return fn.displayName || fn.name || null;
689
+ }
690
+ const o = type;
691
+ if (o.displayName) return o.displayName;
692
+ if (o.render) return getDisplayName(o.render);
693
+ if (o.type) return getDisplayName(o.type);
694
+ return null;
695
+ }
696
+ function chain(el) {
697
+ const out = [];
698
+ try {
699
+ let f = getFiber(el);
700
+ let hops = 0;
701
+ while (f && hops < 200) {
702
+ const n = getDisplayName(f.type);
703
+ if (n && out[out.length - 1] !== n) out.push(n);
704
+ f = f.return;
705
+ hops += 1;
706
+ }
707
+ } catch {
708
+ }
709
+ return out;
710
+ }
711
+ var IGNORE_ATTR = "data-rdp-ignore";
712
+ function isIgnored(el) {
713
+ return !!el?.closest(`[${IGNORE_ATTR}]`);
714
+ }
715
+ function summarize(v) {
716
+ if (v == null) return String(v);
717
+ switch (typeof v) {
718
+ case "string":
719
+ return v.length > 40 ? `"${v.slice(0, 40)}\u2026"` : `"${v}"`;
720
+ case "number":
721
+ case "boolean":
722
+ return String(v);
723
+ case "function":
724
+ return "\u0192";
725
+ case "object":
726
+ if (Array.isArray(v)) return `Array(${v.length})`;
727
+ if (v.$$typeof) return null;
728
+ return "{\u2026}";
729
+ default:
730
+ return null;
731
+ }
732
+ }
733
+ function extractProps(el) {
734
+ try {
735
+ let f = getFiber(el);
736
+ while (f && typeof f.type === "string") f = f.return;
737
+ const props = f?.memoizedProps;
738
+ if (!props || typeof props !== "object") return void 0;
739
+ const out = [];
740
+ for (const name of Object.keys(props)) {
741
+ if (name === "children") continue;
742
+ const s = summarize(props[name]);
743
+ if (s == null) continue;
744
+ out.push({ name, value: s });
745
+ if (out.length >= 12) break;
746
+ }
747
+ return out.length ? out : void 0;
748
+ } catch {
749
+ return void 0;
750
+ }
751
+ }
752
+ function extractRaw(el) {
753
+ const host = el.closest("[data-dev-file], [data-dev-component]");
754
+ const attrFile = host?.getAttribute("data-dev-file") ?? void 0;
755
+ const attrLine = host?.getAttribute("data-dev-line");
756
+ const attrCol = host?.getAttribute("data-dev-column");
757
+ let fiberName = null;
758
+ try {
759
+ fiberName = chain(el)[0] ?? null;
760
+ } catch {
761
+ }
762
+ const domTag = el.tagName.toLowerCase();
763
+ return {
764
+ componentName: fiberName || host?.getAttribute("data-dev-component") || domTag,
765
+ filePath: attrFile,
766
+ line: attrLine ? Number(attrLine) : void 0,
767
+ column: attrCol ? Number(attrCol) : void 0,
768
+ domTag
769
+ };
770
+ }
771
+ function graphMatch(el, graph, fallbackName) {
772
+ const idx = getIndex(graph);
773
+ if (idx) {
774
+ const name = chain(el).find((n) => idx.byName.has(n));
775
+ if (name) return { componentName: name, node: idx.byName.get(name) ?? null };
776
+ }
777
+ return { componentName: fallbackName, node: findNode(graph, fallbackName) };
778
+ }
779
+ function resolveDisplay(el, graph, route) {
780
+ const raw = extractRaw(el);
781
+ const { componentName, node } = graphMatch(el, graph, raw.componentName);
782
+ return {
783
+ componentName,
784
+ filePath: raw.filePath ?? node?.filePath,
785
+ line: raw.line ?? node?.line,
786
+ domTag: raw.domTag,
787
+ route
788
+ };
789
+ }
790
+ function resolveSelected(el, graph, route) {
791
+ const raw = extractRaw(el);
792
+ const { componentName, node } = graphMatch(el, graph, raw.componentName);
793
+ const rel = node?.filePath;
794
+ const absFromAttr = raw.filePath && isAbs(raw.filePath) ? raw.filePath : void 0;
795
+ const absFilePath = absFromAttr ?? (rel ? toAbsPath(graph, rel) : void 0);
796
+ const display = rel ?? (raw.filePath ? toDisplayPath(graph, raw.filePath) : void 0);
797
+ const parents = node ? getParents(graph, node.id) : [];
798
+ return {
799
+ componentName,
800
+ filePath: display,
801
+ absFilePath,
802
+ line: raw.line ?? node?.line,
803
+ column: raw.column ?? node?.column,
804
+ route: route ?? node?.route,
805
+ domTag: raw.domTag,
806
+ parent: parents[0],
807
+ parents,
808
+ children: node ? getChildren(graph, node.id) : [],
809
+ imports: node ? getImports(graph, node.id) : [],
810
+ props: extractProps(el),
811
+ source: "hover",
812
+ nodeId: node?.id
813
+ };
814
+ }
815
+ function selectFromNode(graph, node) {
816
+ return {
817
+ componentName: node.name,
818
+ filePath: node.filePath,
819
+ absFilePath: toAbsPath(graph, node.filePath),
820
+ line: node.line,
821
+ column: node.column,
822
+ route: node.route,
823
+ parent: getParents(graph, node.id)[0],
824
+ parents: getParents(graph, node.id),
825
+ children: getChildren(graph, node.id),
826
+ imports: getImports(graph, node.id),
827
+ source: "graph",
828
+ nodeId: node.id
829
+ };
830
+ }
831
+ function collectMounted(graph) {
832
+ const idx = getIndex(graph);
833
+ if (!idx || typeof document === "undefined") return [];
834
+ try {
835
+ let root = null;
836
+ const all = document.body.getElementsByTagName("*");
837
+ for (let i = 0; i < all.length; i += 1) {
838
+ let f = getFiber(all[i]);
839
+ if (f) {
840
+ let g = 0;
841
+ while (f.return && g < 1e5) {
842
+ f = f.return;
843
+ g += 1;
844
+ }
845
+ root = f;
846
+ break;
847
+ }
848
+ }
849
+ if (!root) return [];
850
+ const counts = /* @__PURE__ */ new Map();
851
+ const stack = [root];
852
+ let visited = 0;
853
+ while (stack.length && visited < 2e5) {
854
+ const cur = stack.pop();
855
+ if (!cur) continue;
856
+ visited += 1;
857
+ const name = getDisplayName(cur.type);
858
+ if (name) {
859
+ const node = idx.byName.get(name);
860
+ if (node && node.type === "component") counts.set(name, (counts.get(name) ?? 0) + 1);
861
+ }
862
+ if (cur.child) stack.push(cur.child);
863
+ if (cur.sibling) stack.push(cur.sibling);
864
+ }
865
+ return [...counts.entries()].map(([name, count]) => ({ name, node: idx.byName.get(name), count })).sort((a, b) => a.name.localeCompare(b.name));
866
+ } catch {
867
+ return [];
868
+ }
869
+ }
870
+ function formatForCopy(s) {
871
+ const lines = [`Component: ${s.componentName}`];
872
+ if (s.filePath) lines.push(`File: ${s.filePath}${s.line ? `:${s.line}` : ""}`);
873
+ if (s.route) lines.push(`Route: ${s.route}`);
874
+ if (s.parent) lines.push(`Parent: ${s.parent}`);
875
+ if (s.children.length) lines.push(`Children: ${s.children.join(", ")}`);
876
+ if (s.imports.length) lines.push(`Imports: ${s.imports.join(", ")}`);
877
+ return lines.join("\n");
878
+ }
879
+ var MAX_DEPTH = 6;
880
+ function Label({ children }) {
881
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-section-label", children });
882
+ }
883
+ function RowActions({
884
+ node,
885
+ onOpen,
886
+ onCopy
887
+ }) {
888
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", gap: 2 }, children: [
889
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Open in editor", onClick: (e) => {
890
+ e.stopPropagation();
891
+ onOpen(node);
892
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(IconFileCode, { size: 13 }) }),
893
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Copy file path", onClick: (e) => {
894
+ e.stopPropagation();
895
+ onCopy(node);
896
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(IconCopy, { size: 13 }) })
897
+ ] });
898
+ }
899
+ function RowShell({
900
+ depth = 0,
901
+ active,
902
+ caret,
903
+ label,
904
+ isRoute,
905
+ onLabel,
906
+ actions
907
+ }) {
908
+ return /* @__PURE__ */ jsxRuntime.jsxs(
909
+ "div",
910
+ {
911
+ className: "rdp-row",
912
+ style: {
913
+ padding: "3px 6px",
914
+ paddingLeft: 6 + depth * 14,
915
+ gap: 4,
916
+ alignItems: "center",
917
+ background: active ? "rgba(105,80,232,0.16)" : void 0
918
+ },
919
+ children: [
920
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 16, display: "grid", placeItems: "center", color: "var(--rdp-text-faint)" }, children: caret }),
921
+ /* @__PURE__ */ jsxRuntime.jsx(
922
+ "button",
923
+ {
924
+ type: "button",
925
+ onClick: onLabel,
926
+ className: "rdp-mono",
927
+ style: {
928
+ flex: 1,
929
+ minWidth: 0,
930
+ textAlign: "left",
931
+ background: "none",
932
+ border: "none",
933
+ cursor: "pointer",
934
+ fontSize: 12,
935
+ fontWeight: active ? 700 : 500,
936
+ color: isRoute ? "var(--rdp-info)" : active ? "var(--rdp-accent)" : "var(--rdp-text)",
937
+ whiteSpace: "nowrap",
938
+ overflow: "hidden",
939
+ textOverflow: "ellipsis"
940
+ },
941
+ children: label
942
+ }
943
+ ),
944
+ actions
945
+ ]
946
+ }
947
+ );
948
+ }
949
+ function Branch({ name, depth, trail, p }) {
950
+ const node = findNode(p.graph, name);
951
+ const id = node?.id ?? name;
952
+ const kids = node ? getChildren(p.graph, id) : [];
953
+ const hasKids = kids.length > 0 && depth < MAX_DEPTH && !trail.has(id);
954
+ const open = p.expanded.has(id);
955
+ const active = p.selected?.nodeId === id || p.selected?.componentName === name;
956
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
957
+ /* @__PURE__ */ jsxRuntime.jsx(
958
+ RowShell,
959
+ {
960
+ depth,
961
+ active,
962
+ isRoute: node?.type === "route",
963
+ caret: hasKids ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { cursor: "pointer" }, onClick: () => p.onToggle(id), children: open ? /* @__PURE__ */ jsxRuntime.jsx(IconChevronDown, { size: 13 }) : /* @__PURE__ */ jsxRuntime.jsx(IconChevronRight, { size: 13 }) }) : null,
964
+ label: name,
965
+ onLabel: () => node && p.onSelect(node),
966
+ actions: node ? /* @__PURE__ */ jsxRuntime.jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy }) : null
967
+ }
968
+ ),
969
+ hasKids && open && kids.map((k) => /* @__PURE__ */ jsxRuntime.jsx(Branch, { name: k, depth: depth + 1, trail: /* @__PURE__ */ new Set([...trail, id]), p }, `${id}>${k}`))
970
+ ] });
971
+ }
972
+ function ComponentGraphTree(p) {
973
+ const { graph, selected, search } = p;
974
+ if (graph && search.trim()) {
975
+ const results = searchNodes(graph, search);
976
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
977
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
978
+ results.length,
979
+ " match",
980
+ results.length === 1 ? "" : "es"
981
+ ] }),
982
+ results.map((node) => /* @__PURE__ */ jsxRuntime.jsx(
983
+ RowShell,
984
+ {
985
+ active: selected?.nodeId === node.id,
986
+ isRoute: node.type === "route",
987
+ label: node.name,
988
+ onLabel: () => p.onSelect(node),
989
+ actions: /* @__PURE__ */ jsxRuntime.jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy })
990
+ },
991
+ node.id
992
+ )),
993
+ results.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 8 }, children: [
994
+ "No components match \u201C",
995
+ search,
996
+ "\u201D."
997
+ ] })
998
+ ] });
999
+ }
1000
+ if (!selected) {
1001
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--rdp-text-faint)", padding: "8px 2px" }, children: "Lock a component (hover + click) or search above to explore the tree." });
1002
+ }
1003
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1004
+ selected.parents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1005
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Parent chain" }),
1006
+ selected.parents.map((parent) => {
1007
+ const node = findNode(graph, parent);
1008
+ return /* @__PURE__ */ jsxRuntime.jsx(
1009
+ RowShell,
1010
+ {
1011
+ isRoute: node?.type === "route",
1012
+ caret: /* @__PURE__ */ jsxRuntime.jsx(IconArrowUp, { size: 12 }),
1013
+ label: parent,
1014
+ onLabel: () => node && p.onSelect(node),
1015
+ actions: node ? /* @__PURE__ */ jsxRuntime.jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy }) : null
1016
+ },
1017
+ `p:${parent}`
1018
+ );
1019
+ })
1020
+ ] }),
1021
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Selected" }),
1022
+ /* @__PURE__ */ jsxRuntime.jsx(RowShell, { active: true, label: selected.componentName, onLabel: () => void 0 }),
1023
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1024
+ "Renders (",
1025
+ selected.children.length,
1026
+ ")"
1027
+ ] }),
1028
+ selected.children.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 20 }, children: "No child components detected." }) : selected.children.map((c) => /* @__PURE__ */ jsxRuntime.jsx(Branch, { name: c, depth: 0, trail: new Set(selected.nodeId ? [selected.nodeId] : []), p }, `c:${c}`)),
1029
+ selected.imports.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1030
+ /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1031
+ "Imports (",
1032
+ selected.imports.length,
1033
+ ")"
1034
+ ] }),
1035
+ selected.imports.map((imp) => {
1036
+ const node = findNode(graph, imp);
1037
+ return /* @__PURE__ */ jsxRuntime.jsx(
1038
+ RowShell,
1039
+ {
1040
+ isRoute: node?.type === "route",
1041
+ label: imp,
1042
+ onLabel: () => node && p.onSelect(node),
1043
+ actions: node ? /* @__PURE__ */ jsxRuntime.jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy }) : null
1044
+ },
1045
+ `i:${imp}`
1046
+ );
1047
+ })
1048
+ ] })
1049
+ ] });
1050
+ }
1051
+ function ComponentGraphPageList({
1052
+ graph,
1053
+ route,
1054
+ selectedName,
1055
+ onSelect,
1056
+ onOpen,
1057
+ onCopy
1058
+ }) {
1059
+ const [items, setItems] = react.useState([]);
1060
+ const [query, setQuery] = react.useState("");
1061
+ const scan = react.useCallback(() => setItems(collectMounted(graph)), [graph]);
1062
+ react.useEffect(() => {
1063
+ let raf2 = 0;
1064
+ const raf1 = requestAnimationFrame(() => {
1065
+ raf2 = requestAnimationFrame(scan);
1066
+ });
1067
+ const late = setTimeout(scan, 600);
1068
+ return () => {
1069
+ cancelAnimationFrame(raf1);
1070
+ cancelAnimationFrame(raf2);
1071
+ clearTimeout(late);
1072
+ };
1073
+ }, [scan, route]);
1074
+ if (!graph) {
1075
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--rdp-text-faint)", padding: "6px 2px" }, children: "Load the component graph (Graph tab) to map mounted components to source files." });
1076
+ }
1077
+ const q = query.trim().toLowerCase();
1078
+ const filtered = q ? items.filter((i) => i.name.toLowerCase().includes(q) || i.node.filePath.toLowerCase().includes(q)) : items;
1079
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1080
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 6 }, children: [
1081
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rdp-sub", children: [
1082
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-mono", style: { color: "var(--rdp-info)" }, children: route ?? "/" }),
1083
+ " \xB7",
1084
+ " ",
1085
+ q ? `${filtered.length} / ${items.length}` : items.length,
1086
+ " component",
1087
+ items.length === 1 ? "" : "s"
1088
+ ] }),
1089
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "rdp-btn rdp-btn-sm", onClick: scan, children: [
1090
+ /* @__PURE__ */ jsxRuntime.jsx(IconRefresh, { size: 13 }),
1091
+ " Rescan"
1092
+ ] })
1093
+ ] }),
1094
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: 8 }, children: /* @__PURE__ */ jsxRuntime.jsx(GraphSearch, { value: query, onChange: setQuery, placeholder: "Filter components on this page\u2026" }) }),
1095
+ items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 4 }, children: "No graph components detected in the live tree. Try Rescan, or regenerate the graph." }) : filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 4 }, children: [
1096
+ "No components match \u201C",
1097
+ query,
1098
+ "\u201D."
1099
+ ] }) : filtered.map(({ name, node, count }) => /* @__PURE__ */ jsxRuntime.jsxs(
1100
+ "div",
1101
+ {
1102
+ className: "rdp-row",
1103
+ style: { padding: "4px 6px", alignItems: "center", background: selectedName === name ? "rgba(105,80,232,0.16)" : void 0 },
1104
+ onClick: () => onSelect(node),
1105
+ children: [
1106
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [
1107
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1108
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-mono", style: { fontWeight: 600, fontSize: 12, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: name }),
1109
+ count > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rdp-chip", style: { background: "var(--rdp-bg-soft)", color: "var(--rdp-text-dim)", height: 15 }, children: [
1110
+ "\xD7",
1111
+ count
1112
+ ] })
1113
+ ] }),
1114
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", color: "var(--rdp-text-faint)", fontSize: 11, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: node.filePath })
1115
+ ] }),
1116
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", gap: 2 }, children: [
1117
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Open in editor", onClick: (e) => {
1118
+ e.stopPropagation();
1119
+ onOpen(node);
1120
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(IconFileCode, { size: 13 }) }),
1121
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Copy file path", onClick: (e) => {
1122
+ e.stopPropagation();
1123
+ onCopy(node);
1124
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(IconCopy, { size: 13 }) })
1125
+ ] })
1126
+ ]
1127
+ },
1128
+ node.id
1129
+ ))
1130
+ ] });
1131
+ }
1132
+
1133
+ // src/tools/component-graph/store.ts
1134
+ var state = {
1135
+ enabled: false,
1136
+ mode: "hover",
1137
+ selected: null,
1138
+ graph: null,
1139
+ status: "idle",
1140
+ search: "",
1141
+ expanded: /* @__PURE__ */ new Set(),
1142
+ toast: null
1143
+ };
1144
+ var listeners3 = /* @__PURE__ */ new Set();
1145
+ var scheduled = false;
1146
+ var toastTimer = null;
1147
+ function emit3() {
1148
+ if (scheduled) return;
1149
+ scheduled = true;
1150
+ const flush = () => {
1151
+ scheduled = false;
1152
+ listeners3.forEach((l) => l());
1153
+ };
1154
+ if (typeof queueMicrotask === "function") queueMicrotask(flush);
1155
+ else void Promise.resolve().then(flush);
1156
+ }
1157
+ function set(patch) {
1158
+ state = { ...state, ...patch };
1159
+ emit3();
1160
+ }
1161
+ function subscribeGraph(l) {
1162
+ listeners3.add(l);
1163
+ return () => listeners3.delete(l);
1164
+ }
1165
+ function getGraphState() {
1166
+ return state;
1167
+ }
1168
+ var SERVER = { ...state };
1169
+ function getGraphServerState() {
1170
+ return SERVER;
1171
+ }
1172
+ function disableInspector() {
1173
+ if (state.enabled) set({ enabled: false });
1174
+ }
1175
+ function toggleInspector(endpoint) {
1176
+ set({ enabled: !state.enabled });
1177
+ if (state.enabled) void loadGraph(endpoint);
1178
+ }
1179
+ function setMode(mode) {
1180
+ if (state.mode !== mode) set({ mode });
1181
+ }
1182
+ function setSelected(selected) {
1183
+ set({ selected });
1184
+ }
1185
+ function setSearch(search) {
1186
+ set({ search });
1187
+ }
1188
+ function toggleExpanded(id) {
1189
+ const expanded = new Set(state.expanded);
1190
+ if (expanded.has(id)) expanded.delete(id);
1191
+ else expanded.add(id);
1192
+ set({ expanded });
1193
+ }
1194
+ function showToast(toast) {
1195
+ set({ toast });
1196
+ if (toastTimer) clearTimeout(toastTimer);
1197
+ toastTimer = setTimeout(() => {
1198
+ toastTimer = null;
1199
+ set({ toast: null });
1200
+ }, 2600);
1201
+ }
1202
+ var graphPromise = null;
1203
+ function loadGraph(endpoint, force = false) {
1204
+ if (!endpoint) {
1205
+ set({ status: "empty" });
1206
+ return Promise.resolve();
1207
+ }
1208
+ if (!force && (state.status === "ready" || state.status === "loading")) {
1209
+ return graphPromise ?? Promise.resolve();
1210
+ }
1211
+ set({ status: "loading" });
1212
+ graphPromise = (async () => {
1213
+ try {
1214
+ const res = await fetch(endpoint, { headers: { Accept: "application/json" } });
1215
+ if (res.status === 404) return set({ graph: null, status: "empty" });
1216
+ if (!res.ok) return set({ status: "error" });
1217
+ const graph = await res.json();
1218
+ set({ graph, status: graph.nodes?.length ? "ready" : "empty" });
1219
+ } catch {
1220
+ set({ status: "error" });
1221
+ }
1222
+ })();
1223
+ return graphPromise;
1224
+ }
1225
+ var TABS = [
1226
+ { value: "hover", label: "Hover", icon: /* @__PURE__ */ jsxRuntime.jsx(IconPointer, { size: 14 }) },
1227
+ { value: "graph", label: "Graph", icon: /* @__PURE__ */ jsxRuntime.jsx(IconGraph, { size: 14 }) },
1228
+ { value: "page", label: "Page", icon: /* @__PURE__ */ jsxRuntime.jsx(IconLayers, { size: 14 }) },
1229
+ { value: "file", label: "File", icon: /* @__PURE__ */ jsxRuntime.jsx(IconFileCode, { size: 14 }) }
1230
+ ];
1231
+ async function copy(text) {
1232
+ if (typeof navigator === "undefined" || !navigator.clipboard) return false;
1233
+ return navigator.clipboard.writeText(text).then(
1234
+ () => true,
1235
+ () => false
1236
+ );
1237
+ }
1238
+ function ComponentGraphPanel({ onClose }) {
1239
+ const state2 = react.useSyncExternalStore(subscribeGraph, getGraphState, getGraphServerState);
1240
+ const config = useDevPanelConfig();
1241
+ const route = config.getRoute();
1242
+ const { enabled, mode, selected, graph, status, search, expanded } = state2;
1243
+ const openLoc = react.useCallback(
1244
+ async (sel) => {
1245
+ const file = sel?.absFilePath ?? sel?.filePath;
1246
+ if (!file) {
1247
+ showToast({ message: "No source path available", tone: "error" });
1248
+ return;
1249
+ }
1250
+ const ok = await config.openInEditor?.({ file, line: sel?.line, column: sel?.column }, config.editor);
1251
+ showToast(ok === false ? { message: "Editor unavailable \u2014 path copied", tone: "info" } : { message: "Opening in your editor\u2026", tone: "success" });
1252
+ },
1253
+ [config]
1254
+ );
1255
+ const copyInfo = react.useCallback(() => {
1256
+ if (!selected) return;
1257
+ void copy(formatForCopy(selected)).then((ok) => showToast({ message: ok ? "Component info copied" : "Copy failed", tone: ok ? "success" : "error" }));
1258
+ }, [selected]);
1259
+ const copyPath = react.useCallback(
1260
+ (sel) => {
1261
+ const path = sel?.filePath ?? sel?.absFilePath;
1262
+ if (!path) return showToast({ message: "No path to copy", tone: "info" });
1263
+ void copy(`${path}${sel?.line ? `:${sel.line}` : ""}`).then((ok) => showToast({ message: ok ? "File path copied" : "Copy failed", tone: ok ? "success" : "error" }));
1264
+ },
1265
+ []
1266
+ );
1267
+ const selectName = react.useCallback(
1268
+ (name) => {
1269
+ const node = graph?.nodes.find((n) => n.name === name || n.id === name);
1270
+ if (node) setSelected(selectFromNode(graph, node));
1271
+ },
1272
+ [graph]
1273
+ );
1274
+ const selectNode = react.useCallback((node) => setSelected(selectFromNode(graph, node)), [graph]);
1275
+ const openNode = react.useCallback((node) => void openLoc(selectFromNode(graph, node)), [graph, openLoc]);
1276
+ const copyNode = react.useCallback((node) => copyPath(selectFromNode(graph, node)), [graph, copyPath]);
1277
+ const statusText = react.useMemo(() => {
1278
+ if (status === "ready" && graph) return `${graph.nodes.length} components \xB7 ${graph.edges.length} relationships`;
1279
+ if (status === "loading" || status === "idle") return "Loading component graph\u2026";
1280
+ if (status === "empty") return "Graph not generated \u2014 run `npx dev-panel-graph` (or add an adapter)";
1281
+ return "Graph endpoint unavailable.";
1282
+ }, [status, graph]);
1283
+ const details = /* @__PURE__ */ jsxRuntime.jsx(
1284
+ NodeDetails,
1285
+ {
1286
+ selected,
1287
+ onOpen: () => void openLoc(selected),
1288
+ onCopyInfo: copyInfo,
1289
+ onCopyPath: () => copyPath(selected),
1290
+ onSelectName: selectName
1291
+ }
1292
+ );
1293
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-surface rdp-panel", style: { bottom: 88, right: 20 }, children: [
1294
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-header", children: [
1295
+ /* @__PURE__ */ jsxRuntime.jsx(IconGraph, { size: 16 }),
1296
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-title", style: { flex: 1 }, children: "Component Graph Inspector" }),
1297
+ enabled && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-chip", style: { background: "var(--rdp-success)", color: "#06210f" }, children: "ON" }),
1298
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { size: 16 }) })
1299
+ ] }),
1300
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-body", children: [
1301
+ /* @__PURE__ */ jsxRuntime.jsxs(
1302
+ "div",
1303
+ {
1304
+ style: {
1305
+ padding: 12,
1306
+ borderRadius: 9,
1307
+ border: `1px solid ${enabled ? "rgba(61,220,132,0.5)" : "var(--rdp-border)"}`,
1308
+ background: enabled ? "rgba(61,220,132,0.08)" : "var(--rdp-bg-elev)",
1309
+ display: "flex",
1310
+ alignItems: "center",
1311
+ justifyContent: "space-between"
1312
+ },
1313
+ children: [
1314
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-title", style: { display: "block" }, children: "Inspect mode" }),
1316
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-sub", children: enabled ? "Hover the UI, click to lock \xB7 Esc to exit" : "Enable, then hover the UI" })
1317
+ ] }),
1318
+ /* @__PURE__ */ jsxRuntime.jsx(
1319
+ "button",
1320
+ {
1321
+ type: "button",
1322
+ className: cx("rdp-btn", enabled && "rdp-btn-primary"),
1323
+ style: { width: "auto" },
1324
+ onClick: () => toggleInspector(config.graphEndpoint),
1325
+ children: enabled ? "On" : "Enable"
1326
+ }
1327
+ )
1328
+ ]
1329
+ }
1330
+ ),
1331
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rdp-tabs", style: { marginTop: 12 }, children: TABS.map((t) => /* @__PURE__ */ jsxRuntime.jsxs(
1332
+ "button",
1333
+ {
1334
+ type: "button",
1335
+ className: "rdp-tab",
1336
+ "aria-selected": mode === t.value,
1337
+ onClick: () => {
1338
+ setMode(t.value);
1339
+ if (t.value === "graph" || t.value === "page") void loadGraph(config.graphEndpoint);
1340
+ },
1341
+ children: [
1342
+ t.icon,
1343
+ t.label
1344
+ ]
1345
+ },
1346
+ t.value
1347
+ )) }),
1348
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 12 }, children: [
1349
+ mode === "hover" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1350
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-section-label", children: "Last locked component" }),
1351
+ details
1352
+ ] }),
1353
+ mode === "graph" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1354
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rdp-sub", style: { marginBottom: 6, color: status === "ready" ? "var(--rdp-success)" : "var(--rdp-text-faint)" }, children: statusText }),
1355
+ (status === "empty" || status === "error") && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "rdp-btn rdp-btn-sm", style: { marginBottom: 8 }, onClick: () => loadGraph(config.graphEndpoint, true), children: "Retry graph load" }),
1356
+ /* @__PURE__ */ jsxRuntime.jsx(GraphSearch, { value: search, onChange: setSearch }),
1357
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1358
+ ComponentGraphTree,
1359
+ {
1360
+ graph,
1361
+ selected,
1362
+ search,
1363
+ expanded,
1364
+ onSelect: selectNode,
1365
+ onToggle: toggleExpanded,
1366
+ onOpen: openNode,
1367
+ onCopy: copyNode
1368
+ }
1369
+ ) }),
1370
+ selected && !search.trim() && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 12 }, children: details })
1371
+ ] }),
1372
+ mode === "page" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1373
+ /* @__PURE__ */ jsxRuntime.jsx(
1374
+ ComponentGraphPageList,
1375
+ {
1376
+ graph,
1377
+ route,
1378
+ selectedName: selected?.componentName,
1379
+ onSelect: selectNode,
1380
+ onOpen: openNode,
1381
+ onCopy: copyNode
1382
+ }
1383
+ ),
1384
+ selected && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 12 }, children: details })
1385
+ ] }),
1386
+ mode === "file" && details
1387
+ ] })
1388
+ ] })
1389
+ ] });
1390
+ }
1391
+ function ComponentGraphOverlay() {
1392
+ const state2 = react.useSyncExternalStore(subscribeGraph, getGraphState, getGraphServerState);
1393
+ const config = useDevPanelConfig();
1394
+ const [hover, setHover] = react.useState(null);
1395
+ const route = config.getRoute();
1396
+ const { enabled } = state2;
1397
+ const openSelected = react.useCallback(
1398
+ async (file, line, column, name) => {
1399
+ if (!file) {
1400
+ showToast({ message: "No source path \u2014 generate the graph or use inspect build", tone: "error" });
1401
+ return;
1402
+ }
1403
+ const ok = await config.openInEditor?.({ file, line, column }, config.editor);
1404
+ showToast(
1405
+ ok === false ? { message: `Editor unavailable \u2014 path copied (${name ?? ""})`, tone: "info" } : { message: "Opening in your editor\u2026", tone: "success" }
1406
+ );
1407
+ },
1408
+ [config]
1409
+ );
1410
+ react.useEffect(() => {
1411
+ if (!enabled) return void 0;
1412
+ let raf = 0;
1413
+ let last = null;
1414
+ const onMove = (e) => {
1415
+ last = e;
1416
+ if (raf) return;
1417
+ raf = requestAnimationFrame(() => {
1418
+ raf = 0;
1419
+ const ev = last;
1420
+ const target = ev?.target;
1421
+ if (!ev || !target || !(target instanceof Element) || isIgnored(target)) {
1422
+ setHover(null);
1423
+ return;
1424
+ }
1425
+ const meta = resolveDisplay(target, getGraphState().graph, route);
1426
+ const r = target.getBoundingClientRect();
1427
+ setHover({
1428
+ name: meta.componentName,
1429
+ filePath: meta.filePath,
1430
+ line: meta.line,
1431
+ domTag: meta.domTag,
1432
+ route: meta.route,
1433
+ rect: { top: r.top, left: r.left, width: r.width, height: r.height },
1434
+ x: ev.clientX,
1435
+ y: ev.clientY
1436
+ });
1437
+ });
1438
+ };
1439
+ const onKey = (e) => {
1440
+ if (e.key === "Escape") disableInspector();
1441
+ };
1442
+ const onClick = (e) => {
1443
+ const target = e.target;
1444
+ if (!target || !(target instanceof Element) || isIgnored(target)) return;
1445
+ const sel = resolveSelected(target, getGraphState().graph, route);
1446
+ e.preventDefault();
1447
+ e.stopPropagation();
1448
+ setSelected(sel);
1449
+ setMode("graph");
1450
+ if (e.metaKey || e.ctrlKey) {
1451
+ void openSelected(sel.absFilePath ?? sel.filePath, sel.line, sel.column, sel.componentName);
1452
+ } else {
1453
+ showToast({ message: `Locked ${sel.componentName}`, tone: "success" });
1454
+ }
1455
+ };
1456
+ document.addEventListener("pointermove", onMove, { passive: true });
1457
+ document.addEventListener("keydown", onKey, true);
1458
+ document.addEventListener("click", onClick, true);
1459
+ return () => {
1460
+ if (raf) cancelAnimationFrame(raf);
1461
+ document.removeEventListener("pointermove", onMove);
1462
+ document.removeEventListener("keydown", onKey, true);
1463
+ document.removeEventListener("click", onClick, true);
1464
+ setHover(null);
1465
+ };
1466
+ }, [enabled, route, openSelected]);
1467
+ if (!enabled) return null;
1468
+ const flipX = typeof window !== "undefined" && hover && hover.x + 374 > window.innerWidth;
1469
+ const flipY = typeof window !== "undefined" && hover && hover.y + 140 > window.innerHeight;
1470
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-overlay", children: [
1471
+ hover && /* @__PURE__ */ jsxRuntime.jsx(
1472
+ "div",
1473
+ {
1474
+ className: "rdp-hl",
1475
+ style: { top: hover.rect.top, left: hover.rect.left, width: hover.rect.width, height: hover.rect.height }
1476
+ }
1477
+ ),
1478
+ hover && /* @__PURE__ */ jsxRuntime.jsxs(
1479
+ "div",
1480
+ {
1481
+ className: "rdp-tooltip",
1482
+ style: {
1483
+ top: flipY ? void 0 : hover.y + 14,
1484
+ bottom: flipY ? window.innerHeight - hover.y + 14 : void 0,
1485
+ left: flipX ? void 0 : hover.x + 14,
1486
+ right: flipX ? window.innerWidth - hover.x + 14 : void 0
1487
+ },
1488
+ children: [
1489
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: hover.filePath ? 4 : 0 }, children: [
1490
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-chip", style: { background: "rgba(105,80,232,0.5)", color: "#fff" }, children: hover.name }),
1491
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-mono", style: { color: "rgba(255,255,255,0.6)", fontSize: 11 }, children: `<${hover.domTag}>` })
1492
+ ] }),
1493
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rdp-mono", style: { fontSize: 11.5, color: "rgba(255,255,255,0.85)", wordBreak: "break-all" }, children: hover.filePath ? `${hover.filePath}${hover.line ? `:${hover.line}` : ""}` : "source resolved from graph on lock" }),
1494
+ hover.route && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.45)" }, children: hover.route }),
1495
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 10.5, marginTop: 4, color: "rgba(255,255,255,0.5)" }, children: "click to lock \xB7 \u2318/Ctrl + click to open \xB7 Esc to exit" })
1496
+ ]
1497
+ }
1498
+ ),
1499
+ state2.toast && /* @__PURE__ */ jsxRuntime.jsx(
1500
+ "div",
1501
+ {
1502
+ className: "rdp-toast",
1503
+ style: {
1504
+ color: state2.toast.tone === "success" ? "var(--rdp-success)" : state2.toast.tone === "error" ? "var(--rdp-error)" : "#fff"
1505
+ },
1506
+ children: state2.toast.message
1507
+ }
1508
+ )
1509
+ ] });
1510
+ }
1511
+ var componentGraphTool = {
1512
+ id: "graph",
1513
+ title: "Component Graph Inspector",
1514
+ subtitle: "Inspect component tree & open source files",
1515
+ color: "primary",
1516
+ icon: /* @__PURE__ */ jsxRuntime.jsx(IconGraph, { size: 19 }),
1517
+ Panel: ComponentGraphPanel,
1518
+ Overlay: ComponentGraphOverlay
1519
+ };
1520
+ function registerComponentGraph() {
1521
+ registerTool(componentGraphTool);
1522
+ }
1523
+ var STORAGE_KEY = "react-dev-panel:corner";
1524
+ var GAP = 20;
1525
+ var FAB = 52;
1526
+ var DRAG_THRESHOLD = 5;
1527
+ function isCorner(v) {
1528
+ return v === "top-left" || v === "top-right" || v === "bottom-left" || v === "bottom-right";
1529
+ }
1530
+ function readCorner() {
1531
+ if (typeof window === "undefined") return "bottom-right";
1532
+ try {
1533
+ const s = window.localStorage.getItem(STORAGE_KEY);
1534
+ if (isCorner(s)) return s;
1535
+ } catch {
1536
+ }
1537
+ return "bottom-right";
1538
+ }
1539
+ function useDraggableCorner() {
1540
+ const [corner, setCorner] = react.useState("bottom-right");
1541
+ const [drag, setDrag] = react.useState(null);
1542
+ const moved = react.useRef(false);
1543
+ const offset = react.useRef({ x: 0, y: 0 });
1544
+ react.useEffect(() => setCorner(readCorner()), []);
1545
+ const onPointerDown = react.useCallback((e) => {
1546
+ if (e.button !== 0) return;
1547
+ const rect = e.currentTarget.getBoundingClientRect();
1548
+ offset.current = { x: e.clientX - rect.left, y: e.clientY - rect.top };
1549
+ moved.current = false;
1550
+ const sx = e.clientX;
1551
+ const sy = e.clientY;
1552
+ const move = (ev) => {
1553
+ if (!moved.current && Math.hypot(ev.clientX - sx, ev.clientY - sy) < DRAG_THRESHOLD) return;
1554
+ moved.current = true;
1555
+ setDrag({ x: ev.clientX - offset.current.x, y: ev.clientY - offset.current.y });
1556
+ };
1557
+ const up = (ev) => {
1558
+ window.removeEventListener("pointermove", move);
1559
+ window.removeEventListener("pointerup", up);
1560
+ if (moved.current) {
1561
+ const cxp = ev.clientX - offset.current.x + FAB / 2;
1562
+ const cyp = ev.clientY - offset.current.y + FAB / 2;
1563
+ const next = `${cyp < window.innerHeight / 2 ? "top" : "bottom"}-${cxp < window.innerWidth / 2 ? "left" : "right"}`;
1564
+ setCorner(next);
1565
+ try {
1566
+ window.localStorage.setItem(STORAGE_KEY, next);
1567
+ } catch {
1568
+ }
1569
+ }
1570
+ setDrag(null);
1571
+ };
1572
+ window.addEventListener("pointermove", move);
1573
+ window.addEventListener("pointerup", up);
1574
+ }, []);
1575
+ const onClickCapture = react.useCallback((e) => {
1576
+ if (moved.current) {
1577
+ e.stopPropagation();
1578
+ e.preventDefault();
1579
+ moved.current = false;
1580
+ }
1581
+ }, []);
1582
+ const isTop = corner.startsWith("top");
1583
+ const isLeft = corner.endsWith("left");
1584
+ const pos = drag ? { top: drag.y, left: drag.x } : {
1585
+ top: isTop ? GAP : void 0,
1586
+ bottom: isTop ? void 0 : GAP,
1587
+ left: isLeft ? GAP : void 0,
1588
+ right: isLeft ? void 0 : GAP
1589
+ };
1590
+ return { pos, isTop, isLeft, onPointerDown, onClickCapture };
1591
+ }
1592
+ function ToolBadge({ tool }) {
1593
+ const badge = tool.useBadge?.();
1594
+ if (!badge) return null;
1595
+ const bg = badge.tone === "error" ? "var(--rdp-error)" : badge.tone === "success" ? "var(--rdp-success)" : "var(--rdp-bg-soft)";
1596
+ const color = badge.tone === "success" ? "#06210f" : "#fff";
1597
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-chip", style: { background: bg, color }, children: badge.label });
1598
+ }
1599
+ function Launcher({
1600
+ tools: tools2,
1601
+ onOpenTool
1602
+ }) {
1603
+ const { pos, isTop, isLeft, onPointerDown, onClickCapture } = useDraggableCorner();
1604
+ const [menuOpen, setMenuOpen] = react.useState(false);
1605
+ const menuPos = {
1606
+ top: isTop ? pos.top !== void 0 ? pos.top + FAB + 8 : GAP + FAB + 8 : void 0,
1607
+ bottom: isTop ? void 0 : GAP + FAB + 8,
1608
+ left: isLeft ? GAP : void 0,
1609
+ right: isLeft ? void 0 : GAP
1610
+ };
1611
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1612
+ /* @__PURE__ */ jsxRuntime.jsx(
1613
+ "button",
1614
+ {
1615
+ type: "button",
1616
+ className: "rdp-fab",
1617
+ style: pos,
1618
+ onPointerDown,
1619
+ onClickCapture,
1620
+ onClick: () => setMenuOpen((v) => !v),
1621
+ "aria-label": "Open Developer Tools",
1622
+ "aria-haspopup": "menu",
1623
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconWrench, { size: 22 })
1624
+ }
1625
+ ),
1626
+ menuOpen && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1627
+ /* @__PURE__ */ jsxRuntime.jsx(
1628
+ "div",
1629
+ {
1630
+ style: { position: "fixed", inset: 0, zIndex: 1 },
1631
+ onClick: () => setMenuOpen(false),
1632
+ "aria-hidden": true
1633
+ }
1634
+ ),
1635
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-surface rdp-menu", style: menuPos, role: "menu", children: [
1636
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-header rdp-menu-head", children: [
1637
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-tile", style: { color: "var(--rdp-accent)", background: "rgba(105,80,232,0.16)" }, children: /* @__PURE__ */ jsxRuntime.jsx(IconWrench, { size: 17 }) }),
1638
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1639
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rdp-title", children: "Developer Tools" }),
1640
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rdp-sub", children: "Internal utilities" })
1641
+ ] })
1642
+ ] }),
1643
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 8 }, children: tools2.map((tool) => /* @__PURE__ */ jsxRuntime.jsxs(
1644
+ "button",
1645
+ {
1646
+ type: "button",
1647
+ className: "rdp-row",
1648
+ role: "menuitem",
1649
+ onClick: () => {
1650
+ onOpenTool(tool.id);
1651
+ setMenuOpen(false);
1652
+ },
1653
+ children: [
1654
+ /* @__PURE__ */ jsxRuntime.jsx(
1655
+ "span",
1656
+ {
1657
+ className: "rdp-tile",
1658
+ style: { color: colorVar(tool.color), background: "rgba(148,163,184,0.1)" },
1659
+ children: tool.icon
1660
+ }
1661
+ ),
1662
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [
1663
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-title", style: { display: "block" }, children: tool.title }),
1664
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-sub", style: { display: "block" }, children: tool.subtitle })
1665
+ ] }),
1666
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, color: "var(--rdp-text-faint)" }, children: [
1667
+ /* @__PURE__ */ jsxRuntime.jsx(ToolBadge, { tool }),
1668
+ /* @__PURE__ */ jsxRuntime.jsx(IconChevronRight, { size: 16 })
1669
+ ] })
1670
+ ]
1671
+ },
1672
+ tool.id
1673
+ )) })
1674
+ ] })
1675
+ ] })
1676
+ ] });
1677
+ }
1678
+ function DevPanel(config) {
1679
+ const enabled = config.enabled ?? (typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true);
1680
+ react.useEffect(() => {
1681
+ if (enabled) injectBaseStyles();
1682
+ }, [enabled]);
1683
+ if (!enabled) return null;
1684
+ return /* @__PURE__ */ jsxRuntime.jsx(DevPanelConfigProvider, { config, children: /* @__PURE__ */ jsxRuntime.jsx(DevPanelInner, { ids: config.tools, theme: config.theme }) });
1685
+ }
1686
+ function DevPanelInner({
1687
+ ids,
1688
+ theme
1689
+ }) {
1690
+ const tools2 = react.useMemo(() => resolveTools(ids), [ids]);
1691
+ const [openId, setOpenId] = react.useState(null);
1692
+ react.useEffect(() => {
1693
+ tools2.forEach((t) => t.init?.());
1694
+ }, [tools2]);
1695
+ const rootStyle = {};
1696
+ if (theme?.accent) rootStyle["--rdp-accent"] = theme.accent;
1697
+ if (theme?.accentContrast)
1698
+ rootStyle["--rdp-accent-contrast"] = theme.accentContrast;
1699
+ const ActivePanel = tools2.find((t) => t.id === openId)?.Panel ?? null;
1700
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-root", style: rootStyle, "data-rdp-ignore": "", children: [
1701
+ /* @__PURE__ */ jsxRuntime.jsx(Launcher, { tools: tools2, onOpenTool: setOpenId }),
1702
+ ActivePanel && /* @__PURE__ */ jsxRuntime.jsx(ActivePanel, { onClose: () => setOpenId(null) }),
1703
+ tools2.map((t) => t.Overlay ? /* @__PURE__ */ jsxRuntime.jsx(t.Overlay, {}, t.id) : null)
1704
+ ] });
1705
+ }
1706
+
1707
+ // src/core/server-open.ts
1708
+ var DEFAULT_GRAPH_ENDPOINT = "/dev-panel/graph";
1709
+ var DEFAULT_OPEN_ENDPOINT = "/dev-panel/open-file";
1710
+ function createServerOpenInEditor(endpoint = DEFAULT_OPEN_ENDPOINT) {
1711
+ return async (loc, editor = "auto") => {
1712
+ try {
1713
+ const res = await fetch(endpoint, {
1714
+ method: "POST",
1715
+ headers: { "Content-Type": "application/json" },
1716
+ body: JSON.stringify({ file: loc.file, line: loc.line, column: loc.column, editor })
1717
+ });
1718
+ if (res.ok) return true;
1719
+ } catch {
1720
+ }
1721
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
1722
+ await navigator.clipboard.writeText(`${loc.file}${loc.line ? `:${loc.line}` : ""}`).catch(() => void 0);
1723
+ }
1724
+ return false;
1725
+ };
1726
+ }
1727
+ var serverOpenInEditor = createServerOpenInEditor();
1728
+
1729
+ // src/index.ts
1730
+ registerDevLogs();
1731
+ registerPagePerformance();
1732
+ registerComponentGraph();
1733
+
1734
+ exports.DEFAULT_GRAPH_ENDPOINT = DEFAULT_GRAPH_ENDPOINT;
1735
+ exports.DEFAULT_OPEN_ENDPOINT = DEFAULT_OPEN_ENDPOINT;
1736
+ exports.DevPanel = DevPanel;
1737
+ exports.createServerOpenInEditor = createServerOpenInEditor;
1738
+ exports.defaultOpenInEditor = defaultOpenInEditor;
1739
+ exports.getRegisteredTools = getRegisteredTools;
1740
+ exports.injectBaseStyles = injectBaseStyles;
1741
+ exports.registerTool = registerTool;
1742
+ exports.serverOpenInEditor = serverOpenInEditor;
1743
+ exports.useDevPanelConfig = useDevPanelConfig;
1744
+ //# sourceMappingURL=index.cjs.map
1745
+ //# sourceMappingURL=index.cjs.map