fix: v2.5.11 — the commit box wraps only when the panel is narrow

The message keeps the row with the commit, Stage all and Unstage all
buttons while there is room (a 50/50 split, a full-width panel); in a
narrow panel the buttons wrap under it instead of squeezing the message
to a few characters. Client 1095/1095 on the committed tree.
This commit is contained in:
2026-10-07 15:40:43 +07:00
parent f0656603c3
commit 1619954fad
3 changed files with 71 additions and 62 deletions
@@ -2099,13 +2099,15 @@ export function LaneChangesPanel({
{(stagedCount > 0 || toStage.length > 0) && ( {(stagedCount > 0 || toStage.length > 0) && (
<div <div
data-testid="lane-changes-actions" data-testid="lane-changes-actions"
className="sticky top-0 z-10 mb-1.5 flex items-center gap-1.5 border-b border-border-light bg-surface-1 pb-2" className="sticky top-0 z-10 mb-1.5 flex flex-wrap items-center gap-1.5 border-b border-border-light bg-surface-1 pb-2"
> >
{/* ONE row: message, commit, then the two bulk index moves. */} {/* One row when there is room; in a narrow panel the buttons
wrap below rather than squeeze the message to a few
characters (its min width is what triggers the wrap). */}
{stagedCount > 0 && ( {stagedCount > 0 && (
<div <div
data-testid="lane-changes-commit" data-testid="lane-changes-commit"
className="flex min-w-0 flex-1 items-center gap-1.5" className="flex min-w-[14rem] flex-1 items-center gap-1.5"
> >
<input <input
value={message} value={message}
@@ -2138,6 +2140,11 @@ export function LaneChangesPanel({
<Sparkles className="h-3 w-3" aria-hidden /> <Sparkles className="h-3 w-3" aria-hidden />
)} )}
</button> </button>
</div>
)}
<div className="flex flex-wrap items-center gap-1.5">
{stagedCount > 0 && (
<>
<label <label
title={t("changes.amendHint")} title={t("changes.amendHint")}
className="flex shrink-0 cursor-pointer items-center gap-1 text-[11px] text-fg-secondary" className="flex shrink-0 cursor-pointer items-center gap-1 text-[11px] text-fg-secondary"
@@ -2160,7 +2167,7 @@ export function LaneChangesPanel({
? t("changes.amend") ? t("changes.amend")
: t("changes.commitStaged", { count: stagedCount })} : t("changes.commitStaged", { count: stagedCount })}
</button> </button>
</div> </>
)} )}
{toStage.length > 0 && ( {toStage.length > 0 && (
<button <button
@@ -2196,6 +2203,7 @@ export function LaneChangesPanel({
</button> </button>
)} )}
</div> </div>
</div>
)} )}
{/* The work range splits like VS Code's Source Control: what the {/* The work range splits like VS Code's Source Control: what the
next commit takes, then what it leaves behind. A partly staged next commit takes, then what it leaves behind. A partly staged
@@ -1069,7 +1069,7 @@ describe("LaneChangesPanel", () => {
expect(screen.getByTestId("lane-file-editor")).toHaveValue("xy"); expect(screen.getByTestId("lane-file-editor")).toHaveValue("xy");
}); });
it("unstages every staged file in one call, on the same row as Stage all and commit", async () => { it("unstages every staged file in one call, in the same pinned bar as Stage all and commit", async () => {
const user = userEvent.setup(); const user = userEvent.setup();
render(<LaneChangesPanel laneId={7} />); render(<LaneChangesPanel laneId={7} />);
await screen.findByTestId("lane-changes-overview"); await screen.findByTestId("lane-changes-overview");
+4 -3
View File
@@ -733,9 +733,10 @@ in every panel narrow enough to sit beside a console.
**Per-file writes are the panel's, per-repo writes are the git bar's.** Staging **Per-file writes are the panel's, per-repo writes are the git bar's.** Staging
one file means nothing unless a commit can take the index as it stands, so the one file means nothing unless a commit can take the index as it stands, so the
overview grows a commit box the moment anything is staged — above the list and overview grows a commit box the moment anything is staged — above the list and
pinned while it scrolls, on ONE row with a **Stage all** that stages every listed file pinned while it scrolls: the message (and its suggest button), the commit button, a **Stage all** that stages every listed file
the index lacks in one call and an **Unstage all** that unstages every staged one — it sends the index lacks in one call and an **Unstage all** that unstages every staged one — one row when the
`only: "staged"`, never `add -A`, and it is absent with a clean index because panel has room, the buttons wrapping under the message when it is narrow. The commit sends
`only: "staged"`, never `add -A`, and the box is absent with a clean index because
there is nothing it could mean. A file you never want committed (a local note, a scratch folder) can be marked **Never stage** (its row's eye-off button, or right-click): **Stage all** and a folder's stage button then skip it, the row is struck through, and the file is drawn orange in the Files tree (`--kept`, apart from the amber of a modified file). The list is kept in this browser per workspace (`agenthub.changes.neverStage.<lane id>`) and git is never told — nothing is written to the repo, and the file's own Stage still works. It is the dashboard's ONLY commit: the git bar there is nothing it could mean. A file you never want committed (a local note, a scratch folder) can be marked **Never stage** (its row's eye-off button, or right-click): **Stage all** and a folder's stage button then skip it, the row is struck through, and the file is drawn orange in the Files tree (`--kept`, apart from the amber of a modified file). The list is kept in this browser per workspace (`agenthub.changes.neverStage.<lane id>`) and git is never told — nothing is written to the repo, and the file's own Stage still works. It is the dashboard's ONLY commit: the git bar
above it has none, so no click can sweep the rest of the tree in with `add -A`. **Discard** is the one above it has none, so no click can sweep the rest of the tree in with `add -A`. **Discard** is the one
irreversible action in the panel: it asks twice in the UI, and the server irreversible action in the panel: it asks twice in the UI, and the server