@ytspar/devbar 1.0.0-canary.2b99e1e → 1.0.0-canary.3c85c90

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.
@@ -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 { DevBarPosition, DevBarSettings, MetricsVisibility } from './settings.js';
14
- export { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, getSettingsManager } from './settings.js';
13
+ export type { DevBarSettings, DevBarPosition, MetricsVisibility } from './settings.js';
14
+ export { DEFAULT_SETTINGS, ACCENT_COLOR_PRESETS, getSettingsManager } from './settings.js';
15
15
  interface EarlyConsoleCapture {
16
16
  errorCount: number;
17
17
  warningCount: number;
@@ -46,8 +46,6 @@ export declare class GlobalDevBar {
46
46
  private apiKeyStatus;
47
47
  private lastOutline;
48
48
  private lastSchema;
49
- private savingOutline;
50
- private savingSchema;
51
49
  private consoleFilter;
52
50
  private showOutlineModal;
53
51
  private showSchemaModal;
@@ -57,10 +55,6 @@ export declare class GlobalDevBar {
57
55
  private clsValue;
58
56
  private inpValue;
59
57
  private reconnectAttempts;
60
- private readonly currentAppPort;
61
- private readonly baseWsPort;
62
- private wsVerified;
63
- private serverProjectDir;
64
58
  private lastDotPosition;
65
59
  private reconnectTimeout;
66
60
  private screenshotTimeout;
@@ -237,10 +231,6 @@ export declare class GlobalDevBar {
237
231
  * Create the settings gear button
238
232
  */
239
233
  private createSettingsButton;
240
- /**
241
- * Create the compact mode toggle button with chevron icon
242
- */
243
- private createCompactToggleButton;
244
234
  /**
245
235
  * Create a settings section with title
246
236
  */
@@ -259,30 +249,6 @@ export declare class GlobalDevBar {
259
249
  private resetToDefaults;
260
250
  private renderCollapsed;
261
251
  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;
286
252
  /**
287
253
  * Create a console badge for error/warning counts
288
254
  */
@@ -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, 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';
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';
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 { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, getSettingsManager } from './settings.js';
18
+ export { DEFAULT_SETTINGS, ACCENT_COLOR_PRESETS, getSettingsManager } from './settings.js';
19
19
  const html2canvas = (html2canvasModule.default ??
20
20
  html2canvasModule);
21
21
  const earlyConsoleCapture = (() => {
@@ -91,8 +91,6 @@ 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;
96
94
  this.consoleFilter = null;
97
95
  // Modal states
98
96
  this.showOutlineModal = false;
@@ -103,8 +101,6 @@ export class GlobalDevBar {
103
101
  this.clsValue = 0;
104
102
  this.inpValue = 0;
105
103
  this.reconnectAttempts = 0;
106
- this.wsVerified = false;
107
- this.serverProjectDir = null;
108
104
  // Track the position of the connection indicator dot for smooth collapse
109
105
  this.lastDotPosition = null;
110
106
  this.reconnectTimeout = null;
@@ -131,39 +127,11 @@ export class GlobalDevBar {
131
127
  this.showSettingsPopover = false;
132
128
  // Overlay element for modals
133
129
  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
- };
151
130
  // Initialize debug config first so we can log during construction
152
131
  this.debugConfig = normalizeDebugConfig(options.debug);
153
132
  this.debug = new DebugLogger(this.debugConfig);
154
133
  // Initialize settings manager
155
134
  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
- }
167
135
  this.options = {
168
136
  position: options.position ?? 'bottom-left',
169
137
  accentColor: options.accentColor ?? COLORS.primary,
@@ -377,64 +345,27 @@ export class GlobalDevBar {
377
345
  document.head.appendChild(style);
378
346
  }
379
347
  }
380
- connectWebSocket(port) {
348
+ connectWebSocket() {
381
349
  if (this.destroyed)
382
350
  return;
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}`);
351
+ this.debug.ws('Connecting to WebSocket', { port: WS_PORT });
352
+ const ws = new WebSocket(`ws://localhost:${WS_PORT}`);
386
353
  this.ws = ws;
387
- this.wsVerified = false;
388
354
  ws.onopen = () => {
389
- this.debug.ws('WebSocket socket opened, awaiting server-info');
355
+ this.sweetlinkConnected = true;
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);
390
361
  ws.send(JSON.stringify({ type: 'browser-client-ready' }));
362
+ // Request settings from server
363
+ ws.send(JSON.stringify({ type: 'load-settings' }));
364
+ this.render();
391
365
  };
392
366
  ws.onmessage = async (event) => {
393
367
  try {
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;
368
+ const command = JSON.parse(event.data);
438
369
  this.debug.ws('Received command', { type: command.type });
439
370
  await this.handleSweetlinkCommand(command);
440
371
  }
@@ -443,21 +374,16 @@ export class GlobalDevBar {
443
374
  }
444
375
  };
445
376
  ws.onclose = () => {
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
- }
377
+ this.sweetlinkConnected = false;
378
+ this.settingsManager.setConnected(false);
379
+ this.debug.ws('WebSocket disconnected');
380
+ this.render();
381
+ // Auto-reconnect with exponential backoff
382
+ if (!this.destroyed && this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
383
+ const delayMs = BASE_RECONNECT_DELAY_MS * 2 ** this.reconnectAttempts;
384
+ this.reconnectAttempts++;
385
+ this.debug.ws('Scheduling reconnect', { attempt: this.reconnectAttempts, delayMs });
386
+ this.reconnectTimeout = setTimeout(() => this.connectWebSocket(), Math.min(delayMs, MAX_RECONNECT_DELAY_MS));
461
387
  }
462
388
  };
463
389
  ws.onerror = () => {
@@ -653,7 +579,6 @@ export class GlobalDevBar {
653
579
  }, durationMs);
654
580
  break;
655
581
  case 'outline':
656
- this.savingOutline = false;
657
582
  this.lastOutline = path;
658
583
  if (this.outlineTimeout)
659
584
  clearTimeout(this.outlineTimeout);
@@ -663,7 +588,6 @@ export class GlobalDevBar {
663
588
  }, durationMs);
664
589
  break;
665
590
  case 'schema':
666
- this.savingSchema = false;
667
591
  this.lastSchema = path;
668
592
  if (this.schemaTimeout)
669
593
  clearTimeout(this.schemaTimeout);
@@ -796,11 +720,7 @@ export class GlobalDevBar {
796
720
  }
797
721
  });
798
722
  // durationThreshold filters out very short interactions
799
- this.inpObserver.observe({
800
- type: 'event',
801
- buffered: true,
802
- durationThreshold: 16,
803
- });
723
+ this.inpObserver.observe({ type: 'event', buffered: true, durationThreshold: 16 });
804
724
  }
805
725
  catch (e) {
806
726
  console.warn('[GlobalDevBar] INP PerformanceObserver not supported', e);
@@ -857,16 +777,12 @@ export class GlobalDevBar {
857
777
  // Load stored theme preference from settings manager
858
778
  const settings = this.settingsManager.getSettings();
859
779
  this.themeMode = settings.themeMode;
860
- // Inject the appropriate theme CSS variables on initial load
861
- injectThemeCSS(getTheme(this.themeMode));
862
780
  this.debug.state('Theme loaded', { mode: this.themeMode });
863
781
  // Listen for system theme changes
864
782
  if (typeof window !== 'undefined' && window.matchMedia) {
865
783
  this.themeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
866
784
  this.themeMediaHandler = () => {
867
785
  if (this.themeMode === 'system') {
868
- // Re-inject theme CSS when system preference changes
869
- injectThemeCSS(getTheme(this.themeMode));
870
786
  this.debug.state('System theme changed', {
871
787
  effectiveTheme: getEffectiveTheme(this.themeMode),
872
788
  });
@@ -893,8 +809,6 @@ export class GlobalDevBar {
893
809
  setThemeMode(mode) {
894
810
  this.themeMode = mode;
895
811
  this.settingsManager.saveSettings({ themeMode: mode });
896
- // Inject the appropriate theme CSS variables
897
- injectThemeCSS(getTheme(mode));
898
812
  this.debug.state('Theme mode changed', { mode, effectiveTheme: getEffectiveTheme(mode) });
899
813
  this.render();
900
814
  }
@@ -1148,13 +1062,9 @@ export class GlobalDevBar {
1148
1062
  this.render();
1149
1063
  }
1150
1064
  handleSaveOutline() {
1151
- if (this.savingOutline)
1152
- return; // Prevent repeated clicks
1153
1065
  const outline = extractDocumentOutline();
1154
1066
  const markdown = outlineToMarkdown(outline);
1155
1067
  if (this.ws?.readyState === WebSocket.OPEN) {
1156
- this.savingOutline = true;
1157
- this.render();
1158
1068
  this.ws.send(JSON.stringify({
1159
1069
  type: 'save-outline',
1160
1070
  data: {
@@ -1168,13 +1078,9 @@ export class GlobalDevBar {
1168
1078
  }
1169
1079
  }
1170
1080
  handleSaveSchema() {
1171
- if (this.savingSchema)
1172
- return; // Prevent repeated clicks
1173
1081
  const schema = extractPageSchema();
1174
1082
  const markdown = schemaToMarkdown(schema);
1175
1083
  if (this.ws?.readyState === WebSocket.OPEN) {
1176
- this.savingSchema = true;
1177
- this.render();
1178
1084
  this.ws.send(JSON.stringify({
1179
1085
  type: 'save-schema',
1180
1086
  data: {
@@ -1552,8 +1458,6 @@ export class GlobalDevBar {
1552
1458
  },
1553
1459
  onSave: () => this.handleSaveOutline(),
1554
1460
  sweetlinkConnected: this.sweetlinkConnected,
1555
- isSaving: this.savingOutline,
1556
- savedPath: this.lastOutline,
1557
1461
  });
1558
1462
  modal.appendChild(header);
1559
1463
  const content = createModalContent();
@@ -1640,8 +1544,6 @@ export class GlobalDevBar {
1640
1544
  },
1641
1545
  onSave: () => this.handleSaveSchema(),
1642
1546
  sweetlinkConnected: this.sweetlinkConnected,
1643
- isSaving: this.savingSchema,
1644
- savedPath: this.lastSchema,
1645
1547
  });
1646
1548
  modal.appendChild(header);
1647
1549
  const content = createModalContent();
@@ -1999,64 +1901,6 @@ export class GlobalDevBar {
1999
1901
  btn.appendChild(svg);
2000
1902
  return btn;
2001
1903
  }
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
- }
2060
1904
  /**
2061
1905
  * Create a settings section with title
2062
1906
  */
@@ -2210,78 +2054,49 @@ export class GlobalDevBar {
2210
2054
  popover.appendChild(themeSection);
2211
2055
  // ========== DISPLAY SECTION ==========
2212
2056
  const displaySection = this.createSettingsSection('Display');
2213
- // Position mini-map selector
2057
+ // Position dropdown
2214
2058
  const positionRow = document.createElement('div');
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',
2059
+ Object.assign(positionRow.style, {
2060
+ display: 'flex',
2061
+ alignItems: 'center',
2062
+ justifyContent: 'space-between',
2063
+ marginBottom: '8px',
2221
2064
  });
2065
+ const posLabel = document.createElement('span');
2066
+ Object.assign(posLabel.style, { color: COLORS.text, fontSize: '0.6875rem' });
2222
2067
  posLabel.textContent = 'Position';
2223
2068
  positionRow.appendChild(posLabel);
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`,
2069
+ const posSelect = document.createElement('select');
2070
+ Object.assign(posSelect.style, {
2071
+ backgroundColor: 'rgba(10, 15, 26, 0.8)',
2072
+ border: `1px solid ${color}40`,
2232
2073
  borderRadius: '4px',
2074
+ color: COLORS.text,
2075
+ fontSize: '0.625rem',
2076
+ padding: '4px 6px',
2077
+ cursor: 'pointer',
2078
+ fontFamily: FONT_MONO,
2233
2079
  });
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
- },
2080
+ const positions = [
2081
+ { value: 'bottom-left', label: 'Bottom Left' },
2082
+ { value: 'bottom-right', label: 'Bottom Right' },
2083
+ { value: 'bottom-center', label: 'Bottom Center' },
2084
+ { value: 'top-left', label: 'Top Left' },
2085
+ { value: 'top-right', label: 'Top Right' },
2244
2086
  ];
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);
2087
+ positions.forEach(({ value, label }) => {
2088
+ const option = document.createElement('option');
2089
+ option.value = value;
2090
+ option.textContent = label;
2091
+ option.selected = this.options.position === value;
2092
+ posSelect.appendChild(option);
2283
2093
  });
2284
- positionRow.appendChild(miniMap);
2094
+ posSelect.onchange = () => {
2095
+ this.options.position = posSelect.value;
2096
+ this.settingsManager.saveSettings({ position: this.options.position });
2097
+ this.render();
2098
+ };
2099
+ positionRow.appendChild(posSelect);
2285
2100
  displaySection.appendChild(positionRow);
2286
2101
  // Compact mode toggle
2287
2102
  displaySection.appendChild(this.createToggleRow('Compact Mode', this.compactMode, accentColor, () => {
@@ -2643,10 +2458,9 @@ export class GlobalDevBar {
2643
2458
  const bp = this.breakpointInfo.tailwindBreakpoint;
2644
2459
  const breakpointData = TAILWIND_BREAKPOINTS[bp];
2645
2460
  const bpSpan = document.createElement('span');
2646
- bpSpan.className = 'devbar-item';
2461
+ bpSpan.className = this.tooltipClass('left', 'devbar-item');
2647
2462
  Object.assign(bpSpan.style, { opacity: '0.9', cursor: 'default' });
2648
- // Use HTML tooltip for breakpoint info
2649
- this.attachBreakpointTooltip(bpSpan, bp, this.breakpointInfo.dimensions, breakpointData?.label || '');
2463
+ bpSpan.setAttribute('data-tooltip', `Tailwind Breakpoint: ${bp}\n${breakpointData?.label || ''}\n\nViewport: ${this.breakpointInfo.dimensions}\n\nBreakpoints:\nbase: <640px | sm: >=640px\nmd: >=768px | lg: >=1024px\nxl: >=1280px | 2xl: >=1536px`);
2650
2464
  let bpText = bp;
2651
2465
  if (bp !== 'base') {
2652
2466
  bpText =
@@ -2668,45 +2482,45 @@ export class GlobalDevBar {
2668
2482
  if (showMetrics.fcp) {
2669
2483
  addSeparator();
2670
2484
  const fcpSpan = document.createElement('span');
2671
- fcpSpan.className = 'devbar-item';
2485
+ fcpSpan.className = this.tooltipClass('left', 'devbar-item');
2672
2486
  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');
2673
2488
  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' });
2675
2489
  infoSection.appendChild(fcpSpan);
2676
2490
  }
2677
2491
  if (showMetrics.lcp) {
2678
2492
  addSeparator();
2679
2493
  const lcpSpan = document.createElement('span');
2680
- lcpSpan.className = 'devbar-item';
2494
+ lcpSpan.className = this.tooltipClass('left', 'devbar-item');
2681
2495
  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');
2682
2497
  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' });
2684
2498
  infoSection.appendChild(lcpSpan);
2685
2499
  }
2686
2500
  if (showMetrics.cls) {
2687
2501
  addSeparator();
2688
2502
  const clsSpan = document.createElement('span');
2689
- clsSpan.className = 'devbar-item';
2503
+ clsSpan.className = this.tooltipClass('left', 'devbar-item');
2690
2504
  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');
2691
2506
  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' });
2693
2507
  infoSection.appendChild(clsSpan);
2694
2508
  }
2695
2509
  if (showMetrics.inp) {
2696
2510
  addSeparator();
2697
2511
  const inpSpan = document.createElement('span');
2698
- inpSpan.className = 'devbar-item';
2512
+ inpSpan.className = this.tooltipClass('left', 'devbar-item');
2699
2513
  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');
2700
2515
  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' });
2702
2516
  infoSection.appendChild(inpSpan);
2703
2517
  }
2704
2518
  if (showMetrics.pageSize) {
2705
2519
  addSeparator();
2706
2520
  const sizeSpan = document.createElement('span');
2707
- sizeSpan.className = 'devbar-item';
2521
+ sizeSpan.className = this.tooltipClass('left', 'devbar-item');
2708
2522
  Object.assign(sizeSpan.style, { opacity: '0.7', cursor: 'default' });
2709
- this.attachInfoTooltip(sizeSpan, 'Total Page Size', 'Compressed/transferred size including HTML, CSS, JS, images, and other resources.');
2523
+ sizeSpan.setAttribute('data-tooltip', 'Total page size (compressed/transferred).\nIncludes HTML, CSS, JS, images, and other resources.');
2710
2524
  sizeSpan.textContent = this.perfStats.totalSize;
2711
2525
  infoSection.appendChild(sizeSpan);
2712
2526
  }
@@ -2732,7 +2546,6 @@ export class GlobalDevBar {
2732
2546
  actionsContainer.appendChild(this.createOutlineButton());
2733
2547
  actionsContainer.appendChild(this.createSchemaButton());
2734
2548
  actionsContainer.appendChild(this.createSettingsButton());
2735
- actionsContainer.appendChild(this.createCompactToggleButton());
2736
2549
  mainRow.appendChild(actionsContainer);
2737
2550
  wrapper.appendChild(mainRow);
2738
2551
  // Render custom controls row if there are any
@@ -2787,207 +2600,6 @@ export class GlobalDevBar {
2787
2600
  wrapper.appendChild(customRow);
2788
2601
  }
2789
2602
  }
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
- }
2991
2603
  /**
2992
2604
  * Create a console badge for error/warning counts
2993
2605
  */
@@ -3026,9 +2638,6 @@ export class GlobalDevBar {
3026
2638
  btn.type = 'button';
3027
2639
  btn.className = this.tooltipClass('right');
3028
2640
  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;
3032
2641
  const tooltip = this.getScreenshotTooltip();
3033
2642
  btn.setAttribute('data-tooltip', tooltip);
3034
2643
  Object.assign(btn.style, {
@@ -3045,11 +2654,11 @@ export class GlobalDevBar {
3045
2654
  borderColor: hasSuccessState ? accentColor : `${accentColor}80`,
3046
2655
  backgroundColor: hasSuccessState ? `${accentColor}33` : 'transparent',
3047
2656
  color: hasSuccessState ? accentColor : `${accentColor}99`,
3048
- cursor: !isDisabled ? 'pointer' : 'not-allowed',
3049
- opacity: isGreyedOut ? '0.4' : '1',
2657
+ cursor: !this.capturing ? 'pointer' : 'not-allowed',
2658
+ opacity: '1',
3050
2659
  transition: 'all 150ms',
3051
2660
  });
3052
- btn.disabled = isDisabled;
2661
+ btn.disabled = this.capturing;
3053
2662
  btn.onclick = (e) => {
3054
2663
  // If we have a saved screenshot path, clicking copies the path
3055
2664
  if (this.lastScreenshot && !e.shiftKey) {
@@ -3106,10 +2715,10 @@ export class GlobalDevBar {
3106
2715
  if (this.lastScreenshot) {
3107
2716
  return `Screenshot saved!\n${this.lastScreenshot}\n\nClick to copy path`;
3108
2717
  }
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`;
2718
+ const baseTooltip = `Screenshot\n\nClick: Save to file\nShift+Click: Copy to clipboard\n\nKeyboard:\nCmd/Ctrl+Shift+S: Save\nCmd/Ctrl+Shift+C: Copy`;
2719
+ return this.sweetlinkConnected
2720
+ ? baseTooltip
2721
+ : `${baseTooltip}\n\nWarning: Sweetlink not connected`;
3113
2722
  }
3114
2723
  /**
3115
2724
  * Get the tooltip text for the AI review button based on current state
@@ -3169,16 +2778,9 @@ export class GlobalDevBar {
3169
2778
  const btn = document.createElement('button');
3170
2779
  btn.type = 'button';
3171
2780
  btn.className = this.tooltipClass('right');
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
- }
2781
+ const tooltip = this.lastOutline
2782
+ ? `Outline saved to:\n${this.lastOutline}`
2783
+ : `Document Outline\n\nView page heading structure and\nsave as markdown.`;
3182
2784
  btn.setAttribute('data-tooltip', tooltip);
3183
2785
  const isActive = this.showOutlineModal || !!this.lastOutline;
3184
2786
  Object.assign(btn.style, getButtonStyles(BUTTON_COLORS.outline, isActive, false));
@@ -3196,16 +2798,9 @@ export class GlobalDevBar {
3196
2798
  const btn = document.createElement('button');
3197
2799
  btn.type = 'button';
3198
2800
  btn.className = this.tooltipClass('right');
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
- }
2801
+ const tooltip = this.lastSchema
2802
+ ? `Schema saved to:\n${this.lastSchema}`
2803
+ : `Page Schema\n\nView JSON-LD, Open Graph, and\nother structured data.`;
3209
2804
  btn.setAttribute('data-tooltip', tooltip);
3210
2805
  const isActive = this.showSchemaModal || !!this.lastSchema;
3211
2806
  Object.assign(btn.style, getButtonStyles(BUTTON_COLORS.schema, isActive, false));
@@ -11,16 +11,8 @@ 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 (legacy fallback) */
14
+ /** Default WebSocket port for Sweetlink connection */
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;
24
16
  /** Duration to show screenshot notification (ms) */
25
17
  export declare const SCREENSHOT_NOTIFICATION_MS = 3000;
26
18
  /** Duration to show clipboard notification (ms) */
@@ -138,28 +130,28 @@ export declare const DEVBAR_THEME: {
138
130
  };
139
131
  };
140
132
  export type DevBarTheme = typeof DEVBAR_THEME;
141
- /** Light theme variant - terminal aesthetic with light green tones */
133
+ /** Light theme variant - same structure, different colors */
142
134
  export declare const DEVBAR_THEME_LIGHT: {
143
135
  readonly colors: {
144
- readonly primary: "#047857";
145
- readonly primaryHover: "#065f46";
146
- readonly primaryGlow: "rgba(4, 120, 87, 0.25)";
147
- readonly error: "#dc2626";
136
+ readonly primary: "#059669";
137
+ readonly primaryHover: "#047857";
138
+ readonly primaryGlow: "rgba(5, 150, 105, 0.2)";
139
+ readonly error: "#ef4444";
148
140
  readonly warning: "#d97706";
149
141
  readonly info: "#2563eb";
150
- readonly purple: "#7c3aed";
142
+ readonly purple: "#9333ea";
151
143
  readonly cyan: "#0891b2";
152
144
  readonly pink: "#db2777";
153
145
  readonly lime: "#65a30d";
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)";
146
+ readonly bg: "#f8fafc";
147
+ readonly bgCard: "rgba(255, 255, 255, 0.98)";
148
+ readonly bgElevated: "rgba(255, 255, 255, 0.99)";
149
+ readonly bgInput: "rgba(241, 245, 249, 0.9)";
150
+ readonly text: "#1e293b";
151
+ readonly textSecondary: "#475569";
152
+ readonly textMuted: "#64748b";
153
+ readonly border: "rgba(5, 150, 105, 0.25)";
154
+ readonly borderSubtle: "rgba(0, 0, 0, 0.06)";
163
155
  };
164
156
  readonly fonts: {
165
157
  readonly mono: "'Departure Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
@@ -189,10 +181,10 @@ export declare const DEVBAR_THEME_LIGHT: {
189
181
  readonly lg: "12px";
190
182
  };
191
183
  readonly shadows: {
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)";
184
+ readonly sm: "0 1px 2px rgba(0, 0, 0, 0.08)";
185
+ readonly md: "0 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(5, 150, 105, 0.1)";
186
+ readonly lg: "0 8px 32px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(5, 150, 105, 0.12)";
187
+ readonly glow: "0 0 20px rgba(5, 150, 105, 0.1)";
196
188
  };
197
189
  readonly transitions: {
198
190
  readonly fast: "150ms";
package/dist/constants.js CHANGED
@@ -18,16 +18,8 @@ export const MAX_RECONNECT_DELAY_MS = 30000;
18
18
  // ============================================================================
19
19
  // WebSocket Settings
20
20
  // ============================================================================
21
- /** Default WebSocket port for Sweetlink connection (legacy fallback) */
21
+ /** Default WebSocket port for Sweetlink connection */
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;
31
23
  // ============================================================================
32
24
  // Notification Durations
33
25
  // ============================================================================
@@ -182,44 +174,44 @@ export const DEVBAR_THEME = {
182
174
  fast: '150ms',
183
175
  },
184
176
  };
185
- /** Light theme variant - terminal aesthetic with light green tones */
177
+ /** Light theme variant - same structure, different colors */
186
178
  export const DEVBAR_THEME_LIGHT = {
187
179
  colors: {
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',
180
+ // Primary accent (darker for light mode)
181
+ primary: '#059669', // darker emerald
182
+ primaryHover: '#047857',
183
+ primaryGlow: 'rgba(5, 150, 105, 0.2)',
184
+ // Semantic colors (same)
185
+ error: PALETTE.red,
186
+ warning: '#d97706', // darker amber
187
+ info: '#2563eb', // darker blue
188
+ // Extended palette (darker variants)
189
+ purple: '#9333ea',
198
190
  cyan: '#0891b2',
199
191
  pink: '#db2777',
200
192
  lime: '#65a30d',
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
193
+ // Backgrounds (light)
194
+ bg: '#f8fafc',
195
+ bgCard: 'rgba(255, 255, 255, 0.98)',
196
+ bgElevated: 'rgba(255, 255, 255, 0.99)',
197
+ bgInput: 'rgba(241, 245, 249, 0.9)',
206
198
  // Text (dark on light)
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)',
199
+ text: '#1e293b',
200
+ textSecondary: '#475569',
201
+ textMuted: '#64748b',
202
+ // Borders (green-tinted)
203
+ border: 'rgba(5, 150, 105, 0.25)',
204
+ borderSubtle: 'rgba(0, 0, 0, 0.06)',
213
205
  },
214
206
  // Other properties same as dark theme
215
207
  fonts: DEVBAR_THEME.fonts,
216
208
  typography: DEVBAR_THEME.typography,
217
209
  radius: DEVBAR_THEME.radius,
218
210
  shadows: {
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)',
211
+ sm: '0 1px 2px rgba(0, 0, 0, 0.08)',
212
+ md: '0 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(5, 150, 105, 0.1)',
213
+ lg: '0 8px 32px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(5, 150, 105, 0.12)',
214
+ glow: '0 0 20px rgba(5, 150, 105, 0.1)',
223
215
  },
224
216
  transitions: DEVBAR_THEME.transitions,
225
217
  };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- export { type A11yState, type AxeResult, type AxeViolation, clearA11yCache, formatViolation, getBadgeColor, getCachedResult, getImpactColor, getViolationCounts, groupViolationsByImpact, isAxeLoaded, preloadAxe, runA11yAudit, } from './accessibility.js';
2
- export { BUTTON_COLORS, CATEGORY_COLORS, COLORS, DEVBAR_THEME, DEVBAR_THEME_LIGHT, type DevBarTheme, type DevBarThemeInput, FONT_MONO, generateBreakpointCSS, generateThemeCSSVars, getEffectiveTheme, getStoredThemeMode, getTheme, getThemeColors, injectThemeCSS, STORAGE_KEYS, setStoredThemeMode, TAILWIND_BREAKPOINTS, type TailwindBreakpoint, type ThemeColors, } from './constants.js';
1
+ export { BUTTON_COLORS, CATEGORY_COLORS, COLORS, DEVBAR_THEME, DEVBAR_THEME_LIGHT, type DevBarTheme, type DevBarThemeInput, FONT_MONO, generateBreakpointCSS, generateThemeCSSVars, getEffectiveTheme, getStoredThemeMode, getTheme, getThemeColors, injectThemeCSS, setStoredThemeMode, STORAGE_KEYS, TAILWIND_BREAKPOINTS, type TailwindBreakpoint, type ThemeColors, } from './constants.js';
3
2
  export { DebugLogger, normalizeDebugConfig } from './debug.js';
4
3
  export { EARLY_CONSOLE_CAPTURE_SCRIPT } from './earlyConsoleCapture.js';
5
4
  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';
8
7
  export { extractDocumentOutline, outlineToMarkdown } from './outline.js';
9
- export { initDebug, initFull, initMinimal, initPerformance, initResponsive, PRESET_DEBUG, PRESET_FULL, PRESET_MINIMAL, PRESET_PERFORMANCE, PRESET_RESPONSIVE, } from './presets.js';
10
8
  export { extractPageSchema, schemaToMarkdown } from './schema.js';
11
- export { beautifyJson, type CookieItem, clearLocalStorage, clearSessionStorage, deleteCookie, deleteLocalStorageItem, deleteSessionStorageItem, formatStorageSummary, getCookies, getLocalStorage, getSessionStorage, getStorageData, type StorageData, type StorageItem, setLocalStorageItem, setSessionStorageItem, } from './storage.js';
9
+ export { formatBytes as formatNetworkBytes, formatDuration, getInitiatorColor, NetworkMonitor, type NetworkEntry, type NetworkState, } from './network.js';
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';
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';
5
3
  // Re-export constants and theme utilities
6
- export { BUTTON_COLORS, CATEGORY_COLORS, COLORS, DEVBAR_THEME, DEVBAR_THEME_LIGHT, FONT_MONO, generateBreakpointCSS, generateThemeCSSVars, getEffectiveTheme, getStoredThemeMode, getTheme, getThemeColors, injectThemeCSS, STORAGE_KEYS, setStoredThemeMode, TAILWIND_BREAKPOINTS, } from './constants.js';
4
+ export { BUTTON_COLORS, CATEGORY_COLORS, COLORS, DEVBAR_THEME, DEVBAR_THEME_LIGHT, FONT_MONO, generateBreakpointCSS, generateThemeCSSVars, getEffectiveTheme, getStoredThemeMode, getTheme, getThemeColors, injectThemeCSS, setStoredThemeMode, STORAGE_KEYS, TAILWIND_BREAKPOINTS, } from './constants.js';
7
5
  // Debug utilities
8
6
  export { DebugLogger, normalizeDebugConfig } from './debug.js';
9
7
  // Early console capture script for injection
10
8
  export { EARLY_CONSOLE_CAPTURE_SCRIPT } from './earlyConsoleCapture.js';
11
9
  // Main vanilla JS devbar
12
10
  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';
17
15
  // Re-export outline/schema functions
18
16
  export { extractDocumentOutline, outlineToMarkdown } from './outline.js';
19
- // Configuration presets
20
- export { initDebug, initFull, initMinimal, initPerformance, initResponsive, PRESET_DEBUG, PRESET_FULL, PRESET_MINIMAL, PRESET_PERFORMANCE, PRESET_RESPONSIVE, } from './presets.js';
21
17
  export { extractPageSchema, schemaToMarkdown } from './schema.js';
18
+ // Network monitoring utilities
19
+ export { formatBytes as formatNetworkBytes, formatDuration, getInitiatorColor, NetworkMonitor, } from './network.js';
22
20
  // Storage inspection utilities
23
21
  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 { type GlobalDevBar } from './GlobalDevBar.js';
6
+ import { GlobalDevBar } from './GlobalDevBar.js';
7
7
  import type { GlobalDevBarOptions } from './types.js';
8
8
  /**
9
9
  * Minimal preset - shows only essential features
@@ -13,10 +13,6 @@ 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;
20
16
  }
21
17
  /**
22
18
  * 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, isSaving, savedPath } = config;
37
+ const { color, title, onClose, onCopyMd, onSave, sweetlinkConnected } = config;
38
38
  const header = document.createElement('div');
39
39
  Object.assign(header.style, {
40
40
  display: 'flex',
@@ -42,8 +42,6 @@ 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',
47
45
  });
48
46
  const titleEl = document.createElement('h2');
49
47
  Object.assign(titleEl.style, {
@@ -55,7 +53,7 @@ export function createModalHeader(config) {
55
53
  titleEl.textContent = title;
56
54
  header.appendChild(titleEl);
57
55
  const headerButtons = document.createElement('div');
58
- Object.assign(headerButtons.style, { display: 'flex', gap: '10px', alignItems: 'center' });
56
+ Object.assign(headerButtons.style, { display: 'flex', gap: '10px' });
59
57
  // Copy MD button
60
58
  const copyBtn = createStyledButton({ color, text: 'Copy MD' });
61
59
  copyBtn.onclick = async () => {
@@ -73,17 +71,8 @@ export function createModalHeader(config) {
73
71
  headerButtons.appendChild(copyBtn);
74
72
  // Save button (if Sweetlink connected)
75
73
  if (sweetlinkConnected && onSave) {
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
- }
74
+ const saveBtn = createStyledButton({ color, text: 'Save' });
75
+ saveBtn.onclick = onSave;
87
76
  headerButtons.appendChild(saveBtn);
88
77
  }
89
78
  // Close button - use same padding as other buttons for consistent height
@@ -96,39 +85,6 @@ export function createModalHeader(config) {
96
85
  closeBtn.onclick = onClose;
97
86
  headerButtons.appendChild(closeBtn);
98
87
  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
- }
132
88
  return header;
133
89
  }
134
90
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ytspar/devbar",
3
- "version": "1.0.0-canary.2b99e1e",
3
+ "version": "1.0.0-canary.3c85c90",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Development toolbar and utilities with Sweetlink integration - pure vanilla JS, no framework dependencies",