@zaclinley/dsh-mailharbor 0.6.0 → 0.7.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 +17 -0
- package/README.md +18 -18
- package/README.zh.md +18 -18
- package/lib/client.js +269 -159
- package/lib/client.js.map +3 -3
- package/lib/index.js +248 -1
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -35,7 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
37
|
// src/client/styles.css
|
|
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\n/* ---------- mailbox sidebar ---------- */\n\n.mailharbor-side {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 214px;\n flex: none;\n padding: 10px 8px;\n overflow: auto;\n border-right: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-side-section {\n padding: 12px 8px 4px;\n font-size: 10.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 32px;\n padding: 0 8px;\n border-radius: 8px;\n color: var(--m-text-2);\n font-size: 12.5px;\n text-align: left;\n flex: none;\n}\n\n.mailharbor-side-item:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-side-item.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-side-item .name {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-side-item .sub {\n display: block;\n font-size: 10.5px;\n color: var(--m-text-3);\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor-side-item .count {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item svg {\n flex: none;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item.active svg {\n color: var(--m-accent);\n}\n\n@media (max-width: 900px) {\n .mailharbor-side {\n display: none;\n }\n}\n\n/* ---------- task board ---------- */\n\n.tasks-board-wrap {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px 18px 40px;\n}\n\n.tasks-summary {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.tasks-summary-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 7px;\n}\n\n.tasks-summary-labels {\n display: flex;\n align-items: baseline;\n gap: 10px;\n}\n\n.tasks-stats {\n display: flex;\n gap: 18px;\n}\n\n.tasks-stat {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 42px;\n}\n\n.tasks-stat strong {\n font-size: 16px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.tasks-stat span {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-stat.danger strong {\n color: var(--m-danger);\n}\n\n.tasks-toolbar {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.tasks-board {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 14px;\n align-items: start;\n}\n\n.task-column {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 140px;\n padding: 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.task-column-head {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 2px 4px 6px;\n}\n\n.task-column-head strong {\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.task-column-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 999px;\n background: var(--m-bg-3);\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-dot {\n width: 7px;\n height: 7px;\n border-radius: 999px;\n background: var(--m-text-3);\n}\n\n.task-dot.NEEDS-ACTION {\n background: #8a8f98;\n}\n\n.task-dot.IN-PROCESS {\n background: var(--m-accent);\n}\n\n.task-dot.COMPLETED {\n background: var(--m-ok);\n}\n\n.task-column-body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 60px;\n}\n\n.task-column-empty {\n padding: 18px 8px;\n text-align: center;\n color: var(--m-text-3);\n font-size: 11.5px;\n border: 1px dashed var(--m-line-soft);\n border-radius: 9px;\n}\n\n.task-card {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 11px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg);\n cursor: grab;\n transition:\n border-color 120ms ease,\n box-shadow 120ms ease;\n}\n\n.task-card:hover {\n border-color: var(--m-line);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);\n}\n\n.task-card.dragging {\n opacity: 0.5;\n}\n\n.task-card-top {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n}\n\n.task-card-title {\n flex: 1;\n min-width: 0;\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--m-text);\n overflow-wrap: anywhere;\n}\n\n.task-priority {\n width: 3px;\n height: 14px;\n margin-top: 2px;\n border-radius: 999px;\n background: transparent;\n flex: none;\n}\n\n.task-priority.p5 {\n background: #d9a441;\n}\n\n.task-priority.p9 {\n background: var(--m-danger);\n}\n\n.task-card-delete {\n width: 18px;\n height: 18px;\n flex: none;\n border-radius: 5px;\n color: var(--m-text-3);\n opacity: 0;\n}\n\n.task-card:hover .task-card-delete {\n opacity: 1;\n}\n\n.task-card-notes {\n margin: 0;\n font-size: 11.5px;\n line-height: 1.55;\n color: var(--m-text-3);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.task-card-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 10.5px;\n color: var(--m-text-3);\n}\n\n.task-due,\n.task-owner,\n.task-project,\n.task-priority-label {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n padding: 1px 6px;\n border-radius: 5px;\n background: var(--m-bg-2);\n}\n\n.task-due.overdue {\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.12);\n}\n\n.task-project {\n color: var(--m-accent);\n background: var(--m-accent-soft);\n}\n\n.task-source {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 22px;\n padding: 0 6px;\n border-radius: 5px;\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-source:hover {\n background: var(--m-hover);\n color: var(--m-text-2);\n}\n\n/* ---------- task editor ---------- */\n\n.mailharbor-tasks .mailharbor-modal {\n max-width: 760px;\n}\n\n.task-editor {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 230px;\n gap: 18px;\n}\n\n.task-editor-main {\n display: flex;\n flex-direction: column;\n gap: 14px;\n min-width: 0;\n}\n\n.task-editor-title input {\n width: 100%;\n height: 38px;\n font-size: 15px;\n font-weight: 500;\n}\n\n.task-editor-section {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.task-editor-section label,\n.task-editor-field label {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.task-editor-section textarea {\n width: 100%;\n resize: vertical;\n}\n\n.task-editor-side {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n align-self: start;\n}\n\n.task-editor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.task-editor-field input,\n.task-editor-field select {\n width: 100%;\n}\n\n.task-priority-picker {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n padding: 3px;\n border: 1px solid var(--m-line);\n border-radius: 8px;\n background: var(--m-bg);\n}\n\n.task-priority-picker button {\n height: 26px;\n border-radius: 6px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.task-priority-picker button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.task-editor-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\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\n/* ---------- conversation rows ---------- */\n\n.mailharbor-thread {\n display: flex;\n flex-direction: column;\n}\n\n.mailharbor-row.compact {\n padding-top: 6px;\n padding-bottom: 6px;\n padding-left: 34px;\n background: var(--m-bg);\n}\n\n.mailharbor-row.compact .line-1 {\n opacity: 0.85;\n}\n\n.mailharbor-row.compact .snippet {\n -webkit-line-clamp: 1;\n}\n\n.mailharbor .to-label {\n font-size: 10.5px;\n color: var(--m-text-3);\n flex: none;\n}\n\n.mailharbor .thread-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 999px;\n background: var(--m-bg-3);\n color: var(--m-text-2);\n font-size: 10.5px;\n flex: none;\n}\n\n.mailharbor .thread-count:hover {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-account {\n height: auto;\n padding: 3px 8px 3px 3px;\n border-radius: 8px;\n gap: 8px;\n}\n\n.mailharbor-account:hover {\n background: var(--m-hover);\n}\n\n/* ---------- mailbox sidebar ---------- */\n\n.mailharbor-side {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 214px;\n flex: none;\n padding: 10px 8px;\n overflow: auto;\n border-right: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-side-section {\n padding: 12px 8px 4px;\n font-size: 10.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 32px;\n padding: 0 8px;\n border-radius: 8px;\n color: var(--m-text-2);\n font-size: 12.5px;\n text-align: left;\n flex: none;\n}\n\n.mailharbor-side-item:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-side-item.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-side-item .name {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-side-item .sub {\n display: block;\n font-size: 10.5px;\n color: var(--m-text-3);\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor-side-item .count {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item svg {\n flex: none;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item.active svg {\n color: var(--m-accent);\n}\n\n@media (max-width: 900px) {\n .mailharbor-side {\n display: none;\n }\n}\n\n/* ---------- task board ---------- */\n\n.tasks-board-wrap {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px 18px 40px;\n}\n\n.tasks-summary {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.tasks-summary-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 7px;\n}\n\n.tasks-summary-labels {\n display: flex;\n align-items: baseline;\n gap: 10px;\n}\n\n.tasks-stats {\n display: flex;\n gap: 18px;\n}\n\n.tasks-stat {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 42px;\n}\n\n.tasks-stat strong {\n font-size: 16px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.tasks-stat span {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-stat.danger strong {\n color: var(--m-danger);\n}\n\n.tasks-toolbar {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.tasks-board {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 14px;\n align-items: start;\n}\n\n.task-column {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 140px;\n padding: 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.task-column-head {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 2px 4px 6px;\n}\n\n.task-column-head strong {\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.task-column-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 999px;\n background: var(--m-bg-3);\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-dot {\n width: 7px;\n height: 7px;\n border-radius: 999px;\n background: var(--m-text-3);\n}\n\n.task-dot.NEEDS-ACTION {\n background: #8a8f98;\n}\n\n.task-dot.IN-PROCESS {\n background: var(--m-accent);\n}\n\n.task-dot.COMPLETED {\n background: var(--m-ok);\n}\n\n.task-dot.CANCELLED {\n background: var(--m-danger);\n opacity: 0.7;\n}\n\n.task-column-body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 60px;\n}\n\n.task-column-empty {\n padding: 18px 8px;\n text-align: center;\n color: var(--m-text-3);\n font-size: 11.5px;\n border: 1px dashed var(--m-line-soft);\n border-radius: 9px;\n}\n\n.task-card {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 11px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg);\n cursor: grab;\n transition:\n border-color 120ms ease,\n box-shadow 120ms ease;\n}\n\n.task-card:hover {\n border-color: var(--m-line);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);\n}\n\n.task-card.dragging {\n opacity: 0.5;\n}\n\n.task-card-top {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n}\n\n.task-card-title {\n flex: 1;\n min-width: 0;\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--m-text);\n overflow-wrap: anywhere;\n}\n\n.task-priority {\n width: 3px;\n height: 14px;\n margin-top: 2px;\n border-radius: 999px;\n background: transparent;\n flex: none;\n}\n\n.task-priority.p5 {\n background: #d9a441;\n}\n\n.task-priority.p9 {\n background: var(--m-danger);\n}\n\n.task-card-delete {\n width: 18px;\n height: 18px;\n flex: none;\n border-radius: 5px;\n color: var(--m-text-3);\n opacity: 0;\n}\n\n.task-card:hover .task-card-delete {\n opacity: 1;\n}\n\n.task-card-notes {\n margin: 0;\n font-size: 11.5px;\n line-height: 1.55;\n color: var(--m-text-3);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.task-card-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 10.5px;\n color: var(--m-text-3);\n}\n\n.task-due,\n.task-owner,\n.task-project,\n.task-priority-label {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n padding: 1px 6px;\n border-radius: 5px;\n background: var(--m-bg-2);\n}\n\n.task-due.overdue {\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.12);\n}\n\n.task-project {\n color: var(--m-accent);\n background: var(--m-accent-soft);\n}\n\n.task-source {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 22px;\n padding: 0 6px;\n border-radius: 5px;\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-source:hover {\n background: var(--m-hover);\n color: var(--m-text-2);\n}\n\n@media (max-width: 1180px) {\n .tasks-board {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n@media (max-width: 720px) {\n .tasks-board {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n/* ---------- AI search ---------- */\n\n.mailharbor-ai-search {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 12px 8px;\n padding: 7px 10px;\n border-radius: 8px;\n background: var(--m-accent-soft);\n color: var(--m-text-2);\n font-size: 12px;\n}\n\n.mailharbor-ai-search.searching {\n background: var(--m-bg-2);\n color: var(--m-text-3);\n}\n\n/* ---------- task editor ---------- */\n\n.mailharbor-tasks .mailharbor-modal {\n max-width: 760px;\n}\n\n.task-editor {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 230px;\n gap: 18px;\n}\n\n.task-editor-main {\n display: flex;\n flex-direction: column;\n gap: 14px;\n min-width: 0;\n}\n\n.task-editor-title input {\n width: 100%;\n height: 38px;\n font-size: 15px;\n font-weight: 500;\n}\n\n.task-editor-section {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.task-editor-section label,\n.task-editor-field label {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.task-editor-section textarea {\n width: 100%;\n resize: vertical;\n}\n\n.task-editor-side {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n align-self: start;\n}\n\n.task-editor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.task-editor-field input,\n.task-editor-field select {\n width: 100%;\n}\n\n.task-priority-picker {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n padding: 3px;\n border: 1px solid var(--m-line);\n border-radius: 8px;\n background: var(--m-bg);\n}\n\n.task-priority-picker button {\n height: 26px;\n border-radius: 6px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.task-priority-picker button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.task-editor-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n}\n";
|
|
39
39
|
|
|
40
40
|
// src/client/icon.tsx
|
|
41
41
|
var import_react = require("react");
|
|
@@ -126,6 +126,10 @@ var api = {
|
|
|
126
126
|
check: (id) => request(`accounts/${encodeURIComponent(id)}/check`, { method: "POST" }),
|
|
127
127
|
aiStatus: (accountId) => request(`ai/status${query({ accountId: accountId ?? null })}`),
|
|
128
128
|
aiModels: () => request("ai/models"),
|
|
129
|
+
aiSearch: (payload) => request("ai/search", {
|
|
130
|
+
method: "POST",
|
|
131
|
+
body: JSON.stringify(payload)
|
|
132
|
+
}),
|
|
129
133
|
aiThread: (accountId, key) => request(`ai/thread${query({ accountId: accountId ?? null, key })}`),
|
|
130
134
|
aiAddCard: (payload) => request("ai/cards", {
|
|
131
135
|
method: "POST",
|
|
@@ -182,6 +186,7 @@ var api = {
|
|
|
182
186
|
}),
|
|
183
187
|
syncTasks: (accountId) => request("tasks/sync", { method: "POST", body: JSON.stringify({ accountId }) }),
|
|
184
188
|
projects: () => request("projects"),
|
|
189
|
+
pickDirectory: () => request("projects/pick-directory", { method: "POST" }),
|
|
185
190
|
projectFor: (accountId, key) => request(`projects/for${query({ accountId: accountId ?? null, key })}`),
|
|
186
191
|
createProject: (payload) => request("projects", {
|
|
187
192
|
method: "POST",
|
|
@@ -1102,10 +1107,28 @@ ${card.output}`
|
|
|
1102
1107
|
"div",
|
|
1103
1108
|
{ className: "mailharbor-field" },
|
|
1104
1109
|
(0, import_react5.createElement)("label", null, "\u9879\u76EE\u76EE\u5F55\uFF08\u9ED8\u8BA4\u53EF\u5728\u8BBE\u7F6E\u91CC\u6539\uFF09"),
|
|
1105
|
-
(0, import_react5.createElement)(
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1110
|
+
(0, import_react5.createElement)(
|
|
1111
|
+
"div",
|
|
1112
|
+
{ style: { display: "flex", gap: "6px" } },
|
|
1113
|
+
(0, import_react5.createElement)("input", {
|
|
1114
|
+
value: projectRoot,
|
|
1115
|
+
onChange: (event) => setProjectRoot(event.target.value)
|
|
1116
|
+
}),
|
|
1117
|
+
(0, import_react5.createElement)(
|
|
1118
|
+
"button",
|
|
1119
|
+
{
|
|
1120
|
+
className: "ghost",
|
|
1121
|
+
title: "\u4ECE\u6587\u4EF6\u7BA1\u7406\u5668\u9009\u62E9",
|
|
1122
|
+
onClick: () => {
|
|
1123
|
+
void api.pickDirectory().then((result) => {
|
|
1124
|
+
if (result.path !== null) setProjectRoot(result.path);
|
|
1125
|
+
}).catch((caught) => setProjectWarnings([errorText(caught)]));
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
(0, import_react5.createElement)(Icon, { name: "folder", size: 13 }),
|
|
1129
|
+
"\u6D4F\u89C8\u2026"
|
|
1130
|
+
)
|
|
1131
|
+
)
|
|
1109
1132
|
),
|
|
1110
1133
|
(0, import_react5.createElement)(
|
|
1111
1134
|
"p",
|
|
@@ -2102,6 +2125,27 @@ var FOLDER_LABELS = {
|
|
|
2102
2125
|
function folderName(name) {
|
|
2103
2126
|
return FOLDER_LABELS[name] ?? name;
|
|
2104
2127
|
}
|
|
2128
|
+
function isSentFolder(mailbox) {
|
|
2129
|
+
return /(^|[/.])(sent|sent items|sent mail|已发送|发件箱)/i.test(mailbox);
|
|
2130
|
+
}
|
|
2131
|
+
function normalizeSubject(subject) {
|
|
2132
|
+
return subject.replace(/^(\s*(re|fw|fwd|回复|转发)\s*[::]\s*)+/gi, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
2133
|
+
}
|
|
2134
|
+
function groupMessages(items) {
|
|
2135
|
+
const map = /* @__PURE__ */ new Map();
|
|
2136
|
+
const order = [];
|
|
2137
|
+
for (const item of items) {
|
|
2138
|
+
const key = normalizeSubject(item.subject) === "" ? `__${item.accountId}:${item.id}` : normalizeSubject(item.subject);
|
|
2139
|
+
const existing = map.get(key);
|
|
2140
|
+
if (existing === void 0) {
|
|
2141
|
+
map.set(key, [item]);
|
|
2142
|
+
order.push(key);
|
|
2143
|
+
} else {
|
|
2144
|
+
existing.push(item);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
return order.map((key) => ({ key, items: map.get(key) ?? [] }));
|
|
2148
|
+
}
|
|
2105
2149
|
function errorText5(error) {
|
|
2106
2150
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
2107
2151
|
}
|
|
@@ -2134,11 +2178,21 @@ function MailPanel() {
|
|
|
2134
2178
|
const [narrow, setNarrow] = (0, import_react9.useState)(() => matchesNarrow());
|
|
2135
2179
|
const [railOpen, setRailOpen] = (0, import_react9.useState)(false);
|
|
2136
2180
|
const [selectMode, setSelectMode] = (0, import_react9.useState)(false);
|
|
2181
|
+
const [sideOpen, setSideOpen] = (0, import_react9.useState)(() => {
|
|
2182
|
+
try {
|
|
2183
|
+
return window.localStorage.getItem("mailharbor.side") !== "0";
|
|
2184
|
+
} catch {
|
|
2185
|
+
return true;
|
|
2186
|
+
}
|
|
2187
|
+
});
|
|
2188
|
+
const [expandedGroups, setExpandedGroups] = (0, import_react9.useState)([]);
|
|
2137
2189
|
const [selectedKeys, setSelectedKeys] = (0, import_react9.useState)([]);
|
|
2138
2190
|
const [projectOptions, setProjectOptions] = (0, import_react9.useState)([]);
|
|
2139
2191
|
const [targetProject, setTargetProject] = (0, import_react9.useState)("");
|
|
2140
2192
|
const [extraction, setExtraction] = (0, import_react9.useState)(null);
|
|
2141
2193
|
const [extractBusy, setExtractBusy] = (0, import_react9.useState)(false);
|
|
2194
|
+
const [aiInfo, setAiInfo] = (0, import_react9.useState)(null);
|
|
2195
|
+
const [aiSearching, setAiSearching] = (0, import_react9.useState)(false);
|
|
2142
2196
|
const [aiRequest, setAiRequest] = (0, import_react9.useState)(null);
|
|
2143
2197
|
const [tick, setTick] = (0, import_react9.useState)(0);
|
|
2144
2198
|
const accounts = state?.accounts ?? [];
|
|
@@ -2183,6 +2237,44 @@ function MailPanel() {
|
|
|
2183
2237
|
setError(errorText5(caught));
|
|
2184
2238
|
}
|
|
2185
2239
|
};
|
|
2240
|
+
const runAiSearch = async () => {
|
|
2241
|
+
const text = query2.trim();
|
|
2242
|
+
if (text === "" || accountScope === null) return;
|
|
2243
|
+
setAiSearching(true);
|
|
2244
|
+
setError("");
|
|
2245
|
+
try {
|
|
2246
|
+
const result = await Promise.race([
|
|
2247
|
+
api.aiSearch({ accountId: accountScope, source, mailbox, query: text }),
|
|
2248
|
+
new Promise((_resolve, reject) => {
|
|
2249
|
+
window.setTimeout(() => reject(new Error("AI \u641C\u7D22\u8D85\u65F6\uFF0C\u5DF2\u6539\u7528\u666E\u901A\u641C\u7D22")), 45e3);
|
|
2250
|
+
})
|
|
2251
|
+
]);
|
|
2252
|
+
setPage({ items: result.items, total: result.total, offset: 0, limit: result.limit });
|
|
2253
|
+
setDrafts([]);
|
|
2254
|
+
setAiInfo(result.ai);
|
|
2255
|
+
setSearch("");
|
|
2256
|
+
} catch (caught) {
|
|
2257
|
+
setError(errorText5(caught));
|
|
2258
|
+
setAiInfo(null);
|
|
2259
|
+
setSearch(text);
|
|
2260
|
+
} finally {
|
|
2261
|
+
setAiSearching(false);
|
|
2262
|
+
}
|
|
2263
|
+
};
|
|
2264
|
+
const toggleSide = () => {
|
|
2265
|
+
setSideOpen((open) => {
|
|
2266
|
+
try {
|
|
2267
|
+
window.localStorage.setItem("mailharbor.side", open ? "0" : "1");
|
|
2268
|
+
} catch {
|
|
2269
|
+
}
|
|
2270
|
+
return !open;
|
|
2271
|
+
});
|
|
2272
|
+
};
|
|
2273
|
+
const toggleGroup = (key) => {
|
|
2274
|
+
setExpandedGroups(
|
|
2275
|
+
(current) => current.includes(key) ? current.filter((entry) => entry !== key) : [...current, key]
|
|
2276
|
+
);
|
|
2277
|
+
};
|
|
2186
2278
|
const toggleSelect = (item) => {
|
|
2187
2279
|
const key = `${item.accountId}:${item.id}`;
|
|
2188
2280
|
setSelectedKeys(
|
|
@@ -2313,11 +2405,15 @@ function MailPanel() {
|
|
|
2313
2405
|
(0, import_react9.useEffect)(() => {
|
|
2314
2406
|
setMailbox(source === "local" ? "all" : "INBOX");
|
|
2315
2407
|
setSelected(null);
|
|
2408
|
+
setAiInfo(null);
|
|
2409
|
+
setSearch("");
|
|
2316
2410
|
setLimit(state?.settings.render.listLimit ?? 40);
|
|
2317
2411
|
}, [source]);
|
|
2318
2412
|
(0, import_react9.useEffect)(() => {
|
|
2319
2413
|
if (accountScope === "all" && source === "server") setSource("local");
|
|
2320
2414
|
setSelected(null);
|
|
2415
|
+
setAiInfo(null);
|
|
2416
|
+
setSearch("");
|
|
2321
2417
|
}, [accountScope, source]);
|
|
2322
2418
|
(0, import_react9.useEffect)(() => {
|
|
2323
2419
|
if (accountScope === null) return;
|
|
@@ -2334,9 +2430,9 @@ function MailPanel() {
|
|
|
2334
2430
|
return [];
|
|
2335
2431
|
}
|
|
2336
2432
|
})
|
|
2337
|
-
).then((
|
|
2433
|
+
).then((groups2) => {
|
|
2338
2434
|
if (cancelled) return;
|
|
2339
|
-
setDrafts(
|
|
2435
|
+
setDrafts(groups2.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)));
|
|
2340
2436
|
setPage(EMPTY_PAGE);
|
|
2341
2437
|
}).finally(() => {
|
|
2342
2438
|
if (!cancelled) setLoading(false);
|
|
@@ -2589,6 +2685,65 @@ function MailPanel() {
|
|
|
2589
2685
|
const loggedIn = isAll ? accounts.some((item) => item.id === state.activeAccountId) : state.loggedIn;
|
|
2590
2686
|
const needsLogin = source === "server" && !isAll && state.loggedIn === false;
|
|
2591
2687
|
const items = page.items;
|
|
2688
|
+
const isSent = isSentFolder(mailbox);
|
|
2689
|
+
const groups = groupMessages(items);
|
|
2690
|
+
const renderMessage = (item, meta = {}) => {
|
|
2691
|
+
const sent = isSent && item.to.length > 0;
|
|
2692
|
+
const first = sent ? item.to[0] : item.from;
|
|
2693
|
+
const who = first === void 0 || first === null ? "" : first.name !== "" ? first.name : first.address;
|
|
2694
|
+
const extra = sent && item.to.length > 1 ? ` +${String(item.to.length - 1)}` : "";
|
|
2695
|
+
const isSelected = selected?.id === item.id && selected?.accountId === item.accountId;
|
|
2696
|
+
return (0, import_react9.createElement)(
|
|
2697
|
+
"div",
|
|
2698
|
+
{
|
|
2699
|
+
key: `${item.accountId}:${item.id}`,
|
|
2700
|
+
className: `mailharbor-row${item.unread ? " unread" : ""}${isSelected ? " selected" : ""}${selectMode ? " selectable" : ""}${meta.compact === true ? " compact" : ""}`,
|
|
2701
|
+
role: "button",
|
|
2702
|
+
tabIndex: 0,
|
|
2703
|
+
onClick: () => {
|
|
2704
|
+
if (selectMode) toggleSelect(item);
|
|
2705
|
+
else void openMessage(item);
|
|
2706
|
+
},
|
|
2707
|
+
onKeyDown: (event) => {
|
|
2708
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
2709
|
+
event.preventDefault();
|
|
2710
|
+
if (selectMode) toggleSelect(item);
|
|
2711
|
+
else void openMessage(item);
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
},
|
|
2715
|
+
(0, import_react9.createElement)(
|
|
2716
|
+
"span",
|
|
2717
|
+
{ className: "line-1" },
|
|
2718
|
+
selectMode ? (0, import_react9.createElement)("input", {
|
|
2719
|
+
type: "checkbox",
|
|
2720
|
+
checked: selectedKeys.includes(`${item.accountId}:${item.id}`),
|
|
2721
|
+
"aria-label": "\u9009\u62E9\u90AE\u4EF6",
|
|
2722
|
+
onClick: (event) => event.stopPropagation(),
|
|
2723
|
+
onChange: () => toggleSelect(item)
|
|
2724
|
+
}) : item.unread ? (0, import_react9.createElement)("span", { className: "dot" }) : null,
|
|
2725
|
+
sent ? (0, import_react9.createElement)("span", { className: "to-label" }, "\u6536\u4EF6\u4EBA ") : null,
|
|
2726
|
+
(0, import_react9.createElement)("span", { className: "from" }, `${who}${extra}`),
|
|
2727
|
+
item.hasAttachments ? (0, import_react9.createElement)(Icon, { name: "paperclip", size: 13 }) : null,
|
|
2728
|
+
source === "local" && item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
|
|
2729
|
+
meta.groupCount !== void 0 && meta.groupCount > 1 ? (0, import_react9.createElement)(
|
|
2730
|
+
"button",
|
|
2731
|
+
{
|
|
2732
|
+
className: "thread-count",
|
|
2733
|
+
title: meta.expanded === true ? "\u6536\u8D77\u4F1A\u8BDD" : `\u5C55\u5F00\u4F1A\u8BDD\uFF08${String(meta.groupCount)} \u5C01\uFF09`,
|
|
2734
|
+
onClick: (event) => {
|
|
2735
|
+
event.stopPropagation();
|
|
2736
|
+
toggleGroup(meta.groupKey ?? "");
|
|
2737
|
+
}
|
|
2738
|
+
},
|
|
2739
|
+
meta.expanded === true ? "\u2212" : String(meta.groupCount)
|
|
2740
|
+
) : null,
|
|
2741
|
+
(0, import_react9.createElement)("span", { className: "date" }, shortDate(item.date))
|
|
2742
|
+
),
|
|
2743
|
+
meta.compact === true ? null : (0, import_react9.createElement)("span", { className: "line-2" }, (0, import_react9.createElement)("span", { className: "subject" }, item.subject)),
|
|
2744
|
+
item.snippet === "" ? null : (0, import_react9.createElement)("span", { className: "snippet" }, item.snippet)
|
|
2745
|
+
);
|
|
2746
|
+
};
|
|
2592
2747
|
return (0, import_react9.createElement)(
|
|
2593
2748
|
"div",
|
|
2594
2749
|
{ className: narrow ? "mailharbor mailharbor-narrow" : "mailharbor" },
|
|
@@ -2596,20 +2751,21 @@ function MailPanel() {
|
|
|
2596
2751
|
"div",
|
|
2597
2752
|
{ className: "mailharbor-topbar" },
|
|
2598
2753
|
(0, import_react9.createElement)(
|
|
2599
|
-
"
|
|
2600
|
-
{
|
|
2601
|
-
|
|
2754
|
+
"button",
|
|
2755
|
+
{
|
|
2756
|
+
className: "mailharbor-account",
|
|
2757
|
+
onClick: toggleSide,
|
|
2758
|
+
title: sideOpen ? "\u6536\u8D77\u8D26\u53F7\u4E0E\u6587\u4EF6\u5939" : "\u5C55\u5F00\u8D26\u53F7\u4E0E\u6587\u4EF6\u5939"
|
|
2759
|
+
},
|
|
2760
|
+
(0, import_react9.createElement)(Avatar, { email: currentAccount?.email ?? "?", size: 26 }),
|
|
2602
2761
|
(0, import_react9.createElement)(
|
|
2603
2762
|
"div",
|
|
2604
2763
|
{ style: { display: "flex", flexDirection: "column", minWidth: 0 } },
|
|
2605
|
-
(0, import_react9.createElement)("span", { className: "name" },
|
|
2606
|
-
(0, import_react9.createElement)(
|
|
2607
|
-
"span",
|
|
2608
|
-
{ className: "small muted" },
|
|
2609
|
-
isAll ? `${String(accounts.length)} \u4E2A\u8D26\u53F7` : currentAccount?.email ?? ""
|
|
2610
|
-
)
|
|
2764
|
+
(0, import_react9.createElement)("span", { className: "name" }, currentAccount?.label ?? ""),
|
|
2765
|
+
(0, import_react9.createElement)("span", { className: "small muted" }, currentAccount?.email ?? "")
|
|
2611
2766
|
),
|
|
2612
|
-
|
|
2767
|
+
state.loggedIn === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null,
|
|
2768
|
+
(0, import_react9.createElement)(Icon, { name: sideOpen ? "chevronLeft" : "chevronRight", size: 14 })
|
|
2613
2769
|
),
|
|
2614
2770
|
(0, import_react9.createElement)(
|
|
2615
2771
|
"div",
|
|
@@ -2637,16 +2793,6 @@ function MailPanel() {
|
|
|
2637
2793
|
)
|
|
2638
2794
|
),
|
|
2639
2795
|
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2640
|
-
(0, import_react9.createElement)(
|
|
2641
|
-
"button",
|
|
2642
|
-
{
|
|
2643
|
-
className: "ghost icon",
|
|
2644
|
-
title: "\u6DFB\u52A0\u8D26\u53F7",
|
|
2645
|
-
"aria-label": "\u6DFB\u52A0\u8D26\u53F7",
|
|
2646
|
-
onClick: () => setShowAdd(true)
|
|
2647
|
-
},
|
|
2648
|
-
(0, import_react9.createElement)(Icon, { name: "plus" })
|
|
2649
|
-
),
|
|
2650
2796
|
(0, import_react9.createElement)(
|
|
2651
2797
|
"button",
|
|
2652
2798
|
{ className: "ghost icon", title: "\u5237\u65B0\u5217\u8868", "aria-label": "\u5237\u65B0\u5217\u8868", onClick: refresh },
|
|
@@ -2688,24 +2834,9 @@ function MailPanel() {
|
|
|
2688
2834
|
) : (0, import_react9.createElement)(
|
|
2689
2835
|
"div",
|
|
2690
2836
|
{ className: "mailharbor-body", "data-mode": narrow && selected !== null ? "reader" : "list" },
|
|
2691
|
-
(0, import_react9.createElement)(
|
|
2837
|
+
sideOpen ? (0, import_react9.createElement)(
|
|
2692
2838
|
"aside",
|
|
2693
2839
|
{ className: "mailharbor-side" },
|
|
2694
|
-
(0, import_react9.createElement)(
|
|
2695
|
-
"button",
|
|
2696
|
-
{
|
|
2697
|
-
className: `mailharbor-side-item${isAll ? " active" : ""}`,
|
|
2698
|
-
onClick: () => {
|
|
2699
|
-
setAccountScope("all");
|
|
2700
|
-
setSource("local");
|
|
2701
|
-
setMailbox("all");
|
|
2702
|
-
setSelected(null);
|
|
2703
|
-
}
|
|
2704
|
-
},
|
|
2705
|
-
(0, import_react9.createElement)(Icon, { name: "mail", size: 15 }),
|
|
2706
|
-
(0, import_react9.createElement)("span", { className: "name" }, "\u7EDF\u4E00\u6536\u4EF6\u7BB1"),
|
|
2707
|
-
(0, import_react9.createElement)("span", { className: "count" }, String(accounts.length))
|
|
2708
|
-
),
|
|
2709
2840
|
(0, import_react9.createElement)("div", { className: "mailharbor-side-section" }, "\u8D26\u53F7"),
|
|
2710
2841
|
accounts.map(
|
|
2711
2842
|
(item) => (0, import_react9.createElement)(
|
|
@@ -2729,12 +2860,12 @@ function MailPanel() {
|
|
|
2729
2860
|
item.enabled === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u505C\u7528") : null
|
|
2730
2861
|
)
|
|
2731
2862
|
),
|
|
2732
|
-
|
|
2863
|
+
(0, import_react9.createElement)(
|
|
2733
2864
|
"div",
|
|
2734
2865
|
{ className: "mailharbor-side-section" },
|
|
2735
2866
|
source === "server" ? "\u670D\u52A1\u5668\u6587\u4EF6\u5939" : "\u672C\u5730\u6587\u4EF6\u5939"
|
|
2736
2867
|
),
|
|
2737
|
-
|
|
2868
|
+
folders.map(
|
|
2738
2869
|
(folder) => (0, import_react9.createElement)(
|
|
2739
2870
|
"button",
|
|
2740
2871
|
{
|
|
@@ -2750,7 +2881,7 @@ function MailPanel() {
|
|
|
2750
2881
|
folder.count > 0 ? (0, import_react9.createElement)("span", { className: "count" }, String(folder.count)) : null
|
|
2751
2882
|
)
|
|
2752
2883
|
)
|
|
2753
|
-
),
|
|
2884
|
+
) : null,
|
|
2754
2885
|
(0, import_react9.createElement)(
|
|
2755
2886
|
"div",
|
|
2756
2887
|
{ className: "mailharbor-list" },
|
|
@@ -2759,15 +2890,19 @@ function MailPanel() {
|
|
|
2759
2890
|
{ className: "mailharbor-filters" },
|
|
2760
2891
|
(0, import_react9.createElement)("input", {
|
|
2761
2892
|
type: "search",
|
|
2762
|
-
placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : "\u641C\u7D22\u90AE\u4EF6",
|
|
2893
|
+
placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : aiSearching ? "AI \u6B63\u5728\u7406\u89E3\u4F60\u7684\u641C\u7D22\u2026" : "\u641C\u7D22\u90AE\u4EF6\uFF0C\u6216\u63CF\u8FF0\u4F60\u8981\u627E\u4EC0\u4E48\uFF08AI\uFF09",
|
|
2763
2894
|
"aria-label": "\u641C\u7D22\u90AE\u4EF6",
|
|
2764
2895
|
value: query2,
|
|
2765
2896
|
onChange: (event) => setQuery(event.target.value),
|
|
2766
2897
|
onKeyDown: (event) => {
|
|
2767
|
-
if (event.key === "Enter")
|
|
2898
|
+
if (event.key === "Enter") {
|
|
2899
|
+
if (showingDrafts) setSearch(query2);
|
|
2900
|
+
else void runAiSearch();
|
|
2901
|
+
}
|
|
2768
2902
|
if (event.key === "Escape") {
|
|
2769
2903
|
setQuery("");
|
|
2770
2904
|
setSearch("");
|
|
2905
|
+
setAiInfo(null);
|
|
2771
2906
|
}
|
|
2772
2907
|
}
|
|
2773
2908
|
}),
|
|
@@ -2796,11 +2931,35 @@ function MailPanel() {
|
|
|
2796
2931
|
onClick: () => {
|
|
2797
2932
|
setQuery("");
|
|
2798
2933
|
setSearch("");
|
|
2934
|
+
setAiInfo(null);
|
|
2799
2935
|
}
|
|
2800
2936
|
},
|
|
2801
2937
|
(0, import_react9.createElement)(Icon, { name: "close", size: 14 })
|
|
2802
2938
|
) : null
|
|
2803
2939
|
),
|
|
2940
|
+
aiInfo === null ? null : (0, import_react9.createElement)(
|
|
2941
|
+
"div",
|
|
2942
|
+
{ className: "mailharbor-ai-search" },
|
|
2943
|
+
(0, import_react9.createElement)(Icon, { name: "sparkles", size: 13 }),
|
|
2944
|
+
(0, import_react9.createElement)(
|
|
2945
|
+
"span",
|
|
2946
|
+
null,
|
|
2947
|
+
aiInfo.explanation !== "" ? aiInfo.explanation : `\u5173\u952E\u8BCD\uFF1A${aiInfo.keywords.join("\u3001")}`
|
|
2948
|
+
),
|
|
2949
|
+
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2950
|
+
(0, import_react9.createElement)(
|
|
2951
|
+
"button",
|
|
2952
|
+
{
|
|
2953
|
+
className: "subtle",
|
|
2954
|
+
onClick: () => {
|
|
2955
|
+
setAiInfo(null);
|
|
2956
|
+
setTick((value) => value + 1);
|
|
2957
|
+
}
|
|
2958
|
+
},
|
|
2959
|
+
"\u6E05\u9664"
|
|
2960
|
+
)
|
|
2961
|
+
),
|
|
2962
|
+
aiSearching ? (0, import_react9.createElement)("div", { className: "mailharbor-ai-search searching" }, "AI \u6B63\u5728\u7406\u89E3\u5E76\u641C\u7D22\u2026") : null,
|
|
2804
2963
|
selectMode ? (0, import_react9.createElement)(
|
|
2805
2964
|
"div",
|
|
2806
2965
|
{ className: "mailharbor-selectbar" },
|
|
@@ -2911,76 +3070,39 @@ function MailPanel() {
|
|
|
2911
3070
|
) : items.length === 0 ? (0, import_react9.createElement)(
|
|
2912
3071
|
"div",
|
|
2913
3072
|
{ className: "mailharbor-empty" },
|
|
2914
|
-
(0, import_react9.createElement)(Icon, { name: "mail", size: 28 }),
|
|
3073
|
+
(0, import_react9.createElement)(Icon, { name: aiInfo === null ? "mail" : "sparkles", size: 28 }),
|
|
2915
3074
|
(0, import_react9.createElement)(
|
|
2916
3075
|
"div",
|
|
2917
3076
|
{ className: "title" },
|
|
2918
|
-
source === "local" ? "\u672C\u5730\u5907\u4EFD\u91CC\u8FD8\u6CA1\u6709\u90AE\u4EF6" : "\u670D\u52A1\u5668\u4E0A\u6CA1\u6709\u90AE\u4EF6"
|
|
3077
|
+
aiInfo === null ? source === "local" ? "\u672C\u5730\u5907\u4EFD\u91CC\u8FD8\u6CA1\u6709\u90AE\u4EF6" : "\u670D\u52A1\u5668\u4E0A\u6CA1\u6709\u90AE\u4EF6" : "AI \u641C\u7D22\u6CA1\u6709\u627E\u5230\u5339\u914D\u90AE\u4EF6"
|
|
2919
3078
|
),
|
|
2920
3079
|
(0, import_react9.createElement)(
|
|
2921
3080
|
"div",
|
|
2922
3081
|
{ className: "small" },
|
|
2923
|
-
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"
|
|
3082
|
+
aiInfo === null ? 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" : "\u6362\u4E2A\u8BF4\u6CD5\uFF0C\u6216\u76F4\u63A5\u8F93\u5165\u5173\u952E\u8BCD\u540E\u6309\u56DE\u8F66\u3002"
|
|
2924
3083
|
)
|
|
2925
|
-
) : items.map(
|
|
2926
|
-
|
|
3084
|
+
) : selectMode ? items.map((item) => renderMessage(item)) : groups.map((group) => {
|
|
3085
|
+
const latest = group.items[0];
|
|
3086
|
+
if (latest === void 0) return null;
|
|
3087
|
+
const expanded = expandedGroups.includes(group.key);
|
|
3088
|
+
return (0, import_react9.createElement)(
|
|
2927
3089
|
"div",
|
|
2928
|
-
{
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
onKeyDown: (event) => {
|
|
2938
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
2939
|
-
event.preventDefault();
|
|
2940
|
-
if (selectMode) toggleSelect(item);
|
|
2941
|
-
else void openMessage(item);
|
|
2942
|
-
}
|
|
2943
|
-
}
|
|
2944
|
-
},
|
|
2945
|
-
(0, import_react9.createElement)(
|
|
2946
|
-
"span",
|
|
2947
|
-
{ className: "line-1" },
|
|
2948
|
-
selectMode ? (0, import_react9.createElement)("input", {
|
|
2949
|
-
type: "checkbox",
|
|
2950
|
-
checked: selectedKeys.includes(`${item.accountId}:${item.id}`),
|
|
2951
|
-
"aria-label": "\u9009\u62E9\u90AE\u4EF6",
|
|
2952
|
-
onClick: (event) => event.stopPropagation(),
|
|
2953
|
-
onChange: () => toggleSelect(item)
|
|
2954
|
-
}) : item.unread ? (0, import_react9.createElement)("span", { className: "dot" }) : null,
|
|
2955
|
-
(0, import_react9.createElement)(
|
|
2956
|
-
"span",
|
|
2957
|
-
{ className: "from" },
|
|
2958
|
-
item.from.name !== "" ? item.from.name : item.from.address
|
|
2959
|
-
),
|
|
2960
|
-
item.hasAttachments ? (0, import_react9.createElement)(Icon, { name: "paperclip", size: 13 }) : null,
|
|
2961
|
-
isAll ? (0, import_react9.createElement)(
|
|
2962
|
-
"span",
|
|
2963
|
-
{ className: "badge" },
|
|
2964
|
-
accounts.find((entry) => entry.id === item.accountId)?.label ?? ""
|
|
2965
|
-
) : null,
|
|
2966
|
-
source === "local" && item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
|
|
2967
|
-
(0, import_react9.createElement)("span", { className: "date" }, shortDate(item.date))
|
|
2968
|
-
),
|
|
2969
|
-
(0, import_react9.createElement)(
|
|
2970
|
-
"span",
|
|
2971
|
-
{ className: "line-2" },
|
|
2972
|
-
(0, import_react9.createElement)("span", { className: "subject" }, item.subject)
|
|
2973
|
-
),
|
|
2974
|
-
item.snippet === "" ? null : (0, import_react9.createElement)("span", { className: "snippet" }, item.snippet)
|
|
2975
|
-
)
|
|
2976
|
-
)
|
|
3090
|
+
{ className: "mailharbor-thread", key: group.key },
|
|
3091
|
+
renderMessage(latest, {
|
|
3092
|
+
groupCount: group.items.length,
|
|
3093
|
+
groupKey: group.key,
|
|
3094
|
+
expanded
|
|
3095
|
+
}),
|
|
3096
|
+
expanded ? group.items.slice(1).map((item) => renderMessage(item, { compact: true })) : null
|
|
3097
|
+
);
|
|
3098
|
+
})
|
|
2977
3099
|
),
|
|
2978
3100
|
!showingDrafts && items.length > 0 ? (0, import_react9.createElement)(
|
|
2979
3101
|
"div",
|
|
2980
3102
|
{ className: "mailharbor-list-foot" },
|
|
2981
3103
|
(0, import_react9.createElement)("span", null, `${String(items.length)} / ${String(page.total)} \u5C01`),
|
|
2982
3104
|
(0, import_react9.createElement)("span", { className: "spacer" }),
|
|
2983
|
-
page.total > items.length ? (0, import_react9.createElement)(
|
|
3105
|
+
page.total > items.length && aiInfo === null ? (0, import_react9.createElement)(
|
|
2984
3106
|
"button",
|
|
2985
3107
|
{ className: "subtle", onClick: () => setLimit((current) => current + 40) },
|
|
2986
3108
|
"\u52A0\u8F7D\u66F4\u591A"
|
|
@@ -3365,13 +3487,28 @@ function MailSettings() {
|
|
|
3365
3487
|
})
|
|
3366
3488
|
),
|
|
3367
3489
|
(0, import_react10.createElement)(
|
|
3368
|
-
"
|
|
3369
|
-
{
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3490
|
+
"div",
|
|
3491
|
+
{ style: { display: "flex", gap: "6px", alignSelf: "end" } },
|
|
3492
|
+
(0, import_react10.createElement)(
|
|
3493
|
+
"button",
|
|
3494
|
+
{
|
|
3495
|
+
className: "ghost small",
|
|
3496
|
+
onClick: () => {
|
|
3497
|
+
void api.pickDirectory().then((result) => {
|
|
3498
|
+
if (result.path !== null) setProjectRoot(result.path);
|
|
3499
|
+
}).catch((caught) => setError(errorText6(caught)));
|
|
3500
|
+
}
|
|
3501
|
+
},
|
|
3502
|
+
"\u6D4F\u89C8\u2026"
|
|
3503
|
+
),
|
|
3504
|
+
(0, import_react10.createElement)(
|
|
3505
|
+
"button",
|
|
3506
|
+
{
|
|
3507
|
+
className: "ghost small",
|
|
3508
|
+
onClick: () => void saveGeneral({ projects: { root: projectRoot } })
|
|
3509
|
+
},
|
|
3510
|
+
"\u4FDD\u5B58\u76EE\u5F55"
|
|
3511
|
+
)
|
|
3375
3512
|
)
|
|
3376
3513
|
)
|
|
3377
3514
|
),
|
|
@@ -3789,7 +3926,8 @@ var FILTERS = [
|
|
|
3789
3926
|
var COLUMNS = [
|
|
3790
3927
|
{ status: "NEEDS-ACTION", label: "\u5F85\u5904\u7406", hint: "\u8FD8\u6CA1\u5F00\u59CB" },
|
|
3791
3928
|
{ status: "IN-PROCESS", label: "\u8FDB\u884C\u4E2D", hint: "\u6B63\u5728\u505A" },
|
|
3792
|
-
{ status: "COMPLETED", label: "\u5DF2\u5B8C\u6210", hint: "\u6536\u5C3E" }
|
|
3929
|
+
{ status: "COMPLETED", label: "\u5DF2\u5B8C\u6210", hint: "\u6536\u5C3E" },
|
|
3930
|
+
{ status: "CANCELLED", label: "\u7EC8\u6B62", hint: "\u4E0D\u505A\u4E86" }
|
|
3793
3931
|
];
|
|
3794
3932
|
var PRIORITY_LABEL = { 0: "\u65E0", 5: "\u4E2D", 9: "\u9AD8" };
|
|
3795
3933
|
function errorText7(error) {
|
|
@@ -3833,7 +3971,9 @@ function TasksIcon() {
|
|
|
3833
3971
|
if (accountId === null) return;
|
|
3834
3972
|
const result = await api.tasks(accountId);
|
|
3835
3973
|
if (!cancelled) {
|
|
3836
|
-
setCount(
|
|
3974
|
+
setCount(
|
|
3975
|
+
result.items.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED").length
|
|
3976
|
+
);
|
|
3837
3977
|
}
|
|
3838
3978
|
} catch {
|
|
3839
3979
|
}
|
|
@@ -3893,6 +4033,7 @@ function TasksPanel() {
|
|
|
3893
4033
|
const [tasks, setTasks] = (0, import_react11.useState)([]);
|
|
3894
4034
|
const [projects, setProjects] = (0, import_react11.useState)([]);
|
|
3895
4035
|
const [filter, setFilter] = (0, import_react11.useState)("all");
|
|
4036
|
+
const [projectFilter, setProjectFilter] = (0, import_react11.useState)("");
|
|
3896
4037
|
const [error, setError] = (0, import_react11.useState)("");
|
|
3897
4038
|
const [toast, setToast] = (0, import_react11.useState)("");
|
|
3898
4039
|
const [busy, setBusy] = (0, import_react11.useState)(false);
|
|
@@ -3938,10 +4079,6 @@ function TasksPanel() {
|
|
|
3938
4079
|
},
|
|
3939
4080
|
[filter]
|
|
3940
4081
|
);
|
|
3941
|
-
const open = tasks.filter((task) => task.status !== "COMPLETED");
|
|
3942
|
-
const doneCount = tasks.length - open.length;
|
|
3943
|
-
const progress = tasks.length === 0 ? 0 : Math.round(doneCount / tasks.length * 100);
|
|
3944
|
-
const overdue = open.filter((task) => task.due !== "" && task.due.slice(0, 10) < dateKey(/* @__PURE__ */ new Date())).length;
|
|
3945
4082
|
const switchAccount = async (id) => {
|
|
3946
4083
|
try {
|
|
3947
4084
|
await api.activateAccount(id);
|
|
@@ -4047,47 +4184,6 @@ function TasksPanel() {
|
|
|
4047
4184
|
(0, import_react11.createElement)(
|
|
4048
4185
|
"div",
|
|
4049
4186
|
{ className: "tasks-board-wrap" },
|
|
4050
|
-
(0, import_react11.createElement)(
|
|
4051
|
-
"div",
|
|
4052
|
-
{ className: "tasks-summary" },
|
|
4053
|
-
(0, import_react11.createElement)(
|
|
4054
|
-
"div",
|
|
4055
|
-
{ className: "tasks-summary-main" },
|
|
4056
|
-
(0, import_react11.createElement)(
|
|
4057
|
-
"div",
|
|
4058
|
-
{ className: "tasks-summary-labels" },
|
|
4059
|
-
(0, import_react11.createElement)("strong", null, "\u603B\u4F53\u8FDB\u5EA6"),
|
|
4060
|
-
(0, import_react11.createElement)(
|
|
4061
|
-
"span",
|
|
4062
|
-
{ className: "muted small" },
|
|
4063
|
-
`${String(doneCount)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
|
|
4064
|
-
)
|
|
4065
|
-
),
|
|
4066
|
-
(0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } }))
|
|
4067
|
-
),
|
|
4068
|
-
(0, import_react11.createElement)(
|
|
4069
|
-
"div",
|
|
4070
|
-
{ className: "tasks-stats" },
|
|
4071
|
-
(0, import_react11.createElement)(
|
|
4072
|
-
"span",
|
|
4073
|
-
{ className: "tasks-stat" },
|
|
4074
|
-
(0, import_react11.createElement)("strong", null, String(open.length)),
|
|
4075
|
-
(0, import_react11.createElement)("span", null, "\u5F85\u529E")
|
|
4076
|
-
),
|
|
4077
|
-
(0, import_react11.createElement)(
|
|
4078
|
-
"span",
|
|
4079
|
-
{ className: overdue > 0 ? "tasks-stat danger" : "tasks-stat" },
|
|
4080
|
-
(0, import_react11.createElement)("strong", null, String(overdue)),
|
|
4081
|
-
(0, import_react11.createElement)("span", null, "\u903E\u671F")
|
|
4082
|
-
),
|
|
4083
|
-
(0, import_react11.createElement)(
|
|
4084
|
-
"span",
|
|
4085
|
-
{ className: "tasks-stat" },
|
|
4086
|
-
(0, import_react11.createElement)("strong", null, String(projects.length)),
|
|
4087
|
-
(0, import_react11.createElement)("span", null, "\u9879\u76EE")
|
|
4088
|
-
)
|
|
4089
|
-
)
|
|
4090
|
-
),
|
|
4091
4187
|
(0, import_react11.createElement)(
|
|
4092
4188
|
"div",
|
|
4093
4189
|
{ className: "tasks-toolbar" },
|
|
@@ -4107,13 +4203,27 @@ function TasksPanel() {
|
|
|
4107
4203
|
)
|
|
4108
4204
|
),
|
|
4109
4205
|
(0, import_react11.createElement)("span", { className: "spacer" }),
|
|
4206
|
+
(0, import_react11.createElement)(
|
|
4207
|
+
"select",
|
|
4208
|
+
{
|
|
4209
|
+
value: projectFilter,
|
|
4210
|
+
onChange: (event) => setProjectFilter(event.target.value),
|
|
4211
|
+
"aria-label": "\u6309\u9879\u76EE\u7B5B\u9009"
|
|
4212
|
+
},
|
|
4213
|
+
(0, import_react11.createElement)("option", { value: "" }, "\u5168\u90E8\u9879\u76EE"),
|
|
4214
|
+
projects.map((project) => (0, import_react11.createElement)("option", { key: project.id, value: project.id }, project.name))
|
|
4215
|
+
),
|
|
4110
4216
|
(0, import_react11.createElement)("span", { className: "small muted" }, "\u62D6\u52A8\u5361\u7247\u53EF\u6539\u53D8\u72B6\u6001")
|
|
4111
4217
|
),
|
|
4112
4218
|
(0, import_react11.createElement)(
|
|
4113
4219
|
"div",
|
|
4114
4220
|
{ className: "tasks-board" },
|
|
4115
4221
|
COLUMNS.map((column) => {
|
|
4116
|
-
const cards = tasks.filter((task) => task.status === column.status).filter(matchesFilter)
|
|
4222
|
+
const cards = tasks.filter((task) => task.status === column.status).filter(matchesFilter).filter((task) => projectFilter === "" || task.projectId === projectFilter).sort((left, right) => {
|
|
4223
|
+
const leftDue = left.due === "" ? "9999" : left.due.slice(0, 10);
|
|
4224
|
+
const rightDue = right.due === "" ? "9999" : right.due.slice(0, 10);
|
|
4225
|
+
return leftDue.localeCompare(rightDue);
|
|
4226
|
+
});
|
|
4117
4227
|
return (0, import_react11.createElement)(
|
|
4118
4228
|
"div",
|
|
4119
4229
|
{
|
|
@@ -4394,7 +4504,7 @@ function TaskEditor({
|
|
|
4394
4504
|
}
|
|
4395
4505
|
|
|
4396
4506
|
// src/client/index.tsx
|
|
4397
|
-
var VERSION = "0.
|
|
4507
|
+
var VERSION = "0.7.0";
|
|
4398
4508
|
var inject = ["slots"];
|
|
4399
4509
|
var PANEL_ID = "mailharbor";
|
|
4400
4510
|
var TASKS_PANEL_ID = "mailharbor-tasks";
|