Merge claude/1
This commit is contained in:
commit
496c9ed1b3
@ -2318,38 +2318,116 @@ class TasksManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async clearAllTasks() {
|
async clearAllTasks() {
|
||||||
// Use the confirmation dialog system if available, otherwise fallback to confirm
|
const result = await this._showClearAllModal(this.tasks);
|
||||||
|
if (!result || !result.confirmed) return false;
|
||||||
|
await this.performClearAll({ cancelRunning: result.cancelRunning });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirmation modal for clearAllTasks. Same openEoModal shape as
|
||||||
|
// _showDeleteTaskModal so visual identity matches every other destructive
|
||||||
|
// confirmation (Uninstall, Delete Task, …). Adds a "Cancel running tasks
|
||||||
|
// too" toggle (off by default) — when off, running/queued tasks are
|
||||||
|
// skipped; when on, they're cancelled first then deleted.
|
||||||
|
// Resolves {confirmed, cancelRunning}. Cancel/backdrop/close → confirmed=false.
|
||||||
|
_showClearAllModal(tasks) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (window.showConfirmation) {
|
const escHtml = (s) => String(s == null ? '' : s)
|
||||||
window.showConfirmation(
|
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
'Clear All Tasks',
|
|
||||||
'Are you sure you want to clear all tasks? This will delete all task history and cannot be undone.',
|
const isActive = (t) => t && (t.status === 'running' || t.status === 'queued' || t.status === 'pending');
|
||||||
() => {
|
const total = (tasks || []).length;
|
||||||
this.performClearAll();
|
const runningCount = (tasks || []).filter(isActive).length;
|
||||||
resolve(true);
|
const terminalCount = total - runningCount;
|
||||||
|
|
||||||
|
const bodyHtml = `
|
||||||
|
<div class="eo-empty-state danger" role="status">
|
||||||
|
<div class="eo-empty-state-icon">
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
|
||||||
|
<line x1="12" y1="9" x2="12" y2="13"></line>
|
||||||
|
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="eo-empty-state-body">
|
||||||
|
<p class="eo-empty-state-title">This cannot be undone</p>
|
||||||
|
<p class="eo-empty-state-text">All selected tasks and their logs will be permanently removed.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${window.eoBadgeRow ? window.eoBadgeRow([
|
||||||
|
{ label: `Total: ${total}`, variant: 'info' },
|
||||||
|
...(runningCount > 0 ? [{ label: `Running: ${runningCount}`, variant: 'warning' }] : []),
|
||||||
|
...(terminalCount > 0 ? [{ label: `Terminal: ${terminalCount}`, variant: 'success' }] : []),
|
||||||
|
]) : ''}
|
||||||
|
${runningCount > 0 ? `
|
||||||
|
<label class="eo-toggle eo-toggle-card" data-eo-toggle-row>
|
||||||
|
<input type="checkbox" id="clear-all-cancel-running">
|
||||||
|
<span class="eo-toggle-track"></span>
|
||||||
|
<span class="eo-toggle-text">
|
||||||
|
<span class="eo-toggle-text-title">Cancel running tasks too</span>
|
||||||
|
<span class="eo-toggle-text-help">Off: skip the ${runningCount} active task${runningCount === 1 ? '' : 's'}. On: cancel them first, then delete.</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
` : ''}
|
||||||
|
`;
|
||||||
|
|
||||||
|
let decided = false;
|
||||||
|
const finish = (val, modal) => {
|
||||||
|
if (decided) return;
|
||||||
|
decided = true;
|
||||||
|
if (modal) modal.close();
|
||||||
|
resolve(val);
|
||||||
|
};
|
||||||
|
|
||||||
|
const m = window.openEoModal({
|
||||||
|
id: 'clear-all-tasks-modal',
|
||||||
|
size: 'sm',
|
||||||
|
eyebrow: 'Delete Tasks',
|
||||||
|
title: total === 1 ? 'Delete 1 task?' : `Delete all ${total} tasks?`,
|
||||||
|
desc: 'Confirm to delete the selected tasks.',
|
||||||
|
body: bodyHtml,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
variant: 'danger',
|
||||||
|
onClick: (modal) => {
|
||||||
|
const cb = modal.bodyEl.querySelector('#clear-all-cancel-running');
|
||||||
|
finish({ confirmed: true, cancelRunning: !!(cb && cb.checked) }, modal);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'Yes, Clear All',
|
{ label: 'Cancel', variant: 'secondary', onClick: (modal) => finish({ confirmed: false }, modal) }
|
||||||
'Cancel',
|
],
|
||||||
'clear',
|
onClose: () => finish({ confirmed: false }, null)
|
||||||
false
|
});
|
||||||
);
|
|
||||||
} else {
|
// Live-update the danger button's label so the user knows exactly
|
||||||
// Fallback to native confirm
|
// what will happen as they flip the toggle. No-op when no running
|
||||||
const confirmed = confirm('Are you sure you want to clear all tasks? This will delete all task history.');
|
// tasks (no toggle in the body in that case).
|
||||||
if (confirmed) {
|
const cb = m.bodyEl.querySelector('#clear-all-cancel-running');
|
||||||
this.performClearAll();
|
const deleteBtn = m.contentEl.querySelector('.btn-danger');
|
||||||
|
const updateLabel = () => {
|
||||||
|
if (!deleteBtn) return;
|
||||||
|
const cancelRunning = !!(cb && cb.checked);
|
||||||
|
if (runningCount > 0 && !cancelRunning) {
|
||||||
|
deleteBtn.textContent = `Delete ${terminalCount} (skip ${runningCount} running)`;
|
||||||
|
} else {
|
||||||
|
deleteBtn.textContent = total === 1 ? 'Delete Task' : `Delete ${total} Tasks`;
|
||||||
}
|
}
|
||||||
resolve(confirmed);
|
};
|
||||||
}
|
if (cb) cb.addEventListener('change', updateLabel);
|
||||||
|
updateLabel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async performClearAll() {
|
async performClearAll(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
const cancelRunning = !!opts.cancelRunning;
|
||||||
|
|
||||||
// Show progress notification with the trash type icon in the left slot
|
// Show progress notification with the trash type icon in the left slot
|
||||||
// (this is conceptually a delete-all action, so 🗑️ matches the row icon).
|
// (this is conceptually a delete-all action, so 🗑️ matches the row icon).
|
||||||
const customIcon = '<span style="font-size:18px;line-height:1;">🗑️</span>';
|
const customIcon = '<span style="font-size:18px;line-height:1;">🗑️</span>';
|
||||||
const progressNotification = window.notificationSystem.show(
|
const progressNotification = window.notificationSystem.show(
|
||||||
'Clearing all tasks...',
|
'Clearing tasks...',
|
||||||
'info',
|
'info',
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -2357,44 +2435,55 @@ class TasksManager {
|
|||||||
customIcon
|
customIcon
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isActive = (t) => t && (t.status === 'running' || t.status === 'queued' || t.status === 'pending');
|
||||||
|
// Partition: which tasks we attempt now, which we skip silently.
|
||||||
|
const targets = (this.tasks || []).filter(t => cancelRunning || !isActive(t));
|
||||||
|
const skipped = (this.tasks || []).filter(t => !targets.includes(t));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Delete all tasks using the task manager
|
// For active tasks (only reached when the toggle is on): cancel first,
|
||||||
const deletePromises = this.tasks.map(task =>
|
// wait for terminal status, then delete. Mirrors the single-row
|
||||||
this.taskManager.deleteTask(task.id)
|
// deleteTask flow so the bash processor has a chance to honour the
|
||||||
);
|
// cancel marker before the DELETE arrives (otherwise we'd 409).
|
||||||
|
await Promise.all(targets.map(async (task) => {
|
||||||
await Promise.all(deletePromises);
|
if (cancelRunning && isActive(task)) {
|
||||||
|
try { await this.taskManager.cancelTask(task.id); } catch {}
|
||||||
// Clear local array and re-render
|
await this._waitForTaskTerminal(task.id, 15_000);
|
||||||
this.tasks = [];
|
}
|
||||||
|
try {
|
||||||
|
await this.taskManager.deleteTask(task.id);
|
||||||
|
} catch (err) {
|
||||||
|
const looks409 = /\bHTTP 409\b/.test(err && err.message ? err.message : '');
|
||||||
|
if (!looks409) throw err;
|
||||||
|
await this.taskManager.deleteTask(task.id, { force: true });
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Re-sync local state: keep anything we intentionally skipped.
|
||||||
|
const deletedIds = new Set(targets.map(t => t.id));
|
||||||
|
this.tasks = this.tasks.filter(t => !deletedIds.has(t.id));
|
||||||
this.renderTasks();
|
this.renderTasks();
|
||||||
this.updateStats();
|
this.updateStats();
|
||||||
this.updateSidebarCounts();
|
this.updateSidebarCounts();
|
||||||
this.generateAppCategories();
|
this.generateAppCategories();
|
||||||
|
|
||||||
// Remove progress notification and show success
|
|
||||||
if (progressNotification && progressNotification.remove) {
|
if (progressNotification && progressNotification.remove) {
|
||||||
progressNotification.remove();
|
progressNotification.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
window.notificationSystem.show(
|
const msg = skipped.length > 0
|
||||||
'All tasks cleared successfully',
|
? `Deleted ${targets.length} tasks (skipped ${skipped.length} still running)`
|
||||||
'success',
|
: (targets.length === 1 ? 'Task deleted' : `Deleted ${targets.length} tasks`);
|
||||||
null,
|
window.notificationSystem.show(msg, 'success', null, null, null, customIcon);
|
||||||
null,
|
|
||||||
null,
|
|
||||||
customIcon
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error clearing tasks:', error);
|
console.error('Error clearing tasks:', error);
|
||||||
|
|
||||||
// Remove progress notification and show error
|
|
||||||
if (progressNotification && progressNotification.remove) {
|
if (progressNotification && progressNotification.remove) {
|
||||||
progressNotification.remove();
|
progressNotification.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.notificationSystem) {
|
if (window.notificationSystem) {
|
||||||
window.notificationSystem.show(
|
window.notificationSystem.show(
|
||||||
`Failed to clear tasks: ${error.message}`,
|
`Failed to clear tasks: ${error.message}`,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user