@mugenlabs/logtape-devtools 0.4.1 → 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/dist/index.mjs CHANGED
@@ -1,431 +1,4 @@
1
- import {
2
- LOG_LEVELS,
3
- createDevtoolsSink,
4
- createLogStore,
5
- defaultLogStore,
6
- safeStringify
7
- } from "./chunk-FDWUISCJ.mjs";
8
-
9
- // src/plugin/logtape-devtools-plugin.tsx
10
- import { useCallback as useCallback4, useMemo as useMemo2, useState as useState2, useSyncExternalStore } from "react";
11
-
12
- // src/plugin/components/log-list.tsx
13
- import { useVirtualizer } from "@tanstack/react-virtual";
14
- import { useCallback as useCallback2, useEffect, useRef } from "react";
15
-
16
- // src/plugin/theme.ts
17
- var theme = {
18
- colors: {
19
- accent: "#6366f1",
20
- accentHover: "#818cf8",
21
- background: "#1e1e1e",
22
- border: "#333",
23
- borderFocus: "#6366f1",
24
- borderInput: "#444",
25
- levels: {
26
- // Badge colours keep at least 4.5:1 contrast against white badge text.
27
- debug: { badge: "#2f7a2f", bg: "#1a2a1a", color: "#88bb88" },
28
- error: { badge: "#dc2626", bg: "#2a1a1a", color: "#ef4444" },
29
- fatal: { badge: "#b91c1c", bg: "#3a0a0a", color: "#ff6b6b" },
30
- info: { badge: "#2563eb", bg: "#1a2a3a", color: "#60a5fa" },
31
- trace: { badge: "#5555aa", bg: "#1a1a2e", color: "#8888bb" },
32
- warning: { badge: "#eab308", bg: "#2a2a1a", color: "#facc15" }
33
- },
34
- scrollbar: "#444",
35
- scrollbarHover: "#555",
36
- surface: "#1a1a1a",
37
- surfaceHover: "#2a2a2a",
38
- surfaceSelected: "#2a2a3a",
39
- textDimmed: "#555",
40
- textMuted: "#666",
41
- textPrimary: "#e0e0e0",
42
- textSecondary: "#888",
43
- white: "#fff"
44
- },
45
- fontFamily: {
46
- mono: 'ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace',
47
- sans: 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif'
48
- },
49
- fontSize: {
50
- base: "12px",
51
- lg: "13px",
52
- md: "11px",
53
- sm: "10px",
54
- xs: "9px"
55
- },
56
- radius: {
57
- lg: "6px",
58
- md: "4px",
59
- pill: "10px",
60
- sm: "3px"
61
- },
62
- spacing: {
63
- lg: "8px",
64
- md: "6px",
65
- sm: "4px",
66
- xl: "12px",
67
- xs: "2px",
68
- xxl: "16px"
69
- }
70
- };
71
- var getLevelColors = (level) => theme.colors.levels[level] ?? theme.colors.levels.info;
72
-
73
- // src/plugin/components/log-row.tsx
74
- import { memo, useCallback } from "react";
75
-
76
- // src/plugin/format.ts
77
- var timeFormatter;
78
- function formatTime(ts) {
79
- timeFormatter ??= new Intl.DateTimeFormat(void 0, {
80
- hour: "2-digit",
81
- hour12: false,
82
- minute: "2-digit",
83
- second: "2-digit"
84
- });
85
- return timeFormatter.format(new Date(ts));
86
- }
87
-
88
- // src/plugin/icons.tsx
89
- import { jsx, jsxs } from "react/jsx-runtime";
90
- var defaults = { color: "currentColor", size: 14 };
91
- var Svg = ({
92
- size = defaults.size,
93
- color = defaults.color,
94
- style,
95
- children
96
- }) => /* @__PURE__ */ jsx(
97
- "svg",
98
- {
99
- "aria-hidden": "true",
100
- fill: "none",
101
- height: size,
102
- stroke: color,
103
- strokeLinecap: "round",
104
- strokeLinejoin: "round",
105
- strokeWidth: 2,
106
- style,
107
- viewBox: "0 0 24 24",
108
- width: size,
109
- xmlns: "http://www.w3.org/2000/svg",
110
- children
111
- }
112
- );
113
- var PlayIcon = (props) => /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("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" }) });
114
- var PauseIcon = (props) => /* @__PURE__ */ jsxs(Svg, { ...props, children: [
115
- /* @__PURE__ */ jsx("rect", { height: 18, rx: 1, width: 5, x: 14, y: 3 }),
116
- /* @__PURE__ */ jsx("rect", { height: 18, rx: 1, width: 5, x: 5, y: 3 })
117
- ] });
118
- var XIcon = (props) => /* @__PURE__ */ jsxs(Svg, { ...props, children: [
119
- /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
120
- /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
121
- ] });
122
- var CheckIcon = (props) => /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) });
123
- var ChevronRightIcon = (props) => /* @__PURE__ */ jsx(Svg, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) });
124
- var PanelLeftCloseIcon = (props) => /* @__PURE__ */ jsxs(Svg, { ...props, children: [
125
- /* @__PURE__ */ jsx("rect", { height: 18, rx: 2, width: 18, x: 3, y: 3 }),
126
- /* @__PURE__ */ jsx("path", { d: "M9 3v18" }),
127
- /* @__PURE__ */ jsx("path", { d: "m16 15-3-3 3-3" })
128
- ] });
129
- var PanelLeftOpenIcon = (props) => /* @__PURE__ */ jsxs(Svg, { ...props, children: [
130
- /* @__PURE__ */ jsx("rect", { height: 18, rx: 2, width: 18, x: 3, y: 3 }),
131
- /* @__PURE__ */ jsx("path", { d: "M9 3v18" }),
132
- /* @__PURE__ */ jsx("path", { d: "m14 9 3 3-3 3" })
133
- ] });
134
- var Trash2Icon = (props) => /* @__PURE__ */ jsxs(Svg, { ...props, children: [
135
- /* @__PURE__ */ jsx("path", { d: "M10 11v6" }),
136
- /* @__PURE__ */ jsx("path", { d: "M14 11v6" }),
137
- /* @__PURE__ */ jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }),
138
- /* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
139
- /* @__PURE__ */ jsx("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
140
- ] });
141
-
142
- // src/plugin/components/level-badge.tsx
143
- import { jsx as jsx2 } from "react/jsx-runtime";
144
- var LEVEL_LABELS = {
145
- debug: "DBG",
146
- error: "ERR",
147
- fatal: "FTL",
148
- info: "INF",
149
- trace: "TRC",
150
- warning: "WRN"
151
- };
152
- var LevelBadge = ({ level }) => {
153
- const colors = getLevelColors(level);
154
- const label = LEVEL_LABELS[level] ?? level.toUpperCase();
155
- return /* @__PURE__ */ jsx2(
156
- "span",
157
- {
158
- style: {
159
- background: colors.badge,
160
- borderRadius: theme.radius.sm,
161
- color: theme.colors.white,
162
- display: "inline-block",
163
- fontFamily: theme.fontFamily.mono,
164
- fontSize: theme.fontSize.sm,
165
- fontWeight: 600,
166
- lineHeight: 1,
167
- minWidth: "28px",
168
- padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
169
- textAlign: "center",
170
- textTransform: "uppercase"
171
- },
172
- children: label
173
- }
174
- );
175
- };
176
-
177
- // src/plugin/components/log-detail.tsx
178
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
179
- var detailRowStyle = {
180
- display: "flex",
181
- gap: theme.spacing.lg,
182
- marginBottom: theme.spacing.md
183
- };
184
- var detailLabelStyle = {
185
- color: theme.colors.textMuted,
186
- flexShrink: 0,
187
- fontSize: theme.fontSize.md,
188
- minWidth: "72px",
189
- textAlign: "right"
190
- };
191
- var detailValueStyle = {
192
- fontSize: theme.fontSize.base,
193
- wordBreak: "break-all"
194
- };
195
- var containerStyle = {
196
- borderTop: `1px solid ${theme.colors.border}`,
197
- padding: `${theme.spacing.lg} ${theme.spacing.xl}`
198
- };
199
- var categoryStyle = {
200
- color: theme.colors.accent,
201
- fontFamily: theme.fontFamily.mono
202
- };
203
- var callerStyle = {
204
- color: theme.colors.textSecondary,
205
- fontFamily: theme.fontFamily.mono
206
- };
207
- var messageStyle = { color: theme.colors.textPrimary };
208
- var propertiesStyle = {
209
- background: "rgba(0,0,0,0.3)",
210
- borderRadius: theme.radius.md,
211
- color: theme.colors.textPrimary,
212
- fontFamily: theme.fontFamily.mono,
213
- fontSize: theme.fontSize.md,
214
- margin: 0,
215
- maxHeight: "200px",
216
- overflow: "auto",
217
- padding: theme.spacing.lg,
218
- whiteSpace: "pre-wrap",
219
- wordBreak: "break-all"
220
- };
221
- var DetailRow = ({ label, children }) => /* @__PURE__ */ jsxs2("div", { style: detailRowStyle, children: [
222
- /* @__PURE__ */ jsx3("span", { style: detailLabelStyle, children: label }),
223
- /* @__PURE__ */ jsx3("span", { style: detailValueStyle, children })
224
- ] });
225
- var LogDetail = ({ record }) => {
226
- const levelColors = getLevelColors(record.level);
227
- const hasProperties = Object.keys(record.properties).length > 0;
228
- return /* @__PURE__ */ jsxs2("div", { "data-testid": "log-detail", style: { ...containerStyle, background: levelColors.bg }, children: [
229
- /* @__PURE__ */ jsx3(DetailRow, { label: "Time", children: formatTime(record.timestamp) }),
230
- /* @__PURE__ */ jsx3(DetailRow, { label: "Level", children: /* @__PURE__ */ jsx3("span", { style: { color: levelColors.color, fontWeight: 600 }, children: record.level }) }),
231
- /* @__PURE__ */ jsx3(DetailRow, { label: "Category", children: /* @__PURE__ */ jsx3("span", { style: categoryStyle, children: record.category.join(".") }) }),
232
- record.caller ? /* @__PURE__ */ jsx3(DetailRow, { label: "Caller", children: /* @__PURE__ */ jsx3("span", { style: callerStyle, children: record.caller }) }) : null,
233
- /* @__PURE__ */ jsx3(DetailRow, { label: "Message", children: /* @__PURE__ */ jsx3("span", { style: messageStyle, children: record.messageText }) }),
234
- hasProperties && /* @__PURE__ */ jsx3(DetailRow, { label: "Data", children: /* @__PURE__ */ jsx3("pre", { style: propertiesStyle, children: safeStringify(record.properties, 2) }) })
235
- ] });
236
- };
237
-
238
- // src/plugin/components/log-row.tsx
239
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
240
- var rowStyle = {
241
- alignItems: "center",
242
- border: "none",
243
- borderBottom: `1px solid ${theme.colors.border}`,
244
- cursor: "pointer",
245
- display: "flex",
246
- gap: theme.spacing.lg,
247
- padding: `${theme.spacing.md} ${theme.spacing.xl}`,
248
- textAlign: "left",
249
- transition: "background 0.1s, filter 0.1s",
250
- width: "100%"
251
- };
252
- var timestampStyle = {
253
- color: theme.colors.textDimmed,
254
- flexShrink: 0,
255
- fontFamily: theme.fontFamily.mono,
256
- fontSize: theme.fontSize.md
257
- };
258
- var categoryStyle2 = {
259
- color: theme.colors.accent,
260
- flexShrink: 0,
261
- fontFamily: theme.fontFamily.mono,
262
- fontSize: theme.fontSize.md,
263
- maxWidth: "180px",
264
- overflow: "hidden",
265
- textOverflow: "ellipsis",
266
- whiteSpace: "nowrap"
267
- };
268
- var messageStyle2 = {
269
- color: theme.colors.textPrimary,
270
- flex: 1,
271
- fontSize: theme.fontSize.base,
272
- overflow: "hidden",
273
- textOverflow: "ellipsis",
274
- whiteSpace: "nowrap"
275
- };
276
- var callerStyle2 = {
277
- color: theme.colors.textDimmed,
278
- flexShrink: 0,
279
- fontFamily: theme.fontFamily.mono,
280
- fontSize: theme.fontSize.sm,
281
- maxWidth: "200px",
282
- opacity: 0.7,
283
- overflow: "hidden",
284
- textOverflow: "ellipsis",
285
- whiteSpace: "nowrap"
286
- };
287
- var chevronStyle = {
288
- color: theme.colors.textDimmed,
289
- display: "flex",
290
- flexShrink: 0,
291
- transition: "transform 0.15s"
292
- };
293
- var LogRowComponent = ({ record, expanded, onToggle }) => {
294
- const levelColors = getLevelColors(record.level);
295
- const handleClick = useCallback(() => {
296
- onToggle(expanded ? null : record.id);
297
- }, [expanded, onToggle, record.id]);
298
- return /* @__PURE__ */ jsxs3("div", { children: [
299
- /* @__PURE__ */ jsxs3(
300
- "button",
301
- {
302
- "aria-expanded": expanded,
303
- "data-lt-interactive": "",
304
- "data-testid": "log-row",
305
- onClick: handleClick,
306
- style: { ...rowStyle, background: expanded ? levelColors.bg : "transparent" },
307
- type: "button",
308
- children: [
309
- /* @__PURE__ */ jsx4("span", { style: timestampStyle, children: formatTime(record.timestamp) }),
310
- /* @__PURE__ */ jsx4(LevelBadge, { level: record.level }),
311
- /* @__PURE__ */ jsx4("span", { style: categoryStyle2, children: record.category.join(".") }),
312
- /* @__PURE__ */ jsx4("span", { style: messageStyle2, children: record.messageText }),
313
- record.caller ? /* @__PURE__ */ jsx4("span", { style: callerStyle2, children: record.caller }) : null,
314
- /* @__PURE__ */ jsx4("span", { style: { ...chevronStyle, transform: expanded ? "rotate(90deg)" : "rotate(0deg)" }, children: /* @__PURE__ */ jsx4(ChevronRightIcon, { size: 12 }) })
315
- ]
316
- }
317
- ),
318
- expanded ? /* @__PURE__ */ jsx4(LogDetail, { record }) : null
319
- ] });
320
- };
321
- var LogRow = memo(LogRowComponent);
322
-
323
- // src/plugin/components/log-list.tsx
324
- import { jsx as jsx5 } from "react/jsx-runtime";
325
- var ESTIMATED_ROW_HEIGHT = 28;
326
- var OVERSCAN = 10;
327
- var STICK_THRESHOLD = 40;
328
- var emptyStyle = {
329
- alignItems: "center",
330
- color: theme.colors.textMuted,
331
- display: "flex",
332
- flex: 1,
333
- fontSize: theme.fontSize.lg,
334
- justifyContent: "center"
335
- };
336
- var containerStyle2 = {
337
- flex: 1,
338
- overflow: "auto",
339
- scrollbarColor: `${theme.colors.scrollbar} transparent`,
340
- scrollbarWidth: "thin"
341
- };
342
- var LogList = ({ records, expandedId, onToggle, autoScroll }) => {
343
- const containerRef = useRef(null);
344
- const isAtBottomRef = useRef(true);
345
- const virtualizer = useVirtualizer({
346
- count: records.length,
347
- estimateSize: () => ESTIMATED_ROW_HEIGHT,
348
- // The virtualizer can ask for keys of indices from a previous, longer list
349
- // (after clear or a narrowing filter), so never assume the index exists.
350
- getItemKey: (index) => records[index]?.id ?? index,
351
- getScrollElement: () => containerRef.current,
352
- overscan: OVERSCAN
353
- });
354
- const recordCount = records.length;
355
- const lastRecordId = records[recordCount - 1]?.id;
356
- useEffect(() => {
357
- if (lastRecordId === void 0 || !(autoScroll && isAtBottomRef.current)) {
358
- return;
359
- }
360
- virtualizer.scrollToIndex(recordCount - 1, { align: "end" });
361
- }, [autoScroll, lastRecordId, recordCount, virtualizer]);
362
- useEffect(() => {
363
- if (recordCount === 0) {
364
- isAtBottomRef.current = true;
365
- }
366
- }, [recordCount]);
367
- const handleScroll = useCallback2(() => {
368
- const el = containerRef.current;
369
- if (!el) {
370
- return;
371
- }
372
- isAtBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < STICK_THRESHOLD;
373
- }, []);
374
- if (records.length === 0) {
375
- return /* @__PURE__ */ jsx5("div", { "data-testid": "log-list-empty", style: emptyStyle, children: "No logs yet" });
376
- }
377
- const virtualItems = virtualizer.getVirtualItems();
378
- return /* @__PURE__ */ jsx5("div", { "data-testid": "log-list", onScroll: handleScroll, ref: containerRef, style: containerStyle2, children: /* @__PURE__ */ jsx5(
379
- "div",
380
- {
381
- style: {
382
- height: `${virtualizer.getTotalSize()}px`,
383
- position: "relative",
384
- width: "100%"
385
- },
386
- children: /* @__PURE__ */ jsx5(
387
- "div",
388
- {
389
- style: {
390
- left: 0,
391
- position: "absolute",
392
- top: 0,
393
- transform: `translateY(${virtualItems[0]?.start ?? 0}px)`,
394
- width: "100%"
395
- },
396
- children: virtualItems.map((virtualItem) => {
397
- const record = records[virtualItem.index];
398
- return /* @__PURE__ */ jsx5(
399
- "div",
400
- {
401
- "data-index": virtualItem.index,
402
- ref: virtualizer.measureElement,
403
- children: /* @__PURE__ */ jsx5(LogRow, { expanded: expandedId === record.id, onToggle, record })
404
- },
405
- virtualItem.key
406
- );
407
- })
408
- }
409
- )
410
- }
411
- ) });
412
- };
413
-
414
- // src/plugin/components/toolbar.tsx
415
- import { Combobox } from "@base-ui-components/react/combobox";
416
- import { Popover } from "@base-ui-components/react/popover";
417
- import { Debouncer } from "@tanstack/pacer";
418
- import { useCallback as useCallback3, useEffect as useEffect2, useMemo, useState } from "react";
419
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
420
- var HOVER_STYLE_ID = "logtape-devtools-hover";
421
- function useHoverStyles() {
422
- useEffect2(() => {
423
- if (document.getElementById(HOVER_STYLE_ID)) {
424
- return;
425
- }
426
- const style = document.createElement("style");
427
- style.id = HOVER_STYLE_ID;
428
- style.textContent = `
1
+ import{a as Ce,b as F,c as ee,d as Y,e as j}from"./chunk-HWGRZ7VO.mjs";import{useCallback as K,useMemo as he,useState as R,useSyncExternalStore as to}from"react";import{useVirtualizer as Ke}from"@tanstack/react-virtual";import{useCallback as Ge,useEffect as me,useRef as fe}from"react";var e={colors:{accent:"#6366f1",accentHover:"#818cf8",background:"#1e1e1e",border:"#333",borderFocus:"#6366f1",borderInput:"#444",levels:{debug:{badge:"#2f7a2f",bg:"#1a2a1a",color:"#88bb88"},error:{badge:"#dc2626",bg:"#2a1a1a",color:"#ef4444"},fatal:{badge:"#b91c1c",bg:"#3a0a0a",color:"#ff6b6b"},info:{badge:"#2563eb",bg:"#1a2a3a",color:"#60a5fa"},trace:{badge:"#5555aa",bg:"#1a1a2e",color:"#8888bb"},warning:{badge:"#eab308",bg:"#2a2a1a",color:"#facc15"}},scrollbar:"#444",scrollbarHover:"#555",surface:"#1a1a1a",surfaceHover:"#2a2a2a",surfaceSelected:"#2a2a3a",textDimmed:"#555",textMuted:"#666",textPrimary:"#e0e0e0",textSecondary:"#888",white:"#fff"},fontFamily:{mono:'ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, monospace',sans:'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif'},fontSize:{base:"12px",lg:"13px",md:"11px",sm:"10px",xs:"9px"},radius:{lg:"6px",md:"4px",pill:"10px",sm:"3px"},spacing:{lg:"8px",md:"6px",sm:"4px",xl:"12px",xs:"2px",xxl:"16px"}},D=o=>e.colors.levels[o]??e.colors.levels.info;import{memo as He,useCallback as Me}from"react";var oe;function W(o){return oe??=new Intl.DateTimeFormat(void 0,{hour:"2-digit",hour12:!1,minute:"2-digit",second:"2-digit"}),oe.format(new Date(o))}import{jsx as s,jsxs as O}from"react/jsx-runtime";var te={color:"currentColor",size:14},w=({size:o=te.size,color:t=te.color,style:l,children:m})=>s("svg",{"aria-hidden":"true",fill:"none",height:o,stroke:t,strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,style:l,viewBox:"0 0 24 24",width:o,xmlns:"http://www.w3.org/2000/svg",children:m}),B=o=>s(w,{...o,children:s("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"})}),A=o=>O(w,{...o,children:[s("rect",{height:18,rx:1,width:5,x:14,y:3}),s("rect",{height:18,rx:1,width:5,x:5,y:3})]}),ne=o=>O(w,{...o,children:[s("path",{d:"M18 6 6 18"}),s("path",{d:"m6 6 12 12"})]}),re=o=>s(w,{...o,children:s("path",{d:"M20 6 9 17l-5-5"})}),se=o=>s(w,{...o,children:s("path",{d:"m9 18 6-6-6-6"})}),ae=o=>O(w,{...o,children:[s("rect",{height:18,rx:2,width:18,x:3,y:3}),s("path",{d:"M9 3v18"}),s("path",{d:"m16 15-3-3 3-3"})]}),le=o=>O(w,{...o,children:[s("rect",{height:18,rx:2,width:18,x:3,y:3}),s("path",{d:"M9 3v18"}),s("path",{d:"m14 9 3 3-3 3"})]}),V=o=>O(w,{...o,children:[s("path",{d:"M10 11v6"}),s("path",{d:"M14 11v6"}),s("path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"}),s("path",{d:"M3 6h18"}),s("path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"})]});import{jsx as Pe}from"react/jsx-runtime";var ke={debug:"DBG",error:"ERR",fatal:"FTL",info:"INF",trace:"TRC",warning:"WRN"},ie=({level:o})=>{let t=D(o),l=ke[o]??o.toUpperCase();return Pe("span",{style:{background:t.badge,borderRadius:e.radius.sm,color:e.colors.white,display:"inline-block",fontFamily:e.fontFamily.mono,fontSize:e.fontSize.sm,fontWeight:600,lineHeight:1,minWidth:"28px",padding:`${e.spacing.xs} ${e.spacing.sm}`,textAlign:"center",textTransform:"uppercase"},children:l})};import{jsx as f,jsxs as de}from"react/jsx-runtime";var Re={display:"flex",gap:e.spacing.lg,marginBottom:e.spacing.md},ze={color:e.colors.textMuted,flexShrink:0,fontSize:e.fontSize.md,minWidth:"72px",textAlign:"right"},Ie={fontSize:e.fontSize.base,wordBreak:"break-all"},De={borderTop:`1px solid ${e.colors.border}`,padding:`${e.spacing.lg} ${e.spacing.xl}`},Te={color:e.colors.accent,fontFamily:e.fontFamily.mono},$e={color:e.colors.textSecondary,fontFamily:e.fontFamily.mono},Fe={color:e.colors.textPrimary},Oe={background:"rgba(0,0,0,0.3)",borderRadius:e.radius.md,color:e.colors.textPrimary,fontFamily:e.fontFamily.mono,fontSize:e.fontSize.md,margin:0,maxHeight:"200px",overflow:"auto",padding:e.spacing.lg,whiteSpace:"pre-wrap",wordBreak:"break-all"},T=({label:o,children:t})=>de("div",{style:Re,children:[f("span",{style:ze,children:o}),f("span",{style:Ie,children:t})]}),ce=({record:o})=>{let t=D(o.level),l=Object.keys(o.properties).length>0;return de("div",{"data-testid":"log-detail",style:{...De,background:t.bg},children:[f(T,{label:"Time",children:W(o.timestamp)}),f(T,{label:"Level",children:f("span",{style:{color:t.color,fontWeight:600},children:o.level})}),f(T,{label:"Category",children:f("span",{style:Te,children:o.category.join(".")})}),o.caller?f(T,{label:"Caller",children:f("span",{style:$e,children:o.caller})}):null,f(T,{label:"Message",children:f("span",{style:Fe,children:o.messageText})}),l&&f(T,{label:"Data",children:f("pre",{style:Oe,children:ee(o.properties,2)})})]})};import{jsx as C,jsxs as pe}from"react/jsx-runtime";var Ee={alignItems:"center",border:"none",borderBottom:`1px solid ${e.colors.border}`,cursor:"pointer",display:"flex",gap:e.spacing.lg,padding:`${e.spacing.md} ${e.spacing.xl}`,textAlign:"left",transition:"background 0.1s, filter 0.1s",width:"100%"},We={color:e.colors.textDimmed,flexShrink:0,fontFamily:e.fontFamily.mono,fontSize:e.fontSize.md},Be={color:e.colors.accent,flexShrink:0,fontFamily:e.fontFamily.mono,fontSize:e.fontSize.md,maxWidth:"180px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},Ae={color:e.colors.textPrimary,flex:1,fontSize:e.fontSize.base,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},Ve={color:e.colors.textDimmed,flexShrink:0,fontFamily:e.fontFamily.mono,fontSize:e.fontSize.sm,maxWidth:"200px",opacity:.7,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},_e={color:e.colors.textDimmed,display:"flex",flexShrink:0,transition:"transform 0.15s"},Ne=({record:o,expanded:t,onToggle:l})=>{let m=D(o.level),a=Me(()=>{l(t?null:o.id)},[t,l,o.id]);return pe("div",{children:[pe("button",{"aria-expanded":t,"data-lt-interactive":"","data-testid":"log-row",onClick:a,style:{...Ee,background:t?m.bg:"transparent"},type:"button",children:[C("span",{style:We,children:W(o.timestamp)}),C(ie,{level:o.level}),C("span",{"data-testid":"log-row-category",style:Be,children:o.category.join(".")}),C("span",{"data-testid":"log-row-message",style:Ae,children:o.messageText}),o.caller?C("span",{style:Ve,children:o.caller}):null,C("span",{style:{..._e,transform:t?"rotate(90deg)":"rotate(0deg)"},children:C(se,{size:12})})]}),t?C(ce,{record:o}):null]})},ge=He(Ne);import{jsx as $}from"react/jsx-runtime";var Ue=28,Xe=10,Ye=40,je={alignItems:"center",color:e.colors.textMuted,display:"flex",flex:1,fontSize:e.fontSize.lg,justifyContent:"center"},qe={flex:1,overflow:"auto",scrollbarColor:`${e.colors.scrollbar} transparent`,scrollbarWidth:"thin"},ue=({records:o,expandedId:t,onToggle:l,autoScroll:m})=>{let a=fe(null),p=fe(!0),y=Ke({count:o.length,estimateSize:()=>Ue,getItemKey:c=>o[c]?.id??c,getScrollElement:()=>a.current,overscan:Xe}),i=o.length,v=o[i-1]?.id;me(()=>{v===void 0||!(m&&p.current)||y.scrollToIndex(i-1,{align:"end"})},[m,v,i,y]),me(()=>{i===0&&(p.current=!0)},[i]);let h=Ge(()=>{let c=a.current;c&&(p.current=c.scrollHeight-c.scrollTop-c.clientHeight<Ye)},[]);if(o.length===0)return $("div",{"data-testid":"log-list-empty",style:je,children:"No logs yet"});let S=y.getVirtualItems();return $("div",{"data-testid":"log-list",onScroll:h,ref:a,style:qe,children:$("div",{style:{height:`${y.getTotalSize()}px`,position:"relative",width:"100%"},children:$("div",{style:{left:0,position:"absolute",top:0,transform:`translateY(${S[0]?.start??0}px)`,width:"100%"},children:S.map(c=>{let P=o[c.index];return $("div",{"data-index":c.index,ref:y.measureElement,children:$(ge,{expanded:t===P.id,onToggle:l,record:P})},c.key)})})})})};import{Combobox as k}from"@base-ui-components/react/combobox";import{Popover as H}from"@base-ui-components/react/popover";import{Debouncer as Je}from"@tanstack/pacer";import{useCallback as q,useEffect as be,useMemo as Qe,useState as Ze}from"react";import{jsx as n,jsxs as d}from"react/jsx-runtime";var ye="logtape-devtools-hover";function eo(){be(()=>{if(document.getElementById(ye))return;let o=document.createElement("style");o.id=ye,o.textContent=`
429
2
  [data-lt-interactive]:hover {
430
3
  filter: brightness(1.25);
431
4
  }
@@ -434,668 +7,14 @@ function useHoverStyles() {
434
7
  }
435
8
  [data-lt-interactive]:focus-visible,
436
9
  [data-lt-input]:focus-visible {
437
- outline: 2px solid ${theme.colors.accent};
10
+ outline: 2px solid ${e.colors.accent};
438
11
  outline-offset: 1px;
439
12
  }
440
13
  [data-lt-clear-btn]:hover {
441
- color: ${theme.colors.textPrimary} !important;
14
+ color: ${e.colors.textPrimary} !important;
442
15
  }
443
16
  [data-lt-chip-remove]:hover {
444
17
  opacity: 1 !important;
445
- color: ${theme.colors.white} !important;
446
- }
447
- `;
448
- document.head.appendChild(style);
449
- }, []);
450
- }
451
- var buttonStyle = {
452
- background: theme.colors.surfaceHover,
453
- border: `1px solid ${theme.colors.border}`,
454
- borderRadius: theme.radius.md,
455
- color: theme.colors.textPrimary,
456
- cursor: "pointer",
457
- fontFamily: theme.fontFamily.sans,
458
- fontSize: theme.fontSize.md,
459
- padding: `${theme.spacing.sm} ${theme.spacing.lg}`,
460
- transition: "filter 0.1s, background 0.1s, border-color 0.1s",
461
- width: "100%"
462
- };
463
- var inputStyle = {
464
- background: theme.colors.surface,
465
- border: `1px solid ${theme.colors.borderInput}`,
466
- borderRadius: theme.radius.md,
467
- color: theme.colors.textPrimary,
468
- fontFamily: theme.fontFamily.sans,
469
- fontSize: theme.fontSize.md,
470
- outline: "none",
471
- padding: `${theme.spacing.sm} ${theme.spacing.lg}`,
472
- transition: "border-color 0.15s",
473
- width: "100%"
474
- };
475
- var sectionLabelStyle = {
476
- color: theme.colors.textMuted,
477
- fontSize: "9px",
478
- fontWeight: 600,
479
- letterSpacing: "0.05em",
480
- textTransform: "uppercase"
481
- };
482
- var LevelToggle = ({
483
- level,
484
- active,
485
- pressed,
486
- onToggle
487
- }) => {
488
- const colors = theme.colors.levels[level];
489
- const handleClick = useCallback3(() => onToggle(level), [level, onToggle]);
490
- return /* @__PURE__ */ jsx6(
491
- "button",
492
- {
493
- "aria-pressed": pressed,
494
- "data-lt-interactive": "",
495
- "data-testid": `level-toggle-${level}`,
496
- onClick: handleClick,
497
- style: {
498
- background: active ? colors.badge : "transparent",
499
- border: `1px solid ${active ? colors.badge : theme.colors.border}`,
500
- borderRadius: theme.radius.sm,
501
- color: active ? theme.colors.white : colors.color,
502
- cursor: "pointer",
503
- fontFamily: theme.fontFamily.mono,
504
- fontSize: theme.fontSize.sm,
505
- fontWeight: 600,
506
- lineHeight: 1,
507
- minWidth: "30px",
508
- opacity: active ? 1 : 0.5,
509
- padding: "3px 0",
510
- textAlign: "center",
511
- transition: "all 0.15s"
512
- },
513
- title: `Filter ${level} logs`,
514
- type: "button",
515
- children: level.slice(0, 3).toUpperCase()
516
- }
517
- );
518
- };
519
- var Toolbar = ({
520
- paused,
521
- onPause,
522
- onResume,
523
- onClear,
524
- levelFilter,
525
- onLevelFilterChange,
526
- categories,
527
- categoryFilter,
528
- onCategoryFilterChange,
529
- onSearchTextChange,
530
- totalCount,
531
- filteredCount
532
- }) => {
533
- useHoverStyles();
534
- const [search, setSearch] = useState("");
535
- const debouncedSearch = useMemo(
536
- () => new Debouncer(onSearchTextChange, { wait: 200 }),
537
- [onSearchTextChange]
538
- );
539
- useEffect2(() => () => debouncedSearch.cancel(), [debouncedSearch]);
540
- const handleSearchChange = useCallback3(
541
- (e) => {
542
- const { value } = e.target;
543
- setSearch(value);
544
- debouncedSearch.maybeExecute(value);
545
- },
546
- [debouncedSearch]
547
- );
548
- const handleLevelToggle = useCallback3(
549
- (level) => {
550
- const next = new Set(levelFilter);
551
- if (next.has(level)) {
552
- next.delete(level);
553
- } else {
554
- next.add(level);
555
- }
556
- onLevelFilterChange(next);
557
- },
558
- [levelFilter, onLevelFilterChange]
559
- );
560
- return /* @__PURE__ */ jsxs4(
561
- "div",
562
- {
563
- "data-testid": "toolbar",
564
- style: {
565
- display: "flex",
566
- flexDirection: "column",
567
- flexShrink: 0,
568
- gap: theme.spacing.xl,
569
- height: "100%",
570
- minWidth: "200px",
571
- overflow: "auto",
572
- padding: `${theme.spacing.lg} ${theme.spacing.xl}`
573
- },
574
- children: [
575
- /* @__PURE__ */ jsxs4("div", { style: { textAlign: "center" }, children: [
576
- /* @__PURE__ */ jsx6(
577
- "span",
578
- {
579
- style: {
580
- color: theme.colors.textPrimary,
581
- fontFamily: theme.fontFamily.mono,
582
- fontSize: theme.fontSize.lg,
583
- fontWeight: 600
584
- },
585
- children: filteredCount === totalCount ? `${totalCount}` : `${filteredCount} / ${totalCount}`
586
- }
587
- ),
588
- /* @__PURE__ */ jsx6("div", { style: { color: theme.colors.textMuted, fontSize: theme.fontSize.sm }, children: "logs" })
589
- ] }),
590
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: theme.spacing.sm }, children: [
591
- /* @__PURE__ */ jsx6("div", { style: sectionLabelStyle, children: "Actions" }),
592
- /* @__PURE__ */ jsx6(
593
- "button",
594
- {
595
- "data-lt-interactive": "",
596
- onClick: paused ? onResume : onPause,
597
- style: {
598
- ...buttonStyle,
599
- background: paused ? theme.colors.levels.warning.badge : buttonStyle.background,
600
- color: paused ? theme.colors.white : buttonStyle.color
601
- },
602
- title: paused ? "Resume live updates" : "Pause live updates",
603
- type: "button",
604
- children: /* @__PURE__ */ jsxs4("span", { style: { alignItems: "center", display: "inline-flex", gap: "6px" }, children: [
605
- paused ? /* @__PURE__ */ jsx6(PlayIcon, { size: 12 }) : /* @__PURE__ */ jsx6(PauseIcon, { size: 12 }),
606
- paused ? "Resume" : "Pause"
607
- ] })
608
- }
609
- ),
610
- /* @__PURE__ */ jsx6(
611
- "button",
612
- {
613
- "data-lt-interactive": "",
614
- onClick: onClear,
615
- style: buttonStyle,
616
- title: "Clear all logs",
617
- type: "button",
618
- children: /* @__PURE__ */ jsxs4("span", { style: { alignItems: "center", display: "inline-flex", gap: "6px" }, children: [
619
- /* @__PURE__ */ jsx6(Trash2Icon, { size: 12 }),
620
- "Clear"
621
- ] })
622
- }
623
- )
624
- ] }),
625
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: theme.spacing.sm }, children: [
626
- /* @__PURE__ */ jsxs4("div", { style: { alignItems: "center", display: "flex", justifyContent: "space-between" }, children: [
627
- /* @__PURE__ */ jsx6("div", { style: sectionLabelStyle, children: "Levels" }),
628
- levelFilter.size > 0 && /* @__PURE__ */ jsx6(
629
- "button",
630
- {
631
- "data-lt-clear-btn": "",
632
- onClick: () => onLevelFilterChange(/* @__PURE__ */ new Set()),
633
- style: {
634
- background: "none",
635
- border: "none",
636
- color: theme.colors.textMuted,
637
- cursor: "pointer",
638
- fontSize: "9px",
639
- padding: 0,
640
- transition: "color 0.1s"
641
- },
642
- title: "Show all levels",
643
- type: "button",
644
- children: "clear"
645
- }
646
- )
647
- ] }),
648
- /* @__PURE__ */ jsx6(
649
- "div",
650
- {
651
- style: {
652
- display: "grid",
653
- gap: theme.spacing.xs,
654
- gridTemplateColumns: "repeat(3, 1fr)"
655
- },
656
- children: LOG_LEVELS.map((level) => /* @__PURE__ */ jsx6(
657
- LevelToggle,
658
- {
659
- active: levelFilter.size === 0 || levelFilter.has(level),
660
- level,
661
- onToggle: handleLevelToggle,
662
- pressed: levelFilter.has(level)
663
- },
664
- level
665
- ))
666
- }
667
- )
668
- ] }),
669
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: theme.spacing.sm }, children: [
670
- /* @__PURE__ */ jsxs4("div", { style: { alignItems: "center", display: "flex", justifyContent: "space-between" }, children: [
671
- /* @__PURE__ */ jsx6("div", { style: sectionLabelStyle, children: "Category" }),
672
- categoryFilter.length > 0 && /* @__PURE__ */ jsx6(
673
- "button",
674
- {
675
- "data-lt-clear-btn": "",
676
- onClick: () => onCategoryFilterChange([]),
677
- style: {
678
- background: "none",
679
- border: "none",
680
- color: theme.colors.textMuted,
681
- cursor: "pointer",
682
- fontSize: "9px",
683
- padding: 0,
684
- transition: "color 0.1s"
685
- },
686
- title: "Clear all categories",
687
- type: "button",
688
- children: "clear"
689
- }
690
- )
691
- ] }),
692
- categories.length === 0 ? /* @__PURE__ */ jsxs4(Popover.Root, { children: [
693
- /* @__PURE__ */ jsx6(
694
- Popover.Trigger,
695
- {
696
- delay: 300,
697
- nativeButton: false,
698
- openOnHover: true,
699
- render: /* @__PURE__ */ jsx6(
700
- "div",
701
- {
702
- "data-testid": "category-input",
703
- style: {
704
- ...inputStyle,
705
- color: theme.colors.textMuted,
706
- cursor: "not-allowed",
707
- opacity: 0.5
708
- },
709
- children: "Category\u2026"
710
- }
711
- )
712
- }
713
- ),
714
- /* @__PURE__ */ jsx6(Popover.Portal, { children: /* @__PURE__ */ jsx6(Popover.Positioner, { sideOffset: 6, style: { zIndex: 999999 }, children: /* @__PURE__ */ jsx6(
715
- Popover.Popup,
716
- {
717
- style: {
718
- background: theme.colors.surface,
719
- border: `1px solid ${theme.colors.border}`,
720
- borderRadius: theme.radius.md,
721
- boxShadow: "0 8px 24px rgba(0,0,0,0.5)",
722
- fontSize: theme.fontSize.sm,
723
- maxWidth: "200px",
724
- padding: `${theme.spacing.sm} ${theme.spacing.lg}`
725
- },
726
- children: /* @__PURE__ */ jsx6("p", { style: { color: theme.colors.textMuted, lineHeight: 1.4, margin: 0 }, children: "Waiting for logs to populate available categories." })
727
- }
728
- ) }) })
729
- ] }) : /* @__PURE__ */ jsxs4(
730
- Combobox.Root,
731
- {
732
- items: categories,
733
- multiple: true,
734
- onValueChange: onCategoryFilterChange,
735
- value: categoryFilter,
736
- children: [
737
- /* @__PURE__ */ jsxs4(
738
- "div",
739
- {
740
- style: {
741
- background: theme.colors.surface,
742
- border: `1px solid ${theme.colors.borderInput}`,
743
- borderRadius: theme.radius.md,
744
- display: "flex",
745
- flexWrap: "wrap",
746
- gap: "3px",
747
- minHeight: "28px",
748
- padding: "3px",
749
- transition: "border-color 0.15s"
750
- },
751
- children: [
752
- categoryFilter.map((cat) => /* @__PURE__ */ jsxs4(
753
- "span",
754
- {
755
- style: {
756
- alignItems: "center",
757
- background: "rgba(99, 102, 241, 0.15)",
758
- border: "1px solid rgba(99, 102, 241, 0.3)",
759
- borderRadius: theme.radius.sm,
760
- color: theme.colors.accentHover,
761
- display: "inline-flex",
762
- fontFamily: theme.fontFamily.mono,
763
- fontSize: "9px",
764
- gap: "3px",
765
- lineHeight: 1,
766
- padding: "1px 4px"
767
- },
768
- children: [
769
- cat,
770
- /* @__PURE__ */ jsx6(
771
- "button",
772
- {
773
- "data-lt-chip-remove": "",
774
- onClick: () => onCategoryFilterChange(categoryFilter.filter((c) => c !== cat)),
775
- style: {
776
- background: "none",
777
- border: "none",
778
- color: theme.colors.textMuted,
779
- cursor: "pointer",
780
- fontSize: "7px",
781
- lineHeight: 1,
782
- opacity: 0.7,
783
- padding: 0,
784
- transition: "color 0.1s, opacity 0.1s"
785
- },
786
- title: `Remove ${cat}`,
787
- type: "button",
788
- children: /* @__PURE__ */ jsx6(XIcon, { size: 7 })
789
- }
790
- )
791
- ]
792
- },
793
- cat
794
- )),
795
- /* @__PURE__ */ jsx6(
796
- Combobox.Input,
797
- {
798
- "aria-label": "Filter by category",
799
- "data-lt-input": "",
800
- "data-testid": "category-input",
801
- placeholder: categoryFilter.length === 0 ? "Category\u2026" : "",
802
- style: {
803
- background: "transparent",
804
- border: "none",
805
- color: theme.colors.textPrimary,
806
- flex: 1,
807
- fontFamily: theme.fontFamily.sans,
808
- fontSize: theme.fontSize.md,
809
- minWidth: "40px",
810
- outline: "none",
811
- padding: `${theme.spacing.xs} ${theme.spacing.sm}`
812
- }
813
- }
814
- )
815
- ]
816
- }
817
- ),
818
- /* @__PURE__ */ jsx6(Combobox.Portal, { children: /* @__PURE__ */ jsx6(Combobox.Positioner, { style: { zIndex: 999999 }, children: /* @__PURE__ */ jsx6(
819
- Combobox.Popup,
820
- {
821
- style: {
822
- background: theme.colors.surface,
823
- border: `1px solid ${theme.colors.border}`,
824
- borderRadius: theme.radius.md,
825
- boxShadow: "0 8px 24px rgba(0,0,0,0.5)",
826
- maxHeight: "200px",
827
- overflow: "auto",
828
- padding: theme.spacing.xs
829
- },
830
- children: /* @__PURE__ */ jsx6(Combobox.List, { children: (item, index) => /* @__PURE__ */ jsxs4(
831
- Combobox.Item,
832
- {
833
- index,
834
- style: {
835
- alignItems: "center",
836
- borderRadius: theme.radius.sm,
837
- color: theme.colors.textPrimary,
838
- cursor: "pointer",
839
- display: "flex",
840
- fontFamily: theme.fontFamily.mono,
841
- fontSize: theme.fontSize.md,
842
- gap: theme.spacing.sm,
843
- justifyContent: "space-between",
844
- padding: `${theme.spacing.sm} ${theme.spacing.lg}`,
845
- transition: "background 0.1s"
846
- },
847
- value: item,
848
- children: [
849
- /* @__PURE__ */ jsx6("span", { style: { flex: 1, overflow: "hidden", textOverflow: "ellipsis" }, children: String(item) }),
850
- /* @__PURE__ */ jsx6(Combobox.ItemIndicator, { children: /* @__PURE__ */ jsx6(CheckIcon, { color: theme.colors.accent, size: 14 }) })
851
- ]
852
- },
853
- String(item)
854
- ) })
855
- }
856
- ) }) })
857
- ]
858
- }
859
- )
860
- ] }),
861
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: theme.spacing.sm }, children: [
862
- /* @__PURE__ */ jsx6("div", { style: sectionLabelStyle, children: "Search" }),
863
- /* @__PURE__ */ jsx6(
864
- "input",
865
- {
866
- "aria-label": "Search logs",
867
- "data-lt-input": "",
868
- "data-testid": "search-input",
869
- onChange: handleSearchChange,
870
- placeholder: "Search\u2026",
871
- style: inputStyle,
872
- type: "text",
873
- value: search
874
- }
875
- )
876
- ] })
877
- ]
878
- }
879
- );
880
- };
881
-
882
- // src/plugin/logtape-devtools-plugin.tsx
883
- import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
884
- var rootStyle = {
885
- background: theme.colors.background,
886
- color: theme.colors.textPrimary,
887
- display: "flex",
888
- flexDirection: "row",
889
- fontFamily: theme.fontFamily.sans,
890
- height: "100%",
891
- overflow: "hidden"
892
- };
893
- var sidebarStyle = {
894
- flexShrink: 0,
895
- overflow: "hidden",
896
- transition: "width 0.2s ease, opacity 0.15s ease"
897
- };
898
- var mainStyle = {
899
- display: "flex",
900
- flex: 1,
901
- flexDirection: "column",
902
- overflow: "hidden"
903
- };
904
- var topBarStyle = {
905
- alignItems: "center",
906
- borderBottom: `1px solid ${theme.colors.border}`,
907
- display: "flex",
908
- flexShrink: 0,
909
- gap: theme.spacing.lg,
910
- padding: `${theme.spacing.sm} ${theme.spacing.lg}`
911
- };
912
- var sidebarToggleStyle = {
913
- background: "none",
914
- border: "none",
915
- color: theme.colors.textMuted,
916
- cursor: "pointer",
917
- fontFamily: theme.fontFamily.sans,
918
- fontSize: theme.fontSize.md,
919
- padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
920
- transition: "color 0.1s"
921
- };
922
- var iconLabelStyle = { alignItems: "center", display: "inline-flex", gap: "6px" };
923
- var countStyle = {
924
- color: theme.colors.textMuted,
925
- fontFamily: theme.fontFamily.mono,
926
- fontSize: theme.fontSize.sm
927
- };
928
- var compactButtonStyle = {
929
- background: theme.colors.surfaceHover,
930
- border: `1px solid ${theme.colors.border}`,
931
- borderRadius: theme.radius.sm,
932
- color: theme.colors.textPrimary,
933
- cursor: "pointer",
934
- fontFamily: theme.fontFamily.sans,
935
- fontSize: theme.fontSize.sm,
936
- padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
937
- transition: "filter 0.1s"
938
- };
939
- var LogTapeDevtoolsPlugin = ({ store }) => {
940
- const allRecords = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
941
- const [paused, setPaused] = useState2(false);
942
- const [pausedRecords, setPausedRecords] = useState2([]);
943
- const [levelFilter, setLevelFilter] = useState2(/* @__PURE__ */ new Set());
944
- const [categoryFilter, setCategoryFilter] = useState2([]);
945
- const [searchText, setSearchText] = useState2("");
946
- const [expandedId, setExpandedId] = useState2(null);
947
- const [sidebarOpen, setSidebarOpen] = useState2(true);
948
- const handlePause = useCallback4(() => {
949
- setPaused(true);
950
- setPausedRecords(store.getSnapshot());
951
- }, [store]);
952
- const handleResume = useCallback4(() => {
953
- setPaused(false);
954
- setPausedRecords([]);
955
- }, []);
956
- const handleClear = useCallback4(() => {
957
- store.clear();
958
- setPausedRecords([]);
959
- setExpandedId(null);
960
- }, [store]);
961
- const records = paused ? pausedRecords : allRecords;
962
- const uniqueCategories = useMemo2(() => {
963
- const seen = /* @__PURE__ */ new Set();
964
- for (const r of records) {
965
- seen.add(r.category.join("."));
966
- }
967
- return Array.from(seen).sort();
968
- }, [records]);
969
- const filteredRecords = useMemo2(
970
- () => records.filter((r) => {
971
- if (levelFilter.size > 0 && !levelFilter.has(r.level)) {
972
- return false;
973
- }
974
- if (categoryFilter.length > 0 && !categoryFilter.includes(r.category.join("."))) {
975
- return false;
976
- }
977
- if (searchText) {
978
- const term = searchText.toLowerCase();
979
- if (!(r.messageText.toLowerCase().includes(term) || r.category.join(".").toLowerCase().includes(term))) {
980
- return false;
981
- }
982
- }
983
- return true;
984
- }),
985
- [records, levelFilter, categoryFilter, searchText]
986
- );
987
- return /* @__PURE__ */ jsxs5("div", { style: rootStyle, children: [
988
- /* @__PURE__ */ jsx7(
989
- "div",
990
- {
991
- style: {
992
- ...sidebarStyle,
993
- borderRight: sidebarOpen ? `1px solid ${theme.colors.border}` : "none",
994
- opacity: sidebarOpen ? 1 : 0,
995
- width: sidebarOpen ? "200px" : "0px"
996
- },
997
- children: /* @__PURE__ */ jsx7(
998
- Toolbar,
999
- {
1000
- categories: uniqueCategories,
1001
- categoryFilter,
1002
- filteredCount: filteredRecords.length,
1003
- levelFilter,
1004
- onCategoryFilterChange: setCategoryFilter,
1005
- onClear: handleClear,
1006
- onLevelFilterChange: setLevelFilter,
1007
- onPause: handlePause,
1008
- onResume: handleResume,
1009
- onSearchTextChange: setSearchText,
1010
- paused,
1011
- totalCount: records.length
1012
- }
1013
- )
18
+ color: ${e.colors.white} !important;
1014
19
  }
1015
- ),
1016
- /* @__PURE__ */ jsxs5("div", { style: mainStyle, children: [
1017
- /* @__PURE__ */ jsxs5("div", { style: topBarStyle, children: [
1018
- /* @__PURE__ */ jsx7(
1019
- "button",
1020
- {
1021
- "aria-expanded": sidebarOpen,
1022
- "data-lt-clear-btn": "",
1023
- onClick: () => setSidebarOpen((v) => !v),
1024
- style: sidebarToggleStyle,
1025
- title: sidebarOpen ? "Collapse sidebar" : "Expand sidebar",
1026
- type: "button",
1027
- children: /* @__PURE__ */ jsxs5("span", { style: iconLabelStyle, children: [
1028
- sidebarOpen ? /* @__PURE__ */ jsx7(PanelLeftCloseIcon, { size: 14 }) : /* @__PURE__ */ jsx7(PanelLeftOpenIcon, { size: 14 }),
1029
- sidebarOpen ? "Hide controls" : "Show controls"
1030
- ] })
1031
- }
1032
- ),
1033
- !sidebarOpen && /* @__PURE__ */ jsxs5(Fragment, { children: [
1034
- /* @__PURE__ */ jsx7("span", { style: countStyle, children: filteredRecords.length === records.length ? `${records.length} logs` : `${filteredRecords.length} / ${records.length} logs` }),
1035
- /* @__PURE__ */ jsx7(
1036
- "button",
1037
- {
1038
- "data-lt-interactive": "",
1039
- onClick: paused ? handleResume : handlePause,
1040
- style: {
1041
- ...compactButtonStyle,
1042
- background: paused ? theme.colors.levels.warning.badge : compactButtonStyle.background,
1043
- color: paused ? theme.colors.white : compactButtonStyle.color
1044
- },
1045
- title: paused ? "Resume" : "Pause",
1046
- type: "button",
1047
- children: paused ? /* @__PURE__ */ jsx7(PlayIcon, { size: 12 }) : /* @__PURE__ */ jsx7(PauseIcon, { size: 12 })
1048
- }
1049
- ),
1050
- /* @__PURE__ */ jsx7(
1051
- "button",
1052
- {
1053
- "data-lt-interactive": "",
1054
- onClick: handleClear,
1055
- style: compactButtonStyle,
1056
- title: "Clear",
1057
- type: "button",
1058
- children: /* @__PURE__ */ jsx7(Trash2Icon, { size: 12 })
1059
- }
1060
- )
1061
- ] })
1062
- ] }),
1063
- /* @__PURE__ */ jsx7(
1064
- LogList,
1065
- {
1066
- autoScroll: !paused,
1067
- expandedId,
1068
- onToggle: setExpandedId,
1069
- records: filteredRecords
1070
- }
1071
- )
1072
- ] })
1073
- ] });
1074
- };
1075
-
1076
- // src/plugin/create-logtape-devtools-plugin.tsx
1077
- import { jsx as jsx8 } from "react/jsx-runtime";
1078
- var createLogTapeDevtoolsPlugin = (options) => ({
1079
- defaultOpen: options?.defaultOpen ?? true,
1080
- id: "logtape-devtools-plugin",
1081
- name: options?.name ?? "LogTape",
1082
- render: /* @__PURE__ */ jsx8(LogTapeDevtoolsPlugin, { store: options?.store ?? defaultLogStore })
1083
- });
1084
-
1085
- // src/create-logtape-devtools.ts
1086
- function createLogTapeDevtools(options) {
1087
- const store = options?.store ?? defaultLogStore;
1088
- return {
1089
- plugin: createLogTapeDevtoolsPlugin({ ...options?.plugin, store }),
1090
- sink: createDevtoolsSink({ ...options?.sink, store })
1091
- };
1092
- }
1093
- export {
1094
- LOG_LEVELS,
1095
- createDevtoolsSink,
1096
- createLogStore,
1097
- createLogTapeDevtools,
1098
- createLogTapeDevtoolsPlugin,
1099
- defaultLogStore
1100
- };
1101
- //# sourceMappingURL=index.mjs.map
20
+ `,document.head.appendChild(o)},[])}var _={background:e.colors.surfaceHover,border:`1px solid ${e.colors.border}`,borderRadius:e.radius.md,color:e.colors.textPrimary,cursor:"pointer",fontFamily:e.fontFamily.sans,fontSize:e.fontSize.md,padding:`${e.spacing.sm} ${e.spacing.lg}`,transition:"filter 0.1s, background 0.1s, border-color 0.1s",width:"100%"},xe={background:e.colors.surface,border:`1px solid ${e.colors.borderInput}`,borderRadius:e.radius.md,color:e.colors.textPrimary,fontFamily:e.fontFamily.sans,fontSize:e.fontSize.md,outline:"none",padding:`${e.spacing.sm} ${e.spacing.lg}`,transition:"border-color 0.15s",width:"100%"},N={color:e.colors.textMuted,fontSize:"9px",fontWeight:600,letterSpacing:"0.05em",textTransform:"uppercase"},oo=({level:o,active:t,pressed:l,onToggle:m})=>{let a=e.colors.levels[o],p=q(()=>m(o),[o,m]);return n("button",{"aria-pressed":l,"data-lt-interactive":"","data-testid":`level-toggle-${o}`,onClick:p,style:{background:t?a.badge:"transparent",border:`1px solid ${t?a.badge:e.colors.border}`,borderRadius:e.radius.sm,color:t?e.colors.white:a.color,cursor:"pointer",fontFamily:e.fontFamily.mono,fontSize:e.fontSize.sm,fontWeight:600,lineHeight:1,minWidth:"30px",opacity:t?1:.5,padding:"3px 0",textAlign:"center",transition:"all 0.15s"},title:`Filter ${o} logs`,type:"button",children:o.slice(0,3).toUpperCase()})},ve=({paused:o,onPause:t,onResume:l,onClear:m,levelFilter:a,onLevelFilterChange:p,categories:y,categoryFilter:i,onCategoryFilterChange:v,onSearchTextChange:h,totalCount:S,filteredCount:c})=>{eo();let[P,x]=Ze(""),z=Qe(()=>new Je(h,{wait:200}),[h]);be(()=>()=>z.cancel(),[z]);let U=q(r=>{let{value:g}=r.target;x(g),z.maybeExecute(g)},[z]),X=q(r=>{let g=new Set(a);g.has(r)?g.delete(r):g.add(r),p(g)},[a,p]);return d("div",{"data-testid":"toolbar",style:{display:"flex",flexDirection:"column",flexShrink:0,gap:e.spacing.xl,height:"100%",minWidth:"200px",overflow:"auto",padding:`${e.spacing.lg} ${e.spacing.xl}`},children:[d("div",{style:{textAlign:"center"},children:[n("span",{style:{color:e.colors.textPrimary,fontFamily:e.fontFamily.mono,fontSize:e.fontSize.lg,fontWeight:600},children:c===S?`${S}`:`${c} / ${S}`}),n("div",{style:{color:e.colors.textMuted,fontSize:e.fontSize.sm},children:"logs"})]}),d("div",{style:{display:"flex",flexDirection:"column",gap:e.spacing.sm},children:[n("div",{style:N,children:"Actions"}),n("button",{"data-lt-interactive":"",onClick:o?l:t,style:{..._,background:o?e.colors.levels.warning.badge:_.background,color:o?e.colors.white:_.color},title:o?"Resume live updates":"Pause live updates",type:"button",children:d("span",{style:{alignItems:"center",display:"inline-flex",gap:"6px"},children:[o?n(B,{size:12}):n(A,{size:12}),o?"Resume":"Pause"]})}),n("button",{"data-lt-interactive":"",onClick:m,style:_,title:"Clear all logs",type:"button",children:d("span",{style:{alignItems:"center",display:"inline-flex",gap:"6px"},children:[n(V,{size:12}),"Clear"]})})]}),d("div",{style:{display:"flex",flexDirection:"column",gap:e.spacing.sm},children:[d("div",{style:{alignItems:"center",display:"flex",justifyContent:"space-between"},children:[n("div",{style:N,children:"Levels"}),a.size>0&&n("button",{"data-lt-clear-btn":"",onClick:()=>p(new Set),style:{background:"none",border:"none",color:e.colors.textMuted,cursor:"pointer",fontSize:"9px",padding:0,transition:"color 0.1s"},title:"Show all levels",type:"button",children:"clear"})]}),n("div",{style:{display:"grid",gap:e.spacing.xs,gridTemplateColumns:"repeat(3, 1fr)"},children:Y.map(r=>n(oo,{active:a.size===0||a.has(r),level:r,onToggle:X,pressed:a.has(r)},r))})]}),d("div",{style:{display:"flex",flexDirection:"column",gap:e.spacing.sm},children:[d("div",{style:{alignItems:"center",display:"flex",justifyContent:"space-between"},children:[n("div",{style:N,children:"Category"}),i.length>0&&n("button",{"data-lt-clear-btn":"",onClick:()=>v([]),style:{background:"none",border:"none",color:e.colors.textMuted,cursor:"pointer",fontSize:"9px",padding:0,transition:"color 0.1s"},title:"Clear all categories",type:"button",children:"clear"})]}),y.length===0?d(H.Root,{children:[n(H.Trigger,{delay:300,nativeButton:!1,openOnHover:!0,render:n("div",{"data-testid":"category-input",style:{...xe,color:e.colors.textMuted,cursor:"not-allowed",opacity:.5},children:"Category\u2026"})}),n(H.Portal,{children:n(H.Positioner,{sideOffset:6,style:{zIndex:999999},children:n(H.Popup,{style:{background:e.colors.surface,border:`1px solid ${e.colors.border}`,borderRadius:e.radius.md,boxShadow:"0 8px 24px rgba(0,0,0,0.5)",fontSize:e.fontSize.sm,maxWidth:"200px",padding:`${e.spacing.sm} ${e.spacing.lg}`},children:n("p",{style:{color:e.colors.textMuted,lineHeight:1.4,margin:0},children:"Waiting for logs to populate available categories."})})})})]}):d(k.Root,{items:y,multiple:!0,onValueChange:v,value:i,children:[d("div",{style:{background:e.colors.surface,border:`1px solid ${e.colors.borderInput}`,borderRadius:e.radius.md,display:"flex",flexWrap:"wrap",gap:"3px",minHeight:"28px",padding:"3px",transition:"border-color 0.15s"},children:[i.map(r=>d("span",{style:{alignItems:"center",background:"rgba(99, 102, 241, 0.15)",border:"1px solid rgba(99, 102, 241, 0.3)",borderRadius:e.radius.sm,color:e.colors.accentHover,display:"inline-flex",fontFamily:e.fontFamily.mono,fontSize:"9px",gap:"3px",lineHeight:1,padding:"1px 4px"},children:[r,n("button",{"data-lt-chip-remove":"",onClick:()=>v(i.filter(g=>g!==r)),style:{background:"none",border:"none",color:e.colors.textMuted,cursor:"pointer",fontSize:"7px",lineHeight:1,opacity:.7,padding:0,transition:"color 0.1s, opacity 0.1s"},title:`Remove ${r}`,type:"button",children:n(ne,{size:7})})]},r)),n(k.Input,{"aria-label":"Filter by category","data-lt-input":"","data-testid":"category-input",placeholder:i.length===0?"Category\u2026":"",style:{background:"transparent",border:"none",color:e.colors.textPrimary,flex:1,fontFamily:e.fontFamily.sans,fontSize:e.fontSize.md,minWidth:"40px",outline:"none",padding:`${e.spacing.xs} ${e.spacing.sm}`}})]}),n(k.Portal,{children:n(k.Positioner,{style:{zIndex:999999},children:n(k.Popup,{style:{background:e.colors.surface,border:`1px solid ${e.colors.border}`,borderRadius:e.radius.md,boxShadow:"0 8px 24px rgba(0,0,0,0.5)",maxHeight:"200px",overflow:"auto",padding:e.spacing.xs},children:n(k.List,{children:(r,g)=>d(k.Item,{index:g,style:{alignItems:"center",borderRadius:e.radius.sm,color:e.colors.textPrimary,cursor:"pointer",display:"flex",fontFamily:e.fontFamily.mono,fontSize:e.fontSize.md,gap:e.spacing.sm,justifyContent:"space-between",padding:`${e.spacing.sm} ${e.spacing.lg}`,transition:"background 0.1s"},value:r,children:[n("span",{style:{flex:1,overflow:"hidden",textOverflow:"ellipsis"},children:String(r)}),n(k.ItemIndicator,{children:n(re,{color:e.colors.accent,size:14})})]},String(r))})})})})]})]}),d("div",{style:{display:"flex",flexDirection:"column",gap:e.spacing.sm},children:[n("div",{style:N,children:"Search"}),n("input",{"aria-label":"Search logs","data-lt-input":"","data-testid":"search-input",onChange:U,placeholder:"Search\u2026",style:xe,type:"text",value:P})]})]})};import{Fragment as po,jsx as u,jsxs as M}from"react/jsx-runtime";var no={background:e.colors.background,color:e.colors.textPrimary,display:"flex",flexDirection:"row",fontFamily:e.fontFamily.sans,height:"100%",overflow:"hidden"},ro={flexShrink:0,overflow:"hidden",transition:"width 0.2s ease, opacity 0.15s ease"},so={display:"flex",flex:1,flexDirection:"column",overflow:"hidden"},ao={alignItems:"center",borderBottom:`1px solid ${e.colors.border}`,display:"flex",flexShrink:0,gap:e.spacing.lg,padding:`${e.spacing.sm} ${e.spacing.lg}`},lo={background:"none",border:"none",color:e.colors.textMuted,cursor:"pointer",fontFamily:e.fontFamily.sans,fontSize:e.fontSize.md,padding:`${e.spacing.xs} ${e.spacing.sm}`,transition:"color 0.1s"},io={alignItems:"center",display:"inline-flex",gap:"6px"},co={color:e.colors.textMuted,fontFamily:e.fontFamily.mono,fontSize:e.fontSize.sm},G={background:e.colors.surfaceHover,border:`1px solid ${e.colors.border}`,borderRadius:e.radius.sm,color:e.colors.textPrimary,cursor:"pointer",fontFamily:e.fontFamily.sans,fontSize:e.fontSize.sm,padding:`${e.spacing.xs} ${e.spacing.sm}`,transition:"filter 0.1s"};function Se(o){return o.categoryKey??o.category.join(".")}var Le=({store:o})=>{let[t,l]=R(!1),[m,a]=R([]),[p,y]=R(new Set),[i,v]=R([]),[h,S]=R(""),[c,P]=R(null),[x,z]=R(!0),U=K(b=>t?()=>{}:o.subscribe(b),[o,t]),X=to(U,o.getSnapshot,o.getSnapshot),r=K(()=>{a(o.getSnapshot()),l(!0)},[o]),g=K(()=>{l(!1),a([])},[]),Q=K(()=>{o.clear(),a([]),P(null)},[o]),L=t?m:X,we=he(()=>{let b=new Set;for(let I of L)b.add(Se(I));return Array.from(b).sort()},[L]),E=he(()=>{let b=h?h.toLowerCase():"";return L.filter(I=>{if(p.size>0&&!p.has(I.level))return!1;let Z=Se(I);return!(i.length>0&&!i.includes(Z)||b&&!((I.messageSearchText??I.messageText.toLowerCase()).includes(b)||Z.toLowerCase().includes(b)))})},[L,p,i,h]);return M("div",{style:no,children:[u("div",{style:{...ro,borderRight:x?`1px solid ${e.colors.border}`:"none",opacity:x?1:0,width:x?"200px":"0px"},children:u(ve,{categories:we,categoryFilter:i,filteredCount:E.length,levelFilter:p,onCategoryFilterChange:v,onClear:Q,onLevelFilterChange:y,onPause:r,onResume:g,onSearchTextChange:S,paused:t,totalCount:L.length})}),M("div",{style:so,children:[M("div",{style:ao,children:[u("button",{"aria-expanded":x,"data-lt-clear-btn":"",onClick:()=>z(b=>!b),style:lo,title:x?"Collapse sidebar":"Expand sidebar",type:"button",children:M("span",{style:io,children:[x?u(ae,{size:14}):u(le,{size:14}),x?"Hide controls":"Show controls"]})}),!x&&M(po,{children:[u("span",{style:co,children:E.length===L.length?`${L.length} logs`:`${E.length} / ${L.length} logs`}),u("button",{"data-lt-interactive":"",onClick:t?g:r,style:{...G,background:t?e.colors.levels.warning.badge:G.background,color:t?e.colors.white:G.color},title:t?"Resume":"Pause",type:"button",children:t?u(B,{size:12}):u(A,{size:12})}),u("button",{"data-lt-interactive":"",onClick:Q,style:G,title:"Clear",type:"button",children:u(V,{size:12})})]})]}),u(ue,{autoScroll:!t,expandedId:c,onToggle:P,records:E})]})]})};import{jsx as go}from"react/jsx-runtime";var J=o=>({defaultOpen:o?.defaultOpen??!0,id:"logtape-devtools-plugin",name:o?.name??"LogTape",render:go(Le,{store:o?.store??F})});function mo(o){let t=o?.store??F;return{plugin:J({...o?.plugin,store:t}),sink:j({...o?.sink,store:t})}}export{Y as LOG_LEVELS,j as createDevtoolsSink,Ce as createLogStore,mo as createLogTapeDevtools,J as createLogTapeDevtoolsPlugin,F as defaultLogStore};