uilint-react 0.2.1 → 0.2.4

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 (120) hide show
  1. package/dist/DevTool.d.ts +11 -0
  2. package/dist/DevTool.d.ts.map +1 -0
  3. package/dist/ElementBadges-BNRIjtRW.js +672 -0
  4. package/dist/ElementBadges-BNRIjtRW.js.map +1 -0
  5. package/dist/VisionIssueBadge-C6vfwtec.js +154 -0
  6. package/dist/VisionIssueBadge-C6vfwtec.js.map +1 -0
  7. package/dist/components/Highlighter.d.ts +6 -0
  8. package/dist/components/Highlighter.d.ts.map +1 -0
  9. package/dist/components/ui-lint/ElementBadges.d.ts +6 -0
  10. package/dist/components/ui-lint/ElementBadges.d.ts.map +1 -0
  11. package/dist/components/ui-lint/InspectionPanel.d.ts +6 -0
  12. package/dist/components/ui-lint/InspectionPanel.d.ts.map +1 -0
  13. package/dist/components/ui-lint/LocatorOverlay.d.ts +14 -0
  14. package/dist/components/ui-lint/LocatorOverlay.d.ts.map +1 -0
  15. package/dist/components/ui-lint/RegionSelector.d.ts +18 -0
  16. package/dist/components/ui-lint/RegionSelector.d.ts.map +1 -0
  17. package/dist/components/ui-lint/ScanResultsPopover.d.ts +6 -0
  18. package/dist/components/ui-lint/ScanResultsPopover.d.ts.map +1 -0
  19. package/dist/components/ui-lint/ScreenshotViewer.d.ts +11 -0
  20. package/dist/components/ui-lint/ScreenshotViewer.d.ts.map +1 -0
  21. package/dist/components/ui-lint/UILintProvider.d.ts +11 -0
  22. package/dist/components/ui-lint/UILintProvider.d.ts.map +1 -0
  23. package/dist/components/ui-lint/VisionIssueBadge.d.ts +6 -0
  24. package/dist/components/ui-lint/VisionIssueBadge.d.ts.map +1 -0
  25. package/dist/components/ui-lint/VisionIssuesPanel.d.ts +12 -0
  26. package/dist/components/ui-lint/VisionIssuesPanel.d.ts.map +1 -0
  27. package/dist/components/ui-lint/badge-layout.d.ts +91 -0
  28. package/dist/components/ui-lint/badge-layout.d.ts.map +1 -0
  29. package/dist/components/ui-lint/code-formatting.d.ts +14 -0
  30. package/dist/components/ui-lint/code-formatting.d.ts.map +1 -0
  31. package/dist/components/ui-lint/dom-utils.d.ts +43 -0
  32. package/dist/components/ui-lint/dom-utils.d.ts.map +1 -0
  33. package/dist/components/ui-lint/index.d.ts +22 -0
  34. package/dist/components/ui-lint/index.d.ts.map +1 -0
  35. package/dist/components/ui-lint/inspection-panel-positioning.d.ts +41 -0
  36. package/dist/components/ui-lint/inspection-panel-positioning.d.ts.map +1 -0
  37. package/dist/components/ui-lint/portal-host.d.ts +3 -0
  38. package/dist/components/ui-lint/portal-host.d.ts.map +1 -0
  39. package/dist/components/ui-lint/scan-results/FileTree.d.ts +25 -0
  40. package/dist/components/ui-lint/scan-results/FileTree.d.ts.map +1 -0
  41. package/dist/components/ui-lint/scan-results/IssueRow.d.ts +16 -0
  42. package/dist/components/ui-lint/scan-results/IssueRow.d.ts.map +1 -0
  43. package/dist/components/ui-lint/scan-results/SearchFilter.d.ts +8 -0
  44. package/dist/components/ui-lint/scan-results/SearchFilter.d.ts.map +1 -0
  45. package/dist/components/ui-lint/source-fetcher.d.ts +40 -0
  46. package/dist/components/ui-lint/source-fetcher.d.ts.map +1 -0
  47. package/dist/components/ui-lint/store.d.ts +194 -0
  48. package/dist/components/ui-lint/store.d.ts.map +1 -0
  49. package/dist/components/ui-lint/toolbar/TabbedToolbar.d.ts +2 -0
  50. package/dist/components/ui-lint/toolbar/TabbedToolbar.d.ts.map +1 -0
  51. package/dist/components/ui-lint/toolbar/icons.d.ts +29 -0
  52. package/dist/components/ui-lint/toolbar/icons.d.ts.map +1 -0
  53. package/dist/components/ui-lint/toolbar/index.d.ts +3 -0
  54. package/dist/components/ui-lint/toolbar/index.d.ts.map +1 -0
  55. package/dist/components/ui-lint/toolbar/tabs/ConfigureTab.d.ts +2 -0
  56. package/dist/components/ui-lint/toolbar/tabs/ConfigureTab.d.ts.map +1 -0
  57. package/dist/components/ui-lint/toolbar/tabs/ESLintTab.d.ts +2 -0
  58. package/dist/components/ui-lint/toolbar/tabs/ESLintTab.d.ts.map +1 -0
  59. package/dist/components/ui-lint/toolbar/tabs/VisionTab.d.ts +2 -0
  60. package/dist/components/ui-lint/toolbar/tabs/VisionTab.d.ts.map +1 -0
  61. package/dist/components/ui-lint/toolbar/tokens.d.ts +45 -0
  62. package/dist/components/ui-lint/toolbar/tokens.d.ts.map +1 -0
  63. package/dist/components/ui-lint/types.d.ts +170 -0
  64. package/dist/components/ui-lint/types.d.ts.map +1 -0
  65. package/dist/components/ui-lint/useDOMObserver.d.ts +11 -0
  66. package/dist/components/ui-lint/useDOMObserver.d.ts.map +1 -0
  67. package/dist/components/ui-lint/visibility-utils.d.ts +41 -0
  68. package/dist/components/ui-lint/visibility-utils.d.ts.map +1 -0
  69. package/dist/consistency/highlights.d.ts +14 -0
  70. package/dist/consistency/highlights.d.ts.map +1 -0
  71. package/dist/consistency/index.d.ts +7 -0
  72. package/dist/consistency/index.d.ts.map +1 -0
  73. package/dist/consistency/snapshot.d.ts +14 -0
  74. package/dist/consistency/snapshot.d.ts.map +1 -0
  75. package/dist/consistency/types.d.ts +5 -0
  76. package/dist/consistency/types.d.ts.map +1 -0
  77. package/dist/devtools.d.ts +8 -0
  78. package/dist/devtools.d.ts.map +1 -0
  79. package/dist/devtools.js +259 -0
  80. package/dist/devtools.js.map +1 -0
  81. package/dist/environment-DVxa60C6.js +26 -0
  82. package/dist/environment-DVxa60C6.js.map +1 -0
  83. package/dist/index-BGzkrD0y.js +12304 -0
  84. package/dist/index-BGzkrD0y.js.map +1 -0
  85. package/dist/index-L3Zm-CoX.js +513 -0
  86. package/dist/index-L3Zm-CoX.js.map +1 -0
  87. package/dist/index.d.ts +13 -339
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +43 -482
  90. package/dist/index.js.map +1 -0
  91. package/dist/lib/utils.d.ts +11 -0
  92. package/dist/lib/utils.d.ts.map +1 -0
  93. package/dist/node.d.ts +7 -38
  94. package/dist/node.d.ts.map +1 -0
  95. package/dist/node.js +32 -79
  96. package/dist/node.js.map +1 -0
  97. package/dist/scanner/dom-scanner.d.ts +7 -0
  98. package/dist/scanner/dom-scanner.d.ts.map +1 -0
  99. package/dist/scanner/environment.d.ts +16 -0
  100. package/dist/scanner/environment.d.ts.map +1 -0
  101. package/dist/scanner/jsdom-adapter.d.ts +30 -0
  102. package/dist/scanner/jsdom-adapter.d.ts.map +1 -0
  103. package/dist/scanner/vision-capture.d.ts +131 -0
  104. package/dist/scanner/vision-capture.d.ts.map +1 -0
  105. package/dist/styles/inject-styles.d.ts +2 -0
  106. package/dist/styles/inject-styles.d.ts.map +1 -0
  107. package/dist/vision-capture-EIrYA_XF.js +216 -0
  108. package/dist/vision-capture-EIrYA_XF.js.map +1 -0
  109. package/dist/web-component.d.ts +2 -0
  110. package/dist/web-component.d.ts.map +1 -0
  111. package/package.json +36 -7
  112. package/dist/ElementBadges-2CTPMJ6L.js +0 -825
  113. package/dist/InspectionPanel-NXSE7CMH.js +0 -10
  114. package/dist/LocatorOverlay-3TKK74BD.js +0 -11
  115. package/dist/UILintToolbar-CLVXQHCZ.js +0 -10
  116. package/dist/chunk-552GIJIQ.js +0 -1109
  117. package/dist/chunk-K46BWHFU.js +0 -271
  118. package/dist/chunk-KKNPFLL2.js +0 -1853
  119. package/dist/chunk-S4IWHBOQ.js +0 -178
  120. package/dist/chunk-Z4AAGFIN.js +0 -933
