@ytspar/devbar 1.3.1 → 1.4.1
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/README.md +43 -0
- package/dist/accessibility.d.ts +4 -0
- package/dist/accessibility.d.ts.map +1 -1
- package/dist/accessibility.js +57 -0
- package/dist/accessibility.js.map +1 -1
- package/dist/constants.d.ts +8 -23
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -3
- 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/index.d.ts +1 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +1 -0
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/keyboard.d.ts +1 -1
- package/dist/modules/keyboard.d.ts.map +1 -1
- package/dist/modules/keyboard.js +4 -11
- package/dist/modules/keyboard.js.map +1 -1
- package/dist/modules/rendering/buttons.d.ts +19 -0
- package/dist/modules/rendering/buttons.d.ts.map +1 -0
- package/dist/modules/rendering/buttons.js +369 -0
- package/dist/modules/rendering/buttons.js.map +1 -0
- package/dist/modules/rendering/collapsed.d.ts +6 -0
- package/dist/modules/rendering/collapsed.d.ts.map +1 -0
- package/dist/modules/rendering/collapsed.js +124 -0
- package/dist/modules/rendering/collapsed.js.map +1 -0
- package/dist/modules/rendering/common.d.ts +21 -0
- package/dist/modules/rendering/common.d.ts.map +1 -0
- package/dist/modules/rendering/common.js +60 -0
- package/dist/modules/rendering/common.js.map +1 -0
- package/dist/modules/rendering/compact.d.ts +6 -0
- package/dist/modules/rendering/compact.d.ts.map +1 -0
- package/dist/modules/rendering/compact.js +107 -0
- package/dist/modules/rendering/compact.js.map +1 -0
- package/dist/modules/rendering/console.d.ts +7 -0
- package/dist/modules/rendering/console.d.ts.map +1 -0
- package/dist/modules/rendering/console.js +78 -0
- package/dist/modules/rendering/console.js.map +1 -0
- package/dist/modules/rendering/expanded.d.ts +13 -0
- package/dist/modules/rendering/expanded.d.ts.map +1 -0
- package/dist/modules/rendering/expanded.js +439 -0
- package/dist/modules/rendering/expanded.js.map +1 -0
- package/dist/modules/rendering/index.d.ts +22 -0
- package/dist/modules/rendering/index.d.ts.map +1 -0
- package/dist/modules/rendering/index.js +109 -0
- package/dist/modules/rendering/index.js.map +1 -0
- package/dist/modules/rendering/modals.d.ts +9 -0
- package/dist/modules/rendering/modals.d.ts.map +1 -0
- package/dist/modules/rendering/modals.js +1068 -0
- package/dist/modules/rendering/modals.js.map +1 -0
- package/dist/modules/rendering/settings.d.ts +6 -0
- package/dist/modules/rendering/settings.d.ts.map +1 -0
- package/dist/modules/rendering/settings.js +605 -0
- package/dist/modules/rendering/settings.js.map +1 -0
- package/dist/modules/rendering.d.ts +15 -16
- package/dist/modules/rendering.d.ts.map +1 -1
- package/dist/modules/rendering.js +15 -2919
- package/dist/modules/rendering.js.map +1 -1
- package/dist/modules/screenshot.d.ts +11 -2
- package/dist/modules/screenshot.d.ts.map +1 -1
- package/dist/modules/screenshot.js +32 -29
- package/dist/modules/screenshot.js.map +1 -1
- package/dist/modules/tooltips.d.ts +7 -5
- package/dist/modules/tooltips.d.ts.map +1 -1
- package/dist/modules/tooltips.js +133 -157
- package/dist/modules/tooltips.js.map +1 -1
- package/dist/modules/types.d.ts +7 -0
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/modules/types.js +14 -1
- package/dist/modules/types.js.map +1 -1
- package/dist/modules/websocket.d.ts.map +1 -1
- package/dist/modules/websocket.js +334 -264
- package/dist/modules/websocket.js.map +1 -1
- package/dist/ui/buttons.d.ts.map +1 -1
- package/dist/ui/buttons.js +11 -9
- package/dist/ui/buttons.js.map +1 -1
- package/dist/ui/cards.js +3 -3
- package/dist/ui/cards.js.map +1 -1
- package/dist/ui/icons.d.ts +13 -0
- package/dist/ui/icons.d.ts.map +1 -1
- package/dist/ui/icons.js +24 -3
- package/dist/ui/icons.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.map +1 -1
- package/dist/ui/modals.d.ts +3 -2
- package/dist/ui/modals.d.ts.map +1 -1
- package/dist/ui/modals.js +28 -26
- package/dist/ui/modals.js.map +1 -1
- package/package.json +3 -4
|
@@ -100,6 +100,304 @@ export function connectWebSocket(state, port) {
|
|
|
100
100
|
state.debug.ws('WebSocket error');
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Per-command handler functions (private, called from handleSweetlinkCommand)
|
|
105
|
+
// ============================================================================
|
|
106
|
+
async function handleScreenshotCommand(ws, command) {
|
|
107
|
+
const targetElement = command.selector
|
|
108
|
+
? document.querySelector(command.selector) || document.body
|
|
109
|
+
: document.body;
|
|
110
|
+
const html2canvas = await getHtml2Canvas();
|
|
111
|
+
const canvas = await html2canvas(targetElement, {
|
|
112
|
+
logging: false,
|
|
113
|
+
useCORS: true,
|
|
114
|
+
allowTaint: true,
|
|
115
|
+
});
|
|
116
|
+
ws.send(JSON.stringify({
|
|
117
|
+
success: true,
|
|
118
|
+
data: {
|
|
119
|
+
screenshot: canvas.toDataURL('image/png'),
|
|
120
|
+
width: canvas.width,
|
|
121
|
+
height: canvas.height,
|
|
122
|
+
selector: command.selector || 'body',
|
|
123
|
+
},
|
|
124
|
+
timestamp: Date.now(),
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
function handleGetLogsCommand(state, ws, command) {
|
|
128
|
+
let logs = state.consoleLogs;
|
|
129
|
+
if (command.filter) {
|
|
130
|
+
const filter = command.filter.toLowerCase();
|
|
131
|
+
logs = logs.filter((log) => log.level.includes(filter) || log.message.toLowerCase().includes(filter));
|
|
132
|
+
}
|
|
133
|
+
ws.send(JSON.stringify({ success: true, data: logs, timestamp: Date.now() }));
|
|
134
|
+
}
|
|
135
|
+
function handleQueryDomCommand(ws, command) {
|
|
136
|
+
if (command.selector) {
|
|
137
|
+
const elements = Array.from(document.querySelectorAll(command.selector));
|
|
138
|
+
const results = elements.map((el) => {
|
|
139
|
+
if (command.property)
|
|
140
|
+
return el[command.property] ?? null;
|
|
141
|
+
return {
|
|
142
|
+
tagName: el.tagName,
|
|
143
|
+
className: el.className,
|
|
144
|
+
id: el.id,
|
|
145
|
+
textContent: el.textContent?.trim().slice(0, 100),
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
ws.send(JSON.stringify({
|
|
149
|
+
success: true,
|
|
150
|
+
data: { count: results.length, results },
|
|
151
|
+
timestamp: Date.now(),
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function handleExecJsCommand(ws, command) {
|
|
156
|
+
if (command.code && typeof command.code === 'string' && command.code.length <= 10000) {
|
|
157
|
+
try {
|
|
158
|
+
// Use indirect eval to avoid strict mode issues
|
|
159
|
+
const indirectEval = eval;
|
|
160
|
+
const result = indirectEval(command.code);
|
|
161
|
+
ws.send(JSON.stringify({ success: true, data: result, timestamp: Date.now() }));
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
ws.send(JSON.stringify({
|
|
165
|
+
success: false,
|
|
166
|
+
error: e instanceof Error ? e.message : 'Execution failed',
|
|
167
|
+
timestamp: Date.now(),
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function handleGetA11yCommand(ws, command) {
|
|
173
|
+
try {
|
|
174
|
+
const result = await runA11yAudit(command.forceRefresh);
|
|
175
|
+
const violationsByImpact = { critical: 0, serious: 0, moderate: 0, minor: 0 };
|
|
176
|
+
for (const v of result.violations) {
|
|
177
|
+
violationsByImpact[v.impact] = (violationsByImpact[v.impact] || 0) + 1;
|
|
178
|
+
}
|
|
179
|
+
ws.send(JSON.stringify({
|
|
180
|
+
success: true,
|
|
181
|
+
data: {
|
|
182
|
+
result,
|
|
183
|
+
summary: {
|
|
184
|
+
totalViolations: result.violations.length,
|
|
185
|
+
totalPasses: result.passes.length,
|
|
186
|
+
totalIncomplete: result.incomplete.length,
|
|
187
|
+
byImpact: violationsByImpact,
|
|
188
|
+
},
|
|
189
|
+
url: window.location.href,
|
|
190
|
+
title: document.title,
|
|
191
|
+
timestamp: Date.now(),
|
|
192
|
+
},
|
|
193
|
+
timestamp: Date.now(),
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
ws.send(JSON.stringify({
|
|
198
|
+
success: false,
|
|
199
|
+
error: e instanceof Error ? e.message : 'Accessibility audit failed',
|
|
200
|
+
timestamp: Date.now(),
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function handleGetOutlineCommand(ws) {
|
|
205
|
+
try {
|
|
206
|
+
const outline = extractDocumentOutline();
|
|
207
|
+
const markdown = outlineToMarkdown(outline);
|
|
208
|
+
ws.send(JSON.stringify({
|
|
209
|
+
success: true,
|
|
210
|
+
data: { outline, markdown, url: window.location.href, title: document.title, timestamp: Date.now() },
|
|
211
|
+
timestamp: Date.now(),
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
ws.send(JSON.stringify({
|
|
216
|
+
success: false,
|
|
217
|
+
error: e instanceof Error ? e.message : 'Outline extraction failed',
|
|
218
|
+
timestamp: Date.now(),
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function handleGetSchemaCommand(ws) {
|
|
223
|
+
try {
|
|
224
|
+
const schema = extractPageSchema();
|
|
225
|
+
const markdown = schemaToMarkdown(schema);
|
|
226
|
+
ws.send(JSON.stringify({
|
|
227
|
+
success: true,
|
|
228
|
+
data: { schema, markdown, url: window.location.href, title: document.title, timestamp: Date.now() },
|
|
229
|
+
timestamp: Date.now(),
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
ws.send(JSON.stringify({
|
|
234
|
+
success: false,
|
|
235
|
+
error: e instanceof Error ? e.message : 'Schema extraction failed',
|
|
236
|
+
timestamp: Date.now(),
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function handleGetVitalsCommand(ws) {
|
|
241
|
+
try {
|
|
242
|
+
const paintEntries = performance.getEntriesByType('paint');
|
|
243
|
+
const fcpEntry = paintEntries.find((e) => e.name === 'first-contentful-paint');
|
|
244
|
+
const fcp = fcpEntry ? Math.round(fcpEntry.startTime) : null;
|
|
245
|
+
// Collect LCP, CLS from buffered observers
|
|
246
|
+
const collectEntries = (entryType) => new Promise((resolve) => {
|
|
247
|
+
try {
|
|
248
|
+
const entries = [];
|
|
249
|
+
const observer = new PerformanceObserver((list) => { entries.push(...list.getEntries()); });
|
|
250
|
+
observer.observe({ type: entryType, buffered: true });
|
|
251
|
+
setTimeout(() => { observer.disconnect(); resolve(entries); }, 0);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
resolve([]);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
const [lcpEntries, layoutShiftEntries, eventEntries] = await Promise.all([
|
|
258
|
+
collectEntries('largest-contentful-paint'),
|
|
259
|
+
collectEntries('layout-shift'),
|
|
260
|
+
collectEntries('event'),
|
|
261
|
+
]);
|
|
262
|
+
const lcp = lcpEntries.length > 0 ? Math.round(lcpEntries[lcpEntries.length - 1].startTime) : null;
|
|
263
|
+
let cls = null;
|
|
264
|
+
if (layoutShiftEntries.length > 0) {
|
|
265
|
+
let clsValue = 0;
|
|
266
|
+
for (const entry of layoutShiftEntries) {
|
|
267
|
+
const se = entry;
|
|
268
|
+
if (!se.hadRecentInput)
|
|
269
|
+
clsValue += se.value;
|
|
270
|
+
}
|
|
271
|
+
cls = Math.round(clsValue * 1000) / 1000;
|
|
272
|
+
}
|
|
273
|
+
let inp = null;
|
|
274
|
+
if (eventEntries.length > 0) {
|
|
275
|
+
let worstDuration = 0;
|
|
276
|
+
for (const entry of eventEntries) {
|
|
277
|
+
const ee = entry;
|
|
278
|
+
if (ee.duration > worstDuration)
|
|
279
|
+
worstDuration = ee.duration;
|
|
280
|
+
}
|
|
281
|
+
inp = Math.round(worstDuration);
|
|
282
|
+
}
|
|
283
|
+
let pageSize = null;
|
|
284
|
+
const resourceEntries = performance.getEntriesByType('resource');
|
|
285
|
+
let totalSize = 0;
|
|
286
|
+
for (const entry of resourceEntries) {
|
|
287
|
+
totalSize += entry.transferSize || 0;
|
|
288
|
+
}
|
|
289
|
+
if (totalSize > 0)
|
|
290
|
+
pageSize = totalSize;
|
|
291
|
+
const vitals = { fcp, lcp, cls, inp, pageSize, url: window.location.href, title: document.title, timestamp: Date.now() };
|
|
292
|
+
const parts = [];
|
|
293
|
+
if (fcp !== null)
|
|
294
|
+
parts.push(`FCP: ${fcp}ms`);
|
|
295
|
+
if (lcp !== null)
|
|
296
|
+
parts.push(`LCP: ${lcp}ms`);
|
|
297
|
+
if (cls !== null)
|
|
298
|
+
parts.push(`CLS: ${cls}`);
|
|
299
|
+
if (inp !== null)
|
|
300
|
+
parts.push(`INP: ${inp}ms`);
|
|
301
|
+
if (pageSize !== null)
|
|
302
|
+
parts.push(`Page size: ${Math.round(pageSize / 1024)}KB`);
|
|
303
|
+
ws.send(JSON.stringify({
|
|
304
|
+
success: true,
|
|
305
|
+
data: { vitals, summary: parts.join(', ') || 'No metrics available yet' },
|
|
306
|
+
timestamp: Date.now(),
|
|
307
|
+
}));
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
ws.send(JSON.stringify({
|
|
311
|
+
success: false,
|
|
312
|
+
error: e instanceof Error ? e.message : 'Vitals collection failed',
|
|
313
|
+
timestamp: Date.now(),
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function handleRefreshCommand(ws) {
|
|
318
|
+
try {
|
|
319
|
+
window.location.reload();
|
|
320
|
+
ws.send(JSON.stringify({ success: true, timestamp: Date.now() }));
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
ws.send(JSON.stringify({
|
|
324
|
+
success: false,
|
|
325
|
+
error: e instanceof Error ? e.message : 'Refresh failed',
|
|
326
|
+
timestamp: Date.now(),
|
|
327
|
+
}));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function handleScreenshotSavedCommand(state, command) {
|
|
331
|
+
handleNotification(state, 'screenshot', command.path, SCREENSHOT_NOTIFICATION_MS);
|
|
332
|
+
}
|
|
333
|
+
function handleDesignReviewSavedCommand(state, command) {
|
|
334
|
+
state.designReviewInProgress = false;
|
|
335
|
+
handleNotification(state, 'designReview', command.reviewPath, DESIGN_REVIEW_NOTIFICATION_MS);
|
|
336
|
+
}
|
|
337
|
+
function handleDesignReviewErrorCommand(state, command) {
|
|
338
|
+
state.designReviewInProgress = false;
|
|
339
|
+
state.designReviewError = command.error || 'Unknown error';
|
|
340
|
+
console.error('[GlobalDevBar] Design review failed:', command.error);
|
|
341
|
+
// Clear error after notification duration
|
|
342
|
+
if (state.designReviewErrorTimeout)
|
|
343
|
+
clearTimeout(state.designReviewErrorTimeout);
|
|
344
|
+
state.designReviewErrorTimeout = setTimeout(() => {
|
|
345
|
+
state.designReviewError = null;
|
|
346
|
+
state.render();
|
|
347
|
+
}, DESIGN_REVIEW_NOTIFICATION_MS);
|
|
348
|
+
state.render();
|
|
349
|
+
}
|
|
350
|
+
function handleApiKeyStatusCommand(state, command) {
|
|
351
|
+
// Properties are at top level of the response
|
|
352
|
+
const response = command;
|
|
353
|
+
state.apiKeyStatus = {
|
|
354
|
+
configured: response.configured ?? false,
|
|
355
|
+
model: response.model,
|
|
356
|
+
pricing: response.pricing,
|
|
357
|
+
};
|
|
358
|
+
// Re-render to update the confirmation modal
|
|
359
|
+
state.render();
|
|
360
|
+
}
|
|
361
|
+
function handleOutlineSavedCommand(state, command) {
|
|
362
|
+
handleNotification(state, 'outline', command.outlinePath, SCREENSHOT_NOTIFICATION_MS);
|
|
363
|
+
}
|
|
364
|
+
function handleOutlineErrorCommand(command) {
|
|
365
|
+
console.error('[GlobalDevBar] Outline save failed:', command.error);
|
|
366
|
+
}
|
|
367
|
+
function handleSchemaSavedCommand(state, command) {
|
|
368
|
+
handleNotification(state, 'schema', command.schemaPath, SCREENSHOT_NOTIFICATION_MS);
|
|
369
|
+
}
|
|
370
|
+
function handleSchemaErrorCommand(command) {
|
|
371
|
+
console.error('[GlobalDevBar] Schema save failed:', command.error);
|
|
372
|
+
}
|
|
373
|
+
function handleConsoleLogsSavedCommand(state, command) {
|
|
374
|
+
handleNotification(state, 'consoleLogs', command.consoleLogsPath, SCREENSHOT_NOTIFICATION_MS);
|
|
375
|
+
}
|
|
376
|
+
function handleConsoleLogsErrorCommand(state, command) {
|
|
377
|
+
state.savingConsoleLogs = false;
|
|
378
|
+
console.error('[GlobalDevBar] Console logs save failed:', command.error);
|
|
379
|
+
state.render();
|
|
380
|
+
}
|
|
381
|
+
function handleA11ySavedCommand(state, command) {
|
|
382
|
+
handleNotification(state, 'a11y', command.a11yPath, SCREENSHOT_NOTIFICATION_MS);
|
|
383
|
+
}
|
|
384
|
+
function handleA11yErrorCommand(state, command) {
|
|
385
|
+
state.savingA11yAudit = false;
|
|
386
|
+
console.error('[GlobalDevBar] A11y save failed:', command.error);
|
|
387
|
+
state.render();
|
|
388
|
+
}
|
|
389
|
+
function handleSettingsLoadedCommand(state, command) {
|
|
390
|
+
handleSettingsLoaded(state, command.settings);
|
|
391
|
+
}
|
|
392
|
+
function handleSettingsSavedCommand(state, command) {
|
|
393
|
+
state.debug.state('Settings saved to server', { path: command.settingsPath });
|
|
394
|
+
}
|
|
395
|
+
function handleSettingsErrorCommand(command) {
|
|
396
|
+
console.error('[GlobalDevBar] Settings operation failed:', command.error);
|
|
397
|
+
}
|
|
398
|
+
// ============================================================================
|
|
399
|
+
// Main command dispatcher
|
|
400
|
+
// ============================================================================
|
|
103
401
|
/**
|
|
104
402
|
* Handle an incoming Sweetlink command from the WebSocket.
|
|
105
403
|
*/
|
|
@@ -108,307 +406,79 @@ async function handleSweetlinkCommand(state, command) {
|
|
|
108
406
|
if (!ws || ws.readyState !== WebSocket.OPEN)
|
|
109
407
|
return;
|
|
110
408
|
switch (command.type) {
|
|
111
|
-
case 'screenshot':
|
|
112
|
-
|
|
113
|
-
? document.querySelector(command.selector) || document.body
|
|
114
|
-
: document.body;
|
|
115
|
-
const html2canvas = await getHtml2Canvas();
|
|
116
|
-
const canvas = await html2canvas(targetElement, {
|
|
117
|
-
logging: false,
|
|
118
|
-
useCORS: true,
|
|
119
|
-
allowTaint: true,
|
|
120
|
-
});
|
|
121
|
-
ws.send(JSON.stringify({
|
|
122
|
-
success: true,
|
|
123
|
-
data: {
|
|
124
|
-
screenshot: canvas.toDataURL('image/png'),
|
|
125
|
-
width: canvas.width,
|
|
126
|
-
height: canvas.height,
|
|
127
|
-
selector: command.selector || 'body',
|
|
128
|
-
},
|
|
129
|
-
timestamp: Date.now(),
|
|
130
|
-
}));
|
|
409
|
+
case 'screenshot':
|
|
410
|
+
await handleScreenshotCommand(ws, command);
|
|
131
411
|
break;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let logs = state.consoleLogs;
|
|
135
|
-
if (command.filter) {
|
|
136
|
-
const filter = command.filter.toLowerCase();
|
|
137
|
-
logs = logs.filter((log) => log.level.includes(filter) || log.message.toLowerCase().includes(filter));
|
|
138
|
-
}
|
|
139
|
-
ws.send(JSON.stringify({ success: true, data: logs, timestamp: Date.now() }));
|
|
412
|
+
case 'get-logs':
|
|
413
|
+
handleGetLogsCommand(state, ws, command);
|
|
140
414
|
break;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (command.selector) {
|
|
144
|
-
const elements = Array.from(document.querySelectorAll(command.selector));
|
|
145
|
-
const results = elements.map((el) => {
|
|
146
|
-
if (command.property)
|
|
147
|
-
return el[command.property] ?? null;
|
|
148
|
-
return {
|
|
149
|
-
tagName: el.tagName,
|
|
150
|
-
className: el.className,
|
|
151
|
-
id: el.id,
|
|
152
|
-
textContent: el.textContent?.trim().slice(0, 100),
|
|
153
|
-
};
|
|
154
|
-
});
|
|
155
|
-
ws.send(JSON.stringify({
|
|
156
|
-
success: true,
|
|
157
|
-
data: { count: results.length, results },
|
|
158
|
-
timestamp: Date.now(),
|
|
159
|
-
}));
|
|
160
|
-
}
|
|
415
|
+
case 'query-dom':
|
|
416
|
+
handleQueryDomCommand(ws, command);
|
|
161
417
|
break;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (command.code && typeof command.code === 'string' && command.code.length <= 10000) {
|
|
165
|
-
try {
|
|
166
|
-
// Use indirect eval to avoid strict mode issues
|
|
167
|
-
const indirectEval = eval;
|
|
168
|
-
const result = indirectEval(command.code);
|
|
169
|
-
ws.send(JSON.stringify({ success: true, data: result, timestamp: Date.now() }));
|
|
170
|
-
}
|
|
171
|
-
catch (e) {
|
|
172
|
-
ws.send(JSON.stringify({
|
|
173
|
-
success: false,
|
|
174
|
-
error: e instanceof Error ? e.message : 'Execution failed',
|
|
175
|
-
timestamp: Date.now(),
|
|
176
|
-
}));
|
|
177
|
-
}
|
|
178
|
-
}
|
|
418
|
+
case 'exec-js':
|
|
419
|
+
handleExecJsCommand(ws, command);
|
|
179
420
|
break;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const result = await runA11yAudit(command.forceRefresh);
|
|
184
|
-
const violationsByImpact = { critical: 0, serious: 0, moderate: 0, minor: 0 };
|
|
185
|
-
for (const v of result.violations) {
|
|
186
|
-
violationsByImpact[v.impact] = (violationsByImpact[v.impact] || 0) + 1;
|
|
187
|
-
}
|
|
188
|
-
ws.send(JSON.stringify({
|
|
189
|
-
success: true,
|
|
190
|
-
data: {
|
|
191
|
-
result,
|
|
192
|
-
summary: {
|
|
193
|
-
totalViolations: result.violations.length,
|
|
194
|
-
totalPasses: result.passes.length,
|
|
195
|
-
totalIncomplete: result.incomplete.length,
|
|
196
|
-
byImpact: violationsByImpact,
|
|
197
|
-
},
|
|
198
|
-
url: window.location.href,
|
|
199
|
-
title: document.title,
|
|
200
|
-
timestamp: Date.now(),
|
|
201
|
-
},
|
|
202
|
-
timestamp: Date.now(),
|
|
203
|
-
}));
|
|
204
|
-
}
|
|
205
|
-
catch (e) {
|
|
206
|
-
ws.send(JSON.stringify({
|
|
207
|
-
success: false,
|
|
208
|
-
error: e instanceof Error ? e.message : 'Accessibility audit failed',
|
|
209
|
-
timestamp: Date.now(),
|
|
210
|
-
}));
|
|
211
|
-
}
|
|
421
|
+
case 'get-a11y':
|
|
422
|
+
await handleGetA11yCommand(ws, command);
|
|
212
423
|
break;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
try {
|
|
216
|
-
const outline = extractDocumentOutline();
|
|
217
|
-
const markdown = outlineToMarkdown(outline);
|
|
218
|
-
ws.send(JSON.stringify({
|
|
219
|
-
success: true,
|
|
220
|
-
data: { outline, markdown, url: window.location.href, title: document.title, timestamp: Date.now() },
|
|
221
|
-
timestamp: Date.now(),
|
|
222
|
-
}));
|
|
223
|
-
}
|
|
224
|
-
catch (e) {
|
|
225
|
-
ws.send(JSON.stringify({
|
|
226
|
-
success: false,
|
|
227
|
-
error: e instanceof Error ? e.message : 'Outline extraction failed',
|
|
228
|
-
timestamp: Date.now(),
|
|
229
|
-
}));
|
|
230
|
-
}
|
|
424
|
+
case 'get-outline':
|
|
425
|
+
handleGetOutlineCommand(ws);
|
|
231
426
|
break;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
const schema = extractPageSchema();
|
|
236
|
-
const markdown = schemaToMarkdown(schema);
|
|
237
|
-
ws.send(JSON.stringify({
|
|
238
|
-
success: true,
|
|
239
|
-
data: { schema, markdown, url: window.location.href, title: document.title, timestamp: Date.now() },
|
|
240
|
-
timestamp: Date.now(),
|
|
241
|
-
}));
|
|
242
|
-
}
|
|
243
|
-
catch (e) {
|
|
244
|
-
ws.send(JSON.stringify({
|
|
245
|
-
success: false,
|
|
246
|
-
error: e instanceof Error ? e.message : 'Schema extraction failed',
|
|
247
|
-
timestamp: Date.now(),
|
|
248
|
-
}));
|
|
249
|
-
}
|
|
427
|
+
case 'get-schema':
|
|
428
|
+
handleGetSchemaCommand(ws);
|
|
250
429
|
break;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
const paintEntries = performance.getEntriesByType('paint');
|
|
255
|
-
const fcpEntry = paintEntries.find((e) => e.name === 'first-contentful-paint');
|
|
256
|
-
const fcp = fcpEntry ? Math.round(fcpEntry.startTime) : null;
|
|
257
|
-
// Collect LCP, CLS from buffered observers
|
|
258
|
-
const collectEntries = (entryType) => new Promise((resolve) => {
|
|
259
|
-
try {
|
|
260
|
-
const entries = [];
|
|
261
|
-
const observer = new PerformanceObserver((list) => { entries.push(...list.getEntries()); });
|
|
262
|
-
observer.observe({ type: entryType, buffered: true });
|
|
263
|
-
setTimeout(() => { observer.disconnect(); resolve(entries); }, 0);
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
resolve([]);
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
const [lcpEntries, layoutShiftEntries, eventEntries] = await Promise.all([
|
|
270
|
-
collectEntries('largest-contentful-paint'),
|
|
271
|
-
collectEntries('layout-shift'),
|
|
272
|
-
collectEntries('event'),
|
|
273
|
-
]);
|
|
274
|
-
const lcp = lcpEntries.length > 0 ? Math.round(lcpEntries[lcpEntries.length - 1].startTime) : null;
|
|
275
|
-
let cls = null;
|
|
276
|
-
if (layoutShiftEntries.length > 0) {
|
|
277
|
-
let clsValue = 0;
|
|
278
|
-
for (const entry of layoutShiftEntries) {
|
|
279
|
-
const se = entry;
|
|
280
|
-
if (!se.hadRecentInput)
|
|
281
|
-
clsValue += se.value;
|
|
282
|
-
}
|
|
283
|
-
cls = Math.round(clsValue * 1000) / 1000;
|
|
284
|
-
}
|
|
285
|
-
let inp = null;
|
|
286
|
-
if (eventEntries.length > 0) {
|
|
287
|
-
let worstDuration = 0;
|
|
288
|
-
for (const entry of eventEntries) {
|
|
289
|
-
const ee = entry;
|
|
290
|
-
if (ee.duration > worstDuration)
|
|
291
|
-
worstDuration = ee.duration;
|
|
292
|
-
}
|
|
293
|
-
inp = Math.round(worstDuration);
|
|
294
|
-
}
|
|
295
|
-
let pageSize = null;
|
|
296
|
-
const resourceEntries = performance.getEntriesByType('resource');
|
|
297
|
-
let totalSize = 0;
|
|
298
|
-
for (const entry of resourceEntries) {
|
|
299
|
-
totalSize += entry.transferSize || 0;
|
|
300
|
-
}
|
|
301
|
-
if (totalSize > 0)
|
|
302
|
-
pageSize = totalSize;
|
|
303
|
-
const vitals = { fcp, lcp, cls, inp, pageSize, url: window.location.href, title: document.title, timestamp: Date.now() };
|
|
304
|
-
const parts = [];
|
|
305
|
-
if (fcp !== null)
|
|
306
|
-
parts.push(`FCP: ${fcp}ms`);
|
|
307
|
-
if (lcp !== null)
|
|
308
|
-
parts.push(`LCP: ${lcp}ms`);
|
|
309
|
-
if (cls !== null)
|
|
310
|
-
parts.push(`CLS: ${cls}`);
|
|
311
|
-
if (inp !== null)
|
|
312
|
-
parts.push(`INP: ${inp}ms`);
|
|
313
|
-
if (pageSize !== null)
|
|
314
|
-
parts.push(`Page size: ${Math.round(pageSize / 1024)}KB`);
|
|
315
|
-
ws.send(JSON.stringify({
|
|
316
|
-
success: true,
|
|
317
|
-
data: { vitals, summary: parts.join(', ') || 'No metrics available yet' },
|
|
318
|
-
timestamp: Date.now(),
|
|
319
|
-
}));
|
|
320
|
-
}
|
|
321
|
-
catch (e) {
|
|
322
|
-
ws.send(JSON.stringify({
|
|
323
|
-
success: false,
|
|
324
|
-
error: e instanceof Error ? e.message : 'Vitals collection failed',
|
|
325
|
-
timestamp: Date.now(),
|
|
326
|
-
}));
|
|
327
|
-
}
|
|
430
|
+
case 'get-vitals':
|
|
431
|
+
await handleGetVitalsCommand(ws);
|
|
328
432
|
break;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
try {
|
|
332
|
-
window.location.reload();
|
|
333
|
-
ws.send(JSON.stringify({ success: true, timestamp: Date.now() }));
|
|
334
|
-
}
|
|
335
|
-
catch (e) {
|
|
336
|
-
ws.send(JSON.stringify({
|
|
337
|
-
success: false,
|
|
338
|
-
error: e instanceof Error ? e.message : 'Refresh failed',
|
|
339
|
-
timestamp: Date.now(),
|
|
340
|
-
}));
|
|
341
|
-
}
|
|
433
|
+
case 'refresh':
|
|
434
|
+
handleRefreshCommand(ws);
|
|
342
435
|
break;
|
|
343
|
-
}
|
|
344
436
|
case 'screenshot-saved':
|
|
345
|
-
|
|
437
|
+
handleScreenshotSavedCommand(state, command);
|
|
346
438
|
break;
|
|
347
439
|
case 'design-review-saved':
|
|
348
|
-
state
|
|
349
|
-
handleNotification(state, 'designReview', command.reviewPath, DESIGN_REVIEW_NOTIFICATION_MS);
|
|
440
|
+
handleDesignReviewSavedCommand(state, command);
|
|
350
441
|
break;
|
|
351
442
|
case 'design-review-error':
|
|
352
|
-
state
|
|
353
|
-
state.designReviewError = command.error || 'Unknown error';
|
|
354
|
-
console.error('[GlobalDevBar] Design review failed:', command.error);
|
|
355
|
-
// Clear error after notification duration
|
|
356
|
-
if (state.designReviewErrorTimeout)
|
|
357
|
-
clearTimeout(state.designReviewErrorTimeout);
|
|
358
|
-
state.designReviewErrorTimeout = setTimeout(() => {
|
|
359
|
-
state.designReviewError = null;
|
|
360
|
-
state.render();
|
|
361
|
-
}, DESIGN_REVIEW_NOTIFICATION_MS);
|
|
362
|
-
state.render();
|
|
443
|
+
handleDesignReviewErrorCommand(state, command);
|
|
363
444
|
break;
|
|
364
|
-
case 'api-key-status':
|
|
365
|
-
|
|
366
|
-
const response = command;
|
|
367
|
-
state.apiKeyStatus = {
|
|
368
|
-
configured: response.configured ?? false,
|
|
369
|
-
model: response.model,
|
|
370
|
-
pricing: response.pricing,
|
|
371
|
-
};
|
|
372
|
-
// Re-render to update the confirmation modal
|
|
373
|
-
state.render();
|
|
445
|
+
case 'api-key-status':
|
|
446
|
+
handleApiKeyStatusCommand(state, command);
|
|
374
447
|
break;
|
|
375
|
-
}
|
|
376
448
|
case 'outline-saved':
|
|
377
|
-
|
|
449
|
+
handleOutlineSavedCommand(state, command);
|
|
378
450
|
break;
|
|
379
451
|
case 'outline-error':
|
|
380
|
-
|
|
452
|
+
handleOutlineErrorCommand(command);
|
|
381
453
|
break;
|
|
382
454
|
case 'schema-saved':
|
|
383
|
-
|
|
455
|
+
handleSchemaSavedCommand(state, command);
|
|
384
456
|
break;
|
|
385
457
|
case 'schema-error':
|
|
386
|
-
|
|
458
|
+
handleSchemaErrorCommand(command);
|
|
387
459
|
break;
|
|
388
460
|
case 'console-logs-saved':
|
|
389
|
-
|
|
461
|
+
handleConsoleLogsSavedCommand(state, command);
|
|
390
462
|
break;
|
|
391
463
|
case 'console-logs-error':
|
|
392
|
-
state
|
|
393
|
-
console.error('[GlobalDevBar] Console logs save failed:', command.error);
|
|
394
|
-
state.render();
|
|
464
|
+
handleConsoleLogsErrorCommand(state, command);
|
|
395
465
|
break;
|
|
396
466
|
case 'a11y-saved':
|
|
397
|
-
|
|
467
|
+
handleA11ySavedCommand(state, command);
|
|
398
468
|
break;
|
|
399
469
|
case 'a11y-error':
|
|
400
|
-
state
|
|
401
|
-
console.error('[GlobalDevBar] A11y save failed:', command.error);
|
|
402
|
-
state.render();
|
|
470
|
+
handleA11yErrorCommand(state, command);
|
|
403
471
|
break;
|
|
404
472
|
case 'settings-loaded':
|
|
405
|
-
|
|
473
|
+
handleSettingsLoadedCommand(state, command);
|
|
406
474
|
break;
|
|
407
475
|
case 'settings-saved':
|
|
408
|
-
state
|
|
476
|
+
handleSettingsSavedCommand(state, command);
|
|
409
477
|
break;
|
|
410
478
|
case 'settings-error':
|
|
411
|
-
|
|
479
|
+
handleSettingsErrorCommand(command);
|
|
480
|
+
break;
|
|
481
|
+
default:
|
|
412
482
|
break;
|
|
413
483
|
}
|
|
414
484
|
}
|