@softize/opus 10.0.0 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/bin/cli.mjs +1 -1
  3. package/bin/lib/db-check-runner.mjs +5 -5
  4. package/bin/lib/db-migrate-runner.mjs +3 -3
  5. package/bin/lib/db-scaffold-runner.mjs +4 -4
  6. package/bin/lib/db.mjs +1 -1
  7. package/bin/lib/gen-manifest.mjs +0 -3
  8. package/bin/lib/gen-runner.mjs +2 -7
  9. package/bin/lib/init.mjs +6 -0
  10. package/bin/lib/materialize.mjs +3 -1
  11. package/docs/data-layer.md +2 -2
  12. package/docs/protocol.md +2 -2
  13. package/package.json +1 -1
  14. package/registry/instructions/opus.md +3 -0
  15. package/registry/skills/build-opus-ui/SKILL.md +2 -0
  16. package/registry/skills/create-opus-action/SKILL.md +2 -0
  17. package/registry/skills/implement-opus-change/SKILL.md +2 -0
  18. package/registry/skills/upgrade-opus/SKILL.md +2 -0
  19. package/registry/skills/write-product-communication/SKILL.md +28 -0
  20. package/registry/skills/write-product-communication/agents/openai.yaml +4 -0
  21. package/src/core/domain.ts +3 -6
  22. package/src/data/readonly-pool.ts +2 -2
  23. package/src/schema/entity.ts +1 -1
  24. package/src/ui/components/patterns/shell-nav.tsx +5 -9
  25. package/src/ui/components/patterns/sidebar.tsx +40 -15
  26. package/src/ui/docs/DocBrowser.tsx +25 -10
  27. package/src/ui/docs/content/actions.md +7 -6
  28. package/src/ui/docs/content/auth.md +3 -2
  29. package/src/ui/docs/content/cli.md +2 -1
  30. package/src/ui/docs/content/confirm.md +2 -2
  31. package/src/ui/docs/content/data.md +1 -1
  32. package/src/ui/docs/content/getting-started.md +15 -23
  33. package/src/ui/docs/content/microcopy.md +119 -72
  34. package/src/ui/docs/content/sidebar.md +21 -1
  35. package/src/ui/docs/content/upgrading.md +1 -1
  36. package/src/ui/docs/registry.tsx +1 -7
  37. package/src/ui/meta.ts +2 -23
  38. package/src/ui/react.tsx +4 -19
  39. package/docs/shellnav.md +0 -131
  40. package/src/ui/components/patterns/app-shell.tsx +0 -227
  41. package/src/ui/components/patterns/section-shell.tsx +0 -246
  42. package/src/ui/docs/content/app-shell.md +0 -155
  43. package/src/ui/docs/content/resizable.md +0 -86
  44. package/src/ui/docs/content/section-shell.md +0 -121
@@ -21,8 +21,9 @@ interface AuthAdapter {
21
21
  }
22
22
  ```
23
23
 
24
- `req` é o request cru do `ServerAdapter`. O `user` sai dos claims/sessão; o `can` é a régua
25
- de autorização o Opus chama, não decide.
24
+ `req` é o request cru do `ServerAdapter`. O `user` vem dos claims ou da sessão, e o driver
25
+ fornece a função `can` que decide cada autorização. O Opus chama essa função sem definir a
26
+ política usada por ela.
26
27
 
27
28
  ## Drivers
28
29
 
@@ -9,7 +9,8 @@ valida as convenções e expõe o estado vivo pros agentes via MCP.
9
9
 
10
10
  ## Gates
11
11
 
12
- > A régua de entrega: 0 violação antes de qualquer handoff. Rode no CI e antes de commitar.
12
+ > Use o check no CI e antes de entregar uma mudança. Ele informa quais contratos precisam de
13
+ > ajuste e encerra com sucesso quando não encontra violações.
13
14
 
14
15
  ```bash
15
16
  opus check src # valida as convenções das actions (exit ≠ 0 se violar)
@@ -75,11 +75,11 @@ render(<Demo />)
75
75
  Monte **um** `<DialogHost />` no shell do app, ao lado do `<Toaster />`:
76
76
 
77
77
  ```tsx
78
- <AppShell sidebar={…}>
78
+ <>
79
79
  {rotas}
80
80
  <Toaster />
81
81
  <DialogHost />
82
- </AppShell>
82
+ </>
83
83
  ```
84
84
 
85
85
  Sem ele, os três **lançam** — em vez de devolver uma promise que nunca resolve. Promise pendurada viraria clique sem efeito, o pior desfecho pra uma interrupção que exige resposta: a pessoa acha que respondeu, ou clica de novo. (`<ConfirmHost />` segue valendo como alias de `<DialogHost />`.)
@@ -88,7 +88,7 @@ const { rows } = await bi.query(sqlDoLlm, { role: roleFor(ctx) })
88
88
 
