@zaclinley/dsh-mailharbor 0.3.0 → 0.4.1
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 +27 -0
- package/README.md +7 -4
- package/README.zh.md +7 -4
- package/lib/client.js +1229 -783
- package/lib/client.js.map +4 -4
- package/lib/index.js +342 -17
- package/lib/index.js.map +4 -4
- package/package.json +1 -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/* ---------- 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";
|
|
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 position: relative;\n}\n\n.mailharbor button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\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 rail ---------- */\n\n.mailharbor-ai {\n display: flex;\n flex-direction: column;\n width: 340px;\n flex: none;\n min-height: 0;\n border-left: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-ai-head {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 46px;\n padding: 0 8px 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-head strong {\n font-size: 13px;\n font-weight: 600;\n}\n\n.mailharbor-ai-confirm {\n display: inline-flex;\n gap: 2px;\n}\n\n.mailharbor-ai-context {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 9px 14px 10px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-context strong {\n font-size: 12.5px;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-ai-warn {\n padding: 8px 14px;\n color: var(--m-warn);\n background: rgba(221, 134, 41, 0.1);\n font-size: 12px;\n flex: none;\n}\n\n.mailharbor-ai-quick {\n display: flex;\n gap: 6px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-quick button {\n flex: 1;\n padding: 0 6px;\n}\n\n.mailharbor-ai-list {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n}\n\n.mailharbor-ai-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 30px 12px;\n text-align: center;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-empty .title {\n color: var(--m-text-2);\n font-weight: 500;\n font-size: 12.5px;\n}\n\n.mailharbor-ai-card {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n overflow: hidden;\n}\n\n.mailharbor-ai-card.generating {\n opacity: 0.7;\n}\n\n.mailharbor-ai-card-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 8px 7px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-card-head > span:first-child {\n color: var(--m-text-2);\n font-weight: 600;\n}\n\n.mailharbor-ai-summary {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 10px 12px;\n}\n\n.mailharbor-ai-summary section h4 {\n margin: 0 0 3px;\n font-size: 11px;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mailharbor-ai-summary section p {\n margin: 0;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-text {\n padding: 10px 12px;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n padding: 6px 8px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-ai-card-actions button {\n height: 26px;\n padding: 0 8px;\n font-size: 11.5px;\n}\n\n.mailharbor-ai-ask {\n display: flex;\n gap: 6px;\n padding: 10px 12px;\n border-top: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-ask input {\n flex: 1;\n height: 32px;\n}\n\n.mailharbor-ai-error {\n color: var(--m-danger);\n font-size: 12px;\n padding: 0 2px;\n}\n\n@media (max-width: 1280px) {\n .mailharbor-ai {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(420px, 94%);\n z-index: 6;\n border-left: 1px solid var(--m-line);\n box-shadow: -12px 0 40px rgba(0, 0, 0, 0.45);\n }\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/* ---------- composer AI ---------- */\n\n.mailharbor-toolbar button.ai {\n color: var(--m-accent);\n}\n\n.mailharbor-compose-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n flex-wrap: wrap;\n}\n\n.mailharbor-versions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n padding-top: 8px;\n}\n\n.mailharbor-versions .chip {\n height: 22px;\n padding: 0 8px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 11px;\n}\n\n.mailharbor-versions .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\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");
|
|
@@ -125,6 +125,21 @@ var api = {
|
|
|
125
125
|
check: (id) => request(`accounts/${encodeURIComponent(id)}/check`, { method: "POST" }),
|
|
126
126
|
aiStatus: (accountId) => request(`ai/status${query({ accountId: accountId ?? null })}`),
|
|
127
127
|
aiModels: () => request("ai/models"),
|
|
128
|
+
aiThread: (accountId, key) => request(`ai/thread${query({ accountId: accountId ?? null, key })}`),
|
|
129
|
+
aiAddCard: (payload) => request("ai/cards", {
|
|
130
|
+
method: "POST",
|
|
131
|
+
body: JSON.stringify(payload)
|
|
132
|
+
}),
|
|
133
|
+
aiDeleteCard: (accountId, key, cardId) => request(`ai/cards${query({ accountId: accountId ?? null, key, cardId })}`, {
|
|
134
|
+
method: "DELETE"
|
|
135
|
+
}),
|
|
136
|
+
aiClearThread: (accountId, key) => request(`ai/thread${query({ accountId: accountId ?? null, key })}`, {
|
|
137
|
+
method: "DELETE"
|
|
138
|
+
}),
|
|
139
|
+
aiCompose: (payload) => request("ai/compose", {
|
|
140
|
+
method: "POST",
|
|
141
|
+
body: JSON.stringify(payload)
|
|
142
|
+
}),
|
|
128
143
|
aiSummary: (payload) => request("ai/summary", {
|
|
129
144
|
method: "POST",
|
|
130
145
|
body: JSON.stringify(payload)
|
|
@@ -240,17 +255,451 @@ function MailIcon() {
|
|
|
240
255
|
}
|
|
241
256
|
|
|
242
257
|
// src/client/panel.tsx
|
|
243
|
-
var
|
|
258
|
+
var import_react8 = require("react");
|
|
244
259
|
|
|
245
|
-
// src/client/
|
|
260
|
+
// src/client/ai-rail.tsx
|
|
246
261
|
var import_react3 = require("react");
|
|
247
262
|
|
|
248
|
-
// src/client/
|
|
263
|
+
// src/client/icons.tsx
|
|
249
264
|
var import_react2 = require("react");
|
|
265
|
+
var PATHS = {
|
|
266
|
+
refresh: (0, import_react2.createElement)("path", { d: "M20 11a8 8 0 1 0-2.3 5.7M20 5v6h-6" }),
|
|
267
|
+
plus: (0, import_react2.createElement)("path", { d: "M12 5v14M5 12h14" }),
|
|
268
|
+
compose: (0, import_react2.createElement)("path", { d: "M4 20h4l10.5-10.5a2.1 2.1 0 0 0-3-3L5 17v3zM13.5 6.5l3 3" }),
|
|
269
|
+
archive: (0, import_react2.createElement)(
|
|
270
|
+
"g",
|
|
271
|
+
null,
|
|
272
|
+
(0, import_react2.createElement)("rect", { x: 3, y: 4, width: 18, height: 5, rx: 1.5 }),
|
|
273
|
+
(0, import_react2.createElement)("path", { d: "M5 9v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V9M10 13h4" })
|
|
274
|
+
),
|
|
275
|
+
paperclip: (0, import_react2.createElement)("path", {
|
|
276
|
+
d: "M20 11.5 12 19.5a5 5 0 0 1-7-7l8-8a3.3 3.3 0 0 1 4.7 4.7l-8 8a1.7 1.7 0 0 1-2.4-2.4l7.3-7.3"
|
|
277
|
+
}),
|
|
278
|
+
mail: (0, import_react2.createElement)(
|
|
279
|
+
"g",
|
|
280
|
+
null,
|
|
281
|
+
(0, import_react2.createElement)("rect", { x: 3, y: 5, width: 18, height: 14, rx: 2.5 }),
|
|
282
|
+
(0, import_react2.createElement)("path", { d: "m4 7 7.1 5a1.6 1.6 0 0 0 1.8 0L20 7" })
|
|
283
|
+
),
|
|
284
|
+
search: (0, import_react2.createElement)("path", { d: "M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14zM20 20l-4-4" }),
|
|
285
|
+
reply: (0, import_react2.createElement)("path", { d: "M9 7 4 12l5 5M4 12h9a6 6 0 0 1 6 6v1" }),
|
|
286
|
+
replyAll: (0, import_react2.createElement)("path", { d: "m9 7-5 5 5 5M14 7l-5 5 5 5M13 12h5a5 5 0 0 1 5 5" }),
|
|
287
|
+
forward: (0, import_react2.createElement)("path", { d: "m15 7 5 5-5 5M20 12h-9a6 6 0 0 0-6 6v1" }),
|
|
288
|
+
check: (0, import_react2.createElement)("path", { d: "m5 13 4 4L19 7" }),
|
|
289
|
+
image: (0, import_react2.createElement)(
|
|
290
|
+
"g",
|
|
291
|
+
null,
|
|
292
|
+
(0, import_react2.createElement)("rect", { x: 3, y: 4, width: 18, height: 16, rx: 2 }),
|
|
293
|
+
(0, import_react2.createElement)("circle", { cx: 8.5, cy: 9.5, r: 1.5 }),
|
|
294
|
+
(0, import_react2.createElement)("path", { d: "m4 18 5-5 4 4 3-3 4 4" })
|
|
295
|
+
),
|
|
296
|
+
close: (0, import_react2.createElement)("path", { d: "M6 6l12 12M18 6 6 18" }),
|
|
297
|
+
chevronLeft: (0, import_react2.createElement)("path", { d: "m14 6-6 6 6 6" }),
|
|
298
|
+
chevronRight: (0, import_react2.createElement)("path", { d: "m10 6 6 6-6 6" }),
|
|
299
|
+
sparkles: (0, import_react2.createElement)(
|
|
300
|
+
"g",
|
|
301
|
+
null,
|
|
302
|
+
(0, import_react2.createElement)("path", { d: "m12 3 1.8 4.6L18.5 9l-4.7 1.4L12 15l-1.8-4.6L5.5 9l4.7-1.4z" }),
|
|
303
|
+
(0, import_react2.createElement)("path", { d: "M18.5 15.5 19.3 18l2.2.8-2.2.7-.8 2.5-.8-2.5-2.2-.7 2.2-.8z" })
|
|
304
|
+
),
|
|
305
|
+
trash: (0, import_react2.createElement)(
|
|
306
|
+
"g",
|
|
307
|
+
null,
|
|
308
|
+
(0, import_react2.createElement)("path", {
|
|
309
|
+
d: "M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6.5 7l.8 12a1 1 0 0 0 1 .9h7.4a1 1 0 0 0 1-.9l.8-12"
|
|
310
|
+
}),
|
|
311
|
+
(0, import_react2.createElement)("path", { d: "M10 11v6M14 11v6" })
|
|
312
|
+
),
|
|
313
|
+
send: (0, import_react2.createElement)("path", { d: "m4 4 17 8-17 8 3.6-8z M7.6 12H21" })
|
|
314
|
+
};
|
|
315
|
+
function Icon({ name, size = 16 }) {
|
|
316
|
+
return (0, import_react2.createElement)(
|
|
317
|
+
"svg",
|
|
318
|
+
{
|
|
319
|
+
width: size,
|
|
320
|
+
height: size,
|
|
321
|
+
viewBox: "0 0 24 24",
|
|
322
|
+
fill: "none",
|
|
323
|
+
stroke: "currentColor",
|
|
324
|
+
strokeWidth: 1.7,
|
|
325
|
+
strokeLinecap: "round",
|
|
326
|
+
strokeLinejoin: "round",
|
|
327
|
+
"aria-hidden": "true",
|
|
328
|
+
focusable: "false"
|
|
329
|
+
},
|
|
330
|
+
PATHS[name] ?? null
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/client/ai-rail.tsx
|
|
335
|
+
function errorText(error) {
|
|
336
|
+
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
337
|
+
}
|
|
338
|
+
function textToHtml(text) {
|
|
339
|
+
return text.split(/\n{2,}/).map(
|
|
340
|
+
(paragraph) => `<p>${paragraph.trim().replaceAll("&", "&").replaceAll("<", "<").replaceAll("\n", "<br>")}</p>`
|
|
341
|
+
).join("");
|
|
342
|
+
}
|
|
343
|
+
function summaryBlocks(text) {
|
|
344
|
+
const blocks = [];
|
|
345
|
+
let current = { title: "", lines: [] };
|
|
346
|
+
const push = () => {
|
|
347
|
+
const body = current.lines.join("\n").trim();
|
|
348
|
+
if (current.title !== "" || body !== "") blocks.push({ title: current.title, body });
|
|
349
|
+
current = { title: "", lines: [] };
|
|
350
|
+
};
|
|
351
|
+
for (const line of text.split("\n")) {
|
|
352
|
+
const heading = /^#{1,4}\s+(.*)$/.exec(line) ?? /^(结论|需要你做什么|关键信息|建议回复要点)\s*$/.exec(line);
|
|
353
|
+
if (heading !== null) {
|
|
354
|
+
push();
|
|
355
|
+
current.title = heading[1] ?? "";
|
|
356
|
+
} else {
|
|
357
|
+
current.lines.push(line);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
push();
|
|
361
|
+
return blocks;
|
|
362
|
+
}
|
|
363
|
+
function suggestionsOf(summary) {
|
|
364
|
+
const match = /建议回复要点[::]?\s*\n([\s\S]*)$/.exec(summary);
|
|
365
|
+
if (match === null) return "\u6839\u636E\u8FD9\u5C01\u90AE\u4EF6\u5199\u4E00\u5C01\u5F97\u4F53\u7684\u56DE\u590D\u3002";
|
|
366
|
+
return `\u6839\u636E\u8FD9\u4E9B\u5EFA\u8BAE\u5199\u56DE\u590D\uFF1A
|
|
367
|
+
${(match[1] ?? "").trim()}`;
|
|
368
|
+
}
|
|
369
|
+
var KIND_LABEL = {
|
|
370
|
+
summary: "AI \u6458\u8981",
|
|
371
|
+
reply: "AI \u56DE\u590D",
|
|
372
|
+
translate: "AI \u7FFB\u8BD1",
|
|
373
|
+
answer: "AI \u56DE\u7B54"
|
|
374
|
+
};
|
|
375
|
+
function threadKey(detail, source) {
|
|
376
|
+
return detail.messageId !== void 0 && detail.messageId !== "" ? `mid:${detail.messageId}` : `${source}:${detail.id}`;
|
|
377
|
+
}
|
|
378
|
+
function AiRail({
|
|
379
|
+
account,
|
|
380
|
+
detail,
|
|
381
|
+
source,
|
|
382
|
+
mailbox,
|
|
383
|
+
request: request2,
|
|
384
|
+
onClose,
|
|
385
|
+
onInsert,
|
|
386
|
+
onSaveDraft,
|
|
387
|
+
onNotice
|
|
388
|
+
}) {
|
|
389
|
+
const [status, setStatus] = (0, import_react3.useState)(null);
|
|
390
|
+
const [thread, setThread] = (0, import_react3.useState)(null);
|
|
391
|
+
const [loading, setLoading] = (0, import_react3.useState)(false);
|
|
392
|
+
const [generating, setGenerating] = (0, import_react3.useState)(null);
|
|
393
|
+
const [error, setError] = (0, import_react3.useState)("");
|
|
394
|
+
const [question, setQuestion] = (0, import_react3.useState)("");
|
|
395
|
+
const [confirmClear, setConfirmClear] = (0, import_react3.useState)(false);
|
|
396
|
+
const list = (0, import_react3.useRef)(null);
|
|
397
|
+
const lastNonce = (0, import_react3.useRef)(0);
|
|
398
|
+
const key = detail === null ? "" : threadKey(detail, source);
|
|
399
|
+
(0, import_react3.useEffect)(() => {
|
|
400
|
+
if (account === null) return;
|
|
401
|
+
let cancelled = false;
|
|
402
|
+
api.aiStatus(account.id).then((next) => {
|
|
403
|
+
if (!cancelled) setStatus(next);
|
|
404
|
+
}).catch(() => {
|
|
405
|
+
if (!cancelled)
|
|
406
|
+
setStatus({ available: false, enabled: false, provider: "", model: "", error: "\u65E0\u6CD5\u8BFB\u53D6 AI \u72B6\u6001" });
|
|
407
|
+
});
|
|
408
|
+
return () => {
|
|
409
|
+
cancelled = true;
|
|
410
|
+
};
|
|
411
|
+
}, [account]);
|
|
412
|
+
(0, import_react3.useEffect)(() => {
|
|
413
|
+
if (account === null || key === "") {
|
|
414
|
+
setThread(null);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
let cancelled = false;
|
|
418
|
+
setLoading(true);
|
|
419
|
+
api.aiThread(account.id, key).then((result) => {
|
|
420
|
+
if (!cancelled) setThread(result.thread);
|
|
421
|
+
}).catch(() => {
|
|
422
|
+
if (!cancelled) setThread(null);
|
|
423
|
+
}).finally(() => {
|
|
424
|
+
if (!cancelled) setLoading(false);
|
|
425
|
+
});
|
|
426
|
+
return () => {
|
|
427
|
+
cancelled = true;
|
|
428
|
+
};
|
|
429
|
+
}, [account, key]);
|
|
430
|
+
const generate = (0, import_react3.useCallback)(
|
|
431
|
+
async (kind, extra = {}) => {
|
|
432
|
+
if (detail === null || account === null) return;
|
|
433
|
+
setGenerating(kind);
|
|
434
|
+
setError("");
|
|
435
|
+
try {
|
|
436
|
+
const result = await api.aiAddCard({
|
|
437
|
+
accountId: detail.accountId,
|
|
438
|
+
key,
|
|
439
|
+
kind,
|
|
440
|
+
source,
|
|
441
|
+
id: detail.id,
|
|
442
|
+
mailbox,
|
|
443
|
+
...extra
|
|
444
|
+
});
|
|
445
|
+
setThread(result.thread);
|
|
446
|
+
} catch (caught) {
|
|
447
|
+
setError(errorText(caught));
|
|
448
|
+
} finally {
|
|
449
|
+
setGenerating(null);
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
[detail, account, key, source, mailbox]
|
|
453
|
+
);
|
|
454
|
+
(0, import_react3.useEffect)(() => {
|
|
455
|
+
if (request2 === null || request2.nonce === lastNonce.current) return;
|
|
456
|
+
lastNonce.current = request2.nonce;
|
|
457
|
+
if (detail !== null) void generate(request2.kind);
|
|
458
|
+
}, [request2]);
|
|
459
|
+
(0, import_react3.useEffect)(() => {
|
|
460
|
+
const element = list.current;
|
|
461
|
+
if (element !== null) element.scrollTop = element.scrollHeight;
|
|
462
|
+
}, [thread, generating]);
|
|
463
|
+
const replyInit = (card) => {
|
|
464
|
+
const subject = card.meta?.subject ?? (detail === null ? "" : /^re:/i.test(detail.subject) ? detail.subject : `Re: ${detail.subject}`);
|
|
465
|
+
return {
|
|
466
|
+
to: card.meta?.to ?? detail?.from.address ?? "",
|
|
467
|
+
cc: "",
|
|
468
|
+
subject,
|
|
469
|
+
html: card.html ?? textToHtml(card.output),
|
|
470
|
+
...card.meta?.inReplyTo !== void 0 && card.meta.inReplyTo !== "" ? { inReplyTo: card.meta.inReplyTo } : {},
|
|
471
|
+
...card.meta?.references !== void 0 && card.meta.references !== "" ? { references: card.meta.references } : {}
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
const copy = (card) => {
|
|
475
|
+
void navigator.clipboard?.writeText(card.output).then(() => onNotice("\u5DF2\u590D\u5236"));
|
|
476
|
+
};
|
|
477
|
+
const remove = async (card) => {
|
|
478
|
+
if (account === null) return;
|
|
479
|
+
try {
|
|
480
|
+
const result = await api.aiDeleteCard(account.id, key, card.id);
|
|
481
|
+
setThread(result.thread);
|
|
482
|
+
} catch (caught) {
|
|
483
|
+
setError(errorText(caught));
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
const clear = async () => {
|
|
487
|
+
if (account === null || key === "") return;
|
|
488
|
+
try {
|
|
489
|
+
await api.aiClearThread(account.id, key);
|
|
490
|
+
setThread(null);
|
|
491
|
+
setConfirmClear(false);
|
|
492
|
+
onNotice("\u5DF2\u6E05\u9664\u8FD9\u5C01\u90AE\u4EF6\u7684 AI \u8BB0\u5F55");
|
|
493
|
+
} catch (caught) {
|
|
494
|
+
setError(errorText(caught));
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const ask = async () => {
|
|
498
|
+
const text = question.trim();
|
|
499
|
+
if (text === "" || generating !== null) return;
|
|
500
|
+
setQuestion("");
|
|
501
|
+
await generate("answer", { instruction: text });
|
|
502
|
+
};
|
|
503
|
+
const disabled = status !== null && (!status.available || !status.enabled);
|
|
504
|
+
const cards = thread?.cards ?? [];
|
|
505
|
+
return (0, import_react3.createElement)(
|
|
506
|
+
"aside",
|
|
507
|
+
{ className: "mailharbor-ai", "aria-label": "AI \u52A9\u624B" },
|
|
508
|
+
(0, import_react3.createElement)(
|
|
509
|
+
"div",
|
|
510
|
+
{ className: "mailharbor-ai-head" },
|
|
511
|
+
(0, import_react3.createElement)(Icon, { name: "sparkles", size: 15 }),
|
|
512
|
+
(0, import_react3.createElement)("strong", null, "AI \u52A9\u624B"),
|
|
513
|
+
status === null || status.model === "" ? null : (0, import_react3.createElement)("span", { className: "badge" }, status.model),
|
|
514
|
+
(0, import_react3.createElement)("span", { className: "spacer" }),
|
|
515
|
+
confirmClear ? (0, import_react3.createElement)(
|
|
516
|
+
"span",
|
|
517
|
+
{ className: "mailharbor-ai-confirm" },
|
|
518
|
+
(0, import_react3.createElement)("button", { className: "subtle", onClick: () => void clear() }, "\u786E\u8BA4\u6E05\u9664"),
|
|
519
|
+
(0, import_react3.createElement)("button", { className: "subtle", onClick: () => setConfirmClear(false) }, "\u53D6\u6D88")
|
|
520
|
+
) : (0, import_react3.createElement)(
|
|
521
|
+
"button",
|
|
522
|
+
{
|
|
523
|
+
className: "subtle icon",
|
|
524
|
+
title: cards.length === 0 ? "\u5173\u95ED" : "\u6E05\u9664\u8FD9\u5C01\u90AE\u4EF6\u7684\u8BB0\u5F55",
|
|
525
|
+
onClick: () => cards.length === 0 ? onClose() : setConfirmClear(true)
|
|
526
|
+
},
|
|
527
|
+
(0, import_react3.createElement)(Icon, { name: cards.length === 0 ? "close" : "trash", size: 14 })
|
|
528
|
+
),
|
|
529
|
+
(0, import_react3.createElement)(
|
|
530
|
+
"button",
|
|
531
|
+
{ className: "subtle icon", title: "\u6536\u8D77", onClick: onClose },
|
|
532
|
+
(0, import_react3.createElement)(Icon, { name: "chevronRight", size: 14 })
|
|
533
|
+
)
|
|
534
|
+
),
|
|
535
|
+
detail === null ? (0, import_react3.createElement)(
|
|
536
|
+
"div",
|
|
537
|
+
{ className: "mailharbor-ai-empty" },
|
|
538
|
+
(0, import_react3.createElement)(Icon, { name: "sparkles", size: 26 }),
|
|
539
|
+
(0, import_react3.createElement)("div", { className: "title" }, "\u5148\u9009\u4E00\u5C01\u90AE\u4EF6"),
|
|
540
|
+
(0, import_react3.createElement)("div", { className: "small" }, "\u9009\u4E2D\u90AE\u4EF6\u540E\uFF0C\u53EF\u5728\u8FD9\u91CC\u751F\u6210\u6458\u8981\u3001\u56DE\u590D\u548C\u7FFB\u8BD1\uFF0C\u8BB0\u5F55\u4F1A\u4E00\u76F4\u4FDD\u7559\u3002")
|
|
541
|
+
) : (0, import_react3.createElement)(
|
|
542
|
+
"div",
|
|
543
|
+
{ className: "mailharbor-ai-context" },
|
|
544
|
+
(0, import_react3.createElement)("span", { className: "small" }, "\u8DDF\u968F\u5F53\u524D\u90AE\u4EF6"),
|
|
545
|
+
(0, import_react3.createElement)("strong", null, detail.subject),
|
|
546
|
+
(0, import_react3.createElement)("span", { className: "small muted" }, "AI \u53EA\u770B\u5230\u8FD9\u5C01\u90AE\u4EF6")
|
|
547
|
+
),
|
|
548
|
+
disabled && detail !== null ? (0, import_react3.createElement)("div", { className: "mailharbor-ai-warn" }, status?.error ?? "AI \u5DF2\u5173\u95ED\uFF0C\u8BF7\u5230\u8BBE\u7F6E\u91CC\u5F00\u542F") : null,
|
|
549
|
+
detail === null ? null : (0, import_react3.createElement)(
|
|
550
|
+
"div",
|
|
551
|
+
{ className: "mailharbor-ai-quick" },
|
|
552
|
+
(0, import_react3.createElement)(
|
|
553
|
+
"button",
|
|
554
|
+
{
|
|
555
|
+
className: "ghost",
|
|
556
|
+
disabled: disabled || generating !== null,
|
|
557
|
+
onClick: () => void generate("summary")
|
|
558
|
+
},
|
|
559
|
+
generating === "summary" ? "\u603B\u7ED3\u4E2D\u2026" : "\u6458\u8981"
|
|
560
|
+
),
|
|
561
|
+
(0, import_react3.createElement)(
|
|
562
|
+
"button",
|
|
563
|
+
{
|
|
564
|
+
className: "ghost",
|
|
565
|
+
disabled: disabled || generating !== null,
|
|
566
|
+
onClick: () => void generate("reply")
|
|
567
|
+
},
|
|
568
|
+
generating === "reply" ? "\u8D77\u8349\u4E2D\u2026" : "\u56DE\u590D"
|
|
569
|
+
),
|
|
570
|
+
(0, import_react3.createElement)(
|
|
571
|
+
"button",
|
|
572
|
+
{
|
|
573
|
+
className: "ghost",
|
|
574
|
+
disabled: disabled || generating !== null,
|
|
575
|
+
onClick: () => void generate("translate", { target: "en" })
|
|
576
|
+
},
|
|
577
|
+
generating === "translate" ? "\u7FFB\u8BD1\u4E2D\u2026" : "\u7FFB\u8BD1"
|
|
578
|
+
)
|
|
579
|
+
),
|
|
580
|
+
(0, import_react3.createElement)(
|
|
581
|
+
"div",
|
|
582
|
+
{ className: "mailharbor-ai-list", ref: list },
|
|
583
|
+
loading ? (0, import_react3.createElement)("div", { className: "mailharbor-ai-empty small" }, "\u6B63\u5728\u8BFB\u53D6\u8BB0\u5F55\u2026") : null,
|
|
584
|
+
!loading && detail !== null && cards.length === 0 && generating === null ? (0, import_react3.createElement)(
|
|
585
|
+
"div",
|
|
586
|
+
{ className: "mailharbor-ai-empty" },
|
|
587
|
+
(0, import_react3.createElement)("div", { className: "title" }, "\u8FD9\u5C01\u90AE\u4EF6\u8FD8\u6CA1\u6709 AI \u8BB0\u5F55"),
|
|
588
|
+
(0, import_react3.createElement)(
|
|
589
|
+
"div",
|
|
590
|
+
{ className: "small" },
|
|
591
|
+
"\u70B9\u4E0A\u9762\u7684\u300C\u6458\u8981\u300D\u5F00\u59CB\uFF1B\u751F\u6210\u7684\u5185\u5BB9\u4F1A\u4FDD\u5B58\u5728\u8FD9\u91CC\uFF0C\u5173\u6389\u9762\u677F\u4E5F\u4E0D\u4F1A\u4E22\u3002"
|
|
592
|
+
)
|
|
593
|
+
) : null,
|
|
594
|
+
cards.map(
|
|
595
|
+
(card) => (0, import_react3.createElement)(
|
|
596
|
+
"article",
|
|
597
|
+
{ className: "mailharbor-ai-card", key: card.id },
|
|
598
|
+
(0, import_react3.createElement)(
|
|
599
|
+
"div",
|
|
600
|
+
{ className: "mailharbor-ai-card-head" },
|
|
601
|
+
(0, import_react3.createElement)("span", null, KIND_LABEL[card.kind] ?? "AI"),
|
|
602
|
+
(0, import_react3.createElement)("span", { className: "muted small" }, new Date(card.createdAt).toLocaleTimeString()),
|
|
603
|
+
(0, import_react3.createElement)("span", { className: "spacer" }),
|
|
604
|
+
(0, import_react3.createElement)(
|
|
605
|
+
"button",
|
|
606
|
+
{
|
|
607
|
+
className: "subtle icon",
|
|
608
|
+
title: "\u5220\u9664",
|
|
609
|
+
onClick: () => void remove(card)
|
|
610
|
+
},
|
|
611
|
+
(0, import_react3.createElement)(Icon, { name: "close", size: 12 })
|
|
612
|
+
)
|
|
613
|
+
),
|
|
614
|
+
card.kind === "summary" ? (0, import_react3.createElement)(
|
|
615
|
+
"div",
|
|
616
|
+
{ className: "mailharbor-ai-summary" },
|
|
617
|
+
summaryBlocks(card.output).map(
|
|
618
|
+
(block, index) => (0, import_react3.createElement)(
|
|
619
|
+
"section",
|
|
620
|
+
{ key: `${block.title}:${String(index)}` },
|
|
621
|
+
block.title === "" ? null : (0, import_react3.createElement)("h4", null, block.title),
|
|
622
|
+
(0, import_react3.createElement)("p", null, block.body)
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
) : (0, import_react3.createElement)("div", { className: "mailharbor-ai-text" }, card.output),
|
|
626
|
+
(0, import_react3.createElement)(
|
|
627
|
+
"div",
|
|
628
|
+
{ className: "mailharbor-ai-card-actions" },
|
|
629
|
+
card.kind === "summary" ? (0, import_react3.createElement)(
|
|
630
|
+
"button",
|
|
631
|
+
{
|
|
632
|
+
className: "ghost",
|
|
633
|
+
disabled: generating !== null,
|
|
634
|
+
onClick: () => void generate("reply", { instruction: suggestionsOf(card.output) })
|
|
635
|
+
},
|
|
636
|
+
"\u7528\u8FD9\u4E9B\u8981\u70B9\u56DE\u590D"
|
|
637
|
+
) : null,
|
|
638
|
+
card.kind === "reply" || card.kind === "translate" ? (0, import_react3.createElement)(
|
|
639
|
+
"button",
|
|
640
|
+
{ className: "ghost", onClick: () => onInsert(replyInit(card)) },
|
|
641
|
+
card.kind === "translate" ? "\u63D2\u5165\u56DE\u590D" : "\u63D2\u5165\u6B63\u6587"
|
|
642
|
+
) : null,
|
|
643
|
+
card.kind === "reply" ? (0, import_react3.createElement)("button", { className: "ghost", onClick: () => onSaveDraft(replyInit(card)) }, "\u5B58\u8349\u7A3F") : null,
|
|
644
|
+
card.kind === "answer" ? (0, import_react3.createElement)(
|
|
645
|
+
"button",
|
|
646
|
+
{
|
|
647
|
+
className: "ghost",
|
|
648
|
+
onClick: () => setQuestion(card.output.slice(0, 80))
|
|
649
|
+
},
|
|
650
|
+
"\u7EE7\u7EED\u8FFD\u95EE"
|
|
651
|
+
) : null,
|
|
652
|
+
(0, import_react3.createElement)("span", { className: "spacer" }),
|
|
653
|
+
(0, import_react3.createElement)("button", { className: "subtle", onClick: () => copy(card) }, "\u590D\u5236")
|
|
654
|
+
)
|
|
655
|
+
)
|
|
656
|
+
),
|
|
657
|
+
generating !== null ? (0, import_react3.createElement)(
|
|
658
|
+
"article",
|
|
659
|
+
{ className: "mailharbor-ai-card generating" },
|
|
660
|
+
(0, import_react3.createElement)("div", { className: "mailharbor-ai-card-head" }, (0, import_react3.createElement)("span", null, KIND_LABEL[generating])),
|
|
661
|
+
(0, import_react3.createElement)("div", { className: "mailharbor-ai-text muted" }, "\u6B63\u5728\u751F\u6210\u2026")
|
|
662
|
+
) : null,
|
|
663
|
+
error === "" ? null : (0, import_react3.createElement)("div", { className: "mailharbor-ai-error" }, error)
|
|
664
|
+
),
|
|
665
|
+
detail === null ? null : (0, import_react3.createElement)(
|
|
666
|
+
"form",
|
|
667
|
+
{
|
|
668
|
+
className: "mailharbor-ai-ask",
|
|
669
|
+
onSubmit: (event) => {
|
|
670
|
+
event.preventDefault();
|
|
671
|
+
void ask();
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
(0, import_react3.createElement)("input", {
|
|
675
|
+
value: question,
|
|
676
|
+
placeholder: "\u95EE\u8FD9\u5C01\u90AE\u4EF6\uFF0C\u4F8B\u5982\uFF1A\u8C01\u9700\u8981\u5728\u4EC0\u4E48\u65F6\u5019\u505A\u4EC0\u4E48\uFF1F",
|
|
677
|
+
"aria-label": "\u95EE AI",
|
|
678
|
+
disabled: disabled || generating !== null,
|
|
679
|
+
onChange: (event) => setQuestion(event.target.value)
|
|
680
|
+
}),
|
|
681
|
+
(0, import_react3.createElement)(
|
|
682
|
+
"button",
|
|
683
|
+
{
|
|
684
|
+
className: "primary icon",
|
|
685
|
+
type: "submit",
|
|
686
|
+
disabled: disabled || generating !== null || question.trim() === ""
|
|
687
|
+
},
|
|
688
|
+
(0, import_react3.createElement)(Icon, { name: "send", size: 14 })
|
|
689
|
+
)
|
|
690
|
+
)
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/client/account-form.tsx
|
|
695
|
+
var import_react5 = require("react");
|
|
696
|
+
|
|
697
|
+
// src/client/components.tsx
|
|
698
|
+
var import_react4 = require("react");
|
|
250
699
|
var import_react_dom = require("react-dom");
|
|
251
700
|
function Avatar({ email, size = 26 }) {
|
|
252
701
|
const letter = (email.trim()[0] ?? "?").toUpperCase();
|
|
253
|
-
return (0,
|
|
702
|
+
return (0, import_react4.createElement)(
|
|
254
703
|
"span",
|
|
255
704
|
{
|
|
256
705
|
className: "avatar",
|
|
@@ -269,12 +718,12 @@ function Field({
|
|
|
269
718
|
children,
|
|
270
719
|
hint
|
|
271
720
|
}) {
|
|
272
|
-
return (0,
|
|
721
|
+
return (0, import_react4.createElement)(
|
|
273
722
|
"div",
|
|
274
723
|
{ className: "mailharbor-field" },
|
|
275
|
-
(0,
|
|
724
|
+
(0, import_react4.createElement)("label", null, label),
|
|
276
725
|
children,
|
|
277
|
-
hint === void 0 ? null : (0,
|
|
726
|
+
hint === void 0 ? null : (0, import_react4.createElement)("span", { className: "small muted" }, hint)
|
|
278
727
|
);
|
|
279
728
|
}
|
|
280
729
|
function Toggle({
|
|
@@ -282,10 +731,10 @@ function Toggle({
|
|
|
282
731
|
onChange,
|
|
283
732
|
label
|
|
284
733
|
}) {
|
|
285
|
-
return (0,
|
|
734
|
+
return (0, import_react4.createElement)(
|
|
286
735
|
"label",
|
|
287
736
|
{ className: "mailharbor-toggle" },
|
|
288
|
-
(0,
|
|
737
|
+
(0, import_react4.createElement)("input", {
|
|
289
738
|
type: "checkbox",
|
|
290
739
|
checked,
|
|
291
740
|
onChange: (event) => {
|
|
@@ -311,7 +760,7 @@ function Modal({
|
|
|
311
760
|
onClose,
|
|
312
761
|
wide = false
|
|
313
762
|
}) {
|
|
314
|
-
(0,
|
|
763
|
+
(0, import_react4.useEffect)(() => {
|
|
315
764
|
const onKey = (event) => {
|
|
316
765
|
if (event.key === "Escape") onClose();
|
|
317
766
|
};
|
|
@@ -321,7 +770,7 @@ function Modal({
|
|
|
321
770
|
};
|
|
322
771
|
}, [onClose]);
|
|
323
772
|
return (0, import_react_dom.createPortal)(
|
|
324
|
-
(0,
|
|
773
|
+
(0, import_react4.createElement)(
|
|
325
774
|
"div",
|
|
326
775
|
{
|
|
327
776
|
className: "mailharbor-modal-mask",
|
|
@@ -329,7 +778,7 @@ function Modal({
|
|
|
329
778
|
if (event.target === event.currentTarget) onClose();
|
|
330
779
|
}
|
|
331
780
|
},
|
|
332
|
-
(0,
|
|
781
|
+
(0, import_react4.createElement)(
|
|
333
782
|
"div",
|
|
334
783
|
{
|
|
335
784
|
className: "mailharbor-modal",
|
|
@@ -338,27 +787,27 @@ function Modal({
|
|
|
338
787
|
"aria-modal": "true",
|
|
339
788
|
"aria-label": title
|
|
340
789
|
},
|
|
341
|
-
(0,
|
|
790
|
+
(0, import_react4.createElement)(
|
|
342
791
|
"header",
|
|
343
792
|
null,
|
|
344
793
|
title,
|
|
345
|
-
(0,
|
|
346
|
-
(0,
|
|
794
|
+
(0, import_react4.createElement)("span", { className: "spacer" }),
|
|
795
|
+
(0, import_react4.createElement)("button", { className: "subtle", onClick: onClose, "aria-label": "\u5173\u95ED" }, "\u2715")
|
|
347
796
|
),
|
|
348
|
-
(0,
|
|
349
|
-
footer === void 0 ? null : (0,
|
|
797
|
+
(0, import_react4.createElement)("div", { className: "content" }, children),
|
|
798
|
+
footer === void 0 ? null : (0, import_react4.createElement)("footer", null, footer)
|
|
350
799
|
)
|
|
351
800
|
),
|
|
352
801
|
portalRoot()
|
|
353
802
|
);
|
|
354
803
|
}
|
|
355
804
|
function ErrorBar({ message, onDismiss }) {
|
|
356
|
-
return (0,
|
|
805
|
+
return (0, import_react4.createElement)(
|
|
357
806
|
"div",
|
|
358
807
|
{ className: "error", role: "alert" },
|
|
359
|
-
(0,
|
|
360
|
-
(0,
|
|
361
|
-
(0,
|
|
808
|
+
(0, import_react4.createElement)("span", null, message),
|
|
809
|
+
(0, import_react4.createElement)("span", { className: "spacer" }),
|
|
810
|
+
(0, import_react4.createElement)("button", { className: "subtle", onClick: onDismiss }, "\u5173\u95ED")
|
|
362
811
|
);
|
|
363
812
|
}
|
|
364
813
|
function shortDate(iso) {
|
|
@@ -393,27 +842,27 @@ function formatBytes(size) {
|
|
|
393
842
|
function domainOf(email) {
|
|
394
843
|
return email.split("@")[1] ?? "example.com";
|
|
395
844
|
}
|
|
396
|
-
function
|
|
845
|
+
function errorText2(error) {
|
|
397
846
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
398
847
|
}
|
|
399
848
|
function AddAccountForm({
|
|
400
849
|
onDone,
|
|
401
850
|
onCancel
|
|
402
851
|
}) {
|
|
403
|
-
const [email, setEmail] = (0,
|
|
404
|
-
const [password, setPassword] = (0,
|
|
405
|
-
const [label, setLabel] = (0,
|
|
406
|
-
const [remember, setRemember] = (0,
|
|
407
|
-
const [advanced, setAdvanced] = (0,
|
|
408
|
-
const [imapHost, setImapHost] = (0,
|
|
409
|
-
const [imapPort, setImapPort] = (0,
|
|
410
|
-
const [imapSecure, setImapSecure] = (0,
|
|
411
|
-
const [smtpHost, setSmtpHost] = (0,
|
|
412
|
-
const [smtpPort, setSmtpPort] = (0,
|
|
413
|
-
const [smtpSecure, setSmtpSecure] = (0,
|
|
414
|
-
const [fromName, setFromName] = (0,
|
|
415
|
-
const [busy, setBusy] = (0,
|
|
416
|
-
const [error, setError] = (0,
|
|
852
|
+
const [email, setEmail] = (0, import_react5.useState)("");
|
|
853
|
+
const [password, setPassword] = (0, import_react5.useState)("");
|
|
854
|
+
const [label, setLabel] = (0, import_react5.useState)("");
|
|
855
|
+
const [remember, setRemember] = (0, import_react5.useState)(true);
|
|
856
|
+
const [advanced, setAdvanced] = (0, import_react5.useState)(false);
|
|
857
|
+
const [imapHost, setImapHost] = (0, import_react5.useState)("");
|
|
858
|
+
const [imapPort, setImapPort] = (0, import_react5.useState)("993");
|
|
859
|
+
const [imapSecure, setImapSecure] = (0, import_react5.useState)(true);
|
|
860
|
+
const [smtpHost, setSmtpHost] = (0, import_react5.useState)("");
|
|
861
|
+
const [smtpPort, setSmtpPort] = (0, import_react5.useState)("465");
|
|
862
|
+
const [smtpSecure, setSmtpSecure] = (0, import_react5.useState)(true);
|
|
863
|
+
const [fromName, setFromName] = (0, import_react5.useState)("");
|
|
864
|
+
const [busy, setBusy] = (0, import_react5.useState)(false);
|
|
865
|
+
const [error, setError] = (0, import_react5.useState)("");
|
|
417
866
|
const submit = async () => {
|
|
418
867
|
setBusy(true);
|
|
419
868
|
setError("");
|
|
@@ -440,22 +889,22 @@ function AddAccountForm({
|
|
|
440
889
|
});
|
|
441
890
|
onDone(state);
|
|
442
891
|
} catch (caught) {
|
|
443
|
-
setError(
|
|
892
|
+
setError(errorText2(caught));
|
|
444
893
|
} finally {
|
|
445
894
|
setBusy(false);
|
|
446
895
|
}
|
|
447
896
|
};
|
|
448
|
-
return (0,
|
|
897
|
+
return (0, import_react5.createElement)(
|
|
449
898
|
"div",
|
|
450
899
|
{ className: "mailharbor-card" },
|
|
451
|
-
(0,
|
|
452
|
-
(0,
|
|
900
|
+
(0, import_react5.createElement)("h3", null, "\u6DFB\u52A0\u90AE\u7BB1\u8D26\u53F7"),
|
|
901
|
+
(0, import_react5.createElement)(
|
|
453
902
|
"div",
|
|
454
903
|
{ className: "mailharbor-grid" },
|
|
455
|
-
(0,
|
|
904
|
+
(0, import_react5.createElement)(
|
|
456
905
|
Field,
|
|
457
906
|
{ label: "\u90AE\u7BB1\u5730\u5740" },
|
|
458
|
-
(0,
|
|
907
|
+
(0, import_react5.createElement)("input", {
|
|
459
908
|
type: "email",
|
|
460
909
|
value: email,
|
|
461
910
|
placeholder: "you@example.com",
|
|
@@ -463,95 +912,95 @@ function AddAccountForm({
|
|
|
463
912
|
autoFocus: true
|
|
464
913
|
})
|
|
465
914
|
),
|
|
466
|
-
(0,
|
|
915
|
+
(0, import_react5.createElement)(
|
|
467
916
|
Field,
|
|
468
917
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801" },
|
|
469
|
-
(0,
|
|
918
|
+
(0, import_react5.createElement)("input", {
|
|
470
919
|
type: "password",
|
|
471
920
|
value: password,
|
|
472
921
|
onChange: (event) => setPassword(event.target.value)
|
|
473
922
|
})
|
|
474
923
|
)
|
|
475
924
|
),
|
|
476
|
-
(0,
|
|
925
|
+
(0, import_react5.createElement)(
|
|
477
926
|
Field,
|
|
478
927
|
{ label: "\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09" },
|
|
479
|
-
(0,
|
|
928
|
+
(0, import_react5.createElement)("input", {
|
|
480
929
|
value: label,
|
|
481
930
|
placeholder: "\u5DE5\u4F5C\u90AE\u7BB1",
|
|
482
931
|
onChange: (event) => setLabel(event.target.value)
|
|
483
932
|
})
|
|
484
933
|
),
|
|
485
|
-
(0,
|
|
486
|
-
(0,
|
|
934
|
+
(0, import_react5.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801\uFF08\u5199\u5165\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF09" }),
|
|
935
|
+
(0, import_react5.createElement)(
|
|
487
936
|
"button",
|
|
488
937
|
{ className: "ghost", onClick: () => setAdvanced(!advanced) },
|
|
489
938
|
advanced ? "\u9690\u85CF\u9AD8\u7EA7\u8BBE\u7F6E" : "\u9AD8\u7EA7\u8BBE\u7F6E\uFF08\u670D\u52A1\u5668\u4E0E\u7AEF\u53E3\uFF09"
|
|
490
939
|
),
|
|
491
|
-
advanced ? (0,
|
|
940
|
+
advanced ? (0, import_react5.createElement)(
|
|
492
941
|
"div",
|
|
493
942
|
{ style: { display: "flex", flexDirection: "column", gap: "10px" } },
|
|
494
|
-
(0,
|
|
943
|
+
(0, import_react5.createElement)(
|
|
495
944
|
"div",
|
|
496
945
|
{ className: "mailharbor-grid three" },
|
|
497
|
-
(0,
|
|
946
|
+
(0, import_react5.createElement)(
|
|
498
947
|
Field,
|
|
499
948
|
{ label: "IMAP \u4E3B\u673A" },
|
|
500
|
-
(0,
|
|
949
|
+
(0, import_react5.createElement)("input", {
|
|
501
950
|
value: imapHost,
|
|
502
951
|
placeholder: `imap.${domainOf(email)}`,
|
|
503
952
|
onChange: (event) => setImapHost(event.target.value)
|
|
504
953
|
})
|
|
505
954
|
),
|
|
506
|
-
(0,
|
|
955
|
+
(0, import_react5.createElement)(
|
|
507
956
|
Field,
|
|
508
957
|
{ label: "\u7AEF\u53E3" },
|
|
509
|
-
(0,
|
|
958
|
+
(0, import_react5.createElement)("input", {
|
|
510
959
|
value: imapPort,
|
|
511
960
|
inputMode: "numeric",
|
|
512
961
|
onChange: (event) => setImapPort(event.target.value)
|
|
513
962
|
})
|
|
514
963
|
),
|
|
515
|
-
(0,
|
|
964
|
+
(0, import_react5.createElement)(Toggle, { checked: imapSecure, onChange: setImapSecure, label: "TLS" })
|
|
516
965
|
),
|
|
517
|
-
(0,
|
|
966
|
+
(0, import_react5.createElement)(
|
|
518
967
|
"div",
|
|
519
968
|
{ className: "mailharbor-grid three" },
|
|
520
|
-
(0,
|
|
969
|
+
(0, import_react5.createElement)(
|
|
521
970
|
Field,
|
|
522
971
|
{ label: "SMTP \u4E3B\u673A" },
|
|
523
|
-
(0,
|
|
972
|
+
(0, import_react5.createElement)("input", {
|
|
524
973
|
value: smtpHost,
|
|
525
974
|
placeholder: `smtp.${domainOf(email)}`,
|
|
526
975
|
onChange: (event) => setSmtpHost(event.target.value)
|
|
527
976
|
})
|
|
528
977
|
),
|
|
529
|
-
(0,
|
|
978
|
+
(0, import_react5.createElement)(
|
|
530
979
|
Field,
|
|
531
980
|
{ label: "\u7AEF\u53E3" },
|
|
532
|
-
(0,
|
|
981
|
+
(0, import_react5.createElement)("input", {
|
|
533
982
|
value: smtpPort,
|
|
534
983
|
inputMode: "numeric",
|
|
535
984
|
onChange: (event) => setSmtpPort(event.target.value)
|
|
536
985
|
})
|
|
537
986
|
),
|
|
538
|
-
(0,
|
|
987
|
+
(0, import_react5.createElement)(Toggle, { checked: smtpSecure, onChange: setSmtpSecure, label: "TLS" })
|
|
539
988
|
),
|
|
540
|
-
(0,
|
|
989
|
+
(0, import_react5.createElement)(
|
|
541
990
|
Field,
|
|
542
991
|
{ label: "\u53D1\u4EF6\u4EBA\u540D\u79F0\uFF08\u53EF\u9009\uFF09" },
|
|
543
|
-
(0,
|
|
992
|
+
(0, import_react5.createElement)("input", {
|
|
544
993
|
value: fromName,
|
|
545
994
|
onChange: (event) => setFromName(event.target.value)
|
|
546
995
|
})
|
|
547
996
|
)
|
|
548
997
|
) : null,
|
|
549
|
-
error === "" ? null : (0,
|
|
550
|
-
(0,
|
|
998
|
+
error === "" ? null : (0, import_react5.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
999
|
+
(0, import_react5.createElement)(
|
|
551
1000
|
"div",
|
|
552
1001
|
{ style: { display: "flex", gap: "8px", justifyContent: "flex-end" } },
|
|
553
|
-
onCancel === void 0 ? null : (0,
|
|
554
|
-
(0,
|
|
1002
|
+
onCancel === void 0 ? null : (0, import_react5.createElement)("button", { className: "ghost", onClick: onCancel }, "\u53D6\u6D88"),
|
|
1003
|
+
(0, import_react5.createElement)(
|
|
555
1004
|
"button",
|
|
556
1005
|
{
|
|
557
1006
|
className: "primary",
|
|
@@ -564,10 +1013,10 @@ function AddAccountForm({
|
|
|
564
1013
|
);
|
|
565
1014
|
}
|
|
566
1015
|
function LoginForm({ account, onDone }) {
|
|
567
|
-
const [password, setPassword] = (0,
|
|
568
|
-
const [remember, setRemember] = (0,
|
|
569
|
-
const [busy, setBusy] = (0,
|
|
570
|
-
const [error, setError] = (0,
|
|
1016
|
+
const [password, setPassword] = (0, import_react5.useState)("");
|
|
1017
|
+
const [remember, setRemember] = (0, import_react5.useState)(true);
|
|
1018
|
+
const [busy, setBusy] = (0, import_react5.useState)(false);
|
|
1019
|
+
const [error, setError] = (0, import_react5.useState)("");
|
|
571
1020
|
const submit = async () => {
|
|
572
1021
|
setBusy(true);
|
|
573
1022
|
setError("");
|
|
@@ -575,24 +1024,24 @@ function LoginForm({ account, onDone }) {
|
|
|
575
1024
|
await api.login(account.id, password, remember);
|
|
576
1025
|
onDone();
|
|
577
1026
|
} catch (caught) {
|
|
578
|
-
setError(
|
|
1027
|
+
setError(errorText2(caught));
|
|
579
1028
|
} finally {
|
|
580
1029
|
setBusy(false);
|
|
581
1030
|
}
|
|
582
1031
|
};
|
|
583
|
-
return (0,
|
|
1032
|
+
return (0, import_react5.createElement)(
|
|
584
1033
|
"div",
|
|
585
1034
|
{ className: "mailharbor-card" },
|
|
586
|
-
(0,
|
|
587
|
-
(0,
|
|
1035
|
+
(0, import_react5.createElement)("h3", null, `\u767B\u5F55 ${account.email}`),
|
|
1036
|
+
(0, import_react5.createElement)(
|
|
588
1037
|
"p",
|
|
589
1038
|
{ className: "small muted" },
|
|
590
1039
|
`${account.imap.host}:${String(account.imap.port)} \xB7 ${account.smtp.host}:${String(account.smtp.port)}`
|
|
591
1040
|
),
|
|
592
|
-
(0,
|
|
1041
|
+
(0, import_react5.createElement)(
|
|
593
1042
|
Field,
|
|
594
1043
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801" },
|
|
595
|
-
(0,
|
|
1044
|
+
(0, import_react5.createElement)("input", {
|
|
596
1045
|
type: "password",
|
|
597
1046
|
value: password,
|
|
598
1047
|
onChange: (event) => setPassword(event.target.value),
|
|
@@ -602,9 +1051,9 @@ function LoginForm({ account, onDone }) {
|
|
|
602
1051
|
autoFocus: true
|
|
603
1052
|
})
|
|
604
1053
|
),
|
|
605
|
-
(0,
|
|
606
|
-
error === "" ? null : (0,
|
|
607
|
-
(0,
|
|
1054
|
+
(0, import_react5.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801\uFF08\u5199\u5165\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF09" }),
|
|
1055
|
+
error === "" ? null : (0, import_react5.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
1056
|
+
(0, import_react5.createElement)(
|
|
608
1057
|
"button",
|
|
609
1058
|
{ className: "primary", onClick: () => void submit(), disabled: busy || password === "" },
|
|
610
1059
|
busy ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
|
|
@@ -615,10 +1064,10 @@ function AddAccountModal({
|
|
|
615
1064
|
onClose,
|
|
616
1065
|
onDone
|
|
617
1066
|
}) {
|
|
618
|
-
return (0,
|
|
1067
|
+
return (0, import_react5.createElement)(
|
|
619
1068
|
Modal,
|
|
620
1069
|
{ title: "\u6DFB\u52A0\u90AE\u7BB1\u8D26\u53F7", onClose },
|
|
621
|
-
(0,
|
|
1070
|
+
(0, import_react5.createElement)(AddAccountForm, {
|
|
622
1071
|
onDone: (state) => {
|
|
623
1072
|
onDone(state);
|
|
624
1073
|
onClose();
|
|
@@ -629,8 +1078,8 @@ function AddAccountModal({
|
|
|
629
1078
|
}
|
|
630
1079
|
|
|
631
1080
|
// src/client/composer.tsx
|
|
632
|
-
var
|
|
633
|
-
function
|
|
1081
|
+
var import_react6 = require("react");
|
|
1082
|
+
function errorText3(error) {
|
|
634
1083
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
635
1084
|
}
|
|
636
1085
|
function readFileBase64(file) {
|
|
@@ -650,27 +1099,115 @@ function Composer({
|
|
|
650
1099
|
onClose,
|
|
651
1100
|
onSent
|
|
652
1101
|
}) {
|
|
653
|
-
const [to, setTo] = (0,
|
|
654
|
-
const [cc, setCc] = (0,
|
|
655
|
-
const [bcc, setBcc] = (0,
|
|
656
|
-
const [subject, setSubject] = (0,
|
|
657
|
-
const [showCc, setShowCc] = (0,
|
|
658
|
-
const [attachments, setAttachments] = (0,
|
|
659
|
-
const [busy, setBusy] = (0,
|
|
660
|
-
const [error, setError] = (0,
|
|
661
|
-
const [confirming, setConfirming] = (0,
|
|
662
|
-
const [draftId, setDraftId] = (0,
|
|
663
|
-
const [
|
|
664
|
-
const [
|
|
665
|
-
const
|
|
666
|
-
(0,
|
|
1102
|
+
const [to, setTo] = (0, import_react6.useState)(initial.to);
|
|
1103
|
+
const [cc, setCc] = (0, import_react6.useState)(initial.cc);
|
|
1104
|
+
const [bcc, setBcc] = (0, import_react6.useState)(initial.bcc);
|
|
1105
|
+
const [subject, setSubject] = (0, import_react6.useState)(initial.subject);
|
|
1106
|
+
const [showCc, setShowCc] = (0, import_react6.useState)(initial.cc !== "" || initial.bcc !== "");
|
|
1107
|
+
const [attachments, setAttachments] = (0, import_react6.useState)(initial.attachments ?? []);
|
|
1108
|
+
const [busy, setBusy] = (0, import_react6.useState)(false);
|
|
1109
|
+
const [error, setError] = (0, import_react6.useState)("");
|
|
1110
|
+
const [confirming, setConfirming] = (0, import_react6.useState)(false);
|
|
1111
|
+
const [draftId, setDraftId] = (0, import_react6.useState)(initial.draftId);
|
|
1112
|
+
const [savedAt, setSavedAt] = (0, import_react6.useState)(null);
|
|
1113
|
+
const [dirty, setDirty] = (0, import_react6.useState)(0);
|
|
1114
|
+
const [translating, setTranslating] = (0, import_react6.useState)(null);
|
|
1115
|
+
const [pendingLang, setPendingLang] = (0, import_react6.useState)(null);
|
|
1116
|
+
const [versions, setVersions] = (0, import_react6.useState)([{ html: initial.html, label: "\u539F\u59CB" }]);
|
|
1117
|
+
const [versionIndex, setVersionIndex] = (0, import_react6.useState)(0);
|
|
1118
|
+
const [aiBusy, setAiBusy] = (0, import_react6.useState)(null);
|
|
1119
|
+
const [aiDraftOpen, setAiDraftOpen] = (0, import_react6.useState)(false);
|
|
1120
|
+
const [aiInstruction, setAiInstruction] = (0, import_react6.useState)("");
|
|
1121
|
+
const [hasBody, setHasBody] = (0, import_react6.useState)(initial.html.trim() !== "");
|
|
1122
|
+
const editor = (0, import_react6.useRef)(null);
|
|
1123
|
+
const closing = (0, import_react6.useRef)(false);
|
|
1124
|
+
(0, import_react6.useEffect)(() => {
|
|
667
1125
|
if (editor.current !== null) editor.current.innerHTML = initial.html;
|
|
1126
|
+
setHasBody((editor.current?.innerHTML ?? "").trim() !== "");
|
|
668
1127
|
}, []);
|
|
669
1128
|
const html = () => editor.current?.innerHTML ?? "";
|
|
670
1129
|
const text = () => editor.current?.innerText ?? "";
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
1130
|
+
const markDirty = () => {
|
|
1131
|
+
setDirty((value) => value + 1);
|
|
1132
|
+
setHasBody((editor.current?.innerHTML ?? "").trim() !== "");
|
|
1133
|
+
};
|
|
1134
|
+
const saveNow = async () => {
|
|
1135
|
+
if (to.trim() === "" && subject.trim() === "" && html().trim() === "") return false;
|
|
1136
|
+
try {
|
|
1137
|
+
const { draft } = await api.saveDraft(account.id, {
|
|
1138
|
+
...draftId === void 0 ? {} : { id: draftId },
|
|
1139
|
+
to,
|
|
1140
|
+
cc,
|
|
1141
|
+
bcc,
|
|
1142
|
+
subject,
|
|
1143
|
+
html: html(),
|
|
1144
|
+
attachments,
|
|
1145
|
+
...initial.inReplyTo === void 0 ? {} : { inReplyTo: initial.inReplyTo },
|
|
1146
|
+
...initial.references === void 0 ? {} : { references: initial.references }
|
|
1147
|
+
});
|
|
1148
|
+
setDraftId(draft.id);
|
|
1149
|
+
setSavedAt(/* @__PURE__ */ new Date());
|
|
1150
|
+
setDirty(0);
|
|
1151
|
+
return true;
|
|
1152
|
+
} catch {
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
(0, import_react6.useEffect)(() => {
|
|
1157
|
+
if (dirty === 0) return;
|
|
1158
|
+
const timer = window.setTimeout(() => {
|
|
1159
|
+
void saveNow();
|
|
1160
|
+
}, 2e3);
|
|
1161
|
+
return () => {
|
|
1162
|
+
window.clearTimeout(timer);
|
|
1163
|
+
};
|
|
1164
|
+
}, [dirty, to, cc, bcc, subject, attachments]);
|
|
1165
|
+
const close = () => {
|
|
1166
|
+
if (closing.current) return;
|
|
1167
|
+
closing.current = true;
|
|
1168
|
+
void saveNow().finally(() => {
|
|
1169
|
+
setDirty(0);
|
|
1170
|
+
closing.current = false;
|
|
1171
|
+
onClose();
|
|
1172
|
+
});
|
|
1173
|
+
};
|
|
1174
|
+
const pushVersion = (nextHtml, label) => {
|
|
1175
|
+
const base = versions.slice(0, versionIndex + 1);
|
|
1176
|
+
if (base[base.length - 1]?.html !== html()) base.push({ html: html(), label: "\u624B\u52A8" });
|
|
1177
|
+
base.push({ html: nextHtml, label });
|
|
1178
|
+
setVersions(base.slice(-10));
|
|
1179
|
+
setVersionIndex(Math.min(base.length - 1, 9));
|
|
1180
|
+
if (editor.current !== null) editor.current.innerHTML = nextHtml;
|
|
1181
|
+
setHasBody(nextHtml.trim() !== "");
|
|
1182
|
+
markDirty();
|
|
1183
|
+
};
|
|
1184
|
+
const restoreVersion = (index) => {
|
|
1185
|
+
const version = versions[index];
|
|
1186
|
+
if (version === void 0 || editor.current === null) return;
|
|
1187
|
+
editor.current.innerHTML = version.html;
|
|
1188
|
+
setVersionIndex(index);
|
|
1189
|
+
setHasBody(version.html.trim() !== "");
|
|
1190
|
+
markDirty();
|
|
1191
|
+
};
|
|
1192
|
+
const runAi = async (mode) => {
|
|
1193
|
+
setAiBusy(mode);
|
|
1194
|
+
setError("");
|
|
1195
|
+
try {
|
|
1196
|
+
const result = await api.aiCompose({
|
|
1197
|
+
accountId: account.id,
|
|
1198
|
+
mode,
|
|
1199
|
+
instruction: aiInstruction,
|
|
1200
|
+
html: html(),
|
|
1201
|
+
language: "zh"
|
|
1202
|
+
});
|
|
1203
|
+
pushVersion(result.html, mode === "draft" ? "AI \u8D77\u8349" : mode === "polish" ? "AI \u6DA6\u8272" : "AI \u7F29\u77ED");
|
|
1204
|
+
setAiDraftOpen(false);
|
|
1205
|
+
setAiInstruction("");
|
|
1206
|
+
} catch (caught) {
|
|
1207
|
+
setError(errorText3(caught));
|
|
1208
|
+
} finally {
|
|
1209
|
+
setAiBusy(null);
|
|
1210
|
+
}
|
|
674
1211
|
};
|
|
675
1212
|
const translate = async (target) => {
|
|
676
1213
|
if (editor.current === null) return;
|
|
@@ -683,13 +1220,18 @@ function Composer({
|
|
|
683
1220
|
setError("");
|
|
684
1221
|
try {
|
|
685
1222
|
const result = await api.aiTranslate({ accountId: account.id, html: html(), target });
|
|
686
|
-
|
|
1223
|
+
pushVersion(result.html, target === "en" ? "\u7FFB\u8BD1 EN" : "\u7FFB\u8BD1 \u4E2D");
|
|
687
1224
|
} catch (caught) {
|
|
688
|
-
setError(
|
|
1225
|
+
setError(errorText3(caught));
|
|
689
1226
|
} finally {
|
|
690
1227
|
setTranslating(null);
|
|
691
1228
|
}
|
|
692
1229
|
};
|
|
1230
|
+
const exec = (command, value) => {
|
|
1231
|
+
editor.current?.focus();
|
|
1232
|
+
document.execCommand(command, false, value);
|
|
1233
|
+
markDirty();
|
|
1234
|
+
};
|
|
693
1235
|
const addFiles = async (files) => {
|
|
694
1236
|
if (files === null || files.length === 0) return;
|
|
695
1237
|
setError("");
|
|
@@ -705,29 +1247,15 @@ function Composer({
|
|
|
705
1247
|
total += file.size;
|
|
706
1248
|
}
|
|
707
1249
|
setAttachments(next);
|
|
1250
|
+
markDirty();
|
|
708
1251
|
};
|
|
709
1252
|
const saveDraft = async () => {
|
|
710
1253
|
setBusy(true);
|
|
711
1254
|
setError("");
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
cc,
|
|
717
|
-
bcc,
|
|
718
|
-
subject,
|
|
719
|
-
html: html(),
|
|
720
|
-
attachments,
|
|
721
|
-
...initial.inReplyTo === void 0 ? {} : { inReplyTo: initial.inReplyTo },
|
|
722
|
-
...initial.references === void 0 ? {} : { references: initial.references }
|
|
723
|
-
});
|
|
724
|
-
setDraftId(draft.id);
|
|
725
|
-
onSent("\u8349\u7A3F\u5DF2\u4FDD\u5B58");
|
|
726
|
-
} catch (caught) {
|
|
727
|
-
setError(errorText2(caught));
|
|
728
|
-
} finally {
|
|
729
|
-
setBusy(false);
|
|
730
|
-
}
|
|
1255
|
+
const ok = await saveNow();
|
|
1256
|
+
setBusy(false);
|
|
1257
|
+
if (ok) onSent("\u8349\u7A3F\u5DF2\u4FDD\u5B58");
|
|
1258
|
+
else setError("\u8349\u7A3F\u4FDD\u5B58\u5931\u8D25");
|
|
731
1259
|
};
|
|
732
1260
|
const send = async () => {
|
|
733
1261
|
setBusy(true);
|
|
@@ -753,35 +1281,36 @@ function Composer({
|
|
|
753
1281
|
onSent("\u90AE\u4EF6\u5DF2\u53D1\u9001");
|
|
754
1282
|
onClose();
|
|
755
1283
|
} catch (caught) {
|
|
756
|
-
setError(
|
|
1284
|
+
setError(errorText3(caught));
|
|
757
1285
|
} finally {
|
|
758
1286
|
setBusy(false);
|
|
759
1287
|
setConfirming(false);
|
|
760
1288
|
}
|
|
761
1289
|
};
|
|
762
|
-
return (0,
|
|
1290
|
+
return (0, import_react6.createElement)(
|
|
763
1291
|
Modal,
|
|
764
1292
|
{
|
|
765
1293
|
title: initial.mode === "reply" ? "\u56DE\u590D" : initial.mode === "replyAll" ? "\u5168\u90E8\u56DE\u590D" : initial.mode === "forward" ? "\u8F6C\u53D1" : "\u5199\u90AE\u4EF6",
|
|
766
|
-
onClose,
|
|
1294
|
+
onClose: close,
|
|
767
1295
|
wide: true,
|
|
768
|
-
footer: (0,
|
|
1296
|
+
footer: (0, import_react6.createElement)(
|
|
769
1297
|
"div",
|
|
770
|
-
{
|
|
771
|
-
(0,
|
|
772
|
-
(0,
|
|
773
|
-
(0,
|
|
774
|
-
|
|
1298
|
+
{ className: "mailharbor-compose-foot" },
|
|
1299
|
+
(0, import_react6.createElement)("span", { className: "small muted" }, `\u53D1\u4EF6\u4EBA ${account.email}`),
|
|
1300
|
+
savedAt === null ? null : (0, import_react6.createElement)("span", { className: "small muted" }, `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${savedAt.toLocaleTimeString()}`),
|
|
1301
|
+
(0, import_react6.createElement)("span", { className: "spacer" }),
|
|
1302
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: () => void saveDraft(), disabled: busy }, "\u5B58\u8349\u7A3F"),
|
|
1303
|
+
confirming ? (0, import_react6.createElement)(
|
|
775
1304
|
"span",
|
|
776
1305
|
{ style: { display: "inline-flex", gap: "6px", alignItems: "center" } },
|
|
777
|
-
(0,
|
|
778
|
-
(0,
|
|
1306
|
+
(0, import_react6.createElement)("span", { className: "small" }, "\u786E\u5B9A\u53D1\u9001\uFF1F"),
|
|
1307
|
+
(0, import_react6.createElement)(
|
|
779
1308
|
"button",
|
|
780
1309
|
{ className: "primary", onClick: () => void send(), disabled: busy },
|
|
781
1310
|
busy ? "\u53D1\u9001\u4E2D\u2026" : "\u786E\u5B9A"
|
|
782
1311
|
),
|
|
783
|
-
(0,
|
|
784
|
-
) : (0,
|
|
1312
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: () => setConfirming(false) }, "\u53D6\u6D88")
|
|
1313
|
+
) : (0, import_react6.createElement)(
|
|
785
1314
|
"button",
|
|
786
1315
|
{
|
|
787
1316
|
className: "primary",
|
|
@@ -792,62 +1321,75 @@ function Composer({
|
|
|
792
1321
|
)
|
|
793
1322
|
)
|
|
794
1323
|
},
|
|
795
|
-
error === "" ? null : (0,
|
|
796
|
-
(0,
|
|
1324
|
+
error === "" ? null : (0, import_react6.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
1325
|
+
(0, import_react6.createElement)(
|
|
797
1326
|
"div",
|
|
798
1327
|
{ className: "mailharbor-grid" },
|
|
799
|
-
(0,
|
|
1328
|
+
(0, import_react6.createElement)(
|
|
800
1329
|
Field,
|
|
801
1330
|
{ label: "\u6536\u4EF6\u4EBA" },
|
|
802
|
-
(0,
|
|
1331
|
+
(0, import_react6.createElement)("input", {
|
|
803
1332
|
value: to,
|
|
804
|
-
onChange: (event) =>
|
|
1333
|
+
onChange: (event) => {
|
|
1334
|
+
setTo(event.target.value);
|
|
1335
|
+
markDirty();
|
|
1336
|
+
},
|
|
805
1337
|
placeholder: "a@example.com, b@example.com"
|
|
806
1338
|
})
|
|
807
1339
|
),
|
|
808
|
-
(0,
|
|
1340
|
+
(0, import_react6.createElement)(
|
|
809
1341
|
Field,
|
|
810
1342
|
{ label: "\u4E3B\u9898" },
|
|
811
|
-
(0,
|
|
1343
|
+
(0, import_react6.createElement)("input", {
|
|
812
1344
|
value: subject,
|
|
813
|
-
onChange: (event) =>
|
|
1345
|
+
onChange: (event) => {
|
|
1346
|
+
setSubject(event.target.value);
|
|
1347
|
+
markDirty();
|
|
1348
|
+
},
|
|
1349
|
+
placeholder: "\u90AE\u4EF6\u4E3B\u9898"
|
|
814
1350
|
})
|
|
815
1351
|
)
|
|
816
1352
|
),
|
|
817
|
-
showCc ? (0,
|
|
1353
|
+
showCc ? (0, import_react6.createElement)(
|
|
818
1354
|
"div",
|
|
819
1355
|
{ className: "mailharbor-grid" },
|
|
820
|
-
(0,
|
|
1356
|
+
(0, import_react6.createElement)(
|
|
821
1357
|
Field,
|
|
822
1358
|
{ label: "\u6284\u9001" },
|
|
823
|
-
(0,
|
|
1359
|
+
(0, import_react6.createElement)("input", {
|
|
824
1360
|
value: cc,
|
|
825
|
-
onChange: (event) =>
|
|
1361
|
+
onChange: (event) => {
|
|
1362
|
+
setCc(event.target.value);
|
|
1363
|
+
markDirty();
|
|
1364
|
+
}
|
|
826
1365
|
})
|
|
827
1366
|
),
|
|
828
|
-
(0,
|
|
1367
|
+
(0, import_react6.createElement)(
|
|
829
1368
|
Field,
|
|
830
1369
|
{ label: "\u5BC6\u9001" },
|
|
831
|
-
(0,
|
|
1370
|
+
(0, import_react6.createElement)("input", {
|
|
832
1371
|
value: bcc,
|
|
833
|
-
onChange: (event) =>
|
|
1372
|
+
onChange: (event) => {
|
|
1373
|
+
setBcc(event.target.value);
|
|
1374
|
+
markDirty();
|
|
1375
|
+
}
|
|
834
1376
|
})
|
|
835
1377
|
)
|
|
836
|
-
) : (0,
|
|
1378
|
+
) : (0, import_react6.createElement)(
|
|
837
1379
|
"button",
|
|
838
1380
|
{ className: "subtle small", style: { alignSelf: "flex-start" }, onClick: () => setShowCc(true) },
|
|
839
1381
|
"\u6DFB\u52A0\u6284\u9001 / \u5BC6\u9001"
|
|
840
1382
|
),
|
|
841
|
-
(0,
|
|
1383
|
+
(0, import_react6.createElement)(
|
|
842
1384
|
"div",
|
|
843
1385
|
{ className: "mailharbor-toolbar" },
|
|
844
|
-
(0,
|
|
845
|
-
(0,
|
|
846
|
-
(0,
|
|
847
|
-
(0,
|
|
848
|
-
(0,
|
|
849
|
-
(0,
|
|
850
|
-
(0,
|
|
1386
|
+
(0, import_react6.createElement)("button", { onClick: () => exec("bold"), title: "\u52A0\u7C97" }, "B"),
|
|
1387
|
+
(0, import_react6.createElement)("button", { onClick: () => exec("italic"), title: "\u659C\u4F53" }, "I"),
|
|
1388
|
+
(0, import_react6.createElement)("button", { onClick: () => exec("underline"), title: "\u4E0B\u5212\u7EBF" }, "U"),
|
|
1389
|
+
(0, import_react6.createElement)("button", { onClick: () => exec("insertUnorderedList"), title: "\u65E0\u5E8F\u5217\u8868" }, "\u2022 \u5217\u8868"),
|
|
1390
|
+
(0, import_react6.createElement)("button", { onClick: () => exec("insertOrderedList"), title: "\u6709\u5E8F\u5217\u8868" }, "1. \u5217\u8868"),
|
|
1391
|
+
(0, import_react6.createElement)("button", { onClick: () => exec("formatBlock", "blockquote"), title: "\u5F15\u7528" }, "\u5F15\u7528"),
|
|
1392
|
+
(0, import_react6.createElement)(
|
|
851
1393
|
"button",
|
|
852
1394
|
{
|
|
853
1395
|
onClick: () => {
|
|
@@ -858,23 +1400,44 @@ function Composer({
|
|
|
858
1400
|
},
|
|
859
1401
|
"\u94FE\u63A5"
|
|
860
1402
|
),
|
|
861
|
-
(0,
|
|
862
|
-
(0,
|
|
863
|
-
(0,
|
|
1403
|
+
(0, import_react6.createElement)("button", { onClick: () => exec("removeFormat"), title: "\u6E05\u9664\u683C\u5F0F" }, "\u6E05\u9664"),
|
|
1404
|
+
(0, import_react6.createElement)("span", { className: "spacer" }),
|
|
1405
|
+
(0, import_react6.createElement)(
|
|
1406
|
+
"button",
|
|
1407
|
+
{
|
|
1408
|
+
className: "ai",
|
|
1409
|
+
onClick: () => setAiDraftOpen(!aiDraftOpen),
|
|
1410
|
+
disabled: aiBusy !== null,
|
|
1411
|
+
title: "\u8BA9 AI \u8D77\u8349\u6B63\u6587"
|
|
1412
|
+
},
|
|
1413
|
+
(0, import_react6.createElement)(Icon, { name: "sparkles", size: 13 }),
|
|
1414
|
+
"\u8D77\u8349"
|
|
1415
|
+
),
|
|
1416
|
+
(0, import_react6.createElement)(
|
|
1417
|
+
"button",
|
|
1418
|
+
{ className: "ai", onClick: () => void runAi("polish"), disabled: aiBusy !== null || !hasBody },
|
|
1419
|
+
aiBusy === "polish" ? "\u6DA6\u8272\u4E2D\u2026" : "\u6DA6\u8272"
|
|
1420
|
+
),
|
|
1421
|
+
(0, import_react6.createElement)(
|
|
1422
|
+
"button",
|
|
1423
|
+
{ className: "ai", onClick: () => void runAi("shorten"), disabled: aiBusy !== null || !hasBody },
|
|
1424
|
+
aiBusy === "shorten" ? "\u7F29\u77ED\u4E2D\u2026" : "\u7F29\u77ED"
|
|
1425
|
+
),
|
|
1426
|
+
(0, import_react6.createElement)(
|
|
864
1427
|
"button",
|
|
865
1428
|
{ onClick: () => setPendingLang("zh"), disabled: translating !== null, title: "AI \u7FFB\u8BD1\u6210\u4E2D\u6587" },
|
|
866
|
-
translating === "zh" ? "\
|
|
1429
|
+
translating === "zh" ? "\u2026" : "\u4E2D"
|
|
867
1430
|
),
|
|
868
|
-
(0,
|
|
1431
|
+
(0, import_react6.createElement)(
|
|
869
1432
|
"button",
|
|
870
1433
|
{ onClick: () => setPendingLang("en"), disabled: translating !== null, title: "AI \u7FFB\u8BD1\u6210\u82F1\u6587" },
|
|
871
1434
|
translating === "en" ? "\u2026" : "EN"
|
|
872
1435
|
),
|
|
873
|
-
(0,
|
|
1436
|
+
(0, import_react6.createElement)(
|
|
874
1437
|
"label",
|
|
875
1438
|
{ className: "mailharbor-toggle small" },
|
|
876
1439
|
"\u9644\u4EF6",
|
|
877
|
-
(0,
|
|
1440
|
+
(0, import_react6.createElement)("input", {
|
|
878
1441
|
type: "file",
|
|
879
1442
|
multiple: true,
|
|
880
1443
|
style: { display: "none" },
|
|
@@ -885,101 +1448,92 @@ function Composer({
|
|
|
885
1448
|
})
|
|
886
1449
|
)
|
|
887
1450
|
),
|
|
888
|
-
|
|
1451
|
+
aiDraftOpen ? (0, import_react6.createElement)(
|
|
1452
|
+
"div",
|
|
1453
|
+
{ className: "mailharbor-confirm" },
|
|
1454
|
+
(0, import_react6.createElement)("input", {
|
|
1455
|
+
value: aiInstruction,
|
|
1456
|
+
placeholder: "\u60F3\u8BA9 AI \u5199\u4EC0\u4E48\uFF1F\u4F8B\u5982\uFF1A\u786E\u8BA4\u6536\u5230\uFF0C\u5468\u4E94\u524D\u7ED9\u7B54\u590D",
|
|
1457
|
+
onChange: (event) => setAiInstruction(event.target.value),
|
|
1458
|
+
onKeyDown: (event) => {
|
|
1459
|
+
if (event.key === "Enter") void runAi("draft");
|
|
1460
|
+
}
|
|
1461
|
+
}),
|
|
1462
|
+
(0, import_react6.createElement)(
|
|
1463
|
+
"button",
|
|
1464
|
+
{ className: "primary", onClick: () => void runAi("draft"), disabled: aiBusy !== null },
|
|
1465
|
+
aiBusy === "draft" ? "\u751F\u6210\u4E2D\u2026" : "\u751F\u6210"
|
|
1466
|
+
),
|
|
1467
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: () => setAiDraftOpen(false) }, "\u53D6\u6D88")
|
|
1468
|
+
) : null,
|
|
1469
|
+
pendingLang === null ? null : (0, import_react6.createElement)(
|
|
889
1470
|
"div",
|
|
890
1471
|
{ className: "mailharbor-confirm" },
|
|
891
|
-
(0,
|
|
892
|
-
(0,
|
|
893
|
-
(0,
|
|
894
|
-
(0,
|
|
1472
|
+
(0, import_react6.createElement)("span", null, pendingLang === "en" ? "\u628A\u6B63\u6587\u7FFB\u8BD1\u6210\u82F1\u6587\uFF1F" : "\u628A\u6B63\u6587\u7FFB\u8BD1\u6210\u4E2D\u6587\uFF1F"),
|
|
1473
|
+
(0, import_react6.createElement)("span", { className: "spacer" }),
|
|
1474
|
+
(0, import_react6.createElement)("button", { className: "ghost", onClick: () => setPendingLang(null) }, "\u53D6\u6D88"),
|
|
1475
|
+
(0, import_react6.createElement)("button", { className: "primary", onClick: () => void translate(pendingLang) }, "\u7FFB\u8BD1")
|
|
895
1476
|
),
|
|
896
|
-
attachments.length === 0 ? null : (0,
|
|
1477
|
+
attachments.length === 0 ? null : (0, import_react6.createElement)(
|
|
897
1478
|
"div",
|
|
898
1479
|
{ className: "mailharbor-attachments", style: { padding: 0, border: 0 } },
|
|
899
1480
|
attachments.map(
|
|
900
|
-
(file, index) => (0,
|
|
1481
|
+
(file, index) => (0, import_react6.createElement)(
|
|
901
1482
|
"span",
|
|
902
1483
|
{ className: "mailharbor-attachment", key: `${file.filename}:${String(index)}` },
|
|
903
|
-
(0,
|
|
904
|
-
(0,
|
|
1484
|
+
(0, import_react6.createElement)("span", { className: "name" }, file.filename),
|
|
1485
|
+
(0, import_react6.createElement)(
|
|
905
1486
|
"button",
|
|
906
1487
|
{
|
|
907
1488
|
className: "subtle",
|
|
908
|
-
onClick: () =>
|
|
1489
|
+
onClick: () => {
|
|
1490
|
+
setAttachments(attachments.filter((_item, position) => position !== index));
|
|
1491
|
+
markDirty();
|
|
1492
|
+
}
|
|
909
1493
|
},
|
|
910
1494
|
"\u2715"
|
|
911
1495
|
)
|
|
912
1496
|
)
|
|
913
1497
|
)
|
|
914
1498
|
),
|
|
915
|
-
(0,
|
|
1499
|
+
(0, import_react6.createElement)("div", {
|
|
916
1500
|
className: "mailharbor-editor",
|
|
917
1501
|
ref: editor,
|
|
918
1502
|
contentEditable: true,
|
|
919
1503
|
suppressContentEditableWarning: true,
|
|
920
1504
|
"data-placeholder": "\u5199\u70B9\u4EC0\u4E48\u2026",
|
|
921
|
-
spellCheck: true
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
reply: (0, import_react5.createElement)("path", { d: "M9 7 4 12l5 5M4 12h9a6 6 0 0 1 6 6v1" }),
|
|
949
|
-
replyAll: (0, import_react5.createElement)("path", { d: "m9 7-5 5 5 5M14 7l-5 5 5 5M13 12h5a5 5 0 0 1 5 5" }),
|
|
950
|
-
forward: (0, import_react5.createElement)("path", { d: "m15 7 5 5-5 5M20 12h-9a6 6 0 0 0-6 6v1" }),
|
|
951
|
-
check: (0, import_react5.createElement)("path", { d: "m5 13 4 4L19 7" }),
|
|
952
|
-
image: (0, import_react5.createElement)(
|
|
953
|
-
"g",
|
|
954
|
-
null,
|
|
955
|
-
(0, import_react5.createElement)("rect", { x: 3, y: 4, width: 18, height: 16, rx: 2 }),
|
|
956
|
-
(0, import_react5.createElement)("circle", { cx: 8.5, cy: 9.5, r: 1.5 }),
|
|
957
|
-
(0, import_react5.createElement)("path", { d: "m4 18 5-5 4 4 3-3 4 4" })
|
|
958
|
-
),
|
|
959
|
-
close: (0, import_react5.createElement)("path", { d: "M6 6l12 12M18 6 6 18" }),
|
|
960
|
-
chevronLeft: (0, import_react5.createElement)("path", { d: "m14 6-6 6 6 6" })
|
|
961
|
-
};
|
|
962
|
-
function Icon({ name, size = 16 }) {
|
|
963
|
-
return (0, import_react5.createElement)(
|
|
964
|
-
"svg",
|
|
965
|
-
{
|
|
966
|
-
width: size,
|
|
967
|
-
height: size,
|
|
968
|
-
viewBox: "0 0 24 24",
|
|
969
|
-
fill: "none",
|
|
970
|
-
stroke: "currentColor",
|
|
971
|
-
strokeWidth: 1.7,
|
|
972
|
-
strokeLinecap: "round",
|
|
973
|
-
strokeLinejoin: "round",
|
|
974
|
-
"aria-hidden": "true",
|
|
975
|
-
focusable: "false"
|
|
976
|
-
},
|
|
977
|
-
PATHS[name] ?? null
|
|
1505
|
+
spellCheck: true,
|
|
1506
|
+
onInput: markDirty
|
|
1507
|
+
}),
|
|
1508
|
+
(0, import_react6.createElement)(
|
|
1509
|
+
"div",
|
|
1510
|
+
{ className: "mailharbor-versions" },
|
|
1511
|
+
(0, import_react6.createElement)("span", { className: "small muted" }, "\u7248\u672C"),
|
|
1512
|
+
versions.map(
|
|
1513
|
+
(version, index) => (0, import_react6.createElement)(
|
|
1514
|
+
"button",
|
|
1515
|
+
{
|
|
1516
|
+
key: `${version.label}:${String(index)}`,
|
|
1517
|
+
className: index === versionIndex ? "chip active" : "chip",
|
|
1518
|
+
title: version.label,
|
|
1519
|
+
onClick: () => restoreVersion(index)
|
|
1520
|
+
},
|
|
1521
|
+
String(index + 1)
|
|
1522
|
+
)
|
|
1523
|
+
),
|
|
1524
|
+
versions.length > 1 ? (0, import_react6.createElement)(
|
|
1525
|
+
"button",
|
|
1526
|
+
{ className: "subtle small", onClick: () => restoreVersion(Math.max(0, versionIndex - 1)) },
|
|
1527
|
+
"\u6062\u590D\u4E0A\u4E00\u7248"
|
|
1528
|
+
) : null,
|
|
1529
|
+
(0, import_react6.createElement)("span", { className: "spacer" }),
|
|
1530
|
+
(0, import_react6.createElement)("span", { className: "small muted" }, versions[versionIndex]?.label ?? "")
|
|
1531
|
+
)
|
|
978
1532
|
);
|
|
979
1533
|
}
|
|
980
1534
|
|
|
981
1535
|
// src/client/reader.tsx
|
|
982
|
-
var
|
|
1536
|
+
var import_react7 = require("react");
|
|
983
1537
|
var IFRAME_STYLE = `
|
|
984
1538
|
html, body { margin: 0; padding: 0; }
|
|
985
1539
|
body {
|
|
@@ -1011,18 +1565,13 @@ function srcDoc(detail, allowRemoteImages) {
|
|
|
1011
1565
|
function escapeHtml(value) {
|
|
1012
1566
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
1013
1567
|
}
|
|
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
|
-
}
|
|
1019
1568
|
function hasRemoteImages(detail) {
|
|
1020
1569
|
return detail.html !== null && /<img[^>]+src=["']https?:/i.test(detail.html);
|
|
1021
1570
|
}
|
|
1022
1571
|
function addressLine(list) {
|
|
1023
1572
|
return list.map((item) => item.name !== "" ? `${item.name} <${item.address}>` : item.address).join("\u3001");
|
|
1024
1573
|
}
|
|
1025
|
-
function
|
|
1574
|
+
function errorText4(error) {
|
|
1026
1575
|
return error instanceof Error ? error.message : String(error);
|
|
1027
1576
|
}
|
|
1028
1577
|
function Reader({
|
|
@@ -1031,115 +1580,49 @@ function Reader({
|
|
|
1031
1580
|
mailbox,
|
|
1032
1581
|
onChanged,
|
|
1033
1582
|
onReply,
|
|
1034
|
-
|
|
1583
|
+
onAiAction,
|
|
1035
1584
|
onClose
|
|
1036
1585
|
}) {
|
|
1037
|
-
const [plain, setPlain] = (0,
|
|
1038
|
-
const [loadImages, setLoadImages] = (0,
|
|
1039
|
-
const [busy, setBusy] = (0,
|
|
1040
|
-
const [error, setError] = (0,
|
|
1041
|
-
const
|
|
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)("");
|
|
1051
|
-
const remoteImages = (0, import_react6.useMemo)(() => hasRemoteImages(detail), [detail]);
|
|
1586
|
+
const [plain, setPlain] = (0, import_react7.useState)(false);
|
|
1587
|
+
const [loadImages, setLoadImages] = (0, import_react7.useState)(false);
|
|
1588
|
+
const [busy, setBusy] = (0, import_react7.useState)(false);
|
|
1589
|
+
const [error, setError] = (0, import_react7.useState)("");
|
|
1590
|
+
const remoteImages = (0, import_react7.useMemo)(() => hasRemoteImages(detail), [detail]);
|
|
1052
1591
|
const html = detail.html !== null && detail.html !== "";
|
|
1053
1592
|
const toggleRead = async () => {
|
|
1054
|
-
setBusy(true);
|
|
1055
|
-
setError("");
|
|
1056
|
-
try {
|
|
1057
|
-
const result = await api.setRead({
|
|
1058
|
-
id: detail.id,
|
|
1059
|
-
accountId: detail.accountId,
|
|
1060
|
-
source,
|
|
1061
|
-
mailbox,
|
|
1062
|
-
read: detail.unread
|
|
1063
|
-
});
|
|
1064
|
-
onChanged({ unread: !result.read });
|
|
1065
|
-
} catch (caught) {
|
|
1066
|
-
setError(errorText3(caught));
|
|
1067
|
-
} finally {
|
|
1068
|
-
setBusy(false);
|
|
1069
|
-
}
|
|
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("");
|
|
1593
|
+
setBusy(true);
|
|
1594
|
+
setError("");
|
|
1100
1595
|
try {
|
|
1101
|
-
const result = await api.
|
|
1596
|
+
const result = await api.setRead({
|
|
1597
|
+
id: detail.id,
|
|
1102
1598
|
accountId: detail.accountId,
|
|
1103
1599
|
source,
|
|
1104
|
-
id: detail.id,
|
|
1105
1600
|
mailbox,
|
|
1106
|
-
|
|
1107
|
-
language: replyLanguage
|
|
1601
|
+
read: detail.unread
|
|
1108
1602
|
});
|
|
1109
|
-
|
|
1603
|
+
onChanged({ unread: !result.read });
|
|
1110
1604
|
} catch (caught) {
|
|
1111
|
-
|
|
1605
|
+
setError(errorText4(caught));
|
|
1112
1606
|
} finally {
|
|
1113
|
-
|
|
1607
|
+
setBusy(false);
|
|
1114
1608
|
}
|
|
1115
1609
|
};
|
|
1116
|
-
|
|
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
|
-
};
|
|
1127
|
-
return (0, import_react6.createElement)(
|
|
1610
|
+
return (0, import_react7.createElement)(
|
|
1128
1611
|
"div",
|
|
1129
1612
|
{ className: "mailharbor-reader" },
|
|
1130
|
-
(0,
|
|
1613
|
+
(0, import_react7.createElement)(
|
|
1131
1614
|
"div",
|
|
1132
1615
|
{ className: "mailharbor-reader-head" },
|
|
1133
|
-
(0,
|
|
1616
|
+
(0, import_react7.createElement)(
|
|
1134
1617
|
"div",
|
|
1135
1618
|
{ className: "mailharbor-reader-title" },
|
|
1136
|
-
onClose === void 0 ? null : (0,
|
|
1619
|
+
onClose === void 0 ? null : (0, import_react7.createElement)(
|
|
1137
1620
|
"button",
|
|
1138
1621
|
{ className: "ghost icon", onClick: onClose, "aria-label": "\u8FD4\u56DE\u5217\u8868", title: "\u8FD4\u56DE\u5217\u8868" },
|
|
1139
|
-
(0,
|
|
1622
|
+
(0, import_react7.createElement)(Icon, { name: "chevronLeft" })
|
|
1140
1623
|
),
|
|
1141
|
-
(0,
|
|
1142
|
-
(0,
|
|
1624
|
+
(0, import_react7.createElement)("h2", { style: { flex: 1 } }, detail.subject),
|
|
1625
|
+
(0, import_react7.createElement)(
|
|
1143
1626
|
"button",
|
|
1144
1627
|
{
|
|
1145
1628
|
className: "ghost icon",
|
|
@@ -1148,82 +1631,88 @@ function Reader({
|
|
|
1148
1631
|
title: detail.unread ? "\u6807\u4E3A\u5DF2\u8BFB" : "\u6807\u4E3A\u672A\u8BFB",
|
|
1149
1632
|
"aria-label": detail.unread ? "\u6807\u4E3A\u5DF2\u8BFB" : "\u6807\u4E3A\u672A\u8BFB"
|
|
1150
1633
|
},
|
|
1151
|
-
(0,
|
|
1634
|
+
(0, import_react7.createElement)(Icon, { name: "check" })
|
|
1152
1635
|
)
|
|
1153
1636
|
),
|
|
1154
|
-
(0,
|
|
1637
|
+
(0, import_react7.createElement)(
|
|
1155
1638
|
"div",
|
|
1156
1639
|
{ className: "mailharbor-reader-meta" },
|
|
1157
|
-
(0,
|
|
1640
|
+
(0, import_react7.createElement)(
|
|
1158
1641
|
"span",
|
|
1159
1642
|
{ className: "who" },
|
|
1160
|
-
(0,
|
|
1161
|
-
(0,
|
|
1162
|
-
detail.from.name !== "" ? (0,
|
|
1643
|
+
(0, import_react7.createElement)(Avatar, { email: detail.from.address, size: 22 }),
|
|
1644
|
+
(0, import_react7.createElement)("strong", null, detail.from.name !== "" ? detail.from.name : detail.from.address),
|
|
1645
|
+
detail.from.name !== "" ? (0, import_react7.createElement)("span", null, `<${detail.from.address}>`) : null
|
|
1163
1646
|
),
|
|
1164
|
-
(0,
|
|
1165
|
-
detail.backedUp === false ? (0,
|
|
1647
|
+
(0, import_react7.createElement)("span", null, longDate(detail.date)),
|
|
1648
|
+
detail.backedUp === false ? (0, import_react7.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null
|
|
1166
1649
|
),
|
|
1167
|
-
(0,
|
|
1650
|
+
(0, import_react7.createElement)(
|
|
1168
1651
|
"div",
|
|
1169
1652
|
{ className: "mailharbor-reader-meta" },
|
|
1170
|
-
(0,
|
|
1171
|
-
detail.cc.length === 0 ? null : (0,
|
|
1653
|
+
(0, import_react7.createElement)("span", null, `\u6536\u4EF6\u4EBA: ${addressLine(detail.to) || "\u2014"}`),
|
|
1654
|
+
detail.cc.length === 0 ? null : (0, import_react7.createElement)("span", null, `\u6284\u9001: ${addressLine(detail.cc)}`)
|
|
1172
1655
|
),
|
|
1173
|
-
(0,
|
|
1656
|
+
(0, import_react7.createElement)(
|
|
1174
1657
|
"div",
|
|
1175
1658
|
{ className: "mailharbor-reader-actions" },
|
|
1176
|
-
(0,
|
|
1659
|
+
(0, import_react7.createElement)(
|
|
1177
1660
|
"button",
|
|
1178
1661
|
{ className: "ghost", onClick: () => onReply("reply") },
|
|
1179
|
-
(0,
|
|
1662
|
+
(0, import_react7.createElement)(Icon, { name: "reply" }),
|
|
1180
1663
|
"\u56DE\u590D"
|
|
1181
1664
|
),
|
|
1182
|
-
(0,
|
|
1665
|
+
(0, import_react7.createElement)(
|
|
1183
1666
|
"button",
|
|
1184
1667
|
{ className: "ghost", onClick: () => onReply("replyAll") },
|
|
1185
|
-
(0,
|
|
1668
|
+
(0, import_react7.createElement)(Icon, { name: "replyAll" }),
|
|
1186
1669
|
"\u5168\u90E8\u56DE\u590D"
|
|
1187
1670
|
),
|
|
1188
|
-
(0,
|
|
1671
|
+
(0, import_react7.createElement)(
|
|
1189
1672
|
"button",
|
|
1190
1673
|
{ className: "ghost", onClick: () => onReply("forward") },
|
|
1191
|
-
(0,
|
|
1674
|
+
(0, import_react7.createElement)(Icon, { name: "forward" }),
|
|
1192
1675
|
"\u8F6C\u53D1"
|
|
1193
1676
|
),
|
|
1194
|
-
(0,
|
|
1195
|
-
(0,
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1677
|
+
(0, import_react7.createElement)("span", { className: "mailharbor-divider" }),
|
|
1678
|
+
(0, import_react7.createElement)(
|
|
1679
|
+
"button",
|
|
1680
|
+
{ className: "ghost", onClick: () => onAiAction("summary") },
|
|
1681
|
+
(0, import_react7.createElement)(Icon, { name: "sparkles", size: 14 }),
|
|
1682
|
+
"AI \u6458\u8981"
|
|
1683
|
+
),
|
|
1684
|
+
(0, import_react7.createElement)("button", { className: "ghost", onClick: () => onAiAction("reply") }, "AI \u56DE\u590D"),
|
|
1685
|
+
(0, import_react7.createElement)("button", { className: "ghost", onClick: () => onAiAction("translate") }, "AI \u7FFB\u8BD1"),
|
|
1686
|
+
(0, import_react7.createElement)("span", { className: "spacer" }),
|
|
1687
|
+
html ? (0, import_react7.createElement)(
|
|
1199
1688
|
"button",
|
|
1200
1689
|
{ className: "subtle", onClick: () => setPlain(!plain) },
|
|
1201
1690
|
plain ? "\u663E\u793A HTML" : "\u7EAF\u6587\u672C"
|
|
1202
1691
|
) : null,
|
|
1203
|
-
remoteImages && !loadImages ? (0,
|
|
1692
|
+
remoteImages && !loadImages ? (0, import_react7.createElement)(
|
|
1204
1693
|
"button",
|
|
1205
1694
|
{ className: "subtle", onClick: () => setLoadImages(true) },
|
|
1206
|
-
(0,
|
|
1695
|
+
(0, import_react7.createElement)(Icon, { name: "image", size: 14 }),
|
|
1207
1696
|
"\u663E\u793A\u56FE\u7247"
|
|
1208
1697
|
) : null
|
|
1209
1698
|
),
|
|
1210
|
-
error === "" ? null : (0,
|
|
1699
|
+
error === "" ? null : (0, import_react7.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error)
|
|
1211
1700
|
),
|
|
1212
|
-
(0,
|
|
1701
|
+
(0, import_react7.createElement)(
|
|
1213
1702
|
"div",
|
|
1214
1703
|
{ className: "mailharbor-reader-body" },
|
|
1215
|
-
html && !plain ? (0,
|
|
1704
|
+
html && !plain ? (0, import_react7.createElement)("iframe", {
|
|
1216
1705
|
title: "\u90AE\u4EF6\u6B63\u6587",
|
|
1217
1706
|
sandbox: "",
|
|
1218
1707
|
srcDoc: srcDoc(detail, loadImages),
|
|
1219
1708
|
referrerPolicy: "no-referrer"
|
|
1220
|
-
}) : (0,
|
|
1709
|
+
}) : (0, import_react7.createElement)("pre", null, detail.text !== "" ? detail.text : "\uFF08\u65E0\u6B63\u6587\uFF09")
|
|
1221
1710
|
),
|
|
1222
|
-
detail.attachments.length === 0 ? null : (0,
|
|
1711
|
+
detail.attachments.length === 0 ? null : (0, import_react7.createElement)(
|
|
1223
1712
|
"div",
|
|
1224
1713
|
{ className: "mailharbor-attachments" },
|
|
1225
1714
|
detail.attachments.map(
|
|
1226
|
-
(file) => (0,
|
|
1715
|
+
(file) => (0, import_react7.createElement)(
|
|
1227
1716
|
"a",
|
|
1228
1717
|
{
|
|
1229
1718
|
className: "mailharbor-attachment",
|
|
@@ -1243,132 +1732,19 @@ function Reader({
|
|
|
1243
1732
|
download: file.filename,
|
|
1244
1733
|
title: file.filename
|
|
1245
1734
|
},
|
|
1246
|
-
(0,
|
|
1247
|
-
(0,
|
|
1248
|
-
(0,
|
|
1249
|
-
)
|
|
1250
|
-
)
|
|
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
|
-
)
|
|
1735
|
+
(0, import_react7.createElement)(Icon, { name: "paperclip", size: 14 }),
|
|
1736
|
+
(0, import_react7.createElement)("span", { className: "name" }, file.filename),
|
|
1737
|
+
(0, import_react7.createElement)("span", { className: "muted small" }, formatBytes(file.size))
|
|
1288
1738
|
)
|
|
1289
1739
|
)
|
|
1290
|
-
)
|
|
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
|
|
1740
|
+
)
|
|
1365
1741
|
);
|
|
1366
1742
|
}
|
|
1367
1743
|
|
|
1368
1744
|
// src/client/panel.tsx
|
|
1369
1745
|
var EMPTY_PAGE = { items: [], total: 0, offset: 0, limit: 40 };
|
|
1370
1746
|
var DRAFTS = "__drafts";
|
|
1371
|
-
function
|
|
1747
|
+
function errorText5(error) {
|
|
1372
1748
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
1373
1749
|
}
|
|
1374
1750
|
function matchesNarrow() {
|
|
@@ -1379,41 +1755,43 @@ function quoteForReply(detail) {
|
|
|
1379
1755
|
return `<p><br></p><p>------------------ \u539F\u59CB\u90AE\u4EF6 ------------------</p><blockquote style="margin:0 0 0 12px;padding-left:12px;border-left:2px solid #d7dbe3;color:#565c67">${body}</blockquote>`;
|
|
1380
1756
|
}
|
|
1381
1757
|
function MailPanel() {
|
|
1382
|
-
const [state, setState] = (0,
|
|
1383
|
-
const [error, setError] = (0,
|
|
1384
|
-
const [accountScope, setAccountScope] = (0,
|
|
1385
|
-
const [source, setSource] = (0,
|
|
1386
|
-
const [mailbox, setMailbox] = (0,
|
|
1387
|
-
const [folders, setFolders] = (0,
|
|
1388
|
-
const [query2, setQuery] = (0,
|
|
1389
|
-
const [search, setSearch] = (0,
|
|
1390
|
-
const [page, setPage] = (0,
|
|
1391
|
-
const [drafts, setDrafts] = (0,
|
|
1392
|
-
const [limit, setLimit] = (0,
|
|
1393
|
-
const [loading, setLoading] = (0,
|
|
1394
|
-
const [selected, setSelected] = (0,
|
|
1395
|
-
const [compose, setCompose] = (0,
|
|
1396
|
-
const [showAdd, setShowAdd] = (0,
|
|
1397
|
-
const [showBackup, setShowBackup] = (0,
|
|
1398
|
-
const [backup, setBackup] = (0,
|
|
1399
|
-
const [toast, setToast] = (0,
|
|
1400
|
-
const [narrow, setNarrow] = (0,
|
|
1401
|
-
const [
|
|
1758
|
+
const [state, setState] = (0, import_react8.useState)(null);
|
|
1759
|
+
const [error, setError] = (0, import_react8.useState)("");
|
|
1760
|
+
const [accountScope, setAccountScope] = (0, import_react8.useState)(null);
|
|
1761
|
+
const [source, setSource] = (0, import_react8.useState)("local");
|
|
1762
|
+
const [mailbox, setMailbox] = (0, import_react8.useState)("all");
|
|
1763
|
+
const [folders, setFolders] = (0, import_react8.useState)([]);
|
|
1764
|
+
const [query2, setQuery] = (0, import_react8.useState)("");
|
|
1765
|
+
const [search, setSearch] = (0, import_react8.useState)("");
|
|
1766
|
+
const [page, setPage] = (0, import_react8.useState)(EMPTY_PAGE);
|
|
1767
|
+
const [drafts, setDrafts] = (0, import_react8.useState)([]);
|
|
1768
|
+
const [limit, setLimit] = (0, import_react8.useState)(40);
|
|
1769
|
+
const [loading, setLoading] = (0, import_react8.useState)(false);
|
|
1770
|
+
const [selected, setSelected] = (0, import_react8.useState)(null);
|
|
1771
|
+
const [compose, setCompose] = (0, import_react8.useState)(null);
|
|
1772
|
+
const [showAdd, setShowAdd] = (0, import_react8.useState)(false);
|
|
1773
|
+
const [showBackup, setShowBackup] = (0, import_react8.useState)(false);
|
|
1774
|
+
const [backup, setBackup] = (0, import_react8.useState)(null);
|
|
1775
|
+
const [toast, setToast] = (0, import_react8.useState)("");
|
|
1776
|
+
const [narrow, setNarrow] = (0, import_react8.useState)(() => matchesNarrow());
|
|
1777
|
+
const [railOpen, setRailOpen] = (0, import_react8.useState)(false);
|
|
1778
|
+
const [aiRequest, setAiRequest] = (0, import_react8.useState)(null);
|
|
1779
|
+
const [tick, setTick] = (0, import_react8.useState)(0);
|
|
1402
1780
|
const accounts = state?.accounts ?? [];
|
|
1403
1781
|
const isAll = accountScope === "all";
|
|
1404
1782
|
const account = accounts.find((item) => item.id === accountScope) ?? null;
|
|
1405
1783
|
const composeAccount = accounts.find((item) => item.id === compose?.accountId) ?? account ?? accounts[0] ?? null;
|
|
1406
1784
|
const showingDrafts = mailbox === DRAFTS && source === "local";
|
|
1407
|
-
const refresh = (0,
|
|
1785
|
+
const refresh = (0, import_react8.useCallback)(() => {
|
|
1408
1786
|
setTick((value) => value + 1);
|
|
1409
1787
|
}, []);
|
|
1410
|
-
const notice = (0,
|
|
1788
|
+
const notice = (0, import_react8.useCallback)((message) => {
|
|
1411
1789
|
setToast(message);
|
|
1412
1790
|
window.setTimeout(() => {
|
|
1413
1791
|
setToast("");
|
|
1414
1792
|
}, 3200);
|
|
1415
1793
|
}, []);
|
|
1416
|
-
(0,
|
|
1794
|
+
(0, import_react8.useEffect)(() => {
|
|
1417
1795
|
let cancelled = false;
|
|
1418
1796
|
api.state().then((next) => {
|
|
1419
1797
|
if (cancelled) return;
|
|
@@ -1421,13 +1799,13 @@ function MailPanel() {
|
|
|
1421
1799
|
if (next.settings.render.listLimit > 0) setLimit(next.settings.render.listLimit);
|
|
1422
1800
|
setAccountScope((current) => current ?? next.activeAccountId);
|
|
1423
1801
|
}).catch((caught) => {
|
|
1424
|
-
if (!cancelled) setError(
|
|
1802
|
+
if (!cancelled) setError(errorText5(caught));
|
|
1425
1803
|
});
|
|
1426
1804
|
return () => {
|
|
1427
1805
|
cancelled = true;
|
|
1428
1806
|
};
|
|
1429
1807
|
}, [tick]);
|
|
1430
|
-
(0,
|
|
1808
|
+
(0, import_react8.useEffect)(() => {
|
|
1431
1809
|
if (accountScope === null) return;
|
|
1432
1810
|
let cancelled = false;
|
|
1433
1811
|
api.folders({ accountId: accountScope, source }).then((result) => {
|
|
@@ -1451,16 +1829,16 @@ function MailPanel() {
|
|
|
1451
1829
|
cancelled = true;
|
|
1452
1830
|
};
|
|
1453
1831
|
}, [accountScope, source, tick]);
|
|
1454
|
-
(0,
|
|
1832
|
+
(0, import_react8.useEffect)(() => {
|
|
1455
1833
|
setMailbox(source === "local" ? "all" : "INBOX");
|
|
1456
1834
|
setSelected(null);
|
|
1457
1835
|
setLimit(state?.settings.render.listLimit ?? 40);
|
|
1458
1836
|
}, [source]);
|
|
1459
|
-
(0,
|
|
1837
|
+
(0, import_react8.useEffect)(() => {
|
|
1460
1838
|
if (accountScope === "all" && source === "server") setSource("local");
|
|
1461
1839
|
setSelected(null);
|
|
1462
1840
|
}, [accountScope, source]);
|
|
1463
|
-
(0,
|
|
1841
|
+
(0, import_react8.useEffect)(() => {
|
|
1464
1842
|
if (accountScope === null) return;
|
|
1465
1843
|
let cancelled = false;
|
|
1466
1844
|
setLoading(true);
|
|
@@ -1492,7 +1870,7 @@ function MailPanel() {
|
|
|
1492
1870
|
setDrafts([]);
|
|
1493
1871
|
}
|
|
1494
1872
|
}).catch((caught) => {
|
|
1495
|
-
if (!cancelled) setError(
|
|
1873
|
+
if (!cancelled) setError(errorText5(caught));
|
|
1496
1874
|
}).finally(() => {
|
|
1497
1875
|
if (!cancelled) setLoading(false);
|
|
1498
1876
|
});
|
|
@@ -1500,7 +1878,7 @@ function MailPanel() {
|
|
|
1500
1878
|
cancelled = true;
|
|
1501
1879
|
};
|
|
1502
1880
|
}, [accountScope, source, mailbox, search, limit, tick]);
|
|
1503
|
-
(0,
|
|
1881
|
+
(0, import_react8.useEffect)(() => {
|
|
1504
1882
|
if (typeof window.matchMedia !== "function") return;
|
|
1505
1883
|
const media = window.matchMedia("(max-width: 900px)");
|
|
1506
1884
|
const onChange = () => setNarrow(media.matches);
|
|
@@ -1509,7 +1887,7 @@ function MailPanel() {
|
|
|
1509
1887
|
media.removeEventListener("change", onChange);
|
|
1510
1888
|
};
|
|
1511
1889
|
}, []);
|
|
1512
|
-
(0,
|
|
1890
|
+
(0, import_react8.useEffect)(() => {
|
|
1513
1891
|
if (!showBackup || accountScope === null || isAll) return;
|
|
1514
1892
|
let cancelled = false;
|
|
1515
1893
|
const poll = async () => {
|
|
@@ -1547,7 +1925,7 @@ function MailPanel() {
|
|
|
1547
1925
|
}
|
|
1548
1926
|
}
|
|
1549
1927
|
} catch (caught) {
|
|
1550
|
-
setError(
|
|
1928
|
+
setError(errorText5(caught));
|
|
1551
1929
|
}
|
|
1552
1930
|
};
|
|
1553
1931
|
const startCompose = (mode) => {
|
|
@@ -1586,6 +1964,43 @@ function MailPanel() {
|
|
|
1586
1964
|
...detail.messageId === "" ? {} : { inReplyTo: detail.messageId, references: [...detail.references, detail.messageId].join(" ") }
|
|
1587
1965
|
});
|
|
1588
1966
|
};
|
|
1967
|
+
const requestAi = (kind) => {
|
|
1968
|
+
setRailOpen(true);
|
|
1969
|
+
setAiRequest({ kind, nonce: Date.now() });
|
|
1970
|
+
};
|
|
1971
|
+
const insertReply = (init) => {
|
|
1972
|
+
if (selected === null) return;
|
|
1973
|
+
setCompose({
|
|
1974
|
+
mode: "reply",
|
|
1975
|
+
accountId: selected.accountId,
|
|
1976
|
+
to: init.to,
|
|
1977
|
+
cc: init.cc,
|
|
1978
|
+
bcc: "",
|
|
1979
|
+
subject: init.subject,
|
|
1980
|
+
html: init.html,
|
|
1981
|
+
...init.inReplyTo === void 0 ? {} : { inReplyTo: init.inReplyTo },
|
|
1982
|
+
...init.references === void 0 ? {} : { references: init.references }
|
|
1983
|
+
});
|
|
1984
|
+
};
|
|
1985
|
+
const saveReplyDraft = async (init) => {
|
|
1986
|
+
if (selected === null) return;
|
|
1987
|
+
try {
|
|
1988
|
+
await api.saveDraft(selected.accountId, {
|
|
1989
|
+
to: init.to,
|
|
1990
|
+
cc: init.cc,
|
|
1991
|
+
bcc: "",
|
|
1992
|
+
subject: init.subject,
|
|
1993
|
+
html: init.html,
|
|
1994
|
+
attachments: [],
|
|
1995
|
+
...init.inReplyTo === void 0 ? {} : { inReplyTo: init.inReplyTo },
|
|
1996
|
+
...init.references === void 0 ? {} : { references: init.references }
|
|
1997
|
+
});
|
|
1998
|
+
notice("\u5DF2\u5B58\u4E3A\u8349\u7A3F");
|
|
1999
|
+
refresh();
|
|
2000
|
+
} catch (caught) {
|
|
2001
|
+
setError(errorText5(caught));
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
1589
2004
|
const openDraft = (draft) => {
|
|
1590
2005
|
setCompose({
|
|
1591
2006
|
mode: "new",
|
|
@@ -1606,7 +2021,7 @@ function MailPanel() {
|
|
|
1606
2021
|
notice("\u8349\u7A3F\u5DF2\u5220\u9664");
|
|
1607
2022
|
refresh();
|
|
1608
2023
|
} catch (caught) {
|
|
1609
|
-
setError(
|
|
2024
|
+
setError(errorText5(caught));
|
|
1610
2025
|
}
|
|
1611
2026
|
};
|
|
1612
2027
|
const switchAccount = async (id) => {
|
|
@@ -1617,7 +2032,7 @@ function MailPanel() {
|
|
|
1617
2032
|
if (id === "all") setSource("local");
|
|
1618
2033
|
refresh();
|
|
1619
2034
|
} catch (caught) {
|
|
1620
|
-
setError(
|
|
2035
|
+
setError(errorText5(caught));
|
|
1621
2036
|
}
|
|
1622
2037
|
};
|
|
1623
2038
|
const startBackup = async () => {
|
|
@@ -1628,7 +2043,7 @@ function MailPanel() {
|
|
|
1628
2043
|
setBackup(result.status);
|
|
1629
2044
|
setShowBackup(true);
|
|
1630
2045
|
} catch (caught) {
|
|
1631
|
-
setError(
|
|
2046
|
+
setError(errorText5(caught));
|
|
1632
2047
|
}
|
|
1633
2048
|
};
|
|
1634
2049
|
const stopBackup = async () => {
|
|
@@ -1637,10 +2052,10 @@ function MailPanel() {
|
|
|
1637
2052
|
const result = await api.stopBackup(accountScope);
|
|
1638
2053
|
setBackup(result.status);
|
|
1639
2054
|
} catch (caught) {
|
|
1640
|
-
setError(
|
|
2055
|
+
setError(errorText5(caught));
|
|
1641
2056
|
}
|
|
1642
2057
|
};
|
|
1643
|
-
const folderOptions = (0,
|
|
2058
|
+
const folderOptions = (0, import_react8.useMemo)(
|
|
1644
2059
|
() => folders.map((folder) => ({
|
|
1645
2060
|
value: folder.name,
|
|
1646
2061
|
label: `${folder.label ?? folder.name}${folder.count > 0 ? ` (${String(folder.count)})` : ""}`
|
|
@@ -1648,27 +2063,27 @@ function MailPanel() {
|
|
|
1648
2063
|
[folders]
|
|
1649
2064
|
);
|
|
1650
2065
|
if (state === null) {
|
|
1651
|
-
return (0,
|
|
2066
|
+
return (0, import_react8.createElement)(
|
|
1652
2067
|
"div",
|
|
1653
2068
|
{ className: "mailharbor" },
|
|
1654
|
-
(0,
|
|
2069
|
+
(0, import_react8.createElement)("div", { className: "mailharbor-empty" }, error === "" ? "\u6B63\u5728\u52A0\u8F7D\u90AE\u7BB1\u2026" : error)
|
|
1655
2070
|
);
|
|
1656
2071
|
}
|
|
1657
2072
|
if (accounts.length === 0) {
|
|
1658
|
-
return (0,
|
|
2073
|
+
return (0, import_react8.createElement)(
|
|
1659
2074
|
"div",
|
|
1660
2075
|
{ className: "mailharbor" },
|
|
1661
|
-
(0,
|
|
2076
|
+
(0, import_react8.createElement)(
|
|
1662
2077
|
"div",
|
|
1663
2078
|
{ className: "mailharbor-settings" },
|
|
1664
|
-
(0,
|
|
1665
|
-
(0,
|
|
2079
|
+
(0, import_react8.createElement)("h2", null, "\u6B22\u8FCE\u4F7F\u7528 MailHarbor"),
|
|
2080
|
+
(0, import_react8.createElement)(
|
|
1666
2081
|
"p",
|
|
1667
2082
|
{ className: "muted small" },
|
|
1668
2083
|
"\u628A\u4EFB\u610F IMAP/SMTP \u90AE\u7BB1\u63A5\u8FDB DeepSeek Harness\uFF1A\u672C\u5730\u5907\u4EFD\u3001\u5B9E\u65F6\u8BFB\u4FE1\u3001\u5199\u90AE\u4EF6\u4E0E Agent \u5DE5\u5177\u3002"
|
|
1669
2084
|
),
|
|
1670
|
-
error === "" ? null : (0,
|
|
1671
|
-
(0,
|
|
2085
|
+
error === "" ? null : (0, import_react8.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
2086
|
+
(0, import_react8.createElement)(AddAccountForm, {
|
|
1672
2087
|
onDone: (next) => {
|
|
1673
2088
|
setState(next);
|
|
1674
2089
|
setAccountScope(next.activeAccountId);
|
|
@@ -1682,38 +2097,38 @@ function MailPanel() {
|
|
|
1682
2097
|
const loggedIn = isAll ? accounts.some((item) => item.id === state.activeAccountId) : state.loggedIn;
|
|
1683
2098
|
const needsLogin = source === "server" && !isAll && state.loggedIn === false;
|
|
1684
2099
|
const items = page.items;
|
|
1685
|
-
return (0,
|
|
2100
|
+
return (0, import_react8.createElement)(
|
|
1686
2101
|
"div",
|
|
1687
2102
|
{ className: narrow ? "mailharbor mailharbor-narrow" : "mailharbor" },
|
|
1688
|
-
(0,
|
|
2103
|
+
(0, import_react8.createElement)(
|
|
1689
2104
|
"div",
|
|
1690
2105
|
{ className: "mailharbor-topbar" },
|
|
1691
|
-
(0,
|
|
2106
|
+
(0, import_react8.createElement)(
|
|
1692
2107
|
"div",
|
|
1693
2108
|
{ className: "mailharbor-account" },
|
|
1694
|
-
(0,
|
|
1695
|
-
accounts.length > 1 ? (0,
|
|
2109
|
+
(0, import_react8.createElement)(Avatar, { email: isAll ? "all" : currentAccount?.email ?? "?", size: 26 }),
|
|
2110
|
+
accounts.length > 1 ? (0, import_react8.createElement)(
|
|
1696
2111
|
"select",
|
|
1697
2112
|
{
|
|
1698
2113
|
value: accountScope ?? "",
|
|
1699
2114
|
onChange: (event) => void switchAccount(event.target.value),
|
|
1700
2115
|
"aria-label": "\u8D26\u53F7"
|
|
1701
2116
|
},
|
|
1702
|
-
(0,
|
|
1703
|
-
accounts.map((item) => (0,
|
|
1704
|
-
) : (0,
|
|
1705
|
-
isAll ? (0,
|
|
1706
|
-
!isAll && state.loggedIn === false ? (0,
|
|
2117
|
+
(0, import_react8.createElement)("option", { key: "all", value: "all" }, "\u5168\u90E8\u8D26\u53F7"),
|
|
2118
|
+
accounts.map((item) => (0, import_react8.createElement)("option", { key: item.id, value: item.id }, item.label))
|
|
2119
|
+
) : (0, import_react8.createElement)("span", { className: "name" }, currentAccount?.label ?? ""),
|
|
2120
|
+
isAll ? (0, import_react8.createElement)("span", { className: "badge" }, `${String(accounts.length)} \u4E2A\u8D26\u53F7`) : null,
|
|
2121
|
+
!isAll && state.loggedIn === false ? (0, import_react8.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null
|
|
1707
2122
|
),
|
|
1708
|
-
(0,
|
|
2123
|
+
(0, import_react8.createElement)(
|
|
1709
2124
|
"div",
|
|
1710
2125
|
{ className: "mailharbor-segment", role: "tablist" },
|
|
1711
|
-
(0,
|
|
2126
|
+
(0, import_react8.createElement)(
|
|
1712
2127
|
"button",
|
|
1713
2128
|
{ className: source === "local" ? "active" : "", onClick: () => setSource("local"), role: "tab" },
|
|
1714
2129
|
"\u672C\u5730\u5907\u4EFD"
|
|
1715
2130
|
),
|
|
1716
|
-
(0,
|
|
2131
|
+
(0, import_react8.createElement)(
|
|
1717
2132
|
"button",
|
|
1718
2133
|
{
|
|
1719
2134
|
className: source === "server" ? "active" : "",
|
|
@@ -1730,8 +2145,8 @@ function MailPanel() {
|
|
|
1730
2145
|
"\u670D\u52A1\u5668\u5B9E\u65F6"
|
|
1731
2146
|
)
|
|
1732
2147
|
),
|
|
1733
|
-
(0,
|
|
1734
|
-
(0,
|
|
2148
|
+
(0, import_react8.createElement)("span", { className: "spacer" }),
|
|
2149
|
+
(0, import_react8.createElement)(
|
|
1735
2150
|
"button",
|
|
1736
2151
|
{
|
|
1737
2152
|
className: "ghost icon",
|
|
@@ -1739,46 +2154,56 @@ function MailPanel() {
|
|
|
1739
2154
|
"aria-label": "\u6DFB\u52A0\u8D26\u53F7",
|
|
1740
2155
|
onClick: () => setShowAdd(true)
|
|
1741
2156
|
},
|
|
1742
|
-
(0,
|
|
2157
|
+
(0, import_react8.createElement)(Icon, { name: "plus" })
|
|
1743
2158
|
),
|
|
1744
|
-
(0,
|
|
2159
|
+
(0, import_react8.createElement)(
|
|
1745
2160
|
"button",
|
|
1746
2161
|
{ className: "ghost icon", title: "\u5237\u65B0\u5217\u8868", "aria-label": "\u5237\u65B0\u5217\u8868", onClick: refresh },
|
|
1747
|
-
(0,
|
|
2162
|
+
(0, import_react8.createElement)(Icon, { name: "refresh" })
|
|
1748
2163
|
),
|
|
1749
|
-
(0,
|
|
2164
|
+
(0, import_react8.createElement)(
|
|
1750
2165
|
"button",
|
|
1751
2166
|
{
|
|
1752
2167
|
className: "ghost",
|
|
1753
2168
|
onClick: () => void startBackup(),
|
|
1754
2169
|
title: isAll ? "\u5907\u4EFD\u5F53\u524D\u6D3B\u52A8\u8D26\u53F7" : "\u5907\u4EFD\u8FD9\u4E2A\u8D26\u53F7"
|
|
1755
2170
|
},
|
|
1756
|
-
(0,
|
|
2171
|
+
(0, import_react8.createElement)(Icon, { name: "archive" }),
|
|
1757
2172
|
"\u5907\u4EFD"
|
|
1758
2173
|
),
|
|
1759
|
-
(0,
|
|
2174
|
+
(0, import_react8.createElement)(
|
|
2175
|
+
"button",
|
|
2176
|
+
{
|
|
2177
|
+
className: railOpen ? "ghost active" : "ghost",
|
|
2178
|
+
onClick: () => setRailOpen(!railOpen),
|
|
2179
|
+
title: "AI \u5DE5\u4F5C\u53F0"
|
|
2180
|
+
},
|
|
2181
|
+
(0, import_react8.createElement)(Icon, { name: "sparkles" }),
|
|
2182
|
+
"AI"
|
|
2183
|
+
),
|
|
2184
|
+
(0, import_react8.createElement)(
|
|
1760
2185
|
"button",
|
|
1761
2186
|
{ className: "primary", onClick: () => startCompose("new") },
|
|
1762
|
-
(0,
|
|
2187
|
+
(0, import_react8.createElement)(Icon, { name: "compose" }),
|
|
1763
2188
|
"\u5199\u90AE\u4EF6"
|
|
1764
2189
|
)
|
|
1765
2190
|
),
|
|
1766
|
-
toast === "" ? null : (0,
|
|
1767
|
-
error === "" ? null : (0,
|
|
1768
|
-
needsLogin ? (0,
|
|
2191
|
+
toast === "" ? null : (0, import_react8.createElement)("div", { className: "small", style: { padding: "6px 14px", color: "var(--m-ok)" } }, toast),
|
|
2192
|
+
error === "" ? null : (0, import_react8.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
|
|
2193
|
+
needsLogin ? (0, import_react8.createElement)(
|
|
1769
2194
|
"div",
|
|
1770
2195
|
{ className: "mailharbor-settings" },
|
|
1771
|
-
(0,
|
|
1772
|
-
) : (0,
|
|
2196
|
+
(0, import_react8.createElement)(LoginForm, { account: currentAccount, onDone: refresh })
|
|
2197
|
+
) : (0, import_react8.createElement)(
|
|
1773
2198
|
"div",
|
|
1774
2199
|
{ className: "mailharbor-body", "data-mode": narrow && selected !== null ? "reader" : "list" },
|
|
1775
|
-
(0,
|
|
2200
|
+
(0, import_react8.createElement)(
|
|
1776
2201
|
"div",
|
|
1777
2202
|
{ className: "mailharbor-list" },
|
|
1778
|
-
(0,
|
|
2203
|
+
(0, import_react8.createElement)(
|
|
1779
2204
|
"div",
|
|
1780
2205
|
{ className: "mailharbor-filters" },
|
|
1781
|
-
(0,
|
|
2206
|
+
(0, import_react8.createElement)(
|
|
1782
2207
|
"select",
|
|
1783
2208
|
{
|
|
1784
2209
|
value: mailbox,
|
|
@@ -1789,10 +2214,10 @@ function MailPanel() {
|
|
|
1789
2214
|
"aria-label": "\u90AE\u7BB1\u6587\u4EF6\u5939"
|
|
1790
2215
|
},
|
|
1791
2216
|
folderOptions.map(
|
|
1792
|
-
(option) => (0,
|
|
2217
|
+
(option) => (0, import_react8.createElement)("option", { key: option.value, value: option.value }, option.label)
|
|
1793
2218
|
)
|
|
1794
2219
|
),
|
|
1795
|
-
(0,
|
|
2220
|
+
(0, import_react8.createElement)("input", {
|
|
1796
2221
|
type: "search",
|
|
1797
2222
|
placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : "\u641C\u7D22\u90AE\u4EF6",
|
|
1798
2223
|
"aria-label": "\u641C\u7D22\u90AE\u4EF6",
|
|
@@ -1806,7 +2231,7 @@ function MailPanel() {
|
|
|
1806
2231
|
}
|
|
1807
2232
|
}
|
|
1808
2233
|
}),
|
|
1809
|
-
query2 !== search || search !== "" ? (0,
|
|
2234
|
+
query2 !== search || search !== "" ? (0, import_react8.createElement)(
|
|
1810
2235
|
"button",
|
|
1811
2236
|
{
|
|
1812
2237
|
className: "subtle",
|
|
@@ -1816,22 +2241,22 @@ function MailPanel() {
|
|
|
1816
2241
|
setSearch("");
|
|
1817
2242
|
}
|
|
1818
2243
|
},
|
|
1819
|
-
(0,
|
|
2244
|
+
(0, import_react8.createElement)(Icon, { name: "close", size: 14 })
|
|
1820
2245
|
) : null
|
|
1821
2246
|
),
|
|
1822
|
-
(0,
|
|
2247
|
+
(0, import_react8.createElement)(
|
|
1823
2248
|
"div",
|
|
1824
2249
|
{ className: "mailharbor-rows" },
|
|
1825
|
-
loading && items.length === 0 && drafts.length === 0 ? (0,
|
|
2250
|
+
loading && items.length === 0 && drafts.length === 0 ? (0, import_react8.createElement)("div", { className: "mailharbor-empty" }, "\u6B63\u5728\u8BFB\u53D6\u2026") : showingDrafts ? drafts.length === 0 ? (0, import_react8.createElement)(
|
|
1826
2251
|
"div",
|
|
1827
2252
|
{ className: "mailharbor-empty" },
|
|
1828
|
-
(0,
|
|
1829
|
-
(0,
|
|
1830
|
-
(0,
|
|
2253
|
+
(0, import_react8.createElement)(Icon, { name: "compose", size: 28 }),
|
|
2254
|
+
(0, import_react8.createElement)("div", { className: "title" }, "\u6CA1\u6709\u8349\u7A3F"),
|
|
2255
|
+
(0, import_react8.createElement)("div", { className: "small" }, "\u5199\u4FE1\u65F6\u70B9\u300C\u5B58\u8349\u7A3F\u300D\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002")
|
|
1831
2256
|
) : drafts.filter(
|
|
1832
2257
|
(draft) => search === "" ? true : `${draft.subject} ${draft.to} ${draft.cc}`.toLowerCase().includes(search.toLowerCase())
|
|
1833
2258
|
).map(
|
|
1834
|
-
(draft) => (0,
|
|
2259
|
+
(draft) => (0, import_react8.createElement)(
|
|
1835
2260
|
"div",
|
|
1836
2261
|
{
|
|
1837
2262
|
key: draft.id,
|
|
@@ -1846,21 +2271,21 @@ function MailPanel() {
|
|
|
1846
2271
|
}
|
|
1847
2272
|
}
|
|
1848
2273
|
},
|
|
1849
|
-
(0,
|
|
2274
|
+
(0, import_react8.createElement)(
|
|
1850
2275
|
"span",
|
|
1851
2276
|
{ className: "line-1" },
|
|
1852
|
-
(0,
|
|
2277
|
+
(0, import_react8.createElement)(
|
|
1853
2278
|
"span",
|
|
1854
2279
|
{ className: "from" },
|
|
1855
2280
|
`\u8349\u7A3F${draft.to === "" ? "" : ` \xB7 \u6536\u4EF6\u4EBA ${draft.to}`}`
|
|
1856
2281
|
),
|
|
1857
|
-
isAll ? (0,
|
|
2282
|
+
isAll ? (0, import_react8.createElement)(
|
|
1858
2283
|
"span",
|
|
1859
2284
|
{ className: "badge" },
|
|
1860
2285
|
accounts.find((item) => item.id === draft.accountId)?.label ?? ""
|
|
1861
2286
|
) : null,
|
|
1862
|
-
(0,
|
|
1863
|
-
(0,
|
|
2287
|
+
(0, import_react8.createElement)("span", { className: "date" }, shortDate(draft.updatedAt)),
|
|
2288
|
+
(0, import_react8.createElement)(
|
|
1864
2289
|
"button",
|
|
1865
2290
|
{
|
|
1866
2291
|
className: "subtle",
|
|
@@ -1870,40 +2295,40 @@ function MailPanel() {
|
|
|
1870
2295
|
void removeDraft(draft.id, draft.accountId);
|
|
1871
2296
|
}
|
|
1872
2297
|
},
|
|
1873
|
-
(0,
|
|
2298
|
+
(0, import_react8.createElement)(Icon, { name: "close", size: 13 })
|
|
1874
2299
|
)
|
|
1875
2300
|
),
|
|
1876
|
-
(0,
|
|
2301
|
+
(0, import_react8.createElement)(
|
|
1877
2302
|
"span",
|
|
1878
2303
|
{ className: "line-2" },
|
|
1879
|
-
(0,
|
|
2304
|
+
(0, import_react8.createElement)(
|
|
1880
2305
|
"span",
|
|
1881
2306
|
{ className: "subject" },
|
|
1882
2307
|
draft.subject === "" ? "(\u65E0\u4E3B\u9898)" : draft.subject
|
|
1883
2308
|
)
|
|
1884
2309
|
),
|
|
1885
|
-
draft.html === "" ? null : (0,
|
|
2310
|
+
draft.html === "" ? null : (0, import_react8.createElement)(
|
|
1886
2311
|
"span",
|
|
1887
2312
|
{ className: "snippet" },
|
|
1888
2313
|
draft.html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 120)
|
|
1889
2314
|
)
|
|
1890
2315
|
)
|
|
1891
|
-
) : items.length === 0 ? (0,
|
|
2316
|
+
) : items.length === 0 ? (0, import_react8.createElement)(
|
|
1892
2317
|
"div",
|
|
1893
2318
|
{ className: "mailharbor-empty" },
|
|
1894
|
-
(0,
|
|
1895
|
-
(0,
|
|
2319
|
+
(0, import_react8.createElement)(Icon, { name: "mail", size: 28 }),
|
|
2320
|
+
(0, import_react8.createElement)(
|
|
1896
2321
|
"div",
|
|
1897
2322
|
{ className: "title" },
|
|
1898
2323
|
source === "local" ? "\u672C\u5730\u5907\u4EFD\u91CC\u8FD8\u6CA1\u6709\u90AE\u4EF6" : "\u670D\u52A1\u5668\u4E0A\u6CA1\u6709\u90AE\u4EF6"
|
|
1899
2324
|
),
|
|
1900
|
-
(0,
|
|
2325
|
+
(0, import_react8.createElement)(
|
|
1901
2326
|
"div",
|
|
1902
2327
|
{ className: "small" },
|
|
1903
2328
|
source === "local" ? "\u70B9\u9876\u90E8\u300C\u5907\u4EFD\u300D\u628A\u670D\u52A1\u5668\u90AE\u4EF6\u540C\u6B65\u4E0B\u6765\u3002" : "\u6362\u4E00\u4E2A\u6587\u4EF6\u5939\u6216\u7A0D\u540E\u5237\u65B0\u3002"
|
|
1904
2329
|
)
|
|
1905
2330
|
) : items.map(
|
|
1906
|
-
(item) => (0,
|
|
2331
|
+
(item) => (0, import_react8.createElement)(
|
|
1907
2332
|
"div",
|
|
1908
2333
|
{
|
|
1909
2334
|
key: `${item.accountId}:${item.id}`,
|
|
@@ -1918,122 +2343,123 @@ function MailPanel() {
|
|
|
1918
2343
|
}
|
|
1919
2344
|
}
|
|
1920
2345
|
},
|
|
1921
|
-
(0,
|
|
2346
|
+
(0, import_react8.createElement)(
|
|
1922
2347
|
"span",
|
|
1923
2348
|
{ className: "line-1" },
|
|
1924
|
-
item.unread ? (0,
|
|
1925
|
-
(0,
|
|
2349
|
+
item.unread ? (0, import_react8.createElement)("span", { className: "dot" }) : null,
|
|
2350
|
+
(0, import_react8.createElement)(
|
|
1926
2351
|
"span",
|
|
1927
2352
|
{ className: "from" },
|
|
1928
2353
|
item.from.name !== "" ? item.from.name : item.from.address
|
|
1929
2354
|
),
|
|
1930
|
-
item.hasAttachments ? (0,
|
|
1931
|
-
isAll ? (0,
|
|
2355
|
+
item.hasAttachments ? (0, import_react8.createElement)(Icon, { name: "paperclip", size: 13 }) : null,
|
|
2356
|
+
isAll ? (0, import_react8.createElement)(
|
|
1932
2357
|
"span",
|
|
1933
2358
|
{ className: "badge" },
|
|
1934
2359
|
accounts.find((entry) => entry.id === item.accountId)?.label ?? ""
|
|
1935
2360
|
) : null,
|
|
1936
|
-
item.backedUp === false ? (0,
|
|
1937
|
-
(0,
|
|
2361
|
+
item.backedUp === false ? (0, import_react8.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
|
|
2362
|
+
(0, import_react8.createElement)("span", { className: "date" }, shortDate(item.date))
|
|
1938
2363
|
),
|
|
1939
|
-
(0,
|
|
2364
|
+
(0, import_react8.createElement)(
|
|
1940
2365
|
"span",
|
|
1941
2366
|
{ className: "line-2" },
|
|
1942
|
-
(0,
|
|
2367
|
+
(0, import_react8.createElement)("span", { className: "subject" }, item.subject)
|
|
1943
2368
|
),
|
|
1944
|
-
item.snippet === "" ? null : (0,
|
|
2369
|
+
item.snippet === "" ? null : (0, import_react8.createElement)("span", { className: "snippet" }, item.snippet)
|
|
1945
2370
|
)
|
|
1946
2371
|
)
|
|
1947
2372
|
),
|
|
1948
|
-
!showingDrafts && items.length > 0 ? (0,
|
|
2373
|
+
!showingDrafts && items.length > 0 ? (0, import_react8.createElement)(
|
|
1949
2374
|
"div",
|
|
1950
2375
|
{ className: "mailharbor-list-foot" },
|
|
1951
|
-
(0,
|
|
1952
|
-
(0,
|
|
1953
|
-
page.total > items.length ? (0,
|
|
2376
|
+
(0, import_react8.createElement)("span", null, `${String(items.length)} / ${String(page.total)} \u5C01`),
|
|
2377
|
+
(0, import_react8.createElement)("span", { className: "spacer" }),
|
|
2378
|
+
page.total > items.length ? (0, import_react8.createElement)(
|
|
1954
2379
|
"button",
|
|
1955
2380
|
{ className: "subtle", onClick: () => setLimit((current) => current + 40) },
|
|
1956
2381
|
"\u52A0\u8F7D\u66F4\u591A"
|
|
1957
2382
|
) : null
|
|
1958
|
-
) : showingDrafts && drafts.length > 0 ? (0,
|
|
2383
|
+
) : showingDrafts && drafts.length > 0 ? (0, import_react8.createElement)(
|
|
1959
2384
|
"div",
|
|
1960
2385
|
{ className: "mailharbor-list-foot" },
|
|
1961
|
-
(0,
|
|
2386
|
+
(0, import_react8.createElement)("span", null, `${String(drafts.length)} \u5C01\u8349\u7A3F`)
|
|
1962
2387
|
) : null
|
|
1963
2388
|
),
|
|
1964
|
-
(0,
|
|
2389
|
+
(0, import_react8.createElement)(
|
|
1965
2390
|
"div",
|
|
1966
2391
|
{ className: "mailharbor-reader" },
|
|
1967
|
-
selected === null ? (0,
|
|
2392
|
+
selected === null ? (0, import_react8.createElement)(
|
|
1968
2393
|
"div",
|
|
1969
2394
|
{ className: "mailharbor-empty" },
|
|
1970
|
-
(0,
|
|
1971
|
-
(0,
|
|
1972
|
-
(0,
|
|
2395
|
+
(0, import_react8.createElement)(Icon, { name: "mail", size: 34 }),
|
|
2396
|
+
(0, import_react8.createElement)("div", { className: "title" }, "\u9009\u62E9\u4E00\u5C01\u90AE\u4EF6\u5F00\u59CB\u9605\u8BFB"),
|
|
2397
|
+
(0, import_react8.createElement)(
|
|
1973
2398
|
"div",
|
|
1974
2399
|
{ className: "small" },
|
|
1975
2400
|
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"
|
|
1976
2401
|
)
|
|
1977
|
-
) : (0,
|
|
2402
|
+
) : (0, import_react8.createElement)(Reader, {
|
|
1978
2403
|
detail: selected,
|
|
1979
2404
|
source,
|
|
1980
2405
|
mailbox,
|
|
1981
2406
|
onChanged: (patch) => setSelected({ ...selected, ...patch }),
|
|
1982
2407
|
onReply: (mode) => startCompose(mode),
|
|
1983
|
-
|
|
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
|
-
}),
|
|
2408
|
+
onAiAction: requestAi,
|
|
1994
2409
|
onClose: narrow ? () => setSelected(null) : void 0
|
|
1995
2410
|
})
|
|
1996
|
-
)
|
|
2411
|
+
),
|
|
2412
|
+
railOpen ? (0, import_react8.createElement)(AiRail, {
|
|
2413
|
+
account: accounts.find((item) => item.id === (selected?.accountId ?? accountScope)) ?? currentAccount,
|
|
2414
|
+
detail: selected,
|
|
2415
|
+
source,
|
|
2416
|
+
mailbox,
|
|
2417
|
+
request: aiRequest,
|
|
2418
|
+
onClose: () => setRailOpen(false),
|
|
2419
|
+
onInsert: insertReply,
|
|
2420
|
+
onSaveDraft: (init) => void saveReplyDraft(init),
|
|
2421
|
+
onNotice: notice
|
|
2422
|
+
}) : null
|
|
1997
2423
|
),
|
|
1998
|
-
showBackup && backup !== null ? (0,
|
|
2424
|
+
showBackup && backup !== null ? (0, import_react8.createElement)(
|
|
1999
2425
|
Modal,
|
|
2000
2426
|
{
|
|
2001
2427
|
title: "\u5907\u4EFD",
|
|
2002
2428
|
onClose: () => setShowBackup(false),
|
|
2003
|
-
footer: (0,
|
|
2429
|
+
footer: (0, import_react8.createElement)(
|
|
2004
2430
|
"div",
|
|
2005
2431
|
{ style: { display: "flex", gap: "8px", width: "100%", alignItems: "center" } },
|
|
2006
|
-
(0,
|
|
2007
|
-
(0,
|
|
2008
|
-
backup.running ? (0,
|
|
2009
|
-
(0,
|
|
2432
|
+
(0, import_react8.createElement)("span", { className: "small muted" }, backup.running ? "\u5907\u4EFD\u8FDB\u884C\u4E2D\u2026" : backup.message),
|
|
2433
|
+
(0, import_react8.createElement)("span", { className: "spacer" }),
|
|
2434
|
+
backup.running ? (0, import_react8.createElement)("button", { className: "ghost danger", onClick: () => void stopBackup() }, "\u505C\u6B62\u5907\u4EFD") : (0, import_react8.createElement)("button", { className: "primary", onClick: () => void startBackup() }, "\u5F00\u59CB\u5907\u4EFD"),
|
|
2435
|
+
(0, import_react8.createElement)("button", { className: "ghost", onClick: () => setShowBackup(false) }, "\u5173\u95ED")
|
|
2010
2436
|
)
|
|
2011
2437
|
},
|
|
2012
|
-
(0,
|
|
2438
|
+
(0, import_react8.createElement)(
|
|
2013
2439
|
"div",
|
|
2014
2440
|
{ className: "mailharbor-kv" },
|
|
2015
|
-
(0,
|
|
2016
|
-
(0,
|
|
2017
|
-
(0,
|
|
2018
|
-
(0,
|
|
2019
|
-
(0,
|
|
2020
|
-
(0,
|
|
2021
|
-
(0,
|
|
2022
|
-
(0,
|
|
2441
|
+
(0, import_react8.createElement)("dt", null, "\u5F00\u59CB\u65F6\u95F4"),
|
|
2442
|
+
(0, import_react8.createElement)("dd", null, backup.startedAt ?? "\u2014"),
|
|
2443
|
+
(0, import_react8.createElement)("dt", null, "\u4E0A\u6B21\u6210\u529F"),
|
|
2444
|
+
(0, import_react8.createElement)("dd", null, backup.lastSuccessAt ?? "\u2014"),
|
|
2445
|
+
(0, import_react8.createElement)("dt", null, "\u65B0\u589E"),
|
|
2446
|
+
(0, import_react8.createElement)("dd", null, String(backup.added)),
|
|
2447
|
+
(0, import_react8.createElement)("dt", null, "\u603B\u6570"),
|
|
2448
|
+
(0, import_react8.createElement)("dd", null, String(backup.total))
|
|
2023
2449
|
),
|
|
2024
|
-
(0,
|
|
2450
|
+
(0, import_react8.createElement)(
|
|
2025
2451
|
"div",
|
|
2026
2452
|
{ className: "mailharbor-log" },
|
|
2027
2453
|
backup.output.length === 0 ? "\uFF08\u7B49\u5F85\u8F93\u51FA\u2026\uFF09" : backup.output.join("\n")
|
|
2028
2454
|
)
|
|
2029
2455
|
) : null,
|
|
2030
|
-
compose !== null && composeAccount !== null ? (0,
|
|
2456
|
+
compose !== null && composeAccount !== null ? (0, import_react8.createElement)(Composer, {
|
|
2031
2457
|
account: composeAccount,
|
|
2032
2458
|
initial: compose,
|
|
2033
2459
|
onClose: () => setCompose(null),
|
|
2034
2460
|
onSent: (message) => notice(message)
|
|
2035
2461
|
}) : null,
|
|
2036
|
-
showAdd ? (0,
|
|
2462
|
+
showAdd ? (0, import_react8.createElement)(AddAccountModal, {
|
|
2037
2463
|
onClose: () => setShowAdd(false),
|
|
2038
2464
|
onDone: (next) => {
|
|
2039
2465
|
setState(next);
|
|
@@ -2045,32 +2471,32 @@ function MailPanel() {
|
|
|
2045
2471
|
}
|
|
2046
2472
|
|
|
2047
2473
|
// src/client/settings.tsx
|
|
2048
|
-
var
|
|
2049
|
-
function
|
|
2474
|
+
var import_react9 = require("react");
|
|
2475
|
+
function errorText6(error) {
|
|
2050
2476
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
2051
2477
|
}
|
|
2052
2478
|
function MailSettings() {
|
|
2053
|
-
const [state, setState] = (0,
|
|
2054
|
-
const [settings, setSettings] = (0,
|
|
2055
|
-
const [error, setError] = (0,
|
|
2056
|
-
const [toast, setToast] = (0,
|
|
2057
|
-
const [signature, setSignature] = (0,
|
|
2058
|
-
const [markOnOpen, setMarkOnOpen] = (0,
|
|
2059
|
-
const [pollSeconds, setPollSeconds] = (0,
|
|
2060
|
-
const [listLimit, setListLimit] = (0,
|
|
2061
|
-
const [aiEnabled, setAiEnabled] = (0,
|
|
2062
|
-
const [aiProvider, setAiProvider] = (0,
|
|
2063
|
-
const [aiModel, setAiModel] = (0,
|
|
2064
|
-
const [aiModels, setAiModels] = (0,
|
|
2065
|
-
const [aiStatus, setAiStatus] = (0,
|
|
2066
|
-
const [styleText, setStyleText] = (0,
|
|
2067
|
-
const [styleMeta, setStyleMeta] = (0,
|
|
2068
|
-
const [styleBusy, setStyleBusy] = (0,
|
|
2069
|
-
const [editing, setEditing] = (0,
|
|
2070
|
-
const [
|
|
2071
|
-
const [adding, setAdding] = (0,
|
|
2072
|
-
const [tick, setTick] = (0,
|
|
2073
|
-
(0,
|
|
2479
|
+
const [state, setState] = (0, import_react9.useState)(null);
|
|
2480
|
+
const [settings, setSettings] = (0, import_react9.useState)(null);
|
|
2481
|
+
const [error, setError] = (0, import_react9.useState)("");
|
|
2482
|
+
const [toast, setToast] = (0, import_react9.useState)("");
|
|
2483
|
+
const [signature, setSignature] = (0, import_react9.useState)("");
|
|
2484
|
+
const [markOnOpen, setMarkOnOpen] = (0, import_react9.useState)(true);
|
|
2485
|
+
const [pollSeconds, setPollSeconds] = (0, import_react9.useState)(60);
|
|
2486
|
+
const [listLimit, setListLimit] = (0, import_react9.useState)(40);
|
|
2487
|
+
const [aiEnabled, setAiEnabled] = (0, import_react9.useState)(true);
|
|
2488
|
+
const [aiProvider, setAiProvider] = (0, import_react9.useState)("deepseek-account");
|
|
2489
|
+
const [aiModel, setAiModel] = (0, import_react9.useState)("deepseek-flash");
|
|
2490
|
+
const [aiModels, setAiModels] = (0, import_react9.useState)({ providers: [] });
|
|
2491
|
+
const [aiStatus, setAiStatus] = (0, import_react9.useState)(null);
|
|
2492
|
+
const [styleText, setStyleText] = (0, import_react9.useState)("");
|
|
2493
|
+
const [styleMeta, setStyleMeta] = (0, import_react9.useState)({ updatedAt: "", sampleCount: 0 });
|
|
2494
|
+
const [styleBusy, setStyleBusy] = (0, import_react9.useState)(null);
|
|
2495
|
+
const [editing, setEditing] = (0, import_react9.useState)(null);
|
|
2496
|
+
const [checkResults, setCheckResults] = (0, import_react9.useState)({});
|
|
2497
|
+
const [adding, setAdding] = (0, import_react9.useState)(false);
|
|
2498
|
+
const [tick, setTick] = (0, import_react9.useState)(0);
|
|
2499
|
+
(0, import_react9.useEffect)(() => {
|
|
2074
2500
|
let cancelled = false;
|
|
2075
2501
|
Promise.all([api.state(), api.settings()]).then(([appState, view]) => {
|
|
2076
2502
|
if (cancelled) return;
|
|
@@ -2084,13 +2510,13 @@ function MailSettings() {
|
|
|
2084
2510
|
setAiProvider(view.ai.provider);
|
|
2085
2511
|
setAiModel(view.ai.model);
|
|
2086
2512
|
}).catch((caught) => {
|
|
2087
|
-
if (!cancelled) setError(
|
|
2513
|
+
if (!cancelled) setError(errorText6(caught));
|
|
2088
2514
|
});
|
|
2089
2515
|
return () => {
|
|
2090
2516
|
cancelled = true;
|
|
2091
2517
|
};
|
|
2092
2518
|
}, [tick]);
|
|
2093
|
-
(0,
|
|
2519
|
+
(0, import_react9.useEffect)(() => {
|
|
2094
2520
|
let cancelled = false;
|
|
2095
2521
|
const accountId = settings?.activeAccountId ?? state?.activeAccountId ?? null;
|
|
2096
2522
|
if (accountId === null) return;
|
|
@@ -2128,38 +2554,38 @@ function MailSettings() {
|
|
|
2128
2554
|
setSettings(result.settings);
|
|
2129
2555
|
notify("\u8BBE\u7F6E\u5DF2\u4FDD\u5B58");
|
|
2130
2556
|
} catch (caught) {
|
|
2131
|
-
setError(
|
|
2557
|
+
setError(errorText6(caught));
|
|
2132
2558
|
}
|
|
2133
2559
|
};
|
|
2134
2560
|
const account = settings?.accounts.find((item) => item.id === settings.activeAccountId) ?? null;
|
|
2135
2561
|
if (state === null || settings === null) {
|
|
2136
|
-
return (0,
|
|
2562
|
+
return (0, import_react9.createElement)("div", { className: "mailharbor mailharbor-settings" }, error === "" ? "\u6B63\u5728\u52A0\u8F7D\u8BBE\u7F6E\u2026" : error);
|
|
2137
2563
|
}
|
|
2138
|
-
return (0,
|
|
2564
|
+
return (0, import_react9.createElement)(
|
|
2139
2565
|
"div",
|
|
2140
2566
|
{ className: "mailharbor" },
|
|
2141
|
-
(0,
|
|
2567
|
+
(0, import_react9.createElement)(
|
|
2142
2568
|
"div",
|
|
2143
2569
|
{ className: "mailharbor-settings" },
|
|
2144
|
-
(0,
|
|
2145
|
-
(0,
|
|
2146
|
-
toast === "" ? null : (0,
|
|
2147
|
-
error === "" ? null : (0,
|
|
2148
|
-
(0,
|
|
2570
|
+
(0, import_react9.createElement)("h2", null, "MailHarbor"),
|
|
2571
|
+
(0, import_react9.createElement)("p", { className: "small muted" }, "\u591A\u8D26\u53F7 IMAP/SMTP \u90AE\u7BB1\u3001\u672C\u5730\u5907\u4EFD\u3001AI \u4E0E\u4EFB\u52A1\u3002\u8BBE\u7F6E\u4F1A\u7ACB\u5373\u751F\u6548\u3002"),
|
|
2572
|
+
toast === "" ? null : (0, import_react9.createElement)("div", { className: "small", style: { color: "var(--m-ok)" } }, toast),
|
|
2573
|
+
error === "" ? null : (0, import_react9.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
|
|
2574
|
+
(0, import_react9.createElement)(
|
|
2149
2575
|
"div",
|
|
2150
2576
|
{ className: "mailharbor-card" },
|
|
2151
|
-
(0,
|
|
2152
|
-
settings.accounts.length === 0 ? (0,
|
|
2153
|
-
(item) => (0,
|
|
2577
|
+
(0, import_react9.createElement)("h3", null, "\u8D26\u53F7"),
|
|
2578
|
+
settings.accounts.length === 0 ? (0, import_react9.createElement)(AddAccountForm, { onDone: () => reload() }) : settings.accounts.map(
|
|
2579
|
+
(item) => (0, import_react9.createElement)(AccountRow, {
|
|
2154
2580
|
key: item.id,
|
|
2155
2581
|
account: item,
|
|
2156
2582
|
active: item.id === settings.activeAccountId,
|
|
2157
2583
|
loggedIn: item.id === settings.activeAccountId ? state.loggedIn : void 0,
|
|
2158
2584
|
editing: editing === item.id,
|
|
2159
|
-
checkResult:
|
|
2585
|
+
checkResult: checkResults[item.id] ?? "",
|
|
2160
2586
|
onToggleEdit: () => {
|
|
2161
2587
|
setEditing(editing === item.id ? null : item.id);
|
|
2162
|
-
|
|
2588
|
+
setCheckResults((prev) => ({ ...prev, [item.id]: "" }));
|
|
2163
2589
|
},
|
|
2164
2590
|
onActivate: async () => {
|
|
2165
2591
|
await api.activateAccount(item.id);
|
|
@@ -2176,11 +2602,12 @@ function MailSettings() {
|
|
|
2176
2602
|
onCheck: async () => {
|
|
2177
2603
|
try {
|
|
2178
2604
|
const result = await api.check(item.id);
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2605
|
+
setCheckResults((prev) => ({
|
|
2606
|
+
...prev,
|
|
2607
|
+
[item.id]: `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 ?? ""}`}`
|
|
2608
|
+
}));
|
|
2182
2609
|
} catch (caught) {
|
|
2183
|
-
|
|
2610
|
+
setCheckResults((prev) => ({ ...prev, [item.id]: `\u5931\u8D25: ${errorText6(caught)}` }));
|
|
2184
2611
|
}
|
|
2185
2612
|
},
|
|
2186
2613
|
onSave: async (patch) => {
|
|
@@ -2194,32 +2621,32 @@ function MailSettings() {
|
|
|
2194
2621
|
}
|
|
2195
2622
|
})
|
|
2196
2623
|
),
|
|
2197
|
-
adding ? (0,
|
|
2624
|
+
adding ? (0, import_react9.createElement)(AddAccountForm, {
|
|
2198
2625
|
onDone: () => {
|
|
2199
2626
|
setAdding(false);
|
|
2200
2627
|
reload();
|
|
2201
2628
|
},
|
|
2202
2629
|
onCancel: () => setAdding(false)
|
|
2203
|
-
}) : (0,
|
|
2630
|
+
}) : (0, import_react9.createElement)("button", { className: "ghost", onClick: () => setAdding(true) }, "+ \u6DFB\u52A0\u8D26\u53F7")
|
|
2204
2631
|
),
|
|
2205
|
-
(0,
|
|
2632
|
+
(0, import_react9.createElement)(
|
|
2206
2633
|
"div",
|
|
2207
2634
|
{ className: "mailharbor-card" },
|
|
2208
|
-
(0,
|
|
2209
|
-
(0,
|
|
2635
|
+
(0, import_react9.createElement)("h3", null, "\u5199\u4FE1\u4E0E\u9605\u8BFB"),
|
|
2636
|
+
(0, import_react9.createElement)(
|
|
2210
2637
|
Field,
|
|
2211
2638
|
{ label: "\u7B7E\u540D\uFF08\u7EAF\u6587\u672C\uFF0C\u53D1\u9001\u65F6\u8FFD\u52A0\u5230\u6B63\u6587\u672B\u5C3E\uFF09" },
|
|
2212
|
-
(0,
|
|
2639
|
+
(0, import_react9.createElement)("textarea", {
|
|
2213
2640
|
rows: 4,
|
|
2214
2641
|
value: signature,
|
|
2215
2642
|
onChange: (event) => setSignature(event.target.value)
|
|
2216
2643
|
})
|
|
2217
2644
|
),
|
|
2218
|
-
(0,
|
|
2645
|
+
(0, import_react9.createElement)(
|
|
2219
2646
|
"div",
|
|
2220
2647
|
{ style: { display: "flex", gap: "10px", alignItems: "center", flexWrap: "wrap" } },
|
|
2221
|
-
(0,
|
|
2222
|
-
(0,
|
|
2648
|
+
(0, import_react9.createElement)("button", { className: "ghost", onClick: () => void saveGeneral({ signature }) }, "\u4FDD\u5B58\u7B7E\u540D"),
|
|
2649
|
+
(0, import_react9.createElement)(Toggle, {
|
|
2223
2650
|
checked: markOnOpen,
|
|
2224
2651
|
onChange: (value) => {
|
|
2225
2652
|
setMarkOnOpen(value);
|
|
@@ -2228,13 +2655,13 @@ function MailSettings() {
|
|
|
2228
2655
|
label: "\u6253\u5F00\u90AE\u4EF6\u65F6\u6807\u4E3A\u5DF2\u8BFB"
|
|
2229
2656
|
})
|
|
2230
2657
|
),
|
|
2231
|
-
(0,
|
|
2658
|
+
(0, import_react9.createElement)(
|
|
2232
2659
|
"div",
|
|
2233
2660
|
{ className: "mailharbor-grid" },
|
|
2234
|
-
(0,
|
|
2661
|
+
(0, import_react9.createElement)(
|
|
2235
2662
|
Field,
|
|
2236
2663
|
{ label: "\u5217\u8868\u6BCF\u9875\u6570\u91CF" },
|
|
2237
|
-
(0,
|
|
2664
|
+
(0, import_react9.createElement)("input", {
|
|
2238
2665
|
type: "number",
|
|
2239
2666
|
min: 10,
|
|
2240
2667
|
max: 200,
|
|
@@ -2243,10 +2670,10 @@ function MailSettings() {
|
|
|
2243
2670
|
onBlur: () => void saveGeneral({ render: { listLimit } })
|
|
2244
2671
|
})
|
|
2245
2672
|
),
|
|
2246
|
-
(0,
|
|
2673
|
+
(0, import_react9.createElement)(
|
|
2247
2674
|
Field,
|
|
2248
2675
|
{ label: "\u672A\u8BFB\u89D2\u6807\u8F6E\u8BE2\uFF08\u79D2\uFF09" },
|
|
2249
|
-
(0,
|
|
2676
|
+
(0, import_react9.createElement)("input", {
|
|
2250
2677
|
type: "number",
|
|
2251
2678
|
min: 15,
|
|
2252
2679
|
max: 3600,
|
|
@@ -2257,11 +2684,11 @@ function MailSettings() {
|
|
|
2257
2684
|
)
|
|
2258
2685
|
)
|
|
2259
2686
|
),
|
|
2260
|
-
(0,
|
|
2687
|
+
(0, import_react9.createElement)(
|
|
2261
2688
|
"div",
|
|
2262
2689
|
{ className: "mailharbor-card" },
|
|
2263
|
-
(0,
|
|
2264
|
-
(0,
|
|
2690
|
+
(0, import_react9.createElement)("h3", null, "AI"),
|
|
2691
|
+
(0, import_react9.createElement)(Toggle, {
|
|
2265
2692
|
checked: aiEnabled,
|
|
2266
2693
|
onChange: (value) => {
|
|
2267
2694
|
setAiEnabled(value);
|
|
@@ -2269,22 +2696,22 @@ function MailSettings() {
|
|
|
2269
2696
|
},
|
|
2270
2697
|
label: "\u542F\u7528 AI\uFF08\u6458\u8981\u3001AI \u56DE\u590D\u3001\u7FFB\u8BD1\u3001\u5199\u4F5C\u98CE\u683C\uFF09"
|
|
2271
2698
|
}),
|
|
2272
|
-
aiStatus === null ? null : (0,
|
|
2699
|
+
aiStatus === null ? null : (0, import_react9.createElement)(
|
|
2273
2700
|
"div",
|
|
2274
2701
|
{ className: "small", style: { color: aiStatus.available ? "var(--m-ok)" : "var(--m-warn)" } },
|
|
2275
2702
|
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
2703
|
),
|
|
2277
|
-
(0,
|
|
2704
|
+
(0, import_react9.createElement)(
|
|
2278
2705
|
"div",
|
|
2279
2706
|
{ className: "mailharbor-grid" },
|
|
2280
|
-
(0,
|
|
2707
|
+
(0, import_react9.createElement)(
|
|
2281
2708
|
Field,
|
|
2282
2709
|
{ label: "Provider" },
|
|
2283
|
-
aiModels.providers.length === 0 ? (0,
|
|
2710
|
+
aiModels.providers.length === 0 ? (0, import_react9.createElement)("input", {
|
|
2284
2711
|
value: aiProvider,
|
|
2285
2712
|
onChange: (event) => setAiProvider(event.target.value),
|
|
2286
2713
|
onBlur: () => void saveGeneral({ ai: { provider: aiProvider } })
|
|
2287
|
-
}) : (0,
|
|
2714
|
+
}) : (0, import_react9.createElement)(
|
|
2288
2715
|
"select",
|
|
2289
2716
|
{
|
|
2290
2717
|
value: aiProvider,
|
|
@@ -2302,20 +2729,20 @@ function MailSettings() {
|
|
|
2302
2729
|
}
|
|
2303
2730
|
},
|
|
2304
2731
|
aiModels.providers.map(
|
|
2305
|
-
(item) => (0,
|
|
2732
|
+
(item) => (0, import_react9.createElement)("option", { key: item.provider, value: item.provider }, item.name)
|
|
2306
2733
|
)
|
|
2307
2734
|
)
|
|
2308
2735
|
),
|
|
2309
|
-
(0,
|
|
2736
|
+
(0, import_react9.createElement)(
|
|
2310
2737
|
Field,
|
|
2311
2738
|
{ label: "\u6A21\u578B" },
|
|
2312
2739
|
(() => {
|
|
2313
2740
|
const provider = aiModels.providers.find((item) => item.provider === aiProvider);
|
|
2314
|
-
return provider === void 0 || provider.models.length === 0 ? (0,
|
|
2741
|
+
return provider === void 0 || provider.models.length === 0 ? (0, import_react9.createElement)("input", {
|
|
2315
2742
|
value: aiModel,
|
|
2316
2743
|
onChange: (event) => setAiModel(event.target.value),
|
|
2317
2744
|
onBlur: () => void saveGeneral({ ai: { model: aiModel } })
|
|
2318
|
-
}) : (0,
|
|
2745
|
+
}) : (0, import_react9.createElement)(
|
|
2319
2746
|
"select",
|
|
2320
2747
|
{
|
|
2321
2748
|
value: aiModel,
|
|
@@ -2324,37 +2751,37 @@ function MailSettings() {
|
|
|
2324
2751
|
void saveGeneral({ ai: { model: event.target.value } });
|
|
2325
2752
|
}
|
|
2326
2753
|
},
|
|
2327
|
-
provider.models.map((item) => (0,
|
|
2754
|
+
provider.models.map((item) => (0, import_react9.createElement)("option", { key: item.id, value: item.id }, item.name))
|
|
2328
2755
|
);
|
|
2329
2756
|
})()
|
|
2330
2757
|
)
|
|
2331
2758
|
),
|
|
2332
|
-
(0,
|
|
2333
|
-
(0,
|
|
2759
|
+
(0, import_react9.createElement)("hr", null),
|
|
2760
|
+
(0, import_react9.createElement)(
|
|
2334
2761
|
"div",
|
|
2335
2762
|
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
|
|
2336
|
-
(0,
|
|
2337
|
-
(0,
|
|
2763
|
+
(0, import_react9.createElement)("strong", { style: { fontSize: "12.5px" } }, "\u5199\u4F5C\u98CE\u683C"),
|
|
2764
|
+
(0, import_react9.createElement)(
|
|
2338
2765
|
"span",
|
|
2339
2766
|
{ className: "small muted" },
|
|
2340
2767
|
styleMeta.updatedAt === "" ? "\u8FD8\u6CA1\u6709\u751F\u6210" : `${styleMeta.sampleCount} \u5C01\u5DF2\u53D1\u9001\u90AE\u4EF6 \xB7 ${new Date(styleMeta.updatedAt).toLocaleString()}`
|
|
2341
2768
|
)
|
|
2342
2769
|
),
|
|
2343
|
-
(0,
|
|
2770
|
+
(0, import_react9.createElement)(
|
|
2344
2771
|
"p",
|
|
2345
2772
|
{ className: "small muted" },
|
|
2346
2773
|
"\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
2774
|
),
|
|
2348
|
-
(0,
|
|
2775
|
+
(0, import_react9.createElement)("textarea", {
|
|
2349
2776
|
rows: 6,
|
|
2350
2777
|
value: styleText,
|
|
2351
2778
|
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
2779
|
onChange: (event) => setStyleText(event.target.value)
|
|
2353
2780
|
}),
|
|
2354
|
-
(0,
|
|
2781
|
+
(0, import_react9.createElement)(
|
|
2355
2782
|
"div",
|
|
2356
2783
|
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
2357
|
-
(0,
|
|
2784
|
+
(0, import_react9.createElement)(
|
|
2358
2785
|
"button",
|
|
2359
2786
|
{
|
|
2360
2787
|
className: "ghost",
|
|
@@ -2368,12 +2795,12 @@ function MailSettings() {
|
|
|
2368
2795
|
setStyleText(result.style.profile);
|
|
2369
2796
|
setStyleMeta({ updatedAt: result.style.updatedAt, sampleCount: result.style.sampleCount });
|
|
2370
2797
|
notify("\u5DF2\u6839\u636E\u5DF2\u53D1\u9001\u90AE\u4EF6\u751F\u6210\u5199\u4F5C\u98CE\u683C");
|
|
2371
|
-
}).catch((caught) => setError(
|
|
2798
|
+
}).catch((caught) => setError(errorText6(caught))).finally(() => setStyleBusy(null));
|
|
2372
2799
|
}
|
|
2373
2800
|
},
|
|
2374
2801
|
styleBusy === "learn" ? "\u5B66\u4E60\u4E2D\u2026" : "\u4ECE\u5DF2\u53D1\u9001\u90AE\u4EF6\u5B66\u4E60"
|
|
2375
2802
|
),
|
|
2376
|
-
(0,
|
|
2803
|
+
(0, import_react9.createElement)(
|
|
2377
2804
|
"button",
|
|
2378
2805
|
{
|
|
2379
2806
|
className: "primary",
|
|
@@ -2386,42 +2813,42 @@ function MailSettings() {
|
|
|
2386
2813
|
api.aiSaveStyle(accountId, styleText).then((result) => {
|
|
2387
2814
|
setStyleMeta({ updatedAt: result.style.updatedAt, sampleCount: result.style.sampleCount });
|
|
2388
2815
|
notify("\u5199\u4F5C\u98CE\u683C\u5DF2\u4FDD\u5B58");
|
|
2389
|
-
}).catch((caught) => setError(
|
|
2816
|
+
}).catch((caught) => setError(errorText6(caught))).finally(() => setStyleBusy(null));
|
|
2390
2817
|
}
|
|
2391
2818
|
},
|
|
2392
2819
|
"\u4FDD\u5B58\u98CE\u683C"
|
|
2393
2820
|
)
|
|
2394
2821
|
),
|
|
2395
|
-
(0,
|
|
2822
|
+
(0, import_react9.createElement)(
|
|
2396
2823
|
"p",
|
|
2397
2824
|
{ className: "small muted" },
|
|
2398
2825
|
"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"
|
|
2399
2826
|
)
|
|
2400
2827
|
),
|
|
2401
|
-
(0,
|
|
2828
|
+
(0, import_react9.createElement)(
|
|
2402
2829
|
"div",
|
|
2403
2830
|
{ className: "mailharbor-card" },
|
|
2404
|
-
(0,
|
|
2405
|
-
(0,
|
|
2831
|
+
(0, import_react9.createElement)("h3", null, "\u6570\u636E"),
|
|
2832
|
+
(0, import_react9.createElement)(
|
|
2406
2833
|
"dl",
|
|
2407
2834
|
{ className: "mailharbor-kv" },
|
|
2408
|
-
(0,
|
|
2409
|
-
(0,
|
|
2410
|
-
(0,
|
|
2411
|
-
(0,
|
|
2412
|
-
(0,
|
|
2413
|
-
(0,
|
|
2835
|
+
(0, import_react9.createElement)("dt", null, "\u6570\u636E\u76EE\u5F55"),
|
|
2836
|
+
(0, import_react9.createElement)("dd", null, settings.dataDir),
|
|
2837
|
+
(0, import_react9.createElement)("dt", null, "\u914D\u7F6E\u6587\u4EF6"),
|
|
2838
|
+
(0, import_react9.createElement)("dd", null, settings.configFile),
|
|
2839
|
+
(0, import_react9.createElement)("dt", null, "\u5F53\u524D\u8D26\u53F7\u6570\u636E\u5E93"),
|
|
2840
|
+
(0, import_react9.createElement)(
|
|
2414
2841
|
"dd",
|
|
2415
2842
|
null,
|
|
2416
2843
|
account === null ? "\u2014" : `${account.dataDir.startsWith("/") ? account.dataDir : `${settings.dataDir}/${account.dataDir}`}/mail.db`
|
|
2417
2844
|
),
|
|
2418
|
-
(0,
|
|
2419
|
-
(0,
|
|
2845
|
+
(0, import_react9.createElement)("dt", null, "\u63D2\u4EF6\u7248\u672C"),
|
|
2846
|
+
(0, import_react9.createElement)("dd", null, state.version)
|
|
2420
2847
|
),
|
|
2421
|
-
(0,
|
|
2848
|
+
(0, import_react9.createElement)(
|
|
2422
2849
|
"div",
|
|
2423
2850
|
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
2424
|
-
account === null ? null : (0,
|
|
2851
|
+
account === null ? null : (0, import_react9.createElement)(
|
|
2425
2852
|
"button",
|
|
2426
2853
|
{
|
|
2427
2854
|
className: "ghost",
|
|
@@ -2431,7 +2858,7 @@ function MailSettings() {
|
|
|
2431
2858
|
notify(status.status.message === "" ? "\u5907\u4EFD\u5DF2\u5F00\u59CB" : status.status.message);
|
|
2432
2859
|
reload();
|
|
2433
2860
|
} catch (caught) {
|
|
2434
|
-
setError(
|
|
2861
|
+
setError(errorText6(caught));
|
|
2435
2862
|
}
|
|
2436
2863
|
}
|
|
2437
2864
|
},
|
|
@@ -2456,19 +2883,20 @@ function AccountRow({
|
|
|
2456
2883
|
onSave,
|
|
2457
2884
|
onRemove
|
|
2458
2885
|
}) {
|
|
2459
|
-
const [password, setPassword] = (0,
|
|
2460
|
-
const [remember, setRemember] = (0,
|
|
2461
|
-
const [label, setLabel] = (0,
|
|
2462
|
-
const [username, setUsername] = (0,
|
|
2463
|
-
const [imapHost, setImapHost] = (0,
|
|
2464
|
-
const [imapPort, setImapPort] = (0,
|
|
2465
|
-
const [imapSecure, setImapSecure] = (0,
|
|
2466
|
-
const [smtpHost, setSmtpHost] = (0,
|
|
2467
|
-
const [smtpPort, setSmtpPort] = (0,
|
|
2468
|
-
const [smtpSecure, setSmtpSecure] = (0,
|
|
2469
|
-
const [fromName, setFromName] = (0,
|
|
2470
|
-
const [busy, setBusy] = (0,
|
|
2471
|
-
(0,
|
|
2886
|
+
const [password, setPassword] = (0, import_react9.useState)("");
|
|
2887
|
+
const [remember, setRemember] = (0, import_react9.useState)(true);
|
|
2888
|
+
const [label, setLabel] = (0, import_react9.useState)(account.label);
|
|
2889
|
+
const [username, setUsername] = (0, import_react9.useState)(account.username);
|
|
2890
|
+
const [imapHost, setImapHost] = (0, import_react9.useState)(account.imap.host);
|
|
2891
|
+
const [imapPort, setImapPort] = (0, import_react9.useState)(String(account.imap.port));
|
|
2892
|
+
const [imapSecure, setImapSecure] = (0, import_react9.useState)(account.imap.secure);
|
|
2893
|
+
const [smtpHost, setSmtpHost] = (0, import_react9.useState)(account.smtp.host);
|
|
2894
|
+
const [smtpPort, setSmtpPort] = (0, import_react9.useState)(String(account.smtp.port));
|
|
2895
|
+
const [smtpSecure, setSmtpSecure] = (0, import_react9.useState)(account.smtp.secure);
|
|
2896
|
+
const [fromName, setFromName] = (0, import_react9.useState)(account.smtp.fromName);
|
|
2897
|
+
const [busy, setBusy] = (0, import_react9.useState)(false);
|
|
2898
|
+
const [checking, setChecking] = (0, import_react9.useState)(false);
|
|
2899
|
+
(0, import_react9.useEffect)(() => {
|
|
2472
2900
|
setLabel(account.label);
|
|
2473
2901
|
setUsername(account.username);
|
|
2474
2902
|
setImapHost(account.imap.host);
|
|
@@ -2487,103 +2915,122 @@ function AccountRow({
|
|
|
2487
2915
|
setBusy(false);
|
|
2488
2916
|
}
|
|
2489
2917
|
};
|
|
2490
|
-
return (0,
|
|
2918
|
+
return (0, import_react9.createElement)(
|
|
2491
2919
|
"div",
|
|
2492
2920
|
{ className: "mailharbor-card", style: { background: "var(--m-bg-1)" } },
|
|
2493
|
-
(0,
|
|
2921
|
+
(0, import_react9.createElement)(
|
|
2494
2922
|
"div",
|
|
2495
2923
|
{ className: "mailharbor-account-row" },
|
|
2496
|
-
(0,
|
|
2497
|
-
(0,
|
|
2924
|
+
(0, import_react9.createElement)(Avatar, { email: account.email, size: 30 }),
|
|
2925
|
+
(0, import_react9.createElement)(
|
|
2498
2926
|
"div",
|
|
2499
2927
|
{ style: { display: "flex", flexDirection: "column", minWidth: 0 } },
|
|
2500
|
-
(0,
|
|
2501
|
-
(0,
|
|
2928
|
+
(0, import_react9.createElement)("strong", null, account.label),
|
|
2929
|
+
(0, import_react9.createElement)("span", { className: "small muted" }, account.email)
|
|
2502
2930
|
),
|
|
2503
|
-
active ? (0,
|
|
2504
|
-
loggedIn === false ? (0,
|
|
2505
|
-
(0,
|
|
2506
|
-
active ? null : (0,
|
|
2507
|
-
(0,
|
|
2508
|
-
|
|
2931
|
+
active ? (0, import_react9.createElement)("span", { className: "badge ok" }, "\u5F53\u524D\u8D26\u53F7") : null,
|
|
2932
|
+
loggedIn === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null,
|
|
2933
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2934
|
+
active ? null : (0, import_react9.createElement)("button", { className: "ghost small", onClick: () => void wrap(onActivate) }, "\u8BBE\u4E3A\u5F53\u524D"),
|
|
2935
|
+
(0, import_react9.createElement)(
|
|
2936
|
+
"button",
|
|
2937
|
+
{
|
|
2938
|
+
className: "ghost small",
|
|
2939
|
+
disabled: checking,
|
|
2940
|
+
onClick: () => {
|
|
2941
|
+
setChecking(true);
|
|
2942
|
+
void wrap(onCheck).finally(() => setChecking(false));
|
|
2943
|
+
}
|
|
2944
|
+
},
|
|
2945
|
+
checking ? "\u68C0\u67E5\u4E2D\u2026" : "\u68C0\u67E5\u8FDE\u63A5"
|
|
2946
|
+
),
|
|
2947
|
+
(0, import_react9.createElement)("button", { className: "ghost small", onClick: onToggleEdit }, editing ? "\u6536\u8D77" : "\u7F16\u8F91")
|
|
2948
|
+
),
|
|
2949
|
+
checkResult === "" ? null : (0, import_react9.createElement)(
|
|
2950
|
+
"div",
|
|
2951
|
+
{
|
|
2952
|
+
className: "mailharbor-check",
|
|
2953
|
+
style: { color: checkResult.startsWith("\u5931\u8D25") ? "var(--m-danger)" : "var(--m-ok)" }
|
|
2954
|
+
},
|
|
2955
|
+
checkResult
|
|
2509
2956
|
),
|
|
2510
|
-
editing ? (0,
|
|
2957
|
+
editing ? (0, import_react9.createElement)(
|
|
2511
2958
|
"div",
|
|
2512
2959
|
{ style: { display: "flex", flexDirection: "column", gap: "10px" } },
|
|
2513
|
-
(0,
|
|
2960
|
+
(0, import_react9.createElement)(
|
|
2514
2961
|
"div",
|
|
2515
2962
|
{ className: "mailharbor-grid" },
|
|
2516
|
-
(0,
|
|
2963
|
+
(0, import_react9.createElement)(
|
|
2517
2964
|
Field,
|
|
2518
2965
|
{ label: "\u663E\u793A\u540D\u79F0" },
|
|
2519
|
-
(0,
|
|
2966
|
+
(0, import_react9.createElement)("input", {
|
|
2520
2967
|
value: label,
|
|
2521
2968
|
onChange: (event) => setLabel(event.target.value)
|
|
2522
2969
|
})
|
|
2523
2970
|
),
|
|
2524
|
-
(0,
|
|
2971
|
+
(0, import_react9.createElement)(
|
|
2525
2972
|
Field,
|
|
2526
2973
|
{ label: "\u767B\u5F55\u7528\u6237\u540D" },
|
|
2527
|
-
(0,
|
|
2974
|
+
(0, import_react9.createElement)("input", {
|
|
2528
2975
|
value: username,
|
|
2529
2976
|
onChange: (event) => setUsername(event.target.value)
|
|
2530
2977
|
})
|
|
2531
2978
|
)
|
|
2532
2979
|
),
|
|
2533
|
-
(0,
|
|
2980
|
+
(0, import_react9.createElement)(
|
|
2534
2981
|
"div",
|
|
2535
2982
|
{ className: "mailharbor-grid three" },
|
|
2536
|
-
(0,
|
|
2983
|
+
(0, import_react9.createElement)(
|
|
2537
2984
|
Field,
|
|
2538
2985
|
{ label: "IMAP \u4E3B\u673A" },
|
|
2539
|
-
(0,
|
|
2986
|
+
(0, import_react9.createElement)("input", {
|
|
2540
2987
|
value: imapHost,
|
|
2541
2988
|
onChange: (event) => setImapHost(event.target.value)
|
|
2542
2989
|
})
|
|
2543
2990
|
),
|
|
2544
|
-
(0,
|
|
2991
|
+
(0, import_react9.createElement)(
|
|
2545
2992
|
Field,
|
|
2546
2993
|
{ label: "\u7AEF\u53E3" },
|
|
2547
|
-
(0,
|
|
2994
|
+
(0, import_react9.createElement)("input", {
|
|
2548
2995
|
value: imapPort,
|
|
2549
2996
|
onChange: (event) => setImapPort(event.target.value)
|
|
2550
2997
|
})
|
|
2551
2998
|
),
|
|
2552
|
-
(0,
|
|
2999
|
+
(0, import_react9.createElement)(Toggle, { checked: imapSecure, onChange: setImapSecure, label: "TLS" })
|
|
2553
3000
|
),
|
|
2554
|
-
(0,
|
|
3001
|
+
(0, import_react9.createElement)(
|
|
2555
3002
|
"div",
|
|
2556
3003
|
{ className: "mailharbor-grid three" },
|
|
2557
|
-
(0,
|
|
3004
|
+
(0, import_react9.createElement)(
|
|
2558
3005
|
Field,
|
|
2559
3006
|
{ label: "SMTP \u4E3B\u673A" },
|
|
2560
|
-
(0,
|
|
3007
|
+
(0, import_react9.createElement)("input", {
|
|
2561
3008
|
value: smtpHost,
|
|
2562
3009
|
onChange: (event) => setSmtpHost(event.target.value)
|
|
2563
3010
|
})
|
|
2564
3011
|
),
|
|
2565
|
-
(0,
|
|
3012
|
+
(0, import_react9.createElement)(
|
|
2566
3013
|
Field,
|
|
2567
3014
|
{ label: "\u7AEF\u53E3" },
|
|
2568
|
-
(0,
|
|
3015
|
+
(0, import_react9.createElement)("input", {
|
|
2569
3016
|
value: smtpPort,
|
|
2570
3017
|
onChange: (event) => setSmtpPort(event.target.value)
|
|
2571
3018
|
})
|
|
2572
3019
|
),
|
|
2573
|
-
(0,
|
|
3020
|
+
(0, import_react9.createElement)(Toggle, { checked: smtpSecure, onChange: setSmtpSecure, label: "TLS" })
|
|
2574
3021
|
),
|
|
2575
|
-
(0,
|
|
3022
|
+
(0, import_react9.createElement)(
|
|
2576
3023
|
Field,
|
|
2577
3024
|
{ label: "\u53D1\u4EF6\u4EBA\u540D\u79F0" },
|
|
2578
|
-
(0,
|
|
3025
|
+
(0, import_react9.createElement)("input", {
|
|
2579
3026
|
value: fromName,
|
|
2580
3027
|
onChange: (event) => setFromName(event.target.value)
|
|
2581
3028
|
})
|
|
2582
3029
|
),
|
|
2583
|
-
(0,
|
|
3030
|
+
(0, import_react9.createElement)(
|
|
2584
3031
|
"div",
|
|
2585
3032
|
{ style: { display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" } },
|
|
2586
|
-
(0,
|
|
3033
|
+
(0, import_react9.createElement)(
|
|
2587
3034
|
"button",
|
|
2588
3035
|
{
|
|
2589
3036
|
className: "primary small",
|
|
@@ -2599,7 +3046,7 @@ function AccountRow({
|
|
|
2599
3046
|
},
|
|
2600
3047
|
"\u4FDD\u5B58\u8D26\u53F7"
|
|
2601
3048
|
),
|
|
2602
|
-
(0,
|
|
3049
|
+
(0, import_react9.createElement)(
|
|
2603
3050
|
"button",
|
|
2604
3051
|
{
|
|
2605
3052
|
className: "ghost small danger",
|
|
@@ -2608,21 +3055,21 @@ function AccountRow({
|
|
|
2608
3055
|
"\u79FB\u9664\u8D26\u53F7"
|
|
2609
3056
|
)
|
|
2610
3057
|
),
|
|
2611
|
-
(0,
|
|
2612
|
-
(0,
|
|
3058
|
+
(0, import_react9.createElement)("hr", { style: { border: 0, borderTop: "1px solid var(--m-line)", width: "100%" } }),
|
|
3059
|
+
(0, import_react9.createElement)(
|
|
2613
3060
|
Field,
|
|
2614
3061
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801\uFF08\u4E0D\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09" },
|
|
2615
|
-
(0,
|
|
3062
|
+
(0, import_react9.createElement)("input", {
|
|
2616
3063
|
type: "password",
|
|
2617
3064
|
value: password,
|
|
2618
3065
|
onChange: (event) => setPassword(event.target.value)
|
|
2619
3066
|
})
|
|
2620
3067
|
),
|
|
2621
|
-
(0,
|
|
3068
|
+
(0, import_react9.createElement)(
|
|
2622
3069
|
"div",
|
|
2623
3070
|
{ style: { display: "flex", gap: "8px", alignItems: "center" } },
|
|
2624
|
-
(0,
|
|
2625
|
-
(0,
|
|
3071
|
+
(0, import_react9.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801" }),
|
|
3072
|
+
(0, import_react9.createElement)(
|
|
2626
3073
|
"button",
|
|
2627
3074
|
{
|
|
2628
3075
|
className: "primary small",
|
|
@@ -2634,15 +3081,14 @@ function AccountRow({
|
|
|
2634
3081
|
},
|
|
2635
3082
|
"\u767B\u5F55"
|
|
2636
3083
|
),
|
|
2637
|
-
(0,
|
|
2638
|
-
)
|
|
2639
|
-
checkResult === "" ? null : (0, import_react8.createElement)("div", { className: "small muted" }, checkResult)
|
|
3084
|
+
(0, import_react9.createElement)("button", { className: "ghost small", onClick: () => void wrap(onLogout) }, "\u9000\u51FA\u767B\u5F55")
|
|
3085
|
+
)
|
|
2640
3086
|
) : null
|
|
2641
3087
|
);
|
|
2642
3088
|
}
|
|
2643
3089
|
|
|
2644
3090
|
// src/client/index.tsx
|
|
2645
|
-
var VERSION = "0.
|
|
3091
|
+
var VERSION = "0.4.1";
|
|
2646
3092
|
var inject = ["slots"];
|
|
2647
3093
|
var PANEL_ID = "mailharbor";
|
|
2648
3094
|
var STYLE_ID = "mailharbor-style";
|