react-dev-panel 0.1.0 → 0.2.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.
package/dist/index.js CHANGED
@@ -1,375 +1,785 @@
1
1
  export { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT, createServerOpenInEditor, serverOpenInEditor } from './chunk-2ZAPVMUL.js';
2
- import { createContext, useContext, useEffect, useMemo, useState, useSyncExternalStore, useCallback, useRef } from 'react';
2
+ import Box6 from '@mui/material/Box';
3
+ import Chip from '@mui/material/Chip';
4
+ import Stack from '@mui/material/Stack';
5
+ import Tooltip2 from '@mui/material/Tooltip';
6
+ import Collapse from '@mui/material/Collapse';
7
+ import TextField from '@mui/material/TextField';
8
+ import Typography from '@mui/material/Typography';
9
+ import IconButton from '@mui/material/IconButton';
10
+ import ToggleButton from '@mui/material/ToggleButton';
11
+ import { useTheme, alpha } from '@mui/material/styles';
12
+ import InputAdornment from '@mui/material/InputAdornment';
13
+ import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
14
+ import { createContext, useContext, useMemo, useState, useEffect, useSyncExternalStore, useCallback, useRef } from 'react';
15
+ import { LuBug, LuTrash2, LuX, LuSearch, LuGauge, LuWorkflow, LuWrench, LuChevronRight, LuChevronDown, LuMousePointerClick, LuLayers, LuFile, LuArrowUp, LuRefreshCw, LuFileCode, LuCopy, LuClipboardCopy, LuCheck } from 'react-icons/lu';
3
16
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
17
  import { onLCP, onCLS, onINP, onFCP, onTTFB } from 'web-vitals';
18
+ import Switch from '@mui/material/Switch';
19
+ import Divider from '@mui/material/Divider';
20
+ import Button from '@mui/material/Button';
21
+ import Fab from '@mui/material/Fab';
22
+ import Menu from '@mui/material/Menu';
23
+ import MenuItem from '@mui/material/MenuItem';
5
24
 
6
- // src/core/styles.ts
7
- var RDP_STYLE_ID = "react-dev-panel-styles";
8
- var RDP_Z = 2147483e3;
9
- var CSS = `
10
- .rdp-root, .rdp-root * { box-sizing: border-box; }
11
- .rdp-root {
12
- --rdp-bg: #11161d;
13
- --rdp-bg-elev: #1a212b;
14
- --rdp-bg-soft: rgba(148,163,184,0.06);
15
- --rdp-border: rgba(255,255,255,0.09);
16
- --rdp-text: #e6e9ee;
17
- --rdp-text-dim: #9aa4b2;
18
- --rdp-text-faint: #6b7585;
19
- --rdp-accent: #6950E8;
20
- --rdp-accent-contrast: #ffffff;
21
- --rdp-success: #3ddc84;
22
- --rdp-warning: #e0a82e;
23
- --rdp-error: #e5575c;
24
- --rdp-info: #56a6e8;
25
- --rdp-radius: 10px;
26
- --rdp-shadow: 0 8px 28px rgba(0,0,0,0.5);
27
- --rdp-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
28
- --rdp-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
29
- color: var(--rdp-text);
30
- font-family: var(--rdp-sans);
31
- font-size: 13px;
32
- line-height: 1.4;
25
+ // src/core/registry.ts
26
+ var tools = /* @__PURE__ */ new Map();
27
+ function registerTool(def) {
28
+ tools.set(def.id, def);
33
29
  }
34
-
35
- /* Launcher FAB */
36
- .rdp-fab {
37
- position: fixed; width: 52px; height: 52px; border-radius: 50%;
38
- display: grid; place-items: center; cursor: grab; border: none;
39
- color: var(--rdp-accent-contrast);
40
- background: linear-gradient(135deg, var(--rdp-accent), #4a36b8);
41
- box-shadow: 0 4px 12px rgba(105,80,232,0.35), 0 8px 24px rgba(0,0,0,0.5);
42
- z-index: ${RDP_Z};
43
- }
44
- .rdp-fab:active { cursor: grabbing; }
45
- .rdp-fab-badge {
46
- position: absolute; bottom: -3px; right: -3px; min-width: 18px; height: 18px;
47
- padding: 0 5px; border-radius: 999px; background: var(--rdp-error); color: #fff;
48
- font-size: 10px; font-weight: 700; display: grid; place-items: center;
49
- border: 2px solid var(--rdp-bg);
30
+ function getRegisteredTools() {
31
+ return [...tools.values()];
50
32
  }
51
-
52
- /* Floating surfaces */
53
- .rdp-surface {
54
- position: fixed; background: var(--rdp-bg); border: 1px solid var(--rdp-border);
55
- border-radius: var(--rdp-radius); box-shadow: var(--rdp-shadow); overflow: hidden;
56
- display: flex; flex-direction: column; z-index: ${RDP_Z};
57
- }
58
- .rdp-menu { width: 320px; }
59
- .rdp-panel { width: min(460px, calc(100vw - 32px)); max-height: min(78vh, 720px); }
60
-
61
- .rdp-header {
62
- display: flex; align-items: center; gap: 10px; padding: 10px 14px;
63
- border-bottom: 1px solid var(--rdp-border); flex-shrink: 0;
33
+ function resolveTools(ids) {
34
+ if (!ids || ids.length === 0) return getRegisteredTools();
35
+ return ids.map((id) => tools.get(id)).filter((t) => Boolean(t));
64
36
  }
65
- .rdp-menu-head {
66
- background: linear-gradient(135deg, rgba(105,80,232,0.18), rgba(105,80,232,0.04));
37
+ function buildProtocolUrl(editor, loc) {
38
+ const line = loc.line ?? 1;
39
+ const col = loc.column ?? 1;
40
+ switch (editor) {
41
+ case "cursor":
42
+ return `cursor://file/${loc.file}:${line}:${col}`;
43
+ case "webstorm":
44
+ return `webstorm://open?file=${encodeURIComponent(loc.file)}&line=${line}&column=${col}`;
45
+ case "zed":
46
+ return `zed://file/${loc.file}:${line}:${col}`;
47
+ case "vscode":
48
+ case "auto":
49
+ default:
50
+ return `vscode://file/${loc.file}:${line}:${col}`;
51
+ }
67
52
  }
68
- .rdp-title { font-weight: 700; font-size: 13px; }
69
- .rdp-sub { color: var(--rdp-text-dim); font-size: 12px; }
70
- .rdp-body { flex: 1; overflow-y: auto; padding: 12px; }
71
-
72
- /* Menu rows */
73
- .rdp-row {
74
- display: flex; align-items: flex-start; gap: 12px; padding: 10px 12px;
75
- border-radius: 8px; cursor: pointer; width: 100%; text-align: left; border: none;
76
- background: transparent; color: inherit; font: inherit;
53
+ var defaultOpenInEditor = async (loc, editor = "auto") => {
54
+ const url = buildProtocolUrl(editor, loc);
55
+ if (url && typeof window !== "undefined") {
56
+ try {
57
+ window.location.assign(url);
58
+ return true;
59
+ } catch {
60
+ }
61
+ }
62
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
63
+ await navigator.clipboard.writeText(`${loc.file}${loc.line ? `:${loc.line}` : ""}`).catch(() => void 0);
64
+ }
65
+ return false;
66
+ };
67
+ var DevPanelContext = createContext(null);
68
+ function resolve(config) {
69
+ const enabled = config.enabled ?? (typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true);
70
+ return {
71
+ enabled,
72
+ editor: config.editor ?? "auto",
73
+ getRoute: config.getRoute ?? (() => typeof location !== "undefined" ? location.pathname : void 0),
74
+ openInEditor: config.openInEditor ?? defaultOpenInEditor,
75
+ graphEndpoint: config.graphEndpoint,
76
+ theme: config.theme ?? {},
77
+ tools: config.tools
78
+ };
77
79
  }
78
- .rdp-row:hover { background: rgba(255,255,255,0.05); }
79
- .rdp-tile {
80
- width: 38px; height: 38px; flex-shrink: 0; border-radius: 9px;
81
- display: grid; place-items: center;
80
+ function DevPanelConfigProvider({
81
+ config,
82
+ children
83
+ }) {
84
+ const value = useMemo(() => resolve(config), [config]);
85
+ return /* @__PURE__ */ jsx(DevPanelContext.Provider, { value, children });
82
86
  }
83
-
84
- /* Primitives */
85
- .rdp-chip {
86
- display: inline-flex; align-items: center; height: 18px; padding: 0 7px;
87
- border-radius: 999px; font-size: 11px; font-weight: 700; line-height: 1;
88
- }
89
- .rdp-btn {
90
- display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px;
91
- border-radius: 8px; border: 1px solid var(--rdp-border); background: transparent;
92
- color: var(--rdp-text); font: inherit; font-size: 12px; cursor: pointer;
93
- }
94
- .rdp-btn:hover { background: rgba(255,255,255,0.05); }
95
- .rdp-btn-primary {
96
- background: var(--rdp-accent); color: var(--rdp-accent-contrast); border-color: transparent;
97
- font-weight: 600; justify-content: center; width: 100%;
98
- }
99
- .rdp-btn-primary:hover { filter: brightness(1.08); }
100
- .rdp-btn-sm { padding: 2px 7px; font-size: 11px; border-radius: 6px; }
101
- .rdp-iconbtn {
102
- display: grid; place-items: center; width: 28px; height: 28px; border-radius: 7px;
103
- border: 1px solid var(--rdp-border); background: transparent; color: var(--rdp-text);
104
- cursor: pointer;
105
- }
106
- .rdp-iconbtn:hover { background: rgba(255,255,255,0.06); }
107
- .rdp-iconbtn-bare { border: none; width: 22px; height: 22px; color: var(--rdp-text-dim); }
108
- .rdp-iconbtn-bare:hover { color: var(--rdp-text); background: rgba(255,255,255,0.06); }
109
-
110
- .rdp-tabs { display: flex; gap: 4px; background: var(--rdp-bg-soft); padding: 3px; border-radius: 9px; }
111
- .rdp-tab {
112
- flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px;
113
- padding: 6px 4px; border-radius: 7px; border: none; background: transparent;
114
- color: var(--rdp-text-dim); font: inherit; font-size: 12px; cursor: pointer;
87
+ function useDevPanelConfig() {
88
+ const ctx = useContext(DevPanelContext);
89
+ if (!ctx) throw new Error("useDevPanelConfig must be used within <DevPanel> / DevPanelConfigProvider");
90
+ return ctx;
115
91
  }
116
- .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); }
117
92
 
