create-stylus-ide 1.0.0

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.
Files changed (135) hide show
  1. package/Readme.MD +1515 -0
  2. package/cli.js +28 -0
  3. package/frontend/.vscode/settings.json +9 -0
  4. package/frontend/app/api/chat/route.ts +101 -0
  5. package/frontend/app/api/check-setup/route.ts +93 -0
  6. package/frontend/app/api/cleanup/route.ts +14 -0
  7. package/frontend/app/api/compile/route.ts +95 -0
  8. package/frontend/app/api/compile-stream/route.ts +98 -0
  9. package/frontend/app/api/complete/route.ts +86 -0
  10. package/frontend/app/api/deploy/route.ts +118 -0
  11. package/frontend/app/api/export-abi/route.ts +58 -0
  12. package/frontend/app/favicon.ico +0 -0
  13. package/frontend/app/globals.css +177 -0
  14. package/frontend/app/layout.tsx +29 -0
  15. package/frontend/app/ml/page.tsx +694 -0
  16. package/frontend/app/page.tsx +1132 -0
  17. package/frontend/app/providers.tsx +18 -0
  18. package/frontend/app/qlearning/page.tsx +188 -0
  19. package/frontend/app/raytracing/page.tsx +268 -0
  20. package/frontend/components/abi/ABIDialog.tsx +132 -0
  21. package/frontend/components/ai/AICompletionPopup.tsx +76 -0
  22. package/frontend/components/ai/ChatPanel.tsx +292 -0
  23. package/frontend/components/ai/QuickActions.tsx +128 -0
  24. package/frontend/components/blockchain/BlockchainContractBanner.tsx +64 -0
  25. package/frontend/components/blockchain/BlockchainLoadingDialog.tsx +188 -0
  26. package/frontend/components/deploy/DeployDialog.tsx +334 -0
  27. package/frontend/components/editor/FileTabs.tsx +181 -0
  28. package/frontend/components/editor/MonacoEditor.tsx +306 -0
  29. package/frontend/components/file-tree/ContextMenu.tsx +110 -0
  30. package/frontend/components/file-tree/DeleteConfirmDialog.tsx +61 -0
  31. package/frontend/components/file-tree/FileInputDialog.tsx +97 -0
  32. package/frontend/components/file-tree/FileNode.tsx +60 -0
  33. package/frontend/components/file-tree/FileTree.tsx +259 -0
  34. package/frontend/components/file-tree/FileTreeSkeleton.tsx +26 -0
  35. package/frontend/components/file-tree/FolderNode.tsx +105 -0
  36. package/frontend/components/github/GitHubLoadingDialog.tsx +201 -0
  37. package/frontend/components/github/GitHubMetadataBanner.tsx +61 -0
  38. package/frontend/components/github/LoadFromGitHubDialog.tsx +125 -0
  39. package/frontend/components/github/URLCopyButton.tsx +60 -0
  40. package/frontend/components/interact/ContractInteraction.tsx +323 -0
  41. package/frontend/components/interact/ContractPlaceholder.tsx +41 -0
  42. package/frontend/components/orbit/BenchmarkDialog.tsx +342 -0
  43. package/frontend/components/orbit/OrbitExplorer.tsx +273 -0
  44. package/frontend/components/project/ProjectActions.tsx +176 -0
  45. package/frontend/components/q-learning/ContractConfig.tsx +172 -0
  46. package/frontend/components/q-learning/MazeGrid.tsx +346 -0
  47. package/frontend/components/q-learning/PathAnimation.tsx +384 -0
  48. package/frontend/components/q-learning/QTableHeatmap.tsx +300 -0
  49. package/frontend/components/q-learning/TrainingForm.tsx +349 -0
  50. package/frontend/components/ray-tracing/ContractConfig.tsx +245 -0
  51. package/frontend/components/ray-tracing/MintingForm.tsx +280 -0
  52. package/frontend/components/ray-tracing/RenderCanvas.tsx +228 -0
  53. package/frontend/components/ray-tracing/RenderingPanel.tsx +259 -0
  54. package/frontend/components/ray-tracing/StyleControls.tsx +217 -0
  55. package/frontend/components/setup/SetupGuide.tsx +290 -0
  56. package/frontend/components/ui/KeyboardShortcutHint.tsx +74 -0
  57. package/frontend/components/ui/alert-dialog.tsx +157 -0
  58. package/frontend/components/ui/alert.tsx +66 -0
  59. package/frontend/components/ui/badge.tsx +46 -0
  60. package/frontend/components/ui/button.tsx +62 -0
  61. package/frontend/components/ui/card.tsx +92 -0
  62. package/frontend/components/ui/context-menu.tsx +252 -0
  63. package/frontend/components/ui/dialog.tsx +143 -0
  64. package/frontend/components/ui/dropdown-menu.tsx +257 -0
  65. package/frontend/components/ui/input.tsx +21 -0
  66. package/frontend/components/ui/label.tsx +24 -0
  67. package/frontend/components/ui/progress.tsx +31 -0
  68. package/frontend/components/ui/scroll-area.tsx +58 -0
  69. package/frontend/components/ui/select.tsx +190 -0
  70. package/frontend/components/ui/separator.tsx +28 -0
  71. package/frontend/components/ui/sheet.tsx +139 -0
  72. package/frontend/components/ui/skeleton.tsx +13 -0
  73. package/frontend/components/ui/slider.tsx +63 -0
  74. package/frontend/components/ui/sonner.tsx +40 -0
  75. package/frontend/components/ui/tabs.tsx +66 -0
  76. package/frontend/components/ui/textarea.tsx +18 -0
  77. package/frontend/components/wallet/ConnectButton.tsx +167 -0
  78. package/frontend/components/wallet/FaucetButton.tsx +256 -0
  79. package/frontend/components.json +22 -0
  80. package/frontend/eslint.config.mjs +18 -0
  81. package/frontend/hooks/useAICompletion.ts +75 -0
  82. package/frontend/hooks/useBlockchainLoader.ts +58 -0
  83. package/frontend/hooks/useChats.ts +137 -0
  84. package/frontend/hooks/useCompilation.ts +173 -0
  85. package/frontend/hooks/useFileTabs.ts +178 -0
  86. package/frontend/hooks/useGitHubLoader.ts +50 -0
  87. package/frontend/hooks/useKeyboardShortcuts.ts +47 -0
  88. package/frontend/hooks/usePanelState.ts +115 -0
  89. package/frontend/hooks/useProjectState.ts +276 -0
  90. package/frontend/hooks/useResponsive.ts +29 -0
  91. package/frontend/lib/abi-parser.ts +58 -0
  92. package/frontend/lib/blockchain-api.ts +374 -0
  93. package/frontend/lib/blockchain-explorers.ts +75 -0
  94. package/frontend/lib/blockchain-loader.ts +112 -0
  95. package/frontend/lib/cargo-template.ts +64 -0
  96. package/frontend/lib/compilation.ts +529 -0
  97. package/frontend/lib/constants.ts +31 -0
  98. package/frontend/lib/deployment.ts +176 -0
  99. package/frontend/lib/file-utils.ts +83 -0
  100. package/frontend/lib/github-api.ts +246 -0
  101. package/frontend/lib/github-loader.ts +369 -0
  102. package/frontend/lib/ml-contract-template.txt +900 -0
  103. package/frontend/lib/orbit-chains.ts +181 -0
  104. package/frontend/lib/output-formatter.ts +68 -0
  105. package/frontend/lib/project-manager.ts +632 -0
  106. package/frontend/lib/ray-tracing-abi.ts +206 -0
  107. package/frontend/lib/storage.ts +189 -0
  108. package/frontend/lib/templates.ts +1662 -0
  109. package/frontend/lib/url-parser.ts +188 -0
  110. package/frontend/lib/utils.ts +6 -0
  111. package/frontend/lib/wagmi-config.ts +24 -0
  112. package/frontend/next.config.ts +7 -0
  113. package/frontend/package-lock.json +16259 -0
  114. package/frontend/package.json +60 -0
  115. package/frontend/postcss.config.mjs +7 -0
  116. package/frontend/public/file.svg +1 -0
  117. package/frontend/public/globe.svg +1 -0
  118. package/frontend/public/ml-weights/.gitkeep +0 -0
  119. package/frontend/public/ml-weights/model.pkl +0 -0
  120. package/frontend/public/ml-weights/model_weights.json +27102 -0
  121. package/frontend/public/ml-weights/test_samples.json +7888 -0
  122. package/frontend/public/next.svg +1 -0
  123. package/frontend/public/vercel.svg +1 -0
  124. package/frontend/public/window.svg +1 -0
  125. package/frontend/scripts/check-env.js +52 -0
  126. package/frontend/scripts/setup.js +285 -0
  127. package/frontend/tailwind.config.ts +64 -0
  128. package/frontend/tsconfig.json +34 -0
  129. package/frontend/types/blockchain.ts +63 -0
  130. package/frontend/types/github.ts +54 -0
  131. package/frontend/types/project.ts +106 -0
  132. package/ml-training/README.md +56 -0
  133. package/ml-training/train_tiny_model.py +325 -0
  134. package/ml-training/update_template.py +59 -0
  135. package/package.json +30 -0
