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