From 92c214254e5519a7cd6910faa19109e4362836c8 Mon Sep 17 00:00:00 2001 From: evandrodevbr Date: Sat, 8 Aug 2026 21:59:06 -0300 Subject: [PATCH] =?UTF-8?q?docs:=20plano=20de=20implementa=C3=A7=C3=A3o=20?= =?UTF-8?q?do=20redesign=20do=20front-end?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-08-08-frontend-redesign.md | 2836 +++++++++++++++++ 1 file changed, 2836 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-08-frontend-redesign.md diff --git a/docs/superpowers/plans/2026-08-08-frontend-redesign.md b/docs/superpowers/plans/2026-08-08-frontend-redesign.md new file mode 100644 index 0000000..7471f9a --- /dev/null +++ b/docs/superpowers/plans/2026-08-08-frontend-redesign.md @@ -0,0 +1,2836 @@ +# 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