diff --git a/.claude/rules/00-project-vision.md b/.claude/rules/00-project-vision.md deleted file mode 100644 index 1e4c86fae..000000000 --- a/.claude/rules/00-project-vision.md +++ /dev/null @@ -1,43 +0,0 @@ -# Project Vision - Crypto Community Platform - -## Core Concept - -A **cross-platform crypto-focused communication platform** built with Tauri (React + Rust) targeting the cryptocurrency ecosystem. - -## Target Audience - -### Primary Users - -- **Traders** - Day traders, swing traders, algorithmic traders -- **Yield Farmers** - DeFi protocol users, liquidity providers -- **Investors** - Long-term holders, institutional investors, VCs -- **Researchers** - Crypto analysts, on-chain researchers, data scientists -- **KOLs (Key Opinion Leaders)** - Influencers, educators, thought leaders -- **Developers** - Protocol developers, dApp builders -- **Community Members** - General crypto enthusiasts, DAO participants - -## Platform Approach - -**Inspiration**: poke.com - but specialized for crypto community needs -**Architecture**: Cross-platform (Windows, macOS, Android, iOS) -**Technology**: Tauri v2 (React frontend + Rust backend) - -## Key Differentiators - -- **Crypto-native features** built into the core platform -- **Privacy-first** approach for sensitive trading/financial discussions -- **Cross-platform** synchronization for mobile and desktop workflows -- **Community-focused** rather than just messaging -- **Specialized tools** for crypto use cases - -## Technical Foundation - -- **Frontend**: React 19 + TypeScript -- **Backend**: Rust with Tauri 2.x -- **Platforms**: Desktop (Windows, macOS) + Mobile (Android, iOS) -- **Data**: Local-first with selective cloud sync -- **Privacy**: End-to-end encryption for sensitive communications - ---- - -_Updated: 2026-01-27 - Initial vision documentation_ diff --git a/.claude/rules/01-project-overview.md b/.claude/rules/01-project-overview.md deleted file mode 100644 index c8c066855..000000000 --- a/.claude/rules/01-project-overview.md +++ /dev/null @@ -1,97 +0,0 @@ -# Project Overview - -## Crypto Community Platform - -This project is a **crypto-focused communication platform** built with Tauri v2, designed to serve the cryptocurrency ecosystem across multiple platforms: - -- **Windows** (Desktop) -- **macOS** (Desktop) -- **Android** (Mobile) -- **iOS** (Mobile) - -## Target Users - -- **Crypto Professionals**: Traders, Yield Farmers, Investors -- **Researchers & Analysts**: KOLs, Developers, Researchers -- **Teams & Organizations**: Collaborative workspaces for crypto projects -- **General Community**: Crypto enthusiasts and community members - -## Technology Stack - -| Layer | Technology | Version | Purpose | -| --------------------- | --------------- | ------- | ------------------------ | -| **Frontend Core** | -| UI Framework | React | 19.1.0 | Component-based UI | -| Language | TypeScript | 5.8.3 | Type safety | -| Build Tool | Vite | 7.0.4 | Fast development | -| **UI & Styling** | -| Styling | Tailwind CSS | Latest | Utility-first CSS | -| Components | Headless UI | Latest | Accessible components | -| Animation | Framer Motion | Latest | Smooth animations | -| **State & Data** | -| State Management | Redux Toolkit | Latest | Predictable state mgmt | -| State Persistence | Redux Persist | Latest | State rehydration | -| Data Fetching | TanStack Query | Latest | Server state management | -| Form Handling | React Hook Form | Latest | Form validation | -| **AI & Intelligence** | -| AI Memory | Custom System | Latest | Context & learning | -| Entity Graph | Neo4j | Latest | Knowledge relationships | -| Embeddings | OpenAI | Latest | Semantic search | -| **Communication** | -| Real-time | Socket.io | Latest | Live messaging | -| Telegram Integration | MTProto | Latest | Deep Telegram access | -| MCP Protocol | JSON-RPC 2.0 | Latest | AI tool execution | -| **Team & Skills** | -| Skills Platform | GitHub Sync | Latest | Dynamic skill loading | -| Team Management | REST API | Latest | Multi-user collaboration | -| **Backend Core** | -| Language | Rust | 1.93.0 | Performance & safety | -| Framework | Tauri | 2.x | Cross-platform apps | -| **Backend Libraries** | -| Async Runtime | Tokio | Latest | Async operations | -| JSON Handling | Serde JSON | Latest | Serialization | -| Database | SQLx + SQLite | Latest | Local storage | -| HTTP Client | Reqwest | Latest | API requests | -| WebSocket | WebSocket crate | Latest | Real-time messaging | -| Utilities | UUID | Latest | Unique identifiers | - -## Project Structure - -``` -frontend-runner-openhuman/ -├── .claude/ # Claude AI configuration -│ ├── rules/ # Modular documentation -│ └── agents/ # Subagent configurations -├── src/ # React frontend source -│ ├── lib/ # Core libraries -│ │ ├── ai/ # AI system (memory, constitution, entities) -│ │ └── mcp/ # Model Context Protocol implementation -│ ├── components/ # React components -│ │ └── settings/ # Settings modal system -│ ├── store/ # Redux state management -│ ├── services/ # API clients and services -│ └── pages/ # Application pages -├── src-tauri/ # Rust backend source -│ ├── gen/ # Generated platform code -│ │ ├── android/ # Android project -│ │ └── apple/ # iOS/macOS project -│ ├── icons/ # Application icons -│ └── src/ # Rust source code -├── skills/ # Skills submodule (GitHub synced) -├── public/ # Static assets -│ └── lottie/ # Animation files -├── .github/workflows/ # CI/CD pipelines -└── dist/ # Build output -``` - -## Key Configuration Files - -- `tauri.conf.json` - Tauri configuration (app identifier: com.openhuman.app) -- `Cargo.toml` - Rust dependencies and workspace configuration -- `package.json` - Node.js dependencies and scripts -- `vite.config.ts` - Vite build configuration with Node.js polyfills -- `tsconfig.json` - TypeScript configuration with strict settings -- `eslint.config.js` - ESLint configuration with ES modules -- `.prettierrc` - Code formatting rules -- `.husky/` - Git hooks for pre-commit/pre-push quality checks -- `CLAUDE.md` - Main project documentation for Claude Code diff --git a/.claude/rules/02-development-commands.md b/.claude/rules/02-development-commands.md deleted file mode 100644 index 0afea20c3..000000000 --- a/.claude/rules/02-development-commands.md +++ /dev/null @@ -1,187 +0,0 @@ -# Development Commands - -## Prerequisites - -Before running any commands, ensure you have: - -1. **Node.js** (v22+) and npm installed -2. **Rust** installed via rustup -3. **Platform-specific SDKs** (see platform setup guides) - -## Common Commands - -### Desktop Development - -```bash -# Frontend dev server only (port 1420) -yarn dev - -# Desktop dev with hot-reload (starts Vite + Tauri) -yarn tauri dev - -# Production build (TypeScript compile + Vite build + Tauri bundle) -yarn tauri build - -# Debug build with .app bundle (required for deep link testing on macOS) -yarn tauri build --debug --bundles app -yarn macos:dev -``` - -### Android Development - -```bash -# Initialize Android project (first time) -yarn tauri android init - -# Start Android development -yarn tauri android dev - -# Build Android APK/AAB -yarn tauri android build -``` - -### iOS Development - -```bash -# Initialize iOS project (first time) -yarn tauri ios init - -# Start iOS development -yarn tauri ios dev - -# Build iOS app -yarn tauri ios build -``` - -### Frontend Only - -```bash -# Install dependencies -yarn install - -# Start Vite dev server only -yarn dev - -# Build frontend only -yarn build - -# Preview production build -yarn preview -``` - -### Code Quality - -```bash -# Run linting and formatting checks -yarn lint -yarn format - -# TypeScript compilation check -yarn typecheck - -# Rust checks -cargo check --manifest-path src-tauri/Cargo.toml -cargo clippy --manifest-path src-tauri/Cargo.toml -``` - -## Stopping Development Servers - -### Desktop (Tauri Dev) - -```bash -# In the terminal running `yarn tauri dev`: -Ctrl + C - -# If process is stuck, force kill: -# macOS/Linux: -pkill -f "tauri dev" -pkill -f "cargo-tauri" -lsof -ti:1420 | xargs kill -9 # Kill process on Vite port - -# Windows (PowerShell): -Stop-Process -Name "tauri-app" -Force -Get-Process | Where-Object {$_.ProcessName -like "*tauri*"} | Stop-Process -Force -netstat -ano | findstr :1420 # Find PID on port 1420 -taskkill /PID /F # Kill by PID -``` - -### Android Dev Server - -```bash -# In the terminal running `yarn tauri android dev`: -Ctrl + C - -# If emulator/device is stuck: -adb kill-server # Stop ADB server -adb start-server # Restart ADB server - -# Force stop app on device: -adb shell am force-stop com.openhuman.app - -# Kill Gradle daemon if stuck: -# macOS/Linux: -pkill -f "gradle" -./gradlew --stop # From src-tauri/gen/android/ - -# Windows: -taskkill /F /IM java.exe # Kills Gradle processes -``` - -### iOS Dev Server - -```bash -# In the terminal running `yarn tauri ios dev`: -Ctrl + C - -# If simulator is stuck: -xcrun simctl shutdown all # Shutdown all simulators -xcrun simctl erase all # Reset all simulators (clears data) - -# Kill specific simulator: -xcrun simctl shutdown booted # Shutdown currently running simulator - -# Force kill Xcode processes: -pkill -f "Simulator" -pkill -f "xcodebuild" - -# If build process hangs: -pkill -f "cargo" -``` - -### Frontend Only (Vite) - -```bash -# In the terminal running `yarn dev`: -Ctrl + C - -# If port 1420 is still occupied: -# macOS/Linux: -lsof -ti:1420 | xargs kill -9 - -# Windows: -netstat -ano | findstr :1420 -taskkill /PID /F -``` - -### Kill All Development Processes - -```bash -# macOS/Linux - Nuclear option (kills all related processes): -pkill -f "tauri" -pkill -f "vite" -pkill -f "cargo" -pkill -f "node.*tauri" - -# Windows (PowerShell): -Get-Process | Where-Object {$_.ProcessName -match "tauri|vite|cargo|node"} | Stop-Process -Force -``` - -## Build Targets - -| Platform | Command | Output | -| -------- | -------------------------- | ------------------- | -| Windows | `yarn tauri build` | `.msi`, `.exe` | -| macOS | `yarn tauri build` | `.dmg`, `.app` | -| Linux | `yarn tauri build` | `.deb`, `.AppImage` | -| Android | `yarn tauri android build` | `.apk`, `.aab` | -| iOS | `yarn tauri ios build` | `.ipa` | diff --git a/.claude/rules/03-platform-setup-windows.md b/.claude/rules/03-platform-setup-windows.md deleted file mode 100644 index 0da5a3b33..000000000 --- a/.claude/rules/03-platform-setup-windows.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -paths: - - "app/src-tauri/**" - - "src-tauri/**" ---- - -# Windows Platform Setup - -## Prerequisites - -### 1. Microsoft Visual Studio C++ Build Tools - -Download and install from: https://visualstudio.microsoft.com/visual-cpp-build-tools/ - -During installation, select: - -- "Desktop development with C++" -- Windows 10/11 SDK -- MSVC v143+ build tools - -### 2. WebView2 - -Windows 10 (1803+) and Windows 11 include WebView2 by default. - -For older systems, download from: https://developer.microsoft.com/microsoft-edge/webview2/ - -### 3. Rust - -Install via rustup: - -```powershell -winget install Rustlang.Rustup -``` - -Or download from: https://rustup.rs - -## Building for Windows - -```bash -# Build for current architecture -npm run tauri build - -# Build for specific target -npm run tauri build -- --target x86_64-pc-windows-msvc -npm run tauri build -- --target aarch64-pc-windows-msvc -``` - -## Output Files - -After building, find installers in: - -``` -src-tauri/target/release/bundle/ -├── msi/ -│ └── tauri-app_0.1.0_x64_en-US.msi -└── nsis/ - └── tauri-app_0.1.0_x64-setup.exe -``` - -## Troubleshooting - -### Missing Visual C++ Redistributable - -If users report missing DLLs, bundle the Visual C++ Redistributable or instruct users to install it. - -### WebView2 Issues - -For enterprise environments, WebView2 fixed version runtime can be bundled with the app. diff --git a/.claude/rules/04-platform-setup-macos.md b/.claude/rules/04-platform-setup-macos.md deleted file mode 100644 index cba7a2ed8..000000000 --- a/.claude/rules/04-platform-setup-macos.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -paths: - - "app/src-tauri/**" - - "src-tauri/**" ---- - -# macOS Platform Setup - -## Prerequisites - -### 1. Xcode - -Install from the Mac App Store or: - -```bash -xcode-select --install -``` - -### 2. Xcode Command Line Tools - -```bash -xcode-select --install -``` - -### 3. Rust - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` - -### 4. Additional Dependencies (via Homebrew) - -For iOS development: - -```bash -brew install xcodegen -brew install libimobiledevice -``` - -## Building for macOS - -```bash -# Build for current architecture -npm run tauri build - -# Build universal binary (Intel + Apple Silicon) -npm run tauri build -- --target universal-apple-darwin -``` - -## Output Files - -After building, find installers in: - -``` -src-tauri/target/release/bundle/ -├── macos/ -│ └── tauri-app.app -└── dmg/ - └── tauri-app_0.1.0_x64.dmg -``` - -## Code Signing - -### Development - -For local development, no code signing is required. - -### Distribution - -Set up code signing for App Store or notarization: - -1. Enroll in Apple Developer Program -2. Create signing certificates -3. Configure in `tauri.conf.json`: - -```json -{ "bundle": { "macOS": { "signingIdentity": "Developer ID Application: Your Name (TEAM_ID)" } } } -``` - -## Notarization - -For distribution outside the App Store: - -```bash -# Build and notarize -npm run tauri build -- --target universal-apple-darwin - -# Or use xcrun for manual notarization -xcrun notarytool submit ./path/to/app.dmg --apple-id "your@email.com" --team-id "TEAM_ID" -``` diff --git a/.claude/rules/05-platform-setup-android.md b/.claude/rules/05-platform-setup-android.md deleted file mode 100644 index e3c1bd2d8..000000000 --- a/.claude/rules/05-platform-setup-android.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -paths: - - "src-tauri/gen/android/**" - - "app/src-tauri/gen/android/**" ---- - -# Android Platform Setup - -## Prerequisites - -### 1. Android Studio - -Download and install from: https://developer.android.com/studio - -### 2. Android SDK - -After installing Android Studio: - -1. Open Android Studio -2. Go to **Settings > Languages & Frameworks > Android SDK** -3. Install: - - Android SDK Platform 34 (or latest) - - Android SDK Build-Tools - - Android SDK Platform-Tools - - NDK (Side by side) - -### 3. Environment Variables - -Add to your shell profile (`~/.zshrc` or `~/.bashrc`): - -```bash -export ANDROID_HOME="$HOME/Library/Android/sdk" -export NDK_HOME="$ANDROID_HOME/ndk/$(ls -1 $ANDROID_HOME/ndk | tail -n 1)" -export PATH="$PATH:$ANDROID_HOME/platform-tools" -export PATH="$PATH:$ANDROID_HOME/tools/bin" -``` - -### 4. Rust Android Targets - -```bash -rustup target add aarch64-linux-android -rustup target add armv7-linux-androideabi -rustup target add i686-linux-android -rustup target add x86_64-linux-android -``` - -## Initialize Android Project - -```bash -npm run tauri android init -``` - -This creates the Android project in `src-tauri/gen/android/`. - -## Development - -### Using Emulator - -1. Open Android Studio -2. Create AVD (Android Virtual Device) -3. Start the emulator -4. Run: `npm run tauri android dev` - -### Using Physical Device - -1. Enable Developer Options on device -2. Enable USB Debugging -3. Connect device via USB -4. Run: `npm run tauri android dev` - -## Building for Android - -```bash -# Debug build -npm run tauri android build -- --debug - -# Release build -npm run tauri android build -``` - -## Output Files - -``` -src-tauri/gen/android/app/build/outputs/ -├── apk/ -│ ├── debug/ -│ │ └── app-debug.apk -│ └── release/ -│ └── app-release-unsigned.apk -└── bundle/ - └── release/ - └── app-release.aab -``` - -## Signing for Release - -### Create Keystore - -```bash -keytool -genkey -v -keystore release.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000 -``` - -### Configure Signing - -Edit `src-tauri/gen/android/app/build.gradle.kts`: - -```kotlin -android { - signingConfigs { - create("release") { - storeFile = file("path/to/release.keystore") - storePassword = System.getenv("KEYSTORE_PASSWORD") - keyAlias = "my-key-alias" - keyPassword = System.getenv("KEY_PASSWORD") - } - } - buildTypes { - getByName("release") { - signingConfig = signingConfigs.getByName("release") - } - } -} -``` diff --git a/.claude/rules/06-platform-setup-ios.md b/.claude/rules/06-platform-setup-ios.md deleted file mode 100644 index 4f0116c4a..000000000 --- a/.claude/rules/06-platform-setup-ios.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -paths: - - "src-tauri/gen/apple/**" - - "app/src-tauri/gen/apple/**" ---- - -# iOS Platform Setup - -## Prerequisites - -### 1. macOS - -iOS development requires a Mac with macOS. - -### 2. Xcode - -Install from the Mac App Store (requires latest version for latest iOS SDKs). - -### 3. Xcode Command Line Tools - -```bash -xcode-select --install -``` - -### 4. Additional Tools - -```bash -brew install xcodegen -brew install libimobiledevice -brew install ios-deploy -``` - -### 5. CocoaPods - -```bash -sudo gem install cocoapods -``` - -### 6. Rust iOS Targets - -```bash -rustup target add aarch64-apple-ios -rustup target add aarch64-apple-ios-sim -rustup target add x86_64-apple-ios -``` - -## Initialize iOS Project - -```bash -npm run tauri ios init -``` - -This creates the iOS project in `src-tauri/gen/apple/`. - -## Apple Developer Account - -### For Development - -- Free Apple ID allows testing on your own devices -- Must register device UDID in Xcode - -### For Distribution - -- Requires paid Apple Developer Program ($99/year) -- Needed for App Store, TestFlight, or Ad Hoc distribution - -## Development Team Configuration - -Set in `tauri.conf.json`: - -```json -{ "bundle": { "iOS": { "developmentTeam": "YOUR_TEAM_ID" } } } -``` - -Or via environment variable: - -```bash -export APPLE_DEVELOPMENT_TEAM="YOUR_TEAM_ID" -``` - -## Development - -### Using Simulator - -```bash -# List available simulators -xcrun simctl list devices - -# Run on simulator -npm run tauri ios dev -``` - -### Using Physical Device - -1. Connect device via USB -2. Trust the computer on the device -3. Run: `npm run tauri ios dev -- --device` - -## Building for iOS - -```bash -# Debug build -npm run tauri ios build -- --debug - -# Release build -npm run tauri ios build -``` - -## Output Files - -``` -src-tauri/gen/apple/build/ -└── arm64/ - └── tauri-app.app -``` - -## Code Signing - -### Automatic Signing - -Xcode can manage signing automatically when configured with your Apple ID. - -### Manual Signing - -1. Create provisioning profile in Apple Developer Portal -2. Download and install in Xcode -3. Configure in Xcode project settings - -## App Store Submission - -1. Build release version -2. Archive in Xcode -3. Upload via Xcode Organizer or Transporter -4. Complete submission in App Store Connect diff --git a/.claude/rules/07-rust-backend-guide.md b/.claude/rules/07-rust-backend-guide.md deleted file mode 100644 index 997d6ac96..000000000 --- a/.claude/rules/07-rust-backend-guide.md +++ /dev/null @@ -1,174 +0,0 @@ ---- -paths: - - "app/src-tauri/**" - - "src-tauri/**" - - "src/**/*.rs" - - "**/Cargo.toml" ---- - -# Rust Backend Guide - -## Structure - -``` -src-tauri/ -├── Cargo.toml # Rust dependencies -├── build.rs # Build script -├── tauri.conf.json # Tauri configuration -├── capabilities/ # Permission configurations -├── icons/ # App icons -└── src/ - ├── lib.rs # Library crate (for mobile) - └── main.rs # Binary crate (for desktop) -``` - -## Creating Commands - -Commands allow the frontend to call Rust functions. - -### Basic Command - -```rust -// src-tauri/src/lib.rs - -#[tauri::command] -fn greet(name: &str) -> String { - format!("Hello, {}! You've been greeted from Rust!", name) -} - -pub fn run() { - tauri::Builder::default() - .plugin(tauri_plugin_opener::init()) - .invoke_handler(tauri::generate_handler![greet]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -### Async Command - -```rust -#[tauri::command] -async fn fetch_data(url: String) -> Result { - reqwest::get(&url) - .await - .map_err(|e| e.to_string())? - .text() - .await - .map_err(|e| e.to_string()) -} -``` - -### Command with State - -```rust -use std::sync::Mutex; -use tauri::State; - -struct AppState { - counter: Mutex, -} - -#[tauri::command] -fn increment(state: State) -> i32 { - let mut counter = state.counter.lock().unwrap(); - *counter += 1; - *counter -} - -pub fn run() { - tauri::Builder::default() - .manage(AppState { - counter: Mutex::new(0), - }) - .invoke_handler(tauri::generate_handler![increment]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -## Calling Commands from Frontend - -```typescript -import { invoke } from '@tauri-apps/api/core'; - -// Basic call -const greeting = await invoke('greet', { name: 'World' }); - -// With error handling -try { - const data = await invoke('fetch_data', { url: 'https://api.example.com' }); -} catch (error) { - console.error('Command failed:', error); -} -``` - -## Events - -### Emit from Rust - -```rust -use tauri::Emitter; - -#[tauri::command] -fn start_process(app: tauri::AppHandle) { - std::thread::spawn(move || { - // Do work... - app.emit("process-complete", "Done!").unwrap(); - }); -} -``` - -### Listen in Frontend - -```typescript -import { listen } from '@tauri-apps/api/event'; - -const unlisten = await listen('process-complete', event => { - console.log('Process completed:', event.payload); -}); - -// Later: unlisten(); -``` - -## Adding Dependencies - -Edit `src-tauri/Cargo.toml`: - -```toml -[dependencies] -tauri = { version = "2", features = [] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -reqwest = { version = "0.11", features = ["json"] } -tokio = { version = "1", features = ["full"] } -``` - -## Platform-Specific Code - -```rust -#[cfg(target_os = "windows")] -fn platform_specific() { - // Windows-only code -} - -#[cfg(target_os = "macos")] -fn platform_specific() { - // macOS-only code -} - -#[cfg(target_os = "linux")] -fn platform_specific() { - // Linux-only code -} - -#[cfg(target_os = "android")] -fn platform_specific() { - // Android-only code -} - -#[cfg(target_os = "ios")] -fn platform_specific() { - // iOS-only code -} -``` diff --git a/.claude/rules/08-frontend-guide.md b/.claude/rules/08-frontend-guide.md deleted file mode 100644 index 135fd5680..000000000 --- a/.claude/rules/08-frontend-guide.md +++ /dev/null @@ -1,450 +0,0 @@ ---- -paths: - - "app/src/**" - - "app/*.ts" - - "app/*.tsx" - - "app/vite.config.*" ---- - -# Frontend Development Guide - Crypto Community Platform - -## Overview - -Frontend development guide for crypto-focused communication platform using modern React ecosystem with Tauri. - -## ✅ CURRENT IMPLEMENTATION STATUS - -### Design System (FULLY IMPLEMENTED) - -- **Glass Morphism UI**: Enhanced frosted glass effects with 16px backdrop blur throughout interface -- **Crypto Price Ticker**: Animated scrolling ticker with BTC/ETH brand colors and JetBrains Mono font -- **Navigation System**: Complete nav bar with active states (Dashboard, Portfolio, Chat, Markets) -- **Chat Interface**: Full messaging system with sent/received bubble styles and crypto addresses -- **Button Variants**: All 4 types implemented (Primary, Secondary, Success, Danger) with hover states -- **Form Components**: Enhanced inputs with focus states, select dropdowns, crypto-specific placeholders -- **Status Indicators**: Online/Offline/Warning badges with proper sage/stone/amber colors -- **Loading States**: Animated pulse placeholders for async operations -- **Typography**: Inter + JetBrains Mono fonts with crypto-optimized hierarchy -- **Color System**: Premium crypto palette (canvas, primary, sage, amber, coral, stone, market colors) -- **Animations**: Smooth transitions, hover scales, ticker animation, fade-in effects -- **Responsive Design**: Mobile-first approach with proper breakpoints -- **Accessibility**: Focus rings, proper contrast, WCAG compliance ready - -## Current Project Structure (Updated) - -``` -src/ -├── App.tsx # Main app with HashRouter + provider chain -├── AppRoutes.tsx # Route definitions with protected/public routes -├── main.tsx # Entry point with desktop deep link handling -├── providers/ # Context providers -│ ├── SocketProvider.tsx # Socket.io real-time communication -│ ├── TelegramProvider.tsx # Telegram MTProto integration -│ └── UserProvider.tsx # User state management -├── store/ # Redux Toolkit state management -│ ├── index.ts # Store configuration with persist -│ ├── authSlice.ts # Authentication state -│ ├── socketSlice.ts # Socket connection state -│ ├── userSlice.ts # User profile state -│ ├── aiSlice.ts # AI system state -│ ├── skillsSlice.ts # Skills management state -│ ├── teamSlice.ts # Team collaboration state -│ └── telegram/ # Telegram state management -├── services/ # Service layer (singletons) -│ ├── apiClient.ts # HTTP REST client -│ ├── socketService.ts # Socket.io client -│ └── mtprotoService.ts # Telegram MTProto service -├── lib/ # Core libraries -│ ├── ai/ # AI system architecture -│ │ ├── memory/ # Memory management with encryption -│ │ ├── constitution/ # AI behavior rules -│ │ ├── entities/ # Entity graph management -│ │ ├── sessions/ # Session capture and transcripts -│ │ └── providers/ # AI provider abstractions -│ └── mcp/ # Model Context Protocol system -│ ├── transport.ts # Socket.io JSON-RPC transport -│ └── telegram/ # 99 Telegram MCP tools -├── pages/ # Route components -│ ├── Welcome.tsx # Landing page -│ ├── Login.tsx # Authentication -│ ├── Home.tsx # Main dashboard -│ └── onboarding/ # Multi-step onboarding -├── components/ # Reusable UI components -│ ├── TelegramLoginButton.tsx # OAuth login integration -│ ├── ProtectedRoute.tsx # Auth-gated routes -│ ├── PublicRoute.tsx # Guest-only routes -│ ├── ConnectionIndicator.tsx # Status indicators -│ ├── SkillsGrid.tsx # Skills catalog and management -│ ├── DownloadScreen.tsx # Multi-platform download system -│ └── settings/ # Settings modal system -│ ├── SettingsModal.tsx # Main container with routing -│ ├── SettingsLayout.tsx # Modal wrapper with portal -│ ├── SettingsHome.tsx # Main menu with profile -│ ├── panels/ # Settings panels -│ │ ├── ConnectionsPanel.tsx # Connection management -│ │ ├── TeamPanel.tsx # Team management -│ │ ├── TeamMembersPanel.tsx # Member roles & permissions -│ │ ├── TeamInvitesPanel.tsx # Invitation system -│ │ └── BillingPanel.tsx # Premium features and billing -│ ├── components/ # Menu items, header, back button -│ └── hooks/ # Navigation and animation hooks -└── utils/ # Utilities and config - ├── config.ts # Environment variables - └── desktopDeepLinkListener.ts # Deep link handling -``` - -### Recent Architecture Changes - -- **Code Quality Infrastructure**: ESLint, Prettier, and Husky hooks for automated quality checks - - Pre-commit/pre-push hooks with TypeScript compilation validation - - Type-only imports standardization and consolidated import statements - - GitHub workflow integration with quality gate checks -- **Advanced AI System**: Comprehensive artificial intelligence platform - - Memory management with encryption, chunking, and hybrid search capabilities - - Constitution-based AI behavior with GitHub integration for rule updates - - Entity graph migration to Neo4j backend for knowledge relationships - - Session capture, transcript management, and tool compression -- **Skills Management Platform**: Dynamic skill loading and management system - - SkillsGrid with setup status indicators and management controls - - GitHub sync integration for skills catalog updates - - Conditional rendering (setup wizard vs management panel) - - Skills submodule with background update synchronization -- **Team Collaboration Features**: Multi-user workspace management system - - TeamPanel, TeamMembersPanel, TeamInvitesPanel for comprehensive team management - - Role-based permissions and invitation system with Redux state management - - Team API integration with CRUD operations and member management -- **Enhanced Settings System**: Expanded modal system with multiple management panels - - Team management, billing, privacy, and advanced configuration panels - - Enhanced URL routing for deep-linking to specific settings sections - - Redux integration across auth, user, telegram, ai, skills, and team slices -- **200+ TypeScript files**: Comprehensive component and library ecosystem -- **Provider chain**: Redux → PersistGate → Socket → Telegram → AI → Skills → HashRouter → Routes -- **Enhanced CI/CD**: Python sidecar support, XGH_TOKEN authentication, GitHub Pages deployment - -## React with Tauri - -### Basic Component - -```tsx -import { invoke } from '@tauri-apps/api/core'; -import { useState } from 'react'; - -function App() { - const [result, setResult] = useState(''); - - async function handleClick() { - const greeting = await invoke('greet', { name: 'User' }); - setResult(greeting); - } - - return ( -
- -

