@ytspar/devbar 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/GlobalDevBar.d.ts.map +1 -1
- package/dist/GlobalDevBar.js +2 -0
- package/dist/GlobalDevBar.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +29 -4
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/rendering.d.ts.map +1 -1
- package/dist/modules/rendering.js +236 -145
- package/dist/modules/rendering.js.map +1 -1
- package/dist/modules/screenshot.d.ts.map +1 -1
- package/dist/modules/screenshot.js +113 -53
- package/dist/modules/screenshot.js.map +1 -1
- package/dist/settings.d.ts +15 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +14 -0
- package/dist/settings.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/buttons.d.ts +5 -0
- package/dist/ui/buttons.d.ts.map +1 -1
- package/dist/ui/buttons.js +30 -0
- package/dist/ui/buttons.js.map +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/modals.d.ts +3 -1
- package/dist/ui/modals.d.ts.map +1 -1
- package/dist/ui/modals.js +27 -21
- package/dist/ui/modals.js.map +1 -1
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +17 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
import { BUTTON_COLORS, CATEGORY_COLORS, CSS_COLORS, FONT_MONO, TAILWIND_BREAKPOINTS, } from '../constants.js';
|
|
8
8
|
import { extractDocumentOutline, outlineToMarkdown } from '../outline.js';
|
|
9
9
|
import { extractPageSchema, schemaToMarkdown } from '../schema.js';
|
|
10
|
-
import { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS } from '../settings.js';
|
|
11
|
-
import { createEmptyMessage, createInfoBox, createModalBox, createModalContent, createModalHeader, createModalOverlay, createStyledButton, createSvgIcon, getButtonStyles, } from '../ui/index.js';
|
|
10
|
+
import { ACCENT_COLOR_PRESETS, DEFAULT_SETTINGS, resolveSaveLocation } from '../settings.js';
|
|
11
|
+
import { createEmptyMessage, createInfoBox, createModalBox, createModalContent, createModalHeader, createModalOverlay, createCloseButton, createStyledButton, createSvgIcon, getButtonStyles, } from '../ui/index.js';
|
|
12
12
|
import { getResponsiveMetricVisibility } from './performance.js';
|
|
13
13
|
import { calculateCostEstimate, closeDesignReviewConfirm, copyPathToClipboard, handleDocumentOutline, handlePageSchema, handleSaveConsoleLogs, handleSaveOutline, handleSaveSchema, proceedWithDesignReview, showDesignReviewConfirmation, } from './screenshot.js';
|
|
14
14
|
import { setThemeMode } from './theme.js';
|
|
@@ -25,6 +25,38 @@ function captureDotPosition(state, element) {
|
|
|
25
25
|
bottom: window.innerHeight - (rect.top + rect.height / 2),
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Create the connection indicator (outer wrapper + inner colored dot).
|
|
30
|
+
* The caller is responsible for attaching tooltip and click handlers, since
|
|
31
|
+
* those differ between compact and expanded modes.
|
|
32
|
+
*/
|
|
33
|
+
function createConnectionIndicator(state) {
|
|
34
|
+
const connIndicator = document.createElement('span');
|
|
35
|
+
connIndicator.className = 'devbar-clickable';
|
|
36
|
+
Object.assign(connIndicator.style, {
|
|
37
|
+
width: '12px',
|
|
38
|
+
height: '12px',
|
|
39
|
+
borderRadius: '50%',
|
|
40
|
+
backgroundColor: 'transparent',
|
|
41
|
+
display: 'flex',
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
cursor: 'pointer',
|
|
45
|
+
flexShrink: '0',
|
|
46
|
+
});
|
|
47
|
+
const connDot = document.createElement('span');
|
|
48
|
+
connDot.className = 'devbar-conn-dot';
|
|
49
|
+
Object.assign(connDot.style, {
|
|
50
|
+
width: '6px',
|
|
51
|
+
height: '6px',
|
|
52
|
+
borderRadius: '50%',
|
|
53
|
+
backgroundColor: state.sweetlinkConnected ? CSS_COLORS.primary : CSS_COLORS.textMuted,
|
|
54
|
+
boxShadow: state.sweetlinkConnected ? `0 0 6px ${CSS_COLORS.primary}` : 'none',
|
|
55
|
+
transition: 'all 300ms',
|
|
56
|
+
});
|
|
57
|
+
connIndicator.appendChild(connDot);
|
|
58
|
+
return connIndicator;
|
|
59
|
+
}
|
|
28
60
|
/**
|
|
29
61
|
* Main render dispatch - creates container and delegates to appropriate renderer.
|
|
30
62
|
*/
|
|
@@ -66,24 +98,30 @@ function renderOverlays(state, consoleCaptureSingleton) {
|
|
|
66
98
|
state.overlayElement.remove();
|
|
67
99
|
state.overlayElement = null;
|
|
68
100
|
}
|
|
69
|
-
//
|
|
101
|
+
// Safety: only one overlay at a time. First match wins; close the rest.
|
|
70
102
|
if (state.consoleFilter) {
|
|
103
|
+
state.showOutlineModal = false;
|
|
104
|
+
state.showSchemaModal = false;
|
|
105
|
+
state.showDesignReviewConfirm = false;
|
|
106
|
+
state.showSettingsPopover = false;
|
|
71
107
|
renderConsolePopup(state, consoleCaptureSingleton);
|
|
72
108
|
}
|
|
73
|
-
|
|
74
|
-
|
|
109
|
+
else if (state.showOutlineModal) {
|
|
110
|
+
state.showSchemaModal = false;
|
|
111
|
+
state.showDesignReviewConfirm = false;
|
|
112
|
+
state.showSettingsPopover = false;
|
|
75
113
|
renderOutlineModal(state);
|
|
76
114
|
}
|
|
77
|
-
|
|
78
|
-
|
|
115
|
+
else if (state.showSchemaModal) {
|
|
116
|
+
state.showDesignReviewConfirm = false;
|
|
117
|
+
state.showSettingsPopover = false;
|
|
79
118
|
renderSchemaModal(state);
|
|
80
119
|
}
|
|
81
|
-
|
|
82
|
-
|
|
120
|
+
else if (state.showDesignReviewConfirm) {
|
|
121
|
+
state.showSettingsPopover = false;
|
|
83
122
|
renderDesignReviewConfirmModal(state);
|
|
84
123
|
}
|
|
85
|
-
|
|
86
|
-
if (state.showSettingsPopover) {
|
|
124
|
+
else if (state.showSettingsPopover) {
|
|
87
125
|
renderSettingsPopover(state);
|
|
88
126
|
}
|
|
89
127
|
}
|
|
@@ -107,6 +145,8 @@ function renderCollapsed(state) {
|
|
|
107
145
|
bottom: `${state.lastDotPosition.bottom - 13}px`,
|
|
108
146
|
left: `${state.lastDotPosition.left - 13}px`,
|
|
109
147
|
};
|
|
148
|
+
// Clear after use so expand doesn't re-use stale values
|
|
149
|
+
state.lastDotPosition = null;
|
|
110
150
|
}
|
|
111
151
|
else {
|
|
112
152
|
// Fallback preset positions for when no dot position was captured
|
|
@@ -244,17 +284,8 @@ function renderCompact(state) {
|
|
|
244
284
|
fontSize: '0.6875rem',
|
|
245
285
|
});
|
|
246
286
|
// Connection indicator
|
|
247
|
-
const connIndicator =
|
|
248
|
-
|
|
249
|
-
Object.assign(connIndicator.style, {
|
|
250
|
-
width: '12px',
|
|
251
|
-
height: '12px',
|
|
252
|
-
borderRadius: '50%',
|
|
253
|
-
display: 'flex',
|
|
254
|
-
alignItems: 'center',
|
|
255
|
-
justifyContent: 'center',
|
|
256
|
-
cursor: 'pointer',
|
|
257
|
-
});
|
|
287
|
+
const connIndicator = createConnectionIndicator(state);
|
|
288
|
+
const connDot = connIndicator.querySelector('.devbar-conn-dot');
|
|
258
289
|
attachTextTooltip(state, connIndicator, () => state.sweetlinkConnected ? 'Sweetlink connected' : 'Sweetlink disconnected');
|
|
259
290
|
connIndicator.onclick = (e) => {
|
|
260
291
|
e.stopPropagation();
|
|
@@ -263,15 +294,6 @@ function renderCompact(state) {
|
|
|
263
294
|
state.debug.state('Collapsed DevBar from compact mode');
|
|
264
295
|
state.render();
|
|
265
296
|
};
|
|
266
|
-
const connDot = document.createElement('span');
|
|
267
|
-
Object.assign(connDot.style, {
|
|
268
|
-
width: '6px',
|
|
269
|
-
height: '6px',
|
|
270
|
-
borderRadius: '50%',
|
|
271
|
-
backgroundColor: state.sweetlinkConnected ? CSS_COLORS.primary : CSS_COLORS.textMuted,
|
|
272
|
-
boxShadow: state.sweetlinkConnected ? `0 0 6px ${CSS_COLORS.primary}` : 'none',
|
|
273
|
-
});
|
|
274
|
-
connIndicator.appendChild(connDot);
|
|
275
297
|
wrapper.appendChild(connIndicator);
|
|
276
298
|
// Error badge
|
|
277
299
|
if (errorCount > 0) {
|
|
@@ -422,19 +444,7 @@ function renderExpanded(state, customControls) {
|
|
|
422
444
|
lineHeight: '1rem',
|
|
423
445
|
});
|
|
424
446
|
// Connection indicator (click to collapse)
|
|
425
|
-
const connIndicator =
|
|
426
|
-
connIndicator.className = 'devbar-clickable';
|
|
427
|
-
Object.assign(connIndicator.style, {
|
|
428
|
-
width: '12px',
|
|
429
|
-
height: '12px',
|
|
430
|
-
borderRadius: '50%',
|
|
431
|
-
backgroundColor: 'transparent',
|
|
432
|
-
display: 'flex',
|
|
433
|
-
alignItems: 'center',
|
|
434
|
-
justifyContent: 'center',
|
|
435
|
-
cursor: 'pointer',
|
|
436
|
-
flexShrink: '0',
|
|
437
|
-
});
|
|
447
|
+
const connIndicator = createConnectionIndicator(state);
|
|
438
448
|
attachTextTooltip(state, connIndicator, () => state.sweetlinkConnected
|
|
439
449
|
? 'Sweetlink connected (click to minimize)'
|
|
440
450
|
: 'Sweetlink disconnected (click to minimize)');
|
|
@@ -445,16 +455,6 @@ function renderExpanded(state, customControls) {
|
|
|
445
455
|
state.debug.state('Collapsed DevBar (connection dot click)');
|
|
446
456
|
state.render();
|
|
447
457
|
};
|
|
448
|
-
const connDot = document.createElement('span');
|
|
449
|
-
Object.assign(connDot.style, {
|
|
450
|
-
width: '6px',
|
|
451
|
-
height: '6px',
|
|
452
|
-
borderRadius: '50%',
|
|
453
|
-
backgroundColor: state.sweetlinkConnected ? CSS_COLORS.primary : CSS_COLORS.textMuted,
|
|
454
|
-
boxShadow: state.sweetlinkConnected ? `0 0 6px ${CSS_COLORS.primary}` : 'none',
|
|
455
|
-
transition: 'all 300ms',
|
|
456
|
-
});
|
|
457
|
-
connIndicator.appendChild(connDot);
|
|
458
458
|
// Status row wrapper - keeps connection dot, info, and badges together
|
|
459
459
|
const statusRow = document.createElement('div');
|
|
460
460
|
statusRow.className = 'devbar-status';
|
|
@@ -719,6 +719,7 @@ function createConsoleBadge(state, type, count, color) {
|
|
|
719
719
|
state.consoleFilter = state.consoleFilter === type ? null : type;
|
|
720
720
|
state.showOutlineModal = false;
|
|
721
721
|
state.showSchemaModal = false;
|
|
722
|
+
state.showSettingsPopover = false;
|
|
722
723
|
state.render();
|
|
723
724
|
};
|
|
724
725
|
return badge;
|
|
@@ -728,8 +729,9 @@ function createScreenshotButton(state, accentColor) {
|
|
|
728
729
|
btn.type = 'button';
|
|
729
730
|
const hasSuccessState = state.copiedToClipboard || state.copiedPath || state.lastScreenshot;
|
|
730
731
|
const isDisabled = state.capturing;
|
|
731
|
-
|
|
732
|
-
|
|
732
|
+
const effectiveSave = resolveSaveLocation(state.options.saveLocation, state.sweetlinkConnected);
|
|
733
|
+
// Grey out only when effective save is 'local' but sweetlink not connected (explicit 'local' setting)
|
|
734
|
+
const isGreyedOut = effectiveSave === 'local' && !state.sweetlinkConnected && !hasSuccessState;
|
|
733
735
|
// Attach HTML tooltip
|
|
734
736
|
attachButtonTooltip(state, btn, accentColor, (tooltip, h) => {
|
|
735
737
|
if (state.copiedToClipboard) {
|
|
@@ -742,51 +744,57 @@ function createScreenshotButton(state, accentColor) {
|
|
|
742
744
|
}
|
|
743
745
|
if (state.lastScreenshot) {
|
|
744
746
|
const screenshotPath = state.lastScreenshot;
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
copyLink.
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
747
|
+
const isDownloaded = screenshotPath.endsWith('downloaded');
|
|
748
|
+
if (isDownloaded) {
|
|
749
|
+
h.addSuccess('Screenshot downloaded!');
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
h.addSuccess('Screenshot saved!', screenshotPath);
|
|
753
|
+
const copyLink = document.createElement('div');
|
|
754
|
+
Object.assign(copyLink.style, {
|
|
755
|
+
color: accentColor,
|
|
756
|
+
cursor: 'pointer',
|
|
757
|
+
fontSize: '0.625rem',
|
|
758
|
+
marginTop: '6px',
|
|
759
|
+
opacity: '0.8',
|
|
760
|
+
transition: 'opacity 150ms',
|
|
761
|
+
});
|
|
762
|
+
copyLink.textContent = 'copy path';
|
|
763
|
+
copyLink.onmouseenter = () => {
|
|
764
|
+
copyLink.style.opacity = '1';
|
|
765
|
+
};
|
|
766
|
+
copyLink.onmouseleave = () => {
|
|
767
|
+
copyLink.style.opacity = '0.8';
|
|
768
|
+
};
|
|
769
|
+
copyLink.onclick = async (e) => {
|
|
770
|
+
e.stopPropagation();
|
|
771
|
+
try {
|
|
772
|
+
await navigator.clipboard.writeText(screenshotPath);
|
|
773
|
+
copyLink.textContent = '\u2713 copied!';
|
|
774
|
+
copyLink.style.cursor = 'default';
|
|
775
|
+
copyLink.onclick = null;
|
|
776
|
+
}
|
|
777
|
+
catch {
|
|
778
|
+
copyLink.textContent = '\u00d7 failed to copy';
|
|
779
|
+
copyLink.style.color = CSS_COLORS.error;
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
tooltip.appendChild(copyLink);
|
|
783
|
+
}
|
|
776
784
|
return;
|
|
777
785
|
}
|
|
778
786
|
h.addTitle('Screenshot');
|
|
779
|
-
|
|
780
|
-
|
|
787
|
+
h.addSectionHeader('Actions');
|
|
788
|
+
if (effectiveSave === 'local' && !state.sweetlinkConnected) {
|
|
781
789
|
h.addShortcut('Shift+Click', 'Copy to clipboard');
|
|
782
|
-
h.addWarning('Sweetlink not connected.
|
|
790
|
+
h.addWarning('Sweetlink not connected. Switch save method to Auto or Download.');
|
|
783
791
|
}
|
|
784
792
|
else {
|
|
785
|
-
|
|
786
|
-
h.addShortcut('Click',
|
|
793
|
+
const saveLabel = effectiveSave === 'local' ? 'Save to file' : 'Download';
|
|
794
|
+
h.addShortcut('Click', saveLabel);
|
|
787
795
|
h.addShortcut('Shift+Click', 'Copy to clipboard');
|
|
788
796
|
h.addSectionHeader('Keyboard');
|
|
789
|
-
h.addShortcut('Cmd or Ctrl+Shift+S',
|
|
797
|
+
h.addShortcut('Cmd or Ctrl+Shift+S', saveLabel);
|
|
790
798
|
h.addShortcut('Cmd or Ctrl+Shift+C', 'Copy');
|
|
791
799
|
}
|
|
792
800
|
});
|
|
@@ -914,13 +922,14 @@ function createOutlineButton(state) {
|
|
|
914
922
|
// Attach HTML tooltip
|
|
915
923
|
attachButtonTooltip(state, btn, BUTTON_COLORS.outline, (_tooltip, h) => {
|
|
916
924
|
if (state.lastOutline) {
|
|
917
|
-
|
|
925
|
+
const isDownloaded = state.lastOutline.endsWith('downloaded');
|
|
926
|
+
h.addSuccess(isDownloaded ? 'Outline downloaded!' : 'Outline saved!', isDownloaded ? undefined : state.lastOutline);
|
|
918
927
|
return;
|
|
919
928
|
}
|
|
920
929
|
h.addTitle('Document Outline');
|
|
921
930
|
h.addDescription('View page heading structure and save as markdown.');
|
|
922
|
-
if (!state.sweetlinkConnected) {
|
|
923
|
-
h.addWarning('Sweetlink not connected.
|
|
931
|
+
if (state.options.saveLocation === 'local' && !state.sweetlinkConnected) {
|
|
932
|
+
h.addWarning('Sweetlink not connected. Switch save method to Auto or Download.');
|
|
924
933
|
}
|
|
925
934
|
});
|
|
926
935
|
Object.assign(btn.style, getButtonStyles(BUTTON_COLORS.outline, isActive, false));
|
|
@@ -941,13 +950,14 @@ function createSchemaButton(state) {
|
|
|
941
950
|
// Attach HTML tooltip
|
|
942
951
|
attachButtonTooltip(state, btn, BUTTON_COLORS.schema, (_tooltip, h) => {
|
|
943
952
|
if (state.lastSchema) {
|
|
944
|
-
|
|
953
|
+
const isDownloaded = state.lastSchema.endsWith('downloaded');
|
|
954
|
+
h.addSuccess(isDownloaded ? 'Schema downloaded!' : 'Schema saved!', isDownloaded ? undefined : state.lastSchema);
|
|
945
955
|
return;
|
|
946
956
|
}
|
|
947
957
|
h.addTitle('Page Schema');
|
|
948
958
|
h.addDescription('View JSON-LD, Open Graph, and other structured data.');
|
|
949
|
-
if (!state.sweetlinkConnected) {
|
|
950
|
-
h.addWarning('Sweetlink not connected.
|
|
959
|
+
if (state.options.saveLocation === 'local' && !state.sweetlinkConnected) {
|
|
960
|
+
h.addWarning('Sweetlink not connected. Switch save method to Auto or Download.');
|
|
951
961
|
}
|
|
952
962
|
});
|
|
953
963
|
Object.assign(btn.style, getButtonStyles(BUTTON_COLORS.schema, isActive, false));
|
|
@@ -1111,6 +1121,7 @@ function renderConsolePopup(state, consoleCaptureSingleton) {
|
|
|
1111
1121
|
},
|
|
1112
1122
|
onSave: () => handleSaveConsoleLogs(state, logs),
|
|
1113
1123
|
sweetlinkConnected: state.sweetlinkConnected,
|
|
1124
|
+
saveLocation: state.options.saveLocation,
|
|
1114
1125
|
isSaving: state.savingConsoleLogs,
|
|
1115
1126
|
savedPath: state.lastConsoleLogs,
|
|
1116
1127
|
});
|
|
@@ -1176,6 +1187,7 @@ function renderOutlineModal(state) {
|
|
|
1176
1187
|
},
|
|
1177
1188
|
onSave: () => handleSaveOutline(state),
|
|
1178
1189
|
sweetlinkConnected: state.sweetlinkConnected,
|
|
1190
|
+
saveLocation: state.options.saveLocation,
|
|
1179
1191
|
isSaving: state.savingOutline,
|
|
1180
1192
|
savedPath: state.lastOutline,
|
|
1181
1193
|
});
|
|
@@ -1261,6 +1273,7 @@ function renderSchemaModal(state) {
|
|
|
1261
1273
|
},
|
|
1262
1274
|
onSave: () => handleSaveSchema(state),
|
|
1263
1275
|
sweetlinkConnected: state.sweetlinkConnected,
|
|
1276
|
+
saveLocation: state.options.saveLocation,
|
|
1264
1277
|
isSaving: state.savingSchema,
|
|
1265
1278
|
savedPath: state.lastSchema,
|
|
1266
1279
|
});
|
|
@@ -1457,15 +1470,7 @@ function renderDesignReviewConfirmModal(state) {
|
|
|
1457
1470
|
Object.assign(title.style, { color, fontSize: '0.875rem', fontWeight: '600' });
|
|
1458
1471
|
title.textContent = 'AI Design Review';
|
|
1459
1472
|
header.appendChild(title);
|
|
1460
|
-
|
|
1461
|
-
color: CSS_COLORS.textMuted,
|
|
1462
|
-
text: '\u00D7',
|
|
1463
|
-
padding: '0',
|
|
1464
|
-
fontSize: '1.25rem',
|
|
1465
|
-
});
|
|
1466
|
-
closeBtn.style.border = 'none';
|
|
1467
|
-
closeBtn.onclick = closeModal;
|
|
1468
|
-
header.appendChild(closeBtn);
|
|
1473
|
+
header.appendChild(createCloseButton(closeModal));
|
|
1469
1474
|
modal.appendChild(header);
|
|
1470
1475
|
// Content
|
|
1471
1476
|
const content = document.createElement('div');
|
|
@@ -1494,13 +1499,7 @@ function renderDesignReviewConfirmModal(state) {
|
|
|
1494
1499
|
padding: '14px 18px',
|
|
1495
1500
|
borderTop: `1px solid ${CSS_COLORS.border}`,
|
|
1496
1501
|
});
|
|
1497
|
-
|
|
1498
|
-
color: CSS_COLORS.textMuted,
|
|
1499
|
-
text: 'Cancel',
|
|
1500
|
-
padding: '8px 16px',
|
|
1501
|
-
});
|
|
1502
|
-
cancelBtn.onclick = closeModal;
|
|
1503
|
-
footer.appendChild(cancelBtn);
|
|
1502
|
+
footer.appendChild(createCloseButton(closeModal, 'Cancel'));
|
|
1504
1503
|
if (state.apiKeyStatus?.configured) {
|
|
1505
1504
|
const proceedBtn = createStyledButton({ color, text: 'Run Review', padding: '8px 16px' });
|
|
1506
1505
|
proceedBtn.style.backgroundColor = `${color}20`;
|
|
@@ -1509,6 +1508,7 @@ function renderDesignReviewConfirmModal(state) {
|
|
|
1509
1508
|
}
|
|
1510
1509
|
modal.appendChild(footer);
|
|
1511
1510
|
overlay.appendChild(modal);
|
|
1511
|
+
state.overlayElement = overlay;
|
|
1512
1512
|
document.body.appendChild(overlay);
|
|
1513
1513
|
}
|
|
1514
1514
|
function renderApiKeyNotConfiguredContent() {
|
|
@@ -1594,16 +1594,26 @@ function renderApiKeyConfiguredContent(state) {
|
|
|
1594
1594
|
// ============================================================================
|
|
1595
1595
|
function renderSettingsPopover(state) {
|
|
1596
1596
|
const { position, accentColor } = state.options;
|
|
1597
|
-
const color = CSS_COLORS.textSecondary;
|
|
1598
1597
|
const popover = document.createElement('div');
|
|
1599
1598
|
popover.setAttribute('data-devbar', 'true');
|
|
1600
|
-
|
|
1599
|
+
popover.setAttribute('data-devbar-overlay', 'true');
|
|
1600
|
+
// Position: centered over the devbar on desktop, centered on screen on mobile
|
|
1601
1601
|
const isTop = position.startsWith('top');
|
|
1602
|
-
const
|
|
1602
|
+
const popoverWidth = 480;
|
|
1603
|
+
const edgePad = 16;
|
|
1604
|
+
let leftPx;
|
|
1605
|
+
if (state.container && window.innerWidth > 640) {
|
|
1606
|
+
const barRect = state.container.getBoundingClientRect();
|
|
1607
|
+
const barCenter = barRect.left + barRect.width / 2;
|
|
1608
|
+
leftPx = Math.max(edgePad, Math.min(barCenter - popoverWidth / 2, window.innerWidth - popoverWidth - edgePad));
|
|
1609
|
+
}
|
|
1610
|
+
else {
|
|
1611
|
+
leftPx = Math.max(edgePad, (window.innerWidth - popoverWidth) / 2);
|
|
1612
|
+
}
|
|
1603
1613
|
Object.assign(popover.style, {
|
|
1604
1614
|
position: 'fixed',
|
|
1605
1615
|
[isTop ? 'top' : 'bottom']: '70px',
|
|
1606
|
-
|
|
1616
|
+
left: `${leftPx}px`,
|
|
1607
1617
|
zIndex: '10003',
|
|
1608
1618
|
backgroundColor: 'var(--devbar-color-bg-elevated)',
|
|
1609
1619
|
border: `1px solid ${accentColor}`,
|
|
@@ -1611,13 +1621,42 @@ function renderSettingsPopover(state) {
|
|
|
1611
1621
|
boxShadow: `0 8px 32px rgba(0, 0, 0, 0.5), 0 0 0 1px ${accentColor}33`,
|
|
1612
1622
|
backdropFilter: 'blur(8px)',
|
|
1613
1623
|
WebkitBackdropFilter: 'blur(8px)',
|
|
1614
|
-
|
|
1615
|
-
maxWidth: '
|
|
1624
|
+
width: `${popoverWidth}px`,
|
|
1625
|
+
maxWidth: 'calc(100vw - 32px)',
|
|
1616
1626
|
maxHeight: 'calc(100vh - 100px)',
|
|
1617
1627
|
overflowY: 'auto',
|
|
1618
1628
|
fontFamily: FONT_MONO,
|
|
1619
1629
|
});
|
|
1620
|
-
|
|
1630
|
+
popover.appendChild(createSettingsHeader(state));
|
|
1631
|
+
// Two-column grid for settings sections (collapses to 1 column on mobile via CSS)
|
|
1632
|
+
const grid = document.createElement('div');
|
|
1633
|
+
grid.className = 'devbar-settings-grid';
|
|
1634
|
+
Object.assign(grid.style, {
|
|
1635
|
+
display: 'grid',
|
|
1636
|
+
gridTemplateColumns: '1fr 1fr',
|
|
1637
|
+
});
|
|
1638
|
+
// Left column: Theme + Display
|
|
1639
|
+
const color = CSS_COLORS.textSecondary;
|
|
1640
|
+
const leftCol = document.createElement('div');
|
|
1641
|
+
Object.assign(leftCol.style, { borderRight: `1px solid ${color}20` });
|
|
1642
|
+
leftCol.appendChild(createThemeSection(state));
|
|
1643
|
+
leftCol.appendChild(createDisplaySection(state));
|
|
1644
|
+
grid.appendChild(leftCol);
|
|
1645
|
+
// Right column: Features + Metrics
|
|
1646
|
+
const rightCol = document.createElement('div');
|
|
1647
|
+
rightCol.appendChild(createFeaturesSection(state));
|
|
1648
|
+
rightCol.appendChild(createMetricsSection(state));
|
|
1649
|
+
grid.appendChild(rightCol);
|
|
1650
|
+
popover.appendChild(grid);
|
|
1651
|
+
popover.appendChild(createResetSection(state));
|
|
1652
|
+
state.overlayElement = popover;
|
|
1653
|
+
document.body.appendChild(popover);
|
|
1654
|
+
}
|
|
1655
|
+
// ============================================================================
|
|
1656
|
+
// Settings Popover Section Builders
|
|
1657
|
+
// ============================================================================
|
|
1658
|
+
function createSettingsHeader(state) {
|
|
1659
|
+
const { accentColor } = state.options;
|
|
1621
1660
|
const header = document.createElement('div');
|
|
1622
1661
|
Object.assign(header.style, {
|
|
1623
1662
|
display: 'flex',
|
|
@@ -1634,20 +1673,15 @@ function renderSettingsPopover(state) {
|
|
|
1634
1673
|
Object.assign(title.style, { color: accentColor, fontSize: '0.75rem', fontWeight: '600' });
|
|
1635
1674
|
title.textContent = 'Settings';
|
|
1636
1675
|
header.appendChild(title);
|
|
1637
|
-
|
|
1638
|
-
color: CSS_COLORS.textMuted,
|
|
1639
|
-
text: '\u00D7',
|
|
1640
|
-
padding: '2px 6px',
|
|
1641
|
-
fontSize: '0.875rem',
|
|
1642
|
-
});
|
|
1643
|
-
closeBtn.style.border = 'none';
|
|
1644
|
-
closeBtn.onclick = () => {
|
|
1676
|
+
header.appendChild(createCloseButton(() => {
|
|
1645
1677
|
state.showSettingsPopover = false;
|
|
1646
1678
|
state.render();
|
|
1647
|
-
};
|
|
1648
|
-
header
|
|
1649
|
-
|
|
1650
|
-
|
|
1679
|
+
}));
|
|
1680
|
+
return header;
|
|
1681
|
+
}
|
|
1682
|
+
function createThemeSection(state) {
|
|
1683
|
+
const { accentColor } = state.options;
|
|
1684
|
+
const color = CSS_COLORS.textSecondary;
|
|
1651
1685
|
const themeSection = createSettingsSection('Theme');
|
|
1652
1686
|
const themeOptions = document.createElement('div');
|
|
1653
1687
|
Object.assign(themeOptions.style, { display: 'flex', gap: '6px' });
|
|
@@ -1673,8 +1707,11 @@ function renderSettingsPopover(state) {
|
|
|
1673
1707
|
themeOptions.appendChild(btn);
|
|
1674
1708
|
});
|
|
1675
1709
|
themeSection.appendChild(themeOptions);
|
|
1676
|
-
|
|
1677
|
-
|
|
1710
|
+
return themeSection;
|
|
1711
|
+
}
|
|
1712
|
+
function createDisplaySection(state) {
|
|
1713
|
+
const { accentColor } = state.options;
|
|
1714
|
+
const color = CSS_COLORS.textSecondary;
|
|
1678
1715
|
const displaySection = createSettingsSection('Display');
|
|
1679
1716
|
// Position mini-map selector
|
|
1680
1717
|
const positionRow = document.createElement('div');
|
|
@@ -1697,6 +1734,7 @@ function renderSettingsPopover(state) {
|
|
|
1697
1734
|
border: `1px solid ${color}30`,
|
|
1698
1735
|
borderRadius: '4px',
|
|
1699
1736
|
});
|
|
1737
|
+
// Position indicator styles - rectangular bars representing DevBar
|
|
1700
1738
|
const positionConfigs = [
|
|
1701
1739
|
{ value: 'top-left', style: { top: '6px', left: '6px' }, title: 'Top Left' },
|
|
1702
1740
|
{ value: 'top-right', style: { top: '6px', right: '6px' }, title: 'Top Right' },
|
|
@@ -1807,8 +1845,11 @@ function renderSettingsPopover(state) {
|
|
|
1807
1845
|
});
|
|
1808
1846
|
accentRow.appendChild(colorSwatches);
|
|
1809
1847
|
displaySection.appendChild(accentRow);
|
|
1810
|
-
|
|
1811
|
-
|
|
1848
|
+
return displaySection;
|
|
1849
|
+
}
|
|
1850
|
+
function createFeaturesSection(state) {
|
|
1851
|
+
const { accentColor } = state.options;
|
|
1852
|
+
const color = CSS_COLORS.textSecondary;
|
|
1812
1853
|
const featuresSection = createSettingsSection('Features');
|
|
1813
1854
|
featuresSection.appendChild(createToggleRow('Screenshot Button', state.options.showScreenshot, accentColor, () => {
|
|
1814
1855
|
state.options.showScreenshot = !state.options.showScreenshot;
|
|
@@ -1825,8 +1866,58 @@ function renderSettingsPopover(state) {
|
|
|
1825
1866
|
state.settingsManager.saveSettings({ showTooltips: state.options.showTooltips });
|
|
1826
1867
|
state.render();
|
|
1827
1868
|
}));
|
|
1828
|
-
|
|
1829
|
-
|
|
1869
|
+
// Save location selector
|
|
1870
|
+
const saveLocRow = document.createElement('div');
|
|
1871
|
+
Object.assign(saveLocRow.style, { marginBottom: '6px' });
|
|
1872
|
+
const saveLocLabel = document.createElement('div');
|
|
1873
|
+
Object.assign(saveLocLabel.style, {
|
|
1874
|
+
color: CSS_COLORS.text,
|
|
1875
|
+
fontSize: '0.6875rem',
|
|
1876
|
+
marginBottom: '6px',
|
|
1877
|
+
});
|
|
1878
|
+
saveLocLabel.textContent = 'Save Method';
|
|
1879
|
+
saveLocRow.appendChild(saveLocLabel);
|
|
1880
|
+
const saveLocOptions = document.createElement('div');
|
|
1881
|
+
Object.assign(saveLocOptions.style, { display: 'flex', gap: '6px' });
|
|
1882
|
+
const saveLocChoices = [
|
|
1883
|
+
{ value: 'auto', label: 'Auto' },
|
|
1884
|
+
{ value: 'download', label: 'Download' },
|
|
1885
|
+
{ value: 'local', label: 'Local' },
|
|
1886
|
+
];
|
|
1887
|
+
saveLocChoices.forEach(({ value, label }) => {
|
|
1888
|
+
const btn = document.createElement('button');
|
|
1889
|
+
const isActive = state.options.saveLocation === value;
|
|
1890
|
+
const isLocalDisabled = value === 'local' && !state.sweetlinkConnected;
|
|
1891
|
+
Object.assign(btn.style, {
|
|
1892
|
+
padding: '4px 10px',
|
|
1893
|
+
backgroundColor: isActive ? `${accentColor}20` : 'transparent',
|
|
1894
|
+
border: `1px solid ${isActive ? accentColor : `${color}40`}`,
|
|
1895
|
+
borderRadius: '4px',
|
|
1896
|
+
color: isActive ? accentColor : color,
|
|
1897
|
+
fontSize: '0.625rem',
|
|
1898
|
+
cursor: isLocalDisabled ? 'not-allowed' : 'pointer',
|
|
1899
|
+
transition: 'all 150ms',
|
|
1900
|
+
opacity: isLocalDisabled ? '0.5' : '1',
|
|
1901
|
+
});
|
|
1902
|
+
btn.textContent = label;
|
|
1903
|
+
if (isLocalDisabled) {
|
|
1904
|
+
btn.title = 'Sweetlink not connected';
|
|
1905
|
+
}
|
|
1906
|
+
btn.onclick = () => {
|
|
1907
|
+
if (isLocalDisabled)
|
|
1908
|
+
return;
|
|
1909
|
+
state.options.saveLocation = value;
|
|
1910
|
+
state.settingsManager.saveSettings({ saveLocation: value });
|
|
1911
|
+
state.render();
|
|
1912
|
+
};
|
|
1913
|
+
saveLocOptions.appendChild(btn);
|
|
1914
|
+
});
|
|
1915
|
+
saveLocRow.appendChild(saveLocOptions);
|
|
1916
|
+
featuresSection.appendChild(saveLocRow);
|
|
1917
|
+
return featuresSection;
|
|
1918
|
+
}
|
|
1919
|
+
function createMetricsSection(state) {
|
|
1920
|
+
const { accentColor } = state.options;
|
|
1830
1921
|
const metricsSection = createSettingsSection('Metrics');
|
|
1831
1922
|
const metricsToggles = [
|
|
1832
1923
|
{ key: 'breakpoint', label: 'Breakpoint' },
|
|
@@ -1853,8 +1944,10 @@ function renderSettingsPopover(state) {
|
|
|
1853
1944
|
state.render();
|
|
1854
1945
|
}));
|
|
1855
1946
|
});
|
|
1856
|
-
|
|
1857
|
-
|
|
1947
|
+
return metricsSection;
|
|
1948
|
+
}
|
|
1949
|
+
function createResetSection(state) {
|
|
1950
|
+
const color = CSS_COLORS.textSecondary;
|
|
1858
1951
|
const resetSection = document.createElement('div');
|
|
1859
1952
|
Object.assign(resetSection.style, {
|
|
1860
1953
|
padding: '10px 14px',
|
|
@@ -1876,9 +1969,7 @@ function renderSettingsPopover(state) {
|
|
|
1876
1969
|
state.applySettings(defaults);
|
|
1877
1970
|
};
|
|
1878
1971
|
resetSection.appendChild(resetBtn);
|
|
1879
|
-
|
|
1880
|
-
state.overlayElement = popover;
|
|
1881
|
-
document.body.appendChild(popover);
|
|
1972
|
+
return resetSection;
|
|
1882
1973
|
}
|
|
1883
1974
|
// ============================================================================
|
|
1884
1975
|
// Settings UI Helpers
|