118
- .rdp-input {
119
- display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 9px;
120
- border: 1px solid var(--rdp-border); background: var(--rdp-bg-soft);
93
+ // src/tools/dev-logs/store.ts
94
+ var MAX_ENTRIES = 500;
95
+ var STORAGE_KEY = "react-dev-panel:dev-logs";
96
+ var entries = [];
97
+ var seq = 0;
98
+ var installed = false;
99
+ var hydrated = false;
100
+ var emitScheduled = false;
101
+ var persistTimer = null;
102
+ var listeners = /* @__PURE__ */ new Set();
103
+ var EMPTY = [];
104
+ function emit() {
105
+ if (emitScheduled) return;
106
+ emitScheduled = true;
107
+ const flush = () => {
108
+ emitScheduled = false;
109
+ listeners.forEach((listener) => listener());
110
+ };
111
+ if (typeof queueMicrotask === "function") queueMicrotask(flush);
112
+ else void Promise.resolve().then(flush);
121
113
  }
122
- .rdp-input input {
123
- flex: 1; background: transparent; border: none; outline: none; color: var(--rdp-text);
124
- font: inherit; font-size: 13px;
114
+ function persist() {
115
+ if (typeof window === "undefined") return;
116
+ let list = entries;
117
+ for (let attempt = 0; attempt < 6; attempt += 1) {
118
+ try {
119
+ window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(list));
120
+ return;
121
+ } catch {
122
+ if (!list.length) return;
123
+ list = list.slice(0, Math.floor(list.length / 2));
124
+ }
125
+ }
125
126
  }
126
- .rdp-mono { font-family: var(--rdp-mono); }
127
- .rdp-section-label {
128
- display: block; margin: 8px 0 2px; color: var(--rdp-text-faint); font-weight: 700;
129
- text-transform: uppercase; letter-spacing: 0.5px; font-size: 10px;
127
+ function schedulePersist() {
128
+ if (typeof window === "undefined" || persistTimer) return;
129
+ persistTimer = setTimeout(() => {
130
+ persistTimer = null;
131
+ persist();
132
+ }, 500);
130
133
  }
131
-
132
- /* Inspector overlay bits */
133
- .rdp-overlay { position: fixed; inset: 0; pointer-events: none; z-index: ${RDP_Z - 1}; }
134
- .rdp-hl {
135
- position: fixed; border: 1px solid var(--rdp-accent); border-radius: 3px;
136
- background: rgba(105,80,232,0.14); box-shadow: 0 0 0 1px rgba(105,80,232,0.4);
137
- transition: all 60ms linear; pointer-events: none;
138
- }
139
- .rdp-tooltip {
140
- position: fixed; max-width: 360px; padding: 8px 10px; border-radius: 9px;
141
- background: rgba(15,18,24,0.86); backdrop-filter: blur(8px);
142
- border: 1px solid rgba(255,255,255,0.12); box-shadow: var(--rdp-shadow); color: #fff;
143
- pointer-events: none;
144
- }
145
- .rdp-toast {
146
- position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
147
- padding: 7px 14px; border-radius: 9px; background: rgba(15,18,24,0.92);
148
- border: 1px solid rgba(255,255,255,0.12); box-shadow: var(--rdp-shadow);
149
- font-size: 12px; font-weight: 600; z-index: ${RDP_Z};
134
+ function hydrate() {
135
+ if (hydrated || typeof window === "undefined") return;
136
+ hydrated = true;
137
+ try {
138
+ const raw = window.sessionStorage.getItem(STORAGE_KEY);
139
+ if (!raw) return;
140
+ const parsed = JSON.parse(raw);
141
+ if (!Array.isArray(parsed)) return;
142
+ entries = parsed.slice(0, MAX_ENTRIES);
143
+ seq = entries.reduce((max, entry) => {
144
+ const n = Number(String(entry.id).replace("log-", ""));
145
+ return Number.isFinite(n) && n > max ? n : max;
146
+ }, 0);
147
+ emit();
148
+ } catch {
149
+ }
150
150
  }
151
-
152
- .rdp-body::-webkit-scrollbar, .rdp-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
153
- .rdp-body::-webkit-scrollbar-thumb, .rdp-scroll::-webkit-scrollbar-thumb {
154
- background: rgba(255,255,255,0.14); border-radius: 8px;
155
- }
156
- .rdp-kbd {
157
- padding: 1px 5px; border-radius: 5px; border: 1px solid var(--rdp-border);
158
- background: rgba(255,255,255,0.04); font-family: var(--rdp-mono); font-size: 11px;
159
- color: var(--rdp-text-dim);
160
- }
161
- `;
162
- var injected = false;
163
- function injectBaseStyles() {
164
- if (injected || typeof document === "undefined") return;
165
- if (document.getElementById(RDP_STYLE_ID)) {
166
- injected = true;
167
- return;
151
+ function getDevLogs() {
152
+ return entries;
153
+ }
154
+ function getDevLogsServerSnapshot() {
155
+ return EMPTY;
156
+ }
157
+ function subscribeDevLogs(listener) {
158
+ listeners.add(listener);
159
+ return () => listeners.delete(listener);
160
+ }
161
+ function clearDevLogs() {
162
+ entries = [];
163
+ if (persistTimer) {
164
+ clearTimeout(persistTimer);
165
+ persistTimer = null;
168
166
  }
169
- const style = document.createElement("style");
170
- style.id = RDP_STYLE_ID;
171
- style.textContent = CSS;
172
- document.head.appendChild(style);
173
- injected = true;
174
- }
175
- function colorVar(key) {
176
- switch (key) {
177
- case "info":
178
- return "var(--rdp-info)";
179
- case "warning":
180
- return "var(--rdp-warning)";
181
- case "success":
182
- return "var(--rdp-success)";
183
- case "error":
184
- return "var(--rdp-error)";
185
- default:
186
- return "var(--rdp-accent)";
167
+ if (typeof window !== "undefined") {
168
+ try {
169
+ window.sessionStorage.removeItem(STORAGE_KEY);
170
+ } catch {
171
+ }
187
172
  }
173
+ emit();
188
174
  }
189
- function cx(...parts) {
190
- return parts.filter(Boolean).join(" ");
175
+ function errorCount() {
176
+ return entries.reduce((n, e) => e.level === "error" ? n + 1 : n, 0);
191
177
  }
192
-
193
- // src/core/registry.ts
194
- var tools = /* @__PURE__ */ new Map();
195
- function registerTool(def) {
196
- tools.set(def.id, def);
178
+ function addDevLog(entry) {
179
+ seq += 1;
180
+ const next = {
181
+ ...entry,
182
+ id: `log-${seq}`,
183
+ timestamp: Date.now(),
184
+ path: typeof window !== "undefined" ? window.location.pathname : void 0
185
+ };
186
+ entries = [next, ...entries].slice(0, MAX_ENTRIES);
187
+ emit();
188
+ schedulePersist();
197
189
  }
198
- function getRegisteredTools() {
199
- return [...tools.values()];
190
+ function safeString(value) {
191
+ if (typeof value === "string") return value;
192
+ if (value instanceof Error) return value.message;
193
+ try {
194
+ return JSON.stringify(value);
195
+ } catch {
196
+ return String(value);
197
+ }
200
198
  }
201
- function resolveTools(ids) {
202
- if (!ids || ids.length === 0) return getRegisteredTools();
203
- return ids.map((id) => tools.get(id)).filter((t) => Boolean(t));
199
+ function classifyApolloPayload(payload) {
200
+ if (payload && typeof payload === "object") {
201
+ const p = payload;
202
+ if (p.gqlErrors) return "server";
203
+ if (p.networkError || p.networkStatus) return "network";
204
+ }
205
+ return "server";
206
+ }
207
+ function captureConsole(level, args) {
208
+ const firstStr = typeof args[0] === "string" ? args[0] : safeString(args[0]);
209
+ if (firstStr.startsWith("[Apollo error]")) {
210
+ const payload = args[1];
211
+ addDevLog({
212
+ source: classifyApolloPayload(payload),
213
+ level,
214
+ message: firstStr.replace("[Apollo error] ", "").trim() || "Apollo error",
215
+ detail: payload !== void 0 ? safeString(payload) : void 0
216
+ });
217
+ return;
218
+ }
219
+ const errorArg = args.find((a) => a instanceof Error);
220
+ addDevLog({
221
+ source: "client",
222
+ level,
223
+ message: args.map(safeString).join(" ").trim() || `console.${level}`,
224
+ stack: errorArg?.stack
225
+ });
204
226
  }
205
- function Svg({ children, size = 16 }) {
206
- return /* @__PURE__ */ jsx(
207
- "svg",
208
- {
209
- width: size,
210
- height: size,
211
- viewBox: "0 0 24 24",
212
- fill: "none",
213
- stroke: "currentColor",
214
- strokeWidth: 2,
215
- strokeLinecap: "round",
216
- strokeLinejoin: "round",
217
- "aria-hidden": "true",
218
- children
227
+ var NETWORK_IGNORE = ["/_next/", "/__nextjs", "hot-update", "webpack", ".map", "/monitoring"];
228
+ function shouldIgnoreNetwork(url) {
229
+ return NETWORK_IGNORE.some((needle) => url.includes(needle));
230
+ }
231
+ function resolveRequest(input) {
232
+ if (typeof input === "string") return { url: input, method: "GET" };
233
+ if (input instanceof URL) return { url: input.href, method: "GET" };
234
+ return { url: input.url, method: input.method || "GET" };
235
+ }
236
+ function graphqlOperationName(body) {
237
+ if (typeof body !== "string") return void 0;
238
+ try {
239
+ const parsed = JSON.parse(body);
240
+ if (Array.isArray(parsed)) {
241
+ const names = parsed.map((p) => p?.operationName).filter(Boolean);
242
+ return names.length ? names.join(", ") : void 0;
219
243
  }
220
- );
244
+ return parsed?.operationName || void 0;
245
+ } catch {
246
+ return void 0;
247
+ }
221
248
  }
222
- var IconWrench = ({ size }) => /* @__PURE__ */ jsx(Svg, { size, children: /* @__PURE__ */ 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" }) });
223
- var IconBug = ({ size }) => /* @__PURE__ */ jsxs(Svg, { size, children: [
224
- /* @__PURE__ */ jsx("rect", { x: "8", y: "6", width: "8", height: "14", rx: "4" }),
225
- /* @__PURE__ */ jsx("path", { d: "M9 6a3 3 0 0 1 6 0M3 13h5M16 13h5M4 18l4-2M20 18l-4-2M4 8l4 2M20 8l-4 2" })
226
- ] });
227
- var IconGauge = ({ size }) => /* @__PURE__ */ jsxs(Svg, { size, children: [
228
- /* @__PURE__ */ jsx("path", { d: "M12 14l3-3" }),
229
- /* @__PURE__ */ jsx("path", { d: "M3.5 18a9 9 0 1 1 17 0" })
230
- ] });
231
- var IconGraph = ({ size }) => /* @__PURE__ */ jsxs(Svg, { size, children: [
232
- /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1.5" }),
233
- /* @__PURE__ */ jsx("rect", { x: "14", y: "14", width: "7", height: "7", rx: "1.5" }),
234
- /* @__PURE__ */ jsx("path", { d: "M10 6.5h4a3 3 0 0 1 3 3V14" })
235
- ] });
236
- var IconX = ({ size }) => /* @__PURE__ */ jsx(Svg, { size, children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6L6 18" }) });
237
- var IconChevronRight = ({ size }) => /* @__PURE__ */ jsx(Svg, { size, children: /* @__PURE__ */ jsx("path", { d: "M9 6l6 6-6 6" }) });
238
- var IconChevronDown = ({ size }) => /* @__PURE__ */ jsx(Svg, { size, children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" }) });
239
- var IconSearch = ({ size }) => /* @__PURE__ */ jsxs(Svg, { size, children: [
240
- /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
241
- /* @__PURE__ */ jsx("path", { d: "M21 21l-4-4" })
242
- ] });
243
- var IconCopy = ({ size }) => /* @__PURE__ */ jsxs(Svg, { size, children: [
244
- /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
245
- /* @__PURE__ */ jsx("path", { d: "M5 15V5a2 2 0 0 1 2-2h10" })
246
- ] });
247
- var IconFileCode = ({ size }) => /* @__PURE__ */ jsxs(Svg, { size, children: [
248
- /* @__PURE__ */ jsx("path", { d: "M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z" }),
249
- /* @__PURE__ */ jsx("path", { d: "M14 3v5h5M10 12l-2 2 2 2M14 12l2 2-2 2" })
250
- ] });
251
- var IconRefresh = ({ size }) => /* @__PURE__ */ jsxs(Svg, { size, children: [
252
- /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-3-6.7L21 8" }),
253
- /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" })
254
- ] });
255
- var IconPointer = ({ size }) => /* @__PURE__ */ jsx(Svg, { size, children: /* @__PURE__ */ jsx("path", { d: "M5 3l16 7-7 2-2 7z" }) });
256
- var IconLayers = ({ size }) => /* @__PURE__ */ jsx(Svg, { size, children: /* @__PURE__ */ jsx("path", { d: "M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5" }) });
257
- var IconArrowUp = ({ size }) => /* @__PURE__ */ jsx(Svg, { size, children: /* @__PURE__ */ jsx("path", { d: "M12 19V5M6 11l6-6 6 6" }) });
258
- var buffer = [];
259
- var nextId = 1;
260
- var listeners = /* @__PURE__ */ new Set();
261
- function emit() {
262
- listeners.forEach((l) => l());
249
+ function detectOpType(query, opName) {
250
+ if (typeof query !== "string") return void 0;
251
+ if (opName) {
252
+ const named = new RegExp(`\\b(query|mutation|subscription)\\s+${opName}\\b`).exec(query);
253
+ if (named) return named[1];
254
+ }
255
+ const keyword = /\b(query|mutation|subscription)\b/.exec(query);
256
+ if (keyword) return keyword[1];
257
+ return /^\s*\{/.test(query) ? "query" : void 0;
263
258
  }
264
- function push(level, message, meta) {
265
- buffer = [...buffer.slice(-499), { id: nextId++, level, time: Date.now(), message, meta }];
266
- emit();
259
+ function graphqlOperationType(body) {
260
+ if (typeof body !== "string") return void 0;
261
+ try {
262
+ const parsed = JSON.parse(body);
263
+ const ops = Array.isArray(parsed) ? parsed : [parsed];
264
+ const types = ops.map((p) => detectOpType(p?.query, p?.operationName)).filter(Boolean);
265
+ const unique = Array.from(new Set(types));
266
+ return unique.length === 1 ? unique[0] : void 0;
267
+ } catch {
268
+ return void 0;
269
+ }
270
+ }
271
+ function collapseSignedQuery(u) {
272
+ return u.searchParams.has("X-Amz-Signature") ? "?[signed]" : u.search;
273
+ }
274
+ function shortUrl(url) {
275
+ try {
276
+ const u = new URL(url, window.location.origin);
277
+ return u.pathname + collapseSignedQuery(u);
278
+ } catch {
279
+ return url;
280
+ }
267
281
  }
268
- function stringify(args) {
269
- return args.map((a) => {
270
- if (typeof a === "string") return a;
282
+ function redactedUrl(url) {
283
+ try {
284
+ const u = new URL(url, window.location.origin);
285
+ return `${u.origin}${u.pathname}${collapseSignedQuery(u)}`;
286
+ } catch {
287
+ return url;
288
+ }
289
+ }
290
+ function networkLevel(status) {
291
+ if (status === 0 || status >= 500) return "error";
292
+ if (status >= 400) return "warn";
293
+ return "info";
294
+ }
295
+ var MAX_REASON_CHARS = 160;
296
+ function errorReason(body) {
297
+ if (!body) return void 0;
298
+ const text = body.trim();
299
+ if (!text) return void 0;
300
+ const xmlCode = /<Code>([^<]+)<\/Code>/i.exec(text)?.[1];
301
+ const xmlMessage = /<Message>([^<]+)<\/Message>/i.exec(text)?.[1];
302
+ if (xmlCode || xmlMessage) return [xmlCode, xmlMessage].filter(Boolean).join(": ").slice(0, MAX_REASON_CHARS);
303
+ if (text[0] === "{" || text[0] === "[") {
271
304
  try {
272
- return JSON.stringify(a);
305
+ const parsed = JSON.parse(text);
306
+ const gql = parsed?.errors?.map((e) => e?.message).filter(Boolean).join("; ");
307
+ const reason = gql || (typeof parsed.message === "string" ? parsed.message : void 0) || (typeof parsed.error === "string" ? parsed.error : void 0);
308
+ if (reason) return reason.slice(0, MAX_REASON_CHARS);
273
309
  } catch {
274
- return String(a);
275
310
  }
276
- }).join(" ").slice(0, 2e3);
311
+ }
312
+ const firstLine = text.split("\n").find((line) => line.trim());
313
+ return firstLine ? firstLine.slice(0, MAX_REASON_CHARS) : void 0;
277
314
  }
278
- var installed = false;
279
- function installCapture() {
280
- if (installed || typeof window === "undefined") return;
281
- installed = true;
282
- ["log", "info", "warn", "error"].forEach((level) => {
283
- const original = console[level].bind(console);
284
- console[level] = (...args) => {
285
- push(level, stringify(args));
286
- original(...args);
287
- };
288
- });
289
- if (typeof window.fetch === "function") {
290
- const orig = window.fetch.bind(window);
291
- window.fetch = async (input, init) => {
292
- const method = (init?.method ?? "GET").toUpperCase();
293
- const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
294
- const start = Date.now();
295
- try {
296
- const res = await orig(input, init);
297
- push(res.ok ? "network" : "error", `${method} ${url}`, `${res.status} \xB7 ${Date.now() - start}ms`);
298
- return res;
299
- } catch (err) {
300
- push("error", `${method} ${url}`, `failed \xB7 ${String(err)}`);
301
- throw err;
302
- }
303
- };
315
+ function graphqlResponseErrors(body) {
316
+ if (!body) return void 0;
317
+ const trimmed = body.trim();
318
+ if (trimmed[0] !== "{" && trimmed[0] !== "[") return void 0;
319
+ try {
320
+ const parsed = JSON.parse(trimmed);
321
+ const result = Array.isArray(parsed) ? parsed[0] : parsed;
322
+ const errors = result?.errors;
323
+ if (!Array.isArray(errors) || errors.length === 0) return void 0;
324
+ const reason = errors.map((e) => {
325
+ const code = e?.extensions?.code;
326
+ return code ? `${code}: ${e?.message ?? ""}`.trim() : e?.message;
327
+ }).filter(Boolean).join("; ").slice(0, MAX_REASON_CHARS) || "GraphQL error";
328
+ const data = result?.data;
329
+ const partial = data != null && typeof data === "object" && Object.values(data).some((v) => v != null);
330
+ return { reason, partial };
331
+ } catch {
332
+ return void 0;
304
333
  }
305
334
  }
306
- function subscribeLogs(listener) {
307
- listeners.add(listener);
308
- return () => listeners.delete(listener);
335
+ var MAX_BODY_CHARS = 5e4;
336
+ function truncateBody(text) {
337
+ if (text.length <= MAX_BODY_CHARS) return text;
338
+ return `${text.slice(0, MAX_BODY_CHARS)}
339
+ \u2026 [truncated ${text.length - MAX_BODY_CHARS} chars]`;
309
340
  }
310
- function getLogs() {
311
- return buffer;
341
+ function prettyMaybeJson(text) {
342
+ const trimmed = text.trim();
343
+ if (trimmed[0] !== "{" && trimmed[0] !== "[") return text;
344
+ try {
345
+ return JSON.stringify(JSON.parse(trimmed), null, 2);
346
+ } catch {
347
+ return text;
348
+ }
312
349
  }
313
- var EMPTY = [];
314
- function getServerLogs() {
315
- return EMPTY;
350
+ function formatGraphqlBody(parsed) {
351
+ const ops = Array.isArray(parsed) ? parsed : [parsed];
352
+ if (!ops.length || !ops.every((op) => op && typeof op === "object" && typeof op.query === "string")) return null;
353
+ return ops.map((op) => {
354
+ const parts = [];
355
+ if (op.operationName) parts.push(`# Operation: ${op.operationName}`);
356
+ if (op.variables && Object.keys(op.variables).length > 0)
357
+ parts.push(`# Variables
358
+ ${JSON.stringify(op.variables, null, 2)}`);
359
+ parts.push(`# Query
360
+ ${op.query.trim()}`);
361
+ return parts.join("\n\n");
362
+ }).join("\n\n\u2014\u2014\u2014\n\n");
363
+ }
364
+ function formatBodyString(text) {
365
+ const trimmed = text.trim();
366
+ if (trimmed[0] === "{" || trimmed[0] === "[") {
367
+ try {
368
+ const parsed = JSON.parse(trimmed);
369
+ return formatGraphqlBody(parsed) ?? JSON.stringify(parsed, null, 2);
370
+ } catch {
371
+ return text;
372
+ }
373
+ }
374
+ return text;
375
+ }
376
+ var SENSITIVE_HEADERS = ["authorization", "cookie", "set-cookie", "x-api-key", "proxy-authorization"];
377
+ function formatHeaders(source) {
378
+ const lines = [];
379
+ source.forEach((value, key) => {
380
+ const redacted = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? "[redacted]" : value;
381
+ lines.push(`${key}: ${redacted}`);
382
+ });
383
+ return lines.length ? lines.join("\n") : void 0;
384
+ }
385
+ function requestHeaders(input, init) {
386
+ const merged = new Headers();
387
+ if (input instanceof Request) input.headers.forEach((value, key) => merged.set(key, value));
388
+ if (init?.headers) new Headers(init.headers).forEach((value, key) => merged.set(key, value));
389
+ return formatHeaders(merged);
390
+ }
391
+ function rawRequestBody(body) {
392
+ if (typeof body !== "string") return void 0;
393
+ return truncateBody(body);
394
+ }
395
+ function readableRequestBody(body) {
396
+ if (body == null) return void 0;
397
+ if (typeof body === "string") return truncateBody(formatBodyString(body));
398
+ if (body instanceof URLSearchParams) return body.toString();
399
+ const name = body.constructor?.name;
400
+ return `[${name || "binary"} body \u2014 not shown]`;
401
+ }
402
+ async function readResponseBody(res) {
403
+ try {
404
+ const ct = res.headers.get("content-type") ?? "";
405
+ if (ct && !/json|text|xml|graphql|javascript|csv|html/i.test(ct)) return `[${ct} response \u2014 not shown]`;
406
+ const text = await res.text();
407
+ if (!text) return void 0;
408
+ return truncateBody(prettyMaybeJson(text));
409
+ } catch {
410
+ return void 0;
411
+ }
316
412
  }
