Add selective line restoration, file-history annotation, and sidebar section menu #52

Merged
Christoph merged 5 commits from UI-UX into main 2026-09-18 18:48:06 +00:00
Owner

This change implements three user-facing and internal updates: selective restoration of lines from a historical commit into the working file, an annotation on file history entries that marks when the latest history entry matches the working tree, and a small left-sidebar section menu to toggle visibility of non-branch panels. It also includes a couple of UI polish and styling tweaks (repository select cursor and per-group accents in the branch panel).

What changed (behavioral summary)

  • Selective line restoration from historical commits

    • New Tauri command get_file_restore_patch(path, commit, file) that produces the reverse diff (historical -> working) for a single file. The patch text is filtered to remove mode lines and returned as UTF-8 text.
    • apply_file_patch now supports an additional action string "restore-lines". Applying this action validates the patch (validate_restore_patch) and then checks/applies it via the existing patch application helpers.
    • Validation enforces: the target is an existing regular file, the patch contains only text-line changes (rejects mode, rename, binary, and git-binary-patch style lines), and the patch must only modify the single selected file (checked via git apply --numstat -z).
    • Frontend wiring: App.svelte imports and uses getFileRestorePatch, opens a line-restore flow from the compare dialog (onRestoreLines), exposes a Restore lines… button in CompareDialog.svelte when applicable, and provides restoreSelectedLines which invokes applyFilePatch(..., "restore-lines"), refreshes diffs/history/views, and surfaces any errors.
    • Tests added (Rust unit tests) cover that selected-line restores preserve unselected changes and the index, and that invalid or stale patches are rejected.
  • File history annotation

    • The file-history command result is now wrapped in FileHistoryCommit { commit: GitCommit, matches_working_tree: bool }.
    • list_file_history now returns the annotated commits; annotate_file_history computes matches_working_tree by running a git diff --quiet of the first returned commit against the working tree for regular files only. The implementation only marks the first entry (index 0) as matching the working tree when appropriate.
    • A unit test was added that asserts the boolean toggles only when the current working file exactly matches the latest commit entry and under changes like local edits, staging, committing, and deletion.
  • Sidebar section menu and visibility toggles

    • A new SidebarSectionMenu component is imported and opened on contextmenu over the left sidebar (right-click). It allows toggling visibility for non-branch panels: Worktrees, Tags, Stashes, Files (Explorer).
    • Visibility is persisted in localStorage under gitlite.sidebarVisibility.v1. The layout and handle visibility logic (panel ordering and sizing) were updated to respect the sidebarVisibility map.
    • A small accessibility/behavior helper restores focus when the menu is closed.
  • UI and styling tweaks

    • RepoTabs.svelte: change repository-select cursor from grab to pointer for clickable select elements.
    • BranchPanel.svelte: per-group accent styling for branch group headers (local vs remote) via CSS variables and color-mix, with a subtle left inset shadow and hover color changes.

Implementation notes (helpful to review)

  • Rust backend

    • New functions in src-tauri/src/git.rs: get_file_restore_patch, validate_restore_patch, and annotate_file_history. apply_file_patch was extended to handle the restore-lines action by validating and then applying the patch.
    • list_file_history signature in the Rust tauri back-end now returns Vec<FileHistoryCommit> (annotated commits) instead of Vec<GitCommit>.
    • main.rs exports get_file_restore_patch to the Tauri command list so the frontend can call it.
  • Frontend

    • App.svelte additions: state and functions to open the historical-line restore flow (openHistoricalLineRestore), to fetch the historical patch (getFileRestorePatch), to apply selected lines (restoreSelectedLines), and to manage sidebarVisibility (load/save, toggle). The LinePatch component is passed a new restoreCommit prop in its invocation.
    • CompareDialog.svelte gained an optional onRestoreLines callback prop and shows a Restore lines… button when the comparison represents a historical comparison (no to_hash) and the selected file is a modified file (no old_path). When clicked it triggers the openHistoricalLineRestore flow.
    • Sidebar visibility is respected in the computed layout (buildLeftSidebarRows, sidebarHandleVisible, expandedSidebarPanels). Right-click on the left sidebar opens the section menu.

Tests

  • New tests added in src-tauri/src/git.rs tests module:
    • restore_lines_preserves_unselected_changes_and_index
    • restore_lines_rejects_stale_or_wrong_file_patches
    • file_history_marks_only_an_identical_current_file

No test execution results were provided. The commits and tests are present in the diff but the PR does not include CI output.

Recommended checks for reviewers:

  • Backend unit tests: run cargo test for the src-tauri crate to verify the new tests and ensure no regressions.
  • Manual UI flow: open a repository, open a historical comparison (a compare shown with from_hash and no to_hash), select a modified file in the comparison, click the new Restore lines… button, select/apply a subset of hunks in the line-patch dialog and confirm that:
    1. only the selected lines are restored from the historical commit,
    2. other unstaged or staged changes are preserved as expected,
    3. index/staged blob contents remain unchanged when the test expects it.
  • Verify file-history API shape: consumers of the list_file_history tauri command should now expect objects with commit and matches_working_tree keys; ensure frontend FileHistoryDialog uses the new shape (the diff shows corresponding UI changes, but please confirm no external consumers rely on the old plain GitCommit array).
  • Sidebar persistence: toggle several panels via the new section menu and reload the app to confirm visibility persists in localStorage under the key gitlite.sidebarVisibility.v1.

