@softize/opus 12.10.0 → 13.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 (154) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/bin/lib/check.mjs +1098 -310
  3. package/bin/lib/copy.mjs +12 -5
  4. package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
  5. package/docs/adr/0004-page-content-state-is-composed.md +65 -0
  6. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +180 -0
  7. package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
  8. package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
  9. package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
  10. package/docs/radius-scale.md +1 -1
  11. package/package.json +1 -1
  12. package/registry/instructions/opus.md +5 -0
  13. package/registry/skills/build-opus-ui/SKILL.md +27 -16
  14. package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
  15. package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
  16. package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
  17. package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
  18. package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
  19. package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
  20. package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
  21. package/registry/skills/model-opus-dictionary/SKILL.md +4 -2
  22. package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
  23. package/registry/templates/app/src/App.tsx +1 -1
  24. package/src/core/dictionary.ts +52 -14
  25. package/src/core/index.ts +10 -0
  26. package/src/core/ui-context.ts +29 -0
  27. package/src/schema/drivers/zod.ts +17 -8
  28. package/src/ui/components/patterns/action-form-card.tsx +18 -12
  29. package/src/ui/components/patterns/confirm.tsx +163 -40
  30. package/src/ui/components/patterns/content-header.tsx +335 -61
  31. package/src/ui/components/patterns/data-state.tsx +23 -10
  32. package/src/ui/components/patterns/list.tsx +1097 -783
  33. package/src/ui/components/patterns/page-state.tsx +115 -0
  34. package/src/ui/components/patterns/page.tsx +231 -41
  35. package/src/ui/components/patterns/sidebar.tsx +357 -83
  36. package/src/ui/components/patterns/trigger.tsx +37 -30
  37. package/src/ui/components/patterns/view.tsx +7 -11
  38. package/src/ui/components/primitives/alert.tsx +298 -110
  39. package/src/ui/components/primitives/ask.tsx +2 -1
  40. package/src/ui/components/primitives/badge.tsx +91 -30
  41. package/src/ui/components/primitives/button.tsx +99 -60
  42. package/src/ui/components/primitives/calendar.tsx +39 -39
  43. package/src/ui/components/primitives/card.tsx +96 -23
  44. package/src/ui/components/primitives/detail.tsx +2 -2
  45. package/src/ui/components/primitives/dialog.tsx +196 -39
  46. package/src/ui/components/primitives/dictionary-value.tsx +9 -14
  47. package/src/ui/components/primitives/dot.tsx +74 -21
  48. package/src/ui/components/primitives/drawer.tsx +40 -24
  49. package/src/ui/components/primitives/empty.tsx +3 -3
  50. package/src/ui/components/primitives/item.tsx +135 -79
  51. package/src/ui/components/primitives/menu.tsx +11 -3
  52. package/src/ui/components/primitives/metric-card.tsx +133 -0
  53. package/src/ui/components/primitives/sonner.tsx +187 -8
  54. package/src/ui/components/primitives/table.tsx +2 -2
  55. package/src/ui/docs/DocBrowser.tsx +104 -25
  56. package/src/ui/docs/changelog.tsx +1 -1
  57. package/src/ui/docs/content/accordion.md +22 -16
  58. package/src/ui/docs/content/action-form-card.md +8 -8
  59. package/src/ui/docs/content/action-form-dialog.md +9 -9
  60. package/src/ui/docs/content/action-form.md +28 -34
  61. package/src/ui/docs/content/action-list-dialog.md +11 -6
  62. package/src/ui/docs/content/action-list.md +64 -39
  63. package/src/ui/docs/content/action-trigger.md +21 -14
  64. package/src/ui/docs/content/action-view.md +8 -8
  65. package/src/ui/docs/content/actions.md +9 -9
  66. package/src/ui/docs/content/ai.md +3 -3
  67. package/src/ui/docs/content/alert.md +54 -28
  68. package/src/ui/docs/content/aspect-ratio.md +4 -4
  69. package/src/ui/docs/content/audit.md +2 -2
  70. package/src/ui/docs/content/auth.md +3 -3
  71. package/src/ui/docs/content/avatar.md +34 -14
  72. package/src/ui/docs/content/badge.md +21 -22
  73. package/src/ui/docs/content/breadcrumb.md +13 -8
  74. package/src/ui/docs/content/button.md +93 -15
  75. package/src/ui/docs/content/calendar.md +5 -5
  76. package/src/ui/docs/content/card.md +6 -6
  77. package/src/ui/docs/content/carousel.md +16 -11
  78. package/src/ui/docs/content/chat.md +3 -3
  79. package/src/ui/docs/content/checkbox.md +7 -7
  80. package/src/ui/docs/content/cli.md +5 -5
  81. package/src/ui/docs/content/collapsible.md +8 -8
  82. package/src/ui/docs/content/command.md +16 -8
  83. package/src/ui/docs/content/composer.md +2 -2
  84. package/src/ui/docs/content/content.md +44 -0
  85. package/src/ui/docs/content/copyable.md +4 -3
  86. package/src/ui/docs/content/customization.md +7 -7
  87. package/src/ui/docs/content/cycle.md +3 -3
  88. package/src/ui/docs/content/data-state.md +11 -12
  89. package/src/ui/docs/content/data.md +26 -33
  90. package/src/ui/docs/content/detail.md +8 -5
  91. package/src/ui/docs/content/dialog.md +339 -31
  92. package/src/ui/docs/content/dictionary-value.md +19 -18
  93. package/src/ui/docs/content/dock.md +3 -3
  94. package/src/ui/docs/content/dot.md +7 -7
  95. package/src/ui/docs/content/drawer.md +32 -16
  96. package/src/ui/docs/content/empty-value.md +2 -2
  97. package/src/ui/docs/content/empty.md +19 -12
  98. package/src/ui/docs/content/events.md +4 -4
  99. package/src/ui/docs/content/field.md +34 -12
  100. package/src/ui/docs/content/getting-started.md +1 -1
  101. package/src/ui/docs/content/icon-picker.md +8 -4
  102. package/src/ui/docs/content/input-otp.md +20 -12
  103. package/src/ui/docs/content/input.md +121 -9
  104. package/src/ui/docs/content/item.md +64 -24
  105. package/src/ui/docs/content/kbd.md +19 -11
  106. package/src/ui/docs/content/label.md +5 -3
  107. package/src/ui/docs/content/log.md +4 -4
  108. package/src/ui/docs/content/markdown.md +7 -6
  109. package/src/ui/docs/content/mcp.md +13 -15
  110. package/src/ui/docs/content/menu.md +36 -17
  111. package/src/ui/docs/content/metric-card.md +41 -0
  112. package/src/ui/docs/content/observability.md +2 -2
  113. package/src/ui/docs/content/page.md +93 -10
  114. package/src/ui/docs/content/pagination.md +22 -17
  115. package/src/ui/docs/content/popover.md +16 -8
  116. package/src/ui/docs/content/progress.md +7 -5
  117. package/src/ui/docs/content/queue.md +5 -5
  118. package/src/ui/docs/content/radio-group.md +20 -12
  119. package/src/ui/docs/content/router.md +11 -6
  120. package/src/ui/docs/content/scheduler.md +4 -5
  121. package/src/ui/docs/content/scroll-area.md +12 -7
  122. package/src/ui/docs/content/select.md +42 -29
  123. package/src/ui/docs/content/semantic-context.md +63 -0
  124. package/src/ui/docs/content/separator.md +5 -5
  125. package/src/ui/docs/content/sidebar.md +325 -56
  126. package/src/ui/docs/content/skeleton.md +5 -4
  127. package/src/ui/docs/content/slider.md +8 -7
  128. package/src/ui/docs/content/spinner.md +8 -8
  129. package/src/ui/docs/content/split.md +8 -5
  130. package/src/ui/docs/content/storage.md +6 -8
  131. package/src/ui/docs/content/switch.md +8 -7
  132. package/src/ui/docs/content/table.md +16 -6
  133. package/src/ui/docs/content/tabs.md +28 -14
  134. package/src/ui/docs/content/testing.md +9 -11
  135. package/src/ui/docs/content/textarea.md +5 -4
  136. package/src/ui/docs/content/toast.md +47 -13
  137. package/src/ui/docs/content/toggle.md +75 -7
  138. package/src/ui/docs/content/tokens.md +31 -3
  139. package/src/ui/docs/content/tooltip.md +19 -11
  140. package/src/ui/docs/content/truncate.md +7 -8
  141. package/src/ui/docs/content/ui.md +10 -9
  142. package/src/ui/docs/content/upgrading.md +7 -8
  143. package/src/ui/docs/doc-client.tsx +2 -2
  144. package/src/ui/docs/registry.tsx +580 -229
  145. package/src/ui/lib/semantic-context.ts +30 -0
  146. package/src/ui/meta.ts +278 -286
  147. package/src/ui/react.tsx +377 -111
  148. package/src/ui/theme.css +116 -0
  149. package/src/ui/components/primitives/alert-dialog.tsx +0 -190
  150. package/src/ui/docs/content/alert-dialog.md +0 -73
  151. package/src/ui/docs/content/button-group.md +0 -71
  152. package/src/ui/docs/content/confirm.md +0 -120
  153. package/src/ui/docs/content/input-group.md +0 -78
  154. package/src/ui/docs/content/toggle-group.md +0 -81
