dev-inspector 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLogList = createLogList;
4
+ function fmtTime(ts) {
5
+ const d = new Date(ts);
6
+ const hh = String(d.getHours()).padStart(2, "0");
7
+ const mm = String(d.getMinutes()).padStart(2, "0");
8
+ const ss = String(d.getSeconds()).padStart(2, "0");
9
+ return `${hh}:${mm}:${ss}`;
10
+ }
11
+ function toneForEntry(entry) {
12
+ if (entry.source === "console") {
13
+ if (entry.level === "error")
14
+ return "error";
15
+ if (entry.level === "warn")
16
+ return "warning";
17
+ return "neutral";
18
+ }
19
+ const s = entry.status;
20
+ if (typeof s !== "number")
21
+ return "error";
22
+ if (s >= 400)
23
+ return "error";
24
+ if (s >= 300)
25
+ return "warning";
26
+ return "success";
27
+ }
28
+ function classForTone(tone) {
29
+ if (tone === "error")
30
+ return "di-itemToneError";
31
+ if (tone === "warning")
32
+ return "di-itemToneWarning";
33
+ if (tone === "success")
34
+ return "di-itemToneSuccess";
35
+ return "di-itemToneNeutral";
36
+ }
37
+ function isNetworkFailure(entry) {
38
+ if (entry.source !== "network")
39
+ return false;
40
+ return typeof entry.status !== "number" || entry.status >= 400;
41
+ }
42
+ function createLogList(doc) {
43
+ const el = doc.createElement("ul");
44
+ el.className = "di-list";
45
+ const append = (entry) => {
46
+ const li = doc.createElement("li");
47
+ const tone = toneForEntry(entry);
48
+ li.className = `di-item ${classForTone(tone)}`;
49
+ const meta = doc.createElement("div");
50
+ meta.className = "di-meta";
51
+ const time = doc.createElement("span");
52
+ time.textContent = fmtTime(entry.timestamp);
53
+ const source = doc.createElement("span");
54
+ source.textContent = entry.source;
55
+ const detail = doc.createElement("span");
56
+ if (entry.source === "console") {
57
+ detail.textContent = entry.level;
58
+ }
59
+ else {
60
+ detail.className = `di-statusChip ${isNetworkFailure(entry) ? "di-statusChipError" : "di-statusChipSuccess"}`;
61
+ detail.textContent = typeof entry.status === "number" ? String(entry.status) : "ERR";
62
+ }
63
+ meta.append(time, source, detail);
64
+ const msg = doc.createElement("div");
65
+ msg.className = "di-msg";
66
+ msg.textContent = entry.message;
67
+ li.append(meta, msg);
68
+ el.append(li);
69
+ };
70
+ const clear = () => {
71
+ el.replaceChildren();
72
+ };
73
+ return { el, append, clear };
74
+ }
@@ -0,0 +1,15 @@
1
+ import type { LogStorage } from "../storage/logStorage";
2
+ export type PanelOptions = {
3
+ storage: LogStorage;
4
+ title?: string;
5
+ initiallyOpen?: boolean;
6
+ mount?: HTMLElement;
7
+ };
8
+ export type PanelHandle = {
9
+ open: () => void;
10
+ close: () => void;
11
+ toggle: () => void;
12
+ destroy: () => void;
13
+ isOpen: () => boolean;
14
+ };
15
+ export declare function createPanel(options: PanelOptions): PanelHandle;
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createPanel = createPanel;
4
+ const logList_1 = require("./logList");
5
+ const panelStyles_1 = require("./panelStyles");
6
+ function clamp(n, min, max) {
7
+ return Math.max(min, Math.min(max, n));
8
+ }
9
+ function ensureDocument() {
10
+ if (typeof document === "undefined") {
11
+ throw new Error("Panel UI requires a browser-like environment with document.");
12
+ }
13
+ return document;
14
+ }
15
+ function ensureStyle(doc) {
16
+ const id = "dev-inspector-panel-style";
17
+ const existing = doc.getElementById(id);
18
+ if (existing)
19
+ return;
20
+ const style = doc.createElement("style");
21
+ style.id = id;
22
+ style.textContent = panelStyles_1.PANEL_CSS;
23
+ doc.head.append(style);
24
+ }
25
+ function createPanel(options) {
26
+ var _a, _b, _c, _d;
27
+ const doc = ensureDocument();
28
+ ensureStyle(doc);
29
+ const mount = (_a = options.mount) !== null && _a !== void 0 ? _a : doc.body;
30
+ const title = (_b = options.title) !== null && _b !== void 0 ? _b : "Dev Inspector";
31
+ let open = (_c = options.initiallyOpen) !== null && _c !== void 0 ? _c : false;
32
+ let tab = "console";
33
+ const root = doc.createElement("div");
34
+ root.className = "di-root";
35
+ const toggleBtn = doc.createElement("button");
36
+ toggleBtn.type = "button";
37
+ toggleBtn.className = "di-toggle";
38
+ toggleBtn.setAttribute("aria-label", "Dev Inspector");
39
+ toggleBtn.innerHTML =
40
+ `<span class="di-toggleTitle">Dev Inspector</span>` +
41
+ `<span class="di-toggleMeta">` +
42
+ `<span class="di-toggleBadge" data-di-toggle-count="console">` +
43
+ `<svg class="di-toggleIcon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 5.5C4 4.67 4.67 4 5.5 4H18.5C19.33 4 20 4.67 20 5.5V15.5C20 16.33 19.33 17 18.5 17H13.5L12 18.5L10.5 17H5.5C4.67 17 4 16.33 4 15.5V5.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M7 8H17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M7 11H14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>` +
44
+ `<span data-di-toggle-count-value="console">0</span>` +
45
+ `<span class="di-toggleErr" data-di-toggle-error="console" aria-label="Console errors">` +
46
+ `<span class="di-toggleErrIcon">!</span>` +
47
+ `<span data-di-toggle-error-value="console">0</span>` +
48
+ `</span>` +
49
+ `</span>` +
50
+ `<span class="di-toggleBadge" data-di-toggle-count="network">` +
51
+ `<svg class="di-toggleIcon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 12C4 7.58 7.58 4 12 4C16.42 4 20 7.58 20 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M6 12C6 8.69 8.69 6 12 6C15.31 6 18 8.69 18 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M8.5 12C8.5 10.07 10.07 8.5 12 8.5C13.93 8.5 15.5 10.07 15.5 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M12 12L12 20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="12" cy="20" r="1.5" fill="currentColor"/></svg>` +
52
+ `<span data-di-toggle-count-value="network">0</span>` +
53
+ `<span class="di-toggleErr" data-di-toggle-error="network" aria-label="Network errors">` +
54
+ `<span class="di-toggleErrIcon">!</span>` +
55
+ `<span data-di-toggle-error-value="network">0</span>` +
56
+ `</span>` +
57
+ `</span>` +
58
+ `</span>`;
59
+ const panel = doc.createElement("div");
60
+ panel.className = `di-panel${open ? "" : " di-hidden"}`;
61
+ const resizeHandle = doc.createElement("div");
62
+ resizeHandle.className = "di-resizeHandle";
63
+ resizeHandle.setAttribute("role", "separator");
64
+ resizeHandle.setAttribute("aria-label", "Resize panel");
65
+ const header = doc.createElement("div");
66
+ header.className = "di-header";
67
+ const headerRow = doc.createElement("div");
68
+ headerRow.className = "di-headerRow";
69
+ const titleEl = doc.createElement("div");
70
+ titleEl.className = "di-title";
71
+ titleEl.textContent = title;
72
+ const actions = doc.createElement("div");
73
+ actions.className = "di-actions";
74
+ const clearBtn = doc.createElement("button");
75
+ clearBtn.type = "button";
76
+ clearBtn.className = "di-btn";
77
+ clearBtn.textContent = "Clear";
78
+ const closeBtn = doc.createElement("button");
79
+ closeBtn.type = "button";
80
+ closeBtn.className = "di-btn";
81
+ closeBtn.textContent = "Close";
82
+ actions.append(clearBtn, closeBtn);
83
+ headerRow.append(resizeHandle, titleEl, actions);
84
+ const tabs = doc.createElement("div");
85
+ tabs.className = "di-tabs";
86
+ const consoleTab = doc.createElement("button");
87
+ consoleTab.type = "button";
88
+ consoleTab.className = "di-tab";
89
+ consoleTab.innerHTML =
90
+ `<svg class="di-tabIcon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 5.5C4 4.67 4.67 4 5.5 4H18.5C19.33 4 20 4.67 20 5.5V15.5C20 16.33 19.33 17 18.5 17H13.5L12 18.5L10.5 17H5.5C4.67 17 4 16.33 4 15.5V5.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M7 8H17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M7 11H14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>` +
91
+ `<span>Console</span>` +
92
+ `<span class="di-badge" data-di-count="console">0</span>`;
93
+ const networkTab = doc.createElement("button");
94
+ networkTab.type = "button";
95
+ networkTab.className = "di-tab";
96
+ networkTab.innerHTML =
97
+ `<svg class="di-tabIcon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 12C4 7.58 7.58 4 12 4C16.42 4 20 7.58 20 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M6 12C6 8.69 8.69 6 12 6C15.31 6 18 8.69 18 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M8.5 12C8.5 10.07 10.07 8.5 12 8.5C13.93 8.5 15.5 10.07 15.5 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M12 12L12 20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><circle cx="12" cy="20" r="1.5" fill="currentColor"/></svg>` +
98
+ `<span>Network</span>` +
99
+ `<span class="di-badge" data-di-count="network">0</span>`;
100
+ tabs.append(consoleTab, networkTab);
101
+ header.append(headerRow, tabs);
102
+ const body = doc.createElement("div");
103
+ body.className = "di-body";
104
+ const list = (0, logList_1.createLogList)(doc);
105
+ body.append(list.el);
106
+ panel.append(header, body);
107
+ root.append(toggleBtn, panel);
108
+ mount.append(root);
109
+ const MAX_WIDTH_CAP = 920;
110
+ const MAX_HEIGHT_CAP = 720;
111
+ const MARGIN_X = 24;
112
+ const MARGIN_Y = 68;
113
+ const minSize = (() => {
114
+ const r = panel.getBoundingClientRect();
115
+ return { w: Math.round(r.width), h: Math.round(r.height) };
116
+ })();
117
+ const getMaxSize = () => {
118
+ const win = globalThis;
119
+ const vw = typeof win.innerWidth === "number" ? win.innerWidth : 0;
120
+ const vh = typeof win.innerHeight === "number" ? win.innerHeight : 0;
121
+ return {
122
+ w: Math.max(200, Math.min(MAX_WIDTH_CAP, vw - MARGIN_X)),
123
+ h: Math.max(180, Math.min(MAX_HEIGHT_CAP, vh - MARGIN_Y)),
124
+ };
125
+ };
126
+ const getCurrentSize = () => {
127
+ const r = panel.getBoundingClientRect();
128
+ return { w: Math.round(r.width), h: Math.round(r.height) };
129
+ };
130
+ const applySize = (next) => {
131
+ const max = getMaxSize();
132
+ const effMinW = Math.min(minSize.w, max.w);
133
+ const effMinH = Math.min(minSize.h, max.h);
134
+ const w = clamp(next.w, effMinW, max.w);
135
+ const h = clamp(next.h, effMinH, max.h);
136
+ panel.style.width = `${w}px`;
137
+ panel.style.height = `${h}px`;
138
+ };
139
+ const ensureWithinViewport = () => {
140
+ if (!panel.style.width && !panel.style.height)
141
+ return;
142
+ applySize(getCurrentSize());
143
+ };
144
+ const entries = { console: [], network: [] };
145
+ const errorCounts = { console: 0, network: 0 };
146
+ const isConsoleError = (e) => e.source === "console" && e.level === "error";
147
+ const isNetworkError = (e) => e.source === "network" && (typeof e.status !== "number" || e.status >= 400);
148
+ const updateTabStyles = () => {
149
+ if (tab === "console") {
150
+ consoleTab.classList.add("di-tabActive");
151
+ networkTab.classList.remove("di-tabActive");
152
+ }
153
+ else {
154
+ networkTab.classList.add("di-tabActive");
155
+ consoleTab.classList.remove("di-tabActive");
156
+ }
157
+ };
158
+ const updateCounts = () => {
159
+ const c = header.querySelector('[data-di-count="console"]');
160
+ const n = header.querySelector('[data-di-count="network"]');
161
+ if (c)
162
+ c.textContent = String(entries.console.length);
163
+ if (n)
164
+ n.textContent = String(entries.network.length);
165
+ const tc = toggleBtn.querySelector('[data-di-toggle-count-value="console"]');
166
+ const tn = toggleBtn.querySelector('[data-di-toggle-count-value="network"]');
167
+ if (tc)
168
+ tc.textContent = String(entries.console.length);
169
+ if (tn)
170
+ tn.textContent = String(entries.network.length);
171
+ const tec = toggleBtn.querySelector('[data-di-toggle-error-value="console"]');
172
+ const ten = toggleBtn.querySelector('[data-di-toggle-error-value="network"]');
173
+ if (tec)
174
+ tec.textContent = String(errorCounts.console);
175
+ if (ten)
176
+ ten.textContent = String(errorCounts.network);
177
+ const ecWrap = toggleBtn.querySelector('[data-di-toggle-error="console"]');
178
+ const enWrap = toggleBtn.querySelector('[data-di-toggle-error="network"]');
179
+ if (ecWrap)
180
+ ecWrap.style.display = errorCounts.console > 0 ? "inline-flex" : "none";
181
+ if (enWrap)
182
+ enWrap.style.display = errorCounts.network > 0 ? "inline-flex" : "none";
183
+ };
184
+ const renderTab = () => {
185
+ list.clear();
186
+ entries[tab].forEach((e) => list.append(e));
187
+ body.scrollTop = body.scrollHeight;
188
+ };
189
+ const hydrateFromStorage = () => {
190
+ entries.console = [];
191
+ entries.network = [];
192
+ errorCounts.console = 0;
193
+ errorCounts.network = 0;
194
+ options.storage.getAll().forEach((e) => {
195
+ if (e.source === "network") {
196
+ entries.network.push(e);
197
+ if (isNetworkError(e))
198
+ errorCounts.network += 1;
199
+ }
200
+ else {
201
+ entries.console.push(e);
202
+ if (isConsoleError(e))
203
+ errorCounts.console += 1;
204
+ }
205
+ });
206
+ updateCounts();
207
+ updateTabStyles();
208
+ renderTab();
209
+ };
210
+ hydrateFromStorage();
211
+ const onNewLog = (entry) => {
212
+ if (entry.source === "network") {
213
+ entries.network.push(entry);
214
+ if (isNetworkError(entry))
215
+ errorCounts.network += 1;
216
+ }
217
+ else {
218
+ entries.console.push(entry);
219
+ if (isConsoleError(entry))
220
+ errorCounts.console += 1;
221
+ }
222
+ updateCounts();
223
+ if (entry.source === tab) {
224
+ list.append(entry);
225
+ body.scrollTop = body.scrollHeight;
226
+ }
227
+ };
228
+ const unsub = options.storage.onNewLog(onNewLog);
229
+ const onCleared = () => {
230
+ entries.console = [];
231
+ entries.network = [];
232
+ errorCounts.console = 0;
233
+ errorCounts.network = 0;
234
+ updateCounts();
235
+ renderTab();
236
+ };
237
+ options.storage.addEventListener("cleared", onCleared);
238
+ const applyVisibility = () => {
239
+ if (open)
240
+ panel.classList.remove("di-hidden");
241
+ else
242
+ panel.classList.add("di-hidden");
243
+ };
244
+ const openPanel = () => {
245
+ open = true;
246
+ applyVisibility();
247
+ };
248
+ const closePanel = () => {
249
+ open = false;
250
+ applyVisibility();
251
+ };
252
+ const toggle = () => {
253
+ open = !open;
254
+ applyVisibility();
255
+ };
256
+ const onToggleClick = () => toggle();
257
+ const onCloseClick = () => closePanel();
258
+ const onClearClick = () => {
259
+ options.storage.clear();
260
+ };
261
+ const onConsoleTab = () => {
262
+ tab = "console";
263
+ updateTabStyles();
264
+ renderTab();
265
+ };
266
+ const onNetworkTab = () => {
267
+ tab = "network";
268
+ updateTabStyles();
269
+ renderTab();
270
+ };
271
+ toggleBtn.addEventListener("click", onToggleClick);
272
+ closeBtn.addEventListener("click", onCloseClick);
273
+ clearBtn.addEventListener("click", onClearClick);
274
+ consoleTab.addEventListener("click", onConsoleTab);
275
+ networkTab.addEventListener("click", onNetworkTab);
276
+ let resizing = false;
277
+ let startX = 0;
278
+ let startY = 0;
279
+ let startW = 0;
280
+ let startH = 0;
281
+ const onResizeMove = (ev) => {
282
+ if (!resizing)
283
+ return;
284
+ const dx = startX - ev.clientX;
285
+ const dy = startY - ev.clientY;
286
+ applySize({ w: startW + dx, h: startH + dy });
287
+ };
288
+ const stopResize = () => {
289
+ var _a, _b, _c;
290
+ if (!resizing)
291
+ return;
292
+ resizing = false;
293
+ const win = globalThis;
294
+ (_a = win.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(win, "pointermove", onResizeMove);
295
+ (_b = win.removeEventListener) === null || _b === void 0 ? void 0 : _b.call(win, "pointerup", stopResize);
296
+ (_c = win.removeEventListener) === null || _c === void 0 ? void 0 : _c.call(win, "pointercancel", stopResize);
297
+ };
298
+ const onResizeStart = (ev) => {
299
+ var _a, _b, _c;
300
+ resizing = true;
301
+ startX = ev.clientX;
302
+ startY = ev.clientY;
303
+ const cur = getCurrentSize();
304
+ startW = cur.w;
305
+ startH = cur.h;
306
+ try {
307
+ resizeHandle.setPointerCapture(ev.pointerId);
308
+ }
309
+ catch (_d) {
310
+ void 0;
311
+ }
312
+ const win = globalThis;
313
+ (_a = win.addEventListener) === null || _a === void 0 ? void 0 : _a.call(win, "pointermove", onResizeMove);
314
+ (_b = win.addEventListener) === null || _b === void 0 ? void 0 : _b.call(win, "pointerup", stopResize);
315
+ (_c = win.addEventListener) === null || _c === void 0 ? void 0 : _c.call(win, "pointercancel", stopResize);
316
+ };
317
+ resizeHandle.addEventListener("pointerdown", onResizeStart);
318
+ const win = globalThis;
319
+ const onWindowResize = () => ensureWithinViewport();
320
+ (_d = win.addEventListener) === null || _d === void 0 ? void 0 : _d.call(win, "resize", onWindowResize);
321
+ const destroy = () => {
322
+ var _a;
323
+ toggleBtn.removeEventListener("click", onToggleClick);
324
+ closeBtn.removeEventListener("click", onCloseClick);
325
+ clearBtn.removeEventListener("click", onClearClick);
326
+ consoleTab.removeEventListener("click", onConsoleTab);
327
+ networkTab.removeEventListener("click", onNetworkTab);
328
+ options.storage.removeEventListener("cleared", onCleared);
329
+ resizeHandle.removeEventListener("pointerdown", onResizeStart);
330
+ stopResize();
331
+ (_a = win.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(win, "resize", onWindowResize);
332
+ unsub();
333
+ root.remove();
334
+ };
335
+ return {
336
+ open: openPanel,
337
+ close: closePanel,
338
+ toggle,
339
+ destroy,
340
+ isOpen: () => open,
341
+ };
342
+ }
@@ -0,0 +1 @@
1
+ export declare const PANEL_CSS = "\n.di-root, .di-root * {\n box-sizing: border-box;\n}\n\n.di-toggle {\n position: fixed;\n right: 12px;\n bottom: 12px;\n z-index: 2147483647;\n border: 1px solid rgba(255,255,255,0.18);\n background: rgba(20,20,20,0.92);\n color: #fff;\n font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n padding: 8px 10px;\n border-radius: 10px;\n cursor: pointer;\n backdrop-filter: blur(8px);\n box-shadow: 0 10px 30px rgba(0,0,0,0.35);\n display: inline-flex;\n align-items: center;\n gap: 10px;\n}\n\n.di-toggleTitle {\n font-weight: 600;\n}\n\n.di-toggleMeta {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n.di-toggleBadge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n border-radius: 999px;\n border: 1px solid rgba(255,255,255,0.16);\n background: rgba(0,0,0,0.28);\n font-size: 11px;\n line-height: 1;\n}\n\n.di-toggleErr {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin-left: 2px;\n padding: 2px 6px;\n border-radius: 999px;\n background: rgba(239, 68, 68, 0.95);\n color: #fff;\n font-size: 11px;\n line-height: 1;\n}\n\n.di-toggleErrIcon {\n font-weight: 700;\n transform: translateY(-0.5px);\n}\n\n.di-toggleIcon {\n width: 14px;\n height: 14px;\n display: inline-block;\n}\n\n.di-panel {\n position: fixed;\n right: 12px;\n bottom: 56px;\n width: min(520px, calc(100vw - 24px));\n height: min(380px, calc(100vh - 84px));\n z-index: 2147483647;\n border: 1px solid rgba(255,255,255,0.14);\n background: rgba(16,16,16,0.92);\n color: #eaeaea;\n border-radius: 12px;\n overflow: hidden;\n display: grid;\n grid-template-rows: auto 1fr;\n backdrop-filter: blur(8px);\n box-shadow: 0 10px 30px rgba(0,0,0,0.45);\n}\n\n.di-resizeHandle {\n width: 18px;\n height: 18px;\n flex: 0 0 18px;\n border-radius: 8px;\n cursor: nwse-resize;\n touch-action: none;\n opacity: 0.9;\n border: 1px solid rgba(255,255,255,0.14);\n background: rgba(255,255,255,0.06);\n position: relative;\n}\n\n.di-resizeHandle::before {\n content: \"\";\n position: absolute;\n inset: 4px;\n border-radius: 6px;\n background:\n linear-gradient(135deg, rgba(255,255,255,0.65) 0 2px, transparent 0) 0 0 / 6px 6px,\n linear-gradient(135deg, rgba(255,255,255,0.35) 0 2px, transparent 0) 3px 3px / 6px 6px;\n}\n\n.di-resizeHandle:hover {\n opacity: 1;\n border-color: rgba(255,255,255,0.24);\n background: rgba(255,255,255,0.10);\n}\n\n.di-hidden {\n display: none;\n}\n\n.di-header {\n display: grid;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid rgba(255,255,255,0.12);\n}\n\n.di-headerRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.di-title {\n font: 600 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n opacity: 0.95;\n}\n\n.di-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.di-btn {\n border: 1px solid rgba(255,255,255,0.14);\n background: rgba(255,255,255,0.06);\n color: #fff;\n font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n padding: 6px 8px;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.di-tabs {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.di-tab {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border: 1px solid rgba(255,255,255,0.14);\n background: rgba(255,255,255,0.06);\n color: #fff;\n padding: 6px 10px;\n border-radius: 999px;\n cursor: pointer;\n font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n opacity: 0.9;\n}\n\n.di-tab:hover {\n background: rgba(255,255,255,0.10);\n border-color: rgba(255,255,255,0.22);\n}\n\n.di-tabActive {\n opacity: 1;\n background: rgba(255,255,255,0.14);\n border-color: rgba(255,255,255,0.28);\n}\n\n.di-tabIcon {\n width: 14px;\n height: 14px;\n display: inline-block;\n}\n\n.di-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 6px;\n border-radius: 999px;\n border: 1px solid rgba(255,255,255,0.16);\n background: rgba(0,0,0,0.28);\n font-size: 11px;\n line-height: 1;\n}\n\n.di-body {\n overflow: auto;\n}\n\n.di-list {\n margin: 0;\n padding: 8px 10px;\n list-style: none;\n display: grid;\n gap: 6px;\n}\n\n.di-item {\n padding: 8px 10px;\n border: 1px solid rgba(255,255,255,0.10);\n border-radius: 10px;\n background: rgba(255,255,255,0.04);\n}\n\n.di-itemToneNeutral {\n border-color: rgba(255,255,255,0.10);\n background: rgba(255,255,255,0.04);\n}\n\n.di-itemToneSuccess {\n border-color: rgba(34, 197, 94, 0.35);\n background: rgba(34, 197, 94, 0.10);\n}\n\n.di-itemToneWarning {\n border-color: rgba(245, 158, 11, 0.42);\n background: rgba(245, 158, 11, 0.12);\n}\n\n.di-itemToneError {\n border-color: rgba(239, 68, 68, 0.45);\n background: rgba(239, 68, 68, 0.12);\n}\n\n.di-meta {\n display: flex;\n gap: 10px;\n font: 11px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n opacity: 0.85;\n margin-bottom: 4px;\n}\n\n.di-statusChip {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 2px 8px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1;\n color: #fff;\n}\n\n.di-statusChipSuccess {\n background: rgba(34, 197, 94, 0.95);\n}\n\n.di-statusChipError {\n background: rgba(239, 68, 68, 0.95);\n}\n\n.di-msg {\n white-space: pre-wrap;\n word-break: break-word;\n font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n";