Skip to content

feat(ui): an empty state offers the thing it is describing - #86

Open
youhide wants to merge 1 commit into
mainfrom
c3b-empty-states-act
Open

feat(ui): an empty state offers the thing it is describing#86
youhide wants to merge 1 commit into
mainfrom
c3b-empty-states-act

Conversation

@youhide

@youhide youhide commented Aug 21, 2026

Copy link
Copy Markdown
Owner

C3b, the half of C3 that changes behaviour. #85 made there be one place to put an action; this puts one there.

Two states that could carry an action, and now do

A clean working directory said "the working directory matches the last commit" and left you to go find that commit. Its own doc comment claimed it said "what to do next" — it did not. It offers the commit now.

A file filter matching nothing said so and stopped. It offers to clear itself.

Both dispatch messages that already existed, so nothing new is plumbed through the update loop. This is a button where there was a sentence, not a feature.

An action is offered only when it exists

An unborn branch has no last commit, so that state keeps the sentence and no button. One that selected a commit which is not there would be worse than none, and a test holds that open.

What is deliberately left alone

The panes that say "Select a file to see its changes". Their next action is clicking a row in the list beside them, and a button repeating that is chrome, not help.

The strings, and what they cost

They go through the catalogue rather than inline, which is what ADR-0008 asks of new user-facing text, and both locales get them.

That needed the catalogue to reach two widgets it did not: detail::view and staging::view each take it now, threaded from the screen that already had it.

Worth flagging rather than hiding: staging::view was already eleven arguments behind an #[allow(clippy::too_many_arguments)], and this makes it thirteen. Bundling them into a struct is the right answer and is not this change.

Sabotage — four, four distinct failures

Sabotage What broke
the clean state goes back to describing only a_clean_working_directory_offers_the_commit_it_matches and offering_the_last_commit_actually_selects_it
the button selects the working directory instead offering_the_last_commit_actually_selects_it
an unborn branch gets a button anyway an_unborn_branch_has_no_commit_to_offer
the filter's action label is emptied a_filter_that_matches_nothing_offers_to_clear_itself

The second one matters most: without it, a button that renders but goes nowhere would pass.

A coverage gap these tests closed

Nothing had ever asserted the clean-working-directory screen. Rewriting its wording broke no test — the whole suite stayed green through a copy change to a state every user with a committed repository sees. That is why two of the four are about it.

Screenshots

staging.png retaken, showing the new empty state on a fresh clone. The other three are unchanged.

Gate

cargo fmt --all -- --check, cargo clippy --workspace --all-targets --all-features --locked -- -D warnings, cargo test --workspace --all-features --locked — 904 tests, all green.

`UI_SPEC.md` asks empty states to carry the next action rather than only naming
an absence. Two of them can, and now do.

A clean working directory said "the working directory matches the last commit"
and left you to go and find that commit. Its own doc comment claimed it said
"what to do next", which it did not. It offers the commit instead — one button,
dispatching the selection that was already reachable everywhere else.

A file filter matching nothing said so and stopped. It offers to clear itself.

Both dispatch messages that already existed, so nothing new is plumbed through
the update loop: this is a button where there was a sentence, not a feature.

An action is offered only when it exists. An unborn branch has no last commit,
so that state keeps the sentence and no button — one that selected a commit
which is not there would be worse than none, and a test holds that open.

The strings go through the catalogue rather than inline, which is what ADR-0008
asks of new user-facing text, and both locales get them. That needed the
catalogue to reach two widgets it did not: `detail::view` and `staging::view`
each take it now, threaded from the screen that already had it.

Worth flagging rather than hiding: `staging::view` was already eleven arguments
behind an `#[allow(clippy::too_many_arguments)]`, and this makes it thirteen.
Bundling them into a struct is the right answer and is not this change.

The panes that say "Select a file to see its changes" are deliberately left
alone. Their next action is clicking a row in the list beside them, and a button
repeating that is chrome.

Four tests, one per behaviour, and the clean-state pair caught that nothing had
ever asserted that screen at all — changing its wording broke no test before
these existed.
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