@@ -1,79 +1,348 @@
1
- ## Sidebar
1
+ ## Navegação dentro de um layout
2
2
 
3
- `Sidebar` é uma coluna de chrome e navegação. Ela não escolhe a posição: entra em qualquer `Pane` de um `Split`. A mesma forma serve para a sidebar global do app e a navegação interna de Configurações.
3
+ Use `Sidebar` para apresentar navegação global ou contextual em uma coluna lateral. O componente
4
+ organiza a superfície e compartilha o estado de colapso; posição e largura pertencem a um `Pane`
5
+ dentro de `Split`.
6
+
7
+ O exemplo canônico mantém a largura inicial em `rem`, deixa a navegação rolar e preserva o
8
+ cabeçalho e o rodapé nas extremidades.
4
9
 
5
10
  ```tsx preview
6
11
  render(
7
- <div className="h-72 overflow-hidden rounded-lg border border-border">
8
- <Split resizable>
9
- <Pane initialSize={32} minSize={20} inset="none">
10
- <Sidebar divider={false} className="w-full">
11
- <PaneHeader className="px-4 py-3 text-sm font-semibold">Projeto</PaneHeader>
12
- <PaneContent>
12
+ <div className="h-80 overflow-hidden rounded-lg border border-border">
13
+ <Split>
14
+ <Pane initialSize="16rem" inset="none">
15
+ <Sidebar className="w-full">
16
+ <PaneHeader className="px-4 py-3 text-sm font-semibold">
17
+ Projeto
18
+ </PaneHeader>
19
+ <PaneBody>
13
20
  <SidebarNav
14
- groups={[{ label: 'Design', items: [{ id: 'preview', label: 'Preview' }, { id: 'code', label: 'Código' }] }]}
15
- activeId="preview"
21
+ groups={[
22
+ {
23
+ label: 'Workspace',
24
+ items: [
25
+ { id: 'overview', label: 'Visão geral', icon: <LayoutDashboard /> },
26
+ { id: 'sessions', label: 'Sessões', icon: <MessagesSquare /> },
27
+ ],
28
+ },
29
+ ]}
30
+ activeId="overview"
16
31
  onSelect={() => {}}
17
32
  />
18
- </PaneContent>
19
- <PaneFooter><span className="text-xs text-muted-foreground">Configurações</span></PaneFooter>
33
+ </PaneBody>
34
+ <PaneFooter>
35
+ <SidebarItem label="Configurações" icon={<Settings />} onClick={() => {}} />
36
+ </PaneFooter>
20
37
  </Sidebar>
21
38
  </Pane>
22
- <Pane grow inset="lg"><p className="text-sm text-muted-foreground">Conteúdo da tela.</p></Pane>
39
+ <Pane grow inset="lg">
40
+ <p className="text-sm text-muted-foreground">Conteúdo da página.</p>
41
+ </Pane>
23
42
  </Split>
24
43
  </div>,
25
44
  )
26
45
  ```
