@neuradigi/debug-console 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +237 -0
- package/dist/index.cjs +266 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +158 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.js +266 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/** Console levels mirrored into the in-app buffer. */
|
|
2
|
+
type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
3
|
+
/** A single captured log line. */
|
|
4
|
+
interface LogEntry {
|
|
5
|
+
id: number;
|
|
6
|
+
level: LogLevel;
|
|
7
|
+
time: Date;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
/** Where the round launcher button is pinned. */
|
|
11
|
+
type LauncherPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
12
|
+
/** Options for {@link initDebugConsole}. */
|
|
13
|
+
interface DebugConsoleOptions {
|
|
14
|
+
/** Master switch. When `false`, the console is NOT patched and nothing is mounted. Default `true`. */
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
/** Max buffered entries (oldest dropped). Default `500`. */
|
|
17
|
+
max?: number;
|
|
18
|
+
/** Also capture `window` `error` + `unhandledrejection` events. Default `true`. */
|
|
19
|
+
captureGlobalErrors?: boolean;
|
|
20
|
+
/** Launcher corner. Default `'top-right'`. */
|
|
21
|
+
position?: LauncherPosition;
|
|
22
|
+
/** Accent color (any CSS color or gradient) for the launcher, count pill and active chips. */
|
|
23
|
+
accent?: string;
|
|
24
|
+
/** Open the panel immediately. Default `false`. */
|
|
25
|
+
open?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Runtime handle returned by {@link initDebugConsole}. */
|
|
28
|
+
interface DebugConsoleHandle {
|
|
29
|
+
/** The mounted custom element. */
|
|
30
|
+
readonly element: HTMLElement;
|
|
31
|
+
/** Open the panel. */
|
|
32
|
+
show(): void;
|
|
33
|
+
/** Close the panel. */
|
|
34
|
+
hide(): void;
|
|
35
|
+
/** Toggle the panel. */
|
|
36
|
+
toggle(): void;
|
|
37
|
+
/** Empty the buffer. */
|
|
38
|
+
clear(): void;
|
|
39
|
+
/** Remove the element from the DOM. */
|
|
40
|
+
destroy(): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Emitted to subscribers whenever the buffer changes. */
|
|
44
|
+
type LogEvent = {
|
|
45
|
+
type: 'add';
|
|
46
|
+
entry: LogEntry;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'clear';
|
|
49
|
+
};
|
|
50
|
+
type LogListener = (event: LogEvent) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Framework-agnostic capture core. Patches the global `console`, buffers entries
|
|
53
|
+
* (bounded, oldest dropped), captures uncaught errors, and notifies subscribers.
|
|
54
|
+
* A single shared instance is exported as {@link logger}.
|
|
55
|
+
*
|
|
56
|
+
* Patched methods always call the original first, so DevTools keeps working —
|
|
57
|
+
* this MIRRORS console output, it never swallows it.
|
|
58
|
+
*/
|
|
59
|
+
declare class LoggerCore {
|
|
60
|
+
private readonly _entries;
|
|
61
|
+
private readonly listeners;
|
|
62
|
+
private nextId;
|
|
63
|
+
private max;
|
|
64
|
+
private patched;
|
|
65
|
+
private errorsHooked;
|
|
66
|
+
/** The current buffer, oldest → newest (read-only). */
|
|
67
|
+
get entries(): readonly LogEntry[];
|
|
68
|
+
/** The configured buffer cap. */
|
|
69
|
+
get limit(): number;
|
|
70
|
+
/** Patch the console and (optionally) global error handlers. Idempotent. */
|
|
71
|
+
init(options?: {
|
|
72
|
+
max?: number;
|
|
73
|
+
captureGlobalErrors?: boolean;
|
|
74
|
+
}): void;
|
|
75
|
+
/** Subscribe to buffer changes. Returns an unsubscribe function. */
|
|
76
|
+
subscribe(listener: LogListener): () => void;
|
|
77
|
+
/** Empty the buffer (does not unpatch the console). */
|
|
78
|
+
clear(): void;
|
|
79
|
+
private patchConsole;
|
|
80
|
+
private captureGlobalErrors;
|
|
81
|
+
private push;
|
|
82
|
+
private emit;
|
|
83
|
+
private stringifyArgs;
|
|
84
|
+
private stringifyValue;
|
|
85
|
+
}
|
|
86
|
+
/** The shared capture core (console is global, so there is exactly one). */
|
|
87
|
+
declare const logger: LoggerCore;
|
|
88
|
+
|
|
89
|
+
declare const HTMLElementBase: typeof HTMLElement;
|
|
90
|
+
/**
|
|
91
|
+
* `<debug-console>` custom element. Renders the launcher + panel inside a shadow
|
|
92
|
+
* root (full style isolation) and mirrors {@link logger}'s buffer in real time.
|
|
93
|
+
* Register it with `defineDebugConsole()` or mount it via `initDebugConsole()`.
|
|
94
|
+
*/
|
|
95
|
+
declare class DebugConsoleElement extends HTMLElementBase {
|
|
96
|
+
private readonly root;
|
|
97
|
+
private launcherEl;
|
|
98
|
+
private badgeEl;
|
|
99
|
+
private panelEl;
|
|
100
|
+
private countEl;
|
|
101
|
+
private listEl;
|
|
102
|
+
private emptyEl;
|
|
103
|
+
private autoScrollBtn;
|
|
104
|
+
private downloadBtn;
|
|
105
|
+
private clearBtn;
|
|
106
|
+
private unsubscribe;
|
|
107
|
+
private isOpen;
|
|
108
|
+
private autoScroll;
|
|
109
|
+
private filter;
|
|
110
|
+
constructor();
|
|
111
|
+
connectedCallback(): void;
|
|
112
|
+
disconnectedCallback(): void;
|
|
113
|
+
/** Open the panel. */
|
|
114
|
+
show(): void;
|
|
115
|
+
/** Close the panel. */
|
|
116
|
+
hide(): void;
|
|
117
|
+
/** Toggle the panel. */
|
|
118
|
+
toggle(): void;
|
|
119
|
+
private render;
|
|
120
|
+
private query;
|
|
121
|
+
private replayExisting;
|
|
122
|
+
private onLog;
|
|
123
|
+
/** Build one row (XSS-safe via textContent) and trim the DOM to the buffer cap. */
|
|
124
|
+
private appendRow;
|
|
125
|
+
/** Recompute counts, badge, count pill, disabled states and the empty message. */
|
|
126
|
+
private refresh;
|
|
127
|
+
private setOpen;
|
|
128
|
+
private toggleAutoScroll;
|
|
129
|
+
private setFilter;
|
|
130
|
+
private scrollToBottom;
|
|
131
|
+
private scrollToBottomIfFollowing;
|
|
132
|
+
private download;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** The custom element tag name. */
|
|
136
|
+
declare const ELEMENT_NAME = "debug-console";
|
|
137
|
+
/**
|
|
138
|
+
* Register the `<debug-console>` custom element (idempotent, browser-only).
|
|
139
|
+
* Call this if you want to place `<debug-console>` in your own markup/JSX/template
|
|
140
|
+
* instead of using {@link initDebugConsole}. You still need to call
|
|
141
|
+
* {@link startCapture} (or `initDebugConsole`) once to begin capturing.
|
|
142
|
+
*/
|
|
143
|
+
declare function defineDebugConsole(name?: string): void;
|
|
144
|
+
/**
|
|
145
|
+
* Begin patching the console + capturing errors, without mounting any UI.
|
|
146
|
+
* Useful when you render `<debug-console>` yourself. Idempotent.
|
|
147
|
+
*/
|
|
148
|
+
declare function startCapture(options?: Pick<DebugConsoleOptions, 'max' | 'captureGlobalErrors'>): void;
|
|
149
|
+
/**
|
|
150
|
+
* One-call setup: start capturing and mount the overlay at the document root.
|
|
151
|
+
*
|
|
152
|
+
* Returns a handle to control/destroy it, or `null` when disabled or when there
|
|
153
|
+
* is no DOM (SSR). When `enabled` is `false` the console is NOT patched and no
|
|
154
|
+
* element is mounted.
|
|
155
|
+
*/
|
|
156
|
+
declare function initDebugConsole(options?: DebugConsoleOptions): DebugConsoleHandle | null;
|
|
157
|
+
|
|
158
|
+
export { DebugConsoleElement, type DebugConsoleHandle, type DebugConsoleOptions, ELEMENT_NAME, type LauncherPosition, type LogEntry, type LogEvent, type LogLevel, defineDebugConsole, initDebugConsole, logger, startCapture };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/** Console levels mirrored into the in-app buffer. */
|
|
2
|
+
type LogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
|
|
3
|
+
/** A single captured log line. */
|
|
4
|
+
interface LogEntry {
|
|
5
|
+
id: number;
|
|
6
|
+
level: LogLevel;
|
|
7
|
+
time: Date;
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
/** Where the round launcher button is pinned. */
|
|
11
|
+
type LauncherPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
12
|
+
/** Options for {@link initDebugConsole}. */
|
|
13
|
+
interface DebugConsoleOptions {
|
|
14
|
+
/** Master switch. When `false`, the console is NOT patched and nothing is mounted. Default `true`. */
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
/** Max buffered entries (oldest dropped). Default `500`. */
|
|
17
|
+
max?: number;
|
|
18
|
+
/** Also capture `window` `error` + `unhandledrejection` events. Default `true`. */
|
|
19
|
+
captureGlobalErrors?: boolean;
|
|
20
|
+
/** Launcher corner. Default `'top-right'`. */
|
|
21
|
+
position?: LauncherPosition;
|
|
22
|
+
/** Accent color (any CSS color or gradient) for the launcher, count pill and active chips. */
|
|
23
|
+
accent?: string;
|
|
24
|
+
/** Open the panel immediately. Default `false`. */
|
|
25
|
+
open?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Runtime handle returned by {@link initDebugConsole}. */
|
|
28
|
+
interface DebugConsoleHandle {
|
|
29
|
+
/** The mounted custom element. */
|
|
30
|
+
readonly element: HTMLElement;
|
|
31
|
+
/** Open the panel. */
|
|
32
|
+
show(): void;
|
|
33
|
+
/** Close the panel. */
|
|
34
|
+
hide(): void;
|
|
35
|
+
/** Toggle the panel. */
|
|
36
|
+
toggle(): void;
|
|
37
|
+
/** Empty the buffer. */
|
|
38
|
+
clear(): void;
|
|
39
|
+
/** Remove the element from the DOM. */
|
|
40
|
+
destroy(): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Emitted to subscribers whenever the buffer changes. */
|
|
44
|
+
type LogEvent = {
|
|
45
|
+
type: 'add';
|
|
46
|
+
entry: LogEntry;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'clear';
|
|
49
|
+
};
|
|
50
|
+
type LogListener = (event: LogEvent) => void;
|
|
51
|
+
/**
|
|
52
|
+
* Framework-agnostic capture core. Patches the global `console`, buffers entries
|
|
53
|
+
* (bounded, oldest dropped), captures uncaught errors, and notifies subscribers.
|
|
54
|
+
* A single shared instance is exported as {@link logger}.
|
|
55
|
+
*
|
|
56
|
+
* Patched methods always call the original first, so DevTools keeps working —
|
|
57
|
+
* this MIRRORS console output, it never swallows it.
|
|
58
|
+
*/
|
|
59
|
+
declare class LoggerCore {
|
|
60
|
+
private readonly _entries;
|
|
61
|
+
private readonly listeners;
|
|
62
|
+
private nextId;
|
|
63
|
+
private max;
|
|
64
|
+
private patched;
|
|
65
|
+
private errorsHooked;
|
|
66
|
+
/** The current buffer, oldest → newest (read-only). */
|
|
67
|
+
get entries(): readonly LogEntry[];
|
|
68
|
+
/** The configured buffer cap. */
|
|
69
|
+
get limit(): number;
|
|
70
|
+
/** Patch the console and (optionally) global error handlers. Idempotent. */
|
|
71
|
+
init(options?: {
|
|
72
|
+
max?: number;
|
|
73
|
+
captureGlobalErrors?: boolean;
|
|
74
|
+
}): void;
|
|
75
|
+
/** Subscribe to buffer changes. Returns an unsubscribe function. */
|
|
76
|
+
subscribe(listener: LogListener): () => void;
|
|
77
|
+
/** Empty the buffer (does not unpatch the console). */
|
|
78
|
+
clear(): void;
|
|
79
|
+
private patchConsole;
|
|
80
|
+
private captureGlobalErrors;
|
|
81
|
+
private push;
|
|
82
|
+
private emit;
|
|
83
|
+
private stringifyArgs;
|
|
84
|
+
private stringifyValue;
|
|
85
|
+
}
|
|
86
|
+
/** The shared capture core (console is global, so there is exactly one). */
|
|
87
|
+
declare const logger: LoggerCore;
|
|
88
|
+
|
|
89
|
+
declare const HTMLElementBase: typeof HTMLElement;
|
|
90
|
+
/**
|
|
91
|
+
* `<debug-console>` custom element. Renders the launcher + panel inside a shadow
|
|
92
|
+
* root (full style isolation) and mirrors {@link logger}'s buffer in real time.
|
|
93
|
+
* Register it with `defineDebugConsole()` or mount it via `initDebugConsole()`.
|
|
94
|
+
*/
|
|
95
|
+
declare class DebugConsoleElement extends HTMLElementBase {
|
|
96
|
+
private readonly root;
|
|
97
|
+
private launcherEl;
|
|
98
|
+
private badgeEl;
|
|
99
|
+
private panelEl;
|
|
100
|
+
private countEl;
|
|
101
|
+
private listEl;
|
|
102
|
+
private emptyEl;
|
|
103
|
+
private autoScrollBtn;
|
|
104
|
+
private downloadBtn;
|
|
105
|
+
private clearBtn;
|
|
106
|
+
private unsubscribe;
|
|
107
|
+
private isOpen;
|
|
108
|
+
private autoScroll;
|
|
109
|
+
private filter;
|
|
110
|
+
constructor();
|
|
111
|
+
connectedCallback(): void;
|
|
112
|
+
disconnectedCallback(): void;
|
|
113
|
+
/** Open the panel. */
|
|
114
|
+
show(): void;
|
|
115
|
+
/** Close the panel. */
|
|
116
|
+
hide(): void;
|
|
117
|
+
/** Toggle the panel. */
|
|
118
|
+
toggle(): void;
|
|
119
|
+
private render;
|
|
120
|
+
private query;
|
|
121
|
+
private replayExisting;
|
|
122
|
+
private onLog;
|
|
123
|
+
/** Build one row (XSS-safe via textContent) and trim the DOM to the buffer cap. */
|
|
124
|
+
private appendRow;
|
|
125
|
+
/** Recompute counts, badge, count pill, disabled states and the empty message. */
|
|
126
|
+
private refresh;
|
|
127
|
+
private setOpen;
|
|
128
|
+
private toggleAutoScroll;
|
|
129
|
+
private setFilter;
|
|
130
|
+
private scrollToBottom;
|
|
131
|
+
private scrollToBottomIfFollowing;
|
|
132
|
+
private download;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** The custom element tag name. */
|
|
136
|
+
declare const ELEMENT_NAME = "debug-console";
|
|
137
|
+
/**
|
|
138
|
+
* Register the `<debug-console>` custom element (idempotent, browser-only).
|
|
139
|
+
* Call this if you want to place `<debug-console>` in your own markup/JSX/template
|
|
140
|
+
* instead of using {@link initDebugConsole}. You still need to call
|
|
141
|
+
* {@link startCapture} (or `initDebugConsole`) once to begin capturing.
|
|
142
|
+
*/
|
|
143
|
+
declare function defineDebugConsole(name?: string): void;
|
|
144
|
+
/**
|
|
145
|
+
* Begin patching the console + capturing errors, without mounting any UI.
|
|
146
|
+
* Useful when you render `<debug-console>` yourself. Idempotent.
|
|
147
|
+
*/
|
|
148
|
+
declare function startCapture(options?: Pick<DebugConsoleOptions, 'max' | 'captureGlobalErrors'>): void;
|
|
149
|
+
/**
|
|
150
|
+
* One-call setup: start capturing and mount the overlay at the document root.
|
|
151
|
+
*
|
|
152
|
+
* Returns a handle to control/destroy it, or `null` when disabled or when there
|
|
153
|
+
* is no DOM (SSR). When `enabled` is `false` the console is NOT patched and no
|
|
154
|
+
* element is mounted.
|
|
155
|
+
*/
|
|
156
|
+
declare function initDebugConsole(options?: DebugConsoleOptions): DebugConsoleHandle | null;
|
|
157
|
+
|
|
158
|
+
export { DebugConsoleElement, type DebugConsoleHandle, type DebugConsoleOptions, ELEMENT_NAME, type LauncherPosition, type LogEntry, type LogEvent, type LogLevel, defineDebugConsole, initDebugConsole, logger, startCapture };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
var d=e=>`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${e}</svg>`,c={terminal:d('<rect x="3" y="4" width="18" height="16" rx="2"/><path d="m7 9 3 3-3 3"/><path d="M13 15h4"/>'),pin:d('<path d="M12 17v5"/><path d="M9 10.8V6a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8"/><path d="M9 10.8a2 2 0 0 1-1.1 1.8l-1.8.9A2 2 0 0 0 5 15.2V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.8a2 2 0 0 0-1.1-1.7l-1.8-.9A2 2 0 0 1 15 10.8V6a1 1 0 0 0-1-1"/>'),chevronsDown:d('<path d="m7 6 5 5 5-5"/><path d="m7 13 5 5 5-5"/>'),download:d('<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/>'),trash:d('<path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="m19 6-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>'),close:d('<path d="M18 6 6 18"/><path d="m6 6 12 12"/>')};var v=["log","info","warn","error","debug"],u=class{constructor(){this._entries=[];this.listeners=new Set;this.nextId=0;this.max=500;this.patched=false;this.errorsHooked=false;}get entries(){return this._entries}get limit(){return this.max}init(o={}){typeof o.max=="number"&&o.max>0&&(this.max=Math.floor(o.max)),this.patchConsole(),o.captureGlobalErrors!==false&&this.captureGlobalErrors();}subscribe(o){return this.listeners.add(o),()=>{this.listeners.delete(o);}}clear(){this._entries.length=0,this.emit({type:"clear"});}patchConsole(){if(this.patched||typeof console>"u")return;this.patched=true;let o=console;for(let t of v){let a=o[t].bind(console);o[t]=(...r)=>{a(...r),this.push(t,this.stringifyArgs(r));};}}captureGlobalErrors(){this.errorsHooked||typeof window>"u"||(this.errorsHooked=true,window.addEventListener("error",o=>{let t=o.error!=null?this.stringifyValue(o.error):o.message||"Uncaught error";this.push("error",t);}),window.addEventListener("unhandledrejection",o=>{this.push("error",`Unhandled promise rejection: ${this.stringifyValue(o.reason)}`);}));}push(o,t){let a={id:this.nextId++,level:o,time:new Date,text:t};this._entries.push(a),this._entries.length>this.max&&this._entries.splice(0,this._entries.length-this.max),this.emit({type:"add",entry:a});}emit(o){this.listeners.forEach(t=>{try{t(o);}catch{}});}stringifyArgs(o){return o.map(t=>this.stringifyValue(t)).join(" ")}stringifyValue(o){if(typeof o=="string")return o;if(o instanceof Error)return `${o.name}: ${o.message}`;try{let t=new WeakSet;return JSON.stringify(o,(r,i)=>{if(typeof i=="bigint")return i.toString();if(typeof i=="object"&&i!==null){if(t.has(i))return "[Circular]";t.add(i);}return i},2)??String(o)}catch{return String(o)}}},l=new u;var f=`
|
|
2
|
+
:host {
|
|
3
|
+
--dc-bg: #0b0e14;
|
|
4
|
+
--dc-surface: #11151f;
|
|
5
|
+
--dc-border: #232a3a;
|
|
6
|
+
--dc-text: #c9d1d9;
|
|
7
|
+
--dc-muted: #8b949e;
|
|
8
|
+
--dc-row-hover: rgba(255, 255, 255, 0.04);
|
|
9
|
+
--dc-shadow: 0 10px 34px rgba(0, 0, 0, 0.5);
|
|
10
|
+
--dc-accent: linear-gradient(135deg, #57bdff, #4f77fb, #3a5fcc);
|
|
11
|
+
--dc-error: #ff6b6b;
|
|
12
|
+
--dc-warn: #ffc107;
|
|
13
|
+
--dc-info: #57bdff;
|
|
14
|
+
--dc-badge-error: #dc3545;
|
|
15
|
+
--dc-badge-warn: #ffc107;
|
|
16
|
+
font-family: 'Cascadia Code', 'Fira Code', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
20
|
+
[hidden] { display: none !important; }
|
|
21
|
+
|
|
22
|
+
/* ===== Launcher ===== */
|
|
23
|
+
.dc-launcher {
|
|
24
|
+
position: fixed;
|
|
25
|
+
top: 16px;
|
|
26
|
+
right: 16px;
|
|
27
|
+
z-index: 2147483000;
|
|
28
|
+
width: 44px;
|
|
29
|
+
height: 44px;
|
|
30
|
+
display: inline-flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
border: none;
|
|
34
|
+
border-radius: 50%;
|
|
35
|
+
background: var(--dc-accent);
|
|
36
|
+
color: #fff;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
box-shadow: var(--dc-shadow);
|
|
39
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
40
|
+
}
|
|
41
|
+
.dc-launcher svg { width: 22px; height: 22px; }
|
|
42
|
+
.dc-launcher:hover { transform: scale(1.05); box-shadow: 0 6px 20px rgba(79, 119, 251, 0.45); }
|
|
43
|
+
:host([data-position="top-left"]) .dc-launcher { top: 16px; left: 16px; right: auto; }
|
|
44
|
+
:host([data-position="bottom-right"]) .dc-launcher { top: auto; bottom: 16px; right: 16px; }
|
|
45
|
+
:host([data-position="bottom-left"]) .dc-launcher { top: auto; bottom: 16px; left: 16px; right: auto; }
|
|
46
|
+
|
|
47
|
+
/* ===== Badge ===== */
|
|
48
|
+
.dc-badge {
|
|
49
|
+
position: absolute;
|
|
50
|
+
top: -4px;
|
|
51
|
+
right: -4px;
|
|
52
|
+
min-width: 18px;
|
|
53
|
+
height: 18px;
|
|
54
|
+
padding: 0 5px;
|
|
55
|
+
display: inline-flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
font-size: 11px;
|
|
59
|
+
font-weight: 700;
|
|
60
|
+
line-height: 1;
|
|
61
|
+
border-radius: 999px;
|
|
62
|
+
border: 2px solid var(--dc-bg);
|
|
63
|
+
}
|
|
64
|
+
.dc-badge--error { background: var(--dc-badge-error); color: #fff; }
|
|
65
|
+
.dc-badge--warn { background: var(--dc-badge-warn); color: #000; }
|
|
66
|
+
|
|
67
|
+
/* ===== Panel ===== */
|
|
68
|
+
.dc-panel {
|
|
69
|
+
position: fixed;
|
|
70
|
+
top: 0;
|
|
71
|
+
left: 0;
|
|
72
|
+
right: 0;
|
|
73
|
+
z-index: 2147482000;
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: column;
|
|
76
|
+
max-height: 50vh;
|
|
77
|
+
background: var(--dc-bg);
|
|
78
|
+
color: var(--dc-text);
|
|
79
|
+
border-bottom: 1px solid var(--dc-border);
|
|
80
|
+
box-shadow: var(--dc-shadow);
|
|
81
|
+
transform: translateY(-100%);
|
|
82
|
+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
83
|
+
}
|
|
84
|
+
.dc-panel--open { transform: translateY(0); }
|
|
85
|
+
|
|
86
|
+
/* ===== Header ===== */
|
|
87
|
+
.dc-header {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: space-between;
|
|
91
|
+
gap: 8px;
|
|
92
|
+
padding: 8px 16px;
|
|
93
|
+
background: var(--dc-surface);
|
|
94
|
+
border-bottom: 1px solid var(--dc-border);
|
|
95
|
+
flex-shrink: 0;
|
|
96
|
+
}
|
|
97
|
+
.dc-title { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
98
|
+
.dc-title-icon { display: inline-flex; color: var(--dc-info); }
|
|
99
|
+
.dc-title-icon svg { width: 20px; height: 20px; }
|
|
100
|
+
.dc-title-text { font-size: 14px; font-weight: 700; letter-spacing: 0.02em; white-space: nowrap; color: var(--dc-text); }
|
|
101
|
+
.dc-count {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
min-width: 20px;
|
|
106
|
+
height: 18px;
|
|
107
|
+
padding: 0 6px;
|
|
108
|
+
font-size: 11px;
|
|
109
|
+
font-weight: 700;
|
|
110
|
+
line-height: 1;
|
|
111
|
+
color: #fff;
|
|
112
|
+
background: var(--dc-accent);
|
|
113
|
+
border-radius: 999px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* ===== Actions ===== */
|
|
117
|
+
.dc-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
|
|
118
|
+
.dc-action {
|
|
119
|
+
position: relative;
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
width: 32px;
|
|
124
|
+
height: 32px;
|
|
125
|
+
color: var(--dc-muted);
|
|
126
|
+
background: transparent;
|
|
127
|
+
border: none;
|
|
128
|
+
border-radius: 6px;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
transition: color 0.2s ease, background-color 0.2s ease;
|
|
131
|
+
}
|
|
132
|
+
.dc-action svg { width: 18px; height: 18px; }
|
|
133
|
+
.dc-action:hover:not(:disabled) { color: var(--dc-text); background: var(--dc-row-hover); }
|
|
134
|
+
.dc-action:disabled { opacity: 0.35; cursor: default; }
|
|
135
|
+
.dc-action--active { color: var(--dc-info); }
|
|
136
|
+
|
|
137
|
+
/* ===== Tooltips (self-contained; no host overlay layer) ===== */
|
|
138
|
+
.dc-action[data-tip]:hover::after,
|
|
139
|
+
.dc-launcher[data-tip]:hover::after {
|
|
140
|
+
content: attr(data-tip);
|
|
141
|
+
position: absolute;
|
|
142
|
+
padding: 4px 8px;
|
|
143
|
+
border-radius: 6px;
|
|
144
|
+
background: #000;
|
|
145
|
+
color: #fff;
|
|
146
|
+
font-size: 11px;
|
|
147
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
148
|
+
font-weight: 500;
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
pointer-events: none;
|
|
151
|
+
z-index: 10;
|
|
152
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
|
|
153
|
+
}
|
|
154
|
+
.dc-action[data-tip]:hover::after {
|
|
155
|
+
top: calc(100% + 6px);
|
|
156
|
+
left: 50%;
|
|
157
|
+
transform: translateX(-50%);
|
|
158
|
+
}
|
|
159
|
+
.dc-launcher[data-tip]:hover::after {
|
|
160
|
+
top: 50%;
|
|
161
|
+
right: calc(100% + 8px);
|
|
162
|
+
transform: translateY(-50%);
|
|
163
|
+
}
|
|
164
|
+
:host([data-position="top-left"]) .dc-launcher[data-tip]:hover::after,
|
|
165
|
+
:host([data-position="bottom-left"]) .dc-launcher[data-tip]:hover::after {
|
|
166
|
+
right: auto;
|
|
167
|
+
left: calc(100% + 8px);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* ===== Filter chips ===== */
|
|
171
|
+
.dc-filters {
|
|
172
|
+
display: flex;
|
|
173
|
+
flex-wrap: wrap;
|
|
174
|
+
gap: 4px;
|
|
175
|
+
padding: 8px 16px;
|
|
176
|
+
background: var(--dc-surface);
|
|
177
|
+
border-bottom: 1px solid var(--dc-border);
|
|
178
|
+
flex-shrink: 0;
|
|
179
|
+
}
|
|
180
|
+
.dc-chip {
|
|
181
|
+
padding: 3px 12px;
|
|
182
|
+
font-size: 12px;
|
|
183
|
+
font-weight: 600;
|
|
184
|
+
letter-spacing: 0.03em;
|
|
185
|
+
color: var(--dc-muted);
|
|
186
|
+
background: transparent;
|
|
187
|
+
border: 1px solid var(--dc-border);
|
|
188
|
+
border-radius: 999px;
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
font-family: inherit;
|
|
191
|
+
transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
|
|
192
|
+
}
|
|
193
|
+
.dc-chip:hover { color: var(--dc-text); border-color: #4f77fb; }
|
|
194
|
+
.dc-chip--active { color: #fff; border-color: transparent; background: var(--dc-accent); }
|
|
195
|
+
|
|
196
|
+
/* ===== List ===== */
|
|
197
|
+
.dc-list {
|
|
198
|
+
flex: 1;
|
|
199
|
+
min-height: 0;
|
|
200
|
+
overflow-y: auto;
|
|
201
|
+
overflow-x: hidden;
|
|
202
|
+
padding: 4px 0;
|
|
203
|
+
scrollbar-width: thin;
|
|
204
|
+
scrollbar-color: var(--dc-border) transparent;
|
|
205
|
+
}
|
|
206
|
+
.dc-list::-webkit-scrollbar { width: 10px; }
|
|
207
|
+
.dc-list::-webkit-scrollbar-thumb { background: var(--dc-border); border-radius: 999px; }
|
|
208
|
+
.dc-list::-webkit-scrollbar-track { background: transparent; }
|
|
209
|
+
|
|
210
|
+
.dc-row {
|
|
211
|
+
display: grid;
|
|
212
|
+
grid-template-columns: auto auto minmax(0, 1fr);
|
|
213
|
+
gap: 8px;
|
|
214
|
+
align-items: baseline;
|
|
215
|
+
padding: 4px 16px;
|
|
216
|
+
border-left: 3px solid transparent;
|
|
217
|
+
font-size: 12px;
|
|
218
|
+
line-height: 1.5;
|
|
219
|
+
}
|
|
220
|
+
.dc-row:hover { background: var(--dc-row-hover); }
|
|
221
|
+
.dc-row--error { border-left-color: var(--dc-error); }
|
|
222
|
+
.dc-row--error .dc-tag, .dc-row--error .dc-text { color: var(--dc-error); }
|
|
223
|
+
.dc-row--warn { border-left-color: var(--dc-warn); }
|
|
224
|
+
.dc-row--warn .dc-tag { color: var(--dc-warn); }
|
|
225
|
+
.dc-row--info { border-left-color: var(--dc-info); }
|
|
226
|
+
.dc-row--info .dc-tag { color: var(--dc-info); }
|
|
227
|
+
.dc-row--log, .dc-row--debug { border-left-color: var(--dc-muted); }
|
|
228
|
+
.dc-row--log .dc-tag, .dc-row--debug .dc-tag { color: var(--dc-muted); }
|
|
229
|
+
|
|
230
|
+
.dc-time { color: var(--dc-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
231
|
+
.dc-tag { min-width: 42px; font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; }
|
|
232
|
+
.dc-text { margin: 0; min-width: 0; font-family: inherit; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; color: var(--dc-text); }
|
|
233
|
+
|
|
234
|
+
.dc-empty { padding: 24px 16px; text-align: center; font-size: 13px; color: var(--dc-muted); }
|
|
235
|
+
|
|
236
|
+
@media (max-width: 600px) {
|
|
237
|
+
.dc-panel { max-height: 65vh; }
|
|
238
|
+
}
|
|
239
|
+
`;var b=[{value:"all",label:"All",match:()=>true},{value:"log",label:"Log",match:e=>e==="log"||e==="debug"},{value:"info",label:"Info",match:e=>e==="info"},{value:"warn",label:"Warn",match:e=>e==="warn"},{value:"error",label:"Error",match:e=>e==="error"}],s=(e,o=2)=>String(e).padStart(o,"0"),m=e=>`${s(e.getHours())}:${s(e.getMinutes())}:${s(e.getSeconds())}.${s(e.getMilliseconds(),3)}`,x=e=>`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())} ${m(e)}`,w=e=>`${e.getFullYear()}${s(e.getMonth()+1)}${s(e.getDate())}-${s(e.getHours())}${s(e.getMinutes())}${s(e.getSeconds())}`,y=typeof HTMLElement<"u"?HTMLElement:class{},p=class extends y{constructor(){super();this.unsubscribe=null;this.isOpen=false;this.autoScroll=true;this.filter=b[0];this.root=this.attachShadow({mode:"open"});}connectedCallback(){this.render(),this.replayExisting(),this.unsubscribe=l.subscribe(t=>this.onLog(t));}disconnectedCallback(){this.unsubscribe?.(),this.unsubscribe=null;}show(){this.setOpen(true);}hide(){this.setOpen(false);}toggle(){this.setOpen(!this.isOpen);}render(){this.root.innerHTML=`
|
|
240
|
+
<style>${f}</style>
|
|
241
|
+
<button class="dc-launcher" type="button" part="launcher" data-tip="Show application logs" aria-label="Show application logs">
|
|
242
|
+
${c.terminal}
|
|
243
|
+
<span class="dc-badge" hidden></span>
|
|
244
|
+
</button>
|
|
245
|
+
<section class="dc-panel" part="panel" role="log" aria-live="polite" aria-label="Application logs">
|
|
246
|
+
<header class="dc-header">
|
|
247
|
+
<div class="dc-title">
|
|
248
|
+
<span class="dc-title-icon">${c.terminal}</span>
|
|
249
|
+
<span class="dc-title-text">Application Logs</span>
|
|
250
|
+
<span class="dc-count" hidden>0</span>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="dc-actions">
|
|
253
|
+
<button class="dc-action dc-action--active" type="button" data-act="autoscroll" data-tip="Auto-scroll: on (following newest)" aria-label="Toggle auto-scroll">${c.pin}</button>
|
|
254
|
+
<button class="dc-action" type="button" data-act="bottom" data-tip="Scroll to bottom" aria-label="Scroll to bottom">${c.chevronsDown}</button>
|
|
255
|
+
<button class="dc-action" type="button" data-act="download" data-tip="Download logs" aria-label="Download logs" disabled>${c.download}</button>
|
|
256
|
+
<button class="dc-action" type="button" data-act="clear" data-tip="Clear logs" aria-label="Clear logs" disabled>${c.trash}</button>
|
|
257
|
+
<button class="dc-action" type="button" data-act="close" data-tip="Close" aria-label="Close application logs">${c.close}</button>
|
|
258
|
+
</div>
|
|
259
|
+
</header>
|
|
260
|
+
<div class="dc-filters"></div>
|
|
261
|
+
<div class="dc-list"></div>
|
|
262
|
+
<div class="dc-empty">No log entries captured yet.</div>
|
|
263
|
+
</section>
|
|
264
|
+
`,this.launcherEl=this.query(".dc-launcher"),this.badgeEl=this.query(".dc-badge"),this.panelEl=this.query(".dc-panel"),this.countEl=this.query(".dc-count"),this.listEl=this.query(".dc-list"),this.emptyEl=this.query(".dc-empty"),this.autoScrollBtn=this.query('[data-act="autoscroll"]'),this.downloadBtn=this.query('[data-act="download"]'),this.clearBtn=this.query('[data-act="clear"]'),this.launcherEl.addEventListener("click",()=>this.setOpen(true)),this.query(".dc-actions").addEventListener("click",a=>{switch(a.target.closest(".dc-action")?.dataset.act){case "autoscroll":this.toggleAutoScroll();break;case "bottom":this.scrollToBottom();break;case "download":this.download();break;case "clear":l.clear();break;case "close":this.setOpen(false);break}});let t=this.query(".dc-filters");for(let a of b){let r=document.createElement("button");r.type="button",r.className="dc-chip"+(a===this.filter?" dc-chip--active":""),r.textContent=a.label,r.addEventListener("click",()=>this.setFilter(a,r)),t.appendChild(r);}}query(t){return this.root.querySelector(t)}replayExisting(){for(let t of l.entries)this.appendRow(t);this.refresh(),this.scrollToBottomIfFollowing();}onLog(t){t.type==="clear"?this.listEl.replaceChildren():this.appendRow(t.entry),this.refresh(),t.type==="add"&&this.scrollToBottomIfFollowing();}appendRow(t){let a=document.createElement("div");a.className=`dc-row dc-row--${t.level}`,a.dataset.level=t.level,a.hidden=!this.filter.match(t.level);let r=document.createElement("span");r.className="dc-time",r.textContent=m(t.time);let i=document.createElement("span");i.className="dc-tag",i.textContent=t.level;let n=document.createElement("pre");for(n.className="dc-text",n.textContent=t.text,a.append(r,i,n),this.listEl.appendChild(a);this.listEl.childElementCount>l.limit;)this.listEl.firstElementChild?.remove();}refresh(){let t=0,a=0;for(let n of l.entries)n.level==="error"?t++:n.level==="warn"&&a++;let r=l.entries.length;this.countEl.textContent=String(r),this.countEl.hidden=r===0;let i=t+a;this.badgeEl.hidden=this.isOpen||i===0,this.badgeEl.textContent=String(i),this.badgeEl.className="dc-badge "+(t>0?"dc-badge--error":"dc-badge--warn"),this.downloadBtn.disabled=r===0,this.clearBtn.disabled=r===0,this.emptyEl.hidden=!!this.listEl.querySelector(".dc-row:not([hidden])");}setOpen(t){this.isOpen=t,this.panelEl.classList.toggle("dc-panel--open",t),this.launcherEl.hidden=t,this.refresh(),t&&this.scrollToBottomIfFollowing();}toggleAutoScroll(){this.autoScroll=!this.autoScroll,this.autoScrollBtn.classList.toggle("dc-action--active",this.autoScroll),this.autoScrollBtn.dataset.tip=this.autoScroll?"Auto-scroll: on (following newest)":"Auto-scroll: off",this.scrollToBottomIfFollowing();}setFilter(t,a){this.filter=t,this.root.querySelectorAll(".dc-chip").forEach(r=>r.classList.toggle("dc-chip--active",r===a)),this.listEl.querySelectorAll(".dc-row").forEach(r=>{r.hidden=!t.match(r.dataset.level);}),this.refresh(),this.scrollToBottomIfFollowing();}scrollToBottom(){this.listEl.scrollTop=this.listEl.scrollHeight;}scrollToBottomIfFollowing(){!this.isOpen||!this.autoScroll||requestAnimationFrame(()=>{this.listEl.scrollTop=this.listEl.scrollHeight;});}download(){let t=l.entries;if(t.length===0)return;let a=t.map(h=>`[${x(h.time)}] ${h.level.toUpperCase()} ${h.text}`).join(`
|
|
265
|
+
`),r=new Blob([a],{type:"text/plain;charset=utf-8"}),i=URL.createObjectURL(r),n=document.createElement("a");n.href=i,n.download=`application-logs-${w(new Date)}.log`,document.body.appendChild(n),n.click(),n.remove(),URL.revokeObjectURL(i);}};var g="debug-console";function E(e=g){typeof customElements>"u"||customElements.get(e)||customElements.define(e,p);}function L(e={}){l.init({max:e.max,captureGlobalErrors:e.captureGlobalErrors});}function O(e={}){if(e.enabled===false||typeof document>"u")return null;L(e),E();let o=document.querySelector(g),t=o??document.createElement(g);if(!o){e.position&&t.setAttribute("data-position",e.position),e.accent&&t.style.setProperty("--dc-accent",e.accent);let a=()=>{document.body.appendChild(t),e.open&&t.show();};document.body?a():window.addEventListener("DOMContentLoaded",a,{once:true});}return {element:t,show:()=>t.show(),hide:()=>t.hide(),toggle:()=>t.toggle(),clear:()=>l.clear(),destroy:()=>t.remove()}}export{p as DebugConsoleElement,g as ELEMENT_NAME,E as defineDebugConsole,O as initDebugConsole,l as logger,L as startCapture};//# sourceMappingURL=index.js.map
|
|
266
|
+
//# sourceMappingURL=index.js.map
|