react-dev-panel 0.1.0 → 0.2.1

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 Typography3 = 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 Typography3__default = /*#__PURE__*/_interopDefault(Typography3);
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));
59
+ }
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
+ }
67
75
  }
68
- .rdp-menu-head {
69
- background: linear-gradient(135deg, rgba(105,80,232,0.18), rgba(105,80,232,0.04));
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
+ };
70
102
  }
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;
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 });
80
109
  }
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;
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;
85
114
  }
86
115
 
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;
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);
118
136
  }
119
- .rdp-tab[aria-selected="true"] { background: var(--rdp-bg-elev); color: var(--rdp-text); box-shadow: 0 1px 2px rgba(0,0,0,0.3); }
120
-
121
- .rdp-input {
122
- display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 9px;
123
- border: 1px solid var(--rdp-border); background: var(--rdp-bg-soft);
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
+ }
270
304
  }
271
- function stringify(args) {
272
- return args.map((a) => {
273
- if (typeof a === "string") return a;
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
+ }
312
+ }
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(Typography3__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
+ )
654
+ ] });
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
+ Typography3__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(Typography3__default.default, { variant: "caption", sx: { color: "text.disabled", fontFamily: "monospace" }, children: formatTime(entry.timestamp) }),
702
+ showPath && entry.path && /* @__PURE__ */ jsxRuntime.jsx(Typography3__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)) }) })
359
710
  ] });
360
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(Typography3__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(Typography3__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,195 +848,171 @@ 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(Typography3__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(Typography3__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(Typography3__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(Typography3__default.default, { variant: "caption", sx: { color: `${RATING_COLOR[m.rating]}.main`, ml: "auto" }, children: m.rating })
908
+ ] }),
909
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__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
  }
950
+ var EDITORS = [
951
+ { key: "vscode", label: "VS Code" },
952
+ { key: "cursor", label: "Cursor" },
953
+ { key: "webstorm", label: "WebStorm" }
954
+ ];
537
955
  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
- )) });
956
+ 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
957
  }
550
958
  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 })
959
+ return /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", spacing: 1, sx: { mt: 0.5 }, children: [
960
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { minWidth: 56, color: "text.disabled", fontWeight: 600, flexShrink: 0 }, children: label }),
961
+ /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { sx: { minWidth: 0, flex: 1 }, children })
554
962
  ] });
555
963
  }
556
964
  function NodeDetails({
557
965
  selected,
966
+ editor,
558
967
  onOpen,
968
+ onOpenEditor,
559
969
  onCopyInfo,
560
970
  onCopyPath,
561
971
  onSelectName
562
972
  }) {
563
973
  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." });
974
+ return /* @__PURE__ */ jsxRuntime.jsx(Typography3__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
975
  }
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
- );
976
+ 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: [
977
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", alignItems: "center", spacing: 0.75, children: [
978
+ /* @__PURE__ */ jsxRuntime.jsx(Chip__default.default, { label: selected.componentName, size: "small", color: "primary", variant: "soft", sx: { height: 20, fontSize: "0.68rem", fontWeight: 700 } }),
979
+ selected.domTag && /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { color: "text.disabled", fontFamily: "monospace" }, children: `<${selected.domTag}>` })
980
+ ] }),
981
+ selected.filePath ? /* @__PURE__ */ jsxRuntime.jsxs(Typography3__default.default, { variant: "caption", sx: { display: "block", mt: 0.75, fontFamily: "monospace", fontSize: "0.68rem", wordBreak: "break-all", color: "text.secondary" }, children: [
982
+ selected.filePath,
983
+ selected.line ? `:${selected.line}` : "",
984
+ selected.line && selected.column ? `:${selected.column}` : ""
985
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(Typography3__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." }),
986
+ selected.route && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Route", children: /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { fontFamily: "monospace" }, children: selected.route }) }),
987
+ selected.parent && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Parent", children: /* @__PURE__ */ jsxRuntime.jsx(Chips, { names: [selected.parent], onSelect: onSelectName }) }),
988
+ selected.children.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Renders", children: /* @__PURE__ */ jsxRuntime.jsx(Chips, { names: selected.children, onSelect: onSelectName }) }),
989
+ selected.imports.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Imports", children: /* @__PURE__ */ jsxRuntime.jsx(Chips, { names: selected.imports, onSelect: onSelectName }) }),
990
+ 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: [
991
+ /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { component: "span", sx: { color: "primary.main" }, children: p.name }),
992
+ "=",
993
+ p.value
994
+ ] }, p.name)) }) }),
995
+ /* @__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" }),
996
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", alignItems: "center", spacing: 0.5, useFlexGap: true, sx: { flexWrap: "wrap", mt: 0.75 }, children: [
997
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { color: "text.disabled" }, children: "force:" }),
998
+ EDITORS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
999
+ Button__default.default,
1000
+ {
1001
+ size: "small",
1002
+ variant: opt.key === editor ? "contained" : "text",
1003
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(lu.LuLink, { size: 12 }),
1004
+ onClick: () => onOpenEditor(opt.key),
1005
+ sx: { textTransform: "none", fontSize: "0.66rem", py: 0.1, minWidth: 0 },
1006
+ children: opt.label
1007
+ },
1008
+ opt.key
1009
+ ))
1010
+ ] }),
1011
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", spacing: 1, sx: { mt: 1 }, children: [
1012
+ /* @__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 }) }) }),
1013
+ /* @__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 }) }) }) })
1014
+ ] })
1015
+ ] });
615
1016
  }
616
1017
 
617
1018
  // src/tools/component-graph/graph-utils.ts
@@ -878,27 +1279,23 @@ function formatForCopy(s) {
878
1279
  }
879
1280
  var MAX_DEPTH = 6;
880
1281
  function Label({ children }) {
881
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rdp-section-label", children });
1282
+ return /* @__PURE__ */ jsxRuntime.jsx(Typography3__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
1283
  }
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) => {
1284
+ function RowActions({ node, onOpen, onCopy }) {
1285
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1286
+ /* @__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
1287
  e.stopPropagation();
891
1288
  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) => {
1289
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuFileCode, { size: 13 }) }) }),
1290
+ /* @__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
1291
  e.stopPropagation();
895
1292
  onCopy(node);
896
- }, children: /* @__PURE__ */ jsxRuntime.jsx(IconCopy, { size: 13 }) })
1293
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { size: 13 }) }) })
897
1294
  ] });