27
46
 
28
- ## Colapso
29
-
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
-
32
- `SidebarItem` também é a linha de árvores de navegação. `actions` e o controle formado por
33
- `onToggle`/`expanded` são irmãos do botão principal, portanto menus e chevrons não criam
34
- controles interativos aninhados. No modo recolhido, a linha conserva somente o destino com
35
- ícone e tooltip. No modo aberto, o chevron ocupa o lugar do ícone da pasta durante o hover da própria linha ou
36
- foco, e as ações ficam sobrepostas à extremidade direita: controles invisíveis não reduzem o
37
- espaço disponível para o rótulo. O foco comum no destino não mantém o chevron aberto;
38
- somente o foco visível no próprio controle o revela para navegação por teclado.
39
- As ações aparecem no hover da própria linha, no foco visível do próprio controle ou enquanto o menu está
40
- aberto; focar o destino da linha não revela as reticências.
41
- O rótulo que não cabe na linha desaparece num gradiente até a borda, em vez de terminar em
42
- reticências; a linha mede o próprio transbordo, então o rótulo que cabe inteiro fica intacto.
43
- O foco visível usa o mesmo anel do `Button` (`ring-2 ring-ring/50`), na linha aberta, no botão do
44
- rail recolhido e no chevron sem ele a navegação por teclado cairia no anel padrão do navegador,
45
- que destoa do tema.
46
- Durante drag-and-drop, `dropPosition="before"` e `"after"` desenham uma linha sobreposta ao
47
- limite do item; `"inside"` realça a superfície da pasta. O indicador nunca reserva espaço.
48
-
49
- Árvores montadas por composição usam `SidebarGroupLabel` para os mesmos rótulos discretos
50
- que o `SidebarNav` desenha automaticamente. O heading permanece `text-sm`; hierarquia vem
51
- do peso médio e da cor atenuada, não de reduzir legibilidade.
47
+ Use `resizable` no `Split` somente quando ajustar a largura fizer parte da tarefa. Nesse caso, o
48
+ `Pane` também declara `minSize`, e `divider={false}` evita que `Sidebar` desenhe uma segunda
49
+ divisória ao lado do handle.
50
+
51
+ ## Anatomia e responsabilidades
52
+
53
+ Cada parte possui uma responsabilidade única. Essa separação permite reutilizar os mesmos slots em
54
+ uma sidebar fixa, redimensionável ou recolhida.
55
+
56
+ | Parte | Responsabilidade |
57
+ |---|---|
58
+ | `Split` | Define a relação espacial e, opcionalmente, o redimensionamento. |
59
+ | `Pane` | Define largura, limites e espaçamento interno da coluna. |
60
+ | `Sidebar` | Desenha a superfície lateral e fornece o estado de colapso. |
61
+ | `PaneHeader` | Mantém identidade, contexto ou ações no topo. |
62
+ | `PaneBody` | Ocupa o espaço restante e concentra a rolagem vertical. |
63
+ | `PaneFooter` | Mantém ações persistentes no rodapé. |
64
+ | `SidebarNav` ou `ShellNav` | Apresenta e controla os destinos de navegação. |
65
+
66
+ `PaneContent` permanece como alias temporário de `PaneBody` durante a versão 12. Código novo usa
67
+ `PaneBody`.
68
+
69
+ ## Escolher a navegação
70
+
71
+ Use `SidebarNav` para grupos planos de destinos. Ele recebe dados e produz `SidebarItem` com a mesma
72
+ semântica usada na composição manual.
73
+
74
+ ```tsx preview
75
+ <div className="h-72 w-64 overflow-hidden rounded-lg border border-border">
76
+ <Sidebar className="w-full">
77
+ <PaneBody>
78
+ <SidebarNav
79
+ groups={[
80
+ {
81
+ label: 'Configurações',
82
+ items: [
83
+ { id: 'users', label: 'Usuários', icon: <Users /> },
84
+ { id: 'roles', label: 'Papéis', icon: <ShieldCheck /> },
85
+ { id: 'imports', label: 'Importações', icon: <Database /> },
86
+ ],
87
+ },
88
+ ]}
89
+ activeId="users"
90
+ onSelect={() => {}}
91
+ />
92
+ </PaneBody>
93
+ </Sidebar>
94
+ </div>
95
+ ```
96
+
97
+ ## Compor uma árvore
98
+
99
+ Uma árvore não pertence ao contrato de `SidebarNav`: profundidade, carregamento e expansão variam
100
+ por produto. Componha cada nó com `SidebarItem`, envolva seus descendentes em `SidebarTreeGroup` e
101
+ mantenha o estado no consumidor. O grupo aplica o recuo e desenha a guia vertical; o destino
102
+ principal e o controle de expansão permanecem irmãos acessíveis.
103
+
104
+ ```tsx preview
105
+ const [open, setOpen] = useState(true)
106
+
107
+ render(
108
+ <div className="h-72 w-64 overflow-hidden rounded-lg border border-border">
109
+ <Sidebar className="w-full">
110
+ <PaneBody className="p-2">
111
+ <SidebarGroupLabel>Documentação</SidebarGroupLabel>
112
+ <SidebarItem
113
+ label="Componentes"
114
+ icon={<Folder />}
115
+ expanded={open}
116
+ onToggle={() => setOpen((current) => !current)}
117
+ onClick={() => setOpen((current) => !current)}
118
+ />
119
+ {open && (
120
+ <SidebarTreeGroup>
121
+ <SidebarItem label="Layout" icon={<Folder />} expanded onToggle={() => {}} onClick={() => {}} />
122
+ <SidebarTreeGroup>
123
+ <SidebarItem label="Sidebar" icon={<PanelLeft />} active onClick={() => {}} />
124
+ <SidebarItem label="Page" icon={<PanelsTopLeft />} onClick={() => {}} />
125
+ </SidebarTreeGroup>
126
+ </SidebarTreeGroup>
127
+ )}
128
+ </PaneBody>
129
+ </Sidebar>
130
+ </div>,
131
+ )
132
+ ```
133
+
134
+ Esse é o padrão usado pelo `DocBrowser`: seções são rótulos, grupos nomeados são nós expansíveis e
135
+ páginas são folhas. Um grupo começa aberto e volta a abrir quando contém a página ativa.
136
+
137
+ Use `ShellNav` para uma navegação plana que precisa de cabeçalho próprio e grupos ancorados no
138
+ rodapé. Ele gerencia a rolagem internamente e se adapta quando estiver dentro de uma `Sidebar`
139
+ recolhida.
52
140
 
