Adds a better diff design and removes the select and copy feature
This commit is contained in:
245
design_handoff_helder_inline_diff/README.md
Normal file
245
design_handoff_helder_inline_diff/README.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# 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<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 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.
|
||||
Reference in New Issue
Block a user