# Frontend Redesign Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Substituir todo o front-end do Transcript (Tauri 2) por uma interface React 19 + Tailwind v4 com design system próprio (tokens, primitivos, Storybook), design book em markdown, e as 4 superfícies reformuladas (janela principal com abas, configurações, overlay de ditado, histórico) — sem tocar na marca nem no backend. **Architecture:** Reescrita completa da camada de UI. Primeiro uma rodada de concorrência: 3 agentes designers produzem direções visuais em mockups HTML estáticos (design/proposals//); o usuário escolhe; depois a implementação segue a direção vencedora — tokens → primitivos (Storybook) → estado (hooks que portam a máquina de ditado atual) → telas (Editor, Settings, History) → cutover (apaga app.ts antigo) → design book → auditoria final. O backend Rust só muda 2 constantes de tamanho de janela (ver Task 1). **Tech Stack:** React 19, Vite 7, TypeScript 5.9, Tailwind CSS v4 (tokens em `@theme`), Storybook 8, Vitest + Testing Library, lucide-react, Tauri 2 (comandos e eventos inalterados). ## Global Constraints - Textos da UI em pt-BR, portando as mensagens atuais (ex.: "Ouvindo…", "Pronto para gravar.", "Texto copiado."). - Todos os comandos Tauri usam os nomes snake_case exatos (`get_app_snapshot`, `stop_recording`, `process_transcript_text`… ver Task 8). - Todos os eventos Tauri usam os nomes exatos (`transcript://snapshot`, `transcript://dictation-started`, `transcript://dictation-finished`, `transcript://dictation-failed`, `transcript://toggle-recording`). - Backend Rust intocado exceto: `APP_WINDOW_WIDTH` e `APP_WINDOW_HEIGHT` em `src-tauri/src/ui_commands.rs:28-29` (Task 1). - Instalação de dependências com `npm` (o repo usa package-lock.json; pnpm-workspace.yaml é de outro workspace e não deve ser tocado). - Dark-first: tema escuro ativo; tokens do tema claro prontos mas sem toggle na UI. - Marca (logo, nome "Transcript") intocada. - A janela é redimensionada pelo Rust durante o ditado (barra 560×124, revisão 720×~300) — o overlay precisa renderizar bem nesses tamanhos. - Sem novas dependências além das listadas na Task 1. - Commits em pt-BR, frequentes, um por task. --- ## Phase A — Fundação ### Task 1: Stack React + Tailwind + configuração **Files:** - Modify: `package.json` - Modify: `vite.config.ts` - Modify: `tsconfig.json` - Modify: `index.html` - Modify: `src-tauri/tauri.conf.json` - Modify: `src-tauri/src/ui_commands.rs:28-29` - Create: `src/test/setup.ts` - Create: `src/main.tsx` - Create: `src/App.tsx` - Create: `src/App.test.tsx` - Delete: dependências `@hypabolic/pi-hypa`, `lucide-static` **Interfaces:** - Produces: `src/App.tsx` exporta `export function App({ client = createNativeClient() }: { client?: NativeClient })` (esqueleto mínimo: topbar com marca + versão + botão de config; ainda sem abas). `src/main.tsx` monta `` em `#app`. `src/test/setup.ts` registra cleanup do Testing Library. - [ ] **Step 1: Instalar dependências** ```bash npm i react react-dom lucide-react npm i -D @vitejs/plugin-react tailwindcss @tailwindcss/vite \ @types/react @types/react-dom @testing-library/react \ @testing-library/jest-dom @testing-library/user-event \ storybook @storybook/react-vite @storybook/react npm rm @hypabolic/pi-hypa lucide-static ``` - [ ] **Step 2: Adicionar scripts no `package.json`** ```json "scripts": { "dev": "vite --host 127.0.0.1 --port 1420", "build": "tsc --noEmit && vite build", "test": "vitest", "typecheck": "tsc --noEmit", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "tauri:dev": "tauri dev", "tauri:build": "tauri build" } ``` - [ ] **Step 3: Reescrever `vite.config.ts`** ```ts import { defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; export default defineConfig({ plugins: [react(), tailwindcss()], clearScreen: false, server: { host: "127.0.0.1", port: 1420, strictPort: true, }, envPrefix: ["VITE_", "TAURI_"], test: { environment: "jsdom", setupFiles: ["./src/test/setup.ts"], }, }); ``` - [ ] **Step 4: Adicionar `jsx` no `tsconfig.json`** (dentro de `compilerOptions`) ```json "jsx": "react-jsx" ``` - [ ] **Step 5: Criar `src/test/setup.ts`** ```ts import "@testing-library/jest-dom/vitest"; import { cleanup } from "@testing-library/react"; import { afterEach } from "vitest"; afterEach(() => { cleanup(); }); ``` - [ ] **Step 6: Apontar `index.html` para o entry React** ```html ``` (título, descrição, favicon e `
` permanecem) - [ ] **Step 7: Criar `src/main.tsx`** ```tsx import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { App } from "./App"; import "./styles.css"; const root = document.getElementById("app"); if (!root) throw new Error("elemento #app não encontrado"); createRoot(root).render( , ); ``` - [ ] **Step 8: Criar `src/App.tsx` mínimo (topbar com marca — será expandido na Task 10)** ```tsx import { createNativeClient, type NativeClient } from "./lib/tauri"; import { Settings, Minus, X } from "lucide-react"; export function App({ client = createNativeClient(), }: { client?: NativeClient; }) { return (

Transcript

); } ``` - [ ] **Step 9: Criar `src/styles.css` base** (a Task 4 define os tokens; aqui só o mínimo para o shell funcionar) ```css @import "tailwindcss"; @layer base { html { color-scheme: dark; } body { @apply bg-bg-base font-sans text-base text-fg antialiased; } } ``` - [ ] **Step 10: Criar `src/lib/tauri.ts` com o mínimo para o App compilar** — apenas `NativeClient` e `createNativeClient` por ora; a Task 8 expande com `commands` e `EVENTS`. ```ts import { invoke as tauriInvoke } from "@tauri-apps/api/core"; export interface NativeClient { invoke(command: string, args?: Record): Promise; } export function createNativeClient(): NativeClient { return { invoke: (command, args) => tauriInvoke(command, args), }; } ``` - [ ] **Step 11: Criar `src/App.test.tsx` (teste do shell)** ```tsx import { describe, expect, it } from "vitest"; import { render, screen } from "@testing-library/react"; import { App } from "./App"; import type { NativeClient } from "./lib/tauri"; const client: NativeClient = { invoke: () => Promise.reject(new Error("não usado neste teste")), }; describe("App", () => { it("renderiza a marca e os controles de janela", () => { render(); expect(screen.getByRole("heading", { name: "Transcript" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Configurações" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Minimizar" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Fechar" })).toBeInTheDocument(); }); }); ``` - [ ] **Step 12: Ajustar a janela no `src-tauri/tauri.conf.json`** (janela vira redimensionável e maior; mantém frameless, transparent, alwaysOnTop) ```json { "label": "main", "width": 640, "height": 520, "minWidth": 420, "minHeight": 360, "maxWidth": 1024, "maxHeight": 768, "resizable": true, "maximizable": false, "minimizable": true, "closable": true, "alwaysOnTop": true, "visible": true, "decorations": false, "transparent": true, "backgroundColor": "#00000000", "shadow": false, "dragDropEnabled": false, "center": true } ``` - [ ] **Step 13: Atualizar as constantes de restauração no Rust** (`src-tauri/src/ui_commands.rs:28-29`). Sem isso, `restore_main_window` (tray) encolheria a janela para 380×460 a cada restauração. ```rust const APP_WINDOW_WIDTH: u32 = 640; const APP_WINDOW_HEIGHT: u32 = 520; ``` - [ ] **Step 14: Rodar o teste e verificar falha do shell** (o App antigo ainda é o entry de produção; o teste novo valida o novo shell) Run: `npm test -- --run src/App.test.tsx` Expected: PASS - [ ] **Step 15: Verificação completa** Run: `npm run typecheck && npm run build && npm test -- --run` Expected: tudo PASS (o app antigo continua passando; `build` gera o bundle novo). - [ ] **Step 16: Commit** ```bash git add -A git commit -m "feat: fundação React 19 + Tailwind v4 + janela redimensionável" ``` --- ### Task 2: Brief da fase de concorrência **Files:** - Create: `design/proposals/BRIEF.md` **Interfaces:** - Produces: `design/proposals/BRIEF.md` — documento que os 3 agentes designers seguem na Task 3. - [ ] **Step 1: Criar o BRIEF com este conteúdo exato** ```markdown # Brief — Direções visuais para o Transcript (front-end) Contexto: Transcript é um utilitário desktop (Tauri 2, Windows/Linux) de ditado por voz: hotkey global → fala → transcrição → texto inserido no app em foco. A UI atual é dark glass/acrylic, janela compacta, tudo numa tela. O front será reescrito em React 19 + Tailwind v4. Nesta fase você propõe UMA direção visual completa, em mockups HTML estáticos. ## Superfícies a cobrir (4 mockups mínimos, 7 arquivos) 1. Janela principal — estado vazio (design/proposals//main-empty.html) 2. Janela principal — com texto transcrito + painel "Refinar com IA" aberto (main-text.html) 3. Overlay de ditado — gravando (waveform) (dictation-recording.html) 4. Overlay de ditado — revisão pós-transcrição (dictation-review.html) 5. Configurações — com abas internas (settings.html) 6. Histórico — lista de áudios recentes (history.html) 7. tokens.css — direção estética em tokens CSS (tokens.css) Restrições de produto: janela pode ser maior que hoje, mas continua compacta (não é um IDE); o overlay de ditado é a superfície mais usada e aparece redimensionando a janela para ~560×124 (gravação) e ~720×~300 (revisão) — deve caber nesses tamanhos; fluxo primário: hotkey → fala → insere (velocidade acima de tudo). ## Conteúdo de cada mockup - HTML autossuficiente (CSS inline no