53
141
  ```tsx
54
142
  <Sidebar collapsed={collapsed}>
55
- <PaneHeader><MySidebarHeader onToggle={() => setCollapsed(!collapsed)} /></PaneHeader>
56
- <PaneContent><SidebarNav groups={groups} activeId={active} onSelect={go} /></PaneContent>
57
- <PaneFooter><UserMenu /></PaneFooter>
143
+ <ShellNav
144
+ heading={<ShellNavHeading title="Relatórios" action={<CreateReportButton />} />}
145
+ groups={reportGroups}
146
+ footer={settingsGroups}
147
+ activeId={activeId}
148
+ onSelect={navigateToReport}
149
+ />
58
150
  </Sidebar>
59
151
  ```
60
152
 
61
- ## Navegação contextual
153
+ ## Recolher a coluna
62
154
 
63
- `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.
155
+ `collapsed` pertence à `Sidebar`. Nesse estado, `SidebarItem`, `SidebarNav` e `ShellNav` mantêm
156
+ somente os ícones e expõem os rótulos em tooltips. Por isso, todo destino que aparece no modo
157
+ recolhido precisa de um ícone reconhecível e de um `label` completo.
64
158
 
65
- ```tsx
66
- <SidebarNav
67
- groups={[
68
- {
69
- label: 'Configurações',
70
- subgroups: [
71
- { label: 'Acesso', items: [{ id: 'users', label: 'Usuários' }] },
72
- { label: 'Dados', items: [{ id: 'imports', label: 'Importações' }] },
73
- ],
74
- },
75
- ]}
76
- activeId={active}
77
- onSelect={go}
78
- />
159
+ ```tsx preview
160
+ const [collapsed, setCollapsed] = useState(false)
161
+
162
+ render(
163
+ <div className="flex h-64 overflow-hidden rounded-lg border border-border">
164
+ <Sidebar collapsed={collapsed}>
165
+ <PaneHeader className="flex h-12 items-center justify-center">
166
+ <Button
167
+ variant="ghost"
168
+ size="icon-sm"
169
+ aria-label={collapsed ? 'Expandir navegação' : 'Recolher navegação'}
170
+ onClick={() => setCollapsed((current) => !current)}
171
+ >
172
+ <PanelLeftClose className={collapsed ? 'rotate-180' : ''} />
173
+ </Button>
174
+ </PaneHeader>
175
+ <PaneBody>
176
+ <SidebarNav
177
+ groups={[
178
+ {
179
+ items: [
180
+ { id: 'home', label: 'Início', icon: <House /> },
181
+ { id: 'reports', label: 'Relatórios', icon: <ChartNoAxesColumn /> },
182
+ ],
183
+ },
184
+ ]}
185
+ activeId="home"
186
+ onSelect={() => {}}
187
+ />
188
+ </PaneBody>
189
+ </Sidebar>
190
+ <div className="flex-1 p-4 text-sm text-muted-foreground">
191
+ O conteúdo ocupa o espaço liberado pela sidebar.
192
+ </div>
193
+ </div>,
194
+ )
195
+ ```
196
+
197
+ O consumidor controla o estado e decide onde colocar o gatilho. A transição de largura pertence à
198
+ `Sidebar`; a aplicação não precisa trocar a árvore de componentes.
199
+
200
+ ## Itens com expansão e ações
201
+
202
+ `SidebarItem` mantém o destino principal, a expansão e as ações como controles irmãos. Essa
203
+ estrutura evita botões aninhados e permite que cada controle receba foco de forma independente.
204
+
205
+ ```tsx preview
206
+ <div className="w-72 rounded-lg border border-border p-2">
207
+ <SidebarItem
208
+ label="Contratos"
209
+ icon={<Folder />}
210
+ expanded
211
+ onToggle={() => {}}
212
+ actions={
213
+ <Button variant="ghost" size="icon-xs" aria-label="Mais ações">
214
+ <Ellipsis />
215
+ </Button>
216
+ }
217
+ onClick={() => {}}
218
+ />
219
+ <SidebarItem
220
+ label="Criar workspace"
221
+ icon={<FilePlus2 />}
222
+ className="pl-6"
223
+ onClick={() => {}}
224
+ />
225
+ </div>
79
226
  ```
