uilint-react 0.1.24 → 0.1.26

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.
@@ -1,1359 +0,0 @@
1
- "use client";
2
- import {
3
- buildEditorUrl,
4
- useUILintContext
5
- } from "./chunk-I4C3NAUH.js";
6
-
7
- // src/components/ui-lint/InspectionPanel.tsx
8
- import { useState, useEffect, useCallback, useMemo } from "react";
9
- import { createPortal } from "react-dom";
10
-
11
- // src/components/ui-lint/source-fetcher.ts
12
- var sourceCache = /* @__PURE__ */ new Map();
13
- var CACHE_TTL = 5 * 60 * 1e3;
14
- var API_ENDPOINT = "/api/.uilint/source";
15
- async function fetchSource(filePath) {
16
- const cached = sourceCache.get(filePath);
17
- if (cached && Date.now() - cached.fetchedAt < CACHE_TTL) {
18
- return {
19
- content: cached.content,
20
- relativePath: cached.relativePath
21
- };
22
- }
23
- try {
24
- const response = await fetch(
25
- `${API_ENDPOINT}?path=${encodeURIComponent(filePath)}`
26
- );
27
- if (!response.ok) {
28
- console.warn(`[UILint] Failed to fetch source: ${response.statusText}`);
29
- return null;
30
- }
31
- const data = await response.json();
32
- sourceCache.set(filePath, {
33
- ...data,
34
- fetchedAt: Date.now()
35
- });
36
- return data;
37
- } catch (error) {
38
- console.error("[UILint] Error fetching source:", error);
39
- return null;
40
- }
41
- }
42
- async function fetchSourceWithContext(source, contextLines = 5) {
43
- const result = await fetchSource(source.fileName);
44
- if (!result) return null;
45
- const allLines = result.content.split("\n");
46
- const targetLine = source.lineNumber - 1;
47
- const startLine = Math.max(0, targetLine - contextLines);
48
- const endLine = Math.min(allLines.length, targetLine + contextLines + 1);
49
- return {
50
- lines: allLines.slice(startLine, endLine),
51
- startLine: startLine + 1,
52
- // Back to 1-indexed
53
- highlightLine: source.lineNumber,
54
- relativePath: result.relativePath
55
- };
56
- }
57
- function clearSourceCache() {
58
- sourceCache.clear();
59
- }
60
- function getCachedSource(filePath) {
61
- const cached = sourceCache.get(filePath);
62
- if (!cached) return null;
63
- if (Date.now() - cached.fetchedAt >= CACHE_TTL) {
64
- sourceCache.delete(filePath);
65
- return null;
66
- }
67
- return {
68
- content: cached.content,
69
- relativePath: cached.relativePath
70
- };
71
- }
72
- async function prefetchSources(filePaths) {
73
- const uniquePaths = [...new Set(filePaths)].filter((path) => {
74
- const cached = sourceCache.get(path);
75
- return !cached || Date.now() - cached.fetchedAt >= CACHE_TTL;
76
- });
77
- const BATCH_SIZE = 5;
78
- for (let i = 0; i < uniquePaths.length; i += BATCH_SIZE) {
79
- const batch = uniquePaths.slice(i, i + BATCH_SIZE);
80
- await Promise.all(batch.map(fetchSource));
81
- }
82
- }
83
-
84
- // src/components/ui-lint/InspectionPanel.tsx
85
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
86
- var STYLES = {
87
- bg: "rgba(17, 24, 39, 0.95)",
88
- bgSurface: "rgba(31, 41, 55, 0.9)",
89
- border: "rgba(75, 85, 99, 0.5)",
90
- text: "#F9FAFB",
91
- textMuted: "#9CA3AF",
92
- textDim: "#6B7280",
93
- accent: "#3B82F6",
94
- accentHover: "#2563EB",
95
- success: "#10B981",
96
- warning: "#F59E0B",
97
- shadow: "0 -8px 32px rgba(0, 0, 0, 0.4)",
98
- blur: "blur(16px)",
99
- font: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
100
- fontMono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace'
101
- };
102
- var PANEL_WIDTH = 420;
103
- function InspectionPanel() {
104
- const { inspectedElement, setInspectedElement } = useUILintContext();
105
- const [mounted, setMounted] = useState(false);
106
- const [activeTab, setActiveTab] = useState(
107
- "info"
108
- );
109
- useEffect(() => {
110
- setMounted(true);
111
- }, []);
112
- if (!mounted || !inspectedElement) return null;
113
- const content = /* @__PURE__ */ jsxs(
114
- "div",
115
- {
116
- "data-ui-lint": true,
117
- style: {
118
- position: "fixed",
119
- top: 0,
120
- right: 0,
121
- bottom: 0,
122
- width: PANEL_WIDTH,
123
- backgroundColor: STYLES.bg,
124
- backdropFilter: STYLES.blur,
125
- WebkitBackdropFilter: STYLES.blur,
126
- borderLeft: `1px solid ${STYLES.border}`,
127
- boxShadow: STYLES.shadow,
128
- fontFamily: STYLES.font,
129
- color: STYLES.text,
130
- overflow: "hidden",
131
- zIndex: 99998
132
- },
133
- children: [
134
- /* @__PURE__ */ jsx("style", { children: `
135
- @keyframes uilint-panel-slide-in {
136
- from { transform: translateX(100%); }
137
- to { transform: translateX(0); }
138
- }
139
- @keyframes uilint-spin {
140
- from { transform: rotate(0deg); }
141
- to { transform: rotate(360deg); }
142
- }
143
- ` }),
144
- /* @__PURE__ */ jsxs(
145
- "div",
146
- {
147
- style: {
148
- display: "flex",
149
- flexDirection: "column",
150
- height: "100%",
151
- animation: "uilint-panel-slide-in 0.2s ease-out"
152
- },
153
- children: [
154
- /* @__PURE__ */ jsx(
155
- PanelHeader,
156
- {
157
- element: inspectedElement,
158
- onClose: () => setInspectedElement(null)
159
- }
160
- ),
161
- /* @__PURE__ */ jsxs(
162
- "div",
163
- {
164
- style: {
165
- display: "flex",
166
- borderBottom: `1px solid ${STYLES.border}`
167
- },
168
- children: [
169
- /* @__PURE__ */ jsx(
170
- TabButton,
171
- {
172
- label: "Info",
173
- active: activeTab === "info",
174
- onClick: () => setActiveTab("info")
175
- }
176
- ),
177
- /* @__PURE__ */ jsx(
178
- TabButton,
179
- {
180
- label: "Source",
181
- active: activeTab === "source",
182
- onClick: () => setActiveTab("source")
183
- }
184
- ),
185
- /* @__PURE__ */ jsx(
186
- TabButton,
187
- {
188
- label: "Scan",
189
- active: activeTab === "scan",
190
- onClick: () => setActiveTab("scan"),
191
- accent: true
192
- }
193
- )
194
- ]
195
- }
196
- ),
197
- /* @__PURE__ */ jsxs("div", { style: { flex: 1, overflow: "auto" }, children: [
198
- activeTab === "info" && /* @__PURE__ */ jsx(InfoTab, { element: inspectedElement }),
199
- activeTab === "source" && /* @__PURE__ */ jsx(SourceTab, { element: inspectedElement }),
200
- activeTab === "scan" && /* @__PURE__ */ jsx(ScanTab, { element: inspectedElement })
201
- ] })
202
- ]
203
- }
204
- )
205
- ]
206
- }
207
- );
208
- return createPortal(content, document.body);
209
- }
210
- function PanelHeader({
211
- element,
212
- onClose
213
- }) {
214
- const componentName = element.componentStack[0]?.name || element.element.tagName.toLowerCase();
215
- const handleOpenInCursor = useCallback(() => {
216
- if (element.source) {
217
- const url = buildEditorUrl(element.source, "cursor");
218
- window.open(url, "_blank");
219
- }
220
- }, [element.source]);
221
- return /* @__PURE__ */ jsxs(
222
- "div",
223
- {
224
- style: {
225
- display: "flex",
226
- alignItems: "center",
227
- justifyContent: "space-between",
228
- padding: "12px 16px",
229
- borderBottom: `1px solid ${STYLES.border}`,
230
- backgroundColor: STYLES.bgSurface
231
- },
232
- children: [
233
- /* @__PURE__ */ jsxs("div", { children: [
234
- /* @__PURE__ */ jsx("div", { style: { fontSize: "14px", fontWeight: 600 }, children: componentName }),
235
- /* @__PURE__ */ jsxs("div", { style: { fontSize: "11px", color: STYLES.textMuted }, children: [
236
- "<",
237
- element.element.tagName.toLowerCase(),
238
- ">"
239
- ] })
240
- ] }),
241
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
242
- element.source && /* @__PURE__ */ jsxs(
243
- "button",
244
- {
245
- onClick: handleOpenInCursor,
246
- style: {
247
- display: "flex",
248
- alignItems: "center",
249
- gap: "4px",
250
- padding: "6px 10px",
251
- borderRadius: "6px",
252
- border: "none",
253
- backgroundColor: STYLES.accent,
254
- color: "#FFFFFF",
255
- fontSize: "11px",
256
- fontWeight: 500,
257
- cursor: "pointer",
258
- transition: "background-color 0.15s"
259
- },
260
- onMouseEnter: (e) => {
261
- e.currentTarget.style.backgroundColor = STYLES.accentHover;
262
- },
263
- onMouseLeave: (e) => {
264
- e.currentTarget.style.backgroundColor = STYLES.accent;
265
- },
266
- title: "Open in Cursor",
267
- children: [
268
- /* @__PURE__ */ jsx(CursorIcon, {}),
269
- "Open in Cursor"
270
- ]
271
- }
272
- ),
273
- /* @__PURE__ */ jsx(
274
- "button",
275
- {
276
- onClick: onClose,
277
- style: {
278
- display: "flex",
279
- alignItems: "center",
280
- justifyContent: "center",
281
- width: "28px",
282
- height: "28px",
283
- borderRadius: "6px",
284
- border: "none",
285
- backgroundColor: "transparent",
286
- color: STYLES.textMuted,
287
- cursor: "pointer",
288
- transition: "all 0.15s"
289
- },
290
- onMouseEnter: (e) => {
291
- e.currentTarget.style.backgroundColor = STYLES.bgSurface;
292
- e.currentTarget.style.color = STYLES.text;
293
- },
294
- onMouseLeave: (e) => {
295
- e.currentTarget.style.backgroundColor = "transparent";
296
- e.currentTarget.style.color = STYLES.textMuted;
297
- },
298
- children: /* @__PURE__ */ jsx(CloseIcon, {})
299
- }
300
- )
301
- ] })
302
- ]
303
- }
304
- );
305
- }
306
- function TabButton({
307
- label,
308
- active,
309
- onClick,
310
- accent
311
- }) {
312
- return /* @__PURE__ */ jsx(
313
- "button",
314
- {
315
- onClick,
316
- style: {
317
- flex: 1,
318
- padding: "10px 16px",
319
- border: "none",
320
- backgroundColor: "transparent",
321
- color: active ? accent ? STYLES.success : STYLES.accent : STYLES.textMuted,
322
- fontSize: "12px",
323
- fontWeight: 500,
324
- cursor: "pointer",
325
- borderBottom: active ? `2px solid ${accent ? STYLES.success : STYLES.accent}` : "2px solid transparent",
326
- marginBottom: "-1px",
327
- transition: "all 0.15s"
328
- },
329
- children: label
330
- }
331
- );
332
- }
333
- function InfoTab({ element }) {
334
- return /* @__PURE__ */ jsxs("div", { style: { padding: "16px" }, children: [
335
- /* @__PURE__ */ jsxs(Section, { title: "Element", children: [
336
- /* @__PURE__ */ jsx(
337
- InfoRow,
338
- {
339
- label: "Tag",
340
- value: `<${element.element.tagName.toLowerCase()}>`
341
- }
342
- ),
343
- element.element.className && /* @__PURE__ */ jsx(
344
- InfoRow,
345
- {
346
- label: "Classes",
347
- value: typeof element.element.className === "string" ? element.element.className : "",
348
- mono: true
349
- }
350
- ),
351
- element.source && /* @__PURE__ */ jsx(
352
- InfoRow,
353
- {
354
- label: "Location",
355
- value: `Line ${element.source.lineNumber}${element.source.columnNumber ? `, Col ${element.source.columnNumber}` : ""}`
356
- }
357
- )
358
- ] }),
359
- element.source && /* @__PURE__ */ jsx(Section, { title: "Source File", children: /* @__PURE__ */ jsx(
360
- "div",
361
- {
362
- style: {
363
- fontSize: "11px",
364
- color: STYLES.textDim,
365
- fontFamily: STYLES.fontMono,
366
- wordBreak: "break-all"
367
- },
368
- children: element.source.fileName
369
- }
370
- ) }),
371
- element.componentStack.length > 0 && /* @__PURE__ */ jsx(Section, { title: "Component Stack", children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "4px" }, children: [
372
- element.componentStack.slice(0, 10).map((comp, index) => /* @__PURE__ */ jsx(
373
- ComponentStackItem,
374
- {
375
- component: comp,
376
- index,
377
- isFirst: index === 0
378
- },
379
- index
380
- )),
381
- element.componentStack.length > 10 && /* @__PURE__ */ jsxs(
382
- "div",
383
- {
384
- style: {
385
- fontSize: "11px",
386
- color: STYLES.textDim,
387
- marginTop: "4px"
388
- },
389
- children: [
390
- "...and ",
391
- element.componentStack.length - 10,
392
- " more"
393
- ]
394
- }
395
- )
396
- ] }) }),
397
- /* @__PURE__ */ jsxs(Section, { title: "Dimensions", children: [
398
- /* @__PURE__ */ jsx(
399
- InfoRow,
400
- {
401
- label: "Size",
402
- value: `${Math.round(element.rect.width)} \xD7 ${Math.round(
403
- element.rect.height
404
- )}px`
405
- }
406
- ),
407
- /* @__PURE__ */ jsx(
408
- InfoRow,
409
- {
410
- label: "Position",
411
- value: `(${Math.round(element.rect.left)}, ${Math.round(
412
- element.rect.top
413
- )})`
414
- }
415
- )
416
- ] })
417
- ] });
418
- }
419
- function SourceTab({ element }) {
420
- const [sourceData, setSourceData] = useState(null);
421
- const [loading, setLoading] = useState(false);
422
- const [error, setError] = useState(null);
423
- useEffect(() => {
424
- if (!element.source) {
425
- setError("No source information available");
426
- return;
427
- }
428
- setLoading(true);
429
- setError(null);
430
- fetchSourceWithContext(element.source, 8).then((data) => {
431
- if (data) {
432
- setSourceData(data);
433
- } else {
434
- setError("Failed to load source file");
435
- }
436
- }).catch(() => {
437
- setError("Failed to load source file");
438
- }).finally(() => {
439
- setLoading(false);
440
- });
441
- }, [element.source]);
442
- if (loading) {
443
- return /* @__PURE__ */ jsx(
444
- "div",
445
- {
446
- style: {
447
- display: "flex",
448
- alignItems: "center",
449
- justifyContent: "center",
450
- padding: "48px",
451
- color: STYLES.textMuted,
452
- fontSize: "13px"
453
- },
454
- children: "Loading source..."
455
- }
456
- );
457
- }
458
- if (error) {
459
- return /* @__PURE__ */ jsx(
460
- "div",
461
- {
462
- style: {
463
- display: "flex",
464
- alignItems: "center",
465
- justifyContent: "center",
466
- padding: "48px",
467
- color: STYLES.textMuted,
468
- fontSize: "13px"
469
- },
470
- children: error
471
- }
472
- );
473
- }
474
- if (!sourceData) return null;
475
- return /* @__PURE__ */ jsxs("div", { style: { padding: "12px" }, children: [
476
- /* @__PURE__ */ jsx(
477
- "div",
478
- {
479
- style: {
480
- padding: "8px 12px",
481
- marginBottom: "8px",
482
- backgroundColor: STYLES.bgSurface,
483
- borderRadius: "6px",
484
- fontSize: "11px",
485
- fontFamily: STYLES.fontMono,
486
- color: STYLES.textMuted,
487
- wordBreak: "break-all"
488
- },
489
- children: sourceData.relativePath
490
- }
491
- ),
492
- /* @__PURE__ */ jsx(
493
- "div",
494
- {
495
- style: {
496
- backgroundColor: STYLES.bgSurface,
497
- borderRadius: "8px",
498
- overflow: "hidden",
499
- border: `1px solid ${STYLES.border}`
500
- },
501
- children: /* @__PURE__ */ jsx(
502
- "pre",
503
- {
504
- style: {
505
- margin: 0,
506
- padding: "12px 0",
507
- overflow: "auto",
508
- fontSize: "12px",
509
- lineHeight: "1.6",
510
- fontFamily: STYLES.fontMono
511
- },
512
- children: sourceData.lines.map((line, index) => {
513
- const lineNumber = sourceData.startLine + index;
514
- const isHighlight = lineNumber === sourceData.highlightLine;
515
- return /* @__PURE__ */ jsxs(
516
- "div",
517
- {
518
- style: {
519
- display: "flex",
520
- backgroundColor: isHighlight ? "rgba(59, 130, 246, 0.2)" : "transparent",
521
- borderLeft: isHighlight ? `3px solid ${STYLES.accent}` : "3px solid transparent"
522
- },
523
- children: [
524
- /* @__PURE__ */ jsx(
525
- "span",
526
- {
527
- style: {
528
- display: "inline-block",
529
- width: "48px",
530
- paddingRight: "12px",
531
- textAlign: "right",
532
- color: isHighlight ? STYLES.accent : STYLES.textDim,
533
- userSelect: "none",
534
- flexShrink: 0
535
- },
536
- children: lineNumber
537
- }
538
- ),
539
- /* @__PURE__ */ jsx(
540
- "code",
541
- {
542
- style: {
543
- color: isHighlight ? STYLES.text : STYLES.textMuted,
544
- whiteSpace: "pre"
545
- },
546
- children: line || " "
547
- }
548
- )
549
- ]
550
- },
551
- lineNumber
552
- );
553
- })
554
- }
555
- )
556
- }
557
- )
558
- ] });
559
- }
560
- function ScanTab({ element }) {
561
- const { elementIssuesCache, autoScanState } = useUILintContext();
562
- const [scanning, setScanning] = useState(false);
563
- const [error, setError] = useState(null);
564
- const [fixPrompt, setFixPrompt] = useState(null);
565
- const [copied, setCopied] = useState(false);
566
- const componentName = element.componentStack[0]?.name || element.element.tagName.toLowerCase();
567
- const componentLine = element.source?.lineNumber;
568
- const cachedIssue = useMemo(() => {
569
- if (element.scannedElementId) {
570
- const cached = elementIssuesCache.get(element.scannedElementId);
571
- if (cached) return cached;
572
- }
573
- if (element.source) {
574
- for (const [, issue] of elementIssuesCache) {
575
- const scannedElement = autoScanState.elements.find(
576
- (el) => el.id === issue.elementId
577
- );
578
- if (scannedElement?.source?.fileName === element.source.fileName) {
579
- return issue;
580
- }
581
- }
582
- }
583
- return null;
584
- }, [
585
- element.scannedElementId,
586
- element.source,
587
- elementIssuesCache,
588
- autoScanState.elements
589
- ]);
590
- const generateFixPrompt = useCallback(
591
- (issues, relativePath) => {
592
- if (issues.length === 0) {
593
- return `No style issues found in the \`${componentName}\` component in \`${relativePath}\`. The component appears to follow the styleguide.`;
594
- }
595
- const issueList = issues.map((issue) => {
596
- const lineInfo = issue.line ? `Line ${issue.line}: ` : "";
597
- return `- ${lineInfo}${issue.message}`;
598
- }).join("\n");
599
- return `Fix the following style issues in the \`${componentName}\` component in \`${relativePath}\`:
600
-
601
- Issues found:
602
- ${issueList}
603
-
604
- Please update this component to match our styleguide.`;
605
- },
606
- [componentName]
607
- );
608
- const cachedFixPrompt = useMemo(() => {
609
- if (!cachedIssue || cachedIssue.status !== "complete") return null;
610
- const relativePath = element.source?.fileName || "unknown";
611
- return generateFixPrompt(cachedIssue.issues, relativePath);
612
- }, [cachedIssue, element.source, generateFixPrompt]);
613
- const handleScan = useCallback(async () => {
614
- if (!element.source) {
615
- setError("No source information available");
616
- return;
617
- }
618
- setScanning(true);
619
- setError(null);
620
- setFixPrompt(null);
621
- try {
622
- const sourceResponse = await fetch(
623
- `/api/.uilint/source?path=${encodeURIComponent(
624
- element.source.fileName
625
- )}`
626
- );
627
- if (!sourceResponse.ok) {
628
- throw new Error("Failed to fetch source code");
629
- }
630
- const sourceData = await sourceResponse.json();
631
- const sourceCode = sourceData.content;
632
- const relativePath = sourceData.relativePath || element.source.fileName;
633
- const analyzeResponse = await fetch("/api/.uilint/analyze", {
634
- method: "POST",
635
- headers: { "Content-Type": "application/json" },
636
- body: JSON.stringify({
637
- sourceCode,
638
- filePath: relativePath,
639
- componentName,
640
- componentLine
641
- })
642
- });
643
- if (!analyzeResponse.ok) {
644
- throw new Error("Failed to analyze source code");
645
- }
646
- const result = await analyzeResponse.json();
647
- const issues = result.issues || [];
648
- setFixPrompt(generateFixPrompt(issues, relativePath));
649
- } catch (err) {
650
- setError(
651
- err instanceof Error ? err.message : "An error occurred during scanning"
652
- );
653
- } finally {
654
- setScanning(false);
655
- }
656
- }, [element.source, componentName, componentLine, generateFixPrompt]);
657
- const handleCopy = useCallback(async (text) => {
658
- try {
659
- await navigator.clipboard.writeText(text);
660
- setCopied(true);
661
- setTimeout(() => setCopied(false), 2e3);
662
- } catch {
663
- setError("Failed to copy to clipboard");
664
- }
665
- }, []);
666
- const showCachedScanning = cachedIssue?.status === "scanning" && !scanning && !fixPrompt;
667
- const showCachedPending = cachedIssue?.status === "pending" && !scanning && !fixPrompt;
668
- const showCachedError = cachedIssue?.status === "error" && !scanning && !fixPrompt;
669
- const showCachedResult = cachedFixPrompt && !fixPrompt && !scanning;
670
- const showScanButton = !cachedIssue && !fixPrompt && !scanning;
671
- const showManualResult = fixPrompt && !scanning;
672
- return /* @__PURE__ */ jsxs("div", { style: { padding: "16px" }, children: [
673
- showCachedScanning && /* @__PURE__ */ jsxs(
674
- "div",
675
- {
676
- style: {
677
- display: "flex",
678
- flexDirection: "column",
679
- alignItems: "center",
680
- justifyContent: "center",
681
- padding: "48px 24px",
682
- gap: "16px"
683
- },
684
- children: [
685
- /* @__PURE__ */ jsx(
686
- "div",
687
- {
688
- style: {
689
- width: "32px",
690
- height: "32px",
691
- border: `3px solid ${STYLES.border}`,
692
- borderTopColor: STYLES.success,
693
- borderRadius: "50%",
694
- animation: "uilint-spin 1s linear infinite"
695
- }
696
- }
697
- ),
698
- /* @__PURE__ */ jsx("div", { style: { color: STYLES.textMuted, fontSize: "13px" }, children: "Auto-scan in progress..." })
699
- ]
700
- }
701
- ),
702
- showCachedPending && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
703
- /* @__PURE__ */ jsxs(
704
- "div",
705
- {
706
- style: {
707
- display: "inline-flex",
708
- alignItems: "center",
709
- gap: "8px",
710
- padding: "12px 24px",
711
- borderRadius: "8px",
712
- backgroundColor: STYLES.bgSurface,
713
- color: STYLES.textMuted,
714
- fontSize: "13px"
715
- },
716
- children: [
717
- /* @__PURE__ */ jsx(
718
- "div",
719
- {
720
- style: {
721
- width: "8px",
722
- height: "8px",
723
- borderRadius: "50%",
724
- backgroundColor: "rgba(156, 163, 175, 0.5)"
725
- }
726
- }
727
- ),
728
- "Waiting in scan queue..."
729
- ]
730
- }
731
- ),
732
- /* @__PURE__ */ jsx("div", { style: { marginTop: "16px" }, children: /* @__PURE__ */ jsx(
733
- "button",
734
- {
735
- onClick: handleScan,
736
- disabled: !element.source,
737
- style: {
738
- padding: "8px 16px",
739
- borderRadius: "6px",
740
- border: `1px solid ${STYLES.border}`,
741
- backgroundColor: "transparent",
742
- color: STYLES.textMuted,
743
- fontSize: "12px",
744
- cursor: element.source ? "pointer" : "not-allowed",
745
- transition: "all 0.15s"
746
- },
747
- onMouseEnter: (e) => {
748
- if (element.source) {
749
- e.currentTarget.style.borderColor = STYLES.accent;
750
- e.currentTarget.style.color = STYLES.text;
751
- }
752
- },
753
- onMouseLeave: (e) => {
754
- e.currentTarget.style.borderColor = STYLES.border;
755
- e.currentTarget.style.color = STYLES.textMuted;
756
- },
757
- children: "Scan Now"
758
- }
759
- ) })
760
- ] }),
761
- showCachedError && /* @__PURE__ */ jsxs("div", { children: [
762
- /* @__PURE__ */ jsx(
763
- "div",
764
- {
765
- style: {
766
- padding: "16px",
767
- backgroundColor: "rgba(239, 68, 68, 0.1)",
768
- border: "1px solid rgba(239, 68, 68, 0.3)",
769
- borderRadius: "8px",
770
- color: "#EF4444",
771
- fontSize: "13px",
772
- marginBottom: "16px"
773
- },
774
- children: "Auto-scan failed for this element"
775
- }
776
- ),
777
- /* @__PURE__ */ jsx("div", { style: { textAlign: "center" }, children: /* @__PURE__ */ jsxs(
778
- "button",
779
- {
780
- onClick: handleScan,
781
- disabled: !element.source,
782
- style: {
783
- display: "inline-flex",
784
- alignItems: "center",
785
- gap: "8px",
786
- padding: "10px 20px",
787
- borderRadius: "8px",
788
- border: "none",
789
- backgroundColor: element.source ? STYLES.success : STYLES.textDim,
790
- color: "#FFFFFF",
791
- fontSize: "13px",
792
- fontWeight: 600,
793
- cursor: element.source ? "pointer" : "not-allowed"
794
- },
795
- children: [
796
- /* @__PURE__ */ jsx(ScanIcon, {}),
797
- "Retry Scan"
798
- ]
799
- }
800
- ) })
801
- ] }),
802
- showCachedResult && /* @__PURE__ */ jsxs("div", { children: [
803
- /* @__PURE__ */ jsxs(
804
- "div",
805
- {
806
- style: {
807
- display: "flex",
808
- alignItems: "center",
809
- gap: "8px",
810
- marginBottom: "12px",
811
- padding: "8px 12px",
812
- backgroundColor: "rgba(16, 185, 129, 0.1)",
813
- borderRadius: "6px",
814
- fontSize: "11px",
815
- color: STYLES.success
816
- },
817
- children: [
818
- /* @__PURE__ */ jsx(CheckIconSmall, {}),
819
- "Results from auto-scan"
820
- ]
821
- }
822
- ),
823
- /* @__PURE__ */ jsxs(
824
- "div",
825
- {
826
- style: {
827
- display: "flex",
828
- alignItems: "center",
829
- justifyContent: "space-between",
830
- marginBottom: "12px"
831
- },
832
- children: [
833
- /* @__PURE__ */ jsx(
834
- "div",
835
- {
836
- style: {
837
- fontSize: "12px",
838
- fontWeight: 600,
839
- color: STYLES.text
840
- },
841
- children: "Fix Prompt"
842
- }
843
- ),
844
- /* @__PURE__ */ jsx(
845
- "button",
846
- {
847
- onClick: () => handleCopy(cachedFixPrompt),
848
- style: {
849
- display: "flex",
850
- alignItems: "center",
851
- gap: "6px",
852
- padding: "6px 12px",
853
- borderRadius: "6px",
854
- border: "none",
855
- backgroundColor: copied ? STYLES.success : STYLES.accent,
856
- color: "#FFFFFF",
857
- fontSize: "11px",
858
- fontWeight: 500,
859
- cursor: "pointer",
860
- transition: "all 0.15s"
861
- },
862
- children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
863
- /* @__PURE__ */ jsx(CheckIcon, {}),
864
- "Copied!"
865
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
866
- /* @__PURE__ */ jsx(CopyIcon, {}),
867
- "Copy to Clipboard"
868
- ] })
869
- }
870
- )
871
- ]
872
- }
873
- ),
874
- /* @__PURE__ */ jsx(
875
- "div",
876
- {
877
- style: {
878
- padding: "12px",
879
- backgroundColor: STYLES.bgSurface,
880
- border: `1px solid ${STYLES.border}`,
881
- borderRadius: "8px",
882
- fontFamily: STYLES.fontMono,
883
- fontSize: "12px",
884
- lineHeight: 1.6,
885
- whiteSpace: "pre-wrap",
886
- color: STYLES.text,
887
- maxHeight: "300px",
888
- overflow: "auto"
889
- },
890
- children: cachedFixPrompt
891
- }
892
- ),
893
- /* @__PURE__ */ jsx(
894
- "div",
895
- {
896
- style: {
897
- marginTop: "12px",
898
- fontSize: "11px",
899
- color: STYLES.textMuted,
900
- textAlign: "center"
901
- },
902
- children: "Paste this prompt into Cursor to fix the issues"
903
- }
904
- ),
905
- /* @__PURE__ */ jsx("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx(
906
- "button",
907
- {
908
- onClick: handleScan,
909
- style: {
910
- padding: "8px 16px",
911
- borderRadius: "6px",
912
- border: `1px solid ${STYLES.border}`,
913
- backgroundColor: "transparent",
914
- color: STYLES.textMuted,
915
- fontSize: "12px",
916
- cursor: "pointer",
917
- transition: "all 0.15s"
918
- },
919
- onMouseEnter: (e) => {
920
- e.currentTarget.style.borderColor = STYLES.accent;
921
- e.currentTarget.style.color = STYLES.text;
922
- },
923
- onMouseLeave: (e) => {
924
- e.currentTarget.style.borderColor = STYLES.border;
925
- e.currentTarget.style.color = STYLES.textMuted;
926
- },
927
- children: "Rescan"
928
- }
929
- ) })
930
- ] }),
931
- showScanButton && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
932
- /* @__PURE__ */ jsxs(
933
- "button",
934
- {
935
- onClick: handleScan,
936
- disabled: !element.source,
937
- style: {
938
- display: "inline-flex",
939
- alignItems: "center",
940
- gap: "8px",
941
- padding: "12px 24px",
942
- borderRadius: "8px",
943
- border: "none",
944
- backgroundColor: element.source ? STYLES.success : STYLES.textDim,
945
- color: "#FFFFFF",
946
- fontSize: "14px",
947
- fontWeight: 600,
948
- cursor: element.source ? "pointer" : "not-allowed",
949
- transition: "all 0.15s"
950
- },
951
- onMouseEnter: (e) => {
952
- if (element.source) {
953
- e.currentTarget.style.backgroundColor = "#059669";
954
- }
955
- },
956
- onMouseLeave: (e) => {
957
- if (element.source) {
958
- e.currentTarget.style.backgroundColor = STYLES.success;
959
- }
960
- },
961
- children: [
962
- /* @__PURE__ */ jsx(ScanIcon, {}),
963
- "Scan with LLM"
964
- ]
965
- }
966
- ),
967
- /* @__PURE__ */ jsx(
968
- "div",
969
- {
970
- style: {
971
- marginTop: "12px",
972
- fontSize: "12px",
973
- color: STYLES.textMuted
974
- },
975
- children: "Analyze this component for style issues"
976
- }
977
- ),
978
- !element.source && /* @__PURE__ */ jsx(
979
- "div",
980
- {
981
- style: {
982
- marginTop: "8px",
983
- fontSize: "11px",
984
- color: STYLES.warning
985
- },
986
- children: "No source information available"
987
- }
988
- )
989
- ] }),
990
- scanning && /* @__PURE__ */ jsxs(
991
- "div",
992
- {
993
- style: {
994
- display: "flex",
995
- flexDirection: "column",
996
- alignItems: "center",
997
- justifyContent: "center",
998
- padding: "48px 24px",
999
- gap: "16px"
1000
- },
1001
- children: [
1002
- /* @__PURE__ */ jsx(
1003
- "div",
1004
- {
1005
- style: {
1006
- width: "32px",
1007
- height: "32px",
1008
- border: `3px solid ${STYLES.border}`,
1009
- borderTopColor: STYLES.success,
1010
- borderRadius: "50%",
1011
- animation: "uilint-spin 1s linear infinite"
1012
- }
1013
- }
1014
- ),
1015
- /* @__PURE__ */ jsx("div", { style: { color: STYLES.textMuted, fontSize: "13px" }, children: "Analyzing source code..." })
1016
- ]
1017
- }
1018
- ),
1019
- error && /* @__PURE__ */ jsx(
1020
- "div",
1021
- {
1022
- style: {
1023
- padding: "16px",
1024
- backgroundColor: "rgba(239, 68, 68, 0.1)",
1025
- border: "1px solid rgba(239, 68, 68, 0.3)",
1026
- borderRadius: "8px",
1027
- color: "#EF4444",
1028
- fontSize: "13px",
1029
- marginTop: "16px"
1030
- },
1031
- children: error
1032
- }
1033
- ),
1034
- showManualResult && /* @__PURE__ */ jsxs("div", { children: [
1035
- /* @__PURE__ */ jsxs(
1036
- "div",
1037
- {
1038
- style: {
1039
- display: "flex",
1040
- alignItems: "center",
1041
- justifyContent: "space-between",
1042
- marginBottom: "12px"
1043
- },
1044
- children: [
1045
- /* @__PURE__ */ jsx(
1046
- "div",
1047
- {
1048
- style: {
1049
- fontSize: "12px",
1050
- fontWeight: 600,
1051
- color: STYLES.text
1052
- },
1053
- children: "Fix Prompt"
1054
- }
1055
- ),
1056
- /* @__PURE__ */ jsx(
1057
- "button",
1058
- {
1059
- onClick: () => handleCopy(fixPrompt),
1060
- style: {
1061
- display: "flex",
1062
- alignItems: "center",
1063
- gap: "6px",
1064
- padding: "6px 12px",
1065
- borderRadius: "6px",
1066
- border: "none",
1067
- backgroundColor: copied ? STYLES.success : STYLES.accent,
1068
- color: "#FFFFFF",
1069
- fontSize: "11px",
1070
- fontWeight: 500,
1071
- cursor: "pointer",
1072
- transition: "all 0.15s"
1073
- },
1074
- children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
1075
- /* @__PURE__ */ jsx(CheckIcon, {}),
1076
- "Copied!"
1077
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1078
- /* @__PURE__ */ jsx(CopyIcon, {}),
1079
- "Copy to Clipboard"
1080
- ] })
1081
- }
1082
- )
1083
- ]
1084
- }
1085
- ),
1086
- /* @__PURE__ */ jsx(
1087
- "div",
1088
- {
1089
- style: {
1090
- padding: "12px",
1091
- backgroundColor: STYLES.bgSurface,
1092
- border: `1px solid ${STYLES.border}`,
1093
- borderRadius: "8px",
1094
- fontFamily: STYLES.fontMono,
1095
- fontSize: "12px",
1096
- lineHeight: 1.6,
1097
- whiteSpace: "pre-wrap",
1098
- color: STYLES.text,
1099
- maxHeight: "300px",
1100
- overflow: "auto"
1101
- },
1102
- children: fixPrompt
1103
- }
1104
- ),
1105
- /* @__PURE__ */ jsx(
1106
- "div",
1107
- {
1108
- style: {
1109
- marginTop: "12px",
1110
- fontSize: "11px",
1111
- color: STYLES.textMuted,
1112
- textAlign: "center"
1113
- },
1114
- children: "Paste this prompt into Cursor to fix the issues"
1115
- }
1116
- ),
1117
- /* @__PURE__ */ jsx("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx(
1118
- "button",
1119
- {
1120
- onClick: () => {
1121
- setFixPrompt(null);
1122
- handleScan();
1123
- },
1124
- style: {
1125
- padding: "8px 16px",
1126
- borderRadius: "6px",
1127
- border: `1px solid ${STYLES.border}`,
1128
- backgroundColor: "transparent",
1129
- color: STYLES.textMuted,
1130
- fontSize: "12px",
1131
- cursor: "pointer",
1132
- transition: "all 0.15s"
1133
- },
1134
- onMouseEnter: (e) => {
1135
- e.currentTarget.style.borderColor = STYLES.accent;
1136
- e.currentTarget.style.color = STYLES.text;
1137
- },
1138
- onMouseLeave: (e) => {
1139
- e.currentTarget.style.borderColor = STYLES.border;
1140
- e.currentTarget.style.color = STYLES.textMuted;
1141
- },
1142
- children: "Scan Again"
1143
- }
1144
- ) })
1145
- ] })
1146
- ] });
1147
- }
1148
- function CheckIconSmall() {
1149
- return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
1150
- "path",
1151
- {
1152
- d: "M20 6L9 17l-5-5",
1153
- stroke: "currentColor",
1154
- strokeWidth: "2",
1155
- strokeLinecap: "round",
1156
- strokeLinejoin: "round"
1157
- }
1158
- ) });
1159
- }
1160
- function Section({
1161
- title,
1162
- children
1163
- }) {
1164
- return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "20px" }, children: [
1165
- /* @__PURE__ */ jsx(
1166
- "div",
1167
- {
1168
- style: {
1169
- fontSize: "11px",
1170
- fontWeight: 600,
1171
- color: STYLES.textDim,
1172
- textTransform: "uppercase",
1173
- letterSpacing: "0.5px",
1174
- marginBottom: "8px"
1175
- },
1176
- children: title
1177
- }
1178
- ),
1179
- children
1180
- ] });
1181
- }
1182
- function InfoRow({
1183
- label,
1184
- value,
1185
- mono
1186
- }) {
1187
- return /* @__PURE__ */ jsxs(
1188
- "div",
1189
- {
1190
- style: {
1191
- display: "flex",
1192
- justifyContent: "space-between",
1193
- alignItems: "flex-start",
1194
- fontSize: "12px",
1195
- marginBottom: "4px"
1196
- },
1197
- children: [
1198
- /* @__PURE__ */ jsx("span", { style: { color: STYLES.textMuted }, children: label }),
1199
- /* @__PURE__ */ jsx(
1200
- "span",
1201
- {
1202
- style: {
1203
- color: STYLES.text,
1204
- fontFamily: mono ? STYLES.fontMono : void 0,
1205
- textAlign: "right",
1206
- maxWidth: "200px",
1207
- wordBreak: "break-word"
1208
- },
1209
- children: value
1210
- }
1211
- )
1212
- ]
1213
- }
1214
- );
1215
- }
1216
- function ComponentStackItem({
1217
- component,
1218
- index,
1219
- isFirst
1220
- }) {
1221
- const handleClick = useCallback(() => {
1222
- if (component.source) {
1223
- const url = buildEditorUrl(component.source, "cursor");
1224
- window.open(url, "_blank");
1225
- }
1226
- }, [component.source]);
1227
- return /* @__PURE__ */ jsxs(
1228
- "div",
1229
- {
1230
- style: {
1231
- display: "flex",
1232
- alignItems: "center",
1233
- gap: "8px",
1234
- padding: "6px 8px",
1235
- marginLeft: index * 8,
1236
- backgroundColor: isFirst ? "rgba(59, 130, 246, 0.1)" : "transparent",
1237
- borderRadius: "4px",
1238
- cursor: component.source ? "pointer" : "default",
1239
- transition: "background-color 0.15s"
1240
- },
1241
- onClick: handleClick,
1242
- onMouseEnter: (e) => {
1243
- if (component.source) {
1244
- e.currentTarget.style.backgroundColor = "rgba(59, 130, 246, 0.15)";
1245
- }
1246
- },
1247
- onMouseLeave: (e) => {
1248
- e.currentTarget.style.backgroundColor = isFirst ? "rgba(59, 130, 246, 0.1)" : "transparent";
1249
- },
1250
- children: [
1251
- /* @__PURE__ */ jsx(
1252
- "span",
1253
- {
1254
- style: {
1255
- fontSize: "12px",
1256
- fontWeight: isFirst ? 600 : 400,
1257
- color: isFirst ? STYLES.accent : STYLES.textMuted
1258
- },
1259
- children: component.name
1260
- }
1261
- ),
1262
- component.source && /* @__PURE__ */ jsxs(
1263
- "span",
1264
- {
1265
- style: {
1266
- fontSize: "10px",
1267
- color: STYLES.textDim,
1268
- fontFamily: STYLES.fontMono
1269
- },
1270
- children: [
1271
- ":",
1272
- component.source.lineNumber
1273
- ]
1274
- }
1275
- )
1276
- ]
1277
- }
1278
- );
1279
- }
1280
- function CursorIcon() {
1281
- return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
1282
- "path",
1283
- {
1284
- d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3",
1285
- stroke: "currentColor",
1286
- strokeWidth: "2",
1287
- strokeLinecap: "round",
1288
- strokeLinejoin: "round"
1289
- }
1290
- ) });
1291
- }
1292
- function CloseIcon() {
1293
- return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
1294
- "path",
1295
- {
1296
- d: "M18 6L6 18M6 6l12 12",
1297
- stroke: "currentColor",
1298
- strokeWidth: "2",
1299
- strokeLinecap: "round",
1300
- strokeLinejoin: "round"
1301
- }
1302
- ) });
1303
- }
1304
- function ScanIcon() {
1305
- return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
1306
- "path",
1307
- {
1308
- d: "M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83",
1309
- stroke: "currentColor",
1310
- strokeWidth: "2",
1311
- strokeLinecap: "round"
1312
- }
1313
- ) });
1314
- }
1315
- function CopyIcon() {
1316
- return /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: [
1317
- /* @__PURE__ */ jsx(
1318
- "rect",
1319
- {
1320
- x: "9",
1321
- y: "9",
1322
- width: "13",
1323
- height: "13",
1324
- rx: "2",
1325
- stroke: "currentColor",
1326
- strokeWidth: "2"
1327
- }
1328
- ),
1329
- /* @__PURE__ */ jsx(
1330
- "path",
1331
- {
1332
- d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1",
1333
- stroke: "currentColor",
1334
- strokeWidth: "2"
1335
- }
1336
- )
1337
- ] });
1338
- }
1339
- function CheckIcon() {
1340
- return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
1341
- "path",
1342
- {
1343
- d: "M20 6L9 17l-5-5",
1344
- stroke: "currentColor",
1345
- strokeWidth: "2",
1346
- strokeLinecap: "round",
1347
- strokeLinejoin: "round"
1348
- }
1349
- ) });
1350
- }
1351
-
1352
- export {
1353
- fetchSource,
1354
- fetchSourceWithContext,
1355
- clearSourceCache,
1356
- getCachedSource,
1357
- prefetchSources,
1358
- InspectionPanel
1359
- };