diff --git a/static/src/components/chat/monitor/MonitorDirector.ts b/static/src/components/chat/monitor/MonitorDirector.ts index fb045ae..d18781c 100644 --- a/static/src/components/chat/monitor/MonitorDirector.ts +++ b/static/src/components/chat/monitor/MonitorDirector.ts @@ -2774,22 +2774,27 @@ export class MonitorDirector implements MonitorDriver { payload?.arguments?.title || payload?.arguments?.task || '待办摘要'; - const tasks = this.normalizeTodoTasks(payload?.arguments?.tasks || summary); - await this.ensureTodoWindowVisible(); - await this.typeTodoSummary(summary); - for (const task of tasks) { - await this.animateTodoAppend(task); - } - await sleep(320); - }; + const tasks = this.normalizeTodoTasks(payload?.arguments?.tasks || summary); + await this.ensureTodoWindowVisible(); + this.resetTodoBoard({ summary: true, list: true }); + await this.typeTodoSummary(summary); + for (const task of tasks) { + await this.animateTodoAppend(task, { scrollIntoView: false }); + } + if (this.elements.todoList) { + this.elements.todoList.scrollTop = 0; + } + await sleep(320); + }; this.sceneHandlers.todoUpdate = async (payload, runtime) => { this.applySceneStatus(runtime, 'todoUpdate', '正在调整待办'); await this.ensureTodoWindowVisible(); const targetText = payload?.arguments?.title || payload?.arguments?.task || null; + const targetIndex = Number(payload?.arguments?.task_index || payload?.arguments?.index || 0) || null; const completed = payload?.arguments?.completed ?? payload?.arguments?.done ?? payload?.arguments?.checked ?? true; - await this.toggleTodoItem(targetText, !!completed); + await this.toggleTodoItem(targetText, !!completed, targetIndex); await sleep(260); }; @@ -2801,6 +2806,7 @@ export class MonitorDirector implements MonitorDriver { await this.click(); } this.closeWindow(this.elements.todoWindow); + this.resetTodoBoard({ summary: true, list: true }); await sleep(320); }; @@ -5015,11 +5021,18 @@ export class MonitorDirector implements MonitorDriver { } private findTodoItemByText(text?: string | null): HTMLElement | null { - if (!text) return this.getTodoItems()[0] || null; + if (!text) return null; const target = (text || '').trim(); return this.getTodoItems().find(item => item.querySelector('.todo-text')?.textContent?.trim() === target) || null; } + private findTodoItemByIndex(index?: number | null): HTMLElement | null { + if (!index || index < 1) return null; + const items = this.getTodoItems(); + if (index > items.length) return null; + return items[index - 1]; + } + private normalizeTodoTasks(raw: any): Array<{ text: string; done?: boolean }> { if (Array.isArray(raw)) { return raw @@ -5027,7 +5040,14 @@ export class MonitorDirector implements MonitorDriver { if (typeof item === 'string') return { text: item, done: false }; if (item && typeof item === 'object') { const text = String(item.title || item.task || item.text || '').trim(); - const done = typeof item.completed === 'boolean' ? item.completed : !!item.done; + const done = + typeof item.completed === 'boolean' + ? item.completed + : typeof item.done === 'boolean' + ? item.done + : typeof item.checked === 'boolean' + ? item.checked + : false; if (!text) return null; return { text, done }; } @@ -5068,6 +5088,17 @@ export class MonitorDirector implements MonitorDriver { }); } + private resetTodoBoard(options: { summary?: boolean; list?: boolean } = {}) { + const { summary = true, list = true } = options; + if (summary && this.elements.todoSummary) { + this.elements.todoSummary.textContent = ''; + } + if (list && this.elements.todoList) { + this.elements.todoList.innerHTML = ''; + this.elements.todoList.scrollTop = 0; + } + } + private async ensureTodoWindowVisible() { if (this.isWindowVisible(this.elements.todoWindow)) { this.showWindow(this.elements.todoWindow); @@ -5076,6 +5107,7 @@ export class MonitorDirector implements MonitorDriver { await this.movePointerToApp('todo'); await this.click({ count: 2 }); this.showWindow(this.elements.todoWindow); + this.resetTodoBoard({ summary: true, list: true }); } private async typeTodoSummary(text: string) { @@ -5090,26 +5122,44 @@ export class MonitorDirector implements MonitorDriver { } } - private async animateTodoAppend(task: { text: string; done?: boolean }) { + private async animateTodoAppend(task: { text: string; done?: boolean }, options: { scrollIntoView?: boolean } = {}) { if (!this.elements.todoList) return; + const { scrollIntoView = false } = options; const card = this.createTodoItem(task.text, !!task.done); this.elements.todoList.appendChild(card); requestAnimationFrame(() => card.classList.add('visible')); - await this.scrollTodoItemIntoView(card); + if (scrollIntoView) { + await this.scrollTodoItemIntoView(card); + } await sleep(180); } private async scrollTodoItemIntoView(card: HTMLElement | null) { if (!card || !this.elements.todoList) return; const body = this.elements.todoList; - const targetTop = card.offsetTop - body.clientHeight * 0.2; - const clamped = Math.max(0, targetTop); - body.scrollTo({ top: clamped, behavior: 'smooth' }); - await this.waitForScrollSettled(body, clamped); + const cardTop = card.offsetTop; + const cardBottom = cardTop + card.offsetHeight; + const viewTop = body.scrollTop; + const viewBottom = viewTop + body.clientHeight; + + // 如果完整可见,直接返回 + if (cardTop >= viewTop && cardBottom <= viewBottom) { + return; + } + + // 让整个卡片进入视口:若底部被遮挡,滚到卡片底部露出;否则滚到卡片顶部 + const targetTop = + cardBottom > viewBottom ? cardBottom - body.clientHeight + 4 : Math.max(0, cardTop - 4); + + body.scrollTop = Math.max(0, targetTop); + await sleep(30); } - private async toggleTodoItem(text?: string | null, done?: boolean) { - const card = this.findTodoItemByText(text); + private async toggleTodoItem(text?: string | null, done?: boolean, index?: number | null) { + const card = + this.findTodoItemByIndex(index) || + this.findTodoItemByText(text) || + (!text && !index ? this.getTodoItems()[0] : null); if (!card) return; await this.scrollTodoItemIntoView(card); const check = card.querySelector('.todo-check') as HTMLElement | null; diff --git a/static/src/styles/components/chat/_virtual-monitor.scss b/static/src/styles/components/chat/_virtual-monitor.scss index 8325e09..ebe7ff4 100644 --- a/static/src/styles/components/chat/_virtual-monitor.scss +++ b/static/src/styles/components/chat/_virtual-monitor.scss @@ -1139,10 +1139,10 @@ /* Todo Window */ .virtual-monitor-surface .todo-window { - width: 520px; - height: 360px; - top: 180px; - left: 520px; + width: 360px; + height: 300px; + top: 110px; + left: 640px; display: flex; flex-direction: column; } @@ -1153,6 +1153,7 @@ flex-direction: column; gap: 12px; padding: 12px; + overflow: hidden; } .virtual-monitor-surface .todo-summary { @@ -1168,13 +1169,16 @@ } .virtual-monitor-surface .todo-progress { - flex: 1 1 auto; + flex: 1 1 0%; min-height: 0; + max-height: 100%; display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; + height: 100%; + overflow-y: auto !important; padding-right: 2px; + overscroll-behavior: contain; } .virtual-monitor-surface .todo-summary,