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:
@@ -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,63 +2140,69 @@ export function LaneChangesPanel({
|
|||||||
<Sparkles className="h-3 w-3" aria-hidden />
|
<Sparkles className="h-3 w-3" aria-hidden />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<label
|
|
||||||
title={t("changes.amendHint")}
|
|
||||||
className="flex shrink-0 cursor-pointer items-center gap-1 text-[11px] text-fg-secondary"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
data-testid="lane-changes-amend"
|
|
||||||
checked={amend}
|
|
||||||
onChange={(e) => setAmend(e.target.checked)}
|
|
||||||
/>
|
|
||||||
{t("changes.amend")}
|
|
||||||
</label>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={busy || suggesting || !(message.trim() || amend)}
|
|
||||||
onClick={() => void commitStaged()}
|
|
||||||
className="shrink-0 flex items-center gap-1 rounded border border-border-light px-1.5 py-0.5 text-[11px] text-fg-secondary hover:bg-surface-2 hover:text-fg-primary disabled:cursor-not-allowed disabled:opacity-40"
|
|
||||||
>
|
|
||||||
{amend
|
|
||||||
? t("changes.amend")
|
|
||||||
: t("changes.commitStaged", { count: stagedCount })}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{toStage.length > 0 && (
|
<div className="flex flex-wrap items-center gap-1.5">
|
||||||
<button
|
{stagedCount > 0 && (
|
||||||
type="button"
|
<>
|
||||||
disabled={busy}
|
<label
|
||||||
onClick={() =>
|
title={t("changes.amendHint")}
|
||||||
void act(
|
className="flex shrink-0 cursor-pointer items-center gap-1 text-[11px] text-fg-secondary"
|
||||||
"stage",
|
>
|
||||||
toStage.map((f) => f.path)
|
<input
|
||||||
)
|
type="checkbox"
|
||||||
}
|
data-testid="lane-changes-amend"
|
||||||
className="shrink-0 flex items-center gap-1 rounded border border-border-light px-1.5 py-0.5 text-[11px] text-fg-secondary hover:bg-surface-2 hover:text-fg-primary disabled:cursor-not-allowed disabled:opacity-40"
|
checked={amend}
|
||||||
>
|
onChange={(e) => setAmend(e.target.checked)}
|
||||||
<Plus className="h-3 w-3" />
|
/>
|
||||||
{t("changes.stageAll", { count: toStage.length })}
|
{t("changes.amend")}
|
||||||
</button>
|
</label>
|
||||||
)}
|
<button
|
||||||
{stagedCount > 0 && (
|
type="button"
|
||||||
<button
|
disabled={busy || suggesting || !(message.trim() || amend)}
|
||||||
type="button"
|
onClick={() => void commitStaged()}
|
||||||
data-testid="lane-changes-unstage-all"
|
className="shrink-0 flex items-center gap-1 rounded border border-border-light px-1.5 py-0.5 text-[11px] text-fg-secondary hover:bg-surface-2 hover:text-fg-primary disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
disabled={busy}
|
>
|
||||||
onClick={() =>
|
{amend
|
||||||
void act(
|
? t("changes.amend")
|
||||||
"unstage",
|
: t("changes.commitStaged", { count: stagedCount })}
|
||||||
files.filter((f) => f.staged).map((f) => f.path)
|
</button>
|
||||||
)
|
</>
|
||||||
}
|
)}
|
||||||
className="shrink-0 flex items-center gap-1 rounded border border-border-light px-1.5 py-0.5 text-[11px] text-fg-secondary hover:bg-surface-2 hover:text-fg-primary disabled:cursor-not-allowed disabled:opacity-40"
|
{toStage.length > 0 && (
|
||||||
>
|
<button
|
||||||
<Minus className="h-3 w-3" />
|
type="button"
|
||||||
{t("changes.unstageAll", { count: stagedCount })}
|
disabled={busy}
|
||||||
</button>
|
onClick={() =>
|
||||||
)}
|
void act(
|
||||||
|
"stage",
|
||||||
|
toStage.map((f) => f.path)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="shrink-0 flex items-center gap-1 rounded border border-border-light px-1.5 py-0.5 text-[11px] text-fg-secondary hover:bg-surface-2 hover:text-fg-primary disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
|
>
|
||||||
|
<Plus className="h-3 w-3" />
|
||||||
|
{t("changes.stageAll", { count: toStage.length })}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{stagedCount > 0 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-testid="lane-changes-unstage-all"
|
||||||
|
disabled={busy}
|
||||||
|
onClick={() =>
|
||||||
|
void act(
|
||||||
|
"unstage",
|
||||||
|
files.filter((f) => f.staged).map((f) => f.path)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="shrink-0 flex items-center gap-1 rounded border border-border-light px-1.5 py-0.5 text-[11px] text-fg-secondary hover:bg-surface-2 hover:text-fg-primary disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
|
>
|
||||||
|
<Minus className="h-3 w-3" />
|
||||||
|
{t("changes.unstageAll", { count: stagedCount })}
|
||||||
|
</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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user