227
+
228
+ Quando o rótulo não cabe, ele termina em um gradiente. As ações aparecem ao passar o ponteiro, ao
229
+ receber foco visível ou enquanto um menu permanece aberto; como são sobrepostas, não reduzem o
230
+ espaço disponível para o texto.
231
+
232
+ Para árvores arrastáveis, `dropPosition` comunica o destino visual sem reservar espaço:
233
+ `before` e `after` desenham uma linha na fronteira, enquanto `inside` realça a superfície do item.
234
+ O driver de drag-and-drop continua externo e fornece seus atributos por `dragProps`.
235
+
236
+ ## Acessibilidade
237
+
238
+ - `activeId` resulta em `aria-current="page"` no destino atual.
239
+ - `navLabel` nomeia a landmark de navegação.
240
+ - O controle de expansão informa `aria-expanded` e recebe um rótulo baseado no item.
241
+ - Itens desabilitados continuam visíveis, mas não respondem à interação.
242
+ - O foco visível usa o anel semântico do tema no destino, nas ações e no controle de expansão.
243
+ - No modo recolhido, o tooltip preserva o nome que deixou de aparecer visualmente.
244
+
245
+ ## Propriedades de Sidebar
246
+
247
+ | Propriedade | Tipo | Padrão | Descrição |
248
+ |---|---|---|---|
249
+ | `collapsed` | `boolean` | `false` | Recolhe a coluna e publica esse estado para as navegações descendentes. |
250
+ | `divider` | `boolean` | `true` | Desenha a borda na lateral. Desative quando o `Split` já fornecer o handle. |
251
+ | `className` | `string` | | Ajusta a raiz. A largura externa deve continuar pertencendo ao `Pane`. |
252
+ | `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou uma navegação que gerencie a própria estrutura. |
253
+
254
+ ## Propriedades de PaneHeader
255
+
256
+ | Propriedade | Tipo | Padrão | Descrição |
257
+ |---|---|---|---|
258
+ | `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
259
+ | `children` | `ReactNode` | | Identidade, contexto ou ações apresentadas no topo. |
260
+
261
+ ## Propriedades de PaneBody
262
+
263
+ | Propriedade | Tipo | Padrão | Descrição |
264
+ |---|---|---|---|
265
+ | `className` | `string` | | Ajusta a região flexível e rolável. |
266
+ | `children` | `ReactNode` | | Conteúdo que ocupa o espaço entre cabeçalho e rodapé. |
267
+
268
+ ## Propriedades de PaneFooter
269
+
270
+ | Propriedade | Tipo | Padrão | Descrição |
271
+ |---|---|---|---|
272
+ | `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
273
+ | `children` | `ReactNode` | | Navegação ou ações que precisam permanecer acessíveis no rodapé. |
274
+
275
+ ## Propriedades de SidebarNav
276
+
277
+ | Propriedade | Tipo | Padrão | Descrição |
278
+ |---|---|---|---|
279
+ | `groups` | `SidebarNavGroup[]` | | Grupos e itens apresentados na ordem recebida. Grupos vazios são omitidos. |
280
+ | `activeId` | `string` | | Identificador do destino atual. |
281
+ | `onSelect` | `(id: string) => void` | | Recebe o identificador selecionado; roteamento permanece com o consumidor. |
282
+ | `navLabel` | `string` | `'Navegação'` | Nome acessível da landmark `nav`. |
283
+ | `className` | `string` | | Ajusta a raiz da navegação. |
284
+
285
+ ### Estrutura de SidebarNavGroup
286
+
287
+ | Campo | Tipo | Descrição |
288
+ |---|---|---|
289
+ | `label` | `string` | Rótulo opcional do grupo. Rótulos vazios não reservam espaço. |
290
+ | `items` | `SidebarNavItem[]` | Itens diretos do grupo. |
291
+
292
+ `SidebarNavItem` acrescenta `id` às propriedades de `SidebarItem`. Para mais níveis, componha
293
+ `SidebarItem` e `SidebarTreeGroup` diretamente.
294
+
295
+ ## Propriedades de SidebarItem
296
+
297
+ | Propriedade | Tipo | Padrão | Descrição |
298
+ |---|---|---|---|
299
+ | `label` | `string` | | Nome visível do destino e conteúdo do tooltip no modo recolhido. |
300
+ | `icon` | `ReactNode` | | Ícone apresentado antes do rótulo e preservado no modo recolhido. |
301
+ | `badge` | `ReactNode` | | Informação curta no fim da linha, como contagem ou estado. |
302
+ | `active` | `boolean` | `false` | Destaca o destino e aplica `aria-current="page"`. |
303
+ | `disabled` | `boolean` | `false` | Mantém o item visível sem permitir interação. |
304
+ | `actions` | `ReactNode` | | Controles relacionados exibidos no fim da linha. |
305
+ | `onToggle` | `() => void` | | Adiciona o controle independente de expansão. |
306
+ | `expanded` | `boolean` | `false` | Define o estado acessível e a rotação do controle de expansão. |
307
+ | `dropPosition` | `'before' \| 'inside' \| 'after'` | | Indica visualmente onde um item arrastado será solto. |
308
+ | `dragProps` | `HTMLAttributes<HTMLDivElement>` | | Encaminha atributos fornecidos pelo driver de drag-and-drop para a linha. |
309
+ | `tooltipHint` | `ReactNode` | | Acrescenta contexto ao tooltip do modo recolhido. |
310
+ | `className` | `string` | | Ajusta a linha nos estados aberto e recolhido. |
311
+ | `onClick` | `() => void` | | Executa a navegação principal do item. |
312
+
313
+ ## Propriedades de SidebarGroupLabel
314
+
315
+ | Propriedade | Tipo | Padrão | Descrição |
316
+ |---|---|---|---|
317
+ | `className` | `string` | | Ajusta o rótulo discreto do grupo. |
318
+ | `children` | `ReactNode` | | Nome do conjunto de destinos. |
319
+
320
+ ## Propriedades de SidebarTreeGroup
321
+
322
+ | Propriedade | Tipo | Padrão | Descrição |
323
+ |---|---|---|---|
324
+ | `className` | `string` | | Ajusta o grupo sem substituir o recuo, o espaçamento e a guia vertical padrão. |
325
+ | `children` | `ReactNode` | | Itens ou grupos que descendem do nó anterior. |
326
+
327
+ ## Propriedades de ShellNav
328
+
329
+ | Propriedade | Tipo | Padrão | Descrição |
330
+ |---|---|---|---|
331
+ | `groups` | `ShellNavGroup[]` | | Grupos planos exibidos na região rolável. Grupos vazios são omitidos. |
332
+ | `activeId` | `string` | | Identificador do destino atual. |
333
+ | `onSelect` | `(id: string) => void` | | Recebe o identificador selecionado. |
334
+ | `heading` | `ReactNode` | | Conteúdo fixo acima da navegação; fica oculto no modo recolhido. |
335
+ | `footer` | `ShellNavGroup[]` | | Grupos ancorados abaixo da região rolável. |
336
+ | `navLabel` | `string` | `'Navegação'` | Nome acessível da landmark `nav`. |
337
+ | `className` | `string` | | Ajusta a coluna da navegação. A largura vem do contêiner. |
338
+
339
+ Cada `ShellNavGroup` recebe `label` opcional e `items`. Um `ShellNavItem` declara `id`, `label`,
340
+ `icon`, `badge` e `disabled`.
341
+
342
+ ## Propriedades de ShellNavHeading
343
+
344
+ | Propriedade | Tipo | Padrão | Descrição |
345
+ |---|---|---|---|
346
+ | `title` | `ReactNode` | | Título da navegação. |
347
+ | `action` | `ReactNode` | | Ação relacionada apresentada no extremo oposto. |
348
+ | `className` | `string` | | Ajusta a faixa de cabeçalho. |
@@ -1,6 +1,7 @@
1
1
  ## Linha de lista com avatar
2
2
 
3
- O skeleton imita a forma do conteúdo: círculo pro avatar (rounded-full), barras com a largura aproximada do texto.
3
+ Use `Skeleton` para preservar a forma do conteúdo durante o carregamento. Combine círculos para
4
+ avatares e barras com dimensões próximas às linhas de texto esperadas.
4
5
 
5
6
  ```tsx preview col
