thatgfsj-code 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,128 +5,141 @@ export function useChat(app) {
5
5
  isThinking: false,
6
6
  streaming: '',
7
7
  streamingToolCalls: [],
8
+ queuedMessage: null,
8
9
  });
9
10
  const processingRef = useRef(false);
10
- const sendMessage = useCallback((input) => {
11
- if (processingRef.current)
12
- return;
13
- processingRef.current = true;
11
+ const queuedRef = useRef(null);
12
+ const processStream = async (input) => {
14
13
  setState(prev => ({
15
14
  ...prev,
16
15
  messages: [...prev.messages, { role: 'user', content: input }],
17
16
  isThinking: true,
18
17
  streaming: '',
19
18
  streamingToolCalls: [],
19
+ queuedMessage: null,
20
20
  }));
21
21
  app.session.addMessage('user', input);
22
22
  const stream = app.streamResponse();
23
23
  let fullContent = '';
24
24
  let currentToolCalls = [];
25
25
  let lastUpdateTime = 0;
26
- const THROTTLE_MS = 50; // Update UI at most every 50ms
27
- const processStream = async () => {
28
- try {
29
- for await (const chunk of stream) {
30
- if (chunk.includes('@@TOOL@@')) {
31
- const parts = chunk.split('\n');
32
- for (const part of parts) {
33
- if (part.startsWith('@@TOOL@@')) {
34
- try {
35
- const data = JSON.parse(part.slice(8));
36
- if (data.action === 'call') {
37
- currentToolCalls.push({ name: data.name, args: data.args || '' });
38
- setState(prev => ({
39
- ...prev,
40
- isThinking: false,
41
- streamingToolCalls: [...currentToolCalls],
42
- }));
43
- }
44
- else if (data.action === 'result') {
45
- const lastIdx = currentToolCalls.length - 1;
46
- if (lastIdx >= 0) {
47
- currentToolCalls[lastIdx] = {
48
- ...currentToolCalls[lastIdx],
49
- result: data.output || data.error || '',
50
- isError: !!data.error,
51
- };
52
- }
53
- setState(prev => ({
54
- ...prev,
55
- streamingToolCalls: [...currentToolCalls],
56
- isThinking: true,
57
- }));
58
- }
26
+ const THROTTLE_MS = 50;
27
+ try {
28
+ for await (const chunk of stream) {
29
+ if (chunk.includes('@@TOOL@@')) {
30
+ const parts = chunk.split('\n');
31
+ for (const part of parts) {
32
+ if (part.startsWith('@@TOOL@@')) {
33
+ try {
34
+ const data = JSON.parse(part.slice(8));
35
+ if (data.action === 'call') {
36
+ currentToolCalls.push({ name: data.name, args: data.args || '' });
37
+ setState(prev => ({
38
+ ...prev,
39
+ isThinking: false,
40
+ streamingToolCalls: [...currentToolCalls],
41
+ }));
59
42
  }
60
- catch {
61
- fullContent += part;
43
+ else if (data.action === 'result') {
44
+ const lastIdx = currentToolCalls.length - 1;
45
+ if (lastIdx >= 0) {
46
+ currentToolCalls[lastIdx] = {
47
+ ...currentToolCalls[lastIdx],
48
+ result: data.output || data.error || '',
49
+ isError: !!data.error,
50
+ };
51
+ }
52
+ setState(prev => ({
53
+ ...prev,
54
+ streamingToolCalls: [...currentToolCalls],
55
+ isThinking: true,
56
+ }));
62
57
  }
63
58
  }
64
- else if (part) {
59
+ catch {
65
60
  fullContent += part;
66
61
  }
67
62
  }
68
- }
69
- else {
70
- fullContent += chunk;
71
- }
72
- // Throttle UI updates
73
- const now = Date.now();
74
- if (now - lastUpdateTime >= THROTTLE_MS) {
75
- lastUpdateTime = now;
76
- setState(prev => ({ ...prev, isThinking: false, streaming: fullContent }));
63
+ else if (part) {
64
+ fullContent += part;
65
+ }
77
66
  }
78
67
  }
79
- // Final update
80
- if (fullContent.trim() || currentToolCalls.length > 0) {
81
- app.session.addMessage('assistant', fullContent);
68
+ else {
69
+ fullContent += chunk;
82
70
  }
83
- setState(prev => ({
84
- ...prev,
85
- messages: [
86
- ...prev.messages,
87
- ...(fullContent.trim() || currentToolCalls.length > 0
88
- ? [{
89
- role: 'assistant',
90
- content: fullContent,
91
- toolCalls: currentToolCalls.length > 0 ? currentToolCalls : undefined,
92
- }]
93
- : []),
94
- ],
95
- streaming: '',
96
- streamingToolCalls: [],
97
- isThinking: false,
98
- }));
99
- app.session.truncate();
100
- }
101
- catch (error) {
102
- const msg = error.message || String(error);
103
- let errorMsg = `Error: ${msg}`;
104
- if (msg.includes('401') || msg.includes('403') || msg.includes('Unauthorized')) {
105
- errorMsg = `❌ API key invalid. Run \`gfcode init\` to reconfigure.`;
71
+ const now = Date.now();
72
+ if (now - lastUpdateTime >= THROTTLE_MS) {
73
+ lastUpdateTime = now;
74
+ setState(prev => ({ ...prev, isThinking: false, streaming: fullContent }));
106
75
  }
107
- else if (msg.includes('429') || msg.includes('rate limit') || msg.includes('quota')) {
108
- errorMsg = `❌ Rate limit exceeded. Wait or run \`gfcode init\` to switch provider.`;
109
- }
110
- else if (msg.includes('ECONNREFUSED') || msg.includes('ENOTFOUND')) {
111
- errorMsg = `❌ Cannot connect. Check network or run \`gfcode init\`.`;
112
- }
113
- setState(prev => ({
114
- ...prev,
115
- messages: [
116
- ...prev.messages,
117
- ...(fullContent.trim()
118
- ? [{ role: 'assistant', content: fullContent }]
119
- : []),
120
- { role: 'assistant', content: errorMsg },
121
- ],
122
- streaming: '',
123
- streamingToolCalls: [],
124
- isThinking: false,
125
- }));
126
76
  }
77
+ if (fullContent.trim() || currentToolCalls.length > 0) {
78
+ app.session.addMessage('assistant', fullContent);
79
+ }
80
+ setState(prev => ({
81
+ ...prev,
82
+ messages: [
83
+ ...prev.messages,
84
+ ...(fullContent.trim() || currentToolCalls.length > 0
85
+ ? [{
86
+ role: 'assistant',
87
+ content: fullContent,
88
+ toolCalls: currentToolCalls.length > 0 ? currentToolCalls : undefined,
89
+ }]
90
+ : []),
91
+ ],
92
+ streaming: '',
93
+ streamingToolCalls: [],
94
+ isThinking: false,
95
+ }));
96
+ app.session.truncate();
97
+ }
98
+ catch (error) {
99
+ const msg = error.message || String(error);
100
+ let errorMsg = `Error: ${msg}`;
101
+ if (msg.includes('401') || msg.includes('403') || msg.includes('Unauthorized')) {
102
+ errorMsg = `❌ API key invalid. Run \`gfcode init\` to reconfigure.`;
103
+ }
104
+ else if (msg.includes('429') || msg.includes('rate limit') || msg.includes('quota')) {
105
+ errorMsg = `❌ Rate limit exceeded. Wait or run \`gfcode init\` to switch provider.`;
106
+ }
107
+ else if (msg.includes('ECONNREFUSED') || msg.includes('ENOTFOUND')) {
108
+ errorMsg = `❌ Cannot connect. Check network or run \`gfcode init\`.`;
109
+ }
110
+ setState(prev => ({
111
+ ...prev,
112
+ messages: [
113
+ ...prev.messages,
114
+ ...(fullContent.trim()
115
+ ? [{ role: 'assistant', content: fullContent }]
116
+ : []),
117
+ { role: 'assistant', content: errorMsg },
118
+ ],
119
+ streaming: '',
120
+ streamingToolCalls: [],
121
+ isThinking: false,
122
+ }));
123
+ }
124
+ // Check if there's a queued message
125
+ if (queuedRef.current) {
126
+ const next = queuedRef.current;
127
+ queuedRef.current = null;
128
+ await processStream(next);
129
+ }
130
+ else {
127
131
  processingRef.current = false;
128
- };
129
- processStream();
132
+ }
133
+ };
134
+ const sendMessage = useCallback((input) => {
135
+ // If currently processing, queue the message
136
+ if (processingRef.current) {
137
+ queuedRef.current = input;
138
+ setState(prev => ({ ...prev, queuedMessage: input }));
139
+ return;
140
+ }
141
+ processingRef.current = true;
142
+ processStream(input);
130
143
  }, [app]);
131
144
  return { ...state, sendMessage };
132
145
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useChat.js","sourceRoot":"","sources":["../../../src/tui/hooks/useChat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useChat.js","sourceRoot":"","sources":["../../../src/tui/hooks/useChat.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,10 @@ interface CommandResult {
4
4
  output?: string;
5
5
  action?: 'clear' | 'reinit';
6
6
  }
7
+ export declare const COMMAND_LIST: {
8
+ name: string;
9
+ desc: string;
10
+ }[];
7
11
  export declare function useCommands(app: App): {
8
12
  handleCommand: (input: string) => CommandResult;
9
13
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useCommands.d.ts","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE9C,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG;2BAEQ,MAAM,KAAG,aAAa;EA+GjE"}
1
+ {"version":3,"file":"useCommands.d.ts","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE9C,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC7B;AAeD,eAAO,MAAM,YAAY;;;GAQxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG;2BAEQ,MAAM,KAAG,aAAa;EAuHjE"}
@@ -1,10 +1,35 @@
1
1
  import { useCallback } from 'react';
2
+ // 中文命令别名映射
3
+ const CMD_ALIASES = {
4
+ '/模型': '/model',
5
+ '/新建': '/new',
6
+ '/清空': '/new',
7
+ '/压缩': '/compact',
8
+ '/技能': '/skills',
9
+ '/技能管理': '/skills',
10
+ '/mcp': '/mcp',
11
+ '/帮助': '/help',
12
+ '/服务商': '/provider',
13
+ };
14
+ export const COMMAND_LIST = [
15
+ { name: '/模型', desc: '切换模型' },
16
+ { name: '/服务商', desc: '更换服务商' },
17
+ { name: '/新建', desc: '新建会话' },
18
+ { name: '/压缩', desc: '压缩上下文' },
19
+ { name: '/技能', desc: '管理技能' },
20
+ { name: '/mcp', desc: 'MCP 设置' },
21
+ { name: '/帮助', desc: '查看帮助' },
22
+ ];
2
23
  export function useCommands(app) {
3
24
  const handleCommand = useCallback((input) => {
4
25
  const cmd = input.trim();
5
26
  const parts = cmd.split(/\s+/);
6
- const name = parts[0].toLowerCase();
27
+ let name = parts[0].toLowerCase();
7
28
  const arg = parts.slice(1).join(' ');
29
+ // 中文别名映射
30
+ if (CMD_ALIASES[name]) {
31
+ name = CMD_ALIASES[name];
32
+ }
8
33
  // ── /model [name] ───────────────────────────────────
9
34
  if (name === '/model') {
10
35
  if (!arg) {
@@ -12,59 +37,57 @@ export function useCommands(app) {
12
37
  return {
13
38
  handled: true,
14
39
  output: [
15
- `Current: ${c.provider} / ${c.model}`,
40
+ `当前: ${c.provider} / ${c.model}`,
16
41
  '',
17
- 'Usage: /model <name>',
18
- ' /model deepseek-chat',
19
- ' /model gpt-4o',
20
- ' /model claude-sonnet-4-20250514',
21
- ' /model qwen3-235b-a22b',
42
+ '用法: /模型 <名称>',
43
+ ' /模型 deepseek-chat',
44
+ ' /模型 gpt-4o',
45
+ ' /模型 claude-sonnet-4-20250514',
22
46
  '',
23
- 'Or: /provider to change provider',
47
+ '或: /服务商 更换服务商',
24
48
  ].join('\n'),
25
49
  };
26
50
  }
27
51
  app.config.save({ model: arg });
28
- return { handled: true, output: `Model → ${arg}` };
52
+ return { handled: true, output: `模型 → ${arg}` };
29
53
  }
30
54
  // ── /provider ───────────────────────────────────────
31
55
  if (name === '/provider') {
32
56
  return { handled: true, action: 'reinit' };
33
57
  }
34
58
  // ── /new, /clear ────────────────────────────────────
35
- if (name === '/new' || name === '/clear') {
59
+ if (name === '/new') {
36
60
  app.session.clear();
37
- return { handled: true, output: 'New session.', action: 'clear' };
61
+ return { handled: true, output: '新会话已创建。', action: 'clear' };
38
62
  }
39
63
  // ── /compact ────────────────────────────────────────
40
64
  if (name === '/compact') {
41
65
  const before = app.session.getMessageCount();
42
66
  app.session.truncate();
43
67
  const after = app.session.getMessageCount();
44
- return { handled: true, output: `Compacted: ${before} → ${after} messages` };
68
+ return { handled: true, output: `上下文已压缩: ${before} → ${after} 条消息` };
45
69
  }
46
70
  // ── /skills [id] ────────────────────────────────────
47
71
  if (name === '/skills') {
48
72
  if (arg) {
49
- // Toggle specific skill
50
73
  if (app.skills.isActive(arg)) {
51
74
  app.skills.deactivate(arg);
52
- return { handled: true, output: `✗ ${arg}` };
75
+ return { handled: true, output: `✗ 已关闭: ${arg}` };
53
76
  }
54
77
  else if (app.skills.activate(arg)) {
55
- return { handled: true, output: `✓ ${arg}` };
78
+ return { handled: true, output: `✓ 已开启: ${arg}` };
56
79
  }
57
- return { handled: true, output: `Unknown skill: ${arg}` };
80
+ return { handled: true, output: `未知技能: ${arg}` };
58
81
  }
59
82
  const all = app.skills.list();
60
83
  const lines = [
61
- 'Skills:',
84
+ '技能列表:',
62
85
  ...all.map(s => {
63
86
  const mark = app.skills.isActive(s.id) ? '✓' : '·';
64
- return ` ${mark} ${s.id}`;
87
+ return ` ${mark} ${s.id} ${s.description}`;
65
88
  }),
66
89
  '',
67
- 'Usage: /skills <id> to toggle',
90
+ '用法: /技能 <id> 切换',
68
91
  ];
69
92
  return { handled: true, output: lines.join('\n') };
70
93
  }
@@ -73,29 +96,34 @@ export function useCommands(app) {
73
96
  return {
74
97
  handled: true,
75
98
  output: [
76
- 'MCP: ~/.thatgfsj/mcp.json',
99
+ 'MCP 配置: ~/.thatgfsj/mcp.json',
77
100
  '',
78
101
  ' {',
79
102
  ' "servers": {',
80
- ' "name": { "command": "npx", "args": ["-y", "server"] }',
103
+ ' "名称": { "command": "npx", "args": ["-y", "server"] }',
81
104
  ' }',
82
105
  ' }',
83
106
  ].join('\n'),
84
107
  };
85
108
  }
86
109
  // ── /help ───────────────────────────────────────────
87
- if (name === '/help' || name === 'help') {
110
+ if (name === '/help') {
88
111
  return {
89
112
  handled: true,
90
113
  output: [
91
- '/model <name> Switch model',
92
- '/provider Change provider',
93
- '/new New session',
94
- '/compact Compress context',
95
- '/skills [id] List/toggle skills',
96
- '/mcp MCP settings',
97
- '/help This help',
98
- 'exit Quit',
114
+ '命令列表:',
115
+ ' /模型 <名称> 切换模型',
116
+ ' /服务商 更换服务商',
117
+ ' /新建 新建会话',
118
+ ' /压缩 压缩上下文',
119
+ ' /技能 [id] 管理技能',
120
+ ' /mcp MCP 设置',
121
+ ' /帮助 查看帮助',
122
+ ' exit 退出',
123
+ '',
124
+ '快捷键:',
125
+ ' ↑/↓ 历史记录',
126
+ ' Ctrl+C 退出',
99
127
  ].join('\n'),
100
128
  };
101
129
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useCommands.js","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useCommands.js","sourceRoot":"","sources":["../../../src/tui/hooks/useCommands.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thatgfsj-code",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "Thatgfsj Code - AI Coding Assistant",
5
5
  "main": "dist/cmd/index.js",
6
6
  "type": "module",
package/src/cmd/index.tsx CHANGED
@@ -28,7 +28,7 @@ process.on('unhandledRejection', (reason) => {
28
28
  program
29
29
  .name('gfcode')
30
30
  .description('Thatgfsj Code - AI Coding Assistant')
31
- .version('0.9.3')
31
+ .version('0.9.5')
32
32
  .argument('[prompt]', 'Task to execute (omit to start interactive mode)')
33
33
  .option('-m, --model <model>', 'Specify model')
34
34
  .option('-i, --interactive', 'Force interactive mode')
package/src/tui/app.tsx CHANGED
@@ -8,39 +8,63 @@ import { UserInput } from './components/UserInput.js';
8
8
  import { StatusBar } from './components/StatusBar.js';
9
9
  import { ModelSelector } from './components/ModelSelector.js';
10
10
  import { useChat } from './hooks/useChat.js';
11
- import { useCommands } from './hooks/useCommands.js';
11
+ import { useCommands, COMMAND_LIST } from './hooks/useCommands.js';
12
12
  import type { App } from '../app/index.js';
13
13
  import type { MessageData } from './components/ChatMessage.js';
14
+ import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
15
+ import { join } from 'path';
16
+ import { homedir } from 'os';
14
17
 
15
18
  interface Props {
16
19
  app: App;
17
20
  }
18
21
 
22
+ function saveModelToHistory(model: string) {
23
+ const dir = join(homedir(), '.thatgfsj');
24
+ const path = join(dir, 'models.json');
25
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
26
+
27
+ let history: string[] = [];
28
+ if (existsSync(path)) {
29
+ try { history = JSON.parse(readFileSync(path, 'utf-8')); } catch {}
30
+ }
31
+ if (!history.includes(model)) {
32
+ history.push(model);
33
+ writeFileSync(path, JSON.stringify(history, null, 2));
34
+ }
35
+ }
36
+
19
37
  export function TuiApp({ app }: Props) {
20
- const { messages, isThinking, streaming, streamingToolCalls, sendMessage } = useChat(app);
38
+ const { messages, isThinking, streaming, streamingToolCalls, queuedMessage, sendMessage } = useChat(app);
21
39
  const { handleCommand } = useCommands(app);
22
40
  const [systemMessages, setSystemMessages] = useState<MessageData[]>([]);
23
41
  const [showModelSelector, setShowModelSelector] = useState(false);
42
+ const [addModelMode, setAddModelMode] = useState(false);
24
43
  const { stdout } = useStdout();
25
44
  const terminalWidth = stdout?.columns || 80;
26
45
 
27
46
  const onSubmit = useCallback(async (input: string) => {
28
- // If model selector is showing, treat input as custom model name
29
- if (showModelSelector) {
30
- app.config.save({ model: input.trim() });
31
- setShowModelSelector(false);
32
- setSystemMessages(prev => [
33
- ...prev,
34
- { role: 'assistant', content: `Model → ${input.trim()}` },
35
- ]);
47
+ // 添加新模型模式
48
+ if (addModelMode) {
49
+ const model = input.trim();
50
+ if (model) {
51
+ app.config.save({ model });
52
+ saveModelToHistory(model);
53
+ setSystemMessages(prev => [
54
+ ...prev,
55
+ { role: 'assistant', content: `模型已切换: ${model}` },
56
+ ]);
57
+ }
58
+ setAddModelMode(false);
36
59
  return;
37
60
  }
38
61
 
62
+ if (showModelSelector) return;
63
+
39
64
  const result = handleCommand(input);
40
65
 
41
66
  if (result.handled) {
42
- // Special: /model shows selector
43
- if (input.trim().toLowerCase() === '/model') {
67
+ if (input.trim().toLowerCase() === '/model' || input.trim() === '/模型') {
44
68
  setShowModelSelector(true);
45
69
  return;
46
70
  }
@@ -53,9 +77,7 @@ export function TuiApp({ app }: Props) {
53
77
  ]);
54
78
  }
55
79
 
56
- if (result.action === 'clear') {
57
- setSystemMessages([]);
58
- }
80
+ if (result.action === 'clear') setSystemMessages([]);
59
81
 
60
82
  if (result.action === 'reinit') {
61
83
  const { WelcomeScreen } = await import('./welcome.js');
@@ -65,15 +87,15 @@ export function TuiApp({ app }: Props) {
65
87
  Object.assign(app, newApp);
66
88
  setSystemMessages(prev => [
67
89
  ...prev,
68
- { role: 'assistant', content: 'Config updated. Model: ' + app.config.get().model },
90
+ { role: 'assistant', content: '配置已更新,模型: ' + app.config.get().model },
69
91
  ]);
70
92
  }
71
93
 
72
94
  return;
73
95
  }
74
96
 
75
- await sendMessage(input);
76
- }, [handleCommand, sendMessage, app, showModelSelector]);
97
+ sendMessage(input);
98
+ }, [handleCommand, sendMessage, app, showModelSelector, addModelMode]);
77
99
 
78
100
  const allMessages = [...systemMessages, ...messages];
79
101
  const activeSkills = app.skills.listActive().map(s => s.id);
@@ -88,21 +110,35 @@ export function TuiApp({ app }: Props) {
88
110
  width={terminalWidth - 4}
89
111
  />
90
112
  <Thinking active={isThinking} />
113
+ {queuedMessage && (
114
+ <Box paddingLeft={1}>
115
+ <Text color="#F59E0B">📎 已排队: </Text>
116
+ <Text color="#94A3B8">{queuedMessage}</Text>
117
+ </Box>
118
+ )}
91
119
  {showModelSelector ? (
92
120
  <ModelSelector
93
121
  currentModel={app.config.get().model}
94
122
  onSelect={(model) => {
95
123
  app.config.save({ model });
124
+ saveModelToHistory(model);
96
125
  setShowModelSelector(false);
97
126
  setSystemMessages(prev => [
98
127
  ...prev,
99
- { role: 'assistant', content: `Model ${model}` },
128
+ { role: 'assistant', content: `模型已切换: ${model}` },
100
129
  ]);
101
130
  }}
102
- onCancel={() => setShowModelSelector(false)}
131
+ onAddNew={() => {
132
+ setShowModelSelector(false);
133
+ setAddModelMode(true);
134
+ }}
103
135
  />
136
+ ) : addModelMode ? (
137
+ <Box paddingLeft={1}>
138
+ <Text color="#06B6D4" bold>输入新模型名称: </Text>
139
+ </Box>
104
140
  ) : (
105
- <UserInput onSubmit={onSubmit} disabled={isThinking} />
141
+ <UserInput onSubmit={onSubmit} disabled={false} />
106
142
  )}
107
143
  <StatusBar messageCount={allMessages.length} skills={activeSkills} />
108
144
  </Box>
@@ -14,7 +14,7 @@ export const Header = React.memo(function Header({ provider, model }: Props) {
14
14
  <Box>
15
15
  <Text color="#06B6D4" bold> ⚡ </Text>
16
16
  <Text color="#22D3EE" bold>THATGFSJ CODE</Text>
17
- <Text dimColor> v0.9.3</Text>
17
+ <Text dimColor> v0.9.5</Text>
18
18
  </Box>
19
19
  <Box>
20
20
  <Text color="#06B6D4" bold> {provider} </Text>