diff --git a/components/email/email-list-item.tsx b/components/email/email-list-item.tsx index 0db55e12..f12b5a6d 100644 --- a/components/email/email-list-item.tsx +++ b/components/email/email-list-item.tsx @@ -51,9 +51,9 @@ export function EmailListItem({ email, selected, onClick, onContextMenu, onToggl const isFocusedMailLayout = mailLayout === 'focus'; const inlinePreview = showPreview && email.preview ? ` ${email.preview}` : ''; - // Resolve color tags using keyword definitions from settings + // Resolve color tags using keyword definitions from settings; unknown tags fall back to gray const colorTagIds = getEmailColorTags(email.keywords); - const keywordDefs = colorTagIds.map(id => emailKeywords.find(k => k.id === id)).filter(Boolean) as typeof emailKeywords; + const keywordDefs = colorTagIds.map(id => emailKeywords.find(k => k.id === id) ?? { id, label: id, color: 'gray' }); // Use first tag for background coloring const keywordDef = keywordDefs[0] ?? null; const colorTag = keywordDef ? KEYWORD_PALETTE[keywordDef.color]?.bg ?? null : null; diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index e5000d14..eb8c1fb9 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -3070,13 +3070,13 @@ export function EmailViewer({ <> {currentColors.slice(0, 3).map((tagId) => { - const kw = emailKeywords.find(k => k.id === tagId); - return kw ? : null; + const kw = emailKeywords.find(k => k.id === tagId) ?? { id: tagId, label: tagId, color: 'gray' }; + return ; })} {showToolbarLabels && currentColors.length === 1 && ( - {emailKeywords.find(k => k.id === currentColors[0])?.label} + {emailKeywords.find(k => k.id === currentColors[0])?.label ?? currentColors[0]} )} @@ -3674,11 +3674,11 @@ export function EmailViewer({ {currentColors.length > 0 && ( {currentColors.map((tagId) => { - const kw = emailKeywords.find(k => k.id === tagId); - const dotClass = kw ? KEYWORD_PALETTE[kw.color]?.dot : null; - return dotClass ? ( - - ) : null; + const kw = emailKeywords.find(k => k.id === tagId) ?? { id: tagId, label: tagId, color: 'gray' }; + const dotClass = KEYWORD_PALETTE[kw.color]?.dot || 'bg-gray-500'; + return ( + + ); })} )} diff --git a/components/email/thread-list-item.tsx b/components/email/thread-list-item.tsx index 9ee8e705..819d180d 100644 --- a/components/email/thread-list-item.tsx +++ b/components/email/thread-list-item.tsx @@ -67,9 +67,9 @@ const SingleEmailItem = React.forwardRef( const isFocusedMailLayout = mailLayout === 'focus'; const inlinePreview = showPreview && email.preview ? ` ${email.preview}` : ''; - // Resolve color tags using keyword definitions + // Resolve color tags using keyword definitions; unknown tags fall back to gray const tagIds = getEmailColorTags(email.keywords); - const resolvedKeywordDefs = tagIds.map(id => emailKeywords.find(k => k.id === id)).filter(Boolean) as typeof emailKeywords; + const resolvedKeywordDefs = tagIds.map(id => emailKeywords.find(k => k.id === id) ?? { id, label: id, color: 'gray' }); const resolvedKeywordDef = resolvedKeywordDefs[0] ?? null; const resolvedColorTag = (() => { if (colorTag) return colorTag; @@ -375,7 +375,7 @@ export const ThreadListItem = React.forwardRef state.emailKeywords); - const keywordDef = threadColor ? emailKeywordDefs.find(k => k.id === threadColor) : null; + const keywordDef = threadColor ? (emailKeywordDefs.find(k => k.id === threadColor) ?? { id: threadColor, label: threadColor, color: 'gray' }) : null; const colorTag = keywordDef ? KEYWORD_PALETTE[keywordDef.color]?.bg ?? null : null; const isSelected = selectedEmailId === latestEmail.id || diff --git a/components/filters/filter-rule-modal.tsx b/components/filters/filter-rule-modal.tsx index 3dd5f4da..96c7b293 100644 --- a/components/filters/filter-rule-modal.tsx +++ b/components/filters/filter-rule-modal.tsx @@ -17,6 +17,7 @@ import type { } from "@/lib/jmap/sieve-types"; import type { Mailbox } from "@/lib/jmap/types"; import { buildMailboxTree, flattenMailboxTree, type MailboxNode, generateUUID } from "@/lib/utils"; +import { useSettingsStore } from "@/stores/settings-store"; interface FilterRuleModalProps { rule?: FilterRule; @@ -58,6 +59,7 @@ export function FilterRuleModal({ }: FilterRuleModalProps) { const t = useTranslations("settings.filters"); const isEdit = !!rule; + const emailKeywords = useSettingsStore((state) => state.emailKeywords); const [name, setName] = useState(rule?.name || ""); const [matchType, setMatchType] = useState<"all" | "any">(rule?.matchType || "all"); @@ -375,12 +377,17 @@ export function FilterRuleModal({ )} {action.type === "add_label" && ( - updateAction(index, { value: e.target.value })} - placeholder={t("label_placeholder")} - className="flex-1 min-w-[140px]" - /> + className={`${selectClass} flex-1 min-w-[140px]`} + aria-label={t("label_placeholder")} + > + + {emailKeywords.map((kw) => ( + + ))} + )}