@@ -10,12 +10,15 @@ import {
10
10
CornerDownLeftIcon ,
11
11
DeleteIcon ,
12
12
SendIcon ,
13
+ Square ,
13
14
} from "lucide-react" ;
14
15
import { Tabs , TabsList , TabsTrigger } from "./ui/tabs" ;
16
+ import type { ServerStatus } from "./chat-provider" ;
15
17
16
18
interface MessageInputProps {
17
19
onSendMessage : ( message : string , type : "user" | "raw" ) => void ;
18
20
disabled ?: boolean ;
21
+ serverStatus : ServerStatus ;
19
22
}
20
23
21
24
interface SentChar {
@@ -24,9 +27,27 @@ interface SentChar {
24
27
timestamp : number ;
25
28
}
26
29
30
+ // List of keys to send as raw input when in control mode
31
+
32
+ const specialKeys : Record < string , string > = {
33
+ ArrowUp : "\x1b[A" , // Escape sequence for up arrow
34
+ ArrowDown : "\x1b[B" , // Escape sequence for down arrow
35
+ ArrowRight : "\x1b[C" , // Escape sequence for right arrow
36
+ ArrowLeft : "\x1b[D" , // Escape sequence for left arrow
37
+ Escape : "\x1b" , // Escape key
38
+ Tab : "\t" , // Tab key
39
+ Delete : "\x1b[3~" , // Delete key
40
+ Home : "\x1b[H" , // Home key
41
+ End : "\x1b[F" , // End key
42
+ PageUp : "\x1b[5~" , // Page Up
43
+ PageDown : "\x1b[6~" , // Page Down
44
+ Backspace : "\b" , // Backspace key
45
+ } ;
46
+
27
47
export default function MessageInput ( {
28
48
onSendMessage,
29
49
disabled = false ,
50
+ serverStatus,
30
51
} : MessageInputProps ) {
31
52
const [ message , setMessage ] = useState ( "" ) ;
32
53
const [ inputMode , setInputMode ] = useState ( "text" ) ;
@@ -69,22 +90,6 @@ export default function MessageInput({
69
90
const handleKeyDown = ( e : KeyboardEvent < HTMLTextAreaElement > ) => {
70
91
// In control mode, send special keys as raw messages
71
92
if ( inputMode === "control" && ! disabled ) {
72
- // List of keys to send as raw input when in control mode
73
- const specialKeys : Record < string , string > = {
74
- ArrowUp : "\x1b[A" , // Escape sequence for up arrow
75
- ArrowDown : "\x1b[B" , // Escape sequence for down arrow
76
- ArrowRight : "\x1b[C" , // Escape sequence for right arrow
77
- ArrowLeft : "\x1b[D" , // Escape sequence for left arrow
78
- Escape : "\x1b" , // Escape key
79
- Tab : "\t" , // Tab key
80
- Delete : "\x1b[3~" , // Delete key
81
- Home : "\x1b[H" , // Home key
82
- End : "\x1b[F" , // End key
83
- PageUp : "\x1b[5~" , // Page Up
84
- PageDown : "\x1b[6~" , // Page Down
85
- Backspace : "\b" , // Backspace key
86
- } ;
87
-
88
93
// Check if the pressed key is in our special keys map
89
94
if ( specialKeys [ e . key ] ) {
90
95
e . preventDefault ( ) ;
@@ -168,8 +173,13 @@ export default function MessageInput({
168
173
value = { message }
169
174
onChange = { ( e ) => setMessage ( e . target . value ) }
170
175
onKeyDown = { handleKeyDown }
171
- placeholder = { "Type a message..." }
176
+ placeholder = {
177
+ serverStatus === "running"
178
+ ? "Running..."
179
+ : "Type a message..."
180
+ }
172
181
className = "resize-none w-full text-sm outline-none p-4 h-20"
182
+ disabled = { serverStatus !== "stable" }
173
183
/>
174
184
) }
175
185
</ div >
@@ -194,7 +204,7 @@ export default function MessageInput({
194
204
</ TabsTrigger >
195
205
</ TabsList >
196
206
197
- { inputMode === "text" && (
207
+ { inputMode === "text" && serverStatus !== "running" && (
198
208
< Button
199
209
type = "submit"
200
210
disabled = { disabled || ! message . trim ( ) }
@@ -206,6 +216,20 @@ export default function MessageInput({
206
216
</ Button >
207
217
) }
208
218
219
+ { inputMode === "text" && serverStatus === "running" && (
220
+ < Button
221
+ size = "icon"
222
+ className = "rounded-full"
223
+ disabled = { disabled }
224
+ onClick = { ( ) => {
225
+ onSendMessage ( specialKeys . Escape , "raw" ) ;
226
+ } }
227
+ >
228
+ < Square />
229
+ < span className = "sr-only" > Stop</ span >
230
+ </ Button >
231
+ ) }
232
+
209
233
{ inputMode === "control" && ! disabled && (
210
234
< div className = "flex items-center gap-1" >
211
235
{ sentChars . map ( ( char ) => (
0 commit comments