generated from nhcarrigan/template
e6e9f7ae59
## Summary A large productivity-focused feature branch delivering a suite of improvements across automation, project management, theming, performance, and documentation. ### Features - **Guided Project Workflow** (#189) — Four-phase workflow panel (Discuss → Plan → Execute → Verify) to keep projects structured from idea to completion - **Automated Task Loop** (#179) — Per-task conversation orchestration with wave-based parallel execution, blocked-task detection, and concurrency control - **Wave-Based Parallel Execution** (#191) — Tasks run in dependency-aware waves with configurable concurrency; independent tasks execute in parallel - **Auto-Commit After Task Completion** (#192) — Task Loop optionally commits after each completed task so progress is never lost - **PRD Creator** (#180) — AI-assisted PRD and task list panel that outputs `hikari-tasks.json` for the Task Loop to consume - **Project Context Panel** (#188) — Persistent `PROJECT.md`, `REQUIREMENTS.md`, `ROADMAP.md`, and `STATE.md` files injected into Claude's context automatically - **Codebase Mapper** (#190) — Generates a `CODEBASE.md` architectural summary so Claude always understands the project structure - **Community Preset Themes** (#181) — Six built-in community themes: Dracula, Catppuccin Mocha, Nord, Solarized Dark, Gruvbox Dark, and Rosé Pine - **In-App Changelog Panel** (#193) — Fetches release notes from GitHub at runtime and displays them inside the app - **Full Embedded Documentation** (#196) — Replaced the single-page help modal with a 12-page paginated docs browser featuring a sidebar TOC, prev/next navigation, keyboard navigation (arrow keys, `?` shortcut), and comprehensive coverage of every feature ### Performance & Fixes - **Lazy Loading & Virtualisation** (#194) — Virtual windowing for conversation history, markdown memoisation, and debounced search for smooth rendering of large sessions - **Ctrl+C Copy Fix** (#195) — `Ctrl+C` now copies selected text as expected; interrupt-Claude behaviour only fires when no text is selected ### UX - Back-to-workflow button in PRD Creator and Task Loop panels for easy navigation - Navigation icon cluster replaced with a single clean dropdown menu ## Closes Closes #179 Closes #180 Closes #181 Closes #188 Closes #189 Closes #190 Closes #191 Closes #192 Closes #193 Closes #194 Closes #195 Closes #196 --- ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #197 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
459 lines
26 KiB
Markdown
459 lines
26 KiB
Markdown
# Hikari Desktop — Codebase Map
|
||
|
||
> Auto-generated codebase overview. Last updated: 2026-03-06.
|
||
|
||
## Overview
|
||
|
||
Hikari Desktop is a **Tauri v2** desktop application that wraps the Claude Code CLI with a visual anime character avatar (Hikari) who appears on-screen and reacts in real-time to Claude's activity. When Claude is thinking, she thinks. When it's editing code, she codes. When it's using MCP tools, she glows with magical energy.
|
||
|
||
The app supports multiple simultaneous conversations (tabs), each with its own isolated Claude CLI process. It provides a rich UI layer on top of Claude Code, including a built-in file editor, git panel, achievement system, cost tracking, session history, notifications, and more.
|
||
|
||
**Repositories:**
|
||
|
||
- Primary: `git.nhcarrigan.com` (Gitea) — `nhcarrigan/hikari-desktop`
|
||
- Mirror: `github.com/naomi-lgbt/hikari-desktop`
|
||
|
||
**Current version:** `1.10.0`
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
The application follows a standard Tauri architecture:
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ Frontend (WebView) │
|
||
│ SvelteKit + Svelte 5 + TailwindCSS 4 + TypeScript │
|
||
│ │
|
||
│ ┌─────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────┐ │
|
||
│ │AnimeGirl│ │ Terminal │ │ InputBar │ │ Editor │ │
|
||
│ │ Sprites │ │ View │ │ + Slash Cmds│ │CodeMirror│ │
|
||
│ └────┬────┘ └────┬─────┘ └──────┬───────┘ └────┬─────┘ │
|
||
│ │ │ │ │ │
|
||
│ ┌────▼─────────────▼───────────────▼────────────────▼──────┐ │
|
||
│ │ Svelte Stores (reactive state) │ │
|
||
│ │ conversations · character · config · agents · stats … │ │
|
||
│ └──────────────────────────┬───────────────────────────────┘ │
|
||
│ │ tauri.ts (event listeners) │
|
||
└─────────────────────────────┼────────────────────────────────┘
|
||
│ Tauri IPC (invoke / emit)
|
||
┌─────────────────────────────┼────────────────────────────────┐
|
||
│ Backend (Rust) │
|
||
│ ┌──────────────────────────▼───────────────────────────────┐ │
|
||
│ │ commands.rs (invoke handlers) │ │
|
||
│ └──────────────────────────┬───────────────────────────────┘ │
|
||
│ │ │
|
||
│ ┌──────────────────────────▼───────────────────────────────┐ │
|
||
│ │ BridgeManager — HashMap<conversation_id, WslBridge> │ │
|
||
│ └──────────────────────────┬───────────────────────────────┘ │
|
||
│ │ │
|
||
│ ┌──────────────────────────▼───────────────────────────────┐ │
|
||
│ │ WslBridge — spawns `claude --output-format stream-json`│ │
|
||
│ │ reads NDJSON stdout → emits events to frontend │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ config · stats · cost_tracking · sessions · git · clipboard │
|
||
│ achievements · discord_rpc · notifications · snippets … │
|
||
└──────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Directory Structure
|
||
|
||
```
|
||
hikari-desktop/
|
||
├── src/ # SvelteKit frontend
|
||
│ ├── routes/
|
||
│ │ ├── +page.svelte # Main app layout (root page)
|
||
│ │ ├── +layout.svelte # App-level layout wrapper
|
||
│ │ ├── +layout.ts # SvelteKit layout config (SSR disabled)
|
||
│ │ └── test-achievement/ # Dev-only achievement test page
|
||
│ ├── lib/
|
||
│ │ ├── tauri.ts # Tauri event listeners + IPC bridge
|
||
│ │ ├── commands/ # Slash command definitions
|
||
│ │ ├── components/ # 60+ Svelte components
|
||
│ │ │ └── editor/ # CodeMirror-based file editor components
|
||
│ │ ├── notifications/ # Notification system
|
||
│ │ ├── sounds/ # Sound effect triggers
|
||
│ │ ├── stores/ # All Svelte reactive stores
|
||
│ │ ├── types/ # TypeScript type definitions
|
||
│ │ └── utils/ # Pure utility functions
|
||
│ ├── app.css # Global styles + CSS variables (themes)
|
||
│ └── app.html # HTML shell
|
||
│
|
||
├── src-tauri/ # Tauri Rust backend
|
||
│ ├── src/
|
||
│ │ ├── main.rs # Process entry point
|
||
│ │ ├── lib.rs # Tauri app setup + command registration
|
||
│ │ ├── types.rs # All shared Rust types + serialisation
|
||
│ │ ├── wsl_bridge.rs # Claude CLI process management + NDJSON parser
|
||
│ │ ├── bridge_manager.rs # Per-conversation WslBridge registry
|
||
│ │ ├── commands.rs # All #[tauri::command] handlers
|
||
│ │ ├── config.rs # Config read/write (tauri-plugin-store)
|
||
│ │ ├── stats.rs # Token usage + cost calculation
|
||
│ │ ├── cost_tracking.rs # Budget alerts + cost history (CSV export)
|
||
│ │ ├── achievements.rs # Achievement unlock logic
|
||
│ │ ├── sessions.rs # Conversation session persistence (JSON)
|
||
│ │ ├── git.rs # Git operations via CLI
|
||
│ │ ├── clipboard.rs # Clipboard history management
|
||
│ │ ├── notifications.rs # System notification dispatch
|
||
│ │ ├── discord_rpc.rs # Discord Rich Presence manager
|
||
│ │ ├── drafts.rs # Draft message persistence
|
||
│ │ ├── snippets.rs # Snippet library CRUD
|
||
│ │ ├── quick_actions.rs # Quick action CRUD
|
||
│ │ ├── debug_logger.rs # TauriLogLayer (routes tracing → frontend)
|
||
│ │ ├── temp_manager.rs # Temporary file lifecycle management
|
||
│ │ ├── tool_cache.rs # Tool call result caching
|
||
│ │ ├── tray.rs # System tray setup
|
||
│ │ ├── process_ext.rs # HideWindow trait (Windows console hiding)
|
||
│ │ ├── vbs_notification.rs # VBScript-based notification fallback (Windows)
|
||
│ │ ├── windows_toast.rs # Windows native toast notifications
|
||
│ │ └── wsl_notifications.rs# WSL notify-send bridge
|
||
│ ├── capabilities/ # Tauri permission capabilities
|
||
│ ├── tests/ # Rust integration tests
|
||
│ ├── Cargo.toml
|
||
│ ├── Cargo.lock
|
||
│ └── tauri.conf.json # Tauri app configuration
|
||
│
|
||
├── static/
|
||
│ ├── sprites/ # Anime character PNG sprites (one per state)
|
||
│ └── sounds/ # MP3 sound effects (connected, working, done…)
|
||
│
|
||
├── check-all.sh # Full QA script (lint → format → types → test)
|
||
├── vitest.config.ts # Frontend test configuration
|
||
├── vitest.setup.ts # Tauri API mocks for tests
|
||
├── svelte.config.js # SvelteKit config (static adapter)
|
||
├── vite.config.js # Vite config
|
||
├── eslint.config.js # ESLint 9 flat config
|
||
├── tsconfig.json # TypeScript config
|
||
└── .gitea/workflows/ # CI/CD (Gitea Actions)
|
||
```
|
||
|
||
---
|
||
|
||
## Key Components
|
||
|
||
### Backend (Rust)
|
||
|
||
#### `wsl_bridge.rs` — Claude CLI Process Manager
|
||
|
||
The most critical backend file. `WslBridge` spawns a single `claude` CLI process per conversation using `--output-format stream-json`, which causes Claude Code to emit NDJSON messages on stdout. A dedicated reader thread consumes stdout line-by-line, parses each line into a `ClaudeMessage` enum variant, and emits the appropriate frontend events.
|
||
|
||
Key responsibilities:
|
||
|
||
- Locates the `claude` binary (checks `~/.local/bin`, `~/.claude/local`, system paths, and falls back to a login-shell `which claude`)
|
||
- Detects WSL environment to handle cross-platform path differences
|
||
- Maps tool names to character states (Read/Glob/Grep → `searching`, Edit/Write → `coding`, `mcp__*` → `mcp`)
|
||
- Batches permission requests from a single assistant message
|
||
- Tracks token usage per session
|
||
|
||
#### `bridge_manager.rs` — Multi-Conversation Orchestrator
|
||
|
||
`BridgeManager` holds a `HashMap<String, WslBridge>` keyed by `conversation_id`. This enables true parallel conversations — each tab has its own isolated Claude process. The manager is wrapped in `Arc<Mutex<BridgeManager>>` (using `parking_lot`) and injected into Tauri's managed state.
|
||
|
||
#### `types.rs` — Shared Type Definitions
|
||
|
||
Defines the complete Claude stream-JSON protocol as Rust enums/structs:
|
||
|
||
- `ClaudeMessage` — top-level message variants: `System`, `Assistant`, `User`, `StreamEvent`, `Result`, `RateLimitEvent`
|
||
- `ContentBlock` — `Text`, `Thinking`, `ToolUse`, `ToolResult`
|
||
- `CharacterState` — `Idle | Thinking | Typing | Searching | Coding | Mcp | Permission | Success | Error`
|
||
- All frontend event types (`OutputEvent`, `StateChangeEvent`, `PermissionPromptEvent`, `AgentStartEvent`, etc.)
|
||
|
||
#### `commands.rs` — IPC Command Handlers
|
||
|
||
Registers all Tauri commands exposed to the frontend. Over 80 commands covering: Claude process management, configuration, stats, sessions, git, clipboard, cost tracking, MCP servers, plugins, drafts, snippets, quick actions, file system operations, authentication, and notifications.
|
||
|
||
#### `debug_logger.rs` — In-App Debug Console
|
||
|
||
A custom `tracing` subscriber layer (`TauriLogLayer`) that captures all `tracing::info!/warn!/error!` calls and emits them as `debug:log` events to the frontend debug console — essential since production Windows builds have no stdout.
|
||
|
||
---
|
||
|
||
### Frontend (TypeScript/Svelte 5)
|
||
|
||
#### `src/routes/+page.svelte` — Root Layout
|
||
|
||
The main page. Renders a two-panel layout:
|
||
|
||
- **Left panel**: `<AnimeGirl>` character display with state-reactive glow effects (trans pride gradient colours per state)
|
||
- **Right panel**: `<Terminal>` + `<InputBar>` (or `<EditorPanel>` when the editor is open)
|
||
|
||
Also handles: global keyboard shortcuts, compact mode (280×400 mini widget), window close confirmation, Discord RPC updates, and background image loading.
|
||
|
||
#### `src/lib/tauri.ts` — Event Bridge
|
||
|
||
Sets up all Tauri event listeners on app mount. Translates backend events into store mutations:
|
||
|
||
| Event | Action |
|
||
| ------------------------ | ----------------------------------------------------------------------- |
|
||
| `claude:connection` | Updates conversation connection status; sends greeting on first connect |
|
||
| `claude:state` | Updates character state; triggers per-conversation sound effects |
|
||
| `claude:output` | Appends lines to the correct conversation's terminal history |
|
||
| `claude:session` | Stores the Claude session ID |
|
||
| `claude:cwd` | Updates working directory (used by the editor) |
|
||
| `claude:permission` | Adds permission requests to conversation state |
|
||
| `claude:agent-start/end` | Updates agent monitor panel |
|
||
| `claude:question` | Stores pending user question |
|
||
|
||
Also manages Discord RPC updates and the session greeting flow.
|
||
|
||
#### `src/lib/stores/conversations.ts` — Core State Store
|
||
|
||
The central state container. Each conversation (`Conversation` interface) tracks:
|
||
|
||
- Terminal lines (`TerminalLine[]`)
|
||
- Connection status, session ID, working directory
|
||
- Character state, processing flag
|
||
- Granted/pending tool permissions
|
||
- Pending user questions
|
||
- Scroll position, attachments, draft text
|
||
- Sound tracking (per-conversation, prevents replays on tab switch)
|
||
- Conversation summary (for compaction)
|
||
|
||
Tab names are randomly chosen from a curated list of whimsical names (Starfall, Moonbeam, Sakura, etc.).
|
||
|
||
#### `src/lib/stores/claude.ts` — Backwards-Compat Facade
|
||
|
||
A thin wrapper that re-exports `conversationsStore` methods under the original `claudeStore` API. Maintains backwards compatibility whilst the codebase migrated to multi-conversation support.
|
||
|
||
#### `src/lib/stores/character.ts` — Character State Store
|
||
|
||
Manages the global character state displayed by `<AnimeGirl>`. Supports `setState()` (persistent) and `setTemporaryState(state, durationMs)` (auto-reverts to `idle` after a timeout — used for success/error flashes).
|
||
|
||
#### `src/lib/utils/stateMapper.ts` — Stream → State Mapping
|
||
|
||
Pure utility that maps Claude stream-JSON message types to `CharacterState` values. Tool categorisation mirrors the Rust side: search tools → `searching`, coding tools → `coding`, MCP tools → `mcp`, Task tool → `thinking`.
|
||
|
||
#### `src/lib/components/`
|
||
|
||
Key components beyond the basics:
|
||
|
||
| Component | Purpose |
|
||
| --------------------------- | ------------------------------------------------------------- |
|
||
| `AnimeGirl.svelte` | Displays the character sprite, subscribes to `characterState` |
|
||
| `Terminal.svelte` | Renders the conversation message history |
|
||
| `InputBar.svelte` | User input with slash command menu, attachment support |
|
||
| `StatusBar.svelte` | Top bar: connection indicator, token/cost stats, controls |
|
||
| `ConversationTabs.svelte` | Multi-tab navigation with per-tab status indicators |
|
||
| `ConfigSidebar.svelte` | Settings panel (model, theme, notifications, budget, etc.) |
|
||
| `PermissionModal.svelte` | Handles tool permission grant/deny UI |
|
||
| `UserQuestionModal.svelte` | Renders `AskUserQuestion` prompts from Claude |
|
||
| `AgentMonitorPanel.svelte` | Live subagent tree with status badges |
|
||
| `GitPanel.svelte` | Git status, diff, stage/unstage, commit, push/pull |
|
||
| `editor/EditorPanel.svelte` | Full CodeMirror editor with file browser and tabs |
|
||
| `DiffViewer.svelte` | Syntax-highlighted diff display |
|
||
| `AchievementsPanel.svelte` | Achievement gallery |
|
||
| `CostSummary.svelte` | Cost breakdown by session/day/week/month |
|
||
| `MemoryBrowserPanel.svelte` | Browse Claude memory files |
|
||
| `McpManagementPanel.svelte` | MCP server configuration UI |
|
||
| `DebugConsole.svelte` | In-app log viewer (receives `debug:log` events) |
|
||
| `ThinkingBlock.svelte` | Collapsible extended thinking display |
|
||
| `ToolCallBlock.svelte` | Formatted tool use/result display |
|
||
|
||
---
|
||
|
||
## Data Flow
|
||
|
||
### User Sends a Message
|
||
|
||
```
|
||
User types → InputBar
|
||
→ invoke("send_prompt", { conversationId, message })
|
||
→ BridgeManager.send_prompt(conversation_id, message)
|
||
→ WslBridge.send_message() → writes JSON to Claude CLI stdin
|
||
```
|
||
|
||
### Claude Responds (NDJSON Stream)
|
||
|
||
```
|
||
Claude CLI stdout (NDJSON)
|
||
→ WslBridge reader thread (line-by-line)
|
||
→ serde_json::from_str::<ClaudeMessage>()
|
||
→ match message type:
|
||
System(init) → emit claude:connection(connected) + claude:cwd
|
||
StreamEvent → emit claude:state(thinking|typing|searching|coding|mcp)
|
||
Assistant → emit claude:output(assistant|tool|thinking lines)
|
||
User(tool_result)→ emit claude:output(tool result lines)
|
||
Result(success) → emit claude:state(success) + claude:output(result)
|
||
Result(error) → emit claude:state(error)
|
||
RateLimitEvent → emit claude:output(rate-limit line)
|
||
PermissionRequest→ emit claude:permission
|
||
```
|
||
|
||
### Frontend Reacts
|
||
|
||
```
|
||
tauri.ts event listeners
|
||
→ conversationsStore mutations
|
||
→ Svelte reactivity propagates to components
|
||
→ AnimeGirl.svelte: sprite changes to match characterState
|
||
→ Terminal.svelte: new line appended
|
||
→ StatusBar.svelte: token counts update
|
||
→ ConversationTabs.svelte: tab glow colour updates
|
||
```
|
||
|
||
### Permission Flow
|
||
|
||
```
|
||
Claude requests tool permission
|
||
→ WslBridge batches pending tool uses
|
||
→ emit claude:permission (one or more requests)
|
||
→ tauri.ts → claudeStore.requestPermissionForConversation()
|
||
→ PermissionModal.svelte renders
|
||
→ User clicks Allow/Deny
|
||
→ invoke("answer_question", { conversationId, toolUseId, granted })
|
||
→ WslBridge.send_tool_result() → writes result to Claude stdin
|
||
→ Claude CLI resumes
|
||
```
|
||
|
||
---
|
||
|
||
## State Machine
|
||
|
||
The `CharacterState` enum drives both the sprite displayed and the panel glow colour:
|
||
|
||
| State | Trigger | Sprite | Panel Glow |
|
||
| ------------ | --------------------------------- | ----------------------- | ---------------------- |
|
||
| `idle` | Connected, no activity | Standing with clipboard | None |
|
||
| `thinking` | Thinking block / Task tool | Hand on chin | Purple/trans gradient |
|
||
| `typing` | Text content block | At keyboard | Blue/trans gradient |
|
||
| `searching` | Read/Glob/Grep/WebSearch/WebFetch | Magnifying glass | Yellow/trans gradient |
|
||
| `coding` | Edit/Write/NotebookEdit | At monitor | Green/trans gradient |
|
||
| `mcp` | Any `mcp__*` tool | Magical blue energy | Trans pride vibrant |
|
||
| `permission` | Permission requested | Confused shrug | — |
|
||
| `success` | Result: success | Celebrating | Emerald/trans gradient |
|
||
| `error` | Result: error | Worried | Red/trans gradient |
|
||
|
||
`success` and `error` are temporary states (3-second auto-revert to `idle`).
|
||
|
||
---
|
||
|
||
## Dependencies
|
||
|
||
### Frontend (key packages)
|
||
|
||
| Package | Purpose |
|
||
| ------------------------------ | -------------------------------------------------------------- |
|
||
| `@sveltejs/kit` `svelte` | SvelteKit framework + Svelte 5 |
|
||
| `@tauri-apps/api` | Core Tauri IPC (`invoke`, `listen`) |
|
||
| `@tauri-apps/plugin-*` | FS, clipboard, notifications, dialog, shell, store, os, opener |
|
||
| `tailwindcss` v4 | Utility-first CSS |
|
||
| `codemirror` + `@codemirror/*` | Code editor with 20+ language modes |
|
||
| `marked` | Markdown → HTML rendering |
|
||
| `highlight.js` | Syntax highlighting in markdown blocks |
|
||
| `lucide-svelte` | Icon library |
|
||
|
||
### Backend (key crates)
|
||
|
||
| Crate | Purpose |
|
||
| -------------------------------- | ---------------------------------------- |
|
||
| `tauri` v2 | Desktop app framework |
|
||
| `tokio` | Async runtime |
|
||
| `serde` / `serde_json` | JSON serialisation/deserialisation |
|
||
| `parking_lot` | Fast mutex (used for `BridgeManager`) |
|
||
| `uuid` | Unique ID generation |
|
||
| `discord-rich-presence` | Discord RPC integration |
|
||
| `chrono` | Date/time handling for cost tracking |
|
||
| `semver` | Version comparison for update checks |
|
||
| `tempfile` | Temporary file management |
|
||
| `tracing` + `tracing-subscriber` | Structured logging |
|
||
| `dirs` | Cross-platform home directory resolution |
|
||
| `windows` (Windows-only) | Native toast notifications |
|
||
|
||
### Dev / Tooling
|
||
|
||
| Tool | Purpose |
|
||
| -------------------------------- | ----------------------------------------- |
|
||
| `vitest` + `@vitest/coverage-v8` | Frontend unit tests with v8 coverage |
|
||
| `@testing-library/svelte` | Component testing utilities |
|
||
| `jsdom` | DOM environment for tests |
|
||
| `eslint` v9 (flat config) | Linting |
|
||
| `prettier` | Formatting |
|
||
| `svelte-check` | TypeScript type checking for Svelte files |
|
||
| `cargo test` + `cargo llvm-cov` | Rust unit tests and coverage |
|
||
|
||
---
|
||
|
||
## Development Notes
|
||
|
||
### Running the App
|
||
|
||
```bash
|
||
# Frontend dev server only
|
||
source ~/.nvm/nvm.sh && pnpm dev
|
||
|
||
# Full Tauri app (Rust + frontend)
|
||
source ~/.nvm/nvm.sh && pnpm tauri dev
|
||
```
|
||
|
||
### Running Tests
|
||
|
||
```bash
|
||
# All checks (lint → format → type-check → frontend tests → backend tests)
|
||
./check-all.sh
|
||
|
||
# Frontend tests only
|
||
source ~/.nvm/nvm.sh && pnpm test
|
||
|
||
# Frontend with coverage
|
||
source ~/.nvm/nvm.sh && pnpm test:coverage
|
||
|
||
# Backend tests only
|
||
pnpm test:backend
|
||
```
|
||
|
||
### Building
|
||
|
||
```bash
|
||
# Linux build
|
||
pnpm build:linux
|
||
|
||
# Windows cross-compile (requires cargo-xwin)
|
||
pnpm build:windows
|
||
```
|
||
|
||
### Adding a New Tauri Command
|
||
|
||
1. Add the handler function in the appropriate `src-tauri/src/*.rs` file with `#[tauri::command]`
|
||
2. Register it in `lib.rs` `invoke_handler![]`
|
||
3. Call it from the frontend via `invoke("command_name", { args })` in `src/lib/tauri.ts` or a store
|
||
|
||
### Adding a New Frontend Store
|
||
|
||
1. Create `src/lib/stores/my-store.ts` using `writable` or a factory function pattern
|
||
2. Create `src/lib/stores/my-store.test.ts` — all stores must have tests
|
||
3. Expose the store from the appropriate component
|
||
|
||
### Claude Stream-JSON Protocol
|
||
|
||
Claude Code is invoked with `--output-format stream-json --verbose`. See `src-tauri/src/types.rs` for the complete message type definitions. The key field distinguishing subagent messages from top-level messages is `parent_tool_use_id` on `Assistant` messages.
|
||
|
||
### Multi-Conversation Architecture
|
||
|
||
Each tab (`Conversation`) in `conversationsStore` has a unique `conversation_id` string. The backend `BridgeManager` maps these IDs to `WslBridge` instances. All Tauri events carry `conversation_id` in their payload so the frontend can route them to the correct conversation without affecting others.
|
||
|
||
### WSL Detection
|
||
|
||
`wsl_bridge.rs` detects WSL by checking `/proc/version` for "microsoft"/"wsl" strings, checking for `/proc/sys/fs/binfmt_misc/WSLInterop`, and checking `$WSL_DISTRO_NAME`. On native Windows builds, WSL detection always returns `false` (even if launched from a WSL terminal).
|
||
|
||
### Character State Sound Rules
|
||
|
||
Sound effects are managed in `src/lib/tauri.ts` per-conversation to prevent replays when switching tabs. The rules are:
|
||
|
||
- Entering `thinking` from a clean state (`idle`/`success`/`error`) → reset all sound flags
|
||
- Entering `coding` or `searching` (first time per task) → play task-start sound
|
||
- Entering `success` after ≥2 seconds in a long-running phase → play completion sound
|
||
- Entering `error` → play error sound (always)
|
||
- Entering `permission` → play permission sound (always)
|
||
|
||
### Workspace Trust Gate
|
||
|
||
On first connection to a new working directory, the app checks for Claude hooks and prompts the user to trust the workspace. Trusted workspaces are persisted in `HikariConfig.trusted_workspaces`.
|
||
|
||
### Configuration Storage
|
||
|
||
All settings are persisted via `tauri-plugin-store` to a JSON file in the app data directory. The frontend `configStore` (`src/lib/stores/config.ts`) loads configuration on startup and provides reactive derived stores. Changes invoke `save_config` to persist to disk.
|