fix(webui/tasks): auto-expand opens one row, not all of them

autoExpandTask (the monitorTask path) opened its row directly without
collapsing the others and never set highlightedTaskId — unlike every
other opener (toggleTaskDetails, selectTask), which enforce a single
open row. So a burst of monitored task creations, e.g. a multi-app
first install, stacked every panel open at once.

Wait for the row to render, then delegate to selectTask, which collapses
any other open panel, sets highlightedTaskId, attaches the right log
view (live stream vs snapshot) and scrolls into view. Setting
highlightedTaskId also makes monitorTask's own guard trip after the
first task, so the running-task auto-follow takes over from there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-06-22 14:41:36 +01:00
parent 38a7e4c365
commit 70f16ef1e3

View File

@ -197,61 +197,27 @@ Object.assign(TasksManager.prototype, {
window.removeEventListener('taskCompleted', onComplete);
});
},
// Auto-expand a task when it's created
async autoExpandTask(taskId) {
// Wait for task to be rendered
// Auto-expand a task when it's created/started. Waits for its row to land in
// the DOM, then hands off to selectTask, which collapses any other open panel
// first. Opening the row directly here (as this used to) skipped that
// collapse and never set highlightedTaskId, so a burst of monitored tasks —
// e.g. a multi-app first install — stacked every panel open instead of
// showing just the active one.
autoExpandTask(taskId) {
let attempts = 0;
const maxAttempts = 10;
const tryExpand = async () => {
const tryExpand = () => {
attempts++;
// Check if task element exists
const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
if (!taskElement) {
if (attempts < maxAttempts) {
setTimeout(tryExpand, 500); // Try again in 500ms
} else {
console.warn(`⚠️ Could not find task element for ${taskId} after ${maxAttempts} attempts`);
}
return;
// selectTask returns false until the row exists; once it succeeds it has
// also attached the right log view (live stream vs snapshot), set
// highlightedTaskId and scrolled the row into view — nothing more to do.
if (this.selectTask(taskId)) return;
if (attempts < maxAttempts) {
setTimeout(tryExpand, 500);
} else {
console.warn(`⚠️ Could not find task element for ${taskId} after ${maxAttempts} attempts`);
}
// Get the details element
const details = document.getElementById(`details-${taskId}`);
if (!details) {
if (attempts < maxAttempts) {
setTimeout(tryExpand, 500);
} else {
console.warn(`⚠️ Could not find details element for ${taskId}`);
}
return;
}
// Expand the task details
details.style.display = 'block';
details.classList.add('task-details-open');
// Update toggle button
const toggleBtn = document.querySelector(`.task-btn.toggle-details[onclick*="toggleTaskDetails('${taskId}')"]`);
if (toggleBtn) {
toggleBtn.classList.add('expanded');
}
// Scroll to the task
taskElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Load the output if task is completed
const task = await this.taskManager.getTask(taskId);
if (task && (task.status === 'completed' || task.status === 'failed')) {
setTimeout(() => {
this.loadTaskOutput(taskId);
}, 1000);
}
};
tryExpand();