@blu1606/create-walrus-app 0.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 (119) hide show
  1. package/dist/__tests__/helpers/adapter-compliance.d.ts +2 -0
  2. package/dist/__tests__/helpers/adapter-compliance.js +47 -0
  3. package/dist/__tests__/helpers/fixtures.d.ts +21 -0
  4. package/dist/__tests__/helpers/fixtures.js +30 -0
  5. package/dist/__tests__/helpers/fs-helpers.d.ts +12 -0
  6. package/dist/__tests__/helpers/fs-helpers.js +35 -0
  7. package/dist/__tests__/helpers/index.d.ts +4 -0
  8. package/dist/__tests__/helpers/index.js +4 -0
  9. package/dist/__tests__/helpers/test-hooks.d.ts +3 -0
  10. package/dist/__tests__/helpers/test-hooks.js +18 -0
  11. package/dist/context.d.ts +2 -0
  12. package/dist/context.js +43 -0
  13. package/dist/context.test.d.ts +1 -0
  14. package/dist/context.test.js +98 -0
  15. package/dist/generator/file-ops.d.ts +12 -0
  16. package/dist/generator/file-ops.js +40 -0
  17. package/dist/generator/index.d.ts +2 -0
  18. package/dist/generator/index.js +75 -0
  19. package/dist/generator/index.test.d.ts +1 -0
  20. package/dist/generator/index.test.js +143 -0
  21. package/dist/generator/layers.d.ts +3 -0
  22. package/dist/generator/layers.js +59 -0
  23. package/dist/generator/layers.test.d.ts +1 -0
  24. package/dist/generator/layers.test.js +92 -0
  25. package/dist/generator/merge.d.ts +14 -0
  26. package/dist/generator/merge.js +62 -0
  27. package/dist/generator/merge.test.d.ts +1 -0
  28. package/dist/generator/merge.test.js +79 -0
  29. package/dist/generator/transform.d.ts +21 -0
  30. package/dist/generator/transform.js +52 -0
  31. package/dist/generator/transform.test.d.ts +1 -0
  32. package/dist/generator/transform.test.js +51 -0
  33. package/dist/generator/types.d.ts +18 -0
  34. package/dist/generator/types.js +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +106 -0
  37. package/dist/matrix.d.ts +31 -0
  38. package/dist/matrix.js +31 -0
  39. package/dist/matrix.test.d.ts +1 -0
  40. package/dist/matrix.test.js +70 -0
  41. package/dist/post-install/git.d.ts +12 -0
  42. package/dist/post-install/git.js +94 -0
  43. package/dist/post-install/index.d.ts +16 -0
  44. package/dist/post-install/index.js +56 -0
  45. package/dist/post-install/messages.d.ts +9 -0
  46. package/dist/post-install/messages.js +49 -0
  47. package/dist/post-install/package-manager.d.ts +14 -0
  48. package/dist/post-install/package-manager.js +57 -0
  49. package/dist/post-install/validator.d.ts +14 -0
  50. package/dist/post-install/validator.js +114 -0
  51. package/dist/prompts.d.ts +2 -0
  52. package/dist/prompts.js +115 -0
  53. package/dist/test-base.d.ts +1 -0
  54. package/dist/test-base.js +42 -0
  55. package/dist/types.d.ts +19 -0
  56. package/dist/types.js +1 -0
  57. package/dist/types.test.d.ts +1 -0
  58. package/dist/types.test.js +65 -0
  59. package/dist/utils/detect-pm.d.ts +2 -0
  60. package/dist/utils/detect-pm.js +10 -0
  61. package/dist/utils/detect-pm.test.d.ts +1 -0
  62. package/dist/utils/detect-pm.test.js +52 -0
  63. package/dist/utils/logger.d.ts +6 -0
  64. package/dist/utils/logger.js +7 -0
  65. package/dist/validator.d.ts +3 -0
  66. package/dist/validator.js +48 -0
  67. package/dist/validator.test.d.ts +1 -0
  68. package/dist/validator.test.js +96 -0
  69. package/package.json +68 -0
  70. package/templates/base/.env.example +31 -0
  71. package/templates/base/README.md +54 -0
  72. package/templates/base/package.json +19 -0
  73. package/templates/base/src/adapters/storage.ts +58 -0
  74. package/templates/base/src/types/index.ts +9 -0
  75. package/templates/base/src/types/walrus.ts +22 -0
  76. package/templates/base/src/utils/env.ts +41 -0
  77. package/templates/base/src/utils/format.ts +29 -0
  78. package/templates/base/tsconfig.json +19 -0
  79. package/templates/gallery/README.md +44 -0
  80. package/templates/gallery/package.json +6 -0
  81. package/templates/gallery/src/App.tsx +21 -0
  82. package/templates/gallery/src/components/FileCard.tsx +27 -0
  83. package/templates/gallery/src/components/GalleryGrid.tsx +30 -0
  84. package/templates/gallery/src/components/UploadModal.tsx +45 -0
  85. package/templates/gallery/src/styles.css +58 -0
  86. package/templates/gallery/src/types/gallery.ts +13 -0
  87. package/templates/gallery/src/utils/index-manager.ts +37 -0
  88. package/templates/react/.eslintrc.json +26 -0
  89. package/templates/react/README.md +80 -0
  90. package/templates/react/index.html +13 -0
  91. package/templates/react/package.json +32 -0
  92. package/templates/react/src/App.tsx +14 -0
  93. package/templates/react/src/components/Layout.tsx +21 -0
  94. package/templates/react/src/components/WalletConnect.tsx +21 -0
  95. package/templates/react/src/dapp-kit.css +1 -0
  96. package/templates/react/src/hooks/useStorage.ts +40 -0
  97. package/templates/react/src/hooks/useWallet.ts +16 -0
  98. package/templates/react/src/index.css +50 -0
  99. package/templates/react/src/index.ts +10 -0
  100. package/templates/react/src/main.tsx +17 -0
  101. package/templates/react/src/providers/QueryProvider.tsx +18 -0
  102. package/templates/react/src/providers/WalletProvider.tsx +37 -0
  103. package/templates/react/tsconfig.json +27 -0
  104. package/templates/react/tsconfig.node.json +10 -0
  105. package/templates/react/vite.config.ts +19 -0
  106. package/templates/sdk-mysten/README.md +65 -0
  107. package/templates/sdk-mysten/package.json +14 -0
  108. package/templates/sdk-mysten/src/adapter.ts +80 -0
  109. package/templates/sdk-mysten/src/client.ts +45 -0
  110. package/templates/sdk-mysten/src/config.ts +33 -0
  111. package/templates/sdk-mysten/src/index.ts +11 -0
  112. package/templates/sdk-mysten/src/types.ts +19 -0
  113. package/templates/sdk-mysten/test/adapter.test.ts +20 -0
  114. package/templates/simple-upload/README.md +24 -0
  115. package/templates/simple-upload/package.json +6 -0
  116. package/templates/simple-upload/src/App.tsx +27 -0
  117. package/templates/simple-upload/src/components/FilePreview.tsx +40 -0
  118. package/templates/simple-upload/src/components/UploadForm.tsx +51 -0
  119. package/templates/simple-upload/src/styles.css +33 -0
