Skip to content

Commit e28ef33

Browse files
committed
fix: update ModelsPanel to use refs for state tracking and improve error message handling
1 parent 882257a commit e28ef33

1 file changed

Lines changed: 37 additions & 11 deletions

File tree

source/ui/components/panels/ModelsPanel.tsx

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import React, {useCallback, useEffect, useMemo, useState} from 'react';
1+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
22
import {Box, Text, useInput} from 'ink';
33
import Spinner from 'ink-spinner';
4+
import {Alert} from '@inkjs/ui';
45
import ScrollableSelectInput from '../common/ScrollableSelectInput.js';
56
import {
67
fetchAvailableModels,
@@ -60,6 +61,10 @@ export const ModelsPanel: React.FC<Props> = ({
6061
const [manualInputValue, setManualInputValue] = useState('');
6162
const [hasStartedLoading, setHasStartedLoading] = useState(false);
6263

64+
// 使用 ref 同步追踪选择状态,解决 ESC 键需要按两次的问题
65+
const isSelectingRef = useRef(false);
66+
const manualInputModeRef = useRef(false);
67+
6368
// Thinking settings (aligned with ConfigScreen)
6469
const [requestMethod, setRequestMethod] = useState<RequestMethod>('chat');
6570
const [showThinking, setShowThinking] = useState(true);
@@ -98,8 +103,10 @@ export const ModelsPanel: React.FC<Props> = ({
98103

99104
// Reset transient UI state
100105
setIsSelecting(false);
106+
isSelectingRef.current = false;
101107
setSearchTerm('');
102108
setManualInputMode(false);
109+
manualInputModeRef.current = false;
103110
setManualInputValue('');
104111
setHasStartedLoading(false);
105112
setThinkingFocusIndex(0);
@@ -130,6 +137,17 @@ export const ModelsPanel: React.FC<Props> = ({
130137
);
131138
}, [visible, advancedModel, basicModel]);
132139

140+
// Auto-hide error message after 3 seconds
141+
useEffect(() => {
142+
if (errorMessage) {
143+
const timer = setTimeout(() => {
144+
setErrorMessage('');
145+
}, 3000);
146+
return () => clearTimeout(timer);
147+
}
148+
return undefined;
149+
}, [errorMessage]);
150+
133151
const modelTarget: 'advanced' | 'basic' | 'thinking' =
134152
activeTab === 'basic'
135153
? 'basic'
@@ -206,8 +224,10 @@ export const ModelsPanel: React.FC<Props> = ({
206224
const handleModelSelect = useCallback(
207225
(value: string) => {
208226
if (value === '__MANUAL_INPUT__') {
227+
isSelectingRef.current = false;
209228
setIsSelecting(false);
210229
setSearchTerm('');
230+
manualInputModeRef.current = true;
211231
setManualInputMode(true);
212232
setManualInputValue(currentModel);
213233
setHasStartedLoading(false);
@@ -218,6 +238,7 @@ export const ModelsPanel: React.FC<Props> = ({
218238
if (modelTarget !== 'thinking') {
219239
void applyModel(value, modelTarget);
220240
}
241+
isSelectingRef.current = false;
221242
setIsSelecting(false);
222243
setSearchTerm('');
223244
setHasStartedLoading(false);
@@ -230,9 +251,11 @@ export const ModelsPanel: React.FC<Props> = ({
230251
if (cleaned && modelTarget !== 'thinking') {
231252
void applyModel(cleaned, modelTarget);
232253
}
254+
manualInputModeRef.current = false;
233255
setManualInputMode(false);
234256
setManualInputValue('');
235257
setSearchTerm('');
258+
setHasStartedLoading(false);
236259
}, [applyModel, manualInputValue, modelTarget]);
237260

238261
const thinkingEnabledValue = useMemo(() => {
@@ -476,6 +499,7 @@ export const ModelsPanel: React.FC<Props> = ({
476499

477500
if (key.escape) {
478501
// 子视图内 ESC 仅收起回到默认视图。
502+
// 使用 ref 同步检查状态,避免 React 状态更新延迟导致需要按两次 ESC
479503
if (thinkingInputMode) {
480504
setThinkingInputMode(null);
481505
setThinkingInputValue('');
@@ -489,14 +513,16 @@ export const ModelsPanel: React.FC<Props> = ({
489513
setIsThinkingEffortSelecting(false);
490514
return;
491515
}
492-
if (manualInputMode) {
516+
if (manualInputModeRef.current || manualInputMode) {
517+
manualInputModeRef.current = false;
493518
setManualInputMode(false);
494519
setManualInputValue('');
495520
setSearchTerm('');
496521
setHasStartedLoading(false);
497522
return;
498523
}
499-
if (isSelecting) {
524+
if (isSelectingRef.current || isSelecting) {
525+
isSelectingRef.current = false;
500526
setIsSelecting(false);
501527
setSearchTerm('');
502528
setHasStartedLoading(false);
@@ -652,15 +678,20 @@ export const ModelsPanel: React.FC<Props> = ({
652678
// 标记已开始加载流程
653679
setHasStartedLoading(true);
654680
void loadModels()
655-
.then(() => setIsSelecting(true))
681+
.then(() => {
682+
isSelectingRef.current = true;
683+
setIsSelecting(true);
684+
})
656685
.catch(() => {
686+
manualInputModeRef.current = true;
657687
setManualInputMode(true);
658688
setManualInputValue(currentModel);
659689
});
660690
return;
661691
}
662692

663693
if ((input === 'm' || input === 'M') && isModelTab) {
694+
manualInputModeRef.current = true;
664695
setManualInputMode(true);
665696
setManualInputValue(currentModel);
666697
}
@@ -737,12 +768,7 @@ export const ModelsPanel: React.FC<Props> = ({
737768
)}
738769

739770
{errorMessage && !loading && (
740-
<Box flexDirection="column">
741-
<Text color={theme.colors.warning}>{t.modelsPanel.tipLabel}</Text>
742-
<Text color={theme.colors.menuSecondary} dimColor>
743-
{errorMessage}
744-
</Text>
745-
</Box>
771+
<Alert variant="error">{errorMessage}</Alert>
746772
)}
747773

748774
{activeTab === 'thinking' ? (
@@ -950,7 +976,7 @@ export const ModelsPanel: React.FC<Props> = ({
950976
limit={10}
951977
disableNumberShortcuts={true}
952978
initialIndex={selectedIndex}
953-
isFocused={true}
979+
isFocused={isSelecting}
954980
onSelect={item => handleModelSelect(item.value)}
955981
/>
956982
</Box>

0 commit comments

Comments
 (0)