89
89
  1. **Pool com teto** (`max` do pool e/ou `maxConcurrent`) — query de LLM não esgota as
90
90
  conexões do app;
91
- 2. **`BEGIN TRANSACTION READ ONLY`** — escrita morre no servidor;
91
+ 2. **`BEGIN TRANSACTION READ ONLY`** — o servidor rejeita tentativas de escrita;
92
92
  3. **`SET LOCAL ROLE` por transação** — o alcance é do CONTEXTO (crie os roles com grants
93
93
  default-fechado nas suas migrações: sem isso, `sales_read` leria `hr_employees`);
94
94
  4. **Protocolo estendido** — o SQL roda sempre com array de valores; multi-sentença
@@ -4,42 +4,34 @@ title: Getting started
4
4
 
5
5
  # Getting started
6
6
 
7
- O Opus é um SDK/protocolo spec-driven: o domínio **É** a aplicação (contratos +
8
- actions); front e back são cascas. Um projeto Opus-based pina o Opus, herda o runtime e segue os gates.
7
+ O Opus ajuda cliente e servidor a preservar as mesmas regras à medida que uma aplicação
8
+ evolui. Entidades e contratos de action ficam em uma camada compartilhada; o runtime, a UI e
9
+ as ferramentas usam essas definições sem exigir schemas e tipos paralelos.
9
10
 
10
11
  ## O modelo
11
12
 
12
- > Antes de qualquer arquivo: por que o Opus existe e o que ele assume.
13
+ > Comece pelo problema que o modelo compartilhado resolve antes de conhecer seus arquivos.
13
14
 
14
- A aplicação é o **domínio** entidades, dicts e contratos de action. A API e a web são cascas
15
- que consomem esse domínio: zero shape duplicado. O Opus entrega o núcleo (runtime, adapters, UI,
16
- CLI) e os gates que mantêm o padrão. O que cada projeto adiciona é o seu domínio; o resto vem pinado.
15
+ As regras de uma aplicação costumam aparecer em vários lugares: validação, transporte, API,
16
+ formulários e documentação. No Opus, entidades, dicts e contratos de action descrevem essas
17
+ regras uma vez. A API e a web consomem a mesma definição, enquanto runtime, adapters, UI, CLI
18
+ e verificações ajudam a mantê-la consistente. Cada projeto acrescenta seu domínio sobre essa
19
+ base versionada.
17
20
 
18
21
  ## Começar do zero
19
22
 
20
23
  > Projeto novo não se monta à mão: o esqueleto canônico sai do `opus create`, com os
21
24
  > pré-requisitos do protocolo, da UI e do preview já plugados — e os gates verdes.
22
25
 
23
- O Opus mora no registry da Softize (não no npm público) — uma vez por máquina, ensine
24
- o escopo ao pnpm; dentro do projeto criado o `.npmrc` já cuida disso:
25
-
26
26
  ```bash
27
- pnpm config set @softize:registry https://registry.softize.com.br/ --global # 1x por máquina
28
-
29
27
  pnpm dlx @softize/opus create meu-app
30
28
  cd meu-app && git init && pnpm install && pnpm test
31
29
  ```
32
30
 
33
- Máquina nova: se o pnpm reclamar que o diretório global de binários não está no PATH
34
- (o `dlx` exige), rode `pnpm setup` e reabra o terminal — também é 1x por máquina. Se o
35
- setup acusar seção pnpm antiga no shell rc (`ERR_PNPM_BAD_SHELL_SECTION`), é resquício
36
- de versão anterior: `pnpm setup --force` substitui o bloco dele.
37
-
38
- Release recém-saída (menos de 24h): a quarentena do pnpm resolve o `dlx` pra versão
39
- ANTIGA em silêncio ("Comando desconhecido" = sintoma clássico). Fure com a versão
40
- explícita — `pnpm dlx @softize/opus@<versão> create meu-app` — ou com a flag
41
- `pnpm --config.minimum-release-age=0 dlx …`. Dentro de projeto o pnpm se resolve
42
- sozinho (auto-exclui o Opus da quarentena no install).
31
+ O comando cria a estrutura inicial e instala a versão estável disponível. Para experimentar
32
+ uma versão específica, acrescente-a ao pacote: `pnpm dlx @softize/opus@<versão> create meu-app`.
33
+ Se o pnpm informar que o diretório global de binários não está no `PATH`, execute `pnpm setup`
34
+ e abra uma nova sessão do terminal antes de tentar novamente.
43
35
 
44
36
  Nasce com: `opus.config.ts` + domínio-exemplo canônico (0 violações, spec documentada),
45
37
  vite + react + tema do Opus (Tailwind v4 CSS-first), dev server na porta que o Maestro
@@ -59,8 +51,8 @@ pnpm dlx @softize/opus create apps/portal # o app (modo detectado)
59
51
 
