@blu1606/create-walrus-app 1.0.0 → 2.1.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 (107) hide show
  1. package/dist/generator/file-ops.d.ts +8 -0
  2. package/dist/generator/file-ops.js +20 -0
  3. package/dist/generator/index.js +37 -22
  4. package/dist/generator/layers.d.ts +15 -2
  5. package/dist/generator/layers.js +38 -47
  6. package/dist/generator/types.d.ts +9 -1
  7. package/dist/index.js +1 -2
  8. package/dist/post-install/git.d.ts +8 -0
  9. package/dist/post-install/git.js +2 -0
  10. package/dist/post-install/index.d.ts +0 -1
  11. package/dist/post-install/index.js +7 -15
  12. package/dist/post-install/messages.js +1 -1
  13. package/dist/post-install/walrus-deploy.d.ts +6 -0
  14. package/dist/post-install/walrus-deploy.js +77 -0
  15. package/package.json +3 -3
  16. package/{templates/base → presets/react-mysten-gallery}/.env.example +31 -31
  17. package/presets/react-mysten-gallery/.gitkeep +4 -0
  18. package/presets/react-mysten-gallery/README.md +107 -0
  19. package/presets/react-mysten-gallery/package.json +35 -0
  20. package/presets/react-mysten-gallery/scripts/setup-walrus-deploy.sh +286 -0
  21. package/presets/react-mysten-gallery/src/App.tsx +23 -0
  22. package/presets/react-mysten-gallery/src/components/features/file-card.tsx +89 -0
  23. package/{templates/gallery/src/components/GalleryGrid.tsx → presets/react-mysten-gallery/src/components/features/gallery-grid.tsx} +5 -5
  24. package/presets/react-mysten-gallery/src/components/features/upload-modal.tsx +69 -0
  25. package/{templates/react/src/components/WalletConnect.tsx → presets/react-mysten-gallery/src/components/features/wallet-connect.tsx} +1 -1
  26. package/presets/react-mysten-gallery/src/components/layout/app-layout.tsx +21 -0
  27. package/{templates/react/src/hooks/useStorage.ts → presets/react-mysten-gallery/src/hooks/use-download.ts} +2 -18
  28. package/presets/react-mysten-gallery/src/hooks/use-upload.ts +49 -0
  29. package/{templates/react/src/hooks/useWallet.ts → presets/react-mysten-gallery/src/hooks/use-wallet.ts} +2 -7
  30. package/presets/react-mysten-gallery/src/index.css +384 -0
  31. package/presets/react-mysten-gallery/src/index.ts +17 -0
  32. package/presets/react-mysten-gallery/src/lib/walrus/adapter.ts +197 -0
  33. package/presets/react-mysten-gallery/src/lib/walrus/client.ts +87 -0
  34. package/presets/react-mysten-gallery/src/lib/walrus/index.ts +4 -0
  35. package/presets/react-mysten-gallery/src/lib/walrus/types.ts +101 -0
  36. package/{templates/react → presets/react-mysten-gallery}/src/main.tsx +0 -1
  37. package/{templates/react → presets/react-mysten-gallery}/src/providers/WalletProvider.tsx +16 -1
  38. package/{templates/base → presets/react-mysten-gallery}/src/utils/env.ts +41 -41
  39. package/{templates/gallery → presets/react-mysten-gallery}/src/utils/index-manager.ts +2 -2
  40. package/presets/react-mysten-gallery/src/utils/mime-type.ts +97 -0
  41. package/presets/react-mysten-gallery/src/utils/preview-generator.ts +134 -0
  42. package/{templates/react → presets/react-mysten-gallery}/tsconfig.json +20 -8
  43. package/presets/react-mysten-simple-upload/.env.example +31 -0
  44. package/presets/react-mysten-simple-upload/.gitkeep +4 -0
  45. package/presets/react-mysten-simple-upload/README.md +84 -0
  46. package/presets/react-mysten-simple-upload/index.html +13 -0
  47. package/{templates/react → presets/react-mysten-simple-upload}/package.json +15 -12
  48. package/presets/react-mysten-simple-upload/scripts/setup-walrus-deploy.sh +286 -0
  49. package/presets/react-mysten-simple-upload/src/App.tsx +27 -0
  50. package/presets/react-mysten-simple-upload/src/components/features/file-preview.tsx +73 -0
  51. package/{templates/simple-upload/src/components/UploadForm.tsx → presets/react-mysten-simple-upload/src/components/features/upload-form.tsx} +15 -5
  52. package/presets/react-mysten-simple-upload/src/components/features/wallet-connect.tsx +21 -0
  53. package/presets/react-mysten-simple-upload/src/components/layout/app-layout.tsx +21 -0
  54. package/presets/react-mysten-simple-upload/src/hooks/use-download.ts +24 -0
  55. package/presets/react-mysten-simple-upload/src/hooks/use-upload.ts +49 -0
  56. package/presets/react-mysten-simple-upload/src/hooks/use-wallet.ts +11 -0
  57. package/presets/react-mysten-simple-upload/src/index.css +252 -0
  58. package/presets/react-mysten-simple-upload/src/index.ts +16 -0
  59. package/presets/react-mysten-simple-upload/src/lib/walrus/adapter.ts +197 -0
  60. package/presets/react-mysten-simple-upload/src/lib/walrus/client.ts +87 -0
  61. package/presets/react-mysten-simple-upload/src/lib/walrus/index.ts +4 -0
  62. package/{templates/base/src/adapters/storage.ts → presets/react-mysten-simple-upload/src/lib/walrus/types.ts} +83 -58
  63. package/presets/react-mysten-simple-upload/src/main.tsx +16 -0
  64. package/presets/react-mysten-simple-upload/src/providers/QueryProvider.tsx +18 -0
  65. package/presets/react-mysten-simple-upload/src/providers/WalletProvider.tsx +52 -0
  66. package/presets/react-mysten-simple-upload/src/utils/env.ts +41 -0
  67. package/presets/react-mysten-simple-upload/src/utils/mime-type.ts +97 -0
  68. package/presets/react-mysten-simple-upload/tsconfig.json +39 -0
  69. package/presets/react-mysten-simple-upload/tsconfig.node.json +10 -0
  70. package/presets/react-mysten-simple-upload/vite.config.ts +19 -0
  71. package/templates/base/README.md +0 -54
  72. package/templates/base/package.json +0 -19
  73. package/templates/base/src/types/index.ts +0 -9
  74. package/templates/base/src/types/walrus.ts +0 -22
  75. package/templates/base/src/utils/format.ts +0 -29
  76. package/templates/base/tsconfig.json +0 -19
  77. package/templates/gallery/README.md +0 -44
  78. package/templates/gallery/package.json +0 -6
  79. package/templates/gallery/src/App.tsx +0 -21
  80. package/templates/gallery/src/components/FileCard.tsx +0 -27
  81. package/templates/gallery/src/components/UploadModal.tsx +0 -45
  82. package/templates/gallery/src/styles.css +0 -58
  83. package/templates/gallery/src/types/gallery.ts +0 -13
  84. package/templates/react/.eslintrc.json +0 -26
  85. package/templates/react/README.md +0 -80
  86. package/templates/react/src/App.tsx +0 -14
  87. package/templates/react/src/components/Layout.tsx +0 -21
  88. package/templates/react/src/dapp-kit.css +0 -1
  89. package/templates/react/src/index.css +0 -50
  90. package/templates/react/src/index.ts +0 -10
  91. package/templates/sdk-mysten/README.md +0 -65
  92. package/templates/sdk-mysten/package.json +0 -14
  93. package/templates/sdk-mysten/src/adapter.ts +0 -80
  94. package/templates/sdk-mysten/src/client.ts +0 -45
  95. package/templates/sdk-mysten/src/config.ts +0 -33
  96. package/templates/sdk-mysten/src/index.ts +0 -11
  97. package/templates/sdk-mysten/src/types.ts +0 -19
  98. package/templates/sdk-mysten/test/adapter.test.ts +0 -20
  99. package/templates/simple-upload/README.md +0 -24
  100. package/templates/simple-upload/package.json +0 -6
  101. package/templates/simple-upload/src/App.tsx +0 -27
  102. package/templates/simple-upload/src/components/FilePreview.tsx +0 -40
  103. package/templates/simple-upload/src/styles.css +0 -33
  104. /package/{templates/react → presets/react-mysten-gallery}/index.html +0 -0
  105. /package/{templates/react → presets/react-mysten-gallery}/src/providers/QueryProvider.tsx +0 -0
  106. /package/{templates/react → presets/react-mysten-gallery}/tsconfig.node.json +0 -0
  107. /package/{templates/react → presets/react-mysten-gallery}/vite.config.ts +0 -0
