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,1052 @@
1
+ "use client";
2
+ import {
3
+ buildEditorUrl,
4
+ useUILintContext
5
+ } from "./chunk-DAFFOBEU.js";
6
+
7
+ // src/components/ui-lint/InspectionPanel.tsx
8
+ import { useState, useEffect, useCallback } 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 [scanning, setScanning] = useState(false);
562
+ const [error, setError] = useState(null);
563
+ const [fixPrompt, setFixPrompt] = useState(null);
564
+ const [copied, setCopied] = useState(false);
565
+ const componentName = element.componentStack[0]?.name || element.element.tagName.toLowerCase();
566
+ const componentLine = element.source?.lineNumber;
567
+ const handleScan = useCallback(async () => {
568
+ if (!element.source) {
569
+ setError("No source information available");
570
+ return;
571
+ }
572
+ setScanning(true);
573
+ setError(null);
574
+ setFixPrompt(null);
575
+ try {
576
+ const sourceResponse = await fetch(
577
+ `/api/uilint/source?path=${encodeURIComponent(element.source.fileName)}`
578
+ );
579
+ if (!sourceResponse.ok) {
580
+ throw new Error("Failed to fetch source code");
581
+ }
582
+ const sourceData = await sourceResponse.json();
583
+ const sourceCode = sourceData.content;
584
+ const relativePath = sourceData.relativePath || element.source.fileName;
585
+ const analyzeResponse = await fetch("/api/uilint/analyze", {
586
+ method: "POST",
587
+ headers: { "Content-Type": "application/json" },
588
+ body: JSON.stringify({
589
+ sourceCode,
590
+ filePath: relativePath,
591
+ componentName,
592
+ componentLine
593
+ })
594
+ });
595
+ if (!analyzeResponse.ok) {
596
+ throw new Error("Failed to analyze source code");
597
+ }
598
+ const result = await analyzeResponse.json();
599
+ const issues = result.issues || [];
600
+ if (issues.length === 0) {
601
+ setFixPrompt(
602
+ `No style issues found in the \`${componentName}\` component in \`${relativePath}\`. The component appears to follow the styleguide.`
603
+ );
604
+ } else {
605
+ const issueList = issues.map((issue) => {
606
+ const lineInfo = issue.line ? `Line ${issue.line}: ` : "";
607
+ return `- ${lineInfo}${issue.message}`;
608
+ }).join("\n");
609
+ setFixPrompt(
610
+ `Fix the following style issues in the \`${componentName}\` component in \`${relativePath}\`:
611
+
612
+ Issues found:
613
+ ${issueList}
614
+
615
+ Please update this component to match our styleguide.`
616
+ );
617
+ }
618
+ } catch (err) {
619
+ setError(
620
+ err instanceof Error ? err.message : "An error occurred during scanning"
621
+ );
622
+ } finally {
623
+ setScanning(false);
624
+ }
625
+ }, [element.source, componentName, componentLine]);
626
+ const handleCopy = useCallback(async () => {
627
+ if (!fixPrompt) return;
628
+ try {
629
+ await navigator.clipboard.writeText(fixPrompt);
630
+ setCopied(true);
631
+ setTimeout(() => setCopied(false), 2e3);
632
+ } catch {
633
+ setError("Failed to copy to clipboard");
634
+ }
635
+ }, [fixPrompt]);
636
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "16px" }, children: [
637
+ !fixPrompt && !scanning && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "24px 0" }, children: [
638
+ /* @__PURE__ */ jsxs(
639
+ "button",
640
+ {
641
+ onClick: handleScan,
642
+ disabled: !element.source,
643
+ style: {
644
+ display: "inline-flex",
645
+ alignItems: "center",
646
+ gap: "8px",
647
+ padding: "12px 24px",
648
+ borderRadius: "8px",
649
+ border: "none",
650
+ backgroundColor: element.source ? STYLES.success : STYLES.textDim,
651
+ color: "#FFFFFF",
652
+ fontSize: "14px",
653
+ fontWeight: 600,
654
+ cursor: element.source ? "pointer" : "not-allowed",
655
+ transition: "all 0.15s"
656
+ },
657
+ onMouseEnter: (e) => {
658
+ if (element.source) {
659
+ e.currentTarget.style.backgroundColor = "#059669";
660
+ }
661
+ },
662
+ onMouseLeave: (e) => {
663
+ if (element.source) {
664
+ e.currentTarget.style.backgroundColor = STYLES.success;
665
+ }
666
+ },
667
+ children: [
668
+ /* @__PURE__ */ jsx(ScanIcon, {}),
669
+ "Scan with LLM"
670
+ ]
671
+ }
672
+ ),
673
+ /* @__PURE__ */ jsx(
674
+ "div",
675
+ {
676
+ style: {
677
+ marginTop: "12px",
678
+ fontSize: "12px",
679
+ color: STYLES.textMuted
680
+ },
681
+ children: "Analyze this component for style issues"
682
+ }
683
+ ),
684
+ !element.source && /* @__PURE__ */ jsx(
685
+ "div",
686
+ {
687
+ style: {
688
+ marginTop: "8px",
689
+ fontSize: "11px",
690
+ color: STYLES.warning
691
+ },
692
+ children: "No source information available"
693
+ }
694
+ )
695
+ ] }),
696
+ scanning && /* @__PURE__ */ jsxs(
697
+ "div",
698
+ {
699
+ style: {
700
+ display: "flex",
701
+ flexDirection: "column",
702
+ alignItems: "center",
703
+ justifyContent: "center",
704
+ padding: "48px 24px",
705
+ gap: "16px"
706
+ },
707
+ children: [
708
+ /* @__PURE__ */ jsx(
709
+ "div",
710
+ {
711
+ style: {
712
+ width: "32px",
713
+ height: "32px",
714
+ border: `3px solid ${STYLES.border}`,
715
+ borderTopColor: STYLES.success,
716
+ borderRadius: "50%",
717
+ animation: "uilint-spin 1s linear infinite"
718
+ }
719
+ }
720
+ ),
721
+ /* @__PURE__ */ jsx("div", { style: { color: STYLES.textMuted, fontSize: "13px" }, children: "Analyzing source code..." })
722
+ ]
723
+ }
724
+ ),
725
+ error && /* @__PURE__ */ jsx(
726
+ "div",
727
+ {
728
+ style: {
729
+ padding: "16px",
730
+ backgroundColor: "rgba(239, 68, 68, 0.1)",
731
+ border: "1px solid rgba(239, 68, 68, 0.3)",
732
+ borderRadius: "8px",
733
+ color: "#EF4444",
734
+ fontSize: "13px"
735
+ },
736
+ children: error
737
+ }
738
+ ),
739
+ fixPrompt && /* @__PURE__ */ jsxs("div", { children: [
740
+ /* @__PURE__ */ jsxs(
741
+ "div",
742
+ {
743
+ style: {
744
+ display: "flex",
745
+ alignItems: "center",
746
+ justifyContent: "space-between",
747
+ marginBottom: "12px"
748
+ },
749
+ children: [
750
+ /* @__PURE__ */ jsx(
751
+ "div",
752
+ {
753
+ style: {
754
+ fontSize: "12px",
755
+ fontWeight: 600,
756
+ color: STYLES.text
757
+ },
758
+ children: "Fix Prompt"
759
+ }
760
+ ),
761
+ /* @__PURE__ */ jsx(
762
+ "button",
763
+ {
764
+ onClick: handleCopy,
765
+ style: {
766
+ display: "flex",
767
+ alignItems: "center",
768
+ gap: "6px",
769
+ padding: "6px 12px",
770
+ borderRadius: "6px",
771
+ border: "none",
772
+ backgroundColor: copied ? STYLES.success : STYLES.accent,
773
+ color: "#FFFFFF",
774
+ fontSize: "11px",
775
+ fontWeight: 500,
776
+ cursor: "pointer",
777
+ transition: "all 0.15s"
778
+ },
779
+ children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
780
+ /* @__PURE__ */ jsx(CheckIcon, {}),
781
+ "Copied!"
782
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
783
+ /* @__PURE__ */ jsx(CopyIcon, {}),
784
+ "Copy to Clipboard"
785
+ ] })
786
+ }
787
+ )
788
+ ]
789
+ }
790
+ ),
791
+ /* @__PURE__ */ jsx(
792
+ "div",
793
+ {
794
+ style: {
795
+ padding: "12px",
796
+ backgroundColor: STYLES.bgSurface,
797
+ border: `1px solid ${STYLES.border}`,
798
+ borderRadius: "8px",
799
+ fontFamily: STYLES.fontMono,
800
+ fontSize: "12px",
801
+ lineHeight: 1.6,
802
+ whiteSpace: "pre-wrap",
803
+ color: STYLES.text,
804
+ maxHeight: "300px",
805
+ overflow: "auto"
806
+ },
807
+ children: fixPrompt
808
+ }
809
+ ),
810
+ /* @__PURE__ */ jsx(
811
+ "div",
812
+ {
813
+ style: {
814
+ marginTop: "12px",
815
+ fontSize: "11px",
816
+ color: STYLES.textMuted,
817
+ textAlign: "center"
818
+ },
819
+ children: "Paste this prompt into Cursor to fix the issues"
820
+ }
821
+ ),
822
+ /* @__PURE__ */ jsx("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx(
823
+ "button",
824
+ {
825
+ onClick: () => {
826
+ setFixPrompt(null);
827
+ handleScan();
828
+ },
829
+ style: {
830
+ padding: "8px 16px",
831
+ borderRadius: "6px",
832
+ border: `1px solid ${STYLES.border}`,
833
+ backgroundColor: "transparent",
834
+ color: STYLES.textMuted,
835
+ fontSize: "12px",
836
+ cursor: "pointer",
837
+ transition: "all 0.15s"
838
+ },
839
+ onMouseEnter: (e) => {
840
+ e.currentTarget.style.borderColor = STYLES.accent;
841
+ e.currentTarget.style.color = STYLES.text;
842
+ },
843
+ onMouseLeave: (e) => {
844
+ e.currentTarget.style.borderColor = STYLES.border;
845
+ e.currentTarget.style.color = STYLES.textMuted;
846
+ },
847
+ children: "Scan Again"
848
+ }
849
+ ) })
850
+ ] })
851
+ ] });
852
+ }
853
+ function Section({
854
+ title,
855
+ children
856
+ }) {
857
+ return /* @__PURE__ */ jsxs("div", { style: { marginBottom: "20px" }, children: [
858
+ /* @__PURE__ */ jsx(
859
+ "div",
860
+ {
861
+ style: {
862
+ fontSize: "11px",
863
+ fontWeight: 600,
864
+ color: STYLES.textDim,
865
+ textTransform: "uppercase",
866
+ letterSpacing: "0.5px",
867
+ marginBottom: "8px"
868
+ },
869
+ children: title
870
+ }
871
+ ),
872
+ children
873
+ ] });
874
+ }
875
+ function InfoRow({
876
+ label,
877
+ value,
878
+ mono
879
+ }) {
880
+ return /* @__PURE__ */ jsxs(
881
+ "div",
882
+ {
883
+ style: {
884
+ display: "flex",
885
+ justifyContent: "space-between",
886
+ alignItems: "flex-start",
887
+ fontSize: "12px",
888
+ marginBottom: "4px"
889
+ },
890
+ children: [
891
+ /* @__PURE__ */ jsx("span", { style: { color: STYLES.textMuted }, children: label }),
892
+ /* @__PURE__ */ jsx(
893
+ "span",
894
+ {
895
+ style: {
896
+ color: STYLES.text,
897
+ fontFamily: mono ? STYLES.fontMono : void 0,
898
+ textAlign: "right",
899
+ maxWidth: "200px",
900
+ wordBreak: "break-word"
901
+ },
902
+ children: value
903
+ }
904
+ )
905
+ ]
906
+ }
907
+ );
908
+ }
909
+ function ComponentStackItem({
910
+ component,
911
+ index,
912
+ isFirst
913
+ }) {
914
+ const handleClick = useCallback(() => {
915
+ if (component.source) {
916
+ const url = buildEditorUrl(component.source, "cursor");
917
+ window.open(url, "_blank");
918
+ }
919
+ }, [component.source]);
920
+ return /* @__PURE__ */ jsxs(
921
+ "div",
922
+ {
923
+ style: {
924
+ display: "flex",
925
+ alignItems: "center",
926
+ gap: "8px",
927
+ padding: "6px 8px",
928
+ marginLeft: index * 8,
929
+ backgroundColor: isFirst ? "rgba(59, 130, 246, 0.1)" : "transparent",
930
+ borderRadius: "4px",
931
+ cursor: component.source ? "pointer" : "default",
932
+ transition: "background-color 0.15s"
933
+ },
934
+ onClick: handleClick,
935
+ onMouseEnter: (e) => {
936
+ if (component.source) {
937
+ e.currentTarget.style.backgroundColor = "rgba(59, 130, 246, 0.15)";
938
+ }
939
+ },
940
+ onMouseLeave: (e) => {
941
+ e.currentTarget.style.backgroundColor = isFirst ? "rgba(59, 130, 246, 0.1)" : "transparent";
942
+ },
943
+ children: [
944
+ /* @__PURE__ */ jsx(
945
+ "span",
946
+ {
947
+ style: {
948
+ fontSize: "12px",
949
+ fontWeight: isFirst ? 600 : 400,
950
+ color: isFirst ? STYLES.accent : STYLES.textMuted
951
+ },
952
+ children: component.name
953
+ }
954
+ ),
955
+ component.source && /* @__PURE__ */ jsxs(
956
+ "span",
957
+ {
958
+ style: {
959
+ fontSize: "10px",
960
+ color: STYLES.textDim,
961
+ fontFamily: STYLES.fontMono
962
+ },
963
+ children: [
964
+ ":",
965
+ component.source.lineNumber
966
+ ]
967
+ }
968
+ )
969
+ ]
970
+ }
971
+ );
972
+ }
973
+ function CursorIcon() {
974
+ return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
975
+ "path",
976
+ {
977
+ d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3",
978
+ stroke: "currentColor",
979
+ strokeWidth: "2",
980
+ strokeLinecap: "round",
981
+ strokeLinejoin: "round"
982
+ }
983
+ ) });
984
+ }
985
+ function CloseIcon() {
986
+ return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
987
+ "path",
988
+ {
989
+ d: "M18 6L6 18M6 6l12 12",
990
+ stroke: "currentColor",
991
+ strokeWidth: "2",
992
+ strokeLinecap: "round",
993
+ strokeLinejoin: "round"
994
+ }
995
+ ) });
996
+ }
997
+ function ScanIcon() {
998
+ return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
999
+ "path",
1000
+ {
1001
+ 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",
1002
+ stroke: "currentColor",
1003
+ strokeWidth: "2",
1004
+ strokeLinecap: "round"
1005
+ }
1006
+ ) });
1007
+ }
1008
+ function CopyIcon() {
1009
+ return /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: [
1010
+ /* @__PURE__ */ jsx(
1011
+ "rect",
1012
+ {
1013
+ x: "9",
1014
+ y: "9",
1015
+ width: "13",
1016
+ height: "13",
1017
+ rx: "2",
1018
+ stroke: "currentColor",
1019
+ strokeWidth: "2"
1020
+ }
1021
+ ),
1022
+ /* @__PURE__ */ jsx(
1023
+ "path",
1024
+ {
1025
+ d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1",
1026
+ stroke: "currentColor",
1027
+ strokeWidth: "2"
1028
+ }
1029
+ )
1030
+ ] });
1031
+ }
1032
+ function CheckIcon() {
1033
+ return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
1034
+ "path",
1035
+ {
1036
+ d: "M20 6L9 17l-5-5",
1037
+ stroke: "currentColor",
1038
+ strokeWidth: "2",
1039
+ strokeLinecap: "round",
1040
+ strokeLinejoin: "round"
1041
+ }
1042
+ ) });
1043
+ }
1044
+
1045
+ export {
1046
+ fetchSource,
1047
+ fetchSourceWithContext,
1048
+ clearSourceCache,
1049
+ getCachedSource,
1050
+ prefetchSources,
1051
+ InspectionPanel
1052
+ };