Skip to content

Commit 8a88dab

Browse files
committed
fix: apply mode defaults in creation chat input
Change-Id: Ic00fe3e1cd68818771ac324787461a0427fcfb05 Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 3fd215f commit 8a88dab

File tree

1 file changed

+50
-17
lines changed

1 file changed

+50
-17
lines changed

src/browser/components/ChatInput/index.tsx

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import type { Toast } from "../ChatInputToast";
1313
import { ChatInputToast } from "../ChatInputToast";
1414
import { createCommandToast, createErrorToast } from "../ChatInputToasts";
1515
import { parseCommand } from "@/browser/utils/slashCommands/parser";
16-
import { usePersistedState, updatePersistedState } from "@/browser/hooks/usePersistedState";
16+
import {
17+
readPersistedState,
18+
usePersistedState,
19+
updatePersistedState,
20+
} from "@/browser/hooks/usePersistedState";
1721
import { useSettings } from "@/browser/contexts/SettingsContext";
1822
import { useWorkspaceContext } from "@/browser/contexts/WorkspaceContext";
1923
import { useMode } from "@/browser/contexts/ModeContext";
@@ -26,9 +30,11 @@ import { enforceThinkingPolicy } from "@/common/utils/thinking/policy";
2630
import { useSendMessageOptions } from "@/browser/hooks/useSendMessageOptions";
2731
import {
2832
getModelKey,
33+
getThinkingLevelKey,
2934
getWorkspaceAISettingsByModeKey,
3035
getInputKey,
3136
getInputImagesKey,
37+
MODE_AI_DEFAULTS_KEY,
3238
VIM_ENABLED_KEY,
3339
getProjectScopeId,
3440
getPendingScopeId,
@@ -74,7 +80,8 @@ import {
7480
processImageFiles,
7581
} from "@/browser/utils/imageHandling";
7682

77-
import type { ThinkingLevel } from "@/common/types/thinking";
83+
import type { ModeAiDefaults } from "@/common/types/modeAiDefaults";
84+
import { coerceThinkingLevel, type ThinkingLevel } from "@/common/types/thinking";
7885
import type { MuxFrontendMetadata } from "@/common/types/message";
7986
import { prepareUserMessageForSend } from "@/common/types/message";
8087
import { MODEL_ABBREVIATION_EXAMPLES } from "@/common/constants/knownModels";
@@ -277,6 +284,14 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
277284
defaultModel,
278285
setDefaultModel,
279286
} = useModelsFromSettings();
287+
288+
const [modeAiDefaults] = usePersistedState<ModeAiDefaults>(
289+
MODE_AI_DEFAULTS_KEY,
290+
{},
291+
{
292+
listener: true,
293+
}
294+
);
280295
const commandListId = useId();
281296
const telemetry = useTelemetry();
282297
const [vimEnabled, setVimEnabled] = usePersistedState<boolean>(VIM_ENABLED_KEY, false, {
@@ -452,23 +467,41 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
452467
const hasReviews = attachedReviews.length > 0;
453468
const canSend = (hasTypedText || hasImages || hasReviews) && !disabled && !isSendInFlight;
454469

455-
// When entering creation mode, initialize the project-scoped model to the
456-
// default so previous manual picks don't bleed into new creation flows.
457-
// Only runs once per creation session (not when defaultModel changes, which
458-
// would clobber the user's intentional model selection).
459-
const creationModelInitialized = useRef<string | null>(null);
470+
const creationProjectPath = variant === "creation" ? props.projectPath : "";
471+
472+
// Creation variant: keep the project-scoped model/thinking in sync with global per-mode defaults
473+
// so switching Plan/Exec uses the configured defaults (and respects "inherit" semantics).
460474
useEffect(() => {
461-
if (variant === "creation" && defaultModel) {
462-
// Only initialize once per project scope
463-
if (creationModelInitialized.current !== storageKeys.modelKey) {
464-
creationModelInitialized.current = storageKeys.modelKey;
465-
updatePersistedState(storageKeys.modelKey, defaultModel);
466-
}
467-
} else if (variant !== "creation") {
468-
// Reset when leaving creation mode so re-entering triggers initialization
469-
creationModelInitialized.current = null;
475+
if (variant !== "creation") {
476+
return;
477+
}
478+
479+
const scopeId = getProjectScopeId(creationProjectPath);
480+
const modelKey = getModelKey(scopeId);
481+
const thinkingKey = getThinkingLevelKey(scopeId);
482+
483+
const fallbackModel = defaultModel;
484+
485+
const existingModel = readPersistedState<string>(modelKey, fallbackModel);
486+
const candidateModel = modeAiDefaults[mode]?.modelString ?? existingModel;
487+
const resolvedModel =
488+
typeof candidateModel === "string" && candidateModel.trim().length > 0
489+
? candidateModel
490+
: fallbackModel;
491+
492+
const existingThinking = readPersistedState<ThinkingLevel>(thinkingKey, "off");
493+
const candidateThinking = modeAiDefaults[mode]?.thinkingLevel ?? existingThinking ?? "off";
494+
const resolvedThinking = coerceThinkingLevel(candidateThinking) ?? "off";
495+
const effectiveThinking = enforceThinkingPolicy(resolvedModel, resolvedThinking);
496+
497+
if (existingModel !== resolvedModel) {
498+
updatePersistedState(modelKey, resolvedModel);
499+
}
500+
501+
if (existingThinking !== effectiveThinking) {
502+
updatePersistedState(thinkingKey, effectiveThinking);
470503
}
471-
}, [variant, defaultModel, storageKeys.modelKey]);
504+
}, [creationProjectPath, defaultModel, mode, modeAiDefaults, variant]);
472505

473506
// Expose ChatInput auto-focus completion for Storybook/tests.
474507
const chatInputSectionRef = useRef<HTMLDivElement | null>(null);

0 commit comments

Comments
 (0)