# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project state **Phase 1 is scaffolded.** An `electron-vite` + React 18 + TypeScript app now lives at the repo root (`src/main`, `src/preload`, `src/renderer`). The prototype has been ported faithfully and renders against the **mock data** — full UI, git panel, four diff modes + Split, search, and the *simulated* terminals are all working. JetBrains Mono is bundled locally via `@fontsource/jetbrains-mono`; Prism is wired with the correct `markup-templating`→`php` load order; the renderer↔main clipboard bridge is in place (`src/preload/index.ts`). **Phase 2 (real integrations) — complete; DESIGN.md fully implemented.** The app is feature-complete against the functional spec (the only intentional exception is the separate Go-to-File overlay — `⌘P` aliases the unified search instead, per the resolved decision). Editor is writable (save · autosave · dirty tabs · discard); session restore brings back open tabs/active/view modes; close-dirty prompts Save/Don't-Save/Cancel. There's a vitest suite (`npm test`, 51 tests) + ESLint + electron-builder packaging. All over IPC through the preload bridge (`src/preload/index.ts`): - **Filesystem** — tree + in-memory content index, `chokidar` watch (`src/main/fs-service.ts`). The tree/index/search share one source of truth: `rg --files` (honors gitignore + `files.exclude`, includes dotfiles), with a recursive-walk fallback when ripgrep is unavailable. - **Git** — `simple-git`: status→A/M/D/R, the four diff views from HEAD-vs-worktree pairs, stage/unstage/commit/discard (`src/main/git-service.ts`). - **Terminals** — real PTYs via `node-pty` (`src/main/pty-service.ts`) rendered with `@xterm/xterm` (`src/renderer/src/terminals.tsx`). Agent pane is a shell that auto-launches `claude`; bottom pane is a plain shell. Pass-on-to-Agent writes bracketed paste (`\x1b[200~ … \x1b[201~`) to the agent PTY. node-pty is native — `npm run rebuild` (also a `postinstall`) rebuilds it for Electron; it's N-API so the binary is portable. - **Config** — `.helder/` per project (`src/main/config.ts`): `config.default.json` regenerated on launch (full defaults / live docs), sparse `config.json` deep-merged over it, and `theme.css` (created once, never overwritten) injected over the built-in dark theme. The **code font + size are CSS vars** (`--code-font`/`--code-size`/`--term-size`) the editor + xterm read, overridable from `theme.css`. ai command/autoLaunch + shell flow from config into the PTYs; a `.helder` file watcher hot-reloads config/theme. - **Search** — ripgrep (`@vscode/ripgrep`, bundled binary) for content (`--json`, fixed-string smart-case) and the file-name list (`--files`), via `src/main/search-service.ts`. `SearchModal` calls it debounced and falls back to the in-memory index when `window.helder` is absent. The renderer consumes FS/git/config/search via the store in `src/renderer/src/project.tsx` (`useProject` / `useProjectActions`), which falls back to the mock + default config when `window.helder` is absent (browser preview). `src/main/index.ts` registers all IPC handlers + the debounced watchers. - **Editing** — the `code`/`updated` modes are a writable buffer: a transparent textarea over a Prism-highlighted `
` with a scroll-synced gutter (`CodeEditor` in `editor.tsx`). `⌘S` saves to disk (`fs:write`), `editor.autoSave` debounce-saves on change, tabs show the dirty dot, and the git-row context menu has **Discard changes** gated by `git.confirmDiscard`. Original/Diff/Split stay read-only review views. README steps 1–7 plus config + editing are all implemented for real. The editable overlay keeps the caret in view (the textarea is overflow-hidden under the scroller, so `CodeEditor` scrolls the container on input/keyup/click). Not yet done: packaging (electron-builder → .app/.dmg) — `out/` is dev build output only, there is no distributable yet. The two handoff documents remain the contract: - **`DESIGN.md`** — functional/UX source of truth. Every panel, interaction, state, and edge case at the behavior level. Read this for *what the app does*. - **`design_handoff_helder_workbench/README.md`** — technical source of truth. Structure, design tokens, recommended stack, real-integration mechanics, and the suggested implementation order. Read this for *how to build it*. The prototype in `design_handoff_helder_workbench/design/` (React 18 + Babel from CDN, all mock data) is a **visual/interaction reference only — do not ship it as-is**. The HTML is canonical for look and feel; `design/styles.css`'s `:root` block is the canonical design-token list. The `design/src/*.jsx` files map directly to the components to build, but their mock data (`data.js`) and simulated terminals/agent must be replaced with real integrations. ## What Helder is A dark-only (no light mode, no theme toggle) Electron desktop code workbench for reviewing code written by an AI agent. One project per window. Four resizable columns left→right: **Source Control (git)**, **Explorer (file tree)**, **Editor (tabs + diff)**, **Right column (Claude agent terminal stacked over a shell terminal)**. Plus a top title bar and bottom status bar. The defining feature is the **Copy reference / Pass on to Agent** flow that pushes `path:line` references into the agent's input. ## Recommended stack (no codebase exists — follow README) - Electron (latest stable), main + renderer + preload bridge with `contextIsolation: true`. - Renderer: React 18 + TypeScript + Vite (`electron-vite` scaffold). Prototype is already React, so component structure ports directly. - Syntax highlighting: Prism 1.29 (or swap to Shiki/CodeMirror 6; token→color mapping is documented in README). - Fonts: UI = system stack; code/mono = **JetBrains Mono bundled locally** (never Google Fonts CDN in Electron). ## Architecture rules and gotchas (these will bite if ignored) - **Renderer never touches the filesystem, git, or PTYs directly.** All FS (`fs` + `chokidar`), git (`git` / `simple-git`), search (`rg` + fuzzy), terminals (`node-pty` + `xterm.js`), and clipboard go through the main process via IPC / the preload bridge. The prototype keeps all state in the top `App` component; in the real app, lift FS/git/terminal state into main and stream over IPC. - **Prism PHP load order:** `prism-php` requires `prism-markup-templating` to be loaded **first**, or every `Prism.highlight` call throws and silently falls back to plain text. - **Preload must be CommonJS `index.cjs`** and `main` must load `../preload/index.cjs` (see `electron.vite.config.ts` preload `rollupOptions.output`). If they mismatch (or you let it build as `.mjs`), Electron silently loads no preload, `window.helder` is undefined, and the renderer silently falls back to mock data + the "terminal not available" message. Keep the filename and the `main` path in sync. - **Pass on to Agent uses bracketed paste.** Write inserts to the agent PTY wrapped in `\x1b[200~ … \x1b[201~` so the `claude` CLI treats it as *pasted, unsubmitted* input. Insert must never submit — it lands as a new line so the user can stack several references before sending. - **The four diff view modes (Original / Updated / Diff / Split) all derive from one original-text + updated-text pair per changed file.** The prototype computes this with an LCS line diff (`buildDiff()` in `design/src/data.js`); production should prefer real `git diff` output but keep the same four derived views and the same color language everywhere: **red = removed/changed-from, green = added/changed-to**, syntax highlighting on in all modes. - **The agent pane is just a terminal running the `claude` CLI** (`ai.command`, default `claude`, auto-launched when `ai.autoLaunch` is on). The bottom pane is a normal shell PTY. The prototype's simulated agent session (`agentSeed`, `runAgent`, `bootAgent` in `terminals.jsx`) exists only to show the visual style — keep the styling, drop the fakery. - **Chrome budget:** title bar + tab strip + panel headers + status bar combined should stay ≈10% of vertical height. Keep it minimal. ## Confirmed decisions (the "Open assumptions" in DESIGN.md are resolved — do not re-ask) - Search overlay layout: **content matches left (70%), file-name matches right (30%)** — keep as designed. - Tabs **show an unsaved indicator** (a dot in place of the close control) because auto-save defaults off (`editor.autoSave`). - Explorer right-click offers a **file-level Copy reference** (project-relative path only), consistent with the editor's Copy reference — in scope. ## Scope boundaries (this version) - **Git covers staging, unstaging, committing, and discarding only.** Push, pull, fetch, and branch switching are explicitly out of scope. The branch summary bar and status bar are display-only. - **One agent terminal and one shell terminal** — no additional tabs or sessions. - The breadcrumb and status-bar items are **display only** (not clickable, do not navigate). - **Discard is the only destructive git action** and must confirm first (`git.confirmDiscard`, default on). Staging/unstaging do not confirm by default. ## Configuration Settings are project-scoped, living in a `.helder/` folder in the opened project's root: - `.helder/config.json` — sparse; only user-overridden values. - `.helder/config.default.json` — full defaults, **regenerated on launch** from built-in defaults (live documentation of every setting; the app never reads user edits from it). - Effective value = `config.json` if present, else `config.default.json`, merged key by key. - `.helder/theme.css` — custom CSS theme applied over the built-in dark theme; **code font and font size live here**, not in the config files. ## Design tokens Canonical source is the `:root` block in `design_handoff_helder_workbench/design/styles.css`. Surfaces are cool charcoal (`--bg-0` editor `#16171a` → `--bg-3` headers/tabs `#23262b`); single cool-blue accent `--accent #4d8dff`; git status `--add #5cbd6b` / `--del #e0696a` / `--mod #d8a85c` / `--ren #5aa6d6`. File-type icons are 15×15 monogram chips (no brand logos). Recreate UI icons as a small inline-SVG set (or Lucide), keeping the monogram chips for file types. Respect `prefers-reduced-motion`; keep motion subtle. ## Suggested implementation order (from README) 1. Electron shell + frameless dark window; port tokens to CSS vars; bundle JetBrains Mono. 2. Static layout: four resizable columns + title/status bars. 3. Real file tree + open files into tabs (read-only) with Prism highlighting. 4. Git panel from `git status` (read-only) → staging + commit → the four diff modes + Split. 5. Search (ripgrep + fuzzy). 6. Terminals via node-pty + xterm.js; run `claude` in the agent pane. 7. Copy reference + Pass-on-to-Agent (clipboard + bracketed-paste into the agent PTY). ## Commands - `npm run dev` — launch the app in Electron with HMR (`electron-vite dev`). - `npm run build` — type-stripped production build into `out/` (`electron-vite build`). A frontend change is not done until this succeeds. - `npm run preview` / `npm start` — run the built app (`electron-vite preview`). - `npm run typecheck` — `tsc --noEmit` over the renderer (`tsconfig.web.json`) and main/preload (`tsconfig.node.json`). The build itself uses esbuild and does NOT type-check, so run this separately to catch type errors. - `npm test` — vitest suite in `test/` (pure logic + node-side services: diff, fuzzy, highlight, config, fs, git). `npm run test:watch` for watch mode. - `npm run lint` — ESLint (flat config in `eslint.config.js`). `.prettierrc.json` defines formatting (not auto-applied). - `npm run pack` — unpacked app into `dist/` (electron-builder, unsigned). `npm run dist` / `dist:mac` for distributables. App icon comes from `build/icon.png`. Native `node-pty` + `rg` are asar-unpacked so they load when packaged. Keep all five green (typecheck · lint · test · build, and pack when touching main/packaging) when changing code.