898
1295
  }
899
1296
  function RowShell({
900
1297
  depth = 0,
901
- active,
1298
+ active = false,
902
1299
  caret,
903
1300
  label,
904
1301
  isRoute,
@@ -906,42 +1303,43 @@ function RowShell({
906
1303
  actions
907
1304
  }) {
908
1305
  return /* @__PURE__ */ jsxRuntime.jsxs(
909
- "div",
1306
+ Stack__default.default,
910
1307
  {
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
- },
1308
+ direction: "row",
1309
+ alignItems: "center",
1310
+ spacing: 0.5,
1311
+ sx: (t) => ({
1312
+ pl: 0.5 + depth * 1.5,
1313
+ pr: 0.5,
1314
+ py: 0.25,
1315
+ borderRadius: 1,
1316
+ bgcolor: active ? styles.alpha(t.palette.primary.main, 0.16) : "transparent",
1317
+ "&:hover": { bgcolor: active ? styles.alpha(t.palette.primary.main, 0.2) : "action.hover" },
1318
+ "&:hover .rdp-row-actions": { opacity: 1 }
1319
+ }),
919
1320
  children: [
920
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: 16, display: "grid", placeItems: "center", color: "var(--rdp-text-faint)" }, children: caret }),
1321
+ /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { sx: { width: 16, display: "grid", placeItems: "center", color: "text.disabled" }, children: caret }),
921
1322
  /* @__PURE__ */ jsxRuntime.jsx(
922
- "button",
1323
+ Typography3__default.default,
923
1324
  {
924
- type: "button",
925
1325
  onClick: onLabel,
926
- className: "rdp-mono",
927
- style: {
1326
+ variant: "caption",
1327
+ sx: {
928
1328
  flex: 1,
929
1329
  minWidth: 0,
930
- textAlign: "left",
931
- background: "none",
932
- border: "none",
933
1330
  cursor: "pointer",
934
- fontSize: 12,
935
1331
  fontWeight: active ? 700 : 500,
936
- color: isRoute ? "var(--rdp-info)" : active ? "var(--rdp-accent)" : "var(--rdp-text)",
937
- whiteSpace: "nowrap",
1332
+ fontFamily: "monospace",
1333
+ fontSize: "0.72rem",
938
1334
  overflow: "hidden",
939
- textOverflow: "ellipsis"
1335
+ textOverflow: "ellipsis",
1336
+ whiteSpace: "nowrap",
1337
+ color: isRoute ? "info.main" : active ? "primary.main" : "text.primary"
940
1338
  },
941
1339
  children: label
942
1340
  }
943
1341
  ),
944
- actions
1342
+ 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
1343
  ]
946
1344
  }
947
1345
  );
