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,933 +0,0 @@
1
- "use client";
2
- import {
3
- Badge,
4
- fetchSourceWithWindow
5
- } from "./chunk-S4IWHBOQ.js";
6
- import {
7
- buildEditorUrl,
8
- useUILintContext,
9
- useUILintStore
10
- } from "./chunk-552GIJIQ.js";
11
-
12
- // src/components/ui-lint/InspectionPanel.tsx
13
- import React, {
14
- useState,
15
- useEffect,
16
- useCallback,
17
- useMemo,
18
- useRef
19
- } from "react";
20
- import { createPortal } from "react-dom";
21
-
22
- // src/components/ui-lint/code-formatting.ts
23
- function leadingWhitespace(s) {
24
- const match = s.match(/^[\t ]+/);
25
- return match ? match[0] : "";
26
- }
27
- function dedentLines(lines) {
28
- if (!lines.length) return { lines, removed: 0 };
29
- const nonEmpty = lines.filter((l) => l.trim().length > 0);
30
- if (!nonEmpty.length) return { lines, removed: 0 };
31
- let min = Infinity;
32
- for (const l of nonEmpty) {
33
- min = Math.min(min, leadingWhitespace(l).length);
34
- if (min === 0) break;
35
- }
36
- if (!Number.isFinite(min) || min <= 0) return { lines, removed: 0 };
37
- return {
38
- removed: min,
39
- lines: lines.map((l) => l.length >= min ? l.slice(min) : "")
40
- };
41
- }
42
-
43
- // src/components/ui-lint/inspection-panel-positioning.ts
44
- function clamp(n, min, max) {
45
- return Math.max(min, Math.min(max, n));
46
- }
47
- function overflowScore(left, top, size, viewport, padding) {
48
- const right = left + size.width;
49
- const bottom = top + size.height;
50
- const oLeft = Math.max(0, padding - left);
51
- const oTop = Math.max(0, padding - top);
52
- const oRight = Math.max(0, right - (viewport.width - padding));
53
- const oBottom = Math.max(0, bottom - (viewport.height - padding));
54
- return oLeft * 2 + oRight * 2 + oTop + oBottom;
55
- }
56
- function intersectionArea(a, b) {
57
- const x1 = Math.max(a.left, b.left);
58
- const y1 = Math.max(a.top, b.top);
59
- const x2 = Math.min(a.right, b.right);
60
- const y2 = Math.min(a.bottom, b.bottom);
61
- const w = Math.max(0, x2 - x1);
62
- const h = Math.max(0, y2 - y1);
63
- return w * h;
64
- }
65
- function computeInspectionPanelPosition(params) {
66
- const { rect, popover, viewport } = params;
67
- const padding = params.padding ?? 12;
68
- const badge = { x: rect.right, y: rect.top };
69
- const xMax = viewport.width - popover.width - padding;
70
- const yMax = viewport.height - popover.height - padding;
71
- const rawCandidates = [
72
- {
73
- placement: "right",
74
- left: rect.right + padding,
75
- top: rect.top
76
- },
77
- {
78
- // keep close to the badge by aligning the right edge to rect.right
79
- placement: "left",
80
- left: rect.right - popover.width - padding,
81
- top: rect.top
82
- },
83
- {
84
- placement: "bottom",
85
- left: rect.right - popover.width,
86
- top: rect.bottom + padding
87
- },
88
- {
89
- placement: "top",
90
- left: rect.right - popover.width,
91
- top: rect.top - popover.height - padding
92
- }
93
- ];
94
- const candidates = rawCandidates.map((c) => ({
95
- raw: c,
96
- clamped: {
97
- ...c,
98
- left: clamp(c.left, padding, xMax),
99
- top: clamp(c.top, padding, yMax)
100
- }
101
- }));
102
- const distanceToBadge = (pos) => {
103
- const nearestX = clamp(badge.x, pos.left, pos.left + popover.width);
104
- const nearestY = clamp(badge.y, pos.top, pos.top + popover.height);
105
- const dx = badge.x - nearestX;
106
- const dy = badge.y - nearestY;
107
- return dx * dx + dy * dy;
108
- };
109
- const score = (raw, pos) => {
110
- const overflow = overflowScore(
111
- pos.left,
112
- pos.top,
113
- popover,
114
- viewport,
115
- padding
116
- );
117
- const clampDelta = Math.abs(raw.left - pos.left) + Math.abs(raw.top - pos.top);
118
- const box = {
119
- left: pos.left,
120
- top: pos.top,
121
- right: pos.left + popover.width,
122
- bottom: pos.top + popover.height
123
- };
124
- const overlap = intersectionArea(box, rect);
125
- const dist = distanceToBadge(pos);
126
- return overflow * 1e6 + clampDelta * 1e3 + overlap * 10 + dist;
127
- };
128
- let best = candidates[0].clamped;
129
- let bestScore = score(candidates[0].raw, candidates[0].clamped);
130
- for (const c of candidates.slice(1)) {
131
- const s = score(c.raw, c.clamped);
132
- if (s < bestScore) {
133
- best = c.clamped;
134
- bestScore = s;
135
- }
136
- }
137
- return best;
138
- }
139
-
140
- // src/components/ui-lint/InspectionPanel.tsx
141
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
142
- var STYLES = {
143
- bg: "rgba(17, 24, 39, 0.98)",
144
- bgSurface: "rgba(31, 41, 55, 0.95)",
145
- border: "rgba(75, 85, 99, 0.6)",
146
- text: "#F9FAFB",
147
- textMuted: "#9CA3AF",
148
- textDim: "#6B7280",
149
- accent: "#3B82F6",
150
- accentHover: "#2563EB",
151
- success: "#10B981",
152
- warning: "#F59E0B",
153
- error: "#EF4444",
154
- shadow: "0 8px 32px rgba(0, 0, 0, 0.5)",
155
- blur: "blur(12px)",
156
- font: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
157
- fontMono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace'
158
- };
159
- var POPOVER_WIDTH = 450;
160
- var POPOVER_MAX_HEIGHT = 450;
161
- function InspectionPanel() {
162
- const {
163
- inspectedElement,
164
- setInspectedElement,
165
- elementIssuesCache,
166
- autoScanState
167
- } = useUILintContext();
168
- const fileIssuesCache = useUILintStore((s) => s.fileIssuesCache);
169
- const appRoot = useUILintStore((s) => s.appRoot);
170
- const workspaceRoot = useUILintStore((s) => s.workspaceRoot);
171
- const editorBaseDir = appRoot || workspaceRoot;
172
- const [mounted, setMounted] = useState(false);
173
- const [showFullContext, setShowFullContext] = useState(true);
174
- const popoverRef = useRef(null);
175
- const [position, setPosition] = useState({ top: 0, left: 0 });
176
- useEffect(() => {
177
- setMounted(true);
178
- }, []);
179
- useEffect(() => {
180
- if (!inspectedElement) return;
181
- const handleClickOutside = (e) => {
182
- const target = e.target;
183
- if (target?.closest?.("[data-ui-lint]")) return;
184
- const rect = inspectedElement.element.getBoundingClientRect();
185
- if (rect.top < -1e3 || rect.left < -1e3) {
186
- inspectedElement.element.remove();
187
- }
188
- setInspectedElement(null);
189
- };
190
- const timer = setTimeout(() => {
191
- document.addEventListener("click", handleClickOutside, true);
192
- }, 50);
193
- return () => {
194
- clearTimeout(timer);
195
- document.removeEventListener("click", handleClickOutside, true);
196
- };
197
- }, [inspectedElement, setInspectedElement]);
198
- const isFileLevelIssue = useMemo(() => {
199
- if (!inspectedElement) return false;
200
- const rect = inspectedElement.element.getBoundingClientRect();
201
- return rect.top < -1e3 || rect.left < -1e3;
202
- }, [inspectedElement]);
203
- const cachedIssue = useMemo(() => {
204
- if (!inspectedElement) return null;
205
- if (isFileLevelIssue && inspectedElement.source) {
206
- const fileIssues = fileIssuesCache.get(inspectedElement.source.fileName);
207
- if (fileIssues && fileIssues.length > 0) {
208
- const matchingIssues = fileIssues.filter((issue) => {
209
- if (issue.line !== inspectedElement.source.lineNumber) return false;
210
- if (inspectedElement.source.columnNumber && issue.column !== inspectedElement.source.columnNumber) {
211
- return false;
212
- }
213
- return true;
214
- });
215
- return {
216
- elementId: `file:${inspectedElement.source.fileName}`,
217
- issues: matchingIssues.length > 0 ? matchingIssues : fileIssues,
218
- status: "complete"
219
- };
220
- }
221
- return null;
222
- }
223
- if (inspectedElement.scannedElementId) {
224
- const cached = elementIssuesCache.get(inspectedElement.scannedElementId);
225
- if (cached) return cached;
226
- }
227
- if (inspectedElement.source) {
228
- for (const [, issue] of elementIssuesCache) {
229
- const scannedElement = autoScanState.elements.find(
230
- (el) => el.id === issue.elementId
231
- );
232
- if (scannedElement?.source?.fileName === inspectedElement.source.fileName) {
233
- return issue;
234
- }
235
- }
236
- }
237
- return null;
238
- }, [
239
- inspectedElement,
240
- elementIssuesCache,
241
- fileIssuesCache,
242
- autoScanState.elements,
243
- isFileLevelIssue
244
- ]);
245
- const eslintIssues = useMemo(() => cachedIssue?.issues || [], [cachedIssue]);
246
- const lineRange = useMemo(() => {
247
- if (!eslintIssues.length) {
248
- return inspectedElement?.source.lineNumber.toString() || "0";
249
- }
250
- const lines = eslintIssues.map((i) => i.line).sort((a, b) => a - b);
251
- const min = lines[0];
252
- const max = lines[lines.length - 1];
253
- return min === max ? min.toString() : `${min}-${max}`;
254
- }, [eslintIssues, inspectedElement]);
255
- useEffect(() => {
256
- if (!inspectedElement) return;
257
- let rafId = null;
258
- const update = () => {
259
- rafId = null;
260
- if (isFileLevelIssue) {
261
- const measured2 = popoverRef.current ? {
262
- width: popoverRef.current.offsetWidth,
263
- height: popoverRef.current.offsetHeight
264
- } : null;
265
- const popoverSize2 = measured2 ?? {
266
- width: POPOVER_WIDTH,
267
- height: POPOVER_MAX_HEIGHT
268
- };
269
- const allUILintElements = document.querySelectorAll("[data-ui-lint]");
270
- let scanResultsPopover = null;
271
- for (const el of allUILintElements) {
272
- const htmlEl = el;
273
- if (htmlEl === popoverRef.current) continue;
274
- const style = window.getComputedStyle(htmlEl);
275
- const width = htmlEl.offsetWidth;
276
- const isRightWidth = width >= 310 && width <= 330;
277
- const isRightPosition = style.position === "relative" || style.position === "absolute";
278
- const rect2 = htmlEl.getBoundingClientRect();
279
- const isVisible = rect2.width > 0 && rect2.height > 0;
280
- if (isRightWidth && isRightPosition && isVisible) {
281
- scanResultsPopover = htmlEl;
282
- break;
283
- }
284
- }
285
- const gap = 12;
286
- const padding = 12;
287
- if (scanResultsPopover) {
288
- const scanRect = scanResultsPopover.getBoundingClientRect();
289
- const left = scanRect.right + gap;
290
- const top = scanRect.top;
291
- const maxLeft = window.innerWidth - popoverSize2.width - padding;
292
- const adjustedLeft = Math.min(left, maxLeft);
293
- const adjustedTop = Math.max(padding, top);
294
- setPosition({ top: adjustedTop, left: adjustedLeft });
295
- } else {
296
- const top = padding;
297
- const left = Math.max(
298
- padding,
299
- window.innerWidth - popoverSize2.width - padding
300
- );
301
- setPosition({ top, left });
302
- }
303
- return;
304
- }
305
- const rect = inspectedElement.element.getBoundingClientRect();
306
- const measured = popoverRef.current ? {
307
- width: popoverRef.current.offsetWidth,
308
- height: popoverRef.current.offsetHeight
309
- } : null;
310
- const popoverSize = measured ?? {
311
- width: POPOVER_WIDTH,
312
- height: POPOVER_MAX_HEIGHT
313
- };
314
- const next = computeInspectionPanelPosition({
315
- rect,
316
- popover: popoverSize,
317
- viewport: { width: window.innerWidth, height: window.innerHeight },
318
- padding: 12
319
- });
320
- setPosition({ top: next.top, left: next.left });
321
- };
322
- const schedule = () => {
323
- if (rafId != null) return;
324
- rafId = window.requestAnimationFrame(update);
325
- };
326
- schedule();
327
- if (isFileLevelIssue) {
328
- window.addEventListener("resize", schedule);
329
- } else {
330
- window.addEventListener("scroll", schedule, true);
331
- window.addEventListener("resize", schedule);
332
- }
333
- return () => {
334
- if (rafId != null) window.cancelAnimationFrame(rafId);
335
- if (isFileLevelIssue) {
336
- window.removeEventListener("resize", schedule);
337
- } else {
338
- window.removeEventListener("scroll", schedule, true);
339
- window.removeEventListener("resize", schedule);
340
- }
341
- };
342
- }, [inspectedElement, showFullContext, isFileLevelIssue]);
343
- const handleOpenInCursor = useCallback(() => {
344
- if (!inspectedElement) return;
345
- const url = buildEditorUrl(
346
- inspectedElement.source,
347
- "cursor",
348
- editorBaseDir
349
- );
350
- window.open(url, "_blank");
351
- }, [inspectedElement, editorBaseDir]);
352
- const handleUILintInteraction = useCallback(
353
- (e) => {
354
- e.stopPropagation();
355
- },
356
- []
357
- );
358
- if (!mounted || !inspectedElement) return null;
359
- const content = /* @__PURE__ */ jsxs(
360
- "div",
361
- {
362
- ref: popoverRef,
363
- "data-ui-lint": true,
364
- onMouseDown: handleUILintInteraction,
365
- onPointerDown: handleUILintInteraction,
366
- onClick: handleUILintInteraction,
367
- onKeyDown: handleUILintInteraction,
368
- style: {
369
- position: "fixed",
370
- top: position.top,
371
- left: position.left,
372
- width: POPOVER_WIDTH,
373
- maxHeight: POPOVER_MAX_HEIGHT,
374
- backgroundColor: STYLES.bg,
375
- backdropFilter: STYLES.blur,
376
- WebkitBackdropFilter: STYLES.blur,
377
- border: `1px solid ${STYLES.border}`,
378
- borderRadius: "12px",
379
- boxShadow: STYLES.shadow,
380
- fontFamily: STYLES.font,
381
- color: STYLES.text,
382
- overflow: "hidden",
383
- zIndex: 99998,
384
- animation: "uilint-popover-appear 0.15s ease-out",
385
- pointerEvents: "auto"
386
- // Ensure panel is interactive
387
- },
388
- children: [
389
- /* @__PURE__ */ jsx("style", { children: `
390
- @keyframes uilint-popover-appear {
391
- from { opacity: 0; transform: scale(0.95); }
392
- to { opacity: 1; transform: scale(1); }
393
- }
394
- @keyframes uilint-spin {
395
- from { transform: rotate(0deg); }
396
- to { transform: rotate(360deg); }
397
- }
398
- ` }),
399
- /* @__PURE__ */ jsx(
400
- PopoverHeader,
401
- {
402
- element: inspectedElement,
403
- issueCount: eslintIssues.length,
404
- lineRange,
405
- onClose: () => setInspectedElement(null)
406
- }
407
- ),
408
- /* @__PURE__ */ jsxs(
409
- "div",
410
- {
411
- style: {
412
- maxHeight: POPOVER_MAX_HEIGHT - 120,
413
- overflowY: "auto"
414
- },
415
- children: [
416
- cachedIssue?.status === "scanning" && /* @__PURE__ */ jsxs(
417
- "div",
418
- {
419
- style: {
420
- display: "flex",
421
- alignItems: "center",
422
- justifyContent: "center",
423
- padding: "32px",
424
- gap: "12px"
425
- },
426
- children: [
427
- /* @__PURE__ */ jsx(
428
- "div",
429
- {
430
- style: {
431
- width: "20px",
432
- height: "20px",
433
- border: `2px solid ${STYLES.border}`,
434
- borderTopColor: STYLES.accent,
435
- borderRadius: "50%",
436
- animation: "uilint-spin 0.8s linear infinite"
437
- }
438
- }
439
- ),
440
- /* @__PURE__ */ jsx("span", { style: { color: STYLES.textMuted, fontSize: "13px" }, children: "Scanning..." })
441
- ]
442
- }
443
- ),
444
- cachedIssue?.status === "complete" && eslintIssues.length === 0 && /* @__PURE__ */ jsxs(
445
- "div",
446
- {
447
- style: {
448
- display: "flex",
449
- alignItems: "center",
450
- justifyContent: "center",
451
- padding: "32px",
452
- gap: "8px",
453
- color: STYLES.success,
454
- fontSize: "13px"
455
- },
456
- children: [
457
- /* @__PURE__ */ jsx(CheckIcon, {}),
458
- "No issues found"
459
- ]
460
- }
461
- ),
462
- cachedIssue?.status === "complete" && eslintIssues.length > 0 && /* @__PURE__ */ jsx(
463
- IssuesList,
464
- {
465
- issues: eslintIssues,
466
- source: inspectedElement.source,
467
- showFullContext
468
- }
469
- ),
470
- !cachedIssue && /* @__PURE__ */ jsx(
471
- "div",
472
- {
473
- style: {
474
- padding: "24px 16px",
475
- textAlign: "center",
476
- color: STYLES.textMuted,
477
- fontSize: "12px"
478
- },
479
- children: "Enable live scanning to analyze this element"
480
- }
481
- )
482
- ]
483
- }
484
- ),
485
- cachedIssue?.status === "complete" && eslintIssues.length > 0 && /* @__PURE__ */ jsxs(
486
- "div",
487
- {
488
- style: {
489
- display: "flex",
490
- alignItems: "center",
491
- justifyContent: "space-between",
492
- padding: "10px 14px",
493
- borderTop: `1px solid ${STYLES.border}`,
494
- backgroundColor: STYLES.bgSurface
495
- },
496
- children: [
497
- /* @__PURE__ */ jsxs(
498
- "button",
499
- {
500
- onClick: () => setShowFullContext(!showFullContext),
501
- style: {
502
- display: "flex",
503
- alignItems: "center",
504
- gap: "6px",
505
- padding: "5px 10px",
506
- borderRadius: "6px",
507
- border: `1px solid ${STYLES.border}`,
508
- backgroundColor: "transparent",
509
- color: STYLES.textMuted,
510
- fontSize: "11px",
511
- fontWeight: 500,
512
- cursor: "pointer",
513
- transition: "all 0.15s"
514
- },
515
- onMouseEnter: (e) => {
516
- e.currentTarget.style.backgroundColor = STYLES.bg;
517
- e.currentTarget.style.color = STYLES.text;
518
- },
519
- onMouseLeave: (e) => {
520
- e.currentTarget.style.backgroundColor = "transparent";
521
- e.currentTarget.style.color = STYLES.textMuted;
522
- },
523
- children: [
524
- showFullContext ? /* @__PURE__ */ jsx(CollapseIcon, {}) : /* @__PURE__ */ jsx(ExpandIcon, {}),
525
- showFullContext ? "Hide context" : "Show full context"
526
- ]
527
- }
528
- ),
529
- /* @__PURE__ */ jsxs(
530
- "button",
531
- {
532
- onClick: handleOpenInCursor,
533
- style: {
534
- display: "flex",
535
- alignItems: "center",
536
- gap: "4px",
537
- padding: "5px 10px",
538
- borderRadius: "6px",
539
- border: "none",
540
- backgroundColor: STYLES.accent,
541
- color: "#FFFFFF",
542
- fontSize: "11px",
543
- fontWeight: 500,
544
- cursor: "pointer",
545
- transition: "background-color 0.15s"
546
- },
547
- onMouseEnter: (e) => {
548
- e.currentTarget.style.backgroundColor = STYLES.accentHover;
549
- },
550
- onMouseLeave: (e) => {
551
- e.currentTarget.style.backgroundColor = STYLES.accent;
552
- },
553
- title: "Open in Cursor",
554
- children: [
555
- /* @__PURE__ */ jsx(ExternalLinkIcon, {}),
556
- "Open in Cursor"
557
- ]
558
- }
559
- )
560
- ]
561
- }
562
- )
563
- ]
564
- }
565
- );
566
- return createPortal(content, document.body);
567
- }
568
- function PopoverHeader({
569
- element,
570
- issueCount,
571
- lineRange,
572
- onClose
573
- }) {
574
- const fileName = element.source.fileName.split("/").pop() || "Unknown";
575
- return /* @__PURE__ */ jsxs(
576
- "div",
577
- {
578
- style: {
579
- display: "flex",
580
- alignItems: "center",
581
- justifyContent: "space-between",
582
- padding: "12px 14px",
583
- borderBottom: `1px solid ${STYLES.border}`,
584
- backgroundColor: STYLES.bgSurface
585
- },
586
- children: [
587
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "10px" }, children: [
588
- /* @__PURE__ */ jsxs(
589
- "div",
590
- {
591
- style: {
592
- display: "flex",
593
- alignItems: "center",
594
- gap: "6px",
595
- fontSize: "13px",
596
- fontWeight: 600
597
- },
598
- children: [
599
- /* @__PURE__ */ jsx("span", { style: { fontFamily: STYLES.fontMono }, children: fileName }),
600
- /* @__PURE__ */ jsxs("span", { style: { color: STYLES.textDim, fontWeight: 400 }, children: [
601
- ":",
602
- lineRange
603
- ] })
604
- ]
605
- }
606
- ),
607
- issueCount > 0 && /* @__PURE__ */ jsx(Badge, { count: issueCount, backgroundColor: STYLES.error })
608
- ] }),
609
- /* @__PURE__ */ jsx(
610
- "button",
611
- {
612
- onClick: onClose,
613
- style: {
614
- display: "flex",
615
- alignItems: "center",
616
- justifyContent: "center",
617
- width: "26px",
618
- height: "26px",
619
- borderRadius: "6px",
620
- border: "none",
621
- backgroundColor: "transparent",
622
- color: STYLES.textMuted,
623
- cursor: "pointer",
624
- transition: "all 0.15s"
625
- },
626
- onMouseEnter: (e) => {
627
- e.currentTarget.style.backgroundColor = STYLES.bg;
628
- e.currentTarget.style.color = STYLES.text;
629
- },
630
- onMouseLeave: (e) => {
631
- e.currentTarget.style.backgroundColor = "transparent";
632
- e.currentTarget.style.color = STYLES.textMuted;
633
- },
634
- children: /* @__PURE__ */ jsx(CloseIcon, {})
635
- }
636
- )
637
- ]
638
- }
639
- );
640
- }
641
- function IssuesList({
642
- issues,
643
- source,
644
- showFullContext
645
- }) {
646
- const issuesByLine = useMemo(() => {
647
- const map = /* @__PURE__ */ new Map();
648
- issues.forEach((issue) => {
649
- const existing = map.get(issue.line) || [];
650
- existing.push(issue);
651
- map.set(issue.line, existing);
652
- });
653
- return map;
654
- }, [issues]);
655
- const sortedLines = useMemo(
656
- () => Array.from(issuesByLine.keys()).sort((a, b) => a - b),
657
- [issuesByLine]
658
- );
659
- return /* @__PURE__ */ jsx("div", { style: { padding: "12px" }, children: sortedLines.map((lineNumber, index) => /* @__PURE__ */ jsx(
660
- CodeBlockWithAnnotations,
661
- {
662
- lineNumber,
663
- issues: issuesByLine.get(lineNumber),
664
- source,
665
- showFullContext,
666
- isLast: index === sortedLines.length - 1
667
- },
668
- lineNumber
669
- )) });
670
- }
671
- function CodeBlockWithAnnotations({
672
- lineNumber,
673
- issues,
674
- source,
675
- showFullContext,
676
- isLast
677
- }) {
678
- const [codeData, setCodeData] = useState(null);
679
- const [loading, setLoading] = useState(true);
680
- const contextLines = showFullContext ? 5 : 0;
681
- useEffect(() => {
682
- const issueSource = {
683
- fileName: source.fileName,
684
- lineNumber,
685
- columnNumber: issues[0]?.column || 0
686
- };
687
- setLoading(true);
688
- fetchSourceWithWindow(issueSource, {
689
- linesAbove: contextLines,
690
- linesBelow: contextLines
691
- }).then((data) => {
692
- if (data) {
693
- setCodeData({
694
- lines: data.lines,
695
- startLine: data.startLine,
696
- highlightLine: data.highlightLine
697
- });
698
- }
699
- }).finally(() => {
700
- setLoading(false);
701
- });
702
- }, [source.fileName, lineNumber, issues, contextLines]);
703
- return /* @__PURE__ */ jsx(
704
- "div",
705
- {
706
- style: {
707
- marginBottom: isLast ? 0 : "12px",
708
- backgroundColor: STYLES.bg,
709
- borderRadius: "8px",
710
- overflow: "hidden",
711
- border: `1px solid ${STYLES.border}`
712
- },
713
- children: loading ? /* @__PURE__ */ jsx(
714
- "div",
715
- {
716
- style: {
717
- padding: "12px",
718
- textAlign: "center",
719
- color: STYLES.textDim,
720
- fontSize: "11px"
721
- },
722
- children: "Loading..."
723
- }
724
- ) : codeData ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
725
- "pre",
726
- {
727
- style: {
728
- margin: 0,
729
- padding: "8px 0",
730
- overflow: "auto",
731
- fontSize: "11px",
732
- lineHeight: "1.5",
733
- fontFamily: STYLES.fontMono
734
- },
735
- children: dedentLines(codeData.lines).lines.map((line, index) => {
736
- const currentLineNumber = codeData.startLine + index;
737
- const isHighlight = currentLineNumber === codeData.highlightLine;
738
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
739
- /* @__PURE__ */ jsxs(
740
- "div",
741
- {
742
- style: {
743
- display: "flex",
744
- backgroundColor: isHighlight ? "rgba(239, 68, 68, 0.1)" : "transparent",
745
- borderLeft: isHighlight ? `2px solid ${STYLES.error}` : "2px solid transparent"
746
- },
747
- children: [
748
- /* @__PURE__ */ jsx(
749
- "span",
750
- {
751
- style: {
752
- display: "inline-block",
753
- width: "36px",
754
- paddingRight: "8px",
755
- paddingLeft: "8px",
756
- textAlign: "right",
757
- color: isHighlight ? STYLES.error : STYLES.textDim,
758
- userSelect: "none",
759
- flexShrink: 0
760
- },
761
- children: currentLineNumber
762
- }
763
- ),
764
- /* @__PURE__ */ jsx(
765
- "code",
766
- {
767
- style: {
768
- color: isHighlight ? STYLES.text : STYLES.textMuted,
769
- whiteSpace: "pre",
770
- paddingRight: "8px"
771
- },
772
- children: line || " "
773
- }
774
- )
775
- ]
776
- }
777
- ),
778
- isHighlight && issues.map((issue, issueIndex) => /* @__PURE__ */ jsx(InlineAnnotation, { issue }, issueIndex))
779
- ] }, currentLineNumber);
780
- })
781
- }
782
- ) }) : /* @__PURE__ */ jsx(
783
- "div",
784
- {
785
- style: {
786
- padding: "12px",
787
- textAlign: "center",
788
- color: STYLES.textDim,
789
- fontSize: "11px"
790
- },
791
- children: "Could not load source"
792
- }
793
- )
794
- }
795
- );
796
- }
797
- function InlineAnnotation({ issue }) {
798
- const ruleUrl = issue.ruleId ? `https://github.com/peter-suggate/uilint/blob/main/packages/uilint-eslint/src/rules/${issue.ruleId.split("/").pop()}.ts` : null;
799
- return /* @__PURE__ */ jsxs(
800
- "div",
801
- {
802
- style: {
803
- display: "flex",
804
- alignItems: "flex-start",
805
- gap: "8px",
806
- padding: "6px 8px 6px 46px",
807
- backgroundColor: "rgba(239, 68, 68, 0.08)",
808
- borderLeft: `2px solid ${STYLES.error}`
809
- },
810
- children: [
811
- /* @__PURE__ */ jsx(ErrorSquiggleIcon, {}),
812
- /* @__PURE__ */ jsxs("div", { style: { flex: 1, fontSize: "11px", lineHeight: "1.4" }, children: [
813
- /* @__PURE__ */ jsx("span", { style: { color: STYLES.text }, children: issue.message }),
814
- issue.ruleId && /* @__PURE__ */ jsxs(Fragment, { children: [
815
- " ",
816
- /* @__PURE__ */ jsxs(
817
- "a",
818
- {
819
- href: ruleUrl || "#",
820
- target: "_blank",
821
- rel: "noopener noreferrer",
822
- style: {
823
- color: STYLES.textDim,
824
- textDecoration: "none",
825
- fontFamily: STYLES.fontMono,
826
- fontSize: "10px",
827
- transition: "color 0.15s"
828
- },
829
- onMouseEnter: (e) => {
830
- e.currentTarget.style.color = STYLES.accent;
831
- },
832
- onMouseLeave: (e) => {
833
- e.currentTarget.style.color = STYLES.textDim;
834
- },
835
- children: [
836
- "(",
837
- issue.ruleId,
838
- ")"
839
- ]
840
- }
841
- )
842
- ] })
843
- ] })
844
- ]
845
- }
846
- );
847
- }
848
- function CheckIcon() {
849
- return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
850
- "path",
851
- {
852
- d: "M20 6L9 17l-5-5",
853
- stroke: "currentColor",
854
- strokeWidth: "2",
855
- strokeLinecap: "round",
856
- strokeLinejoin: "round"
857
- }
858
- ) });
859
- }
860
- function ExternalLinkIcon() {
861
- return /* @__PURE__ */ jsx("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
862
- "path",
863
- {
864
- d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3",
865
- stroke: "currentColor",
866
- strokeWidth: "2",
867
- strokeLinecap: "round",
868
- strokeLinejoin: "round"
869
- }
870
- ) });
871
- }
872
- function CloseIcon() {
873
- return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
874
- "path",
875
- {
876
- d: "M18 6L6 18M6 6l12 12",
877
- stroke: "currentColor",
878
- strokeWidth: "2",
879
- strokeLinecap: "round",
880
- strokeLinejoin: "round"
881
- }
882
- ) });
883
- }
884
- function ErrorSquiggleIcon() {
885
- return /* @__PURE__ */ jsx(
886
- "svg",
887
- {
888
- width: "14",
889
- height: "14",
890
- viewBox: "0 0 24 24",
891
- fill: "none",
892
- style: { flexShrink: 0, marginTop: "1px" },
893
- children: /* @__PURE__ */ jsx(
894
- "path",
895
- {
896
- d: "M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z",
897
- stroke: STYLES.error,
898
- strokeWidth: "2",
899
- strokeLinecap: "round",
900
- strokeLinejoin: "round"
901
- }
902
- )
903
- }
904
- );
905
- }
906
- function ExpandIcon() {
907
- return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
908
- "path",
909
- {
910
- d: "M8 3H5a2 2 0 00-2 2v3m18 0V5a2 2 0 00-2-2h-3m0 18h3a2 2 0 002-2v-3M3 16v3a2 2 0 002 2h3",
911
- stroke: "currentColor",
912
- strokeWidth: "2",
913
- strokeLinecap: "round",
914
- strokeLinejoin: "round"
915
- }
916
- ) });
917
- }
918
- function CollapseIcon() {
919
- return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
920
- "path",
921
- {
922
- d: "M4 14h6m0 0v6m0-6l-7 7m17-11h-6m0 0V4m0 6l7-7",
923
- stroke: "currentColor",
924
- strokeWidth: "2",
925
- strokeLinecap: "round",
926
- strokeLinejoin: "round"
927
- }
928
- ) });
929
- }
930
-
931
- export {
932
- InspectionPanel
933
- };