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