# Handoff: Helder — in-pane diff (screen 3b) ## Overview Helder is a desktop editor for working with Claude Code. This handoff covers **one screen**: the workspace with a modified file open in the editor pane, in **Actual** view. The idea behind the screen: the user always reads the **current** version of the file. A changed line is marked in place with a teal rule and a teal tint — nothing else moves, no split view, no `+`/`−` line pairs. When the user hovers a changed line, the **whole original file** slides in over the right-hand column (agent + terminal) at the **same scroll position**, so the two versions can be compared line by line. The overlay disappears on mouse-out. There is no mode to enter and no mode to leave. ## About the design files `03b-in-pane-diff.html` in this folder is a **design reference created in HTML** — a prototype that shows the intended look and behaviour. It is not production code to copy. The task is to **recreate this design in the target codebase's own environment** (Electron + React, Tauri, SwiftUI, whatever Helder already uses) with that codebase's established patterns, components and styling layer. If no environment exists yet, choose the framework that fits the product and implement the design there. The HTML uses inline styles throughout because of how the design tool works. Do not treat that as a styling instruction — move the values into the codebase's own token/theme layer. ## Fidelity **High fidelity.** Colours, type, spacing, row heights and states below are final and exact. Recreate the UI pixel-perfectly. The only intentionally loose part is the file-tree and source-control content, which is sample data. ## Screen **Name:** Workspace — file modified, Actual view **Canvas:** 1760 × 1000 px (design size; the real window is resizable — see *Responsive behaviour*) **Purpose:** read and edit a file that has uncommitted changes, while Claude Code works in the right column; check what any changed line used to be without leaving the file. ### Column layout (left → right) | Region | Width | Background | Divider | |---|---|---|---| | Source control | 300 px, fixed | `#18202B` | 1 px `#232C39` right | | Explorer | 288 px, fixed | `#18202B` | 1 px `#232C39` right | | Editor | flexible (692 px at 1760) | `#101720` | — | | Agent + terminal | 480 px, fixed | `#101720` | 1 px `#232C39` left | Vertical stack: title bar 44 px → body row (flex: 1) → status bar 28 px. ### Title bar — 44 px, `#18202B`, 1 px `#232C39` bottom - Traffic lights: three 12 px circles, `#E8913A` / `#6C7783` / `#3A424C`, 8 px gap, 14 px from left. - Wordmark `helder.` — IBM Plex Mono 700 13 px `#F4F5F4`; **the period is `#E8913A` and blinks** (see *Animation*). - Repo · worktree: `bob` `#6C7783` · `bob2` `#E8913A` 500, Plex Mono 12 px. - Breadcrumb: Plex Mono 12 px `#6C7783`, `›` separators, last crumb `#F4F5F4` 600. - Right cluster, each 6 px 9 px padding, Plex Sans 12 px label + Plex Mono 12 px 600 shortcut chip (1 px `#232C39`, radius 2, padding 4 px 6 px): Search ⌘F · Fluid ⌘L · Hidden ⌘. (label and chip `#6C7783` — the off state) · Note ⌘N · **Git ⌘G active**: background `rgba(232,145,58,.10)`, label `#F4F5F4`, chip border `rgba(232,145,58,.35)`. Then a 26 × 26 `?` button, 1 px `#232C39`. ### The 46 px header row All three panel headers sit on **one 46 px row** with their 1 px `#232C39` bottom borders on the same line. This alignment is deliberate — do not let any one of them grow. 1. **Source control commit row** — 46 px, padding 0 12 px, flex, 8 px gap, centred. Commit field: flex 1, height 30 px, `#101720`, 1 px `#232C39`, radius 2, padding 0 9 px, Plex Sans 12 px `#E4E7E6`. Sample value: `runs mail check once an hour`. Push button: 30 × 30, 1 px `#232C39`, radius 2, glyph `↑` Plex Mono 15 px `#BAC0C0`. 2. **Explorer header** — 46 px, padding 0 12 px, space-between, Plex Mono 700 10 px, tracking `.14em`, uppercase, `#6C7783`: `EXPLORER` … `BOB`. 3. **Editor header** — 46 px. A 46 px-wide agent-rail cell first (centred `BOB`, same label style), then: `MODIFIED` (Plex Mono 700 10 px uppercase `#6C7783`), `+2` `#8FBFB4`, `−2` `#C4741F` (both Plex Mono 500 12 px), spacer, then the view switch. **View switch** — a single bordered group, 1 px `#232C39`, radius 2, `overflow: hidden`, each item 5 px 12 px, Plex Sans 11 px, dividers 1 px `#232C39` between items: `Actual` (**active**: `#E8913A` fill, `#171C22` text, weight 500) · `Original` · `Preview` · `Diff` (inactive: `#BAC0C0`). Actual is the default and stays the default. ### Source control panel Section header: padding 14 px 12 px 6 px, Plex Mono 700 10 px tracking `.14em` uppercase `#6C7783`, count on the right in `#E8913A`. Folder line: 22 px, `▤` + path, Plex Mono 11 px `#6C7783`, ellipsis on overflow. File row: 26 px, 8 px gap — status letter (Plex Mono 600 10 px, 12 px wide; **staged `#8FBFB4`, unstaged `#F0B476`**), file-type chip (Plex Mono 600 10 px, `#101720` text, radius 2, padding 4 px 5 px; `md` `#BAC0C0`, `yml` `#F8C793`, `tsv` `#A8D6CB`, `sh` `#EFBCB0`, `json`/`php` `#C3A6CE`, `js` `#F0B476`), then the file name Plex Sans 12 px `#BAC0C0`. **Selected row:** background `#232C39`, 2 px `#E8913A` left border, left padding reduced by 2 px so the text does not shift, name `#F4F5F4` 500. Unselected rows carry `border-left: 2px solid transparent` so nothing moves on selection. Footer: 12 px padding, 1 px `#232C39` top, Plex Mono 11 px `#6C7783`, `3 staged · 3 unstaged`. ### Explorer Rows are **24 px**. Folder: 8 px-wide chevron (`⌄` open `#E8913A`, `›` closed `#6C7783`), `▤` `#828D9A`, name Plex Sans 13 px (`#F4F5F4` when open, `#BAC0C0` when closed). Indent = 12 px + 16 px per level. Files use the same type chips as source control; a modified file shows a trailing `M` (Plex Mono 600 10 px `#8FBFB4`). Selected file: `#232C39`, 2 px `#E8913A` left border, name 500 `#F4F5F4`. ### Editor pane - **Agent rail** — 46 px, 1 px `#232C39` right. Holds per-block `M` markers aligned to the changed region: a marker is Plex Mono 600 10 px, 20 px tall, centred; the active block is `#8FBFB4` on `#232C39`, an unstaged block is `#C4741F` on transparent. - **Code grid** — `grid-template-columns: 44px 1fr`, `align-content: start`, padding 8 px 0, Plex Mono 400 **13 px / 20 px**. Line numbers: right-aligned, padding-right 12 px, `#5A6472`. Line text: padding-right 18 px, `#BAC0C0`; markdown headings `#F4F5F4`; fenced-code markers `#6C7783`; inline code and links `#F0B476`; a markdown link target `#A8D6CB`. Long lines **wrap** (`text-wrap: pretty`) and keep their single line number — wrapped rows are how the pane behaves at any width. - **Changed line (the core of this screen)** — line number `#8FBFB4` on `rgba(143,191,180,.12)` with a **2 px `#8FBFB4` left border**; the text cell has the same `rgba(143,191,180,.12)` tint and text `#F4F5F4`. No `+` glyph, no second row, no strikethrough. This is the current content, simply marked as changed. - **Minimap strip** — 16 px, 1 px `#232C39` left, 4 × 4 px marks: `#6C7783` normal, `#8FBFB4` staged change, `#C4741F` unstaged change. ### Agent + terminal column (480 px) Conversation body: padding 14 px 16 px, 12 px gap, Plex Sans 13 px / 1.6 `#BAC0C0`; tool output and metadata in Plex Mono 12 px / 1.6 `#6C7783`; file paths and identifiers inline in Plex Mono `#F0B476`; lead-ins (`Why it was there.`) `#F4F5F4` 600. Prompt field: 44 px, `#101720`, **2 px `#E8913A` border + `0 0 0 2px rgba(232,145,58,.22)` ring** (the house focus state), radius 2, `›` `#E8913A`, 1 × 16 px `#E8913A` caret that blinks. Status lines: Plex Mono 11 px `#6C7783`; `bob` chip = `#E8913A` fill, `#171C22` text, radius 2; `⑂ bob2` `#A8D6CB`; `▸▸ auto mode on` `#E8913A`. Terminal: 150 px, 1 px `#232C39` top, Plex Mono 12 px / 1.7; prompt `[bob2]` `#A8D6CB`, `[~/code/bob]$` `#828D9A`, 7 × 14 px `#E8913A` blinking block cursor. ### Status bar — 28 px, `#18202B`, 1 px `#232C39` top Plex Mono 11 px `#6C7783`, 16 px gap, 0 14 px padding: `⑂ bob2` `#E8913A` · `+22` `#A8D6CB` · `−66` `#C4741F` · spacer · file path · `·` · `Ln 1, Col 1` · `UTF-8` · `LF` · language `#D4D9D8`. ## Interactions & behaviour ### The hover reveal (the one behaviour to get right) - **Trigger:** `mouseenter` anywhere on a changed line's text cell. **Dismiss:** `mouseleave`. No click, no pin, no delay in the prototype. If you add a delay, keep it under 120 ms in and 0 ms out — the gesture must feel like looking, not like opening something. - **What appears:** a panel pinned to the **top, right and bottom of the body row**, 496 px wide, so it covers the whole agent + terminal column and nothing else. `#101720`, **2 px `#C4741F` left border**, `box-shadow: 0 4px 16px rgba(23,28,34,.10)`, `overflow: hidden`, `z-index` above the agent column. - **Panel header:** 46 px (the same 46 px as the editor header, so the first code line of both panes starts at the same y), `#18202B`, 1 px `#232C39` bottom: `ORIGINAL` Plex Mono 700 10 px tracking `.14em` `#C4741F`, then `before this change · same scroll` Plex Mono 11 px `#6C7783`, then `hold hover` right-aligned in the same muted style. - **Panel body:** the **whole original file**, not a hunk — same lines, same order, same wrap points and **the same vertical position for every line as in the editor pane**. In the prototype this is achieved by scaling the type to the narrower column: `grid-template-columns: 35px 1fr`, font `400 10.3px/20px` Plex Mono, gutter padding-right 9 px, text padding-right 11 px. The line-height stays 20 px, so line *n* sits at the same y in both panes. **In the real implementation, do it properly:** render the original text into the same layout the editor uses and synchronise scroll offsets, so line *n* of the original is always level with line *n* of the current file. The rule to satisfy: *the changed line and its previous version are on the same horizontal line of the screen*. - **The old line inside the panel:** line number `#C4741F` on `rgba(196,116,31,.14)` with a 2 px `#C4741F` left border; text cell same tint, text `#E4E7E6`. So: **teal = what is there now, amber = what was there before.** Never green/red. - The editor keeps its own scroll while the overlay is open; the overlay follows it. ### Other states - Hover on any list row (source control, explorer, menus): fill deepens one step (`#18202B` → `#232C39`). No lift, no shadow, no opacity change, no scale on press. - Focus on a text field: 2 px `#E8913A` border + 2 px `rgba(232,145,58,.22)` ring. - Disabled: 40 % opacity, `cursor: not-allowed`. ### Animation Only two things move: 1. **The blinking period / caret** — `@keyframes blink { 0%,49% { opacity:1 } 50%,100% { opacity:0 } }`, `1.1s steps(1, end) infinite`. Used on the wordmark period, the prompt caret and the terminal cursor. Freeze under `prefers-reduced-motion`. 2. **Colour transitions** — 160 ms `cubic-bezier(0.22, 1, 0.36, 1)`; 120 ms on small controls. No entrance animation, no slide, no fade for the overlay — it is there or it is not. No scroll reveal, no parallax. ### Responsive behaviour Source control (300) and explorer (288) and the agent column (480) are fixed; the editor takes the rest. Below roughly 1400 px the design has not been specified — ask before inventing collapse behaviour. The hover panel is always 496 px and always anchored right. ## State | State | Type | Notes | |---|---|---| | `activeView` | `'actual' \| 'original' \| 'preview' \| 'diff'` | Default `'actual'`. Persist per file. | | `hoveredChangedLine` | `lineNumber \| null` | Drives the overlay. Cleared on mouse-out and on scroll-end if the pointer leaves. | | `changedLines` | `Map` | From the VCS diff of working tree vs HEAD/index. | | `originalText` | `string` | Full HEAD/index version of the open file, needed for the overlay. | | `editorScrollTop` | `number` | Mirrored into the overlay so the two panes stay in register. | | `selectedFile`, `stagedFiles`, `unstagedFiles` | — | Source control and explorer content. | Data needed: the file's working-tree content, its original content, and a line-level diff mapping current line numbers to previous content. The overlay must be able to render the original file immediately — pre-fetch it when a modified file is opened, not on hover. ## Design tokens **Surfaces** `#101720` base · `#18202B` raised · `#232C39` selected/hover · `#3A424C` rule-strong **Text** `#F4F5F4` strong · `#E4E7E6` body-strong · `#BAC0C0` body · `#828D9A` dim · `#6C7783` muted · `#5A6472` line numbers **Accent** `#E8913A` amber · `#C4741F` amber-deep (previous state) · `#F0B476` amber-soft (inline code) **Change** `#8FBFB4` current/changed, tint `rgba(143,191,180,.12)` · `#C4741F` previous, tint `rgba(196,116,31,.14)` **Syntax / chips** `#A8D6CB` string · `#F8C793` number-keyword · `#C3A6CE` structural · `#D8BEE4` directive · `#EFBCB0` selector · `#D4D9D8` property **Radius** 0 default · 2 px controls, chips, panels · 4 px window **Borders** 1 px `#232C39` hairline · 2 px accent, used only for the selection marker, the changed-line rule and a focused field **Shadow** dropdown `0 4px 16px rgba(23,28,34,.10)`; modal `0 16px 48px rgba(23,28,34,.18)`. Nothing else casts a shadow. **Row heights** 22 px meta · 24 px explorer · 26 px source-control file · 28 px status bar · 30 px menu · 34/38 px fields · 44 px title bar · 46 px panel headers **Type** IBM Plex Mono 400/500/600/700 — code, labels, all numbers; IBM Plex Sans 400/500/600 — UI text and prose. Code 13 px / 20 px. Labels 10 px, tracking `.14em`, uppercase. UI text 12–13 px. **Do not use** green or red for change, gradients, blur, emoji, white or pure black. ## Assets None. No images, icons or fonts ship with this handoff. Both faces load from Google Fonts (IBM Plex Mono, IBM Plex Sans) — swap in the licensed binaries in the app. Every glyph in the design is a Unicode mark set in Plex Mono (`›` `⌄` `▤` `↑` `⑂` `·` `→` `▸▸` `✳` `└`); if the app already uses Lucide at stroke width 1.75, those are the sanctioned replacements for the folder and arrow marks. ## Files - `03b-in-pane-diff.html` — the screen, standalone. Hover the teal line 31 (`- The scheduler fires it. Job \`mail-checker-ronde\`, every hour, on the hour.`) to see the original overlay. - Source of truth in the design project: `Helder IDE.dc.html`, section **[ 03 ] Diff**, screen **3b**. Section 3a in the same file shows the older full-screen side-by-side diff, kept for reference; 3b replaces it for in-pane use.