@ytspar/devbar 0.0.1 → 1.0.0-canary.19cb05c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/debug.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * DevBar Debug Utilities
3
+ *
4
+ * Debug logging system for DevBar lifecycle, state, and events.
5
+ */
6
+ /**
7
+ * Normalize debug option to DebugConfig
8
+ */
9
+ export function normalizeDebugConfig(debug) {
10
+ if (!debug) {
11
+ return { enabled: false };
12
+ }
13
+ if (debug === true) {
14
+ return {
15
+ enabled: true,
16
+ logLifecycle: true,
17
+ logStateChanges: true,
18
+ logWebSocket: true,
19
+ logPerformance: true,
20
+ };
21
+ }
22
+ return {
23
+ enabled: debug.enabled,
24
+ logLifecycle: debug.logLifecycle ?? true,
25
+ logStateChanges: debug.logStateChanges ?? true,
26
+ logWebSocket: debug.logWebSocket ?? true,
27
+ logPerformance: debug.logPerformance ?? true,
28
+ };
29
+ }
30
+ /**
31
+ * Debug logger for DevBar
32
+ */
33
+ export class DebugLogger {
34
+ constructor(config) {
35
+ this.prefix = '[DevBar]';
36
+ this.config = config;
37
+ }
38
+ /**
39
+ * Update debug configuration
40
+ */
41
+ setConfig(config) {
42
+ this.config = config;
43
+ }
44
+ /**
45
+ * Log lifecycle events (init, destroy, etc.)
46
+ */
47
+ lifecycle(message, data) {
48
+ if (this.config.enabled && this.config.logLifecycle) {
49
+ this.log('lifecycle', message, data);
50
+ }
51
+ }
52
+ /**
53
+ * Log state changes (collapse, modal open/close, etc.)
54
+ */
55
+ state(message, data) {
56
+ if (this.config.enabled && this.config.logStateChanges) {
57
+ this.log('state', message, data);
58
+ }
59
+ }
60
+ /**
61
+ * Log WebSocket events (connect, disconnect, messages)
62
+ */
63
+ ws(message, data) {
64
+ if (this.config.enabled && this.config.logWebSocket) {
65
+ this.log('ws', message, data);
66
+ }
67
+ }
68
+ /**
69
+ * Log performance measurements (FCP, LCP, CLS, INP)
70
+ */
71
+ perf(message, data) {
72
+ if (this.config.enabled && this.config.logPerformance) {
73
+ this.log('perf', message, data);
74
+ }
75
+ }
76
+ log(category, message, data) {
77
+ const timestamp = new Date().toISOString().split('T')[1].slice(0, -1);
78
+ const categoryColors = {
79
+ lifecycle: '#10b981', // emerald
80
+ state: '#3b82f6', // blue
81
+ ws: '#a855f7', // purple
82
+ perf: '#f59e0b', // amber
83
+ };
84
+ const color = categoryColors[category] || '#6b7280';
85
+ if (data !== undefined) {
86
+ console.log(`%c${this.prefix}%c [${category}] %c${timestamp}%c ${message}`, 'color: #10b981; font-weight: bold', `color: ${color}`, 'color: #6b7280', 'color: inherit', data);
87
+ }
88
+ else {
89
+ console.log(`%c${this.prefix}%c [${category}] %c${timestamp}%c ${message}`, 'color: #10b981; font-weight: bold', `color: ${color}`, 'color: #6b7280', 'color: inherit');
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Early Console Capture Script
3
+ *
4
+ * This script captures console logs BEFORE React hydrates, which is critical for
5
+ * catching hydration errors and other early-stage issues. It must be injected
6
+ * into the HTML <head> as an inline script.
7
+ *
8
+ * The script creates `window.__sweetlinkEarlyLogs` which GlobalDevBar will pick up
9
+ * when it mounts, merging these early logs with subsequent console output.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // In your Document component (Remix) or _document.tsx (Next.js)
14
+ * import { EARLY_CONSOLE_CAPTURE_SCRIPT } from '@ytspar/devbar';
15
+ *
16
+ * <head>
17
+ * <script dangerouslySetInnerHTML={{ __html: EARLY_CONSOLE_CAPTURE_SCRIPT }} />
18
+ * </head>
19
+ * ```
20
+ */
21
+ export declare const EARLY_CONSOLE_CAPTURE_SCRIPT = "\n(function() {\n if (window.__sweetlinkEarlyLogs) return;\n window.__sweetlinkEarlyLogs = [];\n var orig = {\n log: console.log,\n error: console.error,\n warn: console.warn,\n info: console.info\n };\n function formatArg(a) {\n if (a instanceof Error) {\n return a.name + ': ' + a.message + (a.stack ? '\\n' + a.stack : '');\n }\n if (typeof a === 'object' && a !== null) {\n try {\n return JSON.stringify(a, function(key, val) {\n if (val instanceof Error) {\n return val.name + ': ' + val.message;\n }\n return val;\n });\n } catch(e) {\n return String(a);\n }\n }\n return String(a);\n }\n function capture(level, args) {\n window.__sweetlinkEarlyLogs.push({\n level: level,\n message: Array.from(args).map(formatArg).join(' '),\n timestamp: new Date().toISOString()\n });\n }\n console.log = function() { capture('log', arguments); orig.log.apply(console, arguments); };\n console.error = function() { capture('error', arguments); orig.error.apply(console, arguments); };\n console.warn = function() { capture('warn', arguments); orig.warn.apply(console, arguments); };\n console.info = function() { capture('info', arguments); orig.info.apply(console, arguments); };\n // Capture uncaught errors\n window.addEventListener('error', function(e) {\n window.__sweetlinkEarlyLogs.push({\n level: 'error',\n message: 'Uncaught ' + (e.error ? formatArg(e.error) : e.message) + ' at ' + e.filename + ':' + e.lineno + ':' + e.colno,\n timestamp: new Date().toISOString()\n });\n });\n // Capture unhandled promise rejections\n window.addEventListener('unhandledrejection', function(e) {\n window.__sweetlinkEarlyLogs.push({\n level: 'error',\n message: 'Unhandled Promise Rejection: ' + formatArg(e.reason),\n timestamp: new Date().toISOString()\n });\n });\n})();\n";
22
+ /**
23
+ * Type declaration for the window object with early logs
24
+ * Use this to type-check access to early logs in your app
25
+ */
26
+ declare global {
27
+ interface Window {
28
+ __sweetlinkEarlyLogs?: Array<{
29
+ level: string;
30
+ message: string;
31
+ timestamp: string;
32
+ }>;
33
+ }
34
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Early Console Capture Script
3
+ *
4
+ * This script captures console logs BEFORE React hydrates, which is critical for
5
+ * catching hydration errors and other early-stage issues. It must be injected
6
+ * into the HTML <head> as an inline script.
7
+ *
8
+ * The script creates `window.__sweetlinkEarlyLogs` which GlobalDevBar will pick up
9
+ * when it mounts, merging these early logs with subsequent console output.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // In your Document component (Remix) or _document.tsx (Next.js)
14
+ * import { EARLY_CONSOLE_CAPTURE_SCRIPT } from '@ytspar/devbar';
15
+ *
16
+ * <head>
17
+ * <script dangerouslySetInnerHTML={{ __html: EARLY_CONSOLE_CAPTURE_SCRIPT }} />
18
+ * </head>
19
+ * ```
20
+ */
21
+ export const EARLY_CONSOLE_CAPTURE_SCRIPT = `
22
+ (function() {
23
+ if (window.__sweetlinkEarlyLogs) return;
24
+ window.__sweetlinkEarlyLogs = [];
25
+ var orig = {
26
+ log: console.log,
27
+ error: console.error,
28
+ warn: console.warn,
29
+ info: console.info
30
+ };
31
+ function formatArg(a) {
32
+ if (a instanceof Error) {
33
+ return a.name + ': ' + a.message + (a.stack ? '\\n' + a.stack : '');
34
+ }
35
+ if (typeof a === 'object' && a !== null) {
36
+ try {
37
+ return JSON.stringify(a, function(key, val) {
38
+ if (val instanceof Error) {
39
+ return val.name + ': ' + val.message;
40
+ }
41
+ return val;
42
+ });
43
+ } catch(e) {
44
+ return String(a);
45
+ }
46
+ }
47
+ return String(a);
48
+ }
49
+ function capture(level, args) {
50
+ window.__sweetlinkEarlyLogs.push({
51
+ level: level,
52
+ message: Array.from(args).map(formatArg).join(' '),
53
+ timestamp: new Date().toISOString()
54
+ });
55
+ }
56
+ console.log = function() { capture('log', arguments); orig.log.apply(console, arguments); };
57
+ console.error = function() { capture('error', arguments); orig.error.apply(console, arguments); };
58
+ console.warn = function() { capture('warn', arguments); orig.warn.apply(console, arguments); };
59
+ console.info = function() { capture('info', arguments); orig.info.apply(console, arguments); };
60
+ // Capture uncaught errors
61
+ window.addEventListener('error', function(e) {
62
+ window.__sweetlinkEarlyLogs.push({
63
+ level: 'error',
64
+ message: 'Uncaught ' + (e.error ? formatArg(e.error) : e.message) + ' at ' + e.filename + ':' + e.lineno + ':' + e.colno,
65
+ timestamp: new Date().toISOString()
66
+ });
67
+ });
68
+ // Capture unhandled promise rejections
69
+ window.addEventListener('unhandledrejection', function(e) {
70
+ window.__sweetlinkEarlyLogs.push({
71
+ level: 'error',
72
+ message: 'Unhandled Promise Rejection: ' + formatArg(e.reason),
73
+ timestamp: new Date().toISOString()
74
+ });
75
+ });
76
+ })();
77
+ `;
@@ -0,0 +1,13 @@
1
+ export { type A11yState, type AxeResult, type AxeViolation, clearA11yCache, formatViolation, getBadgeColor, getCachedResult, getImpactColor, getViolationCounts, groupViolationsByImpact, isAxeLoaded, preloadAxe, runA11yAudit, } from './accessibility.js';
2
+ export { BUTTON_COLORS, CATEGORY_COLORS, COLORS, DEVBAR_THEME, DEVBAR_THEME_LIGHT, type DevBarTheme, type DevBarThemeInput, FONT_MONO, generateBreakpointCSS, generateThemeCSSVars, getEffectiveTheme, getStoredThemeMode, getTheme, getThemeColors, injectThemeCSS, STORAGE_KEYS, setStoredThemeMode, TAILWIND_BREAKPOINTS, type TailwindBreakpoint, type ThemeColors, } from './constants.js';
3
+ export { DebugLogger, normalizeDebugConfig } from './debug.js';
4
+ export { EARLY_CONSOLE_CAPTURE_SCRIPT } from './earlyConsoleCapture.js';
5
+ export { destroyGlobalDevBar, earlyConsoleCapture, GlobalDevBar, getGlobalDevBar, initGlobalDevBar, } from './GlobalDevBar.js';
6
+ export { getHtml2Canvas, isHtml2CanvasLoaded, preloadHtml2Canvas } from './lazy/index.js';
7
+ export { formatBytes as formatNetworkBytes, formatDuration, getInitiatorColor, type NetworkEntry, NetworkMonitor, type NetworkState, } from './network.js';
8
+ export { extractDocumentOutline, outlineToMarkdown } from './outline.js';
9
+ export { initDebug, initFull, initMinimal, initPerformance, initResponsive, PRESET_DEBUG, PRESET_FULL, PRESET_MINIMAL, PRESET_PERFORMANCE, PRESET_RESPONSIVE, } from './presets.js';
10
+ export { extractPageSchema, schemaToMarkdown } from './schema.js';
11
+ export { beautifyJson, type CookieItem, clearLocalStorage, clearSessionStorage, deleteCookie, deleteLocalStorageItem, deleteSessionStorageItem, formatStorageSummary, getCookies, getLocalStorage, getSessionStorage, getStorageData, type StorageData, type StorageItem, setLocalStorageItem, setSessionStorageItem, } from './storage.js';
12
+ export type { ConsoleLog, DebugConfig, DevBarControl, GlobalDevBarOptions, OutlineNode, PageSchema, SweetlinkCommand, ThemeMode, } from './types.js';
13
+ export { canvasToDataUrl, copyCanvasToClipboard, delay, formatArg, formatArgs, prepareForCapture, } from './utils.js';
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ // DevBar - Development toolbar and utilities
2
+ // Pure vanilla JavaScript - no framework dependencies
3
+ // Accessibility audit utilities
4
+ export { clearA11yCache, formatViolation, getBadgeColor, getCachedResult, getImpactColor, getViolationCounts, groupViolationsByImpact, isAxeLoaded, preloadAxe, runA11yAudit, } from './accessibility.js';
5
+ // Re-export constants and theme utilities
6
+ export { BUTTON_COLORS, CATEGORY_COLORS, COLORS, DEVBAR_THEME, DEVBAR_THEME_LIGHT, FONT_MONO, generateBreakpointCSS, generateThemeCSSVars, getEffectiveTheme, getStoredThemeMode, getTheme, getThemeColors, injectThemeCSS, STORAGE_KEYS, setStoredThemeMode, TAILWIND_BREAKPOINTS, } from './constants.js';
7
+ // Debug utilities
8
+ export { DebugLogger, normalizeDebugConfig } from './debug.js';
9
+ // Early console capture script for injection
10
+ export { EARLY_CONSOLE_CAPTURE_SCRIPT } from './earlyConsoleCapture.js';
11
+ // Main vanilla JS devbar
12
+ export { destroyGlobalDevBar, earlyConsoleCapture, GlobalDevBar, getGlobalDevBar, initGlobalDevBar, } from './GlobalDevBar.js';
13
+ // Lazy loading utilities
14
+ export { getHtml2Canvas, isHtml2CanvasLoaded, preloadHtml2Canvas } from './lazy/index.js';
15
+ // Network monitoring utilities
16
+ export { formatBytes as formatNetworkBytes, formatDuration, getInitiatorColor, NetworkMonitor, } from './network.js';
17
+ // Re-export outline/schema functions
18
+ export { extractDocumentOutline, outlineToMarkdown } from './outline.js';
19
+ // Configuration presets
20
+ export { initDebug, initFull, initMinimal, initPerformance, initResponsive, PRESET_DEBUG, PRESET_FULL, PRESET_MINIMAL, PRESET_PERFORMANCE, PRESET_RESPONSIVE, } from './presets.js';
21
+ export { extractPageSchema, schemaToMarkdown } from './schema.js';
22
+ // Storage inspection utilities
23
+ export { beautifyJson, clearLocalStorage, clearSessionStorage, deleteCookie, deleteLocalStorageItem, deleteSessionStorageItem, formatStorageSummary, getCookies, getLocalStorage, getSessionStorage, getStorageData, setLocalStorageItem, setSessionStorageItem, } from './storage.js';
24
+ // Re-export utilities for external use
25
+ export { canvasToDataUrl, copyCanvasToClipboard, delay, formatArg, formatArgs, prepareForCapture, } from './utils.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Lazy Loading Utilities
3
+ *
4
+ * Re-exports all lazy loading modules.
5
+ */
6
+ export { getHtml2Canvas, isHtml2CanvasLoaded, preloadHtml2Canvas } from './lazyHtml2Canvas.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Lazy Loading Utilities
3
+ *
4
+ * Re-exports all lazy loading modules.
5
+ */
6
+ export { getHtml2Canvas, isHtml2CanvasLoaded, preloadHtml2Canvas } from './lazyHtml2Canvas.js';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Lazy Loading for html2canvas
3
+ *
4
+ * Defers html2canvas-pro loading until first screenshot capture,
5
+ * reducing initial bundle size and page load time.
6
+ */
7
+ type Html2CanvasFunc = (element: HTMLElement, options?: {
8
+ logging?: boolean;
9
+ useCORS?: boolean;
10
+ allowTaint?: boolean;
11
+ scale?: number;
12
+ width?: number;
13
+ windowWidth?: number;
14
+ }) => Promise<HTMLCanvasElement>;
15
+ /**
16
+ * Get html2canvas function, lazily loading it on first call.
17
+ * Subsequent calls return the same cached promise.
18
+ */
19
+ export declare function getHtml2Canvas(): Promise<Html2CanvasFunc>;
20
+ /**
21
+ * Check if html2canvas has been loaded
22
+ */
23
+ export declare function isHtml2CanvasLoaded(): boolean;
24
+ /**
25
+ * Preload html2canvas without waiting for result.
26
+ * Useful for warming up the cache before user interaction.
27
+ */
28
+ export declare function preloadHtml2Canvas(): void;
29
+ export {};
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Lazy Loading for html2canvas
3
+ *
4
+ * Defers html2canvas-pro loading until first screenshot capture,
5
+ * reducing initial bundle size and page load time.
6
+ */
7
+ // Cached promise for the html2canvas module
8
+ let html2canvasPromise = null;
9
+ /**
10
+ * Get html2canvas function, lazily loading it on first call.
11
+ * Subsequent calls return the same cached promise.
12
+ */
13
+ export async function getHtml2Canvas() {
14
+ if (!html2canvasPromise) {
15
+ html2canvasPromise = import('html2canvas-pro').then((module) => {
16
+ // Handle ESM/CJS interop
17
+ const html2canvas = module.default ?? module;
18
+ return html2canvas;
19
+ });
20
+ }
21
+ return html2canvasPromise;
22
+ }
23
+ /**
24
+ * Check if html2canvas has been loaded
25
+ */
26
+ export function isHtml2CanvasLoaded() {
27
+ return html2canvasPromise !== null;
28
+ }
29
+ /**
30
+ * Preload html2canvas without waiting for result.
31
+ * Useful for warming up the cache before user interaction.
32
+ */
33
+ export function preloadHtml2Canvas() {
34
+ getHtml2Canvas().catch(() => {
35
+ // Silently ignore preload errors
36
+ });
37
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Network Activity Monitor
3
+ *
4
+ * Tracks network requests using PerformanceObserver and fetch interception.
5
+ */
6
+ /**
7
+ * Network request entry
8
+ */
9
+ export interface NetworkEntry {
10
+ url: string;
11
+ name: string;
12
+ initiatorType: string;
13
+ duration: number;
14
+ transferSize: number;
15
+ encodedBodySize: number;
16
+ decodedBodySize: number;
17
+ startTime: number;
18
+ responseEnd: number;
19
+ method?: string;
20
+ status?: number;
21
+ statusText?: string;
22
+ }
23
+ /**
24
+ * Network monitor state
25
+ */
26
+ export interface NetworkState {
27
+ entries: NetworkEntry[];
28
+ totalRequests: number;
29
+ totalSize: number;
30
+ pendingCount: number;
31
+ }
32
+ /**
33
+ * Network activity monitor using PerformanceObserver
34
+ */
35
+ export declare class NetworkMonitor {
36
+ private entries;
37
+ private observer;
38
+ private listeners;
39
+ private maxEntries;
40
+ /**
41
+ * Start monitoring network activity
42
+ */
43
+ start(): void;
44
+ /**
45
+ * Stop monitoring network activity
46
+ */
47
+ stop(): void;
48
+ /**
49
+ * Add a new entry from PerformanceResourceTiming
50
+ */
51
+ private addEntry;
52
+ /**
53
+ * Extract resource name from URL
54
+ */
55
+ private getResourceName;
56
+ /**
57
+ * Get current network state
58
+ */
59
+ getState(): NetworkState;
60
+ /**
61
+ * Get entries filtered by type
62
+ */
63
+ getEntriesByType(type: string): NetworkEntry[];
64
+ /**
65
+ * Get entries filtered by search query
66
+ */
67
+ search(query: string): NetworkEntry[];
68
+ /**
69
+ * Clear all entries
70
+ */
71
+ clear(): void;
72
+ /**
73
+ * Subscribe to state changes
74
+ */
75
+ subscribe(listener: (state: NetworkState) => void): () => void;
76
+ /**
77
+ * Notify all listeners of state change
78
+ */
79
+ private notifyListeners;
80
+ }
81
+ /**
82
+ * Format bytes to human-readable string
83
+ */
84
+ export declare function formatBytes(bytes: number): string;
85
+ /**
86
+ * Format duration to human-readable string
87
+ */
88
+ export declare function formatDuration(ms: number): string;
89
+ /**
90
+ * Get color for initiator type
91
+ */
92
+ export declare function getInitiatorColor(type: string): string;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Network Activity Monitor
3
+ *
4
+ * Tracks network requests using PerformanceObserver and fetch interception.
5
+ */
6
+ /**
7
+ * Network activity monitor using PerformanceObserver
8
+ */
9
+ export class NetworkMonitor {
10
+ constructor() {
11
+ this.entries = [];
12
+ this.observer = null;
13
+ this.listeners = new Set();
14
+ this.maxEntries = 200;
15
+ }
16
+ /**
17
+ * Start monitoring network activity
18
+ */
19
+ start() {
20
+ if (typeof PerformanceObserver === 'undefined') {
21
+ console.warn('[NetworkMonitor] PerformanceObserver not supported');
22
+ return;
23
+ }
24
+ // Get already loaded resources
25
+ const existingResources = performance.getEntriesByType('resource');
26
+ for (const entry of existingResources) {
27
+ this.addEntry(entry);
28
+ }
29
+ // Watch for new resources
30
+ try {
31
+ this.observer = new PerformanceObserver((list) => {
32
+ for (const entry of list.getEntries()) {
33
+ this.addEntry(entry);
34
+ }
35
+ this.notifyListeners();
36
+ });
37
+ this.observer.observe({ type: 'resource', buffered: true });
38
+ }
39
+ catch (e) {
40
+ console.warn('[NetworkMonitor] Failed to start observer', e);
41
+ }
42
+ }
43
+ /**
44
+ * Stop monitoring network activity
45
+ */
46
+ stop() {
47
+ if (this.observer) {
48
+ this.observer.disconnect();
49
+ this.observer = null;
50
+ }
51
+ }
52
+ /**
53
+ * Add a new entry from PerformanceResourceTiming
54
+ */
55
+ addEntry(timing) {
56
+ const entry = {
57
+ url: timing.name,
58
+ name: this.getResourceName(timing.name),
59
+ initiatorType: timing.initiatorType,
60
+ duration: Math.round(timing.duration),
61
+ transferSize: timing.transferSize,
62
+ encodedBodySize: timing.encodedBodySize,
63
+ decodedBodySize: timing.decodedBodySize,
64
+ startTime: Math.round(timing.startTime),
65
+ responseEnd: Math.round(timing.responseEnd),
66
+ };
67
+ this.entries.push(entry);
68
+ // Trim to max entries
69
+ if (this.entries.length > this.maxEntries) {
70
+ this.entries = this.entries.slice(-this.maxEntries);
71
+ }
72
+ }
73
+ /**
74
+ * Extract resource name from URL
75
+ */
76
+ getResourceName(url) {
77
+ try {
78
+ const urlObj = new URL(url);
79
+ const pathname = urlObj.pathname;
80
+ const parts = pathname.split('/').filter(Boolean);
81
+ return parts.length > 0 ? parts[parts.length - 1] : urlObj.hostname;
82
+ }
83
+ catch {
84
+ return url.slice(0, 50);
85
+ }
86
+ }
87
+ /**
88
+ * Get current network state
89
+ */
90
+ getState() {
91
+ let totalSize = 0;
92
+ for (const entry of this.entries) {
93
+ totalSize += entry.transferSize || 0;
94
+ }
95
+ return {
96
+ entries: [...this.entries],
97
+ totalRequests: this.entries.length,
98
+ totalSize,
99
+ pendingCount: 0, // Could track with fetch interception
100
+ };
101
+ }
102
+ /**
103
+ * Get entries filtered by type
104
+ */
105
+ getEntriesByType(type) {
106
+ return this.entries.filter((e) => e.initiatorType === type);
107
+ }
108
+ /**
109
+ * Get entries filtered by search query
110
+ */
111
+ search(query) {
112
+ const lowerQuery = query.toLowerCase();
113
+ return this.entries.filter((e) => e.url.toLowerCase().includes(lowerQuery) ||
114
+ e.name.toLowerCase().includes(lowerQuery) ||
115
+ e.initiatorType.toLowerCase().includes(lowerQuery));
116
+ }
117
+ /**
118
+ * Clear all entries
119
+ */
120
+ clear() {
121
+ this.entries = [];
122
+ this.notifyListeners();
123
+ }
124
+ /**
125
+ * Subscribe to state changes
126
+ */
127
+ subscribe(listener) {
128
+ this.listeners.add(listener);
129
+ return () => this.listeners.delete(listener);
130
+ }
131
+ /**
132
+ * Notify all listeners of state change
133
+ */
134
+ notifyListeners() {
135
+ const state = this.getState();
136
+ for (const listener of this.listeners) {
137
+ listener(state);
138
+ }
139
+ }
140
+ }
141
+ /**
142
+ * Format bytes to human-readable string
143
+ */
144
+ export function formatBytes(bytes) {
145
+ if (bytes === 0)
146
+ return '0 B';
147
+ if (bytes < 1024)
148
+ return `${bytes} B`;
149
+ if (bytes < 1024 * 1024)
150
+ return `${(bytes / 1024).toFixed(1)} KB`;
151
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
152
+ }
153
+ /**
154
+ * Format duration to human-readable string
155
+ */
156
+ export function formatDuration(ms) {
157
+ if (ms < 1000)
158
+ return `${Math.round(ms)}ms`;
159
+ return `${(ms / 1000).toFixed(2)}s`;
160
+ }
161
+ /**
162
+ * Get color for initiator type
163
+ */
164
+ export function getInitiatorColor(type) {
165
+ const colors = {
166
+ script: '#f59e0b', // amber
167
+ link: '#3b82f6', // blue
168
+ css: '#a855f7', // purple
169
+ fetch: '#10b981', // emerald
170
+ xmlhttprequest: '#10b981', // emerald
171
+ img: '#ec4899', // pink
172
+ iframe: '#06b6d4', // cyan
173
+ other: '#6b7280', // gray
174
+ };
175
+ return colors[type] || colors.other;
176
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Document Outline Extraction
3
+ *
4
+ * Functions for extracting and formatting the semantic document outline.
5
+ */
6
+ import type { OutlineNode } from './types.js';
7
+ /**
8
+ * Extract the document outline from the page
9
+ */
10
+ export declare function extractDocumentOutline(): OutlineNode[];
11
+ /**
12
+ * Convert an outline to markdown format
13
+ */
14
+ export declare function outlineToMarkdown(outline: OutlineNode[], indent?: number): string;