Compatibility / reviewer actions

  • The tauri return shape for list_file_history changed to include matches_working_tree. If any external code or plugin expected a plain GitCommit list from that command, it must be updated to handle FileHistoryCommit objects.
  • The new get_file_restore_patch command is exported; review callers for correct usage and error handling.
  • The new restore-lines action path in apply_file_patch performs stricter validation than generic patch application — this is intentional and enforced in validate_restore_patch.

If you want, I can list the specific files that changed for each of the bullet items above or produce a short checklist of smoke tests to run in the UI (open compare, right-click sidebar, etc.).

This change implements three user-facing and internal updates: selective restoration of lines from a historical commit into the working file, an annotation on file history entries that marks when the latest history entry matches the working tree, and a small left-sidebar section menu to toggle visibility of non-branch panels. It also includes a couple of UI polish and styling tweaks (repository select cursor and per-group accents in the branch panel). ## What changed (behavioral summary) - Selective line restoration from historical commits - New Tauri command `get_file_restore_patch(path, commit, file)` that produces the reverse diff (historical -> working) for a single file. The patch text is filtered to remove mode lines and returned as UTF-8 text. - `apply_file_patch` now supports an additional action string `"restore-lines"`. Applying this action validates the patch (`validate_restore_patch`) and then checks/applies it via the existing patch application helpers. - Validation enforces: the target is an existing regular file, the patch contains only text-line changes (rejects mode, rename, binary, and git-binary-patch style lines), and the patch must only modify the single selected file (checked via `git apply --numstat -z`). - Frontend wiring: `App.svelte` imports and uses `getFileRestorePatch`, opens a line-restore flow from the compare dialog (`onRestoreLines`), exposes a `Restore lines…` button in `CompareDialog.svelte` when applicable, and provides `restoreSelectedLines` which invokes `applyFilePatch(..., "restore-lines")`, refreshes diffs/history/views, and surfaces any errors. - Tests added (Rust unit tests) cover that selected-line restores preserve unselected changes and the index, and that invalid or stale patches are rejected. - File history annotation - The file-history command result is now wrapped in `FileHistoryCommit { commit: GitCommit, matches_working_tree: bool }`. - `list_file_history` now returns the annotated commits; `annotate_file_history` computes `matches_working_tree` by running a `git diff --quiet` of the first returned commit against the working tree for regular files only. The implementation only marks the first entry (index 0) as matching the working tree when appropriate. - A unit test was added that asserts the boolean toggles only when the current working file exactly matches the latest commit entry and under changes like local edits, staging, committing, and deletion. - Sidebar section menu and visibility toggles - A new `SidebarSectionMenu` component is imported and opened on contextmenu over the left sidebar (right-click). It allows toggling visibility for non-branch panels: Worktrees, Tags, Stashes, Files (Explorer). - Visibility is persisted in localStorage under `gitlite.sidebarVisibility.v1`. The layout and handle visibility logic (panel ordering and sizing) were updated to respect the `sidebarVisibility` map. - A small accessibility/behavior helper restores focus when the menu is closed. - UI and styling tweaks - `RepoTabs.svelte`: change repository-select cursor from `grab` to `pointer` for clickable select elements. - `BranchPanel.svelte`: per-group accent styling for branch group headers (local vs remote) via CSS variables and color-mix, with a subtle left inset shadow and hover color changes. ## Implementation notes (helpful to review) - Rust backend - New functions in `src-tauri/src/git.rs`: `get_file_restore_patch`, `validate_restore_patch`, and `annotate_file_history`. `apply_file_patch` was extended to handle the `restore-lines` action by validating and then applying the patch. - `list_file_history` signature in the Rust tauri back-end now returns `Vec<FileHistoryCommit>` (annotated commits) instead of `Vec<GitCommit>`. - `main.rs` exports `get_file_restore_patch` to the Tauri command list so the frontend can call it. - Frontend - `App.svelte` additions: state and functions to open the historical-line restore flow (`openHistoricalLineRestore`), to fetch the historical patch (`getFileRestorePatch`), to apply selected lines (`restoreSelectedLines`), and to manage `sidebarVisibility` (load/save, toggle). The `LinePatch` component is passed a new `restoreCommit` prop in its invocation. - `CompareDialog.svelte` gained an optional `onRestoreLines` callback prop and shows a `Restore lines…` button when the comparison represents a historical comparison (no `to_hash`) and the selected file is a modified file (no `old_path`). When clicked it triggers the `openHistoricalLineRestore` flow. - Sidebar visibility is respected in the computed layout (`buildLeftSidebarRows`, `sidebarHandleVisible`, `expandedSidebarPanels`). Right-click on the left sidebar opens the section menu. ## Tests - New tests added in `src-tauri/src/git.rs` tests module: - `restore_lines_preserves_unselected_changes_and_index` - `restore_lines_rejects_stale_or_wrong_file_patches` - `file_history_marks_only_an_identical_current_file` No test execution results were provided. The commits and tests are present in the diff but the PR does not include CI output. Recommended checks for reviewers: - Backend unit tests: run `cargo test` for the `src-tauri` crate to verify the new tests and ensure no regressions. - Manual UI flow: open a repository, open a historical comparison (a compare shown with `from_hash` and no `to_hash`), select a modified file in the comparison, click the new `Restore lines…` button, select/apply a subset of hunks in the line-patch dialog and confirm that: 1) only the selected lines are restored from the historical commit, 2) other unstaged or staged changes are preserved as expected, 3) index/staged blob contents remain unchanged when the test expects it. - Verify file-history API shape: consumers of the `list_file_history` tauri command should now expect objects with `commit` and `matches_working_tree` keys; ensure frontend `FileHistoryDialog` uses the new shape (the diff shows corresponding UI changes, but please confirm no external consumers rely on the old plain `GitCommit` array). - Sidebar persistence: toggle several panels via the new section menu and reload the app to confirm visibility persists in localStorage under the key `gitlite.sidebarVisibility.v1`. ## Compatibility / reviewer actions - The tauri return shape for `list_file_history` changed to include `matches_working_tree`. If any external code or plugin expected a plain `GitCommit` list from that command, it must be updated to handle `FileHistoryCommit` objects. - The new `get_file_restore_patch` command is exported; review callers for correct usage and error handling. - The new `restore-lines` action path in `apply_file_patch` performs stricter validation than generic patch application — this is intentional and enforced in `validate_restore_patch`. If you want, I can list the specific files that changed for each of the bullet items above or produce a short checklist of smoke tests to run in the UI (open compare, right-click sidebar, etc.).
Christoph added 5 commits 2026-09-18 18:47:53 +00:00
Add a floating SidebarSectionMenu component and wiring in App.svelte to let users show/hide individual left-sidebar panels (worktrees, tags, stashes, files). Visibility is tracked in new sidebarVisibility state and persisted to localStorage under SIDEBAR_VISIBILITY_KEY. The menu opens via contextmenu on the left sidebar and returns focus to the invoking element when closed.

