uilint-react 0.1.18 → 0.1.20

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.
@@ -0,0 +1,276 @@
1
+ "use client";
2
+ import {
3
+ useUILintContext
4
+ } from "./chunk-DAFFOBEU.js";
5
+
6
+ // src/components/ui-lint/UILintToolbar.tsx
7
+ import { useState, useRef, useEffect } from "react";
8
+ import { createPortal } from "react-dom";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ var STYLES = {
11
+ bg: "rgba(17, 24, 39, 0.9)",
12
+ bgHover: "rgba(31, 41, 55, 0.95)",
13
+ border: "rgba(75, 85, 99, 0.5)",
14
+ text: "#F9FAFB",
15
+ textMuted: "#9CA3AF",
16
+ accent: "#3B82F6",
17
+ accentHover: "#2563EB",
18
+ shadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
19
+ blur: "blur(12px)",
20
+ font: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
21
+ fontMono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace'
22
+ };
23
+ function UILintToolbar() {
24
+ const { settings, updateSettings, inspectedElement } = useUILintContext();
25
+ const [showSettings, setShowSettings] = useState(false);
26
+ const [mounted, setMounted] = useState(false);
27
+ const settingsRef = useRef(null);
28
+ useEffect(() => {
29
+ setMounted(true);
30
+ }, []);
31
+ useEffect(() => {
32
+ const handleClickOutside = (e) => {
33
+ if (settingsRef.current && !settingsRef.current.contains(e.target)) {
34
+ setShowSettings(false);
35
+ }
36
+ };
37
+ if (showSettings) {
38
+ document.addEventListener("mousedown", handleClickOutside);
39
+ return () => document.removeEventListener("mousedown", handleClickOutside);
40
+ }
41
+ }, [showSettings]);
42
+ if (!mounted) return null;
43
+ if (inspectedElement) return null;
44
+ const content = /* @__PURE__ */ jsxs(
45
+ "div",
46
+ {
47
+ "data-ui-lint": true,
48
+ style: {
49
+ position: "fixed",
50
+ top: "24px",
51
+ right: "24px",
52
+ zIndex: 99999,
53
+ fontFamily: STYLES.font
54
+ },
55
+ children: [
56
+ /* @__PURE__ */ jsx("style", { children: `
57
+ @keyframes uilint-fade-in {
58
+ from { opacity: 0; transform: scale(0.95); }
59
+ to { opacity: 1; transform: scale(1); }
60
+ }
61
+ ` }),
62
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, ref: settingsRef, children: [
63
+ /* @__PURE__ */ jsx(
64
+ "button",
65
+ {
66
+ onClick: () => setShowSettings(!showSettings),
67
+ style: {
68
+ display: "flex",
69
+ alignItems: "center",
70
+ justifyContent: "center",
71
+ width: "48px",
72
+ height: "48px",
73
+ borderRadius: "50%",
74
+ border: `1px solid ${STYLES.border}`,
75
+ backgroundColor: showSettings ? STYLES.bgHover : STYLES.bg,
76
+ backdropFilter: STYLES.blur,
77
+ WebkitBackdropFilter: STYLES.blur,
78
+ boxShadow: STYLES.shadow,
79
+ cursor: "pointer",
80
+ transition: "all 0.2s ease-out",
81
+ color: showSettings ? STYLES.text : STYLES.textMuted,
82
+ fontSize: "20px"
83
+ },
84
+ onMouseEnter: (e) => {
85
+ e.currentTarget.style.transform = "scale(1.05)";
86
+ e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.5)";
87
+ },
88
+ onMouseLeave: (e) => {
89
+ e.currentTarget.style.transform = "scale(1)";
90
+ e.currentTarget.style.boxShadow = STYLES.shadow;
91
+ },
92
+ title: "UILint Settings (Alt+Click any element to inspect)",
93
+ children: /* @__PURE__ */ jsx(UILintIcon, { active: showSettings })
94
+ }
95
+ ),
96
+ showSettings && /* @__PURE__ */ jsx(SettingsPopover, { settings, onUpdate: updateSettings }),
97
+ /* @__PURE__ */ jsxs(
98
+ "div",
99
+ {
100
+ style: {
101
+ position: "absolute",
102
+ left: "100%",
103
+ top: "50%",
104
+ transform: "translateY(-50%)",
105
+ marginLeft: "12px",
106
+ padding: "8px 12px",
107
+ borderRadius: "8px",
108
+ backgroundColor: STYLES.bg,
109
+ border: `1px solid ${STYLES.border}`,
110
+ backdropFilter: STYLES.blur,
111
+ WebkitBackdropFilter: STYLES.blur,
112
+ boxShadow: STYLES.shadow,
113
+ fontSize: "12px",
114
+ color: STYLES.textMuted,
115
+ whiteSpace: "nowrap",
116
+ opacity: 0,
117
+ transition: "opacity 0.2s",
118
+ pointerEvents: "none"
119
+ },
120
+ className: "uilint-hint",
121
+ children: [
122
+ /* @__PURE__ */ jsx("span", { style: { color: STYLES.text }, children: "Alt+Click" }),
123
+ " any element to inspect"
124
+ ]
125
+ }
126
+ )
127
+ ] })
128
+ ]
129
+ }
130
+ );
131
+ return createPortal(content, document.body);
132
+ }
133
+ function SettingsPopover({
134
+ settings,
135
+ onUpdate
136
+ }) {
137
+ return /* @__PURE__ */ jsxs(
138
+ "div",
139
+ {
140
+ style: {
141
+ position: "absolute",
142
+ top: "100%",
143
+ right: 0,
144
+ marginTop: "8px",
145
+ width: "280px",
146
+ padding: "16px",
147
+ borderRadius: "12px",
148
+ border: `1px solid ${STYLES.border}`,
149
+ backgroundColor: STYLES.bg,
150
+ backdropFilter: STYLES.blur,
151
+ WebkitBackdropFilter: STYLES.blur,
152
+ boxShadow: STYLES.shadow,
153
+ animation: "uilint-fade-in 0.15s ease-out"
154
+ },
155
+ children: [
156
+ /* @__PURE__ */ jsx(
157
+ "div",
158
+ {
159
+ style: {
160
+ fontSize: "13px",
161
+ fontWeight: 600,
162
+ color: STYLES.text,
163
+ marginBottom: "12px"
164
+ },
165
+ children: "UILint Settings"
166
+ }
167
+ ),
168
+ /* @__PURE__ */ jsx(
169
+ SettingToggle,
170
+ {
171
+ label: "Hide node_modules",
172
+ checked: settings.hideNodeModules,
173
+ onChange: (checked) => onUpdate({ hideNodeModules: checked })
174
+ }
175
+ ),
176
+ /* @__PURE__ */ jsxs(
177
+ "div",
178
+ {
179
+ style: {
180
+ marginTop: "12px",
181
+ paddingTop: "12px",
182
+ borderTop: `1px solid ${STYLES.border}`,
183
+ fontSize: "11px",
184
+ color: STYLES.textMuted,
185
+ lineHeight: 1.5
186
+ },
187
+ children: [
188
+ /* @__PURE__ */ jsx("strong", { style: { color: STYLES.text }, children: "Alt+Click" }),
189
+ " any element to open the inspector sidebar"
190
+ ]
191
+ }
192
+ )
193
+ ]
194
+ }
195
+ );
196
+ }
197
+ function SettingToggle({
198
+ label,
199
+ checked,
200
+ onChange
201
+ }) {
202
+ return /* @__PURE__ */ jsxs(
203
+ "label",
204
+ {
205
+ style: {
206
+ display: "flex",
207
+ alignItems: "center",
208
+ justifyContent: "space-between",
209
+ padding: "8px 0",
210
+ cursor: "pointer"
211
+ },
212
+ children: [
213
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: STYLES.textMuted }, children: label }),
214
+ /* @__PURE__ */ jsx(
215
+ "div",
216
+ {
217
+ onClick: () => onChange(!checked),
218
+ style: {
219
+ width: "36px",
220
+ height: "20px",
221
+ borderRadius: "10px",
222
+ backgroundColor: checked ? STYLES.accent : "rgba(75, 85, 99, 0.5)",
223
+ position: "relative",
224
+ transition: "background-color 0.2s"
225
+ },
226
+ children: /* @__PURE__ */ jsx(
227
+ "div",
228
+ {
229
+ style: {
230
+ position: "absolute",
231
+ top: "2px",
232
+ left: checked ? "18px" : "2px",
233
+ width: "16px",
234
+ height: "16px",
235
+ borderRadius: "50%",
236
+ backgroundColor: "#FFFFFF",
237
+ transition: "left 0.2s",
238
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)"
239
+ }
240
+ }
241
+ )
242
+ }
243
+ )
244
+ ]
245
+ }
246
+ );
247
+ }
248
+ function UILintIcon({ active }) {
249
+ return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [
250
+ /* @__PURE__ */ jsx(
251
+ "rect",
252
+ {
253
+ x: "3",
254
+ y: "3",
255
+ width: "18",
256
+ height: "18",
257
+ rx: "3",
258
+ stroke: active ? STYLES.accent : "currentColor",
259
+ strokeWidth: "2"
260
+ }
261
+ ),
262
+ /* @__PURE__ */ jsx(
263
+ "path",
264
+ {
265
+ d: "M7 12h10M12 7v10",
266
+ stroke: active ? STYLES.accent : "currentColor",
267
+ strokeWidth: "2",
268
+ strokeLinecap: "round"
269
+ }
270
+ )
271
+ ] });
272
+ }
273
+
274
+ export {
275
+ UILintToolbar
276
+ };
@@ -0,0 +1,283 @@
1
+ "use client";
2
+ import {
3
+ useUILintContext
4
+ } from "./chunk-DAFFOBEU.js";
5
+
6
+ // src/components/ui-lint/LocatorOverlay.tsx
7
+ import { useState, useEffect, useMemo } from "react";
8
+ import { createPortal } from "react-dom";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ var STYLES = {
11
+ bg: "rgba(17, 24, 39, 0.95)",
12
+ border: "rgba(59, 130, 246, 0.8)",
13
+ borderHighlight: "#3B82F6",
14
+ text: "#F9FAFB",
15
+ textMuted: "#9CA3AF",
16
+ accent: "#3B82F6",
17
+ font: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
18
+ fontMono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
19
+ shadow: "0 4px 20px rgba(0, 0, 0, 0.5)",
20
+ blur: "blur(12px)"
21
+ };
22
+ function getFileName(path) {
23
+ const parts = path.split("/");
24
+ return parts[parts.length - 1] || path;
25
+ }
26
+ function LocatorOverlay() {
27
+ const { locatorTarget } = useUILintContext();
28
+ const [mounted, setMounted] = useState(false);
29
+ useEffect(() => {
30
+ setMounted(true);
31
+ }, []);
32
+ const currentSource = useMemo(() => {
33
+ if (!locatorTarget) return null;
34
+ if (locatorTarget.stackIndex === 0) {
35
+ return locatorTarget.source;
36
+ }
37
+ const stackItem = locatorTarget.componentStack[locatorTarget.stackIndex - 1];
38
+ return stackItem?.source || null;
39
+ }, [locatorTarget]);
40
+ const currentName = useMemo(() => {
41
+ if (!locatorTarget) return "";
42
+ if (locatorTarget.stackIndex === 0) {
43
+ return locatorTarget.element.tagName.toLowerCase();
44
+ }
45
+ const stackItem = locatorTarget.componentStack[locatorTarget.stackIndex - 1];
46
+ return stackItem?.name || "Unknown";
47
+ }, [locatorTarget]);
48
+ if (!mounted || !locatorTarget) return null;
49
+ const { rect } = locatorTarget;
50
+ const hasParents = locatorTarget.componentStack.length > 0;
51
+ const content = /* @__PURE__ */ jsxs("div", { "data-ui-lint": true, style: { pointerEvents: "none" }, children: [
52
+ /* @__PURE__ */ jsx("style", { children: `
53
+ @keyframes uilint-locator-fade-in {
54
+ from { opacity: 0; }
55
+ to { opacity: 1; }
56
+ }
57
+ @keyframes uilint-locator-pulse {
58
+ 0%, 100% { opacity: 0.8; }
59
+ 50% { opacity: 1; }
60
+ }
61
+ ` }),
62
+ /* @__PURE__ */ jsx(
63
+ "div",
64
+ {
65
+ style: {
66
+ position: "fixed",
67
+ top: rect.top - 2,
68
+ left: rect.left - 2,
69
+ width: rect.width + 4,
70
+ height: rect.height + 4,
71
+ border: `2px solid ${STYLES.borderHighlight}`,
72
+ borderRadius: "4px",
73
+ boxShadow: `0 0 0 1px rgba(59, 130, 246, 0.3), inset 0 0 0 1px rgba(59, 130, 246, 0.1)`,
74
+ animation: "uilint-locator-fade-in 0.1s ease-out",
75
+ zIndex: 99997
76
+ }
77
+ }
78
+ ),
79
+ /* @__PURE__ */ jsx(
80
+ InfoTooltip,
81
+ {
82
+ rect,
83
+ source: currentSource,
84
+ componentName: currentName,
85
+ stackIndex: locatorTarget.stackIndex,
86
+ stackLength: locatorTarget.componentStack.length,
87
+ hasParents
88
+ }
89
+ )
90
+ ] });
91
+ return createPortal(content, document.body);
92
+ }
93
+ function InfoTooltip({
94
+ rect,
95
+ source,
96
+ componentName,
97
+ stackIndex,
98
+ stackLength,
99
+ hasParents
100
+ }) {
101
+ const viewportHeight = window.innerHeight;
102
+ const spaceAbove = rect.top;
103
+ const spaceBelow = viewportHeight - rect.bottom;
104
+ const positionAbove = spaceAbove > 100 || spaceBelow < 100;
105
+ const tooltipStyle = {
106
+ position: "fixed",
107
+ left: Math.max(8, Math.min(rect.left, window.innerWidth - 320)),
108
+ zIndex: 99999,
109
+ animation: "uilint-locator-fade-in 0.15s ease-out"
110
+ };
111
+ if (positionAbove) {
112
+ tooltipStyle.bottom = viewportHeight - rect.top + 8;
113
+ } else {
114
+ tooltipStyle.top = rect.bottom + 8;
115
+ }
116
+ return /* @__PURE__ */ jsxs(
117
+ "div",
118
+ {
119
+ style: {
120
+ ...tooltipStyle,
121
+ display: "flex",
122
+ flexDirection: "column",
123
+ gap: "6px",
124
+ padding: "10px 12px",
125
+ borderRadius: "8px",
126
+ backgroundColor: STYLES.bg,
127
+ backdropFilter: STYLES.blur,
128
+ WebkitBackdropFilter: STYLES.blur,
129
+ border: `1px solid ${STYLES.border}`,
130
+ boxShadow: STYLES.shadow,
131
+ fontFamily: STYLES.font,
132
+ maxWidth: "320px",
133
+ pointerEvents: "auto"
134
+ },
135
+ children: [
136
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
137
+ /* @__PURE__ */ jsxs(
138
+ "span",
139
+ {
140
+ style: {
141
+ fontSize: "13px",
142
+ fontWeight: 600,
143
+ color: STYLES.accent
144
+ },
145
+ children: [
146
+ "<",
147
+ componentName,
148
+ " />"
149
+ ]
150
+ }
151
+ ),
152
+ hasParents && /* @__PURE__ */ jsx(
153
+ "span",
154
+ {
155
+ style: {
156
+ fontSize: "10px",
157
+ color: STYLES.textMuted,
158
+ padding: "2px 6px",
159
+ backgroundColor: "rgba(59, 130, 246, 0.15)",
160
+ borderRadius: "4px"
161
+ },
162
+ children: stackIndex === 0 ? "element" : `parent ${stackIndex}/${stackLength}`
163
+ }
164
+ )
165
+ ] }),
166
+ source && /* @__PURE__ */ jsxs(
167
+ "div",
168
+ {
169
+ style: {
170
+ display: "flex",
171
+ alignItems: "center",
172
+ gap: "6px",
173
+ fontSize: "11px",
174
+ fontFamily: STYLES.fontMono,
175
+ color: STYLES.text
176
+ },
177
+ children: [
178
+ /* @__PURE__ */ jsx("span", { style: { opacity: 0.9 }, children: getFileName(source.fileName) }),
179
+ /* @__PURE__ */ jsx("span", { style: { color: STYLES.textMuted }, children: ":" }),
180
+ /* @__PURE__ */ jsx("span", { style: { color: STYLES.accent }, children: source.lineNumber })
181
+ ]
182
+ }
183
+ ),
184
+ /* @__PURE__ */ jsxs(
185
+ "div",
186
+ {
187
+ style: {
188
+ display: "flex",
189
+ alignItems: "center",
190
+ gap: "12px",
191
+ fontSize: "10px",
192
+ color: STYLES.textMuted,
193
+ borderTop: `1px solid rgba(75, 85, 99, 0.3)`,
194
+ paddingTop: "8px",
195
+ marginTop: "2px"
196
+ },
197
+ children: [
198
+ /* @__PURE__ */ jsx("span", { children: "Click to open" }),
199
+ hasParents && /* @__PURE__ */ jsxs(Fragment, { children: [
200
+ /* @__PURE__ */ jsx("span", { style: { opacity: 0.5 }, children: "\u2022" }),
201
+ /* @__PURE__ */ jsx("span", { children: "Scroll to navigate parents" })
202
+ ] })
203
+ ]
204
+ }
205
+ )
206
+ ]
207
+ }
208
+ );
209
+ }
210
+ function InspectedElementHighlight() {
211
+ const { inspectedElement } = useUILintContext();
212
+ const [mounted, setMounted] = useState(false);
213
+ const [rect, setRect] = useState(null);
214
+ useEffect(() => {
215
+ setMounted(true);
216
+ }, []);
217
+ useEffect(() => {
218
+ if (!inspectedElement?.element) return;
219
+ const updateRect = () => {
220
+ if (inspectedElement.element) {
221
+ setRect(inspectedElement.element.getBoundingClientRect());
222
+ }
223
+ };
224
+ updateRect();
225
+ window.addEventListener("scroll", updateRect, true);
226
+ window.addEventListener("resize", updateRect);
227
+ return () => {
228
+ window.removeEventListener("scroll", updateRect, true);
229
+ window.removeEventListener("resize", updateRect);
230
+ };
231
+ }, [inspectedElement?.element]);
232
+ if (!mounted || !inspectedElement || !rect) return null;
233
+ const content = /* @__PURE__ */ jsxs("div", { "data-ui-lint": true, style: { pointerEvents: "none" }, children: [
234
+ /* @__PURE__ */ jsx("style", { children: `
235
+ @keyframes uilint-inspected-pulse {
236
+ 0%, 100% { box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.6), 0 0 8px rgba(16, 185, 129, 0.3); }
237
+ 50% { box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.8), 0 0 16px rgba(16, 185, 129, 0.5); }
238
+ }
239
+ ` }),
240
+ /* @__PURE__ */ jsx(
241
+ "div",
242
+ {
243
+ style: {
244
+ position: "fixed",
245
+ top: rect.top - 3,
246
+ left: rect.left - 3,
247
+ width: rect.width + 6,
248
+ height: rect.height + 6,
249
+ border: "2px solid #10B981",
250
+ borderRadius: "6px",
251
+ backgroundColor: "rgba(16, 185, 129, 0.08)",
252
+ animation: "uilint-inspected-pulse 2s ease-in-out infinite",
253
+ zIndex: 99996
254
+ }
255
+ }
256
+ ),
257
+ /* @__PURE__ */ jsx(
258
+ "div",
259
+ {
260
+ style: {
261
+ position: "fixed",
262
+ top: rect.top - 24,
263
+ left: rect.left - 3,
264
+ padding: "2px 8px",
265
+ backgroundColor: "#10B981",
266
+ color: "#FFFFFF",
267
+ fontSize: "10px",
268
+ fontWeight: 600,
269
+ fontFamily: STYLES.font,
270
+ borderRadius: "4px 4px 0 0",
271
+ zIndex: 99996
272
+ },
273
+ children: "Inspecting"
274
+ }
275
+ )
276
+ ] });
277
+ return createPortal(content, document.body);
278
+ }
279
+
280
+ export {
281
+ LocatorOverlay,
282
+ InspectedElementHighlight
283
+ };