uilint-react 0.1.45 → 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-RV7I6QXS.js → ElementBadges-E35MQ2SV.js} +73 -32
- package/dist/InspectionPanel-EOW6OJFT.js +10 -0
- package/dist/{LocatorOverlay-Z24VU27L.js → LocatorOverlay-BF6EED4N.js} +2 -2
- package/dist/UILintToolbar-XJN6LFWZ.js +10 -0
- package/dist/{chunk-ZZOKTGSU.js → chunk-JURUYCUC.js} +18 -103
- package/dist/{chunk-YLTHKMTO.js → chunk-LQ3WQYIF.js} +1 -1
- package/dist/{chunk-BJD2V2LF.js → chunk-OU5EEQT6.js} +4 -4
- package/dist/chunk-S4IWHBOQ.js +178 -0
- package/dist/{chunk-EQKEHJI4.js → chunk-XUMLILUN.js} +124 -25
- package/dist/index.js +7 -6
- package/package.json +2 -2
- package/dist/InspectionPanel-QC6WR5IG.js +0 -10
- package/dist/UILintToolbar-5NK77IQJ.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 ]+/);
|
|
@@ -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
|
}
|
|
@@ -1533,7 +1612,7 @@ function UILintToolbar() {
|
|
|
1533
1612
|
const [nextjsOverlayVisible, setNextjsOverlayVisible] = useState2(false);
|
|
1534
1613
|
const toolbarRef = useRef2(null);
|
|
1535
1614
|
const settingsRef = useRef2(null);
|
|
1536
|
-
|
|
1615
|
+
useEffect3(() => {
|
|
1537
1616
|
const checkForNextOverlay = () => {
|
|
1538
1617
|
const overlaySelectors = [
|
|
1539
1618
|
"nextjs-portal",
|
|
@@ -1572,12 +1651,15 @@ function UILintToolbar() {
|
|
|
1572
1651
|
});
|
|
1573
1652
|
const totalIssues = elementIssues + fileLevelIssues;
|
|
1574
1653
|
const hasIssues = totalIssues > 0;
|
|
1575
|
-
|
|
1654
|
+
useEffect3(() => {
|
|
1576
1655
|
setMounted(true);
|
|
1577
1656
|
}, []);
|
|
1578
|
-
|
|
1657
|
+
useEffect3(() => {
|
|
1579
1658
|
const handleClickOutside = (e) => {
|
|
1580
1659
|
const target = e.target;
|
|
1660
|
+
if (target?.closest?.("[data-ui-lint]")) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1581
1663
|
if (showSettings && settingsRef.current && toolbarRef.current) {
|
|
1582
1664
|
if (!settingsRef.current.contains(target) && !toolbarRef.current.contains(target)) {
|
|
1583
1665
|
handleCloseSettings();
|
|
@@ -1590,10 +1672,10 @@ function UILintToolbar() {
|
|
|
1590
1672
|
if (showResults) setShowResults(false);
|
|
1591
1673
|
}
|
|
1592
1674
|
};
|
|
1593
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
1675
|
+
document.addEventListener("mousedown", handleClickOutside, true);
|
|
1594
1676
|
document.addEventListener("keydown", handleEscape);
|
|
1595
1677
|
return () => {
|
|
1596
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
1678
|
+
document.removeEventListener("mousedown", handleClickOutside, true);
|
|
1597
1679
|
document.removeEventListener("keydown", handleEscape);
|
|
1598
1680
|
};
|
|
1599
1681
|
}, [showSettings, showResults]);
|
|
@@ -1628,6 +1710,12 @@ function UILintToolbar() {
|
|
|
1628
1710
|
setSettingsClosing(false);
|
|
1629
1711
|
}, 150);
|
|
1630
1712
|
}, []);
|
|
1713
|
+
const handleUILintInteraction = useCallback2(
|
|
1714
|
+
(e) => {
|
|
1715
|
+
e.stopPropagation();
|
|
1716
|
+
},
|
|
1717
|
+
[]
|
|
1718
|
+
);
|
|
1631
1719
|
if (!mounted) return null;
|
|
1632
1720
|
const issueVariant = !liveScanEnabled ? "default" : hasIssues ? "warning" : isComplete ? "success" : "default";
|
|
1633
1721
|
const bottomPosition = nextjsOverlayVisible ? "80px" : "20px";
|
|
@@ -1635,13 +1723,18 @@ function UILintToolbar() {
|
|
|
1635
1723
|
"div",
|
|
1636
1724
|
{
|
|
1637
1725
|
"data-ui-lint": true,
|
|
1726
|
+
onMouseDown: handleUILintInteraction,
|
|
1727
|
+
onClick: handleUILintInteraction,
|
|
1728
|
+
onKeyDown: handleUILintInteraction,
|
|
1638
1729
|
style: {
|
|
1639
1730
|
position: "fixed",
|
|
1640
1731
|
bottom: bottomPosition,
|
|
1641
1732
|
left: "20px",
|
|
1642
1733
|
zIndex: 99999,
|
|
1643
1734
|
fontFamily: TOKENS.fontFamily,
|
|
1644
|
-
transition: `bottom ${TOKENS.transitionSlow}
|
|
1735
|
+
transition: `bottom ${TOKENS.transitionSlow}`,
|
|
1736
|
+
pointerEvents: "none"
|
|
1737
|
+
// Allow clicks to pass through empty space
|
|
1645
1738
|
},
|
|
1646
1739
|
children: [
|
|
1647
1740
|
/* @__PURE__ */ jsx5("style", { children: globalStyles }),
|
|
@@ -1654,7 +1747,9 @@ function UILintToolbar() {
|
|
|
1654
1747
|
marginBottom: "10px",
|
|
1655
1748
|
fontSize: "11px",
|
|
1656
1749
|
color: TOKENS.textMuted,
|
|
1657
|
-
letterSpacing: "0.02em"
|
|
1750
|
+
letterSpacing: "0.02em",
|
|
1751
|
+
pointerEvents: "auto"
|
|
1752
|
+
// Re-enable pointer events for hint
|
|
1658
1753
|
},
|
|
1659
1754
|
"aria-hidden": !liveScanEnabled,
|
|
1660
1755
|
children: /* @__PURE__ */ jsx5(
|
|
@@ -1697,7 +1792,9 @@ function UILintToolbar() {
|
|
|
1697
1792
|
`${TOKENS.warning}30`
|
|
1698
1793
|
)}` : TOKENS.shadowMd,
|
|
1699
1794
|
overflow: "hidden",
|
|
1700
|
-
transition: `box-shadow ${TOKENS.transitionBase}
|
|
1795
|
+
transition: `box-shadow ${TOKENS.transitionBase}`,
|
|
1796
|
+
pointerEvents: "auto"
|
|
1797
|
+
// Re-enable pointer events for interactive toolbar
|
|
1701
1798
|
},
|
|
1702
1799
|
children: [
|
|
1703
1800
|
/* @__PURE__ */ jsx5(
|
|
@@ -1755,7 +1852,9 @@ function UILintToolbar() {
|
|
|
1755
1852
|
position: "absolute",
|
|
1756
1853
|
bottom: "100%",
|
|
1757
1854
|
left: 0,
|
|
1758
|
-
marginBottom: "8px"
|
|
1855
|
+
marginBottom: "8px",
|
|
1856
|
+
pointerEvents: "auto"
|
|
1857
|
+
// Re-enable pointer events for popover
|
|
1759
1858
|
},
|
|
1760
1859
|
children: /* @__PURE__ */ jsx5(SettingsPopover, { settings })
|
|
1761
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
|
-
};
|