uilint-react 0.1.28 → 0.1.29
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-HFQNIIO2.js → ElementBadges-J4QLKPLG.js} +262 -366
- package/dist/{InspectionPanel-4OWY4FVY.js → InspectionPanel-G6JJNNY4.js} +2 -2
- package/dist/{LocatorOverlay-JJDOKNOS.js → LocatorOverlay-MLZ3K4PZ.js} +2 -2
- package/dist/{UILintToolbar-GMZ6YSI2.js → UILintToolbar-2FFHUGZF.js} +2 -2
- package/dist/{chunk-QYRESGFG.js → chunk-LULWI5RD.js} +1 -1
- package/dist/{chunk-5VJ2Q2QW.js → chunk-MDEVC3SQ.js} +4 -4
- package/dist/{chunk-7X5HN55P.js → chunk-PD24RVVC.js} +1 -1
- package/dist/{chunk-XLIDEQXH.js → chunk-WUEPTJ24.js} +1 -1
- package/dist/index.js +4 -4
- package/package.json +2 -2
|
@@ -2,11 +2,131 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
import {
|
|
4
4
|
useUILintContext
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MDEVC3SQ.js";
|
|
6
6
|
|
|
7
7
|
// src/components/ui-lint/ElementBadges.tsx
|
|
8
8
|
import React, { useState, useEffect, useCallback, useMemo } from "react";
|
|
9
9
|
import { createPortal } from "react-dom";
|
|
10
|
+
|
|
11
|
+
// src/components/ui-lint/badge-layout.ts
|
|
12
|
+
var DEFAULT_CONFIG = {
|
|
13
|
+
repulsionForce: 50,
|
|
14
|
+
anchorStrength: 0.3,
|
|
15
|
+
minDistance: 24,
|
|
16
|
+
iterations: 50,
|
|
17
|
+
damping: 0.9
|
|
18
|
+
};
|
|
19
|
+
function computeLayout(positions, config) {
|
|
20
|
+
if (positions.length === 0) return [];
|
|
21
|
+
if (positions.length === 1) {
|
|
22
|
+
return [{ ...positions[0], nudgedX: positions[0].x, nudgedY: positions[0].y }];
|
|
23
|
+
}
|
|
24
|
+
const nodes = positions.map((p) => ({
|
|
25
|
+
position: p,
|
|
26
|
+
nudgedX: p.x,
|
|
27
|
+
nudgedY: p.y,
|
|
28
|
+
velocityX: 0,
|
|
29
|
+
velocityY: 0
|
|
30
|
+
}));
|
|
31
|
+
for (let iter = 0; iter < config.iterations; iter++) {
|
|
32
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
33
|
+
let fx = 0;
|
|
34
|
+
let fy = 0;
|
|
35
|
+
for (let j = 0; j < nodes.length; j++) {
|
|
36
|
+
if (i === j) continue;
|
|
37
|
+
const dx = nodes[i].nudgedX - nodes[j].nudgedX;
|
|
38
|
+
const dy = nodes[i].nudgedY - nodes[j].nudgedY;
|
|
39
|
+
const dist = Math.max(Math.hypot(dx, dy), 1);
|
|
40
|
+
if (dist < config.minDistance) {
|
|
41
|
+
const force = config.repulsionForce / (dist * dist);
|
|
42
|
+
fx += dx / dist * force;
|
|
43
|
+
fy += dy / dist * force;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const anchorDx = positions[i].x - nodes[i].nudgedX;
|
|
47
|
+
const anchorDy = positions[i].y - nodes[i].nudgedY;
|
|
48
|
+
fx += anchorDx * config.anchorStrength;
|
|
49
|
+
fy += anchorDy * config.anchorStrength;
|
|
50
|
+
nodes[i].velocityX = (nodes[i].velocityX + fx) * config.damping;
|
|
51
|
+
nodes[i].velocityY = (nodes[i].velocityY + fy) * config.damping;
|
|
52
|
+
nodes[i].nudgedX += nodes[i].velocityX;
|
|
53
|
+
nodes[i].nudgedY += nodes[i].velocityY;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return nodes.map((node) => ({
|
|
57
|
+
...node.position,
|
|
58
|
+
nudgedX: node.nudgedX,
|
|
59
|
+
nudgedY: node.nudgedY
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
var BadgeLayoutBuilder = class _BadgeLayoutBuilder {
|
|
63
|
+
config;
|
|
64
|
+
positions;
|
|
65
|
+
constructor(positions) {
|
|
66
|
+
this.positions = positions;
|
|
67
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a new layout builder with badge positions
|
|
71
|
+
*/
|
|
72
|
+
static create(positions) {
|
|
73
|
+
return new _BadgeLayoutBuilder(positions);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Set the repulsion force (how strongly badges push apart)
|
|
77
|
+
* Higher values = badges spread more aggressively
|
|
78
|
+
*/
|
|
79
|
+
repulsion(force) {
|
|
80
|
+
this.config.repulsionForce = force;
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Set the anchor strength (how strongly badges stay near origin)
|
|
85
|
+
* Higher values = badges stay closer to their original positions
|
|
86
|
+
*/
|
|
87
|
+
anchorStrength(strength) {
|
|
88
|
+
this.config.anchorStrength = strength;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Set the minimum distance between badge centers
|
|
93
|
+
* Badges closer than this will be pushed apart
|
|
94
|
+
*/
|
|
95
|
+
minDistance(distance) {
|
|
96
|
+
this.config.minDistance = distance;
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Set the number of simulation iterations
|
|
101
|
+
* More iterations = more stable but slower
|
|
102
|
+
*/
|
|
103
|
+
iterations(count) {
|
|
104
|
+
this.config.iterations = count;
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Set the damping factor (velocity decay per step)
|
|
109
|
+
* Lower values = system settles faster but may be less stable
|
|
110
|
+
*/
|
|
111
|
+
damping(factor) {
|
|
112
|
+
this.config.damping = factor;
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Run the simulation and return nudged positions
|
|
117
|
+
*/
|
|
118
|
+
compute() {
|
|
119
|
+
return computeLayout(this.positions, this.config);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
function findNearbyBadges(positions, x, y, threshold) {
|
|
123
|
+
return positions.filter((p) => {
|
|
124
|
+
const dist = Math.hypot(p.nudgedX - x, p.nudgedY - y);
|
|
125
|
+
return dist <= threshold;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/components/ui-lint/ElementBadges.tsx
|
|
10
130
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
131
|
var STYLES = {
|
|
12
132
|
bg: "rgba(17, 24, 39, 0.95)",
|
|
@@ -29,260 +149,29 @@ function getScaleFromDistance(distance) {
|
|
|
29
149
|
const t = (distance - NEAR_DISTANCE) / (FAR_DISTANCE - NEAR_DISTANCE);
|
|
30
150
|
return MAX_SCALE - t * (MAX_SCALE - MIN_SCALE);
|
|
31
151
|
}
|
|
32
|
-
var CLUSTER_THRESHOLD = 24;
|
|
33
152
|
function getBadgeColor(issueCount) {
|
|
34
153
|
if (issueCount === 0) return STYLES.success;
|
|
35
154
|
if (issueCount <= 2) return STYLES.warning;
|
|
36
155
|
return STYLES.error;
|
|
37
156
|
}
|
|
38
|
-
function
|
|
39
|
-
element
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
const [rect, setRect] = useState(null);
|
|
45
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
const updateRect = () => {
|
|
48
|
-
if (element.element && document.contains(element.element)) {
|
|
49
|
-
setRect(element.element.getBoundingClientRect());
|
|
50
|
-
} else {
|
|
51
|
-
setRect(null);
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
updateRect();
|
|
55
|
-
let rafId;
|
|
56
|
-
const handleUpdate = () => {
|
|
57
|
-
updateRect();
|
|
58
|
-
rafId = requestAnimationFrame(handleUpdate);
|
|
59
|
-
};
|
|
60
|
-
rafId = requestAnimationFrame(handleUpdate);
|
|
61
|
-
return () => {
|
|
62
|
-
cancelAnimationFrame(rafId);
|
|
63
|
-
};
|
|
64
|
-
}, [element.element]);
|
|
65
|
-
const handleClick = useCallback(
|
|
66
|
-
(e) => {
|
|
67
|
-
e.preventDefault();
|
|
68
|
-
e.stopPropagation();
|
|
69
|
-
onSelect(element, issue);
|
|
70
|
-
},
|
|
71
|
-
[element, issue, onSelect]
|
|
72
|
-
);
|
|
73
|
-
if (!rect) return null;
|
|
74
|
-
if (rect.top < -50 || rect.top > window.innerHeight + 50) return null;
|
|
75
|
-
if (rect.left < -50 || rect.left > window.innerWidth + 50) return null;
|
|
76
|
-
const scale = isHovered ? 1.1 : getScaleFromDistance(distance);
|
|
77
|
-
const badgeStyle = {
|
|
78
|
-
position: "fixed",
|
|
79
|
-
top: rect.top - 8,
|
|
80
|
-
left: rect.right - 8,
|
|
81
|
-
zIndex: isHovered ? 99999 : 99995,
|
|
82
|
-
cursor: "pointer",
|
|
83
|
-
transition: "transform 0.1s ease-out",
|
|
84
|
-
transform: `scale(${scale})`,
|
|
85
|
-
transformOrigin: "center center"
|
|
86
|
-
};
|
|
87
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
88
|
-
isHovered && /* @__PURE__ */ jsx(
|
|
89
|
-
"div",
|
|
90
|
-
{
|
|
91
|
-
style: {
|
|
92
|
-
position: "fixed",
|
|
93
|
-
top: rect.top - 2,
|
|
94
|
-
left: rect.left - 2,
|
|
95
|
-
width: rect.width + 4,
|
|
96
|
-
height: rect.height + 4,
|
|
97
|
-
border: `2px solid ${STYLES.highlight}`,
|
|
98
|
-
borderRadius: "4px",
|
|
99
|
-
pointerEvents: "none",
|
|
100
|
-
zIndex: 99994,
|
|
101
|
-
boxShadow: `0 0 0 1px rgba(59, 130, 246, 0.3)`
|
|
102
|
-
},
|
|
103
|
-
"data-ui-lint": true
|
|
104
|
-
}
|
|
105
|
-
),
|
|
106
|
-
/* @__PURE__ */ jsxs(
|
|
107
|
-
"div",
|
|
108
|
-
{
|
|
109
|
-
style: badgeStyle,
|
|
110
|
-
"data-ui-lint": true,
|
|
111
|
-
onMouseEnter: () => setIsHovered(true),
|
|
112
|
-
onMouseLeave: () => setIsHovered(false),
|
|
113
|
-
onClick: handleClick,
|
|
114
|
-
children: [
|
|
115
|
-
issue.status === "scanning" && /* @__PURE__ */ jsx(ScanningBadge, {}),
|
|
116
|
-
issue.status === "complete" && /* @__PURE__ */ jsx(IssueBadge, { count: issue.issues.length }),
|
|
117
|
-
issue.status === "error" && /* @__PURE__ */ jsx(ErrorBadge, {}),
|
|
118
|
-
issue.status === "pending" && /* @__PURE__ */ jsx(PendingBadge, {})
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
)
|
|
122
|
-
] });
|
|
123
|
-
}
|
|
124
|
-
function IssueBadge({ count }) {
|
|
125
|
-
const color = getBadgeColor(count);
|
|
126
|
-
if (count === 0) {
|
|
127
|
-
return /* @__PURE__ */ jsx(
|
|
128
|
-
"div",
|
|
129
|
-
{
|
|
130
|
-
style: {
|
|
131
|
-
display: "flex",
|
|
132
|
-
alignItems: "center",
|
|
133
|
-
justifyContent: "center",
|
|
134
|
-
width: "18px",
|
|
135
|
-
height: "18px",
|
|
136
|
-
borderRadius: "50%",
|
|
137
|
-
backgroundColor: color,
|
|
138
|
-
boxShadow: STYLES.shadow,
|
|
139
|
-
border: `1px solid ${STYLES.border}`
|
|
140
|
-
},
|
|
141
|
-
children: /* @__PURE__ */ jsx(CheckIcon, {})
|
|
142
|
-
}
|
|
143
|
-
);
|
|
157
|
+
function formatElementLabel(element) {
|
|
158
|
+
const tag = element.tagName.toLowerCase();
|
|
159
|
+
const source = element.source;
|
|
160
|
+
if (source) {
|
|
161
|
+
const fileName = source.fileName.split("/").pop() || "Unknown";
|
|
162
|
+
return `${tag} > ${fileName}`;
|
|
144
163
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
style: {
|
|
149
|
-
display: "flex",
|
|
150
|
-
alignItems: "center",
|
|
151
|
-
justifyContent: "center",
|
|
152
|
-
minWidth: "18px",
|
|
153
|
-
height: "18px",
|
|
154
|
-
padding: "0 5px",
|
|
155
|
-
borderRadius: "9px",
|
|
156
|
-
backgroundColor: color,
|
|
157
|
-
color: STYLES.text,
|
|
158
|
-
fontSize: "10px",
|
|
159
|
-
fontWeight: 700,
|
|
160
|
-
fontFamily: STYLES.font,
|
|
161
|
-
boxShadow: STYLES.shadow,
|
|
162
|
-
border: `1px solid ${STYLES.border}`
|
|
163
|
-
},
|
|
164
|
-
children: count > 9 ? "9+" : count
|
|
165
|
-
}
|
|
166
|
-
);
|
|
164
|
+
const componentName = element.componentStack[0]?.name;
|
|
165
|
+
return componentName ? `${tag} > ${componentName}` : tag;
|
|
167
166
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
alignItems: "center",
|
|
175
|
-
justifyContent: "center",
|
|
176
|
-
width: "18px",
|
|
177
|
-
height: "18px",
|
|
178
|
-
borderRadius: "50%",
|
|
179
|
-
backgroundColor: STYLES.bg,
|
|
180
|
-
boxShadow: STYLES.shadow,
|
|
181
|
-
border: `1px solid ${STYLES.border}`
|
|
182
|
-
},
|
|
183
|
-
children: [
|
|
184
|
-
/* @__PURE__ */ jsx("style", { children: `
|
|
185
|
-
@keyframes uilint-badge-spin {
|
|
186
|
-
from { transform: rotate(0deg); }
|
|
187
|
-
to { transform: rotate(360deg); }
|
|
188
|
-
}
|
|
189
|
-
` }),
|
|
190
|
-
/* @__PURE__ */ jsx(
|
|
191
|
-
"div",
|
|
192
|
-
{
|
|
193
|
-
style: {
|
|
194
|
-
width: "10px",
|
|
195
|
-
height: "10px",
|
|
196
|
-
border: "2px solid rgba(59, 130, 246, 0.3)",
|
|
197
|
-
borderTopColor: "#3B82F6",
|
|
198
|
-
borderRadius: "50%",
|
|
199
|
-
animation: "uilint-badge-spin 0.8s linear infinite"
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
)
|
|
203
|
-
]
|
|
204
|
-
}
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
function PendingBadge() {
|
|
208
|
-
return /* @__PURE__ */ jsx(
|
|
209
|
-
"div",
|
|
210
|
-
{
|
|
211
|
-
style: {
|
|
212
|
-
width: "10px",
|
|
213
|
-
height: "10px",
|
|
214
|
-
borderRadius: "50%",
|
|
215
|
-
backgroundColor: "rgba(156, 163, 175, 0.5)",
|
|
216
|
-
boxShadow: STYLES.shadow
|
|
167
|
+
var NEARBY_THRESHOLD = 30;
|
|
168
|
+
function BadgeAnimationStyles() {
|
|
169
|
+
return /* @__PURE__ */ jsx("style", { children: `
|
|
170
|
+
@keyframes uilint-badge-spin {
|
|
171
|
+
from { transform: rotate(0deg); }
|
|
172
|
+
to { transform: rotate(360deg); }
|
|
217
173
|
}
|
|
218
|
-
}
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
function ErrorBadge() {
|
|
222
|
-
return /* @__PURE__ */ jsx(
|
|
223
|
-
"div",
|
|
224
|
-
{
|
|
225
|
-
style: {
|
|
226
|
-
display: "flex",
|
|
227
|
-
alignItems: "center",
|
|
228
|
-
justifyContent: "center",
|
|
229
|
-
width: "18px",
|
|
230
|
-
height: "18px",
|
|
231
|
-
borderRadius: "50%",
|
|
232
|
-
backgroundColor: STYLES.error,
|
|
233
|
-
boxShadow: STYLES.shadow,
|
|
234
|
-
border: `1px solid ${STYLES.border}`
|
|
235
|
-
},
|
|
236
|
-
children: /* @__PURE__ */ jsx(ExclamationIcon, {})
|
|
237
|
-
}
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
var UnionFind = class {
|
|
241
|
-
parent = /* @__PURE__ */ new Map();
|
|
242
|
-
find(x) {
|
|
243
|
-
if (!this.parent.has(x)) {
|
|
244
|
-
this.parent.set(x, x);
|
|
245
|
-
}
|
|
246
|
-
if (this.parent.get(x) !== x) {
|
|
247
|
-
this.parent.set(x, this.find(this.parent.get(x)));
|
|
248
|
-
}
|
|
249
|
-
return this.parent.get(x);
|
|
250
|
-
}
|
|
251
|
-
union(x, y) {
|
|
252
|
-
const px = this.find(x);
|
|
253
|
-
const py = this.find(y);
|
|
254
|
-
if (px !== py) {
|
|
255
|
-
this.parent.set(px, py);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
function clusterBadges(positions, threshold) {
|
|
260
|
-
if (positions.length === 0) return [];
|
|
261
|
-
const uf = new UnionFind();
|
|
262
|
-
for (let i = 0; i < positions.length; i++) {
|
|
263
|
-
for (let j = i + 1; j < positions.length; j++) {
|
|
264
|
-
const dist = Math.hypot(
|
|
265
|
-
positions[i].x - positions[j].x,
|
|
266
|
-
positions[i].y - positions[j].y
|
|
267
|
-
);
|
|
268
|
-
if (dist <= threshold) {
|
|
269
|
-
uf.union(positions[i].element.id, positions[j].element.id);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
const clusters = /* @__PURE__ */ new Map();
|
|
274
|
-
for (const pos of positions) {
|
|
275
|
-
const root = uf.find(pos.element.id);
|
|
276
|
-
if (!clusters.has(root)) {
|
|
277
|
-
clusters.set(root, []);
|
|
278
|
-
}
|
|
279
|
-
clusters.get(root).push(pos);
|
|
280
|
-
}
|
|
281
|
-
return Array.from(clusters.entries()).map(([id, badges]) => {
|
|
282
|
-
const centroidX = badges.reduce((sum, b) => sum + b.x, 0) / badges.length;
|
|
283
|
-
const centroidY = badges.reduce((sum, b) => sum + b.y, 0) / badges.length;
|
|
284
|
-
return { id, badges, centroidX, centroidY };
|
|
285
|
-
});
|
|
174
|
+
` });
|
|
286
175
|
}
|
|
287
176
|
function ElementBadges() {
|
|
288
177
|
const { autoScanState, elementIssuesCache, setInspectedElement } = useUILintContext();
|
|
@@ -341,66 +230,61 @@ function ElementBadges() {
|
|
|
341
230
|
},
|
|
342
231
|
[setInspectedElement]
|
|
343
232
|
);
|
|
344
|
-
const
|
|
345
|
-
() =>
|
|
233
|
+
const nudgedPositions = useMemo(
|
|
234
|
+
() => BadgeLayoutBuilder.create(badgePositions).minDistance(24).repulsion(50).anchorStrength(0.3).iterations(50).compute(),
|
|
346
235
|
[badgePositions]
|
|
347
236
|
);
|
|
348
237
|
if (!mounted) return null;
|
|
349
238
|
if (autoScanState.status === "idle") return null;
|
|
350
|
-
const content = /* @__PURE__ */
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const distance = Math.hypot(x - cursorPos.x, y - cursorPos.y);
|
|
354
|
-
return /* @__PURE__ */ jsx(
|
|
355
|
-
ElementBadge,
|
|
356
|
-
{
|
|
357
|
-
element,
|
|
358
|
-
issue,
|
|
359
|
-
distance,
|
|
360
|
-
onSelect: handleSelect
|
|
361
|
-
},
|
|
362
|
-
element.id
|
|
363
|
-
);
|
|
364
|
-
} else {
|
|
239
|
+
const content = /* @__PURE__ */ jsxs("div", { "data-ui-lint": true, children: [
|
|
240
|
+
/* @__PURE__ */ jsx(BadgeAnimationStyles, {}),
|
|
241
|
+
nudgedPositions.map((nudgedPos) => {
|
|
365
242
|
const distance = Math.hypot(
|
|
366
|
-
|
|
367
|
-
|
|
243
|
+
nudgedPos.nudgedX - cursorPos.x,
|
|
244
|
+
nudgedPos.nudgedY - cursorPos.y
|
|
245
|
+
);
|
|
246
|
+
const nearbyBadges = findNearbyBadges(
|
|
247
|
+
nudgedPositions,
|
|
248
|
+
nudgedPos.nudgedX,
|
|
249
|
+
nudgedPos.nudgedY,
|
|
250
|
+
NEARBY_THRESHOLD
|
|
368
251
|
);
|
|
369
252
|
return /* @__PURE__ */ jsx(
|
|
370
|
-
|
|
253
|
+
NudgedBadge,
|
|
371
254
|
{
|
|
372
|
-
|
|
255
|
+
position: nudgedPos,
|
|
373
256
|
distance,
|
|
257
|
+
nearbyBadges,
|
|
258
|
+
cursorPos,
|
|
374
259
|
onSelect: handleSelect
|
|
375
260
|
},
|
|
376
|
-
|
|
261
|
+
nudgedPos.element.id
|
|
377
262
|
);
|
|
378
|
-
}
|
|
379
|
-
|
|
263
|
+
})
|
|
264
|
+
] });
|
|
380
265
|
return createPortal(content, document.body);
|
|
381
266
|
}
|
|
382
|
-
function
|
|
267
|
+
function NudgedBadge({
|
|
268
|
+
position,
|
|
269
|
+
distance,
|
|
270
|
+
nearbyBadges,
|
|
271
|
+
cursorPos,
|
|
272
|
+
onSelect
|
|
273
|
+
}) {
|
|
383
274
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
384
275
|
const [hoveredIndex, setHoveredIndex] = useState(null);
|
|
385
276
|
const closeTimeoutRef = React.useRef(null);
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
} else if (issue.status === "scanning") {
|
|
398
|
-
return { type: "scanning", color: STYLES.highlight };
|
|
399
|
-
} else {
|
|
400
|
-
return { type: "pending", color: "rgba(156, 163, 175, 0.5)" };
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
}, [cluster.badges]);
|
|
277
|
+
const { element, issue, rect, nudgedX, nudgedY } = position;
|
|
278
|
+
const hasNearbyBadges = nearbyBadges.length > 1;
|
|
279
|
+
const badgeColor = useMemo(() => {
|
|
280
|
+
if (issue.status === "error") return STYLES.error;
|
|
281
|
+
if (issue.status === "scanning") return STYLES.highlight;
|
|
282
|
+
if (issue.status === "pending") return "rgba(156, 163, 175, 0.7)";
|
|
283
|
+
if (issue.status === "complete") {
|
|
284
|
+
return getBadgeColor(issue.issues.length);
|
|
285
|
+
}
|
|
286
|
+
return STYLES.success;
|
|
287
|
+
}, [issue]);
|
|
404
288
|
const handleMouseEnter = useCallback(() => {
|
|
405
289
|
if (closeTimeoutRef.current) {
|
|
406
290
|
clearTimeout(closeTimeoutRef.current);
|
|
@@ -414,27 +298,58 @@ function ClusteredBadge({ cluster, distance, onSelect }) {
|
|
|
414
298
|
setHoveredIndex(null);
|
|
415
299
|
}, 150);
|
|
416
300
|
}, []);
|
|
417
|
-
const
|
|
301
|
+
const handleClick = useCallback(
|
|
302
|
+
(e) => {
|
|
303
|
+
e.preventDefault();
|
|
304
|
+
e.stopPropagation();
|
|
305
|
+
onSelect(element, issue);
|
|
306
|
+
},
|
|
307
|
+
[element, issue, onSelect]
|
|
308
|
+
);
|
|
309
|
+
const hoveredBadge = useMemo(() => {
|
|
310
|
+
if (hoveredIndex === null) return null;
|
|
311
|
+
return nearbyBadges[hoveredIndex] ?? null;
|
|
312
|
+
}, [hoveredIndex, nearbyBadges]);
|
|
418
313
|
const dropdownStyle = useMemo(() => {
|
|
419
|
-
const preferRight =
|
|
420
|
-
const preferBelow =
|
|
314
|
+
const preferRight = nudgedX < window.innerWidth - 220;
|
|
315
|
+
const preferBelow = nudgedY < window.innerHeight - 200;
|
|
421
316
|
return {
|
|
422
317
|
position: "fixed",
|
|
423
|
-
top: preferBelow ?
|
|
424
|
-
bottom: preferBelow ? void 0 : window.innerHeight -
|
|
425
|
-
left: preferRight ?
|
|
426
|
-
right: preferRight ? void 0 : window.innerWidth -
|
|
318
|
+
top: preferBelow ? nudgedY + 12 : void 0,
|
|
319
|
+
bottom: preferBelow ? void 0 : window.innerHeight - nudgedY + 12,
|
|
320
|
+
left: preferRight ? nudgedX - 8 : void 0,
|
|
321
|
+
right: preferRight ? void 0 : window.innerWidth - nudgedX - 8,
|
|
427
322
|
zIndex: 1e5,
|
|
428
323
|
backgroundColor: STYLES.bg,
|
|
429
324
|
borderRadius: "8px",
|
|
430
325
|
border: `1px solid ${STYLES.border}`,
|
|
431
326
|
boxShadow: "0 4px 20px rgba(0, 0, 0, 0.4)",
|
|
432
327
|
padding: "4px 0",
|
|
433
|
-
minWidth: "
|
|
328
|
+
minWidth: "200px",
|
|
434
329
|
fontFamily: STYLES.font
|
|
435
330
|
};
|
|
436
|
-
}, [
|
|
331
|
+
}, [nudgedX, nudgedY]);
|
|
332
|
+
const scale = isExpanded ? 1.1 : getScaleFromDistance(distance);
|
|
333
|
+
const issueCount = issue.status === "complete" ? issue.issues.length : 0;
|
|
437
334
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
335
|
+
isExpanded && !hoveredBadge && /* @__PURE__ */ jsx(
|
|
336
|
+
"div",
|
|
337
|
+
{
|
|
338
|
+
style: {
|
|
339
|
+
position: "fixed",
|
|
340
|
+
top: rect.top - 2,
|
|
341
|
+
left: rect.left - 2,
|
|
342
|
+
width: rect.width + 4,
|
|
343
|
+
height: rect.height + 4,
|
|
344
|
+
border: `2px solid ${STYLES.highlight}`,
|
|
345
|
+
borderRadius: "4px",
|
|
346
|
+
pointerEvents: "none",
|
|
347
|
+
zIndex: 99994,
|
|
348
|
+
boxShadow: `0 0 0 1px rgba(59, 130, 246, 0.3)`
|
|
349
|
+
},
|
|
350
|
+
"data-ui-lint": true
|
|
351
|
+
}
|
|
352
|
+
),
|
|
438
353
|
hoveredBadge && /* @__PURE__ */ jsx(
|
|
439
354
|
"div",
|
|
440
355
|
{
|
|
@@ -458,98 +373,79 @@ function ClusteredBadge({ cluster, distance, onSelect }) {
|
|
|
458
373
|
{
|
|
459
374
|
style: {
|
|
460
375
|
position: "fixed",
|
|
461
|
-
top:
|
|
462
|
-
left:
|
|
376
|
+
top: nudgedY - 9,
|
|
377
|
+
left: nudgedX - 9,
|
|
463
378
|
zIndex: isExpanded ? 99999 : 99995,
|
|
464
379
|
cursor: "pointer",
|
|
465
|
-
transition: "transform 0.1s ease-out",
|
|
466
|
-
transform: `scale(${
|
|
380
|
+
transition: "transform 0.1s ease-out, top 0.15s ease-out, left 0.15s ease-out",
|
|
381
|
+
transform: `scale(${scale})`,
|
|
467
382
|
transformOrigin: "center center"
|
|
468
383
|
},
|
|
469
384
|
"data-ui-lint": true,
|
|
470
385
|
onMouseEnter: handleMouseEnter,
|
|
471
386
|
onMouseLeave: handleMouseLeave,
|
|
387
|
+
onClick: handleClick,
|
|
472
388
|
children: /* @__PURE__ */ jsx(
|
|
473
389
|
"div",
|
|
474
390
|
{
|
|
475
391
|
style: {
|
|
476
392
|
display: "flex",
|
|
477
393
|
alignItems: "center",
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
394
|
+
justifyContent: "center",
|
|
395
|
+
width: "18px",
|
|
396
|
+
height: "18px",
|
|
397
|
+
borderRadius: "50%",
|
|
398
|
+
backgroundColor: badgeColor,
|
|
481
399
|
boxShadow: STYLES.shadow,
|
|
482
|
-
border: `1px solid ${STYLES.border}
|
|
483
|
-
overflow: "hidden"
|
|
400
|
+
border: `1px solid ${STYLES.border}`
|
|
484
401
|
},
|
|
485
|
-
children:
|
|
402
|
+
children: issue.status === "scanning" ? /* @__PURE__ */ jsx(
|
|
403
|
+
"div",
|
|
404
|
+
{
|
|
405
|
+
style: {
|
|
406
|
+
width: "10px",
|
|
407
|
+
height: "10px",
|
|
408
|
+
border: "2px solid rgba(255, 255, 255, 0.3)",
|
|
409
|
+
borderTopColor: "#FFFFFF",
|
|
410
|
+
borderRadius: "50%",
|
|
411
|
+
animation: "uilint-badge-spin 0.8s linear infinite"
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
) : issue.status === "error" ? /* @__PURE__ */ jsx(ExclamationIconTiny, {}) : issue.status === "pending" ? /* @__PURE__ */ jsx(
|
|
486
415
|
"div",
|
|
487
416
|
{
|
|
488
417
|
style: {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
418
|
+
width: "6px",
|
|
419
|
+
height: "6px",
|
|
420
|
+
borderRadius: "50%",
|
|
421
|
+
backgroundColor: "rgba(255, 255, 255, 0.4)"
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
) : issueCount === 0 ? /* @__PURE__ */ jsx(CheckIconTiny, {}) : /* @__PURE__ */ jsx(
|
|
425
|
+
"span",
|
|
426
|
+
{
|
|
427
|
+
style: {
|
|
428
|
+
color: STYLES.text,
|
|
429
|
+
fontSize: "10px",
|
|
430
|
+
fontWeight: 700,
|
|
431
|
+
fontFamily: STYLES.font
|
|
497
432
|
},
|
|
498
|
-
children:
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
{
|
|
502
|
-
style: {
|
|
503
|
-
color: STYLES.text,
|
|
504
|
-
fontSize: "10px",
|
|
505
|
-
fontWeight: 700,
|
|
506
|
-
fontFamily: STYLES.font
|
|
507
|
-
},
|
|
508
|
-
children: segment.count > 9 ? "9+" : segment.count
|
|
509
|
-
}
|
|
510
|
-
)),
|
|
511
|
-
segment.type === "error" && /* @__PURE__ */ jsx(ExclamationIconTiny, {}),
|
|
512
|
-
segment.type === "scanning" && /* @__PURE__ */ jsx(
|
|
513
|
-
"div",
|
|
514
|
-
{
|
|
515
|
-
style: {
|
|
516
|
-
width: "8px",
|
|
517
|
-
height: "8px",
|
|
518
|
-
border: "1.5px solid rgba(255, 255, 255, 0.3)",
|
|
519
|
-
borderTopColor: "#FFFFFF",
|
|
520
|
-
borderRadius: "50%",
|
|
521
|
-
animation: "uilint-badge-spin 0.8s linear infinite"
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
),
|
|
525
|
-
segment.type === "pending" && /* @__PURE__ */ jsx(
|
|
526
|
-
"div",
|
|
527
|
-
{
|
|
528
|
-
style: {
|
|
529
|
-
width: "6px",
|
|
530
|
-
height: "6px",
|
|
531
|
-
borderRadius: "50%",
|
|
532
|
-
backgroundColor: "rgba(255, 255, 255, 0.4)"
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
)
|
|
536
|
-
]
|
|
537
|
-
},
|
|
538
|
-
index
|
|
539
|
-
))
|
|
433
|
+
children: issueCount > 9 ? "9+" : issueCount
|
|
434
|
+
}
|
|
435
|
+
)
|
|
540
436
|
}
|
|
541
437
|
)
|
|
542
438
|
}
|
|
543
439
|
),
|
|
544
|
-
isExpanded && /* @__PURE__ */ jsx(
|
|
440
|
+
isExpanded && hasNearbyBadges && /* @__PURE__ */ jsx(
|
|
545
441
|
"div",
|
|
546
442
|
{
|
|
547
443
|
style: dropdownStyle,
|
|
548
444
|
"data-ui-lint": true,
|
|
549
445
|
onMouseEnter: handleMouseEnter,
|
|
550
446
|
onMouseLeave: handleMouseLeave,
|
|
551
|
-
children:
|
|
552
|
-
|
|
447
|
+
children: nearbyBadges.map((badge, index) => /* @__PURE__ */ jsx(
|
|
448
|
+
DropdownItem,
|
|
553
449
|
{
|
|
554
450
|
badge,
|
|
555
451
|
isHovered: hoveredIndex === index,
|
|
@@ -563,14 +459,14 @@ function ClusteredBadge({ cluster, distance, onSelect }) {
|
|
|
563
459
|
)
|
|
564
460
|
] });
|
|
565
461
|
}
|
|
566
|
-
function
|
|
462
|
+
function DropdownItem({
|
|
567
463
|
badge,
|
|
568
464
|
isHovered,
|
|
569
465
|
onMouseEnter,
|
|
570
466
|
onMouseLeave,
|
|
571
467
|
onClick
|
|
572
468
|
}) {
|
|
573
|
-
const
|
|
469
|
+
const elementLabel = formatElementLabel(badge.element);
|
|
574
470
|
const issueCount = badge.issue.status === "complete" ? badge.issue.issues.length : 0;
|
|
575
471
|
const color = getBadgeColor(issueCount);
|
|
576
472
|
return /* @__PURE__ */ jsxs(
|
|
@@ -607,12 +503,12 @@ function ClusterDropdownItem({
|
|
|
607
503
|
style: {
|
|
608
504
|
fontSize: "12px",
|
|
609
505
|
color: STYLES.text,
|
|
610
|
-
maxWidth: "
|
|
506
|
+
maxWidth: "160px",
|
|
611
507
|
overflow: "hidden",
|
|
612
508
|
textOverflow: "ellipsis",
|
|
613
509
|
whiteSpace: "nowrap"
|
|
614
510
|
},
|
|
615
|
-
children:
|
|
511
|
+
children: elementLabel
|
|
616
512
|
}
|
|
617
513
|
)
|
|
618
514
|
] }),
|
|
@@ -796,10 +796,10 @@ function UILintUI() {
|
|
|
796
796
|
const [components, setComponents] = useState(null);
|
|
797
797
|
useEffect(() => {
|
|
798
798
|
Promise.all([
|
|
799
|
-
import("./UILintToolbar-
|
|
800
|
-
import("./InspectionPanel-
|
|
801
|
-
import("./LocatorOverlay-
|
|
802
|
-
import("./ElementBadges-
|
|
799
|
+
import("./UILintToolbar-2FFHUGZF.js"),
|
|
800
|
+
import("./InspectionPanel-G6JJNNY4.js"),
|
|
801
|
+
import("./LocatorOverlay-MLZ3K4PZ.js"),
|
|
802
|
+
import("./ElementBadges-J4QLKPLG.js")
|
|
803
803
|
]).then(([toolbar, panel, locator, badges]) => {
|
|
804
804
|
setComponents({
|
|
805
805
|
Toolbar: toolbar.UILintToolbar,
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
UILintToolbar
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PD24RVVC.js";
|
|
5
5
|
import {
|
|
6
6
|
InspectionPanel,
|
|
7
7
|
clearSourceCache,
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
fetchSourceWithContext,
|
|
10
10
|
getCachedSource,
|
|
11
11
|
prefetchSources
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LULWI5RD.js";
|
|
13
13
|
import {
|
|
14
14
|
LocatorOverlay
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WUEPTJ24.js";
|
|
16
16
|
import {
|
|
17
17
|
DATA_UILINT_ID,
|
|
18
18
|
DEFAULT_SETTINGS,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
scanDOMForSources,
|
|
32
32
|
updateElementRects,
|
|
33
33
|
useUILintContext
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-MDEVC3SQ.js";
|
|
35
35
|
|
|
36
36
|
// src/consistency/snapshot.ts
|
|
37
37
|
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.29",
|
|
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.29",
|
|
38
38
|
"zustand": "^5.0.5"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|