Skip to content

Lite: file tree: give directory rows (most of) the features from file rows - #15949

Open
cbjeukendrup wants to merge 9 commits into
gitbutlerapp:masterfrom
cbjeukendrup:lite-directory-file-actions
Open

cbjeukendrup wants to merge 9 commits into
gitbutlerapp:masterfrom
cbjeukendrup:lite-directory-file-actions

Conversation

@cbjeukendrup

@cbjeukendrup cbjeukendrup commented Sep 14, 2026

Copy link
Copy Markdown
Contributor
  • Context menu
    Scherm­afbeelding 2026-09-14 om 12 27 04

  • 'Reviewed' check marks
    Scherm­afbeelding 2026-09-14 om 12 30 38

@cbjeukendrup
cbjeukendrup marked this pull request as ready for review September 14, 2026 10:31
@cbjeukendrup
cbjeukendrup force-pushed the lite-directory-file-actions branch 2 times, most recently from 3bf280f to 4e7695a Compare September 20, 2026 20:49
@PavelLaptev

Copy link
Copy Markdown
Contributor

@cbjeukendrup thanks for the PR!
How folder "checkmarks" work?

@cbjeukendrup

Copy link
Copy Markdown
Contributor Author

A folder is marked 'reviewed' iff all files under it are reviewed:

Schermopname.2026-09-21.om.09.37.00.mov

@PavelLaptev

Copy link
Copy Markdown
Contributor

Schermopname.2026-09-21.om.09.37.00.mov

Gotcha. I wonder if we should add the ability to check files and folders via right-click? Currently, it only works via the UI button, and reviewing a folder isn't possible yet.

@cbjeukendrup

Copy link
Copy Markdown
Contributor Author

I could look into that. 'Marking a folder (un)reviewed' means 'marking all files under it (un)reviewed', right?

(Or do you think we should give folders their own reviewed state? I'm not sure that'd be a good idea, because the folder is in a way only a transient UI thing: if the file tree changes (files added/removed), the folder might be split, or disappear its parent.)

cbjeukendrup and others added 8 commits September 21, 2026 10:03
A directory row held `filePaths`, which is enough for the checkbox and
drag sources but says nothing about what each file is. Carrying the items
themselves lets a directory tell a change from a conflict and reach the
`TreeChange` behind it, which is what acting on a subtree needs.

`filePaths` was a projection of the items, so keeping both would have been
two sources of truth.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cut, absorb, uncommit and discard were each written out three times — in
the file row's menu, in the checked-set toolbar, and (for uncommit) as a
closure in Details threaded down as a prop. They differ only in what they
are addressed to, so they now live in `useFileSetActions`, which names its
subject by address and looks the changes back up itself.

`useFileSetSubject` is the other half: it decides whether a row stands for
its own files or gives way to the checked set, and reports the count so a
label can say what it is about to act on. Cut, absorb and uncommit now
count as discard always has.

A linked worktree's files keep the terms they were given: they cut into the
workspace like any other, but absorb and discard act on the project's own
checkout, so `useFileSetActions` reports neither and `fileSetMenuItems`
offers neither. Said once there, it holds for every row addressed to a set,
not just the file row it was written for.

`useDiscardFileChanges` loses its `extendToCheckedFiles` flag: deciding the
subject was never the mutation's job. The `uncommit`/`canUncommit` props
threaded through FilesTree and the diff viewer go with it, the worktree
lane's included.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Right-click a folder, or reach for its kebab, and it offers what its path
alone can answer for: open it in an editor, reveal it in the file manager,
copy its path, and fold or unfold it. None of these ask what the folder 
belongs to, so they come straight from the group a file row already uses.

The row splits the way a file row does, so a list being scrolled pays for
none of it — but the scrolling half still renders the row whole: an inert
button where the kebab goes, and nothing behind a right-click that has no
menu to open yet. A row that gained its controls back as the list settled
would be its own flicker. That placeholder is the one a file row already
uses, so it moves to `Row.tsx`, next to the toolbar it sits in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cut, absorb, uncommit and discard now reach a folder, addressed to every
file below it, and the labels count what they are about to touch. A folder
gives way to the checked set on the same terms a file does — a file when it
is checked, a folder when its box reads checked, which is when every file
below it is.

Conflicts below a folder are no part of that subject; they have nothing to
commit or discard yet. They get "Mark as Resolved" instead, which already
took a list of paths.

Knowing what it belongs to also tells a folder where it lives: in a linked
worktree it opens and reveals against that checkout, as a file row there
already did, and offers the same acts a file row there is offered.

Drops "Copy File Paths": a folder already copies its own path like any
other row, and the bulk variant duplicated that without adding anything a
user could not already do file by file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Absorb, discard and uncommit keyed off a single change, which a directory
row has none of, so they quietly did nothing there. They now take what the
row stands for — its own file, or every file below a folder — the same
subject its menu is addressed to.

Reveal was gated on file rows outright; a folder has a path in the worktree
like anything else. Open in editor was limited to uncommitted files, though
the menu has always offered it wherever a row is listed; now the chord
agrees with the menu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The label carries the whole path, which a folded chain of segments and a
deep indent both need, and the checkbox teaches its chord — both as a file
row has always done.

Mid-scroll the checkbox drops both, as a file row's does: no tooltip and
no handler, so the row teaches no chord it cannot yet act on. The label's
tooltip stays — a path is a path whether or not the list has settled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Playwright cannot open a native menu, but it can see the button that opens
one, and the rest of what a directory row answers for — its fold state, and
the files its checkbox stands for — is ordinary DOM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A directory stands for the files below it, so it reads their reviewed
state the way its checkbox reads their checked state: reviewed once every
change below it is, conflicts not counting. The row then recedes and
shows the same tick a reviewed file row shows in place of its change
type.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@PavelLaptev

Copy link
Copy Markdown
Contributor

'Marking a folder (un)reviewed' means 'marking all files under it (un)reviewed', right?

yes, a folder just represents its files. Technically I'm not sure how feasible it is, but I thought it would be consistent and nice for UX. @samhh wonder wdyt

Reviewing was the one thing a row said but could not do: the tick was shown
in both lists, and the only way to set it was the diff header's own button,
one file at a time. Both row menus now offer it, and a directory offers it
for every change below it at once — the same subtree its checkbox and its
other acts stand for.

Reviewing records the version of the diff it saw, and only the patch carries
one, so the act names its changes rather than their addresses and looks their
diffs up before marking: the ones the diff pane loads anyway, usually already
in the cache. Unlike the acts beside it, it does not give way to the checked
set — how far a reader has got through a row is the row's own business, as
marking its conflicts resolved is — and it is the one item branch files get,
since review state is kept for them too.

The diff header leaves it out: it offers reviewing in its own chrome, and its
button also folds the file away, which a menu item would not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cbjeukendrup
cbjeukendrup force-pushed the lite-directory-file-actions branch from 4e7695a to 6e4efa5 Compare September 21, 2026 08:29
@cbjeukendrup

Copy link
Copy Markdown
Contributor Author

How would this be?

Schermopname.2026-09-21.om.10.16.09.mov

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.

2 participants