@@ -0,0 +1,259 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { FileNode as FileNodeType } from '@/types/project';
5
+ import { FileNode } from './FileNode';
6
+ import { FolderNode } from './FolderNode';
7
+ import { FileTreeContextMenu } from './ContextMenu';
8
+ import { FileInputDialog } from './FileInputDialog';
9
+ import { DeleteConfirmDialog } from './DeleteConfirmDialog';
10
+ import { Button } from '@/components/ui/button';
11
+ import { Plus, FolderPlus } from 'lucide-react';
12
+ import { ScrollArea } from '@/components/ui/scroll-area';
13
+
14
+ interface FileTreeProps {
15
+ structure: FileNodeType[];
16
+ activeFilePath: string | null;
17
+ onFileClick: (path: string) => void;
18
+ onFolderToggle: (path: string, expanded: boolean) => void;
19
+ onNewFile?: (parentPath?: string) => void;
20
+ onNewFolder?: (parentPath?: string) => void;
21
+ onRename?: (oldPath: string, newName: string) => void;
22
+ onDuplicate?: (path: string) => void;
23
+ onDelete?: (path: string, isFolder: boolean) => void;
24
+ className?: string;
25
+ }
26
+
27
+ export function FileTree({
28
+ structure,
29
+ activeFilePath,
30
+ onFileClick,
31
+ onFolderToggle,
32
+ onNewFile,
33
+ onNewFolder,
34
+ onRename,
35
+ onDuplicate,
36
+ onDelete,
37
+ className = '',
38
+ }: FileTreeProps) {
39
+ const [contextNode, setContextNode] = useState<FileNodeType | null>(null);
40
+
41
+ // Dialog states
42
+ const [newFileDialog, setNewFileDialog] = useState<{
43
+ open: boolean;
44
+ parentPath?: string;
45
+ }>({ open: false });
46
+
47
+ const [newFolderDialog, setNewFolderDialog] = useState<{
48
+ open: boolean;
49
+ parentPath?: string;
50
+ }>({ open: false });
51
+
52
+ const [renameDialog, setRenameDialog] = useState<{
53
+ open: boolean;
54
+ path?: string;
55
+ currentName?: string;
56
+ }>({ open: false });
57
+
58
+ const [deleteDialog, setDeleteDialog] = useState<{
59
+ open: boolean;
60
+ path?: string;
61
+ name?: string;
62
+ isFolder?: boolean;
63
+ }>({ open: false });
64
+
65
+ // Context menu handlers
66
+ const handleNewFile = (parentPath?: string) => {
67
+ setNewFileDialog({ open: true, parentPath });
68
+ };
69
+
70
+ const handleNewFolder = (parentPath?: string) => {
71
+ setNewFolderDialog({ open: true, parentPath });
72
+ };
73
+
74
+ const handleRename = (path: string) => {
75
+ const pathParts = path.split('/');
76
+ const currentName = pathParts[pathParts.length - 1];
77
+ setRenameDialog({ open: true, path, currentName });
78
+ };
79
+
80
+ const handleDuplicate = (path: string) => {
81
+ onDuplicate?.(path);
82
+ };
83
+
84
+ const handleDelete = (path: string, isFolder: boolean) => {
85
+ const pathParts = path.split('/');
86
+ const name = pathParts[pathParts.length - 1];
87
+ setDeleteDialog({ open: true, path, name, isFolder });
88
+ };
89
+
90
+ // Dialog confirm handlers
91
+ const confirmNewFile = (name: string) => {
92
+ const fullPath = newFileDialog.parentPath
93
+ ? `${newFileDialog.parentPath}/${name}`
94
+ : name;
95
+ onNewFile?.(fullPath);
96
+ };
97
+
98
+ const confirmNewFolder = (name: string) => {
99
+ const fullPath = newFolderDialog.parentPath
100
+ ? `${newFolderDialog.parentPath}/${name}`
101
+ : name;
102
+ onNewFolder?.(fullPath);
103
+ };
104
+
105
+ const confirmRename = (newName: string) => {
106
+ if (renameDialog.path) {
107
+ onRename?.(renameDialog.path, newName);
108
+ }
109
+ };
110
+
111
+ const confirmDelete = () => {
112
+ if (deleteDialog.path !== undefined && deleteDialog.isFolder !== undefined) {
113
+ onDelete?.(deleteDialog.path, deleteDialog.isFolder);
114
+ }
115
+ };
116
+
117
+ return (
118
+ <>
119
+ <FileTreeContextMenu
120
+ node={null}
121
+ onNewFile={handleNewFile}
122
+ onNewFolder={handleNewFolder}
123
+ onRename={handleRename}
124
+ onDuplicate={handleDuplicate}
125
+ onDelete={handleDelete}
126
+ >
127
+ <div className={`flex flex-col h-full border-r border-border bg-card ${className}`}>
128
+ {/* Header */}
129
+ <div className="h-10 border-b border-border flex items-center justify-between px-3">
130
+ <span className="text-xs font-semibold text-muted-foreground uppercase">
131
+ Files
132
+ </span>
133
+
134
+ <div className="flex items-center gap-1">
135
+ {onNewFile && (
136
+ <Button
137
+ variant="ghost"
138
+ size="icon"
139
+ className="h-6 w-6"
140
+ onClick={() => handleNewFile()}
141
+ title="New File"
142
+ >
143
+ <Plus className="h-3.5 w-3.5" />
144
+ </Button>
145
+ )}
146
+
147
+ {onNewFolder && (
148
+ <Button
149
+ variant="ghost"
150
+ size="icon"
151
+ className="h-6 w-6"
152
+ onClick={() => handleNewFolder()}
153
+ title="New Folder"
154
+ >
155
+ <FolderPlus className="h-3.5 w-3.5" />
156
+ </Button>
157
+ )}
158
+ </div>
159
+ </div>
160
+
161
+ {/* File Tree */}
162
+ <ScrollArea className="flex-1">
163
+ <div className="p-2">
164
+ {structure.length === 0 ? (
165
+ <div className="text-xs text-muted-foreground text-center py-8">
166
+ No files yet. Create a new file to get started.
167
+ </div>
168
+ ) : (
169
+ <div className="space-y-0.5">
170
+ {structure.map((node) => (
171
+ node.type === 'folder' ? (
172
+ <FileTreeContextMenu
173
+ key={node.id}
174
+ node={node}
175
+ onNewFile={handleNewFile}
176
+ onNewFolder={handleNewFolder}
177
+ onRename={handleRename}
178
+ onDuplicate={handleDuplicate}
179
+ onDelete={handleDelete}
180
+ >
181
+ <div>
182
+ <FolderNode
183
+ node={node}
184
+ activeFilePath={activeFilePath}
185
+ onFileClick={onFileClick}
186
+ onFolderToggle={onFolderToggle}
187
+ onContextMenu={() => setContextNode(node)}
188
+ depth={0}
189
+ />
190
+ </div>
191
+ </FileTreeContextMenu>
192
+ ) : (
193
+ <FileTreeContextMenu
194
+ key={node.id}
195
+ node={node}
196
+ onNewFile={handleNewFile}
197
+ onNewFolder={handleNewFolder}
198
+ onRename={handleRename}
199
+ onDuplicate={handleDuplicate}
200
+ onDelete={handleDelete}
201
+ >
202
+ <div>
203
+ <FileNode
204
+ node={node}
205
+ isActive={node.path === activeFilePath}
206
+ onClick={() => onFileClick(node.path)}
207
+ onContextMenu={() => setContextNode(node)}
208
+ depth={0}
209
+ />
210
+ </div>
211
+ </FileTreeContextMenu>
212
+ )
213
+ ))}
214
+ </div>
215
+ )}
216
+ </div>
217
+ </ScrollArea>
218
+ </div>
219
+ </FileTreeContextMenu>
220
+
221
+ {/* Dialogs */}
222
+ <FileInputDialog
223
+ open={newFileDialog.open}
224
+ onOpenChange={(open) => setNewFileDialog({ open })}
225
+ title="New File"
226
+ description="Enter the name for the new file (e.g., utils.rs)"
227
+ placeholder="filename.rs"
228
+ onConfirm={confirmNewFile}
229
+ />
230
+
231
+ <FileInputDialog
232
+ open={newFolderDialog.open}
233
+ onOpenChange={(open) => setNewFolderDialog({ open })}
234
+ title="New Folder"
235
+ description="Enter the name for the new folder"
236
+ placeholder="folder-name"
237
+ onConfirm={confirmNewFolder}
238
+ />
239
+
240
+ <FileInputDialog
241
+ open={renameDialog.open}
242
+ onOpenChange={(open) => setRenameDialog({ open })}
243
+ title="Rename"
244
+ description="Enter the new name"
245
+ defaultValue={renameDialog.currentName}
246
+ placeholder="new-name"
247
+ onConfirm={confirmRename}
248
+ />
249
+
250
+ <DeleteConfirmDialog
251
+ open={deleteDialog.open}
252
+ onOpenChange={(open) => setDeleteDialog({ open })}
253
+ itemName={deleteDialog.name || ''}
254
+ isFolder={deleteDialog.isFolder || false}
255
+ onConfirm={confirmDelete}
256
+ />
257
+ </>
258
+ );
259
+ }
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import { Skeleton } from '@/components/ui/skeleton';
4
+
5
+ export function FileTreeSkeleton() {
6
+ return (
7
+ <div className="h-full p-2 space-y-1">
8
+ {/* Header skeleton */}
9
+ <div className="flex items-center justify-between px-2 py-2 mb-2">
10
+ <Skeleton className="h-4 w-24" />
11
+ <div className="flex gap-1">
12
+ <Skeleton className="h-6 w-6" />
13
+ <Skeleton className="h-6 w-6" />
14
+ </div>
15
+ </div>
16
+
17
+ {/* Folder skeletons */}
18
+ {[0, 1, 2, 3, 4].map((i) => (
19
+ <div key={i} className="flex items-center gap-2 px-2 py-1">
20
+ <Skeleton className="h-4 w-4" />
21
+ <Skeleton className="h-4 w-32" style={{ width: `${Math.random() * 100 + 80}px` }} />
22
+ </div>
23
+ ))}
24
+ </div>
25
+ );
26
+ }
@@ -0,0 +1,105 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { FileNode as FileNodeType } from '@/types/project';
5
+ import { ChevronRight, ChevronDown, Folder, FolderOpen } from 'lucide-react';
6
+ import { FileNode } from './FileNode';
7
+ import { FileTreeContextMenu } from './ContextMenu';
8
+ import { cn } from '@/lib/utils';
9
+
10
+ interface FolderNodeProps {
11
+ node: FileNodeType;
12
+ activeFilePath: string | null;
13
+ onFileClick: (path: string) => void;
14
+ onFolderToggle: (path: string, expanded: boolean) => void;
15
+ onContextMenu: () => void;
16
+ depth: number;
17
+ }
18
+
19
+ export function FolderNode({
20
+ node,
21
+ activeFilePath,
22
+ onFileClick,
23
+ onFolderToggle,
24
+ onContextMenu,
25
+ depth,
26
+ }: FolderNodeProps) {
27
+ const [isExpanded, setIsExpanded] = useState(node.expanded || false);
28
+ const paddingLeft = depth * 12 + 8;
29
+
30
+ const handleToggle = (e: React.MouseEvent) => {
31
+ e.stopPropagation();
32
+ const newExpanded = !isExpanded;
33
+ setIsExpanded(newExpanded);
34
+ onFolderToggle(node.path, newExpanded);
35
+ };
36
+
37
+ const hasChildren = node.children && node.children.length > 0;
38
+
39
+ return (
40
+ <div className="select-none">
41
+ {/* Folder Header */}
42
+ <div
43
+ className={cn(
44
+ 'flex items-center gap-1 px-2 py-1.5 rounded-md cursor-pointer text-sm',
45
+ 'hover:bg-accent transition-colors'
46
+ )}
47
+ style={{ paddingLeft: `${paddingLeft}px` }}
48
+ onClick={handleToggle}
49
+ onContextMenu={(e) => {
50
+ e.stopPropagation();
51
+ onContextMenu();
52
+ }}
53
+ >
54
+ {/* Chevron */}
55
+ <div className="flex items-center justify-center w-4 h-4">
56
+ {hasChildren ? (
57
+ isExpanded ? (
58
+ <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
59
+ ) : (
60
+ <ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />
61
+ )
62
+ ) : null}
63
+ </div>
64
+
65
+ {/* Folder Icon */}
66
+ {isExpanded ? (
67
+ <FolderOpen className="h-4 w-4 text-blue-500" />
68
+ ) : (
69
+ <Folder className="h-4 w-4 text-blue-500" />
70
+ )}
71
+
72
+ {/* Folder Name */}
73
+ <span className="truncate">{node.name}</span>
74
+ </div>
75
+
76
+ {/* Children (when expanded) */}
77
+ {isExpanded && hasChildren && (
78
+ <div className="space-y-0.5">
79
+ {node.children!.map((childNode) => (
80
+ childNode.type === 'folder' ? (
81
+ <FolderNode
82
+ key={childNode.id}
83
+ node={childNode}
84
+ activeFilePath={activeFilePath}
85
+ onFileClick={onFileClick}
86
+ onFolderToggle={onFolderToggle}
87
+ onContextMenu={onContextMenu}
88
+ depth={depth + 1}
89
+ />
90
+ ) : (
91
+ <FileNode
92
+ key={childNode.id}
93
+ node={childNode}
94
+ isActive={childNode.path === activeFilePath}
95
+ onClick={() => onFileClick(childNode.path)}
96
+ onContextMenu={onContextMenu}
97
+ depth={depth + 1}
98
+ />
99
+ )
100
+ ))}
101
+ </div>
102
+ )}
103
+ </div>
104
+ );
105
+ }
@@ -0,0 +1,201 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import {
5
+ Dialog,
6
+ DialogContent,
7
+ DialogDescription,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ } from '@/components/ui/dialog';
11
+ import { Progress } from '@/components/ui/progress';
12
+ import { Alert, AlertDescription } from '@/components/ui/alert';
13
+ import { CheckCircle2, Loader2, XCircle, Github } from 'lucide-react';
14
+ import { Button } from '@/components/ui/button';
15
+ import { GitHubLoadProgress } from '@/lib/github-loader';
16
+
17
+ interface GitHubLoadingDialogProps {
18
+ open: boolean;
19
+ onOpenChange: (open: boolean) => void;
20
+ progress: GitHubLoadProgress | null;
21
+ onRetry?: () => void;
22
+ }
23
+
24
+ export function GitHubLoadingDialog({
25
+ open,
26
+ onOpenChange,
27
+ progress,
28
+ onRetry,
29
+ }: GitHubLoadingDialogProps) {
30
+ const [dots, setDots] = useState('');
31
+
32
+ // Animated dots for loading
33
+ useEffect(() => {
34
+ if (progress?.stage === 'downloading-files' || progress?.stage === 'fetching-tree') {
35
+ const interval = setInterval(() => {
36
+ setDots((prev) => (prev.length >= 3 ? '' : prev + '.'));
37
+ }, 500);
38
+ return () => clearInterval(interval);
39
+ }
40
+ }, [progress?.stage]);
41
+
42
+ const getStageIcon = () => {
43
+ if (!progress) return <Loader2 className="h-5 w-5 animate-spin text-blue-500" />;
44
+
45
+ switch (progress.stage) {
46
+ case 'complete':
47
+ return <CheckCircle2 className="h-5 w-5 text-green-500" />;
48
+ case 'error':
49
+ return <XCircle className="h-5 w-5 text-red-500" />;
50
+ default:
51
+ return <Loader2 className="h-5 w-5 animate-spin text-blue-500" />;
52
+ }
53
+ };
54
+
55
+ const getStageTitle = () => {
56
+ if (!progress) return 'Loading...';
57
+
58
+ switch (progress.stage) {
59
+ case 'validating':
60
+ return 'Validating Repository';
61
+ case 'fetching-tree':
62
+ return 'Fetching Repository Structure';
63
+ case 'downloading-files':
64
+ return 'Downloading Files';
65
+ case 'complete':
66
+ return 'Success!';
67
+ case 'error':
68
+ return 'Error';
69
+ default:
70
+ return 'Loading...';
71
+ }
72
+ };
73
+
74
+ return (
75
+ <Dialog open={open} onOpenChange={onOpenChange}>
76
+ <DialogContent className="sm:max-w-md">
77
+ <DialogHeader>
78
+ <div className="flex items-center gap-3">
79
+ {progress?.stage === 'error' ? (
80
+ getStageIcon()
81
+ ) : progress?.stage === 'complete' ? (
82
+ getStageIcon()
83
+ ) : (
84
+ <Github className="h-5 w-5 text-muted-foreground" />
85
+ )}
86
+ <DialogTitle>{getStageTitle()}</DialogTitle>
87
+ </div>
88
+ <DialogDescription>
89
+ {progress?.stage === 'complete'
90
+ ? 'Repository loaded successfully'
91
+ : progress?.stage === 'error'
92
+ ? 'Failed to load repository'
93
+ : 'Loading from GitHub...'}
94
+ </DialogDescription>
95
+ </DialogHeader>
96
+
97
+ <div className="space-y-4">
98
+ {/* Progress Bar */}
99
+ {progress && progress.stage !== 'error' && progress.stage !== 'complete' && (
100
+ <Progress value={progress.progress} className="w-full" />
101
+ )}
102
+
103
+ {/* Status Message */}
104
+ {progress && (
105
+ <div className="space-y-2">
106
+ <p className="text-sm text-muted-foreground">
107
+ {progress.message}
108
+ {(progress.stage === 'downloading-files' || progress.stage === 'fetching-tree') &&
109
+ dots}
110
+ </p>
111
+
112
+ {/* File Progress */}
113
+ {progress.filesTotal && progress.filesDownloaded !== undefined && (
114
+ <div className="text-xs text-muted-foreground">
115
+ {progress.filesDownloaded} / {progress.filesTotal} files downloaded
116
+ </div>
117
+ )}
118
+
119
+ {/* Current File */}
120
+ {progress.currentFile && (
121
+ <div className="text-xs text-muted-foreground font-mono bg-muted px-2 py-1 rounded">
122
+ {progress.currentFile}
123
+ </div>
124
+ )}
125
+ </div>
126
+ )}
127
+
128
+ {/* Error Message */}
129
+ {progress?.stage === 'error' && (
130
+ <div className="space-y-3">
131
+ <Alert variant="destructive">
132
+ <XCircle className="h-4 w-4" />
133
+ <AlertDescription className="whitespace-pre-wrap">
134
+ {progress.message}
135
+ </AlertDescription>
136
+ </Alert>
137
+
138
+ {/* Help text for common errors */}
139
+ {progress.message.includes('rate limit') && (
140
+ <div className="text-xs text-muted-foreground space-y-1">
141
+ <p className="font-semibold">To increase rate limits:</p>
142
+ <ol className="list-decimal list-inside space-y-1">
143
+ <li>Create a GitHub Personal Access Token</li>
144
+ <li>Add NEXT_PUBLIC_GITHUB_TOKEN to .env.local</li>
145
+ <li>Restart your dev server</li>
146
+ </ol>
147
+ </div>
148
+ )}
149
+
150
+ {progress.message.includes('not found') && (
151
+ <div className="text-xs text-muted-foreground">
152
+ <p>Make sure the repository URL is correct and the repository is public.</p>
153
+ </div>
154
+ )}
155
+
156
+ {progress.message.includes('private') && (
157
+ <div className="text-xs text-muted-foreground">
158
+ <p>This repository is private. Public repositories only are supported.</p>
159
+ </div>
160
+ )}
161
+
162
+ {progress.message.includes('Network error') && (
163
+ <div className="text-xs text-muted-foreground">
164
+ <p>Check your internet connection and try again.</p>
165
+ </div>
166
+ )}
167
+ </div>
168
+ )}
169
+
170
+ {/* Success Message */}
171
+ {progress?.stage === 'complete' && (
172
+ <Alert>
173
+ <CheckCircle2 className="h-4 w-4" />
174
+ <AlertDescription>
175
+ Loaded {progress.filesDownloaded} files successfully
176
+ </AlertDescription>
177
+ </Alert>
178
+ )}
179
+
180
+ {/* Actions */}
181
+ {progress?.stage === 'error' && onRetry && (
182
+ <div className="flex gap-2">
183
+ <Button onClick={onRetry} className="flex-1">
184
+ Retry
185
+ </Button>
186
+ <Button onClick={() => onOpenChange(false)} variant="outline" className="flex-1">
187
+ Cancel
188
+ </Button>
189
+ </div>
190
+ )}
191
+
192
+ {progress?.stage === 'complete' && (
193
+ <Button onClick={() => onOpenChange(false)} className="w-full">
194
+ Continue to IDE
195
+ </Button>
196
+ )}
197
+ </div>
198
+ </DialogContent>
199
+ </Dialog>
200
+ );
201
+ }
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import { ExternalLink, Github, GitBranch, Folder } from 'lucide-react';
4
+ import { Button } from '@/components/ui/button';
5
+ import { URLCopyButton } from './URLCopyButton';
6
+
7
+ interface GitHubMetadataBannerProps {
8
+ owner: string;
9
+ repo: string;
10
+ branch: string;
11
+ url: string;
12
+ folderPath?: string; // ✅ ADD THIS
13
+ }
14
+
15
+ export function GitHubMetadataBanner({
16
+ owner,
17
+ repo,
18
+ branch,
19
+ url,
20
+ folderPath // ✅ ADD THIS
21
+ }: GitHubMetadataBannerProps) {
22
+ return (
23
+ <div className="h-8 border-b border-border bg-blue-500/10 flex items-center justify-between px-4 text-xs">
24
+ <div className="flex items-center gap-3 text-blue-600 dark:text-blue-400">
25
+ <div className="flex items-center gap-1.5">
26
+ <Github className="h-3.5 w-3.5" />
27
+ <span className="font-medium">{owner}/{repo}</span>
28
+ </div>
29
+ <div className="flex items-center gap-1.5 text-muted-foreground">
30
+ <GitBranch className="h-3 w-3" />
31
+ <span>{branch}</span>
32
+ </div>
33
+ {/* ✅ NEW: Show folder path if loading specific folder */}
34
+ {folderPath && (
35
+ <div className="flex items-center gap-1.5 text-muted-foreground">
36
+ <Folder className="h-3 w-3" />
37
+ <span>/{folderPath}</span>
38
+ </div>
39
+ )}
40
+ </div>
41
+ <div className="flex items-center gap-2">
42
+ {/* ✅ NEW: Share button */}
43
+ <URLCopyButton
44
+ githubUrl={url}
45
+ branch={branch}
46
+ folderPath={folderPath}
47
+ />
48
+
49
+ <Button
50
+ variant="ghost"
51
+ size="sm"
52
+ className="h-6 text-xs gap-1"
53
+ onClick={() => window.open(url, '_blank')}
54
+ >
55
+ View on GitHub
56
+ <ExternalLink className="h-3 w-3" />
57
+ </Button>
58
+ </div>
59
+ </div>
60
+ );
61
+ }