dev-inspector 1.0.0 → 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.
package/README.md CHANGED
@@ -28,6 +28,26 @@ initDevInspector({
28
28
  });
29
29
  ```
30
30
 
31
+ ## Important: Browser-only (SSR)
32
+
33
+ Dev Inspector’s UI (`createPanel()` and the default `initDevInspector()` flow) requires a **browser environment** (it needs `document`).
34
+
35
+ If your app uses **SSR** (Next.js, Remix, Nuxt, SvelteKit, etc.), do not call `initDevInspector()` at module scope on the server. Initialize it **client-side only** (e.g. in an effect, lifecycle hook, or a client-only component).
36
+
37
+ Example (client-only init with dynamic import):
38
+
39
+ ```ts
40
+ async function initInBrowser() {
41
+ if (typeof window === "undefined") return;
42
+ const { initDevInspector } = await import("dev-inspector");
43
+ initDevInspector({
44
+ panelOptions: { initiallyOpen: true, title: "Dev Inspector" },
45
+ });
46
+ }
47
+
48
+ initInBrowser();
49
+ ```
50
+
31
51
  If you want manual control, you can keep the returned handles:
32
52
 
33
53
  ```ts
@@ -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);
@@ -24,6 +21,34 @@ function truncateString(value, maxLen) {
24
21
  return value;
25
22
  return value.slice(0, maxLen);
26
23
  }
