mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 05:12:33 +00:00
chore: drop dead crypto + stale rules, Windows doctor disk probe (#856)
Two cleanups surfaced during a codebase review: ## Changes - **Drop dead integration-token crypto** — `integrationTokensCrypto.ts` + helpers; no remaining consumers - **Trim stale `.claude/rules`** — rules that contradict current CLAUDE.md - **Windows doctor disk probe** — detect low-disk conditions on Windows - **Refresh stale binary-size claim** in docs Net: +82 / −3,596 (mostly dead-code removal). ## Test plan - [ ] `yarn typecheck` green — no dangling imports to crypto module - [ ] `cargo check` green - [ ] Doctor runs on Windows host Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
This commit is contained in:
co-authored by
Steven Enamakel
parent
1408666706
commit
53befcd56f
@@ -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_
|
||||
@@ -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
|
||||
@@ -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 <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 <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` |
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
```
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
@@ -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<String, String> {
|
||||
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<i32>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn increment(state: State<AppState>) -> 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<string>('greet', { name: 'World' });
|
||||
|
||||
// With error handling
|
||||
try {
|
||||
const data = await invoke<string>('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
|
||||
}
|
||||
```
|
||||
@@ -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<string>('greet', { name: 'User' });
|
||||
setResult(greeting);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={handleClick}>Greet</button>
|
||||
<p>{result}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="flex items-center justify-between p-4">
|
||||
<h1>Chat</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>{profile?.username}</span>
|
||||
<div
|
||||
className={`w-2 h-2 rounded-full ${
|
||||
isConnected ? "bg-sage-500" : "bg-coral-500"
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 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<typeof messageSchema>;
|
||||
|
||||
function MessageInput() {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm<MessageForm>({
|
||||
resolver: zodResolver(messageSchema),
|
||||
});
|
||||
|
||||
const onSubmit = (data: MessageForm) => {
|
||||
// Send message via Tauri IPC
|
||||
invoke("send_message", data);
|
||||
reset();
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="flex gap-2">
|
||||
<input
|
||||
{...register("content")}
|
||||
placeholder="Type a message..."
|
||||
className="flex-1 p-2 border rounded"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
{errors.content && (
|
||||
<p className="text-red-500">{errors.content.message}</p>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 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<Channel[]>("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 <div>Loading channels...</div>;
|
||||
if (error) return <div>Error loading channels</div>;
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{channels?.map((channel) => (
|
||||
<div key={channel.id} className="p-2 hover:bg-gray-100 rounded">
|
||||
{channel.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
@@ -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`
|
||||
@@ -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_
|
||||
@@ -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._
|
||||
@@ -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=<loginToken>`
|
||||
|
||||
### 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<serde_json::Value, String>
|
||||
```
|
||||
|
||||
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_
|
||||
@@ -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_
|
||||
@@ -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._
|
||||
@@ -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<dyn std::error::Error>> {
|
||||
// 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<boolean>('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_
|
||||
@@ -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<MemoryClient>`
|
||||
in `MemoryState` (a `Mutex<Option<MemoryClientRef>>`).
|
||||
|
||||
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<ToolContent>, 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) |
|
||||
@@ -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<T>` 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.
|
||||
@@ -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<string> {
|
||||
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<string> {
|
||||
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<string, unknown>;
|
||||
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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -466,27 +466,68 @@ fn check_workspace(config: &Config, items: &mut Vec<DiagnosticItem>) {
|
||||
}
|
||||
|
||||
fn available_disk_space_mb(path: &Path) -> Option<u64> {
|
||||
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<u64> {
|
||||
let line = stdout.lines().rev().find(|line| !line.trim().is_empty())?;
|
||||
let avail = line.split_whitespace().nth(3)?;
|
||||
avail.parse::<u64>().ok()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn available_disk_space_mb_windows(path: &Path) -> Option<u64> {
|
||||
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<u64> {
|
||||
let line = stdout.lines().rev().find(|line| !line.trim().is_empty())?;
|
||||
let avail = line.split_whitespace().nth(3)?;
|
||||
avail.parse::<u64>().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 {
|
||||
|
||||
Reference in New Issue
Block a user