Files
helder/design_handoff_helder_inline_diff

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: EXPLORERBOB.
  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 gridgrid-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<lineNumber, { current: string, previous: string, staged: boolean }> 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 1213 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.