@zaclinley/dsh-mailharbor 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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";
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";
39
39
 
40
40
  // src/client/icon.tsx
41
41
  var import_react = require("react");
@@ -499,6 +499,12 @@ var PATHS = {
499
499
  folder: (0, import_react3.createElement)("path", {
500
500
  d: "M3.5 6.5A1.5 1.5 0 0 1 5 5h4l2 2.5h8A1.5 1.5 0 0 1 20.5 9v8.5A1.5 1.5 0 0 1 19 19H5a1.5 1.5 0 0 1-1.5-1.5z"
501
501
  }),
502
+ calendar: (0, import_react3.createElement)(
503
+ "g",
504
+ null,
505
+ (0, import_react3.createElement)("rect", { x: 3.5, y: 5, width: 17, height: 15.5, rx: 2 }),
506
+ (0, import_react3.createElement)("path", { d: "M3.5 9.5h17M8 3.5v3M16 3.5v3" })
507
+ ),
502
508
  checklist: (0, import_react3.createElement)(
503
509
  "g",
504
510
  null,
@@ -2080,6 +2086,22 @@ function consumeOpenMail() {
2080
2086
  // src/client/panel.tsx
2081
2087
  var EMPTY_PAGE = { items: [], total: 0, offset: 0, limit: 40 };
2082
2088
  var DRAFTS = "__drafts";
2089
+ var folderCache = /* @__PURE__ */ new Map();
2090
+ var pageCache = /* @__PURE__ */ new Map();
2091
+ var FOLDER_LABELS = {
2092
+ INBOX: "\u6536\u4EF6\u7BB1",
2093
+ "Sent Items": "\u5DF2\u53D1\u9001",
2094
+ Sent: "\u5DF2\u53D1\u9001",
2095
+ Drafts: "\u8349\u7A3F",
2096
+ "Junk E-mail": "\u5783\u573E\u90AE\u4EF6",
2097
+ Junk: "\u5783\u573E\u90AE\u4EF6",
2098
+ Trash: "\u5DF2\u5220\u9664",
2099
+ Deleted: "\u5DF2\u5220\u9664",
2100
+ Archive: "\u5B58\u6863"
2101
+ };
2102
+ function folderName(name) {
2103
+ return FOLDER_LABELS[name] ?? name;
2104
+ }
2083
2105
  function errorText5(error) {
2084
2106
  return error instanceof ApiError || error instanceof Error ? error.message : String(error);
2085
2107
  }
@@ -2094,7 +2116,7 @@ function MailPanel() {
2094
2116
  const [state, setState] = (0, import_react9.useState)(null);
2095
2117
  const [error, setError] = (0, import_react9.useState)("");
2096
2118
  const [accountScope, setAccountScope] = (0, import_react9.useState)(null);
2097
- const [source, setSource] = (0, import_react9.useState)("local");
2119
+ const [source, setSource] = (0, import_react9.useState)("server");
2098
2120
  const [mailbox, setMailbox] = (0, import_react9.useState)("all");
2099
2121
  const [folders, setFolders] = (0, import_react9.useState)([]);
2100
2122
  const [query2, setQuery] = (0, import_react9.useState)("");
@@ -2263,6 +2285,9 @@ function MailPanel() {
2263
2285
  (0, import_react9.useEffect)(() => {
2264
2286
  if (accountScope === null) return;
2265
2287
  let cancelled = false;
2288
+ const cacheKey = `${accountScope}:${source}`;
2289
+ const cached = folderCache.get(cacheKey);
2290
+ if (cached !== void 0) setFolders(cached);
2266
2291
  api.folders({ accountId: accountScope, source }).then((result) => {
2267
2292
  if (cancelled) return;
2268
2293
  const items2 = source === "local" ? [
@@ -2277,8 +2302,9 @@ function MailPanel() {
2277
2302
  }))
2278
2303
  ] : result.items.map((item) => ({ ...item, label: item.label ?? item.name }));
2279
2304
  setFolders(items2);
2305
+ folderCache.set(cacheKey, items2);
2280
2306
  }).catch(() => {
2281
- if (!cancelled) setFolders([]);
2307
+ if (!cancelled && cached === void 0) setFolders([]);
2282
2308
  });
2283
2309
  return () => {
2284
2310
  cancelled = true;
@@ -2319,10 +2345,28 @@ function MailPanel() {
2319
2345
  cancelled = true;
2320
2346
  };
2321
2347
  }
2348
+ const pageKey = `${accountScope}:${source}:${mailbox}:${search}:${String(limit)}`;
2349
+ const cachedPage = pageCache.get(pageKey);
2350
+ if (cachedPage !== void 0) {
2351
+ setPage(cachedPage);
2352
+ setLoading(false);
2353
+ }
2322
2354
  api.messages({ accountId: accountScope, source, mailbox, query: search, limit }).then((result) => {
2323
2355
  if (!cancelled) {
2324
2356
  setPage(result);
2325
2357
  setDrafts([]);
2358
+ if (source === "server" && mailbox !== "all") {
2359
+ const update = (list) => list.map((folder) => folder.name === mailbox ? { ...folder, count: result.total } : folder);
2360
+ setFolders(update);
2361
+ const key = `${accountScope}:server`;
2362
+ const cachedFolders = folderCache.get(key);
2363
+ if (cachedFolders !== void 0) folderCache.set(key, update(cachedFolders));
2364
+ }
2365
+ pageCache.set(pageKey, result);
2366
+ if (pageCache.size > 12) {
2367
+ const oldest = pageCache.keys().next().value;
2368
+ if (oldest !== void 0) pageCache.delete(oldest);
2369
+ }
2326
2370
  }
2327
2371
  }).catch((caught) => {
2328
2372
  if (!cancelled) setError(errorText5(caught));
@@ -2510,18 +2554,11 @@ function MailPanel() {
2510
2554
  setError(errorText5(caught));
2511
2555
  }
2512
2556
  };
2513
- const folderOptions = (0, import_react9.useMemo)(
2514
- () => folders.map((folder) => ({
2515
- value: folder.name,
2516
- label: `${folder.label ?? folder.name}${folder.count > 0 ? ` (${String(folder.count)})` : ""}`
2517
- })),
2518
- [folders]
2519
- );
2520
2557
  if (state === null) {
2521
2558
  return (0, import_react9.createElement)(
2522
2559
  "div",
2523
2560
  { className: "mailharbor" },
2524
- (0, import_react9.createElement)("div", { className: "mailharbor-empty" }, error === "" ? "\u6B63\u5728\u52A0\u8F7D\u90AE\u7BB1\u2026" : error)
2561
+ (0, import_react9.createElement)("div", { className: "mailharbor-settings" }, "\u6B63\u5728\u8BFB\u53D6\u90AE\u7BB1\u2026")
2525
2562
  );
2526
2563
  }
2527
2564
  if (accounts.length === 0) {
@@ -2562,17 +2599,16 @@ function MailPanel() {
2562
2599
  "div",
2563
2600
  { className: "mailharbor-account" },
2564
2601
  (0, import_react9.createElement)(Avatar, { email: isAll ? "all" : currentAccount?.email ?? "?", size: 26 }),
2565
- accounts.length > 1 ? (0, import_react9.createElement)(
2566
- "select",
2567
- {
2568
- value: accountScope ?? "",
2569
- onChange: (event) => void switchAccount(event.target.value),
2570
- "aria-label": "\u8D26\u53F7"
2571
- },
2572
- (0, import_react9.createElement)("option", { key: "all", value: "all" }, "\u5168\u90E8\u8D26\u53F7"),
2573
- accounts.map((item) => (0, import_react9.createElement)("option", { key: item.id, value: item.id }, item.label))
2574
- ) : (0, import_react9.createElement)("span", { className: "name" }, currentAccount?.label ?? ""),
2575
- isAll ? (0, import_react9.createElement)("span", { className: "badge" }, `${String(accounts.length)} \u4E2A\u8D26\u53F7`) : null,
2602
+ (0, import_react9.createElement)(
2603
+ "div",
2604
+ { 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
+ )
2611
+ ),
2576
2612
  !isAll && state.loggedIn === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u767B\u5F55") : null
2577
2613
  ),
2578
2614
  (0, import_react9.createElement)(
@@ -2653,25 +2689,74 @@ function MailPanel() {
2653
2689
  "div",
2654
2690
  { className: "mailharbor-body", "data-mode": narrow && selected !== null ? "reader" : "list" },
2655
2691
  (0, import_react9.createElement)(
2656
- "div",
2657
- { className: "mailharbor-list" },
2692
+ "aside",
2693
+ { className: "mailharbor-side" },
2658
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
+ (0, import_react9.createElement)("div", { className: "mailharbor-side-section" }, "\u8D26\u53F7"),
2710
+ accounts.map(
2711
+ (item) => (0, import_react9.createElement)(
2712
+ "button",
2713
+ {
2714
+ key: item.id,
2715
+ className: `mailharbor-side-item${!isAll && item.id === accountScope ? " active" : ""}`,
2716
+ onClick: () => {
2717
+ void switchAccount(item.id);
2718
+ setSource("server");
2719
+ setMailbox("INBOX");
2720
+ }
2721
+ },
2722
+ (0, import_react9.createElement)(Avatar, { email: item.email, size: 18 }),
2723
+ (0, import_react9.createElement)(
2724
+ "span",
2725
+ { className: "name" },
2726
+ (0, import_react9.createElement)("span", null, item.label),
2727
+ (0, import_react9.createElement)("span", { className: "sub" }, item.email)
2728
+ ),
2729
+ item.enabled === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u505C\u7528") : null
2730
+ )
2731
+ ),
2732
+ isAll ? null : (0, import_react9.createElement)(
2659
2733
  "div",
2660
- { className: "mailharbor-filters" },
2661
- (0, import_react9.createElement)(
2662
- "select",
2734
+ { className: "mailharbor-side-section" },
2735
+ source === "server" ? "\u670D\u52A1\u5668\u6587\u4EF6\u5939" : "\u672C\u5730\u6587\u4EF6\u5939"
2736
+ ),
2737
+ isAll ? null : folders.map(
2738
+ (folder) => (0, import_react9.createElement)(
2739
+ "button",
2663
2740
  {
2664
- value: mailbox,
2665
- onChange: (event) => {
2666
- setMailbox(event.target.value);
2741
+ key: folder.name,
2742
+ className: `mailharbor-side-item${mailbox === folder.name ? " active" : ""}`,
2743
+ onClick: () => {
2744
+ setMailbox(folder.name);
2667
2745
  setSelected(null);
2668
- },
2669
- "aria-label": "\u90AE\u7BB1\u6587\u4EF6\u5939"
2746
+ }
2670
2747
  },
2671
- folderOptions.map(
2672
- (option) => (0, import_react9.createElement)("option", { key: option.value, value: option.value }, option.label)
2673
- )
2674
- ),
2748
+ (0, import_react9.createElement)(Icon, { name: folder.name === DRAFTS ? "compose" : "folder", size: 14 }),
2749
+ (0, import_react9.createElement)("span", { className: "name" }, folderName(folder.label ?? folder.name)),
2750
+ folder.count > 0 ? (0, import_react9.createElement)("span", { className: "count" }, String(folder.count)) : null
2751
+ )
2752
+ )
2753
+ ),
2754
+ (0, import_react9.createElement)(
2755
+ "div",
2756
+ { className: "mailharbor-list" },
2757
+ (0, import_react9.createElement)(
2758
+ "div",
2759
+ { className: "mailharbor-filters" },
2675
2760
  (0, import_react9.createElement)("input", {
2676
2761
  type: "search",
2677
2762
  placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : "\u641C\u7D22\u90AE\u4EF6",
@@ -2878,7 +2963,7 @@ function MailPanel() {
2878
2963
  { className: "badge" },
2879
2964
  accounts.find((entry) => entry.id === item.accountId)?.label ?? ""
2880
2965
  ) : null,
2881
- item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
2966
+ source === "local" && item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
2882
2967
  (0, import_react9.createElement)("span", { className: "date" }, shortDate(item.date))
2883
2968
  ),
2884
2969
  (0, import_react9.createElement)(
@@ -3062,15 +3147,6 @@ function MailSettings() {
3062
3147
  const [pollSeconds, setPollSeconds] = (0, import_react10.useState)(60);
3063
3148
  const [listLimit, setListLimit] = (0, import_react10.useState)(40);
3064
3149
  const [projectRoot, setProjectRoot] = (0, import_react10.useState)("");
3065
- const [caldavAccountId, setCaldavAccountId] = (0, import_react10.useState)("");
3066
- const [caldavUrl, setCaldavUrl] = (0, import_react10.useState)("");
3067
- const [caldavUser, setCaldavUser] = (0, import_react10.useState)("");
3068
- const [caldavPassword, setCaldavPassword] = (0, import_react10.useState)("");
3069
- const [caldavHref, setCaldavHref] = (0, import_react10.useState)("");
3070
- const [caldavHasPassword, setCaldavHasPassword] = (0, import_react10.useState)(false);
3071
- const [caldavCalendar, setCaldavCalendar] = (0, import_react10.useState)(null);
3072
- const [caldavMessage, setCaldavMessage] = (0, import_react10.useState)("");
3073
- const [caldavBusy, setCaldavBusy] = (0, import_react10.useState)(false);
3074
3150
  const [aiEnabled, setAiEnabled] = (0, import_react10.useState)(true);
3075
3151
  const [aiProvider, setAiProvider] = (0, import_react10.useState)("deepseek-account");
3076
3152
  const [aiModel, setAiModel] = (0, import_react10.useState)("deepseek-flash");
@@ -3094,9 +3170,6 @@ function MailSettings() {
3094
3170
  setPollSeconds(view.read.pollSeconds);
3095
3171
  setListLimit(view.render.listLimit);
3096
3172
  setProjectRoot(view.projects.root);
3097
- setCaldavAccountId(
3098
- (current) => current || view.activeAccountId || appState.settings.activeAccountId || ""
3099
- );
3100
3173
  setAiEnabled(view.ai.enabled);
3101
3174
  setAiProvider(view.ai.provider);
3102
3175
  setAiModel(view.ai.model);
@@ -3107,23 +3180,6 @@ function MailSettings() {
3107
3180
  cancelled = true;
3108
3181
  };
3109
3182
  }, [tick]);
3110
- (0, import_react10.useEffect)(() => {
3111
- let cancelled = false;
3112
- if (caldavAccountId === "") return;
3113
- api.taskCaldav(caldavAccountId).then((info) => {
3114
- if (cancelled) return;
3115
- setCaldavUrl(info.caldav?.url ?? "");
3116
- setCaldavUser(info.caldav?.username ?? "");
3117
- setCaldavHref(info.caldav?.calendarHref ?? "");
3118
- setCaldavHasPassword(info.hasPassword);
3119
- setCaldavCalendar(info.calendar);
3120
- }).catch((caught) => {
3121
- if (!cancelled) setCaldavMessage(errorText6(caught));
3122
- });
3123
- return () => {
3124
- cancelled = true;
3125
- };
3126
- }, [caldavAccountId, tick]);
3127
3183
  (0, import_react10.useEffect)(() => {
3128
3184
  let cancelled = false;
3129
3185
  const accountId = settings?.activeAccountId ?? state?.activeAccountId ?? null;
@@ -3319,139 +3375,6 @@ function MailSettings() {
3319
3375
  )
3320
3376
  )
3321
3377
  ),
3322
- (0, import_react10.createElement)(
3323
- "div",
3324
- { className: "mailharbor-card" },
3325
- (0, import_react10.createElement)("h3", null, "\u4EFB\u52A1\u4E0E CalDAV"),
3326
- (0, import_react10.createElement)(
3327
- "p",
3328
- { className: "small muted" },
3329
- "\u4EFB\u52A1\u9762\u677F\u7684\u300C\u540C\u6B65 CalDAV\u300D\u4F7F\u7528\u8FD9\u91CC\u7684\u914D\u7F6E\u3002\u5BC6\u7801\u53EA\u5B58\u7CFB\u7EDF Keychain\uFF0C\u4E0D\u5199\u5165\u914D\u7F6E\u6587\u4EF6\u3002"
3330
- ),
3331
- (0, import_react10.createElement)(
3332
- "div",
3333
- { className: "mailharbor-grid" },
3334
- (0, import_react10.createElement)(
3335
- Field,
3336
- { label: "\u8D26\u53F7" },
3337
- (0, import_react10.createElement)(
3338
- "select",
3339
- {
3340
- value: caldavAccountId,
3341
- onChange: (event) => {
3342
- setCaldavAccountId(event.target.value);
3343
- setCaldavMessage("");
3344
- }
3345
- },
3346
- (settings?.accounts ?? []).map(
3347
- (item) => (0, import_react10.createElement)("option", { key: item.id, value: item.id }, `${item.label}\uFF08${item.email}\uFF09`)
3348
- )
3349
- )
3350
- ),
3351
- (0, import_react10.createElement)(
3352
- Field,
3353
- { label: "\u72B6\u6001" },
3354
- (0, import_react10.createElement)(
3355
- "span",
3356
- { className: "small", style: { paddingTop: "7px", color: "var(--m-text-3)" } },
3357
- caldavCalendar === null ? "\u672A\u540C\u6B65" : caldavCalendar.lastSync === "" ? "\u672A\u540C\u6B65" : `\u4E0A\u6B21\u540C\u6B65 ${new Date(caldavCalendar.lastSync).toLocaleString()}`,
3358
- caldavCalendar?.error ? ` \xB7 \u5931\u8D25\uFF1A${caldavCalendar.error}` : ""
3359
- )
3360
- )
3361
- ),
3362
- (0, import_react10.createElement)(
3363
- "div",
3364
- { className: "mailharbor-grid" },
3365
- (0, import_react10.createElement)(
3366
- Field,
3367
- { label: "CalDAV \u5730\u5740" },
3368
- (0, import_react10.createElement)("input", {
3369
- value: caldavUrl,
3370
- placeholder: "https://caldav.example.com/",
3371
- onChange: (event) => setCaldavUrl(event.target.value)
3372
- })
3373
- ),
3374
- (0, import_react10.createElement)(
3375
- Field,
3376
- { label: "\u7528\u6237\u540D" },
3377
- (0, import_react10.createElement)("input", {
3378
- value: caldavUser,
3379
- placeholder: "\u901A\u5E38\u662F\u90AE\u7BB1\u5730\u5740",
3380
- onChange: (event) => setCaldavUser(event.target.value)
3381
- })
3382
- )
3383
- ),
3384
- (0, import_react10.createElement)(
3385
- "div",
3386
- { className: "mailharbor-grid" },
3387
- (0, import_react10.createElement)(
3388
- Field,
3389
- { label: caldavHasPassword ? "\u5E94\u7528\u5BC6\u7801\uFF08\u7559\u7A7A\u8868\u793A\u4E0D\u4FEE\u6539\uFF09" : "\u5E94\u7528\u5BC6\u7801" },
3390
- (0, import_react10.createElement)("input", {
3391
- type: "password",
3392
- value: caldavPassword,
3393
- placeholder: caldavHasPassword ? "\u5DF2\u4FDD\u5B58" : "iCloud / Fastmail \u7B49\u5E94\u7528\u4E13\u7528\u5BC6\u7801",
3394
- onChange: (event) => setCaldavPassword(event.target.value)
3395
- })
3396
- ),
3397
- (0, import_react10.createElement)(
3398
- Field,
3399
- { label: "\u4EFB\u52A1\u65E5\u5386\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09" },
3400
- (0, import_react10.createElement)("input", {
3401
- value: caldavHref,
3402
- placeholder: "\u7559\u7A7A\u81EA\u52A8\u53D1\u73B0",
3403
- onChange: (event) => setCaldavHref(event.target.value)
3404
- })
3405
- )
3406
- ),
3407
- (0, import_react10.createElement)(
3408
- "div",
3409
- { style: { display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" } },
3410
- (0, import_react10.createElement)(
3411
- "button",
3412
- {
3413
- className: "primary small",
3414
- disabled: caldavBusy,
3415
- onClick: () => {
3416
- setCaldavBusy(true);
3417
- setCaldavMessage("\u4FDD\u5B58\u4E2D\u2026");
3418
- void api.saveTaskCaldav({
3419
- accountId: caldavAccountId,
3420
- url: caldavUrl,
3421
- username: caldavUser,
3422
- ...caldavPassword === "" ? {} : { password: caldavPassword },
3423
- calendarHref: caldavHref
3424
- }).then((info) => {
3425
- setCaldavHasPassword(info.hasPassword);
3426
- setCaldavPassword("");
3427
- setCaldavMessage(info.hasPassword ? "\u5DF2\u4FDD\u5B58" : "\u5DF2\u4FDD\u5B58\u5730\u5740\uFF0C\u4F46\u8FD8\u6CA1\u6709\u5BC6\u7801");
3428
- setTick((value) => value + 1);
3429
- }).catch((caught) => setCaldavMessage(errorText6(caught))).finally(() => setCaldavBusy(false));
3430
- }
3431
- },
3432
- caldavBusy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58 CalDAV"
3433
- ),
3434
- (0, import_react10.createElement)(
3435
- "button",
3436
- {
3437
- className: "ghost small",
3438
- disabled: caldavBusy,
3439
- onClick: () => {
3440
- setCaldavBusy(true);
3441
- setCaldavMessage("\u540C\u6B65\u4E2D\u2026");
3442
- void api.syncTasks(caldavAccountId).then((result) => {
3443
- setCaldavMessage(
3444
- `\u540C\u6B65\u5B8C\u6210\uFF1A\u62C9\u53D6 ${String(result.result.pulled)}\uFF0C\u63A8\u9001 ${String(result.result.pushed)}`
3445
- );
3446
- setTick((value) => value + 1);
3447
- }).catch((caught) => setCaldavMessage(errorText6(caught))).finally(() => setCaldavBusy(false));
3448
- }
3449
- },
3450
- "\u7ACB\u5373\u540C\u6B65"
3451
- ),
3452
- (0, import_react10.createElement)("span", { className: "small muted" }, caldavMessage)
3453
- )
3454
- ),
3455
3378
  (0, import_react10.createElement)(
3456
3379
  "div",
3457
3380
  { className: "mailharbor-card" },
@@ -3858,19 +3781,17 @@ function AccountRow({
3858
3781
  // src/client/tasks-panel.tsx
3859
3782
  var import_react11 = require("react");
3860
3783
  var FILTERS = [
3861
- { id: "open", label: "\u5F85\u529E" },
3784
+ { id: "all", label: "\u5168\u90E8" },
3862
3785
  { id: "today", label: "\u4ECA\u5929" },
3863
3786
  { id: "week", label: "\u672C\u5468" },
3864
- { id: "overdue", label: "\u903E\u671F" },
3865
- { id: "done", label: "\u5DF2\u5B8C\u6210" },
3866
- { id: "all", label: "\u5168\u90E8" }
3787
+ { id: "overdue", label: "\u903E\u671F" }
3867
3788
  ];
3868
- var STATUS_LABEL = {
3869
- "NEEDS-ACTION": "\u5F85\u5904\u7406",
3870
- "IN-PROCESS": "\u8FDB\u884C\u4E2D",
3871
- COMPLETED: "\u5DF2\u5B8C\u6210",
3872
- CANCELLED: "\u5DF2\u53D6\u6D88"
3873
- };
3789
+ var COLUMNS = [
3790
+ { status: "NEEDS-ACTION", label: "\u5F85\u5904\u7406", hint: "\u8FD8\u6CA1\u5F00\u59CB" },
3791
+ { status: "IN-PROCESS", label: "\u8FDB\u884C\u4E2D", hint: "\u6B63\u5728\u505A" },
3792
+ { status: "COMPLETED", label: "\u5DF2\u5B8C\u6210", hint: "\u6536\u5C3E" }
3793
+ ];
3794
+ var PRIORITY_LABEL = { 0: "\u65E0", 5: "\u4E2D", 9: "\u9AD8" };
3874
3795
  function errorText7(error) {
3875
3796
  return error instanceof ApiError || error instanceof Error ? error.message : String(error);
3876
3797
  }
@@ -3886,11 +3807,13 @@ function weekEnd() {
3886
3807
  }
3887
3808
  function dueText(due) {
3888
3809
  if (due === "") return "";
3810
+ const value = due.slice(0, 10);
3889
3811
  const today = dateKey(/* @__PURE__ */ new Date());
3890
- if (due.slice(0, 10) < today) return `\u903E\u671F ${due.slice(0, 10)}`;
3891
- return due.slice(0, 10);
3812
+ if (value < today) return `\u903E\u671F ${value}`;
3813
+ if (value === today) return "\u4ECA\u5929";
3814
+ return value;
3892
3815
  }
3893
- var EMPTY_TASK = {
3816
+ var EMPTY = {
3894
3817
  title: "",
3895
3818
  notes: "",
3896
3819
  due: "",
@@ -3910,9 +3833,7 @@ function TasksIcon() {
3910
3833
  if (accountId === null) return;
3911
3834
  const result = await api.tasks(accountId);
3912
3835
  if (!cancelled) {
3913
- setCount(
3914
- result.items.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED").length
3915
- );
3836
+ setCount(result.items.filter((task) => task.status !== "COMPLETED").length);
3916
3837
  }
3917
3838
  } catch {
3918
3839
  }
@@ -3971,13 +3892,13 @@ function TasksPanel() {
3971
3892
  const [state, setState] = (0, import_react11.useState)(null);
3972
3893
  const [tasks, setTasks] = (0, import_react11.useState)([]);
3973
3894
  const [projects, setProjects] = (0, import_react11.useState)([]);
3974
- const [calendar, setCalendar] = (0, import_react11.useState)(null);
3975
- const [filter, setFilter] = (0, import_react11.useState)("open");
3895
+ const [filter, setFilter] = (0, import_react11.useState)("all");
3976
3896
  const [error, setError] = (0, import_react11.useState)("");
3977
3897
  const [toast, setToast] = (0, import_react11.useState)("");
3978
3898
  const [busy, setBusy] = (0, import_react11.useState)(false);
3979
3899
  const [editing, setEditing] = (0, import_react11.useState)(null);
3980
- const [creating, setCreating] = (0, import_react11.useState)(false);
3900
+ const [creating, setCreating] = (0, import_react11.useState)(null);
3901
+ const [dragging, setDragging] = (0, import_react11.useState)(null);
3981
3902
  const [tick, setTick] = (0, import_react11.useState)(0);
3982
3903
  const accountId = state?.settings.activeAccountId ?? null;
3983
3904
  (0, import_react11.useEffect)(() => {
@@ -3989,7 +3910,6 @@ function TasksPanel() {
3989
3910
  Promise.all([api.tasks(accountId), api.projects()]).then(([list, projectList]) => {
3990
3911
  if (cancelled) return;
3991
3912
  setTasks(list.items);
3992
- setCalendar(list.calendar);
3993
3913
  setProjects(projectList.items);
3994
3914
  }).catch((caught) => {
3995
3915
  if (!cancelled) setError(errorText7(caught));
@@ -4007,38 +3927,21 @@ function TasksPanel() {
4007
3927
  setToast("");
4008
3928
  }, 3e3);
4009
3929
  };
4010
- const visible = (0, import_react11.useMemo)(() => {
4011
- const today = dateKey(/* @__PURE__ */ new Date());
4012
- const end = weekEnd();
4013
- return tasks.filter((task) => {
4014
- const done2 = task.status === "COMPLETED" || task.status === "CANCELLED";
4015
- if (filter === "done") return done2;
4016
- if (done2) return false;
4017
- if (filter === "all") return true;
3930
+ const matchesFilter = (0, import_react11.useCallback)(
3931
+ (task) => {
3932
+ const today = dateKey(/* @__PURE__ */ new Date());
3933
+ const end = weekEnd();
4018
3934
  if (filter === "overdue") return task.due !== "" && task.due.slice(0, 10) < today;
4019
3935
  if (filter === "today") return task.due.slice(0, 10) === today;
4020
3936
  if (filter === "week") return task.due !== "" && task.due.slice(0, 10) <= end;
4021
3937
  return true;
4022
- });
4023
- }, [tasks, filter]);
4024
- const open = tasks.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED");
4025
- const done = tasks.length - open.length;
4026
- const progress = tasks.length === 0 ? 0 : Math.round(done / tasks.length * 100);
4027
- const byProject = (0, import_react11.useMemo)(() => {
4028
- const groups = /* @__PURE__ */ new Map();
4029
- for (const task of tasks) {
4030
- const key = task.projectId ?? "";
4031
- const group = groups.get(key) ?? {
4032
- project: projects.find((project) => project.id === key) ?? null,
4033
- total: 0,
4034
- done: 0
4035
- };
4036
- group.total += 1;
4037
- if (task.status === "COMPLETED" || task.status === "CANCELLED") group.done += 1;
4038
- groups.set(key, group);
4039
- }
4040
- return [...groups.values()].filter((group) => group.project !== null);
4041
- }, [tasks, projects]);
3938
+ },
3939
+ [filter]
3940
+ );
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;
4042
3945
  const switchAccount = async (id) => {
4043
3946
  try {
4044
3947
  await api.activateAccount(id);
@@ -4047,10 +3950,10 @@ function TasksPanel() {
4047
3950
  setError(errorText7(caught));
4048
3951
  }
4049
3952
  };
4050
- const toggle = async (task) => {
3953
+ const move = async (task, status) => {
3954
+ if (task.status === status) return;
4051
3955
  try {
4052
- const next = task.status === "COMPLETED" ? "NEEDS-ACTION" : "COMPLETED";
4053
- const { task: updated } = await api.updateTask(task.id, { status: next });
3956
+ const { task: updated } = await api.updateTask(task.id, { status });
4054
3957
  setTasks((current) => current.map((item) => item.id === task.id ? updated : item));
4055
3958
  } catch (caught) {
4056
3959
  setError(errorText7(caught));
@@ -4060,6 +3963,7 @@ function TasksPanel() {
4060
3963
  try {
4061
3964
  await api.deleteTask(task.id);
4062
3965
  setTasks((current) => current.filter((item) => item.id !== task.id));
3966
+ notify("\u4EFB\u52A1\u5DF2\u5220\u9664");
4063
3967
  } catch (caught) {
4064
3968
  setError(errorText7(caught));
4065
3969
  }
@@ -4074,21 +3978,8 @@ function TasksPanel() {
4074
3978
  setError(errorText7(caught));
4075
3979
  }
4076
3980
  };
4077
- const sync = async () => {
4078
- if (accountId === null) return;
4079
- setBusy(true);
4080
- setError("");
4081
- try {
4082
- const result = await api.syncTasks(accountId);
4083
- notify(`\u5DF2\u540C\u6B65\uFF1A\u62C9\u53D6 ${String(result.result.pulled)}\uFF0C\u63A8\u9001 ${String(result.result.pushed)}`);
4084
- refresh();
4085
- } catch (caught) {
4086
- setError(errorText7(caught));
4087
- } finally {
4088
- setBusy(false);
4089
- }
4090
- };
4091
3981
  const save = async (draft) => {
3982
+ if (accountId === null) return;
4092
3983
  setBusy(true);
4093
3984
  setError("");
4094
3985
  try {
@@ -4115,7 +4006,8 @@ function TasksPanel() {
4115
4006
  });
4116
4007
  }
4117
4008
  setEditing(null);
4118
- setCreating(false);
4009
+ setCreating(null);
4010
+ notify("\u4EFB\u52A1\u5DF2\u4FDD\u5B58");
4119
4011
  refresh();
4120
4012
  } catch (caught) {
4121
4013
  setError(errorText7(caught));
@@ -4125,7 +4017,7 @@ function TasksPanel() {
4125
4017
  };
4126
4018
  return (0, import_react11.createElement)(
4127
4019
  "div",
4128
- { className: "mailharbor" },
4020
+ { className: "mailharbor mailharbor-tasks" },
4129
4021
  (0, import_react11.createElement)(
4130
4022
  "div",
4131
4023
  { className: "mailharbor-topbar" },
@@ -4143,72 +4035,62 @@ function TasksPanel() {
4143
4035
  )
4144
4036
  ),
4145
4037
  (0, import_react11.createElement)("span", { className: "spacer" }),
4146
- calendar === null ? null : (0, import_react11.createElement)(
4147
- "span",
4148
- { className: "small muted" },
4149
- calendar.lastSync === "" ? "\u672A\u540C\u6B65" : `\u540C\u6B65\u4E8E ${new Date(calendar.lastSync).toLocaleString()}`
4150
- ),
4151
- (0, import_react11.createElement)(
4152
- "button",
4153
- { className: "ghost", onClick: () => void sync(), disabled: busy },
4154
- busy ? "\u540C\u6B65\u4E2D\u2026" : "\u540C\u6B65 CalDAV"
4155
- ),
4156
4038
  (0, import_react11.createElement)(
4157
4039
  "button",
4158
- { className: "primary", onClick: () => setCreating(true) },
4040
+ { className: "primary", onClick: () => setCreating("NEEDS-ACTION") },
4159
4041
  (0, import_react11.createElement)(Icon, { name: "plus" }),
4160
4042
  "\u65B0\u5EFA\u4EFB\u52A1"
4161
4043
  )
4162
4044
  ),
4163
- toast === "" ? null : (0, import_react11.createElement)("div", { className: "small", style: { padding: "6px 14px", color: "var(--m-ok)" } }, toast),
4045
+ toast === "" ? null : (0, import_react11.createElement)("div", { className: "small", style: { padding: "6px 18px", color: "var(--m-ok)" } }, toast),
4164
4046
  error === "" ? null : (0, import_react11.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
4165
4047
  (0, import_react11.createElement)(
4166
4048
  "div",
4167
- { className: "tasks-body" },
4049
+ { className: "tasks-board-wrap" },
4168
4050
  (0, import_react11.createElement)(
4169
4051
  "div",
4170
- { className: "tasks-overview" },
4052
+ { className: "tasks-summary" },
4171
4053
  (0, import_react11.createElement)(
4172
4054
  "div",
4173
- { className: "tasks-progress" },
4055
+ { className: "tasks-summary-main" },
4174
4056
  (0, import_react11.createElement)(
4175
4057
  "div",
4176
- { className: "tasks-progress-head" },
4058
+ { className: "tasks-summary-labels" },
4177
4059
  (0, import_react11.createElement)("strong", null, "\u603B\u4F53\u8FDB\u5EA6"),
4178
4060
  (0, import_react11.createElement)(
4179
4061
  "span",
4180
4062
  { className: "muted small" },
4181
- `${String(done)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
4063
+ `${String(doneCount)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
4182
4064
  )
4183
4065
  ),
4184
- (0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } })),
4185
- (0, import_react11.createElement)("div", { className: "small muted" }, `\u5F85\u529E ${String(open.length)} \u6761`)
4066
+ (0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } }))
4186
4067
  ),
4187
- ...byProject.map(
4188
- (group) => (0, import_react11.createElement)(
4189
- "div",
4190
- { key: group.project?.id ?? "none", className: "tasks-project" },
4191
- (0, import_react11.createElement)(
4192
- "div",
4193
- { className: "tasks-project-head" },
4194
- (0, import_react11.createElement)("span", { className: "name" }, group.project?.name ?? ""),
4195
- (0, import_react11.createElement)("span", { className: "muted small" }, `${String(group.done)}/${String(group.total)}`)
4196
- ),
4197
- (0, import_react11.createElement)(
4198
- "div",
4199
- { className: "tasks-bar small" },
4200
- (0, import_react11.createElement)("span", {
4201
- style: {
4202
- width: `${String(group.total === 0 ? 0 : Math.round(group.done / group.total * 100))}%`
4203
- }
4204
- })
4205
- )
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")
4206
4088
  )
4207
4089
  )
4208
4090
  ),
4209
4091
  (0, import_react11.createElement)(
4210
4092
  "div",
4211
- { className: "tasks-list-wrap" },
4093
+ { className: "tasks-toolbar" },
4212
4094
  (0, import_react11.createElement)(
4213
4095
  "div",
4214
4096
  { className: "tasks-filters" },
@@ -4224,73 +4106,128 @@ function TasksPanel() {
4224
4106
  )
4225
4107
  )
4226
4108
  ),
4227
- visible.length === 0 ? (0, import_react11.createElement)(
4228
- "div",
4229
- { className: "mailharbor-empty" },
4230
- (0, import_react11.createElement)(Icon, { name: "checklist", size: 28 }),
4231
- (0, import_react11.createElement)("div", { className: "title" }, "\u6CA1\u6709\u4EFB\u52A1"),
4232
- (0, import_react11.createElement)("div", { className: "small" }, "\u53EF\u4EE5\u5728\u8FD9\u91CC\u65B0\u5EFA\uFF0C\u6216\u5728\u90AE\u4EF6\u91CC\u7528\u300C\u63D0\u53D6\u4EFB\u52A1\u300D\u3002")
4233
- ) : (0, import_react11.createElement)(
4234
- "div",
4235
- { className: "tasks-list" },
4236
- visible.map(
4237
- (task) => (0, import_react11.createElement)(
4109
+ (0, import_react11.createElement)("span", { className: "spacer" }),
4110
+ (0, import_react11.createElement)("span", { className: "small muted" }, "\u62D6\u52A8\u5361\u7247\u53EF\u6539\u53D8\u72B6\u6001")
4111
+ ),
4112
+ (0, import_react11.createElement)(
4113
+ "div",
4114
+ { className: "tasks-board" },
4115
+ COLUMNS.map((column) => {
4116
+ const cards = tasks.filter((task) => task.status === column.status).filter(matchesFilter);
4117
+ return (0, import_react11.createElement)(
4118
+ "div",
4119
+ {
4120
+ key: column.status,
4121
+ className: `task-column task-column-${column.status}`,
4122
+ onDragOver: (event) => {
4123
+ event.preventDefault();
4124
+ },
4125
+ onDrop: (event) => {
4126
+ event.preventDefault();
4127
+ const task = tasks.find((item) => item.id === dragging);
4128
+ if (task !== void 0) void move(task, column.status);
4129
+ setDragging(null);
4130
+ }
4131
+ },
4132
+ (0, import_react11.createElement)(
4238
4133
  "div",
4239
- { key: task.id, className: `tasks-row${task.status === "COMPLETED" ? " done" : ""}` },
4240
- (0, import_react11.createElement)("input", {
4241
- type: "checkbox",
4242
- checked: task.status === "COMPLETED",
4243
- "aria-label": "\u5B8C\u6210\u4EFB\u52A1",
4244
- onChange: () => void toggle(task)
4245
- }),
4134
+ { className: "task-column-head" },
4135
+ (0, import_react11.createElement)("span", { className: `task-dot ${column.status}` }),
4136
+ (0, import_react11.createElement)("strong", null, column.label),
4137
+ (0, import_react11.createElement)("span", { className: "task-column-count" }, String(cards.length)),
4138
+ (0, import_react11.createElement)("span", { className: "spacer" }),
4246
4139
  (0, import_react11.createElement)(
4247
4140
  "button",
4248
- { className: "tasks-title", onClick: () => setEditing(task) },
4249
- (0, import_react11.createElement)("span", null, task.title),
4250
- (0, import_react11.createElement)(
4251
- "span",
4252
- { className: "tasks-meta" },
4253
- task.due === "" ? null : (0, import_react11.createElement)(
4254
- "span",
4141
+ {
4142
+ className: "subtle icon",
4143
+ title: `\u5728\u300C${column.label}\u300D\u4E2D\u65B0\u5EFA`,
4144
+ onClick: () => setCreating(column.status)
4145
+ },
4146
+ (0, import_react11.createElement)(Icon, { name: "plus", size: 14 })
4147
+ )
4148
+ ),
4149
+ (0, import_react11.createElement)(
4150
+ "div",
4151
+ { className: "task-column-body" },
4152
+ cards.length === 0 ? (0, import_react11.createElement)("div", { className: "task-column-empty" }, column.hint) : cards.map(
4153
+ (task) => (0, import_react11.createElement)(
4154
+ "article",
4155
+ {
4156
+ key: task.id,
4157
+ className: `task-card${dragging === task.id ? " dragging" : ""}`,
4158
+ draggable: true,
4159
+ onDragStart: () => setDragging(task.id),
4160
+ onDragEnd: () => setDragging(null),
4161
+ onClick: () => setEditing(task)
4162
+ },
4163
+ (0, import_react11.createElement)(
4164
+ "div",
4165
+ { className: "task-card-top" },
4166
+ (0, import_react11.createElement)("span", { className: `task-priority p${String(task.priority)}` }),
4167
+ (0, import_react11.createElement)("span", { className: "task-card-title" }, task.title),
4168
+ (0, import_react11.createElement)(
4169
+ "button",
4170
+ {
4171
+ className: "task-card-delete",
4172
+ title: "\u5220\u9664\u4EFB\u52A1",
4173
+ onClick: (event) => {
4174
+ event.stopPropagation();
4175
+ void remove(task);
4176
+ }
4177
+ },
4178
+ (0, import_react11.createElement)(Icon, { name: "close", size: 12 })
4179
+ )
4180
+ ),
4181
+ task.notes === "" ? null : (0, import_react11.createElement)("p", { className: "task-card-notes" }, task.notes),
4182
+ (0, import_react11.createElement)(
4183
+ "div",
4184
+ { className: "task-card-meta" },
4185
+ task.due === "" ? null : (0, import_react11.createElement)(
4186
+ "span",
4187
+ {
4188
+ className: task.due.slice(0, 10) < dateKey(/* @__PURE__ */ new Date()) && task.status !== "COMPLETED" ? "task-due overdue" : "task-due"
4189
+ },
4190
+ (0, import_react11.createElement)(Icon, { name: "calendar", size: 12 }),
4191
+ dueText(task.due)
4192
+ ),
4193
+ task.owner === "" ? null : (0, import_react11.createElement)("span", { className: "task-owner" }, task.owner),
4194
+ task.projectId === null ? null : (0, import_react11.createElement)(
4195
+ "span",
4196
+ { className: "task-project" },
4197
+ projects.find((project) => project.id === task.projectId)?.name ?? "\u9879\u76EE"
4198
+ ),
4199
+ task.priority > 0 ? (0, import_react11.createElement)(
4200
+ "span",
4201
+ { className: "task-priority-label" },
4202
+ `\u4F18\u5148\u7EA7 ${PRIORITY_LABEL[task.priority] ?? String(task.priority)}`
4203
+ ) : null
4204
+ ),
4205
+ task.messageKey === "" ? null : (0, import_react11.createElement)(
4206
+ "button",
4255
4207
  {
4256
- className: task.due.slice(0, 10) < dateKey(/* @__PURE__ */ new Date()) ? "due overdue" : "due"
4208
+ className: "task-source",
4209
+ onClick: (event) => {
4210
+ event.stopPropagation();
4211
+ void openSource(task);
4212
+ }
4257
4213
  },
4258
- dueText(task.due)
4259
- ),
4260
- task.owner === "" ? null : (0, import_react11.createElement)("span", null, task.owner),
4261
- (0, import_react11.createElement)("span", { className: "badge" }, STATUS_LABEL[task.status] ?? task.status),
4262
- task.projectId === null ? null : (0, import_react11.createElement)(
4263
- "span",
4264
- { className: "badge" },
4265
- projects.find((project) => project.id === task.projectId)?.name ?? "\u9879\u76EE"
4214
+ (0, import_react11.createElement)(Icon, { name: "mail", size: 12 }),
4215
+ "\u6765\u6E90\u90AE\u4EF6"
4266
4216
  )
4267
4217
  )
4268
- ),
4269
- task.messageKey === "" ? null : (0, import_react11.createElement)(
4270
- "button",
4271
- {
4272
- className: "subtle small",
4273
- title: "\u6253\u5F00\u6765\u6E90\u90AE\u4EF6",
4274
- onClick: () => void openSource(task)
4275
- },
4276
- "\u6765\u6E90"
4277
- ),
4278
- (0, import_react11.createElement)(
4279
- "button",
4280
- { className: "subtle icon", title: "\u5220\u9664", onClick: () => void remove(task) },
4281
- (0, import_react11.createElement)(Icon, { name: "trash", size: 13 })
4282
4218
  )
4283
4219
  )
4284
- )
4285
- )
4220
+ );
4221
+ })
4286
4222
  )
4287
4223
  ),
4288
- creating || editing !== null ? (0, import_react11.createElement)(TaskEditor, {
4224
+ creating !== null || editing !== null ? (0, import_react11.createElement)(TaskEditor, {
4289
4225
  task: editing,
4226
+ status: creating ?? "NEEDS-ACTION",
4290
4227
  projects,
4291
4228
  busy,
4292
4229
  onClose: () => {
4293
- setCreating(false);
4230
+ setCreating(null);
4294
4231
  setEditing(null);
4295
4232
  },
4296
4233
  onSave: (draft) => void save(draft)
@@ -4299,13 +4236,15 @@ function TasksPanel() {
4299
4236
  }
4300
4237
  function TaskEditor({
4301
4238
  task,
4239
+ status,
4302
4240
  projects,
4303
4241
  busy,
4304
4242
  onClose,
4305
4243
  onSave
4306
4244
  }) {
4307
4245
  const [draft, setDraft] = (0, import_react11.useState)({
4308
- ...EMPTY_TASK,
4246
+ ...EMPTY,
4247
+ status,
4309
4248
  ...task === null ? {} : {
4310
4249
  id: task.id,
4311
4250
  title: task.title,
@@ -4323,111 +4262,139 @@ function TaskEditor({
4323
4262
  {
4324
4263
  title: task === null ? "\u65B0\u5EFA\u4EFB\u52A1" : "\u7F16\u8F91\u4EFB\u52A1",
4325
4264
  onClose,
4265
+ wide: true,
4326
4266
  footer: (0, import_react11.createElement)(
4327
4267
  "div",
4328
- { style: { display: "flex", gap: "8px", width: "100%", justifyContent: "flex-end" } },
4268
+ { className: "task-editor-foot" },
4269
+ (0, import_react11.createElement)("span", { className: "small muted" }, "\u63D0\u793A\uFF1A\u4EFB\u52A1\u4F1A\u4FDD\u5B58\u5728\u672C\u5730\uFF0C\u53EF\u5728\u770B\u677F\u4E2D\u62D6\u52A8\u6539\u53D8\u72B6\u6001\u3002"),
4270
+ (0, import_react11.createElement)("span", { className: "spacer" }),
4329
4271
  (0, import_react11.createElement)("button", { className: "ghost", onClick: onClose }, "\u53D6\u6D88"),
4330
4272
  (0, import_react11.createElement)(
4331
4273
  "button",
4332
- { className: "primary", disabled: busy || draft.title.trim() === "", onClick: () => onSave(draft) },
4333
- busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
4334
- )
4335
- )
4336
- },
4337
- (0, import_react11.createElement)(
4338
- "div",
4339
- { className: "mailharbor-field" },
4340
- (0, import_react11.createElement)("label", null, "\u6807\u9898"),
4341
- (0, import_react11.createElement)("input", {
4342
- value: draft.title,
4343
- autoFocus: true,
4344
- onChange: (event) => set({ title: event.target.value })
4345
- })
4346
- ),
4347
- (0, import_react11.createElement)(
4348
- "div",
4349
- { className: "mailharbor-field" },
4350
- (0, import_react11.createElement)("label", null, "\u5907\u6CE8"),
4351
- (0, import_react11.createElement)("textarea", {
4352
- rows: 3,
4353
- value: draft.notes,
4354
- onChange: (event) => set({ notes: event.target.value })
4355
- })
4356
- ),
4357
- (0, import_react11.createElement)(
4358
- "div",
4359
- { className: "mailharbor-grid" },
4360
- (0, import_react11.createElement)(
4361
- "div",
4362
- { className: "mailharbor-field" },
4363
- (0, import_react11.createElement)("label", null, "\u622A\u6B62\u65E5\u671F"),
4364
- (0, import_react11.createElement)("input", {
4365
- type: "date",
4366
- value: draft.due,
4367
- onChange: (event) => set({ due: event.target.value })
4368
- })
4369
- ),
4370
- (0, import_react11.createElement)(
4371
- "div",
4372
- { className: "mailharbor-field" },
4373
- (0, import_react11.createElement)("label", null, "\u4F18\u5148\u7EA7"),
4374
- (0, import_react11.createElement)(
4375
- "select",
4376
4274
  {
4377
- value: String(draft.priority),
4378
- onChange: (event) => set({ priority: Number(event.target.value) })
4275
+ className: "primary",
4276
+ disabled: busy || draft.title.trim() === "",
4277
+ onClick: () => onSave(draft)
4379
4278
  },
4380
- (0, import_react11.createElement)("option", { value: "0" }, "\u65E0"),
4381
- (0, import_react11.createElement)("option", { value: "5" }, "\u4E2D"),
4382
- (0, import_react11.createElement)("option", { value: "9" }, "\u9AD8")
4279
+ busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u4EFB\u52A1"
4383
4280
  )
4384
4281
  )
4385
- ),
4282
+ },
4386
4283
  (0, import_react11.createElement)(
4387
4284
  "div",
4388
- { className: "mailharbor-grid" },
4285
+ { className: "task-editor" },
4389
4286
  (0, import_react11.createElement)(
4390
4287
  "div",
4391
- { className: "mailharbor-field" },
4392
- (0, import_react11.createElement)("label", null, "\u72B6\u6001"),
4288
+ { className: "task-editor-main" },
4393
4289
  (0, import_react11.createElement)(
4394
- "select",
4395
- {
4396
- value: draft.status,
4397
- onChange: (event) => set({ status: event.target.value })
4398
- },
4399
- Object.entries(STATUS_LABEL).map(([value, label]) => (0, import_react11.createElement)("option", { key: value, value }, label))
4290
+ "div",
4291
+ { className: "task-editor-title" },
4292
+ (0, import_react11.createElement)("input", {
4293
+ value: draft.title,
4294
+ autoFocus: true,
4295
+ placeholder: "\u8981\u505A\u4EC0\u4E48\uFF1F",
4296
+ onChange: (event) => set({ title: event.target.value })
4297
+ })
4298
+ ),
4299
+ (0, import_react11.createElement)(
4300
+ "div",
4301
+ { className: "task-editor-section" },
4302
+ (0, import_react11.createElement)("label", null, "\u5907\u6CE8"),
4303
+ (0, import_react11.createElement)("textarea", {
4304
+ rows: 5,
4305
+ value: draft.notes,
4306
+ placeholder: "\u80CC\u666F\u3001\u4E0B\u4E00\u6B65\u3001\u94FE\u63A5\u2026",
4307
+ onChange: (event) => set({ notes: event.target.value })
4308
+ })
4309
+ ),
4310
+ (0, import_react11.createElement)(
4311
+ "div",
4312
+ { className: "task-editor-section" },
4313
+ (0, import_react11.createElement)("label", null, "\u6765\u6E90"),
4314
+ draft.id === void 0 ? (0, import_react11.createElement)("span", { className: "small muted" }, "\u4ECE\u90AE\u4EF6\u63D0\u53D6\u7684\u4EFB\u52A1\u4F1A\u81EA\u52A8\u5173\u8054\u6765\u6E90\u90AE\u4EF6\u3002") : (0, import_react11.createElement)(
4315
+ "span",
4316
+ { className: "small muted" },
4317
+ task?.messageSubject !== void 0 && task.messageSubject !== "" ? `\u6765\u81EA\u90AE\u4EF6\uFF1A${task.messageSubject}` : "\u65E0\u5173\u8054\u90AE\u4EF6"
4318
+ )
4400
4319
  )
4401
4320
  ),
4402
4321
  (0, import_react11.createElement)(
4403
- "div",
4404
- { className: "mailharbor-field" },
4405
- (0, import_react11.createElement)("label", null, "\u9879\u76EE"),
4322
+ "aside",
4323
+ { className: "task-editor-side" },
4406
4324
  (0, import_react11.createElement)(
4407
- "select",
4408
- {
4409
- value: draft.projectId,
4410
- onChange: (event) => set({ projectId: event.target.value })
4411
- },
4412
- (0, import_react11.createElement)("option", { value: "" }, "\u65E0\u9879\u76EE"),
4413
- projects.map((project) => (0, import_react11.createElement)("option", { key: project.id, value: project.id }, project.name))
4325
+ "div",
4326
+ { className: "task-editor-field" },
4327
+ (0, import_react11.createElement)("label", null, "\u72B6\u6001"),
4328
+ (0, import_react11.createElement)(
4329
+ "select",
4330
+ {
4331
+ value: draft.status,
4332
+ onChange: (event) => set({ status: event.target.value })
4333
+ },
4334
+ COLUMNS.map((column) => (0, import_react11.createElement)("option", { key: column.status, value: column.status }, column.label))
4335
+ )
4336
+ ),
4337
+ (0, import_react11.createElement)(
4338
+ "div",
4339
+ { className: "task-editor-field" },
4340
+ (0, import_react11.createElement)("label", null, "\u622A\u6B62\u65E5\u671F"),
4341
+ (0, import_react11.createElement)("input", {
4342
+ type: "date",
4343
+ value: draft.due,
4344
+ onChange: (event) => set({ due: event.target.value })
4345
+ })
4346
+ ),
4347
+ (0, import_react11.createElement)(
4348
+ "div",
4349
+ { className: "task-editor-field" },
4350
+ (0, import_react11.createElement)("label", null, "\u4F18\u5148\u7EA7"),
4351
+ (0, import_react11.createElement)(
4352
+ "div",
4353
+ { className: "task-priority-picker" },
4354
+ [0, 5, 9].map(
4355
+ (value) => (0, import_react11.createElement)(
4356
+ "button",
4357
+ {
4358
+ key: value,
4359
+ className: draft.priority === value ? "active" : "",
4360
+ onClick: () => set({ priority: value })
4361
+ },
4362
+ PRIORITY_LABEL[value] ?? String(value)
4363
+ )
4364
+ )
4365
+ )
4366
+ ),
4367
+ (0, import_react11.createElement)(
4368
+ "div",
4369
+ { className: "task-editor-field" },
4370
+ (0, import_react11.createElement)("label", null, "\u9879\u76EE"),
4371
+ (0, import_react11.createElement)(
4372
+ "select",
4373
+ {
4374
+ value: draft.projectId,
4375
+ onChange: (event) => set({ projectId: event.target.value })
4376
+ },
4377
+ (0, import_react11.createElement)("option", { value: "" }, "\u65E0\u9879\u76EE"),
4378
+ projects.map((project) => (0, import_react11.createElement)("option", { key: project.id, value: project.id }, project.name))
4379
+ )
4380
+ ),
4381
+ (0, import_react11.createElement)(
4382
+ "div",
4383
+ { className: "task-editor-field" },
4384
+ (0, import_react11.createElement)("label", null, "\u8D1F\u8D23\u4EBA"),
4385
+ (0, import_react11.createElement)("input", {
4386
+ value: draft.owner,
4387
+ placeholder: "\u53EF\u9009",
4388
+ onChange: (event) => set({ owner: event.target.value })
4389
+ })
4414
4390
  )
4415
4391
  )
4416
- ),
4417
- (0, import_react11.createElement)(
4418
- "div",
4419
- { className: "mailharbor-field" },
4420
- (0, import_react11.createElement)("label", null, "\u8D1F\u8D23\u4EBA"),
4421
- (0, import_react11.createElement)("input", {
4422
- value: draft.owner,
4423
- onChange: (event) => set({ owner: event.target.value })
4424
- })
4425
4392
  )
4426
4393
  );
4427
4394
  }
4428
4395
 
4429
4396
  // src/client/index.tsx
4430
- var VERSION = "0.5.1";
4397
+ var VERSION = "0.6.0";
4431
4398
  var inject = ["slots"];
4432
4399
  var PANEL_ID = "mailharbor";
4433
4400
  var TASKS_PANEL_ID = "mailharbor-tasks";