@@ -0,0 +1,107 @@
1
+ # gallery-demo
2
+
3
+ This is a File Gallery Walrus application.
4
+
5
+ ## Features
6
+
7
+ - Upload multiple files to Walrus
8
+ - Grid view of all uploaded files
9
+ - Persistent index (localStorage)
10
+ - Delete files from gallery
11
+ - File metadata display (name, size, upload date)
12
+
13
+ ## Usage
14
+
15
+ 1. Click "Choose File" and select a file
16
+ 2. Click "Add to Gallery" to upload
17
+ 3. Files appear in the grid below
18
+ 4. Click "Delete" to remove files from gallery
19
+ 5. Gallery index persists in localStorage
20
+
21
+ ## Code Structure
22
+
23
+ - `GalleryGrid.tsx` - Grid layout for files
24
+ - `FileCard.tsx` - Individual file display
25
+ - `UploadModal.tsx` - Upload UI
26
+ - `index-manager.ts` - localStorage persistence
27
+ - `App.tsx` - Main app layout
28
+
29
+ ## Gallery Index Format
30
+
31
+ The gallery maintains a local index in localStorage:
32
+
33
+ ```json
34
+ {
35
+ "version": "1.0",
36
+ "items": [
37
+ {
38
+ "blobId": "abc123...",
39
+ "name": "photo.jpg",
40
+ "size": 102400,
41
+ "contentType": "image/jpeg",
42
+ "uploadedAt": 1705449600000
43
+ }
44
+ ],
45
+ "lastModified": 1705449600000
46
+ }
47
+ ```
48
+
49
+ ## Deploy to Walrus Sites
50
+
51
+ ### First-time Setup
52
+
53
+ ```bash
54
+ pnpm setup-walrus-deploy
55
+ ```
56
+
57
+ This will automatically:
58
+ - Install Bun (if not already installed)
59
+ - Download site-builder binary for your OS
60
+ - Clone Walrus Sites portal to `~/.walrus/portal`
61
+ - Add deployment scripts to package.json
62
+
63
+ ### Configure SUI Private Key
64
+
65
+ Edit the portal configuration:
66
+
67
+ **Linux/macOS:**
68
+ ```bash
69
+ nano ~/.walrus/portal/.env
70
+ ```
71
+
72
+ **Windows:**
73
+ ```bash
74
+ notepad %USERPROFILE%\.walrus\portal\.env
75
+ ```
76
+
77
+ Add your private key:
78
+ ```env
79
+ SUI_PRIVATE_KEY=0x...
80
+ WALRUS_NETWORK=testnet
81
+ ```
82
+
83
+ ### Build & Deploy
84
+
85
+ ```bash
86
+ # Build production bundle
87
+ pnpm build
88
+
89
+ # Deploy to Walrus Sites (testnet, 10 epochs)
90
+ pnpm deploy:walrus
91
+ ```
92
+
93
+ ### Preview Locally
94
+
95
+ ```bash
96
+ pnpm walrus:portal
97
+ ```
98
+
99
+ This starts the local portal server to preview your deployed site.
100
+
101
+ ## Available Scripts
102
+
103
+ - `pnpm dev` - Start development server
104
+ - `pnpm build` - Build for production
105
+ - `pnpm setup-walrus-deploy` - One-time deployment setup
106
+ - `pnpm deploy:walrus` - Deploy to Walrus Sites
107
+ - `pnpm walrus:portal` - Start local portal preview
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "gallery-demo",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Walrus application scaffolded with create-walrus-app",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "tsc && vite build",
9
+ "dev": "vite",
10
+ "lint": "eslint . --ext .ts,.tsx",
11
+ "preview": "vite preview",
12
+ "type-check": "tsc --noEmit",
13
+ "setup-walrus-deploy": "bash scripts/setup-walrus-deploy.sh"
14
+ },
15
+ "dependencies": {
16
+ "@mysten/dapp-kit": "^0.14.0",
17
+ "@mysten/sui": "^1.10.0",
18
+ "@mysten/walrus": "^0.9.0",
19
+ "@tanstack/react-query": "^5.17.0",
20
+ "react": "^18.2.0",
21
+ "react-dom": "^18.2.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/react": "^18.2.48",
25
+ "@types/react-dom": "^18.2.18",
26
+ "@typescript-eslint/eslint-plugin": "^6.19.0",
27
+ "@typescript-eslint/parser": "^6.19.0",
28
+ "@vitejs/plugin-react": "^4.2.1",
29
+ "eslint": "^8.56.0",
30
+ "eslint-plugin-react": "^7.33.2",
31
+ "eslint-plugin-react-hooks": "^4.6.0",
32
+ "typescript": "^5.3.3",
33
+ "vite": "^5.0.11"
34
+ }
35
+ }
@@ -0,0 +1,286 @@
1
+ #!/bin/bash
2
+ # setup-walrus-deploy.sh - Zero-config Walrus Sites deployment setup (testnet)
3
+ # Auto-installs dependencies, downloads tools, clones portal
4
+ # Supports: Linux, macOS, Windows (Git Bash/WSL)
5
+
6
+ set -e # Exit on error
7
+
8
+ echo "🦭 Walrus Sites Zero-Config Setup (testnet)"
9
+ echo ""
10
+
11
+ # ============================================================================
12
+ # 1. Detect OS & Architecture
13
+ # ============================================================================
14
+ detect_os() {
15
+ OS=$(uname -s | tr '[:upper:]' '[:lower:]')
16
+ ARCH=$(uname -m)
17
+
18
+ case "$OS" in
19
+ linux*) OS_TYPE="linux" ;;
20
+ darwin*) OS_TYPE="macos" ;;
21
+ mingw*|msys*|cygwin*) OS_TYPE="windows" ;;
22
+ *)
23
+ echo "❌ Unsupported OS: $OS"
24
+ exit 1
25
+ ;;
26
+ esac
27
+
28
+ echo "✅ Detected: $OS_TYPE ($ARCH)"
29
+ }
30
+
31
+ # ============================================================================
32
+ # 2. Auto-install Bun (if not exists)
33
+ # ============================================================================
34
+ setup_bun() {
35
+ if command -v bun &>/dev/null; then
36
+ echo "✅ Bun already installed: $(bun --version)"
37
+ return 0
38
+ fi
39
+
40
+ echo "📥 Installing Bun..."
41
+ if [ "$OS_TYPE" = "windows" ]; then
42
+ # Windows (PowerShell install via Git Bash)
43
+ powershell -c "irm bun.sh/install.ps1 | iex"
44
+ else
45
+ # Linux/macOS
46
+ curl -fsSL https://bun.sh/install | bash
47
+ fi
48
+
49
+ # Add to PATH for current session
50
+ if [ "$OS_TYPE" = "windows" ]; then
51
+ export PATH="$USERPROFILE/.bun/bin:$PATH"
52
+ else
53
+ export BUN_INSTALL="$HOME/.bun"
54
+ export PATH="$BUN_INSTALL/bin:$PATH"
55
+ fi
56
+
57
+ # Verify installation
58
+ if command -v bun &>/dev/null; then
59
+ echo "✅ Bun installed: $(bun --version)"
60
+ else
61
+ echo "⚠️ Bun installed but not in PATH. Restart terminal or run:"
62
+ echo " export PATH=\"\$HOME/.bun/bin:\$PATH\""
63
+ fi
64
+ }
65
+
66
+ # ============================================================================
67
+ # 3. Download site-builder binary (if not exists)
68
+ # ============================================================================
69
+ setup_site_builder() {
70
+ # Set install directory based on OS
71
+ if [ "$OS_TYPE" = "windows" ]; then
72
+ WALRUS_BIN="$USERPROFILE/.walrus/bin"
73
+ SITE_BUILDER="$WALRUS_BIN/site-builder.exe"
74
+ else
75
+ WALRUS_BIN="$HOME/.walrus/bin"
76
+ SITE_BUILDER="$WALRUS_BIN/site-builder"
77
+ fi
78
+
79
+ # Check if already exists
80
+ if [ -f "$SITE_BUILDER" ]; then
81
+ echo "✅ site-builder already exists: $SITE_BUILDER"
82
+ chmod +x "$SITE_BUILDER" 2>/dev/null || true
83
+ return 0
84
+ fi
85
+
86
+ echo "📥 Downloading site-builder for $OS_TYPE..."
87
+ mkdir -p "$WALRUS_BIN"
88
+
89
+ # Select binary based on OS
90
+ case "$OS_TYPE" in
91
+ linux) BINARY_NAME="site-builder-linux" ;;
92
+ macos) BINARY_NAME="site-builder-macos" ;;
93
+ windows) BINARY_NAME="site-builder-windows.exe" ;;
94
+ esac
95
+
96
+ DOWNLOAD_URL="https://github.com/MystenLabs/walrus-sites/releases/latest/download/$BINARY_NAME"
97
+
98
+ # Download with retry
99
+ if ! curl -fsSL -o "$SITE_BUILDER" "$DOWNLOAD_URL"; then
100
+ echo "❌ Failed to download site-builder from: $DOWNLOAD_URL"
101
+ exit 1
102
+ fi
103
+
104
+ chmod +x "$SITE_BUILDER"
105
+ echo "✅ site-builder installed: $SITE_BUILDER"
106
+
107
+ # Add to PATH hint (won't persist after script)
108
+ if [ "$OS_TYPE" = "windows" ]; then
109
+ export PATH="$USERPROFILE/.walrus/bin:$PATH"
110
+ else
111
+ export PATH="$HOME/.walrus/bin:$PATH"
112
+ fi
113
+ }
114
+
115
+ # ============================================================================
116
+ # 4. Clone Walrus Portal (if not exists)
117
+ # ============================================================================
118
+ setup_portal() {
119
+ if [ "$OS_TYPE" = "windows" ]; then
120
+ PORTAL_DIR="$USERPROFILE/.walrus/portal"
121
+ else
122
+ PORTAL_DIR="$HOME/.walrus/portal"
123
+ fi
124
+
125
+ if [ -d "$PORTAL_DIR" ]; then
126
+ echo "✅ Portal already exists: $PORTAL_DIR"
127
+ echo " Updating..."
128
+ cd "$PORTAL_DIR"
129
+ git pull --quiet || echo "⚠️ Git pull failed (may be offline)"
130
+ else
131
+ echo "📂 Cloning Walrus Sites portal..."
132
+ mkdir -p "$(dirname "$PORTAL_DIR")"
133
+
134
+ # Clone with depth=1 for faster download
135
+ if ! git clone --depth=1 https://github.com/MystenLabs/walrus-sites.git "$PORTAL_DIR"; then
136
+ echo "❌ Failed to clone portal repository"
137
+ exit 1
138
+ fi
139
+
140
+ cd "$PORTAL_DIR"
141
+ echo "✅ Portal cloned to: $PORTAL_DIR"
142
+ fi
143
+
144
+ # Setup .env if not exists
145
+ if [ ! -f ".env" ]; then
146
+ if [ -f ".env.example" ]; then
147
+ cp .env.example .env
148
+ echo "✅ Created .env from .env.example"
149
+ else
150
+ # Create minimal .env
151
+ cat > .env << 'EOF'
152
+ # Walrus Portal Configuration (Testnet)
153
+ WALRUS_NETWORK=testnet
154
+ SUI_PRIVATE_KEY=
155
+
156
+ # Optional: Uncomment to customize
157
+ # PORTAL_PORT=3000
158
+ EOF
159
+ echo "✅ Created .env template"
160
+ fi
161
+
162
+ echo ""
163
+ echo "⚠️ ACTION REQUIRED:"
164
+ echo " Edit $PORTAL_DIR/.env"
165
+ echo " Add your SUI_PRIVATE_KEY=0x..."
166
+ echo ""
167
+ else
168
+ echo "✅ .env already configured"
169
+ fi
170
+
171
+ # Install portal dependencies
172
+ echo "📦 Installing portal dependencies..."
173
+ if ! bun install --silent; then
174
+ echo "❌ Bun install failed"
175
+ exit 1
176
+ fi
177
+
178
+ echo "✅ Portal dependencies installed"
179
+ }
180
+
181
+ # ============================================================================
182
+ # 5. Add npm scripts to project package.json
183
+ # ============================================================================
184
+ add_project_scripts() {
185
+ PROJECT_DIR="${1:-.}" # Default to current directory
186
+ cd "$PROJECT_DIR" || { echo "❌ Invalid project directory"; exit 1; }
187
+
188
+ if [ ! -f "package.json" ]; then
189
+ echo "❌ No package.json found in $PROJECT_DIR"
190
+ exit 1
191
+ fi
192
+
193
+ echo "📝 Adding Walrus deploy scripts to package.json..."
194
+
195
+ # Use Node.js to safely modify package.json (guaranteed to exist in Node projects)
196
+ node -e "
197
+ const fs = require('fs');
198
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
199
+
200
+ pkg.scripts = pkg.scripts || {};
201
+
202
+ // Add scripts (only if not already exists)
203
+ if (!pkg.scripts['setup-walrus-deploy']) {
204
+ pkg.scripts['setup-walrus-deploy'] = 'bash scripts/setup-walrus-deploy.sh';
205
+ }
206
+
207
+ if (!pkg.scripts['deploy:walrus']) {
208
+ const siteBuilderPath = process.platform === 'win32'
209
+ ? '%USERPROFILE%/.walrus/bin/site-builder.exe'
210
+ : '~/.walrus/bin/site-builder';
211
+ pkg.scripts['deploy:walrus'] = siteBuilderPath + ' --context=testnet deploy ./dist --epochs 10';
212
+ }
213
+
214
+ if (!pkg.scripts['walrus:portal']) {
215
+ const portalPath = process.platform === 'win32'
216
+ ? 'cd %USERPROFILE%/.walrus/portal'
217
+ : 'cd ~/.walrus/portal';
218
+ pkg.scripts['walrus:portal'] = portalPath + ' && bun run server';
219
+ }
220
+
221
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\\n');
222
+ " || {
223
+ echo "❌ Failed to update package.json"
224
+ exit 1
225
+ }
226
+
227
+ echo "✅ Scripts added to package.json:"
228
+ echo " - setup-walrus-deploy"
229
+ echo " - deploy:walrus"
230
+ echo " - walrus:portal"
231
+ }
232
+
233
+ # ============================================================================
234
+ # Main Execution
235
+ # ============================================================================
236
+ main() {
237
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
238
+
239
+ # Trap errors
240
+ trap 'echo "❌ Setup failed at line $LINENO"' ERR
241
+
242
+ # Prerequisites check
243
+ if ! command -v git &>/dev/null; then
244
+ echo "❌ Git not found. Install: https://git-scm.com"
245
+ exit 1
246
+ fi
247
+
248
+ if ! command -v node &>/dev/null; then
249
+ echo "❌ Node.js not found. Install: https://nodejs.org"
250
+ exit 1
251
+ fi
252
+
253
+ # Run setup steps
254
+ detect_os
255
+ setup_bun
256
+ setup_site_builder
257
+ setup_portal
258
+ add_project_scripts "$@"
259
+
260
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
261
+ echo ""
262
+ echo "🎉 Setup Complete!"
263
+ echo ""
264
+ echo "Next Steps:"
265
+ echo " 1. Configure your SUI private key:"
266
+ if [ "$OS_TYPE" = "windows" ]; then
267
+ echo " notepad %USERPROFILE%\\.walrus\\portal\\.env"
268
+ else
269
+ echo " nano ~/.walrus/portal/.env"
270
+ fi
271
+ echo " Add: SUI_PRIVATE_KEY=0x..."
272
+ echo ""
273
+ echo " 2. Build your project:"
274
+ echo " pnpm build"
275
+ echo ""
276
+ echo " 3. Deploy to Walrus Sites:"
277
+ echo " pnpm deploy:walrus"
278
+ echo ""
279
+ echo " 4. (Optional) Preview locally:"
280
+ echo " pnpm walrus:portal"
281
+ echo ""
282
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
283
+ }
284
+
285
+ # Run main with all script arguments
286
+ main "$@"
@@ -0,0 +1,23 @@
1
+ import { useState } from 'react';
2
+ import { AppLayout } from './components/layout/app-layout.js';
3
+ import { GalleryGrid } from './components/features/gallery-grid.js';
4
+ import { UploadModal } from './components/features/upload-modal.js';
5
+ import './index.css';
6
+
7
+ function App() {
8
+ const [refreshKey, setRefreshKey] = useState(0);
9
+
10
+ return (
11
+ <AppLayout>
12
+ <div className="gallery-app">
13
+ <h2><span className="text-accent">🖼️</span> File Gallery</h2>
14
+ <p className="text-secondary">Upload and manage multiple files on <span className="text-accent">Walrus</span></p>
15
+
16
+ <UploadModal onSuccess={() => setRefreshKey((k) => k + 1)} />
17
+ <GalleryGrid key={refreshKey} />
18
+ </div>
19
+ </AppLayout>
20
+ );
21
+ }
22
+
23
+ export default App;
@@ -0,0 +1,89 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { removeItem } from '../../utils/index-manager.js';
3
+ import type { GalleryItem } from '../../lib/walrus/types.js';
4
+ import {
5
+ generatePreviewUrl,
6
+ isImageType,
7
+ revokePreviewUrl,
8
+ } from '../../utils/preview-generator.js';
9
+
10
+ interface FileCardProps {
11
+ item: GalleryItem;
12
+ onDelete: () => void;
13
+ }
14
+
15
+ export function FileCard({ item, onDelete }: FileCardProps) {
16
+ const [previewUrl, setPreviewUrl] = useState<string | null>(
17
+ item.previewUrl || null
18
+ );
19
+ const [isLoadingPreview, setIsLoadingPreview] = useState(false);
20
+
21
+ // Load preview if it's an image and we don't have a preview URL yet
22
+ useEffect(() => {
23
+ if (!previewUrl && isImageType(item.contentType)) {
24
+ setIsLoadingPreview(true);
25
+ generatePreviewUrl(item.blobId, item.contentType)
26
+ .then((url) => setPreviewUrl(url))
27
+ .catch((error) => {
28
+ console.error('Failed to load preview:', error);
29
+ })
30
+ .finally(() => setIsLoadingPreview(false));
31
+ }
32
+
33
+ // Cleanup: revoke object URL when component unmounts
34
+ return () => {
35
+ if (previewUrl) {
36
+ revokePreviewUrl(previewUrl);
37
+ }
38
+ };
39
+ }, [item.blobId, item.contentType, previewUrl]);
40
+
41
+ const handleDelete = () => {
42
+ if (confirm(`Delete ${item.name}?`)) {
43
+ // Revoke preview URL before deleting
44
+ if (previewUrl) {
45
+ revokePreviewUrl(previewUrl);
46
+ }
47
+ removeItem(item.blobId);
48
+ onDelete();
49
+ }
50
+ };
51
+
52
+ const formatBytes = (bytes: number): string => {
53
+ if (bytes === 0) return '0 Bytes';
54
+ const k = 1024;
55
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
56
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
57
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
58
+ };
59
+
60
+ const formatDate = (timestamp: number): string => {
61
+ return new Date(timestamp).toLocaleString();
62
+ };
63
+
64
+ return (
65
+ <div className="file-card">
66
+ {/* Image Preview */}
67
+ {isImageType(item.contentType) && (
68
+ <div className="image-preview">
69
+ {isLoadingPreview ? (
70
+ <div className="loading-placeholder">Loading preview...</div>
71
+ ) : previewUrl ? (
72
+ <img src={previewUrl} alt={item.name} />
73
+ ) : (
74
+ <div className="error-placeholder">Preview not available</div>
75
+ )}
76
+ </div>
77
+ )}
78
+
79
+ {/* File Info */}
80
+ <h4>{item.name}</h4>
81
+ <p className="text-secondary">Size: {formatBytes(item.size)}</p>
82
+ <p className="text-secondary">Uploaded: {formatDate(item.uploadedAt)}</p>
83
+ <p className="blob-id text-accent">{item.blobId.slice(0, 16)}...</p>
84
+ <button onClick={handleDelete} className="btn-danger">
85
+ Delete
86
+ </button>
87
+ </div>
88
+ );
89
+ }
@@ -1,7 +1,7 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { FileCard } from './FileCard.js';
3
- import { loadIndex } from '../utils/index-manager.js';
4
- import type { GalleryItem } from '../types/gallery.js';
2
+ import { FileCard } from './file-card.js';
3
+ import { loadIndex } from '../../utils/index-manager.js';
4
+ import type { GalleryItem } from '../../lib/walrus/types.js';
5
5
 