@@ -953,14 +1351,14 @@ function Branch({ name, depth, trail, p }) {
953
1351
  const hasKids = kids.length > 0 && depth < MAX_DEPTH && !trail.has(id);
954
1352
  const open = p.expanded.has(id);
955
1353
  const active = p.selected?.nodeId === id || p.selected?.componentName === name;
956
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1354
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
957
1355
  /* @__PURE__ */ jsxRuntime.jsx(
958
1356
  RowShell,
959
1357
  {
960
1358
  depth,
961
1359
  active,
962
1360
  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,
1361
+ 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
1362
  label: name,
965
1363
  onLabel: () => node && p.onSelect(node),
966
1364
  actions: node ? /* @__PURE__ */ jsxRuntime.jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy }) : null
@@ -973,7 +1371,7 @@ function ComponentGraphTree(p) {
973
1371
  const { graph, selected, search } = p;
974
1372
  if (graph && search.trim()) {
975
1373
  const results = searchNodes(graph, search);
976
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1374
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
977
1375
  /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
978
1376
  results.length,
979
1377
  " match",
@@ -990,7 +1388,7 @@ function ComponentGraphTree(p) {
990
1388
  },
991
1389
  node.id
992
1390
  )),
993
- results.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "var(--rdp-text-faint)", paddingLeft: 8 }, children: [
1391
+ results.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs(Typography3__default.default, { variant: "caption", sx: { color: "text.disabled", pl: 1 }, children: [
994
1392
  "No components match \u201C",
995
1393
  search,
996
1394
  "\u201D."
@@ -998,9 +1396,9 @@ function ComponentGraphTree(p) {
998
1396
  ] });
999
1397
  }
1000
1398
  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." });
1399
+ return /* @__PURE__ */ jsxRuntime.jsx(Typography3__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
1400
  }
1003
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1401
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
1004
1402
  selected.parents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1005
1403
  /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Parent chain" }),
1006
1404
  selected.parents.map((parent) => {
@@ -1009,7 +1407,7 @@ function ComponentGraphTree(p) {
1009
1407
  RowShell,
1010
1408
  {
1011
1409
  isRoute: node?.type === "route",
1012
- caret: /* @__PURE__ */ jsxRuntime.jsx(IconArrowUp, { size: 12 }),
1410
+ caret: /* @__PURE__ */ jsxRuntime.jsx(lu.LuArrowUp, { size: 12 }),
1013
1411
  label: parent,
1014
1412
  onLabel: () => node && p.onSelect(node),
1015
1413
  actions: node ? /* @__PURE__ */ jsxRuntime.jsx(RowActions, { node, onOpen: p.onOpen, onCopy: p.onCopy }) : null
@@ -1019,13 +1417,13 @@ function ComponentGraphTree(p) {
1019
1417
  })
1020
1418
  ] }),
1021
1419
  /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Selected" }),
1022
- /* @__PURE__ */ jsxRuntime.jsx(RowShell, { active: true, label: selected.componentName, onLabel: () => void 0 }),
1420
+ /* @__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
1421
  /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1024
1422
  "Renders (",
1025
1423
  selected.children.length,
1026
1424
  ")"
1027
1425
  ] }),
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}`)),
1426
+ selected.children.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(Typography3__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
1427
  selected.imports.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1030
1428
  /* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
1031
1429
  "Imports (",
@@ -1072,56 +1470,60 @@ function ComponentGraphPageList({
1072
1470
  };
1073
1471
  }, [scan, route]);
1074
1472
  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." });
1473
+ return /* @__PURE__ */ jsxRuntime.jsx(Typography3__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
1474
  }
1077
1475
  const q = query.trim().toLowerCase();
1078
1476
  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",
1477
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
1478
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { mb: 0.5 }, children: [
1479
+ /* @__PURE__ */ jsxRuntime.jsxs(Typography3__default.default, { variant: "caption", sx: { color: "text.secondary" }, children: [
1480
+ /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { component: "span", sx: { fontFamily: "monospace", color: "info.main" }, children: route ?? "/" }),
1084
1481
  " ",
1482
+ "\xB7 ",
1085
1483
  q ? `${filtered.length} / ${items.length}` : items.length,
1086
1484
  " component",
1087
1485
  items.length === 1 ? "" : "s"
1088
1486
  ] }),
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
- ] })
1487
+ /* @__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
1488
  ] }),
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: [
1489
+ /* @__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" }) }),
1490
+ items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(Typography3__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(Typography3__default.default, { variant: "caption", sx: { color: "text.disabled", pl: 0.5 }, children: [
1096
1491
  "No components match \u201C",
1097
1492
  query,
1098
1493
  "\u201D."
1099
1494
  ] }) : filtered.map(({ name, node, count }) => /* @__PURE__ */ jsxRuntime.jsxs(
1100
- "div",
1495
+ Stack__default.default,
1101
1496
  {
1102
- className: "rdp-row",
1103
- style: { padding: "4px 6px", alignItems: "center", background: selectedName === name ? "rgba(105,80,232,0.16)" : void 0 },
1497
+ direction: "row",
1498
+ alignItems: "center",
1499
+ spacing: 0.5,
1104
1500
  onClick: () => onSelect(node),
1501
+ sx: (t) => ({
1502
+ px: 0.5,
1503
+ py: 0.4,
1504
+ borderRadius: 1,
1505
+ cursor: "pointer",
1506
+ bgcolor: selectedName === name ? styles.alpha(t.palette.primary.main, 0.16) : "transparent",
1507
+ "&:hover": { bgcolor: "action.hover" },
1508
+ "&:hover .rdp-row-actions": { opacity: 1 }
1509
+ }),
1105
1510
  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
- ] })
1511
+ /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { sx: { flex: 1, minWidth: 0 }, children: [
1512
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", alignItems: "center", spacing: 0.5, children: [
1513
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { fontFamily: "monospace", fontWeight: 600, fontSize: "0.72rem", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: name }),
1514
+ 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
1515
  ] }),
1114
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", color: "var(--rdp-text-faint)", fontSize: 11, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: node.filePath })
1516
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { display: "block", color: "text.disabled", fontSize: "0.62rem", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: node.filePath })
1115
1517
  ] }),
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) => {
1518
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", spacing: 0.25, className: "rdp-row-actions", sx: { opacity: 0, transition: "opacity 120ms" }, children: [
1519
+ /* @__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
1520
  e.stopPropagation();
1119
1521
  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) => {
1522
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuFileCode, { size: 13 }) }) }),
1523
+ /* @__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
1524
  e.stopPropagation();
1123
1525
  onCopy(node);
1124
- }, children: /* @__PURE__ */ jsxRuntime.jsx(IconCopy, { size: 13 }) })
1526
+ }, children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuCopy, { size: 13 }) }) })
1125
1527
  ] })
1126
1528
  ]
1127
1529
  },
@@ -1223,10 +1625,10 @@ function loadGraph(endpoint, force = false) {
1223
1625
  return graphPromise;
1224
1626
  }
1225
1627
  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 }) }
1628
+ { value: "hover", label: "Hover", icon: /* @__PURE__ */ jsxRuntime.jsx(lu.LuMousePointerClick, { size: 14 }) },
1629
+ { value: "graph", label: "Graph", icon: /* @__PURE__ */ jsxRuntime.jsx(lu.LuWorkflow, { size: 14 }) },
1630
+ { value: "page", label: "Page", icon: /* @__PURE__ */ jsxRuntime.jsx(lu.LuLayers, { size: 14 }) },
1631
+ { value: "file", label: "File", icon: /* @__PURE__ */ jsxRuntime.jsx(lu.LuFile, { size: 14 }) }
1230
1632
  ];
