@zaclinley/dsh-mailharbor 0.4.0 → 0.5.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 +28 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +2050 -749
- package/lib/client.js.map +4 -4
- package/lib/index.js +1296 -44
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -27,6 +27,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
27
27
|
var index_exports = {};
|
|
28
28
|
__export(index_exports, {
|
|
29
29
|
PANEL_ID: () => PANEL_ID,
|
|
30
|
+
TASKS_PANEL_ID: () => TASKS_PANEL_ID,
|
|
30
31
|
VERSION: () => VERSION,
|
|
31
32
|
apply: () => apply,
|
|
32
33
|
inject: () => inject
|
|
@@ -34,7 +35,7 @@ __export(index_exports, {
|
|
|
34
35
|
module.exports = __toCommonJS(index_exports);
|
|
35
36
|
|
|
36
37
|
// 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 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
|
+
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 background: var(--m-bg);\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 > input,\n.mailharbor-field > select,\n.mailharbor-field > textarea,\n.mailharbor-card > textarea {\n width: 100%;\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: minmax(0, 1fr) minmax(0, 1fr);\n gap: 10px;\n min-width: 0;\n}\n\n.mailharbor-grid.three {\n grid-template-columns: minmax(0, 2fr) minmax(72px, 90px) auto;\n align-items: end;\n min-width: 0;\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/* ---------- selection / extraction ---------- */\n\n.mailharbor-selectbar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n margin: 0 12px 8px;\n border: 1px solid var(--m-accent-soft);\n border-radius: 9px;\n background: var(--m-accent-soft);\n}\n\n.mailharbor-selectbar select {\n height: 28px;\n max-width: 200px;\n}\n\n.mailharbor-row.selectable input[type='checkbox'] {\n width: 15px;\n height: 15px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 9px 10px;\n border: 1px solid var(--m-line-soft);\n border-radius: 9px;\n background: var(--m-bg-1);\n cursor: pointer;\n}\n\n.mailharbor-extract-row input {\n margin-top: 2px;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row > span {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n gap: 6px 12px;\n margin: 0;\n font-size: 12.5px;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n overflow-wrap: anywhere;\n}\n\n/* ---------- tasks panel ---------- */\n\n.mailharbor .chip {\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n.tasks-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 18px;\n padding: 18px 20px 48px;\n width: 100%;\n max-width: 940px;\n margin: 0 auto;\n}\n\n.tasks-overview {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));\n gap: 12px;\n}\n\n.tasks-progress,\n.tasks-project {\n display: flex;\n flex-direction: column;\n gap: 7px;\n padding: 12px 14px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n min-width: 0;\n}\n\n.tasks-progress-head,\n.tasks-project-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-width: 0;\n}\n\n.tasks-project-head .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12.5px;\n font-weight: 500;\n}\n\n.tasks-bar {\n height: 6px;\n border-radius: 999px;\n background: var(--m-bg-3);\n overflow: hidden;\n}\n\n.tasks-bar span {\n display: block;\n height: 100%;\n border-radius: 999px;\n background: var(--m-accent);\n transition: width 200ms ease;\n}\n\n.tasks-bar.small {\n height: 4px;\n}\n\n.tasks-filters {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.tasks-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.tasks-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 9px 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.tasks-row input[type='checkbox'] {\n width: 16px;\n height: 16px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.tasks-row .tasks-title {\n flex: 1;\n min-width: 0;\n height: auto;\n padding: 0;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n text-align: left;\n border: 0;\n background: transparent;\n}\n\n.tasks-row .tasks-title:hover {\n background: transparent;\n}\n\n.tasks-row .tasks-title > span:first-child {\n font-size: 13px;\n color: var(--m-text);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n}\n\n.tasks-row.done .tasks-title > span:first-child {\n color: var(--m-text-3);\n text-decoration: line-through;\n}\n\n.tasks-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-meta .due.overdue {\n color: var(--m-danger);\n}\n\n.tasks-list-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* ---------- markdown ---------- */\n\n.mailharbor-markdown {\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 12.5px;\n line-height: 1.68;\n overflow-wrap: anywhere;\n}\n\n.mailharbor-markdown > *:first-child {\n margin-top: 0;\n}\n\n.mailharbor-markdown > *:last-child {\n margin-bottom: 0;\n}\n\n.mailharbor-markdown p {\n margin: 0;\n white-space: pre-wrap;\n}\n\n.mailharbor-markdown h1,\n.mailharbor-markdown h2,\n.mailharbor-markdown h3,\n.mailharbor-markdown h4,\n.mailharbor-markdown h5,\n.mailharbor-markdown h6 {\n margin: 4px 0 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.mailharbor-markdown ul,\n.mailharbor-markdown ol {\n margin: 0;\n padding-left: 20px;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.mailharbor-markdown li::marker {\n color: var(--m-text-3);\n}\n\n.mailharbor-markdown a {\n color: var(--m-accent);\n text-decoration: none;\n}\n\n.mailharbor-markdown a:hover {\n text-decoration: underline;\n}\n\n.mailharbor-markdown code {\n padding: 1px 4px;\n border-radius: 4px;\n background: var(--m-bg-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n}\n\n.mailharbor-markdown pre {\n margin: 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n overflow: auto;\n}\n\n.mailharbor-markdown pre code {\n padding: 0;\n background: transparent;\n}\n\n.mailharbor-markdown blockquote {\n margin: 0;\n padding-left: 10px;\n border-left: 2px solid var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor-markdown hr {\n width: 100%;\n margin: 4px 0;\n border: 0;\n border-top: 1px solid var(--m-line-soft);\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.mailharbor-settings-page {\n height: auto;\n min-height: 100%;\n overflow: visible;\n}\n\n.mailharbor-settings {\n flex: 0 0 auto;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n padding: 18px 20px 48px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n overflow-x: hidden;\n}\n\n.mailharbor-settings > * {\n min-width: 0;\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 min-width: 0;\n overflow: hidden;\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
39
|
|
|
39
40
|
// src/client/icon.tsx
|
|
40
41
|
var import_react = require("react");
|
|
@@ -161,6 +162,46 @@ var api = {
|
|
|
161
162
|
method: "POST",
|
|
162
163
|
body: JSON.stringify({ accountId: accountId ?? null })
|
|
163
164
|
}),
|
|
165
|
+
tasks: (accountId, params = {}) => request(
|
|
166
|
+
`tasks${query({ accountId: accountId ?? null, ...params })}`
|
|
167
|
+
),
|
|
168
|
+
createTask: (payload) => request("tasks", { method: "POST", body: JSON.stringify(payload) }),
|
|
169
|
+
updateTask: (id, patch) => request(`tasks/${encodeURIComponent(id)}`, {
|
|
170
|
+
method: "PATCH",
|
|
171
|
+
body: JSON.stringify(patch)
|
|
172
|
+
}),
|
|
173
|
+
deleteTask: (id) => request(`tasks/${encodeURIComponent(id)}`, { method: "DELETE" }),
|
|
174
|
+
extractTasks: (payload) => request("tasks/extract", {
|
|
175
|
+
method: "POST",
|
|
176
|
+
body: JSON.stringify(payload)
|
|
177
|
+
}),
|
|
178
|
+
taskCaldav: (accountId) => request(`tasks/caldav${query({ accountId: accountId ?? null })}`),
|
|
179
|
+
saveTaskCaldav: (payload) => request("tasks/caldav", {
|
|
180
|
+
method: "PUT",
|
|
181
|
+
body: JSON.stringify(payload)
|
|
182
|
+
}),
|
|
183
|
+
syncTasks: (accountId) => request("tasks/sync", { method: "POST", body: JSON.stringify({ accountId }) }),
|
|
184
|
+
projects: () => request("projects"),
|
|
185
|
+
projectFor: (accountId, key) => request(`projects/for${query({ accountId: accountId ?? null, key })}`),
|
|
186
|
+
createProject: (payload) => request("projects", {
|
|
187
|
+
method: "POST",
|
|
188
|
+
body: JSON.stringify(payload)
|
|
189
|
+
}),
|
|
190
|
+
addProjectMessage: (id, payload) => request(`projects/${encodeURIComponent(id)}/messages`, {
|
|
191
|
+
method: "POST",
|
|
192
|
+
body: JSON.stringify(payload)
|
|
193
|
+
}),
|
|
194
|
+
removeProjectMessage: (id, accountId, key) => request(
|
|
195
|
+
`projects/${encodeURIComponent(id)}/messages${query({ accountId: accountId ?? null, key })}`,
|
|
196
|
+
{ method: "DELETE" }
|
|
197
|
+
),
|
|
198
|
+
transferProject: (id, payload) => request(`projects/${encodeURIComponent(id)}/transfer`, {
|
|
199
|
+
method: "POST",
|
|
200
|
+
body: JSON.stringify(payload)
|
|
201
|
+
}),
|
|
202
|
+
lookupMessage: (accountId, key) => request(
|
|
203
|
+
`messages/lookup${query({ accountId: accountId ?? null, key })}`
|
|
204
|
+
),
|
|
164
205
|
settings: () => request("settings"),
|
|
165
206
|
saveSettings: (patch) => request("settings", { method: "PATCH", body: JSON.stringify(patch) }),
|
|
166
207
|
backupStatus: (accountId) => request(`backup/status${query({ accountId: accountId ?? null })}`),
|
|
@@ -255,65 +296,218 @@ function MailIcon() {
|
|
|
255
296
|
}
|
|
256
297
|
|
|
257
298
|
// src/client/panel.tsx
|
|
258
|
-
var
|
|
299
|
+
var import_react9 = require("react");
|
|
259
300
|
|
|
260
301
|
// src/client/ai-rail.tsx
|
|
261
|
-
var
|
|
302
|
+
var import_react5 = require("react");
|
|
262
303
|
|
|
263
|
-
// src/client/
|
|
304
|
+
// src/client/components.tsx
|
|
264
305
|
var import_react2 = require("react");
|
|
306
|
+
var import_react_dom = require("react-dom");
|
|
307
|
+
function Avatar({ email, size = 26 }) {
|
|
308
|
+
const letter = (email.trim()[0] ?? "?").toUpperCase();
|
|
309
|
+
return (0, import_react2.createElement)(
|
|
310
|
+
"span",
|
|
311
|
+
{
|
|
312
|
+
className: "avatar",
|
|
313
|
+
style: {
|
|
314
|
+
width: `${String(size)}px`,
|
|
315
|
+
height: `${String(size)}px`,
|
|
316
|
+
fontSize: `${String(Math.round(size * 0.42))}px`
|
|
317
|
+
},
|
|
318
|
+
"aria-hidden": "true"
|
|
319
|
+
},
|
|
320
|
+
letter
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
function Field({
|
|
324
|
+
label,
|
|
325
|
+
children,
|
|
326
|
+
hint
|
|
327
|
+
}) {
|
|
328
|
+
return (0, import_react2.createElement)(
|
|
329
|
+
"div",
|
|
330
|
+
{ className: "mailharbor-field" },
|
|
331
|
+
(0, import_react2.createElement)("label", null, label),
|
|
332
|
+
children,
|
|
333
|
+
hint === void 0 ? null : (0, import_react2.createElement)("span", { className: "small muted" }, hint)
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
function Toggle({
|
|
337
|
+
checked,
|
|
338
|
+
onChange,
|
|
339
|
+
label
|
|
340
|
+
}) {
|
|
341
|
+
return (0, import_react2.createElement)(
|
|
342
|
+
"label",
|
|
343
|
+
{ className: "mailharbor-toggle" },
|
|
344
|
+
(0, import_react2.createElement)("input", {
|
|
345
|
+
type: "checkbox",
|
|
346
|
+
checked,
|
|
347
|
+
onChange: (event) => {
|
|
348
|
+
onChange(event.target.checked);
|
|
349
|
+
}
|
|
350
|
+
}),
|
|
351
|
+
label
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
function portalRoot() {
|
|
355
|
+
const existing = document.getElementById("mailharbor-portal");
|
|
356
|
+
if (existing !== null) return existing;
|
|
357
|
+
const element = document.createElement("div");
|
|
358
|
+
element.id = "mailharbor-portal";
|
|
359
|
+
element.className = "mailharbor mailharbor-portal";
|
|
360
|
+
document.body.append(element);
|
|
361
|
+
return element;
|
|
362
|
+
}
|
|
363
|
+
function Modal({
|
|
364
|
+
title,
|
|
365
|
+
children,
|
|
366
|
+
footer,
|
|
367
|
+
onClose,
|
|
368
|
+
wide = false
|
|
369
|
+
}) {
|
|
370
|
+
(0, import_react2.useEffect)(() => {
|
|
371
|
+
const onKey = (event) => {
|
|
372
|
+
if (event.key === "Escape") onClose();
|
|
373
|
+
};
|
|
374
|
+
window.addEventListener("keydown", onKey);
|
|
375
|
+
return () => {
|
|
376
|
+
window.removeEventListener("keydown", onKey);
|
|
377
|
+
};
|
|
378
|
+
}, [onClose]);
|
|
379
|
+
return (0, import_react_dom.createPortal)(
|
|
380
|
+
(0, import_react2.createElement)(
|
|
381
|
+
"div",
|
|
382
|
+
{
|
|
383
|
+
className: "mailharbor-modal-mask",
|
|
384
|
+
onMouseDown: (event) => {
|
|
385
|
+
if (event.target === event.currentTarget) onClose();
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
(0, import_react2.createElement)(
|
|
389
|
+
"div",
|
|
390
|
+
{
|
|
391
|
+
className: "mailharbor-modal",
|
|
392
|
+
style: wide ? { width: "min(920px, 100%)" } : void 0,
|
|
393
|
+
role: "dialog",
|
|
394
|
+
"aria-modal": "true",
|
|
395
|
+
"aria-label": title
|
|
396
|
+
},
|
|
397
|
+
(0, import_react2.createElement)(
|
|
398
|
+
"header",
|
|
399
|
+
null,
|
|
400
|
+
title,
|
|
401
|
+
(0, import_react2.createElement)("span", { className: "spacer" }),
|
|
402
|
+
(0, import_react2.createElement)("button", { className: "subtle", onClick: onClose, "aria-label": "\u5173\u95ED" }, "\u2715")
|
|
403
|
+
),
|
|
404
|
+
(0, import_react2.createElement)("div", { className: "content" }, children),
|
|
405
|
+
footer === void 0 ? null : (0, import_react2.createElement)("footer", null, footer)
|
|
406
|
+
)
|
|
407
|
+
),
|
|
408
|
+
portalRoot()
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
function ErrorBar({ message, onDismiss }) {
|
|
412
|
+
return (0, import_react2.createElement)(
|
|
413
|
+
"div",
|
|
414
|
+
{ className: "error", role: "alert" },
|
|
415
|
+
(0, import_react2.createElement)("span", null, message),
|
|
416
|
+
(0, import_react2.createElement)("span", { className: "spacer" }),
|
|
417
|
+
(0, import_react2.createElement)("button", { className: "subtle", onClick: onDismiss }, "\u5173\u95ED")
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
function shortDate(iso) {
|
|
421
|
+
const date = new Date(iso);
|
|
422
|
+
if (Number.isNaN(date.getTime())) return iso.slice(0, 16);
|
|
423
|
+
const now = /* @__PURE__ */ new Date();
|
|
424
|
+
const sameDay = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
|
|
425
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
426
|
+
if (sameDay) return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
427
|
+
if (date.getFullYear() === now.getFullYear()) return `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
428
|
+
return `${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
429
|
+
}
|
|
430
|
+
function longDate(iso) {
|
|
431
|
+
const date = new Date(iso);
|
|
432
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
433
|
+
return date.toLocaleString(void 0, {
|
|
434
|
+
year: "numeric",
|
|
435
|
+
month: "2-digit",
|
|
436
|
+
day: "2-digit",
|
|
437
|
+
hour: "2-digit",
|
|
438
|
+
minute: "2-digit"
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function formatBytes(size) {
|
|
442
|
+
if (!Number.isFinite(size) || size <= 0) return "";
|
|
443
|
+
if (size < 1024) return `${String(size)} B`;
|
|
444
|
+
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
|
|
445
|
+
return `${(size / 1024 / 1024).toFixed(1)} MB`;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// src/client/icons.tsx
|
|
449
|
+
var import_react3 = require("react");
|
|
265
450
|
var PATHS = {
|
|
266
|
-
refresh: (0,
|
|
267
|
-
plus: (0,
|
|
268
|
-
compose: (0,
|
|
269
|
-
archive: (0,
|
|
451
|
+
refresh: (0, import_react3.createElement)("path", { d: "M20 11a8 8 0 1 0-2.3 5.7M20 5v6h-6" }),
|
|
452
|
+
plus: (0, import_react3.createElement)("path", { d: "M12 5v14M5 12h14" }),
|
|
453
|
+
compose: (0, import_react3.createElement)("path", { d: "M4 20h4l10.5-10.5a2.1 2.1 0 0 0-3-3L5 17v3zM13.5 6.5l3 3" }),
|
|
454
|
+
archive: (0, import_react3.createElement)(
|
|
270
455
|
"g",
|
|
271
456
|
null,
|
|
272
|
-
(0,
|
|
273
|
-
(0,
|
|
457
|
+
(0, import_react3.createElement)("rect", { x: 3, y: 4, width: 18, height: 5, rx: 1.5 }),
|
|
458
|
+
(0, import_react3.createElement)("path", { d: "M5 9v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V9M10 13h4" })
|
|
274
459
|
),
|
|
275
|
-
paperclip: (0,
|
|
460
|
+
paperclip: (0, import_react3.createElement)("path", {
|
|
276
461
|
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
462
|
}),
|
|
278
|
-
mail: (0,
|
|
463
|
+
mail: (0, import_react3.createElement)(
|
|
279
464
|
"g",
|
|
280
465
|
null,
|
|
281
|
-
(0,
|
|
282
|
-
(0,
|
|
466
|
+
(0, import_react3.createElement)("rect", { x: 3, y: 5, width: 18, height: 14, rx: 2.5 }),
|
|
467
|
+
(0, import_react3.createElement)("path", { d: "m4 7 7.1 5a1.6 1.6 0 0 0 1.8 0L20 7" })
|
|
283
468
|
),
|
|
284
|
-
search: (0,
|
|
285
|
-
reply: (0,
|
|
286
|
-
replyAll: (0,
|
|
287
|
-
forward: (0,
|
|
288
|
-
check: (0,
|
|
289
|
-
image: (0,
|
|
469
|
+
search: (0, import_react3.createElement)("path", { d: "M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14zM20 20l-4-4" }),
|
|
470
|
+
reply: (0, import_react3.createElement)("path", { d: "M9 7 4 12l5 5M4 12h9a6 6 0 0 1 6 6v1" }),
|
|
471
|
+
replyAll: (0, import_react3.createElement)("path", { d: "m9 7-5 5 5 5M14 7l-5 5 5 5M13 12h5a5 5 0 0 1 5 5" }),
|
|
472
|
+
forward: (0, import_react3.createElement)("path", { d: "m15 7 5 5-5 5M20 12h-9a6 6 0 0 0-6 6v1" }),
|
|
473
|
+
check: (0, import_react3.createElement)("path", { d: "m5 13 4 4L19 7" }),
|
|
474
|
+
image: (0, import_react3.createElement)(
|
|
290
475
|
"g",
|
|
291
476
|
null,
|
|
292
|
-
(0,
|
|
293
|
-
(0,
|
|
294
|
-
(0,
|
|
477
|
+
(0, import_react3.createElement)("rect", { x: 3, y: 4, width: 18, height: 16, rx: 2 }),
|
|
478
|
+
(0, import_react3.createElement)("circle", { cx: 8.5, cy: 9.5, r: 1.5 }),
|
|
479
|
+
(0, import_react3.createElement)("path", { d: "m4 18 5-5 4 4 3-3 4 4" })
|
|
295
480
|
),
|
|
296
|
-
close: (0,
|
|
297
|
-
chevronLeft: (0,
|
|
298
|
-
chevronRight: (0,
|
|
299
|
-
sparkles: (0,
|
|
481
|
+
close: (0, import_react3.createElement)("path", { d: "M6 6l12 12M18 6 6 18" }),
|
|
482
|
+
chevronLeft: (0, import_react3.createElement)("path", { d: "m14 6-6 6 6 6" }),
|
|
483
|
+
chevronRight: (0, import_react3.createElement)("path", { d: "m10 6 6 6-6 6" }),
|
|
484
|
+
sparkles: (0, import_react3.createElement)(
|
|
300
485
|
"g",
|
|
301
486
|
null,
|
|
302
|
-
(0,
|
|
303
|
-
(0,
|
|
487
|
+
(0, import_react3.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" }),
|
|
488
|
+
(0, import_react3.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
489
|
),
|
|
305
|
-
trash: (0,
|
|
490
|
+
trash: (0, import_react3.createElement)(
|
|
306
491
|
"g",
|
|
307
492
|
null,
|
|
308
|
-
(0,
|
|
493
|
+
(0, import_react3.createElement)("path", {
|
|
309
494
|
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
495
|
}),
|
|
311
|
-
(0,
|
|
496
|
+
(0, import_react3.createElement)("path", { d: "M10 11v6M14 11v6" })
|
|
312
497
|
),
|
|
313
|
-
send: (0,
|
|
498
|
+
send: (0, import_react3.createElement)("path", { d: "m4 4 17 8-17 8 3.6-8z M7.6 12H21" }),
|
|
499
|
+
folder: (0, import_react3.createElement)("path", {
|
|
500
|
+
d: "M3.5 6.5A1.5 1.5 0 0 1 5 5h4l2 2.5h8A1.5 1.5 0 0 1 20.5 9v8.5A1.5 1.5 0 0 1 19 19H5a1.5 1.5 0 0 1-1.5-1.5z"
|
|
501
|
+
}),
|
|
502
|
+
checklist: (0, import_react3.createElement)(
|
|
503
|
+
"g",
|
|
504
|
+
null,
|
|
505
|
+
(0, import_react3.createElement)("path", { d: "M4 6.5 5.5 8 8 5M4 12.5 5.5 14 8 11M4 18.5 5.5 20 8 17" }),
|
|
506
|
+
(0, import_react3.createElement)("path", { d: "M11 6.5h9M11 12.5h9M11 18.5h9" })
|
|
507
|
+
)
|
|
314
508
|
};
|
|
315
509
|
function Icon({ name, size = 16 }) {
|
|
316
|
-
return (0,
|
|
510
|
+
return (0, import_react3.createElement)(
|
|
317
511
|
"svg",
|
|
318
512
|
{
|
|
319
513
|
width: size,
|
|
@@ -331,6 +525,114 @@ function Icon({ name, size = 16 }) {
|
|
|
331
525
|
);
|
|
332
526
|
}
|
|
333
527
|
|
|
528
|
+
// src/client/markdown.tsx
|
|
529
|
+
var import_react4 = require("react");
|
|
530
|
+
var INLINE = /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)|\*\*([^*]+)\*\*|__([^_]+)__|`([^`]+)`|~~([^~]+)~~|\*([^*]+)\*|_([^_]+)_/g;
|
|
531
|
+
function inline(text, keyPrefix) {
|
|
532
|
+
const nodes = [];
|
|
533
|
+
let last = 0;
|
|
534
|
+
let index = 0;
|
|
535
|
+
INLINE.lastIndex = 0;
|
|
536
|
+
let match;
|
|
537
|
+
while ((match = INLINE.exec(text)) !== null) {
|
|
538
|
+
if (match.index > last) nodes.push(text.slice(last, match.index));
|
|
539
|
+
const key = `${keyPrefix}-${String(index++)}`;
|
|
540
|
+
if (match[1] !== void 0 && match[2] !== void 0) {
|
|
541
|
+
nodes.push((0, import_react4.createElement)("a", { key, href: match[2], target: "_blank", rel: "noreferrer noopener" }, match[1]));
|
|
542
|
+
} else if (match[3] !== void 0) {
|
|
543
|
+
nodes.push((0, import_react4.createElement)("strong", { key }, match[3]));
|
|
544
|
+
} else if (match[4] !== void 0) {
|
|
545
|
+
nodes.push((0, import_react4.createElement)("strong", { key }, match[4]));
|
|
546
|
+
} else if (match[5] !== void 0) {
|
|
547
|
+
nodes.push((0, import_react4.createElement)("code", { key }, match[5]));
|
|
548
|
+
} else if (match[6] !== void 0) {
|
|
549
|
+
nodes.push((0, import_react4.createElement)("del", { key }, match[6]));
|
|
550
|
+
} else if (match[7] !== void 0) {
|
|
551
|
+
nodes.push((0, import_react4.createElement)("em", { key }, match[7]));
|
|
552
|
+
} else if (match[8] !== void 0) {
|
|
553
|
+
nodes.push((0, import_react4.createElement)("em", { key }, match[8]));
|
|
554
|
+
}
|
|
555
|
+
last = INLINE.lastIndex;
|
|
556
|
+
}
|
|
557
|
+
if (last < text.length) nodes.push(text.slice(last));
|
|
558
|
+
return nodes;
|
|
559
|
+
}
|
|
560
|
+
function Markdown({ text }) {
|
|
561
|
+
const lines = text.replace(/\r\n/g, "\n").split("\n");
|
|
562
|
+
const blocks = [];
|
|
563
|
+
let index = 0;
|
|
564
|
+
let key = 0;
|
|
565
|
+
const isBlockStart = (line) => /^```/.test(line) || /^#{1,6}\s+/.test(line) || /^>\s?/.test(line) || /^([-*+]|\d+\.)\s+/.test(line) || /^(-{3,}|\*{3,}|_{3,})$/.test(line);
|
|
566
|
+
while (index < lines.length) {
|
|
567
|
+
const line = lines[index] ?? "";
|
|
568
|
+
const k = `b${String(key++)}`;
|
|
569
|
+
if (line.trim() === "") {
|
|
570
|
+
index += 1;
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
if (/^```/.test(line)) {
|
|
574
|
+
const code = [];
|
|
575
|
+
index += 1;
|
|
576
|
+
while (index < lines.length && !/^```/.test(lines[index] ?? "")) {
|
|
577
|
+
code.push(lines[index] ?? "");
|
|
578
|
+
index += 1;
|
|
579
|
+
}
|
|
580
|
+
index += 1;
|
|
581
|
+
blocks.push((0, import_react4.createElement)("pre", { key: k }, (0, import_react4.createElement)("code", null, code.join("\n"))));
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
if (/^(-{3,}|\*{3,}|_{3,})$/.test(line.trim())) {
|
|
585
|
+
blocks.push((0, import_react4.createElement)("hr", { key: k }));
|
|
586
|
+
index += 1;
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
const heading = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
590
|
+
if (heading !== null) {
|
|
591
|
+
const level = Math.min(6, (heading[1] ?? "#").length);
|
|
592
|
+
blocks.push((0, import_react4.createElement)(`h${String(level)}`, { key: k }, inline(heading[2] ?? "", k)));
|
|
593
|
+
index += 1;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
if (/^>\s?/.test(line)) {
|
|
597
|
+
const quote = [];
|
|
598
|
+
while (index < lines.length && /^>\s?/.test(lines[index] ?? "")) {
|
|
599
|
+
quote.push((lines[index] ?? "").replace(/^>\s?/, ""));
|
|
600
|
+
index += 1;
|
|
601
|
+
}
|
|
602
|
+
blocks.push((0, import_react4.createElement)("blockquote", { key: k }, (0, import_react4.createElement)("p", null, inline(quote.join(" "), k))));
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (/^([-*+]|\d+\.)\s+/.test(line)) {
|
|
606
|
+
const ordered = /^\d+\.\s+/.test(line);
|
|
607
|
+
const items = [];
|
|
608
|
+
while (index < lines.length && /^([-*+]|\d+\.)\s+/.test(lines[index] ?? "")) {
|
|
609
|
+
items.push((lines[index] ?? "").replace(/^([-*+]|\d+\.)\s+/, ""));
|
|
610
|
+
index += 1;
|
|
611
|
+
}
|
|
612
|
+
blocks.push(
|
|
613
|
+
(0, import_react4.createElement)(
|
|
614
|
+
ordered ? "ol" : "ul",
|
|
615
|
+
{ key: k },
|
|
616
|
+
items.map(
|
|
617
|
+
(item, itemIndex) => (0, import_react4.createElement)("li", { key: `${k}-${String(itemIndex)}` }, inline(item, `${k}-${String(itemIndex)}`))
|
|
618
|
+
)
|
|
619
|
+
)
|
|
620
|
+
);
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
const paragraph = [];
|
|
624
|
+
while (index < lines.length) {
|
|
625
|
+
const current = lines[index] ?? "";
|
|
626
|
+
if (current.trim() === "" || paragraph.length > 0 && isBlockStart(current)) break;
|
|
627
|
+
paragraph.push(current);
|
|
628
|
+
index += 1;
|
|
629
|
+
}
|
|
630
|
+
blocks.push((0, import_react4.createElement)("p", { key: k }, inline(paragraph.join(" "), k)));
|
|
631
|
+
}
|
|
632
|
+
if (blocks.length === 0) return null;
|
|
633
|
+
return (0, import_react4.createElement)("div", { className: "mailharbor-markdown" }, blocks);
|
|
634
|
+
}
|
|
635
|
+
|
|
334
636
|
// src/client/ai-rail.tsx
|
|
335
637
|
function errorText(error) {
|
|
336
638
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
@@ -386,17 +688,23 @@ function AiRail({
|
|
|
386
688
|
onSaveDraft,
|
|
387
689
|
onNotice
|
|
388
690
|
}) {
|
|
389
|
-
const [status, setStatus] = (0,
|
|
390
|
-
const [thread, setThread] = (0,
|
|
391
|
-
const [loading, setLoading] = (0,
|
|
392
|
-
const [generating, setGenerating] = (0,
|
|
393
|
-
const [error, setError] = (0,
|
|
394
|
-
const [question, setQuestion] = (0,
|
|
395
|
-
const [confirmClear, setConfirmClear] = (0,
|
|
396
|
-
const
|
|
397
|
-
const
|
|
691
|
+
const [status, setStatus] = (0, import_react5.useState)(null);
|
|
692
|
+
const [thread, setThread] = (0, import_react5.useState)(null);
|
|
693
|
+
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
694
|
+
const [generating, setGenerating] = (0, import_react5.useState)(null);
|
|
695
|
+
const [error, setError] = (0, import_react5.useState)("");
|
|
696
|
+
const [question, setQuestion] = (0, import_react5.useState)("");
|
|
697
|
+
const [confirmClear, setConfirmClear] = (0, import_react5.useState)(false);
|
|
698
|
+
const [project, setProject] = (0, import_react5.useState)(null);
|
|
699
|
+
const [projectOpen, setProjectOpen] = (0, import_react5.useState)(false);
|
|
700
|
+
const [projectName, setProjectName] = (0, import_react5.useState)("");
|
|
701
|
+
const [projectRoot, setProjectRoot] = (0, import_react5.useState)("");
|
|
702
|
+
const [projectBusy, setProjectBusy] = (0, import_react5.useState)(false);
|
|
703
|
+
const [projectWarnings, setProjectWarnings] = (0, import_react5.useState)([]);
|
|
704
|
+
const list = (0, import_react5.useRef)(null);
|
|
705
|
+
const lastNonce = (0, import_react5.useRef)(0);
|
|
398
706
|
const key = detail === null ? "" : threadKey(detail, source);
|
|
399
|
-
(0,
|
|
707
|
+
(0, import_react5.useEffect)(() => {
|
|
400
708
|
if (account === null) return;
|
|
401
709
|
let cancelled = false;
|
|
402
710
|
api.aiStatus(account.id).then((next) => {
|
|
@@ -409,7 +717,7 @@ function AiRail({
|
|
|
409
717
|
cancelled = true;
|
|
410
718
|
};
|
|
411
719
|
}, [account]);
|
|
412
|
-
(0,
|
|
720
|
+
(0, import_react5.useEffect)(() => {
|
|
413
721
|
if (account === null || key === "") {
|
|
414
722
|
setThread(null);
|
|
415
723
|
return;
|
|
@@ -427,7 +735,7 @@ function AiRail({
|
|
|
427
735
|
cancelled = true;
|
|
428
736
|
};
|
|
429
737
|
}, [account, key]);
|
|
430
|
-
const generate = (0,
|
|
738
|
+
const generate = (0, import_react5.useCallback)(
|
|
431
739
|
async (kind, extra = {}) => {
|
|
432
740
|
if (detail === null || account === null) return;
|
|
433
741
|
setGenerating(kind);
|
|
@@ -451,15 +759,85 @@ function AiRail({
|
|
|
451
759
|
},
|
|
452
760
|
[detail, account, key, source, mailbox]
|
|
453
761
|
);
|
|
454
|
-
(0,
|
|
762
|
+
(0, import_react5.useEffect)(() => {
|
|
455
763
|
if (request2 === null || request2.nonce === lastNonce.current) return;
|
|
456
764
|
lastNonce.current = request2.nonce;
|
|
457
765
|
if (detail !== null) void generate(request2.kind);
|
|
458
766
|
}, [request2]);
|
|
459
|
-
(0,
|
|
767
|
+
(0, import_react5.useEffect)(() => {
|
|
460
768
|
const element = list.current;
|
|
461
769
|
if (element !== null) element.scrollTop = element.scrollHeight;
|
|
462
770
|
}, [thread, generating]);
|
|
771
|
+
(0, import_react5.useEffect)(() => {
|
|
772
|
+
if (detail === null || account === null) {
|
|
773
|
+
setProject(null);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
let cancelled = false;
|
|
777
|
+
api.projectFor(detail.accountId, key).then((result) => {
|
|
778
|
+
if (!cancelled) setProject(result.project);
|
|
779
|
+
}).catch(() => {
|
|
780
|
+
if (!cancelled) setProject(null);
|
|
781
|
+
});
|
|
782
|
+
return () => {
|
|
783
|
+
cancelled = true;
|
|
784
|
+
};
|
|
785
|
+
}, [detail, account, key, thread]);
|
|
786
|
+
const openProject = async () => {
|
|
787
|
+
setProjectOpen(true);
|
|
788
|
+
setProjectWarnings([]);
|
|
789
|
+
try {
|
|
790
|
+
const settings = await api.settings();
|
|
791
|
+
setProjectRoot(settings.projects.root);
|
|
792
|
+
setProjectName(detail?.subject.replace(/^(re|fwd):\s*/i, "").slice(0, 80) ?? "");
|
|
793
|
+
} catch {
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
const conversation = () => (thread?.cards ?? []).map(
|
|
797
|
+
(card) => `### ${KIND_LABEL[card.kind] ?? "AI"} \xB7 ${new Date(card.createdAt).toLocaleString()}
|
|
798
|
+
|
|
799
|
+
${card.output}`
|
|
800
|
+
).join("\n\n");
|
|
801
|
+
const transfer = async () => {
|
|
802
|
+
if (project === null || detail === null || account === null) return;
|
|
803
|
+
setProjectBusy(true);
|
|
804
|
+
setProjectWarnings([]);
|
|
805
|
+
try {
|
|
806
|
+
await api.transferProject(project.id, {
|
|
807
|
+
accountId: account.id,
|
|
808
|
+
key,
|
|
809
|
+
conversation: conversation() === "" ? "\uFF08\u6682\u65E0\u65B0\u7684 AI \u8BB0\u5F55\uFF09" : conversation()
|
|
810
|
+
});
|
|
811
|
+
onNotice("\u5DF2\u8FFD\u52A0\u5230\u9879\u76EE\u5BF9\u8BDD");
|
|
812
|
+
setProjectOpen(false);
|
|
813
|
+
} catch (caught) {
|
|
814
|
+
setProjectWarnings([errorText(caught)]);
|
|
815
|
+
} finally {
|
|
816
|
+
setProjectBusy(false);
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
const create = async () => {
|
|
820
|
+
if (detail === null || account === null) return;
|
|
821
|
+
setProjectBusy(true);
|
|
822
|
+
setProjectWarnings([]);
|
|
823
|
+
try {
|
|
824
|
+
const result = await api.createProject({
|
|
825
|
+
accountId: account.id,
|
|
826
|
+
name: projectName.trim() === "" ? detail.subject : projectName.trim(),
|
|
827
|
+
root: projectRoot,
|
|
828
|
+
conversation: conversation() === "" ? "\uFF08\u8FD8\u6CA1\u6709 AI \u8BB0\u5F55\uFF09" : conversation(),
|
|
829
|
+
message: { key, subject: detail.subject, date: detail.date }
|
|
830
|
+
});
|
|
831
|
+
setProject(result.project);
|
|
832
|
+
setProjectWarnings(result.warnings);
|
|
833
|
+
onNotice(`\u9879\u76EE\u5DF2\u521B\u5EFA\uFF1A${result.project.path}`);
|
|
834
|
+
if (result.warnings.length === 0) setProjectOpen(false);
|
|
835
|
+
} catch (caught) {
|
|
836
|
+
setProjectWarnings([errorText(caught)]);
|
|
837
|
+
} finally {
|
|
838
|
+
setProjectBusy(false);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
463
841
|
const replyInit = (card) => {
|
|
464
842
|
const subject = card.meta?.subject ?? (detail === null ? "" : /^re:/i.test(detail.subject) ? detail.subject : `Re: ${detail.subject}`);
|
|
465
843
|
return {
|
|
@@ -502,54 +880,58 @@ function AiRail({
|
|
|
502
880
|
};
|
|
503
881
|
const disabled = status !== null && (!status.available || !status.enabled);
|
|
504
882
|
const cards = thread?.cards ?? [];
|
|
505
|
-
return (0,
|
|
883
|
+
return (0, import_react5.createElement)(
|
|
506
884
|
"aside",
|
|
507
885
|
{ className: "mailharbor-ai", "aria-label": "AI \u52A9\u624B" },
|
|
508
|
-
(0,
|
|
886
|
+
(0, import_react5.createElement)(
|
|
509
887
|
"div",
|
|
510
888
|
{ className: "mailharbor-ai-head" },
|
|
511
|
-
(0,
|
|
512
|
-
(0,
|
|
513
|
-
status === null || status.model === "" ? null : (0,
|
|
514
|
-
(0,
|
|
515
|
-
confirmClear ? (0,
|
|
889
|
+
(0, import_react5.createElement)(Icon, { name: "sparkles", size: 15 }),
|
|
890
|
+
(0, import_react5.createElement)("strong", null, "AI \u52A9\u624B"),
|
|
891
|
+
status === null || status.model === "" ? null : (0, import_react5.createElement)("span", { className: "badge" }, status.model),
|
|
892
|
+
(0, import_react5.createElement)("span", { className: "spacer" }),
|
|
893
|
+
confirmClear ? (0, import_react5.createElement)(
|
|
516
894
|
"span",
|
|
517
895
|
{ className: "mailharbor-ai-confirm" },
|
|
518
|
-
(0,
|
|
519
|
-
(0,
|
|
520
|
-
) : (0,
|
|
896
|
+
(0, import_react5.createElement)("button", { className: "subtle", onClick: () => void clear() }, "\u786E\u8BA4\u6E05\u9664"),
|
|
897
|
+
(0, import_react5.createElement)("button", { className: "subtle", onClick: () => setConfirmClear(false) }, "\u53D6\u6D88")
|
|
898
|
+
) : (0, import_react5.createElement)(
|
|
521
899
|
"button",
|
|
522
900
|
{
|
|
523
901
|
className: "subtle icon",
|
|
524
902
|
title: cards.length === 0 ? "\u5173\u95ED" : "\u6E05\u9664\u8FD9\u5C01\u90AE\u4EF6\u7684\u8BB0\u5F55",
|
|
525
903
|
onClick: () => cards.length === 0 ? onClose() : setConfirmClear(true)
|
|
526
904
|
},
|
|
527
|
-
(0,
|
|
905
|
+
(0, import_react5.createElement)(Icon, { name: cards.length === 0 ? "close" : "trash", size: 14 })
|
|
528
906
|
),
|
|
529
|
-
(0,
|
|
907
|
+
(0, import_react5.createElement)(
|
|
530
908
|
"button",
|
|
531
909
|
{ className: "subtle icon", title: "\u6536\u8D77", onClick: onClose },
|
|
532
|
-
(0,
|
|
910
|
+
(0, import_react5.createElement)(Icon, { name: "chevronRight", size: 14 })
|
|
533
911
|
)
|
|
534
912
|
),
|
|
535
|
-
detail === null ? (0,
|
|
913
|
+
detail === null ? (0, import_react5.createElement)(
|
|
536
914
|
"div",
|
|
537
915
|
{ className: "mailharbor-ai-empty" },
|
|
538
|
-
(0,
|
|
539
|
-
(0,
|
|
540
|
-
(0,
|
|
541
|
-
) : (0,
|
|
916
|
+
(0, import_react5.createElement)(Icon, { name: "sparkles", size: 26 }),
|
|
917
|
+
(0, import_react5.createElement)("div", { className: "title" }, "\u5148\u9009\u4E00\u5C01\u90AE\u4EF6"),
|
|
918
|
+
(0, import_react5.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")
|
|
919
|
+
) : (0, import_react5.createElement)(
|
|
542
920
|
"div",
|
|
543
921
|
{ className: "mailharbor-ai-context" },
|
|
544
|
-
(0,
|
|
545
|
-
(0,
|
|
546
|
-
(0,
|
|
922
|
+
(0, import_react5.createElement)("span", { className: "small" }, "\u8DDF\u968F\u5F53\u524D\u90AE\u4EF6"),
|
|
923
|
+
(0, import_react5.createElement)("strong", null, detail.subject),
|
|
924
|
+
(0, import_react5.createElement)(
|
|
925
|
+
"span",
|
|
926
|
+
{ className: "small muted" },
|
|
927
|
+
project === null ? "AI \u53EA\u770B\u5230\u8FD9\u5C01\u90AE\u4EF6" : `\u9879\u76EE\uFF1A${project.name}`
|
|
928
|
+
)
|
|
547
929
|
),
|
|
548
|
-
disabled && detail !== null ? (0,
|
|
549
|
-
detail === null ? null : (0,
|
|
930
|
+
disabled && detail !== null ? (0, import_react5.createElement)("div", { className: "mailharbor-ai-warn" }, status?.error ?? "AI \u5DF2\u5173\u95ED\uFF0C\u8BF7\u5230\u8BBE\u7F6E\u91CC\u5F00\u542F") : null,
|
|
931
|
+
detail === null ? null : (0, import_react5.createElement)(
|
|
550
932
|
"div",
|
|
551
933
|
{ className: "mailharbor-ai-quick" },
|
|
552
|
-
(0,
|
|
934
|
+
(0, import_react5.createElement)(
|
|
553
935
|
"button",
|
|
554
936
|
{
|
|
555
937
|
className: "ghost",
|
|
@@ -558,7 +940,7 @@ function AiRail({
|
|
|
558
940
|
},
|
|
559
941
|
generating === "summary" ? "\u603B\u7ED3\u4E2D\u2026" : "\u6458\u8981"
|
|
560
942
|
),
|
|
561
|
-
(0,
|
|
943
|
+
(0, import_react5.createElement)(
|
|
562
944
|
"button",
|
|
563
945
|
{
|
|
564
946
|
className: "ghost",
|
|
@@ -567,7 +949,7 @@ function AiRail({
|
|
|
567
949
|
},
|
|
568
950
|
generating === "reply" ? "\u8D77\u8349\u4E2D\u2026" : "\u56DE\u590D"
|
|
569
951
|
),
|
|
570
|
-
(0,
|
|
952
|
+
(0, import_react5.createElement)(
|
|
571
953
|
"button",
|
|
572
954
|
{
|
|
573
955
|
className: "ghost",
|
|
@@ -575,58 +957,64 @@ function AiRail({
|
|
|
575
957
|
onClick: () => void generate("translate", { target: "en" })
|
|
576
958
|
},
|
|
577
959
|
generating === "translate" ? "\u7FFB\u8BD1\u4E2D\u2026" : "\u7FFB\u8BD1"
|
|
960
|
+
),
|
|
961
|
+
(0, import_react5.createElement)(
|
|
962
|
+
"button",
|
|
963
|
+
{ className: "ghost", onClick: () => void openProject() },
|
|
964
|
+
(0, import_react5.createElement)(Icon, { name: "folder", size: 13 }),
|
|
965
|
+
"\u9879\u76EE"
|
|
578
966
|
)
|
|
579
967
|
),
|
|
580
|
-
(0,
|
|
968
|
+
(0, import_react5.createElement)(
|
|
581
969
|
"div",
|
|
582
970
|
{ className: "mailharbor-ai-list", ref: list },
|
|
583
|
-
loading ? (0,
|
|
584
|
-
!loading && detail !== null && cards.length === 0 && generating === null ? (0,
|
|
971
|
+
loading ? (0, import_react5.createElement)("div", { className: "mailharbor-ai-empty small" }, "\u6B63\u5728\u8BFB\u53D6\u8BB0\u5F55\u2026") : null,
|
|
972
|
+
!loading && detail !== null && cards.length === 0 && generating === null ? (0, import_react5.createElement)(
|
|
585
973
|
"div",
|
|
586
974
|
{ className: "mailharbor-ai-empty" },
|
|
587
|
-
(0,
|
|
588
|
-
(0,
|
|
975
|
+
(0, import_react5.createElement)("div", { className: "title" }, "\u8FD9\u5C01\u90AE\u4EF6\u8FD8\u6CA1\u6709 AI \u8BB0\u5F55"),
|
|
976
|
+
(0, import_react5.createElement)(
|
|
589
977
|
"div",
|
|
590
978
|
{ className: "small" },
|
|
591
979
|
"\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
980
|
)
|
|
593
981
|
) : null,
|
|
594
982
|
cards.map(
|
|
595
|
-
(card) => (0,
|
|
983
|
+
(card) => (0, import_react5.createElement)(
|
|
596
984
|
"article",
|
|
597
985
|
{ className: "mailharbor-ai-card", key: card.id },
|
|
598
|
-
(0,
|
|
986
|
+
(0, import_react5.createElement)(
|
|
599
987
|
"div",
|
|
600
988
|
{ className: "mailharbor-ai-card-head" },
|
|
601
|
-
(0,
|
|
602
|
-
(0,
|
|
603
|
-
(0,
|
|
604
|
-
(0,
|
|
989
|
+
(0, import_react5.createElement)("span", null, KIND_LABEL[card.kind] ?? "AI"),
|
|
990
|
+
(0, import_react5.createElement)("span", { className: "muted small" }, new Date(card.createdAt).toLocaleTimeString()),
|
|
991
|
+
(0, import_react5.createElement)("span", { className: "spacer" }),
|
|
992
|
+
(0, import_react5.createElement)(
|
|
605
993
|
"button",
|
|
606
994
|
{
|
|
607
995
|
className: "subtle icon",
|
|
608
996
|
title: "\u5220\u9664",
|
|
609
997
|
onClick: () => void remove(card)
|
|
610
998
|
},
|
|
611
|
-
(0,
|
|
999
|
+
(0, import_react5.createElement)(Icon, { name: "close", size: 12 })
|
|
612
1000
|
)
|
|
613
1001
|
),
|
|
614
|
-
card.kind === "summary" ? (0,
|
|
1002
|
+
card.kind === "summary" ? (0, import_react5.createElement)(
|
|
615
1003
|
"div",
|
|
616
1004
|
{ className: "mailharbor-ai-summary" },
|
|
617
1005
|
summaryBlocks(card.output).map(
|
|
618
|
-
(block, index) => (0,
|
|
1006
|
+
(block, index) => (0, import_react5.createElement)(
|
|
619
1007
|
"section",
|
|
620
1008
|
{ key: `${block.title}:${String(index)}` },
|
|
621
|
-
block.title === "" ? null : (0,
|
|
622
|
-
(0,
|
|
1009
|
+
block.title === "" ? null : (0, import_react5.createElement)("h4", null, block.title),
|
|
1010
|
+
(0, import_react5.createElement)(Markdown, { text: block.body })
|
|
623
1011
|
)
|
|
624
1012
|
)
|
|
625
|
-
) : (0,
|
|
626
|
-
(0,
|
|
1013
|
+
) : (0, import_react5.createElement)("div", { className: "mailharbor-ai-text" }, (0, import_react5.createElement)(Markdown, { text: card.output })),
|
|
1014
|
+
(0, import_react5.createElement)(
|
|
627
1015
|
"div",
|
|
628
1016
|
{ className: "mailharbor-ai-card-actions" },
|
|
629
|
-
card.kind === "summary" ? (0,
|
|
1017
|
+
card.kind === "summary" ? (0, import_react5.createElement)(
|
|
630
1018
|
"button",
|
|
631
1019
|
{
|
|
632
1020
|
className: "ghost",
|
|
@@ -635,13 +1023,13 @@ function AiRail({
|
|
|
635
1023
|
},
|
|
636
1024
|
"\u7528\u8FD9\u4E9B\u8981\u70B9\u56DE\u590D"
|
|
637
1025
|
) : null,
|
|
638
|
-
card.kind === "reply" || card.kind === "translate" ? (0,
|
|
1026
|
+
card.kind === "reply" || card.kind === "translate" ? (0, import_react5.createElement)(
|
|
639
1027
|
"button",
|
|
640
1028
|
{ className: "ghost", onClick: () => onInsert(replyInit(card)) },
|
|
641
1029
|
card.kind === "translate" ? "\u63D2\u5165\u56DE\u590D" : "\u63D2\u5165\u6B63\u6587"
|
|
642
1030
|
) : null,
|
|
643
|
-
card.kind === "reply" ? (0,
|
|
644
|
-
card.kind === "answer" ? (0,
|
|
1031
|
+
card.kind === "reply" ? (0, import_react5.createElement)("button", { className: "ghost", onClick: () => onSaveDraft(replyInit(card)) }, "\u5B58\u8349\u7A3F") : null,
|
|
1032
|
+
card.kind === "answer" ? (0, import_react5.createElement)(
|
|
645
1033
|
"button",
|
|
646
1034
|
{
|
|
647
1035
|
className: "ghost",
|
|
@@ -649,196 +1037,125 @@ function AiRail({
|
|
|
649
1037
|
},
|
|
650
1038
|
"\u7EE7\u7EED\u8FFD\u95EE"
|
|
651
1039
|
) : null,
|
|
652
|
-
(0,
|
|
653
|
-
(0,
|
|
1040
|
+
(0, import_react5.createElement)("span", { className: "spacer" }),
|
|
1041
|
+
(0, import_react5.createElement)("button", { className: "subtle", onClick: () => copy(card) }, "\u590D\u5236")
|
|
654
1042
|
)
|
|
655
1043
|
)
|
|
656
1044
|
),
|
|
657
|
-
generating !== null ? (0,
|
|
1045
|
+
generating !== null ? (0, import_react5.createElement)(
|
|
658
1046
|
"article",
|
|
659
1047
|
{ className: "mailharbor-ai-card generating" },
|
|
660
|
-
(0,
|
|
661
|
-
(0,
|
|
1048
|
+
(0, import_react5.createElement)("div", { className: "mailharbor-ai-card-head" }, (0, import_react5.createElement)("span", null, KIND_LABEL[generating])),
|
|
1049
|
+
(0, import_react5.createElement)("div", { className: "mailharbor-ai-text muted" }, "\u6B63\u5728\u751F\u6210\u2026")
|
|
662
1050
|
) : null,
|
|
663
|
-
error === "" ? null : (0,
|
|
1051
|
+
error === "" ? null : (0, import_react5.createElement)("div", { className: "mailharbor-ai-error" }, error)
|
|
664
1052
|
),
|
|
665
|
-
|
|
666
|
-
|
|
1053
|
+
projectOpen ? (0, import_react5.createElement)(
|
|
1054
|
+
Modal,
|
|
667
1055
|
{
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
1056
|
+
title: project === null ? "\u8F6C\u5230\u9879\u76EE" : `\u9879\u76EE\uFF1A${project.name}`,
|
|
1057
|
+
onClose: () => setProjectOpen(false),
|
|
1058
|
+
footer: (0, import_react5.createElement)(
|
|
1059
|
+
"div",
|
|
1060
|
+
{ style: { display: "flex", gap: "8px", width: "100%", alignItems: "center" } },
|
|
1061
|
+
projectWarnings.length === 0 ? (0, import_react5.createElement)(
|
|
1062
|
+
"span",
|
|
1063
|
+
{ className: "small muted" },
|
|
1064
|
+
project === null ? "\u628A\u5F53\u524D AI \u8BB0\u5F55\u5E26\u8FDB\u4E00\u4E2A\u65B0\u5DE5\u4F5C\u7A7A\u95F4" : "\u628A\u5F53\u524D AI \u8BB0\u5F55\u8FFD\u52A0\u5230\u9879\u76EE\u5BF9\u8BDD"
|
|
1065
|
+
) : (0, import_react5.createElement)(
|
|
1066
|
+
"span",
|
|
1067
|
+
{ className: "small", style: { color: "var(--m-warn)" } },
|
|
1068
|
+
projectWarnings.join("\uFF1B")
|
|
1069
|
+
),
|
|
1070
|
+
(0, import_react5.createElement)("span", { className: "spacer" }),
|
|
1071
|
+
(0, import_react5.createElement)("button", { className: "ghost", onClick: () => setProjectOpen(false) }, "\u5173\u95ED"),
|
|
1072
|
+
project === null ? (0, import_react5.createElement)(
|
|
1073
|
+
"button",
|
|
1074
|
+
{ className: "primary", disabled: projectBusy, onClick: () => void create() },
|
|
1075
|
+
projectBusy ? "\u521B\u5EFA\u4E2D\u2026" : "\u521B\u5EFA\u9879\u76EE"
|
|
1076
|
+
) : (0, import_react5.createElement)(
|
|
1077
|
+
"button",
|
|
1078
|
+
{ className: "primary", disabled: projectBusy, onClick: () => void transfer() },
|
|
1079
|
+
projectBusy ? "\u8FFD\u52A0\u4E2D\u2026" : "\u8FFD\u52A0\u5F53\u524D\u8BB0\u5F55"
|
|
1080
|
+
)
|
|
1081
|
+
)
|
|
673
1082
|
},
|
|
674
|
-
(0,
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
1083
|
+
project === null ? (0, import_react5.createElement)(
|
|
1084
|
+
"div",
|
|
1085
|
+
{ style: { display: "flex", flexDirection: "column", gap: "10px" } },
|
|
1086
|
+
(0, import_react5.createElement)(
|
|
1087
|
+
"div",
|
|
1088
|
+
{ className: "mailharbor-field" },
|
|
1089
|
+
(0, import_react5.createElement)("label", null, "\u9879\u76EE\u540D\u79F0"),
|
|
1090
|
+
(0, import_react5.createElement)("input", {
|
|
1091
|
+
value: projectName,
|
|
1092
|
+
onChange: (event) => setProjectName(event.target.value)
|
|
1093
|
+
})
|
|
1094
|
+
),
|
|
1095
|
+
(0, import_react5.createElement)(
|
|
1096
|
+
"div",
|
|
1097
|
+
{ className: "mailharbor-field" },
|
|
1098
|
+
(0, import_react5.createElement)("label", null, "\u9879\u76EE\u76EE\u5F55\uFF08\u9ED8\u8BA4\u53EF\u5728\u8BBE\u7F6E\u91CC\u6539\uFF09"),
|
|
1099
|
+
(0, import_react5.createElement)("input", {
|
|
1100
|
+
value: projectRoot,
|
|
1101
|
+
onChange: (event) => setProjectRoot(event.target.value)
|
|
1102
|
+
})
|
|
1103
|
+
),
|
|
1104
|
+
(0, import_react5.createElement)(
|
|
1105
|
+
"p",
|
|
1106
|
+
{ className: "small muted" },
|
|
1107
|
+
`\u4F1A\u521B\u5EFA\u300C${projectName.trim() === "" ? "\u9879\u76EE" : projectName.trim()}\u300D\u6587\u4EF6\u5939\uFF0C\u5199\u5165 BRIEF.md \u548C\u5F53\u524D ${String(thread?.cards.length ?? 0)} \u6761 AI \u8BB0\u5F55\uFF0C\u5E76\u5728\u4FA7\u680F\u6CE8\u518C\u5DE5\u4F5C\u7A7A\u95F4\u3001\u5F00\u4E00\u4E2A\u4F1A\u8BDD\u3002`
|
|
1108
|
+
)
|
|
1109
|
+
) : (0, import_react5.createElement)(
|
|
1110
|
+
"div",
|
|
1111
|
+
{ style: { display: "flex", flexDirection: "column", gap: "8px" } },
|
|
1112
|
+
(0, import_react5.createElement)(
|
|
1113
|
+
"dl",
|
|
1114
|
+
{ className: "mailharbor-kv" },
|
|
1115
|
+
(0, import_react5.createElement)("dt", null, "\u76EE\u5F55"),
|
|
1116
|
+
(0, import_react5.createElement)("dd", null, project.path),
|
|
1117
|
+
(0, import_react5.createElement)("dt", null, "AI \u8BB0\u5F55"),
|
|
1118
|
+
(0, import_react5.createElement)("dd", null, `${String(thread?.cards.length ?? 0)} \u6761`)
|
|
1119
|
+
),
|
|
1120
|
+
project.sessionId === "" ? (0, import_react5.createElement)("p", { className: "small muted" }, "\u4F1A\u8BDD\u672A\u521B\u5EFA\uFF1A\u5F53\u524D\u7EC4\u5408\u6CA1\u6709 sessions \u670D\u52A1\u3002") : null,
|
|
1121
|
+
(0, import_react5.createElement)(
|
|
1122
|
+
"p",
|
|
1123
|
+
{ className: "small muted" },
|
|
1124
|
+
"\u300C\u8FFD\u52A0\u5F53\u524D\u8BB0\u5F55\u300D\u4F1A\u628A\u5DE5\u4F5C\u53F0\u91CC\u7684\u5361\u7247\u5199\u5165\u9879\u76EE\u7684 CONVERSATION.md\u3002"
|
|
1125
|
+
)
|
|
689
1126
|
)
|
|
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");
|
|
699
|
-
var import_react_dom = require("react-dom");
|
|
700
|
-
function Avatar({ email, size = 26 }) {
|
|
701
|
-
const letter = (email.trim()[0] ?? "?").toUpperCase();
|
|
702
|
-
return (0, import_react4.createElement)(
|
|
703
|
-
"span",
|
|
704
|
-
{
|
|
705
|
-
className: "avatar",
|
|
706
|
-
style: {
|
|
707
|
-
width: `${String(size)}px`,
|
|
708
|
-
height: `${String(size)}px`,
|
|
709
|
-
fontSize: `${String(Math.round(size * 0.42))}px`
|
|
710
|
-
},
|
|
711
|
-
"aria-hidden": "true"
|
|
712
|
-
},
|
|
713
|
-
letter
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
function Field({
|
|
717
|
-
label,
|
|
718
|
-
children,
|
|
719
|
-
hint
|
|
720
|
-
}) {
|
|
721
|
-
return (0, import_react4.createElement)(
|
|
722
|
-
"div",
|
|
723
|
-
{ className: "mailharbor-field" },
|
|
724
|
-
(0, import_react4.createElement)("label", null, label),
|
|
725
|
-
children,
|
|
726
|
-
hint === void 0 ? null : (0, import_react4.createElement)("span", { className: "small muted" }, hint)
|
|
727
|
-
);
|
|
728
|
-
}
|
|
729
|
-
function Toggle({
|
|
730
|
-
checked,
|
|
731
|
-
onChange,
|
|
732
|
-
label
|
|
733
|
-
}) {
|
|
734
|
-
return (0, import_react4.createElement)(
|
|
735
|
-
"label",
|
|
736
|
-
{ className: "mailharbor-toggle" },
|
|
737
|
-
(0, import_react4.createElement)("input", {
|
|
738
|
-
type: "checkbox",
|
|
739
|
-
checked,
|
|
740
|
-
onChange: (event) => {
|
|
741
|
-
onChange(event.target.checked);
|
|
742
|
-
}
|
|
743
|
-
}),
|
|
744
|
-
label
|
|
745
|
-
);
|
|
746
|
-
}
|
|
747
|
-
function portalRoot() {
|
|
748
|
-
const existing = document.getElementById("mailharbor-portal");
|
|
749
|
-
if (existing !== null) return existing;
|
|
750
|
-
const element = document.createElement("div");
|
|
751
|
-
element.id = "mailharbor-portal";
|
|
752
|
-
element.className = "mailharbor mailharbor-portal";
|
|
753
|
-
document.body.append(element);
|
|
754
|
-
return element;
|
|
755
|
-
}
|
|
756
|
-
function Modal({
|
|
757
|
-
title,
|
|
758
|
-
children,
|
|
759
|
-
footer,
|
|
760
|
-
onClose,
|
|
761
|
-
wide = false
|
|
762
|
-
}) {
|
|
763
|
-
(0, import_react4.useEffect)(() => {
|
|
764
|
-
const onKey = (event) => {
|
|
765
|
-
if (event.key === "Escape") onClose();
|
|
766
|
-
};
|
|
767
|
-
window.addEventListener("keydown", onKey);
|
|
768
|
-
return () => {
|
|
769
|
-
window.removeEventListener("keydown", onKey);
|
|
770
|
-
};
|
|
771
|
-
}, [onClose]);
|
|
772
|
-
return (0, import_react_dom.createPortal)(
|
|
773
|
-
(0, import_react4.createElement)(
|
|
774
|
-
"div",
|
|
1127
|
+
) : null,
|
|
1128
|
+
detail === null ? null : (0, import_react5.createElement)(
|
|
1129
|
+
"form",
|
|
775
1130
|
{
|
|
776
|
-
className: "mailharbor-
|
|
777
|
-
|
|
778
|
-
|
|
1131
|
+
className: "mailharbor-ai-ask",
|
|
1132
|
+
onSubmit: (event) => {
|
|
1133
|
+
event.preventDefault();
|
|
1134
|
+
void ask();
|
|
779
1135
|
}
|
|
780
1136
|
},
|
|
781
|
-
(0,
|
|
782
|
-
|
|
1137
|
+
(0, import_react5.createElement)("input", {
|
|
1138
|
+
value: question,
|
|
1139
|
+
placeholder: "\u95EE\u8FD9\u5C01\u90AE\u4EF6\uFF0C\u4F8B\u5982\uFF1A\u8C01\u9700\u8981\u5728\u4EC0\u4E48\u65F6\u5019\u505A\u4EC0\u4E48\uFF1F",
|
|
1140
|
+
"aria-label": "\u95EE AI",
|
|
1141
|
+
disabled: disabled || generating !== null,
|
|
1142
|
+
onChange: (event) => setQuestion(event.target.value)
|
|
1143
|
+
}),
|
|
1144
|
+
(0, import_react5.createElement)(
|
|
1145
|
+
"button",
|
|
783
1146
|
{
|
|
784
|
-
className: "
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
"aria-modal": "true",
|
|
788
|
-
"aria-label": title
|
|
1147
|
+
className: "primary icon",
|
|
1148
|
+
type: "submit",
|
|
1149
|
+
disabled: disabled || generating !== null || question.trim() === ""
|
|
789
1150
|
},
|
|
790
|
-
(0,
|
|
791
|
-
"header",
|
|
792
|
-
null,
|
|
793
|
-
title,
|
|
794
|
-
(0, import_react4.createElement)("span", { className: "spacer" }),
|
|
795
|
-
(0, import_react4.createElement)("button", { className: "subtle", onClick: onClose, "aria-label": "\u5173\u95ED" }, "\u2715")
|
|
796
|
-
),
|
|
797
|
-
(0, import_react4.createElement)("div", { className: "content" }, children),
|
|
798
|
-
footer === void 0 ? null : (0, import_react4.createElement)("footer", null, footer)
|
|
1151
|
+
(0, import_react5.createElement)(Icon, { name: "send", size: 14 })
|
|
799
1152
|
)
|
|
800
|
-
)
|
|
801
|
-
portalRoot()
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
function ErrorBar({ message, onDismiss }) {
|
|
805
|
-
return (0, import_react4.createElement)(
|
|
806
|
-
"div",
|
|
807
|
-
{ className: "error", role: "alert" },
|
|
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")
|
|
1153
|
+
)
|
|
811
1154
|
);
|
|
812
1155
|
}
|
|
813
|
-
function shortDate(iso) {
|
|
814
|
-
const date = new Date(iso);
|
|
815
|
-
if (Number.isNaN(date.getTime())) return iso.slice(0, 16);
|
|
816
|
-
const now = /* @__PURE__ */ new Date();
|
|
817
|
-
const sameDay = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
|
|
818
|
-
const pad = (value) => String(value).padStart(2, "0");
|
|
819
|
-
if (sameDay) return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
820
|
-
if (date.getFullYear() === now.getFullYear()) return `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
821
|
-
return `${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
822
|
-
}
|
|
823
|
-
function longDate(iso) {
|
|
824
|
-
const date = new Date(iso);
|
|
825
|
-
if (Number.isNaN(date.getTime())) return iso;
|
|
826
|
-
return date.toLocaleString(void 0, {
|
|
827
|
-
year: "numeric",
|
|
828
|
-
month: "2-digit",
|
|
829
|
-
day: "2-digit",
|
|
830
|
-
hour: "2-digit",
|
|
831
|
-
minute: "2-digit"
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
function formatBytes(size) {
|
|
835
|
-
if (!Number.isFinite(size) || size <= 0) return "";
|
|
836
|
-
if (size < 1024) return `${String(size)} B`;
|
|
837
|
-
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
|
|
838
|
-
return `${(size / 1024 / 1024).toFixed(1)} MB`;
|
|
839
|
-
}
|
|
840
1156
|
|
|
841
1157
|
// src/client/account-form.tsx
|
|
1158
|
+
var import_react6 = require("react");
|
|
842
1159
|
function domainOf(email) {
|
|
843
1160
|
return email.split("@")[1] ?? "example.com";
|
|
844
1161
|
}
|
|
@@ -849,20 +1166,20 @@ function AddAccountForm({
|
|
|
849
1166
|
onDone,
|
|
850
1167
|
onCancel
|
|
851
1168
|
}) {
|
|
852
|
-
const [email, setEmail] = (0,
|
|
853
|
-
const [password, setPassword] = (0,
|
|
854
|
-
const [label, setLabel] = (0,
|
|
855
|
-
const [remember, setRemember] = (0,
|
|
856
|
-
const [advanced, setAdvanced] = (0,
|
|
857
|
-
const [imapHost, setImapHost] = (0,
|
|
858
|
-
const [imapPort, setImapPort] = (0,
|
|
859
|
-
const [imapSecure, setImapSecure] = (0,
|
|
860
|
-
const [smtpHost, setSmtpHost] = (0,
|
|
861
|
-
const [smtpPort, setSmtpPort] = (0,
|
|
862
|
-
const [smtpSecure, setSmtpSecure] = (0,
|
|
863
|
-
const [fromName, setFromName] = (0,
|
|
864
|
-
const [busy, setBusy] = (0,
|
|
865
|
-
const [error, setError] = (0,
|
|
1169
|
+
const [email, setEmail] = (0, import_react6.useState)("");
|
|
1170
|
+
const [password, setPassword] = (0, import_react6.useState)("");
|
|
1171
|
+
const [label, setLabel] = (0, import_react6.useState)("");
|
|
1172
|
+
const [remember, setRemember] = (0, import_react6.useState)(true);
|
|
1173
|
+
const [advanced, setAdvanced] = (0, import_react6.useState)(false);
|
|
1174
|
+
const [imapHost, setImapHost] = (0, import_react6.useState)("");
|
|
1175
|
+
const [imapPort, setImapPort] = (0, import_react6.useState)("993");
|
|
1176
|
+
const [imapSecure, setImapSecure] = (0, import_react6.useState)(true);
|
|
1177
|
+
const [smtpHost, setSmtpHost] = (0, import_react6.useState)("");
|
|
1178
|
+
const [smtpPort, setSmtpPort] = (0, import_react6.useState)("465");
|
|
1179
|
+
const [smtpSecure, setSmtpSecure] = (0, import_react6.useState)(true);
|
|
1180
|
+
const [fromName, setFromName] = (0, import_react6.useState)("");
|
|
1181
|
+
const [busy, setBusy] = (0, import_react6.useState)(false);
|
|
1182
|
+
const [error, setError] = (0, import_react6.useState)("");
|
|
866
1183
|
const submit = async () => {
|
|
867
1184
|
setBusy(true);
|
|
868
1185
|
setError("");
|
|
@@ -894,17 +1211,17 @@ function AddAccountForm({
|
|
|
894
1211
|
setBusy(false);
|
|
895
1212
|
}
|
|
896
1213
|
};
|
|
897
|
-
return (0,
|
|
1214
|
+
return (0, import_react6.createElement)(
|
|
898
1215
|
"div",
|
|
899
1216
|
{ className: "mailharbor-card" },
|
|
900
|
-
(0,
|
|
901
|
-
(0,
|
|
1217
|
+
(0, import_react6.createElement)("h3", null, "\u6DFB\u52A0\u90AE\u7BB1\u8D26\u53F7"),
|
|
1218
|
+
(0, import_react6.createElement)(
|
|
902
1219
|
"div",
|
|
903
1220
|
{ className: "mailharbor-grid" },
|
|
904
|
-
(0,
|
|
1221
|
+
(0, import_react6.createElement)(
|
|
905
1222
|
Field,
|
|
906
1223
|
{ label: "\u90AE\u7BB1\u5730\u5740" },
|
|
907
|
-
(0,
|
|
1224
|
+
(0, import_react6.createElement)("input", {
|
|
908
1225
|
type: "email",
|
|
909
1226
|
value: email,
|
|
910
1227
|
placeholder: "you@example.com",
|
|
@@ -912,95 +1229,95 @@ function AddAccountForm({
|
|
|
912
1229
|
autoFocus: true
|
|
913
1230
|
})
|
|
914
1231
|
),
|
|
915
|
-
(0,
|
|
1232
|
+
(0, import_react6.createElement)(
|
|
916
1233
|
Field,
|
|
917
1234
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801" },
|
|
918
|
-
(0,
|
|
1235
|
+
(0, import_react6.createElement)("input", {
|
|
919
1236
|
type: "password",
|
|
920
1237
|
value: password,
|
|
921
1238
|
onChange: (event) => setPassword(event.target.value)
|
|
922
1239
|
})
|
|
923
1240
|
)
|
|
924
1241
|
),
|
|
925
|
-
(0,
|
|
1242
|
+
(0, import_react6.createElement)(
|
|
926
1243
|
Field,
|
|
927
1244
|
{ label: "\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09" },
|
|
928
|
-
(0,
|
|
1245
|
+
(0, import_react6.createElement)("input", {
|
|
929
1246
|
value: label,
|
|
930
1247
|
placeholder: "\u5DE5\u4F5C\u90AE\u7BB1",
|
|
931
1248
|
onChange: (event) => setLabel(event.target.value)
|
|
932
1249
|
})
|
|
933
1250
|
),
|
|
934
|
-
(0,
|
|
935
|
-
(0,
|
|
1251
|
+
(0, import_react6.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801\uFF08\u5199\u5165\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF09" }),
|
|
1252
|
+
(0, import_react6.createElement)(
|
|
936
1253
|
"button",
|
|
937
1254
|
{ className: "ghost", onClick: () => setAdvanced(!advanced) },
|
|
938
1255
|
advanced ? "\u9690\u85CF\u9AD8\u7EA7\u8BBE\u7F6E" : "\u9AD8\u7EA7\u8BBE\u7F6E\uFF08\u670D\u52A1\u5668\u4E0E\u7AEF\u53E3\uFF09"
|
|
939
1256
|
),
|
|
940
|
-
advanced ? (0,
|
|
1257
|
+
advanced ? (0, import_react6.createElement)(
|
|
941
1258
|
"div",
|
|
942
1259
|
{ style: { display: "flex", flexDirection: "column", gap: "10px" } },
|
|
943
|
-
(0,
|
|
1260
|
+
(0, import_react6.createElement)(
|
|
944
1261
|
"div",
|
|
945
1262
|
{ className: "mailharbor-grid three" },
|
|
946
|
-
(0,
|
|
1263
|
+
(0, import_react6.createElement)(
|
|
947
1264
|
Field,
|
|
948
1265
|
{ label: "IMAP \u4E3B\u673A" },
|
|
949
|
-
(0,
|
|
1266
|
+
(0, import_react6.createElement)("input", {
|
|
950
1267
|
value: imapHost,
|
|
951
1268
|
placeholder: `imap.${domainOf(email)}`,
|
|
952
1269
|
onChange: (event) => setImapHost(event.target.value)
|
|
953
1270
|
})
|
|
954
1271
|
),
|
|
955
|
-
(0,
|
|
1272
|
+
(0, import_react6.createElement)(
|
|
956
1273
|
Field,
|
|
957
1274
|
{ label: "\u7AEF\u53E3" },
|
|
958
|
-
(0,
|
|
1275
|
+
(0, import_react6.createElement)("input", {
|
|
959
1276
|
value: imapPort,
|
|
960
1277
|
inputMode: "numeric",
|
|
961
1278
|
onChange: (event) => setImapPort(event.target.value)
|
|
962
1279
|
})
|
|
963
1280
|
),
|
|
964
|
-
(0,
|
|
1281
|
+
(0, import_react6.createElement)(Toggle, { checked: imapSecure, onChange: setImapSecure, label: "TLS" })
|
|
965
1282
|
),
|
|
966
|
-
(0,
|
|
1283
|
+
(0, import_react6.createElement)(
|
|
967
1284
|
"div",
|
|
968
1285
|
{ className: "mailharbor-grid three" },
|
|
969
|
-
(0,
|
|
1286
|
+
(0, import_react6.createElement)(
|
|
970
1287
|
Field,
|
|
971
1288
|
{ label: "SMTP \u4E3B\u673A" },
|
|
972
|
-
(0,
|
|
1289
|
+
(0, import_react6.createElement)("input", {
|
|
973
1290
|
value: smtpHost,
|
|
974
1291
|
placeholder: `smtp.${domainOf(email)}`,
|
|
975
1292
|
onChange: (event) => setSmtpHost(event.target.value)
|
|
976
1293
|
})
|
|
977
1294
|
),
|
|
978
|
-
(0,
|
|
1295
|
+
(0, import_react6.createElement)(
|
|
979
1296
|
Field,
|
|
980
1297
|
{ label: "\u7AEF\u53E3" },
|
|
981
|
-
(0,
|
|
1298
|
+
(0, import_react6.createElement)("input", {
|
|
982
1299
|
value: smtpPort,
|
|
983
1300
|
inputMode: "numeric",
|
|
984
1301
|
onChange: (event) => setSmtpPort(event.target.value)
|
|
985
1302
|
})
|
|
986
1303
|
),
|
|
987
|
-
(0,
|
|
1304
|
+
(0, import_react6.createElement)(Toggle, { checked: smtpSecure, onChange: setSmtpSecure, label: "TLS" })
|
|
988
1305
|
),
|
|
989
|
-
(0,
|
|
1306
|
+
(0, import_react6.createElement)(
|
|
990
1307
|
Field,
|
|
991
1308
|
{ label: "\u53D1\u4EF6\u4EBA\u540D\u79F0\uFF08\u53EF\u9009\uFF09" },
|
|
992
|
-
(0,
|
|
1309
|
+
(0, import_react6.createElement)("input", {
|
|
993
1310
|
value: fromName,
|
|
994
1311
|
onChange: (event) => setFromName(event.target.value)
|
|
995
1312
|
})
|
|
996
1313
|
)
|
|
997
1314
|
) : null,
|
|
998
|
-
error === "" ? null : (0,
|
|
999
|
-
(0,
|
|
1315
|
+
error === "" ? null : (0, import_react6.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
1316
|
+
(0, import_react6.createElement)(
|
|
1000
1317
|
"div",
|
|
1001
1318
|
{ style: { display: "flex", gap: "8px", justifyContent: "flex-end" } },
|
|
1002
|
-
onCancel === void 0 ? null : (0,
|
|
1003
|
-
(0,
|
|
1319
|
+
onCancel === void 0 ? null : (0, import_react6.createElement)("button", { className: "ghost", onClick: onCancel }, "\u53D6\u6D88"),
|
|
1320
|
+
(0, import_react6.createElement)(
|
|
1004
1321
|
"button",
|
|
1005
1322
|
{
|
|
1006
1323
|
className: "primary",
|
|
@@ -1013,10 +1330,10 @@ function AddAccountForm({
|
|
|
1013
1330
|
);
|
|
1014
1331
|
}
|
|
1015
1332
|
function LoginForm({ account, onDone }) {
|
|
1016
|
-
const [password, setPassword] = (0,
|
|
1017
|
-
const [remember, setRemember] = (0,
|
|
1018
|
-
const [busy, setBusy] = (0,
|
|
1019
|
-
const [error, setError] = (0,
|
|
1333
|
+
const [password, setPassword] = (0, import_react6.useState)("");
|
|
1334
|
+
const [remember, setRemember] = (0, import_react6.useState)(true);
|
|
1335
|
+
const [busy, setBusy] = (0, import_react6.useState)(false);
|
|
1336
|
+
const [error, setError] = (0, import_react6.useState)("");
|
|
1020
1337
|
const submit = async () => {
|
|
1021
1338
|
setBusy(true);
|
|
1022
1339
|
setError("");
|
|
@@ -1029,19 +1346,19 @@ function LoginForm({ account, onDone }) {
|
|
|
1029
1346
|
setBusy(false);
|
|
1030
1347
|
}
|
|
1031
1348
|
};
|
|
1032
|
-
return (0,
|
|
1349
|
+
return (0, import_react6.createElement)(
|
|
1033
1350
|
"div",
|
|
1034
1351
|
{ className: "mailharbor-card" },
|
|
1035
|
-
(0,
|
|
1036
|
-
(0,
|
|
1352
|
+
(0, import_react6.createElement)("h3", null, `\u767B\u5F55 ${account.email}`),
|
|
1353
|
+
(0, import_react6.createElement)(
|
|
1037
1354
|
"p",
|
|
1038
1355
|
{ className: "small muted" },
|
|
1039
1356
|
`${account.imap.host}:${String(account.imap.port)} \xB7 ${account.smtp.host}:${String(account.smtp.port)}`
|
|
1040
1357
|
),
|
|
1041
|
-
(0,
|
|
1358
|
+
(0, import_react6.createElement)(
|
|
1042
1359
|
Field,
|
|
1043
1360
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801" },
|
|
1044
|
-
(0,
|
|
1361
|
+
(0, import_react6.createElement)("input", {
|
|
1045
1362
|
type: "password",
|
|
1046
1363
|
value: password,
|
|
1047
1364
|
onChange: (event) => setPassword(event.target.value),
|
|
@@ -1051,9 +1368,9 @@ function LoginForm({ account, onDone }) {
|
|
|
1051
1368
|
autoFocus: true
|
|
1052
1369
|
})
|
|
1053
1370
|
),
|
|
1054
|
-
(0,
|
|
1055
|
-
error === "" ? null : (0,
|
|
1056
|
-
(0,
|
|
1371
|
+
(0, import_react6.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801\uFF08\u5199\u5165\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF09" }),
|
|
1372
|
+
error === "" ? null : (0, import_react6.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
1373
|
+
(0, import_react6.createElement)(
|
|
1057
1374
|
"button",
|
|
1058
1375
|
{ className: "primary", onClick: () => void submit(), disabled: busy || password === "" },
|
|
1059
1376
|
busy ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
|
|
@@ -1064,10 +1381,10 @@ function AddAccountModal({
|
|
|
1064
1381
|
onClose,
|
|
1065
1382
|
onDone
|
|
1066
1383
|
}) {
|
|
1067
|
-
return (0,
|
|
1384
|
+
return (0, import_react6.createElement)(
|
|
1068
1385
|
Modal,
|
|
1069
1386
|
{ title: "\u6DFB\u52A0\u90AE\u7BB1\u8D26\u53F7", onClose },
|
|
1070
|
-
(0,
|
|
1387
|
+
(0, import_react6.createElement)(AddAccountForm, {
|
|
1071
1388
|
onDone: (state) => {
|
|
1072
1389
|
onDone(state);
|
|
1073
1390
|
onClose();
|
|
@@ -1078,7 +1395,7 @@ function AddAccountModal({
|
|
|
1078
1395
|
}
|
|
1079
1396
|
|
|
1080
1397
|
// src/client/composer.tsx
|
|
1081
|
-
var
|
|
1398
|
+
var import_react7 = require("react");
|
|
1082
1399
|
function errorText3(error) {
|
|
1083
1400
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
1084
1401
|
}
|
|
@@ -1099,29 +1416,29 @@ function Composer({
|
|
|
1099
1416
|
onClose,
|
|
1100
1417
|
onSent
|
|
1101
1418
|
}) {
|
|
1102
|
-
const [to, setTo] = (0,
|
|
1103
|
-
const [cc, setCc] = (0,
|
|
1104
|
-
const [bcc, setBcc] = (0,
|
|
1105
|
-
const [subject, setSubject] = (0,
|
|
1106
|
-
const [showCc, setShowCc] = (0,
|
|
1107
|
-
const [attachments, setAttachments] = (0,
|
|
1108
|
-
const [busy, setBusy] = (0,
|
|
1109
|
-
const [error, setError] = (0,
|
|
1110
|
-
const [confirming, setConfirming] = (0,
|
|
1111
|
-
const [draftId, setDraftId] = (0,
|
|
1112
|
-
const [savedAt, setSavedAt] = (0,
|
|
1113
|
-
const [dirty, setDirty] = (0,
|
|
1114
|
-
const [translating, setTranslating] = (0,
|
|
1115
|
-
const [pendingLang, setPendingLang] = (0,
|
|
1116
|
-
const [versions, setVersions] = (0,
|
|
1117
|
-
const [versionIndex, setVersionIndex] = (0,
|
|
1118
|
-
const [aiBusy, setAiBusy] = (0,
|
|
1119
|
-
const [aiDraftOpen, setAiDraftOpen] = (0,
|
|
1120
|
-
const [aiInstruction, setAiInstruction] = (0,
|
|
1121
|
-
const [hasBody, setHasBody] = (0,
|
|
1122
|
-
const editor = (0,
|
|
1123
|
-
const closing = (0,
|
|
1124
|
-
(0,
|
|
1419
|
+
const [to, setTo] = (0, import_react7.useState)(initial.to);
|
|
1420
|
+
const [cc, setCc] = (0, import_react7.useState)(initial.cc);
|
|
1421
|
+
const [bcc, setBcc] = (0, import_react7.useState)(initial.bcc);
|
|
1422
|
+
const [subject, setSubject] = (0, import_react7.useState)(initial.subject);
|
|
1423
|
+
const [showCc, setShowCc] = (0, import_react7.useState)(initial.cc !== "" || initial.bcc !== "");
|
|
1424
|
+
const [attachments, setAttachments] = (0, import_react7.useState)(initial.attachments ?? []);
|
|
1425
|
+
const [busy, setBusy] = (0, import_react7.useState)(false);
|
|
1426
|
+
const [error, setError] = (0, import_react7.useState)("");
|
|
1427
|
+
const [confirming, setConfirming] = (0, import_react7.useState)(false);
|
|
1428
|
+
const [draftId, setDraftId] = (0, import_react7.useState)(initial.draftId);
|
|
1429
|
+
const [savedAt, setSavedAt] = (0, import_react7.useState)(null);
|
|
1430
|
+
const [dirty, setDirty] = (0, import_react7.useState)(0);
|
|
1431
|
+
const [translating, setTranslating] = (0, import_react7.useState)(null);
|
|
1432
|
+
const [pendingLang, setPendingLang] = (0, import_react7.useState)(null);
|
|
1433
|
+
const [versions, setVersions] = (0, import_react7.useState)([{ html: initial.html, label: "\u539F\u59CB" }]);
|
|
1434
|
+
const [versionIndex, setVersionIndex] = (0, import_react7.useState)(0);
|
|
1435
|
+
const [aiBusy, setAiBusy] = (0, import_react7.useState)(null);
|
|
1436
|
+
const [aiDraftOpen, setAiDraftOpen] = (0, import_react7.useState)(false);
|
|
1437
|
+
const [aiInstruction, setAiInstruction] = (0, import_react7.useState)("");
|
|
1438
|
+
const [hasBody, setHasBody] = (0, import_react7.useState)(initial.html.trim() !== "");
|
|
1439
|
+
const editor = (0, import_react7.useRef)(null);
|
|
1440
|
+
const closing = (0, import_react7.useRef)(false);
|
|
1441
|
+
(0, import_react7.useEffect)(() => {
|
|
1125
1442
|
if (editor.current !== null) editor.current.innerHTML = initial.html;
|
|
1126
1443
|
setHasBody((editor.current?.innerHTML ?? "").trim() !== "");
|
|
1127
1444
|
}, []);
|
|
@@ -1153,7 +1470,7 @@ function Composer({
|
|
|
1153
1470
|
return false;
|
|
1154
1471
|
}
|
|
1155
1472
|
};
|
|
1156
|
-
(0,
|
|
1473
|
+
(0, import_react7.useEffect)(() => {
|
|
1157
1474
|
if (dirty === 0) return;
|
|
1158
1475
|
const timer = window.setTimeout(() => {
|
|
1159
1476
|
void saveNow();
|
|
@@ -1287,30 +1604,30 @@ function Composer({
|
|
|
1287
1604
|
setConfirming(false);
|
|
1288
1605
|
}
|
|
1289
1606
|
};
|
|
1290
|
-
return (0,
|
|
1607
|
+
return (0, import_react7.createElement)(
|
|
1291
1608
|
Modal,
|
|
1292
1609
|
{
|
|
1293
1610
|
title: initial.mode === "reply" ? "\u56DE\u590D" : initial.mode === "replyAll" ? "\u5168\u90E8\u56DE\u590D" : initial.mode === "forward" ? "\u8F6C\u53D1" : "\u5199\u90AE\u4EF6",
|
|
1294
1611
|
onClose: close,
|
|
1295
1612
|
wide: true,
|
|
1296
|
-
footer: (0,
|
|
1613
|
+
footer: (0, import_react7.createElement)(
|
|
1297
1614
|
"div",
|
|
1298
1615
|
{ className: "mailharbor-compose-foot" },
|
|
1299
|
-
(0,
|
|
1300
|
-
savedAt === null ? null : (0,
|
|
1301
|
-
(0,
|
|
1302
|
-
(0,
|
|
1303
|
-
confirming ? (0,
|
|
1616
|
+
(0, import_react7.createElement)("span", { className: "small muted" }, `\u53D1\u4EF6\u4EBA ${account.email}`),
|
|
1617
|
+
savedAt === null ? null : (0, import_react7.createElement)("span", { className: "small muted" }, `\u5DF2\u81EA\u52A8\u4FDD\u5B58 ${savedAt.toLocaleTimeString()}`),
|
|
1618
|
+
(0, import_react7.createElement)("span", { className: "spacer" }),
|
|
1619
|
+
(0, import_react7.createElement)("button", { className: "ghost", onClick: () => void saveDraft(), disabled: busy }, "\u5B58\u8349\u7A3F"),
|
|
1620
|
+
confirming ? (0, import_react7.createElement)(
|
|
1304
1621
|
"span",
|
|
1305
1622
|
{ style: { display: "inline-flex", gap: "6px", alignItems: "center" } },
|
|
1306
|
-
(0,
|
|
1307
|
-
(0,
|
|
1623
|
+
(0, import_react7.createElement)("span", { className: "small" }, "\u786E\u5B9A\u53D1\u9001\uFF1F"),
|
|
1624
|
+
(0, import_react7.createElement)(
|
|
1308
1625
|
"button",
|
|
1309
1626
|
{ className: "primary", onClick: () => void send(), disabled: busy },
|
|
1310
1627
|
busy ? "\u53D1\u9001\u4E2D\u2026" : "\u786E\u5B9A"
|
|
1311
1628
|
),
|
|
1312
|
-
(0,
|
|
1313
|
-
) : (0,
|
|
1629
|
+
(0, import_react7.createElement)("button", { className: "ghost", onClick: () => setConfirming(false) }, "\u53D6\u6D88")
|
|
1630
|
+
) : (0, import_react7.createElement)(
|
|
1314
1631
|
"button",
|
|
1315
1632
|
{
|
|
1316
1633
|
className: "primary",
|
|
@@ -1321,14 +1638,14 @@ function Composer({
|
|
|
1321
1638
|
)
|
|
1322
1639
|
)
|
|
1323
1640
|
},
|
|
1324
|
-
error === "" ? null : (0,
|
|
1325
|
-
(0,
|
|
1641
|
+
error === "" ? null : (0, import_react7.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
1642
|
+
(0, import_react7.createElement)(
|
|
1326
1643
|
"div",
|
|
1327
1644
|
{ className: "mailharbor-grid" },
|
|
1328
|
-
(0,
|
|
1645
|
+
(0, import_react7.createElement)(
|
|
1329
1646
|
Field,
|
|
1330
1647
|
{ label: "\u6536\u4EF6\u4EBA" },
|
|
1331
|
-
(0,
|
|
1648
|
+
(0, import_react7.createElement)("input", {
|
|
1332
1649
|
value: to,
|
|
1333
1650
|
onChange: (event) => {
|
|
1334
1651
|
setTo(event.target.value);
|
|
@@ -1337,10 +1654,10 @@ function Composer({
|
|
|
1337
1654
|
placeholder: "a@example.com, b@example.com"
|
|
1338
1655
|
})
|
|
1339
1656
|
),
|
|
1340
|
-
(0,
|
|
1657
|
+
(0, import_react7.createElement)(
|
|
1341
1658
|
Field,
|
|
1342
1659
|
{ label: "\u4E3B\u9898" },
|
|
1343
|
-
(0,
|
|
1660
|
+
(0, import_react7.createElement)("input", {
|
|
1344
1661
|
value: subject,
|
|
1345
1662
|
onChange: (event) => {
|
|
1346
1663
|
setSubject(event.target.value);
|
|
@@ -1350,13 +1667,13 @@ function Composer({
|
|
|
1350
1667
|
})
|
|
1351
1668
|
)
|
|
1352
1669
|
),
|
|
1353
|
-
showCc ? (0,
|
|
1670
|
+
showCc ? (0, import_react7.createElement)(
|
|
1354
1671
|
"div",
|
|
1355
1672
|
{ className: "mailharbor-grid" },
|
|
1356
|
-
(0,
|
|
1673
|
+
(0, import_react7.createElement)(
|
|
1357
1674
|
Field,
|
|
1358
1675
|
{ label: "\u6284\u9001" },
|
|
1359
|
-
(0,
|
|
1676
|
+
(0, import_react7.createElement)("input", {
|
|
1360
1677
|
value: cc,
|
|
1361
1678
|
onChange: (event) => {
|
|
1362
1679
|
setCc(event.target.value);
|
|
@@ -1364,10 +1681,10 @@ function Composer({
|
|
|
1364
1681
|
}
|
|
1365
1682
|
})
|
|
1366
1683
|
),
|
|
1367
|
-
(0,
|
|
1684
|
+
(0, import_react7.createElement)(
|
|
1368
1685
|
Field,
|
|
1369
1686
|
{ label: "\u5BC6\u9001" },
|
|
1370
|
-
(0,
|
|
1687
|
+
(0, import_react7.createElement)("input", {
|
|
1371
1688
|
value: bcc,
|
|
1372
1689
|
onChange: (event) => {
|
|
1373
1690
|
setBcc(event.target.value);
|
|
@@ -1375,21 +1692,21 @@ function Composer({
|
|
|
1375
1692
|
}
|
|
1376
1693
|
})
|
|
1377
1694
|
)
|
|
1378
|
-
) : (0,
|
|
1695
|
+
) : (0, import_react7.createElement)(
|
|
1379
1696
|
"button",
|
|
1380
1697
|
{ className: "subtle small", style: { alignSelf: "flex-start" }, onClick: () => setShowCc(true) },
|
|
1381
1698
|
"\u6DFB\u52A0\u6284\u9001 / \u5BC6\u9001"
|
|
1382
1699
|
),
|
|
1383
|
-
(0,
|
|
1700
|
+
(0, import_react7.createElement)(
|
|
1384
1701
|
"div",
|
|
1385
1702
|
{ className: "mailharbor-toolbar" },
|
|
1386
|
-
(0,
|
|
1387
|
-
(0,
|
|
1388
|
-
(0,
|
|
1389
|
-
(0,
|
|
1390
|
-
(0,
|
|
1391
|
-
(0,
|
|
1392
|
-
(0,
|
|
1703
|
+
(0, import_react7.createElement)("button", { onClick: () => exec("bold"), title: "\u52A0\u7C97" }, "B"),
|
|
1704
|
+
(0, import_react7.createElement)("button", { onClick: () => exec("italic"), title: "\u659C\u4F53" }, "I"),
|
|
1705
|
+
(0, import_react7.createElement)("button", { onClick: () => exec("underline"), title: "\u4E0B\u5212\u7EBF" }, "U"),
|
|
1706
|
+
(0, import_react7.createElement)("button", { onClick: () => exec("insertUnorderedList"), title: "\u65E0\u5E8F\u5217\u8868" }, "\u2022 \u5217\u8868"),
|
|
1707
|
+
(0, import_react7.createElement)("button", { onClick: () => exec("insertOrderedList"), title: "\u6709\u5E8F\u5217\u8868" }, "1. \u5217\u8868"),
|
|
1708
|
+
(0, import_react7.createElement)("button", { onClick: () => exec("formatBlock", "blockquote"), title: "\u5F15\u7528" }, "\u5F15\u7528"),
|
|
1709
|
+
(0, import_react7.createElement)(
|
|
1393
1710
|
"button",
|
|
1394
1711
|
{
|
|
1395
1712
|
onClick: () => {
|
|
@@ -1400,9 +1717,9 @@ function Composer({
|
|
|
1400
1717
|
},
|
|
1401
1718
|
"\u94FE\u63A5"
|
|
1402
1719
|
),
|
|
1403
|
-
(0,
|
|
1404
|
-
(0,
|
|
1405
|
-
(0,
|
|
1720
|
+
(0, import_react7.createElement)("button", { onClick: () => exec("removeFormat"), title: "\u6E05\u9664\u683C\u5F0F" }, "\u6E05\u9664"),
|
|
1721
|
+
(0, import_react7.createElement)("span", { className: "spacer" }),
|
|
1722
|
+
(0, import_react7.createElement)(
|
|
1406
1723
|
"button",
|
|
1407
1724
|
{
|
|
1408
1725
|
className: "ai",
|
|
@@ -1410,34 +1727,34 @@ function Composer({
|
|
|
1410
1727
|
disabled: aiBusy !== null,
|
|
1411
1728
|
title: "\u8BA9 AI \u8D77\u8349\u6B63\u6587"
|
|
1412
1729
|
},
|
|
1413
|
-
(0,
|
|
1730
|
+
(0, import_react7.createElement)(Icon, { name: "sparkles", size: 13 }),
|
|
1414
1731
|
"\u8D77\u8349"
|
|
1415
1732
|
),
|
|
1416
|
-
(0,
|
|
1733
|
+
(0, import_react7.createElement)(
|
|
1417
1734
|
"button",
|
|
1418
1735
|
{ className: "ai", onClick: () => void runAi("polish"), disabled: aiBusy !== null || !hasBody },
|
|
1419
1736
|
aiBusy === "polish" ? "\u6DA6\u8272\u4E2D\u2026" : "\u6DA6\u8272"
|
|
1420
1737
|
),
|
|
1421
|
-
(0,
|
|
1738
|
+
(0, import_react7.createElement)(
|
|
1422
1739
|
"button",
|
|
1423
1740
|
{ className: "ai", onClick: () => void runAi("shorten"), disabled: aiBusy !== null || !hasBody },
|
|
1424
1741
|
aiBusy === "shorten" ? "\u7F29\u77ED\u4E2D\u2026" : "\u7F29\u77ED"
|
|
1425
1742
|
),
|
|
1426
|
-
(0,
|
|
1743
|
+
(0, import_react7.createElement)(
|
|
1427
1744
|
"button",
|
|
1428
1745
|
{ onClick: () => setPendingLang("zh"), disabled: translating !== null, title: "AI \u7FFB\u8BD1\u6210\u4E2D\u6587" },
|
|
1429
1746
|
translating === "zh" ? "\u2026" : "\u4E2D"
|
|
1430
1747
|
),
|
|
1431
|
-
(0,
|
|
1748
|
+
(0, import_react7.createElement)(
|
|
1432
1749
|
"button",
|
|
1433
1750
|
{ onClick: () => setPendingLang("en"), disabled: translating !== null, title: "AI \u7FFB\u8BD1\u6210\u82F1\u6587" },
|
|
1434
1751
|
translating === "en" ? "\u2026" : "EN"
|
|
1435
1752
|
),
|
|
1436
|
-
(0,
|
|
1753
|
+
(0, import_react7.createElement)(
|
|
1437
1754
|
"label",
|
|
1438
1755
|
{ className: "mailharbor-toggle small" },
|
|
1439
1756
|
"\u9644\u4EF6",
|
|
1440
|
-
(0,
|
|
1757
|
+
(0, import_react7.createElement)("input", {
|
|
1441
1758
|
type: "file",
|
|
1442
1759
|
multiple: true,
|
|
1443
1760
|
style: { display: "none" },
|
|
@@ -1448,10 +1765,10 @@ function Composer({
|
|
|
1448
1765
|
})
|
|
1449
1766
|
)
|
|
1450
1767
|
),
|
|
1451
|
-
aiDraftOpen ? (0,
|
|
1768
|
+
aiDraftOpen ? (0, import_react7.createElement)(
|
|
1452
1769
|
"div",
|
|
1453
1770
|
{ className: "mailharbor-confirm" },
|
|
1454
|
-
(0,
|
|
1771
|
+
(0, import_react7.createElement)("input", {
|
|
1455
1772
|
value: aiInstruction,
|
|
1456
1773
|
placeholder: "\u60F3\u8BA9 AI \u5199\u4EC0\u4E48\uFF1F\u4F8B\u5982\uFF1A\u786E\u8BA4\u6536\u5230\uFF0C\u5468\u4E94\u524D\u7ED9\u7B54\u590D",
|
|
1457
1774
|
onChange: (event) => setAiInstruction(event.target.value),
|
|
@@ -1459,30 +1776,30 @@ function Composer({
|
|
|
1459
1776
|
if (event.key === "Enter") void runAi("draft");
|
|
1460
1777
|
}
|
|
1461
1778
|
}),
|
|
1462
|
-
(0,
|
|
1779
|
+
(0, import_react7.createElement)(
|
|
1463
1780
|
"button",
|
|
1464
1781
|
{ className: "primary", onClick: () => void runAi("draft"), disabled: aiBusy !== null },
|
|
1465
1782
|
aiBusy === "draft" ? "\u751F\u6210\u4E2D\u2026" : "\u751F\u6210"
|
|
1466
1783
|
),
|
|
1467
|
-
(0,
|
|
1784
|
+
(0, import_react7.createElement)("button", { className: "ghost", onClick: () => setAiDraftOpen(false) }, "\u53D6\u6D88")
|
|
1468
1785
|
) : null,
|
|
1469
|
-
pendingLang === null ? null : (0,
|
|
1786
|
+
pendingLang === null ? null : (0, import_react7.createElement)(
|
|
1470
1787
|
"div",
|
|
1471
1788
|
{ className: "mailharbor-confirm" },
|
|
1472
|
-
(0,
|
|
1473
|
-
(0,
|
|
1474
|
-
(0,
|
|
1475
|
-
(0,
|
|
1789
|
+
(0, import_react7.createElement)("span", null, pendingLang === "en" ? "\u628A\u6B63\u6587\u7FFB\u8BD1\u6210\u82F1\u6587\uFF1F" : "\u628A\u6B63\u6587\u7FFB\u8BD1\u6210\u4E2D\u6587\uFF1F"),
|
|
1790
|
+
(0, import_react7.createElement)("span", { className: "spacer" }),
|
|
1791
|
+
(0, import_react7.createElement)("button", { className: "ghost", onClick: () => setPendingLang(null) }, "\u53D6\u6D88"),
|
|
1792
|
+
(0, import_react7.createElement)("button", { className: "primary", onClick: () => void translate(pendingLang) }, "\u7FFB\u8BD1")
|
|
1476
1793
|
),
|
|
1477
|
-
attachments.length === 0 ? null : (0,
|
|
1794
|
+
attachments.length === 0 ? null : (0, import_react7.createElement)(
|
|
1478
1795
|
"div",
|
|
1479
1796
|
{ className: "mailharbor-attachments", style: { padding: 0, border: 0 } },
|
|
1480
1797
|
attachments.map(
|
|
1481
|
-
(file, index) => (0,
|
|
1798
|
+
(file, index) => (0, import_react7.createElement)(
|
|
1482
1799
|
"span",
|
|
1483
1800
|
{ className: "mailharbor-attachment", key: `${file.filename}:${String(index)}` },
|
|
1484
|
-
(0,
|
|
1485
|
-
(0,
|
|
1801
|
+
(0, import_react7.createElement)("span", { className: "name" }, file.filename),
|
|
1802
|
+
(0, import_react7.createElement)(
|
|
1486
1803
|
"button",
|
|
1487
1804
|
{
|
|
1488
1805
|
className: "subtle",
|
|
@@ -1496,7 +1813,7 @@ function Composer({
|
|
|
1496
1813
|
)
|
|
1497
1814
|
)
|
|
1498
1815
|
),
|
|
1499
|
-
(0,
|
|
1816
|
+
(0, import_react7.createElement)("div", {
|
|
1500
1817
|
className: "mailharbor-editor",
|
|
1501
1818
|
ref: editor,
|
|
1502
1819
|
contentEditable: true,
|
|
@@ -1505,12 +1822,12 @@ function Composer({
|
|
|
1505
1822
|
spellCheck: true,
|
|
1506
1823
|
onInput: markDirty
|
|
1507
1824
|
}),
|
|
1508
|
-
(0,
|
|
1825
|
+
(0, import_react7.createElement)(
|
|
1509
1826
|
"div",
|
|
1510
1827
|
{ className: "mailharbor-versions" },
|
|
1511
|
-
(0,
|
|
1828
|
+
(0, import_react7.createElement)("span", { className: "small muted" }, "\u7248\u672C"),
|
|
1512
1829
|
versions.map(
|
|
1513
|
-
(version, index) => (0,
|
|
1830
|
+
(version, index) => (0, import_react7.createElement)(
|
|
1514
1831
|
"button",
|
|
1515
1832
|
{
|
|
1516
1833
|
key: `${version.label}:${String(index)}`,
|
|
@@ -1521,19 +1838,19 @@ function Composer({
|
|
|
1521
1838
|
String(index + 1)
|
|
1522
1839
|
)
|
|
1523
1840
|
),
|
|
1524
|
-
versions.length > 1 ? (0,
|
|
1841
|
+
versions.length > 1 ? (0, import_react7.createElement)(
|
|
1525
1842
|
"button",
|
|
1526
1843
|
{ className: "subtle small", onClick: () => restoreVersion(Math.max(0, versionIndex - 1)) },
|
|
1527
1844
|
"\u6062\u590D\u4E0A\u4E00\u7248"
|
|
1528
1845
|
) : null,
|
|
1529
|
-
(0,
|
|
1530
|
-
(0,
|
|
1846
|
+
(0, import_react7.createElement)("span", { className: "spacer" }),
|
|
1847
|
+
(0, import_react7.createElement)("span", { className: "small muted" }, versions[versionIndex]?.label ?? "")
|
|
1531
1848
|
)
|
|
1532
1849
|
);
|
|
1533
1850
|
}
|
|
1534
1851
|
|
|
1535
1852
|
// src/client/reader.tsx
|
|
1536
|
-
var
|
|
1853
|
+
var import_react8 = require("react");
|
|
1537
1854
|
var IFRAME_STYLE = `
|
|
1538
1855
|
html, body { margin: 0; padding: 0; }
|
|
1539
1856
|
body {
|
|
@@ -1581,13 +1898,14 @@ function Reader({
|
|
|
1581
1898
|
onChanged,
|
|
1582
1899
|
onReply,
|
|
1583
1900
|
onAiAction,
|
|
1901
|
+
onExtractTasks,
|
|
1584
1902
|
onClose
|
|
1585
1903
|
}) {
|
|
1586
|
-
const [plain, setPlain] = (0,
|
|
1587
|
-
const [loadImages, setLoadImages] = (0,
|
|
1588
|
-
const [busy, setBusy] = (0,
|
|
1589
|
-
const [error, setError] = (0,
|
|
1590
|
-
const remoteImages = (0,
|
|
1904
|
+
const [plain, setPlain] = (0, import_react8.useState)(false);
|
|
1905
|
+
const [loadImages, setLoadImages] = (0, import_react8.useState)(false);
|
|
1906
|
+
const [busy, setBusy] = (0, import_react8.useState)(false);
|
|
1907
|
+
const [error, setError] = (0, import_react8.useState)("");
|
|
1908
|
+
const remoteImages = (0, import_react8.useMemo)(() => hasRemoteImages(detail), [detail]);
|
|
1591
1909
|
const html = detail.html !== null && detail.html !== "";
|
|
1592
1910
|
const toggleRead = async () => {
|
|
1593
1911
|
setBusy(true);
|
|
@@ -1607,22 +1925,22 @@ function Reader({
|
|
|
1607
1925
|
setBusy(false);
|
|
1608
1926
|
}
|
|
1609
1927
|
};
|
|
1610
|
-
return (0,
|
|
1928
|
+
return (0, import_react8.createElement)(
|
|
1611
1929
|
"div",
|
|
1612
1930
|
{ className: "mailharbor-reader" },
|
|
1613
|
-
(0,
|
|
1931
|
+
(0, import_react8.createElement)(
|
|
1614
1932
|
"div",
|
|
1615
1933
|
{ className: "mailharbor-reader-head" },
|
|
1616
|
-
(0,
|
|
1934
|
+
(0, import_react8.createElement)(
|
|
1617
1935
|
"div",
|
|
1618
1936
|
{ className: "mailharbor-reader-title" },
|
|
1619
|
-
onClose === void 0 ? null : (0,
|
|
1937
|
+
onClose === void 0 ? null : (0, import_react8.createElement)(
|
|
1620
1938
|
"button",
|
|
1621
1939
|
{ className: "ghost icon", onClick: onClose, "aria-label": "\u8FD4\u56DE\u5217\u8868", title: "\u8FD4\u56DE\u5217\u8868" },
|
|
1622
|
-
(0,
|
|
1940
|
+
(0, import_react8.createElement)(Icon, { name: "chevronLeft" })
|
|
1623
1941
|
),
|
|
1624
|
-
(0,
|
|
1625
|
-
(0,
|
|
1942
|
+
(0, import_react8.createElement)("h2", { style: { flex: 1 } }, detail.subject),
|
|
1943
|
+
(0, import_react8.createElement)(
|
|
1626
1944
|
"button",
|
|
1627
1945
|
{
|
|
1628
1946
|
className: "ghost icon",
|
|
@@ -1631,88 +1949,94 @@ function Reader({
|
|
|
1631
1949
|
title: detail.unread ? "\u6807\u4E3A\u5DF2\u8BFB" : "\u6807\u4E3A\u672A\u8BFB",
|
|
1632
1950
|
"aria-label": detail.unread ? "\u6807\u4E3A\u5DF2\u8BFB" : "\u6807\u4E3A\u672A\u8BFB"
|
|
1633
1951
|
},
|
|
1634
|
-
(0,
|
|
1952
|
+
(0, import_react8.createElement)(Icon, { name: "check" })
|
|
1635
1953
|
)
|
|
1636
1954
|
),
|
|
1637
|
-
(0,
|
|
1955
|
+
(0, import_react8.createElement)(
|
|
1638
1956
|
"div",
|
|
1639
1957
|
{ className: "mailharbor-reader-meta" },
|
|
1640
|
-
(0,
|
|
1958
|
+
(0, import_react8.createElement)(
|
|
1641
1959
|
"span",
|
|
1642
1960
|
{ className: "who" },
|
|
1643
|
-
(0,
|
|
1644
|
-
(0,
|
|
1645
|
-
detail.from.name !== "" ? (0,
|
|
1961
|
+
(0, import_react8.createElement)(Avatar, { email: detail.from.address, size: 22 }),
|
|
1962
|
+
(0, import_react8.createElement)("strong", null, detail.from.name !== "" ? detail.from.name : detail.from.address),
|
|
1963
|
+
detail.from.name !== "" ? (0, import_react8.createElement)("span", null, `<${detail.from.address}>`) : null
|
|
1646
1964
|
),
|
|
1647
|
-
(0,
|
|
1648
|
-
detail.backedUp === false ? (0,
|
|
1965
|
+
(0, import_react8.createElement)("span", null, longDate(detail.date)),
|
|
1966
|
+
detail.backedUp === false ? (0, import_react8.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null
|
|
1649
1967
|
),
|
|
1650
|
-
(0,
|
|
1968
|
+
(0, import_react8.createElement)(
|
|
1651
1969
|
"div",
|
|
1652
1970
|
{ className: "mailharbor-reader-meta" },
|
|
1653
|
-
(0,
|
|
1654
|
-
detail.cc.length === 0 ? null : (0,
|
|
1971
|
+
(0, import_react8.createElement)("span", null, `\u6536\u4EF6\u4EBA: ${addressLine(detail.to) || "\u2014"}`),
|
|
1972
|
+
detail.cc.length === 0 ? null : (0, import_react8.createElement)("span", null, `\u6284\u9001: ${addressLine(detail.cc)}`)
|
|
1655
1973
|
),
|
|
1656
|
-
(0,
|
|
1974
|
+
(0, import_react8.createElement)(
|
|
1657
1975
|
"div",
|
|
1658
1976
|
{ className: "mailharbor-reader-actions" },
|
|
1659
|
-
(0,
|
|
1977
|
+
(0, import_react8.createElement)(
|
|
1660
1978
|
"button",
|
|
1661
1979
|
{ className: "ghost", onClick: () => onReply("reply") },
|
|
1662
|
-
(0,
|
|
1980
|
+
(0, import_react8.createElement)(Icon, { name: "reply" }),
|
|
1663
1981
|
"\u56DE\u590D"
|
|
1664
1982
|
),
|
|
1665
|
-
(0,
|
|
1983
|
+
(0, import_react8.createElement)(
|
|
1666
1984
|
"button",
|
|
1667
1985
|
{ className: "ghost", onClick: () => onReply("replyAll") },
|
|
1668
|
-
(0,
|
|
1986
|
+
(0, import_react8.createElement)(Icon, { name: "replyAll" }),
|
|
1669
1987
|
"\u5168\u90E8\u56DE\u590D"
|
|
1670
1988
|
),
|
|
1671
|
-
(0,
|
|
1989
|
+
(0, import_react8.createElement)(
|
|
1672
1990
|
"button",
|
|
1673
1991
|
{ className: "ghost", onClick: () => onReply("forward") },
|
|
1674
|
-
(0,
|
|
1992
|
+
(0, import_react8.createElement)(Icon, { name: "forward" }),
|
|
1675
1993
|
"\u8F6C\u53D1"
|
|
1676
1994
|
),
|
|
1677
|
-
(0,
|
|
1678
|
-
(0,
|
|
1995
|
+
(0, import_react8.createElement)("span", { className: "mailharbor-divider" }),
|
|
1996
|
+
(0, import_react8.createElement)(
|
|
1679
1997
|
"button",
|
|
1680
1998
|
{ className: "ghost", onClick: () => onAiAction("summary") },
|
|
1681
|
-
(0,
|
|
1999
|
+
(0, import_react8.createElement)(Icon, { name: "sparkles", size: 14 }),
|
|
1682
2000
|
"AI \u6458\u8981"
|
|
1683
2001
|
),
|
|
1684
|
-
(0,
|
|
1685
|
-
(0,
|
|
1686
|
-
(0,
|
|
1687
|
-
|
|
2002
|
+
(0, import_react8.createElement)("button", { className: "ghost", onClick: () => onAiAction("reply") }, "AI \u56DE\u590D"),
|
|
2003
|
+
(0, import_react8.createElement)("button", { className: "ghost", onClick: () => onAiAction("translate") }, "AI \u7FFB\u8BD1"),
|
|
2004
|
+
(0, import_react8.createElement)(
|
|
2005
|
+
"button",
|
|
2006
|
+
{ className: "ghost", onClick: onExtractTasks },
|
|
2007
|
+
(0, import_react8.createElement)(Icon, { name: "checklist", size: 14 }),
|
|
2008
|
+
"\u63D0\u53D6\u4EFB\u52A1"
|
|
2009
|
+
),
|
|
2010
|
+
(0, import_react8.createElement)("span", { className: "spacer" }),
|
|
2011
|
+
html ? (0, import_react8.createElement)(
|
|
1688
2012
|
"button",
|
|
1689
2013
|
{ className: "subtle", onClick: () => setPlain(!plain) },
|
|
1690
2014
|
plain ? "\u663E\u793A HTML" : "\u7EAF\u6587\u672C"
|
|
1691
2015
|
) : null,
|
|
1692
|
-
remoteImages && !loadImages ? (0,
|
|
2016
|
+
remoteImages && !loadImages ? (0, import_react8.createElement)(
|
|
1693
2017
|
"button",
|
|
1694
2018
|
{ className: "subtle", onClick: () => setLoadImages(true) },
|
|
1695
|
-
(0,
|
|
2019
|
+
(0, import_react8.createElement)(Icon, { name: "image", size: 14 }),
|
|
1696
2020
|
"\u663E\u793A\u56FE\u7247"
|
|
1697
2021
|
) : null
|
|
1698
2022
|
),
|
|
1699
|
-
error === "" ? null : (0,
|
|
2023
|
+
error === "" ? null : (0, import_react8.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error)
|
|
1700
2024
|
),
|
|
1701
|
-
(0,
|
|
2025
|
+
(0, import_react8.createElement)(
|
|
1702
2026
|
"div",
|
|
1703
2027
|
{ className: "mailharbor-reader-body" },
|
|
1704
|
-
html && !plain ? (0,
|
|
2028
|
+
html && !plain ? (0, import_react8.createElement)("iframe", {
|
|
1705
2029
|
title: "\u90AE\u4EF6\u6B63\u6587",
|
|
1706
2030
|
sandbox: "",
|
|
1707
2031
|
srcDoc: srcDoc(detail, loadImages),
|
|
1708
2032
|
referrerPolicy: "no-referrer"
|
|
1709
|
-
}) : (0,
|
|
2033
|
+
}) : (0, import_react8.createElement)("pre", null, detail.text !== "" ? detail.text : "\uFF08\u65E0\u6B63\u6587\uFF09")
|
|
1710
2034
|
),
|
|
1711
|
-
detail.attachments.length === 0 ? null : (0,
|
|
2035
|
+
detail.attachments.length === 0 ? null : (0, import_react8.createElement)(
|
|
1712
2036
|
"div",
|
|
1713
2037
|
{ className: "mailharbor-attachments" },
|
|
1714
2038
|
detail.attachments.map(
|
|
1715
|
-
(file) => (0,
|
|
2039
|
+
(file) => (0, import_react8.createElement)(
|
|
1716
2040
|
"a",
|
|
1717
2041
|
{
|
|
1718
2042
|
className: "mailharbor-attachment",
|
|
@@ -1732,15 +2056,27 @@ function Reader({
|
|
|
1732
2056
|
download: file.filename,
|
|
1733
2057
|
title: file.filename
|
|
1734
2058
|
},
|
|
1735
|
-
(0,
|
|
1736
|
-
(0,
|
|
1737
|
-
(0,
|
|
2059
|
+
(0, import_react8.createElement)(Icon, { name: "paperclip", size: 14 }),
|
|
2060
|
+
(0, import_react8.createElement)("span", { className: "name" }, file.filename),
|
|
2061
|
+
(0, import_react8.createElement)("span", { className: "muted small" }, formatBytes(file.size))
|
|
1738
2062
|
)
|
|
1739
2063
|
)
|
|
1740
2064
|
)
|
|
1741
2065
|
);
|
|
1742
2066
|
}
|
|
1743
2067
|
|
|
2068
|
+
// src/client/nav.ts
|
|
2069
|
+
var pending = null;
|
|
2070
|
+
function requestOpenMail(request2) {
|
|
2071
|
+
pending = request2;
|
|
2072
|
+
window.dispatchEvent(new CustomEvent("mailharbor:open-mail", { detail: request2 }));
|
|
2073
|
+
}
|
|
2074
|
+
function consumeOpenMail() {
|
|
2075
|
+
const value = pending;
|
|
2076
|
+
pending = null;
|
|
2077
|
+
return value;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
1744
2080
|
// src/client/panel.tsx
|
|
1745
2081
|
var EMPTY_PAGE = { items: [], total: 0, offset: 0, limit: 40 };
|
|
1746
2082
|
var DRAFTS = "__drafts";
|
|
@@ -1755,43 +2091,162 @@ function quoteForReply(detail) {
|
|
|
1755
2091
|
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>`;
|
|
1756
2092
|
}
|
|
1757
2093
|
function MailPanel() {
|
|
1758
|
-
const [state, setState] = (0,
|
|
1759
|
-
const [error, setError] = (0,
|
|
1760
|
-
const [accountScope, setAccountScope] = (0,
|
|
1761
|
-
const [source, setSource] = (0,
|
|
1762
|
-
const [mailbox, setMailbox] = (0,
|
|
1763
|
-
const [folders, setFolders] = (0,
|
|
1764
|
-
const [query2, setQuery] = (0,
|
|
1765
|
-
const [search, setSearch] = (0,
|
|
1766
|
-
const [page, setPage] = (0,
|
|
1767
|
-
const [drafts, setDrafts] = (0,
|
|
1768
|
-
const [limit, setLimit] = (0,
|
|
1769
|
-
const [loading, setLoading] = (0,
|
|
1770
|
-
const [selected, setSelected] = (0,
|
|
1771
|
-
const [compose, setCompose] = (0,
|
|
1772
|
-
const [showAdd, setShowAdd] = (0,
|
|
1773
|
-
const [showBackup, setShowBackup] = (0,
|
|
1774
|
-
const [backup, setBackup] = (0,
|
|
1775
|
-
const [toast, setToast] = (0,
|
|
1776
|
-
const [narrow, setNarrow] = (0,
|
|
1777
|
-
const [railOpen, setRailOpen] = (0,
|
|
1778
|
-
const [
|
|
1779
|
-
const [
|
|
2094
|
+
const [state, setState] = (0, import_react9.useState)(null);
|
|
2095
|
+
const [error, setError] = (0, import_react9.useState)("");
|
|
2096
|
+
const [accountScope, setAccountScope] = (0, import_react9.useState)(null);
|
|
2097
|
+
const [source, setSource] = (0, import_react9.useState)("local");
|
|
2098
|
+
const [mailbox, setMailbox] = (0, import_react9.useState)("all");
|
|
2099
|
+
const [folders, setFolders] = (0, import_react9.useState)([]);
|
|
2100
|
+
const [query2, setQuery] = (0, import_react9.useState)("");
|
|
2101
|
+
const [search, setSearch] = (0, import_react9.useState)("");
|
|
2102
|
+
const [page, setPage] = (0, import_react9.useState)(EMPTY_PAGE);
|
|
2103
|
+
const [drafts, setDrafts] = (0, import_react9.useState)([]);
|
|
2104
|
+
const [limit, setLimit] = (0, import_react9.useState)(40);
|
|
2105
|
+
const [loading, setLoading] = (0, import_react9.useState)(false);
|
|
2106
|
+
const [selected, setSelected] = (0, import_react9.useState)(null);
|
|
2107
|
+
const [compose, setCompose] = (0, import_react9.useState)(null);
|
|
2108
|
+
const [showAdd, setShowAdd] = (0, import_react9.useState)(false);
|
|
2109
|
+
const [showBackup, setShowBackup] = (0, import_react9.useState)(false);
|
|
2110
|
+
const [backup, setBackup] = (0, import_react9.useState)(null);
|
|
2111
|
+
const [toast, setToast] = (0, import_react9.useState)("");
|
|
2112
|
+
const [narrow, setNarrow] = (0, import_react9.useState)(() => matchesNarrow());
|
|
2113
|
+
const [railOpen, setRailOpen] = (0, import_react9.useState)(false);
|
|
2114
|
+
const [selectMode, setSelectMode] = (0, import_react9.useState)(false);
|
|
2115
|
+
const [selectedKeys, setSelectedKeys] = (0, import_react9.useState)([]);
|
|
2116
|
+
const [projectOptions, setProjectOptions] = (0, import_react9.useState)([]);
|
|
2117
|
+
const [targetProject, setTargetProject] = (0, import_react9.useState)("");
|
|
2118
|
+
const [extraction, setExtraction] = (0, import_react9.useState)(null);
|
|
2119
|
+
const [extractBusy, setExtractBusy] = (0, import_react9.useState)(false);
|
|
2120
|
+
const [aiRequest, setAiRequest] = (0, import_react9.useState)(null);
|
|
2121
|
+
const [tick, setTick] = (0, import_react9.useState)(0);
|
|
1780
2122
|
const accounts = state?.accounts ?? [];
|
|
1781
2123
|
const isAll = accountScope === "all";
|
|
1782
2124
|
const account = accounts.find((item) => item.id === accountScope) ?? null;
|
|
1783
2125
|
const composeAccount = accounts.find((item) => item.id === compose?.accountId) ?? account ?? accounts[0] ?? null;
|
|
1784
2126
|
const showingDrafts = mailbox === DRAFTS && source === "local";
|
|
1785
|
-
const
|
|
2127
|
+
const keyOf = (item) => item.messageId !== void 0 && item.messageId !== "" ? `mid:${item.messageId}` : `local:${item.id}`;
|
|
2128
|
+
const openFromRequest = (0, import_react9.useCallback)((detail) => {
|
|
2129
|
+
setAccountScope(detail.accountId);
|
|
2130
|
+
setSource("local");
|
|
2131
|
+
setMailbox(detail.mailbox === "" ? "all" : detail.mailbox);
|
|
2132
|
+
api.activateAccount(detail.accountId).then(() => {
|
|
2133
|
+
setRailOpen(false);
|
|
2134
|
+
return api.message({
|
|
2135
|
+
id: detail.id,
|
|
2136
|
+
accountId: detail.accountId,
|
|
2137
|
+
source: "local",
|
|
2138
|
+
mailbox: detail.mailbox === "" ? "all" : detail.mailbox
|
|
2139
|
+
});
|
|
2140
|
+
}).then((message) => setSelected(message)).catch((caught) => setError(errorText5(caught)));
|
|
2141
|
+
}, []);
|
|
2142
|
+
(0, import_react9.useEffect)(() => {
|
|
2143
|
+
const queued = consumeOpenMail();
|
|
2144
|
+
if (queued !== null) openFromRequest(queued);
|
|
2145
|
+
const onOpen = (event) => {
|
|
2146
|
+
const detail = event.detail;
|
|
2147
|
+
if (detail !== void 0) openFromRequest(detail);
|
|
2148
|
+
};
|
|
2149
|
+
window.addEventListener("mailharbor:open-mail", onOpen);
|
|
2150
|
+
return () => {
|
|
2151
|
+
window.removeEventListener("mailharbor:open-mail", onOpen);
|
|
2152
|
+
};
|
|
2153
|
+
}, [openFromRequest]);
|
|
2154
|
+
const enterSelect = async () => {
|
|
2155
|
+
setSelectMode(true);
|
|
2156
|
+
try {
|
|
2157
|
+
const list = await api.projects();
|
|
2158
|
+
setProjectOptions(list.items);
|
|
2159
|
+
setTargetProject(list.items[0]?.id ?? "");
|
|
2160
|
+
} catch (caught) {
|
|
2161
|
+
setError(errorText5(caught));
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
const toggleSelect = (item) => {
|
|
2165
|
+
const key = `${item.accountId}:${item.id}`;
|
|
2166
|
+
setSelectedKeys(
|
|
2167
|
+
(current) => current.includes(key) ? current.filter((entry) => entry !== key) : [...current, key]
|
|
2168
|
+
);
|
|
2169
|
+
};
|
|
2170
|
+
const addSelectedToProject = async () => {
|
|
2171
|
+
if (targetProject === "") {
|
|
2172
|
+
setError("\u5148\u521B\u5EFA\u4E00\u4E2A\u9879\u76EE");
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
const chosen = items.filter((item) => selectedKeys.includes(`${item.accountId}:${item.id}`));
|
|
2176
|
+
if (chosen.length === 0) return;
|
|
2177
|
+
try {
|
|
2178
|
+
for (const item of chosen) {
|
|
2179
|
+
await api.addProjectMessage(targetProject, {
|
|
2180
|
+
accountId: item.accountId,
|
|
2181
|
+
key: keyOf(item),
|
|
2182
|
+
subject: item.subject,
|
|
2183
|
+
date: item.date
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
notice(`\u5DF2\u52A0\u5165\u9879\u76EE\uFF1A${String(chosen.length)} \u5C01`);
|
|
2187
|
+
setSelectMode(false);
|
|
2188
|
+
setSelectedKeys([]);
|
|
2189
|
+
} catch (caught) {
|
|
2190
|
+
setError(errorText5(caught));
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
const extractTasks = async () => {
|
|
2194
|
+
if (selected === null) return;
|
|
2195
|
+
setExtractBusy(true);
|
|
2196
|
+
try {
|
|
2197
|
+
const result = await api.extractTasks({
|
|
2198
|
+
accountId: selected.accountId,
|
|
2199
|
+
source,
|
|
2200
|
+
id: selected.id,
|
|
2201
|
+
mailbox
|
|
2202
|
+
});
|
|
2203
|
+
if (result.suggestions.length === 0) {
|
|
2204
|
+
notice("\u8FD9\u5C01\u90AE\u4EF6\u6CA1\u6709\u63D0\u53D6\u5230\u660E\u786E\u4EFB\u52A1");
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
setExtraction({ items: result.suggestions, checked: result.suggestions.map(() => true) });
|
|
2208
|
+
} catch (caught) {
|
|
2209
|
+
setError(errorText5(caught));
|
|
2210
|
+
} finally {
|
|
2211
|
+
setExtractBusy(false);
|
|
2212
|
+
}
|
|
2213
|
+
};
|
|
2214
|
+
const saveExtraction = async () => {
|
|
2215
|
+
if (extraction === null || selected === null) return;
|
|
2216
|
+
setExtractBusy(true);
|
|
2217
|
+
try {
|
|
2218
|
+
const key = keyOf(selected);
|
|
2219
|
+
for (const [index, suggestion] of extraction.items.entries()) {
|
|
2220
|
+
if (!extraction.checked[index]) continue;
|
|
2221
|
+
await api.createTask({
|
|
2222
|
+
accountId: selected.accountId,
|
|
2223
|
+
title: suggestion.title,
|
|
2224
|
+
notes: suggestion.notes,
|
|
2225
|
+
due: suggestion.due,
|
|
2226
|
+
priority: suggestion.priority,
|
|
2227
|
+
owner: suggestion.owner,
|
|
2228
|
+
messageKey: key,
|
|
2229
|
+
messageSubject: selected.subject
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
setExtraction(null);
|
|
2233
|
+
notice("\u4EFB\u52A1\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u5728\u300C\u4EFB\u52A1\u300D\u9762\u677F\u67E5\u770B");
|
|
2234
|
+
} catch (caught) {
|
|
2235
|
+
setError(errorText5(caught));
|
|
2236
|
+
} finally {
|
|
2237
|
+
setExtractBusy(false);
|
|
2238
|
+
}
|
|
2239
|
+
};
|
|
2240
|
+
const refresh = (0, import_react9.useCallback)(() => {
|
|
1786
2241
|
setTick((value) => value + 1);
|
|
1787
2242
|
}, []);
|
|
1788
|
-
const notice = (0,
|
|
2243
|
+
const notice = (0, import_react9.useCallback)((message) => {
|
|
1789
2244
|
setToast(message);
|
|
1790
2245
|
window.setTimeout(() => {
|
|
1791
2246
|
setToast("");
|
|
1792
2247
|
}, 3200);
|
|
1793
2248
|
}, []);
|
|
1794
|
-
(0,
|
|
2249
|
+
(0, import_react9.useEffect)(() => {
|
|
1795
2250
|
let cancelled = false;
|
|
1796
2251
|
api.state().then((next) => {
|
|
1797
2252
|
if (cancelled) return;
|
|
@@ -1805,7 +2260,7 @@ function MailPanel() {
|
|
|
1805
2260
|
cancelled = true;
|
|
1806
2261
|
};
|
|
1807
2262
|
}, [tick]);
|
|
1808
|
-
(0,
|
|
2263
|
+
(0, import_react9.useEffect)(() => {
|
|
1809
2264
|
if (accountScope === null) return;
|
|
1810
2265
|
let cancelled = false;
|
|
1811
2266
|
api.folders({ accountId: accountScope, source }).then((result) => {
|
|
@@ -1829,16 +2284,16 @@ function MailPanel() {
|
|
|
1829
2284
|
cancelled = true;
|
|
1830
2285
|
};
|
|
1831
2286
|
}, [accountScope, source, tick]);
|
|
1832
|
-
(0,
|
|
2287
|
+
(0, import_react9.useEffect)(() => {
|
|
1833
2288
|
setMailbox(source === "local" ? "all" : "INBOX");
|
|
1834
2289
|
setSelected(null);
|
|
1835
2290
|
setLimit(state?.settings.render.listLimit ?? 40);
|
|
1836
2291
|
}, [source]);
|
|
1837
|
-
(0,
|
|
2292
|
+
(0, import_react9.useEffect)(() => {
|
|
1838
2293
|
if (accountScope === "all" && source === "server") setSource("local");
|
|
1839
2294
|
setSelected(null);
|
|
1840
2295
|
}, [accountScope, source]);
|
|
1841
|
-
(0,
|
|
2296
|
+
(0, import_react9.useEffect)(() => {
|
|
1842
2297
|
if (accountScope === null) return;
|
|
1843
2298
|
let cancelled = false;
|
|
1844
2299
|
setLoading(true);
|
|
@@ -1878,7 +2333,7 @@ function MailPanel() {
|
|
|
1878
2333
|
cancelled = true;
|
|
1879
2334
|
};
|
|
1880
2335
|
}, [accountScope, source, mailbox, search, limit, tick]);
|
|
1881
|
-
(0,
|
|
2336
|
+
(0, import_react9.useEffect)(() => {
|
|
1882
2337
|
if (typeof window.matchMedia !== "function") return;
|
|
1883
2338
|
const media = window.matchMedia("(max-width: 900px)");
|
|
1884
2339
|
const onChange = () => setNarrow(media.matches);
|
|
@@ -1887,7 +2342,7 @@ function MailPanel() {
|
|
|
1887
2342
|
media.removeEventListener("change", onChange);
|
|
1888
2343
|
};
|
|
1889
2344
|
}, []);
|
|
1890
|
-
(0,
|
|
2345
|
+
(0, import_react9.useEffect)(() => {
|
|
1891
2346
|
if (!showBackup || accountScope === null || isAll) return;
|
|
1892
2347
|
let cancelled = false;
|
|
1893
2348
|
const poll = async () => {
|
|
@@ -2055,7 +2510,7 @@ function MailPanel() {
|
|
|
2055
2510
|
setError(errorText5(caught));
|
|
2056
2511
|
}
|
|
2057
2512
|
};
|
|
2058
|
-
const folderOptions = (0,
|
|
2513
|
+
const folderOptions = (0, import_react9.useMemo)(
|
|
2059
2514
|
() => folders.map((folder) => ({
|
|
2060
2515
|
value: folder.name,
|
|
2061
2516
|
label: `${folder.label ?? folder.name}${folder.count > 0 ? ` (${String(folder.count)})` : ""}`
|
|
@@ -2063,27 +2518,27 @@ function MailPanel() {
|
|
|
2063
2518
|
[folders]
|
|
2064
2519
|
);
|
|
2065
2520
|
if (state === null) {
|
|
2066
|
-
return (0,
|
|
2521
|
+
return (0, import_react9.createElement)(
|
|
2067
2522
|
"div",
|
|
2068
2523
|
{ className: "mailharbor" },
|
|
2069
|
-
(0,
|
|
2524
|
+
(0, import_react9.createElement)("div", { className: "mailharbor-empty" }, error === "" ? "\u6B63\u5728\u52A0\u8F7D\u90AE\u7BB1\u2026" : error)
|
|
2070
2525
|
);
|
|
2071
2526
|
}
|
|
2072
2527
|
if (accounts.length === 0) {
|
|
2073
|
-
return (0,
|
|
2528
|
+
return (0, import_react9.createElement)(
|
|
2074
2529
|
"div",
|
|
2075
2530
|
{ className: "mailharbor" },
|
|
2076
|
-
(0,
|
|
2531
|
+
(0, import_react9.createElement)(
|
|
2077
2532
|
"div",
|
|
2078
2533
|
{ className: "mailharbor-settings" },
|
|
2079
|
-
(0,
|
|
2080
|
-
(0,
|
|
2534
|
+
(0, import_react9.createElement)("h2", null, "\u6B22\u8FCE\u4F7F\u7528 MailHarbor"),
|
|
2535
|
+
(0, import_react9.createElement)(
|
|
2081
2536
|
"p",
|
|
2082
2537
|
{ className: "muted small" },
|
|
2083
2538
|
"\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"
|
|
2084
2539
|
),
|
|
2085
|
-
error === "" ? null : (0,
|
|
2086
|
-
(0,
|
|
2540
|
+
error === "" ? null : (0, import_react9.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
2541
|
+
(0, import_react9.createElement)(AddAccountForm, {
|
|
2087
2542
|
onDone: (next) => {
|
|
2088
2543
|
setState(next);
|
|
2089
2544
|
setAccountScope(next.activeAccountId);
|
|
@@ -2097,38 +2552,38 @@ function MailPanel() {
|
|
|
2097
2552
|
const loggedIn = isAll ? accounts.some((item) => item.id === state.activeAccountId) : state.loggedIn;
|
|
2098
2553
|
const needsLogin = source === "server" && !isAll && state.loggedIn === false;
|
|
2099
2554
|
const items = page.items;
|
|
2100
|
-
return (0,
|
|
2555
|
+
return (0, import_react9.createElement)(
|
|
2101
2556
|
"div",
|
|
2102
2557
|
{ className: narrow ? "mailharbor mailharbor-narrow" : "mailharbor" },
|
|
2103
|
-
(0,
|
|
2558
|
+
(0, import_react9.createElement)(
|
|
2104
2559
|
"div",
|
|
2105
2560
|
{ className: "mailharbor-topbar" },
|
|
2106
|
-
(0,
|
|
2561
|
+
(0, import_react9.createElement)(
|
|
2107
2562
|
"div",
|
|
2108
2563
|
{ className: "mailharbor-account" },
|
|
2109
|
-
(0,
|
|
2110
|
-
accounts.length > 1 ? (0,
|
|
2564
|
+
(0, import_react9.createElement)(Avatar, { email: isAll ? "all" : currentAccount?.email ?? "?", size: 26 }),
|
|
2565
|
+
accounts.length > 1 ? (0, import_react9.createElement)(
|
|
2111
2566
|
"select",
|
|
2112
2567
|
{
|
|
2113
2568
|
value: accountScope ?? "",
|
|
2114
2569
|
onChange: (event) => void switchAccount(event.target.value),
|
|
2115
2570
|
"aria-label": "\u8D26\u53F7"
|
|
2116
2571
|
},
|
|
2117
|
-
(0,
|
|
2118
|
-
accounts.map((item) => (0,
|
|
2119
|
-
) : (0,
|
|
2120
|
-
isAll ? (0,
|
|
2121
|
-
!isAll && state.loggedIn === false ? (0,
|
|
2572
|
+
(0, import_react9.createElement)("option", { key: "all", value: "all" }, "\u5168\u90E8\u8D26\u53F7"),
|
|
2573
|
+
accounts.map((item) => (0, import_react9.createElement)("option", { key: item.id, value: item.id }, item.label))
|
|
2574
|
+
) : (0, import_react9.createElement)("span", { className: "name" }, currentAccount?.label ?? ""),
|
|
2575
|
+
isAll ? (0, import_react9.createElement)("span", { className: "badge" }, `${String(accounts.length)} \u4E2A\u8D26\u53F7`) : null,
|
|
2576
|
+
!isAll && state.loggedIn === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null
|
|
2122
2577
|
),
|
|
2123
|
-
(0,
|
|
2578
|
+
(0, import_react9.createElement)(
|
|
2124
2579
|
"div",
|
|
2125
2580
|
{ className: "mailharbor-segment", role: "tablist" },
|
|
2126
|
-
(0,
|
|
2581
|
+
(0, import_react9.createElement)(
|
|
2127
2582
|
"button",
|
|
2128
2583
|
{ className: source === "local" ? "active" : "", onClick: () => setSource("local"), role: "tab" },
|
|
2129
2584
|
"\u672C\u5730\u5907\u4EFD"
|
|
2130
2585
|
),
|
|
2131
|
-
(0,
|
|
2586
|
+
(0, import_react9.createElement)(
|
|
2132
2587
|
"button",
|
|
2133
2588
|
{
|
|
2134
2589
|
className: source === "server" ? "active" : "",
|
|
@@ -2145,8 +2600,8 @@ function MailPanel() {
|
|
|
2145
2600
|
"\u670D\u52A1\u5668\u5B9E\u65F6"
|
|
2146
2601
|
)
|
|
2147
2602
|
),
|
|
2148
|
-
(0,
|
|
2149
|
-
(0,
|
|
2603
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2604
|
+
(0, import_react9.createElement)(
|
|
2150
2605
|
"button",
|
|
2151
2606
|
{
|
|
2152
2607
|
className: "ghost icon",
|
|
@@ -2154,56 +2609,56 @@ function MailPanel() {
|
|
|
2154
2609
|
"aria-label": "\u6DFB\u52A0\u8D26\u53F7",
|
|
2155
2610
|
onClick: () => setShowAdd(true)
|
|
2156
2611
|
},
|
|
2157
|
-
(0,
|
|
2612
|
+
(0, import_react9.createElement)(Icon, { name: "plus" })
|
|
2158
2613
|
),
|
|
2159
|
-
(0,
|
|
2614
|
+
(0, import_react9.createElement)(
|
|
2160
2615
|
"button",
|
|
2161
2616
|
{ className: "ghost icon", title: "\u5237\u65B0\u5217\u8868", "aria-label": "\u5237\u65B0\u5217\u8868", onClick: refresh },
|
|
2162
|
-
(0,
|
|
2617
|
+
(0, import_react9.createElement)(Icon, { name: "refresh" })
|
|
2163
2618
|
),
|
|
2164
|
-
(0,
|
|
2619
|
+
(0, import_react9.createElement)(
|
|
2165
2620
|
"button",
|
|
2166
2621
|
{
|
|
2167
2622
|
className: "ghost",
|
|
2168
2623
|
onClick: () => void startBackup(),
|
|
2169
2624
|
title: isAll ? "\u5907\u4EFD\u5F53\u524D\u6D3B\u52A8\u8D26\u53F7" : "\u5907\u4EFD\u8FD9\u4E2A\u8D26\u53F7"
|
|
2170
2625
|
},
|
|
2171
|
-
(0,
|
|
2626
|
+
(0, import_react9.createElement)(Icon, { name: "archive" }),
|
|
2172
2627
|
"\u5907\u4EFD"
|
|
2173
2628
|
),
|
|
2174
|
-
(0,
|
|
2629
|
+
(0, import_react9.createElement)(
|
|
2175
2630
|
"button",
|
|
2176
2631
|
{
|
|
2177
2632
|
className: railOpen ? "ghost active" : "ghost",
|
|
2178
2633
|
onClick: () => setRailOpen(!railOpen),
|
|
2179
2634
|
title: "AI \u5DE5\u4F5C\u53F0"
|
|
2180
2635
|
},
|
|
2181
|
-
(0,
|
|
2636
|
+
(0, import_react9.createElement)(Icon, { name: "sparkles" }),
|
|
2182
2637
|
"AI"
|
|
2183
2638
|
),
|
|
2184
|
-
(0,
|
|
2639
|
+
(0, import_react9.createElement)(
|
|
2185
2640
|
"button",
|
|
2186
2641
|
{ className: "primary", onClick: () => startCompose("new") },
|
|
2187
|
-
(0,
|
|
2642
|
+
(0, import_react9.createElement)(Icon, { name: "compose" }),
|
|
2188
2643
|
"\u5199\u90AE\u4EF6"
|
|
2189
2644
|
)
|
|
2190
2645
|
),
|
|
2191
|
-
toast === "" ? null : (0,
|
|
2192
|
-
error === "" ? null : (0,
|
|
2193
|
-
needsLogin ? (0,
|
|
2646
|
+
toast === "" ? null : (0, import_react9.createElement)("div", { className: "small", style: { padding: "6px 14px", color: "var(--m-ok)" } }, toast),
|
|
2647
|
+
error === "" ? null : (0, import_react9.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
|
|
2648
|
+
needsLogin ? (0, import_react9.createElement)(
|
|
2194
2649
|
"div",
|
|
2195
2650
|
{ className: "mailharbor-settings" },
|
|
2196
|
-
(0,
|
|
2197
|
-
) : (0,
|
|
2651
|
+
(0, import_react9.createElement)(LoginForm, { account: currentAccount, onDone: refresh })
|
|
2652
|
+
) : (0, import_react9.createElement)(
|
|
2198
2653
|
"div",
|
|
2199
2654
|
{ className: "mailharbor-body", "data-mode": narrow && selected !== null ? "reader" : "list" },
|
|
2200
|
-
(0,
|
|
2655
|
+
(0, import_react9.createElement)(
|
|
2201
2656
|
"div",
|
|
2202
2657
|
{ className: "mailharbor-list" },
|
|
2203
|
-
(0,
|
|
2658
|
+
(0, import_react9.createElement)(
|
|
2204
2659
|
"div",
|
|
2205
2660
|
{ className: "mailharbor-filters" },
|
|
2206
|
-
(0,
|
|
2661
|
+
(0, import_react9.createElement)(
|
|
2207
2662
|
"select",
|
|
2208
2663
|
{
|
|
2209
2664
|
value: mailbox,
|
|
@@ -2214,10 +2669,10 @@ function MailPanel() {
|
|
|
2214
2669
|
"aria-label": "\u90AE\u7BB1\u6587\u4EF6\u5939"
|
|
2215
2670
|
},
|
|
2216
2671
|
folderOptions.map(
|
|
2217
|
-
(option) => (0,
|
|
2672
|
+
(option) => (0, import_react9.createElement)("option", { key: option.value, value: option.value }, option.label)
|
|
2218
2673
|
)
|
|
2219
2674
|
),
|
|
2220
|
-
(0,
|
|
2675
|
+
(0, import_react9.createElement)("input", {
|
|
2221
2676
|
type: "search",
|
|
2222
2677
|
placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : "\u641C\u7D22\u90AE\u4EF6",
|
|
2223
2678
|
"aria-label": "\u641C\u7D22\u90AE\u4EF6",
|
|
@@ -2231,7 +2686,24 @@ function MailPanel() {
|
|
|
2231
2686
|
}
|
|
2232
2687
|
}
|
|
2233
2688
|
}),
|
|
2234
|
-
|
|
2689
|
+
source === "local" ? (0, import_react9.createElement)(
|
|
2690
|
+
"button",
|
|
2691
|
+
{
|
|
2692
|
+
className: selectMode ? "ghost active" : "ghost",
|
|
2693
|
+
title: "\u9009\u62E9\u591A\u5C01\u90AE\u4EF6\u52A0\u5165\u9879\u76EE",
|
|
2694
|
+
onClick: () => {
|
|
2695
|
+
if (selectMode) {
|
|
2696
|
+
setSelectMode(false);
|
|
2697
|
+
setSelectedKeys([]);
|
|
2698
|
+
} else {
|
|
2699
|
+
void enterSelect();
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
},
|
|
2703
|
+
Icon({ name: "checklist", size: 14 }),
|
|
2704
|
+
"\u9009\u62E9"
|
|
2705
|
+
) : null,
|
|
2706
|
+
query2 !== search || search !== "" ? (0, import_react9.createElement)(
|
|
2235
2707
|
"button",
|
|
2236
2708
|
{
|
|
2237
2709
|
className: "subtle",
|
|
@@ -2241,22 +2713,60 @@ function MailPanel() {
|
|
|
2241
2713
|
setSearch("");
|
|
2242
2714
|
}
|
|
2243
2715
|
},
|
|
2244
|
-
(0,
|
|
2716
|
+
(0, import_react9.createElement)(Icon, { name: "close", size: 14 })
|
|
2245
2717
|
) : null
|
|
2246
2718
|
),
|
|
2247
|
-
(0,
|
|
2719
|
+
selectMode ? (0, import_react9.createElement)(
|
|
2720
|
+
"div",
|
|
2721
|
+
{ className: "mailharbor-selectbar" },
|
|
2722
|
+
(0, import_react9.createElement)("span", { className: "small" }, `\u5DF2\u9009 ${String(selectedKeys.length)} \u5C01`),
|
|
2723
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2724
|
+
(0, import_react9.createElement)(
|
|
2725
|
+
"select",
|
|
2726
|
+
{
|
|
2727
|
+
value: targetProject,
|
|
2728
|
+
onChange: (event) => setTargetProject(event.target.value),
|
|
2729
|
+
"aria-label": "\u76EE\u6807\u9879\u76EE"
|
|
2730
|
+
},
|
|
2731
|
+
(0, import_react9.createElement)("option", { value: "" }, projectOptions.length === 0 ? "\u8FD8\u6CA1\u6709\u9879\u76EE" : "\u9009\u62E9\u9879\u76EE"),
|
|
2732
|
+
projectOptions.map(
|
|
2733
|
+
(project) => (0, import_react9.createElement)("option", { key: project.id, value: project.id }, project.name)
|
|
2734
|
+
)
|
|
2735
|
+
),
|
|
2736
|
+
(0, import_react9.createElement)(
|
|
2737
|
+
"button",
|
|
2738
|
+
{
|
|
2739
|
+
className: "primary",
|
|
2740
|
+
disabled: selectedKeys.length === 0 || targetProject === "",
|
|
2741
|
+
onClick: () => void addSelectedToProject()
|
|
2742
|
+
},
|
|
2743
|
+
"\u52A0\u5165\u9879\u76EE"
|
|
2744
|
+
),
|
|
2745
|
+
(0, import_react9.createElement)(
|
|
2746
|
+
"button",
|
|
2747
|
+
{
|
|
2748
|
+
className: "ghost",
|
|
2749
|
+
onClick: () => {
|
|
2750
|
+
setSelectMode(false);
|
|
2751
|
+
setSelectedKeys([]);
|
|
2752
|
+
}
|
|
2753
|
+
},
|
|
2754
|
+
"\u53D6\u6D88"
|
|
2755
|
+
)
|
|
2756
|
+
) : null,
|
|
2757
|
+
(0, import_react9.createElement)(
|
|
2248
2758
|
"div",
|
|
2249
2759
|
{ className: "mailharbor-rows" },
|
|
2250
|
-
loading && items.length === 0 && drafts.length === 0 ? (0,
|
|
2760
|
+
loading && items.length === 0 && drafts.length === 0 ? (0, import_react9.createElement)("div", { className: "mailharbor-empty" }, "\u6B63\u5728\u8BFB\u53D6\u2026") : showingDrafts ? drafts.length === 0 ? (0, import_react9.createElement)(
|
|
2251
2761
|
"div",
|
|
2252
2762
|
{ className: "mailharbor-empty" },
|
|
2253
|
-
(0,
|
|
2254
|
-
(0,
|
|
2255
|
-
(0,
|
|
2763
|
+
(0, import_react9.createElement)(Icon, { name: "compose", size: 28 }),
|
|
2764
|
+
(0, import_react9.createElement)("div", { className: "title" }, "\u6CA1\u6709\u8349\u7A3F"),
|
|
2765
|
+
(0, import_react9.createElement)("div", { className: "small" }, "\u5199\u4FE1\u65F6\u70B9\u300C\u5B58\u8349\u7A3F\u300D\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002")
|
|
2256
2766
|
) : drafts.filter(
|
|
2257
2767
|
(draft) => search === "" ? true : `${draft.subject} ${draft.to} ${draft.cc}`.toLowerCase().includes(search.toLowerCase())
|
|
2258
2768
|
).map(
|
|
2259
|
-
(draft) => (0,
|
|
2769
|
+
(draft) => (0, import_react9.createElement)(
|
|
2260
2770
|
"div",
|
|
2261
2771
|
{
|
|
2262
2772
|
key: draft.id,
|
|
@@ -2271,21 +2781,21 @@ function MailPanel() {
|
|
|
2271
2781
|
}
|
|
2272
2782
|
}
|
|
2273
2783
|
},
|
|
2274
|
-
(0,
|
|
2784
|
+
(0, import_react9.createElement)(
|
|
2275
2785
|
"span",
|
|
2276
2786
|
{ className: "line-1" },
|
|
2277
|
-
(0,
|
|
2787
|
+
(0, import_react9.createElement)(
|
|
2278
2788
|
"span",
|
|
2279
2789
|
{ className: "from" },
|
|
2280
2790
|
`\u8349\u7A3F${draft.to === "" ? "" : ` \xB7 \u6536\u4EF6\u4EBA ${draft.to}`}`
|
|
2281
2791
|
),
|
|
2282
|
-
isAll ? (0,
|
|
2792
|
+
isAll ? (0, import_react9.createElement)(
|
|
2283
2793
|
"span",
|
|
2284
2794
|
{ className: "badge" },
|
|
2285
2795
|
accounts.find((item) => item.id === draft.accountId)?.label ?? ""
|
|
2286
2796
|
) : null,
|
|
2287
|
-
(0,
|
|
2288
|
-
(0,
|
|
2797
|
+
(0, import_react9.createElement)("span", { className: "date" }, shortDate(draft.updatedAt)),
|
|
2798
|
+
(0, import_react9.createElement)(
|
|
2289
2799
|
"button",
|
|
2290
2800
|
{
|
|
2291
2801
|
className: "subtle",
|
|
@@ -2295,121 +2805,132 @@ function MailPanel() {
|
|
|
2295
2805
|
void removeDraft(draft.id, draft.accountId);
|
|
2296
2806
|
}
|
|
2297
2807
|
},
|
|
2298
|
-
(0,
|
|
2808
|
+
(0, import_react9.createElement)(Icon, { name: "close", size: 13 })
|
|
2299
2809
|
)
|
|
2300
2810
|
),
|
|
2301
|
-
(0,
|
|
2811
|
+
(0, import_react9.createElement)(
|
|
2302
2812
|
"span",
|
|
2303
2813
|
{ className: "line-2" },
|
|
2304
|
-
(0,
|
|
2814
|
+
(0, import_react9.createElement)(
|
|
2305
2815
|
"span",
|
|
2306
2816
|
{ className: "subject" },
|
|
2307
2817
|
draft.subject === "" ? "(\u65E0\u4E3B\u9898)" : draft.subject
|
|
2308
2818
|
)
|
|
2309
2819
|
),
|
|
2310
|
-
draft.html === "" ? null : (0,
|
|
2820
|
+
draft.html === "" ? null : (0, import_react9.createElement)(
|
|
2311
2821
|
"span",
|
|
2312
2822
|
{ className: "snippet" },
|
|
2313
2823
|
draft.html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 120)
|
|
2314
2824
|
)
|
|
2315
2825
|
)
|
|
2316
|
-
) : items.length === 0 ? (0,
|
|
2826
|
+
) : items.length === 0 ? (0, import_react9.createElement)(
|
|
2317
2827
|
"div",
|
|
2318
2828
|
{ className: "mailharbor-empty" },
|
|
2319
|
-
(0,
|
|
2320
|
-
(0,
|
|
2829
|
+
(0, import_react9.createElement)(Icon, { name: "mail", size: 28 }),
|
|
2830
|
+
(0, import_react9.createElement)(
|
|
2321
2831
|
"div",
|
|
2322
2832
|
{ className: "title" },
|
|
2323
2833
|
source === "local" ? "\u672C\u5730\u5907\u4EFD\u91CC\u8FD8\u6CA1\u6709\u90AE\u4EF6" : "\u670D\u52A1\u5668\u4E0A\u6CA1\u6709\u90AE\u4EF6"
|
|
2324
2834
|
),
|
|
2325
|
-
(0,
|
|
2835
|
+
(0, import_react9.createElement)(
|
|
2326
2836
|
"div",
|
|
2327
2837
|
{ className: "small" },
|
|
2328
2838
|
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"
|
|
2329
2839
|
)
|
|
2330
2840
|
) : items.map(
|
|
2331
|
-
(item) => (0,
|
|
2841
|
+
(item) => (0, import_react9.createElement)(
|
|
2332
2842
|
"div",
|
|
2333
2843
|
{
|
|
2334
2844
|
key: `${item.accountId}:${item.id}`,
|
|
2335
|
-
className: `mailharbor-row${item.unread ? " unread" : ""}${selected?.id === item.id && selected?.accountId === item.accountId ? " selected" : ""}`,
|
|
2845
|
+
className: `mailharbor-row${item.unread ? " unread" : ""}${selected?.id === item.id && selected?.accountId === item.accountId ? " selected" : ""}${selectMode ? " selectable" : ""}`,
|
|
2336
2846
|
role: "button",
|
|
2337
2847
|
tabIndex: 0,
|
|
2338
|
-
onClick: () =>
|
|
2848
|
+
onClick: () => {
|
|
2849
|
+
if (selectMode) toggleSelect(item);
|
|
2850
|
+
else void openMessage(item);
|
|
2851
|
+
},
|
|
2339
2852
|
onKeyDown: (event) => {
|
|
2340
2853
|
if (event.key === "Enter" || event.key === " ") {
|
|
2341
2854
|
event.preventDefault();
|
|
2342
|
-
|
|
2855
|
+
if (selectMode) toggleSelect(item);
|
|
2856
|
+
else void openMessage(item);
|
|
2343
2857
|
}
|
|
2344
2858
|
}
|
|
2345
2859
|
},
|
|
2346
|
-
(0,
|
|
2860
|
+
(0, import_react9.createElement)(
|
|
2347
2861
|
"span",
|
|
2348
2862
|
{ className: "line-1" },
|
|
2349
|
-
|
|
2350
|
-
|
|
2863
|
+
selectMode ? (0, import_react9.createElement)("input", {
|
|
2864
|
+
type: "checkbox",
|
|
2865
|
+
checked: selectedKeys.includes(`${item.accountId}:${item.id}`),
|
|
2866
|
+
"aria-label": "\u9009\u62E9\u90AE\u4EF6",
|
|
2867
|
+
onClick: (event) => event.stopPropagation(),
|
|
2868
|
+
onChange: () => toggleSelect(item)
|
|
2869
|
+
}) : item.unread ? (0, import_react9.createElement)("span", { className: "dot" }) : null,
|
|
2870
|
+
(0, import_react9.createElement)(
|
|
2351
2871
|
"span",
|
|
2352
2872
|
{ className: "from" },
|
|
2353
2873
|
item.from.name !== "" ? item.from.name : item.from.address
|
|
2354
2874
|
),
|
|
2355
|
-
item.hasAttachments ? (0,
|
|
2356
|
-
isAll ? (0,
|
|
2875
|
+
item.hasAttachments ? (0, import_react9.createElement)(Icon, { name: "paperclip", size: 13 }) : null,
|
|
2876
|
+
isAll ? (0, import_react9.createElement)(
|
|
2357
2877
|
"span",
|
|
2358
2878
|
{ className: "badge" },
|
|
2359
2879
|
accounts.find((entry) => entry.id === item.accountId)?.label ?? ""
|
|
2360
2880
|
) : null,
|
|
2361
|
-
item.backedUp === false ? (0,
|
|
2362
|
-
(0,
|
|
2881
|
+
item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
|
|
2882
|
+
(0, import_react9.createElement)("span", { className: "date" }, shortDate(item.date))
|
|
2363
2883
|
),
|
|
2364
|
-
(0,
|
|
2884
|
+
(0, import_react9.createElement)(
|
|
2365
2885
|
"span",
|
|
2366
2886
|
{ className: "line-2" },
|
|
2367
|
-
(0,
|
|
2887
|
+
(0, import_react9.createElement)("span", { className: "subject" }, item.subject)
|
|
2368
2888
|
),
|
|
2369
|
-
item.snippet === "" ? null : (0,
|
|
2889
|
+
item.snippet === "" ? null : (0, import_react9.createElement)("span", { className: "snippet" }, item.snippet)
|
|
2370
2890
|
)
|
|
2371
2891
|
)
|
|
2372
2892
|
),
|
|
2373
|
-
!showingDrafts && items.length > 0 ? (0,
|
|
2893
|
+
!showingDrafts && items.length > 0 ? (0, import_react9.createElement)(
|
|
2374
2894
|
"div",
|
|
2375
2895
|
{ className: "mailharbor-list-foot" },
|
|
2376
|
-
(0,
|
|
2377
|
-
(0,
|
|
2378
|
-
page.total > items.length ? (0,
|
|
2896
|
+
(0, import_react9.createElement)("span", null, `${String(items.length)} / ${String(page.total)} \u5C01`),
|
|
2897
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2898
|
+
page.total > items.length ? (0, import_react9.createElement)(
|
|
2379
2899
|
"button",
|
|
2380
2900
|
{ className: "subtle", onClick: () => setLimit((current) => current + 40) },
|
|
2381
2901
|
"\u52A0\u8F7D\u66F4\u591A"
|
|
2382
2902
|
) : null
|
|
2383
|
-
) : showingDrafts && drafts.length > 0 ? (0,
|
|
2903
|
+
) : showingDrafts && drafts.length > 0 ? (0, import_react9.createElement)(
|
|
2384
2904
|
"div",
|
|
2385
2905
|
{ className: "mailharbor-list-foot" },
|
|
2386
|
-
(0,
|
|
2906
|
+
(0, import_react9.createElement)("span", null, `${String(drafts.length)} \u5C01\u8349\u7A3F`)
|
|
2387
2907
|
) : null
|
|
2388
2908
|
),
|
|
2389
|
-
(0,
|
|
2909
|
+
(0, import_react9.createElement)(
|
|
2390
2910
|
"div",
|
|
2391
2911
|
{ className: "mailharbor-reader" },
|
|
2392
|
-
selected === null ? (0,
|
|
2912
|
+
selected === null ? (0, import_react9.createElement)(
|
|
2393
2913
|
"div",
|
|
2394
2914
|
{ className: "mailharbor-empty" },
|
|
2395
|
-
(0,
|
|
2396
|
-
(0,
|
|
2397
|
-
(0,
|
|
2915
|
+
(0, import_react9.createElement)(Icon, { name: "mail", size: 34 }),
|
|
2916
|
+
(0, import_react9.createElement)("div", { className: "title" }, "\u9009\u62E9\u4E00\u5C01\u90AE\u4EF6\u5F00\u59CB\u9605\u8BFB"),
|
|
2917
|
+
(0, import_react9.createElement)(
|
|
2398
2918
|
"div",
|
|
2399
2919
|
{ className: "small" },
|
|
2400
2920
|
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"
|
|
2401
2921
|
)
|
|
2402
|
-
) : (0,
|
|
2922
|
+
) : (0, import_react9.createElement)(Reader, {
|
|
2403
2923
|
detail: selected,
|
|
2404
2924
|
source,
|
|
2405
2925
|
mailbox,
|
|
2406
2926
|
onChanged: (patch) => setSelected({ ...selected, ...patch }),
|
|
2407
2927
|
onReply: (mode) => startCompose(mode),
|
|
2408
2928
|
onAiAction: requestAi,
|
|
2929
|
+
onExtractTasks: () => void extractTasks(),
|
|
2409
2930
|
onClose: narrow ? () => setSelected(null) : void 0
|
|
2410
2931
|
})
|
|
2411
2932
|
),
|
|
2412
|
-
railOpen ? (0,
|
|
2933
|
+
railOpen ? (0, import_react9.createElement)(AiRail, {
|
|
2413
2934
|
account: accounts.find((item) => item.id === (selected?.accountId ?? accountScope)) ?? currentAccount,
|
|
2414
2935
|
detail: selected,
|
|
2415
2936
|
source,
|
|
@@ -2421,45 +2942,101 @@ function MailPanel() {
|
|
|
2421
2942
|
onNotice: notice
|
|
2422
2943
|
}) : null
|
|
2423
2944
|
),
|
|
2424
|
-
showBackup && backup !== null ? (0,
|
|
2945
|
+
showBackup && backup !== null ? (0, import_react9.createElement)(
|
|
2425
2946
|
Modal,
|
|
2426
2947
|
{
|
|
2427
2948
|
title: "\u5907\u4EFD",
|
|
2428
2949
|
onClose: () => setShowBackup(false),
|
|
2429
|
-
footer: (0,
|
|
2950
|
+
footer: (0, import_react9.createElement)(
|
|
2430
2951
|
"div",
|
|
2431
2952
|
{ style: { display: "flex", gap: "8px", width: "100%", alignItems: "center" } },
|
|
2432
|
-
(0,
|
|
2433
|
-
(0,
|
|
2434
|
-
backup.running ? (0,
|
|
2435
|
-
(0,
|
|
2953
|
+
(0, import_react9.createElement)("span", { className: "small muted" }, backup.running ? "\u5907\u4EFD\u8FDB\u884C\u4E2D\u2026" : backup.message),
|
|
2954
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2955
|
+
backup.running ? (0, import_react9.createElement)("button", { className: "ghost danger", onClick: () => void stopBackup() }, "\u505C\u6B62\u5907\u4EFD") : (0, import_react9.createElement)("button", { className: "primary", onClick: () => void startBackup() }, "\u5F00\u59CB\u5907\u4EFD"),
|
|
2956
|
+
(0, import_react9.createElement)("button", { className: "ghost", onClick: () => setShowBackup(false) }, "\u5173\u95ED")
|
|
2436
2957
|
)
|
|
2437
2958
|
},
|
|
2438
|
-
(0,
|
|
2959
|
+
(0, import_react9.createElement)(
|
|
2439
2960
|
"div",
|
|
2440
2961
|
{ className: "mailharbor-kv" },
|
|
2441
|
-
(0,
|
|
2442
|
-
(0,
|
|
2443
|
-
(0,
|
|
2444
|
-
(0,
|
|
2445
|
-
(0,
|
|
2446
|
-
(0,
|
|
2447
|
-
(0,
|
|
2448
|
-
(0,
|
|
2962
|
+
(0, import_react9.createElement)("dt", null, "\u5F00\u59CB\u65F6\u95F4"),
|
|
2963
|
+
(0, import_react9.createElement)("dd", null, backup.startedAt ?? "\u2014"),
|
|
2964
|
+
(0, import_react9.createElement)("dt", null, "\u4E0A\u6B21\u6210\u529F"),
|
|
2965
|
+
(0, import_react9.createElement)("dd", null, backup.lastSuccessAt ?? "\u2014"),
|
|
2966
|
+
(0, import_react9.createElement)("dt", null, "\u65B0\u589E"),
|
|
2967
|
+
(0, import_react9.createElement)("dd", null, String(backup.added)),
|
|
2968
|
+
(0, import_react9.createElement)("dt", null, "\u603B\u6570"),
|
|
2969
|
+
(0, import_react9.createElement)("dd", null, String(backup.total))
|
|
2449
2970
|
),
|
|
2450
|
-
(0,
|
|
2971
|
+
(0, import_react9.createElement)(
|
|
2451
2972
|
"div",
|
|
2452
2973
|
{ className: "mailharbor-log" },
|
|
2453
2974
|
backup.output.length === 0 ? "\uFF08\u7B49\u5F85\u8F93\u51FA\u2026\uFF09" : backup.output.join("\n")
|
|
2454
2975
|
)
|
|
2455
2976
|
) : null,
|
|
2456
|
-
|
|
2977
|
+
extraction !== null ? (0, import_react9.createElement)(
|
|
2978
|
+
Modal,
|
|
2979
|
+
{
|
|
2980
|
+
title: "\u63D0\u53D6\u4EFB\u52A1",
|
|
2981
|
+
onClose: () => setExtraction(null),
|
|
2982
|
+
footer: (0, import_react9.createElement)(
|
|
2983
|
+
"div",
|
|
2984
|
+
{ style: { display: "flex", gap: "8px", width: "100%", alignItems: "center" } },
|
|
2985
|
+
(0, import_react9.createElement)("span", { className: "small muted" }, "\u52FE\u9009\u8981\u4FDD\u5B58\u7684\u4EFB\u52A1\uFF1B\u4FDD\u5B58\u540E\u53EF\u5230\u300C\u4EFB\u52A1\u300D\u9762\u677F\u8DDF\u8FDB\u3002"),
|
|
2986
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2987
|
+
(0, import_react9.createElement)("button", { className: "ghost", onClick: () => setExtraction(null) }, "\u53D6\u6D88"),
|
|
2988
|
+
(0, import_react9.createElement)(
|
|
2989
|
+
"button",
|
|
2990
|
+
{
|
|
2991
|
+
className: "primary",
|
|
2992
|
+
disabled: extractBusy || extraction.checked.every((value) => !value),
|
|
2993
|
+
onClick: () => void saveExtraction()
|
|
2994
|
+
},
|
|
2995
|
+
extractBusy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u4EFB\u52A1"
|
|
2996
|
+
)
|
|
2997
|
+
)
|
|
2998
|
+
},
|
|
2999
|
+
(0, import_react9.createElement)(
|
|
3000
|
+
"div",
|
|
3001
|
+
{ style: { display: "flex", flexDirection: "column", gap: "8px" } },
|
|
3002
|
+
extraction.items.map(
|
|
3003
|
+
(item, index) => (0, import_react9.createElement)(
|
|
3004
|
+
"label",
|
|
3005
|
+
{ key: `${item.title}:${String(index)}`, className: "mailharbor-extract-row" },
|
|
3006
|
+
(0, import_react9.createElement)("input", {
|
|
3007
|
+
type: "checkbox",
|
|
3008
|
+
checked: extraction.checked[index] ?? false,
|
|
3009
|
+
onChange: () => {
|
|
3010
|
+
const checked = [...extraction.checked];
|
|
3011
|
+
checked[index] = !checked[index];
|
|
3012
|
+
setExtraction({ ...extraction, checked });
|
|
3013
|
+
}
|
|
3014
|
+
}),
|
|
3015
|
+
(0, import_react9.createElement)(
|
|
3016
|
+
"span",
|
|
3017
|
+
null,
|
|
3018
|
+
(0, import_react9.createElement)("strong", null, item.title),
|
|
3019
|
+
(0, import_react9.createElement)(
|
|
3020
|
+
"span",
|
|
3021
|
+
{ className: "small muted" },
|
|
3022
|
+
[
|
|
3023
|
+
item.due === "" ? "" : `\u622A\u6B62 ${item.due}`,
|
|
3024
|
+
item.owner === "" ? "" : `\u8D1F\u8D23\u4EBA ${item.owner}`,
|
|
3025
|
+
item.notes
|
|
3026
|
+
].filter((part) => part !== "").join(" \xB7 ")
|
|
3027
|
+
)
|
|
3028
|
+
)
|
|
3029
|
+
)
|
|
3030
|
+
)
|
|
3031
|
+
)
|
|
3032
|
+
) : null,
|
|
3033
|
+
compose !== null && composeAccount !== null ? (0, import_react9.createElement)(Composer, {
|
|
2457
3034
|
account: composeAccount,
|
|
2458
3035
|
initial: compose,
|
|
2459
3036
|
onClose: () => setCompose(null),
|
|
2460
3037
|
onSent: (message) => notice(message)
|
|
2461
3038
|
}) : null,
|
|
2462
|
-
showAdd ? (0,
|
|
3039
|
+
showAdd ? (0, import_react9.createElement)(AddAccountModal, {
|
|
2463
3040
|
onClose: () => setShowAdd(false),
|
|
2464
3041
|
onDone: (next) => {
|
|
2465
3042
|
setState(next);
|
|
@@ -2471,32 +3048,33 @@ function MailPanel() {
|
|
|
2471
3048
|
}
|
|
2472
3049
|
|
|
2473
3050
|
// src/client/settings.tsx
|
|
2474
|
-
var
|
|
3051
|
+
var import_react10 = require("react");
|
|
2475
3052
|
function errorText6(error) {
|
|
2476
3053
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
2477
3054
|
}
|
|
2478
3055
|
function MailSettings() {
|
|
2479
|
-
const [state, setState] = (0,
|
|
2480
|
-
const [settings, setSettings] = (0,
|
|
2481
|
-
const [error, setError] = (0,
|
|
2482
|
-
const [toast, setToast] = (0,
|
|
2483
|
-
const [signature, setSignature] = (0,
|
|
2484
|
-
const [markOnOpen, setMarkOnOpen] = (0,
|
|
2485
|
-
const [pollSeconds, setPollSeconds] = (0,
|
|
2486
|
-
const [listLimit, setListLimit] = (0,
|
|
2487
|
-
const [
|
|
2488
|
-
const [
|
|
2489
|
-
const [
|
|
2490
|
-
const [
|
|
2491
|
-
const [
|
|
2492
|
-
const [
|
|
2493
|
-
const [
|
|
2494
|
-
const [
|
|
2495
|
-
const [
|
|
2496
|
-
const [
|
|
2497
|
-
const [
|
|
2498
|
-
const [
|
|
2499
|
-
(0,
|
|
3056
|
+
const [state, setState] = (0, import_react10.useState)(null);
|
|
3057
|
+
const [settings, setSettings] = (0, import_react10.useState)(null);
|
|
3058
|
+
const [error, setError] = (0, import_react10.useState)("");
|
|
3059
|
+
const [toast, setToast] = (0, import_react10.useState)("");
|
|
3060
|
+
const [signature, setSignature] = (0, import_react10.useState)("");
|
|
3061
|
+
const [markOnOpen, setMarkOnOpen] = (0, import_react10.useState)(true);
|
|
3062
|
+
const [pollSeconds, setPollSeconds] = (0, import_react10.useState)(60);
|
|
3063
|
+
const [listLimit, setListLimit] = (0, import_react10.useState)(40);
|
|
3064
|
+
const [projectRoot, setProjectRoot] = (0, import_react10.useState)("");
|
|
3065
|
+
const [aiEnabled, setAiEnabled] = (0, import_react10.useState)(true);
|
|
3066
|
+
const [aiProvider, setAiProvider] = (0, import_react10.useState)("deepseek-account");
|
|
3067
|
+
const [aiModel, setAiModel] = (0, import_react10.useState)("deepseek-flash");
|
|
3068
|
+
const [aiModels, setAiModels] = (0, import_react10.useState)({ providers: [] });
|
|
3069
|
+
const [aiStatus, setAiStatus] = (0, import_react10.useState)(null);
|
|
3070
|
+
const [styleText, setStyleText] = (0, import_react10.useState)("");
|
|
3071
|
+
const [styleMeta, setStyleMeta] = (0, import_react10.useState)({ updatedAt: "", sampleCount: 0 });
|
|
3072
|
+
const [styleBusy, setStyleBusy] = (0, import_react10.useState)(null);
|
|
3073
|
+
const [editing, setEditing] = (0, import_react10.useState)(null);
|
|
3074
|
+
const [checkResults, setCheckResults] = (0, import_react10.useState)({});
|
|
3075
|
+
const [adding, setAdding] = (0, import_react10.useState)(false);
|
|
3076
|
+
const [tick, setTick] = (0, import_react10.useState)(0);
|
|
3077
|
+
(0, import_react10.useEffect)(() => {
|
|
2500
3078
|
let cancelled = false;
|
|
2501
3079
|
Promise.all([api.state(), api.settings()]).then(([appState, view]) => {
|
|
2502
3080
|
if (cancelled) return;
|
|
@@ -2506,6 +3084,7 @@ function MailSettings() {
|
|
|
2506
3084
|
setMarkOnOpen(view.read.markOnOpen);
|
|
2507
3085
|
setPollSeconds(view.read.pollSeconds);
|
|
2508
3086
|
setListLimit(view.render.listLimit);
|
|
3087
|
+
setProjectRoot(view.projects.root);
|
|
2509
3088
|
setAiEnabled(view.ai.enabled);
|
|
2510
3089
|
setAiProvider(view.ai.provider);
|
|
2511
3090
|
setAiModel(view.ai.model);
|
|
@@ -2516,7 +3095,7 @@ function MailSettings() {
|
|
|
2516
3095
|
cancelled = true;
|
|
2517
3096
|
};
|
|
2518
3097
|
}, [tick]);
|
|
2519
|
-
(0,
|
|
3098
|
+
(0, import_react10.useEffect)(() => {
|
|
2520
3099
|
let cancelled = false;
|
|
2521
3100
|
const accountId = settings?.activeAccountId ?? state?.activeAccountId ?? null;
|
|
2522
3101
|
if (accountId === null) return;
|
|
@@ -2559,33 +3138,33 @@ function MailSettings() {
|
|
|
2559
3138
|
};
|
|
2560
3139
|
const account = settings?.accounts.find((item) => item.id === settings.activeAccountId) ?? null;
|
|
2561
3140
|
if (state === null || settings === null) {
|
|
2562
|
-
return (0,
|
|
3141
|
+
return (0, import_react10.createElement)("div", { className: "mailharbor mailharbor-settings" }, error === "" ? "\u6B63\u5728\u52A0\u8F7D\u8BBE\u7F6E\u2026" : error);
|
|
2563
3142
|
}
|
|
2564
|
-
return (0,
|
|
3143
|
+
return (0, import_react10.createElement)(
|
|
2565
3144
|
"div",
|
|
2566
|
-
{ className: "mailharbor" },
|
|
2567
|
-
(0,
|
|
3145
|
+
{ className: "mailharbor mailharbor-settings-page" },
|
|
3146
|
+
(0, import_react10.createElement)(
|
|
2568
3147
|
"div",
|
|
2569
3148
|
{ className: "mailharbor-settings" },
|
|
2570
|
-
(0,
|
|
2571
|
-
(0,
|
|
2572
|
-
toast === "" ? null : (0,
|
|
2573
|
-
error === "" ? null : (0,
|
|
2574
|
-
(0,
|
|
3149
|
+
(0, import_react10.createElement)("h2", null, "MailHarbor"),
|
|
3150
|
+
(0, import_react10.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"),
|
|
3151
|
+
toast === "" ? null : (0, import_react10.createElement)("div", { className: "small", style: { color: "var(--m-ok)" } }, toast),
|
|
3152
|
+
error === "" ? null : (0, import_react10.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
|
|
3153
|
+
(0, import_react10.createElement)(
|
|
2575
3154
|
"div",
|
|
2576
3155
|
{ className: "mailharbor-card" },
|
|
2577
|
-
(0,
|
|
2578
|
-
settings.accounts.length === 0 ? (0,
|
|
2579
|
-
(item) => (0,
|
|
3156
|
+
(0, import_react10.createElement)("h3", null, "\u8D26\u53F7"),
|
|
3157
|
+
settings.accounts.length === 0 ? (0, import_react10.createElement)(AddAccountForm, { onDone: () => reload() }) : settings.accounts.map(
|
|
3158
|
+
(item) => (0, import_react10.createElement)(AccountRow, {
|
|
2580
3159
|
key: item.id,
|
|
2581
3160
|
account: item,
|
|
2582
3161
|
active: item.id === settings.activeAccountId,
|
|
2583
3162
|
loggedIn: item.id === settings.activeAccountId ? state.loggedIn : void 0,
|
|
2584
3163
|
editing: editing === item.id,
|
|
2585
|
-
checkResult:
|
|
3164
|
+
checkResult: checkResults[item.id] ?? "",
|
|
2586
3165
|
onToggleEdit: () => {
|
|
2587
3166
|
setEditing(editing === item.id ? null : item.id);
|
|
2588
|
-
|
|
3167
|
+
setCheckResults((prev) => ({ ...prev, [item.id]: "" }));
|
|
2589
3168
|
},
|
|
2590
3169
|
onActivate: async () => {
|
|
2591
3170
|
await api.activateAccount(item.id);
|
|
@@ -2602,11 +3181,12 @@ function MailSettings() {
|
|
|
2602
3181
|
onCheck: async () => {
|
|
2603
3182
|
try {
|
|
2604
3183
|
const result = await api.check(item.id);
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
3184
|
+
setCheckResults((prev) => ({
|
|
3185
|
+
...prev,
|
|
3186
|
+
[item.id]: `IMAP \u6B63\u5E38\uFF08${String(result.imap.mailboxes.length)} \u4E2A\u6587\u4EF6\u5939\uFF09\xB7 SMTP ${result.smtp.ok ? "\u6B63\u5E38" : `\u5931\u8D25: ${result.smtp.error ?? ""}`}`
|
|
3187
|
+
}));
|
|
2608
3188
|
} catch (caught) {
|
|
2609
|
-
|
|
3189
|
+
setCheckResults((prev) => ({ ...prev, [item.id]: `\u5931\u8D25: ${errorText6(caught)}` }));
|
|
2610
3190
|
}
|
|
2611
3191
|
},
|
|
2612
3192
|
onSave: async (patch) => {
|
|
@@ -2620,32 +3200,32 @@ function MailSettings() {
|
|
|
2620
3200
|
}
|
|
2621
3201
|
})
|
|
2622
3202
|
),
|
|
2623
|
-
adding ? (0,
|
|
3203
|
+
adding ? (0, import_react10.createElement)(AddAccountForm, {
|
|
2624
3204
|
onDone: () => {
|
|
2625
3205
|
setAdding(false);
|
|
2626
3206
|
reload();
|
|
2627
3207
|
},
|
|
2628
3208
|
onCancel: () => setAdding(false)
|
|
2629
|
-
}) : (0,
|
|
3209
|
+
}) : (0, import_react10.createElement)("button", { className: "ghost", onClick: () => setAdding(true) }, "+ \u6DFB\u52A0\u8D26\u53F7")
|
|
2630
3210
|
),
|
|
2631
|
-
(0,
|
|
3211
|
+
(0, import_react10.createElement)(
|
|
2632
3212
|
"div",
|
|
2633
3213
|
{ className: "mailharbor-card" },
|
|
2634
|
-
(0,
|
|
2635
|
-
(0,
|
|
3214
|
+
(0, import_react10.createElement)("h3", null, "\u5199\u4FE1\u4E0E\u9605\u8BFB"),
|
|
3215
|
+
(0, import_react10.createElement)(
|
|
2636
3216
|
Field,
|
|
2637
3217
|
{ label: "\u7B7E\u540D\uFF08\u7EAF\u6587\u672C\uFF0C\u53D1\u9001\u65F6\u8FFD\u52A0\u5230\u6B63\u6587\u672B\u5C3E\uFF09" },
|
|
2638
|
-
(0,
|
|
3218
|
+
(0, import_react10.createElement)("textarea", {
|
|
2639
3219
|
rows: 4,
|
|
2640
3220
|
value: signature,
|
|
2641
3221
|
onChange: (event) => setSignature(event.target.value)
|
|
2642
3222
|
})
|
|
2643
3223
|
),
|
|
2644
|
-
(0,
|
|
3224
|
+
(0, import_react10.createElement)(
|
|
2645
3225
|
"div",
|
|
2646
3226
|
{ style: { display: "flex", gap: "10px", alignItems: "center", flexWrap: "wrap" } },
|
|
2647
|
-
(0,
|
|
2648
|
-
(0,
|
|
3227
|
+
(0, import_react10.createElement)("button", { className: "ghost", onClick: () => void saveGeneral({ signature }) }, "\u4FDD\u5B58\u7B7E\u540D"),
|
|
3228
|
+
(0, import_react10.createElement)(Toggle, {
|
|
2649
3229
|
checked: markOnOpen,
|
|
2650
3230
|
onChange: (value) => {
|
|
2651
3231
|
setMarkOnOpen(value);
|
|
@@ -2654,13 +3234,13 @@ function MailSettings() {
|
|
|
2654
3234
|
label: "\u6253\u5F00\u90AE\u4EF6\u65F6\u6807\u4E3A\u5DF2\u8BFB"
|
|
2655
3235
|
})
|
|
2656
3236
|
),
|
|
2657
|
-
(0,
|
|
3237
|
+
(0, import_react10.createElement)(
|
|
2658
3238
|
"div",
|
|
2659
3239
|
{ className: "mailharbor-grid" },
|
|
2660
|
-
(0,
|
|
3240
|
+
(0, import_react10.createElement)(
|
|
2661
3241
|
Field,
|
|
2662
3242
|
{ label: "\u5217\u8868\u6BCF\u9875\u6570\u91CF" },
|
|
2663
|
-
(0,
|
|
3243
|
+
(0, import_react10.createElement)("input", {
|
|
2664
3244
|
type: "number",
|
|
2665
3245
|
min: 10,
|
|
2666
3246
|
max: 200,
|
|
@@ -2669,10 +3249,10 @@ function MailSettings() {
|
|
|
2669
3249
|
onBlur: () => void saveGeneral({ render: { listLimit } })
|
|
2670
3250
|
})
|
|
2671
3251
|
),
|
|
2672
|
-
(0,
|
|
3252
|
+
(0, import_react10.createElement)(
|
|
2673
3253
|
Field,
|
|
2674
3254
|
{ label: "\u672A\u8BFB\u89D2\u6807\u8F6E\u8BE2\uFF08\u79D2\uFF09" },
|
|
2675
|
-
(0,
|
|
3255
|
+
(0, import_react10.createElement)("input", {
|
|
2676
3256
|
type: "number",
|
|
2677
3257
|
min: 15,
|
|
2678
3258
|
max: 3600,
|
|
@@ -2683,11 +3263,38 @@ function MailSettings() {
|
|
|
2683
3263
|
)
|
|
2684
3264
|
)
|
|
2685
3265
|
),
|
|
2686
|
-
(0,
|
|
3266
|
+
(0, import_react10.createElement)(
|
|
3267
|
+
"div",
|
|
3268
|
+
{ className: "mailharbor-card" },
|
|
3269
|
+
(0, import_react10.createElement)("h3", null, "\u9879\u76EE"),
|
|
3270
|
+
(0, import_react10.createElement)("p", { className: "small muted" }, "\u4ECE\u90AE\u4EF6\u521B\u5EFA\u9879\u76EE\u65F6\u7684\u9ED8\u8BA4\u76EE\u5F55\uFF1B\u5355\u4E2A\u9879\u76EE\u53EF\u4EE5\u5728\u521B\u5EFA\u65F6\u6539\u3002"),
|
|
3271
|
+
(0, import_react10.createElement)(
|
|
3272
|
+
"div",
|
|
3273
|
+
{ className: "mailharbor-grid", style: { gridTemplateColumns: "minmax(0, 1fr) auto" } },
|
|
3274
|
+
(0, import_react10.createElement)(
|
|
3275
|
+
Field,
|
|
3276
|
+
{ label: "\u9ED8\u8BA4\u9879\u76EE\u76EE\u5F55" },
|
|
3277
|
+
(0, import_react10.createElement)("input", {
|
|
3278
|
+
value: projectRoot,
|
|
3279
|
+
onChange: (event) => setProjectRoot(event.target.value)
|
|
3280
|
+
})
|
|
3281
|
+
),
|
|
3282
|
+
(0, import_react10.createElement)(
|
|
3283
|
+
"button",
|
|
3284
|
+
{
|
|
3285
|
+
className: "ghost small",
|
|
3286
|
+
style: { alignSelf: "end" },
|
|
3287
|
+
onClick: () => void saveGeneral({ projects: { root: projectRoot } })
|
|
3288
|
+
},
|
|
3289
|
+
"\u4FDD\u5B58\u76EE\u5F55"
|
|
3290
|
+
)
|
|
3291
|
+
)
|
|
3292
|
+
),
|
|
3293
|
+
(0, import_react10.createElement)(
|
|
2687
3294
|
"div",
|
|
2688
3295
|
{ className: "mailharbor-card" },
|
|
2689
|
-
(0,
|
|
2690
|
-
(0,
|
|
3296
|
+
(0, import_react10.createElement)("h3", null, "AI"),
|
|
3297
|
+
(0, import_react10.createElement)(Toggle, {
|
|
2691
3298
|
checked: aiEnabled,
|
|
2692
3299
|
onChange: (value) => {
|
|
2693
3300
|
setAiEnabled(value);
|
|
@@ -2695,22 +3302,22 @@ function MailSettings() {
|
|
|
2695
3302
|
},
|
|
2696
3303
|
label: "\u542F\u7528 AI\uFF08\u6458\u8981\u3001AI \u56DE\u590D\u3001\u7FFB\u8BD1\u3001\u5199\u4F5C\u98CE\u683C\uFF09"
|
|
2697
3304
|
}),
|
|
2698
|
-
aiStatus === null ? null : (0,
|
|
3305
|
+
aiStatus === null ? null : (0, import_react10.createElement)(
|
|
2699
3306
|
"div",
|
|
2700
3307
|
{ className: "small", style: { color: aiStatus.available ? "var(--m-ok)" : "var(--m-warn)" } },
|
|
2701
3308
|
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"
|
|
2702
3309
|
),
|
|
2703
|
-
(0,
|
|
3310
|
+
(0, import_react10.createElement)(
|
|
2704
3311
|
"div",
|
|
2705
3312
|
{ className: "mailharbor-grid" },
|
|
2706
|
-
(0,
|
|
3313
|
+
(0, import_react10.createElement)(
|
|
2707
3314
|
Field,
|
|
2708
3315
|
{ label: "Provider" },
|
|
2709
|
-
aiModels.providers.length === 0 ? (0,
|
|
3316
|
+
aiModels.providers.length === 0 ? (0, import_react10.createElement)("input", {
|
|
2710
3317
|
value: aiProvider,
|
|
2711
3318
|
onChange: (event) => setAiProvider(event.target.value),
|
|
2712
3319
|
onBlur: () => void saveGeneral({ ai: { provider: aiProvider } })
|
|
2713
|
-
}) : (0,
|
|
3320
|
+
}) : (0, import_react10.createElement)(
|
|
2714
3321
|
"select",
|
|
2715
3322
|
{
|
|
2716
3323
|
value: aiProvider,
|
|
@@ -2728,20 +3335,20 @@ function MailSettings() {
|
|
|
2728
3335
|
}
|
|
2729
3336
|
},
|
|
2730
3337
|
aiModels.providers.map(
|
|
2731
|
-
(item) => (0,
|
|
3338
|
+
(item) => (0, import_react10.createElement)("option", { key: item.provider, value: item.provider }, item.name)
|
|
2732
3339
|
)
|
|
2733
3340
|
)
|
|
2734
3341
|
),
|
|
2735
|
-
(0,
|
|
3342
|
+
(0, import_react10.createElement)(
|
|
2736
3343
|
Field,
|
|
2737
3344
|
{ label: "\u6A21\u578B" },
|
|
2738
3345
|
(() => {
|
|
2739
3346
|
const provider = aiModels.providers.find((item) => item.provider === aiProvider);
|
|
2740
|
-
return provider === void 0 || provider.models.length === 0 ? (0,
|
|
3347
|
+
return provider === void 0 || provider.models.length === 0 ? (0, import_react10.createElement)("input", {
|
|
2741
3348
|
value: aiModel,
|
|
2742
3349
|
onChange: (event) => setAiModel(event.target.value),
|
|
2743
3350
|
onBlur: () => void saveGeneral({ ai: { model: aiModel } })
|
|
2744
|
-
}) : (0,
|
|
3351
|
+
}) : (0, import_react10.createElement)(
|
|
2745
3352
|
"select",
|
|
2746
3353
|
{
|
|
2747
3354
|
value: aiModel,
|
|
@@ -2750,37 +3357,37 @@ function MailSettings() {
|
|
|
2750
3357
|
void saveGeneral({ ai: { model: event.target.value } });
|
|
2751
3358
|
}
|
|
2752
3359
|
},
|
|
2753
|
-
provider.models.map((item) => (0,
|
|
3360
|
+
provider.models.map((item) => (0, import_react10.createElement)("option", { key: item.id, value: item.id }, item.name))
|
|
2754
3361
|
);
|
|
2755
3362
|
})()
|
|
2756
3363
|
)
|
|
2757
3364
|
),
|
|
2758
|
-
(0,
|
|
2759
|
-
(0,
|
|
3365
|
+
(0, import_react10.createElement)("hr", null),
|
|
3366
|
+
(0, import_react10.createElement)(
|
|
2760
3367
|
"div",
|
|
2761
3368
|
{ style: { display: "flex", alignItems: "center", gap: "8px" } },
|
|
2762
|
-
(0,
|
|
2763
|
-
(0,
|
|
3369
|
+
(0, import_react10.createElement)("strong", { style: { fontSize: "12.5px" } }, "\u5199\u4F5C\u98CE\u683C"),
|
|
3370
|
+
(0, import_react10.createElement)(
|
|
2764
3371
|
"span",
|
|
2765
3372
|
{ className: "small muted" },
|
|
2766
3373
|
styleMeta.updatedAt === "" ? "\u8FD8\u6CA1\u6709\u751F\u6210" : `${styleMeta.sampleCount} \u5C01\u5DF2\u53D1\u9001\u90AE\u4EF6 \xB7 ${new Date(styleMeta.updatedAt).toLocaleString()}`
|
|
2767
3374
|
)
|
|
2768
3375
|
),
|
|
2769
|
-
(0,
|
|
3376
|
+
(0, import_react10.createElement)(
|
|
2770
3377
|
"p",
|
|
2771
3378
|
{ className: "small muted" },
|
|
2772
3379
|
"\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"
|
|
2773
3380
|
),
|
|
2774
|
-
(0,
|
|
3381
|
+
(0, import_react10.createElement)("textarea", {
|
|
2775
3382
|
rows: 6,
|
|
2776
3383
|
value: styleText,
|
|
2777
3384
|
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",
|
|
2778
3385
|
onChange: (event) => setStyleText(event.target.value)
|
|
2779
3386
|
}),
|
|
2780
|
-
(0,
|
|
3387
|
+
(0, import_react10.createElement)(
|
|
2781
3388
|
"div",
|
|
2782
3389
|
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
2783
|
-
(0,
|
|
3390
|
+
(0, import_react10.createElement)(
|
|
2784
3391
|
"button",
|
|
2785
3392
|
{
|
|
2786
3393
|
className: "ghost",
|
|
@@ -2799,7 +3406,7 @@ function MailSettings() {
|
|
|
2799
3406
|
},
|
|
2800
3407
|
styleBusy === "learn" ? "\u5B66\u4E60\u4E2D\u2026" : "\u4ECE\u5DF2\u53D1\u9001\u90AE\u4EF6\u5B66\u4E60"
|
|
2801
3408
|
),
|
|
2802
|
-
(0,
|
|
3409
|
+
(0, import_react10.createElement)(
|
|
2803
3410
|
"button",
|
|
2804
3411
|
{
|
|
2805
3412
|
className: "primary",
|
|
@@ -2818,36 +3425,36 @@ function MailSettings() {
|
|
|
2818
3425
|
"\u4FDD\u5B58\u98CE\u683C"
|
|
2819
3426
|
)
|
|
2820
3427
|
),
|
|
2821
|
-
(0,
|
|
3428
|
+
(0, import_react10.createElement)(
|
|
2822
3429
|
"p",
|
|
2823
3430
|
{ className: "small muted" },
|
|
2824
3431
|
"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"
|
|
2825
3432
|
)
|
|
2826
3433
|
),
|
|
2827
|
-
(0,
|
|
3434
|
+
(0, import_react10.createElement)(
|
|
2828
3435
|
"div",
|
|
2829
3436
|
{ className: "mailharbor-card" },
|
|
2830
|
-
(0,
|
|
2831
|
-
(0,
|
|
3437
|
+
(0, import_react10.createElement)("h3", null, "\u6570\u636E"),
|
|
3438
|
+
(0, import_react10.createElement)(
|
|
2832
3439
|
"dl",
|
|
2833
3440
|
{ className: "mailharbor-kv" },
|
|
2834
|
-
(0,
|
|
2835
|
-
(0,
|
|
2836
|
-
(0,
|
|
2837
|
-
(0,
|
|
2838
|
-
(0,
|
|
2839
|
-
(0,
|
|
3441
|
+
(0, import_react10.createElement)("dt", null, "\u6570\u636E\u76EE\u5F55"),
|
|
3442
|
+
(0, import_react10.createElement)("dd", null, settings.dataDir),
|
|
3443
|
+
(0, import_react10.createElement)("dt", null, "\u914D\u7F6E\u6587\u4EF6"),
|
|
3444
|
+
(0, import_react10.createElement)("dd", null, settings.configFile),
|
|
3445
|
+
(0, import_react10.createElement)("dt", null, "\u5F53\u524D\u8D26\u53F7\u6570\u636E\u5E93"),
|
|
3446
|
+
(0, import_react10.createElement)(
|
|
2840
3447
|
"dd",
|
|
2841
3448
|
null,
|
|
2842
3449
|
account === null ? "\u2014" : `${account.dataDir.startsWith("/") ? account.dataDir : `${settings.dataDir}/${account.dataDir}`}/mail.db`
|
|
2843
3450
|
),
|
|
2844
|
-
(0,
|
|
2845
|
-
(0,
|
|
3451
|
+
(0, import_react10.createElement)("dt", null, "\u63D2\u4EF6\u7248\u672C"),
|
|
3452
|
+
(0, import_react10.createElement)("dd", null, state.version)
|
|
2846
3453
|
),
|
|
2847
|
-
(0,
|
|
3454
|
+
(0, import_react10.createElement)(
|
|
2848
3455
|
"div",
|
|
2849
3456
|
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
2850
|
-
account === null ? null : (0,
|
|
3457
|
+
account === null ? null : (0, import_react10.createElement)(
|
|
2851
3458
|
"button",
|
|
2852
3459
|
{
|
|
2853
3460
|
className: "ghost",
|
|
@@ -2882,19 +3489,41 @@ function AccountRow({
|
|
|
2882
3489
|
onSave,
|
|
2883
3490
|
onRemove
|
|
2884
3491
|
}) {
|
|
2885
|
-
const [password, setPassword] = (0,
|
|
2886
|
-
const [remember, setRemember] = (0,
|
|
2887
|
-
const [label, setLabel] = (0,
|
|
2888
|
-
const [username, setUsername] = (0,
|
|
2889
|
-
const [imapHost, setImapHost] = (0,
|
|
2890
|
-
const [imapPort, setImapPort] = (0,
|
|
2891
|
-
const [imapSecure, setImapSecure] = (0,
|
|
2892
|
-
const [smtpHost, setSmtpHost] = (0,
|
|
2893
|
-
const [smtpPort, setSmtpPort] = (0,
|
|
2894
|
-
const [smtpSecure, setSmtpSecure] = (0,
|
|
2895
|
-
const [fromName, setFromName] = (0,
|
|
2896
|
-
const [busy, setBusy] = (0,
|
|
2897
|
-
(0,
|
|
3492
|
+
const [password, setPassword] = (0, import_react10.useState)("");
|
|
3493
|
+
const [remember, setRemember] = (0, import_react10.useState)(true);
|
|
3494
|
+
const [label, setLabel] = (0, import_react10.useState)(account.label);
|
|
3495
|
+
const [username, setUsername] = (0, import_react10.useState)(account.username);
|
|
3496
|
+
const [imapHost, setImapHost] = (0, import_react10.useState)(account.imap.host);
|
|
3497
|
+
const [imapPort, setImapPort] = (0, import_react10.useState)(String(account.imap.port));
|
|
3498
|
+
const [imapSecure, setImapSecure] = (0, import_react10.useState)(account.imap.secure);
|
|
3499
|
+
const [smtpHost, setSmtpHost] = (0, import_react10.useState)(account.smtp.host);
|
|
3500
|
+
const [smtpPort, setSmtpPort] = (0, import_react10.useState)(String(account.smtp.port));
|
|
3501
|
+
const [smtpSecure, setSmtpSecure] = (0, import_react10.useState)(account.smtp.secure);
|
|
3502
|
+
const [fromName, setFromName] = (0, import_react10.useState)(account.smtp.fromName);
|
|
3503
|
+
const [busy, setBusy] = (0, import_react10.useState)(false);
|
|
3504
|
+
const [checking, setChecking] = (0, import_react10.useState)(false);
|
|
3505
|
+
const [caldavUrl, setCaldavUrl] = (0, import_react10.useState)(account.caldav?.url ?? "");
|
|
3506
|
+
const [caldavUser, setCaldavUser] = (0, import_react10.useState)(account.caldav?.username ?? "");
|
|
3507
|
+
const [caldavHref, setCaldavHref] = (0, import_react10.useState)(account.caldav?.calendarHref ?? "");
|
|
3508
|
+
const [caldavPassword, setCaldavPassword] = (0, import_react10.useState)("");
|
|
3509
|
+
const [caldavStatus, setCaldavStatus] = (0, import_react10.useState)("");
|
|
3510
|
+
(0, import_react10.useEffect)(() => {
|
|
3511
|
+
if (!editing) return;
|
|
3512
|
+
let cancelled = false;
|
|
3513
|
+
api.taskCaldav(account.id).then((info) => {
|
|
3514
|
+
if (cancelled) return;
|
|
3515
|
+
setCaldavUrl(info.caldav?.url ?? "");
|
|
3516
|
+
setCaldavUser(info.caldav?.username ?? "");
|
|
3517
|
+
setCaldavHref(info.caldav?.calendarHref ?? "");
|
|
3518
|
+
setCaldavStatus(info.hasPassword ? "\u5DF2\u4FDD\u5B58\u5E94\u7528\u5BC6\u7801" : "\u5C1A\u672A\u4FDD\u5B58\u5E94\u7528\u5BC6\u7801");
|
|
3519
|
+
}).catch(() => {
|
|
3520
|
+
if (!cancelled) setCaldavStatus("\u65E0\u6CD5\u8BFB\u53D6 CalDAV \u914D\u7F6E");
|
|
3521
|
+
});
|
|
3522
|
+
return () => {
|
|
3523
|
+
cancelled = true;
|
|
3524
|
+
};
|
|
3525
|
+
}, [editing, account.id]);
|
|
3526
|
+
(0, import_react10.useEffect)(() => {
|
|
2898
3527
|
setLabel(account.label);
|
|
2899
3528
|
setUsername(account.username);
|
|
2900
3529
|
setImapHost(account.imap.host);
|
|
@@ -2913,103 +3542,122 @@ function AccountRow({
|
|
|
2913
3542
|
setBusy(false);
|
|
2914
3543
|
}
|
|
2915
3544
|
};
|
|
2916
|
-
return (0,
|
|
3545
|
+
return (0, import_react10.createElement)(
|
|
2917
3546
|
"div",
|
|
2918
3547
|
{ className: "mailharbor-card", style: { background: "var(--m-bg-1)" } },
|
|
2919
|
-
(0,
|
|
3548
|
+
(0, import_react10.createElement)(
|
|
2920
3549
|
"div",
|
|
2921
3550
|
{ className: "mailharbor-account-row" },
|
|
2922
|
-
(0,
|
|
2923
|
-
(0,
|
|
3551
|
+
(0, import_react10.createElement)(Avatar, { email: account.email, size: 30 }),
|
|
3552
|
+
(0, import_react10.createElement)(
|
|
2924
3553
|
"div",
|
|
2925
3554
|
{ style: { display: "flex", flexDirection: "column", minWidth: 0 } },
|
|
2926
|
-
(0,
|
|
2927
|
-
(0,
|
|
3555
|
+
(0, import_react10.createElement)("strong", null, account.label),
|
|
3556
|
+
(0, import_react10.createElement)("span", { className: "small muted" }, account.email)
|
|
2928
3557
|
),
|
|
2929
|
-
active ? (0,
|
|
2930
|
-
loggedIn === false ? (0,
|
|
2931
|
-
(0,
|
|
2932
|
-
active ? null : (0,
|
|
2933
|
-
(0,
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
3558
|
+
active ? (0, import_react10.createElement)("span", { className: "badge ok" }, "\u5F53\u524D\u8D26\u53F7") : null,
|
|
3559
|
+
loggedIn === false ? (0, import_react10.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null,
|
|
3560
|
+
(0, import_react10.createElement)("span", { className: "spacer" }),
|
|
3561
|
+
active ? null : (0, import_react10.createElement)("button", { className: "ghost small", onClick: () => void wrap(onActivate) }, "\u8BBE\u4E3A\u5F53\u524D"),
|
|
3562
|
+
(0, import_react10.createElement)(
|
|
3563
|
+
"button",
|
|
3564
|
+
{
|
|
3565
|
+
className: "ghost small",
|
|
3566
|
+
disabled: checking,
|
|
3567
|
+
onClick: () => {
|
|
3568
|
+
setChecking(true);
|
|
3569
|
+
void wrap(onCheck).finally(() => setChecking(false));
|
|
3570
|
+
}
|
|
3571
|
+
},
|
|
3572
|
+
checking ? "\u68C0\u67E5\u4E2D\u2026" : "\u68C0\u67E5\u8FDE\u63A5"
|
|
3573
|
+
),
|
|
3574
|
+
(0, import_react10.createElement)("button", { className: "ghost small", onClick: onToggleEdit }, editing ? "\u6536\u8D77" : "\u7F16\u8F91")
|
|
3575
|
+
),
|
|
3576
|
+
checkResult === "" ? null : (0, import_react10.createElement)(
|
|
3577
|
+
"div",
|
|
3578
|
+
{
|
|
3579
|
+
className: "mailharbor-check",
|
|
3580
|
+
style: { color: checkResult.startsWith("\u5931\u8D25") ? "var(--m-danger)" : "var(--m-ok)" }
|
|
3581
|
+
},
|
|
3582
|
+
checkResult
|
|
3583
|
+
),
|
|
3584
|
+
editing ? (0, import_react10.createElement)(
|
|
2937
3585
|
"div",
|
|
2938
3586
|
{ style: { display: "flex", flexDirection: "column", gap: "10px" } },
|
|
2939
|
-
(0,
|
|
3587
|
+
(0, import_react10.createElement)(
|
|
2940
3588
|
"div",
|
|
2941
3589
|
{ className: "mailharbor-grid" },
|
|
2942
|
-
(0,
|
|
3590
|
+
(0, import_react10.createElement)(
|
|
2943
3591
|
Field,
|
|
2944
3592
|
{ label: "\u663E\u793A\u540D\u79F0" },
|
|
2945
|
-
(0,
|
|
3593
|
+
(0, import_react10.createElement)("input", {
|
|
2946
3594
|
value: label,
|
|
2947
3595
|
onChange: (event) => setLabel(event.target.value)
|
|
2948
3596
|
})
|
|
2949
3597
|
),
|
|
2950
|
-
(0,
|
|
3598
|
+
(0, import_react10.createElement)(
|
|
2951
3599
|
Field,
|
|
2952
3600
|
{ label: "\u767B\u5F55\u7528\u6237\u540D" },
|
|
2953
|
-
(0,
|
|
3601
|
+
(0, import_react10.createElement)("input", {
|
|
2954
3602
|
value: username,
|
|
2955
3603
|
onChange: (event) => setUsername(event.target.value)
|
|
2956
3604
|
})
|
|
2957
3605
|
)
|
|
2958
3606
|
),
|
|
2959
|
-
(0,
|
|
3607
|
+
(0, import_react10.createElement)(
|
|
2960
3608
|
"div",
|
|
2961
3609
|
{ className: "mailharbor-grid three" },
|
|
2962
|
-
(0,
|
|
3610
|
+
(0, import_react10.createElement)(
|
|
2963
3611
|
Field,
|
|
2964
3612
|
{ label: "IMAP \u4E3B\u673A" },
|
|
2965
|
-
(0,
|
|
3613
|
+
(0, import_react10.createElement)("input", {
|
|
2966
3614
|
value: imapHost,
|
|
2967
3615
|
onChange: (event) => setImapHost(event.target.value)
|
|
2968
3616
|
})
|
|
2969
3617
|
),
|
|
2970
|
-
(0,
|
|
3618
|
+
(0, import_react10.createElement)(
|
|
2971
3619
|
Field,
|
|
2972
3620
|
{ label: "\u7AEF\u53E3" },
|
|
2973
|
-
(0,
|
|
3621
|
+
(0, import_react10.createElement)("input", {
|
|
2974
3622
|
value: imapPort,
|
|
2975
3623
|
onChange: (event) => setImapPort(event.target.value)
|
|
2976
3624
|
})
|
|
2977
3625
|
),
|
|
2978
|
-
(0,
|
|
3626
|
+
(0, import_react10.createElement)(Toggle, { checked: imapSecure, onChange: setImapSecure, label: "TLS" })
|
|
2979
3627
|
),
|
|
2980
|
-
(0,
|
|
3628
|
+
(0, import_react10.createElement)(
|
|
2981
3629
|
"div",
|
|
2982
3630
|
{ className: "mailharbor-grid three" },
|
|
2983
|
-
(0,
|
|
3631
|
+
(0, import_react10.createElement)(
|
|
2984
3632
|
Field,
|
|
2985
3633
|
{ label: "SMTP \u4E3B\u673A" },
|
|
2986
|
-
(0,
|
|
3634
|
+
(0, import_react10.createElement)("input", {
|
|
2987
3635
|
value: smtpHost,
|
|
2988
3636
|
onChange: (event) => setSmtpHost(event.target.value)
|
|
2989
3637
|
})
|
|
2990
3638
|
),
|
|
2991
|
-
(0,
|
|
3639
|
+
(0, import_react10.createElement)(
|
|
2992
3640
|
Field,
|
|
2993
3641
|
{ label: "\u7AEF\u53E3" },
|
|
2994
|
-
(0,
|
|
3642
|
+
(0, import_react10.createElement)("input", {
|
|
2995
3643
|
value: smtpPort,
|
|
2996
3644
|
onChange: (event) => setSmtpPort(event.target.value)
|
|
2997
3645
|
})
|
|
2998
3646
|
),
|
|
2999
|
-
(0,
|
|
3647
|
+
(0, import_react10.createElement)(Toggle, { checked: smtpSecure, onChange: setSmtpSecure, label: "TLS" })
|
|
3000
3648
|
),
|
|
3001
|
-
(0,
|
|
3649
|
+
(0, import_react10.createElement)(
|
|
3002
3650
|
Field,
|
|
3003
3651
|
{ label: "\u53D1\u4EF6\u4EBA\u540D\u79F0" },
|
|
3004
|
-
(0,
|
|
3652
|
+
(0, import_react10.createElement)("input", {
|
|
3005
3653
|
value: fromName,
|
|
3006
3654
|
onChange: (event) => setFromName(event.target.value)
|
|
3007
3655
|
})
|
|
3008
3656
|
),
|
|
3009
|
-
(0,
|
|
3657
|
+
(0, import_react10.createElement)(
|
|
3010
3658
|
"div",
|
|
3011
3659
|
{ style: { display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" } },
|
|
3012
|
-
(0,
|
|
3660
|
+
(0, import_react10.createElement)(
|
|
3013
3661
|
"button",
|
|
3014
3662
|
{
|
|
3015
3663
|
className: "primary small",
|
|
@@ -3025,7 +3673,7 @@ function AccountRow({
|
|
|
3025
3673
|
},
|
|
3026
3674
|
"\u4FDD\u5B58\u8D26\u53F7"
|
|
3027
3675
|
),
|
|
3028
|
-
(0,
|
|
3676
|
+
(0, import_react10.createElement)(
|
|
3029
3677
|
"button",
|
|
3030
3678
|
{
|
|
3031
3679
|
className: "ghost small danger",
|
|
@@ -3034,21 +3682,90 @@ function AccountRow({
|
|
|
3034
3682
|
"\u79FB\u9664\u8D26\u53F7"
|
|
3035
3683
|
)
|
|
3036
3684
|
),
|
|
3037
|
-
(0,
|
|
3038
|
-
(0,
|
|
3685
|
+
(0, import_react10.createElement)("hr", { style: { border: 0, borderTop: "1px solid var(--m-line)", width: "100%" } }),
|
|
3686
|
+
(0, import_react10.createElement)("h4", { style: { margin: 0, fontSize: "12.5px" } }, "CalDAV \u4EFB\u52A1\u540C\u6B65"),
|
|
3687
|
+
(0, import_react10.createElement)(
|
|
3688
|
+
"div",
|
|
3689
|
+
{ className: "mailharbor-grid" },
|
|
3690
|
+
(0, import_react10.createElement)(
|
|
3691
|
+
Field,
|
|
3692
|
+
{ label: "CalDAV \u5730\u5740" },
|
|
3693
|
+
(0, import_react10.createElement)("input", {
|
|
3694
|
+
value: caldavUrl,
|
|
3695
|
+
placeholder: "https://caldav.example.com/",
|
|
3696
|
+
onChange: (event) => setCaldavUrl(event.target.value)
|
|
3697
|
+
})
|
|
3698
|
+
),
|
|
3699
|
+
(0, import_react10.createElement)(
|
|
3700
|
+
Field,
|
|
3701
|
+
{ label: "\u7528\u6237\u540D" },
|
|
3702
|
+
(0, import_react10.createElement)("input", {
|
|
3703
|
+
value: caldavUser,
|
|
3704
|
+
onChange: (event) => setCaldavUser(event.target.value)
|
|
3705
|
+
})
|
|
3706
|
+
)
|
|
3707
|
+
),
|
|
3708
|
+
(0, import_react10.createElement)(
|
|
3709
|
+
"div",
|
|
3710
|
+
{ className: "mailharbor-grid" },
|
|
3711
|
+
(0, import_react10.createElement)(
|
|
3712
|
+
Field,
|
|
3713
|
+
{ label: "\u5E94\u7528\u5BC6\u7801\uFF08\u7559\u7A7A\u8868\u793A\u4E0D\u4FEE\u6539\uFF09" },
|
|
3714
|
+
(0, import_react10.createElement)("input", {
|
|
3715
|
+
type: "password",
|
|
3716
|
+
value: caldavPassword,
|
|
3717
|
+
onChange: (event) => setCaldavPassword(event.target.value)
|
|
3718
|
+
})
|
|
3719
|
+
),
|
|
3720
|
+
(0, import_react10.createElement)(
|
|
3721
|
+
Field,
|
|
3722
|
+
{ label: "\u4EFB\u52A1\u65E5\u5386\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09" },
|
|
3723
|
+
(0, import_react10.createElement)("input", {
|
|
3724
|
+
value: caldavHref,
|
|
3725
|
+
placeholder: "\u81EA\u52A8\u53D1\u73B0",
|
|
3726
|
+
onChange: (event) => setCaldavHref(event.target.value)
|
|
3727
|
+
})
|
|
3728
|
+
)
|
|
3729
|
+
),
|
|
3730
|
+
(0, import_react10.createElement)(
|
|
3731
|
+
"div",
|
|
3732
|
+
{ style: { display: "flex", gap: "8px", alignItems: "center" } },
|
|
3733
|
+
(0, import_react10.createElement)(
|
|
3734
|
+
"button",
|
|
3735
|
+
{
|
|
3736
|
+
className: "ghost small",
|
|
3737
|
+
onClick: () => {
|
|
3738
|
+
setCaldavStatus("\u4FDD\u5B58\u4E2D\u2026");
|
|
3739
|
+
void api.saveTaskCaldav({
|
|
3740
|
+
accountId: account.id,
|
|
3741
|
+
url: caldavUrl,
|
|
3742
|
+
username: caldavUser,
|
|
3743
|
+
...caldavPassword === "" ? {} : { password: caldavPassword },
|
|
3744
|
+
calendarHref: caldavHref
|
|
3745
|
+
}).then((info) => {
|
|
3746
|
+
setCaldavStatus(info.hasPassword ? "\u5DF2\u4FDD\u5B58" : "\u672A\u4FDD\u5B58\u5BC6\u7801");
|
|
3747
|
+
setCaldavPassword("");
|
|
3748
|
+
}).catch((caught) => setCaldavStatus(errorText6(caught)));
|
|
3749
|
+
}
|
|
3750
|
+
},
|
|
3751
|
+
"\u4FDD\u5B58 CalDAV"
|
|
3752
|
+
),
|
|
3753
|
+
(0, import_react10.createElement)("span", { className: "small muted" }, caldavStatus)
|
|
3754
|
+
),
|
|
3755
|
+
(0, import_react10.createElement)(
|
|
3039
3756
|
Field,
|
|
3040
3757
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801\uFF08\u4E0D\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09" },
|
|
3041
|
-
(0,
|
|
3758
|
+
(0, import_react10.createElement)("input", {
|
|
3042
3759
|
type: "password",
|
|
3043
3760
|
value: password,
|
|
3044
3761
|
onChange: (event) => setPassword(event.target.value)
|
|
3045
3762
|
})
|
|
3046
3763
|
),
|
|
3047
|
-
(0,
|
|
3764
|
+
(0, import_react10.createElement)(
|
|
3048
3765
|
"div",
|
|
3049
3766
|
{ style: { display: "flex", gap: "8px", alignItems: "center" } },
|
|
3050
|
-
(0,
|
|
3051
|
-
(0,
|
|
3767
|
+
(0, import_react10.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801" }),
|
|
3768
|
+
(0, import_react10.createElement)(
|
|
3052
3769
|
"button",
|
|
3053
3770
|
{
|
|
3054
3771
|
className: "primary small",
|
|
@@ -3060,17 +3777,588 @@ function AccountRow({
|
|
|
3060
3777
|
},
|
|
3061
3778
|
"\u767B\u5F55"
|
|
3062
3779
|
),
|
|
3063
|
-
(0,
|
|
3064
|
-
)
|
|
3065
|
-
|
|
3780
|
+
(0, import_react10.createElement)("button", { className: "ghost small", onClick: () => void wrap(onLogout) }, "\u9000\u51FA\u767B\u5F55")
|
|
3781
|
+
)
|
|
3782
|
+
) : null
|
|
3783
|
+
);
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
// src/client/tasks-panel.tsx
|
|
3787
|
+
var import_react11 = require("react");
|
|
3788
|
+
var FILTERS = [
|
|
3789
|
+
{ id: "open", label: "\u5F85\u529E" },
|
|
3790
|
+
{ id: "today", label: "\u4ECA\u5929" },
|
|
3791
|
+
{ id: "week", label: "\u672C\u5468" },
|
|
3792
|
+
{ id: "overdue", label: "\u903E\u671F" },
|
|
3793
|
+
{ id: "done", label: "\u5DF2\u5B8C\u6210" },
|
|
3794
|
+
{ id: "all", label: "\u5168\u90E8" }
|
|
3795
|
+
];
|
|
3796
|
+
var STATUS_LABEL = {
|
|
3797
|
+
"NEEDS-ACTION": "\u5F85\u5904\u7406",
|
|
3798
|
+
"IN-PROCESS": "\u8FDB\u884C\u4E2D",
|
|
3799
|
+
COMPLETED: "\u5DF2\u5B8C\u6210",
|
|
3800
|
+
CANCELLED: "\u5DF2\u53D6\u6D88"
|
|
3801
|
+
};
|
|
3802
|
+
function errorText7(error) {
|
|
3803
|
+
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
3804
|
+
}
|
|
3805
|
+
function dateKey(value) {
|
|
3806
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
3807
|
+
return `${String(value.getFullYear())}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;
|
|
3808
|
+
}
|
|
3809
|
+
function weekEnd() {
|
|
3810
|
+
const now = /* @__PURE__ */ new Date();
|
|
3811
|
+
const end = new Date(now);
|
|
3812
|
+
end.setDate(now.getDate() + (7 - (now.getDay() + 6) % 7));
|
|
3813
|
+
return dateKey(end);
|
|
3814
|
+
}
|
|
3815
|
+
function dueText(due) {
|
|
3816
|
+
if (due === "") return "";
|
|
3817
|
+
const today = dateKey(/* @__PURE__ */ new Date());
|
|
3818
|
+
if (due.slice(0, 10) < today) return `\u903E\u671F ${due.slice(0, 10)}`;
|
|
3819
|
+
return due.slice(0, 10);
|
|
3820
|
+
}
|
|
3821
|
+
var EMPTY_TASK = {
|
|
3822
|
+
title: "",
|
|
3823
|
+
notes: "",
|
|
3824
|
+
due: "",
|
|
3825
|
+
priority: 0,
|
|
3826
|
+
status: "NEEDS-ACTION",
|
|
3827
|
+
owner: "",
|
|
3828
|
+
projectId: ""
|
|
3829
|
+
};
|
|
3830
|
+
function TasksIcon() {
|
|
3831
|
+
const [count, setCount] = (0, import_react11.useState)(0);
|
|
3832
|
+
(0, import_react11.useEffect)(() => {
|
|
3833
|
+
let cancelled = false;
|
|
3834
|
+
const load = async () => {
|
|
3835
|
+
try {
|
|
3836
|
+
const app = await api.state();
|
|
3837
|
+
const accountId = app.settings.activeAccountId;
|
|
3838
|
+
if (accountId === null) return;
|
|
3839
|
+
const result = await api.tasks(accountId);
|
|
3840
|
+
if (!cancelled) {
|
|
3841
|
+
setCount(
|
|
3842
|
+
result.items.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED").length
|
|
3843
|
+
);
|
|
3844
|
+
}
|
|
3845
|
+
} catch {
|
|
3846
|
+
}
|
|
3847
|
+
};
|
|
3848
|
+
void load();
|
|
3849
|
+
const timer = window.setInterval(() => {
|
|
3850
|
+
void load();
|
|
3851
|
+
}, 6e4);
|
|
3852
|
+
return () => {
|
|
3853
|
+
cancelled = true;
|
|
3854
|
+
window.clearInterval(timer);
|
|
3855
|
+
};
|
|
3856
|
+
}, []);
|
|
3857
|
+
return (0, import_react11.createElement)(
|
|
3858
|
+
"span",
|
|
3859
|
+
{
|
|
3860
|
+
style: {
|
|
3861
|
+
position: "relative",
|
|
3862
|
+
display: "inline-flex",
|
|
3863
|
+
alignItems: "center",
|
|
3864
|
+
justifyContent: "center",
|
|
3865
|
+
width: "18px",
|
|
3866
|
+
height: "18px"
|
|
3867
|
+
}
|
|
3868
|
+
},
|
|
3869
|
+
(0, import_react11.createElement)(Icon, { name: "checklist", size: 16 }),
|
|
3870
|
+
count > 0 ? (0, import_react11.createElement)(
|
|
3871
|
+
"span",
|
|
3872
|
+
{
|
|
3873
|
+
style: {
|
|
3874
|
+
position: "absolute",
|
|
3875
|
+
right: "-5px",
|
|
3876
|
+
bottom: "-3px",
|
|
3877
|
+
display: "flex",
|
|
3878
|
+
alignItems: "center",
|
|
3879
|
+
justifyContent: "center",
|
|
3880
|
+
boxSizing: "border-box",
|
|
3881
|
+
minWidth: "13px",
|
|
3882
|
+
height: "13px",
|
|
3883
|
+
padding: "0 3px",
|
|
3884
|
+
borderRadius: "999px",
|
|
3885
|
+
background: "#5686fe",
|
|
3886
|
+
color: "#fff",
|
|
3887
|
+
fontSize: "9px",
|
|
3888
|
+
fontWeight: 600,
|
|
3889
|
+
lineHeight: "13px",
|
|
3890
|
+
boxShadow: "0 0 0 2px var(--dsw-alias-bg-base, #151517)",
|
|
3891
|
+
pointerEvents: "none"
|
|
3892
|
+
}
|
|
3893
|
+
},
|
|
3894
|
+
count > 99 ? "99+" : String(count)
|
|
3066
3895
|
) : null
|
|
3067
3896
|
);
|
|
3068
3897
|
}
|
|
3898
|
+
function TasksPanel() {
|
|
3899
|
+
const [state, setState] = (0, import_react11.useState)(null);
|
|
3900
|
+
const [tasks, setTasks] = (0, import_react11.useState)([]);
|
|
3901
|
+
const [projects, setProjects] = (0, import_react11.useState)([]);
|
|
3902
|
+
const [calendar, setCalendar] = (0, import_react11.useState)(null);
|
|
3903
|
+
const [filter, setFilter] = (0, import_react11.useState)("open");
|
|
3904
|
+
const [error, setError] = (0, import_react11.useState)("");
|
|
3905
|
+
const [toast, setToast] = (0, import_react11.useState)("");
|
|
3906
|
+
const [busy, setBusy] = (0, import_react11.useState)(false);
|
|
3907
|
+
const [editing, setEditing] = (0, import_react11.useState)(null);
|
|
3908
|
+
const [creating, setCreating] = (0, import_react11.useState)(false);
|
|
3909
|
+
const [tick, setTick] = (0, import_react11.useState)(0);
|
|
3910
|
+
const accountId = state?.settings.activeAccountId ?? null;
|
|
3911
|
+
(0, import_react11.useEffect)(() => {
|
|
3912
|
+
api.state().then(setState).catch((caught) => setError(errorText7(caught)));
|
|
3913
|
+
}, [tick]);
|
|
3914
|
+
(0, import_react11.useEffect)(() => {
|
|
3915
|
+
if (accountId === null) return;
|
|
3916
|
+
let cancelled = false;
|
|
3917
|
+
Promise.all([api.tasks(accountId), api.projects()]).then(([list, projectList]) => {
|
|
3918
|
+
if (cancelled) return;
|
|
3919
|
+
setTasks(list.items);
|
|
3920
|
+
setCalendar(list.calendar);
|
|
3921
|
+
setProjects(projectList.items);
|
|
3922
|
+
}).catch((caught) => {
|
|
3923
|
+
if (!cancelled) setError(errorText7(caught));
|
|
3924
|
+
});
|
|
3925
|
+
return () => {
|
|
3926
|
+
cancelled = true;
|
|
3927
|
+
};
|
|
3928
|
+
}, [accountId, tick]);
|
|
3929
|
+
const refresh = (0, import_react11.useCallback)(() => {
|
|
3930
|
+
setTick((value) => value + 1);
|
|
3931
|
+
}, []);
|
|
3932
|
+
const notify = (message) => {
|
|
3933
|
+
setToast(message);
|
|
3934
|
+
window.setTimeout(() => {
|
|
3935
|
+
setToast("");
|
|
3936
|
+
}, 3e3);
|
|
3937
|
+
};
|
|
3938
|
+
const visible = (0, import_react11.useMemo)(() => {
|
|
3939
|
+
const today = dateKey(/* @__PURE__ */ new Date());
|
|
3940
|
+
const end = weekEnd();
|
|
3941
|
+
return tasks.filter((task) => {
|
|
3942
|
+
const done2 = task.status === "COMPLETED" || task.status === "CANCELLED";
|
|
3943
|
+
if (filter === "done") return done2;
|
|
3944
|
+
if (done2) return false;
|
|
3945
|
+
if (filter === "all") return true;
|
|
3946
|
+
if (filter === "overdue") return task.due !== "" && task.due.slice(0, 10) < today;
|
|
3947
|
+
if (filter === "today") return task.due.slice(0, 10) === today;
|
|
3948
|
+
if (filter === "week") return task.due !== "" && task.due.slice(0, 10) <= end;
|
|
3949
|
+
return true;
|
|
3950
|
+
});
|
|
3951
|
+
}, [tasks, filter]);
|
|
3952
|
+
const open = tasks.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED");
|
|
3953
|
+
const done = tasks.length - open.length;
|
|
3954
|
+
const progress = tasks.length === 0 ? 0 : Math.round(done / tasks.length * 100);
|
|
3955
|
+
const byProject = (0, import_react11.useMemo)(() => {
|
|
3956
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3957
|
+
for (const task of tasks) {
|
|
3958
|
+
const key = task.projectId ?? "";
|
|
3959
|
+
const group = groups.get(key) ?? {
|
|
3960
|
+
project: projects.find((project) => project.id === key) ?? null,
|
|
3961
|
+
total: 0,
|
|
3962
|
+
done: 0
|
|
3963
|
+
};
|
|
3964
|
+
group.total += 1;
|
|
3965
|
+
if (task.status === "COMPLETED" || task.status === "CANCELLED") group.done += 1;
|
|
3966
|
+
groups.set(key, group);
|
|
3967
|
+
}
|
|
3968
|
+
return [...groups.values()].filter((group) => group.project !== null);
|
|
3969
|
+
}, [tasks, projects]);
|
|
3970
|
+
const switchAccount = async (id) => {
|
|
3971
|
+
try {
|
|
3972
|
+
await api.activateAccount(id);
|
|
3973
|
+
refresh();
|
|
3974
|
+
} catch (caught) {
|
|
3975
|
+
setError(errorText7(caught));
|
|
3976
|
+
}
|
|
3977
|
+
};
|
|
3978
|
+
const toggle = async (task) => {
|
|
3979
|
+
try {
|
|
3980
|
+
const next = task.status === "COMPLETED" ? "NEEDS-ACTION" : "COMPLETED";
|
|
3981
|
+
const { task: updated } = await api.updateTask(task.id, { status: next });
|
|
3982
|
+
setTasks((current) => current.map((item) => item.id === task.id ? updated : item));
|
|
3983
|
+
} catch (caught) {
|
|
3984
|
+
setError(errorText7(caught));
|
|
3985
|
+
}
|
|
3986
|
+
};
|
|
3987
|
+
const remove = async (task) => {
|
|
3988
|
+
try {
|
|
3989
|
+
await api.deleteTask(task.id);
|
|
3990
|
+
setTasks((current) => current.filter((item) => item.id !== task.id));
|
|
3991
|
+
} catch (caught) {
|
|
3992
|
+
setError(errorText7(caught));
|
|
3993
|
+
}
|
|
3994
|
+
};
|
|
3995
|
+
const openSource = async (task) => {
|
|
3996
|
+
if (task.messageKey === "") return;
|
|
3997
|
+
try {
|
|
3998
|
+
const found = await api.lookupMessage(task.accountId, task.messageKey);
|
|
3999
|
+
requestOpenMail({ accountId: task.accountId, id: found.id, mailbox: found.mailbox });
|
|
4000
|
+
document.querySelector('button[aria-label="MailHarbor"]')?.click();
|
|
4001
|
+
} catch (caught) {
|
|
4002
|
+
setError(errorText7(caught));
|
|
4003
|
+
}
|
|
4004
|
+
};
|
|
4005
|
+
const sync = async () => {
|
|
4006
|
+
if (accountId === null) return;
|
|
4007
|
+
setBusy(true);
|
|
4008
|
+
setError("");
|
|
4009
|
+
try {
|
|
4010
|
+
const result = await api.syncTasks(accountId);
|
|
4011
|
+
notify(`\u5DF2\u540C\u6B65\uFF1A\u62C9\u53D6 ${String(result.result.pulled)}\uFF0C\u63A8\u9001 ${String(result.result.pushed)}`);
|
|
4012
|
+
refresh();
|
|
4013
|
+
} catch (caught) {
|
|
4014
|
+
setError(errorText7(caught));
|
|
4015
|
+
} finally {
|
|
4016
|
+
setBusy(false);
|
|
4017
|
+
}
|
|
4018
|
+
};
|
|
4019
|
+
const save = async (draft) => {
|
|
4020
|
+
setBusy(true);
|
|
4021
|
+
setError("");
|
|
4022
|
+
try {
|
|
4023
|
+
if (draft.id === void 0) {
|
|
4024
|
+
await api.createTask({
|
|
4025
|
+
accountId,
|
|
4026
|
+
title: draft.title,
|
|
4027
|
+
notes: draft.notes,
|
|
4028
|
+
due: draft.due,
|
|
4029
|
+
priority: draft.priority,
|
|
4030
|
+
status: draft.status,
|
|
4031
|
+
owner: draft.owner,
|
|
4032
|
+
projectId: draft.projectId === "" ? null : draft.projectId
|
|
4033
|
+
});
|
|
4034
|
+
} else {
|
|
4035
|
+
await api.updateTask(draft.id, {
|
|
4036
|
+
title: draft.title,
|
|
4037
|
+
notes: draft.notes,
|
|
4038
|
+
due: draft.due,
|
|
4039
|
+
priority: draft.priority,
|
|
4040
|
+
status: draft.status,
|
|
4041
|
+
owner: draft.owner,
|
|
4042
|
+
projectId: draft.projectId === "" ? null : draft.projectId
|
|
4043
|
+
});
|
|
4044
|
+
}
|
|
4045
|
+
setEditing(null);
|
|
4046
|
+
setCreating(false);
|
|
4047
|
+
refresh();
|
|
4048
|
+
} catch (caught) {
|
|
4049
|
+
setError(errorText7(caught));
|
|
4050
|
+
} finally {
|
|
4051
|
+
setBusy(false);
|
|
4052
|
+
}
|
|
4053
|
+
};
|
|
4054
|
+
return (0, import_react11.createElement)(
|
|
4055
|
+
"div",
|
|
4056
|
+
{ className: "mailharbor" },
|
|
4057
|
+
(0, import_react11.createElement)(
|
|
4058
|
+
"div",
|
|
4059
|
+
{ className: "mailharbor-topbar" },
|
|
4060
|
+
(0, import_react11.createElement)(Icon, { name: "checklist", size: 16 }),
|
|
4061
|
+
(0, import_react11.createElement)("strong", { style: { fontSize: "13px" } }, "\u4EFB\u52A1"),
|
|
4062
|
+
state === null || state.accounts.length < 2 ? null : (0, import_react11.createElement)(
|
|
4063
|
+
"select",
|
|
4064
|
+
{
|
|
4065
|
+
value: accountId ?? "",
|
|
4066
|
+
onChange: (event) => void switchAccount(event.target.value),
|
|
4067
|
+
"aria-label": "\u8D26\u53F7"
|
|
4068
|
+
},
|
|
4069
|
+
state.accounts.map(
|
|
4070
|
+
(account) => (0, import_react11.createElement)("option", { key: account.id, value: account.id }, account.label)
|
|
4071
|
+
)
|
|
4072
|
+
),
|
|
4073
|
+
(0, import_react11.createElement)("span", { className: "spacer" }),
|
|
4074
|
+
calendar === null ? null : (0, import_react11.createElement)(
|
|
4075
|
+
"span",
|
|
4076
|
+
{ className: "small muted" },
|
|
4077
|
+
calendar.lastSync === "" ? "\u672A\u540C\u6B65" : `\u540C\u6B65\u4E8E ${new Date(calendar.lastSync).toLocaleString()}`
|
|
4078
|
+
),
|
|
4079
|
+
(0, import_react11.createElement)(
|
|
4080
|
+
"button",
|
|
4081
|
+
{ className: "ghost", onClick: () => void sync(), disabled: busy },
|
|
4082
|
+
busy ? "\u540C\u6B65\u4E2D\u2026" : "\u540C\u6B65 CalDAV"
|
|
4083
|
+
),
|
|
4084
|
+
(0, import_react11.createElement)(
|
|
4085
|
+
"button",
|
|
4086
|
+
{ className: "primary", onClick: () => setCreating(true) },
|
|
4087
|
+
(0, import_react11.createElement)(Icon, { name: "plus" }),
|
|
4088
|
+
"\u65B0\u5EFA\u4EFB\u52A1"
|
|
4089
|
+
)
|
|
4090
|
+
),
|
|
4091
|
+
toast === "" ? null : (0, import_react11.createElement)("div", { className: "small", style: { padding: "6px 14px", color: "var(--m-ok)" } }, toast),
|
|
4092
|
+
error === "" ? null : (0, import_react11.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
|
|
4093
|
+
(0, import_react11.createElement)(
|
|
4094
|
+
"div",
|
|
4095
|
+
{ className: "tasks-body" },
|
|
4096
|
+
(0, import_react11.createElement)(
|
|
4097
|
+
"div",
|
|
4098
|
+
{ className: "tasks-overview" },
|
|
4099
|
+
(0, import_react11.createElement)(
|
|
4100
|
+
"div",
|
|
4101
|
+
{ className: "tasks-progress" },
|
|
4102
|
+
(0, import_react11.createElement)(
|
|
4103
|
+
"div",
|
|
4104
|
+
{ className: "tasks-progress-head" },
|
|
4105
|
+
(0, import_react11.createElement)("strong", null, "\u603B\u4F53\u8FDB\u5EA6"),
|
|
4106
|
+
(0, import_react11.createElement)(
|
|
4107
|
+
"span",
|
|
4108
|
+
{ className: "muted small" },
|
|
4109
|
+
`${String(done)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
|
|
4110
|
+
)
|
|
4111
|
+
),
|
|
4112
|
+
(0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } })),
|
|
4113
|
+
(0, import_react11.createElement)("div", { className: "small muted" }, `\u5F85\u529E ${String(open.length)} \u6761`)
|
|
4114
|
+
),
|
|
4115
|
+
...byProject.map(
|
|
4116
|
+
(group) => (0, import_react11.createElement)(
|
|
4117
|
+
"div",
|
|
4118
|
+
{ key: group.project?.id ?? "none", className: "tasks-project" },
|
|
4119
|
+
(0, import_react11.createElement)(
|
|
4120
|
+
"div",
|
|
4121
|
+
{ className: "tasks-project-head" },
|
|
4122
|
+
(0, import_react11.createElement)("span", { className: "name" }, group.project?.name ?? ""),
|
|
4123
|
+
(0, import_react11.createElement)("span", { className: "muted small" }, `${String(group.done)}/${String(group.total)}`)
|
|
4124
|
+
),
|
|
4125
|
+
(0, import_react11.createElement)(
|
|
4126
|
+
"div",
|
|
4127
|
+
{ className: "tasks-bar small" },
|
|
4128
|
+
(0, import_react11.createElement)("span", {
|
|
4129
|
+
style: {
|
|
4130
|
+
width: `${String(group.total === 0 ? 0 : Math.round(group.done / group.total * 100))}%`
|
|
4131
|
+
}
|
|
4132
|
+
})
|
|
4133
|
+
)
|
|
4134
|
+
)
|
|
4135
|
+
)
|
|
4136
|
+
),
|
|
4137
|
+
(0, import_react11.createElement)(
|
|
4138
|
+
"div",
|
|
4139
|
+
{ className: "tasks-list-wrap" },
|
|
4140
|
+
(0, import_react11.createElement)(
|
|
4141
|
+
"div",
|
|
4142
|
+
{ className: "tasks-filters" },
|
|
4143
|
+
FILTERS.map(
|
|
4144
|
+
(item) => (0, import_react11.createElement)(
|
|
4145
|
+
"button",
|
|
4146
|
+
{
|
|
4147
|
+
key: item.id,
|
|
4148
|
+
className: filter === item.id ? "chip active" : "chip",
|
|
4149
|
+
onClick: () => setFilter(item.id)
|
|
4150
|
+
},
|
|
4151
|
+
item.label
|
|
4152
|
+
)
|
|
4153
|
+
)
|
|
4154
|
+
),
|
|
4155
|
+
visible.length === 0 ? (0, import_react11.createElement)(
|
|
4156
|
+
"div",
|
|
4157
|
+
{ className: "mailharbor-empty" },
|
|
4158
|
+
(0, import_react11.createElement)(Icon, { name: "checklist", size: 28 }),
|
|
4159
|
+
(0, import_react11.createElement)("div", { className: "title" }, "\u6CA1\u6709\u4EFB\u52A1"),
|
|
4160
|
+
(0, import_react11.createElement)("div", { className: "small" }, "\u53EF\u4EE5\u5728\u8FD9\u91CC\u65B0\u5EFA\uFF0C\u6216\u5728\u90AE\u4EF6\u91CC\u7528\u300C\u63D0\u53D6\u4EFB\u52A1\u300D\u3002")
|
|
4161
|
+
) : (0, import_react11.createElement)(
|
|
4162
|
+
"div",
|
|
4163
|
+
{ className: "tasks-list" },
|
|
4164
|
+
visible.map(
|
|
4165
|
+
(task) => (0, import_react11.createElement)(
|
|
4166
|
+
"div",
|
|
4167
|
+
{ key: task.id, className: `tasks-row${task.status === "COMPLETED" ? " done" : ""}` },
|
|
4168
|
+
(0, import_react11.createElement)("input", {
|
|
4169
|
+
type: "checkbox",
|
|
4170
|
+
checked: task.status === "COMPLETED",
|
|
4171
|
+
"aria-label": "\u5B8C\u6210\u4EFB\u52A1",
|
|
4172
|
+
onChange: () => void toggle(task)
|
|
4173
|
+
}),
|
|
4174
|
+
(0, import_react11.createElement)(
|
|
4175
|
+
"button",
|
|
4176
|
+
{ className: "tasks-title", onClick: () => setEditing(task) },
|
|
4177
|
+
(0, import_react11.createElement)("span", null, task.title),
|
|
4178
|
+
(0, import_react11.createElement)(
|
|
4179
|
+
"span",
|
|
4180
|
+
{ className: "tasks-meta" },
|
|
4181
|
+
task.due === "" ? null : (0, import_react11.createElement)(
|
|
4182
|
+
"span",
|
|
4183
|
+
{
|
|
4184
|
+
className: task.due.slice(0, 10) < dateKey(/* @__PURE__ */ new Date()) ? "due overdue" : "due"
|
|
4185
|
+
},
|
|
4186
|
+
dueText(task.due)
|
|
4187
|
+
),
|
|
4188
|
+
task.owner === "" ? null : (0, import_react11.createElement)("span", null, task.owner),
|
|
4189
|
+
(0, import_react11.createElement)("span", { className: "badge" }, STATUS_LABEL[task.status] ?? task.status),
|
|
4190
|
+
task.projectId === null ? null : (0, import_react11.createElement)(
|
|
4191
|
+
"span",
|
|
4192
|
+
{ className: "badge" },
|
|
4193
|
+
projects.find((project) => project.id === task.projectId)?.name ?? "\u9879\u76EE"
|
|
4194
|
+
)
|
|
4195
|
+
)
|
|
4196
|
+
),
|
|
4197
|
+
task.messageKey === "" ? null : (0, import_react11.createElement)(
|
|
4198
|
+
"button",
|
|
4199
|
+
{
|
|
4200
|
+
className: "subtle small",
|
|
4201
|
+
title: "\u6253\u5F00\u6765\u6E90\u90AE\u4EF6",
|
|
4202
|
+
onClick: () => void openSource(task)
|
|
4203
|
+
},
|
|
4204
|
+
"\u6765\u6E90"
|
|
4205
|
+
),
|
|
4206
|
+
(0, import_react11.createElement)(
|
|
4207
|
+
"button",
|
|
4208
|
+
{ className: "subtle icon", title: "\u5220\u9664", onClick: () => void remove(task) },
|
|
4209
|
+
(0, import_react11.createElement)(Icon, { name: "trash", size: 13 })
|
|
4210
|
+
)
|
|
4211
|
+
)
|
|
4212
|
+
)
|
|
4213
|
+
)
|
|
4214
|
+
)
|
|
4215
|
+
),
|
|
4216
|
+
creating || editing !== null ? (0, import_react11.createElement)(TaskEditor, {
|
|
4217
|
+
task: editing,
|
|
4218
|
+
projects,
|
|
4219
|
+
busy,
|
|
4220
|
+
onClose: () => {
|
|
4221
|
+
setCreating(false);
|
|
4222
|
+
setEditing(null);
|
|
4223
|
+
},
|
|
4224
|
+
onSave: (draft) => void save(draft)
|
|
4225
|
+
}) : null
|
|
4226
|
+
);
|
|
4227
|
+
}
|
|
4228
|
+
function TaskEditor({
|
|
4229
|
+
task,
|
|
4230
|
+
projects,
|
|
4231
|
+
busy,
|
|
4232
|
+
onClose,
|
|
4233
|
+
onSave
|
|
4234
|
+
}) {
|
|
4235
|
+
const [draft, setDraft] = (0, import_react11.useState)({
|
|
4236
|
+
...EMPTY_TASK,
|
|
4237
|
+
...task === null ? {} : {
|
|
4238
|
+
id: task.id,
|
|
4239
|
+
title: task.title,
|
|
4240
|
+
notes: task.notes,
|
|
4241
|
+
due: task.due.slice(0, 10),
|
|
4242
|
+
priority: task.priority,
|
|
4243
|
+
status: task.status,
|
|
4244
|
+
owner: task.owner,
|
|
4245
|
+
projectId: task.projectId ?? ""
|
|
4246
|
+
}
|
|
4247
|
+
});
|
|
4248
|
+
const set = (patch) => setDraft((current) => ({ ...current, ...patch }));
|
|
4249
|
+
return (0, import_react11.createElement)(
|
|
4250
|
+
Modal,
|
|
4251
|
+
{
|
|
4252
|
+
title: task === null ? "\u65B0\u5EFA\u4EFB\u52A1" : "\u7F16\u8F91\u4EFB\u52A1",
|
|
4253
|
+
onClose,
|
|
4254
|
+
footer: (0, import_react11.createElement)(
|
|
4255
|
+
"div",
|
|
4256
|
+
{ style: { display: "flex", gap: "8px", width: "100%", justifyContent: "flex-end" } },
|
|
4257
|
+
(0, import_react11.createElement)("button", { className: "ghost", onClick: onClose }, "\u53D6\u6D88"),
|
|
4258
|
+
(0, import_react11.createElement)(
|
|
4259
|
+
"button",
|
|
4260
|
+
{ className: "primary", disabled: busy || draft.title.trim() === "", onClick: () => onSave(draft) },
|
|
4261
|
+
busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
|
|
4262
|
+
)
|
|
4263
|
+
)
|
|
4264
|
+
},
|
|
4265
|
+
(0, import_react11.createElement)(
|
|
4266
|
+
"div",
|
|
4267
|
+
{ className: "mailharbor-field" },
|
|
4268
|
+
(0, import_react11.createElement)("label", null, "\u6807\u9898"),
|
|
4269
|
+
(0, import_react11.createElement)("input", {
|
|
4270
|
+
value: draft.title,
|
|
4271
|
+
autoFocus: true,
|
|
4272
|
+
onChange: (event) => set({ title: event.target.value })
|
|
4273
|
+
})
|
|
4274
|
+
),
|
|
4275
|
+
(0, import_react11.createElement)(
|
|
4276
|
+
"div",
|
|
4277
|
+
{ className: "mailharbor-field" },
|
|
4278
|
+
(0, import_react11.createElement)("label", null, "\u5907\u6CE8"),
|
|
4279
|
+
(0, import_react11.createElement)("textarea", {
|
|
4280
|
+
rows: 3,
|
|
4281
|
+
value: draft.notes,
|
|
4282
|
+
onChange: (event) => set({ notes: event.target.value })
|
|
4283
|
+
})
|
|
4284
|
+
),
|
|
4285
|
+
(0, import_react11.createElement)(
|
|
4286
|
+
"div",
|
|
4287
|
+
{ className: "mailharbor-grid" },
|
|
4288
|
+
(0, import_react11.createElement)(
|
|
4289
|
+
"div",
|
|
4290
|
+
{ className: "mailharbor-field" },
|
|
4291
|
+
(0, import_react11.createElement)("label", null, "\u622A\u6B62\u65E5\u671F"),
|
|
4292
|
+
(0, import_react11.createElement)("input", {
|
|
4293
|
+
type: "date",
|
|
4294
|
+
value: draft.due,
|
|
4295
|
+
onChange: (event) => set({ due: event.target.value })
|
|
4296
|
+
})
|
|
4297
|
+
),
|
|
4298
|
+
(0, import_react11.createElement)(
|
|
4299
|
+
"div",
|
|
4300
|
+
{ className: "mailharbor-field" },
|
|
4301
|
+
(0, import_react11.createElement)("label", null, "\u4F18\u5148\u7EA7"),
|
|
4302
|
+
(0, import_react11.createElement)(
|
|
4303
|
+
"select",
|
|
4304
|
+
{
|
|
4305
|
+
value: String(draft.priority),
|
|
4306
|
+
onChange: (event) => set({ priority: Number(event.target.value) })
|
|
4307
|
+
},
|
|
4308
|
+
(0, import_react11.createElement)("option", { value: "0" }, "\u65E0"),
|
|
4309
|
+
(0, import_react11.createElement)("option", { value: "5" }, "\u4E2D"),
|
|
4310
|
+
(0, import_react11.createElement)("option", { value: "9" }, "\u9AD8")
|
|
4311
|
+
)
|
|
4312
|
+
)
|
|
4313
|
+
),
|
|
4314
|
+
(0, import_react11.createElement)(
|
|
4315
|
+
"div",
|
|
4316
|
+
{ className: "mailharbor-grid" },
|
|
4317
|
+
(0, import_react11.createElement)(
|
|
4318
|
+
"div",
|
|
4319
|
+
{ className: "mailharbor-field" },
|
|
4320
|
+
(0, import_react11.createElement)("label", null, "\u72B6\u6001"),
|
|
4321
|
+
(0, import_react11.createElement)(
|
|
4322
|
+
"select",
|
|
4323
|
+
{
|
|
4324
|
+
value: draft.status,
|
|
4325
|
+
onChange: (event) => set({ status: event.target.value })
|
|
4326
|
+
},
|
|
4327
|
+
Object.entries(STATUS_LABEL).map(([value, label]) => (0, import_react11.createElement)("option", { key: value, value }, label))
|
|
4328
|
+
)
|
|
4329
|
+
),
|
|
4330
|
+
(0, import_react11.createElement)(
|
|
4331
|
+
"div",
|
|
4332
|
+
{ className: "mailharbor-field" },
|
|
4333
|
+
(0, import_react11.createElement)("label", null, "\u9879\u76EE"),
|
|
4334
|
+
(0, import_react11.createElement)(
|
|
4335
|
+
"select",
|
|
4336
|
+
{
|
|
4337
|
+
value: draft.projectId,
|
|
4338
|
+
onChange: (event) => set({ projectId: event.target.value })
|
|
4339
|
+
},
|
|
4340
|
+
(0, import_react11.createElement)("option", { value: "" }, "\u65E0\u9879\u76EE"),
|
|
4341
|
+
projects.map((project) => (0, import_react11.createElement)("option", { key: project.id, value: project.id }, project.name))
|
|
4342
|
+
)
|
|
4343
|
+
)
|
|
4344
|
+
),
|
|
4345
|
+
(0, import_react11.createElement)(
|
|
4346
|
+
"div",
|
|
4347
|
+
{ className: "mailharbor-field" },
|
|
4348
|
+
(0, import_react11.createElement)("label", null, "\u8D1F\u8D23\u4EBA"),
|
|
4349
|
+
(0, import_react11.createElement)("input", {
|
|
4350
|
+
value: draft.owner,
|
|
4351
|
+
onChange: (event) => set({ owner: event.target.value })
|
|
4352
|
+
})
|
|
4353
|
+
)
|
|
4354
|
+
);
|
|
4355
|
+
}
|
|
3069
4356
|
|
|
3070
4357
|
// src/client/index.tsx
|
|
3071
|
-
var VERSION = "0.
|
|
4358
|
+
var VERSION = "0.5.0";
|
|
3072
4359
|
var inject = ["slots"];
|
|
3073
4360
|
var PANEL_ID = "mailharbor";
|
|
4361
|
+
var TASKS_PANEL_ID = "mailharbor-tasks";
|
|
3074
4362
|
var STYLE_ID = "mailharbor-style";
|
|
3075
4363
|
function ensureStyles() {
|
|
3076
4364
|
if (document.getElementById(STYLE_ID) !== null) return;
|
|
@@ -3106,6 +4394,19 @@ function apply(ctx) {
|
|
|
3106
4394
|
MailIcon
|
|
3107
4395
|
)
|
|
3108
4396
|
);
|
|
4397
|
+
ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: TASKS_PANEL_ID }, TasksPanel));
|
|
4398
|
+
ctx.slots.inject(
|
|
4399
|
+
"sidebar.panellist",
|
|
4400
|
+
() => ctx.slots.register(
|
|
4401
|
+
{
|
|
4402
|
+
name: "sidebar.panellist",
|
|
4403
|
+
id: TASKS_PANEL_ID,
|
|
4404
|
+
order: 21,
|
|
4405
|
+
label: "\u4EFB\u52A1"
|
|
4406
|
+
},
|
|
4407
|
+
TasksIcon
|
|
4408
|
+
)
|
|
4409
|
+
);
|
|
3109
4410
|
}
|
|
3110
4411
|
|
|
3111
4412
|
return module.exports;
|