uilint-react 0.1.39 → 0.1.41

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.
@@ -3,7 +3,7 @@ import {
3
3
  groupBySourceFile,
4
4
  useUILintContext,
5
5
  useUILintStore
6
- } from "./chunk-ILK73X6L.js";
6
+ } from "./chunk-LAL3JTAA.js";
7
7
 
8
8
  // src/components/ui-lint/UILintToolbar.tsx
9
9
  import { useState as useState2, useRef as useRef2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
@@ -60,134 +60,8 @@ function getStatusColor(issueCount) {
60
60
  return STYLES.warning;
61
61
  }
62
62
 
63
- // src/components/ui-lint/toolbar-icons.tsx
64
- import { jsx, jsxs } from "react/jsx-runtime";
65
- function StopIcon() {
66
- return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "1", fill: "currentColor" }) });
67
- }
68
- function EllipsisIcon() {
69
- return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
70
- /* @__PURE__ */ jsx("circle", { cx: "5", cy: "12", r: "2", fill: "currentColor" }),
71
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2", fill: "currentColor" }),
72
- /* @__PURE__ */ jsx("circle", { cx: "19", cy: "12", r: "2", fill: "currentColor" })
73
- ] });
74
- }
75
- function CheckCircleIcon() {
76
- return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
77
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeWidth: "2" }),
78
- /* @__PURE__ */ jsx(
79
- "path",
80
- {
81
- d: "M8 12l3 3 5-6",
82
- stroke: "currentColor",
83
- strokeWidth: "2",
84
- strokeLinecap: "round",
85
- strokeLinejoin: "round"
86
- }
87
- )
88
- ] });
89
- }
90
- function SpinnerIcon() {
91
- return /* @__PURE__ */ jsxs(
92
- "svg",
93
- {
94
- width: "14",
95
- height: "14",
96
- viewBox: "0 0 24 24",
97
- fill: "none",
98
- style: {
99
- animation: "uilint-spin 1s linear infinite"
100
- },
101
- children: [
102
- /* @__PURE__ */ jsx(
103
- "path",
104
- {
105
- d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83",
106
- stroke: "currentColor",
107
- strokeWidth: "2",
108
- strokeLinecap: "round",
109
- opacity: "0.3"
110
- }
111
- ),
112
- /* @__PURE__ */ jsx(
113
- "path",
114
- {
115
- d: "M12 2v4",
116
- stroke: "currentColor",
117
- strokeWidth: "2",
118
- strokeLinecap: "round"
119
- }
120
- )
121
- ]
122
- }
123
- );
124
- }
125
- function EyeIcon() {
126
- return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
127
- /* @__PURE__ */ jsx(
128
- "path",
129
- {
130
- d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z",
131
- stroke: "currentColor",
132
- strokeWidth: "2",
133
- strokeLinecap: "round",
134
- strokeLinejoin: "round"
135
- }
136
- ),
137
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3", stroke: "currentColor", strokeWidth: "2" })
138
- ] });
139
- }
140
- function EyeOffIcon() {
141
- return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
142
- /* @__PURE__ */ jsx(
143
- "path",
144
- {
145
- d: "M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24",
146
- stroke: "currentColor",
147
- strokeWidth: "2",
148
- strokeLinecap: "round",
149
- strokeLinejoin: "round"
150
- }
151
- ),
152
- /* @__PURE__ */ jsx(
153
- "path",
154
- {
155
- d: "M1 1l22 22",
156
- stroke: "currentColor",
157
- strokeWidth: "2",
158
- strokeLinecap: "round",
159
- strokeLinejoin: "round"
160
- }
161
- )
162
- ] });
163
- }
164
- function WarningIcon() {
165
- return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
166
- "path",
167
- {
168
- d: "M12 9v4m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z",
169
- stroke: "currentColor",
170
- strokeWidth: "2",
171
- strokeLinecap: "round",
172
- strokeLinejoin: "round"
173
- }
174
- ) });
175
- }
176
- function ChevronIcon() {
177
- return /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
178
- "path",
179
- {
180
- d: "M9 6l6 6-6 6",
181
- stroke: "currentColor",
182
- strokeWidth: "2",
183
- strokeLinecap: "round",
184
- strokeLinejoin: "round"
185
- }
186
- ) });
187
- }
188
-
189
63
  // src/components/ui-lint/SettingsPopover.tsx
