@zaclinley/dsh-mailharbor 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +22 -19
- package/README.zh.md +22 -19
- package/lib/client.js +405 -438
- package/lib/client.js.map +3 -3
- package/lib/index.js +68 -472
- package/lib/index.js.map +4 -4
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -35,7 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
37
|
// src/client/styles.css
|
|
38
|
-
var styles_default = "/* MailHarbor panel + settings.\n Tokens come from the DSH shell (dark by default); fallbacks match the\n default dark theme so the panel also renders in a bare host page.\n Component rules are all scoped under `.mailharbor` and the generic element\n reset lives in `:where()` so it cannot outrank a component class. */\n\n.mailharbor {\n --m-bg: var(--dsw-alias-bg-base, #151517);\n --m-bg-1: var(--dsw-alias-bg-layer-1, #232324);\n --m-bg-2: var(--dsw-alias-bg-layer-2, #2c2c2e);\n --m-bg-3: var(--dsw-alias-bg-layer-3, #353638);\n --m-line: var(--dsw-alias-border-l2, #ffffff1f);\n --m-line-soft: var(--dsw-alias-border-l1, #ffffff0f);\n --m-line-strong: var(--dsw-alias-border-l3, #ffffff29);\n --m-text: var(--dsw-alias-label-primary, #f9fafb);\n --m-text-2: var(--dsw-alias-label-secondary, #cfd3d6);\n --m-text-3: var(--dsw-alias-label-tertiary, #adb2b8);\n --m-caption: var(--dsw-alias-label-caption, #81858c);\n --m-accent: #5686fe;\n --m-accent-soft: rgba(86, 134, 254, 0.14);\n --m-primary-bg: var(--dsw-alias-brand-primary, #f9fafb);\n --m-primary-fg: var(--dsw-alias-label-primary-foreground, #0f1115);\n --m-hover: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n --m-danger: #f25a5a;\n --m-ok: #3fbf7f;\n --m-warn: var(--dsw-alias-state-warn-label, #dd8629);\n --m-radius: 8px;\n --m-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);\n\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--m-bg);\n color: var(--m-text);\n font-size: 13px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n\n.mailharbor *,\n.mailharbor *::before,\n.mailharbor *::after {\n box-sizing: border-box;\n}\n\n/* ---------- base controls ---------- */\n\n.mailharbor :where(button) {\n font: inherit;\n color: inherit;\n background: transparent;\n border: 0;\n margin: 0;\n cursor: pointer;\n}\n\n.mailharbor button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 30px;\n padding: 0 12px;\n border-radius: var(--m-radius);\n border: 1px solid transparent;\n color: var(--m-text-2);\n font-size: 12.5px;\n font-weight: 500;\n white-space: nowrap;\n transition:\n background 120ms ease,\n color 120ms ease,\n border-color 120ms ease;\n}\n\n.mailharbor button:hover:not(:disabled) {\n background: var(--m-hover);\n color: var(--m-text);\n}\n\n.mailharbor button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n\n.mailharbor button.primary {\n background: var(--m-primary-bg);\n color: var(--m-primary-fg);\n border-color: transparent;\n}\n\n.mailharbor button.primary:hover:not(:disabled) {\n background: var(--m-primary-bg);\n filter: brightness(0.94);\n color: var(--m-primary-fg);\n}\n\n.mailharbor button.ghost {\n border-color: var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor button.ghost:hover:not(:disabled) {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor button.subtle {\n color: var(--m-text-3);\n padding: 0 8px;\n}\n\n.mailharbor button.icon {\n width: 30px;\n padding: 0;\n}\n\n.mailharbor button.danger {\n color: var(--m-danger);\n}\n\n.mailharbor input,\n.mailharbor select,\n.mailharbor textarea {\n font: inherit;\n color: var(--m-text);\n background: var(--m-bg-1);\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n padding: 0 10px;\n height: 32px;\n min-width: 0;\n}\n\n.mailharbor textarea {\n height: auto;\n padding: 8px 10px;\n resize: vertical;\n line-height: 1.6;\n}\n\n.mailharbor select {\n padding-right: 26px;\n appearance: none;\n background-image:\n linear-gradient(45deg, transparent 50%, var(--m-text-3) 50%),\n linear-gradient(135deg, var(--m-text-3) 50%, transparent 50%);\n background-position:\n calc(100% - 15px) 14px,\n calc(100% - 10px) 14px;\n background-size:\n 5px 5px,\n 5px 5px;\n background-repeat: no-repeat;\n}\n\n.mailharbor ::placeholder {\n color: var(--m-caption);\n}\n\n.mailharbor input:focus-visible,\n.mailharbor select:focus-visible,\n.mailharbor textarea:focus-visible,\n.mailharbor button:focus-visible,\n.mailharbor [contenteditable='true']:focus-visible {\n outline: 2px solid rgba(86, 134, 254, 0.55);\n outline-offset: -1px;\n}\n\n.mailharbor .spacer {\n flex: 1;\n}\n\n.mailharbor .muted {\n color: var(--m-text-3);\n}\n\n.mailharbor .small {\n font-size: 12px;\n}\n\n.mailharbor .badge {\n display: inline-flex;\n align-items: center;\n height: 18px;\n padding: 0 6px;\n border-radius: 999px;\n font-size: 10.5px;\n font-weight: 500;\n background: var(--m-bg-3);\n color: var(--m-text-2);\n}\n\n.mailharbor .badge.ok {\n background: rgba(63, 191, 127, 0.16);\n color: var(--m-ok);\n}\n\n.mailharbor .badge.warn {\n background: rgba(221, 134, 41, 0.16);\n color: var(--m-warn);\n}\n\n.mailharbor .error {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n color: var(--m-danger);\n background: rgba(242, 90, 90, 0.1);\n border-bottom: 1px solid rgba(242, 90, 90, 0.2);\n font-size: 12.5px;\n}\n\n/* ---------- shell ---------- */\n\n.mailharbor-topbar {\n display: flex;\n align-items: center;\n gap: 10px;\n height: 52px;\n padding: 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n flex: none;\n}\n\n.mailharbor-account {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n max-width: 220px;\n padding: 4px 8px 4px 4px;\n border-radius: 999px;\n border: 1px solid transparent;\n cursor: default;\n}\n\n.mailharbor-account:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-account .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 500;\n}\n\n.mailharbor-account select {\n height: 26px;\n background: transparent;\n border-color: transparent;\n font-weight: 500;\n}\n\n.mailharbor-body {\n display: flex;\n flex: 1;\n min-height: 0;\n position: relative;\n}\n\n.mailharbor button.active {\n background: var(--m-accent-soft);\n color: var(--m-text);\n}\n\n.mailharbor-list {\n display: flex;\n flex-direction: column;\n width: 360px;\n flex: none;\n border-right: 1px solid var(--m-line-soft);\n min-height: 0;\n}\n\n.mailharbor-reader {\n flex: 1;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n@media (max-width: 1000px) {\n .mailharbor-list {\n width: 300px;\n }\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='reader'] .mailharbor-list {\n display: none;\n}\n\n.mailharbor-narrow .mailharbor-body[data-mode='list'] .mailharbor-reader {\n display: none;\n}\n\n/* ---------- segmented control ---------- */\n\n.mailharbor-segment {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 2px;\n border-radius: 9px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-segment button {\n height: 26px;\n padding: 0 12px;\n border-radius: 7px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-segment button:hover:not(.active) {\n color: var(--m-text-2);\n background: transparent;\n}\n\n.mailharbor-segment button.active {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n/* ---------- list ---------- */\n\n.mailharbor-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-filters select {\n width: 128px;\n flex: none;\n font-size: 12px;\n}\n\n.mailharbor-filters input[type='search'] {\n flex: 1;\n height: 30px;\n font-size: 12.5px;\n}\n\n.mailharbor-rows {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n.mailharbor-row {\n display: flex;\n flex-direction: column;\n gap: 3px;\n width: 100%;\n padding: 10px 14px;\n border: 0;\n border-bottom: 1px solid var(--m-line-soft);\n border-left: 2px solid transparent;\n border-radius: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n user-select: none;\n}\n\n.mailharbor-row:hover {\n background: var(--m-hover);\n}\n\n.mailharbor-row.selected {\n background: var(--m-accent-soft);\n border-left-color: var(--m-accent);\n}\n\n.mailharbor-row .line-1,\n.mailharbor-row .line-2 {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n width: 100%;\n}\n\n.mailharbor-row .from {\n font-weight: 600;\n font-size: 12.5px;\n color: var(--m-text-2);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-row .date {\n margin-left: auto;\n color: var(--m-caption);\n font-size: 11px;\n flex: none;\n}\n\n.mailharbor-row .subject {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n.mailharbor-row .snippet {\n color: var(--m-caption);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n width: 100%;\n}\n\n.mailharbor-row.unread .from,\n.mailharbor-row.unread .subject {\n color: var(--m-text);\n}\n\n.mailharbor-row .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--m-accent);\n flex: none;\n}\n\n.mailharbor-row .paperclip {\n color: var(--m-caption);\n flex: none;\n display: inline-flex;\n}\n\n.mailharbor-list-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-top: 1px solid var(--m-line-soft);\n color: var(--m-text-3);\n font-size: 11.5px;\n flex: none;\n}\n\n.mailharbor-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n padding: 48px 24px;\n color: var(--m-text-3);\n text-align: center;\n}\n\n.mailharbor-empty svg {\n opacity: 0.5;\n}\n\n.mailharbor-empty .title {\n color: var(--m-text-2);\n font-size: 13px;\n font-weight: 500;\n}\n\n/* ---------- reader ---------- */\n\n.mailharbor-reader-head {\n padding: 16px 20px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mailharbor-reader-title {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.mailharbor-reader-title h2 {\n margin: 0;\n font-size: 17px;\n line-height: 1.35;\n font-weight: 600;\n letter-spacing: -0.01em;\n word-break: break-word;\n}\n\n.mailharbor-reader-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 10px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor-reader-meta .who {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: var(--m-text-2);\n}\n\n.mailharbor-reader-meta .who strong {\n color: var(--m-text);\n font-weight: 600;\n}\n\n.mailharbor-reader-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.mailharbor-reader-body {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 14px 18px 18px;\n background: var(--m-bg-2);\n}\n\n.mailharbor-reader-body iframe {\n flex: 1;\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.mailharbor-reader-body pre {\n flex: 1;\n overflow: auto;\n margin: 0;\n padding: 20px 22px;\n white-space: pre-wrap;\n word-break: break-word;\n background: #fff;\n border-radius: 10px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n color: #171a21;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12.5px;\n line-height: 1.7;\n}\n\n.mailharbor-attachments {\n padding: 10px 18px;\n border-top: 1px solid var(--m-line-soft);\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n flex: none;\n background: var(--m-bg);\n}\n\n.mailharbor-attachment {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 30px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: var(--m-radius);\n font-size: 12px;\n text-decoration: none;\n color: var(--m-text-2);\n max-width: 260px;\n background: var(--m-bg-1);\n}\n\n.mailharbor-attachment:hover {\n border-color: var(--m-line-strong);\n color: var(--m-text);\n}\n\n.mailharbor-attachment .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ---------- AI rail ---------- */\n\n.mailharbor-ai {\n display: flex;\n flex-direction: column;\n width: 340px;\n flex: none;\n min-height: 0;\n border-left: 1px solid var(--m-line-soft);\n background: var(--m-bg);\n}\n\n.mailharbor-ai-head {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 46px;\n padding: 0 8px 0 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-head strong {\n font-size: 13px;\n font-weight: 600;\n}\n\n.mailharbor-ai-confirm {\n display: inline-flex;\n gap: 2px;\n}\n\n.mailharbor-ai-context {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 9px 14px 10px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-context strong {\n font-size: 12.5px;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mailharbor-ai-warn {\n padding: 8px 14px;\n color: var(--m-warn);\n background: rgba(221, 134, 41, 0.1);\n font-size: 12px;\n flex: none;\n}\n\n.mailharbor-ai-quick {\n display: flex;\n gap: 6px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-quick button {\n flex: 1;\n padding: 0 6px;\n}\n\n.mailharbor-ai-list {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n}\n\n.mailharbor-ai-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 30px 12px;\n text-align: center;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-empty .title {\n color: var(--m-text-2);\n font-weight: 500;\n font-size: 12.5px;\n}\n\n.mailharbor-ai-card {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n overflow: hidden;\n}\n\n.mailharbor-ai-card.generating {\n opacity: 0.7;\n}\n\n.mailharbor-ai-card-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 8px 7px 12px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-ai-card-head > span:first-child {\n color: var(--m-text-2);\n font-weight: 600;\n}\n\n.mailharbor-ai-summary {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 10px 12px;\n}\n\n.mailharbor-ai-summary section h4 {\n margin: 0 0 3px;\n font-size: 11px;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n\n.mailharbor-ai-summary section p {\n margin: 0;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-text {\n padding: 10px 12px;\n max-height: 320px;\n overflow: auto;\n white-space: pre-wrap;\n font-size: 12.5px;\n line-height: 1.65;\n color: var(--m-text);\n}\n\n.mailharbor-ai-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n padding: 6px 8px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-ai-card-actions button {\n height: 26px;\n padding: 0 8px;\n font-size: 11.5px;\n}\n\n.mailharbor-ai-ask {\n display: flex;\n gap: 6px;\n padding: 10px 12px;\n border-top: 1px solid var(--m-line-soft);\n flex: none;\n}\n\n.mailharbor-ai-ask input {\n flex: 1;\n height: 32px;\n}\n\n.mailharbor-ai-error {\n color: var(--m-danger);\n font-size: 12px;\n padding: 0 2px;\n}\n\n@media (max-width: 1280px) {\n .mailharbor-ai {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(420px, 94%);\n z-index: 6;\n border-left: 1px solid var(--m-line);\n box-shadow: -12px 0 40px rgba(0, 0, 0, 0.45);\n }\n}\n\n/* ---------- AI summary ---------- */\n\n.mailharbor-divider {\n width: 1px;\n height: 18px;\n margin: 0 4px;\n background: var(--m-line);\n}\n\n.mailharbor-summary {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.mailharbor-summary section h4 {\n margin: 0 0 6px;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-summary section p {\n margin: 0;\n white-space: pre-wrap;\n line-height: 1.75;\n color: var(--m-text);\n}\n\n/* ---------- modal / forms ---------- */\n\n.mailharbor-portal {\n position: fixed;\n inset: 0;\n z-index: 80;\n height: auto;\n background: transparent;\n pointer-events: none;\n}\n\n.mailharbor-portal .mailharbor-modal-mask {\n pointer-events: auto;\n}\n\n.mailharbor-modal-mask {\n position: fixed;\n inset: 0;\n z-index: 60;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: rgba(0, 0, 0, 0.55);\n backdrop-filter: blur(2px);\n}\n\n.mailharbor-modal {\n display: flex;\n flex-direction: column;\n width: min(720px, 100%);\n max-height: 86vh;\n overflow: hidden;\n border: 1px solid var(--m-line);\n border-radius: 14px;\n background: var(--m-bg-1);\n color: var(--m-text);\n box-shadow: var(--m-shadow);\n}\n\n.mailharbor-modal > header {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 52px;\n padding: 0 14px 0 18px;\n border-bottom: 1px solid var(--m-line-soft);\n font-size: 14px;\n font-weight: 600;\n}\n\n.mailharbor-modal .content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 18px;\n overflow: auto;\n}\n\n.mailharbor-modal > footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 18px;\n border-top: 1px solid var(--m-line-soft);\n}\n\n.mailharbor-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.mailharbor-field > input,\n.mailharbor-field > select,\n.mailharbor-field > textarea,\n.mailharbor-card > textarea {\n width: 100%;\n min-width: 0;\n}\n\n.mailharbor-field > label {\n font-size: 11.5px;\n color: var(--m-text-3);\n}\n\n.mailharbor-grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 10px;\n min-width: 0;\n}\n\n.mailharbor-grid.three {\n grid-template-columns: minmax(0, 2fr) minmax(72px, 90px) auto;\n align-items: end;\n min-width: 0;\n}\n\n.mailharbor-toggle {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n color: var(--m-text-2);\n font-size: 12.5px;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.mailharbor-toggle input[type='checkbox'] {\n width: 14px;\n height: 14px;\n accent-color: var(--m-accent);\n}\n\n/* ---------- composer ---------- */\n\n.mailharbor-toolbar {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-wrap: wrap;\n padding: 6px;\n border: 1px solid var(--m-line-soft);\n border-bottom: 0;\n border-radius: 10px 10px 0 0;\n background: var(--m-bg-2);\n}\n\n.mailharbor-toolbar button {\n height: 26px;\n padding: 0 9px;\n border-radius: 6px;\n color: var(--m-text-2);\n font-size: 12px;\n}\n\n.mailharbor-toolbar button:hover {\n background: var(--m-bg-3);\n color: var(--m-text);\n}\n\n.mailharbor-editor {\n min-height: 240px;\n max-height: 44vh;\n overflow: auto;\n padding: 14px 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 0 0 10px 10px;\n background: #fff;\n color: #171a21;\n outline: none;\n font-size: 13.5px;\n line-height: 1.7;\n}\n\n.mailharbor-editor:empty::before {\n content: attr(data-placeholder);\n color: #9aa0ab;\n}\n\n/* ---------- inline confirm ---------- */\n\n.mailharbor-confirm {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 8px;\n border: 1px solid var(--m-line);\n background: var(--m-bg-2);\n color: var(--m-text-2);\n font-size: 12.5px;\n}\n\n/* ---------- selection / extraction ---------- */\n\n.mailharbor-selectbar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n margin: 0 12px 8px;\n border: 1px solid var(--m-accent-soft);\n border-radius: 9px;\n background: var(--m-accent-soft);\n}\n\n.mailharbor-selectbar select {\n height: 28px;\n max-width: 200px;\n}\n\n.mailharbor-row.selectable input[type='checkbox'] {\n width: 15px;\n height: 15px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 9px 10px;\n border: 1px solid var(--m-line-soft);\n border-radius: 9px;\n background: var(--m-bg-1);\n cursor: pointer;\n}\n\n.mailharbor-extract-row input {\n margin-top: 2px;\n accent-color: var(--m-accent);\n}\n\n.mailharbor-extract-row > span {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n gap: 6px 12px;\n margin: 0;\n font-size: 12.5px;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n overflow-wrap: anywhere;\n}\n\n/* ---------- tasks panel ---------- */\n\n.mailharbor .chip {\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 12px;\n}\n\n.mailharbor .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n.tasks-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: flex;\n flex-direction: column;\n gap: 18px;\n padding: 18px 20px 48px;\n width: 100%;\n max-width: 940px;\n margin: 0 auto;\n}\n\n.tasks-overview {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));\n gap: 12px;\n}\n\n.tasks-progress,\n.tasks-project {\n display: flex;\n flex-direction: column;\n gap: 7px;\n padding: 12px 14px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n min-width: 0;\n}\n\n.tasks-progress-head,\n.tasks-project-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n min-width: 0;\n}\n\n.tasks-project-head .name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12.5px;\n font-weight: 500;\n}\n\n.tasks-bar {\n height: 6px;\n border-radius: 999px;\n background: var(--m-bg-3);\n overflow: hidden;\n}\n\n.tasks-bar span {\n display: block;\n height: 100%;\n border-radius: 999px;\n background: var(--m-accent);\n transition: width 200ms ease;\n}\n\n.tasks-bar.small {\n height: 4px;\n}\n\n.tasks-filters {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.tasks-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.tasks-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 9px 12px;\n border: 1px solid var(--m-line-soft);\n border-radius: 10px;\n background: var(--m-bg-1);\n}\n\n.tasks-row input[type='checkbox'] {\n width: 16px;\n height: 16px;\n flex: none;\n accent-color: var(--m-accent);\n}\n\n.tasks-row .tasks-title {\n flex: 1;\n min-width: 0;\n height: auto;\n padding: 0;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n text-align: left;\n border: 0;\n background: transparent;\n}\n\n.tasks-row .tasks-title:hover {\n background: transparent;\n}\n\n.tasks-row .tasks-title > span:first-child {\n font-size: 13px;\n color: var(--m-text);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n}\n\n.tasks-row.done .tasks-title > span:first-child {\n color: var(--m-text-3);\n text-decoration: line-through;\n}\n\n.tasks-meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--m-text-3);\n}\n\n.tasks-meta .due.overdue {\n color: var(--m-danger);\n}\n\n.tasks-list-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n/* ---------- markdown ---------- */\n\n.mailharbor-markdown {\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 12.5px;\n line-height: 1.68;\n overflow-wrap: anywhere;\n}\n\n.mailharbor-markdown > *:first-child {\n margin-top: 0;\n}\n\n.mailharbor-markdown > *:last-child {\n margin-bottom: 0;\n}\n\n.mailharbor-markdown p {\n margin: 0;\n white-space: pre-wrap;\n}\n\n.mailharbor-markdown h1,\n.mailharbor-markdown h2,\n.mailharbor-markdown h3,\n.mailharbor-markdown h4,\n.mailharbor-markdown h5,\n.mailharbor-markdown h6 {\n margin: 4px 0 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--m-text);\n}\n\n.mailharbor-markdown ul,\n.mailharbor-markdown ol {\n margin: 0;\n padding-left: 20px;\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n\n.mailharbor-markdown li::marker {\n color: var(--m-text-3);\n}\n\n.mailharbor-markdown a {\n color: var(--m-accent);\n text-decoration: none;\n}\n\n.mailharbor-markdown a:hover {\n text-decoration: underline;\n}\n\n.mailharbor-markdown code {\n padding: 1px 4px;\n border-radius: 4px;\n background: var(--m-bg-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px;\n}\n\n.mailharbor-markdown pre {\n margin: 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n overflow: auto;\n}\n\n.mailharbor-markdown pre code {\n padding: 0;\n background: transparent;\n}\n\n.mailharbor-markdown blockquote {\n margin: 0;\n padding-left: 10px;\n border-left: 2px solid var(--m-line);\n color: var(--m-text-2);\n}\n\n.mailharbor-markdown hr {\n width: 100%;\n margin: 4px 0;\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n}\n\n/* ---------- composer AI ---------- */\n\n.mailharbor-toolbar button.ai {\n color: var(--m-accent);\n}\n\n.mailharbor-compose-foot {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n flex-wrap: wrap;\n}\n\n.mailharbor-versions {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n padding-top: 8px;\n}\n\n.mailharbor-versions .chip {\n height: 22px;\n padding: 0 8px;\n border: 1px solid var(--m-line);\n border-radius: 999px;\n color: var(--m-text-3);\n font-size: 11px;\n}\n\n.mailharbor-versions .chip.active {\n background: var(--m-accent-soft);\n border-color: transparent;\n color: var(--m-text);\n}\n\n/* ---------- settings ---------- */\n\n.mailharbor.mailharbor-settings-page {\n height: auto;\n min-height: 100%;\n overflow: visible;\n}\n\n.mailharbor-settings {\n flex: 0 0 auto;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n padding: 18px 20px 48px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n overflow-x: hidden;\n}\n\n.mailharbor-settings > * {\n min-width: 0;\n}\n\n.mailharbor-settings h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n\n.mailharbor-settings h3 {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--m-text-3);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n.mailharbor-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--m-line-soft);\n border-radius: 12px;\n background: var(--m-bg-1);\n min-width: 0;\n overflow: hidden;\n}\n\n.mailharbor-account-row {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.mailharbor-account-row .meta {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.mailharbor-account-row .meta strong {\n font-weight: 600;\n}\n\n.mailharbor-account-row .meta span {\n color: var(--m-text-3);\n font-size: 11.5px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.mailharbor .avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n border-radius: 50%;\n background: var(--m-accent-soft);\n color: var(--m-accent);\n font-weight: 600;\n}\n\n.mailharbor-log {\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--m-bg);\n border: 1px solid var(--m-line-soft);\n color: var(--m-text-2);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n line-height: 1.6;\n max-height: 220px;\n overflow: auto;\n white-space: pre-wrap;\n}\n\n.mailharbor-kv {\n display: grid;\n grid-template-columns: minmax(120px, 170px) 1fr;\n gap: 6px 14px;\n font-size: 12px;\n margin: 0;\n}\n\n.mailharbor-kv dt {\n color: var(--m-text-3);\n}\n\n.mailharbor-kv dd {\n margin: 0;\n color: var(--m-text-2);\n word-break: break-all;\n}\n\n.mailharbor hr {\n border: 0;\n border-top: 1px solid var(--m-line-soft);\n margin: 2px 0;\n}\n";
|
|
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)("
|
|
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-
|
|
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
|
-
|
|
2566
|
-
"
|
|
2567
|
-
{
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
"
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
)
|
|
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
|
-
"
|
|
2657
|
-
{ className: "mailharbor-
|
|
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-
|
|
2661
|
-
|
|
2662
|
-
|
|
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
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
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
|
-
|
|
2672
|
-
|
|
2673
|
-
)
|
|
2674
|
-
)
|
|
2748
|
+
(0, import_react9.createElement)(Icon, { name: folder.name === DRAFTS ? "compose" : "folder", size: 14 }),
|
|
2749
|
+
(0, import_react9.createElement)("span", { className: "name" }, folderName(folder.label ?? folder.name)),
|
|
2750
|
+
folder.count > 0 ? (0, import_react9.createElement)("span", { className: "count" }, String(folder.count)) : null
|
|
2751
|
+
)
|
|
2752
|
+
)
|
|
2753
|
+
),
|
|
2754
|
+
(0, import_react9.createElement)(
|
|
2755
|
+
"div",
|
|
2756
|
+
{ className: "mailharbor-list" },
|
|
2757
|
+
(0, import_react9.createElement)(
|
|
2758
|
+
"div",
|
|
2759
|
+
{ className: "mailharbor-filters" },
|
|
2675
2760
|
(0, import_react9.createElement)("input", {
|
|
2676
2761
|
type: "search",
|
|
2677
2762
|
placeholder: showingDrafts ? "\u641C\u7D22\u8349\u7A3F" : "\u641C\u7D22\u90AE\u4EF6",
|
|
@@ -2878,7 +2963,7 @@ function MailPanel() {
|
|
|
2878
2963
|
{ className: "badge" },
|
|
2879
2964
|
accounts.find((entry) => entry.id === item.accountId)?.label ?? ""
|
|
2880
2965
|
) : null,
|
|
2881
|
-
item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
|
|
2966
|
+
source === "local" && item.backedUp === false ? (0, import_react9.createElement)("span", { className: "badge warn" }, "\u672A\u5907\u4EFD") : null,
|
|
2882
2967
|
(0, import_react9.createElement)("span", { className: "date" }, shortDate(item.date))
|
|
2883
2968
|
),
|
|
2884
2969
|
(0, import_react9.createElement)(
|
|
@@ -3062,15 +3147,6 @@ function MailSettings() {
|
|
|
3062
3147
|
const [pollSeconds, setPollSeconds] = (0, import_react10.useState)(60);
|
|
3063
3148
|
const [listLimit, setListLimit] = (0, import_react10.useState)(40);
|
|
3064
3149
|
const [projectRoot, setProjectRoot] = (0, import_react10.useState)("");
|
|
3065
|
-
const [caldavAccountId, setCaldavAccountId] = (0, import_react10.useState)("");
|
|
3066
|
-
const [caldavUrl, setCaldavUrl] = (0, import_react10.useState)("");
|
|
3067
|
-
const [caldavUser, setCaldavUser] = (0, import_react10.useState)("");
|
|
3068
|
-
const [caldavPassword, setCaldavPassword] = (0, import_react10.useState)("");
|
|
3069
|
-
const [caldavHref, setCaldavHref] = (0, import_react10.useState)("");
|
|
3070
|
-
const [caldavHasPassword, setCaldavHasPassword] = (0, import_react10.useState)(false);
|
|
3071
|
-
const [caldavCalendar, setCaldavCalendar] = (0, import_react10.useState)(null);
|
|
3072
|
-
const [caldavMessage, setCaldavMessage] = (0, import_react10.useState)("");
|
|
3073
|
-
const [caldavBusy, setCaldavBusy] = (0, import_react10.useState)(false);
|
|
3074
3150
|
const [aiEnabled, setAiEnabled] = (0, import_react10.useState)(true);
|
|
3075
3151
|
const [aiProvider, setAiProvider] = (0, import_react10.useState)("deepseek-account");
|
|
3076
3152
|
const [aiModel, setAiModel] = (0, import_react10.useState)("deepseek-flash");
|
|
@@ -3094,9 +3170,6 @@ function MailSettings() {
|
|
|
3094
3170
|
setPollSeconds(view.read.pollSeconds);
|
|
3095
3171
|
setListLimit(view.render.listLimit);
|
|
3096
3172
|
setProjectRoot(view.projects.root);
|
|
3097
|
-
setCaldavAccountId(
|
|
3098
|
-
(current) => current || view.activeAccountId || appState.settings.activeAccountId || ""
|
|
3099
|
-
);
|
|
3100
3173
|
setAiEnabled(view.ai.enabled);
|
|
3101
3174
|
setAiProvider(view.ai.provider);
|
|
3102
3175
|
setAiModel(view.ai.model);
|
|
@@ -3107,23 +3180,6 @@ function MailSettings() {
|
|
|
3107
3180
|
cancelled = true;
|
|
3108
3181
|
};
|
|
3109
3182
|
}, [tick]);
|
|
3110
|
-
(0, import_react10.useEffect)(() => {
|
|
3111
|
-
let cancelled = false;
|
|
3112
|
-
if (caldavAccountId === "") return;
|
|
3113
|
-
api.taskCaldav(caldavAccountId).then((info) => {
|
|
3114
|
-
if (cancelled) return;
|
|
3115
|
-
setCaldavUrl(info.caldav?.url ?? "");
|
|
3116
|
-
setCaldavUser(info.caldav?.username ?? "");
|
|
3117
|
-
setCaldavHref(info.caldav?.calendarHref ?? "");
|
|
3118
|
-
setCaldavHasPassword(info.hasPassword);
|
|
3119
|
-
setCaldavCalendar(info.calendar);
|
|
3120
|
-
}).catch((caught) => {
|
|
3121
|
-
if (!cancelled) setCaldavMessage(errorText6(caught));
|
|
3122
|
-
});
|
|
3123
|
-
return () => {
|
|
3124
|
-
cancelled = true;
|
|
3125
|
-
};
|
|
3126
|
-
}, [caldavAccountId, tick]);
|
|
3127
3183
|
(0, import_react10.useEffect)(() => {
|
|
3128
3184
|
let cancelled = false;
|
|
3129
3185
|
const accountId = settings?.activeAccountId ?? state?.activeAccountId ?? null;
|
|
@@ -3319,139 +3375,6 @@ function MailSettings() {
|
|
|
3319
3375
|
)
|
|
3320
3376
|
)
|
|
3321
3377
|
),
|
|
3322
|
-
(0, import_react10.createElement)(
|
|
3323
|
-
"div",
|
|
3324
|
-
{ className: "mailharbor-card" },
|
|
3325
|
-
(0, import_react10.createElement)("h3", null, "\u4EFB\u52A1\u4E0E CalDAV"),
|
|
3326
|
-
(0, import_react10.createElement)(
|
|
3327
|
-
"p",
|
|
3328
|
-
{ className: "small muted" },
|
|
3329
|
-
"\u4EFB\u52A1\u9762\u677F\u7684\u300C\u540C\u6B65 CalDAV\u300D\u4F7F\u7528\u8FD9\u91CC\u7684\u914D\u7F6E\u3002\u5BC6\u7801\u53EA\u5B58\u7CFB\u7EDF Keychain\uFF0C\u4E0D\u5199\u5165\u914D\u7F6E\u6587\u4EF6\u3002"
|
|
3330
|
-
),
|
|
3331
|
-
(0, import_react10.createElement)(
|
|
3332
|
-
"div",
|
|
3333
|
-
{ className: "mailharbor-grid" },
|
|
3334
|
-
(0, import_react10.createElement)(
|
|
3335
|
-
Field,
|
|
3336
|
-
{ label: "\u8D26\u53F7" },
|
|
3337
|
-
(0, import_react10.createElement)(
|
|
3338
|
-
"select",
|
|
3339
|
-
{
|
|
3340
|
-
value: caldavAccountId,
|
|
3341
|
-
onChange: (event) => {
|
|
3342
|
-
setCaldavAccountId(event.target.value);
|
|
3343
|
-
setCaldavMessage("");
|
|
3344
|
-
}
|
|
3345
|
-
},
|
|
3346
|
-
(settings?.accounts ?? []).map(
|
|
3347
|
-
(item) => (0, import_react10.createElement)("option", { key: item.id, value: item.id }, `${item.label}\uFF08${item.email}\uFF09`)
|
|
3348
|
-
)
|
|
3349
|
-
)
|
|
3350
|
-
),
|
|
3351
|
-
(0, import_react10.createElement)(
|
|
3352
|
-
Field,
|
|
3353
|
-
{ label: "\u72B6\u6001" },
|
|
3354
|
-
(0, import_react10.createElement)(
|
|
3355
|
-
"span",
|
|
3356
|
-
{ className: "small", style: { paddingTop: "7px", color: "var(--m-text-3)" } },
|
|
3357
|
-
caldavCalendar === null ? "\u672A\u540C\u6B65" : caldavCalendar.lastSync === "" ? "\u672A\u540C\u6B65" : `\u4E0A\u6B21\u540C\u6B65 ${new Date(caldavCalendar.lastSync).toLocaleString()}`,
|
|
3358
|
-
caldavCalendar?.error ? ` \xB7 \u5931\u8D25\uFF1A${caldavCalendar.error}` : ""
|
|
3359
|
-
)
|
|
3360
|
-
)
|
|
3361
|
-
),
|
|
3362
|
-
(0, import_react10.createElement)(
|
|
3363
|
-
"div",
|
|
3364
|
-
{ className: "mailharbor-grid" },
|
|
3365
|
-
(0, import_react10.createElement)(
|
|
3366
|
-
Field,
|
|
3367
|
-
{ label: "CalDAV \u5730\u5740" },
|
|
3368
|
-
(0, import_react10.createElement)("input", {
|
|
3369
|
-
value: caldavUrl,
|
|
3370
|
-
placeholder: "https://caldav.example.com/",
|
|
3371
|
-
onChange: (event) => setCaldavUrl(event.target.value)
|
|
3372
|
-
})
|
|
3373
|
-
),
|
|
3374
|
-
(0, import_react10.createElement)(
|
|
3375
|
-
Field,
|
|
3376
|
-
{ label: "\u7528\u6237\u540D" },
|
|
3377
|
-
(0, import_react10.createElement)("input", {
|
|
3378
|
-
value: caldavUser,
|
|
3379
|
-
placeholder: "\u901A\u5E38\u662F\u90AE\u7BB1\u5730\u5740",
|
|
3380
|
-
onChange: (event) => setCaldavUser(event.target.value)
|
|
3381
|
-
})
|
|
3382
|
-
)
|
|
3383
|
-
),
|
|
3384
|
-
(0, import_react10.createElement)(
|
|
3385
|
-
"div",
|
|
3386
|
-
{ className: "mailharbor-grid" },
|
|
3387
|
-
(0, import_react10.createElement)(
|
|
3388
|
-
Field,
|
|
3389
|
-
{ label: caldavHasPassword ? "\u5E94\u7528\u5BC6\u7801\uFF08\u7559\u7A7A\u8868\u793A\u4E0D\u4FEE\u6539\uFF09" : "\u5E94\u7528\u5BC6\u7801" },
|
|
3390
|
-
(0, import_react10.createElement)("input", {
|
|
3391
|
-
type: "password",
|
|
3392
|
-
value: caldavPassword,
|
|
3393
|
-
placeholder: caldavHasPassword ? "\u5DF2\u4FDD\u5B58" : "iCloud / Fastmail \u7B49\u5E94\u7528\u4E13\u7528\u5BC6\u7801",
|
|
3394
|
-
onChange: (event) => setCaldavPassword(event.target.value)
|
|
3395
|
-
})
|
|
3396
|
-
),
|
|
3397
|
-
(0, import_react10.createElement)(
|
|
3398
|
-
Field,
|
|
3399
|
-
{ label: "\u4EFB\u52A1\u65E5\u5386\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09" },
|
|
3400
|
-
(0, import_react10.createElement)("input", {
|
|
3401
|
-
value: caldavHref,
|
|
3402
|
-
placeholder: "\u7559\u7A7A\u81EA\u52A8\u53D1\u73B0",
|
|
3403
|
-
onChange: (event) => setCaldavHref(event.target.value)
|
|
3404
|
-
})
|
|
3405
|
-
)
|
|
3406
|
-
),
|
|
3407
|
-
(0, import_react10.createElement)(
|
|
3408
|
-
"div",
|
|
3409
|
-
{ style: { display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" } },
|
|
3410
|
-
(0, import_react10.createElement)(
|
|
3411
|
-
"button",
|
|
3412
|
-
{
|
|
3413
|
-
className: "primary small",
|
|
3414
|
-
disabled: caldavBusy,
|
|
3415
|
-
onClick: () => {
|
|
3416
|
-
setCaldavBusy(true);
|
|
3417
|
-
setCaldavMessage("\u4FDD\u5B58\u4E2D\u2026");
|
|
3418
|
-
void api.saveTaskCaldav({
|
|
3419
|
-
accountId: caldavAccountId,
|
|
3420
|
-
url: caldavUrl,
|
|
3421
|
-
username: caldavUser,
|
|
3422
|
-
...caldavPassword === "" ? {} : { password: caldavPassword },
|
|
3423
|
-
calendarHref: caldavHref
|
|
3424
|
-
}).then((info) => {
|
|
3425
|
-
setCaldavHasPassword(info.hasPassword);
|
|
3426
|
-
setCaldavPassword("");
|
|
3427
|
-
setCaldavMessage(info.hasPassword ? "\u5DF2\u4FDD\u5B58" : "\u5DF2\u4FDD\u5B58\u5730\u5740\uFF0C\u4F46\u8FD8\u6CA1\u6709\u5BC6\u7801");
|
|
3428
|
-
setTick((value) => value + 1);
|
|
3429
|
-
}).catch((caught) => setCaldavMessage(errorText6(caught))).finally(() => setCaldavBusy(false));
|
|
3430
|
-
}
|
|
3431
|
-
},
|
|
3432
|
-
caldavBusy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58 CalDAV"
|
|
3433
|
-
),
|
|
3434
|
-
(0, import_react10.createElement)(
|
|
3435
|
-
"button",
|
|
3436
|
-
{
|
|
3437
|
-
className: "ghost small",
|
|
3438
|
-
disabled: caldavBusy,
|
|
3439
|
-
onClick: () => {
|
|
3440
|
-
setCaldavBusy(true);
|
|
3441
|
-
setCaldavMessage("\u540C\u6B65\u4E2D\u2026");
|
|
3442
|
-
void api.syncTasks(caldavAccountId).then((result) => {
|
|
3443
|
-
setCaldavMessage(
|
|
3444
|
-
`\u540C\u6B65\u5B8C\u6210\uFF1A\u62C9\u53D6 ${String(result.result.pulled)}\uFF0C\u63A8\u9001 ${String(result.result.pushed)}`
|
|
3445
|
-
);
|
|
3446
|
-
setTick((value) => value + 1);
|
|
3447
|
-
}).catch((caught) => setCaldavMessage(errorText6(caught))).finally(() => setCaldavBusy(false));
|
|
3448
|
-
}
|
|
3449
|
-
},
|
|
3450
|
-
"\u7ACB\u5373\u540C\u6B65"
|
|
3451
|
-
),
|
|
3452
|
-
(0, import_react10.createElement)("span", { className: "small muted" }, caldavMessage)
|
|
3453
|
-
)
|
|
3454
|
-
),
|
|
3455
3378
|
(0, import_react10.createElement)(
|
|
3456
3379
|
"div",
|
|
3457
3380
|
{ className: "mailharbor-card" },
|
|
@@ -3858,19 +3781,17 @@ function AccountRow({
|
|
|
3858
3781
|
// src/client/tasks-panel.tsx
|
|
3859
3782
|
var import_react11 = require("react");
|
|
3860
3783
|
var FILTERS = [
|
|
3861
|
-
{ id: "
|
|
3784
|
+
{ id: "all", label: "\u5168\u90E8" },
|
|
3862
3785
|
{ id: "today", label: "\u4ECA\u5929" },
|
|
3863
3786
|
{ id: "week", label: "\u672C\u5468" },
|
|
3864
|
-
{ id: "overdue", label: "\u903E\u671F" }
|
|
3865
|
-
{ id: "done", label: "\u5DF2\u5B8C\u6210" },
|
|
3866
|
-
{ id: "all", label: "\u5168\u90E8" }
|
|
3787
|
+
{ id: "overdue", label: "\u903E\u671F" }
|
|
3867
3788
|
];
|
|
3868
|
-
var
|
|
3869
|
-
"NEEDS-ACTION": "\u5F85\u5904\u7406",
|
|
3870
|
-
"IN-PROCESS": "\u8FDB\u884C\u4E2D",
|
|
3871
|
-
COMPLETED: "\u5DF2\u5B8C\u6210",
|
|
3872
|
-
|
|
3873
|
-
};
|
|
3789
|
+
var COLUMNS = [
|
|
3790
|
+
{ status: "NEEDS-ACTION", label: "\u5F85\u5904\u7406", hint: "\u8FD8\u6CA1\u5F00\u59CB" },
|
|
3791
|
+
{ status: "IN-PROCESS", label: "\u8FDB\u884C\u4E2D", hint: "\u6B63\u5728\u505A" },
|
|
3792
|
+
{ status: "COMPLETED", label: "\u5DF2\u5B8C\u6210", hint: "\u6536\u5C3E" }
|
|
3793
|
+
];
|
|
3794
|
+
var PRIORITY_LABEL = { 0: "\u65E0", 5: "\u4E2D", 9: "\u9AD8" };
|
|
3874
3795
|
function errorText7(error) {
|
|
3875
3796
|
return error instanceof ApiError || error instanceof Error ? error.message : String(error);
|
|
3876
3797
|
}
|
|
@@ -3886,11 +3807,13 @@ function weekEnd() {
|
|
|
3886
3807
|
}
|
|
3887
3808
|
function dueText(due) {
|
|
3888
3809
|
if (due === "") return "";
|
|
3810
|
+
const value = due.slice(0, 10);
|
|
3889
3811
|
const today = dateKey(/* @__PURE__ */ new Date());
|
|
3890
|
-
if (
|
|
3891
|
-
|
|
3812
|
+
if (value < today) return `\u903E\u671F ${value}`;
|
|
3813
|
+
if (value === today) return "\u4ECA\u5929";
|
|
3814
|
+
return value;
|
|
3892
3815
|
}
|
|
3893
|
-
var
|
|
3816
|
+
var EMPTY = {
|
|
3894
3817
|
title: "",
|
|
3895
3818
|
notes: "",
|
|
3896
3819
|
due: "",
|
|
@@ -3910,9 +3833,7 @@ function TasksIcon() {
|
|
|
3910
3833
|
if (accountId === null) return;
|
|
3911
3834
|
const result = await api.tasks(accountId);
|
|
3912
3835
|
if (!cancelled) {
|
|
3913
|
-
setCount(
|
|
3914
|
-
result.items.filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED").length
|
|
3915
|
-
);
|
|
3836
|
+
setCount(result.items.filter((task) => task.status !== "COMPLETED").length);
|
|
3916
3837
|
}
|
|
3917
3838
|
} catch {
|
|
3918
3839
|
}
|
|
@@ -3971,13 +3892,13 @@ function TasksPanel() {
|
|
|
3971
3892
|
const [state, setState] = (0, import_react11.useState)(null);
|
|
3972
3893
|
const [tasks, setTasks] = (0, import_react11.useState)([]);
|
|
3973
3894
|
const [projects, setProjects] = (0, import_react11.useState)([]);
|
|
3974
|
-
const [
|
|
3975
|
-
const [filter, setFilter] = (0, import_react11.useState)("open");
|
|
3895
|
+
const [filter, setFilter] = (0, import_react11.useState)("all");
|
|
3976
3896
|
const [error, setError] = (0, import_react11.useState)("");
|
|
3977
3897
|
const [toast, setToast] = (0, import_react11.useState)("");
|
|
3978
3898
|
const [busy, setBusy] = (0, import_react11.useState)(false);
|
|
3979
3899
|
const [editing, setEditing] = (0, import_react11.useState)(null);
|
|
3980
|
-
const [creating, setCreating] = (0, import_react11.useState)(
|
|
3900
|
+
const [creating, setCreating] = (0, import_react11.useState)(null);
|
|
3901
|
+
const [dragging, setDragging] = (0, import_react11.useState)(null);
|
|
3981
3902
|
const [tick, setTick] = (0, import_react11.useState)(0);
|
|
3982
3903
|
const accountId = state?.settings.activeAccountId ?? null;
|
|
3983
3904
|
(0, import_react11.useEffect)(() => {
|
|
@@ -3989,7 +3910,6 @@ function TasksPanel() {
|
|
|
3989
3910
|
Promise.all([api.tasks(accountId), api.projects()]).then(([list, projectList]) => {
|
|
3990
3911
|
if (cancelled) return;
|
|
3991
3912
|
setTasks(list.items);
|
|
3992
|
-
setCalendar(list.calendar);
|
|
3993
3913
|
setProjects(projectList.items);
|
|
3994
3914
|
}).catch((caught) => {
|
|
3995
3915
|
if (!cancelled) setError(errorText7(caught));
|
|
@@ -4007,38 +3927,21 @@ function TasksPanel() {
|
|
|
4007
3927
|
setToast("");
|
|
4008
3928
|
}, 3e3);
|
|
4009
3929
|
};
|
|
4010
|
-
const
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
const done2 = task.status === "COMPLETED" || task.status === "CANCELLED";
|
|
4015
|
-
if (filter === "done") return done2;
|
|
4016
|
-
if (done2) return false;
|
|
4017
|
-
if (filter === "all") return true;
|
|
3930
|
+
const matchesFilter = (0, import_react11.useCallback)(
|
|
3931
|
+
(task) => {
|
|
3932
|
+
const today = dateKey(/* @__PURE__ */ new Date());
|
|
3933
|
+
const end = weekEnd();
|
|
4018
3934
|
if (filter === "overdue") return task.due !== "" && task.due.slice(0, 10) < today;
|
|
4019
3935
|
if (filter === "today") return task.due.slice(0, 10) === today;
|
|
4020
3936
|
if (filter === "week") return task.due !== "" && task.due.slice(0, 10) <= end;
|
|
4021
3937
|
return true;
|
|
4022
|
-
}
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
const
|
|
4026
|
-
const
|
|
4027
|
-
const
|
|
4028
|
-
|
|
4029
|
-
for (const task of tasks) {
|
|
4030
|
-
const key = task.projectId ?? "";
|
|
4031
|
-
const group = groups.get(key) ?? {
|
|
4032
|
-
project: projects.find((project) => project.id === key) ?? null,
|
|
4033
|
-
total: 0,
|
|
4034
|
-
done: 0
|
|
4035
|
-
};
|
|
4036
|
-
group.total += 1;
|
|
4037
|
-
if (task.status === "COMPLETED" || task.status === "CANCELLED") group.done += 1;
|
|
4038
|
-
groups.set(key, group);
|
|
4039
|
-
}
|
|
4040
|
-
return [...groups.values()].filter((group) => group.project !== null);
|
|
4041
|
-
}, [tasks, projects]);
|
|
3938
|
+
},
|
|
3939
|
+
[filter]
|
|
3940
|
+
);
|
|
3941
|
+
const open = tasks.filter((task) => task.status !== "COMPLETED");
|
|
3942
|
+
const doneCount = tasks.length - open.length;
|
|
3943
|
+
const progress = tasks.length === 0 ? 0 : Math.round(doneCount / tasks.length * 100);
|
|
3944
|
+
const overdue = open.filter((task) => task.due !== "" && task.due.slice(0, 10) < dateKey(/* @__PURE__ */ new Date())).length;
|
|
4042
3945
|
const switchAccount = async (id) => {
|
|
4043
3946
|
try {
|
|
4044
3947
|
await api.activateAccount(id);
|
|
@@ -4047,10 +3950,10 @@ function TasksPanel() {
|
|
|
4047
3950
|
setError(errorText7(caught));
|
|
4048
3951
|
}
|
|
4049
3952
|
};
|
|
4050
|
-
const
|
|
3953
|
+
const move = async (task, status) => {
|
|
3954
|
+
if (task.status === status) return;
|
|
4051
3955
|
try {
|
|
4052
|
-
const
|
|
4053
|
-
const { task: updated } = await api.updateTask(task.id, { status: next });
|
|
3956
|
+
const { task: updated } = await api.updateTask(task.id, { status });
|
|
4054
3957
|
setTasks((current) => current.map((item) => item.id === task.id ? updated : item));
|
|
4055
3958
|
} catch (caught) {
|
|
4056
3959
|
setError(errorText7(caught));
|
|
@@ -4060,6 +3963,7 @@ function TasksPanel() {
|
|
|
4060
3963
|
try {
|
|
4061
3964
|
await api.deleteTask(task.id);
|
|
4062
3965
|
setTasks((current) => current.filter((item) => item.id !== task.id));
|
|
3966
|
+
notify("\u4EFB\u52A1\u5DF2\u5220\u9664");
|
|
4063
3967
|
} catch (caught) {
|
|
4064
3968
|
setError(errorText7(caught));
|
|
4065
3969
|
}
|
|
@@ -4074,21 +3978,8 @@ function TasksPanel() {
|
|
|
4074
3978
|
setError(errorText7(caught));
|
|
4075
3979
|
}
|
|
4076
3980
|
};
|
|
4077
|
-
const sync = async () => {
|
|
4078
|
-
if (accountId === null) return;
|
|
4079
|
-
setBusy(true);
|
|
4080
|
-
setError("");
|
|
4081
|
-
try {
|
|
4082
|
-
const result = await api.syncTasks(accountId);
|
|
4083
|
-
notify(`\u5DF2\u540C\u6B65\uFF1A\u62C9\u53D6 ${String(result.result.pulled)}\uFF0C\u63A8\u9001 ${String(result.result.pushed)}`);
|
|
4084
|
-
refresh();
|
|
4085
|
-
} catch (caught) {
|
|
4086
|
-
setError(errorText7(caught));
|
|
4087
|
-
} finally {
|
|
4088
|
-
setBusy(false);
|
|
4089
|
-
}
|
|
4090
|
-
};
|
|
4091
3981
|
const save = async (draft) => {
|
|
3982
|
+
if (accountId === null) return;
|
|
4092
3983
|
setBusy(true);
|
|
4093
3984
|
setError("");
|
|
4094
3985
|
try {
|
|
@@ -4115,7 +4006,8 @@ function TasksPanel() {
|
|
|
4115
4006
|
});
|
|
4116
4007
|
}
|
|
4117
4008
|
setEditing(null);
|
|
4118
|
-
setCreating(
|
|
4009
|
+
setCreating(null);
|
|
4010
|
+
notify("\u4EFB\u52A1\u5DF2\u4FDD\u5B58");
|
|
4119
4011
|
refresh();
|
|
4120
4012
|
} catch (caught) {
|
|
4121
4013
|
setError(errorText7(caught));
|
|
@@ -4125,7 +4017,7 @@ function TasksPanel() {
|
|
|
4125
4017
|
};
|
|
4126
4018
|
return (0, import_react11.createElement)(
|
|
4127
4019
|
"div",
|
|
4128
|
-
{ className: "mailharbor" },
|
|
4020
|
+
{ className: "mailharbor mailharbor-tasks" },
|
|
4129
4021
|
(0, import_react11.createElement)(
|
|
4130
4022
|
"div",
|
|
4131
4023
|
{ className: "mailharbor-topbar" },
|
|
@@ -4143,72 +4035,62 @@ function TasksPanel() {
|
|
|
4143
4035
|
)
|
|
4144
4036
|
),
|
|
4145
4037
|
(0, import_react11.createElement)("span", { className: "spacer" }),
|
|
4146
|
-
calendar === null ? null : (0, import_react11.createElement)(
|
|
4147
|
-
"span",
|
|
4148
|
-
{ className: "small muted" },
|
|
4149
|
-
calendar.lastSync === "" ? "\u672A\u540C\u6B65" : `\u540C\u6B65\u4E8E ${new Date(calendar.lastSync).toLocaleString()}`
|
|
4150
|
-
),
|
|
4151
|
-
(0, import_react11.createElement)(
|
|
4152
|
-
"button",
|
|
4153
|
-
{ className: "ghost", onClick: () => void sync(), disabled: busy },
|
|
4154
|
-
busy ? "\u540C\u6B65\u4E2D\u2026" : "\u540C\u6B65 CalDAV"
|
|
4155
|
-
),
|
|
4156
4038
|
(0, import_react11.createElement)(
|
|
4157
4039
|
"button",
|
|
4158
|
-
{ className: "primary", onClick: () => setCreating(
|
|
4040
|
+
{ className: "primary", onClick: () => setCreating("NEEDS-ACTION") },
|
|
4159
4041
|
(0, import_react11.createElement)(Icon, { name: "plus" }),
|
|
4160
4042
|
"\u65B0\u5EFA\u4EFB\u52A1"
|
|
4161
4043
|
)
|
|
4162
4044
|
),
|
|
4163
|
-
toast === "" ? null : (0, import_react11.createElement)("div", { className: "small", style: { padding: "6px
|
|
4045
|
+
toast === "" ? null : (0, import_react11.createElement)("div", { className: "small", style: { padding: "6px 18px", color: "var(--m-ok)" } }, toast),
|
|
4164
4046
|
error === "" ? null : (0, import_react11.createElement)(ErrorBar, { message: error, onDismiss: () => setError("") }),
|
|
4165
4047
|
(0, import_react11.createElement)(
|
|
4166
4048
|
"div",
|
|
4167
|
-
{ className: "tasks-
|
|
4049
|
+
{ className: "tasks-board-wrap" },
|
|
4168
4050
|
(0, import_react11.createElement)(
|
|
4169
4051
|
"div",
|
|
4170
|
-
{ className: "tasks-
|
|
4052
|
+
{ className: "tasks-summary" },
|
|
4171
4053
|
(0, import_react11.createElement)(
|
|
4172
4054
|
"div",
|
|
4173
|
-
{ className: "tasks-
|
|
4055
|
+
{ className: "tasks-summary-main" },
|
|
4174
4056
|
(0, import_react11.createElement)(
|
|
4175
4057
|
"div",
|
|
4176
|
-
{ className: "tasks-
|
|
4058
|
+
{ className: "tasks-summary-labels" },
|
|
4177
4059
|
(0, import_react11.createElement)("strong", null, "\u603B\u4F53\u8FDB\u5EA6"),
|
|
4178
4060
|
(0, import_react11.createElement)(
|
|
4179
4061
|
"span",
|
|
4180
4062
|
{ className: "muted small" },
|
|
4181
|
-
`${String(
|
|
4063
|
+
`${String(doneCount)} / ${String(tasks.length)} \xB7 ${String(progress)}%`
|
|
4182
4064
|
)
|
|
4183
4065
|
),
|
|
4184
|
-
(0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } }))
|
|
4185
|
-
(0, import_react11.createElement)("div", { className: "small muted" }, `\u5F85\u529E ${String(open.length)} \u6761`)
|
|
4066
|
+
(0, import_react11.createElement)("div", { className: "tasks-bar" }, (0, import_react11.createElement)("span", { style: { width: `${String(progress)}%` } }))
|
|
4186
4067
|
),
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
)
|
|
4068
|
+
(0, import_react11.createElement)(
|
|
4069
|
+
"div",
|
|
4070
|
+
{ className: "tasks-stats" },
|
|
4071
|
+
(0, import_react11.createElement)(
|
|
4072
|
+
"span",
|
|
4073
|
+
{ className: "tasks-stat" },
|
|
4074
|
+
(0, import_react11.createElement)("strong", null, String(open.length)),
|
|
4075
|
+
(0, import_react11.createElement)("span", null, "\u5F85\u529E")
|
|
4076
|
+
),
|
|
4077
|
+
(0, import_react11.createElement)(
|
|
4078
|
+
"span",
|
|
4079
|
+
{ className: overdue > 0 ? "tasks-stat danger" : "tasks-stat" },
|
|
4080
|
+
(0, import_react11.createElement)("strong", null, String(overdue)),
|
|
4081
|
+
(0, import_react11.createElement)("span", null, "\u903E\u671F")
|
|
4082
|
+
),
|
|
4083
|
+
(0, import_react11.createElement)(
|
|
4084
|
+
"span",
|
|
4085
|
+
{ className: "tasks-stat" },
|
|
4086
|
+
(0, import_react11.createElement)("strong", null, String(projects.length)),
|
|
4087
|
+
(0, import_react11.createElement)("span", null, "\u9879\u76EE")
|
|
4206
4088
|
)
|
|
4207
4089
|
)
|
|
4208
4090
|
),
|
|
4209
4091
|
(0, import_react11.createElement)(
|
|
4210
4092
|
"div",
|
|
4211
|
-
{ className: "tasks-
|
|
4093
|
+
{ className: "tasks-toolbar" },
|
|
4212
4094
|
(0, import_react11.createElement)(
|
|
4213
4095
|
"div",
|
|
4214
4096
|
{ className: "tasks-filters" },
|
|
@@ -4224,73 +4106,128 @@ function TasksPanel() {
|
|
|
4224
4106
|
)
|
|
4225
4107
|
)
|
|
4226
4108
|
),
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
)
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4109
|
+
(0, import_react11.createElement)("span", { className: "spacer" }),
|
|
4110
|
+
(0, import_react11.createElement)("span", { className: "small muted" }, "\u62D6\u52A8\u5361\u7247\u53EF\u6539\u53D8\u72B6\u6001")
|
|
4111
|
+
),
|
|
4112
|
+
(0, import_react11.createElement)(
|
|
4113
|
+
"div",
|
|
4114
|
+
{ className: "tasks-board" },
|
|
4115
|
+
COLUMNS.map((column) => {
|
|
4116
|
+
const cards = tasks.filter((task) => task.status === column.status).filter(matchesFilter);
|
|
4117
|
+
return (0, import_react11.createElement)(
|
|
4118
|
+
"div",
|
|
4119
|
+
{
|
|
4120
|
+
key: column.status,
|
|
4121
|
+
className: `task-column task-column-${column.status}`,
|
|
4122
|
+
onDragOver: (event) => {
|
|
4123
|
+
event.preventDefault();
|
|
4124
|
+
},
|
|
4125
|
+
onDrop: (event) => {
|
|
4126
|
+
event.preventDefault();
|
|
4127
|
+
const task = tasks.find((item) => item.id === dragging);
|
|
4128
|
+
if (task !== void 0) void move(task, column.status);
|
|
4129
|
+
setDragging(null);
|
|
4130
|
+
}
|
|
4131
|
+
},
|
|
4132
|
+
(0, import_react11.createElement)(
|
|
4238
4133
|
"div",
|
|
4239
|
-
{
|
|
4240
|
-
(0, import_react11.createElement)("
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
onChange: () => void toggle(task)
|
|
4245
|
-
}),
|
|
4134
|
+
{ className: "task-column-head" },
|
|
4135
|
+
(0, import_react11.createElement)("span", { className: `task-dot ${column.status}` }),
|
|
4136
|
+
(0, import_react11.createElement)("strong", null, column.label),
|
|
4137
|
+
(0, import_react11.createElement)("span", { className: "task-column-count" }, String(cards.length)),
|
|
4138
|
+
(0, import_react11.createElement)("span", { className: "spacer" }),
|
|
4246
4139
|
(0, import_react11.createElement)(
|
|
4247
4140
|
"button",
|
|
4248
|
-
{
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4141
|
+
{
|
|
4142
|
+
className: "subtle icon",
|
|
4143
|
+
title: `\u5728\u300C${column.label}\u300D\u4E2D\u65B0\u5EFA`,
|
|
4144
|
+
onClick: () => setCreating(column.status)
|
|
4145
|
+
},
|
|
4146
|
+
(0, import_react11.createElement)(Icon, { name: "plus", size: 14 })
|
|
4147
|
+
)
|
|
4148
|
+
),
|
|
4149
|
+
(0, import_react11.createElement)(
|
|
4150
|
+
"div",
|
|
4151
|
+
{ className: "task-column-body" },
|
|
4152
|
+
cards.length === 0 ? (0, import_react11.createElement)("div", { className: "task-column-empty" }, column.hint) : cards.map(
|
|
4153
|
+
(task) => (0, import_react11.createElement)(
|
|
4154
|
+
"article",
|
|
4155
|
+
{
|
|
4156
|
+
key: task.id,
|
|
4157
|
+
className: `task-card${dragging === task.id ? " dragging" : ""}`,
|
|
4158
|
+
draggable: true,
|
|
4159
|
+
onDragStart: () => setDragging(task.id),
|
|
4160
|
+
onDragEnd: () => setDragging(null),
|
|
4161
|
+
onClick: () => setEditing(task)
|
|
4162
|
+
},
|
|
4163
|
+
(0, import_react11.createElement)(
|
|
4164
|
+
"div",
|
|
4165
|
+
{ className: "task-card-top" },
|
|
4166
|
+
(0, import_react11.createElement)("span", { className: `task-priority p${String(task.priority)}` }),
|
|
4167
|
+
(0, import_react11.createElement)("span", { className: "task-card-title" }, task.title),
|
|
4168
|
+
(0, import_react11.createElement)(
|
|
4169
|
+
"button",
|
|
4170
|
+
{
|
|
4171
|
+
className: "task-card-delete",
|
|
4172
|
+
title: "\u5220\u9664\u4EFB\u52A1",
|
|
4173
|
+
onClick: (event) => {
|
|
4174
|
+
event.stopPropagation();
|
|
4175
|
+
void remove(task);
|
|
4176
|
+
}
|
|
4177
|
+
},
|
|
4178
|
+
(0, import_react11.createElement)(Icon, { name: "close", size: 12 })
|
|
4179
|
+
)
|
|
4180
|
+
),
|
|
4181
|
+
task.notes === "" ? null : (0, import_react11.createElement)("p", { className: "task-card-notes" }, task.notes),
|
|
4182
|
+
(0, import_react11.createElement)(
|
|
4183
|
+
"div",
|
|
4184
|
+
{ className: "task-card-meta" },
|
|
4185
|
+
task.due === "" ? null : (0, import_react11.createElement)(
|
|
4186
|
+
"span",
|
|
4187
|
+
{
|
|
4188
|
+
className: task.due.slice(0, 10) < dateKey(/* @__PURE__ */ new Date()) && task.status !== "COMPLETED" ? "task-due overdue" : "task-due"
|
|
4189
|
+
},
|
|
4190
|
+
(0, import_react11.createElement)(Icon, { name: "calendar", size: 12 }),
|
|
4191
|
+
dueText(task.due)
|
|
4192
|
+
),
|
|
4193
|
+
task.owner === "" ? null : (0, import_react11.createElement)("span", { className: "task-owner" }, task.owner),
|
|
4194
|
+
task.projectId === null ? null : (0, import_react11.createElement)(
|
|
4195
|
+
"span",
|
|
4196
|
+
{ className: "task-project" },
|
|
4197
|
+
projects.find((project) => project.id === task.projectId)?.name ?? "\u9879\u76EE"
|
|
4198
|
+
),
|
|
4199
|
+
task.priority > 0 ? (0, import_react11.createElement)(
|
|
4200
|
+
"span",
|
|
4201
|
+
{ className: "task-priority-label" },
|
|
4202
|
+
`\u4F18\u5148\u7EA7 ${PRIORITY_LABEL[task.priority] ?? String(task.priority)}`
|
|
4203
|
+
) : null
|
|
4204
|
+
),
|
|
4205
|
+
task.messageKey === "" ? null : (0, import_react11.createElement)(
|
|
4206
|
+
"button",
|
|
4255
4207
|
{
|
|
4256
|
-
className: task
|
|
4208
|
+
className: "task-source",
|
|
4209
|
+
onClick: (event) => {
|
|
4210
|
+
event.stopPropagation();
|
|
4211
|
+
void openSource(task);
|
|
4212
|
+
}
|
|
4257
4213
|
},
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
task.owner === "" ? null : (0, import_react11.createElement)("span", null, task.owner),
|
|
4261
|
-
(0, import_react11.createElement)("span", { className: "badge" }, STATUS_LABEL[task.status] ?? task.status),
|
|
4262
|
-
task.projectId === null ? null : (0, import_react11.createElement)(
|
|
4263
|
-
"span",
|
|
4264
|
-
{ className: "badge" },
|
|
4265
|
-
projects.find((project) => project.id === task.projectId)?.name ?? "\u9879\u76EE"
|
|
4214
|
+
(0, import_react11.createElement)(Icon, { name: "mail", size: 12 }),
|
|
4215
|
+
"\u6765\u6E90\u90AE\u4EF6"
|
|
4266
4216
|
)
|
|
4267
4217
|
)
|
|
4268
|
-
),
|
|
4269
|
-
task.messageKey === "" ? null : (0, import_react11.createElement)(
|
|
4270
|
-
"button",
|
|
4271
|
-
{
|
|
4272
|
-
className: "subtle small",
|
|
4273
|
-
title: "\u6253\u5F00\u6765\u6E90\u90AE\u4EF6",
|
|
4274
|
-
onClick: () => void openSource(task)
|
|
4275
|
-
},
|
|
4276
|
-
"\u6765\u6E90"
|
|
4277
|
-
),
|
|
4278
|
-
(0, import_react11.createElement)(
|
|
4279
|
-
"button",
|
|
4280
|
-
{ className: "subtle icon", title: "\u5220\u9664", onClick: () => void remove(task) },
|
|
4281
|
-
(0, import_react11.createElement)(Icon, { name: "trash", size: 13 })
|
|
4282
4218
|
)
|
|
4283
4219
|
)
|
|
4284
|
-
)
|
|
4285
|
-
)
|
|
4220
|
+
);
|
|
4221
|
+
})
|
|
4286
4222
|
)
|
|
4287
4223
|
),
|
|
4288
|
-
creating || editing !== null ? (0, import_react11.createElement)(TaskEditor, {
|
|
4224
|
+
creating !== null || editing !== null ? (0, import_react11.createElement)(TaskEditor, {
|
|
4289
4225
|
task: editing,
|
|
4226
|
+
status: creating ?? "NEEDS-ACTION",
|
|
4290
4227
|
projects,
|
|
4291
4228
|
busy,
|
|
4292
4229
|
onClose: () => {
|
|
4293
|
-
setCreating(
|
|
4230
|
+
setCreating(null);
|
|
4294
4231
|
setEditing(null);
|
|
4295
4232
|
},
|
|
4296
4233
|
onSave: (draft) => void save(draft)
|
|
@@ -4299,13 +4236,15 @@ function TasksPanel() {
|
|
|
4299
4236
|
}
|
|
4300
4237
|
function TaskEditor({
|
|
4301
4238
|
task,
|
|
4239
|
+
status,
|
|
4302
4240
|
projects,
|
|
4303
4241
|
busy,
|
|
4304
4242
|
onClose,
|
|
4305
4243
|
onSave
|
|
4306
4244
|
}) {
|
|
4307
4245
|
const [draft, setDraft] = (0, import_react11.useState)({
|
|
4308
|
-
...
|
|
4246
|
+
...EMPTY,
|
|
4247
|
+
status,
|
|
4309
4248
|
...task === null ? {} : {
|
|
4310
4249
|
id: task.id,
|
|
4311
4250
|
title: task.title,
|
|
@@ -4323,111 +4262,139 @@ function TaskEditor({
|
|
|
4323
4262
|
{
|
|
4324
4263
|
title: task === null ? "\u65B0\u5EFA\u4EFB\u52A1" : "\u7F16\u8F91\u4EFB\u52A1",
|
|
4325
4264
|
onClose,
|
|
4265
|
+
wide: true,
|
|
4326
4266
|
footer: (0, import_react11.createElement)(
|
|
4327
4267
|
"div",
|
|
4328
|
-
{
|
|
4268
|
+
{ className: "task-editor-foot" },
|
|
4269
|
+
(0, import_react11.createElement)("span", { className: "small muted" }, "\u63D0\u793A\uFF1A\u4EFB\u52A1\u4F1A\u4FDD\u5B58\u5728\u672C\u5730\uFF0C\u53EF\u5728\u770B\u677F\u4E2D\u62D6\u52A8\u6539\u53D8\u72B6\u6001\u3002"),
|
|
4270
|
+
(0, import_react11.createElement)("span", { className: "spacer" }),
|
|
4329
4271
|
(0, import_react11.createElement)("button", { className: "ghost", onClick: onClose }, "\u53D6\u6D88"),
|
|
4330
4272
|
(0, import_react11.createElement)(
|
|
4331
4273
|
"button",
|
|
4332
|
-
{ className: "primary", disabled: busy || draft.title.trim() === "", onClick: () => onSave(draft) },
|
|
4333
|
-
busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
|
|
4334
|
-
)
|
|
4335
|
-
)
|
|
4336
|
-
},
|
|
4337
|
-
(0, import_react11.createElement)(
|
|
4338
|
-
"div",
|
|
4339
|
-
{ className: "mailharbor-field" },
|
|
4340
|
-
(0, import_react11.createElement)("label", null, "\u6807\u9898"),
|
|
4341
|
-
(0, import_react11.createElement)("input", {
|
|
4342
|
-
value: draft.title,
|
|
4343
|
-
autoFocus: true,
|
|
4344
|
-
onChange: (event) => set({ title: event.target.value })
|
|
4345
|
-
})
|
|
4346
|
-
),
|
|
4347
|
-
(0, import_react11.createElement)(
|
|
4348
|
-
"div",
|
|
4349
|
-
{ className: "mailharbor-field" },
|
|
4350
|
-
(0, import_react11.createElement)("label", null, "\u5907\u6CE8"),
|
|
4351
|
-
(0, import_react11.createElement)("textarea", {
|
|
4352
|
-
rows: 3,
|
|
4353
|
-
value: draft.notes,
|
|
4354
|
-
onChange: (event) => set({ notes: event.target.value })
|
|
4355
|
-
})
|
|
4356
|
-
),
|
|
4357
|
-
(0, import_react11.createElement)(
|
|
4358
|
-
"div",
|
|
4359
|
-
{ className: "mailharbor-grid" },
|
|
4360
|
-
(0, import_react11.createElement)(
|
|
4361
|
-
"div",
|
|
4362
|
-
{ className: "mailharbor-field" },
|
|
4363
|
-
(0, import_react11.createElement)("label", null, "\u622A\u6B62\u65E5\u671F"),
|
|
4364
|
-
(0, import_react11.createElement)("input", {
|
|
4365
|
-
type: "date",
|
|
4366
|
-
value: draft.due,
|
|
4367
|
-
onChange: (event) => set({ due: event.target.value })
|
|
4368
|
-
})
|
|
4369
|
-
),
|
|
4370
|
-
(0, import_react11.createElement)(
|
|
4371
|
-
"div",
|
|
4372
|
-
{ className: "mailharbor-field" },
|
|
4373
|
-
(0, import_react11.createElement)("label", null, "\u4F18\u5148\u7EA7"),
|
|
4374
|
-
(0, import_react11.createElement)(
|
|
4375
|
-
"select",
|
|
4376
4274
|
{
|
|
4377
|
-
|
|
4378
|
-
|
|
4275
|
+
className: "primary",
|
|
4276
|
+
disabled: busy || draft.title.trim() === "",
|
|
4277
|
+
onClick: () => onSave(draft)
|
|
4379
4278
|
},
|
|
4380
|
-
|
|
4381
|
-
(0, import_react11.createElement)("option", { value: "5" }, "\u4E2D"),
|
|
4382
|
-
(0, import_react11.createElement)("option", { value: "9" }, "\u9AD8")
|
|
4279
|
+
busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u4EFB\u52A1"
|
|
4383
4280
|
)
|
|
4384
4281
|
)
|
|
4385
|
-
|
|
4282
|
+
},
|
|
4386
4283
|
(0, import_react11.createElement)(
|
|
4387
4284
|
"div",
|
|
4388
|
-
{ className: "
|
|
4285
|
+
{ className: "task-editor" },
|
|
4389
4286
|
(0, import_react11.createElement)(
|
|
4390
4287
|
"div",
|
|
4391
|
-
{ className: "
|
|
4392
|
-
(0, import_react11.createElement)("label", null, "\u72B6\u6001"),
|
|
4288
|
+
{ className: "task-editor-main" },
|
|
4393
4289
|
(0, import_react11.createElement)(
|
|
4394
|
-
"
|
|
4395
|
-
{
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4290
|
+
"div",
|
|
4291
|
+
{ className: "task-editor-title" },
|
|
4292
|
+
(0, import_react11.createElement)("input", {
|
|
4293
|
+
value: draft.title,
|
|
4294
|
+
autoFocus: true,
|
|
4295
|
+
placeholder: "\u8981\u505A\u4EC0\u4E48\uFF1F",
|
|
4296
|
+
onChange: (event) => set({ title: event.target.value })
|
|
4297
|
+
})
|
|
4298
|
+
),
|
|
4299
|
+
(0, import_react11.createElement)(
|
|
4300
|
+
"div",
|
|
4301
|
+
{ className: "task-editor-section" },
|
|
4302
|
+
(0, import_react11.createElement)("label", null, "\u5907\u6CE8"),
|
|
4303
|
+
(0, import_react11.createElement)("textarea", {
|
|
4304
|
+
rows: 5,
|
|
4305
|
+
value: draft.notes,
|
|
4306
|
+
placeholder: "\u80CC\u666F\u3001\u4E0B\u4E00\u6B65\u3001\u94FE\u63A5\u2026",
|
|
4307
|
+
onChange: (event) => set({ notes: event.target.value })
|
|
4308
|
+
})
|
|
4309
|
+
),
|
|
4310
|
+
(0, import_react11.createElement)(
|
|
4311
|
+
"div",
|
|
4312
|
+
{ className: "task-editor-section" },
|
|
4313
|
+
(0, import_react11.createElement)("label", null, "\u6765\u6E90"),
|
|
4314
|
+
draft.id === void 0 ? (0, import_react11.createElement)("span", { className: "small muted" }, "\u4ECE\u90AE\u4EF6\u63D0\u53D6\u7684\u4EFB\u52A1\u4F1A\u81EA\u52A8\u5173\u8054\u6765\u6E90\u90AE\u4EF6\u3002") : (0, import_react11.createElement)(
|
|
4315
|
+
"span",
|
|
4316
|
+
{ className: "small muted" },
|
|
4317
|
+
task?.messageSubject !== void 0 && task.messageSubject !== "" ? `\u6765\u81EA\u90AE\u4EF6\uFF1A${task.messageSubject}` : "\u65E0\u5173\u8054\u90AE\u4EF6"
|
|
4318
|
+
)
|
|
4400
4319
|
)
|
|
4401
4320
|
),
|
|
4402
4321
|
(0, import_react11.createElement)(
|
|
4403
|
-
"
|
|
4404
|
-
{ className: "
|
|
4405
|
-
(0, import_react11.createElement)("label", null, "\u9879\u76EE"),
|
|
4322
|
+
"aside",
|
|
4323
|
+
{ className: "task-editor-side" },
|
|
4406
4324
|
(0, import_react11.createElement)(
|
|
4407
|
-
"
|
|
4408
|
-
{
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4325
|
+
"div",
|
|
4326
|
+
{ className: "task-editor-field" },
|
|
4327
|
+
(0, import_react11.createElement)("label", null, "\u72B6\u6001"),
|
|
4328
|
+
(0, import_react11.createElement)(
|
|
4329
|
+
"select",
|
|
4330
|
+
{
|
|
4331
|
+
value: draft.status,
|
|
4332
|
+
onChange: (event) => set({ status: event.target.value })
|
|
4333
|
+
},
|
|
4334
|
+
COLUMNS.map((column) => (0, import_react11.createElement)("option", { key: column.status, value: column.status }, column.label))
|
|
4335
|
+
)
|
|
4336
|
+
),
|
|
4337
|
+
(0, import_react11.createElement)(
|
|
4338
|
+
"div",
|
|
4339
|
+
{ className: "task-editor-field" },
|
|
4340
|
+
(0, import_react11.createElement)("label", null, "\u622A\u6B62\u65E5\u671F"),
|
|
4341
|
+
(0, import_react11.createElement)("input", {
|
|
4342
|
+
type: "date",
|
|
4343
|
+
value: draft.due,
|
|
4344
|
+
onChange: (event) => set({ due: event.target.value })
|
|
4345
|
+
})
|
|
4346
|
+
),
|
|
4347
|
+
(0, import_react11.createElement)(
|
|
4348
|
+
"div",
|
|
4349
|
+
{ className: "task-editor-field" },
|
|
4350
|
+
(0, import_react11.createElement)("label", null, "\u4F18\u5148\u7EA7"),
|
|
4351
|
+
(0, import_react11.createElement)(
|
|
4352
|
+
"div",
|
|
4353
|
+
{ className: "task-priority-picker" },
|
|
4354
|
+
[0, 5, 9].map(
|
|
4355
|
+
(value) => (0, import_react11.createElement)(
|
|
4356
|
+
"button",
|
|
4357
|
+
{
|
|
4358
|
+
key: value,
|
|
4359
|
+
className: draft.priority === value ? "active" : "",
|
|
4360
|
+
onClick: () => set({ priority: value })
|
|
4361
|
+
},
|
|
4362
|
+
PRIORITY_LABEL[value] ?? String(value)
|
|
4363
|
+
)
|
|
4364
|
+
)
|
|
4365
|
+
)
|
|
4366
|
+
),
|
|
4367
|
+
(0, import_react11.createElement)(
|
|
4368
|
+
"div",
|
|
4369
|
+
{ className: "task-editor-field" },
|
|
4370
|
+
(0, import_react11.createElement)("label", null, "\u9879\u76EE"),
|
|
4371
|
+
(0, import_react11.createElement)(
|
|
4372
|
+
"select",
|
|
4373
|
+
{
|
|
4374
|
+
value: draft.projectId,
|
|
4375
|
+
onChange: (event) => set({ projectId: event.target.value })
|
|
4376
|
+
},
|
|
4377
|
+
(0, import_react11.createElement)("option", { value: "" }, "\u65E0\u9879\u76EE"),
|
|
4378
|
+
projects.map((project) => (0, import_react11.createElement)("option", { key: project.id, value: project.id }, project.name))
|
|
4379
|
+
)
|
|
4380
|
+
),
|
|
4381
|
+
(0, import_react11.createElement)(
|
|
4382
|
+
"div",
|
|
4383
|
+
{ className: "task-editor-field" },
|
|
4384
|
+
(0, import_react11.createElement)("label", null, "\u8D1F\u8D23\u4EBA"),
|
|
4385
|
+
(0, import_react11.createElement)("input", {
|
|
4386
|
+
value: draft.owner,
|
|
4387
|
+
placeholder: "\u53EF\u9009",
|
|
4388
|
+
onChange: (event) => set({ owner: event.target.value })
|
|
4389
|
+
})
|
|
4414
4390
|
)
|
|
4415
4391
|
)
|
|
4416
|
-
),
|
|
4417
|
-
(0, import_react11.createElement)(
|
|
4418
|
-
"div",
|
|
4419
|
-
{ className: "mailharbor-field" },
|
|
4420
|
-
(0, import_react11.createElement)("label", null, "\u8D1F\u8D23\u4EBA"),
|
|
4421
|
-
(0, import_react11.createElement)("input", {
|
|
4422
|
-
value: draft.owner,
|
|
4423
|
-
onChange: (event) => set({ owner: event.target.value })
|
|
4424
|
-
})
|
|
4425
4392
|
)
|
|
4426
4393
|
);
|
|
4427
4394
|
}
|
|
4428
4395
|
|
|
4429
4396
|
// src/client/index.tsx
|
|
4430
|
-
var VERSION = "0.
|
|
4397
|
+
var VERSION = "0.6.0";
|
|
4431
4398
|
var inject = ["slots"];
|
|
4432
4399
|
var PANEL_ID = "mailharbor";
|
|
4433
4400
|
var TASKS_PANEL_ID = "mailharbor-tasks";
|