@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +52 -3
  2. package/bin/lib/check.mjs +37 -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/docs/data-layer.md +2 -2
  10. package/docs/elevation-scale.md +2 -2
  11. package/docs/protocol.md +2 -2
  12. package/package.json +1 -1
  13. package/registry/skills/build-opus-ui/SKILL.md +8 -3
  14. package/registry/skills/build-opus-ui/references/evaluations.md +2 -0
  15. package/registry/skills/build-opus-ui/references/ui-patterns.md +3 -0
  16. package/src/core/domain.ts +3 -6
  17. package/src/schema/entity.ts +1 -1
  18. package/src/ui/components/patterns/shell-nav.tsx +5 -9
  19. package/src/ui/components/patterns/sidebar.tsx +40 -15
  20. package/src/ui/components/primitives/alert-dialog.tsx +1 -1
  21. package/src/ui/components/primitives/calendar.tsx +1 -1
  22. package/src/ui/components/primitives/card.tsx +3 -3
  23. package/src/ui/components/primitives/chat.tsx +1 -1
  24. package/src/ui/components/primitives/composer.tsx +2 -2
  25. package/src/ui/components/primitives/dialog.tsx +1 -1
  26. package/src/ui/components/primitives/menu.tsx +2 -2
  27. package/src/ui/components/primitives/popover.tsx +1 -1
  28. package/src/ui/docs/DocBrowser.tsx +25 -10
  29. package/src/ui/docs/content/card.md +1 -1
  30. package/src/ui/docs/content/composer.md +1 -1
  31. package/src/ui/docs/content/confirm.md +2 -2
  32. package/src/ui/docs/content/sidebar.md +21 -1
  33. package/src/ui/docs/content/tokens.md +4 -7
  34. package/src/ui/docs/registry.tsx +0 -6
  35. package/src/ui/meta.ts +3 -24
  36. package/src/ui/react.tsx +4 -19
  37. package/src/ui/theme.css +0 -7
  38. package/docs/shellnav.md +0 -131
  39. package/src/ui/components/patterns/app-shell.tsx +0 -227
  40. package/src/ui/components/patterns/section-shell.tsx +0 -246
  41. package/src/ui/docs/content/app-shell.md +0 -155
  42. package/src/ui/docs/content/resizable.md +0 -86
  43. 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.