- Add docs/ with ARCHITECTURE, ABSTRACTIONS, GETTING-STARTED, THEMING - Add GitHub Actions workflow with automated quality checks: • Tests (frontend + Rust) • Coverage (70% threshold) • Code Health (CodeScene delta analysis) • Documentation check (warning only) • Lint & Format (ESLint + Clippy + rustfmt) - Configure coverage in vite.config.ts (v8 provider, 70% threshold) - Add test:coverage script to package.json - Install @vitest/coverage-v8 - Update CLAUDE.md with docs-update requirement - Add CI/CD setup guides in .github/
8.7 KiB
8.7 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)
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 test:e2e # Playwright E2E tests
Directory Structure
laputa-app/
├── src/ # React frontend
│ ├── main.tsx # Entry point (renders <App />)
│ ├── App.tsx # Root component — orchestrates 4-panel layout
│ ├── App.css # App shell layout styles
│ ├── types.ts # Shared TS types (VaultEntry, GitCommit, 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
│ │ ├── Sidebar.tsx # Left panel: filters + section groups
│ │ ├── NoteList.tsx # Second panel: filtered note list
│ │ ├── Editor.tsx # Third panel: tabs + BlockNote + diff
│ │ ├── Inspector.tsx # Fourth panel: metadata + relationships
│ │ ├── DynamicPropertiesPanel.tsx # Editable frontmatter properties
│ │ ├── EditableValue.tsx # Inline value editor component
│ │ ├── DiffView.tsx # Git diff viewer
│ │ ├── ResizeHandle.tsx # Draggable panel divider
│ │ ├── StatusBar.tsx # Bottom status bar
│ │ ├── QuickOpenPalette.tsx # Cmd+P command palette
│ │ ├── CreateNoteDialog.tsx # New note modal
│ │ ├── CommitDialog.tsx # Git commit modal
│ │ ├── Toast.tsx # Toast notifications
│ │ ├── Editor.css # Editor layout styles
│ │ ├── EditorTheme.css # BlockNote theme overrides
│ │ └── ui/ # shadcn/ui primitives (button, dialog, etc.)
│ │
│ ├── hooks/ # Custom React hooks
│ │ ├── useVaultLoader.ts # Loads vault entries, git status, content
│ │ ├── useNoteActions.ts # Tab management, frontmatter CRUD, navigation
│ │ └── useTheme.ts # Flattens theme.json into CSS variables
│ │
│ ├── utils/ # Pure utility functions
│ │ ├── frontmatter.ts # TypeScript YAML frontmatter parser
│ │ └── wikilinks.ts # Wikilink preprocessing + word count
│ │
│ ├── 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
│ │ └── default.json
│ ├── src/
│ │ ├── main.rs # Entry point (calls lib::run())
│ │ ├── lib.rs # Tauri command registration (9 commands)
│ │ ├── vault.rs # Vault scanning + markdown parsing
│ │ ├── frontmatter.rs # YAML frontmatter manipulation
│ │ └── git.rs # Git CLI operations
│ └── icons/ # App icons
│
├── e2e/ # Playwright E2E tests
│ ├── app.spec.ts # App loading tests
│ ├── core-flows.spec.ts # Main user workflows
│ ├── keyboard-shortcuts.spec.ts
│ ├── quick-open.spec.ts
│ ├── screenshot.spec.ts # Visual regression screenshots
│ └── ...
│
├── package.json # Frontend dependencies + scripts
├── vite.config.ts # Vite bundler config
├── tsconfig.json # TypeScript config
├── playwright.config.ts # E2E test config
├── CLAUDE.md # Project instructions for Claude
└── docs/ # This documentation
Key Files to Know
Start here
| File | Why it matters |
|---|---|
src/App.tsx |
The root component. Shows how the 4-panel layout is assembled and how state flows between components. |
src/types.ts |
All shared TypeScript types. Read this first to understand the data model. |
src-tauri/src/lib.rs |
All 9 Tauri commands in one place. This is the frontend-backend API surface. |
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/mock-tauri.ts |
Mock data for browser testing. Shows the shape of all Tauri responses. |
Backend
| File | Why it matters |
|---|---|
src-tauri/src/vault.rs |
Vault scanning, frontmatter parsing, entity type inference. The core backend logic. |
src-tauri/src/frontmatter.rs |
YAML manipulation — how properties are updated/deleted in files. |
src-tauri/src/git.rs |
All git operations. Shells out to git CLI. |
Editor
| File | Why it matters |
|---|---|
src/components/Editor.tsx |
BlockNote setup, custom wikilink schema, tab bar, breadcrumb bar, diff toggle. |
src/utils/wikilinks.ts |
The wikilink preprocessing pipeline (markdown → BlockNote blocks with wikilinks). |
src/components/EditorTheme.css |
BlockNote CSS overrides for typography and styling. |
Styling
| File | Why it matters |
|---|---|
src/index.css |
All CSS custom properties (colors, spacing). The design token source of truth. |
src/theme.json |
Editor-specific theme (fonts, headings, lists, code blocks). |
src/hooks/useTheme.ts |
Converts theme.json into CSS variables for the editor. |
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: 'all' | 'favorites' }
| { kind: 'sectionGroup'; type: string }
| { kind: 'entity'; entry: VaultEntry }
| { kind: 'topic'; entry: VaultEntry }
This pattern makes it easy to handle all selection states exhaustively.
Running Tests
# Unit tests (fast, no browser)
pnpm test
# Rust tests
cd src-tauri && cargo test
# E2E tests (requires dev server)
pnpm test:e2e
# Single Playwright test
npx playwright test e2e/screenshot.spec.ts
# Visual verification screenshots
npx playwright test e2e/screenshot.spec.ts
# Screenshots saved to test-results/
Common Tasks
Add a new Tauri command
- Write the Rust function in
vault.rs,git.rs, or a new module - Add
#[tauri::command]wrapper inlib.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/) - Add the folder → type mapping in
vault.rs:parse_md_file()(thematchon folder names) - The sidebar section groups are defined as
SECTION_GROUPSinSidebar.tsx— add it there - Update
CreateNoteDialog.tsxtype options if users should be able to create it