@@ -0,0 +1,41 @@
1
+ export interface EnvConfig {
2
+ walrusNetwork: string;
3
+ walrusAggregator: string;
4
+ walrusPublisher: string;
5
+ suiNetwork: string;
6
+ suiRpc: string;
7
+ blockberryKey?: string;
8
+ }
9
+
10
+ export function loadEnv(): EnvConfig {
11
+ const getEnv = (key: string, required = true): string => {
12
+ const value = import.meta.env[key];
13
+ if (required && !value) {
14
+ throw new Error(`Missing required environment variable: ${key}`);
15
+ }
16
+ return value || '';
17
+ };
18
+
19
+ return {
20
+ walrusNetwork: getEnv('VITE_WALRUS_NETWORK'),
21
+ walrusAggregator: getEnv('VITE_WALRUS_AGGREGATOR'),
22
+ walrusPublisher: getEnv('VITE_WALRUS_PUBLISHER'),
23
+ suiNetwork: getEnv('VITE_SUI_NETWORK'),
24
+ suiRpc: getEnv('VITE_SUI_RPC'),
25
+ blockberryKey: getEnv('VITE_BLOCKBERRY_KEY', false),
26
+ };
27
+ }
28
+
29
+ export function validateEnv(config: EnvConfig): void {
30
+ if (!['testnet', 'mainnet', 'devnet'].includes(config.walrusNetwork)) {
31
+ throw new Error(`Invalid WALRUS_NETWORK: ${config.walrusNetwork}`);
32
+ }
33
+
34
+ if (!config.walrusAggregator.startsWith('http')) {
35
+ throw new Error('WALRUS_AGGREGATOR must be a valid HTTP URL');
36
+ }
37
+
38
+ if (!config.walrusPublisher.startsWith('http')) {
39
+ throw new Error('WALRUS_PUBLISHER must be a valid HTTP URL');
40
+ }
41
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Format bytes to human-readable size
3
+ */
4
+ export function formatBytes(bytes: number): string {
5
+ if (bytes === 0) return '0 Bytes';
6
+
7
+ const k = 1024;
8
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
9
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
10
+
11
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
12
+ }
13
+
14
+ /**
15
+ * Format blob ID for display (truncate middle)
16
+ */
17
+ export function formatBlobId(blobId: string, length = 12): string {
18
+ if (blobId.length <= length) return blobId;
19
+
20
+ const part = Math.floor((length - 3) / 2);
21
+ return `${blobId.slice(0, part)}...${blobId.slice(-part)}`;
22
+ }
23
+
24
+ /**
25
+ * Format timestamp to locale string
26
+ */
27
+ export function formatDate(timestamp: number): string {
28
+ return new Date(timestamp).toLocaleString();
29
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "resolveJsonModule": true,
8
+ "allowImportingTsExtensions": true,
9
+ "strict": true,
10
+ "noUnusedLocals": true,
11
+ "noUnusedParameters": true,
12
+ "noFallthroughCasesInSwitch": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "types": ["vite/client"]
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }
@@ -0,0 +1,44 @@
1
+ # File Gallery Use Case
2
+
3
+ Manage multiple files with a persistent index.
4
+
5
+ ## Features
6
+
7
+ - Upload multiple files
8
+ - Grid view of all files
9
+ - Local index (localStorage)
10
+ - Delete files from gallery
11
+ - File metadata display
12
+
13
+ ## Index Format
14
+
15
+ ```json
16
+ {
17
+ "version": "1.0",
18
+ "items": [
19
+ {
20
+ "blobId": "abc123...",
21
+ "name": "photo.jpg",
22
+ "size": 102400,
23
+ "contentType": "image/jpeg",
24
+ "uploadedAt": 1705449600000
25
+ }
26
+ ],
27
+ "lastModified": 1705449600000
28
+ }
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ 1. Click "Choose File" to select a file
34
+ 2. Click "Add to Gallery" to upload
35
+ 3. Files appear in the grid below
36
+ 4. Click "Delete" to remove files from gallery
37
+
38
+ ## Code Structure
39
+
40
+ - `GalleryGrid.tsx` - Grid layout for files
41
+ - `FileCard.tsx` - Individual file display
42
+ - `UploadModal.tsx` - Upload UI
43
+ - `index-manager.ts` - localStorage persistence
44
+ - `gallery.ts` - Type definitions
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "dependencies": {}
6
+ }
@@ -0,0 +1,21 @@
1
+ import { useState } from 'react';
2
+ import { Layout } from '../../react/src/components/Layout.js';
3
+ import { GalleryGrid } from './components/GalleryGrid.js';
4
+ import { UploadModal } from './components/UploadModal.js';
5
+ import './styles.css';
6
+
7
+ function App() {
8
+ const [refreshKey, setRefreshKey] = useState(0);
9
+
10
+ return (
11
+ <Layout>
12
+ <div className="gallery-app">
13
+ <h2>🖼️ File Gallery</h2>
14
+ <UploadModal onSuccess={() => setRefreshKey((k) => k + 1)} />
15
+ <GalleryGrid key={refreshKey} />
16
+ </div>
17
+ </Layout>
18
+ );
19
+ }
20
+
21
+ export default App;
@@ -0,0 +1,27 @@
1
+ import { formatBytes, formatDate } from '../../../base/src/utils/format.js';
2
+ import { removeItem } from '../utils/index-manager.js';
3
+ import type { GalleryItem } from '../types/gallery.js';
4
+
5
+ interface FileCardProps {
6
+ item: GalleryItem;
7
+ onDelete: () => void;
8
+ }
9
+
10
+ export function FileCard({ item, onDelete }: FileCardProps) {
11
+ const handleDelete = () => {
12
+ if (confirm(`Delete ${item.name}?`)) {
13
+ removeItem(item.blobId);
14
+ onDelete();
15
+ }
16
+ };
17
+
18
+ return (
19
+ <div className="file-card">
20
+ <h4>{item.name}</h4>
21
+ <p>Size: {formatBytes(item.size)}</p>
22
+ <p>Uploaded: {formatDate(item.uploadedAt)}</p>
23
+ <p className="blob-id">Blob ID: {item.blobId.slice(0, 12)}...</p>
24
+ <button onClick={handleDelete}>Delete</button>
25
+ </div>
26
+ );
27
+ }
@@ -0,0 +1,30 @@
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';
5
+
6
+ export function GalleryGrid() {
7
+ const [items, setItems] = useState<GalleryItem[]>([]);
8
+
9
+ useEffect(() => {
10
+ const index = loadIndex();
11
+ setItems(index.items);
12
+ }, []);
13
+
14
+ const refreshGallery = () => {
15
+ const index = loadIndex();
16
+ setItems(index.items);
17
+ };
18
+
19
+ return (
20
+ <div className="gallery-grid">
21
+ {items.length === 0 ? (
22
+ <p>No files yet. Upload your first file!</p>
23
+ ) : (
24
+ items.map((item) => (
25
+ <FileCard key={item.blobId} item={item} onDelete={refreshGallery} />
26
+ ))
27
+ )}
28
+ </div>
29
+ );
30
+ }
@@ -0,0 +1,45 @@
1
+ import { useState } from 'react';
2
+ import { useUpload } from '../../../react/src/hooks/useStorage.js';
3
+ import { addItem } from '../utils/index-manager.js';
4
+
5
+ interface UploadModalProps {
6
+ onSuccess: () => void;
7
+ }
8
+
9
+ export function UploadModal({ onSuccess }: UploadModalProps) {
10
+ const [file, setFile] = useState<File | null>(null);
11
+ const upload = useUpload();
12
+
13
+ const handleUpload = async () => {
14
+ if (!file) return;
15
+
16
+ upload.mutate(
17
+ { file, options: { epochs: 1 } },
18
+ {
19
+ onSuccess: async (data) => {
20
+ addItem({
21
+ blobId: data.blobId,
22
+ name: file.name,
23
+ size: file.size,
24
+ contentType: file.type,
25
+ uploadedAt: Date.now(),
26
+ });
27
+ setFile(null);
28
+ onSuccess();
29
+ },
30
+ }
31
+ );
32
+ };
33
+
34
+ return (
35
+ <div className="upload-modal">
36
+ <input
37
+ type="file"
38
+ onChange={(e) => setFile(e.target.files?.[0] || null)}
39
+ />
40
+ <button onClick={handleUpload} disabled={!file || upload.isPending}>
41
+ {upload.isPending ? 'Uploading...' : 'Add to Gallery'}
42
+ </button>
43
+ </div>
44
+ );
45
+ }
@@ -0,0 +1,58 @@
1
+ .gallery-app {
2
+ max-width: 1200px;
3
+ margin: 0 auto;
4
+ }
5
+
6
+ .upload-modal {
7
+ margin: 2rem 0;
8
+ padding: 1.5rem;
9
+ border: 1px solid #333;
10
+ border-radius: 8px;
11
+ display: flex;
12
+ gap: 1rem;
13
+ align-items: center;
14
+ }
15
+
16
+ .gallery-grid {
17
+ display: grid;
18
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
19
+ gap: 1.5rem;
20
+ margin: 2rem 0;
21
+ }
22
+
23
+ .file-card {
24
+ padding: 1.5rem;
25
+ border: 1px solid #333;
26
+ border-radius: 8px;
27
+ background: #1a1a1a;
28
+ }
29
+
30
+ .file-card h4 {
31
+ margin: 0 0 1rem 0;
32
+ color: #fff;
33
+ overflow: hidden;
34
+ text-overflow: ellipsis;
35
+ white-space: nowrap;
36
+ }
37
+
38
+ .file-card p {
39
+ margin: 0.5rem 0;
40
+ font-size: 0.9rem;
41
+ color: #aaa;
42
+ }
43
+
44
+ .file-card .blob-id {
45
+ font-family: monospace;
46
+ font-size: 0.8rem;
47
+ color: #888;
48
+ }
49
+
50
+ .file-card button {
51
+ margin-top: 1rem;
52
+ width: 100%;
53
+ }
54
+
55
+ button:disabled {
56
+ opacity: 0.5;
57
+ cursor: not-allowed;
58
+ }
@@ -0,0 +1,13 @@
1
+ export interface GalleryItem {
2
+ blobId: string;
3
+ name: string;
4
+ size: number;
5
+ contentType: string;
6
+ uploadedAt: number;
7
+ }
8
+
9
+ export interface GalleryIndex {
10
+ version: '1.0';
11
+ items: GalleryItem[];
12
+ lastModified: number;
13
+ }
@@ -0,0 +1,37 @@
1
+ import type { GalleryIndex, GalleryItem } from '../types/gallery.js';
2
+
3
+ const INDEX_KEY = 'gallery-index';
4
+
5
+ export function loadIndex(): GalleryIndex {
6
+ try {
7
+ const stored = localStorage.getItem(INDEX_KEY);
8
+ if (!stored) {
9
+ return { version: '1.0', items: [], lastModified: Date.now() };
10
+ }
11
+ const parsed = JSON.parse(stored);
12
+ if (!parsed.version || !Array.isArray(parsed.items)) {
13
+ throw new Error('Invalid index format');
14
+ }
15
+ return parsed;
16
+ } catch (error) {
17
+ console.warn('Failed to load gallery index, resetting:', error);
18
+ return { version: '1.0', items: [], lastModified: Date.now() };
19
+ }
20
+ }
21
+
22
+ export function saveIndex(index: GalleryIndex): void {
23
+ index.lastModified = Date.now();
24
+ localStorage.setItem(INDEX_KEY, JSON.stringify(index));
25
+ }
26
+
27
+ export function addItem(item: GalleryItem): void {
28
+ const index = loadIndex();
29
+ index.items.push(item);
30
+ saveIndex(index);
31
+ }
32
+
33
+ export function removeItem(blobId: string): void {
34
+ const index = loadIndex();
35
+ index.items = index.items.filter((item) => item.blobId !== blobId);
36
+ saveIndex(index);
37
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "extends": [
3
+ "eslint:recommended",
4
+ "plugin:@typescript-eslint/recommended",
5
+ "plugin:react/recommended",
6
+ "plugin:react-hooks/recommended"
7
+ ],
8
+ "parser": "@typescript-eslint/parser",
9
+ "parserOptions": {
10
+ "ecmaVersion": 2020,
11
+ "sourceType": "module",
12
+ "ecmaFeatures": {
13
+ "jsx": true
14
+ }
15
+ },
16
+ "plugins": ["@typescript-eslint", "react", "react-hooks"],
17
+ "rules": {
18
+ "no-console": "warn",
19
+ "react/react-in-jsx-scope": "off"
20
+ },
21
+ "settings": {
22
+ "react": {
23
+ "version": "detect"
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,80 @@
1
+ # React + Vite Framework Layer
2
+
3
+ Modern React 18 application with Vite build system.
4
+
5
+ ## Features
6
+
7
+ ✅ **React 18** - Hooks, Suspense, Concurrent features
8
+ ✅ **Vite 5** - Lightning-fast HMR and builds
9
+ ✅ **TanStack Query** - Async state management
10
+ ✅ **@mysten/dapp-kit** - Sui wallet integration
11
+ ✅ **TypeScript** - Full type safety
12
+
13
+ ## Project Structure
14
+
15
+ ```
16
+ src/
17
+ ├── components/ # Reusable UI components
18
+ ├── providers/ # Context providers
19
+ ├── hooks/ # Custom React hooks
20
+ ├── App.tsx # Root component
21
+ └── main.tsx # Entry point
22
+ ```
23
+
24
+ ## Custom Hooks
25
+
26
+ ### `useUpload()`
27
+
28
+ Upload files to Walrus:
29
+
30
+ ```typescript
31
+ const upload = useUpload();
32
+
33
+ upload.mutate({ file: myFile, options: { epochs: 1 } });
34
+ ```
35
+
36
+ ### `useDownload(blobId)`
37
+
38
+ Download blob data:
39
+
40
+ ```typescript
41
+ const { data, isLoading } = useDownload(blobId);
42
+ ```
43
+
44
+ ### `useMetadata(blobId)`
45
+
46
+ Fetch blob metadata:
47
+
48
+ ```typescript
49
+ const { data: metadata } = useMetadata(blobId);
50
+ console.log(`Size: ${metadata.size} bytes`);
51
+ ```
52
+
53
+ ### `useWallet()`
54
+
55
+ Access wallet state:
56
+
57
+ ```typescript
58
+ const { isConnected, address } = useWallet();
59
+ ```
60
+
61
+ ## Development
62
+
63
+ ```bash
64
+ npm run dev # Start dev server (http://localhost:3000)
65
+ npm run build # Build for production
66
+ npm run preview # Preview production build
67
+ ```
68
+
69
+ ## Wallet Setup
70
+
71
+ 1. Install Sui Wallet browser extension
72
+ 2. Get testnet SUI from faucet
73
+ 3. Connect wallet in the app
74
+
75
+ ## Resources
76
+
77
+ - [React Docs](https://react.dev)
78
+ - [Vite Docs](https://vitejs.dev)
79
+ - [TanStack Query](https://tanstack.com/query)
80
+ - [@mysten/dapp-kit](https://sdk.mystenlabs.com/dapp-kit)
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Walrus App</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview",
10
+ "lint": "eslint . --ext .ts,.tsx",
11
+ "type-check": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "react": "^18.2.0",
15
+ "react-dom": "^18.2.0",
16
+ "@tanstack/react-query": "^5.17.0",
17
+ "@mysten/dapp-kit": "^0.14.0",
18
+ "@mysten/sui": "^1.10.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/react": "^18.2.48",
22
+ "@types/react-dom": "^18.2.18",
23
+ "@vitejs/plugin-react": "^4.2.1",
24
+ "vite": "^5.0.11",
25
+ "typescript": "^5.3.3",
26
+ "eslint": "^8.56.0",
27
+ "@typescript-eslint/eslint-plugin": "^6.19.0",
28
+ "@typescript-eslint/parser": "^6.19.0",
29
+ "eslint-plugin-react": "^7.33.2",
30
+ "eslint-plugin-react-hooks": "^4.6.0"
31
+ }
32
+ }
@@ -0,0 +1,14 @@
1
+ import { Layout } from './components/Layout.js';
2
+
3
+ function App() {
4
+ return (
5
+ <Layout>
6
+ <div className="welcome">
7
+ <h2>Welcome to Walrus Starter Kit</h2>
8
+ <p>This app will be customized by the use case layer</p>
9
+ </div>
10
+ </Layout>
11
+ );
12
+ }
13
+
14
+ export default App;
@@ -0,0 +1,21 @@
1
+ import { ReactNode } from 'react';
2
+ import { WalletConnect } from './WalletConnect.js';
3
+
4
+ interface LayoutProps {
5
+ children: ReactNode;
6
+ }
7
+
8
+ export function Layout({ children }: LayoutProps) {
9
+ return (
10
+ <div className="app-layout">
11
+ <header className="app-header">
12
+ <h1>🌊 Walrus App</h1>
13
+ <WalletConnect />
14
+ </header>
15
+ <main className="app-main">{children}</main>
16
+ <footer className="app-footer">
17
+ <p>Powered by Walrus & Sui</p>
18
+ </footer>
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,21 @@
1
+ import { ConnectButton } from '@mysten/dapp-kit';
2
+ import { useWallet } from '../hooks/useWallet.js';
3
+
4
+ export function WalletConnect() {
5
+ const { isConnected, address } = useWallet();
6
+
7
+ return (
8
+ <div className="wallet-connect">
9
+ {isConnected ? (
10
+ <div className="wallet-info">
11
+ <span>
12
+ Connected: {address?.slice(0, 6)}...{address?.slice(-4)}
13
+ </span>
14
+ </div>
15
+ ) : (
16
+ <p>Please connect your wallet</p>
17
+ )}
18
+ <ConnectButton />
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1 @@
1
+ @import '@mysten/dapp-kit/dist/index.css';
@@ -0,0 +1,40 @@
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
+ }
19
+
20
+ export function useDownload(blobId: string | null) {
21
+ return useQuery({
22
+ queryKey: ['blob', blobId],
23
+ queryFn: async () => {
24
+ if (!blobId) throw new Error('No blob ID provided');
25
+ return await storageAdapter.download(blobId);
26
+ },
27
+ enabled: !!blobId,
28
+ });
29
+ }
30
+
31
+ export function useMetadata(blobId: string | null) {
32
+ return useQuery({
33
+ queryKey: ['metadata', blobId],
34
+ queryFn: async () => {
35
+ if (!blobId) throw new Error('No blob ID provided');
36
+ return await storageAdapter.getMetadata(blobId);
37
+ },
38
+ enabled: !!blobId,
39
+ });
40
+ }
@@ -0,0 +1,16 @@
1
+ import {
2
+ useCurrentAccount,
3
+ useSignAndExecuteTransaction,
4
+ } from '@mysten/dapp-kit';
5
+
6
+ export function useWallet() {
7
+ const currentAccount = useCurrentAccount();
8
+ const { mutate: signAndExecute } = useSignAndExecuteTransaction();
9
+
10
+ return {
11
+ account: currentAccount,
12
+ isConnected: !!currentAccount,
13
+ address: currentAccount?.address,
14
+ signAndExecute,
15
+ };
16
+ }