@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.
- package/CHANGELOG.md +43 -0
- package/bin/cli.mjs +1 -1
- package/bin/lib/db-check-runner.mjs +5 -5
- package/bin/lib/db-migrate-runner.mjs +3 -3
- package/bin/lib/db-scaffold-runner.mjs +4 -4
- package/bin/lib/db.mjs +1 -1
- package/bin/lib/gen-manifest.mjs +0 -3
- package/bin/lib/gen-runner.mjs +2 -7
- package/bin/lib/init.mjs +6 -0
- package/bin/lib/materialize.mjs +3 -1
- package/docs/data-layer.md +2 -2
- package/docs/protocol.md +2 -2
- package/package.json +1 -1
- package/registry/instructions/opus.md +3 -0
- package/registry/skills/build-opus-ui/SKILL.md +2 -0
- package/registry/skills/create-opus-action/SKILL.md +2 -0
- package/registry/skills/implement-opus-change/SKILL.md +2 -0
- package/registry/skills/upgrade-opus/SKILL.md +2 -0
- package/registry/skills/write-product-communication/SKILL.md +28 -0
- package/registry/skills/write-product-communication/agents/openai.yaml +4 -0
- package/src/core/domain.ts +3 -6
- package/src/data/readonly-pool.ts +2 -2
- package/src/schema/entity.ts +1 -1
- package/src/ui/components/patterns/shell-nav.tsx +5 -9
- package/src/ui/components/patterns/sidebar.tsx +40 -15
- package/src/ui/docs/DocBrowser.tsx +25 -10
- package/src/ui/docs/content/actions.md +7 -6
- package/src/ui/docs/content/auth.md +3 -2
- package/src/ui/docs/content/cli.md +2 -1
- package/src/ui/docs/content/confirm.md +2 -2
- package/src/ui/docs/content/data.md +1 -1
- package/src/ui/docs/content/getting-started.md +15 -23
- package/src/ui/docs/content/microcopy.md +119 -72
- package/src/ui/docs/content/sidebar.md +21 -1
- package/src/ui/docs/content/upgrading.md +1 -1
- package/src/ui/docs/registry.tsx +1 -7
- package/src/ui/meta.ts +2 -23
- package/src/ui/react.tsx +4 -19
- package/docs/shellnav.md +0 -131
- package/src/ui/components/patterns/app-shell.tsx +0 -227
- package/src/ui/components/patterns/section-shell.tsx +0 -246
- package/src/ui/docs/content/app-shell.md +0 -155
- package/src/ui/docs/content/resizable.md +0 -86
- 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`
|
|
25
|
-
|
|
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
|
-
>
|
|
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
|
-
|
|
78
|
+
<>
|
|
79
79
|
{rotas}
|
|
80
80
|
<Toaster />
|
|
81
81
|
<DialogHost />
|
|
82
|
-
|
|
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`** —
|
|
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
|
|
8
|
-
|
|
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
|
-
>
|
|
13
|
+
> Comece pelo problema que o modelo compartilhado resolve antes de conhecer seus arquivos.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
63
|
-
>
|
|
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:
|
|
2
|
+
title: Comunicação
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# Comunicação
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15
|
+
## Antes de escrever
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
21
|
+
Organize a explicação nesta ordem sempre que ela ajudar:
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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 |
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
##
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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 `
|
|
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
|
|
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
|
|
package/src/ui/docs/registry.tsx
CHANGED
|
@@ -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: '
|
|
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
|
|
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
|
|
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,
|
|
219
|
-
export type { SidebarProps, SidebarItemProps, SidebarNavGroup } from './components/patterns/sidebar.tsx'
|
|
220
|
-
|
|
221
|
-
//
|
|
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
|
|