11import { formatDurationMmSs } from '../../../core/time/date' ;
2+ import type { StreamChunk } from '../../../core/types' ;
23import { FLAVOR_TEXTS } from '../flavor-texts' ;
34import type { ChatState } from '../state/chat-state' ;
45
@@ -8,10 +9,21 @@ interface ThinkingIndicatorControllerDeps {
89 updateQueueIndicator : ( ) => void ;
910}
1011
12+ type ModelQueueChunk = Extract < StreamChunk , { type : 'model_queue' } > ;
13+
1114const SHOW_DELAY_MS = 400 ;
1215
16+ function buildQueueLabel ( chunk : ModelQueueChunk ) : string {
17+ const parts : string [ ] = [ ] ;
18+ if ( chunk . queueCount && chunk . queueCount > 0 ) parts . push ( `${ chunk . queueCount } ahead` ) ;
19+ if ( chunk . waitTimeMs && chunk . waitTimeMs > 0 ) parts . push ( `~${ Math . ceil ( chunk . waitTimeMs / 1000 ) } s wait` ) ;
20+ return parts . length > 0 ? `Model is queued (${ parts . join ( ' · ' ) } )...` : 'Model is queued...' ;
21+ }
22+
1323/** Owns the delayed flavor-text indicator and its elapsed-time timer. */
1424export class ThinkingIndicatorController {
25+ private queueLabel : string | null = null ;
26+
1527 constructor ( private readonly deps : ThinkingIndicatorControllerDeps ) { }
1628
1729 show ( overrideText ?: string , overrideCls ?: string ) : void {
@@ -35,10 +47,12 @@ export class ThinkingIndicatorController {
3547 state . setThinkingIndicatorTimeout ( null , null ) ;
3648 if ( ! state . currentContentEl || state . thinkingEl || state . currentThinkingState ) return ;
3749
38- const cls = overrideCls ? `qoderian-thinking ${ overrideCls } ` : 'qoderian-thinking' ;
50+ const queueActive = this . queueLabel !== null ;
51+ const queueCls = queueActive ? ' qoderian-thinking--queue' : '' ;
52+ const cls = `qoderian-thinking${ overrideCls ? ` ${ overrideCls } ` : queueCls } ` ;
3953 state . thinkingEl = state . currentContentEl . createDiv ( { cls } ) ;
40- const text = overrideText || FLAVOR_TEXTS [ Math . floor ( Math . random ( ) * FLAVOR_TEXTS . length ) ] ;
41- state . thinkingEl . createSpan ( { text } ) ;
54+ const text = overrideText || this . queueLabel || FLAVOR_TEXTS [ Math . floor ( Math . random ( ) * FLAVOR_TEXTS . length ) ] ;
55+ state . thinkingEl . createSpan ( { cls : 'qoderian-thinking-text' , text } ) ;
4256
4357 const timerSpan = state . thinkingEl . createSpan ( { cls : 'qoderian-thinking-hint' } ) ;
4458 const updateTimer = ( ) => {
@@ -64,8 +78,32 @@ export class ThinkingIndicatorController {
6478 } , SHOW_DELAY_MS ) , timerWindow ) ;
6579 }
6680
81+ /** Reflects CLI model-capacity queue progress onto the waiting indicator. */
82+ setQueue ( chunk : ModelQueueChunk ) : void {
83+ const { state } = this . deps ;
84+ if ( chunk . status === 'ready' ) {
85+ if ( this . queueLabel === null ) return ;
86+ this . queueLabel = null ;
87+ if ( state . thinkingEl ) {
88+ state . thinkingEl . removeClass ( 'qoderian-thinking--queue' ) ;
89+ const label = state . thinkingEl . querySelector < HTMLElement > ( '.qoderian-thinking-text' ) ;
90+ label ?. setText ( FLAVOR_TEXTS [ Math . floor ( Math . random ( ) * FLAVOR_TEXTS . length ) ] ) ;
91+ }
92+ return ;
93+ }
94+
95+ this . queueLabel = buildQueueLabel ( chunk ) ;
96+ if ( state . thinkingEl ) {
97+ state . thinkingEl . addClass ( 'qoderian-thinking--queue' ) ;
98+ state . thinkingEl . querySelector < HTMLElement > ( '.qoderian-thinking-text' ) ?. setText ( this . queueLabel ) ;
99+ return ;
100+ }
101+ this . show ( this . queueLabel , 'qoderian-thinking--queue' ) ;
102+ }
103+
67104 hide ( ) : void {
68105 const { state } = this . deps ;
106+ this . queueLabel = null ;
69107 if ( state . thinkingIndicatorTimeout ) {
70108 const activeWindow = this . deps . getMessagesEl ( ) . ownerDocument . defaultView ?? window ;
71109 state . clearThinkingIndicatorTimeout ( activeWindow ) ;
0 commit comments