# MediaVerse — Technology Stack

## 1. Stack Overview

| Layer | Technology | Purpose |
|-------|------------|---------|
| **Frontend** | React 18 + TypeScript | UI components and state |
| **Desktop Framework** | Tauri 2.0 | Native desktop shell |
| **Backend** | Rust | Core business logic |
| **Media Processing** | FFmpeg | Video/audio processing |
| **Download Engine** | yt-dlp | Content extraction |
| **Database** | SQLite | Local data persistence |
| **State Management** | Zustand | Client-side state |
| **Styling** | Tailwind CSS | Utility-first CSS |
| **Icons** | Lucide React | Consistent iconography |
| **Analytics** | PostHog | Product analytics |
| **Error Tracking** | Sentry | Error monitoring |

---

## 2. Frontend Stack

### 2.1 React + TypeScript

**Version:** React 18.2+, TypeScript 5.3+

**Key Libraries:**

| Library | Version | Purpose |
|---------|---------|---------|
| react | 18.2.x | Core framework |
| react-dom | 18.2.x | DOM renderer |
| @types/react | 18.2.x | Type definitions |
| typescript | 5.3.x | Type system |

**Configuration:**

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/hooks/*": ["./src/hooks/*"],
      "@/stores/*": ["./src/stores/*"],
      "@/types/*": ["./src/types/*"],
      "@/utils/*": ["./src/utils/*"]
    }
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}
```

### 2.2 State Management — Zustand

**Version:** 4.4.x

**Why Zustand:**
- Minimal boilerplate vs Redux
- TypeScript-first design
- No provider wrapper needed
- Excellent DevTools support
- Small bundle size (~1KB)

**Store Structure:**

```typescript
// stores/downloadStore.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';

interface DownloadState {
  downloads: Map<string, Download>;
  queue: string[];
  activeCount: number;
  maxConcurrent: number;
  
  // Actions
  addDownload: (download: Download) => void;
  updateProgress: (id: string, progress: Progress) => void;
  pauseDownload: (id: string) => void;
  resumeDownload: (id: string) => void;
  cancelDownload: (id: string) => void;
  removeDownload: (id: string) => void;
  reorderQueue: (from: number, to: number) => void;
}

export const useDownloadStore = create<DownloadState>()(
  devtools(
    immer(
      persist(
        (set, get) => ({
          downloads: new Map(),
          queue: [],
          activeCount: 0,
          maxConcurrent: 4,
          
          addDownload: (download) => set((state) => {
            state.downloads.set(download.id, download);
            state.queue.push(download.id);
          }),
          
          updateProgress: (id, progress) => set((state) => {
            const download = state.downloads.get(id);
            if (download) {
              download.progress = progress;
              download.updatedAt = new Date();
            }
          }),
          
          // ... other actions
        }),
        {
          name: 'download-storage',
          partialize: (state) => ({
            maxConcurrent: state.maxConcurrent,
            // Don't persist active downloads
          }),
        }
      )
    ),
    { name: 'DownloadStore' }
  )
);
```

### 2.3 Styling — Tailwind CSS

**Version:** 3.4.x

**Configuration:**

```javascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  darkMode: 'class',
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          100: '#dbeafe',
          200: '#bfdbfe',
          300: '#93c5fd',
          400: '#60a5fa',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
          800: '#1e40af',
          900: '#1e3a8a',
        },
        surface: {
          50: '#fafafa',
          100: '#f5f5f5',
          200: '#e5e5e5',
          300: '#d4d4d4',
          400: '#a3a3a3',
          500: '#737373',
          600: '#525252',
          700: '#404040',
          800: '#262626',
          900: '#171717',
          950: '#0a0a0a',
        },
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },
      animation: {
        'fade-in': 'fadeIn 0.2s ease-out',
        'slide-up': 'slideUp 0.3s ease-out',
        'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0' },
          '100%': { opacity: '1' },
        },
        slideUp: {
          '0%': { transform: 'translateY(10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
    require('tailwind-scrollbar'),
  ],
};
```

---

## 3. Desktop Framework — Tauri

### 3.1 Tauri vs Electron Comparison

| Aspect | Tauri | Electron |
|--------|-------|----------|
| **Bundle Size** | ~3-5 MB | ~150+ MB |
| **Memory Usage** | ~50-100 MB | ~200-500 MB |
| **Startup Time** | <1s | 2-5s |
| **Security** | Rust-based, memory-safe | V8 sandbox |
| **Native APIs** | Full Rust access | Node.js + native modules |
| **Frontend** | Any web framework | Any web framework |
| **Backend** | Rust | Node.js |
| **Auto-updater** | Built-in | electron-updater |
| **Code Signing** | Built-in | Requires extra setup |
| **Mobile Support** | Planned (iOS/Android) | Capacitor/Cordova |
| **Community** | Growing rapidly | Mature, large |
| **Learning Curve** | Rust required | JavaScript only |

### 3.2 Tauri Configuration

```json
// tauri.conf.json
{
  "build": {
    "beforeBuildCommand": "npm run build",
    "beforeDevCommand": "npm run dev",
    "devPath": "http://localhost:5173",
    "distDir": "../dist"
  },
  "tauri": {
    "allowlist": {
      "all": false,
      "fs": {
        "all": true,
        "scope": ["$APPDATA/**", "$DOWNLOAD/**", "$HOME/MediaVerse/**"]
      },
      "http": {
        "all": true,
        "scope": ["https://*", "http://*"]
      },
      "notification": {
        "all": true
      },
      "os": {
        "all": true
      },
      "path": {
        "all": true
      },
      "process": {
        "all": true
      },
      "shell": {
        "all": false,
        "open": true,
        "scope": [
          {
            "name": "binaries/yt-dlp",
            "cmd": "yt-dlp",
            "args": true
          },
          {
            "name": "binaries/ffmpeg",
            "cmd": "ffmpeg",
            "args": true
          }
        ]
      },
      "window": {
        "all": true
      }
    },
    "bundle": {
      "active": true,
      "category": "Entertainment",
      "copyright": "© 2024 MediaVerse",
      "deb": {
        "depends": ["ffmpeg", "python3"]
      },
      "externalBin": ["yt-dlp", "ffmpeg"],
      "icon": [
        "icons/32x32.png",
        "icons/128x128.png",
        "icons/128x128@2x.png",
        "icons/icon.icns",
        "icons/icon.ico"
      ],
      "identifier": "com.mediaverse.app",
      "longDescription": "Premium media management platform",
      "macOS": {
        "entitlements": "entitlements.plist",
        "exceptionDomain": "",
        "frameworks": [],
        "providerShortName": null,
        "signingIdentity": null
      },
      "resources": [],
      "shortDescription": "MediaVerse",
      "targets": ["msi", "dmg", "appimage", "deb"],
      "windows": {
        "certificateThumbprint": null,
        "digestAlgorithm": "sha256",
        "timestampUrl": ""
      }
    },
    "security": {
      "csp": "default-src 'self'; connect-src 'self' https:; img-src 'self' https: data:; script-src 'self'; style-src 'self' 'unsafe-inline'"
    },
    "updater": {
      "active": true,
      "endpoints": ["https://updates.mediaverse.app/v1/{{target}}/{{arch}}/{{current_version}}"],
      "dialog": true,
      "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFBQkNDRUZ..."
    },
    "windows": [
      {
        "fullscreen": false,
        "height": 800,
        "resizable": true,
        "title": "MediaVerse",
        "width": 1200,
        "minWidth": 900,
        "minHeight": 600,
        "center": true,
        "decorations": true,
        "transparent": false
      }
    ]
  }
}
```

### 3.3 Tauri Commands

```rust
// src-tauri/src/commands/download.rs
use tauri::State;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize)]
pub struct DownloadRequest {
    pub url: String,
    pub quality: Option<String>,
    pub format: Option<String>,
    pub output_path: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct DownloadResponse {
    pub id: String,
    pub status: String,
    pub message: String,
}

#[tauri::command]
pub async fn create_download(
    request: DownloadRequest,
    download_manager: State<'_, DownloadManager>,
) -> Result<DownloadResponse, String> {
    let download = download_manager
        .create_download(request)
        .await
        .map_err(|e| e.to_string())?;
    
    Ok(DownloadResponse {
        id: download.id.to_string(),
        status: "queued".to_string(),
        message: "Download added to queue".to_string(),
    })
}

#[tauri::command]
pub async fn get_download_progress(
    id: String,
    download_manager: State<'_, DownloadManager>,
) -> Result<ProgressInfo, String> {
    download_manager
        .get_progress(&id)
        .await
        .map_err(|e| e.to_string())
}

// Event emitter for progress updates
#[tauri::command]
pub async fn subscribe_download_events(
    window: tauri::Window,
    download_manager: State<'_, DownloadManager>,
) -> Result<(), String> {
    let mut rx = download_manager.subscribe_events().await;
    
    tauri::async_runtime::spawn(async move {
        while let Some(event) = rx.recv().await {
            window
                .emit("download:progress", &event)
                .expect("failed to emit event");
        }
    });
    
    Ok(())
}
```

---

## 4. Backend Stack — Rust

### 4.1 Core Dependencies

```toml
# Cargo.toml
[package]
name = "mediaverse-core"
version = "0.1.0"
edition = "2021"

[dependencies]
# Async runtime
tokio = { version = "1.35", features = ["full"] }

# Serialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

# Database
rusqlite = { version = "0.30", features = ["bundled", "chrono", "uuid"] }
diesel = { version = "2.1", features = ["sqlite", "chrono"] }

# HTTP client
reqwest = { version = "0.11", features = ["json", "stream"] }

# Error handling
thiserror = "1.0"
anyhow = "1.0"

# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

# Configuration
config = "0.14"
dotenvy = "0.15"

# Process management
tokio-process = "0.2"

# File system
walkdir = "2.4"
tempfile = "3.9"

# Cryptography
ring = "0.17"
base64 = "0.21"

# Time
chrono = { version = "0.4", features = ["serde"] }

# UUID
uuid = { version = "1.6", features = ["v4", "serde"] }

# Regex
regex = "1.10"

# Tauri integration
tauri = { version = "1.5", features = ["shell-open"] }

[dev-dependencies]
tokio-test = "0.4"
mockall = "0.12"
criterion = { version = "0.5", features = ["async_tokio"] }
```

### 4.2 Project Structure

```
src-tauri/
├── Cargo.toml
├── tauri.conf.json
└── src/
    ├── main.rs                 # Application entry
    ├── lib.rs                  # Library exports
    ├── commands/               # Tauri command handlers
    │   ├── mod.rs
    │   ├── download.rs
    │   ├── media.rs
    │   ├── automation.rs
    │   └── settings.rs
    ├── modules/                # Domain modules
    │   ├── mod.rs
    │   ├── downloader/
    │   │   ├── mod.rs
    │   │   ├── manager.rs
    │   │   ├── queue.rs
    │   │   ├── worker.rs
    │   │   └── models.rs
    │   ├── media_manager/
    │   │   ├── mod.rs
    │   │   ├── library.rs
    │   │   ├── search.rs
    │   │   ├── thumbnails.rs
    │   │   └── models.rs
    │   └── automation/
    │       ├── mod.rs
    │       ├── engine.rs
    │       ├── scheduler.rs
    │       └── models.rs
    ├── infrastructure/         # External adapters
    │   ├── mod.rs
    │   ├── database.rs
    │   ├── filesystem.rs
    │   ├── http.rs
    │   ├── ffmpeg.rs
    │   └── ytdlp.rs
    ├── core/                   # Shared core
    │   ├── mod.rs
    │   ├── events.rs
    │   ├── config.rs
    │   ├── errors.rs
    │   └── logging.rs
    └── utils/                  # Utilities
        ├── mod.rs
        ├── paths.rs
        ├── validation.rs
        └── formatting.rs
```

---

## 5. Media Processing — FFmpeg

### 5.1 FFmpeg Integration

```rust
// src/infrastructure/ffmpeg.rs
use std::path::{Path, PathBuf};
use std::process::Stdio;
use tokio::process::Command;

pub struct FFmpeg {
    binary_path: PathBuf,
}

#[derive(Debug, Clone)]
pub struct ConversionOptions {
    pub video_codec: Option<String>,
    pub audio_codec: Option<String>,
    pub video_bitrate: Option<String>,
    pub audio_bitrate: Option<String>,
    pub resolution: Option<(u32, u32)>,
    pub frame_rate: Option<f32>,
    pub audio_only: bool,
    pub preserve_metadata: bool,
}

impl FFmpeg {
    pub fn new(binary_path: impl AsRef<Path>) -> Self {
        Self {
            binary_path: binary_path.as_ref().to_path_buf(),
        }
    }
    
    pub async fn convert(
        &self,
        input: &Path,
        output: &Path,
        options: &ConversionOptions,
    ) -> Result<(), FFmpegError> {
        let mut cmd = Command::new(&self.binary_path);
        
        cmd.arg("-i").arg(input)
            .arg("-y")
            .arg("-progress").arg("pipe:1")
            .arg("-nostats");
        
        // Video options
        if options.audio_only {
            cmd.arg("-vn");
        } else {
            if let Some(codec) = &options.video_codec {
                cmd.arg("-c:v").arg(codec);
            }
            if let Some(bitrate) = &options.video_bitrate {
                cmd.arg("-b:v").arg(bitrate);
            }
            if let Some((w, h)) = options.resolution {
                cmd.arg("-vf").arg(format!("scale={}:{}", w, h));
            }
            if let Some(fps) = options.frame_rate {
                cmd.arg("-r").arg(fps.to_string());
            }
        }
        
        // Audio options
        if let Some(codec) = &options.audio_codec {
            cmd.arg("-c:a").arg(codec);
        }
        if let Some(bitrate) = &options.audio_bitrate {
            cmd.arg("-b:a").arg(bitrate);
        }
        
        // Metadata
        if options.preserve_metadata {
            cmd.arg("-map_metadata").arg("0");
        }
        
        cmd.arg(output);
        
        let output = cmd
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()?
            .wait_with_output()
            .await?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(FFmpegError::ConversionFailed(stderr.to_string()));
        }
        
        Ok(())
    }
    
    pub async fn extract_thumbnail(
        &self,
        input: &Path,
        timestamp: f64,
        output: &Path,
        width: u32,
        height: u32,
    ) -> Result<(), FFmpegError> {
        let mut cmd = Command::new(&self.binary_path);
        
        cmd.arg("-ss").arg(timestamp.to_string())
            .arg("-i").arg(input)
            .arg("-vframes").arg("1")
            .arg("-s").arg(format!("{}x{}", width, height))
            .arg("-f").arg("image2")
            .arg("-c:v").arg("mjpeg")
            .arg("-q:v").arg("2")
            .arg("-y")
            .arg(output);
        
        let output = cmd.output().await?;
        
        if !output.status.success() {
            return Err(FFmpegError::ThumbnailExtractionFailed);
        }
        
        Ok(())
    }
    
    pub async fn probe(&self, input: &Path) -> Result<MediaInfo, FFmpegError> {
        let output = Command::new(&self.binary_path)
            .arg("-v").arg("quiet")
            .arg("-print_format").arg("json")
            .arg("-show_format")
            .arg("-show_streams")
            .arg(input)
            .output()
            .await?;
        
        if !output.status.success() {
            return Err(FFmpegError::ProbeFailed);
        }
        
        let info: MediaInfo = serde_json::from_slice(&output.stdout)?;
        Ok(info)
    }
}
```

---

## 6. Database — SQLite

### 6.1 Why SQLite

| Advantage | Explanation |
|-----------|-------------|
| **Zero Configuration** | No server setup required |
| **Single File** | Easy backup and migration |
| **Cross-Platform** | Works on all target platforms |
| **Small Footprint** | ~1MB library size |
| **ACID Compliant** | Full transaction support |
| **Fast Enough** | Handles millions of rows |
| **Full-Text Search** | Built-in FTS5 extension |

### 6.2 Database Configuration

```rust
// src/infrastructure/database.rs
use rusqlite::{Connection, Result, params};
use std::path::Path;

pub struct Database {
    conn: Connection,
}

impl Database {
    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
        let conn = Connection::open(path)?;
        
        // Enable WAL mode for better concurrency
        conn.execute_batch("
            PRAGMA journal_mode = WAL;
            PRAGMA synchronous = NORMAL;
            PRAGMA cache_size = 10000;
            PRAGMA temp_store = MEMORY;
            PRAGMA mmap_size = 30000000000;
        ")?;
        
        // Initialize schema
        Self::init_schema(&conn)?;
        
        Ok(Self { conn })
    }
    
    fn init_schema(conn: &Connection) -> Result<()> {
        conn.execute_batch(include_str!("../schema.sql"))?;
        Ok(())
    }
    
    pub fn connection(&self) -> &Connection {
        &self.conn
    }
    
    pub fn transaction<T>(&self, f: impl FnOnce(&Connection) -> Result<T>) -> Result<T> {
        let tx = self.conn.transaction()?;
        let result = f(&tx)?;
        tx.commit()?;
        Ok(result)
    }
}
```

---

## 7. Folder Structure

### 7.1 Complete Project Structure

```
mediaverse/
├── .github/                      # GitHub Actions, templates
│   ├── workflows/
│   │   ├── ci.yml
│   │   ├── release.yml
│   │   └── test.yml
│   └── PULL_REQUEST_TEMPLATE.md
├── docs/                         # Documentation
│   ├── architecture/
│   ├── api/
│   └── user-guide/
├── scripts/                      # Build scripts
│   ├── build.sh
│   ├── release.sh
│   └── setup.sh
├── src/                          # Frontend source
│   ├── components/               # React components
│   │   ├── ui/                   # Base UI components
│   │   │   ├── Button.tsx
│   │   │   ├── Input.tsx
│   │   │   ├── Card.tsx
│   │   │   ├── Dialog.tsx
│   │   │   ├── Dropdown.tsx
│   │   │   ├── Progress.tsx
│   │   │   ├── Skeleton.tsx
│   │   │   ├── Toast.tsx
│   │   │   └── Tooltip.tsx
│   │   ├── layout/               # Layout components
│   │   │   ├── Sidebar.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── MainContent.tsx
│   │   │   └── PlayerBar.tsx
│   │   ├── download/             # Download-specific
│   │   │   ├── DownloadList.tsx
│   │   │   ├── DownloadItem.tsx
│   │   │   ├── AddDownload.tsx
│   │   │   ├── QualitySelector.tsx
│   │   │   └── ProgressBar.tsx
│   │   ├── media/                # Media manager
│   │   │   ├── MediaGrid.tsx
│   │   │   ├── MediaList.tsx
│   │   │   ├── MediaCard.tsx
│   │   │   ├── MediaDetail.tsx
│   │   │   ├── FolderTree.tsx
│   │   │   ├── CollectionView.tsx
│   │   │   ├── SearchBar.tsx
│   │   │   ├── FilterPanel.tsx
│   │   │   └── TagCloud.tsx
│   │   ├── player/               # Media player
│   │   │   ├── VideoPlayer.tsx
│   │   │   ├── AudioPlayer.tsx
│   │   │   ├── PlayerControls.tsx
│   │   │   ├── Timeline.tsx
│   │   │   └── VolumeControl.tsx
│   │   ├── automation/           # Automation UI
│   │   │   ├── WorkflowBuilder.tsx
│   │   │   ├── RuleEditor.tsx
│   │   │   ├── TriggerSelector.tsx
│   │   │   ├── ActionLibrary.tsx
│   │   │   └── ExecutionLog.tsx
│   │   └── settings/             # Settings UI
│   │       ├── SettingsPanel.tsx
│   │       ├── GeneralSettings.tsx
│   │       ├── DownloadSettings.tsx
│   │       ├── MediaSettings.tsx
│   │       ├── AutomationSettings.tsx
│   │       ├── ShortcutSettings.tsx
│   │       └── AboutPanel.tsx
│   ├── hooks/                    # Custom React hooks
│   │   ├── useDownloads.ts
│   │   ├── useMedia.ts
│   │   ├── useSearch.ts
│   │   ├── usePlayer.ts
│   │   ├── useAutomation.ts
│   │   ├── useSettings.ts
│   │   ├── useTheme.ts
│   │   ├── useKeyboard.ts
│   │   └── useTauri.ts
│   ├── stores/                   # Zustand stores
│   │   ├── downloadStore.ts
│   │   ├── mediaStore.ts
│   │   ├── playerStore.ts
│   │   ├── automationStore.ts
│   │   ├── settingsStore.ts
│   │   └── uiStore.ts
│   ├── types/                    # TypeScript types
│   │   ├── download.ts
│   │   ├── media.ts
│   │   ├── automation.ts
│   │   ├── settings.ts
│   │   ├── api.ts
│   │   └── index.ts
│   ├── utils/                    # Utility functions
│   │   ├── format.ts             # Formatting utilities
│   │   ├── validation.ts         # Validation
│   │   ├── storage.ts            # Local storage
│   │   ├── constants.ts          # Constants
│   │   └── helpers.ts            # Misc helpers
│   ├── services/                 # API services
│   │   ├── downloadService.ts
│   │   ├── mediaService.ts
│   │   ├── automationService.ts
│   │   └── settingsService.ts
│   ├── styles/                   # Global styles
│   │   ├── globals.css
│   │   └── animations.css
│   ├── App.tsx                   # Root component
│   ├── main.tsx                  # Entry point
│   └── vite-env.d.ts
├── src-tauri/                    # Rust backend
│   ├── Cargo.toml
│   ├── tauri.conf.json
│   ├── build.rs
│   └── src/
│       ├── main.rs
│       ├── lib.rs
│       ├── commands/
│       ├── modules/
│       ├── infrastructure/
│       ├── core/
│       └── utils/
├── resources/                    # Static resources
│   ├── icons/
│   ├── images/
│   ├── fonts/
│   └── binaries/                 # Bundled binaries
│       ├── yt-dlp
│       └── ffmpeg
├── tests/                        # Test files
│   ├── unit/
│   ├── integration/
│   └── e2e/
├── .eslintrc.cjs
├── .prettierrc
├── .gitignore
├── index.html
├── package.json
├── tailwind.config.js
├── tsconfig.json
├── vite.config.ts
└── README.md
```

---

## 8. Development Tools

### 8.1 Build Tools

| Tool | Purpose | Configuration |
|------|---------|---------------|
| **Vite** | Build tool | `vite.config.ts` |
| **ESLint** | Linting | `.eslintrc.cjs` |
| **Prettier** | Formatting | `.prettierrc` |
| **TypeScript** | Type checking | `tsconfig.json` |
| **Cargo** | Rust build | `Cargo.toml` |

### 8.2 Testing Stack

| Tool | Purpose |
|------|---------|
| **Vitest** | Unit testing (Vite-native) |
| **React Testing Library** | Component testing |
| **Playwright** | E2E testing |
| **Cypress** | Alternative E2E |
| **cargo test** | Rust unit tests |
| **Criterion** | Rust benchmarks |

### 8.3 CI/CD Pipeline

```yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test-frontend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm run type-check
      - run: npm run test:unit
      - run: npm run build

  test-backend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-action@stable
      - run: cargo test --all-features
      - run: cargo clippy -- -D warnings
      - run: cargo fmt --check

  build:
    needs: [test-frontend, test-backend]
    strategy:
      matrix:
        platform: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.platform }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - uses: dtolnay/rust-action@stable
      - run: npm ci
      - run: npm run tauri build
```

---

*Document Version: 1.0*
*Last Updated: 2026-05-27*
*Owner: Engineering Team*