60
52
  ## Carregar o Opus num projeto existente
61
53
 
62
- > O pin vive no `opus.json`; o setup grava o esqueleto local. Ausência de `opus.json` = projeto
63
- > não inicializado não improvise, rode o setup.
54
+ > O `opus.json` registra a versão usada pelo projeto. Se o arquivo ainda não existe, o setup
55
+ > cria a estrutura necessária antes das demais etapas.
64
56
 
65
57
  A versão do Opus fica pinada em `opus.json`. O `opus setup` é per-app: grava o `opus.json`,
66
58
  mantém o bloco gerenciado do `CLAUDE.md` (fora dele o arquivo é seu) e semeia o dia zero
@@ -1,83 +1,130 @@
1
1
  ---
2
- title: Microcopy
2
+ title: Comunicação
3
3
  ---
4
4
 
5
- # Microcopy
5
+ # Comunicação
6
6
 
7
- Código em inglês; o que humano (UI, comentário, doc) em pt-BR. Frases começam com
8
- maiúscula e terminam com ponto; labels curtos em sentence case, sem ponto. **Esta página
9
- é a fonte** `build-opus-ui` aplica esta convenção (regra duplicada em vez de apontada é
10
- defeito).
7
+ Toda comunicação do produto deve ajudar alguém a compreender o que está acontecendo e a
8
+ seguir adiante com confiança. Esta orientação vale para documentação, interfaces, mensagens
9
+ de erro, CLI, onboarding, textos explicativos, comentários e respostas produzidas por agentes.
11
10
 
12
- ## Língua por camada
11
+ Clareza não significa apenas escrever frases curtas. Um texto claro parte da situação do
12
+ leitor, apresenta o que ele precisa saber e oferece contexto suficiente para que a próxima
13
+ decisão não dependa de adivinhação.
13
14
 
14
- > Regra dura — não tem caso a caso.
15
+ ## Antes de escrever
15
16
 
16
- | Camada | Língua |
17
- |---|---|
18
- | Identificadores de código (vars, funções, tipos, arquivos, dirs, domínios) | Inglês |
19
- | Nomes de action / rota | Inglês (`resource.verb`) |
20
- | Colunas e tabelas de banco | Inglês (snake_case) |
21
- | Valores de código (enums, status, chaves de dict) | Inglês — mesmo em exemplos e fixtures (`active`, não `ativo`); a UI mostra o label pt |
22
- | Texto de UI visível (labels, headings, botões, placeholders, mensagens) | pt-BR |
23
- | Comentários e docs (`.md`) | pt-BR |
17
+ Identifique quem lerá o texto, o que essa pessoa está tentando fazer e qual decisão ou ação
18
+ deve ser possível depois da leitura. Quando essas respostas não estiverem claras, investigue o
19
+ fluxo antes de escolher as palavras.
24
20
 
25
- ## Frase vs label
21
+ Organize a explicação nesta ordem sempre que ela ajudar:
26
22
 
27
- > Frase (mensagem, descrição, tooltip, help): maiúscula no início, ponto no fim. Label
28
- > (botão, heading, item de menu, chip): sentence case, sem ponto.
23
+ 1. apresente a situação ou o problema reconhecível pelo leitor;
24
+ 2. explique o benefício ou o efeito esperado;
25
+ 3. descreva o mecanismo somente no nível necessário;
26
+ 4. indique a próxima ação quando houver uma.
29
27
 