6
7
  <div className="flex items-center gap-3">
@@ -14,7 +15,7 @@ O skeleton imita a forma do conteúdo: círculo pro avatar (rounded-full), barra
14
15
 
15
16
  ## Card em carregamento
16
17
 
17
- O esqueleto reproduz o layout final do card — título, descrição, conteúdo e ações — pra tela não pular quando os dados chegarem.
18
+ O esqueleto reproduz o layout final do card — título, descrição, conteúdo e ações — para tela não pular quando os dados chegarem.
18
19
 
19
20
  ```tsx preview col
20
21
  <Card>
@@ -22,10 +23,10 @@ O esqueleto reproduz o layout final do card — título, descrição, conteúdo
22
23
  <Skeleton className="h-5 w-40" />
23
24
  <Skeleton className="h-4 w-56" />
24
25
  </CardHeader>
25
- <CardContent className="space-y-2">
26
+ <CardBody className="space-y-2">
26
27
  <Skeleton className="h-4 w-full" />
27
28
  <Skeleton className="h-4 w-3/4" />
28
- </CardContent>
29
+ </CardBody>
29
30
  <CardFooter className="gap-2">
30
31
  <Skeleton className="h-8 w-28" />
31
32
  <Skeleton className="h-8 w-24" />
@@ -1,6 +1,7 @@
1
- ## Básico
1
+ ## Um valor na faixa
2
2
 
3
- defaultValue é um array — [n] pra um thumb. min, max e step delimitam a faixa; aqui de 0 a 100 em passos de 5.
3
+ Use `Slider` para escolher um valor dentro de uma faixa. `defaultValue` recebe um array; `[n]`
4
+ representa um único controle. `min`, `max` e `step` delimitam os valores disponíveis.
4
5
 
5
6
  ```tsx preview col