317
- function clearLogs() {
318
- buffer = [];
319
- emit();
413
+ function captureFetch() {
414
+ if (typeof window.fetch !== "function") return;
415
+ const origFetch = window.fetch.bind(window);
416
+ window.fetch = async (input, init) => {
417
+ const { url, method: inferredMethod } = resolveRequest(input);
418
+ const method = (init?.method || inferredMethod || "GET").toUpperCase();
419
+ if (shouldIgnoreNetwork(url)) return origFetch(input, init);
420
+ const opName = graphqlOperationName(init?.body ?? null);
421
+ const opType = graphqlOperationType(init?.body ?? null);
422
+ const reqHeaders = requestHeaders(input, init);
423
+ const requestBody = readableRequestBody(init?.body ?? null);
424
+ const requestBodyRaw = rawRequestBody(init?.body ?? null);
425
+ const start = Date.now();
426
+ const log = (status, statusText, errored, responseBody, responseHeaders) => {
427
+ const ms = Date.now() - start;
428
+ const opLabel = opName ? `${opType ? `${opType} ` : ""}${opName}` : void 0;
429
+ const label = opLabel ? `${shortUrl(url)} \xB7 ${opLabel}` : shortUrl(url);
430
+ const gqlErrors = !errored && status < 400 ? graphqlResponseErrors(responseBody) : void 0;
431
+ const statusLabel = errored ? "FAILED" : String(status);
432
+ const reason = errored ? statusText : status >= 400 ? errorReason(responseBody) : gqlErrors?.reason;
433
+ const level = errored ? "error" : gqlErrors ? gqlErrors.partial ? "warn" : "error" : networkLevel(status);
434
+ addDevLog({
435
+ source: "network",
436
+ level,
437
+ message: `${method} ${statusLabel} ${label} (${ms}ms)${reason ? ` \u2014 ${reason}` : ""}`,
438
+ detail: `${method} ${redactedUrl(url)}
439
+ ${errored ? statusText : `${status} ${statusText}`} \xB7 ${ms}ms${gqlErrors ? ` \xB7 GraphQL ${gqlErrors.partial ? "partial error" : "error"}` : ""}`,
440
+ requestHeaders: reqHeaders,
441
+ requestBody,
442
+ requestBodyRaw,
443
+ responseHeaders,
444
+ responseBody
445
+ });
446
+ };
447
+ try {
448
+ const res = await origFetch(input, init);
449
+ let clone = null;
450
+ try {
451
+ clone = res.clone();
452
+ } catch {
453
+ clone = null;
454
+ }
455
+ const resHeaders = formatHeaders(res.headers);
456
+ if (clone) void readResponseBody(clone).then((body) => log(res.status, res.statusText, false, body, resHeaders));
457
+ else log(res.status, res.statusText, false, void 0, resHeaders);
458
+ return res;
459
+ } catch (err) {
460
+ log(0, err instanceof Error ? err.message : "Network error", true);
461
+ throw err;
462
+ }
463
+ };
320
464
  }
321
- var LEVELS = ["all", "log", "warn", "error", "network"];
322
- var LEVEL_COLOR = {
323
- log: "var(--rdp-text-dim)",
324
- info: "var(--rdp-info)",
325
- warn: "var(--rdp-warning)",
326
- error: "var(--rdp-error)",
327
- network: "var(--rdp-info)"
465
+ function installDevLogCapture() {
466
+ if (installed || typeof window === "undefined") return;
467
+ installed = true;
468
+ hydrate();
469
+ captureFetch();
470
+ window.addEventListener("pagehide", persist);
471
+ const origError = console.error.bind(console);
472
+ const origWarn = console.warn.bind(console);
473
+ const origLog = console.log.bind(console);
474
+ console.error = (...args) => {
475
+ origError(...args);
476
+ try {
477
+ captureConsole("error", args);
478
+ } catch {
479
+ }
480
+ };
481
+ console.warn = (...args) => {
482
+ origWarn(...args);
483
+ try {
484
+ captureConsole("warn", args);
485
+ } catch {
486
+ }
487
+ };
488
+ console.log = (...args) => {
489
+ origLog(...args);
490
+ try {
491
+ captureConsole("info", args);
492
+ } catch {
493
+ }
494
+ };
495
+ window.addEventListener("error", (event) => {
496
+ addDevLog({
497
+ source: "client",
498
+ level: "error",
499
+ message: event.message || "Uncaught error",
500
+ stack: event.error?.stack,
501
+ detail: event.filename ? `${event.filename}:${event.lineno}:${event.colno}` : void 0
502
+ });
503
+ });
504
+ window.addEventListener("unhandledrejection", (event) => {
505
+ const reason = event.reason;
506
+ addDevLog({
507
+ source: "client",
508
+ level: "error",
509
+ message: reason?.message ? `Unhandled rejection: ${reason.message}` : "Unhandled promise rejection",
510
+ stack: reason?.stack,
511
+ detail: safeString(event.reason)
512
+ });
513
+ });
514
+ }
515
+ var SOURCE_COLOR = {
516
+ server: "warning",
517
+ client: "info",
518
+ network: "success"
328
519
  };
329
- function DevLogsPanel({ onClose }) {
330
- const logs = useSyncExternalStore(subscribeLogs, getLogs, getServerLogs);
331
- const [filter, setFilter] = useState("all");
332
- const shown = filter === "all" ? logs : logs.filter((l) => l.level === filter);
333
- return /* @__PURE__ */ jsxs("div", { className: "rdp-surface rdp-panel", style: { bottom: 88, right: 20 }, children: [
334
- /* @__PURE__ */ jsxs("div", { className: "rdp-header", children: [
335
- /* @__PURE__ */ jsx(IconBug, { size: 16 }),
336
- /* @__PURE__ */ jsx("span", { className: "rdp-title", style: { flex: 1 }, children: "Developer Logs" }),
337
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: clearLogs, title: "Clear", style: { width: "auto", padding: "0 8px" }, children: "Clear" }),
338
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(IconX, { size: 16 }) })
520
+ function formatTime(ts) {
521
+ const d = new Date(ts);
522
+ const pad = (n) => String(n).padStart(2, "0");
523
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
524
+ }
525
+ function buildSections(entry) {
526
+ const sections = [];
527
+ const requestTabs = [];
528
+ if (entry.requestBody) {
529
+ const hasRaw = Boolean(entry.requestBodyRaw && entry.requestBodyRaw !== entry.requestBody);
530
+ requestTabs.push({
531
+ key: "payload",
532
+ label: "Payload",
533
+ value: entry.requestBody,
534
+ variants: hasRaw ? [
535
+ { key: "pretty", label: "Pretty", value: entry.requestBody },
536
+ { key: "raw", label: "Raw", value: entry.requestBodyRaw }
537
+ ] : void 0
538
+ });
539
+ }
540
+ if (entry.requestHeaders) requestTabs.push({ key: "headers", label: "Headers", value: entry.requestHeaders });
541
+ if (requestTabs.length) sections.push({ label: "Request", tabs: requestTabs });
542
+ const responseTabs = [];
543
+ if (entry.responseBody) responseTabs.push({ key: "body", label: "Body", value: entry.responseBody });
544
+ if (entry.responseHeaders) responseTabs.push({ key: "headers", label: "Headers", value: entry.responseHeaders });
545
+ if (responseTabs.length) sections.push({ label: "Response", tabs: responseTabs });
546
+ if (entry.stack) sections.push({ label: "Stack trace", tabs: [{ key: "stack", label: "Stack trace", value: entry.stack }] });
547
+ if (!sections.length && entry.detail) sections.push({ label: "Detail", tabs: [{ key: "detail", label: "Detail", value: entry.detail }] });
548
+ return sections;
549
+ }
550
+ var TOGGLE_SX = {
551
+ "& .MuiToggleButton-root": {
552
+ py: 0,
553
+ px: 0.75,
554
+ height: 20,
555
+ fontSize: "0.6rem",
556
+ fontWeight: 700,
557
+ lineHeight: 1,
558
+ letterSpacing: 0.3,
559
+ textTransform: "uppercase",
560
+ border: "1px solid",
561
+ borderColor: "divider"
562
+ }
563
+ };
564
+ function CopyButton({ value }) {
565
+ const [copied, setCopied] = useState(false);
566
+ const handleCopy = useCallback(
567
+ (e) => {
568
+ e.stopPropagation();
569
+ navigator.clipboard?.writeText(value).then(
570
+ () => {
571
+ setCopied(true);
572
+ window.setTimeout(() => setCopied(false), 1200);
573
+ },
574
+ () => void 0
575
+ );
576
+ },
577
+ [value]
578
+ );
579
+ return /* @__PURE__ */ jsx(Tooltip2, { title: copied ? "Copied" : "Copy", placement: "top", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: handleCopy, sx: { color: copied ? "success.main" : void 0 }, children: copied ? /* @__PURE__ */ jsx(LuCheck, { size: 13 }) : /* @__PURE__ */ jsx(LuCopy, { size: 13 }) }) });
580
+ }
581
+ function SectionBlock({ section }) {
582
+ const theme = useTheme();
583
+ const [activeTabKey, setActiveTabKey] = useState(section.tabs[0]?.key);
584
+ const [variantByTab, setVariantByTab] = useState({});
585
+ const activeTab = section.tabs.find((t) => t.key === activeTabKey) ?? section.tabs[0];
586
+ const activeVariantKey = variantByTab[activeTab.key] ?? activeTab.variants?.[0]?.key;
587
+ const activeVariant = activeTab.variants?.find((v) => v.key === activeVariantKey);
588
+ const displayValue = activeVariant?.value ?? activeTab.value;
589
+ const showTabs = section.tabs.length > 1;
590
+ return /* @__PURE__ */ jsxs(Box6, { children: [
591
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1, py: 0.25, gap: 0.5, flexWrap: "wrap" }, children: [
592
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.75, sx: { minWidth: 0 }, children: [
593
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.disabled", fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.4 }, children: section.label }),
594
+ showTabs && /* @__PURE__ */ jsx(ToggleButtonGroup, { size: "small", exclusive: true, value: activeTab.key, onChange: (_, next) => next && setActiveTabKey(next), sx: TOGGLE_SX, children: section.tabs.map((t) => /* @__PURE__ */ jsx(ToggleButton, { value: t.key, children: t.label }, t.key)) })
595
+ ] }),
596
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
597
+ activeTab.variants && /* @__PURE__ */ jsx(
598
+ ToggleButtonGroup,
599
+ {
600
+ size: "small",
601
+ exclusive: true,
602
+ value: activeVariantKey,
603
+ onChange: (_, next) => next && setVariantByTab((prev) => ({ ...prev, [activeTab.key]: next })),
604
+ sx: TOGGLE_SX,
605
+ children: activeTab.variants.map((v) => /* @__PURE__ */ jsx(ToggleButton, { value: v.key, children: v.label }, v.key))
606
+ }
607
+ ),
608
+ /* @__PURE__ */ jsx(CopyButton, { value: displayValue })
609
+ ] })
339
610
  ] }),
340
- /* @__PURE__ */ jsx("div", { style: { padding: "8px 12px 0", display: "flex", gap: 4, flexWrap: "wrap" }, children: LEVELS.map((lvl) => /* @__PURE__ */ jsx(
341
- "button",
611
+ /* @__PURE__ */ jsx(
612
+ Box6,
342
613
  {
343
- type: "button",
344
- className: cx("rdp-btn", "rdp-btn-sm"),
345
- style: filter === lvl ? { borderColor: "var(--rdp-accent)", color: "var(--rdp-accent)" } : void 0,
346
- onClick: () => setFilter(lvl),
347
- children: lvl
348
- },
349
- lvl
350
- )) }),
351
- /* @__PURE__ */ jsx("div", { className: "rdp-body rdp-mono", style: { fontSize: 12 }, children: shown.length === 0 ? /* @__PURE__ */ jsx("div", { style: { color: "var(--rdp-text-faint)" }, children: "No log entries captured yet." }) : shown.slice().reverse().map((entry) => /* @__PURE__ */ jsxs("div", { style: { padding: "3px 0", borderBottom: "1px solid var(--rdp-border)" }, children: [
352
- /* @__PURE__ */ jsx("span", { style: { color: LEVEL_COLOR[entry.level], fontWeight: 700, marginRight: 6 }, children: entry.level === "network" ? "net" : entry.level }),
353
- /* @__PURE__ */ jsx("span", { style: { wordBreak: "break-word" }, children: entry.message }),
354
- entry.meta && /* @__PURE__ */ jsx("span", { style: { color: "var(--rdp-text-faint)", marginLeft: 6 }, children: entry.meta })
355
- ] }, entry.id)) })
614
+ component: "pre",
615
+ sx: {
616
+ m: 0,
617
+ px: 1,
618
+ pb: 1,
619
+ fontFamily: "monospace",
620
+ fontSize: "0.7rem",
621
+ color: "text.secondary",
622
+ whiteSpace: "pre-wrap",
623
+ wordBreak: "break-word",
624
+ overflowX: "auto",
625
+ maxHeight: 280,
626
+ bgcolor: alpha(theme.palette.grey[500], 0.06)
627
+ },
628
+ children: displayValue
629
+ }
630
+ )
356
631
  ] });
357
632
  }
