dev-inspector 1.0.1 → 1.0.2

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.
@@ -34,7 +34,16 @@ function getCircularReplacer() {
34
34
  };
35
35
  }
36
36
  function formatArgs(args) {
37
- return args.map(safeStringify).join(" ");
37
+ const parts = args
38
+ .map((a) => {
39
+ if (a instanceof Error)
40
+ return `${a.name}: ${a.message}`;
41
+ if (typeof a === "object" && a !== null)
42
+ return null;
43
+ return safeStringify(a);
44
+ })
45
+ .filter((x) => typeof x === "string" && x.length > 0);
46
+ return parts.join(" ");
38
47
  }
39
48
  function installConsoleLogger(options) {
40
49
  var _a;
@@ -6,9 +6,6 @@ function createId() {
6
6
  seq += 1;
7
7
  return `${Date.now()}-${seq}`;
8
8
  }
9
- function now() {
10
- return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
11
- }
12
9
  function safeToString(value) {
13
10
  try {
14
11
  return String(value);
@@ -66,31 +63,7 @@ function makeMessage(entry) {
66
63
  const m = (_a = entry.method) !== null && _a !== void 0 ? _a : "";
67
64
  const u = formatUrlForMessage(entry.url);
68
65
  const s = typeof entry.status === "number" ? ` ${entry.status}` : "";
69
- const d = typeof entry.durationMs === "number" ? ` ${Math.round(entry.durationMs)}ms` : "";
70
- return `${m} ${u}${s}${d}`.trim();
71
- }
72
- function getFetchTimingDurationMs(url, startPerf) {
73
- if (!url)
74
- return undefined;
75
- if (typeof performance === "undefined")
76
- return undefined;
77
- if (typeof performance.getEntriesByName !== "function")
78
- return undefined;
79
- const entries = performance.getEntriesByName(url);
80
- if (!entries || entries.length === 0)
81
- return undefined;
82
- const candidates = entries.filter((e) => {
83
- const rt = e;
84
- const initiatorOk = typeof rt.initiatorType === "string" ? rt.initiatorType === "fetch" : true;
85
- const startOk = typeof rt.startTime === "number" ? Math.abs(rt.startTime - startPerf) < 1000 : true;
86
- return initiatorOk && startOk;
87
- });
88
- const chosen = (candidates.length > 0 ? candidates : entries)[(candidates.length > 0 ? candidates : entries).length - 1];
89
- if (typeof chosen.duration === "number" && chosen.duration > 0)
90
- return chosen.duration;
91
- if (typeof chosen.responseEnd === "number" && typeof chosen.startTime === "number" && chosen.responseEnd > 0)
92
- return chosen.responseEnd - chosen.startTime;
93
- return undefined;
66
+ return `${m} ${u}${s}`.trim();
94
67
  }
95
68
  const xhrMeta = new WeakMap();
96
69
  function installNetworkLogger(options) {
@@ -106,9 +79,7 @@ function installNetworkLogger(options) {
106
79
  const originalXhrSend = canXhr ? g.XMLHttpRequest.prototype.send : undefined;
107
80
  if (canFetch) {
108
81
  g.fetch = (async (...args) => {
109
- var _a, _b, _c;
110
- const start = now();
111
- const startPerf = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : start;
82
+ var _a;
112
83
  const id = createId();
113
84
  let method;
114
85
  let url;
@@ -122,12 +93,11 @@ function installNetworkLogger(options) {
122
93
  if (includeBodies && init && "body" in init)
123
94
  requestBody = init.body;
124
95
  }
125
- catch (_d) {
96
+ catch (_b) {
126
97
  void 0;
127
98
  }
128
99
  try {
129
100
  const res = await Reflect.apply(originalFetch, globalThis, args);
130
- const durationMs = (_b = getFetchTimingDurationMs(url, startPerf)) !== null && _b !== void 0 ? _b : (now() - start);
131
101
  const status = res.status;
132
102
  let responseBody;
133
103
  if (includeBodies) {
@@ -142,17 +112,15 @@ function installNetworkLogger(options) {
142
112
  method,
143
113
  url,
144
114
  status,
145
- durationMs,
146
115
  requestBody: includeBodies ? requestBody : undefined,
147
116
  responseBody: includeBodies ? responseBody : undefined,
148
- message: makeMessage({ method, url, status, durationMs }),
117
+ message: makeMessage({ method, url, status }),
149
118
  };
150
119
  options.emit(entry);
151
120
  }
152
121
  return res;
153
122
  }
154
123
  catch (err) {
155
- const durationMs = (_c = getFetchTimingDurationMs(url, startPerf)) !== null && _c !== void 0 ? _c : (now() - start);
156
124
  if (active) {
157
125
  const entry = {
158
126
  id,
@@ -161,10 +129,9 @@ function installNetworkLogger(options) {
161
129
  method,
162
130
  url,
163
131
  status: undefined,
164
- durationMs,
165
132
  requestBody: includeBodies ? requestBody : undefined,
166
133
  responseBody: includeBodies ? safeToString(err) : undefined,
167
- message: makeMessage({ method, url, status: undefined, durationMs }),
134
+ message: makeMessage({ method, url, status: undefined }),
168
135
  };
169
136
  options.emit(entry);
170
137
  }
@@ -179,16 +146,16 @@ function installNetworkLogger(options) {
179
146
  proto.open = function (...args) {
180
147
  try {
181
148
  const [method, url] = args;
182
- xhrMeta.set(this, { id: createId(), start: now(), method: safeToString(method).toUpperCase(), url: safeToString(url) });
149
+ xhrMeta.set(this, { id: createId(), method: safeToString(method).toUpperCase(), url: safeToString(url) });
183
150
  }
184
151
  catch (_a) {
185
- xhrMeta.set(this, { id: createId(), start: now() });
152
+ xhrMeta.set(this, { id: createId() });
186
153
  }
187
154
  return originalOpen.apply(this, args);
188
155
  };
189
156
  proto.send = function (...args) {
190
157
  var _a;
191
- const meta = (_a = xhrMeta.get(this)) !== null && _a !== void 0 ? _a : { id: createId(), start: now() };
158
+ const meta = (_a = xhrMeta.get(this)) !== null && _a !== void 0 ? _a : { id: createId() };
192
159
  if (includeBodies) {
193
160
  try {
194
161
  meta.requestBody = args[0];
@@ -199,7 +166,6 @@ function installNetworkLogger(options) {
199
166
  }
200
167
  xhrMeta.set(this, meta);
201
168
  const onLoadEnd = () => {
202
- const durationMs = now() - meta.start;
203
169
  const status = typeof this.status === "number" ? this.status : undefined;
204
170
  let responseBody;
205
171
  if (includeBodies) {
@@ -221,10 +187,9 @@ function installNetworkLogger(options) {
221
187
  method: meta.method,
222
188
  url: meta.url,
223
189
  status,
224
- durationMs,
225
190
  requestBody: includeBodies ? meta.requestBody : undefined,
226
191
  responseBody: includeBodies ? responseBody : undefined,
227
- message: makeMessage({ method: meta.method, url: meta.url, status, durationMs }),
192
+ message: makeMessage({ method: meta.method, url: meta.url, status }),
228
193
  };
229
194
  options.emit(entry);
230
195
  }
@@ -0,0 +1,16 @@
1
+ export type JsonViewerOptions = {
2
+ maxDepth?: number;
3
+ maxKeys?: number;
4
+ maxNodes?: number;
5
+ };
6
+ type Ctx = {
7
+ doc: Document;
8
+ seen: WeakSet<object>;
9
+ nodes: number;
10
+ maxDepth: number;
11
+ maxKeys: number;
12
+ maxNodes: number;
13
+ };
14
+ export declare function renderAny(ctx: Ctx, value: unknown, depth: number): HTMLElement;
15
+ export declare function createJsonViewer(doc: Document, value: unknown, options?: JsonViewerOptions): HTMLElement;
16
+ export {};
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderAny = renderAny;
4
+ exports.createJsonViewer = createJsonViewer;
5
+ function isObject(value) {
6
+ return typeof value === "object" && value !== null;
7
+ }
8
+ function typeLabel(value) {
9
+ if (value === null)
10
+ return "null";
11
+ const t = typeof value;
12
+ if (t !== "object")
13
+ return t;
14
+ if (Array.isArray(value))
15
+ return `Array(${value.length})`;
16
+ const tag = Object.prototype.toString.call(value);
17
+ return tag.slice(8, -1) || "Object";
18
+ }
19
+ function shortPreview(value) {
20
+ if (value === null)
21
+ return "null";
22
+ if (typeof value === "string") {
23
+ const s = value.length > 120 ? `${value.slice(0, 120)}…` : value;
24
+ return JSON.stringify(s);
25
+ }
26
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
27
+ return String(value);
28
+ if (typeof value === "undefined")
29
+ return "undefined";
30
+ if (typeof value === "function")
31
+ return "function";
32
+ if (typeof value === "symbol")
33
+ return "symbol";
34
+ if (typeof value === "object") {
35
+ if (Array.isArray(value))
36
+ return `Array(${value.length})`;
37
+ return "Object";
38
+ }
39
+ return String(value);
40
+ }
41
+ function canExpand(value) {
42
+ if (!isObject(value))
43
+ return false;
44
+ if (value instanceof Date)
45
+ return false;
46
+ if (value instanceof RegExp)
47
+ return false;
48
+ return true;
49
+ }
50
+ function row(doc, key, valueEl) {
51
+ const el = doc.createElement("div");
52
+ el.className = "di-jsonRow";
53
+ const k = doc.createElement("span");
54
+ k.className = "di-jsonKey";
55
+ k.textContent = key;
56
+ el.append(k, valueEl);
57
+ return el;
58
+ }
59
+ function leaf(ctx, value) {
60
+ const el = ctx.doc.createElement("span");
61
+ el.className = "di-jsonValue";
62
+ el.textContent = shortPreview(value);
63
+ return el;
64
+ }
65
+ function renderObject(ctx, value, depth) {
66
+ if (!isObject(value))
67
+ return leaf(ctx, value);
68
+ if (!canExpand(value))
69
+ return leaf(ctx, value);
70
+ if (ctx.nodes >= ctx.maxNodes) {
71
+ const el = ctx.doc.createElement("span");
72
+ el.className = "di-jsonValue";
73
+ el.textContent = "[truncated]";
74
+ return el;
75
+ }
76
+ const obj = value;
77
+ if (ctx.seen.has(obj)) {
78
+ const el = ctx.doc.createElement("span");
79
+ el.className = "di-jsonValue";
80
+ el.textContent = "[circular]";
81
+ return el;
82
+ }
83
+ ctx.seen.add(obj);
84
+ ctx.nodes += 1;
85
+ const details = ctx.doc.createElement("details");
86
+ details.className = "di-jsonNode";
87
+ details.open = depth === 0;
88
+ const summary = ctx.doc.createElement("summary");
89
+ summary.className = "di-jsonSummary";
90
+ summary.textContent = `${typeLabel(value)} ${shortPreview(value)}`;
91
+ details.append(summary);
92
+ if (depth >= ctx.maxDepth) {
93
+ const el = ctx.doc.createElement("div");
94
+ el.className = "di-jsonBody";
95
+ const msg = ctx.doc.createElement("div");
96
+ msg.className = "di-jsonTrunc";
97
+ msg.textContent = "Max depth reached";
98
+ el.append(msg);
99
+ details.append(el);
100
+ return details;
101
+ }
102
+ const body = ctx.doc.createElement("div");
103
+ body.className = "di-jsonBody";
104
+ if (Array.isArray(value)) {
105
+ const arr = value;
106
+ const limit = Math.min(arr.length, ctx.maxKeys);
107
+ for (let i = 0; i < limit; i += 1) {
108
+ const v = arr[i];
109
+ body.append(row(ctx.doc, String(i), renderAny(ctx, v, depth + 1)));
110
+ }
111
+ if (arr.length > limit) {
112
+ const more = ctx.doc.createElement("div");
113
+ more.className = "di-jsonMore";
114
+ more.textContent = `… +${arr.length - limit} more`;
115
+ body.append(more);
116
+ }
117
+ }
118
+ else {
119
+ const rec = value;
120
+ const keys = Object.keys(rec);
121
+ const limit = Math.min(keys.length, ctx.maxKeys);
122
+ for (let i = 0; i < limit; i += 1) {
123
+ const k = keys[i];
124
+ const v = rec[k];
125
+ body.append(row(ctx.doc, k, renderAny(ctx, v, depth + 1)));
126
+ }
127
+ if (keys.length > limit) {
128
+ const more = ctx.doc.createElement("div");
129
+ more.className = "di-jsonMore";
130
+ more.textContent = `… +${keys.length - limit} more`;
131
+ body.append(more);
132
+ }
133
+ }
134
+ details.append(body);
135
+ return details;
136
+ }
137
+ function renderAny(ctx, value, depth) {
138
+ if (!canExpand(value))
139
+ return leaf(ctx, value);
140
+ return renderObject(ctx, value, depth);
141
+ }
142
+ function createJsonViewer(doc, value, options = {}) {
143
+ var _a, _b, _c;
144
+ const ctx = {
145
+ doc,
146
+ seen: new WeakSet(),
147
+ nodes: 0,
148
+ maxDepth: Math.max(1, (_a = options.maxDepth) !== null && _a !== void 0 ? _a : 6),
149
+ maxKeys: Math.max(10, (_b = options.maxKeys) !== null && _b !== void 0 ? _b : 200),
150
+ maxNodes: Math.max(50, (_c = options.maxNodes) !== null && _c !== void 0 ? _c : 2000),
151
+ };
152
+ const root = doc.createElement("div");
153
+ root.className = "di-jsonRoot";
154
+ root.append(renderAny(ctx, value, 0));
155
+ return root;
156
+ }
package/lib/ui/logList.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createLogList = createLogList;
4
+ const jsonViewer_1 = require("./jsonViewer");
4
5
  function fmtTime(ts) {
5
6
  const d = new Date(ts);
6
7
  const hh = String(d.getHours()).padStart(2, "0");
@@ -39,6 +40,20 @@ function isNetworkFailure(entry) {
39
40
  return false;
40
41
  return typeof entry.status !== "number" || entry.status >= 400;
41
42
  }
43
+ function isInspectableValue(value) {
44
+ if (typeof value !== "object" || value === null)
45
+ return false;
46
+ if (value instanceof Error)
47
+ return false;
48
+ if (value instanceof Date)
49
+ return false;
50
+ if (value instanceof RegExp)
51
+ return false;
52
+ return true;
53
+ }
54
+ function getInspectableArgs(args) {
55
+ return args.filter(isInspectableValue);
56
+ }
42
57
  function createLogList(doc) {
43
58
  const el = doc.createElement("ul");
44
59
  el.className = "di-list";
@@ -61,10 +76,32 @@ function createLogList(doc) {
61
76
  detail.textContent = typeof entry.status === "number" ? String(entry.status) : "ERR";
62
77
  }
63
78
  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);
79
+ li.append(meta);
80
+ if (entry.message && entry.message.trim().length > 0) {
81
+ const msg = doc.createElement("div");
82
+ msg.className = "di-msg";
83
+ msg.textContent = entry.message;
84
+ li.append(msg);
85
+ }
86
+ if (entry.source === "console") {
87
+ const inspectable = getInspectableArgs(entry.args);
88
+ if (inspectable.length > 0) {
89
+ const details = doc.createElement("details");
90
+ details.className = "di-details";
91
+ const summary = doc.createElement("summary");
92
+ summary.className = "di-detailsSummary";
93
+ summary.textContent = "Inspect";
94
+ const viewerWrap = doc.createElement("div");
95
+ viewerWrap.className = "di-detailsBody";
96
+ viewerWrap.append((0, jsonViewer_1.createJsonViewer)(doc, inspectable.length === 1 ? inspectable[0] : inspectable, {
97
+ maxDepth: 6,
98
+ maxKeys: 200,
99
+ maxNodes: 2000,
100
+ }));
101
+ details.append(summary, viewerWrap);
102
+ li.append(details);
103
+ }
104
+ }
68
105
  el.append(li);
69
106
  };
70
107
  const clear = () => {
@@ -1 +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";
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\n.di-details {\n margin-top: 8px;\n}\n\n.di-detailsSummary {\n cursor: pointer;\n font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n opacity: 0.9;\n user-select: none;\n}\n\n.di-detailsBody {\n margin-top: 6px;\n padding: 8px 10px;\n border-radius: 10px;\n border: 1px solid rgba(255,255,255,0.10);\n background: rgba(0,0,0,0.22);\n}\n\n.di-jsonRoot {\n font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n.di-jsonNode {\n margin-left: 0;\n}\n\n.di-jsonSummary {\n cursor: pointer;\n user-select: none;\n opacity: 0.95;\n}\n\n.di-jsonBody {\n margin-top: 6px;\n padding-left: 12px;\n display: grid;\n gap: 4px;\n}\n\n.di-jsonRow {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 10px;\n align-items: start;\n}\n\n.di-jsonKey {\n opacity: 0.85;\n color: rgba(255,255,255,0.78);\n}\n\n.di-jsonValue {\n opacity: 0.95;\n color: rgba(255,255,255,0.92);\n}\n\n.di-jsonMore, .di-jsonTrunc {\n opacity: 0.75;\n}\n";
@@ -279,4 +279,67 @@ exports.PANEL_CSS = `
279
279
  word-break: break-word;
280
280
  font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
281
281
  }
282
+
283
+ .di-details {
284
+ margin-top: 8px;
285
+ }
286
+
287
+ .di-detailsSummary {
288
+ cursor: pointer;
289
+ font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
290
+ opacity: 0.9;
291
+ user-select: none;
292
+ }
293
+
294
+ .di-detailsBody {
295
+ margin-top: 6px;
296
+ padding: 8px 10px;
297
+ border-radius: 10px;
298
+ border: 1px solid rgba(255,255,255,0.10);
299
+ background: rgba(0,0,0,0.22);
300
+ }
301
+
302
+ .di-jsonRoot {
303
+ font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
304
+ white-space: pre-wrap;
305
+ word-break: break-word;
306
+ }
307
+
308
+ .di-jsonNode {
309
+ margin-left: 0;
310
+ }
311
+
312
+ .di-jsonSummary {
313
+ cursor: pointer;
314
+ user-select: none;
315
+ opacity: 0.95;
316
+ }
317
+
318
+ .di-jsonBody {
319
+ margin-top: 6px;
320
+ padding-left: 12px;
321
+ display: grid;
322
+ gap: 4px;
323
+ }
324
+
325
+ .di-jsonRow {
326
+ display: grid;
327
+ grid-template-columns: auto 1fr;
328
+ gap: 10px;
329
+ align-items: start;
330
+ }
331
+
332
+ .di-jsonKey {
333
+ opacity: 0.85;
334
+ color: rgba(255,255,255,0.78);
335
+ }
336
+
337
+ .di-jsonValue {
338
+ opacity: 0.95;
339
+ color: rgba(255,255,255,0.92);
340
+ }
341
+
342
+ .di-jsonMore, .di-jsonTrunc {
343
+ opacity: 0.75;
344
+ }
282
345
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-inspector",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "In-page devtools-style logger panel for web apps (console + network).",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",