- Panels and resize handles now respect visibility (buildLeftSidebarRows, sidebarHandleVisible, expandedSidebarPanels).
- Branch panel remains always visible and cannot be toggled; stored preferences only apply to the other panels.
- Safe fallbacks: localStorage errors are ignored so the feature still works without persistence; menu includes keyboard navigation and appropriate ARIA roles.
Previously the enabled repository select used cursor:grab, which could
mislead users into thinking the tab was draggable even when reordering
wasn't active. Change the non-disabled state to cursor:pointer to more
accurately indicate clickability. The grabbing cursor is still used
while reordering, so no change to drag UX.
Annotate file history entries with matches_working_tree and surface that
information in the UI so the most recent commit can be identified as
"current version" when its content equals the working tree.

- Backend: add FileHistoryCommit and annotate_file_history(...) which checks
  (only for regular files) whether the newest commit's blob matches the
  working tree via `git diff --quiet`. list_file_history now returns the
  annotated commits.
- Types: add optional matches_working_tree to GitCommit shape used by the UI.
- UI: show a "Current version"/"Aktueller Stand" badge and disable Diff/Restore
  actions for entries that match the working tree (text localized for de/en).

Also add a test that verifies the matching behavior across file edits,
staging, committing and deletion. No external API breaking changes.
Add a new Tauri command to produce a diff between the working file and a historical commit (get_file_restore_patch) and support a new apply action ("restore-lines") that validates and applies only text-line changes for a single regular file.

Behavior changes and constraints:
- Fetch a filtered reverse diff for a file in a commit so UI can display selectable lines from an older revision.
- Applying "restore-lines" verifies the target is a regular file, rejects binary/metadata patches, and ensures the patch only modifies the selected file.
- Restored lines are applied to the working tree without staging other changes; the index is preserved.
- The operation rejects stale patches or patches targeting the wrong file.

UI wiring:
- Compare dialog gets a "Restore lines…" action for applicable modified files and opens the line-patch dialog in restore mode.
- Line-patch dialog gains a restore mode (restoreCommit) with adjusted UI/rendering to pair removed/added lines, helper text, and dedicated "Restore selected" / "Restore hunk" actions.
- App integration handles fetching the restore patch, applying selected lines, and refreshing views.

Tests:
- Add tests covering correct behavior (preserve unstaged/staged changes and index) and guard cases (stale/wrong-file patches).
Add local/remote classes to the group header buttons and introduce a
--group-accent CSS variable used with color-mix for background, text tint,
hover state, and an inset accent shadow. Local headers use --color-info and
remote headers use --color-accent.

This is a visual-only change (no toggle/behavior changes): group headers now
have a subtle, distinct accent for local vs. remote sections instead of a
uniform muted surface.
Christoph merged commit 62c75dd2fa into main 2026-09-18 18:48:06 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Christoph/GitLite#52