diff --git a/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialog.test.ts b/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialog.test.ts index a3a38541152..eacdd3e5994 100644 --- a/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialog.test.ts +++ b/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialog.test.ts @@ -1,11 +1,29 @@ import { describe, expect, it } from 'vitest' import { buildCommitMessageGenerationParams } from './SourceControlTextGenerationDialog' +import { getDefaultSourceControlTextGenerationSaveTargetKey } from './SourceControlTextGenerationDialogForm' import { applyCommitMessageGenerationDefaults, applySourceControlTextGenerationDefaults } from './SourceControlTextGenerationDefaults' describe('buildCommitMessageGenerationParams', () => { + it('defaults saved text-generation recipes to the global target when repo and global are available', () => { + expect( + getDefaultSourceControlTextGenerationSaveTargetKey([ + { + target: { type: 'repo', repoId: 'repo-1' }, + label: 'Save for this repository only', + successMessage: '' + }, + { + target: { type: 'global' }, + label: 'Save as default for all repositories', + successMessage: '' + } + ]) + ).toBe('global') + }) + it('preserves the resolved model and thinking level for the selected agent', () => { expect( buildCommitMessageGenerationParams({ diff --git a/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialogForm.tsx b/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialogForm.tsx index a0f405a14f1..d003bc79e3a 100644 --- a/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialogForm.tsx +++ b/src/renderer/src/components/right-sidebar/SourceControlTextGenerationDialogForm.tsx @@ -54,10 +54,20 @@ type SourceControlTextGenerationDialogFormProps = { ) => Promise | void } -function sourceControlTextGenerationSaveTargetKey(target: SourceControlAiWriteTarget): string { +export function sourceControlTextGenerationSaveTargetKey( + target: SourceControlAiWriteTarget +): string { return target.type === 'repo' ? `repo:${target.repoId}` : 'global' } +export function getDefaultSourceControlTextGenerationSaveTargetKey( + saveTargets: SourceControlTextGenerationSaveTarget[] +): string { + const defaultTarget = + saveTargets.find((saveTarget) => saveTarget.target.type === 'global') ?? saveTargets[0] + return defaultTarget ? sourceControlTextGenerationSaveTargetKey(defaultTarget.target) : 'global' +} + function agentLabel(agentId: TuiAgent): string { return getAgentCatalog().find((agent) => agent.id === agentId)?.label ?? agentId } @@ -86,9 +96,7 @@ export function SourceControlTextGenerationDialogForm({ const [agentArgs, setAgentArgs] = useState(baseParams?.agentArgs ?? '') const [generationError, setGenerationError] = useState(null) const [savingTargetKey, setSavingTargetKey] = useState(null) - const defaultSaveTargetKey = saveTargets[0] - ? sourceControlTextGenerationSaveTargetKey(saveTargets[0].target) - : 'global' + const defaultSaveTargetKey = getDefaultSourceControlTextGenerationSaveTargetKey(saveTargets) const [saveTargetKey, setSaveTargetKey] = useState(defaultSaveTargetKey) const commandTemplateId = `source-control-${actionId}-command-template` const selectedSaveTarget = diff --git a/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-result.ts b/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-result.ts new file mode 100644 index 00000000000..117ad745044 --- /dev/null +++ b/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-result.ts @@ -0,0 +1,28 @@ +import type { getAgentCatalog } from '@/lib/agent-catalog' +import type { useAppStore } from '@/store' +import type { useRepoById } from '@/store/selectors' +import type { TuiAgent } from '../../../../shared/types' +import type { SourceControlAgentActionDeliveryPlanState } from './SourceControlAgentActionDialogForm' + +export type UseSourceControlAgentActionDialogResult = { + handleOpenChange: (nextOpen: boolean) => void + agentOptions: ReturnType + selectedAgent: TuiAgent | null + hasEnabledAgents: boolean + detecting: boolean + statusCopy: string | null + agentArgs: string + commandTemplate: string + saveTargetValue: string + saveTargets: { value: string; label: string }[] + settings: ReturnType['settings'] + repo: ReturnType + deliveryPlan: SourceControlAgentActionDeliveryPlanState + canStart: boolean + isStarting: boolean + onSelectedAgentChange: (agent: TuiAgent | null) => void + onAgentArgsChange: (value: string) => void + onCommandTemplateChange: (value: string) => void + onSaveAgentDefaultChange: (value: string) => void + handleStart: () => Promise +} diff --git a/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.test.ts b/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.test.ts new file mode 100644 index 00000000000..9e86f3dcdb2 --- /dev/null +++ b/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest' +import { + buildSourceControlAgentSaveTargets, + getDefaultSourceControlAgentSaveTargetValue +} from './source-control-agent-action-dialog-support' + +describe('source control agent action dialog save targets', () => { + it('defaults saved launch recipes to the global target even when a repo target exists', () => { + expect(buildSourceControlAgentSaveTargets('repo-1').map((target) => target.value)).toEqual([ + 'none', + 'repo', + 'global' + ]) + expect(getDefaultSourceControlAgentSaveTargetValue()).toBe('global') + }) +}) diff --git a/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.ts b/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.ts index b7d9f8d336e..dc8aaaf6c41 100644 --- a/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.ts +++ b/src/renderer/src/components/right-sidebar/source-control-agent-action-dialog-support.ts @@ -44,6 +44,10 @@ export function buildSourceControlAgentSaveTargets(repoId?: string | null): { return targets } +export function getDefaultSourceControlAgentSaveTargetValue(): string { + return 'global' +} + export function buildSourceControlAgentConnectionErrorPlan(): SourceControlAgentActionDeliveryPlanState { return { status: 'error', diff --git a/src/renderer/src/components/right-sidebar/useSourceControlAgentActionDialog.ts b/src/renderer/src/components/right-sidebar/useSourceControlAgentActionDialog.ts index 3cf3b7deeba..8f388a83027 100644 --- a/src/renderer/src/components/right-sidebar/useSourceControlAgentActionDialog.ts +++ b/src/renderer/src/components/right-sidebar/useSourceControlAgentActionDialog.ts @@ -9,6 +9,7 @@ import { isTuiAgentEnabled } from '../../../../shared/tui-agent-selection' import type { TuiAgent } from '../../../../shared/types' import { type SourceControlAgentActionDeliveryPlanState } from './SourceControlAgentActionDialogForm' import type { SourceControlAgentActionDialogProps } from './SourceControlAgentActionDialog' +import type { UseSourceControlAgentActionDialogResult } from './source-control-agent-action-dialog-result' import { buildSourceControlAgentConnectionErrorPlan, buildSourceControlAgentSaveTargets, @@ -17,28 +18,7 @@ import { } from './source-control-agent-action-dialog-support' import { runSourceControlAgentActionStart } from './runSourceControlAgentActionStart' -export type UseSourceControlAgentActionDialogResult = { - handleOpenChange: (nextOpen: boolean) => void - agentOptions: ReturnType - selectedAgent: TuiAgent | null - hasEnabledAgents: boolean - detecting: boolean - statusCopy: string | null - agentArgs: string - commandTemplate: string - saveTargetValue: string - saveTargets: { value: string; label: string }[] - settings: ReturnType['settings'] - repo: ReturnType - deliveryPlan: SourceControlAgentActionDeliveryPlanState - canStart: boolean - isStarting: boolean - onSelectedAgentChange: (agent: TuiAgent | null) => void - onAgentArgsChange: (value: string) => void - onCommandTemplateChange: (value: string) => void - onSaveAgentDefaultChange: (value: string) => void - handleStart: () => Promise -} +const DEFAULT_SAVE_TARGET_VALUE = 'global' export function useSourceControlAgentActionDialog({ open, @@ -75,7 +55,7 @@ export function useSourceControlAgentActionDialog({ }) const [isStarting, setIsStarting] = useState(false) const saveTargets = useMemo(() => buildSourceControlAgentSaveTargets(repoId), [repoId]) - const [saveTargetValue, setSaveTargetValue] = useState(repoId ? 'repo' : 'global') + const [saveTargetValue, setSaveTargetValue] = useState(DEFAULT_SAVE_TARGET_VALUE) const disabledAgents = settings?.disabledTuiAgents const connectionUnavailable = Boolean(worktreeId && connectionId === undefined) @@ -106,7 +86,7 @@ export function useSourceControlAgentActionDialog({ setCommandTemplate(savedCommandInputTemplate ?? '{basePrompt}') setAgentArgs(savedAgentArgs ?? '') setSelectedAgent(savedAgentId ?? null) - setSaveTargetValue(repoId ? 'repo' : 'global') + setSaveTargetValue(DEFAULT_SAVE_TARGET_VALUE) let stale = false void refreshDetectedAgents().then((nextAgents) => { if (stale) { @@ -141,11 +121,11 @@ export function useSourceControlAgentActionDialog({ (nextOpen: boolean) => { if (!nextOpen) { setDeliveryPlan({ status: 'idle' }) - setSaveTargetValue(repoId ? 'repo' : 'global') + setSaveTargetValue(DEFAULT_SAVE_TARGET_VALUE) } onOpenChange(nextOpen) }, - [onOpenChange, repoId] + [onOpenChange] ) const enabledDetectedAgents = useMemo(