1231
1633
  async function copy(text) {
1232
1634
  if (typeof navigator === "undefined" || !navigator.clipboard) return false;
@@ -1237,17 +1639,15 @@ async function copy(text) {
1237
1639
  }
1238
1640
  function ComponentGraphPanel({ onClose }) {
1239
1641
  const state2 = react.useSyncExternalStore(subscribeGraph, getGraphState, getGraphServerState);
1642
+ const theme = styles.useTheme();
1240
1643
  const config = useDevPanelConfig();
1241
1644
  const route = config.getRoute();
1242
1645
  const { enabled, mode, selected, graph, status, search, expanded } = state2;
1243
1646
  const openLoc = react.useCallback(
1244
- async (sel) => {
1647
+ async (sel, editor) => {
1245
1648
  const file = sel?.absFilePath ?? sel?.filePath;
1246
- if (!file) {
1247
- showToast({ message: "No source path available", tone: "error" });
1248
- return;
1249
- }
1250
- const ok = await config.openInEditor?.({ file, line: sel?.line, column: sel?.column }, config.editor);
1649
+ if (!file) return showToast({ message: "No source path available", tone: "error" });
1650
+ const ok = await config.openInEditor?.({ file, line: sel?.line, column: sel?.column }, editor ?? config.editor);
1251
1651
  showToast(ok === false ? { message: "Editor unavailable \u2014 path copied", tone: "info" } : { message: "Opening in your editor\u2026", tone: "success" });
1252
1652
  },
1253
1653
  [config]
@@ -1256,14 +1656,11 @@ function ComponentGraphPanel({ onClose }) {
1256
1656
  if (!selected) return;
1257
1657
  void copy(formatForCopy(selected)).then((ok) => showToast({ message: ok ? "Component info copied" : "Copy failed", tone: ok ? "success" : "error" }));
1258
1658
  }, [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
- );
1659
+ const copyPath = react.useCallback((sel) => {
1660
+ const path = sel?.filePath ?? sel?.absFilePath;
1661
+ if (!path) return showToast({ message: "No path to copy", tone: "info" });
1662
+ void copy(`${path}${sel?.line ? `:${sel.line}` : ""}`).then((ok) => showToast({ message: ok ? "File path copied" : "Copy failed", tone: ok ? "success" : "error" }));
1663
+ }, []);
1267
1664
  const selectName = react.useCallback(
1268
1665
  (name) => {
1269
1666
  const node = graph?.nodes.find((n) => n.name === name || n.id === name);
@@ -1284,126 +1681,120 @@ function ComponentGraphPanel({ onClose }) {
1284
1681
  NodeDetails,
1285
1682
  {
1286
1683
  selected,
1684
+ editor: config.editor,
1287
1685
  onOpen: () => void openLoc(selected),
1686
+ onOpenEditor: (editor) => void openLoc(selected, editor),
1288
1687
  onCopyInfo: copyInfo,
1289
1688
  onCopyPath: () => copyPath(selected),
1290
1689
  onSelectName: selectName
1291
1690
  }
1292
1691
  );
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
1692
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1693
+ Box6__default.default,
1694
+ {
1695
+ "data-rdp-ignore": "",
1696
+ sx: {
1697
+ position: "fixed",
1698
+ bottom: 96,
1699
+ right: 24,
1700
+ zIndex: (t) => t.zIndex.modal + 1,
1701
+ width: "min(460px, calc(100vw - 32px))",
1702
+ maxHeight: "min(78vh, 720px)",
1703
+ display: "flex",
1704
+ flexDirection: "column",
1705
+ borderRadius: 2,
1706
+ overflow: "hidden",
1707
+ border: "1px solid",
1708
+ borderColor: "divider",
1709
+ bgcolor: "background.paper",
1710
+ boxShadow: 12
1711
+ },
1712
+ children: [
1713
+ /* @__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: [
1714
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", alignItems: "center", spacing: 1, children: [
1715
+ /* @__PURE__ */ jsxRuntime.jsx(lu.LuWorkflow, { size: 16 }),
1716
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "subtitle2", sx: { fontWeight: 700 }, children: "Component Graph Inspector" }),
1717
+ enabled && /* @__PURE__ */ jsxRuntime.jsx(Chip__default.default, { label: "ON", size: "small", color: "success", variant: "soft", sx: { height: 18, fontSize: "0.6rem", fontWeight: 700 } })
1718
+ ] }),
1719
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton__default.default, { size: "small", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuX, { size: 16 }) })
1352
1720
  ] }),
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,
1721
+ /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { sx: { flex: 1, overflowY: "auto", p: 1.5 }, children: [
1722
+ /* @__PURE__ */ jsxRuntime.jsx(
1723
+ Box6__default.default,
1359
1724
  {
1360
- graph,
1361
- selected,
1362
- search,
1363
- expanded,
1364
- onSelect: selectNode,
1365
- onToggle: toggleExpanded,
1366
- onOpen: openNode,
1367
- onCopy: copyNode
1725
+ sx: {
1726
+ p: 1.25,
1727
+ borderRadius: 1.5,
1728
+ border: "1px solid",
1729
+ borderColor: enabled ? styles.alpha(theme.palette.success.main, 0.5) : "divider",
1730
+ bgcolor: enabled ? styles.alpha(theme.palette.success.main, 0.08) : "background.default"
1731
+ },
1732
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [
1733
+ /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
1734
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "body2", sx: { fontWeight: 700 }, children: "Inspect mode" }),
1735
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__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" })
1736
+ ] }),
1737
+ /* @__PURE__ */ jsxRuntime.jsx(Switch__default.default, { checked: enabled, onChange: () => toggleInspector(config.graphEndpoint) })
1738
+ ] })
1368
1739
  }
1369
- ) }),
1370
- selected && !search.trim() && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 12 }, children: details })
1371
- ] }),
1372
- mode === "page" && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1740
+ ),
1373
1741
  /* @__PURE__ */ jsxRuntime.jsx(
1374
- ComponentGraphPageList,
1742
+ ToggleButtonGroup__default.default,
1375
1743
  {
1376
- graph,
1377
- route,
1378
- selectedName: selected?.componentName,
1379
- onSelect: selectNode,
1380
- onOpen: openNode,
1381
- onCopy: copyNode
1744
+ fullWidth: true,
1745
+ exclusive: true,
1746
+ size: "small",
1747
+ value: mode,
1748
+ onChange: (_, next) => {
1749
+ if (!next) return;
1750
+ setMode(next);
1751
+ if (next === "graph" || next === "page") void loadGraph(config.graphEndpoint);
1752
+ },
1753
+ sx: { mt: 1.5 },
1754
+ children: TABS.map((t) => /* @__PURE__ */ jsxRuntime.jsxs(ToggleButton__default.default, { value: t.value, sx: { textTransform: "none", gap: 0.5, py: 0.5 }, children: [
1755
+ t.icon,
1756
+ t.label
1757
+ ] }, t.value))
1382
1758
  }
1383
1759
  ),
