Skip to content

Commit d57e64e

Browse files
committed
Fix mode switching performance in PromptsView by using local state for instant UI updates
1 parent 7020d1d commit d57e64e

File tree

1 file changed

+38
-28
lines changed

1 file changed

+38
-28
lines changed

webview-ui/src/components/prompts/PromptsView.tsx

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
6060
customModes,
6161
} = useExtensionState()
6262

63+
// Use a local state to track the visually active mode
64+
// This prevents flickering when switching modes rapidly by:
65+
// 1. Updating the UI immediately when a mode is clicked
66+
// 2. Not syncing with the backend mode state (which would cause flickering)
67+
// 3. Still sending the mode change to the backend for persistence
68+
const [visualMode, setVisualMode] = useState(mode)
69+
6370
// Memoize modes to preserve array order
6471
const modes = useMemo(() => getAllModes(customModes), [customModes])
6572

@@ -126,22 +133,25 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
126133
// Handle mode switching with explicit state initialization
127134
const handleModeSwitch = useCallback(
128135
(modeConfig: ModeConfig) => {
129-
if (modeConfig.slug === mode) return // Prevent unnecessary updates
136+
if (modeConfig.slug === visualMode) return // Prevent unnecessary updates
137+
138+
// Immediately update visual state for instant feedback
139+
setVisualMode(modeConfig.slug)
130140

131-
// First switch the mode
141+
// Then send the mode change message to the backend
132142
switchMode(modeConfig.slug)
133143

134144
// Exit tools edit mode when switching modes
135145
setIsToolsEditMode(false)
136146
},
137-
[mode, switchMode, setIsToolsEditMode],
147+
[visualMode, switchMode, setIsToolsEditMode],
138148
)
139149

140150
// Helper function to get current mode's config
141151
const getCurrentMode = useCallback((): ModeConfig | undefined => {
142-
const findMode = (m: ModeConfig): boolean => m.slug === mode
152+
const findMode = (m: ModeConfig): boolean => m.slug === visualMode
143153
return customModes?.find(findMode) || modes.find(findMode)
144-
}, [mode, customModes, modes])
154+
}, [visualMode, customModes, modes])
145155

