aegiscode 3.1.10 → 3.4.2
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.
- package/bin/aegis.js +165 -0
- package/package.json +18 -86
- package/LICENSE +0 -21
- package/README.md +0 -394
- package/dist/main.js +0 -2171
- package/gui/run-gui.cjs +0 -13
- package/src/agent/Agent.ts +0 -903
- package/src/agent/SimpleAgent.ts +0 -48
- package/src/agent/index.ts +0 -54
- package/src/agent/orchestrator/AppBuilder.ts +0 -443
- package/src/agent/orchestrator/CouncilAgent.ts +0 -310
- package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
- package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
- package/src/agent/orchestrator/index.ts +0 -38
- package/src/agent/orchestrator/utils.ts +0 -397
- package/src/agent/pricing.ts +0 -115
- package/src/agent/router.ts +0 -74
- package/src/agent/routerStats.ts +0 -121
- package/src/agent/types.ts +0 -318
- package/src/auth/login.ts +0 -383
- package/src/cli/config.ts +0 -189
- package/src/cli/index.ts +0 -17
- package/src/cli/middleware.ts +0 -119
- package/src/cli/types.ts +0 -75
- package/src/config/ConfigManager.ts +0 -587
- package/src/config/index.ts +0 -7
- package/src/config/types.ts +0 -584
- package/src/context/CompactionService.ts +0 -300
- package/src/context/ContextManager.ts +0 -450
- package/src/context/FileAnalyzer.ts +0 -267
- package/src/context/TokenCounter.ts +0 -265
- package/src/context/index.ts +0 -27
- package/src/context/storage/CacheStore.ts +0 -176
- package/src/context/storage/JSONLStore.ts +0 -201
- package/src/context/storage/MemoryStore.ts +0 -205
- package/src/context/storage/PersistentStore.ts +0 -327
- package/src/context/storage/index.ts +0 -9
- package/src/context/storage/pathUtils.ts +0 -114
- package/src/context/test.ts +0 -309
- package/src/context/types.ts +0 -268
- package/src/hooks/HookExecutor.ts +0 -434
- package/src/hooks/HookManager.ts +0 -596
- package/src/hooks/HookService.ts +0 -269
- package/src/hooks/Matcher.ts +0 -157
- package/src/hooks/index.ts +0 -63
- package/src/hooks/types.ts +0 -424
- package/src/main.tsx +0 -596
- package/src/mcp/HealthMonitor.ts +0 -150
- package/src/mcp/McpClient.ts +0 -491
- package/src/mcp/McpRegistry.ts +0 -321
- package/src/mcp/createMcpTool.ts +0 -251
- package/src/mcp/index.ts +0 -15
- package/src/mcp/server.ts +0 -334
- package/src/mcp/test-server.ts +0 -88
- package/src/mcp/test.ts +0 -372
- package/src/mcp/types.ts +0 -247
- package/src/memory/AgentMemoryBus.ts +0 -432
- package/src/memory/CloudSync.ts +0 -99
- package/src/memory/DriveSync.ts +0 -106
- package/src/memory/SharedMemory.ts +0 -951
- package/src/memory/index.ts +0 -14
- package/src/memory/machineFingerprint.ts +0 -40
- package/src/orchestrator/SubAgentMetadata.ts +0 -136
- package/src/prompts/builder.ts +0 -213
- package/src/prompts/default.ts +0 -144
- package/src/prompts/index.ts +0 -16
- package/src/prompts/plan.ts +0 -64
- package/src/prompts/test.ts +0 -78
- package/src/services/AnthropicChatService.ts +0 -341
- package/src/services/ChatService.ts +0 -347
- package/src/services/ClaudeCliChatService.ts +0 -256
- package/src/services/CloudSync.ts +0 -168
- package/src/services/CostLedger.ts +0 -211
- package/src/services/Heartbeat.ts +0 -135
- package/src/services/LearningCollector.ts +0 -291
- package/src/services/OllamaInstaller.ts +0 -342
- package/src/services/VersionChecker.ts +0 -445
- package/src/services/index.ts +0 -57
- package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
- package/src/services/streaming/RenderingProfile.ts +0 -90
- package/src/services/streaming/StreamEventParser.ts +0 -181
- package/src/services/streaming/ThrottledRenderer.ts +0 -139
- package/src/services/streaming/TranscriptBuffer.ts +0 -574
- package/src/services/streaming/eventStatusMap.ts +0 -52
- package/src/services/streaming/index.ts +0 -46
- package/src/services/streaming/renderFormatting.ts +0 -79
- package/src/services/streaming/types.ts +0 -234
- package/src/skills/SkillLoader.ts +0 -126
- package/src/skills/SkillRegistry.ts +0 -366
- package/src/skills/index.ts +0 -48
- package/src/skills/types.ts +0 -146
- package/src/slash-commands/billing.ts +0 -70
- package/src/slash-commands/build.ts +0 -413
- package/src/slash-commands/builtinCommands.ts +0 -2733
- package/src/slash-commands/clone.ts +0 -242
- package/src/slash-commands/council.ts +0 -125
- package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
- package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
- package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
- package/src/slash-commands/custom/index.ts +0 -7
- package/src/slash-commands/debate.ts +0 -254
- package/src/slash-commands/gmail.ts +0 -105
- package/src/slash-commands/index.ts +0 -388
- package/src/slash-commands/mcpCommand.ts +0 -205
- package/src/slash-commands/types.ts +0 -201
- package/src/store/index.ts +0 -76
- package/src/store/selectors.ts +0 -246
- package/src/store/slices/appSlice.ts +0 -205
- package/src/store/slices/commandSlice.ts +0 -115
- package/src/store/slices/configSlice.ts +0 -46
- package/src/store/slices/focusSlice.ts +0 -64
- package/src/store/slices/index.ts +0 -9
- package/src/store/slices/sessionSlice.ts +0 -424
- package/src/store/streaming-buffer.ts +0 -425
- package/src/store/test.ts +0 -296
- package/src/store/types.ts +0 -274
- package/src/store/vanilla.ts +0 -186
- package/src/tools/builtin/bash.ts +0 -236
- package/src/tools/builtin/council.ts +0 -105
- package/src/tools/builtin/edit.ts +0 -213
- package/src/tools/builtin/glob.ts +0 -136
- package/src/tools/builtin/grep.ts +0 -263
- package/src/tools/builtin/index.ts +0 -61
- package/src/tools/builtin/memory.ts +0 -66
- package/src/tools/builtin/read.ts +0 -168
- package/src/tools/builtin/skill.ts +0 -97
- package/src/tools/builtin/snapshot.ts +0 -40
- package/src/tools/builtin/task.ts +0 -106
- package/src/tools/builtin/write.ts +0 -134
- package/src/tools/createTool.ts +0 -221
- package/src/tools/execution/ExecutionPipeline.ts +0 -263
- package/src/tools/execution/index.ts +0 -40
- package/src/tools/execution/stages/CacheStage.ts +0 -131
- package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
- package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
- package/src/tools/execution/stages/ExecutionStage.ts +0 -48
- package/src/tools/execution/stages/FormattingStage.ts +0 -44
- package/src/tools/execution/stages/HookStage.ts +0 -72
- package/src/tools/execution/stages/PermissionStage.ts +0 -287
- package/src/tools/execution/stages/PostHookStage.ts +0 -71
- package/src/tools/execution/stages/index.ts +0 -12
- package/src/tools/execution/test.ts +0 -266
- package/src/tools/execution/types.ts +0 -273
- package/src/tools/index.ts +0 -81
- package/src/tools/registry.ts +0 -304
- package/src/tools/schemas.ts +0 -109
- package/src/tools/test.ts +0 -220
- package/src/tools/types.ts +0 -175
- package/src/tools/validation/PermissionChecker.ts +0 -242
- package/src/tools/validation/SensitiveFileDetector.ts +0 -210
- package/src/tools/validation/index.ts +0 -11
- package/src/ui/App.tsx +0 -166
- package/src/ui/components/AegisInterface.tsx +0 -484
- package/src/ui/components/common/ChatSearch.tsx +0 -150
- package/src/ui/components/common/ErrorBoundary.tsx +0 -82
- package/src/ui/components/common/ExitMessage.tsx +0 -120
- package/src/ui/components/common/LoadingIndicator.tsx +0 -49
- package/src/ui/components/common/index.ts +0 -6
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
- package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
- package/src/ui/components/dialog/SetupWizard.tsx +0 -297
- package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
- package/src/ui/components/dialog/index.ts +0 -8
- package/src/ui/components/index.ts +0 -28
- package/src/ui/components/input/CommandSuggestions.tsx +0 -139
- package/src/ui/components/input/CustomTextInput.tsx +0 -220
- package/src/ui/components/input/InputArea.tsx +0 -361
- package/src/ui/components/input/PromptSuggestions.tsx +0 -66
- package/src/ui/components/input/index.ts +0 -6
- package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
- package/src/ui/components/layout/ContextBar.tsx +0 -79
- package/src/ui/components/layout/MessageArea.tsx +0 -96
- package/src/ui/components/layout/MessageList.tsx +0 -647
- package/src/ui/components/layout/MessageSeparator.tsx +0 -26
- package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
- package/src/ui/components/layout/index.ts +0 -7
- package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
- package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
- package/src/ui/components/markdown/index.ts +0 -8
- package/src/ui/components/markdown/parser.ts +0 -336
- package/src/ui/components/markdown/types.ts +0 -66
- package/src/ui/focus/FocusManager.ts +0 -137
- package/src/ui/focus/index.ts +0 -13
- package/src/ui/focus/types.ts +0 -54
- package/src/ui/focus/useFocus.ts +0 -75
- package/src/ui/hooks/index.ts +0 -11
- package/src/ui/hooks/useAgent.ts +0 -284
- package/src/ui/hooks/useCommandHistory.ts +0 -87
- package/src/ui/hooks/useCommandProcessor.ts +0 -443
- package/src/ui/hooks/useConfirmation.ts +0 -99
- package/src/ui/hooks/useCtrlCHandler.ts +0 -100
- package/src/ui/hooks/useInputBuffer.ts +0 -122
- package/src/ui/hooks/useTerminalSize.ts +0 -68
- package/src/ui/hooks/useTerminalWidth.ts +0 -5
- package/src/ui/hooks/useWindowedList.ts +0 -118
- package/src/ui/render-debugger.ts +0 -621
- package/src/ui/test.ts +0 -189
- package/src/ui/themes/ThemeManager.ts +0 -332
- package/src/ui/themes/aegisTheme.ts +0 -87
- package/src/ui/themes/darkTheme.ts +0 -87
- package/src/ui/themes/defaultTheme.ts +0 -85
- package/src/ui/themes/index.ts +0 -10
- package/src/ui/themes/lightTheme.ts +0 -89
- package/src/ui/themes/popularThemes.ts +0 -187
- package/src/ui/themes/types.ts +0 -130
- package/src/utils/clipboard.ts +0 -48
- package/src/utils/debug.ts +0 -43
- package/src/utils/environment.ts +0 -68
- package/src/utils/index.ts +0 -10
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* InputArea - 输入区域组件
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React, { useCallback, useState, useMemo, useEffect, useRef, memo } from 'react';
|
|
9
|
-
import { Box, Text, useInput } from 'ink';
|
|
10
|
-
|
|
11
|
-
const PromptGlyph: React.FC<{ isProcessing: boolean; lockedIn: boolean; glowPhase: number; color: string; idleColor: string }> = memo(({ isProcessing, lockedIn, glowPhase, color, idleColor }) => {
|
|
12
|
-
const displayColor = (isProcessing && lockedIn)
|
|
13
|
-
? ELECTRIC_COLORS[glowPhase % ELECTRIC_COLORS.length]
|
|
14
|
-
: idleColor;
|
|
15
|
-
return <Text color={displayColor} bold>□</Text>;
|
|
16
|
-
});
|
|
17
|
-
PromptGlyph.displayName = 'PromptGlyph';
|
|
18
|
-
import { CustomTextInput } from './CustomTextInput.js';
|
|
19
|
-
import { CommandSuggestions } from './CommandSuggestions.js';
|
|
20
|
-
|
|
21
|
-
import { themeManager } from '../../themes/index.js';
|
|
22
|
-
import { FocusId, focusManager } from '../../focus/index.js';
|
|
23
|
-
import { getState, useIsThinking, usePendingCommands, useCurrentCommand } from '../../../store/index.js';
|
|
24
|
-
import { vanillaStore } from '../../../store/vanilla.js';
|
|
25
|
-
import { getStreamingContent } from '../../../store/streaming-buffer.js';
|
|
26
|
-
|
|
27
|
-
// Electric blue color ramp — cycles fast to simulate voltage/energy
|
|
28
|
-
const ELECTRIC_COLORS = [
|
|
29
|
-
'#0055FF', '#0077FF', '#0099FF', '#00AAFF',
|
|
30
|
-
'#00CCFF', '#00EEFF', '#00FFFF', '#00EEFF',
|
|
31
|
-
'#00CCFF', '#00AAFF', '#0099FF', '#0077FF',
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
interface InputAreaProps {
|
|
35
|
-
/** 提交回调 */
|
|
36
|
-
onSubmit?: (value: string) => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*/
|
|
43
|
-
export const InputArea: React.FC<InputAreaProps> = React.memo(
|
|
44
|
-
({
|
|
45
|
-
onSubmit,
|
|
46
|
-
}) => {
|
|
47
|
-
// 使用 ref 保持回调引用稳
|
|
48
|
-
const onSubmitRef = useRef(onSubmit);
|
|
49
|
-
|
|
50
|
-
// 更新 ref(不触发重新渲
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
onSubmitRef.current = onSubmit;
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// 自管理命令历史(使用 ref 避免状态变化导致重新渲染传播到父组
|
|
56
|
-
const historyRef = useRef<string[]>([]);
|
|
57
|
-
const historyIndexRef = useRef(-1);
|
|
58
|
-
|
|
59
|
-
const addToHistory = useCallback((command: string) => {
|
|
60
|
-
if (command.trim()) {
|
|
61
|
-
const history = historyRef.current;
|
|
62
|
-
if (history[history.length - 1] !== command) {
|
|
63
|
-
history.push(command);
|
|
64
|
-
if (history.length > 100) history.shift();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
historyIndexRef.current = -1;
|
|
68
|
-
}, []);
|
|
69
|
-
|
|
70
|
-
const getPreviousCommand = useCallback(() => {
|
|
71
|
-
const history = historyRef.current;
|
|
72
|
-
if (history.length === 0) return null;
|
|
73
|
-
if (historyIndexRef.current < history.length - 1) {
|
|
74
|
-
historyIndexRef.current++;
|
|
75
|
-
return history[history.length - 1 - historyIndexRef.current];
|
|
76
|
-
}
|
|
77
|
-
return history[0];
|
|
78
|
-
}, []);
|
|
79
|
-
|
|
80
|
-
const getNextCommand = useCallback(() => {
|
|
81
|
-
if (historyIndexRef.current > 0) {
|
|
82
|
-
historyIndexRef.current--;
|
|
83
|
-
return historyRef.current[historyRef.current.length - 1 - historyIndexRef.current];
|
|
84
|
-
}
|
|
85
|
-
historyIndexRef.current = -1;
|
|
86
|
-
return '';
|
|
87
|
-
}, []);
|
|
88
|
-
|
|
89
|
-
const theme = themeManager.getTheme();
|
|
90
|
-
|
|
91
|
-
// 自己订阅需要的状
|
|
92
|
-
const isProcessing = useIsThinking();
|
|
93
|
-
const pendingCommands = usePendingCommands();
|
|
94
|
-
const currentCommand = useCurrentCommand();
|
|
95
|
-
|
|
96
|
-
const [pasteNotification, setPasteNotification] = useState<string | null>(null);
|
|
97
|
-
const pasteNotifRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
98
|
-
|
|
99
|
-
const [thinkingSeconds, setThinkingSeconds] = useState(0);
|
|
100
|
-
const [streamingTokens, setStreamingTokens] = useState(0);
|
|
101
|
-
const thinkingTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
if (isProcessing) {
|
|
104
|
-
setThinkingSeconds(0);
|
|
105
|
-
setStreamingTokens(0);
|
|
106
|
-
thinkingTimerRef.current = setInterval(() => {
|
|
107
|
-
setThinkingSeconds(s => s + 1);
|
|
108
|
-
const buf = getStreamingContent();
|
|
109
|
-
if (buf) setStreamingTokens(Math.ceil(buf.content.length / 4));
|
|
110
|
-
}, 1000);
|
|
111
|
-
} else {
|
|
112
|
-
if (thinkingTimerRef.current) { clearInterval(thinkingTimerRef.current); thinkingTimerRef.current = null; }
|
|
113
|
-
setThinkingSeconds(0);
|
|
114
|
-
setStreamingTokens(0);
|
|
115
|
-
}
|
|
116
|
-
return () => { if (thinkingTimerRef.current) { clearInterval(thinkingTimerRef.current); thinkingTimerRef.current = null; } };
|
|
117
|
-
}, [isProcessing]);
|
|
118
|
-
|
|
119
|
-
const [hasStreamingMessage, setHasStreamingMessage] = useState(
|
|
120
|
-
() => getState().session.messages.some(m => m.isStreaming)
|
|
121
|
-
);
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
let prev = hasStreamingMessage;
|
|
124
|
-
const unsubscribe = vanillaStore.subscribe((state) => {
|
|
125
|
-
const msgs = state.session.messages;
|
|
126
|
-
const newVal = msgs[msgs.length - 1]?.isStreaming ?? false;
|
|
127
|
-
if (newVal !== prev) {
|
|
128
|
-
prev = newVal;
|
|
129
|
-
setHasStreamingMessage(newVal);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
return unsubscribe;
|
|
133
|
-
}, []);
|
|
134
|
-
|
|
135
|
-
// Electric glow animation — 200ms ticks, only active while processing
|
|
136
|
-
const [glowPhase, setGlowPhase] = useState(0);
|
|
137
|
-
const glowTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
if (isProcessing) {
|
|
140
|
-
setGlowPhase(0);
|
|
141
|
-
glowTimerRef.current = setInterval(() => setGlowPhase(p => p + 1), 150);
|
|
142
|
-
} else {
|
|
143
|
-
if (glowTimerRef.current) { clearInterval(glowTimerRef.current); glowTimerRef.current = null; }
|
|
144
|
-
setGlowPhase(0);
|
|
145
|
-
}
|
|
146
|
-
return () => { if (glowTimerRef.current) { clearInterval(glowTimerRef.current); glowTimerRef.current = null; } };
|
|
147
|
-
}, [isProcessing]);
|
|
148
|
-
|
|
149
|
-
// Cursor blink at idle (530 ms on/off), piggybacks on glowPhase during generation
|
|
150
|
-
const [idleBlink, setIdleBlink] = useState(true);
|
|
151
|
-
const idleBlinkRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
152
|
-
useEffect(() => {
|
|
153
|
-
if (!isProcessing) {
|
|
154
|
-
setIdleBlink(true);
|
|
155
|
-
idleBlinkRef.current = setInterval(() => setIdleBlink(v => !v), 530);
|
|
156
|
-
} else {
|
|
157
|
-
if (idleBlinkRef.current) { clearInterval(idleBlinkRef.current); idleBlinkRef.current = null; }
|
|
158
|
-
setIdleBlink(true);
|
|
159
|
-
}
|
|
160
|
-
return () => { if (idleBlinkRef.current) { clearInterval(idleBlinkRef.current); idleBlinkRef.current = null; } };
|
|
161
|
-
}, [isProcessing]);
|
|
162
|
-
const cursorOn = isProcessing ? Math.floor(glowPhase / 5) % 2 === 0 : idleBlink;
|
|
163
|
-
|
|
164
|
-
const thinkingLabel = useMemo(() => {
|
|
165
|
-
if (!isProcessing) return null;
|
|
166
|
-
const cmd = currentCommand?.slice(0, 80);
|
|
167
|
-
const cmdStr = cmd ? ` · locked: ${cmd}` : '';
|
|
168
|
-
const elapsed = thinkingSeconds >= 2 ? ` · ${thinkingSeconds}s` : '';
|
|
169
|
-
const tokens = streamingTokens > 0 ? ` · ~${streamingTokens}t` : '';
|
|
170
|
-
if (hasStreamingMessage) return `generating${tokens}${elapsed}${cmdStr}`;
|
|
171
|
-
return `thinking${elapsed}${cmdStr}`;
|
|
172
|
-
}, [isProcessing, hasStreamingMessage, thinkingSeconds, streamingTokens, currentCommand]);
|
|
173
|
-
|
|
174
|
-
// 计
|
|
175
|
-
const placeholder = useMemo(() => {
|
|
176
|
-
if (isProcessing) {
|
|
177
|
-
return pendingCommands.length > 0
|
|
178
|
-
? `Queued: ${pendingCommands.length} command(s). Type to add more...`
|
|
179
|
-
: 'Processing... Type to queue next command';
|
|
180
|
-
}
|
|
181
|
-
return 'Type a message...';
|
|
182
|
-
}, [isProcessing, pendingCommands.length]);
|
|
183
|
-
|
|
184
|
-
// 自管理的输入状
|
|
185
|
-
const [input, setInput] = useState('');
|
|
186
|
-
const [cursorPosition, setCursorPosition] = useState(0);
|
|
187
|
-
const inputRef = useRef({ value: '', cursorPosition: 0 });
|
|
188
|
-
|
|
189
|
-
// 更新 ref 保持同
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
inputRef.current = { value: input, cursorPosition };
|
|
192
|
-
}, [input, cursorPosition]);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// 设置输入
|
|
197
|
-
const handleChange = useCallback((newValue: string) => {
|
|
198
|
-
inputRef.current.value = newValue;
|
|
199
|
-
setInput(newValue);
|
|
200
|
-
// cursor is always set immediately after via onChangeCursorPosition — no guard needed
|
|
201
|
-
}, []);
|
|
202
|
-
|
|
203
|
-
// 设置光标位
|
|
204
|
-
const handleChangeCursorPosition = useCallback((pos: number) => {
|
|
205
|
-
const newPos = Math.max(0, Math.min(pos, inputRef.current.value.length));
|
|
206
|
-
inputRef.current.cursorPosition = newPos;
|
|
207
|
-
setCursorPosition(newPos);
|
|
208
|
-
}, []);
|
|
209
|
-
|
|
210
|
-
const showSuggestions = input.startsWith('/') && input.length > 0 && !isProcessing;
|
|
211
|
-
const showPromptSuggestions = false; // Suggestions disabled
|
|
212
|
-
|
|
213
|
-
// 选择建议回
|
|
214
|
-
const handleSelectSuggestion = useCallback((newValue: string) => {
|
|
215
|
-
handleChange(newValue);
|
|
216
|
-
handleChangeCursorPosition(newValue.length);
|
|
217
|
-
}, [handleChange, handleChangeCursorPosition]);
|
|
218
|
-
|
|
219
|
-
// 清空输
|
|
220
|
-
const clearInput = useCallback(() => {
|
|
221
|
-
setInput('');
|
|
222
|
-
setCursorPosition(0);
|
|
223
|
-
inputRef.current = { value: '', cursorPosition: 0 };
|
|
224
|
-
}, []);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// Paste — mimic Claude Code: show paste size notification
|
|
229
|
-
// Trimming is handled in CustomTextInput.tsx's handlePasteStable
|
|
230
|
-
const handlePaste = useCallback((text: string) => {
|
|
231
|
-
const lines = text.split('\n').length;
|
|
232
|
-
const chars = text.length;
|
|
233
|
-
|
|
234
|
-
if (lines > 1 || chars > 200) {
|
|
235
|
-
const msg = lines > 1 ? `pasted ${lines} lines (${chars} chars)` : `pasted ${chars} chars`;
|
|
236
|
-
setPasteNotification(msg);
|
|
237
|
-
if (pasteNotifRef.current) clearTimeout(pasteNotifRef.current);
|
|
238
|
-
pasteNotifRef.current = setTimeout(() => setPasteNotification(null), 2000);
|
|
239
|
-
}
|
|
240
|
-
}, []);
|
|
241
|
-
|
|
242
|
-
// 提交处
|
|
243
|
-
const handleSubmit = useCallback(
|
|
244
|
-
(_value: string) => {
|
|
245
|
-
// Read from ref, not prop: the prop lags until React re-renders, so holding
|
|
246
|
-
// Enter fires multiple submits with the same stale value. The ref is
|
|
247
|
-
// pre-cleared here so any queued handler calls before re-render bail out.
|
|
248
|
-
const current = inputRef.current.value;
|
|
249
|
-
if (current.trim() && onSubmitRef.current) {
|
|
250
|
-
inputRef.current = { value: '', cursorPosition: 0 }; // guard before any async
|
|
251
|
-
addToHistory(current);
|
|
252
|
-
clearInput(); // queues setInput('') for React
|
|
253
|
-
onSubmitRef.current(current);
|
|
254
|
-
}
|
|
255
|
-
},
|
|
256
|
-
[clearInput, addToHistory]
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
// 处理上下箭
|
|
260
|
-
const handleArrowUpInternal = useCallback(() => {
|
|
261
|
-
const prevCmd = getPreviousCommand();
|
|
262
|
-
if (prevCmd !== null && prevCmd !== undefined) {
|
|
263
|
-
handleChange(prevCmd);
|
|
264
|
-
handleChangeCursorPosition(prevCmd.length);
|
|
265
|
-
}
|
|
266
|
-
}, [handleChange, handleChangeCursorPosition, getPreviousCommand]);
|
|
267
|
-
|
|
268
|
-
const handleArrowDownInternal = useCallback(() => {
|
|
269
|
-
const nextCmd = getNextCommand();
|
|
270
|
-
if (nextCmd !== null && nextCmd !== undefined) {
|
|
271
|
-
handleChange(nextCmd);
|
|
272
|
-
handleChangeCursorPosition(nextCmd.length);
|
|
273
|
-
}
|
|
274
|
-
}, [handleChange, handleChangeCursorPosition, getNextCommand]);
|
|
275
|
-
|
|
276
|
-
// 处理 Tab — 由 CommandSuggestions 接管
|
|
277
|
-
useInput((_char, key) => {
|
|
278
|
-
if (focusManager.getCurrentFocus() !== FocusId.MAIN_INPUT) return;
|
|
279
|
-
// Tab is handled by CommandSuggestions; just swallow it here
|
|
280
|
-
// to prevent default browser-like behavior
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return (
|
|
285
|
-
<Box flexDirection="column">
|
|
286
|
-
{/* Paste notification — Claude Code style: minimal, brief */}
|
|
287
|
-
{pasteNotification && (
|
|
288
|
-
<Box paddingX={0} marginBottom={0}>
|
|
289
|
-
<Text color={theme.colors.text.muted} dimColor>
|
|
290
|
-
{pasteNotification}
|
|
291
|
-
</Text>
|
|
292
|
-
</Box>
|
|
293
|
-
)}
|
|
294
|
-
{/* Thinking indicator — Claude Code style: minimal */}
|
|
295
|
-
<Box paddingX={0} marginBottom={0}>
|
|
296
|
-
{thinkingLabel ? (
|
|
297
|
-
<Text color={theme.colors.text.muted} dimColor>
|
|
298
|
-
{thinkingLabel}
|
|
299
|
-
{pendingCommands.length > 0 && <Text dimColor> · queued: {pendingCommands.length}</Text>}
|
|
300
|
-
</Text>
|
|
301
|
-
) : null}
|
|
302
|
-
</Box>
|
|
303
|
-
|
|
304
|
-
{/* 输入框 — Claude Code style: no border, just prompt + input */}
|
|
305
|
-
<Box
|
|
306
|
-
flexDirection="row"
|
|
307
|
-
paddingX={0}
|
|
308
|
-
paddingY={0}
|
|
309
|
-
>
|
|
310
|
-
{/* Spinner while AI works, □ when idle */}
|
|
311
|
-
<Box marginRight={1}>
|
|
312
|
-
<PromptGlyph
|
|
313
|
-
isProcessing={isProcessing}
|
|
314
|
-
lockedIn={currentCommand !== null}
|
|
315
|
-
glowPhase={glowPhase}
|
|
316
|
-
color={theme.colors.primary}
|
|
317
|
-
idleColor={theme.colors.primary}
|
|
318
|
-
/>
|
|
319
|
-
</Box>
|
|
320
|
-
|
|
321
|
-
{/* 输入框 - 始终启用,支持命令队列 */}
|
|
322
|
-
<Box flexGrow={1}>
|
|
323
|
-
<CustomTextInput
|
|
324
|
-
value={input}
|
|
325
|
-
cursorPosition={cursorPosition}
|
|
326
|
-
onChange={handleChange}
|
|
327
|
-
onChangeCursorPosition={handleChangeCursorPosition}
|
|
328
|
-
onSubmit={handleSubmit}
|
|
329
|
-
onPaste={handlePaste}
|
|
330
|
-
onArrowUp={handleArrowUpInternal}
|
|
331
|
-
onArrowDown={handleArrowDownInternal}
|
|
332
|
-
placeholder={placeholder}
|
|
333
|
-
focusId={FocusId.MAIN_INPUT}
|
|
334
|
-
disabled={false}
|
|
335
|
-
cursorOn={cursorOn}
|
|
336
|
-
/>
|
|
337
|
-
</Box>
|
|
338
|
-
</Box>
|
|
339
|
-
|
|
340
|
-
{/* Command suggestions — below the input bar */}
|
|
341
|
-
{showSuggestions && (
|
|
342
|
-
<CommandSuggestions
|
|
343
|
-
input={input}
|
|
344
|
-
cursorPosition={cursorPosition}
|
|
345
|
-
onSelectSuggestion={handleSelectSuggestion}
|
|
346
|
-
visible={showSuggestions}
|
|
347
|
-
/>
|
|
348
|
-
)}
|
|
349
|
-
|
|
350
|
-
{/* Prompt suggestions — disabled */}
|
|
351
|
-
</Box>
|
|
352
|
-
);
|
|
353
|
-
},
|
|
354
|
-
// 自定义比较函数:始终返回 true,因为回调通过 ref 访
|
|
355
|
-
// 这样 props 变化不会触发重新渲
|
|
356
|
-
() => false
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
InputArea.displayName = 'InputArea';
|
|
360
|
-
|
|
361
|
-
export default InputArea;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Box, Text, useInput } from 'ink';
|
|
3
|
-
import { themeManager } from '../../themes/index.js';
|
|
4
|
-
import { FocusId, focusManager } from '../../focus/index.js';
|
|
5
|
-
|
|
6
|
-
const SUGGESTIONS = [
|
|
7
|
-
{ label: 'Explain codebase', text: 'Explain this codebase — what does it do and how is it structured?' },
|
|
8
|
-
{ label: 'Fix a bug', text: 'Find and fix the bug: ' },
|
|
9
|
-
{ label: 'Write tests', text: 'Write tests for ' },
|
|
10
|
-
{ label: 'Refactor', text: 'Refactor this to be cleaner and more maintainable: ' },
|
|
11
|
-
{ label: 'Review changes', text: 'Review my recent git changes and flag any issues' },
|
|
12
|
-
{ label: 'Add feature', text: 'Implement a new feature: ' },
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
interface PromptSuggestionsProps {
|
|
16
|
-
onSelect: (text: string) => void;
|
|
17
|
-
visible: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const PromptSuggestions: React.FC<PromptSuggestionsProps> = ({ onSelect, visible }) => {
|
|
21
|
-
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
|
22
|
-
const theme = themeManager.getTheme();
|
|
23
|
-
|
|
24
|
-
useInput(
|
|
25
|
-
(_, key) => {
|
|
26
|
-
if (!visible) return;
|
|
27
|
-
if (focusManager.getCurrentFocus() !== FocusId.MAIN_INPUT) return;
|
|
28
|
-
|
|
29
|
-
if (key.tab && !key.shift) {
|
|
30
|
-
setSelectedIndex(i => (i + 1) % SUGGESTIONS.length);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (key.tab && key.shift) {
|
|
34
|
-
setSelectedIndex(i => (i <= 0 ? SUGGESTIONS.length - 1 : i - 1));
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (key.return) {
|
|
38
|
-
onSelect(SUGGESTIONS[selectedIndex].text);
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
{ isActive: visible }
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
if (!visible) return null;
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<Box flexDirection="row" paddingX={2} marginTop={0} flexWrap="wrap">
|
|
48
|
-
{SUGGESTIONS.map((s, i) => {
|
|
49
|
-
const isSelected = i === selectedIndex;
|
|
50
|
-
return (
|
|
51
|
-
<Box key={s.label} marginRight={2}>
|
|
52
|
-
<Text
|
|
53
|
-
color={isSelected ? theme.colors.primary : theme.colors.text.muted}
|
|
54
|
-
bold={isSelected}
|
|
55
|
-
dimColor={!isSelected}
|
|
56
|
-
>
|
|
57
|
-
{isSelected ? '▸ ' : ' '}{s.label}
|
|
58
|
-
</Text>
|
|
59
|
-
</Box>
|
|
60
|
-
);
|
|
61
|
-
})}
|
|
62
|
-
</Box>
|
|
63
|
-
);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export default PromptSuggestions;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ChatStatusBar — Claude Code style minimal status bar
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import React from 'react';
|
|
6
|
-
import { Box, Text } from 'ink';
|
|
7
|
-
import { useShallow } from 'zustand/react/shallow';
|
|
8
|
-
import { themeManager } from '../../themes/index.js';
|
|
9
|
-
import { useClawdStore } from '../../../store/index.js';
|
|
10
|
-
|
|
11
|
-
interface ChatStatusBarProps {
|
|
12
|
-
model?: string;
|
|
13
|
-
/** True when `model` was picked by the auto-router rather than /model */
|
|
14
|
-
modelIsAuto?: boolean;
|
|
15
|
-
isVisible?: boolean;
|
|
16
|
-
isScrolledUp?: boolean;
|
|
17
|
-
renderLatency?: number;
|
|
18
|
-
routerEnabled?: boolean;
|
|
19
|
-
onToggleRouter?: () => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function formatTokens(count: number): string {
|
|
23
|
-
if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;
|
|
24
|
-
if (count >= 1000) return `${(count / 1000).toFixed(1)}k`;
|
|
25
|
-
return String(count);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const ChatStatusBar: React.FC<ChatStatusBarProps> = React.memo(({
|
|
29
|
-
model,
|
|
30
|
-
modelIsAuto = false,
|
|
31
|
-
isVisible = true,
|
|
32
|
-
isScrolledUp = false,
|
|
33
|
-
renderLatency = 0,
|
|
34
|
-
routerEnabled = false,
|
|
35
|
-
onToggleRouter,
|
|
36
|
-
}) => {
|
|
37
|
-
const theme = themeManager.getTheme();
|
|
38
|
-
const displayModel = model;
|
|
39
|
-
|
|
40
|
-
const { messageCount, queuedCommands } = useClawdStore(
|
|
41
|
-
useShallow((state) => ({
|
|
42
|
-
messageCount: state.session.messages.length,
|
|
43
|
-
queuedCommands: state.command.pendingCommands.length,
|
|
44
|
-
}))
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
if (!isVisible) return null;
|
|
48
|
-
|
|
49
|
-
const items: string[] = [];
|
|
50
|
-
|
|
51
|
-
if (displayModel) {
|
|
52
|
-
const truncated = displayModel.length > 24 ? displayModel.slice(0, 24) + '…' : displayModel;
|
|
53
|
-
items.push(modelIsAuto ? `${truncated} (auto)` : truncated);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (renderLatency > 50) {
|
|
57
|
-
items.push(`lag: ${renderLatency}ms`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (messageCount !== undefined) {
|
|
61
|
-
items.push(`${messageCount} msgs`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (isScrolledUp) {
|
|
65
|
-
items.push('↑ scrolled');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (queuedCommands > 0) {
|
|
69
|
-
items.push(`queue: ${queuedCommands}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (items.length === 0) return null;
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<Box flexDirection="row" paddingX={0} marginTop={0}>
|
|
76
|
-
<Text color={theme.colors.text.muted} dimColor>
|
|
77
|
-
{items.join(' · ')}
|
|
78
|
-
<Text> · </Text>
|
|
79
|
-
<Text color={routerEnabled ? theme.colors.success : theme.colors.text.muted} bold={routerEnabled}>
|
|
80
|
-
R:{routerEnabled ? 'ON' : 'OFF'}
|
|
81
|
-
</Text>
|
|
82
|
-
<Text dimColor> alt+r</Text>
|
|
83
|
-
</Text>
|
|
84
|
-
</Box>
|
|
85
|
-
);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
ChatStatusBar.displayName = 'ChatStatusBar';
|
|
89
|
-
|
|
90
|
-
export default ChatStatusBar;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ContextBar — shows current workflow phase with step progress
|
|
3
|
-
* Inspired by VSCode / Claude Code context bars
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { Box, Text } from 'ink';
|
|
8
|
-
import { themeManager } from '../../themes/index.js';
|
|
9
|
-
import type { WorkflowState } from '../../../store/types.js';
|
|
10
|
-
|
|
11
|
-
const PHASE_COLORS: Record<string, string> = {
|
|
12
|
-
building: '#22c55e', // green
|
|
13
|
-
refactoring:'#f59e0b', // amber
|
|
14
|
-
debugging: '#ef4444', // red
|
|
15
|
-
testing: '#3b82f6', // blue
|
|
16
|
-
planning: '#7c6fd4', // purple
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function getPhaseColor(phase: string): string {
|
|
20
|
-
const key = phase.toLowerCase().trim();
|
|
21
|
-
for (const [k, v] of Object.entries(PHASE_COLORS)) {
|
|
22
|
-
if (key.includes(k) || k.includes(key)) return v;
|
|
23
|
-
}
|
|
24
|
-
return themeManager.getTheme().colors.primary;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface ContextBarProps {
|
|
28
|
-
workflow: WorkflowState;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const ContextBar: React.FC<ContextBarProps> = React.memo(({ workflow }) => {
|
|
32
|
-
const theme = themeManager.getTheme();
|
|
33
|
-
|
|
34
|
-
if (!workflow.visible) return null;
|
|
35
|
-
|
|
36
|
-
const phaseColor = getPhaseColor(workflow.phase);
|
|
37
|
-
const indicator = `● ${workflow.phase} ${workflow.target}`;
|
|
38
|
-
const progress = `${workflow.currentStepIndex}/${workflow.totalSteps}`;
|
|
39
|
-
|
|
40
|
-
// Step indicators: ● active ◉ done ○ pending
|
|
41
|
-
const stepIndicators = workflow.steps.map((s, i) => {
|
|
42
|
-
const dot = s.status === 'active' ? '◉' : s.status === 'done' ? '●' : '○';
|
|
43
|
-
const color = s.status === 'active' ? phaseColor
|
|
44
|
-
: s.status === 'done' ? theme.colors.success
|
|
45
|
-
: theme.colors.text.muted;
|
|
46
|
-
return (
|
|
47
|
-
<Text key={i} color={color}>
|
|
48
|
-
{dot} {s.label}
|
|
49
|
-
{i < workflow.steps.length - 1 && (
|
|
50
|
-
<Text color={theme.colors.text.muted}> · </Text>
|
|
51
|
-
)}
|
|
52
|
-
</Text>
|
|
53
|
-
);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Separator line
|
|
57
|
-
const sepColor = theme.colors.border.light;
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<Box flexDirection="column" marginTop={0} marginBottom={0}>
|
|
61
|
-
<Box>
|
|
62
|
-
<Text color={sepColor}>─</Text>
|
|
63
|
-
<Text> </Text>
|
|
64
|
-
<Text color={phaseColor} bold>{indicator}</Text>
|
|
65
|
-
<Text color={theme.colors.text.muted}> · {progress}</Text>
|
|
66
|
-
</Box>
|
|
67
|
-
<Box marginLeft={1}>
|
|
68
|
-
{stepIndicators}
|
|
69
|
-
</Box>
|
|
70
|
-
<Box>
|
|
71
|
-
<Text color={sepColor}>─</Text>
|
|
72
|
-
</Box>
|
|
73
|
-
</Box>
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
ContextBar.displayName = 'ContextBar';
|
|
78
|
-
|
|
79
|
-
export default ContextBar;
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MessageArea - 消息列表区域
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React from 'react';
|
|
8
|
-
import { Box, Text } from 'ink';
|
|
9
|
-
import { MessageRenderer } from '../markdown/MessageRenderer.js';
|
|
10
|
-
import { useTerminalWidth } from '../../hooks/index.js';
|
|
11
|
-
import { themeManager } from '../../themes/index.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* UI 消息类型
|
|
15
|
-
*/
|
|
16
|
-
export interface UIMessage {
|
|
17
|
-
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
18
|
-
content: string;
|
|
19
|
-
timestamp?: number;
|
|
20
|
-
metadata?: {
|
|
21
|
-
model?: string;
|
|
22
|
-
tokenUsage?: { input: number; output: number };
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface MessageAreaProps {
|
|
27
|
-
/** 消息列表 */
|
|
28
|
-
messages: UIMessage[];
|
|
29
|
-
/** 最大显示消息数 */
|
|
30
|
-
maxMessages?: number;
|
|
31
|
-
/** 是否显示时间戳 */
|
|
32
|
-
showTimestamp?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
37
|
-
*/
|
|
38
|
-
function formatTimestamp(timestamp: number): string {
|
|
39
|
-
const date = new Date(timestamp);
|
|
40
|
-
return date.toLocaleTimeString('en-US', {
|
|
41
|
-
hour: '2-digit',
|
|
42
|
-
minute: '2-digit',
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
*
|
|
48
|
-
*/
|
|
49
|
-
export const MessageArea: React.FC<MessageAreaProps> = ({
|
|
50
|
-
messages,
|
|
51
|
-
maxMessages = 50,
|
|
52
|
-
showTimestamp = false,
|
|
53
|
-
}) => {
|
|
54
|
-
const terminalWidth = useTerminalWidth();
|
|
55
|
-
const theme = themeManager.getTheme();
|
|
56
|
-
|
|
57
|
-
// 限制显示的消息数
|
|
58
|
-
const displayMessages = messages.slice(-maxMessages);
|
|
59
|
-
|
|
60
|
-
if (displayMessages.length === 0) {
|
|
61
|
-
return (
|
|
62
|
-
<Box flexDirection="column" padding={1}>
|
|
63
|
-
<Text color={theme.colors.text.muted} dimColor>
|
|
64
|
-
No messages yet. Start a conversation!
|
|
65
|
-
</Text>
|
|
66
|
-
</Box>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<Box flexDirection="column" paddingX={1}>
|
|
72
|
-
{displayMessages.map((message, index) => (
|
|
73
|
-
<Box key={index} flexDirection="column" marginBottom={1}>
|
|
74
|
-
{/* 时间戳 */}
|
|
75
|
-
{showTimestamp && message.timestamp && (
|
|
76
|
-
<Box marginBottom={0}>
|
|
77
|
-
<Text color={theme.colors.text.muted} dimColor>
|
|
78
|
-
{formatTimestamp(message.timestamp)}
|
|
79
|
-
</Text>
|
|
80
|
-
</Box>
|
|
81
|
-
)}
|
|
82
|
-
|
|
83
|
-
{/* 消息内容 */}
|
|
84
|
-
<MessageRenderer
|
|
85
|
-
content={message.content}
|
|
86
|
-
role={message.role}
|
|
87
|
-
terminalWidth={terminalWidth - 2}
|
|
88
|
-
showPrefix={true}
|
|
89
|
-
/>
|
|
90
|
-
</Box>
|
|
91
|
-
))}
|
|
92
|
-
</Box>
|
|
93
|
-
);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export default MessageArea;
|