@zaclinley/dsh-mailharbor 0.6.0 → 0.7.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/lib/client.js CHANGED
@@ -35,7 +35,7 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/client/styles.css
38
- var styles_default = "/* MailHarbor panel + settings.\n Tokens come from the DSH shell (dark by default); fallbacks match the\n default dark theme so the panel also renders in a bare host page.\n Component rules are all scoped under `.mailharbor` and the generic element\n reset lives in `:where()` so it cannot outrank a component class. */\n\n.mailharbor {\n --m-bg: var(--dsw-alias-bg-base, #151517);\n --m-bg-1: var(--dsw-alias-bg-layer-1, #232324);\n --m-bg-2: var(--dsw-alias-bg-layer-2, #2c2c2e);\n --m-bg-3: var(--dsw-alias-bg-layer-3, #353638);\n --m-line: var(--dsw-alias-border-l2, #ffffff1f);\n --m-line-soft: var(--dsw-alias-border-l1, #ffffff0f);\n --m-line-strong: var(--dsw-alias-border-l3, #ffffff29);\n --m-text: var(--dsw-alias-label-primary, #f9fafb);\n --m-text-2: var(--dsw-alias-label-secondary, #cfd3d6);\n --m-text-3: var(--dsw-alias-label-tertiary, #adb2b8);\n --m-caption: var(--dsw-alias-label-caption, #81858c);\n --m-accent: #5686fe;\n --m-accent-soft: rgba(86, 134, 254, 0.14);\n --m-primary-bg: var(--dsw-alias-brand-primary, #f9fafb);\n --m-primary-fg: var(--dsw-alias-label-primary-foreground, #0f1115);\n --m-hover: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n --m-danger: #f25a5a;\n --m-ok: #3fbf7f;\n --m-warn: var(--dsw-alias-state-warn-label, #dd8629);\n --m-radius: 8px;\n --m-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);\n\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--m-bg);\n color: var(--m-text);\n font-size: 13px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n\n.mailharbor *,\n.mailharbor *::before,\n.mailharbor *::after {\n box-sizing: border-box;\n}\n\n/* ---------- base controls ---------- */\n\n.mailharbor :where(button) {\n font: inherit;\n color: inherit;\n background: transparent;\n border: 0;\n margin: 0;\n cursor: pointer;\n}\n\n.mailharbor button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 30px;\n padding: 0 12px;\n border-radius: var(--m-radius);\n border: 1px solid transparent;\n color: var(--m-text-2);\n font-size: 12.5px;\n font-weight: 500;\n white-space: nowrap;\n transition:\n background 120ms ease,\n color 120ms ease,\n border-color 120ms ease;\n}\n\n.mailharbor button:hover:not(:disabled) {\n background: var(--m-hover);\n color: var(--m-text);\n}\n\n.mailharbor button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n\n.mailharbor button.primary {\n background: var(--m-primary-bg);\n color: var(--m-primary-fg);\n border-color: transparent;\n}\n\n.mailharbor button.primary:hover:not(:disabled) {\n background: var(--m-primary-bg);\n filter: brightness(0.94);\n color: var(--m-primary-fg);\n}\n\n.mailharbor button.ghost {\n border-color: var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor button.ghost:hover:not(:disabled) {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor button.subtle {\n color: var(--m-text-3);\n padding: 0 8px;\n}\n\n.mailharbor button.icon {\n width: 30px;\n padding: 0;\n}\n\n.mailharbor button.danger {\n color: var(--m-danger);\n}\n\n.mailharbor input,\n.mailharbor select,\n.mailharbor textarea {\n font: inherit;\n color: var(--m-text);\n background: var(--m-bg-1);\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n padding: 0 10px;\n height: 32px;\n min-width: 0;\n}\n\n.mailharbor textarea {\n height: auto;\n padding: 8px 10px;\n resize: vertical;\n line-height: 1.6;\n}\n\n.mailharbor select {\n padding-right: 26px;\n appearance: none;\n background-image:\n linear-gradient(45deg, transparent 50%, var(--m-text-3) 50%),\n linear-gradient(135deg, var(--m-text-3) 50%, transparent 50%);\n background-position:\n calc(100% - 15px) 14px,\n calc(100% - 10px) 14px;\n background-size:\n 5px 5px,\n 5px 5px;\n background-repeat: no-repeat;\n}\n\n.mailharbor ::placeholder {\n color: var(--m-caption);\n}\n\n.mailharbor input:focus-visible,\n.mailharbor select:focus-visible,\n.mailharbor textarea:focus-visible,\n.mailharbor button:focus-visible,\n.mailharbor [contenteditable='true']:focus-visible {\n outline: 2px solid rgba(86, 134, 254, 0.55);\n outline-offset: -1px;\n}\n\n.mailharbor .spacer {\n flex: 1;\n}\n\n.mailharbor .muted {\n color: var(--m-text-3);\n}\n\n.mailharbor .small {\n font-size: 12px;\n}\n\n.mailharbor .badge {\n display: inline-flex;\n align-items: center;\n height: 18px;\n padding: 0 6px;\n border-radius: 999px;\n font-size: 10.5px;\n font-weight: 500;\n background: var(--m-bg-3);\n color: var(--m-text-2);\n}\n\n.mailharbor .badge.ok {\n background: rgba(63, 191, 127, 0.16);\n color: var(--m-ok);\n}\n\n.mailharbor .badge.warn {\n background: rgba(221, 134, 41, 0.16);\n color: var(--m-warn);\n}\n\n.mailharbor .error {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.1);\n border-bottom: 1px solid rgba(242, 90, 90, 0.2);\n font-size: 12.5px;\n}\n\n/* ---------- shell ---------- */\n\n.mailharbor-topbar {\n display: flex;\n align-items: center;\n gap: 10px;\n height: 52px;\n padding: 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n flex: none;\n}\n\n.mailharbor-account {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n max-width: 220px;\n padding: 4px 8px 4px 4px;\n border-radius: 999px;\n border: 1px solid transparent;\n cursor: default;\n}\n\n.mailharbor-account:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-account .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.mailharbor-account select {\n height: 26px;\n background: transparent;\n border-color: transparent;\n font-weight: 500;\n}\n\n.mailharbor-body {\n display: flex;\n flex: 1;\n min-height: 0;\n position: relative;\n}\n\n.mailharbor button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-list {\n display: flex;\n flex-direction: column;\n width: 360px;\n flex: none;\n border-right: 1px solid var(--m-line-soft);\n min-height: 0;\n}\n\n.mailharbor-reader {\n flex: 1;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n@media (max-width: 1000px) {\n .mailharbor-list {\n width: 300px;\n }\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='reader'] .mailharbor-list {\n display: none;\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='list'] .mailharbor-reader {\n display: none;\n}\n\n/* ---------- segmented control ---------- */\n\n.mailharbor-segment {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px;\n border-radius: 9px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-segment button {\n height: 26px;\n padding: 0 12px;\n border-radius: 7px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-segment button:hover:not(.active) {\n color: var(--m-text-2);\n background: transparent;\n}\n\n.mailharbor-segment button.active {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n/* ---------- list ---------- */\n\n.mailharbor-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-filters select {\n width: 128px;\n flex: none;\n font-size: 12px;\n}\n\n.mailharbor-filters input[type='search'] {\n flex: 1;\n height: 30px;\n font-size: 12.5px;\n}\n\n.mailharbor-rows {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n.mailharbor-row {\n display: flex;\n flex-direction: column;\n gap: 3px;\n width: 100%;\n padding: 10px 14px;\n border: 0;\n border-bottom: 1px solid var(--m-line-soft);\n border-left: 2px solid transparent;\n border-radius: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n user-select: none;\n}\n\n.mailharbor-row:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-row.selected {\n background: var(--m-accent-soft);\n border-left-color: var(--m-accent);\n}\n\n.mailharbor-row .line-1,\n.mailharbor-row .line-2 {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n width: 100%;\n}\n\n.mailharbor-row .from {\n font-weight: 600;\n font-size: 12.5px;\n color: var(--m-text-2);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-row .date {\n margin-left: auto;\n color: var(--m-caption);\n font-size: 11px;\n flex: none;\n}\n\n.mailharbor-row .subject {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n.mailharbor-row .snippet {\n color: var(--m-caption);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n width: 100%;\n}\n\n.mailharbor-row.unread .from,\n.mailharbor-row.unread .subject {\n color: var(--m-text);\n}\n\n.mailharbor-row .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--m-accent);\n flex: none;\n}\n\n.mailharbor-row .paperclip {\n color: var(--m-caption);\n flex: none;\n display: inline-flex;\n}\n\n.mailharbor-list-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-top: 1px solid var(--m-line-soft);\n color: var(--m-text-3);\n font-size: 11.5px;\n flex: none;\n}\n\n.mailharbor-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 48px 24px;\n color: var(--m-text-3);\n text-align: center;\n}\n\n.mailharbor-empty svg {\n opacity: 0.5;\n}\n\n.mailharbor-empty .title {\n color: var(--m-text-2);\n font-size: 13px;\n font-weight: 500;\n}\n\n/* ---------- reader ---------- */\n\n.mailharbor-reader-head {\n padding: 16px 20px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mailharbor-reader-title {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mailharbor-reader-title h2 {\n margin: 0;\n font-size: 17px;\n line-height: 1.35;\n font-weight: 600;\n letter-spacing: -0.01em;\n word-break: break-word;\n}\n\n.mailharbor-reader-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 10px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-reader-meta .who {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: var(--m-text-2);\n}\n\n.mailharbor-reader-meta .who strong {\n color: var(--m-text);\n font-weight: 600;\n}\n\n.mailharbor-reader-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mailharbor-reader-body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 14px 18px 18px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-reader-body iframe {\n flex: 1;\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.mailharbor-reader-body pre {\n flex: 1;\n overflow: auto;\n margin: 0;\n padding: 20px 22px;\n white-space: pre-wrap;\n word-break: break-word;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n color: #171a21;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12.5px;\n line-height: 1.7;\n}\n\n.mailharbor-attachments {\n padding: 10px 18px;\n border-top: 1px solid var(--m-line-soft);\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex: none;\n background: var(--m-bg);\n}\n\n.mailharbor-attachment {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n font-size: 12px;\n text-decoration: none;\n color: var(--m-text-2);\n max-width: 260px;\n background: var(--m-bg-1);\n}\n\n.mailharbor-attachment:hover {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor-attachment .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ---------- AI rail ---------- */\n\n.mailharbor-ai {\n display: flex;\n flex-direction: column;\n width: 340px;\n flex: none;\n min-height: 0;\n border-left: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-ai-head {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 46px;\n padding: 0 8px 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-head strong {\n font-size: 13px;\n font-weight: 600;\n}\n\n.mailharbor-ai-confirm {\n display: inline-flex;\n gap: 2px;\n}\n\n.mailharbor-ai-context {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 9px 14px 10px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-context strong {\n font-size: 12.5px;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-ai-warn {\n padding: 8px 14px;\n color: var(--m-warn);\n background: rgba(221, 134, 41, 0.1);\n font-size: 12px;\n flex: none;\n}\n\n.mailharbor-ai-quick {\n display: flex;\n gap: 6px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-quick button {\n flex: 1;\n padding: 0 6px;\n}\n\n.mailharbor-ai-list {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n}\n\n.mailharbor-ai-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 30px 12px;\n text-align: center;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-empty .title {\n color: var(--m-text-2);\n font-weight: 500;\n font-size: 12.5px;\n}\n\n.mailharbor-ai-card {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n overflow: hidden;\n}\n\n.mailharbor-ai-card.generating {\n opacity: 0.7;\n}\n\n.mailharbor-ai-card-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 8px 7px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-card-head > span:first-child {\n color: var(--m-text-2);\n font-weight: 600;\n}\n\n.mailharbor-ai-summary {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 10px 12px;\n}\n\n.mailharbor-ai-summary section h4 {\n margin: 0 0 3px;\n font-size: 11px;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mailharbor-ai-summary section p {\n margin: 0;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-text {\n padding: 10px 12px;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n padding: 6px 8px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-ai-card-actions button {\n height: 26px;\n padding: 0 8px;\n font-size: 11.5px;\n}\n\n.mailharbor-ai-ask {\n display: flex;\n gap: 6px;\n padding: 10px 12px;\n border-top: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-ask input {\n flex: 1;\n height: 32px;\n}\n\n.mailharbor-ai-error {\n color: var(--m-danger);\n font-size: 12px;\n padding: 0 2px;\n}\n\n@media (max-width: 1280px) {\n .mailharbor-ai {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(420px, 94%);\n z-index: 6;\n border-left: 1px solid var(--m-line);\n box-shadow: -12px 0 40px rgba(0, 0, 0, 0.45);\n }\n}\n\n/* ---------- AI summary ---------- */\n\n.mailharbor-divider {\n width: 1px;\n height: 18px;\n margin: 0 4px;\n background: var(--m-line);\n}\n\n.mailharbor-summary {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.mailharbor-summary section h4 {\n margin: 0 0 6px;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-summary section p {\n margin: 0;\n white-space: pre-wrap;\n line-height: 1.75;\n color: var(--m-text);\n}\n\n/* ---------- modal / forms ---------- */\n\n.mailharbor-portal {\n position: fixed;\n inset: 0;\n z-index: 80;\n height: auto;\n background: transparent;\n pointer-events: none;\n}\n\n.mailharbor-portal .mailharbor-modal-mask {\n pointer-events: auto;\n}\n\n.mailharbor-modal-mask {\n position: fixed;\n inset: 0;\n z-index: 60;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(2px);\n}\n\n.mailharbor-modal {\n display: flex;\n flex-direction: column;\n width: min(720px, 100%);\n max-height: 86vh;\n overflow: hidden;\n border: 1px solid var(--m-line);\n border-radius: 14px;\n background: var(--m-bg-1);\n color: var(--m-text);\n box-shadow: var(--m-shadow);\n}\n\n.mailharbor-modal > header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 52px;\n padding: 0 14px 0 18px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 14px;\n font-weight: 600;\n}\n\n.mailharbor-modal .content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 18px;\n overflow: auto;\n}\n\n.mailharbor-modal > footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 18px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.mailharbor-field > input,\n.mailharbor-field > select,\n.mailharbor-field > textarea,\n.mailharbor-card > textarea {\n width: 100%;\n min-width: 0;\n}\n\n.mailharbor-field > label {\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 10px;\n min-width: 0;\n}\n\n.mailharbor-grid.three {\n grid-template-columns: minmax(0, 2fr) minmax(72px, 90px) auto;\n align-items: end;\n min-width: 0;\n}\n\n.mailharbor-toggle {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n color: var(--m-text-2);\n font-size: 12.5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.mailharbor-toggle input[type='checkbox'] {\n width: 14px;\n height: 14px;\n accent-color: var(--m-accent);\n}\n\n/* ---------- composer ---------- */\n\n.mailharbor-toolbar {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-wrap: wrap;\n padding: 6px;\n border: 1px solid var(--m-line-soft);\n border-bottom: 0;\n border-radius: 10px 10px 0 0;\n background: var(--m-bg-2);\n}\n\n.mailharbor-toolbar button {\n height: 26px;\n padding: 0 9px;\n border-radius: 6px;\n color: var(--m-text-2);\n font-size: 12px;\n}\n\n.mailharbor-toolbar button:hover {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n.mailharbor-editor {\n min-height: 240px;\n max-height: 44vh;\n overflow: auto;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 0 0 10px 10px;\n background: #fff;\n color: #171a21;\n outline: none;\n font-size: 13.5px;\n line-height: 1.7;\n}\n\n.mailharbor-editor:empty::before {\n content: attr(data-placeholder);\n color: #9aa0ab;\n}\n\n/* ---------- inline confirm ---------- */\n\n.mailharbor-confirm {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 8px;\n border: 1px solid var(--m-line);\n background: var(--m-bg-2);\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n/* ---------- selection / extraction ---------- */\n\n.mailharbor-selectbar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n margin: 0 12px 8px;\n border: 1px solid var(--m-accent-soft);\n border-radius: 9px;\n background: var(--m-accent-soft);\n}\n\n.mailharbor-selectbar select {\n height: 28px;\n max-width: 200px;\n}\n\n.mailharbor-row.selectable input[type='checkbox'] {\n width: 15px;\n height: 15px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 9px 10px;\n border: 1px solid var(--m-line-soft);\n border-radius: 9px;\n background: var(--m-bg-1);\n cursor: pointer;\n}\n\n.mailharbor-extract-row input {\n margin-top: 2px;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row > span {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n gap: 6px 12px;\n margin: 0;\n font-size: 12.5px;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n overflow-wrap: anywhere;\n}\n\n/* ---------- tasks panel ---------- */\n\n.mailharbor .chip {\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n.tasks-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 18px;\n padding: 18px 20px 48px;\n width: 100%;\n max-width: 940px;\n margin: 0 auto;\n}\n\n.tasks-overview {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));\n gap: 12px;\n}\n\n.tasks-progress,\n.tasks-project {\n display: flex;\n flex-direction: column;\n gap: 7px;\n padding: 12px 14px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n min-width: 0;\n}\n\n.tasks-progress-head,\n.tasks-project-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-width: 0;\n}\n\n.tasks-project-head .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12.5px;\n font-weight: 500;\n}\n\n.tasks-bar {\n height: 6px;\n border-radius: 999px;\n background: var(--m-bg-3);\n overflow: hidden;\n}\n\n.tasks-bar span {\n display: block;\n height: 100%;\n border-radius: 999px;\n background: var(--m-accent);\n transition: width 200ms ease;\n}\n\n.tasks-bar.small {\n height: 4px;\n}\n\n.tasks-filters {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.tasks-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.tasks-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 9px 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.tasks-row input[type='checkbox'] {\n width: 16px;\n height: 16px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.tasks-row .tasks-title {\n flex: 1;\n min-width: 0;\n height: auto;\n padding: 0;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n text-align: left;\n border: 0;\n background: transparent;\n}\n\n.tasks-row .tasks-title:hover {\n background: transparent;\n}\n\n.tasks-row .tasks-title > span:first-child {\n font-size: 13px;\n color: var(--m-text);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n}\n\n.tasks-row.done .tasks-title > span:first-child {\n color: var(--m-text-3);\n text-decoration: line-through;\n}\n\n.tasks-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-meta .due.overdue {\n color: var(--m-danger);\n}\n\n.tasks-list-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* ---------- markdown ---------- */\n\n.mailharbor-markdown {\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 12.5px;\n line-height: 1.68;\n overflow-wrap: anywhere;\n}\n\n.mailharbor-markdown > *:first-child {\n margin-top: 0;\n}\n\n.mailharbor-markdown > *:last-child {\n margin-bottom: 0;\n}\n\n.mailharbor-markdown p {\n margin: 0;\n white-space: pre-wrap;\n}\n\n.mailharbor-markdown h1,\n.mailharbor-markdown h2,\n.mailharbor-markdown h3,\n.mailharbor-markdown h4,\n.mailharbor-markdown h5,\n.mailharbor-markdown h6 {\n margin: 4px 0 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.mailharbor-markdown ul,\n.mailharbor-markdown ol {\n margin: 0;\n padding-left: 20px;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.mailharbor-markdown li::marker {\n color: var(--m-text-3);\n}\n\n.mailharbor-markdown a {\n color: var(--m-accent);\n text-decoration: none;\n}\n\n.mailharbor-markdown a:hover {\n text-decoration: underline;\n}\n\n.mailharbor-markdown code {\n padding: 1px 4px;\n border-radius: 4px;\n background: var(--m-bg-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n}\n\n.mailharbor-markdown pre {\n margin: 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n overflow: auto;\n}\n\n.mailharbor-markdown pre code {\n padding: 0;\n background: transparent;\n}\n\n.mailharbor-markdown blockquote {\n margin: 0;\n padding-left: 10px;\n border-left: 2px solid var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor-markdown hr {\n width: 100%;\n margin: 4px 0;\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n}\n\n/* ---------- composer AI ---------- */\n\n.mailharbor-toolbar button.ai {\n color: var(--m-accent);\n}\n\n.mailharbor-compose-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n flex-wrap: wrap;\n}\n\n.mailharbor-versions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n padding-top: 8px;\n}\n\n.mailharbor-versions .chip {\n height: 22px;\n padding: 0 8px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 11px;\n}\n\n.mailharbor-versions .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n/* ---------- settings ---------- */\n\n.mailharbor.mailharbor-settings-page {\n height: auto;\n min-height: 100%;\n overflow: visible;\n}\n\n.mailharbor-settings {\n flex: 0 0 auto;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n padding: 18px 20px 48px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n overflow-x: hidden;\n}\n\n.mailharbor-settings > * {\n min-width: 0;\n}\n\n.mailharbor-settings h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n\n.mailharbor-settings h3 {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n min-width: 0;\n overflow: hidden;\n}\n\n.mailharbor-account-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.mailharbor-account-row .meta {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.mailharbor-account-row .meta strong {\n font-weight: 600;\n}\n\n.mailharbor-account-row .meta span {\n color: var(--m-text-3);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor .avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n border-radius: 50%;\n background: var(--m-accent-soft);\n color: var(--m-accent);\n font-weight: 600;\n}\n\n.mailharbor-log {\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n color: var(--m-text-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n line-height: 1.6;\n max-height: 220px;\n overflow: auto;\n white-space: pre-wrap;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: minmax(120px, 170px) 1fr;\n gap: 6px 14px;\n font-size: 12px;\n margin: 0;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n color: var(--m-text-2);\n word-break: break-all;\n}\n\n.mailharbor hr {\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n margin: 2px 0;\n}\n\n/* ---------- mailbox sidebar ---------- */\n\n.mailharbor-side {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 214px;\n flex: none;\n padding: 10px 8px;\n overflow: auto;\n border-right: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-side-section {\n padding: 12px 8px 4px;\n font-size: 10.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 32px;\n padding: 0 8px;\n border-radius: 8px;\n color: var(--m-text-2);\n font-size: 12.5px;\n text-align: left;\n flex: none;\n}\n\n.mailharbor-side-item:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-side-item.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-side-item .name {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-side-item .sub {\n display: block;\n font-size: 10.5px;\n color: var(--m-text-3);\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor-side-item .count {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item svg {\n flex: none;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item.active svg {\n color: var(--m-accent);\n}\n\n@media (max-width: 900px) {\n .mailharbor-side {\n display: none;\n }\n}\n\n/* ---------- task board ---------- */\n\n.tasks-board-wrap {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px 18px 40px;\n}\n\n.tasks-summary {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.tasks-summary-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 7px;\n}\n\n.tasks-summary-labels {\n display: flex;\n align-items: baseline;\n gap: 10px;\n}\n\n.tasks-stats {\n display: flex;\n gap: 18px;\n}\n\n.tasks-stat {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 42px;\n}\n\n.tasks-stat strong {\n font-size: 16px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.tasks-stat span {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-stat.danger strong {\n color: var(--m-danger);\n}\n\n.tasks-toolbar {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.tasks-board {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 14px;\n align-items: start;\n}\n\n.task-column {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 140px;\n padding: 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.task-column-head {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 2px 4px 6px;\n}\n\n.task-column-head strong {\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.task-column-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 999px;\n background: var(--m-bg-3);\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-dot {\n width: 7px;\n height: 7px;\n border-radius: 999px;\n background: var(--m-text-3);\n}\n\n.task-dot.NEEDS-ACTION {\n background: #8a8f98;\n}\n\n.task-dot.IN-PROCESS {\n background: var(--m-accent);\n}\n\n.task-dot.COMPLETED {\n background: var(--m-ok);\n}\n\n.task-column-body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 60px;\n}\n\n.task-column-empty {\n padding: 18px 8px;\n text-align: center;\n color: var(--m-text-3);\n font-size: 11.5px;\n border: 1px dashed var(--m-line-soft);\n border-radius: 9px;\n}\n\n.task-card {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 11px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg);\n cursor: grab;\n transition:\n border-color 120ms ease,\n box-shadow 120ms ease;\n}\n\n.task-card:hover {\n border-color: var(--m-line);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);\n}\n\n.task-card.dragging {\n opacity: 0.5;\n}\n\n.task-card-top {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n}\n\n.task-card-title {\n flex: 1;\n min-width: 0;\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--m-text);\n overflow-wrap: anywhere;\n}\n\n.task-priority {\n width: 3px;\n height: 14px;\n margin-top: 2px;\n border-radius: 999px;\n background: transparent;\n flex: none;\n}\n\n.task-priority.p5 {\n background: #d9a441;\n}\n\n.task-priority.p9 {\n background: var(--m-danger);\n}\n\n.task-card-delete {\n width: 18px;\n height: 18px;\n flex: none;\n border-radius: 5px;\n color: var(--m-text-3);\n opacity: 0;\n}\n\n.task-card:hover .task-card-delete {\n opacity: 1;\n}\n\n.task-card-notes {\n margin: 0;\n font-size: 11.5px;\n line-height: 1.55;\n color: var(--m-text-3);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.task-card-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 10.5px;\n color: var(--m-text-3);\n}\n\n.task-due,\n.task-owner,\n.task-project,\n.task-priority-label {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n padding: 1px 6px;\n border-radius: 5px;\n background: var(--m-bg-2);\n}\n\n.task-due.overdue {\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.12);\n}\n\n.task-project {\n color: var(--m-accent);\n background: var(--m-accent-soft);\n}\n\n.task-source {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 22px;\n padding: 0 6px;\n border-radius: 5px;\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-source:hover {\n background: var(--m-hover);\n color: var(--m-text-2);\n}\n\n/* ---------- task editor ---------- */\n\n.mailharbor-tasks .mailharbor-modal {\n max-width: 760px;\n}\n\n.task-editor {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 230px;\n gap: 18px;\n}\n\n.task-editor-main {\n display: flex;\n flex-direction: column;\n gap: 14px;\n min-width: 0;\n}\n\n.task-editor-title input {\n width: 100%;\n height: 38px;\n font-size: 15px;\n font-weight: 500;\n}\n\n.task-editor-section {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.task-editor-section label,\n.task-editor-field label {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.task-editor-section textarea {\n width: 100%;\n resize: vertical;\n}\n\n.task-editor-side {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n align-self: start;\n}\n\n.task-editor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.task-editor-field input,\n.task-editor-field select {\n width: 100%;\n}\n\n.task-priority-picker {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n padding: 3px;\n border: 1px solid var(--m-line);\n border-radius: 8px;\n background: var(--m-bg);\n}\n\n.task-priority-picker button {\n height: 26px;\n border-radius: 6px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.task-priority-picker button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.task-editor-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n}\n";
38
+ var styles_default = "/* MailHarbor panel + settings.\n Tokens come from the DSH shell (dark by default); fallbacks match the\n default dark theme so the panel also renders in a bare host page.\n Component rules are all scoped under `.mailharbor` and the generic element\n reset lives in `:where()` so it cannot outrank a component class. */\n\n.mailharbor {\n --m-bg: var(--dsw-alias-bg-base, #151517);\n --m-bg-1: var(--dsw-alias-bg-layer-1, #232324);\n --m-bg-2: var(--dsw-alias-bg-layer-2, #2c2c2e);\n --m-bg-3: var(--dsw-alias-bg-layer-3, #353638);\n --m-line: var(--dsw-alias-border-l2, #ffffff1f);\n --m-line-soft: var(--dsw-alias-border-l1, #ffffff0f);\n --m-line-strong: var(--dsw-alias-border-l3, #ffffff29);\n --m-text: var(--dsw-alias-label-primary, #f9fafb);\n --m-text-2: var(--dsw-alias-label-secondary, #cfd3d6);\n --m-text-3: var(--dsw-alias-label-tertiary, #adb2b8);\n --m-caption: var(--dsw-alias-label-caption, #81858c);\n --m-accent: #5686fe;\n --m-accent-soft: rgba(86, 134, 254, 0.14);\n --m-primary-bg: var(--dsw-alias-brand-primary, #f9fafb);\n --m-primary-fg: var(--dsw-alias-label-primary-foreground, #0f1115);\n --m-hover: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n --m-danger: #f25a5a;\n --m-ok: #3fbf7f;\n --m-warn: var(--dsw-alias-state-warn-label, #dd8629);\n --m-radius: 8px;\n --m-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);\n\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--m-bg);\n color: var(--m-text);\n font-size: 13px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n\n.mailharbor *,\n.mailharbor *::before,\n.mailharbor *::after {\n box-sizing: border-box;\n}\n\n/* ---------- base controls ---------- */\n\n.mailharbor :where(button) {\n font: inherit;\n color: inherit;\n background: transparent;\n border: 0;\n margin: 0;\n cursor: pointer;\n}\n\n.mailharbor button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 30px;\n padding: 0 12px;\n border-radius: var(--m-radius);\n border: 1px solid transparent;\n color: var(--m-text-2);\n font-size: 12.5px;\n font-weight: 500;\n white-space: nowrap;\n transition:\n background 120ms ease,\n color 120ms ease,\n border-color 120ms ease;\n}\n\n.mailharbor button:hover:not(:disabled) {\n background: var(--m-hover);\n color: var(--m-text);\n}\n\n.mailharbor button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n\n.mailharbor button.primary {\n background: var(--m-primary-bg);\n color: var(--m-primary-fg);\n border-color: transparent;\n}\n\n.mailharbor button.primary:hover:not(:disabled) {\n background: var(--m-primary-bg);\n filter: brightness(0.94);\n color: var(--m-primary-fg);\n}\n\n.mailharbor button.ghost {\n border-color: var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor button.ghost:hover:not(:disabled) {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor button.subtle {\n color: var(--m-text-3);\n padding: 0 8px;\n}\n\n.mailharbor button.icon {\n width: 30px;\n padding: 0;\n}\n\n.mailharbor button.danger {\n color: var(--m-danger);\n}\n\n.mailharbor input,\n.mailharbor select,\n.mailharbor textarea {\n font: inherit;\n color: var(--m-text);\n background: var(--m-bg-1);\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n padding: 0 10px;\n height: 32px;\n min-width: 0;\n}\n\n.mailharbor textarea {\n height: auto;\n padding: 8px 10px;\n resize: vertical;\n line-height: 1.6;\n}\n\n.mailharbor select {\n padding-right: 26px;\n appearance: none;\n background-image:\n linear-gradient(45deg, transparent 50%, var(--m-text-3) 50%),\n linear-gradient(135deg, var(--m-text-3) 50%, transparent 50%);\n background-position:\n calc(100% - 15px) 14px,\n calc(100% - 10px) 14px;\n background-size:\n 5px 5px,\n 5px 5px;\n background-repeat: no-repeat;\n}\n\n.mailharbor ::placeholder {\n color: var(--m-caption);\n}\n\n.mailharbor input:focus-visible,\n.mailharbor select:focus-visible,\n.mailharbor textarea:focus-visible,\n.mailharbor button:focus-visible,\n.mailharbor [contenteditable='true']:focus-visible {\n outline: 2px solid rgba(86, 134, 254, 0.55);\n outline-offset: -1px;\n}\n\n.mailharbor .spacer {\n flex: 1;\n}\n\n.mailharbor .muted {\n color: var(--m-text-3);\n}\n\n.mailharbor .small {\n font-size: 12px;\n}\n\n.mailharbor .badge {\n display: inline-flex;\n align-items: center;\n height: 18px;\n padding: 0 6px;\n border-radius: 999px;\n font-size: 10.5px;\n font-weight: 500;\n background: var(--m-bg-3);\n color: var(--m-text-2);\n}\n\n.mailharbor .badge.ok {\n background: rgba(63, 191, 127, 0.16);\n color: var(--m-ok);\n}\n\n.mailharbor .badge.warn {\n background: rgba(221, 134, 41, 0.16);\n color: var(--m-warn);\n}\n\n.mailharbor .error {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.1);\n border-bottom: 1px solid rgba(242, 90, 90, 0.2);\n font-size: 12.5px;\n}\n\n/* ---------- shell ---------- */\n\n.mailharbor-topbar {\n display: flex;\n align-items: center;\n gap: 10px;\n height: 52px;\n padding: 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n flex: none;\n}\n\n.mailharbor-account {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n max-width: 220px;\n padding: 4px 8px 4px 4px;\n border-radius: 999px;\n border: 1px solid transparent;\n cursor: default;\n}\n\n.mailharbor-account:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-account .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.mailharbor-account select {\n height: 26px;\n background: transparent;\n border-color: transparent;\n font-weight: 500;\n}\n\n.mailharbor-body {\n display: flex;\n flex: 1;\n min-height: 0;\n position: relative;\n}\n\n.mailharbor button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-list {\n display: flex;\n flex-direction: column;\n width: 360px;\n flex: none;\n border-right: 1px solid var(--m-line-soft);\n min-height: 0;\n}\n\n.mailharbor-reader {\n flex: 1;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n@media (max-width: 1000px) {\n .mailharbor-list {\n width: 300px;\n }\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='reader'] .mailharbor-list {\n display: none;\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='list'] .mailharbor-reader {\n display: none;\n}\n\n/* ---------- segmented control ---------- */\n\n.mailharbor-segment {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px;\n border-radius: 9px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-segment button {\n height: 26px;\n padding: 0 12px;\n border-radius: 7px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-segment button:hover:not(.active) {\n color: var(--m-text-2);\n background: transparent;\n}\n\n.mailharbor-segment button.active {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n/* ---------- list ---------- */\n\n.mailharbor-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-filters select {\n width: 128px;\n flex: none;\n font-size: 12px;\n}\n\n.mailharbor-filters input[type='search'] {\n flex: 1;\n height: 30px;\n font-size: 12.5px;\n}\n\n.mailharbor-rows {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n.mailharbor-row {\n display: flex;\n flex-direction: column;\n gap: 3px;\n width: 100%;\n padding: 10px 14px;\n border: 0;\n border-bottom: 1px solid var(--m-line-soft);\n border-left: 2px solid transparent;\n border-radius: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n user-select: none;\n}\n\n.mailharbor-row:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-row.selected {\n background: var(--m-accent-soft);\n border-left-color: var(--m-accent);\n}\n\n.mailharbor-row .line-1,\n.mailharbor-row .line-2 {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n width: 100%;\n}\n\n.mailharbor-row .from {\n font-weight: 600;\n font-size: 12.5px;\n color: var(--m-text-2);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-row .date {\n margin-left: auto;\n color: var(--m-caption);\n font-size: 11px;\n flex: none;\n}\n\n.mailharbor-row .subject {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n.mailharbor-row .snippet {\n color: var(--m-caption);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n width: 100%;\n}\n\n.mailharbor-row.unread .from,\n.mailharbor-row.unread .subject {\n color: var(--m-text);\n}\n\n.mailharbor-row .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--m-accent);\n flex: none;\n}\n\n.mailharbor-row .paperclip {\n color: var(--m-caption);\n flex: none;\n display: inline-flex;\n}\n\n.mailharbor-list-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-top: 1px solid var(--m-line-soft);\n color: var(--m-text-3);\n font-size: 11.5px;\n flex: none;\n}\n\n.mailharbor-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 48px 24px;\n color: var(--m-text-3);\n text-align: center;\n}\n\n.mailharbor-empty svg {\n opacity: 0.5;\n}\n\n.mailharbor-empty .title {\n color: var(--m-text-2);\n font-size: 13px;\n font-weight: 500;\n}\n\n/* ---------- reader ---------- */\n\n.mailharbor-reader-head {\n padding: 16px 20px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mailharbor-reader-title {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mailharbor-reader-title h2 {\n margin: 0;\n font-size: 17px;\n line-height: 1.35;\n font-weight: 600;\n letter-spacing: -0.01em;\n word-break: break-word;\n}\n\n.mailharbor-reader-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 10px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-reader-meta .who {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: var(--m-text-2);\n}\n\n.mailharbor-reader-meta .who strong {\n color: var(--m-text);\n font-weight: 600;\n}\n\n.mailharbor-reader-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mailharbor-reader-body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 14px 18px 18px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-reader-body iframe {\n flex: 1;\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.mailharbor-reader-body pre {\n flex: 1;\n overflow: auto;\n margin: 0;\n padding: 20px 22px;\n white-space: pre-wrap;\n word-break: break-word;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n color: #171a21;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12.5px;\n line-height: 1.7;\n}\n\n.mailharbor-attachments {\n padding: 10px 18px;\n border-top: 1px solid var(--m-line-soft);\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex: none;\n background: var(--m-bg);\n}\n\n.mailharbor-attachment {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n font-size: 12px;\n text-decoration: none;\n color: var(--m-text-2);\n max-width: 260px;\n background: var(--m-bg-1);\n}\n\n.mailharbor-attachment:hover {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor-attachment .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ---------- AI rail ---------- */\n\n.mailharbor-ai {\n display: flex;\n flex-direction: column;\n width: 340px;\n flex: none;\n min-height: 0;\n border-left: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-ai-head {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 46px;\n padding: 0 8px 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-head strong {\n font-size: 13px;\n font-weight: 600;\n}\n\n.mailharbor-ai-confirm {\n display: inline-flex;\n gap: 2px;\n}\n\n.mailharbor-ai-context {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 9px 14px 10px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-context strong {\n font-size: 12.5px;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-ai-warn {\n padding: 8px 14px;\n color: var(--m-warn);\n background: rgba(221, 134, 41, 0.1);\n font-size: 12px;\n flex: none;\n}\n\n.mailharbor-ai-quick {\n display: flex;\n gap: 6px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-quick button {\n flex: 1;\n padding: 0 6px;\n}\n\n.mailharbor-ai-list {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n}\n\n.mailharbor-ai-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 30px 12px;\n text-align: center;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-empty .title {\n color: var(--m-text-2);\n font-weight: 500;\n font-size: 12.5px;\n}\n\n.mailharbor-ai-card {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n overflow: hidden;\n}\n\n.mailharbor-ai-card.generating {\n opacity: 0.7;\n}\n\n.mailharbor-ai-card-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 8px 7px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-card-head > span:first-child {\n color: var(--m-text-2);\n font-weight: 600;\n}\n\n.mailharbor-ai-summary {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 10px 12px;\n}\n\n.mailharbor-ai-summary section h4 {\n margin: 0 0 3px;\n font-size: 11px;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mailharbor-ai-summary section p {\n margin: 0;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-text {\n padding: 10px 12px;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n padding: 6px 8px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-ai-card-actions button {\n height: 26px;\n padding: 0 8px;\n font-size: 11.5px;\n}\n\n.mailharbor-ai-ask {\n display: flex;\n gap: 6px;\n padding: 10px 12px;\n border-top: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-ask input {\n flex: 1;\n height: 32px;\n}\n\n.mailharbor-ai-error {\n color: var(--m-danger);\n font-size: 12px;\n padding: 0 2px;\n}\n\n@media (max-width: 1280px) {\n .mailharbor-ai {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(420px, 94%);\n z-index: 6;\n border-left: 1px solid var(--m-line);\n box-shadow: -12px 0 40px rgba(0, 0, 0, 0.45);\n }\n}\n\n/* ---------- AI summary ---------- */\n\n.mailharbor-divider {\n width: 1px;\n height: 18px;\n margin: 0 4px;\n background: var(--m-line);\n}\n\n.mailharbor-summary {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.mailharbor-summary section h4 {\n margin: 0 0 6px;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-summary section p {\n margin: 0;\n white-space: pre-wrap;\n line-height: 1.75;\n color: var(--m-text);\n}\n\n/* ---------- modal / forms ---------- */\n\n.mailharbor-portal {\n position: fixed;\n inset: 0;\n z-index: 80;\n height: auto;\n background: transparent;\n pointer-events: none;\n}\n\n.mailharbor-portal .mailharbor-modal-mask {\n pointer-events: auto;\n}\n\n.mailharbor-modal-mask {\n position: fixed;\n inset: 0;\n z-index: 60;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(2px);\n}\n\n.mailharbor-modal {\n display: flex;\n flex-direction: column;\n width: min(720px, 100%);\n max-height: 86vh;\n overflow: hidden;\n border: 1px solid var(--m-line);\n border-radius: 14px;\n background: var(--m-bg-1);\n color: var(--m-text);\n box-shadow: var(--m-shadow);\n}\n\n.mailharbor-modal > header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 52px;\n padding: 0 14px 0 18px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 14px;\n font-weight: 600;\n}\n\n.mailharbor-modal .content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 18px;\n overflow: auto;\n}\n\n.mailharbor-modal > footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 18px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.mailharbor-field > input,\n.mailharbor-field > select,\n.mailharbor-field > textarea,\n.mailharbor-card > textarea {\n width: 100%;\n min-width: 0;\n}\n\n.mailharbor-field > label {\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 10px;\n min-width: 0;\n}\n\n.mailharbor-grid.three {\n grid-template-columns: minmax(0, 2fr) minmax(72px, 90px) auto;\n align-items: end;\n min-width: 0;\n}\n\n.mailharbor-toggle {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n color: var(--m-text-2);\n font-size: 12.5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.mailharbor-toggle input[type='checkbox'] {\n width: 14px;\n height: 14px;\n accent-color: var(--m-accent);\n}\n\n/* ---------- composer ---------- */\n\n.mailharbor-toolbar {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-wrap: wrap;\n padding: 6px;\n border: 1px solid var(--m-line-soft);\n border-bottom: 0;\n border-radius: 10px 10px 0 0;\n background: var(--m-bg-2);\n}\n\n.mailharbor-toolbar button {\n height: 26px;\n padding: 0 9px;\n border-radius: 6px;\n color: var(--m-text-2);\n font-size: 12px;\n}\n\n.mailharbor-toolbar button:hover {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n.mailharbor-editor {\n min-height: 240px;\n max-height: 44vh;\n overflow: auto;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 0 0 10px 10px;\n background: #fff;\n color: #171a21;\n outline: none;\n font-size: 13.5px;\n line-height: 1.7;\n}\n\n.mailharbor-editor:empty::before {\n content: attr(data-placeholder);\n color: #9aa0ab;\n}\n\n/* ---------- inline confirm ---------- */\n\n.mailharbor-confirm {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 8px;\n border: 1px solid var(--m-line);\n background: var(--m-bg-2);\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n/* ---------- selection / extraction ---------- */\n\n.mailharbor-selectbar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n margin: 0 12px 8px;\n border: 1px solid var(--m-accent-soft);\n border-radius: 9px;\n background: var(--m-accent-soft);\n}\n\n.mailharbor-selectbar select {\n height: 28px;\n max-width: 200px;\n}\n\n.mailharbor-row.selectable input[type='checkbox'] {\n width: 15px;\n height: 15px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 9px 10px;\n border: 1px solid var(--m-line-soft);\n border-radius: 9px;\n background: var(--m-bg-1);\n cursor: pointer;\n}\n\n.mailharbor-extract-row input {\n margin-top: 2px;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row > span {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n gap: 6px 12px;\n margin: 0;\n font-size: 12.5px;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n overflow-wrap: anywhere;\n}\n\n/* ---------- tasks panel ---------- */\n\n.mailharbor .chip {\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n.tasks-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 18px;\n padding: 18px 20px 48px;\n width: 100%;\n max-width: 940px;\n margin: 0 auto;\n}\n\n.tasks-overview {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));\n gap: 12px;\n}\n\n.tasks-progress,\n.tasks-project {\n display: flex;\n flex-direction: column;\n gap: 7px;\n padding: 12px 14px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n min-width: 0;\n}\n\n.tasks-progress-head,\n.tasks-project-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-width: 0;\n}\n\n.tasks-project-head .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12.5px;\n font-weight: 500;\n}\n\n.tasks-bar {\n height: 6px;\n border-radius: 999px;\n background: var(--m-bg-3);\n overflow: hidden;\n}\n\n.tasks-bar span {\n display: block;\n height: 100%;\n border-radius: 999px;\n background: var(--m-accent);\n transition: width 200ms ease;\n}\n\n.tasks-bar.small {\n height: 4px;\n}\n\n.tasks-filters {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.tasks-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.tasks-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 9px 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.tasks-row input[type='checkbox'] {\n width: 16px;\n height: 16px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.tasks-row .tasks-title {\n flex: 1;\n min-width: 0;\n height: auto;\n padding: 0;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n text-align: left;\n border: 0;\n background: transparent;\n}\n\n.tasks-row .tasks-title:hover {\n background: transparent;\n}\n\n.tasks-row .tasks-title > span:first-child {\n font-size: 13px;\n color: var(--m-text);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n}\n\n.tasks-row.done .tasks-title > span:first-child {\n color: var(--m-text-3);\n text-decoration: line-through;\n}\n\n.tasks-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-meta .due.overdue {\n color: var(--m-danger);\n}\n\n.tasks-list-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* ---------- markdown ---------- */\n\n.mailharbor-markdown {\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 12.5px;\n line-height: 1.68;\n overflow-wrap: anywhere;\n}\n\n.mailharbor-markdown > *:first-child {\n margin-top: 0;\n}\n\n.mailharbor-markdown > *:last-child {\n margin-bottom: 0;\n}\n\n.mailharbor-markdown p {\n margin: 0;\n white-space: pre-wrap;\n}\n\n.mailharbor-markdown h1,\n.mailharbor-markdown h2,\n.mailharbor-markdown h3,\n.mailharbor-markdown h4,\n.mailharbor-markdown h5,\n.mailharbor-markdown h6 {\n margin: 4px 0 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.mailharbor-markdown ul,\n.mailharbor-markdown ol {\n margin: 0;\n padding-left: 20px;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.mailharbor-markdown li::marker {\n color: var(--m-text-3);\n}\n\n.mailharbor-markdown a {\n color: var(--m-accent);\n text-decoration: none;\n}\n\n.mailharbor-markdown a:hover {\n text-decoration: underline;\n}\n\n.mailharbor-markdown code {\n padding: 1px 4px;\n border-radius: 4px;\n background: var(--m-bg-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n}\n\n.mailharbor-markdown pre {\n margin: 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n overflow: auto;\n}\n\n.mailharbor-markdown pre code {\n padding: 0;\n background: transparent;\n}\n\n.mailharbor-markdown blockquote {\n margin: 0;\n padding-left: 10px;\n border-left: 2px solid var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor-markdown hr {\n width: 100%;\n margin: 4px 0;\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n}\n\n/* ---------- composer AI ---------- */\n\n.mailharbor-toolbar button.ai {\n color: var(--m-accent);\n}\n\n.mailharbor-compose-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n flex-wrap: wrap;\n}\n\n.mailharbor-versions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n padding-top: 8px;\n}\n\n.mailharbor-versions .chip {\n height: 22px;\n padding: 0 8px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 11px;\n}\n\n.mailharbor-versions .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n/* ---------- settings ---------- */\n\n.mailharbor.mailharbor-settings-page {\n height: auto;\n min-height: 100%;\n overflow: visible;\n}\n\n.mailharbor-settings {\n flex: 0 0 auto;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n padding: 18px 20px 48px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n overflow-x: hidden;\n}\n\n.mailharbor-settings > * {\n min-width: 0;\n}\n\n.mailharbor-settings h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n\n.mailharbor-settings h3 {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n min-width: 0;\n overflow: hidden;\n}\n\n.mailharbor-account-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.mailharbor-account-row .meta {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.mailharbor-account-row .meta strong {\n font-weight: 600;\n}\n\n.mailharbor-account-row .meta span {\n color: var(--m-text-3);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor .avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n border-radius: 50%;\n background: var(--m-accent-soft);\n color: var(--m-accent);\n font-weight: 600;\n}\n\n.mailharbor-log {\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n color: var(--m-text-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n line-height: 1.6;\n max-height: 220px;\n overflow: auto;\n white-space: pre-wrap;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: minmax(120px, 170px) 1fr;\n gap: 6px 14px;\n font-size: 12px;\n margin: 0;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n color: var(--m-text-2);\n word-break: break-all;\n}\n\n.mailharbor hr {\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n margin: 2px 0;\n}\n\n/* ---------- conversation rows ---------- */\n\n.mailharbor-thread {\n display: flex;\n flex-direction: column;\n}\n\n.mailharbor-row.compact {\n padding-top: 6px;\n padding-bottom: 6px;\n padding-left: 34px;\n background: var(--m-bg);\n}\n\n.mailharbor-row.compact .line-1 {\n opacity: 0.85;\n}\n\n.mailharbor-row.compact .snippet {\n -webkit-line-clamp: 1;\n}\n\n.mailharbor .to-label {\n font-size: 10.5px;\n color: var(--m-text-3);\n flex: none;\n}\n\n.mailharbor .thread-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 999px;\n background: var(--m-bg-3);\n color: var(--m-text-2);\n font-size: 10.5px;\n flex: none;\n}\n\n.mailharbor .thread-count:hover {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-account {\n height: auto;\n padding: 3px 8px 3px 3px;\n border-radius: 8px;\n gap: 8px;\n}\n\n.mailharbor-account:hover {\n background: var(--m-hover);\n}\n\n/* ---------- mailbox sidebar ---------- */\n\n.mailharbor-side {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 214px;\n flex: none;\n padding: 10px 8px;\n overflow: auto;\n border-right: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-side-section {\n padding: 12px 8px 4px;\n font-size: 10.5px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 32px;\n padding: 0 8px;\n border-radius: 8px;\n color: var(--m-text-2);\n font-size: 12.5px;\n text-align: left;\n flex: none;\n}\n\n.mailharbor-side-item:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-side-item.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-side-item .name {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-side-item .sub {\n display: block;\n font-size: 10.5px;\n color: var(--m-text-3);\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor-side-item .count {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item svg {\n flex: none;\n color: var(--m-text-3);\n}\n\n.mailharbor-side-item.active svg {\n color: var(--m-accent);\n}\n\n@media (max-width: 900px) {\n .mailharbor-side {\n display: none;\n }\n}\n\n/* ---------- task board ---------- */\n\n.tasks-board-wrap {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px 18px 40px;\n}\n\n.tasks-summary {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.tasks-summary-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 7px;\n}\n\n.tasks-summary-labels {\n display: flex;\n align-items: baseline;\n gap: 10px;\n}\n\n.tasks-stats {\n display: flex;\n gap: 18px;\n}\n\n.tasks-stat {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 42px;\n}\n\n.tasks-stat strong {\n font-size: 16px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.tasks-stat span {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-stat.danger strong {\n color: var(--m-danger);\n}\n\n.tasks-toolbar {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.tasks-board {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 14px;\n align-items: start;\n}\n\n.task-column {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 140px;\n padding: 8px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n}\n\n.task-column-head {\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 2px 4px 6px;\n}\n\n.task-column-head strong {\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.task-column-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 999px;\n background: var(--m-bg-3);\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-dot {\n width: 7px;\n height: 7px;\n border-radius: 999px;\n background: var(--m-text-3);\n}\n\n.task-dot.NEEDS-ACTION {\n background: #8a8f98;\n}\n\n.task-dot.IN-PROCESS {\n background: var(--m-accent);\n}\n\n.task-dot.COMPLETED {\n background: var(--m-ok);\n}\n\n.task-dot.CANCELLED {\n background: var(--m-danger);\n opacity: 0.7;\n}\n\n.task-column-body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-height: 60px;\n}\n\n.task-column-empty {\n padding: 18px 8px;\n text-align: center;\n color: var(--m-text-3);\n font-size: 11.5px;\n border: 1px dashed var(--m-line-soft);\n border-radius: 9px;\n}\n\n.task-card {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 11px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg);\n cursor: grab;\n transition:\n border-color 120ms ease,\n box-shadow 120ms ease;\n}\n\n.task-card:hover {\n border-color: var(--m-line);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);\n}\n\n.task-card.dragging {\n opacity: 0.5;\n}\n\n.task-card-top {\n display: flex;\n align-items: flex-start;\n gap: 7px;\n}\n\n.task-card-title {\n flex: 1;\n min-width: 0;\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--m-text);\n overflow-wrap: anywhere;\n}\n\n.task-priority {\n width: 3px;\n height: 14px;\n margin-top: 2px;\n border-radius: 999px;\n background: transparent;\n flex: none;\n}\n\n.task-priority.p5 {\n background: #d9a441;\n}\n\n.task-priority.p9 {\n background: var(--m-danger);\n}\n\n.task-card-delete {\n width: 18px;\n height: 18px;\n flex: none;\n border-radius: 5px;\n color: var(--m-text-3);\n opacity: 0;\n}\n\n.task-card:hover .task-card-delete {\n opacity: 1;\n}\n\n.task-card-notes {\n margin: 0;\n font-size: 11.5px;\n line-height: 1.55;\n color: var(--m-text-3);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.task-card-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 10.5px;\n color: var(--m-text-3);\n}\n\n.task-due,\n.task-owner,\n.task-project,\n.task-priority-label {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n padding: 1px 6px;\n border-radius: 5px;\n background: var(--m-bg-2);\n}\n\n.task-due.overdue {\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.12);\n}\n\n.task-project {\n color: var(--m-accent);\n background: var(--m-accent-soft);\n}\n\n.task-source {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 22px;\n padding: 0 6px;\n border-radius: 5px;\n color: var(--m-text-3);\n font-size: 10.5px;\n}\n\n.task-source:hover {\n background: var(--m-hover);\n color: var(--m-text-2);\n}\n\n@media (max-width: 1180px) {\n .tasks-board {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n\n@media (max-width: 720px) {\n .tasks-board {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n\n/* ---------- AI search ---------- */\n\n.mailharbor-ai-search {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 12px 8px;\n padding: 7px 10px;\n border-radius: 8px;\n background: var(--m-accent-soft);\n color: var(--m-text-2);\n font-size: 12px;\n}\n\n.mailharbor-ai-search.searching {\n background: var(--m-bg-2);\n color: var(--m-text-3);\n}\n\n/* ---------- task editor ---------- */\n\n.mailharbor-tasks .mailharbor-modal {\n max-width: 760px;\n}\n\n.task-editor {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 230px;\n gap: 18px;\n}\n\n.task-editor-main {\n display: flex;\n flex-direction: column;\n gap: 14px;\n min-width: 0;\n}\n\n.task-editor-title input {\n width: 100%;\n height: 38px;\n font-size: 15px;\n font-weight: 500;\n}\n\n.task-editor-section {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.task-editor-section label,\n.task-editor-field label {\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.task-editor-section textarea {\n width: 100%;\n resize: vertical;\n}\n\n.task-editor-side {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n align-self: start;\n}\n\n.task-editor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.task-editor-field input,\n.task-editor-field select {\n width: 100%;\n}\n\n.task-priority-picker {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 4px;\n padding: 3px;\n border: 1px solid var(--m-line);\n border-radius: 8px;\n background: var(--m-bg);\n}\n\n.task-priority-picker button {\n height: 26px;\n border-radius: 6px;\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.task-priority-picker button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.task-editor-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n}\n";
39
39
 
40
40
  // src/client/icon.tsx
41
41
  var import_react = require("react");
@@ -126,6 +126,10 @@ var api = {
126
126
  check: (id) => request(`accounts/${encodeURIComponent(id)}/check`, { method: "POST" }),
127
127
  aiStatus: (accountId) => request(`ai/status${query({ accountId: accountId ?? null })}`),
128
128
  aiModels: () => request("ai/models"),
129
+ aiSearch: (payload) => request("ai/search", {
130
+ method: "POST",
131
+ body: JSON.stringify(payload)
132
+ }),
129
133
  aiThread: (accountId, key) => request(`ai/thread${query({ accountId: accountId ?? null, key })}`),
130
134
  aiAddCard: (payload) => request("ai/cards", {
131
135
  method: "POST",
@@ -182,6 +186,7 @@ var api = {
182
186
  }),
183
187
  syncTasks: (accountId) => request("tasks/sync", { method: "POST", body: JSON.stringify({ accountId }) }),
184
188
  projects: () => request("projects"),
189
+ pickDirectory: () => request("projects/pick-directory", { method: "POST" }),
185
190
  projectFor: (accountId, key) => request(`projects/for${query({ accountId: accountId ?? null, key })}`),
186
191
  createProject: (payload) => request("projects", {
187
192
  method: "POST",
@@ -496,6 +501,12 @@ var PATHS = {
496
501
  (0, import_react3.createElement)("path", { d: "M10 11v6M14 11v6" })
497
502
  ),
498
503
  send: (0, import_react3.createElement)("path", { d: "m4 4 17 8-17 8 3.6-8z M7.6 12H21" }),
504
+ list: (0, import_react3.createElement)(
505
+ "g",
506
+ null,
507
+ (0, import_react3.createElement)("path", { d: "M8 6h12M8 12h12M8 18h12" }),
508
+ (0, import_react3.createElement)("path", { d: "M4 6h.01M4 12h.01M4 18h.01" })
509
+ ),
499
510
  folder: (0, import_react3.createElement)("path", {
500
511
  d: "M3.5 6.5A1.5 1.5 0 0 1 5 5h4l2 2.5h8A1.5 1.5 0 0 1 20.5 9v8.5A1.5 1.5 0 0 1 19 19H5a1.5 1.5 0 0 1-1.5-1.5z"
501
512
  }),
@@ -1102,10 +1113,28 @@ ${card.output}`
1102
1113
  "div",
1103
1114
  { className: "mailharbor-field" },
1104
1115
  (0, import_react5.createElement)("label", null, "\u9879\u76EE\u76EE\u5F55\uFF08\u9ED8\u8BA4\u53EF\u5728\u8BBE\u7F6E\u91CC\u6539\uFF09"),
1105
- (0, import_react5.createElement)("input", {
1106
- value: projectRoot,
1107
- onChange: (event) => setProjectRoot(event.target.value)
1108
- })
1116
+ (0, import_react5.createElement)(
1117
+ "div",
1118
+ { style: { display: "flex", gap: "6px" } },
1119
+ (0, import_react5.createElement)("input", {
1120
+ value: projectRoot,
1121
+ onChange: (event) => setProjectRoot(event.target.value)
1122
+ }),
1123
+ (0, import_react5.createElement)(
1124
+ "button",
1125
+ {
1126
+ className: "ghost",
1127
+ title: "\u4ECE\u6587\u4EF6\u7BA1\u7406\u5668\u9009\u62E9",
1128
+ onClick: () => {
1129
+ void api.pickDirectory().then((result) => {
1130
+ if (result.path !== null) setProjectRoot(result.path);
1131
+ }).catch((caught) => setProjectWarnings([errorText(caught)]));
1132
+ }
1133
+ },
1134
+ (0, import_react5.createElement)(Icon, { name: "folder", size: 13 }),
1135
+ "\u6D4F\u89C8\u2026"
1136
+ )
1137
+ )
1109
1138
  ),
1110
1139
  (0, import_react5.createElement)(
1111
1140
  "p",
@@ -1905,6 +1934,7 @@ function Reader({
1905
1934
  onReply,
1906
1935
  onAiAction,
1907
1936
  onExtractTasks,
1937
+ onAddToProject,
1908
1938
  onClose
1909
1939
  }) {
1910
1940
  const [plain, setPlain] = (0, import_react8.useState)(false);
@@ -2013,6 +2043,12 @@ function Reader({
2013
2043
  (0, import_react8.createElement)(Icon, { name: "checklist", size: 14 }),
2014
2044
  "\u63D0\u53D6\u4EFB\u52A1"
2015
2045
  ),
2046
+ (0, import_react8.createElement)(
2047
+ "button",
2048
+ { className: "ghost", onClick: onAddToProject },
2049
+ (0, import_react8.createElement)(Icon, { name: "folder", size: 14 }),
2050
+ "\u52A0\u5165\u9879\u76EE"
2051
+ ),
2016
2052
  (0, import_react8.createElement)("span", { className: "spacer" }),
2017
2053
  html ? (0, import_react8.createElement)(
2018
2054
  "button",
@@ -2102,6 +2138,27 @@ var FOLDER_LABELS = {
2102
2138
  function folderName(name) {
2103
2139
  return FOLDER_LABELS[name] ?? name;
2104
2140
  }
2141
+ function isSentFolder(mailbox) {
2142
+ return /(^|[/.])(sent|sent items|sent mail|已发送|发件箱)/i.test(mailbox);
2143
+ }
2144
+ function normalizeSubject(subject) {
2145
+ return subject.replace(/^(\s*(re|fw|fwd|回复|转发)\s*[::]\s*)+/gi, "").replace(/\s+/g, " ").trim().toLowerCase();
2146
+ }
2147
+ function groupMessages(items) {
2148
+ const map = /* @__PURE__ */ new Map();
2149
+ const order = [];
2150
+ for (const item of items) {
2151
+ const key = normalizeSubject(item.subject) === "" ? `__${item.accountId}:${item.id}` : normalizeSubject(item.subject);
2152
+ const existing = map.get(key);
2153
+ if (existing === void 0) {
2154
+ map.set(key, [item]);
2155
+ order.push(key);
2156
+ } else {
2157
+ existing.push(item);
2158
+ }
2159
+ }
2160
+ return order.map((key) => ({ key, items: map.get(key) ?? [] }));
2161
+ }
2105
2162
  function errorText5(error) {
2106
2163
  return error instanceof ApiError || error instanceof Error ? error.message : String(error);
2107
2164
  }
@@ -2134,11 +2191,26 @@ function MailPanel() {
2134
2191
  const [narrow, setNarrow] = (0, import_react9.useState)(() => matchesNarrow());
2135
2192
  const [railOpen, setRailOpen] = (0, import_react9.useState)(false);
2136
2193
  const [selectMode, setSelectMode] = (0, import_react9.useState)(false);
2194
+ const [sideOpen, setSideOpen] = (0, import_react9.useState)(() => {
2195
+ try {
2196
+ return window.localStorage.getItem("mailharbor.side") !== "0";
2197
+ } catch {
2198
+ return true;
2199
+ }
2200
+ });
2201
+ const [expandedGroups, setExpandedGroups] = (0, import_react9.useState)([]);
2137
2202
  const [selectedKeys, setSelectedKeys] = (0, import_react9.useState)([]);
2138
2203
  const [projectOptions, setProjectOptions] = (0, import_react9.useState)([]);
2139
2204
  const [targetProject, setTargetProject] = (0, import_react9.useState)("");
2140
2205
  const [extraction, setExtraction] = (0, import_react9.useState)(null);
2141
2206
  const [extractBusy, setExtractBusy] = (0, import_react9.useState)(false);
2207
+ const [aiInfo, setAiInfo] = (0, import_react9.useState)(null);
2208
+ const [projectPicker, setProjectPicker] = (0, import_react9.useState)(null);
2209
+ const [pickerProjects, setPickerProjects] = (0, import_react9.useState)([]);
2210
+ const [pickerTarget, setPickerTarget] = (0, import_react9.useState)("");
2211
+ const [pickerNewName, setPickerNewName] = (0, import_react9.useState)("");
2212
+ const [pickerBusy, setPickerBusy] = (0, import_react9.useState)(false);
2213
+ const [aiSearching, setAiSearching] = (0, import_react9.useState)(false);
2142
2214
  const [aiRequest, setAiRequest] = (0, import_react9.useState)(null);
2143
2215
  const [tick, setTick] = (0, import_react9.useState)(0);
2144
2216
  const accounts = state?.accounts ?? [];
@@ -2183,6 +2255,104 @@ function MailPanel() {
2183
2255
  setError(errorText5(caught));
2184
2256
  }
2185
2257
  };
2258
+ const runAiSearch = async () => {
2259
+ const text = query2.trim();
2260
+ if (text === "" || accountScope === null) return;
2261
+ setAiSearching(true);
2262
+ setError("");
2263
+ try {
2264
+ const result = await Promise.race([
2265
+ api.aiSearch({ accountId: accountScope, source, mailbox, query: text }),
2266
+ new Promise((_resolve, reject) => {
2267
+ window.setTimeout(() => reject(new Error("AI \u641C\u7D22\u8D85\u65F6\uFF0C\u5DF2\u6539\u7528\u666E\u901A\u641C\u7D22")), 45e3);
2268
+ })
2269
+ ]);
2270
+ setPage({ items: result.items, total: result.total, offset: 0, limit: result.limit });
2271
+ setDrafts([]);
2272
+ setAiInfo(result.ai);
2273
+ setSearch("");
2274
+ } catch (caught) {
2275
+ setError(errorText5(caught));
2276
+ setAiInfo(null);
2277
+ setSearch(text);
2278
+ } finally {
2279
+ setAiSearching(false);
2280
+ }
2281
+ };
2282
+ const openProjectPicker = () => {
2283
+ if (selected === null) return;
2284
+ const key = keyOf(selected);
2285
+ if (!key.startsWith("mid:") && !key.startsWith("local:")) {
2286
+ setError("\u8FD9\u5C01\u90AE\u4EF6\u7F3A\u5C11 Message-ID\uFF0C\u65E0\u6CD5\u5173\u8054\u5230\u9879\u76EE");
2287
+ return;
2288
+ }
2289
+ setProjectPicker({
2290
+ accountId: selected.accountId,
2291
+ key,
2292
+ subject: selected.subject,
2293
+ date: selected.date
2294
+ });
2295
+ setPickerNewName(selected.subject.replace(/^(re|fwd):\s*/i, "").slice(0, 80));
2296
+ setPickerTarget("");
2297
+ void api.projects().then((list) => {
2298
+ setPickerProjects(list.items);
2299
+ setPickerTarget(list.items[0]?.id ?? "");
2300
+ }).catch((caught) => setError(errorText5(caught)));
2301
+ };
2302
+ const saveProjectPick = async () => {
2303
+ if (projectPicker === null) return;
2304
+ setPickerBusy(true);
2305
+ setError("");
2306
+ try {
2307
+ if (pickerTarget !== "") {
2308
+ await api.addProjectMessage(pickerTarget, {
2309
+ accountId: projectPicker.accountId,
2310
+ key: projectPicker.key,
2311
+ subject: projectPicker.subject,
2312
+ date: projectPicker.date
2313
+ });
2314
+ notice("\u5DF2\u52A0\u5165\u9879\u76EE");
2315
+ } else {
2316
+ const name = pickerNewName.trim();
2317
+ if (name === "") throw new Error("\u5148\u9009\u62E9\u4E00\u4E2A\u9879\u76EE\uFF0C\u6216\u586B\u5199\u65B0\u9879\u76EE\u540D\u79F0");
2318
+ const settings = await api.settings();
2319
+ const created = await api.createProject({
2320
+ accountId: projectPicker.accountId,
2321
+ name,
2322
+ root: settings.projects.root,
2323
+ conversation: `\uFF08\u4ECE\u90AE\u4EF6\u52A0\u5165\uFF09
2324
+
2325
+ ${projectPicker.subject}`,
2326
+ message: {
2327
+ key: projectPicker.key,
2328
+ subject: projectPicker.subject,
2329
+ date: projectPicker.date
2330
+ }
2331
+ });
2332
+ notice(`\u9879\u76EE\u5DF2\u521B\u5EFA\uFF1A${created.project.path}`);
2333
+ }
2334
+ setProjectPicker(null);
2335
+ setTick((value) => value + 1);
2336
+ } catch (caught) {
2337
+ setError(errorText5(caught));
2338
+ } finally {
2339
+ setPickerBusy(false);
2340
+ }
2341
+ };
2342
+ const toggleSide = () => {
2343
+ setSideOpen((open) => {
2344
+ try {
2345
+ window.localStorage.setItem("mailharbor.side", open ? "0" : "1");
2346
+ } catch {
2347
+ }
2348
+ return !open;
2349
+ });
2350
+ };
2351
+ const toggleGroup = (key) => {
2352
+ setExpandedGroups(
2353
+ (current) => current.includes(key) ? current.filter((entry) => entry !== key) : [...current, key]
2354
+ );
2355
+ };
2186
2356
  const toggleSelect = (item) => {
2187
2357
  const key = `${item.accountId}:${item.id}`;
2188
2358
  setSelectedKeys(
@@ -2194,8 +2364,11 @@ function MailPanel() {
2194
2364
  setError("\u5148\u521B\u5EFA\u4E00\u4E2A\u9879\u76EE");
2195
2365
  return;
2196
2366
  }
2197
- const chosen = items.filter((item) => selectedKeys.includes(`${item.accountId}:${item.id}`));
2198
- if (chosen.length === 0) return;
2367
+ const chosen = items.filter((item) => selectedKeys.includes(`${item.accountId}:${item.id}`)).filter((item) => item.messageId !== void 0 && item.messageId !== "");
2368
+ if (chosen.length === 0) {
2369
+ setError("\u6240\u9009\u90AE\u4EF6\u7F3A\u5C11 Message-ID\uFF0C\u65E0\u6CD5\u5173\u8054\u5230\u9879\u76EE");
2370
+ return;
2371
+ }
2199
2372
  try {
2200
2373
  for (const item of chosen) {
2201
2374
  await api.addProjectMessage(targetProject, {
@@ -2313,11 +2486,15 @@ function MailPanel() {
2313
2486
  (0, import_react9.useEffect)(() => {
2314
2487
  setMailbox(source === "local" ? "all" : "INBOX");
2315
2488
  setSelected(null);
2489
+ setAiInfo(null);
2490
+ setSearch("");
2316
2491
  setLimit(state?.settings.render.listLimit ?? 40);
2317
2492
  }, [source]);
2318
2493
  (0, import_react9.useEffect)(() => {
2319
2494
  if (accountScope === "all" && source === "server") setSource("local");
2320
2495
  setSelected(null);
2496
+ setAiInfo(null);
2497
+ setSearch("");
2321
2498
  }, [accountScope, source]);
2322
2499
  (0, import_react9.useEffect)(() => {
2323
2500
  if (accountScope === null) return;
@@ -2334,9 +2511,9 @@ function MailPanel() {
2334
2511
  return [];
2335
2512
  }
2336
2513
  })
2337
- ).then((groups) => {
2514
+ ).then((groups2) => {
2338
2515
  if (cancelled) return;
2339
- setDrafts(groups.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)));
2516
+ setDrafts(groups2.flat().sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)));
2340
2517
  setPage(EMPTY_PAGE);
2341
2518
  }).finally(() => {
2342
2519
  if (!cancelled) setLoading(false);
@@ -2589,6 +2766,65 @@ function MailPanel() {
2589
2766
  const loggedIn = isAll ? accounts.some((item) => item.id === state.activeAccountId) : state.loggedIn;
2590
2767
  const needsLogin = source === "server" && !isAll && state.loggedIn === false;
2591
2768
  const items = page.items;
2769
+ const isSent = isSentFolder(mailbox);
2770
+ const groups = groupMessages(items);
2771
+ const renderMessage = (item, meta = {}) => {
2772
+ const sent = isSent && item.to.length > 0;
2773
+ const first = sent ? item.to[0] : item.from;
2774
+ const who = first === void 0 || first === null ? "" : first.name !== "" ? first.name : first.address;
2775
+ const extra = sent && item.to.length > 1 ? ` +${String(item.to.length - 1)}` : "";
2776
+ const isSelected = selected?.id === item.id && selected?.accountId === item.accountId;
2777
+ return (0, import_react9.createElement)(
2778
+ "div",
2779
+ {
2780
+ key: `${item.accountId}:${item.id}`,
2781
+ className: `mailharbor-row${item.unread ? " unread" : ""}${isSelected ? " selected" : ""}${selectMode ? " selectable" : ""}${meta.compact === true ? " compact" : ""}`,
2782
+ role: "button",
2783
+ tabIndex: 0,
2784
+ onClick: () => {
2785
+ if (selectMode) toggleSelect(item);
2786
+ else void openMessage(item);
2787
+ },
2788
+ onKeyDown: (event) => {
2789
+ if (event.key === "Enter" || event.key === " ") {
2790
+ event.preventDefault();
2791
+ if (selectMode) toggleSelect(item);
2792
+ else void openMessage(item);
2793
+ }
2794
+ }
2795
+ },
2796
+ (0, import_react9.createElement)(
2797
+ "span",
2798
+ { className: "line-1" },
2799
+ selectMode ? (0, import_react9.createElement)("input", {
2800
+ type: "checkbox",
2801
+ checked: selectedKeys.includes(`${item.accountId}:${item.id}`),
2802
+ "aria-label": "\u9009\u62E9\u90AE\u4EF6",
2803
+ onClick: (event) => event.stopPropagation(),
2804
+ onChange: () => toggleSelect(item)
2805
+ }) : item.unread ? (0, import_react9.createElement)("span", { className: "dot" }) : null,
2806
+ sent ? (0, import_react9.createElement)("span", { className: "to-label" }, "\u6536\u4EF6\u4EBA ") : null,
2807
+ (0, import_react9.createElement)("span", { className: "from" }, `${who}${extra}`),
2808
+ item.hasAttachments ? (0, import_react9.createElement)(Icon, { name: "paperclip", size: 13 }) : null,
2809
+ source === "local" && item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
2810
+ meta.groupCount !== void 0 && meta.groupCount > 1 ? (0, import_react9.createElement)(
2811
+ "button",
2812
+ {
2813
+ className: "thread-count",
2814
+ title: meta.expanded === true ? "\u6536\u8D77\u4F1A\u8BDD" : `\u5C55\u5F00\u4F1A\u8BDD\uFF08${String(meta.groupCount)} \u5C01\uFF09`,
2815
+ onClick: (event) => {
2816
+ event.stopPropagation();
2817
+ toggleGroup(meta.groupKey ?? "");
2818
+ }
2819
+ },
2820
+ meta.expanded === true ? "\u2212" : String(meta.groupCount)
2821
+ ) : null,
2822
+ (0, import_react9.createElement)("span", { className: "date" }, shortDate(item.date))
2823
+ ),
2824
+ meta.compact === true ? null : (0, import_react9.createElement)("span", { className: "line-2" }, (0, import_react9.createElement)("span", { className: "subject" }, item.subject)),
2825
+ item.snippet === "" ? null : (0, import_react9.createElement)("span", { className: "snippet" }, item.snippet)
2826
+ );
2827
+ };
2592
2828
  return (0, import_react9.createElement)(
2593
2829
  "div",
2594
2830
  { className: narrow ? "mailharbor mailharbor-narrow" : "mailharbor" },
@@ -2596,20 +2832,21 @@ function MailPanel() {
2596
2832
  "div",
2597
2833
  { className: "mailharbor-topbar" },
2598
2834
  (0, import_react9.createElement)(
2599
- "div",
2600
- { className: "mailharbor-account" },
2601
- (0, import_react9.createElement)(Avatar, { email: isAll ? "all" : currentAccount?.email ?? "?", size: 26 }),
2835
+ "button",
2836
+ {
2837
+ className: "mailharbor-account",
2838
+ onClick: toggleSide,
2839
+ title: sideOpen ? "\u6536\u8D77\u8D26\u53F7\u4E0E\u6587\u4EF6\u5939" : "\u5C55\u5F00\u8D26\u53F7\u4E0E\u6587\u4EF6\u5939"
2840
+ },
2841
+ (0, import_react9.createElement)(Avatar, { email: currentAccount?.email ?? "?", size: 26 }),
2602
2842
  (0, import_react9.createElement)(
2603
2843
  "div",
2604
2844
  { style: { display: "flex", flexDirection: "column", minWidth: 0 } },
2605
- (0, import_react9.createElement)("span", { className: "name" }, isAll ? "\u7EDF\u4E00\u6536\u4EF6\u7BB1" : currentAccount?.label ?? ""),
2606
- (0, import_react9.createElement)(
2607
- "span",
2608
- { className: "small muted" },
2609
- isAll ? `${String(accounts.length)} \u4E2A\u8D26\u53F7` : currentAccount?.email ?? ""
2610
- )
2845
+ (0, import_react9.createElement)("span", { className: "name" }, currentAccount?.label ?? ""),
2846
+ (0, import_react9.createElement)("span", { className: "small muted" }, currentAccount?.email ?? "")
2611
2847
  ),
2612
- !isAll && state.loggedIn === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null
2848
+ state.loggedIn === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null,
2849
+ (0, import_react9.createElement)(Icon, { name: sideOpen ? "chevronLeft" : "chevronRight", size: 14 })
2613
2850
  ),
2614
2851
  (0, import_react9.createElement)(
2615
2852
  "div",
@@ -2637,16 +2874,6 @@ function MailPanel() {
2637
2874
  )
2638
2875
  ),
2639
2876
  (0, import_react9.createElement)("span", { className: "spacer" }),
2640
- (0, import_react9.createElement)(
2641
- "button",
2642
- {
2643
- className: "ghost icon",
2644
- title: "\u6DFB\u52A0\u8D26\u53F7",
2645
- "aria-label": "\u6DFB\u52A0\u8D26\u53F7",
2646
- onClick: () => setShowAdd(true)
2647
- },
2648
- (0, import_react9.createElement)(Icon, { name: "plus" })
2649
- ),
2650
2877
  (0, import_react9.createElement)(
2651
2878
  "button",
2652
2879
  { className: "ghost icon", title: "\u5237\u65B0\u5217\u8868", "aria-label": "\u5237\u65B0\u5217\u8868", onClick: refresh },
@@ -2688,24 +2915,9 @@ function MailPanel() {
2688
2915
  ) : (0, import_react9.createElement)(
2689
2916
  "div",
2690
2917
  { className: "mailharbor-body", "data-mode": narrow && selected !== null ? "reader" : "list" },
2691
- (0, import_react9.createElement)(
2918
+ sideOpen ? (0, import_react9.createElement)(
2692
2919
  "aside",
2693
2920
  { className: "mailharbor-side" },
2694
- (0, import_react9.createElement)(
2695
- "button",
2696
- {
2697
- className: `mailharbor-side-item${isAll ? " active" : ""}`,
2698
- onClick: () => {
2699
- setAccountScope("all");
2700
- setSource("local");
2701
- setMailbox("all");
2702
- setSelected(null);
2703
- }
2704
- },
2705
- (0, import_react9.createElement)(Icon, { name: "mail", size: 15 }),
2706
- (0, import_react9.createElement)("span", { className: "name" }, "\u7EDF\u4E00\u6536\u4EF6\u7BB1"),
2707
- (0, import_react9.createElement)("span", { className: "count" }, String(accounts.length))
2708
- ),
2709
2921
  (0, import_react9.createElement)("div", { className: "mailharbor-side-section" }, "\u8D26\u53F7"),
2710
2922
  accounts.map(
2711
2923
  (item) => (0, import_react9.createElement)(
@@ -2729,12 +2941,12 @@ function MailPanel() {
2729
2941
  item.enabled === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u505C\u7528") : null
2730
2942
  )
2731
2943
  ),
2732
- isAll ? null : (0, import_react9.createElement)(
2944
+ (0, import_react9.createElement)(
2733
2945
  "div",
2734
2946
  { className: "mailharbor-side-section" },
2735
2947
  source === "server" ? "\u670D\u52A1\u5668\u6587\u4EF6\u5939" : "\u672C\u5730\u6587\u4EF6\u5939"
2736
2948
  ),
2737
- isAll ? null : folders.map(
2949
+ folders.map(
2738
2950
  (folder) => (0, import_react9.createElement)(
2739
2951
  "button",
2740
2952
  {
@@ -2750,7 +2962,7 @@ function MailPanel() {
2750
2962
  folder.count > 0 ? (0, import_react9.createElement)("span", { className: "count" }, String(folder.count)) : null
2751
2963
  )
2752
2964
  )
2753
- ),
2965
+ ) : null,
2754
2966
  (0, import_react9.createElement)(
2755
2967
  "div",
2756
2968
  { className: "mailharbor-list" },
@@ -2759,19 +2971,23 @@ function MailPanel() {
2759
2971
  { className: "mailharbor-filters" },
2760
2972
  (0, import_react9.createElement)("input", {
2761
2973
  type: "search",
2762
- placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : "\u641C\u7D22\u90AE\u4EF6",
2974
+ placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : aiSearching ? "AI \u6B63\u5728\u7406\u89E3\u4F60\u7684\u641C\u7D22\u2026" : "\u641C\u7D22\u90AE\u4EF6\uFF0C\u6216\u63CF\u8FF0\u4F60\u8981\u627E\u4EC0\u4E48\uFF08AI\uFF09",
2763
2975
  "aria-label": "\u641C\u7D22\u90AE\u4EF6",
2764
2976
  value: query2,
2765
2977
  onChange: (event) => setQuery(event.target.value),
2766
2978
  onKeyDown: (event) => {
2767
- if (event.key === "Enter") setSearch(query2);
2979
+ if (event.key === "Enter") {
2980
+ if (showingDrafts) setSearch(query2);
2981
+ else void runAiSearch();
2982
+ }
2768
2983
  if (event.key === "Escape") {
2769
2984
  setQuery("");
2770
2985
  setSearch("");
2986
+ setAiInfo(null);
2771
2987
  }
2772
2988
  }
2773
2989
  }),
2774
- source === "local" ? (0, import_react9.createElement)(
2990
+ (0, import_react9.createElement)(
2775
2991
  "button",
2776
2992
  {
2777
2993
  className: selectMode ? "ghost active" : "ghost",
@@ -2785,9 +3001,9 @@ function MailPanel() {
2785
3001
  }
2786
3002
  }
2787
3003
  },
2788
- Icon({ name: "checklist", size: 14 }),
3004
+ Icon({ name: "list", size: 14 }),
2789
3005
  "\u9009\u62E9"
2790
- ) : null,
3006
+ ),
2791
3007
  query2 !== search || search !== "" ? (0, import_react9.createElement)(
2792
3008
  "button",
2793
3009
  {
@@ -2796,11 +3012,35 @@ function MailPanel() {
2796
3012
  onClick: () => {
2797
3013
  setQuery("");
2798
3014
  setSearch("");
3015
+ setAiInfo(null);
2799
3016
  }
2800
3017
  },
2801
3018
  (0, import_react9.createElement)(Icon, { name: "close", size: 14 })
2802
3019
  ) : null
2803
3020
  ),
3021
+ aiInfo === null ? null : (0, import_react9.createElement)(
3022
+ "div",
3023
+ { className: "mailharbor-ai-search" },
3024
+ (0, import_react9.createElement)(Icon, { name: "sparkles", size: 13 }),
3025
+ (0, import_react9.createElement)(
3026
+ "span",
3027
+ null,
3028
+ aiInfo.explanation !== "" ? aiInfo.explanation : `\u5173\u952E\u8BCD\uFF1A${aiInfo.keywords.join("\u3001")}`
3029
+ ),
3030
+ (0, import_react9.createElement)("span", { className: "spacer" }),
3031
+ (0, import_react9.createElement)(
3032
+ "button",
3033
+ {
3034
+ className: "subtle",
3035
+ onClick: () => {
3036
+ setAiInfo(null);
3037
+ setTick((value) => value + 1);
3038
+ }
3039
+ },
3040
+ "\u6E05\u9664"
3041
+ )
3042
+ ),
3043
+ aiSearching ? (0, import_react9.createElement)("div", { className: "mailharbor-ai-search searching" }, "AI \u6B63\u5728\u7406\u89E3\u5E76\u641C\u7D22\u2026") : null,
2804
3044
  selectMode ? (0, import_react9.createElement)(
2805
3045
  "div",
2806
3046
  { className: "mailharbor-selectbar" },
@@ -2911,76 +3151,39 @@ function MailPanel() {
2911
3151
  ) : items.length === 0 ? (0, import_react9.createElement)(
2912
3152
  "div",
2913
3153
  { className: "mailharbor-empty" },
2914
- (0, import_react9.createElement)(Icon, { name: "mail", size: 28 }),
3154
+ (0, import_react9.createElement)(Icon, { name: aiInfo === null ? "mail" : "sparkles", size: 28 }),
2915
3155
  (0, import_react9.createElement)(
2916
3156
  "div",
2917
3157
  { className: "title" },
2918
- source === "local" ? "\u672C\u5730\u5907\u4EFD\u91CC\u8FD8\u6CA1\u6709\u90AE\u4EF6" : "\u670D\u52A1\u5668\u4E0A\u6CA1\u6709\u90AE\u4EF6"
3158
+ aiInfo === null ? source === "local" ? "\u672C\u5730\u5907\u4EFD\u91CC\u8FD8\u6CA1\u6709\u90AE\u4EF6" : "\u670D\u52A1\u5668\u4E0A\u6CA1\u6709\u90AE\u4EF6" : "AI \u641C\u7D22\u6CA1\u6709\u627E\u5230\u5339\u914D\u90AE\u4EF6"
2919
3159
  ),
2920
3160
  (0, import_react9.createElement)(
2921
3161
  "div",
2922
3162
  { className: "small" },
2923
- source === "local" ? "\u70B9\u9876\u90E8\u300C\u5907\u4EFD\u300D\u628A\u670D\u52A1\u5668\u90AE\u4EF6\u540C\u6B65\u4E0B\u6765\u3002" : "\u6362\u4E00\u4E2A\u6587\u4EF6\u5939\u6216\u7A0D\u540E\u5237\u65B0\u3002"
3163
+ aiInfo === null ? source === "local" ? "\u70B9\u9876\u90E8\u300C\u5907\u4EFD\u300D\u628A\u670D\u52A1\u5668\u90AE\u4EF6\u540C\u6B65\u4E0B\u6765\u3002" : "\u6362\u4E00\u4E2A\u6587\u4EF6\u5939\u6216\u7A0D\u540E\u5237\u65B0\u3002" : "\u6362\u4E2A\u8BF4\u6CD5\uFF0C\u6216\u76F4\u63A5\u8F93\u5165\u5173\u952E\u8BCD\u540E\u6309\u56DE\u8F66\u3002"
2924
3164
  )
2925
- ) : items.map(
2926
- (item) => (0, import_react9.createElement)(
3165
+ ) : selectMode ? items.map((item) => renderMessage(item)) : groups.map((group) => {
3166
+ const latest = group.items[0];
3167
+ if (latest === void 0) return null;
3168
+ const expanded = expandedGroups.includes(group.key);
3169
+ return (0, import_react9.createElement)(
2927
3170
  "div",
2928
- {
2929
- key: `${item.accountId}:${item.id}`,
2930
- className: `mailharbor-row${item.unread ? " unread" : ""}${selected?.id === item.id && selected?.accountId === item.accountId ? " selected" : ""}${selectMode ? " selectable" : ""}`,
2931
- role: "button",
2932
- tabIndex: 0,
2933
- onClick: () => {
2934
- if (selectMode) toggleSelect(item);
2935
- else void openMessage(item);
2936
- },
2937
- onKeyDown: (event) => {
2938
- if (event.key === "Enter" || event.key === " ") {
2939
- event.preventDefault();
2940
- if (selectMode) toggleSelect(item);
2941
- else void openMessage(item);
2942
- }
2943
- }
2944
- },
2945
- (0, import_react9.createElement)(
2946
- "span",
2947
- { className: "line-1" },
2948
- selectMode ? (0, import_react9.createElement)("input", {
2949
- type: "checkbox",
2950
- checked: selectedKeys.includes(`${item.accountId}:${item.id}`),
2951
- "aria-label": "\u9009\u62E9\u90AE\u4EF6",
2952
- onClick: (event) => event.stopPropagation(),
2953
- onChange: () => toggleSelect(item)
2954
- }) : item.unread ? (0, import_react9.createElement)("span", { className: "dot" }) : null,
2955
- (0, import_react9.createElement)(
2956
- "span",
2957
- { className: "from" },
2958
- item.from.name !== "" ? item.from.name : item.from.address
2959
- ),
2960
- item.hasAttachments ? (0, import_react9.createElement)(Icon, { name: "paperclip", size: 13 }) : null,
2961
- isAll ? (0, import_react9.createElement)(
2962
- "span",
2963
- { className: "badge" },
2964
- accounts.find((entry) => entry.id === item.accountId)?.label ?? ""
2965
- ) : null,
2966
- source === "local" && item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
2967
- (0, import_react9.createElement)("span", { className: "date" }, shortDate(item.date))
2968
- ),
2969
- (0, import_react9.createElement)(
2970
- "span",
2971
- { className: "line-2" },
2972
- (0, import_react9.createElement)("span", { className: "subject" }, item.subject)
2973
- ),
2974
- item.snippet === "" ? null : (0, import_react9.createElement)("span", { className: "snippet" }, item.snippet)
2975
- )
2976
- )
3171
+ { className: "mailharbor-thread", key: group.key },
3172
+ renderMessage(latest, {
3173
+ groupCount: group.items.length,
3174
+ groupKey: group.key,
3175
+ expanded
3176
+ }),
3177
+ expanded ? group.items.slice(1).map((item) => renderMessage(item, { compact: true })) : null
3178
+ );
3179
+ })
2977
3180
  ),
2978
3181
  !showingDrafts && items.length > 0 ? (0, import_react9.createElement)(
2979
3182
  "div",
2980
3183
  { className: "mailharbor-list-foot" },
2981
3184
  (0, import_react9.createElement)("span", null, `${String(items.length)} / ${String(page.total)} \u5C01`),
2982
3185
  (0, import_react9.createElement)("span", { className: "spacer" }),
2983
- page.total > items.length ? (0, import_react9.createElement)(
3186
+ page.total > items.length && aiInfo === null ? (0, import_react9.createElement)(
2984
3187
  "button",
2985
3188
  { className: "subtle", onClick: () => setLimit((current) => current + 40) },
2986
3189
  "\u52A0\u8F7D\u66F4\u591A"
@@ -3012,6 +3215,7 @@ function MailPanel() {
3012
3215
  onReply: (mode) => startCompose(mode),
3013
3216
  onAiAction: requestAi,
3014
3217
  onExtractTasks: () => void extractTasks(),
3218
+ onAddToProject: openProjectPicker,
3015
3219
  onClose: narrow ? () => setSelected(null) : void 0
3016
3220
  })
3017
3221
  ),
@@ -3059,6 +3263,64 @@ function MailPanel() {
3059
3263
  backup.output.length === 0 ? "\uFF08\u7B49\u5F85\u8F93\u51FA\u2026\uFF09" : backup.output.join("\n")
3060
3264
  )
3061
3265
  ) : null,
3266
+ projectPicker !== null ? (0, import_react9.createElement)(
3267
+ Modal,
3268
+ {
3269
+ title: "\u52A0\u5165\u9879\u76EE",
3270
+ onClose: () => setProjectPicker(null),
3271
+ footer: (0, import_react9.createElement)(
3272
+ "div",
3273
+ { style: { display: "flex", gap: "8px", width: "100%", alignItems: "center" } },
3274
+ (0, import_react9.createElement)("span", { className: "small muted" }, "\u628A\u8FD9\u5C01\u90AE\u4EF6\u5173\u8054\u5230\u4E00\u4E2A\u9879\u76EE"),
3275
+ (0, import_react9.createElement)("span", { className: "spacer" }),
3276
+ (0, import_react9.createElement)("button", { className: "ghost", onClick: () => setProjectPicker(null) }, "\u53D6\u6D88"),
3277
+ (0, import_react9.createElement)(
3278
+ "button",
3279
+ { className: "primary", disabled: pickerBusy, onClick: () => void saveProjectPick() },
3280
+ pickerBusy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
3281
+ )
3282
+ )
3283
+ },
3284
+ (0, import_react9.createElement)(
3285
+ "div",
3286
+ { style: { display: "flex", flexDirection: "column", gap: "10px" } },
3287
+ pickerProjects.length === 0 ? (0, import_react9.createElement)("p", { className: "small muted" }, "\u8FD8\u6CA1\u6709\u9879\u76EE\uFF0C\u5728\u4E0B\u9762\u65B0\u5EFA\u4E00\u4E2A\u3002") : (0, import_react9.createElement)(
3288
+ "div",
3289
+ { className: "mailharbor-field" },
3290
+ (0, import_react9.createElement)("label", null, "\u9009\u62E9\u5DF2\u6709\u9879\u76EE"),
3291
+ (0, import_react9.createElement)(
3292
+ "select",
3293
+ {
3294
+ value: pickerTarget,
3295
+ onChange: (event) => setPickerTarget(event.target.value)
3296
+ },
3297
+ (0, import_react9.createElement)("option", { value: "" }, "\u4E0D\u52A0\u5165\u5DF2\u6709\u9879\u76EE"),
3298
+ pickerProjects.map(
3299
+ (project) => (0, import_react9.createElement)(
3300
+ "option",
3301
+ { key: project.id, value: project.id },
3302
+ `${project.name} \xB7 ${project.messages.length} \u5C01`
3303
+ )
3304
+ )
3305
+ )
3306
+ ),
3307
+ (0, import_react9.createElement)(
3308
+ "div",
3309
+ { className: "mailharbor-field" },
3310
+ (0, import_react9.createElement)("label", null, pickerProjects.length === 0 ? "\u65B0\u5EFA\u9879\u76EE" : "\u6216\u65B0\u5EFA\u9879\u76EE"),
3311
+ (0, import_react9.createElement)("input", {
3312
+ value: pickerNewName,
3313
+ placeholder: "\u9879\u76EE\u540D\u79F0",
3314
+ onChange: (event) => setPickerNewName(event.target.value)
3315
+ })
3316
+ ),
3317
+ (0, import_react9.createElement)(
3318
+ "p",
3319
+ { className: "small muted" },
3320
+ pickerTarget === "" ? "\u4FDD\u5B58\u65F6\u4F1A\u7528\u4E0A\u9762\u7684\u540D\u79F0\u521B\u5EFA\u9879\u76EE\uFF0C\u5E76\u628A\u5F53\u524D\u90AE\u4EF6\u52A0\u8FDB\u53BB\u3002" : "\u4FDD\u5B58\u540E\u8FD9\u5C01\u90AE\u4EF6\u4F1A\u51FA\u73B0\u5728\u9879\u76EE\u7684\u6765\u6E90\u90AE\u4EF6\u5217\u8868\u91CC\u3002"
3321
+ )
3322
+ )
3323
+ ) : null,
3062
3324
  extraction !== null ? (0, import_react9.createElement)(
3063
3325
  Modal,
3064
3326
  {
@@ -3365,13 +3627,28 @@ function MailSettings() {
3365
3627
  })
3366
3628
  ),
3367
3629
  (0, import_react10.createElement)(
3368
- "button",
3369
- {
3370
- className: "ghost small",
3371
- style: { alignSelf: "end" },
3372
- onClick: () => void saveGeneral({ projects: { root: projectRoot } })
3373
- },
3374
- "\u4FDD\u5B58\u76EE\u5F55"
3630
+ "div",
3631
+ { style: { display: "flex", gap: "6px", alignSelf: "end" } },
3632
+ (0, import_react10.createElement)(
3633
+ "button",
3634
+ {
3635
+ className: "ghost small",
3636
+ onClick: () => {
3637
+ void api.pickDirectory().then((result) => {
3638
+ if (result.path !== null) setProjectRoot(result.path);
3639
+ }).catch((caught) => setError(errorText6(caught)));
3640
+ }
3641
+ },
3642
+ "\u6D4F\u89C8\u2026"
3643
+ ),
3644
+ (0, import_react10.createElement)(
3645
+ "button",
3646
+ {
3647
+ className: "ghost small",
3648
+ onClick: () => void saveGeneral({ projects: { root: projectRoot } })
3649
+ },
3650
+ "\u4FDD\u5B58\u76EE\u5F55"
3651
+ )
3375
3652
  )
3376
3653
  )
3377
3654
  ),
@@ -3789,7 +4066,8 @@ var FILTERS = [
3789
4066
  var COLUMNS = [
3790
4067
  { status: "NEEDS-ACTION", label: "\u5F85\u5904\u7406", hint: "\u8FD8\u6CA1\u5F00\u59CB" },
3791
4068
  { status: "IN-PROCESS", label: "\u8FDB\u884C\u4E2D", hint: "\u6B63\u5728\u505A" },
3792
- { status: "COMPLETED", label: "\u5DF2\u5B8C\u6210", hint: "\u6536\u5C3E" }
4069
+ { status: "COMPLETED", label: "\u5DF2\u5B8C\u6210", hint: "\u6536\u5C3E" },
4070
+ { status: "CANCELLED", label: "\u7EC8\u6B62", hint: "\u4E0D\u505A\u4E86" }
3793
4071
  ];
3794
4072
  var PRIORITY_LABEL = { 0: "\u65E0", 5: "\u4E2D", 9: "\u9AD8" };
3795
4073
  function errorText7(error) {
@@ -3833,7 +4111,9 @@ function TasksIcon() {
3833
4111
  if (accountId === null) return;
3834
4112
  const result = await api.tasks(accountId);
3835
4113
  if (!cancelled) {
3836
- setCount(result.items.filter((task) => task.status !== "COMPLETED").length);
4114
+ setCount(
4115
+ result.items.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED").length
4116
+ );
3837
4117
  }
3838
4118
  } catch {
3839
4119
  }
@@ -3893,6 +4173,7 @@ function TasksPanel() {
3893
4173
  const [tasks, setTasks] = (0, import_react11.useState)([]);
3894
4174
  const [projects, setProjects] = (0, import_react11.useState)([]);
3895
4175
  const [filter, setFilter] = (0, import_react11.useState)("all");
4176
+ const [projectFilter, setProjectFilter] = (0, import_react11.useState)("");
3896
4177
  const [error, setError] = (0, import_react11.useState)("");
3897
4178
  const [toast, setToast] = (0, import_react11.useState)("");
3898
4179
  const [busy, setBusy] = (0, import_react11.useState)(false);
@@ -3938,10 +4219,6 @@ function TasksPanel() {
3938
4219
  },
3939
4220
  [filter]
3940
4221
  );
3941
- const open = tasks.filter((task) => task.status !== "COMPLETED");
3942
- const doneCount = tasks.length - open.length;
3943
- const progress = tasks.length === 0 ? 0 : Math.round(doneCount / tasks.length * 100);
3944
- const overdue = open.filter((task) => task.due !== "" && task.due.slice(0, 10) < dateKey(/* @__PURE__ */ new Date())).length;
3945
4222
  const switchAccount = async (id) => {
3946
4223
  try {
3947
4224
  await api.activateAccount(id);
@@ -4047,47 +4324,6 @@ function TasksPanel() {
4047
4324
  (0, import_react11.createElement)(
4048
4325
  "div",
4049
4326
  { className: "tasks-board-wrap" },
4050
- (0, import_react11.createElement)(
4051
- "div",
4052
- { className: "tasks-summary" },
4053
- (0, import_react11.createElement)(
4054
- "div",
4055
- { className: "tasks-summary-main" },
4056
- (0, import_react11.createElement)(
4057
- "div",
4058
- { className: "tasks-summary-labels" },
4059
- (0, import_react11.createElement)("strong", null, "\u603B\u4F53\u8FDB\u5EA6"),
4060
- (0, import_react11.createElement)(
4061
- "span",
4062
- { className: "muted small" },
4063
- `${String(doneCount)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
4064
- )
4065
- ),
4066
- (0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } }))
4067
- ),
4068
- (0, import_react11.createElement)(
4069
- "div",
4070
- { className: "tasks-stats" },
4071
- (0, import_react11.createElement)(
4072
- "span",
4073
- { className: "tasks-stat" },
4074
- (0, import_react11.createElement)("strong", null, String(open.length)),
4075
- (0, import_react11.createElement)("span", null, "\u5F85\u529E")
4076
- ),
4077
- (0, import_react11.createElement)(
4078
- "span",
4079
- { className: overdue > 0 ? "tasks-stat danger" : "tasks-stat" },
4080
- (0, import_react11.createElement)("strong", null, String(overdue)),
4081
- (0, import_react11.createElement)("span", null, "\u903E\u671F")
4082
- ),
4083
- (0, import_react11.createElement)(
4084
- "span",
4085
- { className: "tasks-stat" },
4086
- (0, import_react11.createElement)("strong", null, String(projects.length)),
4087
- (0, import_react11.createElement)("span", null, "\u9879\u76EE")
4088
- )
4089
- )
4090
- ),
4091
4327
  (0, import_react11.createElement)(
4092
4328
  "div",
4093
4329
  { className: "tasks-toolbar" },
@@ -4107,13 +4343,27 @@ function TasksPanel() {
4107
4343
  )
4108
4344
  ),
