devlog-ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # LogView
1
+ # Devlog-UI
2
2
 
3
3
  A lightweight, browser-based dev logger with a beautiful debug UI. Zero dependencies, framework-agnostic, production-safe.
4
4
 
package/dist/index.cjs CHANGED
@@ -1,9 +1,9 @@
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(`
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(`
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,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=`
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=`
7
7
  :host {
8
8
  --bg-primary: ${g.bgPrimary};
9
9
  --bg-secondary: ${g.bgSecondary};
@@ -544,13 +544,13 @@
544
544
  .log-data-diff .log-data-toggle {
545
545
  color: ${g.levelInfo};
546
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`
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`
549
549
  <div class="log-data log-data-diff">
550
550
  <button class="log-data-toggle" data-target="${s}">
551
551
  diff
552
552
  </button>
553
- <div class="log-data-content" id="${s}">${Je(l)}</div>
553
+ <div class="log-data-content" id="${s}">${Je(a)}</div>
554
554
  </div>
555
555
  `}return`
556
556
  <div class="log-data">
@@ -563,12 +563,12 @@
563
563
  <div class="log-entry-header">
564
564
  <span class="log-level log-level-${t.level}">${t.level}</span>
565
565
  <span class="log-time">${We(t.timestamp)}</span>
566
- ${r?`<span class="log-context" title="Context">${M(Ke(t.context))}</span>`:""}
566
+ ${o?`<span class="log-context" title="Context">${M(Ke(t.context))}</span>`:""}
567
567
  <span class="log-source" title="${M(ue(t.source))}">${M(ue(t.source))}</span>
568
568
  </div>
569
569
  <div class="log-message">${M(t.message)}</div>
570
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>
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>
572
572
  <html lang="en">
573
573
  <head>
574
574
  <meta charset="UTF-8">
@@ -1001,6 +1001,7 @@
1001
1001
  <button class="btn btn-copy" id="btn-copy-json" title="Copy as JSON">JSON</button>
1002
1002
  <button class="btn btn-copy" id="btn-copy-text" title="Copy as Text">TXT</button>
1003
1003
  <button class="btn btn-timeline" id="btn-timeline" title="Toggle Timeline">Timeline</button>
1004
+ <button class="btn btn-timeline" id="btn-timeline-pause" title="Pause/Resume Timeline" style="display: none;">⏸</button>
1004
1005
  <button class="btn" id="btn-clear">Clear</button>
1005
1006
  </div>
1006
1007
  </div>
@@ -1025,6 +1026,7 @@
1025
1026
  </div>
1026
1027
  <input type="text" class="filter-input" id="filter-search" placeholder="Search logs..." data-filter="search">
1027
1028
  <input type="text" class="filter-input" id="filter-file" placeholder="Filter by file..." data-filter="file" style="max-width: 120px;">
1029
+ <button class="filter-clear-btn" title="Clear all filters">✕</button>
1028
1030
  </div>
1029
1031
  <div class="filter-status" id="filter-status" style="display: none;"></div>
1030
1032
  </div>
@@ -1062,6 +1064,7 @@
1062
1064
  const btnCopyJson = document.getElementById('btn-copy-json');
1063
1065
  const btnCopyText = document.getElementById('btn-copy-text');
1064
1066
  const btnTimeline = document.getElementById('btn-timeline');
1067
+ const btnTimelinePause = document.getElementById('btn-timeline-pause');
1065
1068
  const timelineContainer = document.getElementById('timeline-container');
1066
1069
  const timelineCanvas = document.getElementById('timeline-canvas');
1067
1070
  const timelineTooltip = document.getElementById('timeline-tooltip');
@@ -1071,6 +1074,7 @@
1071
1074
  let timelineVisible = false;
1072
1075
  let timelineWindow = 30000; // 30 seconds default
1073
1076
  let timelineInterval = null;
1077
+ let timelinePaused = false;
1074
1078
  const LEVEL_COLORS = {
1075
1079
  debug: '#6e6e6e',
1076
1080
  info: '#3794ff',
@@ -1080,6 +1084,7 @@
1080
1084
  const filterSearch = document.getElementById('filter-search');
1081
1085
  const filterFile = document.getElementById('filter-file');
1082
1086
  const filterStatus = document.getElementById('filter-status');
1087
+ const filterClearBtn = document.querySelector('.filter-clear-btn');
1083
1088
 
1084
1089
  // Connect to broadcast channel
1085
1090
  function connect() {
@@ -1335,12 +1340,14 @@
1335
1340
 
1336
1341
  // Export logs as JSON
1337
1342
  function exportLogsJson() {
1338
- return JSON.stringify(logs, null, 2);
1343
+ const logsToExport = getFilteredLogs();
1344
+ return JSON.stringify(logsToExport, null, 2);
1339
1345
  }
1340
1346
 
1341
1347
  // Export logs as text
1342
1348
  function exportLogsText() {
1343
- return logs.map(log => {
1349
+ const logsToExport = getFilteredLogs();
1350
+ return logsToExport.map(log => {
1344
1351
  const time = new Date(log.timestamp).toISOString();
1345
1352
  const level = log.level.toUpperCase().padEnd(5);
1346
1353
  const source = log.source.file + ':' + log.source.line;
@@ -1422,12 +1429,36 @@
1422
1429
  renderAllLogs();
1423
1430
  });
1424
1431
 
1432
+ // Filter: Clear button
1433
+ if (filterClearBtn) {
1434
+ filterClearBtn.addEventListener('click', () => {
1435
+ // Reset all filters
1436
+ filter.levels = new Set(['debug', 'info', 'warn', 'error']);
1437
+ filter.search = '';
1438
+ filter.file = '';
1439
+
1440
+ // Reset UI elements
1441
+ filterSearch.value = '';
1442
+ filterFile.value = '';
1443
+ document.querySelectorAll('.filter-level-btn').forEach(btn => {
1444
+ btn.classList.add('active');
1445
+ });
1446
+
1447
+ // Re-render
1448
+ renderAllLogs();
1449
+ });
1450
+ }
1451
+
1425
1452
  // Timeline: Toggle button
1426
1453
  btnTimeline.addEventListener('click', () => {
1427
1454
  timelineVisible = !timelineVisible;
1428
1455
  timelineContainer.style.display = timelineVisible ? 'block' : 'none';
1429
1456
  btnTimeline.classList.toggle('active', timelineVisible);
1457
+ btnTimelinePause.style.display = timelineVisible ? 'inline-block' : 'none';
1430
1458
  if (timelineVisible) {
1459
+ timelinePaused = false;
1460
+ btnTimelinePause.textContent = '⏸';
1461
+ btnTimelinePause.classList.remove('active');
1431
1462
  resizeCanvas();
1432
1463
  drawTimeline();
1433
1464
  startTimelineRefresh();
@@ -1436,6 +1467,18 @@
1436
1467
  }
1437
1468
  });
1438
1469
 
1470
+ // Timeline: Pause/Resume button
1471
+ btnTimelinePause.addEventListener('click', () => {
1472
+ timelinePaused = !timelinePaused;
1473
+ btnTimelinePause.textContent = timelinePaused ? '▶' : '⏸';
1474
+ btnTimelinePause.classList.toggle('active', timelinePaused);
1475
+ if (timelinePaused) {
1476
+ stopTimelineRefresh();
1477
+ } else {
1478
+ startTimelineRefresh();
1479
+ }
1480
+ });
1481
+
1439
1482
  // Timeline: Time window buttons
1440
1483
  document.querySelectorAll('.timeline-btn[data-window]').forEach(btn => {
1441
1484
  btn.addEventListener('click', (e) => {
@@ -1587,8 +1630,8 @@
1587
1630
  connect();
1588
1631
  <\/script>
1589
1632
  </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":""}">
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":""}">
1592
1635
  <div class="filter-row">
1593
1636
  <div class="filter-levels">
1594
1637
  <button class="filter-level-btn ${t.levels.has("debug")?"active":""}" data-level="debug" title="Debug">D</button>
@@ -1602,11 +1645,11 @@
1602
1645
  <div class="filter-file">
1603
1646
  <input type="text" class="filter-input filter-file-input" placeholder="Filter by file..." value="${be(t.file)}" data-filter="file">
1604
1647
  </div>
1605
- ${o?'<button class="filter-clear-btn" title="Clear filters">✕</button>':""}
1648
+ ${r?'<button class="filter-clear-btn" title="Clear filters">✕</button>':""}
1606
1649
  </div>
1607
- ${o?`<div class="filter-status">Showing ${n} of ${e} logs</div>`:""}
1650
+ ${r?`<div class="filter-status">Showing ${n} of ${e} logs</div>`:""}
1608
1651
  </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=`
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=`
1610
1653
  <div class="devlogger-header">
1611
1654
  <div class="devlogger-title">
1612
1655
  DevLogger
@@ -1623,13 +1666,13 @@
1623
1666
  <div class="filter-bar-container"></div>
1624
1667
  <div class="devlogger-logs"></div>
1625
1668
  <div class="devlogger-footer">
1626
- <span class="devlogger-log-count">${r.length} logs</span>
1669
+ <span class="devlogger-log-count">${o.length} logs</span>
1627
1670
  <span class="devlogger-shortcut">Ctrl+Shift+L to toggle</span>
1628
1671
  </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=`
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=`
1630
1673
  <span class="devlogger-no-results-icon">🔍</span>
1631
1674
  <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=`
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=`
1633
1676
  <div class="devlogger-timeline" style="
1634
1677
  position: relative;
1635
1678
  background: #1e1e1e;
@@ -1709,13 +1752,12 @@
1709
1752
  pointer-events: none;
1710
1753
  "></div>
1711
1754
  </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,`
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,`
1713
1756
  <strong>${s.name}</strong><br>
1714
1757
  Status: ${s.status}<br>
1715
1758
  ${s.duration!==void 0?`Duration: ${s.duration}ms`:"Running..."}<br>
1716
1759
  ${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,`
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,`
1718
1761
  <strong>[${s.level.toUpperCase()}]</strong> ${s.message}<br>
1719
1762
  <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
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;