633
+ function LogRow({ entry, showPath }) {
634
+ const [open, setOpen] = useState(false);
635
+ const sections = useMemo(() => buildSections(entry), [entry]);
636
+ const expandable = sections.length > 0;
637
+ return /* @__PURE__ */ jsxs(Box6, { sx: { border: "1px solid", borderColor: "divider", borderRadius: 1, overflow: "hidden" }, children: [
638
+ /* @__PURE__ */ jsxs(
639
+ Box6,
640
+ {
641
+ onClick: expandable ? () => setOpen((v) => !v) : void 0,
642
+ sx: {
643
+ display: "flex",
644
+ alignItems: "flex-start",
645
+ gap: 1,
646
+ px: 1,
647
+ py: 0.75,
648
+ cursor: expandable ? "pointer" : "default",
649
+ "&:hover": expandable ? { bgcolor: "action.hover" } : void 0
650
+ },
651
+ children: [
652
+ /* @__PURE__ */ jsx(
653
+ Chip,
654
+ {
655
+ label: entry.source,
656
+ size: "small",
657
+ color: SOURCE_COLOR[entry.source],
658
+ variant: "soft",
659
+ sx: { height: 18, fontSize: "0.6rem", textTransform: "uppercase", fontWeight: 700, mt: 0.25, flexShrink: 0 }
660
+ }
661
+ ),
662
+ /* @__PURE__ */ jsxs(Box6, { sx: { flex: 1, minWidth: 0 }, children: [
663
+ /* @__PURE__ */ jsx(
664
+ Typography,
665
+ {
666
+ variant: "body2",
667
+ sx: {
668
+ fontFamily: "monospace",
669
+ fontSize: "0.75rem",
670
+ color: entry.level === "error" ? "error.main" : "text.primary",
671
+ wordBreak: "break-word",
672
+ whiteSpace: "pre-wrap"
673
+ },
674
+ children: entry.message
675
+ }
676
+ ),
677
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { mt: 0.25 }, children: [
678
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.disabled", fontFamily: "monospace" }, children: formatTime(entry.timestamp) }),
679
+ showPath && entry.path && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.disabled", fontFamily: "monospace", wordBreak: "break-all" }, children: entry.path })
680
+ ] })
681
+ ] }),
682
+ expandable && /* @__PURE__ */ jsx(Box6, { sx: { flexShrink: 0, color: "text.disabled", transform: open ? "rotate(180deg)" : "none", transition: "transform 0.15s", mt: 0.25 }, children: /* @__PURE__ */ jsx(LuChevronDown, { size: 14 }) })
683
+ ]
684
+ }
685
+ ),
686
+ expandable && /* @__PURE__ */ jsx(Collapse, { in: open, unmountOnExit: true, children: /* @__PURE__ */ jsx(Stack, { spacing: 0.5, sx: { borderTop: "1px solid", borderColor: "divider", py: 0.5 }, children: sections.map((section) => /* @__PURE__ */ jsx(SectionBlock, { section }, section.label)) }) })
687
+ ] });
688
+ }
689
+ function DevLogsPanel({ onClose }) {
690
+ const logs = useSyncExternalStore(subscribeDevLogs, getDevLogs, getDevLogsServerSnapshot);
691
+ const pathname = useDevPanelConfig().getRoute();
692
+ const [source, setSource] = useState("all");
693
+ const [level, setLevel] = useState("all");
694
+ const [thisPageOnly, setThisPageOnly] = useState(true);
695
+ const [search, setSearch2] = useState("");
696
+ const filtered = useMemo(() => {
697
+ const term = search.trim().toLowerCase();
698
+ return logs.filter((entry) => {
699
+ if (source !== "all" && entry.source !== source) return false;
700
+ if (level !== "all" && entry.level !== level) return false;
701
+ if (thisPageOnly && entry.path && entry.path !== pathname) return false;
702
+ if (term) {
703
+ const haystack = `${entry.message} ${entry.detail ?? ""} ${entry.stack ?? ""}`.toLowerCase();
704
+ if (!haystack.includes(term)) return false;
705
+ }
706
+ return true;
707
+ });
708
+ }, [logs, source, level, thisPageOnly, search, pathname]);
709
+ return /* @__PURE__ */ jsxs(
710
+ Box6,
711
+ {
712
+ "data-rdp-ignore": "",
713
+ sx: {
714
+ position: "fixed",
715
+ bottom: 96,
716
+ right: 24,
717
+ zIndex: (t) => t.zIndex.modal + 1,
718
+ width: "min(460px, calc(100vw - 32px))",
719
+ maxHeight: "min(70vh, 640px)",
720
+ display: "flex",
721
+ flexDirection: "column",
722
+ borderRadius: 2,
723
+ overflow: "hidden",
724
+ border: "1px solid",
725
+ borderColor: "divider",
726
+ bgcolor: "background.paper",
727
+ boxShadow: 12
728
+ },
729
+ children: [
730
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1.5, py: 1, borderBottom: "1px solid", borderColor: "divider", flexShrink: 0 }, children: [
731
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [
732
+ /* @__PURE__ */ jsx(LuBug, { size: 16 }),
733
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", sx: { fontWeight: 700 }, children: "Developer Logs" }),
734
+ /* @__PURE__ */ jsx(Chip, { label: `${filtered.length}/${logs.length}`, size: "small", variant: "soft", sx: { height: 18, fontSize: "0.65rem" } })
735
+ ] }),
736
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, children: [
737
+ /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: clearDevLogs, disabled: logs.length === 0, children: /* @__PURE__ */ jsx(LuTrash2, { size: 15 }) }),
738
+ /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onClose, children: /* @__PURE__ */ jsx(LuX, { size: 16 }) })
739
+ ] })
740
+ ] }),
741
+ /* @__PURE__ */ jsxs(Stack, { spacing: 1, sx: { px: 1.5, py: 1, borderBottom: "1px solid", borderColor: "divider", flexShrink: 0 }, children: [
742
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", flexWrap: "wrap", useFlexGap: true, children: [
743
+ /* @__PURE__ */ jsx(ToggleButtonGroup, { size: "small", exclusive: true, value: source, onChange: (_, v) => v && setSource(v), children: ["all", "client", "server", "network"].map((v) => /* @__PURE__ */ jsx(ToggleButton, { value: v, sx: { px: 1, py: 0.25, fontSize: "0.7rem", textTransform: "capitalize" }, children: v }, v)) }),
744
+ /* @__PURE__ */ jsx(ToggleButton, { size: "small", value: "thisPage", selected: thisPageOnly, onChange: () => setThisPageOnly((v) => !v), sx: { px: 1, py: 0.25, fontSize: "0.7rem" }, children: "This page" })
745
+ ] }),
746
+ /* @__PURE__ */ jsx(ToggleButtonGroup, { size: "small", exclusive: true, value: level, onChange: (_, v) => v && setLevel(v), children: ["all", "error", "warn", "info"].map((v) => /* @__PURE__ */ jsx(ToggleButton, { value: v, sx: { px: 1, py: 0.25, fontSize: "0.7rem", textTransform: "capitalize" }, children: v }, v)) }),
747
+ /* @__PURE__ */ jsx(
748
+ TextField,
749
+ {
750
+ size: "small",
751
+ placeholder: "Search...",
752
+ value: search,
753
+ onChange: (e) => setSearch2(e.target.value),
754
+ fullWidth: true,
755
+ slotProps: {
756
+ input: {
757
+ startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(LuSearch, { size: 15 }) })
758
+ }
759
+ }
760
+ }
761
+ )
762
+ ] }),
763
+ /* @__PURE__ */ jsx(Box6, { sx: { flex: 1, overflowY: "auto", p: 1 }, children: filtered.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.disabled", textAlign: "center", py: 4 }, children: logs.length === 0 ? "No activity captured this session." : "No logs match the current filters." }) : /* @__PURE__ */ jsx(Stack, { spacing: 0.75, children: filtered.map((entry) => /* @__PURE__ */ jsx(LogRow, { entry, showPath: !thisPageOnly }, entry.id)) }) })
764
+ ]
765
+ }
766
+ );
767
+ }
358
768
  function useBadge() {
359
- const logs = useSyncExternalStore(subscribeLogs, getLogs, getServerLogs);
360
- const errors = logs.filter((l) => l.level === "error").length;
769
+ const logs = useSyncExternalStore(subscribeDevLogs, getDevLogs, getDevLogsServerSnapshot);
361
770
  if (logs.length === 0) return null;
771
+ const errors = errorCount();
362
772
  return errors > 0 ? { label: String(errors), tone: "error" } : { label: String(logs.length), tone: "neutral" };
363
773
  }
364
774
  var devLogsTool = {
365
775
  id: "logs",
366
776
  title: "Developer Logs",
367
- subtitle: "Client & network activity",
777
+ subtitle: "Client, server & network activity",
368
778
  color: "info",
369
- icon: /* @__PURE__ */ jsx(IconBug, { size: 19 }),
779
+ icon: /* @__PURE__ */ jsx(LuBug, { size: 19 }),
370
780
  Panel: DevLogsPanel,
371
781
  useBadge,
372
- init: installCapture
782
+ init: installDevLogCapture
373
783
  };
374
784
  function registerDevLogs() {
375
785
  registerTool(devLogsTool);
@@ -389,12 +799,7 @@ function emit2() {
389
799
  function record(m) {
390
800
  metrics = {
391
801
  ...metrics,
392
- [m.name]: {
393
- name: m.name,
394
- value: m.value,
395
- rating: m.rating,
396
- unit: m.name === "CLS" ? "" : "ms"
397
- }
802
+ [m.name]: { name: m.name, value: m.value, rating: m.rating, unit: m.name === "CLS" ? "" : "ms" }
398
803
  };
399
804
  emit2();
400
805
  }
@@ -420,134 +825,112 @@ function serverSnapshot() {
420
825
  return EMPTY2;
421
826
  }
422
827
  var RATING_COLOR = {
423
- good: "var(--rdp-success)",
424
- "needs-improvement": "var(--rdp-warning)",
425
- poor: "var(--rdp-error)"
828
+ good: "success",
829
+ "needs-improvement": "warning",
830
+ poor: "error"
426
831
  };
427
832
  function PagePerformancePanel({ onClose }) {
428
833
  const data = useSyncExternalStore(subscribe, snapshot, serverSnapshot);
429
834
  const order = ["LCP", "INP", "CLS", "FCP", "TTFB"];
430
- return /* @__PURE__ */ jsxs("div", { className: "rdp-surface rdp-panel", style: { bottom: 88, right: 20 }, children: [
431
- /* @__PURE__ */ jsxs("div", { className: "rdp-header", children: [
432
- /* @__PURE__ */ jsx(IconGauge, { size: 16 }),
433
- /* @__PURE__ */ jsx("span", { className: "rdp-title", style: { flex: 1 }, children: "Page Performance" }),
434
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(IconX, { size: 16 }) })
435
- ] }),
436
- /* @__PURE__ */ jsxs("div", { className: "rdp-body", children: [
437
- order.every((k) => !data[k]) && /* @__PURE__ */ jsx("div", { style: { color: "var(--rdp-text-faint)" }, children: "Collecting Web Vitals\u2026 interact with the page (INP needs an interaction)." }),
438
- order.map((key) => {
439
- const m = data[key];
440
- if (!m) return null;
441
- return /* @__PURE__ */ jsxs("div", { style: { padding: "8px 0", borderBottom: "1px solid var(--rdp-border)" }, children: [
442
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
443
- /* @__PURE__ */ jsx("span", { className: "rdp-chip", style: { background: RATING_COLOR[m.rating], color: "#06210f" }, children: m.name }),
444
- /* @__PURE__ */ jsxs("span", { className: "rdp-mono", style: { fontWeight: 700 }, children: [
445
- m.unit === "ms" ? Math.round(m.value) : m.value.toFixed(3),
446
- m.unit
447
- ] }),
448
- /* @__PURE__ */ jsx("span", { style: { color: RATING_COLOR[m.rating], fontSize: 11, marginLeft: "auto" }, children: m.rating })
835
+ return /* @__PURE__ */ jsxs(
836
+ Box6,
837
+ {
838
+ "data-rdp-ignore": "",
839
+ sx: {
840
+ position: "fixed",
841
+ bottom: 96,
842
+ right: 24,
843
+ zIndex: (t) => t.zIndex.modal + 1,
844
+ width: "min(440px, calc(100vw - 32px))",
845
+ maxHeight: "min(70vh, 640px)",
846
+ display: "flex",
847
+ flexDirection: "column",
848
+ borderRadius: 2,
849
+ overflow: "hidden",
850
+ border: "1px solid",
851
+ borderColor: "divider",
852
+ bgcolor: "background.paper",
853
+ boxShadow: 12
854
+ },
855
+ children: [
856
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1.5, py: 1, borderBottom: "1px solid", borderColor: "divider" }, children: [
857
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [
858
+ /* @__PURE__ */ jsx(LuGauge, { size: 16 }),
859
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", sx: { fontWeight: 700 }, children: "Page Performance" })
449
860
  ] }),
450
- /* @__PURE__ */ jsx("div", { className: "rdp-sub", style: { marginTop: 2 }, children: HINTS[key] })
451
- ] }, key);
452
- })
453
- ] })
454
- ] });
861
+ /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onClose, children: /* @__PURE__ */ jsx(LuX, { size: 16 }) })
862
+ ] }),
863
+ /* @__PURE__ */ jsxs(Box6, { sx: { flex: 1, overflowY: "auto", p: 1.5 }, children: [
864
+ order.every((k) => !data[k]) && /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.disabled" }, children: "Collecting Web Vitals\u2026 interact with the page (INP needs an interaction)." }),
865
+ order.map((key) => {
866
+ const m = data[key];
867
+ if (!m) return null;
868
+ return /* @__PURE__ */ jsxs(Box6, { sx: { py: 1, borderBottom: "1px solid", borderColor: "divider" }, children: [
869
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [
870
+ /* @__PURE__ */ jsx(
871
+ Chip,
872
+ {
873
+ label: m.name,
874
+ size: "small",
875
+ color: RATING_COLOR[m.rating],
876
+ variant: "soft",
877
+ sx: { height: 20, fontSize: "0.65rem", fontWeight: 700 }
878
+ }
879
+ ),
880
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", sx: { fontFamily: "monospace", fontWeight: 700 }, children: [
881
+ m.unit === "ms" ? Math.round(m.value) : m.value.toFixed(3),
882
+ m.unit
883
+ ] }),
884
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: `${RATING_COLOR[m.rating]}.main`, ml: "auto" }, children: m.rating })
885
+ ] }),
886
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary", display: "block", mt: 0.25 }, children: HINTS[key] })
887
+ ] }, key);
888
+ })
889
+ ] })
890
+ ]
891
+ }
892
+ );
455
893
  }
456
894
  var pagePerformanceTool = {
457
895
  id: "perf",
458
896
  title: "Page Performance",
459
897
  subtitle: "Web Vitals & fix suggestions",
460
898
  color: "warning",
461
- icon: /* @__PURE__ */ jsx(IconGauge, { size: 19 }),
899
+ icon: /* @__PURE__ */ jsx(LuGauge, { size: 19 }),
462
900
  Panel: PagePerformancePanel,
463
901
  init: installPerf
464
902
  };
465
903
  function registerPagePerformance() {
466
904
  registerTool(pagePerformanceTool);
467
905
  }
468
- function buildProtocolUrl(editor, loc) {
469
- const line = loc.line ?? 1;
470
- const col = loc.column ?? 1;
471
- switch (editor) {
472
- case "cursor":
473
- return `cursor://file/${loc.file}:${line}:${col}`;
474
- case "webstorm":
475
- return `webstorm://open?file=${encodeURIComponent(loc.file)}&line=${line}&column=${col}`;
476
- case "zed":
477
- return `zed://file/${loc.file}:${line}:${col}`;
478
- case "vscode":
479
- case "auto":
480
- default:
481
- return `vscode://file/${loc.file}:${line}:${col}`;
482
- }
483
- }
484
- var defaultOpenInEditor = async (loc, editor = "auto") => {
485
- const url = buildProtocolUrl(editor, loc);
486
- if (url && typeof window !== "undefined") {
487
- try {
488
- window.location.assign(url);
489
- return true;
490
- } catch {
491
- }
492
- }
493
- if (typeof navigator !== "undefined" && navigator.clipboard) {
494
- await navigator.clipboard.writeText(`${loc.file}${loc.line ? `:${loc.line}` : ""}`).catch(() => void 0);
495
- }
496
- return false;
497
- };
498
- var DevPanelContext = createContext(null);
499
- function resolve(config) {
500
- const enabled = config.enabled ?? (typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true);
501
- return {
502
- enabled,
503
- editor: config.editor ?? "auto",
504
- getRoute: config.getRoute ?? (() => typeof location !== "undefined" ? location.pathname : void 0),
505
- openInEditor: config.openInEditor ?? defaultOpenInEditor,
506
- graphEndpoint: config.graphEndpoint,
507
- theme: config.theme ?? {},
508
- tools: config.tools
509
- };
510
- }
511
- function DevPanelConfigProvider({
512
- config,
513
- children
514
- }) {
515
- const value = useMemo(() => resolve(config), [config]);
516
- return /* @__PURE__ */ jsx(DevPanelContext.Provider, { value, children });
517
- }
518
- function useDevPanelConfig() {
519
- const ctx = useContext(DevPanelContext);
520
- if (!ctx) throw new Error("useDevPanelConfig must be used within <DevPanel> / DevPanelConfigProvider");
521
- return ctx;
522
- }
523
906
  function GraphSearch({
524
907
  value,
525
908
  onChange,
526
909
  placeholder = "Search components\u2026"
527
910
  }) {
528
- return /* @__PURE__ */ jsxs("div", { className: "rdp-input", children: [
529
- /* @__PURE__ */ jsx("span", { style: { color: "var(--rdp-text-faint)", display: "grid", placeItems: "center" }, children: /* @__PURE__ */ jsx(IconSearch, { size: 15 }) }),
530
- /* @__PURE__ */ jsx("input", { value, onChange: (e) => onChange(e.target.value), placeholder, "aria-label": "Search components" }),
531
- value && /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: () => onChange(""), "aria-label": "Clear", children: /* @__PURE__ */ jsx(IconX, { size: 14 }) })
532
- ] });
911
+ return /* @__PURE__ */ jsx(
912
+ TextField,
913
+ {
914
+ size: "small",
915
+ fullWidth: true,
916
+ placeholder,
917
+ value,
918
+ onChange: (e) => onChange(e.target.value),
919
+ slotProps: {
920
+ input: {
921
+ startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(LuSearch, { size: 15 }) })
922
+ }
923
+ }
924
+ }
925
+ );
533
926
  }
534
927
  function Chips({ names, onSelect }) {
535
- return /* @__PURE__ */ jsx("span", { style: { display: "flex", flexWrap: "wrap", gap: 4 }, children: names.map((n) => /* @__PURE__ */ jsx(
536
- "button",
537
- {
538
- type: "button",
539
- className: "rdp-chip rdp-mono",
540
- style: { border: "1px solid var(--rdp-border)", background: "transparent", color: "var(--rdp-text)", cursor: "pointer" },
541
- onClick: () => onSelect(n),
542
- children: n
543
- },
544
- n
545
- )) });
928
+ return /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 0.5, useFlexGap: true, sx: { flexWrap: "wrap" }, children: names.map((n) => /* @__PURE__ */ jsx(Chip, { label: n, size: "small", variant: "outlined", onClick: () => onSelect(n), sx: { height: 18, fontSize: "0.62rem", fontFamily: "monospace" } }, n)) });
546
929
  }
547
930
  function Row({ label, children }) {
548
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, marginTop: 5 }, children: [
549
- /* @__PURE__ */ jsx("span", { style: { minWidth: 52, color: "var(--rdp-text-faint)", fontWeight: 600, flexShrink: 0, fontSize: 11 }, children: label }),
550
- /* @__PURE__ */ jsx("span", { style: { minWidth: 0, flex: 1 }, children })
931
+ return /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { mt: 0.5 }, children: [
932
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { minWidth: 56, color: "text.disabled", fontWeight: 600, flexShrink: 0 }, children: label }),
933
+ /* @__PURE__ */ jsx(Box6, { sx: { minWidth: 0, flex: 1 }, children })
551
934
  ] });
552
935
  }
