@eduwork/dsh-mail 0.1.2 → 0.2.0

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 CHANGED
@@ -29,126 +29,133 @@ window.__ModuleLoader__.load({
29
29
  let react = require("react");
30
30
  react = __toESM(react, 1);
31
31
  let react_jsx_runtime = require("react/jsx-runtime");
32
+ //#region src/client/locale.ts
33
+ const LOCALE_NS = "settings.mailAssistant";
34
+ const dictionaries = {
35
+ "zh": {
36
+ "nav": "邮件助手",
37
+ "title": "邮件助手",
38
+ "description": "让 Agent 通过标准 IMAP 只读检索邮件,并按当前权限模式通过 SMTP 发信。它不是邮箱客户端,不会移动、删除、归档或修改邮件状态。",
39
+ "loading": "正在读取配置…",
40
+ "save": "保存",
41
+ "saving": "正在保存…",
42
+ "saved": "配置已保存,新调用立即生效。",
43
+ "readPermission": "允许 Agent 读信",
44
+ "readHint": "可列出目录、分页查找、读取邮件和下载附件;IMAP 始终只读。",
45
+ "sendPermission": "允许 Agent 发信",
46
+ "sendHint": "可发送纯文本邮件;普通权限逐次确认,Full Access 不再弹窗。",
47
+ "identity": "邮箱账号",
48
+ "identityHint": "先填写日常使用的邮箱信息。多数邮箱要求使用客户端授权码,而不是网页登录密码。",
49
+ "email": "邮箱地址",
50
+ "emailHint": "用于收信和发信。",
51
+ "username": "登录用户名",
52
+ "usernameHint": "仅当服务商要求时填写;留空则使用邮箱地址。",
53
+ "fromName": "发件人显示名称",
54
+ "fromNameHint": "收件人看到的名称;可留空。",
55
+ "inbox": "收件箱目录",
56
+ "inboxHint": "通常保持 INBOX。",
57
+ "password": "密码 / 客户端授权码",
58
+ "passwordHint": "安全写入本机凭据存储,不会写进 settings.yaml,也不会回显。",
59
+ "configured": "已保存",
60
+ "missing": "未填写",
61
+ "credentialPlaceholder": "输入授权码;已保存时留空不会改变",
62
+ "clearPassword": "清除授权码",
63
+ "servers": "邮箱服务器",
64
+ "serversHint": "选择常见服务商可自动填写;单位邮箱或自建邮箱请选择“手动配置”。",
65
+ "preset": "邮箱服务商",
66
+ "custom": "手动配置",
67
+ "imap": "收信(IMAP)",
68
+ "smtp": "发信(SMTP)",
69
+ "host": "服务器地址",
70
+ "port": "端口",
71
+ "tls": "加密",
72
+ "implicit": "隐式 TLS",
73
+ "starttls": "STARTTLS",
74
+ "advanced": "高级设置",
75
+ "advancedHint": "登录用户名、收件箱目录和安全上限通常不需要修改。",
76
+ "collapse": "收起",
77
+ "agentAccess": "开放给 Agent",
78
+ "agentAccessHint": "账号和服务器保存好后,再按需要分别开放读信或发信。",
79
+ "permissionNeedsSetup": "请先填写邮箱地址、授权码和相应的服务器。",
80
+ "limits": "安全上限",
81
+ "bodyLimit": "正文字符",
82
+ "messageLimit": "整封邮件字节",
83
+ "attachmentLimit": "附件总字节",
84
+ "usage": "保存后,在对话里让 Agent“查一下最近邮件”即可验证。插件不会在启动或保存时主动连接邮箱。",
85
+ "error": "保存失败",
86
+ "reload": "重新加载",
87
+ "unavailable": "当前连接未提供此设置命名空间。",
88
+ "readOnly": "当前连接为只读模式,无法保存设置。",
89
+ "credentialPartial": "授权码已经保存,但其他配置保存失败;请修正后再次保存。"
90
+ },
91
+ "en": {
92
+ "nav": "Mail assistant",
93
+ "title": "Mail assistant",
94
+ "description": "Let the agent search mail through read-only IMAP and send through SMTP under the current permission preset. This is not a mail client: it never moves, deletes, archives, or changes message state.",
95
+ "loading": "Loading configuration…",
96
+ "save": "Save",
97
+ "saving": "Saving…",
98
+ "saved": "Saved. New calls use the configuration immediately.",
99
+ "readPermission": "Allow the agent to read mail",
100
+ "readHint": "List folders, page through searches, read messages, and download attachments. IMAP always stays read-only.",
101
+ "sendPermission": "Allow the agent to send mail",
102
+ "sendHint": "Send plain-text messages. Ordinary permissions ask each time; Full Access does not prompt.",
103
+ "identity": "Mailbox account",
104
+ "identityHint": "Start with the mailbox information you normally use. Most providers require an app password instead of the web-login password.",
105
+ "email": "Email address",
106
+ "emailHint": "Used for both incoming and outgoing mail.",
107
+ "username": "Login username",
108
+ "usernameHint": "Only set this when required by your provider; otherwise the email address is used.",
109
+ "fromName": "Sender display name",
110
+ "fromNameHint": "The name recipients see; optional.",
111
+ "inbox": "Inbox folder",
112
+ "inboxHint": "Usually keep INBOX.",
113
+ "password": "Password / app password",
114
+ "passwordHint": "Stored securely in the local credential store. It is never written to settings.yaml or returned to this page.",
115
+ "configured": "Saved",
116
+ "missing": "Missing",
117
+ "credentialPlaceholder": "Enter an app password; leave blank to keep the saved value",
118
+ "clearPassword": "Clear app password",
119
+ "servers": "Mail servers",
120
+ "serversHint": "Choose a common provider to fill these automatically, or use Manual configuration for institutional and self-hosted mail.",
121
+ "preset": "Mail provider",
122
+ "custom": "Manual configuration",
123
+ "imap": "Incoming (IMAP)",
124
+ "smtp": "Outgoing (SMTP)",
125
+ "host": "Server address",
126
+ "port": "Port",
127
+ "tls": "Encryption",
128
+ "implicit": "Implicit TLS",
129
+ "starttls": "STARTTLS",
130
+ "advanced": "Advanced settings",
131
+ "advancedHint": "Login username, inbox folder, and safety limits usually need no changes.",
132
+ "collapse": "Collapse",
133
+ "agentAccess": "Agent access",
134
+ "agentAccessHint": "After saving the account and servers, enable reading and sending independently as needed.",
135
+ "permissionNeedsSetup": "Enter the email address, app password, and corresponding server first.",
136
+ "limits": "Safety limits",
137
+ "bodyLimit": "Body characters",
138
+ "messageLimit": "Whole-message bytes",
139
+ "attachmentLimit": "Total attachment bytes",
140
+ "usage": "After saving, ask the agent to find recent mail. The plugin never connects to a mailbox during startup or save.",
141
+ "error": "Save failed",
142
+ "reload": "Reload",
143
+ "unavailable": "This connection does not expose the mail settings namespace.",
144
+ "readOnly": "This connection exposes settings in read-only mode.",
145
+ "credentialPartial": "The app password was stored, but the remaining settings failed to save. Fix the form and save again."
146
+ }
147
+ };
148
+ //#endregion
32
149
  //#region src/client/index.tsx
33
150
  const inject = [
34
151
  "slots",
152
+ "locale",
35
153
  "remote",
36
154
  "remote.credentials",
37
- "settingsScope"
155
+ "configForms"
38
156
  ];
39
157
  const NS = "dsh-mail-assistant";
40
158
  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
- unavailable: "当前 DSH 连接未提供此设置命名空间。",
94
- readOnly: "当前连接为只读模式,无法保存设置。",
95
- credentialPartial: "授权码已经保存,但其他配置保存失败;请修正后再次保存。"
96
- } : {
97
- nav: "Mail assistant",
98
- title: "Mail assistant",
99
- 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.",
100
- loading: "Loading configuration…",
101
- save: "Save",
102
- saving: "Saving…",
103
- saved: "Saved. New calls use the configuration immediately.",
104
- readPermission: "Allow the agent to read mail",
105
- readHint: "List folders, page through searches, read messages, and download attachments. IMAP always stays read-only.",
106
- sendPermission: "Allow the agent to send mail",
107
- sendHint: "Send plain-text messages. Ordinary permissions ask each time; Full Access does not prompt.",
108
- identity: "Mailbox account",
109
- identityHint: "Start with the mailbox information you normally use. Most providers require an app password instead of the web-login password.",
110
- email: "Email address",
111
- emailHint: "Used for both incoming and outgoing mail.",
112
- username: "Login username",
113
- usernameHint: "Only set this when required by your provider; otherwise the email address is used.",
114
- fromName: "Sender display name",
115
- fromNameHint: "The name recipients see; optional.",
116
- inbox: "Inbox folder",
117
- inboxHint: "Usually keep INBOX.",
118
- password: "Password / app password",
119
- passwordHint: "Stored securely in DSH Credentials. It is never written to settings.yaml or returned to this page.",
120
- configured: "Saved",
121
- missing: "Missing",
122
- credentialPlaceholder: "Enter an app password; leave blank to keep the saved value",
123
- clearPassword: "Clear app password",
124
- servers: "Mail servers",
125
- serversHint: "Choose a common provider to fill these automatically, or use Manual configuration for institutional and self-hosted mail.",
126
- preset: "Mail provider",
127
- custom: "Manual configuration",
128
- imap: "Incoming (IMAP)",
129
- smtp: "Outgoing (SMTP)",
130
- host: "Server address",
131
- port: "Port",
132
- tls: "Encryption",
133
- implicit: "Implicit TLS",
134
- starttls: "STARTTLS",
135
- advanced: "Advanced settings",
136
- advancedHint: "Login username, inbox folder, and safety limits usually need no changes.",
137
- collapse: "Collapse",
138
- agentAccess: "Agent access",
139
- agentAccessHint: "After saving the account and servers, enable reading and sending independently as needed.",
140
- permissionNeedsSetup: "Enter the email address, app password, and corresponding server first.",
141
- limits: "Safety limits",
142
- bodyLimit: "Body characters",
143
- messageLimit: "Whole-message bytes",
144
- attachmentLimit: "Total attachment bytes",
145
- usage: "After saving, ask the agent to find recent mail. The plugin never connects to a mailbox during startup or save.",
146
- error: "Save failed",
147
- reload: "Reload",
148
- unavailable: "This DSH connection does not expose the mail settings namespace.",
149
- readOnly: "This connection exposes settings in read-only mode.",
150
- credentialPartial: "The app password was stored, but the remaining settings failed to save. Fix the form and save again."
151
- };
152
159
  const defaults = {
153
160
  readEnabled: false,
154
161
  sendEnabled: false,
@@ -221,7 +228,7 @@ window.__ModuleLoader__.load({
221
228
  for (const [key, candidate] of Object.entries(presets)) if (Object.entries(candidate).every(([field, expected]) => value[field] === expected)) return key;
222
229
  return "custom";
223
230
  }
224
- function MailSettings({ service }) {
231
+ function MailSettings({ t, service }) {
225
232
  const [draft, setDraft] = (0, react.useState)(null);
226
233
  const [passwordConfigured, setPasswordConfigured] = (0, react.useState)(false);
227
234
  const [credentialWritable, setCredentialWritable] = (0, react.useState)(false);
@@ -282,10 +289,10 @@ window.__ModuleLoader__.load({
282
289
  path: [field],
283
290
  value
284
291
  }));
285
- await service.scope.mutate(ops, snapshot.revision);
286
- setNotice(copy.saved);
292
+ if (await service.scope.mutate(ops, snapshot.revision) === false) throw new Error(t("error"));
293
+ setNotice(t("saved"));
287
294
  } catch (cause) {
288
- setError(passwordStored ? copy.credentialPartial : cause?.message || `${copy.error}`);
295
+ setError(passwordStored ? t("credentialPartial") : cause?.message || `${t("error")}`);
289
296
  } finally {
290
297
  setBusy(false);
291
298
  }
@@ -298,7 +305,7 @@ window.__ModuleLoader__.load({
298
305
  await unwrap(service.credentials.unset(PASSWORD_REF));
299
306
  setPasswordConfigured(false);
300
307
  setPassword("");
301
- setNotice(copy.saved);
308
+ setNotice(t("saved"));
302
309
  } catch (cause) {
303
310
  setError(cause?.message || String(cause));
304
311
  } finally {
@@ -369,10 +376,10 @@ window.__ModuleLoader__.load({
369
376
  };
370
377
  if (!draft) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
371
378
  style: styles.root,
372
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error || (snapshot.status === "unavailable" ? copy.unavailable : copy.loading) }), error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
379
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error || (snapshot.status === "unavailable" ? t("unavailable") : t("loading")) }), error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
373
380
  style: styles.button,
374
381
  onClick: () => load(),
375
- children: copy.reload
382
+ children: t("reload")
376
383
  })]
