devlog-ui 0.1.7 → 1.0.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/README.md CHANGED
@@ -71,6 +71,9 @@ logger.configure({
71
71
  maxLogs: 1000, // Max logs in memory (FIFO rotation)
72
72
  minLevel: "debug", // Minimum level: 'debug' | 'info' | 'warn' | 'error'
73
73
  enabled: true, // Enable/disable logging
74
+ shortcutAction: "toggle", // Ctrl+Shift+L: 'toggle' | 'popout'
75
+ showToggleButton: true, // Show the floating toggle button
76
+ spanCollapsed: false, // Collapse span groups by default
74
77
  });
75
78
  ```
76
79
 
@@ -561,6 +564,9 @@ interface LoggerConfig {
561
564
  maxLogs?: number;
562
565
  minLevel?: LogLevel;
563
566
  enabled?: boolean;
567
+ shortcutAction?: "toggle" | "popout";
568
+ showToggleButton?: boolean;
569
+ spanCollapsed?: boolean;
564
570
  }
565
571
 
566
572
  interface ExportOptions {
package/dist/index.cjs CHANGED
@@ -1,9 +1,9 @@
1
- "use strict";var Fe=Object.defineProperty;var Oe=(t,e,n)=>e in t?Fe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var p=(t,e,n)=>Oe(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 F(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,r)=>F(n,e[r]));if(w(t)&&w(e)){const n=Object.keys(t),r=Object.keys(e);return n.length!==r.length?!1:n.every(o=>F(t[o],e[o]))}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 re(t,e,n="",r=!1){const o=[];if(!w(t)&&!w(e))return F(t,e)?r&&o.push({path:n||"(root)",type:"unchanged",oldValue:t,newValue:e}):o.push({path:n||"(root)",type:"changed",oldValue:t,newValue:e}),o;if(!w(t))return o.push({path:n||"(root)",type:"changed",oldValue:t,newValue:e}),o;if(!w(e))return o.push({path:n||"(root)",type:"changed",oldValue:t,newValue:e}),o;const s=new Set([...Object.keys(t),...Object.keys(e)]);for(const c of s){const a=n?`${n}.${c}`:c,l=t[c],d=e[c],u=c in t,h=c in e;!u&&h?o.push({path:a,type:"added",newValue:d}):u&&!h?o.push({path:a,type:"removed",oldValue:l}):w(l)&&w(d)?o.push(...re(l,d,a,r)):Array.isArray(l)&&Array.isArray(d)?F(l,d)?r&&o.push({path:a,type:"unchanged",oldValue:l,newValue:d}):o.push({path:a,type:"changed",oldValue:l,newValue:d}):F(l,d)?r&&o.push({path:a,type:"unchanged",oldValue:l,newValue:d}):o.push({path:a,type:"changed",oldValue:l,newValue:d})}return o}function te(t,e){const n=re(t,e,"",!1),r={added:0,removed:0,changed:0,unchanged:0};for(const o of n)r[o.type]++;return{changes:n,summary:r}}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 Pe={maxLogs:1e3,persist:!1,minLevel:"debug",enabled:Ne()};function ze(){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 r=e[n];if(!r||r.includes("logger.ts")||r.includes("logger.js"))continue;const o=r.match(/at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?/);if(o)return{function:o[1]||void 0,file:ge(o[2]||"unknown"),line:parseInt(o[3]||"0",10),column:parseInt(o[4]||"0",10)};const s=r.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,r,o){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:o,context:r,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 r={...this._event.context,...n};return new G(this.logger,e,r,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 oe{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 oe(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",{...Pe});p(this,"sessionId");p(this,"globalContext",{});this.sessionId=ze()}log(e,n,r,o,s){try{if(!this.config.enabled||de[e]<de[this.config.minLevel])return;const c=Object.keys(this.globalContext).length>0||o?{...this.globalContext,...o}:void 0,a={id:qe(),timestamp:Date.now(),level:e,message:String(n),data:this.safeCloneData(r),source:ye(),sessionId:this.sessionId,context:c,spanId:s};this.store(a),this.notify(a)}catch(c){typeof console<"u"&&console.warn&&console.warn("[DevLogger] Internal error:",c)}}logWithContext(e,n,r,o){this.log(e,n,r,o)}logWithSpan(e,n,r,o,s){this.log(e,n,r,s,o)}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(r=>this.safeClone(r,n));try{const r={};for(const o of Object.keys(e))r[o]=this.safeClone(e[o],n);return r}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(o=>o.id)),r=e.filter(o=>!n.has(o.id));for(this.logs=[...r,...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 oe(this,e)}exportLogs(e={}){try{const{format:n="json",lastMs:r,levels:o,search:s,pretty:c=!0}=e;let a=[...this.logs];if(r!==void 0&&r>0){const l=Date.now()-r;a=a.filter(d=>d.timestamp>=l)}if(o&&o.length>0){const l=new Set(o);a=a.filter(d=>l.has(d.level))}if(s){const l=s.toLowerCase();a=a.filter(d=>d.message.toLowerCase().includes(l)||JSON.stringify(d.data).toLowerCase().includes(l))}return n==="text"?this.formatLogsAsText(a):c?JSON.stringify(a,null,2):JSON.stringify(a)}catch{return"[]"}}formatLogsAsText(e){return e.map(n=>{const r=new Date(n.timestamp).toISOString(),o=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(", ")}]`:"",a=n.spanId?` (span: ${n.spanId})`:"",l=n.data.length>0?`
3
- Data: ${JSON.stringify(n.data)}`:"";return`[${r}] ${o} ${n.message}${c}${a}
4
- Source: ${s}${l}`}).join(`
1
+ "use strict";var Oe=Object.defineProperty;var Ne=(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)=>Ne(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var Z=typeof document<"u"?document.currentScript:null;const ue={debug:0,info:1,warn:2,error:3};function w(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function A(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)=>A(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=>A(t[r],e[r]))}return!1}function te(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(te).join(", ")}]`:`[${t.length} items]`;if(w(t)){const e=Object.keys(t);return e.length===0?"{}":e.length<=2?`{${e.map(n=>`${n}: ${te(t[n])}`).join(", ")}}`:`{${e.length} keys}`}return String(t)}function re(t,e,n="",o=!1){const r=[];if(!w(t)&&!w(e))return A(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 a of s){const l=n?`${n}.${a}`:a,c=t[a],d=e[a],f=a in t,h=a in e;!f&&h?r.push({path:l,type:"added",newValue:d}):f&&!h?r.push({path:l,type:"removed",oldValue:c}):w(c)&&w(d)?r.push(...re(c,d,l,o)):Array.isArray(c)&&Array.isArray(d)?A(c,d)?o&&r.push({path:l,type:"unchanged",oldValue:c,newValue:d}):r.push({path:l,type:"changed",oldValue:c,newValue:d}):A(c,d)?o&&r.push({path:l,type:"unchanged",oldValue:c,newValue:d}):r.push({path:l,type:"changed",oldValue:c,newValue:d})}return r}function ne(t,e){const n=re(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 ze(t){return t.summary.added>0||t.summary.removed>0||t.summary.changed>0}const Pe={BASE_URL:"/",DEV:!1,MODE:"production",PROD:!0,SSR:!1};function qe(){try{if(typeof{url:typeof document>"u"?require("url").pathToFileURL(__filename).href:Z&&Z.tagName.toUpperCase()==="SCRIPT"&&Z.src||new URL("index.cjs",document.baseURI).href}<"u"&&Pe)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 Ue={maxLogs:1e3,persist:!1,minLevel:"debug",enabled:qe(),shortcutAction:"toggle",showToggleButton:!0,spanCollapsed:!1};function Ve(){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 je(){return`log_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function we(){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:fe(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:fe(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 fe(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 We(){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:We(),name:n,startTime:Date.now(),status:"running",parentId:r,context:o,source:we(),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 se{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 se(this.logger,{...this.context,...e})}}class Ge{constructor(){p(this,"logs",[]);p(this,"spans",new Map);p(this,"subscribers",new Set);p(this,"spanSubscribers",new Set);p(this,"config",{...Ue});p(this,"sessionId");p(this,"globalContext",{});this.sessionId=Ve()}log(e,n,o,r,s){try{if(!this.config.enabled||ue[e]<ue[this.config.minLevel])return;const a=Object.keys(this.globalContext).length>0||r?{...this.globalContext,...r}:void 0,l={id:je(),timestamp:Date.now(),level:e,message:String(n),data:this.safeCloneData(o),source:we(),sessionId:this.sessionId,context:a,spanId:s};this.store(l),this.notify(l)}catch(a){typeof console<"u"&&console.warn&&console.warn("[DevLogger] Internal error:",a)}}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 se(this,e)}exportLogs(e={}){try{const{format:n="json",lastMs:o,levels:r,search:s,pretty:a=!0}=e;let l=[...this.logs];if(o!==void 0&&o>0){const c=Date.now()-o;l=l.filter(d=>d.timestamp>=c)}if(r&&r.length>0){const c=new Set(r);l=l.filter(d=>c.has(d.level))}if(s){const c=s.toLowerCase();l=l.filter(d=>d.message.toLowerCase().includes(c)||JSON.stringify(d.data).toLowerCase().includes(c))}return n==="text"?this.formatLogsAsText(l):a?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}`,a=n.context?` [${Object.entries(n.context).map(([d,f])=>`${d}=${f}`).join(", ")}]`:"",l=n.spanId?` (span: ${n.spanId})`:"",c=n.data.length>0?`
3
+ Data: ${JSON.stringify(n.data)}`:"";return`[${o}] ${r} ${n.message}${a}${l}
4
+ Source: ${s}${c}`}).join(`
5
5
 
6
- `)}async copyLogs(e={}){try{const n=this.exportLogs(e);return await navigator.clipboard.writeText(n),!0}catch{return!1}}diff(e,n,r,o="info"){try{const s=te(n,r);return this.log(o,e,[{__type:"Diff",diff:s,oldObj:this.safeClone(n),newObj:this.safeClone(r)}]),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=`
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=ne(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 ne(e,n)}catch{return{changes:[],summary:{added:0,removed:0,changed:0,unchanged:0}}}}}const u=new Ge,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
7
  :host {
8
8
  --bg-primary: ${g.bgPrimary};
9
9
  --bg-secondary: ${g.bgSecondary};
@@ -221,6 +221,90 @@
221
221
  padding-left: 10px;
222
222
  }
223
223
 
224
+ .span-group {
225
+ border-bottom: 1px solid var(--border);
226
+ }
227
+
228
+ .span-header {
229
+ display: flex;
230
+ align-items: center;
231
+ gap: 8px;
232
+ padding: 8px 12px;
233
+ background: var(--bg-secondary);
234
+ cursor: pointer;
235
+ }
236
+
237
+ .span-header:hover {
238
+ background: var(--bg-hover);
239
+ }
240
+
241
+ .span-toggle {
242
+ background: transparent;
243
+ border: none;
244
+ color: var(--text-muted);
245
+ cursor: pointer;
246
+ padding: 2px;
247
+ }
248
+
249
+ .span-toggle::before {
250
+ content: '▾';
251
+ display: inline-block;
252
+ transition: transform 0.15s ease;
253
+ }
254
+
255
+ .span-group.collapsed .span-toggle::before {
256
+ transform: rotate(-90deg);
257
+ }
258
+
259
+ .span-title {
260
+ font-weight: 600;
261
+ font-size: 12px;
262
+ color: var(--text-primary);
263
+ }
264
+
265
+ .span-status {
266
+ font-size: 10px;
267
+ text-transform: uppercase;
268
+ padding: 2px 6px;
269
+ border-radius: 3px;
270
+ color: var(--text-primary);
271
+ background: var(--bg-primary);
272
+ }
273
+
274
+ .span-status-running {
275
+ color: ${g.levelInfo};
276
+ background: rgba(55, 148, 255, 0.15);
277
+ }
278
+
279
+ .span-status-success {
280
+ color: ${g.levelInfo};
281
+ background: rgba(55, 148, 255, 0.15);
282
+ }
283
+
284
+ .span-status-error {
285
+ color: ${g.levelError};
286
+ background: rgba(241, 76, 76, 0.15);
287
+ }
288
+
289
+ .span-duration {
290
+ color: var(--text-muted);
291
+ font-size: 11px;
292
+ }
293
+
294
+ .span-count {
295
+ margin-left: auto;
296
+ color: var(--text-secondary);
297
+ font-size: 11px;
298
+ }
299
+
300
+ .span-logs {
301
+ padding-left: 4px;
302
+ }
303
+
304
+ .span-group.collapsed .span-logs {
305
+ display: none;
306
+ }
307
+
224
308
  .log-message {
225
309
  color: var(--text-primary);
226
310
  word-break: break-word;
@@ -544,31 +628,31 @@
544
628
  .log-data-diff .log-data-toggle {
545
629
  color: ${g.levelInfo};
546
630
  }
547
- `;function We(t){const e=new Date(t),n=e.getHours().toString().padStart(2,"0"),r=e.getMinutes().toString().padStart(2,"0"),o=e.getSeconds().toString().padStart(2,"0"),s=e.getMilliseconds().toString().padStart(3,"0");return`${n}:${r}:${o}.${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 E(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(E).join(", ")}]`:`[${t.length} items]`;if(typeof t=="object"){const e=Object.keys(t);return e.length===0?"{}":e.length<=2?`{${e.map(n=>`${n}: ${E(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 r="";return t.type==="added"?r=E(t.newValue):t.type==="removed"?r=E(t.oldValue):t.type==="changed"&&(r=`${E(t.oldValue)} → ${E(t.newValue)}`),`<div class="diff-entry ${e}"><span class="diff-icon">${n}</span> <span class="diff-path">${t.path}</span>: <span class="diff-value">${r}</span></div>`}function Je(t){const{diff:e}=t,{summary:n,changes:r}=e;let o='<div class="diff-container">';if(o+='<div class="diff-summary">',n.added>0&&(o+=`<span class="diff-count diff-added">+${n.added}</span>`),n.removed>0&&(o+=`<span class="diff-count diff-removed">-${n.removed}</span>`),n.changed>0&&(o+=`<span class="diff-count diff-changed">~${n.changed}</span>`),n.added===0&&n.removed===0&&n.changed===0&&(o+='<span class="diff-count diff-unchanged">No changes</span>'),o+="</div>",r.length>0){o+='<div class="diff-changes">';for(const s of r)o+=Ge(s);o+="</div>"}return o+="</div>",o}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,r=Ye(t.data),o=t.context&&Object.keys(t.context).length>0,s=`data-${t.id}`,c=()=>{if(!n)return"";if(r){const a=t.data.find(xe);if(a)return`
631
+ `;function Xe(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 pe(t){return t.file==="unknown"?"unknown":`${t.file}:${t.line}`}function Le(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 Ye(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 Ke(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+=Ye(s);r+="</div>"}return r+="</div>",r}function Qe(t){if(t.length===0)return"";try{return t.map(e=>typeof e=="string"?e:JSON.stringify(e,null,2)).join(`
632
+ `)}catch{return"[Unable to display data]"}}function Ze(t){return t.some(Le)}function B(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function et(t){return!t||Object.keys(t).length===0?"":Object.entries(t).map(([e,n])=>`${e}=${n}`).join(" · ")}function J(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=Ze(t.data),r=t.context&&Object.keys(t.context).length>0,s=`data-${t.id}`,a=()=>{if(!n)return"";if(o){const l=t.data.find(Le);if(l)return`
549
633
  <div class="log-data log-data-diff">
550
634
  <button class="log-data-toggle" data-target="${s}">
551
635
  diff
552
636
  </button>
553
- <div class="log-data-content" id="${s}">${Je(a)}</div>
637
+ <div class="log-data-content" id="${s}">${Ke(l)}</div>
554
638
  </div>
555
639
  `}return`
556
640
  <div class="log-data">
557
641
  <button class="log-data-toggle" data-target="${s}">
558
642
  ${t.data.length} item${t.data.length>1?"s":""}
559
643
  </button>
560
- <pre class="log-data-content" id="${s}">${M(Xe(t.data))}</pre>
644
+ <pre class="log-data-content" id="${s}">${B(Qe(t.data))}</pre>
561
645
  </div>
562
646
  `};if(e.innerHTML=`
563
647
  <div class="log-entry-header">
564
648
  <span class="log-level log-level-${t.level}">${t.level}</span>
565
- <span class="log-time">${We(t.timestamp)}</span>
566
- ${o?`<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>
649
+ <span class="log-time">${Xe(t.timestamp)}</span>
650
+ ${r?`<span class="log-context" title="Context">${B(et(t.context))}</span>`:""}
651
+ <span class="log-source" title="${B(pe(t.source))}">${B(pe(t.source))}</span>
568
652
  </div>
569
- <div class="log-message">${M(t.message)}</div>
570
- ${c()}
571
- `,n){const a=e.querySelector(".log-data-toggle"),l=e.querySelector(`#${s}`);a&&l&&a.addEventListener("click",()=>{a.classList.toggle("expanded"),l.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 r={type:e,payload:n,senderId:this.senderId,timestamp:Date.now()};this.channel.postMessage(r)}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=ot();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 rt(){return b!==null&&!b.closed}function ot(){return`<!DOCTYPE html>
653
+ <div class="log-message">${B(t.message)}</div>
654
+ ${a()}
655
+ `,n){const l=e.querySelector(".log-data-toggle"),c=e.querySelector(`#${s}`);l&&c&&l.addEventListener("click",()=>{l.classList.toggle("expanded"),c.classList.toggle("visible")})}return e}function tt(){const t=document.createElement("div");return t.className="devlogger-empty",t.textContent="No logs yet...",t}const nt="devlogger-sync";function ot(){return`sender_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}class rt{constructor(){p(this,"channel",null);p(this,"handlers",new Set);p(this,"senderId");p(this,"isConnected",!1);this.senderId=ot(),this.connect()}connect(){try{if(typeof BroadcastChannel>"u"){console.warn("[DevLogger] BroadcastChannel not supported");return}this.channel=new BroadcastChannel(nt),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 rt,he=500,be=700;let b=null;function st(){try{if(b&&!b.closed)return b.focus(),b;const t=Math.max(0,(screen.width-he)/2),e=Math.max(0,(screen.height-be)/2),n=at();return b=window.open("","devlogger-popout",`width=${he},height=${be},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(u.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 me(){try{b&&!b.closed&&b.close(),b=null}catch{}}function it(){return b!==null&&!b.closed}function at(){return`<!DOCTYPE html>
572
656
  <html lang="en">
573
657
  <head>
574
658
  <meta charset="UTF-8">
@@ -1630,8 +1714,8 @@
1630
1714
  connect();
1631
1715
  <\/script>
1632
1716
  </body>
1633
- </html>`}function P(){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(),r=t.message.toLowerCase().includes(n),o=JSON.stringify(t.data).toLowerCase().includes(n);if(!r&&!o)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 r=ie(t);return`
1634
- <div class="filter-bar ${r?"filter-active":""}">
1717
+ </html>`}function z(){return{levels:new Set(["debug","info","warn","error"]),search:"",file:""}}function lt(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=>lt(n,e))}function ie(t){return!(t.levels.size===4)||t.search!==""||t.file!==""}function ct(t,e,n){const o=ie(t);return`
1718
+ <div class="filter-bar ${o?"filter-active":""}">
1635
1719
  <div class="filter-row">
1636
1720
  <div class="filter-levels">
1637
1721
  <button class="filter-level-btn ${t.levels.has("debug")?"active":""}" data-level="debug" title="Debug">D</button>
@@ -1640,39 +1724,45 @@
1640
1724
  <button class="filter-level-btn ${t.levels.has("error")?"active":""}" data-level="error" title="Error">E</button>
1641
1725
  </div>
1642
1726
  <div class="filter-search">
1643
- <input type="text" class="filter-input" placeholder="Search logs..." value="${be(t.search)}" data-filter="search">
1727
+ <input type="text" class="filter-input" placeholder="Search logs..." value="${ve(t.search)}" data-filter="search">
1644
1728
  </div>
1645
1729
  <div class="filter-file">
1646
- <input type="text" class="filter-input filter-file-input" placeholder="Filter by file..." value="${be(t.file)}" data-filter="file">
1730
+ <input type="text" class="filter-input filter-file-input" placeholder="Filter by file..." value="${ve(t.file)}" data-filter="file">
1647
1731
  </div>
1648
- ${r?'<button class="filter-clear-btn" title="Clear filters">✕</button>':""}
1732
+ ${o?'<button class="filter-clear-btn" title="Clear filters">✕</button>':""}
1649
1733
  </div>
1650
- ${r?`<div class="filter-status">Showing ${n} of ${e} logs</div>`:""}
1734
+ ${o?`<div class="filter-status">Showing ${n} of ${e} logs</div>`:""}
1651
1735
  </div>
1652
- `}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:P()};function lt(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",()=>O.toggle()),t.appendChild(n),i.toggleBtn=n;const r=document.createElement("div");r.className="devlogger-container hidden";const o=f.getLogs(),s=V(o,i.filter);r.innerHTML=`
1736
+ `}function ve(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}const ee={key:"l",ctrlKey:!0,shiftKey:!0};function ae(){return u.getConfig().shortcutAction==="popout"?"popout":"toggle"}function dt(){return ae()==="popout"?"Ctrl+Shift+L to open pop-out":"Ctrl+Shift+L to toggle"}function gt(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function Se(t){return t.duration===void 0?"running":`${Math.round(t.duration)}ms`}function ye(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,spanUnsubscribe:null,channelUnsubscribe:null,filter:z()};function ut(t){const e=document.createElement("style");if(e.textContent=Je,t.appendChild(e),u.getConfig().showToggleButton){const s=document.createElement("button");s.className="devlogger-toggle",s.innerHTML="📋",s.title=ae()==="popout"?"Toggle DevLogger":"Toggle DevLogger (Ctrl+Shift+L)",s.addEventListener("click",()=>T.toggle()),t.appendChild(s),i.toggleBtn=s}const n=document.createElement("div");n.className="devlogger-container hidden";const o=u.getLogs(),r=V(o,i.filter);n.innerHTML=`
1653
1737
  <div class="devlogger-header">
1654
1738
  <div class="devlogger-title">
1655
1739
  DevLogger
1656
- <span class="devlogger-badge">${s.length}</span>
1740
+ <span class="devlogger-badge">${r.length}</span>
1657
1741
  </div>
1658
1742
  <div class="devlogger-actions">
1659
1743
  <button class="devlogger-btn" data-action="copy-json" title="Copy as JSON">JSON</button>
1660
1744
  <button class="devlogger-btn" data-action="copy-text" title="Copy as Text">TXT</button>
1661
1745
  <button class="devlogger-btn" data-action="clear" title="Clear logs">Clear</button>
1662
1746
  <button class="devlogger-btn devlogger-btn-primary" data-action="popout" title="Open in new window">Pop-out</button>
1663
- <button class="devlogger-btn" data-action="close" title="Close (Ctrl+Shift+L)">✕</button>
1747
+ <button class="devlogger-btn" data-action="close" title="Close">✕</button>
1664
1748
  </div>
1665
1749
  </div>
1666
1750
  <div class="filter-bar-container"></div>
1667
1751
  <div class="devlogger-logs"></div>
1668
1752
  <div class="devlogger-footer">
1669
1753
  <span class="devlogger-log-count">${o.length} logs</span>
1670
- <span class="devlogger-shortcut">Ctrl+Shift+L to toggle</span>
1754
+ <span class="devlogger-shortcut">${dt()}</span>
1671
1755
  </div>
1672
- `,i.badge=r.querySelector(".devlogger-badge"),i.logsList=r.querySelector(".devlogger-logs"),i.filterBar=r.querySelector(".filter-bar-container"),r.querySelectorAll("[data-action]").forEach(c=>{c.addEventListener("click",a=>{const l=a.currentTarget.dataset.action,d=a.currentTarget;switch(l){case"clear":f.clear(),N.sendClear(),y();break;case"popout":O.popout();break;case"close":O.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(r),i.container=r,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),at()}function at(){if(!i.filterBar)return;i.filterBar.querySelectorAll(".filter-level-btn").forEach(r=>{r.addEventListener("click",o=>{const s=o.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",r=>{i.filter.search=r.target.value,ne(),y()});const e=i.filterBar.querySelector('[data-filter="file"]');e&&e.addEventListener("input",r=>{i.filter.file=r.target.value,ne(),y()});const n=i.filterBar.querySelector(".filter-clear-btn");n&&n.addEventListener("click",()=>{i.filter=P(),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=`
1756
+ `,i.badge=n.querySelector(".devlogger-badge"),i.logsList=n.querySelector(".devlogger-logs"),i.filterBar=n.querySelector(".filter-bar-container"),n.querySelectorAll("[data-action]").forEach(s=>{s.addEventListener("click",a=>{const l=a.currentTarget.dataset.action,c=a.currentTarget;switch(l){case"clear":u.clear(),N.sendClear(),y();break;case"popout":T.popout();break;case"close":T.close();break;case"copy-json":u.copyLogs({format:"json"}).then(d=>{ye(c,d)});break;case"copy-text":u.copyLogs({format:"text"}).then(d=>{ye(c,d)});break}})}),t.appendChild(n),i.container=n,R(),y()}function R(){if(!i.filterBar)return;const t=u.getLogs(),e=V(t,i.filter);i.filterBar.innerHTML=ct(i.filter,t.length,e.length),ft()}function ft(){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),R(),y()})});const t=i.filterBar.querySelector('[data-filter="search"]');t&&t.addEventListener("input",o=>{i.filter.search=o.target.value,oe(),y()});const e=i.filterBar.querySelector('[data-filter="file"]');e&&e.addEventListener("input",o=>{i.filter.file=o.target.value,oe(),y()});const n=i.filterBar.querySelector(".filter-clear-btn");n&&n.addEventListener("click",()=>{i.filter=z(),R(),y()})}function y(){if(!i.logsList)return;const t=u.getLogs(),e=V(t,i.filter);if(i.logsList.innerHTML="",t.length===0)i.logsList.appendChild(tt());else if(e.length===0){const n=document.createElement("div");n.className="devlogger-no-results",n.innerHTML=`
1673
1757
  <span class="devlogger-no-results-icon">🔍</span>
1674
1758
  <span class="devlogger-no-results-text">No logs match your filter</span>
1675
- `,i.logsList.appendChild(n)}else{const n=document.createDocumentFragment();for(const r of e)n.appendChild(we(r));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(o=>o.id===t.id)){const o=i.logsList.querySelector(".devlogger-empty, .devlogger-no-results");o&&o.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),r=i.filterBar.querySelector(".filter-bar");r&&r.classList.toggle("filter-active",n);let o=i.filterBar.querySelector(".filter-status");if(n){o||(o=document.createElement("div"),o.className="filter-status",r?.appendChild(o)),o.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=P(),I(),y()}),i.filterBar.querySelector(".filter-row")?.appendChild(s))}else o?.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(),O.toggle())}function dt(t){switch(t.type){case"CLEAR_LOGS":f.clear(),y();break;case"SYNC_REQUEST":N.sendSyncResponse(f.getLogs());break}}const O={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,lt(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 rt()},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=P(),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=P()}catch{}},isVisible(){return i.visible},isInitialized(){return i.initialized}},Se={captureErrors:!0,captureRejections:!0,errorPrefix:"[Uncaught Error]",rejectionPrefix:"[Unhandled Rejection]"};let z=!1,L={...Se},q=null,A=null;function gt(t,e,n,r,o){try{const s=o||(t instanceof ErrorEvent?t.error:null),c=s?.message||String(t),a=s?.stack;f.error(`${L.errorPrefix} ${c}`,{source:e||"unknown",line:n||0,column:r||0,stack:a,originalError:s?{name:s.name,message:s.message,stack:s.stack}:void 0})}catch{}if(q)try{return q(t,e,n,r,o)??!1}catch{}return!1}function ut(t){try{const e=t.reason;let n,r={};e instanceof Error?(n=e.message,r={name:e.name,message:e.message,stack:e.stack}):typeof e=="string"?n=e:(n="Unknown rejection reason",r={reason:e}),f.error(`${L.rejectionPrefix} ${n}`,r)}catch{}}function ft(t={}){if(z){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)),z=!0}catch{}}function pt(){if(z)try{if(typeof window>"u")return;window.onerror=q,A&&window.removeEventListener("unhandledrejection",A),q=null,A=null,z=!1}catch{}}function ht(){return z}function bt(){return{...L}}const vt={install:ft,uninstall:pt,isActive:ht,getConfig:bt},J="devlogger_persisted_logs",le="devlogger_session_active",Ce={storage:"session",maxPersisted:500,debounceMs:100};let R=!1,S={...Ce},U=[],x=null,ae=!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(le)==="active":!1}catch{return!1}}function yt(){try{const t=k();if(!t)return;t.setItem(le,"active")}catch{}}function ke(){try{const t=k();if(!t)return;t.removeItem(le)}catch{}}function Ee(t){try{const e=k();if(!e)return;const n=t.slice(-S.maxPersisted),r=JSON.stringify(n);e.setItem(J,r)}catch(e){if(e instanceof DOMException&&e.name==="QuotaExceededError")try{const n=k();if(!n)return;const r=t.slice(-Math.floor(S.maxPersisted/2));n.setItem(J,JSON.stringify(r))}catch{}}}function $e(){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(r=>r&&typeof r.id=="string"&&typeof r.timestamp=="number"&&typeof r.level=="string"&&typeof r.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(()=>{Ee(U),x=null},S.debounceMs)}function X(){try{x&&(clearTimeout(x),x=null),U.length>0&&Ee(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},ae=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 ae}function Et(){return $e()}function $t(){try{const t=$e();return t.length===0?0:(f.importLogs(t),t.length)}catch{return 0}}function Tt(){xt(),U=[],ae=!1}function It(){return R}function Rt(){return{...S}}const _t={enable:St,disable:Ct,isActive:It,hadCrash:kt,getPersistedLogs:Et,rehydrate:$t,clear:Tt,getConfig:Rt},Te={captureFetch:!0,captureXHR:!0,includeHeaders:!1,includeBody:!1,includeResponse:!1,maxResponseLength:1e3,ignorePatterns:[],context:{}};let $=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 r=typeof e=="string"?e:e instanceof URL?e.href:e.url;if(Ie(r))return $(e,n);const{host:o,path:s}=Re(r),c=n?.method||"GET",a=`${c} ${s}`,l=f.span(a,{...v.context,type:"fetch",method:c,host:o});if(l.info(`Request started: ${r}`),v.includeHeaders&&n?.headers&&l.debug("Request headers",n.headers),v.includeBody&&n?.body)try{const u=typeof n.body=="string"?Y(n.body):n.body;l.debug("Request body",u)}catch{l.debug("Request body","[Unable to parse]")}const d=performance.now();try{const u=await $(e,n),h=Math.round(performance.now()-d);if(u.ok){if(l.info(`Response: ${u.status} ${u.statusText} (${h}ms)`),v.includeResponse)try{const K=await u.clone().text(),m=Y(_e(K,v.maxResponseLength));l.debug("Response body",m)}catch{l.debug("Response body","[Unable to read]")}l.end()}else l.warn(`Response: ${u.status} ${u.statusText} (${h}ms)`),l.fail();return u}catch(u){const h=Math.round(performance.now()-d);throw l.error(`Request failed after ${h}ms`,u),l.fail(),u}}}function Mt(){H=XMLHttpRequest.prototype.open,T=XMLHttpRequest.prototype.send,XMLHttpRequest.prototype.open=function(t,e,n=!0,r,o){const s=typeof e=="string"?e:e.href;return this.__networkCapture={method:t,url:s,ignored:Ie(s)},H.call(this,t,e,n,r,o)},XMLHttpRequest.prototype.send=function(t){const e=this.__networkCapture;if(!e||e.ignored)return T.call(this,t);const{method:n,url:r}=e,{host:o,path:s}=Re(r),c=`${n} ${s}`,a=f.span(c,{...v.context,type:"xhr",method:n,host:o});if(a.info(`XHR Request started: ${r}`),v.includeBody&&t)try{const d=typeof t=="string"?Y(t):t;a.debug("Request body",d)}catch{a.debug("Request body","[Unable to parse]")}const l=performance.now();return this.addEventListener("load",()=>{const d=Math.round(performance.now()-l);if(this.status>=200&&this.status<400){if(a.info(`Response: ${this.status} ${this.statusText} (${d}ms)`),v.includeResponse&&this.responseText){const u=Y(_e(this.responseText,v.maxResponseLength));a.debug("Response body",u)}a.end()}else a.warn(`Response: ${this.status} ${this.statusText} (${d}ms)`),a.fail()}),this.addEventListener("error",()=>{const d=Math.round(performance.now()-l);a.error(`XHR Request failed after ${d}ms`),a.fail()}),this.addEventListener("abort",()=>{const d=Math.round(performance.now()-l);a.warn(`XHR Request aborted after ${d}ms`),a.fail()}),this.addEventListener("timeout",()=>{const d=Math.round(performance.now()-l);a.error(`XHR Request timeout after ${d}ms`),a.fail()}),T.call(this,t)}}function Bt(){H&&(XMLHttpRequest.prototype.open=H,H=null),T&&(XMLHttpRequest.prototype.send=T,T=null)}const Ft={install(t={}){try{if(B)return;v={...Te,...t},v.captureFetch&&typeof globalThis.fetch=="function"&&($=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;$&&(globalThis.fetch=$,$=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)}},Ot={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=`
1759
+ `,i.logsList.appendChild(n)}else{const n=document.createDocumentFragment(),o=new Map,r=new Map,s=new Map;for(const a of e){if(!a.spanId){n.appendChild(J(a));continue}let l=o.get(a.spanId);if(!l){const d=u.getSpan(a.spanId);l=ke(a.spanId,d),o.set(a.spanId,l);const f=l.querySelector(".span-logs");f&&r.set(a.spanId,f),n.appendChild(l)}const c=r.get(a.spanId);c&&c.appendChild(J(a)),s.set(a.spanId,(s.get(a.spanId)??0)+1)}for(const[a,l]of s){const d=o.get(a)?.querySelector(".span-count");d&&(d.textContent=`${l} log${l===1?"":"s"}`)}i.logsList.appendChild(n)}Ce(e.length,t.length),le()}function pt(t){if(!i.logsList)return;const e=u.getLogs(),n=V(e,i.filter);if(n.some(r=>r.id===t.id)){const r=i.logsList.querySelector(".devlogger-empty, .devlogger-no-results");if(r&&r.remove(),t.spanId){let s=i.logsList.querySelector(`.span-group[data-span-id="${t.spanId}"]`);if(!s){const l=u.getSpan(t.spanId);s=ke(t.spanId,l),i.logsList.appendChild(s)}const a=s.querySelector(".span-logs");a&&(a.appendChild(J(t)),ht(s,a))}else i.logsList.appendChild(J(t))}Ce(n.length,e.length),oe(),le()}function Ce(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 oe(){if(!i.filterBar)return;const t=u.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(),R(),y()}),i.filterBar.querySelector(".filter-row")?.appendChild(s))}else r?.remove(),i.filterBar.querySelector(".filter-clear-btn")?.remove()}function le(){i.logsList&&i.visible&&(i.logsList.scrollTop=i.logsList.scrollHeight)}function ht(t,e){const n=t.querySelector(".span-count");if(n){const o=e.querySelectorAll(".log-entry").length;n.textContent=`${o} log${o===1?"":"s"}`}}function ke(t,e){const n=document.createElement("div");n.className="span-group",n.dataset.spanId=t;const o=e?.name??"Span",r=e?.status??"running",s=e?Se(e):"running",a=document.createElement("div");a.className="span-header",a.innerHTML=`
1760
+ <button class="span-toggle" type="button" aria-label="Toggle span logs"></button>
1761
+ <span class="span-title">${gt(o)}</span>
1762
+ <span class="span-status span-status-${r}">${r}</span>
1763
+ <span class="span-duration">${s}</span>
1764
+ <span class="span-count">0 logs</span>
1765
+ `;const l=document.createElement("div");l.className="span-logs",u.getConfig().spanCollapsed&&n.classList.add("collapsed");const c=a.querySelector(".span-toggle"),d=()=>{n.classList.toggle("collapsed")};return a.addEventListener("click",f=>{f.target?.closest(".span-toggle")||d()}),c?.addEventListener("click",()=>d()),n.appendChild(a),n.appendChild(l),n}function bt(t){const e=i.logsList?.querySelector(`.span-group[data-span-id="${t.id}"]`);if(!e)return;const n=e.querySelector(".span-title");n&&(n.textContent=t.name);const o=e.querySelector(".span-status");o&&(o.textContent=t.status,o.className=`span-status span-status-${t.status}`);const r=e.querySelector(".span-duration");r&&(r.textContent=Se(t))}function xe(t){t.key.toLowerCase()===ee.key&&t.ctrlKey===ee.ctrlKey&&t.shiftKey===ee.shiftKey&&(t.preventDefault(),ae()==="popout"?T.popout():T.toggle())}function mt(t){switch(t.type){case"CLEAR_LOGS":u.clear(),y();break;case"SYNC_REQUEST":N.sendSyncResponse(u.getLogs());break}}const T={init(){try{if(i.initialized||!u.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,ut(e),i.unsubscribe=u.subscribe(n=>{pt(n),N.sendLog(n)}),i.spanUnsubscribe=u.subscribeSpans(n=>{bt(n)}),i.channelUnsubscribe=N.subscribe(mt),document.addEventListener("keydown",xe),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,le())}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(),st()}catch(t){console.warn("[DevLogger] Failed to open pop-out:",t)}},closePopout(){me()},isPopoutOpen(){return it()},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),R(),y()}catch{}},getFilter(){return{...i.filter,levels:new Set(i.filter.levels)}},clearFilter(){try{i.filter=z(),R(),y()}catch{}},destroy(){try{me(),i.unsubscribe&&(i.unsubscribe(),i.unsubscribe=null),i.spanUnsubscribe&&(i.spanUnsubscribe(),i.spanUnsubscribe=null),i.channelUnsubscribe&&(i.channelUnsubscribe(),i.channelUnsubscribe=null),document.removeEventListener("keydown",xe),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.spanUnsubscribe=null,i.filter=z()}catch{}},isVisible(){return i.visible},isInitialized(){return i.initialized}},$e={captureErrors:!0,captureRejections:!0,errorPrefix:"[Uncaught Error]",rejectionPrefix:"[Unhandled Rejection]"};let P=!1,L={...$e},q=null,H=null;function vt(t,e,n,o,r){try{const s=r||(t instanceof ErrorEvent?t.error:null),a=s?.message||String(t),l=s?.stack;u.error(`${L.errorPrefix} ${a}`,{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 yt(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}),u.error(`${L.rejectionPrefix} ${n}`,o)}catch{}}function xt(t={}){if(P){L={...L,...t};return}try{if(typeof window>"u")return;L={...$e,...t},q=window.onerror,L.captureErrors&&(window.onerror=vt),L.captureRejections&&(H=yt,window.addEventListener("unhandledrejection",H)),P=!0}catch{}}function wt(){if(P)try{if(typeof window>"u")return;window.onerror=q,H&&window.removeEventListener("unhandledrejection",H),q=null,H=null,P=!1}catch{}}function Lt(){return P}function St(){return{...L}}const Ct={install:xt,uninstall:wt,isActive:Lt,getConfig:St},X="devlogger_persisted_logs",ce="devlogger_session_active",Ee={storage:"session",maxPersisted:500,debounceMs:100};let D=!1,S={...Ee},U=[],x=null,de=!1,W=null;function k(){try{return typeof window>"u"?null:S.storage==="local"?localStorage:sessionStorage}catch{return null}}function kt(){try{const t=k();return t?t.getItem(ce)==="active":!1}catch{return!1}}function $t(){try{const t=k();if(!t)return;t.setItem(ce,"active")}catch{}}function Te(){try{const t=k();if(!t)return;t.removeItem(ce)}catch{}}function Ie(t){try{const e=k();if(!e)return;const n=t.slice(-S.maxPersisted),o=JSON.stringify(n);e.setItem(X,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(X,JSON.stringify(o))}catch{}}}function Re(){try{const t=k();if(!t)return[];const e=t.getItem(X);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 Et(){try{const t=k();if(!t)return;t.removeItem(X)}catch{}}function Tt(t){U=t,x&&clearTimeout(x),x=setTimeout(()=>{Ie(U),x=null},S.debounceMs)}function Y(){try{x&&(clearTimeout(x),x=null),U.length>0&&Ie(U),Te()}catch{}}function It(){if(!D)return;const t=u.getLogs();Tt([...t])}function Rt(t={}){if(D){S={...S,...t};return}try{if(typeof window>"u")return;S={...Ee,...t},de=kt(),$t(),W=u.subscribe(It),window.addEventListener("beforeunload",Y),window.addEventListener("pagehide",Y),D=!0}catch{}}function Dt(){if(D)try{if(typeof window>"u")return;x&&(clearTimeout(x),x=null),W&&(W(),W=null),window.removeEventListener("beforeunload",Y),window.removeEventListener("pagehide",Y),Te(),D=!1}catch{}}function _t(){return de}function Mt(){return Re()}function Bt(){try{const t=Re();return t.length===0?0:(u.importLogs(t),t.length)}catch{return 0}}function Ft(){Et(),U=[],de=!1}function At(){return D}function Ht(){return{...S}}const Ot={enable:Rt,disable:Dt,isActive:At,hadCrash:_t,getPersistedLogs:Mt,rehydrate:Bt,clear:Ft,getConfig:Ht},De={captureFetch:!0,captureXHR:!0,includeHeaders:!1,includeBody:!1,includeResponse:!1,maxResponseLength:1e3,ignorePatterns:[],context:{}};let E=null,O=null,I=null,F=!1,m={...De};function _e(t){for(const e of m.ignorePatterns)if(typeof e=="string"){if(t.includes(e))return!0}else if(e.test(t))return!0;return!1}function Me(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 Be(t,e){return t.length<=e?t:t.slice(0,e)+"... (truncated)"}function K(t){try{return JSON.parse(t)}catch{return t}}function Nt(){return async function(e,n){const o=typeof e=="string"?e:e instanceof URL?e.href:e.url;if(_e(o))return E(e,n);const{host:r,path:s}=Me(o),a=n?.method||"GET",l=`${a} ${s}`,c=u.span(l,{...m.context,type:"fetch",method:a,host:r});if(c.info(`Request started: ${o}`),m.includeHeaders&&n?.headers&&c.debug("Request headers",n.headers),m.includeBody&&n?.body)try{const f=typeof n.body=="string"?K(n.body):n.body;c.debug("Request body",f)}catch{c.debug("Request body","[Unable to parse]")}const d=performance.now();try{const f=await E(e,n),h=Math.round(performance.now()-d);if(f.ok){if(c.info(`Response: ${f.status} ${f.statusText} (${h}ms)`),m.includeResponse)try{const Q=await f.clone().text(),v=K(Be(Q,m.maxResponseLength));c.debug("Response body",v)}catch{c.debug("Response body","[Unable to read]")}c.end()}else c.warn(`Response: ${f.status} ${f.statusText} (${h}ms)`),c.fail();return f}catch(f){const h=Math.round(performance.now()-d);throw c.error(`Request failed after ${h}ms`,f),c.fail(),f}}}function zt(){O=XMLHttpRequest.prototype.open,I=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:_e(s)},O.call(this,t,e,n,o,r)},XMLHttpRequest.prototype.send=function(t){const e=this.__networkCapture;if(!e||e.ignored)return I.call(this,t);const{method:n,url:o}=e,{host:r,path:s}=Me(o),a=`${n} ${s}`,l=u.span(a,{...m.context,type:"xhr",method:n,host:r});if(l.info(`XHR Request started: ${o}`),m.includeBody&&t)try{const d=typeof t=="string"?K(t):t;l.debug("Request body",d)}catch{l.debug("Request body","[Unable to parse]")}const c=performance.now();return this.addEventListener("load",()=>{const d=Math.round(performance.now()-c);if(this.status>=200&&this.status<400){if(l.info(`Response: ${this.status} ${this.statusText} (${d}ms)`),m.includeResponse&&this.responseText){const f=K(Be(this.responseText,m.maxResponseLength));l.debug("Response body",f)}l.end()}else l.warn(`Response: ${this.status} ${this.statusText} (${d}ms)`),l.fail()}),this.addEventListener("error",()=>{const d=Math.round(performance.now()-c);l.error(`XHR Request failed after ${d}ms`),l.fail()}),this.addEventListener("abort",()=>{const d=Math.round(performance.now()-c);l.warn(`XHR Request aborted after ${d}ms`),l.fail()}),this.addEventListener("timeout",()=>{const d=Math.round(performance.now()-c);l.error(`XHR Request timeout after ${d}ms`),l.fail()}),I.call(this,t)}}function Pt(){O&&(XMLHttpRequest.prototype.open=O,O=null),I&&(XMLHttpRequest.prototype.send=I,I=null)}const qt={install(t={}){try{if(F)return;m={...De,...t},m.captureFetch&&typeof globalThis.fetch=="function"&&(E=globalThis.fetch,globalThis.fetch=Nt()),m.captureXHR&&typeof XMLHttpRequest<"u"&&zt(),F=!0,u.debug("[NetworkCapture] Installed",{fetch:m.captureFetch,xhr:m.captureXHR})}catch(e){console.warn("[NetworkCapture] Install error:",e)}},uninstall(){try{if(!F)return;E&&(globalThis.fetch=E,E=null),Pt(),F=!1,u.debug("[NetworkCapture] Uninstalled")}catch(t){console.warn("[NetworkCapture] Uninstall error:",t)}},isActive(){return F},getConfig(){return{...m}},addIgnorePattern(t){m.ignorePatterns.push(t)}},Ut={debug:"#6e6e6e",info:"#3794ff",warn:"#cca700",error:"#f14c4c"},Vt={running:"#3794ff",success:"#4caf50",error:"#f14c4c"};class Fe{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=`
1676
1766
  <div class="devlogger-timeline" style="
1677
1767
  position: relative;
1678
1768
  background: #1e1e1e;
@@ -1752,12 +1842,12 @@
1752
1842
  pointer-events: none;
1753
1843
  "></div>
1754
1844
  </div>
1755
- `,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 r=n.currentTarget,o=parseInt(r.dataset.window||"60000",10);this.setTimeWindow(o),this.container.querySelectorAll("[data-window]").forEach(s=>{const c=s;c===r?(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,r=this.canvas.height,o=Date.now(),s=o-this.config.timeWindow;e.fillStyle="#1e1e1e",e.fillRect(0,0,n,r),this.spanBounds.clear(),this.logBounds.clear(),this.drawTimeGrid(e,n,r,s,o);const c=f.getLogs().filter(l=>l.timestamp>=s),a=f.getSpans().filter(l=>l.startTime>=s||l.endTime&&l.endTime>=s);this.config.showSpans&&this.drawSpans(e,a,n,r,s,o),this.config.showLogs&&this.drawLogs(e,c,n,r,s,o)}drawTimeGrid(e,n,r,o,s){const c=s-o,a=6;e.strokeStyle="#333",e.lineWidth=1,e.font="10px SF Mono, monospace",e.fillStyle="#666";for(let l=0;l<=a;l++){const d=l/a*n,u=o+c*l/a;e.beginPath(),e.moveTo(d,0),e.lineTo(d,r),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,r-4)}}drawSpans(e,n,r,o,s,c){const a=c-s,l=20,d=4,u=20,h=n.filter(m=>!m.parentId);let C=u;const K=(m,_,Pt=0)=>{const D=(m.startTime-s)/a*r,Me=((m.endTime||c)-s)/a*r,j=Math.max(Me-D,2),ce=At[m.status];e.fillStyle=ce+"40",e.fillRect(D,_,j,l),e.strokeStyle=ce,e.lineWidth=1,e.strokeRect(D,_,j,l),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:l}),_+l+d};for(const m of h)C=K(m,C)}drawLogs(e,n,r,o,s,c){const a=c-s,l=8,d=30;for(const u of n){const h=(u.timestamp-s)/a*r,C=Ot[u.level];e.fillStyle=C,e.beginPath(),e.moveTo(h,o-d),e.lineTo(h-l/2,o-d-l),e.lineTo(h+l/2,o-d-l),e.closePath(),e.fill(),this.logBounds.set(u.id,{x:h,y:o-d-l/2,r:l})}}handleMouseMove(e){if(!this.canvas||!this.tooltip)return;const n=this.canvas.getBoundingClientRect(),r=e.clientX-n.left,o=e.clientY-n.top;for(const s of f.getSpans()){const c=this.spanBounds.get(s.id);if(c&&r>=c.x&&r<=c.x+c.w&&o>=c.y&&o<=c.y+c.h){this.showTooltip(e,`
1845
+ `,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 a=s;a===o?(a.style.background="#0e639c",a.style.borderColor="#0e639c",a.style.color="white"):(a.style.background="transparent",a.style.borderColor="#3c3c3c",a.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 a=u.getLogs().filter(c=>c.timestamp>=s),l=u.getSpans().filter(c=>c.startTime>=s||c.endTime&&c.endTime>=s);this.config.showSpans&&this.drawSpans(e,l,n,o,s,r),this.config.showLogs&&this.drawLogs(e,a,n,o,s,r)}drawTimeGrid(e,n,o,r,s){const a=s-r,l=6;e.strokeStyle="#333",e.lineWidth=1,e.font="10px SF Mono, monospace",e.fillStyle="#666";for(let c=0;c<=l;c++){const d=c/l*n,f=r+a*c/l;e.beginPath(),e.moveTo(d,0),e.lineTo(d,o),e.stroke();const h=new Date(f),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,a){const l=a-s,c=20,d=4,f=20,h=n.filter(v=>!v.parentId);let C=f;const Q=(v,_,Gt=0)=>{const M=(v.startTime-s)/l*o,Ae=((v.endTime||a)-s)/l*o,j=Math.max(Ae-M,2),ge=Vt[v.status];e.fillStyle=ge+"40",e.fillRect(M,_,j,c),e.strokeStyle=ge,e.lineWidth=1,e.strokeRect(M,_,j,c),e.fillStyle="#ccc",e.font="10px SF Mono, monospace";const He=v.duration?`${v.name} (${v.duration}ms)`:v.name;return e.fillText(He,M+4,_+14,j-8),this.spanBounds.set(v.id,{x:M,y:_,w:j,h:c}),_+c+d};for(const v of h)C=Q(v,C)}drawLogs(e,n,o,r,s,a){const l=a-s,c=8,d=30;for(const f of n){const h=(f.timestamp-s)/l*o,C=Ut[f.level];e.fillStyle=C,e.beginPath(),e.moveTo(h,r-d),e.lineTo(h-c/2,r-d-c),e.lineTo(h+c/2,r-d-c),e.closePath(),e.fill(),this.logBounds.set(f.id,{x:h,y:r-d-c/2,r:c})}}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 u.getSpans()){const a=this.spanBounds.get(s.id);if(a&&o>=a.x&&o<=a.x+a.w&&r>=a.y&&r<=a.y+a.h){this.showTooltip(e,`
1756
1846
  <strong>${s.name}</strong><br>
1757
1847
  Status: ${s.status}<br>
1758
1848
  ${s.duration!==void 0?`Duration: ${s.duration}ms`:"Running..."}<br>
1759
1849
  ${s.context?`Context: ${JSON.stringify(s.context)}`:""}
1760
- `);return}}for(const s of f.getLogs()){const c=this.logBounds.get(s.id);if(c&&Math.sqrt((r-c.x)**2+(o-c.y)**2)<=c.r){this.showTooltip(e,`
1850
+ `);return}}for(const s of u.getLogs()){const a=this.logBounds.get(s.id);if(a&&Math.sqrt((o-a.x)**2+(r-a.y)**2)<=a.r){this.showTooltip(e,`
1761
1851
  <strong>[${s.level.toUpperCase()}]</strong> ${s.message}<br>
1762
1852
  <small>${new Date(s.timestamp).toISOString()}</small>
1763
- `);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=O;exports.ErrorCapture=vt;exports.LogPersistence=_t;exports.NetworkCapture=Ft;exports.Timeline=De;exports.VERSION=Nt;exports.computeDiff=re;exports.createDiffResult=te;exports.createTimeline=Ht;exports.formatValue=ee;exports.hasChanges=Ae;exports.logger=f;
1853
+ `);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 jt(t){return new Fe(t)}const Wt="0.1.0";exports.DevLoggerUI=T;exports.ErrorCapture=Ct;exports.LogPersistence=Ot;exports.NetworkCapture=qt;exports.Timeline=Fe;exports.VERSION=Wt;exports.computeDiff=re;exports.createDiffResult=ne;exports.createTimeline=jt;exports.formatValue=te;exports.hasChanges=ze;exports.logger=u;
package/dist/index.d.ts CHANGED
@@ -331,6 +331,12 @@ export declare interface LoggerConfig {
331
331
  minLevel?: LogLevel;
332
332
  /** Enable/disable logging entirely (default: true) */
333
333
  enabled?: boolean;
334
+ /** Ctrl+Shift+L action: toggle overlay or open pop-out (default: 'toggle') */
335
+ shortcutAction?: 'toggle' | 'popout';
336
+ /** Show the floating toggle button in the UI (default: true) */
337
+ showToggleButton?: boolean;
338
+ /** Collapse span groups by default in the UI (default: false) */
339
+ spanCollapsed?: boolean;
334
340
  }
335
341
 
336
342
  /**