Skip to content

Commit 55648e0

Browse files
docs(specs): create 004-side-by-side-alignment
1 parent d4a7bd9 commit 55648e0

4 files changed

Lines changed: 322 additions & 0 deletions

File tree

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Data Model: Side-by-Side Diff Alignment
2+
3+
## Types
4+
5+
### DiffRowPair
6+
7+
Represents a single row in the side-by-side diff view.
8+
9+
```typescript
10+
interface DiffRowPair {
11+
original: DiffLine | null;
12+
modified: DiffLine | null;
13+
}
14+
```
15+
16+
**Fields:**
17+
18+
- `original` - The diff line to display on the left (original) side, or `null` for placeholder
19+
- `modified` - The diff line to display on the right (modified) side, or `null` for placeholder
20+
21+
## Algorithm: pairLines
22+
23+
Converts a flat array of `DiffLine[]` into paired rows for side-by-side display.
24+
25+
### Input
26+
27+
```typescript
28+
lines: DiffLine[]
29+
```
30+
31+
### Output
32+
33+
```typescript
34+
DiffRowPair[]
35+
```
36+
37+
### Logic Flow
38+
39+
1. **Unchanged lines**: Pair with themselves on both sides
40+
41+
```typescript
42+
{ original: line, modified: line }
43+
```
44+
45+
2. **Consecutive removed/added lines**: Collect and pair together
46+
47+
```typescript
48+
// Collect all consecutive removed lines
49+
removedLines = [removed1, removed2, ...]
50+
51+
// Collect all consecutive added lines that follow
52+
addedLines = [added1, added2, ...]
53+
54+
// Pair them using max length
55+
maxLength = Math.max(removedLines.length, addedLines.length)
56+
for (k = 0; k < maxLength; k++) {
57+
pairs.push({
58+
original: removedLines[k] ?? null,
59+
modified: addedLines[k] ?? null
60+
})
61+
}
62+
```
63+
64+
3. **Standalone added lines**: Pair with null on left
65+
```typescript
66+
{ original: null, modified: line }
67+
```
68+
69+
## Examples
70+
71+
### Example 1: Equal removed/added lines
72+
73+
**Input:**
74+
75+
```typescript
76+
[
77+
{ type: 'removed', text: 'old1' },
78+
{ type: 'removed', text: 'old2' },
79+
{ type: 'added', text: 'new1' },
80+
{ type: 'added', text: 'new2' },
81+
];
82+
```
83+
84+
**Output:**
85+
86+
```typescript
87+
[
88+
{ original: 'old1', modified: 'new1' },
89+
{ original: 'old2', modified: 'new2' },
90+
];
91+
```
92+
93+
### Example 2: More removed than added
94+
95+
**Input:**
96+
97+
```typescript
98+
[
99+
{ type: 'removed', text: 'old1' },
100+
{ type: 'removed', text: 'old2' },
101+
{ type: 'removed', text: 'old3' },
102+
{ type: 'added', text: 'new1' },
103+
];
104+
```
105+
106+
**Output:**
107+
108+
```typescript
109+
[
110+
{ original: 'old1', modified: 'new1' },
111+
{ original: 'old2', modified: null },
112+
{ original: 'old3', modified: null },
113+
];
114+
```
115+
116+
### Example 3: More added than removed
117+
118+
**Input:**
119+
120+
```typescript
121+
[
122+
{ type: 'removed', text: 'old1' },
123+
{ type: 'added', text: 'new1' },
124+
{ type: 'added', text: 'new2' },
125+
{ type: 'added', text: 'new3' },
126+
];
127+
```
128+
129+
**Output:**
130+
131+
```typescript
132+
[
133+
{ original: 'old1', modified: 'new1' },
134+
{ original: null, modified: 'new2' },
135+
{ original: null, modified: 'new3' },
136+
];
137+
```
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Implementation Plan: Side-by-Side Diff Alignment
2+
3+
## Overview
4+
5+
Improve the side-by-side diff view by aligning consecutive removed and added lines on the same row, matching standard diff tool behavior.
6+
7+
## Implementation Steps
8+
9+
### 1. Test Design βœ…
10+
11+
- [x] Write test for consecutive removed/added lines aligned on same row
12+
- [x] Write test for multiple consecutive removed/added lines
13+
- [x] Write test for more removed lines than added lines
14+
- [x] Write test for more added lines than removed lines
15+
- [x] Write test for non-consecutive removed/added lines
16+
- [x] Update existing tests to reflect new row counts
17+
18+
### 2. Implementation βœ…
19+
20+
- [x] Update `pairLines` function to use look-ahead algorithm
21+
- [x] Collect consecutive removed lines
22+
- [x] Collect consecutive added lines that follow
23+
- [x] Pair them using max length with null placeholders
24+
- [x] Maintain correct line numbering
25+
26+
### 3. Verification βœ…
27+
28+
- [x] Run test suite to verify all tests pass
29+
- [x] Verify 100% code coverage achieved
30+
- [x] Run linter to ensure code quality
31+
- [x] Run type checker to ensure type safety
32+
- [x] Manual testing in browser
33+
34+
## Test Results
35+
36+
```
37+
Test Files 12 passed (12)
38+
Tests 113 passed (113)
39+
Coverage 100% (statements, branches, functions, lines)
40+
```
41+
42+
## Files Modified
43+
44+
- `src/components/DiffViewer/SideBySideView.tsx` - Updated `pairLines` function
45+
- `src/components/DiffViewer/SideBySideView.test.tsx` - Added/updated tests
46+
47+
## Completion Status
48+
49+
βœ… **COMPLETED** - All tests passing with 100% coverage
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Quickstart: Side-by-Side Diff Alignment
2+
3+
## What Changed
4+
5+
The side-by-side diff view now aligns consecutive removed and added lines on the same row, making it easier to see what changed between the original and modified versions.
6+
7+
## Example
8+
9+
**Before:**
10+
11+
```
12+
Row 1: [Line 3: removed text] [empty]
13+
Row 2: [empty] [Line 3: added text]
14+
```
15+
16+
**After:**
17+
18+
```
19+
Row 1: [Line 3: removed text] [Line 3: added text]
20+
```
21+
22+
## Testing
23+
24+
Run the test suite:
25+
26+
```bash
27+
npm run test:ci
28+
```
29+
30+
Expected: All 113 tests pass with 100% coverage
31+
32+
## Manual Testing
33+
34+
1. Start the dev server:
35+
36+
```bash
37+
npm start
38+
```
39+
40+
2. Enter text with changes in both input areas
41+
42+
3. Switch to "Side-by-Side" view
43+
44+
4. Observe that removed lines (red, left side) align horizontally with added lines (green, right side)
45+
46+
## Key Files
47+
48+
- `src/components/DiffViewer/SideBySideView.tsx` - Implementation
49+
- `src/components/DiffViewer/SideBySideView.test.tsx` - Tests
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Feature Specification: Side-by-Side Diff Alignment
2+
3+
**Feature Branch**: `004-side-by-side-alignment`
4+
**Created**: 2026-03-04
5+
**Status**: Completed
6+
**Input**: User observation: "Would it make sense in SideBySideView to align the original and modified diff next to each other?"
7+
8+
## Problem Statement
9+
10+
In the side-by-side diff view, consecutive removed and added lines were displayed on separate rows with empty placeholders, making it difficult to see what changed between the original and modified versions. This is inconsistent with standard diff tools (GitHub, GitLab, etc.) which align removed/added lines on the same row for easier comparison.
11+
12+
### Before
13+
14+
- Line 3 (removed) appeared on its own row with empty space on the right
15+
- Line 3 (added) appeared on the next row with empty space on the left
16+
- Users had to visually scan across multiple rows to understand what changed
17+
18+
### After
19+
20+
- Consecutive removed/added lines are paired together on the same row
21+
- Removed content appears on the left, added content on the right
22+
- Users can immediately see what was changed by comparing horizontally
23+
24+
## Requirements
25+
26+
### Functional Requirements
27+
28+
- **FR-001**: System MUST pair consecutive removed and added lines on the same row in side-by-side view
29+
- **FR-002**: System MUST handle multiple consecutive removed lines followed by multiple consecutive added lines by pairing them in order (1st removed with 1st added, 2nd removed with 2nd added, etc.)
30+
- **FR-003**: System MUST handle unequal numbers of removed and added lines by filling remaining rows with placeholders
31+
- **FR-004**: System MUST preserve existing behavior for non-consecutive removed/added lines (separated by unchanged lines)
32+
- **FR-005**: System MUST maintain correct line numbering for both original and modified sides
33+
34+
### Technical Requirements
35+
36+
- **TR-001**: The `pairLines` function MUST use a look-ahead algorithm to group consecutive removed/added lines
37+
- **TR-002**: Implementation MUST maintain 100% test coverage
38+
- **TR-003**: All existing tests MUST continue to pass with updated expectations
39+
40+
## Implementation Details
41+
42+
### Modified Component
43+
44+
- `src/components/DiffViewer/SideBySideView.tsx`
45+
46+
### Algorithm
47+
48+
The `pairLines` function was updated to:
49+
50+
1. Iterate through diff lines sequentially
51+
2. When encountering a removed line, collect all consecutive removed lines
52+
3. Look ahead to collect any consecutive added lines that follow
53+
4. Pair them together using the maximum count (filling with `null` for missing entries)
54+
5. Continue with remaining lines
55+
56+
### Test Coverage
57+
58+
Added comprehensive tests covering:
59+
60+
- Consecutive removed/added lines aligned on same row
61+
- Multiple consecutive removed/added lines
62+
- More removed lines than added lines
63+
- More added lines than removed lines
64+
- Non-consecutive removed/added lines (with unchanged lines between them)
65+
66+
## Success Criteria
67+
68+
- βœ… **SC-001**: Consecutive removed and added lines appear on the same row in side-by-side view
69+
- βœ… **SC-002**: Line numbers remain accurate for both original and modified columns
70+
- βœ… **SC-003**: All 113 tests pass with 100% coverage (statements, branches, functions, lines)
71+
- βœ… **SC-004**: No visual regression in unified view or other components
72+
- βœ… **SC-005**: Improved user experience matches standard diff tool behavior
73+
74+
## Verification
75+
76+
```bash
77+
# Run tests with coverage
78+
npm run test:ci
79+
80+
# Expected results:
81+
# βœ“ All 113 tests passing
82+
# βœ“ 100% coverage across all metrics
83+
```
84+
85+
## Related Specs
86+
87+
- `001-text-diff` - Original diff implementation

0 commit comments

Comments
Β (0)