@softize/opus 9.1.1 → 11.0.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 +52 -3
- package/bin/lib/check.mjs +37 -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/docs/data-layer.md +2 -2
- package/docs/elevation-scale.md +2 -2
- package/docs/protocol.md +2 -2
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/SKILL.md +8 -3
- package/registry/skills/build-opus-ui/references/evaluations.md +2 -0
- package/registry/skills/build-opus-ui/references/ui-patterns.md +3 -0
- package/src/core/domain.ts +3 -6
- 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/components/primitives/alert-dialog.tsx +1 -1
- package/src/ui/components/primitives/calendar.tsx +1 -1
- package/src/ui/components/primitives/card.tsx +3 -3
- package/src/ui/components/primitives/chat.tsx +1 -1
- package/src/ui/components/primitives/composer.tsx +2 -2
- package/src/ui/components/primitives/dialog.tsx +1 -1
- package/src/ui/components/primitives/menu.tsx +2 -2
- package/src/ui/components/primitives/popover.tsx +1 -1
- package/src/ui/docs/DocBrowser.tsx +25 -10
- package/src/ui/docs/content/card.md +1 -1
- package/src/ui/docs/content/composer.md +1 -1
- package/src/ui/docs/content/confirm.md +2 -2
- package/src/ui/docs/content/sidebar.md +21 -1
- package/src/ui/docs/content/tokens.md +4 -7
- package/src/ui/docs/registry.tsx +0 -6
- package/src/ui/meta.ts +3 -24
- package/src/ui/react.tsx +4 -19
- package/src/ui/theme.css +0 -7
- 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
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
## Horizontal
|
|
2
|
-
|
|
3
|
-
ResizablePanelGroup envolve os painéis e o ResizableHandle entre eles. O grupo ocupa a altura do pai (h-full), então dê um tamanho ao container.
|
|
4
|
-
|
|
5
|
-
```tsx preview col
|
|
6
|
-
<div className="h-48 rounded-md border">
|
|
7
|
-
<ResizablePanelGroup orientation="horizontal">
|
|
8
|
-
<ResizablePanel defaultSize={35}>
|
|
9
|
-
<div className="flex h-full items-center justify-center p-4 text-sm">
|
|
10
|
-
Árvore do repositório
|
|
11
|
-
</div>
|
|
12
|
-
</ResizablePanel>
|
|
13
|
-
<ResizableHandle />
|
|
14
|
-
<ResizablePanel defaultSize={65}>
|
|
15
|
-
<div className="flex h-full items-center justify-center p-4 text-sm">
|
|
16
|
-
Diff da sessão
|
|
17
|
-
</div>
|
|
18
|
-
</ResizablePanel>
|
|
19
|
-
</ResizablePanelGroup>
|
|
20
|
-
</div>
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Com alça visível
|
|
24
|
-
|
|
25
|
-
withHandle no ResizableHandle desenha a pega central — facilita acertar o arraste quando a divisória precisa ficar óbvia.
|
|
26
|
-
|
|
27
|
-
```tsx preview col
|
|
28
|
-
<div className="h-48 rounded-md border">
|
|
29
|
-
<ResizablePanelGroup orientation="horizontal">
|
|
30
|
-
<ResizablePanel defaultSize={40} minSize={20}>
|
|
31
|
-
<div className="flex h-full items-center justify-center p-4 text-sm">
|
|
32
|
-
Painel do agente developer
|
|
33
|
-
</div>
|
|
34
|
-
</ResizablePanel>
|
|
35
|
-
<ResizableHandle withHandle />
|
|
36
|
-
<ResizablePanel defaultSize={60} minSize={20}>
|
|
37
|
-
<div className="flex h-full items-center justify-center p-4 text-sm">
|
|
38
|
-
Preview do worktree
|
|
39
|
-
</div>
|
|
40
|
-
</ResizablePanel>
|
|
41
|
-
</ResizablePanelGroup>
|
|
42
|
-
</div>
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Aninhado
|
|
46
|
-
|
|
47
|
-
Um ResizablePanelGroup dentro de um painel cruza as direções: orientation=vertical empilha os painéis e a alça vira horizontal.
|
|
48
|
-
|
|
49
|
-
```tsx preview col
|
|
50
|
-
<div className="h-64 rounded-md border">
|
|
51
|
-
<ResizablePanelGroup orientation="horizontal">
|
|
52
|
-
<ResizablePanel defaultSize={30}>
|
|
53
|
-
<div className="flex h-full items-center justify-center p-4 text-sm">
|
|
54
|
-
Workspace Empresa X
|
|
55
|
-
</div>
|
|
56
|
-
</ResizablePanel>
|
|
57
|
-
<ResizableHandle withHandle />
|
|
58
|
-
<ResizablePanel defaultSize={70}>
|
|
59
|
-
<ResizablePanelGroup orientation="vertical">
|
|
60
|
-
<ResizablePanel defaultSize={60}>
|
|
61
|
-
<div className="flex h-full items-center justify-center p-4 text-sm">
|
|
62
|
-
Chat do reviewer
|
|
63
|
-
</div>
|
|
64
|
-
</ResizablePanel>
|
|
65
|
-
<ResizableHandle withHandle />
|
|
66
|
-
<ResizablePanel defaultSize={40}>
|
|
67
|
-
<div className="flex h-full items-center justify-center p-4 text-sm">
|
|
68
|
-
Saída do opus check
|
|
69
|
-
</div>
|
|
70
|
-
</ResizablePanel>
|
|
71
|
-
</ResizablePanelGroup>
|
|
72
|
-
</ResizablePanel>
|
|
73
|
-
</ResizablePanelGroup>
|
|
74
|
-
</div>
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Props
|
|
78
|
-
|
|
79
|
-
| Prop | Tipo | Default | Descrição |
|
|
80
|
-
|---|---|---|---|
|
|
81
|
-
| `orientation (ResizablePanelGroup)` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do arraste — horizontal vira colunas, vertical empilha em linhas. |
|
|
82
|
-
| `defaultSize (ResizablePanel)` | `number \| string` | | Tamanho inicial do painel, em porcentagem do grupo. |
|
|
83
|
-
| `minSize (ResizablePanel)` | `number \| string` | | Tamanho mínimo até onde o painel encolhe no arraste. |
|
|
84
|
-
| `maxSize (ResizablePanel)` | `number \| string` | | Tamanho máximo até onde o painel cresce no arraste. |
|
|
85
|
-
| `collapsible (ResizablePanel)` | `boolean` | `false` | Deixa o painel colapsar ao ser arrastado abaixo do minSize. |
|
|
86
|
-
| `withHandle (ResizableHandle)` | `boolean` | `false` | Desenha a pega central na divisória — torna o ponto de arraste visível. |
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
## Seção com navegação própria
|
|
2
|
-
|
|
3
|
-
O nível do meio do chrome. O `AppShell` é o quadro do app e o `Page` é o esqueleto de **uma** tela; entre os dois falta a **seção**: um grupo de telas irmãs (Configurações, Relatórios, a doc) que quer a lista **dentro** do conteúdo, não pendurada na sidebar do app.
|
|
4
|
-
|
|
5
|
-
A nav é `w-56` — mais estreita que o `w-64` do `AppShell` de propósito, pra hierarquia ficar legível quando as duas aparecem lado a lado.
|
|
6
|
-
|
|
7
|
-
```tsx preview col
|
|
8
|
-
render(
|
|
9
|
-
<div className="h-96 w-full overflow-hidden rounded-lg border border-border">
|
|
10
|
-
<SectionShell
|
|
11
|
-
groups={[
|
|
12
|
-
{ label: 'Acesso', items: [{ id: 'users', label: 'Usuários' }, { id: 'roles', label: 'Perfis' }] },
|
|
13
|
-
{ label: 'Cadastros', items: [{ id: 'units', label: 'Unidades' }, { id: 'departments', label: 'Departamentos' }] },
|
|
14
|
-
{ items: [{ id: 'audit', label: 'Auditoria' }, { id: 'integrations', label: 'Integrações', disabled: true }] },
|
|
15
|
-
]}
|
|
16
|
-
activeId="users"
|
|
17
|
-
onSelect={() => {}}
|
|
18
|
-
>
|
|
19
|
-
<Page title="Usuários" description="As contas do IdP.">
|
|
20
|
-
<div className="rounded-lg border border-dashed border-border p-10 text-center text-sm text-muted-foreground">
|
|
21
|
-
A tela do item ativo (em geral um Page).
|
|
22
|
-
</div>
|
|
23
|
-
</Page>
|
|
24
|
-
</SectionShell>
|
|
25
|
-
</div>,
|
|
26
|
-
)
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Quando usar — sidebar, tabs ou seção
|
|
30
|
-
|
|
31
|
-
A regra da casa, pra não virar gosto pessoal:
|
|
32
|
-
|
|
33
|
-
- **Sidebar do app** (`AppShell`) — os **contextos de negócio**, o mapa que a pessoa carrega na cabeça o dia todo. Custa caro: cada item aqui compete com todos os outros.
|
|
34
|
-
- **`SectionShell`** — as telas de uma seção que a pessoa **visita raro e navega por dentro** quando visita. Configurações é o caso canônico: nenhum admin quer 6 itens de config disputando espaço com Vendas e Estoque. Também é a escolha quando a lista é **dinâmica** (relatórios, documentos) e não cabe num nav estático.
|
|
35
|
-
- **Tabs** — quando são **facetas do mesmo objeto** (a mesma entidade vista de ângulos diferentes), não telas distintas. Se cada aba tem URL própria e faz sentido dar deep-link, provavelmente é seção, não aba.
|
|
36
|
-
|
|
37
|
-
Sintoma de que uma seção devia sair da sidebar: o item de topo **não tem tela própria** e só existe pra abrir um accordion. Isso é um nó de menu, não um destino — e cobra o preço de um destino.
|
|
38
|
-
|
|
39
|
-
## Roteamento fica de fora
|
|
40
|
-
|
|
41
|
-
O componente é **controlado**: `activeId` + `onSelect`. Ele não lê nem escreve URL — quem roteia é o app, com o mecanismo que já usa (path, querystring, estado). Mesma decisão do `AppShell`: o shell é o quadro, não o conteúdo.
|
|
42
|
-
|
|
43
|
-
O padrão comum é casar `activeId` com um segmento do path e navegar no `onSelect`:
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
<SectionShell
|
|
47
|
-
groups={groups}
|
|
48
|
-
activeId={segments[1]} // /settings/users → 'users'
|
|
49
|
-
onSelect={(id) => navigate(`/settings/${id}`)}
|
|
50
|
-
>
|
|
51
|
-
{screen}
|
|
52
|
-
</SectionShell>
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## O painel remonta na troca
|
|
56
|
-
|
|
57
|
-
Trocar de item **remonta** o painel — é isso que zera o scroll (sem o remount, a tela nova abre na altura em que a anterior estava). É o detalhe que toda cópia à mão esquecia.
|
|
58
|
-
|
|
59
|
-
A chave do remount é o `activeId`. `scrollResetKey` sobrescreve, e há dois usos legítimos — em direções opostas:
|
|
60
|
-
|
|
61
|
-
```tsx
|
|
62
|
-
// NUNCA remontar: a seção preserva o scroll ao trocar de item (chave constante).
|
|
63
|
-
<SectionShell activeId={id} scrollResetKey="fixa" …>
|
|
64
|
-
|
|
65
|
-
// Remontar MAIS: também dentro da mesma tela, quando a sub-rota troca de registro.
|
|
66
|
-
<SectionShell activeId={id} scrollResetKey={`${id}/${recordId}`} …>
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Passar `scrollResetKey={id}` com o mesmo valor do `activeId` é o default escrito à mão — não faz nada.
|
|
70
|
-
|
|
71
|
-
## Nav redimensionável
|
|
72
|
-
|
|
73
|
-
`resizable` põe uma divisa arrastável entre a nav e o painel — o mesmo mecanismo do `rail` do AppShell, agora no terceiro nível do chrome. Útil quando os rótulos da nav variam de tamanho (nome de sessão, de arquivo) e o `w-56` fixo ora aperta, ora sobra. Ligado, a largura passa a ser % (`navDefaultSize`/`navMinSize`); desligado, segue a coluna fixa de sempre.
|
|
74
|
-
|
|
75
|
-
```tsx preview
|
|
76
|
-
<div className="h-64 overflow-hidden rounded-md border">
|
|
77
|
-
<SectionShell
|
|
78
|
-
resizable
|
|
79
|
-
navDefaultSize={30}
|
|
80
|
-
groups={[{ items: [{ id: 'acoes', label: 'Ações' }, { id: 'entidades', label: 'Entidades' }] }]}
|
|
81
|
-
activeId="acoes"
|
|
82
|
-
onSelect={() => {}}
|
|
83
|
-
>
|
|
84
|
-
<div className="p-4 text-sm text-muted-foreground">Arraste a divisa à esquerda.</div>
|
|
85
|
-
</SectionShell>
|
|
86
|
-
</div>
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Três níveis, quando precisar
|
|
90
|
-
|
|
91
|
-
A maioria das seções é lista simples (`items` no grupo). Quando um grupo tem famílias internas — sub-pasta na doc, por exemplo — use `subgroups`: eles saem **depois** dos `items` soltos, com rótulo mais fraco e mais indentado.
|
|
92
|
-
|
|
93
|
-
```tsx
|
|
94
|
-
groups={[
|
|
95
|
-
{
|
|
96
|
-
label: 'Guias',
|
|
97
|
-
items: [{ id: 'deploy', label: 'Deploy' }], // solto na seção
|
|
98
|
-
subgroups: [{ label: 'CI', items: [{ id: 'build', label: 'Build' }] }],
|
|
99
|
-
},
|
|
100
|
-
]}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Rótulo vazio (`''`) conta como ausente — quem monta a lista a partir de dados não precisa normalizar antes.
|
|
104
|
-
|
|
105
|
-
## Props
|
|
106
|
-
|
|
107
|
-
| Prop | Tipo | O que faz |
|
|
108
|
-
|---|---|---|
|
|
109
|
-
| `groups` | `SectionNavGroup[]` | A nav: grupos (`label` opcional) → `items` e/ou `subgroups`. |
|
|
110
|
-
| `activeId` | `string` | O item destacado (ganha `aria-current="page"`). Ausente = nenhum. |
|
|
111
|
-
| `onSelect` | `(id: string) => void` | Clique no item. |
|
|
112
|
-
| `navHeader` / `navFooter` | `ReactNode` | Topo e rodapé da nav (título da seção, busca, botão de criar). |
|
|
113
|
-
| `navLabel` | `string` | Rótulo da landmark `<nav>`. Default: `Navegação da seção`. |
|
|
114
|
-
| `scrollResetKey` | `string` | O que remonta o painel. Default: `activeId`. |
|
|
115
|
-
| `resizable` | `boolean` | Nav arrastável pelo usuário (mesma divisa do `rail` do AppShell). Default: `false` — a nav é a coluna fixa `w-56`. |
|
|
116
|
-
| `navDefaultSize` / `navMinSize` | `number` | Largura inicial e mínima da nav em %, quando `resizable`. Default: `18` e `12`. |
|
|
117
|
-
| `className` / `navClassName` | `string` | Classes da raiz e da COLUNA da nav (o wrapper, não a `<nav>` de dentro). Com `resizable`, largura em `navClassName` não vale — quem manda é `navDefaultSize`. |
|
|
118
|
-
|
|
119
|
-
Cada `SectionNavItem` tem `id`, `label` e, opcionais, `icon`, `badge` e `disabled` (visível mas inerte — a tela existe no mapa e ainda não abre).
|
|
120
|
-
|
|
121
|
-
O `id` precisa ser único em **toda** a nav, não só dentro do grupo: o `activeId` casa por id, então id repetido marca dois itens como atuais e o painel não remonta ao alternar entre eles.
|