dsh-email 0.15.2 → 0.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.en.md +3 -0
- package/README.md +3 -0
- package/docs/USAGE.en.md +2 -0
- package/docs/USAGE.md +2 -0
- package/docs/VALIDATION.md +2 -0
- package/docs/validation/0.15.3.md +7 -0
- package/lib/client.js +70 -23
- package/lib/config.d.ts +2 -0
- package/lib/host-config.js +1 -0
- package/lib/settings.d.ts +3 -0
- package/lib/settings.js +5 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
[历史英文记录](docs/CHANGELOG.en.md)
|
|
6
6
|
|
|
7
|
+
## 0.15.3 (2026-10-04)
|
|
8
|
+
|
|
9
|
+
- 完成 [#21](https://github.com/STARDUSTLC666/dsh-email/issues/21):邮箱账号页新增「显示新邮件弹窗」开关,自动保存、重启后保留。关闭时隐藏当前卡片并停止自动邮件检查,不影响手动邮件工具。
|
|
10
|
+
- 重新开启建立新基线,不补弹关闭期间的旧邮件;跨页面保存的开关在下一次本地设置刷新时生效。关闭或卸载后,迟到的网络响应不能重新显示弹窗。
|
|
11
|
+
- 验收范围见 [0.15.3 通知开关检查](docs/validation/0.15.3.md)。
|
|
12
|
+
|
|
7
13
|
## 0.15.2 (2026-10-03)
|
|
8
14
|
|
|
9
15
|
- 修复语言切换后设置菜单滞后一轮;英文草稿、账号、发送失败引导不再回退中文,保留结果不明时禁止盲目重发的提示。
|
package/README.en.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[中文](README.md)
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
Read, send, search and organize email from DSH with multiple accounts.
|
|
6
8
|
|
|
7
9
|
[](https://www.npmjs.com/package/dsh-email) [](https://www.npmjs.com/package/dsh-email)
|
|
@@ -11,6 +13,7 @@ Read, send, search and organize email from DSH with multiple accounts.
|
|
|
11
13
|
- Read, send, reply and forward using IMAP and SMTP.
|
|
12
14
|
- Manage attachments, search results and mailbox organization.
|
|
13
15
|
- Configure multiple accounts, Outlook OAuth2 and send approval.
|
|
16
|
+
- Turn new-mail popups off in settings; the preference survives restarts.
|
|
14
17
|
- Prepare and edit local drafts, review To/Cc, content and attachment bytes, then confirm sending.
|
|
15
18
|
- Manage trusted recipients per account and preview address, group and Cc matches.
|
|
16
19
|
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.en.md)
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
在 DSH 中收发、搜索和整理邮件,支持多个邮箱账号。
|
|
6
8
|
|
|
7
9
|
[](https://www.npmjs.com/package/dsh-email) [](https://www.npmjs.com/package/dsh-email)
|
|
@@ -11,6 +13,7 @@
|
|
|
11
13
|
- 通过 IMAP / SMTP 收发、回复和转发邮件。
|
|
12
14
|
- 管理附件、搜索结果与邮箱整理操作。
|
|
13
15
|
- 多账号设置,支持 Outlook OAuth2 和发信审批。
|
|
16
|
+
- 新邮件弹窗可在设置中关闭,保存后重启仍生效。
|
|
14
17
|
- 准备和编辑本地草稿,核对发件人、To/Cc、正文与附件预览后再确认发送。
|
|
15
18
|
- 按账号管理可信收件人,预览规则如何匹配地址、地址组和抄送。
|
|
16
19
|
|
package/docs/USAGE.en.md
CHANGED
|
@@ -24,6 +24,8 @@ See the [draft workbench](DRAFTS.en.md) for editable drafts and per-account trus
|
|
|
24
24
|
|
|
25
25
|
Once an account is configured, a "whale-girl courier" popup lives in the bottom-right corner of the main UI: it checks for new mail every 30 seconds and shows a card (sender + subject) when something arrives, auto-dismissing after 12 seconds. The popup shares the cursor logic with `email_watch` but keeps its own counter, so they never consume each other's mail.
|
|
26
26
|
|
|
27
|
+
In Settings → Email → Accounts, turn off **Show new-mail popups** to hide the current popup and stop automatic mail checks. This setting saves automatically and survives a restart; it defaults to on. Re-enabling first seeds a fresh baseline, without replaying mail from the disabled period. Checks pause while the page is hidden. A change saved in another page takes effect on the next local-settings refresh. Conversation tools, including `email_watch`, remain available.
|
|
28
|
+
|
|
27
29
|
The popup artwork prefers the locally installed [dsh-deep-whale](https://github.com/Small-tailqwq/dsh-deep-whale) whale-girl skin assets (**never bundled** — read at runtime from your own installation): the artwork is a derivative of the original whale-girl character by [上善](https://www.pixiv.net/users/62155430) (skin by Small-tailqwq), published under CC BY-NC-SA 4.0 (Attribution-NonCommercial-ShareAlike); the popup shows the full attribution chain. Without the skin, a bundled community whale-girl artwork is used (copyright stays with the original author, personal non-commercial use only; removed on request via issue).
|
|
28
30
|
|
|
29
31
|
Example:
|
package/docs/USAGE.md
CHANGED
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
|
|
25
25
|
配置好账号后,主界面右下角会出现「鲸鱼娘递信」小弹窗:每 30 秒检查一次新邮件,有新邮件时弹出卡片(发件人 + 主题),12 秒自动消失。弹窗与 `email_watch` 工具共用同一套游标逻辑但各自独立计数,互不抢占。
|
|
26
26
|
|
|
27
|
+
在「设置 → 邮件 → 邮箱账号」取消勾选「显示新邮件弹窗」,立即隐藏当前弹窗并停止自动检查;设置会自动保存,重启后仍生效。默认开启。再次开启时先建立新的基线,不补弹关闭期间的旧邮件。页面隐藏时暂停检查;另一个页面保存的开关在下一次本地设置刷新时生效。对话中的 `email_watch` 等邮件工具照常可用。
|
|
28
|
+
|
|
27
29
|
弹窗形象优先使用本地安装的 [dsh-deep-whale](https://github.com/Small-tailqwq/dsh-deep-whale) 鲸鱼娘皮肤素材(**不打包分发**,运行时从你自己的安装目录读取):该素材为一创 [上善](https://www.pixiv.net/users/62155430) 鲸鱼娘形象的衍生创作(二创 Small-tailqwq),以 CC BY-NC-SA 4.0(署名-非商业性使用-相同方式共享)发布,弹窗内附完整署名链。未安装皮肤时使用内置的社区鲸鱼娘形象(版权归原作者,仅供个人非商业使用;如有异议请提 Issue,会立即移除)。
|
|
28
30
|
|
|
29
31
|
示例对话:
|
package/docs/VALIDATION.md
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 0.15.3 通知开关验收 · 2026-10-04
|
|
2
|
+
|
|
3
|
+
Windows / Node 24.16.0:334 项测试全部通过。新增回归覆盖默认开启、持久化、不重建连接池、关闭时停止自动检查、跨页面重新开启、静默基线,以及忽略取消信号的迟到响应在关闭或卸载后不能复活弹窗。
|
|
4
|
+
|
|
5
|
+
在官方源码构建的 DSH 0.2.0-rc.2(639ed01539)独立 profile 中操作了中文和英文设置页,关闭开关、自动保存、键盘 Space 重新开启及页面重新加载。主界面实际显示了合成新邮件弹窗,再从邮件设置关闭开关,卡片被移除。设置写入独立配置文件,测试账号和合成新邮件不含真实邮箱凭据。
|
|
6
|
+
|
|
7
|
+
网络和通知测试不发送邮件,也不改变对话中的邮件工具权限。真实邮箱登录、SMTP 投递及原生桌面窗口未在本轮验收;网页通过不能替代原生桌面验收。
|
package/lib/client.js
CHANGED
|
@@ -32,7 +32,7 @@ function emailError(error) {
|
|
|
32
32
|
/** 「编辑即保存」的防抖窗口:连续打字只落一次盘。 */
|
|
33
33
|
const SAVE_DEBOUNCE_MS = 800;
|
|
34
34
|
|
|
35
|
-
async function api(action, payload) {
|
|
35
|
+
async function api(action, payload, signal) {
|
|
36
36
|
const init = action === undefined
|
|
37
37
|
? { credentials: "same-origin" }
|
|
38
38
|
: {
|
|
@@ -41,7 +41,7 @@ async function api(action, payload) {
|
|
|
41
41
|
headers: { "Content-Type": "application/json" },
|
|
42
42
|
body: JSON.stringify(Object.assign({ action }, payload)),
|
|
43
43
|
};
|
|
44
|
-
const res = await fetch(ROUTE, init);
|
|
44
|
+
const res = await fetch(ROUTE, { ...init, signal });
|
|
45
45
|
const body = await res.json();
|
|
46
46
|
if (!res.ok || !body.ok) {
|
|
47
47
|
const error = new Error((body && body.error && body.error.message) || ("request failed " + res.status));
|
|
@@ -138,6 +138,7 @@ const EMPTY = {
|
|
|
138
138
|
password: "",
|
|
139
139
|
inboxFolder: "INBOX",
|
|
140
140
|
sendApproval: true,
|
|
141
|
+
newMailPopup: true,
|
|
141
142
|
downloadDir: "",
|
|
142
143
|
accountsYaml: "",
|
|
143
144
|
serverPresets: "",
|
|
@@ -290,7 +291,7 @@ var UI = {
|
|
|
290
291
|
"global.sendApproval": "发信前弹确认(强烈建议保留;Full Access 模式下会被自动拒绝)",
|
|
291
292
|
"global.downloadDirLabel": "附件下载目录(默认保存在当前工作区)",
|
|
292
293
|
"global.downloadDirPlaceholder": "留空使用默认",
|
|
293
|
-
"global.hint": "
|
|
294
|
+
"global.hint": "以上设置对所有账号统一生效。",
|
|
294
295
|
|
|
295
296
|
// 自动保存状态条
|
|
296
297
|
"status.saved": "已保存",
|
|
@@ -471,7 +472,7 @@ var UI = {
|
|
|
471
472
|
"global.sendApproval": "Ask for confirmation before sending (strongly recommended; auto-denied under Full Access)",
|
|
472
473
|
"global.downloadDirLabel": "Attachment download directory (defaults to the current workspace)",
|
|
473
474
|
"global.downloadDirPlaceholder": "Leave empty for the default",
|
|
474
|
-
"global.hint": "These
|
|
475
|
+
"global.hint": "These settings apply to every account.",
|
|
475
476
|
|
|
476
477
|
// 自动保存状态条
|
|
477
478
|
"status.saved": "Saved",
|
|
@@ -2300,6 +2301,11 @@ function EmailSettingsSection() {
|
|
|
2300
2301
|
const [tab, setTab] = useState(emailActiveTab);
|
|
2301
2302
|
useEffect(() => { emailActiveTab = tab; }, [tab]);
|
|
2302
2303
|
const [draft, setDraft] = useState(null);
|
|
2304
|
+
useEffect(() => {
|
|
2305
|
+
if (draft === null) return undefined;
|
|
2306
|
+
updateWhalePreference?.(draft.newMailPopup !== false);
|
|
2307
|
+
return () => updateWhalePreference?.(null);
|
|
2308
|
+
}, [draft?.newMailPopup]);
|
|
2303
2309
|
const [snapshot, setSnapshot] = useState(undefined);
|
|
2304
2310
|
const [busy, setBusy] = useState(false);
|
|
2305
2311
|
const [error, setError] = useState("");
|
|
@@ -2592,6 +2598,16 @@ function EmailSettingsSection() {
|
|
|
2592
2598
|
// 全局设置:发信确认和下载目录对所有账号一视同仁,不属于任何一张卡片,
|
|
2593
2599
|
// 所以摆在卡片列表上方,而不是塞进某一张卡里。
|
|
2594
2600
|
h("div", { className: "dshe-global" }, [
|
|
2601
|
+
h("label", { className: "dshe-check" }, [
|
|
2602
|
+
h("input", {
|
|
2603
|
+
type: "checkbox",
|
|
2604
|
+
checked: draft.newMailPopup !== false,
|
|
2605
|
+
disabled: snapshot?.writable === false,
|
|
2606
|
+
onChange: (e) => update({ newMailPopup: e.target.checked }),
|
|
2607
|
+
}),
|
|
2608
|
+
dt("显示新邮件弹窗", "Show new-mail popups"),
|
|
2609
|
+
]),
|
|
2610
|
+
h("div", { className: "dshe-hint" }, dt("关闭后立即隐藏弹窗并停止自动检查,不影响对话中手动查看邮件。重新开启时从当前邮件开始提醒。", "Turning this off hides the popup and stops automatic mail checks. You can still check mail in a conversation. Turning it back on starts with a fresh baseline.")),
|
|
2595
2611
|
h("label", { className: "dshe-check" }, [
|
|
2596
2612
|
h("input", {
|
|
2597
2613
|
type: "checkbox",
|
|
@@ -2641,6 +2657,7 @@ function EmailSettingsSection() {
|
|
|
2641
2657
|
// when newCount > 0. The artwork URL/credit come from the settings snapshot
|
|
2642
2658
|
// (skin artwork is served from the user's local dsh-deep-whale install with
|
|
2643
2659
|
// its CC BY-NC-SA attribution chain; otherwise a built-in fallback is used).
|
|
2660
|
+
let updateWhalePreference = null;
|
|
2644
2661
|
function startWhaleWidget() {
|
|
2645
2662
|
if (document.querySelector("#dsh-email-whale-root")) return () => {};
|
|
2646
2663
|
const root = document.createElement("div");
|
|
@@ -2651,17 +2668,26 @@ function startWhaleWidget() {
|
|
|
2651
2668
|
let hideTimer = 0;
|
|
2652
2669
|
let whaleUrl = "";
|
|
2653
2670
|
let whaleCredit = "";
|
|
2671
|
+
let enabled = true;
|
|
2672
|
+
let localPreference = null;
|
|
2673
|
+
let disposed = false;
|
|
2674
|
+
let running = false;
|
|
2675
|
+
let generation = 0;
|
|
2676
|
+
let seedBaseline = true;
|
|
2677
|
+
let watchAbort = null;
|
|
2654
2678
|
/** 当前弹窗的数据。语言切了就靠它原样重画一遍,无需再拉一次接口。 */
|
|
2655
2679
|
let current = null;
|
|
2656
2680
|
|
|
2657
2681
|
const closePopup = () => {
|
|
2658
2682
|
if (hideTimer) { clearTimeout(hideTimer); hideTimer = 0; }
|
|
2659
2683
|
root.replaceChildren();
|
|
2684
|
+
current = null;
|
|
2660
2685
|
};
|
|
2661
2686
|
|
|
2662
2687
|
const showPopup = (value) => {
|
|
2663
|
-
current = value;
|
|
2664
2688
|
closePopup();
|
|
2689
|
+
if (disposed || !enabled) return;
|
|
2690
|
+
current = value;
|
|
2665
2691
|
const card = document.createElement("div");
|
|
2666
2692
|
card.className = "dshe-whale-card";
|
|
2667
2693
|
if (whaleUrl) {
|
|
@@ -2709,46 +2735,63 @@ function startWhaleWidget() {
|
|
|
2709
2735
|
};
|
|
2710
2736
|
|
|
2711
2737
|
// 轮询策略:页面不可见时完全不打扰邮箱服务器;失败指数退避(上限 10 分钟),
|
|
2712
|
-
// 成功立刻回到 30s
|
|
2738
|
+
// 成功立刻回到 30s;本地设置快照同样刷新,以便跨页面的通知开关生效。
|
|
2713
2739
|
const POLL_MS = 30000;
|
|
2714
|
-
const SNAPSHOT_MS = 600000;
|
|
2715
2740
|
const MAX_BACKOFF_MS = 600000;
|
|
2716
2741
|
let failures = 0;
|
|
2717
|
-
|
|
2718
|
-
|
|
2742
|
+
const setEnabled = (value) => {
|
|
2743
|
+
if (value === enabled) return;
|
|
2744
|
+
enabled = value;
|
|
2745
|
+
generation++;
|
|
2746
|
+
seedBaseline = true;
|
|
2747
|
+
watchAbort?.abort();
|
|
2748
|
+
if (!enabled) closePopup();
|
|
2749
|
+
};
|
|
2750
|
+
updateWhalePreference = value => {
|
|
2751
|
+
localPreference = value;
|
|
2752
|
+
if (value !== null) setEnabled(value);
|
|
2753
|
+
};
|
|
2719
2754
|
|
|
2720
2755
|
const tick = async () => {
|
|
2721
|
-
if (document.hidden) return;
|
|
2756
|
+
if (disposed || document.hidden || running) return;
|
|
2757
|
+
running = true;
|
|
2722
2758
|
try {
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2759
|
+
// Refresh the small local snapshot even when disabled so another tab's
|
|
2760
|
+
// saved preference takes effect. This does not contact the mail server.
|
|
2761
|
+
const snap = await api();
|
|
2762
|
+
if (disposed) return;
|
|
2763
|
+
setEnabled(localPreference ?? (snap?.settings?.value?.newMailPopup !== false));
|
|
2764
|
+
if (snap && snap.whale) {
|
|
2765
|
+
whaleUrl = (snap.whale.url || "").replace(/^\/_dsh\//, "_dsh/");
|
|
2766
|
+
whaleCredit = snap.whale.credit || "";
|
|
2731
2767
|
}
|
|
2732
|
-
if (!
|
|
2733
|
-
const
|
|
2734
|
-
|
|
2768
|
+
if (!enabled || !snap?.accounts?.length || document.hidden) { failures = 0; return; }
|
|
2769
|
+
const version = generation;
|
|
2770
|
+
watchAbort = new AbortController();
|
|
2771
|
+
const value = await api("watch", { limit: 5 }, watchAbort.signal);
|
|
2772
|
+
if (disposed || !enabled || version !== generation || document.hidden) return;
|
|
2773
|
+
if (!seedBaseline && value && value.newCount > 0) showPopup(value);
|
|
2774
|
+
seedBaseline = false;
|
|
2735
2775
|
failures = 0;
|
|
2736
2776
|
} catch (e) {
|
|
2737
2777
|
// Not configured or transient error: stay silent and back off.
|
|
2738
2778
|
failures = Math.min(failures + 1, 8);
|
|
2779
|
+
} finally {
|
|
2780
|
+
running = false;
|
|
2781
|
+
watchAbort = null;
|
|
2739
2782
|
}
|
|
2740
2783
|
};
|
|
2741
2784
|
|
|
2742
2785
|
const schedule = () => {
|
|
2743
2786
|
clearTimeout(pollTimer);
|
|
2744
|
-
if (document.hidden) return;
|
|
2787
|
+
if (disposed || document.hidden) return;
|
|
2745
2788
|
const delay = failures === 0 ? POLL_MS : Math.min(POLL_MS * Math.pow(2, failures - 1), MAX_BACKOFF_MS);
|
|
2746
2789
|
pollTimer = setTimeout(() => { void tick().then(schedule); }, delay);
|
|
2747
2790
|
};
|
|
2748
2791
|
|
|
2749
2792
|
const onVisibility = () => {
|
|
2750
2793
|
clearTimeout(pollTimer);
|
|
2751
|
-
if (document.hidden) return;
|
|
2794
|
+
if (disposed || document.hidden) { watchAbort?.abort(); closePopup(); return; }
|
|
2752
2795
|
failures = 0;
|
|
2753
2796
|
void tick().then(schedule);
|
|
2754
2797
|
};
|
|
@@ -2757,10 +2800,14 @@ function startWhaleWidget() {
|
|
|
2757
2800
|
// 弹窗是命令式 DOM,没有 React 那层重渲染:语言切换后自己重画一次(开着的才重画)。
|
|
2758
2801
|
relocalize = () => { if (current !== null && root.childElementCount > 0) showPopup(current); };
|
|
2759
2802
|
return () => {
|
|
2803
|
+
disposed = true;
|
|
2804
|
+
generation++;
|
|
2805
|
+
watchAbort?.abort();
|
|
2760
2806
|
clearTimeout(pollTimer);
|
|
2761
2807
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
2762
2808
|
if (hideTimer) clearTimeout(hideTimer);
|
|
2763
2809
|
relocalize = null;
|
|
2810
|
+
updateWhalePreference = null;
|
|
2764
2811
|
root.remove();
|
|
2765
2812
|
};
|
|
2766
2813
|
}
|
package/lib/config.d.ts
CHANGED
|
@@ -108,6 +108,8 @@ export interface AccountConfig {
|
|
|
108
108
|
inboxFolder?: string;
|
|
109
109
|
}
|
|
110
110
|
export interface EmailConfig extends AccountConfig {
|
|
111
|
+
/** Show browser new-mail notifications. UI policy only; default true. */
|
|
112
|
+
newMailPopup?: boolean;
|
|
111
113
|
/** Per-account recipient rules, independent of IMAP/SMTP connection settings. */
|
|
112
114
|
trustedRecipientsYaml?: string;
|
|
113
115
|
/** Ask the user for approval before email_send. Default true. */
|
package/lib/host-config.js
CHANGED
|
@@ -12,6 +12,7 @@ export const Config = z.object({
|
|
|
12
12
|
clientId: z.string().volatile(), authKind: z.union(['password', 'oauth2']).volatile(),
|
|
13
13
|
imap: endpoint().volatile(), smtp: endpoint().volatile(),
|
|
14
14
|
inboxFolder: z.string().volatile(), sendApproval: z.boolean().volatile(),
|
|
15
|
+
newMailPopup: z.boolean().volatile(),
|
|
15
16
|
maxBodyChars: z.number().volatile(), downloadDir: z.string().volatile(),
|
|
16
17
|
accounts: z.dict(z.any()).role('secret').volatile(), defaultAccount: z.string().volatile(),
|
|
17
18
|
accountsYaml: z.string().role('secret').volatile(), serverPresets: z.string().volatile(),
|
package/lib/settings.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare const EmailSettingsSchema: import("@deepseek-ai/schemastery").def
|
|
|
12
12
|
password: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
13
13
|
inboxFolder: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
14
14
|
sendApproval: import("@deepseek-ai/schemastery").default<boolean, boolean, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
15
|
+
newMailPopup: import("@deepseek-ai/schemastery").default<boolean, boolean, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
15
16
|
trustedRecipientsYaml: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
16
17
|
maxBodyChars: import("@deepseek-ai/schemastery").default<number, number, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
17
18
|
downloadDir: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
@@ -41,6 +42,7 @@ export declare const EmailSettingsSchema: import("@deepseek-ai/schemastery").def
|
|
|
41
42
|
password: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
42
43
|
inboxFolder: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
43
44
|
sendApproval: import("@deepseek-ai/schemastery").default<boolean, boolean, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
45
|
+
newMailPopup: import("@deepseek-ai/schemastery").default<boolean, boolean, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
44
46
|
trustedRecipientsYaml: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
45
47
|
maxBodyChars: import("@deepseek-ai/schemastery").default<number, number, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
46
48
|
downloadDir: import("@deepseek-ai/schemastery").default<string, string, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
@@ -71,6 +73,7 @@ export interface EmailSettingsValue {
|
|
|
71
73
|
password: string;
|
|
72
74
|
inboxFolder: string;
|
|
73
75
|
sendApproval: boolean;
|
|
76
|
+
newMailPopup?: boolean;
|
|
74
77
|
trustedRecipientsYaml?: string;
|
|
75
78
|
maxBodyChars: number;
|
|
76
79
|
downloadDir: string;
|
package/lib/settings.js
CHANGED
|
@@ -14,6 +14,7 @@ export const EmailSettingsSchema = z.object({
|
|
|
14
14
|
password: z.string().role('secret').default(''),
|
|
15
15
|
inboxFolder: z.string().default('INBOX'),
|
|
16
16
|
sendApproval: z.boolean().default(true),
|
|
17
|
+
newMailPopup: z.boolean().default(true),
|
|
17
18
|
trustedRecipientsYaml: z.string().default(''),
|
|
18
19
|
maxBodyChars: z.number().default(20000),
|
|
19
20
|
downloadDir: z.string().default(''),
|
|
@@ -41,6 +42,7 @@ export function toSettingsBase(config) {
|
|
|
41
42
|
...(config.password !== undefined && config.password !== '' ? { password: config.password } : {}),
|
|
42
43
|
...(config.inboxFolder !== undefined && config.inboxFolder !== '' ? { inboxFolder: config.inboxFolder } : {}),
|
|
43
44
|
...(config.sendApproval !== undefined ? { sendApproval: config.sendApproval } : {}),
|
|
45
|
+
...(config.newMailPopup !== undefined ? { newMailPopup: config.newMailPopup } : {}),
|
|
44
46
|
...(config.trustedRecipientsYaml !== undefined ? { trustedRecipientsYaml: config.trustedRecipientsYaml } : {}),
|
|
45
47
|
...(config.maxBodyChars !== undefined ? { maxBodyChars: config.maxBodyChars } : {}),
|
|
46
48
|
...(config.downloadDir !== undefined && config.downloadDir !== '' ? { downloadDir: config.downloadDir } : {}),
|
|
@@ -203,6 +205,9 @@ function describeValue(value) {
|
|
|
203
205
|
*/
|
|
204
206
|
export function validateSettingsValue(value, extraProviders = []) {
|
|
205
207
|
const draft = (value ?? {});
|
|
208
|
+
if (draft.newMailPopup !== undefined && typeof draft.newMailPopup !== 'boolean') {
|
|
209
|
+
throw new Error('新邮件弹窗开关必须为布尔值');
|
|
210
|
+
}
|
|
206
211
|
if (draft.trustedRecipientsYaml !== undefined) {
|
|
207
212
|
if (typeof draft.trustedRecipientsYaml !== 'string')
|
|
208
213
|
throw new Error('可信收件人规则必须为文本');
|