@ytspar/devbar 1.3.1 → 1.4.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.
Files changed (55) hide show
  1. package/README.md +43 -0
  2. package/dist/accessibility.d.ts +4 -0
  3. package/dist/accessibility.d.ts.map +1 -1
  4. package/dist/accessibility.js +57 -0
  5. package/dist/accessibility.js.map +1 -1
  6. package/dist/constants.d.ts +0 -23
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +0 -3
  9. package/dist/constants.js.map +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/modules/index.d.ts +1 -1
  15. package/dist/modules/index.d.ts.map +1 -1
  16. package/dist/modules/index.js +1 -0
  17. package/dist/modules/index.js.map +1 -1
  18. package/dist/modules/keyboard.d.ts +1 -1
  19. package/dist/modules/keyboard.d.ts.map +1 -1
  20. package/dist/modules/keyboard.js +4 -11
  21. package/dist/modules/keyboard.js.map +1 -1
  22. package/dist/modules/rendering.d.ts +1 -1
  23. package/dist/modules/rendering.d.ts.map +1 -1
  24. package/dist/modules/rendering.js +448 -466
  25. package/dist/modules/rendering.js.map +1 -1
  26. package/dist/modules/screenshot.d.ts +11 -2
  27. package/dist/modules/screenshot.d.ts.map +1 -1
  28. package/dist/modules/screenshot.js +32 -29
  29. package/dist/modules/screenshot.js.map +1 -1
  30. package/dist/modules/tooltips.d.ts +1 -1
  31. package/dist/modules/tooltips.d.ts.map +1 -1
  32. package/dist/modules/tooltips.js +13 -13
  33. package/dist/modules/tooltips.js.map +1 -1
  34. package/dist/modules/types.d.ts +7 -0
  35. package/dist/modules/types.d.ts.map +1 -1
  36. package/dist/modules/types.js +14 -1
  37. package/dist/modules/types.js.map +1 -1
  38. package/dist/modules/websocket.d.ts.map +1 -1
  39. package/dist/modules/websocket.js +334 -264
  40. package/dist/modules/websocket.js.map +1 -1
  41. package/dist/ui/buttons.d.ts.map +1 -1
  42. package/dist/ui/buttons.js +3 -1
  43. package/dist/ui/buttons.js.map +1 -1
  44. package/dist/ui/icons.d.ts +13 -0
  45. package/dist/ui/icons.d.ts.map +1 -1
  46. package/dist/ui/icons.js +24 -3
  47. package/dist/ui/icons.js.map +1 -1
  48. package/dist/ui/index.d.ts +1 -1
  49. package/dist/ui/index.d.ts.map +1 -1
  50. package/dist/ui/index.js.map +1 -1
  51. package/dist/ui/modals.d.ts +3 -2
  52. package/dist/ui/modals.d.ts.map +1 -1
  53. package/dist/ui/modals.js +22 -20
  54. package/dist/ui/modals.js.map +1 -1
  55. 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
- const targetElement = command.selector
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
- case 'get-logs': {
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
- case 'query-dom': {
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
- case 'exec-js': {
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
- case 'get-a11y': {
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
- case 'get-outline': {
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
- case 'get-schema': {
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
- case 'get-vitals': {
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
- case 'refresh': {
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
- handleNotification(state, 'screenshot', command.path, SCREENSHOT_NOTIFICATION_MS);
437
+ handleScreenshotSavedCommand(state, command);
346
438
  break;
347
439
  case 'design-review-saved':
348
- state.designReviewInProgress = false;
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.designReviewInProgress = false;
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
- // Properties are at top level of the response
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
- handleNotification(state, 'outline', command.outlinePath, SCREENSHOT_NOTIFICATION_MS);
449
+ handleOutlineSavedCommand(state, command);
378
450
  break;
379
451
  case 'outline-error':
380
- console.error('[GlobalDevBar] Outline save failed:', command.error);
452
+ handleOutlineErrorCommand(command);
381
453
  break;
382
454
  case 'schema-saved':
383
- handleNotification(state, 'schema', command.schemaPath, SCREENSHOT_NOTIFICATION_MS);
455
+ handleSchemaSavedCommand(state, command);
384
456
  break;
385
457
  case 'schema-error':
386
- console.error('[GlobalDevBar] Schema save failed:', command.error);
458
+ handleSchemaErrorCommand(command);
387
459
  break;
388
460
  case 'console-logs-saved':
389
- handleNotification(state, 'consoleLogs', command.consoleLogsPath, SCREENSHOT_NOTIFICATION_MS);
461
+ handleConsoleLogsSavedCommand(state, command);
390
462
  break;
391
463
  case 'console-logs-error':
392
- state.savingConsoleLogs = false;
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
- handleNotification(state, 'a11y', command.a11yPath, SCREENSHOT_NOTIFICATION_MS);
467
+ handleA11ySavedCommand(state, command);
398
468
  break;
399
469
  case 'a11y-error':
400
- state.savingA11yAudit = false;
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
- handleSettingsLoaded(state, command.settings);
473
+ handleSettingsLoadedCommand(state, command);
406
474
  break;
407
475
  case 'settings-saved':
408
- state.debug.state('Settings saved to server', { path: command.settingsPath });
476
+ handleSettingsSavedCommand(state, command);
409
477
  break;
410
478
  case 'settings-error':
411
- console.error('[GlobalDevBar] Settings operation failed:', command.error);
479
+ handleSettingsErrorCommand(command);
480
+ break;
481
+ default:
412
482
  break;
413
483
  }
414
484
  }