553
936
  function NodeDetails({
@@ -558,57 +941,33 @@ function NodeDetails({
558
941
  onSelectName
559
942
  }) {
560
943
  if (!selected) {
561
- return /* @__PURE__ */ 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." });
944
+ return /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.disabled", px: 0.5, py: 1 }, children: "Nothing selected. Enable inspect mode and click a component, or pick one from the tree." });
562
945
  }
563
- return /* @__PURE__ */ jsxs(
564
- "div",
565
- {
566
- style: {
567
- padding: 10,
568
- borderRadius: 9,
569
- border: "1px solid var(--rdp-border)",
570
- background: "var(--rdp-bg-soft)"
571
- },
572
- children: [
573
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
574
- /* @__PURE__ */ jsx("span", { className: "rdp-chip", style: { background: "rgba(105,80,232,0.22)", color: "var(--rdp-accent)" }, children: selected.componentName }),
575
- selected.domTag && /* @__PURE__ */ jsx("span", { className: "rdp-mono", style: { color: "var(--rdp-text-faint)", fontSize: 11 }, children: `<${selected.domTag}>` })
576
- ] }),
577
- selected.filePath ? /* @__PURE__ */ jsxs("div", { className: "rdp-mono", style: { marginTop: 6, fontSize: 11.5, color: "var(--rdp-text-dim)", wordBreak: "break-all" }, children: [
578
- selected.filePath,
579
- selected.line ? `:${selected.line}` : "",
580
- selected.line && selected.column ? `:${selected.column}` : ""
581
- ] }) : /* @__PURE__ */ 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." }),
582
- selected.route && /* @__PURE__ */ jsx(Row, { label: "Route", children: /* @__PURE__ */ jsx("span", { className: "rdp-mono", style: { fontSize: 12 }, children: selected.route }) }),
583
- selected.parent && /* @__PURE__ */ jsx(Row, { label: "Parent", children: /* @__PURE__ */ jsx(Chips, { names: [selected.parent], onSelect: onSelectName }) }),
584
- selected.children.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Renders", children: /* @__PURE__ */ jsx(Chips, { names: selected.children, onSelect: onSelectName }) }),
585
- selected.imports.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Imports", children: /* @__PURE__ */ jsx(Chips, { names: selected.imports, onSelect: onSelectName }) }),
586
- selected.props && selected.props.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Props", children: /* @__PURE__ */ jsx("span", { className: "rdp-mono", style: { fontSize: 11, color: "var(--rdp-text-dim)" }, children: selected.props.map((p) => /* @__PURE__ */ jsxs("span", { style: { marginRight: 8, display: "inline-block" }, children: [
587
- /* @__PURE__ */ jsx("span", { style: { color: "var(--rdp-accent)" }, children: p.name }),
588
- "=",
589
- p.value
590
- ] }, p.name)) }) }),
591
- /* @__PURE__ */ jsxs("button", { type: "button", className: cx("rdp-btn", "rdp-btn-primary"), style: { marginTop: 12 }, onClick: onOpen, children: [
592
- /* @__PURE__ */ jsx(IconFileCode, { size: 14 }),
593
- " Open in editor"
594
- ] }),
595
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, marginTop: 8 }, children: [
596
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn", onClick: onCopyInfo, title: "Copy component info", children: /* @__PURE__ */ jsx(IconCopy, { size: 15 }) }),
597
- /* @__PURE__ */ jsx(
598
- "button",
599
- {
600
- type: "button",
601
- className: "rdp-iconbtn",
602
- onClick: onCopyPath,
603
- disabled: !selected.filePath && !selected.absFilePath,
604
- title: "Copy file path",
605
- children: /* @__PURE__ */ jsx(IconFileCode, { size: 15 })
606
- }
607
- )
608
- ] })
609
- ]
610
- }
611
- );
946
+ return /* @__PURE__ */ jsxs(Box6, { sx: (t) => ({ p: 1.25, borderRadius: 1.5, border: "1px solid", borderColor: "divider", bgcolor: alpha(t.palette.grey[500], 0.06) }), children: [
947
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.75, children: [
948
+ /* @__PURE__ */ jsx(Chip, { label: selected.componentName, size: "small", color: "primary", variant: "soft", sx: { height: 20, fontSize: "0.68rem", fontWeight: 700 } }),
949
+ selected.domTag && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.disabled", fontFamily: "monospace" }, children: `<${selected.domTag}>` })
950
+ ] }),
951
+ selected.filePath ? /* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { display: "block", mt: 0.75, fontFamily: "monospace", fontSize: "0.68rem", wordBreak: "break-all", color: "text.secondary" }, children: [
952
+ selected.filePath,
953
+ selected.line ? `:${selected.line}` : "",
954
+ selected.line && selected.column ? `:${selected.column}` : ""
955
+ ] }) : /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { display: "block", mt: 0.75, color: "warning.main" }, children: "No source path \u2014 run the graph generator (npx dev-panel-graph) or mount an adapter." }),
956
+ selected.route && /* @__PURE__ */ jsx(Row, { label: "Route", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { fontFamily: "monospace" }, children: selected.route }) }),
957
+ selected.parent && /* @__PURE__ */ jsx(Row, { label: "Parent", children: /* @__PURE__ */ jsx(Chips, { names: [selected.parent], onSelect: onSelectName }) }),
958
+ selected.children.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Renders", children: /* @__PURE__ */ jsx(Chips, { names: selected.children, onSelect: onSelectName }) }),
959
+ selected.imports.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Imports", children: /* @__PURE__ */ jsx(Chips, { names: selected.imports, onSelect: onSelectName }) }),
960
+ selected.props && selected.props.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Props", children: /* @__PURE__ */ jsx(Box6, { sx: { fontFamily: "monospace", fontSize: "0.66rem", color: "text.secondary" }, children: selected.props.map((p) => /* @__PURE__ */ jsxs(Box6, { component: "span", sx: { mr: 1, display: "inline-block" }, children: [
961
+ /* @__PURE__ */ jsx(Box6, { component: "span", sx: { color: "primary.main" }, children: p.name }),
962
+ "=",
963
+ p.value
964
+ ] }, p.name)) }) }),
965
+ /* @__PURE__ */ jsx(Button, { fullWidth: true, variant: "contained", startIcon: /* @__PURE__ */ jsx(LuFileCode, { size: 14 }), onClick: onOpen, sx: { textTransform: "none", fontSize: "0.72rem", py: 0.4, mt: 1.25 }, children: "Open in editor" }),
966
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { mt: 1 }, children: [
967
+ /* @__PURE__ */ jsx(Tooltip2, { title: "Copy component info", placement: "top", children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onCopyInfo, sx: { border: "1px solid", borderColor: "divider", borderRadius: 1 }, children: /* @__PURE__ */ jsx(LuClipboardCopy, { size: 15 }) }) }),
968
+ /* @__PURE__ */ jsx(Tooltip2, { title: "Copy file path", placement: "top", children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onCopyPath, disabled: !selected.filePath && !selected.absFilePath, sx: { border: "1px solid", borderColor: "divider", borderRadius: 1 }, children: /* @__PURE__ */ jsx(LuCopy, { size: 15 }) }) }) })
969
+ ] })
970
+ ] });
612
971
  }
613
972
 
614
973
  // src/tools/component-graph/graph-utils.ts
@@ -875,27 +1234,23 @@ function formatForCopy(s) {
875
1234
  }
876
1235
  var MAX_DEPTH = 6;
877
1236
  function Label({ children }) {
878
- return /* @__PURE__ */ jsx("span", { className: "rdp-section-label", children });
1237
+ return /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { display: "block", mt: 1, mb: 0.25, color: "text.disabled", fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.4, fontSize: "0.6rem" }, children });
879
1238
  }
880
- function RowActions({
881
- node,
882
- onOpen,
883
- onCopy
884
- }) {
885
- return /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", gap: 2 }, children: [
886
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Open in editor", onClick: (e) => {
1239
+ function RowActions({ node, onOpen, onCopy }) {
1240
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1241
+ /* @__PURE__ */ jsx(Tooltip2, { title: "Open in editor", placement: "top", children: /* @__PURE__ */ jsx(IconButton, { size: "small", sx: { p: 0.25 }, onClick: (e) => {
887
1242
  e.stopPropagation();
888
1243
  onOpen(node);
889
- }, children: /* @__PURE__ */ jsx(IconFileCode, { size: 13 }) }),
890
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Copy file path", onClick: (e) => {
1244
+ }, children: /* @__PURE__ */ jsx(LuFileCode, { size: 13 }) }) }),
1245
+ /* @__PURE__ */ jsx(Tooltip2, { title: "Copy file path", placement: "top", children: /* @__PURE__ */ jsx(IconButton, { size: "small", sx: { p: 0.25 }, onClick: (e) => {
891
1246
  e.stopPropagation();
892
1247
  onCopy(node);
893
- }, children: /* @__PURE__ */ jsx(IconCopy, { size: 13 }) })
1248
+ }, children: /* @__PURE__ */ jsx(LuCopy, { size: 13 }) }) })
894
1249
  ] });
895
1250
  }