377
384
  });
378
385
  const credentialReady = passwordConfigured || password !== "";
@@ -420,7 +427,7 @@ window.__ModuleLoader__.load({
420
427
  color: "#a82332",
421
428
  fontSize: 11
422
429
  },
423
- children: copy.permissionNeedsSetup
430
+ children: t("permissionNeedsSetup")
424
431
  })
425
432
  ] })]
426
433
  });
@@ -453,20 +460,20 @@ window.__ModuleLoader__.load({
453
460
  marginTop: 10
454
461
  },
455
462
  children: [
456
- field(copy.host, `${prefix}Host`),
457
- field(copy.port, `${prefix}Port`, "number"),
463
+ field(t("host"), `${prefix}Host`),
464
+ field(t("port"), `${prefix}Port`, "number"),
458
465
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
459
466
  style: styles.label,
460
- children: [copy.tls, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
467
+ children: [t("tls"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
461
468
  value: draft[`${prefix}Tls`],
462
469
  onChange: (event) => set(`${prefix}Tls`, event.currentTarget.value),
463
470
  style: styles.input,
464
471
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
465
472
  value: "implicit",
466
- children: copy.implicit
473
+ children: t("implicit")
467
474
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
468
475
  value: "starttls",
469
- children: copy.starttls
476
+ children: t("starttls")
470
477
  })]
471
478
  })]
