@ytspar/devbar 1.0.0-canary.bf42899 → 1.0.0-canary.c511f13
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 +34 -0
- package/dist/GlobalDevBar.js +506 -93
- package/dist/constants.d.ts +30 -20
- package/dist/constants.js +37 -27
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -7
- package/dist/outline.js +43 -10
- package/dist/presets.d.ts +1 -1
- package/dist/schema.js +4 -3
- package/dist/settings.d.ts +150 -0
- package/dist/settings.js +292 -0
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +2 -2
- package/dist/ui/modals.d.ts +4 -0
- package/dist/ui/modals.js +53 -7
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/package.json +1 -1
package/dist/GlobalDevBar.js
CHANGED
|
@@ -8,12 +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,
|
|
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, VERIFICATION_TIMEOUT_MS, 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
|
+
import { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, getSettingsManager, } from './settings.js';
|
|
15
16
|
import { createEmptyMessage, createInfoBox, createModalBox, createModalContent, createModalHeader, createModalOverlay, createStyledButton, createSvgIcon, getButtonStyles, } from './ui/index.js';
|
|
16
17
|
import { canvasToDataUrl, copyCanvasToClipboard, delay, formatArgs, prepareForCapture, } from './utils.js';
|
|
18
|
+
export { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, getSettingsManager } from './settings.js';
|
|
17
19
|
const html2canvas = (html2canvasModule.default ??
|
|
18
20
|
html2canvasModule);
|
|
19
21
|
const earlyConsoleCapture = (() => {
|
|
@@ -89,6 +91,8 @@ export class GlobalDevBar {
|
|
|
89
91
|
this.apiKeyStatus = null;
|
|
90
92
|
this.lastOutline = null;
|
|
91
93
|
this.lastSchema = null;
|
|
94
|
+
this.savingOutline = false;
|
|
95
|
+
this.savingSchema = false;
|
|
92
96
|
this.consoleFilter = null;
|
|
93
97
|
// Modal states
|
|
94
98
|
this.showOutlineModal = false;
|
|
@@ -99,6 +103,9 @@ export class GlobalDevBar {
|
|
|
99
103
|
this.clsValue = 0;
|
|
100
104
|
this.inpValue = 0;
|
|
101
105
|
this.reconnectAttempts = 0;
|
|
106
|
+
this.wsVerified = false;
|
|
107
|
+
this.serverProjectDir = null;
|
|
108
|
+
this.verificationTimeout = null;
|
|
102
109
|
// Track the position of the connection indicator dot for smooth collapse
|
|
103
110
|
this.lastDotPosition = null;
|
|
104
111
|
this.reconnectTimeout = null;
|
|
@@ -128,6 +135,19 @@ export class GlobalDevBar {
|
|
|
128
135
|
// Initialize debug config first so we can log during construction
|
|
129
136
|
this.debugConfig = normalizeDebugConfig(options.debug);
|
|
130
137
|
this.debug = new DebugLogger(this.debugConfig);
|
|
138
|
+
// Initialize settings manager
|
|
139
|
+
this.settingsManager = getSettingsManager();
|
|
140
|
+
// Calculate app port from URL for multi-instance support
|
|
141
|
+
if (typeof window !== 'undefined') {
|
|
142
|
+
this.currentAppPort =
|
|
143
|
+
parseInt(window.location.port, 10) || (window.location.protocol === 'https:' ? 443 : 80);
|
|
144
|
+
// Calculate expected WS port (appPort + port offset) like SweetlinkBridge does
|
|
145
|
+
this.baseWsPort = this.currentAppPort > 0 ? this.currentAppPort + WS_PORT_OFFSET : WS_PORT;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.currentAppPort = 0;
|
|
149
|
+
this.baseWsPort = WS_PORT;
|
|
150
|
+
}
|
|
131
151
|
this.options = {
|
|
132
152
|
position: options.position ?? 'bottom-left',
|
|
133
153
|
accentColor: options.accentColor ?? COLORS.primary,
|
|
@@ -283,6 +303,8 @@ export class GlobalDevBar {
|
|
|
283
303
|
this.reconnectAttempts = MAX_RECONNECT_ATTEMPTS; // Prevent reconnection
|
|
284
304
|
if (this.reconnectTimeout)
|
|
285
305
|
clearTimeout(this.reconnectTimeout);
|
|
306
|
+
if (this.verificationTimeout)
|
|
307
|
+
clearTimeout(this.verificationTimeout);
|
|
286
308
|
if (this.ws)
|
|
287
309
|
this.ws.close();
|
|
288
310
|
// Clear timeouts
|
|
@@ -341,22 +363,82 @@ export class GlobalDevBar {
|
|
|
341
363
|
document.head.appendChild(style);
|
|
342
364
|
}
|
|
343
365
|
}
|
|
344
|
-
connectWebSocket() {
|
|
366
|
+
connectWebSocket(port) {
|
|
345
367
|
if (this.destroyed)
|
|
346
368
|
return;
|
|
347
|
-
|
|
348
|
-
|
|
369
|
+
const targetPort = port ?? this.baseWsPort;
|
|
370
|
+
this.debug.ws('Connecting to WebSocket', { port: targetPort, appPort: this.currentAppPort });
|
|
371
|
+
const ws = new WebSocket(`ws://localhost:${targetPort}`);
|
|
349
372
|
this.ws = ws;
|
|
373
|
+
this.wsVerified = false;
|
|
374
|
+
// Timeout for server-info verification
|
|
375
|
+
this.verificationTimeout = setTimeout(() => {
|
|
376
|
+
if (!this.wsVerified && ws.readyState === WebSocket.OPEN) {
|
|
377
|
+
// Server didn't send server-info (old version) - accept for backwards compatibility
|
|
378
|
+
this.debug.ws('Server is old version (no server-info), accepting for backwards compat');
|
|
379
|
+
this.wsVerified = true;
|
|
380
|
+
this.sweetlinkConnected = true;
|
|
381
|
+
this.reconnectAttempts = 0;
|
|
382
|
+
this.settingsManager.setWebSocket(ws);
|
|
383
|
+
this.settingsManager.setConnected(true);
|
|
384
|
+
ws.send(JSON.stringify({ type: 'load-settings' }));
|
|
385
|
+
this.render();
|
|
386
|
+
}
|
|
387
|
+
}, VERIFICATION_TIMEOUT_MS);
|
|
350
388
|
ws.onopen = () => {
|
|
351
|
-
this.
|
|
352
|
-
this.reconnectAttempts = 0;
|
|
353
|
-
this.debug.ws('WebSocket connected');
|
|
389
|
+
this.debug.ws('WebSocket socket opened, awaiting server-info');
|
|
354
390
|
ws.send(JSON.stringify({ type: 'browser-client-ready' }));
|
|
355
|
-
this.render();
|
|
356
391
|
};
|
|
357
392
|
ws.onmessage = async (event) => {
|
|
358
393
|
try {
|
|
359
|
-
const
|
|
394
|
+
const message = JSON.parse(event.data);
|
|
395
|
+
// Handle server-info for port matching
|
|
396
|
+
if (message.type === 'server-info') {
|
|
397
|
+
if (this.verificationTimeout) {
|
|
398
|
+
clearTimeout(this.verificationTimeout);
|
|
399
|
+
this.verificationTimeout = null;
|
|
400
|
+
}
|
|
401
|
+
const serverAppPort = message.appPort;
|
|
402
|
+
const serverMatchesApp = serverAppPort === null || serverAppPort === this.currentAppPort;
|
|
403
|
+
if (!serverMatchesApp) {
|
|
404
|
+
this.debug.ws('Server mismatch', {
|
|
405
|
+
serverAppPort,
|
|
406
|
+
currentAppPort: this.currentAppPort,
|
|
407
|
+
tryingNextPort: targetPort + 1,
|
|
408
|
+
});
|
|
409
|
+
ws.close();
|
|
410
|
+
// Try next port
|
|
411
|
+
const nextPort = targetPort + 1;
|
|
412
|
+
if (nextPort < this.baseWsPort + MAX_PORT_RETRIES) {
|
|
413
|
+
setTimeout(() => this.connectWebSocket(nextPort), PORT_RETRY_DELAY_MS);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
this.debug.ws('No matching server found, will retry from base port');
|
|
417
|
+
setTimeout(() => this.connectWebSocket(this.baseWsPort), PORT_SCAN_RESTART_DELAY_MS);
|
|
418
|
+
}
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
// Server matches - mark as verified and connected
|
|
422
|
+
this.wsVerified = true;
|
|
423
|
+
this.sweetlinkConnected = true;
|
|
424
|
+
this.reconnectAttempts = 0;
|
|
425
|
+
this.serverProjectDir = message.projectDir ?? null;
|
|
426
|
+
this.debug.ws('Server verified', {
|
|
427
|
+
appPort: serverAppPort ?? 'any',
|
|
428
|
+
projectDir: this.serverProjectDir,
|
|
429
|
+
});
|
|
430
|
+
this.settingsManager.setWebSocket(ws);
|
|
431
|
+
this.settingsManager.setConnected(true);
|
|
432
|
+
ws.send(JSON.stringify({ type: 'load-settings' }));
|
|
433
|
+
this.render();
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
// Ignore other commands until verified
|
|
437
|
+
if (!this.wsVerified) {
|
|
438
|
+
this.debug.ws('Ignoring command before verification', { type: message.type });
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const command = message;
|
|
360
442
|
this.debug.ws('Received command', { type: command.type });
|
|
361
443
|
await this.handleSweetlinkCommand(command);
|
|
362
444
|
}
|
|
@@ -365,15 +447,25 @@ export class GlobalDevBar {
|
|
|
365
447
|
}
|
|
366
448
|
};
|
|
367
449
|
ws.onclose = () => {
|
|
368
|
-
this.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
this.
|
|
375
|
-
this.
|
|
376
|
-
this.
|
|
450
|
+
if (this.verificationTimeout) {
|
|
451
|
+
clearTimeout(this.verificationTimeout);
|
|
452
|
+
this.verificationTimeout = null;
|
|
453
|
+
}
|
|
454
|
+
// Only reset connection state if we were actually verified/connected
|
|
455
|
+
if (this.wsVerified) {
|
|
456
|
+
this.sweetlinkConnected = false;
|
|
457
|
+
this.wsVerified = false;
|
|
458
|
+
this.serverProjectDir = null;
|
|
459
|
+
this.settingsManager.setConnected(false);
|
|
460
|
+
this.debug.ws('WebSocket disconnected');
|
|
461
|
+
this.render();
|
|
462
|
+
// Auto-reconnect with exponential backoff (start from base port)
|
|
463
|
+
if (!this.destroyed && this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
|
464
|
+
const delayMs = BASE_RECONNECT_DELAY_MS * 2 ** this.reconnectAttempts;
|
|
465
|
+
this.reconnectAttempts++;
|
|
466
|
+
this.debug.ws('Scheduling reconnect', { attempt: this.reconnectAttempts, delayMs });
|
|
467
|
+
this.reconnectTimeout = setTimeout(() => this.connectWebSocket(this.baseWsPort), Math.min(delayMs, MAX_RECONNECT_DELAY_MS));
|
|
468
|
+
}
|
|
377
469
|
}
|
|
378
470
|
};
|
|
379
471
|
ws.onerror = () => {
|
|
@@ -500,8 +592,48 @@ export class GlobalDevBar {
|
|
|
500
592
|
case 'schema-error':
|
|
501
593
|
console.error('[GlobalDevBar] Schema save failed:', command.error);
|
|
502
594
|
break;
|
|
595
|
+
case 'settings-loaded':
|
|
596
|
+
this.handleSettingsLoaded(command.settings);
|
|
597
|
+
break;
|
|
598
|
+
case 'settings-saved':
|
|
599
|
+
this.debug.state('Settings saved to server', { path: command.settingsPath });
|
|
600
|
+
break;
|
|
601
|
+
case 'settings-error':
|
|
602
|
+
console.error('[GlobalDevBar] Settings operation failed:', command.error);
|
|
603
|
+
break;
|
|
503
604
|
}
|
|
504
605
|
}
|
|
606
|
+
/**
|
|
607
|
+
* Handle settings loaded from server
|
|
608
|
+
*/
|
|
609
|
+
handleSettingsLoaded(settings) {
|
|
610
|
+
if (!settings) {
|
|
611
|
+
this.debug.state('No server settings found, using local');
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
this.debug.state('Settings loaded from server', settings);
|
|
615
|
+
// Update settings manager
|
|
616
|
+
this.settingsManager.handleSettingsLoaded(settings);
|
|
617
|
+
// Apply settings to local state
|
|
618
|
+
this.applySettings(settings);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Apply settings to the DevBar state and options
|
|
622
|
+
*/
|
|
623
|
+
applySettings(settings) {
|
|
624
|
+
// Update local state
|
|
625
|
+
this.themeMode = settings.themeMode;
|
|
626
|
+
this.compactMode = settings.compactMode;
|
|
627
|
+
// Update options
|
|
628
|
+
this.options.position = settings.position;
|
|
629
|
+
this.options.accentColor = settings.accentColor;
|
|
630
|
+
this.options.showScreenshot = settings.showScreenshot;
|
|
631
|
+
this.options.showConsoleBadges = settings.showConsoleBadges;
|
|
632
|
+
this.options.showTooltips = settings.showTooltips;
|
|
633
|
+
this.options.showMetrics = { ...settings.showMetrics };
|
|
634
|
+
// Re-render with new settings
|
|
635
|
+
this.render();
|
|
636
|
+
}
|
|
505
637
|
/**
|
|
506
638
|
* Handle notification state updates with auto-clear timeout
|
|
507
639
|
*/
|
|
@@ -529,6 +661,7 @@ export class GlobalDevBar {
|
|
|
529
661
|
}, durationMs);
|
|
530
662
|
break;
|
|
531
663
|
case 'outline':
|
|
664
|
+
this.savingOutline = false;
|
|
532
665
|
this.lastOutline = path;
|
|
533
666
|
if (this.outlineTimeout)
|
|
534
667
|
clearTimeout(this.outlineTimeout);
|
|
@@ -538,6 +671,7 @@ export class GlobalDevBar {
|
|
|
538
671
|
}, durationMs);
|
|
539
672
|
break;
|
|
540
673
|
case 'schema':
|
|
674
|
+
this.savingSchema = false;
|
|
541
675
|
this.lastSchema = path;
|
|
542
676
|
if (this.schemaTimeout)
|
|
543
677
|
clearTimeout(this.schemaTimeout);
|
|
@@ -670,7 +804,11 @@ export class GlobalDevBar {
|
|
|
670
804
|
}
|
|
671
805
|
});
|
|
672
806
|
// durationThreshold filters out very short interactions
|
|
673
|
-
this.inpObserver.observe({
|
|
807
|
+
this.inpObserver.observe({
|
|
808
|
+
type: 'event',
|
|
809
|
+
buffered: true,
|
|
810
|
+
durationThreshold: 16,
|
|
811
|
+
});
|
|
674
812
|
}
|
|
675
813
|
catch (e) {
|
|
676
814
|
console.warn('[GlobalDevBar] INP PerformanceObserver not supported', e);
|
|
@@ -724,14 +862,17 @@ export class GlobalDevBar {
|
|
|
724
862
|
window.addEventListener('keydown', this.keydownHandler);
|
|
725
863
|
}
|
|
726
864
|
setupTheme() {
|
|
727
|
-
// Load stored theme preference
|
|
728
|
-
|
|
865
|
+
// Load stored theme preference from settings manager
|
|
866
|
+
const settings = this.settingsManager.getSettings();
|
|
867
|
+
this.themeMode = settings.themeMode;
|
|
729
868
|
this.debug.state('Theme loaded', { mode: this.themeMode });
|
|
730
869
|
// Listen for system theme changes
|
|
731
870
|
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
732
871
|
this.themeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
733
872
|
this.themeMediaHandler = () => {
|
|
734
873
|
if (this.themeMode === 'system') {
|
|
874
|
+
// Re-inject theme CSS when system preference changes
|
|
875
|
+
injectThemeCSS(getTheme(this.themeMode));
|
|
735
876
|
this.debug.state('System theme changed', {
|
|
736
877
|
effectiveTheme: getEffectiveTheme(this.themeMode),
|
|
737
878
|
});
|
|
@@ -742,10 +883,8 @@ export class GlobalDevBar {
|
|
|
742
883
|
}
|
|
743
884
|
}
|
|
744
885
|
loadCompactMode() {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const stored = localStorage.getItem(STORAGE_KEYS.compactMode);
|
|
748
|
-
this.compactMode = stored === 'true';
|
|
886
|
+
const settings = this.settingsManager.getSettings();
|
|
887
|
+
this.compactMode = settings.compactMode;
|
|
749
888
|
this.debug.state('Compact mode loaded', { compactMode: this.compactMode });
|
|
750
889
|
}
|
|
751
890
|
/**
|
|
@@ -759,7 +898,9 @@ export class GlobalDevBar {
|
|
|
759
898
|
*/
|
|
760
899
|
setThemeMode(mode) {
|
|
761
900
|
this.themeMode = mode;
|
|
762
|
-
|
|
901
|
+
this.settingsManager.saveSettings({ themeMode: mode });
|
|
902
|
+
// Inject the appropriate theme CSS variables
|
|
903
|
+
injectThemeCSS(getTheme(mode));
|
|
763
904
|
this.debug.state('Theme mode changed', { mode, effectiveTheme: getEffectiveTheme(mode) });
|
|
764
905
|
this.render();
|
|
765
906
|
}
|
|
@@ -774,9 +915,7 @@ export class GlobalDevBar {
|
|
|
774
915
|
*/
|
|
775
916
|
toggleCompactMode() {
|
|
776
917
|
this.compactMode = !this.compactMode;
|
|
777
|
-
|
|
778
|
-
localStorage.setItem(STORAGE_KEYS.compactMode, String(this.compactMode));
|
|
779
|
-
}
|
|
918
|
+
this.settingsManager.saveSettings({ compactMode: this.compactMode });
|
|
780
919
|
this.debug.state('Compact mode toggled', { compactMode: this.compactMode });
|
|
781
920
|
this.render();
|
|
782
921
|
}
|
|
@@ -1015,9 +1154,13 @@ export class GlobalDevBar {
|
|
|
1015
1154
|
this.render();
|
|
1016
1155
|
}
|
|
1017
1156
|
handleSaveOutline() {
|
|
1157
|
+
if (this.savingOutline)
|
|
1158
|
+
return; // Prevent repeated clicks
|
|
1018
1159
|
const outline = extractDocumentOutline();
|
|
1019
1160
|
const markdown = outlineToMarkdown(outline);
|
|
1020
1161
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
1162
|
+
this.savingOutline = true;
|
|
1163
|
+
this.render();
|
|
1021
1164
|
this.ws.send(JSON.stringify({
|
|
1022
1165
|
type: 'save-outline',
|
|
1023
1166
|
data: {
|
|
@@ -1031,9 +1174,13 @@ export class GlobalDevBar {
|
|
|
1031
1174
|
}
|
|
1032
1175
|
}
|
|
1033
1176
|
handleSaveSchema() {
|
|
1177
|
+
if (this.savingSchema)
|
|
1178
|
+
return; // Prevent repeated clicks
|
|
1034
1179
|
const schema = extractPageSchema();
|
|
1035
1180
|
const markdown = schemaToMarkdown(schema);
|
|
1036
1181
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
1182
|
+
this.savingSchema = true;
|
|
1183
|
+
this.render();
|
|
1037
1184
|
this.ws.send(JSON.stringify({
|
|
1038
1185
|
type: 'save-schema',
|
|
1039
1186
|
data: {
|
|
@@ -1411,6 +1558,8 @@ export class GlobalDevBar {
|
|
|
1411
1558
|
},
|
|
1412
1559
|
onSave: () => this.handleSaveOutline(),
|
|
1413
1560
|
sweetlinkConnected: this.sweetlinkConnected,
|
|
1561
|
+
isSaving: this.savingOutline,
|
|
1562
|
+
savedPath: this.lastOutline,
|
|
1414
1563
|
});
|
|
1415
1564
|
modal.appendChild(header);
|
|
1416
1565
|
const content = createModalContent();
|
|
@@ -1497,6 +1646,8 @@ export class GlobalDevBar {
|
|
|
1497
1646
|
},
|
|
1498
1647
|
onSave: () => this.handleSaveSchema(),
|
|
1499
1648
|
sweetlinkConnected: this.sweetlinkConnected,
|
|
1649
|
+
isSaving: this.savingSchema,
|
|
1650
|
+
savedPath: this.lastSchema,
|
|
1500
1651
|
});
|
|
1501
1652
|
modal.appendChild(header);
|
|
1502
1653
|
const content = createModalContent();
|
|
@@ -1854,6 +2005,130 @@ export class GlobalDevBar {
|
|
|
1854
2005
|
btn.appendChild(svg);
|
|
1855
2006
|
return btn;
|
|
1856
2007
|
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Create the compact mode toggle button with chevron icon
|
|
2010
|
+
*/
|
|
2011
|
+
createCompactToggleButton() {
|
|
2012
|
+
const btn = document.createElement('button');
|
|
2013
|
+
btn.type = 'button';
|
|
2014
|
+
btn.className = this.tooltipClass('right');
|
|
2015
|
+
const isCompact = this.compactMode;
|
|
2016
|
+
const tooltip = isCompact ? 'Expand (Cmd+Shift+M)' : 'Compact (Cmd+Shift+M)';
|
|
2017
|
+
btn.setAttribute('data-tooltip', tooltip);
|
|
2018
|
+
const { accentColor } = this.options;
|
|
2019
|
+
const iconColor = COLORS.textSecondary;
|
|
2020
|
+
Object.assign(btn.style, {
|
|
2021
|
+
display: 'flex',
|
|
2022
|
+
alignItems: 'center',
|
|
2023
|
+
justifyContent: 'center',
|
|
2024
|
+
width: '22px',
|
|
2025
|
+
height: '22px',
|
|
2026
|
+
minWidth: '22px',
|
|
2027
|
+
minHeight: '22px',
|
|
2028
|
+
flexShrink: '0',
|
|
2029
|
+
borderRadius: '50%',
|
|
2030
|
+
border: `1px solid ${accentColor}60`,
|
|
2031
|
+
backgroundColor: 'transparent',
|
|
2032
|
+
color: `${iconColor}99`,
|
|
2033
|
+
cursor: 'pointer',
|
|
2034
|
+
transition: 'all 150ms',
|
|
2035
|
+
});
|
|
2036
|
+
btn.onmouseenter = () => {
|
|
2037
|
+
btn.style.borderColor = accentColor;
|
|
2038
|
+
btn.style.backgroundColor = `${accentColor}20`;
|
|
2039
|
+
btn.style.color = iconColor;
|
|
2040
|
+
};
|
|
2041
|
+
btn.onmouseleave = () => {
|
|
2042
|
+
btn.style.borderColor = `${accentColor}60`;
|
|
2043
|
+
btn.style.backgroundColor = 'transparent';
|
|
2044
|
+
btn.style.color = `${iconColor}99`;
|
|
2045
|
+
};
|
|
2046
|
+
btn.onclick = () => {
|
|
2047
|
+
this.toggleCompactMode();
|
|
2048
|
+
};
|
|
2049
|
+
// Chevron icon SVG - points right when expanded, left when compact
|
|
2050
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
2051
|
+
svg.setAttribute('width', '12');
|
|
2052
|
+
svg.setAttribute('height', '12');
|
|
2053
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
2054
|
+
svg.setAttribute('fill', 'none');
|
|
2055
|
+
svg.setAttribute('stroke', 'currentColor');
|
|
2056
|
+
svg.setAttribute('stroke-width', '2');
|
|
2057
|
+
svg.setAttribute('stroke-linecap', 'round');
|
|
2058
|
+
svg.setAttribute('stroke-linejoin', 'round');
|
|
2059
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
|
|
2060
|
+
// Left chevron (<) when expanded to shrink, right chevron (>) when compact to expand
|
|
2061
|
+
path.setAttribute('points', isCompact ? '9 18 15 12 9 6' : '15 18 9 12 15 6');
|
|
2062
|
+
svg.appendChild(path);
|
|
2063
|
+
btn.appendChild(svg);
|
|
2064
|
+
return btn;
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Create a settings section with title
|
|
2068
|
+
*/
|
|
2069
|
+
createSettingsSection(title, hasBorder = true) {
|
|
2070
|
+
const color = COLORS.textSecondary;
|
|
2071
|
+
const section = document.createElement('div');
|
|
2072
|
+
Object.assign(section.style, {
|
|
2073
|
+
padding: '10px 14px',
|
|
2074
|
+
borderBottom: hasBorder ? `1px solid ${color}20` : 'none',
|
|
2075
|
+
});
|
|
2076
|
+
const sectionTitle = document.createElement('div');
|
|
2077
|
+
Object.assign(sectionTitle.style, {
|
|
2078
|
+
color,
|
|
2079
|
+
fontSize: '0.625rem',
|
|
2080
|
+
textTransform: 'uppercase',
|
|
2081
|
+
letterSpacing: '0.1em',
|
|
2082
|
+
marginBottom: '8px',
|
|
2083
|
+
});
|
|
2084
|
+
sectionTitle.textContent = title;
|
|
2085
|
+
section.appendChild(sectionTitle);
|
|
2086
|
+
return section;
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Create a toggle switch row
|
|
2090
|
+
*/
|
|
2091
|
+
createToggleRow(label, checked, accentColor, onChange) {
|
|
2092
|
+
const color = COLORS.textSecondary;
|
|
2093
|
+
const row = document.createElement('div');
|
|
2094
|
+
Object.assign(row.style, {
|
|
2095
|
+
display: 'flex',
|
|
2096
|
+
alignItems: 'center',
|
|
2097
|
+
justifyContent: 'space-between',
|
|
2098
|
+
marginBottom: '6px',
|
|
2099
|
+
});
|
|
2100
|
+
const labelEl = document.createElement('span');
|
|
2101
|
+
Object.assign(labelEl.style, { color: COLORS.text, fontSize: '0.6875rem' });
|
|
2102
|
+
labelEl.textContent = label;
|
|
2103
|
+
row.appendChild(labelEl);
|
|
2104
|
+
const toggle = document.createElement('button');
|
|
2105
|
+
Object.assign(toggle.style, {
|
|
2106
|
+
width: '32px',
|
|
2107
|
+
height: '18px',
|
|
2108
|
+
borderRadius: '9px',
|
|
2109
|
+
border: 'none',
|
|
2110
|
+
backgroundColor: checked ? accentColor : `${color}40`,
|
|
2111
|
+
position: 'relative',
|
|
2112
|
+
cursor: 'pointer',
|
|
2113
|
+
transition: 'all 150ms',
|
|
2114
|
+
flexShrink: '0',
|
|
2115
|
+
});
|
|
2116
|
+
const knob = document.createElement('span');
|
|
2117
|
+
Object.assign(knob.style, {
|
|
2118
|
+
position: 'absolute',
|
|
2119
|
+
top: '2px',
|
|
2120
|
+
left: checked ? '16px' : '2px',
|
|
2121
|
+
width: '14px',
|
|
2122
|
+
height: '14px',
|
|
2123
|
+
borderRadius: '50%',
|
|
2124
|
+
backgroundColor: '#fff',
|
|
2125
|
+
transition: 'left 150ms',
|
|
2126
|
+
});
|
|
2127
|
+
toggle.appendChild(knob);
|
|
2128
|
+
toggle.onclick = onChange;
|
|
2129
|
+
row.appendChild(toggle);
|
|
2130
|
+
return row;
|
|
2131
|
+
}
|
|
1857
2132
|
/**
|
|
1858
2133
|
* Render the settings popover
|
|
1859
2134
|
*/
|
|
@@ -1876,7 +2151,10 @@ export class GlobalDevBar {
|
|
|
1876
2151
|
boxShadow: `0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px ${accentColor}33`,
|
|
1877
2152
|
backdropFilter: 'blur(8px)',
|
|
1878
2153
|
WebkitBackdropFilter: 'blur(8px)',
|
|
1879
|
-
minWidth: '
|
|
2154
|
+
minWidth: '240px',
|
|
2155
|
+
maxWidth: '280px',
|
|
2156
|
+
maxHeight: 'calc(100vh - 100px)',
|
|
2157
|
+
overflowY: 'auto',
|
|
1880
2158
|
fontFamily: FONT_MONO,
|
|
1881
2159
|
});
|
|
1882
2160
|
// Header
|
|
@@ -1887,6 +2165,10 @@ export class GlobalDevBar {
|
|
|
1887
2165
|
justifyContent: 'space-between',
|
|
1888
2166
|
padding: '10px 14px',
|
|
1889
2167
|
borderBottom: `1px solid ${accentColor}30`,
|
|
2168
|
+
position: 'sticky',
|
|
2169
|
+
top: '0',
|
|
2170
|
+
backgroundColor: 'rgba(17, 24, 39, 0.98)',
|
|
2171
|
+
zIndex: '1',
|
|
1890
2172
|
});
|
|
1891
2173
|
const title = document.createElement('span');
|
|
1892
2174
|
Object.assign(title.style, { color: accentColor, fontSize: '0.75rem', fontWeight: '600' });
|
|
@@ -1905,19 +2187,8 @@ export class GlobalDevBar {
|
|
|
1905
2187
|
};
|
|
1906
2188
|
header.appendChild(closeBtn);
|
|
1907
2189
|
popover.appendChild(header);
|
|
1908
|
-
//
|
|
1909
|
-
const themeSection =
|
|
1910
|
-
Object.assign(themeSection.style, { padding: '10px 14px', borderBottom: `1px solid ${color}20` });
|
|
1911
|
-
const themeSectionTitle = document.createElement('div');
|
|
1912
|
-
Object.assign(themeSectionTitle.style, {
|
|
1913
|
-
color,
|
|
1914
|
-
fontSize: '0.625rem',
|
|
1915
|
-
textTransform: 'uppercase',
|
|
1916
|
-
letterSpacing: '0.1em',
|
|
1917
|
-
marginBottom: '8px',
|
|
1918
|
-
});
|
|
1919
|
-
themeSectionTitle.textContent = 'Theme';
|
|
1920
|
-
themeSection.appendChild(themeSectionTitle);
|
|
2190
|
+
// ========== THEME SECTION ==========
|
|
2191
|
+
const themeSection = this.createSettingsSection('Theme');
|
|
1921
2192
|
const themeOptions = document.createElement('div');
|
|
1922
2193
|
Object.assign(themeOptions.style, { display: 'flex', gap: '6px' });
|
|
1923
2194
|
const themeModes = ['system', 'dark', 'light'];
|
|
@@ -1943,73 +2214,214 @@ export class GlobalDevBar {
|
|
|
1943
2214
|
});
|
|
1944
2215
|
themeSection.appendChild(themeOptions);
|
|
1945
2216
|
popover.appendChild(themeSection);
|
|
1946
|
-
//
|
|
1947
|
-
const displaySection =
|
|
1948
|
-
|
|
1949
|
-
const
|
|
1950
|
-
Object.assign(
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
marginBottom: '
|
|
1956
|
-
});
|
|
1957
|
-
displaySectionTitle.textContent = 'Display';
|
|
1958
|
-
displaySection.appendChild(displaySectionTitle);
|
|
1959
|
-
// Compact mode toggle
|
|
1960
|
-
const compactRow = document.createElement('div');
|
|
1961
|
-
Object.assign(compactRow.style, {
|
|
1962
|
-
display: 'flex',
|
|
1963
|
-
alignItems: 'center',
|
|
1964
|
-
justifyContent: 'space-between',
|
|
2217
|
+
// ========== DISPLAY SECTION ==========
|
|
2218
|
+
const displaySection = this.createSettingsSection('Display');
|
|
2219
|
+
// Position mini-map selector
|
|
2220
|
+
const positionRow = document.createElement('div');
|
|
2221
|
+
Object.assign(positionRow.style, { marginBottom: '10px' });
|
|
2222
|
+
const posLabel = document.createElement('div');
|
|
2223
|
+
Object.assign(posLabel.style, {
|
|
2224
|
+
color: COLORS.text,
|
|
2225
|
+
fontSize: '0.6875rem',
|
|
2226
|
+
marginBottom: '6px',
|
|
1965
2227
|
});
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
const toggle = document.createElement('button');
|
|
1972
|
-
const isCompact = this.compactMode;
|
|
1973
|
-
Object.assign(toggle.style, {
|
|
1974
|
-
width: '32px',
|
|
1975
|
-
height: '18px',
|
|
1976
|
-
borderRadius: '9px',
|
|
1977
|
-
border: 'none',
|
|
1978
|
-
backgroundColor: isCompact ? accentColor : `${color}40`,
|
|
2228
|
+
posLabel.textContent = 'Position';
|
|
2229
|
+
positionRow.appendChild(posLabel);
|
|
2230
|
+
// Mini-map container
|
|
2231
|
+
const miniMap = document.createElement('div');
|
|
2232
|
+
Object.assign(miniMap.style, {
|
|
1979
2233
|
position: 'relative',
|
|
1980
|
-
|
|
1981
|
-
|
|
2234
|
+
width: '100%',
|
|
2235
|
+
height: '50px',
|
|
2236
|
+
backgroundColor: 'rgba(10, 15, 26, 0.6)',
|
|
2237
|
+
border: `1px solid ${color}30`,
|
|
2238
|
+
borderRadius: '4px',
|
|
1982
2239
|
});
|
|
1983
|
-
const
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2240
|
+
const positionConfigs = [
|
|
2241
|
+
{ value: 'top-left', style: { top: '8px', left: '10%' }, title: 'Top Left' },
|
|
2242
|
+
{ value: 'top-right', style: { top: '8px', right: '6%' }, title: 'Top Right' },
|
|
2243
|
+
{ value: 'bottom-left', style: { bottom: '8px', left: '10%' }, title: 'Bottom Left' },
|
|
2244
|
+
{ value: 'bottom-right', style: { bottom: '8px', right: '6%' }, title: 'Bottom Right' },
|
|
2245
|
+
{
|
|
2246
|
+
value: 'bottom-center',
|
|
2247
|
+
style: { bottom: '6px', left: '50%', transform: 'translateX(-50%)' },
|
|
2248
|
+
title: 'Bottom Center',
|
|
2249
|
+
},
|
|
2250
|
+
];
|
|
2251
|
+
positionConfigs.forEach(({ value, style, title }) => {
|
|
2252
|
+
const indicator = document.createElement('button');
|
|
2253
|
+
const isActive = this.options.position === value;
|
|
2254
|
+
Object.assign(indicator.style, {
|
|
2255
|
+
position: 'absolute',
|
|
2256
|
+
width: '20px',
|
|
2257
|
+
height: '6px',
|
|
2258
|
+
backgroundColor: isActive ? accentColor : `${color}60`,
|
|
2259
|
+
border: `1px solid ${isActive ? accentColor : `${color}40`}`,
|
|
2260
|
+
borderRadius: '2px',
|
|
2261
|
+
cursor: 'pointer',
|
|
2262
|
+
padding: '0',
|
|
2263
|
+
transition: 'all 150ms',
|
|
2264
|
+
boxShadow: isActive ? `0 0 8px ${accentColor}60` : 'none',
|
|
2265
|
+
...style,
|
|
2266
|
+
});
|
|
2267
|
+
indicator.title = title;
|
|
2268
|
+
indicator.onclick = () => {
|
|
2269
|
+
this.options.position = value;
|
|
2270
|
+
this.settingsManager.saveSettings({ position: value });
|
|
2271
|
+
this.render();
|
|
2272
|
+
};
|
|
2273
|
+
// Hover effect
|
|
2274
|
+
indicator.onmouseenter = () => {
|
|
2275
|
+
if (!isActive) {
|
|
2276
|
+
indicator.style.backgroundColor = accentColor;
|
|
2277
|
+
indicator.style.borderColor = accentColor;
|
|
2278
|
+
indicator.style.boxShadow = `0 0 6px ${accentColor}40`;
|
|
2279
|
+
}
|
|
2280
|
+
};
|
|
2281
|
+
indicator.onmouseleave = () => {
|
|
2282
|
+
if (!isActive) {
|
|
2283
|
+
indicator.style.backgroundColor = `${color}60`;
|
|
2284
|
+
indicator.style.borderColor = `${color}40`;
|
|
2285
|
+
indicator.style.boxShadow = 'none';
|
|
2286
|
+
}
|
|
2287
|
+
};
|
|
2288
|
+
miniMap.appendChild(indicator);
|
|
1993
2289
|
});
|
|
1994
|
-
|
|
1995
|
-
|
|
2290
|
+
positionRow.appendChild(miniMap);
|
|
2291
|
+
displaySection.appendChild(positionRow);
|
|
2292
|
+
// Compact mode toggle
|
|
2293
|
+
displaySection.appendChild(this.createToggleRow('Compact Mode', this.compactMode, accentColor, () => {
|
|
1996
2294
|
this.toggleCompactMode();
|
|
1997
|
-
};
|
|
1998
|
-
compactRow.appendChild(toggle);
|
|
1999
|
-
displaySection.appendChild(compactRow);
|
|
2295
|
+
}));
|
|
2000
2296
|
// Keyboard shortcut hint
|
|
2001
2297
|
const shortcutHint = document.createElement('div');
|
|
2002
2298
|
Object.assign(shortcutHint.style, {
|
|
2003
2299
|
color: COLORS.textMuted,
|
|
2004
2300
|
fontSize: '0.5625rem',
|
|
2005
|
-
marginTop: '
|
|
2301
|
+
marginTop: '2px',
|
|
2302
|
+
marginBottom: '8px',
|
|
2006
2303
|
});
|
|
2007
2304
|
shortcutHint.textContent = 'Keyboard: Cmd+Shift+M';
|
|
2008
2305
|
displaySection.appendChild(shortcutHint);
|
|
2306
|
+
// Accent color
|
|
2307
|
+
const accentRow = document.createElement('div');
|
|
2308
|
+
Object.assign(accentRow.style, { marginBottom: '6px' });
|
|
2309
|
+
const accentLabel = document.createElement('div');
|
|
2310
|
+
Object.assign(accentLabel.style, {
|
|
2311
|
+
color: COLORS.text,
|
|
2312
|
+
fontSize: '0.6875rem',
|
|
2313
|
+
marginBottom: '6px',
|
|
2314
|
+
});
|
|
2315
|
+
accentLabel.textContent = 'Accent Color';
|
|
2316
|
+
accentRow.appendChild(accentLabel);
|
|
2317
|
+
const colorSwatches = document.createElement('div');
|
|
2318
|
+
Object.assign(colorSwatches.style, {
|
|
2319
|
+
display: 'flex',
|
|
2320
|
+
gap: '6px',
|
|
2321
|
+
flexWrap: 'wrap',
|
|
2322
|
+
});
|
|
2323
|
+
ACCENT_COLOR_PRESETS.forEach(({ name, value }) => {
|
|
2324
|
+
const swatch = document.createElement('button');
|
|
2325
|
+
const isActive = this.options.accentColor === value;
|
|
2326
|
+
Object.assign(swatch.style, {
|
|
2327
|
+
width: '24px',
|
|
2328
|
+
height: '24px',
|
|
2329
|
+
borderRadius: '50%',
|
|
2330
|
+
backgroundColor: value,
|
|
2331
|
+
border: isActive ? '2px solid #fff' : '2px solid transparent',
|
|
2332
|
+
cursor: 'pointer',
|
|
2333
|
+
transition: 'all 150ms',
|
|
2334
|
+
boxShadow: isActive ? `0 0 8px ${value}` : 'none',
|
|
2335
|
+
});
|
|
2336
|
+
swatch.title = name;
|
|
2337
|
+
swatch.onclick = () => {
|
|
2338
|
+
this.options.accentColor = value;
|
|
2339
|
+
this.settingsManager.saveSettings({ accentColor: value });
|
|
2340
|
+
this.render();
|
|
2341
|
+
};
|
|
2342
|
+
colorSwatches.appendChild(swatch);
|
|
2343
|
+
});
|
|
2344
|
+
accentRow.appendChild(colorSwatches);
|
|
2345
|
+
displaySection.appendChild(accentRow);
|
|
2009
2346
|
popover.appendChild(displaySection);
|
|
2347
|
+
// ========== FEATURES SECTION ==========
|
|
2348
|
+
const featuresSection = this.createSettingsSection('Features');
|
|
2349
|
+
featuresSection.appendChild(this.createToggleRow('Screenshot Button', this.options.showScreenshot, accentColor, () => {
|
|
2350
|
+
this.options.showScreenshot = !this.options.showScreenshot;
|
|
2351
|
+
this.settingsManager.saveSettings({ showScreenshot: this.options.showScreenshot });
|
|
2352
|
+
this.render();
|
|
2353
|
+
}));
|
|
2354
|
+
featuresSection.appendChild(this.createToggleRow('Console Badges', this.options.showConsoleBadges, accentColor, () => {
|
|
2355
|
+
this.options.showConsoleBadges = !this.options.showConsoleBadges;
|
|
2356
|
+
this.settingsManager.saveSettings({ showConsoleBadges: this.options.showConsoleBadges });
|
|
2357
|
+
this.render();
|
|
2358
|
+
}));
|
|
2359
|
+
featuresSection.appendChild(this.createToggleRow('Tooltips', this.options.showTooltips, accentColor, () => {
|
|
2360
|
+
this.options.showTooltips = !this.options.showTooltips;
|
|
2361
|
+
this.settingsManager.saveSettings({ showTooltips: this.options.showTooltips });
|
|
2362
|
+
this.render();
|
|
2363
|
+
}));
|
|
2364
|
+
popover.appendChild(featuresSection);
|
|
2365
|
+
// ========== METRICS SECTION ==========
|
|
2366
|
+
const metricsSection = this.createSettingsSection('Metrics');
|
|
2367
|
+
const metricsToggles = [
|
|
2368
|
+
{ key: 'breakpoint', label: 'Breakpoint' },
|
|
2369
|
+
{ key: 'fcp', label: 'FCP' },
|
|
2370
|
+
{ key: 'lcp', label: 'LCP' },
|
|
2371
|
+
{ key: 'cls', label: 'CLS' },
|
|
2372
|
+
{ key: 'inp', label: 'INP' },
|
|
2373
|
+
{ key: 'pageSize', label: 'Page Size' },
|
|
2374
|
+
];
|
|
2375
|
+
metricsToggles.forEach(({ key, label }) => {
|
|
2376
|
+
const currentValue = this.options.showMetrics[key] ?? true;
|
|
2377
|
+
metricsSection.appendChild(this.createToggleRow(label, currentValue, accentColor, () => {
|
|
2378
|
+
this.options.showMetrics[key] = !this.options.showMetrics[key];
|
|
2379
|
+
this.settingsManager.saveSettings({
|
|
2380
|
+
showMetrics: {
|
|
2381
|
+
breakpoint: this.options.showMetrics.breakpoint ?? true,
|
|
2382
|
+
fcp: this.options.showMetrics.fcp ?? true,
|
|
2383
|
+
lcp: this.options.showMetrics.lcp ?? true,
|
|
2384
|
+
cls: this.options.showMetrics.cls ?? true,
|
|
2385
|
+
inp: this.options.showMetrics.inp ?? true,
|
|
2386
|
+
pageSize: this.options.showMetrics.pageSize ?? true,
|
|
2387
|
+
},
|
|
2388
|
+
});
|
|
2389
|
+
this.render();
|
|
2390
|
+
}));
|
|
2391
|
+
});
|
|
2392
|
+
popover.appendChild(metricsSection);
|
|
2393
|
+
// ========== RESET SECTION ==========
|
|
2394
|
+
const resetSection = document.createElement('div');
|
|
2395
|
+
Object.assign(resetSection.style, {
|
|
2396
|
+
padding: '10px 14px',
|
|
2397
|
+
borderTop: `1px solid ${color}20`,
|
|
2398
|
+
});
|
|
2399
|
+
const resetBtn = createStyledButton({
|
|
2400
|
+
color: COLORS.textMuted,
|
|
2401
|
+
text: 'Reset to Defaults',
|
|
2402
|
+
padding: '6px 12px',
|
|
2403
|
+
fontSize: '0.625rem',
|
|
2404
|
+
});
|
|
2405
|
+
Object.assign(resetBtn.style, {
|
|
2406
|
+
width: '100%',
|
|
2407
|
+
justifyContent: 'center',
|
|
2408
|
+
});
|
|
2409
|
+
resetBtn.onclick = () => {
|
|
2410
|
+
this.resetToDefaults();
|
|
2411
|
+
};
|
|
2412
|
+
resetSection.appendChild(resetBtn);
|
|
2413
|
+
popover.appendChild(resetSection);
|
|
2010
2414
|
this.overlayElement = popover;
|
|
2011
2415
|
document.body.appendChild(popover);
|
|
2012
2416
|
}
|
|
2417
|
+
/**
|
|
2418
|
+
* Reset all settings to defaults
|
|
2419
|
+
*/
|
|
2420
|
+
resetToDefaults() {
|
|
2421
|
+
this.settingsManager.resetToDefaults();
|
|
2422
|
+
const defaults = DEFAULT_SETTINGS;
|
|
2423
|
+
this.applySettings(defaults);
|
|
2424
|
+
}
|
|
2013
2425
|
renderCollapsed() {
|
|
2014
2426
|
if (!this.container)
|
|
2015
2427
|
return;
|
|
@@ -2325,6 +2737,7 @@ export class GlobalDevBar {
|
|
|
2325
2737
|
actionsContainer.appendChild(this.createOutlineButton());
|
|
2326
2738
|
actionsContainer.appendChild(this.createSchemaButton());
|
|
2327
2739
|
actionsContainer.appendChild(this.createSettingsButton());
|
|
2740
|
+
actionsContainer.appendChild(this.createCompactToggleButton());
|
|
2328
2741
|
mainRow.appendChild(actionsContainer);
|
|
2329
2742
|
wrapper.appendChild(mainRow);
|
|
2330
2743
|
// Render custom controls row if there are any
|