Repository navigation
diff: Add a Git history example and complete review APIs - #3388
Merged
Merged
Conversation
huacnlee
enabled auto-merge (squash)
October 7, 2026 06:13
linruohan
pushed a commit
to linruohan/gpui-component
that referenced
this pull request
Oct 9, 2026
…3388) ## Description Add a desktop Git history browser inspired by tig as a complete Diff example. Keep commits and changed files in a resizable left sidebar, with source as the main work area. Default to Dark appearance and Split layout; retain syntax highlighting, wrapped source, added/deleted backgrounds and inline emphasis. Refine the command surfaces and containment: commit-body disclosure sits immediately after the subject, copying sits beside the hash, Refresh belongs to history, and compact change-navigation/settings buttons share the metadata row. The settings icon opens a top-right anchored menu containing layout, appearance and display choices. Commit details, sidebar headings and the source frame have theme-token borders. Changed-file rows have more vertical padding, file icons and aligned status/statistics. Commit bodies scroll independently and reset on commit changes. The sidebar toggle lives at the left of the status bar (Cmd/Ctrl+B). Status reports actual commit/file position and selection; shortcut hints use Kbd resolved from Actions. There are no function-key bindings. Startup `--commit`, `--file` and `--line` arguments reproduce a real source change without changing repository contents or limiting subsequent navigation. Fix source spacing when line numbers are disabled: rendered row insets now match the width budget, annotation anchors include the inset, and an empty Split cell uses the source row height. Wrapped rows remain aligned and selectable when line numbers change. Fix Diff wheel handling so vertical wheel input scrolls rows instead of being remapped to horizontal scrolling. Reuse the existing horizontal scroll mask. Clearing gutter selection now emits `SelectionChanged(None)` once without interrupting Shift extension. Complete the additive review API: expose parsed conflict coordinates/labels, add runtime presentation setters, and provide renderer names matching GPUI Kit conventions. Existing renderer names remain supported with unchanged signatures. This builds on longbridge#3378; it does not repeat the dependency update from longbridge#3380. English and Chinese Diff documentation are synchronized. ## Screenshot <img width="2100" height="1290" alt="image" src="https://github.com/user-attachments/assets/ecf65e9d-0efb-4ce7-8906-7c3ab00ec319" /> Reproduce with: ```sh cargo run -p example-tig -- --commit 4890b1c --file crates/component/src/speech/waveform.rs --line 70 . ``` Earlier screenshots are retained: [initial preview](https://raw.githubusercontent.com/longbridge/gpui-kit/diff-review-example/examples/tig/screenshots/before.png) and [intermediate preview](https://raw.githubusercontent.com/longbridge/gpui-kit/diff-review-example/examples/tig/screenshots/unified.png). ## Public API ### gpui-component (`gpui_kit::component::diff`) - `DiffConflict` (`Clone`, `Debug`) — parser-owned conflict snapshot with private fields. - `DiffConflict::lines(&self) -> Range<usize>` — one-based, half-open source range including markers. - `DiffConflict::current_lines(&self) -> Range<usize>` — current-side source range, possibly empty. - `DiffConflict::base_lines(&self) -> Option<Range<usize>>` — optional diff3 base source range. - `DiffConflict::incoming_lines(&self) -> Range<usize>` — incoming-side source range, possibly empty. - `DiffConflict::current_label(&self) -> &SharedString` — opening marker label. - `DiffConflict::base_label(&self) -> Option<&SharedString>` — optional base marker label. - `DiffConflict::incoming_label(&self) -> &SharedString` — closing marker label. - `DiffFile::conflicts(&self) -> &[DiffConflict]` — source-ordered snapshots; slice indices address conflict resolution. - `DiffState::set_expansion_lines(&mut self, lines: usize, cx: &mut Context<Self>)` — update incremental context expansion size. - `DiffState::set_min_collapsed_lines(&mut self, lines: usize, cx: &mut Context<Self>)` — update the context folding threshold while retaining the viewport anchor. - `DiffState::set_inline_unit(&mut self, unit: Option<DiffInlineUnit>, cx: &mut Context<Self>)` — switch word/grapheme emphasis or disable it. - `DiffState::set_inline_max_line_length(&mut self, length: usize, cx: &mut Context<Self>)` — update the inline preparation limit and invalidate earlier preparation. - `DiffState::set_syntax_max_line_length(&mut self, length: usize, cx: &mut Context<Self>)` — update the syntax preparation limit and invalidate earlier preparation. - `Diff::render_annotation<F, E>(self, render: F) -> Self` — render application-owned annotation content. - `Diff::render_header<F, E>(self, render: F) -> Self` — replace header content while retaining its shell and collapse control. - `Diff::render_header_prefix<F, E>(self, render: F) -> Self` — insert content before the path. - `Diff::render_header_title_suffix<F, E>(self, render: F) -> Self` — insert content directly after the path. - `Diff::render_header_suffix<F, E>(self, render: F) -> Self` — insert trailing content after statistics. For annotation renderers, `F: Fn(&DiffAnnotation, &mut Window, &mut App) -> E + 'static`; header renderers use `&DiffFile` instead. All require `E: IntoElement`. Existing `annotation_content`, `header`, `header_prefix`, `header_title_suffix`, and `header_suffix` keep their signatures as compatibility aliases. ## How to Test ```sh cargo run -p example-tig -- . ``` Select commits with Up/Down or j/k, use [ / ] to change files, Enter to focus source, and Escape to return to history. Use n/Shift+N to navigate changes and the settings menu to disable wrapping before checking vertical versus horizontal wheel gestures. Validated locally on Linux: - `cargo test -p example-tig --locked` — five tests, including production-view navigation, startup file selection, default Dark appearance, independent message scrolling and stale-result rejection. - `cargo test -p gpui-kit --features test-support --test diff --locked` — all 16 Diff UI tests, including wheel axes, gutter event ordering and wrapped-source selection with hidden/toggled line numbers. - Clippy with warnings denied for Component, Kit, Story and the example, including test targets. - Standard and `gpui-fast` example builds/checks; Component wasm check. - Format and whitespace checks; repository smoke checks for history, an empty bare repository and an invalid path. - Native Dark Split capture of a real SpeechWaveform change showing wrapped Rust, inline emphasis and added/deleted backgrounds; earlier standard/minimum-size inspections are retained. No full test suite was run. macOS and Windows runtime behavior was not tested locally. The `typos` executable is not installed locally. AI-assisted implementation and review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Add a desktop Git history browser inspired by tig as a complete Diff example. Keep commits and changed files in a resizable left sidebar, with source as the main work area. Default to Dark appearance and Split layout; retain syntax highlighting, wrapped source, added/deleted backgrounds and inline emphasis.
Refine the command surfaces and containment: commit-body disclosure sits immediately after the subject, copying sits beside the hash, Refresh belongs to history, and compact change-navigation/settings buttons share the metadata row. The settings icon opens a top-right anchored menu containing layout, appearance and display choices. Commit details, sidebar headings and the source frame have theme-token borders. Changed-file rows have more vertical padding, file icons and aligned status/statistics. Commit bodies scroll independently and reset on commit changes.
The sidebar toggle lives at the left of the status bar (Cmd/Ctrl+B). Status reports actual commit/file position and selection; shortcut hints use Kbd resolved from Actions. There are no function-key bindings. Startup
--commit,--fileand--linearguments reproduce a real source change without changing repository contents or limiting subsequent navigation.Fix source spacing when line numbers are disabled: rendered row insets now match the width budget, annotation anchors include the inset, and an empty Split cell uses the source row height. Wrapped rows remain aligned and selectable when line numbers change.
Fix Diff wheel handling so vertical wheel input scrolls rows instead of being remapped to horizontal scrolling. Reuse the existing horizontal scroll mask. Clearing gutter selection now emits
SelectionChanged(None)once without interrupting Shift extension.Complete the additive review API: expose parsed conflict coordinates/labels, add runtime presentation setters, and provide renderer names matching GPUI Kit conventions. Existing renderer names remain supported with unchanged signatures. This builds on #3378; it does not repeat the dependency update from #3380. English and Chinese Diff documentation are synchronized.
Screenshot
Reproduce with:
cargo run -p example-tig -- --commit 4890b1c2 --file crates/component/src/speech/waveform.rs --line 70 .Earlier screenshots are retained: initial preview and intermediate preview.
Public API
gpui-component (
gpui_kit::component::diff)DiffConflict(Clone,Debug) — parser-owned conflict snapshot with private fields.DiffConflict::lines(&self) -> Range<usize>— one-based, half-open source range including markers.DiffConflict::current_lines(&self) -> Range<usize>— current-side source range, possibly empty.DiffConflict::base_lines(&self) -> Option<Range<usize>>— optional diff3 base source range.DiffConflict::incoming_lines(&self) -> Range<usize>— incoming-side source range, possibly empty.DiffConflict::current_label(&self) -> &SharedString— opening marker label.DiffConflict::base_label(&self) -> Option<&SharedString>— optional base marker label.DiffConflict::incoming_label(&self) -> &SharedString— closing marker label.DiffFile::conflicts(&self) -> &[DiffConflict]— source-ordered snapshots; slice indices address conflict resolution.DiffState::set_expansion_lines(&mut self, lines: usize, cx: &mut Context<Self>)— update incremental context expansion size.DiffState::set_min_collapsed_lines(&mut self, lines: usize, cx: &mut Context<Self>)— update the context folding threshold while retaining the viewport anchor.DiffState::set_inline_unit(&mut self, unit: Option<DiffInlineUnit>, cx: &mut Context<Self>)— switch word/grapheme emphasis or disable it.DiffState::set_inline_max_line_length(&mut self, length: usize, cx: &mut Context<Self>)— update the inline preparation limit and invalidate earlier preparation.DiffState::set_syntax_max_line_length(&mut self, length: usize, cx: &mut Context<Self>)— update the syntax preparation limit and invalidate earlier preparation.Diff::render_annotation<F, E>(self, render: F) -> Self— render application-owned annotation content.Diff::render_header<F, E>(self, render: F) -> Self— replace header content while retaining its shell and collapse control.Diff::render_header_prefix<F, E>(self, render: F) -> Self— insert content before the path.Diff::render_header_title_suffix<F, E>(self, render: F) -> Self— insert content directly after the path.Diff::render_header_suffix<F, E>(self, render: F) -> Self— insert trailing content after statistics.For annotation renderers,
F: Fn(&DiffAnnotation, &mut Window, &mut App) -> E + 'static; header renderers use&DiffFileinstead. All requireE: IntoElement. Existingannotation_content,header,header_prefix,header_title_suffix, andheader_suffixkeep their signatures as compatibility aliases.How to Test
cargo run -p example-tig -- .Select commits with Up/Down or j/k, use [ / ] to change files, Enter to focus source, and Escape to return to history. Use n/Shift+N to navigate changes and the settings menu to disable wrapping before checking vertical versus horizontal wheel gestures.
Validated locally on Linux:
cargo test -p example-tig --locked— five tests, including production-view navigation, startup file selection, default Dark appearance, independent message scrolling and stale-result rejection.cargo test -p gpui-kit --features test-support --test diff --locked— all 16 Diff UI tests, including wheel axes, gutter event ordering and wrapped-source selection with hidden/toggled line numbers.gpui-fastexample builds/checks; Component wasm check.No full test suite was run. macOS and Windows runtime behavior was not tested locally. The
typosexecutable is not installed locally.AI-assisted implementation and review.