@@ -1,1853 +0,0 @@
1
- "use client";
2
- import {
3
- BADGE_COLORS,
4
- Badge,
5
- fetchSource,
6
- getCachedSource
7
- } from "./chunk-S4IWHBOQ.js";
8
- import {
9
- groupBySourceFile,
10
- useUILintContext,
11
- useUILintStore
12
- } from "./chunk-552GIJIQ.js";
13
-
14
- // src/components/ui-lint/UILintToolbar.tsx
15
- import React3, { useState as useState2, useRef as useRef2, useEffect as useEffect3, useCallback as useCallback2 } from "react";
16
- import { createPortal } from "react-dom";
17
-
18
- // src/components/ui-lint/toolbar-styles.ts
19
- var STYLES = {
20
- // Backgrounds
21
- bg: "rgba(17, 24, 39, 0.92)",
22
- bgHover: "rgba(31, 41, 55, 0.95)",
23
- bgSegment: "rgba(31, 41, 55, 0.6)",
24
- bgSegmentHover: "rgba(55, 65, 81, 0.8)",
25
- bgPopover: "rgba(17, 24, 39, 0.96)",
26
- // Borders
27
- border: "rgba(75, 85, 99, 0.5)",
28
- borderLight: "rgba(107, 114, 128, 0.3)",
29
- divider: "rgba(75, 85, 99, 0.4)",
30
- // Text
31
- text: "#F9FAFB",
32
- textMuted: "#9CA3AF",
33
- textDim: "#6B7280",
34
- // Accent colors
35
- accent: "#3B82F6",
36
- accentHover: "#2563EB",
37
- accentLight: "rgba(59, 130, 246, 0.15)",
38
- // Status colors
39
- success: "#10B981",
40
- successLight: "rgba(16, 185, 129, 0.15)",
41
- warning: "#F59E0B",
42
- warningLight: "rgba(245, 158, 11, 0.15)",
43
- error: "#EF4444",
44
- errorLight: "rgba(239, 68, 68, 0.15)",
45
- // Badge colors
46
- badgeBg: "#3B82F6",
47
- badgeText: "#FFFFFF",
48
- // Effects
49
- shadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
50
- shadowLg: "0 12px 48px rgba(0, 0, 0, 0.5)",
51
- blur: "blur(12px)",
52
- // Typography
53
- font: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
54
- fontMono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
55
- // Sizing
56
- pillHeight: "40px",
57
- pillRadius: "20px",
58
- popoverRadius: "12px",
59
- buttonRadius: "8px",
60
- // Transitions
61
- transition: "all 0.15s ease-out",
62
- transitionFast: "all 0.1s ease-out"
63
- };
64
-
65
- // src/components/ui-lint/SettingsPopover.tsx
66
- import { jsx, jsxs } from "react/jsx-runtime";
67
- function SettingsPopover({ settings }) {
68
- const wsConnected = useUILintStore((s) => s.wsConnected);
69
- const wsUrl = useUILintStore((s) => s.wsUrl);
70
- const connectWebSocket = useUILintStore(
71
- (s) => s.connectWebSocket
72
- );
73
- const disconnectWebSocket = useUILintStore(
74
- (s) => s.disconnectWebSocket
75
- );
76
- return /* @__PURE__ */ jsxs(
77
- "div",
78
- {
79
- style: {
80
- position: "absolute",
81
- bottom: "100%",
82
- left: 0,
83
- marginBottom: "8px",
84
- width: "260px",
85
- padding: "14px",
86
- borderRadius: STYLES.popoverRadius,
87
- border: `1px solid ${STYLES.border}`,
88
- backgroundColor: STYLES.bgPopover,
89
- backdropFilter: STYLES.blur,
90
- WebkitBackdropFilter: STYLES.blur,
91
- boxShadow: STYLES.shadowLg,
92
- animation: "uilint-fade-in 0.15s ease-out"
93
- },
94
- children: [
95
- /* @__PURE__ */ jsx(
96
- "div",
97
- {
98
- style: {
99
- fontSize: "12px",
100
- fontWeight: 600,
101
- color: STYLES.text,
102
- marginBottom: "12px"
103
- },
104
- children: "UILint Settings"
105
- }
106
- ),
107
- /* @__PURE__ */ jsxs(
108
- "div",
109
- {
110
- style: {
111
- padding: "10px 12px",
112
- borderRadius: STYLES.buttonRadius,
113
- border: `1px solid ${STYLES.border}`,
114
- backgroundColor: STYLES.bgSegment
115
- },
116
- children: [
117
- /* @__PURE__ */ jsx(
118
- "div",
119
- {
120
- style: {
121
- display: "flex",
122
- alignItems: "center",
123
- justifyContent: "space-between",
124
- marginBottom: "8px"
125
- },
126
- children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
127
- /* @__PURE__ */ jsx(
128
- "div",
129
- {
130
- style: {
131
- width: "8px",
132
- height: "8px",
133
- borderRadius: "50%",
134
- backgroundColor: wsConnected ? STYLES.success : STYLES.error,
135
- boxShadow: wsConnected ? `0 0 8px ${STYLES.success}` : `0 0 8px ${STYLES.error}`
136
- }
137
- }
138
- ),
139
- /* @__PURE__ */ jsx(
140
- "span",
141
- {
142
- style: {
143
- fontSize: "12px",
144
- fontWeight: 600,
145
- color: wsConnected ? STYLES.success : STYLES.error
146
- },
147
- children: wsConnected ? "Connected" : "Disconnected"
148
- }
149
- )
150
- ] })
151
- }
152
- ),
153
- /* @__PURE__ */ jsx(
154
- "div",
155
- {
156
- style: {
157
- fontSize: "10px",
158
- color: STYLES.textDim,
159
- fontFamily: STYLES.fontMono,
160
- wordBreak: "break-all",
161
- marginBottom: "10px"
162
- },
163
- children: wsUrl
164
- }
165
- ),
166
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
167
- /* @__PURE__ */ jsx(
168
- "button",
169
- {
170
- onClick: () => connectWebSocket(wsUrl),
171
- style: {
172
- flex: 1,
173
- padding: "6px 10px",
174
- borderRadius: "6px",
175
- border: `1px solid ${STYLES.border}`,
176
- backgroundColor: wsConnected ? "transparent" : STYLES.accent,
177
- color: wsConnected ? STYLES.textMuted : "#FFFFFF",
178
- fontSize: "11px",
179
- fontWeight: 600,
180
- cursor: "pointer",
181
- transition: STYLES.transitionFast
182
- },
183
- title: "Reconnect to WebSocket server",
184
- children: wsConnected ? "Reconnect" : "Connect"
185
- }
186
- ),
187
- /* @__PURE__ */ jsx(
188
- "button",
189
- {
190
- onClick: () => disconnectWebSocket(),
191
- disabled: !wsConnected,
192
- style: {
193
- padding: "6px 10px",
194
- borderRadius: "6px",
195
- border: `1px solid ${STYLES.border}`,
196
- backgroundColor: "transparent",
197
- color: wsConnected ? STYLES.textMuted : STYLES.textDim,
198
- fontSize: "11px",
199
- fontWeight: 600,
200
- cursor: wsConnected ? "pointer" : "not-allowed",
201
- opacity: wsConnected ? 1 : 0.5,
202
- transition: STYLES.transitionFast
203
- },
204
- title: "Disconnect from WebSocket server",
205
- children: "Disconnect"
206
- }
207
- )
208
- ] })
209
- ]
210
- }
211
- ),
212
- /* @__PURE__ */ jsxs(
213
- "div",
214
- {
215
- style: {
216
- marginTop: "12px",
217
- fontSize: "11px",
218
- color: STYLES.textMuted,
219
- lineHeight: 1.5
220
- },
221
- children: [
222
- /* @__PURE__ */ jsx("strong", { style: { color: STYLES.text }, children: "\u2325+Click" }),
223
- " any element to open the inspector sidebar"
224
- ]
225
- }
226
- )
227
- ]
228
- }
229
- );
230
- }
231
-
232
- // src/components/ui-lint/ScanPanelStack.tsx
233
- import { useRef, useEffect as useEffect2 } from "react";
234
-
235
- // src/components/ui-lint/ScanResultsPopover.tsx
236
- import { useState, useCallback, useMemo, useEffect } from "react";
237
-
238
- // src/components/ui-lint/toolbar-icons.tsx
239
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
240
- function ChevronIcon() {
241
- return /* @__PURE__ */ jsx2("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx2(
242
- "path",
243
- {
244
- d: "M9 6l6 6-6 6",
245
- stroke: "currentColor",
246
- strokeWidth: "2",
247
- strokeLinecap: "round",
248
- strokeLinejoin: "round"
249
- }
250
- ) });
251
- }
252
-
253
- // src/components/ui-lint/ScanResultsPopover.tsx
254
- import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
255
- function getDisambiguatedName(path, allPaths) {
256
- const parts = path.split("/");
257
- const fileName = parts[parts.length - 1] || path;
258
- const duplicates = allPaths.filter((p) => {
259
- const pParts = p.split("/");
260
- return pParts[pParts.length - 1] === fileName && p !== path;
261
- });
262
- if (duplicates.length === 0) {
263
- return fileName;
264
- }
265
- if (parts.length >= 2) {
266
- return `${parts[parts.length - 2]}/${fileName}`;
267
- }
268
- return fileName;
269
- }
270
- function ScanResultsPopover({ onClose }) {
271
- const { autoScanState, liveScanEnabled } = useUILintContext();
272
- const elementIssuesCache = useUILintStore(
273
- (s) => s.elementIssuesCache
274
- );
275
- const fileIssuesCache = useUILintStore((s) => s.fileIssuesCache);
276
- const setLocatorTarget = useUILintStore(
277
- (s) => s.setLocatorTarget
278
- );
279
- const setInspectedElement = useUILintStore(
280
- (s) => s.setInspectedElement
281
- );
282
- const [expandedFiles, setExpandedFiles] = useState(/* @__PURE__ */ new Set());
283
- const [searchQuery, setSearchQuery] = useState("");
284
- const isScanning = autoScanState.status === "scanning";
285
- const isComplete = autoScanState.status === "complete";
286
- const allFilesWithIssues = useMemo(() => {
287
- const sourceFiles = groupBySourceFile(autoScanState.elements);
288
- const allPaths = sourceFiles.map((sf) => sf.path);
289
- const filePathsSet = new Set(sourceFiles.map((sf) => sf.path));
290
- const result = [];
291
- for (const sf of sourceFiles) {
292
- const elementsWithIssues = [];
293
- for (const el of sf.elements) {
294
- const cached = elementIssuesCache.get(el.id);
295
- const issueCount = cached?.issues.length || 0;
296
- if (issueCount > 0) {
297
- const ruleIds = Array.from(
298
- new Set(
299
- (cached?.issues || []).map((i) => i.ruleId).filter((r) => typeof r === "string")
300
- )
301
- );
302
- elementsWithIssues.push({ element: el, issueCount, ruleIds });
303
- }
304
- }
305
- const fileLevelIssues = fileIssuesCache.get(sf.path) || [];
306
- const elementIssueCount = elementsWithIssues.reduce(
307
- (sum, e) => sum + e.issueCount,
308
- 0
309
- );
310
- const totalIssues2 = elementIssueCount + fileLevelIssues.length;
311
- if (totalIssues2 > 0) {
312
- elementsWithIssues.sort(
313
- (a, b) => a.element.source.lineNumber - b.element.source.lineNumber
314
- );
315
- result.push({
316
- path: sf.path,
317
- displayName: sf.displayName,
318
- disambiguatedName: getDisambiguatedName(sf.path, allPaths),
319
- issueCount: totalIssues2,
320
- elementsWithIssues,
321
- fileLevelIssues
322
- });
323
- }
324
- }
325
- for (const [filePath, fileLevelIssues] of fileIssuesCache.entries()) {
326
- if (!filePathsSet.has(filePath) && fileLevelIssues.length > 0) {
327
- const parts = filePath.split("/");
328
- const displayName = parts[parts.length - 1] || filePath;
329
- const allPaths2 = Array.from(filePathsSet).concat([filePath]);
330
- result.push({
331
- path: filePath,
332
- displayName,
333
- disambiguatedName: getDisambiguatedName(filePath, allPaths2),
334
- issueCount: fileLevelIssues.length,
335
- elementsWithIssues: [],
336
- fileLevelIssues
337
- });
338
- }
339
- }
340
- result.sort((a, b) => b.issueCount - a.issueCount);
341
- return result;
342
- }, [autoScanState.elements, elementIssuesCache, fileIssuesCache]);
343
- const filesWithIssues = useMemo(() => {
344
- if (!searchQuery.trim()) {
345
- return allFilesWithIssues;
346
- }
347
- const raw = searchQuery.toLowerCase().trim();
348
- const tagQuery = raw.replace(/[<>]/g, "").replace("/", "").trim();
349
- const query = raw;
350
- return allFilesWithIssues.map((file) => {
351
- const fileMatches = file.path.toLowerCase().includes(query) || file.displayName.toLowerCase().includes(query) || file.disambiguatedName.toLowerCase().includes(query);
352
- const matchingElements = file.elementsWithIssues.filter((item) => {
353
- const tagMatches = item.element.tagName.toLowerCase().includes(tagQuery);
354
- const ruleMatches = item.ruleIds.some(
355
- (r) => r.toLowerCase().includes(query)
356
- );
357
- return tagMatches || ruleMatches;
358
- });
359
- if (fileMatches) {
360
- return file;
361
- }
362
- if (matchingElements.length > 0) {
363
- const filteredIssueCount = matchingElements.reduce(
364
- (sum, e) => sum + e.issueCount,
365
- 0
366
- );
367
- return {
368
- ...file,
369
- elementsWithIssues: matchingElements,
370
- issueCount: filteredIssueCount
371
- };
372
- }
373
- return null;
374
- }).filter((file) => file !== null);
375
- }, [allFilesWithIssues, searchQuery]);
376
- const totalIssues = filesWithIssues.reduce((sum, f) => sum + f.issueCount, 0);
377
- const totalAllIssues = allFilesWithIssues.reduce(
378
- (sum, f) => sum + f.issueCount,
379
- 0
380
- );
381
- const progress = autoScanState.totalElements > 0 ? autoScanState.currentIndex / autoScanState.totalElements * 100 : 0;
382
- const toggleFile = useCallback((path) => {
383
- setExpandedFiles((prev) => {
384
- const next = new Set(prev);
385
- if (next.has(path)) {
386
- next.delete(path);
387
- } else {
388
- next.add(path);
389
- }
390
- return next;
391
- });
392
- }, []);
393
- const handleElementHover = useCallback(
394
- (element) => {
395
- if (!element) {
396
- setLocatorTarget(null);
397
- return;
398
- }
399
- const target = {
400
- element: element.element,
401
- source: element.source,
402
- rect: element.element.getBoundingClientRect()
403
- };
404
- setLocatorTarget(target);
405
- },
406
- [setLocatorTarget]
407
- );
408
- const handleElementClick = useCallback(
409
- (element) => {
410
- element.element.scrollIntoView({
411
- behavior: "smooth",
412
- inline: "nearest",
413
- block: "nearest"
414
- });
415
- setInspectedElement({
416
- element: element.element,
417
- source: element.source,
418
- rect: element.element.getBoundingClientRect(),
419
- scannedElementId: element.id
420
- });
421
- setLocatorTarget(null);
422
- },
423
- [setInspectedElement, setLocatorTarget]
424
- );
425
- const handleFileLevelIssueClick = useCallback(
426
- (filePath, issue) => {
427
- const dummyElement = document.createElement("div");
428
- dummyElement.style.position = "fixed";
429
- dummyElement.style.top = "-9999px";
430
- dummyElement.style.left = "-9999px";
431
- document.body.appendChild(dummyElement);
432
- const source = {
433
- fileName: filePath,
434
- lineNumber: issue.line,
435
- columnNumber: issue.column
436
- };
437
- setInspectedElement({
438
- element: dummyElement,
439
- source,
440
- rect: new DOMRect(0, 0, 0, 0)
441
- });
442
- },
443
- [setInspectedElement]
444
- );
445
- return /* @__PURE__ */ jsxs3(
446
- "div",
447
- {
448
- "data-ui-lint": true,
449
- style: {
450
- position: "relative",
451
- width: "320px",
452
- maxHeight: "450px",
453
- borderRadius: STYLES.popoverRadius,
454
- border: `1px solid ${STYLES.border}`,
455
- backgroundColor: STYLES.bgPopover,
456
- backdropFilter: STYLES.blur,
457
- WebkitBackdropFilter: STYLES.blur,
458
- boxShadow: STYLES.shadowLg,
459
- animation: "uilint-fade-in 0.15s ease-out",
460
- display: "flex",
461
- flexDirection: "column",
462
- overflow: "hidden"
463
- },
464
- children: [
465
- /* @__PURE__ */ jsxs3(
466
- "div",
467
- {
468
- style: {
469
- padding: "12px 14px",
470
- borderBottom: `1px solid ${STYLES.border}`
471
- },
472
- children: [
473
- /* @__PURE__ */ jsxs3(
474
- "div",
475
- {
476
- style: {
477
- display: "flex",
478
- alignItems: "center",
479
- justifyContent: "space-between"
480
- },
481
- children: [
482
- /* @__PURE__ */ jsx3(
483
- "div",
484
- {
485
- style: {
486
- fontSize: "12px",
487
- fontWeight: 600,
488
- color: STYLES.text
489
- },
490
- 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"}`
491
- }
492
- ),
493
- /* @__PURE__ */ jsx3(
494
- "button",
495
- {
496
- onClick: onClose,
497
- style: {
498
- display: "flex",
499
- alignItems: "center",
500
- justifyContent: "center",
501
- width: "26px",
502
- height: "26px",
503
- borderRadius: "6px",
504
- border: "none",
505
- backgroundColor: "transparent",
506
- color: STYLES.textMuted,
507
- cursor: "pointer",
508
- transition: "all 0.15s"
509
- },
510
- onMouseEnter: (e) => {
511
- e.currentTarget.style.backgroundColor = "rgba(55, 65, 81, 0.5)";
512
- e.currentTarget.style.color = STYLES.text;
513
- },
514
- onMouseLeave: (e) => {
515
- e.currentTarget.style.backgroundColor = "transparent";
516
- e.currentTarget.style.color = STYLES.textMuted;
517
- },
518
- title: "Close",
519
- children: /* @__PURE__ */ jsx3(CloseIcon, {})
520
- }
521
- )
522
- ]
523
- }
524
- ),
525
- allFilesWithIssues.length > 0 && /* @__PURE__ */ jsx3("div", { style: { marginTop: "10px" }, children: /* @__PURE__ */ jsxs3(
526
- "div",
527
- {
528
- style: {
529
- display: "flex",
530
- alignItems: "center",
531
- gap: "8px",
532
- padding: "6px 10px",
533
- backgroundColor: "rgba(17, 24, 39, 0.6)",
534
- borderRadius: "6px",
535
- border: `1px solid ${STYLES.border}`
536
- },
537
- children: [
538
- /* @__PURE__ */ jsx3(SearchIcon, {}),
539
- /* @__PURE__ */ jsx3(
540
- "input",
541
- {
542
- type: "text",
543
- value: searchQuery,
544
- onChange: (e) => setSearchQuery(e.target.value),
545
- placeholder: "Filter by file or tag...",
546
- style: {
547
- flex: 1,
548
- border: "none",
549
- backgroundColor: "transparent",
550
- color: STYLES.text,
551
- fontSize: "11px",
552
- fontFamily: STYLES.font,
553
- outline: "none"
554
- }
555
- }
556
- ),
557
- searchQuery && /* @__PURE__ */ jsx3(
558
- "button",
559
- {
560
- onClick: () => setSearchQuery(""),
561
- style: {
562
- display: "flex",
563
- alignItems: "center",
564
- justifyContent: "center",
565
- width: "16px",
566
- height: "16px",
567
- borderRadius: "50%",
568
- border: "none",
569
- backgroundColor: "rgba(75, 85, 99, 0.5)",
570
- color: STYLES.textMuted,
571
- cursor: "pointer",
572
- padding: 0
573
- },
574
- title: "Clear search",
575
- children: /* @__PURE__ */ jsx3(ClearIcon, {})
576
- }
577
- )
578
- ]
579
- }
580
- ) }),
581
- isScanning && /* @__PURE__ */ jsxs3("div", { style: { marginTop: "10px" }, children: [
582
- /* @__PURE__ */ jsxs3(
583
- "div",
584
- {
585
- style: {
586
- display: "flex",
587
- justifyContent: "space-between",
588
- fontSize: "10px",
589
- color: STYLES.textMuted,
590
- marginBottom: "4px"
591
- },
592
- children: [
593
- /* @__PURE__ */ jsx3("span", { children: "Scanning..." }),
594
- /* @__PURE__ */ jsxs3("span", { children: [
595
- autoScanState.currentIndex,
596
- " / ",
597
- autoScanState.totalElements
598
- ] })
599
- ]
600
- }
601
- ),
602
- /* @__PURE__ */ jsx3(
603
- "div",
604
- {
605
- style: {
606
- height: "3px",
607
- backgroundColor: "rgba(75, 85, 99, 0.5)",
608
- borderRadius: "2px",
609
- overflow: "hidden"
610
- },
611
- children: /* @__PURE__ */ jsx3(
612
- "div",
613
- {
614
- style: {
615
- height: "100%",
616
- width: `${progress}%`,
617
- backgroundColor: STYLES.accent,
618
- transition: "width 0.2s ease-out"
619
- }
620
- }
621
- )
622
- }
623
- )
624
- ] })
625
- ]
626
- }
627
- ),
628
- /* @__PURE__ */ jsx3(
629
- "div",
630
- {
631
- style: {
632
- flex: 1,
633
- overflowY: "auto",
634
- padding: "4px 0"
635
- },
636
- onMouseLeave: () => handleElementHover(null),
637
- children: filesWithIssues.length === 0 ? /* @__PURE__ */ jsx3(
638
- "div",
639
- {
640
- style: {
641
- padding: "24px 14px",
642
- textAlign: "center",
643
- fontSize: "11px",
644
- color: STYLES.textMuted
645
- },
646
- children: isScanning ? "Scanning page elements..." : searchQuery && allFilesWithIssues.length > 0 ? /* @__PURE__ */ jsxs3("span", { children: [
647
- 'No matches for "',
648
- searchQuery,
649
- '"'
650
- ] }) : isComplete ? /* @__PURE__ */ jsx3("span", { style: { color: STYLES.success }, children: "\u2713 No issues found" }) : "No files scanned"
651
- }
652
- ) : filesWithIssues.map((file) => /* @__PURE__ */ jsx3(
653
- FileRow,
654
- {
655
- file,
656
- isExpanded: expandedFiles.has(file.path),
657
- onToggle: () => toggleFile(file.path),
658
- onElementHover: handleElementHover,
659
- onElementClick: handleElementClick,
660
- onFileLevelIssueClick: handleFileLevelIssueClick
661
- },
662
- file.path
663
- ))
664
- }
665
- )
666
- ]
667
- }
668
- );
669
- }
670
- function FileRow({
671
- file,
672
- isExpanded,
673
- onToggle,
674
- onElementHover,
675
- onElementClick,
676
- onFileLevelIssueClick
677
- }) {
678
- return /* @__PURE__ */ jsxs3("div", { children: [
679
- /* @__PURE__ */ jsxs3(
680
- "div",
681
- {
682
- style: {
683
- display: "flex",
684
- alignItems: "center",
685
- padding: "8px 12px",
686
- cursor: "pointer",
687
- transition: STYLES.transitionFast,
688
- backgroundColor: isExpanded ? "rgba(59, 130, 246, 0.08)" : "transparent"
689
- },
690
- title: file.path,
691
- onClick: onToggle,
692
- onMouseEnter: (e) => {
693
- if (!isExpanded) {
694
- e.currentTarget.style.backgroundColor = "rgba(55, 65, 81, 0.5)";
695
- }
696
- },
697
- onMouseLeave: (e) => {
698
- e.currentTarget.style.backgroundColor = isExpanded ? "rgba(59, 130, 246, 0.08)" : "transparent";
699
- },
700
- children: [
701
- /* @__PURE__ */ jsx3(
702
- "span",
703
- {
704
- style: {
705
- display: "flex",
706
- alignItems: "center",
707
- justifyContent: "center",
708
- width: "16px",
709
- height: "16px",
710
- marginRight: "6px",
711
- color: STYLES.textMuted,
712
- transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
713
- transition: "transform 0.15s ease-out"
714
- },
715
- children: /* @__PURE__ */ jsx3(ChevronIcon, {})
716
- }
717
- ),
718
- /* @__PURE__ */ jsx3(
719
- "span",
720
- {
721
- style: {
722
- fontSize: "12px",
723
- fontFamily: STYLES.fontMono,
724
- color: STYLES.text,
725
- overflow: "hidden",
726
- textOverflow: "ellipsis",
727
- whiteSpace: "nowrap",
728
- flex: 1,
729
- marginRight: "12px"
730
- },
731
- children: file.disambiguatedName
732
- }
733
- ),
734
- /* @__PURE__ */ jsx3(Badge, { count: file.issueCount, size: "medium" })
735
- ]
736
- }
737
- ),
738
- isExpanded && /* @__PURE__ */ jsxs3(
739
- "div",
740
- {
741
- style: {
742
- backgroundColor: "rgba(17, 24, 39, 0.4)",
743
- borderTop: `1px solid ${STYLES.border}`,
744
- borderBottom: `1px solid ${STYLES.border}`
745
- },
746
- children: [
747
- file.fileLevelIssues.length > 0 && /* @__PURE__ */ jsxs3(Fragment, { children: [
748
- /* @__PURE__ */ jsx3(
749
- "div",
750
- {
751
- style: {
752
- padding: "6px 12px 4px 34px",
753
- fontSize: "10px",
754
- fontWeight: 600,
755
- color: STYLES.textMuted,
756
- textTransform: "uppercase",
757
- letterSpacing: "0.5px"
758
- },
759
- children: "File-level issues"
760
- }
761
- ),
762
- file.fileLevelIssues.map((issue, index) => /* @__PURE__ */ jsx3(
763
- FileLevelIssueRow,
764
- {
765
- filePath: file.path,
766
- issue,
767
- onClick: () => onFileLevelIssueClick(file.path, issue)
768
- },
769
- `${issue.line}-${issue.column}-${index}`
770
- )),
771
- file.elementsWithIssues.length > 0 && /* @__PURE__ */ jsx3(
772
- "div",
773
- {
774
- style: {
775
- height: "1px",
776
- backgroundColor: STYLES.border,
777
- margin: "4px 12px"
778
- }
779
- }
780
- )
781
- ] }),
782
- file.elementsWithIssues.map((item) => /* @__PURE__ */ jsx3(
783
- ElementRow,
784
- {
785
- item,
786
- onHover: onElementHover,
787
- onClick: onElementClick
788
- },
789
- item.element.id
790
- ))
791
- ]
792
- }
793
- )
794
- ] });
795
- }
796
- function ElementRow({ item, onHover, onClick }) {
797
- const { element, issueCount } = item;
798
- return /* @__PURE__ */ jsxs3(
799
- "div",
800
- {
801
- style: {
802
- display: "flex",
803
- alignItems: "center",
804
- padding: "6px 12px 6px 34px",
805
- cursor: "pointer",
806
- transition: STYLES.transitionFast,
807
- backgroundColor: "transparent"
808
- },
809
- onMouseEnter: (e) => {
810
- e.currentTarget.style.backgroundColor = "rgba(59, 130, 246, 0.15)";
811
- onHover(element);
812
- },
813
- onMouseLeave: (e) => {
814
- e.currentTarget.style.backgroundColor = "transparent";
815
- },
816
- onClick: () => onClick(element),
817
- children: [
818
- /* @__PURE__ */ jsxs3(
819
- "div",
820
- {
821
- style: {
822
- display: "flex",
823
- alignItems: "center",
824
- gap: "4px",
825
- flex: 1,
826
- marginRight: "12px"
827
- },
828
- children: [
829
- /* @__PURE__ */ jsxs3(
830
- "span",
831
- {
832
- style: {
833
- fontSize: "11px",
834
- fontFamily: STYLES.fontMono,
835
- color: STYLES.accent
836
- },
837
- children: [
838
- "<",
839
- element.tagName,
840
- ">"
841
- ]
842
- }
843
- ),
844
- /* @__PURE__ */ jsxs3(
845
- "span",
846
- {
847
- style: {
848
- fontSize: "10px",
849
- color: STYLES.textDim
850
- },
851
- children: [
852
- ":",
853
- element.source.lineNumber
854
- ]
855
- }
856
- )
857
- ]
858
- }
859
- ),
860
- /* @__PURE__ */ jsx3(Badge, { count: issueCount, size: "small" })
861
- ]
862
- }
863
- );
864
- }
865
- function extractTagName(line, column) {
866
- if (!line) return null;
867
- if (column !== void 0) {
868
- const searchStart = Math.max(0, column - 50);
869
- const searchEnd = Math.min(line.length, column + 20);
870
- const searchSlice = line.substring(searchStart, searchEnd);
871
- const tagMatches = [
872
- ...searchSlice.matchAll(/<([a-zA-Z][a-zA-Z0-9.-]*|Fragment)\b/g)
873
- ];
874
- for (const match of tagMatches) {
875
- const tagStart = searchStart + match.index;
876
- const tagEnd = tagStart + match[0].length;
877
- if (column >= tagStart && column <= tagEnd + 30) {
878
- return match[1];
879
- }
880
- }
881
- }
882
- const trimmed = line.trim();
883
- const jsxTagMatch = trimmed.match(/^<([a-zA-Z][a-zA-Z0-9.-]*|Fragment)\b/);
884
- if (jsxTagMatch) {
885
- return jsxTagMatch[1];
886
- }
887
- return null;
888
- }
889
- function FileLevelIssueRow({
890
- filePath,
891
- issue,
892
- onClick
893
- }) {
894
- const fileName = filePath.split("/").pop() || filePath;
895
- const [tagName, setTagName] = useState(null);
896
- useEffect(() => {
897
- const cached = getCachedSource(filePath);
898
- if (cached) {
899
- const lines = cached.content.split("\n");
900
- const lineIndex = issue.line - 1;
901
- if (lineIndex >= 0 && lineIndex < lines.length) {
902
- const line = lines[lineIndex];
903
- const tag = extractTagName(line, issue.column);
904
- if (tag) {
905
- setTagName(tag);
906
- return;
907
- }
908
- }
909
- }
910
- fetchSource(filePath).then((result) => {
911
- if (result) {
912
- const lines = result.content.split("\n");
913
- const lineIndex = issue.line - 1;
914
- if (lineIndex >= 0 && lineIndex < lines.length) {
915
- const line = lines[lineIndex];
916
- const tag = extractTagName(line, issue.column);
917
- if (tag) {
918
- setTagName(tag);
919
- }
920
- }
921
- }
922
- });
923
- }, [filePath, issue.line, issue.column]);
924
- return /* @__PURE__ */ jsxs3(
925
- "div",
926
- {
927
- style: {
928
- display: "flex",
929
- alignItems: "center",
930
- padding: "6px 12px 6px 34px",
931
- cursor: "pointer",
932
- transition: STYLES.transitionFast,
933
- backgroundColor: "transparent"
934
- },
935
- onMouseEnter: (e) => {
936
- e.currentTarget.style.backgroundColor = "rgba(59, 130, 246, 0.15)";
937
- },
938
- onMouseLeave: (e) => {
939
- e.currentTarget.style.backgroundColor = "transparent";
940
- },
941
- onClick,
942
- children: [
943
- /* @__PURE__ */ jsxs3(
944
- "div",
945
- {
946
- style: {
947
- display: "flex",
948
- alignItems: "center",
949
- gap: "4px",
950
- flex: 1,
951
- marginRight: "12px"
952
- },
953
- children: [
954
- tagName && /* @__PURE__ */ jsxs3(
955
- "span",
956
- {
957
- style: {
958
- fontSize: "11px",
959
- fontFamily: STYLES.fontMono,
960
- color: STYLES.accent
961
- },
962
- children: [
963
- "<",
964
- tagName,
965
- ">"
966
- ]
967
- }
968
- ),
969
- !tagName && /* @__PURE__ */ jsx3(
970
- "span",
971
- {
972
- style: {
973
- fontSize: "11px",
974
- fontFamily: STYLES.fontMono,
975
- color: STYLES.textMuted
976
- },
977
- children: fileName
978
- }
979
- ),
980
- /* @__PURE__ */ jsxs3(
981
- "span",
982
- {
983
- style: {
984
- fontSize: "10px",
985
- color: STYLES.textDim
986
- },
987
- children: [
988
- ":",
989
- issue.line,
990
- issue.column ? `:${issue.column}` : ""
991
- ]
992
- }
993
- )
994
- ]
995
- }
996
- ),
997
- /* @__PURE__ */ jsx3(Badge, { count: 1, size: "small" })
998
- ]
999
- }
1000
- );
1001
- }
1002
- function SearchIcon() {
1003
- return /* @__PURE__ */ jsxs3(
1004
- "svg",
1005
- {
1006
- width: "12",
1007
- height: "12",
1008
- viewBox: "0 0 24 24",
1009
- fill: "none",
1010
- style: { flexShrink: 0 },
1011
- children: [
1012
- /* @__PURE__ */ jsx3(
1013
- "circle",
1014
- {
1015
- cx: "11",
1016
- cy: "11",
1017
- r: "7",
1018
- stroke: STYLES.textMuted,
1019
- strokeWidth: "2",
1020
- strokeLinecap: "round"
1021
- }
1022
- ),
1023
- /* @__PURE__ */ jsx3(
1024
- "path",
1025
- {
1026
- d: "M21 21l-4.35-4.35",
1027
- stroke: STYLES.textMuted,
1028
- strokeWidth: "2",
1029
- strokeLinecap: "round"
1030
- }
1031
- )
1032
- ]
1033
- }
1034
- );
1035
- }
1036
- function ClearIcon() {
1037
- return /* @__PURE__ */ jsx3("svg", { width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx3(
1038
- "path",
1039
- {
1040
- d: "M18 6L6 18M6 6l12 12",
1041
- stroke: "currentColor",
1042
- strokeWidth: "3",
1043
- strokeLinecap: "round",
1044
- strokeLinejoin: "round"
1045
- }
1046
- ) });
1047
- }
1048
- function CloseIcon() {
1049
- return /* @__PURE__ */ jsx3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx3(
1050
- "path",
1051
- {
1052
- d: "M18 6L6 18M6 6l12 12",
1053
- stroke: "currentColor",
1054
- strokeWidth: "2",
1055
- strokeLinecap: "round",
1056
- strokeLinejoin: "round"
1057
- }
1058
- ) });
1059
- }
1060
-
1061
- // src/components/ui-lint/ScanPanelStack.tsx
1062
- import { jsx as jsx4 } from "react/jsx-runtime";
1063
- function ScanPanelStack({ show, onClose }) {
1064
- const containerRef = useRef(null);
1065
- useEffect2(() => {
1066
- if (!show) return;
1067
- const handleClickOutside = (e) => {
1068
- const target = e.target;
1069
- if (target?.closest?.("[data-ui-lint]")) {
1070
- return;
1071
- }
1072
- if (containerRef.current && !containerRef.current.contains(target)) {
1073
- onClose();
1074
- }
1075
- };
1076
- const timeoutId = setTimeout(() => {
1077
- document.addEventListener("mousedown", handleClickOutside, true);
1078
- }, 100);
1079
- return () => {
1080
- clearTimeout(timeoutId);
1081
- document.removeEventListener("mousedown", handleClickOutside, true);
1082
- };
1083
- }, [show, onClose]);
1084
- useEffect2(() => {
1085
- if (!show) return;
1086
- const handleKeyDown = (e) => {
1087
- if (e.key === "Escape") {
1088
- onClose();
1089
- }
1090
- };
1091
- window.addEventListener("keydown", handleKeyDown);
1092
- return () => window.removeEventListener("keydown", handleKeyDown);
1093
- }, [show, onClose]);
1094
- const handleUILintInteraction = (e) => {
1095
- e.stopPropagation();
1096
- };
1097
- if (!show) return null;
1098
- return /* @__PURE__ */ jsx4(
1099
- "div",
1100
- {
1101
- ref: containerRef,
1102
- "data-ui-lint": true,
1103
- onMouseDown: handleUILintInteraction,
1104
- onPointerDown: handleUILintInteraction,
1105
- onClick: handleUILintInteraction,
1106
- onKeyDown: handleUILintInteraction,
1107
- style: {
1108
- position: "absolute",
1109
- bottom: "100%",
1110
- left: 0,
1111
- marginBottom: "8px",
1112
- pointerEvents: "auto"
1113
- // Ensure panel is interactive
1114
- },
1115
- children: /* @__PURE__ */ jsx4(ScanResultsPopover, { onClose })
1116
- }
1117
- );
1118
- }
1119
-
1120
- // src/components/ui-lint/UILintToolbar.tsx
1121
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1122
- var TOKENS = {
1123
- // Colors
1124
- bgBase: "rgba(15, 15, 15, 0.92)",
1125
- bgElevated: "rgba(25, 25, 25, 0.95)",
1126
- bgHover: "rgba(255, 255, 255, 0.08)",
1127
- bgActive: "rgba(255, 255, 255, 0.12)",
1128
- border: "rgba(255, 255, 255, 0.1)",
1129
- borderFocus: "rgba(99, 179, 237, 0.6)",
1130
- textPrimary: "rgba(255, 255, 255, 0.95)",
1131
- textSecondary: "rgba(255, 255, 255, 0.7)",
1132
- textMuted: "rgba(255, 255, 255, 0.4)",
1133
- textDisabled: "rgba(255, 255, 255, 0.25)",
1134
- accent: "#63b3ed",
1135
- success: BADGE_COLORS.success,
1136
- warning: BADGE_COLORS.warning,
1137
- error: "#f56565",
1138
- fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`,
1139
- fontMono: `"SF Mono", Monaco, "Cascadia Code", monospace`,
1140
- pillHeight: "40px",
1141
- pillRadius: "20px",
1142
- buttonMinWidth: "40px",
1143
- blur: "blur(16px)",
1144
- shadowMd: "0 4px 20px rgba(0, 0, 0, 0.4)",
1145
- shadowGlow: (color) => `0 0 16px ${color}`,
1146
- transitionFast: "150ms cubic-bezier(0.4, 0, 0.2, 1)",
1147
- transitionBase: "200ms cubic-bezier(0.4, 0, 0.2, 1)",
1148
- transitionSlow: "300ms cubic-bezier(0.4, 0, 0.2, 1)"
1149
- };
1150
- var Icons = {
1151
- Eye: () => /* @__PURE__ */ jsxs4(
1152
- "svg",
1153
- {
1154
- width: "16",
1155
- height: "16",
1156
- viewBox: "0 0 24 24",
1157
- fill: "none",
1158
- stroke: "currentColor",
1159
- strokeWidth: "2",
1160
- strokeLinecap: "round",
1161
- strokeLinejoin: "round",
1162
- children: [
1163
- /* @__PURE__ */ jsx5("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
1164
- /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "3" })
1165
- ]
1166
- }
1167
- ),
1168
- Settings: () => /* @__PURE__ */ jsxs4(
1169
- "svg",
1170
- {
1171
- width: "15",
1172
- height: "15",
1173
- viewBox: "0 0 24 24",
1174
- fill: "none",
1175
- stroke: "currentColor",
1176
- strokeWidth: "2",
1177
- strokeLinecap: "round",
1178
- strokeLinejoin: "round",
1179
- children: [
1180
- /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "3" }),
1181
- /* @__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" })
1182
- ]
1183
- }
1184
- ),
1185
- Check: () => /* @__PURE__ */ jsx5(
1186
- "svg",
1187
- {
1188
- width: "12",
1189
- height: "12",
1190
- viewBox: "0 0 24 24",
1191
- fill: "none",
1192
- stroke: "currentColor",
1193
- strokeWidth: "2.5",
1194
- strokeLinecap: "round",
1195
- strokeLinejoin: "round",
1196
- children: /* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" })
1197
- }
1198
- ),
1199
- AlertTriangle: () => /* @__PURE__ */ jsxs4(
1200
- "svg",
1201
- {
1202
- width: "12",
1203
- height: "12",
1204
- viewBox: "0 0 24 24",
1205
- fill: "none",
1206
- stroke: "currentColor",
1207
- strokeWidth: "2",
1208
- strokeLinecap: "round",
1209
- strokeLinejoin: "round",
1210
- children: [
1211
- /* @__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" }),
1212
- /* @__PURE__ */ jsx5("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
1213
- /* @__PURE__ */ jsx5("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
1214
- ]
1215
- }
1216
- ),
1217
- ChevronDown: () => /* @__PURE__ */ jsx5(
1218
- "svg",
1219
- {
1220
- width: "12",
1221
- height: "12",
1222
- viewBox: "0 0 24 24",
1223
- fill: "none",
1224
- stroke: "currentColor",
1225
- strokeWidth: "2",
1226
- strokeLinecap: "round",
1227
- strokeLinejoin: "round",
1228
- children: /* @__PURE__ */ jsx5("polyline", { points: "6 9 12 15 18 9" })
1229
- }
1230
- ),
1231
- X: () => /* @__PURE__ */ jsxs4(
1232
- "svg",
1233
- {
1234
- width: "14",
1235
- height: "14",
1236
- viewBox: "0 0 24 24",
1237
- fill: "none",
1238
- stroke: "currentColor",
1239
- strokeWidth: "2",
1240
- strokeLinecap: "round",
1241
- strokeLinejoin: "round",
1242
- children: [
1243
- /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1244
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1245
- ]
1246
- }
1247
- ),
1248
- Unplug: () => /* @__PURE__ */ jsxs4(
1249
- "svg",
1250
- {
1251
- width: "14",
1252
- height: "14",
1253
- viewBox: "0 0 24 24",
1254
- fill: "none",
1255
- stroke: "currentColor",
1256
- strokeWidth: "2",
1257
- strokeLinecap: "round",
1258
- strokeLinejoin: "round",
1259
- children: [
1260
- /* @__PURE__ */ jsx5("path", { d: "M19 5l3-3" }),
1261
- /* @__PURE__ */ jsx5("path", { d: "M2 22l3-3" }),
1262
- /* @__PURE__ */ jsx5("path", { d: "M6.3 6.3a10 10 0 0 1 13.4 1.3" }),
1263
- /* @__PURE__ */ jsx5("path", { d: "M17.7 17.7a10 10 0 0 1-13.4-1.3" }),
1264
- /* @__PURE__ */ jsx5("path", { d: "m8 15 8-8" })
1265
- ]
1266
- }
1267
- ),
1268
- Scan: () => /* @__PURE__ */ jsxs4(
1269
- "svg",
1270
- {
1271
- width: "14",
1272
- height: "14",
1273
- viewBox: "0 0 24 24",
1274
- fill: "none",
1275
- stroke: "currentColor",
1276
- strokeWidth: "2",
1277
- strokeLinecap: "round",
1278
- strokeLinejoin: "round",
1279
- children: [
1280
- /* @__PURE__ */ jsx5("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }),
1281
- /* @__PURE__ */ jsx5("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }),
1282
- /* @__PURE__ */ jsx5("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }),
1283
- /* @__PURE__ */ jsx5("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" }),
1284
- /* @__PURE__ */ jsx5("line", { x1: "7", y1: "12", x2: "17", y2: "12" })
1285
- ]
1286
- }
1287
- )
1288
- };
1289
- var globalStyles = `
1290
- @keyframes uilint-fade-in {
1291
- from { opacity: 0; transform: translateY(8px) scale(0.98); }
1292
- to { opacity: 1; transform: translateY(0) scale(1); }
1293
- }
1294
-
1295
- @keyframes uilint-fade-out {
1296
- from { opacity: 1; transform: translateY(0) scale(1); }
1297
- to { opacity: 0; transform: translateY(8px) scale(0.98); }
1298
- }
1299
-
1300
- @keyframes uilint-pulse {
1301
- 0%, 100% { opacity: 1; }
1302
- 50% { opacity: 0.6; }
1303
- }
1304
-
1305
- @keyframes uilint-slide-up {
1306
- from { opacity: 0; transform: translateY(12px); }
1307
- to { opacity: 1; transform: translateY(0); }
1308
- }
1309
-
1310
- .uilint-btn {
1311
- display: flex;
1312
- align-items: center;
1313
- justify-content: center;
1314
- gap: 6px;
1315
- height: 100%;
1316
- padding: 0 14px;
1317
- border: none;
1318
- background: transparent;
1319
- color: ${TOKENS.textSecondary};
1320
- font-family: ${TOKENS.fontFamily};
1321
- font-size: 13px;
1322
- font-weight: 500;
1323
- cursor: pointer;
1324
- transition:
1325
- background-color ${TOKENS.transitionFast},
1326
- color ${TOKENS.transitionFast};
1327
- outline: none;
1328
- white-space: nowrap;
1329
- }
1330
-
1331
- .uilint-btn:hover:not(:disabled) {
1332
- background: ${TOKENS.bgHover};
1333
- color: ${TOKENS.textPrimary};
1334
- }
1335
-
1336
- .uilint-btn:active:not(:disabled) {
1337
- background: ${TOKENS.bgActive};
1338
- }
1339
-
1340
- .uilint-btn:focus-visible {
1341
- box-shadow: inset 0 0 0 2px ${TOKENS.borderFocus};
1342
- }
1343
-
1344
- .uilint-btn:disabled {
1345
- cursor: not-allowed;
1346
- color: ${TOKENS.textDisabled};
1347
- }
1348
-
1349
- .uilint-btn--icon {
1350
- padding: 0;
1351
- min-width: ${TOKENS.buttonMinWidth};
1352
- }
1353
-
1354
- .uilint-btn--primary {
1355
- color: ${TOKENS.textPrimary};
1356
- }
1357
-
1358
- .uilint-btn--accent {
1359
- color: ${TOKENS.accent};
1360
- }
1361
-
1362
- .uilint-btn--warning {
1363
- color: ${TOKENS.warning};
1364
- }
1365
-
1366
- .uilint-btn--success {
1367
- color: ${TOKENS.success};
1368
- }
1369
-
1370
- .uilint-popover {
1371
- animation: uilint-fade-in ${TOKENS.transitionSlow} forwards;
1372
- }
1373
-
1374
- .uilint-popover--closing {
1375
- animation: uilint-fade-out ${TOKENS.transitionBase} forwards;
1376
- }
1377
-
1378
- .uilint-scanning-bar {
1379
- animation: uilint-slide-up ${TOKENS.transitionSlow} forwards;
1380
- }
1381
-
1382
- .uilint-scanning-dot {
1383
- width: 6px;
1384
- height: 6px;
1385
- border-radius: 50%;
1386
- background: ${TOKENS.accent};
1387
- animation: uilint-pulse 1.5s ease-in-out infinite;
1388
- }
1389
-
1390
- /* Scrollbar styling */
1391
- [data-ui-lint] * {
1392
- scrollbar-width: thin;
1393
- scrollbar-color: rgba(255, 255, 255, 0.15) rgba(15, 15, 15, 0.3);
1394
- }
1395
-
1396
- [data-ui-lint] *::-webkit-scrollbar {
1397
- width: 8px;
1398
- height: 8px;
1399
- }
1400
-
1401
- [data-ui-lint] *::-webkit-scrollbar-track {
1402
- background: rgba(15, 15, 15, 0.3);
1403
- border-radius: 4px;
1404
- }
1405
-
1406
- [data-ui-lint] *::-webkit-scrollbar-thumb {
1407
- background: rgba(255, 255, 255, 0.15);
1408
- border-radius: 4px;
1409
- }
1410
-
1411
- [data-ui-lint] *::-webkit-scrollbar-thumb:hover {
1412
- background: rgba(255, 255, 255, 0.25);
1413
- }
1414
- `;
1415
- var PillContainer = React3.forwardRef(({ children, glow, style }, ref) => /* @__PURE__ */ jsx5(
1416
- "div",
1417
- {
1418
- ref,
1419
- style: {
1420
- display: "inline-flex",
1421
- alignItems: "center",
1422
- height: TOKENS.pillHeight,
1423
- borderRadius: TOKENS.pillRadius,
1424
- border: `1px solid ${TOKENS.border}`,
1425
- backgroundColor: TOKENS.bgBase,
1426
- backdropFilter: TOKENS.blur,
1427
- WebkitBackdropFilter: TOKENS.blur,
1428
- boxShadow: glow ? `${TOKENS.shadowMd}, ${TOKENS.shadowGlow(glow)}` : TOKENS.shadowMd,
1429
- overflow: "hidden",
1430
- transition: `box-shadow ${TOKENS.transitionBase}`,
1431
- ...style
1432
- },
1433
- children
1434
- }
1435
- ));
1436
- PillContainer.displayName = "PillContainer";
1437
- function Divider() {
1438
- return /* @__PURE__ */ jsx5(
1439
- "div",
1440
- {
1441
- style: {
1442
- width: "1px",
1443
- height: "20px",
1444
- backgroundColor: TOKENS.border,
1445
- flexShrink: 0
1446
- },
1447
- "aria-hidden": "true"
1448
- }
1449
- );
1450
- }
1451
- function Kbd({ children }) {
1452
- return /* @__PURE__ */ jsx5(
1453
- "kbd",
1454
- {
1455
- style: {
1456
- display: "inline-flex",
1457
- alignItems: "center",
1458
- padding: "2px 6px",
1459
- borderRadius: "4px",
1460
- backgroundColor: TOKENS.bgElevated,
1461
- border: `1px solid ${TOKENS.border}`,
1462
- fontSize: "11px",
1463
- fontFamily: TOKENS.fontMono,
1464
- color: TOKENS.textSecondary,
1465
- boxShadow: "0 1px 2px rgba(0, 0, 0, 0.4)"
1466
- },
1467
- children
1468
- }
1469
- );
1470
- }
1471
- function DisconnectedToolbar({
1472
- onSettingsClick,
1473
- showSettings
1474
- }) {
1475
- return /* @__PURE__ */ jsxs4(PillContainer, { children: [
1476
- /* @__PURE__ */ jsxs4(
1477
- "div",
1478
- {
1479
- style: {
1480
- display: "flex",
1481
- alignItems: "center",
1482
- gap: "6px",
1483
- padding: "0 12px",
1484
- color: TOKENS.textMuted,
1485
- fontSize: "12px"
1486
- },
1487
- children: [
1488
- /* @__PURE__ */ jsx5(Icons.Unplug, {}),
1489
- /* @__PURE__ */ jsx5("span", { children: "Not connected" })
1490
- ]
1491
- }
1492
- ),
1493
- /* @__PURE__ */ jsx5(Divider, {}),
1494
- /* @__PURE__ */ jsx5(
1495
- "button",
1496
- {
1497
- className: `uilint-btn uilint-btn--icon ${showSettings ? "uilint-btn--accent" : ""}`,
1498
- onClick: onSettingsClick,
1499
- title: "Settings",
1500
- "aria-label": "Open settings",
1501
- "aria-pressed": showSettings,
1502
- children: /* @__PURE__ */ jsx5(Icons.Settings, {})
1503
- }
1504
- )
1505
- ] });
1506
- }
1507
- function IdleToolbar({
1508
- onStartScan,
1509
- onSettingsClick,
1510
- showSettings
1511
- }) {
1512
- return /* @__PURE__ */ jsxs4(PillContainer, { children: [
1513
- /* @__PURE__ */ jsxs4(
1514
- "button",
1515
- {
1516
- className: "uilint-btn uilint-btn--primary",
1517
- onClick: onStartScan,
1518
- title: "Start scanning (\u2325S)",
1519
- "aria-label": "Start live scanning",
1520
- children: [
1521
- /* @__PURE__ */ jsx5(Icons.Eye, {}),
1522
- /* @__PURE__ */ jsx5("span", { children: "Start Scanning" })
1523
- ]
1524
- }
1525
- ),
1526
- /* @__PURE__ */ jsx5(Divider, {}),
1527
- /* @__PURE__ */ jsx5(
1528
- "button",
1529
- {
1530
- className: `uilint-btn uilint-btn--icon ${showSettings ? "uilint-btn--accent" : ""}`,
1531
- onClick: onSettingsClick,
1532
- title: "Settings",
1533
- "aria-label": "Open settings",
1534
- "aria-pressed": showSettings,
1535
- children: /* @__PURE__ */ jsx5(Icons.Settings, {})
1536
- }
1537
- )
1538
- ] });
1539
- }
1540
- function ScanningToolbar({
1541
- issueCount,
1542
- isScanning,
1543
- showResults,
1544
- onToggleResults,
1545
- onStopScan
1546
- }) {
1547
- const hasIssues = issueCount > 0;
1548
- const getStatusContent = () => {
1549
- if (isScanning) {
1550
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1551
- /* @__PURE__ */ jsx5("div", { className: "uilint-scanning-dot" }),
1552
- /* @__PURE__ */ jsx5("span", { style: { fontFamily: TOKENS.fontMono, fontSize: "12px" }, children: "Scanning..." })
1553
- ] });
1554
- }
1555
- if (hasIssues) {
1556
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1557
- /* @__PURE__ */ jsx5(
1558
- "span",
1559
- {
1560
- style: {
1561
- display: "flex",
1562
- alignItems: "center",
1563
- justifyContent: "center",
1564
- width: "16px",
1565
- height: "16px",
1566
- borderRadius: "50%",
1567
- backgroundColor: `${TOKENS.warning}20`,
1568
- color: TOKENS.warning
1569
- },
1570
- children: /* @__PURE__ */ jsx5(Icons.AlertTriangle, {})
1571
- }
1572
- ),
1573
- /* @__PURE__ */ jsx5(Badge, { count: issueCount })
1574
- ] });
1575
- }
1576
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1577
- /* @__PURE__ */ jsx5(
1578
- "span",
1579
- {
1580
- style: {
1581
- display: "flex",
1582
- alignItems: "center",
1583
- justifyContent: "center",
1584
- width: "16px",
1585
- height: "16px",
1586
- borderRadius: "50%",
1587
- backgroundColor: `${TOKENS.success}20`,
1588
- color: TOKENS.success
1589
- },
1590
- children: /* @__PURE__ */ jsx5(Icons.Check, {})
1591
- }
1592
- ),
1593
- /* @__PURE__ */ jsx5(
1594
- "span",
1595
- {
1596
- style: { fontSize: "12px", fontWeight: 500, color: TOKENS.success },
1597
- children: "All clear"
1598
- }
1599
- )
1600
- ] });
1601
- };
1602
- const statusVariant = hasIssues ? "warning" : isScanning ? "accent" : "success";
1603
- const glowColor = hasIssues ? `${TOKENS.warning}25` : void 0;
1604
- return /* @__PURE__ */ jsxs4(
1605
- "div",
1606
- {
1607
- className: "uilint-scanning-bar",
1608
- style: { display: "flex", alignItems: "center", gap: "10px" },
1609
- children: [
1610
- /* @__PURE__ */ jsxs4(PillContainer, { glow: glowColor, children: [
1611
- /* @__PURE__ */ jsxs4(
1612
- "button",
1613
- {
1614
- className: `uilint-btn uilint-btn--${statusVariant}`,
1615
- onClick: onToggleResults,
1616
- title: hasIssues ? `${issueCount} issue${issueCount !== 1 ? "s" : ""} found` : "View scan results",
1617
- "aria-label": "Toggle scan results",
1618
- "aria-expanded": showResults,
1619
- style: { paddingRight: "10px" },
1620
- children: [
1621
- getStatusContent(),
1622
- /* @__PURE__ */ jsx5(Icons.ChevronDown, {})
1623
- ]
1624
- }
1625
- ),
1626
- /* @__PURE__ */ jsx5(Divider, {}),
1627
- /* @__PURE__ */ jsx5(
1628
- "button",
1629
- {
1630
- className: "uilint-btn uilint-btn--icon",
1631
- onClick: onStopScan,
1632
- title: "Stop scanning (\u2325S)",
1633
- "aria-label": "Stop scanning",
1634
- children: /* @__PURE__ */ jsx5(Icons.X, {})
1635
- }
1636
- )
1637
- ] }),
1638
- /* @__PURE__ */ jsx5(Kbd, { children: "\u2325 + Click" }),
1639
- /* @__PURE__ */ jsx5("span", { style: { fontSize: "12px", color: TOKENS.textMuted }, children: "to inspect" })
1640
- ]
1641
- }
1642
- );
1643
- }
1644
- function UILintToolbar() {
1645
- const {
1646
- settings,
1647
- liveScanEnabled,
1648
- autoScanState,
1649
- enableLiveScan,
1650
- disableLiveScan
1651
- } = useUILintContext();
1652
- const elementIssuesCache = useUILintStore(
1653
- (s) => s.elementIssuesCache
1654
- );
1655
- const fileIssuesCache = useUILintStore((s) => s.fileIssuesCache);
1656
- const wsConnected = useUILintStore((s) => s.wsConnected);
1657
- const [showSettings, setShowSettings] = useState2(false);
1658
- const [showResults, setShowResults] = useState2(false);
1659
- const [mounted, setMounted] = useState2(false);
1660
- const [settingsClosing, setSettingsClosing] = useState2(false);
1661
- const [nextjsOverlayVisible, setNextjsOverlayVisible] = useState2(false);
1662
- const toolbarRef = useRef2(null);
1663
- const settingsRef = useRef2(null);
1664
- const isScanning = autoScanState.status === "scanning" || autoScanState.status === "paused";
1665
- let elementIssues = 0;
1666
- elementIssuesCache.forEach((el) => {
1667
- elementIssues += el.issues.length;
1668
- });
1669
- let fileLevelIssues = 0;
1670
- fileIssuesCache.forEach((issues) => {
1671
- fileLevelIssues += issues.length;
1672
- });
1673
- const totalIssues = elementIssues + fileLevelIssues;
1674
- useEffect3(() => {
1675
- const checkForNextOverlay = () => {
1676
- const overlaySelectors = [
1677
- "nextjs-portal",
1678
- "[data-nextjs-dialog]",
1679
- "[data-nextjs-dialog-overlay]",
1680
- "#__next-build-watcher",
1681
- "[data-nextjs-toast]"
1682
- ];
1683
- const hasOverlay = overlaySelectors.some((selector) => {
1684
- const el = document.querySelector(selector);
1685
- if (!el) return false;
1686
- const style = window.getComputedStyle(el);
1687
- return style.display !== "none" && style.visibility !== "hidden";
1688
- });
1689
- setNextjsOverlayVisible(hasOverlay);
1690
- };
1691
- checkForNextOverlay();
1692
- const observer = new MutationObserver(checkForNextOverlay);
1693
- observer.observe(document.body, {
1694
- childList: true,
1695
- subtree: true,
1696
- attributes: true,
1697
- attributeFilter: ["style", "class"]
1698
- });
1699
- return () => observer.disconnect();
1700
- }, []);
1701
- useEffect3(() => {
1702
- setMounted(true);
1703
- }, []);
1704
- useEffect3(() => {
1705
- const handleClickOutside = (e) => {
1706
- const target = e.target;
1707
- if (target?.closest?.("[data-ui-lint]")) return;
1708
- if (showSettings && settingsRef.current && toolbarRef.current) {
1709
- if (!settingsRef.current.contains(target) && !toolbarRef.current.contains(target)) {
1710
- handleCloseSettings();
1711
- }
1712
- }
1713
- if (showResults) {
1714
- setShowResults(false);
1715
- }
1716
- };
1717
- const handleEscape = (e) => {
1718
- if (e.key === "Escape") {
1719
- if (showSettings) handleCloseSettings();
1720
- if (showResults) setShowResults(false);
1721
- }
1722
- };
1723
- document.addEventListener("mousedown", handleClickOutside, true);
1724
- document.addEventListener("keydown", handleEscape);
1725
- return () => {
1726
- document.removeEventListener("mousedown", handleClickOutside, true);
1727
- document.removeEventListener("keydown", handleEscape);
1728
- };
1729
- }, [showSettings, showResults]);
1730
- const handleStartScan = useCallback2(() => {
1731
- enableLiveScan();
1732
- setShowSettings(false);
1733
- }, [enableLiveScan]);
1734
- const handleStopScan = useCallback2(() => {
1735
- disableLiveScan();
1736
- setShowResults(false);
1737
- }, [disableLiveScan]);
1738
- const handleSettingsClick = useCallback2(() => {
1739
- if (showSettings) {
1740
- handleCloseSettings();
1741
- } else {
1742
- setShowSettings(true);
1743
- }
1744
- }, [showSettings]);
1745
- const handleCloseSettings = useCallback2(() => {
1746
- setSettingsClosing(true);
1747
- setTimeout(() => {
1748
- setShowSettings(false);
1749
- setSettingsClosing(false);
1750
- }, 150);
1751
- }, []);
1752
- const handleToggleResults = useCallback2(() => {
1753
- setShowResults((prev) => !prev);
1754
- }, []);
1755
- const handleUILintInteraction = useCallback2(
1756
- (e) => {
1757
- e.stopPropagation();
1758
- },
1759
- []
1760
- );
1761
- if (!mounted) return null;
1762
- const bottomPosition = nextjsOverlayVisible ? "80px" : "20px";
1763
- const renderToolbar = () => {
1764
- if (!wsConnected) {
1765
- return /* @__PURE__ */ jsx5(
1766
- DisconnectedToolbar,
1767
- {
1768
- onSettingsClick: handleSettingsClick,
1769
- showSettings
1770
- }
1771
- );
1772
- }
1773
- if (!liveScanEnabled) {
1774
- return /* @__PURE__ */ jsx5(
1775
- IdleToolbar,
1776
- {
1777
- onStartScan: handleStartScan,
1778
- onSettingsClick: handleSettingsClick,
1779
- showSettings
1780
- }
1781
- );
1782
- }
1783
- return /* @__PURE__ */ jsx5(
1784
- ScanningToolbar,
1785
- {
1786
- issueCount: totalIssues,
1787
- isScanning,
1788
- showResults,
1789
- onToggleResults: handleToggleResults,
1790
- onStopScan: handleStopScan
1791
- }
1792
- );
1793
- };
1794
- const content = /* @__PURE__ */ jsxs4(
1795
- "div",
1796
- {
1797
- "data-ui-lint": true,
1798
- onMouseDown: handleUILintInteraction,
1799
- onPointerDown: handleUILintInteraction,
1800
- onClick: handleUILintInteraction,
1801
- onKeyDown: handleUILintInteraction,
1802
- style: {
1803
- position: "fixed",
1804
- bottom: bottomPosition,
1805
- left: "20px",
1806
- zIndex: 99999,
1807
- fontFamily: TOKENS.fontFamily,
1808
- transition: `bottom ${TOKENS.transitionSlow}`,
1809
- pointerEvents: "none"
1810
- },
1811
- children: [
1812
- /* @__PURE__ */ jsx5("style", { children: globalStyles }),
1813
- /* @__PURE__ */ jsx5(
1814
- "div",
1815
- {
1816
- ref: toolbarRef,
1817
- role: "toolbar",
1818
- "aria-label": "UI Lint toolbar",
1819
- style: { pointerEvents: "auto" },
1820
- children: renderToolbar()
1821
- }
1822
- ),
1823
- showSettings && !liveScanEnabled && /* @__PURE__ */ jsx5(
1824
- "div",
1825
- {
1826
- ref: settingsRef,
1827
- className: `uilint-popover ${settingsClosing ? "uilint-popover--closing" : ""}`,
1828
- style: {
1829
- position: "absolute",
1830
- bottom: "100%",
1831
- left: 0,
1832
- marginBottom: "8px",
1833
- pointerEvents: "auto"
1834
- },
1835
- children: /* @__PURE__ */ jsx5(SettingsPopover, { settings })
1836
- }
1837
- ),
1838
- /* @__PURE__ */ jsx5(
1839
- ScanPanelStack,
1840
- {
1841
- show: showResults && liveScanEnabled,
1842
- onClose: () => setShowResults(false)
1843
- }
1844
- )
1845
- ]
1846
- }
1847
- );
1848
- return createPortal(content, document.body);
1849
- }
1850
-
1851
- export {
1852
- UILintToolbar
1853
- };