@blu1606/create-walrus-app 0.1.4 → 2.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/dist/generator/file-ops.d.ts +8 -0
- package/dist/generator/file-ops.js +20 -0
- package/dist/generator/index.js +37 -22
- package/dist/generator/layers.d.ts +15 -2
- package/dist/generator/layers.js +38 -47
- package/dist/generator/types.d.ts +9 -1
- package/dist/index.js +1 -2
- package/dist/post-install/git.d.ts +8 -0
- package/dist/post-install/git.js +2 -0
- package/dist/post-install/index.d.ts +0 -1
- package/dist/post-install/index.js +2 -15
- package/dist/post-install/messages.js +1 -1
- package/package.json +68 -68
- package/{templates/base → presets/react-mysten-gallery}/.env.example +31 -31
- package/presets/react-mysten-gallery/.gitkeep +4 -0
- package/{templates/gallery → presets/react-mysten-gallery}/README.md +25 -22
- package/presets/react-mysten-gallery/package.json +34 -0
- package/presets/react-mysten-gallery/src/App.tsx +23 -0
- package/presets/react-mysten-gallery/src/components/features/file-card.tsx +89 -0
- package/{templates/gallery/src/components/GalleryGrid.tsx → presets/react-mysten-gallery/src/components/features/gallery-grid.tsx} +5 -5
- package/presets/react-mysten-gallery/src/components/features/upload-modal.tsx +69 -0
- package/{templates/react/src/components/WalletConnect.tsx → presets/react-mysten-gallery/src/components/features/wallet-connect.tsx} +1 -1
- package/presets/react-mysten-gallery/src/components/layout/app-layout.tsx +21 -0
- package/{templates/react/src/hooks/useStorage.ts → presets/react-mysten-gallery/src/hooks/use-download.ts} +2 -18
- package/presets/react-mysten-gallery/src/hooks/use-upload.ts +49 -0
- package/{templates/react/src/hooks/useWallet.ts → presets/react-mysten-gallery/src/hooks/use-wallet.ts} +2 -7
- package/presets/react-mysten-gallery/src/index.css +384 -0
- package/presets/react-mysten-gallery/src/index.ts +17 -0
- package/presets/react-mysten-gallery/src/lib/walrus/adapter.ts +197 -0
- package/presets/react-mysten-gallery/src/lib/walrus/client.ts +87 -0
- package/presets/react-mysten-gallery/src/lib/walrus/index.ts +4 -0
- package/presets/react-mysten-gallery/src/lib/walrus/types.ts +101 -0
- package/{templates/react → presets/react-mysten-gallery}/src/main.tsx +0 -1
- package/{templates/react → presets/react-mysten-gallery}/src/providers/WalletProvider.tsx +16 -1
- package/{templates/base → presets/react-mysten-gallery}/src/utils/env.ts +41 -41
- package/{templates/gallery → presets/react-mysten-gallery}/src/utils/index-manager.ts +2 -2
- package/presets/react-mysten-gallery/src/utils/mime-type.ts +97 -0
- package/presets/react-mysten-gallery/src/utils/preview-generator.ts +134 -0
- package/{templates/react → presets/react-mysten-gallery}/tsconfig.json +20 -8
- package/presets/react-mysten-simple-upload/.env.example +31 -0
- package/presets/react-mysten-simple-upload/.gitkeep +4 -0
- package/presets/react-mysten-simple-upload/index.html +13 -0
- package/{templates/react → presets/react-mysten-simple-upload}/package.json +13 -11
- package/presets/react-mysten-simple-upload/src/App.tsx +27 -0
- package/presets/react-mysten-simple-upload/src/components/features/file-preview.tsx +73 -0
- package/{templates/simple-upload/src/components/UploadForm.tsx → presets/react-mysten-simple-upload/src/components/features/upload-form.tsx} +15 -5
- package/presets/react-mysten-simple-upload/src/components/features/wallet-connect.tsx +21 -0
- package/presets/react-mysten-simple-upload/src/components/layout/app-layout.tsx +21 -0
- package/presets/react-mysten-simple-upload/src/hooks/use-download.ts +24 -0
- package/presets/react-mysten-simple-upload/src/hooks/use-upload.ts +49 -0
- package/presets/react-mysten-simple-upload/src/hooks/use-wallet.ts +11 -0
- package/presets/react-mysten-simple-upload/src/index.css +252 -0
- package/presets/react-mysten-simple-upload/src/index.ts +16 -0
- package/presets/react-mysten-simple-upload/src/lib/walrus/adapter.ts +197 -0
- package/presets/react-mysten-simple-upload/src/lib/walrus/client.ts +87 -0
- package/presets/react-mysten-simple-upload/src/lib/walrus/index.ts +4 -0
- package/{templates/base/src/adapters/storage.ts → presets/react-mysten-simple-upload/src/lib/walrus/types.ts} +83 -58
- package/presets/react-mysten-simple-upload/src/main.tsx +16 -0
- package/presets/react-mysten-simple-upload/src/providers/QueryProvider.tsx +18 -0
- package/presets/react-mysten-simple-upload/src/providers/WalletProvider.tsx +52 -0
- package/presets/react-mysten-simple-upload/src/utils/env.ts +41 -0
- package/presets/react-mysten-simple-upload/src/utils/mime-type.ts +97 -0
- package/presets/react-mysten-simple-upload/tsconfig.json +39 -0
- package/presets/react-mysten-simple-upload/tsconfig.node.json +10 -0
- package/presets/react-mysten-simple-upload/vite.config.ts +19 -0
- package/dist/__tests__/helpers/adapter-compliance.d.ts +0 -2
- package/dist/__tests__/helpers/adapter-compliance.js +0 -47
- package/dist/__tests__/helpers/fixtures.d.ts +0 -21
- package/dist/__tests__/helpers/fixtures.js +0 -30
- package/dist/__tests__/helpers/fs-helpers.d.ts +0 -12
- package/dist/__tests__/helpers/fs-helpers.js +0 -35
- package/dist/__tests__/helpers/index.d.ts +0 -4
- package/dist/__tests__/helpers/index.js +0 -4
- package/dist/__tests__/helpers/test-hooks.d.ts +0 -3
- package/dist/__tests__/helpers/test-hooks.js +0 -18
- package/dist/context.test.d.ts +0 -1
- package/dist/context.test.js +0 -98
- package/dist/generator/index.test.d.ts +0 -1
- package/dist/generator/index.test.js +0 -143
- package/dist/generator/layers.test.d.ts +0 -1
- package/dist/generator/layers.test.js +0 -92
- package/dist/generator/merge.test.d.ts +0 -1
- package/dist/generator/merge.test.js +0 -79
- package/dist/generator/transform.test.d.ts +0 -1
- package/dist/generator/transform.test.js +0 -51
- package/dist/matrix.test.d.ts +0 -1
- package/dist/matrix.test.js +0 -70
- package/dist/types.test.d.ts +0 -1
- package/dist/types.test.js +0 -65
- package/dist/utils/detect-pm.test.d.ts +0 -1
- package/dist/utils/detect-pm.test.js +0 -52
- package/dist/validator.test.d.ts +0 -1
- package/dist/validator.test.js +0 -96
- package/templates/base/README.md +0 -54
- package/templates/base/package.json +0 -19
- package/templates/base/src/types/index.ts +0 -9
- package/templates/base/src/types/walrus.ts +0 -22
- package/templates/base/src/utils/format.ts +0 -29
- package/templates/base/tsconfig.json +0 -19
- package/templates/gallery/package.json +0 -6
- package/templates/gallery/src/App.tsx +0 -21
- package/templates/gallery/src/components/FileCard.tsx +0 -27
- package/templates/gallery/src/components/UploadModal.tsx +0 -45
- package/templates/gallery/src/styles.css +0 -58
- package/templates/gallery/src/types/gallery.ts +0 -13
- package/templates/react/.eslintrc.json +0 -26
- package/templates/react/README.md +0 -80
- package/templates/react/src/App.tsx +0 -14
- package/templates/react/src/components/Layout.tsx +0 -21
- package/templates/react/src/dapp-kit.css +0 -1
- package/templates/react/src/index.css +0 -50
- package/templates/react/src/index.ts +0 -10
- package/templates/sdk-mysten/README.md +0 -65
- package/templates/sdk-mysten/package.json +0 -14
- package/templates/sdk-mysten/src/adapter.ts +0 -80
- package/templates/sdk-mysten/src/client.ts +0 -45
- package/templates/sdk-mysten/src/config.ts +0 -33
- package/templates/sdk-mysten/src/index.ts +0 -11
- package/templates/sdk-mysten/src/types.ts +0 -19
- package/templates/sdk-mysten/test/adapter.test.ts +0 -20
- package/templates/simple-upload/package.json +0 -6
- package/templates/simple-upload/src/App.tsx +0 -27
- package/templates/simple-upload/src/components/FilePreview.tsx +0 -40
- package/templates/simple-upload/src/styles.css +0 -33
- /package/{templates/react → presets/react-mysten-gallery}/index.html +0 -0
- /package/{templates/react → presets/react-mysten-gallery}/src/providers/QueryProvider.tsx +0 -0
- /package/{templates/react → presets/react-mysten-gallery}/tsconfig.node.json +0 -0
- /package/{templates/react → presets/react-mysten-gallery}/vite.config.ts +0 -0
- /package/{templates/simple-upload → presets/react-mysten-simple-upload}/README.md +0 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/* Dapp Kit styles */
|
|
2
|
+
@import '@mysten/dapp-kit/dist/index.css';
|
|
3
|
+
|
|
4
|
+
/* Google Fonts Import */
|
|
5
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;700&display=swap');
|
|
6
|
+
|
|
7
|
+
/* Global styles */
|
|
8
|
+
:root {
|
|
9
|
+
/* Pallette */
|
|
10
|
+
--walrus-bg-primary: #020617;
|
|
11
|
+
/* Deep Trench */
|
|
12
|
+
--walrus-bg-secondary: #0f172a;
|
|
13
|
+
/* Card/Section bg */
|
|
14
|
+
--walrus-surface: #1e293b;
|
|
15
|
+
/* Input, button bg */
|
|
16
|
+
--walrus-surface-hover: #334155;
|
|
17
|
+
/* Hover states */
|
|
18
|
+
--walrus-accent-cyan: #06b6d4;
|
|
19
|
+
/* Arctic Cyan - Primary Highlight */
|
|
20
|
+
--walrus-accent-blue: #4da2ff;
|
|
21
|
+
/* Sui Blue */
|
|
22
|
+
--walrus-accent-purple: #c084fc;
|
|
23
|
+
/* Secondary Accent */
|
|
24
|
+
--walrus-text-primary: #f8fafc;
|
|
25
|
+
/* Main Text */
|
|
26
|
+
--walrus-text-secondary: #94a3b8;
|
|
27
|
+
/* Muted Text */
|
|
28
|
+
--walrus-border: #334155;
|
|
29
|
+
/* Border Color */
|
|
30
|
+
--walrus-warning: #ef4444;
|
|
31
|
+
--walrus-success: #10b981;
|
|
32
|
+
--walrus-error: #ef4444;
|
|
33
|
+
|
|
34
|
+
/* Base Settings */
|
|
35
|
+
font-family: 'Inter', system-ui, sans-serif;
|
|
36
|
+
line-height: 1.5;
|
|
37
|
+
font-weight: 400;
|
|
38
|
+
|
|
39
|
+
color-scheme: dark;
|
|
40
|
+
color: var(--walrus-text-primary);
|
|
41
|
+
background-color: var(--walrus-bg-primary);
|
|
42
|
+
|
|
43
|
+
font-synthesis: none;
|
|
44
|
+
text-rendering: optimizeLegibility;
|
|
45
|
+
-webkit-font-smoothing: antialiased;
|
|
46
|
+
-moz-osx-font-smoothing: grayscale;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
body {
|
|
50
|
+
margin: 0;
|
|
51
|
+
display: flex;
|
|
52
|
+
place-items: center;
|
|
53
|
+
min-width: 320px;
|
|
54
|
+
min-height: 100vh;
|
|
55
|
+
background-color: var(--walrus-bg-primary);
|
|
56
|
+
color: var(--walrus-text-primary);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#root {
|
|
60
|
+
max-width: 1280px;
|
|
61
|
+
margin: 0 auto;
|
|
62
|
+
padding: 2rem;
|
|
63
|
+
text-align: center;
|
|
64
|
+
width: 100%;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Typography Overrides */
|
|
68
|
+
h1,
|
|
69
|
+
h2,
|
|
70
|
+
h3,
|
|
71
|
+
h4,
|
|
72
|
+
h5,
|
|
73
|
+
h6 {
|
|
74
|
+
font-family: 'JetBrains Mono', monospace;
|
|
75
|
+
color: var(--walrus-text-primary);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
h1 {
|
|
79
|
+
font-size: 3.2em;
|
|
80
|
+
line-height: 1.1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
h2 {
|
|
84
|
+
font-size: 2.5em;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
h3 {
|
|
88
|
+
font-size: 1.5em;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
a {
|
|
92
|
+
font-weight: 500;
|
|
93
|
+
color: var(--walrus-accent-blue);
|
|
94
|
+
text-decoration: inherit;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
a:hover {
|
|
98
|
+
color: var(--walrus-accent-cyan);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Button Styling */
|
|
102
|
+
button {
|
|
103
|
+
border-radius: 8px;
|
|
104
|
+
border: 1px solid var(--walrus-border);
|
|
105
|
+
padding: 0.6em 1.2em;
|
|
106
|
+
font-size: 1em;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
font-family: inherit;
|
|
109
|
+
background-color: var(--walrus-surface);
|
|
110
|
+
color: var(--walrus-text-primary);
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
transition: all 0.25s;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
button:hover:not(:disabled) {
|
|
116
|
+
border-color: var(--walrus-accent-cyan);
|
|
117
|
+
background-color: var(--walrus-surface-hover);
|
|
118
|
+
box-shadow: 0 0 10px rgba(6, 182, 212, 0.2);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
button:focus,
|
|
122
|
+
button:focus-visible {
|
|
123
|
+
outline: 4px auto var(--walrus-accent-cyan);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
button:disabled {
|
|
127
|
+
opacity: 0.5;
|
|
128
|
+
cursor: not-allowed;
|
|
129
|
+
background-color: var(--walrus-bg-secondary);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Form Elements */
|
|
133
|
+
input {
|
|
134
|
+
background-color: var(--walrus-surface);
|
|
135
|
+
border: 1px solid var(--walrus-border);
|
|
136
|
+
color: var(--walrus-text-primary);
|
|
137
|
+
padding: 0.6em;
|
|
138
|
+
border-radius: 8px;
|
|
139
|
+
font-family: inherit;
|
|
140
|
+
font-size: 1em;
|
|
141
|
+
transition: border-color 0.2s;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
input:focus {
|
|
145
|
+
outline: none;
|
|
146
|
+
border-color: var(--walrus-accent-cyan);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Utilities / Components */
|
|
150
|
+
.card {
|
|
151
|
+
background-color: var(--walrus-bg-secondary);
|
|
152
|
+
border: 1px solid var(--walrus-border);
|
|
153
|
+
border-radius: 12px;
|
|
154
|
+
padding: 2rem;
|
|
155
|
+
transition: all 0.2s;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.card:hover {
|
|
159
|
+
border-color: var(--walrus-accent-cyan);
|
|
160
|
+
box-shadow: 0 0 15px rgba(6, 182, 212, 0.15);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.badge {
|
|
164
|
+
display: inline-block;
|
|
165
|
+
padding: 0.2em 0.6em;
|
|
166
|
+
font-size: 0.85em;
|
|
167
|
+
font-weight: 500;
|
|
168
|
+
border-radius: 999px;
|
|
169
|
+
background-color: rgba(192, 132, 252, 0.15);
|
|
170
|
+
color: var(--walrus-accent-purple);
|
|
171
|
+
border: 1px solid rgba(192, 132, 252, 0.3);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.text-accent {
|
|
175
|
+
color: var(--walrus-accent-cyan);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.text-secondary {
|
|
179
|
+
color: var(--walrus-text-secondary);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.text-error {
|
|
183
|
+
color: var(--walrus-error);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.text-warning {
|
|
187
|
+
color: var(--walrus-warning);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* App Specific Containers */
|
|
191
|
+
.simple-upload-app {
|
|
192
|
+
max-width: 900px;
|
|
193
|
+
margin: 0 auto;
|
|
194
|
+
display: flex;
|
|
195
|
+
flex-direction: column;
|
|
196
|
+
gap: 2rem;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Overriding section to look like cards */
|
|
200
|
+
section {
|
|
201
|
+
margin: 0;
|
|
202
|
+
padding: 2rem;
|
|
203
|
+
border: 1px solid var(--walrus-border);
|
|
204
|
+
background-color: var(--walrus-bg-secondary);
|
|
205
|
+
border-radius: 12px;
|
|
206
|
+
transition: border-color 0.2s;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
section:hover {
|
|
210
|
+
border-color: var(--walrus-surface-hover);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
section h3 {
|
|
214
|
+
margin-top: 0;
|
|
215
|
+
color: var(--walrus-accent-cyan);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.upload-form,
|
|
219
|
+
.file-preview {
|
|
220
|
+
display: flex;
|
|
221
|
+
flex-direction: column;
|
|
222
|
+
gap: 1.5rem;
|
|
223
|
+
align-items: stretch;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.file-info {
|
|
227
|
+
background: var(--walrus-bg-primary);
|
|
228
|
+
padding: 1rem;
|
|
229
|
+
border-radius: 8px;
|
|
230
|
+
border: 1px dashed var(--walrus-border);
|
|
231
|
+
text-align: left;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.error {
|
|
235
|
+
color: var(--walrus-error);
|
|
236
|
+
background: rgba(239, 68, 68, 0.1);
|
|
237
|
+
padding: 0.5rem;
|
|
238
|
+
border-radius: 4px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.warning {
|
|
242
|
+
color: var(--walrus-warning);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Code/Pre blocks */
|
|
246
|
+
pre {
|
|
247
|
+
background: var(--walrus-bg-primary) !important;
|
|
248
|
+
color: var(--walrus-text-primary);
|
|
249
|
+
border: 1px solid var(--walrus-border);
|
|
250
|
+
border-radius: 8px;
|
|
251
|
+
font-family: 'JetBrains Mono', monospace;
|
|
252
|
+
}
|
|
253
|
+
/* Gallery-specific styles */
|
|
254
|
+
.gallery-app {
|
|
255
|
+
max-width: 1200px;
|
|
256
|
+
margin: 0 auto;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.gallery-grid {
|
|
260
|
+
display: grid;
|
|
261
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
262
|
+
gap: 1.5rem;
|
|
263
|
+
margin-top: 2rem;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.file-card {
|
|
267
|
+
background: var(--walrus-bg-secondary);
|
|
268
|
+
border: 1px solid var(--walrus-border);
|
|
269
|
+
border-radius: 12px;
|
|
270
|
+
padding: 1.5rem;
|
|
271
|
+
transition: all 0.2s;
|
|
272
|
+
display: flex;
|
|
273
|
+
flex-direction: column;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.file-card:hover {
|
|
277
|
+
border-color: var(--walrus-accent-cyan);
|
|
278
|
+
transform: translateY(-2px);
|
|
279
|
+
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Image Preview Styles */
|
|
283
|
+
.file-card .image-preview {
|
|
284
|
+
width: 100%;
|
|
285
|
+
height: 200px;
|
|
286
|
+
margin-bottom: 1rem;
|
|
287
|
+
border-radius: 8px;
|
|
288
|
+
overflow: hidden;
|
|
289
|
+
background: var(--walrus-bg-primary);
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
justify-content: center;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.file-card .image-preview img {
|
|
296
|
+
width: 100%;
|
|
297
|
+
height: 100%;
|
|
298
|
+
object-fit: cover;
|
|
299
|
+
display: block;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.file-card .image-preview .loading-placeholder,
|
|
303
|
+
.file-card .image-preview .error-placeholder {
|
|
304
|
+
color: var(--walrus-text-secondary);
|
|
305
|
+
font-size: 0.9rem;
|
|
306
|
+
text-align: center;
|
|
307
|
+
padding: 1rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.file-card .image-preview .loading-placeholder {
|
|
311
|
+
animation: pulse 2s ease-in-out infinite;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@keyframes pulse {
|
|
315
|
+
0%, 100% {
|
|
316
|
+
opacity: 0.6;
|
|
317
|
+
}
|
|
318
|
+
50% {
|
|
319
|
+
opacity: 1;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.file-card h4 {
|
|
324
|
+
margin: 0 0 0.75rem 0;
|
|
325
|
+
color: var(--walrus-text-primary);
|
|
326
|
+
font-size: 1.1rem;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.file-card p {
|
|
330
|
+
margin: 0.5rem 0;
|
|
331
|
+
font-size: 0.9rem;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.file-card .blob-id {
|
|
335
|
+
font-family: 'JetBrains Mono', monospace;
|
|
336
|
+
font-size: 0.85rem;
|
|
337
|
+
padding: 0.5rem;
|
|
338
|
+
background: var(--walrus-bg-primary);
|
|
339
|
+
border-radius: 4px;
|
|
340
|
+
margin-top: 1rem;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.upload-modal {
|
|
344
|
+
background: var(--walrus-bg-secondary);
|
|
345
|
+
border: 1px solid var(--walrus-border);
|
|
346
|
+
border-radius: 12px;
|
|
347
|
+
padding: 1.5rem;
|
|
348
|
+
margin-bottom: 2rem;
|
|
349
|
+
display: flex;
|
|
350
|
+
gap: 1rem;
|
|
351
|
+
align-items: center;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.file-input {
|
|
355
|
+
flex: 1;
|
|
356
|
+
padding: 0.75rem;
|
|
357
|
+
background: var(--walrus-surface);
|
|
358
|
+
border: 1px solid var(--walrus-border);
|
|
359
|
+
border-radius: 8px;
|
|
360
|
+
color: var(--walrus-text-primary);
|
|
361
|
+
font-family: inherit;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.btn-danger {
|
|
365
|
+
background: var(--walrus-error);
|
|
366
|
+
color: white;
|
|
367
|
+
border: none;
|
|
368
|
+
padding: 0.5rem 1rem;
|
|
369
|
+
border-radius: 6px;
|
|
370
|
+
cursor: pointer;
|
|
371
|
+
transition: all 0.2s;
|
|
372
|
+
margin-top: 1rem;
|
|
373
|
+
width: 100%;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.btn-danger:hover {
|
|
377
|
+
opacity: 0.9;
|
|
378
|
+
transform: translateY(-1px);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.text-error {
|
|
382
|
+
color: var(--walrus-error);
|
|
383
|
+
margin-top: 0.5rem;
|
|
384
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Re-export Walrus library
|
|
2
|
+
export * from './lib/walrus/index.js';
|
|
3
|
+
|
|
4
|
+
// Re-export features
|
|
5
|
+
export { UploadModal } from './components/features/upload-modal.js';
|
|
6
|
+
export { GalleryGrid } from './components/features/gallery-grid.js';
|
|
7
|
+
export { FileCard } from './components/features/file-card.js';
|
|
8
|
+
export { WalletConnect } from './components/features/wallet-connect.js';
|
|
9
|
+
|
|
10
|
+
// Re-export hooks
|
|
11
|
+
export { useUpload } from './hooks/use-upload.js';
|
|
12
|
+
export { useDownload, useMetadata } from './hooks/use-download.js';
|
|
13
|
+
export { useWallet } from './hooks/use-wallet.js';
|
|
14
|
+
|
|
15
|
+
// Re-export layout
|
|
16
|
+
export { AppLayout } from './components/layout/app-layout.js';
|
|
17
|
+
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
StorageAdapter,
|
|
3
|
+
BlobMetadata,
|
|
4
|
+
UploadOptions,
|
|
5
|
+
DownloadOptions,
|
|
6
|
+
} from './types.js';
|
|
7
|
+
import { getWalrusClient, resetWalrusClient } from './client.js';
|
|
8
|
+
import { WalrusFile, RetryableWalrusClientError } from '@mysten/walrus';
|
|
9
|
+
import { getContentType } from '../../utils/mime-type.js';
|
|
10
|
+
|
|
11
|
+
export class MystenStorageAdapter implements StorageAdapter {
|
|
12
|
+
async upload(
|
|
13
|
+
data: File | Uint8Array,
|
|
14
|
+
options?: UploadOptions
|
|
15
|
+
): Promise<string> {
|
|
16
|
+
const bytes =
|
|
17
|
+
data instanceof File ? new Uint8Array(await data.arrayBuffer()) : data;
|
|
18
|
+
const fileName = data instanceof File ? data.name : 'upload.bin';
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Check if signer and client are provided
|
|
22
|
+
if (!options?.signer) {
|
|
23
|
+
throw new Error('Wallet signer is required for upload. Please connect your wallet.');
|
|
24
|
+
}
|
|
25
|
+
if (!options?.client) {
|
|
26
|
+
throw new Error('Sui client is required for upload.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const client = options.client;
|
|
30
|
+
|
|
31
|
+
console.log('[Upload] Starting upload flow...');
|
|
32
|
+
|
|
33
|
+
// Detect content type with fallback logic
|
|
34
|
+
const contentType = getContentType(
|
|
35
|
+
data,
|
|
36
|
+
options?.contentType
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
console.log(`[Upload] Detected content-type: ${contentType} for file: ${fileName}`);
|
|
40
|
+
|
|
41
|
+
// Create upload flow with full metadata (identifier + tags)
|
|
42
|
+
const flow = client.walrus.writeFilesFlow({
|
|
43
|
+
files: [
|
|
44
|
+
WalrusFile.from({
|
|
45
|
+
contents: bytes,
|
|
46
|
+
identifier: fileName, // Original filename with extension
|
|
47
|
+
tags: {
|
|
48
|
+
'content-type': contentType,
|
|
49
|
+
'original-name': fileName,
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Step 1: Encode the file
|
|
56
|
+
console.log('[Upload] Step 1: Encoding file...');
|
|
57
|
+
await flow.encode();
|
|
58
|
+
console.log('[Upload] Step 1: Encode complete');
|
|
59
|
+
|
|
60
|
+
// Step 2: Register the blob on-chain (requires wallet signature)
|
|
61
|
+
console.log('[Upload] Step 2: Creating register transaction...');
|
|
62
|
+
const registerTx = flow.register({
|
|
63
|
+
epochs: options?.epochs || 1,
|
|
64
|
+
owner: options.signer.address,
|
|
65
|
+
deletable: true,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Sign and execute the register transaction (dapp-kit will handle wrapping)
|
|
69
|
+
console.log('[Upload] Step 2: Signing register transaction...');
|
|
70
|
+
const registerResult = await options.signer.signAndExecuteTransaction({
|
|
71
|
+
transaction: registerTx,
|
|
72
|
+
});
|
|
73
|
+
console.log('[Upload] Step 2: Register complete, digest:', registerResult.digest);
|
|
74
|
+
|
|
75
|
+
// Step 3: Upload the data to storage nodes
|
|
76
|
+
console.log('[Upload] Step 3: Uploading to storage nodes...');
|
|
77
|
+
await flow.upload({ digest: registerResult.digest });
|
|
78
|
+
console.log('[Upload] Step 3: Upload complete');
|
|
79
|
+
|
|
80
|
+
// Step 4: Certify the blob (requires wallet signature)
|
|
81
|
+
console.log('[Upload] Step 4: Creating certify transaction...');
|
|
82
|
+
const certifyTx = flow.certify();
|
|
83
|
+
|
|
84
|
+
// Sign and execute the certify transaction (dapp-kit will handle wrapping)
|
|
85
|
+
console.log('[Upload] Step 4: Signing certify transaction...');
|
|
86
|
+
await options.signer.signAndExecuteTransaction({
|
|
87
|
+
transaction: certifyTx,
|
|
88
|
+
});
|
|
89
|
+
console.log('[Upload] Step 4: Certify complete');
|
|
90
|
+
|
|
91
|
+
// Step 5: Get the uploaded files
|
|
92
|
+
console.log('[Upload] Step 5: Getting file list...');
|
|
93
|
+
const files = await flow.listFiles();
|
|
94
|
+
|
|
95
|
+
if (!files || files.length === 0) {
|
|
96
|
+
throw new Error('Upload completed but no files were returned');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const blobId = files[0].blobId;
|
|
100
|
+
console.log('[Upload] Success! Blob ID:', blobId);
|
|
101
|
+
return blobId;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('[Upload] Error details:', error);
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async download(
|
|
111
|
+
blobId: string,
|
|
112
|
+
options?: DownloadOptions
|
|
113
|
+
): Promise<Uint8Array | string | unknown> {
|
|
114
|
+
const client = getWalrusClient();
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
// Use getFiles for better API - supports future batch downloads
|
|
118
|
+
const [file] = await client.walrus.getFiles({ ids: [blobId] });
|
|
119
|
+
|
|
120
|
+
// Handle different output formats
|
|
121
|
+
switch (options?.format) {
|
|
122
|
+
case 'text':
|
|
123
|
+
return await file.text();
|
|
124
|
+
case 'json':
|
|
125
|
+
return await file.json();
|
|
126
|
+
default:
|
|
127
|
+
return await file.bytes();
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
// Handle retryable errors by resetting client and retrying once
|
|
131
|
+
if (error instanceof RetryableWalrusClientError) {
|
|
132
|
+
console.warn(`[Download] Retryable error detected, resetting client and retrying...`);
|
|
133
|
+
resetWalrusClient();
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const retryClient = getWalrusClient();
|
|
137
|
+
const [file] = await retryClient.walrus.getFiles({ ids: [blobId] });
|
|
138
|
+
|
|
139
|
+
switch (options?.format) {
|
|
140
|
+
case 'text':
|
|
141
|
+
return await file.text();
|
|
142
|
+
case 'json':
|
|
143
|
+
return await file.json();
|
|
144
|
+
default:
|
|
145
|
+
return await file.bytes();
|
|
146
|
+
}
|
|
147
|
+
} catch (retryError) {
|
|
148
|
+
throw new Error(
|
|
149
|
+
`Download retry failed for blob ${blobId}: ${retryError instanceof Error ? retryError.message : 'Unknown error'}`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Download failed for blob ${blobId}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async getMetadata(blobId: string): Promise<BlobMetadata> {
|
|
161
|
+
const client = getWalrusClient();
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
// Get metadata from Walrus
|
|
165
|
+
const metadata = await client.walrus.getBlobMetadata({ blobId });
|
|
166
|
+
|
|
167
|
+
// Get WalrusFile to extract identifier and tags
|
|
168
|
+
const [file] = await client.walrus.getFiles({ ids: [blobId] });
|
|
169
|
+
const identifier = await file.getIdentifier();
|
|
170
|
+
const tags = await file.getTags();
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
blobId,
|
|
174
|
+
size: Number(metadata.metadata.V1.unencoded_length),
|
|
175
|
+
contentType: tags['content-type'] || 'application/octet-stream',
|
|
176
|
+
fileName: tags['original-name'] || identifier || `blob-${blobId.slice(0, 8)}`,
|
|
177
|
+
tags,
|
|
178
|
+
createdAt: (metadata as unknown as { createdAt?: number }).createdAt || Date.now(),
|
|
179
|
+
};
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`Failed to get metadata for blob ${blobId}: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async exists(blobId: string): Promise<boolean> {
|
|
188
|
+
try {
|
|
189
|
+
await this.getMetadata(blobId);
|
|
190
|
+
return true;
|
|
191
|
+
} catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const storageAdapter = new MystenStorageAdapter();
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { walrus, WalrusClient } from '@mysten/walrus';
|
|
2
|
+
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client';
|
|
3
|
+
import { loadEnv } from '../../utils/env.js';
|
|
4
|
+
|
|
5
|
+
// Types
|
|
6
|
+
export type WalrusNetwork = 'testnet' | 'mainnet';
|
|
7
|
+
|
|
8
|
+
export interface MystenWalrusConfig {
|
|
9
|
+
network: WalrusNetwork;
|
|
10
|
+
publisherUrl?: string;
|
|
11
|
+
aggregatorUrl?: string;
|
|
12
|
+
suiRpcUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type WalrusExtendedClient = SuiClient & { walrus: WalrusClient };
|
|
16
|
+
|
|
17
|
+
// Network Configurations
|
|
18
|
+
const NETWORK_CONFIGS: Record<WalrusNetwork, MystenWalrusConfig> = {
|
|
19
|
+
testnet: {
|
|
20
|
+
network: 'testnet',
|
|
21
|
+
publisherUrl: 'https://publisher.walrus-testnet.walrus.space',
|
|
22
|
+
aggregatorUrl: 'https://aggregator.walrus-testnet.walrus.space',
|
|
23
|
+
suiRpcUrl: 'https://fullnode.testnet.sui.io:443',
|
|
24
|
+
},
|
|
25
|
+
mainnet: {
|
|
26
|
+
network: 'mainnet',
|
|
27
|
+
publisherUrl: 'https://publisher.walrus.space',
|
|
28
|
+
aggregatorUrl: 'https://aggregator.walrus.space',
|
|
29
|
+
suiRpcUrl: 'https://fullnode.mainnet.sui.io:443',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function getNetworkConfig(network: WalrusNetwork): MystenWalrusConfig {
|
|
34
|
+
return NETWORK_CONFIGS[network];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Client Management
|
|
38
|
+
let walrusClient: WalrusExtendedClient | null = null;
|
|
39
|
+
|
|
40
|
+
export function getWalrusClient(): WalrusExtendedClient {
|
|
41
|
+
if (walrusClient) {
|
|
42
|
+
return walrusClient;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const env = loadEnv();
|
|
46
|
+
|
|
47
|
+
// Validate network value before casting
|
|
48
|
+
const allowedNetworks: WalrusNetwork[] = ['testnet', 'mainnet'];
|
|
49
|
+
if (!allowedNetworks.includes(env.walrusNetwork as WalrusNetwork)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Invalid WALRUS_NETWORK: ${env.walrusNetwork}. Must be one of: ${allowedNetworks.join(', ')}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const network = env.walrusNetwork as WalrusNetwork;
|
|
55
|
+
const config = getNetworkConfig(network);
|
|
56
|
+
|
|
57
|
+
// Use SuiClient (required for Walrus SDK and CoinWithBalance intent)
|
|
58
|
+
const suiClient = new SuiClient({
|
|
59
|
+
url:
|
|
60
|
+
env.suiRpc ||
|
|
61
|
+
config.suiRpcUrl ||
|
|
62
|
+
getFullnodeUrl(network === 'testnet' ? 'testnet' : 'mainnet'),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Extend the client with Walrus capabilities and upload relay
|
|
66
|
+
walrusClient = suiClient.$extend(
|
|
67
|
+
walrus({
|
|
68
|
+
network: network, // REQUIRED: Walrus SDK needs to know which network
|
|
69
|
+
// Use CDN for WASM - more reliable than bundler resolution
|
|
70
|
+
wasmUrl: 'https://unpkg.com/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm',
|
|
71
|
+
uploadRelay: {
|
|
72
|
+
host:
|
|
73
|
+
env.walrusPublisher ||
|
|
74
|
+
config.publisherUrl ||
|
|
75
|
+
`https://upload-relay.${network}.walrus.space`,
|
|
76
|
+
sendTip: null, // Skip tip config fetch to avoid CORS issues in development
|
|
77
|
+
},
|
|
78
|
+
...(env.walrusAggregator && { aggregatorUrl: env.walrusAggregator }),
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return walrusClient;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function resetWalrusClient(): void {
|
|
86
|
+
walrusClient = null;
|
|
87
|
+
}
|