@zaclinley/dsh-mailharbor 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +7 -4
- package/README.zh.md +7 -4
- package/lib/client.js +1205 -779
- package/lib/client.js.map +4 -4
- package/lib/index.js +327 -15
- 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)(
|
|
889
1452
|
"div",
|
|
890
1453
|
{ className: "mailharbor-confirm" },
|
|
891
|
-
(0,
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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)(
|
|
1470
|
+
"div",
|
|
1471
|
+
{ className: "mailharbor-confirm" },
|
|
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
|
-
}) {
|
|
1037
|
-
const [plain, setPlain] = (0,
|
|
1038
|
-
const [loadImages, setLoadImages] = (0,
|
|
1039
|
-
const [busy, setBusy] = (0,
|
|
1040
|
-
const [error, setError] = (0,
|
|
1041
|
-
const
|
|
1042
|
-
const
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
|
|
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]);
|
|
1052
|
-
const html = detail.html !== null && detail.html !== "";
|
|
1053
|
-
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([]);
|
|
1585
|
+
}) {
|
|
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]);
|
|
1591
|
+
const html = detail.html !== null && detail.html !== "";
|
|
1592
|
+
const toggleRead = async () => {
|
|
1593
|
+
setBusy(true);
|
|
1594
|
+
setError("");
|
|
1076
1595
|
try {
|
|
1077
|
-
const result = await api.
|
|
1078
|
-
accountId: detail.accountId,
|
|
1079
|
-
source,
|
|
1596
|
+
const result = await api.setRead({
|
|
1080
1597
|
id: detail.id,
|
|
1081
|
-
mailbox
|
|
1082
|
-
});
|
|
1083
|
-
setSummaryBlocks(result.blocks);
|
|
1084
|
-
} catch (caught) {
|
|
1085
|
-
setSummaryError(errorText3(caught));
|
|
1086
|
-
} finally {
|
|
1087
|
-
setSummaryBusy(false);
|
|
1088
|
-
}
|
|
1089
|
-
};
|
|
1090
|
-
const openReply = () => {
|
|
1091
|
-
setReplyOpen(true);
|
|
1092
|
-
setReplyText("");
|
|
1093
|
-
setReplyError("");
|
|
1094
|
-
setReplyInstruction("");
|
|
1095
|
-
setReplyLanguage("zh");
|
|
1096
|
-
};
|
|
1097
|
-
const generateReply = async () => {
|
|
1098
|
-
setReplyBusy(true);
|
|
1099
|
-
setReplyError("");
|
|
1100
|
-
try {
|
|
1101
|
-
const result = await api.aiReply({
|
|
1102
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 [checkResult, setCheckResult] = (0,
|
|
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 [checkResult, setCheckResult] = (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,29 +2554,29 @@ 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,
|
|
@@ -2180,7 +2606,7 @@ function MailSettings() {
|
|
|
2180
2606
|
`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 ?? ""}`}`
|
|
2181
2607
|
);
|
|
2182
2608
|
} catch (caught) {
|
|
2183
|
-
setCheckResult(`\u5931\u8D25: ${
|
|
2609
|
+
setCheckResult(`\u5931\u8D25: ${errorText6(caught)}`);
|
|
2184
2610
|
}
|
|
2185
2611
|
},
|
|
2186
2612
|
onSave: async (patch) => {
|
|
@@ -2194,32 +2620,32 @@ function MailSettings() {
|
|
|
2194
2620
|
}
|
|
2195
2621
|
})
|
|
2196
2622
|
),
|
|
2197
|
-
adding ? (0,
|
|
2623
|
+
adding ? (0, import_react9.createElement)(AddAccountForm, {
|
|
2198
2624
|
onDone: () => {
|
|
2199
2625
|
setAdding(false);
|
|
2200
2626
|
reload();
|
|
2201
2627
|
},
|
|
2202
2628
|
onCancel: () => setAdding(false)
|
|
2203
|
-
}) : (0,
|
|
2629
|
+
}) : (0, import_react9.createElement)("button", { className: "ghost", onClick: () => setAdding(true) }, "+ \u6DFB\u52A0\u8D26\u53F7")
|
|
2204
2630
|
),
|
|
2205
|
-
(0,
|
|
2631
|
+
(0, import_react9.createElement)(
|
|
2206
2632
|
"div",
|
|
2207
2633
|
{ className: "mailharbor-card" },
|
|
2208
|
-
(0,
|
|
2209
|
-
(0,
|
|
2634
|
+
(0, import_react9.createElement)("h3", null, "\u5199\u4FE1\u4E0E\u9605\u8BFB"),
|
|
2635
|
+
(0, import_react9.createElement)(
|
|
2210
2636
|
Field,
|
|
2211
2637
|
{ label: "\u7B7E\u540D\uFF08\u7EAF\u6587\u672C\uFF0C\u53D1\u9001\u65F6\u8FFD\u52A0\u5230\u6B63\u6587\u672B\u5C3E\uFF09" },
|
|
2212
|
-
(0,
|
|
2638
|
+
(0, import_react9.createElement)("textarea", {
|
|
2213
2639
|
rows: 4,
|
|
2214
2640
|
value: signature,
|
|
2215
2641
|
onChange: (event) => setSignature(event.target.value)
|
|
2216
2642
|
})
|
|
2217
2643
|
),
|
|
2218
|
-
(0,
|
|
2644
|
+
(0, import_react9.createElement)(
|
|
2219
2645
|
"div",
|
|
2220
2646
|
{ style: { display: "flex", gap: "10px", alignItems: "center", flexWrap: "wrap" } },
|
|
2221
|
-
(0,
|
|
2222
|
-
(0,
|
|
2647
|
+
(0, import_react9.createElement)("button", { className: "ghost", onClick: () => void saveGeneral({ signature }) }, "\u4FDD\u5B58\u7B7E\u540D"),
|
|
2648
|
+
(0, import_react9.createElement)(Toggle, {
|
|
2223
2649
|
checked: markOnOpen,
|
|
2224
2650
|
onChange: (value) => {
|
|
2225
2651
|
setMarkOnOpen(value);
|
|
@@ -2228,13 +2654,13 @@ function MailSettings() {
|
|
|
2228
2654
|
label: "\u6253\u5F00\u90AE\u4EF6\u65F6\u6807\u4E3A\u5DF2\u8BFB"
|
|
2229
2655
|
})
|
|
2230
2656
|
),
|
|
2231
|
-
(0,
|
|
2657
|
+
(0, import_react9.createElement)(
|
|
2232
2658
|
"div",
|
|
2233
2659
|
{ className: "mailharbor-grid" },
|
|
2234
|
-
(0,
|
|
2660
|
+
(0, import_react9.createElement)(
|
|
2235
2661
|
Field,
|
|
2236
2662
|
{ label: "\u5217\u8868\u6BCF\u9875\u6570\u91CF" },
|
|
2237
|
-
(0,
|
|
2663
|
+
(0, import_react9.createElement)("input", {
|
|
2238
2664
|
type: "number",
|
|
2239
2665
|
min: 10,
|
|
2240
2666
|
max: 200,
|
|
@@ -2243,10 +2669,10 @@ function MailSettings() {
|
|
|
2243
2669
|
onBlur: () => void saveGeneral({ render: { listLimit } })
|
|
2244
2670
|
})
|
|
2245
2671
|
),
|
|
2246
|
-
(0,
|
|
2672
|
+
(0, import_react9.createElement)(
|
|
2247
2673
|
Field,
|
|
2248
2674
|
{ label: "\u672A\u8BFB\u89D2\u6807\u8F6E\u8BE2\uFF08\u79D2\uFF09" },
|
|
2249
|
-
(0,
|
|
2675
|
+
(0, import_react9.createElement)("input", {
|
|
2250
2676
|
type: "number",
|
|
2251
2677
|
min: 15,
|
|
2252
2678
|
max: 3600,
|
|
@@ -2257,11 +2683,11 @@ function MailSettings() {
|
|
|
2257
2683
|
)
|
|
2258
2684
|
)
|
|
2259
2685
|
),
|
|
2260
|
-
(0,
|
|
2686
|
+
(0, import_react9.createElement)(
|
|
2261
2687
|
"div",
|
|
2262
2688
|
{ className: "mailharbor-card" },
|
|
2263
|
-
(0,
|
|
2264
|
-
(0,
|
|
2689
|
+
(0, import_react9.createElement)("h3", null, "AI"),
|
|
2690
|
+
(0, import_react9.createElement)(Toggle, {
|
|
2265
2691
|
checked: aiEnabled,
|
|
2266
2692
|
onChange: (value) => {
|
|
2267
2693
|
setAiEnabled(value);
|
|
@@ -2269,22 +2695,22 @@ function MailSettings() {
|
|
|
2269
2695
|
},
|
|
2270
2696
|
label: "\u542F\u7528 AI\uFF08\u6458\u8981\u3001AI \u56DE\u590D\u3001\u7FFB\u8BD1\u3001\u5199\u4F5C\u98CE\u683C\uFF09"
|
|
2271
2697
|
}),
|
|
2272
|
-
aiStatus === null ? null : (0,
|
|
2698
|
+
aiStatus === null ? null : (0, import_react9.createElement)(
|
|
2273
2699
|
"div",
|
|
2274
2700
|
{ className: "small", style: { color: aiStatus.available ? "var(--m-ok)" : "var(--m-warn)" } },
|
|
2275
2701
|
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
2702
|
),
|
|
2277
|
-
(0,
|
|
2703
|
+
(0, import_react9.createElement)(
|
|
2278
2704
|
"div",
|
|
2279
2705
|
{ className: "mailharbor-grid" },
|
|
2280
|
-
(0,
|
|
2706
|
+
(0, import_react9.createElement)(
|
|
2281
2707
|
Field,
|
|
2282
2708
|
{ label: "Provider" },
|
|
2283
|
-
aiModels.providers.length === 0 ? (0,
|
|
2709
|
+
aiModels.providers.length === 0 ? (0, import_react9.createElement)("input", {
|
|
2284
2710
|
value: aiProvider,
|
|
2285
2711
|
onChange: (event) => setAiProvider(event.target.value),
|
|
2286
2712
|
onBlur: () => void saveGeneral({ ai: { provider: aiProvider } })
|
|
2287
|
-
}) : (0,
|
|
2713
|
+
}) : (0, import_react9.createElement)(
|
|
2288
2714
|
"select",
|
|
2289
2715
|
{
|
|
2290
2716
|
value: aiProvider,
|
|
@@ -2302,20 +2728,20 @@ function MailSettings() {
|
|
|
2302
2728
|
}
|
|
2303
2729
|
},
|
|
2304
2730
|
aiModels.providers.map(
|
|
2305
|
-
(item) => (0,
|
|
2731
|
+
(item) => (0, import_react9.createElement)("option", { key: item.provider, value: item.provider }, item.name)
|
|
2306
2732
|
)
|
|
2307
2733
|
)
|
|
2308
2734
|
),
|
|
2309
|
-
(0,
|
|
2735
|
+
(0, import_react9.createElement)(
|
|
2310
2736
|
Field,
|
|
2311
2737
|
{ label: "\u6A21\u578B" },
|
|
2312
2738
|
(() => {
|
|
2313
2739
|
const provider = aiModels.providers.find((item) => item.provider === aiProvider);
|
|
2314
|
-
return provider === void 0 || provider.models.length === 0 ? (0,
|
|
2740
|
+
return provider === void 0 || provider.models.length === 0 ? (0, import_react9.createElement)("input", {
|
|
2315
2741
|
value: aiModel,
|
|
2316
2742
|
onChange: (event) => setAiModel(event.target.value),
|
|
2317
2743
|
onBlur: () => void saveGeneral({ ai: { model: aiModel } })
|
|
2318
|
-
}) : (0,
|
|
2744
|
+
}) : (0, import_react9.createElement)(
|
|
2319
2745
|
"select",
|
|
2320
2746
|
{
|
|
2321
2747
|
value: aiModel,
|
|
@@ -2324,37 +2750,37 @@ function MailSettings() {
|
|
|
2324
2750
|
void saveGeneral({ ai: { model: event.target.value } });
|
|
2325
2751
|
}
|
|
2326
2752
|
},
|
|
2327
|
-
provider.models.map((item) => (0,
|
|
2753
|
+
provider.models.map((item) => (0, import_react9.createElement)("option", { key: item.id, value: item.id }, item.name))
|
|
2328
2754
|
);
|
|
2329
2755
|
})()
|
|
2330
2756
|
)
|
|
2331
2757
|
),
|
|
2332
|
-
(0,
|
|
2333
|
-
(0,
|
|
2758
|
+
(0, import_react9.createElement)("hr", null),
|
|
2759
|
+
(0, import_react9.createElement)(
|
|
2334
2760
|
"div",
|
|
2335
2761
|
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
|
|
2336
|
-
(0,
|
|
2337
|
-
(0,
|
|
2762
|
+
(0, import_react9.createElement)("strong", { style: { fontSize: "12.5px" } }, "\u5199\u4F5C\u98CE\u683C"),
|
|
2763
|
+
(0, import_react9.createElement)(
|
|
2338
2764
|
"span",
|
|
2339
2765
|
{ className: "small muted" },
|
|
2340
2766
|
styleMeta.updatedAt === "" ? "\u8FD8\u6CA1\u6709\u751F\u6210" : `${styleMeta.sampleCount} \u5C01\u5DF2\u53D1\u9001\u90AE\u4EF6 \xB7 ${new Date(styleMeta.updatedAt).toLocaleString()}`
|
|
2341
2767
|
)
|
|
2342
2768
|
),
|
|
2343
|
-
(0,
|
|
2769
|
+
(0, import_react9.createElement)(
|
|
2344
2770
|
"p",
|
|
2345
2771
|
{ className: "small muted" },
|
|
2346
2772
|
"\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
2773
|
),
|
|
2348
|
-
(0,
|
|
2774
|
+
(0, import_react9.createElement)("textarea", {
|
|
2349
2775
|
rows: 6,
|
|
2350
2776
|
value: styleText,
|
|
2351
2777
|
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
2778
|
onChange: (event) => setStyleText(event.target.value)
|
|
2353
2779
|
}),
|
|
2354
|
-
(0,
|
|
2780
|
+
(0, import_react9.createElement)(
|
|
2355
2781
|
"div",
|
|
2356
2782
|
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
2357
|
-
(0,
|
|
2783
|
+
(0, import_react9.createElement)(
|
|
2358
2784
|
"button",
|
|
2359
2785
|
{
|
|
2360
2786
|
className: "ghost",
|
|
@@ -2368,12 +2794,12 @@ function MailSettings() {
|
|
|
2368
2794
|
setStyleText(result.style.profile);
|
|
2369
2795
|
setStyleMeta({ updatedAt: result.style.updatedAt, sampleCount: result.style.sampleCount });
|
|
2370
2796
|
notify("\u5DF2\u6839\u636E\u5DF2\u53D1\u9001\u90AE\u4EF6\u751F\u6210\u5199\u4F5C\u98CE\u683C");
|
|
2371
|
-
}).catch((caught) => setError(
|
|
2797
|
+
}).catch((caught) => setError(errorText6(caught))).finally(() => setStyleBusy(null));
|
|
2372
2798
|
}
|
|
2373
2799
|
},
|
|
2374
2800
|
styleBusy === "learn" ? "\u5B66\u4E60\u4E2D\u2026" : "\u4ECE\u5DF2\u53D1\u9001\u90AE\u4EF6\u5B66\u4E60"
|
|
2375
2801
|
),
|
|
2376
|
-
(0,
|
|
2802
|
+
(0, import_react9.createElement)(
|
|
2377
2803
|
"button",
|
|
2378
2804
|
{
|
|
2379
2805
|
className: "primary",
|
|
@@ -2386,42 +2812,42 @@ function MailSettings() {
|
|
|
2386
2812
|
api.aiSaveStyle(accountId, styleText).then((result) => {
|
|
2387
2813
|
setStyleMeta({ updatedAt: result.style.updatedAt, sampleCount: result.style.sampleCount });
|
|
2388
2814
|
notify("\u5199\u4F5C\u98CE\u683C\u5DF2\u4FDD\u5B58");
|
|
2389
|
-
}).catch((caught) => setError(
|
|
2815
|
+
}).catch((caught) => setError(errorText6(caught))).finally(() => setStyleBusy(null));
|
|
2390
2816
|
}
|
|
2391
2817
|
},
|
|
2392
2818
|
"\u4FDD\u5B58\u98CE\u683C"
|
|
2393
2819
|
)
|
|
2394
2820
|
),
|
|
2395
|
-
(0,
|
|
2821
|
+
(0, import_react9.createElement)(
|
|
2396
2822
|
"p",
|
|
2397
2823
|
{ className: "small muted" },
|
|
2398
2824
|
"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
2825
|
)
|
|
2400
2826
|
),
|
|
2401
|
-
(0,
|
|
2827
|
+
(0, import_react9.createElement)(
|
|
2402
2828
|
"div",
|
|
2403
2829
|
{ className: "mailharbor-card" },
|
|
2404
|
-
(0,
|
|
2405
|
-
(0,
|
|
2830
|
+
(0, import_react9.createElement)("h3", null, "\u6570\u636E"),
|
|
2831
|
+
(0, import_react9.createElement)(
|
|
2406
2832
|
"dl",
|
|
2407
2833
|
{ className: "mailharbor-kv" },
|
|
2408
|
-
(0,
|
|
2409
|
-
(0,
|
|
2410
|
-
(0,
|
|
2411
|
-
(0,
|
|
2412
|
-
(0,
|
|
2413
|
-
(0,
|
|
2834
|
+
(0, import_react9.createElement)("dt", null, "\u6570\u636E\u76EE\u5F55"),
|
|
2835
|
+
(0, import_react9.createElement)("dd", null, settings.dataDir),
|
|
2836
|
+
(0, import_react9.createElement)("dt", null, "\u914D\u7F6E\u6587\u4EF6"),
|
|
2837
|
+
(0, import_react9.createElement)("dd", null, settings.configFile),
|
|
2838
|
+
(0, import_react9.createElement)("dt", null, "\u5F53\u524D\u8D26\u53F7\u6570\u636E\u5E93"),
|
|
2839
|
+
(0, import_react9.createElement)(
|
|
2414
2840
|
"dd",
|
|
2415
2841
|
null,
|
|
2416
2842
|
account === null ? "\u2014" : `${account.dataDir.startsWith("/") ? account.dataDir : `${settings.dataDir}/${account.dataDir}`}/mail.db`
|
|
2417
2843
|
),
|
|
2418
|
-
(0,
|
|
2419
|
-
(0,
|
|
2844
|
+
(0, import_react9.createElement)("dt", null, "\u63D2\u4EF6\u7248\u672C"),
|
|
2845
|
+
(0, import_react9.createElement)("dd", null, state.version)
|
|
2420
2846
|
),
|
|
2421
|
-
(0,
|
|
2847
|
+
(0, import_react9.createElement)(
|
|
2422
2848
|
"div",
|
|
2423
2849
|
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
2424
|
-
account === null ? null : (0,
|
|
2850
|
+
account === null ? null : (0, import_react9.createElement)(
|
|
2425
2851
|
"button",
|
|
2426
2852
|
{
|
|
2427
2853
|
className: "ghost",
|
|
@@ -2431,7 +2857,7 @@ function MailSettings() {
|
|
|
2431
2857
|
notify(status.status.message === "" ? "\u5907\u4EFD\u5DF2\u5F00\u59CB" : status.status.message);
|
|
2432
2858
|
reload();
|
|
2433
2859
|
} catch (caught) {
|
|
2434
|
-
setError(
|
|
2860
|
+
setError(errorText6(caught));
|
|
2435
2861
|
}
|
|
2436
2862
|
}
|
|
2437
2863
|
},
|
|
@@ -2456,19 +2882,19 @@ function AccountRow({
|
|
|
2456
2882
|
onSave,
|
|
2457
2883
|
onRemove
|
|
2458
2884
|
}) {
|
|
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,
|
|
2885
|
+
const [password, setPassword] = (0, import_react9.useState)("");
|
|
2886
|
+
const [remember, setRemember] = (0, import_react9.useState)(true);
|
|
2887
|
+
const [label, setLabel] = (0, import_react9.useState)(account.label);
|
|
2888
|
+
const [username, setUsername] = (0, import_react9.useState)(account.username);
|
|
2889
|
+
const [imapHost, setImapHost] = (0, import_react9.useState)(account.imap.host);
|
|
2890
|
+
const [imapPort, setImapPort] = (0, import_react9.useState)(String(account.imap.port));
|
|
2891
|
+
const [imapSecure, setImapSecure] = (0, import_react9.useState)(account.imap.secure);
|
|
2892
|
+
const [smtpHost, setSmtpHost] = (0, import_react9.useState)(account.smtp.host);
|
|
2893
|
+
const [smtpPort, setSmtpPort] = (0, import_react9.useState)(String(account.smtp.port));
|
|
2894
|
+
const [smtpSecure, setSmtpSecure] = (0, import_react9.useState)(account.smtp.secure);
|
|
2895
|
+
const [fromName, setFromName] = (0, import_react9.useState)(account.smtp.fromName);
|
|
2896
|
+
const [busy, setBusy] = (0, import_react9.useState)(false);
|
|
2897
|
+
(0, import_react9.useEffect)(() => {
|
|
2472
2898
|
setLabel(account.label);
|
|
2473
2899
|
setUsername(account.username);
|
|
2474
2900
|
setImapHost(account.imap.host);
|
|
@@ -2487,103 +2913,103 @@ function AccountRow({
|
|
|
2487
2913
|
setBusy(false);
|
|
2488
2914
|
}
|
|
2489
2915
|
};
|
|
2490
|
-
return (0,
|
|
2916
|
+
return (0, import_react9.createElement)(
|
|
2491
2917
|
"div",
|
|
2492
2918
|
{ className: "mailharbor-card", style: { background: "var(--m-bg-1)" } },
|
|
2493
|
-
(0,
|
|
2919
|
+
(0, import_react9.createElement)(
|
|
2494
2920
|
"div",
|
|
2495
2921
|
{ className: "mailharbor-account-row" },
|
|
2496
|
-
(0,
|
|
2497
|
-
(0,
|
|
2922
|
+
(0, import_react9.createElement)(Avatar, { email: account.email, size: 30 }),
|
|
2923
|
+
(0, import_react9.createElement)(
|
|
2498
2924
|
"div",
|
|
2499
2925
|
{ style: { display: "flex", flexDirection: "column", minWidth: 0 } },
|
|
2500
|
-
(0,
|
|
2501
|
-
(0,
|
|
2926
|
+
(0, import_react9.createElement)("strong", null, account.label),
|
|
2927
|
+
(0, import_react9.createElement)("span", { className: "small muted" }, account.email)
|
|
2502
2928
|
),
|
|
2503
|
-
active ? (0,
|
|
2504
|
-
loggedIn === false ? (0,
|
|
2505
|
-
(0,
|
|
2506
|
-
active ? null : (0,
|
|
2507
|
-
(0,
|
|
2508
|
-
(0,
|
|
2929
|
+
active ? (0, import_react9.createElement)("span", { className: "badge ok" }, "\u5F53\u524D\u8D26\u53F7") : null,
|
|
2930
|
+
loggedIn === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null,
|
|
2931
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2932
|
+
active ? null : (0, import_react9.createElement)("button", { className: "ghost small", onClick: () => void wrap(onActivate) }, "\u8BBE\u4E3A\u5F53\u524D"),
|
|
2933
|
+
(0, import_react9.createElement)("button", { className: "ghost small", onClick: () => void wrap(onCheck) }, "\u68C0\u67E5\u8FDE\u63A5"),
|
|
2934
|
+
(0, import_react9.createElement)("button", { className: "ghost small", onClick: onToggleEdit }, editing ? "\u6536\u8D77" : "\u7F16\u8F91")
|
|
2509
2935
|
),
|
|
2510
|
-
editing ? (0,
|
|
2936
|
+
editing ? (0, import_react9.createElement)(
|
|
2511
2937
|
"div",
|
|
2512
2938
|
{ style: { display: "flex", flexDirection: "column", gap: "10px" } },
|
|
2513
|
-
(0,
|
|
2939
|
+
(0, import_react9.createElement)(
|
|
2514
2940
|
"div",
|
|
2515
2941
|
{ className: "mailharbor-grid" },
|
|
2516
|
-
(0,
|
|
2942
|
+
(0, import_react9.createElement)(
|
|
2517
2943
|
Field,
|
|
2518
2944
|
{ label: "\u663E\u793A\u540D\u79F0" },
|
|
2519
|
-
(0,
|
|
2945
|
+
(0, import_react9.createElement)("input", {
|
|
2520
2946
|
value: label,
|
|
2521
2947
|
onChange: (event) => setLabel(event.target.value)
|
|
2522
2948
|
})
|
|
2523
2949
|
),
|
|
2524
|
-
(0,
|
|
2950
|
+
(0, import_react9.createElement)(
|
|
2525
2951
|
Field,
|
|
2526
2952
|
{ label: "\u767B\u5F55\u7528\u6237\u540D" },
|
|
2527
|
-
(0,
|
|
2953
|
+
(0, import_react9.createElement)("input", {
|
|
2528
2954
|
value: username,
|
|
2529
2955
|
onChange: (event) => setUsername(event.target.value)
|
|
2530
2956
|
})
|
|
2531
2957
|
)
|
|
2532
2958
|
),
|
|
2533
|
-
(0,
|
|
2959
|
+
(0, import_react9.createElement)(
|
|
2534
2960
|
"div",
|
|
2535
2961
|
{ className: "mailharbor-grid three" },
|
|
2536
|
-
(0,
|
|
2962
|
+
(0, import_react9.createElement)(
|
|
2537
2963
|
Field,
|
|
2538
2964
|
{ label: "IMAP \u4E3B\u673A" },
|
|
2539
|
-
(0,
|
|
2965
|
+
(0, import_react9.createElement)("input", {
|
|
2540
2966
|
value: imapHost,
|
|
2541
2967
|
onChange: (event) => setImapHost(event.target.value)
|
|
2542
2968
|
})
|
|
2543
2969
|
),
|
|
2544
|
-
(0,
|
|
2970
|
+
(0, import_react9.createElement)(
|
|
2545
2971
|
Field,
|
|
2546
2972
|
{ label: "\u7AEF\u53E3" },
|
|
2547
|
-
(0,
|
|
2973
|
+
(0, import_react9.createElement)("input", {
|
|
2548
2974
|
value: imapPort,
|
|
2549
2975
|
onChange: (event) => setImapPort(event.target.value)
|
|
2550
2976
|
})
|
|
2551
2977
|
),
|
|
2552
|
-
(0,
|
|
2978
|
+
(0, import_react9.createElement)(Toggle, { checked: imapSecure, onChange: setImapSecure, label: "TLS" })
|
|
2553
2979
|
),
|
|
2554
|
-
(0,
|
|
2980
|
+
(0, import_react9.createElement)(
|
|
2555
2981
|
"div",
|
|
2556
2982
|
{ className: "mailharbor-grid three" },
|
|
2557
|
-
(0,
|
|
2983
|
+
(0, import_react9.createElement)(
|
|
2558
2984
|
Field,
|
|
2559
2985
|
{ label: "SMTP \u4E3B\u673A" },
|
|
2560
|
-
(0,
|
|
2986
|
+
(0, import_react9.createElement)("input", {
|
|
2561
2987
|
value: smtpHost,
|
|
2562
2988
|
onChange: (event) => setSmtpHost(event.target.value)
|
|
2563
2989
|
})
|
|
2564
2990
|
),
|
|
2565
|
-
(0,
|
|
2991
|
+
(0, import_react9.createElement)(
|
|
2566
2992
|
Field,
|
|
2567
2993
|
{ label: "\u7AEF\u53E3" },
|
|
2568
|
-
(0,
|
|
2994
|
+
(0, import_react9.createElement)("input", {
|
|
2569
2995
|
value: smtpPort,
|
|
2570
2996
|
onChange: (event) => setSmtpPort(event.target.value)
|
|
2571
2997
|
})
|
|
2572
2998
|
),
|
|
2573
|
-
(0,
|
|
2999
|
+
(0, import_react9.createElement)(Toggle, { checked: smtpSecure, onChange: setSmtpSecure, label: "TLS" })
|
|
2574
3000
|
),
|
|
2575
|
-
(0,
|
|
3001
|
+
(0, import_react9.createElement)(
|
|
2576
3002
|
Field,
|
|
2577
3003
|
{ label: "\u53D1\u4EF6\u4EBA\u540D\u79F0" },
|
|
2578
|
-
(0,
|
|
3004
|
+
(0, import_react9.createElement)("input", {
|
|
2579
3005
|
value: fromName,
|
|
2580
3006
|
onChange: (event) => setFromName(event.target.value)
|
|
2581
3007
|
})
|
|
2582
3008
|
),
|
|
2583
|
-
(0,
|
|
3009
|
+
(0, import_react9.createElement)(
|
|
2584
3010
|
"div",
|
|
2585
3011
|
{ style: { display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" } },
|
|
2586
|
-
(0,
|
|
3012
|
+
(0, import_react9.createElement)(
|
|
2587
3013
|
"button",
|
|
2588
3014
|
{
|
|
2589
3015
|
className: "primary small",
|
|
@@ -2599,7 +3025,7 @@ function AccountRow({
|
|
|
2599
3025
|
},
|
|
2600
3026
|
"\u4FDD\u5B58\u8D26\u53F7"
|
|
2601
3027
|
),
|
|
2602
|
-
(0,
|
|
3028
|
+
(0, import_react9.createElement)(
|
|
2603
3029
|
"button",
|
|
2604
3030
|
{
|
|
2605
3031
|
className: "ghost small danger",
|
|
@@ -2608,21 +3034,21 @@ function AccountRow({
|
|
|
2608
3034
|
"\u79FB\u9664\u8D26\u53F7"
|
|
2609
3035
|
)
|
|
2610
3036
|
),
|
|
2611
|
-
(0,
|
|
2612
|
-
(0,
|
|
3037
|
+
(0, import_react9.createElement)("hr", { style: { border: 0, borderTop: "1px solid var(--m-line)", width: "100%" } }),
|
|
3038
|
+
(0, import_react9.createElement)(
|
|
2613
3039
|
Field,
|
|
2614
3040
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801\uFF08\u4E0D\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09" },
|
|
2615
|
-
(0,
|
|
3041
|
+
(0, import_react9.createElement)("input", {
|
|
2616
3042
|
type: "password",
|
|
2617
3043
|
value: password,
|
|
2618
3044
|
onChange: (event) => setPassword(event.target.value)
|
|
2619
3045
|
})
|
|
2620
3046
|
),
|
|
2621
|
-
(0,
|
|
3047
|
+
(0, import_react9.createElement)(
|
|
2622
3048
|
"div",
|
|
2623
3049
|
{ style: { display: "flex", gap: "8px", alignItems: "center" } },
|
|
2624
|
-
(0,
|
|
2625
|
-
(0,
|
|
3050
|
+
(0, import_react9.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801" }),
|
|
3051
|
+
(0, import_react9.createElement)(
|
|
2626
3052
|
"button",
|
|
2627
3053
|
{
|
|
2628
3054
|
className: "primary small",
|
|
@@ -2634,15 +3060,15 @@ function AccountRow({
|
|
|
2634
3060
|
},
|
|
2635
3061
|
"\u767B\u5F55"
|
|
2636
3062
|
),
|
|
2637
|
-
(0,
|
|
3063
|
+
(0, import_react9.createElement)("button", { className: "ghost small", onClick: () => void wrap(onLogout) }, "\u9000\u51FA\u767B\u5F55")
|
|
2638
3064
|
),
|
|
2639
|
-
checkResult === "" ? null : (0,
|
|
3065
|
+
checkResult === "" ? null : (0, import_react9.createElement)("div", { className: "small muted" }, checkResult)
|
|
2640
3066
|
) : null
|
|
2641
3067
|
);
|
|
2642
3068
|
}
|
|
2643
3069
|
|
|
2644
3070
|
// src/client/index.tsx
|
|
2645
|
-
var VERSION = "0.
|
|
3071
|
+
var VERSION = "0.4.0";
|
|
2646
3072
|
var inject = ["slots"];
|
|
2647
3073
|
var PANEL_ID = "mailharbor";
|
|
2648
3074
|
var STYLE_ID = "mailharbor-style";
|