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,125 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import {
5
+ Dialog,
6
+ DialogContent,
7
+ DialogDescription,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DialogTrigger,
11
+ } from '@/components/ui/dialog';
12
+ import { Button } from '@/components/ui/button';
13
+ import { Input } from '@/components/ui/input';
14
+ import { Label } from '@/components/ui/label';
15
+ import { Github, Loader2 } from 'lucide-react';
16
+ import { parseURL } from '@/lib/url-parser';
17
+
18
+ interface LoadFromGitHubDialogProps {
19
+ onLoadURL: (url: string) => void;
20
+ isLoading?: boolean;
21
+ }
22
+
23
+ export function LoadFromGitHubDialog({ onLoadURL, isLoading }: LoadFromGitHubDialogProps) {
24
+ const [open, setOpen] = useState(false);
25
+ const [url, setUrl] = useState('');
26
+ const [error, setError] = useState('');
27
+
28
+ const handleSubmit = (e: React.FormEvent) => {
29
+ e.preventDefault();
30
+ setError('');
31
+
32
+ if (!url.trim()) {
33
+ setError('Please enter a GitHub URL');
34
+ return;
35
+ }
36
+
37
+ const parsed = parseURL(url.trim());
38
+
39
+ if (parsed.type !== 'github') {
40
+ setError('Please enter a valid GitHub repository URL');
41
+ return;
42
+ }
43
+
44
+ onLoadURL(url.trim());
45
+ setOpen(false);
46
+ setUrl('');
47
+ };
48
+
49
+ const exampleRepos = [
50
+ {
51
+ name: 'Stylus Hello World',
52
+ url: 'https://github.com/OffchainLabs/stylus-hello-world',
53
+ },
54
+ {
55
+ name: 'Stylus Workshop',
56
+ url: 'https://github.com/OffchainLabs/stylus-workshop-rust-solidity',
57
+ },
58
+ ];
59
+
60
+ return (
61
+ <Dialog open={open} onOpenChange={setOpen}>
62
+ <DialogTrigger asChild>
63
+ <Button variant="outline" size="sm" className="gap-2">
64
+ <Github className="h-4 w-4" />
65
+ <span className="hidden md:inline">Load from GitHub</span>
66
+ </Button>
67
+ </DialogTrigger>
68
+
69
+ <DialogContent className="sm:max-w-md">
70
+ <DialogHeader>
71
+ <DialogTitle>Load from GitHub</DialogTitle>
72
+ <DialogDescription>
73
+ Enter a GitHub repository URL to load it into the IDE
74
+ </DialogDescription>
75
+ </DialogHeader>
76
+
77
+ <form onSubmit={handleSubmit} className="space-y-4">
78
+ <div className="space-y-2">
79
+ <Label htmlFor="github-url">Repository URL</Label>
80
+ <Input
81
+ id="github-url"
82
+ placeholder="https://github.com/owner/repo"
83
+ value={url}
84
+ onChange={(e) => {
85
+ setUrl(e.target.value);
86
+ setError('');
87
+ }}
88
+ disabled={isLoading}
89
+ />
90
+ {error && <p className="text-sm text-red-500">{error}</p>}
91
+ </div>
92
+
93
+ {/* Example Repos */}
94
+ <div className="space-y-2">
95
+ <Label className="text-xs text-muted-foreground">Quick Examples:</Label>
96
+ <div className="space-y-1">
97
+ {exampleRepos.map((repo) => (
98
+ <button
99
+ key={repo.url}
100
+ type="button"
101
+ onClick={() => setUrl(repo.url)}
102
+ className="block w-full text-left text-xs text-blue-500 hover:text-blue-600 hover:underline"
103
+ disabled={isLoading}
104
+ >
105
+ {repo.name}
106
+ </button>
107
+ ))}
108
+ </div>
109
+ </div>
110
+
111
+ <Button type="submit" className="w-full" disabled={isLoading}>
112
+ {isLoading ? (
113
+ <>
114
+ <Loader2 className="h-4 w-4 mr-2 animate-spin" />
115
+ Loading...
116
+ </>
117
+ ) : (
118
+ 'Load Repository'
119
+ )}
120
+ </Button>
121
+ </form>
122
+ </DialogContent>
123
+ </Dialog>
124
+ );
125
+ }
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Button } from '@/components/ui/button';
5
+ import { Check, Link2 } from 'lucide-react';
6
+ import { toast } from 'sonner';
7
+
8
+ interface URLCopyButtonProps {
9
+ githubUrl: string;
10
+ branch?: string;
11
+ file?: string;
12
+ folderPath?: string;
13
+ }
14
+
15
+ export function URLCopyButton({ githubUrl, branch, file, folderPath }: URLCopyButtonProps) {
16
+ const [copied, setCopied] = useState(false);
17
+
18
+ const handleCopy = () => {
19
+ // Build shareable URL
20
+ const baseUrl = window.location.origin;
21
+ const params = new URLSearchParams();
22
+
23
+ params.set('url', githubUrl);
24
+ if (branch) params.set('branch', branch);
25
+ if (file) params.set('file', file);
26
+ if (folderPath) params.set('path', folderPath);
27
+
28
+ const shareUrl = `${baseUrl}/?${params.toString()}`;
29
+
30
+ navigator.clipboard.writeText(shareUrl);
31
+ setCopied(true);
32
+
33
+ toast.success('Link copied!', {
34
+ description: 'Share this URL to open this exact project state',
35
+ });
36
+
37
+ setTimeout(() => setCopied(false), 2000);
38
+ };
39
+
40
+ return (
41
+ <Button
42
+ variant="ghost"
43
+ size="sm"
44
+ className="h-6 text-xs gap-1"
45
+ onClick={handleCopy}
46
+ >
47
+ {copied ? (
48
+ <>
49
+ <Check className="h-3 w-3" />
50
+ Copied
51
+ </>
52
+ ) : (
53
+ <>
54
+ <Link2 className="h-3 w-3" />
55
+ Share
56
+ </>
57
+ )}
58
+ </Button>
59
+ );
60
+ }
@@ -0,0 +1,323 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
5
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
6
+ import { Button } from '@/components/ui/button';
7
+ import { Input } from '@/components/ui/input';
8
+ import { Label } from '@/components/ui/label';
9
+ import { BookOpen, Edit, Loader2, ExternalLink } from 'lucide-react';
10
+ import { useAccount, useChainId, usePublicClient, useWalletClient } from 'wagmi';
11
+ import { parseABI, groupFunctionsByType, formatFunctionSignature, ParsedFunction } from '@/lib/abi-parser';
12
+ import { encodeFunctionData, decodeFunctionResult, type Address } from 'viem';
13
+ import { arbitrum, arbitrumSepolia } from 'wagmi/chains';
14
+
15
+ interface ContractInteractionProps {
16
+ contractAddress: string;
17
+ abi: string;
18
+ }
19
+
20
+ export function ContractInteraction({ contractAddress, abi }: ContractInteractionProps) {
21
+ const [inputValues, setInputValues] = useState<Record<string, string[]>>({});
22
+ const [results, setResults] = useState<Record<string, any>>({});
23
+ const [loading, setLoading] = useState<Record<string, boolean>>({});
24
+
25
+ const { address: userAddress, isConnected } = useAccount();
26
+ const chainId = useChainId();
27
+ const publicClient = usePublicClient();
28
+ const { data: walletClient } = useWalletClient();
29
+
30
+ const functions = parseABI(abi);
31
+ const { read: readFunctions, write: writeFunctions } = groupFunctionsByType(functions);
32
+
33
+ const getExplorerUrl = () => {
34
+ if (chainId === arbitrumSepolia.id) {
35
+ return `https://sepolia.arbiscan.io/address/${contractAddress}`;
36
+ }
37
+ if (chainId === arbitrum.id) {
38
+ return `https://arbiscan.io/address/${contractAddress}`;
39
+ }
40
+ return `https://sepolia.arbiscan.io/address/${contractAddress}`;
41
+ };
42
+
43
+ const handleInputChange = (funcName: string, index: number, value: string) => {
44
+ setInputValues((prev) => ({
45
+ ...prev,
46
+ [funcName]: {
47
+ ...prev[funcName],
48
+ [index]: value,
49
+ },
50
+ }));
51
+ };
52
+
53
+ const getInputValues = (funcName: string, func: ParsedFunction): any[] => {
54
+ const values = inputValues[funcName] || [];
55
+ return func.inputs.map((input, index) => {
56
+ const value = values[index] || '';
57
+
58
+ // Convert based on type
59
+ if (input.type.includes('uint') || input.type.includes('int')) {
60
+ return value ? BigInt(value) : BigInt(0);
61
+ }
62
+ if (input.type === 'bool') {
63
+ return value.toLowerCase() === 'true';
64
+ }
65
+ if (input.type === 'address') {
66
+ return value as Address;
67
+ }
68
+ if (input.type.includes('bytes')) {
69
+ return value as `0x${string}`;
70
+ }
71
+ return value;
72
+ });
73
+ };
74
+
75
+ const handleRead = async (func: ParsedFunction) => {
76
+ if (!publicClient) return;
77
+
78
+ const funcKey = func.name;
79
+ setLoading((prev) => ({ ...prev, [funcKey]: true }));
80
+ setResults((prev) => ({ ...prev, [funcKey]: undefined }));
81
+
82
+ try {
83
+ const args = getInputValues(funcKey, func);
84
+
85
+ const data = await publicClient.readContract({
86
+ address: contractAddress as Address,
87
+ abi: [func],
88
+ functionName: func.name,
89
+ args: args.length > 0 ? args : undefined,
90
+ });
91
+
92
+ setResults((prev) => ({
93
+ ...prev,
94
+ [funcKey]: { success: true, data },
95
+ }));
96
+ } catch (error) {
97
+ console.error('Read error:', error);
98
+ setResults((prev) => ({
99
+ ...prev,
100
+ [funcKey]: {
101
+ success: false,
102
+ error: error instanceof Error ? error.message : 'Read failed',
103
+ },
104
+ }));
105
+ } finally {
106
+ setLoading((prev) => ({ ...prev, [funcKey]: false }));
107
+ }
108
+ };
109
+
110
+ const handleWrite = async (func: ParsedFunction) => {
111
+ if (!walletClient || !userAddress) {
112
+ alert('Please connect your wallet');
113
+ return;
114
+ }
115
+
116
+ const funcKey = func.name;
117
+ setLoading((prev) => ({ ...prev, [funcKey]: true }));
118
+ setResults((prev) => ({ ...prev, [funcKey]: undefined }));
119
+
120
+ try {
121
+ const args = getInputValues(funcKey, func);
122
+
123
+ const hash = await walletClient.writeContract({
124
+ address: contractAddress as Address,
125
+ abi: [func],
126
+ functionName: func.name,
127
+ args: args.length > 0 ? args : undefined,
128
+ account: userAddress,
129
+ });
130
+
131
+ // Wait for transaction
132
+ if (publicClient) {
133
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
134
+
135
+ setResults((prev) => ({
136
+ ...prev,
137
+ [funcKey]: {
138
+ success: true,
139
+ txHash: hash,
140
+ status: receipt.status,
141
+ },
142
+ }));
143
+ } else {
144
+ setResults((prev) => ({
145
+ ...prev,
146
+ [funcKey]: { success: true, txHash: hash },
147
+ }));
148
+ }
149
+ } catch (error) {
150
+ console.error('Write error:', error);
151
+ setResults((prev) => ({
152
+ ...prev,
153
+ [funcKey]: {
154
+ success: false,
155
+ error: error instanceof Error ? error.message : 'Transaction failed',
156
+ },
157
+ }));
158
+ } finally {
159
+ setLoading((prev) => ({ ...prev, [funcKey]: false }));
160
+ }
161
+ };
162
+
163
+ const renderFunctionCard = (func: ParsedFunction, isWrite: boolean) => {
164
+ const funcKey = func.name;
165
+ const isLoading = loading[funcKey];
166
+ const result = results[funcKey];
167
+
168
+ return (
169
+ <Card key={func.name}>
170
+ <CardHeader className="pb-3">
171
+ <CardTitle className="text-base font-mono">{func.name}</CardTitle>
172
+ <CardDescription className="text-xs">
173
+ {formatFunctionSignature(func)}
174
+ </CardDescription>
175
+ </CardHeader>
176
+ <CardContent className="space-y-3">
177
+ {func.inputs.length > 0 && (
178
+ <div className="space-y-2">
179
+ {func.inputs.map((input, index) => (
180
+ <div key={index} className="space-y-1">
181
+ <Label className="text-xs">
182
+ {input.name || `param${index}`} ({input.type})
183
+ </Label>
184
+ <Input
185
+ placeholder={`Enter ${input.type}`}
186
+ value={inputValues[funcKey]?.[index] || ''}
187
+ onChange={(e) =>
188
+ handleInputChange(funcKey, index, e.target.value)
189
+ }
190
+ disabled={isLoading}
191
+ className="h-8 text-sm"
192
+ />
193
+ </div>
194
+ ))}
195
+ </div>
196
+ )}
197
+
198
+ <Button
199
+ onClick={() => (isWrite ? handleWrite(func) : handleRead(func))}
200
+ disabled={isLoading || (isWrite && !isConnected)}
201
+ size="sm"
202
+ variant={isWrite ? 'default' : 'outline'}
203
+ className="w-full"
204
+ >
205
+ {isLoading ? (
206
+ <>
207
+ <Loader2 className="h-3 w-3 mr-2 animate-spin" />
208
+ {isWrite ? 'Sending...' : 'Reading...'}
209
+ </>
210
+ ) : (
211
+ <>{isWrite ? 'Write' : 'Read'}</>
212
+ )}
213
+ </Button>
214
+
215
+ {result && (
216
+ <div
217
+ className={`p-3 rounded-md text-xs ${result.success
218
+ ? 'bg-green-500/10 border border-green-500/20'
219
+ : 'bg-red-500/10 border border-red-500/20'
220
+ }`}
221
+ >
222
+ {result.success ? (
223
+ <div className="space-y-1">
224
+ {result.data !== undefined && (
225
+ <div>
226
+ <span className="font-medium">Result: </span>
227
+ <code className="break-all">
228
+ {typeof result.data === 'bigint'
229
+ ? result.data.toString()
230
+ : JSON.stringify(result.data)}
231
+ </code>
232
+ </div>
233
+ )}
234
+ {result.txHash && (
235
+ <div className="flex items-center gap-2">
236
+ <span className="font-medium">Tx: </span>
237
+ <code className="flex-1 break-all">{result.txHash}</code>
238
+ <a
239
+ href={`${getExplorerUrl().replace('/address/', '/tx/')}/${result.txHash}`}
240
+ target="_blank"
241
+ rel="noopener noreferrer"
242
+ className="text-blue-400 hover:text-blue-300"
243
+ >
244
+ <ExternalLink className="h-3 w-3" />
245
+ </a>
246
+ </div>
247
+ )}
248
+ {result.status && (
249
+ <div>
250
+ <span className="font-medium">Status: </span>
251
+ {result.status === 'success' ? '✓ Success' : '✗ Failed'}
252
+ </div>
253
+ )}
254
+ </div>
255
+ ) : (
256
+ <div className="text-red-400">{result.error}</div>
257
+ )}
258
+ </div>
259
+ )}
260
+ </CardContent>
261
+ </Card>
262
+ );
263
+ };
264
+
265
+ return (
266
+ <div className="h-full flex flex-col">
267
+ <div className="p-4 border-b border-border space-y-2">
268
+ <div className="flex items-center justify-between">
269
+ <h2 className="font-semibold">Contract Interaction</h2>
270
+ <a
271
+ href={getExplorerUrl()}
272
+ target="_blank"
273
+ rel="noopener noreferrer"
274
+ className="text-xs text-muted-foreground hover:text-foreground flex items-center gap-1"
275
+ >
276
+ View on Explorer
277
+ <ExternalLink className="h-3 w-3" />
278
+ </a>
279
+ </div>
280
+ <div className="text-xs font-mono text-muted-foreground break-all">
281
+ {contractAddress}
282
+ </div>
283
+ </div>
284
+
285
+ <Tabs defaultValue="read" className="flex-1 flex flex-col min-h-0">
286
+ <TabsList className="mx-4 mt-4 grid w-[calc(100%-2rem)] grid-cols-2">
287
+ <TabsTrigger value="read" className="flex items-center gap-2">
288
+ <BookOpen className="h-4 w-4" />
289
+ Read ({readFunctions.length})
290
+ </TabsTrigger>
291
+ <TabsTrigger value="write" className="flex items-center gap-2">
292
+ <Edit className="h-4 w-4" />
293
+ Write ({writeFunctions.length})
294
+ </TabsTrigger>
295
+ </TabsList>
296
+
297
+ <TabsContent value="read" className="flex-1 overflow-auto p-4 space-y-3 mt-0">
298
+ {readFunctions.length === 0 ? (
299
+ <p className="text-sm text-muted-foreground text-center py-8">
300
+ No read functions available
301
+ </p>
302
+ ) : (
303
+ readFunctions.map((func) => renderFunctionCard(func, false))
304
+ )}
305
+ </TabsContent>
306
+
307
+ <TabsContent value="write" className="flex-1 overflow-auto p-4 space-y-3 mt-0">
308
+ {!isConnected ? (
309
+ <p className="text-sm text-muted-foreground text-center py-8">
310
+ Connect your wallet to write to the contract
311
+ </p>
312
+ ) : writeFunctions.length === 0 ? (
313
+ <p className="text-sm text-muted-foreground text-center py-8">
314
+ No write functions available
315
+ </p>
316
+ ) : (
317
+ writeFunctions.map((func) => renderFunctionCard(func, true))
318
+ )}
319
+ </TabsContent>
320
+ </Tabs>
321
+ </div>
322
+ );
323
+ }
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { FileText, Zap } from 'lucide-react';
4
+
5
+ export function ContractPlaceholder() {
6
+ return (
7
+ <div className="h-full flex flex-col bg-card">
8
+ {/* Header */}
9
+ <div className="p-3 sm:p-4 border-b border-border flex items-center justify-between shrink-0">
10
+ <div className="flex items-center gap-2">
11
+ <FileText className="h-4 w-4 sm:h-5 sm:w-5 text-primary" />
12
+ <h2 className="font-semibold text-sm sm:text-base">Contract Interaction</h2>
13
+ </div>
14
+ </div>
15
+
16
+ {/* Placeholder Content */}
17
+ <div className="flex-1 flex items-center justify-center p-6">
18
+ <div className="text-center space-y-4 max-w-sm">
19
+ <div className="w-16 h-16 mx-auto bg-muted rounded-full flex items-center justify-center">
20
+ <Zap className="h-8 w-8 text-muted-foreground" />
21
+ </div>
22
+ <div className="space-y-2">
23
+ <h3 className="font-semibold text-lg">No Contract Deployed</h3>
24
+ <p className="text-sm text-muted-foreground">
25
+ Deploy a contract to interact with its functions. Once deployed, you'll be able to read from and write to your smart contract.
26
+ </p>
27
+ </div>
28
+ <div className="text-xs text-muted-foreground bg-muted/50 rounded-lg p-3">
29
+ <p className="font-medium mb-1">To get started:</p>
30
+ <ol className="text-left space-y-1">
31
+ <li>1. Write your Stylus contract</li>
32
+ <li>2. Compile successfully</li>
33
+ <li>3. Connect your wallet</li>
34
+ <li>4. Click Deploy</li>
35
+ </ol>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ );
41
+ }