30
- | Tipo | Assim | Assim não |
28
+ Não comece pela arquitetura interna quando a necessidade do leitor oferece uma introdução
29
+ mais natural. Termos técnicos são bem-vindos quando acrescentam precisão, mas devem ser
30
+ apresentados antes de serem usados como parte da explicação.
31
+
32
+ ## Voz
33
+
34
+ Escreva de maneira calma, próxima e segura. A comunicação pode ser firme sem parecer uma
35
+ ordem interna ou um manifesto.
36
+
37
+ - Prefira explicações completas a slogans, máximas e frases de efeito.
38
+ - Evite absolutos como “sempre”, “nunca”, “zero” e “é” quando houver contexto, condição ou
39
+ exceção relevante.
40
+ - Não use caixa-alta, negrito ou pontuação para fabricar autoridade.
41
+ - Evite sequências telegráficas, equações e símbolos usados como conectores em prosa.
42
+ - Não presuma que o leitor já concorda com a arquitetura ou conhece o vocabulário da equipe.
43
+ - Explique decisões pelo valor que oferecem às pessoas, não apenas pela disciplina que impõem.
44
+ - Mantenha o mesmo termo para o mesmo conceito em todas as superfícies.
45
+
46
+ Em vez de “O domínio é a aplicação. Front e back são cascas”, explique a relação:
47
+
48
+ > O Opus mantém as regras e os contratos do domínio em uma camada compartilhada. Assim,
49
+ > cliente e servidor trabalham com as mesmas definições, sem duplicar schemas, validações ou
50
+ > tipos de transporte.
51
+
52
+ ## Documentação
53
+
54
+ Uma documentação deve formar o modelo mental do leitor antes de cobrar decisões baseadas
55
+ nele. Apresente primeiro o problema, depois a ideia, um exemplo concreto e os próximos passos.
56
+ Detalhes operacionais e situações excepcionais devem ficar próximos da etapa em que se tornam
57
+ úteis, sem interromper a introdução principal.
58
+
59
+ Ao registrar uma norma, deixe claro o peso da afirmação:
60
+
61
+ - **Princípio:** explica a intenção que orienta as decisões.
62
+ - **Regra:** descreve um requisito obrigatório e seu escopo.
63
+ - **Recomendação:** apresenta o caminho preferido quando outras opções continuam válidas.
64
+ - **Comportamento:** informa o que o produto faz automaticamente.
65
+ - **Verificação:** mostra como confirmar que a regra foi atendida.
66
+ - **Exceção:** delimita quando a orientação não se aplica.
67
+
68
+ Uma regra normativa deve informar a condição em que se aplica, a razão que a sustenta e como
69
+ ela é verificada. Por exemplo:
70
+
71
+ > Cada action deve ser declarada em um arquivo próprio. Essa separação permite que o registro e
72
+ > os geradores identifiquem cada action de forma determinística. O `opus check` informa quais
73
+ > arquivos precisam ser separados quando encontra mais de uma declaração.
74
+
75
+ Evite substituir essa explicação por “Uma por arquivo. O gate reprova”. A versão curta omite o
76
+ motivo, não delimita o escopo e transforma uma ajuda em uma advertência.
77
+
78
+ ## Mensagens de erro
79
+
80
+ Uma mensagem de erro deve responder, nessa ordem, ao que aconteceu, como isso afeta a tarefa e
81
+ o que a pessoa pode fazer. Não culpe o usuário e não exponha endpoint, stack trace, status HTTP
82
+ ou detalhes do runtime na interface; esses dados pertencem ao log ou à área de diagnóstico.
83
+
84
+ | Situação | Prefira | Evite |
85
+ |---|---|---|
86
+ | Serviço indisponível | O Multica não respondeu. Tente novamente em alguns instantes. | ERRO: request failed (500) |
87
+ | Recurso ausente | Este workspace não existe mais. Volte à lista para escolher outro. | Workspace inválido. |
88
+ | Permissão | Você não tem acesso a este workspace. Peça acesso a um administrador. | Acesso negado. |
89
+ | Validação | Informe um endereço de e-mail válido. | Campo inválido. |
90
+
91
+ Quando não houver uma ação possível, diga isso com honestidade e preserve o trabalho já feito.
92
+ Não ofereça “tente novamente” como resposta automática para falhas que uma nova tentativa não
93
+ pode resolver.
94
+
95
+ ## Textos de interface
96
+
97
+ Código e valores internos permanecem em inglês. O que uma pessoa lê na interface usa pt-BR,
98
+ salvo quando o produto declarar outro idioma.
99
+
100
+ - Frases começam com maiúscula e terminam com ponto.
101
+ - Labels, títulos, itens de menu e botões usam sentence case e não levam ponto.
102
+ - Placeholders de seleção usam `Selecione <artigo> <coisa>…`.
103
+ - Placeholders ajudam a responder, mas não substituem o label do campo.
104
+ - Botões descrevem a ação que executarão, como `Publicar` ou `Salvar alterações`.
105
+ - Textos de ajuda explicam o efeito percebido, não a implementação interna.
106
+ - Abreviações são usadas somente quando o espaço ou o vocabulário do domínio as justificam.
107
+
108
+ | Tipo | Prefira | Evite |
31
109
  |---|---|---|
