From 462667468dafbdad0c7441aea4cf53527a3a48ff Mon Sep 17 00:00:00 2001 From: vyctorbrzezowski Date: Fri, 21 Aug 2026 23:37:34 -0300 Subject: [PATCH] refactor(ui): brand-accent dictation mode with capture bar and full-width wave --- COMPOSER-SELECTS-NOTES.md | 10 ++ ui/src/i18n/locales/en.ts | 2 +- .../chat/components/chat-composer-controls.ts | 22 +++- .../chat/components/chat-composer-view.ts | 25 +--- .../chat/components/chat-voice-activity.ts | 25 +++- ui/src/styles/chat/layout.css | 119 +++++++++++------- 6 files changed, 133 insertions(+), 70 deletions(-) diff --git a/COMPOSER-SELECTS-NOTES.md b/COMPOSER-SELECTS-NOTES.md index 4203da174dad..16a70c7f1255 100644 --- a/COMPOSER-SELECTS-NOTES.md +++ b/COMPOSER-SELECTS-NOTES.md @@ -55,3 +55,13 @@ Lane: composer selects (worktree `composer-bench`). Bench files intocados. - Causa da diferença apontada em review: `wa-dropdown-item::part(icon)` tem 24px intrínsecos (SVG 16px centrado → gap visual maior) e labels 12px/400 `--text` vs 13px/600 `--text-strong` do permission. - Fix (`layout.css`): `::part(icon)` forçado a 16px nos attach/capability items; `::part(label)` = 13px/600 `--text-strong`; notes voltam a 11px/400 muted; mic device labels e model option titles alinhados ao mesmo 13px/600. - Verificado visualmente (dark) em :5230 — attach/capability e permission agora com a mesma tipografia e mesmo trilho de ícone. + +## Voice mode (dictation) refactor + +- Referências: barra estilo "Listening" (Cancel ✕ à esquerda, waveform larga, ✓ à direita) + luz circulando a borda. +- Cor: dictation migrou de `--danger` para `--accent` (red brand) — borda orbitante (`chat-dictation-edge-orbit`, mantida), tint da borda, barras da wave e ✓ confirm (accent sólido, hover `--accent-hover`). +- Wave: `openclaw-microphone-activity` ganhou atributo `bars` (perfil senoidal simétrico); default 7 intacto (e2e do Talk). Dictation usa 36 barras em strip full-width acima do editor; partial transcript centralizado e ellipsized abaixo. +- Barra de captura: footer vira Cancel (✕ + "Cancel", pill neutra — mesmo elemento do mic, preservando pointer capture do hold) · label central `role="status"` (Starting…/Recording m:ss/Finishing…) · ✓ accent à direita (insere). Elementos inúteis já ocultos (lead/meta/controls/mic-picker), agora com actions esticada em `space-between`. +- i18n: `+discardDictation` ("Cancel dictation"), `-dictationReleaseToInsert` (sem usos); baseline+verify ok. +- Limpeza: CSS morto `chat-send-btn__dictation-time` e `agent-chat__dictation-copy` removidos. +- Verificação: DOM do estado dictating injetado no bench (:5230) — screenshots dark/light inspecionados (`/tmp/herdr/dict-*.png`); vitest dictation/composer suites: mesmas 5 falhas pré-existentes do checkpoint, 72 pass; tsgo:ui sem erros nos arquivos tocados; stylelint só com os 3 erros pré-existentes. diff --git a/ui/src/i18n/locales/en.ts b/ui/src/i18n/locales/en.ts index 75780edb99cf..6ef62da4a667 100644 --- a/ui/src/i18n/locales/en.ts +++ b/ui/src/i18n/locales/en.ts @@ -5978,7 +5978,7 @@ export const en: TranslationMap = { dictationProviderUnavailable: "No transcription provider is configured for dictation. Choose one in Settings to dictate.", dictationRecording: "Recording {elapsed}", - dictationReleaseToInsert: "Release to insert dictation", + discardDictation: "Cancel dictation", realtimeTalkRequiresMicrophone: "Realtime voice input requires browser microphone access.", selectedMicrophoneUnavailable: "The selected microphone is unavailable. Choose another input or System default.", diff --git a/ui/src/pages/chat/components/chat-composer-controls.ts b/ui/src/pages/chat/components/chat-composer-controls.ts index 55d01c3d801d..440429938097 100644 --- a/ui/src/pages/chat/components/chat-composer-controls.ts +++ b/ui/src/pages/chat/components/chat-composer-controls.ts @@ -232,7 +232,7 @@ export function renderComposerVoiceButton(props: ComposerVoiceButtonProps) { const label = finalizing ? t("chat.composer.dictationFinalizing") : active - ? t("chat.composer.dictationReleaseToInsert") + ? t("chat.composer.discardDictation") : (props.idleLabel ?? t("chat.composer.startVoiceInput")); const tooltip = props.dictation && !(active || finalizing) ? t("chat.composer.voiceGestureHint") : label; @@ -266,7 +266,12 @@ export function renderComposerVoiceButton(props: ComposerVoiceButtonProps) { ${finalizing ? icons.loader : active - ? icons.stop + ? html` + ${icons.x} + ${t("common.cancel")} + ` : html` ${icons.mic} ${label} @@ -359,6 +364,19 @@ export function renderChatPrimaryActions(props: ChatRunControlsProps) { ); const dictationConfirmAction = props.dictation?.active ? html` + ${props.dictation.finalizing + ? t("chat.composer.dictationFinalizing") + : props.dictation.connecting + ? t("chat.composer.dictationConnecting") + : t("chat.composer.dictationRecording", { + elapsed: props.dictation.elapsed, + })}