Files
hikari-desktop/CODEBASE.md
hikari e6e9f7ae59
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m39s
CI / Build Linux (push) Has been cancelled
CI / Build Windows (cross-compile) (push) Has been cancelled
CI / Lint & Test (push) Has been cancelled
feat: productivity suite — task loop, workflow, theming, docs & more (#197)
## 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>
2026-03-07 03:08:33 -08:00

26 KiB
Raw Permalink Blame History

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
  • ContentBlockText, Thinking, ToolUse, ToolResult
  • CharacterStateIdle | 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

# Frontend dev server only
source ~/.nvm/nvm.sh && pnpm dev

# Full Tauri app (Rust + frontend)
source ~/.nvm/nvm.sh && pnpm tauri dev

Running Tests

# 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

# 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.