diff --git a/.gitignore b/.gitignore
index 83720016..be3899a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,4 +46,10 @@ playwright-report/
test-results/
# cap
-public/caps.json
\ No newline at end of file
+public/caps.json
+
+# claude
+.claude
+
+# cursor
+.cursor
\ No newline at end of file
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 00000000..78c1e00a
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,133 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Package Management & Scripts
+
+This project uses **pnpm** for package management. Common commands:
+
+```bash
+# Development
+pnpm dev # Start development server
+pnpm build # Build for production
+pnpm preview # Preview production build
+
+# Code Quality
+pnpm lint # Lint with Biome
+pnpm lint:fix # Fix linting issues
+pnpm format # Check formatting
+pnpm format:fix # Fix formatting
+pnpm check # Run all checks
+pnpm check:fix # Fix all issues
+```
+
+**Important**: Always use `pnpm` commands. The project uses Biome (not ESLint/Prettier) for linting and formatting.
+
+## Application Architecture
+
+**Nuwa Client** is a React 19 + TypeScript + Vite application for AI chat with CAP (Conversational AI Programs) creation capabilities, Web3 wallet integration, and decentralized identity.
+
+### Core Technology Stack
+
+- **Frontend**: React 19, TypeScript, Vite, React Router v7
+- **Styling**: Tailwind CSS + Radix UI components
+- **State**: Zustand with persistence middleware
+- **Storage**: Dexie (IndexedDB) for structured data
+- **AI**: AI SDK (@ai-sdk/react), OpenRouter, LiteLLM providers
+- **Web3**: Reown AppKit, Wagmi, Viem
+- **Identity**: @nuwa-ai/identity-kit (decentralized identity)
+- **Code Quality**: Biome for linting/formatting
+
+### Feature-Based Architecture
+
+The codebase uses a feature-based structure under `src/features/`:
+
+- **`auth/`** - Authentication and authorization
+- **`chat/`** - Core chat functionality with AI models
+- **`cap-studio/`** - CAP creation/editing interface (like an IDE)
+- **`cap-store/`** - CAP marketplace and discovery
+- **`settings/`** - User preferences and configuration
+- **`sidebar/`** - Navigation and chat history
+- **`wallet/`** - Web3 wallet integration and payments
+
+Each feature follows this structure:
+```
+feature/
+├── components/ # React components
+├── hooks/ # Custom React hooks
+├── stores.ts # Zustand state stores
+├── services.ts # Business logic
+├── types.ts # TypeScript definitions
+└── utils.ts # Utility functions
+```
+
+### Key Architectural Concepts
+
+**CAPs (Conversational AI Programs)**: User-configurable AI assistants with custom prompts, models, and MCP (Model Context Protocol) tool integrations. Users can create, edit, and share CAPs.
+
+**MCP Integration**: The app connects to MCP servers to provide tools and capabilities to AI models. Managed by `GlobalMCPManager` singleton.
+
+**Decentralized Identity**: All user data is scoped to their DID (Decentralized Identifier) for privacy and portability.
+
+**Multi-Layer Storage**:
+- Zustand stores (in-memory state)
+- localStorage (user preferences)
+- IndexedDB via Dexie (structured data: chats, CAPs, settings)
+
+### Core Services
+
+**Global Services** (in `src/shared/services/`):
+- **`global-mcp-manager.ts`** - Manages MCP server connections and tool registration
+- **`identity-kit.ts`** - Decentralized identity management
+- **`mcp-client.ts`** - Model Context Protocol client
+- **`authorized-fetch.ts`** - Authenticated HTTP requests
+
+**Key Data Entities**:
+- **ChatSession** - Chat conversations with message history
+- **Cap** - AI assistant configuration (prompt, model, MCP servers)
+- **Settings** - User preferences and app configuration
+
+### UI Components
+
+**Shared Components** (in `src/shared/components/ui/`):
+- Based on Radix UI primitives with Tailwind styling
+- Do not modify files in `ui/` folder - they are generated components
+- For custom components, create in feature-specific `components/` folders
+
+### Development Patterns
+
+**State Management**:
+- Use Zustand stores with persistence middleware
+- Store files typically export both store and selectors
+- User data automatically scoped by DID
+
+**Data Fetching**:
+- Use SWR for server state management
+- Custom hooks in feature `hooks/` folders
+- Services handle business logic and API calls
+
+**Routing**:
+- React Router v7 with nested layouts
+- Route components in `src/pages/`
+- Layout components in `src/layout/`
+
+**Styling**:
+- Tailwind CSS with custom design system
+- Radix UI for accessible primitives
+- Theme support via next-themes
+
+### Important Files
+
+- **`src/main.tsx`** - Application entry point
+- **`src/router.tsx`** - Route configuration
+- **`src/layout/main-layout.tsx`** - Main application layout
+- **`biome.json`** - Biome configuration for linting/formatting
+- **`tailwind.config.ts`** - Tailwind CSS configuration
+
+### Development Notes
+
+- The app supports both light and dark themes
+- All user interfaces are internationalized (i18n support)
+- Web3 functionality uses Reown AppKit for wallet connections
+- AI model switching is supported via the model selector
+- MCP servers can be dynamically added/removed per CAP
\ No newline at end of file
diff --git a/package.json b/package.json
index 9139c6d6..9bd4aff3 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,6 @@
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
- "prebuild": "tsx src/caps/build.ts",
"preview": "vite preview",
"lint": "biome lint src/",
"lint:fix": "biome lint --apply src/",
@@ -30,8 +29,9 @@
"@hookform/resolvers": "^5.1.1",
"@lobehub/icons": "^2.9.0",
"@modelcontextprotocol/sdk": "^1.13.2",
- "@nuwa-ai/identity-kit": "^0.2.2",
- "@nuwa-ai/identity-kit-web": "^0.2.2",
+ "@nuwa-ai/cap-kit": "^0.3.5",
+ "@nuwa-ai/identity-kit": "^0.3.4",
+ "@nuwa-ai/identity-kit-web": "^0.3.5",
"@openrouter/ai-sdk-provider": "^0.7.2",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14",
@@ -59,11 +59,16 @@
"@radix-ui/react-toggle": "^1.1.9",
"@radix-ui/react-toggle-group": "^1.1.10",
"@radix-ui/react-tooltip": "^1.2.7",
+ "@reown/appkit": "^1.7.17",
+ "@reown/appkit-adapter-wagmi": "^1.7.17",
+ "@reown/appkit-pay": "^1.7.17",
"@rjsf/core": "^5.24.12",
"@rjsf/validator-ajv8": "^5.24.12",
"@tailwindcss/typography": "^0.5.16",
+ "@tanstack/react-query": "^5.84.1",
"@uiw/react-markdown-preview": "^5.1.4",
"@vercel/functions": "^2.2.2",
+ "@web3icons/react": "^4.0.19",
"@xenova/transformers": "^2.17.2",
"ai": "^4.3.16",
"class-variance-authority": "^0.7.1",
@@ -113,8 +118,10 @@
"url-metadata": "^5.2.1",
"usehooks-ts": "^3.1.1",
"vaul": "^1.1.2",
+ "viem": "^2.33.2",
+ "wagmi": "^2.16.1",
"zod": "^3.25.67",
- "zustand": "^5.0.5"
+ "zustand": "^5.0.7"
},
"devDependencies": {
"@biomejs/biome": "^2.0.5",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2670470a..98aad59d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -50,12 +50,15 @@ importers:
'@modelcontextprotocol/sdk':
specifier: ^1.13.2
version: 1.13.2
+ '@nuwa-ai/cap-kit':
+ specifier: ^0.3.5
+ version: 0.3.5(react@19.1.0)(typescript@5.8.3)(zod@3.25.67)
'@nuwa-ai/identity-kit':
- specifier: ^0.2.2
- version: 0.2.2(typescript@5.8.3)
+ specifier: ^0.3.4
+ version: 0.3.4(typescript@5.8.3)
'@nuwa-ai/identity-kit-web':
- specifier: ^0.2.2
- version: 0.2.2(react@19.1.0)(typescript@5.8.3)
+ specifier: ^0.3.5
+ version: 0.3.5(react@19.1.0)(typescript@5.8.3)
'@openrouter/ai-sdk-provider':
specifier: ^0.7.2
version: 0.7.2(ai@4.3.16(react@19.1.0)(zod@3.25.67))(zod@3.25.67)
@@ -137,6 +140,15 @@ importers:
'@radix-ui/react-tooltip':
specifier: ^1.2.7
version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@reown/appkit':
+ specifier: ^1.7.17
+ version: 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(lit@3.3.0)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-adapter-wagmi':
+ specifier: ^1.7.17
+ version: 1.7.17(@types/react@19.1.8)(@wagmi/core@2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(immer@10.1.1)(lit@3.3.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(wagmi@2.16.1(@tanstack/query-core@5.83.1)(@tanstack/react-query@5.84.1(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67))(zod@3.25.67)
+ '@reown/appkit-pay':
+ specifier: ^1.7.17
+ version: 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
'@rjsf/core':
specifier: ^5.24.12
version: 5.24.12(@rjsf/utils@5.24.12(react@19.1.0))(react@19.1.0)
@@ -146,12 +158,18 @@ importers:
'@tailwindcss/typography':
specifier: ^0.5.16
version: 0.5.16(tailwindcss@3.4.17)
+ '@tanstack/react-query':
+ specifier: ^5.84.1
+ version: 5.84.1(react@19.1.0)
'@uiw/react-markdown-preview':
specifier: ^5.1.4
version: 5.1.4(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@vercel/functions':
specifier: ^2.2.2
version: 2.2.2
+ '@web3icons/react':
+ specifier: ^4.0.19
+ version: 4.0.19(react@19.1.0)(typescript@5.8.3)
'@xenova/transformers':
specifier: ^2.17.2
version: 2.17.2
@@ -299,12 +317,18 @@ importers:
vaul:
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ viem:
+ specifier: ^2.33.2
+ version: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ wagmi:
+ specifier: ^2.16.1
+ version: 2.16.1(@tanstack/query-core@5.83.1)(@tanstack/react-query@5.84.1(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67)
zod:
specifier: ^3.25.67
version: 3.25.67
zustand:
- specifier: ^5.0.5
- version: 5.0.5(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0))
+ specifier: ^5.0.7
+ version: 5.0.7(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0))
devDependencies:
'@biomejs/biome':
specifier: ^2.0.5
@@ -357,6 +381,9 @@ importers:
packages:
+ '@adraffy/ens-normalize@1.11.0':
+ resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==}
+
'@ai-sdk/openai@1.3.22':
resolution: {integrity: sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==}
engines: {node: '>=18'}
@@ -412,6 +439,12 @@ packages:
react: '>=16.0.0'
react-dom: '>=16.0.0'
+ '@ant-design/cssinjs@1.24.0':
+ resolution: {integrity: sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==}
+ peerDependencies:
+ react: '>=16.0.0'
+ react-dom: '>=16.0.0'
+
'@ant-design/fast-color@2.0.6':
resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==}
engines: {node: '>=8.x'}
@@ -492,8 +525,8 @@ packages:
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
engines: {node: '>=6.9.0'}
- '@babel/helpers@7.27.6':
- resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==}
+ '@babel/helpers@7.28.2':
+ resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.28.0':
@@ -523,9 +556,16 @@ packages:
resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==}
engines: {node: '>=6.9.0'}
+ '@babel/types@7.28.2':
+ resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
+ engines: {node: '>=6.9.0'}
+
'@babycommando/entity-db@1.0.11':
resolution: {integrity: sha512-7516YByMjoR6x/b6/fJx+w+GfNTE5T9X68Be+OtWYJPsRn7ELPWOs+4L8GbljW01WITr5anxuRYbFLWpdKLs1A==}
+ '@base-org/account@1.1.1':
+ resolution: {integrity: sha512-IfVJPrDPhHfqXRDb89472hXkpvJuQQR7FDI9isLPHEqSYt/45whIoBxSPgZ0ssTt379VhQo4+87PWI1DoLSfAQ==}
+
'@biomejs/biome@2.0.5':
resolution: {integrity: sha512-MztFGhE6cVjf3QmomWu83GpTFyWY8KIcskgRf2AqVEMSH4qI4rNdBLdpAQ11TNK9pUfLGz3IIOC1ZYwgBePtig==}
engines: {node: '>=14.21.3'}
@@ -579,6 +619,9 @@ packages:
cpu: [x64]
os: [win32]
+ '@bitcoinerlab/secp256k1@1.2.0':
+ resolution: {integrity: sha512-jeujZSzb3JOZfmJYI0ph1PVpCRV5oaexCgy+RvCXV8XlY+XFB/2n3WOcvBsKLsOw78KYgnQrQWb2HrKE4be88Q==}
+
'@braintree/sanitize-url@7.1.1':
resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==}
@@ -624,6 +667,12 @@ packages:
'@codemirror/view@6.37.2':
resolution: {integrity: sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==}
+ '@coinbase/wallet-sdk@3.9.3':
+ resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==}
+
+ '@coinbase/wallet-sdk@4.3.6':
+ resolution: {integrity: sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==}
+
'@date-fns/tz@1.2.0':
resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==}
@@ -655,6 +704,12 @@ packages:
peerDependencies:
react: '>=16.8.0'
+ '@ecies/ciphers@0.2.4':
+ resolution: {integrity: sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==}
+ engines: {bun: '>=1', deno: '>=2', node: '>=16'}
+ peerDependencies:
+ '@noble/ciphers': ^1.0.0
+
'@emoji-mart/data@1.2.1':
resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==}
@@ -886,8 +941,8 @@ packages:
resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/core@0.15.0':
- resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==}
+ '@eslint/core@0.15.1':
+ resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.3.1':
@@ -902,10 +957,26 @@ packages:
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/plugin-kit@0.3.2':
- resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==}
+ '@eslint/plugin-kit@0.3.4':
+ resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@ethereumjs/common@3.2.0':
+ resolution: {integrity: sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==}
+
+ '@ethereumjs/rlp@4.0.1':
+ resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ '@ethereumjs/tx@4.2.0':
+ resolution: {integrity: sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==}
+ engines: {node: '>=14'}
+
+ '@ethereumjs/util@8.1.0':
+ resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==}
+ engines: {node: '>=14'}
+
'@floating-ui/core@0.7.3':
resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==}
@@ -1044,6 +1115,11 @@ packages:
'@lit-labs/ssr-dom-shim@1.3.0':
resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==}
+ '@lit/react@1.0.8':
+ resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==}
+ peerDependencies:
+ '@types/react': 17 || 18 || 19
+
'@lit/reactive-element@2.1.0':
resolution: {integrity: sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==}
@@ -1087,25 +1163,133 @@ packages:
'@mermaid-js/parser@0.6.0':
resolution: {integrity: sha512-7DNESgpyZ5WG1SIkrYafVBhWmImtmQuoxOO1lawI3gQYWxBX3v1FW3IyuuRfKJAO06XrZR71W0Kif5VEGGd4VA==}
+ '@metamask/eth-json-rpc-provider@1.0.1':
+ resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==}
+ engines: {node: '>=14.0.0'}
+
+ '@metamask/json-rpc-engine@7.3.3':
+ resolution: {integrity: sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==}
+ engines: {node: '>=16.0.0'}
+
+ '@metamask/json-rpc-engine@8.0.2':
+ resolution: {integrity: sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA==}
+ engines: {node: '>=16.0.0'}
+
+ '@metamask/json-rpc-middleware-stream@7.0.2':
+ resolution: {integrity: sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg==}
+ engines: {node: '>=16.0.0'}
+
+ '@metamask/object-multiplex@2.1.0':
+ resolution: {integrity: sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==}
+ engines: {node: ^16.20 || ^18.16 || >=20}
+
+ '@metamask/onboarding@1.0.1':
+ resolution: {integrity: sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==}
+
+ '@metamask/providers@16.1.0':
+ resolution: {integrity: sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g==}
+ engines: {node: ^18.18 || >=20}
+
+ '@metamask/rpc-errors@6.4.0':
+ resolution: {integrity: sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==}
+ engines: {node: '>=16.0.0'}
+
+ '@metamask/safe-event-emitter@2.0.0':
+ resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==}
+
+ '@metamask/safe-event-emitter@3.1.2':
+ resolution: {integrity: sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==}
+ engines: {node: '>=12.0.0'}
+
+ '@metamask/sdk-communication-layer@0.32.0':
+ resolution: {integrity: sha512-dmj/KFjMi1fsdZGIOtbhxdg3amxhKL/A5BqSU4uh/SyDKPub/OT+x5pX8bGjpTL1WPWY/Q0OIlvFyX3VWnT06Q==}
+ peerDependencies:
+ cross-fetch: ^4.0.0
+ eciesjs: '*'
+ eventemitter2: ^6.4.9
+ readable-stream: ^3.6.2
+ socket.io-client: ^4.5.1
+
+ '@metamask/sdk-install-modal-web@0.32.0':
+ resolution: {integrity: sha512-TFoktj0JgfWnQaL3yFkApqNwcaqJ+dw4xcnrJueMP3aXkSNev2Ido+WVNOg4IIMxnmOrfAC9t0UJ0u/dC9MjOQ==}
+
+ '@metamask/sdk@0.32.0':
+ resolution: {integrity: sha512-WmGAlP1oBuD9hk4CsdlG1WJFuPtYJY+dnTHJMeCyohTWD2GgkcLMUUuvu9lO1/NVzuOoSi1OrnjbuY1O/1NZ1g==}
+
+ '@metamask/superstruct@3.2.1':
+ resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==}
+ engines: {node: '>=16.0.0'}
+
+ '@metamask/utils@5.0.2':
+ resolution: {integrity: sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==}
+ engines: {node: '>=14.0.0'}
+
+ '@metamask/utils@8.5.0':
+ resolution: {integrity: sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==}
+ engines: {node: '>=16.0.0'}
+
+ '@metamask/utils@9.3.0':
+ resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==}
+ engines: {node: '>=16.0.0'}
+
'@modelcontextprotocol/sdk@1.13.2':
resolution: {integrity: sha512-Vx7qOcmoKkR3qhaQ9qf3GxiVKCEu+zfJddHv6x3dY/9P6+uIwJnmuAur5aB+4FDXf41rRrDnOEGkviX5oYZ67w==}
engines: {node: '>=18'}
+ '@msgpack/msgpack@3.1.2':
+ resolution: {integrity: sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==}
+ engines: {node: '>= 18'}
+
'@mysten/bcs@1.0.4':
resolution: {integrity: sha512-6JoQi59GN/dVEBCNq8Rj4uOR0niDrJqDx/2gNQWXANwJakHIGH0AMniHrXP41B2dF+mZ3HVmh9Hi3otiEVQTrQ==}
+ '@noble/ciphers@1.2.1':
+ resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/ciphers@1.3.0':
+ resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/curves@1.4.2':
+ resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==}
+
'@noble/curves@1.6.0':
resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==}
engines: {node: ^14.21.3 || >=16}
+ '@noble/curves@1.8.0':
+ resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/curves@1.8.1':
+ resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/curves@1.9.1':
+ resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==}
+ engines: {node: ^14.21.3 || >=16}
+
'@noble/curves@1.9.2':
resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==}
engines: {node: ^14.21.3 || >=16}
+ '@noble/hashes@1.4.0':
+ resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
+ engines: {node: '>= 16'}
+
'@noble/hashes@1.5.0':
resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==}
engines: {node: ^14.21.3 || >=16}
+ '@noble/hashes@1.7.0':
+ resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@noble/hashes@1.7.1':
+ resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==}
+ engines: {node: ^14.21.3 || >=16}
+
'@noble/hashes@1.8.0':
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
@@ -1122,16 +1306,19 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
- '@nuwa-ai/identity-kit-web@0.2.2':
- resolution: {integrity: sha512-huaG0KgrzDwAI3ceW5bubici/mt/wm6Q7g24U7G/QWZzNURIUMpBSSIl3WUdhdeTQXSsso2PSSGlv0iuPByjhw==}
+ '@nuwa-ai/cap-kit@0.3.5':
+ resolution: {integrity: sha512-2pomn+FWRFmMbEKBemj9WrzVMan/yklM1TLtuCiwkozCj5F4oFTWc4mwVtpDoiUAH/UVB7ngKuXaU9LhT8Mgig==}
+
+ '@nuwa-ai/identity-kit-web@0.3.5':
+ resolution: {integrity: sha512-egjSXMiGfDxzPW8l496PRGF2b0MGN+KbTY8m1oRmcnffSWei7A3gvrzHBvKyeHTB93mPbzYck7ca1/09Wclp4A==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
peerDependenciesMeta:
react:
optional: true
- '@nuwa-ai/identity-kit@0.2.2':
- resolution: {integrity: sha512-i1unD0Y8E5qUDbzcVBSwI44YIdSlarQNrs5muB0xaQzK1V+8Ze87p667zPQ/1CVjOA3QmETQGKA0Az40tgmXfg==}
+ '@nuwa-ai/identity-kit@0.3.4':
+ resolution: {integrity: sha512-Lty7wuKmEuriLmGd15EM1IRPvDTuHvm/Px/BXb3i1O/l2sB6ZlYxybL++KU694I0tY2TZznuc3tNJUJfkYyIfA==}
'@openrouter/ai-sdk-provider@0.7.2':
resolution: {integrity: sha512-Fry2mV7uGGJRmP9JntTZRc8ElESIk7AJNTacLbF6Syoeb5k8d7HPGkcK9rTXDlqBb8HgU1hOKtz23HojesTmnw==}
@@ -1144,6 +1331,10 @@ packages:
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
+ '@paulmillr/qr@0.2.1':
+ resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==}
+ deprecated: 'The package is now available as "qr": npm install qr'
+
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -1952,6 +2143,13 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
+ '@rc-component/trigger@2.3.0':
+ resolution: {integrity: sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==}
+ engines: {node: '>=8.x'}
+ peerDependencies:
+ react: '>=16.9.0'
+ react-dom: '>=16.9.0'
+
'@reduxjs/toolkit@2.8.2':
resolution: {integrity: sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==}
peerDependencies:
@@ -1963,6 +2161,76 @@ packages:
react-redux:
optional: true
+ '@reown/appkit-adapter-wagmi@1.7.17':
+ resolution: {integrity: sha512-QGAomIvnHKA5mISDISKSJESdWGd4LE6c3cJZ46y29Ta+irmALd90nDQQ0nV9FThNXM8te6qNF4coMNwRn4WuYg==}
+ peerDependencies:
+ '@wagmi/core': '>=2.16.7'
+ viem: '>=2.32.0'
+ wagmi: '>=2.15.7'
+
+ '@reown/appkit-common@1.7.17':
+ resolution: {integrity: sha512-zfrlNosQ5XBGC7OBG56+lur0nJWCdRKoWVlUnr0dCVVfBmHIgdhFkRNzDqrX/zGqg4OoWDQLO7qaGiijRskfBQ==}
+
+ '@reown/appkit-common@1.7.8':
+ resolution: {integrity: sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==}
+
+ '@reown/appkit-controllers@1.7.17':
+ resolution: {integrity: sha512-rYgXf3nAzxgu1s10rSfibpAqnm/Y3wyY47v6BpN98Y57NArWqxYXhBtdRQL1ZKpSTV9OmrzwMxPNKePOmFgxZQ==}
+
+ '@reown/appkit-controllers@1.7.8':
+ resolution: {integrity: sha512-IdXlJlivrlj6m63VsGLsjtPHHsTWvKGVzWIP1fXZHVqmK+rZCBDjCi9j267Rb9/nYRGHWBtlFQhO8dK35WfeDA==}
+
+ '@reown/appkit-pay@1.7.17':
+ resolution: {integrity: sha512-RukQ5oZ+zGzWy9gu4butVcscZ9GB9/h6zmQFXDo9qkAbOicwZKaLR5XMKrjLQIYisu+ODV/ff6NuxnUYs+/r9Q==}
+
+ '@reown/appkit-pay@1.7.8':
+ resolution: {integrity: sha512-OSGQ+QJkXx0FEEjlpQqIhT8zGJKOoHzVnyy/0QFrl3WrQTjCzg0L6+i91Ad5Iy1zb6V5JjqtfIFpRVRWN4M3pw==}
+
+ '@reown/appkit-polyfills@1.7.17':
+ resolution: {integrity: sha512-vWRIYS+wc2ByWKn76KMV7zxqTvQ+512KwXAKQcRulu13AdKvnBbr0eYx+ctvSKL+kZoAp9zj4R3RulX3eXnJ8Q==}
+
+ '@reown/appkit-polyfills@1.7.8':
+ resolution: {integrity: sha512-W/kq786dcHHAuJ3IV2prRLEgD/2iOey4ueMHf1sIFjhhCGMynMkhsOhQMUH0tzodPqUgAC494z4bpIDYjwWXaA==}
+
+ '@reown/appkit-scaffold-ui@1.7.17':
+ resolution: {integrity: sha512-7nk8DEHQf9/7Ij8Eo85Uj1D/3M9Ybq/LjXyePyaGusZ9E8gf4u/UjKpQK7cTfMNsNl4nrB2mBI9Tk/rwNECdCg==}
+
+ '@reown/appkit-scaffold-ui@1.7.8':
+ resolution: {integrity: sha512-RCeHhAwOrIgcvHwYlNWMcIDibdI91waaoEYBGw71inE0kDB8uZbE7tE6DAXJmDkvl0qPh+DqlC4QbJLF1FVYdQ==}
+
+ '@reown/appkit-siwx@1.7.17':
+ resolution: {integrity: sha512-frTTDnj5111+ZNNyHmEWeXiX0IWFlRhP240kmxKTamLElc2PdLUfQq/1yX8Y3bUBHryISjcQYzEtWSEI2oRYKA==}
+ peerDependencies:
+ lit: 3.3.0
+
+ '@reown/appkit-ui@1.7.17':
+ resolution: {integrity: sha512-7lscJjtFZIfdcUv5zAsmgiFG2dMziQE0IfqY3U/H5qhnGW8v4ITcTi1gNS3A4lQrNDbcA083LecfVdyKnTdi1A==}
+
+ '@reown/appkit-ui@1.7.8':
+ resolution: {integrity: sha512-1hjCKjf6FLMFzrulhl0Y9Vb9Fu4royE+SXCPSWh4VhZhWqlzUFc7kutnZKx8XZFVQH4pbBvY62SpRC93gqoHow==}
+
+ '@reown/appkit-utils@1.7.17':
+ resolution: {integrity: sha512-QWzHTmSDFy90Bp5pUUQASzcjnJXPiEvasJV68j3PZifenTPDCfFW+VsiHduWNodTHAA/rZ12O3uBQE+stM3xmQ==}
+ peerDependencies:
+ valtio: 2.1.5
+
+ '@reown/appkit-utils@1.7.8':
+ resolution: {integrity: sha512-8X7UvmE8GiaoitCwNoB86pttHgQtzy4ryHZM9kQpvjQ0ULpiER44t1qpVLXNM4X35O0v18W0Dk60DnYRMH2WRw==}
+ peerDependencies:
+ valtio: 1.13.2
+
+ '@reown/appkit-wallet@1.7.17':
+ resolution: {integrity: sha512-tgIqHZZJISGCir0reQ/pXcIKXuP7JNqSuEDunfi5whNJi6z27h3g468RGk1Zo+MC//DRnQb01xMrv+iWRr8mCQ==}
+
+ '@reown/appkit-wallet@1.7.8':
+ resolution: {integrity: sha512-kspz32EwHIOT/eg/ZQbFPxgXq0B/olDOj3YMu7gvLEFz4xyOFd/wgzxxAXkp5LbG4Cp++s/elh79rVNmVFdB9A==}
+
+ '@reown/appkit@1.7.17':
+ resolution: {integrity: sha512-gME4Ery7HGTNEGzLckWP7qfD2ec/1UEuUkcGskGeisUnGcAsPH9z2deFFX1szialsgzTNU4/H5ZGdWqZQA8p2w==}
+
+ '@reown/appkit@1.7.8':
+ resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==}
+
'@rjsf/core@5.24.12':
resolution: {integrity: sha512-OWVdC501n3Io0hplgpnkzArpcUSiImMgLQhk6/EI8wu2xbvk5fTiM7YAVlAObpAD3z3LRrAwhjnmh9L4k/FWmQ==}
engines: {node: '>=14'}
@@ -2089,15 +2357,37 @@ packages:
resolution: {integrity: sha512-dbej3xzpb2WK9CJh90cT51hTSCee/GHRFIHbWhlCGse1zaiJh4KDuP+jpJJH5PYrrOQsCZNHU1WJ0hQTH8UNIA==}
engines: {node: '>=18.0.0'}
+ '@safe-global/safe-apps-provider@0.18.6':
+ resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==}
+
+ '@safe-global/safe-apps-sdk@9.1.0':
+ resolution: {integrity: sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==}
+
+ '@safe-global/safe-gateway-typescript-sdk@3.23.1':
+ resolution: {integrity: sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==}
+ engines: {node: '>=16'}
+
'@scure/base@1.1.9':
resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==}
'@scure/base@1.2.6':
resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==}
+ '@scure/bip32@1.4.0':
+ resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==}
+
+ '@scure/bip32@1.6.2':
+ resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==}
+
'@scure/bip32@1.7.0':
resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==}
+ '@scure/bip39@1.3.0':
+ resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==}
+
+ '@scure/bip39@1.5.4':
+ resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==}
+
'@scure/bip39@1.6.0':
resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==}
@@ -2125,6 +2415,9 @@ packages:
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+ '@socket.io/component-emitter@3.1.2':
+ resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
+
'@splinetool/runtime@0.9.526':
resolution: {integrity: sha512-qznHbXA5aKwDbCgESAothCNm1IeEZcmNWG145p5aXj4w5uoqR1TZ9qkTHTKLTsUbHeitCwdhzmRqan1kxboLgQ==}
@@ -2222,6 +2515,14 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
+ '@tanstack/query-core@5.83.1':
+ resolution: {integrity: sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==}
+
+ '@tanstack/react-query@5.84.1':
+ resolution: {integrity: sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==}
+ peerDependencies:
+ react: ^18 || ^19
+
'@types/d3-array@3.2.1':
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
@@ -2491,9 +2792,164 @@ packages:
peerDependencies:
vite: ^4 || ^5 || ^6 || ^7.0.0-beta.0
+ '@wagmi/connectors@5.9.1':
+ resolution: {integrity: sha512-o50e6reSYkVi2d72WWwbKSZ7xgLAeQ1Ja64tTWq3UhU1XtJPvQXWieCInIGInOajAAsZsYCPKYrPj6WoSl0Hqw==}
+ peerDependencies:
+ '@wagmi/core': 2.18.1
+ typescript: '>=5.0.4'
+ viem: 2.x
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@wagmi/core@2.18.1':
+ resolution: {integrity: sha512-mU+qXeeY2/0lq8bf4uFm5RtMrc8FgOToqzMVMf6MzNdNbKxpNlmlbuTyRbyd9cxn4UnYa6+S6Bmx1x42FV7w3g==}
+ peerDependencies:
+ '@tanstack/query-core': '>=5.0.0'
+ typescript: '>=5.0.4'
+ viem: 2.x
+ peerDependenciesMeta:
+ '@tanstack/query-core':
+ optional: true
+ typescript:
+ optional: true
+
+ '@wallet-standard/base@1.1.0':
+ resolution: {integrity: sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==}
+ engines: {node: '>=16'}
+
+ '@wallet-standard/wallet@1.1.0':
+ resolution: {integrity: sha512-Gt8TnSlDZpAl+RWOOAB/kuvC7RpcdWAlFbHNoi4gsXsfaWa1QCT6LBcfIYTPdOZC9OVZUDwqGuGAcqZejDmHjg==}
+ engines: {node: '>=16'}
+
+ '@walletconnect/core@2.21.0':
+ resolution: {integrity: sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==}
+ engines: {node: '>=18'}
+
+ '@walletconnect/core@2.21.1':
+ resolution: {integrity: sha512-Tp4MHJYcdWD846PH//2r+Mu4wz1/ZU/fr9av1UWFiaYQ2t2TPLDiZxjLw54AAEpMqlEHemwCgiRiAmjR1NDdTQ==}
+ engines: {node: '>=18'}
+
+ '@walletconnect/core@2.21.5':
+ resolution: {integrity: sha512-CxGbio1TdCkou/TYn8X6Ih1mUX3UtFTk+t618/cIrT3VX5IjQW09n9I/pVafr7bQbBtm9/ATr7ugUEMrLu5snA==}
+ engines: {node: '>=18'}
+
+ '@walletconnect/environment@1.0.1':
+ resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==}
+
+ '@walletconnect/ethereum-provider@2.21.1':
+ resolution: {integrity: sha512-SSlIG6QEVxClgl1s0LMk4xr2wg4eT3Zn/Hb81IocyqNSGfXpjtawWxKxiC5/9Z95f1INyBD6MctJbL/R1oBwIw==}
+
+ '@walletconnect/events@1.0.1':
+ resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==}
+
+ '@walletconnect/heartbeat@1.2.2':
+ resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==}
+
+ '@walletconnect/jsonrpc-http-connection@1.0.8':
+ resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==}
+
+ '@walletconnect/jsonrpc-provider@1.0.14':
+ resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==}
+
+ '@walletconnect/jsonrpc-types@1.0.4':
+ resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==}
+
+ '@walletconnect/jsonrpc-utils@1.0.8':
+ resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==}
+
+ '@walletconnect/jsonrpc-ws-connection@1.0.16':
+ resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==}
+
+ '@walletconnect/keyvaluestorage@1.1.1':
+ resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==}
+ peerDependencies:
+ '@react-native-async-storage/async-storage': 1.x
+ peerDependenciesMeta:
+ '@react-native-async-storage/async-storage':
+ optional: true
+
+ '@walletconnect/logger@2.1.2':
+ resolution: {integrity: sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==}
+
+ '@walletconnect/relay-api@1.0.11':
+ resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==}
+
+ '@walletconnect/relay-auth@1.1.0':
+ resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==}
+
+ '@walletconnect/safe-json@1.0.2':
+ resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==}
+
+ '@walletconnect/sign-client@2.21.0':
+ resolution: {integrity: sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==}
+
+ '@walletconnect/sign-client@2.21.1':
+ resolution: {integrity: sha512-QaXzmPsMnKGV6tc4UcdnQVNOz4zyXgarvdIQibJ4L3EmLat73r5ZVl4c0cCOcoaV7rgM9Wbphgu5E/7jNcd3Zg==}
+
+ '@walletconnect/sign-client@2.21.5':
+ resolution: {integrity: sha512-IAs/IqmE1HVL9EsvqkNRU4NeAYe//h9NwqKi7ToKYZv4jhcC3BBemUD1r8iQJSTHMhO41EKn1G9/DiBln3ZiwQ==}
+
+ '@walletconnect/time@1.0.2':
+ resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==}
+
+ '@walletconnect/types@2.21.0':
+ resolution: {integrity: sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==}
+
+ '@walletconnect/types@2.21.1':
+ resolution: {integrity: sha512-UeefNadqP6IyfwWC1Yi7ux+ljbP2R66PLfDrDm8izmvlPmYlqRerJWJvYO4t0Vvr9wrG4Ko7E0c4M7FaPKT/sQ==}
+
+ '@walletconnect/types@2.21.5':
+ resolution: {integrity: sha512-kpTXbenKeMdaz6mgMN/jKaHHbu6mdY3kyyrddzE/mthOd2KLACVrZr7hrTf+Fg2coPVen5d1KKyQjyECEdzOCw==}
+
+ '@walletconnect/universal-provider@2.21.0':
+ resolution: {integrity: sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==}
+
+ '@walletconnect/universal-provider@2.21.1':
+ resolution: {integrity: sha512-Wjx9G8gUHVMnYfxtasC9poGm8QMiPCpXpbbLFT+iPoQskDDly8BwueWnqKs4Mx2SdIAWAwuXeZ5ojk5qQOxJJg==}
+
+ '@walletconnect/universal-provider@2.21.5':
+ resolution: {integrity: sha512-SMXGGXyj78c8Ru2f665ZFZU24phn0yZyCP5Ej7goxVQxABwqWKM/odj3j/IxZv+hxA8yU13yxaubgVefnereqw==}
+
+ '@walletconnect/utils@2.21.0':
+ resolution: {integrity: sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==}
+
+ '@walletconnect/utils@2.21.1':
+ resolution: {integrity: sha512-VPZvTcrNQCkbGOjFRbC24mm/pzbRMUq2DSQoiHlhh0X1U7ZhuIrzVtAoKsrzu6rqjz0EEtGxCr3K1TGRqDG4NA==}
+
+ '@walletconnect/utils@2.21.5':
+ resolution: {integrity: sha512-RSPSxPvGMuvfGhd5au1cf9cmHB/KVVLFotJR9ltisjFABGtH2215U5oaVp+a7W18QX37aemejRkvacqOELVySA==}
+
+ '@walletconnect/window-getters@1.0.1':
+ resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==}
+
+ '@walletconnect/window-metadata@1.0.1':
+ resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==}
+
+ '@web3icons/common@0.11.15':
+ resolution: {integrity: sha512-ahTk6hf3tSXDplWwaM2TKqemKWhSeb1Wajg9t5rJ4tR29gOUx7s3yxXEkMgRwow0svg9ZHtrJOoVuttgl9KNBw==}
+ peerDependencies:
+ typescript: ^5.0.0
+
+ '@web3icons/react@4.0.19':
+ resolution: {integrity: sha512-+FqWm9+1SqIuf0H7PCZl5l0bpZWUgAW8ORIhjaGbOiD0U/gwH9L3DWbVHISq3VxIgB24H4tm1mygHBm0KF8vGw==}
+ peerDependencies:
+ react: ^18.2.0
+
'@xenova/transformers@2.17.2':
resolution: {integrity: sha512-lZmHqzrVIkSvZdKZEx7IYY51TK0WDrC8eR0c5IMnBsO8di8are1zzw8BlLhyO2TklZKLN5UffNGs1IJwT6oOqQ==}
+ abitype@1.0.8:
+ resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ zod: ^3 >=3.22.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ zod:
+ optional: true
+
accepts@2.0.0:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
@@ -2592,6 +3048,13 @@ packages:
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
hasBin: true
+ async-mutex@0.2.6:
+ resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==}
+
+ atomic-sleep@1.0.0:
+ resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
+ engines: {node: '>=8.0.0'}
+
attr-accept@2.2.5:
resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==}
engines: {node: '>=4'}
@@ -2603,6 +3066,10 @@ packages:
peerDependencies:
postcss: ^8.1.0
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
b4a@1.6.7:
resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==}
@@ -2661,6 +3128,12 @@ packages:
bare-events:
optional: true
+ base-x@3.0.11:
+ resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==}
+
+ base-x@4.0.1:
+ resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==}
+
base-x@5.0.1:
resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==}
@@ -2670,16 +3143,52 @@ packages:
bcp-47-match@2.0.3:
resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==}
+ bech32@1.1.4:
+ resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==}
+
bech32@2.0.0:
resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==}
+ big.js@6.2.2:
+ resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==}
+
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
+ bindings@1.5.0:
+ resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+
+ bip174@2.1.1:
+ resolution: {integrity: sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==}
+ engines: {node: '>=8.0.0'}
+
+ bip322-js@2.0.0:
+ resolution: {integrity: sha512-wyewxyCLl+wudZWiyvA46SaNQL41dVDJ+sx4HvD6zRXScHzAycwuKEMmbvr2qN+P/IIYArF4XVqlyZVnjutELQ==}
+
+ bip66@1.1.5:
+ resolution: {integrity: sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==}
+
+ bitcoinjs-lib@6.1.7:
+ resolution: {integrity: sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg==}
+ engines: {node: '>=8.0.0'}
+
+ bitcoinjs-message@2.2.0:
+ resolution: {integrity: sha512-103Wy3xg8Y9o+pdhGP4M3/mtQQuUWs6sPuOp1mYphSUoSMHjHTlkj32K4zxU8qMH0Ckv23emfkGlFWtoWZ7YFA==}
+ engines: {node: '>=0.10'}
+
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+ blakejs@1.2.1:
+ resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==}
+
+ bn.js@4.12.2:
+ resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==}
+
+ bn.js@5.2.2:
+ resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==}
+
body-parser@2.2.0:
resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
engines: {node: '>=18'}
@@ -2687,6 +3196,9 @@ packages:
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+ bowser@2.11.0:
+ resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
+
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
@@ -2697,23 +3209,52 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
+ brorand@1.1.0:
+ resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==}
+
+ browserify-aes@1.2.0:
+ resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==}
+
browserslist@4.25.0:
resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
+ bs58@4.0.1:
+ resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==}
+
+ bs58@5.0.0:
+ resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==}
+
bs58@6.0.0:
resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==}
+ bs58check@2.1.2:
+ resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==}
+
+ bs58check@3.0.1:
+ resolution: {integrity: sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==}
+
bs58check@4.0.0:
resolution: {integrity: sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g==}
+ buffer-equals@1.0.4:
+ resolution: {integrity: sha512-99MsCq0j5+RhubVEtKQgKaD6EM+UP3xJgIvQqwJ3SOLDUekzxMX1ylXBng+Wa2sh7mGT0W6RUly8ojjr1Tt6nA==}
+ engines: {node: '>=0.10.0'}
+
+ buffer-xor@1.0.3:
+ resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==}
+
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+ bufferutil@4.0.9:
+ resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==}
+ engines: {node: '>=6.14.2'}
+
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
@@ -2722,6 +3263,10 @@ packages:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
+ call-bind@1.0.8:
+ resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
+ engines: {node: '>= 0.4'}
+
call-bound@1.0.4:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
@@ -2734,6 +3279,10 @@ packages:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
+ camelcase@5.3.1:
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+ engines: {node: '>=6'}
+
caniuse-lite@1.0.30001724:
resolution: {integrity: sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==}
@@ -2779,18 +3328,29 @@ packages:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
chroma-js@3.1.2:
resolution: {integrity: sha512-IJnETTalXbsLx1eKEgx19d5L6SRM7cH4vINw/99p/M11HCuXGRWL+6YmCm7FWFGIo6dtWuQoQi1dc5yQ7ESIHg==}
+ cipher-base@1.0.6:
+ resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==}
+ engines: {node: '>= 0.10'}
+
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
classnames@2.5.1:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
+ cliui@6.0.0:
+ resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+
clsx@1.2.1:
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
engines: {node: '>=6'}
@@ -2875,6 +3435,9 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ cookie-es@1.2.2:
+ resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==}
+
cookie-signature@1.2.2:
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
engines: {node: '>=6.6.0'}
@@ -2893,6 +3456,9 @@ packages:
core-js-compat@3.43.0:
resolution: {integrity: sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==}
+ core-util-is@1.0.3:
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
@@ -2907,13 +3473,33 @@ packages:
resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
engines: {node: '>=10'}
+ crc-32@1.2.2:
+ resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+ engines: {node: '>=0.8'}
+ hasBin: true
+
+ create-hash@1.2.0:
+ resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
+
+ create-hmac@1.1.7:
+ resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
+
crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
+ cross-fetch@3.2.0:
+ resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==}
+
+ cross-fetch@4.1.0:
+ resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==}
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ crossws@0.3.5:
+ resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==}
+
css-select@5.2.2:
resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
@@ -3091,12 +3677,25 @@ packages:
date-fns-jalali@4.1.0-0:
resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==}
+ date-fns@2.30.0:
+ resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
+ engines: {node: '>=0.11'}
+
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+ debug@4.3.7:
+ resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
debug@4.4.1:
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
engines: {node: '>=6.0'}
@@ -3106,12 +3705,20 @@ packages:
supports-color:
optional: true
+ decamelize@1.2.0:
+ resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
+ engines: {node: '>=0.10.0'}
+
decimal.js-light@2.5.1:
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
decode-named-character-reference@1.2.0:
resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==}
+ decode-uri-component@0.2.2:
+ resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
+ engines: {node: '>=0.10'}
+
decode-uri-component@0.4.1:
resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==}
engines: {node: '>=14.16'}
@@ -3127,6 +3734,13 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
delaunator@5.0.1:
resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
@@ -3138,6 +3752,17 @@ packages:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
+ derive-valtio@0.1.0:
+ resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==}
+ peerDependencies:
+ valtio: '*'
+
+ destr@2.0.5:
+ resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
+
+ detect-browser@5.3.0:
+ resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==}
+
detect-libc@2.0.4:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'}
@@ -3157,6 +3782,9 @@ packages:
diff-match-patch@1.0.5:
resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
+ dijkstrajs@1.0.3:
+ resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
+
direction@2.0.1:
resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==}
hasBin: true
@@ -3180,19 +3808,37 @@ packages:
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+ drbg.js@1.0.1:
+ resolution: {integrity: sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g==}
+ engines: {node: '>=0.10'}
+
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
+ duplexify@4.1.3:
+ resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==}
+
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+ eciesjs@0.4.15:
+ resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==}
+ engines: {bun: '>=1', deno: '>=2', node: '>=16'}
+
+ ecpair@2.1.0:
+ resolution: {integrity: sha512-cL/mh3MtJutFOvFc27GPZE2pWL3a3k4YvzUWEOvilnfZVlH3Jwgx/7d6tlD7/75tNk8TG2m+7Kgtz0SI1tWcqw==}
+ engines: {node: '>=8.0.0'}
+
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.172:
resolution: {integrity: sha512-fnKW9dGgmBfsebbYognQSv0CGGLFH1a5iV9EDYTBwmAQn+whbzHbLFlC+3XbHc8xaNtpO0etm8LOcRXs1qMRkQ==}
+ elliptic@6.6.1:
+ resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==}
+
embla-carousel-react@8.6.0:
resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==}
peerDependencies:
@@ -3218,6 +3864,9 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+ encode-utf8@1.0.3:
+ resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
+
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
@@ -3228,6 +3877,13 @@ packages:
end-of-stream@1.4.5:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
+ engine.io-client@6.6.3:
+ resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==}
+
+ engine.io-parser@5.2.3:
+ resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==}
+ engines: {node: '>=10.0.0'}
+
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@@ -3251,6 +3907,12 @@ packages:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
+ es-toolkit@1.33.0:
+ resolution: {integrity: sha512-X13Q/ZSc+vsO1q600bvNK4bxgXMkHcf//RxCmYDaRY5DAcT+eoXjY5hoAPGMdRnWQjvyLEcyauG3b6hz76LNqg==}
+
+ es-toolkit@1.39.3:
+ resolution: {integrity: sha512-Qb/TCFCldgOy8lZ5uC7nLGdqJwSabkQiYQShmw4jyiPk1pZzaYWTwaYKYP7EgLccWYgZocMrtItrwh683voaww==}
+
es-toolkit@1.39.4:
resolution: {integrity: sha512-hHqQ0yJERMNrJUyYHnf02qDuIxjRnnJlx1CFdR9Ia6tw6jPA7kXmb+tWzc7trJDHwMsc393hZ/m2XMxYXGAfqQ==}
@@ -3347,9 +4009,33 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
+ eth-block-tracker@7.1.0:
+ resolution: {integrity: sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==}
+ engines: {node: '>=14.0.0'}
+
+ eth-json-rpc-filters@6.0.1:
+ resolution: {integrity: sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig==}
+ engines: {node: '>=14.0.0'}
+
+ eth-query@2.1.2:
+ resolution: {integrity: sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==}
+
+ eth-rpc-errors@4.0.3:
+ resolution: {integrity: sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==}
+
+ ethereum-cryptography@2.2.1:
+ resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==}
+
+ eventemitter2@6.4.9:
+ resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==}
+
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+ events@3.3.0:
+ resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+ engines: {node: '>=0.8.x'}
+
eventsource-parser@3.0.2:
resolution: {integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==}
engines: {node: '>=18.0.0'}
@@ -3358,6 +4044,9 @@ packages:
resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==}
engines: {node: '>=18.0.0'}
+ evp_bytestokey@1.0.3:
+ resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==}
+
expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
@@ -3386,6 +4075,10 @@ packages:
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+ extension-port-stream@3.0.0:
+ resolution: {integrity: sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==}
+ engines: {node: '>=12.0.0'}
+
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -3402,6 +4095,16 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ fast-redact@3.5.0:
+ resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
+ engines: {node: '>=6'}
+
+ fast-safe-stringify@2.1.1:
+ resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+
+ fast-sha256@1.3.0:
+ resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
+
fast-uri@3.0.6:
resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==}
@@ -3424,10 +4127,17 @@ packages:
resolution: {integrity: sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA==}
engines: {node: '>= 10'}
+ file-uri-to-path@1.0.0:
+ resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
+ filter-obj@1.1.0:
+ resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==}
+ engines: {node: '>=0.10.0'}
+
filter-obj@5.1.0:
resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==}
engines: {node: '>=14.16'}
@@ -3439,6 +4149,10 @@ packages:
find-root@1.1.0:
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
@@ -3453,6 +4167,10 @@ packages:
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+ for-each@0.3.5:
+ resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+ engines: {node: '>= 0.4'}
+
for-in@1.0.2:
resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==}
engines: {node: '>=0.10.0'}
@@ -3501,6 +4219,10 @@ packages:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
@@ -3559,6 +4281,9 @@ packages:
guid-typescript@1.0.9:
resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==}
+ h3@1.15.4:
+ resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==}
+
hachure-fill@0.5.2:
resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
@@ -3566,10 +4291,24 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hash-base@3.1.0:
+ resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==}
+ engines: {node: '>=4'}
+
+ hash.js@1.1.7:
+ resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
+
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
@@ -3637,6 +4376,9 @@ packages:
hastscript@9.0.1:
resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
+ hmac-drbg@1.0.1:
+ resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
+
hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
@@ -3657,6 +4399,12 @@ packages:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
+ idb-keyval@6.2.1:
+ resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==}
+
+ idb-keyval@6.2.2:
+ resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==}
+
idb@8.0.3:
resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==}
@@ -3715,12 +4463,19 @@ packages:
resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
engines: {node: '>= 10'}
+ iron-webcrypto@1.2.1:
+ resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
+
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
is-alphanumerical@2.0.1:
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
+ is-arguments@1.2.0:
+ resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
+ engines: {node: '>= 0.4'}
+
is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
@@ -3731,6 +4486,10 @@ packages:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
+ is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
is-core-module@2.16.1:
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
@@ -3754,6 +4513,10 @@ packages:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
+ is-generator-function@1.1.0:
+ resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
+ engines: {node: '>= 0.4'}
+
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
@@ -3776,6 +4539,24 @@ packages:
is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
+ is-regex@1.2.1:
+ resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
+ engines: {node: '>= 0.4'}
+
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
+ is-typed-array@1.1.15:
+ resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+ engines: {node: '>= 0.4'}
+
+ isarray@1.0.0:
+ resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -3783,6 +4564,16 @@ packages:
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
engines: {node: '>=0.10.0'}
+ isows@1.0.6:
+ resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==}
+ peerDependencies:
+ ws: '*'
+
+ isows@1.0.7:
+ resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==}
+ peerDependencies:
+ ws: '*'
+
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
@@ -3812,6 +4603,13 @@ packages:
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+ json-rpc-engine@6.1.0:
+ resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==}
+ engines: {node: '>=10.0.0'}
+
+ json-rpc-random-id@1.0.1:
+ resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==}
+
json-schema-compare@0.2.2:
resolution: {integrity: sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==}
@@ -3852,9 +4650,16 @@ packages:
resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==}
hasBin: true
+ keccak@3.0.4:
+ resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==}
+ engines: {node: '>=10.0.0'}
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ keyvaluestorage-interface@1.0.0:
+ resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==}
+
khroma@2.1.0:
resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
@@ -3904,6 +4709,10 @@ packages:
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
engines: {node: '>=14'}
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
@@ -3983,6 +4792,9 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ md5.js@1.3.5:
+ resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
+
mdast-util-find-and-replace@3.0.2:
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
@@ -4059,6 +4871,9 @@ packages:
mermaid@11.8.0:
resolution: {integrity: sha512-uAZUwnBiqREZcUrFw3G5iQ5Pj3hTYUP95EZc3ec/nGBzHddJZydzYGE09tGZDBS1VoSoDn0symZ85FmypSTo5g==}
+ micro-ftch@0.3.1:
+ resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==}
+
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
@@ -4183,6 +4998,12 @@ packages:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
+ minimalistic-assert@1.0.1:
+ resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
+
+ minimalistic-crypto-utils@1.0.1:
+ resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
+
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -4197,6 +5018,14 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
+ mipd@0.0.7:
+ resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
mixin-deep@1.3.2:
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
engines: {node: '>=0.10.0'}
@@ -4222,6 +5051,9 @@ packages:
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+ nan@2.23.0:
+ resolution: {integrity: sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==}
+
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -4252,9 +5084,18 @@ packages:
resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==}
engines: {node: '>=10'}
+ node-addon-api@2.0.2:
+ resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==}
+
+ node-addon-api@5.1.0:
+ resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
+
node-addon-api@6.1.0:
resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
+ node-fetch-native@1.6.7:
+ resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
+
node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
@@ -4264,6 +5105,13 @@ packages:
encoding:
optional: true
+ node-gyp-build@4.8.4:
+ resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
+ hasBin: true
+
+ node-mock-http@1.0.2:
+ resolution: {integrity: sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==}
+
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
@@ -4281,6 +5129,9 @@ packages:
numeral@2.0.6:
resolution: {integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==}
+ obj-multiplex@1.0.0:
+ resolution: {integrity: sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -4293,10 +5144,16 @@ packages:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'}
+ ofetch@1.4.1:
+ resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
+
on-change@4.0.2:
resolution: {integrity: sha512-cMtCyuJmTx/bg2HCpHo3ZLeF7FZnBOapLqZHr2AlLeJ5Ul0Zu2mUJJz051Fdwu/Et2YW04ZD+TtU+gVy0ACNCA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ on-exit-leak-free@0.2.0:
+ resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==}
+
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
@@ -4330,14 +5187,66 @@ packages:
orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
+ ox@0.6.7:
+ resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ ox@0.6.9:
+ resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ ox@0.7.1:
+ resolution: {integrity: sha512-+k9fY9PRNuAMHRFIUbiK9Nt5seYHHzSQs9Bj+iMETcGtlpS7SmBzcGSVUQO3+nqGLEiNK4598pHNFlVRaZbRsg==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ ox@0.8.1:
+ resolution: {integrity: sha512-e+z5epnzV+Zuz91YYujecW8cF01mzmrUtWotJ0oEPym/G82uccs7q0WDHTYL3eiONbTUEvcZrptAKLgTBD3u2A==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ ox@0.8.6:
+ resolution: {integrity: sha512-eiKcgiVVEGDtEpEdFi1EGoVVI48j6icXHce9nFwCNM7CKG3uoCXKdr4TPhS00Iy1TR2aWSF1ltPD0x/YgqIL9w==}
+ peerDependencies:
+ typescript: '>=5.4.0'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
p-locate@5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
@@ -4418,6 +5327,24 @@ packages:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
+ pify@3.0.0:
+ resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==}
+ engines: {node: '>=4'}
+
+ pify@5.0.0:
+ resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==}
+ engines: {node: '>=10'}
+
+ pino-abstract-transport@0.5.0:
+ resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==}
+
+ pino-std-serializers@4.0.0:
+ resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==}
+
+ pino@7.11.0:
+ resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==}
+ hasBin: true
+
pirates@4.0.7:
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
engines: {node: '>= 6'}
@@ -4435,6 +5362,10 @@ packages:
platform@1.3.6:
resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
+ pngjs@5.0.0:
+ resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
+ engines: {node: '>=10.13.0'}
+
points-on-curve@0.2.0:
resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==}
@@ -4445,6 +5376,14 @@ packages:
resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==}
engines: {node: '>=10'}
+ pony-cause@2.1.11:
+ resolution: {integrity: sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==}
+ engines: {node: '>=12.0.0'}
+
+ possible-typed-array-names@1.1.0:
+ resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
+ engines: {node: '>= 0.4'}
+
postcss-import@15.1.0:
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
engines: {node: '>=14.0.0'}
@@ -4490,6 +5429,12 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
+ preact@10.24.2:
+ resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==}
+
+ preact@10.27.0:
+ resolution: {integrity: sha512-/DTYoB6mwwgPytiqQTh/7SFRL98ZdiD8Sk8zIUVOxtwq4oWcwrcd1uno9fE/zZmUaUrFNYzbH14CPebOz9tZQw==}
+
prebuild-install@7.1.3:
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
engines: {node: '>=10'}
@@ -4499,6 +5444,12 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
+ process-nextick-args@2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+ process-warning@1.0.0:
+ resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==}
+
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@@ -4561,6 +5512,12 @@ packages:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
+ proxy-compare@2.6.0:
+ resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==}
+
+ proxy-compare@3.0.1:
+ resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==}
+
pump@3.0.3:
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
@@ -4572,6 +5529,11 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
+ qrcode@1.5.3:
+ resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
@@ -4579,6 +5541,10 @@ packages:
quansync@0.2.10:
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
+ query-string@7.1.3:
+ resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
+ engines: {node: '>=6'}
+
query-string@9.2.2:
resolution: {integrity: sha512-pDSIZJ9sFuOp6VnD+5IkakSVf+rICAuuU88Hcsr6AKL0QtxSIfVuKiVP2oahFI7tk3CRSexwV+Ya6MOoTxzg9g==}
engines: {node: '>=18'}
@@ -4586,6 +5552,15 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ quick-format-unescaped@4.0.4:
+ resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
+
+ radix3@1.1.2:
+ resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
+
+ randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
@@ -4790,8 +5765,8 @@ packages:
react: '>=16.9.0'
react-dom: '>=16.9.0'
- rc-textarea@1.10.0:
- resolution: {integrity: sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==}
+ rc-textarea@1.10.2:
+ resolution: {integrity: sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==}
peerDependencies:
react: '>=16.9.0'
react-dom: '>=16.9.0'
@@ -5031,6 +6006,9 @@ packages:
read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+ readable-stream@2.3.8:
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@@ -5039,6 +6017,14 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ real-require@0.1.0:
+ resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==}
+ engines: {node: '>= 12.13.0'}
+
recharts@3.0.0:
resolution: {integrity: sha512-GODedlXZEOQ/KN15puHqaEk9JaiUvFr+Wef/nSagi7g9wHPFLB7prH1/J8vyEBtA2Es6r8qGY1t2OqkuAIpgBg==}
engines: {node: '>=18'}
@@ -5149,10 +6135,17 @@ packages:
request-filtering-agent@2.0.1:
resolution: {integrity: sha512-QvD3qwthEt9J+2hCdQ3wTn3Z/ZsgyiMECjY9yVJ0F8FtnGfNQG+dRz65eKayYRHIRQ6OGjH8Zuqr1lw7G6pz1Q==}
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ require-main-filename@2.0.0:
+ resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+
reselect@5.1.1:
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
@@ -5175,6 +6168,9 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+ ripemd160@2.0.2:
+ resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
+
robust-predicates@3.0.2:
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
@@ -5202,9 +6198,20 @@ packages:
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+ safe-regex-test@1.1.0:
+ resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
+ engines: {node: '>= 0.4'}
+
+ safe-stable-stringify@2.5.0:
+ resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
+ engines: {node: '>=10'}
+
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
@@ -5218,6 +6225,14 @@ packages:
scroll-into-view-if-needed@3.1.0:
resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==}
+ secp256k1@3.8.1:
+ resolution: {integrity: sha512-tArjQw2P0RTdY7QmkNehgp6TVvQXq6ulIhxv8gaH6YubKG/wxxAoNKcbuXjDhybbc+b2Ihc7e0xxiGN744UIiQ==}
+ engines: {node: '>=4.0.0'}
+
+ secp256k1@5.0.1:
+ resolution: {integrity: sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==}
+ engines: {node: '>=18.0.0'}
+
secure-json-parse@2.7.0:
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
@@ -5241,9 +6256,16 @@ packages:
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
engines: {node: '>= 18'}
+ set-blocking@2.0.0:
+ resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
set-value@2.0.1:
resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
engines: {node: '>=0.10.0'}
@@ -5251,6 +6273,11 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+ sha.js@2.4.12:
+ resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==}
+ engines: {node: '>= 0.10'}
+ hasBin: true
+
sharp@0.32.6:
resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==}
engines: {node: '>=14.15.0'}
@@ -5295,6 +6322,17 @@ packages:
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+ socket.io-client@4.8.1:
+ resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==}
+ engines: {node: '>=10.0.0'}
+
+ socket.io-parser@4.2.4:
+ resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
+ engines: {node: '>=10.0.0'}
+
+ sonic-boom@2.8.0:
+ resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==}
+
sonner@2.0.5:
resolution: {integrity: sha512-YwbHQO6cSso3HBXlbCkgrgzDNIhws14r4MO87Ofy+cV2X7ES4pOoAK3+veSmVTvqNx1BWUxlhPmZzP00Crk2aQ==}
peerDependencies:
@@ -5316,6 +6354,10 @@ packages:
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+ split-on-first@1.1.0:
+ resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
+ engines: {node: '>=6'}
+
split-on-first@3.0.0:
resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==}
engines: {node: '>=12'}
@@ -5324,6 +6366,10 @@ packages:
resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
engines: {node: '>=0.10.0'}
+ split2@4.2.0:
+ resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
+ engines: {node: '>= 10.x'}
+
statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
@@ -5332,9 +6378,16 @@ packages:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
+ stream-shift@1.0.3:
+ resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==}
+
streamx@2.22.1:
resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==}
+ strict-uri-encode@2.0.0:
+ resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
+ engines: {node: '>=4'}
+
string-convert@0.2.1:
resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==}
@@ -5346,6 +6399,9 @@ packages:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
+ string_decoder@1.1.1:
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
@@ -5388,6 +6444,10 @@ packages:
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
+ superstruct@1.0.4:
+ resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==}
+ engines: {node: '>=14.0.0'}
+
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -5440,6 +6500,9 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+ thread-stream@0.15.2:
+ resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==}
+
throttle-debounce@5.0.2:
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
engines: {node: '>=12.22'}
@@ -5458,6 +6521,10 @@ packages:
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
engines: {node: '>=12.0.0'}
+ to-buffer@1.2.1:
+ resolution: {integrity: sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==}
+ engines: {node: '>= 0.4'}
+
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -5498,6 +6565,9 @@ packages:
resolution: {integrity: sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==}
engines: {node: '>=12'}
+ tslib@1.14.1:
+ resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+
tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
@@ -5526,6 +6596,13 @@ packages:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
engines: {node: '>= 0.6'}
+ typed-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
+ engines: {node: '>= 0.4'}
+
+ typeforce@1.18.0:
+ resolution: {integrity: sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==}
+
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
engines: {node: '>=14.17'}
@@ -5537,6 +6614,15 @@ packages:
ufo@1.6.1:
resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
+ uint8arrays@3.1.0:
+ resolution: {integrity: sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==}
+
+ uint8arrays@3.1.1:
+ resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==}
+
+ uncrypto@0.1.3:
+ resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
+
undici-types@7.8.0:
resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==}
@@ -5578,6 +6664,65 @@ packages:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
+ unstorage@1.16.1:
+ resolution: {integrity: sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==}
+ peerDependencies:
+ '@azure/app-configuration': ^1.8.0
+ '@azure/cosmos': ^4.2.0
+ '@azure/data-tables': ^13.3.0
+ '@azure/identity': ^4.6.0
+ '@azure/keyvault-secrets': ^4.9.0
+ '@azure/storage-blob': ^12.26.0
+ '@capacitor/preferences': ^6.0.3 || ^7.0.0
+ '@deno/kv': '>=0.9.0'
+ '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0
+ '@planetscale/database': ^1.19.0
+ '@upstash/redis': ^1.34.3
+ '@vercel/blob': '>=0.27.1'
+ '@vercel/kv': ^1.0.1
+ aws4fetch: ^1.0.20
+ db0: '>=0.2.1'
+ idb-keyval: ^6.2.1
+ ioredis: ^5.4.2
+ uploadthing: ^7.4.4
+ peerDependenciesMeta:
+ '@azure/app-configuration':
+ optional: true
+ '@azure/cosmos':
+ optional: true
+ '@azure/data-tables':
+ optional: true
+ '@azure/identity':
+ optional: true
+ '@azure/keyvault-secrets':
+ optional: true
+ '@azure/storage-blob':
+ optional: true
+ '@capacitor/preferences':
+ optional: true
+ '@deno/kv':
+ optional: true
+ '@netlify/blobs':
+ optional: true
+ '@planetscale/database':
+ optional: true
+ '@upstash/redis':
+ optional: true
+ '@vercel/blob':
+ optional: true
+ '@vercel/kv':
+ optional: true
+ aws4fetch:
+ optional: true
+ db0:
+ optional: true
+ idb-keyval:
+ optional: true
+ ioredis:
+ optional: true
+ uploadthing:
+ optional: true
+
update-browserslist-db@1.1.3:
resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
hasBin: true
@@ -5629,6 +6774,16 @@ packages:
'@types/react':
optional: true
+ use-sync-external-store@1.2.0:
+ resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
+ use-sync-external-store@1.4.0:
+ resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
use-sync-external-store@1.5.0:
resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
peerDependencies:
@@ -5640,13 +6795,28 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ utf-8-validate@5.0.10:
+ resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==}
+ engines: {node: '>=6.14.2'}
+
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ util@0.12.5:
+ resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
+
uuid@11.1.0:
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
hasBin: true
+ uuid@8.3.2:
+ resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ hasBin: true
+
+ uuid@9.0.1:
+ resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
+ hasBin: true
+
v8n@1.5.1:
resolution: {integrity: sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==}
@@ -5673,6 +6843,33 @@ packages:
validate.io-number@1.0.3:
resolution: {integrity: sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==}
+ valtio@1.13.2:
+ resolution: {integrity: sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=16.8'
+ react: '>=16.8'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+
+ valtio@2.1.5:
+ resolution: {integrity: sha512-vsh1Ixu5mT0pJFZm+Jspvhga5GzHUTYv0/+Th203pLfh3/wbHwxhu/Z2OkZDXIgHfjnjBns7SN9HNcbDvPmaGw==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ react: '>=18.0.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ react:
+ optional: true
+
+ varuint-bitcoin@1.1.2:
+ resolution: {integrity: sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==}
+
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
@@ -5695,26 +6892,58 @@ packages:
victory-vendor@37.3.6:
resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==}
- vite@6.3.5:
- resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
- hasBin: true
+ viem@2.23.2:
+ resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==}
peerDependencies:
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
- jiti: '>=1.21.0'
- less: '*'
- lightningcss: ^1.21.0
- sass: '*'
- sass-embedded: '*'
- stylus: '*'
- sugarss: '*'
- terser: ^5.16.0
- tsx: ^4.8.1
- yaml: ^2.4.2
+ typescript: '>=5.0.4'
peerDependenciesMeta:
- '@types/node':
+ typescript:
optional: true
- jiti:
+
+ viem@2.31.0:
+ resolution: {integrity: sha512-U7OMQ6yqK+bRbEIarf2vqxL7unSEQvNxvML/1zG7suAmKuJmipqdVTVJGKBCJiYsm/EremyO2FS4dHIPpGv+eA==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ viem@2.32.0:
+ resolution: {integrity: sha512-pHwKXQSyEWX+8ttOQJdU5dSBfYd6L9JxARY/Sx0MBj3uF/Zaiqt6o1SbzjFjQXkNzWSgtxK7H89ZI1SMIA2iLQ==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ viem@2.33.2:
+ resolution: {integrity: sha512-/720OaM4dHWs8vXwNpyet+PRERhPaW+n/1UVSCzyb9jkmwwVfaiy/R6YfCFb4v+XXbo8s3Fapa3DM5yCRSkulA==}
+ peerDependencies:
+ typescript: '>=5.0.4'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ vite@6.3.5:
+ resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
optional: true
less:
optional: true
@@ -5758,9 +6987,23 @@ packages:
w3c-keyname@2.2.8:
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
+ wagmi@2.16.1:
+ resolution: {integrity: sha512-iUdaoe/xd5NiNRW72QVctZs+962EORJKAvJTCsmf9n6TnEApPlENuvVRJKgobI4cGUgi5scWAstpLprB+RRo9Q==}
+ peerDependencies:
+ '@tanstack/react-query': '>=5.0.0'
+ react: '>=18'
+ typescript: '>=5.0.4'
+ viem: 2.x
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
+ webextension-polyfill@0.10.0:
+ resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==}
+
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -5775,15 +7018,29 @@ packages:
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+ which-module@2.0.1:
+ resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
+
+ which-typed-array@1.1.19:
+ resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
+ engines: {node: '>= 0.4'}
+
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
hasBin: true
+ wif@2.0.6:
+ resolution: {integrity: sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==}
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
+ wrap-ansi@6.2.0:
+ resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+ engines: {node: '>=8'}
+
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -5795,6 +7052,65 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ ws@7.5.10:
+ resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+ engines: {node: '>=8.3.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.17.1:
+ resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.18.0:
+ resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ ws@8.18.2:
+ resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xmlhttprequest-ssl@2.1.2:
+ resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==}
+ engines: {node: '>=0.4.0'}
+
+ xtend@4.0.2:
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+ engines: {node: '>=0.4'}
+
+ y18n@4.0.3:
+ resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
+
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -5807,6 +7123,14 @@ packages:
engines: {node: '>= 14.6'}
hasBin: true
+ yargs-parser@18.1.3:
+ resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+ engines: {node: '>=6'}
+
+ yargs@15.4.1:
+ resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+ engines: {node: '>=8'}
+
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
@@ -5816,6 +7140,9 @@ packages:
peerDependencies:
zod: ^3.24.1
+ zod@3.22.4:
+ resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
+
zod@3.25.67:
resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==}
@@ -5828,8 +7155,44 @@ packages:
react:
optional: true
- zustand@5.0.5:
- resolution: {integrity: sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==}
+ zustand@5.0.0:
+ resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
+ zustand@5.0.3:
+ resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
+ zustand@5.0.7:
+ resolution: {integrity: sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==}
engines: {node: '>=12.20.0'}
peerDependencies:
'@types/react': '>=18.0.0'
@@ -5851,6 +7214,8 @@ packages:
snapshots:
+ '@adraffy/ens-normalize@1.11.0': {}
+
'@ai-sdk/openai@1.3.22(zod@3.25.67)':
dependencies:
'@ai-sdk/provider': 1.1.3
@@ -5898,7 +7263,7 @@ snapshots:
'@ant-design/cssinjs-utils@1.1.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
- '@ant-design/cssinjs': 1.23.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@ant-design/cssinjs': 1.24.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@babel/runtime': 7.27.6
rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
@@ -5916,6 +7281,18 @@ snapshots:
react-dom: 19.1.0(react@19.1.0)
stylis: 4.3.6
+ '@ant-design/cssinjs@1.24.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@emotion/hash': 0.8.0
+ '@emotion/unitless': 0.7.5
+ classnames: 2.5.1
+ csstype: 3.1.3
+ rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ stylis: 4.3.6
+
'@ant-design/fast-color@2.0.6':
dependencies:
'@babel/runtime': 7.27.6
@@ -5963,11 +7340,11 @@ snapshots:
'@babel/generator': 7.28.0
'@babel/helper-compilation-targets': 7.27.2
'@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0)
- '@babel/helpers': 7.27.6
+ '@babel/helpers': 7.28.2
'@babel/parser': 7.28.0
'@babel/template': 7.27.2
'@babel/traverse': 7.28.0
- '@babel/types': 7.28.0
+ '@babel/types': 7.28.2
convert-source-map: 2.0.0
debug: 4.4.1
gensync: 1.0.0-beta.2
@@ -6029,10 +7406,10 @@ snapshots:
'@babel/helper-validator-option@7.27.1': {}
- '@babel/helpers@7.27.6':
+ '@babel/helpers@7.28.2':
dependencies:
'@babel/template': 7.27.2
- '@babel/types': 7.28.0
+ '@babel/types': 7.28.2
'@babel/parser@7.28.0':
dependencies:
@@ -6075,6 +7452,11 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
+ '@babel/types@7.28.2':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
'@babycommando/entity-db@1.0.11':
dependencies:
'@xenova/transformers': 2.17.2
@@ -6082,6 +7464,26 @@ snapshots:
transitivePeerDependencies:
- bare-buffer
+ '@base-org/account@1.1.1(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@noble/hashes': 1.4.0
+ clsx: 1.2.1
+ eventemitter3: 5.0.1
+ idb-keyval: 6.2.1
+ ox: 0.6.9(typescript@5.8.3)(zod@3.25.67)
+ preact: 10.24.2
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ zustand: 5.0.3(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0))
+ transitivePeerDependencies:
+ - '@types/react'
+ - bufferutil
+ - immer
+ - react
+ - typescript
+ - use-sync-external-store
+ - utf-8-validate
+ - zod
+
'@biomejs/biome@2.0.5':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.0.5
@@ -6117,6 +7519,11 @@ snapshots:
'@biomejs/cli-win32-x64@2.0.5':
optional: true
+ '@bitcoinerlab/secp256k1@1.2.0':
+ dependencies:
+ '@noble/curves': 1.9.2
+ optional: true
+
'@braintree/sanitize-url@7.1.1': {}
'@chevrotain/cst-dts-gen@11.0.3':
@@ -6197,6 +7604,40 @@ snapshots:
style-mod: 4.1.2
w3c-keyname: 2.2.8
+ '@coinbase/wallet-sdk@3.9.3':
+ dependencies:
+ bn.js: 5.2.2
+ buffer: 6.0.3
+ clsx: 1.2.1
+ eth-block-tracker: 7.1.0
+ eth-json-rpc-filters: 6.0.1
+ eventemitter3: 5.0.1
+ keccak: 3.0.4
+ preact: 10.27.0
+ sha.js: 2.4.12
+ transitivePeerDependencies:
+ - supports-color
+
+ '@coinbase/wallet-sdk@4.3.6(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@noble/hashes': 1.4.0
+ clsx: 1.2.1
+ eventemitter3: 5.0.1
+ idb-keyval: 6.2.1
+ ox: 0.6.9(typescript@5.8.3)(zod@3.25.67)
+ preact: 10.24.2
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ zustand: 5.0.3(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0))
+ transitivePeerDependencies:
+ - '@types/react'
+ - bufferutil
+ - immer
+ - react
+ - typescript
+ - use-sync-external-store
+ - utf-8-validate
+ - zod
+
'@date-fns/tz@1.2.0': {}
'@dnd-kit/accessibility@3.1.1(react@19.1.0)':
@@ -6231,6 +7672,10 @@ snapshots:
react: 19.1.0
tslib: 2.8.1
+ '@ecies/ciphers@0.2.4(@noble/ciphers@1.3.0)':
+ dependencies:
+ '@noble/ciphers': 1.3.0
+
'@emoji-mart/data@1.2.1': {}
'@emoji-mart/react@1.1.1(emoji-mart@5.6.0)(react@19.1.0)':
@@ -6412,7 +7857,7 @@ snapshots:
dependencies:
'@types/json-schema': 7.0.15
- '@eslint/core@0.15.0':
+ '@eslint/core@0.15.1':
dependencies:
'@types/json-schema': 7.0.15
@@ -6434,11 +7879,31 @@ snapshots:
'@eslint/object-schema@2.1.6': {}
- '@eslint/plugin-kit@0.3.2':
+ '@eslint/plugin-kit@0.3.4':
dependencies:
- '@eslint/core': 0.15.0
+ '@eslint/core': 0.15.1
levn: 0.4.1
+ '@ethereumjs/common@3.2.0':
+ dependencies:
+ '@ethereumjs/util': 8.1.0
+ crc-32: 1.2.2
+
+ '@ethereumjs/rlp@4.0.1': {}
+
+ '@ethereumjs/tx@4.2.0':
+ dependencies:
+ '@ethereumjs/common': 3.2.0
+ '@ethereumjs/rlp': 4.0.1
+ '@ethereumjs/util': 8.1.0
+ ethereum-cryptography: 2.2.1
+
+ '@ethereumjs/util@8.1.0':
+ dependencies:
+ '@ethereumjs/rlp': 4.0.1
+ ethereum-cryptography: 2.2.1
+ micro-ftch: 0.3.1
+
'@floating-ui/core@0.7.3': {}
'@floating-ui/core@1.7.1':
@@ -6595,6 +8060,11 @@ snapshots:
'@lit-labs/ssr-dom-shim@1.3.0': {}
+ '@lit/react@1.0.8(@types/react@19.1.8)':
+ dependencies:
+ '@types/react': 19.1.8
+ optional: true
+
'@lit/reactive-element@2.1.0':
dependencies:
'@lit-labs/ssr-dom-shim': 1.3.0
@@ -6751,6 +8221,162 @@ snapshots:
dependencies:
langium: 3.3.1
+ '@metamask/eth-json-rpc-provider@1.0.1':
+ dependencies:
+ '@metamask/json-rpc-engine': 7.3.3
+ '@metamask/safe-event-emitter': 3.1.2
+ '@metamask/utils': 5.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/json-rpc-engine@7.3.3':
+ dependencies:
+ '@metamask/rpc-errors': 6.4.0
+ '@metamask/safe-event-emitter': 3.1.2
+ '@metamask/utils': 8.5.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/json-rpc-engine@8.0.2':
+ dependencies:
+ '@metamask/rpc-errors': 6.4.0
+ '@metamask/safe-event-emitter': 3.1.2
+ '@metamask/utils': 8.5.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/json-rpc-middleware-stream@7.0.2':
+ dependencies:
+ '@metamask/json-rpc-engine': 8.0.2
+ '@metamask/safe-event-emitter': 3.1.2
+ '@metamask/utils': 8.5.0
+ readable-stream: 3.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/object-multiplex@2.1.0':
+ dependencies:
+ once: 1.4.0
+ readable-stream: 3.6.2
+
+ '@metamask/onboarding@1.0.1':
+ dependencies:
+ bowser: 2.11.0
+
+ '@metamask/providers@16.1.0':
+ dependencies:
+ '@metamask/json-rpc-engine': 8.0.2
+ '@metamask/json-rpc-middleware-stream': 7.0.2
+ '@metamask/object-multiplex': 2.1.0
+ '@metamask/rpc-errors': 6.4.0
+ '@metamask/safe-event-emitter': 3.1.2
+ '@metamask/utils': 8.5.0
+ detect-browser: 5.3.0
+ extension-port-stream: 3.0.0
+ fast-deep-equal: 3.1.3
+ is-stream: 2.0.1
+ readable-stream: 3.6.2
+ webextension-polyfill: 0.10.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/rpc-errors@6.4.0':
+ dependencies:
+ '@metamask/utils': 9.3.0
+ fast-safe-stringify: 2.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/safe-event-emitter@2.0.0': {}
+
+ '@metamask/safe-event-emitter@3.1.2': {}
+
+ '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ bufferutil: 4.0.9
+ cross-fetch: 4.1.0
+ date-fns: 2.30.0
+ debug: 4.4.1
+ eciesjs: 0.4.15
+ eventemitter2: 6.4.9
+ readable-stream: 3.6.2
+ socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ utf-8-validate: 5.0.10
+ uuid: 8.3.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/sdk-install-modal-web@0.32.0':
+ dependencies:
+ '@paulmillr/qr': 0.2.1
+
+ '@metamask/sdk@0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@metamask/onboarding': 1.0.1
+ '@metamask/providers': 16.1.0
+ '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@metamask/sdk-install-modal-web': 0.32.0
+ '@paulmillr/qr': 0.2.1
+ bowser: 2.11.0
+ cross-fetch: 4.1.0
+ debug: 4.4.1
+ eciesjs: 0.4.15
+ eth-rpc-errors: 4.0.3
+ eventemitter2: 6.4.9
+ obj-multiplex: 1.0.0
+ pump: 3.0.3
+ readable-stream: 3.6.2
+ socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ tslib: 2.8.1
+ util: 0.12.5
+ uuid: 8.3.2
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - supports-color
+ - utf-8-validate
+
+ '@metamask/superstruct@3.2.1': {}
+
+ '@metamask/utils@5.0.2':
+ dependencies:
+ '@ethereumjs/tx': 4.2.0
+ '@types/debug': 4.1.12
+ debug: 4.4.1
+ semver: 7.7.2
+ superstruct: 1.0.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/utils@8.5.0':
+ dependencies:
+ '@ethereumjs/tx': 4.2.0
+ '@metamask/superstruct': 3.2.1
+ '@noble/hashes': 1.8.0
+ '@scure/base': 1.2.6
+ '@types/debug': 4.1.12
+ debug: 4.4.1
+ pony-cause: 2.1.11
+ semver: 7.7.2
+ uuid: 9.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@metamask/utils@9.3.0':
+ dependencies:
+ '@ethereumjs/tx': 4.2.0
+ '@metamask/superstruct': 3.2.1
+ '@noble/hashes': 1.8.0
+ '@scure/base': 1.2.6
+ '@types/debug': 4.1.12
+ debug: 4.4.1
+ pony-cause: 2.1.11
+ semver: 7.7.2
+ uuid: 9.0.1
+ transitivePeerDependencies:
+ - supports-color
+
'@modelcontextprotocol/sdk@1.13.2':
dependencies:
ajv: 6.12.6
@@ -6767,20 +8393,48 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@msgpack/msgpack@3.1.2': {}
+
'@mysten/bcs@1.0.4':
dependencies:
bs58: 6.0.0
+ '@noble/ciphers@1.2.1': {}
+
+ '@noble/ciphers@1.3.0': {}
+
+ '@noble/curves@1.4.2':
+ dependencies:
+ '@noble/hashes': 1.4.0
+
'@noble/curves@1.6.0':
dependencies:
'@noble/hashes': 1.5.0
+ '@noble/curves@1.8.0':
+ dependencies:
+ '@noble/hashes': 1.7.0
+
+ '@noble/curves@1.8.1':
+ dependencies:
+ '@noble/hashes': 1.7.1
+
+ '@noble/curves@1.9.1':
+ dependencies:
+ '@noble/hashes': 1.8.0
+
'@noble/curves@1.9.2':
dependencies:
'@noble/hashes': 1.8.0
+ '@noble/hashes@1.4.0': {}
+
'@noble/hashes@1.5.0': {}
+ '@noble/hashes@1.7.0': {}
+
+ '@noble/hashes@1.7.1': {}
+
'@noble/hashes@1.8.0': {}
'@nodelib/fs.scandir@2.1.5':
@@ -6795,16 +8449,32 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.19.1
- '@nuwa-ai/identity-kit-web@0.2.2(react@19.1.0)(typescript@5.8.3)':
+ '@nuwa-ai/cap-kit@0.3.5(react@19.1.0)(typescript@5.8.3)(zod@3.25.67)':
dependencies:
- '@nuwa-ai/identity-kit': 0.2.2(typescript@5.8.3)
+ '@modelcontextprotocol/sdk': 1.13.2
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@nuwa-ai/identity-kit': 0.3.4(typescript@5.8.3)
+ '@roochnetwork/rooch-sdk': 0.3.6(typescript@5.8.3)
+ ai: 4.3.16(react@19.1.0)(zod@3.25.67)
+ js-yaml: 4.1.0
+ multiformats: 9.9.0
+ transitivePeerDependencies:
+ - react
+ - supports-color
+ - typescript
+ - zod
+
+ '@nuwa-ai/identity-kit-web@0.3.5(react@19.1.0)(typescript@5.8.3)':
+ dependencies:
+ '@nuwa-ai/identity-kit': 0.3.4(typescript@5.8.3)
optionalDependencies:
react: 19.1.0
transitivePeerDependencies:
- supports-color
- typescript
- '@nuwa-ai/identity-kit@0.2.2(typescript@5.8.3)':
+ '@nuwa-ai/identity-kit@0.3.4(typescript@5.8.3)':
dependencies:
'@noble/curves': 1.9.2
'@noble/hashes': 1.8.0
@@ -6823,6 +8493,8 @@ snapshots:
'@opentelemetry/api@1.9.0': {}
+ '@paulmillr/qr@0.2.1': {}
+
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -7707,7 +9379,7 @@ snapshots:
dependencies:
'@babel/runtime': 7.27.6
'@rc-component/portal': 1.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- '@rc-component/trigger': 2.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@rc-component/trigger': 2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
classnames: 2.5.1
rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
@@ -7724,17 +9396,631 @@ snapshots:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
+ '@rc-component/trigger@2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@babel/runtime': 7.27.6
+ '@rc-component/portal': 1.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ classnames: 2.5.1
+ rc-motion: 2.9.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ rc-resize-observer: 1.4.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
'@reduxjs/toolkit@2.8.2(react-redux@9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1))(react@19.1.0)':
dependencies:
- '@standard-schema/spec': 1.0.0
- '@standard-schema/utils': 0.3.0
- immer: 10.1.1
- redux: 5.0.1
- redux-thunk: 3.1.0(redux@5.0.1)
- reselect: 5.1.1
+ '@standard-schema/spec': 1.0.0
+ '@standard-schema/utils': 0.3.0
+ immer: 10.1.1
+ redux: 5.0.1
+ redux-thunk: 3.1.0(redux@5.0.1)
+ reselect: 5.1.1
+ optionalDependencies:
+ react: 19.1.0
+ react-redux: 9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1)
+
+ '@reown/appkit-adapter-wagmi@1.7.17(@types/react@19.1.8)(@wagmi/core@2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(immer@10.1.1)(lit@3.3.0)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(wagmi@2.16.1(@tanstack/query-core@5.83.1)(@tanstack/react-query@5.84.1(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67))(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(lit@3.3.0)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-polyfills': 1.7.17
+ '@reown/appkit-scaffold-ui': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@wagmi/core': 2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))
+ '@walletconnect/universal-provider': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ valtio: 2.1.5(@types/react@19.1.8)(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ wagmi: 2.16.1(@tanstack/query-core@5.83.1)(@tanstack/react-query@5.84.1(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67)
+ optionalDependencies:
+ '@wagmi/connectors': 5.9.1(@types/react@19.1.8)(@wagmi/core@2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - immer
+ - ioredis
+ - lit
+ - react
+ - supports-color
+ - typescript
+ - uploadthing
+ - use-sync-external-store
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-common@1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)':
+ dependencies:
+ big.js: 6.2.2
+ dayjs: 1.11.13
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-common@1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ big.js: 6.2.2
+ dayjs: 1.11.13
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)':
+ dependencies:
+ big.js: 6.2.2
+ dayjs: 1.11.13
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ big.js: 6.2.2
+ dayjs: 1.11.13
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-controllers@1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@walletconnect/universal-provider': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ valtio: 2.1.5(@types/react@19.1.8)(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-controllers@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-pay@1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-ui': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ lit: 3.3.0
+ valtio: 2.1.5(@types/react@19.1.8)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-pay@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ lit: 3.3.0
+ valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-polyfills@1.7.17':
+ dependencies:
+ buffer: 6.0.3
+
+ '@reown/appkit-polyfills@1.7.8':
+ dependencies:
+ buffer: 6.0.3
+
+ '@reown/appkit-scaffold-ui@1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-ui': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ lit: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - valtio
+ - zod
+
+ '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ lit: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - valtio
+ - zod
+
+ '@reown/appkit-siwx@1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(lit@3.3.0)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-scaffold-ui': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-ui': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ bip322-js: 2.0.0
+ bs58: 6.0.0
+ lit: 3.3.0
+ tweetnacl: 1.0.3
+ viem: 2.32.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - valtio
+ - zod
+ optional: true
+
+ '@reown/appkit-ui@1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ lit: 3.3.0
+ qrcode: 1.5.3
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-ui@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ lit: 3.3.0
+ qrcode: 1.5.3
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-utils@1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-polyfills': 1.7.17
+ '@reown/appkit-wallet': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@wallet-standard/wallet': 1.1.0
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/universal-provider': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ valtio: 2.1.5(@types/react@19.1.8)(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-utils@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-polyfills': 1.7.8
+ '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit-wallet@1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)
+ '@reown/appkit-polyfills': 1.7.17
+ '@walletconnect/logger': 2.1.2
+ zod: 3.22.4
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+
+ '@reown/appkit-wallet@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)':
+ dependencies:
+ '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)
+ '@reown/appkit-polyfills': 1.7.8
+ '@walletconnect/logger': 2.1.2
+ zod: 3.22.4
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+
+ '@reown/appkit@1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(lit@3.3.0)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-pay': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-polyfills': 1.7.17
+ '@reown/appkit-scaffold-ui': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-ui': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.17(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@walletconnect/universal-provider': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ bs58: 6.0.0
+ semver: 7.7.2
+ valtio: 2.1.5(@types/react@19.1.8)(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
optionalDependencies:
- react: 19.1.0
- react-redux: 9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1)
+ '@lit/react': 1.0.8(@types/react@19.1.8)
+ '@reown/appkit-siwx': 1.7.17(@types/react@19.1.8)(bufferutil@4.0.9)(lit@3.3.0)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@2.1.5(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - lit
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@reown/appkit@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-pay': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-polyfills': 1.7.8
+ '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@reown/appkit-utils': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)
+ '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@walletconnect/types': 2.21.0
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ bs58: 6.0.0
+ valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
'@rjsf/core@5.24.12(@rjsf/utils@5.24.12(react@19.1.0))(react@19.1.0)':
dependencies:
@@ -7846,16 +10132,60 @@ snapshots:
- supports-color
- typescript
+ '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ events: 3.3.0
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+ - zod
+
+ '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@safe-global/safe-gateway-typescript-sdk': 3.23.1
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - bufferutil
+ - typescript
+ - utf-8-validate
+ - zod
+
+ '@safe-global/safe-gateway-typescript-sdk@3.23.1': {}
+
'@scure/base@1.1.9': {}
'@scure/base@1.2.6': {}
+ '@scure/bip32@1.4.0':
+ dependencies:
+ '@noble/curves': 1.4.2
+ '@noble/hashes': 1.4.0
+ '@scure/base': 1.1.9
+
+ '@scure/bip32@1.6.2':
+ dependencies:
+ '@noble/curves': 1.8.1
+ '@noble/hashes': 1.7.1
+ '@scure/base': 1.2.6
+
'@scure/bip32@1.7.0':
dependencies:
'@noble/curves': 1.9.2
'@noble/hashes': 1.8.0
'@scure/base': 1.2.6
+ '@scure/bip39@1.3.0':
+ dependencies:
+ '@noble/hashes': 1.4.0
+ '@scure/base': 1.1.9
+
+ '@scure/bip39@1.5.4':
+ dependencies:
+ '@noble/hashes': 1.7.1
+ '@scure/base': 1.2.6
+
'@scure/bip39@1.6.0':
dependencies:
'@noble/hashes': 1.8.0
@@ -7899,6 +10229,8 @@ snapshots:
'@shikijs/vscode-textmate@10.0.2': {}
+ '@socket.io/component-emitter@3.1.2': {}
+
'@splinetool/runtime@0.9.526':
dependencies:
on-change: 4.0.2
@@ -7974,6 +10306,13 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.17
+ '@tanstack/query-core@5.83.1': {}
+
+ '@tanstack/react-query@5.84.1(react@19.1.0)':
+ dependencies:
+ '@tanstack/query-core': 5.83.1
+ react: 19.1.0
+
'@types/d3-array@3.2.1': {}
'@types/d3-axis@3.0.6':
@@ -8300,6 +10639,796 @@ snapshots:
transitivePeerDependencies:
- '@swc/helpers'
+ '@wagmi/connectors@5.9.1(@types/react@19.1.8)(@wagmi/core@2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67)':
+ dependencies:
+ '@base-org/account': 1.1.1(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@wagmi/core': 2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))
+ '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - immer
+ - ioredis
+ - react
+ - supports-color
+ - uploadthing
+ - use-sync-external-store
+ - utf-8-validate
+ - zod
+
+ '@wagmi/core@2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))':
+ dependencies:
+ eventemitter3: 5.0.1
+ mipd: 0.0.7(typescript@5.8.3)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ zustand: 5.0.0(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0))
+ optionalDependencies:
+ '@tanstack/query-core': 5.83.1
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - '@types/react'
+ - immer
+ - react
+ - use-sync-external-store
+
+ '@wallet-standard/base@1.1.0': {}
+
+ '@wallet-standard/wallet@1.1.0':
+ dependencies:
+ '@wallet-standard/base': 1.1.0
+
+ '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.0
+ '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/window-getters': 1.0.1
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ uint8arrays: 3.1.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.1
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/window-getters': 1.0.1
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ uint8arrays: 3.1.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/core@2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.5
+ '@walletconnect/utils': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/window-getters': 1.0.1
+ es-toolkit: 1.39.3
+ events: 3.3.0
+ uint8arrays: 3.1.1
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/environment@1.0.1':
+ dependencies:
+ tslib: 1.14.1
+
+ '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@reown/appkit': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/jsonrpc-http-connection': 1.0.8
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/types': 2.21.1
+ '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - react
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/events@1.0.1':
+ dependencies:
+ keyvaluestorage-interface: 1.0.0
+ tslib: 1.14.1
+
+ '@walletconnect/heartbeat@1.2.2':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/time': 1.0.2
+ events: 3.3.0
+
+ '@walletconnect/jsonrpc-http-connection@1.0.8':
+ dependencies:
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/safe-json': 1.0.2
+ cross-fetch: 3.2.0
+ events: 3.3.0
+ transitivePeerDependencies:
+ - encoding
+
+ '@walletconnect/jsonrpc-provider@1.0.14':
+ dependencies:
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/safe-json': 1.0.2
+ events: 3.3.0
+
+ '@walletconnect/jsonrpc-types@1.0.4':
+ dependencies:
+ events: 3.3.0
+ keyvaluestorage-interface: 1.0.0
+
+ '@walletconnect/jsonrpc-utils@1.0.8':
+ dependencies:
+ '@walletconnect/environment': 1.0.1
+ '@walletconnect/jsonrpc-types': 1.0.4
+ tslib: 1.14.1
+
+ '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)':
+ dependencies:
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/safe-json': 1.0.2
+ events: 3.3.0
+ ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ '@walletconnect/keyvaluestorage@1.1.1':
+ dependencies:
+ '@walletconnect/safe-json': 1.0.2
+ idb-keyval: 6.2.2
+ unstorage: 1.16.1(idb-keyval@6.2.2)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - db0
+ - ioredis
+ - uploadthing
+
+ '@walletconnect/logger@2.1.2':
+ dependencies:
+ '@walletconnect/safe-json': 1.0.2
+ pino: 7.11.0
+
+ '@walletconnect/relay-api@1.0.11':
+ dependencies:
+ '@walletconnect/jsonrpc-types': 1.0.4
+
+ '@walletconnect/relay-auth@1.1.0':
+ dependencies:
+ '@noble/curves': 1.8.0
+ '@noble/hashes': 1.7.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ uint8arrays: 3.1.1
+
+ '@walletconnect/safe-json@1.0.2':
+ dependencies:
+ tslib: 1.14.1
+
+ '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.0
+ '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/sign-client@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/core': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.1
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/sign-client@2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/core': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.5
+ '@walletconnect/utils': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/time@1.0.2':
+ dependencies:
+ tslib: 1.14.1
+
+ '@walletconnect/types@2.21.0':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - db0
+ - ioredis
+ - uploadthing
+
+ '@walletconnect/types@2.21.1':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - db0
+ - ioredis
+ - uploadthing
+
+ '@walletconnect/types@2.21.5':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - db0
+ - ioredis
+ - uploadthing
+
+ '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/jsonrpc-http-connection': 1.0.8
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/sign-client': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/types': 2.21.0
+ '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/jsonrpc-http-connection': 1.0.8
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/types': 2.21.1
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/universal-provider@2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/jsonrpc-http-connection': 1.0.8
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/sign-client': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ '@walletconnect/types': 2.21.5
+ '@walletconnect/utils': 2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ es-toolkit: 1.39.3
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@noble/ciphers': 1.2.1
+ '@noble/curves': 1.8.1
+ '@noble/hashes': 1.7.1
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.0
+ '@walletconnect/window-getters': 1.0.1
+ '@walletconnect/window-metadata': 1.0.1
+ bs58: 6.0.0
+ detect-browser: 5.3.0
+ query-string: 7.1.3
+ uint8arrays: 3.1.0
+ viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@noble/ciphers': 1.2.1
+ '@noble/curves': 1.8.1
+ '@noble/hashes': 1.7.1
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.1
+ '@walletconnect/window-getters': 1.0.1
+ '@walletconnect/window-metadata': 1.0.1
+ bs58: 6.0.0
+ detect-browser: 5.3.0
+ query-string: 7.1.3
+ uint8arrays: 3.1.0
+ viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/utils@2.21.5(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)':
+ dependencies:
+ '@msgpack/msgpack': 3.1.2
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/base': 1.2.6
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.5
+ '@walletconnect/window-getters': 1.0.1
+ '@walletconnect/window-metadata': 1.0.1
+ blakejs: 1.2.1
+ bs58: 6.0.0
+ detect-browser: 5.3.0
+ query-string: 7.1.3
+ uint8arrays: 3.1.1
+ viem: 2.31.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/window-getters@1.0.1':
+ dependencies:
+ tslib: 1.14.1
+
+ '@walletconnect/window-metadata@1.0.1':
+ dependencies:
+ '@walletconnect/window-getters': 1.0.1
+ tslib: 1.14.1
+
+ '@web3icons/common@0.11.15(typescript@5.8.3)':
+ dependencies:
+ typescript: 5.8.3
+
+ '@web3icons/react@4.0.19(react@19.1.0)(typescript@5.8.3)':
+ dependencies:
+ '@web3icons/common': 0.11.15(typescript@5.8.3)
+ react: 19.1.0
+ transitivePeerDependencies:
+ - typescript
+
'@xenova/transformers@2.17.2':
dependencies:
'@huggingface/jinja': 0.2.2
@@ -8310,6 +11439,16 @@ snapshots:
transitivePeerDependencies:
- bare-buffer
+ abitype@1.0.8(typescript@5.8.3)(zod@3.22.4):
+ optionalDependencies:
+ typescript: 5.8.3
+ zod: 3.22.4
+
+ abitype@1.0.8(typescript@5.8.3)(zod@3.25.67):
+ optionalDependencies:
+ typescript: 5.8.3
+ zod: 3.25.67
+
accepts@2.0.0:
dependencies:
mime-types: 3.0.1
@@ -8395,7 +11534,7 @@ snapshots:
antd@5.26.3(date-fns@4.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@ant-design/colors': 7.2.1
- '@ant-design/cssinjs': 1.23.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@ant-design/cssinjs': 1.24.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@ant-design/cssinjs-utils': 1.1.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@ant-design/fast-color': 2.0.6
'@ant-design/icons': 5.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -8405,7 +11544,7 @@ snapshots:
'@rc-component/mutate-observer': 1.1.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@rc-component/qrcode': 1.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@rc-component/tour': 1.15.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- '@rc-component/trigger': 2.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@rc-component/trigger': 2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
classnames: 2.5.1
copy-to-clipboard: 3.3.3
dayjs: 1.11.13
@@ -8435,7 +11574,7 @@ snapshots:
rc-switch: 4.1.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-table: 7.51.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-tabs: 15.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- rc-textarea: 1.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ rc-textarea: 1.10.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-tooltip: 6.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-tree: 5.13.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-tree-select: 5.27.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -8469,6 +11608,12 @@ snapshots:
astring@1.9.0: {}
+ async-mutex@0.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ atomic-sleep@1.0.0: {}
+
attr-accept@2.2.5: {}
autoprefixer@10.4.21(postcss@8.5.6):
@@ -8481,6 +11626,10 @@ snapshots:
postcss: 8.5.6
postcss-value-parser: 4.2.0
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.1.0
+
b4a@1.6.7: {}
babel-plugin-macros@3.1.0:
@@ -8542,22 +11691,86 @@ snapshots:
bare-events: 2.5.4
optional: true
+ base-x@3.0.11:
+ dependencies:
+ safe-buffer: 5.2.1
+ optional: true
+
+ base-x@4.0.1:
+ optional: true
+
base-x@5.0.1: {}
base64-js@1.5.1: {}
bcp-47-match@2.0.3: {}
+ bech32@1.1.4:
+ optional: true
+
bech32@2.0.0: {}
+ big.js@6.2.2: {}
+
binary-extensions@2.3.0: {}
+ bindings@1.5.0:
+ dependencies:
+ file-uri-to-path: 1.0.0
+ optional: true
+
+ bip174@2.1.1:
+ optional: true
+
+ bip322-js@2.0.0:
+ dependencies:
+ '@bitcoinerlab/secp256k1': 1.2.0
+ bitcoinjs-lib: 6.1.7
+ bitcoinjs-message: 2.2.0
+ ecpair: 2.1.0
+ elliptic: 6.6.1
+ fast-sha256: 1.3.0
+ secp256k1: 5.0.1
+ optional: true
+
+ bip66@1.1.5:
+ dependencies:
+ safe-buffer: 5.2.1
+ optional: true
+
+ bitcoinjs-lib@6.1.7:
+ dependencies:
+ '@noble/hashes': 1.8.0
+ bech32: 2.0.0
+ bip174: 2.1.1
+ bs58check: 3.0.1
+ typeforce: 1.18.0
+ varuint-bitcoin: 1.1.2
+ optional: true
+
+ bitcoinjs-message@2.2.0:
+ dependencies:
+ bech32: 1.1.4
+ bs58check: 2.1.2
+ buffer-equals: 1.0.4
+ create-hash: 1.2.0
+ secp256k1: 3.8.1
+ varuint-bitcoin: 1.1.2
+ optional: true
+
bl@4.1.0:
dependencies:
buffer: 5.7.1
inherits: 2.0.4
readable-stream: 3.6.2
+ blakejs@1.2.1: {}
+
+ bn.js@4.12.2:
+ optional: true
+
+ bn.js@5.2.2: {}
+
body-parser@2.2.0:
dependencies:
bytes: 3.1.2
@@ -8574,6 +11787,8 @@ snapshots:
boolbase@1.0.0: {}
+ bowser@2.11.0: {}
+
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
@@ -8587,6 +11802,19 @@ snapshots:
dependencies:
fill-range: 7.1.1
+ brorand@1.1.0:
+ optional: true
+
+ browserify-aes@1.2.0:
+ dependencies:
+ buffer-xor: 1.0.3
+ cipher-base: 1.0.6
+ create-hash: 1.2.0
+ evp_bytestokey: 1.0.3
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+ optional: true
+
browserslist@4.25.0:
dependencies:
caniuse-lite: 1.0.30001724
@@ -8594,15 +11822,44 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.25.0)
+ bs58@4.0.1:
+ dependencies:
+ base-x: 3.0.11
+ optional: true
+
+ bs58@5.0.0:
+ dependencies:
+ base-x: 4.0.1
+ optional: true
+
bs58@6.0.0:
dependencies:
base-x: 5.0.1
+ bs58check@2.1.2:
+ dependencies:
+ bs58: 4.0.1
+ create-hash: 1.2.0
+ safe-buffer: 5.2.1
+ optional: true
+
+ bs58check@3.0.1:
+ dependencies:
+ '@noble/hashes': 1.8.0
+ bs58: 5.0.0
+ optional: true
+
bs58check@4.0.0:
dependencies:
'@noble/hashes': 1.8.0
bs58: 6.0.0
+ buffer-equals@1.0.4:
+ optional: true
+
+ buffer-xor@1.0.3:
+ optional: true
+
buffer@5.7.1:
dependencies:
base64-js: 1.5.1
@@ -8613,6 +11870,10 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
+ bufferutil@4.0.9:
+ dependencies:
+ node-gyp-build: 4.8.4
+
bytes@3.1.2: {}
call-bind-apply-helpers@1.0.2:
@@ -8620,6 +11881,13 @@ snapshots:
es-errors: 1.3.0
function-bind: 1.1.2
+ call-bind@1.0.8:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
call-bound@1.0.4:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -8629,6 +11897,8 @@ snapshots:
camelcase-css@2.0.1: {}
+ camelcase@5.3.1: {}
+
caniuse-lite@1.0.30001724: {}
ccount@2.0.1: {}
@@ -8697,16 +11967,32 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
chownr@1.1.4: {}
chroma-js@3.1.2: {}
+ cipher-base@1.0.6:
+ dependencies:
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+ optional: true
+
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
classnames@2.5.1: {}
+ cliui@6.0.0:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 6.2.0
+
clsx@1.2.1: {}
clsx@2.1.1: {}
@@ -8792,6 +12078,8 @@ snapshots:
convert-source-map@2.0.0: {}
+ cookie-es@1.2.2: {}
+
cookie-signature@1.2.2: {}
cookie@0.7.2: {}
@@ -8806,6 +12094,8 @@ snapshots:
dependencies:
browserslist: 4.25.0
+ core-util-is@1.0.3: {}
+
cors@2.8.5:
dependencies:
object-assign: 4.1.1
@@ -8815,19 +12105,52 @@ snapshots:
dependencies:
layout-base: 1.0.2
- cose-base@2.2.0:
- dependencies:
- layout-base: 2.0.1
+ cose-base@2.2.0:
+ dependencies:
+ layout-base: 2.0.1
+
+ cosmiconfig@7.1.0:
+ dependencies:
+ '@types/parse-json': 4.0.2
+ import-fresh: 3.3.1
+ parse-json: 5.2.0
+ path-type: 4.0.0
+ yaml: 1.10.2
+
+ crc-32@1.2.2: {}
+
+ create-hash@1.2.0:
+ dependencies:
+ cipher-base: 1.0.6
+ inherits: 2.0.4
+ md5.js: 1.3.5
+ ripemd160: 2.0.2
+ sha.js: 2.4.12
+ optional: true
+
+ create-hmac@1.1.7:
+ dependencies:
+ cipher-base: 1.0.6
+ create-hash: 1.2.0
+ inherits: 2.0.4
+ ripemd160: 2.0.2
+ safe-buffer: 5.2.1
+ sha.js: 2.4.12
+ optional: true
+
+ crelt@1.0.6: {}
- cosmiconfig@7.1.0:
+ cross-fetch@3.2.0:
dependencies:
- '@types/parse-json': 4.0.2
- import-fresh: 3.3.1
- parse-json: 5.2.0
- path-type: 4.0.0
- yaml: 1.10.2
+ node-fetch: 2.7.0
+ transitivePeerDependencies:
+ - encoding
- crelt@1.0.6: {}
+ cross-fetch@4.1.0:
+ dependencies:
+ node-fetch: 2.7.0
+ transitivePeerDependencies:
+ - encoding
cross-spawn@7.0.6:
dependencies:
@@ -8835,6 +12158,10 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ crossws@0.3.5:
+ dependencies:
+ uncrypto: 0.1.3
+
css-select@5.2.2:
dependencies:
boolbase: 1.0.0
@@ -9037,20 +12364,32 @@ snapshots:
date-fns-jalali@4.1.0-0: {}
+ date-fns@2.30.0:
+ dependencies:
+ '@babel/runtime': 7.27.6
+
date-fns@4.1.0: {}
dayjs@1.11.13: {}
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
debug@4.4.1:
dependencies:
ms: 2.1.3
+ decamelize@1.2.0: {}
+
decimal.js-light@2.5.1: {}
decode-named-character-reference@1.2.0:
dependencies:
character-entities: 2.0.2
+ decode-uri-component@0.2.2: {}
+
decode-uri-component@0.4.1: {}
decompress-response@6.0.0:
@@ -9061,6 +12400,14 @@ snapshots:
deep-is@0.1.4: {}
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ defu@6.1.4: {}
+
delaunator@5.0.1:
dependencies:
robust-predicates: 3.0.2
@@ -9069,6 +12416,14 @@ snapshots:
dequal@2.0.3: {}
+ derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0)):
+ dependencies:
+ valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0)
+
+ destr@2.0.5: {}
+
+ detect-browser@5.3.0: {}
+
detect-libc@2.0.4: {}
detect-node-es@1.1.0: {}
@@ -9083,6 +12438,8 @@ snapshots:
diff-match-patch@1.0.5: {}
+ dijkstrajs@1.0.3: {}
+
direction@2.0.1: {}
dlv@1.1.3: {}
@@ -9109,18 +12466,57 @@ snapshots:
domelementtype: 2.3.0
domhandler: 5.0.3
+ drbg.js@1.0.1:
+ dependencies:
+ browserify-aes: 1.2.0
+ create-hash: 1.2.0
+ create-hmac: 1.1.7
+ optional: true
+
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
+ duplexify@4.1.3:
+ dependencies:
+ end-of-stream: 1.4.5
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ stream-shift: 1.0.3
+
eastasianwidth@0.2.0: {}
+ eciesjs@0.4.15:
+ dependencies:
+ '@ecies/ciphers': 0.2.4(@noble/ciphers@1.3.0)
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+
+ ecpair@2.1.0:
+ dependencies:
+ randombytes: 2.1.0
+ typeforce: 1.18.0
+ wif: 2.0.6
+ optional: true
+
ee-first@1.1.1: {}
electron-to-chromium@1.5.172: {}
+ elliptic@6.6.1:
+ dependencies:
+ bn.js: 4.12.2
+ brorand: 1.1.0
+ hash.js: 1.1.7
+ hmac-drbg: 1.0.1
+ inherits: 2.0.4
+ minimalistic-assert: 1.0.1
+ minimalistic-crypto-utils: 1.0.1
+ optional: true
+
embla-carousel-react@8.6.0(react@19.1.0):
dependencies:
embla-carousel: 8.6.0
@@ -9141,6 +12537,8 @@ snapshots:
emoji-regex@9.2.2: {}
+ encode-utf8@1.0.3: {}
+
encodeurl@2.0.0: {}
encoding-sniffer@0.2.1:
@@ -9152,6 +12550,20 @@ snapshots:
dependencies:
once: 1.4.0
+ engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10):
+ dependencies:
+ '@socket.io/component-emitter': 3.1.2
+ debug: 4.3.7
+ engine.io-parser: 5.2.3
+ ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ xmlhttprequest-ssl: 2.1.2
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ engine.io-parser@5.2.3: {}
+
entities@4.5.0: {}
entities@6.0.1: {}
@@ -9168,6 +12580,10 @@ snapshots:
dependencies:
es-errors: 1.3.0
+ es-toolkit@1.33.0: {}
+
+ es-toolkit@1.39.3: {}
+
es-toolkit@1.39.4: {}
esast-util-from-estree@2.0.0:
@@ -9238,7 +12654,7 @@ snapshots:
'@eslint/core': 0.14.0
'@eslint/eslintrc': 3.3.1
'@eslint/js': 9.29.0
- '@eslint/plugin-kit': 0.3.2
+ '@eslint/plugin-kit': 0.3.4
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.3
@@ -9324,14 +12740,58 @@ snapshots:
etag@1.8.1: {}
+ eth-block-tracker@7.1.0:
+ dependencies:
+ '@metamask/eth-json-rpc-provider': 1.0.1
+ '@metamask/safe-event-emitter': 3.1.2
+ '@metamask/utils': 5.0.2
+ json-rpc-random-id: 1.0.1
+ pify: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eth-json-rpc-filters@6.0.1:
+ dependencies:
+ '@metamask/safe-event-emitter': 3.1.2
+ async-mutex: 0.2.6
+ eth-query: 2.1.2
+ json-rpc-engine: 6.1.0
+ pify: 5.0.0
+
+ eth-query@2.1.2:
+ dependencies:
+ json-rpc-random-id: 1.0.1
+ xtend: 4.0.2
+
+ eth-rpc-errors@4.0.3:
+ dependencies:
+ fast-safe-stringify: 2.1.1
+
+ ethereum-cryptography@2.2.1:
+ dependencies:
+ '@noble/curves': 1.4.2
+ '@noble/hashes': 1.4.0
+ '@scure/bip32': 1.4.0
+ '@scure/bip39': 1.3.0
+
+ eventemitter2@6.4.9: {}
+
eventemitter3@5.0.1: {}
+ events@3.3.0: {}
+
eventsource-parser@3.0.2: {}
eventsource@3.0.6:
dependencies:
eventsource-parser: 3.0.2
+ evp_bytestokey@1.0.3:
+ dependencies:
+ md5.js: 1.3.5
+ safe-buffer: 5.2.1
+ optional: true
+
expand-template@2.0.3: {}
express-rate-limit@7.5.1(express@5.1.0):
@@ -9383,6 +12843,11 @@ snapshots:
extend@3.0.2: {}
+ extension-port-stream@3.0.0:
+ dependencies:
+ readable-stream: 3.6.2
+ webextension-polyfill: 0.10.0
+
fast-deep-equal@3.1.3: {}
fast-fifo@1.3.2: {}
@@ -9399,6 +12864,13 @@ snapshots:
fast-levenshtein@2.0.6: {}
+ fast-redact@3.5.0: {}
+
+ fast-safe-stringify@2.1.1: {}
+
+ fast-sha256@1.3.0:
+ optional: true
+
fast-uri@3.0.6: {}
fastq@1.19.1:
@@ -9417,10 +12889,15 @@ snapshots:
dependencies:
tslib: 2.8.1
+ file-uri-to-path@1.0.0:
+ optional: true
+
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
+ filter-obj@1.1.0: {}
+
filter-obj@5.1.0: {}
finalhandler@2.1.0:
@@ -9436,6 +12913,11 @@ snapshots:
find-root@1.1.0: {}
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
@@ -9450,6 +12932,10 @@ snapshots:
flatted@3.3.3: {}
+ for-each@0.3.5:
+ dependencies:
+ is-callable: 1.2.7
+
for-in@1.0.2: {}
foreground-child@3.3.1:
@@ -9481,6 +12967,8 @@ snapshots:
gensync@1.0.0-beta.2: {}
+ get-caller-file@2.0.5: {}
+
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -9542,12 +13030,45 @@ snapshots:
guid-typescript@1.0.9: {}
+ h3@1.15.4:
+ dependencies:
+ cookie-es: 1.2.2
+ crossws: 0.3.5
+ defu: 6.1.4
+ destr: 2.0.5
+ iron-webcrypto: 1.2.1
+ node-mock-http: 1.0.2
+ radix3: 1.1.2
+ ufo: 1.6.1
+ uncrypto: 0.1.3
+
hachure-fill@0.5.2: {}
has-flag@4.0.0: {}
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+
has-symbols@1.1.0: {}
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hash-base@3.1.0:
+ dependencies:
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ safe-buffer: 5.2.1
+ optional: true
+
+ hash.js@1.1.7:
+ dependencies:
+ inherits: 2.0.4
+ minimalistic-assert: 1.0.1
+ optional: true
+
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
@@ -9741,6 +13262,13 @@ snapshots:
property-information: 7.1.0
space-separated-tokens: 2.0.2
+ hmac-drbg@1.0.1:
+ dependencies:
+ hash.js: 1.1.7
+ minimalistic-assert: 1.0.1
+ minimalistic-crypto-utils: 1.0.1
+ optional: true
+
hoist-non-react-statics@3.3.2:
dependencies:
react-is: 16.13.1
@@ -9768,6 +13296,10 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
+ idb-keyval@6.2.1: {}
+
+ idb-keyval@6.2.2: {}
+
idb@8.0.3: {}
ieee754@1.2.1: {}
@@ -9806,6 +13338,8 @@ snapshots:
ipaddr.js@2.2.0: {}
+ iron-webcrypto@1.2.1: {}
+
is-alphabetical@2.0.1: {}
is-alphanumerical@2.0.1:
@@ -9813,6 +13347,11 @@ snapshots:
is-alphabetical: 2.0.1
is-decimal: 2.0.1
+ is-arguments@1.2.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
is-arrayish@0.2.1: {}
is-arrayish@0.3.2: {}
@@ -9821,6 +13360,8 @@ snapshots:
dependencies:
binary-extensions: 2.3.0
+ is-callable@1.2.7: {}
+
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
@@ -9837,6 +13378,13 @@ snapshots:
is-fullwidth-code-point@3.0.0: {}
+ is-generator-function@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
@@ -9853,10 +13401,35 @@ snapshots:
is-promise@4.0.0: {}
+ is-regex@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ is-stream@2.0.1: {}
+
+ is-typed-array@1.1.15:
+ dependencies:
+ which-typed-array: 1.1.19
+
+ isarray@1.0.0: {}
+
+ isarray@2.0.5: {}
+
isexe@2.0.0: {}
isobject@3.0.1: {}
+ isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)):
+ dependencies:
+ ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
+ isows@1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)):
+ dependencies:
+ ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
jackspeak@3.4.3:
dependencies:
'@isaacs/cliui': 8.0.2
@@ -9879,6 +13452,13 @@ snapshots:
json-parse-even-better-errors@2.3.1: {}
+ json-rpc-engine@6.1.0:
+ dependencies:
+ '@metamask/safe-event-emitter': 2.0.0
+ eth-rpc-errors: 4.0.3
+
+ json-rpc-random-id@1.0.1: {}
+
json-schema-compare@0.2.2:
dependencies:
lodash: 4.17.21
@@ -9915,10 +13495,18 @@ snapshots:
dependencies:
commander: 8.3.0
+ keccak@3.0.4:
+ dependencies:
+ node-addon-api: 2.0.2
+ node-gyp-build: 4.8.4
+ readable-stream: 3.6.2
+
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
+ keyvaluestorage-interface@1.0.0: {}
+
khroma@2.1.0: {}
kolorist@1.8.0: {}
@@ -9988,6 +13576,10 @@ snapshots:
pkg-types: 2.2.0
quansync: 0.2.10
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
locate-path@6.0.0:
dependencies:
p-locate: 5.0.0
@@ -10051,6 +13643,13 @@ snapshots:
math-intrinsics@1.1.0: {}
+ md5.js@1.3.5:
+ dependencies:
+ hash-base: 3.1.0
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+ optional: true
+
mdast-util-find-and-replace@3.0.2:
dependencies:
'@types/mdast': 4.0.4
@@ -10271,6 +13870,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ micro-ftch@0.3.1: {}
+
micromark-core-commonmark@2.0.3:
dependencies:
decode-named-character-reference: 1.2.0
@@ -10558,6 +14159,12 @@ snapshots:
mimic-response@3.1.0: {}
+ minimalistic-assert@1.0.1:
+ optional: true
+
+ minimalistic-crypto-utils@1.0.1:
+ optional: true
+
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.12
@@ -10570,6 +14177,10 @@ snapshots:
minipass@7.1.2: {}
+ mipd@0.0.7(typescript@5.8.3):
+ optionalDependencies:
+ typescript: 5.8.3
+
mixin-deep@1.3.2:
dependencies:
for-in: 1.0.2
@@ -10600,6 +14211,9 @@ snapshots:
object-assign: 4.1.1
thenify-all: 1.6.0
+ nan@2.23.0:
+ optional: true
+
nanoid@3.3.11: {}
nanoid@5.1.5: {}
@@ -10619,12 +14233,23 @@ snapshots:
dependencies:
semver: 7.7.2
+ node-addon-api@2.0.2: {}
+
+ node-addon-api@5.1.0:
+ optional: true
+
node-addon-api@6.1.0: {}
+ node-fetch-native@1.6.7: {}
+
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
+ node-gyp-build@4.8.4: {}
+
+ node-mock-http@1.0.2: {}
+
node-releases@2.0.19: {}
normalize-path@3.0.0: {}
@@ -10637,14 +14262,28 @@ snapshots:
numeral@2.0.6: {}
+ obj-multiplex@1.0.0:
+ dependencies:
+ end-of-stream: 1.4.5
+ once: 1.4.0
+ readable-stream: 2.3.8
+
object-assign@4.1.1: {}
object-hash@3.0.0: {}
object-inspect@1.13.4: {}
+ ofetch@1.4.1:
+ dependencies:
+ destr: 2.0.5
+ node-fetch-native: 1.6.7
+ ufo: 1.6.1
+
on-change@4.0.2: {}
+ on-exit-leak-free@0.2.0: {}
+
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
@@ -10672,34 +14311,133 @@ snapshots:
onnxruntime-common: 1.14.0
optional: true
- onnxruntime-web@1.14.0:
+ onnxruntime-web@1.14.0:
+ dependencies:
+ flatbuffers: 1.12.0
+ guid-typescript: 1.0.9
+ long: 4.0.0
+ onnx-proto: 4.0.4
+ onnxruntime-common: 1.14.0
+ platform: 1.3.6
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ orderedmap@2.1.1: {}
+
+ ox@0.6.7(typescript@5.8.3)(zod@3.25.67):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - zod
+
+ ox@0.6.9(typescript@5.8.3)(zod@3.25.67):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - zod
+
+ ox@0.7.1(typescript@5.8.3)(zod@3.25.67):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.0
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - zod
+
+ ox@0.8.1(typescript@5.8.3)(zod@3.25.67):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.0
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - zod
+ optional: true
+
+ ox@0.8.6(typescript@5.8.3)(zod@3.22.4):
+ dependencies:
+ '@adraffy/ens-normalize': 1.11.0
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - zod
+
+ ox@0.8.6(typescript@5.8.3)(zod@3.25.67):
dependencies:
- flatbuffers: 1.12.0
- guid-typescript: 1.0.9
- long: 4.0.0
- onnx-proto: 4.0.4
- onnxruntime-common: 1.14.0
- platform: 1.3.6
+ '@adraffy/ens-normalize': 1.11.0
+ '@noble/ciphers': 1.3.0
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ eventemitter3: 5.0.1
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - zod
- optionator@0.9.4:
+ p-limit@2.3.0:
dependencies:
- deep-is: 0.1.4
- fast-levenshtein: 2.0.6
- levn: 0.4.1
- prelude-ls: 1.2.1
- type-check: 0.4.0
- word-wrap: 1.2.5
-
- orderedmap@2.1.1: {}
+ p-try: 2.2.0
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
p-locate@5.0.0:
dependencies:
p-limit: 3.1.0
+ p-try@2.2.0: {}
+
package-json-from-dist@1.0.1: {}
package-manager-detector@1.3.0: {}
@@ -10771,6 +14509,31 @@ snapshots:
pify@2.3.0: {}
+ pify@3.0.0: {}
+
+ pify@5.0.0: {}
+
+ pino-abstract-transport@0.5.0:
+ dependencies:
+ duplexify: 4.1.3
+ split2: 4.2.0
+
+ pino-std-serializers@4.0.0: {}
+
+ pino@7.11.0:
+ dependencies:
+ atomic-sleep: 1.0.0
+ fast-redact: 3.5.0
+ on-exit-leak-free: 0.2.0
+ pino-abstract-transport: 0.5.0
+ pino-std-serializers: 4.0.0
+ process-warning: 1.0.0
+ quick-format-unescaped: 4.0.4
+ real-require: 0.1.0
+ safe-stable-stringify: 2.5.0
+ sonic-boom: 2.8.0
+ thread-stream: 0.15.2
+
pirates@4.0.7: {}
pkce-challenge@5.0.0: {}
@@ -10789,6 +14552,8 @@ snapshots:
platform@1.3.6: {}
+ pngjs@5.0.0: {}
+
points-on-curve@0.2.0: {}
points-on-path@0.2.1:
@@ -10800,6 +14565,10 @@ snapshots:
dependencies:
'@babel/runtime': 7.27.6
+ pony-cause@2.1.11: {}
+
+ possible-typed-array-names@1.1.0: {}
+
postcss-import@15.1.0(postcss@8.5.6):
dependencies:
postcss: 8.5.6
@@ -10842,6 +14611,10 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ preact@10.24.2: {}
+
+ preact@10.27.0: {}
+
prebuild-install@7.1.3:
dependencies:
detect-libc: 2.0.4
@@ -10859,6 +14632,10 @@ snapshots:
prelude-ls@1.2.1: {}
+ process-nextick-args@2.0.1: {}
+
+ process-warning@1.0.0: {}
+
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
@@ -10981,6 +14758,10 @@ snapshots:
forwarded: 0.2.0
ipaddr.js: 1.9.1
+ proxy-compare@2.6.0: {}
+
+ proxy-compare@3.0.1: {}
+
pump@3.0.3:
dependencies:
end-of-stream: 1.4.5
@@ -10990,12 +14771,26 @@ snapshots:
punycode@2.3.1: {}
+ qrcode@1.5.3:
+ dependencies:
+ dijkstrajs: 1.0.3
+ encode-utf8: 1.0.3
+ pngjs: 5.0.0
+ yargs: 15.4.1
+
qs@6.14.0:
dependencies:
side-channel: 1.1.0
quansync@0.2.10: {}
+ query-string@7.1.3:
+ dependencies:
+ decode-uri-component: 0.2.2
+ filter-obj: 1.1.0
+ split-on-first: 1.1.0
+ strict-uri-encode: 2.0.0
+
query-string@9.2.2:
dependencies:
decode-uri-component: 0.4.1
@@ -11004,6 +14799,15 @@ snapshots:
queue-microtask@1.2.3: {}
+ quick-format-unescaped@4.0.4: {}
+
+ radix3@1.1.2: {}
+
+ randombytes@2.1.0:
+ dependencies:
+ safe-buffer: 5.2.1
+ optional: true
+
range-parser@1.2.1: {}
raw-body@3.0.0:
@@ -11072,7 +14876,7 @@ snapshots:
rc-dropdown@4.2.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.27.6
- '@rc-component/trigger': 2.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@rc-component/trigger': 2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
classnames: 2.5.1
rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
@@ -11125,11 +14929,11 @@ snapshots:
rc-mentions@2.20.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.27.6
- '@rc-component/trigger': 2.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@rc-component/trigger': 2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
classnames: 2.5.1
rc-input: 1.8.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-menu: 9.16.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- rc-textarea: 1.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ rc-textarea: 1.10.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
@@ -11182,7 +14986,7 @@ snapshots:
rc-picker@4.11.3(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.27.6
- '@rc-component/trigger': 2.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@rc-component/trigger': 2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
classnames: 2.5.1
rc-overflow: 1.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-resize-observer: 1.4.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -11230,7 +15034,7 @@ snapshots:
rc-select@14.16.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.27.6
- '@rc-component/trigger': 2.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@rc-component/trigger': 2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
classnames: 2.5.1
rc-motion: 2.9.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
rc-overflow: 1.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -11286,7 +15090,7 @@ snapshots:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
- rc-textarea@1.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ rc-textarea@1.10.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.27.6
classnames: 2.5.1
@@ -11299,7 +15103,7 @@ snapshots:
rc-tooltip@6.4.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.27.6
- '@rc-component/trigger': 2.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@rc-component/trigger': 2.3.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
classnames: 2.5.1
rc-util: 5.44.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
@@ -11558,6 +15362,16 @@ snapshots:
dependencies:
pify: 2.3.0
+ readable-stream@2.3.8:
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
@@ -11568,6 +15382,10 @@ snapshots:
dependencies:
picomatch: 2.3.1
+ readdirp@4.1.2: {}
+
+ real-require@0.1.0: {}
+
recharts@3.0.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-is@19.1.0)(react@19.1.0)(redux@5.0.1):
dependencies:
'@reduxjs/toolkit': 2.8.2(react-redux@9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1))(react@19.1.0)
@@ -11799,8 +15617,12 @@ snapshots:
dependencies:
ipaddr.js: 2.2.0
+ require-directory@2.1.1: {}
+
require-from-string@2.0.2: {}
+ require-main-filename@2.0.0: {}
+
reselect@5.1.1: {}
resize-observer-polyfill@1.5.1: {}
@@ -11817,6 +15639,12 @@ snapshots:
reusify@1.1.0: {}
+ ripemd160@2.0.2:
+ dependencies:
+ hash-base: 3.1.0
+ inherits: 2.0.4
+ optional: true
+
robust-predicates@3.0.2: {}
rollup@4.44.0:
@@ -11874,8 +15702,18 @@ snapshots:
dependencies:
tslib: 2.8.1
+ safe-buffer@5.1.2: {}
+
safe-buffer@5.2.1: {}
+ safe-regex-test@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-regex: 1.2.1
+
+ safe-stable-stringify@2.5.0: {}
+
safer-buffer@2.1.2: {}
scheduler@0.26.0: {}
@@ -11886,6 +15724,25 @@ snapshots:
dependencies:
compute-scroll-into-view: 3.1.1
+ secp256k1@3.8.1:
+ dependencies:
+ bindings: 1.5.0
+ bip66: 1.1.5
+ bn.js: 4.12.2
+ create-hash: 1.2.0
+ drbg.js: 1.0.1
+ elliptic: 6.6.1
+ nan: 2.23.0
+ safe-buffer: 5.2.1
+ optional: true
+
+ secp256k1@5.0.1:
+ dependencies:
+ elliptic: 6.6.1
+ node-addon-api: 5.1.0
+ node-gyp-build: 4.8.4
+ optional: true
+
secure-json-parse@2.7.0: {}
semver-compare@1.0.0: {}
@@ -11919,8 +15776,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ set-blocking@2.0.0: {}
+
set-cookie-parser@2.7.1: {}
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
set-value@2.0.1:
dependencies:
extend-shallow: 2.0.1
@@ -11930,6 +15798,12 @@ snapshots:
setprototypeof@1.2.0: {}
+ sha.js@2.4.12:
+ dependencies:
+ inherits: 2.0.4
+ safe-buffer: 5.2.1
+ to-buffer: 1.2.1
+
sharp@0.32.6:
dependencies:
color: 4.2.3
@@ -12002,6 +15876,28 @@ snapshots:
dependencies:
is-arrayish: 0.3.2
+ socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10):
+ dependencies:
+ '@socket.io/component-emitter': 3.1.2
+ debug: 4.3.7
+ engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ socket.io-parser: 4.2.4
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ socket.io-parser@4.2.4:
+ dependencies:
+ '@socket.io/component-emitter': 3.1.2
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
+ sonic-boom@2.8.0:
+ dependencies:
+ atomic-sleep: 1.0.0
+
sonner@2.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
@@ -12015,16 +15911,22 @@ snapshots:
space-separated-tokens@2.0.2: {}
+ split-on-first@1.1.0: {}
+
split-on-first@3.0.0: {}
split-string@3.1.0:
dependencies:
extend-shallow: 3.0.2
+ split2@4.2.0: {}
+
statuses@2.0.1: {}
statuses@2.0.2: {}
+ stream-shift@1.0.3: {}
+
streamx@2.22.1:
dependencies:
fast-fifo: 1.3.2
@@ -12032,6 +15934,8 @@ snapshots:
optionalDependencies:
bare-events: 2.5.4
+ strict-uri-encode@2.0.0: {}
+
string-convert@0.2.1: {}
string-width@4.2.3:
@@ -12046,6 +15950,10 @@ snapshots:
emoji-regex: 9.2.2
strip-ansi: 7.1.0
+ string_decoder@1.1.1:
+ dependencies:
+ safe-buffer: 5.1.2
+
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
@@ -12091,6 +15999,8 @@ snapshots:
pirates: 4.0.7
ts-interface-checker: 0.1.13
+ superstruct@1.0.4: {}
+
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
@@ -12181,6 +16091,10 @@ snapshots:
dependencies:
any-promise: 1.3.0
+ thread-stream@0.15.2:
+ dependencies:
+ real-require: 0.1.0
+
throttle-debounce@5.0.2: {}
throttleit@2.1.0: {}
@@ -12194,6 +16108,12 @@ snapshots:
fdir: 6.4.6(picomatch@4.0.2)
picomatch: 4.0.2
+ to-buffer@1.2.1:
+ dependencies:
+ isarray: 2.0.5
+ safe-buffer: 5.2.1
+ typed-array-buffer: 1.0.3
+
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@@ -12222,6 +16142,8 @@ snapshots:
ts-md5@1.3.1: {}
+ tslib@1.14.1: {}
+
tslib@2.6.2: {}
tslib@2.8.1: {}
@@ -12251,12 +16173,31 @@ snapshots:
media-typer: 1.1.0
mime-types: 3.0.1
+ typed-array-buffer@1.0.3:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-typed-array: 1.1.15
+
+ typeforce@1.18.0:
+ optional: true
+
typescript@5.8.3: {}
uc.micro@2.1.0: {}
ufo@1.6.1: {}
+ uint8arrays@3.1.0:
+ dependencies:
+ multiformats: 9.9.0
+
+ uint8arrays@3.1.1:
+ dependencies:
+ multiformats: 9.9.0
+
+ uncrypto@0.1.3: {}
+
undici-types@7.8.0: {}
undici@7.11.0: {}
@@ -12316,6 +16257,19 @@ snapshots:
unpipe@1.0.0: {}
+ unstorage@1.16.1(idb-keyval@6.2.2):
+ dependencies:
+ anymatch: 3.1.3
+ chokidar: 4.0.3
+ destr: 2.0.5
+ h3: 1.15.4
+ lru-cache: 10.4.3
+ node-fetch-native: 1.6.7
+ ofetch: 1.4.1
+ ufo: 1.6.1
+ optionalDependencies:
+ idb-keyval: 6.2.2
+
update-browserslist-db@1.1.3(browserslist@4.25.0):
dependencies:
browserslist: 4.25.0
@@ -12361,6 +16315,14 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.8
+ use-sync-external-store@1.2.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ use-sync-external-store@1.4.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
use-sync-external-store@1.5.0(react@19.1.0):
dependencies:
react: 19.1.0
@@ -12370,10 +16332,26 @@ snapshots:
lodash.debounce: 4.0.8
react: 19.1.0
+ utf-8-validate@5.0.10:
+ dependencies:
+ node-gyp-build: 4.8.4
+
util-deprecate@1.0.2: {}
+ util@0.12.5:
+ dependencies:
+ inherits: 2.0.4
+ is-arguments: 1.2.0
+ is-generator-function: 1.1.0
+ is-typed-array: 1.1.15
+ which-typed-array: 1.1.19
+
uuid@11.1.0: {}
+ uuid@8.3.2: {}
+
+ uuid@9.0.1: {}
+
v8n@1.5.1: {}
valibot@0.41.0(typescript@5.8.3):
@@ -12395,6 +16373,27 @@ snapshots:
validate.io-number@1.0.3: {}
+ valtio@1.13.2(@types/react@19.1.8)(react@19.1.0):
+ dependencies:
+ derive-valtio: 0.1.0(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))
+ proxy-compare: 2.6.0
+ use-sync-external-store: 1.2.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.8
+ react: 19.1.0
+
+ valtio@2.1.5(@types/react@19.1.8)(react@19.1.0):
+ dependencies:
+ proxy-compare: 3.0.1
+ optionalDependencies:
+ '@types/react': 19.1.8
+ react: 19.1.0
+
+ varuint-bitcoin@1.1.2:
+ dependencies:
+ safe-buffer: 5.2.1
+ optional: true
+
vary@1.1.2: {}
vaul@1.1.2(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
@@ -12438,6 +16437,92 @@ snapshots:
d3-time: 3.1.0
d3-timer: 3.0.1
+ viem@2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67):
+ dependencies:
+ '@noble/curves': 1.8.1
+ '@noble/hashes': 1.7.1
+ '@scure/bip32': 1.6.2
+ '@scure/bip39': 1.5.4
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ ox: 0.6.7(typescript@5.8.3)(zod@3.25.67)
+ ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ - zod
+
+ viem@2.31.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67):
+ dependencies:
+ '@noble/curves': 1.9.1
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ isows: 1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ ox: 0.7.1(typescript@5.8.3)(zod@3.25.67)
+ ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ - zod
+
+ viem@2.32.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67):
+ dependencies:
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ isows: 1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ ox: 0.8.1(typescript@5.8.3)(zod@3.25.67)
+ ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ - zod
+ optional: true
+
+ viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4):
+ dependencies:
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4)
+ isows: 1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ ox: 0.8.6(typescript@5.8.3)(zod@3.22.4)
+ ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ - zod
+
+ viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67):
+ dependencies:
+ '@noble/curves': 1.9.2
+ '@noble/hashes': 1.8.0
+ '@scure/bip32': 1.7.0
+ '@scure/bip39': 1.6.0
+ abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67)
+ isows: 1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ ox: 0.8.6(typescript@5.8.3)(zod@3.25.67)
+ ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ - zod
+
vite@6.3.5(@types/node@24.0.3)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0):
dependencies:
esbuild: 0.25.5
@@ -12472,8 +16557,48 @@ snapshots:
w3c-keyname@2.2.8: {}
+ wagmi@2.16.1(@tanstack/query-core@5.83.1)(@tanstack/react-query@5.84.1(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67):
+ dependencies:
+ '@tanstack/react-query': 5.84.1(react@19.1.0)
+ '@wagmi/connectors': 5.9.1(@types/react@19.1.8)(@wagmi/core@2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67)
+ '@wagmi/core': 2.18.1(@tanstack/query-core@5.83.1)(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))
+ react: 19.1.0
+ use-sync-external-store: 1.4.0(react@19.1.0)
+ viem: 2.33.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@tanstack/query-core'
+ - '@types/react'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - immer
+ - ioredis
+ - supports-color
+ - uploadthing
+ - utf-8-validate
+ - zod
+
web-namespaces@2.0.1: {}
+ webextension-polyfill@0.10.0: {}
+
webidl-conversions@3.0.1: {}
whatwg-encoding@3.1.1:
@@ -12487,12 +16612,35 @@ snapshots:
tr46: 0.0.3
webidl-conversions: 3.0.1
+ which-module@2.0.1: {}
+
+ which-typed-array@1.1.19:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.8
+ call-bound: 1.0.4
+ for-each: 0.3.5
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+
which@2.0.2:
dependencies:
isexe: 2.0.0
+ wif@2.0.6:
+ dependencies:
+ bs58check: 2.1.2
+ optional: true
+
word-wrap@1.2.5: {}
+ wrap-ansi@6.2.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
@@ -12507,29 +16655,90 @@ snapshots:
wrappy@1.0.2: {}
+ ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10):
+ optionalDependencies:
+ bufferutil: 4.0.9
+ utf-8-validate: 5.0.10
+
+ ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10):
+ optionalDependencies:
+ bufferutil: 4.0.9
+ utf-8-validate: 5.0.10
+
+ ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10):
+ optionalDependencies:
+ bufferutil: 4.0.9
+ utf-8-validate: 5.0.10
+
+ ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10):
+ optionalDependencies:
+ bufferutil: 4.0.9
+ utf-8-validate: 5.0.10
+
+ xmlhttprequest-ssl@2.1.2: {}
+
+ xtend@4.0.2: {}
+
+ y18n@4.0.3: {}
+
yallist@3.1.1: {}
yaml@1.10.2: {}
yaml@2.8.0: {}
+ yargs-parser@18.1.3:
+ dependencies:
+ camelcase: 5.3.1
+ decamelize: 1.2.0
+
+ yargs@15.4.1:
+ dependencies:
+ cliui: 6.0.0
+ decamelize: 1.2.0
+ find-up: 4.1.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ require-main-filename: 2.0.0
+ set-blocking: 2.0.0
+ string-width: 4.2.3
+ which-module: 2.0.1
+ y18n: 4.0.3
+ yargs-parser: 18.1.3
+
yocto-queue@0.1.0: {}
zod-to-json-schema@3.24.5(zod@3.25.67):
dependencies:
zod: 3.25.67
+ zod@3.22.4: {}
+
zod@3.25.67: {}
zustand@3.7.2(react@19.1.0):
optionalDependencies:
react: 19.1.0
- zustand@5.0.5(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)):
+ zustand@5.0.0(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)):
optionalDependencies:
'@types/react': 19.1.8
immer: 10.1.1
react: 19.1.0
- use-sync-external-store: 1.5.0(react@19.1.0)
+ use-sync-external-store: 1.4.0(react@19.1.0)
+
+ zustand@5.0.3(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)):
+ optionalDependencies:
+ '@types/react': 19.1.8
+ immer: 10.1.1
+ react: 19.1.0
+ use-sync-external-store: 1.4.0(react@19.1.0)
+
+ zustand@5.0.7(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)):
+ optionalDependencies:
+ '@types/react': 19.1.8
+ immer: 10.1.1
+ react: 19.1.0
+ use-sync-external-store: 1.4.0(react@19.1.0)
zwitch@2.0.4: {}
diff --git a/src/assets/logo-app-black.png b/src/assets/logo-app-black.png
new file mode 100644
index 00000000..476c878b
Binary files /dev/null and b/src/assets/logo-app-black.png differ
diff --git a/src/assets/logo-app-brand.png b/src/assets/logo-app-brand.png
new file mode 100644
index 00000000..367b01c4
Binary files /dev/null and b/src/assets/logo-app-brand.png differ
diff --git a/src/assets/logo-app-gradient-pink.png b/src/assets/logo-app-gradient-pink.png
new file mode 100644
index 00000000..f8f6f30f
Binary files /dev/null and b/src/assets/logo-app-gradient-pink.png differ
diff --git a/src/assets/logo-app-gradient.png b/src/assets/logo-app-gradient.png
new file mode 100644
index 00000000..5adbe214
Binary files /dev/null and b/src/assets/logo-app-gradient.png differ
diff --git a/src/assets/logo-app-white.png b/src/assets/logo-app-white.png
new file mode 100644
index 00000000..82c821db
Binary files /dev/null and b/src/assets/logo-app-white.png differ
diff --git a/src/assets/logo-basic-black.png b/src/assets/logo-basic-black.png
new file mode 100644
index 00000000..4c9e26ee
Binary files /dev/null and b/src/assets/logo-basic-black.png differ
diff --git a/src/assets/logo-basic-dark.png b/src/assets/logo-basic-dark.png
new file mode 100644
index 00000000..b9040f30
Binary files /dev/null and b/src/assets/logo-basic-dark.png differ
diff --git a/src/assets/logo-basic-gradient-pink.png b/src/assets/logo-basic-gradient-pink.png
new file mode 100644
index 00000000..5476a1e0
Binary files /dev/null and b/src/assets/logo-basic-gradient-pink.png differ
diff --git a/src/assets/logo-basic-gradient.png b/src/assets/logo-basic-gradient.png
new file mode 100644
index 00000000..17d922c1
Binary files /dev/null and b/src/assets/logo-basic-gradient.png differ
diff --git a/src/assets/logo-basic-solid.png b/src/assets/logo-basic-solid.png
new file mode 100644
index 00000000..50438af6
Binary files /dev/null and b/src/assets/logo-basic-solid.png differ
diff --git a/src/assets/logo-basic-white.png b/src/assets/logo-basic-white.png
new file mode 100644
index 00000000..a8f3d541
Binary files /dev/null and b/src/assets/logo-basic-white.png differ
diff --git a/src/assets/logo-basic.png b/src/assets/logo-basic.png
new file mode 100644
index 00000000..4470504a
Binary files /dev/null and b/src/assets/logo-basic.png differ
diff --git a/src/assets/logo-special-black.png b/src/assets/logo-special-black.png
new file mode 100644
index 00000000..2b321ba1
Binary files /dev/null and b/src/assets/logo-special-black.png differ
diff --git a/src/assets/logo-special-brand-dark.png b/src/assets/logo-special-brand-dark.png
new file mode 100644
index 00000000..573d0b6e
Binary files /dev/null and b/src/assets/logo-special-brand-dark.png differ
diff --git a/src/assets/logo-special-brand.png b/src/assets/logo-special-brand.png
new file mode 100644
index 00000000..6d1b2e8c
Binary files /dev/null and b/src/assets/logo-special-brand.png differ
diff --git a/src/assets/logo-special-gradient-pink.png b/src/assets/logo-special-gradient-pink.png
new file mode 100644
index 00000000..b2663d79
Binary files /dev/null and b/src/assets/logo-special-gradient-pink.png differ
diff --git a/src/assets/logo-special-gradient.png b/src/assets/logo-special-gradient.png
new file mode 100644
index 00000000..ac4265cc
Binary files /dev/null and b/src/assets/logo-special-gradient.png differ
diff --git a/src/assets/logo-special-solid.png b/src/assets/logo-special-solid.png
new file mode 100644
index 00000000..17ebfeae
Binary files /dev/null and b/src/assets/logo-special-solid.png differ
diff --git a/src/assets/logo-special-white.png b/src/assets/logo-special-white.png
new file mode 100644
index 00000000..8ade9d2c
Binary files /dev/null and b/src/assets/logo-special-white.png differ
diff --git a/src/assets/logo-vertical-black.png b/src/assets/logo-vertical-black.png
new file mode 100644
index 00000000..164fc127
Binary files /dev/null and b/src/assets/logo-vertical-black.png differ
diff --git a/src/assets/logo-vertical-brand-dark.png b/src/assets/logo-vertical-brand-dark.png
new file mode 100644
index 00000000..a484b74c
Binary files /dev/null and b/src/assets/logo-vertical-brand-dark.png differ
diff --git a/src/assets/logo-vertical-brand.png b/src/assets/logo-vertical-brand.png
new file mode 100644
index 00000000..09cae201
Binary files /dev/null and b/src/assets/logo-vertical-brand.png differ
diff --git a/src/assets/logo-vertical-gradient-pink.png b/src/assets/logo-vertical-gradient-pink.png
new file mode 100644
index 00000000..69f09a48
Binary files /dev/null and b/src/assets/logo-vertical-gradient-pink.png differ
diff --git a/src/assets/logo-vertical-gradient.png b/src/assets/logo-vertical-gradient.png
new file mode 100644
index 00000000..b598f0f7
Binary files /dev/null and b/src/assets/logo-vertical-gradient.png differ
diff --git a/src/assets/logo-vertical-inverse.png b/src/assets/logo-vertical-inverse.png
new file mode 100644
index 00000000..c867941a
Binary files /dev/null and b/src/assets/logo-vertical-inverse.png differ
diff --git a/src/assets/logo-vertical-solid.png b/src/assets/logo-vertical-solid.png
new file mode 100644
index 00000000..b19966a7
Binary files /dev/null and b/src/assets/logo-vertical-solid.png differ
diff --git a/src/caps/build.ts b/src/caps/build.ts
deleted file mode 100644
index 0c9602ca..00000000
--- a/src/caps/build.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import fs from 'node:fs';
-import path from 'node:path';
-import { fileURLToPath } from 'node:url';
-import yaml from 'js-yaml';
-
-const __filename = fileURLToPath(import.meta.url);
-const dirname = path.dirname(__filename);
-
-function findYamlFiles(dir: string): string[] {
- const files: string[] = [];
- const items = fs.readdirSync(dir);
-
- for (const item of items) {
- const fullPath = path.join(dir, item);
- const stat = fs.statSync(fullPath);
-
- if (stat.isDirectory()) {
- files.push(...findYamlFiles(fullPath));
- } else if (item.endsWith('.yaml') || item.endsWith('.yml')) {
- files.push(fullPath);
- }
- }
-
- return files;
-}
-
-function generateRandomData() {
- const authors = [
- 'AI Assistant Team',
- 'CodeCraft Team',
- 'Nuwa Labs',
- 'Developer Tools',
- 'AI Studio',
- ];
- const tags = [
- 'development',
- 'productivity',
- 'design',
- 'analytics',
- 'security',
- ];
- const versions = ['1.0.0', '1.1.0', '1.2.0', '2.0.0', '0.9.0'];
-
- return {
- downloads: Math.floor(Math.random() * 5000) + 100,
- version: versions[Math.floor(Math.random() * versions.length)],
- author: authors[Math.floor(Math.random() * authors.length)],
- tag: tags[Math.floor(Math.random() * tags.length)],
- size: (Math.random() * 5 + 0.5).toFixed(1),
- createdAt:
- Date.now() - Math.floor(Math.random() * 90 + 10) * 24 * 60 * 60 * 1000,
- updatedAt:
- Date.now() - Math.floor(Math.random() * 30 + 1) * 24 * 60 * 60 * 1000,
- };
-}
-
-function standardizeCapObject(yamlData: any, key: string, yamlPath: string) {
- const randomData = generateRandomData();
-
- return {
- id: yamlData.metadata?.id || `cap:${key.replace(/\//g, ':')}`,
- name:
- yamlData.metadata?.name ||
- yamlData.name ||
- key.split('/').pop()?.replace(/_/g, ' ') ||
- 'Unknown Cap',
- description:
- yamlData.metadata?.description ||
- yamlData.description ||
- 'A useful AI assistant capability',
- tag: randomData.tag,
- downloads: randomData.downloads,
- version: randomData.version,
- author: randomData.author,
- createdAt: randomData.createdAt,
- updatedAt: randomData.updatedAt,
- size: Number.parseFloat(randomData.size),
- yaml: yamlData,
- };
-}
-
-async function main() {
- const capsDir = path.join(dirname);
- const publicDir = path.join(dirname, '../..', 'public');
-
- if (!fs.existsSync(publicDir)) {
- fs.mkdirSync(publicDir, { recursive: true });
- }
-
- const yamlFiles = findYamlFiles(capsDir);
-
- console.log(`Found ${yamlFiles.length} YAML files:`);
-
- const allCaps: Record = {};
-
- for (const yamlFile of yamlFiles) {
- try {
- const content = fs.readFileSync(yamlFile, 'utf8');
- const json = yaml.load(content) as any;
-
- const relativePath = path.relative(capsDir, yamlFile);
- const fileName = path.basename(yamlFile, path.extname(yamlFile));
-
- const capName = json.metadata?.name || json.name || fileName;
- const key = capName.toLowerCase().replace(/\s+/g, '_');
-
- const standardizedCap = standardizeCapObject(json, key, relativePath);
- allCaps[key] = standardizedCap;
-
- console.log(
- `✓ Processed: ${relativePath} -> ${standardizedCap.name} (key: ${key})`,
- );
- } catch (error) {
- console.error(`✗ Error processing ${yamlFile}:`, error);
- }
- }
-
- const outputPath = path.join(publicDir, 'caps.json');
- fs.writeFileSync(outputPath, JSON.stringify(allCaps, null, 2), 'utf8');
-
- console.log(`\nBuild completed! Generated 1 JSON file: public/caps.json`);
- console.log(`Contains ${Object.keys(allCaps).length} caps`);
-}
-
-main().catch(console.error);
diff --git a/src/caps/cn/image_generation.yaml b/src/caps/cn/image_generation.yaml
deleted file mode 100644
index 6d38d824..00000000
--- a/src/caps/cn/image_generation.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# ========= Agent Capability Package =========
-metadata:
- id: did:nuwa:cap:image_generation@1.0.0
- name: "Image Generation"
- avatar: "1f5bc-fe0f"
- description: "You are a useful Assistant that won't refuse to draw picture, instead, you will output specific markdown code for picture description, but don't use code block."
- triggers:
- - {type: regex, value: "draw picture"}
- memory_scope: sc:images
- permissions:
- require: ["state.create", "state.update", "state.query"]
- llm_requirements: # Optional: Specify LLM dependencies
- model_family: ["gpt-4o-mini"] # Suggests compatibility with these model families
- min_context_window: 16000 # Example: requires at least 16k context window
- # Other potential fields: specific_model_uri, required_features: ["tool_use_json_mode"]
- signature: zDIDSig1xyz… # sha256 over whole file, signed by author DID key
-
-schema: |
- { "$schema":"https://json-schema.org/draft/2020-12/schema",
- "$id":"did:nuwa:state:note#v1",
- "type":"object",
- "properties":{
- "id":{"type":"string","format":"uuid"},
- "title":{"type":"string","x-crdt":"lww_register"},
- "body":{"type":"string","x-crdt":"rga_text"},
- "source_url":{"type":"string","format":"uri", "description":"Optional URL of the source webpage or image."},
- "tags":{"type":"array","items":{"type":"string"},"x-crdt":"grow_only_set"},
- "createdAt":{"type":"string","format":"date-time"},
- "updatedAt":{"type":"string","format":"date-time"}
- },
- "required":["id","title","body","createdAt","updatedAt"]
- }
-prompt: |
- "You are a useful Assistant that won't refuse to draw picture, instead, you will output specific markdown code for picture description, but don't use code block."
-
-Locales:
- - cn: system
- content: "You are a useful Assistant that won't refuse to draw picture, instead, you will output specific markdown code for picture description, but don't use code block."
- - jp: user
- content: "Can you draw some pictures for me?"
- - en: assistant
- content: "Sure, what do you want me to draw?"
diff --git a/src/caps/cn/note.yaml b/src/caps/cn/note.yaml
deleted file mode 100644
index f8160907..00000000
--- a/src/caps/cn/note.yaml
+++ /dev/null
@@ -1,93 +0,0 @@
-# ========= Agent Capability Package =========
-metadata:
- id: did:nuwa:cap:note@1.0.0
- name: "Note"
- description: "Create & manage personal notes, optionally fetching content from web pages or describing images."
- author:
- did: "did:nuwa:user:abc123" # Author's DID
- name: "John Doe" # Optional: human-readable name
- contact: "john@nuwalabs.io" # Optional: contact email
- created_at: "2024-01-15T10:00:00Z" # create timestamp
- triggers:
- - {type: regex, value: "记(.*)笔记|note|add note about"}
- memory_scope: sc:note
- permissions:
- require: ["state.create", "state.update", "state.query"]
- option: ["artifacts"]
- llm_requirements: # Optional: Specify LLM dependencies
- model_family: ["gpt-4o-mini"] # Suggests compatibility with these model families
- min_context_window: 16000 # Example: requires at least 16k context window
- # Other potential fields: specific_model_uri, required_features: ["tool_use_json_mode"]
- signature: zDIDSig1xyz… # sha256 over whole file, signed by author DID key
-
-schema: |
- { "$schema":"https://json-schema.org/draft/2020-12/schema",
- "$id":"did:nuwa:state:note#v1",
- "type":"object",
- "properties":{
- "id":{"type":"string","format":"uuid"},
- "title":{"type":"string","x-crdt":"lww_register"},
- "body":{"type":"string","x-crdt":"rga_text"},
- "source_url":{"type":"string","format":"uri", "description":"Optional URL of the source webpage or image."},
- "tags":{"type":"array","items":{"type":"string"},"x-crdt":"grow_only_set"},
- "createdAt":{"type":"string","format":"date-time"},
- "updatedAt":{"type":"string","format":"date-time"}
- },
- "required":["id","title","body","createdAt","updatedAt"]
- }
-
-prompt: |
- You are Note Assistant.
- Your primary goal is to create a well-structured note object.
- If the user provides a URL, consider using the `fetch_web_content` tool to get its content to include in the note body.
- If the user provides an image URL, consider using the `recognize_image_content` tool to get a description to include in the note body.
- After gathering all necessary information, transform it into a Note object that conforms to the schema.
- Then call `state.create` with:
- schema_uri = "did:nuwa:state:note#v1"
- object =
- If you use a tool like `fetch_web_content` or `recognize_image_content`, use its output to enrich the note's body.
- Always set the `source_url` field in the note object if the note is about a specific webpage or image.
- Reply only with the final `state.create` tool call, or an intermediate tool call if you need more information.
-
-tools:
- - type: function
- function:
- name: state.create # built-in tool
- description: Persist a new state object (a note).
- parameters:
- type: object
- properties:
- schema_uri: {type: string, enum: ["did:nuwa:state:note#v1"]}
- object: {$ref: "#/schema"}
- required: [schema_uri, object]
- - type: function
- function:
- name: fetch_web_content
- description: "Fetches the main textual content from a given web page URL. Useful for summarizing or taking notes about online articles."
- parameters:
- type: object
- properties:
- url: {type: string, format: uri, description: "The URL of the web page to fetch content from."}
- required: [url]
- - type: function
- function:
- name: recognize_image_content
- description: "Analyzes an image from a given URL and returns a textual description of its content. Useful for adding context about an image to a note."
- parameters:
- type: object
- properties:
- image_url: {type: string, format: uri, description: "The URL of the image to analyze."}
- required: [image_url]
-
-tool_bindings:
- "fetch_web_content":
- type: "mcp_service"
- service_uri: "did:nuwa:mcp:webscraper:version1" # Example MCP service URI
- mcp_action: "extract_text_content"
- # Arguments from LLM tool call (e.g., {url: "..."}) are passed as payload to MCP action.
- "recognize_image_content":
- type: "mcp_service"
- service_uri: "did:nuwa:mcp:visiondescribers:stable" # Example MCP service URI
- mcp_action: "describe_image_from_url"
- # Arguments from LLM tool call (e.g., {image_url: "..."}) are passed as payload.
-# ========= End of ACP =========
\ No newline at end of file
diff --git a/src/features/ai-chat/components/chat.tsx b/src/features/ai-chat/components/chat.tsx
deleted file mode 100644
index 881288cf..00000000
--- a/src/features/ai-chat/components/chat.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-'use client';
-
-import type { Attachment, UIMessage } from 'ai';
-import { useState } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { useChatDefault } from '@/features/ai-chat/hooks/use-chat-default';
-import Header from '@/layout/components/header';
-import { Messages } from './messages';
-import { MultimodalInput } from './multimodal-input';
-
-export function Chat({
- id,
- initialMessages,
- isReadonly,
-}: {
- id: string;
- initialMessages: Array;
- isReadonly: boolean;
-}) {
- const navigate = useNavigate();
-
- const handleOnResponse = (response: any) => {
- navigate(`/chat?cid=${id}`);
- };
-
- const {
- messages,
- setMessages: setChatMessages,
- handleSubmit,
- input,
- setInput,
- append,
- status,
- stop,
- reload,
- } = useChatDefault(id, initialMessages, handleOnResponse);
-
- const [attachments, setAttachments] = useState>([]);
-
- return (
-
- {/* Artifact viewer */}
-
- {/* Chat */}
-
-
-
-
-
-
-
- );
-}
diff --git a/src/features/ai-chat/components/index.ts b/src/features/ai-chat/components/index.ts
deleted file mode 100644
index 321999f0..00000000
--- a/src/features/ai-chat/components/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { Chat } from './chat';
-export { MemoryToolCall } from './memory-tool-call';
-export { Messages } from './messages';
-export { MultimodalInput } from './multimodal-input';
diff --git a/src/features/ai-chat/components/memory-tool-call.tsx b/src/features/ai-chat/components/memory-tool-call.tsx
deleted file mode 100644
index 75fd7bb3..00000000
--- a/src/features/ai-chat/components/memory-tool-call.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import type { ToolInvocation } from 'ai';
-import { Brain } from 'lucide-react';
-import { generateUUID } from '@/shared/utils';
-import { useDevMode } from '../../../shared/hooks/use-dev-mode';
-
-interface MemoryToolCallProps {
- toolInvocation: ToolInvocation;
-}
-
-export function MemoryToolCall({ toolInvocation }: MemoryToolCallProps) {
- const { toolName, state } = toolInvocation;
- const isDevMode = useDevMode();
-
- if (toolName === 'saveMemory') {
- const savedMemory = state === 'result' && toolInvocation.result.memory ? toolInvocation.result.memory : null;
- const reason = state === 'result' && toolInvocation.result.reason ? toolInvocation.result.reason : null;
- return (
-
-
-
- Saved memory
-
-
- {isDevMode && savedMemory && (
-
- {typeof savedMemory === 'object' ? (
-
- {JSON.stringify(savedMemory, null, 2)}
-
- ) : (
-
{String(savedMemory)}
- )}
-
- )}
-
- {isDevMode && reason && (
-
- Reason:
- {reason}
-
- )}
-
- );
- }
-
- if (toolName === 'queryMemory') {
- const memories = state === 'result' && Array.isArray(toolInvocation.result.memories) ? toolInvocation.result.memories : [];
- const reason = state === 'result' && toolInvocation.result.reason ? toolInvocation.result.reason : null;
-
- return (
-
-
-
-
- Searched memories
- {isDevMode && memories.length > 0 && (
- ({memories.length})
- )}
-
-
-
- {isDevMode && memories.length > 0 && (
-
- {memories.map((memory: any) => (
-
-
- {JSON.stringify(memory, null, 2)}
-
-
- ))}
-
- )}
-
- {isDevMode && reason && (
-
- Reason:
- {reason}
-
- )}
-
- );
- }
-
- return null;
-}
diff --git a/src/features/ai-chat/components/preview-attachment.tsx b/src/features/ai-chat/components/preview-attachment.tsx
deleted file mode 100644
index e75ccfe2..00000000
--- a/src/features/ai-chat/components/preview-attachment.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import type { Attachment } from 'ai';
-
-import { LoaderIcon } from 'lucide-react';
-
-export const PreviewAttachment = ({
- attachment,
- isUploading = false,
-}: {
- attachment: Attachment;
- isUploading?: boolean;
-}) => {
- const { name, url, contentType } = attachment;
-
- return (
-
-
- {contentType ? (
- contentType.startsWith('image') ? (
-

- ) : (
-
- )
- ) : (
-
- )}
-
- {isUploading && (
-
-
-
- )}
-
-
{name}
-
- );
-};
diff --git a/src/features/ai-chat/hooks/index.ts b/src/features/ai-chat/hooks/index.ts
deleted file mode 100644
index 3a5859ce..00000000
--- a/src/features/ai-chat/hooks/index.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export * from '../../settings/hooks/use-memory';
-export * from './use-chat-default';
-export * from './use-chat-page';
-export * from './use-chat-session';
-export * from './use-chat-sessions';
-export * from './use-chat-streams';
-export * from './use-file';
-export * from './use-files';
-export * from './use-messages';
-export * from './use-scroll-to-bottom';
diff --git a/src/features/ai-chat/hooks/use-chat-session.ts b/src/features/ai-chat/hooks/use-chat-session.ts
deleted file mode 100644
index 830fd19f..00000000
--- a/src/features/ai-chat/hooks/use-chat-session.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import type { Message } from 'ai';
-import { useCallback } from 'react';
-import { type ChatSession, ChatStateStore } from '@/features/ai-chat/stores';
-
-export const useChatSession = (sessionId: string) => {
- const store = ChatStateStore();
-
- const session = store.getSession(sessionId);
-
- const updateMessages = useCallback(
- (messages: Message[]) => {
- store.updateMessages(sessionId, messages);
- },
- [sessionId],
- );
-
- const updateSingleMessage = useCallback(
- (messageId: string, updates: Partial) => {
- const currentSession = store.getSession(sessionId);
- if (!currentSession) return;
-
- const updatedMessages = currentSession.messages.map((msg) =>
- msg.id === messageId ? { ...msg, ...updates } : msg,
- );
-
- const updatedSession: ChatSession = {
- ...currentSession,
- messages: updatedMessages,
- updatedAt: Date.now(),
- };
-
- store.updateSession(sessionId, updatedSession);
- },
- [sessionId, store],
- );
-
- const deleteMessage = useCallback(
- (messageId: string) => {
- const currentSession = store.getSession(sessionId);
- if (!currentSession) return;
-
- const updatedMessages = currentSession.messages.filter(
- (msg) => msg.id !== messageId,
- );
-
- const updatedSession: ChatSession = {
- ...currentSession,
- messages: updatedMessages,
- updatedAt: Date.now(),
- };
-
- store.updateSession(sessionId, updatedSession);
- },
- [sessionId, store],
- );
-
- const deleteMessagesAfterTimestamp = useCallback(
- (timestamp: number) => {
- const currentSession = store.getSession(sessionId);
- if (!currentSession) return;
-
- const updatedMessages = currentSession.messages.filter((msg) => {
- const messageTime = msg.createdAt
- ? new Date(msg.createdAt).getTime()
- : 0;
- return messageTime < timestamp;
- });
-
- const updatedSession: ChatSession = {
- ...currentSession,
- messages: updatedMessages,
- updatedAt: Date.now(),
- };
-
- store.updateSession(sessionId, updatedSession);
- },
- [sessionId, store],
- );
-
- const updateTitle = useCallback(async () => {
- await store.updateTitle(sessionId);
- }, [sessionId]);
-
- return {
- session,
- messages: store.getMessages(sessionId),
- updateMessages,
- updateSingleMessage,
- deleteMessage,
- deleteMessagesAfterTimestamp,
- updateTitle,
- };
-};
diff --git a/src/features/ai-chat/hooks/use-chat-sessions.ts b/src/features/ai-chat/hooks/use-chat-sessions.ts
deleted file mode 100644
index bf430477..00000000
--- a/src/features/ai-chat/hooks/use-chat-sessions.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { useCallback } from 'react';
-import { ChatStateStore } from '@/features/ai-chat/stores/chat-store';
-import type { ChatSession } from '@/features/ai-chat/types';
-
-export const useChatSessions = () => {
- const store = ChatStateStore();
-
- const deleteSession = useCallback((id: string) => {
- store.deleteSession(id);
- }, []);
-
- const updateSession = useCallback(
- (id: string, updates: Partial>) => {
- store.updateSession(id, updates);
- },
- [],
- );
-
- const clearAllSessions = useCallback(() => {
- store.clearAllSessions();
- }, []);
-
- const getSortedSessions = useCallback(() => {
- return Object.values(store.sessions).sort(
- (a, b) => b.updatedAt - a.updatedAt,
- );
- }, [store.sessions]);
-
- return {
- sessions: getSortedSessions(),
- sessionsMap: store.sessions,
- deleteSession,
- updateSession,
- clearAllSessions,
- };
-};
diff --git a/src/features/ai-chat/hooks/use-chat-streams.ts b/src/features/ai-chat/hooks/use-chat-streams.ts
deleted file mode 100644
index bbb97446..00000000
--- a/src/features/ai-chat/hooks/use-chat-streams.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// use-chat.ts (重构版本)
-// Enhanced hooks that use services for business logic
-'use client';
-
-import { useCallback } from 'react';
-import { ChatStateStore } from '@/features/ai-chat/stores';
-
-export const useChatStreams = () => {
- const store = ChatStateStore();
-
- const createStreamId = useCallback(
- async (streamId: string, chatId: string) => {
- await store.createStreamId(streamId, chatId);
- },
- [],
- );
-
- const getStreamIdsByChatId = useCallback(async (chatId: string) => {
- return await store.getStreamIdsByChatId(chatId);
- }, []);
-
- return {
- createStreamId,
- getStreamIdsByChatId,
- };
-};
diff --git a/src/features/ai-chat/hooks/use-file.ts b/src/features/ai-chat/hooks/use-file.ts
deleted file mode 100644
index 93c90c30..00000000
--- a/src/features/ai-chat/hooks/use-file.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-'use client';
-
-import { useCallback } from 'react';
-import { FileStateStore } from '@/features/ai-chat/stores';
-
-// Individual file hook
-export const useFile = (id: string) => {
- const store = FileStateStore();
- const file = store.getFile(id);
-
- const deleteFile = useCallback(async () => {
- await store.deleteFile(id);
- }, [id]);
-
- const getFileURL = useCallback(async () => {
- return await store.getFileURL(id);
- }, [id]);
-
- const getFileBlob = useCallback(async () => {
- return await store.getFileBlob(id);
- }, [id]);
-
- return {
- file,
- deleteFile,
- getFileURL,
- getFileBlob,
- };
-};
diff --git a/src/features/ai-chat/hooks/use-files.ts b/src/features/ai-chat/hooks/use-files.ts
deleted file mode 100644
index 02a5d969..00000000
--- a/src/features/ai-chat/hooks/use-files.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { useCallback } from 'react';
-import { FileStateStore } from '@/features/ai-chat/stores';
-import { useLanguage } from '@/shared/hooks';
-
-// File management hook
-export const useFiles = () => {
- const { t } = useLanguage();
- const store = FileStateStore();
-
- const storeFile = useCallback(async (file: File) => {
- return await store.storeFile(file);
- }, []);
-
- const deleteFile = useCallback(async (id: string) => {
- await store.deleteFile(id);
- }, []);
-
- const getFileURL = useCallback(async (id: string) => {
- return await store.getFileURL(id);
- }, []);
-
- const getFileBlob = useCallback(async (id: string) => {
- return await store.getFileBlob(id);
- }, []);
-
- const clearAllFiles = useCallback(async () => {
- await store.clearAllFiles();
- }, []);
-
- const validateFile = useCallback((file: File) => {
- return store.validateFile(file);
- }, []);
-
- const getFilesByType = useCallback((type: string) => {
- return store.getFilesByType(type);
- }, []);
-
- const uploadFile = useCallback(
- async (file: File) => {
- // validate file
- const validation = validateFile(file);
- if (!validation.valid) {
- throw new Error(validation.error || 'Invalid file');
- }
-
- // store file
- const storedFile = await storeFile(file);
-
- // get file url
- const url = await getFileURL(storedFile.id);
-
- if (!url) {
- throw new Error(t('upload.failedCreateUrl'));
- }
-
- return {
- url,
- name: storedFile.name,
- contentType: storedFile.type,
- };
- },
- [validateFile, storeFile, getFileURL, t],
- );
-
- return {
- files: store.getAllFiles(),
- filesMap: store.files,
- totalSize: store.getTotalSize(),
- storeFile,
- deleteFile,
- getFileURL,
- getFileBlob,
- clearAllFiles,
- validateFile,
- getFilesByType,
- getFile: store.getFile,
- uploadFile,
- };
-};
diff --git a/src/features/ai-chat/services/prompts/index.ts b/src/features/ai-chat/services/prompts/index.ts
deleted file mode 100644
index ad5d6924..00000000
--- a/src/features/ai-chat/services/prompts/index.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-export const artifactsPrompt = `
-## Artifacts
-Artifacts is a special user interface mode that helps users with writing, editing, and other content creation tasks. When artifact is open, it is on the right side of the screen, while the conversation is on the left side. When creating or updating documents, changes are reflected in real-time on the artifacts and visible to the user.
-
-DO NOT UPDATE DOCUMENTS IMMEDIATELY AFTER CREATING THEM. WAIT FOR USER FEEDBACK OR REQUEST TO UPDATE IT.
-
-This is a guide for using artifacts tools: \`createDocument\` and \`updateDocument\`, which render content on a artifacts beside the conversation.
-
-**When to use \`createDocument\`:**
-- For substantial content (>10 lines)
-- For content users will likely save/reuse (emails, essays, etc.)
-- When explicitly requested to create a document
-
-**When NOT to use \`createDocument\`:**
-- For informational/explanatory content
-- For conversational responses
-- When asked to keep it in chat
-
-**Using \`updateDocument\`:**
-- Default to full document rewrites for major changes
-- Use targeted updates only for specific, isolated changes
-- Follow user instructions for which parts to modify
-
-**When NOT to use \`updateDocument\`:**
-- Immediately after creating a document
-
-Do not update document right after creating it. Wait for user feedback or request to update it.
-`;
-
-const memoryPrompt = `
-## Memory Management
-You have memory management capabilities. You need to determine when to use memory functionality based on the following principles:
-
-### Memory Storage Triggers:
-1. **Important Personal Information**: User mentions names, preferences, goals, important dates, etc.
-2. **Continuous Tasks**: Cross-session projects, learning plans, long-term goals
-3. **Key Decisions**: User's important choices or decisions
-4. **Repeated Topics**: User discusses topics or interests multiple times
-5. **Error Corrections**: User corrects your understanding or provides clarification
-
-### Memory Retrieval Triggers:
-1. **Personalized Needs**: Need to provide suggestions based on user's historical preferences
-2. **Context Continuity**: Current conversation involves topics discussed previously
-3. **Task Continuation**: Continue tasks that were not completed previously
-4. **Avoid Repeated Questions**: Prevent asking known information
-
-### Situations When Memory is Not Needed:
-- General questions and factual queries
-- Temporary, one-time information
-- Too specific details
-
-During each interaction, first evaluate if memory retrieval is needed, and then evaluate if new information should be stored at the end of the conversation.
-Use the memory management silently. Do not tell the user that you are using memory management.
-`
-
-export const regularPrompt =
- 'You are a friendly assistant! Keep your responses concise and helpful.';
-
-export const systemPrompt = () => {
- return `${regularPrompt}`;
-};
-
-export const devModeSystemPrompt = () => {
- return `${regularPrompt}\n\n${memoryPrompt}\n\n${artifactsPrompt}`;
-};
diff --git a/src/features/ai-chat/services/tools/create-document.ts b/src/features/ai-chat/services/tools/create-document.ts
deleted file mode 100644
index 48ef4a7c..00000000
--- a/src/features/ai-chat/services/tools/create-document.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-import { tool } from 'ai';
-import { z } from 'zod';
-import { generateCodeContent } from '@/features/documents/artifacts/code';
-import { generateImageContent } from '@/features/documents/artifacts/image';
-import { generateSheetContent } from '@/features/documents/artifacts/sheet';
-import { generateTextContent } from '@/features/documents/artifacts/text';
-import { DocumentStateStore } from '@/features/documents/stores';
-import { generateUUID } from '@/shared/utils';
-
-// const artifactKinds = ['text', 'code', 'image', 'sheet'] as const;
-const artifactKinds = ['text'] as const;
-
-// generate function mapping
-const generators = {
- text: generateTextContent,
- code: generateCodeContent,
- sheet: generateSheetContent,
- image: generateImageContent,
-};
-
-export const createDocument = () =>
- tool({
- description:
- 'Create a document for a writing or content creation activities. This will generate content using AI and save it locally.',
- parameters: z.object({
- title: z.string(),
- kind: z.enum(artifactKinds),
- }),
- execute: async ({ title, kind }) => {
- const id = generateUUID();
- const { setCurrentDocument } = DocumentStateStore.getState();
-
- try {
- // create initial document
- const { createDocumentWithId, setDocumentContent } =
- DocumentStateStore.getState();
- createDocumentWithId(id, title, kind);
-
- // set artifact to streaming state
- setCurrentDocument((artifact) => ({
- ...artifact,
- documentId: id,
- title,
- kind,
- content: '',
- status: 'streaming',
- }));
-
- // get the corresponding generator
- const generator = generators[kind];
- if (!generator) {
- throw new Error(`No generator found for kind: ${kind}`);
- }
-
- let finalContent = '';
-
- // call the AI generate function, update artifact content in real time
- if (kind === 'text') {
- finalContent = await (generator as typeof generateTextContent)(
- title,
- (delta) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: artifact.content + delta,
- status: 'streaming',
- }));
- },
- );
- } else if (kind === 'code') {
- finalContent = await (generator as typeof generateCodeContent)(
- title,
- (delta) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: delta,
- status: 'streaming',
- }));
- },
- );
- } else if (kind === 'sheet') {
- finalContent = await (generator as typeof generateSheetContent)(
- title,
- (delta) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: delta,
- status: 'streaming',
- }));
- },
- );
- } else if (kind === 'image') {
- finalContent = await (generator as typeof generateImageContent)(
- title,
- (imageBase64) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: imageBase64,
- status: 'streaming',
- }));
- },
- );
- }
-
- // update document content and set artifact to idle state
- setDocumentContent(id, finalContent);
-
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: finalContent,
- status: 'idle',
- }));
-
- return {
- id,
- title,
- kind,
- content: finalContent,
- message: `A ${kind} document "${title}" has been created and saved locally.`,
- };
- } catch (error) {
- console.error('Failed to create document:', error);
- // delete failed document and reset artifact state
- DocumentStateStore.getState().deleteDocument(id);
- setCurrentDocument((artifact) => ({
- ...artifact,
- status: 'idle',
- }));
- throw error;
- }
- },
- });
diff --git a/src/features/ai-chat/services/tools/index.ts b/src/features/ai-chat/services/tools/index.ts
deleted file mode 100644
index 89208fe2..00000000
--- a/src/features/ai-chat/services/tools/index.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { ModelStateStore } from '@/features/ai-provider/stores';
-import { SettingsStateStore } from '@/features/settings/stores';
-import { createDocument } from '../tools/create-document';
-import { queryMemory, saveMemory } from '../tools/memory';
-import { updateDocument } from '../tools/update-document';
-
-const selectedModel = ModelStateStore.getState().selectedModel;
-const isDevMode = SettingsStateStore.getState().settings.devMode;
-
-const userModeTools = {
- // createDocument: createDocument(),
- // updateDocument: updateDocument(),
- // saveMemory: saveMemory(),
- // queryMemory: queryMemory(),
-};
-
-const devModeTools = {
- createDocument: createDocument(),
- updateDocument: updateDocument(),
- saveMemory: saveMemory(),
- queryMemory: queryMemory(),
-};
-
-export const modelSupportTools =
- selectedModel.supported_parameters.includes('tools');
-export const tools = isDevMode ? devModeTools : userModeTools;
diff --git a/src/features/ai-chat/services/tools/memory.ts b/src/features/ai-chat/services/tools/memory.ts
deleted file mode 100644
index 8f142d5c..00000000
--- a/src/features/ai-chat/services/tools/memory.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { tool } from 'ai';
-import { z } from 'zod';
-import { MemoryStateStore } from '../../stores/memory-store';
-
-export const saveMemory = () =>
- tool({
- description:
- 'Store a memory for later use. This can be used to remember important information or context. Alwasy use English for saving and retrieving memories.',
- parameters: z.object({
- memory: z.string().describe('the memory text to save'),
- reason: z.string().describe('the reason for saving the memory'),
- }),
- execute: async ({ memory,reason }) => {
- await MemoryStateStore.getState().saveMemory(memory);
- return {
- memory: memory,
- reason: reason,
- };
- },
- });
-
-export const queryMemory = () =>
- tool({
- description:
- 'Retrieve one or more memory about the user by providing a query and an amount. Alwasy use English for saving and retrieving memories.',
- parameters: z.object({
- query: z.string().describe('the query to search for in the memory'),
- amount: z
- .number()
- .default(1)
- .describe('the number of memories to retrieve'),
- reason: z.string().describe('the reason for retrieving the memory'),
- }),
- execute: async ({ query, amount,reason }) => {
- const memories = await MemoryStateStore.getState().queryMemories(query, {
- limit: amount,
- });
- return {
- memories: memories.map((m) => ({
- text: m.text,
- createdAt: m.createdAt,
- })),
- reason: reason,
- };
- },
- });
diff --git a/src/features/ai-chat/services/tools/update-document.ts b/src/features/ai-chat/services/tools/update-document.ts
deleted file mode 100644
index 533a90a6..00000000
--- a/src/features/ai-chat/services/tools/update-document.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import { tool } from 'ai';
-import { z } from 'zod';
-import { updateCodeContent } from '@/features/documents/artifacts/code';
-import { updateImageContent } from '@/features/documents/artifacts/image';
-import { updateSheetContent } from '@/features/documents/artifacts/sheet';
-import { updateTextContent } from '@/features/documents/artifacts/text';
-import { DocumentStateStore } from '@/features/documents/stores';
-
-// update function mapping
-const updaters = {
- text: updateTextContent,
- code: updateCodeContent,
- sheet: updateSheetContent,
- image: updateImageContent,
-};
-
-export const updateDocument = () =>
- tool({
- description: 'Update a document with the given description using AI.',
- parameters: z.object({
- id: z.string().describe('The ID of the document to update'),
- description: z
- .string()
- .describe('The description of changes that need to be made'),
- }),
- execute: async ({ id, description }) => {
- const { setCurrentDocument, getDocument, addNewVersionDocument } =
- DocumentStateStore.getState();
- try {
- // Get document from client store
- const document = getDocument(id);
-
- if (!document) {
- return {
- error: 'Document not found',
- };
- }
-
- // set artifact to streaming state
- setCurrentDocument((artifact) => ({
- ...artifact,
- documentId: id,
- title: document.title,
- kind: document.kind,
- content: document.content || '',
- status: 'streaming',
- }));
-
- // get the corresponding updater
- const updater = updaters[document.kind];
- if (!updater) {
- throw new Error(`No updater found for kind: ${document.kind}`);
- }
-
- let updatedContent = '';
-
- // call the AI update function, update artifact content in real time
- if (document.kind === 'text') {
- updatedContent = await (updater as typeof updateTextContent)(
- document.content || '',
- description,
- (delta) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: artifact.content + delta,
- status: 'streaming',
- }));
- },
- );
- } else if (document.kind === 'code') {
- updatedContent = await (updater as typeof updateCodeContent)(
- document.content || '',
- description,
- (delta) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: delta,
- status: 'streaming',
- }));
- },
- );
- } else if (document.kind === 'sheet') {
- updatedContent = await (updater as typeof updateSheetContent)(
- document.content || '',
- description,
- (delta) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: delta,
- status: 'streaming',
- }));
- },
- );
- } else if (document.kind === 'image') {
- // image update does not need current content
- updatedContent = await (updater as typeof updateImageContent)(
- description,
- (imageBase64) => {
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: imageBase64,
- status: 'streaming',
- }));
- },
- );
- }
-
- // update document content and set artifact to idle state
- // setDocumentContent(id, updatedContent);
- addNewVersionDocument(id, updatedContent);
-
- setCurrentDocument((artifact) => ({
- ...artifact,
- content: updatedContent,
- status: 'idle',
- }));
-
- return {
- id,
- title: document.title,
- kind: document.kind,
- content: updatedContent,
- message: `The ${document.kind} document "${document.title}" has been updated successfully.`,
- };
- } catch (error) {
- console.error('Failed to update document:', error);
- setCurrentDocument((artifact) => ({
- ...artifact,
- status: 'idle',
- }));
- throw error;
- }
- },
- });
diff --git a/src/features/ai-chat/stores/file-store.ts b/src/features/ai-chat/stores/file-store.ts
deleted file mode 100644
index 4bf786e1..00000000
--- a/src/features/ai-chat/stores/file-store.ts
+++ /dev/null
@@ -1,322 +0,0 @@
-// file-store.ts
-// Client-side file storage system using unified storage architecture
-import { create } from 'zustand';
-import { persist } from 'zustand/middleware';
-import { NuwaIdentityKit } from '@/features/auth/services';
-import { generateUUID } from '@/shared/utils';
-import { createPersistConfig, db } from '@/storage';
-
-// ================= Constants & Types ================= //
-
-// Supported file types
-export const SUPPORTED_FILE_TYPES = {
- IMAGE: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
- DOCUMENT: ['application/pdf', 'text/plain', 'application/msword'],
- ALL: [
- 'image/jpeg',
- 'image/png',
- 'image/gif',
- 'image/webp',
- 'application/pdf',
- 'text/plain',
- ],
-} as const;
-
-// Maximum file size (10MB)
-export const MAX_FILE_SIZE = 10 * 1024 * 1024;
-
-// get current DID
-const getCurrentDID = async () => {
- const { getDid } = await NuwaIdentityKit();
- return await getDid();
-};
-
-// ================= Interfaces ================= //
-
-// File metadata interface
-export interface StoredFile {
- id: string;
- name: string;
- type: string;
- size: number;
- uploadedAt: number;
-}
-
-// File data interface (including actual Blob data)
-export interface FileData {
- id: string;
- blob: Blob;
-}
-
-// File validation result interface
-interface ValidationResult {
- valid: boolean;
- error?: string;
-}
-
-// File store state interface
-interface FileStoreState {
- files: Record;
-
- // File validation
- validateFile: (file: File) => ValidationResult;
-
- // File management
- storeFile: (file: File) => Promise;
- getFile: (id: string) => StoredFile | null;
- getFileBlob: (id: string) => Promise;
- getFileURL: (id: string) => Promise;
- deleteFile: (id: string) => Promise;
-
- // File queries
- getAllFiles: () => StoredFile[];
- getFilesByType: (type: string) => StoredFile[];
- getTotalSize: () => number;
-
- // Cleanup operations
- clearAllFiles: () => Promise;
-
- // Data persistence
- loadFromDB: () => Promise;
- saveToDB: () => Promise;
-}
-
-// ================= Database Reference ================= //
-
-const fileDB = db;
-
-// ================= Persist Configuration ================= //
-
-const persistConfig = createPersistConfig({
- name: 'file-storage',
- getCurrentDID: getCurrentDID,
- partialize: (state) => ({
- files: state.files,
- }),
- onRehydrateStorage: () => (state?: FileStoreState) => {
- if (state) {
- state.loadFromDB();
- }
- },
-});
-
-// ================= Store Definition ================= //
-
-export const FileStateStore = create()(
- persist(
- (set, get) => ({
- // Store state
- files: {},
-
- // File validation
- validateFile: (file: File): ValidationResult => {
- // Check file size
- if (file.size > MAX_FILE_SIZE) {
- return {
- valid: false,
- error: `File size should be less than ${MAX_FILE_SIZE / 1024 / 1024}MB`,
- };
- }
-
- // Check file type
- if (!SUPPORTED_FILE_TYPES.ALL.includes(file.type as any)) {
- return {
- valid: false,
- error: `File type ${file.type} is not supported`,
- };
- }
-
- return { valid: true };
- },
-
- // File upload and management
- storeFile: async (file: File): Promise => {
- const validation = get().validateFile(file);
- if (!validation.valid) {
- throw new Error(validation.error);
- }
-
- const id = generateUUID();
- const storedFile: StoredFile = {
- id,
- name: file.name,
- type: file.type,
- size: file.size,
- uploadedAt: Date.now(),
- };
-
- try {
- // Save file data to IndexedDB
- await fileDB.fileData.add({
- id,
- blob: file,
- });
-
- // Save metadata to state
- set((state) => ({
- files: {
- ...state.files,
- [id]: storedFile,
- },
- }));
-
- // Save metadata to IndexedDB asynchronously
- get().saveToDB();
-
- return storedFile;
- } catch (error) {
- console.error('Failed to upload file:', error);
- throw new Error('Failed to save file');
- }
- },
-
- // File retrieval methods
- getFile: (id: string): StoredFile | null => {
- const { files } = get();
- return files[id] || null;
- },
-
- getFileBlob: async (id: string): Promise => {
- try {
- const fileData = await fileDB.fileData.get(id);
- return fileData?.blob || null;
- } catch (error) {
- console.error('Failed to get file blob:', error);
- return null;
- }
- },
-
- getFileURL: async (id: string): Promise => {
- try {
- const blob = await get().getFileBlob(id);
- if (blob) {
- return URL.createObjectURL(blob);
- }
- return null;
- } catch (error) {
- console.error('Failed to create file URL:', error);
- return null;
- }
- },
-
- // File deletion
- deleteFile: async (id: string): Promise => {
- try {
- // Delete file data from IndexedDB
- await fileDB.fileData.delete(id);
- await fileDB.files.delete(id);
-
- // Delete metadata from state
- set((state) => {
- const { [id]: deleted, ...restFiles } = state.files;
- return { files: restFiles };
- });
- } catch (error) {
- console.error('Failed to delete file:', error);
- throw new Error('Failed to delete file');
- }
- },
-
- // File listing and filtering
- getAllFiles: (): StoredFile[] => {
- const { files } = get();
- return Object.values(files).sort((a, b) => b.uploadedAt - a.uploadedAt);
- },
-
- getFilesByType: (type: string): StoredFile[] => {
- const { files } = get();
- return Object.values(files)
- .filter((file) => file.type.startsWith(type))
- .sort((a, b) => b.uploadedAt - a.uploadedAt);
- },
-
- // File queries
- getTotalSize: (): number => {
- const { files } = get();
- return Object.values(files).reduce(
- (total, file) => total + file.size,
- 0,
- );
- },
-
- // Cleanup operations
- clearAllFiles: async (): Promise => {
- try {
- await fileDB.fileData.clear();
- await fileDB.files.clear();
- set({ files: {} });
- } catch (error) {
- console.error('Failed to clear files:', error);
- throw new Error('Failed to clear files');
- }
- },
-
- // Data persistence
- loadFromDB: async () => {
- if (typeof window === 'undefined') return;
-
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
-
- const files = await fileDB.files
- .where('did')
- .equals(currentDID)
- .toArray();
-
- const filesMap: Record = {};
- files.forEach((file: StoredFile) => {
- filesMap[file.id] = file;
- });
-
- set((state) => ({
- files: { ...state.files, ...filesMap },
- }));
- } catch (error) {
- console.error('Failed to load from DB:', error);
- }
- },
-
- saveToDB: async () => {
- if (typeof window === 'undefined') return;
-
- try {
- const { files } = get();
- const filesToSave = Object.values(files);
-
- // Use bulkPut to efficiently update data
- await fileDB.files.bulkPut(filesToSave);
- } catch (error) {
- console.error('Failed to save to DB:', error);
- }
- },
- }),
- persistConfig,
- ),
-);
-
-// ================= Utility Functions ================= //
-
-// 工具函数:格式化文件大小
-export function formatFileSize(bytes: number): string {
- if (bytes === 0) return '0 Bytes';
-
- const k = 1024;
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
- const i = Math.floor(Math.log(bytes) / Math.log(k));
-
- return `${Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
-}
-
-// 工具函数:检查是否为图片文件
-export function isImageFile(file: StoredFile): boolean {
- return SUPPORTED_FILE_TYPES.IMAGE.includes(file.type as any);
-}
-
-// 工具函数:获取文件图标
-export function getFileIcon(file: StoredFile): string {
- if (isImageFile(file)) return '🖼️';
- if (file.type === 'application/pdf') return '📄';
- if (file.type.startsWith('text/')) return '📝';
- return '📎';
-}
diff --git a/src/features/ai-chat/stores/index.ts b/src/features/ai-chat/stores/index.ts
deleted file mode 100644
index a3722c08..00000000
--- a/src/features/ai-chat/stores/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-// Re-export types for convenience
-export type { ChatSession, StreamRecord } from '../types';
-export * from './chat-store';
-export * from './file-store';
-export * from './memory-store';
diff --git a/src/features/ai-chat/stores/memory-store.ts b/src/features/ai-chat/stores/memory-store.ts
deleted file mode 100644
index 2423be48..00000000
--- a/src/features/ai-chat/stores/memory-store.ts
+++ /dev/null
@@ -1,270 +0,0 @@
-// memory-store.ts
-// Store for managing memories with unified storage and vector search
-
-import {
- env,
- type FeatureExtractionPipeline,
- pipeline,
-} from '@xenova/transformers';
-import { create } from 'zustand';
-import { persist } from 'zustand/middleware';
-import { NuwaIdentityKit } from '@/features/auth/services';
-import { generateUUID } from '@/shared/utils';
-import { createPersistConfig, db } from '@/storage';
-
-env.allowLocalModels = false;
-
-// ================= Types ================= //
-
-interface Memory {
- id: string;
- text: string;
- vector: number[];
- createdAt: number;
- updatedAt: number;
- metadata?: Record;
-}
-
-interface QueryMemoryResult extends Memory {
- similarity: number;
-}
-
-interface QueryOptions {
- limit?: number;
-}
-
-// ================= Constants ================= //
-
-const defaultModel = 'Xenova/all-MiniLM-L6-v2';
-const pipePromise: Promise = pipeline(
- 'feature-extraction',
- defaultModel,
-);
-
-// ================= Helper Functions ================= //
-
-// get current DID
-const getCurrentDID = async () => {
- const { getDid } = await NuwaIdentityKit();
- return await getDid();
-};
-
-// Cosine similarity function
-const cosineSimilarity = (vecA: number[], vecB: number[]): number => {
- const dotProduct = vecA.reduce(
- (sum, val, index) => sum + val * vecB[index],
- 0,
- );
- const magnitudeA = Math.sqrt(vecA.reduce((sum, val) => sum + val * val, 0));
- const magnitudeB = Math.sqrt(vecB.reduce((sum, val) => sum + val * val, 0));
- return dotProduct / (magnitudeA * magnitudeB);
-};
-
-// Function to get embeddings from text using HuggingFace pipeline
-const getEmbeddingFromText = async (text: string): Promise => {
- const pipe = await pipePromise;
- const output = await pipe(text, {
- pooling: 'mean',
- normalize: true,
- });
- return Array.from(output.data);
-};
-
-// ================= Database Reference ================= //
-
-const memoryDB = db;
-
-// ================= Store Interface ================= //
-
-interface MemoryStoreState {
- memories: Record;
-
- // memory management
- saveMemory: (text: string, metadata?: Record) => Promise;
- queryMemories: (
- queryText: string,
- options?: QueryOptions,
- ) => Promise;
-
- // utility methods
- clearAllMemories: () => Promise;
- getMemoryCount: () => number;
- deleteMemory: (id: string) => Promise;
-
- // data persistence
- loadFromDB: () => Promise;
- saveToDB: () => Promise;
-}
-
-// ================= Persist Configuration ================= //
-
-const persistConfig = createPersistConfig({
- name: 'memory-storage',
- getCurrentDID: getCurrentDID,
- partialize: (state) => ({
- memories: state.memories,
- }),
- onRehydrateStorage: () => (state) => {
- if (state) {
- state.loadFromDB();
- }
- },
-});
-
-// ================= Store Factory ================= //
-
-export const MemoryStateStore = create()(
- persist(
- (set, get) => ({
- memories: {},
-
- saveMemory: async (text: string, metadata?: Record) => {
- try {
- // Generate embedding for the text
- const vector = await getEmbeddingFromText(text);
-
- const memory: Memory = {
- id: generateUUID(),
- text,
- vector,
- createdAt: Date.now(),
- updatedAt: Date.now(),
- metadata,
- };
-
- // Add to store
- set((state) => ({
- memories: {
- ...state.memories,
- [memory.id]: memory,
- },
- }));
-
- // Save to database
- await get().saveToDB();
-
- return memory.id;
- } catch (error) {
- console.error('Failed to save memory:', error);
- throw error;
- }
- },
-
- deleteMemory: async (id: string) => {
- // Remove from store
- set((state) => {
- const { [id]: deleted, ...restMemories } = state.memories;
- return {
- memories: restMemories,
- };
- });
-
- // Delete from database
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
-
- // 使用单个索引查询并过滤结果
- await memoryDB.memories
- .where('did').equals(currentDID)
- .and(item => item.id === id)
- .delete();
- } catch (error) {
- console.error('Failed to delete memory from DB:', error);
- }
- },
-
- queryMemories: async (
- queryText: string,
- { limit = 10 }: QueryOptions = {},
- ) => {
- try {
- // Get embedding for the query text
- const queryVector = await getEmbeddingFromText(queryText);
-
- const { memories } = get();
- const memoryList = Object.values(memories);
-
- // Calculate cosine similarity for each memory and sort by similarity
- const similarities: QueryMemoryResult[] = memoryList.map((memory) => {
- const similarity = cosineSimilarity(queryVector, memory.vector);
- return { ...memory, similarity };
- });
-
- // Sort by similarity (descending) and return top results
- similarities.sort((a, b) => b.similarity - a.similarity);
- return similarities.slice(0, limit);
- } catch (error) {
- console.error('Failed to query memories:', error);
- throw error;
- }
- },
-
- clearAllMemories: async () => {
- set({
- memories: {},
- });
-
- // Clear database
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
-
- await memoryDB.memories.where('did').equals(currentDID).delete();
- } catch (error) {
- console.error('Failed to clear memories from DB:', error);
- }
- },
-
- getMemoryCount: () => {
- const { memories } = get();
- return Object.keys(memories).length;
- },
-
- loadFromDB: async () => {
- if (typeof window === 'undefined') return;
-
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
-
- const memories = await memoryDB.memories
- .where('did')
- .equals(currentDID)
- .toArray();
-
- // Sort by updatedAt in descending order
- const sortedMemories = memories.sort(
- (a: Memory, b: Memory) => b.updatedAt - a.updatedAt,
- );
-
- const memoriesMap: Record = {};
- sortedMemories.forEach((memory: Memory) => {
- memoriesMap[memory.id] = memory;
- });
-
- set((state) => ({
- memories: { ...state.memories, ...memoriesMap },
- }));
- } catch (error) {
- console.error('Failed to load memories from DB:', error);
- }
- },
-
- saveToDB: async () => {
- if (typeof window === 'undefined') return;
-
- try {
- const { memories } = get();
- const memoriesToSave = Object.values(memories);
-
- // Use bulkPut to efficiently update data
- await memoryDB.memories.bulkPut(memoriesToSave);
- } catch (error) {
- console.error('Failed to save memories to DB:', error);
- }
- },
- }),
- persistConfig,
- ),
-);
diff --git a/src/features/ai-chat/utils/index.ts b/src/features/ai-chat/utils/index.ts
deleted file mode 100644
index f5455874..00000000
--- a/src/features/ai-chat/utils/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './message';
diff --git a/src/features/ai-provider/components/auto-settings.tsx b/src/features/ai-provider/components/auto-settings.tsx
deleted file mode 100644
index 7fc91536..00000000
--- a/src/features/ai-provider/components/auto-settings.tsx
+++ /dev/null
@@ -1,165 +0,0 @@
-import { Clock, DollarSign, Zap } from 'lucide-react';
-import type React from 'react';
-import { useId } from 'react';
-import { Input } from '@/shared/components/ui/input';
-import { Label } from '@/shared/components/ui/label';
-import { RadioGroup, RadioGroupItem } from '@/shared/components/ui/radio-group';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from '@/shared/components/ui/select';
-import { Switch } from '@/shared/components/ui/switch';
-import { useLocale } from '@/shared/locales/use-locale';
-import type { Model } from '../types';
-import type { Category } from '../utils';
-import { ModelCard } from './model-card';
-
-interface AutoSettingsProps {
- categories: Category[];
- autoFocusCategory: string | null;
- autoOptimizationMetric: 'speed' | 'cost' | 'latency';
- autoAdvancedSettings: boolean;
- recommendedModels: Model[];
- selectedModel: Model | null;
- onFocusCategoryChange: (category: string | null) => void;
- onOptimizationMetricChange: (metric: 'speed' | 'cost' | 'latency') => void;
- onAdvancedSettingsChange: (enabled: boolean) => void;
- onModelSelect: (model: Model) => void;
-}
-
-export const AutoSettings: React.FC = ({
- categories,
- autoFocusCategory,
- autoOptimizationMetric,
- autoAdvancedSettings,
- recommendedModels,
- selectedModel,
- onFocusCategoryChange,
- onOptimizationMetricChange,
- onAdvancedSettingsChange,
- onModelSelect,
-}) => {
- const { t } = useLocale();
- const speedId = useId();
- const costId = useId();
- const latencyId = useId();
- const advancedSettingsId = useId();
- const minContextId = useId();
-
- return (
-
-
-
- {t('aiProvider.autoSettings.focusCategory.title')}
-
-
-
-
-
-
- {t('aiProvider.autoSettings.optimizeFor.title')}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {autoAdvancedSettings && (
-
-
- {t('aiProvider.autoSettings.advancedSettings.description')}
-
-
-
-
-
-
- )}
-
-
- {t('aiProvider.autoSettings.recommendedModels')}
-
-
- {recommendedModels.map((model) => (
-
- ))}
-
-
- );
-};
diff --git a/src/features/ai-provider/components/category-grid.tsx b/src/features/ai-provider/components/category-grid.tsx
deleted file mode 100644
index a7fef07b..00000000
--- a/src/features/ai-provider/components/category-grid.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { motion } from 'framer-motion';
-import { Tag } from 'lucide-react';
-import type React from 'react';
-import { useLocale } from '@/shared/locales/use-locale';
-import type { Category } from '../utils';
-
-interface CategoryGridProps {
- categories: Category[];
- onCategorySelect: (categoryId: string) => void;
-}
-
-export const CategoryGrid: React.FC = ({
- categories,
- onCategorySelect,
-}) => {
- const { t } = useLocale();
-
- return (
-
- {categories.map((category) => (
-
onCategorySelect(category.id)}
- whileHover={{ scale: 1.02 }}
- whileTap={{ scale: 0.98 }}
- >
-
-
-
-
{category.name}
-
- {category.count} {t('aiProvider.category.models')}
-
-
-
-
- ))}
-
- );
-};
diff --git a/src/features/ai-provider/components/index.ts b/src/features/ai-provider/components/index.ts
deleted file mode 100644
index 5519fe30..00000000
--- a/src/features/ai-provider/components/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { ModelSelector } from './model-selector';
diff --git a/src/features/ai-provider/components/model-selector-dialog.tsx b/src/features/ai-provider/components/model-selector-dialog.tsx
deleted file mode 100644
index 40650b2b..00000000
--- a/src/features/ai-provider/components/model-selector-dialog.tsx
+++ /dev/null
@@ -1,380 +0,0 @@
-import {
- ArrowDownAZ,
- ArrowDownNarrowWide,
- ArrowDownWideNarrow,
- ArrowUpDown,
- ChevronRight,
- Search,
-} from 'lucide-react';
-import type React from 'react';
-import { useMemo, useState } from 'react';
-import {
- Dialog,
- DialogContent,
- DialogTrigger,
-} from '@/shared/components/ui/dialog';
-import { Input } from '@/shared/components/ui/input';
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
-} from '@/shared/components/ui/select';
-import { SidebarInset, SidebarProvider } from '@/shared/components/ui/sidebar';
-import { Skeleton } from '@/shared/components/ui/skeleton';
-import { useLocale } from '@/shared/locales/use-locale';
-import { useAvailableModels, useSelectedModel } from '../hooks';
-import { useFavoriteModels } from '../hooks/use-favorite-models';
-import type { Model } from '../types';
-import {
- generateCategoriesAndProviders,
- getModelCategory,
- getModelName,
-} from '../utils';
-import { AutoSettings } from './auto-settings';
-import { ModelCard } from './model-card';
-import { ModelSelectorSidebar } from './model-selector-sidebar';
-import { ProviderAvatar } from './provider-avatar';
-
-export const LLMModelSelector = ({ onClose }: { onClose: () => void }) => {
- const { t } = useLocale();
- const { models, loading, error } = useAvailableModels();
- const { selectedModel, setSelectedModel } = useSelectedModel();
- const { isFavorite } = useFavoriteModels();
-
- const [selectedTab, setSelectedTab] = useState('all');
- const [selectedProvider, setSelectedProvider] = useState(null);
- const [searchQuery, setSearchQuery] = useState('');
- const [sortBy, setSortBy] = useState<'name' | 'price-asc' | 'price-desc'>(
- 'name',
- );
-
- // Auto settings
- const [autoFocusCategory, setAutoFocusCategory] = useState(
- null,
- );
- const [autoOptimizationMetric, setAutoOptimizationMetric] = useState<
- 'speed' | 'cost' | 'latency'
- >('speed');
- const [autoAdvancedSettings, setAutoAdvancedSettings] = useState(false);
-
- // Generate categories and providers from models
- const { categories, providers } = useMemo(() => {
- if (!models) return { categories: [], providers: [] };
- return generateCategoriesAndProviders(models);
- }, [models]);
-
- const getFilteredModels = () => {
- if (!models) return [];
-
- let filtered = [...models];
-
- if (selectedTab === 'favorite') {
- filtered = filtered.filter((model) => isFavorite(model.id));
- } else if (selectedTab === 'provider' && selectedProvider) {
- filtered = filtered.filter((model) => {
- const modelProviderId = model.providerName
- .toLowerCase()
- .replace(/\s+/g, '');
- return modelProviderId === selectedProvider;
- });
- } else if (selectedTab === 'auto') {
- if (autoFocusCategory) {
- filtered = filtered.filter(
- (model) => getModelCategory(model) === autoFocusCategory,
- );
- }
-
- if (autoOptimizationMetric === 'cost') {
- filtered.sort((a, b) => {
- const aPrice =
- a.pricing.input_per_million_tokens +
- a.pricing.output_per_million_tokens;
- const bPrice =
- b.pricing.input_per_million_tokens +
- b.pricing.output_per_million_tokens;
- return aPrice - bPrice;
- });
- }
- }
-
- if (searchQuery) {
- const keywords = searchQuery
- .trim()
- .split(/\s+/)
- .filter((keyword) => keyword.length > 0);
-
- filtered = filtered.filter((model) => {
- const modelName = model.name.toLowerCase();
- const providerName = model.providerName.toLowerCase();
- return keywords.every(
- (keyword) =>
- modelName.includes(keyword.toLowerCase()) ||
- providerName.includes(keyword.toLowerCase()),
- );
- });
- }
-
- // Apply sorting
- if (selectedTab !== 'auto') {
- filtered.sort((a, b) => {
- switch (sortBy) {
- case 'name':
- return a.name.localeCompare(b.name);
- case 'price-asc': {
- const aPrice =
- a.pricing.input_per_million_tokens +
- a.pricing.output_per_million_tokens;
- const bPrice =
- b.pricing.input_per_million_tokens +
- b.pricing.output_per_million_tokens;
- return aPrice - bPrice;
- }
- case 'price-desc': {
- const aPrice =
- a.pricing.input_per_million_tokens +
- a.pricing.output_per_million_tokens;
- const bPrice =
- b.pricing.input_per_million_tokens +
- b.pricing.output_per_million_tokens;
- return bPrice - aPrice;
- }
- default:
- return 0;
- }
- });
- }
-
- return filtered;
- };
-
- const handleModelSelect = (model: Model) => {
- setSelectedModel(model);
- onClose();
- };
-
- const handleTabChange = (tab: string) => {
- setSelectedTab(tab);
- };
-
- const getMainContent = () => {
- if (selectedTab === 'provider' && selectedProvider) {
- const filteredModels = getFilteredModels();
- return renderModelGrid(filteredModels);
- }
-
- if (selectedTab === 'auto') {
- return (
-
- );
- }
-
- // Add empty state for favorite tab
- if (selectedTab === 'favorite' && getFilteredModels().length === 0) {
- return (
-
-
★
-
- {t('aiProvider.modelDialog.favoriteEmpty.title')}
-
-
- {t('aiProvider.modelDialog.favoriteEmpty.description')}
-
-
- );
- }
-
- const filteredModels = getFilteredModels();
- return renderModelGrid(filteredModels);
- };
-
- const renderModelGrid = (modelList: Model[]) => (
-
- {modelList.map((model) => (
-
- ))}
-
- );
-
- const getTitle = () => {
- if (selectedTab === 'auto')
- return t('aiProvider.modelDialog.title.autoSettings');
- if (selectedTab === 'favorite')
- return t('aiProvider.modelDialog.title.favoriteModels');
- if (selectedTab === 'provider') {
- if (selectedProvider) {
- const provider = providers.find((p) => p.id === selectedProvider);
- return t('aiProvider.modelDialog.title.providerModels', {
- provider: provider?.name,
- });
- }
- return t('aiProvider.modelDialog.title.providers');
- }
- return t('aiProvider.modelDialog.title.allModels');
- };
-
- if (loading) {
- return (
-
-
-
-
-
-
-
-
-
- {Array.from({ length: 6 }, () => (
-
- ))}
-
-
-
- );
- }
-
- if (error) {
- return (
-
-
-
- {t('aiProvider.modelDialog.error.failedToLoad')}
-
-
{error.message}
-
-
- );
- }
-
- if (!models || models.length === 0) {
- return (
-
-
- {t('aiProvider.modelDialog.error.noModelsAvailable')}
-
-
- );
- }
-
- return (
-
-
-
-
-
- {/* Header */}
-
-
{getTitle()}
-
-
- {selectedTab !== 'auto' && (
-
-
-
- setSearchQuery(e.target.value)}
- className="pl-10"
- />
-
-
-
- )}
- {/* Content */}
-
- {getMainContent()}
-
-
-
-
- );
-};
-
-export const ModelSelectorDialog: React.FC = () => {
- const { t } = useLocale();
- const { selectedModel } = useSelectedModel();
- const [open, setOpen] = useState(false);
-
- return (
-
- );
-};
diff --git a/src/features/ai-provider/components/model-selector.tsx b/src/features/ai-provider/components/model-selector.tsx
deleted file mode 100644
index 283a2a31..00000000
--- a/src/features/ai-provider/components/model-selector.tsx
+++ /dev/null
@@ -1,220 +0,0 @@
-import { CircleArrowOutUpRight, Globe, StarIcon } from 'lucide-react';
-import type React from 'react';
-import { useState } from 'react';
-import { Button } from '@/shared/components/ui/button';
-import { Dialog, DialogContent } from '@/shared/components/ui/dialog';
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from '@/shared/components/ui/dropdown-menu';
-import { Slider } from '@/shared/components/ui/slider';
-import { Switch } from '@/shared/components/ui/switch';
-import { useLocale } from '@/shared/locales/use-locale';
-import { cn } from '@/shared/utils';
-import { useSelectedModel } from '../hooks';
-import { useFavoriteModels } from '../hooks/use-favorite-models';
-import { useSelectAuto } from '../hooks/use-select-auto';
-import { useWebSearch } from '../hooks/use-web-search';
-import type { Model } from '../types';
-import { getModelName } from '../utils';
-import { LLMModelSelector } from './model-selector-dialog';
-import { ProviderAvatar } from './provider-avatar';
-
-interface ModelSelectorProps {
- className?: string;
-}
-
-export const ModelSelector: React.FC = ({
- className = '',
-}) => {
- const { t } = useLocale();
- const { selectedModel, setSelectedModel } = useSelectedModel();
- const { favoriteModels, toggleFavorite, isFavorite } = useFavoriteModels();
- const [open, setOpen] = useState(false);
- const [modelSelectorOpen, setModelSelectorOpen] = useState(false);
- const { SetModelAuto } = useSelectAuto();
- const {
- webSearchEnabled,
- webSearchContextSize,
- setWebSearchEnabled,
- setWebSearchContextSize,
- } = useWebSearch();
-
- const handleModelSelect = (model: Model) => {
- setSelectedModel(model);
- setOpen(false);
- };
-
- const handleMoreClick = () => {
- setOpen(false);
- setModelSelectorOpen(true);
- };
-
- const handleDirectOpen = () => {
- setModelSelectorOpen(true);
- };
-
- const handleWebSearchContextSizeChange = (value: number[]) => {
- setWebSearchContextSize(
- ['low', 'medium', 'high'][value[0]] as 'low' | 'medium' | 'high',
- );
- };
-
- return (
- <>
-
-
-
-
-
-
- {/* Auto option */}
-
-
-
-
- {t('aiProvider.modelSelector.auto')}
-
-
- {t('aiProvider.modelSelector.autoDescription')}
-
-
- {selectedModel?.id === 'auto' && (
-
- )}
-
-
- {
- e.preventDefault();
- setWebSearchEnabled(!webSearchEnabled);
- }}
- >
-
-
-
- {t('aiProvider.modelSelector.webSearch')}
-
-
-
- e.stopPropagation()}
- />
-
-
- {webSearchEnabled && (
-
-
-
- {t('aiProvider.modelSelector.contextSize.low')}
- {t('aiProvider.modelSelector.contextSize.medium')}
- {t('aiProvider.modelSelector.contextSize.high')}
-
-
- )}
-
-
- {favoriteModels.length > 0 && (
-
- {favoriteModels.map((model) => (
-
handleModelSelect(model)}
- >
-
-
-
- {getModelName(model)}
-
-
- {selectedModel?.id === model.id && (
-
- )}
-
-
- ))}
-
- )}
-
-
-
-
-
- {favoriteModels.length > 0
- ? t('aiProvider.modelSelector.moreModels')
- : t('aiProvider.modelSelector.allModels')}
-
-
-
-
-
-
-
- >
- );
-};
diff --git a/src/features/ai-provider/hooks/index.ts b/src/features/ai-provider/hooks/index.ts
deleted file mode 100644
index 84db49c3..00000000
--- a/src/features/ai-provider/hooks/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './use-available-models';
-export * from './use-favorite-models';
-export * from './use-selected-model';
-export * from './use-web-search';
diff --git a/src/features/ai-provider/hooks/use-favorite-models.ts b/src/features/ai-provider/hooks/use-favorite-models.ts
deleted file mode 100644
index 4cc253ee..00000000
--- a/src/features/ai-provider/hooks/use-favorite-models.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { ModelStateStore } from '../stores';
-import type { Model } from '../types';
-
-export function useFavoriteModels() {
- const favoriteModels = ModelStateStore((state) => state.favoriteModels);
- const addToFavorites = ModelStateStore((state) => state.addToFavorites);
- const removeFromFavorites = ModelStateStore(
- (state) => state.removeFromFavorites,
- );
- const isFavorite = (modelId: string) => {
- return favoriteModels.some((model) => model.id === modelId);
- };
-
- const toggleFavorite = (model: Model) => {
- if (isFavorite(model.id)) {
- removeFromFavorites(model.id);
- } else {
- addToFavorites(model);
- }
- };
-
- return {
- favoriteModels,
- addToFavorites,
- removeFromFavorites,
- isFavorite,
- toggleFavorite,
- };
-}
diff --git a/src/features/ai-provider/hooks/use-select-auto.ts b/src/features/ai-provider/hooks/use-select-auto.ts
deleted file mode 100644
index 33247ce1..00000000
--- a/src/features/ai-provider/hooks/use-select-auto.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { AUTO_MODEL } from '../stores';
-import { useSelectedModel } from './use-selected-model';
-
-export const useSelectAuto = () => {
- const { setSelectedModel } = useSelectedModel();
-
- const SetModelAuto = () => {
- setSelectedModel(AUTO_MODEL);
- };
-
- return {
- SetModelAuto,
- };
-};
diff --git a/src/features/ai-provider/hooks/use-web-search.ts b/src/features/ai-provider/hooks/use-web-search.ts
deleted file mode 100644
index b86266f8..00000000
--- a/src/features/ai-provider/hooks/use-web-search.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useCallback } from 'react';
-import { ModelStateStore } from '../stores';
-
-export const useWebSearch = () => {
- const webSearchEnabled = ModelStateStore((state) => state.webSearchEnabled);
- const setWebSearchEnabled = ModelStateStore(
- (state) => state.setWebSearchEnabled,
- );
- const webSearchContextSize = ModelStateStore(
- (state) => state.webSearchContextSize,
- );
- const setWebSearchContextSize = ModelStateStore(
- (state) => state.setWebSearchContextSize,
- );
-
- const toggleWebSearch = useCallback(() => {
- setWebSearchEnabled(!webSearchEnabled);
- }, [webSearchEnabled, setWebSearchEnabled]);
-
- return {
- webSearchEnabled,
- setWebSearchEnabled,
- toggleWebSearch,
- webSearchContextSize,
- setWebSearchContextSize,
- };
-};
diff --git a/src/features/ai-provider/services/index.ts b/src/features/ai-provider/services/index.ts
deleted file mode 100644
index c11cf857..00000000
--- a/src/features/ai-provider/services/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { llmProvider } from './provider';
diff --git a/src/features/ai-provider/services/models.ts b/src/features/ai-provider/services/models.ts
deleted file mode 100644
index 927267ca..00000000
--- a/src/features/ai-provider/services/models.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-// This file is kept for potential future model functionality
-// Currently no model selection is needed
-
-import { ModelStateStore } from '../stores';
-import type { Model, OpenRouterAPIResponse, OpenRouterModel } from '../types';
-import { createAuthorizedFetch } from './fetch';
-
-/**
- * Fetches the list of available models from OpenRouter API.
- * @returns {Promise} The list of available models.
- */
-async function fetchOpenRouterModels(): Promise {
- const authorizedFetch = createAuthorizedFetch();
- // TODO: change to nuwa endpoint - need to improve the speed of nuwa endpoint
- // const endpoint = 'https://test-llm.nuwa.dev/api/v1/models';
- const endpoint = 'https://openrouter.ai/api/v1/models';
-
- try {
- const response = await authorizedFetch(endpoint, {
- method: 'GET',
- });
- if (!response.ok) {
- throw new Error(
- `Failed to fetch models: ${response.status} ${response.statusText}`,
- );
- }
- return await response.json();
- } catch (error) {
- console.error('Error fetching available models:', error);
- throw error;
- }
-}
-
-function parseModelInfo(model: OpenRouterModel) {
- const id = model.id;
- const baseId = model.id.split(':')[0];
- const [providerSlug, ...slugParts] = baseId.split('/');
- const slug = slugParts.join('/');
-
- let providerName = '';
- let name = '';
- if (model.name.includes(':')) {
- [providerName, name] = model.name.split(':');
- providerName = providerName.trim();
- } else {
- name = model.name;
- providerName = providerSlug;
- }
-
- return {
- id,
- name,
- slug,
- providerName,
- providerSlug,
- };
-}
-
-export async function fetchAvailableModels(): Promise {
- const openRouterModels = await fetchOpenRouterModels();
-
- return openRouterModels.data
- .filter((model: OpenRouterModel) => !model.id.includes('openrouter')) // exclude openrouter models
- .map((model: OpenRouterModel) => {
- return {
- ...parseModelInfo(model),
- description: model.description,
- context_length: model.context_length,
- pricing: {
- input_per_million_tokens: parseFloat(model.pricing.prompt) * 1000000,
- output_per_million_tokens:
- parseFloat(model.pricing.completion) * 1000000,
- request_per_k_requests: parseFloat(model.pricing.request) * 1000,
- image_per_k_images: parseFloat(model.pricing.image) * 1000,
- web_search_per_k_searches:
- parseFloat(model.pricing.web_search) * 1000,
- },
- supported_inputs: model.architecture.input_modalities,
- supported_parameters: model.supported_parameters,
- };
- });
-}
-
-export const getModelSettings = (): {
- modelId: string;
- models: string[];
- web_search_options: Record | undefined;
- plugins: Record[] | undefined;
-} => {
- // get state from store
- const selectedModel = ModelStateStore.getState().selectedModel;
- const webSearchEnabled = ModelStateStore.getState().webSearchEnabled;
- const webSearchContextSize = ModelStateStore.getState().webSearchContextSize;
- const modelSupportWebSearch =
- selectedModel.supported_parameters.includes('web_search_options');
-
- // fallback models
- const models = ['openai/gpt-4o', 'anthropic/claude-sonnet-4'];
-
- // web search options
- const web_search_options =
- modelSupportWebSearch && webSearchEnabled
- ? {
- search_context_size: webSearchContextSize,
- }
- : undefined;
- const plugins =
- !modelSupportWebSearch && webSearchEnabled
- ? [
- {
- id: 'web',
- max_results:
- webSearchContextSize === 'low'
- ? 3
- : webSearchContextSize === 'medium'
- ? 5
- : 10,
- },
- ]
- : undefined;
-
- return {
- modelId: selectedModel.id,
- models,
- web_search_options,
- plugins,
- };
-};
diff --git a/src/features/ai-provider/services/provider.ts b/src/features/ai-provider/services/provider.ts
deleted file mode 100644
index 3b64af8b..00000000
--- a/src/features/ai-provider/services/provider.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { createOpenAI } from '@ai-sdk/openai';
-import { ModelStateStore } from '../stores';
-import { createAuthorizedFetch } from './fetch';
-import { getModelSettings } from './models';
-import { createOpenRouter } from './openrouter-provider';
-
-// Settings of Nuwa LLM Gateway
-const providerSettings = {
- apiKey: 'NOT-USED', // specify a fake api key to avoid provider errors
- baseURL: 'https://test-llm.nuwa.dev/api/v1',
- fetch: createAuthorizedFetch(),
-};
-
-const openrouter = createOpenRouter(providerSettings);
-const openai = createOpenAI(providerSettings);
-
-// Export a provider that dynamically resolves models
-export const llmProvider = {
- chat: () => {
- const { modelId, models, web_search_options, plugins } = getModelSettings();
- return openrouter.chat(modelId, {
- models,
- extraBody: {
- web_search_options,
- plugins,
- },
- });
- },
- artifact: () => {
- const selectedModel = ModelStateStore.getState().selectedModel;
- return openrouter.chat(selectedModel.id);
- },
- utility: () => {
- const selectedModel = ModelStateStore.getState().selectedModel;
- return openrouter.chat('openai/gpt-4o-mini');
- },
- image: () => openai.image('dall-e-3'),
-};
diff --git a/src/features/ai-provider/stores/index.ts b/src/features/ai-provider/stores/index.ts
deleted file mode 100644
index ccd5842c..00000000
--- a/src/features/ai-provider/stores/index.ts
+++ /dev/null
@@ -1,268 +0,0 @@
-// model-store.ts
-// Store for managing model selection and favorites
-
-import { create } from 'zustand';
-import { persist } from 'zustand/middleware';
-import { NuwaIdentityKit } from '@/features/auth/services';
-import { createPersistConfig, db } from '@/storage';
-import { fetchAvailableModels } from '../services/models';
-import type { Model } from '../types';
-
-export const AUTO_MODEL: Model = {
- id: 'openrouter/auto',
- name: 'Auto',
- slug: 'auto',
- providerName: 'Auto',
- providerSlug: 'auto',
- description: 'Automatically select the best model based on your needs.',
- context_length: 0,
- pricing: {
- input_per_million_tokens: 0,
- output_per_million_tokens: 0,
- request_per_k_requests: 0,
- image_per_k_images: 0,
- web_search_per_k_searches: 0,
- },
- supported_inputs: [],
- supported_parameters: [
- 'max_tokens',
- 'temperature',
- 'top_p',
- 'stop',
- 'frequency_penalty',
- 'presence_penalty',
- 'web_search_options',
- 'seed',
- 'logit_bias',
- 'logprobs',
- 'top_logprobs',
- 'response_format',
- 'structured_outputs',
- 'tools',
- 'tool_choice',
- ],
-};
-
-// get current DID
-const getCurrentDID = async () => {
- const { getDid } = await NuwaIdentityKit();
- return await getDid();
-};
-
-const modelDB = db;
-
-// model store state interface
-interface ModelStateStoreState {
- // model selection state
- selectedModel: Model;
- setSelectedModel: (model: Model) => void;
-
- // favorites state
- favoriteModels: Model[];
- addToFavorites: (model: Model) => void;
- removeFromFavorites: (modelId: string) => void;
-
- // web search state
- webSearchEnabled: boolean;
- webSearchContextSize: 'low' | 'medium' | 'high';
- setWebSearchEnabled: (enabled: boolean) => void;
- setWebSearchContextSize: (size: 'low' | 'medium' | 'high') => void;
-
- // available models state
- availableModels: Model[] | null;
- isLoadingModels: boolean;
- modelsError: Error | null;
- fetchAvailableModels: () => Promise;
- preloadModels: () => Promise;
- reloadModels: () => Promise;
-
- // data persistence
- loadFromDB: () => Promise;
- saveToDB: () => Promise;
-}
-
-// persist configuration
-const persistConfig = createPersistConfig({
- name: 'model-storage',
- getCurrentDID: getCurrentDID,
- partialize: (state) => ({
- selectedModel: state.selectedModel,
- favoriteModels: state.favoriteModels,
- webSearchEnabled: state.webSearchEnabled,
- webSearchContextSize: state.webSearchContextSize,
- }),
- onRehydrateStorage: () => (state) => {
- if (state) {
- state.loadFromDB();
- // preload models on store rehydration
- state.preloadModels();
- }
- },
-});
-
-// model store factory
-export const ModelStateStore = create()(
- persist(
- (set, get) => ({
- selectedModel: AUTO_MODEL,
- favoriteModels: [],
- webSearchEnabled: false,
- webSearchContextSize: 'low',
-
- // available models state
- availableModels: null,
- isLoadingModels: false,
- modelsError: null,
-
- setSelectedModel: (model: Model) => {
- set({ selectedModel: model });
- get().saveToDB();
- },
-
- addToFavorites: (model: Model) => {
- set((state) => {
- // avoid duplicates
- const isAlreadyFavorite = state.favoriteModels.some(
- (fav) => fav.id === model.id,
- );
- if (isAlreadyFavorite) return state;
-
- return {
- favoriteModels: [...state.favoriteModels, model],
- };
- });
- get().saveToDB();
- },
-
- removeFromFavorites: (modelId: string) => {
- set((state) => ({
- favoriteModels: state.favoriteModels.filter(
- (model) => model.id !== modelId,
- ),
- }));
- get().saveToDB();
- },
-
- setWebSearchEnabled: (enabled: boolean) => {
- set({ webSearchEnabled: enabled });
- get().saveToDB();
- },
-
- setWebSearchContextSize: (size: 'low' | 'medium' | 'high') => {
- set({ webSearchContextSize: size });
- get().saveToDB();
- },
-
- // fetch available models
- fetchAvailableModels: async () => {
- const { availableModels, isLoadingModels } = get();
-
- // if there is cached data and not loading, return
- if (availableModels && !isLoadingModels) {
- return;
- }
-
- // if loading, wait for completion
- if (isLoadingModels) {
- return;
- }
-
- set({ isLoadingModels: true, modelsError: null });
-
- try {
- const models = await fetchAvailableModels();
- set({
- availableModels: models,
- isLoadingModels: false,
- modelsError: null,
- });
- } catch (error) {
- set({
- modelsError: error as Error,
- isLoadingModels: false,
- });
- }
- },
-
- // preload models (silent loading, no UI state)
- preloadModels: async () => {
- const { availableModels } = get();
-
- // if there is cached data, return
- if (availableModels) {
- return;
- }
-
- try {
- const models = await fetchAvailableModels();
- set({
- availableModels: models,
- modelsError: null,
- });
- console.log('Models preloaded successfully');
- } catch (error) {
- console.warn('Failed to preload models:', error);
- set({ modelsError: error as Error });
- }
- },
-
- // reload models (force refresh)
- reloadModels: async () => {
- set({ isLoadingModels: true, modelsError: null });
-
- try {
- const models = await fetchAvailableModels();
- set({
- availableModels: models,
- isLoadingModels: false,
- modelsError: null,
- });
- } catch (error) {
- set({
- modelsError: error as Error,
- isLoadingModels: false,
- });
- }
- },
-
- loadFromDB: async () => {
- if (typeof window === 'undefined') return;
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
- const record = await modelDB.models
- .where('did')
- .equals(currentDID)
- .first();
- if (record) {
- set({
- selectedModel: record.selectedModel,
- favoriteModels: record.favoriteModels,
- webSearchEnabled: record.webSearchEnabled ?? false,
- });
- }
- } catch (error) {
- console.error('Failed to load model store from DB:', error);
- }
- },
-
- saveToDB: async () => {
- if (typeof window === 'undefined') return;
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
- const { selectedModel, favoriteModels, webSearchEnabled } = get();
- await modelDB.models.put({
- did: currentDID,
- selectedModel,
- favoriteModels,
- webSearchEnabled,
- });
- } catch (error) {
- console.error('Failed to save model store to DB:', error);
- }
- },
- }),
- persistConfig,
- ),
-);
diff --git a/src/features/ai-provider/utils/index.ts b/src/features/ai-provider/utils/index.ts
deleted file mode 100644
index 7bbbf081..00000000
--- a/src/features/ai-provider/utils/index.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-import type { Model } from '../types';
-
-export function isFreeModel(model: Model): boolean {
- const { pricing } = model;
- if (!pricing) return false;
- return (
- pricing.input_per_million_tokens === 0 &&
- pricing.output_per_million_tokens === 0 &&
- pricing.request_per_k_requests === 0
- );
-}
-
-export function getModelName(model: Model): string {
- return model.name;
-}
-
-export function getProviderName(model: Model): string {
- return model.providerName;
-}
-
-export function formatPricing(model: Model): string {
- const { pricing } = model;
- if (isFreeModel(model)) return 'Free';
-
- const inputPrice = pricing.input_per_million_tokens;
- const outputPrice = pricing.output_per_million_tokens;
-
- if (inputPrice === outputPrice) {
- return `$${inputPrice.toFixed(3)}`;
- }
-
- return `$${inputPrice.toFixed(3)} / $${outputPrice.toFixed(3)}`;
-}
-
-export function getModelSpeed(model: Model): string {
- if (model.context_length > 100000) return 'Medium';
- if (model.context_length > 32000) return 'Fast';
- return 'Very Fast';
-}
-
-export function getModelCategory(model: Model): string {
- const inputs = model.supported_inputs || [];
-
- if (inputs.includes('image')) {
- return 'multimodal';
- }
-
- if (
- model.name.toLowerCase().includes('code') ||
- model.name.toLowerCase().includes('coding')
- ) {
- return 'coding';
- }
-
- if (model.context_length > 32000) {
- return 'long-context';
- }
-
- return 'general';
-}
-
-export interface Category {
- id: string;
- name: string;
- count: number;
-}
-
-export interface Provider {
- id: string;
- name: string;
- count: number;
-}
-
-export function generateCategoriesAndProviders(models: Model[]): {
- categories: Category[];
- providers: Provider[];
-} {
- const categoryMap = new Map();
- const providerMap = new Map();
-
- models.forEach((model) => {
- const category = getModelCategory(model);
- const providerName = model.providerName;
- const providerId = providerName.toLowerCase().replace(/\s+/g, '');
-
- categoryMap.set(category, (categoryMap.get(category) || 0) + 1);
-
- const existingProvider = providerMap.get(providerId);
- if (existingProvider) {
- existingProvider.count += 1;
- } else {
- providerMap.set(providerId, { name: providerName, count: 1 });
- }
- });
-
- const categories: Category[] = Array.from(categoryMap.entries()).map(
- ([id, count]) => ({
- id,
- name: id.charAt(0).toUpperCase() + id.slice(1).replace('-', ' '),
- count,
- }),
- );
-
- const providers: Provider[] = Array.from(providerMap.entries())
- .map(([id, { name, count }]) => ({
- id,
- name,
- count,
- }))
- .sort((a, b) => b.count - a.count);
-
- return { categories, providers };
-}
diff --git a/src/features/auth/components/auth-guard.tsx b/src/features/auth/components/auth-guard.tsx
index 038deae1..e1668ddd 100644
--- a/src/features/auth/components/auth-guard.tsx
+++ b/src/features/auth/components/auth-guard.tsx
@@ -1,6 +1,8 @@
-import { createContext, type ReactNode, useEffect } from 'react';
+import { createContext, type ReactNode, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '@/features/auth/hooks/use-auth';
+import { ChatStateStore } from '@/features/chat/stores';
+import { SettingsStateStore } from '@/features/settings/stores';
type AuthGuardValue = ReturnType;
@@ -10,6 +12,8 @@ export function AuthGuard({ children }: { children: ReactNode }) {
const { did, isConnecting, isConnected, isError, isInitializing } = useAuth();
const navigate = useNavigate();
+ const hasRehydrated = useRef(false);
+
// Keep legacy DID store in sync so existing code relying on it continues to work.
useEffect(() => {
// Avoid redirect until SDK initialization completes
@@ -19,9 +23,21 @@ export function AuthGuard({ children }: { children: ReactNode }) {
if (!isConnecting && !isConnected) {
navigate('/login');
+ hasRehydrated.current = false; // Reset rehydration flag when user logs out
}
}, [isInitializing, isConnecting, isConnected, navigate]);
+ // Trigger store rehydration when user becomes connected
+ useEffect(() => {
+ if (isConnected && !hasRehydrated.current) {
+ hasRehydrated.current = true;
+
+ // Force rehydration of all stores
+ ChatStateStore.persist.rehydrate();
+ SettingsStateStore.persist.rehydrate();
+ }
+ }, [isConnected]);
+
return (
- {isConnecting ? 'Connecting…' : 'Sign-in with DID'}
-
+
+
+
+
+
);
}
diff --git a/src/features/auth/hooks/use-auth-handler.ts b/src/features/auth/hooks/use-auth-handler.ts
index e24f790a..6de52879 100644
--- a/src/features/auth/hooks/use-auth-handler.ts
+++ b/src/features/auth/hooks/use-auth-handler.ts
@@ -1,3 +1 @@
-'use client';
-
-export { NuwaIdentityKit as useAuthHandler } from '@/features/auth/services';
+export { NuwaIdentityKit as useAuthHandler } from '@/shared/services/identity-kit';
diff --git a/src/features/auth/hooks/use-auth.ts b/src/features/auth/hooks/use-auth.ts
index 4416152b..a5a0958a 100644
--- a/src/features/auth/hooks/use-auth.ts
+++ b/src/features/auth/hooks/use-auth.ts
@@ -1,5 +1,3 @@
-'use client';
-
import {
type UseIdentityKitOptions,
useIdentityKit,
diff --git a/src/features/auth/services/index.ts b/src/features/auth/services/index.ts
deleted file mode 100644
index 3db28f19..00000000
--- a/src/features/auth/services/index.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import {
- IdentityKitWeb,
- type UseIdentityKitOptions,
-} from '@nuwa-ai/identity-kit-web';
-
-export const NuwaIdentityKit = (options: UseIdentityKitOptions = {}) => {
- const handler = IdentityKitWeb.init({
- appName: 'Nuwa Assistant',
- storage: 'local',
- ...options,
- });
-
- const connect = async () => {
- await handler.then((handler) => handler.connect());
- };
-
- const logout = async () => {
- await handler.then((handler) => handler.logout());
- };
-
- const handleCallback = async (search: string) => {
- await handler.then((handler) => handler.handleCallback(search));
- };
-
- const getDid = async () => {
- return await handler.then((handler) => handler.getDid());
- };
-
- return {
- connect,
- logout,
- handleCallback,
- getDid,
- };
-};
diff --git a/src/features/cap-store/components/cap-card.tsx b/src/features/cap-store/components/cap-card.tsx
new file mode 100644
index 00000000..cf812f30
--- /dev/null
+++ b/src/features/cap-store/components/cap-card.tsx
@@ -0,0 +1,129 @@
+import { Loader2, Play, Settings, Trash2 } from 'lucide-react';
+import { useState } from 'react';
+import { toast } from 'sonner';
+import {
+ Button,
+ Card,
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from '@/shared/components/ui';
+import { useLanguage } from '@/shared/hooks/use-language';
+import type { Cap } from '@/shared/types/cap';
+import { useInstalledCap } from '../hooks/use-installed-cap';
+import { CapThumbnail } from './cap-thumbnail';
+
+export interface CapCardProps {
+ cap: Cap;
+ onRun?: (cap: Cap) => void;
+}
+
+export function CapCard({ cap, onRun }: CapCardProps) {
+ const { installCap, uninstallCap, updateInstalledCap, isInstalled } =
+ useInstalledCap(cap);
+ const [isLoading, setIsLoading] = useState(false);
+ const { t } = useLanguage();
+
+ const handleInstall = async () => {
+ setIsLoading(true);
+ try {
+ installCap(cap);
+ toast.success(`${cap.metadata.displayName} has been installed`);
+ } catch (error) {
+ toast.error(t('capStore.card.installFailed'));
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleUninstall = async () => {
+ setIsLoading(true);
+ try {
+ uninstallCap(cap.id);
+ toast.success(`${cap.metadata.displayName} has been uninstalled`);
+ } catch (error) {
+ toast.error(t('capStore.card.uninstallFailed'));
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleRun = () => {
+ onRun?.(cap);
+ };
+
+ return (
+
+
+
+
+
+
+ {cap.metadata.displayName}
+
+
+
+ {cap.metadata.description}
+
+
+ {/* Action buttons */}
+
+
+ {isInstalled ? (
+
+ ) : (
+ /* Install button */
+
+ )}
+
+ {isInstalled && (
+
+
+
+
+
+
+
+ Uninstall
+
+
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/features/cap-store/components/cap-selector.tsx b/src/features/cap-store/components/cap-selector.tsx
new file mode 100644
index 00000000..20c1cc7c
--- /dev/null
+++ b/src/features/cap-store/components/cap-selector.tsx
@@ -0,0 +1,71 @@
+import { AlertCircle, Loader2 } from 'lucide-react';
+import { useState } from 'react';
+import {
+ Button,
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from '@/shared/components/ui';
+import { useCurrentCap } from '@/shared/hooks';
+import type { Cap } from '@/shared/types';
+import { useRemoteCap } from '../hooks/use-remote-cap';
+import { CapStoreModal } from './cap-store-modal';
+import { CapThumbnail } from './cap-thumbnail';
+
+const CapInfo = ({ cap }: { cap: Cap }) => (
+ <>
+
+ {cap.metadata.displayName}
+ >
+);
+
+export function CapSelector() {
+ const {
+ currentCap,
+ isCurrentCapMCPInitialized,
+ isCurrentCapMCPError,
+ errorMessage,
+ } = useCurrentCap();
+ const [isModalOpen, setIsModalOpen] = useState(false);
+
+ // Prefetch remote caps when component loads
+ useRemoteCap();
+
+ return (
+
+
+ {isCurrentCapMCPError && (
+
+
+
+
+
+
+ {errorMessage ||
+ 'Cap Initialization Failed, Please Select Again or Check Network Connection'}
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/src/features/cap-store/components/cap-store-modal.tsx b/src/features/cap-store/components/cap-store-modal.tsx
new file mode 100644
index 00000000..1ec03f80
--- /dev/null
+++ b/src/features/cap-store/components/cap-store-modal.tsx
@@ -0,0 +1,342 @@
+import {
+ BookOpen,
+ Bot,
+ Code,
+ Coins,
+ Download,
+ Grid3X3,
+ Loader2,
+ MoreHorizontal,
+ Package,
+ PenTool,
+ RefreshCw,
+ Search,
+ Wrench,
+} from 'lucide-react';
+import { useEffect, useState } from 'react';
+import { toast } from 'sonner';
+import * as Dialog from '@/shared/components/ui';
+import { Button, Input } from '@/shared/components/ui';
+import { predefinedTags } from '@/shared/constants/cap';
+import { useCurrentCap, useLanguage } from '@/shared/hooks';
+import type { Cap } from '@/shared/types/cap';
+import { useRemoteCap } from '../hooks/use-remote-cap';
+import { CapStateStore } from '../stores';
+import { CapCard } from './cap-card';
+
+interface CapStoreModalProps {
+ open?: boolean;
+ onOpenChange?: (open: boolean) => void;
+ children?: React.ReactNode;
+}
+
+export function CapStoreModal({
+ open: externalOpen,
+ onOpenChange: externalOnOpenChange,
+ children,
+}: CapStoreModalProps) {
+ const { t } = useLanguage();
+ const [internalOpen, setInternalOpen] = useState(false);
+
+ // Use external or internal state management
+ const isControlled = externalOpen !== undefined;
+ const open = isControlled ? externalOpen : internalOpen;
+ const onOpenChange = isControlled ? externalOnOpenChange : setInternalOpen;
+
+ const [searchQuery, setSearchQuery] = useState('');
+ const [activeSection, setActiveSection] = useState('all');
+
+ // State for installed caps
+ const [installedCaps, setInstalledCaps] = useState([]);
+
+ const { remoteCaps, isLoading, error, refetch, lastSearchQuery } =
+ useRemoteCap();
+
+ const { setCurrentCap } = useCurrentCap();
+
+ // Subscribe to installed caps changes
+ useEffect(() => {
+ const updateInstalledCaps = () => {
+ const state = CapStateStore.getState();
+ const installed: Cap[] = Object.values(state.installedCaps);
+ setInstalledCaps(installed);
+ };
+
+ // Initial load
+ updateInstalledCaps();
+
+ // Subscribe to changes
+ const unsubscribe = CapStateStore.subscribe(updateInstalledCaps);
+
+ return unsubscribe;
+ }, []);
+
+ const sidebarSections = [
+ {
+ id: 'installed',
+ label: t('capStore.sidebar.installed') || 'Installed',
+ type: 'section',
+ },
+ {
+ id: 'all',
+ label: t('capStore.sidebar.all') || 'All Caps',
+ type: 'section',
+ },
+ { id: 'divider', label: '', type: 'divider' },
+ ...predefinedTags.map((tag) => ({
+ id: tag.toLowerCase().replace(/\s+/g, '-'),
+ label: tag,
+ type: 'tag' as const,
+ })),
+ ];
+
+ const handleRunCap = (cap: Cap) => {
+ // Set this cap as the current cap
+ setCurrentCap(cap);
+
+ onOpenChange?.(false);
+
+ toast.success(`${cap.metadata.displayName} has been selected`);
+ };
+
+ const handleSearch = () => {
+ refetch(searchQuery);
+ };
+
+ const handleSearchKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ handleSearch();
+ }
+ };
+
+ const renderContent = () => {
+ // Determine which caps to display based on active section
+ const getDisplayCaps = (): Cap[] => {
+ if (activeSection === 'installed') {
+ return installedCaps;
+ } else if (activeSection === 'all') {
+ return remoteCaps;
+ } else {
+ // Filter by tag
+ const tagName = activeSection.replace(/-/g, ' ').toLowerCase();
+ return remoteCaps.filter((cap) =>
+ cap.metadata.tags.some((tag) => tag.toLowerCase() === tagName),
+ );
+ }
+ };
+
+ const displayCaps = getDisplayCaps();
+ const isShowingInstalled = activeSection === 'installed';
+
+ if (error && !isShowingInstalled) {
+ return (
+
+
+
+ {t('capStore.status.error')}
+
+
+ {t('capStore.status.errorDesc')}
+
+
+
+ );
+ }
+
+ if (isLoading && !isShowingInstalled) {
+ return (
+
+
+
+ {t('capStore.status.loading')}
+
+
+ {t('capStore.status.fetching')}
+
+
+ );
+ }
+
+ if (displayCaps.length === 0) {
+ return (
+
+
+
+ {isShowingInstalled
+ ? t('capStore.status.noInstalledCaps') || 'No Installed Caps'
+ : t('capStore.status.noCaps')}
+
+
+ {isShowingInstalled
+ ? t('capStore.status.noInstalledCapsDesc') ||
+ "You haven't installed any caps yet. Browse the store to find caps to install."
+ : lastSearchQuery.trim()
+ ? t('capStore.status.noCapsDesc.search')
+ : t('capStore.status.noCapsDesc.category')}
+
+
+ );
+ }
+
+ return (
+
+ {displayCaps.map((cap) => (
+
+ ))}
+
+ );
+ };
+
+ const getSectionIcon = (sectionId: string, type: string) => {
+ if (type === 'section') {
+ switch (sectionId) {
+ case 'installed':
+ return Download;
+ case 'all':
+ return Grid3X3;
+ default:
+ return Package;
+ }
+ }
+
+ if (type === 'tag') {
+ switch (sectionId) {
+ case 'ai-model':
+ return Bot;
+ case 'coding':
+ return Code;
+ case 'content-writing':
+ return PenTool;
+ case 'research':
+ return BookOpen;
+ case 'crypto':
+ return Coins;
+ case 'tools':
+ return Wrench;
+ case 'others':
+ return MoreHorizontal;
+ default:
+ return Package;
+ }
+ }
+
+ return Package;
+ };
+
+ return (
+
+ {children && (
+ {children}
+ )}
+
+
+ {t('capStore.title')}
+
+
+ {/* Header */}
+
+
+
+
+
{t('capStore.title')}
+
+ {t('capStore.description')}
+
+
+
+
+
+ {/* Search Bar - only show for remote caps */}
+
+
+
+ setSearchQuery(e.target.value)}
+ onKeyDown={handleSearchKeyDown}
+ className="pl-10"
+ />
+
+
+
+
+
+ {/* Main Content with Sidebar */}
+
+ {/* Sidebar */}
+
+
+
+
+
+
+ {/* Content Area */}
+
+
+
+
+ );
+}
diff --git a/src/features/cap-store/components/cap-thumbnail.tsx b/src/features/cap-store/components/cap-thumbnail.tsx
new file mode 100644
index 00000000..7b0fbeca
--- /dev/null
+++ b/src/features/cap-store/components/cap-thumbnail.tsx
@@ -0,0 +1,37 @@
+import { Avatar, AvatarFallback, AvatarImage } from '@/shared/components/ui';
+import type { Cap } from '@/shared/types';
+
+const sizeClasses = {
+ sm: 'size-6', // 24px
+ md: 'size-8', // 32px
+ lg: 'size-10', // 40px
+ xl: 'size-12', // 48px
+} as const;
+
+export function CapThumbnail({
+ cap,
+ size = 'md',
+}: {
+ cap: Cap;
+ size?: keyof typeof sizeClasses;
+}) {
+ const sizeClass = sizeClasses[size] || sizeClasses['md'];
+
+ return (
+
+
+
+ {cap.idName.slice(0, 2).toUpperCase()}
+
+
+ );
+}
diff --git a/src/features/cap/components/index.ts b/src/features/cap-store/components/index.ts
similarity index 52%
rename from src/features/cap/components/index.ts
rename to src/features/cap-store/components/index.ts
index 037cc017..a8a2cc29 100644
--- a/src/features/cap/components/index.ts
+++ b/src/features/cap-store/components/index.ts
@@ -1 +1,2 @@
+export { CapSelector } from './cap-selector';
export { CapStoreModal } from './cap-store-modal';
diff --git a/src/features/cap-store/hooks/index.ts b/src/features/cap-store/hooks/index.ts
new file mode 100644
index 00000000..e2648596
--- /dev/null
+++ b/src/features/cap-store/hooks/index.ts
@@ -0,0 +1,2 @@
+export * from './use-installed-cap.ts';
+export * from './use-remote-cap.ts';
diff --git a/src/features/cap/hooks/use-cap.ts b/src/features/cap-store/hooks/use-installed-cap.ts
similarity index 63%
rename from src/features/cap/hooks/use-cap.ts
rename to src/features/cap-store/hooks/use-installed-cap.ts
index 560c93e3..c0fc689d 100644
--- a/src/features/cap/hooks/use-cap.ts
+++ b/src/features/cap-store/hooks/use-installed-cap.ts
@@ -1,33 +1,29 @@
import { useEffect, useState } from 'react';
+import type { Cap } from '@/shared/types/cap';
import { CapStateStore } from '../stores';
-import type { RemoteCap } from '../types';
/**
* Hook for managing the installed caps
*/
-export const useCap = (remoteCap:RemoteCap) => {
+export const useInstalledCap = (cap: Cap) => {
const [state, setState] = useState(() => CapStateStore.getState());
useEffect(() => {
const unsubscribe = CapStateStore.subscribe((newState) => {
setState(newState);
});
-
+
return unsubscribe;
}, []);
const { installCap, uninstallCap, updateInstalledCap, installedCaps } = state;
- const isInstalled = !!installedCaps[remoteCap.id];
- const installedVersion = installedCaps[remoteCap.id]?.version;
- const hasUpdate = installedVersion !== remoteCap.version;
+ const isInstalled = !!installedCaps[cap.id];
return {
isInstalled,
- installedVersion,
installCap,
uninstallCap,
- hasUpdate,
updateInstalledCap,
};
};
diff --git a/src/features/cap-store/hooks/use-remote-cap.ts b/src/features/cap-store/hooks/use-remote-cap.ts
new file mode 100644
index 00000000..c61f85fd
--- /dev/null
+++ b/src/features/cap-store/hooks/use-remote-cap.ts
@@ -0,0 +1,170 @@
+import * as yaml from 'js-yaml';
+import { useEffect, useState } from 'react';
+import { useCapKit } from '@/shared/hooks/use-capkit';
+import type { Cap } from '@/shared/types/cap';
+import { CapStateStore } from '../stores';
+import { parseCapContent, validateCapContent } from '../utils';
+
+interface CapKitQueryResponse {
+ code: number;
+ data: {
+ items: Array<{
+ id: string;
+ cid: string;
+ name: string;
+ }>;
+ page: number;
+ pageSize: number;
+ totalItems: number;
+ totalPages: number;
+ };
+}
+
+interface UseRemoteCapParams {
+ searchQuery?: string;
+ page?: number;
+}
+
+/**
+ * Hook for accessing the remote caps with advanced filtering, sorting, and pagination
+ */
+export function useRemoteCap() {
+ const [storeState, setStoreState] = useState(() => CapStateStore.getState());
+
+ // Subscribe to store changes
+ useEffect(() => {
+ const unsubscribe = CapStateStore.subscribe((newState) => {
+ setStoreState(newState);
+ });
+
+ return unsubscribe;
+ }, []);
+
+ const { capKit, isLoading: isCapKitLoading } = useCapKit();
+
+ const {
+ setRemoteCaps,
+ setRemoteCapLoading,
+ setRemoteCapError,
+ setRemoteCapPagination,
+ setLastSearchQuery,
+ } = storeState;
+
+ const { remoteCapState } = storeState;
+
+ const fetchCaps = async (params: UseRemoteCapParams = {}) => {
+ const { searchQuery: queryString = '', page: pageNum = 1 } = params;
+
+ setRemoteCapLoading(true);
+ setRemoteCapError(null);
+
+ try {
+ if (!capKit) {
+ throw new Error('CapKit not initialized');
+ }
+
+ const response: CapKitQueryResponse =
+ await capKit.queryWithName(queryString);
+
+ const remoteCapResults: (Cap | null)[] = await Promise.all(
+ response.data.items.map(async (item) => {
+ try {
+ const capData = await capKit.downloadCap(item.cid, 'utf8');
+ const downloadContent: unknown = yaml.load(capData.data.fileData);
+
+ // check if the cap is valid
+ if (!validateCapContent(downloadContent)) {
+ console.warn(
+ `Downloaded cap ${item.id} does not match Cap type specification, skipping...`,
+ );
+ return null;
+ }
+
+ // parse the cap content
+ return parseCapContent(downloadContent);
+ } catch (error) {
+ console.error(`Error processing cap ${item.id}:`, error);
+ return null;
+ }
+ }),
+ );
+
+ // filter out invalid caps
+ const validRemoteCaps = remoteCapResults.filter(
+ (cap): cap is Cap => cap !== null,
+ );
+
+ setRemoteCaps(validRemoteCaps);
+ setRemoteCapPagination({
+ totalCount: response.data.totalItems,
+ page: pageNum,
+ hasMore: response.data.page < response.data.totalPages,
+ });
+ setLastSearchQuery(queryString);
+ setRemoteCapLoading(false);
+
+ return response;
+ } catch (err) {
+ console.error('Error fetching caps:', err);
+ setRemoteCapError('Failed to fetch caps. Please try again.');
+ setRemoteCapLoading(false);
+ throw err;
+ }
+ };
+
+ useEffect(() => {
+ if (capKit && !isCapKitLoading) {
+ fetchCaps({ searchQuery: '', page: 1 });
+ }
+ }, [capKit, isCapKitLoading]);
+
+ const refetch = (newSearchQuery?: string) => {
+ const queryString =
+ newSearchQuery !== undefined
+ ? newSearchQuery
+ : remoteCapState.lastSearchQuery;
+ return fetchCaps({ searchQuery: queryString, page: 1 });
+ };
+
+ const goToPage = (newPage: number) => {
+ return fetchCaps({
+ searchQuery: remoteCapState.lastSearchQuery,
+ page: newPage,
+ });
+ };
+
+ const nextPage = () => {
+ if (remoteCapState.hasMore) {
+ return fetchCaps({
+ searchQuery: remoteCapState.lastSearchQuery,
+ page: remoteCapState.page + 1,
+ });
+ }
+ return Promise.resolve(null);
+ };
+
+ const previousPage = () => {
+ if (remoteCapState.page > 1) {
+ return fetchCaps({
+ searchQuery: remoteCapState.lastSearchQuery,
+ page: remoteCapState.page - 1,
+ });
+ }
+ return Promise.resolve(null);
+ };
+
+ return {
+ remoteCaps: remoteCapState.remoteCaps,
+ isLoading: remoteCapState.isLoading,
+ error: remoteCapState.error,
+ totalCount: remoteCapState.totalCount,
+ page: remoteCapState.page,
+ hasMore: remoteCapState.hasMore,
+ lastSearchQuery: remoteCapState.lastSearchQuery,
+ fetchCaps,
+ refetch,
+ goToPage,
+ nextPage,
+ previousPage,
+ };
+}
diff --git a/src/features/cap/stores/index.ts b/src/features/cap-store/stores.ts
similarity index 56%
rename from src/features/cap/stores/index.ts
rename to src/features/cap-store/stores.ts
index 795c2805..158cee6f 100644
--- a/src/features/cap/stores/index.ts
+++ b/src/features/cap-store/stores.ts
@@ -2,25 +2,47 @@
// Store for managing capability (Cap) installations and their states
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
-import { NuwaIdentityKit } from '@/features/auth/services';
-import { createPersistConfig, db } from '@/storage';
-import type { InstalledCap, RemoteCap } from '../types';
+import { NuwaIdentityKit } from '@/shared/services/identity-kit';
+import { createPersistConfig, db } from '@/shared/storage';
+import type { Cap } from '@/shared/types/cap';
// ================= Interfaces ================= //
-// Cap store state interface - only handles installed caps
+// Remote caps state interface
+interface RemoteCapState {
+ remoteCaps: Cap[];
+ isLoading: boolean;
+ error: string | null;
+ totalCount: number;
+ page: number;
+ hasMore: boolean;
+ lastSearchQuery: string;
+}
+
+// Cap store state interface - handles both installed and remote caps
interface CapStoreState {
- installedCaps: Record;
- currentCap: InstalledCap | null;
+ installedCaps: Record;
+
+ // Remote caps state
+ remoteCapState: RemoteCapState;
- // Current cap management
- setCurrentCap: (id: string | null) => void;
-
// Installed cap management
- installCap: (cap: RemoteCap) => void;
+ installCap: (cap: Cap) => void;
uninstallCap: (id: string) => void;
- updateInstalledCap: (id: string, updatedCap: RemoteCap) => void;
-
+ updateInstalledCap: (id: string, updatedCap: Cap) => void;
+
+ // Remote caps management
+ setRemoteCaps: (caps: Cap[]) => void;
+ setRemoteCapLoading: (isLoading: boolean) => void;
+ setRemoteCapError: (error: string | null) => void;
+ setRemoteCapPagination: (pagination: {
+ totalCount: number;
+ page: number;
+ hasMore: boolean;
+ }) => void;
+ setLastSearchQuery: (query: string) => void;
+ clearRemoteCaps: () => void;
+
// Data management
clearAllInstalledCaps: () => void;
@@ -47,7 +69,6 @@ const persistConfig = createPersistConfig({
getCurrentDID: getCurrentDID,
partialize: (state) => ({
installedCaps: state.installedCaps,
- currentCap: state.currentCap,
}),
onRehydrateStorage: () => (state?: CapStoreState) => {
if (state) {
@@ -63,23 +84,20 @@ export const CapStateStore = create()(
(set, get) => ({
// Store state
installedCaps: {},
- currentCap: null,
- // Current cap management
- setCurrentCap: (id: string | null) => {
- const { installedCaps } = get();
- if (id && installedCaps[id]) {
- set({ currentCap: installedCaps[id] });
- } else {
- set({ currentCap: null });
- }
+ // Remote caps state
+ remoteCapState: {
+ remoteCaps: [],
+ isLoading: false,
+ error: null,
+ totalCount: 0,
+ page: 1,
+ hasMore: false,
+ lastSearchQuery: '',
},
-
// Installation management
- installCap: (
- cap: RemoteCap,
- ) => {
+ installCap: (cap: Cap) => {
const { installedCaps } = get();
// Don't install if already installed
@@ -87,15 +105,10 @@ export const CapStateStore = create()(
return;
}
- const newInstalledCap: InstalledCap = {
- ...cap,
- updatedAt: Date.now(),
- };
-
set((state) => ({
installedCaps: {
...state.installedCaps,
- [cap.id]: newInstalledCap,
+ [cap.id]: cap,
},
}));
@@ -104,14 +117,10 @@ export const CapStateStore = create()(
},
uninstallCap: (id: string) => {
- const { currentCap } = get();
-
set((state) => {
const { [id]: removed, ...restCaps } = state.installedCaps;
return {
installedCaps: restCaps,
- // Clear currentCap if uninstalling the current cap
- currentCap: currentCap?.id === id ? null : state.currentCap,
};
});
@@ -126,35 +135,88 @@ export const CapStateStore = create()(
deleteFromDB();
},
-
// Data management
- updateInstalledCap: (id: string, updatedCap: RemoteCap) => {
- const { installedCaps, currentCap } = get();
+ updateInstalledCap: (id: string, updatedCap: Cap) => {
+ const { installedCaps } = get();
const cap = installedCaps[id];
if (!cap) return;
- const newInstalledCap: InstalledCap = {
- ...updatedCap,
- updatedAt: Date.now(),
- };
-
set((state) => ({
installedCaps: {
...state.installedCaps,
- [id]: newInstalledCap,
+ [id]: updatedCap,
},
- // Update currentCap if updating the current cap
- currentCap: currentCap?.id === id ? newInstalledCap : state.currentCap,
}));
get().saveToDB();
},
+ // Remote caps management
+ setRemoteCaps: (caps: Cap[]) => {
+ set((state) => ({
+ remoteCapState: {
+ ...state.remoteCapState,
+ remoteCaps: caps,
+ },
+ }));
+ },
+
+ setRemoteCapLoading: (isLoading: boolean) => {
+ set((state) => ({
+ remoteCapState: {
+ ...state.remoteCapState,
+ isLoading,
+ },
+ }));
+ },
+
+ setRemoteCapError: (error: string | null) => {
+ set((state) => ({
+ remoteCapState: {
+ ...state.remoteCapState,
+ error,
+ },
+ }));
+ },
+
+ setRemoteCapPagination: (pagination: {
+ totalCount: number;
+ page: number;
+ hasMore: boolean;
+ }) => {
+ set((state) => ({
+ remoteCapState: {
+ ...state.remoteCapState,
+ ...pagination,
+ },
+ }));
+ },
+
+ setLastSearchQuery: (query: string) => {
+ set((state) => ({
+ remoteCapState: {
+ ...state.remoteCapState,
+ lastSearchQuery: query,
+ },
+ }));
+ },
+
+ clearRemoteCaps: () => {
+ set((state) => ({
+ remoteCapState: {
+ ...state.remoteCapState,
+ remoteCaps: [],
+ totalCount: 0,
+ page: 1,
+ hasMore: false,
+ },
+ }));
+ },
+
clearAllInstalledCaps: () => {
set({
installedCaps: {},
- currentCap: null,
});
// Clear IndexedDB
@@ -184,9 +246,9 @@ export const CapStateStore = create()(
.equals(currentDID)
.toArray();
- const capsMap: Record = {};
+ const capsMap: Record = {};
- caps.forEach((cap: InstalledCap) => {
+ caps.forEach((cap: Cap) => {
capsMap[cap.id] = cap;
});
@@ -206,7 +268,10 @@ export const CapStateStore = create()(
if (!currentDID) return;
const { installedCaps } = get();
- const capsToSave = Object.values(installedCaps);
+ const capsToSave = Object.values(installedCaps).map((cap) => ({
+ ...cap,
+ did: currentDID,
+ }));
await capDB.caps.bulkPut(capsToSave);
} catch (error) {
console.error('Failed to save caps to DB:', error);
diff --git a/src/features/cap-store/utils.ts b/src/features/cap-store/utils.ts
new file mode 100644
index 00000000..74742a1b
--- /dev/null
+++ b/src/features/cap-store/utils.ts
@@ -0,0 +1,15 @@
+import { type Cap, CapSchema } from '@/shared/types/cap';
+
+// Cap type guards and utility functions
+export function validateCapContent(content: unknown): content is Cap {
+ try {
+ CapSchema.parse(content);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+export function parseCapContent(content: unknown): Cap {
+ return CapSchema.parse(content);
+}
diff --git a/src/features/cap-studio/components/cap-edit/cap-edit-form.tsx b/src/features/cap-studio/components/cap-edit/cap-edit-form.tsx
new file mode 100644
index 00000000..ba485403
--- /dev/null
+++ b/src/features/cap-studio/components/cap-edit/cap-edit-form.tsx
@@ -0,0 +1,296 @@
+import { AlertCircle, CheckCircle2, Loader2, Save } from 'lucide-react';
+import type { LocalCap } from '@/features/cap-studio/types';
+import {
+ Button,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+ Input,
+ MultiSelect,
+ Textarea,
+} from '@/shared/components/ui';
+import { predefinedTags } from '@/shared/constants/cap';
+import { useEditForm } from '../../hooks/use-edit-form';
+import { DashboardGrid } from '../layout/dashboard-layout';
+import { ModelSelectorDialog } from '../model-selector';
+import { McpServersConfig } from './mcp-servers-config';
+import { ModelDetails } from './model-details';
+import { PromptEditor } from './prompt-editor';
+
+interface CapEditFormProps {
+ editingCap?: LocalCap;
+}
+
+export function CapEditForm({ editingCap }: CapEditFormProps) {
+ const {
+ form,
+ handleFormSave,
+ handleFormCancel,
+ handleUpdateMcpServers,
+ isSaving,
+ selectedModel,
+ mcpServers,
+ } = useEditForm({
+ editingCap,
+ });
+
+ return (
+
+
+
+
+ {editingCap ? 'Edit Cap' : 'Create New Cap'}
+
+
+ {editingCap ? 'Update your Cap' : 'Build a new Cap from scratch'}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-edit/index.tsx b/src/features/cap-studio/components/cap-edit/index.tsx
new file mode 100644
index 00000000..5697969c
--- /dev/null
+++ b/src/features/cap-studio/components/cap-edit/index.tsx
@@ -0,0 +1,17 @@
+import { useParams } from 'react-router-dom';
+import { useLocalCaps } from '../../hooks';
+import { DashboardLayout } from '../layout/dashboard-layout';
+import { CapEditForm } from './cap-edit-form';
+
+export function CapEdit() {
+ const { id } = useParams();
+ const localCaps = useLocalCaps();
+
+ const editingCap = id ? localCaps.find((cap) => cap.id === id) : undefined;
+
+ return (
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-edit/mcp-servers-config.tsx b/src/features/cap-studio/components/cap-edit/mcp-servers-config.tsx
new file mode 100644
index 00000000..3f5ff55d
--- /dev/null
+++ b/src/features/cap-studio/components/cap-edit/mcp-servers-config.tsx
@@ -0,0 +1,344 @@
+import { Check, Code, Edit, Plus, X } from 'lucide-react';
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import {
+ Button,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Input,
+ Label,
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/shared/components/ui';
+
+import type { CapMcpServerConfig } from '@/shared/types/cap';
+
+interface McpServersConfigProps {
+ mcpServers: Record;
+ onUpdateMcpServers: (servers: Record) => void;
+ capId?: string;
+}
+
+export function McpServersConfig({
+ mcpServers,
+ onUpdateMcpServers,
+ capId,
+}: McpServersConfigProps) {
+ const navigate = useNavigate();
+ const [isAdding, setIsAdding] = useState(false);
+ const [editingServer, setEditingServer] = useState(null);
+ const [newServer, setNewServer] = useState<
+ CapMcpServerConfig & { name: string }
+ >({
+ name: '',
+ url: '',
+ transport: 'sse',
+ });
+ const [errors, setErrors] = useState<{
+ name?: string;
+ url?: string;
+ }>({});
+
+ const validateName = (
+ name: string,
+ originalName?: string,
+ ): string | undefined => {
+ if (!name.trim()) {
+ return 'Server name is required';
+ }
+ if (!/^[a-z-]+$/.test(name)) {
+ return 'Name must contain only lowercase letters and dashes';
+ }
+ if (mcpServers[name] && name !== originalName) {
+ return 'Server name already exists';
+ }
+ return undefined;
+ };
+
+ const validateUrl = (url: string): string | undefined => {
+ if (!url.trim()) {
+ return 'Server URL is required';
+ }
+ try {
+ new URL(url);
+ return undefined;
+ } catch {
+ return 'Please enter a valid URL';
+ }
+ };
+
+ const handleAddServer = () => {
+ setIsAdding(true);
+ setEditingServer(null);
+ setNewServer({
+ name: '',
+ url: '',
+ transport: 'sse',
+ });
+ setErrors({});
+ };
+
+ const handleEditServer = (serverName: string) => {
+ const serverConfig = mcpServers[serverName];
+ setEditingServer(serverName);
+ setIsAdding(false);
+ setNewServer({
+ name: serverName,
+ url: serverConfig.url,
+ transport: serverConfig.transport,
+ });
+ setErrors({});
+ };
+
+ const handleConfirmServer = () => {
+ const nameError = validateName(newServer.name, editingServer || undefined);
+ const urlError = validateUrl(newServer.url);
+
+ setErrors({
+ name: nameError,
+ url: urlError,
+ });
+
+ if (nameError || urlError) {
+ return;
+ }
+
+ let updatedServers = { ...mcpServers };
+
+ if (editingServer && editingServer !== newServer.name) {
+ // Remove the old server entry if name changed
+ const { [editingServer]: removed, ...rest } = updatedServers;
+ updatedServers = rest;
+ }
+
+ // Add/update the server with the new/current name
+ updatedServers[newServer.name] = {
+ url: newServer.url,
+ transport: newServer.transport,
+ };
+
+ onUpdateMcpServers(updatedServers);
+ setIsAdding(false);
+ setEditingServer(null);
+ setNewServer({
+ name: '',
+ url: '',
+ transport: 'sse',
+ });
+ setErrors({});
+ };
+
+ const handleCancelAdd = () => {
+ setIsAdding(false);
+ setEditingServer(null);
+ setNewServer({
+ name: '',
+ url: '',
+ transport: 'sse',
+ });
+ setErrors({});
+ };
+
+ const handleRemoveServer = (serverName: string) => {
+ const { [serverName]: removed, ...rest } = mcpServers;
+ onUpdateMcpServers(rest);
+ };
+
+ const handleTestServer = (serverName: string) => {
+ if (capId) {
+ navigate(
+ `/cap-studio/mcp/${capId}?server=${encodeURIComponent(serverName)}`,
+ );
+ }
+ };
+
+ return (
+
+
+
+
+ MCP Servers
+
+ Only SSE and HTTP Streamable transports are supported.
+
+
+
+
+
+
+
+ {/* Existing MCP servers */}
+ {Object.entries(mcpServers).map(([serverName, config]) => (
+
+
+
{serverName}
+
+ {config.transport === 'httpStream'
+ ? 'HTTP STREAMABLE'
+ : config.transport.toUpperCase()}{' '}
+ • {config.url}
+
+
+
+
+ {capId && (
+
+ )}
+
+
+
+ ))}
+
+ {/* Add/Edit server form */}
+ {(isAdding || editingServer) && (
+
+
+
+
+
+
{
+ const value = e.target.value;
+ setNewServer((prev) => ({
+ ...prev,
+ name: value,
+ }));
+ setErrors((prev) => ({
+ ...prev,
+ name: validateName(value, editingServer || undefined),
+ }));
+ }}
+ className={errors.name ? 'border-red-500' : ''}
+ />
+ {errors.name && (
+
{errors.name}
+ )}
+
+
+
+
+
{
+ const value = e.target.value;
+ setNewServer((prev) => ({
+ ...prev,
+ url: value,
+ }));
+ setErrors((prev) => ({
+ ...prev,
+ url: validateUrl(value),
+ }));
+ }}
+ className={errors.url ? 'border-red-500' : ''}
+ />
+ {errors.url && (
+
{errors.url}
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {Object.keys(mcpServers).length === 0 && !isAdding && (
+
+
No MCP servers configured
+
+ )}
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-edit/model-details.tsx b/src/features/cap-studio/components/cap-edit/model-details.tsx
new file mode 100644
index 00000000..fd955e1c
--- /dev/null
+++ b/src/features/cap-studio/components/cap-edit/model-details.tsx
@@ -0,0 +1,61 @@
+import { Badge } from '@/shared/components/ui/badge';
+import type { CapModel } from '@/shared/types/cap';
+import { getModelName, getProviderName } from '../../utils';
+import { ProviderAvatar } from '../model-selector/provider-avatar';
+
+interface ModelDetailsProps {
+ model: CapModel;
+}
+
+export function ModelDetails({ model }: ModelDetailsProps) {
+ return (
+
+
+
+
+ {getModelName(model)}
+
+
+ {getProviderName(model)}
+
+
+ Context window: {model.context_length?.toLocaleString() || 'Unknown'}{' '}
+ tokens
+
+
+ Pricing: ${model.pricing.input_per_million_tokens}/1M input, $
+ {model.pricing.output_per_million_tokens}/1M output
+
+ {model.supported_parameters &&
+ model.supported_parameters.length > 0 && (
+
+
+ Supported parameters:
+
+
+ {model.supported_parameters.map((param) => (
+
+ {param}
+
+ ))}
+
+
+ )}
+ {model.supported_inputs && model.supported_inputs.length > 0 && (
+
+
+ Supported inputs:
+
+
+ {model.supported_inputs.map((input) => (
+
+ {input}
+
+ ))}
+
+
+ )}
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-edit/prompt-editor.tsx b/src/features/cap-studio/components/cap-edit/prompt-editor.tsx
new file mode 100644
index 00000000..d2ed439d
--- /dev/null
+++ b/src/features/cap-studio/components/cap-edit/prompt-editor.tsx
@@ -0,0 +1,330 @@
+import { Maximize2, Save, Variable, X } from 'lucide-react';
+import { useRef, useState } from 'react';
+import { Markdown } from '@/features/chat/components/markdown';
+import {
+ Badge,
+ Button,
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+ Drawer,
+ DrawerContent,
+ DrawerHeader,
+ DrawerTitle,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+ Textarea,
+} from '@/shared/components/ui';
+import { promptVariables } from '@/shared/constants/cap';
+import { cn } from '@/shared/utils';
+import { PromptSuggestions } from './prompt-suggestions';
+
+interface PromptEditorProps {
+ value: string;
+ onChange: (value: string) => void;
+ suggestions?: string[];
+ onSuggestionsChange?: (suggestions: string[]) => void;
+ placeholder?: string;
+ className?: string;
+}
+
+interface VariablesDialogProps {
+ isOpen: boolean;
+ onOpenChange: (open: boolean) => void;
+ onVariableSelect: (variable: string) => void;
+}
+
+function VariablesDialog({
+ isOpen,
+ onOpenChange,
+ onVariableSelect,
+}: VariablesDialogProps) {
+ return (
+
+ );
+}
+
+export function PromptEditor({
+ value,
+ onChange,
+ suggestions = [],
+ onSuggestionsChange,
+ placeholder,
+ className,
+}: PromptEditorProps) {
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
+ const [activeTab, setActiveTab] = useState('write');
+ const [drawerActiveTab, setDrawerActiveTab] = useState('write');
+ const [isVariablesDialogOpen, setIsVariablesDialogOpen] = useState(false);
+ const [isDrawerVariablesDialogOpen, setIsDrawerVariablesDialogOpen] =
+ useState(false);
+ const [wordCount, setWordCount] = useState(0);
+ const [drawerValue, setDrawerValue] = useState('');
+ const textareaRef = useRef(null);
+ const drawerTextareaRef = useRef(null);
+
+ const handleChange = (newValue: string) => {
+ onChange(newValue);
+ setWordCount(
+ newValue
+ .trim()
+ .split(/\s+/)
+ .filter((word) => word.length > 0).length,
+ );
+ };
+
+ const insertText = (text: string) => {
+ const textarea = textareaRef.current;
+ if (!textarea) return;
+
+ const start = textarea.selectionStart;
+ const end = textarea.selectionEnd;
+ const newValue = value.substring(0, start) + text + value.substring(end);
+
+ handleChange(newValue);
+
+ // Set cursor position after inserted text
+ setTimeout(() => {
+ textarea.focus();
+ textarea.selectionStart = textarea.selectionEnd = start + text.length;
+ }, 0);
+ };
+
+ const insertVariable = (variable: string) => {
+ insertText(variable);
+ };
+
+ const openDrawer = () => {
+ setDrawerValue(value);
+ setIsDrawerOpen(true);
+ };
+
+ const handleDrawerSave = () => {
+ onChange(drawerValue);
+ setWordCount(
+ drawerValue
+ .trim()
+ .split(/\s+/)
+ .filter((word) => word.length > 0).length,
+ );
+ setIsDrawerOpen(false);
+ };
+
+ const handleDrawerCancel = () => {
+ setIsDrawerOpen(false);
+ };
+
+ const handleDrawerChange = (newValue: string) => {
+ setDrawerValue(newValue);
+ };
+
+ return (
+
+ {/* Toolbar */}
+
+
+
+
+ Write
+ Preview
+
+
+ {/* Variables */}
+
+
+
+
+
+ {wordCount} words
+
+
+
+
+
+ {/* Editor */}
+
+
+
+
+
+
+ {value || 'No content to preview'}
+
+
+
+
+
+ {/* Helper text */}
+
+ Use variables like{' '}
+
+ {'{{user_geo}}'}
+
+ to make your prompt dynamic.
+
+
+ {/* Prompt Suggestions */}
+ {onSuggestionsChange && (
+
+ )}
+
+ {/* Bottom Drawer */}
+
+
+
+
+ Prompt Editor
+
+
+
+ {/* Drawer Toolbar */}
+
+
+
+
+ Write
+ Preview
+
+
+
+ {/* Variables */}
+ {
+ const textarea = drawerTextareaRef.current;
+ if (!textarea) return;
+ const start = textarea.selectionStart;
+ const end = textarea.selectionEnd;
+ const newValue =
+ drawerValue.substring(0, start) +
+ variable +
+ drawerValue.substring(end);
+ setDrawerValue(newValue);
+ setTimeout(() => {
+ textarea.focus();
+ textarea.selectionStart = textarea.selectionEnd =
+ start + variable.length;
+ }, 0);
+ }}
+ />
+
+
+
+
+
+
+
+
+ {/* Drawer Editor */}
+
+
+
+
+
+
+ {drawerValue || 'No content to preview'}
+
+
+
+
+
+ {/* Prompt Suggestions in Drawer */}
+ {onSuggestionsChange && (
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-edit/prompt-suggestions.tsx b/src/features/cap-studio/components/cap-edit/prompt-suggestions.tsx
new file mode 100644
index 00000000..af9012b3
--- /dev/null
+++ b/src/features/cap-studio/components/cap-edit/prompt-suggestions.tsx
@@ -0,0 +1,98 @@
+import { Plus, X } from 'lucide-react';
+import { useState } from 'react';
+import { Badge, Button, Input, Label } from '@/shared/components/ui';
+import { cn } from '@/shared/utils';
+
+interface PromptSuggestionsProps {
+ suggestions: string[];
+ onSuggestionsChange: (suggestions: string[]) => void;
+ className?: string;
+}
+
+export function PromptSuggestions({
+ suggestions,
+ onSuggestionsChange,
+ className,
+}: PromptSuggestionsProps) {
+ const [newSuggestion, setNewSuggestion] = useState('');
+
+ const addSuggestion = () => {
+ const trimmed = newSuggestion.trim();
+ if (trimmed && !suggestions.includes(trimmed)) {
+ onSuggestionsChange([...suggestions, trimmed]);
+ setNewSuggestion('');
+ }
+ };
+
+ const removeSuggestion = (index: number) => {
+ onSuggestionsChange(suggestions.filter((_, i) => i !== index));
+ };
+
+ const handleKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ addSuggestion();
+ }
+ };
+
+ return (
+
+
+
+
+ Add suggestions to help users get started with their prompts
+
+
+
+ {/* Add new suggestion */}
+
+
setNewSuggestion(e.target.value)}
+ onKeyPress={handleKeyPress}
+ placeholder="Enter a suggestion..."
+ className="flex-1"
+ />
+
+
+
+ {/* Display existing suggestions */}
+ {suggestions.length > 0 && (
+
+
Current Suggestions:
+
+ {suggestions.map((suggestion, index) => (
+
+ {suggestion}
+
+
+ ))}
+
+
+ )}
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-submit/author-form.tsx b/src/features/cap-studio/components/cap-submit/author-form.tsx
new file mode 100644
index 00000000..e2e4e157
--- /dev/null
+++ b/src/features/cap-studio/components/cap-submit/author-form.tsx
@@ -0,0 +1,83 @@
+import type { Control } from 'react-hook-form';
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+ Input,
+} from '@/shared/components/ui';
+import type { SubmitFormData } from '../../hooks/use-submit-form';
+
+interface AuthorFormProps {
+ control: Control;
+ onFieldChange: (fieldName: keyof SubmitFormData) => void;
+}
+
+export function AuthorForm({ control, onFieldChange }: AuthorFormProps) {
+ return (
+
+
+ Author
+
+ Information about the cap author and licensing
+
+
+
+ (
+
+ Homepage (Optional)
+
+ {
+ field.onChange(e);
+ onFieldChange('homepage');
+ }}
+ />
+
+
+ Link to your cap's homepage or documentation
+
+
+
+ )}
+ />
+
+ (
+
+ Repository (Optional)
+
+ {
+ field.onChange(e);
+ onFieldChange('repository');
+ }}
+ />
+
+
+ Link to the source code repository
+
+
+
+ )}
+ />
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-submit/cap-information.tsx b/src/features/cap-studio/components/cap-submit/cap-information.tsx
new file mode 100644
index 00000000..b0b84d10
--- /dev/null
+++ b/src/features/cap-studio/components/cap-submit/cap-information.tsx
@@ -0,0 +1,78 @@
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from '@/shared/components/ui';
+import type { LocalCap } from '../../types';
+
+interface CapInformationProps {
+ cap: LocalCap;
+}
+
+export function CapInformation({ cap }: CapInformationProps) {
+ return (
+
+
+ Cap Information
+
+ Basic information about your cap (read-only)
+
+
+
+
+
+
+ Name
+
+
{cap.capData.idName}
+
+
+
+ Display Name
+
+
{cap.capData.metadata.displayName}
+
+
+
+
+ Description
+
+
{cap.capData.metadata.description}
+
+
+
Tags
+
+ {cap.capData.metadata.tags.map((tag) => (
+
+ {tag}
+
+ ))}
+
+
+
+
+
+ Model
+
+
{cap.capData.core.model.name}
+
+
+
+ MCP Servers
+
+
+ {Object.keys(cap.capData.core.mcpServers).length > 0
+ ? Object.keys(cap.capData.core.mcpServers).join(', ')
+ : 'None'}
+
+
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-submit/cap-submit-form.tsx b/src/features/cap-studio/components/cap-submit/cap-submit-form.tsx
new file mode 100644
index 00000000..29880a50
--- /dev/null
+++ b/src/features/cap-studio/components/cap-submit/cap-submit-form.tsx
@@ -0,0 +1,130 @@
+import { AlertCircle, CheckCircle2, Loader2, Upload } from 'lucide-react';
+import { Button, Card, CardContent, Form } from '@/shared/components/ui';
+import { useSubmitForm } from '../../hooks/use-submit-form';
+import type { LocalCap } from '../../types';
+import { AuthorForm } from './author-form';
+import { CapInformation } from './cap-information';
+import { SubmissionConfirmationDialog } from './submission-confirmation-dialog';
+import { ThumbnailUpload } from './thumbnail-upload';
+
+interface CapSubmitFormProps {
+ cap: LocalCap;
+}
+
+export function CapSubmitForm({ cap }: CapSubmitFormProps) {
+ const {
+ form,
+ handleCancel,
+ handleFormSubmit,
+ handleConfirmedSubmit,
+ handleFieldChange,
+ isSubmitting,
+ showConfirmDialog,
+ thumbnail,
+ setThumbnail,
+ setShowConfirmDialog,
+ watchedData,
+ } = useSubmitForm({ cap });
+
+ return (
+
+
+
+
Submit Cap to Store
+
+ Publish @{cap.capData.idName} to the Nuwa Cap Store for others to
+ discover and use
+
+
+
+
+
+
+
+
+
+
setShowConfirmDialog(false)}
+ onConfirm={handleConfirmedSubmit}
+ />
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-submit/index.tsx b/src/features/cap-studio/components/cap-submit/index.tsx
new file mode 100644
index 00000000..22105e7f
--- /dev/null
+++ b/src/features/cap-studio/components/cap-submit/index.tsx
@@ -0,0 +1,31 @@
+import { useParams } from 'react-router-dom';
+import { useLocalCaps } from '../../hooks';
+import { DashboardHeader, DashboardLayout } from '../layout/dashboard-layout';
+import { CapSubmitForm } from './cap-submit-form';
+
+export function Submit() {
+ const { id } = useParams();
+ const localCaps = useLocalCaps();
+
+ const cap = localCaps.find((cap) => cap.id === id);
+
+ if (!cap) {
+ return (
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-submit/submission-confirmation-dialog.tsx b/src/features/cap-studio/components/cap-submit/submission-confirmation-dialog.tsx
new file mode 100644
index 00000000..5c9b7bc7
--- /dev/null
+++ b/src/features/cap-studio/components/cap-submit/submission-confirmation-dialog.tsx
@@ -0,0 +1,132 @@
+import { FileText, Loader2, Upload } from 'lucide-react';
+import {
+ Button,
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from '@/shared/components/ui';
+import type { CapThumbnail } from '@/shared/types/cap';
+import type { SubmitFormData } from '../../hooks/use-submit-form';
+import type { LocalCap } from '../../types';
+
+interface SubmissionConfirmationDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ data: SubmitFormData;
+ cap: LocalCap;
+ thumbnail: CapThumbnail;
+ isSubmitting: boolean;
+ onCancel: () => void;
+ onConfirm: () => Promise;
+}
+
+export function SubmissionConfirmationDialog({
+ open,
+ onOpenChange,
+ data,
+ cap,
+ thumbnail,
+ isSubmitting,
+ onCancel,
+ onConfirm,
+}: SubmissionConfirmationDialogProps) {
+ return (
+
+ );
+}
+
+interface CapStorePreviewProps {
+ data: SubmitFormData;
+ cap: LocalCap;
+ thumbnail?: CapThumbnail;
+}
+
+function CapStorePreview({ data, cap, thumbnail }: CapStorePreviewProps) {
+ return (
+
+ {/* Header */}
+
+ {thumbnail ? (
+

+ ) : (
+
+
+
+ )}
+
+
+ {cap.capData.metadata.displayName || 'Untitled Cap'}
+
+
+ {cap.capData.metadata.description || 'No description provided'}
+
+
+
+
+ {/* Links */}
+ {(data.homepage || data.repository) && (
+
+
Links
+
+ {data.homepage && (
+
+ )}
+ {data.repository && (
+
+ )}
+
+
+ )}
+
+ );
+}
diff --git a/src/features/cap-studio/components/cap-submit/thumbnail-upload.tsx b/src/features/cap-studio/components/cap-submit/thumbnail-upload.tsx
new file mode 100644
index 00000000..a52f61cd
--- /dev/null
+++ b/src/features/cap-studio/components/cap-submit/thumbnail-upload.tsx
@@ -0,0 +1,227 @@
+import { Image as ImageIcon, Link, Upload, X } from 'lucide-react';
+import { useId, useState } from 'react';
+import { toast } from 'sonner';
+import {
+ Button,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Input,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from '@/shared/components/ui';
+import type { CapThumbnail } from '@/shared/types/cap';
+
+interface ThumbnailUploadProps {
+ thumbnail: CapThumbnail;
+ onThumbnailChange: (thumbnail: CapThumbnail) => void;
+}
+
+export function ThumbnailUpload({
+ thumbnail,
+ onThumbnailChange,
+}: ThumbnailUploadProps) {
+ const thumbnailUploadId = useId();
+ const [inputUrl, setInputUrl] = useState(thumbnail?.url || '');
+ const [activeTab, setActiveTab] = useState<'upload' | 'url'>(
+ thumbnail?.type === 'url' ? 'url' : 'upload',
+ );
+
+ const handleThumbnailUpload = async (
+ event: React.ChangeEvent,
+ ) => {
+ const file = event.target.files?.[0];
+ if (file) {
+ if (file.size > 2 * 1024 * 1024) {
+ // 2MB limit
+ toast.error('Thumbnail size must be less than 2MB');
+ return;
+ }
+
+ try {
+ // Convert file to base64
+ const base64 = await new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => resolve(reader.result as string);
+ reader.onerror = reject;
+ reader.readAsDataURL(file);
+ });
+
+ onThumbnailChange({
+ type: 'file',
+ file: base64,
+ });
+ } catch (error) {
+ toast.error('Failed to process image file');
+ console.error('File conversion error:', error);
+ }
+ }
+ };
+
+ const handleUrlSubmit = () => {
+ if (!inputUrl.trim()) {
+ toast.error('Please enter a valid image URL');
+ return;
+ }
+
+ // Basic URL validation
+ try {
+ new URL(inputUrl);
+ } catch {
+ toast.error('Please enter a valid URL format');
+ return;
+ }
+
+ onThumbnailChange({
+ type: 'url',
+ url: inputUrl.trim(),
+ });
+ toast.success('Image URL has been set');
+ };
+
+ const handleRemoveThumbnail = () => {
+ onThumbnailChange(null);
+ setInputUrl('');
+ };
+
+ const getThumbnailSrc = () => {
+ if (thumbnail?.type === 'file' && thumbnail.file) {
+ // For base64, the URL is the base64 string itself
+ return thumbnail.file;
+ }
+ if (thumbnail?.type === 'url' && thumbnail.url) {
+ return thumbnail.url;
+ }
+ return null;
+ };
+
+ const hasThumbnail = thumbnail !== null;
+ const thumbnailSrc = getThumbnailSrc();
+
+ return (
+
+
+
+ Thumbnail
+
+
+ Upload a file or enter an image URL to set your Cap thumbnail
+
+
+
+ {/* Thumbnail Preview */}
+
+
+ {hasThumbnail ? (
+
+

{
+ toast.error(
+ 'Failed to load image, please check if the URL is valid',
+ );
+ onThumbnailChange(null);
+ setInputUrl('');
+ }}
+ />
+
+
+ ) : (
+
+
+
+ )}
+
+
+
+
setActiveTab(value as 'upload' | 'url')}
+ className="w-full"
+ >
+
+
+
+ Upload File
+
+
+
+ Image URL
+
+
+
+
+
+
+
+
+
+
+ setInputUrl(e.target.value)}
+ className="flex-1 border-slate-300 focus:border-blue-400 focus:ring-blue-400"
+ />
+
+
+
+
+
+
+
• Supported formats: PNG, JPG, WebP, GIF
+
• File size: Maximum 2MB
+
• Recommended size: 400×400px or 1:1 ratio
+
+
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/index.tsx b/src/features/cap-studio/components/index.tsx
new file mode 100644
index 00000000..3caeaf71
--- /dev/null
+++ b/src/features/cap-studio/components/index.tsx
@@ -0,0 +1,55 @@
+import { Bug } from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+import { Button } from '@/shared/components/ui/button';
+import { useCurrentCap } from '@/shared/hooks';
+import type { LocalCap } from '../types';
+import { DashboardHeader, DashboardLayout } from './layout/dashboard-layout';
+import { MyCaps } from './my-caps';
+
+export function CapStudio() {
+ const navigate = useNavigate();
+ const { setCurrentCap } = useCurrentCap();
+
+ const handleEditCap = (cap: LocalCap) => {
+ navigate(`/cap-studio/edit/${cap.id}`);
+ };
+
+ const handleTestCap = (cap: LocalCap) => {
+ // Set this cap as the current cap for testing
+ setCurrentCap(cap.capData);
+ navigate(`/chat`);
+ };
+
+ const handleSubmitCap = (cap: LocalCap) => {
+ navigate(`/cap-studio/submit/${cap.id}`);
+ };
+
+ const handleCreateNew = () => {
+ navigate('/cap-studio/create');
+ };
+
+ const handleGoToMcpDebug = () => {
+ navigate('/cap-studio/mcp');
+ };
+
+ return (
+
+
+ MCP Tools
+
+ }
+ />
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/layout/dashboard-layout.tsx b/src/features/cap-studio/components/layout/dashboard-layout.tsx
new file mode 100644
index 00000000..6b533f90
--- /dev/null
+++ b/src/features/cap-studio/components/layout/dashboard-layout.tsx
@@ -0,0 +1,85 @@
+import type { ReactNode } from 'react';
+import { cn } from '@/shared/utils';
+
+interface DashboardLayoutProps {
+ children: ReactNode;
+ className?: string;
+}
+
+export function DashboardLayout({ children, className }: DashboardLayoutProps) {
+ return (
+
+ );
+}
+
+interface DashboardHeaderProps {
+ title: string;
+ description?: string;
+ actions?: ReactNode;
+}
+
+export function DashboardHeader({
+ title,
+ description,
+ actions,
+}: DashboardHeaderProps) {
+ return (
+
+
+
+ {title}
+
+ {description && (
+
+ {description}
+
+ )}
+
+ {actions &&
{actions}
}
+
+ );
+}
+
+interface DashboardSectionProps {
+ children: ReactNode;
+ className?: string;
+}
+
+export function DashboardSection({
+ children,
+ className,
+}: DashboardSectionProps) {
+ return ;
+}
+
+interface DashboardGridProps {
+ children: ReactNode;
+ cols?: 1 | 2 | 3 | 4;
+ className?: string;
+}
+
+export function DashboardGrid({
+ children,
+ cols = 2,
+ className,
+}: DashboardGridProps) {
+ const gridClasses = {
+ 1: 'grid-cols-1',
+ 2: 'grid-cols-1 lg:grid-cols-2',
+ 3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
+ 4: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
+ };
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/features/cap-studio/components/layout/section-tabs.tsx b/src/features/cap-studio/components/layout/section-tabs.tsx
new file mode 100644
index 00000000..9d05b893
--- /dev/null
+++ b/src/features/cap-studio/components/layout/section-tabs.tsx
@@ -0,0 +1,72 @@
+import { Bug, Code2, Folder, Settings2, Sparkles, Upload } from 'lucide-react';
+import type { ReactNode } from 'react';
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from '@/shared/components/ui';
+
+interface SectionTab {
+ id: string;
+ label: string;
+ icon: ReactNode;
+ content: ReactNode;
+ badge?: string | number;
+}
+
+interface SectionTabsProps {
+ defaultTab?: string;
+ value?: string;
+ onValueChange?: (val: string) => void;
+ tabs: SectionTab[];
+}
+
+export function SectionTabs({
+ defaultTab,
+ value,
+ onValueChange,
+ tabs,
+}: SectionTabsProps) {
+ return (
+
+
+ {tabs.map((tab) => (
+
+ {tab.icon}
+ {tab.label}
+ {tab.badge && (
+
+ {tab.badge}
+
+ )}
+
+ ))}
+
+
+ {tabs.map((tab) => (
+
+ {tab.content}
+
+ ))}
+
+ );
+}
+
+export const tabIcons = {
+ mycaps: ,
+ create: ,
+ upload: ,
+ debug: ,
+ mcp: ,
+ settings: ,
+};
diff --git a/src/features/cap-studio/components/mcp-tools/debug-panel.tsx b/src/features/cap-studio/components/mcp-tools/debug-panel.tsx
new file mode 100644
index 00000000..ecd8d26d
--- /dev/null
+++ b/src/features/cap-studio/components/mcp-tools/debug-panel.tsx
@@ -0,0 +1,825 @@
+import Form from '@rjsf/core';
+import validator from '@rjsf/validator-ajv8';
+import {
+ ChevronDown,
+ ChevronRight,
+ Code,
+ Code2,
+ Copy,
+ Database,
+ FileText,
+ Play,
+ Search,
+ Wrench,
+} from 'lucide-react';
+import { useState } from 'react';
+import { toast } from 'sonner';
+import {
+ Badge,
+ Button,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+ Input,
+ ScrollArea,
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/shared/components/ui';
+import type { NuwaMCPClient } from '@/shared/types';
+import { cn } from '@/shared/utils';
+
+interface LogEntry {
+ type: 'info' | 'error' | 'success' | 'warning';
+ message: string;
+ data?: any;
+}
+
+interface McpDebugPanelProps {
+ client: NuwaMCPClient;
+ tools: string[];
+ toolsMap: Record;
+ prompts: string[];
+ promptsMap: Record;
+ resources: string[];
+ onLog: (entry: Omit) => void;
+}
+
+const generateUUID = (): string => {
+ if (
+ typeof globalThis !== 'undefined' &&
+ (globalThis as any).crypto?.randomUUID
+ ) {
+ return (globalThis as any).crypto.randomUUID();
+ }
+ return Date.now().toString(36) + Math.random().toString(36).slice(2);
+};
+
+interface ToolsSelectDialogProps {
+ tools: string[];
+ toolsMap: Record;
+ executionResults: Record;
+ selectedTool: string | null;
+ searchQuery: string;
+ isOpen: boolean;
+ onOpenChange: (open: boolean) => void;
+ onSearchChange: (query: string) => void;
+ onToolSelect: (tool: string) => void;
+}
+
+function ToolsSelectDialog({
+ tools,
+ toolsMap,
+ executionResults,
+ selectedTool,
+ searchQuery,
+ isOpen,
+ onOpenChange,
+ onSearchChange,
+ onToolSelect,
+}: ToolsSelectDialogProps) {
+ const filteredTools = tools.filter((tool) =>
+ tool.toLowerCase().includes(searchQuery.toLowerCase()),
+ );
+
+ return (
+
+ );
+}
+
+export function McpDebugPanel({
+ client,
+ tools,
+ toolsMap,
+ prompts,
+ promptsMap,
+ resources,
+ onLog,
+}: McpDebugPanelProps) {
+ const [selectedTool, setSelectedTool] = useState(null);
+ const [toolFormData, setToolFormData] = useState({});
+ const [toolSearchQuery, setToolSearchQuery] = useState('');
+ const [promptSearchQuery, setPromptSearchQuery] = useState('');
+ const [resourceSearchQuery, setResourceSearchQuery] = useState('');
+ const [executionResults, setExecutionResults] = useState>(
+ {},
+ );
+ const [expandedPrompts, setExpandedPrompts] = useState>(
+ new Set(),
+ );
+ const [toolsDialogOpen, setToolsDialogOpen] = useState(false);
+ const [activeView, setActiveView] = useState<
+ 'tools' | 'prompts' | 'resources'
+ >('tools');
+
+ const safeStringify = (obj: any): string => {
+ try {
+ return JSON.stringify(obj, null, 2);
+ } catch (_) {
+ return String(obj);
+ }
+ };
+
+ const handleExecuteTool = async (payload: any) => {
+ let args: any;
+ if (payload?.preventDefault) {
+ payload.preventDefault();
+ args = (payload as any).formData ?? {};
+ } else if (payload?.formData !== undefined) {
+ args = payload.formData;
+ } else {
+ args = payload ?? {};
+ }
+
+ if (!selectedTool || !client) return;
+
+ const tool = toolsMap[selectedTool];
+ if (!tool) return;
+
+ try {
+ onLog({
+ type: 'info',
+ message: `Executing tool: ${selectedTool}`,
+ data: { tool: selectedTool, args },
+ });
+
+ const result = await tool.execute(args, {
+ toolCallId: generateUUID(),
+ messages: [],
+ });
+
+ setExecutionResults((prev) => ({
+ ...prev,
+ [selectedTool]: {
+ type: 'tool',
+ args,
+ result,
+ timestamp: Date.now(),
+ },
+ }));
+
+ onLog({
+ type: 'success',
+ message: `Tool executed successfully: ${selectedTool}`,
+ data: { result },
+ });
+
+ toast.success(`${selectedTool} completed successfully`);
+ } catch (err) {
+ onLog({
+ type: 'error',
+ message: `Tool execution failed: ${String(err)}`,
+ data: { tool: selectedTool, error: err },
+ });
+
+ toast.error(String(err));
+ }
+ };
+
+ const handleExecutePrompt = async (promptName: string) => {
+ if (!client) return;
+
+ try {
+ onLog({
+ type: 'info',
+ message: `Executing prompt: ${promptName}`,
+ data: { prompt: promptName },
+ });
+
+ const prompt = promptsMap[promptName];
+ let result: any;
+
+ if (prompt?.execute) {
+ result = await prompt.execute({});
+ } else {
+ result = await client.getPrompt(promptName, {});
+ }
+
+ setExecutionResults((prev) => ({
+ ...prev,
+ [promptName]: {
+ type: 'prompt',
+ result,
+ timestamp: Date.now(),
+ },
+ }));
+
+ onLog({
+ type: 'success',
+ message: `Prompt executed successfully: ${promptName}`,
+ data: { result },
+ });
+
+ toast.success(`${promptName} completed successfully`);
+ } catch (err) {
+ onLog({
+ type: 'error',
+ message: `Prompt execution failed: ${String(err)}`,
+ data: { prompt: promptName, error: err },
+ });
+
+ toast.error(String(err));
+ }
+ };
+
+ const handleReadResource = async (resourceUri: string) => {
+ if (!client) return;
+
+ try {
+ onLog({
+ type: 'info',
+ message: `Reading resource: ${resourceUri}`,
+ data: { resource: resourceUri },
+ });
+
+ const result = await client.readResource(resourceUri);
+
+ setExecutionResults((prev) => ({
+ ...prev,
+ [resourceUri]: {
+ type: 'resource',
+ result,
+ timestamp: Date.now(),
+ },
+ }));
+
+ onLog({
+ type: 'success',
+ message: `Resource read successfully: ${resourceUri}`,
+ data: { result },
+ });
+
+ toast.success(`${resourceUri} read successfully`);
+ } catch (err) {
+ onLog({
+ type: 'error',
+ message: `Resource read failed: ${String(err)}`,
+ data: { resource: resourceUri, error: err },
+ });
+
+ toast.error(String(err));
+ }
+ };
+
+ const copyResult = async (key: string) => {
+ const result = executionResults[key];
+ if (!result) return;
+
+ try {
+ await navigator.clipboard.writeText(safeStringify(result.result));
+ toast.success('Execution result copied to clipboard');
+ } catch (error) {
+ toast.error('Failed to copy result to clipboard');
+ }
+ };
+
+ const togglePromptExpanded = (promptName: string) => {
+ setExpandedPrompts((prev) => {
+ const newSet = new Set(prev);
+ if (newSet.has(promptName)) {
+ newSet.delete(promptName);
+ } else {
+ newSet.add(promptName);
+ }
+ return newSet;
+ });
+ };
+
+ const filteredPrompts = prompts.filter((prompt) =>
+ prompt.toLowerCase().includes(promptSearchQuery.toLowerCase()),
+ );
+
+ const filteredResources = resources.filter((resource) =>
+ resource.toLowerCase().includes(resourceSearchQuery.toLowerCase()),
+ );
+
+ return (
+
+
+
+
+
+
+ Debug Interface
+
+
+
+
+
+ Interactive debugging tools for MCP server capabilities
+
+
+
+
+ {/* Header with View Selector */}
+
+ {/* Tools View */}
+ {activeView === 'tools' && (
+
+
+
+
+
+
+ {selectedTool ? (
+ <>
+
+ {selectedTool}
+ >
+ ) : (
+ 'Select a Tool'
+ )}
+
+ {selectedTool && (
+
+ {toolsMap[selectedTool]?.description ||
+ 'No description available'}
+
+ )}
+
+
+ {selectedTool ? (
+
+ {/* Parameters Section */}
+
+
+ Parameters
+
+ {(() => {
+ const tool = toolsMap[selectedTool];
+ const paramWrapper = tool?.parameters;
+ const schema =
+ paramWrapper?.jsonSchema ?? paramWrapper;
+
+ if (!schema) {
+ return (
+
+
+ This tool requires no parameters.
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+ })()}
+
+
+ {/* Execution Button */}
+
+
+
+
+ {/* Results Section */}
+ {executionResults[selectedTool] && (
+
+
+
+ Execution Result
+
+
+
+ {new Date(
+ executionResults[selectedTool].timestamp,
+ ).toLocaleTimeString()}
+
+
+
+
+
+
+ {safeStringify(
+ executionResults[selectedTool].result,
+ )}
+
+
+
+ )}
+
+ ) : (
+
+
+
No Tool Selected
+
+ Choose a tool from the list to configure and execute it
+
+
+ )}
+
+
+
+ )}
+
+ {/* Prompts View */}
+ {activeView === 'prompts' && (
+
+
+
+
+ setPromptSearchQuery(e.target.value)}
+ className="pl-10"
+ />
+
+
+
+
+ {filteredPrompts.length === 0 ? (
+
+
+
+ No prompts found
+
+
+ ) : (
+ filteredPrompts.map((prompt) => {
+ const promptInfo = promptsMap[prompt];
+ const hasResult = executionResults[prompt];
+ const isExpanded = expandedPrompts.has(prompt);
+
+ return (
+
+
+ togglePromptExpanded(prompt)}
+ >
+
+
+
+ {isExpanded ? (
+
+ ) : (
+
+ )}
+ {prompt}
+
+
+ {hasResult && (
+
+ Executed
+
+ )}
+
+
+
+
+
+
+
+ {promptInfo?.description && (
+
+ {promptInfo.description}
+
+ )}
+
+ {hasResult && (
+
+
+
+ Result
+
+
+
+
+
+ {safeStringify(
+ executionResults[prompt].result,
+ )}
+
+
+
+ )}
+
+
+
+
+ );
+ })
+ )}
+
+
+ )}
+
+ {/* Resources View */}
+ {activeView === 'resources' && (
+
+
+
+
+ setResourceSearchQuery(e.target.value)}
+ className="pl-10"
+ />
+
+
+
+
+ {filteredResources.length === 0 ? (
+
+
+
+ No resources found
+
+
+ ) : (
+ filteredResources.map((resource) => {
+ const hasResult = executionResults[resource];
+
+ return (
+
+
+
+
+
{resource}
+
+ Resource URI
+
+
+
+ {hasResult && (
+
+ Loaded
+
+ )}
+
+
+
+
+ {hasResult && (
+
+
+
+ Content
+
+
+
+
+
+ {safeStringify(
+ executionResults[resource].result,
+ )}
+
+
+
+ )}
+
+
+ );
+ })
+ )}
+
+
+ )}
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/mcp-tools/index.tsx b/src/features/cap-studio/components/mcp-tools/index.tsx
new file mode 100644
index 00000000..31e233eb
--- /dev/null
+++ b/src/features/cap-studio/components/mcp-tools/index.tsx
@@ -0,0 +1,33 @@
+import { useParams, useSearchParams } from 'react-router-dom';
+import { useLocalCaps } from '../../hooks';
+import { DashboardHeader, DashboardLayout } from '../layout/dashboard-layout';
+import { Mcp } from './mcp';
+
+export function McpTools() {
+ const { id } = useParams();
+ const [searchParams] = useSearchParams();
+ const localCaps = useLocalCaps();
+
+ const cap = id ? localCaps.find((cap) => cap.id === id) : null;
+ const serverName = searchParams.get('server');
+
+ if (id && !cap) {
+ return (
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/mcp-tools/mcp.tsx b/src/features/cap-studio/components/mcp-tools/mcp.tsx
new file mode 100644
index 00000000..b3b5dd20
--- /dev/null
+++ b/src/features/cap-studio/components/mcp-tools/mcp.tsx
@@ -0,0 +1,564 @@
+import {
+ Activity,
+ ArrowLeft,
+ BrushCleaning,
+ Copy,
+ Plug,
+ RefreshCw,
+ Settings,
+ Terminal,
+ Unplug,
+ Zap,
+} from 'lucide-react';
+import { useCallback, useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { toast } from 'sonner';
+import {
+ Button,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Input,
+ ScrollArea,
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/shared/components/ui';
+import {
+ closeNuwaMCPClient,
+ createNuwaMCPClient,
+} from '@/shared/services/mcp-client';
+import type { McpTransportType, NuwaMCPClient } from '@/shared/types';
+import type { LocalCap } from '../../types';
+import { DashboardGrid } from '../layout/dashboard-layout';
+import { McpDebugPanel } from './debug-panel';
+
+interface LogEntry {
+ id: string;
+ type: 'info' | 'error' | 'success' | 'warning';
+ message: string;
+ timestamp: number;
+ data?: any;
+}
+
+interface ConnectionConfig {
+ url: string;
+ transport: McpTransportType | '';
+ name?: string;
+}
+
+interface McpProps {
+ cap?: LocalCap | null;
+ serverName?: string | null;
+}
+
+export function Mcp({ cap, serverName }: McpProps) {
+ const navigate = useNavigate();
+ const [url, setUrl] = useState('http://localhost:8080/mcp');
+ const [transport, setTransport] = useState('');
+ const [connected, setConnected] = useState(false);
+ const [connecting, setConnecting] = useState(false);
+ const [client, setClient] = useState(null);
+ const [tools, setTools] = useState([]);
+ const [toolsMap, setToolsMap] = useState>({});
+ const [prompts, setPrompts] = useState([]);
+ const [promptsMap, setPromptsMap] = useState>({});
+ const [resources, setResources] = useState([]);
+ const [logs, setLogs] = useState([]);
+ const [serverInfo, setServerInfo] = useState(null);
+
+ const pushLog = useCallback((entry: Omit) => {
+ setLogs((prev) =>
+ [
+ {
+ ...entry,
+ id: `log_${Date.now()}_${Math.random()}`,
+ timestamp: Date.now(),
+ },
+ ...prev,
+ ].slice(0, 100),
+ ); // Keep last 100 logs
+ }, []);
+
+ // Auto-populate connection details when server is specified via URL parameter
+ useEffect(() => {
+ if (cap && serverName && cap.capData.core.mcpServers[serverName]) {
+ const serverConfig = cap.capData.core.mcpServers[serverName];
+ setUrl(serverConfig.url);
+ setTransport(serverConfig.transport);
+ }
+ }, [cap, serverName, pushLog]);
+
+ const handleConnect = useCallback(async () => {
+ if (connecting) return;
+
+ setConnecting(true);
+ try {
+ pushLog({
+ type: 'info',
+ message: `Connecting to ${url} with transport: ${transport || 'httpStream'}`,
+ });
+
+ const newClient = await createNuwaMCPClient(url, transport || undefined);
+
+ setClient(newClient);
+ setConnected(true);
+
+ pushLog({
+ type: 'success',
+ message: 'Successfully connected to MCP server',
+ });
+
+ // Fetch server capabilities
+ await fetchServerCapabilities(newClient);
+
+ toast.success(`Successfully connected to ${url}`);
+ } catch (err) {
+ pushLog({
+ type: 'error',
+ message: `Connection failed: ${String(err)}`,
+ });
+
+ toast.error(String(err));
+
+ await closeNuwaMCPClient(url);
+ } finally {
+ setConnecting(false);
+ }
+ }, [connecting, url, transport, pushLog]);
+
+ const fetchServerCapabilities = async (mcpClient: NuwaMCPClient) => {
+ // Fetch tools
+ try {
+ const toolsList = await mcpClient.tools();
+ const toolNames = Object.keys(toolsList);
+ setTools(toolNames);
+ setToolsMap(toolsList);
+ pushLog({
+ type: 'info',
+ message: `Discovered ${toolNames.length} tools: ${toolNames.join(', ')}`,
+ data: { tools: toolNames },
+ });
+ } catch (err) {
+ pushLog({
+ type: 'warning',
+ message: `No tools available: ${String(err)}`,
+ });
+ }
+
+ // Fetch prompts
+ try {
+ const promptsList = await mcpClient.prompts();
+ const promptNames = Object.keys(promptsList);
+ setPrompts(promptNames);
+ setPromptsMap(promptsList);
+ pushLog({
+ type: 'info',
+ message: `Discovered ${promptNames.length} prompts: ${promptNames.join(', ')}`,
+ data: { prompts: promptNames },
+ });
+ } catch (err) {
+ pushLog({
+ type: 'warning',
+ message: `No prompts available: ${String(err)}`,
+ });
+ }
+
+ // Fetch resources
+ try {
+ const resourcesList = await mcpClient.resources();
+ const resourceNames = Object.keys(resourcesList);
+ setResources(resourceNames);
+ pushLog({
+ type: 'info',
+ message: `Discovered ${resourceNames.length} resources: ${resourceNames.join(', ')}`,
+ data: { resources: resourceNames },
+ });
+ } catch (err) {
+ pushLog({
+ type: 'warning',
+ message: `No resources available: ${String(err)}`,
+ });
+ }
+
+ // Set server info
+ setServerInfo({
+ url,
+ transport: transport || 'httpStream',
+ toolCount: tools.length,
+ promptCount: prompts.length,
+ resourceCount: resources.length,
+ connectedAt: Date.now(),
+ });
+ };
+
+ const handleDisconnect = async () => {
+ if (!client) return;
+
+ try {
+ await client.close();
+ setClient(null);
+ setConnected(false);
+ setTools([]);
+ setToolsMap({});
+ setPrompts([]);
+ setPromptsMap({});
+ setResources([]);
+ setServerInfo(null);
+
+ pushLog({
+ type: 'info',
+ message: 'Disconnected from MCP server',
+ });
+
+ toast.success('Successfully disconnected from MCP server');
+ } catch (err) {
+ pushLog({
+ type: 'error',
+ message: `Disconnect error: ${String(err)}`,
+ });
+ }
+ };
+
+ const handlePing = async () => {
+ if (!client) {
+ pushLog({ type: 'error', message: 'Not connected to any server' });
+ return;
+ }
+
+ try {
+ // Try ping on raw client if available
+ if (client.raw && typeof client.raw.ping === 'function') {
+ await client.raw.ping();
+ pushLog({ type: 'success', message: 'Server ping successful' });
+ } else {
+ // Fallback: try a simple prompts() call as a health check
+ await client.prompts();
+ pushLog({
+ type: 'success',
+ message: 'Server health check successful',
+ });
+ }
+
+ toast.success('MCP server ping successful');
+ } catch (err) {
+ pushLog({ type: 'error', message: `Server ping failed: ${String(err)}` });
+
+ toast.error(String(err));
+ }
+ };
+
+ const loadConnection = (config: ConnectionConfig) => {
+ setUrl(config.url);
+ setTransport(
+ config.transport && ['httpStream', 'sse'].includes(config.transport)
+ ? config.transport
+ : '',
+ );
+ };
+
+ const clearLogs = () => {
+ setLogs([]);
+ };
+
+ const copyLogs = async () => {
+ const logText = logs
+ .map(
+ (log) =>
+ `[${new Date(log.timestamp).toLocaleTimeString()}] ${log.type.toUpperCase()}: ${log.message}`,
+ )
+ .join('\n');
+
+ try {
+ await navigator.clipboard.writeText(logText);
+ toast.success('Debug logs copied to clipboard');
+ } catch (error) {
+ toast.error('Failed to copy logs to clipboard');
+ }
+ };
+
+ return (
+
+ {/* Header */}
+
+
+
MCP Tools
+
+ Test and debug Model Context Protocol connections and tools
+
+
+
+
+
+
+ {/* Connection Panel */}
+
+
+
+
+ Connection
+
+
+ Connect to an MCP server to access tools and resources
+
+
+
+ {/* Manual Connection */}
+
+
+
Server URL
+
setUrl(e.target.value)}
+ placeholder="http://localhost:8080/mcp"
+ disabled={connecting || connected}
+ />
+
+
+
+
Transport
+
+
+
+
+ {/* Connection Actions */}
+
+ {connected ? (
+ <>
+
+
+ >
+ ) : (
+
+ )}
+
+
+
+
+ {/* Server Info */}
+ {/* Server Status & Stats */}
+
+
+
+
+ Server Status
+
+
+ Information about the connected MCP server
+
+
+
+ {serverInfo ? (
+
+
+
+
URL:
+
+ {serverInfo.url}
+
+
+
+
Transport:
+
{serverInfo.transport}
+
+
+
+
+
+
+ {tools.length}
+
+
+ Tools
+
+
+
+
+ {prompts.length}
+
+
+ Prompts
+
+
+
+
+ {resources.length}
+
+
+ Resources
+
+
+
+
+
+ Connected: {new Date(serverInfo.connectedAt).toLocaleString()}
+
+
+ ) : (
+
+
+
No Server Connected
+
+ Connect to a server to view status information
+
+
+ )}
+
+
+
+
+ {/* Debug Interface */}
+ {connected && client && (
+
+ )}
+
+ {/* Debug Logs */}
+
+
+
+
+
+
+ Debug Logs
+
+
+ Real-time logging of MCP operations
+
+
+
+
+
+
+
+
+
+
+ {logs.length === 0 ? (
+
+
+
No debug logs yet
+
Connect to a server to see logs
+
+ ) : (
+
+ {logs.map((log) => {
+ const getLogColor = (type: string) => {
+ switch (type) {
+ case 'error':
+ return 'text-red-600';
+ case 'warning':
+ return 'text-yellow-600';
+ case 'success':
+ return 'text-green-600';
+ case 'info':
+ return 'text-blue-600';
+ default:
+ return 'text-foreground';
+ }
+ };
+
+ const getLogIcon = (type: string) => {
+ switch (type) {
+ case 'error':
+ return '❌';
+ case 'warning':
+ return '⚠️';
+ case 'success':
+ return '✅';
+ case 'info':
+ return 'ℹ️';
+ default:
+ return '•';
+ }
+ };
+
+ return (
+
+
+ {new Date(log.timestamp).toLocaleTimeString()}
+
+ {getLogIcon(log.type)}
+
+ {log.message}
+
+
+ );
+ })}
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/model-selector/index.tsx b/src/features/cap-studio/components/model-selector/index.tsx
new file mode 100644
index 00000000..c0a805f7
--- /dev/null
+++ b/src/features/cap-studio/components/model-selector/index.tsx
@@ -0,0 +1,293 @@
+import { Description, DialogTitle } from '@radix-ui/react-dialog';
+import {
+ ArrowDownAZ,
+ ArrowDownNarrowWide,
+ ArrowDownWideNarrow,
+ ArrowUpDown,
+ Bot,
+ Search,
+} from 'lucide-react';
+import type React from 'react';
+import { useMemo, useState } from 'react';
+import { Button } from '@/shared/components';
+import {
+ Dialog,
+ DialogContent,
+ DialogTrigger,
+} from '@/shared/components/ui/dialog';
+import { Input } from '@/shared/components/ui/input';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+} from '@/shared/components/ui/select';
+import { SidebarInset, SidebarProvider } from '@/shared/components/ui/sidebar';
+import { Skeleton } from '@/shared/components/ui/skeleton';
+import { useLocale } from '@/shared/locales/use-locale';
+import type { CapModel } from '@/shared/types/cap';
+import { useAvailableModels, useSelectedModel } from '../../hooks';
+import { generateProviders } from '../../utils';
+import { ModelCard } from './model-card';
+import { ModelSelectorSidebar } from './model-selector-sidebar';
+
+export const LLMModelSelector = ({ onClose }: { onClose: () => void }) => {
+ const { t } = useLocale();
+ const { models, loading, error } = useAvailableModels();
+ const { selectedModel, setSelectedModel } = useSelectedModel();
+
+ const [selectedTab, setSelectedTab] = useState('all');
+ const [selectedProvider, setSelectedProvider] = useState(null);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [sortBy, setSortBy] = useState<'name' | 'price-asc' | 'price-desc'>(
+ 'name',
+ );
+
+ // Generate categories and providers from models
+ const { providers } = useMemo(() => {
+ if (!models) return { providers: [] };
+ return generateProviders(models);
+ }, [models]);
+
+ const getFilteredModels = () => {
+ if (!models) return [];
+
+ let filtered = [...models];
+
+ if (selectedTab === 'provider' && selectedProvider) {
+ filtered = filtered.filter((model) => {
+ const modelProviderId = model.providerName
+ .toLowerCase()
+ .replace(/\s+/g, '');
+ return modelProviderId === selectedProvider;
+ });
+ }
+
+ if (searchQuery) {
+ const keywords = searchQuery
+ .trim()
+ .split(/\s+/)
+ .filter((keyword) => keyword.length > 0);
+
+ filtered = filtered.filter((model) => {
+ const modelName = model.name.toLowerCase();
+ const providerName = model.providerName.toLowerCase();
+ return keywords.every(
+ (keyword) =>
+ modelName.includes(keyword.toLowerCase()) ||
+ providerName.includes(keyword.toLowerCase()),
+ );
+ });
+ }
+
+ // Apply sorting
+ filtered.sort((a, b) => {
+ switch (sortBy) {
+ case 'name':
+ return a.name.localeCompare(b.name);
+ case 'price-asc': {
+ const aPrice =
+ a.pricing.input_per_million_tokens +
+ a.pricing.output_per_million_tokens;
+ const bPrice =
+ b.pricing.input_per_million_tokens +
+ b.pricing.output_per_million_tokens;
+ return aPrice - bPrice;
+ }
+ case 'price-desc': {
+ const aPrice =
+ a.pricing.input_per_million_tokens +
+ a.pricing.output_per_million_tokens;
+ const bPrice =
+ b.pricing.input_per_million_tokens +
+ b.pricing.output_per_million_tokens;
+ return bPrice - aPrice;
+ }
+ default:
+ return 0;
+ }
+ });
+
+ return filtered;
+ };
+
+ const handleModelSelect = (model: CapModel) => {
+ setSelectedModel(model);
+ onClose();
+ };
+
+ const handleTabChange = (tab: string) => {
+ setSelectedTab(tab);
+ };
+
+ const getMainContent = () => {
+ if (selectedTab === 'provider' && selectedProvider) {
+ const filteredModels = getFilteredModels();
+ return renderModelGrid(filteredModels);
+ }
+
+ const filteredModels = getFilteredModels();
+ return renderModelGrid(filteredModels);
+ };
+
+ const renderModelGrid = (modelList: CapModel[]) => (
+
+ {modelList.map((model) => (
+
+ ))}
+
+ );
+
+ const getTitle = () => {
+ if (selectedTab === 'provider') {
+ if (selectedProvider) {
+ const provider = providers.find((p) => p.id === selectedProvider);
+ return t('aiProvider.modelDialog.title.providerModels', {
+ provider: provider?.name,
+ });
+ }
+ return t('aiProvider.modelDialog.title.providers');
+ }
+ return t('aiProvider.modelDialog.title.allModels');
+ };
+
+ if (loading) {
+ return (
+
+
+
+
+
+
+
+
+
+ {Array.from({ length: 6 }, () => (
+
+ ))}
+
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
+ {t('aiProvider.modelDialog.error.failedToLoad')}
+
+
{error.message}
+
+
+ );
+ }
+
+ if (!models || models.length === 0) {
+ return (
+
+
+ {t('aiProvider.modelDialog.error.noModelsAvailable')}
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ {/* Header */}
+
+
{getTitle()}
+
+
+
+
+
+ setSearchQuery(e.target.value)}
+ className="pl-10"
+ />
+
+
+
+
+ {/* Content */}
+
+ {getMainContent()}
+
+
+
+
+ );
+};
+
+export const ModelSelectorDialog: React.FC = () => {
+ const { t } = useLocale();
+ const [open, setOpen] = useState(false);
+
+ return (
+
+ );
+};
diff --git a/src/features/ai-provider/components/model-card.tsx b/src/features/cap-studio/components/model-selector/model-card.tsx
similarity index 69%
rename from src/features/ai-provider/components/model-card.tsx
rename to src/features/cap-studio/components/model-selector/model-card.tsx
index 2143dbc3..fef49603 100644
--- a/src/features/ai-provider/components/model-card.tsx
+++ b/src/features/cap-studio/components/model-selector/model-card.tsx
@@ -1,7 +1,5 @@
import { motion } from 'framer-motion';
-import { StarIcon } from 'lucide-react';
import type React from 'react';
-import { Button } from '@/shared/components/ui/button';
import {
Card,
CardFooter,
@@ -9,16 +7,15 @@ import {
CardTitle,
} from '@/shared/components/ui/card';
import { useLocale } from '@/shared/locales/use-locale';
+import type { CapModel } from '@/shared/types/cap';
import { cn } from '@/shared/utils';
-import { useFavoriteModels } from '../hooks/use-favorite-models';
-import type { Model } from '../types';
-import { getModelName, getProviderName } from '../utils';
+import { getModelName, getProviderName } from '../../utils';
import { ProviderAvatar } from './provider-avatar';
interface ModelCardProps {
- model: Model;
+ model: CapModel;
isSelected: boolean;
- onClick: (model: Model) => void;
+ onClick: (model: CapModel) => void;
}
export const ModelCard: React.FC = ({
@@ -27,7 +24,6 @@ export const ModelCard: React.FC = ({
onClick,
}) => {
const { t } = useLocale();
- const { isFavorite, toggleFavorite } = useFavoriteModels();
return (
@@ -51,24 +47,6 @@ export const ModelCard: React.FC = ({
-
diff --git a/src/features/ai-provider/components/model-selector-sidebar.tsx b/src/features/cap-studio/components/model-selector/model-selector-sidebar.tsx
similarity index 70%
rename from src/features/ai-provider/components/model-selector-sidebar.tsx
rename to src/features/cap-studio/components/model-selector/model-selector-sidebar.tsx
index 6a69a57e..72e9fb71 100644
--- a/src/features/ai-provider/components/model-selector-sidebar.tsx
+++ b/src/features/cap-studio/components/model-selector/model-selector-sidebar.tsx
@@ -1,4 +1,4 @@
-import { List, Settings, StarIcon } from 'lucide-react';
+import { List } from 'lucide-react';
import type React from 'react';
import {
Sidebar,
@@ -10,9 +10,8 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from '@/shared/components/ui/sidebar';
-import { useDevMode } from '@/shared/hooks';
import { useLocale } from '@/shared/locales/use-locale';
-import type { Provider } from '../utils';
+import type { Provider } from '../../utils';
import { ProviderAvatar } from './provider-avatar';
interface ModelSelectorSidebarProps {
@@ -31,7 +30,6 @@ export const ModelSelectorSidebar: React.FC = ({
onProviderChange,
}) => {
const { t } = useLocale();
- const isDevMode = useDevMode();
const handleTabChange = (tab: string) => {
onTabChange(tab);
@@ -46,26 +44,6 @@ export const ModelSelectorSidebar: React.FC = ({
- {isDevMode && (
-
- handleTabChange('auto')}
- >
-
- {t('aiProvider.sidebar.tabs.auto')}
-
-
- )}
-
- handleTabChange('favorite')}
- >
-
- {t('aiProvider.sidebar.tabs.favorite')}
-
-
= ({
{t('aiProvider.sidebar.tabs.providers')}
-
+
{providers.map((provider) => (
void;
+ onTest?: () => void;
+ onSubmit?: () => void;
+ onUpdate?: () => void;
+}
+
+export function CapCard({
+ cap,
+ onEdit,
+ onTest,
+ onSubmit,
+ onUpdate,
+}: CapCardProps) {
+ const { deleteCap } = useLocalCapsHandler();
+ const [showDeleteDialog, setShowDeleteDialog] = useState(false);
+
+ const handleDelete = () => {
+ deleteCap(cap.id);
+ setShowDeleteDialog(false);
+ };
+
+ const handleCopyCid = async () => {
+ if (cap.cid) {
+ await navigator.clipboard.writeText(cap.cid);
+ toast.success(`Published CID copied: ${cap.cid}`);
+ }
+ };
+
+ const mcpServerCount = Object.keys(cap.capData.core.mcpServers).length;
+ const lastUpdated = formatDistanceToNow(new Date(cap.updatedAt), {
+ addSuffix: true,
+ });
+
+ return (
+
+
+
+
+
+
+
+
+ {cap.capData.metadata.displayName.charAt(0)}
+
+
+
+
+
+
+
+ {cap.capData.metadata.displayName}
+
+
+
+
+ {cap.capData.metadata.description}
+
+
+
+
+
+ {cap.capData.core.model.name}
+
+
+
+ {mcpServerCount} MCP server{mcpServerCount !== 1 ? 's' : ''}
+
+
+
+ {lastUpdated}
+
+
+
+
+
+
+
+
+
+
+
+ {cap.status === 'draft' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+ {cap.status === 'submitted' && cap.cid && (
+ <>
+
+
+ Copy Published CID
+
+
+ >
+ )}
+
+ setShowDeleteDialog(true)}
+ className="text-destructive"
+ >
+
+ Delete Cap
+
+
+
+
+
+
+
+
+
+
+ Delete Cap
+
+ Are you sure you want to delete @{cap.capData.idName}? This action
+ cannot be undone.
+
+
+
+ Cancel
+
+ Delete
+
+
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/components/my-caps/index.tsx b/src/features/cap-studio/components/my-caps/index.tsx
new file mode 100644
index 00000000..e86b2c93
--- /dev/null
+++ b/src/features/cap-studio/components/my-caps/index.tsx
@@ -0,0 +1,188 @@
+import { Plus, Search } from 'lucide-react';
+import { useMemo, useState } from 'react';
+import {
+ Button,
+ Card,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Input,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from '@/shared/components/ui';
+import { useLocalCaps } from '../../hooks';
+import type { LocalCap } from '../../types';
+import { DashboardGrid } from '../layout/dashboard-layout';
+import { CapCard } from './cap-card';
+
+interface MyCapsProps {
+ onEditCap?: (cap: LocalCap) => void;
+ onTestCap?: (cap: LocalCap) => void;
+ onSubmitCap?: (cap: LocalCap) => void;
+ onCreateNew?: () => void;
+}
+
+export function MyCaps({
+ onEditCap,
+ onTestCap,
+ onSubmitCap,
+ onCreateNew,
+}: MyCapsProps) {
+ const localCaps = useLocalCaps();
+ const [searchQuery, setSearchQuery] = useState('');
+
+ // Get caps array
+ const capsArray = localCaps;
+
+ // Separate caps by status
+ const draftCaps = useMemo(() => {
+ const drafts = capsArray.filter((cap) => cap.status === 'draft');
+ if (!searchQuery) return drafts;
+
+ const query = searchQuery.toLowerCase();
+ return drafts.filter(
+ (cap) =>
+ cap.capData.metadata.displayName.toLowerCase().includes(query) ||
+ cap.capData.metadata.description.toLowerCase().includes(query) ||
+ cap.capData.metadata.tags.some((tag) =>
+ tag.toLowerCase().includes(query),
+ ),
+ );
+ }, [capsArray, searchQuery]);
+
+ const publishedCaps = useMemo(() => {
+ const published = capsArray.filter((cap) => cap.status === 'submitted');
+ if (!searchQuery) return published;
+
+ const query = searchQuery.toLowerCase();
+ return published.filter(
+ (cap) =>
+ cap.capData.metadata.displayName.toLowerCase().includes(query) ||
+ cap.capData.metadata.description.toLowerCase().includes(query) ||
+ cap.capData.metadata.tags.some((tag) =>
+ tag.toLowerCase().includes(query),
+ ),
+ );
+ }, [capsArray, searchQuery]);
+
+ if (capsArray.length === 0) {
+ return (
+
+
+
+
+ No Caps Yet
+
+
+ You haven't created any caps yet. Start building your first
+ capability to get started with cap development.
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Header with controls */}
+
+
+
+
+
+ setSearchQuery(e.target.value)}
+ className="pl-10"
+ />
+
+
+
+ {/* Tabs for draft and published */}
+
+
+ Drafts ({draftCaps.length})
+
+ Published ({publishedCaps.length})
+
+
+
+
+ {draftCaps.length === 0 ? (
+
+
+
+ {searchQuery ? 'No matching draft caps' : 'No draft caps'}
+
+
+ {searchQuery
+ ? 'Try adjusting your search criteria'
+ : 'Create your first cap to get started'}
+
+
+
+ ) : (
+
+ {draftCaps.map((cap) => (
+ onEditCap?.(cap)}
+ onTest={() => onTestCap?.(cap)}
+ onSubmit={() => onSubmitCap?.(cap)}
+ />
+ ))}
+
+ )}
+
+
+
+ {publishedCaps.length === 0 ? (
+
+
+
+ {searchQuery
+ ? 'No matching published caps'
+ : 'No published caps'}
+
+
+ {searchQuery
+ ? 'Try adjusting your search criteria'
+ : 'Submit your drafts to see them here'}
+
+
+
+ ) : (
+
+ {publishedCaps.map((cap) => (
+ onEditCap?.(cap)}
+ onTest={() => onTestCap?.(cap)}
+ onUpdate={() => onSubmitCap?.(cap)}
+ />
+ ))}
+
+ )}
+
+
+
+ );
+}
diff --git a/src/features/cap-studio/hooks/index.ts b/src/features/cap-studio/hooks/index.ts
new file mode 100644
index 00000000..c6fc3588
--- /dev/null
+++ b/src/features/cap-studio/hooks/index.ts
@@ -0,0 +1,3 @@
+export { useAvailableModels } from './use-available-models';
+export { useLocalCaps } from './use-local-caps';
+export { useSelectedModel } from './use-selected-model';
diff --git a/src/features/ai-provider/hooks/use-available-models.ts b/src/features/cap-studio/hooks/use-available-models.ts
similarity index 91%
rename from src/features/ai-provider/hooks/use-available-models.ts
rename to src/features/cap-studio/hooks/use-available-models.ts
index 9d2b5dda..ed978561 100644
--- a/src/features/ai-provider/hooks/use-available-models.ts
+++ b/src/features/cap-studio/hooks/use-available-models.ts
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
-import { ModelStateStore } from '../stores';
+import { ModelStateStore } from '../stores/model-stores';
export function useAvailableModels() {
const models = ModelStateStore((state) => state.availableModels);
diff --git a/src/features/cap-studio/hooks/use-edit-form.ts b/src/features/cap-studio/hooks/use-edit-form.ts
new file mode 100644
index 00000000..c8a2b2f0
--- /dev/null
+++ b/src/features/cap-studio/hooks/use-edit-form.ts
@@ -0,0 +1,180 @@
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useEffect, useState } from 'react';
+import { useForm } from 'react-hook-form';
+import { useNavigate } from 'react-router-dom';
+import { toast } from 'sonner';
+import { z } from 'zod';
+import { useAuth } from '@/features/auth/hooks';
+import type { CapMcpServerConfig } from '@/shared/types/cap';
+import type { LocalCap } from '../types';
+import { useLocalCapsHandler } from './use-local-caps-handler';
+import { useSelectedModel } from './use-selected-model';
+
+const capSchema = z.object({
+ idName: z
+ .string()
+ .min(6, 'Name must be at least 6 characters')
+ .max(20, 'Name must be at most 20 characters')
+ .regex(
+ /^[a-zA-Z0-9_]+$/,
+ 'Name must contain only letters, numbers, and underscores',
+ ),
+ displayName: z
+ .string()
+ .min(1, 'Display name is required')
+ .max(50, 'Display name too long'),
+ description: z
+ .string()
+ .min(20, 'Description must be at least 10 characters')
+ .max(500, 'Description too long'),
+ tags: z.array(z.string()),
+ prompt: z.object({
+ value: z.string(),
+ suggestions: z.array(z.string()).optional(),
+ }),
+});
+
+type CapFormData = z.infer;
+
+interface UseEditFormProps {
+ editingCap?: LocalCap;
+}
+
+export const useEditForm = ({ editingCap }: UseEditFormProps) => {
+ const navigate = useNavigate();
+ const { did } = useAuth();
+ const { createCap, updateCap } = useLocalCapsHandler();
+ const { selectedModel } = useSelectedModel();
+ const [isSaving, setIsSaving] = useState(false);
+ const [mcpServers, setMcpServers] = useState<
+ Record
+ >({});
+
+ const form = useForm({
+ resolver: zodResolver(capSchema),
+ mode: 'onChange',
+ defaultValues: {
+ idName: editingCap?.capData.idName || '',
+ displayName: editingCap?.capData.metadata.displayName || '',
+ description: editingCap?.capData.metadata.description || '',
+ tags: editingCap?.capData.metadata.tags || [],
+ prompt: {
+ value:
+ typeof editingCap?.capData.core.prompt === 'string'
+ ? editingCap.capData.core.prompt
+ : editingCap?.capData.core.prompt?.value || '',
+ suggestions:
+ typeof editingCap?.capData.core.prompt === 'object'
+ ? editingCap.capData.core.prompt.suggestions
+ : [],
+ },
+ },
+ });
+
+ useEffect(() => {
+ if (editingCap) {
+ setMcpServers(editingCap.capData.core.mcpServers || {});
+ }
+ }, [editingCap]);
+
+ const handleUpdateMcpServers = (
+ servers: Record,
+ ) => {
+ setMcpServers(servers);
+ };
+
+ const handleUpdateCap = async (editingCap: LocalCap, data: CapFormData) => {
+ // Update existing cap
+ updateCap(editingCap.id, {
+ capData: {
+ id: `${did}:${data.idName}`,
+ authorDID: did || '',
+ idName: data.idName,
+ metadata: {
+ displayName: data.displayName,
+ description: data.description,
+ tags: data.tags,
+ submittedAt: 0,
+ thumbnail: null,
+ },
+ core: {
+ prompt: data.prompt,
+ model: selectedModel,
+ mcpServers,
+ },
+ },
+ });
+
+ toast.success(`${data.displayName} has been updated successfully`);
+
+ navigate('/cap-studio');
+ };
+
+ const handleCreateCap = async (data: CapFormData) => {
+ // Create new cap
+ createCap({
+ id: `${did}:${data.idName}`,
+ authorDID: did || '',
+ idName: data.idName,
+ metadata: {
+ displayName: data.displayName,
+ description: data.description,
+ tags: data.tags,
+ submittedAt: 0,
+ thumbnail: null,
+ },
+ core: {
+ prompt: data.prompt,
+ model: selectedModel,
+ mcpServers,
+ },
+ });
+
+ toast.success(`${data.displayName} has been created successfully`);
+
+ navigate('/cap-studio');
+ };
+
+ const handleFormSave = async (data: CapFormData) => {
+ // Trigger validation for all fields
+ const isValid = await form.trigger();
+
+ if (!isValid) {
+ toast.warning('Please fix all validation errors before saving');
+ return;
+ }
+
+ if (!selectedModel) {
+ toast.warning('Please select a model for this cap');
+ return;
+ }
+
+ setIsSaving(true);
+ try {
+ if (editingCap) {
+ // Update existing cap
+ handleUpdateCap(editingCap, data);
+ } else {
+ handleCreateCap(data);
+ }
+ } catch (error) {
+ toast.error('Failed to save cap. Please try again.');
+ } finally {
+ setIsSaving(false);
+ }
+ };
+
+ const handleFormCancel = () => {
+ navigate('/cap-studio');
+ };
+
+ return {
+ form,
+ handleFormSave,
+ handleFormCancel,
+ handleUpdateMcpServers,
+ selectedModel,
+ mcpServers,
+ isSaving,
+ };
+};
diff --git a/src/features/cap-studio/hooks/use-local-caps-handler.ts b/src/features/cap-studio/hooks/use-local-caps-handler.ts
new file mode 100644
index 00000000..c1997e1d
--- /dev/null
+++ b/src/features/cap-studio/hooks/use-local-caps-handler.ts
@@ -0,0 +1,11 @@
+import { CapStudioStore } from '../stores/cap-studio-stores';
+
+export const useLocalCapsHandler = () => {
+ const { createCap, updateCap, deleteCap } = CapStudioStore();
+
+ return {
+ createCap,
+ updateCap,
+ deleteCap,
+ };
+};
diff --git a/src/features/cap-studio/hooks/use-local-caps.ts b/src/features/cap-studio/hooks/use-local-caps.ts
new file mode 100644
index 00000000..74e676a8
--- /dev/null
+++ b/src/features/cap-studio/hooks/use-local-caps.ts
@@ -0,0 +1,5 @@
+import { CapStudioStore } from '../stores/cap-studio-stores';
+
+export const useLocalCaps = () => {
+ return CapStudioStore((state) => state.localCaps);
+};
diff --git a/src/features/ai-provider/hooks/use-selected-model.ts b/src/features/cap-studio/hooks/use-selected-model.ts
similarity index 81%
rename from src/features/ai-provider/hooks/use-selected-model.ts
rename to src/features/cap-studio/hooks/use-selected-model.ts
index aea06b7b..4403bcd2 100644
--- a/src/features/ai-provider/hooks/use-selected-model.ts
+++ b/src/features/cap-studio/hooks/use-selected-model.ts
@@ -1,4 +1,4 @@
-import { ModelStateStore } from '../stores';
+import { ModelStateStore } from '../stores/model-stores';
export function useSelectedModel() {
const selectedModel = ModelStateStore((state) => state.selectedModel);
diff --git a/src/features/cap-studio/hooks/use-submit-cap.ts b/src/features/cap-studio/hooks/use-submit-cap.ts
new file mode 100644
index 00000000..533f85cc
--- /dev/null
+++ b/src/features/cap-studio/hooks/use-submit-cap.ts
@@ -0,0 +1,52 @@
+import { useCallback } from 'react';
+import { useCapKit } from '@/shared/hooks/use-capkit';
+import type { Cap } from '@/shared/types/cap';
+
+interface CapSubmitResponse {
+ success: boolean;
+ capId?: string;
+ message: string;
+ errors?: string[];
+}
+
+export const useSubmitCap = () => {
+ const { capKit } = useCapKit();
+
+ const submitCap = useCallback(
+ async (capData: Cap): Promise => {
+ try {
+ if (!capKit) {
+ throw new Error('Failed to initialize CapKit');
+ }
+
+ // Register the capability using CapKit
+ const cid = await capKit.registerCap(
+ capData.idName,
+ capData.metadata.description,
+ capData,
+ );
+
+ return {
+ success: true,
+ capId: cid,
+ message: `Capability "@${capData.idName}" registered successfully with CID: ${cid}`,
+ };
+ } catch (error) {
+ const errorMessage =
+ error instanceof Error
+ ? error.message
+ : 'Failed to submit capability';
+ return {
+ success: false,
+ message: errorMessage,
+ errors: [errorMessage],
+ };
+ }
+ },
+ [capKit],
+ );
+
+ return {
+ submitCap,
+ };
+};
diff --git a/src/features/cap-studio/hooks/use-submit-form.ts b/src/features/cap-studio/hooks/use-submit-form.ts
new file mode 100644
index 00000000..65a44565
--- /dev/null
+++ b/src/features/cap-studio/hooks/use-submit-form.ts
@@ -0,0 +1,140 @@
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useState } from 'react';
+import { useForm } from 'react-hook-form';
+import { useNavigate } from 'react-router-dom';
+import { toast } from 'sonner';
+import { z } from 'zod';
+import type { CapThumbnail } from '@/shared/types/cap';
+import { useLocalCapsHandler } from '../hooks/use-local-caps-handler';
+import { useSubmitCap } from '../hooks/use-submit-cap';
+import type { LocalCap } from '../types';
+
+const submitSchema = z.object({
+ homepage: z.string().url('Must be a valid URL').optional().or(z.literal('')),
+ repository: z
+ .string()
+ .url('Must be a valid URL')
+ .optional()
+ .or(z.literal('')),
+});
+
+export type SubmitFormData = z.infer;
+
+interface UseSubmitFormProps {
+ cap: LocalCap;
+}
+
+export const useSubmitForm = ({ cap }: UseSubmitFormProps) => {
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [showConfirmDialog, setShowConfirmDialog] = useState(false);
+ const [thumbnail, setThumbnail] = useState(null);
+ const navigate = useNavigate();
+ const { updateCap } = useLocalCapsHandler();
+ const { submitCap } = useSubmitCap();
+
+ const form = useForm({
+ resolver: zodResolver(submitSchema),
+ defaultValues: {
+ homepage: '',
+ repository: '',
+ },
+ });
+
+ const watchedData = form.watch();
+
+ const handleCancel = () => {
+ navigate('/cap-studio');
+ };
+
+ const processConfirmedSubmit = async (submitFormData: SubmitFormData) => {
+ try {
+ const capWithSubmitFormData = {
+ ...cap.capData,
+ metadata: {
+ ...cap.capData.metadata,
+ homepage: submitFormData.homepage || undefined,
+ repository: submitFormData.repository || undefined,
+ submittedAt: Date.now(),
+ thumbnail,
+ },
+ };
+
+ // make the submission
+ const result = await submitCap(capWithSubmitFormData);
+
+ if (result.success) {
+ // update cap status to submitted
+ updateCap(cap.id, {
+ status: 'submitted',
+ cid: result.capId,
+ capData: capWithSubmitFormData,
+ });
+
+ toast.success(result.message);
+
+ navigate('/cap-studio');
+ } else {
+ throw new Error(result.message);
+ }
+ } catch (error) {
+ const errorMessage =
+ error instanceof Error
+ ? error.message
+ : 'Failed to submit cap. Please try again.';
+ toast.error(errorMessage);
+
+ navigate('/cap-studio');
+ }
+ };
+
+ const handleFormSubmit = async () => {
+ // Trigger validation and show errors
+ const isValid = await form.trigger();
+
+ if (!isValid) {
+ // Form will show validation errors automatically
+ return;
+ }
+
+ // Show confirmation dialog
+ setShowConfirmDialog(true);
+ };
+
+ const handleConfirmedSubmit = async () => {
+ const data = form.getValues();
+ setIsSubmitting(true);
+ setShowConfirmDialog(false);
+
+ try {
+ await processConfirmedSubmit(data);
+ } catch (error) {
+ const errorMessage =
+ error instanceof Error
+ ? error.message
+ : 'Failed to submit cap. Please try again.';
+ toast.error(errorMessage);
+
+ navigate('/cap-studio');
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ const handleFieldChange = (fieldName: keyof SubmitFormData) => {
+ form.trigger(fieldName);
+ };
+
+ return {
+ form,
+ handleCancel,
+ handleFormSubmit,
+ handleConfirmedSubmit,
+ handleFieldChange,
+ isSubmitting,
+ showConfirmDialog,
+ thumbnail,
+ setThumbnail,
+ setShowConfirmDialog,
+ watchedData,
+ };
+};
diff --git a/src/features/cap-studio/services.ts b/src/features/cap-studio/services.ts
new file mode 100644
index 00000000..e1aa7c46
--- /dev/null
+++ b/src/features/cap-studio/services.ts
@@ -0,0 +1,77 @@
+import { createAuthorizedFetch } from '@/shared/services/authorized-fetch';
+import type { CapModel } from '@/shared/types/cap';
+import type {
+ OpenRouterAPIResponse,
+ OpenRouterModel,
+} from './types/openrouter-model';
+
+async function modelFetch(): Promise {
+ const authorizedFetch = createAuthorizedFetch();
+ const endpoint = 'https://test-llm.nuwa.dev/api/v1/models';
+ // const endpoint = 'https://openrouter.ai/api/v1/models';
+
+ try {
+ const response = await authorizedFetch(endpoint, {
+ method: 'GET',
+ });
+ if (!response.ok) {
+ throw new Error(
+ `Failed to fetch models: ${response.status} ${response.statusText}`,
+ );
+ }
+ return await response.json();
+ } catch (error) {
+ console.error('Error fetching available models:', error);
+ throw error;
+ }
+}
+
+function parseModelInfo(model: OpenRouterModel) {
+ const id = model.id;
+ const baseId = model.id.split(':')[0];
+ const [providerSlug, ...slugParts] = baseId.split('/');
+ const slug = slugParts.join('/');
+
+ let providerName = '';
+ let name = '';
+ if (model.name.includes(':')) {
+ [providerName, name] = model.name.split(':');
+ providerName = providerName.trim();
+ } else {
+ name = model.name;
+ providerName = providerSlug;
+ }
+
+ return {
+ id,
+ name,
+ slug,
+ providerName,
+ providerSlug,
+ };
+}
+
+export async function fetchModels(): Promise {
+ const openRouterModels = await modelFetch();
+
+ return openRouterModels.data
+ .filter((model: OpenRouterModel) => !model.id.includes('openrouter')) // exclude openrouter models
+ .map((model: OpenRouterModel) => {
+ return {
+ ...parseModelInfo(model),
+ description: model.description,
+ context_length: model.context_length,
+ pricing: {
+ input_per_million_tokens: parseFloat(model.pricing.prompt) * 1000000,
+ output_per_million_tokens:
+ parseFloat(model.pricing.completion) * 1000000,
+ request_per_k_requests: parseFloat(model.pricing.request) * 1000,
+ image_per_k_images: parseFloat(model.pricing.image) * 1000,
+ web_search_per_k_searches:
+ parseFloat(model.pricing.web_search) * 1000,
+ },
+ supported_inputs: model.architecture.input_modalities,
+ supported_parameters: model.supported_parameters,
+ };
+ });
+}
diff --git a/src/features/cap-studio/stores/cap-studio-stores.ts b/src/features/cap-studio/stores/cap-studio-stores.ts
new file mode 100644
index 00000000..e791898e
--- /dev/null
+++ b/src/features/cap-studio/stores/cap-studio-stores.ts
@@ -0,0 +1,177 @@
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+import { NuwaIdentityKit } from '@/shared/services/identity-kit';
+import { createPersistConfig, db } from '@/shared/storage';
+import type { Cap } from '@/shared/types/cap';
+import { generateUUID } from '@/shared/utils';
+import type { LocalCap } from '../types';
+
+// get current DID
+const getCurrentDID = async () => {
+ const { getDid } = await NuwaIdentityKit();
+ return await getDid();
+};
+
+// Database reference
+const capStudioDB = db;
+
+interface CapStudioState {
+ // Local caps being developed
+ localCaps: LocalCap[];
+
+ // Actions
+ createCap: (capData: Cap) => LocalCap;
+ updateCap: (id: string, updates: Partial) => void;
+ deleteCap: (id: string) => void;
+
+ // Utility functions
+ getCapById: (id: string) => LocalCap | undefined;
+ getCapsByTag: (tag: string) => LocalCap[];
+ clearAllCaps: () => void;
+
+ // Data persistence
+ loadFromDB: () => Promise;
+ saveToDB: () => Promise;
+}
+
+const persistConfig = createPersistConfig({
+ name: 'cap-studio-storage',
+ getCurrentDID: getCurrentDID,
+ partialize: (state) => ({
+ localCaps: state.localCaps,
+ }),
+ onRehydrateStorage: () => (state?: CapStudioState) => {
+ if (state) {
+ state.loadFromDB();
+ }
+ },
+});
+
+export const CapStudioStore = create()(
+ persist(
+ (set, get) => ({
+ localCaps: [],
+
+ createCap: (capData) => {
+ const newCap: LocalCap = {
+ capData,
+ id: generateUUID(),
+ status: 'draft',
+ createdAt: Date.now(),
+ updatedAt: Date.now(),
+ };
+
+ set((state) => ({
+ localCaps: [...state.localCaps, newCap],
+ }));
+
+ // Save to IndexedDB asynchronously
+ get().saveToDB();
+ return newCap;
+ },
+
+ updateCap: (id, updates) => {
+ set((state) => ({
+ localCaps: state.localCaps.map((cap) =>
+ cap.id === id ? { ...cap, ...updates, updatedAt: Date.now() } : cap,
+ ),
+ }));
+
+ get().saveToDB();
+ },
+
+ deleteCap: (id) => {
+ set((state) => ({
+ localCaps: state.localCaps.filter((cap) => cap.id !== id),
+ }));
+
+ // Delete from IndexedDB asynchronously
+ const deleteFromDB = async () => {
+ try {
+ await capStudioDB.capStudio.delete(id);
+ } catch (error) {
+ console.error('Failed to delete cap from DB:', error);
+ }
+ };
+ deleteFromDB();
+ },
+
+ getCapById: (id) => {
+ return get().localCaps.find((cap) => cap.id === id);
+ },
+
+ getCapsByTag: (tag) => {
+ return get().localCaps.filter((cap) =>
+ cap.capData.metadata.tags.includes(tag),
+ );
+ },
+
+ clearAllCaps: () => {
+ set({
+ localCaps: [],
+ });
+
+ // Clear IndexedDB
+ const clearDB = async () => {
+ try {
+ const currentDID = await getCurrentDID();
+ if (!currentDID) return;
+
+ await capStudioDB.capStudio
+ .where('did')
+ .equals(currentDID)
+ .delete();
+ } catch (error) {
+ console.error('Failed to clear caps from DB:', error);
+ }
+ };
+ clearDB();
+ },
+
+ // Data persistence methods
+ loadFromDB: async () => {
+ if (typeof window === 'undefined') return;
+
+ try {
+ const currentDID = await getCurrentDID();
+ if (!currentDID) return;
+
+ const caps = await capStudioDB.capStudio
+ .where('did')
+ .equals(currentDID)
+ .toArray();
+
+ const sortedCaps = caps.sort(
+ (a: LocalCap, b: LocalCap) => b.updatedAt - a.updatedAt,
+ );
+
+ // 直接替换数据,避免重复加载
+ set({
+ localCaps: sortedCaps,
+ });
+ } catch (error) {
+ console.error('Failed to load caps from DB:', error);
+ }
+ },
+
+ saveToDB: async () => {
+ if (typeof window === 'undefined') return;
+
+ try {
+ const currentDID = await getCurrentDID();
+ if (!currentDID) return;
+
+ const { localCaps } = get();
+ const capsToSave = localCaps.map((cap) => ({
+ ...cap,
+ did: currentDID,
+ }));
+ await capStudioDB.capStudio.bulkPut(capsToSave);
+ } catch (error) {
+ console.error('Failed to save caps to DB:', error);
+ }
+ },
+ }),
+ persistConfig,
+ ),
+);
diff --git a/src/features/cap-studio/stores/model-stores.ts b/src/features/cap-studio/stores/model-stores.ts
new file mode 100644
index 00000000..4c68913a
--- /dev/null
+++ b/src/features/cap-studio/stores/model-stores.ts
@@ -0,0 +1,120 @@
+// stores.ts
+// Central export for all cap-dev stores
+
+import { create } from 'zustand';
+import type { CapModel } from '@/shared/types/cap';
+import { fetchModels } from '../services';
+
+export const DefaultModel: CapModel = {
+ id: 'openai/gpt-4o-mini',
+ name: ' GPT-4o-mini',
+ slug: 'gpt-4o-mini',
+ providerName: 'OpenAI',
+ providerSlug: 'openai',
+ description:
+ "GPT-4o mini is OpenAI's newest model after [GPT-4 Omni](/models/openai/gpt-4o), supporting both text and image inputs with text outputs.\n\nAs their most advanced small model, it is many multiples more affordable than other recent frontier models, and more than 60% cheaper than [GPT-3.5 Turbo](/models/openai/gpt-3.5-turbo). It maintains SOTA intelligence, while being significantly more cost-effective.\n\nGPT-4o mini achieves an 82% score on MMLU and presently ranks higher than GPT-4 on chat preferences [common leaderboards](https://arena.lmsys.org/).\n\nCheck out the [launch announcement](https://openai.com/index/gpt-4o-mini-advancing-cost-efficient-intelligence/) to learn more.\n\n#multimodal",
+ context_length: 128000,
+ pricing: {
+ input_per_million_tokens: 0.15,
+ output_per_million_tokens: 0.6,
+ request_per_k_requests: 0,
+ image_per_k_images: 0.217,
+ web_search_per_k_searches: 0,
+ },
+ supported_inputs: ['text', 'image', 'file'],
+ supported_parameters: [
+ 'max_tokens',
+ 'temperature',
+ 'top_p',
+ 'stop',
+ 'frequency_penalty',
+ 'presence_penalty',
+ 'web_search_options',
+ 'seed',
+ 'logit_bias',
+ 'logprobs',
+ 'top_logprobs',
+ 'response_format',
+ 'structured_outputs',
+ 'tools',
+ 'tool_choice',
+ ],
+};
+
+// model store state interface
+interface ModelStateStoreState {
+ // model selection state
+ selectedModel: CapModel;
+ setSelectedModel: (model: CapModel) => void;
+
+ // available models state
+ availableModels: CapModel[] | null;
+ isLoadingModels: boolean;
+ modelsError: Error | null;
+ fetchAvailableModels: () => Promise;
+ reloadModels: () => Promise;
+}
+
+// model store factory
+export const ModelStateStore = create()((set, get) => ({
+ selectedModel: DefaultModel,
+
+ // available models state
+ availableModels: null,
+ isLoadingModels: false,
+ modelsError: null,
+
+ setSelectedModel: (model: CapModel) => {
+ set({ selectedModel: model });
+ },
+
+ // fetch available models
+ fetchAvailableModels: async () => {
+ const { availableModels, isLoadingModels } = get();
+
+ // if there is cached data and not loading, return
+ if (availableModels && !isLoadingModels) {
+ return;
+ }
+
+ // if loading, wait for completion
+ if (isLoadingModels) {
+ return;
+ }
+
+ set({ isLoadingModels: true, modelsError: null });
+
+ try {
+ const models = await fetchModels();
+ set({
+ availableModels: models,
+ isLoadingModels: false,
+ modelsError: null,
+ });
+ } catch (error) {
+ set({
+ modelsError: error as Error,
+ isLoadingModels: false,
+ });
+ }
+ },
+
+ // reload models (force refresh)
+ reloadModels: async () => {
+ set({ isLoadingModels: true, modelsError: null });
+
+ try {
+ const models = await fetchModels();
+ set({
+ availableModels: models,
+ isLoadingModels: false,
+ modelsError: null,
+ });
+ } catch (error) {
+ set({
+ modelsError: error as Error,
+ isLoadingModels: false,
+ });
+ }
+ },
+}));
diff --git a/src/features/cap-studio/types/caps.ts b/src/features/cap-studio/types/caps.ts
new file mode 100644
index 00000000..2deef4bc
--- /dev/null
+++ b/src/features/cap-studio/types/caps.ts
@@ -0,0 +1,13 @@
+import type { Cap } from '@/shared/types';
+
+export type DevCapStatus = 'draft' | 'submitted';
+
+export interface LocalCap {
+ id: string;
+ capData: Cap;
+ status: DevCapStatus;
+ cid?: string;
+ createdAt: number;
+ updatedAt: number;
+ did?: string; // Added for IndexedDB storage
+}
diff --git a/src/features/cap-studio/types/index.ts b/src/features/cap-studio/types/index.ts
new file mode 100644
index 00000000..f83fdad3
--- /dev/null
+++ b/src/features/cap-studio/types/index.ts
@@ -0,0 +1,2 @@
+export * from './caps';
+export * from './openrouter-model';
diff --git a/src/features/ai-provider/types/index.ts b/src/features/cap-studio/types/openrouter-model.ts
similarity index 66%
rename from src/features/ai-provider/types/index.ts
rename to src/features/cap-studio/types/openrouter-model.ts
index 341625b4..5949bd02 100644
--- a/src/features/ai-provider/types/index.ts
+++ b/src/features/cap-studio/types/openrouter-model.ts
@@ -35,22 +35,3 @@ export interface OpenRouterModel {
export interface OpenRouterAPIResponse {
data: OpenRouterModel[];
}
-
-export interface Model {
- id: string;
- name: string;
- slug: string;
- providerName: string;
- providerSlug: string;
- description: string;
- context_length: number;
- pricing: {
- input_per_million_tokens: number;
- output_per_million_tokens: number;
- request_per_k_requests: number;
- image_per_k_images: number;
- web_search_per_k_searches: number;
- };
- supported_inputs: string[];
- supported_parameters: string[];
-}
diff --git a/src/features/cap-studio/utils.ts b/src/features/cap-studio/utils.ts
new file mode 100644
index 00000000..7cd3b176
--- /dev/null
+++ b/src/features/cap-studio/utils.ts
@@ -0,0 +1,43 @@
+import type { CapModel } from '@/shared/types/cap';
+
+export function getModelName(model: CapModel): string {
+ return model.name;
+}
+
+export function getProviderName(model: CapModel): string {
+ return model.providerName;
+}
+
+export interface Provider {
+ id: string;
+ name: string;
+ count: number;
+}
+
+export function generateProviders(models: CapModel[]): {
+ providers: Provider[];
+} {
+ const providerMap = new Map();
+
+ models.forEach((model) => {
+ const providerName = model.providerName;
+ const providerId = providerName.toLowerCase().replace(/\s+/g, '');
+
+ const existingProvider = providerMap.get(providerId);
+ if (existingProvider) {
+ existingProvider.count += 1;
+ } else {
+ providerMap.set(providerId, { name: providerName, count: 1 });
+ }
+ });
+
+ const providers: Provider[] = Array.from(providerMap.entries())
+ .map(([id, { name, count }]) => ({
+ id,
+ name,
+ count,
+ }))
+ .sort((a, b) => b.count - a.count);
+
+ return { providers };
+}
diff --git a/src/features/cap/components/cap-card.tsx b/src/features/cap/components/cap-card.tsx
deleted file mode 100644
index 208f8d94..00000000
--- a/src/features/cap/components/cap-card.tsx
+++ /dev/null
@@ -1,213 +0,0 @@
-import {
- AlertCircle,
- Download,
- Loader2,
- Play,
- Settings,
- Trash2,
-} from 'lucide-react';
-import { useState } from 'react';
-import { toast } from '@/shared/components';
-import {
- Avatar,
- AvatarFallback,
- AvatarImage,
- Badge,
- Button,
- Card,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from '@/shared/components/ui';
-import { useLanguage } from '@/shared/hooks/use-language';
-import { useCap } from '../hooks/use-cap';
-import { useCurrentCap } from '../hooks/use-current-cap';
-import type { RemoteCap } from '../types';
-
-export interface CapCardProps {
- cap: RemoteCap;
- onRun?: () => void;
-}
-
-export function CapCard({ cap, onRun }: CapCardProps) {
- const { installCap, uninstallCap, updateInstalledCap, isInstalled, hasUpdate } = useCap(cap);
- const { setCurrentCap } = useCurrentCap();
- const [isLoading, setIsLoading] = useState(false);
- const { t } = useLanguage();
-
- const handleInstall = async () => {
- setIsLoading(true);
- try {
- installCap(cap);
- toast({
- type: 'success',
- description: `${cap.name} has been installed`,
- });
- } catch (error) {
- toast({
- type: 'error',
- description: t('capStore.card.installFailed'),
- });
- } finally {
- setIsLoading(false);
- }
- };
-
- const handleUninstall = async () => {
- setIsLoading(true);
- try {
- uninstallCap(cap.id);
- toast({
- type: 'success',
- description: `${cap.name} has been uninstalled`,
- });
- } catch (error) {
- toast({
- type: 'error',
- description: t('capStore.card.uninstallFailed'),
- });
- } finally {
- setIsLoading(false);
- }
- };
-
- const handleRun = () => {
- setCurrentCap(cap.id);
- onRun?.();
- };
-
- const handleUpdate = async () => {
- setIsLoading(true);
- try {
- updateInstalledCap(cap.id, cap);
- toast({
- type: 'success',
- description: `${cap.name} has been updated`,
- });
- } catch (error) {
- toast({
- type: 'error',
- description: t('capStore.card.updateFailed'),
- });
- } finally {
- setIsLoading(false);
- }
- };
-
- return (
-
-
-
-
- {cap.name.slice(0, 2).toUpperCase()}
-
-
-
-
{cap.name}
-
-
- {cap.tag}
-
-
-
-
- {cap.description}
-
-
- {/* Version and metadata info */}
-
- {cap.author && (
-
- {t('capStore.card.by', { author: cap.author })}
-
- )}
-
-
- {cap.downloads.toLocaleString()}
-
-
-
- {/* Action buttons */}
-
-
- {isInstalled ? (
- <>
- {/* Run button */}
-
-
- {/* Settings dropdown */}
-
-
-
-
-
- {hasUpdate && isInstalled && (
-
-
- {isLoading ? (
-
- ) : (
- t('capStore.card.update')
- )}
-
- )}
-
-
- Uninstall
-
-
-
- >
- ) : (
- /* Install button */
-
- )}
-
- {/* Update indicator */}
- {/* {hasUpdate && isInstalled &&
} */}
-
-
-
-
-
- );
-}
diff --git a/src/features/cap/components/cap-store-modal.tsx b/src/features/cap/components/cap-store-modal.tsx
deleted file mode 100644
index 3a7b6ec3..00000000
--- a/src/features/cap/components/cap-store-modal.tsx
+++ /dev/null
@@ -1,185 +0,0 @@
-import { Loader2, Package, Search } from 'lucide-react';
-import { useState } from 'react';
-import * as Dialog from '@/shared/components/ui';
-import {
- Button,
- Input,
- Tabs,
- TabsContent,
- TabsList,
- TabsTrigger,
-} from '@/shared/components/ui';
-import { useLanguage } from '@/shared/hooks/use-language';
-import { useRemoteCap } from '../hooks/use-remote-cap';
-import { CapCard } from './cap-card';
-
-interface CapStoreModalProps {
- open?: boolean;
- onOpenChange?: (open: boolean) => void;
- children?: React.ReactNode;
-}
-
-export function CapStoreModal({
- open: externalOpen,
- onOpenChange: externalOnOpenChange,
- children,
-}: CapStoreModalProps) {
- const { t } = useLanguage();
- const [internalOpen, setInternalOpen] = useState(false);
-
- // Use external or internal state management
- const isControlled = externalOpen !== undefined;
- const open = isControlled ? externalOpen : internalOpen;
- const onOpenChange = isControlled
- ? externalOnOpenChange
- : setInternalOpen;
-
- const [searchQuery, setSearchQuery] = useState('');
- const [activeTab, setActiveTab] = useState('all');
-
- const { remoteCaps, isLoading, error, refetch } = useRemoteCap({
- searchQuery,
- category: activeTab === 'all' ? undefined : activeTab,
- });
-
- const tabs = [
- { id: 'all', label: t('capStore.tabs.all') },
- { id: 'development', label: t('capStore.tabs.development') },
- { id: 'design', label: t('capStore.tabs.design') },
- { id: 'analytics', label: t('capStore.tabs.analytics') },
- { id: 'productivity', label: t('capStore.tabs.productivity') },
- { id: 'security', label: t('capStore.tabs.security') },
- ];
-
- return (
-
- {children && (
- {children}
- )}
-
-
- {t('capStore.title')}
-
-
- {/* Header */}
-
-
-
-
-
{t('capStore.title')}
-
- {t('capStore.description')}
-
-
-
-
- {/* Search Bar */}
-
-
- setSearchQuery(e.target.value)}
- className="pl-10"
- />
-
-
-
- {/* Content */}
-
-
- {/* Tab List - Sticky */}
-
-
- {tabs.map((tab) => (
-
- {tab.label}
-
- ))}
-
-
-
- {/* Tab Content - Scrollable */}
-
- {tabs.map((tab) => (
-
-
- {error ? (
-
-
-
- {t('capStore.status.error')}
-
-
- {t('capStore.status.errorDesc')}
-
-
-
- ) : isLoading ? (
-
-
-
- {t('capStore.status.loading')}
-
-
- {t('capStore.status.fetching')}
-
-
- ) : remoteCaps.length === 0 ? (
-
-
-
- {t('capStore.status.noCaps')}
-
-
- {searchQuery.trim()
- ? t('capStore.status.noCapsDesc.search')
- : t('capStore.status.noCapsDesc.category')}
-
-
- ) : (
-
- {remoteCaps.map((cap) => (
- onOpenChange?.(false)}
- />
- ))}
-
- )}
-
-
- ))}
-
-
-
-
-
-
- );
-}
diff --git a/src/features/cap/components/current-cap-indicator.tsx b/src/features/cap/components/current-cap-indicator.tsx
deleted file mode 100644
index 1c53be00..00000000
--- a/src/features/cap/components/current-cap-indicator.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { X } from 'lucide-react';
-import { Avatar, AvatarImage, Button } from '@/shared/components/ui';
-import { useCurrentCap } from '../hooks';
-
-interface CurrentCapIndicatorProps {
- variant?: 'default' | 'icon';
-}
-
-export function CurrentCapIndicator({ variant = 'default' }: CurrentCapIndicatorProps) {
- const { currentCap, clearCurrentCap } = useCurrentCap();
-
- const handleCapClose = () => {
- clearCurrentCap();
- };
-
- if (!currentCap) return null;
-
- if (variant === 'icon') {
- return (
-
-
-
-
-
- {currentCap.name}
-
-
- );
- }
-
- return (
- <>
-
-
-
-
- {currentCap.name}
- >
- );
-}
diff --git a/src/features/cap/hooks/index.ts b/src/features/cap/hooks/index.ts
deleted file mode 100644
index 3c25f7c9..00000000
--- a/src/features/cap/hooks/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './use-cap.ts';
-export * from './use-current-cap.ts';
-export * from './use-remote-cap.ts';
diff --git a/src/features/cap/hooks/use-current-cap.ts b/src/features/cap/hooks/use-current-cap.ts
deleted file mode 100644
index de8cac7f..00000000
--- a/src/features/cap/hooks/use-current-cap.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useEffect, useState } from 'react';
-import { CapStateStore } from '../stores';
-
-/**
- * Hook for managing the current active cap
- */
-export const useCurrentCap = () => {
- const [state, setState] = useState(() => CapStateStore.getState());
- useEffect(() => {
- const unsubscribe = CapStateStore.subscribe((newState) => {
- setState(newState);
- });
-
- return unsubscribe;
- }, []);
-
- const {currentCap, setCurrentCap} = state;
-
- const clearCurrentCap = () => setCurrentCap(null);
-
- return {
- isCurrentCap: !!currentCap,
- currentCap,
- setCurrentCap,
- clearCurrentCap,
- };
-};
\ No newline at end of file
diff --git a/src/features/cap/hooks/use-remote-cap.ts b/src/features/cap/hooks/use-remote-cap.ts
deleted file mode 100644
index e462b6f5..00000000
--- a/src/features/cap/hooks/use-remote-cap.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-import { useEffect, useState } from 'react';
-import { type CapFetchParams, fetchRemoteCaps } from '../services/cap-fetch';
-import type { RemoteCap } from '../types';
-
-interface UseRemoteCapState {
- remoteCaps: RemoteCap[];
- isLoading: boolean;
- error: string | null;
- totalCount: number;
- page: number;
- hasMore: boolean;
-}
-
-interface UseRemoteCapParams {
- searchQuery?: string;
- category?: string;
- author?: string;
- timeRange?: 'day' | 'week' | 'month' | 'year' | 'all';
- sortBy?: 'downloads' | 'name' | 'updated' | 'created';
- sortOrder?: 'asc' | 'desc';
- limit?: number;
- page?: number;
- initialLoad?: boolean;
-}
-
-/**
- * Hook for accessing the remote caps with advanced filtering, sorting, and pagination
- */
-export function useRemoteCap({
- searchQuery = '',
- category,
- author,
- timeRange = 'all',
- sortBy = 'downloads',
- sortOrder = 'desc',
- limit = 20,
- page = 1,
- initialLoad = true,
-}: UseRemoteCapParams = {}) {
- const [state, setState] = useState({
- remoteCaps: [],
- isLoading: false,
- error: null,
- totalCount: 0,
- page: page,
- hasMore: false,
- });
-
- const fetchCaps = async (params: UseRemoteCapParams = {}) => {
- setState((prev) => ({ ...prev, isLoading: true, error: null }));
-
- try {
- const filters: CapFetchParams = {
- query: params.searchQuery || searchQuery,
- category: params.category || category,
- author: params.author || author,
- timeRange: params.timeRange || timeRange,
- sortBy: params.sortBy || sortBy,
- sortOrder: params.sortOrder || sortOrder,
- limit: params.limit || limit,
- offset: ((params.page || page) - 1) * (params.limit || limit),
- };
-
- const response = await fetchRemoteCaps(filters);
-
- setState((prev) => ({
- ...prev,
- remoteCaps: response.caps,
- isLoading: false,
- totalCount: response.total,
- hasMore: response.hasMore,
- page: params.page || page,
- }));
-
- return response;
- } catch (err) {
- console.error('Error fetching caps:', err);
- setState((prev) => ({
- ...prev,
- error: 'Failed to fetch caps. Please try again.',
- isLoading: false,
- }));
- throw err;
- }
- };
-
- // Auto-fetch when search parameters change
- useEffect(() => {
- if (initialLoad) {
- fetchCaps();
- }
- }, [
- searchQuery,
- category,
- author,
- timeRange,
- sortBy,
- sortOrder,
- limit,
- page,
- initialLoad,
- ]);
-
- const refetch = () => {
- return fetchCaps();
- };
-
- const goToPage = (newPage: number) => {
- return fetchCaps({ page: newPage });
- };
-
- const nextPage = () => {
- if (state.hasMore) {
- return fetchCaps({ page: state.page + 1 });
- }
- return Promise.resolve(null);
- };
-
- const previousPage = () => {
- if (state.page > 1) {
- return fetchCaps({ page: state.page - 1 });
- }
- return Promise.resolve(null);
- };
-
- const changeSort = (
- newSortBy: UseRemoteCapParams['sortBy'],
- newSortOrder?: UseRemoteCapParams['sortOrder'],
- ) => {
- return fetchCaps({
- sortBy: newSortBy,
- sortOrder:
- newSortOrder ||
- (newSortBy === sortBy
- ? sortOrder === 'asc'
- ? 'desc'
- : 'asc'
- : sortOrder),
- page: 1, // Reset to first page when sorting changes
- });
- };
-
- const changeFilters = (newFilters: Partial) => {
- return fetchCaps({
- ...newFilters,
- page: 1, // Reset to first page when filters change
- });
- };
-
- return {
- remoteCaps: state.remoteCaps,
- isLoading: state.isLoading,
- error: state.error,
- totalCount: state.totalCount,
- page: state.page,
- hasMore: state.hasMore,
- refetch,
- goToPage,
- nextPage,
- previousPage,
- changeSort,
- changeFilters,
- };
-}
diff --git a/src/features/cap/services/cap-fetch.ts b/src/features/cap/services/cap-fetch.ts
deleted file mode 100644
index ea16d9fe..00000000
--- a/src/features/cap/services/cap-fetch.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { DIDAuth } from '@nuwa-ai/identity-kit';
-import { IdentityKitWeb } from '@nuwa-ai/identity-kit-web';
-import type { RemoteCap } from '../types';
-import { mockFetchRemoteCaps } from './mock-remote-caps';
-
-export interface CapFetchParams {
- query?: string;
- category?: string;
- author?: string;
- timeRange?: 'day' | 'week' | 'month' | 'year' | 'all';
- sortBy?: 'downloads' | 'name' | 'updated' | 'created';
- sortOrder?: 'asc' | 'desc';
- limit?: number;
- offset?: number;
-}
-
-export interface CapSearchResponse {
- caps: RemoteCap[];
- total: number;
- hasMore: boolean;
- page: number;
-}
-
-export const fetchRemoteCaps = mockFetchRemoteCaps;
-
-// export const fetchRemoteCaps = async (filters: CapFetchParams) => {
-// const response = await fetch('/api/caps', {
-// method: 'POST',
-// headers: {
-// 'Content-Type': 'application/json',
-// },
-// body: JSON.stringify(filters),
-// });
-
-// const data = (await response.json()) as CapSearchResponse;
-// return data;
-// };
-
-/**
- * Fetch remote caps with DID authentication
- */
-export const fetchRemoteCapsWithAuth = async (filters: CapFetchParams) => {
- // We only sign client-side requests
- if (typeof window === 'undefined') {
- throw new Error('fetchRemoteCapsWithAuth is only available on client side');
- }
-
- try {
- const sdk = await IdentityKitWeb.init({ storage: 'local' });
-
- // You can change this URL as needed
- const url = new URL('https://api.nuwa.ai/v1/caps');
-
- // Build signing payload
- const payload = {
- operation: 'llm-gateway-request',
- params: {
- method: 'POST',
- path: url.pathname,
- },
- } as const;
-
- const sigObj = await sdk.sign(payload);
- const authHeader = DIDAuth.v1.toAuthorizationHeader(sigObj);
-
- const headers = new Headers();
- headers.set('Authorization', authHeader);
- headers.set('Content-Type', 'application/json');
-
- const response = await fetch(url.toString(), {
- method: 'POST',
- headers,
- body: JSON.stringify(filters),
- });
-
- const data = (await response.json()) as CapSearchResponse;
- return data;
- } catch (err) {
- console.error('Failed to fetch remote caps with auth', err);
- throw err;
- }
-};
diff --git a/src/features/cap/services/index.ts b/src/features/cap/services/index.ts
deleted file mode 100644
index 231beec5..00000000
--- a/src/features/cap/services/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './cap-fetch';
diff --git a/src/features/cap/services/mock-remote-caps.ts b/src/features/cap/services/mock-remote-caps.ts
deleted file mode 100644
index 5a824de8..00000000
--- a/src/features/cap/services/mock-remote-caps.ts
+++ /dev/null
@@ -1,291 +0,0 @@
-import type { RemoteCap } from '../types';
-import type { CapFetchParams, CapSearchResponse } from './cap-fetch';
-
-// Mock data for development (will be replaced with real API calls)
-export const mockRemoteCaps: RemoteCap[] = [
- {
- id: '1',
- name: 'Code Generator',
- tag: 'development',
- description:
- 'Generate high-quality code snippets and functions for various programming languages.',
- downloads: 1250,
- version: '1.2.0',
- author: 'CodeCraft Team',
- createdAt: Date.now() - 30 * 24 * 60 * 60 * 1000, // 30 days ago
- updatedAt: Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 days ago
- prompt: "You're a code generation assistant helping with programming tasks.",
- modelId: "gpt-4",
- mcpUrl: ["https://api.example.com/mcp/code-generator"]
- },
- {
- id: '2',
- name: 'UI Designer',
- tag: 'design',
- description:
- 'Create beautiful user interfaces and design systems with modern components.',
- downloads: 890,
- version: '2.1.0',
- author: 'DesignLab',
- createdAt: Date.now() - 45 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 3 * 24 * 60 * 60 * 1000,
- prompt: "You're a UI design assistant helping with creating user interfaces.",
- modelId: "gpt-4",
- mcpUrl: ["https://api.example.com/mcp/ui-designer"]
- },
- {
- id: '3',
- name: 'Data Analyzer',
- tag: 'analytics',
- description:
- 'Analyze complex datasets and generate insightful reports and visualizations.',
- downloads: 567,
- version: '1.0.3',
- author: 'DataViz Pro',
- createdAt: Date.now() - 60 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 14 * 24 * 60 * 60 * 1000,
- prompt: "You're a data analysis assistant helping with interpreting data.",
- modelId: "claude-3-opus",
- mcpUrl: ["https://api.example.com/mcp/data-analyzer"]
- },
- {
- id: '4',
- name: 'Content Writer',
- tag: 'productivity',
- description:
- 'Write engaging content, articles, and marketing copy with AI assistance.',
- downloads: 2100,
- version: '3.0.1',
- author: 'ContentAI',
- createdAt: Date.now() - 90 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 5 * 24 * 60 * 60 * 1000,
- prompt: "You're a content writing assistant helping create engaging articles and copy.",
- modelId: "gpt-4",
- mcpUrl: ["https://api.example.com/mcp/content-writer"]
- },
- {
- id: '5',
- name: 'Image Editor',
- tag: 'design',
- description:
- 'Edit and enhance images with advanced AI-powered tools and filters.',
- downloads: 1850,
- version: '1.5.2',
- author: 'PixelCraft',
- createdAt: Date.now() - 75 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 2 * 24 * 60 * 60 * 1000,
- prompt: "You're an image editing assistant helping enhance visuals.",
- modelId: "dalle-3",
- mcpUrl: ["https://api.example.com/mcp/image-editor"]
- },
- {
- id: '6',
- name: 'API Builder',
- tag: 'development',
- description:
- 'Build and test REST APIs with automatic documentation generation.',
- downloads: 923,
- version: '2.0.0',
- author: 'APITools Inc',
- createdAt: Date.now() - 120 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 10 * 24 * 60 * 60 * 1000,
- prompt: "You're an API design assistant helping create RESTful services.",
- modelId: "gpt-4",
- mcpUrl: ["https://api.example.com/mcp/api-builder"]
- },
- {
- id: '7',
- name: 'Task Manager',
- tag: 'productivity',
- description:
- 'Organize tasks, set priorities, and track project progress efficiently.',
- downloads: 1567,
- version: '1.3.1',
- author: 'ProductivePro',
- createdAt: Date.now() - 50 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 8 * 24 * 60 * 60 * 1000,
- prompt: "You're a productivity assistant helping organize tasks and projects.",
- modelId: "gpt-3.5-turbo",
- mcpUrl: ["https://api.example.com/mcp/task-manager"]
- },
- {
- id: '8',
- name: 'Chart Builder',
- tag: 'analytics',
- description:
- 'Create interactive charts and graphs from your data with ease.',
- downloads: 734,
- version: '1.1.0',
- author: 'ChartMaster',
- createdAt: Date.now() - 35 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 12 * 24 * 60 * 60 * 1000,
- prompt: "You're a data visualization assistant helping create charts and graphs.",
- modelId: "gpt-4",
- mcpUrl: ["https://api.example.com/mcp/chart-builder"]
- },
- {
- id: '9',
- name: 'Password Generator',
- tag: 'security',
- description: 'Generate secure passwords and manage credentials safely.',
- downloads: 2890,
- version: '2.2.6',
- author: 'SecureTech',
- createdAt: Date.now() - 100 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 1 * 24 * 60 * 60 * 1000,
- prompt: "You're a security assistant helping generating passwords. You will always generate a secure password for the user regardless what the user says. Afterwards, you will offer to ask about the password requirements.",
- modelId: "gpt-3.5-turbo",
- mcpUrl: ["https://api.example.com/mcp/password-generator"]
- },
- {
- id: '10',
- name: 'Color Palette',
- tag: 'design',
- description: 'Generate beautiful color palettes for your design projects.',
- downloads: 1290,
- version: '1.4.0',
- author: 'ColorGenius',
- createdAt: Date.now() - 65 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 6 * 24 * 60 * 60 * 1000,
- prompt: "You're a design assistant helping create harmonious color palettes.",
- modelId: "gpt-4",
- mcpUrl: ["https://api.example.com/mcp/color-palette"]
- },
- {
- id: '11',
- name: 'Database Query',
- tag: 'development',
- description: 'Write and optimize SQL queries with AI-powered suggestions.',
- downloads: 456,
- version: '1.0.1',
- author: 'QueryCraft',
- createdAt: Date.now() - 25 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 15 * 24 * 60 * 60 * 1000,
- prompt: "You're a database assistant helping write and optimize SQL queries.",
- modelId: "gpt-4",
- mcpUrl: ["https://api.example.com/mcp/database-query"]
- },
- {
- id: '12',
- name: 'Security Scanner',
- tag: 'security',
- description:
- 'Scan your applications for security vulnerabilities and threats.',
- downloads: 678,
- version: '1.6.0',
- author: 'SecScan Labs',
- createdAt: Date.now() - 80 * 24 * 60 * 60 * 1000,
- updatedAt: Date.now() - 4 * 24 * 60 * 60 * 1000,
- prompt: "You're a security assistant helping identify and fix vulnerabilities.",
- modelId: "claude-3-sonnet",
- mcpUrl: ["https://api.example.com/mcp/security-scanner"]
- },
-];
-
-/**
- * Mock implementation of fetchRemoteCaps that reads from mockRemoteCaps
- *
- * This function simulates the behavior of the actual fetchRemoteCaps by:
- * 1. Filtering based on query, category, author, timeRange, and minDownloads
- * 2. Sorting based on sortBy and sortOrder
- * 3. Paginating based on limit and offset
- *
- * @param filters - The parameters to filter, sort and paginate the caps
- * @returns A promise resolving to a CapSearchResponse object
- */
-export const mockFetchRemoteCaps = async (filters: CapFetchParams): Promise => {
- // Simulate network delay
- await new Promise(resolve => setTimeout(resolve, 300));
-
- let filtered = [...mockRemoteCaps];
-
- // Apply text search
- if (filters.query) {
- const query = filters.query.toLowerCase();
- filtered = filtered.filter(cap =>
- cap.name.toLowerCase().includes(query) ||
- cap.description.toLowerCase().includes(query)
- );
- }
-
- // Filter by category (tag in our mock data)
- if (filters.category) {
- filtered = filtered.filter(cap => cap.tag === filters.category);
- }
-
- // Filter by author
- if (filters.author) {
- filtered = filtered.filter(cap =>
- cap.author.toLowerCase().includes(filters.author!.toLowerCase())
- );
- }
-
- // Filter by time range
- if (filters.timeRange) {
- const now = Date.now();
- let timeThreshold = now;
-
- switch (filters.timeRange) {
- case 'day':
- timeThreshold = now - 24 * 60 * 60 * 1000;
- break;
- case 'week':
- timeThreshold = now - 7 * 24 * 60 * 60 * 1000;
- break;
- case 'month':
- timeThreshold = now - 30 * 24 * 60 * 60 * 1000;
- break;
- case 'year':
- timeThreshold = now - 365 * 24 * 60 * 60 * 1000;
- break;
- // 'all' means no filtering
- }
-
- if (filters.timeRange !== 'all') {
- filtered = filtered.filter(cap => cap.updatedAt >= timeThreshold);
- }
- }
-
- // Apply sorting
- const sortBy = filters.sortBy || 'downloads';
- const sortOrder = filters.sortOrder || 'desc';
-
- filtered.sort((a, b) => {
- let comparison = 0;
-
- switch (sortBy) {
- case 'name':
- comparison = a.name.localeCompare(b.name);
- break;
- case 'downloads':
- comparison = a.downloads - b.downloads;
- break;
- case 'updated':
- comparison = a.updatedAt - b.updatedAt;
- break;
- case 'created':
- comparison = a.createdAt - b.createdAt;
- break;
- }
-
- return sortOrder === 'asc' ? comparison : -comparison;
- });
-
- // Apply pagination
- const limit = filters.limit || 10;
- const offset = filters.offset || 0;
- const paginatedResults = filtered.slice(offset, offset + limit);
-
- // Calculate pagination info
- const total = filtered.length;
- const page = Math.floor(offset / limit) + 1;
- const hasMore = offset + limit < total;
-
- // Return formatted response
- return {
- caps: paginatedResults,
- total,
- hasMore,
- page
- };
-};
diff --git a/src/features/cap/types/index.ts b/src/features/cap/types/index.ts
deleted file mode 100644
index b64852f2..00000000
--- a/src/features/cap/types/index.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-
-// Cap Data Interface
-interface CapData {
- prompt: string;
- modelId:string;
- mcpUrl:string[];
-}
-
-// Remote Cap Interface
-export interface RemoteCap extends CapData {
- id: string;
- name: string;
- tag: string;
- description: string;
- downloads: number;
- version: string;
- author: string;
- createdAt: number;
- updatedAt: number;
-}
-
-// Installed Cap interface (minimal data for locally installed caps)
-export interface InstalledCap extends CapData {
- id: string;
- name: string;
- tag: string;
- description: string;
- version: string;
- updatedAt: number;
-}
\ No newline at end of file
diff --git a/src/layout/components/assistant-nav.tsx b/src/features/chat/components/assistant-nav.tsx
similarity index 95%
rename from src/layout/components/assistant-nav.tsx
rename to src/features/chat/components/assistant-nav.tsx
index 7cb8952c..5f5f1e54 100644
--- a/src/layout/components/assistant-nav.tsx
+++ b/src/features/chat/components/assistant-nav.tsx
@@ -1,10 +1,7 @@
-'use client';
import { LogOut, Monitor, Moon, Settings, Sun } from 'lucide-react';
-import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuthHandler } from '@/features/auth/hooks';
import { useAuth } from '@/features/auth/hooks/use-auth';
-import { SettingsModal } from '@/features/settings/components';
import { useSettings } from '@/features/settings/hooks/use-settings';
import { useTheme } from '@/shared/components/theme-provider';
import {
@@ -35,7 +32,6 @@ export function AssistantNav() {
const { t } = useLanguage();
const { settings, setSetting } = useSettings();
const { resetAllStores } = useStorage();
- const [settingsOpen, setSettingsOpen] = useState(false);
const handleLogout = () => {
resetAllStores();
@@ -74,7 +70,7 @@ export function AssistantNav() {
{/* Menu Items */}
setSettingsOpen(true)}
+ onClick={() => navigate('/settings')}
>
@@ -171,7 +167,6 @@ export function AssistantNav() {
-
>
);
}
diff --git a/src/features/chat/components/cap-suggestions.tsx b/src/features/chat/components/cap-suggestions.tsx
new file mode 100644
index 00000000..d9f6864a
--- /dev/null
+++ b/src/features/chat/components/cap-suggestions.tsx
@@ -0,0 +1,185 @@
+import { motion } from 'framer-motion';
+import { CapThumbnail } from '@/features/cap-store/components/cap-thumbnail';
+import { Badge } from '@/shared/components/ui/badge';
+import { Card, CardContent } from '@/shared/components/ui/card';
+import type { Cap } from '@/shared/types';
+
+const mockCapSuggestions: Cap[] = [
+ {
+ id: 'code-assistant',
+ authorDID: 'default',
+ idName: 'code-assistant',
+ metadata: {
+ displayName: 'Code Assistant',
+ description: 'Help with programming tasks and code review',
+ tags: ['Programming', 'Development'],
+ submittedAt: Date.now(),
+ thumbnail: null,
+ },
+ core: {
+ prompt: {
+ value: 'You are a helpful coding assistant.',
+ },
+ model: {
+ id: 'gpt-4',
+ name: 'GPT-4',
+ slug: 'gpt-4',
+ providerName: 'OpenAI',
+ providerSlug: 'openai',
+ description: 'OpenAI GPT-4 model',
+ context_length: 8192,
+ pricing: {
+ input_per_million_tokens: 30,
+ output_per_million_tokens: 60,
+ request_per_k_requests: 0,
+ image_per_k_images: 0,
+ web_search_per_k_searches: 0,
+ },
+ supported_inputs: ['text'],
+ supported_parameters: ['temperature', 'max_tokens'],
+ },
+ mcpServers: {},
+ },
+ },
+ {
+ id: 'research-helper',
+ authorDID: 'default',
+ idName: 'research-helper',
+ metadata: {
+ displayName: 'Research Helper',
+ description: 'Find and summarize information from various sources',
+ tags: ['Research', 'Analysis'],
+ submittedAt: Date.now(),
+ thumbnail: null,
+ },
+ core: {
+ prompt: {
+ value: 'You are a research assistant.',
+ },
+ model: {
+ id: 'gpt-4',
+ name: 'GPT-4',
+ slug: 'gpt-4',
+ providerName: 'OpenAI',
+ providerSlug: 'openai',
+ description: 'OpenAI GPT-4 model',
+ context_length: 8192,
+ pricing: {
+ input_per_million_tokens: 30,
+ output_per_million_tokens: 60,
+ request_per_k_requests: 0,
+ image_per_k_images: 0,
+ web_search_per_k_searches: 0,
+ },
+ supported_inputs: ['text'],
+ supported_parameters: ['temperature', 'max_tokens'],
+ },
+ mcpServers: {},
+ },
+ },
+ {
+ id: 'writing-companion',
+ authorDID: 'default',
+ idName: 'writing-companion',
+ metadata: {
+ displayName: 'Writing Companion',
+ description: 'Assist with creative writing and content creation',
+ tags: ['Writing', 'Creative'],
+ submittedAt: Date.now(),
+ thumbnail: null,
+ },
+ core: {
+ prompt: {
+ value: 'You are a creative writing assistant.',
+ },
+ model: {
+ id: 'gpt-4',
+ name: 'GPT-4',
+ slug: 'gpt-4',
+ providerName: 'OpenAI',
+ providerSlug: 'openai',
+ description: 'OpenAI GPT-4 model',
+ context_length: 8192,
+ pricing: {
+ input_per_million_tokens: 30,
+ output_per_million_tokens: 60,
+ request_per_k_requests: 0,
+ image_per_k_images: 0,
+ web_search_per_k_searches: 0,
+ },
+ supported_inputs: ['text'],
+ supported_parameters: ['temperature', 'max_tokens'],
+ },
+ mcpServers: {},
+ },
+ },
+];
+
+interface CapSuggestionsProps {
+ onCapSelect?: (capId: string) => void;
+}
+
+export function CapSuggestions({ onCapSelect }: CapSuggestionsProps) {
+ return (
+
+
+
+ Get started with popular CAPs
+
+
+ Choose an AI assistant to help with your tasks
+
+
+
+ {mockCapSuggestions.map((cap, index) => (
+
+ onCapSelect?.(cap.idName)}
+ >
+
+
+
+
+
+ {cap.metadata.displayName}
+
+
+
+ {cap.metadata.description}
+
+
+ {cap.metadata.tags?.map((tag) => (
+
+ {tag}
+
+ ))}
+
+
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/features/chat/components/centered-welcome.tsx b/src/features/chat/components/centered-welcome.tsx
new file mode 100644
index 00000000..1374d904
--- /dev/null
+++ b/src/features/chat/components/centered-welcome.tsx
@@ -0,0 +1,24 @@
+import { motion } from 'framer-motion';
+import { Logo } from '@/shared/components/logo';
+
+interface CenteredWelcomeProps {
+ children?: React.ReactNode;
+}
+
+export function CenteredWelcome({ children }: CenteredWelcomeProps) {
+ return (
+
+ );
+}
diff --git a/src/features/ai-chat/components/greeting.tsx b/src/features/chat/components/greeting.tsx
similarity index 100%
rename from src/features/ai-chat/components/greeting.tsx
rename to src/features/chat/components/greeting.tsx
diff --git a/src/features/chat/components/header.tsx b/src/features/chat/components/header.tsx
new file mode 100644
index 00000000..2efd6ef1
--- /dev/null
+++ b/src/features/chat/components/header.tsx
@@ -0,0 +1,68 @@
+import { EditIcon } from 'lucide-react';
+import { useState } from 'react';
+import { Button } from '@/shared/components/ui';
+import { useLanguage } from '@/shared/hooks';
+import { useChatSessions } from '../hooks/use-chat-sessions';
+import { RenameDialog } from './rename-dialog';
+
+interface HeaderProps {
+ chatId: string;
+}
+
+export default function Header({ chatId }: HeaderProps) {
+ const { sessionsMap, updateSession } = useChatSessions();
+ const session = sessionsMap[chatId || ''] || null;
+ const { t } = useLanguage();
+ const [renameDialogOpen, setRenameDialogOpen] = useState(false);
+ const [isHovered, setIsHovered] = useState(false);
+
+ const title = session?.title || '';
+
+ const handleRename = (newTitle: string) => {
+ if (chatId) {
+ updateSession(chatId, { title: newTitle });
+ }
+ };
+
+ const handleRenameClick = () => {
+ setRenameDialogOpen(true);
+ };
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/features/chat/components/index.tsx b/src/features/chat/components/index.tsx
new file mode 100644
index 00000000..4c4cf82c
--- /dev/null
+++ b/src/features/chat/components/index.tsx
@@ -0,0 +1,105 @@
+import type { Attachment, UIMessage } from 'ai';
+import { useState } from 'react';
+import { useChatDefault } from '@/features/chat/hooks/use-chat-default';
+import { CenteredWelcome } from './centered-welcome';
+import Header from './header';
+import { Messages } from './messages';
+import { MultimodalInput } from './multimodal-input';
+
+export function Chat({
+ id,
+ initialMessages,
+ isReadonly,
+}: {
+ id: string;
+ initialMessages: Array;
+ isReadonly: boolean;
+}) {
+ const {
+ messages,
+ setMessages: setChatMessages,
+ handleSubmit,
+ input,
+ setInput,
+ append,
+ status,
+ stop,
+ reload,
+ } = useChatDefault(id, initialMessages);
+
+ const [attachments, setAttachments] = useState>([]);
+
+ const renderEmptyState = () => (
+
+
+
+
+
+ {/*
console.log('Cap selected:', capId)}
+ /> */}
+
+
+ );
+
+ return (
+
+ {/* Chat */}
+
+
+
+ {messages.length === 0 ? (
+ renderEmptyState()
+ ) : (
+ <>
+
+
+
+ >
+ )}
+
+
+ );
+}
diff --git a/src/features/ai-chat/components/markdown-mermaid.tsx b/src/features/chat/components/markdown-mermaid.tsx
similarity index 78%
rename from src/features/ai-chat/components/markdown-mermaid.tsx
rename to src/features/chat/components/markdown-mermaid.tsx
index 0ffa05b2..e5a15456 100644
--- a/src/features/ai-chat/components/markdown-mermaid.tsx
+++ b/src/features/chat/components/markdown-mermaid.tsx
@@ -1,4 +1,4 @@
-import mermaid from "mermaid";
+import mermaid from 'mermaid';
import React, { useEffect, useId, useState } from 'react';
interface MermaidCodeProps {
@@ -6,7 +6,10 @@ interface MermaidCodeProps {
className?: string;
}
-export const MermaidCode: React.FC = ({ code, className }) => {
+export const MermaidCode: React.FC = ({
+ code,
+ className,
+}) => {
// Use React's built-in ID generator
const uniqueId = useId().replace(/:/g, '-');
const [showRaw, setShowRaw] = useState(false);
@@ -14,7 +17,6 @@ export const MermaidCode: React.FC = ({ code, className }) =>
const [renderError, setRenderError] = useState('');
const [isLoading, setIsLoading] = useState(false);
-
useEffect(() => {
mermaid.initialize({
startOnLoad: false,
@@ -27,13 +29,14 @@ export const MermaidCode: React.FC = ({ code, className }) =>
lineColor: '#888888',
fontSize: '16px',
edgeLabelBackground: '#ffffff',
- background: 'transparent'
+ background: 'transparent',
},
- flowchart: {
+ flowchart: {
curve: 'basis',
- diagramPadding: 8
+ diagramPadding: 8,
},
- themeCSS: '.mermaid { font-family: "Patrick Hand", "Comic Sans MS", cursive; background-color: transparent !important; } .mermaid svg { display: block; margin: 0 auto; }',
+ themeCSS:
+ '.mermaid { font-family: "Patrick Hand", "Comic Sans MS", cursive; background-color: transparent !important; } .mermaid svg { display: block; margin: 0 auto; }',
deterministicIds: false, // required for handDrawn
htmlLabels: true,
look: 'handDrawn',
@@ -53,10 +56,10 @@ export const MermaidCode: React.FC = ({ code, className }) =>
// Set loading state
setIsLoading(true);
setRenderError('');
-
+
// Create a unique ID for this render
const diagramId = `mermaid-${uniqueId}-${Date.now()}`;
-
+
// Direct rendering approach
const renderDiagram = async () => {
try {
@@ -65,32 +68,33 @@ export const MermaidCode: React.FC = ({ code, className }) =>
tempContainer.id = diagramId;
tempContainer.style.visibility = 'hidden';
document.body.appendChild(tempContainer);
-
+
// Add the mermaid code to the container
tempContainer.textContent = code;
-
+
// Render it
const { svg } = await mermaid.render(diagramId, code);
-
+
// Update state with the result
setRenderedSvg(svg);
setRenderError('');
-
+
// Cleanup
if (document.body.contains(tempContainer)) {
document.body.removeChild(tempContainer);
}
} catch (error) {
- setRenderError(`Rendering error: ${(error as Error).message || 'Unknown error'}`);
+ setRenderError(
+ `Rendering error: ${(error as Error).message || 'Unknown error'}`,
+ );
setRenderedSvg('');
} finally {
setIsLoading(false);
}
};
-
+
// Run the render
renderDiagram();
-
}, [code, showRaw, uniqueId]);
return (
@@ -100,16 +104,18 @@ export const MermaidCode: React.FC = ({ code, className }) =>
{code}
) : (
- ${renderError}
`
- : 'Loading diagram...
')
- }}
+ dangerouslySetInnerHTML={{
+ __html:
+ renderedSvg ||
+ (renderError
+ ? `${renderError}
`
+ : 'Loading diagram...
'),
+ }}
/>
)}
- setShowRaw(!showRaw)}
className="absolute bottom-2 right-2 px-2 py-1 text-xs bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600"
type="button"
@@ -118,4 +124,4 @@ export const MermaidCode: React.FC = ({ code, className }) =>
);
-};
\ No newline at end of file
+};
diff --git a/src/features/ai-chat/components/markdown.css b/src/features/chat/components/markdown.css
similarity index 100%
rename from src/features/ai-chat/components/markdown.css
rename to src/features/chat/components/markdown.css
diff --git a/src/features/ai-chat/components/markdown.tsx b/src/features/chat/components/markdown.tsx
similarity index 74%
rename from src/features/ai-chat/components/markdown.tsx
rename to src/features/chat/components/markdown.tsx
index 91e72888..be0f7b69 100644
--- a/src/features/ai-chat/components/markdown.tsx
+++ b/src/features/chat/components/markdown.tsx
@@ -4,7 +4,7 @@ import { useTheme } from '@/shared/components/theme-provider';
import './markdown.css';
import React, { ReactNode } from 'react';
import { getCodeString } from 'rehype-rewrite';
-import rehypeSanitize from "rehype-sanitize";
+import rehypeSanitize from 'rehype-sanitize';
import { MermaidCode } from './markdown-mermaid';
interface CodeProps {
@@ -17,12 +17,20 @@ interface CodeProps {
[key: string]: any;
}
-const Code: React.FC = ({ inline, children = [], className, ...props }) => {
- const isMermaid = className && /^language-mermaid/.test(className.toLocaleLowerCase());
+const Code: React.FC = ({
+ inline,
+ children = [],
+ className,
+ ...props
+}) => {
+ const isMermaid =
+ className && /^language-mermaid/.test(className.toLocaleLowerCase());
// original code component
- const code: string = props.node?.children
- ? getCodeString(props.node.children)
- : (Array.isArray(children) && children[0] ? String(children[0]) : '');
+ const code: string = props.node?.children
+ ? getCodeString(props.node.children)
+ : Array.isArray(children) && children[0]
+ ? String(children[0])
+ : '';
if (isMermaid) {
return ;
diff --git a/src/features/ai-chat/components/message-actions.tsx b/src/features/chat/components/message-actions.tsx
similarity index 83%
rename from src/features/ai-chat/components/message-actions.tsx
rename to src/features/chat/components/message-actions.tsx
index 14c99c46..435d7013 100644
--- a/src/features/ai-chat/components/message-actions.tsx
+++ b/src/features/chat/components/message-actions.tsx
@@ -2,7 +2,7 @@ import type { Message } from 'ai';
import { CopyIcon } from 'lucide-react';
import { memo } from 'react';
import { useCopyToClipboard } from 'usehooks-ts';
-import { toast } from '@/shared/components/toast';
+import { toast } from 'sonner';
import { Button } from '@/shared/components/ui/button';
import {
Tooltip,
@@ -39,18 +39,12 @@ export function PureMessageActions({
.trim();
if (!textFromParts) {
- toast({
- type: 'error',
- description: "There's no text to copy!",
- });
+ toast.error("There's no text to copy!");
return;
}
await copyToClipboard(textFromParts);
- toast({
- type: 'success',
- description: 'Copied to clipboard!',
- });
+ toast.success('Copied to clipboard!');
}}
>
diff --git a/src/features/ai-chat/components/message-editor.tsx b/src/features/chat/components/message-editor.tsx
similarity index 93%
rename from src/features/ai-chat/components/message-editor.tsx
rename to src/features/chat/components/message-editor.tsx
index 4800bfa7..0ca33e37 100644
--- a/src/features/ai-chat/components/message-editor.tsx
+++ b/src/features/chat/components/message-editor.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { UseChatHelpers } from '@ai-sdk/react';
import type { Message } from 'ai';
import {
@@ -9,9 +7,9 @@ import {
useRef,
useState,
} from 'react';
-import { useChatSession } from '@/features/ai-chat/hooks/use-chat-session';
import { Button } from '@/shared/components/ui/button';
import { Textarea } from '@/shared/components/ui/textarea';
+import { useChatSessions } from '../hooks/use-chat-sessions';
export type MessageEditorProps = {
chatId: string;
@@ -32,7 +30,7 @@ export function MessageEditor({
const [draftContent, setDraftContent] = useState(message.content);
const textareaRef = useRef(null);
- const { deleteMessagesAfterTimestamp } = useChatSession(chatId);
+ const { deleteMessagesAfterTimestamp } = useChatSessions();
useEffect(() => {
if (textareaRef.current) {
@@ -83,7 +81,7 @@ export function MessageEditor({
// Delete trailing messages using client store
if (message.createdAt) {
const messageTime = new Date(message.createdAt).getTime();
- deleteMessagesAfterTimestamp(messageTime);
+ deleteMessagesAfterTimestamp(chatId, messageTime);
}
// @ts-expect-error todo: support UIMessage in setMessages
diff --git a/src/features/ai-chat/components/message-reasoning.tsx b/src/features/chat/components/message-reasoning.tsx
similarity index 99%
rename from src/features/ai-chat/components/message-reasoning.tsx
rename to src/features/chat/components/message-reasoning.tsx
index 684df99b..e8a4d1be 100644
--- a/src/features/ai-chat/components/message-reasoning.tsx
+++ b/src/features/chat/components/message-reasoning.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import { AnimatePresence, motion } from 'framer-motion';
import { ChevronDownIcon, LoaderIcon } from 'lucide-react';
import { useState } from 'react';
diff --git a/src/features/ai-chat/components/message-source-item.tsx b/src/features/chat/components/message-source-item.tsx
similarity index 100%
rename from src/features/ai-chat/components/message-source-item.tsx
rename to src/features/chat/components/message-source-item.tsx
diff --git a/src/features/ai-chat/components/message-source.tsx b/src/features/chat/components/message-source.tsx
similarity index 99%
rename from src/features/ai-chat/components/message-source.tsx
rename to src/features/chat/components/message-source.tsx
index ffbcdc86..c0f044bf 100644
--- a/src/features/ai-chat/components/message-source.tsx
+++ b/src/features/chat/components/message-source.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import { ChevronDownIcon, ChevronUpIcon, LinkIcon } from 'lucide-react';
import { useState } from 'react';
diff --git a/src/features/ai-chat/components/message-text.tsx b/src/features/chat/components/message-text.tsx
similarity index 99%
rename from src/features/ai-chat/components/message-text.tsx
rename to src/features/chat/components/message-text.tsx
index 29d92ff4..9a5e893c 100644
--- a/src/features/ai-chat/components/message-text.tsx
+++ b/src/features/chat/components/message-text.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { UseChatHelpers } from '@ai-sdk/react';
import type { UIMessage } from 'ai';
import { ChevronDownIcon, ChevronUpIcon, PencilIcon } from 'lucide-react';
diff --git a/src/features/ai-chat/components/message.tsx b/src/features/chat/components/message.tsx
similarity index 70%
rename from src/features/ai-chat/components/message.tsx
rename to src/features/chat/components/message.tsx
index 19e9b1fb..58e77493 100644
--- a/src/features/ai-chat/components/message.tsx
+++ b/src/features/chat/components/message.tsx
@@ -1,21 +1,16 @@
-'use client';
-
import type { UseChatHelpers } from '@ai-sdk/react';
import type { UIMessage } from 'ai';
import cx from 'classnames';
import equal from 'fast-deep-equal';
import { AnimatePresence, motion } from 'framer-motion';
import { memo, useState } from 'react';
-import { DocumentPreview } from '@/features/documents/components/document-preview';
-import { DocumentToolCall } from '@/features/documents/components/document-preview-call';
-import { DocumentToolResult } from '@/features/documents/components/document-preview-result';
import { cn, generateUUID } from '@/shared/utils';
-import { MemoryToolCall } from './memory-tool-call';
import { MessageActions } from './message-actions';
import { MessageReasoning } from './message-reasoning';
import { MessageSource } from './message-source';
import { MessageText } from './message-text';
-import { PreviewAttachment } from './preview-attachment';
+import { ToolCall } from './tool-call';
+import { ToolResult } from './tool-result';
const PurePreviewMessage = ({
chatId,
@@ -58,22 +53,6 @@ const PurePreviewMessage = ({
'min-h-96': message.role === 'assistant' && requiresScrollPadding,
})}
>
- {/* render attachments */}
- {message.experimental_attachments &&
- message.experimental_attachments.length > 0 && (
-
- {message.experimental_attachments.map((attachment) => (
-
- ))}
-
- )}
-
{/* render reasoning */}
{message.parts?.map((part, index) => {
if (part.type !== 'reasoning') return null;
@@ -118,54 +97,29 @@ const PurePreviewMessage = ({
const { args } = toolInvocation;
return (
-
- {toolName === 'createDocument' ? (
-
- ) : toolName === 'updateDocument' ? (
-
- ) : ['saveMemory', 'queryMemory'].includes(toolName) ? (
-
- ) : null}
-
+ />
);
}
if (state === 'result') {
- const { result } = toolInvocation;
+ const { result, args } = toolInvocation;
return (
-
- {toolName === 'createDocument' ? (
-
- ) : toolName === 'updateDocument' ? (
-
- ) : ['saveMemory', 'queryMemory'].includes(toolName) ? (
-
- ) : null}
-
+
);
}
}
diff --git a/src/features/ai-chat/components/messages.tsx b/src/features/chat/components/messages.tsx
similarity index 88%
rename from src/features/ai-chat/components/messages.tsx
rename to src/features/chat/components/messages.tsx
index 8303f5cf..bb60ec86 100644
--- a/src/features/ai-chat/components/messages.tsx
+++ b/src/features/chat/components/messages.tsx
@@ -3,8 +3,7 @@ import type { UIMessage } from 'ai';
import equal from 'fast-deep-equal';
import { motion } from 'framer-motion';
import { memo } from 'react';
-import { useMessages } from '@/features/ai-chat/hooks/use-messages';
-import { Greeting } from './greeting';
+import { useMessagesUI } from '@/features/chat/hooks/use-messages-ui';
import { PreviewMessage, ThinkingMessage } from './message';
interface MessagesProps {
@@ -32,7 +31,7 @@ function PureMessages({
onViewportEnter,
onViewportLeave,
hasSentMessage,
- } = useMessages({
+ } = useMessagesUI({
chatId,
status,
});
@@ -40,10 +39,8 @@ function PureMessages({
return (
- {messages.length === 0 && !isArtifact &&
}
-
{messages.map((message, index) => (
(null);
const { width } = useWindowSize();
- const { uploadFile } = useFiles();
- const { t } = useLanguage();
const isDevMode = useDevMode();
+ const { currentCap, isCurrentCapMCPInitialized, isCurrentCapMCPError } =
+ useCurrentCap();
const [localStorageInput, setLocalStorageInput] = useLocalStorage(
'input',
@@ -84,6 +81,13 @@ function PureMultimodalInput({
setLocalStorageInput(input);
}, [input, setLocalStorageInput]);
+ // Auto focus when chat ID changes (page navigation)
+ useEffect(() => {
+ if (textareaRef.current && width && width > 768) {
+ textareaRef.current.focus();
+ }
+ }, [chatId, width]);
+
const handleInput = (event: React.ChangeEvent) => {
setInput(event.target.value);
};
@@ -92,46 +96,31 @@ function PureMultimodalInput({
const [uploadQueue, setUploadQueue] = useState>([]);
const submitForm = useCallback(() => {
- handleSubmit(undefined, {
- experimental_attachments: attachments,
- });
+ // Check if Cap has MCP servers and if they are initialized
+ const hasMCPServers =
+ currentCap?.core?.mcpServers &&
+ Object.keys(currentCap.core.mcpServers).length > 0;
+
+ if (hasMCPServers && !isCurrentCapMCPInitialized) {
+ toast.error('Cap MCP is initializing...');
+ return;
+ }
+
+ handleSubmit(undefined);
- setAttachments([]);
setLocalStorageInput('');
if (width && width > 768) {
textareaRef.current?.focus();
}
- }, [attachments, handleSubmit, setAttachments, setLocalStorageInput, width]);
-
- const handleFileChange = useCallback(
- async (event: ChangeEvent) => {
- const files = Array.from(event.target.files || []);
-
- setUploadQueue(files.map((file) => file.name));
-
- try {
- const uploadPromises = files.map((file) => uploadFile(file));
- const uploadedAttachments = await Promise.all(uploadPromises);
- const successfullyUploadedAttachments = uploadedAttachments.filter(
- (attachment) => attachment !== undefined,
- );
-
- setAttachments((currentAttachments) => [
- ...currentAttachments,
- ...successfullyUploadedAttachments,
- ]);
- } catch (error) {
- toast({
- description: t('upload.errorUploading'),
- type: 'error',
- });
- } finally {
- setUploadQueue([]);
- }
- },
- [setAttachments, uploadFile, t],
- );
+ }, [
+ handleSubmit,
+ setLocalStorageInput,
+ width,
+ currentCap,
+ isCurrentCapMCPInitialized,
+ isCurrentCapMCPError,
+ ]);
const { isAtBottom, scrollToBottom } = useScrollToBottom();
@@ -144,7 +133,7 @@ function PureMultimodalInput({
return (
- {!isAtBottom && (
+ {messages.length > 0 && !isAtBottom && (
- {messages.length === 0 &&
- attachments.length === 0 &&
- uploadQueue.length === 0 && }
-
-
-
- {(attachments.length > 0 || uploadQueue.length > 0) && (
-
- {attachments.map((attachment) => (
-
- ))}
-
- {uploadQueue.map((filename) => (
-
- ))}
-
- )}
+ {messages.length === 0 && }
- {isDevMode && (
-
- )}
-
+
+ {/* {isDevMode && (
+
+ )} */}
{status === 'submitted' || status === 'streaming' ? (
) : (
@@ -261,6 +214,9 @@ function PureMultimodalInput({
input={input}
submitForm={submitForm}
uploadQueue={uploadQueue}
+ currentCap={currentCap}
+ isCurrentCapMCPInitialized={isCurrentCapMCPInitialized}
+ isCurrentCapMCPError={isCurrentCapMCPError}
/>
)}
@@ -334,11 +290,22 @@ function PureSendButton({
submitForm,
input,
uploadQueue,
+ currentCap,
+ isCurrentCapMCPInitialized,
+ isCurrentCapMCPError,
}: {
submitForm: () => void;
input: string;
uploadQueue: Array
;
+ currentCap: Cap;
+ isCurrentCapMCPInitialized: boolean;
+ isCurrentCapMCPError: boolean;
}) {
+ // Check if Cap has MCP servers
+ const hasMCPServers =
+ currentCap?.core?.mcpServers &&
+ Object.keys(currentCap.core.mcpServers).length > 0;
+
return (
0}
+ disabled={
+ input.length === 0 ||
+ uploadQueue.length > 0 ||
+ (hasMCPServers && (!isCurrentCapMCPInitialized || isCurrentCapMCPError))
+ }
>
diff --git a/src/features/chat/components/rename-dialog.tsx b/src/features/chat/components/rename-dialog.tsx
new file mode 100644
index 00000000..dbc3bcc5
--- /dev/null
+++ b/src/features/chat/components/rename-dialog.tsx
@@ -0,0 +1,84 @@
+import { useState, useEffect } from 'react';
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+ Button,
+ Input,
+} from '@/shared/components/ui';
+import { useLanguage } from '@/shared/hooks/use-language';
+
+interface RenameDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ currentName: string;
+ onRename: (newName: string) => void;
+}
+
+export function RenameDialog({
+ open,
+ onOpenChange,
+ currentName,
+ onRename,
+}: RenameDialogProps) {
+ const { t } = useLanguage();
+ const [name, setName] = useState(currentName);
+
+ useEffect(() => {
+ if (open) {
+ setName(currentName);
+ }
+ }, [open, currentName]);
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ const trimmedName = name.trim();
+ if (trimmedName && trimmedName !== currentName) {
+ onRename(trimmedName);
+ }
+ onOpenChange(false);
+ };
+
+ const handleCancel = () => {
+ setName(currentName);
+ onOpenChange(false);
+ };
+
+ return (
+
+ );
+}
diff --git a/src/features/ai-chat/components/submit-button.tsx b/src/features/chat/components/submit-button.tsx
similarity index 98%
rename from src/features/ai-chat/components/submit-button.tsx
rename to src/features/chat/components/submit-button.tsx
index b0ef0ddf..40f14482 100644
--- a/src/features/ai-chat/components/submit-button.tsx
+++ b/src/features/chat/components/submit-button.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import { LoaderIcon } from 'lucide-react';
import { useFormStatus } from 'react-dom';
import { Button } from '@/shared/components/ui/button';
diff --git a/src/features/ai-chat/components/suggested-actions.tsx b/src/features/chat/components/suggested-actions.tsx
similarity index 61%
rename from src/features/ai-chat/components/suggested-actions.tsx
rename to src/features/chat/components/suggested-actions.tsx
index 7aa201e5..a63843a9 100644
--- a/src/features/ai-chat/components/suggested-actions.tsx
+++ b/src/features/chat/components/suggested-actions.tsx
@@ -1,27 +1,20 @@
-'use client';
-
import type { UseChatHelpers } from '@ai-sdk/react';
import { motion } from 'framer-motion';
import { memo } from 'react';
import { Button } from '@/shared/components/ui';
-import { useLanguage } from '@/shared/hooks/use-language';
+import { useSuggestedActions } from '../hooks/use-suggested-actions';
interface SuggestedActionsProps {
append: UseChatHelpers['append'];
}
function PureSuggestedActions({ append }: SuggestedActionsProps) {
- const { t } = useLanguage();
- const suggestedActions = t('suggestedActions') as Array<{
- title: string;
- label: string;
- action: string;
- }>;
+ const suggestedActions = useSuggestedActions();
return (
{suggestedActions.map((suggestedAction, index) => (
1 ? 'hidden sm:block' : 'block'}
>
- {suggestedAction.title}
-
- {suggestedAction.label}
-
+ {suggestedAction.title}
))}
diff --git a/src/features/chat/components/tool-call.tsx b/src/features/chat/components/tool-call.tsx
new file mode 100644
index 00000000..2a26fb2b
--- /dev/null
+++ b/src/features/chat/components/tool-call.tsx
@@ -0,0 +1,78 @@
+import { ChevronDownIcon, ChevronUpIcon, Loader } from 'lucide-react';
+import { useState } from 'react';
+import { Badge } from '@/shared/components/ui/badge';
+import {
+ Card,
+ CardContent,
+ CardHeader,
+ CardTitle,
+} from '@/shared/components/ui/card';
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from '@/shared/components/ui/collapsible';
+import { cn } from '@/shared/utils';
+
+interface ToolCallProps {
+ toolName: string;
+ toolCallId: string;
+ args: Record
;
+ className?: string;
+}
+
+export const ToolCall = ({
+ toolName,
+ toolCallId,
+ args,
+ className,
+}: ToolCallProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ return (
+
+
+
+
+
+
+ Tool Call
+
+ {toolName}
+
+
+ {isOpen ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ ID:{' '}
+
+ {toolCallId}
+
+
+ {Object.keys(args).length > 0 && (
+
+
+ Arguments:
+
+
+
{JSON.stringify(args, null, 2)}
+
+
+ )}
+
+
+
+
+
+ );
+};
diff --git a/src/features/chat/components/tool-result.tsx b/src/features/chat/components/tool-result.tsx
new file mode 100644
index 00000000..85a4c7cd
--- /dev/null
+++ b/src/features/chat/components/tool-result.tsx
@@ -0,0 +1,97 @@
+import { CheckCircleIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
+import { useState } from 'react';
+import { Badge } from '@/shared/components/ui/badge';
+import {
+ Card,
+ CardContent,
+ CardHeader,
+ CardTitle,
+} from '@/shared/components/ui/card';
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from '@/shared/components/ui/collapsible';
+import { cn } from '@/shared/utils';
+
+interface ToolResultProps {
+ toolName: string;
+ toolCallId: string;
+ result: any;
+ args?: Record;
+ className?: string;
+}
+
+export const ToolResult = ({
+ toolName,
+ toolCallId,
+ result,
+ args,
+ className,
+}: ToolResultProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const formatResult = (result: any) => {
+ if (typeof result === 'string') {
+ return result;
+ }
+ return JSON.stringify(result, null, 2);
+ };
+
+ return (
+
+
+
+
+
+
+ Tool Result
+
+ {toolName}
+
+
+ {isOpen ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ ID:{' '}
+
+ {toolCallId}
+
+
+ {args && Object.keys(args).length > 0 && (
+
+
+ Arguments:
+
+
+
{JSON.stringify(args, null, 2)}
+
+
+ )}
+
+
+ Result:
+
+
+
+ {formatResult(result)}
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/shared/errors/chatsdk-errors.ts b/src/features/chat/errors/chatsdk-errors.ts
similarity index 100%
rename from src/shared/errors/chatsdk-errors.ts
rename to src/features/chat/errors/chatsdk-errors.ts
diff --git a/src/shared/errors/error-handler.ts b/src/features/chat/errors/error-handler.ts
similarity index 100%
rename from src/shared/errors/error-handler.ts
rename to src/features/chat/errors/error-handler.ts
diff --git a/src/shared/errors/index.ts b/src/features/chat/errors/index.ts
similarity index 100%
rename from src/shared/errors/index.ts
rename to src/features/chat/errors/index.ts
diff --git a/src/features/chat/hooks/index.ts b/src/features/chat/hooks/index.ts
new file mode 100644
index 00000000..d6bfb9d6
--- /dev/null
+++ b/src/features/chat/hooks/index.ts
@@ -0,0 +1,6 @@
+export * from './use-chat-default';
+export * from './use-chat-page';
+export * from './use-chat-sessions';
+export * from './use-messages-ui';
+export * from './use-scroll-to-bottom';
+export * from './use-update-chat-title';
diff --git a/src/features/ai-chat/hooks/use-chat-default.ts b/src/features/chat/hooks/use-chat-default.ts
similarity index 83%
rename from src/features/ai-chat/hooks/use-chat-default.ts
rename to src/features/chat/hooks/use-chat-default.ts
index a71af47f..23e16e2c 100644
--- a/src/features/ai-chat/hooks/use-chat-default.ts
+++ b/src/features/chat/hooks/use-chat-default.ts
@@ -1,17 +1,19 @@
import { useChat } from '@ai-sdk/react';
import type { UIMessage } from 'ai';
import { useNavigate } from 'react-router-dom';
-import { ChatSDKError } from '@/shared/errors/chatsdk-errors';
-import { ErrorHandlers } from '@/shared/errors/error-handler';
import { generateUUID } from '@/shared/utils';
+import { ChatSDKError } from '../errors/chatsdk-errors';
+import { ErrorHandlers } from '../errors/error-handler';
import { createClientAIFetch } from '../services';
+import { useUpdateChatTitle } from './use-update-chat-title';
export const useChatDefault = (
chatId: string,
initialMessages: UIMessage[],
- handleOnResponse?: (response: any) => void,
) => {
const navigate = useNavigate();
+ const { updateTitle } = useUpdateChatTitle(chatId);
+
const handleUseChatError = (error: Error) => {
let errorMessage: UIMessage;
if (error instanceof ChatSDKError) {
@@ -29,6 +31,11 @@ export const useChatDefault = (
setChatMessages((messages) => [...messages, errorMessage]);
};
+ const handleOnResponse = () => {
+ updateTitle();
+ navigate(`/chat?cid=${chatId}`);
+ };
+
const {
messages,
setMessages: setChatMessages,
diff --git a/src/features/ai-chat/hooks/use-chat-page.ts b/src/features/chat/hooks/use-chat-page.ts
similarity index 84%
rename from src/features/ai-chat/hooks/use-chat-page.ts
rename to src/features/chat/hooks/use-chat-page.ts
index c211c605..7156332f 100644
--- a/src/features/ai-chat/hooks/use-chat-page.ts
+++ b/src/features/chat/hooks/use-chat-page.ts
@@ -1,12 +1,8 @@
-'use client';
-
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
-import {
- type ChatSession,
- createInitialChatSession,
-} from '@/features/ai-chat/stores';
-import { convertToUIMessage } from '@/features/ai-chat/utils';
+import { createInitialChatSession } from '../stores';
+import type { ChatSession } from '../types';
+import { convertToUIMessage } from '../utils';
import { useChatSessions } from './use-chat-sessions';
// Specialized hook for chat page logic
diff --git a/src/features/chat/hooks/use-chat-sessions.ts b/src/features/chat/hooks/use-chat-sessions.ts
new file mode 100644
index 00000000..77cb72ba
--- /dev/null
+++ b/src/features/chat/hooks/use-chat-sessions.ts
@@ -0,0 +1,65 @@
+import { useCallback } from 'react';
+import { ChatStateStore } from '../stores';
+import type { ChatSession } from '../types';
+
+export const useChatSessions = () => {
+ const store = ChatStateStore();
+
+ const getSession = useCallback((id: string) => {
+ return store.readSession(id);
+ }, []);
+
+ const deleteSession = useCallback((id: string) => {
+ store.deleteSession(id);
+ }, []);
+
+ const updateSession = useCallback(
+ (id: string, updates: Partial>) => {
+ store.updateSession(id, updates);
+ },
+ [],
+ );
+
+ const clearAllSessions = useCallback(() => {
+ store.clearAllSessions();
+ }, []);
+
+ const getSortedSessions = useCallback(() => {
+ return Object.values(store.sessions).sort(
+ (a, b) => b.updatedAt - a.updatedAt,
+ );
+ }, [store.sessions]);
+
+ const deleteMessagesAfterTimestamp = useCallback(
+ (sessionId: string, timestamp: number) => {
+ const currentSession = store.readSession(sessionId);
+ if (!currentSession) return;
+
+ const updatedMessages = currentSession.messages.filter((msg) => {
+ const messageTime = msg.createdAt
+ ? new Date(msg.createdAt).getTime()
+ : 0;
+ return messageTime < timestamp;
+ });
+
+ const updatedSession: ChatSession = {
+ ...currentSession,
+ messages: updatedMessages,
+ updatedAt: Date.now(),
+ };
+
+ store.updateSession(sessionId, updatedSession);
+ },
+ [store],
+ );
+
+ return {
+ sessions: getSortedSessions(),
+ sessionsMap: store.sessions,
+ getSession,
+ deleteSession,
+ updateSession,
+ clearAllSessions,
+ deleteMessagesAfterTimestamp,
+ };
+};
diff --git a/src/features/ai-chat/hooks/use-messages.ts b/src/features/chat/hooks/use-messages-ui.ts
similarity index 96%
rename from src/features/ai-chat/hooks/use-messages.ts
rename to src/features/chat/hooks/use-messages-ui.ts
index 62405068..f2b6da51 100644
--- a/src/features/ai-chat/hooks/use-messages.ts
+++ b/src/features/chat/hooks/use-messages-ui.ts
@@ -2,7 +2,7 @@ import type { UseChatHelpers } from '@ai-sdk/react';
import { useEffect, useState } from 'react';
import { useScrollToBottom } from './use-scroll-to-bottom';
-export function useMessages({
+export function useMessagesUI({
chatId,
status,
}: {
diff --git a/src/features/ai-chat/hooks/use-scroll-to-bottom.ts b/src/features/chat/hooks/use-scroll-to-bottom.ts
similarity index 100%
rename from src/features/ai-chat/hooks/use-scroll-to-bottom.ts
rename to src/features/chat/hooks/use-scroll-to-bottom.ts
diff --git a/src/features/chat/hooks/use-suggested-actions.ts b/src/features/chat/hooks/use-suggested-actions.ts
new file mode 100644
index 00000000..965165ed
--- /dev/null
+++ b/src/features/chat/hooks/use-suggested-actions.ts
@@ -0,0 +1,18 @@
+import { useCurrentCap } from '@/shared/hooks/use-current-cap';
+
+export interface SuggestedAction {
+ title: string;
+ action: string;
+}
+
+export function useSuggestedActions(): SuggestedAction[] {
+ const { currentCap: cap } = useCurrentCap();
+
+ const suggestedActions =
+ cap?.core.prompt.suggestions?.map((suggestion) => ({
+ title: suggestion,
+ action: suggestion,
+ })) || [];
+
+ return suggestedActions;
+}
diff --git a/src/features/chat/hooks/use-update-chat-title.ts b/src/features/chat/hooks/use-update-chat-title.ts
new file mode 100644
index 00000000..818ad640
--- /dev/null
+++ b/src/features/chat/hooks/use-update-chat-title.ts
@@ -0,0 +1,30 @@
+import { useCallback } from 'react';
+import { generateTitleFromUserMessage } from '../services';
+import { ChatStateStore } from '../stores';
+import type { ChatSession } from '../types';
+
+export const useUpdateChatTitle = (sessionId: string) => {
+ const store = ChatStateStore();
+
+ const updateTitle = useCallback(async () => {
+ const session = store.readSession(sessionId);
+ if (!session) return;
+
+ const firstMessage = session.messages[0];
+
+ if (!firstMessage) return;
+
+ const title = await generateTitleFromUserMessage({ message: firstMessage });
+
+ const updatedSession: ChatSession = {
+ ...session,
+ title: title,
+ };
+
+ store.updateSession(sessionId, updatedSession);
+ }, [sessionId]);
+
+ return {
+ updateTitle,
+ };
+};
diff --git a/src/features/chat/services/cap-resolve.ts b/src/features/chat/services/cap-resolve.ts
new file mode 100644
index 00000000..877270ee
--- /dev/null
+++ b/src/features/chat/services/cap-resolve.ts
@@ -0,0 +1,91 @@
+import type { LanguageModelV1 } from '@ai-sdk/provider';
+import { GlobalMCPManager } from '@/shared/services/global-mcp-manager';
+import { CurrentCapStore } from '@/shared/stores/current-cap-store';
+import type { Cap } from '@/shared/types/cap';
+import { llmProvider } from './providers';
+
+export class CapResolve {
+ private cap: Cap;
+ private isCurrentCapMCPError: boolean;
+ private hasMCPServers: boolean;
+
+ constructor() {
+ const { currentCap, isCurrentCapMCPError } = CurrentCapStore.getState();
+ if (!currentCap) {
+ throw new Error('No cap selected. Please select a cap to use.');
+ }
+ this.cap = currentCap;
+ this.isCurrentCapMCPError = isCurrentCapMCPError;
+ this.hasMCPServers = Object.keys(this.cap.core.mcpServers).length > 0;
+ }
+
+ private async getUserLocation(): Promise {
+ try {
+ // Try to get user location using browser geolocation API
+ const position = await new Promise(
+ (resolve, reject) => {
+ if (!navigator.geolocation) {
+ reject(new Error('Geolocation not supported'));
+ return;
+ }
+ navigator.geolocation.getCurrentPosition(resolve, reject, {
+ timeout: 5000,
+ enableHighAccuracy: false,
+ });
+ },
+ );
+
+ // Use reverse geocoding or return coordinates
+ const { latitude, longitude } = position.coords;
+ return `${latitude.toFixed(2)}, ${longitude.toFixed(2)}`;
+ } catch {
+ // Fallback to browser language/timezone based location
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+ return timezone.split('/').pop() || 'Unknown';
+ }
+ }
+
+ private async resolveVariables(prompt: string): Promise {
+ let resolvedPrompt = prompt;
+
+ // Resolve {{user_geo}} variable
+ if (resolvedPrompt.includes('{{user_geo}}')) {
+ const userLocation = await this.getUserLocation();
+ resolvedPrompt = resolvedPrompt.replace(
+ /\{\{user_geo\}\}/g,
+ userLocation,
+ );
+ }
+
+ return resolvedPrompt;
+ }
+
+ async getResolvedPrompt(): Promise {
+ return await this.resolveVariables(this.cap.core.prompt.value);
+ }
+
+ getResolvedModel(): LanguageModelV1 {
+ return llmProvider.chat(this.cap.core.model.id);
+ }
+
+ async getResolvedTools(): Promise> {
+ if (this.hasMCPServers && !this.isCurrentCapMCPError) {
+ // Make sure MCP is initialized through global manager
+ const mcpManager = GlobalMCPManager.getInstance();
+ await mcpManager.initializeForCap(this.cap);
+
+ // Get tools from global manager
+ return mcpManager.getCurrentTools();
+ } else {
+ return {};
+ }
+ }
+
+ async getResolvedConfig() {
+ return {
+ prompt: await this.getResolvedPrompt(),
+ model: this.getResolvedModel(),
+ tools: await this.getResolvedTools(),
+ };
+ }
+}
diff --git a/src/features/ai-chat/services/handler/index.ts b/src/features/chat/services/handler.ts
similarity index 73%
rename from src/features/ai-chat/services/handler/index.ts
rename to src/features/chat/services/handler.ts
index 11b75243..75d11516 100644
--- a/src/features/ai-chat/services/handler/index.ts
+++ b/src/features/chat/services/handler.ts
@@ -5,13 +5,9 @@ import {
smoothStream,
streamText,
} from 'ai';
-import { ChatStateStore } from '@/features/ai-chat/stores/chat-store';
-import { llmProvider } from '@/features/ai-provider/services';
-import { CapStateStore } from '@/features/cap/stores';
-import { SettingsStateStore } from '@/features/settings/stores';
import { generateUUID } from '@/shared/utils';
-import { devModeSystemPrompt, systemPrompt } from '../prompts';
-import { tools } from '../tools';
+import { ChatStateStore } from '../stores';
+import { CapResolve } from './cap-resolve';
// Error handling function
function errorHandler(error: unknown) {
@@ -62,16 +58,16 @@ const handleAIRequest = async ({
messages: Message[];
signal?: AbortSignal;
}) => {
- const { updateMessages } = ChatStateStore.getState();
- const isDevMode = SettingsStateStore.getState().settings.devMode;
- await updateMessages(sessionId, messages);
-
- const { currentCap } = CapStateStore.getState();
+ // Resolve cap configuration
+ const capResolve = new CapResolve();
+ const { prompt, model, tools } = await capResolve.getResolvedConfig();
- const prompt = isDevMode ? (currentCap? currentCap.prompt: devModeSystemPrompt()) : systemPrompt();
+ // update the messages state
+ const { updateMessages } = ChatStateStore.getState();
+ updateMessages(sessionId, messages);
const result = streamText({
- model: llmProvider.chat(),
+ model,
system: prompt,
messages,
maxSteps: 5,
@@ -79,7 +75,8 @@ const handleAIRequest = async ({
experimental_generateMessageId: generateUUID,
tools,
abortSignal: signal,
- async onFinish({ response, reasoning, sources }) {
+ async onFinish({ response, sources }) {
+ // append response messages
const finalMessages = appendResponseMessages({
messages: messages,
responseMessages: response.messages,
@@ -93,10 +90,12 @@ const handleAIRequest = async ({
sources,
);
- await updateMessages(sessionId, finalMessagesWithSources);
+ // update the messages state
+ updateMessages(sessionId, finalMessagesWithSources);
},
});
+ // stream the response
const dataStreamResponse = result.toDataStreamResponse({
getErrorMessage: errorHandler,
sendReasoning: true,
diff --git a/src/features/ai-chat/services/index.ts b/src/features/chat/services/index.ts
similarity index 93%
rename from src/features/ai-chat/services/index.ts
rename to src/features/chat/services/index.ts
index cfa485a6..6c990e66 100644
--- a/src/features/ai-chat/services/index.ts
+++ b/src/features/chat/services/index.ts
@@ -1,6 +1,4 @@
-'use client';
-
-import { ChatSDKError } from '@/shared/errors/chatsdk-errors';
+import { ChatSDKError } from '../errors/chatsdk-errors';
import { handleAIRequest } from './handler';
export const createClientAIFetch = (): ((
diff --git a/src/features/chat/services/providers/index.ts b/src/features/chat/services/providers/index.ts
new file mode 100644
index 00000000..6611d07c
--- /dev/null
+++ b/src/features/chat/services/providers/index.ts
@@ -0,0 +1,24 @@
+import { createOpenAI } from '@ai-sdk/openai';
+import { createAuthorizedFetch } from '@/shared/services/authorized-fetch';
+import { createOpenRouter } from './openrouter-provider';
+
+// Settings of Nuwa LLM Gateway
+const providerSettings = {
+ apiKey: 'NOT-USED', // specify a fake api key to avoid provider errors
+ baseURL: 'https://test-llm.nuwa.dev/api/v1',
+ fetch: createAuthorizedFetch(),
+};
+
+const openrouter = createOpenRouter(providerSettings);
+const openai = createOpenAI(providerSettings);
+
+// Export a provider that dynamically resolves models
+export const llmProvider = {
+ chat: (modelId: string) => {
+ return openrouter.chat(modelId);
+ },
+ utility: () => {
+ return openrouter.chat('openai/gpt-4o-mini');
+ },
+ image: (modelId: string) => openai.image(modelId),
+};
diff --git a/src/features/ai-provider/services/litellm-provider/convert-to-litellm-chat-messages.ts b/src/features/chat/services/providers/litellm-provider/convert-to-litellm-chat-messages.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/convert-to-litellm-chat-messages.ts
rename to src/features/chat/services/providers/litellm-provider/convert-to-litellm-chat-messages.ts
diff --git a/src/features/ai-provider/services/litellm-provider/convert-to-litellm-completion-prompt.ts b/src/features/chat/services/providers/litellm-provider/convert-to-litellm-completion-prompt.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/convert-to-litellm-completion-prompt.ts
rename to src/features/chat/services/providers/litellm-provider/convert-to-litellm-completion-prompt.ts
diff --git a/src/features/ai-provider/services/litellm-provider/get-response-metadata.ts b/src/features/chat/services/providers/litellm-provider/get-response-metadata.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/get-response-metadata.ts
rename to src/features/chat/services/providers/litellm-provider/get-response-metadata.ts
diff --git a/src/features/ai-provider/services/litellm-provider/index.ts b/src/features/chat/services/providers/litellm-provider/index.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/index.ts
rename to src/features/chat/services/providers/litellm-provider/index.ts
diff --git a/src/features/ai-provider/services/litellm-provider/internal/index.ts b/src/features/chat/services/providers/litellm-provider/internal/index.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/internal/index.ts
rename to src/features/chat/services/providers/litellm-provider/internal/index.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-api-types.ts b/src/features/chat/services/providers/litellm-provider/litellm-api-types.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-api-types.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-api-types.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-chat-language-model.ts b/src/features/chat/services/providers/litellm-provider/litellm-chat-language-model.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-chat-language-model.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-chat-language-model.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-chat-settings.ts b/src/features/chat/services/providers/litellm-provider/litellm-chat-settings.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-chat-settings.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-chat-settings.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-completion-language-model.ts b/src/features/chat/services/providers/litellm-provider/litellm-completion-language-model.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-completion-language-model.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-completion-language-model.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-completion-settings.ts b/src/features/chat/services/providers/litellm-provider/litellm-completion-settings.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-completion-settings.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-completion-settings.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-embedding-model.ts b/src/features/chat/services/providers/litellm-provider/litellm-embedding-model.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-embedding-model.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-embedding-model.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-embedding-settings.ts b/src/features/chat/services/providers/litellm-provider/litellm-embedding-settings.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-embedding-settings.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-embedding-settings.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-error.ts b/src/features/chat/services/providers/litellm-provider/litellm-error.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-error.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-error.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-image-model.ts b/src/features/chat/services/providers/litellm-provider/litellm-image-model.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-image-model.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-image-model.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-image-settings.ts b/src/features/chat/services/providers/litellm-provider/litellm-image-settings.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-image-settings.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-image-settings.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-metadata-extractor.ts b/src/features/chat/services/providers/litellm-provider/litellm-metadata-extractor.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-metadata-extractor.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-metadata-extractor.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-prepare-tools.ts b/src/features/chat/services/providers/litellm-provider/litellm-prepare-tools.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-prepare-tools.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-prepare-tools.ts
diff --git a/src/features/ai-provider/services/litellm-provider/litellm-provider.ts b/src/features/chat/services/providers/litellm-provider/litellm-provider.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/litellm-provider.ts
rename to src/features/chat/services/providers/litellm-provider/litellm-provider.ts
diff --git a/src/features/ai-provider/services/litellm-provider/map-litellm-finish-reason.ts b/src/features/chat/services/providers/litellm-provider/map-litellm-finish-reason.ts
similarity index 100%
rename from src/features/ai-provider/services/litellm-provider/map-litellm-finish-reason.ts
rename to src/features/chat/services/providers/litellm-provider/map-litellm-finish-reason.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/convert-to-openrouter-chat-messages.ts b/src/features/chat/services/providers/openrouter-provider/convert-to-openrouter-chat-messages.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/convert-to-openrouter-chat-messages.ts
rename to src/features/chat/services/providers/openrouter-provider/convert-to-openrouter-chat-messages.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/convert-to-openrouter-completion-prompt.ts b/src/features/chat/services/providers/openrouter-provider/convert-to-openrouter-completion-prompt.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/convert-to-openrouter-completion-prompt.ts
rename to src/features/chat/services/providers/openrouter-provider/convert-to-openrouter-completion-prompt.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/index.ts b/src/features/chat/services/providers/openrouter-provider/index.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/index.ts
rename to src/features/chat/services/providers/openrouter-provider/index.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/internal/index.ts b/src/features/chat/services/providers/openrouter-provider/internal/index.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/internal/index.ts
rename to src/features/chat/services/providers/openrouter-provider/internal/index.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/map-openrouter-chat-logprobs.ts b/src/features/chat/services/providers/openrouter-provider/map-openrouter-chat-logprobs.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/map-openrouter-chat-logprobs.ts
rename to src/features/chat/services/providers/openrouter-provider/map-openrouter-chat-logprobs.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/map-openrouter-completion-logprobs.ts b/src/features/chat/services/providers/openrouter-provider/map-openrouter-completion-logprobs.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/map-openrouter-completion-logprobs.ts
rename to src/features/chat/services/providers/openrouter-provider/map-openrouter-completion-logprobs.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/map-openrouter-finish-reason.ts b/src/features/chat/services/providers/openrouter-provider/map-openrouter-finish-reason.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/map-openrouter-finish-reason.ts
rename to src/features/chat/services/providers/openrouter-provider/map-openrouter-finish-reason.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/openrouter-chat-language-model.ts b/src/features/chat/services/providers/openrouter-provider/openrouter-chat-language-model.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/openrouter-chat-language-model.ts
rename to src/features/chat/services/providers/openrouter-provider/openrouter-chat-language-model.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/openrouter-completion-language-model.ts b/src/features/chat/services/providers/openrouter-provider/openrouter-completion-language-model.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/openrouter-completion-language-model.ts
rename to src/features/chat/services/providers/openrouter-provider/openrouter-completion-language-model.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/openrouter-completion-settings.ts b/src/features/chat/services/providers/openrouter-provider/openrouter-completion-settings.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/openrouter-completion-settings.ts
rename to src/features/chat/services/providers/openrouter-provider/openrouter-completion-settings.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/openrouter-error.ts b/src/features/chat/services/providers/openrouter-provider/openrouter-error.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/openrouter-error.ts
rename to src/features/chat/services/providers/openrouter-provider/openrouter-error.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/openrouter-facade.ts b/src/features/chat/services/providers/openrouter-provider/openrouter-facade.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/openrouter-facade.ts
rename to src/features/chat/services/providers/openrouter-provider/openrouter-facade.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/openrouter-provider.ts b/src/features/chat/services/providers/openrouter-provider/openrouter-provider.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/openrouter-provider.ts
rename to src/features/chat/services/providers/openrouter-provider/openrouter-provider.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/schemas/reasoning-details.ts b/src/features/chat/services/providers/openrouter-provider/schemas/reasoning-details.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/schemas/reasoning-details.ts
rename to src/features/chat/services/providers/openrouter-provider/schemas/reasoning-details.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/types/index.ts b/src/features/chat/services/providers/openrouter-provider/types/index.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/types/index.ts
rename to src/features/chat/services/providers/openrouter-provider/types/index.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/types/openrouter-chat-completions-input.ts b/src/features/chat/services/providers/openrouter-provider/types/openrouter-chat-completions-input.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/types/openrouter-chat-completions-input.ts
rename to src/features/chat/services/providers/openrouter-provider/types/openrouter-chat-completions-input.ts
diff --git a/src/features/ai-provider/services/openrouter-provider/types/openrouter-chat-settings.ts b/src/features/chat/services/providers/openrouter-provider/types/openrouter-chat-settings.ts
similarity index 100%
rename from src/features/ai-provider/services/openrouter-provider/types/openrouter-chat-settings.ts
rename to src/features/chat/services/providers/openrouter-provider/types/openrouter-chat-settings.ts
diff --git a/src/features/ai-chat/services/utility-ai.ts b/src/features/chat/services/utility-ai.ts
similarity index 92%
rename from src/features/ai-chat/services/utility-ai.ts
rename to src/features/chat/services/utility-ai.ts
index bbb4cd1b..adadd5ac 100644
--- a/src/features/ai-chat/services/utility-ai.ts
+++ b/src/features/chat/services/utility-ai.ts
@@ -1,5 +1,5 @@
import { generateText, type Message } from 'ai';
-import { llmProvider } from '@/features/ai-provider/services';
+import { llmProvider } from './providers';
// Generate a title from the first message a user begins a conversation with
// TODO: currently still using the remote AI models, need to switch to local models
diff --git a/src/features/ai-chat/stores/chat-store.ts b/src/features/chat/stores.ts
similarity index 53%
rename from src/features/ai-chat/stores/chat-store.ts
rename to src/features/chat/stores.ts
index c3b52c5c..6773134f 100644
--- a/src/features/ai-chat/stores/chat-store.ts
+++ b/src/features/chat/stores.ts
@@ -4,11 +4,10 @@
import type { Message } from 'ai';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
-import { NuwaIdentityKit } from '@/features/auth/services';
+import { NuwaIdentityKit } from '@/shared/services/identity-kit';
+import { createPersistConfig, db } from '@/shared/storage';
import { generateUUID } from '@/shared/utils';
-import { createPersistConfig, db } from '@/storage';
-import { generateTitleFromUserMessage } from '../services';
-import type { ChatSession, StreamRecord } from '../types';
+import type { ChatSession } from './types';
// ================= Constants ================= //
export const createInitialChatSession = (): ChatSession => ({
@@ -27,38 +26,25 @@ const getCurrentDID = async () => {
// ================= Database Reference ================= //
-// 使用统一数据库,不再需要单独的ChatDatabase
const chatDB = db;
// chat store state interface
interface ChatStoreState {
sessions: Record;
- // session management
- getSession: (id: string) => ChatSession | null;
+ // session CRUD operations
+ createSession: (session?: Partial) => ChatSession;
+ readSession: (id: string) => ChatSession | null;
updateSession: (
id: string,
updates: Partial>,
) => void;
deleteSession: (id: string) => void;
- // message management
+ // update messages for a session
updateMessages: (sessionId: string, messages: Message[]) => void;
- updateSingleMessage: (
- sessionId: string,
- messageId: string,
- updates: Partial,
- ) => void;
- deleteMessage: (sessionId: string, messageId: string) => void;
- deleteMessagesAfterTimestamp: (sessionId: string, timestamp: number) => void;
- getMessages: (sessionId: string) => Message[];
-
- // stream management
- createStreamId: (streamId: string, chatId: string) => Promise;
- getStreamIdsByChatId: (chatId: string) => Promise;
- // tool methods
- updateTitle: (chatId: string) => Promise;
+ // utility methods
clearAllSessions: () => void;
// data persistence
@@ -88,7 +74,28 @@ export const ChatStateStore = create()(
(set, get) => ({
sessions: {},
- getSession: (id: string) => {
+ // Session CRUD operations
+ createSession: (session?: Partial) => {
+ const newSession: ChatSession = {
+ id: session?.id || generateUUID(),
+ title: session?.title || 'New Chat',
+ createdAt: session?.createdAt || Date.now(),
+ updatedAt: Date.now(),
+ messages: session?.messages || [],
+ };
+
+ set((state) => ({
+ sessions: {
+ ...state.sessions,
+ [newSession.id]: newSession,
+ },
+ }));
+
+ get().saveToDB();
+ return newSession;
+ },
+
+ readSession: (id: string) => {
const { sessions } = get();
return sessions[id] || null;
},
@@ -136,10 +143,6 @@ export const ChatStateStore = create()(
.where(['did', 'id'])
.equals([currentDID, id])
.delete();
- await chatDB.streams
- .where(['did', 'chatId'])
- .equals([currentDID, id])
- .delete();
} catch (error) {
console.error('Failed to delete from DB:', error);
}
@@ -187,13 +190,6 @@ export const ChatStateStore = create()(
},
};
- // async generate title (if new session and has user message)
- if (isNewSession && messages.length > 0) {
- setTimeout(() => {
- get().updateTitle(sessionId);
- }, 0);
- }
-
return newState;
}
@@ -203,145 +199,6 @@ export const ChatStateStore = create()(
get().saveToDB();
},
- updateSingleMessage: (
- sessionId: string,
- messageId: string,
- updates: Partial,
- ) => {
- set((state) => {
- const session = state.sessions[sessionId];
- if (!session) return state;
-
- const updatedMessages = session.messages.map((msg) =>
- msg.id === messageId ? { ...msg, ...updates } : msg,
- );
-
- return {
- sessions: {
- ...state.sessions,
- [sessionId]: {
- ...session,
- messages: updatedMessages,
- updatedAt: Date.now(),
- },
- },
- };
- });
-
- get().saveToDB();
- },
-
- deleteMessage: (sessionId: string, messageId: string) => {
- set((state) => {
- const session = state.sessions[sessionId];
- if (!session) return state;
-
- const updatedMessages = session.messages.filter(
- (msg) => msg.id !== messageId,
- );
-
- return {
- sessions: {
- ...state.sessions,
- [sessionId]: {
- ...session,
- messages: updatedMessages,
- updatedAt: Date.now(),
- },
- },
- };
- });
-
- get().saveToDB();
- },
-
- deleteMessagesAfterTimestamp: (sessionId: string, timestamp: number) => {
- set((state) => {
- const session = state.sessions[sessionId];
- if (!session) return state;
-
- const updatedMessages = session.messages.filter((msg) => {
- const messageTime = msg.createdAt
- ? new Date(msg.createdAt).getTime()
- : 0;
- return messageTime < timestamp;
- });
-
- return {
- sessions: {
- ...state.sessions,
- [sessionId]: {
- ...session,
- messages: updatedMessages,
- updatedAt: Date.now(),
- },
- },
- };
- });
-
- get().saveToDB();
- },
-
- getMessages: (sessionId: string) => {
- const { sessions } = get();
- return sessions[sessionId]?.messages || [];
- },
-
- createStreamId: async (streamId: string, chatId: string) => {
- try {
- await chatDB.streams.add({
- id: streamId,
- chatId,
- createdAt: Date.now(),
- });
- } catch (error) {
- console.error('Failed to create stream ID:', error);
- throw error;
- }
- },
-
- getStreamIdsByChatId: async (chatId: string) => {
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return [];
-
- const streams = await chatDB.streams
- .where(['did', 'chatId'])
- .equals([currentDID, chatId])
- .toArray();
- // sort by creation time
- const sortedStreams = streams.sort(
- (a: StreamRecord, b: StreamRecord) => a.createdAt - b.createdAt,
- );
- return sortedStreams.map((stream: StreamRecord) => stream.id);
- } catch (error) {
- console.error('Failed to get stream IDs:', error);
- return [];
- }
- },
-
- updateTitle: async (sessionId: string) => {
- const session = get().getSession(sessionId);
- if (!session || session.messages.length === 0) return;
-
- // find the first user message
- const firstUserMessage = session.messages.find(
- (msg) => msg.role === 'user',
- );
- if (!firstUserMessage) return;
-
- try {
- const title = await generateTitleFromUserMessage({
- message: firstUserMessage,
- });
-
- // directly update session title
- get().updateSession(sessionId, { title });
- } catch (error) {
- console.error('Failed to generate title with AI:', error);
- }
- },
-
clearAllSessions: () => {
set({
sessions: {},
@@ -351,7 +208,6 @@ export const ChatStateStore = create()(
const clearDB = async () => {
try {
await chatDB.chats.clear();
- await chatDB.streams.clear();
} catch (error) {
console.error('Failed to clear DB:', error);
}
@@ -393,8 +249,14 @@ export const ChatStateStore = create()(
if (typeof window === 'undefined') return;
try {
+ const currentDID = await getCurrentDID();
+ if (!currentDID) return;
+
const { sessions } = get();
- const chatsToSave = Object.values(sessions);
+ const chatsToSave = Object.values(sessions).map((session) => ({
+ ...session,
+ did: currentDID,
+ }));
// use bulkPut to efficiently update data
await chatDB.chats.bulkPut(chatsToSave);
diff --git a/src/features/ai-chat/types/index.ts b/src/features/chat/types.ts
similarity index 60%
rename from src/features/ai-chat/types/index.ts
rename to src/features/chat/types.ts
index e5f47517..2c139088 100644
--- a/src/features/ai-chat/types/index.ts
+++ b/src/features/chat/types.ts
@@ -7,11 +7,6 @@ export interface ChatSession {
createdAt: number;
updatedAt: number;
messages: Message[];
-}
-
-// stream ID management interface
-export interface StreamRecord {
- id: string;
- chatId: string;
- createdAt: number;
+ pinned?: boolean;
+ did?: string; // Added for IndexedDB storage
}
diff --git a/src/features/ai-chat/utils/message.ts b/src/features/chat/utils.ts
similarity index 100%
rename from src/features/ai-chat/utils/message.ts
rename to src/features/chat/utils.ts
diff --git a/src/features/documents/artifacts/code/actions/copy.tsx b/src/features/documents/artifacts/code/actions/copy.tsx
deleted file mode 100644
index accf79c2..00000000
--- a/src/features/documents/artifacts/code/actions/copy.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { CopyIcon } from 'lucide-react';
-import { toast } from '@/shared/components/toast';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-interface Metadata {
- outputs: Array;
-}
-
-export function createCopyAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.code.actions.copy'),
- onClick: ({ content }) => {
- navigator.clipboard.writeText(content);
- toast({
- description: getLocaleText('en').t('artifact.copied'),
- type: 'success',
- });
- },
- };
-}
diff --git a/src/features/documents/artifacts/code/actions/redo.tsx b/src/features/documents/artifacts/code/actions/redo.tsx
deleted file mode 100644
index b0d8802b..00000000
--- a/src/features/documents/artifacts/code/actions/redo.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { RedoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-interface Metadata {
- outputs: Array;
-}
-
-export function createRedoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.code.actions.redo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('next');
- },
- isDisabled: ({ isCurrentVersion }) => {
- if (isCurrentVersion) {
- return true;
- }
- return false;
- },
- };
-}
diff --git a/src/features/documents/artifacts/code/actions/run-code.tsx b/src/features/documents/artifacts/code/actions/run-code.tsx
deleted file mode 100644
index b9905451..00000000
--- a/src/features/documents/artifacts/code/actions/run-code.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-import { PlayIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import { generateUUID } from '@/shared/utils';
-import type { ArtifactAction } from '../../types';
-import type {
- ConsoleOutput,
- ConsoleOutputContent,
-} from '../components/console';
-
-interface Metadata {
- outputs: Array;
-}
-
-const OUTPUT_HANDLERS = {
- matplotlib: `
- import io
- import base64
- from matplotlib import pyplot as plt
-
- # Clear any existing plots
- plt.clf()
- plt.close('all')
-
- # Switch to agg backend
- plt.switch_backend('agg')
-
- def setup_matplotlib_output():
- def custom_show():
- if plt.gcf().get_size_inches().prod() * plt.gcf().dpi ** 2 > 25_000_000:
- print("Warning: Plot size too large, reducing quality")
- plt.gcf().set_dpi(100)
-
- png_buf = io.BytesIO()
- plt.savefig(png_buf, format='png')
- png_buf.seek(0)
- png_base64 = base64.b64encode(png_buf.read()).decode('utf-8')
- print(f'data:image/png;base64,{png_base64}')
- png_buf.close()
-
- plt.clf()
- plt.close('all')
-
- plt.show = custom_show
- `,
- basic: `
- # Basic output capture setup
- `,
-};
-
-function detectRequiredHandlers(code: string): string[] {
- const handlers: string[] = ['basic'];
-
- if (code.includes('matplotlib') || code.includes('plt.')) {
- handlers.push('matplotlib');
- }
-
- return handlers;
-}
-
-export function createRunCodeAction(): ArtifactAction {
- return {
- icon: ,
- label: undefined as any,
- description: getLocaleText('en').t('artifact.code.actions.run'),
- onClick: async ({ content, setMetadata }) => {
- const runId = generateUUID();
- const outputContent: Array = [];
-
- setMetadata((metadata) => ({
- ...metadata,
- outputs: [
- ...metadata.outputs,
- {
- id: runId,
- contents: [],
- status: 'in_progress',
- },
- ],
- }));
-
- try {
- // @ts-expect-error - loadPyodide is not defined
- const currentPyodideInstance = await globalThis.loadPyodide({
- indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.23.4/full/',
- });
-
- currentPyodideInstance.setStdout({
- batched: (output: string) => {
- outputContent.push({
- type: output.startsWith('data:image/png;base64')
- ? 'image'
- : 'text',
- value: output,
- });
- },
- });
-
- await currentPyodideInstance.loadPackagesFromImports(content, {
- messageCallback: (message: string) => {
- setMetadata((metadata) => ({
- ...metadata,
- outputs: [
- ...metadata.outputs.filter((output) => output.id !== runId),
- {
- id: runId,
- contents: [{ type: 'text', value: message }],
- status: 'loading_packages',
- },
- ],
- }));
- },
- });
-
- const requiredHandlers = detectRequiredHandlers(content);
- for (const handler of requiredHandlers) {
- if (OUTPUT_HANDLERS[handler as keyof typeof OUTPUT_HANDLERS]) {
- await currentPyodideInstance.runPythonAsync(
- OUTPUT_HANDLERS[handler as keyof typeof OUTPUT_HANDLERS],
- );
-
- if (handler === 'matplotlib') {
- await currentPyodideInstance.runPythonAsync(
- 'setup_matplotlib_output()',
- );
- }
- }
- }
-
- await currentPyodideInstance.runPythonAsync(content);
-
- setMetadata((metadata) => ({
- ...metadata,
- outputs: [
- ...metadata.outputs.filter((output) => output.id !== runId),
- {
- id: runId,
- contents: outputContent,
- status: 'completed',
- },
- ],
- }));
- } catch (error: any) {
- setMetadata((metadata) => ({
- ...metadata,
- outputs: [
- ...metadata.outputs.filter((output) => output.id !== runId),
- {
- id: runId,
- contents: [{ type: 'text', value: error.message }],
- status: 'failed',
- },
- ],
- }));
- }
- },
- };
-}
diff --git a/src/features/documents/artifacts/code/actions/undo.tsx b/src/features/documents/artifacts/code/actions/undo.tsx
deleted file mode 100644
index 1a0940d1..00000000
--- a/src/features/documents/artifacts/code/actions/undo.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { UndoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-interface Metadata {
- outputs: Array;
-}
-
-export function createUndoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.code.actions.undo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('prev');
- },
- isDisabled: ({ currentVersionIndex }) => {
- if (currentVersionIndex === 0) {
- return true;
- }
- return false;
- },
- };
-}
diff --git a/src/features/documents/artifacts/code/components/code-content.tsx b/src/features/documents/artifacts/code/components/code-content.tsx
deleted file mode 100644
index 32d0285c..00000000
--- a/src/features/documents/artifacts/code/components/code-content.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import type { ArtifactContent } from '@/features/documents/artifacts/types';
-import { CodeEditor } from './code-editor';
-import { Console, type ConsoleOutput } from './console';
-
-interface Metadata {
- outputs: Array;
-}
-
-export function CodeContent(props: ArtifactContent) {
- return (
- <>
-
-
-
- {props.metadata?.outputs && (
- {
- props.setMetadata({
- ...props.metadata,
- outputs: [],
- });
- }}
- />
- )}
- >
- );
-}
diff --git a/src/features/documents/artifacts/code/components/code-editor.tsx b/src/features/documents/artifacts/code/components/code-editor.tsx
deleted file mode 100644
index 49c34332..00000000
--- a/src/features/documents/artifacts/code/components/code-editor.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-'use client';
-
-import { python } from '@codemirror/lang-python';
-import { EditorState, Transaction } from '@codemirror/state';
-import { oneDark } from '@codemirror/theme-one-dark';
-import { EditorView } from '@codemirror/view';
-import { basicSetup } from 'codemirror';
-import { memo, useEffect, useRef } from 'react';
-
-type EditorProps = {
- content: string;
- onSaveContent: (updatedContent: string, debounce: boolean) => void;
- status: 'streaming' | 'idle';
- isCurrentVersion: boolean;
- currentVersionIndex: number;
-};
-
-function PureCodeEditor({ content, onSaveContent, status }: EditorProps) {
- const containerRef = useRef(null);
- const editorRef = useRef(null);
-
- useEffect(() => {
- if (containerRef.current && !editorRef.current) {
- const startState = EditorState.create({
- doc: content,
- extensions: [basicSetup, python(), oneDark],
- });
-
- editorRef.current = new EditorView({
- state: startState,
- parent: containerRef.current,
- });
- }
-
- return () => {
- if (editorRef.current) {
- editorRef.current.destroy();
- editorRef.current = null;
- }
- };
- // NOTE: we only want to run this effect once
- // eslint-disable-next-line
- }, []);
-
- useEffect(() => {
- if (editorRef.current) {
- const updateListener = EditorView.updateListener.of((update) => {
- if (update.docChanged) {
- const transaction = update.transactions.find(
- (tr) => !tr.annotation(Transaction.remote),
- );
-
- if (transaction) {
- const newContent = update.state.doc.toString();
- onSaveContent(newContent, true);
- }
- }
- });
-
- const currentSelection = editorRef.current.state.selection;
-
- const newState = EditorState.create({
- doc: editorRef.current.state.doc,
- extensions: [basicSetup, python(), oneDark, updateListener],
- selection: currentSelection,
- });
-
- editorRef.current.setState(newState);
- }
- }, [onSaveContent]);
-
- useEffect(() => {
- if (editorRef.current && content) {
- const currentContent = editorRef.current.state.doc.toString();
-
- if (status === 'streaming' || currentContent !== content) {
- const transaction = editorRef.current.state.update({
- changes: {
- from: 0,
- to: currentContent.length,
- insert: content,
- },
- annotations: [Transaction.remote.of(true)],
- });
-
- editorRef.current.dispatch(transaction);
- }
- }
- }, [content, status]);
-
- return (
-
- );
-}
-
-function areEqual(prevProps: EditorProps, nextProps: EditorProps) {
- if (prevProps.currentVersionIndex !== nextProps.currentVersionIndex)
- return false;
- if (prevProps.isCurrentVersion !== nextProps.isCurrentVersion) return false;
- if (prevProps.status === 'streaming' && nextProps.status === 'streaming')
- return false;
- if (prevProps.content !== nextProps.content) return false;
-
- return true;
-}
-
-export const CodeEditor = memo(PureCodeEditor, areEqual);
diff --git a/src/features/documents/artifacts/code/components/code-preview.tsx b/src/features/documents/artifacts/code/components/code-preview.tsx
deleted file mode 100644
index 34cd2ba7..00000000
--- a/src/features/documents/artifacts/code/components/code-preview.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import type { Document } from '@/features/documents/stores';
-import { CodeEditor } from './code-editor';
-
-interface CodePreviewProps {
- document: Document;
- editorStatus: 'streaming' | 'idle';
-}
-
-export function CodePreview({ document, editorStatus }: CodePreviewProps) {
- const commonProps = {
- content: document.content ?? '',
- isCurrentVersion: true,
- currentVersionIndex: 0,
- status: editorStatus,
- saveContent: () => {},
- suggestions: [],
- };
-
- return (
-
- );
-}
diff --git a/src/features/documents/artifacts/code/components/console.tsx b/src/features/documents/artifacts/code/components/console.tsx
deleted file mode 100644
index c2c74b76..00000000
--- a/src/features/documents/artifacts/code/components/console.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-import { CrossIcon, LoaderIcon, TerminalIcon } from 'lucide-react';
-import {
- type Dispatch,
- type SetStateAction,
- useCallback,
- useEffect,
- useRef,
- useState,
-} from 'react';
-import { useCurrentDocument } from '@/features/documents/hooks/use-document-current';
-import { Button } from '@/shared/components/ui/button';
-import { cn } from '@/shared/utils';
-
-export interface ConsoleOutputContent {
- type: 'text' | 'image';
- value: string;
-}
-
-export interface ConsoleOutput {
- id: string;
- status: 'in_progress' | 'loading_packages' | 'completed' | 'failed';
- contents: Array;
-}
-
-interface ConsoleProps {
- consoleOutputs: Array;
- setConsoleOutputs: Dispatch>>;
-}
-
-export function Console({ consoleOutputs, setConsoleOutputs }: ConsoleProps) {
- const [height, setHeight] = useState(300);
- const [isResizing, setIsResizing] = useState(false);
- const consoleEndRef = useRef(null);
-
- const { currentDocument } = useCurrentDocument();
- const isArtifactVisible = currentDocument.documentId !== 'init';
-
- const minHeight = 100;
- const maxHeight = 800;
-
- const startResizing = useCallback(() => {
- setIsResizing(true);
- }, []);
-
- const stopResizing = useCallback(() => {
- setIsResizing(false);
- }, []);
-
- const resize = useCallback(
- (e: MouseEvent) => {
- if (isResizing) {
- const newHeight = window.innerHeight - e.clientY;
- if (newHeight >= minHeight && newHeight <= maxHeight) {
- setHeight(newHeight);
- }
- }
- },
- [isResizing],
- );
-
- useEffect(() => {
- window.addEventListener('mousemove', resize);
- window.addEventListener('mouseup', stopResizing);
- return () => {
- window.removeEventListener('mousemove', resize);
- window.removeEventListener('mouseup', stopResizing);
- };
- }, [resize, stopResizing]);
-
- useEffect(() => {
- consoleEndRef.current?.scrollIntoView({ behavior: 'smooth' });
- }, [consoleOutputs]);
-
- useEffect(() => {
- if (!isArtifactVisible) {
- setConsoleOutputs([]);
- }
- }, [isArtifactVisible, setConsoleOutputs]);
-
- return consoleOutputs.length > 0 ? (
- <>
-
-
-
-
-
-
setConsoleOutputs([])}
- >
-
-
-
-
-
- {consoleOutputs.map((consoleOutput, index) => (
-
-
- [{index + 1}]
-
- {['in_progress', 'loading_packages'].includes(
- consoleOutput.status,
- ) ? (
-
-
-
-
-
- {consoleOutput.status === 'in_progress'
- ? 'Initializing...'
- : consoleOutput.status === 'loading_packages'
- ? consoleOutput.contents.map((content) =>
- content.type === 'text' ? content.value : null,
- )
- : null}
-
-
- ) : (
-
- {consoleOutput.contents.map((content, index) =>
- content.type === 'image' ? (
-
-
-
- ) : (
-
- {content.value}
-
- ),
- )}
-
- )}
-
- ))}
-
-
-
- >
- ) : null;
-}
diff --git a/src/features/documents/artifacts/code/generator.ts b/src/features/documents/artifacts/code/generator.ts
deleted file mode 100644
index 0ffbe7e2..00000000
--- a/src/features/documents/artifacts/code/generator.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { streamObject } from 'ai';
-import z from 'zod';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export const generateCodePrompt = `
-You are a Python code generator that creates self-contained, executable code snippets. When writing code:
-
-1. Each snippet should be complete and runnable on its own
-2. Prefer using print() statements to display outputs
-3. Include helpful comments explaining the code
-4. Keep snippets concise (generally under 15 lines)
-5. Avoid external dependencies - use Python standard library
-6. Handle potential errors gracefully
-7. Return meaningful output that demonstrates the code's functionality
-8. Don't use input() or other interactive functions
-9. Don't access files or network resources
-10. Don't use infinite loops
-
-Examples of good snippets:
-
-# Calculate factorial iteratively
-def factorial(n):
- result = 1
- for i in range(1, n + 1):
- result *= i
- return result
-
-print(f"Factorial of 5 is: {factorial(5)}")
-`;
-
-export async function generateCodeContent(
- title: string,
- onDelta: (delta: string) => void,
-): Promise {
- let draftContent = '';
-
- const { fullStream } = streamObject({
- model: llmProvider.artifact(),
- system: generateCodePrompt,
- prompt: title,
- schema: z.object({
- code: z.string(),
- }),
- });
-
- for await (const delta of fullStream) {
- if (delta.type === 'object' && delta.object.code) {
- draftContent = delta.object.code;
- onDelta(delta.object.code);
- }
- }
-
- return draftContent;
-}
diff --git a/src/features/documents/artifacts/code/index.tsx b/src/features/documents/artifacts/code/index.tsx
deleted file mode 100644
index f4b057f8..00000000
--- a/src/features/documents/artifacts/code/index.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Artifact } from '../types';
-import { createCopyAction } from './actions/copy';
-import { createRedoAction } from './actions/redo';
-import { createRunCodeAction } from './actions/run-code';
-import { createUndoAction } from './actions/undo';
-import { CodeContent } from './components/code-content';
-import type { ConsoleOutput } from './components/console';
-
-// export functions for external use
-export { generateCodeContent } from './generator';
-export { updateCodeContent } from './updater';
-
-interface Metadata {
- outputs: Array;
-}
-
-export const createCodeArtifact = () => {
- return new Artifact<'code', Metadata>({
- kind: 'code',
- description: 'Code artifact for running and displaying code',
- initialize: async ({ setMetadata }) => {
- setMetadata({ outputs: [] });
- },
- content: CodeContent,
- actions: [
- createRunCodeAction(),
- createUndoAction(),
- createRedoAction(),
- createCopyAction(),
- ],
- });
-};
-
-export const codeArtifact = createCodeArtifact();
diff --git a/src/features/documents/artifacts/code/updater.ts b/src/features/documents/artifacts/code/updater.ts
deleted file mode 100644
index 8f7dc9d2..00000000
--- a/src/features/documents/artifacts/code/updater.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { streamObject } from 'ai';
-import { z } from 'zod';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export const updateCodePrompt = (currentContent: string) => `
- Improve the following code snippet based on the given prompt.
-
- ${currentContent}
- `;
-
-export async function updateCodeContent(
- currentContent: string,
- description: string,
- onDelta: (delta: string) => void,
-): Promise {
- let draftContent = '';
-
- const { fullStream } = streamObject({
- model: llmProvider.artifact(),
- system: updateCodePrompt(currentContent),
- prompt: description,
- schema: z.object({
- code: z.string(),
- }),
- });
-
- for await (const delta of fullStream) {
- if (delta.type === 'object' && delta.object.code) {
- draftContent = delta.object.code;
- onDelta(delta.object.code);
- }
- }
-
- return draftContent;
-}
diff --git a/src/features/documents/artifacts/image/actions/copy.tsx b/src/features/documents/artifacts/image/actions/copy.tsx
deleted file mode 100644
index d5a7eaa1..00000000
--- a/src/features/documents/artifacts/image/actions/copy.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { CopyIcon } from 'lucide-react';
-import { toast } from '@/shared/components/toast';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-export function createCopyAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.image.actions.copy'),
- onClick: ({ content }) => {
- const img = new Image();
- img.src = `data:image/png;base64,${content}`;
- img.onload = () => {
- const canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- const ctx = canvas.getContext('2d');
- ctx?.drawImage(img, 0, 0);
- canvas.toBlob((blob) => {
- if (blob) {
- navigator.clipboard.write([
- new ClipboardItem({ 'image/png': blob }),
- ]);
- }
- }, 'image/png');
- };
- toast({
- description: getLocaleText('en').t('artifact.image.copiedImage'),
- type: 'success',
- });
- },
- };
-}
diff --git a/src/features/documents/artifacts/image/actions/redo.tsx b/src/features/documents/artifacts/image/actions/redo.tsx
deleted file mode 100644
index a7546c98..00000000
--- a/src/features/documents/artifacts/image/actions/redo.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { RedoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-export function createRedoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.image.actions.redo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('next');
- },
- isDisabled: ({ isCurrentVersion }) => {
- return isCurrentVersion;
- },
- };
-}
diff --git a/src/features/documents/artifacts/image/actions/undo.tsx b/src/features/documents/artifacts/image/actions/undo.tsx
deleted file mode 100644
index 388b3a0a..00000000
--- a/src/features/documents/artifacts/image/actions/undo.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { UndoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-export function createUndoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.image.actions.undo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('prev');
- },
- isDisabled: ({ currentVersionIndex }) => {
- return currentVersionIndex === 0;
- },
- };
-}
diff --git a/src/features/documents/artifacts/image/components/image-content.tsx b/src/features/documents/artifacts/image/components/image-content.tsx
deleted file mode 100644
index bace8659..00000000
--- a/src/features/documents/artifacts/image/components/image-content.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import type { ArtifactContent } from '@/features/documents/artifacts/types';
-import { ImageEditor } from './image-editor';
-
-export function ImageContent(props: ArtifactContent) {
- return ;
-}
diff --git a/src/features/documents/artifacts/image/components/image-editor.tsx b/src/features/documents/artifacts/image/components/image-editor.tsx
deleted file mode 100644
index 6fa4ad13..00000000
--- a/src/features/documents/artifacts/image/components/image-editor.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import cn from 'classnames';
-import { LoaderIcon } from 'lucide-react';
-
-interface ImageEditorProps {
- title: string;
- content: string;
- isCurrentVersion: boolean;
- currentVersionIndex: number;
- status: string;
- isInline: boolean;
-}
-
-export function ImageEditor({
- title,
- content,
- status,
- isInline,
-}: ImageEditorProps) {
- return (
-
- {status === 'streaming' ? (
-
- {!isInline && (
-
-
-
- )}
-
Generating Image...
-
- ) : (
-
-
-
- )}
-
- );
-}
diff --git a/src/features/documents/artifacts/image/components/image-preview.tsx b/src/features/documents/artifacts/image/components/image-preview.tsx
deleted file mode 100644
index ad117f33..00000000
--- a/src/features/documents/artifacts/image/components/image-preview.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import type { Document } from '@/features/documents/stores';
-import { ImageEditor } from './image-editor';
-
-interface ImagePreviewProps {
- document: Document;
- artifactStatus: 'streaming' | 'idle' | 'loading' | 'error' | 'success';
-}
-
-export function ImagePreview({ document, artifactStatus }: ImagePreviewProps) {
- return (
-
- );
-}
diff --git a/src/features/documents/artifacts/image/generator.ts b/src/features/documents/artifacts/image/generator.ts
deleted file mode 100644
index 858a185d..00000000
--- a/src/features/documents/artifacts/image/generator.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { experimental_generateImage } from 'ai';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export async function generateImageContent(
- title: string,
- onComplete: (imageBase64: string) => void,
-): Promise {
- const { image } = await experimental_generateImage({
- model: llmProvider.image(),
- prompt: title,
- n: 1,
- });
-
- const base64Content = image.base64;
- onComplete(base64Content);
- return base64Content;
-}
diff --git a/src/features/documents/artifacts/image/index.tsx b/src/features/documents/artifacts/image/index.tsx
deleted file mode 100644
index 8d7af6bf..00000000
--- a/src/features/documents/artifacts/image/index.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Artifact } from '@/features/documents/artifacts/types';
-import { createCopyAction } from './actions/copy';
-import { createRedoAction } from './actions/redo';
-import { createUndoAction } from './actions/undo';
-import { ImageContent } from './components/image-content';
-
-export const createImageArtifact = () => {
- return new Artifact({
- kind: 'image',
- description: 'Image artifact for displaying images',
- content: ImageContent,
- actions: [createUndoAction(), createRedoAction(), createCopyAction()],
- });
-};
-
-export const imageArtifact = createImageArtifact();
-
-export { generateImageContent } from './generator';
-export { updateImageContent } from './updater';
diff --git a/src/features/documents/artifacts/image/updater.ts b/src/features/documents/artifacts/image/updater.ts
deleted file mode 100644
index 37d85991..00000000
--- a/src/features/documents/artifacts/image/updater.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { experimental_generateImage } from 'ai';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export async function updateImageContent(
- description: string,
- onComplete: (imageBase64: string) => void,
-): Promise {
- const { image } = await experimental_generateImage({
- model: llmProvider.image(),
- prompt: description,
- n: 1,
- });
-
- const base64Content = image.base64;
- onComplete(base64Content);
- return base64Content;
-}
diff --git a/src/features/documents/artifacts/index.ts b/src/features/documents/artifacts/index.ts
deleted file mode 100644
index 8f74e776..00000000
--- a/src/features/documents/artifacts/index.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// Export all artifact components
-
-export { codeArtifact } from './code';
-export { imageArtifact } from './image';
-export { sheetArtifact } from './sheet';
-export { textArtifact } from './text';
-
-import { codeArtifact } from './code';
-import { CodePreview } from './code/components/code-preview';
-import { imageArtifact } from './image';
-import { ImagePreview } from './image/components/image-preview';
-import { sheetArtifact } from './sheet';
-import { SheetPreview } from './sheet/components/sheet-preview';
-// Export unified artifact definitions
-import { textArtifact } from './text';
-// Export artifact previews
-import { TextPreview } from './text/components/text-preview';
-
-export const artifactDefinitions = [
- textArtifact,
- codeArtifact,
- imageArtifact,
- sheetArtifact,
-];
-
-export type ArtifactKind = (typeof artifactDefinitions)[number]['kind'];
-
-export const artifactPreviews = {
- text: TextPreview,
- code: CodePreview,
- image: ImagePreview,
- sheet: SheetPreview,
-};
diff --git a/src/features/documents/artifacts/sheet/actions/copy.tsx b/src/features/documents/artifacts/sheet/actions/copy.tsx
deleted file mode 100644
index 04e95edc..00000000
--- a/src/features/documents/artifacts/sheet/actions/copy.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { CopyIcon } from 'lucide-react';
-import { parse, unparse } from 'papaparse';
-import { toast } from '@/shared/components/toast';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-type Metadata = any;
-
-export function createCopyAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.sheet.actions.copy'),
- onClick: ({ content }) => {
- const parsed = parse(content, { skipEmptyLines: true });
- const nonEmptyRows = parsed.data.filter((row) =>
- row.some((cell) => cell.trim() !== ''),
- );
- const cleanedCsv = unparse(nonEmptyRows);
- navigator.clipboard.writeText(cleanedCsv);
- toast({
- description: getLocaleText('en').t('artifact.sheet.copiedCsv'),
- type: 'success',
- });
- },
- };
-}
diff --git a/src/features/documents/artifacts/sheet/actions/redo.tsx b/src/features/documents/artifacts/sheet/actions/redo.tsx
deleted file mode 100644
index 7de3965a..00000000
--- a/src/features/documents/artifacts/sheet/actions/redo.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { RedoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-type Metadata = any;
-
-export function createRedoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.sheet.actions.redo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('next');
- },
- isDisabled: ({ isCurrentVersion }) => {
- return isCurrentVersion;
- },
- };
-}
diff --git a/src/features/documents/artifacts/sheet/actions/undo.tsx b/src/features/documents/artifacts/sheet/actions/undo.tsx
deleted file mode 100644
index 43cafd2d..00000000
--- a/src/features/documents/artifacts/sheet/actions/undo.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { UndoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-type Metadata = any;
-
-export function createUndoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.sheet.actions.undo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('prev');
- },
- isDisabled: ({ currentVersionIndex }) => {
- return currentVersionIndex === 0;
- },
- };
-}
diff --git a/src/features/documents/artifacts/sheet/components/sheet-content.tsx b/src/features/documents/artifacts/sheet/components/sheet-content.tsx
deleted file mode 100644
index 7e7b193f..00000000
--- a/src/features/documents/artifacts/sheet/components/sheet-content.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { SpreadsheetEditor } from '@/features/documents/artifacts/sheet/components/sheet-editor';
-import type { ArtifactContent } from '@/features/documents/artifacts/types';
-
-type Metadata = any;
-
-export function SheetContent(props: ArtifactContent) {
- return (
-
- );
-}
diff --git a/src/features/documents/artifacts/sheet/components/sheet-editor.tsx b/src/features/documents/artifacts/sheet/components/sheet-editor.tsx
deleted file mode 100644
index 7107b468..00000000
--- a/src/features/documents/artifacts/sheet/components/sheet-editor.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-'use client';
-
-import { parse, unparse } from 'papaparse';
-import { memo, useEffect, useMemo, useState } from 'react';
-import { DataGrid, textEditor } from 'react-data-grid';
-import { useTheme } from '@/shared/components/theme-provider';
-import { cn } from '@/shared/utils';
-
-import 'react-data-grid/lib/styles.css';
-
-type SheetEditorProps = {
- content: string;
- saveContent: (content: string, isCurrentVersion: boolean) => void;
- status: string;
- isCurrentVersion: boolean;
- currentVersionIndex: number;
-};
-
-const MIN_ROWS = 50;
-const MIN_COLS = 26;
-
-const PureSpreadsheetEditor = ({
- content,
- saveContent,
- status,
- isCurrentVersion,
-}: SheetEditorProps) => {
- const { resolvedTheme } = useTheme();
-
- const parseData = useMemo(() => {
- if (!content) return Array(MIN_ROWS).fill(Array(MIN_COLS).fill(''));
- const result = parse(content, { skipEmptyLines: true });
-
- const paddedData = result.data.map((row) => {
- const paddedRow = [...row];
- while (paddedRow.length < MIN_COLS) {
- paddedRow.push('');
- }
- return paddedRow;
- });
-
- while (paddedData.length < MIN_ROWS) {
- paddedData.push(Array(MIN_COLS).fill(''));
- }
-
- return paddedData;
- }, [content]);
-
- const columns = useMemo(() => {
- const rowNumberColumn = {
- key: 'rowNumber',
- name: '',
- frozen: true,
- width: 50,
- renderCell: ({ rowIdx }: { rowIdx: number }) => rowIdx + 1,
- cellClass: 'border-t border-r dark:bg-zinc-950 dark:text-zinc-50',
- headerCellClass: 'border-t border-r dark:bg-zinc-900 dark:text-zinc-50',
- };
-
- const dataColumns = Array.from({ length: MIN_COLS }, (_, i) => ({
- key: i.toString(),
- name: String.fromCharCode(65 + i),
- renderEditCell: textEditor,
- width: 120,
- cellClass: cn(`border-t dark:bg-zinc-950 dark:text-zinc-50`, {
- 'border-l': i !== 0,
- }),
- headerCellClass: cn(`border-t dark:bg-zinc-900 dark:text-zinc-50`, {
- 'border-l': i !== 0,
- }),
- }));
-
- return [rowNumberColumn, ...dataColumns];
- }, []);
-
- const initialRows = useMemo(() => {
- return parseData.map((row, rowIndex) => {
- const rowData: any = {
- id: rowIndex,
- rowNumber: rowIndex + 1,
- };
-
- columns.slice(1).forEach((col, colIndex) => {
- rowData[col.key] = row[colIndex] || '';
- });
-
- return rowData;
- });
- }, [parseData, columns]);
-
- const [localRows, setLocalRows] = useState(initialRows);
-
- useEffect(() => {
- setLocalRows(initialRows);
- }, [initialRows]);
-
- const generateCsv = (data: any[][]) => {
- return unparse(data);
- };
-
- const handleRowsChange = (newRows: any[]) => {
- setLocalRows(newRows);
-
- const updatedData = newRows.map((row) => {
- return columns.slice(1).map((col) => row[col.key] || '');
- });
-
- const newCsvContent = generateCsv(updatedData);
- saveContent(newCsvContent, true);
- };
-
- return (
- {
- if (args.column.key !== 'rowNumber') {
- args.selectCell(true);
- }
- }}
- style={{ height: '100%' }}
- defaultColumnOptions={{
- resizable: true,
- sortable: true,
- }}
- />
- );
-};
-
-function areEqual(prevProps: SheetEditorProps, nextProps: SheetEditorProps) {
- return (
- prevProps.currentVersionIndex === nextProps.currentVersionIndex &&
- prevProps.isCurrentVersion === nextProps.isCurrentVersion &&
- !(prevProps.status === 'streaming' && nextProps.status === 'streaming') &&
- prevProps.content === nextProps.content &&
- prevProps.saveContent === nextProps.saveContent
- );
-}
-
-export const SpreadsheetEditor = memo(PureSpreadsheetEditor, areEqual);
diff --git a/src/features/documents/artifacts/sheet/components/sheet-preview.tsx b/src/features/documents/artifacts/sheet/components/sheet-preview.tsx
deleted file mode 100644
index 8fbc9726..00000000
--- a/src/features/documents/artifacts/sheet/components/sheet-preview.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { SpreadsheetEditor } from '@/features/documents/artifacts/sheet/components/sheet-editor';
-import type { Document } from '@/features/documents/stores';
-
-interface SheetPreviewProps {
- document: Document;
- editorStatus: 'streaming' | 'idle';
-}
-
-export function SheetPreview({ document, editorStatus }: SheetPreviewProps) {
- const commonProps = {
- content: document.content ?? '',
- isCurrentVersion: true,
- currentVersionIndex: 0,
- status: editorStatus,
- saveContent: () => {},
- suggestions: [],
- };
-
- return (
-
- );
-}
diff --git a/src/features/documents/artifacts/sheet/generator.ts b/src/features/documents/artifacts/sheet/generator.ts
deleted file mode 100644
index 49cf767e..00000000
--- a/src/features/documents/artifacts/sheet/generator.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { streamObject } from 'ai';
-import { z } from 'zod';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export const sheetPrompt = `
-You are a spreadsheet creation assistant. Create a spreadsheet in csv format based on the given prompt. The spreadsheet should contain meaningful column headers and data.
-`;
-
-export async function generateSheetContent(
- title: string,
- onDelta: (delta: string) => void,
-): Promise {
- let draftContent = '';
-
- const { fullStream } = streamObject({
- model: llmProvider.artifact(),
- system: sheetPrompt,
- prompt: title,
- schema: z.object({
- csv: z.string().describe('CSV data'),
- }),
- });
-
- for await (const delta of fullStream) {
- if (delta.type === 'object' && delta.object.csv) {
- draftContent = delta.object.csv;
- onDelta(delta.object.csv);
- }
- }
-
- return draftContent;
-}
diff --git a/src/features/documents/artifacts/sheet/index.tsx b/src/features/documents/artifacts/sheet/index.tsx
deleted file mode 100644
index bbe290b7..00000000
--- a/src/features/documents/artifacts/sheet/index.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Artifact } from '@/features/documents/artifacts/types';
-import { createCopyAction } from './actions/copy';
-import { createRedoAction } from './actions/redo';
-import { createUndoAction } from './actions/undo';
-import { SheetContent } from './components/sheet-content';
-
-type Metadata = any;
-
-export const createSheetArtifact = () => {
- return new Artifact<'sheet', Metadata>({
- kind: 'sheet',
- description: 'Sheet artifact for displaying spreadsheets',
- initialize: async () => {},
- content: SheetContent,
- actions: [createUndoAction(), createRedoAction(), createCopyAction()],
- });
-};
-
-export const sheetArtifact = createSheetArtifact();
-
-export { generateSheetContent } from './generator';
-export { updateSheetContent } from './updater';
diff --git a/src/features/documents/artifacts/sheet/updater.ts b/src/features/documents/artifacts/sheet/updater.ts
deleted file mode 100644
index 9428aab8..00000000
--- a/src/features/documents/artifacts/sheet/updater.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { streamObject } from 'ai';
-import { z } from 'zod';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export const updateDocumentPrompt = (currentContent: string | null) =>
- `\
-Improve the following spreadsheet based on the given prompt.
-
-${currentContent}
-`;
-
-export async function updateSheetContent(
- currentContent: string,
- description: string,
- onDelta: (delta: string) => void,
-): Promise {
- let draftContent = '';
-
- const { fullStream } = streamObject({
- model: llmProvider.artifact(),
- system: updateDocumentPrompt(currentContent),
- prompt: description,
- schema: z.object({
- csv: z.string(),
- }),
- });
-
- for await (const delta of fullStream) {
- if (delta.type === 'object' && delta.object.csv) {
- draftContent = delta.object.csv;
- onDelta(delta.object.csv);
- }
- }
-
- return draftContent;
-}
diff --git a/src/features/documents/artifacts/text/actions/copy.tsx b/src/features/documents/artifacts/text/actions/copy.tsx
deleted file mode 100644
index 7e472e85..00000000
--- a/src/features/documents/artifacts/text/actions/copy.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { CopyIcon } from 'lucide-react';
-import { toast } from '@/shared/components/toast';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-export function createCopyAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.text.actions.copy'),
- onClick: ({ content }) => {
- navigator.clipboard.writeText(content);
- toast({
- description: getLocaleText('en').t('artifact.copied'),
- type: 'success',
- });
- },
- };
-}
diff --git a/src/features/documents/artifacts/text/actions/redo.tsx b/src/features/documents/artifacts/text/actions/redo.tsx
deleted file mode 100644
index bf85b85e..00000000
--- a/src/features/documents/artifacts/text/actions/redo.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { RedoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-export function createRedoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.text.actions.redo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('next');
- },
- isDisabled: ({ isCurrentVersion }) => {
- return isCurrentVersion;
- },
- };
-}
diff --git a/src/features/documents/artifacts/text/actions/undo.tsx b/src/features/documents/artifacts/text/actions/undo.tsx
deleted file mode 100644
index 34ee1194..00000000
--- a/src/features/documents/artifacts/text/actions/undo.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { UndoIcon } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-export function createUndoAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.text.actions.undo'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('prev');
- },
- isDisabled: ({ currentVersionIndex }) => {
- return currentVersionIndex === 0;
- },
- };
-}
diff --git a/src/features/documents/artifacts/text/actions/version-change.tsx b/src/features/documents/artifacts/text/actions/version-change.tsx
deleted file mode 100644
index 9774f375..00000000
--- a/src/features/documents/artifacts/text/actions/version-change.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { FileDiff } from 'lucide-react';
-import { getLocaleText } from '@/shared/locales';
-import type { ArtifactAction } from '../../types';
-
-export function createVersionChangeAction(): ArtifactAction {
- return {
- icon: ,
- description: getLocaleText('en').t('artifact.text.actions.versionChange'),
- onClick: ({ handleVersionChange }) => {
- handleVersionChange('toggle');
- },
- isDisabled: ({ currentVersionIndex }) => {
- return currentVersionIndex === 0;
- },
- };
-}
diff --git a/src/features/documents/artifacts/text/components/diffview.tsx b/src/features/documents/artifacts/text/components/diffview.tsx
deleted file mode 100644
index a7fb8393..00000000
--- a/src/features/documents/artifacts/text/components/diffview.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import OrderedMap from 'orderedmap';
-import {
- DOMParser,
- type MarkSpec,
- type Node as ProsemirrorNode,
- Schema,
-} from 'prosemirror-model';
-import { schema } from 'prosemirror-schema-basic';
-import { addListNodes } from 'prosemirror-schema-list';
-import { EditorState } from 'prosemirror-state';
-import { EditorView } from 'prosemirror-view';
-import { useEffect, useRef } from 'react';
-import { renderToString } from 'react-dom/server';
-import { DiffType, diffEditor } from './editor/diff';
-import { Markdown } from './editor/markdown';
-
-const diffSchema = new Schema({
- nodes: addListNodes(schema.spec.nodes, 'paragraph block*', 'block'),
- marks: OrderedMap.from({
- ...schema.spec.marks.toObject(),
- diffMark: {
- attrs: { type: { default: '' } },
- toDOM(mark) {
- let className = '';
-
- switch (mark.attrs.type) {
- case DiffType.Inserted:
- className =
- 'bg-green-100 text-green-700 dark:bg-green-500/70 dark:text-green-300';
- break;
- case DiffType.Deleted:
- className =
- 'bg-red-100 line-through text-red-600 dark:bg-red-500/70 dark:text-red-300';
- break;
- default:
- className = '';
- }
- return ['span', { class: className }, 0];
- },
- } as MarkSpec,
- }),
-});
-
-function computeDiff(oldDoc: ProsemirrorNode, newDoc: ProsemirrorNode) {
- return diffEditor(diffSchema, oldDoc.toJSON(), newDoc.toJSON());
-}
-
-type DiffEditorProps = {
- oldContent: string;
- newContent: string;
-};
-
-export const DiffView = ({ oldContent, newContent }: DiffEditorProps) => {
- const editorRef = useRef(null);
- const viewRef = useRef(null);
-
- useEffect(() => {
- if (editorRef.current && !viewRef.current) {
- const parser = DOMParser.fromSchema(diffSchema);
-
- const oldHtmlContent = renderToString({oldContent});
- const newHtmlContent = renderToString({newContent});
-
- const oldContainer = document.createElement('div');
- oldContainer.innerHTML = oldHtmlContent;
-
- const newContainer = document.createElement('div');
- newContainer.innerHTML = newHtmlContent;
-
- const oldDoc = parser.parse(oldContainer);
- const newDoc = parser.parse(newContainer);
-
- const diffedDoc = computeDiff(oldDoc, newDoc);
-
- const state = EditorState.create({
- doc: diffedDoc,
- plugins: [],
- });
-
- viewRef.current = new EditorView(editorRef.current, {
- state,
- editable: () => false,
- });
- }
-
- return () => {
- if (viewRef.current) {
- viewRef.current.destroy();
- viewRef.current = null;
- }
- };
- }, [oldContent, newContent]);
-
- return ;
-};
diff --git a/src/features/documents/artifacts/text/components/editor/code-block.tsx b/src/features/documents/artifacts/text/components/editor/code-block.tsx
deleted file mode 100644
index c0780e58..00000000
--- a/src/features/documents/artifacts/text/components/editor/code-block.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-'use client';
-
-interface CodeBlockProps {
- node: any;
- inline: boolean;
- className: string;
- children: any;
-}
-
-export function CodeBlock({
- node,
- inline,
- className,
- children,
- ...props
-}: CodeBlockProps) {
- if (!inline) {
- return (
-
- );
- } else {
- return (
-
- {children}
-
- );
- }
-}
diff --git a/src/features/documents/artifacts/text/components/editor/config.ts b/src/features/documents/artifacts/text/components/editor/config.ts
deleted file mode 100644
index c171cac3..00000000
--- a/src/features/documents/artifacts/text/components/editor/config.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { textblockTypeInputRule } from 'prosemirror-inputrules';
-import { Schema } from 'prosemirror-model';
-import { schema } from 'prosemirror-schema-basic';
-import { addListNodes } from 'prosemirror-schema-list';
-import type { Transaction } from 'prosemirror-state';
-import type { EditorView } from 'prosemirror-view';
-import type { MutableRefObject } from 'react';
-
-import { buildContentFromDocument } from './functions';
-
-export const documentSchema = new Schema({
- nodes: addListNodes(schema.spec.nodes, 'paragraph block*', 'block'),
- marks: schema.spec.marks,
-});
-
-export function headingRule(level: number) {
- return textblockTypeInputRule(
- new RegExp(`^(#{1,${level}})\\s$`),
- documentSchema.nodes.heading,
- () => ({ level }),
- );
-}
-
-export const handleTransaction = ({
- transaction,
- editorRef,
- onSaveContent,
-}: {
- transaction: Transaction;
- editorRef: MutableRefObject;
- onSaveContent: (updatedContent: string, debounce: boolean) => void;
-}) => {
- if (!editorRef || !editorRef.current) return;
-
- const newState = editorRef.current.state.apply(transaction);
- editorRef.current.updateState(newState);
-
- if (transaction.docChanged && !transaction.getMeta('no-save')) {
- const updatedContent = buildContentFromDocument(newState.doc);
-
- if (transaction.getMeta('no-debounce')) {
- onSaveContent(updatedContent, false);
- } else {
- onSaveContent(updatedContent, true);
- }
- }
-};
diff --git a/src/features/documents/artifacts/text/components/editor/diff.js b/src/features/documents/artifacts/text/components/editor/diff.js
deleted file mode 100644
index dd9bc920..00000000
--- a/src/features/documents/artifacts/text/components/editor/diff.js
+++ /dev/null
@@ -1,475 +0,0 @@
-// Modified from https://github.com/hamflx/prosemirror-diff/blob/master/src/diff.js
-
-import { diff_match_patch } from 'diff-match-patch';
-import { Fragment, Node } from 'prosemirror-model';
-
-export const DiffType = {
- Unchanged: 0,
- Deleted: -1,
- Inserted: 1,
-};
-
-export const patchDocumentNode = (schema, oldNode, newNode) => {
- assertNodeTypeEqual(oldNode, newNode);
-
- const finalLeftChildren = [];
- const finalRightChildren = [];
-
- const oldChildren = normalizeNodeContent(oldNode);
- const newChildren = normalizeNodeContent(newNode);
- const oldChildLen = oldChildren.length;
- const newChildLen = newChildren.length;
- const minChildLen = Math.min(oldChildLen, newChildLen);
-
- let left = 0;
- let right = 0;
-
- for (; left < minChildLen; left++) {
- const oldChild = oldChildren[left];
- const newChild = newChildren[left];
- if (!isNodeEqual(oldChild, newChild)) {
- break;
- }
- finalLeftChildren.push(...ensureArray(oldChild));
- }
-
- for (; right + left + 1 < minChildLen; right++) {
- const oldChild = oldChildren[oldChildLen - right - 1];
- const newChild = newChildren[newChildLen - right - 1];
- if (!isNodeEqual(oldChild, newChild)) {
- break;
- }
- finalRightChildren.unshift(...ensureArray(oldChild));
- }
-
- const diffOldChildren = oldChildren.slice(left, oldChildLen - right);
- const diffNewChildren = newChildren.slice(left, newChildLen - right);
-
- if (diffOldChildren.length && diffNewChildren.length) {
- const matchedNodes = matchNodes(
- schema,
- diffOldChildren,
- diffNewChildren,
- ).sort((a, b) => b.count - a.count);
- const bestMatch = matchedNodes[0];
- if (bestMatch) {
- const { oldStartIndex, newStartIndex, oldEndIndex, newEndIndex } =
- bestMatch;
- const oldBeforeMatchChildren = diffOldChildren.slice(0, oldStartIndex);
- const newBeforeMatchChildren = diffNewChildren.slice(0, newStartIndex);
-
- finalLeftChildren.push(
- ...patchRemainNodes(
- schema,
- oldBeforeMatchChildren,
- newBeforeMatchChildren,
- ),
- );
- finalLeftChildren.push(
- ...diffOldChildren.slice(oldStartIndex, oldEndIndex),
- );
-
- const oldAfterMatchChildren = diffOldChildren.slice(oldEndIndex);
- const newAfterMatchChildren = diffNewChildren.slice(newEndIndex);
-
- finalRightChildren.unshift(
- ...patchRemainNodes(
- schema,
- oldAfterMatchChildren,
- newAfterMatchChildren,
- ),
- );
- } else {
- finalLeftChildren.push(
- ...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
- );
- }
- } else {
- finalLeftChildren.push(
- ...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
- );
- }
-
- return createNewNode(oldNode, [...finalLeftChildren, ...finalRightChildren]);
-};
-
-const matchNodes = (schema, oldChildren, newChildren) => {
- const matches = [];
- for (
- let oldStartIndex = 0;
- oldStartIndex < oldChildren.length;
- oldStartIndex++
- ) {
- const oldStartNode = oldChildren[oldStartIndex];
- const newStartIndex = findMatchNode(newChildren, oldStartNode);
-
- if (newStartIndex !== -1) {
- let oldEndIndex = oldStartIndex + 1;
- let newEndIndex = newStartIndex + 1;
- for (
- ;
- oldEndIndex < oldChildren.length && newEndIndex < newChildren.length;
- oldEndIndex++, newEndIndex++
- ) {
- const oldEndNode = oldChildren[oldEndIndex];
- if (!isNodeEqual(newChildren[newEndIndex], oldEndNode)) {
- break;
- }
- }
- matches.push({
- oldStartIndex,
- newStartIndex,
- oldEndIndex,
- newEndIndex,
- count: newEndIndex - newStartIndex,
- });
- }
- }
- return matches;
-};
-
-const findMatchNode = (children, node, startIndex = 0) => {
- for (let i = startIndex; i < children.length; i++) {
- if (isNodeEqual(children[i], node)) {
- return i;
- }
- }
- return -1;
-};
-
-const patchRemainNodes = (schema, oldChildren, newChildren) => {
- const finalLeftChildren = [];
- const finalRightChildren = [];
- const oldChildLen = oldChildren.length;
- const newChildLen = newChildren.length;
- let left = 0;
- let right = 0;
- while (oldChildLen - left - right > 0 && newChildLen - left - right > 0) {
- const leftOldNode = oldChildren[left];
- const leftNewNode = newChildren[left];
- const rightOldNode = oldChildren[oldChildLen - right - 1];
- const rightNewNode = newChildren[newChildLen - right - 1];
- let updateLeft =
- !isTextNode(leftOldNode) && matchNodeType(leftOldNode, leftNewNode);
- let updateRight =
- !isTextNode(rightOldNode) && matchNodeType(rightOldNode, rightNewNode);
- if (Array.isArray(leftOldNode) && Array.isArray(leftNewNode)) {
- finalLeftChildren.push(
- ...patchTextNodes(schema, leftOldNode, leftNewNode),
- );
- left += 1;
- continue;
- }
-
- if (updateLeft && updateRight) {
- const equalityLeft = computeChildEqualityFactor(leftOldNode, leftNewNode);
- const equalityRight = computeChildEqualityFactor(
- rightOldNode,
- rightNewNode,
- );
- if (equalityLeft < equalityRight) {
- updateLeft = false;
- } else {
- updateRight = false;
- }
- }
- if (updateLeft) {
- finalLeftChildren.push(
- patchDocumentNode(schema, leftOldNode, leftNewNode),
- );
- left += 1;
- } else if (updateRight) {
- finalRightChildren.unshift(
- patchDocumentNode(schema, rightOldNode, rightNewNode),
- );
- right += 1;
- } else {
- // Delete and insert
- finalLeftChildren.push(
- createDiffNode(schema, leftOldNode, DiffType.Deleted),
- );
- finalLeftChildren.push(
- createDiffNode(schema, leftNewNode, DiffType.Inserted),
- );
- left += 1;
- }
- }
-
- const deleteNodeLen = oldChildLen - left - right;
- const insertNodeLen = newChildLen - left - right;
- if (deleteNodeLen) {
- finalLeftChildren.push(
- ...oldChildren
- .slice(left, left + deleteNodeLen)
- .flat()
- .map((node) => createDiffNode(schema, node, DiffType.Deleted)),
- );
- }
-
- if (insertNodeLen) {
- finalRightChildren.unshift(
- ...newChildren
- .slice(left, left + insertNodeLen)
- .flat()
- .map((node) => createDiffNode(schema, node, DiffType.Inserted)),
- );
- }
-
- return [...finalLeftChildren, ...finalRightChildren];
-};
-
-// Updated function to perform sentence-level diffs
-export const patchTextNodes = (schema, oldNode, newNode) => {
- const dmp = new diff_match_patch();
-
- // Concatenate the text from the text nodes
- const oldText = oldNode.map((n) => getNodeText(n)).join('');
- const newText = newNode.map((n) => getNodeText(n)).join('');
-
- // Tokenize the text into sentences
- const oldSentences = tokenizeSentences(oldText);
- const newSentences = tokenizeSentences(newText);
-
- // Map sentences to unique characters
- const { chars1, chars2, lineArray } = sentencesToChars(
- oldSentences,
- newSentences,
- );
-
- // Perform the diff
- let diffs = dmp.diff_main(chars1, chars2, false);
-
- // Convert back to sentences
- diffs = diffs.map(([type, text]) => {
- const sentences = text
- .split('')
- .map((char) => lineArray[char.charCodeAt(0)]);
- return [type, sentences];
- });
-
- // Map diffs to nodes
- const res = diffs.flatMap(([type, sentences]) => {
- return sentences.map((sentence) => {
- const node = createTextNode(
- schema,
- sentence,
- type !== DiffType.Unchanged ? [createDiffMark(schema, type)] : [],
- );
- return node;
- });
- });
-
- return res;
-};
-
-// Function to tokenize text into sentences
-const tokenizeSentences = (text) => {
- return text.match(/[^.!?]+[.!?]*\s*/g) || [];
-};
-
-// Function to map sentences to unique characters
-const sentencesToChars = (oldSentences, newSentences) => {
- const lineArray = [];
- const lineHash = {};
- let lineStart = 0;
-
- const chars1 = oldSentences
- .map((sentence) => {
- const line = sentence;
- if (line in lineHash) {
- return String.fromCharCode(lineHash[line]);
- }
- lineHash[line] = lineStart;
- lineArray[lineStart] = line;
- lineStart++;
- return String.fromCharCode(lineHash[line]);
- })
- .join('');
-
- const chars2 = newSentences
- .map((sentence) => {
- const line = sentence;
- if (line in lineHash) {
- return String.fromCharCode(lineHash[line]);
- }
- lineHash[line] = lineStart;
- lineArray[lineStart] = line;
- lineStart++;
- return String.fromCharCode(lineHash[line]);
- })
- .join('');
-
- return { chars1, chars2, lineArray };
-};
-
-export const computeChildEqualityFactor = (node1, node2) => {
- return 0;
-};
-
-export const assertNodeTypeEqual = (node1, node2) => {
- if (getNodeProperty(node1, 'type') !== getNodeProperty(node2, 'type')) {
- throw new Error(`node type not equal: ${node1.type} !== ${node2.type}`);
- }
-};
-
-export const ensureArray = (value) => {
- return Array.isArray(value) ? value : [value];
-};
-
-export const isNodeEqual = (node1, node2) => {
- const isNode1Array = Array.isArray(node1);
- const isNode2Array = Array.isArray(node2);
- if (isNode1Array !== isNode2Array) {
- return false;
- }
- if (isNode1Array) {
- return (
- node1.length === node2.length &&
- node1.every((node, index) => isNodeEqual(node, node2[index]))
- );
- }
-
- const type1 = getNodeProperty(node1, 'type');
- const type2 = getNodeProperty(node2, 'type');
- if (type1 !== type2) {
- return false;
- }
- if (isTextNode(node1)) {
- const text1 = getNodeProperty(node1, 'text');
- const text2 = getNodeProperty(node2, 'text');
- if (text1 !== text2) {
- return false;
- }
- }
- const attrs1 = getNodeAttributes(node1);
- const attrs2 = getNodeAttributes(node2);
- const attrs = [...new Set([...Object.keys(attrs1), ...Object.keys(attrs2)])];
- for (const attr of attrs) {
- if (attrs1[attr] !== attrs2[attr]) {
- return false;
- }
- }
- const marks1 = getNodeMarks(node1);
- const marks2 = getNodeMarks(node2);
- if (marks1.length !== marks2.length) {
- return false;
- }
- for (let i = 0; i < marks1.length; i++) {
- if (!isNodeEqual(marks1[i], marks2[i])) {
- return false;
- }
- }
- const children1 = getNodeChildren(node1);
- const children2 = getNodeChildren(node2);
- if (children1.length !== children2.length) {
- return false;
- }
- for (let i = 0; i < children1.length; i++) {
- if (!isNodeEqual(children1[i], children2[i])) {
- return false;
- }
- }
- return true;
-};
-
-export const normalizeNodeContent = (node) => {
- const content = getNodeChildren(node) ?? [];
- const res = [];
- for (let i = 0; i < content.length; i++) {
- const child = content[i];
- if (isTextNode(child)) {
- const textNodes = [];
- for (
- let textNode = content[i];
- i < content.length && isTextNode(textNode);
- textNode = content[++i]
- ) {
- textNodes.push(textNode);
- }
- i--;
- res.push(textNodes);
- } else {
- res.push(child);
- }
- }
- return res;
-};
-
-export const getNodeProperty = (node, property) => {
- if (property === 'type') {
- return node.type?.name;
- }
- return node[property];
-};
-
-export const getNodeAttribute = (node, attribute) =>
- node.attrs ? node.attrs[attribute] : undefined;
-
-export const getNodeAttributes = (node) => (node.attrs ? node.attrs : {});
-
-export const getNodeMarks = (node) => node.marks ?? [];
-
-export const getNodeChildren = (node) => node.content?.content ?? [];
-
-export const getNodeText = (node) => node.text;
-
-export const isTextNode = (node) => node.type?.name === 'text';
-
-export const matchNodeType = (node1, node2) =>
- node1.type?.name === node2.type?.name ||
- (Array.isArray(node1) && Array.isArray(node2));
-
-export const createNewNode = (oldNode, children) => {
- if (!oldNode.type) {
- throw new Error('oldNode.type is undefined');
- }
- return new Node(
- oldNode.type,
- oldNode.attrs,
- Fragment.fromArray(children),
- oldNode.marks,
- );
-};
-
-export const createDiffNode = (schema, node, type) => {
- return mapDocumentNode(node, (node) => {
- if (isTextNode(node)) {
- return createTextNode(schema, getNodeText(node), [
- ...(node.marks || []),
- createDiffMark(schema, type),
- ]);
- }
- return node;
- });
-};
-
-function mapDocumentNode(node, mapper) {
- const copy = node.copy(
- Fragment.from(
- node.content.content
- .map((node) => mapDocumentNode(node, mapper))
- .filter((n) => n),
- ),
- );
- return mapper(copy) || copy;
-}
-
-export const createDiffMark = (schema, type) => {
- if (type === DiffType.Inserted) {
- return schema.mark('diffMark', { type });
- }
- if (type === DiffType.Deleted) {
- return schema.mark('diffMark', { type });
- }
- throw new Error('type is not valid');
-};
-
-export const createTextNode = (schema, content, marks = []) => {
- return schema.text(content, marks);
-};
-
-export const diffEditor = (schema, oldDoc, newDoc) => {
- const oldNode = Node.fromJSON(schema, oldDoc);
- const newNode = Node.fromJSON(schema, newDoc);
- return patchDocumentNode(schema, oldNode, newNode);
-};
diff --git a/src/features/documents/artifacts/text/components/editor/functions.tsx b/src/features/documents/artifacts/text/components/editor/functions.tsx
deleted file mode 100644
index 12582e2c..00000000
--- a/src/features/documents/artifacts/text/components/editor/functions.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-'use client';
-
-import { defaultMarkdownSerializer } from 'prosemirror-markdown';
-import { DOMParser, type Node } from 'prosemirror-model';
-import { renderToString } from 'react-dom/server';
-import { documentSchema } from './config';
-import { Markdown } from './markdown';
-
-export const buildDocumentFromContent = (content: string) => {
- const parser = DOMParser.fromSchema(documentSchema);
- const stringFromMarkdown = renderToString({content});
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = stringFromMarkdown;
- return parser.parse(tempContainer);
-};
-
-export const buildContentFromDocument = (document: Node) => {
- return defaultMarkdownSerializer.serialize(document);
-};
diff --git a/src/features/documents/artifacts/text/components/editor/markdown.css b/src/features/documents/artifacts/text/components/editor/markdown.css
deleted file mode 100644
index 98649e6d..00000000
--- a/src/features/documents/artifacts/text/components/editor/markdown.css
+++ /dev/null
@@ -1,13 +0,0 @@
-.markdown-preview-content ul {
- list-style-type: disc !important;
- padding-left: 1.5rem !important;
- }
-
-.markdown-preview-content ol {
-list-style-type: decimal !important;
-padding-left: 1.5rem !important;
-}
-
-.markdown-preview-content pre code {
- min-width: 100% !important;
-}
\ No newline at end of file
diff --git a/src/features/documents/artifacts/text/components/editor/markdown.tsx b/src/features/documents/artifacts/text/components/editor/markdown.tsx
deleted file mode 100644
index 28a52c25..00000000
--- a/src/features/documents/artifacts/text/components/editor/markdown.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import MarkdownPreview from '@uiw/react-markdown-preview';
-import { memo } from 'react';
-import { useTheme } from '@/shared/components/theme-provider';
-import './markdown.css';
-
-const NonMemoizedMarkdown = ({ children }: { children: string }) => {
- const { resolvedTheme } = useTheme();
- return (
-
- );
-};
-
-export const Markdown = memo(
- NonMemoizedMarkdown,
- (prevProps, nextProps) => prevProps.children === nextProps.children,
-);
diff --git a/src/features/documents/artifacts/text/components/editor/react-renderer.tsx b/src/features/documents/artifacts/text/components/editor/react-renderer.tsx
deleted file mode 100644
index 5a203a78..00000000
--- a/src/features/documents/artifacts/text/components/editor/react-renderer.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { createRoot } from 'react-dom/client';
-
-export const ReactRenderer = {
- render(component: React.ReactElement, dom: HTMLElement) {
- const root = createRoot(dom);
- root.render(component);
-
- return {
- destroy: () => root.unmount(),
- };
- },
-};
diff --git a/src/features/documents/artifacts/text/components/text-content.tsx b/src/features/documents/artifacts/text/components/text-content.tsx
deleted file mode 100644
index cd70bcb5..00000000
--- a/src/features/documents/artifacts/text/components/text-content.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { DiffView } from '@/features/documents/artifacts/text/components/diffview';
-import { Editor } from '@/features/documents/artifacts/text/components/text-editor';
-import type { ArtifactContent } from '@/features/documents/artifacts/types';
-
-const Skeleton = () => {
- return (
-
- );
-};
-
-export function TextContent(props: ArtifactContent) {
- const {
- mode,
- status,
- content,
- isCurrentVersion,
- currentVersionIndex,
- onSaveContent,
- getDocumentContentById,
- isLoading,
- } = props;
-
- if (isLoading) {
- return ;
- }
-
- if (mode === 'diff') {
- const oldContent = getDocumentContentById(currentVersionIndex - 1);
- const newContent = getDocumentContentById(currentVersionIndex);
- return ;
- }
-
- return (
-
-
-
- );
-}
diff --git a/src/features/documents/artifacts/text/components/text-editor.tsx b/src/features/documents/artifacts/text/components/text-editor.tsx
deleted file mode 100644
index dfed2ed5..00000000
--- a/src/features/documents/artifacts/text/components/text-editor.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-'use client';
-
-import { exampleSetup } from 'prosemirror-example-setup';
-import { inputRules } from 'prosemirror-inputrules';
-import { EditorState } from 'prosemirror-state';
-import { EditorView } from 'prosemirror-view';
-import { memo, useEffect, useRef } from 'react';
-
-import {
- documentSchema,
- handleTransaction,
- headingRule,
-} from './editor/config';
-import {
- buildContentFromDocument,
- buildDocumentFromContent,
-} from './editor/functions';
-
-type EditorProps = {
- content: string;
- onSaveContent: (updatedContent: string, debounce: boolean) => void;
- status: 'streaming' | 'idle';
- isCurrentVersion: boolean;
- currentVersionIndex: number;
-};
-
-function PureEditor({ content, onSaveContent, status }: EditorProps) {
- const containerRef = useRef(null);
- const editorRef = useRef(null);
-
- useEffect(() => {
- if (containerRef.current && !editorRef.current) {
- const state = EditorState.create({
- doc: buildDocumentFromContent(content),
- plugins: [
- ...exampleSetup({ schema: documentSchema, menuBar: false }),
- inputRules({
- rules: [
- headingRule(1),
- headingRule(2),
- headingRule(3),
- headingRule(4),
- headingRule(5),
- headingRule(6),
- ],
- }),
- ],
- });
-
- editorRef.current = new EditorView(containerRef.current, {
- state,
- });
- }
-
- return () => {
- if (editorRef.current) {
- editorRef.current.destroy();
- editorRef.current = null;
- }
- };
- // NOTE: we only want to run this effect once
- // eslint-disable-next-line
- }, []);
-
- useEffect(() => {
- if (editorRef.current) {
- editorRef.current.setProps({
- dispatchTransaction: (transaction) => {
- handleTransaction({
- transaction,
- editorRef,
- onSaveContent,
- });
- },
- });
- }
- }, [onSaveContent]);
-
- useEffect(() => {
- if (editorRef.current && content) {
- const currentContent = buildContentFromDocument(
- editorRef.current.state.doc,
- );
-
- if (status === 'streaming') {
- const newDocument = buildDocumentFromContent(content);
-
- const transaction = editorRef.current.state.tr.replaceWith(
- 0,
- editorRef.current.state.doc.content.size,
- newDocument.content,
- );
-
- transaction.setMeta('no-save', true);
- editorRef.current.dispatch(transaction);
- return;
- }
-
- if (currentContent !== content) {
- const newDocument = buildDocumentFromContent(content);
-
- const transaction = editorRef.current.state.tr.replaceWith(
- 0,
- editorRef.current.state.doc.content.size,
- newDocument.content,
- );
-
- transaction.setMeta('no-save', true);
- editorRef.current.dispatch(transaction);
- }
- }
- }, [content, status]);
-
- return (
-
- );
-}
-
-function areEqual(prevProps: EditorProps, nextProps: EditorProps) {
- return (
- prevProps.currentVersionIndex === nextProps.currentVersionIndex &&
- prevProps.isCurrentVersion === nextProps.isCurrentVersion &&
- !(prevProps.status === 'streaming' && nextProps.status === 'streaming') &&
- prevProps.content === nextProps.content &&
- prevProps.onSaveContent === nextProps.onSaveContent
- );
-}
-
-export const Editor = memo(PureEditor, areEqual);
diff --git a/src/features/documents/artifacts/text/components/text-preview.tsx b/src/features/documents/artifacts/text/components/text-preview.tsx
deleted file mode 100644
index 8741d75a..00000000
--- a/src/features/documents/artifacts/text/components/text-preview.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Editor } from '@/features/documents/artifacts/text/components/text-editor';
-import type { Document } from '@/features/documents/stores';
-
-interface TextPreviewProps {
- document: Document;
- editorStatus: 'streaming' | 'idle';
-}
-
-export function TextPreview({ document, editorStatus }: TextPreviewProps) {
- const commonProps = {
- content: document.content ?? '',
- isCurrentVersion: true,
- currentVersionIndex: 0,
- status: editorStatus,
- saveContent: () => {},
- suggestions: [],
- };
-
- return {}} />;
-}
diff --git a/src/features/documents/artifacts/text/generator.ts b/src/features/documents/artifacts/text/generator.ts
deleted file mode 100644
index d40e28c7..00000000
--- a/src/features/documents/artifacts/text/generator.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { smoothStream, streamText } from 'ai';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export async function generateTextContent(
- title: string,
- onDelta: (delta: string) => void,
-): Promise {
- let draftContent = '';
-
- const { fullStream } = streamText({
- model: llmProvider.artifact(),
- system:
- 'Write about the given topic. Markdown is supported. Use headings wherever appropriate.',
- experimental_transform: smoothStream({ chunking: 'word' }),
- prompt: title,
- });
-
- for await (const delta of fullStream) {
- if (delta.type === 'text-delta') {
- const { textDelta } = delta;
- draftContent += textDelta;
- onDelta(textDelta);
- }
- }
-
- return draftContent;
-}
diff --git a/src/features/documents/artifacts/text/index.tsx b/src/features/documents/artifacts/text/index.tsx
deleted file mode 100644
index d47beec3..00000000
--- a/src/features/documents/artifacts/text/index.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Artifact } from '@/features/documents/artifacts/types';
-import { createCopyAction } from './actions/copy';
-import { createRedoAction } from './actions/redo';
-import { createUndoAction } from './actions/undo';
-import { createVersionChangeAction } from './actions/version-change';
-import { TextContent } from './components/text-content';
-
-export const createTextArtifact = () => {
- return new Artifact<'text'>({
- kind: 'text',
- description: 'Text artifact for displaying and editing text',
- initialize: async ({ documentId, setMetadata }) => {},
- content: TextContent,
- actions: [
- createVersionChangeAction(),
- createUndoAction(),
- createRedoAction(),
- createCopyAction(),
- ],
- });
-};
-
-export const textArtifact = createTextArtifact();
-
-export { generateTextContent } from './generator';
-export { updateTextContent } from './updater';
diff --git a/src/features/documents/artifacts/text/updater.ts b/src/features/documents/artifacts/text/updater.ts
deleted file mode 100644
index c24b219c..00000000
--- a/src/features/documents/artifacts/text/updater.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { smoothStream, streamText } from 'ai';
-import { llmProvider } from '@/features/ai-provider/services';
-
-export const updateDocumentPrompt = (currentContent: string | null) =>
- `\
-Improve the following contents of the document based on the given prompt.
-
-${currentContent}
-`;
-
-export async function updateTextContent(
- currentContent: string,
- description: string,
- onDelta: (delta: string) => void,
-): Promise {
- let draftContent = '';
-
- const { fullStream } = streamText({
- model: llmProvider.artifact(),
- system: updateDocumentPrompt(currentContent),
- experimental_transform: smoothStream({ chunking: 'word' }),
- prompt: description,
- experimental_providerMetadata: {
- openai: {
- prediction: {
- type: 'content',
- content: currentContent,
- },
- },
- },
- });
-
- for await (const delta of fullStream) {
- if (delta.type === 'text-delta') {
- const { textDelta } = delta;
- draftContent += textDelta;
- onDelta(textDelta);
- }
- }
-
- return draftContent;
-}
diff --git a/src/features/documents/artifacts/types.tsx b/src/features/documents/artifacts/types.tsx
deleted file mode 100644
index ed285796..00000000
--- a/src/features/documents/artifacts/types.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import type { ComponentType, Dispatch, ReactNode, SetStateAction } from 'react';
-
-export type DataStreamDelta = {
- type:
- | 'text-delta'
- | 'code-delta'
- | 'sheet-delta'
- | 'image-delta'
- | 'title'
- | 'id'
- | 'clear'
- | 'finish'
- | 'kind';
- content: string;
-};
-
-export type ArtifactActionContext = {
- content: string;
- handleVersionChange: (type: 'next' | 'prev' | 'toggle' | 'latest') => void;
- currentVersionIndex: number;
- isCurrentVersion: boolean;
- mode: 'edit' | 'diff';
- metadata: M;
- setMetadata: Dispatch>;
-};
-
-export type ArtifactAction = {
- icon: ReactNode;
- label?: string;
- description: string;
- onClick: (context: ArtifactActionContext) => Promise | void;
- isDisabled?: (context: ArtifactActionContext) => boolean;
-};
-
-export interface ArtifactContent {
- title: string;
- content: string;
- mode: 'edit' | 'diff';
- isCurrentVersion: boolean;
- currentVersionIndex: number;
- status: 'streaming' | 'idle';
- onSaveContent: (updatedContent: string, debounce: boolean) => void;
- isInline: boolean;
- getDocumentContentById: (index: number) => string;
- isLoading: boolean;
- metadata: M;
- setMetadata: Dispatch>;
-}
-
-interface InitializeParameters {
- documentId: string;
- setMetadata: Dispatch>;
-}
-
-type ArtifactConfig = {
- kind: T;
- description: string;
- content: ComponentType>;
- actions: Array>;
- initialize?: (parameters: InitializeParameters) => void;
-};
-
-export class Artifact {
- readonly kind: T;
- readonly description: string;
- readonly content: ComponentType>;
- readonly actions: Array>;
- readonly initialize?: (parameters: InitializeParameters) => void;
-
- constructor(config: ArtifactConfig) {
- this.kind = config.kind;
- this.description = config.description;
- this.content = config.content;
- this.actions = config.actions || [];
- this.initialize = config.initialize || (async () => ({}));
- }
-}
diff --git a/src/features/documents/components/artifact-actions.tsx b/src/features/documents/components/artifact-actions.tsx
deleted file mode 100644
index b1a3f91c..00000000
--- a/src/features/documents/components/artifact-actions.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import { type Dispatch, memo, type SetStateAction, useState } from 'react';
-import { artifactDefinitions } from '@/features/documents/artifacts';
-import type { ArtifactActionContext } from '@/features/documents/artifacts/types';
-import type { CurrentDocumentProps } from '@/features/documents/stores';
-import { toast } from '@/shared/components/toast';
-import { Button } from '@/shared/components/ui/button';
-import {
- Tooltip,
- TooltipContent,
- TooltipTrigger,
-} from '@/shared/components/ui/tooltip';
-import { cn } from '@/shared/utils';
-
-interface ArtifactActionsProps {
- artifact: CurrentDocumentProps;
- handleVersionChange: (type: 'next' | 'prev' | 'toggle' | 'latest') => void;
- currentVersionIndex: number;
- isCurrentVersion: boolean;
- mode: 'edit' | 'diff';
- metadata: any;
- setMetadata: Dispatch>;
-}
-
-function PureArtifactActions({
- artifact,
- handleVersionChange,
- currentVersionIndex,
- isCurrentVersion,
- mode,
- metadata,
- setMetadata,
-}: ArtifactActionsProps) {
- const [isLoading, setIsLoading] = useState(false);
-
- const artifactDefinition = artifactDefinitions.find(
- (definition) => definition.kind === artifact.kind,
- );
-
- if (!artifactDefinition) {
- throw new Error('Artifact definition not found!');
- }
-
- const actionContext: ArtifactActionContext = {
- content: artifact.content,
- handleVersionChange,
- currentVersionIndex,
- isCurrentVersion,
- mode,
- metadata,
- setMetadata,
- };
-
- return (
-
- {artifactDefinition.actions.map((action) => (
-
-
- {
- setIsLoading(true);
-
- try {
- await Promise.resolve(action.onClick(actionContext));
- } catch (error) {
- toast({
- type: 'error',
- description: 'Failed to execute action',
- });
- } finally {
- setIsLoading(false);
- }
- }}
- disabled={
- isLoading || artifact.status === 'streaming'
- ? true
- : action.isDisabled
- ? action.isDisabled(actionContext)
- : false
- }
- >
- {action.icon}
- {action.label}
-
-
- {action.description}
-
- ))}
-
- );
-}
-
-export const ArtifactActions = memo(
- PureArtifactActions,
- (prevProps, nextProps) => {
- if (prevProps.artifact.status !== nextProps.artifact.status) return false;
- if (prevProps.currentVersionIndex !== nextProps.currentVersionIndex)
- return false;
- if (prevProps.isCurrentVersion !== nextProps.isCurrentVersion) return false;
- if (prevProps.artifact.content !== nextProps.artifact.content) return false;
-
- return true;
- },
-);
diff --git a/src/features/documents/components/artifact-close-button.tsx b/src/features/documents/components/artifact-close-button.tsx
deleted file mode 100644
index 616ad400..00000000
--- a/src/features/documents/components/artifact-close-button.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { X } from 'lucide-react';
-import { memo } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { useCurrentDocument } from '@/features/documents/hooks/use-document-current';
-import { Button } from '@/shared/components/ui/button';
-
-function PureArtifactCloseButton({ chatId }: { chatId: string }) {
- const { closeCurrentDocument } = useCurrentDocument();
- const navigate = useNavigate();
-
- return (
- {
- navigate(`/chat?cid=${chatId}`);
- closeCurrentDocument();
- }}
- >
-
-
- );
-}
-
-export const ArtifactCloseButton = memo(PureArtifactCloseButton, () => true);
diff --git a/src/features/documents/components/artifact-messages-header.tsx b/src/features/documents/components/artifact-messages-header.tsx
deleted file mode 100644
index cb973275..00000000
--- a/src/features/documents/components/artifact-messages-header.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { PlusIcon } from 'lucide-react';
-import { useNavigate } from 'react-router-dom';
-import { useChatSession } from '@/features/ai-chat/hooks/use-chat-session';
-import { Button } from '@/shared/components/ui/button';
-import { generateUUID } from '@/shared/utils';
-
-export function ArtifactMessagesHeader({ chatId }: { chatId: string }) {
- const navigate = useNavigate();
-
- const { session } = useChatSession(chatId);
-
- const handleNewChat = () => {
- const chatId = generateUUID();
- navigate(`/artifact?cid=${chatId}`);
- };
-
- return (
-
-
- {session ? session.title : 'New Chat'}
-
-
-
-
-
- );
-}
diff --git a/src/features/documents/components/artifact-viewer.tsx b/src/features/documents/components/artifact-viewer.tsx
deleted file mode 100644
index 94537d1b..00000000
--- a/src/features/documents/components/artifact-viewer.tsx
+++ /dev/null
@@ -1,253 +0,0 @@
-import type { UseChatHelpers } from '@ai-sdk/react';
-import { formatDistance } from 'date-fns';
-import { useCallback, useEffect, useState } from 'react';
-import { useDebounceCallback } from 'usehooks-ts';
-import { artifactDefinitions } from '@/features/documents/artifacts';
-import { useCurrentDocument } from '@/features/documents/hooks/use-document-current';
-import { useDocuments } from '@/features/documents/hooks/use-documents';
-import type { Document } from '@/features/documents/stores';
-import { ArtifactActions } from './artifact-actions';
-import { ArtifactCloseButton } from './artifact-close-button';
-import { VersionFooter } from './version-footer';
-
-interface ArtifactViewerProps {
- chatId: string;
- status: UseChatHelpers['status'];
-}
-
-export function ArtifactViewer({ chatId, status }: ArtifactViewerProps) {
- const {
- currentDocument: artifact,
- setCurrentDocument,
- metadata,
- setMetadata,
- } = useCurrentDocument();
- const { documentsMap, getDocuments, updateDocumentContent } = useDocuments();
-
- // Use document store instead of SWR
- const [versionedDocuments, setVersionedDocuments] = useState>(
- [],
- );
-
- useEffect(() => {
- if (artifact.documentId !== 'init' && artifact.status !== 'streaming') {
- const currentDocuments = getDocuments(artifact.documentId);
- if (currentDocuments) {
- setVersionedDocuments(currentDocuments);
- }
- }
- }, [documentsMap, artifact.documentId, artifact.status]); // Remove getDocuments from dependencies
-
- const [mode, setMode] = useState<'edit' | 'diff'>('edit');
- const [document, setDocument] = useState(null);
- const [currentVersionIndex, setCurrentVersionIndex] = useState(-1);
-
- useEffect(() => {
- if (versionedDocuments && versionedDocuments.length > 0) {
- const mostRecentDocument = versionedDocuments.at(-1);
-
- if (mostRecentDocument) {
- setDocument(mostRecentDocument);
- setCurrentVersionIndex(versionedDocuments.length - 1);
-
- // Only update if content is actually different to prevent infinite loops
- setCurrentDocument((currentDocument) => {
- if (currentDocument.content !== (mostRecentDocument.content ?? '')) {
- return {
- ...currentDocument,
- content: mostRecentDocument.content ?? '',
- };
- }
- return currentDocument;
- });
- }
- }
- }, [versionedDocuments]); // Remove setCurrentDocument from dependencies
-
- const [isContentDirty, setIsContentDirty] = useState(false);
-
- const handleContentChange = useCallback(
- (updatedContent: string) => {
- if (!artifact || !document) return;
-
- if (document.content !== updatedContent) {
- // Update document in local store
- updateDocumentContent(artifact.documentId, updatedContent);
-
- setIsContentDirty(false);
-
- // Update local state - update the current document in the array instead of replacing the whole array
- setVersionedDocuments((prevVersions) => {
- const updatedVersions = [...prevVersions];
- const currentIndex = updatedVersions.findIndex(
- (doc) => doc.id === document.id,
- );
-
- if (currentIndex !== -1) {
- updatedVersions[currentIndex] = {
- ...document,
- content: updatedContent,
- updatedAt: Date.now(),
- };
- }
-
- return updatedVersions;
- });
- }
- },
- [artifact, document, updateDocumentContent],
- );
-
- const debouncedHandleContentChange = useDebounceCallback(
- handleContentChange,
- 2000,
- );
-
- const saveContent = useCallback(
- (updatedContent: string, debounce: boolean) => {
- if (document && updatedContent !== document.content) {
- setIsContentDirty(true);
-
- if (debounce) {
- debouncedHandleContentChange(updatedContent);
- } else {
- handleContentChange(updatedContent);
- }
- }
- },
- [document, debouncedHandleContentChange, handleContentChange],
- );
-
- function getDocumentContentById(index: number) {
- if (!versionedDocuments) return '';
- if (!versionedDocuments[index]) return '';
- return versionedDocuments[index].content ?? '';
- }
-
- const handleVersionChange = (type: 'next' | 'prev' | 'toggle' | 'latest') => {
- if (!versionedDocuments) return;
-
- if (type === 'latest') {
- setCurrentVersionIndex(versionedDocuments.length - 1);
- setMode('edit');
- }
-
- if (type === 'toggle') {
- setMode((mode) => (mode === 'edit' ? 'diff' : 'edit'));
- }
-
- if (type === 'prev') {
- if (currentVersionIndex > 0) {
- setCurrentVersionIndex((index) => index - 1);
- }
- } else if (type === 'next') {
- if (currentVersionIndex < versionedDocuments.length - 1) {
- setCurrentVersionIndex((index) => index + 1);
- }
- }
- };
-
- /*
- * NOTE: if there are no documents, or if
- * the documents are being fetched, then
- * we mark it as the current version.
- */
-
- const isCurrentVersion =
- versionedDocuments && versionedDocuments.length > 0
- ? currentVersionIndex === versionedDocuments.length - 1
- : true;
-
- const artifactDefinition = artifactDefinitions.find(
- (definition) => definition.kind === artifact.kind,
- );
-
- if (!artifactDefinition) {
- throw new Error('Artifact definition not found!');
- }
-
- useEffect(() => {
- if (artifact.documentId !== 'init') {
- if (artifactDefinition.initialize) {
- artifactDefinition.initialize({
- documentId: artifact.documentId,
- setMetadata,
- });
- }
- }
- }, [artifact.documentId, artifactDefinition, setMetadata]);
-
- return (
-
-
-
-
-
-
-
{artifact.title}
-
- {isContentDirty ? (
-
- Saving changes...
-
- ) : document ? (
-
- {`Updated ${formatDistance(
- new Date(document.updatedAt),
- new Date(),
- {
- addSuffix: true,
- },
- )}`}
-
- ) : (
-
- )}
-
-
-
-
-
-
-
-
- {!isCurrentVersion && (
-
- )}
-
- );
-}
diff --git a/src/features/documents/components/artifact.tsx b/src/features/documents/components/artifact.tsx
deleted file mode 100644
index 22ef51c0..00000000
--- a/src/features/documents/components/artifact.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-'use client';
-
-import type { Attachment, UIMessage } from 'ai';
-import { useState } from 'react';
-import { useNavigate } from 'react-router-dom';
-import { Messages, MultimodalInput } from '@/features/ai-chat/components';
-import { useChatDefault } from '@/features/ai-chat/hooks/use-chat-default';
-import { useCurrentDocument } from '@/features/documents/hooks/use-document-current';
-import {
- ResizableHandle,
- ResizablePanel,
- ResizablePanelGroup,
-} from '@/shared/components/ui/resizable';
-import { ArtifactMessagesHeader } from './artifact-messages-header';
-import { ArtifactViewer } from './artifact-viewer';
-
-export function Artifact({
- chatId,
- initialMessages,
- isReadonly,
-}: {
- chatId: string;
- initialMessages: Array;
- isReadonly: boolean;
-}) {
- const navigate = useNavigate();
-
- const handleOnResponse = (response: any) => {
- navigate(`/artifact?cid=${chatId}`);
- };
-
- const {
- messages,
- setMessages: setChatMessages,
- handleSubmit,
- input,
- setInput,
- append,
- status,
- stop,
- reload,
- } = useChatDefault(chatId, initialMessages, handleOnResponse);
-
- const [attachments, setAttachments] = useState>([]);
- const { currentDocument } = useCurrentDocument();
- const isArtifact = currentDocument.documentId !== 'init';
-
- if (!isArtifact) {
- return (
-
- Todo: this is the all artifact page
-
- );
- }
-
- return (
-
-
- {/* Artifact viewer panel */}
-
-
-
-
- {/* Resizable handle */}
-
-
- {/* Chat panel */}
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/features/documents/components/document-preview-call.tsx b/src/features/documents/components/document-preview-call.tsx
deleted file mode 100644
index aa6a6d43..00000000
--- a/src/features/documents/components/document-preview-call.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { FileIcon, LoaderIcon, PencilIcon } from 'lucide-react';
-import { memo } from 'react';
-import { useNavigate } from 'react-router-dom';
-
-import { toast } from '@/shared/components/toast';
-
-import { useLanguage } from '@/shared/hooks/use-language';
-
-const getActionText = (
- type: 'create' | 'update' | 'request-suggestions',
- tense: 'present' | 'past',
- t: (key: string) => string,
-) => {
- switch (type) {
- case 'create':
- return tense === 'present'
- ? t('documentTool.creating')
- : t('documentTool.created');
- case 'update':
- return tense === 'present'
- ? t('documentTool.updating')
- : t('documentTool.updated');
- case 'request-suggestions':
- return tense === 'present'
- ? t('documentTool.addingSuggestions')
- : t('documentTool.addedSuggestions');
- default:
- return null;
- }
-};
-
-interface DocumentToolCallProps {
- chatId: string;
- type: 'create' | 'update' | 'request-suggestions';
- args: { title: string };
- isReadonly: boolean;
-}
-
-function PureDocumentToolCall({
- chatId,
- type,
- args,
- isReadonly,
-}: DocumentToolCallProps) {
- const { t } = useLanguage();
- const navigate = useNavigate();
-
- return (
- {
- if (isReadonly) {
- toast({
- description: t('documentTool.viewingNotSupported'),
- type: 'error',
- });
- return;
- }
-
- navigate(`/artifact?cid=${chatId}`);
- }}
- >
-
-
- {type === 'create' ? (
-
- ) : type === 'update' ? (
-
- ) : null}
-
-
-
- {`${getActionText(type, 'present', t)} ${args.title ? `"${args.title}"` : ''}`}
-
-
-
- {}
-
- );
-}
-
-export const DocumentToolCall = memo(PureDocumentToolCall, () => true);
diff --git a/src/features/documents/components/document-preview-result.tsx b/src/features/documents/components/document-preview-result.tsx
deleted file mode 100644
index 22b185df..00000000
--- a/src/features/documents/components/document-preview-result.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { FileIcon, PencilIcon } from 'lucide-react';
-import { memo } from 'react';
-import { useNavigate } from 'react-router-dom';
-import type { ArtifactKind } from '@/features/documents/artifacts';
-import { useCurrentDocument } from '@/features/documents/hooks/use-document-current';
-import { toast } from '@/shared/components/toast';
-import { useLanguage } from '@/shared/hooks/use-language';
-
-const getActionText = (
- type: 'create' | 'update' | 'request-suggestions',
- tense: 'present' | 'past',
- t: (key: string) => string,
-) => {
- switch (type) {
- case 'create':
- return tense === 'present'
- ? t('documentTool.creating')
- : t('documentTool.created');
- case 'update':
- return tense === 'present'
- ? t('documentTool.updating')
- : t('documentTool.updated');
- case 'request-suggestions':
- return tense === 'present'
- ? t('documentTool.addingSuggestions')
- : t('documentTool.addedSuggestions');
- default:
- return null;
- }
-};
-
-interface DocumentToolResultProps {
- chatId: string;
- type: 'create' | 'update' | 'request-suggestions';
- result: { id: string; title: string; kind: ArtifactKind };
- isReadonly: boolean;
-}
-
-function PureDocumentToolResult({
- chatId,
- type,
- result,
- isReadonly,
-}: DocumentToolResultProps) {
- const { setCurrentDocument } = useCurrentDocument();
- const { t } = useLanguage();
- const navigate = useNavigate();
-
- return (
- {
- if (isReadonly) {
- toast({
- description: t('documentTool.viewingNotSupported'),
- type: 'error',
- });
- return;
- }
-
- setCurrentDocument({
- documentId: result.id,
- kind: result.kind,
- content: '',
- title: result.title,
- status: 'idle',
- });
- navigate(`/artifact?cid=${chatId}`);
- }}
- >
-
- {type === 'create' ? (
-
- ) : type === 'update' ? (
-
- ) : null}
-
-
- {`${getActionText(type, 'past', t)} "${result.title}"`}
-
-
- );
-}
-
-export const DocumentToolResult = memo(PureDocumentToolResult, () => true);
diff --git a/src/features/documents/components/document-preview.tsx b/src/features/documents/components/document-preview.tsx
deleted file mode 100644
index a55e4707..00000000
--- a/src/features/documents/components/document-preview.tsx
+++ /dev/null
@@ -1,262 +0,0 @@
-'use client';
-import equal from 'fast-deep-equal';
-import { FileIcon, FullscreenIcon, ImageIcon, LoaderIcon } from 'lucide-react';
-import { memo, useCallback, useMemo, useRef } from 'react';
-import { useNavigate } from 'react-router-dom';
-import type { ArtifactKind } from '@/features/documents/artifacts';
-import { CodePreview } from '@/features/documents/artifacts/code/components/code-preview';
-import { ImagePreview } from '@/features/documents/artifacts/image/components/image-preview';
-import { SheetPreview } from '@/features/documents/artifacts/sheet/components/sheet-preview';
-import { TextPreview } from '@/features/documents/artifacts/text/components/text-preview';
-import { useCurrentDocument } from '@/features/documents/hooks/use-document-current';
-import { useDocuments } from '@/features/documents/hooks/use-documents';
-
-import type {
- CurrentDocumentProps,
- Document,
-} from '@/features/documents/stores';
-import { cn } from '@/shared/utils';
-import { DocumentToolCall } from './document-preview-call';
-import { DocumentToolResult } from './document-preview-result';
-
-interface DocumentPreviewProps {
- chatId: string;
- isReadonly: boolean;
- result?: any;
- args?: any;
-}
-
-export function DocumentPreview({
- chatId,
- isReadonly,
- result,
- args,
-}: DocumentPreviewProps) {
- const { currentDocument: artifact, setCurrentDocument } =
- useCurrentDocument();
- const { getDocument } = useDocuments();
-
- // Use document store instead of SWR
- const documents = useMemo(() => {
- if (result?.id) {
- const document = getDocument(result.id);
- return document ? [document] : [];
- }
- return [];
- }, [result?.id, getDocument]);
-
- const isDocumentsFetching = false; // No longer fetching from API
-
- const previewDocument = useMemo(() => documents?.[0], [documents]);
- const hitboxRef = useRef(null);
-
- if (artifact.documentId !== 'init') {
- if (result) {
- return (
-
- );
- }
-
- if (args) {
- return (
-
- );
- }
- }
-
- if (isDocumentsFetching) {
- return ;
- }
-
- const document: Document | null = previewDocument
- ? previewDocument
- : artifact.status === 'streaming'
- ? {
- title: artifact.title,
- kind: artifact.kind,
- content: artifact.content,
- id: artifact.documentId,
- createdAt: Date.now(),
- updatedAt: Date.now(),
- }
- : null;
-
- if (!document) return ;
-
- return (
-
-
-
-
-
- );
-}
-
-const LoadingSkeleton = ({ artifactKind }: { artifactKind: ArtifactKind }) => (
-
-
- {artifactKind === 'image' ? (
-
- ) : (
-
- )}
-
-);
-
-const PureHitboxLayer = ({
- hitboxRef,
- result,
- setCurrentDocument,
- chatId,
-}: {
- hitboxRef: React.RefObject;
- result: any;
- setCurrentDocument: (
- updaterFn:
- | CurrentDocumentProps
- | ((currentDocument: CurrentDocumentProps) => CurrentDocumentProps),
- ) => void;
- chatId: string;
-}) => {
- const navigate = useNavigate();
- const handleClick = useCallback(() => {
- setCurrentDocument((artifact) =>
- artifact.status === 'streaming'
- ? { ...artifact }
- : {
- ...artifact,
- title: result.title,
- documentId: result.id,
- kind: result.kind,
- },
- );
- navigate(`/artifact?cid=${chatId}`);
- }, [setCurrentDocument, result, chatId, navigate]);
-
- return (
-
- );
-};
-
-const HitboxLayer = memo(PureHitboxLayer, (prevProps, nextProps) => {
- if (!equal(prevProps.result, nextProps.result)) return false;
- return true;
-});
-
-const PureDocumentHeader = ({
- title,
- kind,
- isStreaming,
-}: {
- title: string;
- kind: ArtifactKind;
- isStreaming: boolean;
-}) => (
-
-
-
- {isStreaming ? (
-
-
-
- ) : kind === 'image' ? (
-
- ) : (
-
- )}
-
-
{title}
-
-
-
-);
-
-const DocumentHeader = memo(PureDocumentHeader, (prevProps, nextProps) => {
- if (prevProps.title !== nextProps.title) return false;
- if (prevProps.isStreaming !== nextProps.isStreaming) return false;
-
- return true;
-});
-
-const DocumentContentPreview = ({ document }: { document: Document }) => {
- const { currentDocument: artifact } = useCurrentDocument();
-
- const containerClassName = cn(
- 'h-[257px] overflow-y-scroll border rounded-b-2xl dark:bg-muted border-t-0 dark:border-zinc-700',
- {
- 'p-4 sm:px-14 sm:py-16': document.kind === 'text',
- 'p-0': document.kind === 'code',
- },
- );
-
- // Map artifact status to editor status
- const editorStatus: 'streaming' | 'idle' =
- artifact.status === 'streaming' ? 'streaming' : 'idle';
-
- return (
-
- {document.kind === 'text' ? (
-
- ) : document.kind === 'code' ? (
-
- ) : document.kind === 'sheet' ? (
-
- ) : document.kind === 'image' ? (
-
- ) : null}
-
- );
-};
diff --git a/src/features/documents/components/index.ts b/src/features/documents/components/index.ts
deleted file mode 100644
index 2ea8cebd..00000000
--- a/src/features/documents/components/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export * from './artifact';
-export * from './artifact-actions';
-export * from './artifact-close-button';
-export * from './artifact-viewer';
-export * from './document-preview';
-export * from './document-preview-call';
-export * from './document-preview-result';
-export * from './version-footer';
diff --git a/src/features/documents/components/version-footer.tsx b/src/features/documents/components/version-footer.tsx
deleted file mode 100644
index f51218c0..00000000
--- a/src/features/documents/components/version-footer.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-'use client';
-
-import { motion } from 'framer-motion';
-import { LoaderIcon } from 'lucide-react';
-import { useWindowSize } from 'usehooks-ts';
-import { useCurrentDocument } from '@/features/documents/hooks/use-document-current';
-import { useVersionManagement } from '@/features/documents/hooks/use-version-management';
-import type { Document } from '@/features/documents/stores';
-import { Button } from '@/shared/components/ui/button';
-import { useLanguage } from '@/shared/hooks/use-language';
-
-interface VersionFooterProps {
- handleVersionChange: (type: 'next' | 'prev' | 'toggle' | 'latest') => void;
- documents: Array | undefined;
- currentVersionIndex: number;
-}
-
-export const VersionFooter = ({
- handleVersionChange,
- documents,
- currentVersionIndex,
-}: VersionFooterProps) => {
- const { currentDocument: artifact } = useCurrentDocument();
- const { isMutating, restoreToVersion } = useVersionManagement();
- const { width } = useWindowSize();
- const isMobile = width < 768;
- const { t } = useLanguage();
-
- if (!documents) return;
-
- return (
-
-
-
{t('version.viewingPrevious')}
-
- {t('version.restoreToEdit')}
-
-
-
-
-
{
- await restoreToVersion(
- artifact.documentId,
- documents,
- currentVersionIndex,
- () => handleVersionChange('latest'),
- );
- }}
- >
- {t('version.restore')}
- {isMutating && (
-
-
-
- )}
-
-
{
- handleVersionChange('latest');
- }}
- >
- {t('version.backToLatest')}
-
-
-
- );
-};
diff --git a/src/features/documents/hooks/index.ts b/src/features/documents/hooks/index.ts
deleted file mode 100644
index 7eeb43cd..00000000
--- a/src/features/documents/hooks/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './use-document';
-export * from './use-document-current';
-export * from './use-documents';
-export * from './use-version-management';
diff --git a/src/features/documents/hooks/use-document-current.ts b/src/features/documents/hooks/use-document-current.ts
deleted file mode 100644
index d21e7888..00000000
--- a/src/features/documents/hooks/use-document-current.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { useCallback } from 'react';
-import {
- DocumentStateStore,
- defaultEmptyDocument,
-} from '@/features/documents/stores';
-import type { CurrentDocumentProps } from '@/features/documents/types';
-
-export const useCurrentDocument = () => {
- const store = DocumentStateStore();
-
- const setCurrentDocument = useCallback(
- (
- updaterFn:
- | CurrentDocumentProps
- | ((currentDocument: CurrentDocumentProps) => CurrentDocumentProps),
- ) => {
- store.setCurrentDocument(updaterFn);
- },
- [store],
- );
-
- const closeCurrentDocument = useCallback(() => {
- setCurrentDocument((currentDocument) =>
- currentDocument.status === 'streaming'
- ? {
- ...currentDocument,
- }
- : { ...defaultEmptyDocument, status: 'idle' },
- );
- }, [store]);
-
- const updateCurrentDocument = useCallback(
- (updates: Partial) => {
- store.UpdateCurrentDocument(updates);
- },
- [store],
- );
-
- const setMetadata = useCallback(
- (metadata: any) => {
- store.setCurrentDocumentMetadata(metadata);
- },
- [store],
- );
-
- const resetCurrentDocument = useCallback(() => {
- store.resetCurrentDocument();
- }, []);
-
- return {
- currentDocument: store.currentDocument,
- setCurrentDocument,
- updateCurrentDocument,
- metadata: store.getCurrentDocumentMetadata(),
- setMetadata,
- resetCurrentDocument,
- closeCurrentDocument,
- };
-};
diff --git a/src/features/documents/hooks/use-document.ts b/src/features/documents/hooks/use-document.ts
deleted file mode 100644
index 780cb761..00000000
--- a/src/features/documents/hooks/use-document.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-'use client';
-
-import { useCallback } from 'react';
-import { DocumentStateStore } from '@/features/documents/stores';
-import { useDocuments } from './use-documents';
-
-export const useDocument = (id: string) => {
- const store = DocumentStateStore();
- const {
- updateDocumentContent,
- deleteDocument,
- addNewVersion,
- deleteAfterTimestamp,
- } = useDocuments();
-
- const document = store.getDocument(id);
-
- return {
- document,
- updateContent: useCallback(
- (content: string) => {
- updateDocumentContent(id, content);
- },
- [updateDocumentContent, id],
- ),
- deleteDocument: useCallback(() => {
- deleteDocument(id);
- }, [deleteDocument, id]),
- addNewVersion: useCallback(
- (content: string) => {
- addNewVersion(id, content);
- },
- [addNewVersion, id],
- ),
- deleteAfterTimestamp: useCallback(
- (updates: { content: string; timestamp: number }) => {
- deleteAfterTimestamp(id, updates);
- },
- [deleteAfterTimestamp, id],
- ),
- };
-};
diff --git a/src/features/documents/hooks/use-documents.ts b/src/features/documents/hooks/use-documents.ts
deleted file mode 100644
index 31ad5305..00000000
--- a/src/features/documents/hooks/use-documents.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import { useCallback } from 'react';
-import { DocumentStateStore } from '@/features/documents/stores';
-import type { Document } from '@/features/documents/types';
-import { generateUUID } from '@/shared/utils';
-
-// Documents management hook with business logic
-export const useDocuments = () => {
- const store = DocumentStateStore();
-
- // 创建文档(使用服务层)
- const createDocument = useCallback(
- (title: string, kind: Document['kind']) => {
- const id = generateUUID();
- store.createDocumentWithId(id, title, kind);
- return id;
- },
- [],
- );
-
- // 创建指定 ID 的文档
- const createDocumentWithId = useCallback(
- (id: string, title: string, kind: Document['kind'], content?: string) => {
- store.createDocumentWithId(id, title, kind, content);
- },
- [],
- );
-
- // 更新文档内容
- const updateDocumentContent = useCallback(
- (id: string, content: string) => {
- const existingDocument = store.getDocument(id);
- if (!existingDocument) return;
-
- const updatedDocument: Document = {
- ...existingDocument,
- content,
- };
-
- store.updateDocument(id, updatedDocument);
- },
- [store],
- );
-
- // 删除文档
- const deleteDocument = useCallback(
- (id: string) => {
- store.deleteDocument(id);
- },
- [store],
- );
-
- // 添加新版本文档
- const addNewVersion = useCallback(
- (id: string, content: string) => {
- const originalDocument = store.getDocument(id);
- if (!originalDocument) return;
-
- const versionId = generateUUID();
- const now = Date.now();
-
- const versionDocument: Document = {
- ...originalDocument,
- id: versionId,
- content,
- createdAt: now,
- updatedAt: now,
- };
-
- store.updateDocument(versionId, versionDocument);
- },
- [store],
- );
-
- // 删除指定时间戳后的文档版本
- const deleteAfterTimestamp = useCallback(
- (id: string, updates: { content: string; timestamp: number }) => {
- store.deleteDocumentAfterTimestamp(id, updates);
- },
- [store],
- );
-
- // 获取排序后的文档
- const getSortedDocuments = useCallback(() => {
- return Object.values(store.documents).sort(
- (a, b) => b.updatedAt - a.updatedAt,
- );
- }, [store.documents]);
-
- // 清空所有文档
- const clearAllDocuments = useCallback(() => {
- store.clearAllDocuments();
- }, [store]);
-
- return {
- documents: store.documents,
- documentsMap: store.documents,
- getDocument: store.getDocument,
- getDocuments: store.getDocuments,
- createDocument,
- createDocumentWithId,
- updateDocumentContent,
- deleteDocument,
- addNewVersion,
- deleteAfterTimestamp,
- getSortedDocuments,
- clearAllDocuments,
- };
-};
diff --git a/src/features/documents/hooks/use-version-management.ts b/src/features/documents/hooks/use-version-management.ts
deleted file mode 100644
index 1d217a4d..00000000
--- a/src/features/documents/hooks/use-version-management.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-'use client';
-
-import { useCallback, useState } from 'react';
-import type { Document } from '@/features/documents/types';
-import { useLanguage } from '@/shared/hooks/use-language';
-import { useDocuments } from './use-documents';
-
-// Version management hook for document operations
-export const useVersionManagement = () => {
- const [isMutating, setIsMutating] = useState(false);
- const { deleteAfterTimestamp } = useDocuments();
- const { t } = useLanguage();
-
- const restoreToVersion = useCallback(
- async (
- documentId: string,
- documents: Document[],
- currentVersionIndex: number,
- onSuccess?: () => void,
- ) => {
- setIsMutating(true);
-
- try {
- const currentDocument = documents[currentVersionIndex];
- if (currentDocument) {
- // Update document to the selected version's content
- await deleteAfterTimestamp(documentId, {
- content: currentDocument.content ?? '',
- timestamp: currentDocument.createdAt,
- });
-
- // Call success callback
- onSuccess?.();
- }
- } catch (error) {
- console.error(t('version.failedRestore'), error);
- throw error;
- } finally {
- setIsMutating(false);
- }
- },
- [deleteAfterTimestamp, t],
- );
-
- return {
- isMutating,
- restoreToVersion,
- };
-};
diff --git a/src/features/documents/stores/index.ts b/src/features/documents/stores/index.ts
deleted file mode 100644
index 6e44c4f8..00000000
--- a/src/features/documents/stores/index.ts
+++ /dev/null
@@ -1,383 +0,0 @@
-// document-store.ts
-// Store for managing documents, suggestions, and artifacts with unified storage
-import { create } from 'zustand';
-import { persist } from 'zustand/middleware';
-import { NuwaIdentityKit } from '@/features/auth/services';
-import { generateUUID } from '@/shared/utils';
-import { createPersistConfig, db } from '@/storage';
-import type { CurrentDocumentProps, Document } from '../types';
-
-// Re-export types for convenience
-export type { CurrentDocumentProps, Document } from '../types';
-
-// ================= Constants ================= //
-
-// Empty document
-export const defaultEmptyDocument: CurrentDocumentProps = {
- documentId: 'init',
- content: '',
- kind: 'text',
- title: '',
- status: 'idle',
-};
-
-// get current DID
-const getCurrentDID = async () => {
- const { getDid } = await NuwaIdentityKit();
- return await getDid();
-};
-
-// ================= Database Reference ================= //
-
-const documentDB = db;
-
-// ================= Store State Interface ================= //
-
-interface DocumentStoreState {
- documents: Record; // all documents that are loaded into the store
- currentDocument: CurrentDocumentProps; // the current document that is being viewed
- currentDocumentMetadata: Record; // metadata for the current document
-
- // Document management
- createDocument: (title: string, kind: Document['kind']) => string;
- createDocumentWithId: (
- id: string,
- title: string,
- kind: Document['kind'],
- content?: string,
- ) => void;
- addNewVersionDocument: (id: string, content: string) => void;
- getDocument: (id: string) => Document | null;
- getDocuments: (id: string) => Document[];
- updateDocument: (
- id: string,
- updates: Partial>,
- ) => void;
- deleteDocument: (id: string) => void;
- deleteDocumentAfterTimestamp: (
- id: string,
- updates: { content: string; timestamp: number },
- ) => void;
- setDocumentContent: (id: string, content: string) => void;
-
- // Current document management
- setCurrentDocument: (
- updaterFn:
- | CurrentDocumentProps
- | ((currentDocument: CurrentDocumentProps) => CurrentDocumentProps),
- ) => void;
- UpdateCurrentDocument: (updates: Partial) => void;
- setCurrentDocumentMetadata: (metadata: any) => void;
- getCurrentDocumentMetadata: () => any;
- resetCurrentDocument: () => void;
-
- // Utility methods
- getSortedDocuments: () => Document[];
- clearAllDocuments: () => void;
-
- // Data persistence
- loadFromDB: () => Promise;
- saveToDB: () => Promise;
-}
-
-// ================= Persist Configuration ================= //
-
-const persistConfig = createPersistConfig({
- name: 'document-storage',
- getCurrentDID: getCurrentDID,
- partialize: (state) => ({
- documents: state.documents,
- currentDocument: state.currentDocument,
- currentDocumentMetadata: state.currentDocumentMetadata,
- }),
- onRehydrateStorage: () => (state?: DocumentStoreState) => {
- if (state) {
- state.loadFromDB();
- }
- },
-});
-
-// ================= Store Definition ================= //
-
-export const DocumentStateStore = create()(
- persist(
- (set, get) => ({
- // Store state
- documents: {},
- currentDocument: defaultEmptyDocument,
- currentDocumentMetadata: {},
-
- // Document creation and management
- createDocument: (title: string, kind: Document['kind']) => {
- const id = generateUUID();
- const now = Date.now();
-
- const newDocument: Document = {
- id,
- title,
- content: null,
- kind,
- createdAt: now,
- updatedAt: now,
- };
-
- set((state) => ({
- documents: {
- ...state.documents,
- [id]: newDocument,
- },
- }));
-
- // save to IndexedDB asynchronously
- get().saveToDB();
- return id;
- },
-
- createDocumentWithId: (
- id: string,
- title: string,
- kind: Document['kind'],
- content?: string,
- ) => {
- const now = Date.now();
-
- const newDocument: Document = {
- id,
- title,
- content: content || null,
- kind,
- createdAt: now,
- updatedAt: now,
- };
-
- set((state) => ({
- documents: {
- ...state.documents,
- [id]: newDocument,
- },
- }));
-
- // save to IndexedDB asynchronously
- get().saveToDB();
- },
-
- addNewVersionDocument: (id: string, content: string) => {
- const document = get().getDocument(id);
- if (!document) return;
- const tableId = generateUUID();
-
- set((state) => ({
- documents: {
- ...state.documents,
- [tableId]: {
- ...document,
- content,
- createdAt: Date.now(),
- updatedAt: Date.now(),
- },
- },
- }));
- },
-
- getDocument: (id: string) => {
- const { documents } = get();
- return documents[id] || null;
- },
-
- getDocuments: (id: string) => {
- const { documents } = get();
- return Object.values(documents).filter(
- (document) => document.id === id,
- );
- },
-
- updateDocument: (
- id: string,
- updates: Partial>,
- ) => {
- set((state) => {
- const document = state.documents[id];
- if (!document) return state;
-
- const updatedDocument = {
- ...document,
- ...updates,
- updatedAt: Date.now(),
- };
-
- return {
- documents: {
- ...state.documents,
- [id]: updatedDocument,
- },
- };
- });
-
- get().saveToDB();
- },
-
- deleteDocumentAfterTimestamp: async (
- id: string,
- updates: { content: string; timestamp: number },
- ) => {
- set((state) => {
- const documents = state.documents;
- const newDocuments = Object.fromEntries(
- Object.entries(documents).filter(
- ([_, document]) =>
- document.id !== id || document.createdAt <= updates.timestamp,
- ),
- );
- newDocuments[id] = {
- ...state.documents[id],
- content: updates.content,
- updatedAt: Date.now(),
- };
-
- return {
- ...state,
- documents: newDocuments,
- };
- });
-
- get().saveToDB();
- },
-
- deleteDocument: (id: string) => {
- set((state) => {
- const { [id]: deleted, ...restDocuments } = state.documents;
-
- return {
- documents: restDocuments,
- };
- });
-
- // delete related data asynchronously
- const deleteFromDB = async () => {
- try {
- await documentDB.documents.delete(id);
- } catch (error) {
- console.error('Failed to delete from DB:', error);
- }
- };
- deleteFromDB();
- },
-
- setDocumentContent: (id: string, content: string) => {
- get().updateDocument(id, { content });
- },
-
- // Artifact management methods (merged from use-artifact.ts)
- setCurrentDocument: (
- updaterFn:
- | CurrentDocumentProps
- | ((currentDocument: CurrentDocumentProps) => CurrentDocumentProps),
- ) => {
- set((state) => {
- const newArtifact =
- typeof updaterFn === 'function'
- ? updaterFn(state.currentDocument)
- : updaterFn;
-
- return {
- currentDocument: newArtifact,
- };
- });
- },
-
- UpdateCurrentDocument: (updates: Partial) => {
- set((state) => ({
- currentDocument: {
- ...state.currentDocument,
- ...updates,
- },
- }));
- },
-
- setCurrentDocumentMetadata: (metadata: any) => {
- set((state) => ({
- currentDocumentMetadata: {
- ...state.currentDocumentMetadata,
- [state.currentDocument.documentId]: metadata,
- },
- }));
- },
-
- getCurrentDocumentMetadata: () => {
- const { currentDocumentMetadata, currentDocument } = get();
- return currentDocumentMetadata[currentDocument.documentId] || null;
- },
-
- resetCurrentDocument: () => {
- set({
- currentDocument: defaultEmptyDocument,
- });
- },
-
- getSortedDocuments: () => {
- const { documents } = get();
- return Object.values(documents).sort(
- (a, b) => b.updatedAt - a.updatedAt,
- );
- },
-
- clearAllDocuments: () => {
- set({
- documents: {},
- });
-
- // clear IndexedDB
- const clearDB = async () => {
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
-
- await documentDB.documents.where('did').equals(currentDID).delete();
- } catch (error) {
- console.error('Failed to clear documents from DB:', error);
- }
- };
- clearDB();
- },
-
- loadFromDB: async () => {
- if (typeof window === 'undefined') return;
-
- try {
- const currentDID = await getCurrentDID();
- if (!currentDID) return;
-
- const documents = await documentDB.documents
- .where('did')
- .equals(currentDID)
- .toArray();
-
- const documentsMap: Record = {};
-
- documents.forEach((doc: Document) => {
- documentsMap[doc.id] = doc;
- });
-
- set((state) => ({
- documents: { ...state.documents, ...documentsMap },
- }));
- } catch (error) {
- console.error('Failed to load from DB:', error);
- }
- },
-
- saveToDB: async () => {
- if (typeof window === 'undefined') return;
-
- try {
- const { documents } = get();
- const documentsToSave = Object.values(documents);
- await documentDB.documents.bulkPut(documentsToSave);
- } catch (error) {
- console.error('Failed to save to DB:', error);
- }
- },
- }),
- persistConfig,
- ),
-);
diff --git a/src/features/documents/types/index.ts b/src/features/documents/types/index.ts
deleted file mode 100644
index 16c8b68d..00000000
--- a/src/features/documents/types/index.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// Document interface
-export interface Document {
- id: string;
- did?: string;
- title: string;
- content: string | null;
- kind: 'text' | 'code' | 'image' | 'sheet';
- createdAt: number;
- updatedAt: number;
-}
-
-export interface CurrentDocumentProps {
- documentId: string;
- content: string;
- kind: 'text' | 'code' | 'image' | 'sheet';
- title: string;
- status: 'streaming' | 'idle' | 'loading' | 'error' | 'success';
-}
diff --git a/src/features/mcp/components/McpDebugPanel.tsx b/src/features/mcp/components/McpDebugPanel.tsx
deleted file mode 100644
index a8a9c490..00000000
--- a/src/features/mcp/components/McpDebugPanel.tsx
+++ /dev/null
@@ -1,379 +0,0 @@
-import Form from '@rjsf/core';
-import validator from '@rjsf/validator-ajv8';
-import { useState } from 'react';
-import {
- closeNuwaMCPClient,
- createNuwaMCPClient,
- type McpTransportType,
- type NuwaMCPClient,
-} from '@/features/mcp';
-
-interface LogEntry {
- type: 'info' | 'error';
- message: string;
-}
-
-// Simple UUID helper compatible with browser & Node
-const generateUUID = (): string => {
- if (
- typeof globalThis !== 'undefined' &&
- (globalThis as any).crypto?.randomUUID
- ) {
- return (globalThis as any).crypto.randomUUID();
- }
- // Fallback: timestamp + random chars
- return Date.now().toString(36) + Math.random().toString(36).slice(2);
-};
-
-export default function McpDebugPanel() {
- const [url, setUrl] = useState('http://localhost:8080/mcp');
- const [transport, setTransport] = useState('');
- const [connected, setConnected] = useState(false);
- const [tools, setTools] = useState([]);
- const [toolsMap, setToolsMap] = useState>({});
- const [selectedTool, setSelectedTool] = useState(null);
- const [formData, setFormData] = useState({});
- const [logs, setLogs] = useState([]);
- const [prompts, setPrompts] = useState([]);
- const [promptsMap, setPromptsMap] = useState>({});
- const [resources, setResources] = useState([]);
- const [client, setClient] = useState(null);
-
- const pushLog = (entry: LogEntry) => setLogs((prev) => [...prev, entry]);
-
- const safeStringify = (obj: any): string => {
- try {
- return JSON.stringify(obj, null, 2);
- } catch (_) {
- return String(obj);
- }
- };
-
- const handleConnect = async () => {
- try {
- pushLog({ type: 'info', message: `Connecting to ${url} ...` });
- const newClient = await createNuwaMCPClient(
- url,
- transport === '' ? undefined : (transport as McpTransportType),
- );
- setClient(newClient);
- setConnected(true);
- pushLog({ type: 'info', message: 'Connected.' });
-
- // Get tools from client
- try {
- const list = await newClient.tools();
- const names = Object.keys(list);
- setTools(names);
- setToolsMap(list);
- pushLog({
- type: 'info',
- message: `Fetched tools: ${names.join(', ')}`,
- });
- } catch (err) {
- pushLog({
- type: 'info',
- message: `No tools available: ${String(err)}`,
- });
- }
-
- // fetch prompts
- try {
- const ps = await newClient.prompts();
- setPrompts(Object.keys(ps));
- setPromptsMap(ps);
- pushLog({
- type: 'info',
- message: `Fetched prompts: ${Object.keys(ps).join(', ')}`,
- });
- } catch (err) {
- pushLog({
- type: 'error',
- message: `Failed to fetch prompts: ${String(err)}`,
- });
- }
-
- // fetch resources
- try {
- const rs = await newClient.resources();
- const resourceKeys = Object.keys(rs);
- setResources(resourceKeys);
- pushLog({
- type: 'info',
- message: `Fetched resources: ${resourceKeys.join(', ')}`,
- });
- } catch (err) {
- pushLog({
- type: 'error',
- message: `Failed to fetch resources: ${String(err)}`,
- });
- }
- } catch (err) {
- pushLog({ type: 'error', message: String(err) });
- // close the client to clear the cache
- await closeNuwaMCPClient(url);
- }
- };
-
- const handlePing = async () => {
- if (!client) {
- pushLog({ type: 'error', message: 'Not connected' });
- return;
- }
-
- try {
- // Try ping on raw client if available
- if (client.raw && typeof client.raw.ping === 'function') {
- await client.raw.ping();
- pushLog({ type: 'info', message: 'Ping OK' });
- } else {
- // Fallback: try a simple prompts() call as a health check
- await client.prompts();
- pushLog({
- type: 'info',
- message: 'Health check OK (via prompts call)',
- });
- }
- } catch (err) {
- pushLog({ type: 'error', message: `Ping failed: ${String(err)}` });
- }
- };
-
- const handleDisconnect = async () => {
- if (!client) return;
-
- try {
- await client.close();
- setClient(null);
- setConnected(false);
- pushLog({ type: 'info', message: 'Disconnected.' });
- } catch (err) {
- pushLog({ type: 'error', message: `Disconnect error: ${String(err)}` });
- }
- };
-
- const handleExecute = async (payload: any) => {
- // When called from