24
+ function coerceUrl(input) {
25
+ if (typeof input === "string")
26
+ return input;
27
+ if (!input)
28
+ return undefined;
29
+ if (typeof input.href === "string")
30
+ return input.href;
31
+ if (typeof input.url === "string")
32
+ return input.url;
33
+ return undefined;
34
+ }
35
+ function formatUrlForMessage(raw) {
36
+ var _a, _b;
37
+ if (!raw)
38
+ return "";
39
+ try {
40
+ const loc = globalThis;
41
+ const base = typeof ((_a = loc.location) === null || _a === void 0 ? void 0 : _a.href) === "string" ? loc.location.href : undefined;
42
+ const u = new URL(raw, base);
43
+ const origin = typeof ((_b = loc.location) === null || _b === void 0 ? void 0 : _b.origin) === "string" ? loc.location.origin : undefined;
44
+ if (origin && u.origin === origin)
45
+ return `${u.pathname}${u.search}${u.hash}`;
46
+ return u.href;
47
+ }
48
+ catch (_c) {
49
+ return raw;
50
+ }
51
+ }
27
52
  async function readResponseBody(response, maxLen) {
28
53
  try {
29
54
  const text = await response.text();
@@ -34,12 +59,11 @@ async function readResponseBody(response, maxLen) {
34
59
  }
35
60
  }
36
61
  function makeMessage(entry) {
37
- var _a, _b;
62
+ var _a;
38
63
  const m = (_a = entry.method) !== null && _a !== void 0 ? _a : "";
39
- const u = (_b = entry.url) !== null && _b !== void 0 ? _b : "";
64
+ const u = formatUrlForMessage(entry.url);
40
65
  const s = typeof entry.status === "number" ? ` ${entry.status}` : "";
41
- const d = typeof entry.durationMs === "number" ? ` ${Math.round(entry.durationMs)}ms` : "";
42
- return `${m} ${u}${s}${d}`.trim();
66
+ return `${m} ${u}${s}`.trim();
43
67
  }
44
68
  const xhrMeta = new WeakMap();
45
69
  function installNetworkLogger(options) {
@@ -56,17 +80,13 @@ function installNetworkLogger(options) {
56
80
  if (canFetch) {
57
81
  g.fetch = (async (...args) => {
58
82
  var _a;
59
- const start = now();
60
83
  const id = createId();
61
84
  let method;
62
85
  let url;
63
86
  let requestBody;
64
87
  try {
65
88
  const [input, init] = args;
66
- if (typeof input === "string")
67
- url = input;
68
- else if (input && typeof input.url === "string")
69
- url = input.url;
89
+ url = coerceUrl(input);
70
90
  const reqMethodFromInit = init === null || init === void 0 ? void 0 : init.method;
71
91
  const reqMethodFromInput = input && typeof input.method === "string" ? input.method : undefined;
72
92
  method = ((_a = reqMethodFromInit !== null && reqMethodFromInit !== void 0 ? reqMethodFromInit : reqMethodFromInput) !== null && _a !== void 0 ? _a : "GET").toUpperCase();
@@ -77,8 +97,7 @@ function installNetworkLogger(options) {
77
97
  void 0;
78
98
  }
79
99
  try {
80
- const res = await originalFetch(...args);
81
- const durationMs = now() - start;
100
+ const res = await Reflect.apply(originalFetch, globalThis, args);
82
101
  const status = res.status;
83
102
  let responseBody;
84
103
  if (includeBodies) {
@@ -93,17 +112,15 @@ function installNetworkLogger(options) {
93
112
  method,
94
113
  url,
95
114
  status,
96
- durationMs,
97
115
  requestBody: includeBodies ? requestBody : undefined,
98
116
  responseBody: includeBodies ? responseBody : undefined,
99
- message: makeMessage({ method, url, status, durationMs }),
117
+ message: makeMessage({ method, url, status }),
100
118
  };
101
119
  options.emit(entry);
102
120
  }
103
121
  return res;
104
122
  }
105
123
  catch (err) {
106
- const durationMs = now() - start;
107
124
  if (active) {
108
125
  const entry = {
109
126
  id,
@@ -112,10 +129,9 @@ function installNetworkLogger(options) {
112
129
  method,
113
130
  url,
114
131
  status: undefined,
115
- durationMs,
116
132
  requestBody: includeBodies ? requestBody : undefined,
117
133
  responseBody: includeBodies ? safeToString(err) : undefined,
118
- message: makeMessage({ method, url, status: undefined, durationMs }),
134
+ message: makeMessage({ method, url, status: undefined }),
119
135
  };
120
136
  options.emit(entry);
121
137
  }
@@ -130,16 +146,16 @@ function installNetworkLogger(options) {
130
146
  proto.open = function (...args) {
131
147
  try {
132
148
  const [method, url] = args;
133
- 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) });
134
150
  }
135
151
  catch (_a) {
136
- xhrMeta.set(this, { id: createId(), start: now() });
152
+ xhrMeta.set(this, { id: createId() });
137
153
  }
138
154
  return originalOpen.apply(this, args);
139
155
  };
140
156
  proto.send = function (...args) {
141
157
  var _a;
142
- 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() };
143
159
  if (includeBodies) {
144
160
  try {
145
161
  meta.requestBody = args[0];
@@ -150,7 +166,6 @@ function installNetworkLogger(options) {
150
166
  }
151
167
  xhrMeta.set(this, meta);
152
168
  const onLoadEnd = () => {
153
- const durationMs = now() - meta.start;
154
169
  const status = typeof this.status === "number" ? this.status : undefined;
155
170
  let responseBody;
156
171
  if (includeBodies) {
@@ -172,10 +187,9 @@ function installNetworkLogger(options) {
172
187
  method: meta.method,
173
188
  url: meta.url,
174
189
  status,
175
- durationMs,
176
190
  requestBody: includeBodies ? meta.requestBody : undefined,
177
191
  responseBody: includeBodies ? responseBody : undefined,
178
- message: makeMessage({ method: meta.method, url: meta.url, status, durationMs }),
192
+ message: makeMessage({ method: meta.method, url: meta.url, status }),
179
193
  };
180
194
  options.emit(entry);
181
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.0",
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",
@@ -39,7 +39,6 @@
39
39
  "files": [
40
40
  "lib/**/*"
41
41
  ],
42
-
43
42
  "devDependencies": {
44
43
  "@eslint/js": "^9.39.2",
45
44
  "eslint": "^9.39.2",