896
1251
  function RowShell({
897
1252
  depth = 0,
898
- active,
1253
+ active = false,
899
1254
  caret,
900
1255
  label,
901
1256
  isRoute,
@@ -903,42 +1258,43 @@ function RowShell({
903
1258
  actions
904
1259
  }) {
905
1260
  return /* @__PURE__ */ jsxs(
906
- "div",
1261
+ Stack,
907
1262
  {
908
- className: "rdp-row",
909
- style: {
910
- padding: "3px 6px",
911
- paddingLeft: 6 + depth * 14,
912
- gap: 4,
913
- alignItems: "center",
914
- background: active ? "rgba(105,80,232,0.16)" : void 0
915
- },
1263
+ direction: "row",
1264
+ alignItems: "center",
1265
+ spacing: 0.5,
1266
+ sx: (t) => ({
1267
+ pl: 0.5 + depth * 1.5,
1268
+ pr: 0.5,
1269
+ py: 0.25,
1270
+ borderRadius: 1,
1271
+ bgcolor: active ? alpha(t.palette.primary.main, 0.16) : "transparent",
1272
+ "&:hover": { bgcolor: active ? alpha(t.palette.primary.main, 0.2) : "action.hover" },
1273
+ "&:hover .rdp-row-actions": { opacity: 1 }
1274
+ }),
916
1275
  children: [
917
- /* @__PURE__ */ jsx("span", { style: { width: 16, display: "grid", placeItems: "center", color: "var(--rdp-text-faint)" }, children: caret }),
1276
+ /* @__PURE__ */ jsx(Box6, { sx: { width: 16, display: "grid", placeItems: "center", color: "text.disabled" }, children: caret }),
918
1277
  /* @__PURE__ */ jsx(
919
- "button",
1278
+ Typography,
920
1279
  {
921
- type: "button",
922
1280
  onClick: onLabel,
923
- className: "rdp-mono",
924
- style: {
1281
+ variant: "caption",
1282
+ sx: {
925
1283
  flex: 1,
926
1284
  minWidth: 0,
927
- textAlign: "left",
928
- background: "none",
929
- border: "none",
930
1285
  cursor: "pointer",
931
- fontSize: 12,
932
1286
  fontWeight: active ? 700 : 500,
933
- color: isRoute ? "var(--rdp-info)" : active ? "var(--rdp-accent)" : "var(--rdp-text)",
934
- whiteSpace: "nowrap",
1287
+ fontFamily: "monospace",
1288
+ fontSize: "0.72rem",
935
1289
  overflow: "hidden",
936
- textOverflow: "ellipsis"
1290
+ textOverflow: "ellipsis",
1291
+ whiteSpace: "nowrap",
1292
+ color: isRoute ? "info.main" : active ? "primary.main" : "text.primary"
937
1293
  },
938
1294
  children: label
939
1295
  }
940
1296
  ),
941
- actions
1297
+ actions && /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 0.25, className: "rdp-row-actions", sx: { opacity: 0, transition: "opacity 120ms" }, children: actions })
942
1298
  ]
943
1299
  }
944
1300
  );
@@ -950,14 +1306,14 @@ function Branch({ name, depth, trail, p }) {
950
1306
  const hasKids = kids.length > 0 && depth < MAX_DEPTH && !trail.has(id);
951
1307
  const open = p.expanded.has(id);
952
1308
  const active = p.selected?.nodeId === id || p.selected?.componentName === name;
953
- return /* @__PURE__ */ jsxs("div", { children: [
1309
+ return /* @__PURE__ */ jsxs(Box6, { children: [
954
1310
  /* @__PURE__ */ jsx(
955
1311
  RowShell,
956
1312
  {
957
1313
  depth,
958
1314
  active,
959
1315
  isRoute: node?.type === "route",
960
- caret: hasKids ? /* @__PURE__ */ jsx("span", { style: { cursor: "pointer" }, onClick: () => p.onToggle(id), children: open ? /* @__PURE__ */ jsx(IconChevronDown, { size: 13 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 13 }) }) : null,
1316
+ caret: hasKids ? /* @__PURE__ */ jsx(Box6, { sx: { cursor: "pointer", display: "grid", placeItems: "center" }, onClick: () => p.onToggle(id), children: open ? /* @__PURE__ */ jsx(LuChevronDown, { size: 13 }) : /* @__PURE__ */ jsx(LuChevronRight, { size: 13 }) }) : null,
961
1317
  label: name,
962
1318
  onLabel: () => node && p.onSelect(node),
963
1319
  actions: node ? /* @__PURE__ */ jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy }) : null
@@ -970,7 +1326,7 @@ function ComponentGraphTree(p) {
970
1326
  const { graph, selected, search } = p;
971
1327
  if (graph && search.trim()) {
972
1328
  const results = searchNodes(graph, search);
973
- return /* @__PURE__ */ jsxs("div", { children: [
1329
+ return /* @__PURE__ */ jsxs(Box6, { children: [
974
1330
  /* @__PURE__ */ jsxs(Label, { children: [
975
1331
  results.length,
976
1332
  " match",
@@ -987,7 +1343,7 @@ function ComponentGraphTree(p) {
987
1343
  },
988
1344
  node.id
989
1345
  )),
990
- results.length === 0 && /* @__PURE__ */ jsxs("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 8 }, children: [
1346
+ results.length === 0 && /* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { color: "text.disabled", pl: 1 }, children: [
991
1347
  "No components match \u201C",
992
1348
  search,
993
1349
  "\u201D."
@@ -995,9 +1351,9 @@ function ComponentGraphTree(p) {
995
1351
  ] });
996
1352
  }
997
1353
  if (!selected) {
998
- return /* @__PURE__ */ jsx("div", { style: { color: "var(--rdp-text-faint)", padding: "8px 2px" }, children: "Lock a component (hover + click) or search above to explore the tree." });
1354
+ return /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.disabled", px: 0.5, py: 1 }, children: "Lock a component (hover + click) or search above to explore the tree." });
999
1355
  }
1000
- return /* @__PURE__ */ jsxs("div", { children: [
1356
+ return /* @__PURE__ */ jsxs(Box6, { children: [
1001
1357
  selected.parents.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1002
1358
  /* @__PURE__ */ jsx(Label, { children: "Parent chain" }),
1003
1359
  selected.parents.map((parent) => {
@@ -1006,7 +1362,7 @@ function ComponentGraphTree(p) {
1006
1362
  RowShell,
1007
1363
  {
1008
1364
  isRoute: node?.type === "route",
1009
- caret: /* @__PURE__ */ jsx(IconArrowUp, { size: 12 }),
1365
+ caret: /* @__PURE__ */ jsx(LuArrowUp, { size: 12 }),
1010
1366
  label: parent,
1011
1367
  onLabel: () => node && p.onSelect(node),
1012
1368
  actions: node ? /* @__PURE__ */ jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy }) : null
@@ -1016,13 +1372,13 @@ function ComponentGraphTree(p) {
1016
1372
  })
1017
1373
  ] }),
1018
1374
  /* @__PURE__ */ jsx(Label, { children: "Selected" }),
1019
- /* @__PURE__ */ jsx(RowShell, { active: true, label: selected.componentName, onLabel: () => void 0 }),
1375
+ /* @__PURE__ */ jsx(RowShell, { active: true, label: selected.componentName, onLabel: () => void 0, actions: /* @__PURE__ */ jsx(Chip, { size: "small", variant: "soft", color: "primary", label: selected.source, sx: { height: 16, fontSize: "0.55rem", fontWeight: 700 } }) }),
1020
1376
  /* @__PURE__ */ jsxs(Label, { children: [
1021
1377
  "Renders (",
1022
1378
  selected.children.length,
1023
1379
  ")"
1024
1380
  ] }),
1025
- selected.children.length === 0 ? /* @__PURE__ */ jsx("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 20 }, children: "No child components detected." }) : selected.children.map((c) => /* @__PURE__ */ jsx(Branch, { name: c, depth: 0, trail: new Set(selected.nodeId ? [selected.nodeId] : []), p }, `c:${c}`)),
1381
+ selected.children.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.disabled", pl: 1.5 }, children: "No child components detected." }) : selected.children.map((c) => /* @__PURE__ */ jsx(Branch, { name: c, depth: 0, trail: new Set(selected.nodeId ? [selected.nodeId] : []), p }, `c:${c}`)),
1026
1382
  selected.imports.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1027
1383
  /* @__PURE__ */ jsxs(Label, { children: [
1028
1384
  "Imports (",
@@ -1069,56 +1425,60 @@ function ComponentGraphPageList({
1069
1425
  };
1070
1426
  }, [scan, route]);
1071
1427
  if (!graph) {
1072
- return /* @__PURE__ */ 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." });
1428
+ return /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.disabled", px: 0.5, py: 1 }, children: "Load the component graph (Graph tab) to map mounted components to source files." });
1073
1429
  }
1074
1430
  const q = query.trim().toLowerCase();
1075
1431
  const filtered = q ? items.filter((i) => i.name.toLowerCase().includes(q) || i.node.filePath.toLowerCase().includes(q)) : items;
1076
- return /* @__PURE__ */ jsxs("div", { children: [
1077
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 6 }, children: [
1078
- /* @__PURE__ */ jsxs("span", { className: "rdp-sub", children: [
1079
- /* @__PURE__ */ jsx("span", { className: "rdp-mono", style: { color: "var(--rdp-info)" }, children: route ?? "/" }),
1080
- " \xB7",
1432
+ return /* @__PURE__ */ jsxs(Box6, { children: [
1433
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { mb: 0.5 }, children: [
1434
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: [
1435
+ /* @__PURE__ */ jsx(Box6, { component: "span", sx: { fontFamily: "monospace", color: "info.main" }, children: route ?? "/" }),
1081
1436
  " ",
1437
+ "\xB7 ",
1082
1438
  q ? `${filtered.length} / ${items.length}` : items.length,
1083
1439
  " component",
1084
1440
  items.length === 1 ? "" : "s"
1085
1441
  ] }),
1086
- /* @__PURE__ */ jsxs("button", { type: "button", className: "rdp-btn rdp-btn-sm", onClick: scan, children: [
1087
- /* @__PURE__ */ jsx(IconRefresh, { size: 13 }),
1088
- " Rescan"
1089
- ] })
1442
+ /* @__PURE__ */ jsx(Button, { size: "small", startIcon: /* @__PURE__ */ jsx(LuRefreshCw, { size: 13 }), onClick: scan, sx: { textTransform: "none", fontSize: "0.7rem", py: 0.25 }, children: "Rescan" })
1090
1443
  ] }),
1091
- /* @__PURE__ */ jsx("div", { style: { marginBottom: 8 }, children: /* @__PURE__ */ jsx(GraphSearch, { value: query, onChange: setQuery, placeholder: "Filter components on this page\u2026" }) }),
1092
- items.length === 0 ? /* @__PURE__ */ 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__ */ jsxs("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 4 }, children: [
1444
+ /* @__PURE__ */ jsx(Box6, { sx: { mb: 1 }, children: /* @__PURE__ */ jsx(GraphSearch, { value: query, onChange: setQuery, placeholder: "Filter components on this page\u2026" }) }),
1445
+ items.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.disabled", pl: 0.5 }, children: "No graph components detected in the live tree. Try Rescan, or regenerate the graph." }) : filtered.length === 0 ? /* @__PURE__ */ jsxs(Typography, { variant: "caption", sx: { color: "text.disabled", pl: 0.5 }, children: [
1093
1446
  "No components match \u201C",
1094
1447
  query,
1095
1448
  "\u201D."
1096
1449
  ] }) : filtered.map(({ name, node, count }) => /* @__PURE__ */ jsxs(
1097
- "div",
1450
+ Stack,
1098
1451
  {
1099
- className: "rdp-row",
1100
- style: { padding: "4px 6px", alignItems: "center", background: selectedName === name ? "rgba(105,80,232,0.16)" : void 0 },
1452
+ direction: "row",
1453
+ alignItems: "center",
1454
+ spacing: 0.5,
1101
1455
  onClick: () => onSelect(node),
1456
+ sx: (t) => ({
1457
+ px: 0.5,
1458
+ py: 0.4,
1459
+ borderRadius: 1,
1460
+ cursor: "pointer",
1461
+ bgcolor: selectedName === name ? alpha(t.palette.primary.main, 0.16) : "transparent",
1462
+ "&:hover": { bgcolor: "action.hover" },
1463
+ "&:hover .rdp-row-actions": { opacity: 1 }
1464
+ }),
1102
1465
  children: [
1103
- /* @__PURE__ */ jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [
1104
- /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1105
- /* @__PURE__ */ jsx("span", { className: "rdp-mono", style: { fontWeight: 600, fontSize: 12, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: name }),
1106
- count > 1 && /* @__PURE__ */ jsxs("span", { className: "rdp-chip", style: { background: "var(--rdp-bg-soft)", color: "var(--rdp-text-dim)", height: 15 }, children: [
1107
- "\xD7",
1108
- count
1109
- ] })
1466
+ /* @__PURE__ */ jsxs(Box6, { sx: { flex: 1, minWidth: 0 }, children: [
1467
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, children: [
1468
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { fontFamily: "monospace", fontWeight: 600, fontSize: "0.72rem", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: name }),
1469
+ count > 1 && /* @__PURE__ */ jsx(Chip, { label: `\xD7${count}`, size: "small", variant: "soft", sx: { height: 15, fontSize: "0.55rem", "& .MuiChip-label": { px: 0.5 } } })
1110
1470
  ] }),
1111
- /* @__PURE__ */ jsx("span", { style: { display: "block", color: "var(--rdp-text-faint)", fontSize: 11, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: node.filePath })
1471
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { display: "block", color: "text.disabled", fontSize: "0.62rem", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: node.filePath })
1112
1472
  ] }),
1113
- /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", gap: 2 }, children: [
1114
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Open in editor", onClick: (e) => {
1473
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.25, className: "rdp-row-actions", sx: { opacity: 0, transition: "opacity 120ms" }, children: [
1474
+ /* @__PURE__ */ jsx(Tooltip2, { title: "Open in editor", placement: "top", children: /* @__PURE__ */ jsx(IconButton, { size: "small", sx: { p: 0.25 }, onClick: (e) => {
1115
1475
  e.stopPropagation();
1116
1476
  onOpen(node);
1117
- }, children: /* @__PURE__ */ jsx(IconFileCode, { size: 13 }) }),
1118
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", title: "Copy file path", onClick: (e) => {
1477
+ }, children: /* @__PURE__ */ jsx(LuFileCode, { size: 13 }) }) }),
1478
+ /* @__PURE__ */ jsx(Tooltip2, { title: "Copy file path", placement: "top", children: /* @__PURE__ */ jsx(IconButton, { size: "small", sx: { p: 0.25 }, onClick: (e) => {
1119
1479
  e.stopPropagation();
1120
1480
  onCopy(node);
1121
- }, children: /* @__PURE__ */ jsx(IconCopy, { size: 13 }) })
1481
+ }, children: /* @__PURE__ */ jsx(LuCopy, { size: 13 }) }) })
1122
1482
  ] })
1123
1483
  ]
1124
1484
  },
@@ -1220,10 +1580,10 @@ function loadGraph(endpoint, force = false) {
1220
1580
  return graphPromise;
1221
1581
  }
1222
1582
  var TABS = [
1223
- { value: "hover", label: "Hover", icon: /* @__PURE__ */ jsx(IconPointer, { size: 14 }) },
1224
- { value: "graph", label: "Graph", icon: /* @__PURE__ */ jsx(IconGraph, { size: 14 }) },
1225
- { value: "page", label: "Page", icon: /* @__PURE__ */ jsx(IconLayers, { size: 14 }) },
1226
- { value: "file", label: "File", icon: /* @__PURE__ */ jsx(IconFileCode, { size: 14 }) }
1583
+ { value: "hover", label: "Hover", icon: /* @__PURE__ */ jsx(LuMousePointerClick, { size: 14 }) },
1584
+ { value: "graph", label: "Graph", icon: /* @__PURE__ */ jsx(LuWorkflow, { size: 14 }) },
1585
+ { value: "page", label: "Page", icon: /* @__PURE__ */ jsx(LuLayers, { size: 14 }) },
1586
+ { value: "file", label: "File", icon: /* @__PURE__ */ jsx(LuFile, { size: 14 }) }
1227
1587
  ];
1228
1588
  async function copy(text) {
1229
1589
  if (typeof navigator === "undefined" || !navigator.clipboard) return false;
@@ -1234,16 +1594,14 @@ async function copy(text) {
1234
1594
  }
1235
1595
  function ComponentGraphPanel({ onClose }) {
1236
1596
  const state2 = useSyncExternalStore(subscribeGraph, getGraphState, getGraphServerState);
1597
+ const theme = useTheme();
1237
1598
  const config = useDevPanelConfig();
1238
1599
  const route = config.getRoute();
1239
1600
  const { enabled, mode, selected, graph, status, search, expanded } = state2;
1240
1601
  const openLoc = useCallback(
1241
1602
  async (sel) => {
1242
1603
  const file = sel?.absFilePath ?? sel?.filePath;
1243
- if (!file) {
1244
- showToast({ message: "No source path available", tone: "error" });
1245
- return;
1246
- }
1604
+ if (!file) return showToast({ message: "No source path available", tone: "error" });
1247
1605
  const ok = await config.openInEditor?.({ file, line: sel?.line, column: sel?.column }, config.editor);
1248
1606
  showToast(ok === false ? { message: "Editor unavailable \u2014 path copied", tone: "info" } : { message: "Opening in your editor\u2026", tone: "success" });
1249
1607
  },
@@ -1253,14 +1611,11 @@ function ComponentGraphPanel({ onClose }) {
1253
1611
  if (!selected) return;
1254
1612
  void copy(formatForCopy(selected)).then((ok) => showToast({ message: ok ? "Component info copied" : "Copy failed", tone: ok ? "success" : "error" }));
1255
1613
  }, [selected]);
1256
- const copyPath = useCallback(
1257
- (sel) => {
1258
- const path = sel?.filePath ?? sel?.absFilePath;
1259
- if (!path) return showToast({ message: "No path to copy", tone: "info" });
1260
- void copy(`${path}${sel?.line ? `:${sel.line}` : ""}`).then((ok) => showToast({ message: ok ? "File path copied" : "Copy failed", tone: ok ? "success" : "error" }));
1261
- },
1262
- []
1263
- );
1614
+ const copyPath = useCallback((sel) => {
1615
+ const path = sel?.filePath ?? sel?.absFilePath;
1616
+ if (!path) return showToast({ message: "No path to copy", tone: "info" });
1617
+ void copy(`${path}${sel?.line ? `:${sel.line}` : ""}`).then((ok) => showToast({ message: ok ? "File path copied" : "Copy failed", tone: ok ? "success" : "error" }));
1618
+ }, []);
1264
1619
  const selectName = useCallback(
1265
1620
  (name) => {
1266
1621
  const node = graph?.nodes.find((n) => n.name === name || n.id === name);
@@ -1277,130 +1632,113 @@ function ComponentGraphPanel({ onClose }) {
1277
1632
  if (status === "empty") return "Graph not generated \u2014 run `npx dev-panel-graph` (or add an adapter)";
1278
1633
  return "Graph endpoint unavailable.";
1279
1634
  }, [status, graph]);
1280
- const details = /* @__PURE__ */ jsx(
1281
- NodeDetails,
1635
+ const details = /* @__PURE__ */ jsx(NodeDetails, { selected, onOpen: () => void openLoc(selected), onCopyInfo: copyInfo, onCopyPath: () => copyPath(selected), onSelectName: selectName });
1636
+ return /* @__PURE__ */ jsxs(
1637
+ Box6,
1282
1638
  {
1283
- selected,
1284
- onOpen: () => void openLoc(selected),
1285
- onCopyInfo: copyInfo,
1286
- onCopyPath: () => copyPath(selected),
1287
- onSelectName: selectName
1288
- }
1289
- );
1290
- return /* @__PURE__ */ jsxs("div", { className: "rdp-surface rdp-panel", style: { bottom: 88, right: 20 }, children: [
1291
- /* @__PURE__ */ jsxs("div", { className: "rdp-header", children: [
1292
- /* @__PURE__ */ jsx(IconGraph, { size: 16 }),
1293
- /* @__PURE__ */ jsx("span", { className: "rdp-title", style: { flex: 1 }, children: "Component Graph Inspector" }),
1294
- enabled && /* @__PURE__ */ jsx("span", { className: "rdp-chip", style: { background: "var(--rdp-success)", color: "#06210f" }, children: "ON" }),
1295
- /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-iconbtn-bare", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(IconX, { size: 16 }) })
1296
- ] }),
1297
- /* @__PURE__ */ jsxs("div", { className: "rdp-body", children: [
1298
- /* @__PURE__ */ jsxs(
1299
- "div",
1300
- {
1301
- style: {
1302
- padding: 12,
1303
- borderRadius: 9,
1304
- border: `1px solid ${enabled ? "rgba(61,220,132,0.5)" : "var(--rdp-border)"}`,
1305
- background: enabled ? "rgba(61,220,132,0.08)" : "var(--rdp-bg-elev)",
1306
- display: "flex",
1307
- alignItems: "center",
1308
- justifyContent: "space-between"
1309
- },
1310
- children: [
1311
- /* @__PURE__ */ jsxs("span", { children: [
1312
- /* @__PURE__ */ jsx("span", { className: "rdp-title", style: { display: "block" }, children: "Inspect mode" }),
1313
- /* @__PURE__ */ jsx("span", { className: "rdp-sub", children: enabled ? "Hover the UI, click to lock \xB7 Esc to exit" : "Enable, then hover the UI" })
1314
- ] }),
1315
- /* @__PURE__ */ jsx(
1316
- "button",
1317
- {
1318
- type: "button",
1319
- className: cx("rdp-btn", enabled && "rdp-btn-primary"),
1320
- style: { width: "auto" },
1321
- onClick: () => toggleInspector(config.graphEndpoint),
1322
- children: enabled ? "On" : "Enable"
1323
- }
1324
- )
1325
- ]
1326
- }
1327
- ),
1328
- /* @__PURE__ */ jsx("div", { className: "rdp-tabs", style: { marginTop: 12 }, children: TABS.map((t) => /* @__PURE__ */ jsxs(
1329
- "button",
1330
- {
1331
- type: "button",
1332
- className: "rdp-tab",
1333
- "aria-selected": mode === t.value,
1334
- onClick: () => {
1335
- setMode(t.value);
1336
- if (t.value === "graph" || t.value === "page") void loadGraph(config.graphEndpoint);
1337
- },
1338
- children: [
1339
- t.icon,
1340
- t.label
1341
- ]
1342
- },
1343
- t.value
1344
- )) }),
1345
- /* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
1346
- mode === "hover" && /* @__PURE__ */ jsxs("div", { children: [
1347
- /* @__PURE__ */ jsx("span", { className: "rdp-section-label", children: "Last locked component" }),
1348
- details
1639
+ "data-rdp-ignore": "",
1640
+ sx: {
1641
+ position: "fixed",
1642
+ bottom: 96,
1643
+ right: 24,
1644
+ zIndex: (t) => t.zIndex.modal + 1,
1645
+ width: "min(460px, calc(100vw - 32px))",
1646
+ maxHeight: "min(78vh, 720px)",
1647
+ display: "flex",
1648
+ flexDirection: "column",
1649
+ borderRadius: 2,
1650
+ overflow: "hidden",
1651
+ border: "1px solid",
1652
+ borderColor: "divider",
1653
+ bgcolor: "background.paper",
1654
+ boxShadow: 12
1655
+ },
1656
+ children: [
1657
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1.5, py: 1, borderBottom: "1px solid", borderColor: "divider", flexShrink: 0 }, children: [
1658
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [
1659
+ /* @__PURE__ */ jsx(LuWorkflow, { size: 16 }),
1660
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", sx: { fontWeight: 700 }, children: "Component Graph Inspector" }),
1661
+ enabled && /* @__PURE__ */ jsx(Chip, { label: "ON", size: "small", color: "success", variant: "soft", sx: { height: 18, fontSize: "0.6rem", fontWeight: 700 } })
1662
+ ] }),
1663
+ /* @__PURE__ */ jsx(IconButton, { size: "small", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(LuX, { size: 16 }) })
1349
1664
  ] }),
1350
- mode === "graph" && /* @__PURE__ */ jsxs("div", { children: [
1351
- /* @__PURE__ */ jsx("div", { className: "rdp-sub", style: { marginBottom: 6, color: status === "ready" ? "var(--rdp-success)" : "var(--rdp-text-faint)" }, children: statusText }),
1352
- (status === "empty" || status === "error") && /* @__PURE__ */ jsx("button", { type: "button", className: "rdp-btn rdp-btn-sm", style: { marginBottom: 8 }, onClick: () => loadGraph(config.graphEndpoint, true), children: "Retry graph load" }),
1353
- /* @__PURE__ */ jsx(GraphSearch, { value: search, onChange: setSearch }),
1354
- /* @__PURE__ */ jsx("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsx(
1355
- ComponentGraphTree,
1665
+ /* @__PURE__ */ jsxs(Box6, { sx: { flex: 1, overflowY: "auto", p: 1.5 }, children: [
1666
+ /* @__PURE__ */ jsx(
1667
+ Box6,
1356
1668
  {
1357
- graph,
1358
- selected,
1359
- search,
1360
- expanded,
1361
- onSelect: selectNode,
1362
- onToggle: toggleExpanded,
1363
- onOpen: openNode,
1364
- onCopy: copyNode
1669
+ sx: {
1670
+ p: 1.25,
1671
+ borderRadius: 1.5,
1672
+ border: "1px solid",
1673
+ borderColor: enabled ? alpha(theme.palette.success.main, 0.5) : "divider",
1674
+ bgcolor: enabled ? alpha(theme.palette.success.main, 0.08) : "background.default"
1675
+ },
1676
+ children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [
1677
+ /* @__PURE__ */ jsxs(Box6, { children: [
1678
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 700 }, children: "Inspect mode" }),
1679
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: enabled ? "Hover the UI, click to lock \xB7 Esc to exit" : "Enable, then hover the UI" })
1680
+ ] }),
1681
+ /* @__PURE__ */ jsx(Switch, { checked: enabled, onChange: () => toggleInspector(config.graphEndpoint) })
1682
+ ] })
1365
1683
  }
1366
- ) }),
1367
- selected && !search.trim() && /* @__PURE__ */ jsx("div", { style: { marginTop: 12 }, children: details })
1368
- ] }),
1369
- mode === "page" && /* @__PURE__ */ jsxs("div", { children: [
1684
+ ),
1370
1685
  /* @__PURE__ */ jsx(
1371
- ComponentGraphPageList,
1686
+ ToggleButtonGroup,
1372
1687
  {
1373
- graph,
1374
- route,
1375
- selectedName: selected?.componentName,
1376
- onSelect: selectNode,
1377
- onOpen: openNode,
1378
- onCopy: copyNode
1688
+ fullWidth: true,
1689
+ exclusive: true,
1690
+ size: "small",
1691
+ value: mode,
1692
+ onChange: (_, next) => {
1693
+ if (!next) return;
1694
+ setMode(next);
1695
+ if (next === "graph" || next === "page") void loadGraph(config.graphEndpoint);
1696
+ },
1697
+ sx: { mt: 1.5 },
1698
+ children: TABS.map((t) => /* @__PURE__ */ jsxs(ToggleButton, { value: t.value, sx: { textTransform: "none", gap: 0.5, py: 0.5 }, children: [
1699
+ t.icon,
1700
+ t.label
1701
+ ] }, t.value))
1379
1702
  }
1380
1703
  ),
1381
- selected && /* @__PURE__ */ jsx("div", { style: { marginTop: 12 }, children: details })
1382
- ] }),
1383
- mode === "file" && details
1384
- ] })
1385
- ] })
1386
- ] });
1704
+ /* @__PURE__ */ jsx(Divider, { sx: { my: 1.5 } }),
1705
+ mode === "hover" && /* @__PURE__ */ jsxs(Box6, { children: [
1706
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.disabled", fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.4 }, children: "Last locked component" }),
1707
+ /* @__PURE__ */ jsx(Box6, { sx: { mt: 0.5 }, children: details })
1708
+ ] }),
1709
+ mode === "graph" && /* @__PURE__ */ jsxs(Box6, { children: [
1710
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { display: "block", mb: 0.5, color: status === "ready" ? "success.main" : "text.disabled" }, children: statusText }),
1711
+ (status === "empty" || status === "error") && /* @__PURE__ */ jsx(Chip, { size: "small", variant: "outlined", label: "Retry graph load", onClick: () => loadGraph(config.graphEndpoint, true), sx: { mb: 1, height: 22, fontSize: "0.65rem" } }),
1712
+ /* @__PURE__ */ jsx(GraphSearch, { value: search, onChange: setSearch }),
1713
+ /* @__PURE__ */ jsx(Box6, { sx: { mt: 1 }, children: /* @__PURE__ */ jsx(ComponentGraphTree, { graph, selected, search, expanded, onSelect: selectNode, onToggle: toggleExpanded, onOpen: openNode, onCopy: copyNode }) }),
1714
+ selected && !search.trim() && /* @__PURE__ */ jsx(Box6, { sx: { mt: 1.5 }, children: details })
1715
+ ] }),
1716
+ mode === "page" && /* @__PURE__ */ jsxs(Box6, { children: [
1717
+ /* @__PURE__ */ jsx(ComponentGraphPageList, { graph, route, selectedName: selected?.componentName, onSelect: selectNode, onOpen: openNode, onCopy: copyNode }),
1718
+ selected && /* @__PURE__ */ jsx(Box6, { sx: { mt: 1.5 }, children: details })
1719
+ ] }),
1720
+ mode === "file" && details
1721
+ ] })
1722
+ ]
1723
+ }
1724
+ );
1387
1725
  }
1726
+ var TOOLTIP_W = 360;
1388
1727
  function ComponentGraphOverlay() {
1389
1728
  const state2 = useSyncExternalStore(subscribeGraph, getGraphState, getGraphServerState);
1729
+ const theme = useTheme();
1390
1730
  const config = useDevPanelConfig();
1391
- const [hover, setHover] = useState(null);
1392
1731
  const route = config.getRoute();
1393
1732
  const { enabled } = state2;
1394
- const openSelected = useCallback(
1395
- async (file, line, column, name) => {
1733
+ const [hover, setHover] = useState(null);
1734
+ const open = useCallback(
1735
+ async (file, line, column) => {
1396
1736
  if (!file) {
1397
- showToast({ message: "No source path \u2014 generate the graph or use inspect build", tone: "error" });
1737
+ showToast({ message: "No source path \u2014 generate the graph", tone: "error" });
1398
1738
  return;
1399
1739
  }
1400
1740
  const ok = await config.openInEditor?.({ file, line, column }, config.editor);
1401
- showToast(
1402
- ok === false ? { message: `Editor unavailable \u2014 path copied (${name ?? ""})`, tone: "info" } : { message: "Opening in your editor\u2026", tone: "success" }
1403
- );
1741
+ showToast(ok === false ? { message: "Editor unavailable \u2014 path copied", tone: "info" } : { message: "Opening in your editor\u2026", tone: "success" });
1404
1742
  },
1405
1743
  [config]
1406
1744
  );
@@ -1444,11 +1782,8 @@ function ComponentGraphOverlay() {
1444
1782
  e.stopPropagation();
1445
1783
  setSelected(sel);
1446
1784
  setMode("graph");
1447
- if (e.metaKey || e.ctrlKey) {
1448
- void openSelected(sel.absFilePath ?? sel.filePath, sel.line, sel.column, sel.componentName);
1449
- } else {
1450
- showToast({ message: `Locked ${sel.componentName}`, tone: "success" });
1451
- }
1785
+ if (e.metaKey || e.ctrlKey) void open(sel.absFilePath ?? sel.filePath, sel.line, sel.column);
1786
+ else showToast({ message: `Locked ${sel.componentName}`, tone: "success" });
1452
1787
  };
1453
1788
  document.addEventListener("pointermove", onMove, { passive: true });
1454
1789
  document.addEventListener("keydown", onKey, true);
@@ -1460,47 +1795,92 @@ function ComponentGraphOverlay() {
1460
1795
  document.removeEventListener("click", onClick, true);
1461
1796
  setHover(null);
1462
1797
  };
1463
- }, [enabled, route, openSelected]);
1798
+ }, [enabled, route, open]);
1464
1799
  if (!enabled) return null;
1465
- const flipX = typeof window !== "undefined" && hover && hover.x + 374 > window.innerWidth;
1800
+ const flipX = typeof window !== "undefined" && hover && hover.x + TOOLTIP_W + 14 > window.innerWidth;
1466
1801
  const flipY = typeof window !== "undefined" && hover && hover.y + 140 > window.innerHeight;
1467
- return /* @__PURE__ */ jsxs("div", { className: "rdp-overlay", children: [
1802
+ return /* @__PURE__ */ jsxs(Box6, { "data-rdp-ignore": "", sx: { position: "fixed", inset: 0, pointerEvents: "none", zIndex: (t) => t.zIndex.tooltip + 1 }, children: [
1468
1803
  hover && /* @__PURE__ */ jsx(
1469
- "div",
1804
+ Box6,
1470
1805
  {
1471
- className: "rdp-hl",
1472
- style: { top: hover.rect.top, left: hover.rect.left, width: hover.rect.width, height: hover.rect.height }
1806
+ sx: {
1807
+ position: "fixed",
1808
+ top: hover.rect.top,
1809
+ left: hover.rect.left,
1810
+ width: hover.rect.width,
1811
+ height: hover.rect.height,
1812
+ border: "1px solid",
1813
+ borderColor: "primary.main",
1814
+ bgcolor: alpha(theme.palette.primary.main, 0.12),
1815
+ borderRadius: 0.5,
1816
+ boxShadow: `0 0 0 1px ${alpha(theme.palette.primary.main, 0.4)}`,
1817
+ transition: "all 60ms linear"
1818
+ }
1473
1819
  }
1474
1820
  ),
1475
1821
  hover && /* @__PURE__ */ jsxs(
1476
- "div",
1822
+ Box6,
1477
1823
  {
1478
- className: "rdp-tooltip",
1479
- style: {
1824
+ sx: {
1825
+ position: "fixed",
1480
1826
  top: flipY ? void 0 : hover.y + 14,
1481
1827
  bottom: flipY ? window.innerHeight - hover.y + 14 : void 0,
1482
1828
  left: flipX ? void 0 : hover.x + 14,
1483
- right: flipX ? window.innerWidth - hover.x + 14 : void 0
1829
+ right: flipX ? window.innerWidth - hover.x + 14 : void 0,
1830
+ maxWidth: TOOLTIP_W,
1831
+ px: 1.25,
1832
+ py: 1,
1833
+ borderRadius: 1.5,
1834
+ border: "1px solid",
1835
+ borderColor: alpha(theme.palette.common.white, 0.12),
1836
+ bgcolor: alpha(theme.palette.grey[900], 0.85),
1837
+ backdropFilter: "blur(8px)",
1838
+ boxShadow: "0 8px 28px rgba(0,0,0,0.45)",
1839
+ color: theme.palette.common.white
1484
1840
  },
1485
1841
  children: [
1486
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: hover.filePath ? 4 : 0 }, children: [
1487
- /* @__PURE__ */ jsx("span", { className: "rdp-chip", style: { background: "rgba(105,80,232,0.5)", color: "#fff" }, children: hover.name }),
1488
- /* @__PURE__ */ jsx("span", { className: "rdp-mono", style: { color: "rgba(255,255,255,0.6)", fontSize: 11 }, children: `<${hover.domTag}>` })
1842
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.75, sx: { mb: hover.filePath ? 0.5 : 0 }, children: [
1843
+ /* @__PURE__ */ jsx(
1844
+ Chip,
1845
+ {
1846
+ label: hover.name,
1847
+ size: "small",
1848
+ sx: { height: 18, fontWeight: 700, fontSize: "0.65rem", color: "#fff", bgcolor: alpha(theme.palette.primary.main, 0.5) }
1849
+ }
1850
+ ),
1851
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: alpha(theme.palette.common.white, 0.6), fontFamily: "monospace" }, children: `<${hover.domTag}>` })
1489
1852
  ] }),
1490
- /* @__PURE__ */ 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" }),
1491
- hover.route && /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.45)" }, children: hover.route }),
1492
- /* @__PURE__ */ 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" })
1853
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { display: "block", fontFamily: "monospace", fontSize: "0.68rem", wordBreak: "break-all", color: hover.filePath ? alpha(theme.palette.common.white, 0.85) : alpha(theme.palette.warning.light, 0.9) }, children: hover.filePath ? `${hover.filePath}${hover.line ? `:${hover.line}` : ""}` : "source resolved from graph on lock" }),
1854
+ hover.route && /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { display: "block", fontSize: "0.62rem", color: alpha(theme.palette.common.white, 0.45) }, children: hover.route }),
1855
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { display: "block", mt: 0.5, fontSize: "0.6rem", color: alpha(theme.palette.common.white, 0.5) }, children: "click to lock \xB7 \u2318/Ctrl + click to open \xB7 Esc to exit" })
1493
1856
  ]
1494
1857
  }
1495
1858
  ),