1384
- selected && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 12 }, children: details })
1385
- ] }),
1386
- mode === "file" && details
1387
- ] })
1388
- ] })
1389
- ] });
1760
+ /* @__PURE__ */ jsxRuntime.jsx(Divider__default.default, { sx: { my: 1.5 } }),
1761
+ mode === "hover" && /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
1762
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { color: "text.disabled", fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.4 }, children: "Last locked component" }),
1763
+ /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { sx: { mt: 0.5 }, children: details })
1764
+ ] }),
1765
+ mode === "graph" && /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
1766
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { display: "block", mb: 0.5, color: status === "ready" ? "success.main" : "text.disabled" }, children: statusText }),
1767
+ (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" } }),
1768
+ /* @__PURE__ */ jsxRuntime.jsx(GraphSearch, { value: search, onChange: setSearch }),
1769
+ /* @__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 }) }),
1770
+ selected && !search.trim() && /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { sx: { mt: 1.5 }, children: details })
1771
+ ] }),
1772
+ mode === "page" && /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { children: [
1773
+ /* @__PURE__ */ jsxRuntime.jsx(ComponentGraphPageList, { graph, route, selectedName: selected?.componentName, onSelect: selectNode, onOpen: openNode, onCopy: copyNode }),
1774
+ selected && /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { sx: { mt: 1.5 }, children: details })
1775
+ ] }),
1776
+ mode === "file" && details
1777
+ ] })
1778
+ ]
1779
+ }
1780
+ );
1390
1781
  }
1782
+ var TOOLTIP_W = 360;
1391
1783
  function ComponentGraphOverlay() {
1392
1784
  const state2 = react.useSyncExternalStore(subscribeGraph, getGraphState, getGraphServerState);
1785
+ const theme = styles.useTheme();
1393
1786
  const config = useDevPanelConfig();
1394
- const [hover, setHover] = react.useState(null);
1395
1787
  const route = config.getRoute();
1396
1788
  const { enabled } = state2;
1397
- const openSelected = react.useCallback(
1398
- async (file, line, column, name) => {
1789
+ const [hover, setHover] = react.useState(null);
1790
+ const open = react.useCallback(
1791
+ async (file, line, column) => {
1399
1792
  if (!file) {
1400
- showToast({ message: "No source path \u2014 generate the graph or use inspect build", tone: "error" });
1793
+ showToast({ message: "No source path \u2014 generate the graph", tone: "error" });
1401
1794
  return;
1402
1795
  }
1403
1796
  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
- );
1797
+ showToast(ok === false ? { message: "Editor unavailable \u2014 path copied", tone: "info" } : { message: "Opening in your editor\u2026", tone: "success" });
1407
1798
  },
1408
1799
  [config]
1409
1800
  );
@@ -1447,11 +1838,8 @@ function ComponentGraphOverlay() {
1447
1838
  e.stopPropagation();
1448
1839
  setSelected(sel);
1449
1840
  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
- }
1841
+ if (e.metaKey || e.ctrlKey) void open(sel.absFilePath ?? sel.filePath, sel.line, sel.column);
1842
+ else showToast({ message: `Locked ${sel.componentName}`, tone: "success" });
1455
1843
  };
1456
1844
  document.addEventListener("pointermove", onMove, { passive: true });
1457
1845
  document.addEventListener("keydown", onKey, true);
@@ -1463,47 +1851,92 @@ function ComponentGraphOverlay() {
1463
1851
  document.removeEventListener("click", onClick, true);
1464
1852
  setHover(null);
1465
1853
  };
1466
- }, [enabled, route, openSelected]);
1854
+ }, [enabled, route, open]);
1467
1855
  if (!enabled) return null;
1468
- const flipX = typeof window !== "undefined" && hover && hover.x + 374 > window.innerWidth;
1856
+ const flipX = typeof window !== "undefined" && hover && hover.x + TOOLTIP_W + 14 > window.innerWidth;
1469
1857
  const flipY = typeof window !== "undefined" && hover && hover.y + 140 > window.innerHeight;
1470
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rdp-overlay", children: [
1858
+ 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
1859
  hover && /* @__PURE__ */ jsxRuntime.jsx(
1472
- "div",
1860
+ Box6__default.default,
1473
1861
  {
1474
- className: "rdp-hl",
1475
- style: { top: hover.rect.top, left: hover.rect.left, width: hover.rect.width, height: hover.rect.height }
1862
+ sx: {
1863
+ position: "fixed",
1864
+ top: hover.rect.top,
1865
+ left: hover.rect.left,
1866
+ width: hover.rect.width,
1867
+ height: hover.rect.height,
1868
+ border: "1px solid",
1869
+ borderColor: "primary.main",
1870
+ bgcolor: styles.alpha(theme.palette.primary.main, 0.12),
1871
+ borderRadius: 0.5,
1872
+ boxShadow: `0 0 0 1px ${styles.alpha(theme.palette.primary.main, 0.4)}`,
1873
+ transition: "all 60ms linear"
1874
+ }
1476
1875
  }
1477
1876
  ),
