uilint-react 0.1.44 → 0.1.46
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.
- package/dist/{ElementBadges-OQC6YCQD.js → ElementBadges-E35MQ2SV.js} +73 -32
- package/dist/InspectionPanel-EOW6OJFT.js +10 -0
- package/dist/{LocatorOverlay-RMU6JIFI.js → LocatorOverlay-BF6EED4N.js} +2 -2
- package/dist/UILintToolbar-XJN6LFWZ.js +10 -0
- package/dist/{chunk-X4PRYONL.js → chunk-JURUYCUC.js} +19 -104
- package/dist/{chunk-GCZ53K35.js → chunk-LQ3WQYIF.js} +1 -1
- package/dist/{chunk-HECINZHM.js → chunk-OU5EEQT6.js} +4 -4
- package/dist/chunk-S4IWHBOQ.js +178 -0
- package/dist/{chunk-RULQAXE4.js → chunk-XUMLILUN.js} +168 -25
- package/dist/index.js +7 -6
- package/package.json +2 -2
- package/dist/InspectionPanel-UW5UBZIK.js +0 -10
- package/dist/UILintToolbar-G5AVKNXA.js +0 -10
- package/dist/chunk-W42PI2OF.js +0 -81
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import {
|
|
4
4
|
useUILintContext,
|
|
5
5
|
useUILintStore
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OU5EEQT6.js";
|
|
7
7
|
|
|
8
8
|
// src/components/ui-lint/ElementBadges.tsx
|
|
9
9
|
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
|
@@ -129,6 +129,24 @@ function findNearbyBadges(positions, x, y, threshold) {
|
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// src/components/ui-lint/visibility-utils.ts
|
|
133
|
+
function isElementCoveredByOverlay(element, badgeX, badgeY) {
|
|
134
|
+
const elementsAtPoint = document.elementsFromPoint(badgeX, badgeY);
|
|
135
|
+
for (const el of elementsAtPoint) {
|
|
136
|
+
if (el.hasAttribute("data-ui-lint")) continue;
|
|
137
|
+
if (el === element || element.contains(el)) continue;
|
|
138
|
+
if (el.contains(element)) continue;
|
|
139
|
+
const style = window.getComputedStyle(el);
|
|
140
|
+
const position = style.position;
|
|
141
|
+
const zIndex = parseInt(style.zIndex, 10);
|
|
142
|
+
const isOverlay = (position === "fixed" || position === "absolute") && (zIndex > 0 || style.zIndex === "auto" || style.zIndex === "inherit");
|
|
143
|
+
if (isOverlay) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
132
150
|
// src/components/ui-lint/ElementBadges.tsx
|
|
133
151
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
134
152
|
var STYLES = {
|
|
@@ -256,6 +274,9 @@ function ElementBadges() {
|
|
|
256
274
|
const y = rect.top - 8;
|
|
257
275
|
if (rect.top < -50 || rect.top > window.innerHeight + 50) continue;
|
|
258
276
|
if (rect.left < -50 || rect.left > window.innerWidth + 50) continue;
|
|
277
|
+
if (isElementCoveredByOverlay(element.element, x, y)) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
259
280
|
positions.push({ element, issue, x, y, rect });
|
|
260
281
|
}
|
|
261
282
|
setBadgePositions(positions);
|
|
@@ -316,37 +337,53 @@ function ElementBadges() {
|
|
|
316
337
|
}
|
|
317
338
|
return filtered;
|
|
318
339
|
}, [nudgedPositions, isAltKeyPressed, selectedFilePath, hoveredFilePath]);
|
|
340
|
+
const handleUILintInteraction = useCallback(
|
|
341
|
+
(e) => {
|
|
342
|
+
e.stopPropagation();
|
|
343
|
+
},
|
|
344
|
+
[]
|
|
345
|
+
);
|
|
319
346
|
if (!mounted) return null;
|
|
320
347
|
if (autoScanState.status === "idle") return null;
|
|
321
|
-
const content = /* @__PURE__ */ jsxs(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
nudgedPos
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
348
|
+
const content = /* @__PURE__ */ jsxs(
|
|
349
|
+
"div",
|
|
350
|
+
{
|
|
351
|
+
"data-ui-lint": true,
|
|
352
|
+
onMouseDown: handleUILintInteraction,
|
|
353
|
+
onClick: handleUILintInteraction,
|
|
354
|
+
onKeyDown: handleUILintInteraction,
|
|
355
|
+
style: { pointerEvents: "none" },
|
|
356
|
+
children: [
|
|
357
|
+
/* @__PURE__ */ jsx(BadgeAnimationStyles, {}),
|
|
358
|
+
visibleBadges.filter((pos, idx, arr) => {
|
|
359
|
+
const id = pos.element.id;
|
|
360
|
+
return arr.findIndex((p) => p.element.id === id) === idx;
|
|
361
|
+
}).map((nudgedPos) => {
|
|
362
|
+
const distance = Math.hypot(
|
|
363
|
+
nudgedPos.nudgedX - cursorPos.x,
|
|
364
|
+
nudgedPos.nudgedY - cursorPos.y
|
|
365
|
+
);
|
|
366
|
+
const nearbyBadges = findNearbyBadges(
|
|
367
|
+
visibleBadges,
|
|
368
|
+
nudgedPos.nudgedX,
|
|
369
|
+
nudgedPos.nudgedY,
|
|
370
|
+
NEARBY_THRESHOLD
|
|
371
|
+
);
|
|
372
|
+
return /* @__PURE__ */ jsx(
|
|
373
|
+
NudgedBadge,
|
|
374
|
+
{
|
|
375
|
+
position: nudgedPos,
|
|
376
|
+
distance,
|
|
377
|
+
nearbyBadges,
|
|
378
|
+
cursorPos,
|
|
379
|
+
onSelect: handleSelect
|
|
380
|
+
},
|
|
381
|
+
nudgedPos.element.id
|
|
382
|
+
);
|
|
383
|
+
})
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
);
|
|
350
387
|
return createPortal(content, document.body);
|
|
351
388
|
}
|
|
352
389
|
function NudgedBadge({
|
|
@@ -415,7 +452,9 @@ function NudgedBadge({
|
|
|
415
452
|
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.4)",
|
|
416
453
|
padding: "4px 0",
|
|
417
454
|
minWidth: "200px",
|
|
418
|
-
fontFamily: STYLES.font
|
|
455
|
+
fontFamily: STYLES.font,
|
|
456
|
+
pointerEvents: "auto"
|
|
457
|
+
// Re-enable pointer events for interactive dropdown
|
|
419
458
|
};
|
|
420
459
|
}, [snappedPosition]);
|
|
421
460
|
const scale = isExpanded ? 1.1 : getScaleFromDistance(distance);
|
|
@@ -468,7 +507,9 @@ function NudgedBadge({
|
|
|
468
507
|
cursor: "pointer",
|
|
469
508
|
transition: "transform 0.1s ease-out",
|
|
470
509
|
transform: `scale(${scale})`,
|
|
471
|
-
transformOrigin: "center center"
|
|
510
|
+
transformOrigin: "center center",
|
|
511
|
+
pointerEvents: "auto"
|
|
512
|
+
// Re-enable pointer events for interactive badge
|
|
472
513
|
},
|
|
473
514
|
"data-ui-lint": true,
|
|
474
515
|
onMouseEnter: handleMouseEnter,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
|
-
Badge
|
|
4
|
-
|
|
3
|
+
Badge,
|
|
4
|
+
fetchSourceWithWindow
|
|
5
|
+
} from "./chunk-S4IWHBOQ.js";
|
|
5
6
|
import {
|
|
6
7
|
buildEditorUrl,
|
|
7
8
|
useUILintContext,
|
|
8
9
|
useUILintStore
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OU5EEQT6.js";
|
|
10
11
|
|
|
11
12
|
// src/components/ui-lint/InspectionPanel.tsx
|
|
12
13
|
import React, {
|
|
@@ -18,97 +19,6 @@ import React, {
|
|
|
18
19
|
} from "react";
|
|
19
20
|
import { createPortal } from "react-dom";
|
|
20
21
|
|
|
21
|
-
// src/components/ui-lint/source-fetcher.ts
|
|
22
|
-
var sourceCache = /* @__PURE__ */ new Map();
|
|
23
|
-
var CACHE_TTL = 5 * 60 * 1e3;
|
|
24
|
-
var API_ENDPOINT = "/api/.uilint/source";
|
|
25
|
-
async function fetchSource(filePath) {
|
|
26
|
-
const cached = sourceCache.get(filePath);
|
|
27
|
-
if (cached && Date.now() - cached.fetchedAt < CACHE_TTL) {
|
|
28
|
-
return {
|
|
29
|
-
content: cached.content,
|
|
30
|
-
relativePath: cached.relativePath
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
const response = await fetch(
|
|
35
|
-
`${API_ENDPOINT}?path=${encodeURIComponent(filePath)}`
|
|
36
|
-
);
|
|
37
|
-
if (!response.ok) {
|
|
38
|
-
console.warn(`[UILint] Failed to fetch source: ${response.statusText}`);
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
const data = await response.json();
|
|
42
|
-
sourceCache.set(filePath, {
|
|
43
|
-
...data,
|
|
44
|
-
fetchedAt: Date.now()
|
|
45
|
-
});
|
|
46
|
-
return data;
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.error("[UILint] Error fetching source:", error);
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async function fetchSourceWithContext(source, contextLines = 5) {
|
|
53
|
-
const result = await fetchSource(source.fileName);
|
|
54
|
-
if (!result) return null;
|
|
55
|
-
const allLines = result.content.split("\n");
|
|
56
|
-
const targetLine = source.lineNumber - 1;
|
|
57
|
-
const startLine = Math.max(0, targetLine - contextLines);
|
|
58
|
-
const endLine = Math.min(allLines.length, targetLine + contextLines + 1);
|
|
59
|
-
return {
|
|
60
|
-
lines: allLines.slice(startLine, endLine),
|
|
61
|
-
startLine: startLine + 1,
|
|
62
|
-
// Back to 1-indexed
|
|
63
|
-
highlightLine: source.lineNumber,
|
|
64
|
-
relativePath: result.relativePath
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
async function fetchSourceWithWindow(source, window2) {
|
|
68
|
-
const result = await fetchSource(source.fileName);
|
|
69
|
-
if (!result) return null;
|
|
70
|
-
const allLines = result.content.split("\n");
|
|
71
|
-
const targetLine = source.lineNumber - 1;
|
|
72
|
-
const startLine = Math.max(0, targetLine - Math.max(0, window2.linesAbove));
|
|
73
|
-
const endLine = Math.min(
|
|
74
|
-
allLines.length,
|
|
75
|
-
targetLine + Math.max(0, window2.linesBelow) + 1
|
|
76
|
-
);
|
|
77
|
-
return {
|
|
78
|
-
lines: allLines.slice(startLine, endLine),
|
|
79
|
-
startLine: startLine + 1,
|
|
80
|
-
// Back to 1-indexed
|
|
81
|
-
highlightLine: source.lineNumber,
|
|
82
|
-
relativePath: result.relativePath
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function clearSourceCache() {
|
|
86
|
-
sourceCache.clear();
|
|
87
|
-
}
|
|
88
|
-
function getCachedSource(filePath) {
|
|
89
|
-
const cached = sourceCache.get(filePath);
|
|
90
|
-
if (!cached) return null;
|
|
91
|
-
if (Date.now() - cached.fetchedAt >= CACHE_TTL) {
|
|
92
|
-
sourceCache.delete(filePath);
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
content: cached.content,
|
|
97
|
-
relativePath: cached.relativePath
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
async function prefetchSources(filePaths) {
|
|
101
|
-
const uniquePaths = [...new Set(filePaths)].filter((path) => {
|
|
102
|
-
const cached = sourceCache.get(path);
|
|
103
|
-
return !cached || Date.now() - cached.fetchedAt >= CACHE_TTL;
|
|
104
|
-
});
|
|
105
|
-
const BATCH_SIZE = 5;
|
|
106
|
-
for (let i = 0; i < uniquePaths.length; i += BATCH_SIZE) {
|
|
107
|
-
const batch = uniquePaths.slice(i, i + BATCH_SIZE);
|
|
108
|
-
await Promise.all(batch.map(fetchSource));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
22
|
// src/components/ui-lint/code-formatting.ts
|
|
113
23
|
function leadingWhitespace(s) {
|
|
114
24
|
const match = s.match(/^[\t ]+/);
|
|
@@ -246,7 +156,7 @@ var STYLES = {
|
|
|
246
156
|
font: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
247
157
|
fontMono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace'
|
|
248
158
|
};
|
|
249
|
-
var POPOVER_WIDTH =
|
|
159
|
+
var POPOVER_WIDTH = 450;
|
|
250
160
|
var POPOVER_MAX_HEIGHT = 450;
|
|
251
161
|
function InspectionPanel() {
|
|
252
162
|
const {
|
|
@@ -278,11 +188,11 @@ function InspectionPanel() {
|
|
|
278
188
|
setInspectedElement(null);
|
|
279
189
|
};
|
|
280
190
|
const timer = setTimeout(() => {
|
|
281
|
-
document.addEventListener("click", handleClickOutside);
|
|
191
|
+
document.addEventListener("click", handleClickOutside, true);
|
|
282
192
|
}, 50);
|
|
283
193
|
return () => {
|
|
284
194
|
clearTimeout(timer);
|
|
285
|
-
document.removeEventListener("click", handleClickOutside);
|
|
195
|
+
document.removeEventListener("click", handleClickOutside, true);
|
|
286
196
|
};
|
|
287
197
|
}, [inspectedElement, setInspectedElement]);
|
|
288
198
|
const isFileLevelIssue = useMemo(() => {
|
|
@@ -439,13 +349,21 @@ function InspectionPanel() {
|
|
|
439
349
|
);
|
|
440
350
|
window.open(url, "_blank");
|
|
441
351
|
}, [inspectedElement, editorBaseDir]);
|
|
352
|
+
const handleUILintInteraction = useCallback(
|
|
353
|
+
(e) => {
|
|
354
|
+
e.stopPropagation();
|
|
355
|
+
},
|
|
356
|
+
[]
|
|
357
|
+
);
|
|
442
358
|
if (!mounted || !inspectedElement) return null;
|
|
443
359
|
const content = /* @__PURE__ */ jsxs(
|
|
444
360
|
"div",
|
|
445
361
|
{
|
|
446
362
|
ref: popoverRef,
|
|
447
363
|
"data-ui-lint": true,
|
|
448
|
-
|
|
364
|
+
onMouseDown: handleUILintInteraction,
|
|
365
|
+
onClick: handleUILintInteraction,
|
|
366
|
+
onKeyDown: handleUILintInteraction,
|
|
449
367
|
style: {
|
|
450
368
|
position: "fixed",
|
|
451
369
|
top: position.top,
|
|
@@ -462,7 +380,9 @@ function InspectionPanel() {
|
|
|
462
380
|
color: STYLES.text,
|
|
463
381
|
overflow: "hidden",
|
|
464
382
|
zIndex: 99998,
|
|
465
|
-
animation: "uilint-popover-appear 0.15s ease-out"
|
|
383
|
+
animation: "uilint-popover-appear 0.15s ease-out",
|
|
384
|
+
pointerEvents: "auto"
|
|
385
|
+
// Ensure panel is interactive
|
|
466
386
|
},
|
|
467
387
|
children: [
|
|
468
388
|
/* @__PURE__ */ jsx("style", { children: `
|
|
@@ -1008,10 +928,5 @@ function CollapseIcon() {
|
|
|
1008
928
|
}
|
|
1009
929
|
|
|
1010
930
|
export {
|
|
1011
|
-
fetchSource,
|
|
1012
|
-
fetchSourceWithContext,
|
|
1013
|
-
clearSourceCache,
|
|
1014
|
-
getCachedSource,
|
|
1015
|
-
prefetchSources,
|
|
1016
931
|
InspectionPanel
|
|
1017
932
|
};
|
|
@@ -1063,10 +1063,10 @@ function UILintUI() {
|
|
|
1063
1063
|
const [components, setComponents] = useState(null);
|
|
1064
1064
|
useEffect2(() => {
|
|
1065
1065
|
Promise.all([
|
|
1066
|
-
import("./UILintToolbar-
|
|
1067
|
-
import("./InspectionPanel-
|
|
1068
|
-
import("./LocatorOverlay-
|
|
1069
|
-
import("./ElementBadges-
|
|
1066
|
+
import("./UILintToolbar-XJN6LFWZ.js"),
|
|
1067
|
+
import("./InspectionPanel-EOW6OJFT.js"),
|
|
1068
|
+
import("./LocatorOverlay-BF6EED4N.js"),
|
|
1069
|
+
import("./ElementBadges-E35MQ2SV.js")
|
|
1070
1070
|
]).then(([toolbar, panel, locator, badges]) => {
|
|
1071
1071
|
setComponents({
|
|
1072
1072
|
Toolbar: toolbar.UILintToolbar,
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/ui-lint/Badge.tsx
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
var BADGE_COLORS = {
|
|
6
|
+
success: "#68d391",
|
|
7
|
+
// Soft green
|
|
8
|
+
warning: "#f6ad55",
|
|
9
|
+
// Warm orange
|
|
10
|
+
error: "#ef4444"
|
|
11
|
+
// Red (for future use)
|
|
12
|
+
};
|
|
13
|
+
var FONT_MONO = `"SF Mono", Monaco, "Cascadia Code", monospace`;
|
|
14
|
+
function getBadgeTextColor(issueCount) {
|
|
15
|
+
if (issueCount === 0) return BADGE_COLORS.success;
|
|
16
|
+
return BADGE_COLORS.warning;
|
|
17
|
+
}
|
|
18
|
+
function getBadgeBackgroundColor(issueCount) {
|
|
19
|
+
const color = getBadgeTextColor(issueCount);
|
|
20
|
+
return `${color}20`;
|
|
21
|
+
}
|
|
22
|
+
var BADGE_STYLES = {
|
|
23
|
+
default: {
|
|
24
|
+
minWidth: "20px",
|
|
25
|
+
height: "20px",
|
|
26
|
+
padding: "0 6px",
|
|
27
|
+
borderRadius: "10px",
|
|
28
|
+
fontSize: "11px",
|
|
29
|
+
fontWeight: 600,
|
|
30
|
+
letterSpacing: "-0.02em"
|
|
31
|
+
},
|
|
32
|
+
small: {
|
|
33
|
+
minWidth: "18px",
|
|
34
|
+
height: "18px",
|
|
35
|
+
padding: "0 5px",
|
|
36
|
+
borderRadius: "9px",
|
|
37
|
+
fontSize: "10px",
|
|
38
|
+
fontWeight: 700,
|
|
39
|
+
letterSpacing: "0"
|
|
40
|
+
},
|
|
41
|
+
// For file row badges (slightly larger than small)
|
|
42
|
+
medium: {
|
|
43
|
+
minWidth: "22px",
|
|
44
|
+
height: "18px",
|
|
45
|
+
padding: "0 6px",
|
|
46
|
+
borderRadius: "9px",
|
|
47
|
+
fontSize: "10px",
|
|
48
|
+
fontWeight: 700,
|
|
49
|
+
letterSpacing: "0"
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
function Badge({
|
|
53
|
+
count,
|
|
54
|
+
size = "default",
|
|
55
|
+
backgroundColor,
|
|
56
|
+
color
|
|
57
|
+
}) {
|
|
58
|
+
const sizeStyles = BADGE_STYLES[size];
|
|
59
|
+
const bgColor = backgroundColor ?? getBadgeBackgroundColor(count);
|
|
60
|
+
const textColor = color ?? getBadgeTextColor(count);
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
"span",
|
|
63
|
+
{
|
|
64
|
+
style: {
|
|
65
|
+
display: "inline-flex",
|
|
66
|
+
alignItems: "center",
|
|
67
|
+
justifyContent: "center",
|
|
68
|
+
...sizeStyles,
|
|
69
|
+
backgroundColor: bgColor,
|
|
70
|
+
color: textColor,
|
|
71
|
+
fontFamily: FONT_MONO
|
|
72
|
+
},
|
|
73
|
+
children: count
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/components/ui-lint/source-fetcher.ts
|
|
79
|
+
var sourceCache = /* @__PURE__ */ new Map();
|
|
80
|
+
var CACHE_TTL = 5 * 60 * 1e3;
|
|
81
|
+
var API_ENDPOINT = "/api/.uilint/source";
|
|
82
|
+
async function fetchSource(filePath) {
|
|
83
|
+
const cached = sourceCache.get(filePath);
|
|
84
|
+
if (cached && Date.now() - cached.fetchedAt < CACHE_TTL) {
|
|
85
|
+
return {
|
|
86
|
+
content: cached.content,
|
|
87
|
+
relativePath: cached.relativePath
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const response = await fetch(
|
|
92
|
+
`${API_ENDPOINT}?path=${encodeURIComponent(filePath)}`
|
|
93
|
+
);
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
console.warn(`[UILint] Failed to fetch source: ${response.statusText}`);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
sourceCache.set(filePath, {
|
|
100
|
+
...data,
|
|
101
|
+
fetchedAt: Date.now()
|
|
102
|
+
});
|
|
103
|
+
return data;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error("[UILint] Error fetching source:", error);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function fetchSourceWithContext(source, contextLines = 5) {
|
|
110
|
+
const result = await fetchSource(source.fileName);
|
|
111
|
+
if (!result) return null;
|
|
112
|
+
const allLines = result.content.split("\n");
|
|
113
|
+
const targetLine = source.lineNumber - 1;
|
|
114
|
+
const startLine = Math.max(0, targetLine - contextLines);
|
|
115
|
+
const endLine = Math.min(allLines.length, targetLine + contextLines + 1);
|
|
116
|
+
return {
|
|
117
|
+
lines: allLines.slice(startLine, endLine),
|
|
118
|
+
startLine: startLine + 1,
|
|
119
|
+
// Back to 1-indexed
|
|
120
|
+
highlightLine: source.lineNumber,
|
|
121
|
+
relativePath: result.relativePath
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function fetchSourceWithWindow(source, window) {
|
|
125
|
+
const result = await fetchSource(source.fileName);
|
|
126
|
+
if (!result) return null;
|
|
127
|
+
const allLines = result.content.split("\n");
|
|
128
|
+
const targetLine = source.lineNumber - 1;
|
|
129
|
+
const startLine = Math.max(0, targetLine - Math.max(0, window.linesAbove));
|
|
130
|
+
const endLine = Math.min(
|
|
131
|
+
allLines.length,
|
|
132
|
+
targetLine + Math.max(0, window.linesBelow) + 1
|
|
133
|
+
);
|
|
134
|
+
return {
|
|
135
|
+
lines: allLines.slice(startLine, endLine),
|
|
136
|
+
startLine: startLine + 1,
|
|
137
|
+
// Back to 1-indexed
|
|
138
|
+
highlightLine: source.lineNumber,
|
|
139
|
+
relativePath: result.relativePath
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function clearSourceCache() {
|
|
143
|
+
sourceCache.clear();
|
|
144
|
+
}
|
|
145
|
+
function getCachedSource(filePath) {
|
|
146
|
+
const cached = sourceCache.get(filePath);
|
|
147
|
+
if (!cached) return null;
|
|
148
|
+
if (Date.now() - cached.fetchedAt >= CACHE_TTL) {
|
|
149
|
+
sourceCache.delete(filePath);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
content: cached.content,
|
|
154
|
+
relativePath: cached.relativePath
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async function prefetchSources(filePaths) {
|
|
158
|
+
const uniquePaths = [...new Set(filePaths)].filter((path) => {
|
|
159
|
+
const cached = sourceCache.get(path);
|
|
160
|
+
return !cached || Date.now() - cached.fetchedAt >= CACHE_TTL;
|
|
161
|
+
});
|
|
162
|
+
const BATCH_SIZE = 5;
|
|
163
|
+
for (let i = 0; i < uniquePaths.length; i += BATCH_SIZE) {
|
|
164
|
+
const batch = uniquePaths.slice(i, i + BATCH_SIZE);
|
|
165
|
+
await Promise.all(batch.map(fetchSource));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
BADGE_COLORS,
|
|
171
|
+
Badge,
|
|
172
|
+
fetchSource,
|
|
173
|
+
fetchSourceWithContext,
|
|
174
|
+
fetchSourceWithWindow,
|
|
175
|
+
clearSourceCache,
|
|
176
|
+
getCachedSource,
|
|
177
|
+
prefetchSources
|
|
178
|
+
};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
BADGE_COLORS,
|
|
4
|
-
Badge
|
|
5
|
-
|
|
4
|
+
Badge,
|
|
5
|
+
fetchSource,
|
|
6
|
+
getCachedSource
|
|
7
|
+
} from "./chunk-S4IWHBOQ.js";
|
|
6
8
|
import {
|
|
7
9
|
groupBySourceFile,
|
|
8
10
|
useUILintContext,
|
|
9
11
|
useUILintStore
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OU5EEQT6.js";
|
|
11
13
|
|
|
12
14
|
// src/components/ui-lint/UILintToolbar.tsx
|
|
13
|
-
import { useState as useState2, useRef as useRef2, useEffect as
|
|
15
|
+
import { useState as useState2, useRef as useRef2, useEffect as useEffect3, useCallback as useCallback2 } from "react";
|
|
14
16
|
import { createPortal } from "react-dom";
|
|
15
17
|
|
|
16
18
|
// src/components/ui-lint/toolbar-styles.ts
|
|
@@ -228,10 +230,10 @@ function SettingsPopover({ settings }) {
|
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
// src/components/ui-lint/ScanPanelStack.tsx
|
|
231
|
-
import { useRef, useEffect } from "react";
|
|
233
|
+
import { useRef, useEffect as useEffect2 } from "react";
|
|
232
234
|
|
|
233
235
|
// src/components/ui-lint/ScanResultsPopover.tsx
|
|
234
|
-
import { useState, useCallback, useMemo } from "react";
|
|
236
|
+
import { useState, useCallback, useMemo, useEffect } from "react";
|
|
235
237
|
|
|
236
238
|
// src/components/ui-lint/toolbar-icons.tsx
|
|
237
239
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -860,12 +862,65 @@ function ElementRow({ item, onHover, onClick }) {
|
|
|
860
862
|
}
|
|
861
863
|
);
|
|
862
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
|
+
}
|
|
863
889
|
function FileLevelIssueRow({
|
|
864
890
|
filePath,
|
|
865
891
|
issue,
|
|
866
892
|
onClick
|
|
867
893
|
}) {
|
|
868
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]);
|
|
869
924
|
return /* @__PURE__ */ jsxs3(
|
|
870
925
|
"div",
|
|
871
926
|
{
|
|
@@ -896,7 +951,22 @@ function FileLevelIssueRow({
|
|
|
896
951
|
marginRight: "12px"
|
|
897
952
|
},
|
|
898
953
|
children: [
|
|
899
|
-
/* @__PURE__ */
|
|
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(
|
|
900
970
|
"span",
|
|
901
971
|
{
|
|
902
972
|
style: {
|
|
@@ -992,26 +1062,26 @@ function CloseIcon() {
|
|
|
992
1062
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
993
1063
|
function ScanPanelStack({ show, onClose }) {
|
|
994
1064
|
const containerRef = useRef(null);
|
|
995
|
-
|
|
1065
|
+
useEffect2(() => {
|
|
996
1066
|
if (!show) return;
|
|
997
1067
|
const handleClickOutside = (e) => {
|
|
998
1068
|
const target = e.target;
|
|
1069
|
+
if (target?.closest?.("[data-ui-lint]")) {
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
999
1072
|
if (containerRef.current && !containerRef.current.contains(target)) {
|
|
1000
|
-
|
|
1001
|
-
if (!isUILintElement) {
|
|
1002
|
-
onClose();
|
|
1003
|
-
}
|
|
1073
|
+
onClose();
|
|
1004
1074
|
}
|
|
1005
1075
|
};
|
|
1006
1076
|
const timeoutId = setTimeout(() => {
|
|
1007
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
1077
|
+
document.addEventListener("mousedown", handleClickOutside, true);
|
|
1008
1078
|
}, 100);
|
|
1009
1079
|
return () => {
|
|
1010
1080
|
clearTimeout(timeoutId);
|
|
1011
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
1081
|
+
document.removeEventListener("mousedown", handleClickOutside, true);
|
|
1012
1082
|
};
|
|
1013
1083
|
}, [show, onClose]);
|
|
1014
|
-
|
|
1084
|
+
useEffect2(() => {
|
|
1015
1085
|
if (!show) return;
|
|
1016
1086
|
const handleKeyDown = (e) => {
|
|
1017
1087
|
if (e.key === "Escape") {
|
|
@@ -1021,16 +1091,25 @@ function ScanPanelStack({ show, onClose }) {
|
|
|
1021
1091
|
window.addEventListener("keydown", handleKeyDown);
|
|
1022
1092
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1023
1093
|
}, [show, onClose]);
|
|
1094
|
+
const handleUILintInteraction = (e) => {
|
|
1095
|
+
e.stopPropagation();
|
|
1096
|
+
};
|
|
1024
1097
|
if (!show) return null;
|
|
1025
1098
|
return /* @__PURE__ */ jsx4(
|
|
1026
1099
|
"div",
|
|
1027
1100
|
{
|
|
1028
1101
|
ref: containerRef,
|
|
1102
|
+
"data-ui-lint": true,
|
|
1103
|
+
onMouseDown: handleUILintInteraction,
|
|
1104
|
+
onClick: handleUILintInteraction,
|
|
1105
|
+
onKeyDown: handleUILintInteraction,
|
|
1029
1106
|
style: {
|
|
1030
1107
|
position: "absolute",
|
|
1031
1108
|
bottom: "100%",
|
|
1032
1109
|
left: 0,
|
|
1033
|
-
marginBottom: "8px"
|
|
1110
|
+
marginBottom: "8px",
|
|
1111
|
+
pointerEvents: "auto"
|
|
1112
|
+
// Ensure panel is interactive
|
|
1034
1113
|
},
|
|
1035
1114
|
children: /* @__PURE__ */ jsx4(ScanResultsPopover, { onClose })
|
|
1036
1115
|
}
|
|
@@ -1312,6 +1391,50 @@ var globalStyles = `
|
|
|
1312
1391
|
.uilint-popover--closing {
|
|
1313
1392
|
animation: uilint-fade-out ${TOKENS.transitionBase} forwards;
|
|
1314
1393
|
}
|
|
1394
|
+
|
|
1395
|
+
/* Custom scrollbar styling for dark mode - scoped to uilint components */
|
|
1396
|
+
[data-ui-lint] *,
|
|
1397
|
+
[data-ui-lint] {
|
|
1398
|
+
/* Firefox */
|
|
1399
|
+
scrollbar-width: thin;
|
|
1400
|
+
scrollbar-color: rgba(255, 255, 255, 0.15) rgba(15, 15, 15, 0.3);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
/* WebKit browsers (Chrome, Safari, Edge) */
|
|
1404
|
+
[data-ui-lint] *::-webkit-scrollbar,
|
|
1405
|
+
[data-ui-lint]::-webkit-scrollbar {
|
|
1406
|
+
width: 8px;
|
|
1407
|
+
height: 8px;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
[data-ui-lint] *::-webkit-scrollbar-track,
|
|
1411
|
+
[data-ui-lint]::-webkit-scrollbar-track {
|
|
1412
|
+
background: rgba(15, 15, 15, 0.3);
|
|
1413
|
+
border-radius: 4px;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
[data-ui-lint] *::-webkit-scrollbar-thumb,
|
|
1417
|
+
[data-ui-lint]::-webkit-scrollbar-thumb {
|
|
1418
|
+
background: rgba(255, 255, 255, 0.15);
|
|
1419
|
+
border-radius: 4px;
|
|
1420
|
+
border: 1px solid rgba(15, 15, 15, 0.2);
|
|
1421
|
+
transition: background ${TOKENS.transitionFast};
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
[data-ui-lint] *::-webkit-scrollbar-thumb:hover,
|
|
1425
|
+
[data-ui-lint]::-webkit-scrollbar-thumb:hover {
|
|
1426
|
+
background: rgba(255, 255, 255, 0.25);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
[data-ui-lint] *::-webkit-scrollbar-thumb:active,
|
|
1430
|
+
[data-ui-lint]::-webkit-scrollbar-thumb:active {
|
|
1431
|
+
background: rgba(255, 255, 255, 0.35);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
[data-ui-lint] *::-webkit-scrollbar-corner,
|
|
1435
|
+
[data-ui-lint]::-webkit-scrollbar-corner {
|
|
1436
|
+
background: rgba(15, 15, 15, 0.3);
|
|
1437
|
+
}
|
|
1315
1438
|
`;
|
|
1316
1439
|
function ToolbarButton({
|
|
1317
1440
|
onClick,
|
|
@@ -1489,7 +1612,7 @@ function UILintToolbar() {
|
|
|
1489
1612
|
const [nextjsOverlayVisible, setNextjsOverlayVisible] = useState2(false);
|
|
1490
1613
|
const toolbarRef = useRef2(null);
|
|
1491
1614
|
const settingsRef = useRef2(null);
|
|
1492
|
-
|
|
1615
|
+
useEffect3(() => {
|
|
1493
1616
|
const checkForNextOverlay = () => {
|
|
1494
1617
|
const overlaySelectors = [
|
|
1495
1618
|
"nextjs-portal",
|
|
@@ -1528,12 +1651,15 @@ function UILintToolbar() {
|
|
|
1528
1651
|
});
|
|
1529
1652
|
const totalIssues = elementIssues + fileLevelIssues;
|
|
1530
1653
|
const hasIssues = totalIssues > 0;
|
|
1531
|
-
|
|
1654
|
+
useEffect3(() => {
|
|
1532
1655
|
setMounted(true);
|
|
1533
1656
|
}, []);
|
|
1534
|
-
|
|
1657
|
+
useEffect3(() => {
|
|
1535
1658
|
const handleClickOutside = (e) => {
|
|
1536
1659
|
const target = e.target;
|
|
1660
|
+
if (target?.closest?.("[data-ui-lint]")) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1537
1663
|
if (showSettings && settingsRef.current && toolbarRef.current) {
|
|
1538
1664
|
if (!settingsRef.current.contains(target) && !toolbarRef.current.contains(target)) {
|
|
1539
1665
|
handleCloseSettings();
|
|
@@ -1546,10 +1672,10 @@ function UILintToolbar() {
|
|
|
1546
1672
|
if (showResults) setShowResults(false);
|
|
1547
1673
|
}
|
|
1548
1674
|
};
|
|
1549
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
1675
|
+
document.addEventListener("mousedown", handleClickOutside, true);
|
|
1550
1676
|
document.addEventListener("keydown", handleEscape);
|
|
1551
1677
|
return () => {
|
|
1552
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
1678
|
+
document.removeEventListener("mousedown", handleClickOutside, true);
|
|
1553
1679
|
document.removeEventListener("keydown", handleEscape);
|
|
1554
1680
|
};
|
|
1555
1681
|
}, [showSettings, showResults]);
|
|
@@ -1584,6 +1710,12 @@ function UILintToolbar() {
|
|
|
1584
1710
|
setSettingsClosing(false);
|
|
1585
1711
|
}, 150);
|
|
1586
1712
|
}, []);
|
|
1713
|
+
const handleUILintInteraction = useCallback2(
|
|
1714
|
+
(e) => {
|
|
1715
|
+
e.stopPropagation();
|
|
1716
|
+
},
|
|
1717
|
+
[]
|
|
1718
|
+
);
|
|
1587
1719
|
if (!mounted) return null;
|
|
1588
1720
|
const issueVariant = !liveScanEnabled ? "default" : hasIssues ? "warning" : isComplete ? "success" : "default";
|
|
1589
1721
|
const bottomPosition = nextjsOverlayVisible ? "80px" : "20px";
|
|
@@ -1591,13 +1723,18 @@ function UILintToolbar() {
|
|
|
1591
1723
|
"div",
|
|
1592
1724
|
{
|
|
1593
1725
|
"data-ui-lint": true,
|
|
1726
|
+
onMouseDown: handleUILintInteraction,
|
|
1727
|
+
onClick: handleUILintInteraction,
|
|
1728
|
+
onKeyDown: handleUILintInteraction,
|
|
1594
1729
|
style: {
|
|
1595
1730
|
position: "fixed",
|
|
1596
1731
|
bottom: bottomPosition,
|
|
1597
1732
|
left: "20px",
|
|
1598
1733
|
zIndex: 99999,
|
|
1599
1734
|
fontFamily: TOKENS.fontFamily,
|
|
1600
|
-
transition: `bottom ${TOKENS.transitionSlow}
|
|
1735
|
+
transition: `bottom ${TOKENS.transitionSlow}`,
|
|
1736
|
+
pointerEvents: "none"
|
|
1737
|
+
// Allow clicks to pass through empty space
|
|
1601
1738
|
},
|
|
1602
1739
|
children: [
|
|
1603
1740
|
/* @__PURE__ */ jsx5("style", { children: globalStyles }),
|
|
@@ -1610,7 +1747,9 @@ function UILintToolbar() {
|
|
|
1610
1747
|
marginBottom: "10px",
|
|
1611
1748
|
fontSize: "11px",
|
|
1612
1749
|
color: TOKENS.textMuted,
|
|
1613
|
-
letterSpacing: "0.02em"
|
|
1750
|
+
letterSpacing: "0.02em",
|
|
1751
|
+
pointerEvents: "auto"
|
|
1752
|
+
// Re-enable pointer events for hint
|
|
1614
1753
|
},
|
|
1615
1754
|
"aria-hidden": !liveScanEnabled,
|
|
1616
1755
|
children: /* @__PURE__ */ jsx5(
|
|
@@ -1653,7 +1792,9 @@ function UILintToolbar() {
|
|
|
1653
1792
|
`${TOKENS.warning}30`
|
|
1654
1793
|
)}` : TOKENS.shadowMd,
|
|
1655
1794
|
overflow: "hidden",
|
|
1656
|
-
transition: `box-shadow ${TOKENS.transitionBase}
|
|
1795
|
+
transition: `box-shadow ${TOKENS.transitionBase}`,
|
|
1796
|
+
pointerEvents: "auto"
|
|
1797
|
+
// Re-enable pointer events for interactive toolbar
|
|
1657
1798
|
},
|
|
1658
1799
|
children: [
|
|
1659
1800
|
/* @__PURE__ */ jsx5(
|
|
@@ -1711,7 +1852,9 @@ function UILintToolbar() {
|
|
|
1711
1852
|
position: "absolute",
|
|
1712
1853
|
bottom: "100%",
|
|
1713
1854
|
left: 0,
|
|
1714
|
-
marginBottom: "8px"
|
|
1855
|
+
marginBottom: "8px",
|
|
1856
|
+
pointerEvents: "auto"
|
|
1857
|
+
// Re-enable pointer events for popover
|
|
1715
1858
|
},
|
|
1716
1859
|
children: /* @__PURE__ */ jsx5(SettingsPopover, { settings })
|
|
1717
1860
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
UILintToolbar
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XUMLILUN.js";
|
|
5
|
+
import {
|
|
6
|
+
InspectionPanel
|
|
7
|
+
} from "./chunk-JURUYCUC.js";
|
|
5
8
|
import {
|
|
6
|
-
InspectionPanel,
|
|
7
9
|
clearSourceCache,
|
|
8
10
|
fetchSource,
|
|
9
11
|
fetchSourceWithContext,
|
|
10
12
|
getCachedSource,
|
|
11
13
|
prefetchSources
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-W42PI2OF.js";
|
|
14
|
+
} from "./chunk-S4IWHBOQ.js";
|
|
14
15
|
import {
|
|
15
16
|
LocatorOverlay
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-LQ3WQYIF.js";
|
|
17
18
|
import {
|
|
18
19
|
DATA_UILINT_ID,
|
|
19
20
|
DEFAULT_SETTINGS,
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
scanDOMForSources,
|
|
30
31
|
updateElementRects,
|
|
31
32
|
useUILintContext
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-OU5EEQT6.js";
|
|
33
34
|
|
|
34
35
|
// src/consistency/snapshot.ts
|
|
35
36
|
var DATA_ELEMENTS_ATTR = "data-elements";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uilint-react",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"description": "React component for AI-powered UI consistency checking",
|
|
5
5
|
"author": "Peter Suggate",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"node": ">=20.0.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"uilint-core": "^0.1.
|
|
37
|
+
"uilint-core": "^0.1.46",
|
|
38
38
|
"zustand": "^5.0.5"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
package/dist/chunk-W42PI2OF.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
// src/components/ui-lint/Badge.tsx
|
|
4
|
-
import { jsx } from "react/jsx-runtime";
|
|
5
|
-
var BADGE_COLORS = {
|
|
6
|
-
success: "#68d391",
|
|
7
|
-
// Soft green
|
|
8
|
-
warning: "#f6ad55",
|
|
9
|
-
// Warm orange
|
|
10
|
-
error: "#ef4444"
|
|
11
|
-
// Red (for future use)
|
|
12
|
-
};
|
|
13
|
-
var FONT_MONO = `"SF Mono", Monaco, "Cascadia Code", monospace`;
|
|
14
|
-
function getBadgeTextColor(issueCount) {
|
|
15
|
-
if (issueCount === 0) return BADGE_COLORS.success;
|
|
16
|
-
return BADGE_COLORS.warning;
|
|
17
|
-
}
|
|
18
|
-
function getBadgeBackgroundColor(issueCount) {
|
|
19
|
-
const color = getBadgeTextColor(issueCount);
|
|
20
|
-
return `${color}20`;
|
|
21
|
-
}
|
|
22
|
-
var BADGE_STYLES = {
|
|
23
|
-
default: {
|
|
24
|
-
minWidth: "20px",
|
|
25
|
-
height: "20px",
|
|
26
|
-
padding: "0 6px",
|
|
27
|
-
borderRadius: "10px",
|
|
28
|
-
fontSize: "11px",
|
|
29
|
-
fontWeight: 600,
|
|
30
|
-
letterSpacing: "-0.02em"
|
|
31
|
-
},
|
|
32
|
-
small: {
|
|
33
|
-
minWidth: "18px",
|
|
34
|
-
height: "18px",
|
|
35
|
-
padding: "0 5px",
|
|
36
|
-
borderRadius: "9px",
|
|
37
|
-
fontSize: "10px",
|
|
38
|
-
fontWeight: 700,
|
|
39
|
-
letterSpacing: "0"
|
|
40
|
-
},
|
|
41
|
-
// For file row badges (slightly larger than small)
|
|
42
|
-
medium: {
|
|
43
|
-
minWidth: "22px",
|
|
44
|
-
height: "18px",
|
|
45
|
-
padding: "0 6px",
|
|
46
|
-
borderRadius: "9px",
|
|
47
|
-
fontSize: "10px",
|
|
48
|
-
fontWeight: 700,
|
|
49
|
-
letterSpacing: "0"
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
function Badge({
|
|
53
|
-
count,
|
|
54
|
-
size = "default",
|
|
55
|
-
backgroundColor,
|
|
56
|
-
color
|
|
57
|
-
}) {
|
|
58
|
-
const sizeStyles = BADGE_STYLES[size];
|
|
59
|
-
const bgColor = backgroundColor ?? getBadgeBackgroundColor(count);
|
|
60
|
-
const textColor = color ?? getBadgeTextColor(count);
|
|
61
|
-
return /* @__PURE__ */ jsx(
|
|
62
|
-
"span",
|
|
63
|
-
{
|
|
64
|
-
style: {
|
|
65
|
-
display: "inline-flex",
|
|
66
|
-
alignItems: "center",
|
|
67
|
-
justifyContent: "center",
|
|
68
|
-
...sizeStyles,
|
|
69
|
-
backgroundColor: bgColor,
|
|
70
|
-
color: textColor,
|
|
71
|
-
fontFamily: FONT_MONO
|
|
72
|
-
},
|
|
73
|
-
children: count
|
|
74
|
-
}
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export {
|
|
79
|
-
BADGE_COLORS,
|
|
80
|
-
Badge
|
|
81
|
-
};
|