dev-inspector 1.0.4 → 1.0.5

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.
@@ -14,12 +14,12 @@ function safeToString(value) {
14
14
  return "[unstringifiable]";
15
15
  }
16
16
  }
17
- function truncateString(value, maxLen) {
17
+ function truncateStringMeta(value, maxLen) {
18
18
  if (maxLen <= 0)
19
- return "";
19
+ return { value: "", truncated: value.length > 0 };
20
20
  if (value.length <= maxLen)
21
- return value;
22
- return value.slice(0, maxLen);
21
+ return { value, truncated: false };
22
+ return { value: value.slice(0, maxLen), truncated: true };
23
23
  }
24
24
  function coerceUrl(input) {
25
25
  if (typeof input === "string")
@@ -52,7 +52,8 @@ function formatUrlForMessage(raw) {
52
52
  async function readResponseBody(response, maxLen) {
53
53
  try {
54
54
  const text = await response.text();
55
- return truncateString(text, maxLen);
55
+ const t = truncateStringMeta(text, maxLen);
56
+ return { body: t.value, truncated: t.truncated };
56
57
  }
57
58
  catch (_a) {
58
59
  return undefined;
@@ -79,30 +80,40 @@ function installNetworkLogger(options) {
79
80
  const originalXhrSend = canXhr ? g.XMLHttpRequest.prototype.send : undefined;
80
81
  if (canFetch) {
81
82
  g.fetch = (async (...args) => {
82
- var _a;
83
+ var _a, _b;
83
84
  const id = createId();
84
85
  let method;
85
86
  let url;
86
87
  let requestBody;
88
+ let requestBodyTruncated = false;
87
89
  try {
88
90
  const [input, init] = args;
89
91
  url = coerceUrl(input);
90
92
  const reqMethodFromInit = init === null || init === void 0 ? void 0 : init.method;
91
93
  const reqMethodFromInput = input && typeof input.method === "string" ? input.method : undefined;
92
94
  method = ((_a = reqMethodFromInit !== null && reqMethodFromInit !== void 0 ? reqMethodFromInit : reqMethodFromInput) !== null && _a !== void 0 ? _a : "GET").toUpperCase();
93
- if (includeBodies && init && "body" in init)
95
+ if (includeBodies && init && "body" in init) {
94
96
  requestBody = init.body;
97
+ if (typeof requestBody === "string") {
98
+ const t = truncateStringMeta(requestBody, maxBodyLength);
99
+ requestBody = t.value;
100
+ requestBodyTruncated = t.truncated;
101
+ }
102
+ }
95
103
  }
96
- catch (_b) {
104
+ catch (_c) {
97
105
  void 0;
98
106
  }
99
107
  try {
100
108
  const res = await Reflect.apply(originalFetch, globalThis, args);
101
109
  const status = res.status;
102
110
  let responseBody;
111
+ let responseBodyTruncated = false;
103
112
  if (includeBodies) {
104
113
  const cloned = res.clone();
105
- responseBody = await readResponseBody(cloned, maxBodyLength);
114
+ const r = await readResponseBody(cloned, maxBodyLength);
115
+ responseBody = r === null || r === void 0 ? void 0 : r.body;
116
+ responseBodyTruncated = (_b = r === null || r === void 0 ? void 0 : r.truncated) !== null && _b !== void 0 ? _b : false;
106
117
  }
107
118
  if (active) {
108
119
  const entry = {
@@ -113,7 +124,10 @@ function installNetworkLogger(options) {
113
124
  url,
114
125
  status,
115
126
  requestBody: includeBodies ? requestBody : undefined,
127
+ requestBodyTruncated: includeBodies ? requestBodyTruncated : undefined,
116
128
  responseBody: includeBodies ? responseBody : undefined,
129
+ responseBodyTruncated: includeBodies ? responseBodyTruncated : undefined,
130
+ bodyMaxLength: includeBodies ? maxBodyLength : undefined,
117
131
  message: makeMessage({ method, url, status }),
118
132
  };
119
133
  options.emit(entry);
@@ -130,7 +144,10 @@ function installNetworkLogger(options) {
130
144
  url,
131
145
  status: undefined,
132
146
  requestBody: includeBodies ? requestBody : undefined,
147
+ requestBodyTruncated: includeBodies ? requestBodyTruncated : undefined,
133
148
  responseBody: includeBodies ? safeToString(err) : undefined,
149
+ responseBodyTruncated: undefined,
150
+ bodyMaxLength: includeBodies ? maxBodyLength : undefined,
134
151
  message: makeMessage({ method, url, status: undefined }),
135
152
  };
136
153
  options.emit(entry);
@@ -156,9 +173,15 @@ function installNetworkLogger(options) {
156
173
  proto.send = function (...args) {
157
174
  var _a;
158
175
  const meta = (_a = xhrMeta.get(this)) !== null && _a !== void 0 ? _a : { id: createId() };
176
+ let requestBodyTruncated = false;
159
177
  if (includeBodies) {
160
178
  try {
161
179
  meta.requestBody = args[0];
180
+ if (typeof meta.requestBody === "string") {
181
+ const t = truncateStringMeta(meta.requestBody, maxBodyLength);
182
+ meta.requestBody = t.value;
183
+ requestBodyTruncated = t.truncated;
184
+ }
162
185
  }
163
186
  catch (_b) {
164
187
  void 0;
@@ -168,10 +191,14 @@ function installNetworkLogger(options) {
168
191
  const onLoadEnd = () => {
169
192
  const status = typeof this.status === "number" ? this.status : undefined;
170
193
  let responseBody;
194
+ let responseBodyTruncated = false;
171
195
  if (includeBodies) {
172
196
  try {
173
- if (typeof this.responseText === "string")
174
- responseBody = truncateString(this.responseText, maxBodyLength);
197
+ if (typeof this.responseText === "string") {
198
+ const t = truncateStringMeta(this.responseText, maxBodyLength);
199
+ responseBody = t.value;
200
+ responseBodyTruncated = t.truncated;
201
+ }
175
202
  else
176
203
  responseBody = undefined;
177
204
  }
@@ -188,7 +215,10 @@ function installNetworkLogger(options) {
188
215
  url: meta.url,
189
216
  status,
190
217
  requestBody: includeBodies ? meta.requestBody : undefined,
218
+ requestBodyTruncated: includeBodies ? requestBodyTruncated : undefined,
191
219
  responseBody: includeBodies ? responseBody : undefined,
220
+ responseBodyTruncated: includeBodies ? responseBodyTruncated : undefined,
221
+ bodyMaxLength: includeBodies ? maxBodyLength : undefined,
192
222
  message: makeMessage({ method: meta.method, url: meta.url, status }),
193
223
  };
194
224
  options.emit(entry);
@@ -0,0 +1,2 @@
1
+ import type { LogList } from "./types";
2
+ export declare function createLogList(doc: Document): LogList;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLogList = createLogList;
4
+ const jsonViewer_1 = require("../jsonViewer");
5
+ const networkDetails_1 = require("./networkDetails");
6
+ const shared_1 = require("./shared");
7
+ const shared_2 = require("./shared");
8
+ function createLogList(doc) {
9
+ const el = doc.createElement("ul");
10
+ el.className = "di-list";
11
+ const append = (entry) => {
12
+ const li = doc.createElement("li");
13
+ const tone = (0, shared_2.toneForEntry)(entry);
14
+ li.className = `di-item ${(0, shared_2.classForTone)(tone)}`;
15
+ const meta = doc.createElement("div");
16
+ meta.className = "di-meta";
17
+ const time = doc.createElement("span");
18
+ time.textContent = (0, shared_2.fmtTime)(entry.timestamp);
19
+ const source = doc.createElement("span");
20
+ source.textContent = entry.source;
21
+ const detail = doc.createElement("span");
22
+ if (entry.source === "console") {
23
+ detail.textContent = entry.level;
24
+ }
25
+ else {
26
+ detail.className = `di-statusChip ${(0, shared_2.isNetworkFailure)(entry) ? "di-statusChipError" : "di-statusChipSuccess"}`;
27
+ detail.textContent = typeof entry.status === "number" ? String(entry.status) : "ERR";
28
+ }
29
+ meta.append(time, source, detail);
30
+ li.append(meta);
31
+ if (entry.message && entry.message.trim().length > 0) {
32
+ const msg = doc.createElement("div");
33
+ msg.className = "di-msg";
34
+ msg.textContent = entry.message;
35
+ li.append(msg);
36
+ }
37
+ if (entry.source === "console") {
38
+ const inspectable = (0, shared_1.getInspectableArgs)(entry.args);
39
+ if (inspectable.length > 0) {
40
+ const details = doc.createElement("details");
41
+ details.className = "di-details";
42
+ const summary = doc.createElement("summary");
43
+ summary.className = "di-detailsSummary";
44
+ summary.textContent = "Inspect";
45
+ const viewerWrap = doc.createElement("div");
46
+ viewerWrap.className = "di-detailsBody";
47
+ viewerWrap.append((0, jsonViewer_1.createJsonViewer)(doc, inspectable.length === 1 ? inspectable[0] : inspectable, {
48
+ maxDepth: 6,
49
+ maxKeys: 200,
50
+ maxNodes: 2000,
51
+ }));
52
+ details.append(summary, viewerWrap);
53
+ li.append(details);
54
+ }
55
+ }
56
+ if (entry.source === "network") {
57
+ const details = (0, networkDetails_1.createNetworkDetails)(doc, entry);
58
+ if (details)
59
+ li.append(details);
60
+ }
61
+ el.append(li);
62
+ };
63
+ const clear = () => {
64
+ el.replaceChildren();
65
+ };
66
+ return { el, append, clear };
67
+ }
@@ -0,0 +1,2 @@
1
+ export { createLogList } from "./createLogList";
2
+ export type { LogList } from "./types";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLogList = void 0;
4
+ var createLogList_1 = require("./createLogList");
5
+ Object.defineProperty(exports, "createLogList", { enumerable: true, get: function () { return createLogList_1.createLogList; } });
@@ -0,0 +1,2 @@
1
+ export declare function launchMiniConfetti(anchor: HTMLElement): void;
2
+ export declare function copyText(text: string): Promise<boolean>;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.launchMiniConfetti = launchMiniConfetti;
4
+ exports.copyText = copyText;
5
+ function prefersReducedMotion() {
6
+ var _a, _b;
7
+ try {
8
+ const mm = globalThis;
9
+ return ((_b = (_a = mm.matchMedia) === null || _a === void 0 ? void 0 : _a.call(mm, "(prefers-reduced-motion: reduce)")) === null || _b === void 0 ? void 0 : _b.matches) === true;
10
+ }
11
+ catch (_c) {
12
+ return false;
13
+ }
14
+ }
15
+ function rand(min, max) {
16
+ return min + Math.random() * (max - min);
17
+ }
18
+ function launchMiniConfetti(anchor) {
19
+ if (prefersReducedMotion())
20
+ return;
21
+ if (typeof document === "undefined")
22
+ return;
23
+ const rect = anchor.getBoundingClientRect();
24
+ const cx = rect.left + rect.width / 2;
25
+ const cy = rect.top + rect.height / 2;
26
+ const root = document.createElement("div");
27
+ root.style.position = "fixed";
28
+ root.style.left = "0";
29
+ root.style.top = "0";
30
+ root.style.width = "0";
31
+ root.style.height = "0";
32
+ root.style.pointerEvents = "none";
33
+ root.style.zIndex = "2147483647";
34
+ document.body.append(root);
35
+ const colors = ["#a7ff00", "#7c5cff", "#00c4ff", "#ff9030", "#ef4444"];
36
+ const n = 14;
37
+ const duration = 520;
38
+ for (let i = 0; i < n; i += 1) {
39
+ const p = document.createElement("span");
40
+ const size = Math.round(rand(3, 6));
41
+ p.style.position = "fixed";
42
+ p.style.left = `${cx}px`;
43
+ p.style.top = `${cy}px`;
44
+ p.style.width = `${size}px`;
45
+ p.style.height = `${size}px`;
46
+ p.style.borderRadius = Math.random() < 0.35 ? "999px" : "2px";
47
+ p.style.background = colors[i % colors.length];
48
+ p.style.opacity = "1";
49
+ p.style.willChange = "transform, opacity";
50
+ root.append(p);
51
+ const angle = rand(0, Math.PI * 2);
52
+ const dist = rand(18, 46);
53
+ const dx = Math.cos(angle) * dist;
54
+ const dy = Math.sin(angle) * dist + rand(8, 16);
55
+ const rot = rand(-220, 220);
56
+ const delay = Math.round(rand(0, 60));
57
+ try {
58
+ const anim = p.animate([
59
+ { transform: `translate(-50%, -50%) translate(0px, 0px) rotate(0deg) scale(1)`, opacity: 1 },
60
+ { transform: `translate(-50%, -50%) translate(${dx}px, ${dy}px) rotate(${rot}deg) scale(0.9)`, opacity: 0 },
61
+ ], { duration, delay, easing: "cubic-bezier(0.2, 0.7, 0.2, 1)", fill: "forwards" });
62
+ anim.addEventListener("finish", () => p.remove());
63
+ }
64
+ catch (_a) {
65
+ globalThis.setTimeout(() => p.remove(), duration + delay + 50);
66
+ }
67
+ }
68
+ globalThis.setTimeout(() => root.remove(), duration + 120);
69
+ }
70
+ async function copyText(text) {
71
+ var _a, _b;
72
+ try {
73
+ const nav = globalThis;
74
+ const fn = (_b = (_a = nav.navigator) === null || _a === void 0 ? void 0 : _a.clipboard) === null || _b === void 0 ? void 0 : _b.writeText;
75
+ if (typeof fn === "function") {
76
+ await fn(text);
77
+ return true;
78
+ }
79
+ }
80
+ catch (_c) {
81
+ void 0;
82
+ }
83
+ try {
84
+ if (typeof document === "undefined")
85
+ return false;
86
+ const ta = document.createElement("textarea");
87
+ ta.value = text;
88
+ ta.setAttribute("readonly", "true");
89
+ ta.style.position = "fixed";
90
+ ta.style.left = "-9999px";
91
+ ta.style.top = "0";
92
+ document.body.append(ta);
93
+ ta.select();
94
+ const ok = document.execCommand("copy");
95
+ ta.remove();
96
+ return ok;
97
+ }
98
+ catch (_d) {
99
+ return false;
100
+ }
101
+ }
@@ -0,0 +1,2 @@
1
+ import type { NetworkLogEntry } from "../../../utils/types";
2
+ export declare function createNetworkDetails(doc: Document, entry: NetworkLogEntry): HTMLElement | null;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNetworkDetails = createNetworkDetails;
4
+ const copy_1 = require("./copy");
5
+ const renderValue_1 = require("./renderValue");
6
+ function createBodyDetails(doc, title, value, truncated, maxLen) {
7
+ const details = doc.createElement("details");
8
+ details.className = "di-details di-netBodyDetails";
9
+ const summary = doc.createElement("summary");
10
+ summary.className = "di-detailsSummary di-netDetailsSummary";
11
+ const titleEl = doc.createElement("span");
12
+ titleEl.className = "di-netDetailsTitle";
13
+ titleEl.textContent = title;
14
+ const copyBtn = doc.createElement("button");
15
+ copyBtn.type = "button";
16
+ copyBtn.className = "di-copyBtn";
17
+ copyBtn.textContent = "Copy";
18
+ copyBtn.setAttribute("aria-label", `Copy ${title.toLowerCase()}`);
19
+ copyBtn.addEventListener("click", (ev) => {
20
+ ev.preventDefault();
21
+ ev.stopPropagation();
22
+ const txt = (0, renderValue_1.valueToCopyText)(value);
23
+ void (0, copy_1.copyText)(txt).then((ok) => {
24
+ if (!ok)
25
+ return;
26
+ (0, copy_1.launchMiniConfetti)(copyBtn);
27
+ const prev = copyBtn.textContent;
28
+ copyBtn.textContent = "Copied";
29
+ globalThis.setTimeout(() => {
30
+ copyBtn.textContent = prev !== null && prev !== void 0 ? prev : "Copy";
31
+ }, 900);
32
+ });
33
+ });
34
+ summary.append(titleEl, copyBtn);
35
+ const body = doc.createElement("div");
36
+ body.className = "di-detailsBody";
37
+ if (truncated) {
38
+ const warn = doc.createElement("div");
39
+ warn.className = "di-netTrunc";
40
+ warn.textContent = typeof maxLen === "number" ? `Truncated to first ${maxLen} characters.` : "Truncated.";
41
+ body.append(warn);
42
+ }
43
+ body.append((0, renderValue_1.valueToElement)(doc, value));
44
+ details.append(summary, body);
45
+ return details;
46
+ }
47
+ function createNetworkDetails(doc, entry) {
48
+ const hasReq = typeof entry.requestBody !== "undefined";
49
+ const hasRes = typeof entry.responseBody !== "undefined";
50
+ if (!hasReq && !hasRes)
51
+ return null;
52
+ const wrap = doc.createElement("div");
53
+ wrap.className = "di-netDetailsWrap";
54
+ if (hasReq) {
55
+ wrap.append(createBodyDetails(doc, "Request", entry.requestBody, entry.requestBodyTruncated === true, entry.bodyMaxLength));
56
+ }
57
+ if (hasRes) {
58
+ wrap.append(createBodyDetails(doc, "Response", entry.responseBody, entry.responseBodyTruncated === true, entry.bodyMaxLength));
59
+ }
60
+ return wrap;
61
+ }
@@ -0,0 +1 @@
1
+ export { createNetworkDetails } from "./createNetworkDetails";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNetworkDetails = void 0;
4
+ var createNetworkDetails_1 = require("./createNetworkDetails");
5
+ Object.defineProperty(exports, "createNetworkDetails", { enumerable: true, get: function () { return createNetworkDetails_1.createNetworkDetails; } });
@@ -0,0 +1,2 @@
1
+ export declare function valueToElement(doc: Document, value: unknown): HTMLElement;
2
+ export declare function valueToCopyText(value: unknown): string;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.valueToElement = valueToElement;
4
+ exports.valueToCopyText = valueToCopyText;
5
+ const jsonViewer_1 = require("../../jsonViewer");
6
+ function tryParseJson(text) {
7
+ const t = text.trim();
8
+ if (t.length < 2)
9
+ return undefined;
10
+ const looksLikeObject = t.startsWith("{") && t.endsWith("}");
11
+ const looksLikeArray = t.startsWith("[") && t.endsWith("]");
12
+ if (!looksLikeObject && !looksLikeArray)
13
+ return undefined;
14
+ try {
15
+ return JSON.parse(t);
16
+ }
17
+ catch (_a) {
18
+ return undefined;
19
+ }
20
+ }
21
+ function safeStringify(value) {
22
+ const seen = new WeakSet();
23
+ const replacer = (_key, v) => {
24
+ if (typeof v === "object" && v !== null) {
25
+ const o = v;
26
+ if (seen.has(o))
27
+ return "[circular]";
28
+ seen.add(o);
29
+ }
30
+ return v;
31
+ };
32
+ try {
33
+ return JSON.stringify(value, replacer, 2);
34
+ }
35
+ catch (_a) {
36
+ try {
37
+ return String(value);
38
+ }
39
+ catch (_b) {
40
+ return "[unserializable]";
41
+ }
42
+ }
43
+ }
44
+ function valueToElement(doc, value) {
45
+ if (typeof value === "string") {
46
+ const parsed = tryParseJson(value);
47
+ if (parsed && typeof parsed === "object") {
48
+ return (0, jsonViewer_1.createJsonViewer)(doc, parsed, { maxDepth: 6, maxKeys: 200, maxNodes: 2000 });
49
+ }
50
+ const el = doc.createElement("div");
51
+ el.className = "di-netBodyText";
52
+ el.textContent = value;
53
+ return el;
54
+ }
55
+ if (value instanceof Error) {
56
+ const el = doc.createElement("div");
57
+ el.className = "di-netBodyText";
58
+ el.textContent = `${value.name}: ${value.message}`;
59
+ return el;
60
+ }
61
+ if (value === null || typeof value !== "object") {
62
+ const el = doc.createElement("div");
63
+ el.className = "di-netBodyText";
64
+ el.textContent = String(value);
65
+ return el;
66
+ }
67
+ return (0, jsonViewer_1.createJsonViewer)(doc, value, { maxDepth: 6, maxKeys: 200, maxNodes: 2000 });
68
+ }
69
+ function valueToCopyText(value) {
70
+ if (typeof value === "string")
71
+ return value;
72
+ if (value instanceof Error)
73
+ return `${value.name}: ${value.message}`;
74
+ return safeStringify(value);
75
+ }
@@ -17,6 +17,126 @@ function tryParseJson(text) {
17
17
  return undefined;
18
18
  }
19
19
  }
20
+ function safeStringify(value) {
21
+ const seen = new WeakSet();
22
+ const replacer = (_key, v) => {
23
+ if (typeof v === "object" && v !== null) {
24
+ const o = v;
25
+ if (seen.has(o))
26
+ return "[circular]";
27
+ seen.add(o);
28
+ }
29
+ return v;
30
+ };
31
+ try {
32
+ return JSON.stringify(value, replacer, 2);
33
+ }
34
+ catch (_a) {
35
+ try {
36
+ return String(value);
37
+ }
38
+ catch (_b) {
39
+ return "[unserializable]";
40
+ }
41
+ }
42
+ }
43
+ async function copyText(text) {
44
+ var _a, _b;
45
+ try {
46
+ const nav = globalThis;
47
+ const fn = (_b = (_a = nav.navigator) === null || _a === void 0 ? void 0 : _a.clipboard) === null || _b === void 0 ? void 0 : _b.writeText;
48
+ if (typeof fn === "function") {
49
+ await fn(text);
50
+ return true;
51
+ }
52
+ }
53
+ catch (_c) {
54
+ void 0;
55
+ }
56
+ try {
57
+ if (typeof document === "undefined")
58
+ return false;
59
+ const ta = document.createElement("textarea");
60
+ ta.value = text;
61
+ ta.setAttribute("readonly", "true");
62
+ ta.style.position = "fixed";
63
+ ta.style.left = "-9999px";
64
+ ta.style.top = "0";
65
+ document.body.append(ta);
66
+ ta.select();
67
+ const ok = document.execCommand("copy");
68
+ ta.remove();
69
+ return ok;
70
+ }
71
+ catch (_d) {
72
+ return false;
73
+ }
74
+ }
75
+ function prefersReducedMotion() {
76
+ var _a, _b;
77
+ try {
78
+ const mm = globalThis;
79
+ return ((_b = (_a = mm.matchMedia) === null || _a === void 0 ? void 0 : _a.call(mm, "(prefers-reduced-motion: reduce)")) === null || _b === void 0 ? void 0 : _b.matches) === true;
80
+ }
81
+ catch (_c) {
82
+ return false;
83
+ }
84
+ }
85
+ function rand(min, max) {
86
+ return min + Math.random() * (max - min);
87
+ }
88
+ function launchMiniConfetti(anchor) {
89
+ if (prefersReducedMotion())
90
+ return;
91
+ if (typeof document === "undefined")
92
+ return;
93
+ const rect = anchor.getBoundingClientRect();
94
+ const cx = rect.left + rect.width / 2;
95
+ const cy = rect.top + rect.height / 2;
96
+ const root = document.createElement("div");
97
+ root.style.position = "fixed";
98
+ root.style.left = "0";
99
+ root.style.top = "0";
100
+ root.style.width = "0";
101
+ root.style.height = "0";
102
+ root.style.pointerEvents = "none";
103
+ root.style.zIndex = "2147483647";
104
+ document.body.append(root);
105
+ const colors = ["#a7ff00", "#7c5cff", "#00c4ff", "#ff9030", "#ef4444"];
106
+ const n = 14;
107
+ const duration = 520;
108
+ for (let i = 0; i < n; i += 1) {
109
+ const p = document.createElement("span");
110
+ const size = Math.round(rand(3, 6));
111
+ p.style.position = "fixed";
112
+ p.style.left = `${cx}px`;
113
+ p.style.top = `${cy}px`;
114
+ p.style.width = `${size}px`;
115
+ p.style.height = `${size}px`;
116
+ p.style.borderRadius = Math.random() < 0.35 ? "999px" : "2px";
117
+ p.style.background = colors[i % colors.length];
118
+ p.style.opacity = "1";
119
+ p.style.willChange = "transform, opacity";
120
+ root.append(p);
121
+ const angle = rand(0, Math.PI * 2);
122
+ const dist = rand(18, 46);
123
+ const dx = Math.cos(angle) * dist;
124
+ const dy = Math.sin(angle) * dist + rand(8, 16);
125
+ const rot = rand(-220, 220);
126
+ const delay = Math.round(rand(0, 60));
127
+ try {
128
+ const anim = p.animate([
129
+ { transform: `translate(-50%, -50%) translate(0px, 0px) rotate(0deg) scale(1)`, opacity: 1 },
130
+ { transform: `translate(-50%, -50%) translate(${dx}px, ${dy}px) rotate(${rot}deg) scale(0.9)`, opacity: 0 },
131
+ ], { duration, delay, easing: "cubic-bezier(0.2, 0.7, 0.2, 1)", fill: "forwards" });
132
+ anim.addEventListener("finish", () => p.remove());
133
+ }
134
+ catch (_a) {
135
+ globalThis.setTimeout(() => p.remove(), duration + delay + 50);
136
+ }
137
+ }
138
+ globalThis.setTimeout(() => root.remove(), duration + 120);
139
+ }
20
140
  function valueToElement(doc, value) {
21
141
  if (typeof value === "string") {
22
142
  const parsed = tryParseJson(value);
@@ -42,34 +162,66 @@ function valueToElement(doc, value) {
42
162
  }
43
163
  return (0, jsonViewer_1.createJsonViewer)(doc, value, { maxDepth: 6, maxKeys: 200, maxNodes: 2000 });
44
164
  }
45
- function appendSection(doc, wrap, label, value) {
46
- if (typeof value === "undefined")
47
- return;
48
- const section = doc.createElement("div");
49
- section.className = "di-netBodySection";
50
- const title = doc.createElement("div");
51
- title.className = "di-netBodyLabel";
52
- title.textContent = label;
53
- section.append(title, valueToElement(doc, value));
54
- wrap.append(section);
165
+ function valueToCopyText(value) {
166
+ if (typeof value === "string")
167
+ return value;
168
+ if (value instanceof Error)
169
+ return `${value.name}: ${value.message}`;
170
+ return safeStringify(value);
55
171
  }
56
- function createNetworkDetails(doc, entry) {
57
- const hasReq = typeof entry.requestBody !== "undefined";
58
- const hasRes = typeof entry.responseBody !== "undefined";
59
- if (!hasReq && !hasRes)
60
- return null;
172
+ function createBodyDetails(doc, title, value, truncated, maxLen) {
61
173
  const details = doc.createElement("details");
62
- details.className = "di-details";
174
+ details.className = "di-details di-netBodyDetails";
63
175
  const summary = doc.createElement("summary");
64
- summary.className = "di-detailsSummary";
65
- summary.textContent = "Body";
176
+ summary.className = "di-detailsSummary di-netDetailsSummary";
177
+ const titleEl = doc.createElement("span");
178
+ titleEl.className = "di-netDetailsTitle";
179
+ titleEl.textContent = title;
180
+ const copyBtn = doc.createElement("button");
181
+ copyBtn.type = "button";
182
+ copyBtn.className = "di-copyBtn";
183
+ copyBtn.textContent = "Copy";
184
+ copyBtn.setAttribute("aria-label", `Copy ${title.toLowerCase()}`);
185
+ copyBtn.addEventListener("click", (ev) => {
186
+ ev.preventDefault();
187
+ ev.stopPropagation();
188
+ const txt = valueToCopyText(value);
189
+ void copyText(txt).then((ok) => {
190
+ if (!ok)
191
+ return;
192
+ launchMiniConfetti(copyBtn);
193
+ const prev = copyBtn.textContent;
194
+ copyBtn.textContent = "Copied";
195
+ globalThis.setTimeout(() => {
196
+ copyBtn.textContent = prev !== null && prev !== void 0 ? prev : "Copy";
197
+ }, 900);
198
+ });
199
+ });
200
+ summary.append(titleEl, copyBtn);
66
201
  const body = doc.createElement("div");
67
202
  body.className = "di-detailsBody";
68
- const grid = doc.createElement("div");
69
- grid.className = "di-netBodies";
70
- appendSection(doc, grid, "Request body", entry.requestBody);
71
- appendSection(doc, grid, "Response body", entry.responseBody);
72
- body.append(grid);
203
+ if (truncated) {
204
+ const warn = doc.createElement("div");
205
+ warn.className = "di-netTrunc";
206
+ warn.textContent = typeof maxLen === "number" ? `Truncated to first ${maxLen} characters.` : "Truncated.";
207
+ body.append(warn);
208
+ }
209
+ body.append(valueToElement(doc, value));
73
210
  details.append(summary, body);
74
211
  return details;
75
212
  }
213
+ function createNetworkDetails(doc, entry) {
214
+ const hasReq = typeof entry.requestBody !== "undefined";
215
+ const hasRes = typeof entry.responseBody !== "undefined";
216
+ if (!hasReq && !hasRes)
217
+ return null;
218
+ const wrap = doc.createElement("div");
219
+ wrap.className = "di-netDetailsWrap";
220
+ if (hasReq) {
221
+ wrap.append(createBodyDetails(doc, "Request", entry.requestBody, entry.requestBodyTruncated === true, entry.bodyMaxLength));
222
+ }
223
+ if (hasRes) {
224
+ wrap.append(createBodyDetails(doc, "Response", entry.responseBody, entry.responseBodyTruncated === true, entry.bodyMaxLength));
225
+ }
226
+ return wrap;
227
+ }
@@ -0,0 +1 @@
1
+ export declare function getInspectableArgs(args: unknown[]): unknown[];
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInspectableArgs = getInspectableArgs;
4
+ function isInspectableValue(value) {
5
+ if (typeof value !== "object" || value === null)
6
+ return false;
7
+ if (value instanceof Error)
8
+ return false;
9
+ if (value instanceof Date)
10
+ return false;
11
+ if (value instanceof RegExp)
12
+ return false;
13
+ return true;
14
+ }
15
+ function getInspectableArgs(args) {
16
+ return args.filter(isInspectableValue);
17
+ }
@@ -0,0 +1,3 @@
1
+ export { fmtTime } from "./time";
2
+ export { toneForEntry, classForTone, isNetworkFailure } from "./tone";
3
+ export { getInspectableArgs } from "./consoleInspect";
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInspectableArgs = exports.isNetworkFailure = exports.classForTone = exports.toneForEntry = exports.fmtTime = void 0;
4
+ var time_1 = require("./time");
5
+ Object.defineProperty(exports, "fmtTime", { enumerable: true, get: function () { return time_1.fmtTime; } });
6
+ var tone_1 = require("./tone");
7
+ Object.defineProperty(exports, "toneForEntry", { enumerable: true, get: function () { return tone_1.toneForEntry; } });
8
+ Object.defineProperty(exports, "classForTone", { enumerable: true, get: function () { return tone_1.classForTone; } });
9
+ Object.defineProperty(exports, "isNetworkFailure", { enumerable: true, get: function () { return tone_1.isNetworkFailure; } });
10
+ var consoleInspect_1 = require("./consoleInspect");
11
+ Object.defineProperty(exports, "getInspectableArgs", { enumerable: true, get: function () { return consoleInspect_1.getInspectableArgs; } });
@@ -0,0 +1 @@
1
+ export declare function fmtTime(ts: number): string;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fmtTime = fmtTime;
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
+ }
@@ -0,0 +1,6 @@
1
+ import type { LogEntry } from "../../../utils/types";
2
+ type Tone = "neutral" | "warning" | "error" | "success";
3
+ export declare function toneForEntry(entry: LogEntry): Tone;
4
+ export declare function classForTone(tone: Tone): string;
5
+ export declare function isNetworkFailure(entry: LogEntry): boolean;
6
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toneForEntry = toneForEntry;
4
+ exports.classForTone = classForTone;
5
+ exports.isNetworkFailure = isNetworkFailure;
6
+ function toneForEntry(entry) {
7
+ if (entry.source === "console") {
8
+ if (entry.level === "error")
9
+ return "error";
10
+ if (entry.level === "warn")
11
+ return "warning";
12
+ return "neutral";
13
+ }
14
+ const s = entry.status;
15
+ if (typeof s !== "number")
16
+ return "error";
17
+ if (s >= 400)
18
+ return "error";
19
+ if (s >= 300)
20
+ return "warning";
21
+ return "success";
22
+ }
23
+ function classForTone(tone) {
24
+ if (tone === "error")
25
+ return "di-itemToneError";
26
+ if (tone === "warning")
27
+ return "di-itemToneWarning";
28
+ if (tone === "success")
29
+ return "di-itemToneSuccess";
30
+ return "di-itemToneNeutral";
31
+ }
32
+ function isNetworkFailure(entry) {
33
+ if (entry.source !== "network")
34
+ return false;
35
+ return typeof entry.status !== "number" || entry.status >= 400;
36
+ }
@@ -0,0 +1,6 @@
1
+ import type { LogEntry } from "../../utils/types";
2
+ export type LogList = {
3
+ el: HTMLUListElement;
4
+ append: (entry: LogEntry) => void;
5
+ clear: () => void;
6
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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\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-netBodies {\n display: grid;\n gap: 10px;\n}\n\n.di-netBodySection {\n display: grid;\n gap: 6px;\n}\n\n.di-netBodyLabel {\n font: 11px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n opacity: 0.85;\n}\n\n.di-netBodyText {\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 opacity: 0.95;\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";
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-netBodyDetails {\n border: 0;\n border-radius: 0;\n background: transparent;\n overflow: visible;\n}\n\n\n\n.di-netBodyDetails > summary.di-netDetailsSummary:hover {\n background: rgba(255,255,255,0.06);\n}\n\n.di-netBodyDetails[open] > summary.di-netDetailsSummary {\n background: rgba(255,255,255,0.08);\n border-bottom: 0;\n}\n\n.di-netBodyDetails[open] > .di-detailsBody {\n margin-top: 1px;\n}\n\n.di-netDetailsWrap {\n display: grid;\n gap: 1px;\n}\n\n.di-netDetailsSummary {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.di-netDetailsTitle {\n font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n.di-copyBtn {\n margin-left: auto;\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: 4px 8px;\n border-radius: 999px;\n cursor: pointer;\n}\n\n.di-copyBtn:hover {\n background: rgba(255,255,255,0.10);\n border-color: rgba(255,255,255,0.22);\n}\n\n.di-copyBtn:active {\n transform: translateY(1px);\n}\n\n.di-netTrunc {\n border: 1px solid rgba(245, 158, 11, 0.42);\n background: rgba(245, 158, 11, 0.10);\n color: rgba(255,255,255,0.92);\n border-radius: 10px;\n padding: 8px 10px;\n font: 11px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n margin-bottom: 8px;\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-netBodyText {\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 opacity: 0.95;\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";
@@ -291,27 +291,79 @@ exports.PANEL_CSS = `
291
291
  user-select: none;
292
292
  }
293
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);
294
+ .di-netBodyDetails {
295
+ border: 0;
296
+ border-radius: 0;
297
+ background: transparent;
298
+ overflow: visible;
299
+ }
300
+
301
+
302
+
303
+ .di-netBodyDetails > summary.di-netDetailsSummary:hover {
304
+ background: rgba(255,255,255,0.06);
305
+ }
306
+
307
+ .di-netBodyDetails[open] > summary.di-netDetailsSummary {
308
+ background: rgba(255,255,255,0.08);
309
+ border-bottom: 0;
300
310
  }
301
311
 
302
- .di-netBodies {
312
+ .di-netBodyDetails[open] > .di-detailsBody {
313
+ margin-top: 1px;
314
+ }
315
+
316
+ .di-netDetailsWrap {
303
317
  display: grid;
318
+ gap: 1px;
319
+ }
320
+
321
+ .di-netDetailsSummary {
322
+ display: flex;
323
+ align-items: center;
304
324
  gap: 10px;
305
325
  }
306
326
 
307
- .di-netBodySection {
308
- display: grid;
309
- gap: 6px;
327
+ .di-netDetailsTitle {
328
+ font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
310
329
  }
311
330
 
312
- .di-netBodyLabel {
331
+ .di-copyBtn {
332
+ margin-left: auto;
333
+ border: 1px solid rgba(255,255,255,0.14);
334
+ background: rgba(255,255,255,0.06);
335
+ color: #fff;
336
+ font: 12px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
337
+ padding: 4px 8px;
338
+ border-radius: 999px;
339
+ cursor: pointer;
340
+ }
341
+
342
+ .di-copyBtn:hover {
343
+ background: rgba(255,255,255,0.10);
344
+ border-color: rgba(255,255,255,0.22);
345
+ }
346
+
347
+ .di-copyBtn:active {
348
+ transform: translateY(1px);
349
+ }
350
+
351
+ .di-netTrunc {
352
+ border: 1px solid rgba(245, 158, 11, 0.42);
353
+ background: rgba(245, 158, 11, 0.10);
354
+ color: rgba(255,255,255,0.92);
355
+ border-radius: 10px;
356
+ padding: 8px 10px;
313
357
  font: 11px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
314
- opacity: 0.85;
358
+ margin-bottom: 8px;
359
+ }
360
+
361
+ .di-detailsBody {
362
+ margin-top: 6px;
363
+ padding: 8px 10px;
364
+ border-radius: 10px;
365
+ border: 1px solid rgba(255,255,255,0.10);
366
+ background: rgba(0,0,0,0.22);
315
367
  }
316
368
 
317
369
  .di-netBodyText {
@@ -17,7 +17,10 @@ export type NetworkLogEntry = {
17
17
  status?: number;
18
18
  durationMs?: number;
19
19
  requestBody?: unknown;
20
+ requestBodyTruncated?: boolean;
20
21
  responseBody?: unknown;
22
+ responseBodyTruncated?: boolean;
23
+ bodyMaxLength?: number;
21
24
  message: string;
22
25
  };
23
26
  export type LogEntry = ConsoleLogEntry | NetworkLogEntry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev-inspector",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "In-page devtools-style logger panel for web apps (console + network).",
5
5
  "repository": {
6
6
  "type": "git",