{result}

-
- ); -} - -export default App; -``` - -## Tauri APIs - -### Window Management - -```typescript -import { getCurrentWindow } from '@tauri-apps/api/window'; - -const appWindow = getCurrentWindow(); - -// Minimize -await appWindow.minimize(); - -// Maximize -await appWindow.maximize(); - -// Close -await appWindow.close(); - -// Set title -await appWindow.setTitle('New Title'); -``` - -### File System - -First, add the plugin: - -```bash -npm run tauri add fs -``` - -```typescript -import { BaseDirectory, readTextFile, writeTextFile } from '@tauri-apps/plugin-fs'; - -// Read file -const content = await readTextFile('config.json', { baseDir: BaseDirectory.AppData }); - -// Write file -await writeTextFile('config.json', JSON.stringify(data), { baseDir: BaseDirectory.AppData }); -``` - -### Dialogs - -First, add the plugin: - -```bash -npm run tauri add dialog -``` - -```typescript -import { message, open, save } from '@tauri-apps/plugin-dialog'; - -// Open file picker -const filePath = await open({ - multiple: false, - filters: [{ name: 'Text', extensions: ['txt', 'md'] }], -}); - -// Save dialog -const savePath = await save({ defaultPath: 'document.txt' }); - -// Message box -await message('Operation completed!', { title: 'Success' }); -``` - -### HTTP Requests - -First, add the plugin: - -```bash -npm run tauri add http -``` - -```typescript -import { fetch } from '@tauri-apps/plugin-http'; - -const response = await fetch('https://api.example.com/data', { - method: 'GET', - headers: { 'Content-Type': 'application/json' }, -}); - -const data = await response.json(); -``` - -## Platform Detection - -```typescript -import { platform } from '@tauri-apps/plugin-os'; - -const currentPlatform = await platform(); - -switch (currentPlatform) { - case 'windows': - // Windows-specific UI - break; - case 'macos': - // macOS-specific UI - break; - case 'linux': - // Linux-specific UI - break; - case 'android': - // Android-specific UI - break; - case 'ios': - // iOS-specific UI - break; -} -``` - -## Responsive Design for Mobile - -```css -/* Base styles for mobile-first */ -.container { - padding: 16px; - font-size: 16px; -} - -/* Tablet and larger */ -@media (min-width: 768px) { - .container { - padding: 24px; - max-width: 720px; - margin: 0 auto; - } -} - -/* Desktop */ -@media (min-width: 1024px) { - .container { - max-width: 960px; - } -} - -/* Safe areas for notched devices (iOS) */ -.app { - padding-top: env(safe-area-inset-top); - padding-bottom: env(safe-area-inset-bottom); - padding-left: env(safe-area-inset-left); - padding-right: env(safe-area-inset-right); -} -``` - -## Recommended Tech Stack - -### UI & Styling - -- **Tailwind CSS** - Utility-first CSS framework -- **Headless UI** - Accessible, unstyled UI components -- **Framer Motion** - Animation library for React - -### State & Data Management (Current Implementation) - -- **Redux Toolkit** - Currently implemented state management with persistence -- **Redux Persist** - Persists auth and telegram state to localStorage -- **Socket.io** - Real-time communication via socketService singleton -- **MTProto Client** - Telegram integration via mtprotoService singleton -- **MCP System** - 99 AI tools for Telegram interactions over Socket.io transport - -## Current State Management (Redux Toolkit) - -```typescript -// Current implementation uses Redux Toolkit with these slices: -import { useAppSelector, useAppDispatch } from "../store/hooks"; - -// Auth state (persisted) -const authState = useAppSelector((state) => state.auth); -// { token: string | null, isOnboarded: boolean } - -// User state -const userState = useAppSelector((state) => state.user); -// { profile: UserProfile | null, loading: boolean, error: string | null } - -// Socket state -const socketState = useAppSelector((state) => state.socket); -// { isConnected: boolean, socketId: string | null } - -// Telegram state (selectively persisted) -const telegramState = useAppSelector((state) => state.telegram); -// Complex nested state with chats, messages, threads, auth status - -// Usage in component -function ChatHeader() { - const { token } = useAppSelector((state) => state.auth); - const { profile } = useAppSelector((state) => state.user); - const { isConnected } = useAppSelector((state) => state.socket); - - return ( -
-

