From 8a2887bf9617c62a9e8eea406e25df7f3e5825cc Mon Sep 17 00:00:00 2001 From: Marve10s Date: Sun, 22 Mar 2026 18:47:30 +0300 Subject: [PATCH] fix(web): allow switching away from Ultrathink effort without manual prompt editing When Ultrathink was active, the effort dropdown was fully disabled, requiring the user to manually remove the "Ultrathink:" prefix from the prompt. Now selecting a different effort level automatically strips the prefix and applies the new effort. --- .../chat/ClaudeTraitsPicker.browser.tsx | 5 ++--- .../src/components/chat/ClaudeTraitsPicker.tsx | 17 +++++++++-------- .../CompactComposerControlsMenu.browser.tsx | 5 ++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/web/src/components/chat/ClaudeTraitsPicker.browser.tsx b/apps/web/src/components/chat/ClaudeTraitsPicker.browser.tsx index a675a82d89..42ab15db29 100644 --- a/apps/web/src/components/chat/ClaudeTraitsPicker.browser.tsx +++ b/apps/web/src/components/chat/ClaudeTraitsPicker.browser.tsx @@ -148,7 +148,7 @@ describe("ClaudeTraitsPicker", () => { } }); - it("shows prompt-controlled Ultrathink state with disabled effort controls", async () => { + it("shows prompt-controlled Ultrathink state with selectable effort controls", async () => { const mounted = await mountPicker({ effort: "high", model: "claude-opus-4-6", @@ -166,8 +166,7 @@ describe("ClaudeTraitsPicker", () => { await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).toContain("Effort"); - expect(text).toContain("Remove Ultrathink from the prompt to change effort."); - expect(text).not.toContain("Fallback Effort"); + expect(text).not.toContain("Remove Ultrathink from the prompt to change effort."); }); } finally { await mounted.cleanup(); diff --git a/apps/web/src/components/chat/ClaudeTraitsPicker.tsx b/apps/web/src/components/chat/ClaudeTraitsPicker.tsx index d6585d43d8..a92d22e44f 100644 --- a/apps/web/src/components/chat/ClaudeTraitsPicker.tsx +++ b/apps/web/src/components/chat/ClaudeTraitsPicker.tsx @@ -107,7 +107,6 @@ export const ClaudeTraitsMenuContent = memo(function ClaudeTraitsMenuContentImpl const handleEffortChange = useCallback( (value: ClaudeCodeEffort) => { - if (ultrathinkPromptControlled) return; if (!value) return; const nextEffort = options.find((option) => option === value); if (!nextEffort) return; @@ -119,6 +118,10 @@ export const ClaudeTraitsMenuContent = memo(function ClaudeTraitsMenuContentImpl onPromptChange(nextPrompt); return; } + if (ultrathinkPromptControlled) { + const stripped = prompt.replace(/\bultrathink\b:?\s*/i, ""); + onPromptChange(stripped); + } setProviderModelOptions( threadId, PROVIDER, @@ -151,14 +154,12 @@ export const ClaudeTraitsMenuContent = memo(function ClaudeTraitsMenuContentImpl <>
Effort
- {ultrathinkPromptControlled ? ( -
- Remove Ultrathink from the prompt to change effort. -
- ) : null} - + {options.map((option) => ( - + {CLAUDE_EFFORT_LABELS[option]} {option === defaultReasoningEffort ? " (default)" : ""} diff --git a/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx b/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx index 83716d619a..f83937896b 100644 --- a/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx +++ b/apps/web/src/components/chat/CompactComposerControlsMenu.browser.tsx @@ -159,7 +159,7 @@ describe("CompactComposerControlsMenu", () => { } }); - it("shows prompt-controlled Ultrathink messaging with disabled effort controls", async () => { + it("shows prompt-controlled Ultrathink state with selectable effort controls", async () => { const mounted = await mountMenu({ model: "claude-opus-4-6", prompt: "Ultrathink:\nInvestigate this", @@ -176,8 +176,7 @@ describe("CompactComposerControlsMenu", () => { await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).toContain("Effort"); - expect(text).toContain("Remove Ultrathink from the prompt to change effort."); - expect(text).not.toContain("Fallback Effort"); + expect(text).not.toContain("Remove Ultrathink from the prompt to change effort."); }); } finally { await mounted.cleanup();