32
- | Frase | Opus não inicializado. | ~~opus nao inicializado~~ |
33
- | Frase | O Multica não respondeu — tente de novo. | ~~ERRO: request failed (500)~~ |
34
- | Label | Publicar | ~~PUBLICAR~~ |
35
- | Label | Nova sessão | ~~Nova sessão.~~ |
36
- | Placeholder | Selecione um cliente… | ~~Selecionar~~ · ~~digite o nome…~~ |
37
-
38
- ## As regras que mais pegam
39
-
40
- > Erros recorrentes de revisão atenção redobrada nestes.
41
-
42
- - **Maiúscula é ortografia, não estilo.** Toda frase começa com maiúscula — em UI,
43
- hint/help **e comentário de código** e nome próprio idem (Engineer, Multica, GitHub).
44
- Vale com interpolação: mensagem que começaria com valor ganha um artigo na frente
45
- (`O workspace '${slug}' não existe…`).
46
- - **Frase fluida, não telegrama.** Símbolo não é conector: nada de `—`/`+`/`≈` amarrando
47
- ideias em label ou help. `Staff acesso ao back-office + todos os workspaces` vira
48
- label `Staff` + help `Tem acesso ao back-office e a todos os workspaces.`.
49
- - **"Opus" e "Softize" são nomes próprios.** Em prosa, maiúscula (`o Opus`, `a Softize`);
50
- os tokens técnicos ficam minúsculos: `opus check`, `@softize/opus`, `opus.json`,
51
- `softize.com.br`. É o nome maiúscula; é código minúscula.
52
- - **Abreviação é último caso.** Por extenso: `mínimo`, `máximo`, `configuração`.
53
- `Config indisponível.` é duplamente errado (seco E abreviado): `A configuração não
54
- carregou.`. `Ex.:` em placeholder é tolerado (com `E` maiúsculo).
55
- - **Placeholder de seleção segue UM padrão** (select simples, buscável, múltiplo):
56
- `Selecione <artigo> <coisa>…` — com `…` (o caractere único), nunca o genérico
57
- `Selecionar`. Declare no contrato (`fields.<campo>.placeholder`) — o default do
58
- componente é rede de segurança, não estilo; dois pickers na mesma tela com padrões
59
- diferentes é defeito.
60
- - **Nada de maiúsculo deliberado.** Sem ALL-CAPS — nem a classe `uppercase` — em labels,
61
- headers ou badges.
62
- - **Exemplos são neutros e duráveis.** Nada de cliente real ou com cara de real: use
63
- Empresa X / Empresa Y (`contato@empresa-x.com.br`). Exemplo "de verdade" envelhece,
64
- vaza contexto e vira dívida.
65
-
66
- ## Tom
67
-
68
- > Fale com o usuário, não com o log.
69
-
70
- - **Calmo e informativo, não alarmista.** "Indisponível" em vez de "ERRO"/"FALHOU".
71
- Não culpe o usuário.
72
- - **Orientado à ação.** Quando dá, diga o próximo passo: `Inicie o ambiente`,
73
- `Crie a primeira acima`.
74
- - **Conciso, mas natural — não telegráfico.** Prefira a forma completa do verbo a um
75
- corte abrupto. O meio-termo é a régua: nem seco demais (`rode no projeto`) nem manual
76
- técnico (jargão de arquitetura num help).
77
- - **Explique pelo efeito, não pelo mecanismo.** Se precisou citar sistema interno pra
78
- explicar (`o push sobrescreve o runtime`), reescreva pelo que acontece pro usuário
79
- (`os agentes deste workspace passam a usar esta skill`). Mecanismo é doc/comentário,
80
- não tooltip.
81
- - **Nada de endpoint, status ou stack na tela** — detalhe técnico vai pro console/log.
82
- - **Termos do produto** (sessão, ambiente, cliente, agente), não do código. E o mesmo
83
- termo sempre: se a UI fala "cliente", nenhum label diz "client".
110
+ | Frase | A configuração não carregou. | Config indisponível. |
111
+ | Label | Nova sessão | Nova sessão. |
112
+ | Botão | Publicar | PUBLICAR |
113
+ | Placeholder | Selecione um cliente… | Selecionar |
114
+ | Ajuda | Os agentes deste workspace passam a usar esta skill. | O push sobrescreve o runtime. |
115
+
116
+ ## Revisão
117
+
118
+ Antes de entregar, releia o texto fora do contexto da implementação e confirme:
119
+
120
+ - o leitor consegue reconhecer a situação sem conhecer o código;
121
+ - conceitos novos são explicados antes de orientar decisões;
122
+ - regras obrigatórias estão separadas de recomendações;
123
+ - a razão da orientação aparece quando ajuda a compreendê-la;
124
+ - mensagens de erro preservam a calma e oferecem uma saída real;
125
+ - a frase soa como uma conversa profissional, não como log, slogan ou ordem interna;
126
+ - detalhes técnicos permanecem disponíveis no lugar adequado sem dominar a comunicação.
127
+
128
+ Leia o fluxo completo, não apenas strings isoladas. Um conjunto de frases corretas ainda pode
129
+ produzir uma experiência confusa quando repete informações, muda de vocabulário ou apresenta
130
+ ações fora da ordem em que a pessoa precisa delas.
@@ -27,7 +27,7 @@ render(
27
27
 
28
28
  ## Colapso
29
29
 
30
- `collapsed` pertence à própria `Sidebar`; `SidebarItem` e `SidebarNav` adaptam-se automaticamente para botões `size-9` centralizados, ícones e tooltips. `PaneHeader`, `PaneContent` e `PaneFooter` são slots do pane: a aplicação mantém a identidade e ações que lhe pertencem sem atribuí-las artificialmente à sidebar. `SidebarHeader`, `SidebarContent` e `SidebarFooter` seguem como aliases deprecated durante a migração.
30
+ `collapsed` pertence à própria `Sidebar`; `SidebarItem`, `SidebarNav` e `ShellNav` adaptam-se automaticamente para botões `size-9` centralizados, ícones e tooltips. `PaneHeader`, `PaneContent` e `PaneFooter` são os slots do pane: a aplicação mantém a identidade e ações que lhe pertencem sem atribuí-las artificialmente à sidebar.
31
31
 
32
32
  ```tsx
33
33
  <Sidebar collapsed={collapsed}>
@@ -36,3 +36,23 @@ render(
36
36
  <PaneFooter><UserMenu /></PaneFooter>
37
37
  </Sidebar>
38
38
  ```
39
+
40
+ ## Navegação contextual
41
+
42
+ `SidebarNav` aceita grupos e subgrupos. Isso cobre tanto a navegação global quanto seções internas, como Configurações ou documentação, sem outro shell especializado.
43
+
44
+ ```tsx
45
+ <SidebarNav
46
+ groups={[
47
+ {
48
+ label: 'Configurações',
49
+ subgroups: [
50
+ { label: 'Acesso', items: [{ id: 'users', label: 'Usuários' }] },
51
+ { label: 'Dados', items: [{ id: 'imports', label: 'Importações' }] },
52
+ ],
53
+ },
54
+ ]}
55
+ activeId={active}
56
+ onSelect={go}
57
+ />
58
+ ```
@@ -6,7 +6,7 @@ title: Atualizar o Opus
6
6
 
7
7
  Projeto em produção fica meses sem tocar no Opus — e um dia precisa pular várias
8
8
  versões de uma vez. O ritual é o mesmo para humano e IA, e não depende de memória:
9
- cada peça do caminho está escrita ou é um gate que quebra alto.
9
+ cada etapa está documentada ou é verificada por um gate que informa o que precisa ser corrigido.
10
10
 
11
11
  ## 1. Saber que está atrás
12
12
 
@@ -80,7 +80,6 @@ import paginationMd from './content/pagination.md?raw'
80
80
  import popoverMd from './content/popover.md?raw'
81
81
  import progressMd from './content/progress.md?raw'
82
82
  import radioGroupMd from './content/radio-group.md?raw'
83
- import resizableMd from './content/resizable.md?raw'
84
83
  import scrollAreaMd from './content/scroll-area.md?raw'
85
84
  import selectMd from './content/select.md?raw'
86
85
  import separatorMd from './content/separator.md?raw'
@@ -114,8 +113,6 @@ import actionViewMd from './content/action-view.md?raw'
114
113
  import actionTriggerMd from './content/action-trigger.md?raw'
115
114
  import actionSearchDialogMd from './content/action-list-dialog.md?raw'
116
115
  import pageMd from './content/page.md?raw'
117
- import appShellMd from './content/app-shell.md?raw'
118
- import sectionShellMd from './content/section-shell.md?raw'
119
116
  import sidebarMd from './content/sidebar.md?raw'
120
117
  import splitMd from './content/split.md?raw'
121
118
  import routerMd from './content/router.md?raw'
@@ -270,7 +267,7 @@ export const UI_SECTIONS: DocSection[] = [
270
267
  {
271
268
  pages: [
272
269
  { slug: 'tokens', title: 'Tokens & Tema', render: doc(tokensMd) },
273
- { slug: 'microcopy', title: 'Microcopy', render: doc(microcopyMd) },
270
+ { slug: 'microcopy', title: 'Comunicação', render: doc(microcopyMd) },
274
271
  { slug: 'customization', title: 'Customização', render: doc(customizationMd) },
275
272
  ],
276
273
  },
@@ -295,11 +292,8 @@ export const UI_SECTIONS: DocSection[] = [
295
292
  { slug: 'aspect-ratio', title: 'Aspect Ratio', render: comp('Aspect Ratio', 'aspect-ratio', aspectRatioMd) },
296
293
  { slug: 'card', title: 'Card', render: comp('Card', 'card', cardMd) },
297
294
  { slug: 'collapsible', title: 'Collapsible', render: comp('Collapsible', 'collapsible', collapsibleMd) },
298
- { slug: 'resizable', title: 'Resizable', badge: componentMeta.resizable.deprecated ? 'deprecated' : undefined, render: comp('Resizable', 'resizable', resizableMd) },
299
295
  { slug: 'scroll-area', title: 'Scroll Area', render: comp('Scroll Area', 'scroll-area', scrollAreaMd) },
300
296
  { slug: 'separator', title: 'Separator', render: comp('Separator', 'separator', separatorMd) },
301
- { slug: 'app-shell', title: 'App Shell', badge: componentMeta['app-shell'].deprecated ? 'deprecated' : undefined, render: pattern('App Shell', 'app-shell', appShellMd) },
302
- { slug: 'section-shell', title: 'Section Shell', badge: componentMeta['section-shell'].deprecated ? 'deprecated' : undefined, render: pattern('Section Shell', 'section-shell', sectionShellMd) },
303
297
  ],
304
298
  },
305
299
  {
package/src/ui/meta.ts CHANGED
@@ -283,24 +283,17 @@ export const componentMeta = {
283
283
  whenToUse:
284
284
  'Escolha única entre opções mutuamente exclusivas, todas visíveis ao mesmo tempo (Radix). Cada RadioGroupItem tem um `value`; o item escolhido vira o `value` do RadioGroup, controlado por `value`/`onValueChange` (ou `defaultValue` no modo não controlado). Pareie cada item com um Label. Pra poucas opções que cabem na tela; com muitas, prefira Select; pra ligar/desligar um único item, Checkbox ou Switch.',
285
285
  },
286
- 'resizable': {
287
- name: 'resizable',
288
- ancestry: 'shadcn',
289
- whenToUse:
290
- 'Painéis redimensionáveis por arraste (react-resizable-panels). Compõe ResizablePanelGroup > ResizablePanel + ResizableHandle entre eles; `orientation` (horizontal/vertical) define a direção do arraste, `defaultSize`/`minSize`/`maxSize` (em %) limitam cada painel e `withHandle` desenha a pega na divisória. O grupo ocupa a altura do pai (h-full), então dê tamanho ao container. Pra alternar entre painéis sem dividir a vista, use Tabs.',
291
- deprecated: { alternative: '`Split resizable` + `Pane`', since: 'próxima versão' },
292
- },
293
286
  'split': {
294
287
  name: 'split',
295
288
  ancestry: 'opus',
296
289
  whenToUse:
297
- 'Divide uma área em panes em sequência horizontal ou vertical. Use `resizable` quando a pessoa deve ajustar a fronteira; o mesmo `<Split>` vira flex simples sem ele. Cada `<Pane>` declara tamanho inicial/mínimo e inset. É o mecanismo espacial para sidebar, conteúdo e rail — não use AppShell/SectionShell novos.',
290
+ 'Divide uma área em panes em sequência horizontal ou vertical. Use `resizable` quando a pessoa deve ajustar a fronteira; o mesmo `<Split>` vira flex simples sem ele. Cada `<Pane>` declara tamanho inicial/mínimo e inset. É o mecanismo espacial para sidebar, conteúdo e rail.',
298
291
  },
299
292
  'sidebar': {
300
293
  name: 'sidebar',
301
294
  ancestry: 'opus',
302
295
  whenToUse:
303
- 'Chrome e navegação de uma coluna lateral, encaixada onde um Split decidir. `Sidebar` possui o colapso; `PaneHeader`, `PaneContent` e `PaneFooter` estruturam qualquer pane, e `SidebarNav`/`SidebarItem` apresentam a navegação. Serve tanto a barra global quanto uma nav contextual; em Split redimensionável passe `divider={false}` para não duplicar a divisória. `SidebarHeader`, `SidebarContent` e `SidebarFooter` são aliases deprecated.',
296
+ 'Chrome e navegação de uma coluna lateral, encaixada onde um Split decidir. `Sidebar` possui o colapso; `PaneHeader`, `PaneContent` e `PaneFooter` estruturam qualquer pane, e `SidebarNav`/`SidebarItem` apresentam navegação com grupos e subgrupos. Serve tanto a barra global quanto uma nav contextual; em Split redimensionável passe `divider={false}` para não duplicar a divisória.',
304
297
  },
305
298
  'scroll-area': {
306
299
  name: 'scroll-area',
@@ -381,20 +374,6 @@ export const componentMeta = {
381
374
  whenToUse:
382
375
  'O esqueleto de página do back-office: <main> + container de largura cheia (className="max-w-5xl" estreita e centra) + header com título, descrição e ação à direita (em geral o criar). Presentacional (a página agrega N fontes); o action-driven mora dentro. Pra listagem em modal, ActionListDialog.',
383
376
  },
384
- 'app-shell': {
385
- name: 'app-shell',
386
- ancestry: 'opus',
387
- whenToUse:
388
- 'O chrome da aplicação: sidebar de navegação (header + nav rolável + rodapé ancorado) + conteúdo, com rail opcional à direita (chat de agente, inspetor) em painéis redimensionáveis. É o quadro — o que vai em cada slot é do app. Pro esqueleto de UMA página (título, descrição, ação), Page. Chrome `flush` opcional (full-bleed, filete à esquerda) com <AppShellBar> — a faixa h-12 border-b usada na sidebar (marca) e no topo do conteúdo pra linha do header atravessar a tela. Sidebar recolhível com `collapsible` (opt-in): o shell controla a largura e publica `data-collapsed` no aside; o rótulo some por CSS (`group-data-[collapsed=true]/sidebar:hidden`) porque o nav é do app. O botão é o <AppShellTrigger />, posicionado pelo consumidor; `useAppShell()` dá o estado em JS.',
389
- deprecated: { alternative: '`Split` + `Pane` + `Sidebar`', since: 'próxima versão' },
390
- },
391
- 'section-shell': {
392
- name: 'section-shell',
393
- ancestry: 'opus',
394
- whenToUse:
395
- 'Uma SEÇÃO com navegação própria — o nível entre AppShell (o app) e Page (uma tela): nav w-56 com filete + painel, pras telas irmãs de Configurações, Relatórios ou doc. Use quando a seção é visitada raro e navegada por dentro quando visitada (não vale queimar item na sidebar do app), ou quando a lista é dinâmica demais pra um nav estático. Pra facetas do MESMO objeto, Tabs. Controlado (activeId + onSelect): o roteamento é do app. Grupos aceitam `items` e/ou `subgroups` (o 3º nível, ex.: sub-pasta na doc). O painel remonta quando o `activeId` muda — é o que zera o scroll; `scrollResetKey` sobrescreve a chave nos dois sentidos: constante = nunca remonta (preserva o scroll), mais fina que o activeId = remonta também dentro da mesma tela.',
396
- deprecated: { alternative: '`Split` + `Pane` + `Sidebar`', since: 'próxima versão' },
397
- },
398
377
  'router': {
399
378
  name: 'router',
400
379
  ancestry: 'opus',
package/src/ui/react.tsx CHANGED
@@ -164,7 +164,6 @@ export { Kbd, KbdGroup } from './components/primitives/kbd.tsx'
164
164
  export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from './components/primitives/pagination.tsx'
165
165
  export { Progress } from './components/primitives/progress.tsx'
166
166
  export { RadioGroup, RadioGroupItem } from './components/primitives/radio-group.tsx'
167
- export { ResizableHandle, ResizablePanel, ResizablePanelGroup } from './components/primitives/resizable.tsx'
168
167
  export { ScrollArea, ScrollBar } from './components/primitives/scroll-area.tsx'
169
168
  export { Slider } from './components/primitives/slider.tsx'
170
169
  export { Switch } from './components/primitives/switch.tsx'
@@ -206,29 +205,15 @@ export type { DataStateProps } from './components/patterns/data-state.tsx'
206
205
  export { Page } from './components/patterns/page.tsx'
207
206
  export type { PageProps } from './components/patterns/page.tsx'
208
207
 
209
- // Chrome da aplicação (sidebar + conteúdo + rail opcional redimensionável).
210
- export { AppShell, AppShellBar, AppShellTrigger, useAppShell, useSidebarSlot } from './components/patterns/app-shell.tsx'
211
- export type { AppShellProps } from './components/patterns/app-shell.tsx'
212
-
213
208
  // Layout composicional: Split decide a relação espacial; Pane carrega conteúdo com inset.
214
209
  export { Split, Pane } from './components/patterns/split.tsx'
215
210
  export type { SplitProps, PaneProps } from './components/patterns/split.tsx'
216
211
 
217
212
  // Barra lateral composicional: header/conteúdo/footer e navegação, sem possuir o layout.
218
- export { PaneHeader, PaneContent, PaneFooter, Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarItem, SidebarNav } from './components/patterns/sidebar.tsx'
219
- export type { SidebarProps, SidebarItemProps, SidebarNavGroup } from './components/patterns/sidebar.tsx'
220
-
221
- // Seção com navegação própria (nav w-56 + painel) o nível entre o AppShell e a Page.
222
- export { SectionShell } from './components/patterns/section-shell.tsx'
223
- export type {
224
- SectionShellProps,
225
- SectionNavGroup,
226
- SectionNavSubgroup,
227
- SectionNavItem,
228
- } from './components/patterns/section-shell.tsx'
229
-
230
- // O menu como primitivo PIVOTÁVEL: mesmo nav na sidebar do AppShell ou dentro do conteúdo
231
- // (colapso por CSS via o grupo `sidebar`). Ver docs/shellnav.md.
213
+ export { PaneHeader, PaneContent, PaneFooter, Sidebar, SidebarItem, SidebarNav } from './components/patterns/sidebar.tsx'
214
+ export type { SidebarProps, SidebarItemProps, SidebarNavGroup, SidebarNavSubgroup, SidebarNavItem } from './components/patterns/sidebar.tsx'
215
+
216
+ // O menu como primitivo pivotável: recolhe quando composto dentro de Sidebar.
232
217
  export { ShellNav, ShellNavHeading } from './components/patterns/shell-nav.tsx'
233
218
  export type { ShellNavProps, ShellNavGroup, ShellNavItem, ShellNavHeadingProps } from './components/patterns/shell-nav.tsx'
234
219