Merge claude/1
This commit is contained in:
commit
496c9ed1b3
@ -2318,38 +2318,116 @@ class TasksManager {
|
||||
}
|
||||
|
||||
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) => {
|
||||
if (window.showConfirmation) {
|
||||
window.showConfirmation(
|
||||
'Clear All Tasks',
|
||||
'Are you sure you want to clear all tasks? This will delete all task history and cannot be undone.',
|
||||
() => {
|
||||
this.performClearAll();
|
||||
resolve(true);
|
||||
const escHtml = (s) => String(s == null ? '' : s)
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
const isActive = (t) => t && (t.status === 'running' || t.status === 'queued' || t.status === 'pending');
|
||||
const total = (tasks || []).length;
|
||||
const runningCount = (tasks || []).filter(isActive).length;
|
||||
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',
|
||||
'Cancel',
|
||||
'clear',
|
||||
false
|
||||
);
|
||||
} else {
|
||||
// Fallback to native confirm
|
||||
const confirmed = confirm('Are you sure you want to clear all tasks? This will delete all task history.');
|
||||
if (confirmed) {
|
||||
this.performClearAll();
|
||||
{ label: 'Cancel', variant: 'secondary', onClick: (modal) => finish({ confirmed: false }, modal) }
|
||||
],
|
||||
onClose: () => finish({ confirmed: false }, null)
|
||||
});
|
||||
|
||||
// Live-update the danger button's label so the user knows exactly
|
||||
// what will happen as they flip the toggle. No-op when no running
|
||||
// tasks (no toggle in the body in that case).
|
||||
const cb = m.bodyEl.querySelector('#clear-all-cancel-running');
|
||||
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
|
||||
// (this is conceptually a delete-all action, so 🗑️ matches the row icon).
|
||||
const customIcon = '<span style="font-size:18px;line-height:1;">🗑️</span>';
|
||||
const progressNotification = window.notificationSystem.show(
|
||||
'Clearing all tasks...',
|
||||
'Clearing tasks...',
|
||||
'info',
|
||||
null,
|
||||
null,
|
||||
@ -2357,44 +2435,55 @@ class TasksManager {
|
||||
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 {
|
||||
// Delete all tasks using the task manager
|
||||
const deletePromises = this.tasks.map(task =>
|
||||
this.taskManager.deleteTask(task.id)
|
||||
);
|
||||
|
||||
await Promise.all(deletePromises);
|
||||
|
||||
// Clear local array and re-render
|
||||
this.tasks = [];
|
||||
// For active tasks (only reached when the toggle is on): cancel first,
|
||||
// wait for terminal status, then delete. Mirrors the single-row
|
||||
// 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) => {
|
||||
if (cancelRunning && isActive(task)) {
|
||||
try { await this.taskManager.cancelTask(task.id); } catch {}
|
||||
await this._waitForTaskTerminal(task.id, 15_000);
|
||||
}
|
||||
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.updateStats();
|
||||
this.updateSidebarCounts();
|
||||
this.generateAppCategories();
|
||||
|
||||
// Remove progress notification and show success
|
||||
|
||||
if (progressNotification && progressNotification.remove) {
|
||||
progressNotification.remove();
|
||||
}
|
||||
|
||||
|
||||
if (window.notificationSystem) {
|
||||
window.notificationSystem.show(
|
||||
'All tasks cleared successfully',
|
||||
'success',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
customIcon
|
||||
);
|
||||
const msg = skipped.length > 0
|
||||
? `Deleted ${targets.length} tasks (skipped ${skipped.length} still running)`
|
||||
: (targets.length === 1 ? 'Task deleted' : `Deleted ${targets.length} tasks`);
|
||||
window.notificationSystem.show(msg, 'success', null, null, null, customIcon);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error clearing tasks:', error);
|
||||
|
||||
// Remove progress notification and show error
|
||||
|
||||
if (progressNotification && progressNotification.remove) {
|
||||
progressNotification.remove();
|
||||
}
|
||||
|
||||
|
||||
if (window.notificationSystem) {
|
||||
window.notificationSystem.show(
|
||||
`Failed to clear tasks: ${error.message}`,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user