diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js
index 827276e..6319445 100644
--- a/sidepanel/sidepanel.js
+++ b/sidepanel/sidepanel.js
@@ -37,6 +37,7 @@ const inputInbucketMailbox = document.getElementById('input-inbucket-mailbox');
const inputRunCount = document.getElementById('input-run-count');
const inputAutoSkipFailures = document.getElementById('input-auto-skip-failures');
const autoStartModal = document.getElementById('auto-start-modal');
+const autoStartTitle = autoStartModal?.querySelector('.modal-title');
const autoStartMessage = document.getElementById('auto-start-message');
const btnAutoStartClose = document.getElementById('btn-auto-start-close');
const btnAutoStartCancel = document.getElementById('btn-auto-start-cancel');
@@ -66,7 +67,8 @@ let currentAutoRun = {
let settingsDirty = false;
let settingsSaveInFlight = false;
let settingsAutoSaveTimer = null;
-let autoStartChoiceResolver = null;
+let modalChoiceResolver = null;
+let currentModalActions = [];
const EYE_OPEN_ICON = '';
const EYE_CLOSED_ICON = '';
@@ -110,33 +112,89 @@ function dismissToast(toast) {
toast.addEventListener('animationend', () => toast.remove());
}
-function resolveAutoStartChoice(choice) {
- if (autoStartChoiceResolver) {
- autoStartChoiceResolver(choice);
- autoStartChoiceResolver = null;
+function resetActionModalButtons() {
+ const buttons = [btnAutoStartCancel, btnAutoStartRestart, btnAutoStartContinue];
+ buttons.forEach((button) => {
+ if (!button) return;
+ button.hidden = true;
+ button.disabled = false;
+ button.onclick = null;
+ });
+ currentModalActions = [];
+}
+
+function configureActionModalButton(button, action) {
+ if (!button) return;
+ if (!action) {
+ button.hidden = true;
+ button.onclick = null;
+ return;
}
+
+ button.hidden = false;
+ button.disabled = false;
+ button.textContent = action.label;
+ button.className = `btn ${action.variant || 'btn-outline'} btn-sm`;
+ button.onclick = () => resolveModalChoice(action.id);
+}
+
+function resolveModalChoice(choice) {
+ if (modalChoiceResolver) {
+ modalChoiceResolver(choice);
+ modalChoiceResolver = null;
+ }
+ resetActionModalButtons();
if (autoStartModal) {
autoStartModal.hidden = true;
}
}
-function openAutoStartChoiceDialog(startStep) {
+function openActionModal({ title, message, actions }) {
if (!autoStartModal) {
- return Promise.resolve('restart');
+ return Promise.resolve(null);
}
- if (autoStartChoiceResolver) {
- resolveAutoStartChoice(null);
+ if (modalChoiceResolver) {
+ resolveModalChoice(null);
}
- autoStartMessage.textContent = `检测到当前已有流程进度。继续当前会从步骤 ${startStep} 开始自动执行,重新开始会清空当前流程进度并从步骤 1 新开一轮。`;
+ autoStartTitle.textContent = title;
+ autoStartMessage.textContent = message;
+ currentModalActions = actions || [];
+ configureActionModalButton(btnAutoStartCancel, currentModalActions[0]);
+ configureActionModalButton(btnAutoStartRestart, currentModalActions[1]);
+ configureActionModalButton(btnAutoStartContinue, currentModalActions[2]);
autoStartModal.hidden = false;
return new Promise((resolve) => {
- autoStartChoiceResolver = resolve;
+ modalChoiceResolver = resolve;
});
}
+function openAutoStartChoiceDialog(startStep) {
+ return openActionModal({
+ title: '启动自动',
+ message: `检测到当前已有流程进度。继续当前会从步骤 ${startStep} 开始自动执行,重新开始会清空当前流程进度并从步骤 1 新开一轮。`,
+ actions: [
+ { id: null, label: '取消', variant: 'btn-ghost' },
+ { id: 'restart', label: '重新开始', variant: 'btn-outline' },
+ { id: 'continue', label: '继续当前', variant: 'btn-primary' },
+ ],
+ });
+}
+
+async function openConfirmModal({ title, message, confirmLabel = '确认', confirmVariant = 'btn-primary' }) {
+ const choice = await openActionModal({
+ title,
+ message,
+ actions: [
+ { id: null, label: '取消', variant: 'btn-ghost' },
+ { id: 'confirm', label: confirmLabel, variant: confirmVariant },
+ ],
+ });
+ return choice === 'confirm';
+}
+
function isDoneStatus(status) {
return status === 'completed' || status === 'manual_completed' || status === 'skipped';
}
@@ -634,7 +692,12 @@ async function maybeTakeoverAutoRun(actionLabel) {
return true;
}
- const confirmed = confirm(`当前自动流程已暂停。若继续${actionLabel},将停止自动流程并切换为手动控制。是否继续?`);
+ const confirmed = await openConfirmModal({
+ title: '接管自动',
+ message: `当前自动流程已暂停。若继续${actionLabel},将停止自动流程并切换为手动控制。是否继续?`,
+ confirmLabel: '确认接管',
+ confirmVariant: 'btn-primary',
+ });
if (!confirmed) {
return false;
}
@@ -648,7 +711,12 @@ async function handleSkipStep(step) {
return;
}
- const confirmed = confirm(`这不会真正执行步骤 ${step},只会直接跳过该步骤并放行后续步骤。是否继续?`);
+ const confirmed = await openConfirmModal({
+ title: '跳过步骤',
+ message: `这不会真正执行步骤 ${step},只会直接跳过该步骤并放行后续步骤。是否继续?`,
+ confirmLabel: `跳过步骤 ${step}`,
+ confirmVariant: 'btn-primary',
+ });
if (!confirmed) {
return;
}
@@ -736,13 +804,10 @@ btnStop.addEventListener('click', async () => {
autoStartModal?.addEventListener('click', (event) => {
if (event.target === autoStartModal) {
- resolveAutoStartChoice(null);
+ resolveModalChoice(null);
}
});
-btnAutoStartClose?.addEventListener('click', () => resolveAutoStartChoice(null));
-btnAutoStartCancel?.addEventListener('click', () => resolveAutoStartChoice(null));
-btnAutoStartRestart?.addEventListener('click', () => resolveAutoStartChoice('restart'));
-btnAutoStartContinue?.addEventListener('click', () => resolveAutoStartChoice('continue'));
+btnAutoStartClose?.addEventListener('click', () => resolveModalChoice(null));
// Auto Run
btnAutoRun.addEventListener('click', async () => {
@@ -793,27 +858,35 @@ btnAutoContinue.addEventListener('click', async () => {
// Reset
btnReset.addEventListener('click', async () => {
- if (confirm('确认重置全部步骤和数据吗?')) {
- await chrome.runtime.sendMessage({ type: 'RESET', source: 'sidepanel' });
- syncLatestState({ stepStatuses: STEP_DEFAULT_STATUSES });
- syncAutoRunState({ autoRunning: false, autoRunPhase: 'idle', autoRunCurrentRun: 0, autoRunTotalRuns: 1, autoRunAttemptRun: 0 });
- displayOauthUrl.textContent = '等待中...';
- displayOauthUrl.classList.remove('has-value');
- displayLocalhostUrl.textContent = '等待中...';
- displayLocalhostUrl.classList.remove('has-value');
- inputEmail.value = '';
- displayStatus.textContent = '就绪';
- statusBar.className = 'status-bar';
- logArea.innerHTML = '';
- document.querySelectorAll('.step-row').forEach(row => row.className = 'step-row');
- document.querySelectorAll('.step-status').forEach(el => el.textContent = '');
- setDefaultAutoRunButton();
- applyAutoRunStatus(currentAutoRun);
- markSettingsDirty(false);
- updateStopButtonState(false);
- updateButtonStates();
- updateProgressCounter();
+ const confirmed = await openConfirmModal({
+ title: '重置流程',
+ message: '确认重置全部步骤和数据吗?',
+ confirmLabel: '确认重置',
+ confirmVariant: 'btn-danger',
+ });
+ if (!confirmed) {
+ return;
}
+
+ await chrome.runtime.sendMessage({ type: 'RESET', source: 'sidepanel' });
+ syncLatestState({ stepStatuses: STEP_DEFAULT_STATUSES });
+ syncAutoRunState({ autoRunning: false, autoRunPhase: 'idle', autoRunCurrentRun: 0, autoRunTotalRuns: 1, autoRunAttemptRun: 0 });
+ displayOauthUrl.textContent = '等待中...';
+ displayOauthUrl.classList.remove('has-value');
+ displayLocalhostUrl.textContent = '等待中...';
+ displayLocalhostUrl.classList.remove('has-value');
+ inputEmail.value = '';
+ displayStatus.textContent = '就绪';
+ statusBar.className = 'status-bar';
+ logArea.innerHTML = '';
+ document.querySelectorAll('.step-row').forEach(row => row.className = 'step-row');
+ document.querySelectorAll('.step-status').forEach(el => el.textContent = '');
+ setDefaultAutoRunButton();
+ applyAutoRunStatus(currentAutoRun);
+ markSettingsDirty(false);
+ updateStopButtonState(false);
+ updateButtonStates();
+ updateProgressCounter();
});
// Clear log