103103 .artifact { display : inline-flex; align-items : center; gap : 6px ; background : var (--panel ); border : 1px solid var (--line ); border-radius : 999px ; padding : 5px 14px ; font-size : 13px ; text-decoration : none; color : var (--fg ); box-shadow : var (--shadow ); }
104104 .artifact : hover { border-color : var (--accent ); color : var (--accent ); }
105105 .artifact .size { color : var (--muted ); font-size : 11px ; }
106+ # runStatus { margin : 10px 0 0 ; font-size : 13px ; color : var (--muted ); display : flex; align-items : center; gap : 8px ; }
107+ # runStatus .spin { display : inline-block; animation : spin 1s linear infinite; }
108+ @keyframes spin { to { transform : rotate (360deg ); } }
109+ # todoPanel { margin : 10px 0 0 ; background : var (--panel ); border : 1px solid var (--line ); border-radius : 12px ; box-shadow : var (--shadow ); padding : 10px 14px ; }
110+ # todoPanel .t-title { font-size : 12px ; color : var (--muted ); margin-bottom : 6px ; font-weight : 600 ; }
111+ # todoPanel .t-item { font-size : 13px ; padding : 2px 0 ; display : flex; gap : 8px ; align-items : baseline; }
112+ # todoPanel .t-item .done { color : var (--muted ); text-decoration : line-through; }
113+ # todoPanel .t-item .doing { color : var (--accent ); font-weight : 600 ; }
106114 html [data-theme = "dark" ] .msg pre { background : rgba (255 , 255 , 255 , .06 ); }
107115
108116 /* ---------- auth ---------- */
@@ -172,6 +180,8 @@ <h1>一句话处理 Excel 表格</h1>
172180 </ div >
173181 </ div >
174182 < div id ="chat ">
183+ < div id ="runStatus " style ="display:none "> </ div >
184+ < div id ="todoPanel " style ="display:none "> </ div >
175185 < div id ="messages "> </ div >
176186 < div id ="artifacts " style ="display:none ">
177187 < div style ="font-size:13px;color:var(--muted);margin:8px 0 6px "> 📦 生成的文件(点击下载)</ div >
@@ -297,6 +307,8 @@ <h1>一句话处理 Excel 表格</h1>
297307 if ( es ) { es . close ( ) ; es = null ; }
298308 pendingFiles = [ ] ;
299309 renderFiles ( ) ;
310+ setRunStatus ( '' , false ) ;
311+ renderTodos ( null ) ;
300312 $ ( 'artifacts' ) . style . display = 'none' ;
301313 $ ( 'promptBox' ) . value = '' ; $ ( 'charCount' ) . textContent = '0' ;
302314 $ ( 'home' ) . style . display = 'block' ; $ ( 'chat' ) . style . display = 'none' ;
@@ -312,6 +324,8 @@ <h1>一句话处理 Excel 表格</h1>
312324 currentConv = id ;
313325 if ( es ) { es . close ( ) ; es = null ; }
314326 currentRun = null ;
327+ setRunStatus ( '' , false ) ;
328+ renderTodos ( null ) ;
315329 const res = await api . req ( '/conversations/' + id ) ;
316330 const detail = await res . json ( ) ;
317331 pendingFiles = detail . files || [ ] ;
@@ -446,9 +460,79 @@ <h1>一句话处理 Excel 表格</h1>
446460 return frag ;
447461}
448462
463+ function addAsk ( data , runId ) {
464+ const div = document . createElement ( 'div' ) ;
465+ div . className = 'msg agent' ;
466+ const questions = ( data && data . questions ) || [ ] ;
467+ div . innerHTML = '<div class="who">AI 提问</div>' ;
468+ for ( const q of questions ) {
469+ const qtext = q . question || q . text || JSON . stringify ( q ) ;
470+ const opts = q . options || [ ] ;
471+ const row = document . createElement ( 'div' ) ;
472+ row . style . margin = '8px 0' ;
473+ row . innerHTML = `<div>${ escapeHtml ( qtext ) } </div>` ;
474+ const input = document . createElement ( 'input' ) ;
475+ input . style . cssText = 'width:100%;margin-top:6px;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:8px;color:var(--fg)' ;
476+ input . placeholder = opts . length ? '选择或输入…' : '输入回答…' ;
477+ row . appendChild ( input ) ;
478+ div . appendChild ( row ) ;
479+ if ( opts . length ) {
480+ for ( const o of opts ) {
481+ const label = typeof o === 'string' ? o : ( o . label || JSON . stringify ( o ) ) ;
482+ const b = document . createElement ( 'button' ) ;
483+ b . className = 'attach-btn' ; b . style . margin = '0 6px 6px 0' ;
484+ b . textContent = label ;
485+ b . onclick = ( ) => { input . value = label ; } ;
486+ row . appendChild ( b ) ;
487+ }
488+ }
489+ }
490+ const send = document . createElement ( 'button' ) ;
491+ send . id = 'askSend' ;
492+ send . style . cssText = 'background:var(--accent);color:#fff;border:0;border-radius:8px;padding:7px 16px;margin-top:6px' ;
493+ send . textContent = '回答' ;
494+ send . onclick = async ( ) => {
495+ const answers = [ ...div . querySelectorAll ( 'input' ) ] . map ( i => i . value . trim ( ) ) . filter ( Boolean ) ;
496+ if ( ! answers . length ) return ;
497+ send . disabled = true ;
498+ const res = await api . req ( `/conversations/${ currentConv } /runs/${ runId } /answer` ,
499+ { method : 'POST' , body : JSON . stringify ( { answers } ) } ) ;
500+ if ( ! res . ok ) { send . disabled = false ; addMsg ( 'err' , await errMsg ( res ) ) ; return ; }
501+ div . style . opacity = '.5' ;
502+ } ;
503+ div . appendChild ( send ) ;
504+ $ ( 'messages' ) . appendChild ( div ) ;
505+ $ ( 'messages' ) . scrollTop = $ ( 'messages' ) . scrollHeight ;
506+ }
507+
508+ function strData ( d ) {
509+ if ( d == null ) return '' ;
510+ if ( typeof d === 'string' ) return d ;
511+ if ( Array . isArray ( d ) ) return d . map ( strData ) . join ( ', ' ) ;
512+ return JSON . stringify ( d ) ;
513+ }
514+ function setRunStatus ( text , busy ) {
515+ const box = $ ( 'runStatus' ) ;
516+ if ( ! text ) { box . style . display = 'none' ; return ; }
517+ box . style . display = 'flex' ;
518+ box . innerHTML = ( busy ? '<span class="spin">⚙️</span>' : '' ) + '<span>' + escapeHtml ( text ) + '</span>' ;
519+ }
520+ function renderTodos ( items ) {
521+ const box = $ ( 'todoPanel' ) ;
522+ if ( ! items || ! items . length ) { box . style . display = 'none' ; return ; }
523+ box . style . display = 'block' ;
524+ box . innerHTML = '<div class="t-title">📋 任务进度</div>' + items . map ( t => {
525+ const s = String ( t . status || '' ) ;
526+ const cls = / d o n e | c o m p l e t e / i. test ( s ) ? 'done' : / p r o g r e s s | a c t i v e | c u r r e n t / i. test ( s ) ? 'doing' : '' ;
527+ const icon = cls === 'done' ? '✅' : cls === 'doing' ? '🔄' : '⬜' ;
528+ return `<div class="t-item ${ cls } "><span>${ icon } </span><span>${ escapeHtml ( t . content || '' ) } </span></div>` ;
529+ } ) . join ( '' ) ;
530+ }
531+
449532function startStream ( runId ) {
450533 currentRun = runId ;
451534 $ ( 'stopBtn2' ) . style . display = 'inline-block' ;
535+ setRunStatus ( '正在启动…' , true ) ;
452536 const url = `/conversations/${ currentConv } /runs/${ runId } /stream?access_token=${ encodeURIComponent ( api . token ) } ` ;
453537 es = new EventSource ( url ) ;
454538 let agentDiv = null ;
@@ -458,13 +542,31 @@ <h1>一句话处理 Excel 表格</h1>
458542 if ( ! agentDiv ) agentDiv = addMsg ( 'agent' , '' ) ;
459543 agentDiv . lastChild . textContent = ( agentDiv . lastChild . textContent || '' ) + ( e . data ?. text ?? '' ) ;
460544 $ ( 'messages' ) . scrollTop = $ ( 'messages' ) . scrollHeight ;
461- } else if ( e . type === 'notify' && e . data && e . data . kind === 'tool_start' ) {
462- addMsg ( 'tool' , '· 工具: ' + ( e . data . data || [ ] ) . join ( ', ' ) ) ;
463- } else if ( e . type === 'notify' && e . data && e . data . kind === 'error' ) {
464- addMsg ( 'err' , e . data . data ) ;
545+ } else if ( e . type === 'notify' && e . data ) {
546+ const kind = e . data . kind , data = e . data . data ;
547+ if ( kind === 'tool_start' ) {
548+ setRunStatus ( '准备执行: ' + strData ( data ) , true ) ;
549+ } else if ( kind === 'tool_running' ) {
550+ setRunStatus ( '正在执行: ' + strData ( data ) , true ) ;
551+ } else if ( kind === 'tool' ) {
552+ setRunStatus ( '工具执行完成' , true ) ;
553+ } else if ( kind === 'todos' ) {
554+ renderTodos ( data ) ;
555+ } else if ( kind === 'error' ) {
556+ addMsg ( 'err' , typeof data === 'string' ? data : JSON . stringify ( data ) ) ;
557+ } else if ( kind === 'retry' ) {
558+ setRunStatus ( '模型调用重试中…' , true ) ;
559+ } else if ( kind === 'compact' ) {
560+ setRunStatus ( '正在压缩上下文…' , true ) ;
561+ } else if ( kind === 'ask' || kind === 'confirm' ) {
562+ addAsk ( data , runId ) ;
563+ }
564+ } else if ( e . type === 'log' && e . data && e . data . message ) {
565+ setRunStatus ( e . data . message , true ) ;
465566 } else if ( e . type === 'run' ) {
466567 es . close ( ) ; es = null ; currentRun = null ;
467568 $ ( 'stopBtn2' ) . style . display = 'none' ;
569+ setRunStatus ( '' , false ) ;
468570 openConversation ( currentConv , '' ) ;
469571 loadConversations ( ) ;
470572 loadArtifacts ( ) ;
0 commit comments