1478
1877
  hover && /* @__PURE__ */ jsxRuntime.jsxs(
1479
- "div",
1878
+ Box6__default.default,
1480
1879
  {
1481
- className: "rdp-tooltip",
1482
- style: {
1880
+ sx: {
1881
+ position: "fixed",
1483
1882
  top: flipY ? void 0 : hover.y + 14,
1484
1883
  bottom: flipY ? window.innerHeight - hover.y + 14 : void 0,
1485
1884
  left: flipX ? void 0 : hover.x + 14,
1486
- right: flipX ? window.innerWidth - hover.x + 14 : void 0
1885
+ right: flipX ? window.innerWidth - hover.x + 14 : void 0,
1886
+ maxWidth: TOOLTIP_W,
1887
+ px: 1.25,
1888
+ py: 1,
1889
+ borderRadius: 1.5,
1890
+ border: "1px solid",
1891
+ borderColor: styles.alpha(theme.palette.common.white, 0.12),
1892
+ bgcolor: styles.alpha(theme.palette.grey[900], 0.85),
1893
+ backdropFilter: "blur(8px)",
1894
+ boxShadow: "0 8px 28px rgba(0,0,0,0.45)",
1895
+ color: theme.palette.common.white
1487
1896
  },
1488
1897
  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}>` })
1898
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", alignItems: "center", spacing: 0.75, sx: { mb: hover.filePath ? 0.5 : 0 }, children: [
1899
+ /* @__PURE__ */ jsxRuntime.jsx(
1900
+ Chip__default.default,
1901
+ {
1902
+ label: hover.name,
1903
+ size: "small",
1904
+ sx: { height: 18, fontWeight: 700, fontSize: "0.65rem", color: "#fff", bgcolor: styles.alpha(theme.palette.primary.main, 0.5) }
1905
+ }
1906
+ ),
1907
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { color: styles.alpha(theme.palette.common.white, 0.6), fontFamily: "monospace" }, children: `<${hover.domTag}>` })
1492
1908
  ] }),
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" })
1909
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__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" }),
1910
+ hover.route && /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { display: "block", fontSize: "0.62rem", color: styles.alpha(theme.palette.common.white, 0.45) }, children: hover.route }),
1911
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__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
1912
  ]
1497
1913
  }
1498
1914
  ),
1499
1915
  state2.toast && /* @__PURE__ */ jsxRuntime.jsx(
1500
- "div",
1916
+ Box6__default.default,
1501
1917
  {
1502
- className: "rdp-toast",
1503
- style: {
1504
- color: state2.toast.tone === "success" ? "var(--rdp-success)" : state2.toast.tone === "error" ? "var(--rdp-error)" : "#fff"
1918
+ sx: {
1919
+ position: "fixed",
1920
+ bottom: 24,
1921
+ left: "50%",
1922
+ transform: "translateX(-50%)",
1923
+ px: 1.5,
1924
+ py: 0.75,
1925
+ borderRadius: 1.5,
1926
+ border: "1px solid",
1927
+ borderColor: styles.alpha(theme.palette.common.white, 0.12),
1928
+ bgcolor: styles.alpha(theme.palette.grey[900], 0.9),
1929
+ backdropFilter: "blur(8px)",
1930
+ boxShadow: "0 8px 28px rgba(0,0,0,0.45)"
1505
1931
  },
1506
- children: state2.toast.message
1932
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1933
+ Typography3__default.default,
1934
+ {
1935
+ variant: "caption",
1936
+ sx: { fontWeight: 600, color: state2.toast.tone === "success" ? "success.light" : state2.toast.tone === "error" ? "error.light" : "common.white" },
1937
+ children: state2.toast.message
1938
+ }
1939
+ )
1507
1940
  }
1508
1941
  )
1509
1942
  ] });
@@ -1513,191 +1946,237 @@ var componentGraphTool = {
1513
1946
  title: "Component Graph Inspector",
1514
1947
  subtitle: "Inspect component tree & open source files",
1515
1948
  color: "primary",
1516
- icon: /* @__PURE__ */ jsxRuntime.jsx(IconGraph, { size: 19 }),
1949
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lu.LuWorkflow, { size: 19 }),
1517
1950
  Panel: ComponentGraphPanel,
1518
1951
  Overlay: ComponentGraphOverlay
1519
1952
  };
1520
1953
  function registerComponentGraph() {
1521
1954
  registerTool(componentGraphTool);
1522
1955
  }
1523
- var STORAGE_KEY = "react-dev-panel:corner";
1524
- var GAP = 20;
1525
- var FAB = 52;
1956
+ function ToolTile({ color, size = 38, children }) {
1957
+ return /* @__PURE__ */ jsxRuntime.jsx(
1958
+ Box6__default.default,
1959
+ {
1960
+ sx: (theme) => ({
1961
+ width: size,
1962
+ height: size,
1963
+ flexShrink: 0,
1964
+ borderRadius: 1.5,
1965
+ display: "grid",
1966
+ placeItems: "center",
1967
+ color: theme.palette[color].main,
1968
+ bgcolor: styles.alpha(theme.palette[color].main, 0.14)
1969
+ }),
1970
+ children
1971
+ }
1972
+ );
1973
+ }
1974
+ var STORAGE_KEY2 = "react-dev-panel:corner";
1975
+ var EDGE_GAP = 24;
1976
+ var FAB_SIZE = 56;
1526
1977
  var DRAG_THRESHOLD = 5;
1527
1978
  function isCorner(v) {
1528
1979
  return v === "top-left" || v === "top-right" || v === "bottom-left" || v === "bottom-right";
1529
1980
  }
1530
- function readCorner() {
1981
+ function readStoredCorner() {
1531
1982
  if (typeof window === "undefined") return "bottom-right";
1532
1983
  try {
1533
- const s = window.localStorage.getItem(STORAGE_KEY);
1534
- if (isCorner(s)) return s;
1984
+ const stored = window.localStorage.getItem(STORAGE_KEY2);
1985
+ if (isCorner(stored)) return stored;
1535
1986
  } catch {
1536
1987
  }
1537
1988
  return "bottom-right";
1538
1989
  }
1539
1990
  function useDraggableCorner() {
1540
1991
  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 });
1992
+ const [dragPos, setDragPos] = react.useState(null);
1993
+ const movedRef = react.useRef(false);
1994
+ const offsetRef = react.useRef({ x: 0, y: 0 });
1995
+ react.useEffect(() => setCorner(readStoredCorner()), []);
1996
+ const onPointerDown = react.useCallback((event) => {
1997
+ if (event.button !== 0) return;
1998
+ const rect = event.currentTarget.getBoundingClientRect();
1999
+ offsetRef.current = { x: event.clientX - rect.left, y: event.clientY - rect.top };
2000
+ movedRef.current = false;
2001
+ const startX = event.clientX;
2002
+ const startY = event.clientY;
2003
+ const handleMove = (ev) => {
2004
+ if (!movedRef.current && Math.hypot(ev.clientX - startX, ev.clientY - startY) < DRAG_THRESHOLD) return;
2005
+ movedRef.current = true;
2006
+ setDragPos({ x: ev.clientX - offsetRef.current.x, y: ev.clientY - offsetRef.current.y });
1556
2007
  };
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"}`;
2008
+ const handleUp = (ev) => {
2009
+ window.removeEventListener("pointermove", handleMove);
2010
+ window.removeEventListener("pointerup", handleUp);
2011
+ if (movedRef.current) {
2012
+ const cx = ev.clientX - offsetRef.current.x + FAB_SIZE / 2;
2013
+ const cy = ev.clientY - offsetRef.current.y + FAB_SIZE / 2;
2014
+ const next = `${cy < window.innerHeight / 2 ? "top" : "bottom"}-${cx < window.innerWidth / 2 ? "left" : "right"}`;
1564
2015
  setCorner(next);
1565
2016
  try {
1566
- window.localStorage.setItem(STORAGE_KEY, next);
2017
+ window.localStorage.setItem(STORAGE_KEY2, next);
1567
2018
  } catch {
1568
2019
  }
1569
2020
  }
1570
- setDrag(null);
2021
+ setDragPos(null);
1571
2022
  };
1572
- window.addEventListener("pointermove", move);
1573
- window.addEventListener("pointerup", up);
2023
+ window.addEventListener("pointermove", handleMove);
2024
+ window.addEventListener("pointerup", handleUp);
1574
2025
  }, []);
