Skip to content

Toggle file lists between a path-sorted list and a tree - #330

Open
tresat wants to merge 1 commit into
ness-dev:mainfrom
tresat:tt/tree-views-for-files
Open

tresat wants to merge 1 commit into
ness-dev:mainfrom
tresat:tt/tree-views-for-files

Conversation

@tresat

@tresat tresat commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Each file list in the right column gets a button that switches it between a flat, path-sorted list and a folder tree: the Uncommitted and Committed sections of Changed Files, and All Files.

Changed Files still starts as a list, and All Files still starts as a tree, so nothing changes until you click. All Files gains a list view it didn't have before. Each list remembers its choice per client in localStorage, the same way panels remember being collapsed.

Changed Files: list (default) Changed Files: tree

All Files in list view, filtered. As in the tree, files changed on the branch sort first while filtering:

Implementation

  • All Files' buildTree / sortChildren and its TreeBranch / DirRow renderer move into src/renderer/file-tree.ts and components/FileTree.tsx, and both panels use them, so the trees look and behave the same. Changed-file trees start expanded; All Files still starts collapsed.
  • Folders are keyed with a trailing /, so a file and a folder with the same name (a deleted foo plus a new foo/bar) both show.
  • Staged renames (old -> new from git status --porcelain) are placed and named by their destination. destOf moves from worktree.ts to src/shared/git-paths.ts so main and renderer share it.
  • All Files' list view renders at most 1,000 rows, because nothing in that panel is virtualized. Rows are memoized with ref-stabilized callbacks, and the changed-files map only feeds the list while filtering. That way neither changed-files refreshes nor App re-renders redraw the list.

Testing

  • npm run typecheck, npx electron-vite build, npx vitest run: 1,844 passing, including the new file-tree.test.ts.
  • Checked by hand in the headless web client:
    • both views in all three lists, and the saved choice
    • a collapsed folder staying collapsed through a refresh
    • staged renames, including one that moves a file to the repo root
    • a file and a folder sharing a name
    • All Files list filtering, and opening a file / using the @ button from a row

Known issues, not fixed here

  • Clicking a staged rename opens a diff tab that says "No changes", because the diff opener gets the raw old -> new path. The list view did this before this PR too.
  • Paths that git status --porcelain quotes (spaces, non-ASCII) still parse wrong. Also not new.

Screenshots are from the headless web client, using this PR's own branch as demo data.

Changed Files sections default to the existing flat path list and can
switch to a tree; All Files keeps its tree default and gains a flat list
view, capped at 1000 rows since nothing is virtualized. Each list's
choice persists per client in localStorage.

All Files' buildTree/sortChildren and TreeBranch/DirRow move to shared
modules so both panels render trees identically. Dirs are keyed with a
trailing "/" so a file and dir sharing a name (deleted `foo` plus new
`foo/bar`) both show. Staged renames are placed and named by their
destination path; destOf moves to src/shared/git-paths.ts for reuse.

All Files list rows are memoized with ref-stabilized callbacks, and the
changed-files map only feeds the list memo while filtering.
@tresat tresat changed the title Add list/tree view toggles to All Files, Uncommitted and Committed lists Toggle file lists between a path-sorted list and a tree Oct 22, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant