@zaclinley/dsh-mailharbor 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +36 -35
- package/README.zh.md +35 -33
- package/lib/client.js +236 -266
- package/lib/client.js.map +3 -3
- package/lib/index.js +38 -458
- 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\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\n/* ---------- project manager ---------- */\n\n.project-manager {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.project-manager-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.project-manager-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.project-manager-main strong {\n font-size: 13px;\n}\n\n.project-manager-main .small {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.project-manager-main input {\n width: 100%;\n height: 28px;\n}\n\n.project-manager-actions {\n display: flex;\n gap: 4px;\n flex: none;\n align-items: center;\n}\n\n/* ---------- task editor v2 ---------- */\n\n.task-editor-v2 {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.task-editor-head {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.task-editor-head .task-title-input {\n width: 100%;\n height: 42px;\n padding: 0 12px;\n border-radius: 10px;\n border: 1px solid var(--m-line);\n background: var(--m-bg);\n color: var(--m-text);\n font-size: 16px;\n font-weight: 500;\n}\n\n.task-editor-head .task-title-input:focus {\n outline: none;\n border-color: var(--m-accent);\n}\n\n.task-status-picker {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 4px;\n padding: 4px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.task-status-picker button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 30px;\n border-radius: 7px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.task-status-picker button:hover {\n background: var(--m-hover);\n}\n\n.task-status-picker button.active {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n.task-editor-columns {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 250px;\n gap: 18px;\n}\n\n.task-editor-col {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-width: 0;\n}\n\n.task-editor-col textarea {\n width: 100%;\n resize: vertical;\n min-height: 120px;\n}\n\n.task-editor-col.side {\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-label {\n font-size: 11px;\n color: var(--m-text-3);\n margin-top: 2px;\n}\n\n.task-hint {\n margin: 0;\n font-size: 11.5px;\n color: var(--m-text-3);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.task-hint.danger {\n color: var(--m-danger);\n white-space: normal;\n}\n\n.task-source-row {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n.task-due {\n position: relative;\n}\n\n.task-due-trigger {\n display: flex;\n align-items: center;\n gap: 7px;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: 8px;\n background: var(--m-bg);\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n.task-due-trigger.empty {\n color: var(--m-text-3);\n}\n\n.task-due-trigger span {\n flex: 1;\n text-align: left;\n}\n\n.task-due-popover {\n position: absolute;\n z-index: 20;\n top: calc(100% + 6px);\n left: 0;\n width: 250px;\n padding: 10px;\n border: 1px solid var(--m-line);\n border-radius: 10px;\n background: var(--m-bg-2);\n box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.task-due-quick {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 5px;\n}\n\n.task-due-quick button {\n height: 28px;\n border-radius: 7px;\n background: var(--m-bg-1);\n border: 1px solid var(--m-line-soft);\n font-size: 11.5px;\n color: var(--m-text-2);\n}\n\n.task-due-quick button.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n.task-due-popover input[type='date'] {\n width: 100%;\n}\n\n.task-due-foot {\n display: flex;\n justify-content: space-between;\n}\n\n.task-priority-picker {\n display: grid;\n grid-template-columns: repeat(4, 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: 28px;\n border-radius: 6px;\n font-size: 11.5px;\n color: var(--m-text-3);\n border: 1px solid transparent;\n}\n\n.task-priority-picker button.p3 {\n color: #5aa7f0;\n}\n\n.task-priority-picker button.p5 {\n color: #d9a441;\n}\n\n.task-priority-picker button.p9 {\n color: var(--m-danger);\n}\n\n.task-priority-picker button.active {\n background: var(--m-bg-3);\n border-color: var(--m-line);\n color: var(--m-text);\n}\n\n.task-priority-picker button.p9.active {\n background: rgba(242, 90, 90, 0.16);\n border-color: rgba(242, 90, 90, 0.35);\n color: var(--m-danger);\n}\n\n.task-priority-picker button.p5.active {\n background: rgba(217, 164, 65, 0.16);\n border-color: rgba(217, 164, 65, 0.35);\n color: #d9a441;\n}\n\n.task-priority-picker button.p3.active {\n background: rgba(90, 167, 240, 0.16);\n border-color: rgba(90, 167, 240, 0.35);\n color: #5aa7f0;\n}\n\n.task-priority.p3 {\n background: #5aa7f0;\n}\n\n.task-priority-label.p9 {\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.12);\n}\n\n.task-priority-label.p5 {\n color: #d9a441;\n background: rgba(217, 164, 65, 0.12);\n}\n\n.task-priority-label.p3 {\n color: #5aa7f0;\n background: rgba(90, 167, 240, 0.12);\n}\n\n@media (max-width: 720px) {\n .task-editor-columns {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.mailharbor-spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: mailharbor-spin 0.7s linear infinite;\n opacity: 0.8;\n vertical-align: -2px;\n flex: none;\n}\n\n@keyframes mailharbor-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.extract-body {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.extract-title {\n width: 100%;\n height: 30px;\n font-size: 13px;\n font-weight: 500;\n}\n\n.extract-meta {\n display: grid;\n grid-template-columns: 150px 110px minmax(0, 1fr);\n gap: 6px;\n}\n\n.extract-meta input,\n.extract-meta select {\n height: 28px;\n font-size: 11.5px;\n width: 100%;\n}\n\n.extract-owner {\n width: 100%;\n}\n\n@media (max-width: 720px) {\n .extract-meta {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n/* ---------- conversation reading ---------- */\n\n.mailharbor-thread-view {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px 18px 32px;\n background: var(--m-bg);\n}\n\n.mailharbor-thread-hint {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 0 4px 2px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-thread-msg {\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n overflow: hidden;\n}\n\n.mailharbor-thread-msg.selected {\n border-color: var(--m-accent);\n box-shadow: 0 0 0 1px var(--m-accent-soft);\n}\n\n.mailharbor-thread-head {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n height: auto;\n padding: 9px 12px;\n text-align: left;\n border-radius: 0;\n}\n\n.mailharbor-thread-head:hover {\n background: var(--m-hover);\n}\n\n.thread-who {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.thread-who strong {\n font-size: 12.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.thread-to {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-thread-preview {\n padding: 0 14px 10px 47px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-thread-body {\n padding: 0 12px 12px;\n}\n\n.mailharbor-thread-frame {\n height: 380px;\n border: 1px solid var(--m-line-soft);\n border-radius: 8px;\n overflow: hidden;\n background: #fff;\n}\n\n.mailharbor-thread-frame iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n background: #fff;\n}\n\n.thread-loading {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 4px 12px;\n}\n\n@media (max-width: 900px) {\n .thread-to {\n display: none;\n }\n}\n\n/* ---------- compact reader ---------- */\n\n.mailharbor-reader-head {\n padding: 10px 16px 8px;\n gap: 6px;\n}\n\n.mailharbor-reader-title h2 {\n font-size: 15px;\n}\n\n.mailharbor-reader-meta.compact {\n gap: 10px;\n align-items: center;\n}\n\n.mailharbor-reader-meta.compact .who {\n gap: 6px;\n}\n\n.mailharbor-reader-recipients {\n display: flex;\n flex-wrap: wrap;\n gap: 6px 14px;\n font-size: 11.5px;\n color: var(--m-text-3);\n padding: 0 2px;\n}\n\n.mailharbor-reader-actions {\n gap: 4px;\n flex-wrap: wrap;\n}\n\n.mailharbor-reader-actions button {\n height: 28px;\n padding: 0 8px;\n}\n\n.mailharbor-attachments.compact {\n display: flex;\n gap: 6px;\n padding: 6px 16px;\n overflow-x: auto;\n border-top: 1px solid var(--m-line-soft);\n flex-wrap: nowrap;\n}\n\n.mailharbor-attachments.compact .mailharbor-attachment {\n flex: none;\n padding: 4px 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 6px;\n max-width: 260px;\n}\n\n.mailharbor-reader-body {\n flex: 1;\n min-height: 0;\n}\n\n.thread-who {\n flex-direction: row;\n align-items: baseline;\n gap: 6px;\n min-width: 0;\n}\n\n.thread-who strong {\n max-width: 45%;\n}\n\n.thread-who .small {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n.thread-date {\n flex: none;\n}\n\n.mailharbor button.mailharbor-thread-head {\n justify-content: flex-start;\n padding: 7px 10px;\n gap: 8px;\n}\n\n.mailharbor-thread-preview {\n padding: 0 12px 8px 40px;\n}\n\n.mailharbor-thread-frame {\n height: clamp(220px, 42vh, 420px);\n}\n\n.mailharbor-refreshing {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 0 14px 6px;\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.mailharbor-attachment-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 0 2px;\n}\n\n.mailharbor-attachment-chips .mailharbor-attachment {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n max-width: 280px;\n padding: 4px 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 6px;\n background: var(--m-bg-1);\n font-size: 11.5px;\n}\n\n.mailharbor-attachment-chips .mailharbor-attachment .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-thread-recipients {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n padding: 0 2px 8px;\n}\n\n.mailharbor-thread-frame {\n height: clamp(260px, 55vh, 640px);\n}\n\n.mailharbor-thread-body {\n padding: 8px 12px 12px;\n}\n\n.mailharbor-ai-summary.clamp,\n.mailharbor-ai-text.clamp {\n max-height: 150px;\n overflow: hidden;\n mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);\n -webkit-mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);\n}\n\n.mailharbor-ai-full {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: 66vh;\n overflow: auto;\n padding-right: 4px;\n font-size: 13px;\n line-height: 1.7;\n color: var(--m-text);\n}\n\n.mailharbor-ai-full h4 {\n margin: 0 0 4px;\n font-size: 11px;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-full .mailharbor-markdown {\n font-size: 13px;\n line-height: 1.7;\n}\n\n/* ---------- sync popover ---------- */\n\n.mailharbor-sync {\n position: relative;\n}\n\n.mailharbor-sync-pop {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 30;\n width: 320px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px solid var(--m-line);\n border-radius: 10px;\n background: var(--m-bg-2);\n box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);\n}\n\n.mailharbor-sync-head {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.mailharbor-sync-stats {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-sync-log {\n max-height: 110px;\n overflow: auto;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--m-bg);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n white-space: pre-wrap;\n color: var(--m-text-3);\n}\n\n.mailharbor-sync-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.mailharbor-thread-msg,\n.mailharbor-ai-card {\n flex: none;\n}\n\n.mailharbor-unread-banner {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0 10px 8px;\n padding: 6px 10px;\n border-radius: 8px;\n background: var(--m-accent-soft);\n color: var(--m-text-2);\n font-size: 11.5px;\n text-align: left;\n}\n\n.mailharbor-unread-banner:hover {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-starred {\n display: inline-flex;\n align-items: center;\n color: #e0b341;\n flex: none;\n}\n\n.mailharbor-reader-actions select {\n height: 28px;\n max-width: 130px;\n font-size: 11.5px;\n}\n\n.mailharbor-sync-error {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 12px 6px;\n padding: 6px 10px;\n border-radius: 8px;\n background: rgba(242, 90, 90, 0.12);\n color: var(--m-danger);\n font-size: 11.5px;\n}\n\n.mailharbor-onboarding {\n display: grid;\n gap: 6px;\n margin: 12px 0;\n padding: 10px 12px;\n border: 1px solid var(--m-line);\n border-radius: 10px;\n background: var(--m-bg-2);\n font-size: 12px;\n}\n\n.mailharbor-onboarding strong {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-right: 6px;\n border-radius: 50%;\n background: var(--m-accent-soft);\n color: var(--m-text);\n font-size: 11px;\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: clamp(280px, 28vw, 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.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\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: 1540px) {\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/* ---------- 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\n/* ---------- project manager ---------- */\n\n.project-manager {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.project-manager-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.project-manager-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.project-manager-main strong {\n font-size: 13px;\n}\n\n.project-manager-main .small {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.project-manager-main input {\n width: 100%;\n height: 28px;\n}\n\n.project-manager-actions {\n display: flex;\n gap: 4px;\n flex: none;\n align-items: center;\n}\n\n/* ---------- task editor v2 ---------- */\n\n.task-editor-v2 {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.task-editor-head {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.task-editor-head .task-title-input {\n width: 100%;\n height: 42px;\n padding: 0 12px;\n border-radius: 10px;\n border: 1px solid var(--m-line);\n background: var(--m-bg);\n color: var(--m-text);\n font-size: 16px;\n font-weight: 500;\n}\n\n.task-editor-head .task-title-input:focus {\n outline: none;\n border-color: var(--m-accent);\n}\n\n.task-status-picker {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 4px;\n padding: 4px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.task-status-picker button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 30px;\n border-radius: 7px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.task-status-picker button:hover {\n background: var(--m-hover);\n}\n\n.task-status-picker button.active {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n.task-editor-columns {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 250px;\n gap: 18px;\n}\n\n.task-editor-col {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-width: 0;\n}\n\n.task-editor-col textarea {\n width: 100%;\n resize: vertical;\n min-height: 120px;\n}\n\n.task-editor-col.side {\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-label {\n font-size: 11px;\n color: var(--m-text-3);\n margin-top: 2px;\n}\n\n.task-hint {\n margin: 0;\n font-size: 11.5px;\n color: var(--m-text-3);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.task-hint.danger {\n color: var(--m-danger);\n white-space: normal;\n}\n\n.task-source-row {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n.task-due {\n position: relative;\n}\n\n.task-due-trigger {\n display: flex;\n align-items: center;\n gap: 7px;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: 8px;\n background: var(--m-bg);\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n.task-due-trigger.empty {\n color: var(--m-text-3);\n}\n\n.task-due-trigger span {\n flex: 1;\n text-align: left;\n}\n\n.task-due-popover {\n position: absolute;\n z-index: 20;\n top: calc(100% + 6px);\n left: 0;\n width: 250px;\n padding: 10px;\n border: 1px solid var(--m-line);\n border-radius: 10px;\n background: var(--m-bg-2);\n box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.task-due-quick {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 5px;\n}\n\n.task-due-quick button {\n height: 28px;\n border-radius: 7px;\n background: var(--m-bg-1);\n border: 1px solid var(--m-line-soft);\n font-size: 11.5px;\n color: var(--m-text-2);\n}\n\n.task-due-quick button.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n.task-due-popover input[type='date'] {\n width: 100%;\n}\n\n.task-due-foot {\n display: flex;\n justify-content: space-between;\n}\n\n.task-priority-picker {\n display: grid;\n grid-template-columns: repeat(4, 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: 28px;\n border-radius: 6px;\n font-size: 11.5px;\n color: var(--m-text-3);\n border: 1px solid transparent;\n}\n\n.task-priority-picker button.p3 {\n color: #5aa7f0;\n}\n\n.task-priority-picker button.p5 {\n color: #d9a441;\n}\n\n.task-priority-picker button.p9 {\n color: var(--m-danger);\n}\n\n.task-priority-picker button.active {\n background: var(--m-bg-3);\n border-color: var(--m-line);\n color: var(--m-text);\n}\n\n.task-priority-picker button.p9.active {\n background: rgba(242, 90, 90, 0.16);\n border-color: rgba(242, 90, 90, 0.35);\n color: var(--m-danger);\n}\n\n.task-priority-picker button.p5.active {\n background: rgba(217, 164, 65, 0.16);\n border-color: rgba(217, 164, 65, 0.35);\n color: #d9a441;\n}\n\n.task-priority-picker button.p3.active {\n background: rgba(90, 167, 240, 0.16);\n border-color: rgba(90, 167, 240, 0.35);\n color: #5aa7f0;\n}\n\n.task-priority.p3 {\n background: #5aa7f0;\n}\n\n.task-priority-label.p9 {\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.12);\n}\n\n.task-priority-label.p5 {\n color: #d9a441;\n background: rgba(217, 164, 65, 0.12);\n}\n\n.task-priority-label.p3 {\n color: #5aa7f0;\n background: rgba(90, 167, 240, 0.12);\n}\n\n@media (max-width: 720px) {\n .task-editor-columns {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n.mailharbor-spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: mailharbor-spin 0.7s linear infinite;\n opacity: 0.8;\n vertical-align: -2px;\n flex: none;\n}\n\n@keyframes mailharbor-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.extract-body {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.extract-title {\n width: 100%;\n height: 30px;\n font-size: 13px;\n font-weight: 500;\n}\n\n.extract-meta {\n display: grid;\n grid-template-columns: 150px 110px minmax(0, 1fr);\n gap: 6px;\n}\n\n.extract-meta input,\n.extract-meta select {\n height: 28px;\n font-size: 11.5px;\n width: 100%;\n}\n\n.extract-owner {\n width: 100%;\n}\n\n@media (max-width: 720px) {\n .extract-meta {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n/* ---------- conversation reading ---------- */\n\n.mailharbor-thread-view {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px 18px 32px;\n background: var(--m-bg);\n}\n\n.mailharbor-thread-hint {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 0 4px 2px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-thread-msg {\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n overflow: hidden;\n}\n\n.mailharbor-thread-msg.selected {\n border-color: var(--m-accent);\n box-shadow: 0 0 0 1px var(--m-accent-soft);\n}\n\n.mailharbor-thread-head {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n height: auto;\n padding: 9px 12px;\n text-align: left;\n border-radius: 0;\n}\n\n.mailharbor-thread-head:hover {\n background: var(--m-hover);\n}\n\n.thread-who {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.thread-who strong {\n font-size: 12.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.thread-to {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-thread-preview {\n padding: 0 14px 10px 47px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-thread-body {\n padding: 0 12px 12px;\n}\n\n.mailharbor-thread-frame {\n height: 380px;\n border: 1px solid var(--m-line-soft);\n border-radius: 8px;\n overflow: hidden;\n background: #fff;\n}\n\n.mailharbor-thread-frame iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n background: #fff;\n}\n\n.thread-loading {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 4px 12px;\n}\n\n@media (max-width: 900px) {\n .thread-to {\n display: none;\n }\n}\n\n/* ---------- compact reader ---------- */\n\n.mailharbor-reader-head {\n padding: 10px 16px 8px;\n gap: 6px;\n}\n\n.mailharbor-reader-title h2 {\n font-size: 15px;\n}\n\n.mailharbor-reader-meta.compact {\n gap: 10px;\n align-items: center;\n}\n\n.mailharbor-reader-meta.compact .who {\n gap: 6px;\n}\n\n.mailharbor-reader-recipients {\n display: flex;\n flex-wrap: wrap;\n gap: 6px 14px;\n font-size: 11.5px;\n color: var(--m-text-3);\n padding: 0 2px;\n}\n\n.mailharbor-reader-actions {\n gap: 4px;\n flex-wrap: wrap;\n}\n\n.mailharbor-reader-actions button {\n height: 28px;\n padding: 0 8px;\n}\n\n.mailharbor-attachments.compact {\n display: flex;\n gap: 6px;\n padding: 6px 16px;\n overflow-x: auto;\n border-top: 1px solid var(--m-line-soft);\n flex-wrap: nowrap;\n}\n\n.mailharbor-attachments.compact .mailharbor-attachment {\n flex: none;\n padding: 4px 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 6px;\n max-width: 260px;\n}\n\n.mailharbor-reader-body {\n flex: 1;\n min-height: 0;\n}\n\n.thread-who {\n flex-direction: row;\n align-items: baseline;\n gap: 6px;\n min-width: 0;\n}\n\n.thread-who strong {\n max-width: 45%;\n}\n\n.thread-who .small {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n.thread-date {\n flex: none;\n}\n\n.mailharbor button.mailharbor-thread-head {\n justify-content: flex-start;\n padding: 7px 10px;\n gap: 8px;\n}\n\n.mailharbor-thread-preview {\n padding: 0 12px 8px 40px;\n}\n\n.mailharbor-thread-frame {\n height: clamp(220px, 42vh, 420px);\n}\n\n.mailharbor-refreshing {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 0 14px 6px;\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.mailharbor-attachment-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 0 2px;\n}\n\n.mailharbor-attachment-chips .mailharbor-attachment {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n max-width: 280px;\n padding: 4px 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 6px;\n background: var(--m-bg-1);\n font-size: 11.5px;\n}\n\n.mailharbor-attachment-chips .mailharbor-attachment .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-thread-recipients {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n padding: 0 2px 8px;\n}\n\n.mailharbor-thread-frame {\n height: clamp(260px, 55vh, 640px);\n}\n\n.mailharbor-thread-body {\n padding: 8px 12px 12px;\n}\n\n.mailharbor-ai-summary.clamp,\n.mailharbor-ai-text.clamp {\n max-height: 150px;\n overflow: hidden;\n mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);\n -webkit-mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);\n}\n\n.mailharbor-ai-full {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: 66vh;\n overflow: auto;\n padding-right: 4px;\n font-size: 13px;\n line-height: 1.7;\n color: var(--m-text);\n}\n\n.mailharbor-ai-full h4 {\n margin: 0 0 4px;\n font-size: 11px;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-full .mailharbor-markdown {\n font-size: 13px;\n line-height: 1.7;\n}\n\n/* ---------- sync popover ---------- */\n\n.mailharbor-sync {\n position: relative;\n}\n\n.mailharbor-sync-pop {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 30;\n width: 320px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px solid var(--m-line);\n border-radius: 10px;\n background: var(--m-bg-2);\n box-shadow: 0 16px 40px rgba(0, 0, 0, 0.4);\n}\n\n.mailharbor-sync-head {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.mailharbor-sync-stats {\n display: flex;\n justify-content: space-between;\n gap: 8px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-sync-log {\n max-height: 110px;\n overflow: auto;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--m-bg);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 10.5px;\n white-space: pre-wrap;\n color: var(--m-text-3);\n}\n\n.mailharbor-sync-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.mailharbor-thread-msg,\n.mailharbor-ai-card {\n flex: none;\n}\n\n.mailharbor-unread-banner {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0 10px 8px;\n padding: 6px 10px;\n border-radius: 8px;\n background: var(--m-accent-soft);\n color: var(--m-text-2);\n font-size: 11.5px;\n text-align: left;\n}\n\n.mailharbor-unread-banner:hover {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-starred {\n display: inline-flex;\n align-items: center;\n color: #e0b341;\n flex: none;\n}\n\n.mailharbor-reader-actions select {\n height: 28px;\n max-width: 130px;\n font-size: 11.5px;\n}\n\n.mailharbor-sync-error {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 12px 6px;\n padding: 6px 10px;\n border-radius: 8px;\n background: rgba(242, 90, 90, 0.12);\n color: var(--m-danger);\n font-size: 11.5px;\n}\n\n.mailharbor-onboarding {\n display: grid;\n gap: 6px;\n margin: 12px 0;\n padding: 10px 12px;\n border: 1px solid var(--m-line);\n border-radius: 10px;\n background: var(--m-bg-2);\n font-size: 12px;\n}\n\n.mailharbor-onboarding strong {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-right: 6px;\n border-radius: 50%;\n background: var(--m-accent-soft);\n color: var(--m-text);\n font-size: 11px;\n}\n\n/* ---------- responsive shell ---------- */\n\n.mailharbor-scrim,\n.mailharbor-ai-scrim {\n display: none;\n}\n\n.mailharbor-account {\n flex: 0 1 auto;\n}\n\n.mailharbor-account > div {\n min-width: 0;\n}\n\n.mailharbor-account .small {\n display: block;\n max-width: 150px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-action-label {\n display: inline;\n}\n\n@media (max-width: 1540px) {\n .mailharbor-ai-scrim {\n display: block;\n position: absolute;\n inset: 0;\n z-index: 5;\n border: 0;\n background: rgba(0, 0, 0, 0.32);\n backdrop-filter: blur(1px);\n }\n\n .mailharbor-ai {\n z-index: 6;\n }\n}\n\n@media (max-width: 1220px) {\n .mailharbor-scrim {\n display: block;\n position: absolute;\n inset: 0;\n z-index: 25;\n border: 0;\n background: rgba(0, 0, 0, 0.38);\n }\n\n .mailharbor-side {\n position: absolute;\n inset: 0 auto 0 0;\n z-index: 30;\n display: flex;\n width: min(290px, 86%);\n box-shadow: 20px 0 50px rgba(0, 0, 0, 0.45);\n animation: mailharbor-drawer 160ms ease-out;\n }\n\n .mailharbor-list {\n width: clamp(260px, 36vw, 320px);\n }\n}\n\n@keyframes mailharbor-drawer {\n from {\n transform: translateX(-12px);\n opacity: 0.4;\n }\n to {\n transform: none;\n opacity: 1;\n }\n}\n\n@media (max-width: 900px) {\n .mailharbor-narrow .mailharbor-list {\n width: 100%;\n flex: 1;\n min-width: 0;\n border-right: 0;\n }\n\n .mailharbor-narrow .mailharbor-body[data-mode='reader'] .mailharbor-reader {\n width: 100%;\n flex: 1;\n }\n\n .mailharbor-reader-head {\n padding: 12px 14px 10px;\n gap: 8px;\n }\n\n .mailharbor-reader-title h2 {\n font-size: 16px;\n }\n\n .mailharbor-reader-body {\n padding: 10px 12px 12px;\n }\n\n .mailharbor-ai {\n width: min(420px, 100%);\n }\n}\n\n@media (max-width: 760px) {\n .mailharbor-topbar {\n flex-wrap: wrap;\n height: auto;\n min-height: 52px;\n padding: 8px 10px;\n gap: 6px;\n }\n\n .mailharbor-account {\n max-width: 170px;\n }\n\n .mailharbor-account .small {\n max-width: 105px;\n }\n\n .mailharbor-action-label {\n display: none;\n }\n\n .mailharbor-topbar button.ghost,\n .mailharbor-topbar button.primary {\n padding: 0 9px;\n }\n\n .mailharbor-filters {\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 10px;\n }\n\n .mailharbor-filters input[type='search'] {\n flex: 1 1 100%;\n order: -1;\n }\n\n .mailharbor-settings-page {\n padding: 16px 12px;\n }\n\n .mailharbor-grid {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .mailharbor-grid.three {\n grid-template-columns: minmax(0, 1fr) minmax(64px, 80px) auto;\n }\n\n .mailharbor-sync-pop {\n position: fixed;\n top: 56px;\n left: 10px;\n right: 10px;\n width: auto;\n }\n}\n\n@media (max-width: 560px) {\n .mailharbor-topbar {\n gap: 4px;\n }\n\n .mailharbor-account {\n max-width: 132px;\n }\n\n .mailharbor-account .small {\n display: none;\n }\n\n .mailharbor-account .badge {\n display: none;\n }\n\n .mailharbor-row {\n padding: 9px 12px;\n }\n\n .mailharbor-row .snippet {\n -webkit-line-clamp: 1;\n }\n\n .mailharbor-reader-actions {\n gap: 4px;\n }\n\n .mailharbor-reader-actions button {\n padding: 0 7px;\n }\n}\n\n/* Reader actions collapse on phones: keep reply/forward, put the rest behind \u66F4\u591A. */\n.mailharbor .mailharbor-more-toggle {\n display: none;\n}\n\n@media (max-width: 760px) {\n .mailharbor-reader-actions .mailharbor-secondary {\n display: none;\n }\n\n .mailharbor-reader-actions.expanded .mailharbor-secondary {\n display: inline-flex;\n }\n\n .mailharbor-reader-actions .mailharbor-more-toggle {\n display: inline-flex;\n }\n\n .mailharbor-reader-actions .mailharbor-divider.mailharbor-secondary {\n display: none;\n }\n}\n\n/* Subtle scrollbars keep long lists readable without stealing width. */\n.mailharbor ::-webkit-scrollbar {\n width: 9px;\n height: 9px;\n}\n\n.mailharbor ::-webkit-scrollbar-thumb {\n background: var(--m-line-strong);\n border: 2px solid transparent;\n border-radius: 999px;\n background-clip: padding-box;\n}\n\n.mailharbor ::-webkit-scrollbar-thumb:hover {\n background: var(--m-text-3);\n background-clip: padding-box;\n}\n\n/* ---------- attachment preview & drop target ---------- */\n\n.mailharbor-preview {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 320px;\n max-height: 68vh;\n overflow: auto;\n border-radius: 10px;\n background: var(--m-bg);\n}\n\n.mailharbor-preview.text {\n align-items: stretch;\n justify-content: flex-start;\n}\n\n.mailharbor-preview img {\n display: block;\n max-width: 100%;\n max-height: 66vh;\n object-fit: contain;\n}\n\n.mailharbor-preview iframe {\n width: 100%;\n height: 66vh;\n border: 0;\n border-radius: 10px;\n background: #fff;\n}\n\n.mailharbor-preview-text {\n width: 100%;\n max-height: 66vh;\n margin: 0;\n padding: 16px;\n overflow: auto;\n white-space: pre-wrap;\n word-break: break-word;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n color: var(--m-text-2);\n}\n\n.mailharbor-editor.dragging {\n outline: 2px dashed var(--m-accent);\n outline-offset: -2px;\n background: var(--m-accent-soft);\n}\n\n/* Rows off screen skip layout/paint work in large mailboxes. */\n.mailharbor-row {\n content-visibility: auto;\n contain-intrinsic-size: auto 68px;\n}\n";
|
|
39
39
|
|
|
40
40
|
// src/client/icon.tsx
|
|
41
41
|
var import_react = require("react");
|
|
@@ -154,13 +154,6 @@ var api = {
|
|
|
154
154
|
method: "POST",
|
|
155
155
|
body: JSON.stringify(payload)
|
|
156
156
|
}),
|
|
157
|
-
oauthStatus: (accountId) => request(`oauth/status${query({ accountId })}`),
|
|
158
|
-
oauthStart: (accountId) => request("oauth/start", { method: "POST", body: JSON.stringify({ accountId }) }),
|
|
159
|
-
oauthPoll: (state) => request("oauth/poll", {
|
|
160
|
-
method: "POST",
|
|
161
|
-
body: JSON.stringify({ state })
|
|
162
|
-
}),
|
|
163
|
-
oauthRevoke: (accountId) => request("oauth/revoke", { method: "POST", body: JSON.stringify({ accountId }) }),
|
|
164
157
|
check: (id) => request(`accounts/${encodeURIComponent(id)}/check`, { method: "POST" }),
|
|
165
158
|
aiStatus: (accountId) => request(`ai/status${query({ accountId: accountId ?? null })}`),
|
|
166
159
|
aiModels: () => request("ai/models"),
|
|
@@ -994,7 +987,7 @@ ${card.output}`
|
|
|
994
987
|
),
|
|
995
988
|
(0, import_react5.createElement)(
|
|
996
989
|
"button",
|
|
997
|
-
{ className: "subtle icon", title: "\u6536\u8D77", onClick: onClose },
|
|
990
|
+
{ className: "subtle icon", title: "\u6536\u8D77", "aria-label": "\u5173\u95ED AI \u52A9\u624B", onClick: onClose },
|
|
998
991
|
(0, import_react5.createElement)(Icon, { name: "chevronRight", size: 14 })
|
|
999
992
|
)
|
|
1000
993
|
),
|
|
@@ -1300,8 +1293,6 @@ function AddAccountForm({
|
|
|
1300
1293
|
onCancel
|
|
1301
1294
|
}) {
|
|
1302
1295
|
const [email, setEmail] = (0, import_react6.useState)("");
|
|
1303
|
-
const [authMode, setAuthMode] = (0, import_react6.useState)("password");
|
|
1304
|
-
const [oauthProvider, setOauthProvider] = (0, import_react6.useState)("google");
|
|
1305
1296
|
const [password, setPassword] = (0, import_react6.useState)("");
|
|
1306
1297
|
const [label, setLabel] = (0, import_react6.useState)("");
|
|
1307
1298
|
const [remember, setRemember] = (0, import_react6.useState)(true);
|
|
@@ -1322,8 +1313,8 @@ function AddAccountForm({
|
|
|
1322
1313
|
const domain = domainOf(email);
|
|
1323
1314
|
const { state } = await api.addAccount({
|
|
1324
1315
|
email: email.trim(),
|
|
1325
|
-
|
|
1326
|
-
|
|
1316
|
+
password,
|
|
1317
|
+
remember,
|
|
1327
1318
|
...label.trim() === "" ? {} : { label: label.trim() },
|
|
1328
1319
|
...advanced ? {
|
|
1329
1320
|
imap: {
|
|
@@ -1350,41 +1341,6 @@ function AddAccountForm({
|
|
|
1350
1341
|
"div",
|
|
1351
1342
|
{ className: "mailharbor-card" },
|
|
1352
1343
|
(0, import_react6.createElement)("h3", null, "\u6DFB\u52A0\u90AE\u7BB1\u8D26\u53F7"),
|
|
1353
|
-
(0, import_react6.createElement)(
|
|
1354
|
-
"div",
|
|
1355
|
-
{ className: "mailharbor-grid" },
|
|
1356
|
-
(0, import_react6.createElement)(
|
|
1357
|
-
Field,
|
|
1358
|
-
{ label: "\u767B\u5F55\u65B9\u5F0F" },
|
|
1359
|
-
(0, import_react6.createElement)(
|
|
1360
|
-
"select",
|
|
1361
|
-
{
|
|
1362
|
-
value: authMode,
|
|
1363
|
-
onChange: (event) => setAuthMode(event.target.value === "oauth2" ? "oauth2" : "password")
|
|
1364
|
-
},
|
|
1365
|
-
(0, import_react6.createElement)("option", { value: "password" }, "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801"),
|
|
1366
|
-
(0, import_react6.createElement)("option", { value: "oauth2" }, "OAuth2\uFF08Gmail / Outlook\uFF09")
|
|
1367
|
-
)
|
|
1368
|
-
),
|
|
1369
|
-
authMode === "oauth2" ? (0, import_react6.createElement)(
|
|
1370
|
-
Field,
|
|
1371
|
-
{ label: "OAuth2 \u670D\u52A1\u5546" },
|
|
1372
|
-
(0, import_react6.createElement)(
|
|
1373
|
-
"select",
|
|
1374
|
-
{
|
|
1375
|
-
value: oauthProvider,
|
|
1376
|
-
onChange: (event) => setOauthProvider(event.target.value === "microsoft" ? "microsoft" : "google")
|
|
1377
|
-
},
|
|
1378
|
-
(0, import_react6.createElement)("option", { value: "google" }, "Google / Gmail"),
|
|
1379
|
-
(0, import_react6.createElement)("option", { value: "microsoft" }, "Microsoft 365 / Outlook")
|
|
1380
|
-
)
|
|
1381
|
-
) : null
|
|
1382
|
-
),
|
|
1383
|
-
authMode === "oauth2" ? (0, import_react6.createElement)(
|
|
1384
|
-
"p",
|
|
1385
|
-
{ className: "small muted" },
|
|
1386
|
-
"\u4FDD\u5B58\u540E\u5230\u8D26\u53F7\u8BBE\u7F6E\u91CC\u70B9\u300COAuth \u6388\u6743\u300D\u3002\u6388\u6743\u524D\u8BF7\u5148\u5728\u8BBE\u7F6E\u4E0B\u65B9\u586B\u597D OAuth \u5E94\u7528 Client ID\u3002"
|
|
1387
|
-
) : null,
|
|
1388
1344
|
(0, import_react6.createElement)(
|
|
1389
1345
|
"div",
|
|
1390
1346
|
{ className: "mailharbor-grid" },
|
|
@@ -1399,7 +1355,7 @@ function AddAccountForm({
|
|
|
1399
1355
|
autoFocus: true
|
|
1400
1356
|
})
|
|
1401
1357
|
),
|
|
1402
|
-
|
|
1358
|
+
(0, import_react6.createElement)(
|
|
1403
1359
|
Field,
|
|
1404
1360
|
{ label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801" },
|
|
1405
1361
|
(0, import_react6.createElement)("input", {
|
|
@@ -1407,7 +1363,7 @@ function AddAccountForm({
|
|
|
1407
1363
|
value: password,
|
|
1408
1364
|
onChange: (event) => setPassword(event.target.value)
|
|
1409
1365
|
})
|
|
1410
|
-
)
|
|
1366
|
+
)
|
|
1411
1367
|
),
|
|
1412
1368
|
(0, import_react6.createElement)(
|
|
1413
1369
|
Field,
|
|
@@ -1418,7 +1374,7 @@ function AddAccountForm({
|
|
|
1418
1374
|
onChange: (event) => setLabel(event.target.value)
|
|
1419
1375
|
})
|
|
1420
1376
|
),
|
|
1421
|
-
|
|
1377
|
+
(0, import_react6.createElement)(Toggle, { checked: remember, onChange: setRemember, label: "\u8BB0\u4F4F\u5BC6\u7801\uFF08\u5199\u5165\u7CFB\u7EDF\u94A5\u5319\u4E32\uFF09" }),
|
|
1422
1378
|
(0, import_react6.createElement)(
|
|
1423
1379
|
"button",
|
|
1424
1380
|
{ className: "ghost", onClick: () => setAdvanced(!advanced) },
|
|
@@ -1592,6 +1548,7 @@ function Composer({
|
|
|
1592
1548
|
const [subject, setSubject] = (0, import_react7.useState)(initial.subject);
|
|
1593
1549
|
const [showCc, setShowCc] = (0, import_react7.useState)(initial.cc !== "" || initial.bcc !== "");
|
|
1594
1550
|
const [attachments, setAttachments] = (0, import_react7.useState)(initial.attachments ?? []);
|
|
1551
|
+
const [dragging, setDragging] = (0, import_react7.useState)(false);
|
|
1595
1552
|
const [busy, setBusy] = (0, import_react7.useState)(false);
|
|
1596
1553
|
const [error, setError] = (0, import_react7.useState)("");
|
|
1597
1554
|
const [confirming, setConfirming] = (0, import_react7.useState)(false);
|
|
@@ -2009,13 +1966,30 @@ function Composer({
|
|
|
2009
1966
|
)
|
|
2010
1967
|
),
|
|
2011
1968
|
(0, import_react7.createElement)("div", {
|
|
2012
|
-
className: "mailharbor-editor",
|
|
1969
|
+
className: dragging ? "mailharbor-editor dragging" : "mailharbor-editor",
|
|
2013
1970
|
ref: editor,
|
|
2014
1971
|
contentEditable: true,
|
|
2015
1972
|
suppressContentEditableWarning: true,
|
|
2016
|
-
"data-placeholder": "\u5199\u70B9\u4EC0\u4E48\u2026",
|
|
1973
|
+
"data-placeholder": "\u5199\u70B9\u4EC0\u4E48\u2026\uFF08\u53EF\u62D6\u62FD\u9644\u4EF6\u6216\u7C98\u8D34\u622A\u56FE\uFF09",
|
|
2017
1974
|
spellCheck: true,
|
|
2018
|
-
onInput: markDirty
|
|
1975
|
+
onInput: markDirty,
|
|
1976
|
+
onDragOver: (event) => {
|
|
1977
|
+
event.preventDefault();
|
|
1978
|
+
setDragging(true);
|
|
1979
|
+
},
|
|
1980
|
+
onDragLeave: () => setDragging(false),
|
|
1981
|
+
onDrop: (event) => {
|
|
1982
|
+
event.preventDefault();
|
|
1983
|
+
setDragging(false);
|
|
1984
|
+
void addFiles(event.dataTransfer?.files ?? null);
|
|
1985
|
+
},
|
|
1986
|
+
onPaste: (event) => {
|
|
1987
|
+
const files = event.clipboardData?.files;
|
|
1988
|
+
if (files !== void 0 && files !== null && files.length > 0) {
|
|
1989
|
+
event.preventDefault();
|
|
1990
|
+
void addFiles(files);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
2019
1993
|
}),
|
|
2020
1994
|
(0, import_react7.createElement)(
|
|
2021
1995
|
"div",
|
|
@@ -2089,49 +2063,126 @@ function isSentMailbox(mailbox) {
|
|
|
2089
2063
|
function errorText4(error) {
|
|
2090
2064
|
return error instanceof Error ? error.message : String(error);
|
|
2091
2065
|
}
|
|
2066
|
+
function previewable(contentType) {
|
|
2067
|
+
const type = contentType.toLowerCase();
|
|
2068
|
+
return type.startsWith("image/") && type !== "image/svg+xml" || type === "application/pdf" || type === "text/plain" || type === "text/csv" || type === "text/markdown" || type === "application/json";
|
|
2069
|
+
}
|
|
2070
|
+
function AttachmentPreviewText({ url }) {
|
|
2071
|
+
const [text, setText] = (0, import_react8.useState)("");
|
|
2072
|
+
const [error, setError] = (0, import_react8.useState)("");
|
|
2073
|
+
(0, import_react8.useEffect)(() => {
|
|
2074
|
+
let cancelled = false;
|
|
2075
|
+
fetch(url).then((response) => response.text()).then((value) => {
|
|
2076
|
+
if (!cancelled) setText(value.slice(0, 2e5));
|
|
2077
|
+
}).catch((caught) => {
|
|
2078
|
+
if (!cancelled) setError(caught instanceof Error ? caught.message : String(caught));
|
|
2079
|
+
});
|
|
2080
|
+
return () => {
|
|
2081
|
+
cancelled = true;
|
|
2082
|
+
};
|
|
2083
|
+
}, [url]);
|
|
2084
|
+
if (error !== "") return (0, import_react8.createElement)("p", { className: "small", style: { color: "var(--m-danger)" } }, error);
|
|
2085
|
+
return (0, import_react8.createElement)("pre", { className: "mailharbor-preview-text" }, text === "" ? "\u52A0\u8F7D\u4E2D\u2026" : text);
|
|
2086
|
+
}
|
|
2092
2087
|
function AttachmentChips({
|
|
2093
2088
|
detail,
|
|
2094
2089
|
source,
|
|
2095
2090
|
mailbox
|
|
2096
2091
|
}) {
|
|
2097
2092
|
const [expanded, setExpanded] = (0, import_react8.useState)(false);
|
|
2093
|
+
const [preview, setPreview] = (0, import_react8.useState)(null);
|
|
2098
2094
|
if (detail.attachments.length === 0) return null;
|
|
2099
2095
|
const shown = expanded ? detail.attachments : detail.attachments.slice(0, 3);
|
|
2100
2096
|
const hidden = detail.attachments.length - shown.length;
|
|
2097
|
+
const urlFor = (file, inline2) => source === "server" ? api.attachmentUrl({
|
|
2098
|
+
accountId: detail.accountId,
|
|
2099
|
+
source: "server",
|
|
2100
|
+
mailbox,
|
|
2101
|
+
uid: detail.uid ?? "",
|
|
2102
|
+
part: file.id,
|
|
2103
|
+
...inline2 ? { inline: 1 } : {}
|
|
2104
|
+
}) : api.attachmentUrl({
|
|
2105
|
+
accountId: detail.accountId,
|
|
2106
|
+
source: "local",
|
|
2107
|
+
emailId: detail.id,
|
|
2108
|
+
attachmentId: file.id,
|
|
2109
|
+
...inline2 ? { inline: 1 } : {}
|
|
2110
|
+
});
|
|
2111
|
+
const previewUrl = preview === null ? "" : urlFor(preview, true);
|
|
2101
2112
|
return (0, import_react8.createElement)(
|
|
2102
2113
|
"div",
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2114
|
+
null,
|
|
2115
|
+
(0, import_react8.createElement)(
|
|
2116
|
+
"div",
|
|
2117
|
+
{ className: "mailharbor-attachment-chips" },
|
|
2118
|
+
shown.map(
|
|
2119
|
+
(file) => previewable(file.contentType) ? (0, import_react8.createElement)(
|
|
2120
|
+
"button",
|
|
2121
|
+
{
|
|
2122
|
+
key: file.id,
|
|
2123
|
+
className: "mailharbor-attachment",
|
|
2124
|
+
title: `\u9884\u89C8 ${file.filename}`,
|
|
2125
|
+
onClick: () => setPreview(file)
|
|
2126
|
+
},
|
|
2127
|
+
(0, import_react8.createElement)(Icon, { name: "paperclip", size: 12 }),
|
|
2128
|
+
(0, import_react8.createElement)("span", { className: "name" }, file.filename),
|
|
2129
|
+
(0, import_react8.createElement)("span", { className: "muted small" }, formatBytes(file.size))
|
|
2130
|
+
) : (0, import_react8.createElement)(
|
|
2131
|
+
"a",
|
|
2132
|
+
{
|
|
2133
|
+
className: "mailharbor-attachment",
|
|
2134
|
+
key: file.id,
|
|
2135
|
+
href: urlFor(file, false),
|
|
2136
|
+
download: file.filename,
|
|
2137
|
+
title: file.filename
|
|
2138
|
+
},
|
|
2139
|
+
(0, import_react8.createElement)(Icon, { name: "paperclip", size: 12 }),
|
|
2140
|
+
(0, import_react8.createElement)("span", { className: "name" }, file.filename),
|
|
2141
|
+
(0, import_react8.createElement)("span", { className: "muted small" }, formatBytes(file.size))
|
|
2142
|
+
)
|
|
2143
|
+
),
|
|
2144
|
+
hidden > 0 ? (0, import_react8.createElement)(
|
|
2145
|
+
"button",
|
|
2146
|
+
{ className: "subtle small", onClick: () => setExpanded(true) },
|
|
2147
|
+
`+${String(hidden)} \u4E2A\u9644\u4EF6`
|
|
2148
|
+
) : detail.attachments.length > 3 ? (0, import_react8.createElement)("button", { className: "subtle small", onClick: () => setExpanded(false) }, "\u6536\u8D77\u9644\u4EF6") : null
|
|
2149
|
+
),
|
|
2150
|
+
preview === null ? null : (0, import_react8.createElement)(
|
|
2151
|
+
Modal,
|
|
2152
|
+
{
|
|
2153
|
+
title: preview.filename,
|
|
2154
|
+
wide: true,
|
|
2155
|
+
onClose: () => setPreview(null),
|
|
2156
|
+
footer: (0, import_react8.createElement)(
|
|
2157
|
+
"div",
|
|
2158
|
+
{ style: { display: "flex", gap: "8px", width: "100%", alignItems: "center" } },
|
|
2159
|
+
(0, import_react8.createElement)(
|
|
2160
|
+
"span",
|
|
2161
|
+
{ className: "small muted" },
|
|
2162
|
+
`${preview.contentType} \xB7 ${formatBytes(preview.size)}`
|
|
2163
|
+
),
|
|
2164
|
+
(0, import_react8.createElement)("span", { className: "spacer" }),
|
|
2165
|
+
(0, import_react8.createElement)(
|
|
2166
|
+
"a",
|
|
2167
|
+
{
|
|
2168
|
+
className: "ghost",
|
|
2169
|
+
href: urlFor(preview, false),
|
|
2170
|
+
download: preview.filename,
|
|
2171
|
+
style: { display: "inline-flex", alignItems: "center", gap: "5px" }
|
|
2172
|
+
},
|
|
2173
|
+
"\u4E0B\u8F7D"
|
|
2174
|
+
),
|
|
2175
|
+
(0, import_react8.createElement)("button", { className: "primary small", onClick: () => setPreview(null) }, "\u5173\u95ED")
|
|
2176
|
+
)
|
|
2177
|
+
},
|
|
2178
|
+
(0, import_react8.createElement)(
|
|
2179
|
+
"div",
|
|
2107
2180
|
{
|
|
2108
|
-
className:
|
|
2109
|
-
key: file.id,
|
|
2110
|
-
href: source === "server" ? api.attachmentUrl({
|
|
2111
|
-
accountId: detail.accountId,
|
|
2112
|
-
source: "server",
|
|
2113
|
-
mailbox,
|
|
2114
|
-
uid: detail.uid ?? "",
|
|
2115
|
-
part: file.id
|
|
2116
|
-
}) : api.attachmentUrl({
|
|
2117
|
-
accountId: detail.accountId,
|
|
2118
|
-
source: "local",
|
|
2119
|
-
emailId: detail.id,
|
|
2120
|
-
attachmentId: file.id
|
|
2121
|
-
}),
|
|
2122
|
-
download: file.filename,
|
|
2123
|
-
title: file.filename
|
|
2181
|
+
className: `mailharbor-preview${preview.contentType.toLowerCase().startsWith("image/") ? " image" : preview.contentType.toLowerCase() === "application/pdf" ? " pdf" : " text"}`
|
|
2124
2182
|
},
|
|
2125
|
-
(0, import_react8.createElement)(
|
|
2126
|
-
(0, import_react8.createElement)("span", { className: "name" }, file.filename),
|
|
2127
|
-
(0, import_react8.createElement)("span", { className: "muted small" }, formatBytes(file.size))
|
|
2183
|
+
preview.contentType.toLowerCase().startsWith("image/") ? (0, import_react8.createElement)("img", { src: previewUrl, alt: preview.filename }) : preview.contentType.toLowerCase() === "application/pdf" ? (0, import_react8.createElement)("iframe", { src: previewUrl, title: preview.filename }) : (0, import_react8.createElement)(AttachmentPreviewText, { url: previewUrl })
|
|
2128
2184
|
)
|
|
2129
|
-
)
|
|
2130
|
-
hidden > 0 ? (0, import_react8.createElement)(
|
|
2131
|
-
"button",
|
|
2132
|
-
{ className: "subtle small", onClick: () => setExpanded(true) },
|
|
2133
|
-
`+${String(hidden)} \u4E2A\u9644\u4EF6`
|
|
2134
|
-
) : detail.attachments.length > 3 ? (0, import_react8.createElement)("button", { className: "subtle small", onClick: () => setExpanded(false) }, "\u6536\u8D77\u9644\u4EF6") : null
|
|
2185
|
+
)
|
|
2135
2186
|
);
|
|
2136
2187
|
}
|
|
2137
2188
|
function Reader({
|
|
@@ -2158,6 +2209,7 @@ function Reader({
|
|
|
2158
2209
|
const [details, setDetails] = (0, import_react8.useState)({});
|
|
2159
2210
|
const [openId, setOpenId] = (0, import_react8.useState)(detail.id);
|
|
2160
2211
|
const [detailOpen, setDetailOpen] = (0, import_react8.useState)(false);
|
|
2212
|
+
const [moreActions, setMoreActions] = (0, import_react8.useState)(false);
|
|
2161
2213
|
const selectedCard = (0, import_react8.useRef)(null);
|
|
2162
2214
|
const [busy, setBusy] = (0, import_react8.useState)(false);
|
|
2163
2215
|
const [error, setError] = (0, import_react8.useState)("");
|
|
@@ -2271,7 +2323,7 @@ function Reader({
|
|
|
2271
2323
|
),
|
|
2272
2324
|
(0, import_react8.createElement)(
|
|
2273
2325
|
"div",
|
|
2274
|
-
{ className: "mailharbor-reader-actions" },
|
|
2326
|
+
{ className: moreActions ? "mailharbor-reader-actions expanded" : "mailharbor-reader-actions" },
|
|
2275
2327
|
(0, import_react8.createElement)(
|
|
2276
2328
|
"button",
|
|
2277
2329
|
{ className: "ghost", onClick: () => onReply("reply") },
|
|
@@ -2290,22 +2342,33 @@ function Reader({
|
|
|
2290
2342
|
(0, import_react8.createElement)(Icon, { name: "forward" }),
|
|
2291
2343
|
"\u8F6C\u53D1"
|
|
2292
2344
|
),
|
|
2293
|
-
(0, import_react8.createElement)(
|
|
2345
|
+
(0, import_react8.createElement)(
|
|
2346
|
+
"button",
|
|
2347
|
+
{
|
|
2348
|
+
className: "ghost mailharbor-more-toggle",
|
|
2349
|
+
"aria-expanded": moreActions,
|
|
2350
|
+
onClick: () => setMoreActions(!moreActions)
|
|
2351
|
+
},
|
|
2352
|
+
moreActions ? "\u6536\u8D77" : "\u66F4\u591A",
|
|
2353
|
+
(0, import_react8.createElement)(Icon, { name: moreActions ? "chevronLeft" : "chevronRight", size: 12 })
|
|
2354
|
+
),
|
|
2355
|
+
(0, import_react8.createElement)("span", { className: "mailharbor-divider mailharbor-secondary" }),
|
|
2294
2356
|
onArchive === void 0 ? null : (0, import_react8.createElement)(
|
|
2295
2357
|
"button",
|
|
2296
|
-
{ className: "ghost", onClick: onArchive, title: "\u79FB\u5230\u5F52\u6863\u6587\u4EF6\u5939" },
|
|
2358
|
+
{ className: "ghost mailharbor-secondary", onClick: onArchive, title: "\u79FB\u5230\u5F52\u6863\u6587\u4EF6\u5939" },
|
|
2297
2359
|
(0, import_react8.createElement)(Icon, { name: "archive", size: 14 }),
|
|
2298
2360
|
"\u5F52\u6863"
|
|
2299
2361
|
),
|
|
2300
2362
|
onDelete === void 0 ? null : (0, import_react8.createElement)(
|
|
2301
2363
|
"button",
|
|
2302
|
-
{ className: "ghost", onClick: onDelete, title: "\u79FB\u5230\u5783\u573E\u7BB1" },
|
|
2364
|
+
{ className: "ghost mailharbor-secondary", onClick: onDelete, title: "\u79FB\u5230\u5783\u573E\u7BB1" },
|
|
2303
2365
|
(0, import_react8.createElement)(Icon, { name: "trash", size: 14 }),
|
|
2304
2366
|
"\u5220\u9664"
|
|
2305
2367
|
),
|
|
2306
2368
|
onMove === void 0 || folders.length === 0 ? null : (0, import_react8.createElement)(
|
|
2307
2369
|
"select",
|
|
2308
2370
|
{
|
|
2371
|
+
className: "mailharbor-secondary",
|
|
2309
2372
|
value: "",
|
|
2310
2373
|
"aria-label": "\u79FB\u52A8\u5230\u6587\u4EF6\u5939",
|
|
2311
2374
|
title: "\u79FB\u52A8\u5230\u6587\u4EF6\u5939",
|
|
@@ -2317,24 +2380,32 @@ function Reader({
|
|
|
2317
2380
|
(0, import_react8.createElement)("option", { value: "" }, "\u79FB\u52A8\u5230\u2026"),
|
|
2318
2381
|
folders.map((folder) => (0, import_react8.createElement)("option", { key: folder.name, value: folder.name }, folder.label))
|
|
2319
2382
|
),
|
|
2320
|
-
(0, import_react8.createElement)("span", { className: "mailharbor-divider" }),
|
|
2383
|
+
(0, import_react8.createElement)("span", { className: "mailharbor-divider mailharbor-secondary" }),
|
|
2321
2384
|
(0, import_react8.createElement)(
|
|
2322
2385
|
"button",
|
|
2323
|
-
{ className: "ghost", onClick: () => onAiAction("summary") },
|
|
2386
|
+
{ className: "ghost mailharbor-secondary", onClick: () => onAiAction("summary") },
|
|
2324
2387
|
(0, import_react8.createElement)(Icon, { name: "sparkles", size: 14 }),
|
|
2325
2388
|
"AI \u6458\u8981"
|
|
2326
2389
|
),
|
|
2327
|
-
(0, import_react8.createElement)("button", { className: "ghost", onClick: () => onAiAction("reply") }, "AI \u56DE\u590D"),
|
|
2328
|
-
(0, import_react8.createElement)("button", { className: "ghost", onClick: () => onAiAction("translate") }, "AI \u7FFB\u8BD1"),
|
|
2329
2390
|
(0, import_react8.createElement)(
|
|
2330
2391
|
"button",
|
|
2331
|
-
{ className: "ghost", onClick:
|
|
2392
|
+
{ className: "ghost mailharbor-secondary", onClick: () => onAiAction("reply") },
|
|
2393
|
+
"AI \u56DE\u590D"
|
|
2394
|
+
),
|
|
2395
|
+
(0, import_react8.createElement)(
|
|
2396
|
+
"button",
|
|
2397
|
+
{ className: "ghost mailharbor-secondary", onClick: () => onAiAction("translate") },
|
|
2398
|
+
"AI \u7FFB\u8BD1"
|
|
2399
|
+
),
|
|
2400
|
+
(0, import_react8.createElement)(
|
|
2401
|
+
"button",
|
|
2402
|
+
{ className: "ghost mailharbor-secondary", onClick: onExtractTasks },
|
|
2332
2403
|
(0, import_react8.createElement)(Icon, { name: "checklist", size: 14 }),
|
|
2333
2404
|
"\u63D0\u53D6\u4EFB\u52A1"
|
|
2334
2405
|
),
|
|
2335
2406
|
(0, import_react8.createElement)(
|
|
2336
2407
|
"button",
|
|
2337
|
-
{ className: "ghost", onClick: onAddToProject },
|
|
2408
|
+
{ className: "ghost mailharbor-secondary", onClick: onAddToProject },
|
|
2338
2409
|
(0, import_react8.createElement)(Icon, { name: "folder", size: 14 }),
|
|
2339
2410
|
"\u52A0\u5165\u9879\u76EE"
|
|
2340
2411
|
),
|
|
@@ -2546,13 +2617,15 @@ function MailPanel() {
|
|
|
2546
2617
|
const [selectMode, setSelectMode] = (0, import_react9.useState)(false);
|
|
2547
2618
|
const [sideOpen, setSideOpen] = (0, import_react9.useState)(() => {
|
|
2548
2619
|
try {
|
|
2549
|
-
|
|
2620
|
+
const wide = window.innerWidth > 1220;
|
|
2621
|
+
return wide && window.localStorage.getItem("mailharbor.side") !== "0";
|
|
2550
2622
|
} catch {
|
|
2551
2623
|
return true;
|
|
2552
2624
|
}
|
|
2553
2625
|
});
|
|
2554
2626
|
const [expandedGroups, setExpandedGroups] = (0, import_react9.useState)([]);
|
|
2555
2627
|
const [selectedKeys, setSelectedKeys] = (0, import_react9.useState)([]);
|
|
2628
|
+
const searchRef = (0, import_react9.useRef)(null);
|
|
2556
2629
|
const [projectOptions, setProjectOptions] = (0, import_react9.useState)([]);
|
|
2557
2630
|
const [targetProject, setTargetProject] = (0, import_react9.useState)("");
|
|
2558
2631
|
const [extraction, setExtraction] = (0, import_react9.useState)(null);
|
|
@@ -3027,6 +3100,26 @@ ${projectPicker.subject}`,
|
|
|
3027
3100
|
cancelled = true;
|
|
3028
3101
|
};
|
|
3029
3102
|
}, [accountScope, source, mailbox, search, limit, tick, unreadOnly]);
|
|
3103
|
+
(0, import_react9.useEffect)(() => {
|
|
3104
|
+
const handler = (event) => {
|
|
3105
|
+
const target = event.target;
|
|
3106
|
+
if (target?.closest('input, textarea, select, [contenteditable="true"]') !== null) return;
|
|
3107
|
+
if (event.metaKey || event.ctrlKey || event.altKey) return;
|
|
3108
|
+
if (event.key === "/" && !event.shiftKey) {
|
|
3109
|
+
event.preventDefault();
|
|
3110
|
+
searchRef.current?.focus();
|
|
3111
|
+
return;
|
|
3112
|
+
}
|
|
3113
|
+
if (event.key !== "Escape") return;
|
|
3114
|
+
if (document.querySelector(".mailharbor-modal") !== null) return;
|
|
3115
|
+
if (backupOpen) setBackupOpen(false);
|
|
3116
|
+
else if (railOpen) setRailOpen(false);
|
|
3117
|
+
else if (sideOpen) setSideOpen(false);
|
|
3118
|
+
else if (selected !== null) setSelected(null);
|
|
3119
|
+
};
|
|
3120
|
+
window.addEventListener("keydown", handler);
|
|
3121
|
+
return () => window.removeEventListener("keydown", handler);
|
|
3122
|
+
}, [backupOpen, railOpen, sideOpen, selected]);
|
|
3030
3123
|
(0, import_react9.useEffect)(() => {
|
|
3031
3124
|
if (typeof window.matchMedia !== "function") return;
|
|
3032
3125
|
const media = window.matchMedia("(max-width: 900px)");
|
|
@@ -3036,6 +3129,22 @@ ${projectPicker.subject}`,
|
|
|
3036
3129
|
media.removeEventListener("change", onChange);
|
|
3037
3130
|
};
|
|
3038
3131
|
}, []);
|
|
3132
|
+
(0, import_react9.useEffect)(() => {
|
|
3133
|
+
if (typeof window.matchMedia !== "function") return;
|
|
3134
|
+
const media = window.matchMedia("(max-width: 1220px)");
|
|
3135
|
+
const onChange = () => {
|
|
3136
|
+
if (media.matches) setSideOpen(false);
|
|
3137
|
+
};
|
|
3138
|
+
media.addEventListener("change", onChange);
|
|
3139
|
+
return () => {
|
|
3140
|
+
media.removeEventListener("change", onChange);
|
|
3141
|
+
};
|
|
3142
|
+
}, []);
|
|
3143
|
+
const closeDrawer = () => {
|
|
3144
|
+
if (typeof window.matchMedia === "function" && window.matchMedia("(max-width: 1220px)").matches) {
|
|
3145
|
+
setSideOpen(false);
|
|
3146
|
+
}
|
|
3147
|
+
};
|
|
3039
3148
|
(0, import_react9.useEffect)(() => {
|
|
3040
3149
|
if (accountScope === null || isAll) return;
|
|
3041
3150
|
let cancelled = false;
|
|
@@ -3368,13 +3477,13 @@ ${projectPicker.subject}`,
|
|
|
3368
3477
|
"div",
|
|
3369
3478
|
{ className: "mailharbor-onboarding" },
|
|
3370
3479
|
(0, import_react9.createElement)("div", null, (0, import_react9.createElement)("strong", null, "1"), "\u6DFB\u52A0\u90AE\u7BB1\u8D26\u53F7"),
|
|
3371
|
-
(0, import_react9.createElement)("div", null, (0, import_react9.createElement)("strong", null, "2"), "\u767B\u5F55\uFF08\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801
|
|
3480
|
+
(0, import_react9.createElement)("div", null, (0, import_react9.createElement)("strong", null, "2"), "\u767B\u5F55\uFF08\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801\uFF09"),
|
|
3372
3481
|
(0, import_react9.createElement)("div", null, (0, import_react9.createElement)("strong", null, "3"), "\u81EA\u52A8\u540C\u6B65\u540E\u4F1A\u7ACB\u5373\u51FA\u73B0\u5728\u5217\u8868\u548C AI \u52A9\u624B\u91CC")
|
|
3373
3482
|
),
|
|
3374
3483
|
(0, import_react9.createElement)(
|
|
3375
3484
|
"p",
|
|
3376
3485
|
{ className: "small muted" },
|
|
3377
|
-
"Gmail
|
|
3486
|
+
"Gmail\u3001Outlook\u3001QQ\u3001163 \u7B49\u8BF7\u4F7F\u7528 IMAP \u5E94\u7528\u4E13\u7528\u5BC6\u7801\uFF1B\u90AE\u7BB1\u670D\u52A1\u5668\u5730\u5740\u53EF\u5728\u9AD8\u7EA7\u8BBE\u7F6E\u91CC\u6539\u3002"
|
|
3378
3487
|
),
|
|
3379
3488
|
error === "" ? null : (0, import_react9.createElement)("div", { className: "small", style: { color: "var(--m-danger)" } }, error),
|
|
3380
3489
|
(0, import_react9.createElement)(AddAccountForm, {
|
|
@@ -3580,7 +3689,7 @@ ${projectPicker.subject}`,
|
|
|
3580
3689
|
"button",
|
|
3581
3690
|
{ className: "ghost", onClick: openManager, title: "\u9879\u76EE\u7BA1\u7406" },
|
|
3582
3691
|
(0, import_react9.createElement)(Icon, { name: "folder" }),
|
|
3583
|
-
"\u9879\u76EE"
|
|
3692
|
+
(0, import_react9.createElement)("span", { className: "mailharbor-action-label" }, "\u9879\u76EE")
|
|
3584
3693
|
),
|
|
3585
3694
|
(0, import_react9.createElement)(
|
|
3586
3695
|
"button",
|
|
@@ -3590,13 +3699,13 @@ ${projectPicker.subject}`,
|
|
|
3590
3699
|
title: "AI \u5DE5\u4F5C\u53F0"
|
|
3591
3700
|
},
|
|
3592
3701
|
(0, import_react9.createElement)(Icon, { name: "sparkles" }),
|
|
3593
|
-
"AI"
|
|
3702
|
+
(0, import_react9.createElement)("span", { className: "mailharbor-action-label" }, "AI")
|
|
3594
3703
|
),
|
|
3595
3704
|
(0, import_react9.createElement)(
|
|
3596
3705
|
"button",
|
|
3597
3706
|
{ className: "primary", onClick: () => startCompose("new") },
|
|
3598
3707
|
(0, import_react9.createElement)(Icon, { name: "compose" }),
|
|
3599
|
-
"\u5199\u90AE\u4EF6"
|
|
3708
|
+
(0, import_react9.createElement)("span", { className: "mailharbor-action-label" }, "\u5199\u90AE\u4EF6")
|
|
3600
3709
|
)
|
|
3601
3710
|
),
|
|
3602
3711
|
toast === "" ? null : (0, import_react9.createElement)("div", { className: "small", style: { padding: "6px 14px", color: "var(--m-ok)" } }, toast),
|
|
@@ -3617,6 +3726,11 @@ ${projectPicker.subject}`,
|
|
|
3617
3726
|
) : (0, import_react9.createElement)(
|
|
3618
3727
|
"div",
|
|
3619
3728
|
{ className: "mailharbor-body", "data-mode": narrow && selected !== null ? "reader" : "list" },
|
|
3729
|
+
sideOpen ? (0, import_react9.createElement)("button", {
|
|
3730
|
+
className: "mailharbor-scrim",
|
|
3731
|
+
"aria-label": "\u5173\u95ED\u8D26\u53F7\u4E0E\u6587\u4EF6\u5939",
|
|
3732
|
+
onClick: () => setSideOpen(false)
|
|
3733
|
+
}) : null,
|
|
3620
3734
|
sideOpen ? (0, import_react9.createElement)(
|
|
3621
3735
|
"aside",
|
|
3622
3736
|
{ className: "mailharbor-side" },
|
|
@@ -3630,6 +3744,7 @@ ${projectPicker.subject}`,
|
|
|
3630
3744
|
onClick: () => {
|
|
3631
3745
|
void switchAccount(item.id);
|
|
3632
3746
|
setMailbox("INBOX");
|
|
3747
|
+
closeDrawer();
|
|
3633
3748
|
}
|
|
3634
3749
|
},
|
|
3635
3750
|
(0, import_react9.createElement)(Avatar, { email: item.email, size: 18 }),
|
|
@@ -3656,6 +3771,7 @@ ${projectPicker.subject}`,
|
|
|
3656
3771
|
onClick: () => {
|
|
3657
3772
|
setMailbox(folder.name);
|
|
3658
3773
|
setSelected(null);
|
|
3774
|
+
closeDrawer();
|
|
3659
3775
|
},
|
|
3660
3776
|
onMouseEnter: () => prefetchFolder(folder.name)
|
|
3661
3777
|
},
|
|
@@ -3673,6 +3789,7 @@ ${projectPicker.subject}`,
|
|
|
3673
3789
|
{ className: "mailharbor-filters" },
|
|
3674
3790
|
(0, import_react9.createElement)("input", {
|
|
3675
3791
|
type: "search",
|
|
3792
|
+
ref: searchRef,
|
|
3676
3793
|
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",
|
|
3677
3794
|
"aria-label": "\u641C\u7D22\u90AE\u4EF6",
|
|
3678
3795
|
value: query2,
|
|
@@ -3904,7 +4021,19 @@ ${projectPicker.subject}`,
|
|
|
3904
4021
|
"div",
|
|
3905
4022
|
{ className: "small" },
|
|
3906
4023
|
aiInfo === null ? source === "local" ? "\u70B9\u53F3\u4E0A\u89D2\u300C\u540C\u6B65\u300D\u628A\u670D\u52A1\u5668\u90AE\u4EF6\u62C9\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"
|
|
3907
|
-
)
|
|
4024
|
+
),
|
|
4025
|
+
search !== "" || aiInfo !== null ? (0, import_react9.createElement)(
|
|
4026
|
+
"button",
|
|
4027
|
+
{
|
|
4028
|
+
className: "ghost small",
|
|
4029
|
+
onClick: () => {
|
|
4030
|
+
setQuery("");
|
|
4031
|
+
setSearch("");
|
|
4032
|
+
setAiInfo(null);
|
|
4033
|
+
}
|
|
4034
|
+
},
|
|
4035
|
+
"\u6E05\u9664\u641C\u7D22"
|
|
4036
|
+
) : null
|
|
3908
4037
|
) : selectMode ? items.map((item) => renderMessage(item)) : groups.map((group) => {
|
|
3909
4038
|
const latest = group.items[0];
|
|
3910
4039
|
if (latest === void 0) return null;
|
|
@@ -3990,6 +4119,11 @@ ${projectPicker.subject}`,
|
|
|
3990
4119
|
onMove: (target) => void moveCurrent(target, "\u5DF2\u79FB\u52A8")
|
|
3991
4120
|
})
|
|
3992
4121
|
),
|
|
4122
|
+
railOpen ? (0, import_react9.createElement)("button", {
|
|
4123
|
+
className: "mailharbor-ai-scrim",
|
|
4124
|
+
"aria-label": "\u5173\u95ED AI \u52A9\u624B",
|
|
4125
|
+
onClick: () => setRailOpen(false)
|
|
4126
|
+
}) : null,
|
|
3993
4127
|
railOpen ? (0, import_react9.createElement)(AiRail, {
|
|
3994
4128
|
account: accounts.find((item) => item.id === (selected?.accountId ?? accountScope)) ?? currentAccount,
|
|
3995
4129
|
detail: selected,
|
|
@@ -4343,9 +4477,6 @@ function MailSettings() {
|
|
|
4343
4477
|
const [checkResults, setCheckResults] = (0, import_react10.useState)({});
|
|
4344
4478
|
const [adding, setAdding] = (0, import_react10.useState)(false);
|
|
4345
4479
|
const [tick, setTick] = (0, import_react10.useState)(0);
|
|
4346
|
-
const [googleClientId, setGoogleClientId] = (0, import_react10.useState)("");
|
|
4347
|
-
const [googleClientSecret, setGoogleClientSecret] = (0, import_react10.useState)("");
|
|
4348
|
-
const [microsoftClientId, setMicrosoftClientId] = (0, import_react10.useState)("");
|
|
4349
4480
|
(0, import_react10.useEffect)(() => {
|
|
4350
4481
|
let cancelled = false;
|
|
4351
4482
|
Promise.all([api.state(), api.settings()]).then(([appState, view]) => {
|
|
@@ -4361,9 +4492,6 @@ function MailSettings() {
|
|
|
4361
4492
|
setAiProvider(view.ai.provider);
|
|
4362
4493
|
setAiModel(view.ai.model);
|
|
4363
4494
|
setAiSummaryLength(view.ai.summaryLength);
|
|
4364
|
-
setGoogleClientId(view.oauth?.google.clientId ?? "");
|
|
4365
|
-
setGoogleClientSecret(view.oauth?.google.clientSecret ?? "");
|
|
4366
|
-
setMicrosoftClientId(view.oauth?.microsoft.clientId ?? "");
|
|
4367
4495
|
}).catch((caught) => {
|
|
4368
4496
|
if (!cancelled) setError(errorText6(caught));
|
|
4369
4497
|
});
|
|
@@ -4509,72 +4637,11 @@ function MailSettings() {
|
|
|
4509
4637
|
onCancel: () => setAdding(false)
|
|
4510
4638
|
}) : (0, import_react10.createElement)("button", { className: "ghost", onClick: () => setAdding(true) }, "+ \u6DFB\u52A0\u8D26\u53F7")
|
|
4511
4639
|
),
|
|
4512
|
-
(0, import_react10.createElement)(
|
|
4513
|
-
"div",
|
|
4514
|
-
{ className: "mailharbor-card" },
|
|
4515
|
-
(0, import_react10.createElement)("h3", null, "OAuth2 \u5E94\u7528\uFF08Gmail / Microsoft 365\uFF09"),
|
|
4516
|
-
(0, import_react10.createElement)(
|
|
4517
|
-
"p",
|
|
4518
|
-
{ className: "small muted" },
|
|
4519
|
-
"\u5728 Google Cloud Console \u521B\u5EFA\u300C\u684C\u9762\u5E94\u7528\u300DOAuth \u5BA2\u6237\u7AEF\uFF0C\u6216\u5728 Azure \u6CE8\u518C\u5E94\u7528\u5E76\u5141\u8BB8\u516C\u5171\u5BA2\u6237\u7AEF\u8BBE\u5907\u4EE3\u7801\u6D41\uFF1B\u628A Client ID \u586B\u5230\u8FD9\u91CC\u3002"
|
|
4520
|
-
),
|
|
4521
|
-
(0, import_react10.createElement)(
|
|
4522
|
-
"div",
|
|
4523
|
-
{ className: "mailharbor-grid" },
|
|
4524
|
-
(0, import_react10.createElement)(
|
|
4525
|
-
Field,
|
|
4526
|
-
{ label: "Google Client ID" },
|
|
4527
|
-
(0, import_react10.createElement)("input", {
|
|
4528
|
-
value: googleClientId,
|
|
4529
|
-
placeholder: "xxxx.apps.googleusercontent.com",
|
|
4530
|
-
onChange: (event) => setGoogleClientId(event.target.value)
|
|
4531
|
-
})
|
|
4532
|
-
),
|
|
4533
|
-
(0, import_react10.createElement)(
|
|
4534
|
-
Field,
|
|
4535
|
-
{ label: "Google Client Secret" },
|
|
4536
|
-
(0, import_react10.createElement)("input", {
|
|
4537
|
-
type: "password",
|
|
4538
|
-
value: googleClientSecret,
|
|
4539
|
-
onChange: (event) => setGoogleClientSecret(event.target.value)
|
|
4540
|
-
})
|
|
4541
|
-
)
|
|
4542
|
-
),
|
|
4543
|
-
(0, import_react10.createElement)(
|
|
4544
|
-
Field,
|
|
4545
|
-
{ label: "Microsoft Client ID" },
|
|
4546
|
-
(0, import_react10.createElement)("input", {
|
|
4547
|
-
value: microsoftClientId,
|
|
4548
|
-
onChange: (event) => setMicrosoftClientId(event.target.value)
|
|
4549
|
-
})
|
|
4550
|
-
),
|
|
4551
|
-
(0, import_react10.createElement)(
|
|
4552
|
-
"button",
|
|
4553
|
-
{
|
|
4554
|
-
className: "ghost",
|
|
4555
|
-
onClick: () => void saveGeneral({
|
|
4556
|
-
oauth: {
|
|
4557
|
-
google: {
|
|
4558
|
-
clientId: googleClientId.trim(),
|
|
4559
|
-
clientSecret: googleClientSecret.trim(),
|
|
4560
|
-
redirectPort: settings?.oauth?.google.redirectPort ?? 8765
|
|
4561
|
-
},
|
|
4562
|
-
microsoft: { clientId: microsoftClientId.trim() }
|
|
4563
|
-
}
|
|
4564
|
-
})
|
|
4565
|
-
},
|
|
4566
|
-
"\u4FDD\u5B58 OAuth \u5E94\u7528"
|
|
4567
|
-
)
|
|
4568
|
-
),
|
|
4569
4640
|
(0, import_react10.createElement)(
|
|
4570
4641
|
"div",
|
|
4571
4642
|
{ className: "mailharbor-card" },
|
|
4572
4643
|
(0, import_react10.createElement)("h3", null, "\u5907\u4EFD\u4E0E\u8FC1\u79FB"),
|
|
4573
|
-
(0, import_react10.createElement)(
|
|
4574
|
-
"p",
|
|
4575
|
-
{ className: "small muted" },
|
|
4576
|
-
"\u5BFC\u51FA\u8D26\u53F7\uFF08\u4E0D\u542B\u5BC6\u7801\u548C OAuth \u4EE4\u724C\uFF09\u3001\u7B7E\u540D\u3001\u9605\u8BFB\u4E0E OAuth \u5E94\u7528\u914D\u7F6E\uFF1B\u5BFC\u5165\u65F6\u6309\u90AE\u7BB1\u53BB\u91CD\u3002"
|
|
4577
|
-
),
|
|
4644
|
+
(0, import_react10.createElement)("p", { className: "small muted" }, "\u5BFC\u51FA\u8D26\u53F7\uFF08\u4E0D\u542B\u5BC6\u7801\uFF09\u3001\u7B7E\u540D\u4E0E\u9605\u8BFB\u8BBE\u7F6E\uFF1B\u5BFC\u5165\u65F6\u6309\u90AE\u7BB1\u53BB\u91CD\u3002"),
|
|
4578
4645
|
(0, import_react10.createElement)(
|
|
4579
4646
|
"div",
|
|
4580
4647
|
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
@@ -4596,10 +4663,6 @@ function MailSettings() {
|
|
|
4596
4663
|
)
|
|
4597
4664
|
)
|
|
4598
4665
|
),
|
|
4599
|
-
state.activeAccountId !== null && settings.accounts.find((item) => item.id === state.activeAccountId)?.auth.type === "oauth2" ? (0, import_react10.createElement)(OAuthCard, {
|
|
4600
|
-
account: settings.accounts.find((item) => item.id === state.activeAccountId),
|
|
4601
|
-
onChanged: reload
|
|
4602
|
-
}) : null,
|
|
4603
4666
|
(0, import_react10.createElement)(
|
|
4604
4667
|
"div",
|
|
4605
4668
|
{ className: "mailharbor-card" },
|
|
@@ -5121,99 +5184,6 @@ function AccountRow({
|
|
|
5121
5184
|
) : null
|
|
5122
5185
|
);
|
|
5123
5186
|
}
|
|
5124
|
-
function OAuthCard({ account, onChanged }) {
|
|
5125
|
-
const [status, setStatus] = (0, import_react10.useState)(null);
|
|
5126
|
-
const [flow, setFlow] = (0, import_react10.useState)(null);
|
|
5127
|
-
const [message, setMessage] = (0, import_react10.useState)("");
|
|
5128
|
-
const [busy, setBusy] = (0, import_react10.useState)(false);
|
|
5129
|
-
(0, import_react10.useEffect)(() => {
|
|
5130
|
-
let cancelled = false;
|
|
5131
|
-
api.oauthStatus(account.id).then((result) => {
|
|
5132
|
-
if (!cancelled) setStatus(result);
|
|
5133
|
-
}).catch(() => void 0);
|
|
5134
|
-
return () => {
|
|
5135
|
-
cancelled = true;
|
|
5136
|
-
};
|
|
5137
|
-
}, [account.id]);
|
|
5138
|
-
(0, import_react10.useEffect)(() => {
|
|
5139
|
-
if (flow === null) return;
|
|
5140
|
-
const timer = window.setInterval(() => {
|
|
5141
|
-
void api.oauthPoll(flow.state).then((result) => {
|
|
5142
|
-
if (result.status === "ready") {
|
|
5143
|
-
setFlow(null);
|
|
5144
|
-
setMessage("\u6388\u6743\u6210\u529F");
|
|
5145
|
-
onChanged();
|
|
5146
|
-
} else if (result.status === "error") {
|
|
5147
|
-
setFlow(null);
|
|
5148
|
-
setMessage(result.message ?? "\u6388\u6743\u5931\u8D25");
|
|
5149
|
-
}
|
|
5150
|
-
}).catch((caught) => {
|
|
5151
|
-
setFlow(null);
|
|
5152
|
-
setMessage(errorText6(caught));
|
|
5153
|
-
});
|
|
5154
|
-
}, 2500);
|
|
5155
|
-
return () => window.clearInterval(timer);
|
|
5156
|
-
}, [flow, onChanged]);
|
|
5157
|
-
const start = async () => {
|
|
5158
|
-
setBusy(true);
|
|
5159
|
-
setMessage("");
|
|
5160
|
-
try {
|
|
5161
|
-
const result = await api.oauthStart(account.id);
|
|
5162
|
-
setFlow(result);
|
|
5163
|
-
if (result.flow === "microsoft") setMessage("\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u8F93\u5165\u4E0B\u65B9\u4EE3\u7801\u5B8C\u6210\u767B\u5F55");
|
|
5164
|
-
else setMessage("\u5DF2\u5728\u6D4F\u89C8\u5668\u6253\u5F00\u6388\u6743\u9875\uFF0C\u5B8C\u6210\u540E\u4F1A\u81EA\u52A8\u66F4\u65B0");
|
|
5165
|
-
} catch (caught) {
|
|
5166
|
-
setMessage(errorText6(caught));
|
|
5167
|
-
} finally {
|
|
5168
|
-
setBusy(false);
|
|
5169
|
-
}
|
|
5170
|
-
};
|
|
5171
|
-
const revoke = async () => {
|
|
5172
|
-
setBusy(true);
|
|
5173
|
-
try {
|
|
5174
|
-
await api.oauthRevoke(account.id);
|
|
5175
|
-
setStatus({ ...status ?? { appConfigured: true, expiresAt: null }, authorized: false });
|
|
5176
|
-
setMessage("\u5DF2\u64A4\u9500\u6388\u6743");
|
|
5177
|
-
onChanged();
|
|
5178
|
-
} catch (caught) {
|
|
5179
|
-
setMessage(errorText6(caught));
|
|
5180
|
-
} finally {
|
|
5181
|
-
setBusy(false);
|
|
5182
|
-
}
|
|
5183
|
-
};
|
|
5184
|
-
return (0, import_react10.createElement)(
|
|
5185
|
-
"div",
|
|
5186
|
-
{ className: "mailharbor-card" },
|
|
5187
|
-
(0, import_react10.createElement)("h3", null, `OAuth \u6388\u6743 \xB7 ${account.email}`),
|
|
5188
|
-
(0, import_react10.createElement)(
|
|
5189
|
-
"p",
|
|
5190
|
-
{ className: "small muted" },
|
|
5191
|
-
status?.authorized === true ? `\u5DF2\u6388\u6743${status.expiresAt === null ? "" : `\uFF0C\u8BBF\u95EE\u4EE4\u724C\u6709\u6548\u81F3 ${new Date(status.expiresAt).toLocaleString()}`}` : status?.appConfigured === false ? "\u5C1A\u672A\u914D\u7F6E\u5BF9\u5E94\u670D\u52A1\u5546\u7684 Client ID" : "\u672A\u6388\u6743"
|
|
5192
|
-
),
|
|
5193
|
-
flow?.userCode === void 0 ? null : (0, import_react10.createElement)(
|
|
5194
|
-
"p",
|
|
5195
|
-
null,
|
|
5196
|
-
"\u4EE3\u7801\uFF1A",
|
|
5197
|
-
(0, import_react10.createElement)("strong", null, flow.userCode),
|
|
5198
|
-
flow.verificationUri === void 0 ? null : (0, import_react10.createElement)("span", null, ` \xB7 ${flow.verificationUri}`)
|
|
5199
|
-
),
|
|
5200
|
-
message === "" ? null : (0, import_react10.createElement)("p", { className: "small" }, message),
|
|
5201
|
-
(0, import_react10.createElement)(
|
|
5202
|
-
"div",
|
|
5203
|
-
{ style: { display: "flex", gap: "8px", flexWrap: "wrap" } },
|
|
5204
|
-
(0, import_react10.createElement)(
|
|
5205
|
-
"button",
|
|
5206
|
-
{ className: "primary small", disabled: busy || flow !== null, onClick: () => void start() },
|
|
5207
|
-
status?.authorized === true ? "\u91CD\u65B0\u6388\u6743" : "OAuth \u6388\u6743"
|
|
5208
|
-
),
|
|
5209
|
-
status?.authorized === true ? (0, import_react10.createElement)(
|
|
5210
|
-
"button",
|
|
5211
|
-
{ className: "ghost small danger", disabled: busy, onClick: () => void revoke() },
|
|
5212
|
-
"\u64A4\u9500\u6388\u6743"
|
|
5213
|
-
) : null
|
|
5214
|
-
)
|
|
5215
|
-
);
|
|
5216
|
-
}
|
|
5217
5187
|
|
|
5218
5188
|
// src/client/tasks-panel.tsx
|
|
5219
5189
|
var import_react11 = require("react");
|
|
@@ -5913,7 +5883,7 @@ function TaskEditor({
|
|
|
5913
5883
|
}
|
|
5914
5884
|
|
|
5915
5885
|
// src/client/index.tsx
|
|
5916
|
-
var VERSION = "0.
|
|
5886
|
+
var VERSION = "0.16.1";
|
|
5917
5887
|
var inject = ["slots"];
|
|
5918
5888
|
var PANEL_ID = "mailharbor";
|
|
5919
5889
|
var TASKS_PANEL_ID = "mailharbor-tasks";
|