diff --git a/README.md b/README.md
index 7cf5f51..af19001 100644
--- a/README.md
+++ b/README.md
@@ -75,6 +75,7 @@
- 至少准备一种验证码接收方式:
- DuckDuckGo `@duck.com` + QQ / 163 / Inbucket 转发
- Cloudflare 自定义域邮箱前缀 + QQ / 163 / Inbucket 转发
+ - iCloud Hide My Email,可选择直接从 iCloud 收件箱收码,或转发到 QQ / 163 / 163 VIP / 126 / Gmail 后收码
- 手动填写一个可收信邮箱
- 如果使用 `QQ` / `163` / `163 VIP` / `126` / `Inbucket`,对应页面需要提前能正常打开
@@ -592,6 +593,7 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。
支持:
- `Hotmail`(远程服务 / 本地助手)
+- `iCloud` 收件箱,或 iCloud Hide My Email 转发到 QQ / 163 / 163 VIP / 126 / Gmail 后收码
- `content/qq-mail.js`
- `content/mail-163.js`(163 / 163 VIP / 126)
- `content/inbucket-mail.js`
@@ -773,7 +775,7 @@ content/utils.js 通用工具:等待元素、点击、日志、停
content/vps-panel.js CPA 面板步骤:内部 OAuth 刷新 / Step 10
content/signup-page.js ChatGPT 官网 + OpenAI 注册/登录页步骤:Step 1 / 2 / 3 / 5 / 7 / 9
hotmail-utils.js Hotmail 收信相关通用辅助
-mail-provider-utils.js 网页邮箱 provider 配置辅助
+mail-provider-utils.js 网页邮箱 provider 与 iCloud 转发收码配置辅助
content/duck-mail.js Duck 邮箱自动获取
content/qq-mail.js QQ 邮箱验证码轮询
content/mail-163.js 163 / 163 VIP / 126 邮箱验证码轮询
diff --git a/background.js b/background.js
index 997b841..f12d704 100644
--- a/background.js
+++ b/background.js
@@ -34,6 +34,7 @@ importScripts(
'luckmail-utils.js',
'cloudflare-temp-email-utils.js',
'icloud-utils.js',
+ 'mail-provider-utils.js',
'content/activation-utils.js'
);
@@ -124,6 +125,11 @@ const {
pickReusableIcloudAlias,
toNormalizedEmailSet,
} = self.IcloudUtils;
+const {
+ getIcloudForwardMailConfig: getSharedIcloudForwardMailConfig,
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+} = self.MailProviderUtils;
const {
isRecoverableStep9AuthFailure,
} = self.MultiPageActivationUtils;
@@ -694,19 +700,6 @@ function normalizeIcloudFetchMode(value = '') {
return normalized === 'always_new' ? 'always_new' : 'reuse_existing';
}
-function normalizeIcloudTargetMailboxType(value = '') {
- return String(value || '').trim().toLowerCase() === 'forward-mailbox'
- ? 'forward-mailbox'
- : 'icloud-inbox';
-}
-
-function normalizeIcloudForwardMailProvider(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized)
- ? normalized
- : 'qq';
-}
-
function normalizeCustomEmailPool(value = []) {
const source = Array.isArray(value)
? value
@@ -6894,29 +6887,6 @@ async function executeStep3(state) {
// Step 4: Get Signup Verification Code (qq-mail.js polls, then fills in signup-page.js)
// ============================================================
-function getIcloudForwardMailConfig(state = {}, provider = 'qq') {
- const normalizedProvider = normalizeIcloudForwardMailProvider(provider);
- if (normalizedProvider === GMAIL_PROVIDER) {
- return {
- source: 'gmail-mail',
- url: 'https://mail.google.com/mail/u/0/#inbox',
- label: 'Gmail 邮箱',
- inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'],
- injectSource: 'gmail-mail',
- };
- }
- if (normalizedProvider === '163') {
- return { source: 'mail-163', url: 'https://mail.163.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '163 邮箱' };
- }
- if (normalizedProvider === '163-vip') {
- return { source: 'mail-163', url: 'https://webmail.vip.163.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '163 VIP 邮箱' };
- }
- if (normalizedProvider === '126') {
- return { source: 'mail-163', url: 'https://mail.126.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '126 邮箱' };
- }
- return { source: 'qq-mail', url: 'https://wx.mail.qq.com/', label: 'QQ 邮箱' };
-}
-
function getMailConfig(state) {
const provider = state.mailProvider || 'qq';
if (provider === 'custom') {
@@ -6933,7 +6903,7 @@ function getMailConfig(state) {
const useForwardMailbox = targetMailboxType === 'forward-mailbox';
if (useForwardMailbox) {
const forwardProvider = normalizeIcloudForwardMailProvider(state?.icloudForwardMailProvider);
- const forwardConfig = getIcloudForwardMailConfig(state, forwardProvider);
+ const forwardConfig = getSharedIcloudForwardMailConfig(forwardProvider);
return {
...forwardConfig,
label: `iCloud 转发(${forwardConfig.label})`,
diff --git a/mail-provider-utils.js b/mail-provider-utils.js
index cccb000..6bd3960 100644
--- a/mail-provider-utils.js
+++ b/mail-provider-utils.js
@@ -1,5 +1,15 @@
const HOTMAIL_PROVIDER = 'hotmail-api';
+const GMAIL_PROVIDER = 'gmail';
const NETEASE_LIST_PATH = '/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D';
+const ICLOUD_TARGET_MAILBOX_TYPE_INBOX = 'icloud-inbox';
+const ICLOUD_TARGET_MAILBOX_TYPE_FORWARD = 'forward-mailbox';
+const ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS = [
+ { value: 'qq', label: 'QQ 邮箱' },
+ { value: '163', label: '163 邮箱' },
+ { value: '163-vip', label: '163 VIP 邮箱' },
+ { value: '126', label: '126 邮箱' },
+ { value: GMAIL_PROVIDER, label: 'Gmail 邮箱' },
+];
function normalizeMailProvider(value = '') {
const normalized = String(value || '').trim().toLowerCase();
@@ -16,6 +26,38 @@ function normalizeMailProvider(value = '') {
}
}
+function normalizeIcloudTargetMailboxType(value = '') {
+ return String(value || '').trim().toLowerCase() === ICLOUD_TARGET_MAILBOX_TYPE_FORWARD
+ ? ICLOUD_TARGET_MAILBOX_TYPE_FORWARD
+ : ICLOUD_TARGET_MAILBOX_TYPE_INBOX;
+}
+
+function normalizeIcloudForwardMailProvider(value = '') {
+ const normalized = String(value || '').trim().toLowerCase();
+ return ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS.some((option) => option.value === normalized)
+ ? normalized
+ : 'qq';
+}
+
+function getIcloudForwardMailProviderOptions() {
+ return ICLOUD_FORWARD_MAIL_PROVIDER_OPTIONS.map((option) => ({ ...option }));
+}
+
+function getIcloudForwardMailConfig(provider = 'qq') {
+ const normalizedProvider = normalizeIcloudForwardMailProvider(provider);
+ if (normalizedProvider === GMAIL_PROVIDER) {
+ return {
+ source: 'gmail-mail',
+ url: 'https://mail.google.com/mail/u/0/#inbox',
+ label: 'Gmail 邮箱',
+ inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'],
+ injectSource: 'gmail-mail',
+ };
+ }
+
+ return getMailProviderConfig({ mailProvider: normalizedProvider });
+}
+
function getMailProviderConfig(state = {}, options = {}) {
const provider = normalizeMailProvider(state.mailProvider);
const normalizeInbucketOrigin = options.normalizeInbucketOrigin || (() => '');
@@ -66,8 +108,13 @@ function getMailProviderConfig(state = {}, options = {}) {
}
const api = {
+ GMAIL_PROVIDER,
HOTMAIL_PROVIDER,
+ getIcloudForwardMailConfig,
+ getIcloudForwardMailProviderOptions,
getMailProviderConfig,
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
normalizeMailProvider,
};
diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html
index 8e2e478..e522de6 100644
--- a/sidepanel/sidepanel.html
+++ b/sidepanel/sidepanel.html
@@ -834,6 +834,7 @@
+
diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js
index 81c427b..514f1e1 100644
--- a/sidepanel/sidepanel.js
+++ b/sidepanel/sidepanel.js
@@ -582,17 +582,28 @@ const normalizeIcloudFetchMode = (value) => {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'always_new' ? 'always_new' : 'reuse_existing';
};
-const normalizeIcloudTargetMailboxType = (value) => {
- return String(value || '').trim().toLowerCase() === 'forward-mailbox'
+const normalizeIcloudTargetMailboxType = window.MailProviderUtils?.normalizeIcloudTargetMailboxType
+ || ((value) => String(value || '').trim().toLowerCase() === 'forward-mailbox'
? 'forward-mailbox'
- : 'icloud-inbox';
-};
-const normalizeIcloudForwardMailProvider = (value) => {
- const normalized = String(value || '').trim().toLowerCase();
- return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized)
- ? normalized
- : 'qq';
-};
+ : 'icloud-inbox');
+const getIcloudForwardMailProviderOptions = window.MailProviderUtils?.getIcloudForwardMailProviderOptions
+ || (() => Array.from(selectIcloudForwardMailProvider?.options || [])
+ .map((option) => ({
+ value: String(option?.value || '').trim().toLowerCase(),
+ label: String(option?.textContent || option?.label || option?.value || '').trim(),
+ }))
+ .filter((option) => option.value));
+const normalizeIcloudForwardMailProvider = window.MailProviderUtils?.normalizeIcloudForwardMailProvider
+ || ((value) => {
+ const normalized = String(value || '').trim().toLowerCase();
+ const options = getIcloudForwardMailProviderOptions();
+ return options.some((option) => option.value === normalized)
+ ? normalized
+ : (options[0]?.value || 'qq');
+ });
+const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = Object.fromEntries(
+ getIcloudForwardMailProviderOptions().map((option) => [option.value, option.label])
+);
const getIcloudLoginUrlForHost = window.IcloudUtils?.getIcloudLoginUrlForHost
|| ((host) => host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : (host === 'icloud.com' ? 'https://www.icloud.com/' : ''));
@@ -1687,16 +1698,8 @@ function collectSettingsPayload() {
const icloudForwardMailProviderValue = typeof selectIcloudForwardMailProvider !== 'undefined'
? selectIcloudForwardMailProvider?.value
: '';
- const normalizedIcloudTargetMailboxType = typeof normalizeIcloudTargetMailboxType === 'function'
- ? normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue)
- : (String(icloudTargetMailboxTypeValue || '').trim().toLowerCase() === 'forward-mailbox'
- ? 'forward-mailbox'
- : 'icloud-inbox');
- const normalizedIcloudForwardMailProvider = typeof normalizeIcloudForwardMailProvider === 'function'
- ? normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue)
- : (['qq', '163', '163-vip', '126', 'gmail'].includes(String(icloudForwardMailProviderValue || '').trim().toLowerCase())
- ? String(icloudForwardMailProviderValue || '').trim().toLowerCase()
- : 'qq');
+ const normalizedIcloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue);
+ const normalizedIcloudForwardMailProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue);
const mail2925UseAccountPool = typeof inputMail2925UseAccountPool !== 'undefined'
? Boolean(inputMail2925UseAccountPool?.checked)
: Boolean(latestState?.mail2925UseAccountPool);
@@ -3229,11 +3232,7 @@ function updateMailProviderUI() {
: (normalizeIcloudHostValue(icloudHostPreferenceValue || latestState?.icloudHostPreference || '')
|| normalizeIcloudHostValue(latestState?.preferredIcloudHost)
|| 'icloud.com');
- const icloudTargetMailboxType = typeof normalizeIcloudTargetMailboxType === 'function'
- ? normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue)
- : (String(icloudTargetMailboxTypeValue || '').trim().toLowerCase() === 'forward-mailbox'
- ? 'forward-mailbox'
- : 'icloud-inbox');
+ const icloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue);
const isIcloudComCnHost = selectedIcloudHost === 'icloud.com.cn';
const showIcloudTargetMailboxType = useIcloudProvider;
const showIcloudForwardMailProvider = useIcloudProvider && icloudTargetMailboxType === 'forward-mailbox';
@@ -3380,12 +3379,10 @@ function updateMailProviderUI() {
autoHintText.textContent = '已启用随机子域名:扩展会按当前选中的 Temp 域名提交,并额外携带 enableRandomSubdomain;是否生效取决于后端 RANDOM_SUBDOMAIN_DOMAINS 配置。';
}
if (autoHintText && useIcloudProvider && showIcloudForwardMailProvider) {
- const forwardProvider = typeof normalizeIcloudForwardMailProvider === 'function'
- ? normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue)
- : (['qq', '163', '163-vip', '126', 'gmail'].includes(String(icloudForwardMailProviderValue || '').trim().toLowerCase())
- ? String(icloudForwardMailProviderValue || '').trim().toLowerCase()
- : 'qq');
- const forwardProviderLabel = MAIL_PROVIDER_LOGIN_CONFIGS[forwardProvider]?.label || '目标邮箱';
+ const forwardProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue);
+ const forwardProviderLabel = ICLOUD_FORWARD_MAIL_PROVIDER_LABELS[forwardProvider]
+ || MAIL_PROVIDER_LOGIN_CONFIGS[forwardProvider]?.label
+ || '目标邮箱';
autoHintText.textContent = `iCloud ${isIcloudComCnHost ? 'com.cn' : ''} 当前使用转发收码:第 4/8 步会从 ${forwardProviderLabel} 轮询验证码。`;
}
if (useHotmail) {
@@ -3589,11 +3586,7 @@ function updateButtonStates() {
selectIcloudTargetMailboxType.disabled = disableIcloudControls;
}
if (typeof selectIcloudForwardMailProvider !== 'undefined' && selectIcloudForwardMailProvider) {
- const normalizedIcloudTargetMailboxType = typeof normalizeIcloudTargetMailboxType === 'function'
- ? normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue)
- : (String(icloudTargetMailboxTypeValue || '').trim().toLowerCase() === 'forward-mailbox'
- ? 'forward-mailbox'
- : 'icloud-inbox');
+ const normalizedIcloudTargetMailboxType = normalizeIcloudTargetMailboxType(icloudTargetMailboxTypeValue);
const allowIcloudForwardMailProvider = isIcloudMailProvider()
&& normalizedIcloudTargetMailboxType === 'forward-mailbox';
selectIcloudForwardMailProvider.disabled = disableIcloudControls || !allowIcloudForwardMailProvider;
diff --git a/tests/background-icloud-mail-provider.test.js b/tests/background-icloud-mail-provider.test.js
index bd967ab..1e26b30 100644
--- a/tests/background-icloud-mail-provider.test.js
+++ b/tests/background-icloud-mail-provider.test.js
@@ -1,6 +1,11 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
+const {
+ getIcloudForwardMailConfig,
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+} = require('../mail-provider-utils.js');
const source = fs.readFileSync('background.js', 'utf8');
@@ -51,6 +56,41 @@ function extractFunction(name) {
return source.slice(start, end);
}
+function createGetMailConfigApi() {
+ const bundle = extractFunction('getMailConfig');
+ return new Function('shared', `
+const ICLOUD_PROVIDER = 'icloud';
+const GMAIL_PROVIDER = 'gmail';
+const HOTMAIL_PROVIDER = 'hotmail-api';
+const LUCKMAIL_PROVIDER = 'luckmail-api';
+const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
+const getSharedIcloudForwardMailConfig = shared.getIcloudForwardMailConfig;
+const normalizeIcloudTargetMailboxType = shared.normalizeIcloudTargetMailboxType;
+const normalizeIcloudForwardMailProvider = shared.normalizeIcloudForwardMailProvider;
+function normalizeIcloudHost(value = '') {
+ const normalized = String(value || '').trim().toLowerCase();
+ return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
+}
+function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
+function getConfiguredIcloudHostPreference(state) {
+ const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase();
+ return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
+}
+function getIcloudLoginUrlForHost(host) {
+ return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
+}
+function getIcloudMailUrlForHost(host) {
+ return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
+}
+${bundle}
+return { getMailConfig };
+`)({
+ getIcloudForwardMailConfig,
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+ });
+}
+
test('normalizeMailProvider keeps icloud provider', () => {
const bundle = extractFunction('normalizeMailProvider');
const api = new Function(`
@@ -69,38 +109,7 @@ return { normalizeMailProvider };
});
test('getMailConfig returns icloud mail tab config with host preference', () => {
- const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`;
- const api = new Function(`
-const ICLOUD_PROVIDER = 'icloud';
-const GMAIL_PROVIDER = 'gmail';
-const HOTMAIL_PROVIDER = 'hotmail-api';
-const LUCKMAIL_PROVIDER = 'luckmail-api';
-const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
-function normalizeIcloudHost(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
-}
-function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
-function getConfiguredIcloudHostPreference(state) {
- const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase();
- return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
-}
-function getIcloudLoginUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
-}
-function getIcloudMailUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
-}
-function normalizeIcloudTargetMailboxType(value = '') {
- return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox';
-}
-function normalizeIcloudForwardMailProvider(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq';
-}
-${bundle}
-return { getMailConfig };
-`)();
+ const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
@@ -114,37 +123,7 @@ return { getMailConfig };
});
test('getMailConfig reuses preferred icloud host when preference is auto', () => {
- const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`;
- const api = new Function(`
-const ICLOUD_PROVIDER = 'icloud';
-const GMAIL_PROVIDER = 'gmail';
-const HOTMAIL_PROVIDER = 'hotmail-api';
-const LUCKMAIL_PROVIDER = 'luckmail-api';
-const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
-function normalizeIcloudHost(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
-}
-function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
-function getConfiguredIcloudHostPreference() {
- return '';
-}
-function getIcloudLoginUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
-}
-function getIcloudMailUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
-}
-function normalizeIcloudTargetMailboxType(value = '') {
- return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox';
-}
-function normalizeIcloudForwardMailProvider(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq';
-}
-${bundle}
-return { getMailConfig };
-`)();
+ const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
@@ -159,28 +138,7 @@ return { getMailConfig };
});
test('getMailConfig keeps provider metadata for 2925 mailboxes', () => {
- const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`;
- const api = new Function(`
-const ICLOUD_PROVIDER = 'icloud';
-const GMAIL_PROVIDER = 'gmail';
-const HOTMAIL_PROVIDER = 'hotmail-api';
-const LUCKMAIL_PROVIDER = 'luckmail-api';
-const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
-function normalizeIcloudHost(value = '') { return String(value || '').trim().toLowerCase(); }
-function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
-function getConfiguredIcloudHostPreference() { return ''; }
-function getIcloudLoginUrlForHost(host) { return host; }
-function getIcloudMailUrlForHost(host) { return host; }
-function normalizeIcloudTargetMailboxType(value = '') {
- return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox';
-}
-function normalizeIcloudForwardMailProvider(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq';
-}
-${bundle}
-return { getMailConfig };
-`)();
+ const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: '2925',
@@ -195,38 +153,7 @@ return { getMailConfig };
});
test('getMailConfig uses icloud inbox config when host is com.cn and target mailbox is icloud inbox', () => {
- const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`;
- const api = new Function(`
-const ICLOUD_PROVIDER = 'icloud';
-const GMAIL_PROVIDER = 'gmail';
-const HOTMAIL_PROVIDER = 'hotmail-api';
-const LUCKMAIL_PROVIDER = 'luckmail-api';
-const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
-function normalizeIcloudHost(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
-}
-function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
-function getConfiguredIcloudHostPreference(state) {
- const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase();
- return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
-}
-function getIcloudLoginUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
-}
-function getIcloudMailUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
-}
-function normalizeIcloudTargetMailboxType(value = '') {
- return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox';
-}
-function normalizeIcloudForwardMailProvider(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq';
-}
-${bundle}
-return { getMailConfig };
-`)();
+ const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
@@ -242,38 +169,7 @@ return { getMailConfig };
});
test('getMailConfig uses forward mailbox config when target mailbox type is forward', () => {
- const bundle = `${extractFunction('getIcloudForwardMailConfig')}\n${extractFunction('getMailConfig')}`;
- const api = new Function(`
-const ICLOUD_PROVIDER = 'icloud';
-const GMAIL_PROVIDER = 'gmail';
-const HOTMAIL_PROVIDER = 'hotmail-api';
-const LUCKMAIL_PROVIDER = 'luckmail-api';
-const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
-function normalizeIcloudHost(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
-}
-function normalizeInbucketOrigin(value) { return String(value || '').trim(); }
-function getConfiguredIcloudHostPreference(state) {
- const normalized = String(state?.icloudHostPreference || '').trim().toLowerCase();
- return normalized === 'icloud.com' || normalized === 'icloud.com.cn' ? normalized : '';
-}
-function getIcloudLoginUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/' : 'https://www.icloud.com/';
-}
-function getIcloudMailUrlForHost(host) {
- return host === 'icloud.com.cn' ? 'https://www.icloud.com.cn/mail/' : 'https://www.icloud.com/mail/';
-}
-function normalizeIcloudTargetMailboxType(value = '') {
- return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox';
-}
-function normalizeIcloudForwardMailProvider(value = '') {
- const normalized = String(value || '').trim().toLowerCase();
- return ['qq', '163', '163-vip', '126', 'gmail'].includes(normalized) ? normalized : 'qq';
-}
-${bundle}
-return { getMailConfig };
-`)();
+ const api = createGetMailConfigApi();
assert.deepEqual(api.getMailConfig({
mailProvider: 'icloud',
diff --git a/tests/background-icloud.test.js b/tests/background-icloud.test.js
index e4ead04..bc3d6a1 100644
--- a/tests/background-icloud.test.js
+++ b/tests/background-icloud.test.js
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
+const {
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+} = require('../mail-provider-utils.js');
const source = fs.readFileSync('background.js', 'utf8');
@@ -55,8 +59,6 @@ const bundle = [
extractFunction('normalizeEmailGenerator'),
extractFunction('getEmailGeneratorLabel'),
extractFunction('normalizeVerificationResendCount'),
- extractFunction('normalizeIcloudTargetMailboxType'),
- extractFunction('normalizeIcloudForwardMailProvider'),
extractFunction('normalizePersistentSettingValue'),
extractFunction('finalizeIcloudAliasAfterSuccessfulFlow'),
].join('\n');
@@ -157,6 +159,8 @@ function findIcloudAliasByEmail(aliases, email) {
function getErrorMessage(error) {
return String(typeof error === 'string' ? error : error?.message || '');
}
+const normalizeIcloudTargetMailboxType = overrides.normalizeIcloudTargetMailboxType;
+const normalizeIcloudForwardMailProvider = overrides.normalizeIcloudForwardMailProvider;
${bundle}
@@ -177,7 +181,10 @@ test('normalizeEmailGenerator and label support icloud', () => {
});
test('normalizePersistentSettingValue handles icloud settings', () => {
- const api = createApi();
+ const api = createApi({
+ normalizeIcloudTargetMailboxType,
+ normalizeIcloudForwardMailProvider,
+ });
assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'icloud.com'), 'icloud.com');
assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'bad-host'), 'auto');
assert.equal(api.normalizePersistentSettingValue('icloudTargetMailboxType', 'forward-mailbox'), 'forward-mailbox');
diff --git a/tests/mail-provider-utils.test.js b/tests/mail-provider-utils.test.js
index a52ce10..8b21e17 100644
--- a/tests/mail-provider-utils.test.js
+++ b/tests/mail-provider-utils.test.js
@@ -3,7 +3,11 @@ const assert = require('node:assert/strict');
const {
HOTMAIL_PROVIDER,
+ getIcloudForwardMailConfig,
+ getIcloudForwardMailProviderOptions,
getMailProviderConfig,
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
normalizeMailProvider,
} = require('../mail-provider-utils.js');
@@ -33,3 +37,29 @@ test('getMailProviderConfig preserves the hotmail provider sentinel', () => {
}
);
});
+
+test('iCloud forward mailbox helpers normalize and expose supported providers', () => {
+ assert.equal(normalizeIcloudTargetMailboxType('forward-mailbox'), 'forward-mailbox');
+ assert.equal(normalizeIcloudTargetMailboxType('unknown'), 'icloud-inbox');
+ assert.equal(normalizeIcloudForwardMailProvider('GMAIL'), 'gmail');
+ assert.equal(normalizeIcloudForwardMailProvider('unknown'), 'qq');
+ assert.deepEqual(
+ getIcloudForwardMailProviderOptions().map((option) => option.value),
+ ['qq', '163', '163-vip', '126', 'gmail']
+ );
+});
+
+test('getIcloudForwardMailConfig reuses shared mailbox provider configs', () => {
+ assert.deepEqual(getIcloudForwardMailConfig('126'), {
+ source: 'mail-163',
+ url: 'https://mail.126.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D',
+ label: '126 邮箱',
+ });
+ assert.deepEqual(getIcloudForwardMailConfig('gmail'), {
+ source: 'gmail-mail',
+ url: 'https://mail.google.com/mail/u/0/#inbox',
+ label: 'Gmail 邮箱',
+ inject: ['content/activation-utils.js', 'content/utils.js', 'content/gmail-mail.js'],
+ injectSource: 'gmail-mail',
+ });
+});
diff --git a/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js b/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js
index afa859f..d553d40 100644
--- a/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js
+++ b/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
+const {
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+} = require('../mail-provider-utils');
const source = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -172,7 +176,7 @@ return {
test('updateMailProviderUI keeps the temp domain selector visible and updates the hint when random subdomain is enabled', () => {
const bundle = extractFunction('updateMailProviderUI');
- const api = new Function(`
+ const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = {
cloudflareTempEmailDomains: ['mail.example.com'],
};
@@ -254,7 +258,7 @@ return {
autoHintText,
calls,
};
- `)();
+ `)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
api.updateMailProviderUI();
assert.equal(api.cloudflareTempEmailSection.style.display, '');
diff --git a/tests/sidepanel-contribution-mode.test.js b/tests/sidepanel-contribution-mode.test.js
index dd5fb33..e794302 100644
--- a/tests/sidepanel-contribution-mode.test.js
+++ b/tests/sidepanel-contribution-mode.test.js
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
+const {
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+} = require('../mail-provider-utils');
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -121,7 +125,7 @@ test('sidepanel html contains contribution mode runtime UI and loads the module
test('collectSettingsPayload omits custom password and local sync settings in contribution mode', () => {
const bundle = extractFunction('collectSettingsPayload');
- const api = new Function(`
+ const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = { contributionMode: true };
let cloudflareDomainEditMode = false;
let cloudflareTempEmailDomainEditMode = false;
@@ -188,7 +192,7 @@ return {
collectSettingsPayload,
setLatestState(nextState) { latestState = nextState; },
};
-`)();
+`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
const contributionPayload = api.collectSettingsPayload();
assert.equal('customPassword' in contributionPayload, false);
diff --git a/tests/sidepanel-icloud-provider.test.js b/tests/sidepanel-icloud-provider.test.js
index 58808e9..cc17ee4 100644
--- a/tests/sidepanel-icloud-provider.test.js
+++ b/tests/sidepanel-icloud-provider.test.js
@@ -79,3 +79,297 @@ return { getSelectedIcloudHostPreference, getMailProviderLoginUrl };
assert.equal(api.getSelectedIcloudHostPreference(), 'icloud.com.cn');
assert.equal(api.getMailProviderLoginUrl(), 'https://www.icloud.com.cn/');
});
+
+test('collectSettingsPayload persists icloud target mailbox settings', () => {
+ const bundle = extractFunction('collectSettingsPayload');
+
+ const api = new Function(`
+let latestState = { contributionMode: false };
+let cloudflareDomainEditMode = false;
+let cloudflareTempEmailDomainEditMode = false;
+const selectCfDomain = { value: '' };
+const selectTempEmailDomain = { value: '' };
+const selectPanelMode = { value: 'cpa' };
+const inputVpsUrl = { value: '' };
+const inputVpsPassword = { value: '' };
+const inputSub2ApiUrl = { value: '' };
+const inputSub2ApiEmail = { value: '' };
+const inputSub2ApiPassword = { value: '' };
+const inputSub2ApiGroup = { value: '' };
+const inputSub2ApiDefaultProxy = { value: '' };
+const inputCodex2ApiUrl = { value: '' };
+const inputCodex2ApiAdminKey = { value: '' };
+const inputPassword = { value: '' };
+const selectMailProvider = { value: 'icloud' };
+const selectEmailGenerator = { value: 'duck' };
+const checkboxAutoDeleteIcloud = { checked: false };
+const selectIcloudHostPreference = { value: 'auto' };
+const selectIcloudFetchMode = { value: 'reuse_existing' };
+const selectIcloudTargetMailboxType = { value: 'forward-mailbox' };
+const selectIcloudForwardMailProvider = { value: 'gmail' };
+const inputPhoneVerificationEnabled = { checked: false };
+const inputAccountRunHistoryTextEnabled = { checked: false };
+const inputAccountRunHistoryHelperBaseUrl = { value: '' };
+const inputInbucketHost = { value: '' };
+const inputInbucketMailbox = { value: '' };
+const inputHotmailRemoteBaseUrl = { value: '' };
+const inputHotmailLocalBaseUrl = { value: '' };
+const inputLuckmailApiKey = { value: '' };
+const inputLuckmailBaseUrl = { value: '' };
+const selectLuckmailEmailType = { value: 'ms_graph' };
+const inputLuckmailDomain = { value: '' };
+const inputTempEmailBaseUrl = { value: '' };
+const inputTempEmailAdminAuth = { value: '' };
+const inputTempEmailCustomAuth = { value: '' };
+const inputTempEmailReceiveMailbox = { value: '' };
+const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputAutoSkipFailures = { checked: false };
+const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
+const inputAutoDelayEnabled = { checked: false };
+const inputAutoDelayMinutes = { value: '30' };
+const inputAutoStepDelaySeconds = { value: '' };
+const inputVerificationResendCount = { value: '4' };
+const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
+function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; }
+function normalizeCloudflareDomainValue(value) { return String(value || '').trim(); }
+function getCloudflareTempEmailDomainsFromState() { return { domains: [], activeDomain: '' }; }
+function normalizeCloudflareTempEmailDomainValue(value) { return String(value || '').trim(); }
+function getSelectedLocalCpaStep9Mode() { return 'submit'; }
+function getSelectedMail2925Mode() { return 'provide'; }
+function buildManagedAliasBaseEmailPayload() { return { gmailBaseEmail: '', mail2925BaseEmail: '', emailPrefix: '' }; }
+function getSelectedHotmailServiceMode() { return 'local'; }
+function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
+function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; }
+function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
+function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
+function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
+function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
+function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
+function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
+function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
+function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; }
+function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; }
+${bundle}
+return { collectSettingsPayload };
+`)();
+
+ const payload = api.collectSettingsPayload();
+ assert.equal(payload.icloudTargetMailboxType, 'forward-mailbox');
+ assert.equal(payload.icloudForwardMailProvider, 'gmail');
+});
+
+test('updateMailProviderUI toggles icloud forward mailbox controls and hint', () => {
+ const bundle = extractFunction('updateMailProviderUI');
+ const createRow = (display = 'none') => ({ style: { display } });
+
+ const api = new Function('createRow', `
+let latestState = { icloudHostPreference: 'icloud.com.cn' };
+let cloudflareDomainEditMode = false;
+let cloudflareTempEmailDomainEditMode = false;
+const ICLOUD_PROVIDER = 'icloud';
+const GMAIL_PROVIDER = 'gmail';
+const GMAIL_ALIAS_GENERATOR = 'gmail-alias';
+const LUCKMAIL_PROVIDER = 'luckmail-api';
+const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
+const HOTMAIL_SERVICE_MODE_REMOTE = 'remote';
+const HOTMAIL_SERVICE_MODE_LOCAL = 'local';
+const rowMail2925Mode = createRow();
+const rowMail2925PoolSettings = createRow();
+const rowEmailPrefix = createRow();
+const rowCustomMailProviderPool = createRow();
+const rowInbucketHost = createRow();
+const rowInbucketMailbox = createRow();
+const rowEmailGenerator = createRow();
+const rowCfDomain = createRow();
+const rowTempEmailBaseUrl = createRow();
+const rowTempEmailAdminAuth = createRow();
+const rowTempEmailCustomAuth = createRow();
+const rowTempEmailReceiveMailbox = createRow();
+const rowTempEmailRandomSubdomainToggle = createRow();
+const rowTempEmailDomain = createRow();
+const cloudflareTempEmailSection = createRow();
+const hotmailSection = createRow();
+const mail2925Section = createRow();
+const luckmailSection = createRow();
+const icloudSection = createRow();
+const rowIcloudTargetMailboxType = createRow();
+const rowIcloudForwardMailProvider = createRow();
+const labelEmailPrefix = { textContent: '' };
+const inputEmailPrefix = { placeholder: '', style: { display: '' }, readOnly: false };
+const labelMail2925UseAccountPool = createRow();
+const selectMail2925PoolAccount = { style: { display: 'none' }, disabled: false };
+const btnFetchEmail = { hidden: false, disabled: false, textContent: '' };
+const btnMailLogin = { disabled: false, textContent: '', title: '' };
+const inputEmail = { readOnly: false, placeholder: '', value: '' };
+const autoHintText = { textContent: '' };
+const rowHotmailServiceMode = createRow();
+const rowHotmailRemoteBaseUrl = createRow();
+const rowHotmailLocalBaseUrl = createRow();
+const inputMail2925UseAccountPool = { checked: false };
+const selectMailProvider = { value: 'icloud' };
+const selectEmailGenerator = { value: 'duck', disabled: false, options: [] };
+const selectIcloudTargetMailboxType = { value: 'icloud-inbox' };
+const selectIcloudForwardMailProvider = { value: 'gmail' };
+const selectIcloudHostPreference = { value: 'icloud.com.cn' };
+const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputRunCount = { disabled: false };
+const currentAutoRun = { autoRunning: false };
+const MAIL_PROVIDER_LOGIN_CONFIGS = { gmail: { label: 'Gmail 邮箱' } };
+const ICLOUD_FORWARD_MAIL_PROVIDER_LABELS = { gmail: 'Gmail 邮箱' };
+function normalizeIcloudHost(value) { return String(value || '').trim().toLowerCase(); }
+function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; }
+function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; }
+function getSelectedIcloudHostPreference() { return selectIcloudHostPreference.value; }
+function isLuckmailProvider() { return false; }
+function isCustomMailProvider() { return false; }
+function isIcloudMailProvider() { return selectMailProvider.value === ICLOUD_PROVIDER; }
+function usesCustomMailProviderPool() { return false; }
+function usesGeneratedAliasMailProvider() { return false; }
+function getSelectedMail2925Mode() { return 'provide'; }
+function getManagedAliasProviderUiCopy() { return null; }
+function getCurrentRegistrationEmailUiCopy() { return { buttonLabel: '获取邮箱', placeholder: '邮箱', label: '邮箱' }; }
+function updateMailLoginButtonState() {}
+function getSelectedHotmailServiceMode() { return 'local'; }
+function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; }
+function setCloudflareDomainEditMode() {}
+function getCloudflareTempEmailDomainsFromState() { return { domains: [], activeDomain: '' }; }
+function setCloudflareTempEmailDomainEditMode() {}
+function queueIcloudAliasRefresh() {}
+function hideIcloudLoginHelp() {}
+function syncMail2925PoolAccountOptions() {}
+function getMail2925Accounts() { return []; }
+function renderHotmailAccounts() {}
+function renderMail2925Accounts() {}
+function renderLuckmailPurchases() {}
+function getSelectedEmailGenerator() { return selectEmailGenerator.value; }
+function isAutoRunLockedPhase() { return false; }
+function getCurrentHotmailEmail() { return ''; }
+function getCurrentLuckmailEmail() { return ''; }
+function getCustomEmailPoolSize() { return 0; }
+function getCustomMailProviderPoolSize() { return 0; }
+function syncRunCountFromCustomEmailPool() {}
+function syncRunCountFromCustomMailProviderPool() {}
+function shouldLockRunCountToEmailPool() { return false; }
+${bundle}
+return {
+ updateMailProviderUI,
+ rowIcloudTargetMailboxType,
+ rowIcloudForwardMailProvider,
+ selectIcloudTargetMailboxType,
+ autoHintText,
+};
+`)(createRow);
+
+ api.updateMailProviderUI();
+ assert.equal(api.rowIcloudTargetMailboxType.style.display, '');
+ assert.equal(api.rowIcloudForwardMailProvider.style.display, 'none');
+
+ api.selectIcloudTargetMailboxType.value = 'forward-mailbox';
+ api.updateMailProviderUI();
+ assert.equal(api.rowIcloudForwardMailProvider.style.display, '');
+ assert.match(api.autoHintText.textContent, /Gmail 邮箱/);
+});
+
+test('applySettingsState restores icloud forward mailbox settings before UI refresh', () => {
+ const bundle = extractFunction('applySettingsState');
+ const calls = [];
+
+ const api = new Function('calls', `
+let latestState = {};
+const inputEmail = { value: '' };
+const inputVpsUrl = { value: '' };
+const inputVpsPassword = { value: '' };
+const selectPanelMode = { value: 'cpa' };
+const inputSub2ApiUrl = { value: '' };
+const inputSub2ApiEmail = { value: '' };
+const inputSub2ApiPassword = { value: '' };
+const inputSub2ApiGroup = { value: '' };
+const inputSub2ApiDefaultProxy = { value: '' };
+const inputCodex2ApiUrl = { value: '' };
+const inputCodex2ApiAdminKey = { value: '' };
+const ICLOUD_PROVIDER = 'icloud';
+const GMAIL_PROVIDER = 'gmail';
+const GMAIL_ALIAS_GENERATOR = 'gmail-alias';
+const CUSTOM_EMAIL_POOL_GENERATOR = 'custom-pool';
+const selectMailProvider = { value: '163' };
+const selectEmailGenerator = { value: 'duck' };
+const selectIcloudHostPreference = { value: 'auto' };
+const selectIcloudFetchMode = { value: 'reuse_existing' };
+const selectIcloudTargetMailboxType = { value: 'icloud-inbox' };
+const selectIcloudForwardMailProvider = { value: 'qq' };
+const checkboxAutoDeleteIcloud = { checked: false };
+const inputAccountRunHistoryHelperBaseUrl = { value: '' };
+const inputContributionNickname = { value: '' };
+const inputContributionQq = { value: '' };
+const inputMail2925UseAccountPool = { checked: false };
+const inputInbucketHost = { value: '' };
+const inputInbucketMailbox = { value: '' };
+const inputCustomMailProviderPool = { value: '' };
+const inputCustomEmailPool = { value: '' };
+const inputHotmailRemoteBaseUrl = { value: '' };
+const inputHotmailLocalBaseUrl = { value: '' };
+const inputLuckmailApiKey = { value: '' };
+const inputLuckmailBaseUrl = { value: '' };
+const selectLuckmailEmailType = { value: 'ms_graph' };
+const inputLuckmailDomain = { value: '' };
+const inputAutoSkipFailures = { checked: false };
+const inputAutoSkipFailuresThreadIntervalMinutes = { value: '' };
+const inputAutoDelayEnabled = { checked: false };
+const inputAutoDelayMinutes = { value: '' };
+const inputAutoStepDelaySeconds = { value: '' };
+const inputVerificationResendCount = { value: '' };
+const inputPhoneVerificationEnabled = { checked: false };
+const DEFAULT_PHONE_VERIFICATION_ENABLED = false;
+const inputHeroSmsApiKey = { value: '' };
+const selectHeroSmsCountry = { value: '52', options: [{ value: '52' }] };
+const inputRunCount = { value: '' };
+const DEFAULT_VERIFICATION_RESEND_COUNT = 4;
+function syncLatestState(state) { latestState = { ...latestState, ...state }; }
+function syncAutoRunState() {}
+function syncPasswordField() {}
+function setLocalCpaStep9Mode() {}
+function isCustomMailProvider() { return false; }
+function setMail2925Mode() {}
+function normalizeIcloudFetchMode(value) { return String(value || '') === 'always_new' ? 'always_new' : 'reuse_existing'; }
+function normalizeIcloudTargetMailboxType(value) { return String(value || '').trim().toLowerCase() === 'forward-mailbox' ? 'forward-mailbox' : 'icloud-inbox'; }
+function normalizeIcloudForwardMailProvider(value) { return String(value || '').trim().toLowerCase() === 'gmail' ? 'gmail' : 'qq'; }
+function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
+function setManagedAliasBaseEmailInputForProvider() {}
+function normalizeCustomEmailPoolEntries(value) { return Array.isArray(value) ? value : []; }
+function setHotmailServiceMode() {}
+function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
+function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; }
+function applyCloudflareTempEmailSettingsState() {}
+function renderCloudflareDomainOptions() {}
+function setCloudflareDomainEditMode() {}
+function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
+function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
+function formatAutoStepDelayInputValue(value) { return value == null ? '' : String(value); }
+function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
+function normalizeHeroSmsCountryId() { return 52; }
+function getSelectedHeroSmsCountryOption() { return { label: 'Thailand' }; }
+function updateHeroSmsPlatformDisplay() {}
+function applyAutoRunStatus() {}
+function markSettingsDirty() {}
+function updateAutoDelayInputState() {}
+function updateFallbackThreadIntervalInputState() {}
+function updateAccountRunHistorySettingsUI() {}
+function updatePhoneVerificationSettingsUI() {}
+function updatePanelModeUI() {}
+function updateMailProviderUI() { calls.push({ target: selectIcloudTargetMailboxType.value, provider: selectIcloudForwardMailProvider.value }); }
+function isLuckmailProvider() { return false; }
+function updateButtonStates() {}
+${bundle}
+return { applySettingsState, selectIcloudTargetMailboxType, selectIcloudForwardMailProvider };
+`)(calls);
+
+ api.applySettingsState({
+ mailProvider: 'icloud',
+ icloudTargetMailboxType: 'forward-mailbox',
+ icloudForwardMailProvider: 'gmail',
+ });
+
+ assert.equal(api.selectIcloudTargetMailboxType.value, 'forward-mailbox');
+ assert.equal(api.selectIcloudForwardMailProvider.value, 'gmail');
+ assert.deepEqual(calls.at(-1), { target: 'forward-mailbox', provider: 'gmail' });
+});
diff --git a/tests/sidepanel-mail2925-base-email.test.js b/tests/sidepanel-mail2925-base-email.test.js
index 492b1a7..aa6695e 100644
--- a/tests/sidepanel-mail2925-base-email.test.js
+++ b/tests/sidepanel-mail2925-base-email.test.js
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
+const {
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+} = require('../mail-provider-utils');
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -148,7 +152,7 @@ test('collectSettingsPayload persists currentMail2925AccountId for 2925 account
extractFunction('collectSettingsPayload'),
].join('\n');
- const api = new Function(`
+ const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = {
contributionMode: false,
mail2925UseAccountPool: true,
@@ -222,7 +226,7 @@ function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Num
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
${bundle}
return { collectSettingsPayload };
-`)();
+`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
const payload = api.collectSettingsPayload();
diff --git a/tests/sidepanel-phone-verification-settings.test.js b/tests/sidepanel-phone-verification-settings.test.js
index 6d66b87..bc75814 100644
--- a/tests/sidepanel-phone-verification-settings.test.js
+++ b/tests/sidepanel-phone-verification-settings.test.js
@@ -1,6 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
+const {
+ normalizeIcloudForwardMailProvider,
+ normalizeIcloudTargetMailboxType,
+} = require('../mail-provider-utils');
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
@@ -90,7 +94,7 @@ return {
});
test('collectSettingsPayload keeps local helper sync enabled while persisting sms toggle state', () => {
- const api = new Function(`
+ const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = {
contributionMode: false,
mail2925UseAccountPool: false,
@@ -168,7 +172,7 @@ ${extractFunction('normalizeHeroSmsCountryLabel')}
${extractFunction('getSelectedHeroSmsCountryOption')}
${extractFunction('collectSettingsPayload')}
return { collectSettingsPayload };
-`)();
+`)(normalizeIcloudTargetMailboxType, normalizeIcloudForwardMailProvider);
const payload = api.collectSettingsPayload();
diff --git a/项目完整链路说明.md b/项目完整链路说明.md
index 39d7153..835489e 100644
--- a/项目完整链路说明.md
+++ b/项目完整链路说明.md
@@ -163,7 +163,7 @@
- 2925 当前选中的号池账号 ID `currentMail2925AccountId`
- Cloudflare / Temp Email 设置
- 接码开关,以及 HeroSMS 的 API Key 与默认国家设置
-- iCloud 相关偏好
+- iCloud 相关偏好:Host、获取策略、成功后自动删除、目标邮箱类型与转发收码邮箱 provider
- LuckMail API 配置
- 自动运行默认配置
- 账号运行历史 `accountRunHistory`(以邮箱为主键,保存该邮箱最近一次状态:成功/失败/停止)
@@ -605,6 +605,7 @@ Codex2API 补充:
- `163`、`163 VIP`、`126` 都走同一条“网易网页邮箱”验证码链路。
- sidepanel 只负责切换 provider 与展示登录入口;后台根据 provider 选择对应网页邮箱首页。
- 内容脚本来源统一归类到 `mail-163`,这样 Step 4 / Step 8 继续复用同一套验证码读取与邮件清理逻辑。
+- `mail-provider-utils.js` 同时承接 iCloud 转发收码可选 provider 的归一化与入口配置,避免 background / sidepanel 重新复制 QQ、网易和 Gmail 的收码地址、label 与注入脚本。
- `manifest.json` 需要同时覆盖:
- `https://mail.163.com/*`
- `https://webmail.vip.163.com/*`
@@ -699,8 +700,22 @@ Codex2API 补充:
组成:
- [icloud-utils.js](c:/Users/projectf/Downloads/codex注册扩展/icloud-utils.js)
+- [mail-provider-utils.js](c:/Users/projectf/Downloads/codex注册扩展/mail-provider-utils.js)
- [content/icloud-mail.js](c:/Users/projectf/Downloads/codex注册扩展/content/icloud-mail.js)
+配置:
+
+- `icloudHostPreference` 只决定 iCloud 登录、别名管理和 iCloud Mail 收件箱 Host。
+- `icloudFetchMode` 决定生成注册邮箱时复用已有 Hide My Email 别名,还是始终创建新别名。
+- `icloudTargetMailboxType = icloud-inbox` 时,Step 4 / Step 8 直接打开 iCloud Mail 收件箱轮询验证码。
+- `icloudTargetMailboxType = forward-mailbox` 时,注册邮箱仍由 iCloud Hide My Email 生成,但 Step 4 / Step 8 改为打开 `icloudForwardMailProvider` 指定的转发目标邮箱收码;当前支持 `qq / 163 / 163-vip / 126 / gmail`。
+
+维护约定:
+
+- iCloud 转发目标邮箱 provider 的 label、URL、source、inject 配置统一由 `mail-provider-utils.js` 输出。
+- `background.js` 只根据 `icloudTargetMailboxType` 选择 iCloud Mail 收件箱或共享转发邮箱配置,不再在主文件内硬编码各 provider 地址。
+- `sidepanel/sidepanel.js` 只负责展示、保存和回显目标邮箱类型 / 转发邮箱 provider,不重复定义 provider 业务清单。
+
## 8. 自动运行完整链路
文件:
diff --git a/项目文件结构说明.md b/项目文件结构说明.md
index 45966c6..fde9fec 100644
--- a/项目文件结构说明.md
+++ b/项目文件结构说明.md
@@ -26,7 +26,7 @@
- `hotmail-utils.js`:Hotmail 账号与验证码提取相关的纯工具函数,负责账号筛选、验证码匹配、第三方接口数据归一化。
- `icloud-utils.js`:iCloud 隐私邮箱相关的纯工具函数,负责 host、别名列表、保留状态、已用状态等归一化。
- `luckmail-utils.js`:LuckMail 相关的纯工具函数,负责邮箱购买记录、标签、邮件 cursor、验证码匹配等归一化。
-- `mail-provider-utils.js`:网页邮箱 provider 配置纯工具,负责 `163 / 163 VIP / 126 / QQ / Inbucket / Hotmail` 的基础归一化与页面入口配置。
+- `mail-provider-utils.js`:网页邮箱 provider 配置纯工具,负责 `163 / 163 VIP / 126 / QQ / Inbucket / Hotmail` 的基础归一化与页面入口配置,并统一承接 iCloud 转发收码目标邮箱 provider 的归一化、选项列表和收码入口配置。
- `mail2925-utils.js`:2925 账号池相关的纯工具函数,负责账号归一化、冷却期判断、可用账号挑选、批量导入解析与列表更新。
- `managed-alias-utils.js`:共享 Gmail / 2925 的别名邮箱规则,负责解析基邮箱、校验“当前完整注册邮箱”是否仍与基邮箱兼容、生成 Gmail `+tag` 与 2925 随机后缀邮箱,并输出 sidepanel 可复用的 UI 文案;当前还统一承接 `mail2925Mode` 的归一化与“2925 仅在 provide 模式下参与别名生成”的共享判定。
- `manifest.json`:Chrome 扩展清单,声明权限、背景脚本、侧边栏、内容脚本与规则集。
@@ -125,8 +125,8 @@
- `sidepanel/contribution-content-update-service.js`:侧边栏贡献内容更新服务,负责拉取 `https://apikey.qzz.io/api/content-summary`、缓存公开公告/教程摘要,并输出可用于提示展示的 `promptVersion` 与最新更新时间。
- `sidepanel/contribution-mode.js`:侧边栏贡献模式管理器,负责顶部“贡献”按钮、确认弹窗、贡献模式显隐、复用主自动流程启动、侧栏内贡献状态轮询、上传页跳转,以及贡献模式下对来源选择、配置入口、记录入口和敏感配置行(包括 Codex2API 配置)的禁用与隐藏。
- `sidepanel/sidepanel.css`:侧边栏样式文件;当前额外提供 Hotmail / 2925 共用的号池表单容器、操作按钮行与统一的收起态列表高度样式。
-- `sidepanel/sidepanel.html`:侧边栏页面结构;当前步骤列表已改为动态容器,日志区提供“记录”按钮并挂接邮箱记录覆盖层,顶部新增“贡献”按钮并在设置卡片中新增贡献模式主面板;贡献按钮下方额外挂接一个可关闭的轻提示气泡,用于提示公开公告 / 使用教程有更新;贡献面板内展示 `OAUTH / 回调 / 总状态` 三块真实运行态信息,同时把“接码”开关与“验证码重发”拆成同一行展示;只有开启接码后,下面的 HeroSMS 平台、国家与 API Key 行才会显示;页面继续加载 `managed-alias-utils.js`,并把旧的“邮箱前缀”字段语义改为“别名基邮箱”;当 provider 为 2925 时,会额外显示 `提供邮箱 / 接收邮箱` 模式切换,并把“2925 号池”从别名基邮箱行拆成独立配置行,避免 receive 模式把账号池开关一起隐藏;当前在 `邮箱生成` 区域新增 `自定义邮箱池` 选项和多行邮箱池输入框,并在 `邮箱服务 = 自定义邮箱` 时额外显示 `自定义号池` 文本框;来源下拉框当前支持 `CPA / SUB2API / Codex2API`,其中 Codex2API 额外提供后台地址和管理密钥配置行;Hotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添加”按钮和共享表单容器。
-- `sidepanel/sidepanel.js`:侧边栏主入口脚本,负责 UI 状态同步、动态步骤渲染、按钮交互、共享验证码自动重发次数配置与广播接收,并装配 Hotmail / 2925 / iCloud / LuckMail / 贡献模式 / 邮箱记录面板 / 贡献内容更新服务;当前贡献模式的“开始贡献”会直接复用主自动流程启动逻辑,而独立 manager 负责贡献运行态展示与轮询,同时把 Gmail / 2925 的基邮箱输入、完整注册邮箱输入、自动生成按钮与兼容性校验统一接到共享别名逻辑上;当 provider 为 2925 时,会根据 `mail2925Mode` 决定是否启用别名基邮箱链路,而 2925 号池开关与当前账号选择则独立显示并同时服务于 provide / receive 两种模式;`自定义邮箱池` 模式会按邮箱池长度锁定自动轮数,并把当前输入的邮箱池配置参与自动启动前保存;`邮箱服务 = 自定义邮箱` 时,如果配置了 `customMailProviderPool`,也会按号池长度锁定自动轮数并在 Auto 中按轮次分配注册邮箱,同时在普通失败时继续复用当前邮箱,只有成功或出现手机号验证时才切换下一个邮箱;Step 8 的自定义邮箱确认弹窗当前额外提供“出现手机号验证”按钮,用于直接走与真实 add-phone 一致的 fatal 分支;HeroSMS 国家列表会在 sidepanel 初始化时拉取并恢复到本地保存的国家/API 设置,接码开关关闭时会直接隐藏相关配置;账号记录快照同步改为默认自动模式,只要本地 helper 可用就会自动落盘;新来源 Codex2API 在这里仅补充来源配置接线、表单显隐和 Step 10 按钮文案,不承接协议业务逻辑;Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;侧边栏初始化与点击“自动”前会刷新一次贡献站公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示。
+- `sidepanel/sidepanel.html`:侧边栏页面结构;当前步骤列表已改为动态容器,日志区提供“记录”按钮并挂接邮箱记录覆盖层,顶部新增“贡献”按钮并在设置卡片中新增贡献模式主面板;贡献按钮下方额外挂接一个可关闭的轻提示气泡,用于提示公开公告 / 使用教程有更新;贡献面板内展示 `OAUTH / 回调 / 总状态` 三块真实运行态信息,同时把“接码”开关与“验证码重发”拆成同一行展示;只有开启接码后,下面的 HeroSMS 平台、国家与 API Key 行才会显示;页面继续加载 `managed-alias-utils.js` 与 `mail-provider-utils.js`,并把旧的“邮箱前缀”字段语义改为“别名基邮箱”;当 provider 为 2925 时,会额外显示 `提供邮箱 / 接收邮箱` 模式切换,并把“2925 号池”从别名基邮箱行拆成独立配置行,避免 receive 模式把账号池开关一起隐藏;当前在 `邮箱生成` 区域新增 `自定义邮箱池` 选项和多行邮箱池输入框,并在 `邮箱服务 = 自定义邮箱` 时额外显示 `自定义号池` 文本框;当邮箱服务为 iCloud 时,额外提供目标邮箱类型与转发邮箱 provider 配置,用于选择直接从 iCloud 收件箱收码或从 QQ / 网易 / Gmail 转发目标邮箱收码;来源下拉框当前支持 `CPA / SUB2API / Codex2API`,其中 Codex2API 额外提供后台地址和管理密钥配置行;Hotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添加”按钮和共享表单容器。
+- `sidepanel/sidepanel.js`:侧边栏主入口脚本,负责 UI 状态同步、动态步骤渲染、按钮交互、共享验证码自动重发次数配置与广播接收,并装配 Hotmail / 2925 / iCloud / LuckMail / 贡献模式 / 邮箱记录面板 / 贡献内容更新服务;当前贡献模式的“开始贡献”会直接复用主自动流程启动逻辑,而独立 manager 负责贡献运行态展示与轮询,同时把 Gmail / 2925 的基邮箱输入、完整注册邮箱输入、自动生成按钮与兼容性校验统一接到共享别名逻辑上;当 provider 为 2925 时,会根据 `mail2925Mode` 决定是否启用别名基邮箱链路,而 2925 号池开关与当前账号选择则独立显示并同时服务于 provide / receive 两种模式;当 provider 为 iCloud 时,会保存并回显目标邮箱类型与转发邮箱 provider,相关 provider 选项和 label 复用 `mail-provider-utils.js`;`自定义邮箱池` 模式会按邮箱池长度锁定自动轮数,并把当前输入的邮箱池配置参与自动启动前保存;`邮箱服务 = 自定义邮箱` 时,如果配置了 `customMailProviderPool`,也会按号池长度锁定自动轮数并在 Auto 中按轮次分配注册邮箱,同时在普通失败时继续复用当前邮箱,只有成功或出现手机号验证时才切换下一个邮箱;Step 8 的自定义邮箱确认弹窗当前额外提供“出现手机号验证”按钮,用于直接走与真实 add-phone 一致的 fatal 分支;HeroSMS 国家列表会在 sidepanel 初始化时拉取并恢复到本地保存的国家/API 设置,接码开关关闭时会直接隐藏相关配置;账号记录快照同步改为默认自动模式,只要本地 helper 可用就会自动落盘;新来源 Codex2API 在这里仅补充来源配置接线、表单显隐和 Step 10 按钮文案,不承接协议业务逻辑;Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;侧边栏初始化与点击“自动”前会刷新一次贡献站公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示。
- `sidepanel/update-service.js`:侧边栏更新检查服务,负责 GitHub Releases 查询、`Pro` / `v` 双版本族排序、缓存读取与版本展示。
## `tests/`
@@ -146,7 +146,7 @@
- `tests/background-custom-email-pool.test.js`:测试后台对自定义邮箱池生成方式,以及自定义邮箱服务号池的归一化、标签文案和按轮次取邮箱逻辑。
- `tests/background-generated-email-module.test.js`:测试生成邮箱辅助模块已接入且导出工厂,并覆盖 2925 receive 模式回退普通邮箱生成、自定义邮箱池按轮次取值、2925 provide 模式账号池预热,以及 iCloud `always_new` 传参。
- `tests/background-icloud.test.js`:测试 iCloud 相关后台纯函数与别名收尾逻辑。
-- `tests/background-icloud-mail-provider.test.js`:测试 iCloud 邮箱 provider 配置解析。
+- `tests/background-icloud-mail-provider.test.js`:测试 iCloud 邮箱 provider 配置解析,并覆盖转发收码模式复用共享 provider 配置。
- `tests/background-logging-status-module.test.js`:测试日志 / 状态模块已接入且导出工厂。
- `tests/background-luckmail.test.js`:测试 LuckMail 相关后台逻辑,如购买、复用、标记已用与重置。
- `tests/background-mail2925-session-module.test.js`:测试 2925 会话模块已接入且导出工厂,并覆盖命中上限后“禁用 24 小时 + 切下一个号 + 自动登录”的核心链路。
@@ -176,7 +176,7 @@
- `tests/icloud-mail-content.test.js`:测试 iCloud Mail 内容脚本读取邮件正文和选中状态逻辑。
- `tests/icloud-utils.test.js`:测试 iCloud 工具层的 host、别名列表、保留状态与筛选逻辑。
- `tests/luckmail-utils.test.js`:测试 LuckMail 工具层的购买记录、邮件、游标和验证码匹配逻辑。
-- `tests/mail-provider-utils.test.js`:测试网页邮箱 provider 配置工具对 `126` 等 provider 的归一化与入口配置解析。
+- `tests/mail-provider-utils.test.js`:测试网页邮箱 provider 配置工具对 `126` 等 provider 的归一化与入口配置解析,以及 iCloud 转发收码 provider 选项、归一化和配置复用。
- `tests/mail-2925-content.test.js`:测试 2925 邮箱内容脚本的轮询快照、忽略邮箱对比、按步骤会话隔离已试验证码、单封邮件删除与整箱删除逻辑。
- `tests/mail2925-utils.test.js`:测试 2925 账号池工具层的账号归一化、冷却期判断、可用账号挑选与批量导入解析。
- `tests/managed-alias-utils.test.js`:覆盖 Gmail / 2925 共享别名工具的解析、生成、兼容性判断。
@@ -189,7 +189,7 @@
- `tests/sidepanel-contribution-mode.test.js`:测试侧边栏贡献模式的 HTML 接线、runtime-only 设置保护,以及贡献模式 manager 复用主自动流启动、状态轮询和退出清理逻辑。
- `tests/sidepanel-auto-run-content-refresh.test.js`:测试点击“自动”时会先刷新贡献内容更新摘要,且刷新失败不会阻塞自动流程启动。
- `tests/sidepanel-icloud-manager.test.js`:测试侧边栏 iCloud 管理器模块接线与空态渲染。
-- `tests/sidepanel-icloud-provider.test.js`:测试侧边栏 iCloud 登录地址解析逻辑。
+- `tests/sidepanel-icloud-provider.test.js`:测试侧边栏 iCloud 登录地址解析、目标邮箱类型 / 转发邮箱 provider 保存、回显和显隐联动。
- `tests/sidepanel-luckmail-manager.test.js`:测试侧边栏 LuckMail 管理器模块接线与空态渲染。
- `tests/sidepanel-mail2925-manager.test.js`:测试侧边栏 2925 管理器模块接线、共享号池表单脚本加载顺序、显隐交互与空态渲染。
- `tests/sidepanel-mail2925-mode.test.js`:测试侧边栏保留 `2925 provide / receive` 模式行与独立的号池配置行,并验证 sidepanel 只在 provide 模式下把 2925 视为别名邮箱 provider。