cc7841bd1f
A11y no parent:
- aria-label em botoes icon-only do header (Recarregar dinamico, Buscar
compact, Close); tooltip vira title que SR ignora
- aria-hidden=true em icones decorativos (header title, search input,
subheader info-circle, kanban col head, empty state, button icons)
- aria-busy reativo no mw-col__body durante loading
- aria-label dinamico no count do kanban ("3 conversas em Urgente")
- aria-expanded + aria-controls no menu mobile button
- aria-label no input de busca
- role=note no subheader explicativo
- :inert="(drawerOpen && isMobile) || null" no <section class="mw-page">
— focus trap real: drawer aberto torna conteudo de fundo inerte
(boolean attr via || null pra Vue 3.4 serializar correto)
A11y no Sidebar:
- aria-hidden=true em todos icones decorativos restantes (filter title
icons, list/bell/user/user-minus, channel icons, filter-slash, etc)
Perf — tagsForThread cacheado:
- Antes era chamado in-template (2x por card, recriava array a cada
render). Agora tagsByThreadKey computed Map: lookup O(1) por card,
recompute so quando threadTagsMap ou tagById muda. EMPTY_TAGS frozen
evita criar arrays novos pra threads sem tags.
DRY — channelMeta + KANBAN_COLUMNS shared:
- src/utils/channelMeta.js (novo): CHANNEL_OPTIONS frozen + channelIcon
+ channelLabel. Antes channelIcon estava em 3 lugares (parent, Sidebar,
Card); CHANNEL_OPTIONS em 2 (parent, Sidebar). Agora 1.
- useConversations.js: exporta KANBAN_COLUMNS frozen (metadata canonica:
key + label + icon + color). Antes parent+Sidebar tinham copias locais
de 8 linhas cada + composable tinha KANBAN_ORDER separado. Agora
KANBAN_ORDER deriva de KANBAN_COLUMNS.
Drift eliminado: 3 fontes -> 1 pra channelIcon, 2 -> 1 pra
CHANNEL_OPTIONS, 2 -> 1 pra KANBAN_COLUMNS (KANBAN_ORDER ainda interno
ao composable mas derivado).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>