Updated all three docs to reflect significant features added since they were last written: - AI agent panel (Claude CLI subprocess with tool execution + NDJSON streaming) - Vault cache system (git-based incremental caching in cache.rs) - Theme system (vault-based themes, useThemeManager, ThemePropertyEditor) - Search & indexing (qmd integration, keyword/semantic/hybrid modes) - Pulse view (git activity feed with pagination) - GitHub OAuth (device flow, vault clone/create) - Vault management (multi-vault, vault config, onboarding, WelcomeScreen) - Raw editor mode (CodeMirror 6 alternative) - Command palette (Cmd+K registry) - Auto-sync & conflict resolution Also added mandatory docs-update rule to CLAUDE.md: docs/ files must be updated in the same commit as significant feature changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
17 KiB
17 KiB
Getting Started
How to navigate the codebase, run the app, and find what you need.
Prerequisites
- Node.js 18+ and pnpm
- Rust 1.77.2+ (for the Tauri backend)
- git CLI (required by the git integration features)
- qmd (optional — for search indexing; auto-installed if missing)
Quick Start
# Install dependencies
pnpm install
# Run in browser (no Rust needed — uses mock data)
pnpm dev
# Open http://localhost:5173
# Run with Tauri (full app, requires Rust)
pnpm tauri dev
# Run tests
pnpm test # Vitest unit tests
cargo test # Rust tests (from src-tauri/)
pnpm playwright:smoke # Playwright smoke tests
Directory Structure
laputa-app/
├── src/ # React frontend
│ ├── main.tsx # Entry point (renders <App />)
│ ├── App.tsx # Root component — orchestrates layout + state
│ ├── App.css # App shell layout styles
│ ├── types.ts # Shared TS types (VaultEntry, Settings, etc.)
│ ├── mock-tauri.ts # Mock Tauri layer for browser testing
│ ├── theme.json # Editor theme configuration
│ ├── index.css # Global CSS variables + Tailwind setup
│ │
│ ├── components/ # UI components (~98 files)
│ │ ├── Sidebar.tsx # Left panel: filters + type groups
│ │ ├── SidebarParts.tsx # Sidebar subcomponents
│ │ ├── NoteList.tsx # Second panel: filtered note list
│ │ ├── NoteItem.tsx # Individual note item
│ │ ├── PulseView.tsx # Git activity feed (replaces NoteList)
│ │ ├── Editor.tsx # Third panel: tabs + editor orchestration
│ │ ├── EditorContent.tsx # Editor content area
│ │ ├── EditorRightPanel.tsx # Right panel toggle
│ │ ├── editorSchema.tsx # BlockNote schema + wikilink type
│ │ ├── RawEditorView.tsx # CodeMirror raw editor
│ │ ├── Inspector.tsx # Fourth panel: metadata + relationships
│ │ ├── DynamicPropertiesPanel.tsx # Editable frontmatter properties
│ │ ├── AIChatPanel.tsx # AI chat (API-based)
│ │ ├── AiPanel.tsx # AI agent (Claude CLI subprocess)
│ │ ├── AiMessage.tsx # Agent message display
│ │ ├── AiActionCard.tsx # Agent tool action cards
│ │ ├── SearchPanel.tsx # Search interface
│ │ ├── SettingsPanel.tsx # App settings
│ │ ├── StatusBar.tsx # Bottom bar: vault picker + sync
│ │ ├── CommandPalette.tsx # Cmd+K command launcher
│ │ ├── TabBar.tsx # Tab management
│ │ ├── BreadcrumbBar.tsx # Breadcrumb + word count + actions
│ │ ├── WelcomeScreen.tsx # Onboarding screen
│ │ ├── GitHubVaultModal.tsx # GitHub vault clone/create
│ │ ├── GitHubDeviceFlow.tsx # GitHub OAuth device flow
│ │ ├── ThemePropertyEditor.tsx # Interactive theme editor
│ │ ├── ConflictResolverModal.tsx # Git conflict resolution
│ │ ├── CommitDialog.tsx # Git commit modal
│ │ ├── CreateNoteDialog.tsx # New note modal
│ │ ├── CreateTypeDialog.tsx # New type modal
│ │ ├── UpdateBanner.tsx # In-app update notification
│ │ ├── inspector/ # Inspector sub-panels
│ │ │ ├── BacklinksPanel.tsx
│ │ │ ├── RelationshipsPanel.tsx
│ │ │ ├── GitHistoryPanel.tsx
│ │ │ └── ...
│ │ └── ui/ # shadcn/ui primitives
│ │ ├── button.tsx, dialog.tsx, input.tsx, ...
│ │
│ ├── hooks/ # Custom React hooks (~87 files)
│ │ ├── useVaultLoader.ts # Loads vault entries + content
│ │ ├── useVaultSwitcher.ts # Multi-vault management
│ │ ├── useVaultConfig.ts # Per-vault UI settings
│ │ ├── useNoteActions.ts # Tab management, navigation, CRUD
│ │ ├── useTabManagement.ts # Tab ordering + lifecycle
│ │ ├── useAIChat.ts # AI chat state
│ │ ├── useAiAgent.ts # AI agent state + tool tracking
│ │ ├── useAiActivity.ts # MCP UI bridge listener
│ │ ├── useAutoSync.ts # Auto git pull/push
│ │ ├── useConflictResolver.ts # Git conflict handling
│ │ ├── useEditorSave.ts # Auto-save with debounce
│ │ ├── useTheme.ts # Flatten theme.json → CSS vars
│ │ ├── useThemeManager.ts # Vault theme lifecycle
│ │ ├── useIndexing.ts # Search indexing management
│ │ ├── useNoteSearch.ts # Note search
│ │ ├── useCommandRegistry.ts # Command palette registry
│ │ ├── useAppCommands.ts # App-level commands
│ │ ├── useAppKeyboard.ts # Keyboard shortcuts
│ │ ├── useSettings.ts # App settings
│ │ ├── useOnboarding.ts # First-launch flow
│ │ ├── useCodeMirror.ts # CodeMirror raw editor
│ │ ├── useMcpBridge.ts # MCP WebSocket client
│ │ ├── useMcpStatus.ts # MCP registration status
│ │ ├── useUpdater.ts # In-app updates
│ │ └── ...
│ │
│ ├── utils/ # Pure utility functions (~48 files)
│ │ ├── wikilinks.ts # Wikilink preprocessing pipeline
│ │ ├── frontmatter.ts # TypeScript YAML parser
│ │ ├── ai-agent.ts # Agent stream utilities
│ │ ├── ai-chat.ts # Chat API client + token estimation
│ │ ├── ai-context.ts # Context snapshot builder
│ │ ├── noteListHelpers.ts # Sorting, filtering, date formatting
│ │ ├── themeSchema.ts # Theme editor schema builder
│ │ ├── configMigration.ts # localStorage → vault config migration
│ │ ├── iconRegistry.ts # Phosphor icon registry
│ │ ├── propertyTypes.ts # Property type definitions
│ │ ├── vaultListStore.ts # Vault list persistence
│ │ ├── vaultConfigStore.ts # Vault config store
│ │ └── ...
│ │
│ ├── lib/
│ │ └── utils.ts # Tailwind merge + cn() helper
│ │
│ └── test/
│ └── setup.ts # Vitest test environment setup
│
├── src-tauri/ # Rust backend
│ ├── Cargo.toml # Rust dependencies
│ ├── build.rs # Tauri build script
│ ├── tauri.conf.json # Tauri app configuration
│ ├── capabilities/ # Tauri v2 security capabilities
│ ├── src/
│ │ ├── main.rs # Entry point (calls lib::run())
│ │ ├── lib.rs # Tauri setup + command registration (61 commands)
│ │ ├── commands.rs # All Tauri command handlers
│ │ ├── vault/ # Vault module
│ │ │ ├── mod.rs # Core types, parse_md_file, scan_vault
│ │ │ ├── cache.rs # Git-based incremental caching
│ │ │ ├── parsing.rs # Text processing + title extraction
│ │ │ ├── trash.rs # Trash auto-purge
│ │ │ ├── rename.rs # Rename + cross-vault wikilink update
│ │ │ ├── image.rs # Image attachment saving
│ │ │ ├── migration.rs # Frontmatter migration
│ │ │ └── getting_started.rs # Getting Started vault creation
│ │ ├── frontmatter/ # Frontmatter module
│ │ │ ├── mod.rs, yaml.rs, ops.rs
│ │ ├── git/ # Git module
│ │ │ ├── mod.rs, commit.rs, status.rs, history.rs
│ │ │ ├── conflict.rs, remote.rs, pulse.rs
│ │ ├── github/ # GitHub module
│ │ │ ├── mod.rs, auth.rs, api.rs, clone.rs
│ │ ├── theme/ # Theme module
│ │ │ ├── mod.rs, create.rs, defaults.rs, seed.rs
│ │ ├── search.rs # qmd search integration
│ │ ├── indexing.rs # qmd indexing + progress streaming
│ │ ├── claude_cli.rs # Claude CLI subprocess management
│ │ ├── ai_chat.rs # Direct Anthropic API client
│ │ ├── mcp.rs # MCP server lifecycle + registration
│ │ ├── settings.rs # App settings persistence
│ │ ├── vault_config.rs # Per-vault UI config
│ │ ├── vault_list.rs # Vault list persistence
│ │ └── menu.rs # Native macOS menu bar
│ └── icons/ # App icons
│
├── mcp-server/ # MCP bridge (Node.js)
│ ├── index.js # MCP server entry (stdio, 14 tools)
│ ├── vault.js # Vault file operations
│ ├── ws-bridge.js # WebSocket bridge (ports 9710, 9711)
│ ├── test.js # MCP server tests
│ └── package.json
│
├── e2e/ # Playwright E2E tests (~26 specs)
├── tests/smoke/ # Smoke tests (~10 specs)
├── design/ # Per-task design files
├── demo-vault-v2/ # Getting Started demo vault
├── scripts/ # Build/utility scripts
│
├── package.json # Frontend dependencies + scripts
├── vite.config.ts # Vite bundler config
├── tsconfig.json # TypeScript config
├── playwright.config.ts # E2E test config
├── ui-design.pen # Master design file
├── CLAUDE.md # Project instructions
└── docs/ # This documentation
Key Files to Know
Start here
| File | Why it matters |
|---|---|
src/App.tsx |
Root component. Shows the 4-panel layout, state flow, and how all features connect. |
src/types.ts |
All shared TypeScript types. Read this first to understand the data model. |
src-tauri/src/commands.rs |
All 61 Tauri command handlers. This is the frontend-backend API surface. |
src-tauri/src/lib.rs |
Tauri setup, command registration, startup tasks, WebSocket bridge lifecycle. |
Data layer
| File | Why it matters |
|---|---|
src/hooks/useVaultLoader.ts |
How vault data is loaded and managed. The Tauri/mock branching pattern. |
src/hooks/useNoteActions.ts |
Tab management, wikilink navigation, frontmatter CRUD. The biggest hook. |
src/hooks/useVaultSwitcher.ts |
Multi-vault management, vault switching, Getting Started vault. |
src/mock-tauri.ts |
Mock data for browser testing. Shows the shape of all Tauri responses. |
Backend
| File | Why it matters |
|---|---|
src-tauri/src/vault/mod.rs |
Vault scanning, frontmatter parsing, entity type inference, relationship extraction. |
src-tauri/src/vault/cache.rs |
Git-based incremental caching — how large vaults load fast. |
src-tauri/src/frontmatter/ops.rs |
YAML manipulation — how properties are updated/deleted in files. |
src-tauri/src/git/ |
All git operations (commit, pull, push, conflicts, pulse). |
src-tauri/src/github/ |
GitHub OAuth device flow + repo clone/create. |
src-tauri/src/search.rs |
qmd search integration (keyword/semantic/hybrid). |
src-tauri/src/claude_cli.rs |
Claude CLI subprocess spawning + NDJSON stream parsing. |
Editor
| File | Why it matters |
|---|---|
src/components/Editor.tsx |
BlockNote setup, tab bar, breadcrumb bar, diff/raw toggle. |
src/components/editorSchema.tsx |
Custom wikilink inline content type definition. |
src/utils/wikilinks.ts |
Wikilink preprocessing pipeline (markdown ↔ BlockNote). |
src/components/RawEditorView.tsx |
CodeMirror 6 raw markdown editor. |
AI
| File | Why it matters |
|---|---|
src/components/AiPanel.tsx |
AI agent panel — Claude CLI with tool execution, reasoning, actions. |
src/components/AIChatPanel.tsx |
AI chat panel — API-based chat without tools. |
src/hooks/useAiAgent.ts |
Agent state: messages, streaming, tool tracking, file detection. |
src/utils/ai-context.ts |
Context snapshot builder for AI conversations. |
Styling & Themes
| File | Why it matters |
|---|---|
src/index.css |
All CSS custom properties. The design token source of truth. |
src/theme.json |
Editor-specific theme (fonts, headings, lists, code blocks). |
src/hooks/useThemeManager.ts |
Vault theme lifecycle (switch, create, apply, live preview). |
docs/THEMING.md |
Full theme system documentation. |
Settings & Config
| File | Why it matters |
|---|---|
src/hooks/useSettings.ts |
App settings (API keys, GitHub token, sync interval). |
src/hooks/useVaultConfig.ts |
Per-vault UI preferences (zoom, view mode, colors). |
src/components/SettingsPanel.tsx |
Settings UI including GitHub OAuth connection. |
Architecture Patterns
Tauri/Mock Branching
Every data-fetching operation checks isTauri() and branches:
if (isTauri()) {
result = await invoke<T>('command', { args })
} else {
result = await mockInvoke<T>('command', { args })
}
This lives in useVaultLoader.ts and useNoteActions.ts. Components never call Tauri directly.
Props-Down, Callbacks-Up
No global state management (no Redux, no Context). App.tsx owns the state and passes it down as props. Child-to-parent communication uses callback props (onSelectNote, onCloseTab, etc.).
Discriminated Unions for Selection State
type SidebarSelection =
| { kind: 'filter'; filter: SidebarFilter }
| { kind: 'sectionGroup'; type: string }
| { kind: 'entity'; entry: VaultEntry }
| { kind: 'topic'; entry: VaultEntry }
Command Registry
useCommandRegistry + useAppCommands build a centralized command registry. Commands are registered with labels, shortcuts, and handlers. The CommandPalette (Cmd+K) fuzzy-searches this registry. The native macOS menu bar also triggers commands via useMenuEvents.
Running Tests
# Unit tests (fast, no browser)
pnpm test
# Unit tests with coverage (must pass ≥70%)
pnpm test:coverage
# Rust tests
cargo test
# Rust coverage (must pass ≥85% line coverage)
cargo llvm-cov --manifest-path src-tauri/Cargo.toml --no-clean --fail-under-lines 85
# Playwright smoke tests (requires dev server)
BASE_URL="http://localhost:5173" pnpm playwright:smoke
# Single Playwright test
BASE_URL="http://localhost:5173" npx playwright test tests/smoke/<slug>.spec.ts
Common Tasks
Add a new Tauri command
- Write the Rust function in the appropriate module (
vault/,git/, etc.) - Add a command handler in
commands.rs - Register it in the
generate_handler![]macro inlib.rs - Call it from the frontend via
invoke()in the appropriate hook - Add a mock handler in
mock-tauri.ts
Add a new component
- Create
src/components/MyComponent.tsx - If it needs vault data, receive it as props from the parent
- Wire it into
App.tsxor the relevant parent component - Add a test file
src/components/MyComponent.test.tsx
Add a new entity type
- Create the folder in the vault (e.g.,
~/Laputa/mytype/) - Create a type document:
type/mytype.mdwithIs A: Typefrontmatter (icon, color, order, etc.) - The sidebar section groups are auto-generated from type documents — no code change needed if
visible: true - Update
CreateNoteDialog.tsxtype options if users should be able to create it from the dialog
Add a command palette entry
- Register the command in
useAppCommands.tsvia the command registry - Add a corresponding menu bar item in
menu.rsfor discoverability - If it has a keyboard shortcut, register it in
useAppKeyboard.ts
Add or modify a theme
- Vault-based (preferred): Create/edit a markdown note in
theme/withIs A: Themefrontmatter - Programmatic: Edit defaults in
src-tauri/src/theme/defaults.rs - See
docs/THEMING.mdfor the full property reference
Work with the AI agent
- Agent system prompt: Edit
src/utils/ai-agent.ts(inline system prompt string) - Context building: Edit
src/utils/ai-context.tsfor what data is sent to the agent - Tool action display: Edit
src/components/AiActionCard.tsx - Claude CLI arguments: Edit
src-tauri/src/claude_cli.rs(run_agent_stream())