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.
- package/Readme.MD +1515 -0
- package/cli.js +28 -0
- package/frontend/.vscode/settings.json +9 -0
- package/frontend/app/api/chat/route.ts +101 -0
- package/frontend/app/api/check-setup/route.ts +93 -0
- package/frontend/app/api/cleanup/route.ts +14 -0
- package/frontend/app/api/compile/route.ts +95 -0
- package/frontend/app/api/compile-stream/route.ts +98 -0
- package/frontend/app/api/complete/route.ts +86 -0
- package/frontend/app/api/deploy/route.ts +118 -0
- package/frontend/app/api/export-abi/route.ts +58 -0
- package/frontend/app/favicon.ico +0 -0
- package/frontend/app/globals.css +177 -0
- package/frontend/app/layout.tsx +29 -0
- package/frontend/app/ml/page.tsx +694 -0
- package/frontend/app/page.tsx +1132 -0
- package/frontend/app/providers.tsx +18 -0
- package/frontend/app/qlearning/page.tsx +188 -0
- package/frontend/app/raytracing/page.tsx +268 -0
- package/frontend/components/abi/ABIDialog.tsx +132 -0
- package/frontend/components/ai/AICompletionPopup.tsx +76 -0
- package/frontend/components/ai/ChatPanel.tsx +292 -0
- package/frontend/components/ai/QuickActions.tsx +128 -0
- package/frontend/components/blockchain/BlockchainContractBanner.tsx +64 -0
- package/frontend/components/blockchain/BlockchainLoadingDialog.tsx +188 -0
- package/frontend/components/deploy/DeployDialog.tsx +334 -0
- package/frontend/components/editor/FileTabs.tsx +181 -0
- package/frontend/components/editor/MonacoEditor.tsx +306 -0
- package/frontend/components/file-tree/ContextMenu.tsx +110 -0
- package/frontend/components/file-tree/DeleteConfirmDialog.tsx +61 -0
- package/frontend/components/file-tree/FileInputDialog.tsx +97 -0
- package/frontend/components/file-tree/FileNode.tsx +60 -0
- package/frontend/components/file-tree/FileTree.tsx +259 -0
- package/frontend/components/file-tree/FileTreeSkeleton.tsx +26 -0
- package/frontend/components/file-tree/FolderNode.tsx +105 -0
- package/frontend/components/github/GitHubLoadingDialog.tsx +201 -0
- package/frontend/components/github/GitHubMetadataBanner.tsx +61 -0
- package/frontend/components/github/LoadFromGitHubDialog.tsx +125 -0
- package/frontend/components/github/URLCopyButton.tsx +60 -0
- package/frontend/components/interact/ContractInteraction.tsx +323 -0
- package/frontend/components/interact/ContractPlaceholder.tsx +41 -0
- package/frontend/components/orbit/BenchmarkDialog.tsx +342 -0
- package/frontend/components/orbit/OrbitExplorer.tsx +273 -0
- package/frontend/components/project/ProjectActions.tsx +176 -0
- package/frontend/components/q-learning/ContractConfig.tsx +172 -0
- package/frontend/components/q-learning/MazeGrid.tsx +346 -0
- package/frontend/components/q-learning/PathAnimation.tsx +384 -0
- package/frontend/components/q-learning/QTableHeatmap.tsx +300 -0
- package/frontend/components/q-learning/TrainingForm.tsx +349 -0
- package/frontend/components/ray-tracing/ContractConfig.tsx +245 -0
- package/frontend/components/ray-tracing/MintingForm.tsx +280 -0
- package/frontend/components/ray-tracing/RenderCanvas.tsx +228 -0
- package/frontend/components/ray-tracing/RenderingPanel.tsx +259 -0
- package/frontend/components/ray-tracing/StyleControls.tsx +217 -0
- package/frontend/components/setup/SetupGuide.tsx +290 -0
- package/frontend/components/ui/KeyboardShortcutHint.tsx +74 -0
- package/frontend/components/ui/alert-dialog.tsx +157 -0
- package/frontend/components/ui/alert.tsx +66 -0
- package/frontend/components/ui/badge.tsx +46 -0
- package/frontend/components/ui/button.tsx +62 -0
- package/frontend/components/ui/card.tsx +92 -0
- package/frontend/components/ui/context-menu.tsx +252 -0
- package/frontend/components/ui/dialog.tsx +143 -0
- package/frontend/components/ui/dropdown-menu.tsx +257 -0
- package/frontend/components/ui/input.tsx +21 -0
- package/frontend/components/ui/label.tsx +24 -0
- package/frontend/components/ui/progress.tsx +31 -0
- package/frontend/components/ui/scroll-area.tsx +58 -0
- package/frontend/components/ui/select.tsx +190 -0
- package/frontend/components/ui/separator.tsx +28 -0
- package/frontend/components/ui/sheet.tsx +139 -0
- package/frontend/components/ui/skeleton.tsx +13 -0
- package/frontend/components/ui/slider.tsx +63 -0
- package/frontend/components/ui/sonner.tsx +40 -0
- package/frontend/components/ui/tabs.tsx +66 -0
- package/frontend/components/ui/textarea.tsx +18 -0
- package/frontend/components/wallet/ConnectButton.tsx +167 -0
- package/frontend/components/wallet/FaucetButton.tsx +256 -0
- package/frontend/components.json +22 -0
- package/frontend/eslint.config.mjs +18 -0
- package/frontend/hooks/useAICompletion.ts +75 -0
- package/frontend/hooks/useBlockchainLoader.ts +58 -0
- package/frontend/hooks/useChats.ts +137 -0
- package/frontend/hooks/useCompilation.ts +173 -0
- package/frontend/hooks/useFileTabs.ts +178 -0
- package/frontend/hooks/useGitHubLoader.ts +50 -0
- package/frontend/hooks/useKeyboardShortcuts.ts +47 -0
- package/frontend/hooks/usePanelState.ts +115 -0
- package/frontend/hooks/useProjectState.ts +276 -0
- package/frontend/hooks/useResponsive.ts +29 -0
- package/frontend/lib/abi-parser.ts +58 -0
- package/frontend/lib/blockchain-api.ts +374 -0
- package/frontend/lib/blockchain-explorers.ts +75 -0
- package/frontend/lib/blockchain-loader.ts +112 -0
- package/frontend/lib/cargo-template.ts +64 -0
- package/frontend/lib/compilation.ts +529 -0
- package/frontend/lib/constants.ts +31 -0
- package/frontend/lib/deployment.ts +176 -0
- package/frontend/lib/file-utils.ts +83 -0
- package/frontend/lib/github-api.ts +246 -0
- package/frontend/lib/github-loader.ts +369 -0
- package/frontend/lib/ml-contract-template.txt +900 -0
- package/frontend/lib/orbit-chains.ts +181 -0
- package/frontend/lib/output-formatter.ts +68 -0
- package/frontend/lib/project-manager.ts +632 -0
- package/frontend/lib/ray-tracing-abi.ts +206 -0
- package/frontend/lib/storage.ts +189 -0
- package/frontend/lib/templates.ts +1662 -0
- package/frontend/lib/url-parser.ts +188 -0
- package/frontend/lib/utils.ts +6 -0
- package/frontend/lib/wagmi-config.ts +24 -0
- package/frontend/next.config.ts +7 -0
- package/frontend/package-lock.json +16259 -0
- package/frontend/package.json +60 -0
- package/frontend/postcss.config.mjs +7 -0
- package/frontend/public/file.svg +1 -0
- package/frontend/public/globe.svg +1 -0
- package/frontend/public/ml-weights/.gitkeep +0 -0
- package/frontend/public/ml-weights/model.pkl +0 -0
- package/frontend/public/ml-weights/model_weights.json +27102 -0
- package/frontend/public/ml-weights/test_samples.json +7888 -0
- package/frontend/public/next.svg +1 -0
- package/frontend/public/vercel.svg +1 -0
- package/frontend/public/window.svg +1 -0
- package/frontend/scripts/check-env.js +52 -0
- package/frontend/scripts/setup.js +285 -0
- package/frontend/tailwind.config.ts +64 -0
- package/frontend/tsconfig.json +34 -0
- package/frontend/types/blockchain.ts +63 -0
- package/frontend/types/github.ts +54 -0
- package/frontend/types/project.ts +106 -0
- package/ml-training/README.md +56 -0
- package/ml-training/train_tiny_model.py +325 -0
- package/ml-training/update_template.py +59 -0
- package/package.json +30 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { useChainId, useAccount } from 'wagmi';
|
|
5
|
+
import { arbitrumSepolia } from 'wagmi/chains';
|
|
6
|
+
import { Button } from '@/components/ui/button';
|
|
7
|
+
import { Droplet, ExternalLink, Info } from 'lucide-react';
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
DropdownMenuItem,
|
|
12
|
+
DropdownMenuLabel,
|
|
13
|
+
DropdownMenuSeparator,
|
|
14
|
+
DropdownMenuTrigger,
|
|
15
|
+
} from '@/components/ui/dropdown-menu';
|
|
16
|
+
import { Alert, AlertDescription } from '@/components/ui/alert';
|
|
17
|
+
|
|
18
|
+
const FAUCETS = [
|
|
19
|
+
{
|
|
20
|
+
name: 'Triangle Platform',
|
|
21
|
+
url: 'https://faucet.triangleplatform.com/arbitrum/sepolia',
|
|
22
|
+
description: '0.001 ETH - No requirements',
|
|
23
|
+
recommended: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'LearnWeb3 Faucet',
|
|
27
|
+
url: 'https://learnweb3.io/faucets/arbitrum_sepolia',
|
|
28
|
+
description: '0.0001 ETH - GitHub login',
|
|
29
|
+
recommended: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Arbitrum Bridge (Sepolia → Arb Sepolia)',
|
|
33
|
+
url: 'https://bridge.arbitrum.io/?destinationChain=arbitrum-sepolia&sourceChain=sepolia',
|
|
34
|
+
description: 'Bridge from Ethereum Sepolia',
|
|
35
|
+
isBridge: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Alchemy Faucet',
|
|
39
|
+
url: 'https://www.alchemy.com/faucets/arbitrum-sepolia',
|
|
40
|
+
description: 'Requires Alchemy account',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Chainlink Faucet',
|
|
44
|
+
url: 'https://faucets.chain.link/arbitrum-sepolia',
|
|
45
|
+
description: 'Requires mainnet balance',
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// Orbit chain faucets
|
|
49
|
+
{
|
|
50
|
+
name: 'XAI Testnet Faucet',
|
|
51
|
+
url: 'https://faucet.xai.games',
|
|
52
|
+
description: 'Get sXAI testnet tokens',
|
|
53
|
+
isOrbit: true,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'ApeChain Curtis Faucet',
|
|
57
|
+
url: 'https://curtis.hub.caldera.xyz/',
|
|
58
|
+
description: 'Get APE testnet tokens (via Caldera)',
|
|
59
|
+
isOrbit: true,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'Nitrogen Testnet Faucet',
|
|
63
|
+
url: 'https://nitrogen-faucet.altlayer.io/',
|
|
64
|
+
description: 'Get ETH for Nitrogen testnet',
|
|
65
|
+
isOrbit: true,
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Ethereum Sepolia faucets (to bridge from)
|
|
70
|
+
const ETH_SEPOLIA_FAUCETS = [
|
|
71
|
+
{
|
|
72
|
+
name: 'Alchemy Sepolia Faucet',
|
|
73
|
+
url: 'https://www.alchemy.com/faucets/ethereum-sepolia',
|
|
74
|
+
description: 'Get ETH Sepolia first',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'Infura Sepolia Faucet',
|
|
78
|
+
url: 'https://www.infura.io/faucet/sepolia',
|
|
79
|
+
description: 'Alternative Sepolia source',
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export function FaucetButton() {
|
|
84
|
+
const [mounted, setMounted] = useState(false);
|
|
85
|
+
useEffect(() => setMounted(true), []);
|
|
86
|
+
|
|
87
|
+
const chainId = useChainId();
|
|
88
|
+
const { address } = useAccount();
|
|
89
|
+
|
|
90
|
+
// ✅ Prevent hydration mismatch: render nothing until client mounted
|
|
91
|
+
if (!mounted) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const recommendedFaucets = FAUCETS.filter((f) => f.recommended);
|
|
96
|
+
const bridgeFaucets = FAUCETS.filter((f) => f.isBridge);
|
|
97
|
+
const orbitFaucets = FAUCETS.filter((f) => f.isOrbit);
|
|
98
|
+
const otherFaucets = FAUCETS.filter((f) => !f.recommended && !f.isBridge && !f.isOrbit);
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<DropdownMenu>
|
|
102
|
+
<DropdownMenuTrigger asChild>
|
|
103
|
+
<Button variant="outline" size="sm" className="hidden sm:flex">
|
|
104
|
+
<Droplet className="h-4 w-4 mr-2" />
|
|
105
|
+
Faucet
|
|
106
|
+
</Button>
|
|
107
|
+
</DropdownMenuTrigger>
|
|
108
|
+
|
|
109
|
+
<DropdownMenuContent align="end" className="w-80 max-h-96 overflow-y-auto custom-scrollbar">
|
|
110
|
+
<DropdownMenuLabel className="flex items-center gap-2">
|
|
111
|
+
<Droplet className="h-4 w-4" />
|
|
112
|
+
Get Free Testnet ETH
|
|
113
|
+
</DropdownMenuLabel>
|
|
114
|
+
|
|
115
|
+
<div className="px-2 py-2">
|
|
116
|
+
<Alert className="border-blue-500/20 bg-blue-500/10">
|
|
117
|
+
<Info className="h-4 w-4 text-blue-400" />
|
|
118
|
+
<AlertDescription className="text-xs text-blue-400">
|
|
119
|
+
You're on <strong>Arbitrum Sepolia</strong> (testnet) - all ETH is free!
|
|
120
|
+
</AlertDescription>
|
|
121
|
+
</Alert>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<DropdownMenuSeparator />
|
|
125
|
+
|
|
126
|
+
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
|
127
|
+
Direct Faucets (Recommended)
|
|
128
|
+
</DropdownMenuLabel>
|
|
129
|
+
|
|
130
|
+
{recommendedFaucets.map((faucet) => (
|
|
131
|
+
<DropdownMenuItem key={faucet.name} asChild className="cursor-pointer">
|
|
132
|
+
<a
|
|
133
|
+
href={faucet.url}
|
|
134
|
+
target="_blank"
|
|
135
|
+
rel="noopener noreferrer"
|
|
136
|
+
className="flex items-start justify-between w-full"
|
|
137
|
+
>
|
|
138
|
+
<div className="flex-1">
|
|
139
|
+
<div className="font-medium text-sm flex items-center gap-2">
|
|
140
|
+
{faucet.name}
|
|
141
|
+
<span className="text-xs bg-green-500/20 text-green-400 px-1.5 py-0.5 rounded">
|
|
142
|
+
Easy
|
|
143
|
+
</span>
|
|
144
|
+
</div>
|
|
145
|
+
<div className="text-xs text-muted-foreground">{faucet.description}</div>
|
|
146
|
+
</div>
|
|
147
|
+
<ExternalLink className="h-4 w-4 ml-2 shrink-0" />
|
|
148
|
+
</a>
|
|
149
|
+
</DropdownMenuItem>
|
|
150
|
+
))}
|
|
151
|
+
|
|
152
|
+
<DropdownMenuSeparator />
|
|
153
|
+
|
|
154
|
+
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
|
155
|
+
Bridge Method (2 Steps)
|
|
156
|
+
</DropdownMenuLabel>
|
|
157
|
+
|
|
158
|
+
<div className="px-2 py-2 text-xs text-muted-foreground space-y-2">
|
|
159
|
+
<div>
|
|
160
|
+
<strong>Step 1:</strong> Get ETH Sepolia from:
|
|
161
|
+
</div>
|
|
162
|
+
{ETH_SEPOLIA_FAUCETS.map((faucet) => (
|
|
163
|
+
<a
|
|
164
|
+
key={faucet.name}
|
|
165
|
+
href={faucet.url}
|
|
166
|
+
target="_blank"
|
|
167
|
+
rel="noopener noreferrer"
|
|
168
|
+
className="flex items-center gap-2 hover:text-foreground pl-4"
|
|
169
|
+
>
|
|
170
|
+
<ExternalLink className="h-3 w-3" />
|
|
171
|
+
{faucet.name}
|
|
172
|
+
</a>
|
|
173
|
+
))}
|
|
174
|
+
<div className="pt-1">
|
|
175
|
+
<strong>Step 2:</strong> Bridge to Arbitrum Sepolia:
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
{bridgeFaucets.map((faucet) => (
|
|
180
|
+
<DropdownMenuItem key={faucet.name} asChild className="cursor-pointer">
|
|
181
|
+
<a
|
|
182
|
+
href={faucet.url}
|
|
183
|
+
target="_blank"
|
|
184
|
+
rel="noopener noreferrer"
|
|
185
|
+
className="flex items-start justify-between w-full"
|
|
186
|
+
>
|
|
187
|
+
<div className="flex-1">
|
|
188
|
+
<div className="font-medium text-sm">{faucet.name}</div>
|
|
189
|
+
<div className="text-xs text-muted-foreground">{faucet.description}</div>
|
|
190
|
+
</div>
|
|
191
|
+
<ExternalLink className="h-4 w-4 ml-2 shrink-0" />
|
|
192
|
+
</a>
|
|
193
|
+
</DropdownMenuItem>
|
|
194
|
+
))}
|
|
195
|
+
|
|
196
|
+
{/* ✅ Orbit section */}
|
|
197
|
+
{orbitFaucets.length > 0 && (
|
|
198
|
+
<>
|
|
199
|
+
<DropdownMenuSeparator />
|
|
200
|
+
<DropdownMenuLabel className="text-xs text-muted-foreground">
|
|
201
|
+
Orbit Chain Faucets
|
|
202
|
+
</DropdownMenuLabel>
|
|
203
|
+
|
|
204
|
+
{orbitFaucets.map((faucet) => (
|
|
205
|
+
<DropdownMenuItem key={faucet.name} asChild className="cursor-pointer">
|
|
206
|
+
<a
|
|
207
|
+
href={faucet.url}
|
|
208
|
+
target="_blank"
|
|
209
|
+
rel="noopener noreferrer"
|
|
210
|
+
className="flex items-start justify-between w-full"
|
|
211
|
+
>
|
|
212
|
+
<div className="flex-1">
|
|
213
|
+
<div className="font-medium text-sm">{faucet.name}</div>
|
|
214
|
+
<div className="text-xs text-muted-foreground">{faucet.description}</div>
|
|
215
|
+
</div>
|
|
216
|
+
<ExternalLink className="h-4 w-4 ml-2 shrink-0" />
|
|
217
|
+
</a>
|
|
218
|
+
</DropdownMenuItem>
|
|
219
|
+
))}
|
|
220
|
+
</>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
<DropdownMenuSeparator />
|
|
224
|
+
|
|
225
|
+
<DropdownMenuLabel className="text-xs text-muted-foreground">Other Options</DropdownMenuLabel>
|
|
226
|
+
|
|
227
|
+
{otherFaucets.map((faucet) => (
|
|
228
|
+
<DropdownMenuItem key={faucet.name} asChild className="cursor-pointer">
|
|
229
|
+
<a
|
|
230
|
+
href={faucet.url}
|
|
231
|
+
target="_blank"
|
|
232
|
+
rel="noopener noreferrer"
|
|
233
|
+
className="flex items-start justify-between w-full"
|
|
234
|
+
>
|
|
235
|
+
<div className="flex-1">
|
|
236
|
+
<div className="font-medium text-sm">{faucet.name}</div>
|
|
237
|
+
<div className="text-xs text-muted-foreground">{faucet.description}</div>
|
|
238
|
+
</div>
|
|
239
|
+
<ExternalLink className="h-4 w-4 ml-2 shrink-0" />
|
|
240
|
+
</a>
|
|
241
|
+
</DropdownMenuItem>
|
|
242
|
+
))}
|
|
243
|
+
|
|
244
|
+
{address && (
|
|
245
|
+
<>
|
|
246
|
+
<DropdownMenuSeparator />
|
|
247
|
+
<div className="px-2 py-2 text-xs text-muted-foreground">
|
|
248
|
+
<strong>Your Address:</strong>
|
|
249
|
+
<code className="block mt-1 break-all bg-muted p-1 rounded">{address}</code>
|
|
250
|
+
</div>
|
|
251
|
+
</>
|
|
252
|
+
)}
|
|
253
|
+
</DropdownMenuContent>
|
|
254
|
+
</DropdownMenu>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"iconLibrary": "lucide",
|
|
14
|
+
"aliases": {
|
|
15
|
+
"components": "@/components",
|
|
16
|
+
"utils": "@/lib/utils",
|
|
17
|
+
"ui": "@/components/ui",
|
|
18
|
+
"lib": "@/lib",
|
|
19
|
+
"hooks": "@/hooks"
|
|
20
|
+
},
|
|
21
|
+
"registries": {}
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
3
|
+
import nextTs from "eslint-config-next/typescript";
|
|
4
|
+
|
|
5
|
+
const eslintConfig = defineConfig([
|
|
6
|
+
...nextVitals,
|
|
7
|
+
...nextTs,
|
|
8
|
+
// Override default ignores of eslint-config-next.
|
|
9
|
+
globalIgnores([
|
|
10
|
+
// Default ignores of eslint-config-next:
|
|
11
|
+
".next/**",
|
|
12
|
+
"out/**",
|
|
13
|
+
"build/**",
|
|
14
|
+
"next-env.d.ts",
|
|
15
|
+
]),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
|
|
5
|
+
interface UseAICompletionReturn {
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
completion: string;
|
|
8
|
+
error: string | null;
|
|
9
|
+
generateCompletion: (prompt: string, context: string) => Promise<void>;
|
|
10
|
+
clearCompletion: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useAICompletion(): UseAICompletionReturn {
|
|
14
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
15
|
+
const [completion, setCompletion] = useState("");
|
|
16
|
+
const [error, setError] = useState<string | null>(null);
|
|
17
|
+
|
|
18
|
+
const generateCompletion = useCallback(
|
|
19
|
+
async (prompt: string, context: string) => {
|
|
20
|
+
setIsLoading(true);
|
|
21
|
+
setCompletion("");
|
|
22
|
+
setError(null);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch("/api/complete", {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: { "Content-Type": "application/json" },
|
|
28
|
+
body: JSON.stringify({ prompt, context }),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new Error("Failed to generate completion");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const reader = response.body?.getReader();
|
|
36
|
+
const decoder = new TextDecoder();
|
|
37
|
+
|
|
38
|
+
if (!reader) {
|
|
39
|
+
throw new Error("No response body");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let accumulatedText = "";
|
|
43
|
+
|
|
44
|
+
while (true) {
|
|
45
|
+
const { done, value } = await reader.read();
|
|
46
|
+
if (done) break;
|
|
47
|
+
|
|
48
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
49
|
+
accumulatedText += chunk;
|
|
50
|
+
setCompletion(accumulatedText);
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const errorMessage =
|
|
54
|
+
err instanceof Error ? err.message : "Completion failed";
|
|
55
|
+
setError(errorMessage);
|
|
56
|
+
} finally {
|
|
57
|
+
setIsLoading(false);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
[]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const clearCompletion = useCallback(() => {
|
|
64
|
+
setCompletion("");
|
|
65
|
+
setError(null);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
isLoading,
|
|
70
|
+
completion,
|
|
71
|
+
error,
|
|
72
|
+
generateCompletion,
|
|
73
|
+
clearCompletion,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import { ContractInteractionData } from "@/types/blockchain";
|
|
5
|
+
import { BlockchainURLInfo } from "@/lib/url-parser";
|
|
6
|
+
import {
|
|
7
|
+
fetchContractForInteraction,
|
|
8
|
+
BlockchainLoadProgress,
|
|
9
|
+
} from "@/lib/blockchain-loader";
|
|
10
|
+
|
|
11
|
+
export function useBlockchainLoader() {
|
|
12
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
13
|
+
const [progress, setProgress] = useState<BlockchainLoadProgress | null>(null);
|
|
14
|
+
const [error, setError] = useState<string | null>(null);
|
|
15
|
+
|
|
16
|
+
const loadFromBlockchain = useCallback(
|
|
17
|
+
async (
|
|
18
|
+
urlInfo: BlockchainURLInfo
|
|
19
|
+
): Promise<ContractInteractionData | null> => {
|
|
20
|
+
setIsLoading(true);
|
|
21
|
+
setError(null);
|
|
22
|
+
setProgress(null);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const contractData = await fetchContractForInteraction(
|
|
26
|
+
urlInfo,
|
|
27
|
+
(progressUpdate) => {
|
|
28
|
+
setProgress(progressUpdate);
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
return contractData;
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const errorMessage =
|
|
36
|
+
err instanceof Error ? err.message : "Unknown error";
|
|
37
|
+
setError(errorMessage);
|
|
38
|
+
setIsLoading(false);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
[]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const reset = useCallback(() => {
|
|
46
|
+
setIsLoading(false);
|
|
47
|
+
setProgress(null);
|
|
48
|
+
setError(null);
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
isLoading,
|
|
53
|
+
progress,
|
|
54
|
+
error,
|
|
55
|
+
loadFromBlockchain,
|
|
56
|
+
reset,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useRef } from "react";
|
|
4
|
+
|
|
5
|
+
export interface Message {
|
|
6
|
+
id: string;
|
|
7
|
+
role: "user" | "assistant" | "system";
|
|
8
|
+
content: string;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface UseChatReturn {
|
|
13
|
+
messages: Message[];
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
error: string | null;
|
|
16
|
+
sendMessage: (content: string, context?: string) => Promise<void>;
|
|
17
|
+
clearMessages: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function useChat(): UseChatReturn {
|
|
21
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
22
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
23
|
+
const [error, setError] = useState<string | null>(null);
|
|
24
|
+
const abortControllerRef = useRef<AbortController | null>(null);
|
|
25
|
+
|
|
26
|
+
const sendMessage = useCallback(
|
|
27
|
+
async (content: string, context?: string) => {
|
|
28
|
+
if (!content.trim()) return;
|
|
29
|
+
|
|
30
|
+
// Add user message
|
|
31
|
+
const userMessage: Message = {
|
|
32
|
+
id: `user-${Date.now()}`,
|
|
33
|
+
role: "user",
|
|
34
|
+
content: content.trim(),
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
39
|
+
setIsLoading(true);
|
|
40
|
+
setError(null);
|
|
41
|
+
|
|
42
|
+
// Create assistant message placeholder
|
|
43
|
+
const assistantMessageId = `assistant-${Date.now()}`;
|
|
44
|
+
const assistantMessage: Message = {
|
|
45
|
+
id: assistantMessageId,
|
|
46
|
+
role: "assistant",
|
|
47
|
+
content: "",
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Cancel previous request if exists
|
|
55
|
+
if (abortControllerRef.current) {
|
|
56
|
+
abortControllerRef.current.abort();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
abortControllerRef.current = new AbortController();
|
|
60
|
+
|
|
61
|
+
const response = await fetch("/api/chat", {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
messages: messages.map((m) => ({
|
|
66
|
+
role: m.role,
|
|
67
|
+
content: m.content,
|
|
68
|
+
})),
|
|
69
|
+
context,
|
|
70
|
+
}),
|
|
71
|
+
signal: abortControllerRef.current.signal,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
throw new Error("Failed to get response");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const reader = response.body?.getReader();
|
|
79
|
+
const decoder = new TextDecoder();
|
|
80
|
+
|
|
81
|
+
if (!reader) {
|
|
82
|
+
throw new Error("No response body");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let accumulatedContent = "";
|
|
86
|
+
|
|
87
|
+
while (true) {
|
|
88
|
+
const { done, value } = await reader.read();
|
|
89
|
+
if (done) break;
|
|
90
|
+
|
|
91
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
92
|
+
accumulatedContent += chunk;
|
|
93
|
+
|
|
94
|
+
// Update assistant message with accumulated content
|
|
95
|
+
setMessages((prev) =>
|
|
96
|
+
prev.map((msg) =>
|
|
97
|
+
msg.id === assistantMessageId
|
|
98
|
+
? { ...msg, content: accumulatedContent }
|
|
99
|
+
: msg
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
105
|
+
// Request was cancelled, ignore
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const errorMessage =
|
|
110
|
+
err instanceof Error ? err.message : "Failed to send message";
|
|
111
|
+
setError(errorMessage);
|
|
112
|
+
|
|
113
|
+
// Remove failed assistant message
|
|
114
|
+
setMessages((prev) =>
|
|
115
|
+
prev.filter((msg) => msg.id !== assistantMessageId)
|
|
116
|
+
);
|
|
117
|
+
} finally {
|
|
118
|
+
setIsLoading(false);
|
|
119
|
+
abortControllerRef.current = null;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
[messages]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const clearMessages = useCallback(() => {
|
|
126
|
+
setMessages([]);
|
|
127
|
+
setError(null);
|
|
128
|
+
}, []);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
messages,
|
|
132
|
+
isLoading,
|
|
133
|
+
error,
|
|
134
|
+
sendMessage,
|
|
135
|
+
clearMessages,
|
|
136
|
+
};
|
|
137
|
+
}
|