@zaclinley/dsh-mailharbor 0.2.0 → 0.3.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/CHANGELOG.md +23 -0
- package/README.md +38 -21
- package/README.zh.md +31 -15
- package/lib/client.js +677 -92
- package/lib/client.js.map +3 -3
- package/lib/index.js +519 -64
- package/lib/index.js.map +4 -4
- package/package.json +3 -2
- package/scripts/migrate-legacy.mjs +2 -1
package/lib/client.js
CHANGED
|
@@ -34,7 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(index_exports);
|
|
35
35
|
|
|
36
36
|
// src/client/styles.css
|
|
37
|
-
var styles_default = "/* MailHarbor panel + settings.\n Tokens come from the DSH shell (dark by default); fallbacks match the\n default dark theme so the panel also renders in a bare host page.\n Component rules are all scoped under `.mailharbor` and the generic element\n reset lives in `:where()` so it cannot outrank a component class. */\n\n.mailharbor {\n --m-bg: var(--dsw-alias-bg-base, #151517);\n --m-bg-1: var(--dsw-alias-bg-layer-1, #232324);\n --m-bg-2: var(--dsw-alias-bg-layer-2, #2c2c2e);\n --m-bg-3: var(--dsw-alias-bg-layer-3, #353638);\n --m-line: var(--dsw-alias-border-l2, #ffffff1f);\n --m-line-soft: var(--dsw-alias-border-l1, #ffffff0f);\n --m-line-strong: var(--dsw-alias-border-l3, #ffffff29);\n --m-text: var(--dsw-alias-label-primary, #f9fafb);\n --m-text-2: var(--dsw-alias-label-secondary, #cfd3d6);\n --m-text-3: var(--dsw-alias-label-tertiary, #adb2b8);\n --m-caption: var(--dsw-alias-label-caption, #81858c);\n --m-accent: #5686fe;\n --m-accent-soft: rgba(86, 134, 254, 0.14);\n --m-primary-bg: var(--dsw-alias-brand-primary, #f9fafb);\n --m-primary-fg: var(--dsw-alias-label-primary-foreground, #0f1115);\n --m-hover: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n --m-danger: #f25a5a;\n --m-ok: #3fbf7f;\n --m-warn: var(--dsw-alias-state-warn-label, #dd8629);\n --m-radius: 8px;\n --m-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);\n\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--m-bg);\n color: var(--m-text);\n font-size: 13px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n\n.mailharbor *,\n.mailharbor *::before,\n.mailharbor *::after {\n box-sizing: border-box;\n}\n\n/* ---------- base controls ---------- */\n\n.mailharbor :where(button) {\n font: inherit;\n color: inherit;\n background: transparent;\n border: 0;\n margin: 0;\n cursor: pointer;\n}\n\n.mailharbor button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 30px;\n padding: 0 12px;\n border-radius: var(--m-radius);\n border: 1px solid transparent;\n color: var(--m-text-2);\n font-size: 12.5px;\n font-weight: 500;\n white-space: nowrap;\n transition:\n background 120ms ease,\n color 120ms ease,\n border-color 120ms ease;\n}\n\n.mailharbor button:hover:not(:disabled) {\n background: var(--m-hover);\n color: var(--m-text);\n}\n\n.mailharbor button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n\n.mailharbor button.primary {\n background: var(--m-primary-bg);\n color: var(--m-primary-fg);\n border-color: transparent;\n}\n\n.mailharbor button.primary:hover:not(:disabled) {\n background: var(--m-primary-bg);\n filter: brightness(0.94);\n color: var(--m-primary-fg);\n}\n\n.mailharbor button.ghost {\n border-color: var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor button.ghost:hover:not(:disabled) {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor button.subtle {\n color: var(--m-text-3);\n padding: 0 8px;\n}\n\n.mailharbor button.icon {\n width: 30px;\n padding: 0;\n}\n\n.mailharbor button.danger {\n color: var(--m-danger);\n}\n\n.mailharbor input,\n.mailharbor select,\n.mailharbor textarea {\n font: inherit;\n color: var(--m-text);\n background: var(--m-bg-1);\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n padding: 0 10px;\n height: 32px;\n min-width: 0;\n}\n\n.mailharbor textarea {\n height: auto;\n padding: 8px 10px;\n resize: vertical;\n line-height: 1.6;\n}\n\n.mailharbor select {\n padding-right: 26px;\n appearance: none;\n background-image:\n linear-gradient(45deg, transparent 50%, var(--m-text-3) 50%),\n linear-gradient(135deg, var(--m-text-3) 50%, transparent 50%);\n background-position:\n calc(100% - 15px) 14px,\n calc(100% - 10px) 14px;\n background-size:\n 5px 5px,\n 5px 5px;\n background-repeat: no-repeat;\n}\n\n.mailharbor ::placeholder {\n color: var(--m-caption);\n}\n\n.mailharbor input:focus-visible,\n.mailharbor select:focus-visible,\n.mailharbor textarea:focus-visible,\n.mailharbor button:focus-visible,\n.mailharbor [contenteditable='true']:focus-visible {\n outline: 2px solid rgba(86, 134, 254, 0.55);\n outline-offset: -1px;\n}\n\n.mailharbor .spacer {\n flex: 1;\n}\n\n.mailharbor .muted {\n color: var(--m-text-3);\n}\n\n.mailharbor .small {\n font-size: 12px;\n}\n\n.mailharbor .badge {\n display: inline-flex;\n align-items: center;\n height: 18px;\n padding: 0 6px;\n border-radius: 999px;\n font-size: 10.5px;\n font-weight: 500;\n background: var(--m-bg-3);\n color: var(--m-text-2);\n}\n\n.mailharbor .badge.ok {\n background: rgba(63, 191, 127, 0.16);\n color: var(--m-ok);\n}\n\n.mailharbor .badge.warn {\n background: rgba(221, 134, 41, 0.16);\n color: var(--m-warn);\n}\n\n.mailharbor .error {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.1);\n border-bottom: 1px solid rgba(242, 90, 90, 0.2);\n font-size: 12.5px;\n}\n\n/* ---------- shell ---------- */\n\n.mailharbor-topbar {\n display: flex;\n align-items: center;\n gap: 10px;\n height: 52px;\n padding: 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-account {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n max-width: 220px;\n padding: 4px 8px 4px 4px;\n border-radius: 999px;\n border: 1px solid transparent;\n cursor: default;\n}\n\n.mailharbor-account:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-account .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.mailharbor-account select {\n height: 26px;\n background: transparent;\n border-color: transparent;\n font-weight: 500;\n}\n\n.mailharbor-body {\n display: flex;\n flex: 1;\n min-height: 0;\n}\n\n.mailharbor-list {\n display: flex;\n flex-direction: column;\n width: 360px;\n flex: none;\n border-right: 1px solid var(--m-line-soft);\n min-height: 0;\n}\n\n.mailharbor-reader {\n flex: 1;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n@media (max-width: 1000px) {\n .mailharbor-list {\n width: 300px;\n }\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='reader'] .mailharbor-list {\n display: none;\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='list'] .mailharbor-reader {\n display: none;\n}\n\n/* ---------- segmented control ---------- */\n\n.mailharbor-segment {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px;\n border-radius: 9px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-segment button {\n height: 26px;\n padding: 0 12px;\n border-radius: 7px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-segment button:hover:not(.active) {\n color: var(--m-text-2);\n background: transparent;\n}\n\n.mailharbor-segment button.active {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n/* ---------- list ---------- */\n\n.mailharbor-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-filters select {\n width: 128px;\n flex: none;\n font-size: 12px;\n}\n\n.mailharbor-filters input[type='search'] {\n flex: 1;\n height: 30px;\n font-size: 12.5px;\n}\n\n.mailharbor-rows {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n.mailharbor-row {\n display: flex;\n flex-direction: column;\n gap: 3px;\n width: 100%;\n padding: 10px 14px;\n border: 0;\n border-bottom: 1px solid var(--m-line-soft);\n border-left: 2px solid transparent;\n border-radius: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n user-select: none;\n}\n\n.mailharbor-row:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-row.selected {\n background: var(--m-accent-soft);\n border-left-color: var(--m-accent);\n}\n\n.mailharbor-row .line-1,\n.mailharbor-row .line-2 {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n width: 100%;\n}\n\n.mailharbor-row .from {\n font-weight: 600;\n font-size: 12.5px;\n color: var(--m-text-2);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-row .date {\n margin-left: auto;\n color: var(--m-caption);\n font-size: 11px;\n flex: none;\n}\n\n.mailharbor-row .subject {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n.mailharbor-row .snippet {\n color: var(--m-caption);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n width: 100%;\n}\n\n.mailharbor-row.unread .from,\n.mailharbor-row.unread .subject {\n color: var(--m-text);\n}\n\n.mailharbor-row .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--m-accent);\n flex: none;\n}\n\n.mailharbor-row .paperclip {\n color: var(--m-caption);\n flex: none;\n display: inline-flex;\n}\n\n.mailharbor-list-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-top: 1px solid var(--m-line-soft);\n color: var(--m-text-3);\n font-size: 11.5px;\n flex: none;\n}\n\n.mailharbor-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 48px 24px;\n color: var(--m-text-3);\n text-align: center;\n}\n\n.mailharbor-empty svg {\n opacity: 0.5;\n}\n\n.mailharbor-empty .title {\n color: var(--m-text-2);\n font-size: 13px;\n font-weight: 500;\n}\n\n/* ---------- reader ---------- */\n\n.mailharbor-reader-head {\n padding: 16px 20px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mailharbor-reader-title {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mailharbor-reader-title h2 {\n margin: 0;\n font-size: 17px;\n line-height: 1.35;\n font-weight: 600;\n letter-spacing: -0.01em;\n word-break: break-word;\n}\n\n.mailharbor-reader-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 10px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-reader-meta .who {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: var(--m-text-2);\n}\n\n.mailharbor-reader-meta .who strong {\n color: var(--m-text);\n font-weight: 600;\n}\n\n.mailharbor-reader-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mailharbor-reader-body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 14px 18px 18px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-reader-body iframe {\n flex: 1;\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.mailharbor-reader-body pre {\n flex: 1;\n overflow: auto;\n margin: 0;\n padding: 20px 22px;\n white-space: pre-wrap;\n word-break: break-word;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n color: #171a21;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12.5px;\n line-height: 1.7;\n}\n\n.mailharbor-attachments {\n padding: 10px 18px;\n border-top: 1px solid var(--m-line-soft);\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex: none;\n background: var(--m-bg);\n}\n\n.mailharbor-attachment {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n font-size: 12px;\n text-decoration: none;\n color: var(--m-text-2);\n max-width: 260px;\n background: var(--m-bg-1);\n}\n\n.mailharbor-attachment:hover {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor-attachment .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ---------- modal / forms ---------- */\n\n.mailharbor-portal {\n position: fixed;\n inset: 0;\n z-index: 80;\n height: auto;\n background: transparent;\n pointer-events: none;\n}\n\n.mailharbor-portal .mailharbor-modal-mask {\n pointer-events: auto;\n}\n\n.mailharbor-modal-mask {\n position: fixed;\n inset: 0;\n z-index: 60;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(2px);\n}\n\n.mailharbor-modal {\n display: flex;\n flex-direction: column;\n width: min(720px, 100%);\n max-height: 86vh;\n overflow: hidden;\n border: 1px solid var(--m-line);\n border-radius: 14px;\n background: var(--m-bg-1);\n color: var(--m-text);\n box-shadow: var(--m-shadow);\n}\n\n.mailharbor-modal > header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 52px;\n padding: 0 14px 0 18px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 14px;\n font-weight: 600;\n}\n\n.mailharbor-modal .content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 18px;\n overflow: auto;\n}\n\n.mailharbor-modal > footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 18px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.mailharbor-field > label {\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 10px;\n}\n\n.mailharbor-grid.three {\n grid-template-columns: 2fr 90px auto;\n align-items: end;\n}\n\n.mailharbor-toggle {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n color: var(--m-text-2);\n font-size: 12.5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.mailharbor-toggle input[type='checkbox'] {\n width: 14px;\n height: 14px;\n accent-color: var(--m-accent);\n}\n\n/* ---------- composer ---------- */\n\n.mailharbor-toolbar {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-wrap: wrap;\n padding: 6px;\n border: 1px solid var(--m-line-soft);\n border-bottom: 0;\n border-radius: 10px 10px 0 0;\n background: var(--m-bg-2);\n}\n\n.mailharbor-toolbar button {\n height: 26px;\n padding: 0 9px;\n border-radius: 6px;\n color: var(--m-text-2);\n font-size: 12px;\n}\n\n.mailharbor-toolbar button:hover {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n.mailharbor-editor {\n min-height: 240px;\n max-height: 44vh;\n overflow: auto;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 0 0 10px 10px;\n background: #fff;\n color: #171a21;\n outline: none;\n font-size: 13.5px;\n line-height: 1.7;\n}\n\n.mailharbor-editor:empty::before {\n content: attr(data-placeholder);\n color: #9aa0ab;\n}\n\n/* ---------- settings ---------- */\n\n.mailharbor-settings {\n padding: 20px 24px 48px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-width: 880px;\n}\n\n.mailharbor-settings h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n\n.mailharbor-settings h3 {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.mailharbor-account-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.mailharbor-account-row .meta {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.mailharbor-account-row .meta strong {\n font-weight: 600;\n}\n\n.mailharbor-account-row .meta span {\n color: var(--m-text-3);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor .avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n border-radius: 50%;\n background: var(--m-accent-soft);\n color: var(--m-accent);\n font-weight: 600;\n}\n\n.mailharbor-log {\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n color: var(--m-text-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n line-height: 1.6;\n max-height: 220px;\n overflow: auto;\n white-space: pre-wrap;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: minmax(120px, 170px) 1fr;\n gap: 6px 14px;\n font-size: 12px;\n margin: 0;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n color: var(--m-text-2);\n word-break: break-all;\n}\n\n.mailharbor hr {\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n margin: 2px 0;\n}\n";
|
|
37
|
+
var styles_default = "/* MailHarbor panel + settings.\n Tokens come from the DSH shell (dark by default); fallbacks match the\n default dark theme so the panel also renders in a bare host page.\n Component rules are all scoped under `.mailharbor` and the generic element\n reset lives in `:where()` so it cannot outrank a component class. */\n\n.mailharbor {\n --m-bg: var(--dsw-alias-bg-base, #151517);\n --m-bg-1: var(--dsw-alias-bg-layer-1, #232324);\n --m-bg-2: var(--dsw-alias-bg-layer-2, #2c2c2e);\n --m-bg-3: var(--dsw-alias-bg-layer-3, #353638);\n --m-line: var(--dsw-alias-border-l2, #ffffff1f);\n --m-line-soft: var(--dsw-alias-border-l1, #ffffff0f);\n --m-line-strong: var(--dsw-alias-border-l3, #ffffff29);\n --m-text: var(--dsw-alias-label-primary, #f9fafb);\n --m-text-2: var(--dsw-alias-label-secondary, #cfd3d6);\n --m-text-3: var(--dsw-alias-label-tertiary, #adb2b8);\n --m-caption: var(--dsw-alias-label-caption, #81858c);\n --m-accent: #5686fe;\n --m-accent-soft: rgba(86, 134, 254, 0.14);\n --m-primary-bg: var(--dsw-alias-brand-primary, #f9fafb);\n --m-primary-fg: var(--dsw-alias-label-primary-foreground, #0f1115);\n --m-hover: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n --m-danger: #f25a5a;\n --m-ok: #3fbf7f;\n --m-warn: var(--dsw-alias-state-warn-label, #dd8629);\n --m-radius: 8px;\n --m-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);\n\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--m-bg);\n color: var(--m-text);\n font-size: 13px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n\n.mailharbor *,\n.mailharbor *::before,\n.mailharbor *::after {\n box-sizing: border-box;\n}\n\n/* ---------- base controls ---------- */\n\n.mailharbor :where(button) {\n font: inherit;\n color: inherit;\n background: transparent;\n border: 0;\n margin: 0;\n cursor: pointer;\n}\n\n.mailharbor button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 30px;\n padding: 0 12px;\n border-radius: var(--m-radius);\n border: 1px solid transparent;\n color: var(--m-text-2);\n font-size: 12.5px;\n font-weight: 500;\n white-space: nowrap;\n transition:\n background 120ms ease,\n color 120ms ease,\n border-color 120ms ease;\n}\n\n.mailharbor button:hover:not(:disabled) {\n background: var(--m-hover);\n color: var(--m-text);\n}\n\n.mailharbor button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n\n.mailharbor button.primary {\n background: var(--m-primary-bg);\n color: var(--m-primary-fg);\n border-color: transparent;\n}\n\n.mailharbor button.primary:hover:not(:disabled) {\n background: var(--m-primary-bg);\n filter: brightness(0.94);\n color: var(--m-primary-fg);\n}\n\n.mailharbor button.ghost {\n border-color: var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor button.ghost:hover:not(:disabled) {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor button.subtle {\n color: var(--m-text-3);\n padding: 0 8px;\n}\n\n.mailharbor button.icon {\n width: 30px;\n padding: 0;\n}\n\n.mailharbor button.danger {\n color: var(--m-danger);\n}\n\n.mailharbor input,\n.mailharbor select,\n.mailharbor textarea {\n font: inherit;\n color: var(--m-text);\n background: var(--m-bg-1);\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n padding: 0 10px;\n height: 32px;\n min-width: 0;\n}\n\n.mailharbor textarea {\n height: auto;\n padding: 8px 10px;\n resize: vertical;\n line-height: 1.6;\n}\n\n.mailharbor select {\n padding-right: 26px;\n appearance: none;\n background-image:\n linear-gradient(45deg, transparent 50%, var(--m-text-3) 50%),\n linear-gradient(135deg, var(--m-text-3) 50%, transparent 50%);\n background-position:\n calc(100% - 15px) 14px,\n calc(100% - 10px) 14px;\n background-size:\n 5px 5px,\n 5px 5px;\n background-repeat: no-repeat;\n}\n\n.mailharbor ::placeholder {\n color: var(--m-caption);\n}\n\n.mailharbor input:focus-visible,\n.mailharbor select:focus-visible,\n.mailharbor textarea:focus-visible,\n.mailharbor button:focus-visible,\n.mailharbor [contenteditable='true']:focus-visible {\n outline: 2px solid rgba(86, 134, 254, 0.55);\n outline-offset: -1px;\n}\n\n.mailharbor .spacer {\n flex: 1;\n}\n\n.mailharbor .muted {\n color: var(--m-text-3);\n}\n\n.mailharbor .small {\n font-size: 12px;\n}\n\n.mailharbor .badge {\n display: inline-flex;\n align-items: center;\n height: 18px;\n padding: 0 6px;\n border-radius: 999px;\n font-size: 10.5px;\n font-weight: 500;\n background: var(--m-bg-3);\n color: var(--m-text-2);\n}\n\n.mailharbor .badge.ok {\n background: rgba(63, 191, 127, 0.16);\n color: var(--m-ok);\n}\n\n.mailharbor .badge.warn {\n background: rgba(221, 134, 41, 0.16);\n color: var(--m-warn);\n}\n\n.mailharbor .error {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.1);\n border-bottom: 1px solid rgba(242, 90, 90, 0.2);\n font-size: 12.5px;\n}\n\n/* ---------- shell ---------- */\n\n.mailharbor-topbar {\n display: flex;\n align-items: center;\n gap: 10px;\n height: 52px;\n padding: 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-account {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n max-width: 220px;\n padding: 4px 8px 4px 4px;\n border-radius: 999px;\n border: 1px solid transparent;\n cursor: default;\n}\n\n.mailharbor-account:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-account .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.mailharbor-account select {\n height: 26px;\n background: transparent;\n border-color: transparent;\n font-weight: 500;\n}\n\n.mailharbor-body {\n display: flex;\n flex: 1;\n min-height: 0;\n}\n\n.mailharbor-list {\n display: flex;\n flex-direction: column;\n width: 360px;\n flex: none;\n border-right: 1px solid var(--m-line-soft);\n min-height: 0;\n}\n\n.mailharbor-reader {\n flex: 1;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n@media (max-width: 1000px) {\n .mailharbor-list {\n width: 300px;\n }\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='reader'] .mailharbor-list {\n display: none;\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='list'] .mailharbor-reader {\n display: none;\n}\n\n/* ---------- segmented control ---------- */\n\n.mailharbor-segment {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px;\n border-radius: 9px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-segment button {\n height: 26px;\n padding: 0 12px;\n border-radius: 7px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-segment button:hover:not(.active) {\n color: var(--m-text-2);\n background: transparent;\n}\n\n.mailharbor-segment button.active {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n/* ---------- list ---------- */\n\n.mailharbor-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-filters select {\n width: 128px;\n flex: none;\n font-size: 12px;\n}\n\n.mailharbor-filters input[type='search'] {\n flex: 1;\n height: 30px;\n font-size: 12.5px;\n}\n\n.mailharbor-rows {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n.mailharbor-row {\n display: flex;\n flex-direction: column;\n gap: 3px;\n width: 100%;\n padding: 10px 14px;\n border: 0;\n border-bottom: 1px solid var(--m-line-soft);\n border-left: 2px solid transparent;\n border-radius: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n user-select: none;\n}\n\n.mailharbor-row:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-row.selected {\n background: var(--m-accent-soft);\n border-left-color: var(--m-accent);\n}\n\n.mailharbor-row .line-1,\n.mailharbor-row .line-2 {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n width: 100%;\n}\n\n.mailharbor-row .from {\n font-weight: 600;\n font-size: 12.5px;\n color: var(--m-text-2);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-row .date {\n margin-left: auto;\n color: var(--m-caption);\n font-size: 11px;\n flex: none;\n}\n\n.mailharbor-row .subject {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n.mailharbor-row .snippet {\n color: var(--m-caption);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n width: 100%;\n}\n\n.mailharbor-row.unread .from,\n.mailharbor-row.unread .subject {\n color: var(--m-text);\n}\n\n.mailharbor-row .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--m-accent);\n flex: none;\n}\n\n.mailharbor-row .paperclip {\n color: var(--m-caption);\n flex: none;\n display: inline-flex;\n}\n\n.mailharbor-list-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-top: 1px solid var(--m-line-soft);\n color: var(--m-text-3);\n font-size: 11.5px;\n flex: none;\n}\n\n.mailharbor-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 48px 24px;\n color: var(--m-text-3);\n text-align: center;\n}\n\n.mailharbor-empty svg {\n opacity: 0.5;\n}\n\n.mailharbor-empty .title {\n color: var(--m-text-2);\n font-size: 13px;\n font-weight: 500;\n}\n\n/* ---------- reader ---------- */\n\n.mailharbor-reader-head {\n padding: 16px 20px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mailharbor-reader-title {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mailharbor-reader-title h2 {\n margin: 0;\n font-size: 17px;\n line-height: 1.35;\n font-weight: 600;\n letter-spacing: -0.01em;\n word-break: break-word;\n}\n\n.mailharbor-reader-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 10px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-reader-meta .who {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: var(--m-text-2);\n}\n\n.mailharbor-reader-meta .who strong {\n color: var(--m-text);\n font-weight: 600;\n}\n\n.mailharbor-reader-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mailharbor-reader-body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 14px 18px 18px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-reader-body iframe {\n flex: 1;\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.mailharbor-reader-body pre {\n flex: 1;\n overflow: auto;\n margin: 0;\n padding: 20px 22px;\n white-space: pre-wrap;\n word-break: break-word;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n color: #171a21;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12.5px;\n line-height: 1.7;\n}\n\n.mailharbor-attachments {\n padding: 10px 18px;\n border-top: 1px solid var(--m-line-soft);\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex: none;\n background: var(--m-bg);\n}\n\n.mailharbor-attachment {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n font-size: 12px;\n text-decoration: none;\n color: var(--m-text-2);\n max-width: 260px;\n background: var(--m-bg-1);\n}\n\n.mailharbor-attachment:hover {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor-attachment .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ---------- AI summary ---------- */\n\n.mailharbor-divider {\n width: 1px;\n height: 18px;\n margin: 0 4px;\n background: var(--m-line);\n}\n\n.mailharbor-summary {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.mailharbor-summary section h4 {\n margin: 0 0 6px;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-summary section p {\n margin: 0;\n white-space: pre-wrap;\n line-height: 1.75;\n color: var(--m-text);\n}\n\n/* ---------- modal / forms ---------- */\n\n.mailharbor-portal {\n position: fixed;\n inset: 0;\n z-index: 80;\n height: auto;\n background: transparent;\n pointer-events: none;\n}\n\n.mailharbor-portal .mailharbor-modal-mask {\n pointer-events: auto;\n}\n\n.mailharbor-modal-mask {\n position: fixed;\n inset: 0;\n z-index: 60;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(2px);\n}\n\n.mailharbor-modal {\n display: flex;\n flex-direction: column;\n width: min(720px, 100%);\n max-height: 86vh;\n overflow: hidden;\n border: 1px solid var(--m-line);\n border-radius: 14px;\n background: var(--m-bg-1);\n color: var(--m-text);\n box-shadow: var(--m-shadow);\n}\n\n.mailharbor-modal > header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 52px;\n padding: 0 14px 0 18px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 14px;\n font-weight: 600;\n}\n\n.mailharbor-modal .content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 18px;\n overflow: auto;\n}\n\n.mailharbor-modal > footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 18px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.mailharbor-field > label {\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 10px;\n}\n\n.mailharbor-grid.three {\n grid-template-columns: 2fr 90px auto;\n align-items: end;\n}\n\n.mailharbor-toggle {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n color: var(--m-text-2);\n font-size: 12.5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.mailharbor-toggle input[type='checkbox'] {\n width: 14px;\n height: 14px;\n accent-color: var(--m-accent);\n}\n\n/* ---------- composer ---------- */\n\n.mailharbor-toolbar {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-wrap: wrap;\n padding: 6px;\n border: 1px solid var(--m-line-soft);\n border-bottom: 0;\n border-radius: 10px 10px 0 0;\n background: var(--m-bg-2);\n}\n\n.mailharbor-toolbar button {\n height: 26px;\n padding: 0 9px;\n border-radius: 6px;\n color: var(--m-text-2);\n font-size: 12px;\n}\n\n.mailharbor-toolbar button:hover {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n.mailharbor-editor {\n min-height: 240px;\n max-height: 44vh;\n overflow: auto;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 0 0 10px 10px;\n background: #fff;\n color: #171a21;\n outline: none;\n font-size: 13.5px;\n line-height: 1.7;\n}\n\n.mailharbor-editor:empty::before {\n content: attr(data-placeholder);\n color: #9aa0ab;\n}\n\n/* ---------- inline confirm ---------- */\n\n.mailharbor-confirm {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 8px;\n border: 1px solid var(--m-line);\n background: var(--m-bg-2);\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n/* ---------- settings ---------- */\n\n.mailharbor-settings {\n padding: 20px 24px 48px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-width: 880px;\n}\n\n.mailharbor-settings h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n\n.mailharbor-settings h3 {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.mailharbor-account-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.mailharbor-account-row .meta {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.mailharbor-account-row .meta strong {\n font-weight: 600;\n}\n\n.mailharbor-account-row .meta span {\n color: var(--m-text-3);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor .avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n border-radius: 50%;\n background: var(--m-accent-soft);\n color: var(--m-accent);\n font-weight: 600;\n}\n\n.mailharbor-log {\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n color: var(--m-text-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n line-height: 1.6;\n max-height: 220px;\n overflow: auto;\n white-space: pre-wrap;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: minmax(120px, 170px) 1fr;\n gap: 6px 14px;\n font-size: 12px;\n margin: 0;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n color: var(--m-text-2);\n word-break: break-all;\n}\n\n.mailharbor hr {\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n margin: 2px 0;\n}\n";
|
|
38
38
|
|
|
39
39
|
// src/client/icon.tsx
|
|
40
40
|
var import_react = require("react");
|
|
@@ -123,6 +123,29 @@ var api = {
|
|
|
123
123
|
}),
|
|
124
124
|
logout: (id) => request(`accounts/${encodeURIComponent(id)}/logout`, { method: "POST" }),
|
|
125
125
|
check: (id) => request(`accounts/${encodeURIComponent(id)}/check`, { method: "POST" }),
|
|
126
|
+
aiStatus: (accountId) => request(`ai/status${query({ accountId: accountId ?? null })}`),
|
|
127
|
+
aiModels: () => request("ai/models"),
|
|
128
|
+
aiSummary: (payload) => request("ai/summary", {
|
|
129
|
+
method: "POST",
|
|
130
|
+
body: JSON.stringify(payload)
|
|
131
|
+
}),
|
|
132
|
+
aiReply: (payload) => request("ai/reply", {
|
|
133
|
+
method: "POST",
|
|
134
|
+
body: JSON.stringify(payload)
|
|
135
|
+
}),
|
|
136
|
+
aiTranslate: (payload) => request("ai/translate", {
|
|
137
|
+
method: "POST",
|
|
138
|
+
body: JSON.stringify(payload)
|
|
139
|
+
}),
|
|
140
|
+
aiStyle: (accountId) => request(`ai/style${query({ accountId: accountId ?? null })}`),
|
|
141
|
+
aiSaveStyle: (accountId, profile) => request(`ai/style${query({ accountId: accountId ?? null })}`, {
|
|
142
|
+
method: "PUT",
|
|
143
|
+
body: JSON.stringify({ accountId, profile })
|
|
144
|
+
}),
|
|
145
|
+
aiLearnStyle: (accountId) => request(`ai/style/learn${query({ accountId: accountId ?? null })}`, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
body: JSON.stringify({ accountId: accountId ?? null })
|
|
148
|
+
}),
|
|
126
149
|
settings: () => request("settings"),
|
|
127
150
|
saveSettings: (patch) => request("settings", { method: "PATCH", body: JSON.stringify(patch) }),
|
|
128
151
|
backupStatus: (accountId) => request(`backup/status${query({ accountId: accountId ?? null })}`),
|
|
@@ -163,16 +186,24 @@ function MailIcon() {
|
|
|
163
186
|
return (0, import_react.createElement)(
|
|
164
187
|
"span",
|
|
165
188
|
{
|
|
166
|
-
style: {
|
|
189
|
+
style: {
|
|
190
|
+
position: "relative",
|
|
191
|
+
display: "inline-flex",
|
|
192
|
+
alignItems: "center",
|
|
193
|
+
justifyContent: "center",
|
|
194
|
+
width: "18px",
|
|
195
|
+
height: "18px",
|
|
196
|
+
boxSizing: "border-box"
|
|
197
|
+
}
|
|
167
198
|
},
|
|
168
199
|
(0, import_react.createElement)(
|
|
169
200
|
"svg",
|
|
170
|
-
{ width:
|
|
171
|
-
(0, import_react.createElement)("rect", { x: 2.5, y: 5, width: 19, height: 14, rx: 2.5, stroke: "currentColor", strokeWidth: 1.
|
|
201
|
+
{ width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" },
|
|
202
|
+
(0, import_react.createElement)("rect", { x: 2.5, y: 5, width: 19, height: 14, rx: 2.5, stroke: "currentColor", strokeWidth: 1.7 }),
|
|
172
203
|
(0, import_react.createElement)("path", {
|
|
173
204
|
d: "M3.5 7l7.6 5.4a1.6 1.6 0 0 0 1.8 0L20.5 7",
|
|
174
205
|
stroke: "currentColor",
|
|
175
|
-
strokeWidth: 1.
|
|
206
|
+
strokeWidth: 1.7,
|
|
176
207
|
strokeLinecap: "round"
|
|
177
208
|
})
|
|
178
209
|
),
|
|
@@ -181,18 +212,26 @@ function MailIcon() {
|
|
|
181
212
|
{
|
|
182
213
|
style: {
|
|
183
214
|
position: "absolute",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
215
|
+
right: "-5px",
|
|
216
|
+
bottom: "-3px",
|
|
217
|
+
display: "flex",
|
|
218
|
+
alignItems: "center",
|
|
219
|
+
justifyContent: "center",
|
|
220
|
+
boxSizing: "border-box",
|
|
221
|
+
minWidth: "13px",
|
|
222
|
+
height: "13px",
|
|
188
223
|
padding: "0 3px",
|
|
189
224
|
borderRadius: "999px",
|
|
190
|
-
background: "
|
|
225
|
+
background: "#5686fe",
|
|
191
226
|
color: "#fff",
|
|
192
227
|
fontSize: "9px",
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
228
|
+
fontWeight: 600,
|
|
229
|
+
lineHeight: "13px",
|
|
230
|
+
zIndex: 1,
|
|
231
|
+
letterSpacing: "-0.01em",
|
|
232
|
+
fontVariantNumeric: "tabular-nums",
|
|
233
|
+
boxShadow: "0 0 0 2px var(--dsw-alias-bg-base, #151517)",
|
|
234
|
+
pointerEvents: "none"
|
|
196
235
|
}
|
|
197
236
|
},
|
|
198
237
|
unread > 99 ? "99+" : String(unread)
|
|
@@ -621,6 +660,8 @@ function Composer({
|
|
|
621
660
|
const [error, setError] = (0, import_react4.useState)("");
|
|
622
661
|
const [confirming, setConfirming] = (0, import_react4.useState)(false);
|
|
623
662
|
const [draftId, setDraftId] = (0, import_react4.useState)(initial.draftId);
|
|
663
|
+
const [translating, setTranslating] = (0, import_react4.useState)(null);
|
|
664
|
+
const [pendingLang, setPendingLang] = (0, import_react4.useState)(null);
|
|
624
665
|
const editor = (0, import_react4.useRef)(null);
|
|
625
666
|
(0, import_react4.useEffect)(() => {
|
|
626
667
|
if (editor.current !== null) editor.current.innerHTML = initial.html;
|
|
@@ -631,6 +672,24 @@ function Composer({
|
|
|
631
672
|
editor.current?.focus();
|
|
632
673
|
document.execCommand(command, false, value);
|
|
633
674
|
};
|
|
675
|
+
const translate = async (target) => {
|
|
676
|
+
if (editor.current === null) return;
|
|
677
|
+
if (html().trim() === "") {
|
|
678
|
+
setError("\u6B63\u6587\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u7FFB\u8BD1");
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
setPendingLang(null);
|
|
682
|
+
setTranslating(target);
|
|
683
|
+
setError("");
|
|
684
|
+
try {
|
|
685
|
+
const result = await api.aiTranslate({ accountId: account.id, html: html(), target });
|
|
686
|
+
editor.current.innerHTML = result.html;
|
|
687
|
+
} catch (caught) {
|
|
688
|
+
setError(errorText2(caught));
|
|
689
|
+
} finally {
|
|
690
|
+
setTranslating(null);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
634
693
|
const addFiles = async (files) => {
|
|
635
694
|
if (files === null || files.length === 0) return;
|
|
636
695
|
setError("");
|
|
@@ -801,6 +860,16 @@ function Composer({
|
|
|
801
860
|
),
|
|
802
861
|
(0, import_react4.createElement)("button", { onClick: () => exec("removeFormat"), title: "\u6E05\u9664\u683C\u5F0F" }, "\u6E05\u9664"),
|
|
803
862
|
(0, import_react4.createElement)("span", { className: "spacer" }),
|
|
863
|
+
(0, import_react4.createElement)(
|
|
864
|
+
"button",
|
|
865
|
+
{ onClick: () => setPendingLang("zh"), disabled: translating !== null, title: "AI \u7FFB\u8BD1\u6210\u4E2D\u6587" },
|
|
866
|
+
translating === "zh" ? "\u7FFB\u8BD1\u4E2D\u2026" : "\u4E2D"
|
|
867
|
+
),
|
|
868
|
+
(0, import_react4.createElement)(
|
|
869
|
+
"button",
|
|
870
|
+
{ onClick: () => setPendingLang("en"), disabled: translating !== null, title: "AI \u7FFB\u8BD1\u6210\u82F1\u6587" },
|
|
871
|
+
translating === "en" ? "\u2026" : "EN"
|
|
872
|
+
),
|
|
804
873
|
(0, import_react4.createElement)(
|
|
805
874
|
"label",
|
|
806
875
|
{ className: "mailharbor-toggle small" },
|
|
@@ -816,6 +885,14 @@ function Composer({
|
|
|
816
885
|
})
|
|
817
886
|
)
|
|
818
887
|
),
|
|
888
|
+
pendingLang === null ? null : (0, import_react4.createElement)(
|
|
889
|
+
"div",
|
|
890
|
+
{ className: "mailharbor-confirm" },
|
|
891
|
+
(0, import_react4.createElement)("span", null, pendingLang === "en" ? "\u628A\u6B63\u6587\u7FFB\u8BD1\u6210\u82F1\u6587\uFF1F" : "\u628A\u6B63\u6587\u7FFB\u8BD1\u6210\u4E2D\u6587\uFF1F"),
|
|
892
|
+
(0, import_react4.createElement)("span", { className: "spacer" }),
|
|
893
|
+
(0, import_react4.createElement)("button", { className: "ghost", onClick: () => setPendingLang(null) }, "\u53D6\u6D88"),
|
|
894
|
+
(0, import_react4.createElement)("button", { className: "primary", onClick: () => void translate(pendingLang) }, "\u7FFB\u8BD1")
|
|
895
|
+
),
|
|
819
896
|
attachments.length === 0 ? null : (0, import_react4.createElement)(
|
|
820
897
|
"div",
|
|
821
898
|
{ className: "mailharbor-attachments", style: { padding: 0, border: 0 } },
|
|
@@ -934,25 +1011,43 @@ function srcDoc(detail, allowRemoteImages) {
|
|
|
934
1011
|
function escapeHtml(value) {
|
|
935
1012
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
936
1013
|
}
|
|
1014
|
+
function textToHtml(text) {
|
|
1015
|
+
return text.split(/\n{2,}/).map(
|
|
1016
|
+
(paragraph) => `<p>${paragraph.trim().replaceAll("&", "&").replaceAll("<", "<").replaceAll("\n", "<br>")}</p>`
|
|
1017
|
+
).join("");
|
|
1018
|
+
}
|
|
937
1019
|
function hasRemoteImages(detail) {
|
|
938
1020
|
return detail.html !== null && /<img[^>]+src=["']https?:/i.test(detail.html);
|
|
939
1021
|
}
|
|
940
1022
|
function addressLine(list) {
|
|
941
1023
|
return list.map((item) => item.name !== "" ? `${item.name} <${item.address}>` : item.address).join("\u3001");
|
|
942
1024
|
}
|
|
1025
|
+
function errorText3(error) {
|
|
1026
|
+
return error instanceof Error ? error.message : String(error);
|
|
1027
|
+
}
|
|
943
1028
|
function Reader({
|
|
944
1029
|
detail,
|
|
945
|
-
account,
|
|
946
1030
|
source,
|
|
947
1031
|
mailbox,
|
|
948
1032
|
onChanged,
|
|
949
1033
|
onReply,
|
|
1034
|
+
onAiReply,
|
|
950
1035
|
onClose
|
|
951
1036
|
}) {
|
|
952
1037
|
const [plain, setPlain] = (0, import_react6.useState)(false);
|
|
953
1038
|
const [loadImages, setLoadImages] = (0, import_react6.useState)(false);
|
|
954
1039
|
const [busy, setBusy] = (0, import_react6.useState)(false);
|
|
955
1040
|
const [error, setError] = (0, import_react6.useState)("");
|
|
1041
|
+
const [summaryOpen, setSummaryOpen] = (0, import_react6.useState)(false);
|
|
1042
|
+
const [summaryBusy, setSummaryBusy] = (0, import_react6.useState)(false);
|
|
1043
|
+
const [summaryError, setSummaryError] = (0, import_react6.useState)("");
|
|
1044
|
+
const [summaryBlocks, setSummaryBlocks] = (0, import_react6.useState)([]);
|
|
1045
|
+
const [replyOpen, setReplyOpen] = (0, import_react6.useState)(false);
|
|
1046
|
+
const [replyBusy, setReplyBusy] = (0, import_react6.useState)(false);
|
|
1047
|
+
const [replyError, setReplyError] = (0, import_react6.useState)("");
|
|
1048
|
+
const [replyInstruction, setReplyInstruction] = (0, import_react6.useState)("");
|
|
1049
|
+
const [replyLanguage, setReplyLanguage] = (0, import_react6.useState)("zh");
|
|
1050
|
+
const [replyText, setReplyText] = (0, import_react6.useState)("");
|
|
956
1051
|
const remoteImages = (0, import_react6.useMemo)(() => hasRemoteImages(detail), [detail]);
|
|
957
1052
|
const html = detail.html !== null && detail.html !== "";
|
|
958
1053
|
const toggleRead = async () => {
|
|
@@ -961,18 +1056,74 @@ function Reader({
|
|
|
961
1056
|
try {
|
|
962
1057
|
const result = await api.setRead({
|
|
963
1058
|
id: detail.id,
|
|
964
|
-
accountId:
|
|
1059
|
+
accountId: detail.accountId,
|
|
965
1060
|
source,
|
|
966
1061
|
mailbox,
|
|
967
1062
|
read: detail.unread
|
|
968
1063
|
});
|
|
969
1064
|
onChanged({ unread: !result.read });
|
|
970
1065
|
} catch (caught) {
|
|
971
|
-
setError(
|
|
1066
|
+
setError(errorText3(caught));
|
|
972
1067
|
} finally {
|
|
973
1068
|
setBusy(false);
|
|
974
1069
|
}
|
|
975
1070
|
};
|
|
1071
|
+
const openSummary = async () => {
|
|
1072
|
+
setSummaryOpen(true);
|
|
1073
|
+
setSummaryBusy(true);
|
|
1074
|
+
setSummaryError("");
|
|
1075
|
+
setSummaryBlocks([]);
|
|
1076
|
+
try {
|
|
1077
|
+
const result = await api.aiSummary({
|
|
1078
|
+
accountId: detail.accountId,
|
|
1079
|
+
source,
|
|
1080
|
+
id: detail.id,
|
|
1081
|
+
mailbox
|
|
1082
|
+
});
|
|
1083
|
+
setSummaryBlocks(result.blocks);
|
|
1084
|
+
} catch (caught) {
|
|
1085
|
+
setSummaryError(errorText3(caught));
|
|
1086
|
+
} finally {
|
|
1087
|
+
setSummaryBusy(false);
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
const openReply = () => {
|
|
1091
|
+
setReplyOpen(true);
|
|
1092
|
+
setReplyText("");
|
|
1093
|
+
setReplyError("");
|
|
1094
|
+
setReplyInstruction("");
|
|
1095
|
+
setReplyLanguage("zh");
|
|
1096
|
+
};
|
|
1097
|
+
const generateReply = async () => {
|
|
1098
|
+
setReplyBusy(true);
|
|
1099
|
+
setReplyError("");
|
|
1100
|
+
try {
|
|
1101
|
+
const result = await api.aiReply({
|
|
1102
|
+
accountId: detail.accountId,
|
|
1103
|
+
source,
|
|
1104
|
+
id: detail.id,
|
|
1105
|
+
mailbox,
|
|
1106
|
+
instruction: replyInstruction,
|
|
1107
|
+
language: replyLanguage
|
|
1108
|
+
});
|
|
1109
|
+
setReplyText(result.text);
|
|
1110
|
+
} catch (caught) {
|
|
1111
|
+
setReplyError(errorText3(caught));
|
|
1112
|
+
} finally {
|
|
1113
|
+
setReplyBusy(false);
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
const insertReply = () => {
|
|
1117
|
+
const subject = /^re:/i.test(detail.subject) ? detail.subject : `Re: ${detail.subject}`;
|
|
1118
|
+
onAiReply({
|
|
1119
|
+
to: detail.from.address,
|
|
1120
|
+
cc: "",
|
|
1121
|
+
subject,
|
|
1122
|
+
html: textToHtml(replyText),
|
|
1123
|
+
...detail.messageId === "" ? {} : { inReplyTo: detail.messageId, references: [...detail.references, detail.messageId].join(" ") }
|
|
1124
|
+
});
|
|
1125
|
+
setReplyOpen(false);
|
|
1126
|
+
};
|
|
976
1127
|
return (0, import_react6.createElement)(
|
|
977
1128
|
"div",
|
|
978
1129
|
{ className: "mailharbor-reader" },
|
|
@@ -1040,6 +1191,9 @@ function Reader({
|
|
|
1040
1191
|
(0, import_react6.createElement)(Icon, { name: "forward" }),
|
|
1041
1192
|
"\u8F6C\u53D1"
|
|
1042
1193
|
),
|
|
1194
|
+
(0, import_react6.createElement)("span", { className: "mailharbor-divider" }),
|
|
1195
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: () => void openSummary() }, "AI \u6458\u8981"),
|
|
1196
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: openReply }, "AI \u56DE\u590D"),
|
|
1043
1197
|
(0, import_react6.createElement)("span", { className: "spacer" }),
|
|
1044
1198
|
html ? (0, import_react6.createElement)(
|
|
1045
1199
|
"button",
|
|
@@ -1075,13 +1229,13 @@ function Reader({
|
|
|
1075
1229
|
className: "mailharbor-attachment",
|
|
1076
1230
|
key: file.id,
|
|
1077
1231
|
href: source === "server" ? api.attachmentUrl({
|
|
1078
|
-
accountId:
|
|
1232
|
+
accountId: detail.accountId,
|
|
1079
1233
|
source: "server",
|
|
1080
1234
|
mailbox,
|
|
1081
1235
|
uid: detail.uid ?? "",
|
|
1082
1236
|
part: file.id
|
|
1083
1237
|
}) : api.attachmentUrl({
|
|
1084
|
-
accountId:
|
|
1238
|
+
accountId: detail.accountId,
|
|
1085
1239
|
source: "local",
|
|
1086
1240
|
emailId: detail.id,
|
|
1087
1241
|
attachmentId: file.id
|
|
@@ -1094,13 +1248,127 @@ function Reader({
|
|
|
1094
1248
|
(0, import_react6.createElement)("span", { className: "muted small" }, formatBytes(file.size))
|
|
1095
1249
|
)
|
|
1096
1250
|
)
|
|
1097
|
-
)
|
|
1251
|
+
),
|
|
1252
|
+
summaryOpen ? (0, import_react6.createElement)(
|
|
1253
|
+
Modal,
|
|
1254
|
+
{
|
|
1255
|
+
title: "AI \u6458\u8981",
|
|
1256
|
+
onClose: () => setSummaryOpen(false),
|
|
1257
|
+
footer: (0, import_react6.createElement)(
|
|
1258
|
+
"div",
|
|
1259
|
+
{ style: { display: "flex", gap: "8px", width: "100%", justifyContent: "flex-end" } },
|
|
1260
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: () => setSummaryOpen(false) }, "\u5173\u95ED"),
|
|
1261
|
+
summaryBlocks.length === 0 ? null : (0, import_react6.createElement)(
|
|
1262
|
+
"button",
|
|
1263
|
+
{
|
|
1264
|
+
className: "primary",
|
|
1265
|
+
onClick: () => {
|
|
1266
|
+
void navigator.clipboard?.writeText(
|
|
1267
|
+
summaryBlocks.map((block) => `${block.title}
|
|
1268
|
+
${block.body}`).join("\n\n")
|
|
1269
|
+
).then(() => setSummaryOpen(false));
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
"\u590D\u5236"
|
|
1273
|
+
)
|
|
1274
|
+
)
|
|
1275
|
+
},
|
|
1276
|
+
summaryBusy ? "\u6B63\u5728\u9605\u8BFB\u5E76\u603B\u7ED3\u2026" : null,
|
|
1277
|
+
summaryError === "" ? null : (0, import_react6.createElement)("div", { style: { color: "var(--m-danger)" } }, summaryError),
|
|
1278
|
+
summaryBlocks.length === 0 ? null : (0, import_react6.createElement)(
|
|
1279
|
+
"div",
|
|
1280
|
+
{ className: "mailharbor-summary" },
|
|
1281
|
+
summaryBlocks.map(
|
|
1282
|
+
(block, index) => (0, import_react6.createElement)(
|
|
1283
|
+
"section",
|
|
1284
|
+
{ key: `${block.title}:${String(index)}` },
|
|
1285
|
+
block.title === "" ? null : (0, import_react6.createElement)("h4", null, block.title),
|
|
1286
|
+
(0, import_react6.createElement)("p", null, block.body)
|
|
1287
|
+
)
|
|
1288
|
+
)
|
|
1289
|
+
)
|
|
1290
|
+
) : null,
|
|
1291
|
+
replyOpen ? (0, import_react6.createElement)(
|
|
1292
|
+
Modal,
|
|
1293
|
+
{
|
|
1294
|
+
title: "AI \u56DE\u590D",
|
|
1295
|
+
onClose: () => setReplyOpen(false),
|
|
1296
|
+
footer: (0, import_react6.createElement)(
|
|
1297
|
+
"div",
|
|
1298
|
+
{ style: { display: "flex", gap: "8px", width: "100%", alignItems: "center" } },
|
|
1299
|
+
(0, import_react6.createElement)("span", { className: "small muted" }, "\u751F\u6210\u540E\u53EF\u5728\u6B63\u6587\u91CC\u7EE7\u7EED\u4FEE\u6539\uFF0C\u53D1\u9001\u4ECD\u9700\u4F60\u786E\u8BA4"),
|
|
1300
|
+
(0, import_react6.createElement)("span", { className: "spacer" }),
|
|
1301
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: () => setReplyOpen(false) }, "\u53D6\u6D88"),
|
|
1302
|
+
(0, import_react6.createElement)(
|
|
1303
|
+
"button",
|
|
1304
|
+
{ className: "ghost", onClick: () => void generateReply(), disabled: replyBusy },
|
|
1305
|
+
replyBusy ? "\u751F\u6210\u4E2D\u2026" : replyText === "" ? "\u751F\u6210\u56DE\u590D" : "\u91CD\u65B0\u751F\u6210"
|
|
1306
|
+
),
|
|
1307
|
+
(0, import_react6.createElement)(
|
|
1308
|
+
"button",
|
|
1309
|
+
{
|
|
1310
|
+
className: "primary",
|
|
1311
|
+
onClick: insertReply,
|
|
1312
|
+
disabled: replyBusy || replyText.trim() === ""
|
|
1313
|
+
},
|
|
1314
|
+
"\u63D2\u5165\u5230\u6B63\u6587"
|
|
1315
|
+
)
|
|
1316
|
+
)
|
|
1317
|
+
},
|
|
1318
|
+
(0, import_react6.createElement)(
|
|
1319
|
+
"div",
|
|
1320
|
+
{ className: "mailharbor-grid" },
|
|
1321
|
+
(0, import_react6.createElement)(
|
|
1322
|
+
"div",
|
|
1323
|
+
{ className: "mailharbor-field" },
|
|
1324
|
+
(0, import_react6.createElement)("label", null, "\u60F3\u8BA9 AI \u600E\u4E48\u5199\uFF08\u53EF\u7559\u7A7A\uFF09"),
|
|
1325
|
+
(0, import_react6.createElement)("input", {
|
|
1326
|
+
value: replyInstruction,
|
|
1327
|
+
placeholder: "\u4F8B\u5982\uFF1A\u786E\u8BA4\u6536\u5230\uFF0C\u8BF4\u660E\u5468\u4E94\u524D\u7ED9\u7B54\u590D",
|
|
1328
|
+
onChange: (event) => setReplyInstruction(event.target.value)
|
|
1329
|
+
})
|
|
1330
|
+
),
|
|
1331
|
+
(0, import_react6.createElement)(
|
|
1332
|
+
"div",
|
|
1333
|
+
{ className: "mailharbor-field" },
|
|
1334
|
+
(0, import_react6.createElement)("label", null, "\u8BED\u8A00"),
|
|
1335
|
+
(0, import_react6.createElement)(
|
|
1336
|
+
"div",
|
|
1337
|
+
{ className: "mailharbor-segment" },
|
|
1338
|
+
(0, import_react6.createElement)(
|
|
1339
|
+
"button",
|
|
1340
|
+
{
|
|
1341
|
+
className: replyLanguage === "zh" ? "active" : "",
|
|
1342
|
+
onClick: () => setReplyLanguage("zh")
|
|
1343
|
+
},
|
|
1344
|
+
"\u4E2D\u6587"
|
|
1345
|
+
),
|
|
1346
|
+
(0, import_react6.createElement)(
|
|
1347
|
+
"button",
|
|
1348
|
+
{
|
|
1349
|
+
className: replyLanguage === "en" ? "active" : "",
|
|
1350
|
+
onClick: () => setReplyLanguage("en")
|
|
1351
|
+
},
|
|
1352
|
+
"English"
|
|
1353
|
+
)
|
|
1354
|
+
)
|
|
1355
|
+
)
|
|
1356
|
+
),
|
|
1357
|
+
replyError === "" ? null : (0, import_react6.createElement)("div", { style: { color: "var(--m-danger)" } }, replyError),
|
|
1358
|
+
(0, import_react6.createElement)("textarea", {
|
|
1359
|
+
rows: 12,
|
|
1360
|
+
value: replyText,
|
|
1361
|
+
placeholder: replyBusy ? "\u6B63\u5728\u751F\u6210\u2026" : "\u70B9\u300C\u751F\u6210\u56DE\u590D\u300D\u8BA9 AI \u8D77\u8349\uFF0C\u7ED3\u679C\u4F1A\u663E\u793A\u5728\u8FD9\u91CC",
|
|
1362
|
+
onChange: (event) => setReplyText(event.target.value)
|
|
1363
|
+
})
|
|
1364
|
+
) : null
|
|
1098
1365
|
);
|
|
1099
1366
|
}
|
|
1100
1367
|
|
|
1101
1368
|
// src/client/panel.tsx
|
|
1102
1369
|
var EMPTY_PAGE = { items: [], total: 0, offset: 0, limit: 40 };
|
|
1103
|
-
|
|
1370
|
+
var DRAFTS = "__drafts";
|
|
1371
|
+
function errorText4(error) {
|
|
1104
1372
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
1105
1373
|
}
|
|
1106
1374
|
function matchesNarrow() {
|
|
@@ -1113,12 +1381,14 @@ function quoteForReply(detail) {
|
|
|
1113
1381
|
function MailPanel() {
|
|
1114
1382
|
const [state, setState] = (0, import_react7.useState)(null);
|
|
1115
1383
|
const [error, setError] = (0, import_react7.useState)("");
|
|
1384
|
+
const [accountScope, setAccountScope] = (0, import_react7.useState)(null);
|
|
1116
1385
|
const [source, setSource] = (0, import_react7.useState)("local");
|
|
1117
1386
|
const [mailbox, setMailbox] = (0, import_react7.useState)("all");
|
|
1118
1387
|
const [folders, setFolders] = (0, import_react7.useState)([]);
|
|
1119
1388
|
const [query2, setQuery] = (0, import_react7.useState)("");
|
|
1120
1389
|
const [search, setSearch] = (0, import_react7.useState)("");
|
|
1121
1390
|
const [page, setPage] = (0, import_react7.useState)(EMPTY_PAGE);
|
|
1391
|
+
const [drafts, setDrafts] = (0, import_react7.useState)([]);
|
|
1122
1392
|
const [limit, setLimit] = (0, import_react7.useState)(40);
|
|
1123
1393
|
const [loading, setLoading] = (0, import_react7.useState)(false);
|
|
1124
1394
|
const [selected, setSelected] = (0, import_react7.useState)(null);
|
|
@@ -1129,8 +1399,11 @@ function MailPanel() {
|
|
|
1129
1399
|
const [toast, setToast] = (0, import_react7.useState)("");
|
|
1130
1400
|
const [narrow, setNarrow] = (0, import_react7.useState)(() => matchesNarrow());
|
|
1131
1401
|
const [tick, setTick] = (0, import_react7.useState)(0);
|
|
1132
|
-
const
|
|
1133
|
-
const
|
|
1402
|
+
const accounts = state?.accounts ?? [];
|
|
1403
|
+
const isAll = accountScope === "all";
|
|
1404
|
+
const account = accounts.find((item) => item.id === accountScope) ?? null;
|
|
1405
|
+
const composeAccount = accounts.find((item) => item.id === compose?.accountId) ?? account ?? accounts[0] ?? null;
|
|
1406
|
+
const showingDrafts = mailbox === DRAFTS && source === "local";
|
|
1134
1407
|
const refresh = (0, import_react7.useCallback)(() => {
|
|
1135
1408
|
setTick((value) => value + 1);
|
|
1136
1409
|
}, []);
|
|
@@ -1143,29 +1416,32 @@ function MailPanel() {
|
|
|
1143
1416
|
(0, import_react7.useEffect)(() => {
|
|
1144
1417
|
let cancelled = false;
|
|
1145
1418
|
api.state().then((next) => {
|
|
1146
|
-
if (
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1419
|
+
if (cancelled) return;
|
|
1420
|
+
setState(next);
|
|
1421
|
+
if (next.settings.render.listLimit > 0) setLimit(next.settings.render.listLimit);
|
|
1422
|
+
setAccountScope((current) => current ?? next.activeAccountId);
|
|
1150
1423
|
}).catch((caught) => {
|
|
1151
|
-
if (!cancelled) setError(
|
|
1424
|
+
if (!cancelled) setError(errorText4(caught));
|
|
1152
1425
|
});
|
|
1153
1426
|
return () => {
|
|
1154
1427
|
cancelled = true;
|
|
1155
1428
|
};
|
|
1156
1429
|
}, [tick]);
|
|
1157
1430
|
(0, import_react7.useEffect)(() => {
|
|
1158
|
-
if (
|
|
1431
|
+
if (accountScope === null) return;
|
|
1159
1432
|
let cancelled = false;
|
|
1160
|
-
api.folders({ accountId, source }).then((result) => {
|
|
1433
|
+
api.folders({ accountId: accountScope, source }).then((result) => {
|
|
1161
1434
|
if (cancelled) return;
|
|
1162
1435
|
const items2 = source === "local" ? [
|
|
1163
1436
|
{
|
|
1164
1437
|
name: "all",
|
|
1165
1438
|
label: "\u5168\u90E8\u90AE\u4EF6",
|
|
1166
|
-
count: result.items.reduce((sum, item) => sum + item.count, 0)
|
|
1439
|
+
count: result.items.filter((item) => item.name !== DRAFTS).reduce((sum, item) => sum + item.count, 0)
|
|
1167
1440
|
},
|
|
1168
|
-
...result.items.map((item) => ({
|
|
1441
|
+
...result.items.map((item) => ({
|
|
1442
|
+
...item,
|
|
1443
|
+
label: item.name === DRAFTS ? "\u8349\u7A3F" : item.label ?? item.name
|
|
1444
|
+
}))
|
|
1169
1445
|
] : result.items.map((item) => ({ ...item, label: item.label ?? item.name }));
|
|
1170
1446
|
setFolders(items2);
|
|
1171
1447
|
}).catch(() => {
|
|
@@ -1174,26 +1450,56 @@ function MailPanel() {
|
|
|
1174
1450
|
return () => {
|
|
1175
1451
|
cancelled = true;
|
|
1176
1452
|
};
|
|
1177
|
-
}, [
|
|
1453
|
+
}, [accountScope, source, tick]);
|
|
1178
1454
|
(0, import_react7.useEffect)(() => {
|
|
1179
1455
|
setMailbox(source === "local" ? "all" : "INBOX");
|
|
1180
1456
|
setSelected(null);
|
|
1457
|
+
setLimit(state?.settings.render.listLimit ?? 40);
|
|
1181
1458
|
}, [source]);
|
|
1182
1459
|
(0, import_react7.useEffect)(() => {
|
|
1183
|
-
if (
|
|
1460
|
+
if (accountScope === "all" && source === "server") setSource("local");
|
|
1461
|
+
setSelected(null);
|
|
1462
|
+
}, [accountScope, source]);
|
|
1463
|
+
(0, import_react7.useEffect)(() => {
|
|
1464
|
+
if (accountScope === null) return;
|
|
1184
1465
|
let cancelled = false;
|
|
1185
1466
|
setLoading(true);
|
|
1186
|
-
|
|
1187
|
-
|
|
1467
|
+
if (showingDrafts) {
|
|
1468
|
+
const targets = isAll ? accounts : account === null ? [] : [account];
|
|
1469
|
+
Promise.all(
|
|
1470
|
+
targets.map(async (target) => {
|
|
1471
|
+
try {
|
|
1472
|
+
const result = await api.drafts(target.id);
|
|
1473
|
+
return result.items.map((draft) => ({ ...draft, accountId: target.id }));
|
|
1474
|
+
} catch {
|
|
1475
|
+
return [];
|
|
1476
|
+
}
|
|
1477
|
+
})
|
|
1478
|
+
).then((groups) => {
|
|
1479
|
+
if (cancelled) return;
|
|
1480
|
+
setDrafts(groups.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)));
|
|
1481
|
+
setPage(EMPTY_PAGE);
|
|
1482
|
+
}).finally(() => {
|
|
1483
|
+
if (!cancelled) setLoading(false);
|
|
1484
|
+
});
|
|
1485
|
+
return () => {
|
|
1486
|
+
cancelled = true;
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
api.messages({ accountId: accountScope, source, mailbox, query: search, limit }).then((result) => {
|
|
1490
|
+
if (!cancelled) {
|
|
1491
|
+
setPage(result);
|
|
1492
|
+
setDrafts([]);
|
|
1493
|
+
}
|
|
1188
1494
|
}).catch((caught) => {
|
|
1189
|
-
if (!cancelled) setError(
|
|
1495
|
+
if (!cancelled) setError(errorText4(caught));
|
|
1190
1496
|
}).finally(() => {
|
|
1191
1497
|
if (!cancelled) setLoading(false);
|
|
1192
1498
|
});
|
|
1193
1499
|
return () => {
|
|
1194
1500
|
cancelled = true;
|
|
1195
1501
|
};
|
|
1196
|
-
}, [
|
|
1502
|
+
}, [accountScope, source, mailbox, search, limit, tick]);
|
|
1197
1503
|
(0, import_react7.useEffect)(() => {
|
|
1198
1504
|
if (typeof window.matchMedia !== "function") return;
|
|
1199
1505
|
const media = window.matchMedia("(max-width: 900px)");
|
|
@@ -1204,11 +1510,11 @@ function MailPanel() {
|
|
|
1204
1510
|
};
|
|
1205
1511
|
}, []);
|
|
1206
1512
|
(0, import_react7.useEffect)(() => {
|
|
1207
|
-
if (!showBackup ||
|
|
1513
|
+
if (!showBackup || accountScope === null || isAll) return;
|
|
1208
1514
|
let cancelled = false;
|
|
1209
1515
|
const poll = async () => {
|
|
1210
1516
|
try {
|
|
1211
|
-
const status = await api.backupStatus(
|
|
1517
|
+
const status = await api.backupStatus(accountScope);
|
|
1212
1518
|
if (!cancelled) setBackup(status);
|
|
1213
1519
|
} catch {
|
|
1214
1520
|
}
|
|
@@ -1221,16 +1527,17 @@ function MailPanel() {
|
|
|
1221
1527
|
cancelled = true;
|
|
1222
1528
|
window.clearInterval(timer);
|
|
1223
1529
|
};
|
|
1224
|
-
}, [showBackup,
|
|
1530
|
+
}, [showBackup, accountScope, isAll]);
|
|
1225
1531
|
const openMessage = async (item) => {
|
|
1226
|
-
|
|
1532
|
+
const owner = item.accountId;
|
|
1533
|
+
const ownerSource = source;
|
|
1227
1534
|
setError("");
|
|
1228
1535
|
try {
|
|
1229
|
-
const detail = await api.message({ id: item.id, accountId:
|
|
1536
|
+
const detail = await api.message({ id: item.id, accountId: owner, source: ownerSource, mailbox });
|
|
1230
1537
|
setSelected(detail);
|
|
1231
1538
|
if (detail.unread && state?.settings.read.markOnOpen === true) {
|
|
1232
1539
|
try {
|
|
1233
|
-
await api.setRead({ id: detail.id, accountId:
|
|
1540
|
+
await api.setRead({ id: detail.id, accountId: owner, source: ownerSource, mailbox, read: true });
|
|
1234
1541
|
setSelected({ ...detail, unread: false });
|
|
1235
1542
|
setPage((current) => ({
|
|
1236
1543
|
...current,
|
|
@@ -1240,14 +1547,15 @@ function MailPanel() {
|
|
|
1240
1547
|
}
|
|
1241
1548
|
}
|
|
1242
1549
|
} catch (caught) {
|
|
1243
|
-
setError(
|
|
1550
|
+
setError(errorText4(caught));
|
|
1244
1551
|
}
|
|
1245
1552
|
};
|
|
1246
1553
|
const startCompose = (mode) => {
|
|
1247
|
-
|
|
1554
|
+
const owner = account ?? accounts.find((item) => item.id === state?.activeAccountId) ?? accounts[0];
|
|
1555
|
+
if (owner === void 0) return;
|
|
1248
1556
|
const detail = selected;
|
|
1249
1557
|
if (mode === "new" || detail === null) {
|
|
1250
|
-
setCompose({ mode: "new", to: "", cc: "", bcc: "", subject: "", html: "" });
|
|
1558
|
+
setCompose({ mode: "new", accountId: owner.id, to: "", cc: "", bcc: "", subject: "", html: "" });
|
|
1251
1559
|
return;
|
|
1252
1560
|
}
|
|
1253
1561
|
const subjectPrefix = mode === "forward" ? "Fwd: " : "Re: ";
|
|
@@ -1255,6 +1563,7 @@ function MailPanel() {
|
|
|
1255
1563
|
if (mode === "forward") {
|
|
1256
1564
|
setCompose({
|
|
1257
1565
|
mode,
|
|
1566
|
+
accountId: detail.accountId,
|
|
1258
1567
|
to: "",
|
|
1259
1568
|
cc: "",
|
|
1260
1569
|
bcc: "",
|
|
@@ -1265,9 +1574,10 @@ function MailPanel() {
|
|
|
1265
1574
|
return;
|
|
1266
1575
|
}
|
|
1267
1576
|
const to = detail.from.address;
|
|
1268
|
-
const cc = mode === "replyAll" ? [...detail.to, ...detail.cc].map((item) => item.address).filter((address) => address !==
|
|
1577
|
+
const cc = mode === "replyAll" ? [...detail.to, ...detail.cc].map((item) => item.address).filter((address) => address !== owner.email && address !== to).join(", ") : "";
|
|
1269
1578
|
setCompose({
|
|
1270
1579
|
mode,
|
|
1580
|
+
accountId: detail.accountId,
|
|
1271
1581
|
to,
|
|
1272
1582
|
cc,
|
|
1273
1583
|
bcc: "",
|
|
@@ -1276,32 +1586,58 @@ function MailPanel() {
|
|
|
1276
1586
|
...detail.messageId === "" ? {} : { inReplyTo: detail.messageId, references: [...detail.references, detail.messageId].join(" ") }
|
|
1277
1587
|
});
|
|
1278
1588
|
};
|
|
1589
|
+
const openDraft = (draft) => {
|
|
1590
|
+
setCompose({
|
|
1591
|
+
mode: "new",
|
|
1592
|
+
accountId: draft.accountId,
|
|
1593
|
+
to: draft.to,
|
|
1594
|
+
cc: draft.cc,
|
|
1595
|
+
bcc: draft.bcc,
|
|
1596
|
+
subject: draft.subject,
|
|
1597
|
+
html: draft.html,
|
|
1598
|
+
attachments: draft.attachments,
|
|
1599
|
+
draftId: draft.id
|
|
1600
|
+
});
|
|
1601
|
+
};
|
|
1602
|
+
const removeDraft = async (id, accountId) => {
|
|
1603
|
+
try {
|
|
1604
|
+
await api.deleteDraft(id, accountId);
|
|
1605
|
+
setDrafts((current) => current.filter((draft) => draft.id !== id));
|
|
1606
|
+
notice("\u8349\u7A3F\u5DF2\u5220\u9664");
|
|
1607
|
+
refresh();
|
|
1608
|
+
} catch (caught) {
|
|
1609
|
+
setError(errorText4(caught));
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1279
1612
|
const switchAccount = async (id) => {
|
|
1280
1613
|
try {
|
|
1281
|
-
await api.activateAccount(id);
|
|
1614
|
+
if (id !== "all") await api.activateAccount(id);
|
|
1615
|
+
setAccountScope(id);
|
|
1282
1616
|
setSelected(null);
|
|
1617
|
+
if (id === "all") setSource("local");
|
|
1283
1618
|
refresh();
|
|
1284
1619
|
} catch (caught) {
|
|
1285
|
-
setError(
|
|
1620
|
+
setError(errorText4(caught));
|
|
1286
1621
|
}
|
|
1287
1622
|
};
|
|
1288
1623
|
const startBackup = async () => {
|
|
1289
|
-
|
|
1624
|
+
const owner = accountScope === null || isAll ? accounts[0] : accounts.find((item) => item.id === accountScope);
|
|
1625
|
+
if (owner === void 0) return;
|
|
1290
1626
|
try {
|
|
1291
|
-
const result = await api.startBackup(
|
|
1627
|
+
const result = await api.startBackup(owner.id);
|
|
1292
1628
|
setBackup(result.status);
|
|
1293
1629
|
setShowBackup(true);
|
|
1294
1630
|
} catch (caught) {
|
|
1295
|
-
setError(
|
|
1631
|
+
setError(errorText4(caught));
|
|
1296
1632
|
}
|
|
1297
1633
|
};
|
|
1298
1634
|
const stopBackup = async () => {
|
|
1299
|
-
if (
|
|
1635
|
+
if (accountScope === null || isAll) return;
|
|
1300
1636
|
try {
|
|
1301
|
-
const result = await api.stopBackup(
|
|
1637
|
+
const result = await api.stopBackup(accountScope);
|
|
1302
1638
|
setBackup(result.status);
|
|
1303
1639
|
} catch (caught) {
|
|
1304
|
-
setError(
|
|
1640
|
+
setError(errorText4(caught));
|
|
1305
1641
|
}
|
|
1306
1642
|
};
|
|
1307
1643
|
const folderOptions = (0, import_react7.useMemo)(
|
|
@@ -1318,7 +1654,7 @@ function MailPanel() {
|
|
|
1318
1654
|
(0, import_react7.createElement)("div", { className: "mailharbor-empty" }, error === "" ? "\u6B63\u5728\u52A0\u8F7D\u90AE\u7BB1\u2026" : error)
|
|
1319
1655
|
);
|
|
1320
1656
|
}
|
|
1321
|
-
if (
|
|
1657
|
+
if (accounts.length === 0) {
|
|
1322
1658
|
return (0, import_react7.createElement)(
|
|
1323
1659
|
"div",
|
|
1324
1660
|
{ className: "mailharbor" },
|
|
@@ -1335,13 +1671,16 @@ function MailPanel() {
|
|
|
1335
1671
|
(0, import_react7.createElement)(AddAccountForm, {
|
|
1336
1672
|
onDone: (next) => {
|
|
1337
1673
|
setState(next);
|
|
1674
|
+
setAccountScope(next.activeAccountId);
|
|
1338
1675
|
setSource("local");
|
|
1339
1676
|
}
|
|
1340
1677
|
})
|
|
1341
1678
|
)
|
|
1342
1679
|
);
|
|
1343
1680
|
}
|
|
1344
|
-
const
|
|
1681
|
+
const currentAccount = account ?? accounts[0] ?? null;
|
|
1682
|
+
const loggedIn = isAll ? accounts.some((item) => item.id === state.activeAccountId) : state.loggedIn;
|
|
1683
|
+
const needsLogin = source === "server" && !isAll && state.loggedIn === false;
|
|
1345
1684
|
const items = page.items;
|
|
1346
1685
|
return (0, import_react7.createElement)(
|
|
1347
1686
|
"div",
|
|
@@ -1352,17 +1691,19 @@ function MailPanel() {
|
|
|
1352
1691
|
(0, import_react7.createElement)(
|
|
1353
1692
|
"div",
|
|
1354
1693
|
{ className: "mailharbor-account" },
|
|
1355
|
-
(0, import_react7.createElement)(Avatar, { email:
|
|
1356
|
-
|
|
1694
|
+
(0, import_react7.createElement)(Avatar, { email: isAll ? "all" : currentAccount?.email ?? "?", size: 26 }),
|
|
1695
|
+
accounts.length > 1 ? (0, import_react7.createElement)(
|
|
1357
1696
|
"select",
|
|
1358
1697
|
{
|
|
1359
|
-
value:
|
|
1698
|
+
value: accountScope ?? "",
|
|
1360
1699
|
onChange: (event) => void switchAccount(event.target.value),
|
|
1361
1700
|
"aria-label": "\u8D26\u53F7"
|
|
1362
1701
|
},
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1702
|
+
(0, import_react7.createElement)("option", { key: "all", value: "all" }, "\u5168\u90E8\u8D26\u53F7"),
|
|
1703
|
+
accounts.map((item) => (0, import_react7.createElement)("option", { key: item.id, value: item.id }, item.label))
|
|
1704
|
+
) : (0, import_react7.createElement)("span", { className: "name" }, currentAccount?.label ?? ""),
|
|
1705
|
+
isAll ? (0, import_react7.createElement)("span", { className: "badge" }, `${String(accounts.length)} \u4E2A\u8D26\u53F7`) : null,
|
|
1706
|
+
!isAll && state.loggedIn === false ? (0, import_react7.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null
|
|
1366
1707
|
),
|
|
1367
1708
|
(0, import_react7.createElement)(
|
|
1368
1709
|
"div",
|
|
@@ -1374,7 +1715,18 @@ function MailPanel() {
|
|
|
1374
1715
|
),
|
|
1375
1716
|
(0, import_react7.createElement)(
|
|
1376
1717
|
"button",
|
|
1377
|
-
{
|
|
1718
|
+
{
|
|
1719
|
+
className: source === "server" ? "active" : "",
|
|
1720
|
+
onClick: () => {
|
|
1721
|
+
if (isAll) {
|
|
1722
|
+
notice("\u7EDF\u4E00\u6536\u4EF6\u7BB1\u53EA\u652F\u6301\u672C\u5730\u5907\u4EFD\uFF0C\u5207\u6362\u5230\u5355\u4E2A\u8D26\u53F7\u53EF\u4F7F\u7528\u670D\u52A1\u5668\u5B9E\u65F6");
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
setSource("server");
|
|
1726
|
+
},
|
|
1727
|
+
role: "tab",
|
|
1728
|
+
title: isAll ? "\u7EDF\u4E00\u6536\u4EF6\u7BB1\u53EA\u652F\u6301\u672C\u5730\u5907\u4EFD" : "\u8BFB\u53D6\u670D\u52A1\u5668\u4E0A\u7684\u6700\u65B0\u90AE\u4EF6"
|
|
1729
|
+
},
|
|
1378
1730
|
"\u670D\u52A1\u5668\u5B9E\u65F6"
|
|
1379
1731
|
)
|
|
1380
1732
|
),
|
|
@@ -1396,7 +1748,11 @@ function MailPanel() {
|
|
|
1396
1748
|
),
|
|
1397
1749
|
(0, import_react7.createElement)(
|
|
1398
1750
|
"button",
|
|
1399
|
-
{
|
|
1751
|
+
{
|
|
1752
|
+
className: "ghost",
|
|
1753
|
+
onClick: () => void startBackup(),
|
|
1754
|
+
title: isAll ? "\u5907\u4EFD\u5F53\u524D\u6D3B\u52A8\u8D26\u53F7" : "\u5907\u4EFD\u8FD9\u4E2A\u8D26\u53F7"
|
|
1755
|
+
},
|
|
1400
1756
|
(0, import_react7.createElement)(Icon, { name: "archive" }),
|
|
1401
1757
|
"\u5907\u4EFD"
|
|
1402
1758
|
),
|
|
@@ -1409,7 +1765,11 @@ function MailPanel() {
|
|
|
1409
1765
|
),
|
|
1410
1766
|
toast === "" ? null : (0, import_react7.createElement)("div", { className: "small", style: { padding: "6px 14px", color: "var(--m-ok)" } }, toast),
|
|
1411
1767
|
error === "" ? null : (0, import_react7.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
|
|
1412
|
-
needsLogin ? (0, import_react7.createElement)(
|
|
1768
|
+
needsLogin ? (0, import_react7.createElement)(
|
|
1769
|
+
"div",
|
|
1770
|
+
{ className: "mailharbor-settings" },
|
|
1771
|
+
(0, import_react7.createElement)(LoginForm, { account: currentAccount, onDone: refresh })
|
|
1772
|
+
) : (0, import_react7.createElement)(
|
|
1413
1773
|
"div",
|
|
1414
1774
|
{ className: "mailharbor-body", "data-mode": narrow && selected !== null ? "reader" : "list" },
|
|
1415
1775
|
(0, import_react7.createElement)(
|
|
@@ -1434,7 +1794,7 @@ function MailPanel() {
|
|
|
1434
1794
|
),
|
|
1435
1795
|
(0, import_react7.createElement)("input", {
|
|
1436
1796
|
type: "search",
|
|
1437
|
-
placeholder: "\u641C\u7D22\u90AE\u4EF6",
|
|
1797
|
+
placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : "\u641C\u7D22\u90AE\u4EF6",
|
|
1438
1798
|
"aria-label": "\u641C\u7D22\u90AE\u4EF6",
|
|
1439
1799
|
value: query2,
|
|
1440
1800
|
onChange: (event) => setQuery(event.target.value),
|
|
@@ -1462,7 +1822,73 @@ function MailPanel() {
|
|
|
1462
1822
|
(0, import_react7.createElement)(
|
|
1463
1823
|
"div",
|
|
1464
1824
|
{ className: "mailharbor-rows" },
|
|
1465
|
-
loading && items.length === 0 ? (0, import_react7.createElement)("div", { className: "mailharbor-empty" }, "\u6B63\u5728\u8BFB\u53D6\u2026") :
|
|
1825
|
+
loading && items.length === 0 && drafts.length === 0 ? (0, import_react7.createElement)("div", { className: "mailharbor-empty" }, "\u6B63\u5728\u8BFB\u53D6\u2026") : showingDrafts ? drafts.length === 0 ? (0, import_react7.createElement)(
|
|
1826
|
+
"div",
|
|
1827
|
+
{ className: "mailharbor-empty" },
|
|
1828
|
+
(0, import_react7.createElement)(Icon, { name: "compose", size: 28 }),
|
|
1829
|
+
(0, import_react7.createElement)("div", { className: "title" }, "\u6CA1\u6709\u8349\u7A3F"),
|
|
1830
|
+
(0, import_react7.createElement)("div", { className: "small" }, "\u5199\u4FE1\u65F6\u70B9\u300C\u5B58\u8349\u7A3F\u300D\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002")
|
|
1831
|
+
) : drafts.filter(
|
|
1832
|
+
(draft) => search === "" ? true : `${draft.subject} ${draft.to} ${draft.cc}`.toLowerCase().includes(search.toLowerCase())
|
|
1833
|
+
).map(
|
|
1834
|
+
(draft) => (0, import_react7.createElement)(
|
|
1835
|
+
"div",
|
|
1836
|
+
{
|
|
1837
|
+
key: draft.id,
|
|
1838
|
+
className: "mailharbor-row",
|
|
1839
|
+
role: "button",
|
|
1840
|
+
tabIndex: 0,
|
|
1841
|
+
onClick: () => openDraft(draft),
|
|
1842
|
+
onKeyDown: (event) => {
|
|
1843
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1844
|
+
event.preventDefault();
|
|
1845
|
+
openDraft(draft);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
},
|
|
1849
|
+
(0, import_react7.createElement)(
|
|
1850
|
+
"span",
|
|
1851
|
+
{ className: "line-1" },
|
|
1852
|
+
(0, import_react7.createElement)(
|
|
1853
|
+
"span",
|
|
1854
|
+
{ className: "from" },
|
|
1855
|
+
`\u8349\u7A3F${draft.to === "" ? "" : ` \xB7 \u6536\u4EF6\u4EBA ${draft.to}`}`
|
|
1856
|
+
),
|
|
1857
|
+
isAll ? (0, import_react7.createElement)(
|
|
1858
|
+
"span",
|
|
1859
|
+
{ className: "badge" },
|
|
1860
|
+
accounts.find((item) => item.id === draft.accountId)?.label ?? ""
|
|
1861
|
+
) : null,
|
|
1862
|
+
(0, import_react7.createElement)("span", { className: "date" }, shortDate(draft.updatedAt)),
|
|
1863
|
+
(0, import_react7.createElement)(
|
|
1864
|
+
"button",
|
|
1865
|
+
{
|
|
1866
|
+
className: "subtle",
|
|
1867
|
+
title: "\u5220\u9664\u8349\u7A3F",
|
|
1868
|
+
onClick: (event) => {
|
|
1869
|
+
event.stopPropagation();
|
|
1870
|
+
void removeDraft(draft.id, draft.accountId);
|
|
1871
|
+
}
|
|
1872
|
+
},
|
|
1873
|
+
(0, import_react7.createElement)(Icon, { name: "close", size: 13 })
|
|
1874
|
+
)
|
|
1875
|
+
),
|
|
1876
|
+
(0, import_react7.createElement)(
|
|
1877
|
+
"span",
|
|
1878
|
+
{ className: "line-2" },
|
|
1879
|
+
(0, import_react7.createElement)(
|
|
1880
|
+
"span",
|
|
1881
|
+
{ className: "subject" },
|
|
1882
|
+
draft.subject === "" ? "(\u65E0\u4E3B\u9898)" : draft.subject
|
|
1883
|
+
)
|
|
1884
|
+
),
|
|
1885
|
+
draft.html === "" ? null : (0, import_react7.createElement)(
|
|
1886
|
+
"span",
|
|
1887
|
+
{ className: "snippet" },
|
|
1888
|
+
draft.html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 120)
|
|
1889
|
+
)
|
|
1890
|
+
)
|
|
1891
|
+
) : items.length === 0 ? (0, import_react7.createElement)(
|
|
1466
1892
|
"div",
|
|
1467
1893
|
{ className: "mailharbor-empty" },
|
|
1468
1894
|
(0, import_react7.createElement)(Icon, { name: "mail", size: 28 }),
|
|
@@ -1480,8 +1906,8 @@ function MailPanel() {
|
|
|
1480
1906
|
(item) => (0, import_react7.createElement)(
|
|
1481
1907
|
"div",
|
|
1482
1908
|
{
|
|
1483
|
-
key: item.id
|
|
1484
|
-
className: `mailharbor-row${item.unread ? " unread" : ""}${selected?.id === item.id ? " selected" : ""}`,
|
|
1909
|
+
key: `${item.accountId}:${item.id}`,
|
|
1910
|
+
className: `mailharbor-row${item.unread ? " unread" : ""}${selected?.id === item.id && selected?.accountId === item.accountId ? " selected" : ""}`,
|
|
1485
1911
|
role: "button",
|
|
1486
1912
|
tabIndex: 0,
|
|
1487
1913
|
onClick: () => void openMessage(item),
|
|
@@ -1502,15 +1928,24 @@ function MailPanel() {
|
|
|
1502
1928
|
item.from.name !== "" ? item.from.name : item.from.address
|
|
1503
1929
|
),
|
|
1504
1930
|
item.hasAttachments ? (0, import_react7.createElement)(Icon, { name: "paperclip", size: 13 }) : null,
|
|
1931
|
+
isAll ? (0, import_react7.createElement)(
|
|
1932
|
+
"span",
|
|
1933
|
+
{ className: "badge" },
|
|
1934
|
+
accounts.find((entry) => entry.id === item.accountId)?.label ?? ""
|
|
1935
|
+
) : null,
|
|
1505
1936
|
item.backedUp === false ? (0, import_react7.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
|
|
1506
1937
|
(0, import_react7.createElement)("span", { className: "date" }, shortDate(item.date))
|
|
1507
1938
|
),
|
|
1508
|
-
(0, import_react7.createElement)(
|
|
1939
|
+
(0, import_react7.createElement)(
|
|
1940
|
+
"span",
|
|
1941
|
+
{ className: "line-2" },
|
|
1942
|
+
(0, import_react7.createElement)("span", { className: "subject" }, item.subject)
|
|
1943
|
+
),
|
|
1509
1944
|
item.snippet === "" ? null : (0, import_react7.createElement)("span", { className: "snippet" }, item.snippet)
|
|
1510
1945
|
)
|
|
1511
1946
|
)
|
|
1512
1947
|
),
|
|
1513
|
-
items.length > 0 ? (0, import_react7.createElement)(
|
|
1948
|
+
!showingDrafts && items.length > 0 ? (0, import_react7.createElement)(
|
|
1514
1949
|
"div",
|
|
1515
1950
|
{ className: "mailharbor-list-foot" },
|
|
1516
1951
|
(0, import_react7.createElement)("span", null, `${String(items.length)} / ${String(page.total)} \u5C01`),
|
|
@@ -1520,6 +1955,10 @@ function MailPanel() {
|
|
|
1520
1955
|
{ className: "subtle", onClick: () => setLimit((current) => current + 40) },
|
|
1521
1956
|
"\u52A0\u8F7D\u66F4\u591A"
|
|
1522
1957
|
) : null
|
|
1958
|
+
) : showingDrafts && drafts.length > 0 ? (0, import_react7.createElement)(
|
|
1959
|
+
"div",
|
|
1960
|
+
{ className: "mailharbor-list-foot" },
|
|
1961
|
+
(0, import_react7.createElement)("span", null, `${String(drafts.length)} \u5C01\u8349\u7A3F`)
|
|
1523
1962
|
) : null
|
|
1524
1963
|
),
|
|
1525
1964
|
(0, import_react7.createElement)(
|
|
@@ -1533,15 +1972,25 @@ function MailPanel() {
|
|
|
1533
1972
|
(0, import_react7.createElement)(
|
|
1534
1973
|
"div",
|
|
1535
1974
|
{ className: "small" },
|
|
1536
|
-
|
|
1975
|
+
loggedIn ? "\u5DF2\u767B\u5F55\uFF0C\u53EF\u5207\u6362\u300C\u670D\u52A1\u5668\u5B9E\u65F6\u300D\u67E5\u770B\u6700\u65B0\u90AE\u4EF6\u3002" : "\u6D4F\u89C8\u672C\u5730\u5907\u4EFD\u4E0D\u9700\u8981\u767B\u5F55\u3002"
|
|
1537
1976
|
)
|
|
1538
1977
|
) : (0, import_react7.createElement)(Reader, {
|
|
1539
1978
|
detail: selected,
|
|
1540
|
-
account,
|
|
1541
1979
|
source,
|
|
1542
1980
|
mailbox,
|
|
1543
1981
|
onChanged: (patch) => setSelected({ ...selected, ...patch }),
|
|
1544
1982
|
onReply: (mode) => startCompose(mode),
|
|
1983
|
+
onAiReply: (init) => setCompose({
|
|
1984
|
+
mode: "reply",
|
|
1985
|
+
accountId: selected.accountId,
|
|
1986
|
+
to: init.to,
|
|
1987
|
+
cc: init.cc,
|
|
1988
|
+
bcc: "",
|
|
1989
|
+
subject: init.subject,
|
|
1990
|
+
html: init.html,
|
|
1991
|
+
inReplyTo: init.inReplyTo,
|
|
1992
|
+
references: init.references
|
|
1993
|
+
}),
|
|
1545
1994
|
onClose: narrow ? () => setSelected(null) : void 0
|
|
1546
1995
|
})
|
|
1547
1996
|
)
|
|
@@ -1578,8 +2027,8 @@ function MailPanel() {
|
|
|
1578
2027
|
backup.output.length === 0 ? "\uFF08\u7B49\u5F85\u8F93\u51FA\u2026\uFF09" : backup.output.join("\n")
|
|
1579
2028
|
)
|
|
1580
2029
|
) : null,
|
|
1581
|
-
compose !== null ? (0, import_react7.createElement)(Composer, {
|
|
1582
|
-
account,
|
|
2030
|
+
compose !== null && composeAccount !== null ? (0, import_react7.createElement)(Composer, {
|
|
2031
|
+
account: composeAccount,
|
|
1583
2032
|
initial: compose,
|
|
1584
2033
|
onClose: () => setCompose(null),
|
|
1585
2034
|
onSent: (message) => notice(message)
|
|
@@ -1588,6 +2037,7 @@ function MailPanel() {
|
|
|
1588
2037
|
onClose: () => setShowAdd(false),
|
|
1589
2038
|
onDone: (next) => {
|
|
1590
2039
|
setState(next);
|
|
2040
|
+
setAccountScope(next.activeAccountId);
|
|
1591
2041
|
refresh();
|
|
1592
2042
|
}
|
|
1593
2043
|
}) : null
|
|
@@ -1596,7 +2046,7 @@ function MailPanel() {
|
|
|
1596
2046
|
|
|
1597
2047
|
// src/client/settings.tsx
|
|
1598
2048
|
var import_react8 = require("react");
|
|
1599
|
-
function
|
|
2049
|
+
function errorText5(error) {
|
|
1600
2050
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
1601
2051
|
}
|
|
1602
2052
|
function MailSettings() {
|
|
@@ -1611,6 +2061,11 @@ function MailSettings() {
|
|
|
1611
2061
|
const [aiEnabled, setAiEnabled] = (0, import_react8.useState)(true);
|
|
1612
2062
|
const [aiProvider, setAiProvider] = (0, import_react8.useState)("deepseek-account");
|
|
1613
2063
|
const [aiModel, setAiModel] = (0, import_react8.useState)("deepseek-flash");
|
|
2064
|
+
const [aiModels, setAiModels] = (0, import_react8.useState)({ providers: [] });
|
|
2065
|
+
const [aiStatus, setAiStatus] = (0, import_react8.useState)(null);
|
|
2066
|
+
const [styleText, setStyleText] = (0, import_react8.useState)("");
|
|
2067
|
+
const [styleMeta, setStyleMeta] = (0, import_react8.useState)({ updatedAt: "", sampleCount: 0 });
|
|
2068
|
+
const [styleBusy, setStyleBusy] = (0, import_react8.useState)(null);
|
|
1614
2069
|
const [editing, setEditing] = (0, import_react8.useState)(null);
|
|
1615
2070
|
const [checkResult, setCheckResult] = (0, import_react8.useState)("");
|
|
1616
2071
|
const [adding, setAdding] = (0, import_react8.useState)(false);
|
|
@@ -1629,12 +2084,37 @@ function MailSettings() {
|
|
|
1629
2084
|
setAiProvider(view.ai.provider);
|
|
1630
2085
|
setAiModel(view.ai.model);
|
|
1631
2086
|
}).catch((caught) => {
|
|
1632
|
-
if (!cancelled) setError(
|
|
2087
|
+
if (!cancelled) setError(errorText5(caught));
|
|
1633
2088
|
});
|
|
1634
2089
|
return () => {
|
|
1635
2090
|
cancelled = true;
|
|
1636
2091
|
};
|
|
1637
2092
|
}, [tick]);
|
|
2093
|
+
(0, import_react8.useEffect)(() => {
|
|
2094
|
+
let cancelled = false;
|
|
2095
|
+
const accountId = settings?.activeAccountId ?? state?.activeAccountId ?? null;
|
|
2096
|
+
if (accountId === null) return;
|
|
2097
|
+
Promise.all([api.aiStatus(accountId), api.aiModels(), api.aiStyle(accountId)]).then(([status, models, style]) => {
|
|
2098
|
+
if (cancelled) return;
|
|
2099
|
+
setAiStatus(status);
|
|
2100
|
+
setAiModels(models);
|
|
2101
|
+
setStyleText(style.style?.profile ?? "");
|
|
2102
|
+
setStyleMeta({
|
|
2103
|
+
updatedAt: style.style?.updatedAt ?? "",
|
|
2104
|
+
sampleCount: style.style?.sampleCount ?? 0
|
|
2105
|
+
});
|
|
2106
|
+
const preferred = models.providers.find((item) => item.provider === status.provider) ?? models.providers[0];
|
|
2107
|
+
if (preferred !== void 0 && status.provider === "") {
|
|
2108
|
+
setAiProvider(preferred.provider);
|
|
2109
|
+
const firstModel = preferred.models[0];
|
|
2110
|
+
if (firstModel !== void 0) setAiModel(firstModel.id);
|
|
2111
|
+
}
|
|
2112
|
+
}).catch(() => {
|
|
2113
|
+
});
|
|
2114
|
+
return () => {
|
|
2115
|
+
cancelled = true;
|
|
2116
|
+
};
|
|
2117
|
+
}, [tick, settings?.activeAccountId]);
|
|
1638
2118
|
const reload = () => setTick((value) => value + 1);
|
|
1639
2119
|
const notify = (message) => {
|
|
1640
2120
|
setToast(message);
|
|
@@ -1648,7 +2128,7 @@ function MailSettings() {
|
|
|
1648
2128
|
setSettings(result.settings);
|
|
1649
2129
|
notify("\u8BBE\u7F6E\u5DF2\u4FDD\u5B58");
|
|
1650
2130
|
} catch (caught) {
|
|
1651
|
-
setError(
|
|
2131
|
+
setError(errorText5(caught));
|
|
1652
2132
|
}
|
|
1653
2133
|
};
|
|
1654
2134
|
const account = settings?.accounts.find((item) => item.id === settings.activeAccountId) ?? null;
|
|
@@ -1700,7 +2180,7 @@ function MailSettings() {
|
|
|
1700
2180
|
`IMAP \u6B63\u5E38\uFF08${String(result.imap.mailboxes.length)} \u4E2A\u6587\u4EF6\u5939\uFF09\xB7 SMTP ${result.smtp.ok ? "\u6B63\u5E38" : `\u5931\u8D25: ${result.smtp.error ?? ""}`}`
|
|
1701
2181
|
);
|
|
1702
2182
|
} catch (caught) {
|
|
1703
|
-
setCheckResult(`\u5931\u8D25: ${
|
|
2183
|
+
setCheckResult(`\u5931\u8D25: ${errorText5(caught)}`);
|
|
1704
2184
|
}
|
|
1705
2185
|
},
|
|
1706
2186
|
onSave: async (patch) => {
|
|
@@ -1787,34 +2267,135 @@ function MailSettings() {
|
|
|
1787
2267
|
setAiEnabled(value);
|
|
1788
2268
|
void saveGeneral({ ai: { enabled: value } });
|
|
1789
2269
|
},
|
|
1790
|
-
label: "\u542F\u7528 AI\uFF08\u6458\u8981\
|
|
2270
|
+
label: "\u542F\u7528 AI\uFF08\u6458\u8981\u3001AI \u56DE\u590D\u3001\u7FFB\u8BD1\u3001\u5199\u4F5C\u98CE\u683C\uFF09"
|
|
1791
2271
|
}),
|
|
2272
|
+
aiStatus === null ? null : (0, import_react8.createElement)(
|
|
2273
|
+
"div",
|
|
2274
|
+
{ className: "small", style: { color: aiStatus.available ? "var(--m-ok)" : "var(--m-warn)" } },
|
|
2275
|
+
aiStatus.available ? `\u6A21\u578B\u670D\u52A1\u53EF\u7528 \xB7 ${aiStatus.provider} / ${aiStatus.model}` : aiStatus.error ?? "\u5F53\u524D DSH \u7EC4\u5408\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B\u670D\u52A1"
|
|
2276
|
+
),
|
|
1792
2277
|
(0, import_react8.createElement)(
|
|
1793
2278
|
"div",
|
|
1794
2279
|
{ className: "mailharbor-grid" },
|
|
1795
2280
|
(0, import_react8.createElement)(
|
|
1796
2281
|
Field,
|
|
1797
2282
|
{ label: "Provider" },
|
|
1798
|
-
(0, import_react8.createElement)("input", {
|
|
2283
|
+
aiModels.providers.length === 0 ? (0, import_react8.createElement)("input", {
|
|
1799
2284
|
value: aiProvider,
|
|
1800
2285
|
onChange: (event) => setAiProvider(event.target.value),
|
|
1801
2286
|
onBlur: () => void saveGeneral({ ai: { provider: aiProvider } })
|
|
1802
|
-
})
|
|
2287
|
+
}) : (0, import_react8.createElement)(
|
|
2288
|
+
"select",
|
|
2289
|
+
{
|
|
2290
|
+
value: aiProvider,
|
|
2291
|
+
onChange: (event) => {
|
|
2292
|
+
setAiProvider(event.target.value);
|
|
2293
|
+
const provider = aiModels.providers.find((item) => item.provider === event.target.value);
|
|
2294
|
+
const first = provider?.models[0];
|
|
2295
|
+
if (first !== void 0) setAiModel(first.id);
|
|
2296
|
+
void saveGeneral({
|
|
2297
|
+
ai: {
|
|
2298
|
+
provider: event.target.value,
|
|
2299
|
+
...first === void 0 ? {} : { model: first.id }
|
|
2300
|
+
}
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
},
|
|
2304
|
+
aiModels.providers.map(
|
|
2305
|
+
(item) => (0, import_react8.createElement)("option", { key: item.provider, value: item.provider }, item.name)
|
|
2306
|
+
)
|
|
2307
|
+
)
|
|
1803
2308
|
),
|
|
1804
2309
|
(0, import_react8.createElement)(
|
|
1805
2310
|
Field,
|
|
1806
2311
|
{ label: "\u6A21\u578B" },
|
|
1807
|
-
(
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
2312
|
+
(() => {
|
|
2313
|
+
const provider = aiModels.providers.find((item) => item.provider === aiProvider);
|
|
2314
|
+
return provider === void 0 || provider.models.length === 0 ? (0, import_react8.createElement)("input", {
|
|
2315
|
+
value: aiModel,
|
|
2316
|
+
onChange: (event) => setAiModel(event.target.value),
|
|
2317
|
+
onBlur: () => void saveGeneral({ ai: { model: aiModel } })
|
|
2318
|
+
}) : (0, import_react8.createElement)(
|
|
2319
|
+
"select",
|
|
2320
|
+
{
|
|
2321
|
+
value: aiModel,
|
|
2322
|
+
onChange: (event) => {
|
|
2323
|
+
setAiModel(event.target.value);
|
|
2324
|
+
void saveGeneral({ ai: { model: event.target.value } });
|
|
2325
|
+
}
|
|
2326
|
+
},
|
|
2327
|
+
provider.models.map((item) => (0, import_react8.createElement)("option", { key: item.id, value: item.id }, item.name))
|
|
2328
|
+
);
|
|
2329
|
+
})()
|
|
2330
|
+
)
|
|
2331
|
+
),
|
|
2332
|
+
(0, import_react8.createElement)("hr", null),
|
|
2333
|
+
(0, import_react8.createElement)(
|
|
2334
|
+
"div",
|
|
2335
|
+
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
|
|
2336
|
+
(0, import_react8.createElement)("strong", { style: { fontSize: "12.5px" } }, "\u5199\u4F5C\u98CE\u683C"),
|
|
2337
|
+
(0, import_react8.createElement)(
|
|
2338
|
+
"span",
|
|
2339
|
+
{ className: "small muted" },
|
|
2340
|
+
styleMeta.updatedAt === "" ? "\u8FD8\u6CA1\u6709\u751F\u6210" : `${styleMeta.sampleCount} \u5C01\u5DF2\u53D1\u9001\u90AE\u4EF6 \xB7 ${new Date(styleMeta.updatedAt).toLocaleString()}`
|
|
2341
|
+
)
|
|
2342
|
+
),
|
|
2343
|
+
(0, import_react8.createElement)(
|
|
2344
|
+
"p",
|
|
2345
|
+
{ className: "small muted" },
|
|
2346
|
+
"\u4ECE\u5DF2\u53D1\u9001\u90AE\u4EF6\u5B66\u4E60\u4F60\u7684\u79F0\u547C\u3001\u7ED3\u5C3E\u3001\u8BED\u6C14\u548C\u5E38\u7528\u8868\u8FBE\uFF0CAI \u56DE\u590D\u65F6\u4F1A\u53C2\u8003\u5B83\u3002\u53EA\u4FDD\u5B58\u63D0\u70BC\u540E\u7684\u6863\u6848\uFF0C\u4E0D\u4FDD\u5B58\u90AE\u4EF6\u539F\u6587\u3002"
|
|
2347
|
+
),
|
|
2348
|
+
(0, import_react8.createElement)("textarea", {
|
|
2349
|
+
rows: 6,
|
|
2350
|
+
value: styleText,
|
|
2351
|
+
placeholder: "\u70B9\u300C\u4ECE\u5DF2\u53D1\u9001\u90AE\u4EF6\u5B66\u4E60\u300D\u751F\u6210\uFF0C\u6216\u76F4\u63A5\u5199\u4E00\u6BB5\u4F60\u7684\u5199\u4F5C\u98CE\u683C",
|
|
2352
|
+
onChange: (event) => setStyleText(event.target.value)
|
|
2353
|
+
}),
|
|
2354
|
+
(0, import_react8.createElement)(
|
|
2355
|
+
"div",
|
|
2356
|
+
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
2357
|
+
(0, import_react8.createElement)(
|
|
2358
|
+
"button",
|
|
2359
|
+
{
|
|
2360
|
+
className: "ghost",
|
|
2361
|
+
disabled: styleBusy !== null,
|
|
2362
|
+
onClick: () => {
|
|
2363
|
+
const accountId = settings.activeAccountId;
|
|
2364
|
+
if (accountId === null) return;
|
|
2365
|
+
setStyleBusy("learn");
|
|
2366
|
+
setError("");
|
|
2367
|
+
api.aiLearnStyle(accountId).then((result) => {
|
|
2368
|
+
setStyleText(result.style.profile);
|
|
2369
|
+
setStyleMeta({ updatedAt: result.style.updatedAt, sampleCount: result.style.sampleCount });
|
|
2370
|
+
notify("\u5DF2\u6839\u636E\u5DF2\u53D1\u9001\u90AE\u4EF6\u751F\u6210\u5199\u4F5C\u98CE\u683C");
|
|
2371
|
+
}).catch((caught) => setError(errorText5(caught))).finally(() => setStyleBusy(null));
|
|
2372
|
+
}
|
|
2373
|
+
},
|
|
2374
|
+
styleBusy === "learn" ? "\u5B66\u4E60\u4E2D\u2026" : "\u4ECE\u5DF2\u53D1\u9001\u90AE\u4EF6\u5B66\u4E60"
|
|
2375
|
+
),
|
|
2376
|
+
(0, import_react8.createElement)(
|
|
2377
|
+
"button",
|
|
2378
|
+
{
|
|
2379
|
+
className: "primary",
|
|
2380
|
+
disabled: styleBusy !== null || styleText.trim() === "",
|
|
2381
|
+
onClick: () => {
|
|
2382
|
+
const accountId = settings.activeAccountId;
|
|
2383
|
+
if (accountId === null) return;
|
|
2384
|
+
setStyleBusy("save");
|
|
2385
|
+
setError("");
|
|
2386
|
+
api.aiSaveStyle(accountId, styleText).then((result) => {
|
|
2387
|
+
setStyleMeta({ updatedAt: result.style.updatedAt, sampleCount: result.style.sampleCount });
|
|
2388
|
+
notify("\u5199\u4F5C\u98CE\u683C\u5DF2\u4FDD\u5B58");
|
|
2389
|
+
}).catch((caught) => setError(errorText5(caught))).finally(() => setStyleBusy(null));
|
|
2390
|
+
}
|
|
2391
|
+
},
|
|
2392
|
+
"\u4FDD\u5B58\u98CE\u683C"
|
|
1812
2393
|
)
|
|
1813
2394
|
),
|
|
1814
2395
|
(0, import_react8.createElement)(
|
|
1815
2396
|
"p",
|
|
1816
2397
|
{ className: "small muted" },
|
|
1817
|
-
"AI \u8C03\u7528\u8D70 DSH \u81EA\u5DF1\u7684\u6A21\u578B\u670D\u52A1\uFF0C\u4E0D\u989D\u5916\u4FDD\u5B58\u5BC6\u94A5\
|
|
2398
|
+
"AI \u8C03\u7528\u8D70 DSH \u81EA\u5DF1\u7684\u6A21\u578B\u670D\u52A1\uFF0C\u4E0D\u989D\u5916\u4FDD\u5B58\u5BC6\u94A5\uFF1B\u53D1\u9001\u4ECD\u7136\u53EA\u7531\u4F60\u786E\u8BA4\u3002"
|
|
1818
2399
|
)
|
|
1819
2400
|
),
|
|
1820
2401
|
(0, import_react8.createElement)(
|
|
@@ -1829,7 +2410,11 @@ function MailSettings() {
|
|
|
1829
2410
|
(0, import_react8.createElement)("dt", null, "\u914D\u7F6E\u6587\u4EF6"),
|
|
1830
2411
|
(0, import_react8.createElement)("dd", null, settings.configFile),
|
|
1831
2412
|
(0, import_react8.createElement)("dt", null, "\u5F53\u524D\u8D26\u53F7\u6570\u636E\u5E93"),
|
|
1832
|
-
(0, import_react8.createElement)(
|
|
2413
|
+
(0, import_react8.createElement)(
|
|
2414
|
+
"dd",
|
|
2415
|
+
null,
|
|
2416
|
+
account === null ? "\u2014" : `${account.dataDir.startsWith("/") ? account.dataDir : `${settings.dataDir}/${account.dataDir}`}/mail.db`
|
|
2417
|
+
),
|
|
1833
2418
|
(0, import_react8.createElement)("dt", null, "\u63D2\u4EF6\u7248\u672C"),
|
|
1834
2419
|
(0, import_react8.createElement)("dd", null, state.version)
|
|
1835
2420
|
),
|
|
@@ -1846,7 +2431,7 @@ function MailSettings() {
|
|
|
1846
2431
|
notify(status.status.message === "" ? "\u5907\u4EFD\u5DF2\u5F00\u59CB" : status.status.message);
|
|
1847
2432
|
reload();
|
|
1848
2433
|
} catch (caught) {
|
|
1849
|
-
setError(
|
|
2434
|
+
setError(errorText5(caught));
|
|
1850
2435
|
}
|
|
1851
2436
|
}
|
|
1852
2437
|
},
|
|
@@ -2057,7 +2642,7 @@ function AccountRow({
|
|
|
2057
2642
|
}
|
|
2058
2643
|
|
|
2059
2644
|
// src/client/index.tsx
|
|
2060
|
-
var VERSION = "0.
|
|
2645
|
+
var VERSION = "0.3.0";
|
|
2061
2646
|
var inject = ["slots"];
|
|
2062
2647
|
var PANEL_ID = "mailharbor";
|
|
2063
2648
|
var STYLE_ID = "mailharbor-style";
|