Compare commits

...
Author SHA1 Message Date
Cline Evaluation e268d9f5cd markdown fix 2025-06-24 12:39:56 -06:00
Cline Evaluation 5f267d36c9 markdown fix 2025-06-24 12:31:49 -06:00
Cline Evaluation 066efc45a4 type fix 2025-06-24 11:47:07 -06:00
4 changed files with 76 additions and 30 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fix race condition in model switching
@@ -15,25 +15,36 @@ import { StateServiceClient } from "@/services/grpc-client"
import { PlanActMode, TogglePlanActModeRequest } from "@shared/proto/state"
// Styled component for Act Mode text with more specific styling
const ActModeHighlight: React.FC = () => (
<span
onClick={() => {
StateServiceClient.togglePlanActMode(
TogglePlanActModeRequest.create({
chatSettings: {
mode: PlanActMode.ACT,
},
}),
)
}}
title="Click to toggle to Act Mode"
className="text-[var(--vscode-textLink-foreground)] hover:opacity-90 cursor-pointer inline-flex items-center gap-1">
<div className="p-1 rounded-[12px] bg-[var(--vscode-editor-background)] flex items-center justify-end w-4 border-[1px] border-[var(--vscode-input-border)]">
<div className="rounded-full bg-[var(--vscode-textLink-foreground)] w-2 h-2" />
</div>
Act Mode (A)
</span>
)
const ActModeHighlight: React.FC = () => {
const { chatSettings } = useExtensionState()
return (
<span
onClick={() => {
// Only toggle to Act mode if we're currently in Plan mode
if (chatSettings.mode === "plan") {
StateServiceClient.togglePlanActMode(
TogglePlanActModeRequest.create({
chatSettings: {
mode: PlanActMode.ACT,
preferredLanguage: chatSettings.preferredLanguage,
openAiReasoningEffort: chatSettings.openAIReasoningEffort,
},
}),
)
}
}}
title={chatSettings.mode === "plan" ? "Click to toggle to Act Mode" : "Already in Act Mode"}
className={`text-[var(--vscode-textLink-foreground)] inline-flex items-center gap-1 ${
chatSettings.mode === "plan" ? "hover:opacity-90 cursor-pointer" : "cursor-default opacity-60"
}`}>
<div className="p-1 rounded-[12px] bg-[var(--vscode-editor-background)] flex items-center justify-end w-4 border-[1px] border-[var(--vscode-input-border)]">
<div className="rounded-full bg-[var(--vscode-textLink-foreground)] w-2 h-2" />
</div>
Act Mode (A)
</span>
)
}
interface MarkdownBlockProps {
markdown?: string
@@ -113,19 +113,21 @@ const McpConfigurationView = ({ onDone, initialTab }: McpViewProps) => {
)
}
const StyledTabButton = styled.button<{ isActive: boolean }>`
const StyledTabButton = styled.button<{ isActive: boolean; disabled?: boolean }>`
background: none;
border: none;
border-bottom: 2px solid ${(props) => (props.isActive ? "var(--vscode-foreground)" : "transparent")};
color: ${(props) => (props.isActive ? "var(--vscode-foreground)" : "var(--vscode-descriptionForeground)")};
padding: 8px 16px;
cursor: pointer;
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
font-size: 13px;
margin-bottom: -1px;
font-family: inherit;
opacity: ${(props) => (props.disabled ? 0.6 : 1)};
pointer-events: ${(props) => (props.disabled ? "none" : "auto")};
&:hover {
color: var(--vscode-foreground);
color: ${(props) => (props.disabled ? "var(--vscode-descriptionForeground)" : "var(--vscode-foreground)")};
}
`
@@ -133,12 +135,16 @@ export const TabButton = ({
children,
isActive,
onClick,
disabled,
style,
}: {
children: React.ReactNode
isActive: boolean
onClick: () => void
disabled?: boolean
style?: React.CSSProperties
}) => (
<StyledTabButton isActive={isActive} onClick={onClick}>
<StyledTabButton isActive={isActive} onClick={onClick} disabled={disabled} style={style}>
{children}
</StyledTabButton>
)
@@ -111,6 +111,8 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
const [isUnsavedChangesDialogOpen, setIsUnsavedChangesDialogOpen] = useState(false)
// Store the action to perform after confirmation
const pendingAction = useRef<() => void>()
// Track if we're currently switching modes
const [isSwitchingMode, setIsSwitchingMode] = useState(false)
const {
apiConfiguration,
version,
@@ -418,14 +420,19 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
}
const handlePlanActModeChange = async (tab: "plan" | "act") => {
if (tab === chatSettings.mode) {
// Prevent switching if already in that mode or if currently switching
if (tab === chatSettings.mode || isSwitchingMode) {
return
}
// Update settings first to ensure any changes to the current tab are saved
await handleSubmit(true)
// Set switching state to prevent concurrent operations
setIsSwitchingMode(true)
try {
// Update settings first to ensure any changes to the current tab are saved
await handleSubmit(true)
// Then perform the mode switch
await StateServiceClient.togglePlanActMode(
TogglePlanActModeRequest.create({
chatSettings: {
@@ -437,6 +444,9 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
)
} catch (error) {
console.error("Failed to toggle Plan/Act mode:", error)
} finally {
// Always re-enable mode switching, even on error
setIsSwitchingMode(false)
}
}
@@ -584,13 +594,27 @@ const SettingsView = ({ onDone, targetSection }: SettingsViewProps) => {
<div className="flex gap-[1px] mb-[10px] -mt-2 border-0 border-b border-solid border-[var(--vscode-panel-border)]">
<TabButton
isActive={chatSettings.mode === "plan"}
onClick={() => handlePlanActModeChange("plan")}>
Plan Mode
onClick={() => handlePlanActModeChange("plan")}
disabled={isSwitchingMode}
style={{
opacity: isSwitchingMode ? 0.6 : 1,
cursor: isSwitchingMode ? "not-allowed" : "pointer",
}}>
{isSwitchingMode && chatSettings.mode === "act"
? "Switching..."
: "Plan Mode"}
</TabButton>
<TabButton
isActive={chatSettings.mode === "act"}
onClick={() => handlePlanActModeChange("act")}>
Act Mode
onClick={() => handlePlanActModeChange("act")}
disabled={isSwitchingMode}
style={{
opacity: isSwitchingMode ? 0.6 : 1,
cursor: isSwitchingMode ? "not-allowed" : "pointer",
}}>
{isSwitchingMode && chatSettings.mode === "plan"
? "Switching..."
: "Act Mode"}
</TabButton>
</div>