146156
// Helper function to safely access mode properties
147157
const getModeProperty = <T extends keyof ModeConfig>(
@@ -472,7 +482,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
472482

473483
<div className="flex gap-2 items-center mb-3 flex-wrap py-1">
474484
{modes.map((modeConfig) => {
475-
const isActive = mode === modeConfig.slug
485+
const isActive = visualMode === modeConfig.slug
476486
return (
477487
<button
478488
key={modeConfig.slug}
@@ -493,20 +503,20 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
493503

494504
<div style={{ marginBottom: "20px" }}>
495505
{/* Only show name and delete for custom modes */}
496-
{mode && findModeBySlug(mode, customModes) && (
506+
{visualMode && findModeBySlug(visualMode, customModes) && (
497507
<div className="flex gap-3 mb-4">
498508
<div className="flex-1">
499509
<div className="font-bold mb-1">{t("prompts:createModeDialog.name.label")}</div>
500510
<div className="flex gap-2">
501511
<VSCodeTextField
502-
value={getModeProperty(findModeBySlug(mode, customModes), "name") ?? ""}
512+
value={getModeProperty(findModeBySlug(visualMode, customModes), "name") ?? ""}
503513
onChange={(e: Event | React.FormEvent<HTMLElement>) => {
504514
const target =
505515
(e as CustomEvent)?.detail?.target ||
506516
((e as any).target as HTMLInputElement)
507-
const customMode = findModeBySlug(mode, customModes)
517+
const customMode = findModeBySlug(visualMode, customModes)
508518
if (customMode) {
509-
updateCustomMode(mode, {
519+
updateCustomMode(visualMode, {
510520
...customMode,
511521
name: target.value,
512522
source: customMode.source || "global",
@@ -522,7 +532,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
522532
onClick={() => {
523533
vscode.postMessage({
524534
type: "deleteCustomMode",
525-
slug: mode,
535+
slug: visualMode,
526536
})
527537
}}>
528538
<span className="codicon codicon-trash"></span>
@@ -534,7 +544,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
534544
<div style={{ marginBottom: "16px" }}>
535545
<div className="flex justify-between items-center mb-1">
536546
<div className="font-bold">{t("prompts:roleDefinition.title")}</div>
537-
{!findModeBySlug(mode, customModes) && (
547+
{!findModeBySlug(visualMode, customModes) && (
538548
<Button
539549
variant="ghost"
540550
size="icon"
@@ -555,25 +565,25 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
555565
</div>
556566
<VSCodeTextArea
557567
value={(() => {
558-
const customMode = findModeBySlug(mode, customModes)
559-
const prompt = customModePrompts?.[mode] as PromptComponent
568+
const customMode = findModeBySlug(visualMode, customModes)
569+
const prompt = customModePrompts?.[visualMode] as PromptComponent
560570
return customMode?.roleDefinition ?? prompt?.roleDefinition ?? getRoleDefinition(mode)
561571
})()}
562572
onChange={(e) => {
563573
const value =
564574
(e as CustomEvent)?.detail?.target?.value ||
565575
((e as any).target as HTMLTextAreaElement).value
566-
const customMode = findModeBySlug(mode, customModes)
576+
const customMode = findModeBySlug(visualMode, customModes)
567577
if (customMode) {
568578
// For custom modes, update the JSON file
569-
updateCustomMode(mode, {
579+
updateCustomMode(visualMode, {
570580
...customMode,
571581
roleDefinition: value.trim() || "",
572582
source: customMode.source || "global",
573583
})
574584
} else {
575585
// For built-in modes, update the prompts
576-
updateAgentPrompt(mode, {
586+
updateAgentPrompt(visualMode, {
577587
roleDefinition: value.trim() || undefined,
578588
})
579589
}
@@ -617,7 +627,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
617627
<div className="mb-4">
618628
<div className="flex justify-between items-center mb-1">
619629
<div className="font-bold">{t("prompts:tools.title")}</div>
620-
{findModeBySlug(mode, customModes) && (
630+
{findModeBySlug(visualMode, customModes) && (
621631
<Button
622632
variant="ghost"
623633
size="icon"
@@ -632,16 +642,16 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
632642
</Button>
633643
)}
634644
</div>
635-
{!findModeBySlug(mode, customModes) && (
645+
{!findModeBySlug(visualMode, customModes) && (
636646
<div className="text-sm text-vscode-descriptionForeground mb-2">
637647
{t("prompts:tools.builtInModesText")}
638648
</div>
639649
)}
640-
{isToolsEditMode && findModeBySlug(mode, customModes) ? (
650+
{isToolsEditMode && findModeBySlug(visualMode, customModes) ? (
641651
<div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-2">
642652
{availableGroups.map((group) => {
643653
const currentMode = getCurrentMode()
644-
const isCustomMode = findModeBySlug(mode, customModes)
654+
const isCustomMode = findModeBySlug(visualMode, customModes)
645655
const customMode = isCustomMode
646656
const isGroupEnabled = isCustomMode
647657
? customMode?.groups?.some((g) => getGroupName(g) === group)
@@ -710,7 +720,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
710720
marginBottom: "4px",
711721
}}>
712722
<div style={{ fontWeight: "bold" }}>{t("prompts:customInstructions.title")}</div>
713-
{!findModeBySlug(mode, customModes) && (
723+
{!findModeBySlug(visualMode, customModes) && (
714724
<Button
715725
variant="ghost"
716726
size="icon"
@@ -738,8 +748,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
738748
</div>
739749
<VSCodeTextArea
740750
value={(() => {
741-
const customMode = findModeBySlug(mode, customModes)
742-
const prompt = customModePrompts?.[mode] as PromptComponent
751+
const customMode = findModeBySlug(visualMode, customModes)
752+
const prompt = customModePrompts?.[visualMode] as PromptComponent
743753
return (
744754
customMode?.customInstructions ??
745755
prompt?.customInstructions ??
@@ -750,18 +760,18 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
750760
const value =
751761
(e as CustomEvent)?.detail?.target?.value ||
752762
((e as any).target as HTMLTextAreaElement).value
753-
const customMode = findModeBySlug(mode, customModes)
763+
const customMode = findModeBySlug(visualMode, customModes)
754764
if (customMode) {
755765
// For custom modes, update the JSON file
756-
updateCustomMode(mode, {
766+
updateCustomMode(visualMode, {
757767
...customMode,
758768
customInstructions: value.trim() || undefined,
759769
source: customMode.source || "global",
760770
})
761771
} else {
762772
// For built-in modes, update the prompts
763-
const existingPrompt = customModePrompts?.[mode] as PromptComponent
764-
updateAgentPrompt(mode, {
773+
const existingPrompt = customModePrompts?.[visualMode] as PromptComponent
774+
updateAgentPrompt(visualMode, {
765775
...existingPrompt,
766776
customInstructions: value.trim(),
767777
})

0 commit comments

Comments
 (0)