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,280 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
6
|
+
import { Input } from '@/components/ui/input';
|
|
7
|
+
import { Label } from '@/components/ui/label';
|
|
8
|
+
import { Loader2, Coins, ExternalLink, CheckCircle2 } from 'lucide-react';
|
|
9
|
+
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
|
|
10
|
+
import { arbitrumSepolia } from 'wagmi/chains';
|
|
11
|
+
import { RAY_TRACING_ABI } from '@/lib/ray-tracing-abi';
|
|
12
|
+
|
|
13
|
+
interface MintingFormProps {
|
|
14
|
+
nftAddress: string;
|
|
15
|
+
isNftConnected: boolean;
|
|
16
|
+
predictedColors: [number, number, number] | null;
|
|
17
|
+
cameraX: number;
|
|
18
|
+
cameraY: number;
|
|
19
|
+
cameraZ: number;
|
|
20
|
+
onMintSuccess: (tokenId: bigint) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function MintingForm({
|
|
24
|
+
nftAddress,
|
|
25
|
+
isNftConnected,
|
|
26
|
+
predictedColors,
|
|
27
|
+
cameraX,
|
|
28
|
+
cameraY,
|
|
29
|
+
cameraZ,
|
|
30
|
+
onMintSuccess,
|
|
31
|
+
}: MintingFormProps) {
|
|
32
|
+
const { isConnected: walletConnected } = useAccount();
|
|
33
|
+
|
|
34
|
+
// Background colors (can be customized)
|
|
35
|
+
const [bgTopR, setBgTopR] = useState(255);
|
|
36
|
+
const [bgTopG, setBgTopG] = useState(255);
|
|
37
|
+
const [bgTopB, setBgTopB] = useState(255);
|
|
38
|
+
const [bgBottomR, setBgBottomR] = useState(91);
|
|
39
|
+
const [bgBottomG, setBgBottomG] = useState(127);
|
|
40
|
+
const [bgBottomB, setBgBottomB] = useState(213);
|
|
41
|
+
|
|
42
|
+
const { writeContract, data: hash, isPending, error } = useWriteContract();
|
|
43
|
+
const { isLoading: isConfirming, isSuccess, data: receipt } = useWaitForTransactionReceipt({
|
|
44
|
+
hash,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const handleMint = async () => {
|
|
48
|
+
if (!walletConnected || !isNftConnected || !predictedColors) {
|
|
49
|
+
alert('Please connect wallet and generate colors first');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
writeContract({
|
|
55
|
+
address: nftAddress as `0x${string}`,
|
|
56
|
+
abi: RAY_TRACING_ABI,
|
|
57
|
+
functionName: 'mint',
|
|
58
|
+
args: [
|
|
59
|
+
predictedColors[0], // sphere_r
|
|
60
|
+
predictedColors[1], // sphere_g
|
|
61
|
+
predictedColors[2], // sphere_b
|
|
62
|
+
bgTopR, // bg_color1_r
|
|
63
|
+
bgTopG, // bg_color1_g
|
|
64
|
+
bgTopB, // bg_color1_b
|
|
65
|
+
bgBottomR, // bg_color2_r
|
|
66
|
+
bgBottomG, // bg_color2_g
|
|
67
|
+
bgBottomB, // bg_color2_b
|
|
68
|
+
cameraX, // cam_x
|
|
69
|
+
cameraY, // cam_y
|
|
70
|
+
cameraZ, // cam_z
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error('Minting failed:', err);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Extract token ID from transaction logs when successful
|
|
79
|
+
if (isSuccess && receipt && !isPending) {
|
|
80
|
+
// The mint function returns the token_id
|
|
81
|
+
// We'll parse it from the transaction
|
|
82
|
+
const tokenId = BigInt(0); // This will be the new token ID
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
onMintSuccess(tokenId);
|
|
85
|
+
}, 1000);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const explorerUrl = hash
|
|
89
|
+
? `${arbitrumSepolia.blockExplorers.default.url}/tx/${hash}`
|
|
90
|
+
: null;
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Card>
|
|
94
|
+
<CardHeader>
|
|
95
|
+
<CardTitle>Mint NFT</CardTitle>
|
|
96
|
+
<CardDescription>
|
|
97
|
+
Store rendering parameters on-chain as an NFT
|
|
98
|
+
</CardDescription>
|
|
99
|
+
</CardHeader>
|
|
100
|
+
<CardContent className="space-y-4">
|
|
101
|
+
{/* Background Colors */}
|
|
102
|
+
<div className="space-y-3">
|
|
103
|
+
<h4 className="text-sm font-semibold">Background Gradient</h4>
|
|
104
|
+
|
|
105
|
+
{/* Top Color */}
|
|
106
|
+
<div className="flex items-center gap-3">
|
|
107
|
+
<div
|
|
108
|
+
className="w-12 h-12 rounded-md border border-border"
|
|
109
|
+
style={{ backgroundColor: `rgb(${bgTopR}, ${bgTopG}, ${bgTopB})` }}
|
|
110
|
+
/>
|
|
111
|
+
<div className="flex-1 grid grid-cols-3 gap-2">
|
|
112
|
+
<Input
|
|
113
|
+
type="number"
|
|
114
|
+
placeholder="R"
|
|
115
|
+
value={bgTopR}
|
|
116
|
+
onChange={(e) => setBgTopR(Number(e.target.value))}
|
|
117
|
+
min={0}
|
|
118
|
+
max={255}
|
|
119
|
+
className="text-xs"
|
|
120
|
+
/>
|
|
121
|
+
<Input
|
|
122
|
+
type="number"
|
|
123
|
+
placeholder="G"
|
|
124
|
+
value={bgTopG}
|
|
125
|
+
onChange={(e) => setBgTopG(Number(e.target.value))}
|
|
126
|
+
min={0}
|
|
127
|
+
max={255}
|
|
128
|
+
className="text-xs"
|
|
129
|
+
/>
|
|
130
|
+
<Input
|
|
131
|
+
type="number"
|
|
132
|
+
placeholder="B"
|
|
133
|
+
value={bgTopB}
|
|
134
|
+
onChange={(e) => setBgTopB(Number(e.target.value))}
|
|
135
|
+
min={0}
|
|
136
|
+
max={255}
|
|
137
|
+
className="text-xs"
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
<Label className="text-xs text-muted-foreground w-12">Top</Label>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{/* Bottom Color */}
|
|
144
|
+
<div className="flex items-center gap-3">
|
|
145
|
+
<div
|
|
146
|
+
className="w-12 h-12 rounded-md border border-border"
|
|
147
|
+
style={{ backgroundColor: `rgb(${bgBottomR}, ${bgBottomG}, ${bgBottomB})` }}
|
|
148
|
+
/>
|
|
149
|
+
<div className="flex-1 grid grid-cols-3 gap-2">
|
|
150
|
+
<Input
|
|
151
|
+
type="number"
|
|
152
|
+
placeholder="R"
|
|
153
|
+
value={bgBottomR}
|
|
154
|
+
onChange={(e) => setBgBottomR(Number(e.target.value))}
|
|
155
|
+
min={0}
|
|
156
|
+
max={255}
|
|
157
|
+
className="text-xs"
|
|
158
|
+
/>
|
|
159
|
+
<Input
|
|
160
|
+
type="number"
|
|
161
|
+
placeholder="G"
|
|
162
|
+
value={bgBottomG}
|
|
163
|
+
onChange={(e) => setBgBottomG(Number(e.target.value))}
|
|
164
|
+
min={0}
|
|
165
|
+
max={255}
|
|
166
|
+
className="text-xs"
|
|
167
|
+
/>
|
|
168
|
+
<Input
|
|
169
|
+
type="number"
|
|
170
|
+
placeholder="B"
|
|
171
|
+
value={bgBottomB}
|
|
172
|
+
onChange={(e) => setBgBottomB(Number(e.target.value))}
|
|
173
|
+
min={0}
|
|
174
|
+
max={255}
|
|
175
|
+
className="text-xs"
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
<Label className="text-xs text-muted-foreground w-12">Bottom</Label>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
{/* Parameters Summary */}
|
|
183
|
+
<div className="bg-muted p-3 rounded-md text-xs space-y-1">
|
|
184
|
+
<h4 className="font-semibold mb-2">Parameters to Store</h4>
|
|
185
|
+
<div className="grid grid-cols-2 gap-2">
|
|
186
|
+
<div>
|
|
187
|
+
<span className="text-muted-foreground">Sphere RGB:</span>
|
|
188
|
+
<span className="ml-2 font-mono">
|
|
189
|
+
{predictedColors ? `${predictedColors[0]},${predictedColors[1]},${predictedColors[2]}` : 'N/A'}
|
|
190
|
+
</span>
|
|
191
|
+
</div>
|
|
192
|
+
<div>
|
|
193
|
+
<span className="text-muted-foreground">Camera:</span>
|
|
194
|
+
<span className="ml-2 font-mono">
|
|
195
|
+
{cameraX},{cameraY},{cameraZ}
|
|
196
|
+
</span>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
<p className="text-muted-foreground pt-2">
|
|
200
|
+
Total: 21 bytes (9 colors + 12 camera)
|
|
201
|
+
</p>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Mint Button */}
|
|
205
|
+
<Button
|
|
206
|
+
onClick={handleMint}
|
|
207
|
+
disabled={!walletConnected || !isNftConnected || !predictedColors || isPending || isConfirming}
|
|
208
|
+
className="w-full"
|
|
209
|
+
size="lg"
|
|
210
|
+
>
|
|
211
|
+
{isPending || isConfirming ? (
|
|
212
|
+
<>
|
|
213
|
+
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
214
|
+
{isPending ? 'Waiting for signature...' : 'Minting NFT...'}
|
|
215
|
+
</>
|
|
216
|
+
) : (
|
|
217
|
+
<>
|
|
218
|
+
<Coins className="h-4 w-4 mr-2" />
|
|
219
|
+
Mint NFT (~$0.0001)
|
|
220
|
+
</>
|
|
221
|
+
)}
|
|
222
|
+
</Button>
|
|
223
|
+
|
|
224
|
+
{/* Transaction Status */}
|
|
225
|
+
{hash && (
|
|
226
|
+
<div className="space-y-2 pt-4 border-t border-border">
|
|
227
|
+
<div className="flex items-center justify-between text-sm">
|
|
228
|
+
<span className="text-muted-foreground">Transaction:</span>
|
|
229
|
+
<a
|
|
230
|
+
href={explorerUrl || '#'}
|
|
231
|
+
target="_blank"
|
|
232
|
+
rel="noopener noreferrer"
|
|
233
|
+
className="flex items-center gap-1 text-primary hover:underline"
|
|
234
|
+
>
|
|
235
|
+
{hash.slice(0, 6)}...{hash.slice(-4)}
|
|
236
|
+
<ExternalLink className="h-3 w-3" />
|
|
237
|
+
</a>
|
|
238
|
+
</div>
|
|
239
|
+
<div className="flex items-center justify-between text-sm">
|
|
240
|
+
<span className="text-muted-foreground">Status:</span>
|
|
241
|
+
<span className={isSuccess ? 'text-green-500' : 'text-yellow-500'}>
|
|
242
|
+
{isSuccess ? '✓ Success' : '⏳ Confirming...'}
|
|
243
|
+
</span>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
|
|
248
|
+
{isSuccess && (
|
|
249
|
+
<div className="bg-green-500/10 border border-green-500/20 p-3 rounded-md text-sm text-green-400">
|
|
250
|
+
<div className="flex items-center gap-2 mb-1">
|
|
251
|
+
<CheckCircle2 className="h-4 w-4" />
|
|
252
|
+
<p className="font-medium">NFT Minted Successfully!</p>
|
|
253
|
+
</div>
|
|
254
|
+
<p className="text-xs mt-1">
|
|
255
|
+
Your rendering parameters are now stored on-chain. Ready for Step 5: Full rendering!
|
|
256
|
+
</p>
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
{error && (
|
|
261
|
+
<div className="bg-red-500/10 border border-red-500/20 p-3 rounded-md text-sm text-red-400">
|
|
262
|
+
<p className="font-medium">Minting Failed</p>
|
|
263
|
+
<p className="text-xs mt-1">{error.message}</p>
|
|
264
|
+
</div>
|
|
265
|
+
)}
|
|
266
|
+
|
|
267
|
+
{/* Info Box */}
|
|
268
|
+
<div className="bg-blue-500/10 border border-blue-500/20 p-3 rounded-md text-xs text-blue-400">
|
|
269
|
+
<p className="font-medium mb-1">💡 What Happens:</p>
|
|
270
|
+
<ul className="space-y-1">
|
|
271
|
+
<li>• Stores 21 bytes on-chain (colors + camera)</li>
|
|
272
|
+
<li>• Generates unique token ID</li>
|
|
273
|
+
<li>• You own the NFT</li>
|
|
274
|
+
<li>• Anyone can render it later (on-demand)</li>
|
|
275
|
+
</ul>
|
|
276
|
+
</div>
|
|
277
|
+
</CardContent>
|
|
278
|
+
</Card>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import { Label } from '@/components/ui/label';
|
|
7
|
+
import { Slider } from '@/components/ui/slider';
|
|
8
|
+
import { Sparkles, Download } from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
interface RenderCanvasProps {
|
|
11
|
+
predictedColors: [number, number, number] | null;
|
|
12
|
+
cameraX: number;
|
|
13
|
+
setCameraX: (value: number) => void;
|
|
14
|
+
cameraY: number;
|
|
15
|
+
setCameraY: (value: number) => void;
|
|
16
|
+
cameraZ: number;
|
|
17
|
+
setCameraZ: (value: number) => void;
|
|
18
|
+
isRendering: boolean;
|
|
19
|
+
renderedPixels: number[] | null;
|
|
20
|
+
nftAddress: string;
|
|
21
|
+
isNftConnected: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function RenderCanvas({
|
|
25
|
+
predictedColors,
|
|
26
|
+
cameraX,
|
|
27
|
+
setCameraX,
|
|
28
|
+
cameraY,
|
|
29
|
+
setCameraY,
|
|
30
|
+
cameraZ,
|
|
31
|
+
setCameraZ,
|
|
32
|
+
isRendering,
|
|
33
|
+
renderedPixels,
|
|
34
|
+
nftAddress,
|
|
35
|
+
isNftConnected,
|
|
36
|
+
}: RenderCanvasProps) {
|
|
37
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
38
|
+
|
|
39
|
+
// Client-side simple sphere preview
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!predictedColors || !canvasRef.current) return;
|
|
42
|
+
|
|
43
|
+
const canvas = canvasRef.current;
|
|
44
|
+
const ctx = canvas.getContext('2d');
|
|
45
|
+
if (!ctx) return;
|
|
46
|
+
|
|
47
|
+
const width = 256;
|
|
48
|
+
const height = 256;
|
|
49
|
+
canvas.width = width;
|
|
50
|
+
canvas.height = height;
|
|
51
|
+
|
|
52
|
+
// Clear canvas
|
|
53
|
+
ctx.fillStyle = '#0a0a0a';
|
|
54
|
+
ctx.fillRect(0, 0, width, height);
|
|
55
|
+
|
|
56
|
+
// Draw gradient background
|
|
57
|
+
const bgGradient = ctx.createLinearGradient(0, 0, 0, height);
|
|
58
|
+
bgGradient.addColorStop(0, '#ffffff');
|
|
59
|
+
bgGradient.addColorStop(1, '#5b7fd5');
|
|
60
|
+
ctx.fillStyle = bgGradient;
|
|
61
|
+
ctx.fillRect(0, 0, width, height);
|
|
62
|
+
|
|
63
|
+
// Draw sphere with radial gradient (simulating 3D lighting)
|
|
64
|
+
const centerX = width / 2 + cameraX * 10;
|
|
65
|
+
const centerY = height / 2 - cameraY * 10;
|
|
66
|
+
const radius = 80 + cameraZ * 5;
|
|
67
|
+
|
|
68
|
+
const gradient = ctx.createRadialGradient(
|
|
69
|
+
centerX - radius * 0.3,
|
|
70
|
+
centerY - radius * 0.3,
|
|
71
|
+
radius * 0.1,
|
|
72
|
+
centerX,
|
|
73
|
+
centerY,
|
|
74
|
+
radius
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const [r, g, b] = predictedColors;
|
|
78
|
+
|
|
79
|
+
// Light side (top-left)
|
|
80
|
+
gradient.addColorStop(0, `rgb(${Math.min(255, r + 40)}, ${Math.min(255, g + 40)}, ${Math.min(255, b + 40)})`);
|
|
81
|
+
// Mid tone
|
|
82
|
+
gradient.addColorStop(0.5, `rgb(${r}, ${g}, ${b})`);
|
|
83
|
+
// Shadow (bottom-right)
|
|
84
|
+
gradient.addColorStop(1, `rgb(${Math.floor(r * 0.3)}, ${Math.floor(g * 0.3)}, ${Math.floor(b * 0.3)})`);
|
|
85
|
+
|
|
86
|
+
ctx.fillStyle = gradient;
|
|
87
|
+
ctx.beginPath();
|
|
88
|
+
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
|
89
|
+
ctx.fill();
|
|
90
|
+
|
|
91
|
+
// Add subtle highlight
|
|
92
|
+
const highlight = ctx.createRadialGradient(
|
|
93
|
+
centerX - radius * 0.4,
|
|
94
|
+
centerY - radius * 0.4,
|
|
95
|
+
0,
|
|
96
|
+
centerX - radius * 0.4,
|
|
97
|
+
centerY - radius * 0.4,
|
|
98
|
+
radius * 0.3
|
|
99
|
+
);
|
|
100
|
+
highlight.addColorStop(0, 'rgba(255, 255, 255, 0.6)');
|
|
101
|
+
highlight.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
|
102
|
+
|
|
103
|
+
ctx.fillStyle = highlight;
|
|
104
|
+
ctx.beginPath();
|
|
105
|
+
ctx.arc(centerX - radius * 0.3, centerY - radius * 0.3, radius * 0.3, 0, Math.PI * 2);
|
|
106
|
+
ctx.fill();
|
|
107
|
+
|
|
108
|
+
}, [predictedColors, cameraX, cameraY, cameraZ]);
|
|
109
|
+
|
|
110
|
+
const handleDownload = () => {
|
|
111
|
+
if (!canvasRef.current) return;
|
|
112
|
+
|
|
113
|
+
const link = document.createElement('a');
|
|
114
|
+
link.download = 'ray-tracing-preview.png';
|
|
115
|
+
link.href = canvasRef.current.toDataURL();
|
|
116
|
+
link.click();
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<Card>
|
|
121
|
+
<CardHeader>
|
|
122
|
+
<CardTitle>3D Render Preview</CardTitle>
|
|
123
|
+
<CardDescription>
|
|
124
|
+
{predictedColors
|
|
125
|
+
? 'Client-side preview with predicted colors from MNN'
|
|
126
|
+
: 'Preview colors with neural network first'
|
|
127
|
+
}
|
|
128
|
+
</CardDescription>
|
|
129
|
+
</CardHeader>
|
|
130
|
+
<CardContent className="space-y-4">
|
|
131
|
+
{/* Canvas */}
|
|
132
|
+
<div className="flex items-center justify-center bg-muted rounded-md p-4">
|
|
133
|
+
{predictedColors ? (
|
|
134
|
+
<canvas
|
|
135
|
+
ref={canvasRef}
|
|
136
|
+
className="border border-border rounded-md"
|
|
137
|
+
style={{ imageRendering: 'pixelated' }}
|
|
138
|
+
/>
|
|
139
|
+
) : (
|
|
140
|
+
<div className="flex items-center justify-center h-64 w-64 text-muted-foreground">
|
|
141
|
+
<div className="text-center">
|
|
142
|
+
<Sparkles className="h-12 w-12 mx-auto mb-3 opacity-50" />
|
|
143
|
+
<p className="text-sm">Generate preview first</p>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
{/* Camera Controls */}
|
|
150
|
+
{predictedColors && (
|
|
151
|
+
<>
|
|
152
|
+
<div className="space-y-3 pt-4 border-t border-border">
|
|
153
|
+
<h4 className="text-sm font-semibold">Camera Position</h4>
|
|
154
|
+
|
|
155
|
+
{/* Camera X */}
|
|
156
|
+
<div className="space-y-2">
|
|
157
|
+
<div className="flex items-center justify-between">
|
|
158
|
+
<Label className="text-xs">X-Axis (Left/Right)</Label>
|
|
159
|
+
<span className="text-xs text-muted-foreground">{cameraX}</span>
|
|
160
|
+
</div>
|
|
161
|
+
<Slider
|
|
162
|
+
value={[cameraX]}
|
|
163
|
+
onValueChange={(value) => setCameraX(value[0])}
|
|
164
|
+
min={-20}
|
|
165
|
+
max={20}
|
|
166
|
+
step={1}
|
|
167
|
+
className="w-full"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
{/* Camera Y */}
|
|
172
|
+
<div className="space-y-2">
|
|
173
|
+
<div className="flex items-center justify-between">
|
|
174
|
+
<Label className="text-xs">Y-Axis (Up/Down)</Label>
|
|
175
|
+
<span className="text-xs text-muted-foreground">{cameraY}</span>
|
|
176
|
+
</div>
|
|
177
|
+
<Slider
|
|
178
|
+
value={[cameraY]}
|
|
179
|
+
onValueChange={(value) => setCameraY(value[0])}
|
|
180
|
+
min={-20}
|
|
181
|
+
max={20}
|
|
182
|
+
step={1}
|
|
183
|
+
className="w-full"
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
{/* Camera Z */}
|
|
188
|
+
<div className="space-y-2">
|
|
189
|
+
<div className="flex items-center justify-between">
|
|
190
|
+
<Label className="text-xs">Z-Axis (Zoom)</Label>
|
|
191
|
+
<span className="text-xs text-muted-foreground">{cameraZ}</span>
|
|
192
|
+
</div>
|
|
193
|
+
<Slider
|
|
194
|
+
value={[cameraZ]}
|
|
195
|
+
onValueChange={(value) => setCameraZ(value[0])}
|
|
196
|
+
min={-10}
|
|
197
|
+
max={10}
|
|
198
|
+
step={1}
|
|
199
|
+
className="w-full"
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Download Button */}
|
|
205
|
+
<Button
|
|
206
|
+
onClick={handleDownload}
|
|
207
|
+
variant="outline"
|
|
208
|
+
className="w-full"
|
|
209
|
+
size="sm"
|
|
210
|
+
>
|
|
211
|
+
<Download className="h-4 w-4 mr-2" />
|
|
212
|
+
Download Preview
|
|
213
|
+
</Button>
|
|
214
|
+
|
|
215
|
+
{/* Info about on-chain rendering */}
|
|
216
|
+
<div className="bg-yellow-500/10 border border-yellow-500/20 p-3 rounded-md text-xs text-yellow-400">
|
|
217
|
+
<p className="font-medium mb-1">ℹ️ Client-Side Preview</p>
|
|
218
|
+
<p>
|
|
219
|
+
This is a simplified preview. For full on-chain ray tracing with proper lighting and shadows,
|
|
220
|
+
mint an NFT and render on-chain (Step 4).
|
|
221
|
+
</p>
|
|
222
|
+
</div>
|
|
223
|
+
</>
|
|
224
|
+
)}
|
|
225
|
+
</CardContent>
|
|
226
|
+
</Card>
|
|
227
|
+
);
|
|
228
|
+
}
|