6
6
  export function GalleryGrid() {
7
7
  const [items, setItems] = useState<GalleryItem[]>([]);
@@ -19,7 +19,7 @@ export function GalleryGrid() {
19
19
  return (
20
20
  <div className="gallery-grid">
21
21
  {items.length === 0 ? (
22
- <p>No files yet. Upload your first file!</p>
22
+ <p className="text-secondary">No files yet. Upload your first file!</p>
23
23
  ) : (
24
24
  items.map((item) => (
25
25
  <FileCard key={item.blobId} item={item} onDelete={refreshGallery} />
@@ -27,4 +27,4 @@ export function GalleryGrid() {
27
27
  )}
28
28
  </div>
29
29
  );
30
- }
30
+ }
@@ -0,0 +1,69 @@
1
+ import { useState } from 'react';
2
+ import { useUpload } from '../../hooks/use-upload.js';
3
+ import { addItem } from '../../utils/index-manager.js';
4
+ import { generatePreviewUrl, isImageType } from '../../utils/preview-generator.js';
5
+
6
+ interface UploadModalProps {
7
+ onSuccess: () => void;
8
+ }
9
+
10
+ export function UploadModal({ onSuccess }: UploadModalProps) {
11
+ const [file, setFile] = useState<File | null>(null);
12
+ const upload = useUpload();
13
+
14
+ const handleUpload = async () => {
15
+ if (!file) return;
16
+
17
+ upload.mutate(
18
+ { file, options: { epochs: 1 } },
19
+ {
20
+ onSuccess: async (data) => {
21
+ // Generate preview URL for images
22
+ let previewUrl: string | undefined;
23
+ if (isImageType(file.type)) {
24
+ try {
25
+ previewUrl = await generatePreviewUrl(data.blobId, file.type);
26
+ console.log('Preview URL generated:', previewUrl);
27
+ } catch (error) {
28
+ console.error('Failed to generate preview URL:', error);
29
+ // Continue without preview - FileCard will try to load it later
30
+ }
31
+ }
32
+
33
+ // Add item to gallery with preview URL
34
+ addItem({
35
+ blobId: data.blobId,
36
+ name: file.name,
37
+ size: file.size,
38
+ contentType: file.type,
39
+ uploadedAt: Date.now(),
40
+ previewUrl,
41
+ });
42
+
43
+ setFile(null);
44
+ onSuccess();
45
+ },
46
+ }
47
+ );
48
+ };
49
+
50
+ return (
51
+ <div className="upload-modal">
52
+ <input
53
+ type="file"
54
+ onChange={(e) => setFile(e.target.files?.[0] || null)}
55
+ className="file-input"
56
+ />
57
+ <button
58
+ onClick={handleUpload}
59
+ disabled={!file || upload.isPending}
60
+ className="btn-primary"
61
+ >
62
+ {upload.isPending ? 'Uploading...' : 'Add to Gallery'}
63
+ </button>
64
+ {upload.error && (
65
+ <p className="text-error">Error: {upload.error.message}</p>
66
+ )}
67
+ </div>
68
+ );
69
+ }
@@ -1,5 +1,5 @@
1
1
  import { ConnectButton } from '@mysten/dapp-kit';
2
- import { useWallet } from '../hooks/useWallet.js';
2
+ import { useWallet } from '../../hooks/use-wallet.js';
3
3
 
4
4
  export function WalletConnect() {
5
5
  const { isConnected, address } = useWallet();
@@ -0,0 +1,21 @@
1
+ import { ReactNode } from 'react';
2
+ import { WalletConnect } from '../features/wallet-connect.js';
3
+
4
+ interface LayoutProps {
5
+ children: ReactNode;
6
+ }
7
+
8
+ export function AppLayout({ children }: LayoutProps) {
9
+ return (
10
+ <div className="app-layout">
11
+ <header className="app-header">
12
+ <h1><span className="text-secondary">🌊</span> <span className="text-accent">Walrus</span> App</h1>
13
+ <WalletConnect />
14
+ </header>
15
+ <main className="app-main">{children}</main>
16
+ <footer className="app-footer">
17
+ <p className="text-secondary">Powered by <span className="text-accent">Walrus</span> & <span style={{ color: 'var(--walrus-accent-blue)' }}>Sui</span></p>
18
+ </footer>
19
+ </div>
20
+ );
21
+ }
@@ -1,21 +1,5 @@
1
- import { useMutation, useQuery } from '@tanstack/react-query';
2
- import { storageAdapter } from '../index.js';
3
- import type { UploadOptions } from '../adapters/storage.js';
4
-
5
- export function useUpload() {
6
- return useMutation({
7
- mutationFn: async ({
8
- file,
9
- options,
10
- }: {
11
- file: File;
12
- options?: UploadOptions;
13
- }) => {
14
- const blobId = await storageAdapter.upload(file, options);
15
- return { blobId, file };
16
- },
17
- });
18
- }
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { storageAdapter } from '../lib/walrus/index.js';
19
3
 
20
4
  export function useDownload(blobId: string | null) {
21
5
  return useQuery({