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:
Jwalin Shah
2026-04-23 22:14:48 -07:00
committed by GitHub
co-authored by Steven Enamakel
parent 1408666706
commit 53befcd56f
22 changed files with 83 additions and 3597 deletions
-43
View File
@@ -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_
-97
View File
@@ -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
-187
View File
@@ -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.
-90
View File
@@ -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"
```
-123
View File
@@ -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")
}
}
}
```
-134
View File
@@ -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
-174
View File
@@ -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
}
```
-450
View File
@@ -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
-167
View File
@@ -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`
-169
View File
@@ -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_
-276
View File
@@ -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_
-258
View File
@@ -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) |
+19
View File
@@ -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.
-171
View File
@@ -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);
}
+9 -9
View File
@@ -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.
+55 -14
View File
@@ -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 {