1575
- const onClickCapture = react.useCallback((e) => {
1576
- if (moved.current) {
1577
- e.stopPropagation();
1578
- e.preventDefault();
1579
- moved.current = false;
2026
+ const onClickCapture = react.useCallback((event) => {
2027
+ if (movedRef.current) {
2028
+ event.stopPropagation();
2029
+ event.preventDefault();
2030
+ movedRef.current = false;
1580
2031
  }
1581
2032
  }, []);
2033
+ const dragging = dragPos !== null;
1582
2034
  const isTop = corner.startsWith("top");
1583
2035
  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
2036
+ const positionSx = dragging ? { top: dragPos.y, left: dragPos.x, right: "auto", bottom: "auto" } : {
2037
+ top: isTop ? EDGE_GAP : "auto",
2038
+ bottom: isTop ? "auto" : EDGE_GAP,
2039
+ left: isLeft ? EDGE_GAP : "auto",
2040
+ right: isLeft ? "auto" : EDGE_GAP
1589
2041
  };
1590
- return { pos, isTop, isLeft, onPointerDown, onClickCapture };
2042
+ return { positionSx, onPointerDown, onClickCapture, isTop, isLeft, dragging };
1591
2043
  }
1592
2044
  function ToolBadge({ tool }) {
1593
2045
  const badge = tool.useBadge?.();
1594
2046
  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 });
2047
+ const color = badge.tone === "error" ? "error" : badge.tone === "success" ? "success" : "default";
2048
+ return /* @__PURE__ */ jsxRuntime.jsx(
2049
+ Chip__default.default,
2050
+ {
2051
+ size: "small",
2052
+ variant: "soft",
2053
+ color,
2054
+ label: badge.label,
2055
+ sx: { height: 18, fontWeight: 700, fontSize: "0.65rem", "& .MuiChip-label": { px: 0.75 } }
2056
+ }
2057
+ );
1598
2058
  }
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
- };
2059
+ function Launcher({ tools: tools2, onOpenTool }) {
2060
+ const { positionSx, onPointerDown, onClickCapture, isTop, isLeft, dragging } = useDraggableCorner();
2061
+ const [menuAnchor, setMenuAnchor] = react.useState(null);
1611
2062
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1612
2063
  /* @__PURE__ */ jsxRuntime.jsx(
1613
- "button",
2064
+ Box6__default.default,
1614
2065
  {
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 })
2066
+ "data-rdp-ignore": "",
2067
+ sx: (theme) => ({ position: "fixed", zIndex: theme.zIndex.modal + 2, ...positionSx }),
2068
+ 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(
2069
+ Fab__default.default,
2070
+ {
2071
+ onClick: (e) => setMenuAnchor(e.currentTarget),
2072
+ onClickCapture,
2073
+ onPointerDown,
2074
+ "aria-label": "Open Developer Tools",
2075
+ "aria-haspopup": "menu",
2076
+ sx: (theme) => ({
2077
+ width: FAB_SIZE,
2078
+ height: FAB_SIZE,
2079
+ touchAction: "none",
2080
+ cursor: dragging ? "grabbing" : "grab",
2081
+ color: theme.palette.primary.contrastText,
2082
+ background: `linear-gradient(135deg, ${theme.palette.primary.main}, ${theme.palette.primary.dark})`,
2083
+ boxShadow: `0 4px 12px ${styles.alpha(theme.palette.primary.main, 0.24)}, 0 8px 24px rgba(0,0,0,0.5)`,
2084
+ "&:hover": {
2085
+ background: `linear-gradient(135deg, ${theme.palette.primary.dark}, ${theme.palette.primary.dark})`
2086
+ }
2087
+ }),
2088
+ children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuWrench, { size: 22 })
2089
+ }
2090
+ ) })
1624
2091
  }
