Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/desktop/src/clientPersistence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function makeSecretStorage(available: boolean): DesktopSecretStorage {
}

const clientSettings: ClientSettings = {
autoCreatePrOnPush: true,
autoOpenPlanSidebar: false,
confirmThreadArchive: true,
confirmThreadDelete: false,
Expand Down
70 changes: 70 additions & 0 deletions apps/web/src/components/GitActionsControl.logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1131,3 +1131,73 @@ describe("resolveAutoFeatureBranchName", () => {
assert.equal(ref, "feature/update");
});
});

describe("when: autoCreatePr is disabled", () => {
it("downgrades feature-branch commit+push from PR to plain commit & push", () => {
const quick = resolveQuickAction(
status({ hasWorkingTreeChanges: true }),
false,
false,
true,
false,
);
assert.deepInclude(quick, {
kind: "run_action",
action: "commit_push",
label: "Commit & push",
disabled: false,
});
});

it("downgrades feature-branch ahead-with-upstream from create PR to plain push", () => {
const quick = resolveQuickAction(status({ aheadCount: 2 }), false, false, true, false);
assert.deepInclude(quick, {
kind: "run_action",
action: "push",
label: "Push",
disabled: false,
});
});

it("downgrades feature-branch ahead-without-upstream from create PR to plain push", () => {
const quick = resolveQuickAction(
status({ aheadCount: 1, hasUpstream: false }),
false,
false,
true,
false,
);
assert.deepInclude(quick, {
kind: "run_action",
action: "push",
label: "Push",
disabled: false,
});
});

it("preserves Commit, push & PR when autoCreatePr is true (default)", () => {
const quick = resolveQuickAction(
status({ hasWorkingTreeChanges: true }),
false,
false,
true,
true,
);
assert.deepInclude(quick, {
kind: "run_action",
action: "commit_push_pr",
label: "Commit, push & PR",
disabled: false,
});
});

it("preserves Push & create PR when autoCreatePr is true (default)", () => {
const quick = resolveQuickAction(status({ aheadCount: 2 }), false, false, true, true);
assert.deepInclude(quick, {
kind: "run_action",
action: "create_pr",
label: "Push & create PR",
disabled: false,
});
});
});
7 changes: 4 additions & 3 deletions apps/web/src/components/GitActionsControl.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export function resolveQuickAction(
isBusy: boolean,
isDefaultRef = false,
hasPrimaryRemote = true,
autoCreatePr = true,
): GitQuickAction {
if (isBusy) {
return { label: "Commit", disabled: true, kind: "show_hint", hint: "Git action in progress." };
Expand Down Expand Up @@ -205,7 +206,7 @@ export function resolveQuickAction(
if (!gitStatus.hasUpstream && !hasPrimaryRemote) {
return { label: "Commit", disabled: false, kind: "run_action", action: "commit" };
}
if (hasOpenPr || isDefaultRef) {
if (hasOpenPr || isDefaultRef || !autoCreatePr) {
return { label: "Commit & push", disabled: false, kind: "run_action", action: "commit_push" };
}
return {
Expand Down Expand Up @@ -238,7 +239,7 @@ export function resolveQuickAction(
hint: "No local commits to push.",
};
}
if (hasOpenPr || isDefaultRef) {
if (hasOpenPr || isDefaultRef || !autoCreatePr) {
return {
label: "Push",
disabled: false,
Expand Down Expand Up @@ -272,7 +273,7 @@ export function resolveQuickAction(
}

if (isAhead) {
if (hasOpenPr || isDefaultRef) {
if (hasOpenPr || isDefaultRef || !autoCreatePr) {
return {
label: "Push",
disabled: false,
Expand Down
12 changes: 10 additions & 2 deletions apps/web/src/components/GitActionsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
resolveThreadBranchUpdate,
} from "./GitActionsControl.logic";
import { AnimatedHeight } from "./AnimatedHeight";
import { useSettings } from "../hooks/useSettings";
import { Button } from "~/components/ui/button";
import { Checkbox } from "~/components/ui/checkbox";
import {
Expand Down Expand Up @@ -1136,10 +1137,17 @@ export default function GitActionsControl({
() => buildMenuItems(gitStatusForActions, isGitActionRunning, hasPrimaryRemote),
[gitStatusForActions, hasPrimaryRemote, isGitActionRunning],
);
const autoCreatePrOnPush = useSettings((s) => s.autoCreatePrOnPush);
const quickAction = useMemo(
() =>
resolveQuickAction(gitStatusForActions, isGitActionRunning, isDefaultRef, hasPrimaryRemote),
[gitStatusForActions, hasPrimaryRemote, isDefaultRef, isGitActionRunning],
resolveQuickAction(
gitStatusForActions,
isGitActionRunning,
isDefaultRef,
hasPrimaryRemote,
autoCreatePrOnPush,
),
[autoCreatePrOnPush, gitStatusForActions, hasPrimaryRemote, isDefaultRef, isGitActionRunning],
);
const quickActionDisabledReason = quickAction.disabled
? (quickAction.hint ?? "This action is currently unavailable.")
Expand Down
26 changes: 26 additions & 0 deletions apps/web/src/components/settings/SettingsPanels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,32 @@ export function GeneralSettingsPanel() {
}
/>

<SettingsRow
title="Auto-create PR on push"
description="When pushing a feature branch with no open PR, also create a pull request automatically."
resetAction={
settings.autoCreatePrOnPush !== DEFAULT_UNIFIED_SETTINGS.autoCreatePrOnPush ? (
<SettingResetButton
label="auto-create PR on push"
onClick={() =>
updateSettings({
autoCreatePrOnPush: DEFAULT_UNIFIED_SETTINGS.autoCreatePrOnPush,
})
}
/>
) : null
}
control={
<Switch
checked={settings.autoCreatePrOnPush}
onCheckedChange={(checked) =>
updateSettings({ autoCreatePrOnPush: Boolean(checked) })
}
aria-label="Auto-create PR on push"
/>
}
/>

<SettingsRow
title="New threads"
description="Pick the default workspace mode for newly created draft threads."
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/localApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ describe("wsApi", () => {

it("reads and writes persistence through the desktop bridge when available", async () => {
const clientSettings = {
autoCreatePrOnPush: true,
autoOpenPlanSidebar: false,
confirmThreadArchive: true,
confirmThreadDelete: false,
Expand Down Expand Up @@ -631,6 +632,7 @@ describe("wsApi", () => {
const { createLocalApi } = await import("./localApi");
const api = createLocalApi(rpcClientMock as never);
const clientSettings = {
autoCreatePrOnPush: true,
autoOpenPlanSidebar: false,
confirmThreadArchive: true,
confirmThreadDelete: false,
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type SidebarProjectGroupingMode = typeof SidebarProjectGroupingMode.Type;
export const DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE: SidebarProjectGroupingMode = "repository";

export const ClientSettingsSchema = Schema.Struct({
autoCreatePrOnPush: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
autoOpenPlanSidebar: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
confirmThreadArchive: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
confirmThreadDelete: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
Expand Down
Loading