diff --git a/packages/app/e2e/browser/changes-pane.spec.ts b/packages/app/e2e/browser/changes-pane.spec.ts
index 0da78b92ea..51503d751b 100644
--- a/packages/app/e2e/browser/changes-pane.spec.ts
+++ b/packages/app/e2e/browser/changes-pane.spec.ts
@@ -838,25 +838,36 @@ test("desktop Changes toggles a navigation tree beside the expanded diff documen
await testInfo.attach("changes-toolbar", { path: screenshot, contentType: "image/png" });
});
-test("compact Changes keeps its options control touch-sized", async ({ page }) => {
+test("compact Changes keeps its actions compact and menu-only", async ({ page }) => {
const workspace = await createWorkspaceWithMountedTabDiff();
await useUnwrappedDiffLines(page);
await openWorkspaceChanges(page, workspace);
await page.setViewportSize({ width: 480, height: 900 });
+ const actions = page.getByTestId("changes-actions-menu-trigger").filter({ visible: true });
const options = page.getByRole("button", { name: "Diff options" }).filter({ visible: true });
- const [optionsBox, glyphBox] = await Promise.all([
+ const [actionsBox, optionsBox, glyphBox] = await Promise.all([
+ actions.boundingBox(),
options.boundingBox(),
options.locator("svg").boundingBox(),
]);
- if (!optionsBox || !glyphBox) {
- throw new Error("Compact Changes options geometry could not be measured");
+ if (!actionsBox || !optionsBox || !glyphBox) {
+ throw new Error("Compact Changes toolbar geometry could not be measured");
}
+ expect(actionsBox.width).toBe(48);
+ expect(actionsBox.height).toBe(28);
expect(optionsBox.width).toBe(32);
expect(optionsBox.height).toBe(32);
expect(glyphBox.width).toBe(18);
expect(glyphBox.height).toBe(18);
+ await expect(actions).not.toContainText("Commit");
+ await expect(actions.locator("svg")).toHaveCount(2);
+ await actions.click();
+ await expect(page.getByTestId("changes-primary-cta-menu")).toBeVisible();
+ await expect(page.getByTestId("changes-menu-commit")).toContainText("Commit");
+ await page.keyboard.press("Escape");
+
await options.click();
const wrapLines = page.getByText("Wrap long lines", { exact: true });
await expect(wrapLines).toBeVisible();
diff --git a/packages/app/src/git/actions-split-button.tsx b/packages/app/src/git/actions-split-button.tsx
index 04f806e207..59df725ca7 100644
--- a/packages/app/src/git/actions-split-button.tsx
+++ b/packages/app/src/git/actions-split-button.tsx
@@ -2,7 +2,7 @@ import { LoadingSpinner } from "@/components/ui/loading-spinner";
import { useCallback, useMemo } from "react";
import { View, Text, Pressable, type PressableStateCallbackType } from "react-native";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
-import { ChevronDown, MoreVertical } from "lucide-react-native";
+import { ChevronDown, GitBranch, MoreVertical } from "lucide-react-native";
import { useTranslation } from "react-i18next";
import {
DropdownMenu,
@@ -22,6 +22,7 @@ import { buttonControlHeight } from "@/components/ui/control-geometry";
interface GitActionsSplitButtonProps {
gitActions: GitActions;
hideLabels?: boolean;
+ menuOnly?: boolean;
}
interface GitActionMenuItemProps {
@@ -74,7 +75,11 @@ function GitActionMenuItem({
);
}
-export function GitActionsSplitButton({ gitActions, hideLabels }: GitActionsSplitButtonProps) {
+export function GitActionsSplitButton({
+ gitActions,
+ hideLabels,
+ menuOnly = false,
+}: GitActionsSplitButtonProps) {
const { theme } = useUnistyles();
const { t } = useTranslation();
const runGitAction = useGitActionRunner();
@@ -115,6 +120,62 @@ export function GitActionsSplitButton({ gitActions, hideLabels }: GitActionsSpli
[theme.colors.surface2],
);
+ const menuOnlyTriggerStyle = useCallback(
+ ({ hovered, pressed, open }: { hovered: boolean; pressed: boolean; open: boolean }) => [
+ styles.menuOnlyTrigger,
+ (hovered || pressed || open) &&
+ inlineUnistylesStyle({ backgroundColor: theme.colors.surface2 }),
+ ],
+ [theme.colors.surface2],
+ );
+
+ const menuOnlyActions = useMemo(
+ () => [
+ ...(gitActions.primary ? [gitActions.primary] : []),
+ ...gitActions.secondary,
+ ...gitActions.menu,
+ ],
+ [gitActions.menu, gitActions.primary, gitActions.secondary],
+ );
+
+ if (menuOnly) {
+ if (menuOnlyActions.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+ {menuOnlyActions.map((action, index) => (
+ 0}
+ closeOnSelect={
+ action.status === "idle" &&
+ action.id === "pr" &&
+ action.label === action.pendingLabel &&
+ action.label === action.successLabel
+ }
+ />
+ ))}
+
+
+ );
+ }
+
return (
{gitActions.primary ? (
@@ -224,6 +285,17 @@ const styles = StyleSheet.create((theme) => ({
justifyContent: "center",
position: "relative",
},
+ menuOnlyTrigger: {
+ width: 48,
+ height: buttonControlHeight.xs,
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "center",
+ gap: theme.spacing[1],
+ borderRadius: theme.borderRadius.lg,
+ borderWidth: theme.borderWidth[1],
+ borderColor: theme.colors.borderAccent,
+ },
splitButtonPrimaryDisabled: {
opacity: 0.6,
},
diff --git a/packages/app/src/git/diff-pane.tsx b/packages/app/src/git/diff-pane.tsx
index 0e557516ef..e615d98861 100644
--- a/packages/app/src/git/diff-pane.tsx
+++ b/packages/app/src/git/diff-pane.tsx
@@ -415,7 +415,7 @@ function ChangesToolbar(props: ChangesToolbarProps) {
onToggle={onToggleDesktopTree}
/>
) : null}
- {isMobile ? : null}
+ {isMobile ? : null}