190
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
64
+ import { jsx, jsxs } from "react/jsx-runtime";
191
65
  function SettingsPopover({ settings }) {
192
66
  const wsConnected = useUILintStore((s) => s.wsConnected);
193
67
  const wsUrl = useUILintStore((s) => s.wsUrl);
@@ -197,7 +71,7 @@ function SettingsPopover({ settings }) {
197
71
  const disconnectWebSocket = useUILintStore(
198
72
  (s) => s.disconnectWebSocket
199
73
  );
200
- return /* @__PURE__ */ jsxs2(
74
+ return /* @__PURE__ */ jsxs(
201
75
  "div",
202
76
  {
203
77
  style: {
@@ -216,7 +90,7 @@ function SettingsPopover({ settings }) {
216
90
  animation: "uilint-fade-in 0.15s ease-out"
217
91
  },
218
92
  children: [
219
- /* @__PURE__ */ jsx2(
93
+ /* @__PURE__ */ jsx(
220
94
  "div",
221
95
  {
222
96
  style: {
@@ -228,7 +102,7 @@ function SettingsPopover({ settings }) {
228
102
  children: "UILint Settings"
229
103
  }
230
104
  ),
231
- /* @__PURE__ */ jsxs2(
105
+ /* @__PURE__ */ jsxs(
232
106
  "div",
233
107
  {
234
108
  style: {
@@ -238,7 +112,7 @@ function SettingsPopover({ settings }) {
238
112
  backgroundColor: STYLES.bgSegment
239
113
  },
240
114
  children: [
241
- /* @__PURE__ */ jsx2(
115
+ /* @__PURE__ */ jsx(
242
116
  "div",
243
117
  {
244
118
  style: {
@@ -247,8 +121,8 @@ function SettingsPopover({ settings }) {
247
121
  justifyContent: "space-between",
248
122
  marginBottom: "8px"
249
123
  },
250
- children: /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
251
- /* @__PURE__ */ jsx2(
124
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
125
+ /* @__PURE__ */ jsx(
252
126
  "div",
253
127
  {
254
128
  style: {
@@ -260,7 +134,7 @@ function SettingsPopover({ settings }) {
260
134
  }
261
135
  }
262
136
  ),
263
- /* @__PURE__ */ jsx2(
137
+ /* @__PURE__ */ jsx(
264
138
  "span",
265
139
  {
266
140
  style: {
@@ -274,7 +148,7 @@ function SettingsPopover({ settings }) {
274
148
  ] })
275
149
  }
276
150
  ),
277
- /* @__PURE__ */ jsx2(
151
+ /* @__PURE__ */ jsx(
278
152
  "div",
279
153
  {
280
154
  style: {
@@ -287,8 +161,8 @@ function SettingsPopover({ settings }) {
287
161
  children: wsUrl
288
162
  }
289
163
  ),
290
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: "6px" }, children: [
291
- /* @__PURE__ */ jsx2(
164
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
165
+ /* @__PURE__ */ jsx(
292
166
  "button",
293
167
  {
294
168
  onClick: () => connectWebSocket(wsUrl),
@@ -308,7 +182,7 @@ function SettingsPopover({ settings }) {
308
182
  children: wsConnected ? "Reconnect" : "Connect"
309
183
  }
310
184
  ),
311
- /* @__PURE__ */ jsx2(
185
+ /* @__PURE__ */ jsx(
312
186
  "button",
313
187
  {
314
188
  onClick: () => disconnectWebSocket(),
@@ -333,7 +207,7 @@ function SettingsPopover({ settings }) {
333
207
  ]
334
208
  }
335
209
  ),
336
- /* @__PURE__ */ jsxs2(
210
+ /* @__PURE__ */ jsxs(
337
211
  "div",
338
212
  {
339
213
  style: {
@@ -343,7 +217,7 @@ function SettingsPopover({ settings }) {
343
217
  lineHeight: 1.5
344
218
  },
345
219
  children: [
346
- /* @__PURE__ */ jsx2("strong", { style: { color: STYLES.text }, children: "\u2325+Click" }),
220
+ /* @__PURE__ */ jsx("strong", { style: { color: STYLES.text }, children: "\u2325+Click" }),
347
221
  " any element to open the inspector sidebar"
348
222
  ]
349
223
  }
@@ -358,6 +232,26 @@ import { useRef, useEffect } from "react";
358
232
 
359
233
  // src/components/ui-lint/ScanResultsPopover.tsx
360
234
  import { useState, useCallback, useMemo } from "react";
235
+
236
+ // src/components/ui-lint/toolbar-icons.tsx
237
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
238
+ function StopIcon() {
239
+ return /* @__PURE__ */ jsx2("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx2("rect", { x: "6", y: "6", width: "12", height: "12", rx: "1", fill: "currentColor" }) });
240
+ }
241
+ function ChevronIcon() {
242
+ return /* @__PURE__ */ jsx2("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx2(
243
+ "path",
244
+ {
245
+ d: "M9 6l6 6-6 6",
246
+ stroke: "currentColor",
247
+ strokeWidth: "2",
248
+ strokeLinecap: "round",
249
+ strokeLinejoin: "round"
250
+ }
251
+ ) });
252
+ }
253
+
254
+ // src/components/ui-lint/ScanResultsPopover.tsx
361
255
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
362
256
  function getDisambiguatedName(path, allPaths) {
363
257
  const parts = path.split("/");
@@ -386,9 +280,10 @@ function ScanResultsPopover() {
386
280
  (s) => s.setInspectedElement
387
281
  );
388
282
  const [expandedFiles, setExpandedFiles] = useState(/* @__PURE__ */ new Set());
283
+ const [searchQuery, setSearchQuery] = useState("");
389
284
  const isScanning = autoScanState.status === "scanning";
390
285
  const isComplete = autoScanState.status === "complete";
391
- const filesWithIssues = useMemo(() => {
286
+ const allFilesWithIssues = useMemo(() => {
392
287
  const sourceFiles = groupBySourceFile(autoScanState.elements);
393
288
  const allPaths = sourceFiles.map((sf) => sf.path);
394
289
  const result = [];
@@ -398,7 +293,12 @@ function ScanResultsPopover() {
398
293
  const cached = elementIssuesCache.get(el.id);
399
294
  const issueCount = cached?.issues.length || 0;
400
295
  if (issueCount > 0) {
401
- elementsWithIssues.push({ element: el, issueCount });
296
+ const ruleIds = Array.from(
297
+ new Set(
298
+ (cached?.issues || []).map((i) => i.ruleId).filter((r) => typeof r === "string")
299
+ )
300
+ );
301
+ elementsWithIssues.push({ element: el, issueCount, ruleIds });
402
302
  }
403
303
  }
404
304
  if (elementsWithIssues.length > 0) {
@@ -421,7 +321,44 @@ function ScanResultsPopover() {
421
321
  result.sort((a, b) => b.issueCount - a.issueCount);
422
322
  return result;
423
323
  }, [autoScanState.elements, elementIssuesCache]);
324
+ const filesWithIssues = useMemo(() => {
325
+ if (!searchQuery.trim()) {
326
+ return allFilesWithIssues;
327
+ }
328
+ const raw = searchQuery.toLowerCase().trim();
329
+ const tagQuery = raw.replace(/[<>]/g, "").replace("/", "").trim();
330
+ const query = raw;
331
+ return allFilesWithIssues.map((file) => {
332
+ const fileMatches = file.path.toLowerCase().includes(query) || file.displayName.toLowerCase().includes(query) || file.disambiguatedName.toLowerCase().includes(query);
333
+ const matchingElements = file.elementsWithIssues.filter((item) => {
334
+ const tagMatches = item.element.tagName.toLowerCase().includes(tagQuery);
335
+ const ruleMatches = item.ruleIds.some(
336
+ (r) => r.toLowerCase().includes(query)
337
+ );
338
+ return tagMatches || ruleMatches;
339
+ });
340
+ if (fileMatches) {
341
+ return file;
342
+ }
343
+ if (matchingElements.length > 0) {
344
+ const filteredIssueCount = matchingElements.reduce(
345
+ (sum, e) => sum + e.issueCount,
346
+ 0
347
+ );
348
+ return {
349
+ ...file,
350
+ elementsWithIssues: matchingElements,
351
+ issueCount: filteredIssueCount
352
+ };
353
+ }
354
+ return null;
355
+ }).filter((file) => file !== null);
356
+ }, [allFilesWithIssues, searchQuery]);
424
357
  const totalIssues = filesWithIssues.reduce((sum, f) => sum + f.issueCount, 0);
358
+ const totalAllIssues = allFilesWithIssues.reduce(
359
+ (sum, f) => sum + f.issueCount,
360
+ 0
361
+ );
425
362
  const progress = autoScanState.totalElements > 0 ? autoScanState.currentIndex / autoScanState.totalElements * 100 : 0;
426
363
  const toggleFile = useCallback((path) => {
427
364
  setExpandedFiles((prev) => {
@@ -514,7 +451,7 @@ function ScanResultsPopover() {
514
451
  fontWeight: 600,
515
452
  color: STYLES.text
516
453
  },
517
- children: filesWithIssues.length === 0 ? "No issues found" : `${filesWithIssues.length} ${filesWithIssues.length === 1 ? "file" : "files"} with ${totalIssues} ${totalIssues === 1 ? "issue" : "issues"}`
454
+ children: allFilesWithIssues.length === 0 ? "No issues found" : searchQuery ? `${filesWithIssues.length} of ${allFilesWithIssues.length} files` : `${filesWithIssues.length} ${filesWithIssues.length === 1 ? "file" : "files"} with ${totalAllIssues} ${totalAllIssues === 1 ? "issue" : "issues"}`
518
455
  }
519
456
  ),
520
457
  liveScanEnabled && /* @__PURE__ */ jsx3(
@@ -541,6 +478,62 @@ function ScanResultsPopover() {
541
478
  ]
542
479
  }
543
480
  ),
481
+ allFilesWithIssues.length > 0 && /* @__PURE__ */ jsx3("div", { style: { marginTop: "10px" }, children: /* @__PURE__ */ jsxs3(
482
+ "div",
483
+ {
484
+ style: {
485
+ display: "flex",
486
+ alignItems: "center",
487
+ gap: "8px",
488
+ padding: "6px 10px",
489
+ backgroundColor: "rgba(17, 24, 39, 0.6)",
490
+ borderRadius: "6px",
491
+ border: `1px solid ${STYLES.border}`
492
+ },
493
+ children: [
494
+ /* @__PURE__ */ jsx3(SearchIcon, {}),
495
+ /* @__PURE__ */ jsx3(
496
+ "input",
497
+ {
498
+ type: "text",
499
+ value: searchQuery,
500
+ onChange: (e) => setSearchQuery(e.target.value),
501
+ placeholder: "Filter by file or tag...",
502
+ style: {
503
+ flex: 1,
504
+ border: "none",
505
+ backgroundColor: "transparent",
506
+ color: STYLES.text,
507
+ fontSize: "11px",
508
+ fontFamily: STYLES.font,
509
+ outline: "none"
510
+ }
511
+ }
512
+ ),
513
+ searchQuery && /* @__PURE__ */ jsx3(
514
+ "button",
515
+ {
516
+ onClick: () => setSearchQuery(""),
517
+ style: {
518
+ display: "flex",
519
+ alignItems: "center",
520
+ justifyContent: "center",
521
+ width: "16px",
522
+ height: "16px",
523
+ borderRadius: "50%",
524
+ border: "none",
525
+ backgroundColor: "rgba(75, 85, 99, 0.5)",
526
+ color: STYLES.textMuted,
527
+ cursor: "pointer",
528
+ padding: 0
529
+ },
530
+ title: "Clear search",
531
+ children: /* @__PURE__ */ jsx3(ClearIcon, {})
532
+ }
533
+ )
534
+ ]
535
+ }
536
+ ) }),
544
537
  isScanning && /* @__PURE__ */ jsxs3("div", { style: { marginTop: "10px" }, children: [
545
538
  /* @__PURE__ */ jsxs3(
546
539
  "div",
@@ -606,7 +599,11 @@ function ScanResultsPopover() {
606
599
  fontSize: "11px",
607
600
  color: STYLES.textMuted
608
601
  },
609
- children: isScanning ? "Scanning page elements..." : isComplete ? /* @__PURE__ */ jsx3("span", { style: { color: STYLES.success }, children: "\u2713 No issues found" }) : "No files scanned"
602
+ children: isScanning ? "Scanning page elements..." : searchQuery && allFilesWithIssues.length > 0 ? /* @__PURE__ */ jsxs3("span", { children: [
603
+ 'No matches for "',
604
+ searchQuery,
605
+ '"'
606
+ ] }) : isComplete ? /* @__PURE__ */ jsx3("span", { style: { color: STYLES.success }, children: "\u2713 No issues found" }) : "No files scanned"
610
607
  }
611
608
  ) : filesWithIssues.map((file) => /* @__PURE__ */ jsx3(
612
609
  FileRow,
@@ -818,6 +815,52 @@ function ElementRow({ item, onHover, onClick }) {
818
815
  }
819
816
  );
820
817
  }
818
+ function SearchIcon() {
819
+ return /* @__PURE__ */ jsxs3(
820
+ "svg",
821
+ {
822
+ width: "12",
823
+ height: "12",
824
+ viewBox: "0 0 24 24",
825
+ fill: "none",
826
+ style: { flexShrink: 0 },
827
+ children: [
828
+ /* @__PURE__ */ jsx3(
829
+ "circle",
830
+ {
831
+ cx: "11",
832
+ cy: "11",
833
+ r: "7",
834
+ stroke: STYLES.textMuted,
835
+ strokeWidth: "2",
836
+ strokeLinecap: "round"
837
+ }
838
+ ),
839
+ /* @__PURE__ */ jsx3(
840
+ "path",
841
+ {
842
+ d: "M21 21l-4.35-4.35",
843
+ stroke: STYLES.textMuted,
844
+ strokeWidth: "2",
845
+ strokeLinecap: "round"
846
+ }
847
+ )
848
+ ]
849
+ }
850
+ );
851
+ }
852
+ function ClearIcon() {
853
+ return /* @__PURE__ */ jsx3("svg", { width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx3(
854
+ "path",
855
+ {
856
+ d: "M18 6L6 18M6 6l12 12",
857
+ stroke: "currentColor",
858
+ strokeWidth: "3",
859
+ strokeLinecap: "round",
860
+ strokeLinejoin: "round"
861
+ }
862
+ ) });
863
+ }
821
864
 
822
865
  // src/components/ui-lint/ScanPanelStack.tsx
823
866
  import { jsx as jsx4 } from "react/jsx-runtime";
@@ -858,10 +901,464 @@ function ScanPanelStack({ show, onClose }) {
858
901
 
859
902
  // src/components/ui-lint/UILintToolbar.tsx
860
903
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
904
+ var TOKENS = {
905
+ // Colors
906
+ bgBase: "rgba(15, 15, 15, 0.85)",
907
+ bgElevated: "rgba(25, 25, 25, 0.95)",
908
+ bgHover: "rgba(255, 255, 255, 0.08)",
909
+ bgActive: "rgba(255, 255, 255, 0.12)",
910
+ border: "rgba(255, 255, 255, 0.1)",
911
+ borderFocus: "rgba(99, 179, 237, 0.6)",
912
+ textPrimary: "rgba(255, 255, 255, 0.95)",
913
+ textSecondary: "rgba(255, 255, 255, 0.7)",
914
+ textMuted: "rgba(255, 255, 255, 0.4)",
915
+ textDisabled: "rgba(255, 255, 255, 0.25)",
916
+ accent: "#63b3ed",
917
+ // Calm blue
918
+ success: "#68d391",
919
+ // Soft green
920
+ warning: "#f6ad55",
921
+ // Warm orange
922
+ // Typography
923
+ fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`,
924
+ fontMono: `"SF Mono", Monaco, "Cascadia Code", monospace`,
925
+ // Sizing
926
+ pillHeight: "44px",
927
+ // Touch-friendly
928
+ pillRadius: "22px",
929
+ buttonMinWidth: "44px",
930
+ // Touch target minimum
931
+ // Effects
932
+ blur: "blur(20px)",
933
+ shadowSm: "0 2px 8px rgba(0, 0, 0, 0.3)",
934
+ shadowMd: "0 4px 20px rgba(0, 0, 0, 0.4)",
935
+ shadowGlow: (color) => `0 0 20px ${color}`,
936
+ // Animation
937
+ transitionFast: "150ms cubic-bezier(0.4, 0, 0.2, 1)",
938
+ transitionBase: "200ms cubic-bezier(0.4, 0, 0.2, 1)",
939
+ transitionSlow: "300ms cubic-bezier(0.4, 0, 0.2, 1)"
940
+ };
941
+ var Icons = {
942
+ Eye: () => /* @__PURE__ */ jsxs4(
943
+ "svg",
944
+ {
945
+ width: "18",
946
+ height: "18",
947
+ viewBox: "0 0 24 24",
948
+ fill: "none",
949
+ stroke: "currentColor",
950
+ strokeWidth: "2",
951
+ strokeLinecap: "round",
952
+ strokeLinejoin: "round",
953
+ children: [
954
+ /* @__PURE__ */ jsx5("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
955
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "3" })
956
+ ]
957
+ }
958
+ ),
959
+ EyeOff: () => /* @__PURE__ */ jsxs4(
960
+ "svg",
961
+ {
962
+ width: "18",
963
+ height: "18",
964
+ viewBox: "0 0 24 24",
965
+ fill: "none",
966
+ stroke: "currentColor",
967
+ strokeWidth: "2",
968
+ strokeLinecap: "round",
969
+ strokeLinejoin: "round",
970
+ children: [
971
+ /* @__PURE__ */ jsx5("path", { d: "M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" }),
972
+ /* @__PURE__ */ jsx5("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
973
+ ]
974
+ }
975
+ ),
976
+ Scan: () => /* @__PURE__ */ jsxs4(
977
+ "svg",
978
+ {
979
+ width: "16",
980
+ height: "16",
981
+ viewBox: "0 0 24 24",
982
+ fill: "none",
983
+ stroke: "currentColor",
984
+ strokeWidth: "2",
985
+ strokeLinecap: "round",
986
+ strokeLinejoin: "round",
987
+ children: [
988
+ /* @__PURE__ */ jsx5("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }),
989
+ /* @__PURE__ */ jsx5("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }),
990
+ /* @__PURE__ */ jsx5("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }),
991
+ /* @__PURE__ */ jsx5("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" }),
992
+ /* @__PURE__ */ jsx5("line", { x1: "7", y1: "12", x2: "17", y2: "12" })
993
+ ]
994
+ }
995
+ ),
996
+ Check: () => /* @__PURE__ */ jsx5(
997
+ "svg",
998
+ {
999
+ width: "14",
1000
+ height: "14",
1001
+ viewBox: "0 0 24 24",
1002
+ fill: "none",
1003
+ stroke: "currentColor",
1004
+ strokeWidth: "2.5",
1005
+ strokeLinecap: "round",
1006
+ strokeLinejoin: "round",
1007
+ children: /* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" })
1008
+ }
1009
+ ),
1010
+ AlertTriangle: () => /* @__PURE__ */ jsxs4(
1011
+ "svg",
1012
+ {
1013
+ width: "14",
1014
+ height: "14",
1015
+ viewBox: "0 0 24 24",
1016
+ fill: "none",
1017
+ stroke: "currentColor",
1018
+ strokeWidth: "2",
1019
+ strokeLinecap: "round",
1020
+ strokeLinejoin: "round",
1021
+ children: [
1022
+ /* @__PURE__ */ jsx5("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
1023
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
1024
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
1025
+ ]
1026
+ }
1027
+ ),
1028
+ Settings: () => /* @__PURE__ */ jsxs4(
1029
+ "svg",
1030
+ {
1031
+ width: "16",
1032
+ height: "16",
1033
+ viewBox: "0 0 24 24",
1034
+ fill: "none",
1035
+ stroke: "currentColor",
1036
+ strokeWidth: "2",
1037
+ strokeLinecap: "round",
1038
+ strokeLinejoin: "round",
1039
+ children: [
1040
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "3" }),
1041
+ /* @__PURE__ */ jsx5("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" })
1042
+ ]
1043
+ }
1044
+ ),
1045
+ ChevronRight: () => /* @__PURE__ */ jsx5(
1046
+ "svg",
1047
+ {
1048
+ width: "14",
1049
+ height: "14",
1050
+ viewBox: "0 0 24 24",
1051
+ fill: "none",
1052
+ stroke: "currentColor",
1053
+ strokeWidth: "2",
1054
+ strokeLinecap: "round",
1055
+ strokeLinejoin: "round",
1056
+ children: /* @__PURE__ */ jsx5("polyline", { points: "9 18 15 12 9 6" })
1057
+ }
1058
+ )
1059
+ };
1060
+ var globalStyles = `
1061
+ @keyframes uilint-fade-in {
1062
+ from { opacity: 0; transform: translateY(8px) scale(0.98); }
1063
+ to { opacity: 1; transform: translateY(0) scale(1); }
1064
+ }
1065
+
1066
+ @keyframes uilint-fade-out {
1067
+ from { opacity: 1; transform: translateY(0) scale(1); }
1068
+ to { opacity: 0; transform: translateY(8px) scale(0.98); }
1069
+ }
1070
+
1071
+ @keyframes uilint-pulse {
1072
+ 0%, 100% { opacity: 1; }
1073
+ 50% { opacity: 0.5; }
1074
+ }
1075
+
1076
+ @keyframes uilint-scan-line {
1077
+ 0% { transform: translateX(-100%); }
1078
+ 100% { transform: translateX(100%); }
1079
+ }
1080
+
1081
+ @keyframes uilint-spin {
1082
+ from { transform: rotate(0deg); }
1083
+ to { transform: rotate(360deg); }
1084
+ }
1085
+
1086
+ .uilint-toolbar-btn {
1087
+ display: flex;
1088
+ align-items: center;
1089
+ justify-content: center;
1090
+ height: 100%;
1091
+ border: none;
1092
+ background: transparent;
1093
+ color: ${TOKENS.textSecondary};
1094
+ cursor: pointer;
1095
+ transition:
1096
+ background-color ${TOKENS.transitionFast},
1097
+ color ${TOKENS.transitionFast},
1098
+ transform ${TOKENS.transitionFast};
1099
+ outline: none;
1100
+ position: relative;
1101
+ }
1102
+
1103
+ .uilint-toolbar-btn:hover:not(:disabled) {
1104
+ background: ${TOKENS.bgHover};
1105
+ color: ${TOKENS.textPrimary};
1106
+ }
1107
+
1108
+ .uilint-toolbar-btn:active:not(:disabled) {
1109
+ background: ${TOKENS.bgActive};
1110
+ transform: scale(0.97);
1111
+ }
1112
+
1113
+ .uilint-toolbar-btn:focus-visible {
1114
+ box-shadow: inset 0 0 0 2px ${TOKENS.borderFocus};
1115
+ }
1116
+
1117
+ .uilint-toolbar-btn:disabled {
1118
+ cursor: not-allowed;
1119
+ color: ${TOKENS.textDisabled};
1120
+ }
1121
+
1122
+ .uilint-toolbar-btn--active {
1123
+ background: ${TOKENS.bgActive} !important;
1124
+ color: ${TOKENS.accent} !important;
1125
+ }
1126
+
1127
+ .uilint-toolbar-btn--warning {
1128
+ color: ${TOKENS.warning} !important;
1129
+ }
1130
+
1131
+ .uilint-toolbar-btn--success {
1132
+ color: ${TOKENS.success} !important;
1133
+ }
1134
+
1135
+ .uilint-hint {
1136
+ opacity: 0;
1137
+ transform: translateY(4px);
1138
+ transition:
1139
+ opacity ${TOKENS.transitionBase},
1140
+ transform ${TOKENS.transitionBase};
1141
+ pointer-events: none;
1142
+ }
1143
+
1144
+ .uilint-hint--visible {
1145
+ opacity: 1;
1146
+ transform: translateY(0);
1147
+ }
1148
+
1149
+ .uilint-badge {
1150
+ display: inline-flex;
1151
+ align-items: center;
1152
+ justify-content: center;
1153
+ min-width: 20px;
1154
+ height: 20px;
1155
+ padding: 0 6px;
1156
+ border-radius: 10px;
1157
+ font-size: 11px;
1158
+ font-weight: 600;
1159
+ font-family: ${TOKENS.fontMono};
1160
+ letter-spacing: -0.02em;
1161
+ }
1162
+
1163
+ .uilint-scanning-indicator {
1164
+ position: relative;
1165
+ overflow: hidden;
1166
+ }
1167
+
1168
+ .uilint-scanning-indicator::after {
1169
+ content: '';
1170
+ position: absolute;
1171
+ top: 0;
1172
+ left: 0;
1173
+ right: 0;
1174
+ bottom: 0;
1175
+ background: linear-gradient(
1176
+ 90deg,
1177
+ transparent,
1178
+ rgba(99, 179, 237, 0.3),
1179
+ transparent
1180
+ );
1181
+ animation: uilint-scan-line 1.5s ease-in-out infinite;
1182
+ }
1183
+
1184
+ .uilint-popover {
1185
+ animation: uilint-fade-in ${TOKENS.transitionSlow} forwards;
1186
+ }
1187
+
1188
+ .uilint-popover--closing {
1189
+ animation: uilint-fade-out ${TOKENS.transitionBase} forwards;
1190
+ }
1191
+ `;
1192
+ function ToolbarButton({
1193
+ onClick,
1194
+ disabled,
1195
+ active,
1196
+ variant = "default",
1197
+ title,
1198
+ ariaLabel,
1199
+ width = TOKENS.buttonMinWidth,
1200
+ children
1201
+ }) {
1202
+ const classes = [
1203
+ "uilint-toolbar-btn",
1204
+ active && "uilint-toolbar-btn--active",
1205
+ variant === "warning" && "uilint-toolbar-btn--warning",
1206
+ variant === "success" && "uilint-toolbar-btn--success"
1207
+ ].filter(Boolean).join(" ");
1208
+ return /* @__PURE__ */ jsx5(
1209
+ "button",
1210
+ {
1211
+ className: classes,
1212
+ onClick,
1213
+ disabled,
1214
+ title,
1215
+ "aria-label": ariaLabel,
1216
+ "aria-pressed": active,
1217
+ style: { minWidth: width },
1218
+ children
1219
+ }
1220
+ );
1221
+ }
1222
+ function Divider() {
1223
+ return /* @__PURE__ */ jsx5(
1224
+ "div",
1225
+ {
1226
+ style: {
1227
+ width: "1px",
1228
+ height: "20px",
1229
+ backgroundColor: TOKENS.border,
1230
+ flexShrink: 0
1231
+ },
1232
+ "aria-hidden": "true"
1233
+ }
1234
+ );
1235
+ }
1236
+ function ScanStatus({ status, issueCount, enabled }) {
1237
+ if (!enabled) {
1238
+ return /* @__PURE__ */ jsx5(
1239
+ "span",
1240
+ {
1241
+ style: {
1242
+ fontSize: "12px",
1243
+ color: TOKENS.textDisabled,
1244
+ fontStyle: "italic"
1245
+ },
1246
+ children: "Off"
1247
+ }
1248
+ );
1249
+ }
1250
+ if (status === "scanning" || status === "paused") {
1251
+ return /* @__PURE__ */ jsxs4(
1252
+ "span",
1253
+ {
1254
+ className: "uilint-scanning-indicator",
1255
+ style: {
1256
+ display: "flex",
1257
+ alignItems: "center",
1258
+ gap: "6px",
1259
+ padding: "0 4px"
1260
+ },
1261
+ children: [
1262
+ /* @__PURE__ */ jsx5(Icons.Scan, {}),
1263
+ /* @__PURE__ */ jsx5(
1264
+ "span",
1265
+ {
1266
+ style: {
1267
+ fontSize: "12px",
1268
+ fontFamily: TOKENS.fontMono,
1269
+ animation: `uilint-pulse 1s ease-in-out infinite`
1270
+ },
1271
+ children: "Scanning"
1272
+ }
1273
+ )
1274
+ ]
1275
+ }
1276
+ );
1277
+ }
1278
+ if (issueCount === 0) {
1279
+ return /* @__PURE__ */ jsxs4(
1280
+ "span",
1281
+ {
1282
+ style: {
1283
+ display: "flex",
1284
+ alignItems: "center",
1285
+ gap: "5px"
1286
+ },
1287
+ children: [
1288
+ /* @__PURE__ */ jsx5(
1289
+ "span",
1290
+ {
1291
+ style: {
1292
+ display: "flex",
1293
+ alignItems: "center",
1294
+ justifyContent: "center",
1295
+ width: "18px",
1296
+ height: "18px",
1297
+ borderRadius: "50%",
1298
+ backgroundColor: `${TOKENS.success}20`,
1299
+ color: TOKENS.success
1300
+ },
1301
+ children: /* @__PURE__ */ jsx5(Icons.Check, {})
1302
+ }
1303
+ ),
1304
+ /* @__PURE__ */ jsx5(
1305
+ "span",
1306
+ {
1307
+ style: {
1308
+ fontSize: "12px",
1309
+ fontWeight: 500,
1310
+ color: TOKENS.success
1311
+ },
1312
+ children: "Clear"
1313
+ }
1314
+ )
1315
+ ]
1316
+ }
1317
+ );
1318
+ }
1319
+ return /* @__PURE__ */ jsxs4(
1320
+ "span",
1321
+ {
1322
+ style: {
1323
+ display: "flex",
1324
+ alignItems: "center",
1325
+ gap: "5px"
1326
+ },
1327
+ children: [
1328
+ /* @__PURE__ */ jsx5(
1329
+ "span",
1330
+ {
1331
+ style: {
1332
+ display: "flex",
1333
+ alignItems: "center",
1334
+ justifyContent: "center",
1335
+ width: "18px",
1336
+ height: "18px",
1337
+ borderRadius: "50%",
1338
+ backgroundColor: `${TOKENS.warning}20`,
1339
+ color: TOKENS.warning
1340
+ },
1341
+ children: /* @__PURE__ */ jsx5(Icons.AlertTriangle, {})
1342
+ }
1343
+ ),
1344
+ /* @__PURE__ */ jsx5(
1345
+ "span",
1346
+ {
1347
+ className: "uilint-badge",
1348
+ style: {
1349
+ backgroundColor: `${TOKENS.warning}20`,
1350
+ color: TOKENS.warning
1351
+ },
1352
+ children: issueCount
1353
+ }
1354
+ )
1355
+ ]
1356
+ }
1357
+ );
1358
+ }
861
1359
  function UILintToolbar() {
862
1360
  const {
863
1361
  settings,
864
- inspectedElement,
865
1362
  liveScanEnabled,
866
1363
  autoScanState,
867
1364
  enableLiveScan,
@@ -873,25 +1370,74 @@ function UILintToolbar() {
873
1370
  const [showSettings, setShowSettings] = useState2(false);
874
1371
  const [showResults, setShowResults] = useState2(false);
875
1372
  const [mounted, setMounted] = useState2(false);
1373
+ const [settingsClosing, setSettingsClosing] = useState2(false);
1374
+ const [nextjsOverlayVisible, setNextjsOverlayVisible] = useState2(false);
876
1375
  const toolbarRef = useRef2(null);
877
1376
  const settingsRef = useRef2(null);
1377
+ useEffect2(() => {
1378
+ const checkForNextOverlay = () => {
1379
+ const overlaySelectors = [
1380
+ "nextjs-portal",
1381
+ "[data-nextjs-dialog]",
1382
+ "[data-nextjs-dialog-overlay]",
1383
+ "#__next-build-watcher",
1384
+ "[data-nextjs-toast]"
1385
+ ];
1386
+ const hasOverlay = overlaySelectors.some((selector) => {
1387
+ const el = document.querySelector(selector);
1388
+ if (!el) return false;
1389
+ const style = window.getComputedStyle(el);
1390
+ return style.display !== "none" && style.visibility !== "hidden";
1391
+ });
1392
+ setNextjsOverlayVisible(hasOverlay);
1393
+ };
1394
+ checkForNextOverlay();
1395
+ const observer = new MutationObserver(checkForNextOverlay);
1396
+ observer.observe(document.body, {
1397
+ childList: true,
1398
+ subtree: true,
1399
+ attributes: true,
1400
+ attributeFilter: ["style", "class"]
1401
+ });
1402
+ return () => observer.disconnect();
1403
+ }, []);
1404
+ const isScanning = autoScanState.status === "scanning";
1405
+ const isComplete = autoScanState.status === "complete";
1406
+ let totalIssues = 0;
1407
+ elementIssuesCache.forEach((el) => {
1408
+ totalIssues += el.issues.length;
1409
+ });
1410
+ const hasIssues = totalIssues > 0;
878
1411
  useEffect2(() => {
879
1412
  setMounted(true);
880
1413
  }, []);
881
1414
  useEffect2(() => {
882
1415
  const handleClickOutside = (e) => {
883
1416
  const target = e.target;
884
- if (showSettings && settingsRef.current) {
885
- if (!settingsRef.current.contains(target) && !toolbarRef.current?.contains(target)) {
886
- setShowSettings(false);
1417
+ if (showSettings && settingsRef.current && toolbarRef.current) {
1418
+ if (!settingsRef.current.contains(target) && !toolbarRef.current.contains(target)) {
1419
+ handleCloseSettings();
887
1420
  }
888
1421
  }
889
1422
  };
890
- if (showSettings) {
891
- document.addEventListener("mousedown", handleClickOutside);
892
- return () => document.removeEventListener("mousedown", handleClickOutside);
1423
+ const handleEscape = (e) => {
1424
+ if (e.key === "Escape") {
1425
+ if (showSettings) handleCloseSettings();
1426
+ if (showResults) setShowResults(false);
1427
+ }
1428
+ };
1429
+ document.addEventListener("mousedown", handleClickOutside);
1430
+ document.addEventListener("keydown", handleEscape);
1431
+ return () => {
1432
+ document.removeEventListener("mousedown", handleClickOutside);
1433
+ document.removeEventListener("keydown", handleEscape);
1434
+ };
1435
+ }, [showSettings, showResults]);
1436
+ useEffect2(() => {
1437
+ if (hasIssues && liveScanEnabled && isComplete && !showSettings) {
1438
+ setShowResults(true);
893
1439
  }
894
- }, [showSettings]);
1440
+ }, [hasIssues, liveScanEnabled, isComplete, showSettings]);
895
1441
  const handleToggleClick = useCallback2(() => {
896
1442
  if (liveScanEnabled) {
897
1443
  disableLiveScan();
@@ -899,87 +1445,85 @@ function UILintToolbar() {
899
1445
  } else {
900
1446
  enableLiveScan();
901
1447
  }
902
- setShowSettings(false);
903
- }, [liveScanEnabled, enableLiveScan, disableLiveScan]);
1448
+ if (showSettings) handleCloseSettings();
1449
+ }, [liveScanEnabled, enableLiveScan, disableLiveScan, showSettings]);
904
1450
  const handleIssuesClick = useCallback2(() => {
905
- if (!liveScanEnabled) return;
906
- setShowResults(!showResults);
907
- setShowSettings(false);
908
- }, [liveScanEnabled, showResults]);
909
- const handleSettingsClick = useCallback2(() => {
910
- setShowSettings(!showSettings);
911
- setShowResults(false);
912
- }, [showSettings]);
913
- if (!mounted) return null;
914
- const isScanning = autoScanState.status === "scanning";
915
- const isComplete = autoScanState.status === "complete";
916
- let totalIssues = 0;
917
- elementIssuesCache.forEach((el) => {
918
- totalIssues += el.issues.length;
919
- });
920
- const hasIssues = totalIssues > 0;
921
- const getToggleContent = () => {
922
- if (isScanning) {
923
- return /* @__PURE__ */ jsx5(SpinnerIcon, {});
924
- }
925
- if (liveScanEnabled) {
926
- return /* @__PURE__ */ jsx5(EyeIcon, {});
927
- }
928
- return /* @__PURE__ */ jsx5(EyeOffIcon, {});
929
- };
930
- const getIssuesContent = () => {
931
1451
  if (!liveScanEnabled) {
932
- return /* @__PURE__ */ jsx5("span", { style: { opacity: 0.5 }, children: "--" });
933
- }
934
- if (isScanning) {
935
- return /* @__PURE__ */ jsx5("span", { style: { opacity: 0.7 }, children: "..." });
1452
+ enableLiveScan();
1453
+ return;
936
1454
  }
937
- if (hasIssues) {
938
- return /* @__PURE__ */ jsxs4("span", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
939
- /* @__PURE__ */ jsx5(WarningIcon, {}),
940
- /* @__PURE__ */ jsx5("span", { children: totalIssues })
941
- ] });
1455
+ setShowResults((prev) => !prev);
1456
+ }, [liveScanEnabled, enableLiveScan]);
1457
+ const handleSettingsClick = useCallback2(() => {
1458
+ if (showSettings) {
1459
+ handleCloseSettings();
1460
+ } else {
1461
+ setShowSettings(true);
1462
+ setShowResults(false);
942
1463
  }
943
- return /* @__PURE__ */ jsxs4("span", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
944
- /* @__PURE__ */ jsx5(CheckCircleIcon, {}),
945
- /* @__PURE__ */ jsx5("span", { children: "0" })
946
- ] });
947
- };
1464
+ }, [showSettings]);
1465
+ const handleCloseSettings = useCallback2(() => {
1466
+ setSettingsClosing(true);
1467
+ setTimeout(() => {
1468
+ setShowSettings(false);
1469
+ setSettingsClosing(false);
1470
+ }, 150);
1471
+ }, []);
1472
+ if (!mounted) return null;
1473
+ const issueVariant = !liveScanEnabled ? "default" : hasIssues ? "warning" : isComplete ? "success" : "default";
1474
+ const bottomPosition = nextjsOverlayVisible ? "80px" : "20px";
948
1475
  const content = /* @__PURE__ */ jsxs4(
949
1476
  "div",
950
1477
  {
951
1478
  "data-ui-lint": true,
952
1479
  style: {
953
1480
  position: "fixed",
954
- bottom: "70px",
1481
+ bottom: bottomPosition,
955
1482
  left: "20px",
956
1483
  zIndex: 99999,
957
- fontFamily: STYLES.font
1484
+ fontFamily: TOKENS.fontFamily,
1485
+ transition: `bottom ${TOKENS.transitionSlow}`
958
1486
  },
959
1487
  children: [
960
- /* @__PURE__ */ jsx5("style", { children: `
961
- @keyframes uilint-fade-in {
962
- from { opacity: 0; transform: translateY(8px); }
963
- to { opacity: 1; transform: translateY(0); }
964
- }
965
- @keyframes uilint-spin {
966
- from { transform: rotate(0deg); }
967
- to { transform: rotate(360deg); }
968
- }
969
- ` }),
1488
+ /* @__PURE__ */ jsx5("style", { children: globalStyles }),
970
1489
  /* @__PURE__ */ jsxs4(
971
1490
  "div",
972
1491
  {
1492
+ className: `uilint-hint ${liveScanEnabled ? "uilint-hint--visible" : ""}`,
973
1493
  style: {
974
1494
  textAlign: "center",
975
- marginBottom: "8px",
1495
+ marginBottom: "10px",
976
1496
  fontSize: "11px",
977
- color: STYLES.textDim,
978
- letterSpacing: "0.01em"
1497
+ color: TOKENS.textMuted,
1498
+ letterSpacing: "0.02em",
1499
+ // Subtle dark halo for readability on any background
1500
+ textShadow: `
1501
+ 0 0 4px rgba(0, 0, 0, 0.8),
1502
+ 0 0 8px rgba(0, 0, 0, 0.5),
1503
+ 0 1px 2px rgba(0, 0, 0, 0.9)
1504
+ `
979
1505
  },
1506
+ "aria-hidden": !liveScanEnabled,
980
1507
  children: [
981
- /* @__PURE__ */ jsx5("span", { style: { color: STYLES.textMuted }, children: "\u2325+Click" }),
982
- " to inspect"
1508
+ /* @__PURE__ */ jsx5(
1509
+ "kbd",
1510
+ {
1511
+ style: {
1512
+ display: "inline-block",
1513
+ padding: "2px 5px",
1514
+ marginRight: "4px",
1515
+ borderRadius: "4px",
1516
+ backgroundColor: TOKENS.bgElevated,
1517
+ border: `1px solid ${TOKENS.border}`,
1518
+ fontSize: "10px",
1519
+ fontFamily: TOKENS.fontMono,
1520
+ color: TOKENS.textSecondary,
1521
+ boxShadow: `0 1px 3px rgba(0, 0, 0, 0.5)`
1522
+ },
1523
+ children: "\u2325"
1524
+ }
1525
+ ),
1526
+ /* @__PURE__ */ jsx5("span", { children: "+ Click to inspect element" })
983
1527
  ]
984
1528
  }
985
1529
  ),
@@ -987,144 +1531,85 @@ function UILintToolbar() {
987
1531
  "div",
988
1532
  {
989
1533
  ref: toolbarRef,
1534
+ role: "toolbar",
1535
+ "aria-label": "UI Lint toolbar",
990
1536
  style: {
991
1537
  position: "relative",
992
1538
  display: "inline-flex",
993
1539
  alignItems: "center",
994
- height: STYLES.pillHeight,
995
- borderRadius: STYLES.pillRadius,
996
- border: `1px solid ${STYLES.border}`,
997
- backgroundColor: STYLES.bg,
998
- backdropFilter: STYLES.blur,
999
- WebkitBackdropFilter: STYLES.blur,
1000
- boxShadow: STYLES.shadow,
1001
- overflow: "hidden"
1540
+ height: TOKENS.pillHeight,
1541
+ borderRadius: TOKENS.pillRadius,
1542
+ border: `1px solid ${TOKENS.border}`,
1543
+ backgroundColor: TOKENS.bgBase,
1544
+ backdropFilter: TOKENS.blur,
1545
+ WebkitBackdropFilter: TOKENS.blur,
1546
+ boxShadow: liveScanEnabled && hasIssues ? `${TOKENS.shadowMd}, ${TOKENS.shadowGlow(
1547
+ `${TOKENS.warning}30`
1548
+ )}` : TOKENS.shadowMd,
1549
+ overflow: "hidden",
1550
+ transition: `box-shadow ${TOKENS.transitionBase}`
1002
1551
  },
1003
1552
  children: [
1004
1553
  /* @__PURE__ */ jsx5(
1005
- "button",
1554
+ ToolbarButton,
1006
1555
  {
1007
1556
  onClick: handleToggleClick,
1008
- style: {
1009
- display: "flex",
1010
- alignItems: "center",
1011
- justifyContent: "center",
1012
- height: "100%",
1013
- width: "40px",
1014
- border: "none",
1015
- backgroundColor: liveScanEnabled ? STYLES.bgSegmentHover : "transparent",
1016
- color: liveScanEnabled ? STYLES.accent : STYLES.textMuted,
1017
- cursor: "pointer",
1018
- transition: STYLES.transition
1019
- },
1020
- onMouseEnter: (e) => {
1021
- if (!liveScanEnabled) {
1022
- e.currentTarget.style.backgroundColor = STYLES.bgSegmentHover;
1023
- e.currentTarget.style.color = STYLES.text;
1024
- }
1025
- },
1026
- onMouseLeave: (e) => {
1027
- if (!liveScanEnabled) {
1028
- e.currentTarget.style.backgroundColor = "transparent";
1029
- e.currentTarget.style.color = STYLES.textMuted;
1030
- }
1031
- },
1032
- title: liveScanEnabled ? "Disable live scanning" : "Enable live scanning",
1033
- children: getToggleContent()
1034
- }
1035
- ),
1036
- /* @__PURE__ */ jsx5(
1037
- "div",
1038
- {
1039
- style: {
1040
- width: "1px",
1041
- height: "20px",
1042
- backgroundColor: STYLES.divider
1043
- }
1557
+ active: liveScanEnabled,
1558
+ title: liveScanEnabled ? "Stop scanning (\u2325S)" : "Start scanning (\u2325S)",
1559
+ ariaLabel: liveScanEnabled ? "Stop live scanning" : "Start live scanning",
1560
+ width: "48px",
1561
+ children: liveScanEnabled ? /* @__PURE__ */ jsx5(Icons.Eye, {}) : /* @__PURE__ */ jsx5(Icons.EyeOff, {})
1044
1562
  }
1045
1563
  ),
1564
+ /* @__PURE__ */ jsx5(Divider, {}),
1046
1565
  /* @__PURE__ */ jsx5(
1047
- "button",
1566
+ ToolbarButton,
1048
1567
  {
1049
1568
  onClick: handleIssuesClick,
1050
- disabled: !liveScanEnabled,
1051
- style: {
1052
- display: "flex",
1053
- alignItems: "center",
1054
- justifyContent: "center",
1055
- gap: "4px",
1056
- height: "100%",
1057
- padding: "0 12px",
1058
- border: "none",
1059
- backgroundColor: showResults && liveScanEnabled ? STYLES.bgSegmentHover : "transparent",
1060
- color: !liveScanEnabled ? STYLES.textDim : hasIssues ? STYLES.warning : isComplete ? STYLES.success : STYLES.text,
1061
- fontSize: "13px",
1062
- fontWeight: 500,
1063
- fontFamily: STYLES.font,
1064
- cursor: liveScanEnabled ? "pointer" : "default",
1065
- transition: STYLES.transition,
1066
- opacity: liveScanEnabled ? 1 : 0.6
1067
- },
1068
- onMouseEnter: (e) => {
1069
- if (liveScanEnabled && !showResults) {
1070
- e.currentTarget.style.backgroundColor = STYLES.bgSegmentHover;
1569
+ active: showResults && liveScanEnabled,
1570
+ variant: issueVariant,
1571
+ title: !liveScanEnabled ? "Click to enable scanning" : `${totalIssues} issue${totalIssues !== 1 ? "s" : ""} found`,
1572
+ ariaLabel: !liveScanEnabled ? "Enable scanning to see issues" : `View ${totalIssues} issues`,
1573
+ width: "auto",
1574
+ children: /* @__PURE__ */ jsx5("span", { style: { padding: "0 12px" }, children: /* @__PURE__ */ jsx5(
1575
+ ScanStatus,
1576
+ {
1577
+ status: autoScanState.status,
1578
+ issueCount: totalIssues,
1579
+ enabled: liveScanEnabled
1071
1580
  }
1072
- },
1073
- onMouseLeave: (e) => {
1074
- if (liveScanEnabled && !showResults) {
1075
- e.currentTarget.style.backgroundColor = "transparent";
1076
- }
1077
- },
1078
- title: liveScanEnabled ? `${totalIssues} issues found` : "Enable scanning to see issues",
1079
- children: getIssuesContent()
1080
- }
1081
- ),
1082
- /* @__PURE__ */ jsx5(
1083
- "div",
1084
- {
1085
- style: {
1086
- width: "1px",
1087
- height: "20px",
1088
- backgroundColor: STYLES.divider
1089
- }
1581
+ ) })
1090
1582
  }
1091
1583
  ),
1584
+ /* @__PURE__ */ jsx5(Divider, {}),
1092
1585
  /* @__PURE__ */ jsx5(
1093
- "button",
1586
+ ToolbarButton,
1094
1587
  {
1095
1588
  onClick: handleSettingsClick,
1096
- style: {
1097
- display: "flex",
1098
- alignItems: "center",
1099
- justifyContent: "center",
1100
- height: "100%",
1101
- width: "40px",
1102
- border: "none",
1103
- backgroundColor: showSettings ? STYLES.bgSegmentHover : "transparent",
1104
- color: showSettings ? STYLES.text : STYLES.textMuted,
1105
- cursor: "pointer",
1106
- transition: STYLES.transition
1107
- },
1108
- onMouseEnter: (e) => {
1109
- if (!showSettings) {
1110
- e.currentTarget.style.backgroundColor = STYLES.bgSegmentHover;
1111
- e.currentTarget.style.color = STYLES.text;
1112
- }
1113
- },
1114
- onMouseLeave: (e) => {
1115
- if (!showSettings) {
1116
- e.currentTarget.style.backgroundColor = "transparent";
1117
- e.currentTarget.style.color = STYLES.textMuted;
1118
- }
1119
- },
1589
+ active: showSettings,
1120
1590
  title: "Settings",
1121
- children: /* @__PURE__ */ jsx5(EllipsisIcon, {})
1591
+ ariaLabel: "Open settings",
1592
+ width: "44px",
1593
+ children: /* @__PURE__ */ jsx5(Icons.Settings, {})
1122
1594
  }
1123
1595
  )
1124
1596
  ]
1125
1597
  }
1126
1598
  ),
1127
- showSettings && /* @__PURE__ */ jsx5("div", { ref: settingsRef, children: /* @__PURE__ */ jsx5(SettingsPopover, { settings }) }),
1599
+ showSettings && /* @__PURE__ */ jsx5(
1600
+ "div",
1601
+ {
1602
+ ref: settingsRef,
1603
+ className: `uilint-popover ${settingsClosing ? "uilint-popover--closing" : ""}`,
1604
+ style: {
1605
+ position: "absolute",
1606
+ bottom: "100%",
1607
+ left: 0,
1608
+ marginBottom: "8px"
1609
+ },
1610
+ children: /* @__PURE__ */ jsx5(SettingsPopover, { settings })
1611
+ }
1612
+ ),
1128
1613
  /* @__PURE__ */ jsx5(
1129
1614
  ScanPanelStack,
1130
1615
  {