1496
1859
  state2.toast && /* @__PURE__ */ jsx(
1497
- "div",
1860
+ Box6,
1498
1861
  {
1499
- className: "rdp-toast",
1500
- style: {
1501
- color: state2.toast.tone === "success" ? "var(--rdp-success)" : state2.toast.tone === "error" ? "var(--rdp-error)" : "#fff"
1862
+ sx: {
1863
+ position: "fixed",
1864
+ bottom: 24,
1865
+ left: "50%",
1866
+ transform: "translateX(-50%)",
1867
+ px: 1.5,
1868
+ py: 0.75,
1869
+ borderRadius: 1.5,
1870
+ border: "1px solid",
1871
+ borderColor: alpha(theme.palette.common.white, 0.12),
1872
+ bgcolor: alpha(theme.palette.grey[900], 0.9),
1873
+ backdropFilter: "blur(8px)",
1874
+ boxShadow: "0 8px 28px rgba(0,0,0,0.45)"
1502
1875
  },
1503
- children: state2.toast.message
1876
+ children: /* @__PURE__ */ jsx(
1877
+ Typography,
1878
+ {
1879
+ variant: "caption",
1880
+ sx: { fontWeight: 600, color: state2.toast.tone === "success" ? "success.light" : state2.toast.tone === "error" ? "error.light" : "common.white" },
1881
+ children: state2.toast.message
1882
+ }
1883
+ )
1504
1884
  }
1505
1885
  )
1506
1886
  ] });
@@ -1510,191 +1890,237 @@ var componentGraphTool = {
1510
1890
  title: "Component Graph Inspector",
1511
1891
  subtitle: "Inspect component tree & open source files",
1512
1892
  color: "primary",
1513
- icon: /* @__PURE__ */ jsx(IconGraph, { size: 19 }),
1893
+ icon: /* @__PURE__ */ jsx(LuWorkflow, { size: 19 }),
1514
1894
  Panel: ComponentGraphPanel,
1515
1895
  Overlay: ComponentGraphOverlay
1516
1896
  };
1517
1897
  function registerComponentGraph() {
1518
1898
  registerTool(componentGraphTool);
1519
1899
  }
1520
- var STORAGE_KEY = "react-dev-panel:corner";
1521
- var GAP = 20;
1522
- var FAB = 52;
1900
+ function ToolTile({ color, size = 38, children }) {
1901
+ return /* @__PURE__ */ jsx(
1902
+ Box6,
1903
+ {
1904
+ sx: (theme) => ({
1905
+ width: size,
1906
+ height: size,
1907
+ flexShrink: 0,
1908
+ borderRadius: 1.5,
1909
+ display: "grid",
1910
+ placeItems: "center",
1911
+ color: theme.palette[color].main,
1912
+ bgcolor: alpha(theme.palette[color].main, 0.14)
1913
+ }),
1914
+ children
1915
+ }
1916
+ );
1917
+ }
1918
+ var STORAGE_KEY2 = "react-dev-panel:corner";
1919
+ var EDGE_GAP = 24;
1920
+ var FAB_SIZE = 56;
1523
1921
  var DRAG_THRESHOLD = 5;
1524
1922
  function isCorner(v) {
1525
1923
  return v === "top-left" || v === "top-right" || v === "bottom-left" || v === "bottom-right";
1526
1924
  }
