@mugenlabs/logtape-devtools 0.4.0 → 0.4.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 +5 -3
- package/dist/chunk-HWGRZ7VO.mjs +2 -0
- package/dist/index.mjs +5 -1086
- package/dist/sink.d.ts +10 -0
- package/dist/sink.mjs +1 -13
- package/package.json +5 -5
- package/dist/chunk-FDWUISCJ.mjs +0 -300
- package/dist/chunk-FDWUISCJ.mjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/sink.mjs.map +0 -1
package/dist/sink.d.ts
CHANGED
|
@@ -10,12 +10,22 @@ interface DevtoolsLogRecord {
|
|
|
10
10
|
caller?: string;
|
|
11
11
|
/** Logger category path, e.g. `["app", "auth"]`. */
|
|
12
12
|
category: string[];
|
|
13
|
+
/**
|
|
14
|
+
* Precomputed `category.join(".")` for filter/search hot paths.
|
|
15
|
+
* Optional so hand-built fixtures and older snapshots keep working.
|
|
16
|
+
*/
|
|
17
|
+
categoryKey?: string;
|
|
13
18
|
/** Unique identifier, stable for the lifetime of the record. */
|
|
14
19
|
id: string;
|
|
15
20
|
/** Severity of the record. */
|
|
16
21
|
level: LogLevel;
|
|
17
22
|
/** Raw message parts, alternating literals and interpolated values. */
|
|
18
23
|
message: unknown[];
|
|
24
|
+
/**
|
|
25
|
+
* Cached lowercase {@link messageText} for search.
|
|
26
|
+
* Optional; when omitted the panel falls back to `messageText.toLowerCase()`.
|
|
27
|
+
*/
|
|
28
|
+
messageSearchText?: string;
|
|
19
29
|
/** Message parts rendered into a single searchable string. */
|
|
20
30
|
messageText: string;
|
|
21
31
|
/** Structured properties attached to the log call, deep-cloned for safety. */
|
package/dist/sink.mjs
CHANGED
|
@@ -1,13 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LOG_LEVELS,
|
|
3
|
-
createDevtoolsSink,
|
|
4
|
-
createLogStore,
|
|
5
|
-
defaultLogStore
|
|
6
|
-
} from "./chunk-FDWUISCJ.mjs";
|
|
7
|
-
export {
|
|
8
|
-
LOG_LEVELS,
|
|
9
|
-
createDevtoolsSink,
|
|
10
|
-
createLogStore,
|
|
11
|
-
defaultLogStore
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=sink.mjs.map
|
|
1
|
+
import{a as o,b as e,d as t,e as r}from"./chunk-HWGRZ7VO.mjs";export{t as LOG_LEVELS,r as createDevtoolsSink,o as createLogStore,e as defaultLogStore};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mugenlabs/logtape-devtools",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "A TanStack DevTools plugin for inspecting LogTape logs in the browser",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"logtape",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@base-ui-components/react": "1.0.0-rc.0",
|
|
49
|
-
"@tanstack/pacer": "^0.
|
|
49
|
+
"@tanstack/pacer": "^0.22.0",
|
|
50
50
|
"@tanstack/react-virtual": "^3.14.10"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
"storybook": "^10.6.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@logtape/logtape": "
|
|
63
|
-
"@tanstack/react-devtools": "
|
|
62
|
+
"@logtape/logtape": "^2.0.0",
|
|
63
|
+
"@tanstack/react-devtools": "^0.9.0",
|
|
64
64
|
"react": "^18.0.0 || ^19.0.0",
|
|
65
65
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
66
66
|
},
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
"engines": {
|
|
76
|
-
"node": ">=
|
|
76
|
+
"node": ">=24.0.0"
|
|
77
77
|
},
|
|
78
78
|
"publishConfig": {
|
|
79
79
|
"access": "public",
|
package/dist/chunk-FDWUISCJ.mjs
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
// src/store.ts
|
|
2
|
-
var DEFAULT_MAX_RECORDS = 1e3;
|
|
3
|
-
function normalizeMaxRecords(value) {
|
|
4
|
-
const numeric = Math.floor(Number(value ?? DEFAULT_MAX_RECORDS));
|
|
5
|
-
return Number.isFinite(numeric) && numeric > 0 ? numeric : 0;
|
|
6
|
-
}
|
|
7
|
-
function schedule(callback) {
|
|
8
|
-
if (typeof queueMicrotask === "function") {
|
|
9
|
-
queueMicrotask(callback);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
Promise.resolve().then(callback);
|
|
13
|
-
}
|
|
14
|
-
function createLogStore(options) {
|
|
15
|
-
let max = normalizeMaxRecords(options?.maxRecords);
|
|
16
|
-
let buffer = [];
|
|
17
|
-
let start = 0;
|
|
18
|
-
let size = 0;
|
|
19
|
-
let snapshot = [];
|
|
20
|
-
let snapshotDirty = false;
|
|
21
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
22
|
-
let notifyScheduled = false;
|
|
23
|
-
function emit() {
|
|
24
|
-
for (const listener of listeners) {
|
|
25
|
-
listener();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function notify() {
|
|
29
|
-
if (notifyScheduled) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
notifyScheduled = true;
|
|
33
|
-
schedule(() => {
|
|
34
|
-
notifyScheduled = false;
|
|
35
|
-
emit();
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
function toArray() {
|
|
39
|
-
const result = new Array(size);
|
|
40
|
-
for (let i = 0; i < size; i += 1) {
|
|
41
|
-
result[i] = buffer[(start + i) % buffer.length];
|
|
42
|
-
}
|
|
43
|
-
return result;
|
|
44
|
-
}
|
|
45
|
-
function normalize() {
|
|
46
|
-
const trimmed = size > max;
|
|
47
|
-
const kept = trimmed ? toArray().slice(size - max) : toArray();
|
|
48
|
-
buffer = kept;
|
|
49
|
-
start = 0;
|
|
50
|
-
size = kept.length;
|
|
51
|
-
return trimmed;
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
addRecord: (record) => {
|
|
55
|
-
if (max === 0) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (size < max) {
|
|
59
|
-
buffer.push(record);
|
|
60
|
-
size += 1;
|
|
61
|
-
} else {
|
|
62
|
-
buffer[start] = record;
|
|
63
|
-
start = (start + 1) % buffer.length;
|
|
64
|
-
}
|
|
65
|
-
snapshotDirty = true;
|
|
66
|
-
notify();
|
|
67
|
-
},
|
|
68
|
-
clear: () => {
|
|
69
|
-
buffer = [];
|
|
70
|
-
start = 0;
|
|
71
|
-
size = 0;
|
|
72
|
-
snapshotDirty = true;
|
|
73
|
-
notify();
|
|
74
|
-
},
|
|
75
|
-
getMaxRecords: () => max,
|
|
76
|
-
getSnapshot: () => {
|
|
77
|
-
if (snapshotDirty) {
|
|
78
|
-
snapshot = toArray();
|
|
79
|
-
snapshotDirty = false;
|
|
80
|
-
}
|
|
81
|
-
return snapshot;
|
|
82
|
-
},
|
|
83
|
-
hasListeners: () => listeners.size > 0,
|
|
84
|
-
setMaxSize: (nextMax) => {
|
|
85
|
-
max = normalizeMaxRecords(nextMax);
|
|
86
|
-
if (normalize()) {
|
|
87
|
-
snapshotDirty = true;
|
|
88
|
-
notify();
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
subscribe: (listener) => {
|
|
92
|
-
listeners.add(listener);
|
|
93
|
-
return () => {
|
|
94
|
-
listeners.delete(listener);
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
var defaultLogStore = createLogStore();
|
|
100
|
-
|
|
101
|
-
// src/types.ts
|
|
102
|
-
var LOG_LEVELS = ["trace", "debug", "info", "warning", "error", "fatal"];
|
|
103
|
-
|
|
104
|
-
// src/safe-json.ts
|
|
105
|
-
function toSerializable(value) {
|
|
106
|
-
switch (typeof value) {
|
|
107
|
-
case "bigint":
|
|
108
|
-
return `${value}n`;
|
|
109
|
-
case "symbol":
|
|
110
|
-
return value.toString();
|
|
111
|
-
case "function":
|
|
112
|
-
return `[Function ${value.name || "anonymous"}]`;
|
|
113
|
-
default:
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
if (value instanceof Error) {
|
|
117
|
-
const error = { message: value.message, name: value.name };
|
|
118
|
-
if (value.stack) {
|
|
119
|
-
error.stack = value.stack;
|
|
120
|
-
}
|
|
121
|
-
if ("cause" in value && value.cause !== void 0) {
|
|
122
|
-
error.cause = value.cause;
|
|
123
|
-
}
|
|
124
|
-
return error;
|
|
125
|
-
}
|
|
126
|
-
if (value instanceof Map) {
|
|
127
|
-
return [...value.entries()];
|
|
128
|
-
}
|
|
129
|
-
if (value instanceof Set) {
|
|
130
|
-
return [...value];
|
|
131
|
-
}
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
function createReplacer() {
|
|
135
|
-
const ancestors = [];
|
|
136
|
-
return function replace(_key, value) {
|
|
137
|
-
while (ancestors.length > 0 && ancestors.at(-1)?.serialized !== this) {
|
|
138
|
-
ancestors.pop();
|
|
139
|
-
}
|
|
140
|
-
if (typeof value === "object" && value !== null) {
|
|
141
|
-
if (ancestors.some((ancestor) => ancestor.original === value)) {
|
|
142
|
-
return "[Circular]";
|
|
143
|
-
}
|
|
144
|
-
const serialized = toSerializable(value);
|
|
145
|
-
if (typeof serialized === "object" && serialized !== null) {
|
|
146
|
-
ancestors.push({ original: value, serialized });
|
|
147
|
-
}
|
|
148
|
-
return serialized;
|
|
149
|
-
}
|
|
150
|
-
return toSerializable(value);
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
function safeStringify(value, indent) {
|
|
154
|
-
try {
|
|
155
|
-
const json = JSON.stringify(value, createReplacer(), indent);
|
|
156
|
-
if (json !== void 0) {
|
|
157
|
-
return json;
|
|
158
|
-
}
|
|
159
|
-
} catch {
|
|
160
|
-
}
|
|
161
|
-
return String(value);
|
|
162
|
-
}
|
|
163
|
-
function jsonClone(value) {
|
|
164
|
-
return JSON.parse(safeStringify(value));
|
|
165
|
-
}
|
|
166
|
-
function safeCloneProperties(properties) {
|
|
167
|
-
if (typeof structuredClone === "function") {
|
|
168
|
-
try {
|
|
169
|
-
return structuredClone(properties);
|
|
170
|
-
} catch {
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
try {
|
|
174
|
-
return jsonClone(properties);
|
|
175
|
-
} catch {
|
|
176
|
-
}
|
|
177
|
-
const result = {};
|
|
178
|
-
for (const [key, entry] of Object.entries(properties)) {
|
|
179
|
-
try {
|
|
180
|
-
result[key] = jsonClone(entry);
|
|
181
|
-
} catch {
|
|
182
|
-
result[key] = String(entry);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return result;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// src/sink.ts
|
|
189
|
-
function renderMessage(message) {
|
|
190
|
-
return message.map((part) => {
|
|
191
|
-
if (typeof part === "string") {
|
|
192
|
-
return part;
|
|
193
|
-
}
|
|
194
|
-
if (part === null || part === void 0) {
|
|
195
|
-
return String(part);
|
|
196
|
-
}
|
|
197
|
-
return safeStringify(part);
|
|
198
|
-
}).join("");
|
|
199
|
-
}
|
|
200
|
-
var v8FrameRe = /at\s+(?:.*?\s+\()?(.+?):(\d+):(\d+)\)?$/;
|
|
201
|
-
var smFrameRe = /(?:^|@)(.+?):(\d+):(\d+)$/;
|
|
202
|
-
function captureCallerInfo() {
|
|
203
|
-
const { stack } = new Error();
|
|
204
|
-
if (!stack) {
|
|
205
|
-
return void 0;
|
|
206
|
-
}
|
|
207
|
-
const lines = stack.split("\n");
|
|
208
|
-
for (const line of lines) {
|
|
209
|
-
const trimmed = line.trim();
|
|
210
|
-
if (!trimmed || trimmed === "Error") {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (/logtape/i.test(trimmed)) {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
const match = v8FrameRe.exec(trimmed) ?? smFrameRe.exec(trimmed);
|
|
217
|
-
if (!match) {
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
const [, filePath, lineNo, colNo] = match;
|
|
221
|
-
const fileName = filePath.replace(/[?#].*$/, "").split("/").pop() ?? filePath;
|
|
222
|
-
return `${fileName}:${lineNo}:${colNo}`;
|
|
223
|
-
}
|
|
224
|
-
return void 0;
|
|
225
|
-
}
|
|
226
|
-
function normalizeRecord(record, captureStack, id) {
|
|
227
|
-
const message = [...record.message];
|
|
228
|
-
const normalized = {
|
|
229
|
-
category: [...record.category],
|
|
230
|
-
id,
|
|
231
|
-
level: record.level,
|
|
232
|
-
message,
|
|
233
|
-
properties: safeCloneProperties(record.properties),
|
|
234
|
-
timestamp: record.timestamp
|
|
235
|
-
};
|
|
236
|
-
let messageText;
|
|
237
|
-
Object.defineProperty(normalized, "messageText", {
|
|
238
|
-
configurable: true,
|
|
239
|
-
enumerable: true,
|
|
240
|
-
get: () => {
|
|
241
|
-
messageText ??= renderMessage(message);
|
|
242
|
-
return messageText;
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
if (captureStack) {
|
|
246
|
-
const caller = captureCallerInfo();
|
|
247
|
-
if (caller) {
|
|
248
|
-
normalized.caller = caller;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return normalized;
|
|
252
|
-
}
|
|
253
|
-
function isProductionEnv() {
|
|
254
|
-
try {
|
|
255
|
-
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
} catch {
|
|
259
|
-
}
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
function createDevtoolsSink(options) {
|
|
263
|
-
const store = options?.store ?? defaultLogStore;
|
|
264
|
-
const wantsStack = options?.captureStackTrace ?? true;
|
|
265
|
-
const forceStack = options?.forceStackTrace ?? false;
|
|
266
|
-
const captureStack = wantsStack && (forceStack || !isProductionEnv());
|
|
267
|
-
const sinkId = Math.random().toString(36).slice(2, 8);
|
|
268
|
-
let counter = 0;
|
|
269
|
-
let disposed = false;
|
|
270
|
-
const sink = (record) => {
|
|
271
|
-
if (disposed || store.getMaxRecords?.() === 0) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
try {
|
|
275
|
-
const shouldCaptureStack = captureStack && (forceStack || store.hasListeners());
|
|
276
|
-
counter += 1;
|
|
277
|
-
const normalized = normalizeRecord(
|
|
278
|
-
record,
|
|
279
|
-
shouldCaptureStack,
|
|
280
|
-
`log-${sinkId}-${counter}-${record.timestamp}`
|
|
281
|
-
);
|
|
282
|
-
store.addRecord(normalized);
|
|
283
|
-
} catch {
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
return Object.assign(sink, {
|
|
287
|
-
[Symbol.dispose]: () => {
|
|
288
|
-
disposed = true;
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export {
|
|
294
|
-
createLogStore,
|
|
295
|
-
defaultLogStore,
|
|
296
|
-
safeStringify,
|
|
297
|
-
LOG_LEVELS,
|
|
298
|
-
createDevtoolsSink
|
|
299
|
-
};
|
|
300
|
-
//# sourceMappingURL=chunk-FDWUISCJ.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/types.ts","../src/safe-json.ts","../src/sink.ts"],"sourcesContent":["import type { DevtoolsLogRecord } from \"./types\";\n\ntype Listener = () => void;\n\n/**\n * Reactive log record container backing the devtools panel.\n *\n * Records are kept in a fixed-capacity ring buffer so appending is O(1); the\n * array returned by {@link LogStore.getSnapshot} is rebuilt lazily at most once\n * per mutation batch. Listener notifications are coalesced into a microtask, so\n * a synchronous burst of {@link LogStore.addRecord} calls results in a single\n * notification.\n */\nexport interface LogStore {\n /** Appends a record, evicting the oldest one when the max size is reached. */\n addRecord: (record: DevtoolsLogRecord) => void;\n /** Removes every record and notifies subscribers. */\n clear: () => void;\n /**\n * The current retention limit. `0` means the store discards everything, which\n * lets the sink skip normalisation entirely. Optional for custom stores.\n */\n getMaxRecords?: () => number;\n /**\n * Returns the current records, oldest first.\n *\n * The reference is stable until the store changes, which makes it safe to use\n * with `useSyncExternalStore`.\n */\n getSnapshot: () => DevtoolsLogRecord[];\n /**\n * Whether anything is currently subscribed to the store.\n *\n * Used by the sink to skip expensive work (such as stack trace capture) while\n * no devtools panel is mounted.\n */\n hasListeners: () => boolean;\n /** Updates the maximum number of retained records, trimming if needed. */\n setMaxSize: (size: number) => void;\n /** Registers a change listener. Returns an unsubscribe function. */\n subscribe: (listener: Listener) => () => void;\n}\n\n/** Options for {@link createLogStore}. */\nexport interface LogStoreOptions {\n /**\n * Maximum number of log records to retain. Older records are evicted first.\n *\n * @default 1000\n */\n maxRecords?: number;\n}\n\nconst DEFAULT_MAX_RECORDS = 1000;\n\n/**\n * Coerce a user-supplied capacity into a non-negative integer. Fractions would\n * defeat the `size < max` capacity check and NaN would wedge the ring buffer.\n */\nfunction normalizeMaxRecords(value: number | undefined): number {\n const numeric = Math.floor(Number(value ?? DEFAULT_MAX_RECORDS));\n return Number.isFinite(numeric) && numeric > 0 ? numeric : 0;\n}\n\nfunction schedule(callback: () => void): void {\n if (typeof queueMicrotask === \"function\") {\n queueMicrotask(callback);\n return;\n }\n Promise.resolve().then(callback);\n}\n\n/**\n * Creates an isolated log store.\n *\n * Pass the same instance to `createDevtoolsSink` and\n * `createLogTapeDevtoolsPlugin` to keep the sink and the panel in sync.\n *\n * ```ts\n * const store = createLogStore({ maxRecords: 5000 });\n * ```\n */\nexport function createLogStore(options?: LogStoreOptions): LogStore {\n let max = normalizeMaxRecords(options?.maxRecords);\n\n // Ring buffer: `start` is the index of the oldest record, `size` the number\n // of live entries. Appending is O(1) — no per-record array copy.\n let buffer: DevtoolsLogRecord[] = [];\n let start = 0;\n let size = 0;\n\n let snapshot: DevtoolsLogRecord[] = [];\n let snapshotDirty = false;\n\n const listeners = new Set<Listener>();\n let notifyScheduled = false;\n\n function emit() {\n for (const listener of listeners) {\n listener();\n }\n }\n\n function notify() {\n if (notifyScheduled) {\n return;\n }\n notifyScheduled = true;\n schedule(() => {\n notifyScheduled = false;\n emit();\n });\n }\n\n /** Materializes the ring buffer into a plain, oldest-first array. */\n function toArray(): DevtoolsLogRecord[] {\n const result: DevtoolsLogRecord[] = new Array(size);\n for (let i = 0; i < size; i += 1) {\n result[i] = buffer[(start + i) % buffer.length];\n }\n return result;\n }\n\n /**\n * Rewrites the buffer in logical order, dropping records beyond `max`.\n * Keeps the invariant that a below-capacity buffer is linear (`start === 0`).\n */\n function normalize(): boolean {\n const trimmed = size > max;\n const kept = trimmed ? toArray().slice(size - max) : toArray();\n buffer = kept;\n start = 0;\n size = kept.length;\n return trimmed;\n }\n\n return {\n addRecord: (record) => {\n if (max === 0) {\n return;\n }\n if (size < max) {\n // Below capacity — the buffer is linear, so a plain append is enough.\n buffer.push(record);\n size += 1;\n } else {\n // At capacity — overwrite the oldest slot.\n buffer[start] = record;\n start = (start + 1) % buffer.length;\n }\n snapshotDirty = true;\n notify();\n },\n clear: () => {\n buffer = [];\n start = 0;\n size = 0;\n snapshotDirty = true;\n notify();\n },\n getMaxRecords: () => max,\n getSnapshot: () => {\n if (snapshotDirty) {\n snapshot = toArray();\n snapshotDirty = false;\n }\n return snapshot;\n },\n hasListeners: () => listeners.size > 0,\n setMaxSize: (nextMax) => {\n max = normalizeMaxRecords(nextMax);\n if (normalize()) {\n snapshotDirty = true;\n notify();\n }\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\n/** Default shared store instance used when no custom store is provided */\nexport const defaultLogStore = createLogStore();\n","/** Severity levels supported by LogTape, ordered from least to most severe. */\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warning\" | \"error\" | \"fatal\";\n\n/** Every {@link LogLevel}, ordered from least to most severe. */\nexport const LOG_LEVELS: LogLevel[] = [\"trace\", \"debug\", \"info\", \"warning\", \"error\", \"fatal\"];\n\n/** A LogTape record normalized for display in the devtools panel. */\nexport interface DevtoolsLogRecord {\n /** Source location of the log call (only present when `captureStackTrace` is enabled). */\n caller?: string;\n /** Logger category path, e.g. `[\"app\", \"auth\"]`. */\n category: string[];\n /** Unique identifier, stable for the lifetime of the record. */\n id: string;\n /** Severity of the record. */\n level: LogLevel;\n /** Raw message parts, alternating literals and interpolated values. */\n message: unknown[];\n /** Message parts rendered into a single searchable string. */\n messageText: string;\n /** Structured properties attached to the log call, deep-cloned for safety. */\n properties: Record<string, unknown>;\n /** Creation time in milliseconds since the Unix epoch. */\n timestamp: number;\n}\n","/**\n * JSON helpers shared by the sink and the panel.\n *\n * This module is intentionally React-free so that it can be pulled into the\n * `@mugenlabs/logtape-devtools/sink` entry point.\n */\n\n/**\n * Map values JSON cannot represent (or would silently flatten to `{}`) onto a\n * readable equivalent. Anything else is returned untouched.\n */\nfunction toSerializable(value: unknown): unknown {\n switch (typeof value) {\n case \"bigint\":\n return `${value}n`;\n case \"symbol\":\n return value.toString();\n case \"function\":\n return `[Function ${value.name || \"anonymous\"}]`;\n default:\n break;\n }\n if (value instanceof Error) {\n const error: Record<string, unknown> = { message: value.message, name: value.name };\n if (value.stack) {\n error.stack = value.stack;\n }\n if (\"cause\" in value && value.cause !== undefined) {\n error.cause = value.cause;\n }\n return error;\n }\n if (value instanceof Map) {\n return [...value.entries()];\n }\n if (value instanceof Set) {\n return [...value];\n }\n return value;\n}\n\ninterface Ancestor {\n /** The value as it appeared in the source graph — used for cycle detection. */\n original: object;\n /** What JSON.stringify actually recursed into — used to unwind the stack. */\n serialized: object;\n}\n\n/**\n * Replacer that marks true cycles as `\"[Circular]\"` while leaving shared\n * (non-circular) references intact, and converts unrepresentable values via\n * {@link toSerializable}.\n */\nfunction createReplacer(): (this: unknown, key: string, value: unknown) => unknown {\n const ancestors: Ancestor[] = [];\n return function replace(this: unknown, _key, value) {\n // `this` is the object holding `key`; unwind to the current branch.\n while (ancestors.length > 0 && ancestors.at(-1)?.serialized !== this) {\n ancestors.pop();\n }\n if (typeof value === \"object\" && value !== null) {\n if (ancestors.some((ancestor) => ancestor.original === value)) {\n return \"[Circular]\";\n }\n const serialized = toSerializable(value);\n if (typeof serialized === \"object\" && serialized !== null) {\n ancestors.push({ original: value, serialized });\n }\n return serialized;\n }\n return toSerializable(value);\n };\n}\n\n/**\n * Serializes any value to JSON, never throwing.\n *\n * Errors, Maps, Sets, BigInts, symbols and functions are rendered in a readable\n * form instead of being dropped, circular references become `\"[Circular]\"`,\n * and anything still unserializable falls back to `String(value)`.\n */\nexport function safeStringify(value: unknown, indent?: number): string {\n try {\n const json = JSON.stringify(value, createReplacer(), indent);\n if (json !== undefined) {\n return json;\n }\n } catch {\n // A throwing `toJSON` or getter — fall through.\n }\n return String(value);\n}\n\nfunction jsonClone(value: unknown): unknown {\n return JSON.parse(safeStringify(value));\n}\n\n/**\n * Deep-clones a properties bag, never throwing.\n *\n * Uses `structuredClone` when available and degrades per entry (via a JSON\n * round trip, then `String(value)`) so that a single unclonable value cannot\n * drop the whole object.\n */\nexport function safeCloneProperties(properties: Record<string, unknown>): Record<string, unknown> {\n if (typeof structuredClone === \"function\") {\n try {\n return structuredClone(properties);\n } catch {\n // Functions, DOM nodes, proxies… fall through to the JSON path.\n }\n }\n\n try {\n return jsonClone(properties) as Record<string, unknown>;\n } catch {\n // Fall through to the per-entry path.\n }\n\n const result: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(properties)) {\n try {\n result[key] = jsonClone(entry);\n } catch {\n result[key] = String(entry);\n }\n }\n return result;\n}\n","import type { LogRecord, Sink } from \"@logtape/logtape\";\nimport { safeCloneProperties, safeStringify } from \"./safe-json\";\nimport { defaultLogStore, type LogStore } from \"./store\";\nimport type { DevtoolsLogRecord, LogLevel } from \"./types\";\n\nfunction renderMessage(message: readonly unknown[]): string {\n return message\n .map((part) => {\n if (typeof part === \"string\") {\n return part;\n }\n if (part === null || part === undefined) {\n return String(part);\n }\n return safeStringify(part);\n })\n .join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// Stack trace parser — extracts the caller frame from `new Error().stack`\n// ---------------------------------------------------------------------------\n\n// V8 (Chrome/Node/Edge): \" at functionName (file:line:col)\"\nconst v8FrameRe = /at\\s+(?:.*?\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/;\n// SpiderMonkey (Firefox) / JSC (Safari): \"functionName@file:line:col\"\nconst smFrameRe = /(?:^|@)(.+?):(\\d+):(\\d+)$/;\n\nfunction captureCallerInfo(): string | undefined {\n // biome-ignore lint/suspicious/useErrorMessage: we only need the stack trace, not the message\n const { stack } = new Error();\n if (!stack) {\n return undefined;\n }\n\n const lines = stack.split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and the \"Error\" header\n if (!trimmed || trimmed === \"Error\") {\n continue;\n }\n\n // Skip frames from logtape internals (both @logtape/logtape and @mugenlabs/logtape-devtools)\n if (/logtape/i.test(trimmed)) {\n continue;\n }\n\n // Try V8 format first, then SpiderMonkey/JSC\n const match = v8FrameRe.exec(trimmed) ?? smFrameRe.exec(trimmed);\n if (!match) {\n continue;\n }\n\n const [, filePath, lineNo, colNo] = match;\n\n // Extract just the filename from the full path/URL\n const fileName =\n filePath\n .replace(/[?#].*$/, \"\")\n .split(\"/\")\n .pop() ?? filePath;\n\n return `${fileName}:${lineNo}:${colNo}`;\n }\n\n return undefined;\n}\n\nfunction normalizeRecord(record: LogRecord, captureStack: boolean, id: string): DevtoolsLogRecord {\n const message = [...record.message];\n const normalized = {\n category: [...record.category],\n id,\n level: record.level as LogLevel,\n message,\n properties: safeCloneProperties(record.properties),\n timestamp: record.timestamp,\n } as DevtoolsLogRecord;\n\n // Rendering the message serialises every interpolated value, so defer it to\n // the first read (search or display) instead of paying for it on every log.\n let messageText: string | undefined;\n Object.defineProperty(normalized, \"messageText\", {\n configurable: true,\n enumerable: true,\n get: () => {\n messageText ??= renderMessage(message);\n return messageText;\n },\n });\n\n if (captureStack) {\n const caller = captureCallerInfo();\n if (caller) {\n normalized.caller = caller;\n }\n }\n\n return normalized;\n}\n\nfunction isProductionEnv(): boolean {\n try {\n // Bundlers (Vite, webpack, etc.) statically replace this at build time\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\") {\n return true;\n }\n } catch {\n // process may not exist in browser environments\n }\n return false;\n}\n\n/** Options for {@link createDevtoolsSink}. */\nexport interface DevtoolsSinkOptions {\n /**\n * Capture the source location of each log call via stack trace parsing.\n *\n * Relies on `new Error().stack`, which is engine-dependent. Automatically\n * disabled in production builds (when `process.env.NODE_ENV === \"production\"`)\n * because minified bundles produce meaningless file/line references. Note that\n * source maps do not help here — browsers do not apply source maps to\n * `Error.stack`.\n *\n * Also skipped while no devtools panel is mounted (the store has no\n * subscribers), unless `forceStackTrace` is `true`.\n *\n * @experimental\n * @default true\n */\n captureStackTrace?: boolean;\n /**\n * Force stack trace capture even in production builds and while the panel is\n * closed. Only has an effect when `captureStackTrace` is `true`.\n *\n * @default false\n */\n forceStackTrace?: boolean;\n /** Custom log store instance. Defaults to the shared store. */\n store?: LogStore;\n}\n\n/**\n * A LogTape sink that is also disposable. LogTape disposes sinks when the\n * configuration is reset; a disposed sink stops writing to its store.\n */\nexport type DevtoolsSink = Sink & Disposable;\n\n/**\n * Creates a LogTape sink that forwards log records to the devtools panel.\n *\n * Usage:\n * ```ts\n * import { configure } from \"@logtape/logtape\";\n * import { createDevtoolsSink } from \"@mugenlabs/logtape-devtools\";\n *\n * await configure({\n * sinks: { devtools: createDevtoolsSink() },\n * loggers: [\n * { category: [\"app\"], lowestLevel: \"debug\", sinks: [\"devtools\"] },\n * ],\n * });\n * ```\n */\nexport function createDevtoolsSink(options?: DevtoolsSinkOptions): DevtoolsSink {\n const store = options?.store ?? defaultLogStore;\n const wantsStack = options?.captureStackTrace ?? true;\n const forceStack = options?.forceStackTrace ?? false;\n const captureStack = wantsStack && (forceStack || !isProductionEnv());\n\n // Per-sink counter, plus a random discriminator so that two sinks sharing a\n // single store cannot emit the same id for records with equal timestamps.\n const sinkId = Math.random().toString(36).slice(2, 8);\n let counter = 0;\n let disposed = false;\n\n const sink = (record: LogRecord): void => {\n // A store with no capacity drops every record, so skip normalisation too.\n if (disposed || store.getMaxRecords?.() === 0) {\n return;\n }\n try {\n // Stack capture is the most expensive part of normalization — skip it\n // while nothing is watching the store (i.e. no panel is mounted).\n const shouldCaptureStack = captureStack && (forceStack || store.hasListeners());\n counter += 1;\n const normalized = normalizeRecord(\n record,\n shouldCaptureStack,\n `log-${sinkId}-${counter}-${record.timestamp}`\n );\n store.addRecord(normalized);\n } catch {\n // Fail silently — devtools should never break the application\n }\n };\n\n return Object.assign(sink, {\n [Symbol.dispose]: () => {\n disposed = true;\n },\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/plugin/logtape-devtools-plugin.tsx","../src/plugin/components/log-list.tsx","../src/plugin/theme.ts","../src/plugin/components/log-row.tsx","../src/plugin/format.ts","../src/plugin/icons.tsx","../src/plugin/components/level-badge.tsx","../src/plugin/components/log-detail.tsx","../src/plugin/components/toolbar.tsx","../src/plugin/create-logtape-devtools-plugin.tsx","../src/create-logtape-devtools.ts"],"sourcesContent":["import { useCallback, useMemo, useState, useSyncExternalStore } from \"react\";\nimport type { LogStore } from \"../store\";\nimport type { DevtoolsLogRecord, LogLevel } from \"../types\";\nimport { LogList } from \"./components/log-list\";\nimport { Toolbar } from \"./components/toolbar\";\nimport { PanelLeftCloseIcon, PanelLeftOpenIcon, PauseIcon, PlayIcon, Trash2Icon } from \"./icons\";\nimport { theme } from \"./theme\";\n\ninterface Props {\n store: LogStore;\n}\n\nconst rootStyle = {\n background: theme.colors.background,\n color: theme.colors.textPrimary,\n display: \"flex\",\n flexDirection: \"row\",\n fontFamily: theme.fontFamily.sans,\n height: \"100%\",\n overflow: \"hidden\",\n} as const;\n\nconst sidebarStyle = {\n flexShrink: 0,\n overflow: \"hidden\",\n transition: \"width 0.2s ease, opacity 0.15s ease\",\n} as const;\n\nconst mainStyle = {\n display: \"flex\",\n flex: 1,\n flexDirection: \"column\",\n overflow: \"hidden\",\n} as const;\n\nconst topBarStyle = {\n alignItems: \"center\",\n borderBottom: `1px solid ${theme.colors.border}`,\n display: \"flex\",\n flexShrink: 0,\n gap: theme.spacing.lg,\n padding: `${theme.spacing.sm} ${theme.spacing.lg}`,\n} as const;\n\nconst sidebarToggleStyle = {\n background: \"none\",\n border: \"none\",\n color: theme.colors.textMuted,\n cursor: \"pointer\",\n fontFamily: theme.fontFamily.sans,\n fontSize: theme.fontSize.md,\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n transition: \"color 0.1s\",\n} as const;\n\nconst iconLabelStyle = { alignItems: \"center\", display: \"inline-flex\", gap: \"6px\" } as const;\n\nconst countStyle = {\n color: theme.colors.textMuted,\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.sm,\n} as const;\n\nconst compactButtonStyle = {\n background: theme.colors.surfaceHover,\n border: `1px solid ${theme.colors.border}`,\n borderRadius: theme.radius.sm,\n color: theme.colors.textPrimary,\n cursor: \"pointer\",\n fontFamily: theme.fontFamily.sans,\n fontSize: theme.fontSize.sm,\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n transition: \"filter 0.1s\",\n} as const;\n\nexport const LogTapeDevtoolsPlugin = ({ store }: Props) => {\n const allRecords = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\n const [paused, setPaused] = useState(false);\n const [pausedRecords, setPausedRecords] = useState<DevtoolsLogRecord[]>([]);\n const [levelFilter, setLevelFilter] = useState<Set<LogLevel>>(new Set());\n const [categoryFilter, setCategoryFilter] = useState<string[]>([]);\n const [searchText, setSearchText] = useState(\"\");\n const [expandedId, setExpandedId] = useState<string | null>(null);\n const [sidebarOpen, setSidebarOpen] = useState(true);\n\n const handlePause = useCallback(() => {\n setPaused(true);\n setPausedRecords(store.getSnapshot());\n }, [store]);\n\n const handleResume = useCallback(() => {\n setPaused(false);\n setPausedRecords([]);\n }, []);\n\n const handleClear = useCallback(() => {\n store.clear();\n setPausedRecords([]);\n setExpandedId(null);\n }, [store]);\n\n const records = paused ? pausedRecords : allRecords;\n\n const uniqueCategories = useMemo(() => {\n const seen = new Set<string>();\n for (const r of records) {\n seen.add(r.category.join(\".\"));\n }\n return Array.from(seen).sort();\n }, [records]);\n\n const filteredRecords = useMemo(\n () =>\n records.filter((r) => {\n if (levelFilter.size > 0 && !levelFilter.has(r.level)) {\n return false;\n }\n if (categoryFilter.length > 0 && !categoryFilter.includes(r.category.join(\".\"))) {\n return false;\n }\n if (searchText) {\n const term = searchText.toLowerCase();\n if (\n !(\n r.messageText.toLowerCase().includes(term) ||\n r.category.join(\".\").toLowerCase().includes(term)\n )\n ) {\n return false;\n }\n }\n return true;\n }),\n [records, levelFilter, categoryFilter, searchText]\n );\n\n return (\n <div style={rootStyle}>\n <div\n style={{\n ...sidebarStyle,\n borderRight: sidebarOpen ? `1px solid ${theme.colors.border}` : \"none\",\n opacity: sidebarOpen ? 1 : 0,\n width: sidebarOpen ? \"200px\" : \"0px\",\n }}\n >\n <Toolbar\n categories={uniqueCategories}\n categoryFilter={categoryFilter}\n filteredCount={filteredRecords.length}\n levelFilter={levelFilter}\n onCategoryFilterChange={setCategoryFilter}\n onClear={handleClear}\n onLevelFilterChange={setLevelFilter}\n onPause={handlePause}\n onResume={handleResume}\n onSearchTextChange={setSearchText}\n paused={paused}\n totalCount={records.length}\n />\n </div>\n\n <div style={mainStyle}>\n {/* Sidebar toggle bar */}\n <div style={topBarStyle}>\n <button\n aria-expanded={sidebarOpen}\n data-lt-clear-btn=\"\"\n onClick={() => setSidebarOpen((v) => !v)}\n style={sidebarToggleStyle}\n title={sidebarOpen ? \"Collapse sidebar\" : \"Expand sidebar\"}\n type=\"button\"\n >\n <span style={iconLabelStyle}>\n {sidebarOpen ? <PanelLeftCloseIcon size={14} /> : <PanelLeftOpenIcon size={14} />}\n {sidebarOpen ? \"Hide controls\" : \"Show controls\"}\n </span>\n </button>\n\n {!sidebarOpen && (\n <>\n <span style={countStyle}>\n {filteredRecords.length === records.length\n ? `${records.length} logs`\n : `${filteredRecords.length} / ${records.length} logs`}\n </span>\n <button\n data-lt-interactive=\"\"\n onClick={paused ? handleResume : handlePause}\n style={{\n ...compactButtonStyle,\n background: paused\n ? theme.colors.levels.warning.badge\n : compactButtonStyle.background,\n color: paused ? theme.colors.white : compactButtonStyle.color,\n }}\n title={paused ? \"Resume\" : \"Pause\"}\n type=\"button\"\n >\n {paused ? <PlayIcon size={12} /> : <PauseIcon size={12} />}\n </button>\n <button\n data-lt-interactive=\"\"\n onClick={handleClear}\n style={compactButtonStyle}\n title=\"Clear\"\n type=\"button\"\n >\n <Trash2Icon size={12} />\n </button>\n </>\n )}\n </div>\n <LogList\n autoScroll={!paused}\n expandedId={expandedId}\n onToggle={setExpandedId}\n records={filteredRecords}\n />\n </div>\n </div>\n );\n};\n","import { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport type { DevtoolsLogRecord } from \"../../types\";\nimport { theme } from \"../theme\";\nimport { LogRow } from \"./log-row\";\n\ninterface LogListProps {\n autoScroll: boolean;\n expandedId: string | null;\n onToggle: (id: string | null) => void;\n records: DevtoolsLogRecord[];\n}\n\n/** Collapsed row height — rows are measured after mount, this is only the seed. */\nconst ESTIMATED_ROW_HEIGHT = 28;\nconst OVERSCAN = 10;\n/** Distance from the bottom (px) still considered \"stuck to the bottom\". */\nconst STICK_THRESHOLD = 40;\n\nconst emptyStyle = {\n alignItems: \"center\",\n color: theme.colors.textMuted,\n display: \"flex\",\n flex: 1,\n fontSize: theme.fontSize.lg,\n justifyContent: \"center\",\n} as const;\n\nconst containerStyle = {\n flex: 1,\n overflow: \"auto\",\n scrollbarColor: `${theme.colors.scrollbar} transparent`,\n scrollbarWidth: \"thin\",\n} as const;\n\nexport const LogList = ({ records, expandedId, onToggle, autoScroll }: LogListProps) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const isAtBottomRef = useRef(true);\n\n const virtualizer = useVirtualizer({\n count: records.length,\n estimateSize: () => ESTIMATED_ROW_HEIGHT,\n // The virtualizer can ask for keys of indices from a previous, longer list\n // (after clear or a narrowing filter), so never assume the index exists.\n getItemKey: (index) => records[index]?.id ?? index,\n getScrollElement: () => containerRef.current,\n overscan: OVERSCAN,\n });\n\n const recordCount = records.length;\n // Key on the newest record rather than the count: once the ring buffer is\n // full the count stays constant while the contents keep changing.\n const lastRecordId = records[recordCount - 1]?.id;\n useEffect(() => {\n if (lastRecordId === undefined || !(autoScroll && isAtBottomRef.current)) {\n return;\n }\n virtualizer.scrollToIndex(recordCount - 1, { align: \"end\" });\n }, [autoScroll, lastRecordId, recordCount, virtualizer]);\n\n // The scroll container unmounts while the list is empty; treat the next\n // batch of logs as a fresh start instead of keeping a stale \"scrolled up\".\n useEffect(() => {\n if (recordCount === 0) {\n isAtBottomRef.current = true;\n }\n }, [recordCount]);\n\n const handleScroll = useCallback(() => {\n const el = containerRef.current;\n if (!el) {\n return;\n }\n isAtBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < STICK_THRESHOLD;\n }, []);\n\n if (records.length === 0) {\n return (\n <div data-testid=\"log-list-empty\" style={emptyStyle}>\n No logs yet\n </div>\n );\n }\n\n const virtualItems = virtualizer.getVirtualItems();\n\n return (\n <div data-testid=\"log-list\" onScroll={handleScroll} ref={containerRef} style={containerStyle}>\n {/* Spacer sized to the full list, with only the visible window rendered */}\n <div\n style={{\n height: `${virtualizer.getTotalSize()}px`,\n position: \"relative\",\n width: \"100%\",\n }}\n >\n <div\n style={{\n left: 0,\n position: \"absolute\",\n top: 0,\n transform: `translateY(${virtualItems[0]?.start ?? 0}px)`,\n width: \"100%\",\n }}\n >\n {virtualItems.map((virtualItem) => {\n const record = records[virtualItem.index];\n return (\n <div\n data-index={virtualItem.index}\n key={virtualItem.key}\n ref={virtualizer.measureElement}\n >\n <LogRow expanded={expandedId === record.id} onToggle={onToggle} record={record} />\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n};\n","export const theme = {\n colors: {\n accent: \"#6366f1\",\n accentHover: \"#818cf8\",\n background: \"#1e1e1e\",\n border: \"#333\",\n borderFocus: \"#6366f1\",\n borderInput: \"#444\",\n\n levels: {\n // Badge colours keep at least 4.5:1 contrast against white badge text.\n debug: { badge: \"#2f7a2f\", bg: \"#1a2a1a\", color: \"#88bb88\" },\n error: { badge: \"#dc2626\", bg: \"#2a1a1a\", color: \"#ef4444\" },\n fatal: { badge: \"#b91c1c\", bg: \"#3a0a0a\", color: \"#ff6b6b\" },\n info: { badge: \"#2563eb\", bg: \"#1a2a3a\", color: \"#60a5fa\" },\n trace: { badge: \"#5555aa\", bg: \"#1a1a2e\", color: \"#8888bb\" },\n warning: { badge: \"#eab308\", bg: \"#2a2a1a\", color: \"#facc15\" },\n },\n\n scrollbar: \"#444\",\n scrollbarHover: \"#555\",\n surface: \"#1a1a1a\",\n surfaceHover: \"#2a2a2a\",\n surfaceSelected: \"#2a2a3a\",\n textDimmed: \"#555\",\n textMuted: \"#666\",\n\n textPrimary: \"#e0e0e0\",\n textSecondary: \"#888\",\n white: \"#fff\",\n },\n\n fontFamily: {\n mono: 'ui-monospace, \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, monospace',\n sans: 'system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif',\n },\n\n fontSize: {\n base: \"12px\",\n lg: \"13px\",\n md: \"11px\",\n sm: \"10px\",\n xs: \"9px\",\n },\n\n radius: {\n lg: \"6px\",\n md: \"4px\",\n pill: \"10px\",\n sm: \"3px\",\n },\n\n spacing: {\n lg: \"8px\",\n md: \"6px\",\n sm: \"4px\",\n xl: \"12px\",\n xs: \"2px\",\n xxl: \"16px\",\n },\n} as const;\n\nexport type LogLevelColors = (typeof theme.colors.levels)[keyof typeof theme.colors.levels];\n\n/**\n * Colors for a level, falling back to \"info\" for levels this version does not\n * know about (the LogTape peer range allows newer levels to appear).\n */\nexport const getLevelColors = (level: string): LogLevelColors =>\n (theme.colors.levels as Record<string, LogLevelColors | undefined>)[level] ??\n theme.colors.levels.info;\n","import { memo, useCallback } from \"react\";\nimport type { DevtoolsLogRecord } from \"../../types\";\nimport { formatTime } from \"../format\";\nimport { ChevronRightIcon } from \"../icons\";\nimport { getLevelColors, theme } from \"../theme\";\nimport { LevelBadge } from \"./level-badge\";\nimport { LogDetail } from \"./log-detail\";\n\ninterface LogRowProps {\n expanded: boolean;\n onToggle: (id: string | null) => void;\n record: DevtoolsLogRecord;\n}\n\nconst rowStyle = {\n alignItems: \"center\",\n border: \"none\",\n borderBottom: `1px solid ${theme.colors.border}`,\n cursor: \"pointer\",\n display: \"flex\",\n gap: theme.spacing.lg,\n padding: `${theme.spacing.md} ${theme.spacing.xl}`,\n textAlign: \"left\",\n transition: \"background 0.1s, filter 0.1s\",\n width: \"100%\",\n} as const;\n\nconst timestampStyle = {\n color: theme.colors.textDimmed,\n flexShrink: 0,\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.md,\n} as const;\n\nconst categoryStyle = {\n color: theme.colors.accent,\n flexShrink: 0,\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.md,\n maxWidth: \"180px\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n} as const;\n\nconst messageStyle = {\n color: theme.colors.textPrimary,\n flex: 1,\n fontSize: theme.fontSize.base,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n} as const;\n\nconst callerStyle = {\n color: theme.colors.textDimmed,\n flexShrink: 0,\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.sm,\n maxWidth: \"200px\",\n opacity: 0.7,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n} as const;\n\nconst chevronStyle = {\n color: theme.colors.textDimmed,\n display: \"flex\",\n flexShrink: 0,\n transition: \"transform 0.15s\",\n} as const;\n\nconst LogRowComponent = ({ record, expanded, onToggle }: LogRowProps) => {\n const levelColors = getLevelColors(record.level);\n\n const handleClick = useCallback(() => {\n onToggle(expanded ? null : record.id);\n }, [expanded, onToggle, record.id]);\n\n return (\n <div>\n <button\n aria-expanded={expanded}\n data-lt-interactive=\"\"\n data-testid=\"log-row\"\n onClick={handleClick}\n style={{ ...rowStyle, background: expanded ? levelColors.bg : \"transparent\" }}\n type=\"button\"\n >\n {/* Timestamp */}\n <span style={timestampStyle}>{formatTime(record.timestamp)}</span>\n\n {/* Level badge */}\n <LevelBadge level={record.level} />\n\n {/* Category */}\n <span style={categoryStyle}>{record.category.join(\".\")}</span>\n\n {/* Message */}\n <span style={messageStyle}>{record.messageText}</span>\n\n {/* Caller (right-aligned) */}\n {record.caller ? <span style={callerStyle}>{record.caller}</span> : null}\n\n {/* Expand indicator */}\n <span style={{ ...chevronStyle, transform: expanded ? \"rotate(90deg)\" : \"rotate(0deg)\" }}>\n <ChevronRightIcon size={12} />\n </span>\n </button>\n\n {expanded ? <LogDetail record={record} /> : null}\n </div>\n );\n};\n\n/** Memoised so a new batch of records only re-renders the rows that changed. */\nexport const LogRow = memo(LogRowComponent);\n","// Built once: constructing an Intl formatter per row per render is measurably slow.\nlet timeFormatter: Intl.DateTimeFormat | undefined;\n\n/** Formats a timestamp as a 24-hour `HH:MM:SS` local time string. */\nexport function formatTime(ts: number): string {\n timeFormatter ??= new Intl.DateTimeFormat(undefined, {\n hour: \"2-digit\",\n hour12: false,\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n return timeFormatter.format(new Date(ts));\n}\n","/**\n * Inline SVG icons based on Lucide (https://lucide.dev) — ISC License.\n * Using inline SVGs avoids adding lucide-react as a runtime dependency.\n */\n\ninterface IconProps {\n color?: string;\n size?: number;\n style?: React.CSSProperties;\n}\n\nconst defaults = { color: \"currentColor\", size: 14 } as const;\n\nconst Svg = ({\n size = defaults.size,\n color = defaults.color,\n style,\n children,\n}: IconProps & { children: React.ReactNode }) => (\n <svg\n aria-hidden=\"true\"\n fill=\"none\"\n height={size}\n stroke={color}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n style={style}\n viewBox=\"0 0 24 24\"\n width={size}\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n {children}\n </svg>\n);\n\nexport const PlayIcon = (props: IconProps) => (\n <Svg {...props}>\n <path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\" />\n </Svg>\n);\n\nexport const PauseIcon = (props: IconProps) => (\n <Svg {...props}>\n <rect height={18} rx={1} width={5} x={14} y={3} />\n <rect height={18} rx={1} width={5} x={5} y={3} />\n </Svg>\n);\n\nexport const XIcon = (props: IconProps) => (\n <Svg {...props}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </Svg>\n);\n\nexport const CheckIcon = (props: IconProps) => (\n <Svg {...props}>\n <path d=\"M20 6 9 17l-5-5\" />\n </Svg>\n);\n\nexport const ChevronRightIcon = (props: IconProps) => (\n <Svg {...props}>\n <path d=\"m9 18 6-6-6-6\" />\n </Svg>\n);\n\nexport const PanelLeftCloseIcon = (props: IconProps) => (\n <Svg {...props}>\n <rect height={18} rx={2} width={18} x={3} y={3} />\n <path d=\"M9 3v18\" />\n <path d=\"m16 15-3-3 3-3\" />\n </Svg>\n);\n\nexport const PanelLeftOpenIcon = (props: IconProps) => (\n <Svg {...props}>\n <rect height={18} rx={2} width={18} x={3} y={3} />\n <path d=\"M9 3v18\" />\n <path d=\"m14 9 3 3-3 3\" />\n </Svg>\n);\n\nexport const Trash2Icon = (props: IconProps) => (\n <Svg {...props}>\n <path d=\"M10 11v6\" />\n <path d=\"M14 11v6\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" />\n <path d=\"M3 6h18\" />\n <path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </Svg>\n);\n","import type { LogLevel } from \"../../types\";\nimport { getLevelColors, theme } from \"../theme\";\n\nconst LEVEL_LABELS: Record<LogLevel, string> = {\n debug: \"DBG\",\n error: \"ERR\",\n fatal: \"FTL\",\n info: \"INF\",\n trace: \"TRC\",\n warning: \"WRN\",\n};\n\nexport const LevelBadge = ({ level }: { level: LogLevel }) => {\n const colors = getLevelColors(level);\n const label = (LEVEL_LABELS as Record<string, string | undefined>)[level] ?? level.toUpperCase();\n\n return (\n <span\n style={{\n background: colors.badge,\n borderRadius: theme.radius.sm,\n color: theme.colors.white,\n display: \"inline-block\",\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.sm,\n fontWeight: 600,\n lineHeight: 1,\n minWidth: \"28px\",\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n textAlign: \"center\",\n textTransform: \"uppercase\",\n }}\n >\n {label}\n </span>\n );\n};\n","import { safeStringify } from \"../../safe-json\";\nimport type { DevtoolsLogRecord } from \"../../types\";\nimport { formatTime } from \"../format\";\nimport { theme } from \"../theme\";\n\nconst detailRowStyle = {\n display: \"flex\",\n gap: theme.spacing.lg,\n marginBottom: theme.spacing.md,\n} as const;\n\nconst detailLabelStyle = {\n color: theme.colors.textMuted,\n flexShrink: 0,\n fontSize: theme.fontSize.md,\n minWidth: \"72px\",\n textAlign: \"right\",\n} as const;\n\nconst detailValueStyle = {\n fontSize: theme.fontSize.base,\n wordBreak: \"break-all\",\n} as const;\n\nconst containerStyle = {\n borderTop: `1px solid ${theme.colors.border}`,\n padding: `${theme.spacing.lg} ${theme.spacing.xl}`,\n} as const;\n\nconst categoryStyle = {\n color: theme.colors.accent,\n fontFamily: theme.fontFamily.mono,\n} as const;\n\nconst callerStyle = {\n color: theme.colors.textSecondary,\n fontFamily: theme.fontFamily.mono,\n} as const;\n\nconst messageStyle = { color: theme.colors.textPrimary } as const;\n\nconst propertiesStyle = {\n background: \"rgba(0,0,0,0.3)\",\n borderRadius: theme.radius.md,\n color: theme.colors.textPrimary,\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.md,\n margin: 0,\n maxHeight: \"200px\",\n overflow: \"auto\",\n padding: theme.spacing.lg,\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-all\",\n} as const;\n\nconst DetailRow = ({ label, children }: { label: string; children: React.ReactNode }) => (\n <div style={detailRowStyle}>\n <span style={detailLabelStyle}>{label}</span>\n <span style={detailValueStyle}>{children}</span>\n </div>\n);\n\nexport const LogDetail = ({ record }: { record: DevtoolsLogRecord }) => {\n const levelColors = theme.colors.levels[record.level];\n const hasProperties = Object.keys(record.properties).length > 0;\n\n return (\n <div data-testid=\"log-detail\" style={{ ...containerStyle, background: levelColors.bg }}>\n <DetailRow label=\"Time\">{formatTime(record.timestamp)}</DetailRow>\n <DetailRow label=\"Level\">\n <span style={{ color: levelColors.color, fontWeight: 600 }}>{record.level}</span>\n </DetailRow>\n <DetailRow label=\"Category\">\n <span style={categoryStyle}>{record.category.join(\".\")}</span>\n </DetailRow>\n {record.caller ? (\n <DetailRow label=\"Caller\">\n <span style={callerStyle}>{record.caller}</span>\n </DetailRow>\n ) : null}\n <DetailRow label=\"Message\">\n <span style={messageStyle}>{record.messageText}</span>\n </DetailRow>\n\n {hasProperties && (\n <DetailRow label=\"Data\">\n <pre style={propertiesStyle}>{safeStringify(record.properties, 2)}</pre>\n </DetailRow>\n )}\n </div>\n );\n};\n","import { Combobox } from \"@base-ui-components/react/combobox\";\nimport { Popover } from \"@base-ui-components/react/popover\";\nimport { Debouncer } from \"@tanstack/pacer\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { LOG_LEVELS, type LogLevel } from \"../../types\";\nimport { CheckIcon, PauseIcon, PlayIcon, Trash2Icon, XIcon } from \"../icons\";\nimport { theme } from \"../theme\";\n\ninterface ToolbarProps {\n categories: string[];\n categoryFilter: string[];\n filteredCount: number;\n levelFilter: Set<LogLevel>;\n onCategoryFilterChange: (value: string[]) => void;\n onClear: () => void;\n onLevelFilterChange: (levels: Set<LogLevel>) => void;\n onPause: () => void;\n onResume: () => void;\n /** Called with the debounced search term. The toolbar owns the input value. */\n onSearchTextChange: (value: string) => void;\n paused: boolean;\n totalCount: number;\n}\n\n// Inject hover styles once (scoped via data attribute to avoid leaking)\nconst HOVER_STYLE_ID = \"logtape-devtools-hover\";\nfunction useHoverStyles() {\n useEffect(() => {\n if (document.getElementById(HOVER_STYLE_ID)) {\n return;\n }\n const style = document.createElement(\"style\");\n style.id = HOVER_STYLE_ID;\n style.textContent = `\n [data-lt-interactive]:hover {\n filter: brightness(1.25);\n }\n [data-lt-interactive]:active {\n filter: brightness(0.9);\n }\n [data-lt-interactive]:focus-visible,\n [data-lt-input]:focus-visible {\n outline: 2px solid ${theme.colors.accent};\n outline-offset: 1px;\n }\n [data-lt-clear-btn]:hover {\n color: ${theme.colors.textPrimary} !important;\n }\n [data-lt-chip-remove]:hover {\n opacity: 1 !important;\n color: ${theme.colors.white} !important;\n }\n `;\n document.head.appendChild(style);\n }, []);\n}\n\nconst buttonStyle = {\n background: theme.colors.surfaceHover,\n border: `1px solid ${theme.colors.border}`,\n borderRadius: theme.radius.md,\n color: theme.colors.textPrimary,\n cursor: \"pointer\",\n fontFamily: theme.fontFamily.sans,\n fontSize: theme.fontSize.md,\n padding: `${theme.spacing.sm} ${theme.spacing.lg}`,\n transition: \"filter 0.1s, background 0.1s, border-color 0.1s\",\n width: \"100%\",\n} as const;\n\nconst inputStyle = {\n background: theme.colors.surface,\n border: `1px solid ${theme.colors.borderInput}`,\n borderRadius: theme.radius.md,\n color: theme.colors.textPrimary,\n fontFamily: theme.fontFamily.sans,\n fontSize: theme.fontSize.md,\n outline: \"none\",\n padding: `${theme.spacing.sm} ${theme.spacing.lg}`,\n transition: \"border-color 0.15s\",\n width: \"100%\",\n} as const;\n\nconst sectionLabelStyle = {\n color: theme.colors.textMuted,\n fontSize: \"9px\",\n fontWeight: 600,\n letterSpacing: \"0.05em\",\n textTransform: \"uppercase\",\n} as const;\n\nconst LevelToggle = ({\n level,\n active,\n pressed,\n onToggle,\n}: {\n level: LogLevel;\n /** Visual state: lit when the level is included (all levels when no filter is set). */\n active: boolean;\n /** Semantic state: whether the user has explicitly selected this level. */\n pressed: boolean;\n onToggle: (level: LogLevel) => void;\n}) => {\n const colors = theme.colors.levels[level];\n const handleClick = useCallback(() => onToggle(level), [level, onToggle]);\n\n return (\n <button\n aria-pressed={pressed}\n data-lt-interactive=\"\"\n data-testid={`level-toggle-${level}`}\n onClick={handleClick}\n style={{\n background: active ? colors.badge : \"transparent\",\n border: `1px solid ${active ? colors.badge : theme.colors.border}`,\n borderRadius: theme.radius.sm,\n color: active ? theme.colors.white : colors.color,\n cursor: \"pointer\",\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.sm,\n fontWeight: 600,\n lineHeight: 1,\n minWidth: \"30px\",\n opacity: active ? 1 : 0.5,\n padding: \"3px 0\",\n textAlign: \"center\" as const,\n transition: \"all 0.15s\",\n }}\n title={`Filter ${level} logs`}\n type=\"button\"\n >\n {level.slice(0, 3).toUpperCase()}\n </button>\n );\n};\n\nexport const Toolbar = ({\n paused,\n onPause,\n onResume,\n onClear,\n levelFilter,\n onLevelFilterChange,\n categories,\n categoryFilter,\n onCategoryFilterChange,\n onSearchTextChange,\n totalCount,\n filteredCount,\n}: ToolbarProps) => {\n useHoverStyles();\n\n // The toolbar is the single source of truth for the search input; the parent\n // only ever receives the debounced value.\n const [search, setSearch] = useState(\"\");\n const debouncedSearch = useMemo(\n () => new Debouncer(onSearchTextChange, { wait: 200 }),\n [onSearchTextChange]\n );\n\n useEffect(() => () => debouncedSearch.cancel(), [debouncedSearch]);\n\n const handleSearchChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const { value } = e.target;\n setSearch(value);\n debouncedSearch.maybeExecute(value);\n },\n [debouncedSearch]\n );\n\n const handleLevelToggle = useCallback(\n (level: LogLevel) => {\n const next = new Set(levelFilter);\n if (next.has(level)) {\n next.delete(level);\n } else {\n next.add(level);\n }\n onLevelFilterChange(next);\n },\n [levelFilter, onLevelFilterChange]\n );\n\n return (\n <div\n data-testid=\"toolbar\"\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n flexShrink: 0,\n gap: theme.spacing.xl,\n height: \"100%\",\n minWidth: \"200px\",\n overflow: \"auto\",\n padding: `${theme.spacing.lg} ${theme.spacing.xl}`,\n }}\n >\n {/* Log count */}\n <div style={{ textAlign: \"center\" }}>\n <span\n style={{\n color: theme.colors.textPrimary,\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.lg,\n fontWeight: 600,\n }}\n >\n {filteredCount === totalCount ? `${totalCount}` : `${filteredCount} / ${totalCount}`}\n </span>\n <div style={{ color: theme.colors.textMuted, fontSize: theme.fontSize.sm }}>logs</div>\n </div>\n\n {/* Actions */}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: theme.spacing.sm }}>\n <div style={sectionLabelStyle}>Actions</div>\n <button\n data-lt-interactive=\"\"\n onClick={paused ? onResume : onPause}\n style={{\n ...buttonStyle,\n background: paused ? theme.colors.levels.warning.badge : buttonStyle.background,\n color: paused ? theme.colors.white : buttonStyle.color,\n }}\n title={paused ? \"Resume live updates\" : \"Pause live updates\"}\n type=\"button\"\n >\n <span style={{ alignItems: \"center\", display: \"inline-flex\", gap: \"6px\" }}>\n {paused ? <PlayIcon size={12} /> : <PauseIcon size={12} />}\n {paused ? \"Resume\" : \"Pause\"}\n </span>\n </button>\n\n <button\n data-lt-interactive=\"\"\n onClick={onClear}\n style={buttonStyle}\n title=\"Clear all logs\"\n type=\"button\"\n >\n <span style={{ alignItems: \"center\", display: \"inline-flex\", gap: \"6px\" }}>\n <Trash2Icon size={12} />\n Clear\n </span>\n </button>\n </div>\n\n {/* Level filters */}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: theme.spacing.sm }}>\n <div style={{ alignItems: \"center\", display: \"flex\", justifyContent: \"space-between\" }}>\n <div style={sectionLabelStyle}>Levels</div>\n {levelFilter.size > 0 && (\n <button\n data-lt-clear-btn=\"\"\n onClick={() => onLevelFilterChange(new Set())}\n style={{\n background: \"none\",\n border: \"none\",\n color: theme.colors.textMuted,\n cursor: \"pointer\",\n fontSize: \"9px\",\n padding: 0,\n transition: \"color 0.1s\",\n }}\n title=\"Show all levels\"\n type=\"button\"\n >\n clear\n </button>\n )}\n </div>\n <div\n style={{\n display: \"grid\",\n gap: theme.spacing.xs,\n gridTemplateColumns: \"repeat(3, 1fr)\",\n }}\n >\n {LOG_LEVELS.map((level) => (\n <LevelToggle\n active={levelFilter.size === 0 || levelFilter.has(level)}\n key={level}\n level={level}\n onToggle={handleLevelToggle}\n pressed={levelFilter.has(level)}\n />\n ))}\n </div>\n </div>\n\n {/* Category filter (multi-select combobox) */}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: theme.spacing.sm }}>\n <div style={{ alignItems: \"center\", display: \"flex\", justifyContent: \"space-between\" }}>\n <div style={sectionLabelStyle}>Category</div>\n {categoryFilter.length > 0 && (\n <button\n data-lt-clear-btn=\"\"\n onClick={() => onCategoryFilterChange([])}\n style={{\n background: \"none\",\n border: \"none\",\n color: theme.colors.textMuted,\n cursor: \"pointer\",\n fontSize: \"9px\",\n padding: 0,\n transition: \"color 0.1s\",\n }}\n title=\"Clear all categories\"\n type=\"button\"\n >\n clear\n </button>\n )}\n </div>\n {categories.length === 0 ? (\n <Popover.Root>\n <Popover.Trigger\n delay={300}\n nativeButton={false}\n openOnHover\n render={\n <div\n data-testid=\"category-input\"\n style={{\n ...inputStyle,\n color: theme.colors.textMuted,\n cursor: \"not-allowed\",\n opacity: 0.5,\n }}\n >\n Category…\n </div>\n }\n />\n <Popover.Portal>\n <Popover.Positioner sideOffset={6} style={{ zIndex: 999_999 }}>\n <Popover.Popup\n style={{\n background: theme.colors.surface,\n border: `1px solid ${theme.colors.border}`,\n borderRadius: theme.radius.md,\n boxShadow: \"0 8px 24px rgba(0,0,0,0.5)\",\n fontSize: theme.fontSize.sm,\n maxWidth: \"200px\",\n padding: `${theme.spacing.sm} ${theme.spacing.lg}`,\n }}\n >\n <p style={{ color: theme.colors.textMuted, lineHeight: 1.4, margin: 0 }}>\n Waiting for logs to populate available categories.\n </p>\n </Popover.Popup>\n </Popover.Positioner>\n </Popover.Portal>\n </Popover.Root>\n ) : (\n <Combobox.Root\n items={categories}\n multiple\n onValueChange={onCategoryFilterChange}\n value={categoryFilter}\n >\n <div\n style={{\n background: theme.colors.surface,\n border: `1px solid ${theme.colors.borderInput}`,\n borderRadius: theme.radius.md,\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: \"3px\",\n minHeight: \"28px\",\n padding: \"3px\",\n transition: \"border-color 0.15s\",\n }}\n >\n {categoryFilter.map((cat) => (\n <span\n key={cat}\n style={{\n alignItems: \"center\",\n background: \"rgba(99, 102, 241, 0.15)\",\n border: \"1px solid rgba(99, 102, 241, 0.3)\",\n borderRadius: theme.radius.sm,\n color: theme.colors.accentHover,\n display: \"inline-flex\",\n fontFamily: theme.fontFamily.mono,\n fontSize: \"9px\",\n gap: \"3px\",\n lineHeight: 1,\n padding: \"1px 4px\",\n }}\n >\n {cat}\n <button\n data-lt-chip-remove=\"\"\n onClick={() => onCategoryFilterChange(categoryFilter.filter((c) => c !== cat))}\n style={{\n background: \"none\",\n border: \"none\",\n color: theme.colors.textMuted,\n cursor: \"pointer\",\n fontSize: \"7px\",\n lineHeight: 1,\n opacity: 0.7,\n padding: 0,\n transition: \"color 0.1s, opacity 0.1s\",\n }}\n title={`Remove ${cat}`}\n type=\"button\"\n >\n <XIcon size={7} />\n </button>\n </span>\n ))}\n <Combobox.Input\n aria-label=\"Filter by category\"\n data-lt-input=\"\"\n data-testid=\"category-input\"\n placeholder={categoryFilter.length === 0 ? \"Category…\" : \"\"}\n style={{\n background: \"transparent\",\n border: \"none\",\n color: theme.colors.textPrimary,\n flex: 1,\n fontFamily: theme.fontFamily.sans,\n fontSize: theme.fontSize.md,\n minWidth: \"40px\",\n outline: \"none\",\n padding: `${theme.spacing.xs} ${theme.spacing.sm}`,\n }}\n />\n </div>\n <Combobox.Portal>\n <Combobox.Positioner style={{ zIndex: 999_999 }}>\n <Combobox.Popup\n style={{\n background: theme.colors.surface,\n border: `1px solid ${theme.colors.border}`,\n borderRadius: theme.radius.md,\n boxShadow: \"0 8px 24px rgba(0,0,0,0.5)\",\n maxHeight: \"200px\",\n overflow: \"auto\",\n padding: theme.spacing.xs,\n }}\n >\n <Combobox.List>\n {(item, index) => (\n <Combobox.Item\n index={index}\n key={String(item)}\n style={{\n alignItems: \"center\",\n borderRadius: theme.radius.sm,\n color: theme.colors.textPrimary,\n cursor: \"pointer\",\n display: \"flex\",\n fontFamily: theme.fontFamily.mono,\n fontSize: theme.fontSize.md,\n gap: theme.spacing.sm,\n justifyContent: \"space-between\",\n padding: `${theme.spacing.sm} ${theme.spacing.lg}`,\n transition: \"background 0.1s\",\n }}\n value={item}\n >\n <span style={{ flex: 1, overflow: \"hidden\", textOverflow: \"ellipsis\" }}>\n {String(item)}\n </span>\n <Combobox.ItemIndicator>\n <CheckIcon color={theme.colors.accent} size={14} />\n </Combobox.ItemIndicator>\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Popup>\n </Combobox.Positioner>\n </Combobox.Portal>\n </Combobox.Root>\n )}\n </div>\n\n {/* Text search */}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: theme.spacing.sm }}>\n <div style={sectionLabelStyle}>Search</div>\n <input\n aria-label=\"Search logs\"\n data-lt-input=\"\"\n data-testid=\"search-input\"\n onChange={handleSearchChange}\n placeholder=\"Search…\"\n style={inputStyle}\n type=\"text\"\n value={search}\n />\n </div>\n </div>\n );\n};\n","import type { TanStackDevtoolsReactPlugin } from \"@tanstack/react-devtools\";\nimport { defaultLogStore, type LogStore } from \"../store\";\nimport { LogTapeDevtoolsPlugin } from \"./logtape-devtools-plugin\";\n\nexport interface LogTapeDevtoolsPluginOptions {\n /** Whether to open the devtools panel by default. Default: true */\n defaultOpen?: boolean;\n /** Plugin display name. Default: \"LogTape\" */\n name?: string;\n /** Custom log store instance. Must match the store passed to createDevtoolsSink. */\n store?: LogStore;\n}\n\n/**\n * Creates a TanStack DevTools plugin config object for LogTape.\n *\n * Usage:\n * ```tsx\n * import { TanStackDevtools } from \"@tanstack/react-devtools\";\n * import { createLogTapeDevtoolsPlugin } from \"@mugenlabs/logtape-devtools\";\n *\n * <TanStackDevtools plugins={[createLogTapeDevtoolsPlugin()]} />\n * ```\n */\nexport const createLogTapeDevtoolsPlugin = (\n options?: LogTapeDevtoolsPluginOptions\n): TanStackDevtoolsReactPlugin => ({\n defaultOpen: options?.defaultOpen ?? true,\n id: \"logtape-devtools-plugin\",\n name: options?.name ?? \"LogTape\",\n render: <LogTapeDevtoolsPlugin store={options?.store ?? defaultLogStore} />,\n});\n","import {\n createLogTapeDevtoolsPlugin,\n type LogTapeDevtoolsPluginOptions,\n} from \"./plugin/create-logtape-devtools-plugin\";\nimport { createDevtoolsSink, type DevtoolsSinkOptions } from \"./sink\";\nimport { defaultLogStore, type LogStore } from \"./store\";\n\n/** Options for {@link createLogTapeDevtools}. */\nexport interface CreateLogTapeDevtoolsOptions {\n /** Plugin options, minus `store` — the shared store is wired automatically. */\n plugin?: Omit<LogTapeDevtoolsPluginOptions, \"store\">;\n /** Sink options, minus `store` — the shared store is wired automatically. */\n sink?: Omit<DevtoolsSinkOptions, \"store\">;\n /** Store shared by the sink and the plugin. Defaults to the shared store. */\n store?: LogStore;\n}\n\n/**\n * Creates a LogTape sink and its matching TanStack DevTools plugin, both wired\n * to the same store.\n *\n * Use this instead of calling `createDevtoolsSink` and\n * `createLogTapeDevtoolsPlugin` separately when you do not need to hold on to\n * the store yourself.\n *\n * ```tsx\n * import { configure, getLogger } from \"@logtape/logtape\";\n * import { TanStackDevtools } from \"@tanstack/react-devtools\";\n * import { createLogTapeDevtools } from \"@mugenlabs/logtape-devtools\";\n *\n * const { sink, plugin } = createLogTapeDevtools();\n *\n * await configure({\n * sinks: { devtools: sink },\n * loggers: [{ category: [], lowestLevel: \"trace\", sinks: [\"devtools\"] }],\n * });\n *\n * function App() {\n * return <TanStackDevtools plugins={[plugin]} />;\n * }\n * ```\n */\nexport function createLogTapeDevtools(options?: CreateLogTapeDevtoolsOptions) {\n const store = options?.store ?? defaultLogStore;\n\n return {\n plugin: createLogTapeDevtoolsPlugin({ ...options?.plugin, store }),\n sink: createDevtoolsSink({ ...options?.sink, store }),\n };\n}\n"],"mappings":"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","names":["useCallback","useMemo","useState","useCallback","jsx","jsx","jsxs","jsx","jsxs","categoryStyle","messageStyle","callerStyle","jsx","containerStyle","useCallback","useCallback","useEffect","jsx","jsxs","useEffect","useCallback","jsx","jsxs","useState","useCallback","useMemo","jsx"]}
|
package/dist/sink.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|