472
479
  })
@@ -481,7 +488,7 @@ window.__ModuleLoader__.load({
481
488
  margin: 0,
482
489
  fontSize: 21
483
490
  },
484
- children: copy.title
491
+ children: t("title")
485
492
  }),
486
493
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
487
494
  style: {
@@ -490,7 +497,7 @@ window.__ModuleLoader__.load({
490
497
  fontSize: 12,
491
498
  lineHeight: 1.65
492
499
  },
493
- children: copy.description
500
+ children: t("description")
494
501
  }),
495
502
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
496
503
  style: styles.card,
@@ -500,7 +507,7 @@ window.__ModuleLoader__.load({
500
507
  margin: 0,
501
508
  fontSize: 15
502
509
  },
503
- children: copy.identity
510
+ children: t("identity")
504
511
  }),
505
512
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
506
513
  style: {
@@ -509,12 +516,12 @@ window.__ModuleLoader__.load({
509
516
  fontSize: 11,
510
517
  lineHeight: 1.55
511
518
  },
512
- children: copy.identityHint
519
+ children: t("identityHint")
513
520
  }),
514
521
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
515
522
  style: styles.grid,
516
523
  children: [
517
- field(copy.email, "email", "email", copy.emailHint),
524
+ field(t("email"), "email", "email", t("emailHint")),
518
525
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
519
526
  style: styles.label,
520
527
  children: [
@@ -524,7 +531,7 @@ window.__ModuleLoader__.load({
524
531
  alignItems: "center",
525
532
  gap: 7
526
533
  },
527
- children: [copy.password, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
534
+ children: [t("password"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
528
535
  style: {
529
536
  borderRadius: 999,
530
537
  padding: "2px 7px",
@@ -532,7 +539,7 @@ window.__ModuleLoader__.load({
532
539
  color: passwordConfigured ? "#32724e" : "#9d2f3f",
533
540
  background: passwordConfigured ? "#eaf6ef" : "#f8e9ec"
534
541
  },
535
- children: passwordConfigured ? copy.configured : copy.missing
542
+ children: passwordConfigured ? t("configured") : t("missing")
536
543
  })]
537
544
  }),
538
545
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
@@ -540,11 +547,11 @@ window.__ModuleLoader__.load({
540
547
  autoComplete: "new-password",
541
548
  value: password,
542
549
  disabled: busy || !credentialWritable,
543
- placeholder: copy.credentialPlaceholder,
550
+ placeholder: t("credentialPlaceholder"),
544
551
  onChange: (event) => setPassword(event.currentTarget.value),
545
552
  style: styles.input
546
553
  }),
547
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: copy.passwordHint }),
554
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("passwordHint") }),
548
555
  passwordConfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
