@ytspar/devbar 1.0.0-canary.3c85c90 → 1.0.0-canary.4b73445
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/GlobalDevBar.d.ts +36 -2
- package/dist/GlobalDevBar.js +491 -86
- package/dist/constants.d.ts +28 -20
- package/dist/constants.js +35 -27
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -7
- package/dist/presets.d.ts +1 -1
- package/dist/ui/modals.d.ts +4 -0
- package/dist/ui/modals.js +48 -4
- package/package.json +1 -1
package/dist/GlobalDevBar.d.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
import { getThemeColors } from './constants.js';
|
|
11
11
|
import type { ConsoleLog, DebugConfig, DevBarControl, GlobalDevBarOptions, OutlineNode, PageSchema, SweetlinkCommand, ThemeMode } from './types.js';
|
|
12
12
|
export type { ConsoleLog, DebugConfig, SweetlinkCommand, OutlineNode, PageSchema, GlobalDevBarOptions, DevBarControl, ThemeMode, };
|
|
13
|
-
export type {
|
|
14
|
-
export {
|
|
13
|
+
export type { DevBarPosition, DevBarSettings, MetricsVisibility } from './settings.js';
|
|
14
|
+
export { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, getSettingsManager } from './settings.js';
|
|
15
15
|
interface EarlyConsoleCapture {
|
|
16
16
|
errorCount: number;
|
|
17
17
|
warningCount: number;
|
|
@@ -46,6 +46,8 @@ export declare class GlobalDevBar {
|
|
|
46
46
|
private apiKeyStatus;
|
|
47
47
|
private lastOutline;
|
|
48
48
|
private lastSchema;
|
|
49
|
+
private savingOutline;
|
|
50
|
+
private savingSchema;
|
|
49
51
|
private consoleFilter;
|
|
50
52
|
private showOutlineModal;
|
|
51
53
|
private showSchemaModal;
|
|
@@ -55,6 +57,10 @@ export declare class GlobalDevBar {
|
|
|
55
57
|
private clsValue;
|
|
56
58
|
private inpValue;
|
|
57
59
|
private reconnectAttempts;
|
|
60
|
+
private readonly currentAppPort;
|
|
61
|
+
private readonly baseWsPort;
|
|
62
|
+
private wsVerified;
|
|
63
|
+
private serverProjectDir;
|
|
58
64
|
private lastDotPosition;
|
|
59
65
|
private reconnectTimeout;
|
|
60
66
|
private screenshotTimeout;
|
|
@@ -231,6 +237,10 @@ export declare class GlobalDevBar {
|
|
|
231
237
|
* Create the settings gear button
|
|
232
238
|
*/
|
|
233
239
|
private createSettingsButton;
|
|
240
|
+
/**
|
|
241
|
+
* Create the compact mode toggle button with chevron icon
|
|
242
|
+
*/
|
|
243
|
+
private createCompactToggleButton;
|
|
234
244
|
/**
|
|
235
245
|
* Create a settings section with title
|
|
236
246
|
*/
|
|
@@ -249,6 +259,30 @@ export declare class GlobalDevBar {
|
|
|
249
259
|
private resetToDefaults;
|
|
250
260
|
private renderCollapsed;
|
|
251
261
|
private renderExpanded;
|
|
262
|
+
/** Base styles for tooltip containers */
|
|
263
|
+
private readonly TOOLTIP_BASE_STYLES;
|
|
264
|
+
/** Create a tooltip container element */
|
|
265
|
+
private createTooltipContainer;
|
|
266
|
+
/** Add a bold title to tooltip (metric name, feature name, etc.) */
|
|
267
|
+
private addTooltipTitle;
|
|
268
|
+
/** Add a description paragraph to tooltip */
|
|
269
|
+
private addTooltipDescription;
|
|
270
|
+
/** Add a muted uppercase section header to tooltip */
|
|
271
|
+
private addTooltipSectionHeader;
|
|
272
|
+
/** Add a colored row with dot + label + value (for thresholds) */
|
|
273
|
+
private addTooltipColoredRow;
|
|
274
|
+
/** Add an info row with label + value (for breakpoint details) */
|
|
275
|
+
private addTooltipInfoRow;
|
|
276
|
+
/** Position tooltip above the anchor element, adjusting for screen edges */
|
|
277
|
+
private positionTooltip;
|
|
278
|
+
/** Attach an HTML tooltip to an element with custom content builder */
|
|
279
|
+
private attachHtmlTooltip;
|
|
280
|
+
/** Attach a metric tooltip with title, description, and colored thresholds */
|
|
281
|
+
private attachMetricTooltip;
|
|
282
|
+
/** Attach a breakpoint tooltip showing current breakpoint and all breakpoint ranges */
|
|
283
|
+
private attachBreakpointTooltip;
|
|
284
|
+
/** Attach a simple info tooltip with title and description */
|
|
285
|
+
private attachInfoTooltip;
|
|
252
286
|
/**
|
|
253
287
|
* Create a console badge for error/warning counts
|
|
254
288
|
*/
|
package/dist/GlobalDevBar.js
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
* to avoid React dependency conflicts in host applications.
|
|
9
9
|
*/
|
|
10
10
|
import * as html2canvasModule from 'html2canvas-pro';
|
|
11
|
-
import { BASE_RECONNECT_DELAY_MS, BUTTON_COLORS, CATEGORY_COLORS, CLIPBOARD_NOTIFICATION_MS, COLORS, DESIGN_REVIEW_NOTIFICATION_MS, DEVBAR_SCREENSHOT_QUALITY, FONT_MONO, getEffectiveTheme, getThemeColors, MAX_CONSOLE_LOGS, MAX_RECONNECT_ATTEMPTS, MAX_RECONNECT_DELAY_MS, SCREENSHOT_BLUR_DELAY_MS, SCREENSHOT_NOTIFICATION_MS, SCREENSHOT_SCALE, TAILWIND_BREAKPOINTS, TOOLTIP_STYLES, WS_PORT, } from './constants.js';
|
|
11
|
+
import { BASE_RECONNECT_DELAY_MS, BUTTON_COLORS, CATEGORY_COLORS, CLIPBOARD_NOTIFICATION_MS, COLORS, DESIGN_REVIEW_NOTIFICATION_MS, DEVBAR_SCREENSHOT_QUALITY, FONT_MONO, getEffectiveTheme, getTheme, getThemeColors, injectThemeCSS, MAX_CONSOLE_LOGS, MAX_PORT_RETRIES, MAX_RECONNECT_ATTEMPTS, MAX_RECONNECT_DELAY_MS, PORT_RETRY_DELAY_MS, PORT_SCAN_RESTART_DELAY_MS, SCREENSHOT_BLUR_DELAY_MS, SCREENSHOT_NOTIFICATION_MS, SCREENSHOT_SCALE, TAILWIND_BREAKPOINTS, TOOLTIP_STYLES, WS_PORT, WS_PORT_OFFSET, } from './constants.js';
|
|
12
12
|
import { DebugLogger, normalizeDebugConfig } from './debug.js';
|
|
13
13
|
import { extractDocumentOutline, outlineToMarkdown } from './outline.js';
|
|
14
14
|
import { extractPageSchema, schemaToMarkdown } from './schema.js';
|
|
15
15
|
import { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, getSettingsManager, } from './settings.js';
|
|
16
16
|
import { createEmptyMessage, createInfoBox, createModalBox, createModalContent, createModalHeader, createModalOverlay, createStyledButton, createSvgIcon, getButtonStyles, } from './ui/index.js';
|
|
17
17
|
import { canvasToDataUrl, copyCanvasToClipboard, delay, formatArgs, prepareForCapture, } from './utils.js';
|
|
18
|
-
export {
|
|
18
|
+
export { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, getSettingsManager } from './settings.js';
|
|
19
19
|
const html2canvas = (html2canvasModule.default ??
|
|
20
20
|
html2canvasModule);
|
|
21
21
|
const earlyConsoleCapture = (() => {
|
|
@@ -91,6 +91,8 @@ export class GlobalDevBar {
|
|
|
91
91
|
this.apiKeyStatus = null;
|
|
92
92
|
this.lastOutline = null;
|
|
93
93
|
this.lastSchema = null;
|
|
94
|
+
this.savingOutline = false;
|
|
95
|
+
this.savingSchema = false;
|
|
94
96
|
this.consoleFilter = null;
|
|
95
97
|
// Modal states
|
|
96
98
|
this.showOutlineModal = false;
|
|
@@ -101,6 +103,8 @@ export class GlobalDevBar {
|
|
|
101
103
|
this.clsValue = 0;
|
|
102
104
|
this.inpValue = 0;
|
|
103
105
|
this.reconnectAttempts = 0;
|
|
106
|
+
this.wsVerified = false;
|
|
107
|
+
this.serverProjectDir = null;
|
|
104
108
|
// Track the position of the connection indicator dot for smooth collapse
|
|
105
109
|
this.lastDotPosition = null;
|
|
106
110
|
this.reconnectTimeout = null;
|
|
@@ -127,11 +131,39 @@ export class GlobalDevBar {
|
|
|
127
131
|
this.showSettingsPopover = false;
|
|
128
132
|
// Overlay element for modals
|
|
129
133
|
this.overlayElement = null;
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Tooltip Helpers (DRY system for HTML tooltips)
|
|
136
|
+
// ============================================================================
|
|
137
|
+
/** Base styles for tooltip containers */
|
|
138
|
+
this.TOOLTIP_BASE_STYLES = {
|
|
139
|
+
position: 'fixed',
|
|
140
|
+
zIndex: '10004',
|
|
141
|
+
backgroundColor: 'rgba(17, 24, 39, 0.98)',
|
|
142
|
+
border: `1px solid ${COLORS.border}`,
|
|
143
|
+
borderRadius: '6px',
|
|
144
|
+
padding: '10px 12px',
|
|
145
|
+
fontSize: '0.6875rem',
|
|
146
|
+
fontFamily: FONT_MONO,
|
|
147
|
+
maxWidth: '280px',
|
|
148
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.4)',
|
|
149
|
+
pointerEvents: 'none',
|
|
150
|
+
};
|
|
130
151
|
// Initialize debug config first so we can log during construction
|
|
131
152
|
this.debugConfig = normalizeDebugConfig(options.debug);
|
|
132
153
|
this.debug = new DebugLogger(this.debugConfig);
|
|
133
154
|
// Initialize settings manager
|
|
134
155
|
this.settingsManager = getSettingsManager();
|
|
156
|
+
// Calculate app port from URL for multi-instance support
|
|
157
|
+
if (typeof window !== 'undefined') {
|
|
158
|
+
this.currentAppPort =
|
|
159
|
+
parseInt(window.location.port, 10) || (window.location.protocol === 'https:' ? 443 : 80);
|
|
160
|
+
// Calculate expected WS port (appPort + port offset) like SweetlinkBridge does
|
|
161
|
+
this.baseWsPort = this.currentAppPort > 0 ? this.currentAppPort + WS_PORT_OFFSET : WS_PORT;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
this.currentAppPort = 0;
|
|
165
|
+
this.baseWsPort = WS_PORT;
|
|
166
|
+
}
|
|
135
167
|
this.options = {
|
|
136
168
|
position: options.position ?? 'bottom-left',
|
|
137
169
|
accentColor: options.accentColor ?? COLORS.primary,
|
|
@@ -345,27 +377,64 @@ export class GlobalDevBar {
|
|
|
345
377
|
document.head.appendChild(style);
|
|
346
378
|
}
|
|
347
379
|
}
|
|
348
|
-
connectWebSocket() {
|
|
380
|
+
connectWebSocket(port) {
|
|
349
381
|
if (this.destroyed)
|
|
350
382
|
return;
|
|
351
|
-
|
|
352
|
-
|
|
383
|
+
const targetPort = port ?? this.baseWsPort;
|
|
384
|
+
this.debug.ws('Connecting to WebSocket', { port: targetPort, appPort: this.currentAppPort });
|
|
385
|
+
const ws = new WebSocket(`ws://localhost:${targetPort}`);
|
|
353
386
|
this.ws = ws;
|
|
387
|
+
this.wsVerified = false;
|
|
354
388
|
ws.onopen = () => {
|
|
355
|
-
this.
|
|
356
|
-
this.reconnectAttempts = 0;
|
|
357
|
-
this.debug.ws('WebSocket connected');
|
|
358
|
-
// Update settings manager with WebSocket connection
|
|
359
|
-
this.settingsManager.setWebSocket(ws);
|
|
360
|
-
this.settingsManager.setConnected(true);
|
|
389
|
+
this.debug.ws('WebSocket socket opened, awaiting server-info');
|
|
361
390
|
ws.send(JSON.stringify({ type: 'browser-client-ready' }));
|
|
362
|
-
// Request settings from server
|
|
363
|
-
ws.send(JSON.stringify({ type: 'load-settings' }));
|
|
364
|
-
this.render();
|
|
365
391
|
};
|
|
366
392
|
ws.onmessage = async (event) => {
|
|
367
393
|
try {
|
|
368
|
-
const
|
|
394
|
+
const message = JSON.parse(event.data);
|
|
395
|
+
// Handle server-info for port matching
|
|
396
|
+
if (message.type === 'server-info') {
|
|
397
|
+
const serverAppPort = message.appPort;
|
|
398
|
+
const serverMatchesApp = serverAppPort === null || serverAppPort === this.currentAppPort;
|
|
399
|
+
if (!serverMatchesApp) {
|
|
400
|
+
this.debug.ws('Server mismatch', {
|
|
401
|
+
serverAppPort,
|
|
402
|
+
currentAppPort: this.currentAppPort,
|
|
403
|
+
tryingNextPort: targetPort + 1,
|
|
404
|
+
});
|
|
405
|
+
ws.close();
|
|
406
|
+
// Try next port
|
|
407
|
+
const nextPort = targetPort + 1;
|
|
408
|
+
if (nextPort < this.baseWsPort + MAX_PORT_RETRIES) {
|
|
409
|
+
setTimeout(() => this.connectWebSocket(nextPort), PORT_RETRY_DELAY_MS);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
this.debug.ws('No matching server found, will retry from base port');
|
|
413
|
+
setTimeout(() => this.connectWebSocket(this.baseWsPort), PORT_SCAN_RESTART_DELAY_MS);
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// Server matches - mark as verified and connected
|
|
418
|
+
this.wsVerified = true;
|
|
419
|
+
this.sweetlinkConnected = true;
|
|
420
|
+
this.reconnectAttempts = 0;
|
|
421
|
+
this.serverProjectDir = message.projectDir ?? null;
|
|
422
|
+
this.debug.ws('Server verified', {
|
|
423
|
+
appPort: serverAppPort ?? 'any',
|
|
424
|
+
projectDir: this.serverProjectDir,
|
|
425
|
+
});
|
|
426
|
+
this.settingsManager.setWebSocket(ws);
|
|
427
|
+
this.settingsManager.setConnected(true);
|
|
428
|
+
ws.send(JSON.stringify({ type: 'load-settings' }));
|
|
429
|
+
this.render();
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
// Ignore other commands until verified
|
|
433
|
+
if (!this.wsVerified) {
|
|
434
|
+
this.debug.ws('Ignoring command before verification', { type: message.type });
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const command = message;
|
|
369
438
|
this.debug.ws('Received command', { type: command.type });
|
|
370
439
|
await this.handleSweetlinkCommand(command);
|
|
371
440
|
}
|
|
@@ -374,16 +443,21 @@ export class GlobalDevBar {
|
|
|
374
443
|
}
|
|
375
444
|
};
|
|
376
445
|
ws.onclose = () => {
|
|
377
|
-
|
|
378
|
-
this.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
this.
|
|
385
|
-
|
|
386
|
-
this.
|
|
446
|
+
// Only reset connection state if we were actually verified/connected
|
|
447
|
+
if (this.wsVerified) {
|
|
448
|
+
this.sweetlinkConnected = false;
|
|
449
|
+
this.wsVerified = false;
|
|
450
|
+
this.serverProjectDir = null;
|
|
451
|
+
this.settingsManager.setConnected(false);
|
|
452
|
+
this.debug.ws('WebSocket disconnected');
|
|
453
|
+
this.render();
|
|
454
|
+
// Auto-reconnect with exponential backoff (start from base port)
|
|
455
|
+
if (!this.destroyed && this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
|
456
|
+
const delayMs = BASE_RECONNECT_DELAY_MS * 2 ** this.reconnectAttempts;
|
|
457
|
+
this.reconnectAttempts++;
|
|
458
|
+
this.debug.ws('Scheduling reconnect', { attempt: this.reconnectAttempts, delayMs });
|
|
459
|
+
this.reconnectTimeout = setTimeout(() => this.connectWebSocket(this.baseWsPort), Math.min(delayMs, MAX_RECONNECT_DELAY_MS));
|
|
460
|
+
}
|
|
387
461
|
}
|
|
388
462
|
};
|
|
389
463
|
ws.onerror = () => {
|
|
@@ -579,6 +653,7 @@ export class GlobalDevBar {
|
|
|
579
653
|
}, durationMs);
|
|
580
654
|
break;
|
|
581
655
|
case 'outline':
|
|
656
|
+
this.savingOutline = false;
|
|
582
657
|
this.lastOutline = path;
|
|
583
658
|
if (this.outlineTimeout)
|
|
584
659
|
clearTimeout(this.outlineTimeout);
|
|
@@ -588,6 +663,7 @@ export class GlobalDevBar {
|
|
|
588
663
|
}, durationMs);
|
|
589
664
|
break;
|
|
590
665
|
case 'schema':
|
|
666
|
+
this.savingSchema = false;
|
|
591
667
|
this.lastSchema = path;
|
|
592
668
|
if (this.schemaTimeout)
|
|
593
669
|
clearTimeout(this.schemaTimeout);
|
|
@@ -720,7 +796,11 @@ export class GlobalDevBar {
|
|
|
720
796
|
}
|
|
721
797
|
});
|
|
722
798
|
// durationThreshold filters out very short interactions
|
|
723
|
-
this.inpObserver.observe({
|
|
799
|
+
this.inpObserver.observe({
|
|
800
|
+
type: 'event',
|
|
801
|
+
buffered: true,
|
|
802
|
+
durationThreshold: 16,
|
|
803
|
+
});
|
|
724
804
|
}
|
|
725
805
|
catch (e) {
|
|
726
806
|
console.warn('[GlobalDevBar] INP PerformanceObserver not supported', e);
|
|
@@ -777,12 +857,16 @@ export class GlobalDevBar {
|
|
|
777
857
|
// Load stored theme preference from settings manager
|
|
778
858
|
const settings = this.settingsManager.getSettings();
|
|
779
859
|
this.themeMode = settings.themeMode;
|
|
860
|
+
// Inject the appropriate theme CSS variables on initial load
|
|
861
|
+
injectThemeCSS(getTheme(this.themeMode));
|
|
780
862
|
this.debug.state('Theme loaded', { mode: this.themeMode });
|
|
781
863
|
// Listen for system theme changes
|
|
782
864
|
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
783
865
|
this.themeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
784
866
|
this.themeMediaHandler = () => {
|
|
785
867
|
if (this.themeMode === 'system') {
|
|
868
|
+
// Re-inject theme CSS when system preference changes
|
|
869
|
+
injectThemeCSS(getTheme(this.themeMode));
|
|
786
870
|
this.debug.state('System theme changed', {
|
|
787
871
|
effectiveTheme: getEffectiveTheme(this.themeMode),
|
|
788
872
|
});
|
|
@@ -809,6 +893,8 @@ export class GlobalDevBar {
|
|
|
809
893
|
setThemeMode(mode) {
|
|
810
894
|
this.themeMode = mode;
|
|
811
895
|
this.settingsManager.saveSettings({ themeMode: mode });
|
|
896
|
+
// Inject the appropriate theme CSS variables
|
|
897
|
+
injectThemeCSS(getTheme(mode));
|
|
812
898
|
this.debug.state('Theme mode changed', { mode, effectiveTheme: getEffectiveTheme(mode) });
|
|
813
899
|
this.render();
|
|
814
900
|
}
|
|
@@ -1062,9 +1148,13 @@ export class GlobalDevBar {
|
|
|
1062
1148
|
this.render();
|
|
1063
1149
|
}
|
|
1064
1150
|
handleSaveOutline() {
|
|
1151
|
+
if (this.savingOutline)
|
|
1152
|
+
return; // Prevent repeated clicks
|
|
1065
1153
|
const outline = extractDocumentOutline();
|
|
1066
1154
|
const markdown = outlineToMarkdown(outline);
|
|
1067
1155
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
1156
|
+
this.savingOutline = true;
|
|
1157
|
+
this.render();
|
|
1068
1158
|
this.ws.send(JSON.stringify({
|
|
1069
1159
|
type: 'save-outline',
|
|
1070
1160
|
data: {
|
|
@@ -1078,9 +1168,13 @@ export class GlobalDevBar {
|
|
|
1078
1168
|
}
|
|
1079
1169
|
}
|
|
1080
1170
|
handleSaveSchema() {
|
|
1171
|
+
if (this.savingSchema)
|
|
1172
|
+
return; // Prevent repeated clicks
|
|
1081
1173
|
const schema = extractPageSchema();
|
|
1082
1174
|
const markdown = schemaToMarkdown(schema);
|
|
1083
1175
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
1176
|
+
this.savingSchema = true;
|
|
1177
|
+
this.render();
|
|
1084
1178
|
this.ws.send(JSON.stringify({
|
|
1085
1179
|
type: 'save-schema',
|
|
1086
1180
|
data: {
|
|
@@ -1458,6 +1552,8 @@ export class GlobalDevBar {
|
|
|
1458
1552
|
},
|
|
1459
1553
|
onSave: () => this.handleSaveOutline(),
|
|
1460
1554
|
sweetlinkConnected: this.sweetlinkConnected,
|
|
1555
|
+
isSaving: this.savingOutline,
|
|
1556
|
+
savedPath: this.lastOutline,
|
|
1461
1557
|
});
|
|
1462
1558
|
modal.appendChild(header);
|
|
1463
1559
|
const content = createModalContent();
|
|
@@ -1544,6 +1640,8 @@ export class GlobalDevBar {
|
|
|
1544
1640
|
},
|
|
1545
1641
|
onSave: () => this.handleSaveSchema(),
|
|
1546
1642
|
sweetlinkConnected: this.sweetlinkConnected,
|
|
1643
|
+
isSaving: this.savingSchema,
|
|
1644
|
+
savedPath: this.lastSchema,
|
|
1547
1645
|
});
|
|
1548
1646
|
modal.appendChild(header);
|
|
1549
1647
|
const content = createModalContent();
|
|
@@ -1901,6 +1999,64 @@ export class GlobalDevBar {
|
|
|
1901
1999
|
btn.appendChild(svg);
|
|
1902
2000
|
return btn;
|
|
1903
2001
|
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Create the compact mode toggle button with chevron icon
|
|
2004
|
+
*/
|
|
2005
|
+
createCompactToggleButton() {
|
|
2006
|
+
const btn = document.createElement('button');
|
|
2007
|
+
btn.type = 'button';
|
|
2008
|
+
btn.className = this.tooltipClass('right');
|
|
2009
|
+
const isCompact = this.compactMode;
|
|
2010
|
+
const tooltip = isCompact ? 'Expand (Cmd+Shift+M)' : 'Compact (Cmd+Shift+M)';
|
|
2011
|
+
btn.setAttribute('data-tooltip', tooltip);
|
|
2012
|
+
const { accentColor } = this.options;
|
|
2013
|
+
const iconColor = COLORS.textSecondary;
|
|
2014
|
+
Object.assign(btn.style, {
|
|
2015
|
+
display: 'flex',
|
|
2016
|
+
alignItems: 'center',
|
|
2017
|
+
justifyContent: 'center',
|
|
2018
|
+
width: '22px',
|
|
2019
|
+
height: '22px',
|
|
2020
|
+
minWidth: '22px',
|
|
2021
|
+
minHeight: '22px',
|
|
2022
|
+
flexShrink: '0',
|
|
2023
|
+
borderRadius: '50%',
|
|
2024
|
+
border: `1px solid ${accentColor}60`,
|
|
2025
|
+
backgroundColor: 'transparent',
|
|
2026
|
+
color: `${iconColor}99`,
|
|
2027
|
+
cursor: 'pointer',
|
|
2028
|
+
transition: 'all 150ms',
|
|
2029
|
+
});
|
|
2030
|
+
btn.onmouseenter = () => {
|
|
2031
|
+
btn.style.borderColor = accentColor;
|
|
2032
|
+
btn.style.backgroundColor = `${accentColor}20`;
|
|
2033
|
+
btn.style.color = iconColor;
|
|
2034
|
+
};
|
|
2035
|
+
btn.onmouseleave = () => {
|
|
2036
|
+
btn.style.borderColor = `${accentColor}60`;
|
|
2037
|
+
btn.style.backgroundColor = 'transparent';
|
|
2038
|
+
btn.style.color = `${iconColor}99`;
|
|
2039
|
+
};
|
|
2040
|
+
btn.onclick = () => {
|
|
2041
|
+
this.toggleCompactMode();
|
|
2042
|
+
};
|
|
2043
|
+
// Chevron icon SVG - points right when expanded, left when compact
|
|
2044
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
2045
|
+
svg.setAttribute('width', '12');
|
|
2046
|
+
svg.setAttribute('height', '12');
|
|
2047
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
2048
|
+
svg.setAttribute('fill', 'none');
|
|
2049
|
+
svg.setAttribute('stroke', 'currentColor');
|
|
2050
|
+
svg.setAttribute('stroke-width', '2');
|
|
2051
|
+
svg.setAttribute('stroke-linecap', 'round');
|
|
2052
|
+
svg.setAttribute('stroke-linejoin', 'round');
|
|
2053
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
|
|
2054
|
+
// Left chevron (<) when expanded to shrink, right chevron (>) when compact to expand
|
|
2055
|
+
path.setAttribute('points', isCompact ? '9 18 15 12 9 6' : '15 18 9 12 15 6');
|
|
2056
|
+
svg.appendChild(path);
|
|
2057
|
+
btn.appendChild(svg);
|
|
2058
|
+
return btn;
|
|
2059
|
+
}
|
|
1904
2060
|
/**
|
|
1905
2061
|
* Create a settings section with title
|
|
1906
2062
|
*/
|
|
@@ -2054,49 +2210,78 @@ export class GlobalDevBar {
|
|
|
2054
2210
|
popover.appendChild(themeSection);
|
|
2055
2211
|
// ========== DISPLAY SECTION ==========
|
|
2056
2212
|
const displaySection = this.createSettingsSection('Display');
|
|
2057
|
-
// Position
|
|
2213
|
+
// Position mini-map selector
|
|
2058
2214
|
const positionRow = document.createElement('div');
|
|
2059
|
-
Object.assign(positionRow.style, {
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2215
|
+
Object.assign(positionRow.style, { marginBottom: '10px' });
|
|
2216
|
+
const posLabel = document.createElement('div');
|
|
2217
|
+
Object.assign(posLabel.style, {
|
|
2218
|
+
color: COLORS.text,
|
|
2219
|
+
fontSize: '0.6875rem',
|
|
2220
|
+
marginBottom: '6px',
|
|
2064
2221
|
});
|
|
2065
|
-
const posLabel = document.createElement('span');
|
|
2066
|
-
Object.assign(posLabel.style, { color: COLORS.text, fontSize: '0.6875rem' });
|
|
2067
2222
|
posLabel.textContent = 'Position';
|
|
2068
2223
|
positionRow.appendChild(posLabel);
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2224
|
+
// Mini-map container
|
|
2225
|
+
const miniMap = document.createElement('div');
|
|
2226
|
+
Object.assign(miniMap.style, {
|
|
2227
|
+
position: 'relative',
|
|
2228
|
+
width: '100%',
|
|
2229
|
+
height: '50px',
|
|
2230
|
+
backgroundColor: 'rgba(10, 15, 26, 0.6)',
|
|
2231
|
+
border: `1px solid ${color}30`,
|
|
2073
2232
|
borderRadius: '4px',
|
|
2074
|
-
color: COLORS.text,
|
|
2075
|
-
fontSize: '0.625rem',
|
|
2076
|
-
padding: '4px 6px',
|
|
2077
|
-
cursor: 'pointer',
|
|
2078
|
-
fontFamily: FONT_MONO,
|
|
2079
2233
|
});
|
|
2080
|
-
const
|
|
2081
|
-
{ value: '
|
|
2082
|
-
{ value: '
|
|
2083
|
-
{ value: 'bottom-
|
|
2084
|
-
{ value: '
|
|
2085
|
-
{
|
|
2234
|
+
const positionConfigs = [
|
|
2235
|
+
{ value: 'top-left', style: { top: '8px', left: '10%' }, title: 'Top Left' },
|
|
2236
|
+
{ value: 'top-right', style: { top: '8px', right: '6%' }, title: 'Top Right' },
|
|
2237
|
+
{ value: 'bottom-left', style: { bottom: '8px', left: '10%' }, title: 'Bottom Left' },
|
|
2238
|
+
{ value: 'bottom-right', style: { bottom: '8px', right: '6%' }, title: 'Bottom Right' },
|
|
2239
|
+
{
|
|
2240
|
+
value: 'bottom-center',
|
|
2241
|
+
style: { bottom: '6px', left: '50%', transform: 'translateX(-50%)' },
|
|
2242
|
+
title: 'Bottom Center',
|
|
2243
|
+
},
|
|
2086
2244
|
];
|
|
2087
|
-
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2245
|
+
positionConfigs.forEach(({ value, style, title }) => {
|
|
2246
|
+
const indicator = document.createElement('button');
|
|
2247
|
+
const isActive = this.options.position === value;
|
|
2248
|
+
Object.assign(indicator.style, {
|
|
2249
|
+
position: 'absolute',
|
|
2250
|
+
width: '20px',
|
|
2251
|
+
height: '6px',
|
|
2252
|
+
backgroundColor: isActive ? accentColor : `${color}60`,
|
|
2253
|
+
border: `1px solid ${isActive ? accentColor : `${color}40`}`,
|
|
2254
|
+
borderRadius: '2px',
|
|
2255
|
+
cursor: 'pointer',
|
|
2256
|
+
padding: '0',
|
|
2257
|
+
transition: 'all 150ms',
|
|
2258
|
+
boxShadow: isActive ? `0 0 8px ${accentColor}60` : 'none',
|
|
2259
|
+
...style,
|
|
2260
|
+
});
|
|
2261
|
+
indicator.title = title;
|
|
2262
|
+
indicator.onclick = () => {
|
|
2263
|
+
this.options.position = value;
|
|
2264
|
+
this.settingsManager.saveSettings({ position: value });
|
|
2265
|
+
this.render();
|
|
2266
|
+
};
|
|
2267
|
+
// Hover effect
|
|
2268
|
+
indicator.onmouseenter = () => {
|
|
2269
|
+
if (!isActive) {
|
|
2270
|
+
indicator.style.backgroundColor = accentColor;
|
|
2271
|
+
indicator.style.borderColor = accentColor;
|
|
2272
|
+
indicator.style.boxShadow = `0 0 6px ${accentColor}40`;
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
indicator.onmouseleave = () => {
|
|
2276
|
+
if (!isActive) {
|
|
2277
|
+
indicator.style.backgroundColor = `${color}60`;
|
|
2278
|
+
indicator.style.borderColor = `${color}40`;
|
|
2279
|
+
indicator.style.boxShadow = 'none';
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
miniMap.appendChild(indicator);
|
|
2093
2283
|
});
|
|
2094
|
-
|
|
2095
|
-
this.options.position = posSelect.value;
|
|
2096
|
-
this.settingsManager.saveSettings({ position: this.options.position });
|
|
2097
|
-
this.render();
|
|
2098
|
-
};
|
|
2099
|
-
positionRow.appendChild(posSelect);
|
|
2284
|
+
positionRow.appendChild(miniMap);
|
|
2100
2285
|
displaySection.appendChild(positionRow);
|
|
2101
2286
|
// Compact mode toggle
|
|
2102
2287
|
displaySection.appendChild(this.createToggleRow('Compact Mode', this.compactMode, accentColor, () => {
|
|
@@ -2458,9 +2643,10 @@ export class GlobalDevBar {
|
|
|
2458
2643
|
const bp = this.breakpointInfo.tailwindBreakpoint;
|
|
2459
2644
|
const breakpointData = TAILWIND_BREAKPOINTS[bp];
|
|
2460
2645
|
const bpSpan = document.createElement('span');
|
|
2461
|
-
bpSpan.className =
|
|
2646
|
+
bpSpan.className = 'devbar-item';
|
|
2462
2647
|
Object.assign(bpSpan.style, { opacity: '0.9', cursor: 'default' });
|
|
2463
|
-
|
|
2648
|
+
// Use HTML tooltip for breakpoint info
|
|
2649
|
+
this.attachBreakpointTooltip(bpSpan, bp, this.breakpointInfo.dimensions, breakpointData?.label || '');
|
|
2464
2650
|
let bpText = bp;
|
|
2465
2651
|
if (bp !== 'base') {
|
|
2466
2652
|
bpText =
|
|
@@ -2482,45 +2668,45 @@ export class GlobalDevBar {
|
|
|
2482
2668
|
if (showMetrics.fcp) {
|
|
2483
2669
|
addSeparator();
|
|
2484
2670
|
const fcpSpan = document.createElement('span');
|
|
2485
|
-
fcpSpan.className =
|
|
2671
|
+
fcpSpan.className = 'devbar-item';
|
|
2486
2672
|
Object.assign(fcpSpan.style, { opacity: '0.85', cursor: 'default' });
|
|
2487
|
-
fcpSpan.setAttribute('data-tooltip', 'First Contentful Paint (FCP): Time until first text/image renders.\n\nGood: <1.8s\nNeeds work: 1.8-3s\nPoor: >3s');
|
|
2488
2673
|
fcpSpan.textContent = `FCP ${this.perfStats.fcp}`;
|
|
2674
|
+
this.attachMetricTooltip(fcpSpan, 'First Contentful Paint (FCP)', 'Time until the first text or image renders on screen.', { good: '<1.8s', needsWork: '1.8-3s', poor: '>3s' });
|
|
2489
2675
|
infoSection.appendChild(fcpSpan);
|
|
2490
2676
|
}
|
|
2491
2677
|
if (showMetrics.lcp) {
|
|
2492
2678
|
addSeparator();
|
|
2493
2679
|
const lcpSpan = document.createElement('span');
|
|
2494
|
-
lcpSpan.className =
|
|
2680
|
+
lcpSpan.className = 'devbar-item';
|
|
2495
2681
|
Object.assign(lcpSpan.style, { opacity: '0.85', cursor: 'default' });
|
|
2496
|
-
lcpSpan.setAttribute('data-tooltip', 'Largest Contentful Paint (LCP): Time until largest visible element renders.\n\nGood: <2.5s\nNeeds work: 2.5-4s\nPoor: >4s');
|
|
2497
2682
|
lcpSpan.textContent = `LCP ${this.perfStats.lcp}`;
|
|
2683
|
+
this.attachMetricTooltip(lcpSpan, 'Largest Contentful Paint (LCP)', 'Time until the largest visible element renders on screen.', { good: '<2.5s', needsWork: '2.5-4s', poor: '>4s' });
|
|
2498
2684
|
infoSection.appendChild(lcpSpan);
|
|
2499
2685
|
}
|
|
2500
2686
|
if (showMetrics.cls) {
|
|
2501
2687
|
addSeparator();
|
|
2502
2688
|
const clsSpan = document.createElement('span');
|
|
2503
|
-
clsSpan.className =
|
|
2689
|
+
clsSpan.className = 'devbar-item';
|
|
2504
2690
|
Object.assign(clsSpan.style, { opacity: '0.85', cursor: 'default' });
|
|
2505
|
-
clsSpan.setAttribute('data-tooltip', 'Cumulative Layout Shift (CLS): Visual stability score.\nHigher values mean more unexpected layout shifts.\n\nGood: <0.1\nNeeds work: 0.1-0.25\nPoor: >0.25');
|
|
2506
2691
|
clsSpan.textContent = `CLS ${this.perfStats.cls}`;
|
|
2692
|
+
this.attachMetricTooltip(clsSpan, 'Cumulative Layout Shift (CLS)', 'Visual stability score. Higher values mean more unexpected layout shifts.', { good: '<0.1', needsWork: '0.1-0.25', poor: '>0.25' });
|
|
2507
2693
|
infoSection.appendChild(clsSpan);
|
|
2508
2694
|
}
|
|
2509
2695
|
if (showMetrics.inp) {
|
|
2510
2696
|
addSeparator();
|
|
2511
2697
|
const inpSpan = document.createElement('span');
|
|
2512
|
-
inpSpan.className =
|
|
2698
|
+
inpSpan.className = 'devbar-item';
|
|
2513
2699
|
Object.assign(inpSpan.style, { opacity: '0.85', cursor: 'default' });
|
|
2514
|
-
inpSpan.setAttribute('data-tooltip', 'Interaction to Next Paint (INP): Responsiveness to user input.\nMeasures the longest interaction delay.\n\nGood: <200ms\nNeeds work: 200-500ms\nPoor: >500ms');
|
|
2515
2700
|
inpSpan.textContent = `INP ${this.perfStats.inp}`;
|
|
2701
|
+
this.attachMetricTooltip(inpSpan, 'Interaction to Next Paint (INP)', 'Responsiveness to user input. Measures the longest interaction delay.', { good: '<200ms', needsWork: '200-500ms', poor: '>500ms' });
|
|
2516
2702
|
infoSection.appendChild(inpSpan);
|
|
2517
2703
|
}
|
|
2518
2704
|
if (showMetrics.pageSize) {
|
|
2519
2705
|
addSeparator();
|
|
2520
2706
|
const sizeSpan = document.createElement('span');
|
|
2521
|
-
sizeSpan.className =
|
|
2707
|
+
sizeSpan.className = 'devbar-item';
|
|
2522
2708
|
Object.assign(sizeSpan.style, { opacity: '0.7', cursor: 'default' });
|
|
2523
|
-
|
|
2709
|
+
this.attachInfoTooltip(sizeSpan, 'Total Page Size', 'Compressed/transferred size including HTML, CSS, JS, images, and other resources.');
|
|
2524
2710
|
sizeSpan.textContent = this.perfStats.totalSize;
|
|
2525
2711
|
infoSection.appendChild(sizeSpan);
|
|
2526
2712
|
}
|
|
@@ -2546,6 +2732,7 @@ export class GlobalDevBar {
|
|
|
2546
2732
|
actionsContainer.appendChild(this.createOutlineButton());
|
|
2547
2733
|
actionsContainer.appendChild(this.createSchemaButton());
|
|
2548
2734
|
actionsContainer.appendChild(this.createSettingsButton());
|
|
2735
|
+
actionsContainer.appendChild(this.createCompactToggleButton());
|
|
2549
2736
|
mainRow.appendChild(actionsContainer);
|
|
2550
2737
|
wrapper.appendChild(mainRow);
|
|
2551
2738
|
// Render custom controls row if there are any
|
|
@@ -2600,6 +2787,207 @@ export class GlobalDevBar {
|
|
|
2600
2787
|
wrapper.appendChild(customRow);
|
|
2601
2788
|
}
|
|
2602
2789
|
}
|
|
2790
|
+
/** Create a tooltip container element */
|
|
2791
|
+
createTooltipContainer() {
|
|
2792
|
+
const tooltip = document.createElement('div');
|
|
2793
|
+
tooltip.setAttribute('data-devbar', 'true');
|
|
2794
|
+
Object.assign(tooltip.style, this.TOOLTIP_BASE_STYLES);
|
|
2795
|
+
return tooltip;
|
|
2796
|
+
}
|
|
2797
|
+
/** Add a bold title to tooltip (metric name, feature name, etc.) */
|
|
2798
|
+
addTooltipTitle(container, title) {
|
|
2799
|
+
const titleEl = document.createElement('div');
|
|
2800
|
+
const accentColor = this.settingsManager.get('accentColor') || COLORS.primary;
|
|
2801
|
+
Object.assign(titleEl.style, {
|
|
2802
|
+
color: accentColor,
|
|
2803
|
+
fontWeight: '600',
|
|
2804
|
+
marginBottom: '4px',
|
|
2805
|
+
});
|
|
2806
|
+
titleEl.textContent = title;
|
|
2807
|
+
container.appendChild(titleEl);
|
|
2808
|
+
}
|
|
2809
|
+
/** Add a description paragraph to tooltip */
|
|
2810
|
+
addTooltipDescription(container, description) {
|
|
2811
|
+
const descEl = document.createElement('div');
|
|
2812
|
+
Object.assign(descEl.style, {
|
|
2813
|
+
color: COLORS.text,
|
|
2814
|
+
marginBottom: '10px',
|
|
2815
|
+
lineHeight: '1.4',
|
|
2816
|
+
});
|
|
2817
|
+
descEl.textContent = description;
|
|
2818
|
+
container.appendChild(descEl);
|
|
2819
|
+
}
|
|
2820
|
+
/** Add a muted uppercase section header to tooltip */
|
|
2821
|
+
addTooltipSectionHeader(container, header) {
|
|
2822
|
+
const headerEl = document.createElement('div');
|
|
2823
|
+
Object.assign(headerEl.style, {
|
|
2824
|
+
color: COLORS.textMuted,
|
|
2825
|
+
fontSize: '0.625rem',
|
|
2826
|
+
textTransform: 'uppercase',
|
|
2827
|
+
letterSpacing: '0.05em',
|
|
2828
|
+
marginBottom: '6px',
|
|
2829
|
+
});
|
|
2830
|
+
headerEl.textContent = header;
|
|
2831
|
+
container.appendChild(headerEl);
|
|
2832
|
+
}
|
|
2833
|
+
/** Add a colored row with dot + label + value (for thresholds) */
|
|
2834
|
+
addTooltipColoredRow(container, label, value, color, labelWidth = '70px') {
|
|
2835
|
+
const row = document.createElement('div');
|
|
2836
|
+
Object.assign(row.style, { display: 'flex', alignItems: 'center', gap: '8px' });
|
|
2837
|
+
const dot = document.createElement('span');
|
|
2838
|
+
Object.assign(dot.style, {
|
|
2839
|
+
width: '6px',
|
|
2840
|
+
height: '6px',
|
|
2841
|
+
borderRadius: '50%',
|
|
2842
|
+
backgroundColor: color,
|
|
2843
|
+
flexShrink: '0',
|
|
2844
|
+
});
|
|
2845
|
+
row.appendChild(dot);
|
|
2846
|
+
const labelSpan = document.createElement('span');
|
|
2847
|
+
Object.assign(labelSpan.style, {
|
|
2848
|
+
color,
|
|
2849
|
+
fontWeight: '500',
|
|
2850
|
+
minWidth: labelWidth,
|
|
2851
|
+
});
|
|
2852
|
+
labelSpan.textContent = label;
|
|
2853
|
+
row.appendChild(labelSpan);
|
|
2854
|
+
const valueSpan = document.createElement('span');
|
|
2855
|
+
Object.assign(valueSpan.style, { color: COLORS.textMuted });
|
|
2856
|
+
valueSpan.textContent = value;
|
|
2857
|
+
row.appendChild(valueSpan);
|
|
2858
|
+
container.appendChild(row);
|
|
2859
|
+
}
|
|
2860
|
+
/** Add an info row with label + value (for breakpoint details) */
|
|
2861
|
+
addTooltipInfoRow(container, label, value) {
|
|
2862
|
+
const row = document.createElement('div');
|
|
2863
|
+
Object.assign(row.style, {
|
|
2864
|
+
display: 'flex',
|
|
2865
|
+
gap: '8px',
|
|
2866
|
+
lineHeight: '1.4',
|
|
2867
|
+
});
|
|
2868
|
+
const labelSpan = document.createElement('span');
|
|
2869
|
+
Object.assign(labelSpan.style, { color: COLORS.textMuted });
|
|
2870
|
+
labelSpan.textContent = label;
|
|
2871
|
+
row.appendChild(labelSpan);
|
|
2872
|
+
const valueSpan = document.createElement('span');
|
|
2873
|
+
Object.assign(valueSpan.style, { color: COLORS.text });
|
|
2874
|
+
valueSpan.textContent = value;
|
|
2875
|
+
row.appendChild(valueSpan);
|
|
2876
|
+
container.appendChild(row);
|
|
2877
|
+
}
|
|
2878
|
+
/** Position tooltip above the anchor element, adjusting for screen edges */
|
|
2879
|
+
positionTooltip(tooltip, anchor) {
|
|
2880
|
+
const rect = anchor.getBoundingClientRect();
|
|
2881
|
+
tooltip.style.left = `${rect.left}px`;
|
|
2882
|
+
tooltip.style.bottom = `${window.innerHeight - rect.top + 8}px`;
|
|
2883
|
+
document.body.appendChild(tooltip);
|
|
2884
|
+
// Adjust if off-screen
|
|
2885
|
+
const tooltipRect = tooltip.getBoundingClientRect();
|
|
2886
|
+
if (tooltipRect.right > window.innerWidth - 10) {
|
|
2887
|
+
tooltip.style.left = `${window.innerWidth - tooltipRect.width - 10}px`;
|
|
2888
|
+
}
|
|
2889
|
+
if (tooltipRect.left < 10) {
|
|
2890
|
+
tooltip.style.left = '10px';
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
/** Attach an HTML tooltip to an element with custom content builder */
|
|
2894
|
+
attachHtmlTooltip(element, buildContent) {
|
|
2895
|
+
let tooltipEl = null;
|
|
2896
|
+
element.onmouseenter = () => {
|
|
2897
|
+
tooltipEl = this.createTooltipContainer();
|
|
2898
|
+
buildContent(tooltipEl);
|
|
2899
|
+
this.positionTooltip(tooltipEl, element);
|
|
2900
|
+
};
|
|
2901
|
+
element.onmouseleave = () => {
|
|
2902
|
+
if (tooltipEl) {
|
|
2903
|
+
tooltipEl.remove();
|
|
2904
|
+
tooltipEl = null;
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
}
|
|
2908
|
+
// ============================================================================
|
|
2909
|
+
// Tooltip Attachment Methods (specific tooltip types)
|
|
2910
|
+
// ============================================================================
|
|
2911
|
+
/** Attach a metric tooltip with title, description, and colored thresholds */
|
|
2912
|
+
attachMetricTooltip(element, title, description, thresholds) {
|
|
2913
|
+
this.attachHtmlTooltip(element, (tooltip) => {
|
|
2914
|
+
this.addTooltipTitle(tooltip, title);
|
|
2915
|
+
this.addTooltipDescription(tooltip, description);
|
|
2916
|
+
this.addTooltipSectionHeader(tooltip, 'Thresholds');
|
|
2917
|
+
const thresholdsContainer = document.createElement('div');
|
|
2918
|
+
Object.assign(thresholdsContainer.style, {
|
|
2919
|
+
display: 'flex',
|
|
2920
|
+
flexDirection: 'column',
|
|
2921
|
+
gap: '4px',
|
|
2922
|
+
});
|
|
2923
|
+
this.addTooltipColoredRow(thresholdsContainer, 'Good', thresholds.good, COLORS.primary);
|
|
2924
|
+
this.addTooltipColoredRow(thresholdsContainer, 'Needs work', thresholds.needsWork, COLORS.warning);
|
|
2925
|
+
this.addTooltipColoredRow(thresholdsContainer, 'Poor', thresholds.poor, COLORS.error);
|
|
2926
|
+
tooltip.appendChild(thresholdsContainer);
|
|
2927
|
+
});
|
|
2928
|
+
}
|
|
2929
|
+
/** Attach a breakpoint tooltip showing current breakpoint and all breakpoint ranges */
|
|
2930
|
+
attachBreakpointTooltip(element, breakpoint, dimensions, breakpointLabel) {
|
|
2931
|
+
this.attachHtmlTooltip(element, (tooltip) => {
|
|
2932
|
+
this.addTooltipTitle(tooltip, 'Tailwind Breakpoint');
|
|
2933
|
+
// Current breakpoint info
|
|
2934
|
+
const currentSection = document.createElement('div');
|
|
2935
|
+
Object.assign(currentSection.style, { marginBottom: '10px' });
|
|
2936
|
+
this.addTooltipInfoRow(currentSection, 'Current:', `${breakpoint} (${breakpointLabel})`);
|
|
2937
|
+
this.addTooltipInfoRow(currentSection, 'Viewport:', dimensions);
|
|
2938
|
+
tooltip.appendChild(currentSection);
|
|
2939
|
+
// Breakpoints reference
|
|
2940
|
+
this.addTooltipSectionHeader(tooltip, 'Breakpoints');
|
|
2941
|
+
const bpContainer = document.createElement('div');
|
|
2942
|
+
Object.assign(bpContainer.style, {
|
|
2943
|
+
display: 'flex',
|
|
2944
|
+
flexDirection: 'column',
|
|
2945
|
+
gap: '2px',
|
|
2946
|
+
fontSize: '0.625rem',
|
|
2947
|
+
});
|
|
2948
|
+
const breakpoints = [
|
|
2949
|
+
{ name: 'base', range: '<640px' },
|
|
2950
|
+
{ name: 'sm', range: '≥640px' },
|
|
2951
|
+
{ name: 'md', range: '≥768px' },
|
|
2952
|
+
{ name: 'lg', range: '≥1024px' },
|
|
2953
|
+
{ name: 'xl', range: '≥1280px' },
|
|
2954
|
+
{ name: '2xl', range: '≥1536px' },
|
|
2955
|
+
];
|
|
2956
|
+
for (const bp of breakpoints) {
|
|
2957
|
+
const row = document.createElement('div');
|
|
2958
|
+
Object.assign(row.style, { display: 'flex', gap: '8px' });
|
|
2959
|
+
const nameSpan = document.createElement('span');
|
|
2960
|
+
Object.assign(nameSpan.style, {
|
|
2961
|
+
color: bp.name === breakpoint ? COLORS.primary : COLORS.textMuted,
|
|
2962
|
+
fontWeight: bp.name === breakpoint ? '600' : '400',
|
|
2963
|
+
minWidth: '32px',
|
|
2964
|
+
});
|
|
2965
|
+
nameSpan.textContent = bp.name;
|
|
2966
|
+
row.appendChild(nameSpan);
|
|
2967
|
+
const rangeSpan = document.createElement('span');
|
|
2968
|
+
Object.assign(rangeSpan.style, {
|
|
2969
|
+
color: bp.name === breakpoint ? COLORS.text : COLORS.textMuted,
|
|
2970
|
+
});
|
|
2971
|
+
rangeSpan.textContent = bp.range;
|
|
2972
|
+
row.appendChild(rangeSpan);
|
|
2973
|
+
bpContainer.appendChild(row);
|
|
2974
|
+
}
|
|
2975
|
+
tooltip.appendChild(bpContainer);
|
|
2976
|
+
});
|
|
2977
|
+
}
|
|
2978
|
+
/** Attach a simple info tooltip with title and description */
|
|
2979
|
+
attachInfoTooltip(element, title, description) {
|
|
2980
|
+
this.attachHtmlTooltip(element, (tooltip) => {
|
|
2981
|
+
this.addTooltipTitle(tooltip, title);
|
|
2982
|
+
const descEl = document.createElement('div');
|
|
2983
|
+
Object.assign(descEl.style, {
|
|
2984
|
+
color: COLORS.text,
|
|
2985
|
+
lineHeight: '1.4',
|
|
2986
|
+
});
|
|
2987
|
+
descEl.textContent = description;
|
|
2988
|
+
tooltip.appendChild(descEl);
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2603
2991
|
/**
|
|
2604
2992
|
* Create a console badge for error/warning counts
|
|
2605
2993
|
*/
|
|
@@ -2638,6 +3026,9 @@ export class GlobalDevBar {
|
|
|
2638
3026
|
btn.type = 'button';
|
|
2639
3027
|
btn.className = this.tooltipClass('right');
|
|
2640
3028
|
const hasSuccessState = this.copiedToClipboard || this.copiedPath || this.lastScreenshot;
|
|
3029
|
+
const isDisabled = this.capturing;
|
|
3030
|
+
// Grey out when not connected (save won't work, but clipboard still does)
|
|
3031
|
+
const isGreyedOut = !this.sweetlinkConnected && !hasSuccessState;
|
|
2641
3032
|
const tooltip = this.getScreenshotTooltip();
|
|
2642
3033
|
btn.setAttribute('data-tooltip', tooltip);
|
|
2643
3034
|
Object.assign(btn.style, {
|
|
@@ -2654,11 +3045,11 @@ export class GlobalDevBar {
|
|
|
2654
3045
|
borderColor: hasSuccessState ? accentColor : `${accentColor}80`,
|
|
2655
3046
|
backgroundColor: hasSuccessState ? `${accentColor}33` : 'transparent',
|
|
2656
3047
|
color: hasSuccessState ? accentColor : `${accentColor}99`,
|
|
2657
|
-
cursor: !
|
|
2658
|
-
opacity: '1',
|
|
3048
|
+
cursor: !isDisabled ? 'pointer' : 'not-allowed',
|
|
3049
|
+
opacity: isGreyedOut ? '0.4' : '1',
|
|
2659
3050
|
transition: 'all 150ms',
|
|
2660
3051
|
});
|
|
2661
|
-
btn.disabled =
|
|
3052
|
+
btn.disabled = isDisabled;
|
|
2662
3053
|
btn.onclick = (e) => {
|
|
2663
3054
|
// If we have a saved screenshot path, clicking copies the path
|
|
2664
3055
|
if (this.lastScreenshot && !e.shiftKey) {
|
|
@@ -2715,10 +3106,10 @@ export class GlobalDevBar {
|
|
|
2715
3106
|
if (this.lastScreenshot) {
|
|
2716
3107
|
return `Screenshot saved!\n${this.lastScreenshot}\n\nClick to copy path`;
|
|
2717
3108
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
3109
|
+
if (!this.sweetlinkConnected) {
|
|
3110
|
+
return `Screenshot (Disconnected)\n\nShift+Click: Copy to clipboard\n\n⚠ Sweetlink not connected\nSave to file unavailable`;
|
|
3111
|
+
}
|
|
3112
|
+
return `Screenshot\n\nClick: Save to file\nShift+Click: Copy to clipboard\n\nKeyboard:\nCmd/Ctrl+Shift+S: Save\nCmd/Ctrl+Shift+C: Copy`;
|
|
2722
3113
|
}
|
|
2723
3114
|
/**
|
|
2724
3115
|
* Get the tooltip text for the AI review button based on current state
|
|
@@ -2778,9 +3169,16 @@ export class GlobalDevBar {
|
|
|
2778
3169
|
const btn = document.createElement('button');
|
|
2779
3170
|
btn.type = 'button';
|
|
2780
3171
|
btn.className = this.tooltipClass('right');
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
3172
|
+
let tooltip;
|
|
3173
|
+
if (this.lastOutline) {
|
|
3174
|
+
tooltip = `Outline saved to:\n${this.lastOutline}`;
|
|
3175
|
+
}
|
|
3176
|
+
else if (!this.sweetlinkConnected) {
|
|
3177
|
+
tooltip = `Document Outline\n\nView page heading structure.\n\n⚠ Sweetlink not connected\nSave to file unavailable`;
|
|
3178
|
+
}
|
|
3179
|
+
else {
|
|
3180
|
+
tooltip = `Document Outline\n\nView page heading structure and\nsave as markdown.`;
|
|
3181
|
+
}
|
|
2784
3182
|
btn.setAttribute('data-tooltip', tooltip);
|
|
2785
3183
|
const isActive = this.showOutlineModal || !!this.lastOutline;
|
|
2786
3184
|
Object.assign(btn.style, getButtonStyles(BUTTON_COLORS.outline, isActive, false));
|
|
@@ -2798,9 +3196,16 @@ export class GlobalDevBar {
|
|
|
2798
3196
|
const btn = document.createElement('button');
|
|
2799
3197
|
btn.type = 'button';
|
|
2800
3198
|
btn.className = this.tooltipClass('right');
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
3199
|
+
let tooltip;
|
|
3200
|
+
if (this.lastSchema) {
|
|
3201
|
+
tooltip = `Schema saved to:\n${this.lastSchema}`;
|
|
3202
|
+
}
|
|
3203
|
+
else if (!this.sweetlinkConnected) {
|
|
3204
|
+
tooltip = `Page Schema\n\nView JSON-LD, Open Graph, and\nother structured data.\n\n⚠ Sweetlink not connected\nSave to file unavailable`;
|
|
3205
|
+
}
|
|
3206
|
+
else {
|
|
3207
|
+
tooltip = `Page Schema\n\nView JSON-LD, Open Graph, and\nother structured data.`;
|
|
3208
|
+
}
|
|
2804
3209
|
btn.setAttribute('data-tooltip', tooltip);
|
|
2805
3210
|
const isActive = this.showSchemaModal || !!this.lastSchema;
|
|
2806
3211
|
Object.assign(btn.style, getButtonStyles(BUTTON_COLORS.schema, isActive, false));
|
package/dist/constants.d.ts
CHANGED
|
@@ -11,8 +11,16 @@ export declare const MAX_RECONNECT_ATTEMPTS = 10;
|
|
|
11
11
|
export declare const BASE_RECONNECT_DELAY_MS = 1000;
|
|
12
12
|
/** Maximum delay between reconnection attempts (ms) */
|
|
13
13
|
export declare const MAX_RECONNECT_DELAY_MS = 30000;
|
|
14
|
-
/** Default WebSocket port for Sweetlink connection */
|
|
14
|
+
/** Default WebSocket port for Sweetlink connection (legacy fallback) */
|
|
15
15
|
export declare const WS_PORT = 9223;
|
|
16
|
+
/** Port offset from app port to calculate WebSocket port (matches SweetlinkBridge) */
|
|
17
|
+
export declare const WS_PORT_OFFSET = 6223;
|
|
18
|
+
/** Maximum ports to try when scanning for matching server */
|
|
19
|
+
export declare const MAX_PORT_RETRIES = 10;
|
|
20
|
+
/** Delay between port scan attempts (ms) */
|
|
21
|
+
export declare const PORT_RETRY_DELAY_MS = 100;
|
|
22
|
+
/** Delay before restarting port scan from base after all ports fail (ms) */
|
|
23
|
+
export declare const PORT_SCAN_RESTART_DELAY_MS = 3000;
|
|
16
24
|
/** Duration to show screenshot notification (ms) */
|
|
17
25
|
export declare const SCREENSHOT_NOTIFICATION_MS = 3000;
|
|
18
26
|
/** Duration to show clipboard notification (ms) */
|
|
@@ -130,28 +138,28 @@ export declare const DEVBAR_THEME: {
|
|
|
130
138
|
};
|
|
131
139
|
};
|
|
132
140
|
export type DevBarTheme = typeof DEVBAR_THEME;
|
|
133
|
-
/** Light theme variant -
|
|
141
|
+
/** Light theme variant - terminal aesthetic with light green tones */
|
|
134
142
|
export declare const DEVBAR_THEME_LIGHT: {
|
|
135
143
|
readonly colors: {
|
|
136
|
-
readonly primary: "#
|
|
137
|
-
readonly primaryHover: "#
|
|
138
|
-
readonly primaryGlow: "rgba(
|
|
139
|
-
readonly error: "#
|
|
144
|
+
readonly primary: "#047857";
|
|
145
|
+
readonly primaryHover: "#065f46";
|
|
146
|
+
readonly primaryGlow: "rgba(4, 120, 87, 0.25)";
|
|
147
|
+
readonly error: "#dc2626";
|
|
140
148
|
readonly warning: "#d97706";
|
|
141
149
|
readonly info: "#2563eb";
|
|
142
|
-
readonly purple: "#
|
|
150
|
+
readonly purple: "#7c3aed";
|
|
143
151
|
readonly cyan: "#0891b2";
|
|
144
152
|
readonly pink: "#db2777";
|
|
145
153
|
readonly lime: "#65a30d";
|
|
146
|
-
readonly bg: "#
|
|
147
|
-
readonly bgCard: "rgba(255, 255, 255, 0.
|
|
148
|
-
readonly bgElevated: "rgba(255, 255, 255, 0.
|
|
149
|
-
readonly bgInput: "rgba(
|
|
150
|
-
readonly text: "#
|
|
151
|
-
readonly textSecondary: "#
|
|
152
|
-
readonly textMuted: "#
|
|
153
|
-
readonly border: "rgba(
|
|
154
|
-
readonly borderSubtle: "rgba(
|
|
154
|
+
readonly bg: "#ecfdf5";
|
|
155
|
+
readonly bgCard: "rgba(255, 255, 255, 0.85)";
|
|
156
|
+
readonly bgElevated: "rgba(255, 255, 255, 0.95)";
|
|
157
|
+
readonly bgInput: "rgba(236, 253, 245, 0.9)";
|
|
158
|
+
readonly text: "#064e3b";
|
|
159
|
+
readonly textSecondary: "#065f46";
|
|
160
|
+
readonly textMuted: "#047857";
|
|
161
|
+
readonly border: "rgba(4, 120, 87, 0.3)";
|
|
162
|
+
readonly borderSubtle: "rgba(4, 120, 87, 0.1)";
|
|
155
163
|
};
|
|
156
164
|
readonly fonts: {
|
|
157
165
|
readonly mono: "'Departure Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
|
|
@@ -181,10 +189,10 @@ export declare const DEVBAR_THEME_LIGHT: {
|
|
|
181
189
|
readonly lg: "12px";
|
|
182
190
|
};
|
|
183
191
|
readonly shadows: {
|
|
184
|
-
readonly sm: "0 1px 2px rgba(
|
|
185
|
-
readonly md: "0 4px 12px rgba(
|
|
186
|
-
readonly lg: "0 8px 32px rgba(
|
|
187
|
-
readonly glow: "0 0 20px rgba(
|
|
192
|
+
readonly sm: "0 1px 2px rgba(4, 120, 87, 0.1)";
|
|
193
|
+
readonly md: "0 4px 12px rgba(4, 120, 87, 0.12), 0 0 0 1px rgba(4, 120, 87, 0.15)";
|
|
194
|
+
readonly lg: "0 8px 32px rgba(4, 120, 87, 0.15), 0 0 0 1px rgba(4, 120, 87, 0.2)";
|
|
195
|
+
readonly glow: "0 0 20px rgba(4, 120, 87, 0.15)";
|
|
188
196
|
};
|
|
189
197
|
readonly transitions: {
|
|
190
198
|
readonly fast: "150ms";
|
package/dist/constants.js
CHANGED
|
@@ -18,8 +18,16 @@ export const MAX_RECONNECT_DELAY_MS = 30000;
|
|
|
18
18
|
// ============================================================================
|
|
19
19
|
// WebSocket Settings
|
|
20
20
|
// ============================================================================
|
|
21
|
-
/** Default WebSocket port for Sweetlink connection */
|
|
21
|
+
/** Default WebSocket port for Sweetlink connection (legacy fallback) */
|
|
22
22
|
export const WS_PORT = 9223;
|
|
23
|
+
/** Port offset from app port to calculate WebSocket port (matches SweetlinkBridge) */
|
|
24
|
+
export const WS_PORT_OFFSET = 6223;
|
|
25
|
+
/** Maximum ports to try when scanning for matching server */
|
|
26
|
+
export const MAX_PORT_RETRIES = 10;
|
|
27
|
+
/** Delay between port scan attempts (ms) */
|
|
28
|
+
export const PORT_RETRY_DELAY_MS = 100;
|
|
29
|
+
/** Delay before restarting port scan from base after all ports fail (ms) */
|
|
30
|
+
export const PORT_SCAN_RESTART_DELAY_MS = 3000;
|
|
23
31
|
// ============================================================================
|
|
24
32
|
// Notification Durations
|
|
25
33
|
// ============================================================================
|
|
@@ -174,44 +182,44 @@ export const DEVBAR_THEME = {
|
|
|
174
182
|
fast: '150ms',
|
|
175
183
|
},
|
|
176
184
|
};
|
|
177
|
-
/** Light theme variant -
|
|
185
|
+
/** Light theme variant - terminal aesthetic with light green tones */
|
|
178
186
|
export const DEVBAR_THEME_LIGHT = {
|
|
179
187
|
colors: {
|
|
180
|
-
// Primary accent (darker for
|
|
181
|
-
primary: '#
|
|
182
|
-
primaryHover: '#
|
|
183
|
-
primaryGlow: 'rgba(
|
|
184
|
-
// Semantic colors (
|
|
185
|
-
error:
|
|
186
|
-
warning: '#d97706',
|
|
187
|
-
info: '#2563eb',
|
|
188
|
-
// Extended palette (darker
|
|
189
|
-
purple: '#
|
|
188
|
+
// Primary accent (darker emerald for contrast)
|
|
189
|
+
primary: '#047857', // darker emerald for better contrast
|
|
190
|
+
primaryHover: '#065f46',
|
|
191
|
+
primaryGlow: 'rgba(4, 120, 87, 0.25)',
|
|
192
|
+
// Semantic colors (adjusted for light bg)
|
|
193
|
+
error: '#dc2626',
|
|
194
|
+
warning: '#d97706',
|
|
195
|
+
info: '#2563eb',
|
|
196
|
+
// Extended palette (darker for light mode)
|
|
197
|
+
purple: '#7c3aed',
|
|
190
198
|
cyan: '#0891b2',
|
|
191
199
|
pink: '#db2777',
|
|
192
200
|
lime: '#65a30d',
|
|
193
|
-
// Backgrounds
|
|
194
|
-
bg: '#
|
|
195
|
-
bgCard: 'rgba(255, 255, 255, 0.
|
|
196
|
-
bgElevated: 'rgba(255, 255, 255, 0.
|
|
197
|
-
bgInput: 'rgba(
|
|
201
|
+
// Backgrounds - terminal light green aesthetic
|
|
202
|
+
bg: '#ecfdf5', // very light mint/green
|
|
203
|
+
bgCard: 'rgba(255, 255, 255, 0.85)',
|
|
204
|
+
bgElevated: 'rgba(255, 255, 255, 0.95)',
|
|
205
|
+
bgInput: 'rgba(236, 253, 245, 0.9)', // light mint input
|
|
198
206
|
// Text (dark on light)
|
|
199
|
-
text: '#
|
|
200
|
-
textSecondary: '#
|
|
201
|
-
textMuted: '#
|
|
202
|
-
// Borders (
|
|
203
|
-
border: 'rgba(
|
|
204
|
-
borderSubtle: 'rgba(
|
|
207
|
+
text: '#064e3b', // dark emerald text
|
|
208
|
+
textSecondary: '#065f46',
|
|
209
|
+
textMuted: '#047857',
|
|
210
|
+
// Borders (emerald-tinted)
|
|
211
|
+
border: 'rgba(4, 120, 87, 0.3)',
|
|
212
|
+
borderSubtle: 'rgba(4, 120, 87, 0.1)',
|
|
205
213
|
},
|
|
206
214
|
// Other properties same as dark theme
|
|
207
215
|
fonts: DEVBAR_THEME.fonts,
|
|
208
216
|
typography: DEVBAR_THEME.typography,
|
|
209
217
|
radius: DEVBAR_THEME.radius,
|
|
210
218
|
shadows: {
|
|
211
|
-
sm: '0 1px 2px rgba(
|
|
212
|
-
md: '0 4px 12px rgba(
|
|
213
|
-
lg: '0 8px 32px rgba(
|
|
214
|
-
glow: '0 0 20px rgba(
|
|
219
|
+
sm: '0 1px 2px rgba(4, 120, 87, 0.1)',
|
|
220
|
+
md: '0 4px 12px rgba(4, 120, 87, 0.12), 0 0 0 1px rgba(4, 120, 87, 0.15)',
|
|
221
|
+
lg: '0 8px 32px rgba(4, 120, 87, 0.15), 0 0 0 1px rgba(4, 120, 87, 0.2)',
|
|
222
|
+
glow: '0 0 20px rgba(4, 120, 87, 0.15)',
|
|
215
223
|
},
|
|
216
224
|
transitions: DEVBAR_THEME.transitions,
|
|
217
225
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export {
|
|
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';
|
|
2
3
|
export { DebugLogger, normalizeDebugConfig } from './debug.js';
|
|
3
4
|
export { EARLY_CONSOLE_CAPTURE_SCRIPT } from './earlyConsoleCapture.js';
|
|
4
5
|
export { destroyGlobalDevBar, earlyConsoleCapture, GlobalDevBar, getGlobalDevBar, initGlobalDevBar, } from './GlobalDevBar.js';
|
|
5
|
-
export { initDebug, initFull, initMinimal, initPerformance, initResponsive, PRESET_DEBUG, PRESET_FULL, PRESET_MINIMAL, PRESET_PERFORMANCE, PRESET_RESPONSIVE, } from './presets.js';
|
|
6
6
|
export { getHtml2Canvas, isHtml2CanvasLoaded, preloadHtml2Canvas } from './lazy/index.js';
|
|
7
|
+
export { formatBytes as formatNetworkBytes, formatDuration, getInitiatorColor, type NetworkEntry, NetworkMonitor, type NetworkState, } from './network.js';
|
|
7
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';
|
|
8
10
|
export { extractPageSchema, schemaToMarkdown } from './schema.js';
|
|
9
|
-
export {
|
|
10
|
-
export { beautifyJson, clearLocalStorage, clearSessionStorage, deleteCookie, deleteLocalStorageItem, deleteSessionStorageItem, formatStorageSummary, getCookies, getLocalStorage, getSessionStorage, getStorageData, setLocalStorageItem, setSessionStorageItem, type CookieItem, type StorageData, type StorageItem, } from './storage.js';
|
|
11
|
-
export { clearA11yCache, formatViolation, getBadgeColor, getCachedResult, getImpactColor, getViolationCounts, groupViolationsByImpact, isAxeLoaded, preloadAxe, runA11yAudit, type A11yState, type AxeResult, type AxeViolation, } from './accessibility.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
12
|
export type { ConsoleLog, DebugConfig, DevBarControl, GlobalDevBarOptions, OutlineNode, PageSchema, SweetlinkCommand, ThemeMode, } from './types.js';
|
|
13
13
|
export { canvasToDataUrl, copyCanvasToClipboard, delay, formatArg, formatArgs, prepareForCapture, } from './utils.js';
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
// DevBar - Development toolbar and utilities
|
|
2
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';
|
|
3
5
|
// Re-export constants and theme utilities
|
|
4
|
-
export { BUTTON_COLORS, CATEGORY_COLORS, COLORS, DEVBAR_THEME, DEVBAR_THEME_LIGHT, FONT_MONO, generateBreakpointCSS, generateThemeCSSVars, getEffectiveTheme, getStoredThemeMode, getTheme, getThemeColors, injectThemeCSS,
|
|
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';
|
|
5
7
|
// Debug utilities
|
|
6
8
|
export { DebugLogger, normalizeDebugConfig } from './debug.js';
|
|
7
9
|
// Early console capture script for injection
|
|
8
10
|
export { EARLY_CONSOLE_CAPTURE_SCRIPT } from './earlyConsoleCapture.js';
|
|
9
11
|
// Main vanilla JS devbar
|
|
10
12
|
export { destroyGlobalDevBar, earlyConsoleCapture, GlobalDevBar, getGlobalDevBar, initGlobalDevBar, } from './GlobalDevBar.js';
|
|
11
|
-
// Configuration presets
|
|
12
|
-
export { initDebug, initFull, initMinimal, initPerformance, initResponsive, PRESET_DEBUG, PRESET_FULL, PRESET_MINIMAL, PRESET_PERFORMANCE, PRESET_RESPONSIVE, } from './presets.js';
|
|
13
13
|
// Lazy loading utilities
|
|
14
14
|
export { getHtml2Canvas, isHtml2CanvasLoaded, preloadHtml2Canvas } from './lazy/index.js';
|
|
15
|
+
// Network monitoring utilities
|
|
16
|
+
export { formatBytes as formatNetworkBytes, formatDuration, getInitiatorColor, NetworkMonitor, } from './network.js';
|
|
15
17
|
// Re-export outline/schema functions
|
|
16
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';
|
|
17
21
|
export { extractPageSchema, schemaToMarkdown } from './schema.js';
|
|
18
|
-
// Network monitoring utilities
|
|
19
|
-
export { formatBytes as formatNetworkBytes, formatDuration, getInitiatorColor, NetworkMonitor, } from './network.js';
|
|
20
22
|
// Storage inspection utilities
|
|
21
23
|
export { beautifyJson, clearLocalStorage, clearSessionStorage, deleteCookie, deleteLocalStorageItem, deleteSessionStorageItem, formatStorageSummary, getCookies, getLocalStorage, getSessionStorage, getStorageData, setLocalStorageItem, setSessionStorageItem, } from './storage.js';
|
|
22
|
-
// Accessibility audit utilities
|
|
23
|
-
export { clearA11yCache, formatViolation, getBadgeColor, getCachedResult, getImpactColor, getViolationCounts, groupViolationsByImpact, isAxeLoaded, preloadAxe, runA11yAudit, } from './accessibility.js';
|
|
24
24
|
// Re-export utilities for external use
|
|
25
25
|
export { canvasToDataUrl, copyCanvasToClipboard, delay, formatArg, formatArgs, prepareForCapture, } from './utils.js';
|
package/dist/presets.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Pre-configured options for common DevBar use cases.
|
|
5
5
|
*/
|
|
6
|
-
import { GlobalDevBar } from './GlobalDevBar.js';
|
|
6
|
+
import { type GlobalDevBar } from './GlobalDevBar.js';
|
|
7
7
|
import type { GlobalDevBarOptions } from './types.js';
|
|
8
8
|
/**
|
|
9
9
|
* Minimal preset - shows only essential features
|
package/dist/ui/modals.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export interface ModalConfig {
|
|
|
13
13
|
onCopyMd: () => Promise<void>;
|
|
14
14
|
onSave?: () => void;
|
|
15
15
|
sweetlinkConnected: boolean;
|
|
16
|
+
/** Whether a save operation is in progress */
|
|
17
|
+
isSaving?: boolean;
|
|
18
|
+
/** Path where data was saved (shows confirmation) */
|
|
19
|
+
savedPath?: string | null;
|
|
16
20
|
}
|
|
17
21
|
/**
|
|
18
22
|
* Create modal overlay with click-outside-to-close behavior
|
package/dist/ui/modals.js
CHANGED
|
@@ -34,7 +34,7 @@ export function createModalBox(color) {
|
|
|
34
34
|
* Create modal header with title, copy/save/close buttons
|
|
35
35
|
*/
|
|
36
36
|
export function createModalHeader(config) {
|
|
37
|
-
const { color, title, onClose, onCopyMd, onSave, sweetlinkConnected } = config;
|
|
37
|
+
const { color, title, onClose, onCopyMd, onSave, sweetlinkConnected, isSaving, savedPath } = config;
|
|
38
38
|
const header = document.createElement('div');
|
|
39
39
|
Object.assign(header.style, {
|
|
40
40
|
display: 'flex',
|
|
@@ -42,6 +42,8 @@ export function createModalHeader(config) {
|
|
|
42
42
|
justifyContent: 'space-between',
|
|
43
43
|
padding: '16px 20px',
|
|
44
44
|
borderBottom: `1px solid ${color}40`,
|
|
45
|
+
flexWrap: 'wrap',
|
|
46
|
+
gap: '8px',
|
|
45
47
|
});
|
|
46
48
|
const titleEl = document.createElement('h2');
|
|
47
49
|
Object.assign(titleEl.style, {
|
|
@@ -53,7 +55,7 @@ export function createModalHeader(config) {
|
|
|
53
55
|
titleEl.textContent = title;
|
|
54
56
|
header.appendChild(titleEl);
|
|
55
57
|
const headerButtons = document.createElement('div');
|
|
56
|
-
Object.assign(headerButtons.style, { display: 'flex', gap: '10px' });
|
|
58
|
+
Object.assign(headerButtons.style, { display: 'flex', gap: '10px', alignItems: 'center' });
|
|
57
59
|
// Copy MD button
|
|
58
60
|
const copyBtn = createStyledButton({ color, text: 'Copy MD' });
|
|
59
61
|
copyBtn.onclick = async () => {
|
|
@@ -71,8 +73,17 @@ export function createModalHeader(config) {
|
|
|
71
73
|
headerButtons.appendChild(copyBtn);
|
|
72
74
|
// Save button (if Sweetlink connected)
|
|
73
75
|
if (sweetlinkConnected && onSave) {
|
|
74
|
-
const saveBtn = createStyledButton({
|
|
75
|
-
|
|
76
|
+
const saveBtn = createStyledButton({
|
|
77
|
+
color,
|
|
78
|
+
text: isSaving ? 'Saving...' : 'Save',
|
|
79
|
+
});
|
|
80
|
+
if (isSaving) {
|
|
81
|
+
saveBtn.style.opacity = '0.6';
|
|
82
|
+
saveBtn.style.cursor = 'not-allowed';
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
saveBtn.onclick = onSave;
|
|
86
|
+
}
|
|
76
87
|
headerButtons.appendChild(saveBtn);
|
|
77
88
|
}
|
|
78
89
|
// Close button - use same padding as other buttons for consistent height
|
|
@@ -85,6 +96,39 @@ export function createModalHeader(config) {
|
|
|
85
96
|
closeBtn.onclick = onClose;
|
|
86
97
|
headerButtons.appendChild(closeBtn);
|
|
87
98
|
header.appendChild(headerButtons);
|
|
99
|
+
// Show saved path confirmation below buttons
|
|
100
|
+
if (savedPath) {
|
|
101
|
+
const savedConfirm = document.createElement('div');
|
|
102
|
+
Object.assign(savedConfirm.style, {
|
|
103
|
+
width: '100%',
|
|
104
|
+
marginTop: '4px',
|
|
105
|
+
padding: '8px 12px',
|
|
106
|
+
backgroundColor: `${color}15`,
|
|
107
|
+
border: `1px solid ${color}30`,
|
|
108
|
+
borderRadius: '6px',
|
|
109
|
+
fontSize: '0.75rem',
|
|
110
|
+
color: color,
|
|
111
|
+
display: 'flex',
|
|
112
|
+
alignItems: 'center',
|
|
113
|
+
gap: '6px',
|
|
114
|
+
});
|
|
115
|
+
// Checkmark icon
|
|
116
|
+
const checkmark = document.createElement('span');
|
|
117
|
+
checkmark.textContent = '✓';
|
|
118
|
+
Object.assign(checkmark.style, { fontWeight: '600' });
|
|
119
|
+
savedConfirm.appendChild(checkmark);
|
|
120
|
+
// Path text
|
|
121
|
+
const pathText = document.createElement('span');
|
|
122
|
+
Object.assign(pathText.style, {
|
|
123
|
+
color: '#9ca3af',
|
|
124
|
+
fontFamily: 'monospace',
|
|
125
|
+
fontSize: '0.6875rem',
|
|
126
|
+
wordBreak: 'break-all',
|
|
127
|
+
});
|
|
128
|
+
pathText.textContent = `Saved to ${savedPath}`;
|
|
129
|
+
savedConfirm.appendChild(pathText);
|
|
130
|
+
header.appendChild(savedConfirm);
|
|
131
|
+
}
|
|
88
132
|
return header;
|
|
89
133
|
}
|
|
90
134
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ytspar/devbar",
|
|
3
|
-
"version": "1.0.0-canary.
|
|
3
|
+
"version": "1.0.0-canary.4b73445",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Development toolbar and utilities with Sweetlink integration - pure vanilla JS, no framework dependencies",
|