1625
2092
  ),
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
- ]
2093
+ /* @__PURE__ */ jsxRuntime.jsxs(
2094
+ Menu__default.default,
2095
+ {
2096
+ anchorEl: menuAnchor,
2097
+ open: Boolean(menuAnchor),
2098
+ onClose: () => setMenuAnchor(null),
2099
+ sx: (theme) => ({ zIndex: theme.zIndex.modal + 2 }),
2100
+ anchorOrigin: { vertical: isTop ? "bottom" : "top", horizontal: isLeft ? "left" : "right" },
2101
+ transformOrigin: { vertical: isTop ? "top" : "bottom", horizontal: isLeft ? "left" : "right" },
2102
+ slotProps: {
2103
+ paper: {
2104
+ sx: {
2105
+ width: 320,
2106
+ mt: isTop ? 1.5 : 0,
2107
+ mb: isTop ? 0 : 1.5,
2108
+ borderRadius: 2.5,
2109
+ overflow: "hidden",
2110
+ border: "1px solid",
2111
+ borderColor: "divider",
2112
+ boxShadow: 16
2113
+ }
1671
2114
  },
1672
- tool.id
1673
- )) })
1674
- ] })
1675
- ] })
2115
+ list: { sx: { py: 0 } }
2116
+ },
2117
+ children: [
2118
+ /* @__PURE__ */ jsxRuntime.jsxs(
2119
+ Box6__default.default,
2120
+ {
2121
+ sx: {
2122
+ display: "flex",
2123
+ alignItems: "center",
2124
+ gap: 1.25,
2125
+ px: 2,
2126
+ py: 1.5,
2127
+ background: (theme) => `linear-gradient(135deg, ${styles.alpha(theme.palette.primary.main, 0.16)}, ${styles.alpha(theme.palette.primary.main, 0.04)})`
2128
+ },
2129
+ children: [
2130
+ /* @__PURE__ */ jsxRuntime.jsx(ToolTile, { color: "primary", size: 34, children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuWrench, { size: 17 }) }),
2131
+ /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { sx: { minWidth: 0 }, children: [
2132
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "subtitle2", sx: { fontWeight: 700, lineHeight: 1.2 }, children: "Developer Tools" }),
2133
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { color: "text.secondary" }, children: "Internal utilities" })
2134
+ ] })
2135
+ ]
2136
+ }
2137
+ ),
2138
+ /* @__PURE__ */ jsxRuntime.jsx(Divider__default.default, {}),
2139
+ /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { sx: { p: 1 }, children: tools2.map((tool, i) => /* @__PURE__ */ jsxRuntime.jsxs(
2140
+ MenuItem__default.default,
2141
+ {
2142
+ onClick: () => {
2143
+ onOpenTool(tool.id);
2144
+ setMenuAnchor(null);
2145
+ },
2146
+ disableGutters: true,
2147
+ sx: { alignItems: "flex-start", gap: 1.25, px: 1.25, py: 1.25, mt: i === 0 ? 0 : 0.5, borderRadius: 1.5, whiteSpace: "normal" },
2148
+ children: [
2149
+ /* @__PURE__ */ jsxRuntime.jsx(ToolTile, { color: tool.color ?? "primary", children: tool.icon }),
2150
+ /* @__PURE__ */ jsxRuntime.jsxs(Box6__default.default, { sx: { flex: 1, minWidth: 0 }, children: [
2151
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "subtitle2", sx: { fontWeight: 600, lineHeight: 1.3 }, children: tool.title }),
2152
+ /* @__PURE__ */ jsxRuntime.jsx(Typography3__default.default, { variant: "caption", sx: { color: "text.secondary", display: "block" }, children: tool.subtitle })
2153
+ ] }),
2154
+ /* @__PURE__ */ jsxRuntime.jsxs(Stack__default.default, { direction: "row", spacing: 0.5, alignItems: "center", sx: { mt: 0.25 }, children: [
2155
+ /* @__PURE__ */ jsxRuntime.jsx(ToolBadge, { tool }),
2156
+ /* @__PURE__ */ jsxRuntime.jsx(Box6__default.default, { sx: { display: "grid", placeItems: "center", color: "text.disabled" }, children: /* @__PURE__ */ jsxRuntime.jsx(lu.LuChevronRight, { size: 16 }) })
2157
+ ] })
2158
+ ]
2159
+ },
2160
+ tool.id
2161
+ )) })
2162
+ ]
2163
+ }
2164
+ )
1676
2165
  ] });
1677
2166
  }
1678
2167
  function DevPanel(config) {
1679
2168
  const enabled = config.enabled ?? (typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true);
1680
- react.useEffect(() => {
1681
- if (enabled) injectBaseStyles();
1682
- }, [enabled]);
1683
2169
  if (!enabled) return null;
1684
- return /* @__PURE__ */ jsxRuntime.jsx(DevPanelConfigProvider, { config, children: /* @__PURE__ */ jsxRuntime.jsx(DevPanelInner, { ids: config.tools, theme: config.theme }) });
2170
+ return /* @__PURE__ */ jsxRuntime.jsx(DevPanelConfigProvider, { config, children: /* @__PURE__ */ jsxRuntime.jsx(DevPanelInner, { ids: config.tools }) });
1685
2171
  }
1686
- function DevPanelInner({
1687
- ids,
1688
- theme
1689
- }) {
2172
+ function DevPanelInner({ ids }) {
1690
2173
  const tools2 = react.useMemo(() => resolveTools(ids), [ids]);
1691
2174
  const [openId, setOpenId] = react.useState(null);
1692
2175
  react.useEffect(() => {
1693
2176
  tools2.forEach((t) => t.init?.());
1694
2177
  }, [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
2178
  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: [
2179
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1701
2180
  /* @__PURE__ */ jsxRuntime.jsx(Launcher, { tools: tools2, onOpenTool: setOpenId }),
1702
2181
  ActivePanel && /* @__PURE__ */ jsxRuntime.jsx(ActivePanel, { onClose: () => setOpenId(null) }),
1703
2182
  tools2.map((t) => t.Overlay ? /* @__PURE__ */ jsxRuntime.jsx(t.Overlay, {}, t.id) : null)
@@ -1737,7 +2216,6 @@ exports.DevPanel = DevPanel;
1737
2216
  exports.createServerOpenInEditor = createServerOpenInEditor;
1738
2217
  exports.defaultOpenInEditor = defaultOpenInEditor;
1739
2218
  exports.getRegisteredTools = getRegisteredTools;
1740
- exports.injectBaseStyles = injectBaseStyles;
1741
2219
  exports.registerTool = registerTool;
1742
2220
  exports.serverOpenInEditor = serverOpenInEditor;
1743
2221
  exports.useDevPanelConfig = useDevPanelConfig;