fix: keep todo scrolling simple and visible
This commit is contained in:
parent
8b250c5c6b
commit
d5e6c9c077
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user