devlog-ui 0.1.0

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.cjs ADDED
@@ -0,0 +1,1721 @@
1
+ "use strict";var Oe=Object.defineProperty;var Fe=(t,e,n)=>e in t?Oe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var p=(t,e,n)=>Fe(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var Q=typeof document<"u"?document.currentScript:null;const de={debug:0,info:1,warn:2,error:3};function w(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function O(t,e){if(t===e)return!0;if(typeof t!=typeof e)return!1;if(t===null||e===null)return t===e;if(Array.isArray(t)&&Array.isArray(e))return t.length!==e.length?!1:t.every((n,o)=>O(n,e[o]));if(w(t)&&w(e)){const n=Object.keys(t),o=Object.keys(e);return n.length!==o.length?!1:n.every(r=>O(t[r],e[r]))}return!1}function ee(t){if(t===void 0)return"undefined";if(t===null)return"null";if(typeof t=="string")return`"${t}"`;if(typeof t=="number"||typeof t=="boolean")return String(t);if(Array.isArray(t))return t.length===0?"[]":t.length<=3?`[${t.map(ee).join(", ")}]`:`[${t.length} items]`;if(w(t)){const e=Object.keys(t);return e.length===0?"{}":e.length<=2?`{${e.map(n=>`${n}: ${ee(t[n])}`).join(", ")}}`:`{${e.length} keys}`}return String(t)}function oe(t,e,n="",o=!1){const r=[];if(!w(t)&&!w(e))return O(t,e)?o&&r.push({path:n||"(root)",type:"unchanged",oldValue:t,newValue:e}):r.push({path:n||"(root)",type:"changed",oldValue:t,newValue:e}),r;if(!w(t))return r.push({path:n||"(root)",type:"changed",oldValue:t,newValue:e}),r;if(!w(e))return r.push({path:n||"(root)",type:"changed",oldValue:t,newValue:e}),r;const s=new Set([...Object.keys(t),...Object.keys(e)]);for(const c of s){const l=n?`${n}.${c}`:c,a=t[c],d=e[c],u=c in t,h=c in e;!u&&h?r.push({path:l,type:"added",newValue:d}):u&&!h?r.push({path:l,type:"removed",oldValue:a}):w(a)&&w(d)?r.push(...oe(a,d,l,o)):Array.isArray(a)&&Array.isArray(d)?O(a,d)?o&&r.push({path:l,type:"unchanged",oldValue:a,newValue:d}):r.push({path:l,type:"changed",oldValue:a,newValue:d}):O(a,d)?o&&r.push({path:l,type:"unchanged",oldValue:a,newValue:d}):r.push({path:l,type:"changed",oldValue:a,newValue:d})}return r}function te(t,e){const n=oe(t,e,"",!1),o={added:0,removed:0,changed:0,unchanged:0};for(const r of n)o[r.type]++;return{changes:n,summary:o}}function Ae(t){return t.summary.added>0||t.summary.removed>0||t.summary.changed>0}const He={BASE_URL:"/",DEV:!1,MODE:"production",PROD:!0,SSR:!1};function Ne(){try{if(typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:Q&&Q.tagName.toUpperCase()==="SCRIPT"&&Q.src||new URL("index.cjs",document.baseURI).href}<"u"&&He)return!1;const t=globalThis.process;if(t?.env){const e=t.env.DEVLOGGER_ENABLED||t.env.REACT_APP_DEVLOGGER_ENABLED;if(e==="false"||e==="0")return!1;if(e==="true"||e==="1")return!0;if(t.env.NODE_ENV==="production")return!1}return!0}catch{return!0}}const ze={maxLogs:1e3,persist:!1,minLevel:"debug",enabled:Ne()};function Pe(){const t="devlogger_session_id";try{const e=sessionStorage.getItem(t);if(e)return e;const n=`session_${Date.now()}_${Math.random().toString(36).slice(2,9)}`;return sessionStorage.setItem(t,n),n}catch{return`session_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}}function qe(){return`log_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function ye(){try{const t=new Error().stack;if(!t)return{file:"unknown",line:0};const e=t.split(`
2
+ `);for(let n=4;n<e.length;n++){const o=e[n];if(!o||o.includes("logger.ts")||o.includes("logger.js"))continue;const r=o.match(/at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?/);if(r)return{function:r[1]||void 0,file:ge(r[2]||"unknown"),line:parseInt(r[3]||"0",10),column:parseInt(r[4]||"0",10)};const s=o.match(/(.+)?@(.+?):(\d+):(\d+)/);if(s)return{function:s[1]||void 0,file:ge(s[2]||"unknown"),line:parseInt(s[3]||"0",10),column:parseInt(s[4]||"0",10)}}return{file:"unknown",line:0}}catch{return{file:"unknown",line:0}}}function ge(t){let e=t.replace(/^webpack:\/\/[^/]*\//,"").replace(/^\/@fs/,"").replace(/^file:\/\//,"").replace(/\?.*$/,"");const n=e.split("/");return n.length>2&&(e=n.slice(-2).join("/")),e}function Ue(){return`span_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}class G{constructor(e,n,o,r){p(this,"logger");p(this,"_event");p(this,"_ended",!1);this.logger=e,this._event={id:Ue(),name:n,startTime:Date.now(),status:"running",parentId:r,context:o,source:ye(),sessionId:e.getSessionId()},e.notifySpan(this._event)}get id(){return this._event.id}get event(){return this._event}get ended(){return this._ended}debug(e,...n){this._ended||this.logger.logWithSpan("debug",e,n,this._event.id,this._event.context)}info(e,...n){this._ended||this.logger.logWithSpan("info",e,n,this._event.id,this._event.context)}warn(e,...n){this._ended||this.logger.logWithSpan("warn",e,n,this._event.id,this._event.context)}error(e,...n){this._ended||this.logger.logWithSpan("error",e,n,this._event.id,this._event.context)}span(e,n){const o={...this._event.context,...n};return new G(this.logger,e,o,this._event.id)}end(){this.finish("success")}fail(e){e&&this.error(typeof e=="string"?e:e.message,e),this.finish("error")}finish(e){this._ended||(this._ended=!0,this._event.endTime=Date.now(),this._event.duration=this._event.endTime-this._event.startTime,this._event.status=e,this.logger.notifySpan(this._event))}}class re{constructor(e,n){p(this,"logger");p(this,"context");this.logger=e,this.context=n}debug(e,...n){this.logger.logWithContext("debug",e,n,this.context)}info(e,...n){this.logger.logWithContext("info",e,n,this.context)}warn(e,...n){this.logger.logWithContext("warn",e,n,this.context)}error(e,...n){this.logger.logWithContext("error",e,n,this.context)}span(e,n){return this.logger.span(e,{...this.context,...n})}withContext(e){return new re(this.logger,{...this.context,...e})}}class Ve{constructor(){p(this,"logs",[]);p(this,"spans",new Map);p(this,"subscribers",new Set);p(this,"spanSubscribers",new Set);p(this,"config",{...ze});p(this,"sessionId");p(this,"globalContext",{});this.sessionId=Pe()}log(e,n,o,r,s){try{if(!this.config.enabled||de[e]<de[this.config.minLevel])return;const c=Object.keys(this.globalContext).length>0||r?{...this.globalContext,...r}:void 0,l={id:qe(),timestamp:Date.now(),level:e,message:String(n),data:this.safeCloneData(o),source:ye(),sessionId:this.sessionId,context:c,spanId:s};this.store(l),this.notify(l)}catch(c){typeof console<"u"&&console.warn&&console.warn("[DevLogger] Internal error:",c)}}logWithContext(e,n,o,r){this.log(e,n,o,r)}logWithSpan(e,n,o,r,s){this.log(e,n,o,s,r)}notifySpan(e){this.spans.set(e.id,e);for(const n of this.spanSubscribers)try{n(e)}catch{}}safeCloneData(e){return e.map(n=>this.safeClone(n))}safeClone(e,n=new WeakSet){if(e==null||typeof e!="object")return e;if(e instanceof Error)return{__type:"Error",name:e.name,message:e.message,stack:e.stack};if(e instanceof Date)return{__type:"Date",value:e.toISOString()};if(e instanceof RegExp)return{__type:"RegExp",value:e.toString()};if(n.has(e))return"[Circular Reference]";if(n.add(e),Array.isArray(e))return e.map(o=>this.safeClone(o,n));try{const o={};for(const r of Object.keys(e))o[r]=this.safeClone(e[r],n);return o}catch{return"[Uncloneable Object]"}}store(e){for(this.logs.push(e);this.logs.length>this.config.maxLogs;)this.logs.shift()}notify(e){for(const n of this.subscribers)try{n(e)}catch{}}info(e,...n){this.log("info",e,n)}warn(e,...n){this.log("warn",e,n)}error(e,...n){this.log("error",e,n)}debug(e,...n){this.log("debug",e,n)}configure(e){try{this.config={...this.config,...e}}catch{}}clear(){try{this.logs=[],this.spans.clear()}catch{}}importLogs(e){try{if(!Array.isArray(e)||e.length===0)return;const n=new Set(this.logs.map(r=>r.id)),o=e.filter(r=>!n.has(r.id));for(this.logs=[...o,...this.logs];this.logs.length>this.config.maxLogs;)this.logs.shift()}catch{}}getLogs(){return this.logs}subscribe(e){try{return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}catch{return()=>{}}}getSessionId(){return this.sessionId}getConfig(){return{...this.config}}isEnabled(){return this.config.enabled}span(e,n){try{return new G(this,e,n)}catch{return new G(this,e,n)}}getSpans(){return Array.from(this.spans.values())}getSpan(e){return this.spans.get(e)}getSpanLogs(e){return this.logs.filter(n=>n.spanId===e)}subscribeSpans(e){try{return this.spanSubscribers.add(e),()=>{this.spanSubscribers.delete(e)}}catch{return()=>{}}}setGlobalContext(e){try{this.globalContext={...e}}catch{}}updateGlobalContext(e){try{this.globalContext={...this.globalContext,...e}}catch{}}getGlobalContext(){return{...this.globalContext}}clearGlobalContext(){this.globalContext={}}withContext(e){return new re(this,e)}exportLogs(e={}){try{const{format:n="json",lastMs:o,levels:r,search:s,pretty:c=!0}=e;let l=[...this.logs];if(o!==void 0&&o>0){const a=Date.now()-o;l=l.filter(d=>d.timestamp>=a)}if(r&&r.length>0){const a=new Set(r);l=l.filter(d=>a.has(d.level))}if(s){const a=s.toLowerCase();l=l.filter(d=>d.message.toLowerCase().includes(a)||JSON.stringify(d.data).toLowerCase().includes(a))}return n==="text"?this.formatLogsAsText(l):c?JSON.stringify(l,null,2):JSON.stringify(l)}catch{return"[]"}}formatLogsAsText(e){return e.map(n=>{const o=new Date(n.timestamp).toISOString(),r=n.level.toUpperCase().padEnd(5),s=`${n.source.file}:${n.source.line}`,c=n.context?` [${Object.entries(n.context).map(([d,u])=>`${d}=${u}`).join(", ")}]`:"",l=n.spanId?` (span: ${n.spanId})`:"",a=n.data.length>0?`
3
+ Data: ${JSON.stringify(n.data)}`:"";return`[${o}] ${r} ${n.message}${c}${l}
4
+ Source: ${s}${a}`}).join(`
5
+
6
+ `)}async copyLogs(e={}){try{const n=this.exportLogs(e);return await navigator.clipboard.writeText(n),!0}catch{return!1}}diff(e,n,o,r="info"){try{const s=te(n,o);return this.log(r,e,[{__type:"Diff",diff:s,oldObj:this.safeClone(n),newObj:this.safeClone(o)}]),s}catch{return{changes:[],summary:{added:0,removed:0,changed:0,unchanged:0}}}}computeDiff(e,n){try{return te(e,n)}catch{return{changes:[],summary:{added:0,removed:0,changed:0,unchanged:0}}}}}const f=new Ve,g={bgPrimary:"#1e1e1e",bgSecondary:"#252526",bgHover:"#2a2a2a",bgHeader:"#333333",textPrimary:"#cccccc",textSecondary:"#858585",textMuted:"#6e6e6e",levelDebug:"#6e6e6e",levelInfo:"#3794ff",levelWarn:"#cca700",levelError:"#f14c4c",border:"#3c3c3c",scrollbar:"#4a4a4a",scrollbarHover:"#5a5a5a",buttonBg:"#0e639c",buttonHover:"#1177bb"},je=`
7
+ :host {
8
+ --bg-primary: ${g.bgPrimary};
9
+ --bg-secondary: ${g.bgSecondary};
10
+ --bg-hover: ${g.bgHover};
11
+ --bg-header: ${g.bgHeader};
12
+ --text-primary: ${g.textPrimary};
13
+ --text-secondary: ${g.textSecondary};
14
+ --text-muted: ${g.textMuted};
15
+ --border: ${g.border};
16
+
17
+ all: initial;
18
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
19
+ font-size: 12px;
20
+ line-height: 1.4;
21
+ color: var(--text-primary);
22
+ }
23
+
24
+ * {
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ /* Container */
29
+ .devlogger-container {
30
+ position: fixed;
31
+ top: 0;
32
+ right: 0;
33
+ width: 420px;
34
+ height: 100vh;
35
+ background: var(--bg-primary);
36
+ border-left: 1px solid var(--border);
37
+ display: flex;
38
+ flex-direction: column;
39
+ z-index: 99999;
40
+ box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);
41
+ }
42
+
43
+ .devlogger-container.hidden {
44
+ display: none;
45
+ }
46
+
47
+ /* Header */
48
+ .devlogger-header {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: space-between;
52
+ padding: 8px 12px;
53
+ background: var(--bg-header);
54
+ border-bottom: 1px solid var(--border);
55
+ flex-shrink: 0;
56
+ }
57
+
58
+ .devlogger-title {
59
+ font-weight: 600;
60
+ font-size: 13px;
61
+ color: var(--text-primary);
62
+ display: flex;
63
+ align-items: center;
64
+ gap: 8px;
65
+ }
66
+
67
+ .devlogger-badge {
68
+ background: ${g.levelInfo};
69
+ color: white;
70
+ padding: 2px 6px;
71
+ border-radius: 10px;
72
+ font-size: 10px;
73
+ font-weight: 500;
74
+ }
75
+
76
+ .devlogger-actions {
77
+ display: flex;
78
+ gap: 4px;
79
+ }
80
+
81
+ .devlogger-btn {
82
+ background: transparent;
83
+ border: none;
84
+ color: var(--text-secondary);
85
+ cursor: pointer;
86
+ padding: 4px 8px;
87
+ border-radius: 4px;
88
+ font-size: 12px;
89
+ transition: all 0.15s ease;
90
+ }
91
+
92
+ .devlogger-btn:hover {
93
+ background: var(--bg-hover);
94
+ color: var(--text-primary);
95
+ }
96
+
97
+ .devlogger-btn-primary {
98
+ background: ${g.buttonBg};
99
+ color: white;
100
+ }
101
+
102
+ .devlogger-btn-primary:hover {
103
+ background: ${g.buttonHover};
104
+ color: white;
105
+ }
106
+
107
+ /* Log List */
108
+ .devlogger-logs {
109
+ flex: 1;
110
+ overflow-y: auto;
111
+ overflow-x: hidden;
112
+ }
113
+
114
+ .devlogger-logs::-webkit-scrollbar {
115
+ width: 8px;
116
+ }
117
+
118
+ .devlogger-logs::-webkit-scrollbar-track {
119
+ background: var(--bg-primary);
120
+ }
121
+
122
+ .devlogger-logs::-webkit-scrollbar-thumb {
123
+ background: ${g.scrollbar};
124
+ border-radius: 4px;
125
+ }
126
+
127
+ .devlogger-logs::-webkit-scrollbar-thumb:hover {
128
+ background: ${g.scrollbarHover};
129
+ }
130
+
131
+ .devlogger-empty {
132
+ display: flex;
133
+ align-items: center;
134
+ justify-content: center;
135
+ height: 100%;
136
+ color: var(--text-muted);
137
+ font-style: italic;
138
+ }
139
+
140
+ /* Log Entry */
141
+ .log-entry {
142
+ border-bottom: 1px solid var(--border);
143
+ padding: 8px 12px;
144
+ transition: background 0.1s ease;
145
+ }
146
+
147
+ .log-entry:hover {
148
+ background: var(--bg-hover);
149
+ }
150
+
151
+ .log-entry-header {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 8px;
155
+ margin-bottom: 4px;
156
+ }
157
+
158
+ .log-level {
159
+ font-weight: 600;
160
+ font-size: 10px;
161
+ text-transform: uppercase;
162
+ padding: 2px 6px;
163
+ border-radius: 3px;
164
+ min-width: 45px;
165
+ text-align: center;
166
+ }
167
+
168
+ .log-level-debug {
169
+ background: rgba(110, 110, 110, 0.2);
170
+ color: ${g.levelDebug};
171
+ }
172
+
173
+ .log-level-info {
174
+ background: rgba(55, 148, 255, 0.15);
175
+ color: ${g.levelInfo};
176
+ }
177
+
178
+ .log-level-warn {
179
+ background: rgba(204, 167, 0, 0.15);
180
+ color: ${g.levelWarn};
181
+ }
182
+
183
+ .log-level-error {
184
+ background: rgba(241, 76, 76, 0.15);
185
+ color: ${g.levelError};
186
+ }
187
+
188
+ .log-time {
189
+ color: var(--text-muted);
190
+ font-size: 11px;
191
+ }
192
+
193
+ .log-source {
194
+ color: var(--text-secondary);
195
+ font-size: 11px;
196
+ margin-left: auto;
197
+ max-width: 150px;
198
+ overflow: hidden;
199
+ text-overflow: ellipsis;
200
+ white-space: nowrap;
201
+ }
202
+
203
+ .log-source:hover {
204
+ color: ${g.levelInfo};
205
+ }
206
+
207
+ .log-context {
208
+ font-size: 10px;
209
+ color: ${g.levelInfo};
210
+ background: rgba(55, 148, 255, 0.1);
211
+ padding: 2px 6px;
212
+ border-radius: 3px;
213
+ max-width: 150px;
214
+ overflow: hidden;
215
+ text-overflow: ellipsis;
216
+ white-space: nowrap;
217
+ }
218
+
219
+ .log-entry-in-span {
220
+ border-left: 2px solid ${g.levelInfo};
221
+ padding-left: 10px;
222
+ }
223
+
224
+ .log-message {
225
+ color: var(--text-primary);
226
+ word-break: break-word;
227
+ margin-bottom: 4px;
228
+ }
229
+
230
+ /* Data Display */
231
+ .log-data {
232
+ margin-top: 6px;
233
+ }
234
+
235
+ .log-data-toggle {
236
+ background: transparent;
237
+ border: none;
238
+ color: var(--text-secondary);
239
+ cursor: pointer;
240
+ padding: 2px 0;
241
+ font-size: 11px;
242
+ display: flex;
243
+ align-items: center;
244
+ gap: 4px;
245
+ }
246
+
247
+ .log-data-toggle:hover {
248
+ color: var(--text-primary);
249
+ }
250
+
251
+ .log-data-toggle::before {
252
+ content: '▶';
253
+ font-size: 8px;
254
+ transition: transform 0.15s ease;
255
+ }
256
+
257
+ .log-data-toggle.expanded::before {
258
+ transform: rotate(90deg);
259
+ }
260
+
261
+ .log-data-content {
262
+ display: none;
263
+ margin-top: 6px;
264
+ padding: 8px;
265
+ background: var(--bg-secondary);
266
+ border-radius: 4px;
267
+ font-size: 11px;
268
+ overflow-x: auto;
269
+ white-space: pre-wrap;
270
+ word-break: break-all;
271
+ }
272
+
273
+ .log-data-content.visible {
274
+ display: block;
275
+ }
276
+
277
+ /* Toggle Button (floating) */
278
+ .devlogger-toggle {
279
+ position: fixed;
280
+ bottom: 20px;
281
+ right: 20px;
282
+ width: 48px;
283
+ height: 48px;
284
+ border-radius: 50%;
285
+ background: ${g.buttonBg};
286
+ color: white;
287
+ border: none;
288
+ cursor: pointer;
289
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
290
+ font-size: 20px;
291
+ display: flex;
292
+ align-items: center;
293
+ justify-content: center;
294
+ transition: all 0.2s ease;
295
+ z-index: 99998;
296
+ }
297
+
298
+ .devlogger-toggle:hover {
299
+ background: ${g.buttonHover};
300
+ transform: scale(1.05);
301
+ }
302
+
303
+ .devlogger-toggle.hidden {
304
+ display: none;
305
+ }
306
+
307
+ /* Footer / Status */
308
+ .devlogger-footer {
309
+ padding: 6px 12px;
310
+ background: var(--bg-header);
311
+ border-top: 1px solid var(--border);
312
+ font-size: 11px;
313
+ color: var(--text-muted);
314
+ display: flex;
315
+ justify-content: space-between;
316
+ flex-shrink: 0;
317
+ }
318
+
319
+ .devlogger-shortcut {
320
+ opacity: 0.7;
321
+ }
322
+
323
+ /* Filter Bar */
324
+ .filter-bar {
325
+ padding: 8px 12px;
326
+ background: var(--bg-secondary);
327
+ border-bottom: 1px solid var(--border);
328
+ flex-shrink: 0;
329
+ }
330
+
331
+ .filter-bar.filter-active {
332
+ background: rgba(55, 148, 255, 0.08);
333
+ border-bottom-color: ${g.levelInfo};
334
+ }
335
+
336
+ .filter-row {
337
+ display: flex;
338
+ align-items: center;
339
+ gap: 8px;
340
+ }
341
+
342
+ .filter-levels {
343
+ display: flex;
344
+ gap: 2px;
345
+ }
346
+
347
+ .filter-level-btn {
348
+ width: 24px;
349
+ height: 24px;
350
+ border: 1px solid var(--border);
351
+ border-radius: 4px;
352
+ background: transparent;
353
+ color: var(--text-muted);
354
+ cursor: pointer;
355
+ font-size: 10px;
356
+ font-weight: 600;
357
+ transition: all 0.15s ease;
358
+ }
359
+
360
+ .filter-level-btn:hover {
361
+ background: var(--bg-hover);
362
+ color: var(--text-primary);
363
+ }
364
+
365
+ .filter-level-btn.active {
366
+ color: white;
367
+ }
368
+
369
+ .filter-level-btn.active[data-level="debug"] {
370
+ background: ${g.levelDebug};
371
+ border-color: ${g.levelDebug};
372
+ }
373
+
374
+ .filter-level-btn.active[data-level="info"] {
375
+ background: ${g.levelInfo};
376
+ border-color: ${g.levelInfo};
377
+ }
378
+
379
+ .filter-level-btn.active[data-level="warn"] {
380
+ background: ${g.levelWarn};
381
+ border-color: ${g.levelWarn};
382
+ }
383
+
384
+ .filter-level-btn.active[data-level="error"] {
385
+ background: ${g.levelError};
386
+ border-color: ${g.levelError};
387
+ }
388
+
389
+ .filter-search {
390
+ flex: 1;
391
+ }
392
+
393
+ .filter-file {
394
+ width: 120px;
395
+ }
396
+
397
+ .filter-input {
398
+ width: 100%;
399
+ padding: 4px 8px;
400
+ border: 1px solid var(--border);
401
+ border-radius: 4px;
402
+ background: var(--bg-primary);
403
+ color: var(--text-primary);
404
+ font-size: 11px;
405
+ font-family: inherit;
406
+ outline: none;
407
+ transition: border-color 0.15s ease;
408
+ }
409
+
410
+ .filter-input::placeholder {
411
+ color: var(--text-muted);
412
+ }
413
+
414
+ .filter-input:focus {
415
+ border-color: ${g.levelInfo};
416
+ }
417
+
418
+ .filter-clear-btn {
419
+ width: 24px;
420
+ height: 24px;
421
+ border: none;
422
+ border-radius: 4px;
423
+ background: rgba(241, 76, 76, 0.2);
424
+ color: ${g.levelError};
425
+ cursor: pointer;
426
+ font-size: 12px;
427
+ transition: all 0.15s ease;
428
+ }
429
+
430
+ .filter-clear-btn:hover {
431
+ background: ${g.levelError};
432
+ color: white;
433
+ }
434
+
435
+ .filter-status {
436
+ margin-top: 6px;
437
+ font-size: 10px;
438
+ color: ${g.levelInfo};
439
+ }
440
+
441
+ /* No results state */
442
+ .devlogger-no-results {
443
+ display: flex;
444
+ flex-direction: column;
445
+ align-items: center;
446
+ justify-content: center;
447
+ height: 100%;
448
+ color: var(--text-muted);
449
+ gap: 8px;
450
+ }
451
+
452
+ .devlogger-no-results-icon {
453
+ font-size: 24px;
454
+ opacity: 0.5;
455
+ }
456
+
457
+ .devlogger-no-results-text {
458
+ font-style: italic;
459
+ }
460
+
461
+ /* Diff Display */
462
+ .diff-container {
463
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
464
+ font-size: 11px;
465
+ }
466
+
467
+ .diff-summary {
468
+ display: flex;
469
+ gap: 8px;
470
+ margin-bottom: 8px;
471
+ padding-bottom: 8px;
472
+ border-bottom: 1px solid var(--border);
473
+ }
474
+
475
+ .diff-count {
476
+ padding: 2px 6px;
477
+ border-radius: 3px;
478
+ font-weight: 600;
479
+ }
480
+
481
+ .diff-count.diff-added {
482
+ background: rgba(80, 200, 120, 0.2);
483
+ color: #50c878;
484
+ }
485
+
486
+ .diff-count.diff-removed {
487
+ background: rgba(241, 76, 76, 0.2);
488
+ color: ${g.levelError};
489
+ }
490
+
491
+ .diff-count.diff-changed {
492
+ background: rgba(204, 167, 0, 0.2);
493
+ color: ${g.levelWarn};
494
+ }
495
+
496
+ .diff-count.diff-unchanged {
497
+ background: var(--bg-hover);
498
+ color: var(--text-muted);
499
+ }
500
+
501
+ .diff-changes {
502
+ display: flex;
503
+ flex-direction: column;
504
+ gap: 2px;
505
+ }
506
+
507
+ .diff-entry {
508
+ padding: 2px 6px;
509
+ border-radius: 3px;
510
+ display: flex;
511
+ gap: 6px;
512
+ }
513
+
514
+ .diff-entry.diff-added {
515
+ background: rgba(80, 200, 120, 0.1);
516
+ color: #50c878;
517
+ }
518
+
519
+ .diff-entry.diff-removed {
520
+ background: rgba(241, 76, 76, 0.1);
521
+ color: ${g.levelError};
522
+ }
523
+
524
+ .diff-entry.diff-changed {
525
+ background: rgba(204, 167, 0, 0.1);
526
+ color: ${g.levelWarn};
527
+ }
528
+
529
+ .diff-icon {
530
+ font-weight: bold;
531
+ width: 12px;
532
+ flex-shrink: 0;
533
+ }
534
+
535
+ .diff-path {
536
+ color: var(--text-secondary);
537
+ flex-shrink: 0;
538
+ }
539
+
540
+ .diff-value {
541
+ word-break: break-all;
542
+ }
543
+
544
+ .log-data-diff .log-data-toggle {
545
+ color: ${g.levelInfo};
546
+ }
547
+ `;function We(t){const e=new Date(t),n=e.getHours().toString().padStart(2,"0"),o=e.getMinutes().toString().padStart(2,"0"),r=e.getSeconds().toString().padStart(2,"0"),s=e.getMilliseconds().toString().padStart(3,"0");return`${n}:${o}:${r}.${s}`}function ue(t){return t.file==="unknown"?"unknown":`${t.file}:${t.line}`}function xe(t){return t!==null&&typeof t=="object"&&t.__type==="Diff"&&t.diff!==void 0}function $(t){if(t===void 0)return"undefined";if(t===null)return"null";if(typeof t=="string")return`"${t}"`;if(typeof t=="number"||typeof t=="boolean")return String(t);if(Array.isArray(t))return t.length===0?"[]":t.length<=3?`[${t.map($).join(", ")}]`:`[${t.length} items]`;if(typeof t=="object"){const e=Object.keys(t);return e.length===0?"{}":e.length<=2?`{${e.map(n=>`${n}: ${$(t[n])}`).join(", ")}}`:`{${e.length} keys}`}return String(t)}function Ge(t){const e=`diff-${t.type}`,n=t.type==="added"?"+":t.type==="removed"?"-":t.type==="changed"?"~":" ";let o="";return t.type==="added"?o=$(t.newValue):t.type==="removed"?o=$(t.oldValue):t.type==="changed"&&(o=`${$(t.oldValue)} → ${$(t.newValue)}`),`<div class="diff-entry ${e}"><span class="diff-icon">${n}</span> <span class="diff-path">${t.path}</span>: <span class="diff-value">${o}</span></div>`}function Je(t){const{diff:e}=t,{summary:n,changes:o}=e;let r='<div class="diff-container">';if(r+='<div class="diff-summary">',n.added>0&&(r+=`<span class="diff-count diff-added">+${n.added}</span>`),n.removed>0&&(r+=`<span class="diff-count diff-removed">-${n.removed}</span>`),n.changed>0&&(r+=`<span class="diff-count diff-changed">~${n.changed}</span>`),n.added===0&&n.removed===0&&n.changed===0&&(r+='<span class="diff-count diff-unchanged">No changes</span>'),r+="</div>",o.length>0){r+='<div class="diff-changes">';for(const s of o)r+=Ge(s);r+="</div>"}return r+="</div>",r}function Xe(t){if(t.length===0)return"";try{return t.map(e=>typeof e=="string"?e:JSON.stringify(e,null,2)).join(`
548
+ `)}catch{return"[Unable to display data]"}}function Ye(t){return t.some(xe)}function M(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function Ke(t){return!t||Object.keys(t).length===0?"":Object.entries(t).map(([e,n])=>`${e}=${n}`).join(" · ")}function we(t){const e=document.createElement("div");e.className=`log-entry${t.spanId?" log-entry-in-span":""}`,e.dataset.id=t.id,t.spanId&&(e.dataset.spanId=t.spanId);const n=t.data.length>0,o=Ye(t.data),r=t.context&&Object.keys(t.context).length>0,s=`data-${t.id}`,c=()=>{if(!n)return"";if(o){const l=t.data.find(xe);if(l)return`
549
+ <div class="log-data log-data-diff">
550
+ <button class="log-data-toggle" data-target="${s}">
551
+ diff
552
+ </button>
553
+ <div class="log-data-content" id="${s}">${Je(l)}</div>
554
+ </div>
555
+ `}return`
556
+ <div class="log-data">
557
+ <button class="log-data-toggle" data-target="${s}">
558
+ ${t.data.length} item${t.data.length>1?"s":""}
559
+ </button>
560
+ <pre class="log-data-content" id="${s}">${M(Xe(t.data))}</pre>
561
+ </div>
562
+ `};if(e.innerHTML=`
563
+ <div class="log-entry-header">
564
+ <span class="log-level log-level-${t.level}">${t.level}</span>
565
+ <span class="log-time">${We(t.timestamp)}</span>
566
+ ${r?`<span class="log-context" title="Context">${M(Ke(t.context))}</span>`:""}
567
+ <span class="log-source" title="${M(ue(t.source))}">${M(ue(t.source))}</span>
568
+ </div>
569
+ <div class="log-message">${M(t.message)}</div>
570
+ ${c()}
571
+ `,n){const l=e.querySelector(".log-data-toggle"),a=e.querySelector(`#${s}`);l&&a&&l.addEventListener("click",()=>{l.classList.toggle("expanded"),a.classList.toggle("visible")})}return e}function Qe(){const t=document.createElement("div");return t.className="devlogger-empty",t.textContent="No logs yet...",t}const Ze="devlogger-sync";function et(){return`sender_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}class tt{constructor(){p(this,"channel",null);p(this,"handlers",new Set);p(this,"senderId");p(this,"isConnected",!1);this.senderId=et(),this.connect()}connect(){try{if(typeof BroadcastChannel>"u"){console.warn("[DevLogger] BroadcastChannel not supported");return}this.channel=new BroadcastChannel(Ze),this.channel.onmessage=e=>{this.handleMessage(e.data)},this.channel.onmessageerror=()=>{console.warn("[DevLogger] Channel message error")},this.isConnected=!0}catch(e){console.warn("[DevLogger] Failed to connect to channel:",e),this.isConnected=!1}}handleMessage(e){if(e.senderId!==this.senderId)for(const n of this.handlers)try{n(e)}catch{}}send(e,n){try{if(!this.channel||!this.isConnected)return;const o={type:e,payload:n,senderId:this.senderId,timestamp:Date.now()};this.channel.postMessage(o)}catch{}}sendLog(e){this.send("NEW_LOG",e)}requestSync(){this.send("SYNC_REQUEST")}sendSyncResponse(e){this.send("SYNC_RESPONSE",e)}sendClear(){this.send("CLEAR_LOGS")}subscribe(e){return this.handlers.add(e),()=>{this.handlers.delete(e)}}isActive(){return this.isConnected}getSenderId(){return this.senderId}close(){try{this.channel&&(this.channel.close(),this.channel=null),this.handlers.clear(),this.isConnected=!1}catch{}}}const N=new tt,fe=500,pe=700;let b=null;function nt(){try{if(b&&!b.closed)return b.focus(),b;const t=Math.max(0,(screen.width-fe)/2),e=Math.max(0,(screen.height-pe)/2),n=rt();return b=window.open("","devlogger-popout",`width=${fe},height=${pe},left=${t},top=${e},resizable=yes,scrollbars=yes`),b?(b.document.open(),b.document.write(n),b.document.close(),b.addEventListener("load",()=>{setTimeout(()=>{N.sendSyncResponse(f.getLogs())},100)}),b):(console.warn("[DevLogger] Pop-out blocked by browser"),null)}catch(t){return console.warn("[DevLogger] Failed to open pop-out:",t),null}}function he(){try{b&&!b.closed&&b.close(),b=null}catch{}}function ot(){return b!==null&&!b.closed}function rt(){return`<!DOCTYPE html>
572
+ <html lang="en">
573
+ <head>
574
+ <meta charset="UTF-8">
575
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
576
+ <title>DevLogger - Pop-out</title>
577
+ <style>
578
+ :root {
579
+ --bg-primary: #1e1e1e;
580
+ --bg-secondary: #252526;
581
+ --bg-hover: #2a2a2a;
582
+ --bg-header: #333333;
583
+ --text-primary: #cccccc;
584
+ --text-secondary: #858585;
585
+ --text-muted: #6e6e6e;
586
+ --border: #3c3c3c;
587
+ --level-debug: #6e6e6e;
588
+ --level-info: #3794ff;
589
+ --level-warn: #cca700;
590
+ --level-error: #f14c4c;
591
+ --button-bg: #0e639c;
592
+ --button-hover: #1177bb;
593
+ }
594
+
595
+ * {
596
+ box-sizing: border-box;
597
+ margin: 0;
598
+ padding: 0;
599
+ }
600
+
601
+ body {
602
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
603
+ font-size: 12px;
604
+ line-height: 1.4;
605
+ background: var(--bg-primary);
606
+ color: var(--text-primary);
607
+ height: 100vh;
608
+ display: flex;
609
+ flex-direction: column;
610
+ }
611
+
612
+ .header {
613
+ display: flex;
614
+ align-items: center;
615
+ justify-content: space-between;
616
+ padding: 12px 16px;
617
+ background: var(--bg-header);
618
+ border-bottom: 1px solid var(--border);
619
+ }
620
+
621
+ .title {
622
+ font-weight: 600;
623
+ font-size: 14px;
624
+ display: flex;
625
+ align-items: center;
626
+ gap: 8px;
627
+ }
628
+
629
+ .badge {
630
+ background: var(--level-info);
631
+ color: white;
632
+ padding: 2px 8px;
633
+ border-radius: 10px;
634
+ font-size: 11px;
635
+ }
636
+
637
+ .status {
638
+ display: flex;
639
+ align-items: center;
640
+ gap: 6px;
641
+ font-size: 11px;
642
+ color: var(--text-muted);
643
+ }
644
+
645
+ .status-dot {
646
+ width: 8px;
647
+ height: 8px;
648
+ border-radius: 50%;
649
+ background: #4caf50;
650
+ }
651
+
652
+ .status-dot.disconnected {
653
+ background: var(--level-error);
654
+ }
655
+
656
+ .actions {
657
+ display: flex;
658
+ gap: 8px;
659
+ }
660
+
661
+ .btn {
662
+ background: transparent;
663
+ border: 1px solid var(--border);
664
+ color: var(--text-secondary);
665
+ padding: 6px 12px;
666
+ border-radius: 4px;
667
+ cursor: pointer;
668
+ font-size: 12px;
669
+ transition: all 0.15s ease;
670
+ }
671
+
672
+ .btn:hover {
673
+ background: var(--bg-hover);
674
+ color: var(--text-primary);
675
+ }
676
+
677
+ .btn-copy {
678
+ font-size: 10px;
679
+ padding: 4px 8px;
680
+ }
681
+
682
+ .btn-copy.success {
683
+ background: #4caf50;
684
+ border-color: #4caf50;
685
+ color: white;
686
+ }
687
+
688
+ .btn-copy.error {
689
+ background: var(--level-error);
690
+ border-color: var(--level-error);
691
+ color: white;
692
+ }
693
+
694
+ .btn-timeline {
695
+ font-size: 10px;
696
+ padding: 4px 8px;
697
+ }
698
+
699
+ .btn-timeline.active {
700
+ background: var(--level-info);
701
+ border-color: var(--level-info);
702
+ color: white;
703
+ }
704
+
705
+ /* Timeline Container */
706
+ .timeline-container {
707
+ position: relative;
708
+ background: var(--bg-secondary);
709
+ border-bottom: 1px solid var(--border);
710
+ padding: 8px;
711
+ }
712
+
713
+ #timeline-canvas {
714
+ width: 100%;
715
+ height: 120px;
716
+ background: var(--bg-primary);
717
+ border-radius: 4px;
718
+ }
719
+
720
+ .timeline-tooltip {
721
+ position: absolute;
722
+ background: var(--bg-header);
723
+ border: 1px solid var(--border);
724
+ border-radius: 4px;
725
+ padding: 6px 10px;
726
+ font-size: 11px;
727
+ pointer-events: none;
728
+ display: none;
729
+ z-index: 100;
730
+ max-width: 250px;
731
+ }
732
+
733
+ .timeline-controls {
734
+ display: flex;
735
+ justify-content: center;
736
+ gap: 4px;
737
+ margin-top: 6px;
738
+ }
739
+
740
+ .timeline-btn {
741
+ background: transparent;
742
+ border: 1px solid var(--border);
743
+ color: var(--text-muted);
744
+ padding: 2px 8px;
745
+ border-radius: 3px;
746
+ cursor: pointer;
747
+ font-size: 10px;
748
+ }
749
+
750
+ .timeline-btn:hover {
751
+ background: var(--bg-hover);
752
+ color: var(--text-primary);
753
+ }
754
+
755
+ .timeline-btn.active {
756
+ background: var(--button-bg);
757
+ border-color: var(--button-bg);
758
+ color: white;
759
+ }
760
+
761
+ /* Filter Bar Styles */
762
+ .filter-bar {
763
+ padding: 8px 16px;
764
+ background: var(--bg-secondary);
765
+ border-bottom: 1px solid var(--border);
766
+ }
767
+
768
+ .filter-bar.filter-active {
769
+ border-bottom-color: var(--level-info);
770
+ }
771
+
772
+ .filter-row {
773
+ display: flex;
774
+ align-items: center;
775
+ gap: 8px;
776
+ }
777
+
778
+ .filter-levels {
779
+ display: flex;
780
+ gap: 4px;
781
+ }
782
+
783
+ .filter-level-btn {
784
+ width: 24px;
785
+ height: 24px;
786
+ border: 1px solid var(--border);
787
+ border-radius: 4px;
788
+ background: transparent;
789
+ color: var(--text-muted);
790
+ cursor: pointer;
791
+ font-size: 11px;
792
+ font-weight: 600;
793
+ transition: all 0.15s ease;
794
+ }
795
+
796
+ .filter-level-btn:hover {
797
+ background: var(--bg-hover);
798
+ color: var(--text-primary);
799
+ }
800
+
801
+ .filter-level-btn.active {
802
+ background: var(--bg-hover);
803
+ color: var(--text-primary);
804
+ border-color: var(--text-secondary);
805
+ }
806
+
807
+ .filter-level-btn[data-level="debug"].active { border-color: var(--level-debug); color: var(--level-debug); }
808
+ .filter-level-btn[data-level="info"].active { border-color: var(--level-info); color: var(--level-info); }
809
+ .filter-level-btn[data-level="warn"].active { border-color: var(--level-warn); color: var(--level-warn); }
810
+ .filter-level-btn[data-level="error"].active { border-color: var(--level-error); color: var(--level-error); }
811
+
812
+ .filter-input {
813
+ flex: 1;
814
+ min-width: 80px;
815
+ padding: 4px 8px;
816
+ border: 1px solid var(--border);
817
+ border-radius: 4px;
818
+ background: var(--bg-primary);
819
+ color: var(--text-primary);
820
+ font-size: 12px;
821
+ font-family: inherit;
822
+ }
823
+
824
+ .filter-input:focus {
825
+ outline: none;
826
+ border-color: var(--level-info);
827
+ }
828
+
829
+ .filter-input::placeholder {
830
+ color: var(--text-muted);
831
+ }
832
+
833
+ .filter-clear-btn {
834
+ width: 24px;
835
+ height: 24px;
836
+ border: 1px solid var(--border);
837
+ border-radius: 4px;
838
+ background: transparent;
839
+ color: var(--text-muted);
840
+ cursor: pointer;
841
+ font-size: 14px;
842
+ line-height: 1;
843
+ transition: all 0.15s ease;
844
+ }
845
+
846
+ .filter-clear-btn:hover {
847
+ background: var(--level-error);
848
+ border-color: var(--level-error);
849
+ color: white;
850
+ }
851
+
852
+ .filter-status {
853
+ font-size: 11px;
854
+ color: var(--text-muted);
855
+ margin-top: 6px;
856
+ }
857
+
858
+ .logs {
859
+ flex: 1;
860
+ overflow-y: auto;
861
+ overflow-x: hidden;
862
+ }
863
+
864
+ .logs::-webkit-scrollbar {
865
+ width: 8px;
866
+ }
867
+
868
+ .logs::-webkit-scrollbar-track {
869
+ background: var(--bg-primary);
870
+ }
871
+
872
+ .logs::-webkit-scrollbar-thumb {
873
+ background: #4a4a4a;
874
+ border-radius: 4px;
875
+ }
876
+
877
+ .empty {
878
+ display: flex;
879
+ align-items: center;
880
+ justify-content: center;
881
+ height: 100%;
882
+ color: var(--text-muted);
883
+ font-style: italic;
884
+ }
885
+
886
+ .log-entry {
887
+ border-bottom: 1px solid var(--border);
888
+ padding: 10px 16px;
889
+ }
890
+
891
+ .log-entry:hover {
892
+ background: var(--bg-hover);
893
+ }
894
+
895
+ .log-header {
896
+ display: flex;
897
+ align-items: center;
898
+ gap: 10px;
899
+ margin-bottom: 4px;
900
+ }
901
+
902
+ .log-level {
903
+ font-weight: 600;
904
+ font-size: 10px;
905
+ text-transform: uppercase;
906
+ padding: 2px 6px;
907
+ border-radius: 3px;
908
+ min-width: 45px;
909
+ text-align: center;
910
+ }
911
+
912
+ .log-level-debug { background: rgba(110, 110, 110, 0.2); color: var(--level-debug); }
913
+ .log-level-info { background: rgba(55, 148, 255, 0.15); color: var(--level-info); }
914
+ .log-level-warn { background: rgba(204, 167, 0, 0.15); color: var(--level-warn); }
915
+ .log-level-error { background: rgba(241, 76, 76, 0.15); color: var(--level-error); }
916
+
917
+ .log-time {
918
+ color: var(--text-muted);
919
+ font-size: 11px;
920
+ }
921
+
922
+ .log-source {
923
+ color: var(--text-secondary);
924
+ font-size: 11px;
925
+ margin-left: auto;
926
+ }
927
+
928
+ .log-message {
929
+ color: var(--text-primary);
930
+ word-break: break-word;
931
+ }
932
+
933
+ .log-data {
934
+ margin-top: 6px;
935
+ }
936
+
937
+ .log-data-toggle {
938
+ background: transparent;
939
+ border: none;
940
+ color: var(--text-secondary);
941
+ cursor: pointer;
942
+ padding: 2px 0;
943
+ font-size: 11px;
944
+ display: flex;
945
+ align-items: center;
946
+ gap: 4px;
947
+ }
948
+
949
+ .log-data-toggle:hover {
950
+ color: var(--text-primary);
951
+ }
952
+
953
+ .log-data-toggle::before {
954
+ content: '▶';
955
+ font-size: 8px;
956
+ transition: transform 0.15s ease;
957
+ }
958
+
959
+ .log-data-toggle.expanded::before {
960
+ transform: rotate(90deg);
961
+ }
962
+
963
+ .log-data-content {
964
+ display: none;
965
+ margin-top: 6px;
966
+ padding: 8px;
967
+ background: var(--bg-secondary);
968
+ border-radius: 4px;
969
+ font-size: 11px;
970
+ overflow-x: auto;
971
+ white-space: pre-wrap;
972
+ word-break: break-all;
973
+ }
974
+
975
+ .log-data-content.visible {
976
+ display: block;
977
+ }
978
+
979
+ .footer {
980
+ padding: 8px 16px;
981
+ background: var(--bg-header);
982
+ border-top: 1px solid var(--border);
983
+ font-size: 11px;
984
+ color: var(--text-muted);
985
+ display: flex;
986
+ justify-content: space-between;
987
+ }
988
+ </style>
989
+ </head>
990
+ <body>
991
+ <div class="header">
992
+ <div class="title">
993
+ DevLogger
994
+ <span class="badge" id="log-count">0</span>
995
+ </div>
996
+ <div class="status">
997
+ <span class="status-dot" id="status-dot"></span>
998
+ <span id="status-text">Connected</span>
999
+ </div>
1000
+ <div class="actions">
1001
+ <button class="btn btn-copy" id="btn-copy-json" title="Copy as JSON">JSON</button>
1002
+ <button class="btn btn-copy" id="btn-copy-text" title="Copy as Text">TXT</button>
1003
+ <button class="btn btn-timeline" id="btn-timeline" title="Toggle Timeline">Timeline</button>
1004
+ <button class="btn" id="btn-clear">Clear</button>
1005
+ </div>
1006
+ </div>
1007
+
1008
+ <div class="timeline-container" id="timeline-container" style="display: none;">
1009
+ <canvas id="timeline-canvas"></canvas>
1010
+ <div class="timeline-tooltip" id="timeline-tooltip"></div>
1011
+ <div class="timeline-controls">
1012
+ <button class="timeline-btn" data-window="10000">10s</button>
1013
+ <button class="timeline-btn active" data-window="30000">30s</button>
1014
+ <button class="timeline-btn" data-window="60000">60s</button>
1015
+ </div>
1016
+ </div>
1017
+
1018
+ <div class="filter-bar" id="filter-bar">
1019
+ <div class="filter-row">
1020
+ <div class="filter-levels">
1021
+ <button class="filter-level-btn active" data-level="debug" title="Debug">D</button>
1022
+ <button class="filter-level-btn active" data-level="info" title="Info">I</button>
1023
+ <button class="filter-level-btn active" data-level="warn" title="Warning">W</button>
1024
+ <button class="filter-level-btn active" data-level="error" title="Error">E</button>
1025
+ </div>
1026
+ <input type="text" class="filter-input" id="filter-search" placeholder="Search logs..." data-filter="search">
1027
+ <input type="text" class="filter-input" id="filter-file" placeholder="Filter by file..." data-filter="file" style="max-width: 120px;">
1028
+ </div>
1029
+ <div class="filter-status" id="filter-status" style="display: none;"></div>
1030
+ </div>
1031
+
1032
+ <div class="logs" id="logs">
1033
+ <div class="empty">Waiting for logs...</div>
1034
+ </div>
1035
+
1036
+ <div class="footer">
1037
+ <span id="footer-count">0 logs</span>
1038
+ <span>Pop-out Window</span>
1039
+ </div>
1040
+
1041
+ <script>
1042
+ // Pop-out window script
1043
+ const CHANNEL_NAME = 'devlogger-sync';
1044
+ let logs = [];
1045
+ let channel = null;
1046
+ let isConnected = false;
1047
+
1048
+ // Filter state
1049
+ const filter = {
1050
+ levels: new Set(['debug', 'info', 'warn', 'error']),
1051
+ search: '',
1052
+ file: ''
1053
+ };
1054
+
1055
+ // DOM elements
1056
+ const logsContainer = document.getElementById('logs');
1057
+ const logCountBadge = document.getElementById('log-count');
1058
+ const footerCount = document.getElementById('footer-count');
1059
+ const statusDot = document.getElementById('status-dot');
1060
+ const statusText = document.getElementById('status-text');
1061
+ const btnClear = document.getElementById('btn-clear');
1062
+ const btnCopyJson = document.getElementById('btn-copy-json');
1063
+ const btnCopyText = document.getElementById('btn-copy-text');
1064
+ const btnTimeline = document.getElementById('btn-timeline');
1065
+ const timelineContainer = document.getElementById('timeline-container');
1066
+ const timelineCanvas = document.getElementById('timeline-canvas');
1067
+ const timelineTooltip = document.getElementById('timeline-tooltip');
1068
+ const filterBar = document.getElementById('filter-bar');
1069
+
1070
+ // Timeline state
1071
+ let timelineVisible = false;
1072
+ let timelineWindow = 30000; // 30 seconds default
1073
+ let timelineInterval = null;
1074
+ const LEVEL_COLORS = {
1075
+ debug: '#6e6e6e',
1076
+ info: '#3794ff',
1077
+ warn: '#cca700',
1078
+ error: '#f14c4c'
1079
+ };
1080
+ const filterSearch = document.getElementById('filter-search');
1081
+ const filterFile = document.getElementById('filter-file');
1082
+ const filterStatus = document.getElementById('filter-status');
1083
+
1084
+ // Connect to broadcast channel
1085
+ function connect() {
1086
+ try {
1087
+ channel = new BroadcastChannel(CHANNEL_NAME);
1088
+ channel.onmessage = handleMessage;
1089
+ isConnected = true;
1090
+ updateStatus(true);
1091
+
1092
+ // Request sync from main window
1093
+ channel.postMessage({
1094
+ type: 'SYNC_REQUEST',
1095
+ senderId: 'popout',
1096
+ timestamp: Date.now()
1097
+ });
1098
+ } catch (e) {
1099
+ console.error('Failed to connect:', e);
1100
+ updateStatus(false);
1101
+ }
1102
+ }
1103
+
1104
+ // Handle incoming messages
1105
+ function handleMessage(event) {
1106
+ const message = event.data;
1107
+
1108
+ switch (message.type) {
1109
+ case 'NEW_LOG':
1110
+ addLog(message.payload);
1111
+ break;
1112
+ case 'SYNC_RESPONSE':
1113
+ syncLogs(message.payload);
1114
+ break;
1115
+ case 'CLEAR_LOGS':
1116
+ clearLogs();
1117
+ break;
1118
+ }
1119
+ }
1120
+
1121
+ // Add a single log
1122
+ function addLog(log) {
1123
+ logs.push(log);
1124
+ // Only render if log matches current filter
1125
+ if (matchesFilter(log)) {
1126
+ // Remove empty state if present
1127
+ const empty = logsContainer.querySelector('.empty');
1128
+ if (empty) empty.remove();
1129
+ renderLog(log);
1130
+ scrollToBottom();
1131
+ }
1132
+ updateCounts();
1133
+ updateFilterUI();
1134
+ }
1135
+
1136
+ // Sync all logs
1137
+ function syncLogs(newLogs) {
1138
+ logs = newLogs || [];
1139
+ renderAllLogs();
1140
+ updateCounts();
1141
+ }
1142
+
1143
+ // Clear all logs
1144
+ function clearLogs() {
1145
+ logs = [];
1146
+ renderAllLogs();
1147
+ updateCounts();
1148
+ }
1149
+
1150
+ // Check if a log matches the current filter
1151
+ function matchesFilter(log) {
1152
+ // Level filter
1153
+ if (filter.levels.size > 0 && !filter.levels.has(log.level)) {
1154
+ return false;
1155
+ }
1156
+
1157
+ // File filter
1158
+ if (filter.file && !log.source.file.toLowerCase().includes(filter.file.toLowerCase())) {
1159
+ return false;
1160
+ }
1161
+
1162
+ // Text search
1163
+ if (filter.search) {
1164
+ const searchLower = filter.search.toLowerCase();
1165
+ const messageMatch = log.message.toLowerCase().includes(searchLower);
1166
+ const dataMatch = JSON.stringify(log.data).toLowerCase().includes(searchLower);
1167
+ if (!messageMatch && !dataMatch) {
1168
+ return false;
1169
+ }
1170
+ }
1171
+
1172
+ return true;
1173
+ }
1174
+
1175
+ // Get filtered logs
1176
+ function getFilteredLogs() {
1177
+ return logs.filter(log => matchesFilter(log));
1178
+ }
1179
+
1180
+ // Check if any filter is active
1181
+ function isFilterActive() {
1182
+ return filter.levels.size !== 4 || filter.search !== '' || filter.file !== '';
1183
+ }
1184
+
1185
+ // Update filter UI
1186
+ function updateFilterUI() {
1187
+ const active = isFilterActive();
1188
+ const filteredLogs = getFilteredLogs();
1189
+
1190
+ filterBar.classList.toggle('filter-active', active);
1191
+
1192
+ if (active) {
1193
+ filterStatus.style.display = 'block';
1194
+ filterStatus.textContent = 'Showing ' + filteredLogs.length + ' of ' + logs.length + ' logs';
1195
+ } else {
1196
+ filterStatus.style.display = 'none';
1197
+ }
1198
+
1199
+ // Update counts
1200
+ logCountBadge.textContent = filteredLogs.length;
1201
+ if (active) {
1202
+ footerCount.textContent = filteredLogs.length + ' of ' + logs.length + ' logs';
1203
+ } else {
1204
+ footerCount.textContent = logs.length + ' logs';
1205
+ }
1206
+ }
1207
+
1208
+ // Render all logs
1209
+ function renderAllLogs() {
1210
+ const filteredLogs = getFilteredLogs();
1211
+
1212
+ if (logs.length === 0) {
1213
+ logsContainer.innerHTML = '<div class="empty">No logs yet...</div>';
1214
+ updateFilterUI();
1215
+ return;
1216
+ }
1217
+
1218
+ if (filteredLogs.length === 0) {
1219
+ logsContainer.innerHTML = '<div class="empty">No logs match your filter</div>';
1220
+ updateFilterUI();
1221
+ return;
1222
+ }
1223
+
1224
+ logsContainer.innerHTML = '';
1225
+ filteredLogs.forEach(log => renderLog(log));
1226
+ scrollToBottom();
1227
+ updateFilterUI();
1228
+ }
1229
+
1230
+ // Render a single log entry
1231
+ function renderLog(log) {
1232
+ // Remove empty state if present
1233
+ const empty = logsContainer.querySelector('.empty');
1234
+ if (empty) empty.remove();
1235
+
1236
+ const entry = document.createElement('div');
1237
+ entry.className = 'log-entry';
1238
+
1239
+ const time = formatTime(log.timestamp);
1240
+ const source = formatSource(log.source);
1241
+ const hasData = log.data && log.data.length > 0;
1242
+ const dataId = 'data-' + log.id;
1243
+
1244
+ entry.innerHTML = \`
1245
+ <div class="log-header">
1246
+ <span class="log-level log-level-\${log.level}">\${log.level}</span>
1247
+ <span class="log-time">\${time}</span>
1248
+ <span class="log-source">\${escapeHtml(source)}</span>
1249
+ </div>
1250
+ <div class="log-message">\${escapeHtml(log.message)}</div>
1251
+ \${hasData ? \`
1252
+ <div class="log-data">
1253
+ <button class="log-data-toggle" data-target="\${dataId}">
1254
+ \${log.data.length} item\${log.data.length > 1 ? 's' : ''}
1255
+ </button>
1256
+ <pre class="log-data-content" id="\${dataId}">\${escapeHtml(formatData(log.data))}</pre>
1257
+ </div>
1258
+ \` : ''}
1259
+ \`;
1260
+
1261
+ // Add toggle handler
1262
+ if (hasData) {
1263
+ const toggle = entry.querySelector('.log-data-toggle');
1264
+ const content = entry.querySelector('#' + dataId);
1265
+ toggle.addEventListener('click', () => {
1266
+ toggle.classList.toggle('expanded');
1267
+ content.classList.toggle('visible');
1268
+ });
1269
+ }
1270
+
1271
+ logsContainer.appendChild(entry);
1272
+ }
1273
+
1274
+ // Format timestamp
1275
+ function formatTime(timestamp) {
1276
+ const date = new Date(timestamp);
1277
+ return date.toTimeString().split(' ')[0] + '.' +
1278
+ date.getMilliseconds().toString().padStart(3, '0');
1279
+ }
1280
+
1281
+ // Format source
1282
+ function formatSource(source) {
1283
+ if (!source || source.file === 'unknown') return 'unknown';
1284
+ return source.file + ':' + source.line;
1285
+ }
1286
+
1287
+ // Format data
1288
+ function formatData(data) {
1289
+ try {
1290
+ return data.map(item =>
1291
+ typeof item === 'string' ? item : JSON.stringify(item, null, 2)
1292
+ ).join('\\n');
1293
+ } catch {
1294
+ return '[Unable to display]';
1295
+ }
1296
+ }
1297
+
1298
+ // Escape HTML
1299
+ function escapeHtml(str) {
1300
+ const div = document.createElement('div');
1301
+ div.textContent = str;
1302
+ return div.innerHTML;
1303
+ }
1304
+
1305
+ // Update counts
1306
+ function updateCounts() {
1307
+ const count = logs.length;
1308
+ logCountBadge.textContent = count;
1309
+ footerCount.textContent = count + ' logs';
1310
+ }
1311
+
1312
+ // Update connection status
1313
+ function updateStatus(connected) {
1314
+ isConnected = connected;
1315
+ statusDot.className = 'status-dot' + (connected ? '' : ' disconnected');
1316
+ statusText.textContent = connected ? 'Connected' : 'Disconnected';
1317
+ }
1318
+
1319
+ // Scroll to bottom
1320
+ function scrollToBottom() {
1321
+ logsContainer.scrollTop = logsContainer.scrollHeight;
1322
+ }
1323
+
1324
+ // Clear button handler
1325
+ btnClear.addEventListener('click', () => {
1326
+ if (channel) {
1327
+ channel.postMessage({
1328
+ type: 'CLEAR_LOGS',
1329
+ senderId: 'popout',
1330
+ timestamp: Date.now()
1331
+ });
1332
+ }
1333
+ clearLogs();
1334
+ });
1335
+
1336
+ // Export logs as JSON
1337
+ function exportLogsJson() {
1338
+ return JSON.stringify(logs, null, 2);
1339
+ }
1340
+
1341
+ // Export logs as text
1342
+ function exportLogsText() {
1343
+ return logs.map(log => {
1344
+ const time = new Date(log.timestamp).toISOString();
1345
+ const level = log.level.toUpperCase().padEnd(5);
1346
+ const source = log.source.file + ':' + log.source.line;
1347
+ const context = log.context ? ' [' + Object.entries(log.context).map(([k, v]) => k + '=' + v).join(', ') + ']' : '';
1348
+ const span = log.spanId ? ' (span: ' + log.spanId + ')' : '';
1349
+ const data = log.data.length > 0 ? '\\n Data: ' + JSON.stringify(log.data) : '';
1350
+ return '[' + time + '] ' + level + ' ' + log.message + context + span + '\\n Source: ' + source + data;
1351
+ }).join('\\n\\n');
1352
+ }
1353
+
1354
+ // Show copy feedback
1355
+ function showCopyFeedback(button, success) {
1356
+ const originalText = button.textContent;
1357
+ button.textContent = success ? '✓' : '✗';
1358
+ button.classList.add(success ? 'success' : 'error');
1359
+ setTimeout(() => {
1360
+ button.textContent = originalText;
1361
+ button.classList.remove('success', 'error');
1362
+ }, 1500);
1363
+ }
1364
+
1365
+ // Copy JSON handler
1366
+ btnCopyJson.addEventListener('click', () => {
1367
+ navigator.clipboard.writeText(exportLogsJson()).then(() => {
1368
+ showCopyFeedback(btnCopyJson, true);
1369
+ }).catch(() => {
1370
+ showCopyFeedback(btnCopyJson, false);
1371
+ });
1372
+ });
1373
+
1374
+ // Copy Text handler
1375
+ btnCopyText.addEventListener('click', () => {
1376
+ navigator.clipboard.writeText(exportLogsText()).then(() => {
1377
+ showCopyFeedback(btnCopyText, true);
1378
+ }).catch(() => {
1379
+ showCopyFeedback(btnCopyText, false);
1380
+ });
1381
+ });
1382
+
1383
+ // Check connection periodically
1384
+ setInterval(() => {
1385
+ if (channel) {
1386
+ try {
1387
+ channel.postMessage({
1388
+ type: 'PING',
1389
+ senderId: 'popout',
1390
+ timestamp: Date.now()
1391
+ });
1392
+ } catch {
1393
+ updateStatus(false);
1394
+ }
1395
+ }
1396
+ }, 5000);
1397
+
1398
+ // Filter: Level buttons
1399
+ document.querySelectorAll('.filter-level-btn').forEach(btn => {
1400
+ btn.addEventListener('click', (e) => {
1401
+ const level = e.currentTarget.dataset.level;
1402
+ if (filter.levels.has(level)) {
1403
+ filter.levels.delete(level);
1404
+ e.currentTarget.classList.remove('active');
1405
+ } else {
1406
+ filter.levels.add(level);
1407
+ e.currentTarget.classList.add('active');
1408
+ }
1409
+ renderAllLogs();
1410
+ });
1411
+ });
1412
+
1413
+ // Filter: Search input
1414
+ filterSearch.addEventListener('input', (e) => {
1415
+ filter.search = e.target.value;
1416
+ renderAllLogs();
1417
+ });
1418
+
1419
+ // Filter: File input
1420
+ filterFile.addEventListener('input', (e) => {
1421
+ filter.file = e.target.value;
1422
+ renderAllLogs();
1423
+ });
1424
+
1425
+ // Timeline: Toggle button
1426
+ btnTimeline.addEventListener('click', () => {
1427
+ timelineVisible = !timelineVisible;
1428
+ timelineContainer.style.display = timelineVisible ? 'block' : 'none';
1429
+ btnTimeline.classList.toggle('active', timelineVisible);
1430
+ if (timelineVisible) {
1431
+ resizeCanvas();
1432
+ drawTimeline();
1433
+ startTimelineRefresh();
1434
+ } else {
1435
+ stopTimelineRefresh();
1436
+ }
1437
+ });
1438
+
1439
+ // Timeline: Time window buttons
1440
+ document.querySelectorAll('.timeline-btn[data-window]').forEach(btn => {
1441
+ btn.addEventListener('click', (e) => {
1442
+ timelineWindow = parseInt(e.currentTarget.dataset.window);
1443
+ document.querySelectorAll('.timeline-btn[data-window]').forEach(b => b.classList.remove('active'));
1444
+ e.currentTarget.classList.add('active');
1445
+ drawTimeline();
1446
+ });
1447
+ });
1448
+
1449
+ // Timeline: Resize canvas for proper DPI
1450
+ function resizeCanvas() {
1451
+ const rect = timelineCanvas.getBoundingClientRect();
1452
+ const dpr = window.devicePixelRatio || 1;
1453
+ timelineCanvas.width = rect.width * dpr;
1454
+ timelineCanvas.height = rect.height * dpr;
1455
+ const ctx = timelineCanvas.getContext('2d');
1456
+ ctx.scale(dpr, dpr);
1457
+ }
1458
+
1459
+ // Timeline: Draw the timeline visualization
1460
+ function drawTimeline() {
1461
+ const ctx = timelineCanvas.getContext('2d');
1462
+ const rect = timelineCanvas.getBoundingClientRect();
1463
+ const width = rect.width;
1464
+ const height = rect.height;
1465
+
1466
+ // Clear canvas
1467
+ ctx.fillStyle = '#1e1e1e';
1468
+ ctx.fillRect(0, 0, width, height);
1469
+
1470
+ // Calculate time bounds
1471
+ const now = Date.now();
1472
+ const startTime = now - timelineWindow;
1473
+
1474
+ // Filter logs within time window
1475
+ const visibleLogs = logs.filter(log => log.timestamp >= startTime && log.timestamp <= now);
1476
+
1477
+ // Draw time grid
1478
+ ctx.strokeStyle = '#333';
1479
+ ctx.lineWidth = 1;
1480
+ const gridLines = 5;
1481
+ for (let i = 0; i <= gridLines; i++) {
1482
+ const x = (i / gridLines) * width;
1483
+ ctx.beginPath();
1484
+ ctx.moveTo(x, 0);
1485
+ ctx.lineTo(x, height);
1486
+ ctx.stroke();
1487
+
1488
+ // Draw time labels
1489
+ const time = new Date(startTime + (i / gridLines) * timelineWindow);
1490
+ ctx.fillStyle = '#666';
1491
+ ctx.font = '10px monospace';
1492
+ ctx.fillText(time.toTimeString().split(' ')[0], x + 3, height - 5);
1493
+ }
1494
+
1495
+ // Draw log markers
1496
+ const markerHeight = 16;
1497
+ const bottomOffset = 20;
1498
+
1499
+ visibleLogs.forEach(log => {
1500
+ const x = ((log.timestamp - startTime) / timelineWindow) * width;
1501
+ const y = height - bottomOffset - markerHeight / 2;
1502
+ const color = LEVEL_COLORS[log.level] || LEVEL_COLORS.debug;
1503
+
1504
+ // Draw marker
1505
+ ctx.beginPath();
1506
+ ctx.arc(x, y, 4, 0, Math.PI * 2);
1507
+ ctx.fillStyle = color;
1508
+ ctx.fill();
1509
+
1510
+ // Store bounds for hover
1511
+ log._timelineBounds = { x, y, r: 8 };
1512
+ });
1513
+
1514
+ // Draw level legend
1515
+ ctx.font = '10px monospace';
1516
+ let legendX = 10;
1517
+ Object.entries(LEVEL_COLORS).forEach(([level, color]) => {
1518
+ ctx.fillStyle = color;
1519
+ ctx.beginPath();
1520
+ ctx.arc(legendX, 12, 4, 0, Math.PI * 2);
1521
+ ctx.fill();
1522
+ ctx.fillStyle = '#888';
1523
+ ctx.fillText(level, legendX + 8, 15);
1524
+ legendX += ctx.measureText(level).width + 20;
1525
+ });
1526
+ }
1527
+
1528
+ // Timeline: Mouse move for tooltips
1529
+ timelineCanvas.addEventListener('mousemove', (e) => {
1530
+ const rect = timelineCanvas.getBoundingClientRect();
1531
+ const x = e.clientX - rect.left;
1532
+ const y = e.clientY - rect.top;
1533
+
1534
+ const hoveredLog = logs.find(log => {
1535
+ if (!log._timelineBounds) return false;
1536
+ const b = log._timelineBounds;
1537
+ const dx = x - b.x;
1538
+ const dy = y - b.y;
1539
+ return Math.sqrt(dx * dx + dy * dy) <= b.r;
1540
+ });
1541
+
1542
+ if (hoveredLog) {
1543
+ const time = new Date(hoveredLog.timestamp).toTimeString().split(' ')[0];
1544
+ timelineTooltip.innerHTML =
1545
+ '<strong>[' + hoveredLog.level.toUpperCase() + ']</strong> ' +
1546
+ escapeHtml(hoveredLog.message.substring(0, 100)) +
1547
+ (hoveredLog.message.length > 100 ? '...' : '') +
1548
+ '<br><small>' + time + '</small>';
1549
+ timelineTooltip.style.display = 'block';
1550
+ timelineTooltip.style.left = Math.min(e.clientX - rect.left + 10, rect.width - 260) + 'px';
1551
+ timelineTooltip.style.top = (e.clientY - rect.top - 50) + 'px';
1552
+ } else {
1553
+ timelineTooltip.style.display = 'none';
1554
+ }
1555
+ });
1556
+
1557
+ // Timeline: Hide tooltip on mouse leave
1558
+ timelineCanvas.addEventListener('mouseleave', () => {
1559
+ timelineTooltip.style.display = 'none';
1560
+ });
1561
+
1562
+ // Timeline: Start auto-refresh
1563
+ function startTimelineRefresh() {
1564
+ if (timelineInterval) return;
1565
+ timelineInterval = setInterval(() => {
1566
+ if (timelineVisible) drawTimeline();
1567
+ }, 500);
1568
+ }
1569
+
1570
+ // Timeline: Stop auto-refresh
1571
+ function stopTimelineRefresh() {
1572
+ if (timelineInterval) {
1573
+ clearInterval(timelineInterval);
1574
+ timelineInterval = null;
1575
+ }
1576
+ }
1577
+
1578
+ // Handle window resize for timeline
1579
+ window.addEventListener('resize', () => {
1580
+ if (timelineVisible) {
1581
+ resizeCanvas();
1582
+ drawTimeline();
1583
+ }
1584
+ });
1585
+
1586
+ // Initialize
1587
+ connect();
1588
+ <\/script>
1589
+ </body>
1590
+ </html>`}function z(){return{levels:new Set(["debug","info","warn","error"]),search:"",file:""}}function it(t,e){if(e.levels.size>0&&!e.levels.has(t.level)||e.file&&!t.source.file.toLowerCase().includes(e.file.toLowerCase()))return!1;if(e.search){const n=e.search.toLowerCase(),o=t.message.toLowerCase().includes(n),r=JSON.stringify(t.data).toLowerCase().includes(n);if(!o&&!r)return!1}return!0}function V(t,e){return t.filter(n=>it(n,e))}function ie(t){return!(t.levels.size===4)||t.search!==""||t.file!==""}function st(t,e,n){const o=ie(t);return`
1591
+ <div class="filter-bar ${o?"filter-active":""}">
1592
+ <div class="filter-row">
1593
+ <div class="filter-levels">
1594
+ <button class="filter-level-btn ${t.levels.has("debug")?"active":""}" data-level="debug" title="Debug">D</button>
1595
+ <button class="filter-level-btn ${t.levels.has("info")?"active":""}" data-level="info" title="Info">I</button>
1596
+ <button class="filter-level-btn ${t.levels.has("warn")?"active":""}" data-level="warn" title="Warning">W</button>
1597
+ <button class="filter-level-btn ${t.levels.has("error")?"active":""}" data-level="error" title="Error">E</button>
1598
+ </div>
1599
+ <div class="filter-search">
1600
+ <input type="text" class="filter-input" placeholder="Search logs..." value="${be(t.search)}" data-filter="search">
1601
+ </div>
1602
+ <div class="filter-file">
1603
+ <input type="text" class="filter-input filter-file-input" placeholder="Filter by file..." value="${be(t.file)}" data-filter="file">
1604
+ </div>
1605
+ ${o?'<button class="filter-clear-btn" title="Clear filters">✕</button>':""}
1606
+ </div>
1607
+ ${o?`<div class="filter-status">Showing ${n} of ${e} logs</div>`:""}
1608
+ </div>
1609
+ `}function be(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}const Z={key:"l",ctrlKey:!0,shiftKey:!0};function ve(t,e){const n=t.textContent;t.textContent=e?"✓":"✗",t.disabled=!0,setTimeout(()=>{t.textContent=n,t.disabled=!1},1e3)}const i={initialized:!1,visible:!1,host:null,shadow:null,container:null,logsList:null,filterBar:null,toggleBtn:null,badge:null,unsubscribe:null,channelUnsubscribe:null,filter:z()};function at(t){const e=document.createElement("style");e.textContent=je,t.appendChild(e);const n=document.createElement("button");n.className="devlogger-toggle",n.innerHTML="📋",n.title="Toggle DevLogger (Ctrl+Shift+L)",n.addEventListener("click",()=>F.toggle()),t.appendChild(n),i.toggleBtn=n;const o=document.createElement("div");o.className="devlogger-container hidden";const r=f.getLogs(),s=V(r,i.filter);o.innerHTML=`
1610
+ <div class="devlogger-header">
1611
+ <div class="devlogger-title">
1612
+ DevLogger
1613
+ <span class="devlogger-badge">${s.length}</span>
1614
+ </div>
1615
+ <div class="devlogger-actions">
1616
+ <button class="devlogger-btn" data-action="copy-json" title="Copy as JSON">JSON</button>
1617
+ <button class="devlogger-btn" data-action="copy-text" title="Copy as Text">TXT</button>
1618
+ <button class="devlogger-btn" data-action="clear" title="Clear logs">Clear</button>
1619
+ <button class="devlogger-btn devlogger-btn-primary" data-action="popout" title="Open in new window">Pop-out</button>
1620
+ <button class="devlogger-btn" data-action="close" title="Close (Ctrl+Shift+L)">✕</button>
1621
+ </div>
1622
+ </div>
1623
+ <div class="filter-bar-container"></div>
1624
+ <div class="devlogger-logs"></div>
1625
+ <div class="devlogger-footer">
1626
+ <span class="devlogger-log-count">${r.length} logs</span>
1627
+ <span class="devlogger-shortcut">Ctrl+Shift+L to toggle</span>
1628
+ </div>
1629
+ `,i.badge=o.querySelector(".devlogger-badge"),i.logsList=o.querySelector(".devlogger-logs"),i.filterBar=o.querySelector(".filter-bar-container"),o.querySelectorAll("[data-action]").forEach(c=>{c.addEventListener("click",l=>{const a=l.currentTarget.dataset.action,d=l.currentTarget;switch(a){case"clear":f.clear(),N.sendClear(),y();break;case"popout":F.popout();break;case"close":F.close();break;case"copy-json":f.copyLogs({format:"json"}).then(u=>{ve(d,u)});break;case"copy-text":f.copyLogs({format:"text"}).then(u=>{ve(d,u)});break}})}),t.appendChild(o),i.container=o,I(),y()}function I(){if(!i.filterBar)return;const t=f.getLogs(),e=V(t,i.filter);i.filterBar.innerHTML=st(i.filter,t.length,e.length),lt()}function lt(){if(!i.filterBar)return;i.filterBar.querySelectorAll(".filter-level-btn").forEach(o=>{o.addEventListener("click",r=>{const s=r.currentTarget.dataset.level;i.filter.levels.has(s)?i.filter.levels.delete(s):i.filter.levels.add(s),I(),y()})});const t=i.filterBar.querySelector('[data-filter="search"]');t&&t.addEventListener("input",o=>{i.filter.search=o.target.value,ne(),y()});const e=i.filterBar.querySelector('[data-filter="file"]');e&&e.addEventListener("input",o=>{i.filter.file=o.target.value,ne(),y()});const n=i.filterBar.querySelector(".filter-clear-btn");n&&n.addEventListener("click",()=>{i.filter=z(),I(),y()})}function y(){if(!i.logsList)return;const t=f.getLogs(),e=V(t,i.filter);if(i.logsList.innerHTML="",t.length===0)i.logsList.appendChild(Qe());else if(e.length===0){const n=document.createElement("div");n.className="devlogger-no-results",n.innerHTML=`
1630
+ <span class="devlogger-no-results-icon">🔍</span>
1631
+ <span class="devlogger-no-results-text">No logs match your filter</span>
1632
+ `,i.logsList.appendChild(n)}else{const n=document.createDocumentFragment();for(const o of e)n.appendChild(we(o));i.logsList.appendChild(n)}Le(e.length,t.length),se()}function ct(t){if(!i.logsList)return;const e=f.getLogs(),n=V(e,i.filter);if(n.some(r=>r.id===t.id)){const r=i.logsList.querySelector(".devlogger-empty, .devlogger-no-results");r&&r.remove(),i.logsList.appendChild(we(t))}Le(n.length,e.length),ne(),se()}function Le(t,e){i.badge&&(i.badge.textContent=String(t));const n=i.container?.querySelector(".devlogger-log-count");n&&(ie(i.filter)?n.textContent=`${t} of ${e} logs`:n.textContent=`${e} logs`)}function ne(){if(!i.filterBar)return;const t=f.getLogs(),e=V(t,i.filter),n=ie(i.filter),o=i.filterBar.querySelector(".filter-bar");o&&o.classList.toggle("filter-active",n);let r=i.filterBar.querySelector(".filter-status");if(n){r||(r=document.createElement("div"),r.className="filter-status",o?.appendChild(r)),r.textContent=`Showing ${e.length} of ${t.length} logs`;let s=i.filterBar.querySelector(".filter-clear-btn");s||(s=document.createElement("button"),s.className="filter-clear-btn",s.setAttribute("title","Clear filters"),s.textContent="✕",s.addEventListener("click",()=>{i.filter=z(),I(),y()}),i.filterBar.querySelector(".filter-row")?.appendChild(s))}else r?.remove(),i.filterBar.querySelector(".filter-clear-btn")?.remove()}function se(){i.logsList&&i.visible&&(i.logsList.scrollTop=i.logsList.scrollHeight)}function me(t){t.key.toLowerCase()===Z.key&&t.ctrlKey===Z.ctrlKey&&t.shiftKey===Z.shiftKey&&(t.preventDefault(),F.toggle())}function dt(t){switch(t.type){case"CLEAR_LOGS":f.clear(),y();break;case"SYNC_REQUEST":N.sendSyncResponse(f.getLogs());break}}const F={init(){try{if(i.initialized||!f.isEnabled()||typeof document>"u")return;const t=document.createElement("div");t.id="devlogger-host",document.body.appendChild(t),i.host=t;const e=t.attachShadow({mode:"open"});i.shadow=e,at(e),i.unsubscribe=f.subscribe(n=>{ct(n),N.sendLog(n)}),i.channelUnsubscribe=N.subscribe(dt),document.addEventListener("keydown",me),i.initialized=!0}catch(t){console.warn("[DevLogger UI] Init error:",t)}},open(){try{i.initialized||this.init(),i.container&&(i.container.classList.remove("hidden"),i.visible=!0,se())}catch{}},close(){try{i.container&&(i.container.classList.add("hidden"),i.visible=!1)}catch{}},toggle(){i.visible?this.close():this.open()},popout(){try{i.initialized||this.init(),nt()}catch(t){console.warn("[DevLogger] Failed to open pop-out:",t)}},closePopout(){he()},isPopoutOpen(){return ot()},setFilter(t){try{t.levels!==void 0&&(i.filter.levels=t.levels),t.search!==void 0&&(i.filter.search=t.search),t.file!==void 0&&(i.filter.file=t.file),I(),y()}catch{}},getFilter(){return{...i.filter,levels:new Set(i.filter.levels)}},clearFilter(){try{i.filter=z(),I(),y()}catch{}},destroy(){try{he(),i.unsubscribe&&(i.unsubscribe(),i.unsubscribe=null),i.channelUnsubscribe&&(i.channelUnsubscribe(),i.channelUnsubscribe=null),document.removeEventListener("keydown",me),i.host&&(i.host.remove(),i.host=null),i.initialized=!1,i.visible=!1,i.shadow=null,i.container=null,i.logsList=null,i.filterBar=null,i.toggleBtn=null,i.badge=null,i.filter=z()}catch{}},isVisible(){return i.visible},isInitialized(){return i.initialized}},Se={captureErrors:!0,captureRejections:!0,errorPrefix:"[Uncaught Error]",rejectionPrefix:"[Unhandled Rejection]"};let P=!1,L={...Se},q=null,A=null;function gt(t,e,n,o,r){try{const s=r||(t instanceof ErrorEvent?t.error:null),c=s?.message||String(t),l=s?.stack;f.error(`${L.errorPrefix} ${c}`,{source:e||"unknown",line:n||0,column:o||0,stack:l,originalError:s?{name:s.name,message:s.message,stack:s.stack}:void 0})}catch{}if(q)try{return q(t,e,n,o,r)??!1}catch{}return!1}function ut(t){try{const e=t.reason;let n,o={};e instanceof Error?(n=e.message,o={name:e.name,message:e.message,stack:e.stack}):typeof e=="string"?n=e:(n="Unknown rejection reason",o={reason:e}),f.error(`${L.rejectionPrefix} ${n}`,o)}catch{}}function ft(t={}){if(P){L={...L,...t};return}try{if(typeof window>"u")return;L={...Se,...t},q=window.onerror,L.captureErrors&&(window.onerror=gt),L.captureRejections&&(A=ut,window.addEventListener("unhandledrejection",A)),P=!0}catch{}}function pt(){if(P)try{if(typeof window>"u")return;window.onerror=q,A&&window.removeEventListener("unhandledrejection",A),q=null,A=null,P=!1}catch{}}function ht(){return P}function bt(){return{...L}}const vt={install:ft,uninstall:pt,isActive:ht,getConfig:bt},J="devlogger_persisted_logs",ae="devlogger_session_active",Ce={storage:"session",maxPersisted:500,debounceMs:100};let R=!1,S={...Ce},U=[],x=null,le=!1,W=null;function k(){try{return typeof window>"u"?null:S.storage==="local"?localStorage:sessionStorage}catch{return null}}function mt(){try{const t=k();return t?t.getItem(ae)==="active":!1}catch{return!1}}function yt(){try{const t=k();if(!t)return;t.setItem(ae,"active")}catch{}}function ke(){try{const t=k();if(!t)return;t.removeItem(ae)}catch{}}function $e(t){try{const e=k();if(!e)return;const n=t.slice(-S.maxPersisted),o=JSON.stringify(n);e.setItem(J,o)}catch(e){if(e instanceof DOMException&&e.name==="QuotaExceededError")try{const n=k();if(!n)return;const o=t.slice(-Math.floor(S.maxPersisted/2));n.setItem(J,JSON.stringify(o))}catch{}}}function Ee(){try{const t=k();if(!t)return[];const e=t.getItem(J);if(!e)return[];const n=JSON.parse(e);return Array.isArray(n)?n.filter(o=>o&&typeof o.id=="string"&&typeof o.timestamp=="number"&&typeof o.level=="string"&&typeof o.message=="string"):[]}catch{return[]}}function xt(){try{const t=k();if(!t)return;t.removeItem(J)}catch{}}function wt(t){U=t,x&&clearTimeout(x),x=setTimeout(()=>{$e(U),x=null},S.debounceMs)}function X(){try{x&&(clearTimeout(x),x=null),U.length>0&&$e(U),ke()}catch{}}function Lt(){if(!R)return;const t=f.getLogs();wt([...t])}function St(t={}){if(R){S={...S,...t};return}try{if(typeof window>"u")return;S={...Ce,...t},le=mt(),yt(),W=f.subscribe(Lt),window.addEventListener("beforeunload",X),window.addEventListener("pagehide",X),R=!0}catch{}}function Ct(){if(R)try{if(typeof window>"u")return;x&&(clearTimeout(x),x=null),W&&(W(),W=null),window.removeEventListener("beforeunload",X),window.removeEventListener("pagehide",X),ke(),R=!1}catch{}}function kt(){return le}function $t(){return Ee()}function Et(){try{const t=Ee();return t.length===0?0:(f.importLogs(t),t.length)}catch{return 0}}function Tt(){xt(),U=[],le=!1}function It(){return R}function Rt(){return{...S}}const _t={enable:St,disable:Ct,isActive:It,hadCrash:kt,getPersistedLogs:$t,rehydrate:Et,clear:Tt,getConfig:Rt},Te={captureFetch:!0,captureXHR:!0,includeHeaders:!1,includeBody:!1,includeResponse:!1,maxResponseLength:1e3,ignorePatterns:[],context:{}};let E=null,H=null,T=null,B=!1,v={...Te};function Ie(t){for(const e of v.ignorePatterns)if(typeof e=="string"){if(t.includes(e))return!0}else if(e.test(t))return!0;return!1}function Re(t){try{const e=new URL(t,window.location.origin);return{host:e.host,path:e.pathname+e.search,full:e.href}}catch{return{host:"unknown",path:t,full:t}}}function _e(t,e){return t.length<=e?t:t.slice(0,e)+"... (truncated)"}function Y(t){try{return JSON.parse(t)}catch{return t}}function Dt(){return async function(e,n){const o=typeof e=="string"?e:e instanceof URL?e.href:e.url;if(Ie(o))return E(e,n);const{host:r,path:s}=Re(o),c=n?.method||"GET",l=`${c} ${s}`,a=f.span(l,{...v.context,type:"fetch",method:c,host:r});if(a.info(`Request started: ${o}`),v.includeHeaders&&n?.headers&&a.debug("Request headers",n.headers),v.includeBody&&n?.body)try{const u=typeof n.body=="string"?Y(n.body):n.body;a.debug("Request body",u)}catch{a.debug("Request body","[Unable to parse]")}const d=performance.now();try{const u=await E(e,n),h=Math.round(performance.now()-d);if(u.ok){if(a.info(`Response: ${u.status} ${u.statusText} (${h}ms)`),v.includeResponse)try{const K=await u.clone().text(),m=Y(_e(K,v.maxResponseLength));a.debug("Response body",m)}catch{a.debug("Response body","[Unable to read]")}a.end()}else a.warn(`Response: ${u.status} ${u.statusText} (${h}ms)`),a.fail();return u}catch(u){const h=Math.round(performance.now()-d);throw a.error(`Request failed after ${h}ms`,u),a.fail(),u}}}function Mt(){H=XMLHttpRequest.prototype.open,T=XMLHttpRequest.prototype.send,XMLHttpRequest.prototype.open=function(t,e,n=!0,o,r){const s=typeof e=="string"?e:e.href;return this.__networkCapture={method:t,url:s,ignored:Ie(s)},H.call(this,t,e,n,o,r)},XMLHttpRequest.prototype.send=function(t){const e=this.__networkCapture;if(!e||e.ignored)return T.call(this,t);const{method:n,url:o}=e,{host:r,path:s}=Re(o),c=`${n} ${s}`,l=f.span(c,{...v.context,type:"xhr",method:n,host:r});if(l.info(`XHR Request started: ${o}`),v.includeBody&&t)try{const d=typeof t=="string"?Y(t):t;l.debug("Request body",d)}catch{l.debug("Request body","[Unable to parse]")}const a=performance.now();return this.addEventListener("load",()=>{const d=Math.round(performance.now()-a);if(this.status>=200&&this.status<400){if(l.info(`Response: ${this.status} ${this.statusText} (${d}ms)`),v.includeResponse&&this.responseText){const u=Y(_e(this.responseText,v.maxResponseLength));l.debug("Response body",u)}l.end()}else l.warn(`Response: ${this.status} ${this.statusText} (${d}ms)`),l.fail()}),this.addEventListener("error",()=>{const d=Math.round(performance.now()-a);l.error(`XHR Request failed after ${d}ms`),l.fail()}),this.addEventListener("abort",()=>{const d=Math.round(performance.now()-a);l.warn(`XHR Request aborted after ${d}ms`),l.fail()}),this.addEventListener("timeout",()=>{const d=Math.round(performance.now()-a);l.error(`XHR Request timeout after ${d}ms`),l.fail()}),T.call(this,t)}}function Bt(){H&&(XMLHttpRequest.prototype.open=H,H=null),T&&(XMLHttpRequest.prototype.send=T,T=null)}const Ot={install(t={}){try{if(B)return;v={...Te,...t},v.captureFetch&&typeof globalThis.fetch=="function"&&(E=globalThis.fetch,globalThis.fetch=Dt()),v.captureXHR&&typeof XMLHttpRequest<"u"&&Mt(),B=!0,f.debug("[NetworkCapture] Installed",{fetch:v.captureFetch,xhr:v.captureXHR})}catch(e){console.warn("[NetworkCapture] Install error:",e)}},uninstall(){try{if(!B)return;E&&(globalThis.fetch=E,E=null),Bt(),B=!1,f.debug("[NetworkCapture] Uninstalled")}catch(t){console.warn("[NetworkCapture] Uninstall error:",t)}},isActive(){return B},getConfig(){return{...v}},addIgnorePattern(t){v.ignorePatterns.push(t)}},Ft={debug:"#6e6e6e",info:"#3794ff",warn:"#cca700",error:"#f14c4c"},At={running:"#3794ff",success:"#4caf50",error:"#f14c4c"};class De{constructor(e){p(this,"container");p(this,"config");p(this,"canvas",null);p(this,"ctx",null);p(this,"intervalId",null);p(this,"tooltip",null);p(this,"spanBounds",new Map);p(this,"logBounds",new Map);const n=typeof e.container=="string"?document.querySelector(e.container):e.container;if(!n)throw new Error("Timeline: Container not found");this.container=n,this.config={container:n,timeWindow:e.timeWindow??6e4,refreshInterval:e.refreshInterval??1e3,showSpans:e.showSpans??!0,showLogs:e.showLogs??!0,height:e.height??200},this.init()}init(){this.container.innerHTML=`
1633
+ <div class="devlogger-timeline" style="
1634
+ position: relative;
1635
+ background: #1e1e1e;
1636
+ border: 1px solid #3c3c3c;
1637
+ border-radius: 4px;
1638
+ overflow: hidden;
1639
+ ">
1640
+ <div class="timeline-header" style="
1641
+ display: flex;
1642
+ justify-content: space-between;
1643
+ padding: 8px 12px;
1644
+ background: #252526;
1645
+ border-bottom: 1px solid #3c3c3c;
1646
+ font-family: 'SF Mono', monospace;
1647
+ font-size: 12px;
1648
+ color: #ccc;
1649
+ ">
1650
+ <span>Timeline</span>
1651
+ <div class="timeline-controls">
1652
+ <button data-window="10000" style="
1653
+ background: transparent;
1654
+ border: 1px solid #3c3c3c;
1655
+ color: #858585;
1656
+ padding: 2px 8px;
1657
+ border-radius: 3px;
1658
+ cursor: pointer;
1659
+ margin-left: 4px;
1660
+ font-size: 11px;
1661
+ ">10s</button>
1662
+ <button data-window="30000" style="
1663
+ background: transparent;
1664
+ border: 1px solid #3c3c3c;
1665
+ color: #858585;
1666
+ padding: 2px 8px;
1667
+ border-radius: 3px;
1668
+ cursor: pointer;
1669
+ margin-left: 4px;
1670
+ font-size: 11px;
1671
+ ">30s</button>
1672
+ <button data-window="60000" style="
1673
+ background: #0e639c;
1674
+ border: 1px solid #0e639c;
1675
+ color: white;
1676
+ padding: 2px 8px;
1677
+ border-radius: 3px;
1678
+ cursor: pointer;
1679
+ margin-left: 4px;
1680
+ font-size: 11px;
1681
+ ">1m</button>
1682
+ <button data-window="300000" style="
1683
+ background: transparent;
1684
+ border: 1px solid #3c3c3c;
1685
+ color: #858585;
1686
+ padding: 2px 8px;
1687
+ border-radius: 3px;
1688
+ cursor: pointer;
1689
+ margin-left: 4px;
1690
+ font-size: 11px;
1691
+ ">5m</button>
1692
+ </div>
1693
+ </div>
1694
+ <div class="timeline-canvas-container" style="position: relative;">
1695
+ <canvas class="timeline-canvas"></canvas>
1696
+ </div>
1697
+ <div class="timeline-tooltip" style="
1698
+ position: absolute;
1699
+ display: none;
1700
+ background: #333;
1701
+ border: 1px solid #555;
1702
+ padding: 8px;
1703
+ border-radius: 4px;
1704
+ font-family: 'SF Mono', monospace;
1705
+ font-size: 11px;
1706
+ color: #ccc;
1707
+ max-width: 300px;
1708
+ z-index: 1000;
1709
+ pointer-events: none;
1710
+ "></div>
1711
+ </div>
1712
+ `,this.canvas=this.container.querySelector(".timeline-canvas"),this.tooltip=this.container.querySelector(".timeline-tooltip"),this.canvas&&(this.ctx=this.canvas.getContext("2d"),this.resizeCanvas()),this.container.querySelectorAll("[data-window]").forEach(e=>{e.addEventListener("click",n=>{const o=n.currentTarget,r=parseInt(o.dataset.window||"60000",10);this.setTimeWindow(r),this.container.querySelectorAll("[data-window]").forEach(s=>{const c=s;c===o?(c.style.background="#0e639c",c.style.borderColor="#0e639c",c.style.color="white"):(c.style.background="transparent",c.style.borderColor="#3c3c3c",c.style.color="#858585")})})}),this.canvas&&(this.canvas.addEventListener("mousemove",this.handleMouseMove.bind(this)),this.canvas.addEventListener("mouseleave",this.handleMouseLeave.bind(this))),this.startRefresh()}resizeCanvas(){if(!this.canvas)return;const e=this.container.getBoundingClientRect();this.canvas.width=e.width-2,this.canvas.height=this.config.height,this.render()}startRefresh(){this.intervalId||(this.intervalId=window.setInterval(()=>this.render(),this.config.refreshInterval),this.render())}stopRefresh(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}setTimeWindow(e){this.config.timeWindow=e,this.render()}render(){if(!this.canvas||!this.ctx)return;const e=this.ctx,n=this.canvas.width,o=this.canvas.height,r=Date.now(),s=r-this.config.timeWindow;e.fillStyle="#1e1e1e",e.fillRect(0,0,n,o),this.spanBounds.clear(),this.logBounds.clear(),this.drawTimeGrid(e,n,o,s,r);const c=f.getLogs().filter(a=>a.timestamp>=s),l=f.getSpans().filter(a=>a.startTime>=s||a.endTime&&a.endTime>=s);this.config.showSpans&&this.drawSpans(e,l,n,o,s,r),this.config.showLogs&&this.drawLogs(e,c,n,o,s,r)}drawTimeGrid(e,n,o,r,s){const c=s-r,l=6;e.strokeStyle="#333",e.lineWidth=1,e.font="10px SF Mono, monospace",e.fillStyle="#666";for(let a=0;a<=l;a++){const d=a/l*n,u=r+c*a/l;e.beginPath(),e.moveTo(d,0),e.lineTo(d,o),e.stroke();const h=new Date(u),C=`${h.getMinutes().toString().padStart(2,"0")}:${h.getSeconds().toString().padStart(2,"0")}`;e.fillText(C,d+2,o-4)}}drawSpans(e,n,o,r,s,c){const l=c-s,a=20,d=4,u=20,h=n.filter(m=>!m.parentId);let C=u;const K=(m,_,zt=0)=>{const D=(m.startTime-s)/l*o,Me=((m.endTime||c)-s)/l*o,j=Math.max(Me-D,2),ce=At[m.status];e.fillStyle=ce+"40",e.fillRect(D,_,j,a),e.strokeStyle=ce,e.lineWidth=1,e.strokeRect(D,_,j,a),e.fillStyle="#ccc",e.font="10px SF Mono, monospace";const Be=m.duration?`${m.name} (${m.duration}ms)`:m.name;return e.fillText(Be,D+4,_+14,j-8),this.spanBounds.set(m.id,{x:D,y:_,w:j,h:a}),_+a+d};for(const m of h)C=K(m,C)}drawLogs(e,n,o,r,s,c){const l=c-s,a=8,d=30;for(const u of n){const h=(u.timestamp-s)/l*o,C=Ft[u.level];e.fillStyle=C,e.beginPath(),e.moveTo(h,r-d),e.lineTo(h-a/2,r-d-a),e.lineTo(h+a/2,r-d-a),e.closePath(),e.fill(),this.logBounds.set(u.id,{x:h,y:r-d-a/2,r:a})}}handleMouseMove(e){if(!this.canvas||!this.tooltip)return;const n=this.canvas.getBoundingClientRect(),o=e.clientX-n.left,r=e.clientY-n.top;for(const s of f.getSpans()){const c=this.spanBounds.get(s.id);if(c&&o>=c.x&&o<=c.x+c.w&&r>=c.y&&r<=c.y+c.h){this.showTooltip(e,`
1713
+ <strong>${s.name}</strong><br>
1714
+ Status: ${s.status}<br>
1715
+ ${s.duration!==void 0?`Duration: ${s.duration}ms`:"Running..."}<br>
1716
+ ${s.context?`Context: ${JSON.stringify(s.context)}`:""}
1717
+ `);return}}for(const s of f.getLogs()){const c=this.logBounds.get(s.id);if(c&&Math.sqrt((o-c.x)**2+(r-c.y)**2)<=c.r){this.showTooltip(e,`
1718
+ <strong>[${s.level.toUpperCase()}]</strong> ${s.message}<br>
1719
+ <small>${new Date(s.timestamp).toISOString()}</small>
1720
+ `);return}}this.hideTooltip()}handleMouseLeave(){this.hideTooltip()}showTooltip(e,n){this.tooltip&&(this.tooltip.innerHTML=n,this.tooltip.style.display="block",this.tooltip.style.left=`${e.offsetX+10}px`,this.tooltip.style.top=`${e.offsetY+10}px`)}hideTooltip(){this.tooltip&&(this.tooltip.style.display="none")}destroy(){this.stopRefresh(),this.container.innerHTML=""}}function Ht(t){return new De(t)}const Nt="0.1.0";exports.DevLoggerUI=F;exports.ErrorCapture=vt;exports.LogPersistence=_t;exports.NetworkCapture=Ot;exports.Timeline=De;exports.VERSION=Nt;exports.computeDiff=oe;exports.createDiffResult=te;exports.createTimeline=Ht;exports.formatValue=ee;exports.hasChanges=Ae;exports.logger=f;
1721
+ //# sourceMappingURL=index.cjs.map