@@ -27,10 +27,16 @@ const InputControls = styled.div`
2727 align-items: flex-end;
2828` ;
2929
30- const InputField = styled . textarea < { isEditing ?: boolean } > `
30+ const InputField = styled . textarea < { isEditing ?: boolean ; canInterrupt ?: boolean } > `
3131 flex: 1;
3232 background: ${ ( props ) => ( props . isEditing ? "var(--color-editing-mode-alpha)" : "#1e1e1e" ) } ;
33- border: 1px solid ${ ( props ) => ( props . isEditing ? "var(--color-editing-mode)" : "#3e3e42" ) } ;
33+ border: 1px solid
34+ ${ ( props ) =>
35+ props . isEditing
36+ ? "var(--color-editing-mode)"
37+ : props . canInterrupt
38+ ? "var(--color-interrupted)"
39+ : "#3e3e42" } ;
3440 color: #d4d4d4;
3541 padding: 8px 12px;
3642 border-radius: 4px;
@@ -44,7 +50,12 @@ const InputField = styled.textarea<{ isEditing?: boolean }>`
4450
4551 &:focus {
4652 outline: none;
47- border-color: ${ ( props ) => ( props . isEditing ? "var(--color-editing-mode)" : "#569cd6" ) } ;
53+ border-color: ${ ( props ) =>
54+ props . isEditing
55+ ? "var(--color-editing-mode)"
56+ : props . canInterrupt
57+ ? "var(--color-interrupted)"
58+ : "#569cd6" } ;
4859 }
4960
5061 &::placeholder {
@@ -101,6 +112,7 @@ export interface ChatInputProps {
101112 isCompacting ?: boolean ;
102113 editingMessage ?: { id : string ; content : string } ;
103114 onCancelEdit ?: ( ) => void ;
115+ canInterrupt ?: boolean ; // Whether Esc can be used to interrupt streaming
104116}
105117
106118// Helper function to convert parsed command to display toast
@@ -245,6 +257,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
245257 isCompacting = false ,
246258 editingMessage,
247259 onCancelEdit,
260+ canInterrupt = false ,
248261} ) => {
249262 const [ input , setInput ] = usePersistedState ( "input:" + workspaceId , "" ) ;
250263 const [ isSending , setIsSending ] = useState ( false ) ;
@@ -373,12 +386,10 @@ export const ChatInput: React.FC<ChatInputProps> = ({
373386 setIsSending ( true ) ;
374387
375388 try {
376- const result = await window . api . workspace . sendMessage (
377- workspaceId ,
378- messageText ,
379- editingMessage ?. id ,
380- thinkingLevel
381- ) ;
389+ const result = await window . api . workspace . sendMessage ( workspaceId , messageText , {
390+ editMessageId : editingMessage ?. id ,
391+ thinkingLevel,
392+ } ) ;
382393
383394 if ( ! result . success ) {
384395 // Log error for debugging
@@ -422,10 +433,23 @@ export const ChatInput: React.FC<ChatInputProps> = ({
422433 } ;
423434
424435 const handleKeyDown = ( e : React . KeyboardEvent ) => {
425- // Handle Escape key to cancel editing
426- if ( e . key === "Escape" && editingMessage && onCancelEdit ) {
436+ // Handle Escape key
437+ if ( e . key === "Escape" ) {
427438 e . preventDefault ( ) ;
428- onCancelEdit ( ) ;
439+
440+ // Priority 1: Cancel editing if in edit mode
441+ if ( editingMessage && onCancelEdit ) {
442+ onCancelEdit ( ) ;
443+ return ;
444+ }
445+
446+ // Priority 2: Interrupt streaming if active
447+ if ( canInterrupt ) {
448+ // Send empty message to trigger interrupt
449+ void window . api . workspace . sendMessage ( workspaceId , "" ) ;
450+ return ;
451+ }
452+
429453 return ;
430454 }
431455
@@ -479,9 +503,12 @@ export const ChatInput: React.FC<ChatInputProps> = ({
479503 ? "Edit your message... (Esc to cancel, Enter to send)"
480504 : isCompacting
481505 ? "Compacting conversation..."
482- : "Type a message... (Enter to send, Shift+Enter for newline)"
506+ : canInterrupt
507+ ? "Type a message... (Esc to interrupt, Enter to send, Shift+Enter for newline)"
508+ : "Type a message... (Enter to send, Shift+Enter for newline)"
483509 }
484510 disabled = { disabled || isSending || isCompacting }
511+ canInterrupt = { canInterrupt }
485512 />
486513 </ InputControls >
487514 < ModeToggles >
0 commit comments