6
7
  <Slider defaultValue={[40]} min={0} max={100} step={5} />
@@ -8,7 +9,7 @@ defaultValue é um array — [n] pra um thumb. min, max e step delimitam a faixa
8
9
 
9
10
  ## Controlado
10
11
 
11
- onValueChange recebe o array atualizado leia value[0] pra um thumb. Bom pra mostrar o valor escolhido ao lado do rótulo.
12
+ `onValueChange` recebe o array atualizado. Para um único controle, leia `value[0]`.
12
13
 
13
14
  ```tsx preview col
14
15
  const [parallelism, setParallelism] = useState([4])
@@ -26,7 +27,7 @@ render(
26
27
 
27
28
  ## Intervalo
28
29
 
29
- value com dois números rende dois thumbs e a faixa fica entre eles — o jeito de filtrar por um intervalo, como o custo estimado de uma task.
30
+ Dois números em `value` criam um intervalo selecionável entre dois controles.
30
31
 
31
32
  ```tsx preview col
32
33
  const [budget, setBudget] = useState([20, 60])
@@ -50,11 +51,11 @@ disabled esmaece o trilho e o thumb e bloqueia o arraste — o valor fica congel
50
51
  <Slider defaultValue={[70]} min={0} max={100} disabled />
51
52
  ```
52
53
 
53
- ## Props
54
+ ## Propriedades de Slider
54
55
 
55
- | Prop | Tipo | Default | Descrição |
56
+ | Propriedade | Tipo | Padrão | Descrição |
56
57
  |---|---|---|---|
57
- | `value` | `number[]` | | O valor no modo controlado — [n] pra um thumb, [a, b] pra um intervalo. Pareie com onValueChange. |
58
+ | `value` | `number[]` | | O valor no modo controlado — [n] para um thumb, [a, b] para um intervalo. Pareie com onValueChange. |
58
59
  | `onValueChange` | `(value: number[]) => void` | | Chamado a cada arraste, com o array atualizado. |
59
60
  | `defaultValue` | `number[]` | | Valor inicial no modo não controlado. |
60
61
  | `min` | `number` | `0` | Limite inferior da faixa. |
@@ -1,7 +1,7 @@
1
- ## Tamanhos
1
+ ## Carregamento sem progresso conhecido
2
2
 
3
- Divergência da casa: o shadcn atual tem size-4 fixo; mantivemos sm/default/lg pra cobrir botão
4
- (sm) e estados maiores (lg). O aria-label Carregando vem embutido.
3
+ Use `Spinner` quando a duração ou o progresso da espera não forem conhecidos. `sm` atende ações
4
+ compactas; `lg`, estados mais amplos. O componentepossui o nome acessível “Carregando”.
5
5
 
6
6
  ```tsx preview
7
7
  <Spinner size="sm" />
@@ -11,8 +11,8 @@ Divergência da casa: o shadcn atual só tem size-4 fixo; mantivemos sm/default/
11
11
 
12
12
  ## No botão
13
13
 
14
- Ação em andamento: disabled + Spinner sm + verbo no gerúndio substitui o Loader2Icon inline
15
- repetido.
14
+ Durante uma ação, combine o spinner compacto com o estado desabilitado e um rótulo que descreva o
15
+ andamento.
16
16
 
17
17
  ```tsx preview
18
18
  <Button disabled><Spinner size="sm" /> Publicando…</Button>
@@ -30,8 +30,8 @@ Skeleton.
30
30
  </div>
31
31
  ```
32
32
 
33
- ## Props
33
+ ## Propriedades de Spinner
34
34
 
35
- | Prop | Tipo | Default | Descrição |
35
+ | Propriedade | Tipo | Padrão | Descrição |
36
36
  |---|---|---|---|
37
- | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | O tamanho — sm pra dentro de botão, lg pra estados de página. |
37
+ | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | O tamanho — sm para dentro de botão, lg para estados de página. |
@@ -1,6 +1,8 @@
1
- ## Split & Pane
1
+ ## Dividir uma área
2
2
 
3
- `Split` organiza áreas lado a lado ou uma sobre a outra. A ordem dos `Pane` define a posição de cada área. Sem `resizable`, o layout usa flex; com essa opção, a pessoa também pode ajustar as divisórias com ponteiro ou teclado.
3
+ Use `Split` para organizar áreas lado a lado ou empilhadas. A ordem dos `Pane` define a posição. Com
4
+ `resizable`, a pessoa pode ajustar as divisórias por ponteiro ou teclado; sem essa propriedade, o
5
+ layout permanece estático.
4
6
 
5
7
  ```tsx preview
6
8
  render(
@@ -17,11 +19,12 @@ render(
17
19
  )
18
20
  ```
19
21
 
20
- ## Inset
22
+ ## Espaçamento interno
21
23
 
