@@ -18,6 +18,7 @@ import {
1818 type ThemeMode ,
1919} from "./theme" ;
2020import { truncateText } from "./format" ;
21+ import { generateUUID } from "./uuid" ;
2122import {
2223 startThemeTransition ,
2324 type ThemeTransitionContext ,
@@ -40,6 +41,7 @@ import type {
4041import {
4142 defaultDiscordActions ,
4243 defaultSlackActions ,
44+ type ChatQueueItem ,
4345 type CronFormState ,
4446 type DiscordForm ,
4547 type IMessageForm ,
@@ -49,7 +51,7 @@ import {
4951} from "./ui-types" ;
5052import {
5153 loadChatHistory ,
52- sendChat ,
54+ sendChatMessage ,
5355 handleChatEvent ,
5456 type ChatEventPayload ,
5557} from "./controllers/chat" ;
@@ -214,6 +216,7 @@ export class ClawdbotApp extends LitElement {
214216 @state ( ) chatStreamStartedAt : number | null = null ;
215217 @state ( ) chatRunId : string | null = null ;
216218 @state ( ) chatThinkingLevel : string | null = null ;
219+ @state ( ) chatQueue : ChatQueueItem [ ] = [ ] ;
217220 @state ( ) toolOutputExpanded = new Set < string > ( ) ;
218221
219222 @state ( ) nodesLoading = false ;
@@ -761,6 +764,7 @@ export class ClawdbotApp extends LitElement {
761764 const state = handleChatEvent ( this , payload ) ;
762765 if ( state === "final" || state === "error" || state === "aborted" ) {
763766 this . resetToolStream ( ) ;
767+ void this . flushChatQueue ( ) ;
764768 }
765769 if ( state === "final" ) void loadChatHistory ( this ) ;
766770 return ;
@@ -1003,19 +1007,32 @@ export class ClawdbotApp extends LitElement {
10031007 async loadCron ( ) {
10041008 await Promise . all ( [ loadCronStatus ( this ) , loadCronJobs ( this ) ] ) ;
10051009 }
1006- async handleSendChat (
1007- messageOverride ?: string ,
1008- opts ?: { restoreDraft ?: boolean } ,
1010+
1011+ private isChatBusy ( ) {
1012+ return this . chatSending || Boolean ( this . chatRunId ) ;
1013+ }
1014+
1015+ private enqueueChatMessage ( text : string ) {
1016+ const trimmed = text . trim ( ) ;
1017+ if ( ! trimmed ) return ;
1018+ this . chatQueue = [
1019+ ...this . chatQueue ,
1020+ {
1021+ id : generateUUID ( ) ,
1022+ text : trimmed ,
1023+ createdAt : Date . now ( ) ,
1024+ } ,
1025+ ] ;
1026+ }
1027+
1028+ private async sendChatMessageNow (
1029+ message : string ,
1030+ opts ?: { previousDraft ?: string ; restoreDraft ?: boolean } ,
10091031 ) {
1010- if ( ! this . connected ) return ;
1011- const previousDraft = this . chatMessage ;
1012- if ( messageOverride != null ) {
1013- this . chatMessage = messageOverride ;
1014- }
10151032 this . resetToolStream ( ) ;
1016- const ok = await sendChat ( this ) ;
1017- if ( ! ok && messageOverride != null ) {
1018- this . chatMessage = previousDraft ;
1033+ const ok = await sendChatMessage ( this , message ) ;
1034+ if ( ! ok && opts ?. previousDraft != null ) {
1035+ this . chatMessage = opts . previousDraft ;
10191036 }
10201037 if ( ok ) {
10211038 this . setLastActiveSessionKey ( this . sessionKey ) ;
@@ -1028,10 +1045,53 @@ export class ClawdbotApp extends LitElement {
10281045 this . resetToolStream ( ) ;
10291046 void loadChatHistory ( this ) ;
10301047 }
1031- if ( ok && messageOverride != null && opts ?. restoreDraft && previousDraft . trim ( ) ) {
1032- this . chatMessage = previousDraft ;
1048+ if ( ok && opts ?. restoreDraft && opts . previousDraft ? .trim ( ) ) {
1049+ this . chatMessage = opts . previousDraft ;
10331050 }
10341051 this . scheduleChatScroll ( ) ;
1052+ if ( ok && ! this . chatRunId ) {
1053+ void this . flushChatQueue ( ) ;
1054+ }
1055+ return ok ;
1056+ }
1057+
1058+ private async flushChatQueue ( ) {
1059+ if ( ! this . connected || this . isChatBusy ( ) ) return ;
1060+ const [ next , ...rest ] = this . chatQueue ;
1061+ if ( ! next ) return ;
1062+ this . chatQueue = rest ;
1063+ const ok = await this . sendChatMessageNow ( next . text ) ;
1064+ if ( ! ok ) {
1065+ this . chatQueue = [ next , ...this . chatQueue ] ;
1066+ }
1067+ }
1068+
1069+ removeQueuedMessage ( id : string ) {
1070+ this . chatQueue = this . chatQueue . filter ( ( item ) => item . id !== id ) ;
1071+ }
1072+
1073+ async handleSendChat (
1074+ messageOverride ?: string ,
1075+ opts ?: { restoreDraft ?: boolean } ,
1076+ ) {
1077+ if ( ! this . connected ) return ;
1078+ const previousDraft = this . chatMessage ;
1079+ const message = ( messageOverride ?? this . chatMessage ) . trim ( ) ;
1080+ if ( ! message ) return ;
1081+
1082+ if ( messageOverride == null ) {
1083+ this . chatMessage = "" ;
1084+ }
1085+
1086+ if ( this . isChatBusy ( ) ) {
1087+ this . enqueueChatMessage ( message ) ;
1088+ return ;
1089+ }
1090+
1091+ await this . sendChatMessageNow ( message , {
1092+ previousDraft : messageOverride == null ? previousDraft : undefined ,
1093+ restoreDraft : Boolean ( messageOverride && opts ?. restoreDraft ) ,
1094+ } ) ;
10351095 }
10361096
10371097 async handleWhatsAppStart ( force : boolean ) {
0 commit comments