549
556
  type: "button",
550
557
  disabled: busy || !credentialWritable,
@@ -554,11 +561,11 @@ window.__ModuleLoader__.load({
554
561
  color: "#a82332"
555
562
  },
556
563
  onClick: clearPassword,
557
- children: copy.clearPassword
564
+ children: t("clearPassword")
558
565
  })
559
566
  ]
560
567
  }),
561
- field(copy.fromName, "fromName", "text", copy.fromNameHint)
568
+ field(t("fromName"), "fromName", "text", t("fromNameHint"))
562
569
  ]
563
570
  })
564
571
  ]
@@ -578,7 +585,7 @@ window.__ModuleLoader__.load({
578
585
  margin: 0,
579
586
  fontSize: 15
580
587
  },
581
- children: copy.servers
588
+ children: t("servers")
582
589
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
583
590
  style: {
584
591
  margin: "6px 0 0",
@@ -586,20 +593,20 @@ window.__ModuleLoader__.load({
586
593
  fontSize: 11,
587
594
  lineHeight: 1.55
588
595
  },
589
- children: copy.serversHint
596
+ children: t("serversHint")
590
597
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
591
598
  style: {
592
599
  ...styles.label,
593
600
  minWidth: 210
594
601
  },
595
- children: [copy.preset, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
602
+ children: [t("preset"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
596
603
  value: preset,
597
604
  onChange: (event) => applyPreset(event.currentTarget.value),
598
605
  style: styles.input,
599
606
  children: [
600
607
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
601
608
  value: "custom",
602
- children: copy.custom
609
+ children: t("custom")
603
610
  }),
604
611
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
605
612
  value: "gmail",
@@ -629,7 +636,7 @@ window.__ModuleLoader__.load({
629
636
  display: "grid",
630
637
  gap: 11
631
638
  },
632
- children: [server("imap", copy.imap), server("smtp", copy.smtp)]
639
+ children: [server("imap", t("imap")), server("smtp", t("smtp"))]
633
640
  })]
634
641
  }),
635
642
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
@@ -651,7 +658,7 @@ window.__ModuleLoader__.load({
651
658
  display: "block",
652
659
  fontSize: 13
653
660
  },
654
- children: copy.advanced
661
+ children: t("advanced")
655
662
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
656
663
  style: {
657
664
  display: "block",
@@ -659,28 +666,28 @@ window.__ModuleLoader__.load({
659
666
  color: colors.secondary,
660
667
  fontSize: 11
661
668
  },
662
- children: copy.advancedHint
663
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: advancedOpen ? copy.collapse : "›" })]
669
+ children: t("advancedHint")
670
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: advancedOpen ? t("collapse") : "›" })]
664
671
  }), advancedOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