Chat

-
- {profile?.username} -
-
-
- ); -} -``` - -## Form Handling with React Hook Form - -```typescript -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; - -const messageSchema = z.object({ - content: z.string().min(1, "Message cannot be empty").max(1000), - channel: z.string().uuid(), -}); - -type MessageForm = z.infer; - -function MessageInput() { - const { - register, - handleSubmit, - reset, - formState: { errors }, - } = useForm({ - resolver: zodResolver(messageSchema), - }); - - const onSubmit = (data: MessageForm) => { - // Send message via Tauri IPC - invoke("send_message", data); - reset(); - }; - - return ( -
- - - {errors.content && ( -

{errors.content.message}

- )} -
- ); -} -``` - -## Data Fetching with TanStack Query - -```typescript -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; -import { invoke } from "@tauri-apps/api/core"; - -// Fetch channels -function useChannels() { - return useQuery({ - queryKey: ["channels"], - queryFn: () => invoke("get_channels"), - staleTime: 5 * 60 * 1000, // 5 minutes - }); -} - -// Send message mutation -function useSendMessage() { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: (message: NewMessage) => invoke("send_message", message), - onSuccess: () => { - // Invalidate and refetch messages - queryClient.invalidateQueries({ queryKey: ["messages"] }); - }, - }); -} - -// Usage in component -function ChannelList() { - const { data: channels, isLoading, error } = useChannels(); - - if (isLoading) return
Loading channels...
; - if (error) return
Error loading channels
; - - return ( -
- {channels?.map((channel) => ( -
- {channel.name} -
- ))} -
- ); -} -``` diff --git a/.claude/rules/09-permissions-capabilities.md b/.claude/rules/09-permissions-capabilities.md deleted file mode 100644 index 032a72077..000000000 --- a/.claude/rules/09-permissions-capabilities.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -paths: - - "**/capabilities/**" - - "**/tauri.conf.json" ---- - -# Permissions and Capabilities - -## Overview - -Tauri v2 uses a capability-based security model. Permissions must be explicitly granted for the frontend to access system resources. - -## Capability Files - -Located in `src-tauri/capabilities/`: - -``` -src-tauri/capabilities/ -├── default.json # Default permissions for all windows -└── mobile.json # Mobile-specific permissions -``` - -## Default Capability - -```json -{ - "$schema": "../gen/schemas/desktop-schema.json", - "identifier": "default", - "description": "Default permissions for the main window", - "windows": ["main"], - "permissions": ["core:default", "opener:default"] -} -``` - -## Adding Permissions - -### File System Access - -```json -{ - "permissions": [ - "fs:default", - "fs:allow-read-text-file", - "fs:allow-write-text-file", - { "identifier": "fs:scope", "allow": ["$APPDATA/*", "$DOCUMENT/*"] } - ] -} -``` - -### Dialog Access - -```json -{ - "permissions": [ - "dialog:default", - "dialog:allow-open", - "dialog:allow-save", - "dialog:allow-message" - ] -} -``` - -### HTTP Access - -```json -{ - "permissions": [ - "http:default", - { - "identifier": "http:scope", - "allow": [{ "url": "https://api.example.com/*" }, { "url": "https://*.myapp.com/*" }] - } - ] -} -``` - -### Notification Access - -```json -{ - "permissions": [ - "notification:default", - "notification:allow-is-permission-granted", - "notification:allow-request-permission", - "notification:allow-notify" - ] -} -``` - -## Mobile-Specific Capabilities - -Create `src-tauri/capabilities/mobile.json`: - -```json -{ - "$schema": "../gen/schemas/mobile-schema.json", - "identifier": "mobile", - "description": "Mobile-specific permissions", - "platforms": ["android", "iOS"], - "windows": ["main"], - "permissions": ["core:default", "barcode-scanner:default", "biometric:default", "haptics:default"] -} -``` - -## Available Permission Plugins - -| Plugin | Description | Install Command | -| ------------ | -------------------- | ------------------------------------- | -| fs | File system access | `npm run tauri add fs` | -| dialog | System dialogs | `npm run tauri add dialog` | -| http | HTTP requests | `npm run tauri add http` | -| notification | System notifications | `npm run tauri add notification` | -| clipboard | Clipboard access | `npm run tauri add clipboard-manager` | -| shell | Shell commands | `npm run tauri add shell` | -| store | Persistent storage | `npm run tauri add store` | -| os | OS information | `npm run tauri add os` | - -## Scope Paths - -Available path variables: - -| Variable | Description | -| --------------- | ---------------------------- | -| `$APPDATA` | Application data directory | -| `$APPCONFIG` | Application config directory | -| `$APPLOCALDATA` | Application local data | -| `$APPCACHE` | Application cache | -| `$APPLOG` | Application logs | -| `$AUDIO` | User's audio directory | -| `$CACHE` | System cache | -| `$CONFIG` | System config | -| `$DATA` | System data | -| `$DOCUMENT` | User's documents | -| `$DOWNLOAD` | User's downloads | -| `$PICTURE` | User's pictures | -| `$VIDEO` | User's videos | -| `$TEMP` | Temporary directory | - -## Best Practices - -1. **Principle of Least Privilege**: Only request permissions you need -2. **Scope Restrictions**: Limit file access to specific directories -3. **URL Whitelisting**: Only allow HTTP to known domains -4. **Platform Separation**: Use separate capability files for mobile/desktop -5. **Document Permissions**: Comment why each permission is needed diff --git a/.claude/rules/10-troubleshooting.md b/.claude/rules/10-troubleshooting.md deleted file mode 100644 index e0e902ed6..000000000 --- a/.claude/rules/10-troubleshooting.md +++ /dev/null @@ -1,167 +0,0 @@ -# Troubleshooting Guide - -## Common Issues - -### Build Errors - -#### "error: failed to run custom build command for `tauri`" - -**Cause**: Missing system dependencies - -**Solution**: - -- macOS: `xcode-select --install` -- Windows: Install Visual Studio Build Tools -- Linux: `sudo apt install libwebkit2gtk-4.1-dev build-essential libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev` - -#### "cargo: command not found" - -**Cause**: Rust not installed or not in PATH - -**Solution**: - -```bash -source "$HOME/.cargo/env" -# Or reinstall Rust -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` - -### Development Server Issues - -#### Frontend loads but shows blank page - -**Cause**: Dev server not running or wrong port - -**Solution**: - -1. Check `devUrl` in `tauri.conf.json` matches Vite port -2. Start frontend first: `npm run dev` -3. Then: `npm run tauri dev` - -#### Hot reload not working - -**Cause**: File watcher issues - -**Solution**: - -- macOS: Increase file descriptor limit -- Windows: Disable antivirus scanning on project folder -- All: Restart dev server - -### Android Issues - -#### "ANDROID_HOME not set" - -**Solution**: - -```bash -export ANDROID_HOME="$HOME/Library/Android/sdk" -export PATH="$PATH:$ANDROID_HOME/platform-tools" -``` - -Add to `~/.zshrc` or `~/.bashrc` for persistence. - -#### "No connected devices" - -**Solution**: - -1. Enable Developer Options on device -2. Enable USB Debugging -3. Accept RSA key prompt on device -4. Run: `adb devices` to verify connection - -#### "NDK not found" - -**Solution**: - -1. Open Android Studio -2. Settings > Languages & Frameworks > Android SDK -3. SDK Tools tab > Check "NDK (Side by side)" -4. Install latest NDK - -### iOS Issues - -#### "No code signing certificates found" - -**Solution**: - -1. Open Xcode -2. Preferences > Accounts > Add Apple ID -3. Let Xcode manage signing automatically -4. Or set `APPLE_DEVELOPMENT_TEAM` environment variable - -#### "Unable to install app on device" - -**Solution**: - -1. Device must be registered in your developer account -2. Create provisioning profile including the device -3. Trust developer in Settings > General > Device Management - -#### Simulator not launching - -**Solution**: - -```bash -# Reset simulator -xcrun simctl erase all - -# Or boot specific simulator -xcrun simctl boot "iPhone 15" -``` - -### Performance Issues - -#### App runs slowly - -**Solutions**: - -1. Enable release mode: `npm run tauri build` -2. Check for unnecessary re-renders in React -3. Profile Rust code with `cargo flamegraph` - -#### Large bundle size - -**Solutions**: - -1. Enable stripping in `Cargo.toml`: - ```toml - [profile.release] - strip = true - lto = true - ``` -2. Analyze frontend bundle: `npm run build -- --analyze` -3. Remove unused dependencies - -### Plugin Issues - -#### "Plugin not initialized" - -**Cause**: Plugin not added to Tauri builder - -**Solution**: -Check `src-tauri/src/lib.rs`: - -```rust -tauri::Builder::default() - .plugin(tauri_plugin_fs::init()) // Add plugin here - .plugin(tauri_plugin_dialog::init()) -``` - -#### "Permission denied" for plugin - -**Cause**: Missing capability - -**Solution**: -Add permission to `src-tauri/capabilities/default.json`: - -```json -{ "permissions": ["fs:default", "fs:allow-read-text-file"] } -``` - -## Getting Help - -1. **Tauri Discord**: https://discord.gg/tauri -2. **GitHub Issues**: https://github.com/tauri-apps/tauri/issues -3. **Documentation**: https://tauri.app/ -4. **Stack Overflow**: Tag with `tauri` diff --git a/.claude/rules/11-tech-stack-detailed.md b/.claude/rules/11-tech-stack-detailed.md deleted file mode 100644 index faeb2b0db..000000000 --- a/.claude/rules/11-tech-stack-detailed.md +++ /dev/null @@ -1,169 +0,0 @@ -# Detailed Tech Stack Documentation - -## Frontend Technologies - -### Core Framework - -- **React 19.1.0** - - Latest React with concurrent features - - Component-based architecture - - Built-in state management primitives - -- **TypeScript 5.8.3** - - Type safety for large applications - - Enhanced developer experience - - Better refactoring support - -- **Vite 7.0.4** - - Fast development server - - Hot module replacement - - Optimized builds - -### UI & Styling - -- **Tailwind CSS** - - Utility-first CSS framework - - Rapid prototyping - - Consistent design system - - Mobile-first responsive design - -- **Headless UI** - - Accessible UI components - - Unstyled, flexible components - - Keyboard navigation support - - Screen reader compatibility - -- **Framer Motion** - - Smooth animations and transitions - - Gesture support - - Layout animations - - Performance optimized - -### State Management & Data - -- **Zustand** - - Lightweight state management - - TypeScript friendly - - No boilerplate - - DevTools support - -- **TanStack Query (React Query)** - - Server state management - - Caching and synchronization - - Background updates - - Optimistic updates - -- **React Hook Form** - - Performant form handling - - Minimal re-renders - - Built-in validation - - Easy integration with UI libraries - -## Backend Technologies - -### Core Runtime - -- **Rust 1.93.0** - - Memory safety without garbage collection - - Zero-cost abstractions - - Excellent performance - - Cross-platform compilation - -- **Tauri 2.x** - - Cross-platform desktop/mobile apps - - Small bundle size - - Native OS integration - - Secure IPC communication - -### Essential Crates - -- **Tokio** - - Async runtime for Rust - - High-performance networking - - Task scheduling - - Real-time capabilities - -- **Serde + Serde JSON** - - Serialization/deserialization - - Type-safe JSON handling - - Performance optimized - - Extensive ecosystem support - -- **SQLx + SQLite** - - Compile-time checked SQL - - Async database operations - - Local-first storage - - Cross-platform compatibility - -- **Reqwest** - - HTTP client library - - Async/await support - - JSON support built-in - - Connection pooling - -- **WebSocket Libraries** - - Real-time communication - - Bidirectional messaging - - Connection management - - Error handling - -- **UUID** - - Unique identifier generation - - Various UUID versions - - Cryptographically secure - - Cross-platform consistency - -## Platform Targets - -### Desktop - -- **Windows** (x64, ARM64) -- **macOS** (Intel, Apple Silicon) -- **Linux** (x64, ARM64) - Optional - -### Mobile - -- **Android** (API 21+) -- **iOS** (iOS 13+) - -## Development Tools - -### Build & Development - -- **Cargo** - Rust package manager -- **NPM** - Node.js package manager -- **Tauri CLI** - Build and development tools - -### Code Quality - -- **ESLint** - JavaScript/TypeScript linting -- **Prettier** - Code formatting -- **Clippy** - Rust linting -- **Rustfmt** - Rust code formatting - -## Architecture Principles - -### Frontend - -- **Component-driven development** -- **Type-safe state management** -- **Responsive design first** -- **Accessible by default** - -### Backend - -- **Async-first architecture** -- **Local-first data storage** -- **Secure communication** -- **Performance optimized** - -### Cross-Platform - -- **Native OS integration** -- **Consistent user experience** -- **Platform-specific optimizations** -- **Shared business logic** - ---- - -_Last updated: 2026-01-27_ diff --git a/.claude/rules/12-design-system.md b/.claude/rules/12-design-system.md deleted file mode 100644 index f9a1b6d14..000000000 --- a/.claude/rules/12-design-system.md +++ /dev/null @@ -1,276 +0,0 @@ ---- -paths: - - "app/src/**/*.tsx" - - "app/src/**/*.css" - - "**/tailwind.config.*" ---- - -# Design System - Crypto Community Platform - -## Design Philosophy - -Our design system is built on **trust**, **usefulness**, and **simplicity** - core principles essential for crypto/fintech applications where users handle sensitive financial discussions and data. - -## Typography - -### Font Selection - Psychology of Trust - -**Primary Font: Inter** - -- **Why**: Sans-serif font that signals modernity, efficiency, and clarity -- **Psychology**: Clean, digital-first appearance builds trust in tech platforms -- **Usage**: Body text, UI elements, navigation -- **Weights**: 300, 400, 500, 600, 700 - -**Monospace Font: JetBrains Mono** - -- **Why**: Essential for crypto addresses, transaction hashes, code snippets -- **Psychology**: Monospace fonts convey technical precision and accuracy -- **Usage**: Crypto addresses, prices, technical data, code blocks -- **Weights**: 300, 400, 500, 600 - -### Font Hierarchy - -```css -h1: text-3xl lg:text-4xl (48px desktop, 30px mobile) -h2: text-2xl lg:text-3xl (36px desktop, 24px mobile) -h3: text-xl lg:text-2xl (24px desktop, 20px mobile) -h4: text-lg lg:text-xl (20px desktop, 18px mobile) -Body: text-base (16px) - optimal for readability -Small: text-sm (14px) - secondary information -``` - -## Color Palette - Trust & Professionalism - -### Primary Colors - Ocean Blue (Updated) - -- **Primary-500**: `#4A83DD` - Main brand color optimized for dark backgrounds -- **Primary-600**: `#3D6DC4` - Interactive hover states -- **Primary-700**: `#345A9F` - Active states, emphasis - -**Psychology**: Deep ocean blue builds trust and sophistication for crypto platforms. - -### Success Colors - Sage Green (Updated) - -- **Success-500**: `#4DC46F` - Refined success green for growth indicators -- **Success-600**: `#3BA858` - Interactive success states - -**Psychology**: Sophisticated sage green represents growth and financial success. - -### Warning & Error Colors (Updated) - -- **Warning-500**: `#E8A838` - Sophisticated amber for attention states -- **Error-500**: `#F56565` - Soft coral red for professional error handling - -### Canvas Colors - Background Layers (Updated) - -- **Canvas-50**: `#FAFAF9` - Base background with subtle warmth -- **Canvas-100**: `#F5F5F4` - Secondary background -- **Canvas-150**: `#EDEDEC` - Tertiary background -- **Canvas-200**: `#E5E5E3` - Card background -- **Canvas-300**: `#D4D4D1` - Hover states - -### Stone/Slate Neutrals - Text & UI Elements - -- **Stone-500**: `#78716C` - Mid-tone text -- **Stone-900**: `#1C1917` - Primary text, high contrast -- **Slate-400**: `#94A3B8` - Secondary text, data elements - -### Market Colors - Crypto Trading - -- **Bullish**: `#4DC46F` - Green for gains (matches sage) -- **Bearish**: `#F56565` - Red for losses (matches coral) -- **Neutral**: `#94A3B8` - Gray for no change -- **Bitcoin**: `#F7931A` - Bitcoin orange -- **Ethereum**: `#627EEA` - Ethereum purple -- **Stablecoin**: `#5B9BF3` - Blue for stables - -### Accent Colors - Special Elements - -- **Lavender**: `#9B8AFB` - Premium features -- **Mint**: `#6EE7B7` - Achievements -- **Sky**: `#7DD3FC` - Notifications -- **Rose**: `#FDA4AF` - Alerts -- **Gold**: `#FCD34D` - Rewards - -## Component Library - -### Buttons - Clear Action Hierarchy - -```css -.btn-primary - Main actions (Send, Buy, Confirm) -.btn-secondary - Secondary actions (Cancel, Back) -.btn-success - Positive confirmations (Approve, Accept) -.btn-danger - Destructive actions (Delete, Reject) -``` - -**Design Principles**: - -- Clear visual hierarchy prevents costly mistakes -- Consistent interaction patterns build familiarity -- Adequate touch targets (44px min) for mobile accessibility - -### Cards - Content Organization - -```css -.card - Basic content container with soft shadows -.card-hover - Interactive cards with elevation feedback -``` - -**Psychology**: Cards create clear content boundaries, reduce cognitive load, and provide familiar interaction patterns. - -### Inputs - Trustworthy Data Entry - -```css -.input-primary - Consistent form styling with focus states -``` - -**Features**: - -- Clear focus indicators for accessibility -- Proper contrast ratios (WCAG 2.1 AA compliant) -- Error states with helpful messaging -- Placeholder text that guides without overwhelming - -### Status Indicators - Clear Information Hierarchy - -```css -.status-online - Connected, active states -.status-offline - Disconnected, inactive states -.status-warning - Attention required -``` - -### Navigation - Intuitive Wayfinding - -```css -.nav-item - Default navigation states -.nav-item-active - Current location indicator -``` - -## Layout Principles - -### Spacing System - -- **Base unit**: 4px (0.25rem) -- **Common spacing**: 8px, 16px, 24px, 32px, 48px -- **Component padding**: 24px (6 units) -- **Section spacing**: 48px (12 units) - -### Responsive Breakpoints - -```css -sm: 640px - Small tablets -md: 768px - Tablets -lg: 1024px - Small desktops -xl: 1280px - Large desktops -``` - -### Grid System - -- **Mobile**: Single column, 16px margins -- **Tablet**: 2-3 columns, 24px margins -- **Desktop**: Multi-column layouts, 32px margins - -## Interactive Elements - -### Shadows - Depth & Hierarchy - -```css -shadow-soft: Subtle elevation for cards -shadow-medium: Interactive hover states -shadow-strong: Modals, overlays, emphasis -``` - -### Animation - Smooth Interactions - -- **Duration**: 200ms for micro-interactions, 300ms for transitions -- **Easing**: `ease-in-out` for natural feel -- **Principles**: Reduce motion for accessibility, maintain performance - -### Focus States - Accessibility First - -- **Ring**: 2px blue outline with 2px offset -- **Color**: Primary-500 for consistency -- **Visibility**: Clear on all interactive elements - -## Mobile Optimization - -### Touch Targets - -- **Minimum**: 44px × 44px -- **Recommended**: 48px × 48px for primary actions -- **Spacing**: 8px minimum between interactive elements - -### Typography Scale - -- **Mobile-first**: Base sizes optimized for readability on small screens -- **Progressive enhancement**: Larger sizes on desktop -- **Line height**: 1.6 for optimal mobile reading - -### Safe Areas - -- **iOS**: Respect notch and home indicator -- **Android**: Navigation and status bar accommodation -- **CSS**: `env(safe-area-inset-*)` for dynamic adjustment - -## Accessibility Standards - -### WCAG 2.1 AA Compliance - -- **Contrast ratios**: 4.5:1 for normal text, 3:1 for large text -- **Color**: Never sole indicator of information -- **Focus**: Visible focus indicators on all interactive elements -- **Motion**: Respect `prefers-reduced-motion` - -### Screen Reader Support - -- **Semantic HTML**: Proper heading hierarchy, landmarks -- **ARIA labels**: Descriptive labels for complex interactions -- **Live regions**: Dynamic content announcements - -## Usage Guidelines - -### When to Use Primary Blue - -- **Call-to-action buttons**: Sign up, log in, send transaction -- **Active navigation**: Current page indicators -- **Links**: Primary navigation and important links -- **Progress indicators**: Loading states, completion - -### When to Use Success Green - -- **Positive confirmations**: Transaction successful, account verified -- **Profit indicators**: Price increases, portfolio gains -- **Status indicators**: Online, connected, active - -### When to Use Warning Orange - -- **Caution states**: Pending transactions, rate limits -- **Important notices**: Security warnings, updates required -- **Validation**: Form warnings that aren't errors - -### When to Use Error Red - -- **Destructive actions**: Delete account, remove funds -- **Error states**: Failed transactions, connection errors -- **Loss indicators**: Price decreases, portfolio losses - -## Implementation Notes - -### CSS Custom Properties - -All colors, spacing, and typography scales are available as CSS custom properties for consistent theming. - -### Dark Mode Readiness - -Color palette includes dark mode variations for future implementation. - -### Performance - -- **Font loading**: `font-display: swap` for improved loading experience -- **Critical CSS**: Base styles inlined, components loaded asynchronously -- **Animation**: Hardware-accelerated where appropriate - ---- - -_This design system prioritizes user trust through consistent, accessible, and professional visual design - essential for crypto community platforms where financial decisions are made._ diff --git a/.claude/rules/13-backend-auth-implementation.md b/.claude/rules/13-backend-auth-implementation.md deleted file mode 100644 index f4fb5033b..000000000 --- a/.claude/rules/13-backend-auth-implementation.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -paths: - - "**/auth/**" - - "**/*auth*.ts" - - "**/*auth*.tsx" - - "**/*auth*.rs" - - "app/src-tauri/src/lib.rs" ---- - -# Backend Authentication Implementation Guide - -## Overview - -The login web-to-desktop flow uses deep links to hand off authentication from a web browser to the Tauri desktop app. The frontend and Rust-side token exchange are implemented. This document specifies the full architecture, backend requirements, and platform-specific gotchas discovered during development. - -## Architecture - -``` -Web Browser Backend Server Desktop App (Tauri) - │ │ │ - │ 1. User clicks login │ │ - │─────────────────────────────>│ │ - │ │ │ - │ 2. Auth flow (Telegram/ │ │ - │ Phone OTP) │ │ - │<────────────────────────────>│ │ - │ │ │ - │ 3. POST /api/auth/ │ │ - │ web-complete │ │ - │─────────────────────────────>│ │ - │ │ │ - │ 4. Returns loginToken │ │ - │<─────────────────────────────│ │ - │ │ │ - │ 5. Redirect to │ │ - │ openhuman://auth?token= │ │ - │─────────────────────────────────────────────────────────────>│ - │ │ │ - │ │ 6. Rust invoke │ - │ │ exchange_token │ - │ │ (POST /auth/desktop-exchange) │ - │ │<─────────────────────────────│ - │ │ │ - │ │ 7. Returns sessionToken │ - │ │ + user object │ - │ │─────────────────────────────>│ - │ │ │ - │ │ 8. App stores session, │ - │ │ navigates to onboarding│ -``` - -**Key**: Step 6 uses a **Rust Tauri command** (`exchange_token`) via `invoke()` instead of browser `fetch()`. This bypasses CORS restrictions that block WebView requests to external APIs. - -## Required Endpoints - -### 1. `GET /auth/telegram` - -Initiates Telegram OAuth. The frontend opens this URL in the system browser. - -**Query params:** - -- `platform=desktop` — indicates the callback should produce a deep link handoff - -**Behavior:** - -1. Redirect user to Telegram OAuth authorization URL -2. On callback, validate Telegram user data -3. Create or find user in database -4. Generate a short-lived `loginToken` (single-use, 5-minute TTL) -5. Redirect to `openhuman://auth?token=` - -### 2. `POST /api/auth/web-complete` - -Called by the web frontend after phone-based authentication completes. - -**Request body:** - -```json -{ "method": "phone", "phoneNumber": "+1234567890", "countryCode": "+1" } -``` - -or - -```json -{ - "method": "telegram", - "telegramUser": { - /* Telegram user object */ - } -} -``` - -**Response (200):** - -```json -{ "loginToken": "short-lived-opaque-token" } -``` - -**Behavior:** - -1. Validate the authentication data (verify OTP for phone, verify Telegram hash) -2. Create or find user in database -3. Generate a short-lived `loginToken` - - Store in database with: token value, user ID, created_at, expires_at, used (boolean) - - TTL: 5 minutes - - Single-use: invalidate after first exchange -4. Return the token - -### 3. `POST /auth/desktop-exchange` - -Called by the Tauri Rust command `exchange_token` (NOT browser fetch). Exchanges the short-lived handoff token for a long-lived session. - -**Note:** The endpoint path is `/auth/desktop-exchange` (no `/api` prefix) — this matches the current frontend implementation. - -**Request body:** - -```json -{ "token": "loginToken-from-web" } -``` - -**Response (200):** - -```json -{ - "sessionToken": "long-lived-session-token", - "user": { "id": "uuid", "username": "string", "firstName": "string" } -} -``` - -**Error response (401):** - -```json -{ "success": false, "error": "Token expired or invalid" } -``` - -**Behavior:** - -1. Look up the `loginToken` in the database -2. Validate: not expired, not already used -3. Mark token as used (single-use enforcement) -4. Generate a long-lived `sessionToken` (e.g., 30-day TTL) -5. Return session token and user profile - -## Database Schema - -### `users` table - -| Column | Type | Description | -| ------------ | --------------- | ---------------- | -| id | UUID (PK) | User ID | -| username | TEXT | Display name | -| first_name | TEXT (nullable) | First name | -| phone_number | TEXT (nullable) | Phone number | -| telegram_id | TEXT (nullable) | Telegram user ID | -| created_at | TIMESTAMP | Account creation | -| updated_at | TIMESTAMP | Last update | - -### `login_tokens` table (handoff tokens) - -| Column | Type | Description | -| ---------- | ---------------------- | ------------------------------ | -| id | UUID (PK) | Token record ID | -| token | TEXT (unique, indexed) | The opaque token string | -| user_id | UUID (FK -> users) | Associated user | -| created_at | TIMESTAMP | When issued | -| expires_at | TIMESTAMP | Expiration (created_at + 5min) | -| used | BOOLEAN | Whether already exchanged | - -### `sessions` table - -| Column | Type | Description | -| ---------- | ---------------------- | --------------------------------- | -| id | UUID (PK) | Session ID | -| token | TEXT (unique, indexed) | Session token | -| user_id | UUID (FK -> users) | Associated user | -| created_at | TIMESTAMP | When issued | -| expires_at | TIMESTAMP | Expiration (created_at + 30 days) | -| revoked | BOOLEAN | Whether revoked | - -## Token Generation - -- Use cryptographically secure random bytes (32+ bytes, base64url-encoded) -- `loginToken`: short-lived (5 min), single-use, opaque -- `sessionToken`: long-lived (30 days), opaque, revocable - -## Security Requirements - -1. **Single-use handoff tokens** — Mark as used immediately on exchange; reject reuse -2. **Short TTL on handoff tokens** — 5 minutes maximum -3. **HTTPS only** in production — tokens travel as URL parameters and POST bodies -4. **Rate limiting** — on `/api/auth/web-complete` and `/auth/desktop-exchange` -5. **Token entropy** — minimum 256 bits of randomness -6. **Deep link validation** — the desktop app only processes `openhuman://auth` paths; ignore unknown paths -7. **Telegram data verification** — validate the `hash` field using your bot token per Telegram docs - -## Implementation Details - -### Rust Token Exchange Command - -The desktop app calls the backend via a Rust Tauri command (`exchange_token` in `src-tauri/src/lib.rs`) using `reqwest`. This bypasses browser CORS restrictions that would block direct `fetch()` calls from the WebView to external APIs (e.g., ngrok tunnels). - -```rust -#[tauri::command] -async fn exchange_token(backend_url: String, token: String) -> Result -``` - -Frontend invocation: - -```typescript -const data = await invoke<{ sessionToken?: string; user?: object }>('exchange_token', { - backendUrl: BACKEND_URL, - token, -}); -``` - -### Deep Link Listener - -Located in `src/utils/desktopDeepLinkListener.ts`. Key behaviors: - -- Uses **lazy dynamic import** in `main.tsx` to avoid loading before Tauri IPC is ready -- No `window.__TAURI__` guard — the try/catch handles non-Tauri environments -- Uses `localStorage.setItem('deepLinkHandled', 'true')` to prevent infinite reload loops (since `getCurrent()` returns the same URL after `window.location.replace()`) -- Clears the `deepLinkHandled` flag on next startup so future deep links work - -### Backend URL Configuration - -Configured in `src/utils/config.ts`: - -```typescript -export const BACKEND_URL = - import.meta.env.VITE_BACKEND_URL || 'https://2937933edf8a.ngrok-free.app'; -``` - -Set `VITE_BACKEND_URL` environment variable for different environments. - -## Frontend Integration Points - -| File | Role | -| -------------------------------------- | ------------------------------------------------------------------------------------ | -| `src/main.tsx` | Lazy-imports and starts the deep link listener | -| `src/utils/desktopDeepLinkListener.ts` | Parses deep link -> invokes Rust `exchange_token` -> stores session -> navigates | -| `src/utils/deeplink.ts` | Web-side: calls `/api/auth/web-complete` and builds `openhuman://auth?token=...` URL | -| `src/utils/config.ts` | Backend URL configuration | -| `src/pages/Login.tsx` | Opens `GET /auth/telegram?platform=desktop` in browser | -| `src-tauri/src/lib.rs` | Rust `exchange_token` command using `reqwest` (CORS-free) | - -## Phone OTP Flow (Future) - -The frontend has a phone input UI but the OTP verification flow needs: - -1. `POST /api/auth/send-otp` — sends SMS to phone number -2. `POST /api/auth/verify-otp` — verifies code, returns success -3. Then `POST /api/auth/web-complete` with `method: "phone"` to get the handoff token - -## Platform-Specific Notes - -See `14-deep-link-platform-guide.md` for detailed platform gotchas. - ---- - -_Last updated: 2026-01-28_ diff --git a/.claude/rules/14-deep-link-platform-guide.md b/.claude/rules/14-deep-link-platform-guide.md deleted file mode 100644 index fd288934e..000000000 --- a/.claude/rules/14-deep-link-platform-guide.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -paths: - - "**/*deep*link*" - - "**/tauri.conf.json" - - "app/src-tauri/**" ---- - -# Deep Link Platform Guide - -## Overview - -The `openhuman://` custom URL scheme is used to hand off authentication from a web browser to the Tauri desktop app. This document covers platform-specific behavior, gotchas, and build requirements discovered during development. - -## Scheme Registration - -Configured in `src-tauri/tauri.conf.json`: - -```json -{ "plugins": { "deep-link": { "desktop": { "schemes": ["openhuman"] } } } } -``` - -## macOS - -### How It Works - -- URL schemes are registered via `Info.plist` inside the `.app` bundle -- The `CFBundleURLSchemes` entry is automatically generated by Tauri from the config -- macOS LaunchServices maps the scheme to the installed app - -### Critical: `tauri dev` Does NOT Support Deep Links - -- `npm run tauri dev` runs the binary directly without a `.app` bundle -- No `Info.plist` exists, so macOS cannot register the URL scheme -- **You must use `npm run tauri build --debug` and run the `.app` bundle** - -### `register_all()` Is Unsupported on macOS - -- The Tauri deep-link plugin's `register_all()` method panics on macOS with `unsupported platform` -- Only call it on Windows and Linux: - ```rust - #[cfg(any(windows, target_os = "linux"))] - { - app.deep_link().register_all()?; - } - ``` - -### Build & Install Workflow - -```bash -# Build debug .app bundle -npm run tauri build -- --debug --bundles app - -# Install to /Applications (or run from bundle path) -cp -R src-tauri/target/debug/bundle/macos/tauri-app.app /Applications/ - -# Launch -open /Applications/tauri-app.app - -# Test deep link -open "openhuman://auth?token=YOUR_TOKEN" -``` - -### Cargo Caching Gotcha - -- Cargo may not re-embed updated `dist/` frontend assets on incremental builds -- The `tauri-build` build script embeds frontend assets at compile time -- **If the app shows stale UI, run `cargo clean` before rebuilding:** - ```bash - cargo clean --manifest-path src-tauri/Cargo.toml - npm run tauri build -- --debug --bundles app - ``` - -### WebKit Cache - -- The macOS WebView (WKWebView) caches aggressively -- Clear caches when debugging stale content: - ```bash - rm -rf ~/Library/WebKit/com.openhuman.app - rm -rf ~/Library/Caches/com.openhuman.app - rm -rf ~/Library/Application\ Support/com.openhuman.app - ``` - -### Debug Builds: Secondary Instance Behavior - -- In debug mode, clicking a deep link while the app is running may briefly spawn a secondary instance -- The `onOpenUrl` event may fire twice (one may be an empty string) -- The `deepLinkHandled` localStorage flag prevents double-processing - -## Windows & Linux - -### How It Works - -- URL schemes are registered at runtime via `register_all()` in the `setup` hook -- This works in both dev mode (`tauri dev`) and production builds -- No special build or install steps needed for deep link testing - -### Code - -```rust -#[cfg(any(windows, target_os = "linux"))] -{ - app.deep_link().register_all()?; -} -``` - -## All Platforms - -### `window.__TAURI__` Is Not Available Immediately - -- The Tauri IPC bridge (`window.__TAURI__`) is injected asynchronously -- Code that runs at module load time (top of `main.tsx`) cannot reliably check `__TAURI__` -- **Solution**: Use dynamic `import()` for the deep link listener and wrap plugin calls in try/catch: - ```typescript - // main.tsx - import('./utils/desktopDeepLinkListener').then(m => { - m.setupDesktopDeepLinkListener().catch(console.error); - }); - ``` - -### `alert()` Is Suppressed in Tauri WebView - -- `window.alert()` does not display in Tauri's WKWebView on macOS -- For debugging, use visible DOM elements or `console.log()` (viewable via Safari Web Inspector) - -### `document.title` Is Overridden by Tauri - -- Tauri sets the window title from `tauri.conf.json` `app.windows[].title` -- Setting `document.title` in JS has no visible effect on the window title bar - -### Infinite Reload Loop Prevention - -- `getCurrent()` returns the deep link URL that launched/activated the app -- After `window.location.replace()`, the page reloads and `getCurrent()` returns the same URL again -- **Solution**: Set `localStorage.deepLinkHandled = 'true'` before navigating, check and clear it on next load - -### CORS: Use Rust for Backend Calls - -- Browser `fetch()` from the Tauri WebView is subject to CORS -- External APIs (especially ngrok tunnels) typically don't set `Access-Control-Allow-Origin` -- **Solution**: Use a Rust Tauri command with `reqwest` to make HTTP requests (no CORS): - ```typescript - // Instead of fetch(): - const data = await invoke('exchange_token', { backendUrl, token }); - ``` - -## Tauri Plugin Dependencies - -### Cargo.toml - -```toml -tauri-plugin-deep-link = "2.0.0" -reqwest = { version = "0.12", features = ["json"] } -tokio = { version = "1", features = ["full"] } -``` - -### package.json - -```json -"@tauri-apps/plugin-deep-link": "^2" -``` - -### Capabilities (`src-tauri/capabilities/default.json`) - -```json -{ "permissions": ["core:default", "opener:default", "deep-link:default"] } -``` - ---- - -_Last updated: 2026-01-28_ diff --git a/.claude/rules/15-settings-modal-system.md b/.claude/rules/15-settings-modal-system.md deleted file mode 100644 index f31b88c92..000000000 --- a/.claude/rules/15-settings-modal-system.md +++ /dev/null @@ -1,258 +0,0 @@ ---- -paths: - - "app/src/components/settings/**" ---- - -# Settings Modal System - URL-Based Modal Architecture - -## Overview - -Complete settings modal system with clean white design that overlays on existing content. Features URL-based routing, Redux integration, and reusable component architecture for system settings management. - -## Architecture - -### Modal Infrastructure - -**Location**: `src/components/settings/` - -The settings modal system uses a clean architectural pattern: - -``` -SettingsModal.tsx # Route-based modal container -├── SettingsLayout.tsx # createPortal modal wrapper with backdrop -├── SettingsHome.tsx # Main menu with user profile -├── panels/ -│ └── ConnectionsPanel.tsx # Connection management interface -├── components/ -│ ├── SettingsHeader.tsx # User profile section -│ ├── SettingsMenuItem.tsx # Individual menu items -│ ├── SettingsBackButton.tsx # Back navigation -│ └── SettingsPanelLayout.tsx # Panel wrapper -└── hooks/ - ├── useSettingsNavigation.ts # URL routing logic - └── useSettingsAnimation.ts # Animation state -``` - -### URL Routing Pattern - -``` -/settings # Main settings menu -/settings/connections # Connection management -/settings/messaging # Future: messaging settings -/settings/privacy # Future: privacy settings -/settings/profile # Future: profile settings -/settings/advanced # Future: advanced settings -/settings/billing # Future: billing settings -``` - -## Design Specifications - -### Modal Container - -- **Width**: 520px (desktop), responsive on mobile -- **Background**: Pure white (#FFFFFF) - contrasts with app's glass morphism -- **Border-radius**: 16px -- **Shadow**: `0 20px 25px -5px rgba(0, 0, 0, 0.1)` -- **Backdrop**: Black 50% opacity with 8px blur -- **Position**: Fixed center with flexbox - -### User Profile Section - -- **Avatar**: 56px circular with border and shadow -- **Typography**: 18px semibold name, 14px gray email -- **Background**: Subtle gradient from white to gray-50 -- **Integration**: Redux user state for name and email display - -### Menu Items - -- **Height**: 52px with proper touch targets -- **Hover**: bg-gray-50 with smooth transitions -- **Icons**: 20px with consistent spacing -- **Chevron**: 16px with translateX(2px) hover animation -- **Typography**: 15px medium weight for clarity - -### Animation System - -- **Entry**: 200ms ease-out modal slide up -- **Panel transitions**: 250ms slide from right -- **Micro-interactions**: 150ms hover effects -- **Exit**: 150ms ease-in with backdrop fade - -## Component Usage - -### Basic Modal Implementation - -```tsx -// Trigger settings modal (from any component) -import { useNavigate } from 'react-router-dom'; - -const navigate = useNavigate(); -const openSettings = () => navigate('/settings'); -const openConnections = () => navigate('/settings/connections'); -``` - -### Settings Navigation Hook - -```tsx -import { useSettingsNavigation } from '../hooks/useSettingsNavigation'; - -const { currentRoute, navigateTo, navigateBack, closeModal } = useSettingsNavigation(); - -// Navigate to connections -navigateTo('connections'); - -// Go back or close -navigateBack(); // or closeModal(); -``` - -### Redux Integration - -```tsx -// User profile data -const { user } = useAppSelector(state => state.user); -const displayName = user?.username || user?.firstName || 'User'; - -// Connection status -const { isAuthenticated } = useAppSelector(state => state.telegram); - -// Logout functionality -const dispatch = useAppDispatch(); -const handleLogout = () => { - dispatch(clearToken()); - navigate('/'); -}; -``` - -## Connection Management - -### Status Display - -- **Connected**: Green badge with proper status -- **Offline**: Gray badge for disconnected services -- **Coming Soon**: Disabled state for future integrations - -### Integration Points - -- **Telegram**: Uses existing `TelegramConnectionModal` for setup -- **Redux State**: Real-time status from telegram slice -- **Component Reuse**: Leverages `connectOptions` from onboarding - -### Connection Actions - -```tsx -// Connect new service -const handleConnect = (serviceId: string) => { - if (serviceId === 'telegram') { - setTelegramModalOpen(true); - } - // Future: other service connection flows -}; - -// Disconnect service -const handleDisconnect = (serviceId: string) => { - // Service-specific disconnection logic -}; -``` - -## Mobile Responsiveness - -### Breakpoint Behavior - -- **Mobile (<640px)**: Full-screen modal with slight margins -- **Tablet (640-1024px)**: Scaled modal with backdrop -- **Desktop (>1024px)**: Fixed 520px width - -### Touch Interactions - -- **Minimum target size**: 48px for accessibility -- **Swipe gestures**: Down-to-close support -- **Safe areas**: iOS notch and navigation accommodation - -## Accessibility Features - -### Focus Management - -- Trap focus within modal during interaction -- Return focus to trigger element on close -- Keyboard navigation between menu items - -### ARIA Labels - -- `role="dialog"` with proper modal attributes -- `aria-labelledby` for modal title -- Screen reader friendly navigation - -### Keyboard Support - -- **Escape key**: Close modal and return to previous page -- **Arrow keys**: Navigate between menu items -- **Enter/Space**: Activate menu items -- **Tab**: Focus trap within modal - -## Integration Patterns - -### Existing Component Reuse - -- **Connection Options**: Reuses `connectOptions` array from `ConnectStep.tsx` -- **Modal Pattern**: Follows `TelegramConnectionModal.tsx` pattern -- **Redux Patterns**: Uses existing slice patterns and selectors - -### State Management - -- **No new Redux state**: Leverages existing auth, user, telegram slices -- **URL state**: Modal state driven by route parameters -- **Component state**: Local state for animations and temporary UI state - -### Future Extensibility - -- **Panel Structure**: Easy to add new settings panels -- **Menu Items**: Simple configuration for new settings categories -- **Service Integration**: Pattern established for new connection types - -## Performance Considerations - -### Code Splitting - -- Settings panels lazy-loaded when accessed -- Modal infrastructure loaded on first settings access -- Minimal impact on initial app bundle size - -### Animation Performance - -- Hardware-accelerated CSS transforms -- Proper will-change declarations for animations -- Debounced interactions for smooth experience - -### Memory Management - -- Proper cleanup of event listeners and timers -- Component unmounting handled correctly -- Redux subscriptions managed efficiently - -## Development Guidelines - -### Adding New Settings Panels - -1. Create panel component in `src/components/settings/panels/` -2. Add route in `SettingsModal.tsx` switch statement -3. Add menu item in `SettingsHome.tsx` menu array -4. Follow `ConnectionsPanel.tsx` pattern for consistency - -### Styling Conventions - -- Use existing Tailwind classes where possible -- Follow clean white design (not glass morphism) -- Maintain 52px height for interactive elements -- Use consistent spacing and typography scales - -### Testing Patterns - -- Test modal open/close functionality -- Verify URL navigation between panels -- Test Redux state integration -- Ensure mobile responsive behavior -- Validate accessibility requirements - ---- - -_This settings modal system provides a robust, extensible foundation for app configuration while maintaining the sophisticated design standards of the crypto community platform._ diff --git a/.claude/rules/16-macos-background-execution.md b/.claude/rules/16-macos-background-execution.md deleted file mode 100644 index aae0cf4af..000000000 --- a/.claude/rules/16-macos-background-execution.md +++ /dev/null @@ -1,304 +0,0 @@ ---- -paths: - - "app/src-tauri/src/lib.rs" - - "**/Info.plist" - - "**/tauri.conf.json" ---- - -# macOS Background Execution - Menu Bar & Autostart - -## Overview - -Complete implementation of macOS background execution features including system tray menu bar app and launch at login functionality for the Outsourced crypto community platform. - -## Features Implemented - -### 1. System Tray (Menu Bar App) - -- **Tray icon** appears in macOS menu bar -- **Click to toggle** window visibility (left-click on icon) -- **Context menu** with two options: - - "Show/Hide Window" - Toggle window visibility - - "Quit" - Exit application completely -- **Window starts hidden** on launch (background execution) -- **Close button minimizes to tray** instead of quitting app - -### 2. Launch at Login (Autostart) - -- **LaunchAgent configuration** for macOS -- **Configurable autostart** via plugin API -- **Command-line flags** support for launch arguments -- **Native macOS integration** using Launch Services - -## Implementation Details - -### Dependencies Added - -**Cargo.toml**: - -```toml -tauri = { version = "2", features = ["tray-icon", "macos-private-api"] } -tauri-plugin-autostart = "2" -``` - -### Configuration Changes - -**tauri.conf.json**: - -```json -{ - "app": { - "windows": [ - { - "visible": false, // Start hidden - "decorations": true, - "resizable": true, - "center": true - } - ], - "trayIcon": { - "id": "main-tray", - "iconPath": "icons/icon.png", - "iconAsTemplate": true, - "menuOnLeftClick": false, - "tooltip": "Outsourced - Crypto Community Platform" - }, - "macOSPrivateApi": true // Required for advanced tray features - } -} -``` - -**capabilities/default.json**: - -```json -{ - "permissions": [ - "core:tray:default", - "autostart:default", - "autostart:allow-enable", - "autostart:allow-disable", - "autostart:allow-is-enabled" - ] -} -``` - -### Rust Implementation - -**Key Components**: - -1. **Toggle Window Visibility** - Helper function to show/hide main window -2. **Setup Tray** - Creates system tray with menu and event handlers -3. **Window Close Handler** - macOS-specific override to minimize instead of quit -4. **Autostart Plugin** - Configured with LaunchAgent for macOS - -**Code Structure** (`src-tauri/src/lib.rs`): - -```rust -// System tray with menu -fn setup_tray(app: &AppHandle) -> Result<(), Box> { - // Creates "Show/Hide Window" and "Quit" menu items - // Handles left-click on tray icon to toggle visibility - // Menu click events for show/hide and quit actions -} - -// Main app setup -pub fn run() { - tauri::Builder::default() - .plugin(tauri_plugin_autostart::init( - tauri_plugin_autostart::MacosLauncher::LaunchAgent, - Some(vec!["--flag1", "--flag2"]), - )) - .setup(|app| { - // Desktop-only tray setup - #[cfg(desktop)] - setup_tray(app.handle())?; - - // macOS-specific close behavior - #[cfg(target_os = "macos")] - // Override close button to hide instead of quit - }) -} -``` - -## Platform-Specific Behavior - -### macOS - -- Tray icon appears in menu bar -- Close button hides window (minimizes to tray) -- LaunchAgent for autostart integration -- Native macOS menu bar styling - -### Windows/Linux - -- Tray icon in system tray -- Same menu functionality -- Platform-appropriate autostart mechanisms - -### Mobile (iOS/Android) - -- Tray features disabled (desktop-only) -- Autostart not applicable on mobile - -## Usage - -### From Frontend (Future Integration) - -Control autostart via Tauri commands: - -```typescript -import { invoke } from '@tauri-apps/api/core'; - -// Enable autostart -await invoke('plugin:autostart|enable'); - -// Disable autostart -await invoke('plugin:autostart|disable'); - -// Check if enabled -const isEnabled = await invoke('plugin:autostart|is_enabled'); -``` - -### Tray Behavior - -**User Actions**: - -1. **Left-click tray icon** → Toggle window visibility -2. **Right-click tray icon** → Open context menu -3. **"Show/Hide Window"** → Toggle visibility -4. **"Quit"** → Exit application -5. **Close window button** → Hide window (macOS only) - -## Testing - -### Build & Test - -```bash -# Clean build -cargo clean --manifest-path src-tauri/Cargo.toml - -# Build debug app bundle (required for tray testing on macOS) -npm run tauri build -- --debug --bundles app - -# Install to Applications -cp -R src-tauri/target/debug/bundle/macos/tauri-app.app /Applications/ - -# Launch and test -open /Applications/tauri-app.app -``` - -### Verification Checklist - -- [ ] Tray icon appears in menu bar -- [ ] Left-click toggles window visibility -- [ ] Context menu has "Show/Hide Window" and "Quit" -- [ ] Close button hides window (doesn't quit) -- [ ] Window can be shown again from tray -- [ ] Quit option properly exits app -- [ ] Window starts hidden on launch -- [ ] Autostart can be enabled/disabled - -## Build Requirements - -### macOS Deep Link & Tray Testing - -- Must use `.app` bundle (not `tauri dev`) -- `tauri dev` does NOT support deep links or full tray functionality -- Use debug build: `npm run tauri build -- --debug --bundles app` - -### Cargo Cache Issues - -If UI appears outdated after rebuild: - -```bash -cargo clean --manifest-path src-tauri/Cargo.toml -npm run tauri build -- --debug --bundles app -``` - -### WebKit Cache - -Clear WebKit cache if needed: - -```bash -rm -rf ~/Library/WebKit/com.openhuman.app -rm -rf ~/Library/Caches/com.openhuman.app -``` - -## Configuration Options - -### Autostart Arguments - -Customize launch arguments in `lib.rs`: - -```rust -.plugin(tauri_plugin_autostart::init( - tauri_plugin_autostart::MacosLauncher::LaunchAgent, - Some(vec!["--minimized", "--silent"]), // Custom flags -)) -``` - -### Tray Icon - -Change tray icon path in `tauri.conf.json`: - -```json -{ - "trayIcon": { - "iconPath": "icons/custom-tray-icon.png", - "iconAsTemplate": true // Adapts to dark/light menu bar - } -} -``` - -### Window Behavior - -Adjust window settings: - -```json -{ - "windows": [ - { - "visible": false, // Start hidden - "decorations": true, // Show title bar - "resizable": true, // Allow resize - "center": true // Center on screen - } - ] -} -``` - -## Known Limitations - -1. **Menu bar icon styling** - Uses template mode for dark/light theme adaptation -2. **LaunchAgent delay** - macOS may have slight delay before launching at login -3. **Bundle requirement** - Full functionality requires `.app` bundle, not `tauri dev` -4. **Desktop-only** - Mobile platforms don't support system tray - -## Future Enhancements - -Potential improvements for future development: - -1. **Settings Integration** - Add autostart toggle in settings modal -2. **Notification Support** - Show notifications from tray -3. **Quick Actions** - Add more tray menu items -4. **Status Indicators** - Show connection status in tray icon -5. **Tray Tooltip** - Dynamic tooltip with app status - -## Security Considerations - -### macOS Private API - -- Required for advanced tray features -- Approved by Apple for this use case -- No App Store restrictions for direct distribution - -### LaunchAgent - -- Installed in user's LaunchAgents directory -- User can disable via System Settings -- Respects macOS security policies - ---- - -_Implementation completed: 2026-01-29_ -_Status: Fully functional, tested, and production-ready_ diff --git a/.claude/rules/17-skills-memory-inference-flow.md b/.claude/rules/17-skills-memory-inference-flow.md deleted file mode 100644 index 965c6d0fa..000000000 --- a/.claude/rules/17-skills-memory-inference-flow.md +++ /dev/null @@ -1,287 +0,0 @@ ---- -paths: - - "**/skills/**" - - "**/memory/**" - - "src/openhuman/**" - - "app/src/providers/SkillProvider.tsx" - - "app/src/lib/ai/**" ---- - -# Skills → Memory Layer → Agent Inference: Full Flow - -## Overview - -This document traces the complete data flow from skill discovery and OAuth/sync events, through -the TinyHumans Neocortex memory layer (tinyhumansai SDK), and into the Rust-side agentic -inference loop — showing exactly how skill data is written to and read from memory and how it -reaches the LLM context at inference time. - ---- - -## 1. Skill Discovery & Lifecycle (SkillProvider.tsx) - -**File**: `src/providers/SkillProvider.tsx` - -On app mount (when a JWT token is present) `SkillProvider` calls -`invoke('runtime_discover_skills')` → the Rust runtime scans -`skills/skills/{skill-id}/manifest.json` from the git submodule and returns a manifest list. - -``` -Token present - → discoverSkills() - → invoke('runtime_discover_skills') // Rust: qjs_engine.rs:184 - → reads skills/skills/*/manifest.json - → filters: is_javascript() && supports_current_platform() - → returns manifests[] - → skillManager.registerSkill(manifest) // in-memory registry - → for each manifest with setupComplete: - skillManager.startSkill(manifest) // starts V8/QuickJS instance -``` - -Two Tauri event listeners run continuously: - -| Event | What it does | -| ------------------------------ | ------------------------------------------------------------------------ | -| `skill-state-changed` | Dispatches `setSkillState` into Redux `skillsSlice.skillStates[skillId]` | -| `runtime:skill-status-changed` | Updates `skillsSlice.skills[skillId].status`; surfaces errors | - ---- - -## 2. Memory Client Initialisation - -**File**: `src-tauri/src/memory/mod.rs` -**Tauri command**: `init_memory_client` (called by frontend after auth) - -The `MemoryClient` wraps the `TinyHumansMemoryClient` from the `tinyhumansai` Rust crate. -It is constructed with the user's JWT (`authSlice.token`) and stored as `Arc` -in `MemoryState` (a `Mutex>`). - -Base URL resolution (in priority order): - -1. `OPENHUMAN_BASE_URL` env var -2. `TINYHUMANS_BASE_URL` env var -3. SDK default - ---- - -## 3. Skill → Memory Sync: Two Write Paths - -Both trigger inside `src-tauri/src/runtime/qjs_skill_instance.rs`. - -### 3a. OAuth Completion (`skill/oauth-complete`) - -When a user connects a skill via OAuth, the JS runtime calls back with `skill/oauth-complete`. -After the OAuth flow completes the skill's **ops state** (data published via `state.set()`) is -snapshotted and stored to memory: - -``` -skill/oauth-complete handler - → handle_js_call(rt, ctx, "onOAuthComplete", params) // runs skill JS - → ops_state.read().data.clone() // snapshot skill state - → tokio::spawn (fire-and-forget): - MemoryClient::store_skill_sync( - skill_id = e.g. "gmail" - integration_id = params["integrationId"] // e.g. user email - title = "{skill} OAuth sync — {integrationId}" - content = JSON snapshot of ops state - namespace = "skill:{skill_id}:{integration_id}" - ) - → tinyhumansai::TinyHumansMemoryClient::insert_memory(InsertMemoryParams { ... }) - → HTTP POST to TinyHumans Neocortex API -``` - -### 3b. Periodic Sync (`skill/sync`) - -The runtime fires `skill/sync` events on a cron schedule. The flow is identical to OAuth -completion but uses `integration_id = "default"` and title `"{skill} periodic sync"`: - -``` -skill/sync handler - → handle_js_call(rt, ctx, "onSync", "{}") - → ops_state.read().data.clone() - → tokio::spawn: - MemoryClient::store_skill_sync( - skill_id = e.g. "notion" - integration_id = "default" - namespace = "skill:notion:default" - content = JSON snapshot - ) -``` - -### Namespace Pattern - -All skill memories are stored under `skill:{skill_id}:{integration_id}`. -Examples: `skill:gmail:user@example.com`, `skill:notion:default`. - ---- - -## 4. Memory Operations Reference - -**File**: `src-tauri/src/memory/mod.rs` - -| Method | SDK call | When used | -| --------------------------- | --------------- | ----------------------------------------------------- | -| `store_skill_sync(...)` | `insert_memory` | OAuth complete, periodic sync | -| `query_skill_context(...)` | `query_memory` | RAG query — fetch relevant chunks for a user question | -| `recall_skill_context(...)` | `recall_memory` | Recall synthesised summary from Master node | -| `clear_skill_memory(...)` | `delete_memory` | OAuth revoke / disconnect | - ---- - -## 5. Conversation → Inference: Two Code Paths - -**File**: `src/pages/Conversations.tsx` - -`useRustChat()` returns `true` when running in Tauri (desktop). This selects between two paths: - -``` -handleSendMessage(text) - ├─ rustChat == true → Rust path (invoke chat_send) - └─ rustChat == false → Web path (handleSendMessageWeb — TypeScript loop) -``` - -### 5a. Rust Path (Desktop) - -``` -chatSend({ threadId, message, model, authToken, backendUrl, messages, notionContext }) - → invoke('chat_send') // src-tauri/src/commands/chat.rs:359 - → spawns background task - → chat_send_inner(...) -``` - -Completion events flow back over Tauri events: - -| Event | Frontend handler | -| ------------------ | ------------------------------------- | -| `chat:tool_call` | shows active tool indicator | -| `chat:tool_result` | clears tool indicator | -| `chat:done` | `dispatch(addInferenceResponse(...))` | -| `chat:error` | shows error, clears loading state | - -### 5b. Web/Fallback Path - -Used when not running in Tauri (browser). Runs the agentic loop entirely in TypeScript: - -- Calls `inferenceApi.createChatCompletion(request)` directly -- Executes tools via `skillManager.callTool(skillId, toolName, args)` -- Both paths share the same 5-round `MAX_TOOL_ROUNDS` limit and `{skillId}__{toolName}` naming convention - ---- - -## 6. Rust Agentic Loop in Detail - -**File**: `src-tauri/src/commands/chat.rs` — `chat_send_inner()` - -``` -Step 1: Load OpenClaw context - → load_openclaw_context(app) - → reads ai/SOUL.md, IDENTITY.md, AGENTS.md, USER.md, BOOTSTRAP.md, MEMORY.md, TOOLS.md - → cached in static AI_CONFIG_CACHE (cleared on restart) - → truncated to MAX_CONTEXT_CHARS (20,000 chars) - -Step 2: Recall memory context - → MemoryClient::recall_skill_context("conversations", thread_id, 10) - → tinyhumansai recall_memory(namespace="skill:conversations:{thread_id}") - → returns synthesised summary string or None - -Step 3: Build processed user message - processed = user_message - if openclaw_context → prepend as "## Project Context\n...\n\nUser message: {processed}" - if memory_context → prepend as "[MEMORY_CONTEXT]\n{mem}\n[/MEMORY_CONTEXT]\n\n{processed}" - if notion_context → prepend as "{notionContext}\n\n{processed}" - -Step 4: Build messages array - → history (ChatMessagePayload[]) + processed user message - -Step 5: Discover tools - → engine.all_tools() - → returns all tools from running skills - → namespaced: "{skill_id}__{tool_name}" - → formatted as OpenAI function-calling schema - -Step 6: Agentic loop (max 5 rounds) - for round in 0..MAX_TOOL_ROUNDS: - POST {backend_url}/openai/v1/chat/completions - body: { model, messages, tools, tool_choice: "auto" } - timeout: 120s - auth: Bearer {auth_token} - - if finish_reason == "tool_calls": - emit chat:tool_call - engine.call_tool(skill_id, tool_name, args) // 60s timeout - → QuickJS/V8 runtime executes skill JS tool handler - emit chat:tool_result - append tool result to messages - continue loop - - else (finish_reason == "stop"): - emit chat:done { full_response, rounds_used, token_counts } - return Ok(()) -``` - ---- - -## 7. Tool Execution: Rust → Skill JS - -**File**: `src-tauri/src/runtime/qjs_engine.rs` — `call_tool(skill_id, tool_name, args)` - -The Rust runtime routes `call_tool` into the running QuickJS/V8 skill instance: - -- Serialises `args` as JSON -- Calls the JS tool handler registered by the skill -- Returns `ToolCallResult { content: Vec, is_error: bool }` -- Text content is extracted and appended as the `tool` role message in the loop - ---- - -## 8. End-to-End Flow Summary - -``` -User types message (Conversations.tsx) - │ - ├─ [Web path only] invoke('recall_memory') → TinyHumans API (recall) - │ returns synthesised context - ├─ buildNotionContext() → from Redux skillStates.notion - │ - ▼ -invoke('chat_send') [Rust/desktop path] - │ - ▼ -Rust: chat_send_inner() - ├─ load_openclaw_context() → ai/*.md files (cached) - ├─ recall_skill_context("conversations", tid) → TinyHumans API (recall) - ├─ Build prompt: [OpenClaw] + [MEMORY] + [Notion] + user_message - ├─ discover_tools() → all running skill tools - │ - └─ Agentic loop (≤5 rounds): - POST /openai/v1/chat/completions → Backend LLM (neocortex-mk1) - │ - ├─ tool_calls → engine.call_tool() → QuickJS/V8 skill instance - │ └─ JS tool handler - │ └─ returns result string - │ └─ appended as tool message - │ - └─ stop → emit chat:done - └─ Frontend: dispatch(addInferenceResponse(...)) - -Separately (async, fire-and-forget): - Skill onSync() / onOAuthComplete() - → MemoryClient::store_skill_sync() - → TinyHumans insert_memory (namespace: skill:{id}:{integrationId}) -``` - ---- - -## Key Files Reference - -| File | Role | -| --------------------------------------------- | -------------------------------------------------- | -| `src/providers/SkillProvider.tsx` | Discovery, lifecycle, Redux state sync | -| `src/pages/Conversations.tsx` | Message send, both code paths, Notion context | -| `src/services/chatService.ts` | `chatSend()`, `chatCancel()`, `useRustChat()` | -| `src-tauri/src/commands/chat.rs` | Rust agentic loop, context assembly, tool dispatch | -| `src-tauri/src/commands/memory.rs` | `recall_memory` Tauri command | -| `src-tauri/src/memory/mod.rs` | `MemoryClient` wrapping tinyhumansai SDK | -| `src-tauri/src/runtime/qjs_skill_instance.rs` | Skill sync → memory write triggers | -| `src-tauri/src/runtime/qjs_engine.rs` | `discover_skills()`, `call_tool()`, `all_tools()` | -| `skills/skills/*/manifest.json` | Skill metadata (git submodule) | diff --git a/.claude/rules/README.md b/.claude/rules/README.md new file mode 100644 index 000000000..e0046273a --- /dev/null +++ b/.claude/rules/README.md @@ -0,0 +1,19 @@ +# `.claude/rules/` + +This directory is intentionally near-empty. + +Authoritative docs for AI agents and contributors: + +- **[`CLAUDE.md`](../../CLAUDE.md)** — repo layout, runtime scope, commands, frontend/Tauri/Rust conventions, testing, debug logging, feature workflow. +- **[`AGENTS.md`](../../AGENTS.md)** — RPC controller patterns, `RpcOutcome` contract. +- **[`docs/ARCHITECTURE.md`](../../docs/ARCHITECTURE.md)** — narrative architecture, dual-socket sync. +- **[`docs/DESIGN_GUIDELINES.md`](../../docs/DESIGN_GUIDELINES.md)** — visual language. +- **[`docs/E2E-TESTING.md`](../../docs/E2E-TESTING.md)** — WDIO/Appium testing. +- **[`docs/src/README.md`](../../docs/src/README.md)** — frontend. +- **[`docs/src-tauri/README.md`](../../docs/src-tauri/README.md)** — Tauri shell. + +## When to add a file here + +Only add a `*.md` file in this directory if you need **path-gated context** loaded conditionally by Claude Code (via the `paths:` frontmatter) for a narrow part of the tree, AND the content is not already covered in `CLAUDE.md`. + +Each file added here ships in every agent context that matches its `paths:` glob — so keep them small, current, and non-overlapping with `CLAUDE.md`. Stale rules actively mislead agents. diff --git a/app/src/utils/integrationTokensCrypto.ts b/app/src/utils/integrationTokensCrypto.ts deleted file mode 100644 index 3c62dfee2..000000000 --- a/app/src/utils/integrationTokensCrypto.ts +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Helpers for encrypting/decrypting OAuth integration tokens. - * Matches backend format: IV (16 bytes) + AuthTag (16 bytes) + EncryptedData. - * IV is derived from SHA-256(message) for encryption (deterministic). - */ - -export type IntegrationTokensPayload = { - accessToken: string; - refreshToken: string; - /** ISO timestamp string */ - expiresAt: string; -}; - -/** Stored value: encrypted blob only; decrypt with key when needed */ -export type StoredIntegrationTokens = { encrypted: string }; - -const HEX_REGEX = /^[0-9a-fA-F]*$/; - -export function hexToBytes(hex: string): Uint8Array { - const cleanHex = hex.trim().replace(/^0x/i, ''); - if (!cleanHex) return new Uint8Array(); - if (cleanHex.length % 2 !== 0) { - throw new TypeError(`hexToBytes: hex string must have even length (got ${cleanHex.length})`); - } - if (!HEX_REGEX.test(cleanHex)) { - throw new TypeError('hexToBytes: hex string must contain only [0-9a-fA-F] characters'); - } - const bytes = new Uint8Array(cleanHex.length / 2); - for (let i = 0; i < cleanHex.length; i += 2) { - bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16); - } - return bytes; -} - -export function hexToBase64(hex: string): string { - const bytes = hexToBytes(hex); - if (bytes.length === 0) return ''; - let binary = ''; - for (let i = 0; i < bytes.length; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); -} - -export function base64ToBytes(b64: string): Uint8Array { - let normalized = b64.replace(/-/g, '+').replace(/_/g, '/'); - const pad = normalized.length % 4; - if (pad === 2) normalized += '=='; - else if (pad === 3) normalized += '='; - - const binary = atob(normalized); - const bytes = new Uint8Array(binary.length); - for (let i = 0; i < binary.length; i++) { - bytes[i] = binary.charCodeAt(i); - } - return bytes; -} - -/** - * Decrypt an encrypted tokens payload (base64) using a 32-byte key (hex). - * Backend format: IV (16) + AuthTag (16) + EncryptedData. - */ -export async function decryptIntegrationTokens( - encryptedPayload: string, - keyHex: string -): Promise { - if (typeof crypto === 'undefined' || !crypto.subtle) { - throw new Error('Web Crypto API is not available for decryption'); - } - - const keyBytes = hexToBytes(keyHex); - if (keyBytes.length !== 32) { - throw new Error('Invalid encryption key: expected 32-byte AES-GCM key'); - } - - const combined = base64ToBytes(encryptedPayload); - if (combined.length <= 32) { - throw new Error('Encrypted payload too short'); - } - - const iv = combined.slice(0, 16); - const authTag = combined.slice(16, 32); - const encryptedData = combined.slice(32); - const ciphertextWithTag = new Uint8Array(encryptedData.length + authTag.length); - ciphertextWithTag.set(encryptedData, 0); - ciphertextWithTag.set(authTag, encryptedData.length); - - const cryptoKey = await crypto.subtle.importKey( - 'raw', - keyBytes as unknown as BufferSource, - { name: 'AES-GCM' }, - false, - ['decrypt'] - ); - - const decrypted = await crypto.subtle.decrypt( - { name: 'AES-GCM', iv, tagLength: 128 }, - cryptoKey, - ciphertextWithTag as unknown as BufferSource - ); - - return new TextDecoder().decode(decrypted); -} - -/** - * Encrypt a plaintext string (e.g. JSON) using a 32-byte key (hex). - * Matches backend: deterministic IV = first 16 bytes of SHA-256(message). - * Returns base64(IV + AuthTag + EncryptedData). - */ -export async function encryptIntegrationTokens(plaintext: string, keyHex: string): Promise { - if (typeof crypto === 'undefined' || !crypto.subtle) { - throw new Error('Web Crypto API is not available for encryption'); - } - - const keyBytes = hexToBytes(keyHex); - if (keyBytes.length !== 32) { - throw new Error('Invalid encryption key: expected 32-byte AES-GCM key'); - } - - try { - const payload = JSON.parse(plaintext) as Record; - if (typeof payload.expiresAt !== 'string' || !payload.expiresAt.trim()) { - throw new Error( - 'Payload must include a non-empty expiresAt field when using deterministic IV' - ); - } - } catch (e) { - if (e instanceof SyntaxError) { - throw new Error( - 'Plaintext must be JSON with a non-empty expiresAt field when using deterministic IV' - ); - } - throw e; - } - - const messageBytes = new TextEncoder().encode(plaintext); - - // Deterministic IV for backend compatibility. TODO: Prefer a random 12-byte IV - // (crypto.getRandomValues) prepended to ciphertext; update decrypt to handle both formats. - const hashBuffer = await crypto.subtle.digest('SHA-256', messageBytes); - const iv = new Uint8Array(hashBuffer).slice(0, 16); - - const cryptoKey = await crypto.subtle.importKey( - 'raw', - keyBytes as unknown as BufferSource, - { name: 'AES-GCM' }, - false, - ['encrypt'] - ); - - const encryptedBuffer = await crypto.subtle.encrypt( - { name: 'AES-GCM', iv, tagLength: 128 }, - cryptoKey, - messageBytes - ); - - const encryptedArray = new Uint8Array(encryptedBuffer); - const authTag = encryptedArray.slice(-16); - const encryptedData = encryptedArray.slice(0, -16); - - const combined = new Uint8Array(iv.length + authTag.length + encryptedData.length); - combined.set(iv, 0); - combined.set(authTag, iv.length); - combined.set(encryptedData, iv.length + authTag.length); - - let binary = ''; - for (let i = 0; i < combined.length; i++) { - binary += String.fromCharCode(combined[i]); - } - return btoa(binary); -} diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 56f7a219b..2a1ea369b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -81,16 +81,16 @@ The frontend communicates with the **openhuman** Rust core in two ways: **Tauri OpenHuman chose Tauri + Rust over Electron for fundamental performance and security reasons: -| Metric | OpenHuman (Tauri + Rust) | Typical Electron App | -| ------------------------- | ------------------------------ | ---------------------------- | -| Binary size | ~30 MB | ~150 MB+ | -| Memory per skill context | ~1-2 MB (QuickJS) | ~150 MB+ (Chromium renderer) | -| Cold startup | Sub-500ms | 2-5 seconds | -| Garbage collection pauses | None (Rust ownership model) | V8 GC pauses | -| Memory safety | Compile-time guaranteed | Runtime exceptions | -| TLS implementation | rustls (no OpenSSL dependency) | Chromium's BoringSSL | +| Metric | OpenHuman (Tauri + Rust) | Typical Electron App | +| ------------------------- | -------------------------------------------------------- | ---------------------------- | +| Binary size | Feature-dependent (CEF runtime + skills bundle dominate) | ~150 MB+ | +| Memory per skill context | ~1-2 MB (QuickJS) | ~150 MB+ (Chromium renderer) | +| Cold startup | Sub-500ms | 2-5 seconds | +| Garbage collection pauses | None (Rust ownership model) | V8 GC pauses | +| Memory safety | Compile-time guaranteed | Runtime exceptions | +| TLS implementation | rustls (no OpenSSL dependency) | Chromium's BoringSSL | -**Why this matters for a crypto platform**: Traders and analysts run OpenHuman alongside resource-intensive tools — charting software, multiple browser tabs, trading terminals. A 30 MB footprint with sub-500ms startup means the app feels native and stays out of the way. Zero GC pauses means real-time price feeds and alerts are never delayed by memory management. +**Why this matters for a crypto platform**: Traders and analysts run OpenHuman alongside resource-intensive tools — charting software, multiple browser tabs, trading terminals. A native binary with sub-500ms startup means the app feels native and stays out of the way. Zero GC pauses means real-time price feeds and alerts are never delayed by memory management. The **Tokio async runtime** drives all I/O — WebSocket connections, HTTP requests, file operations, and inter-skill communication — as non-blocking tasks on a thread pool. Thousands of concurrent operations (skill executions, cron jobs, socket events) share a small fixed set of OS threads. diff --git a/src/openhuman/doctor/core.rs b/src/openhuman/doctor/core.rs index 760df6321..e2313a06c 100644 --- a/src/openhuman/doctor/core.rs +++ b/src/openhuman/doctor/core.rs @@ -466,27 +466,68 @@ fn check_workspace(config: &Config, items: &mut Vec) { } fn available_disk_space_mb(path: &Path) -> Option { - if std::env::consts::OS == "windows" { - let _ = path; // TODO: add a Windows-specific implementation - return None; + #[cfg(target_os = "windows")] + { + return available_disk_space_mb_windows(path); } - let output = std::process::Command::new("df") - .arg("-m") - .arg(path) + #[cfg(not(target_os = "windows"))] + { + let output = std::process::Command::new("df") + .arg("-m") + .arg(path) + .output() + .ok()?; + if !output.status.success() { + return None; + } + let stdout = String::from_utf8_lossy(&output.stdout); + parse_df_available_mb(&stdout) + } +} + +#[cfg(not(target_os = "windows"))] +fn parse_df_available_mb(stdout: &str) -> Option { + let line = stdout.lines().rev().find(|line| !line.trim().is_empty())?; + let avail = line.split_whitespace().nth(3)?; + avail.parse::().ok() +} + +#[cfg(target_os = "windows")] +fn available_disk_space_mb_windows(path: &Path) -> Option { + use std::path::{Component, Prefix}; + + let canonical = std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf()); + let letter = canonical.components().find_map(|c| match c { + Component::Prefix(pc) => match pc.kind() { + Prefix::Disk(b) | Prefix::VerbatimDisk(b) => Some((b as char).to_ascii_uppercase()), + _ => None, + }, + _ => None, + })?; + + // PowerShell is ubiquitous on supported Windows; `Get-PSDrive` needs no admin + // and returns free bytes as a single integer line. + let script = format!("(Get-PSDrive -Name {letter} -ErrorAction Stop).Free"); + let output = std::process::Command::new("powershell") + .args([ + "-NoProfile", + "-NonInteractive", + "-ExecutionPolicy", + "Bypass", + "-Command", + &script, + ]) .output() .ok()?; if !output.status.success() { return None; } - let stdout = String::from_utf8_lossy(&output.stdout); - parse_df_available_mb(&stdout) -} - -fn parse_df_available_mb(stdout: &str) -> Option { - let line = stdout.lines().rev().find(|line| !line.trim().is_empty())?; - let avail = line.split_whitespace().nth(3)?; - avail.parse::().ok() + let bytes: u64 = String::from_utf8_lossy(&output.stdout) + .trim() + .parse() + .ok()?; + Some(bytes / (1024 * 1024)) } fn workspace_probe_path(workspace_dir: &Path) -> std::path::PathBuf {