diff --git a/background.js b/background.js index d9b0ac9..911638f 100644 --- a/background.js +++ b/background.js @@ -50,7 +50,7 @@ initializeSessionStorageAccess(); // 状态管理(chrome.storage.session + chrome.storage.local) // ============================================================ -const PERSISTED_SETTING_DEFAULTS = { +const LEGACY_PERSISTED_SETTING_DEFAULTS = { panelMode: 'cpa', // Step 1 / Step 9 的来源模式:cpa | sub2api。 vpsUrl: '', // VPS 面板地址,可手动填写。 vpsPassword: '', // VPS 面板登录密码,可手动填写。 @@ -61,6 +61,18 @@ const PERSISTED_SETTING_DEFAULTS = { sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME, // SUB2API 创建账号时绑定的分组名。 customPassword: '', // 自定义账号密码;留空时由程序自动生成随机密码。 autoRunSkipFailures: false, // 自动运行遇到失败步骤后,是否继续执行后续流程。 + autoRunFallbackThreadIntervalMinutes: 0, // 兜底模式下,两轮线程之间的等待分钟数。 + autoRunDelayEnabled: false, // 自动运行是否启用启动前倒计时。 + autoRunDelayMinutes: 30, // 自动运行倒计时分钟数。 + autoStepDelaySeconds: null, // 自动运行每一步执行前的额外等待秒数;0 或空表示不延迟。 + mailProvider: '163', // 验证码邮箱来源(163 / 163-vip / qq / inbucket)。 + autoRunSkipFailures: false, // 自动运行遇到失败步骤后,是否继续执行后续流程。 + autoRunDelayEnabled: false, // 自动运行是否启用启动前倒计时。 + autoRunDelayMinutes: 30, // 自动运行倒计时分钟数。 + autoStepDelaySeconds: null, // 自动运行每一步执行前的额外等待秒数;0 或空表示不延迟。 + mailProvider: '163', // 验证码邮箱来源(163 / 163-vip / qq / inbucket)。 + autoRunSkipFailures: false, // 自动运行遇到失败步骤后,是否继续执行后续流程。 + autoRunFallbackThreadIntervalMinutes: 0, // 兜底模式下,两轮线程之间的等待分钟数。 autoRunDelayEnabled: false, // 自动运行是否启用启动前倒计时。 autoRunDelayMinutes: 30, // 自动运行倒计时分钟数。 autoStepDelaySeconds: null, // 自动运行每一步执行前的额外等待秒数;0 或空表示不延迟。 @@ -73,6 +85,30 @@ const PERSISTED_SETTING_DEFAULTS = { hotmailAccounts: [], }; +const PERSISTED_SETTING_DEFAULTS = { + panelMode: 'cpa', + vpsUrl: '', + vpsPassword: '', + localCpaStep9Mode: DEFAULT_LOCAL_CPA_STEP9_MODE, + sub2apiUrl: DEFAULT_SUB2API_URL, + sub2apiEmail: '', + sub2apiPassword: '', + sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME, + customPassword: '', + autoRunSkipFailures: false, + autoRunFallbackThreadIntervalMinutes: 0, + autoRunDelayEnabled: false, + autoRunDelayMinutes: 30, + autoStepDelaySeconds: null, + mailProvider: '163', + emailGenerator: 'duck', + inbucketHost: '', + inbucketMailbox: '', + cloudflareDomain: '', + cloudflareDomains: [], + hotmailAccounts: [], +}; + const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS); const SETTINGS_EXPORT_SCHEMA_VERSION = 1; const SETTINGS_EXPORT_FILENAME_PREFIX = 'multipage-settings'; @@ -123,6 +159,23 @@ function normalizeAutoRunDelayMinutes(value) { ); } +function normalizeAutoRunFallbackThreadIntervalMinutes(value) { + const rawValue = String(value ?? '').trim(); + if (!rawValue) { + return 0; + } + + const numeric = Number(rawValue); + if (!Number.isFinite(numeric)) { + return 0; + } + + return Math.min( + AUTO_RUN_DELAY_MAX_MINUTES, + Math.max(0, Math.floor(numeric)) + ); +} + function normalizeAutoStepDelaySeconds(value, fallback = null) { const rawValue = String(value ?? '').trim(); if (!rawValue) { @@ -256,6 +309,8 @@ function normalizePersistentSettingValue(key, value) { case 'autoRunSkipFailures': case 'autoRunDelayEnabled': return Boolean(value); + case 'autoRunFallbackThreadIntervalMinutes': + return normalizeAutoRunFallbackThreadIntervalMinutes(value); case 'autoRunDelayMinutes': return normalizeAutoRunDelayMinutes(value); case 'autoStepDelaySeconds': @@ -3435,6 +3490,7 @@ async function autoRunLoop(totalRuns, options = {}) { vpsPassword: prevState.vpsPassword, customPassword: prevState.customPassword, autoRunSkipFailures: prevState.autoRunSkipFailures, + autoRunFallbackThreadIntervalMinutes: prevState.autoRunFallbackThreadIntervalMinutes, autoRunDelayEnabled: prevState.autoRunDelayEnabled, autoRunDelayMinutes: prevState.autoRunDelayMinutes, autoStepDelaySeconds: prevState.autoStepDelaySeconds, @@ -3481,6 +3537,16 @@ async function autoRunLoop(totalRuns, options = {}) { successfulRuns += 1; autoRunCurrentRun = successfulRuns; await addLog(`=== 目标 ${successfulRuns}/${totalRuns} 轮已完成(第 ${attemptRuns} 次尝试成功)===`, 'ok'); + const fallbackThreadIntervalMinutes = normalizeAutoRunFallbackThreadIntervalMinutes( + (await getState()).autoRunFallbackThreadIntervalMinutes + ); + if (autoRunSkipFailures && totalRuns > 1 && successfulRuns < totalRuns && fallbackThreadIntervalMinutes > 0) { + await addLog( + `兜底模式:第 ${successfulRuns}/${totalRuns} 轮已完成,等待 ${fallbackThreadIntervalMinutes} 分钟后再启动下一轮新线程。`, + 'info' + ); + await sleepWithStop(fallbackThreadIntervalMinutes * 60 * 1000); + } continue; } catch (err) { if (isStopError(err)) { diff --git a/sidepanel/sidepanel.css b/sidepanel/sidepanel.css index 64e7c00..883aafc 100644 --- a/sidepanel/sidepanel.css +++ b/sidepanel/sidepanel.css @@ -721,6 +721,72 @@ header { flex-shrink: 0; } +.fallback-inline { + justify-content: space-between; +} + +.fallback-thread-interval { + display: flex; + align-items: center; + gap: 12px; + margin-left: auto; +} + +.toggle-switch { + position: relative; + display: inline-flex; + align-items: center; + flex: 0 0 auto; + cursor: pointer; +} + +.toggle-switch > span:not(.toggle-switch-track) { + display: none; +} + +.toggle-switch input { + position: absolute; + opacity: 0; + width: 1px; + height: 1px; + pointer-events: none; +} + +.toggle-switch-track { + width: 38px; + height: 22px; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--bg-elevated); + display: inline-flex; + align-items: center; + padding: 2px; + transition: background var(--transition), border-color var(--transition), box-shadow var(--transition); +} + +.toggle-switch-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--bg-base); + box-shadow: var(--shadow-sm); + transition: transform var(--transition), background var(--transition); +} + +.toggle-switch input:checked + .toggle-switch-track { + background: var(--blue-soft); + border-color: var(--blue); +} + +.toggle-switch input:checked + .toggle-switch-track .toggle-switch-thumb { + transform: translateX(16px); + background: var(--blue); +} + +.toggle-switch input:disabled + .toggle-switch-track { + opacity: 0.55; +} + .auto-delay-inline { justify-content: space-between; gap: 12px; @@ -1196,6 +1262,24 @@ header { margin-bottom: 14px; } +.modal-option-row { + margin-bottom: 14px; +} + +.modal-option-label { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: var(--text-secondary); + cursor: pointer; +} + +.modal-option-label input[type="checkbox"] { + accent-color: var(--blue); + cursor: pointer; +} + .modal-actions { display: flex; justify-content: flex-end; diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 4d0363a..40bce50 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -172,12 +172,24 @@ -