665
672
  style: { marginTop: 14 },
666
673
  children: [
667
674
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
668
675
  style: styles.grid,
669
- children: [field(copy.username, "username", "text", copy.usernameHint), field(copy.inbox, "inboxFolder", "text", copy.inboxHint)]
676
+ children: [field(t("username"), "username", "text", t("usernameHint")), field(t("inbox"), "inboxFolder", "text", t("inboxHint"))]
670
677
  }),
671
678
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
672
679
  style: {
673
680
  margin: "17px 0 10px",
674
681
  fontSize: 13
675
682
  },
676
- children: copy.limits
683
+ children: t("limits")
677
684
  }),
678
685
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
679
686
  style: styles.grid,
680
687
  children: [
681
- field(copy.bodyLimit, "maxBodyChars", "number"),
682
- field(copy.messageLimit, "maxMessageBytes", "number"),
683
- field(copy.attachmentLimit, "maxAttachmentBytes", "number")
688
+ field(t("bodyLimit"), "maxBodyChars", "number"),
689
+ field(t("messageLimit"), "maxMessageBytes", "number"),
690
+ field(t("attachmentLimit"), "maxAttachmentBytes", "number")
684
691
  ]
685
692
  })
686
693
  ]
@@ -694,7 +701,7 @@ window.__ModuleLoader__.load({
694
701
  margin: 0,
695
702
  fontSize: 15
696
703
  },
697
- children: copy.agentAccess
704
+ children: t("agentAccess")
698
705
  }),
