@zaclinley/dsh-mailharbor 0.5.0 → 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)(
@@ -3502,27 +3587,6 @@ function AccountRow({
3502
3587
  const [fromName, setFromName] = (0, import_react10.useState)(account.smtp.fromName);
3503
3588
  const [busy, setBusy] = (0, import_react10.useState)(false);
3504
3589
  const [checking, setChecking] = (0, import_react10.useState)(false);
3505
- const [caldavUrl, setCaldavUrl] = (0, import_react10.useState)(account.caldav?.url ?? "");
3506
- const [caldavUser, setCaldavUser] = (0, import_react10.useState)(account.caldav?.username ?? "");
3507
- const [caldavHref, setCaldavHref] = (0, import_react10.useState)(account.caldav?.calendarHref ?? "");
3508
- const [caldavPassword, setCaldavPassword] = (0, import_react10.useState)("");
3509
- const [caldavStatus, setCaldavStatus] = (0, import_react10.useState)("");
3510
- (0, import_react10.useEffect)(() => {
3511
- if (!editing) return;
3512
- let cancelled = false;
3513
- api.taskCaldav(account.id).then((info) => {
3514
- if (cancelled) return;
3515
- setCaldavUrl(info.caldav?.url ?? "");
3516
- setCaldavUser(info.caldav?.username ?? "");
3517
- setCaldavHref(info.caldav?.calendarHref ?? "");
3518
- setCaldavStatus(info.hasPassword ? "\u5DF2\u4FDD\u5B58\u5E94\u7528\u5BC6\u7801" : "\u5C1A\u672A\u4FDD\u5B58\u5E94\u7528\u5BC6\u7801");
3519
- }).catch(() => {
3520
- if (!cancelled) setCaldavStatus("\u65E0\u6CD5\u8BFB\u53D6 CalDAV \u914D\u7F6E");
3521
- });
3522
- return () => {
3523
- cancelled = true;
3524
- };
3525
- }, [editing, account.id]);
3526
3590
  (0, import_react10.useEffect)(() => {
3527
3591
  setLabel(account.label);
3528
3592
  setUsername(account.username);
@@ -3683,75 +3747,6 @@ function AccountRow({
3683
3747
  )
3684
3748
  ),
3685
3749
  (0, import_react10.createElement)("hr", { style: { border: 0, borderTop: "1px solid var(--m-line)", width: "100%" } }),
3686
- (0, import_react10.createElement)("h4", { style: { margin: 0, fontSize: "12.5px" } }, "CalDAV \u4EFB\u52A1\u540C\u6B65"),
3687
- (0, import_react10.createElement)(
3688
- "div",
3689
- { className: "mailharbor-grid" },
3690
- (0, import_react10.createElement)(
3691
- Field,
3692
- { label: "CalDAV \u5730\u5740" },
3693
- (0, import_react10.createElement)("input", {
3694
- value: caldavUrl,
3695
- placeholder: "https://caldav.example.com/",
3696
- onChange: (event) => setCaldavUrl(event.target.value)
3697
- })
3698
- ),
3699
- (0, import_react10.createElement)(
3700
- Field,
3701
- { label: "\u7528\u6237\u540D" },
3702
- (0, import_react10.createElement)("input", {
3703
- value: caldavUser,
3704
- onChange: (event) => setCaldavUser(event.target.value)
3705
- })
3706
- )
3707
- ),
3708
- (0, import_react10.createElement)(
3709
- "div",
3710
- { className: "mailharbor-grid" },
3711
- (0, import_react10.createElement)(
3712
- Field,
3713
- { label: "\u5E94\u7528\u5BC6\u7801\uFF08\u7559\u7A7A\u8868\u793A\u4E0D\u4FEE\u6539\uFF09" },
3714
- (0, import_react10.createElement)("input", {
3715
- type: "password",
3716
- value: caldavPassword,
3717
- onChange: (event) => setCaldavPassword(event.target.value)
3718
- })
3719
- ),
3720
- (0, import_react10.createElement)(
3721
- Field,
3722
- { label: "\u4EFB\u52A1\u65E5\u5386\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09" },
3723
- (0, import_react10.createElement)("input", {
3724
- value: caldavHref,
3725
- placeholder: "\u81EA\u52A8\u53D1\u73B0",
3726
- onChange: (event) => setCaldavHref(event.target.value)
3727
- })
3728
- )
3729
- ),
3730
- (0, import_react10.createElement)(
3731
- "div",
3732
- { style: { display: "flex", gap: "8px", alignItems: "center" } },
3733
- (0, import_react10.createElement)(
3734
- "button",
3735
- {
3736
- className: "ghost small",
3737
- onClick: () => {
3738
- setCaldavStatus("\u4FDD\u5B58\u4E2D\u2026");
3739
- void api.saveTaskCaldav({
3740
- accountId: account.id,
3741
- url: caldavUrl,
3742
- username: caldavUser,
3743
- ...caldavPassword === "" ? {} : { password: caldavPassword },
3744
- calendarHref: caldavHref
3745
- }).then((info) => {
3746
- setCaldavStatus(info.hasPassword ? "\u5DF2\u4FDD\u5B58" : "\u672A\u4FDD\u5B58\u5BC6\u7801");
3747
- setCaldavPassword("");
3748
- }).catch((caught) => setCaldavStatus(errorText6(caught)));
3749
- }
3750
- },
3751
- "\u4FDD\u5B58 CalDAV"
3752
- ),
3753
- (0, import_react10.createElement)("span", { className: "small muted" }, caldavStatus)
3754
- ),
3755
3750
  (0, import_react10.createElement)(
3756
3751
  Field,
3757
3752
  { label: "\u5BC6\u7801 / \u5E94\u7528\u4E13\u7528\u5BC6\u7801\uFF08\u4E0D\u5199\u5165\u914D\u7F6E\u6587\u4EF6\uFF09" },
@@ -3786,19 +3781,17 @@ function AccountRow({
3786
3781
  // src/client/tasks-panel.tsx
3787
3782
  var import_react11 = require("react");
3788
3783
  var FILTERS = [
3789
- { id: "open", label: "\u5F85\u529E" },
3784
+ { id: "all", label: "\u5168\u90E8" },
3790
3785
  { id: "today", label: "\u4ECA\u5929" },
3791
3786
  { id: "week", label: "\u672C\u5468" },
3792
- { id: "overdue", label: "\u903E\u671F" },
3793
- { id: "done", label: "\u5DF2\u5B8C\u6210" },
3794
- { id: "all", label: "\u5168\u90E8" }
3787
+ { id: "overdue", label: "\u903E\u671F" }
3795
3788
  ];
3796
- var STATUS_LABEL = {
3797
- "NEEDS-ACTION": "\u5F85\u5904\u7406",
3798
- "IN-PROCESS": "\u8FDB\u884C\u4E2D",
3799
- COMPLETED: "\u5DF2\u5B8C\u6210",
3800
- CANCELLED: "\u5DF2\u53D6\u6D88"
3801
- };
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" };
3802
3795
  function errorText7(error) {
3803
3796
  return error instanceof ApiError || error instanceof Error ? error.message : String(error);
3804
3797
  }
@@ -3814,11 +3807,13 @@ function weekEnd() {
3814
3807
  }
3815
3808
  function dueText(due) {
3816
3809
  if (due === "") return "";
3810
+ const value = due.slice(0, 10);
3817
3811
  const today = dateKey(/* @__PURE__ */ new Date());
3818
- if (due.slice(0, 10) < today) return `\u903E\u671F ${due.slice(0, 10)}`;
3819
- return due.slice(0, 10);
3812
+ if (value < today) return `\u903E\u671F ${value}`;
3813
+ if (value === today) return "\u4ECA\u5929";
3814
+ return value;
3820
3815
  }
3821
- var EMPTY_TASK = {
3816
+ var EMPTY = {
3822
3817
  title: "",
3823
3818
  notes: "",
3824
3819
  due: "",
@@ -3838,9 +3833,7 @@ function TasksIcon() {
3838
3833
  if (accountId === null) return;
3839
3834
  const result = await api.tasks(accountId);
3840
3835
  if (!cancelled) {
3841
- setCount(
3842
- result.items.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED").length
3843
- );
3836
+ setCount(result.items.filter((task) => task.status !== "COMPLETED").length);
3844
3837
  }
3845
3838
  } catch {
3846
3839
  }
@@ -3899,13 +3892,13 @@ function TasksPanel() {
3899
3892
  const [state, setState] = (0, import_react11.useState)(null);
3900
3893
  const [tasks, setTasks] = (0, import_react11.useState)([]);
3901
3894
  const [projects, setProjects] = (0, import_react11.useState)([]);
3902
- const [calendar, setCalendar] = (0, import_react11.useState)(null);
3903
- const [filter, setFilter] = (0, import_react11.useState)("open");
3895
+ const [filter, setFilter] = (0, import_react11.useState)("all");
3904
3896
  const [error, setError] = (0, import_react11.useState)("");
3905
3897
  const [toast, setToast] = (0, import_react11.useState)("");
3906
3898
  const [busy, setBusy] = (0, import_react11.useState)(false);
3907
3899
  const [editing, setEditing] = (0, import_react11.useState)(null);
3908
- 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);
3909
3902
  const [tick, setTick] = (0, import_react11.useState)(0);
3910
3903
  const accountId = state?.settings.activeAccountId ?? null;
3911
3904
  (0, import_react11.useEffect)(() => {
@@ -3917,7 +3910,6 @@ function TasksPanel() {
3917
3910
  Promise.all([api.tasks(accountId), api.projects()]).then(([list, projectList]) => {
3918
3911
  if (cancelled) return;
3919
3912
  setTasks(list.items);
3920
- setCalendar(list.calendar);
3921
3913
  setProjects(projectList.items);
3922
3914
  }).catch((caught) => {
3923
3915
  if (!cancelled) setError(errorText7(caught));
@@ -3935,38 +3927,21 @@ function TasksPanel() {
3935
3927
  setToast("");
3936
3928
  }, 3e3);
3937
3929
  };
3938
- const visible = (0, import_react11.useMemo)(() => {
3939
- const today = dateKey(/* @__PURE__ */ new Date());
3940
- const end = weekEnd();
3941
- return tasks.filter((task) => {
3942
- const done2 = task.status === "COMPLETED" || task.status === "CANCELLED";
3943
- if (filter === "done") return done2;
3944
- if (done2) return false;
3945
- if (filter === "all") return true;
3930
+ const matchesFilter = (0, import_react11.useCallback)(
3931
+ (task) => {
3932
+ const today = dateKey(/* @__PURE__ */ new Date());
3933
+ const end = weekEnd();
3946
3934
  if (filter === "overdue") return task.due !== "" && task.due.slice(0, 10) < today;
3947
3935
  if (filter === "today") return task.due.slice(0, 10) === today;
3948
3936
  if (filter === "week") return task.due !== "" && task.due.slice(0, 10) <= end;
3949
3937
  return true;
3950
- });
3951
- }, [tasks, filter]);
3952
- const open = tasks.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED");
3953
- const done = tasks.length - open.length;
3954
- const progress = tasks.length === 0 ? 0 : Math.round(done / tasks.length * 100);
3955
- const byProject = (0, import_react11.useMemo)(() => {
3956
- const groups = /* @__PURE__ */ new Map();
3957
- for (const task of tasks) {
3958
- const key = task.projectId ?? "";
3959
- const group = groups.get(key) ?? {
3960
- project: projects.find((project) => project.id === key) ?? null,
3961
- total: 0,
3962
- done: 0
3963
- };
3964
- group.total += 1;
3965
- if (task.status === "COMPLETED" || task.status === "CANCELLED") group.done += 1;
3966
- groups.set(key, group);
3967
- }
3968
- return [...groups.values()].filter((group) => group.project !== null);
3969
- }, [tasks, projects]);
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;
3970
3945
  const switchAccount = async (id) => {
3971
3946
  try {
3972
3947
  await api.activateAccount(id);
@@ -3975,10 +3950,10 @@ function TasksPanel() {
3975
3950
  setError(errorText7(caught));
3976
3951
  }
3977
3952
  };
3978
- const toggle = async (task) => {
3953
+ const move = async (task, status) => {
3954
+ if (task.status === status) return;
3979
3955
  try {
3980
- const next = task.status === "COMPLETED" ? "NEEDS-ACTION" : "COMPLETED";
3981
- const { task: updated } = await api.updateTask(task.id, { status: next });
3956
+ const { task: updated } = await api.updateTask(task.id, { status });
3982
3957
  setTasks((current) => current.map((item) => item.id === task.id ? updated : item));
3983
3958
  } catch (caught) {
3984
3959
  setError(errorText7(caught));
@@ -3988,6 +3963,7 @@ function TasksPanel() {
3988
3963
  try {
3989
3964
  await api.deleteTask(task.id);
3990
3965
  setTasks((current) => current.filter((item) => item.id !== task.id));
3966
+ notify("\u4EFB\u52A1\u5DF2\u5220\u9664");
3991
3967
  } catch (caught) {
3992
3968
  setError(errorText7(caught));
3993
3969
  }
@@ -4002,21 +3978,8 @@ function TasksPanel() {
4002
3978
  setError(errorText7(caught));
4003
3979
  }
4004
3980
  };
4005
- const sync = async () => {
4006
- if (accountId === null) return;
4007
- setBusy(true);
4008
- setError("");
4009
- try {
4010
- const result = await api.syncTasks(accountId);
4011
- notify(`\u5DF2\u540C\u6B65\uFF1A\u62C9\u53D6 ${String(result.result.pulled)}\uFF0C\u63A8\u9001 ${String(result.result.pushed)}`);
4012
- refresh();
4013
- } catch (caught) {
4014
- setError(errorText7(caught));
4015
- } finally {
4016
- setBusy(false);
4017
- }
4018
- };
4019
3981
  const save = async (draft) => {
3982
+ if (accountId === null) return;
4020
3983
  setBusy(true);
4021
3984
  setError("");
4022
3985
  try {
@@ -4043,7 +4006,8 @@ function TasksPanel() {
4043
4006
  });
4044
4007
  }
4045
4008
  setEditing(null);
4046
- setCreating(false);
4009
+ setCreating(null);
4010
+ notify("\u4EFB\u52A1\u5DF2\u4FDD\u5B58");
4047
4011
  refresh();
4048
4012
  } catch (caught) {
4049
4013
  setError(errorText7(caught));
@@ -4053,7 +4017,7 @@ function TasksPanel() {
4053
4017
  };
4054
4018
  return (0, import_react11.createElement)(
4055
4019
  "div",
4056
- { className: "mailharbor" },
4020
+ { className: "mailharbor mailharbor-tasks" },
4057
4021
  (0, import_react11.createElement)(
4058
4022
  "div",
4059
4023
  { className: "mailharbor-topbar" },
@@ -4071,72 +4035,62 @@ function TasksPanel() {
4071
4035
  )
4072
4036
  ),
4073
4037
  (0, import_react11.createElement)("span", { className: "spacer" }),
4074
- calendar === null ? null : (0, import_react11.createElement)(
4075
- "span",
4076
- { className: "small muted" },
4077
- calendar.lastSync === "" ? "\u672A\u540C\u6B65" : `\u540C\u6B65\u4E8E ${new Date(calendar.lastSync).toLocaleString()}`
4078
- ),
4079
- (0, import_react11.createElement)(
4080
- "button",
4081
- { className: "ghost", onClick: () => void sync(), disabled: busy },
4082
- busy ? "\u540C\u6B65\u4E2D\u2026" : "\u540C\u6B65 CalDAV"
4083
- ),
4084
4038
  (0, import_react11.createElement)(
4085
4039
  "button",
4086
- { className: "primary", onClick: () => setCreating(true) },
4040
+ { className: "primary", onClick: () => setCreating("NEEDS-ACTION") },
4087
4041
  (0, import_react11.createElement)(Icon, { name: "plus" }),
4088
4042
  "\u65B0\u5EFA\u4EFB\u52A1"
4089
4043
  )
4090
4044
  ),
4091
- 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),
4092
4046
  error === "" ? null : (0, import_react11.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
4093
4047
  (0, import_react11.createElement)(
4094
4048
  "div",
4095
- { className: "tasks-body" },
4049
+ { className: "tasks-board-wrap" },
4096
4050
  (0, import_react11.createElement)(
4097
4051
  "div",
4098
- { className: "tasks-overview" },
4052
+ { className: "tasks-summary" },
4099
4053
  (0, import_react11.createElement)(
4100
4054
  "div",
4101
- { className: "tasks-progress" },
4055
+ { className: "tasks-summary-main" },
4102
4056
  (0, import_react11.createElement)(
4103
4057
  "div",
4104
- { className: "tasks-progress-head" },
4058
+ { className: "tasks-summary-labels" },
4105
4059
  (0, import_react11.createElement)("strong", null, "\u603B\u4F53\u8FDB\u5EA6"),
4106
4060
  (0, import_react11.createElement)(
4107
4061
  "span",
4108
4062
  { className: "muted small" },
4109
- `${String(done)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
4063
+ `${String(doneCount)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
4110
4064
  )
4111
4065
  ),
4112
- (0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } })),
4113
- (0, import_react11.createElement)("div", { className: "small muted" }, `\u5F85\u529E ${String(open.length)} \u6761`)
4066
+ (0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } }))
4114
4067
  ),
4115
- ...byProject.map(
4116
- (group) => (0, import_react11.createElement)(
4117
- "div",
4118
- { key: group.project?.id ?? "none", className: "tasks-project" },
4119
- (0, import_react11.createElement)(
4120
- "div",
4121
- { className: "tasks-project-head" },
4122
- (0, import_react11.createElement)("span", { className: "name" }, group.project?.name ?? ""),
4123
- (0, import_react11.createElement)("span", { className: "muted small" }, `${String(group.done)}/${String(group.total)}`)
4124
- ),
4125
- (0, import_react11.createElement)(
4126
- "div",
4127
- { className: "tasks-bar small" },
4128
- (0, import_react11.createElement)("span", {
4129
- style: {
4130
- width: `${String(group.total === 0 ? 0 : Math.round(group.done / group.total * 100))}%`
4131
- }
4132
- })
4133
- )
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")
4134
4088
  )
4135
4089
  )
4136
4090
  ),
4137
4091
  (0, import_react11.createElement)(
4138
4092
  "div",
4139
- { className: "tasks-list-wrap" },
4093
+ { className: "tasks-toolbar" },
4140
4094
  (0, import_react11.createElement)(
4141
4095
  "div",
4142
4096
  { className: "tasks-filters" },
@@ -4152,73 +4106,128 @@ function TasksPanel() {
4152
4106
  )
4153
4107
  )
4154
4108
  ),
4155
- visible.length === 0 ? (0, import_react11.createElement)(
4156
- "div",
4157
- { className: "mailharbor-empty" },
4158
- (0, import_react11.createElement)(Icon, { name: "checklist", size: 28 }),
4159
- (0, import_react11.createElement)("div", { className: "title" }, "\u6CA1\u6709\u4EFB\u52A1"),
4160
- (0, import_react11.createElement)("div", { className: "small" }, "\u53EF\u4EE5\u5728\u8FD9\u91CC\u65B0\u5EFA\uFF0C\u6216\u5728\u90AE\u4EF6\u91CC\u7528\u300C\u63D0\u53D6\u4EFB\u52A1\u300D\u3002")
4161
- ) : (0, import_react11.createElement)(
4162
- "div",
4163
- { className: "tasks-list" },
4164
- visible.map(
4165
- (task) => (0, import_react11.createElement)(
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)(
4166
4133
  "div",
4167
- { key: task.id, className: `tasks-row${task.status === "COMPLETED" ? " done" : ""}` },
4168
- (0, import_react11.createElement)("input", {
4169
- type: "checkbox",
4170
- checked: task.status === "COMPLETED",
4171
- "aria-label": "\u5B8C\u6210\u4EFB\u52A1",
4172
- onChange: () => void toggle(task)
4173
- }),
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" }),
4174
4139
  (0, import_react11.createElement)(
4175
4140
  "button",
4176
- { className: "tasks-title", onClick: () => setEditing(task) },
4177
- (0, import_react11.createElement)("span", null, task.title),
4178
- (0, import_react11.createElement)(
4179
- "span",
4180
- { className: "tasks-meta" },
4181
- task.due === "" ? null : (0, import_react11.createElement)(
4182
- "span",
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",
4183
4207
  {
4184
- 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
+ }
4185
4213
  },
4186
- dueText(task.due)
4187
- ),
4188
- task.owner === "" ? null : (0, import_react11.createElement)("span", null, task.owner),
4189
- (0, import_react11.createElement)("span", { className: "badge" }, STATUS_LABEL[task.status] ?? task.status),
4190
- task.projectId === null ? null : (0, import_react11.createElement)(
4191
- "span",
4192
- { className: "badge" },
4193
- projects.find((project) => project.id === task.projectId)?.name ?? "\u9879\u76EE"
4214
+ (0, import_react11.createElement)(Icon, { name: "mail", size: 12 }),
4215
+ "\u6765\u6E90\u90AE\u4EF6"
4194
4216
  )
4195
4217
  )
4196
- ),
4197
- task.messageKey === "" ? null : (0, import_react11.createElement)(
4198
- "button",
4199
- {
4200
- className: "subtle small",
4201
- title: "\u6253\u5F00\u6765\u6E90\u90AE\u4EF6",
4202
- onClick: () => void openSource(task)
4203
- },
4204
- "\u6765\u6E90"
4205
- ),
4206
- (0, import_react11.createElement)(
4207
- "button",
4208
- { className: "subtle icon", title: "\u5220\u9664", onClick: () => void remove(task) },
4209
- (0, import_react11.createElement)(Icon, { name: "trash", size: 13 })
4210
4218
  )
4211
4219
  )
4212
- )
4213
- )
4220
+ );
4221
+ })
4214
4222
  )
4215
4223
  ),
4216
- creating || editing !== null ? (0, import_react11.createElement)(TaskEditor, {
4224
+ creating !== null || editing !== null ? (0, import_react11.createElement)(TaskEditor, {
4217
4225
  task: editing,
4226
+ status: creating ?? "NEEDS-ACTION",
4218
4227
  projects,
4219
4228
  busy,
4220
4229
  onClose: () => {
4221
- setCreating(false);
4230
+ setCreating(null);
4222
4231
  setEditing(null);
4223
4232
  },
4224
4233
  onSave: (draft) => void save(draft)
@@ -4227,13 +4236,15 @@ function TasksPanel() {
4227
4236
  }
4228
4237
  function TaskEditor({
4229
4238
  task,
4239
+ status,
4230
4240
  projects,
4231
4241
  busy,
4232
4242
  onClose,
4233
4243
  onSave
4234
4244
  }) {
4235
4245
  const [draft, setDraft] = (0, import_react11.useState)({
4236
- ...EMPTY_TASK,
4246
+ ...EMPTY,
4247
+ status,
4237
4248
  ...task === null ? {} : {
4238
4249
  id: task.id,
4239
4250
  title: task.title,
@@ -4251,111 +4262,139 @@ function TaskEditor({
4251
4262
  {
4252
4263
  title: task === null ? "\u65B0\u5EFA\u4EFB\u52A1" : "\u7F16\u8F91\u4EFB\u52A1",
4253
4264
  onClose,
4265
+ wide: true,
4254
4266
  footer: (0, import_react11.createElement)(
4255
4267
  "div",
4256
- { 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" }),
4257
4271
  (0, import_react11.createElement)("button", { className: "ghost", onClick: onClose }, "\u53D6\u6D88"),
4258
4272
  (0, import_react11.createElement)(
4259
4273
  "button",
4260
- { className: "primary", disabled: busy || draft.title.trim() === "", onClick: () => onSave(draft) },
4261
- busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
4262
- )
4263
- )
4264
- },
4265
- (0, import_react11.createElement)(
4266
- "div",
4267
- { className: "mailharbor-field" },
4268
- (0, import_react11.createElement)("label", null, "\u6807\u9898"),
4269
- (0, import_react11.createElement)("input", {
4270
- value: draft.title,
4271
- autoFocus: true,
4272
- onChange: (event) => set({ title: event.target.value })
4273
- })
4274
- ),
4275
- (0, import_react11.createElement)(
4276
- "div",
4277
- { className: "mailharbor-field" },
4278
- (0, import_react11.createElement)("label", null, "\u5907\u6CE8"),
4279
- (0, import_react11.createElement)("textarea", {
4280
- rows: 3,
4281
- value: draft.notes,
4282
- onChange: (event) => set({ notes: event.target.value })
4283
- })
4284
- ),
4285
- (0, import_react11.createElement)(
4286
- "div",
4287
- { className: "mailharbor-grid" },
4288
- (0, import_react11.createElement)(
4289
- "div",
4290
- { className: "mailharbor-field" },
4291
- (0, import_react11.createElement)("label", null, "\u622A\u6B62\u65E5\u671F"),
4292
- (0, import_react11.createElement)("input", {
4293
- type: "date",
4294
- value: draft.due,
4295
- onChange: (event) => set({ due: event.target.value })
4296
- })
4297
- ),
4298
- (0, import_react11.createElement)(
4299
- "div",
4300
- { className: "mailharbor-field" },
4301
- (0, import_react11.createElement)("label", null, "\u4F18\u5148\u7EA7"),
4302
- (0, import_react11.createElement)(
4303
- "select",
4304
4274
  {
4305
- value: String(draft.priority),
4306
- onChange: (event) => set({ priority: Number(event.target.value) })
4275
+ className: "primary",
4276
+ disabled: busy || draft.title.trim() === "",
4277
+ onClick: () => onSave(draft)
4307
4278
  },
4308
- (0, import_react11.createElement)("option", { value: "0" }, "\u65E0"),
4309
- (0, import_react11.createElement)("option", { value: "5" }, "\u4E2D"),
4310
- (0, import_react11.createElement)("option", { value: "9" }, "\u9AD8")
4279
+ busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u4EFB\u52A1"
4311
4280
  )
4312
4281
  )
4313
- ),
4282
+ },
4314
4283
  (0, import_react11.createElement)(
4315
4284
  "div",
4316
- { className: "mailharbor-grid" },
4285
+ { className: "task-editor" },
4317
4286
  (0, import_react11.createElement)(
4318
4287
  "div",
4319
- { className: "mailharbor-field" },
4320
- (0, import_react11.createElement)("label", null, "\u72B6\u6001"),
4288
+ { className: "task-editor-main" },
4321
4289
  (0, import_react11.createElement)(
4322
- "select",
4323
- {
4324
- value: draft.status,
4325
- onChange: (event) => set({ status: event.target.value })
4326
- },
4327
- Object.entries(STATUS_LABEL).map(([value, label]) => (0, import_react11.createElement)("option", { key: value, value }, label))
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
+ )
4328
4319
  )
4329
4320
  ),
4330
4321
  (0, import_react11.createElement)(
4331
- "div",
4332
- { className: "mailharbor-field" },
4333
- (0, import_react11.createElement)("label", null, "\u9879\u76EE"),
4322
+ "aside",
4323
+ { className: "task-editor-side" },
4334
4324
  (0, import_react11.createElement)(
4335
- "select",
4336
- {
4337
- value: draft.projectId,
4338
- onChange: (event) => set({ projectId: event.target.value })
4339
- },
4340
- (0, import_react11.createElement)("option", { value: "" }, "\u65E0\u9879\u76EE"),
4341
- projects.map((project) => (0, import_react11.createElement)("option", { key: project.id, value: project.id }, project.name))
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
+ })
4342
4390
  )
4343
4391
  )
4344
- ),
4345
- (0, import_react11.createElement)(
4346
- "div",
4347
- { className: "mailharbor-field" },
4348
- (0, import_react11.createElement)("label", null, "\u8D1F\u8D23\u4EBA"),
4349
- (0, import_react11.createElement)("input", {
4350
- value: draft.owner,
4351
- onChange: (event) => set({ owner: event.target.value })
4352
- })
4353
4392
  )
4354
4393
  );
4355
4394
  }
4356
4395
 
4357
4396
  // src/client/index.tsx
4358
- var VERSION = "0.5.0";
4397
+ var VERSION = "0.6.0";
4359
4398
  var inject = ["slots"];
4360
4399
  var PANEL_ID = "mailharbor";
4361
4400
  var TASKS_PANEL_ID = "mailharbor-tasks";