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 +1 -1
- package/dist/index.cjs +66 -24
- package/dist/index.js +199 -157
- package/dist/noop.cjs +0 -1
- package/dist/noop.js +0 -1
- package/package.json +4 -4
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/noop.cjs.map +0 -1
- package/dist/noop.js.map +0 -1
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`);for(let n=4;n<e.length;n++){const
|
|
3
|
-
Data: ${JSON.stringify(n.data)}`:"";return`[${
|
|
4
|
-
Source: ${s}${
|
|
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
|
|
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"),
|
|
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,
|
|
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(
|
|
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
|
-
${
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1591
|
-
<div class="filter-bar ${
|
|
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
|
-
${
|
|
1648
|
+
${r?'<button class="filter-clear-btn" title="Clear filters">✕</button>':""}
|
|
1606
1649
|
</div>
|
|
1607
|
-
${
|
|
1650
|
+
${r?`<div class="filter-status">Showing ${n} of ${e} logs</div>`:""}
|
|
1608
1651
|
</div>
|
|
1609
|
-
`}function be(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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:
|
|
1652
|
+
`}function be(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}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">${
|
|
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=
|
|
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
|
|
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((
|
|
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=
|
|
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;
|