699
706
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
700
707
  style: {
@@ -703,11 +710,11 @@ window.__ModuleLoader__.load({
703
710
  fontSize: 11,
704
711
  lineHeight: 1.55
705
712
  },
706
- children: copy.agentAccessHint
713
+ children: t("agentAccessHint")
707
714
  }),
708
715
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
709
716
  style: styles.grid,
710
- children: [permission("readEnabled", copy.readPermission, copy.readHint, readReady), permission("sendEnabled", copy.sendPermission, copy.sendHint, sendReady)]
717
+ children: [permission("readEnabled", t("readPermission"), t("readHint"), readReady), permission("sendEnabled", t("sendPermission"), t("sendHint"), sendReady)]
711
718
  })
712
719
  ]
713
720
  }),
@@ -717,7 +724,7 @@ window.__ModuleLoader__.load({
717
724
  fontSize: 11,
718
725
  lineHeight: 1.55
719
726
  },
720
- children: copy.usage
727
+ children: t("usage")
721
728
  }),
722
729
  !snapshot.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
723
730
  role: "status",
@@ -725,7 +732,7 @@ window.__ModuleLoader__.load({
725
732
  color: colors.secondary,
726
733
  fontSize: 12
727
734
  },
728
- children: copy.readOnly
735
+ children: t("readOnly")
729
736
  }),
730
737
  error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
731
738
  role: "alert",
@@ -748,21 +755,24 @@ window.__ModuleLoader__.load({
748
755
  disabled: busy || !snapshot.writable,
749
756
  onClick: save,
750
757
  style: styles.primary,
751
- children: busy ? copy.saving : copy.save
758
+ children: busy ? t("saving") : t("save")
752
759
  })
753
760
  ]
754
761
  });
755
762
  }
756
763
  function apply(ctx) {
764
+ ctx.effect(() => ctx.locale.register(LOCALE_NS, dictionaries), "mail: dictionaries");
765
+ const t = ctx.locale.bind(LOCALE_NS);
757
766
  const service = {
758
- scope: ctx.settingsScope.bind({ namespace: NS }),
767
+ scope: ctx.configForms.get(NS),
759
768
  credentials: ctx.remote.credentials
760
769
  };
761
770
  ctx.slots.inject("settings.section", () => ctx.slots.register({
762
771
  name: "settings.section",
763
772
  id: "mail-assistant",
773
+ locale: LOCALE_NS,
764
774
  order: 25,
765
- label: () => copy.nav,
775
+ label: () => t("nav"),
766
776
  inject: () => ({ service })
767
777
  }, MailSettings));
768
778
  }