Files
boocode/openspec/changes/enhanced-file-panel/design.md
indifferentketchup c935687725 chore(openspec): drop 9 superseded proposals + 11 stub archive files
Drop 9 batch proposals that are superseded by the boocode-lift-analysis
(boocontext-audit, conductor upgrades, self-healing/verify-gate skills):
add-3tier-memory, import-llm-evaluator, import-pregel-engine, plugin-platform,
conductor-evolution, code-intelligence-upgrade, dev-workflow, ui-overhaul,
agent-reliability.

Delete 11 stub archive files (49-66B each, 'Status: Shipped. Archived.' only)
that provide zero documentation value over the existing CHANGELOG.md + git tags.
2026-06-07 22:15:38 +00:00

2.0 KiB

Enhanced File Panel — Design Decisions

D1: Diff preference system

A useDiffPreferences hook manages three stored preferences:

interface DiffPreferences {
  layout: 'unified' | 'split';    // default: 'unified'
  wrapLines: boolean;              // default: false
  hideWhitespace: boolean;         // default: false
}

Persisted via localStorage key boocode.diff.preferences. Reference: /opt/forks/paseo/hooks/use-changes-preferences/storage.ts

D2: Side-by-side diff layout

Split layout renders two columns (left = removals, right = additions) with aligned line numbers.

Algorithm (adapted from Paseo diff-layout.ts buildSplitDiffRows):

  1. Parse unified diff into hunks (reuse existing splitDiffByFile)
  2. Group remove lines into pendingRemovals, add lines into pendingAdditions
  3. Flush paired rows when encountering context lines
  4. Unpaired lines get an empty cell on one side

D3: Hide whitespace (server-side)

GET /api/projects/:id/git/diff gains optional whitespace=1 query param. When set, git diff appends -w (--ignore-all-space).

D4: Wrap lines (CSS-only)

When wrapLines is true: white-space: pre-wrap; overflow-wrap: anywhere replaces white-space: pre; overflow-x: auto. Gutter stays unwrapped.

D5: Expand/Collapse all

allExpanded computed as files.every(f => expandedPaths.has(f.path)). Toggle button adds all paths to expandedPaths or clears them.

D6: Inline diff comments

Data model:

interface DiffComment {
  id: string; filePath: string; side: 'old' | 'new';
  lineNumber: number; body: string;
  createdAt: number; updatedAt: number;
}

Zustand store with localStorage persistence, keyed by ${sessionId}:${mode}.

D7: File editing

Double-click file in tree → fetch content via view_file → textarea → Save calls POST /api/projects/:id/write_file → triggers git_diff_refresh. Path validated via existing pathGuard.

D8: No DB / no WS frames / no contract changes

All new state is client-side (localStorage, React state, Zustand).