22
- O `Pane` traz `inset="md"` por default. Use `none` para chrome, navegação e conteúdo que já possui padding próprio; `sm` e `lg` atendem densidades comuns. `className` sempre pode ajustar o caso específico.
24
+ `Pane` usa `inset="md"` por padrão. Use `none` para navegação e conteúdo que já controla o próprio
25
+ espaçamento; `sm` e `lg` atendem outras densidades.
23
26
 
24
- ## Rail
27
+ ## Largura inicial
25
28
 
26
29
  Quando uma área precisa continuar legível em telas largas, use uma unidade absoluta para o tamanho inicial ou mínimo. Números continuam representando porcentagens; strings aceitam `%`, `rem`, `em`, `vh`, `vw` e `px`. Prefira `rem` para acompanhar a escala tipográfica configurada pela aplicação.
27
30
 
@@ -7,10 +7,8 @@ title: Armazenamento
7
7
  > **Experimental.** Superfície mínima, sem consumidor interno ainda — cresce por
8
8
  > reincidência de caso real, não por especulação.
9
9
 
10
- Upload, download, remoção e URL de acesso o que backoffice precisa de arquivos
11
- (anexo de nota, foto de produto, contrato). O contrato é um adapter do core
12
- (`StorageAdapter`): a key é um **caminho lógico** (`clientes/123/contrato.pdf`) e o
13
- driver resolve onde ela mora.
10
+ Use `StorageAdapter` para upload, download, remoção e geração de URL de arquivos. A chave é um
11
+ caminho lógico, como `clientes/123/contrato.pdf`; cada driver decide onde o conteúdo é armazenado.
14
12
 
15
13
  ## O contrato
16
14
 
@@ -23,8 +21,8 @@ interface StorageAdapter {
23
21
  }
24
22
  ```
25
23
 
26
- Keys passam pela mesma régua em todo driver (`normalizeKey`): relativas, sem `..`,
27
- sem `\` traversal é barrado no contrato, não em cada driver.
24
+ `normalizeKey` aplica a mesma validação a todos os drivers: chaves são relativas e não aceitam
25
+ `..` nem `\`. Assim, tentativas de escapar do diretório são bloqueadas antes do armazenamento.
28
26
 
29
27
  ## Drivers
30
28
 
@@ -44,7 +42,7 @@ const prod = s3Storage({
44
42
  })
45
43
  ```
46
44
 
47
- Os peers do S3 são **opcionais** (`@aws-sdk/client-s3`; `s3-request-presigner` pro
45
+ Os peers do S3 são **opcionais** (`@aws-sdk/client-s3`; `s3-request-presigner` para o
48
46
  `url()`): quem não usa o driver não os instala — importados sob demanda.
49
47
 
50
48
  ## No runtime
@@ -62,7 +60,7 @@ handler: async (ctx, input) => {
62
60
  }
63
61
  ```
64
62
 
65
- ## Limites (por enquanto)
63
+ ## Limites atuais
66
64
 
67
65
  Bytes em memória (`Uint8Array`), sem streams — arquivo gigante ainda não é o caso;
68
66
  sem `list`, sem metadata rica. Cada um entra quando um projeto real cobrar — é a
@@ -1,6 +1,7 @@
1
- ## Básico
1
+ ## Alternar uma preferência
2
2
 
3
- Sempre em par com Label (htmlFor↔id) clicar no texto alterna a chave. defaultChecked pro modo não controlado.
3
+ Use `Switch` para uma preferência booleana que entra em vigor imediatamente. Associe o controle a
4
+ `Label`; `defaultChecked` define o estado inicial no modo não controlado.
4
5
 
5
6
  ```tsx preview
6
7
  <div className="flex items-center gap-2">
@@ -11,7 +12,7 @@ Sempre em par com Label (htmlFor↔id) — clicar no texto alterna a chave. defa
11
12
 
12
13
  ## Controlado
13
14
 
14
- onCheckedChange recebe um boolean pareie com checked pra guardar o estado no componente que decide.
15
+ Use `checked` e `onCheckedChange` quando o estado pertencer ao consumidor.
15
16
 
16
17
  ```tsx preview
17
18
  const [autoReview, setAutoReview] = useState(true)
@@ -30,7 +31,7 @@ render(
30
31
 
31
32
  ## Tamanho sm
32
33
 
33
- size=sm encolhe a chave — pra densidade em linha de lista, como cada repositório do workspace.
34
+ Use `size="sm"` em linhas de lista e outras composições compactas.
34
35
 
35
36
  ```tsx preview col-start
36
37
  <div className="flex items-center gap-2">
@@ -58,12 +59,12 @@ disabled esmaece e bloqueia a chave — ligada ou desligada — e o rótulo em p
58
59
  </div>
59
60
  ```
60
61
 
61
- ## Props
62
+ ## Propriedades de Switch
62
63
 
63
- | Prop | Tipo | Default | Descrição |
64
+ | Propriedade | Tipo | Padrão | Descrição |
64
65
  |---|---|---|---|
65
66
  | `checked` | `boolean` | | O estado, no modo controlado — parear com onCheckedChange. |
66
67
  | `onCheckedChange` | `(checked: boolean) => void` | | Chamado a cada alternância, com o novo estado. |
67
68
  | `defaultChecked` | `boolean` | `false` | Estado inicial no modo não controlado. |
68
- | `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave — sm pra densidade em linha de lista. |
69
+ | `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave — sm para densidade em linha de lista. |
69
70
  | `disabled` | `boolean` | `false` | Esmaece e bloqueia — o Label em par esmaece junto (peer-disabled). |