uilint-react 0.1.17 → 0.1.19

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,595 @@
1
+ "use client";
2
+ import {
3
+ useUILintContext
4
+ } from "./chunk-7WYVWDRU.js";
5
+
6
+ // src/components/ui-lint/UILintToolbar.tsx
7
+ import { useState, useRef, useEffect } 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.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
+ var MODES = [
24
+ { id: "off", label: "Off", icon: "\u25CB" },
25
+ { id: "sources", label: "Sources", icon: "\u25C9" },
26
+ { id: "inspect", label: "Inspect", icon: "\u25CE" }
27
+ ];
28
+ function UILintToolbar() {
29
+ const {
30
+ mode,
31
+ setMode,
32
+ scannedElements,
33
+ sourceFiles,
34
+ settings,
35
+ updateSettings,
36
+ rescan,
37
+ isScanning
38
+ } = useUILintContext();
39
+ const [isExpanded, setIsExpanded] = useState(false);
40
+ const [showSettings, setShowSettings] = useState(false);
41
+ const [mounted, setMounted] = useState(false);
42
+ const settingsRef = useRef(null);
43
+ useEffect(() => {
44
+ setMounted(true);
45
+ }, []);
46
+ useEffect(() => {
47
+ const handleClickOutside = (e) => {
48
+ if (settingsRef.current && !settingsRef.current.contains(e.target)) {
49
+ setShowSettings(false);
50
+ }
51
+ };
52
+ if (showSettings) {
53
+ document.addEventListener("mousedown", handleClickOutside);
54
+ return () => document.removeEventListener("mousedown", handleClickOutside);
55
+ }
56
+ }, [showSettings]);
57
+ if (!mounted) return null;
58
+ const content = /* @__PURE__ */ jsxs(
59
+ "div",
60
+ {
61
+ "data-ui-lint": true,
62
+ style: {
63
+ position: "fixed",
64
+ bottom: "24px",
65
+ left: "50%",
66
+ transform: "translateX(-50%)",
67
+ zIndex: 99999,
68
+ fontFamily: STYLES.font
69
+ },
70
+ children: [
71
+ !isExpanded && /* @__PURE__ */ jsx(
72
+ "button",
73
+ {
74
+ onClick: () => setIsExpanded(true),
75
+ style: {
76
+ display: "flex",
77
+ alignItems: "center",
78
+ justifyContent: "center",
79
+ width: "48px",
80
+ height: "48px",
81
+ borderRadius: "50%",
82
+ border: `1px solid ${STYLES.border}`,
83
+ backgroundColor: STYLES.bg,
84
+ backdropFilter: STYLES.blur,
85
+ WebkitBackdropFilter: STYLES.blur,
86
+ boxShadow: STYLES.shadow,
87
+ cursor: "pointer",
88
+ transition: "all 0.2s ease-out",
89
+ color: mode === "off" ? STYLES.textMuted : STYLES.accent,
90
+ fontSize: "20px"
91
+ },
92
+ onMouseEnter: (e) => {
93
+ e.currentTarget.style.transform = "scale(1.1)";
94
+ e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.5)";
95
+ },
96
+ onMouseLeave: (e) => {
97
+ e.currentTarget.style.transform = "scale(1)";
98
+ e.currentTarget.style.boxShadow = STYLES.shadow;
99
+ },
100
+ title: "UILint (\u2318+Shift+D)",
101
+ children: isScanning ? /* @__PURE__ */ jsx(SpinnerIcon, {}) : /* @__PURE__ */ jsx(UILintIcon, { active: mode !== "off" })
102
+ }
103
+ ),
104
+ isExpanded && /* @__PURE__ */ jsxs(
105
+ "div",
106
+ {
107
+ style: {
108
+ display: "flex",
109
+ alignItems: "center",
110
+ gap: "4px",
111
+ padding: "6px",
112
+ borderRadius: "28px",
113
+ border: `1px solid ${STYLES.border}`,
114
+ backgroundColor: STYLES.bg,
115
+ backdropFilter: STYLES.blur,
116
+ WebkitBackdropFilter: STYLES.blur,
117
+ boxShadow: STYLES.shadow,
118
+ animation: "uilint-fade-in 0.2s ease-out",
119
+ minWidth: "380px"
120
+ },
121
+ children: [
122
+ /* @__PURE__ */ jsx("style", { children: `
123
+ @keyframes uilint-fade-in {
124
+ from { opacity: 0; transform: scale(0.95); }
125
+ to { opacity: 1; transform: scale(1); }
126
+ }
127
+ @keyframes uilint-spin {
128
+ from { transform: rotate(0deg); }
129
+ to { transform: rotate(360deg); }
130
+ }
131
+ ` }),
132
+ MODES.map((m) => /* @__PURE__ */ jsx(
133
+ ModeButton,
134
+ {
135
+ mode: m,
136
+ isActive: mode === m.id,
137
+ onClick: () => setMode(m.id)
138
+ },
139
+ m.id
140
+ )),
141
+ /* @__PURE__ */ jsx(
142
+ "div",
143
+ {
144
+ style: {
145
+ width: "1px",
146
+ height: "24px",
147
+ backgroundColor: STYLES.border,
148
+ margin: "0 4px"
149
+ }
150
+ }
151
+ ),
152
+ mode !== "off" && /* @__PURE__ */ jsx(
153
+ "div",
154
+ {
155
+ style: {
156
+ display: "flex",
157
+ alignItems: "center",
158
+ justifyContent: "center",
159
+ gap: "6px",
160
+ padding: "6px 10px",
161
+ borderRadius: "16px",
162
+ backgroundColor: "rgba(59, 130, 246, 0.15)",
163
+ fontSize: "12px",
164
+ color: STYLES.accent,
165
+ fontWeight: 500,
166
+ minWidth: "120px"
167
+ },
168
+ children: isScanning ? /* @__PURE__ */ jsxs(Fragment, { children: [
169
+ /* @__PURE__ */ jsx(SpinnerIcon, { size: 12 }),
170
+ /* @__PURE__ */ jsx("span", { children: "Scanning..." })
171
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
172
+ /* @__PURE__ */ jsxs("span", { children: [
173
+ sourceFiles.length,
174
+ " files"
175
+ ] }),
176
+ /* @__PURE__ */ jsx("span", { style: { color: STYLES.textMuted }, children: "\u2022" }),
177
+ /* @__PURE__ */ jsxs("span", { children: [
178
+ scannedElements.length,
179
+ " elements"
180
+ ] })
181
+ ] })
182
+ }
183
+ ),
184
+ mode !== "off" && /* @__PURE__ */ jsx(
185
+ "button",
186
+ {
187
+ onClick: rescan,
188
+ disabled: isScanning,
189
+ style: {
190
+ display: "flex",
191
+ alignItems: "center",
192
+ justifyContent: "center",
193
+ width: "32px",
194
+ height: "32px",
195
+ borderRadius: "50%",
196
+ border: "none",
197
+ backgroundColor: "transparent",
198
+ color: STYLES.textMuted,
199
+ cursor: isScanning ? "not-allowed" : "pointer",
200
+ opacity: isScanning ? 0.5 : 1,
201
+ transition: "all 0.15s"
202
+ },
203
+ onMouseEnter: (e) => {
204
+ if (!isScanning) {
205
+ e.currentTarget.style.backgroundColor = STYLES.bgHover;
206
+ e.currentTarget.style.color = STYLES.text;
207
+ }
208
+ },
209
+ onMouseLeave: (e) => {
210
+ e.currentTarget.style.backgroundColor = "transparent";
211
+ e.currentTarget.style.color = STYLES.textMuted;
212
+ },
213
+ title: "Rescan page",
214
+ children: /* @__PURE__ */ jsx(RefreshIcon, {})
215
+ }
216
+ ),
217
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, ref: settingsRef, children: [
218
+ /* @__PURE__ */ jsx(
219
+ "button",
220
+ {
221
+ onClick: () => setShowSettings(!showSettings),
222
+ style: {
223
+ display: "flex",
224
+ alignItems: "center",
225
+ justifyContent: "center",
226
+ width: "32px",
227
+ height: "32px",
228
+ borderRadius: "50%",
229
+ border: "none",
230
+ backgroundColor: showSettings ? STYLES.bgHover : "transparent",
231
+ color: showSettings ? STYLES.text : STYLES.textMuted,
232
+ cursor: "pointer",
233
+ transition: "all 0.15s"
234
+ },
235
+ onMouseEnter: (e) => {
236
+ e.currentTarget.style.backgroundColor = STYLES.bgHover;
237
+ e.currentTarget.style.color = STYLES.text;
238
+ },
239
+ onMouseLeave: (e) => {
240
+ if (!showSettings) {
241
+ e.currentTarget.style.backgroundColor = "transparent";
242
+ e.currentTarget.style.color = STYLES.textMuted;
243
+ }
244
+ },
245
+ title: "Settings",
246
+ children: /* @__PURE__ */ jsx(SettingsIcon, {})
247
+ }
248
+ ),
249
+ showSettings && /* @__PURE__ */ jsx(
250
+ SettingsPopover,
251
+ {
252
+ settings,
253
+ onUpdate: updateSettings
254
+ }
255
+ )
256
+ ] }),
257
+ /* @__PURE__ */ jsx(
258
+ "button",
259
+ {
260
+ onClick: () => setIsExpanded(false),
261
+ style: {
262
+ display: "flex",
263
+ alignItems: "center",
264
+ justifyContent: "center",
265
+ width: "32px",
266
+ height: "32px",
267
+ borderRadius: "50%",
268
+ border: "none",
269
+ backgroundColor: "transparent",
270
+ color: STYLES.textMuted,
271
+ cursor: "pointer",
272
+ transition: "all 0.15s"
273
+ },
274
+ onMouseEnter: (e) => {
275
+ e.currentTarget.style.backgroundColor = STYLES.bgHover;
276
+ e.currentTarget.style.color = STYLES.text;
277
+ },
278
+ onMouseLeave: (e) => {
279
+ e.currentTarget.style.backgroundColor = "transparent";
280
+ e.currentTarget.style.color = STYLES.textMuted;
281
+ },
282
+ title: "Collapse",
283
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
284
+ }
285
+ )
286
+ ]
287
+ }
288
+ ),
289
+ !isExpanded && mode === "off" && /* @__PURE__ */ jsx(
290
+ "div",
291
+ {
292
+ style: {
293
+ position: "absolute",
294
+ top: "100%",
295
+ left: "50%",
296
+ transform: "translateX(-50%)",
297
+ marginTop: "8px",
298
+ padding: "4px 8px",
299
+ borderRadius: "4px",
300
+ backgroundColor: STYLES.bg,
301
+ border: `1px solid ${STYLES.border}`,
302
+ fontSize: "11px",
303
+ color: STYLES.textMuted,
304
+ whiteSpace: "nowrap",
305
+ opacity: 0,
306
+ transition: "opacity 0.2s",
307
+ pointerEvents: "none"
308
+ },
309
+ className: "uilint-hint",
310
+ children: "\u2318+Shift+D"
311
+ }
312
+ )
313
+ ]
314
+ }
315
+ );
316
+ return createPortal(content, document.body);
317
+ }
318
+ function ModeButton({
319
+ mode,
320
+ isActive,
321
+ onClick
322
+ }) {
323
+ return /* @__PURE__ */ jsxs(
324
+ "button",
325
+ {
326
+ onClick,
327
+ style: {
328
+ display: "flex",
329
+ alignItems: "center",
330
+ gap: "6px",
331
+ padding: "8px 14px",
332
+ borderRadius: "20px",
333
+ border: "none",
334
+ backgroundColor: isActive ? STYLES.accent : "transparent",
335
+ color: isActive ? "#FFFFFF" : STYLES.textMuted,
336
+ fontSize: "13px",
337
+ fontWeight: 500,
338
+ cursor: "pointer",
339
+ transition: "all 0.15s ease-out",
340
+ fontFamily: STYLES.font
341
+ },
342
+ onMouseEnter: (e) => {
343
+ if (!isActive) {
344
+ e.currentTarget.style.backgroundColor = STYLES.bgHover;
345
+ e.currentTarget.style.color = STYLES.text;
346
+ }
347
+ },
348
+ onMouseLeave: (e) => {
349
+ if (!isActive) {
350
+ e.currentTarget.style.backgroundColor = "transparent";
351
+ e.currentTarget.style.color = STYLES.textMuted;
352
+ }
353
+ },
354
+ children: [
355
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "14px" }, children: mode.icon }),
356
+ /* @__PURE__ */ jsx("span", { children: mode.label })
357
+ ]
358
+ }
359
+ );
360
+ }
361
+ function SettingsPopover({
362
+ settings,
363
+ onUpdate
364
+ }) {
365
+ return /* @__PURE__ */ jsxs(
366
+ "div",
367
+ {
368
+ style: {
369
+ position: "absolute",
370
+ bottom: "100%",
371
+ right: 0,
372
+ marginBottom: "8px",
373
+ width: "280px",
374
+ padding: "16px",
375
+ borderRadius: "12px",
376
+ border: `1px solid ${STYLES.border}`,
377
+ backgroundColor: STYLES.bg,
378
+ backdropFilter: STYLES.blur,
379
+ WebkitBackdropFilter: STYLES.blur,
380
+ boxShadow: STYLES.shadow,
381
+ animation: "uilint-fade-in 0.15s ease-out"
382
+ },
383
+ children: [
384
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "13px", fontWeight: 600, color: STYLES.text, marginBottom: "12px" }, children: "Settings" }),
385
+ /* @__PURE__ */ jsx(
386
+ SettingToggle,
387
+ {
388
+ label: "Show file labels",
389
+ checked: settings.showLabels,
390
+ onChange: (checked) => onUpdate({ showLabels: checked })
391
+ }
392
+ ),
393
+ /* @__PURE__ */ jsx(
394
+ SettingToggle,
395
+ {
396
+ label: "Hide node_modules",
397
+ checked: settings.hideNodeModules,
398
+ onChange: (checked) => onUpdate({ hideNodeModules: checked })
399
+ }
400
+ ),
401
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "12px" }, children: [
402
+ /* @__PURE__ */ jsx(
403
+ "div",
404
+ {
405
+ style: {
406
+ fontSize: "12px",
407
+ color: STYLES.textMuted,
408
+ marginBottom: "6px"
409
+ },
410
+ children: "Label position"
411
+ }
412
+ ),
413
+ /* @__PURE__ */ jsxs(
414
+ "select",
415
+ {
416
+ value: settings.labelPosition,
417
+ onChange: (e) => onUpdate({ labelPosition: e.target.value }),
418
+ style: {
419
+ width: "100%",
420
+ padding: "6px 8px",
421
+ borderRadius: "6px",
422
+ border: `1px solid ${STYLES.border}`,
423
+ backgroundColor: STYLES.bgHover,
424
+ color: STYLES.text,
425
+ fontSize: "12px",
426
+ cursor: "pointer"
427
+ },
428
+ children: [
429
+ /* @__PURE__ */ jsx("option", { value: "top-left", children: "Top Left" }),
430
+ /* @__PURE__ */ jsx("option", { value: "top-right", children: "Top Right" }),
431
+ /* @__PURE__ */ jsx("option", { value: "bottom-left", children: "Bottom Left" }),
432
+ /* @__PURE__ */ jsx("option", { value: "bottom-right", children: "Bottom Right" })
433
+ ]
434
+ }
435
+ )
436
+ ] })
437
+ ]
438
+ }
439
+ );
440
+ }
441
+ function SettingToggle({
442
+ label,
443
+ checked,
444
+ onChange
445
+ }) {
446
+ return /* @__PURE__ */ jsxs(
447
+ "label",
448
+ {
449
+ style: {
450
+ display: "flex",
451
+ alignItems: "center",
452
+ justifyContent: "space-between",
453
+ padding: "8px 0",
454
+ cursor: "pointer"
455
+ },
456
+ children: [
457
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: STYLES.textMuted }, children: label }),
458
+ /* @__PURE__ */ jsx(
459
+ "div",
460
+ {
461
+ onClick: () => onChange(!checked),
462
+ style: {
463
+ width: "36px",
464
+ height: "20px",
465
+ borderRadius: "10px",
466
+ backgroundColor: checked ? STYLES.accent : "rgba(75, 85, 99, 0.5)",
467
+ position: "relative",
468
+ transition: "background-color 0.2s"
469
+ },
470
+ children: /* @__PURE__ */ jsx(
471
+ "div",
472
+ {
473
+ style: {
474
+ position: "absolute",
475
+ top: "2px",
476
+ left: checked ? "18px" : "2px",
477
+ width: "16px",
478
+ height: "16px",
479
+ borderRadius: "50%",
480
+ backgroundColor: "#FFFFFF",
481
+ transition: "left 0.2s",
482
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.2)"
483
+ }
484
+ }
485
+ )
486
+ }
487
+ )
488
+ ]
489
+ }
490
+ );
491
+ }
492
+ function UILintIcon({ active }) {
493
+ return /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [
494
+ /* @__PURE__ */ jsx(
495
+ "rect",
496
+ {
497
+ x: "3",
498
+ y: "3",
499
+ width: "18",
500
+ height: "18",
501
+ rx: "3",
502
+ stroke: active ? STYLES.accent : "currentColor",
503
+ strokeWidth: "2"
504
+ }
505
+ ),
506
+ /* @__PURE__ */ jsx(
507
+ "path",
508
+ {
509
+ d: "M7 12h10M12 7v10",
510
+ stroke: active ? STYLES.accent : "currentColor",
511
+ strokeWidth: "2",
512
+ strokeLinecap: "round"
513
+ }
514
+ )
515
+ ] });
516
+ }
517
+ function SpinnerIcon({ size = 16 }) {
518
+ return /* @__PURE__ */ jsx(
519
+ "svg",
520
+ {
521
+ width: size,
522
+ height: size,
523
+ viewBox: "0 0 24 24",
524
+ fill: "none",
525
+ style: { animation: "uilint-spin 1s linear infinite" },
526
+ children: /* @__PURE__ */ jsx(
527
+ "circle",
528
+ {
529
+ cx: "12",
530
+ cy: "12",
531
+ r: "10",
532
+ stroke: "currentColor",
533
+ strokeWidth: "3",
534
+ strokeLinecap: "round",
535
+ strokeDasharray: "31.4 31.4",
536
+ fill: "none"
537
+ }
538
+ )
539
+ }
540
+ );
541
+ }
542
+ function RefreshIcon() {
543
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
544
+ /* @__PURE__ */ jsx(
545
+ "path",
546
+ {
547
+ d: "M21 12a9 9 0 11-9-9c2.52 0 4.93 1 6.74 2.74L21 8",
548
+ stroke: "currentColor",
549
+ strokeWidth: "2",
550
+ strokeLinecap: "round",
551
+ strokeLinejoin: "round"
552
+ }
553
+ ),
554
+ /* @__PURE__ */ jsx(
555
+ "path",
556
+ {
557
+ d: "M21 3v5h-5",
558
+ stroke: "currentColor",
559
+ strokeWidth: "2",
560
+ strokeLinecap: "round",
561
+ strokeLinejoin: "round"
562
+ }
563
+ )
564
+ ] });
565
+ }
566
+ function SettingsIcon() {
567
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
568
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3", stroke: "currentColor", strokeWidth: "2" }),
569
+ /* @__PURE__ */ jsx(
570
+ "path",
571
+ {
572
+ d: "M12 1v2m0 18v2M4.22 4.22l1.42 1.42m12.72 12.72l1.42 1.42M1 12h2m18 0h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42",
573
+ stroke: "currentColor",
574
+ strokeWidth: "2",
575
+ strokeLinecap: "round"
576
+ }
577
+ )
578
+ ] });
579
+ }
580
+ function ChevronDownIcon() {
581
+ return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
582
+ "path",
583
+ {
584
+ d: "M6 9l6 6 6-6",
585
+ stroke: "currentColor",
586
+ strokeWidth: "2",
587
+ strokeLinecap: "round",
588
+ strokeLinejoin: "round"
589
+ }
590
+ ) });
591
+ }
592
+
593
+ export {
594
+ UILintToolbar
595
+ };