@eduwork/dsh-mail 0.1.0-alpha.5

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/lib/client.js ADDED
@@ -0,0 +1,759 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@eduwork/dsh-mail",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
28
+ //#endregion
29
+ let react = require("react");
30
+ react = __toESM(react, 1);
31
+ let react_jsx_runtime = require("react/jsx-runtime");
32
+ //#region src/client/index.tsx
33
+ const inject = [
34
+ "slots",
35
+ "remote",
36
+ "remote.settings",
37
+ "remote.credentials"
38
+ ];
39
+ const NS = "dsh-mail-assistant";
40
+ const PASSWORD_REF = "DSH_MAIL_ASSISTANT_PASSWORD";
41
+ const copy = typeof navigator !== "undefined" && navigator.language.toLowerCase().startsWith("zh") ? {
42
+ nav: "邮件助手",
43
+ title: "邮件助手",
44
+ description: "让 Agent 通过标准 IMAP 只读检索邮件,并按当前 DSH 权限模式通过 SMTP 发信。它不是邮箱客户端,不会移动、删除、归档或修改邮件状态。",
45
+ loading: "正在读取配置…",
46
+ save: "保存",
47
+ saving: "正在保存…",
48
+ saved: "配置已保存,新调用立即生效。",
49
+ readPermission: "允许 Agent 读信",
50
+ readHint: "可列出目录、分页查找、读取邮件和下载附件;IMAP 始终只读。",
51
+ sendPermission: "允许 Agent 发信",
52
+ sendHint: "可发送纯文本邮件;普通权限逐次确认,Full Access 不再弹窗。",
53
+ identity: "邮箱账号",
54
+ identityHint: "先填写日常使用的邮箱信息。多数邮箱要求使用客户端授权码,而不是网页登录密码。",
55
+ email: "邮箱地址",
56
+ emailHint: "用于收信和发信。",
57
+ username: "登录用户名",
58
+ usernameHint: "仅当服务商要求时填写;留空则使用邮箱地址。",
59
+ fromName: "发件人显示名称",
60
+ fromNameHint: "收件人看到的名称;可留空。",
61
+ inbox: "收件箱目录",
62
+ inboxHint: "通常保持 INBOX。",
63
+ password: "密码 / 客户端授权码",
64
+ passwordHint: "安全写入 DSH 凭据存储,不会写进 settings.yaml,也不会回显。",
65
+ configured: "已保存",
66
+ missing: "未填写",
67
+ credentialPlaceholder: "输入授权码;已保存时留空不会改变",
68
+ clearPassword: "清除授权码",
69
+ servers: "邮箱服务器",
70
+ serversHint: "选择常见服务商可自动填写;单位邮箱或自建邮箱请选择“手动配置”。",
71
+ preset: "邮箱服务商",
72
+ custom: "手动配置",
73
+ imap: "收信(IMAP)",
74
+ smtp: "发信(SMTP)",
75
+ host: "服务器地址",
76
+ port: "端口",
77
+ tls: "加密",
78
+ implicit: "隐式 TLS",
79
+ starttls: "STARTTLS",
80
+ advanced: "高级设置",
81
+ advancedHint: "登录用户名、收件箱目录和安全上限通常不需要修改。",
82
+ collapse: "收起",
83
+ agentAccess: "开放给 Agent",
84
+ agentAccessHint: "账号和服务器保存好后,再按需要分别开放读信或发信。",
85
+ permissionNeedsSetup: "请先填写邮箱地址、授权码和相应的服务器。",
86
+ limits: "安全上限",
87
+ bodyLimit: "正文字符",
88
+ messageLimit: "整封邮件字节",
89
+ attachmentLimit: "附件总字节",
90
+ usage: "保存后,在对话里让 Agent“查一下最近邮件”即可验证。插件不会在启动或保存时主动连接邮箱。",
91
+ error: "保存失败",
92
+ reload: "重新加载",
93
+ credentialPartial: "授权码已经保存,但其他配置保存失败;请修正后再次保存。"
94
+ } : {
95
+ nav: "Mail assistant",
96
+ title: "Mail assistant",
97
+ description: "Let the agent search mail through read-only IMAP and send through SMTP under the current DSH permission preset. This is not a mail client: it never moves, deletes, archives, or changes message state.",
98
+ loading: "Loading configuration…",
99
+ save: "Save",
100
+ saving: "Saving…",
101
+ saved: "Saved. New calls use the configuration immediately.",
102
+ readPermission: "Allow the agent to read mail",
103
+ readHint: "List folders, page through searches, read messages, and download attachments. IMAP always stays read-only.",
104
+ sendPermission: "Allow the agent to send mail",
105
+ sendHint: "Send plain-text messages. Ordinary permissions ask each time; Full Access does not prompt.",
106
+ identity: "Mailbox account",
107
+ identityHint: "Start with the mailbox information you normally use. Most providers require an app password instead of the web-login password.",
108
+ email: "Email address",
109
+ emailHint: "Used for both incoming and outgoing mail.",
110
+ username: "Login username",
111
+ usernameHint: "Only set this when required by your provider; otherwise the email address is used.",
112
+ fromName: "Sender display name",
113
+ fromNameHint: "The name recipients see; optional.",
114
+ inbox: "Inbox folder",
115
+ inboxHint: "Usually keep INBOX.",
116
+ password: "Password / app password",
117
+ passwordHint: "Stored securely in DSH Credentials. It is never written to settings.yaml or returned to this page.",
118
+ configured: "Saved",
119
+ missing: "Missing",
120
+ credentialPlaceholder: "Enter an app password; leave blank to keep the saved value",
121
+ clearPassword: "Clear app password",
122
+ servers: "Mail servers",
123
+ serversHint: "Choose a common provider to fill these automatically, or use Manual configuration for institutional and self-hosted mail.",
124
+ preset: "Mail provider",
125
+ custom: "Manual configuration",
126
+ imap: "Incoming (IMAP)",
127
+ smtp: "Outgoing (SMTP)",
128
+ host: "Server address",
129
+ port: "Port",
130
+ tls: "Encryption",
131
+ implicit: "Implicit TLS",
132
+ starttls: "STARTTLS",
133
+ advanced: "Advanced settings",
134
+ advancedHint: "Login username, inbox folder, and safety limits usually need no changes.",
135
+ collapse: "Collapse",
136
+ agentAccess: "Agent access",
137
+ agentAccessHint: "After saving the account and servers, enable reading and sending independently as needed.",
138
+ permissionNeedsSetup: "Enter the email address, app password, and corresponding server first.",
139
+ limits: "Safety limits",
140
+ bodyLimit: "Body characters",
141
+ messageLimit: "Whole-message bytes",
142
+ attachmentLimit: "Total attachment bytes",
143
+ usage: "After saving, ask the agent to find recent mail. The plugin never connects to a mailbox during startup or save.",
144
+ error: "Save failed",
145
+ reload: "Reload",
146
+ credentialPartial: "The app password was stored, but the remaining settings failed to save. Fix the form and save again."
147
+ };
148
+ const defaults = {
149
+ readEnabled: false,
150
+ sendEnabled: false,
151
+ email: "",
152
+ username: "",
153
+ fromName: "",
154
+ inboxFolder: "INBOX",
155
+ imapHost: "",
156
+ imapPort: 993,
157
+ imapTls: "implicit",
158
+ smtpHost: "",
159
+ smtpPort: 465,
160
+ smtpTls: "implicit",
161
+ maxBodyChars: 2e4,
162
+ maxMessageBytes: 26214400,
163
+ maxAttachmentBytes: 20971520
164
+ };
165
+ const presets = {
166
+ gmail: {
167
+ imapHost: "imap.gmail.com",
168
+ imapPort: 993,
169
+ imapTls: "implicit",
170
+ smtpHost: "smtp.gmail.com",
171
+ smtpPort: 465,
172
+ smtpTls: "implicit"
173
+ },
174
+ outlook: {
175
+ imapHost: "outlook.office365.com",
176
+ imapPort: 993,
177
+ imapTls: "implicit",
178
+ smtpHost: "smtp.office365.com",
179
+ smtpPort: 587,
180
+ smtpTls: "starttls"
181
+ },
182
+ qq: {
183
+ imapHost: "imap.qq.com",
184
+ imapPort: 993,
185
+ imapTls: "implicit",
186
+ smtpHost: "smtp.qq.com",
187
+ smtpPort: 465,
188
+ smtpTls: "implicit"
189
+ },
190
+ "163": {
191
+ imapHost: "imap.163.com",
192
+ imapPort: 993,
193
+ imapTls: "implicit",
194
+ smtpHost: "smtp.163.com",
195
+ smtpPort: 465,
196
+ smtpTls: "implicit"
197
+ },
198
+ icloud: {
199
+ imapHost: "imap.mail.me.com",
200
+ imapPort: 993,
201
+ imapTls: "implicit",
202
+ smtpHost: "smtp.mail.me.com",
203
+ smtpPort: 587,
204
+ smtpTls: "starttls"
205
+ }
206
+ };
207
+ async function unwrap(operation) {
208
+ const result = await operation;
209
+ if (result?.ok === true) return result.value;
210
+ throw new Error(result?.error?.message || result?.error?.code || "Remote operation failed");
211
+ }
212
+ function inputNumber(value) {
213
+ const parsed = Number(value);
214
+ return Number.isFinite(parsed) ? Math.trunc(parsed) : 0;
215
+ }
216
+ function detectPreset(value) {
217
+ for (const [key, candidate] of Object.entries(presets)) if (Object.entries(candidate).every(([field, expected]) => value[field] === expected)) return key;
218
+ return "custom";
219
+ }
220
+ function MailSettings({ service }) {
221
+ const [draft, setDraft] = (0, react.useState)(null);
222
+ const [revision, setRevision] = (0, react.useState)(0);
223
+ const [passwordConfigured, setPasswordConfigured] = (0, react.useState)(false);
224
+ const [password, setPassword] = (0, react.useState)("");
225
+ const [busy, setBusy] = (0, react.useState)(false);
226
+ const [notice, setNotice] = (0, react.useState)("");
227
+ const [error, setError] = (0, react.useState)("");
228
+ const [preset, setPreset] = (0, react.useState)("custom");
229
+ const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
230
+ const load = async () => {
231
+ setError("");
232
+ setNotice("");
233
+ const [settings, credentials] = await Promise.all([unwrap(service.settings.describe()), unwrap(service.credentials.describe([PASSWORD_REF]))]);
234
+ const namespace = settings.namespaces.find((candidate) => candidate.ns === NS);
235
+ if (!namespace) throw new Error("dsh-mail-assistant settings namespace is not available");
236
+ const next = {
237
+ ...defaults,
238
+ ...namespace.value ?? {}
239
+ };
240
+ setDraft(next);
241
+ setPreset(detectPreset(next));
242
+ setRevision(namespace.revision ?? 0);
243
+ setPasswordConfigured(credentials[PASSWORD_REF]?.configured === true);
244
+ setPassword("");
245
+ };
246
+ (0, react.useEffect)(() => {
247
+ load().catch((cause) => setError(cause?.message || String(cause)));
248
+ }, []);
249
+ const set = (key, value) => setDraft((current) => ({
250
+ ...current,
251
+ [key]: value
252
+ }));
253
+ const applyPreset = (key) => {
254
+ setPreset(key);
255
+ if (presets[key]) setDraft((current) => ({
256
+ ...current,
257
+ ...presets[key]
258
+ }));
259
+ };
260
+ const save = async () => {
261
+ setBusy(true);
262
+ setError("");
263
+ setNotice("");
264
+ let passwordStored = false;
265
+ try {
266
+ if (password !== "") {
267
+ await unwrap(service.credentials.set(PASSWORD_REF, password));
268
+ passwordStored = true;
269
+ setPasswordConfigured(true);
270
+ setPassword("");
271
+ }
272
+ const namespace = await unwrap(service.settings.replace(NS, draft, revision));
273
+ setDraft({
274
+ ...defaults,
275
+ ...namespace.value ?? draft
276
+ });
277
+ setRevision(namespace.revision ?? revision + 1);
278
+ setNotice(copy.saved);
279
+ } catch (cause) {
280
+ setError(passwordStored ? copy.credentialPartial : cause?.message || `${copy.error}`);
281
+ } finally {
282
+ setBusy(false);
283
+ }
284
+ };
285
+ const clearPassword = async () => {
286
+ setBusy(true);
287
+ setError("");
288
+ setNotice("");
289
+ try {
290
+ await unwrap(service.credentials.unset(PASSWORD_REF));
291
+ setPasswordConfigured(false);
292
+ setPassword("");
293
+ setNotice(copy.saved);
294
+ } catch (cause) {
295
+ setError(cause?.message || String(cause));
296
+ } finally {
297
+ setBusy(false);
298
+ }
299
+ };
300
+ const colors = (0, react.useMemo)(() => ({
301
+ border: "var(--dsw-alias-border-l2, #e5d4cc)",
302
+ bg: "var(--dsw-alias-bg-layer-1, #fff)",
303
+ secondary: "var(--dsw-alias-label-secondary, #6c625f)",
304
+ primary: "var(--dsw-alias-label-primary, #241a18)",
305
+ accent: "var(--dsw-alias-brand-primary, #9d2f3f)",
306
+ soft: "var(--dsw-alias-bg-layer-2, #f8f3f1)"
307
+ }), []);
308
+ const styles = {
309
+ root: {
310
+ maxWidth: 820,
311
+ padding: "10px 0 42px",
312
+ color: colors.primary
313
+ },
314
+ card: {
315
+ border: `1px solid ${colors.border}`,
316
+ borderRadius: 14,
317
+ padding: 18,
318
+ background: colors.bg,
319
+ marginTop: 14
320
+ },
321
+ grid: {
322
+ display: "grid",
323
+ gridTemplateColumns: "repeat(auto-fit, minmax(210px, 1fr))",
324
+ gap: 12
325
+ },
326
+ label: {
327
+ display: "grid",
328
+ gap: 6,
329
+ color: colors.secondary,
330
+ fontSize: 12
331
+ },
332
+ input: {
333
+ boxSizing: "border-box",
334
+ width: "100%",
335
+ minHeight: 38,
336
+ border: `1px solid ${colors.border}`,
337
+ borderRadius: 9,
338
+ padding: "7px 10px",
339
+ background: colors.bg,
340
+ color: colors.primary
341
+ },
342
+ button: {
343
+ minHeight: 36,
344
+ border: `1px solid ${colors.border}`,
345
+ borderRadius: 9,
346
+ padding: "7px 12px",
347
+ background: colors.bg,
348
+ color: colors.primary,
349
+ cursor: "pointer"
350
+ },
351
+ primary: {
352
+ minHeight: 36,
353
+ border: 0,
354
+ borderRadius: 9,
355
+ padding: "7px 14px",
356
+ background: colors.accent,
357
+ color: "#fff",
358
+ cursor: "pointer",
359
+ fontWeight: 650
360
+ }
361
+ };
362
+ if (!draft) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
363
+ style: styles.root,
364
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error || copy.loading }), error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
365
+ style: styles.button,
366
+ onClick: () => load(),
367
+ children: copy.reload
368
+ })]
369
+ });
370
+ const credentialReady = passwordConfigured || password !== "";
371
+ const readReady = draft.email.trim() !== "" && draft.imapHost.trim() !== "" && credentialReady;
372
+ const sendReady = draft.email.trim() !== "" && draft.smtpHost.trim() !== "" && credentialReady;
373
+ const permission = (key, title, hint, ready) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
374
+ style: {
375
+ display: "flex",
376
+ alignItems: "flex-start",
377
+ gap: 11,
378
+ padding: 13,
379
+ border: `1px solid ${draft[key] ? colors.accent : colors.border}`,
380
+ borderRadius: 11,
381
+ background: colors.soft,
382
+ opacity: !ready && !draft[key] ? .62 : 1
383
+ },
384
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
385
+ type: "checkbox",
386
+ checked: draft[key],
387
+ disabled: !ready && !draft[key],
388
+ onChange: (event) => set(key, event.currentTarget.checked),
389
+ style: { marginTop: 3 }
390
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
391
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
392
+ style: {
393
+ display: "block",
394
+ fontSize: 13
395
+ },
396
+ children: title
397
+ }),
398
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
399
+ style: {
400
+ display: "block",
401
+ marginTop: 4,
402
+ color: colors.secondary,
403
+ fontSize: 11,
404
+ lineHeight: 1.5
405
+ },
406
+ children: hint
407
+ }),
408
+ !ready && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
409
+ style: {
410
+ display: "block",
411
+ marginTop: 4,
412
+ color: "#a82332",
413
+ fontSize: 11
414
+ },
415
+ children: copy.permissionNeedsSetup
416
+ })
417
+ ] })]
418
+ });
419
+ const field = (label, key, type = "text", hint) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
420
+ style: styles.label,
421
+ children: [
422
+ label,
423
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
424
+ type,
425
+ value: draft[key],
426
+ onChange: (event) => set(key, type === "number" ? inputNumber(event.currentTarget.value) : event.currentTarget.value),
427
+ style: styles.input
428
+ }),
429
+ hint && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: hint })
430
+ ]
431
+ });
432
+ const server = (prefix, title) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
433
+ style: {
434
+ border: `1px solid ${colors.border}`,
435
+ borderRadius: 11,
436
+ padding: 13
437
+ },
438
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
439
+ style: { fontSize: 13 },
440
+ children: title
441
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
442
+ style: {
443
+ ...styles.grid,
444
+ gridTemplateColumns: "minmax(180px, 2fr) minmax(90px, .7fr) minmax(130px, 1fr)",
445
+ marginTop: 10
446
+ },
447
+ children: [
448
+ field(copy.host, `${prefix}Host`),
449
+ field(copy.port, `${prefix}Port`, "number"),
450
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
451
+ style: styles.label,
452
+ children: [copy.tls, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
453
+ value: draft[`${prefix}Tls`],
454
+ onChange: (event) => set(`${prefix}Tls`, event.currentTarget.value),
455
+ style: styles.input,
456
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
457
+ value: "implicit",
458
+ children: copy.implicit
459
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
460
+ value: "starttls",
461
+ children: copy.starttls
462
+ })]
463
+ })]
464
+ })
465
+ ]
466
+ })]
467
+ });
468
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
469
+ style: styles.root,
470
+ children: [
471
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
472
+ style: {
473
+ margin: 0,
474
+ fontSize: 21
475
+ },
476
+ children: copy.title
477
+ }),
478
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
479
+ style: {
480
+ margin: "7px 0 0",
481
+ color: colors.secondary,
482
+ fontSize: 12,
483
+ lineHeight: 1.65
484
+ },
485
+ children: copy.description
486
+ }),
487
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
488
+ style: styles.card,
489
+ children: [
490
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
491
+ style: {
492
+ margin: 0,
493
+ fontSize: 15
494
+ },
495
+ children: copy.identity
496
+ }),
497
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
498
+ style: {
499
+ margin: "6px 0 13px",
500
+ color: colors.secondary,
501
+ fontSize: 11,
502
+ lineHeight: 1.55
503
+ },
504
+ children: copy.identityHint
505
+ }),
506
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
507
+ style: styles.grid,
508
+ children: [
509
+ field(copy.email, "email", "email", copy.emailHint),
510
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
511
+ style: styles.label,
512
+ children: [
513
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
514
+ style: {
515
+ display: "flex",
516
+ alignItems: "center",
517
+ gap: 7
518
+ },
519
+ children: [copy.password, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
520
+ style: {
521
+ borderRadius: 999,
522
+ padding: "2px 7px",
523
+ fontSize: 10,
524
+ color: passwordConfigured ? "#32724e" : "#9d2f3f",
525
+ background: passwordConfigured ? "#eaf6ef" : "#f8e9ec"
526
+ },
527
+ children: passwordConfigured ? copy.configured : copy.missing
528
+ })]
529
+ }),
530
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
531
+ type: "password",
532
+ autoComplete: "new-password",
533
+ value: password,
534
+ placeholder: copy.credentialPlaceholder,
535
+ onChange: (event) => setPassword(event.currentTarget.value),
536
+ style: styles.input
537
+ }),
538
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: copy.passwordHint }),
539
+ passwordConfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
540
+ type: "button",
541
+ disabled: busy,
542
+ style: {
543
+ ...styles.button,
544
+ justifySelf: "start",
545
+ color: "#a82332"
546
+ },
547
+ onClick: clearPassword,
548
+ children: copy.clearPassword
549
+ })
550
+ ]
551
+ }),
552
+ field(copy.fromName, "fromName", "text", copy.fromNameHint)
553
+ ]
554
+ })
555
+ ]
556
+ }),
557
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
558
+ style: styles.card,
559
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
560
+ style: {
561
+ display: "flex",
562
+ justifyContent: "space-between",
563
+ gap: 12,
564
+ alignItems: "flex-start",
565
+ marginBottom: 13
566
+ },
567
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
568
+ style: {
569
+ margin: 0,
570
+ fontSize: 15
571
+ },
572
+ children: copy.servers
573
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
574
+ style: {
575
+ margin: "6px 0 0",
576
+ color: colors.secondary,
577
+ fontSize: 11,
578
+ lineHeight: 1.55
579
+ },
580
+ children: copy.serversHint
581
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
582
+ style: {
583
+ ...styles.label,
584
+ minWidth: 210
585
+ },
586
+ children: [copy.preset, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
587
+ value: preset,
588
+ onChange: (event) => applyPreset(event.currentTarget.value),
589
+ style: styles.input,
590
+ children: [
591
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
592
+ value: "custom",
593
+ children: copy.custom
594
+ }),
595
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
596
+ value: "gmail",
597
+ children: "Gmail"
598
+ }),
599
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
600
+ value: "outlook",
601
+ children: "Outlook / Microsoft 365"
602
+ }),
603
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
604
+ value: "qq",
605
+ children: "QQ Mail"
606
+ }),
607
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
608
+ value: "163",
609
+ children: "163 Mail"
610
+ }),
611
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
612
+ value: "icloud",
613
+ children: "iCloud Mail"
614
+ })
615
+ ]
616
+ })]
617
+ })]
618
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
619
+ style: {
620
+ display: "grid",
621
+ gap: 11
622
+ },
623
+ children: [server("imap", copy.imap), server("smtp", copy.smtp)]
624
+ })]
625
+ }),
626
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
627
+ style: styles.card,
628
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
629
+ type: "button",
630
+ "aria-expanded": advancedOpen,
631
+ onClick: () => setAdvancedOpen((current) => !current),
632
+ style: {
633
+ ...styles.button,
634
+ width: "100%",
635
+ display: "flex",
636
+ justifyContent: "space-between",
637
+ alignItems: "center",
638
+ textAlign: "left"
639
+ },
640
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
641
+ style: {
642
+ display: "block",
643
+ fontSize: 13
644
+ },
645
+ children: copy.advanced
646
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
647
+ style: {
648
+ display: "block",
649
+ marginTop: 3,
650
+ color: colors.secondary,
651
+ fontSize: 11
652
+ },
653
+ children: copy.advancedHint
654
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: advancedOpen ? copy.collapse : "›" })]
655
+ }), advancedOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
656
+ style: { marginTop: 14 },
657
+ children: [
658
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
659
+ style: styles.grid,
660
+ children: [field(copy.username, "username", "text", copy.usernameHint), field(copy.inbox, "inboxFolder", "text", copy.inboxHint)]
661
+ }),
662
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
663
+ style: {
664
+ margin: "17px 0 10px",
665
+ fontSize: 13
666
+ },
667
+ children: copy.limits
668
+ }),
669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
670
+ style: styles.grid,
671
+ children: [
672
+ field(copy.bodyLimit, "maxBodyChars", "number"),
673
+ field(copy.messageLimit, "maxMessageBytes", "number"),
674
+ field(copy.attachmentLimit, "maxAttachmentBytes", "number")
675
+ ]
676
+ })
677
+ ]
678
+ })]
679
+ }),
680
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
681
+ style: styles.card,
682
+ children: [
683
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
684
+ style: {
685
+ margin: 0,
686
+ fontSize: 15
687
+ },
688
+ children: copy.agentAccess
689
+ }),
690
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
691
+ style: {
692
+ margin: "6px 0 13px",
693
+ color: colors.secondary,
694
+ fontSize: 11,
695
+ lineHeight: 1.55
696
+ },
697
+ children: copy.agentAccessHint
698
+ }),
699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
700
+ style: styles.grid,
701
+ children: [permission("readEnabled", copy.readPermission, copy.readHint, readReady), permission("sendEnabled", copy.sendPermission, copy.sendHint, sendReady)]
702
+ })
703
+ ]
704
+ }),
705
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
706
+ style: {
707
+ color: colors.secondary,
708
+ fontSize: 11,
709
+ lineHeight: 1.55
710
+ },
711
+ children: copy.usage
712
+ }),
713
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
714
+ role: "alert",
715
+ style: {
716
+ color: "#a82332",
717
+ fontSize: 12
718
+ },
719
+ children: error
720
+ }),
721
+ notice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
722
+ role: "status",
723
+ style: {
724
+ color: "#32724e",
725
+ fontSize: 12
726
+ },
727
+ children: notice
728
+ }),
729
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
730
+ type: "button",
731
+ disabled: busy,
732
+ onClick: save,
733
+ style: styles.primary,
734
+ children: busy ? copy.saving : copy.save
735
+ })
736
+ ]
737
+ });
738
+ }
739
+ function apply(ctx) {
740
+ const service = {
741
+ settings: ctx.remote.settings,
742
+ credentials: ctx.remote.credentials
743
+ };
744
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
745
+ name: "settings.section",
746
+ id: "mail-assistant",
747
+ order: 25,
748
+ label: () => copy.nav,
749
+ inject: () => ({ service })
750
+ }, MailSettings));
751
+ }
752
+ //#endregion
753
+ exports.apply = apply;
754
+ exports.inject = inject;
755
+ return module.exports;
756
+ }
757
+ });
758
+
759
+ //# sourceMappingURL=client.js.map