1527
- function readCorner() {
1925
+ function readStoredCorner() {
1528
1926
  if (typeof window === "undefined") return "bottom-right";
1529
1927
  try {
1530
- const s = window.localStorage.getItem(STORAGE_KEY);
1531
- if (isCorner(s)) return s;
1928
+ const stored = window.localStorage.getItem(STORAGE_KEY2);
1929
+ if (isCorner(stored)) return stored;
1532
1930
  } catch {
1533
1931
  }
1534
1932
  return "bottom-right";
1535
1933
  }
1536
1934
  function useDraggableCorner() {
1537
1935
  const [corner, setCorner] = useState("bottom-right");
1538
- const [drag, setDrag] = useState(null);
1539
- const moved = useRef(false);
1540
- const offset = useRef({ x: 0, y: 0 });
1541
- useEffect(() => setCorner(readCorner()), []);
1542
- const onPointerDown = useCallback((e) => {
1543
- if (e.button !== 0) return;
1544
- const rect = e.currentTarget.getBoundingClientRect();
1545
- offset.current = { x: e.clientX - rect.left, y: e.clientY - rect.top };
1546
- moved.current = false;
1547
- const sx = e.clientX;
1548
- const sy = e.clientY;
1549
- const move = (ev) => {
1550
- if (!moved.current && Math.hypot(ev.clientX - sx, ev.clientY - sy) < DRAG_THRESHOLD) return;
1551
- moved.current = true;
1552
- setDrag({ x: ev.clientX - offset.current.x, y: ev.clientY - offset.current.y });
1936
+ const [dragPos, setDragPos] = useState(null);
1937
+ const movedRef = useRef(false);
1938
+ const offsetRef = useRef({ x: 0, y: 0 });
1939
+ useEffect(() => setCorner(readStoredCorner()), []);
1940
+ const onPointerDown = useCallback((event) => {
1941
+ if (event.button !== 0) return;
1942
+ const rect = event.currentTarget.getBoundingClientRect();
1943
+ offsetRef.current = { x: event.clientX - rect.left, y: event.clientY - rect.top };
1944
+ movedRef.current = false;
1945
+ const startX = event.clientX;
1946
+ const startY = event.clientY;
1947
+ const handleMove = (ev) => {
1948
+ if (!movedRef.current && Math.hypot(ev.clientX - startX, ev.clientY - startY) < DRAG_THRESHOLD) return;
1949
+ movedRef.current = true;
1950
+ setDragPos({ x: ev.clientX - offsetRef.current.x, y: ev.clientY - offsetRef.current.y });
1553
1951
  };
1554
- const up = (ev) => {
1555
- window.removeEventListener("pointermove", move);
1556
- window.removeEventListener("pointerup", up);
1557
- if (moved.current) {
1558
- const cxp = ev.clientX - offset.current.x + FAB / 2;
1559
- const cyp = ev.clientY - offset.current.y + FAB / 2;
1560
- const next = `${cyp < window.innerHeight / 2 ? "top" : "bottom"}-${cxp < window.innerWidth / 2 ? "left" : "right"}`;
1952
+ const handleUp = (ev) => {
1953
+ window.removeEventListener("pointermove", handleMove);
1954
+ window.removeEventListener("pointerup", handleUp);
1955
+ if (movedRef.current) {
1956
+ const cx = ev.clientX - offsetRef.current.x + FAB_SIZE / 2;
1957
+ const cy = ev.clientY - offsetRef.current.y + FAB_SIZE / 2;
1958
+ const next = `${cy < window.innerHeight / 2 ? "top" : "bottom"}-${cx < window.innerWidth / 2 ? "left" : "right"}`;
1561
1959
  setCorner(next);
1562
1960
  try {
1563
- window.localStorage.setItem(STORAGE_KEY, next);
1961
+ window.localStorage.setItem(STORAGE_KEY2, next);
1564
1962
  } catch {
1565
1963
  }
1566
1964
  }
1567
- setDrag(null);
1965
+ setDragPos(null);
1568
1966
  };
1569
- window.addEventListener("pointermove", move);
1570
- window.addEventListener("pointerup", up);
1967
+ window.addEventListener("pointermove", handleMove);
1968
+ window.addEventListener("pointerup", handleUp);
1571
1969
  }, []);
1572
- const onClickCapture = useCallback((e) => {
1573
- if (moved.current) {
1574
- e.stopPropagation();
1575
- e.preventDefault();
1576
- moved.current = false;
1970
+ const onClickCapture = useCallback((event) => {
1971
+ if (movedRef.current) {
1972
+ event.stopPropagation();
1973
+ event.preventDefault();
1974
+ movedRef.current = false;
1577
1975
  }
1578
1976
  }, []);
1977
+ const dragging = dragPos !== null;
1579
1978
  const isTop = corner.startsWith("top");
1580
1979
  const isLeft = corner.endsWith("left");
1581
- const pos = drag ? { top: drag.y, left: drag.x } : {
1582
- top: isTop ? GAP : void 0,
1583
- bottom: isTop ? void 0 : GAP,
1584
- left: isLeft ? GAP : void 0,
1585
- right: isLeft ? void 0 : GAP
1980
+ const positionSx = dragging ? { top: dragPos.y, left: dragPos.x, right: "auto", bottom: "auto" } : {
1981
+ top: isTop ? EDGE_GAP : "auto",
1982
+ bottom: isTop ? "auto" : EDGE_GAP,
1983
+ left: isLeft ? EDGE_GAP : "auto",
1984
+ right: isLeft ? "auto" : EDGE_GAP
1586
1985
  };
1587
- return { pos, isTop, isLeft, onPointerDown, onClickCapture };
1986
+ return { positionSx, onPointerDown, onClickCapture, isTop, isLeft, dragging };
1588
1987
  }
1589
1988
  function ToolBadge({ tool }) {
1590
1989
  const badge = tool.useBadge?.();
1591
1990
  if (!badge) return null;
1592
- const bg = badge.tone === "error" ? "var(--rdp-error)" : badge.tone === "success" ? "var(--rdp-success)" : "var(--rdp-bg-soft)";
1593
- const color = badge.tone === "success" ? "#06210f" : "#fff";
1594
- return /* @__PURE__ */ jsx("span", { className: "rdp-chip", style: { background: bg, color }, children: badge.label });
1991
+ const color = badge.tone === "error" ? "error" : badge.tone === "success" ? "success" : "default";
1992
+ return /* @__PURE__ */ jsx(
1993
+ Chip,
1994
+ {
1995
+ size: "small",
1996
+ variant: "soft",
1997
+ color,
1998
+ label: badge.label,
1999
+ sx: { height: 18, fontWeight: 700, fontSize: "0.65rem", "& .MuiChip-label": { px: 0.75 } }
2000
+ }
2001
+ );
1595
2002
  }
1596
- function Launcher({
1597
- tools: tools2,
1598
- onOpenTool
1599
- }) {
1600
- const { pos, isTop, isLeft, onPointerDown, onClickCapture } = useDraggableCorner();
1601
- const [menuOpen, setMenuOpen] = useState(false);
1602
- const menuPos = {
1603
- top: isTop ? pos.top !== void 0 ? pos.top + FAB + 8 : GAP + FAB + 8 : void 0,
1604
- bottom: isTop ? void 0 : GAP + FAB + 8,
1605
- left: isLeft ? GAP : void 0,
1606
- right: isLeft ? void 0 : GAP
1607
- };
2003
+ function Launcher({ tools: tools2, onOpenTool }) {
2004
+ const { positionSx, onPointerDown, onClickCapture, isTop, isLeft, dragging } = useDraggableCorner();
2005
+ const [menuAnchor, setMenuAnchor] = useState(null);
1608
2006
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1609
2007
  /* @__PURE__ */ jsx(
1610
- "button",
2008
+ Box6,
1611
2009
  {
1612
- type: "button",
1613
- className: "rdp-fab",
1614
- style: pos,
1615
- onPointerDown,
1616
- onClickCapture,
1617
- onClick: () => setMenuOpen((v) => !v),
1618
- "aria-label": "Open Developer Tools",
1619
- "aria-haspopup": "menu",
1620
- children: /* @__PURE__ */ jsx(IconWrench, { size: 22 })
2010
+ "data-rdp-ignore": "",
2011
+ sx: (theme) => ({ position: "fixed", zIndex: theme.zIndex.modal + 2, ...positionSx }),
2012
+ children: /* @__PURE__ */ jsx(Tooltip2, { title: dragging ? "" : "Developer Tools \u2014 drag to a corner", placement: isLeft ? "right" : "left", arrow: true, children: /* @__PURE__ */ jsx(
2013
+ Fab,
2014
+ {
2015
+ onClick: (e) => setMenuAnchor(e.currentTarget),
2016
+ onClickCapture,
2017
+ onPointerDown,
2018
+ "aria-label": "Open Developer Tools",
2019
+ "aria-haspopup": "menu",
2020
+ sx: (theme) => ({
2021
+ width: FAB_SIZE,
2022
+ height: FAB_SIZE,
2023
+ touchAction: "none",
2024
+ cursor: dragging ? "grabbing" : "grab",
2025
+ color: theme.palette.primary.contrastText,
2026
+ background: `linear-gradient(135deg, ${theme.palette.primary.main}, ${theme.palette.primary.dark})`,
2027
+ boxShadow: `0 4px 12px ${alpha(theme.palette.primary.main, 0.24)}, 0 8px 24px rgba(0,0,0,0.5)`,
2028
+ "&:hover": {
2029
+ background: `linear-gradient(135deg, ${theme.palette.primary.dark}, ${theme.palette.primary.dark})`
2030
+ }
2031
+ }),
2032
+ children: /* @__PURE__ */ jsx(LuWrench, { size: 22 })
2033
+ }
2034
+ ) })
1621
2035
  }
1622
2036
  ),
1623
- menuOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
1624
- /* @__PURE__ */ jsx(
1625
- "div",
1626
- {
1627
- style: { position: "fixed", inset: 0, zIndex: 1 },
1628
- onClick: () => setMenuOpen(false),
1629
- "aria-hidden": true
1630
- }
1631
- ),
1632
- /* @__PURE__ */ jsxs("div", { className: "rdp-surface rdp-menu", style: menuPos, role: "menu", children: [
1633
- /* @__PURE__ */ jsxs("div", { className: "rdp-header rdp-menu-head", children: [
1634
- /* @__PURE__ */ jsx("span", { className: "rdp-tile", style: { color: "var(--rdp-accent)", background: "rgba(105,80,232,0.16)" }, children: /* @__PURE__ */ jsx(IconWrench, { size: 17 }) }),
1635
- /* @__PURE__ */ jsxs("div", { children: [
1636
- /* @__PURE__ */ jsx("div", { className: "rdp-title", children: "Developer Tools" }),
1637
- /* @__PURE__ */ jsx("div", { className: "rdp-sub", children: "Internal utilities" })
1638
- ] })
1639
- ] }),
1640
- /* @__PURE__ */ jsx("div", { style: { padding: 8 }, children: tools2.map((tool) => /* @__PURE__ */ jsxs(
1641
- "button",
1642
- {
1643
- type: "button",
1644
- className: "rdp-row",
1645
- role: "menuitem",
1646
- onClick: () => {
1647
- onOpenTool(tool.id);
1648
- setMenuOpen(false);
1649
- },
1650
- children: [
1651
- /* @__PURE__ */ jsx(
1652
- "span",
1653
- {
1654
- className: "rdp-tile",
1655
- style: { color: colorVar(tool.color), background: "rgba(148,163,184,0.1)" },
1656
- children: tool.icon
1657
- }
1658
- ),
1659
- /* @__PURE__ */ jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [
1660
- /* @__PURE__ */ jsx("span", { className: "rdp-title", style: { display: "block" }, children: tool.title }),
1661
- /* @__PURE__ */ jsx("span", { className: "rdp-sub", style: { display: "block" }, children: tool.subtitle })
1662
- ] }),
1663
- /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, color: "var(--rdp-text-faint)" }, children: [
1664
- /* @__PURE__ */ jsx(ToolBadge, { tool }),
1665
- /* @__PURE__ */ jsx(IconChevronRight, { size: 16 })
1666
- ] })
1667
- ]
2037
+ /* @__PURE__ */ jsxs(
2038
+ Menu,
2039
+ {
2040
+ anchorEl: menuAnchor,
2041
+ open: Boolean(menuAnchor),
2042
+ onClose: () => setMenuAnchor(null),
2043
+ sx: (theme) => ({ zIndex: theme.zIndex.modal + 2 }),
2044
+ anchorOrigin: { vertical: isTop ? "bottom" : "top", horizontal: isLeft ? "left" : "right" },
2045
+ transformOrigin: { vertical: isTop ? "top" : "bottom", horizontal: isLeft ? "left" : "right" },
2046
+ slotProps: {
2047
+ paper: {
2048
+ sx: {
2049
+ width: 320,
2050
+ mt: isTop ? 1.5 : 0,
2051
+ mb: isTop ? 0 : 1.5,
2052
+ borderRadius: 2.5,
2053
+ overflow: "hidden",
2054
+ border: "1px solid",
2055
+ borderColor: "divider",
2056
+ boxShadow: 16
2057
+ }
1668
2058
  },
1669
- tool.id
1670
- )) })
1671
- ] })
1672
- ] })
2059
+ list: { sx: { py: 0 } }
2060
+ },
2061
+ children: [
2062
+ /* @__PURE__ */ jsxs(
2063
+ Box6,
2064
+ {
2065
+ sx: {
2066
+ display: "flex",
2067
+ alignItems: "center",
2068
+ gap: 1.25,
2069
+ px: 2,
2070
+ py: 1.5,
2071
+ background: (theme) => `linear-gradient(135deg, ${alpha(theme.palette.primary.main, 0.16)}, ${alpha(theme.palette.primary.main, 0.04)})`
2072
+ },
2073
+ children: [
2074
+ /* @__PURE__ */ jsx(ToolTile, { color: "primary", size: 34, children: /* @__PURE__ */ jsx(LuWrench, { size: 17 }) }),
2075
+ /* @__PURE__ */ jsxs(Box6, { sx: { minWidth: 0 }, children: [
2076
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", sx: { fontWeight: 700, lineHeight: 1.2 }, children: "Developer Tools" }),
2077
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary" }, children: "Internal utilities" })
2078
+ ] })
2079
+ ]
2080
+ }
2081
+ ),
2082
+ /* @__PURE__ */ jsx(Divider, {}),
2083
+ /* @__PURE__ */ jsx(Box6, { sx: { p: 1 }, children: tools2.map((tool, i) => /* @__PURE__ */ jsxs(
2084
+ MenuItem,
2085
+ {
2086
+ onClick: () => {
2087
+ onOpenTool(tool.id);
2088
+ setMenuAnchor(null);
2089
+ },
2090
+ disableGutters: true,
2091
+ sx: { alignItems: "flex-start", gap: 1.25, px: 1.25, py: 1.25, mt: i === 0 ? 0 : 0.5, borderRadius: 1.5, whiteSpace: "normal" },
2092
+ children: [
2093
+ /* @__PURE__ */ jsx(ToolTile, { color: tool.color ?? "primary", children: tool.icon }),
2094
+ /* @__PURE__ */ jsxs(Box6, { sx: { flex: 1, minWidth: 0 }, children: [
2095
+ /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", sx: { fontWeight: 600, lineHeight: 1.3 }, children: tool.title }),
2096
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", sx: { color: "text.secondary", display: "block" }, children: tool.subtitle })
2097
+ ] }),
2098
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", sx: { mt: 0.25 }, children: [
2099
+ /* @__PURE__ */ jsx(ToolBadge, { tool }),
2100
+ /* @__PURE__ */ jsx(Box6, { sx: { display: "grid", placeItems: "center", color: "text.disabled" }, children: /* @__PURE__ */ jsx(LuChevronRight, { size: 16 }) })
2101
+ ] })
2102
+ ]
2103
+ },
2104
+ tool.id
2105
+ )) })
2106
+ ]
2107
+ }
2108
+ )
1673
2109
  ] });
1674
2110
  }
1675
2111
  function DevPanel(config) {
1676
2112
  const enabled = config.enabled ?? (typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true);
1677
- useEffect(() => {
1678
- if (enabled) injectBaseStyles();
1679
- }, [enabled]);
1680
2113
  if (!enabled) return null;
1681
- return /* @__PURE__ */ jsx(DevPanelConfigProvider, { config, children: /* @__PURE__ */ jsx(DevPanelInner, { ids: config.tools, theme: config.theme }) });
2114
+ return /* @__PURE__ */ jsx(DevPanelConfigProvider, { config, children: /* @__PURE__ */ jsx(DevPanelInner, { ids: config.tools }) });
1682
2115
  }
1683
- function DevPanelInner({
1684
- ids,
1685
- theme
1686
- }) {
2116
+ function DevPanelInner({ ids }) {
1687
2117
  const tools2 = useMemo(() => resolveTools(ids), [ids]);
1688
2118
  const [openId, setOpenId] = useState(null);
1689
2119
  useEffect(() => {
1690
2120
  tools2.forEach((t) => t.init?.());
1691
2121
  }, [tools2]);
1692
- const rootStyle = {};
1693
- if (theme?.accent) rootStyle["--rdp-accent"] = theme.accent;
1694
- if (theme?.accentContrast)
1695
- rootStyle["--rdp-accent-contrast"] = theme.accentContrast;
1696
2122
  const ActivePanel = tools2.find((t) => t.id === openId)?.Panel ?? null;
1697
- return /* @__PURE__ */ jsxs("div", { className: "rdp-root", style: rootStyle, "data-rdp-ignore": "", children: [
2123
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1698
2124
  /* @__PURE__ */ jsx(Launcher, { tools: tools2, onOpenTool: setOpenId }),
1699
2125
  ActivePanel && /* @__PURE__ */ jsx(ActivePanel, { onClose: () => setOpenId(null) }),
1700
2126
  tools2.map((t) => t.Overlay ? /* @__PURE__ */ jsx(t.Overlay, {}, t.id) : null)
@@ -1706,6 +2132,6 @@ registerDevLogs();
1706
2132
  registerPagePerformance();
1707
2133
  registerComponentGraph();
1708
2134
 
1709
- export { DevPanel, defaultOpenInEditor, getRegisteredTools, injectBaseStyles, registerTool, useDevPanelConfig };
2135
+ export { DevPanel, defaultOpenInEditor, getRegisteredTools, registerTool, useDevPanelConfig };
1710
2136
  //# sourceMappingURL=index.js.map
1711
2137
  //# sourceMappingURL=index.js.map