4109
4345
  (0, import_react11.createElement)("span", { className: "spacer" }),
4346
+ (0, import_react11.createElement)(
4347
+ "select",
4348
+ {
4349
+ value: projectFilter,
4350
+ onChange: (event) => setProjectFilter(event.target.value),
4351
+ "aria-label": "\u6309\u9879\u76EE\u7B5B\u9009"
4352
+ },
4353
+ (0, import_react11.createElement)("option", { value: "" }, "\u5168\u90E8\u9879\u76EE"),
4354
+ projects.map((project) => (0, import_react11.createElement)("option", { key: project.id, value: project.id }, project.name))
4355
+ ),
4110
4356
  (0, import_react11.createElement)("span", { className: "small muted" }, "\u62D6\u52A8\u5361\u7247\u53EF\u6539\u53D8\u72B6\u6001")
4111
4357
  ),
4112
4358
  (0, import_react11.createElement)(
4113
4359
  "div",
4114
4360
  { className: "tasks-board" },
4115
4361
  COLUMNS.map((column) => {
4116
- const cards = tasks.filter((task) => task.status === column.status).filter(matchesFilter);
4362
+ const cards = tasks.filter((task) => task.status === column.status).filter(matchesFilter).filter((task) => projectFilter === "" || task.projectId === projectFilter).sort((left, right) => {
4363
+ const leftDue = left.due === "" ? "9999" : left.due.slice(0, 10);
4364
+ const rightDue = right.due === "" ? "9999" : right.due.slice(0, 10);
4365
+ return leftDue.localeCompare(rightDue);
4366
+ });
4117
4367
  return (0, import_react11.createElement)(
4118
4368
  "div",
4119
4369
  {
@@ -4394,7 +4644,7 @@ function TaskEditor({
4394
4644
  }
4395
4645
 
4396
4646
  // src/client/index.tsx
4397
- var VERSION = "0.6.0";
4647
+ var VERSION = "0.7.1";
4398
4648
  var inject = ["slots"];
4399
4649
  var PANEL_ID = "mailharbor";
4400
4650
  var TASKS_PANEL_ID = "mailharbor-tasks";