uilint-react 0.2.1 → 0.2.3

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,825 +0,0 @@
1
- "use client";
2
- "use client";
3
- import {
4
- useUILintContext,
5
- useUILintStore
6
- } from "./chunk-552GIJIQ.js";
7
-
8
- // src/components/ui-lint/ElementBadges.tsx
9
- import React, { useState, useEffect, useCallback, useMemo } from "react";
10
- import { createPortal } from "react-dom";
11
-
12
- // src/components/ui-lint/badge-layout.ts
13
- var DEFAULT_CONFIG = {
14
- repulsionForce: 50,
15
- anchorStrength: 0.3,
16
- minDistance: 24,
17
- iterations: 50,
18
- damping: 0.9
19
- };
20
- function computeLayout(positions, config) {
21
- if (positions.length === 0) return [];
22
- if (positions.length === 1) {
23
- return [
24
- { ...positions[0], nudgedX: positions[0].x, nudgedY: positions[0].y }
25
- ];
26
- }
27
- const nodes = positions.map((p) => ({
28
- position: p,
29
- nudgedX: p.x,
30
- nudgedY: p.y,
31
- velocityX: 0,
32
- velocityY: 0
33
- }));
34
- for (let iter = 0; iter < config.iterations; iter++) {
35
- for (let i = 0; i < nodes.length; i++) {
36
- let fx = 0;
37
- let fy = 0;
38
- for (let j = 0; j < nodes.length; j++) {
39
- if (i === j) continue;
40
- const dx = nodes[i].nudgedX - nodes[j].nudgedX;
41
- const dy = nodes[i].nudgedY - nodes[j].nudgedY;
42
- const dist = Math.max(Math.hypot(dx, dy), 1);
43
- if (dist < config.minDistance) {
44
- const force = config.repulsionForce / (dist * dist);
45
- fx += dx / dist * force;
46
- fy += dy / dist * force;
47
- }
48
- }
49
- const anchorDx = positions[i].x - nodes[i].nudgedX;
50
- const anchorDy = positions[i].y - nodes[i].nudgedY;
51
- fx += anchorDx * config.anchorStrength;
52
- fy += anchorDy * config.anchorStrength;
53
- nodes[i].velocityX = (nodes[i].velocityX + fx) * config.damping;
54
- nodes[i].velocityY = (nodes[i].velocityY + fy) * config.damping;
55
- nodes[i].nudgedX += nodes[i].velocityX;
56
- nodes[i].nudgedY += nodes[i].velocityY;
57
- }
58
- }
59
- return nodes.map((node) => ({
60
- ...node.position,
61
- nudgedX: node.nudgedX,
62
- nudgedY: node.nudgedY
63
- }));
64
- }
65
- var BadgeLayoutBuilder = class _BadgeLayoutBuilder {
66
- config;
67
- positions;
68
- constructor(positions) {
69
- this.positions = positions;
70
- this.config = { ...DEFAULT_CONFIG };
71
- }
72
- /**
73
- * Create a new layout builder with badge positions
74
- */
75
- static create(positions) {
76
- return new _BadgeLayoutBuilder(positions);
77
- }
78
- /**
79
- * Set the repulsion force (how strongly badges push apart)
80
- * Higher values = badges spread more aggressively
81
- */
82
- repulsion(force) {
83
- this.config.repulsionForce = force;
84
- return this;
85
- }
86
- /**
87
- * Set the anchor strength (how strongly badges stay near origin)
88
- * Higher values = badges stay closer to their original positions
89
- */
90
- anchorStrength(strength) {
91
- this.config.anchorStrength = strength;
92
- return this;
93
- }
94
- /**
95
- * Set the minimum distance between badge centers
96
- * Badges closer than this will be pushed apart
97
- */
98
- minDistance(distance) {
99
- this.config.minDistance = distance;
100
- return this;
101
- }
102
- /**
103
- * Set the number of simulation iterations
104
- * More iterations = more stable but slower
105
- */
106
- iterations(count) {
107
- this.config.iterations = count;
108
- return this;
109
- }
110
- /**
111
- * Set the damping factor (velocity decay per step)
112
- * Lower values = system settles faster but may be less stable
113
- */
114
- damping(factor) {
115
- this.config.damping = factor;
116
- return this;
117
- }
118
- /**
119
- * Run the simulation and return nudged positions
120
- */
121
- compute() {
122
- return computeLayout(this.positions, this.config);
123
- }
124
- };
125
- function findNearbyBadges(positions, x, y, threshold) {
126
- return positions.filter((p) => {
127
- const dist = Math.hypot(p.nudgedX - x, p.nudgedY - y);
128
- return dist <= threshold;
129
- });
130
- }
131
-
132
- // src/components/ui-lint/visibility-utils.ts
133
- function rectFromLTRB(left, top, right, bottom) {
134
- const width = Math.max(0, right - left);
135
- const height = Math.max(0, bottom - top);
136
- return { left, top, right, bottom, width, height };
137
- }
138
- function intersectRects(a, b) {
139
- const left = Math.max(a.left, b.left);
140
- const top = Math.max(a.top, b.top);
141
- const right = Math.min(a.right, b.right);
142
- const bottom = Math.min(a.bottom, b.bottom);
143
- if (right <= left || bottom <= top) return null;
144
- return rectFromLTRB(left, top, right, bottom);
145
- }
146
- function isOverflowClipping(style) {
147
- const vals = [style.overflow, style.overflowX, style.overflowY];
148
- return vals.some((v) => v && v !== "visible");
149
- }
150
- function getElementVisibleRect(element) {
151
- const el = element;
152
- const base = el.getBoundingClientRect();
153
- let visible = rectFromLTRB(
154
- base.left,
155
- base.top,
156
- base.right,
157
- base.bottom
158
- );
159
- const viewport = rectFromLTRB(0, 0, window.innerWidth, window.innerHeight);
160
- visible = intersectRects(visible, viewport);
161
- if (!visible) return null;
162
- let cur = el.parentElement;
163
- while (cur) {
164
- const style = window.getComputedStyle(cur);
165
- if (isOverflowClipping(style)) {
166
- const r = cur.getBoundingClientRect();
167
- const clip = rectFromLTRB(r.left, r.top, r.right, r.bottom);
168
- visible = intersectRects(visible, clip);
169
- if (!visible) return null;
170
- }
171
- cur = cur.parentElement;
172
- }
173
- return visible;
174
- }
175
- function isElementCoveredByOverlay(element, badgeX, badgeY) {
176
- const elementsAtPoint = document.elementsFromPoint(badgeX, badgeY);
177
- for (const el of elementsAtPoint) {
178
- if (el.hasAttribute("data-ui-lint")) continue;
179
- if (el === element || element.contains(el) || el.contains(element)) {
180
- return false;
181
- }
182
- const style = window.getComputedStyle(el);
183
- const position = style.position;
184
- const zIndex = parseInt(style.zIndex, 10);
185
- const isOverlay = (position === "fixed" || position === "absolute") && (zIndex > 0 || style.zIndex === "auto" || style.zIndex === "inherit");
186
- if (isOverlay) {
187
- return true;
188
- }
189
- }
190
- return false;
191
- }
192
-
193
- // src/components/ui-lint/ElementBadges.tsx
194
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
195
- var STYLES = {
196
- bg: "rgba(17, 24, 39, 0.95)",
197
- success: "#10B981",
198
- warning: "#F59E0B",
199
- error: "#EF4444",
200
- text: "#FFFFFF",
201
- border: "rgba(255, 255, 255, 0.2)",
202
- highlight: "#3B82F6",
203
- font: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
204
- shadow: "0 2px 8px rgba(0, 0, 0, 0.3)"
205
- };
206
- var NEAR_DISTANCE = 0;
207
- var FAR_DISTANCE = 150;
208
- var MIN_SCALE = 0.5;
209
- var MAX_SCALE = 1;
210
- function getScaleFromDistance(distance) {
211
- if (distance <= NEAR_DISTANCE) return MAX_SCALE;
212
- if (distance >= FAR_DISTANCE) return MIN_SCALE;
213
- const t = (distance - NEAR_DISTANCE) / (FAR_DISTANCE - NEAR_DISTANCE);
214
- return MAX_SCALE - t * (MAX_SCALE - MIN_SCALE);
215
- }
216
- function getBadgeColor(issueCount) {
217
- if (issueCount === 0) return STYLES.success;
218
- return STYLES.warning;
219
- }
220
- function formatElementLabel(element) {
221
- const tag = element.tagName.toLowerCase();
222
- const source = element.source;
223
- if (source) {
224
- const fileName = source.fileName.split("/").pop() || "Unknown";
225
- return `${tag} > ${fileName}`;
226
- }
227
- return tag;
228
- }
229
- var NEARBY_THRESHOLD = 30;
230
- var WINDOW_EDGE_THRESHOLD = 20;
231
- var BADGE_SIZE = 18;
232
- function snapToWindowBounds(x, y) {
233
- const minX = WINDOW_EDGE_THRESHOLD;
234
- const maxX = window.innerWidth - BADGE_SIZE - WINDOW_EDGE_THRESHOLD;
235
- const minY = WINDOW_EDGE_THRESHOLD;
236
- const maxY = window.innerHeight - BADGE_SIZE - WINDOW_EDGE_THRESHOLD;
237
- return {
238
- x: Math.max(minX, Math.min(maxX, x)),
239
- y: Math.max(minY, Math.min(maxY, y))
240
- };
241
- }
242
- function shouldShowBadge(issue, isAltKeyPressed) {
243
- if (issue.status === "complete" && issue.issues.length === 0) {
244
- return false;
245
- }
246
- if (issue.status === "error") return true;
247
- if (issue.status === "complete" && issue.issues.length > 0) return true;
248
- if (issue.status === "scanning" || issue.status === "pending") {
249
- return isAltKeyPressed;
250
- }
251
- return false;
252
- }
253
- function BadgeAnimationStyles() {
254
- return /* @__PURE__ */ jsx("style", { children: `
255
- @keyframes uilint-badge-spin {
256
- from { transform: rotate(0deg); }
257
- to { transform: rotate(360deg); }
258
- }
259
- ` });
260
- }
261
- function ElementBadges() {
262
- const { autoScanState, elementIssuesCache, setInspectedElement } = useUILintContext();
263
- const [mounted, setMounted] = useState(false);
264
- const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });
265
- const [badgePositions, setBadgePositions] = useState([]);
266
- const [isAltKeyPressed, setIsAltKeyPressed] = useState(false);
267
- const hoveredFilePath = useUILintStore((s) => s.hoveredFilePath);
268
- const selectedFilePath = useUILintStore(
269
- (s) => s.selectedFilePath
270
- );
271
- useEffect(() => {
272
- setMounted(true);
273
- }, []);
274
- useEffect(() => {
275
- const handleKeyDown = (e) => {
276
- if (e.altKey) {
277
- setIsAltKeyPressed(true);
278
- }
279
- };
280
- const handleKeyUp = (e) => {
281
- if (!e.altKey) {
282
- setIsAltKeyPressed(false);
283
- }
284
- };
285
- const handleBlur = () => {
286
- setIsAltKeyPressed(false);
287
- };
288
- window.addEventListener("keydown", handleKeyDown);
289
- window.addEventListener("keyup", handleKeyUp);
290
- window.addEventListener("blur", handleBlur);
291
- return () => {
292
- window.removeEventListener("keydown", handleKeyDown);
293
- window.removeEventListener("keyup", handleKeyUp);
294
- window.removeEventListener("blur", handleBlur);
295
- };
296
- }, []);
297
- useEffect(() => {
298
- const handleMouseMove = (e) => {
299
- setCursorPos({ x: e.clientX, y: e.clientY });
300
- };
301
- window.addEventListener("mousemove", handleMouseMove);
302
- return () => window.removeEventListener("mousemove", handleMouseMove);
303
- }, []);
304
- useEffect(() => {
305
- if (autoScanState.status === "idle") {
306
- setBadgePositions([]);
307
- return;
308
- }
309
- const updatePositions = () => {
310
- const positions = [];
311
- for (const element of autoScanState.elements) {
312
- const issue = elementIssuesCache.get(element.id);
313
- if (!issue) continue;
314
- if (!element.element || !document.contains(element.element)) continue;
315
- const rect = element.element.getBoundingClientRect();
316
- const visible = getElementVisibleRect(element.element);
317
- if (!visible) continue;
318
- const desiredX = rect.right - 8;
319
- const desiredY = rect.top - 8;
320
- const x = Math.min(desiredX, visible.right - 8);
321
- const y = Math.max(visible.top + 2, desiredY);
322
- const testX = visible.left + Math.min(8, visible.width / 2);
323
- const testY = visible.top + Math.min(8, visible.height / 2);
324
- if (isElementCoveredByOverlay(element.element, testX, testY)) {
325
- continue;
326
- }
327
- const visibleRect = DOMRect.fromRect({
328
- x: visible.left,
329
- y: visible.top,
330
- width: visible.width,
331
- height: visible.height
332
- });
333
- positions.push({ element, issue, x, y, rect: visibleRect });
334
- }
335
- setBadgePositions(positions);
336
- };
337
- let rafId = null;
338
- const scheduleUpdate = () => {
339
- if (rafId !== null) return;
340
- rafId = requestAnimationFrame(() => {
341
- rafId = null;
342
- updatePositions();
343
- });
344
- };
345
- scheduleUpdate();
346
- const handleScroll = () => scheduleUpdate();
347
- const handleResize = () => scheduleUpdate();
348
- const handleVisibility = () => {
349
- if (document.visibilityState === "visible") {
350
- scheduleUpdate();
351
- }
352
- };
353
- window.addEventListener("scroll", handleScroll, true);
354
- window.addEventListener("resize", handleResize);
355
- document.addEventListener("visibilitychange", handleVisibility);
356
- return () => {
357
- if (rafId !== null) {
358
- cancelAnimationFrame(rafId);
359
- }
360
- window.removeEventListener("scroll", handleScroll, true);
361
- window.removeEventListener("resize", handleResize);
362
- document.removeEventListener("visibilitychange", handleVisibility);
363
- };
364
- }, [autoScanState.status, autoScanState.elements, elementIssuesCache]);
365
- const handleSelect = useCallback(
366
- (element, issue) => {
367
- const inspected = {
368
- element: element.element,
369
- source: element.source,
370
- rect: element.element.getBoundingClientRect(),
371
- scannedElementId: element.id
372
- };
373
- setInspectedElement(inspected);
374
- },
375
- [setInspectedElement]
376
- );
377
- const nudgedPositions = useMemo(
378
- () => BadgeLayoutBuilder.create(badgePositions).minDistance(24).repulsion(50).anchorStrength(0.3).iterations(50).compute(),
379
- [badgePositions]
380
- );
381
- const visibleBadges = useMemo(() => {
382
- let filtered = nudgedPositions.filter(
383
- (pos) => shouldShowBadge(pos.issue, isAltKeyPressed)
384
- );
385
- const activeFilePath = selectedFilePath || hoveredFilePath;
386
- if (activeFilePath) {
387
- filtered = filtered.filter(
388
- (pos) => pos.element.source.fileName === activeFilePath
389
- );
390
- }
391
- return filtered;
392
- }, [nudgedPositions, isAltKeyPressed, selectedFilePath, hoveredFilePath]);
393
- const handleUILintInteraction = useCallback(
394
- (e) => {
395
- e.stopPropagation();
396
- },
397
- []
398
- );
399
- if (!mounted) return null;
400
- if (autoScanState.status === "idle") return null;
401
- const content = /* @__PURE__ */ jsxs(
402
- "div",
403
- {
404
- "data-ui-lint": true,
405
- onMouseDown: handleUILintInteraction,
406
- onPointerDown: handleUILintInteraction,
407
- onClick: handleUILintInteraction,
408
- onKeyDown: handleUILintInteraction,
409
- style: { pointerEvents: "none" },
410
- children: [
411
- /* @__PURE__ */ jsx(BadgeAnimationStyles, {}),
412
- visibleBadges.filter((pos, idx, arr) => {
413
- const id = pos.element.id;
414
- return arr.findIndex((p) => p.element.id === id) === idx;
415
- }).map((nudgedPos) => {
416
- const distance = Math.hypot(
417
- nudgedPos.nudgedX - cursorPos.x,
418
- nudgedPos.nudgedY - cursorPos.y
419
- );
420
- const nearbyBadges = findNearbyBadges(
421
- visibleBadges,
422
- nudgedPos.nudgedX,
423
- nudgedPos.nudgedY,
424
- NEARBY_THRESHOLD
425
- );
426
- return /* @__PURE__ */ jsx(
427
- NudgedBadge,
428
- {
429
- position: nudgedPos,
430
- distance,
431
- nearbyBadges,
432
- cursorPos,
433
- onSelect: handleSelect
434
- },
435
- nudgedPos.element.id
436
- );
437
- })
438
- ]
439
- }
440
- );
441
- return createPortal(content, document.body);
442
- }
443
- function NudgedBadge({
444
- position,
445
- distance,
446
- nearbyBadges,
447
- cursorPos,
448
- onSelect
449
- }) {
450
- const [isExpanded, setIsExpanded] = useState(false);
451
- const [hoveredIndex, setHoveredIndex] = useState(null);
452
- const closeTimeoutRef = React.useRef(null);
453
- const { element, issue, rect, nudgedX, nudgedY } = position;
454
- const hasNearbyBadges = nearbyBadges.length > 1;
455
- const snappedPosition = useMemo(
456
- () => snapToWindowBounds(nudgedX, nudgedY),
457
- [nudgedX, nudgedY]
458
- );
459
- const badgeColor = useMemo(() => {
460
- if (issue.status === "error") return STYLES.error;
461
- if (issue.status === "scanning") return STYLES.highlight;
462
- if (issue.status === "pending") return "rgba(156, 163, 175, 0.7)";
463
- if (issue.status === "complete") {
464
- return getBadgeColor(issue.issues.length);
465
- }
466
- return STYLES.success;
467
- }, [issue]);
468
- const handleMouseEnter = useCallback(() => {
469
- if (closeTimeoutRef.current) {
470
- clearTimeout(closeTimeoutRef.current);
471
- closeTimeoutRef.current = null;
472
- }
473
- setIsExpanded(true);
474
- }, []);
475
- const handleMouseLeave = useCallback(() => {
476
- closeTimeoutRef.current = setTimeout(() => {
477
- setIsExpanded(false);
478
- setHoveredIndex(null);
479
- }, 150);
480
- }, []);
481
- const handleClick = useCallback(
482
- (e) => {
483
- e.preventDefault();
484
- e.stopPropagation();
485
- onSelect(element, issue);
486
- },
487
- [element, issue, onSelect]
488
- );
489
- const hoveredBadge = useMemo(() => {
490
- if (hoveredIndex === null) return null;
491
- return nearbyBadges[hoveredIndex] ?? null;
492
- }, [hoveredIndex, nearbyBadges]);
493
- const dropdownStyle = useMemo(() => {
494
- const preferRight = snappedPosition.x < window.innerWidth - 220;
495
- const preferBelow = snappedPosition.y < window.innerHeight - 200;
496
- return {
497
- position: "fixed",
498
- top: preferBelow ? snappedPosition.y + 12 : void 0,
499
- bottom: preferBelow ? void 0 : window.innerHeight - snappedPosition.y + 12,
500
- left: preferRight ? snappedPosition.x - 8 : void 0,
501
- right: preferRight ? void 0 : window.innerWidth - snappedPosition.x - 8,
502
- zIndex: 1e5,
503
- backgroundColor: STYLES.bg,
504
- borderRadius: "8px",
505
- border: `1px solid ${STYLES.border}`,
506
- boxShadow: "0 4px 20px rgba(0, 0, 0, 0.4)",
507
- padding: "4px 0",
508
- minWidth: "200px",
509
- fontFamily: STYLES.font,
510
- pointerEvents: "auto"
511
- // Re-enable pointer events for interactive dropdown
512
- };
513
- }, [snappedPosition]);
514
- const scale = isExpanded ? 1.1 : getScaleFromDistance(distance);
515
- const issueCount = issue.status === "complete" ? issue.issues.length : 0;
516
- return /* @__PURE__ */ jsxs(Fragment, { children: [
517
- isExpanded && !hoveredBadge && /* @__PURE__ */ jsx(
518
- "div",
519
- {
520
- style: {
521
- position: "fixed",
522
- top: rect.top - 2,
523
- left: rect.left - 2,
524
- width: rect.width + 4,
525
- height: rect.height + 4,
526
- border: `2px solid ${STYLES.highlight}`,
527
- borderRadius: "4px",
528
- pointerEvents: "none",
529
- zIndex: 99994,
530
- boxShadow: `0 0 0 1px rgba(59, 130, 246, 0.3)`
531
- },
532
- "data-ui-lint": true
533
- }
534
- ),
535
- hoveredBadge && /* @__PURE__ */ jsx(
536
- "div",
537
- {
538
- style: {
539
- position: "fixed",
540
- top: hoveredBadge.rect.top - 2,
541
- left: hoveredBadge.rect.left - 2,
542
- width: hoveredBadge.rect.width + 4,
543
- height: hoveredBadge.rect.height + 4,
544
- border: `2px solid ${STYLES.highlight}`,
545
- borderRadius: "4px",
546
- pointerEvents: "none",
547
- zIndex: 99994,
548
- boxShadow: `0 0 0 1px rgba(59, 130, 246, 0.3)`
549
- },
550
- "data-ui-lint": true
551
- }
552
- ),
553
- /* @__PURE__ */ jsx(
554
- "div",
555
- {
556
- style: {
557
- position: "fixed",
558
- top: snappedPosition.y - 0,
559
- left: snappedPosition.x - 0,
560
- zIndex: isExpanded ? 99999 : 99995,
561
- cursor: "pointer",
562
- transition: "transform 0.1s ease-out",
563
- transform: `scale(${scale})`,
564
- transformOrigin: "center center",
565
- pointerEvents: "auto"
566
- // Re-enable pointer events for interactive badge
567
- },
568
- "data-ui-lint": true,
569
- onMouseEnter: handleMouseEnter,
570
- onMouseLeave: handleMouseLeave,
571
- onClick: handleClick,
572
- children: /* @__PURE__ */ jsx(
573
- "div",
574
- {
575
- style: {
576
- display: "flex",
577
- alignItems: "center",
578
- justifyContent: "center",
579
- width: "18px",
580
- height: "18px",
581
- borderRadius: "50%",
582
- backgroundColor: badgeColor,
583
- boxShadow: STYLES.shadow,
584
- border: `1px solid ${STYLES.border}`
585
- },
586
- children: issue.status === "scanning" ? /* @__PURE__ */ jsx(
587
- "div",
588
- {
589
- style: {
590
- width: "10px",
591
- height: "10px",
592
- border: "2px solid rgba(255, 255, 255, 0.3)",
593
- borderTopColor: "#FFFFFF",
594
- borderRadius: "50%",
595
- animation: "uilint-badge-spin 0.8s linear infinite"
596
- }
597
- }
598
- ) : issue.status === "error" ? /* @__PURE__ */ jsx(ExclamationIconTiny, {}) : issue.status === "pending" ? /* @__PURE__ */ jsx(
599
- "div",
600
- {
601
- style: {
602
- width: "6px",
603
- height: "6px",
604
- borderRadius: "50%",
605
- backgroundColor: "rgba(255, 255, 255, 0.4)"
606
- }
607
- }
608
- ) : issueCount === 0 ? /* @__PURE__ */ jsx(CheckIconTiny, {}) : /* @__PURE__ */ jsx(
609
- "span",
610
- {
611
- style: {
612
- color: STYLES.text,
613
- fontSize: "10px",
614
- fontWeight: 700,
615
- fontFamily: STYLES.font
616
- },
617
- children: issueCount > 9 ? "9+" : issueCount
618
- }
619
- )
620
- }
621
- )
622
- }
623
- ),
624
- isExpanded && hasNearbyBadges && /* @__PURE__ */ jsx(
625
- "div",
626
- {
627
- style: dropdownStyle,
628
- "data-ui-lint": true,
629
- onMouseEnter: handleMouseEnter,
630
- onMouseLeave: handleMouseLeave,
631
- children: nearbyBadges.map((badge, index) => /* @__PURE__ */ jsx(
632
- DropdownItem,
633
- {
634
- badge,
635
- isHovered: hoveredIndex === index,
636
- onMouseEnter: () => setHoveredIndex(index),
637
- onMouseLeave: () => setHoveredIndex(null),
638
- onClick: () => onSelect(badge.element, badge.issue)
639
- },
640
- badge.element.id
641
- ))
642
- }
643
- )
644
- ] });
645
- }
646
- function DropdownItem({
647
- badge,
648
- isHovered,
649
- onMouseEnter,
650
- onMouseLeave,
651
- onClick
652
- }) {
653
- const elementLabel = formatElementLabel(badge.element);
654
- const issueCount = badge.issue.status === "complete" ? badge.issue.issues.length : 0;
655
- const color = getBadgeColor(issueCount);
656
- return /* @__PURE__ */ jsxs(
657
- "div",
658
- {
659
- style: {
660
- display: "flex",
661
- alignItems: "center",
662
- justifyContent: "space-between",
663
- padding: "8px 12px",
664
- cursor: "pointer",
665
- backgroundColor: isHovered ? "rgba(59, 130, 246, 0.15)" : "transparent",
666
- transition: "background-color 0.1s"
667
- },
668
- onMouseEnter,
669
- onMouseLeave,
670
- onClick,
671
- children: [
672
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
673
- /* @__PURE__ */ jsx(
674
- "div",
675
- {
676
- style: {
677
- width: "6px",
678
- height: "6px",
679
- borderRadius: "50%",
680
- backgroundColor: color
681
- }
682
- }
683
- ),
684
- /* @__PURE__ */ jsx(
685
- "span",
686
- {
687
- style: {
688
- fontSize: "12px",
689
- color: STYLES.text,
690
- maxWidth: "160px",
691
- overflow: "hidden",
692
- textOverflow: "ellipsis",
693
- whiteSpace: "nowrap"
694
- },
695
- children: elementLabel
696
- }
697
- )
698
- ] }),
699
- badge.issue.status === "complete" && (issueCount === 0 ? /* @__PURE__ */ jsx(
700
- "div",
701
- {
702
- style: {
703
- width: "14px",
704
- height: "14px",
705
- borderRadius: "50%",
706
- backgroundColor: color,
707
- display: "flex",
708
- alignItems: "center",
709
- justifyContent: "center"
710
- },
711
- children: /* @__PURE__ */ jsx(CheckIcon, {})
712
- }
713
- ) : /* @__PURE__ */ jsx(
714
- "div",
715
- {
716
- style: {
717
- minWidth: "16px",
718
- height: "16px",
719
- padding: "0 4px",
720
- borderRadius: "8px",
721
- backgroundColor: color,
722
- color: STYLES.text,
723
- fontSize: "10px",
724
- fontWeight: 700,
725
- display: "flex",
726
- alignItems: "center",
727
- justifyContent: "center"
728
- },
729
- children: issueCount > 9 ? "9+" : issueCount
730
- }
731
- )),
732
- badge.issue.status === "scanning" && /* @__PURE__ */ jsx(
733
- "div",
734
- {
735
- style: {
736
- width: "12px",
737
- height: "12px",
738
- border: "2px solid rgba(59, 130, 246, 0.3)",
739
- borderTopColor: STYLES.highlight,
740
- borderRadius: "50%",
741
- animation: "uilint-badge-spin 0.8s linear infinite"
742
- }
743
- }
744
- ),
745
- badge.issue.status === "error" && /* @__PURE__ */ jsx(
746
- "div",
747
- {
748
- style: {
749
- width: "14px",
750
- height: "14px",
751
- borderRadius: "50%",
752
- backgroundColor: STYLES.error,
753
- display: "flex",
754
- alignItems: "center",
755
- justifyContent: "center"
756
- },
757
- children: /* @__PURE__ */ jsx(ExclamationIcon, {})
758
- }
759
- ),
760
- badge.issue.status === "pending" && /* @__PURE__ */ jsx(
761
- "div",
762
- {
763
- style: {
764
- width: "8px",
765
- height: "8px",
766
- borderRadius: "50%",
767
- backgroundColor: "rgba(156, 163, 175, 0.5)"
768
- }
769
- }
770
- )
771
- ]
772
- }
773
- );
774
- }
775
- function CheckIcon() {
776
- return /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
777
- "path",
778
- {
779
- d: "M20 6L9 17l-5-5",
780
- stroke: STYLES.text,
781
- strokeWidth: "3",
782
- strokeLinecap: "round",
783
- strokeLinejoin: "round"
784
- }
785
- ) });
786
- }
787
- function CheckIconTiny() {
788
- return /* @__PURE__ */ jsx("svg", { width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
789
- "path",
790
- {
791
- d: "M20 6L9 17l-5-5",
792
- stroke: STYLES.text,
793
- strokeWidth: "4",
794
- strokeLinecap: "round",
795
- strokeLinejoin: "round"
796
- }
797
- ) });
798
- }
799
- function ExclamationIconTiny() {
800
- return /* @__PURE__ */ jsx("svg", { width: "8", height: "8", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
801
- "path",
802
- {
803
- d: "M12 8v4M12 16h.01",
804
- stroke: STYLES.text,
805
- strokeWidth: "4",
806
- strokeLinecap: "round",
807
- strokeLinejoin: "round"
808
- }
809
- ) });
810
- }
811
- function ExclamationIcon() {
812
- return /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
813
- "path",
814
- {
815
- d: "M12 8v4M12 16h.01",
816
- stroke: STYLES.text,
817
- strokeWidth: "3",
818
- strokeLinecap: "round",
819
- strokeLinejoin: "round"
820
- }
821
- ) });
822
- }
823
- export {
824
- ElementBadges
825
- };