@softize/opus 19.0.0 → 20.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 (142) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/bin/cli.mjs +5 -0
  3. package/bin/lib/check.mjs +198 -10
  4. package/bin/lib/copy.mjs +29 -0
  5. package/bin/lib/mcp.mjs +1 -1
  6. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +5 -0
  7. package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
  8. package/docs/data-products.md +15 -9
  9. package/docs/protocol.md +3 -4
  10. package/docs/releasing.md +2 -2
  11. package/package.json +2 -2
  12. package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
  13. package/registry/templates/app/package.json +1 -1
  14. package/src/core/runtime.ts +96 -8
  15. package/src/core/types.ts +5 -2
  16. package/src/ui/components/patterns/action-form-card.tsx +11 -1
  17. package/src/ui/components/patterns/content-header.tsx +15 -3
  18. package/src/ui/components/patterns/fill-region.ts +150 -0
  19. package/src/ui/components/patterns/list.tsx +89 -78
  20. package/src/ui/components/patterns/page.tsx +20 -4
  21. package/src/ui/components/patterns/presentation.tsx +52 -37
  22. package/src/ui/components/patterns/split.tsx +44 -4
  23. package/src/ui/components/patterns/surface-anatomy.ts +35 -8
  24. package/src/ui/components/patterns/trigger.tsx +12 -2
  25. package/src/ui/components/primitives/alert.tsx +9 -1
  26. package/src/ui/components/primitives/avatar.tsx +7 -3
  27. package/src/ui/components/primitives/carousel.tsx +34 -5
  28. package/src/ui/components/primitives/checkbox.tsx +11 -3
  29. package/src/ui/components/primitives/command.tsx +2 -1
  30. package/src/ui/components/primitives/copyable.tsx +120 -10
  31. package/src/ui/components/primitives/dialog.tsx +41 -10
  32. package/src/ui/components/primitives/drawer.tsx +9 -1
  33. package/src/ui/components/primitives/empty.tsx +1 -1
  34. package/src/ui/components/primitives/icon-picker.tsx +6 -2
  35. package/src/ui/components/primitives/icon-tile.tsx +7 -0
  36. package/src/ui/components/primitives/input-group.tsx +9 -2
  37. package/src/ui/components/primitives/input-otp.tsx +1 -1
  38. package/src/ui/components/primitives/kbd.tsx +1 -1
  39. package/src/ui/components/primitives/label.tsx +3 -0
  40. package/src/ui/components/primitives/menu.tsx +5 -0
  41. package/src/ui/components/primitives/popover.tsx +1 -1
  42. package/src/ui/components/primitives/progress.tsx +9 -1
  43. package/src/ui/components/primitives/slider.tsx +62 -11
  44. package/src/ui/components/primitives/truncate.tsx +10 -1
  45. package/src/ui/docs/content/accordion.md +74 -27
  46. package/src/ui/docs/content/action-form-card.md +24 -15
  47. package/src/ui/docs/content/action-form-dialog.md +17 -7
  48. package/src/ui/docs/content/action-form.md +92 -77
  49. package/src/ui/docs/content/action-list-dialog.md +75 -38
  50. package/src/ui/docs/content/action-list.md +134 -52
  51. package/src/ui/docs/content/action-trigger.md +27 -10
  52. package/src/ui/docs/content/action-view.md +14 -14
  53. package/src/ui/docs/content/actions.md +161 -66
  54. package/src/ui/docs/content/ai.md +43 -44
  55. package/src/ui/docs/content/alert.md +83 -29
  56. package/src/ui/docs/content/ask.md +51 -26
  57. package/src/ui/docs/content/aspect-ratio.md +22 -24
  58. package/src/ui/docs/content/audit.md +23 -19
  59. package/src/ui/docs/content/auth.md +38 -19
  60. package/src/ui/docs/content/avatar.md +54 -5
  61. package/src/ui/docs/content/badge.md +29 -9
  62. package/src/ui/docs/content/breadcrumb.md +21 -8
  63. package/src/ui/docs/content/button.md +73 -43
  64. package/src/ui/docs/content/calendar.md +134 -26
  65. package/src/ui/docs/content/card.md +41 -15
  66. package/src/ui/docs/content/carousel.md +37 -6
  67. package/src/ui/docs/content/chat.md +118 -37
  68. package/src/ui/docs/content/checkbox.md +73 -7
  69. package/src/ui/docs/content/cli.md +94 -55
  70. package/src/ui/docs/content/collapsible.md +43 -19
  71. package/src/ui/docs/content/command.md +30 -8
  72. package/src/ui/docs/content/composer.md +23 -8
  73. package/src/ui/docs/content/content.md +31 -7
  74. package/src/ui/docs/content/copyable.md +39 -8
  75. package/src/ui/docs/content/customization.md +55 -46
  76. package/src/ui/docs/content/cycle.md +33 -18
  77. package/src/ui/docs/content/data-products.md +115 -0
  78. package/src/ui/docs/content/data-state.md +20 -6
  79. package/src/ui/docs/content/data.md +20 -19
  80. package/src/ui/docs/content/detail.md +12 -5
  81. package/src/ui/docs/content/dialog.md +107 -24
  82. package/src/ui/docs/content/dictionary-value.md +4 -4
  83. package/src/ui/docs/content/dock.md +54 -19
  84. package/src/ui/docs/content/dot.md +35 -5
  85. package/src/ui/docs/content/drawer.md +63 -19
  86. package/src/ui/docs/content/empty-value.md +20 -6
  87. package/src/ui/docs/content/empty.md +24 -3
  88. package/src/ui/docs/content/events.md +76 -16
  89. package/src/ui/docs/content/executions.md +9 -9
  90. package/src/ui/docs/content/field.md +21 -6
  91. package/src/ui/docs/content/getting-started.md +44 -34
  92. package/src/ui/docs/content/icon-picker.md +19 -13
  93. package/src/ui/docs/content/icon-tile.md +28 -5
  94. package/src/ui/docs/content/input-otp.md +58 -7
  95. package/src/ui/docs/content/input.md +22 -3
  96. package/src/ui/docs/content/item.md +53 -6
  97. package/src/ui/docs/content/kbd.md +5 -1
  98. package/src/ui/docs/content/label.md +13 -4
  99. package/src/ui/docs/content/log.md +5 -5
  100. package/src/ui/docs/content/markdown.md +5 -4
  101. package/src/ui/docs/content/mcp.md +49 -23
  102. package/src/ui/docs/content/menu.md +95 -10
  103. package/src/ui/docs/content/metric-card.md +12 -4
  104. package/src/ui/docs/content/observability.md +10 -9
  105. package/src/ui/docs/content/page.md +118 -12
  106. package/src/ui/docs/content/pagination.md +13 -5
  107. package/src/ui/docs/content/pane.md +55 -5
  108. package/src/ui/docs/content/popover.md +42 -5
  109. package/src/ui/docs/content/presentation.md +91 -47
  110. package/src/ui/docs/content/progress.md +27 -14
  111. package/src/ui/docs/content/queue.md +39 -21
  112. package/src/ui/docs/content/radio-group.md +26 -4
  113. package/src/ui/docs/content/router.md +29 -10
  114. package/src/ui/docs/content/runtime.md +90 -54
  115. package/src/ui/docs/content/scheduler.md +53 -21
  116. package/src/ui/docs/content/scroll-area.md +42 -35
  117. package/src/ui/docs/content/select.md +78 -33
  118. package/src/ui/docs/content/semantic-context.md +21 -2
  119. package/src/ui/docs/content/separator.md +15 -9
  120. package/src/ui/docs/content/sidebar.md +3 -2
  121. package/src/ui/docs/content/skeleton.md +6 -1
  122. package/src/ui/docs/content/slider.md +37 -9
  123. package/src/ui/docs/content/spinner.md +4 -3
  124. package/src/ui/docs/content/split.md +19 -0
  125. package/src/ui/docs/content/storage.md +8 -9
  126. package/src/ui/docs/content/switch.md +10 -7
  127. package/src/ui/docs/content/table.md +46 -8
  128. package/src/ui/docs/content/tabs.md +14 -4
  129. package/src/ui/docs/content/testing.md +76 -31
  130. package/src/ui/docs/content/textarea.md +5 -4
  131. package/src/ui/docs/content/toast.md +48 -7
  132. package/src/ui/docs/content/toggle.md +3 -3
  133. package/src/ui/docs/content/tokens.md +15 -26
  134. package/src/ui/docs/content/tooltip.md +18 -2
  135. package/src/ui/docs/content/truncate.md +36 -6
  136. package/src/ui/docs/content/ui.md +74 -22
  137. package/src/ui/docs/content/upgrading.md +45 -36
  138. package/src/ui/docs/plugin.ts +36 -2
  139. package/src/ui/docs/registry.tsx +6 -0
  140. package/src/ui/drivers/react.tsx +16 -8
  141. package/src/ui/meta.ts +31 -31
  142. package/src/vite/design.ts +16 -4
@@ -17,13 +17,58 @@ direita; separe ações perigosas e declare `context="danger"` nelas.
17
17
  </Menu>
18
18
  ```
19
19
 
20
+ `MenuShortcut` só exibe o atalho. Registre a tecla no aplicativo; o menu não a escuta.
21
+
22
+ ## Executar a ação do item
23
+
24
+ `onSelect` recebe a escolha feita por clique ou teclado. Depois dele, o menu fecha. Para manter o
25
+ painel aberto, como em uma alternância que a pessoa pode repetir, chame `event.preventDefault()` no
26
+ handler. `disabled` mantém o item visível, mas impede a seleção.
27
+
28
+ ```tsx preview
29
+ const [pinned, setPinned] = useState(false)
30
+ const [last, setLast] = useState('Nenhuma ação ainda.')
31
+
32
+ render(
33
+ <div className="flex items-center gap-4">
34
+ <Menu>
35
+ <MenuTrigger asChild>
36
+ <Button variant="outline">Sessão <ChevronDown /></Button>
37
+ </MenuTrigger>
38
+ <MenuContent align="start">
39
+ <MenuItem onSelect={() => setLast('Link copiado.')}><Link /> Copiar link</MenuItem>
40
+ <MenuItem
41
+ onSelect={(event) => {
42
+ event.preventDefault()
43
+ setPinned((current) => !current)
44
+ }}
45
+ >
46
+ {pinned ? <PinOff /> : <Pin />} {pinned ? 'Desafixar' : 'Fixar'}
47
+ </MenuItem>
48
+ <MenuItem disabled><Share2 /> Compartilhar</MenuItem>
49
+ </MenuContent>
50
+ </Menu>
51
+ <p className="text-sm text-muted-foreground">{last}</p>
52
+ </div>,
53
+ )
54
+ ```
55
+
56
+ Para navegar em vez de executar, use `asChild` com um link: o item passa a renderizar o `<a>`,
57
+ com o estilo e a navegação por teclado do menu.
58
+
59
+ ```tsx
60
+ <MenuItem asChild>
61
+ <a href="/sessoes/42">Abrir sessão</a>
62
+ </MenuItem>
63
+ ```
64
+
20
65
  ## Abrir pelo menu de contexto
21
66
 
22
67
  Para abrir pelo clique direito, controle o estado do menu e use a posição do ponteiro como âncora.
23
- Esse padrão substitui o antigo `ContextMenu` sem introduzir outra família de componentes.
68
+ Não há componente separado para menu de contexto; use `Menu` controlado.
24
69
 
25
70
  ```tsx preview
26
- const [pos, setPos] = useState(null)
71
+ const [pos, setPos] = useState<{ x: number; y: number } | null>(null)
27
72
 
28
73
  render(
29
74
  <div>
@@ -34,7 +79,7 @@ render(
34
79
  }}
35
80
  className="flex h-28 items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground select-none"
36
81
  >
37
- clique direito aqui
82
+ Clique com o botão direito aqui
38
83
  </div>
39
84
  <Menu open={pos !== null} onOpenChange={(open) => { if (!open) setPos(null) }}>
40
85
  <MenuTrigger asChild>
@@ -83,7 +128,8 @@ render(
83
128
 
84
129
  ## Submenu e inset
85
130
 
86
- MenuSub aninha um nível; inset alinha itens sem ícone com os que têm.
131
+ `MenuSub` aninha um nível: `MenuSubTrigger` abre o painel `MenuSubContent` ao lado do item. `inset`
132
+ alinha itens sem ícone com os que têm.
87
133
 
88
134
  ```tsx preview
89
135
  <Menu>
@@ -93,7 +139,7 @@ MenuSub aninha um nível; inset alinha itens sem ícone com os que têm.
93
139
  <MenuContent align="start">
94
140
  <MenuItem><ExternalLink /> Abrir preview</MenuItem>
95
141
  <MenuSub>
96
- <MenuSubTrigger><ArrowDownAZ /> Mover para</MenuSubTrigger>
142
+ <MenuSubTrigger><FolderInput /> Mover para</MenuSubTrigger>
97
143
  <MenuSubContent>
98
144
  <MenuItem>Empresa X</MenuItem>
99
145
  <MenuItem>Softize</MenuItem>
@@ -104,6 +150,10 @@ MenuSub aninha um nível; inset alinha itens sem ícone com os que têm.
104
150
  </Menu>
105
151
  ```
106
152
 
153
+ `MenuContent` já renderiza o painel em um portal, fora do fluxo da página. `MenuPortal` continua
154
+ exportado, mas está depreciado e sai em uma próxima versão maior: envolver o conteúdo com ele não
155
+ muda onde o painel aparece. Código novo usa só `MenuContent`.
156
+
107
157
  ## Propriedades de Menu
108
158
 
109
159
  | Propriedade | Tipo | Padrão | Descrição |
@@ -112,10 +162,26 @@ MenuSub aninha um nível; inset alinha itens sem ícone com os que têm.
112
162
  | `onOpenChange` | `(open: boolean) => void` | | Chamado quando o painel abre ou fecha. |
113
163
  | `defaultOpen` | `boolean` | `false` | Estado inicial no modo não controlado. |
114
164
 
165
+ ## Propriedades de MenuTrigger
166
+
167
+ | Propriedade | Tipo | Padrão | Descrição |
168
+ |---|---|---|---|
169
+ | `asChild` | `boolean` | `false` | Usa o filho, como um `Button`, como gatilho em vez de renderizar outro `<button>`. |
170
+
171
+ ## Propriedades de MenuContent
172
+
173
+ | Propriedade | Tipo | Padrão | Descrição |
174
+ |---|---|---|---|
175
+ | `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alinhamento do painel em relação ao gatilho. |
176
+ | `sideOffset` | `number` | `4` | Distância entre o gatilho e o painel. |
177
+
115
178
  ## Propriedades de MenuItem
116
179
 
117
180
  | Propriedade | Tipo | Padrão | Descrição |
118
181
  |---|---|---|---|
182
+ | `onSelect` | `(event: Event) => void` | | Chamado quando a pessoa escolhe o item por clique ou teclado. `event.preventDefault()` mantém o menu aberto. |
183
+ | `disabled` | `boolean` | `false` | Mantém o item visível e impede a seleção. |
184
+ | `asChild` | `boolean` | `false` | Renderiza o filho, como um `<a>`, com o comportamento de item de menu. |
119
185
  | `context` | `'neutral' \| 'danger'` | `'neutral'` | `danger` sinaliza uma ação com consequência perigosa. |
120
186
  | `inset` | `boolean` | | Alinha um item sem ícone com os itens que possuem ícone. |
121
187
 
@@ -125,12 +191,11 @@ MenuSub aninha um nível; inset alinha itens sem ícone com os que têm.
125
191
  `MenuLabel` para nomear o grupo e `MenuSeparator` para separá-lo do próximo. Não tem props
126
192
  próprias além das de DOM.
127
193
 
128
- ## Propriedades de MenuContent
194
+ ## Propriedades de MenuLabel
129
195
 
130
196
  | Propriedade | Tipo | Padrão | Descrição |
131
197
  |---|---|---|---|
132
- | `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alinhamento do painel em relação ao gatilho. |
133
- | `sideOffset` | `number` | `4` | Distância entre o gatilho e o painel. |
198
+ | `inset` | `boolean` | | Alinha o rótulo com itens que possuem ícone. |
134
199
 
135
200
  ## Propriedades de MenuCheckboxItem
136
201
 
@@ -153,14 +218,34 @@ próprias além das de DOM.
153
218
  | `value` | `string` | | Valor que o item entrega ao `MenuRadioGroup` quando é selecionado. |
154
219
  | `disabled` | `boolean` | `false` | Impede a seleção do item. |
155
220
 
156
- ## Propriedades de MenuLabel
221
+ ## Propriedades de MenuSeparator
222
+
223
+ `MenuSeparator` desenha a linha entre grupos de itens. Não tem props próprias; aceita as de um
224
+ elemento `div`, como `className`.
225
+
226
+ ## Propriedades de MenuShortcut
227
+
228
+ `MenuShortcut` exibe o atalho no extremo direito do item. Não tem props próprias; aceita as de um
229
+ elemento `span`, como `className`.
230
+
231
+ ## Propriedades de MenuSub
157
232
 
158
233
  | Propriedade | Tipo | Padrão | Descrição |
159
234
  |---|---|---|---|
160
- | `inset` | `boolean` | | Alinha o rótulo com itens que possuem ícone. |
235
+ | `open` | `boolean` | | Estado do submenu no modo controlado. |
236
+ | `onOpenChange` | `(open: boolean) => void` | | Chamado quando o submenu abre ou fecha. |
237
+ | `defaultOpen` | `boolean` | `false` | Estado inicial no modo não controlado. |
161
238
 
162
239
  ## Propriedades de MenuSubTrigger
163
240
 
164
241
  | Propriedade | Tipo | Padrão | Descrição |
165
242
  |---|---|---|---|
166
243
  | `inset` | `boolean` | | Alinha o gatilho do submenu sem ícone com os itens que possuem ícone. |
244
+ | `disabled` | `boolean` | `false` | Impede que o submenu abra. |
245
+
246
+ ## Propriedades de MenuSubContent
247
+
248
+ | Propriedade | Tipo | Padrão | Descrição |
249
+ |---|---|---|---|
250
+ | `sideOffset` | `number` | `0` | Distância entre o item que abre o submenu e o painel. |
251
+ | `alignOffset` | `number` | `0` | Deslocamento do painel ao longo do item que o abre. |
@@ -1,6 +1,6 @@
1
- MetricCard apresenta uma medida resumida com hierarquia e espaçamento consistentes. Passe o
2
- valor já calculado e formatado; busca de dados, recorte e regras de negócio permanecem no
3
- consumidor.
1
+ Use `MetricCard` para apresentar uma medida resumida, como um total ou uma contagem, com rótulo,
2
+ valor e contexto em hierarquia consistente. Passe o valor já calculado e formatado; busca de dados,
3
+ recorte e regras de negócio permanecem no consumidor.
4
4
 
5
5
  ```tsx preview col
6
6
  <MetricCard
@@ -101,6 +101,11 @@ render(
101
101
  `active`, `activationLabel` e `onActivate` formam uma capacidade única: informe as três propriedades
102
102
  para tornar o card ativável. O consumidor continua responsável pelo estado e pelo recorte aplicado.
103
103
 
104
+ O card ativo recebe a borda na cor primária. Ao passar o ponteiro, a borda se destaca, e o foco de
105
+ teclado desenha um anel por dentro da borda. Com ativação, use `onActivate` em vez de `onClick`: o
106
+ tipo recusa `onClick`, porque um clique genérico no card também dispararia ao usar `labelAction` ou
107
+ `action`. Enquanto `loading` estiver ativo, a ativação fica desabilitada.
108
+
104
109
  ## Carregamento
105
110
 
106
111
  `loading` preserva a geometria do card com placeholders e marca a superfície como ocupada para
@@ -125,7 +130,10 @@ tecnologias assistivas. O contêiner da coleção continua responsável pela men
125
130
  | `action` | `ReactNode` | | Ação relacionada diretamente à medida. |
126
131
  | `active` | `boolean` | | Indica se a medida controla o recorte atualmente aplicado. Exige `activationLabel` e `onActivate`. |
127
132
  | `activationLabel` | `string` | | Nome acessível da ativação. Exige `active` e `onActivate`. |
128
- | `onActivate` | `() => void` | | Ativa a medida como recorte ou seleção. Exige `active` e `activationLabel`. |
133
+ | `onActivate` | `() => void` | | Ativa a medida como recorte ou seleção. Exige `active` e `activationLabel`; não combina com `onClick`. Desabilitada durante `loading`. |
134
+ | `className` | `string` | | Ajusta o card, ou o contêiner quando há ativação ou medida complementar. |
135
+
136
+ As demais propriedades são as nativas de `div`.
129
137
 
130
138
  ## Propriedades de MetricCardSecondaryMetric
131
139
 
@@ -37,20 +37,21 @@ global. Um `Tracer` também pode ser injetado para testes ou providers não glob
37
37
 
38
38
  ## Semântica
39
39
 
40
- - nomes de span: `opus.action <action>` e `opus.reaction <reaction>`;
41
- - parent vem de `ContextBase.trace` ou do evento recebido;
42
- - actions usam `ActionResult.ok` para status; reactions usam resolve/reject;
43
- - somente atributos allowlisted de baixa cardinalidade são anexados;
44
- - input, output, actor, tenant, request ID, error code/message e baggage não entram no span;
40
+ - Os spans se chamam `opus.action <action>` e `opus.reaction <reaction>`.
41
+ - O span pai vem de `ContextBase.trace` ou do evento recebido.
42
+ - Actions usam `ActionResult.ok` para o status; reactions usam a resolução ou a rejeição da Promise.
43
+ - Somente atributos de baixa cardinalidade, de uma lista fixa, são anexados.
44
+ - Input, output, ator, tenant, request ID, código e mensagem de erro e baggage não entram no span.
45
45
  - `TraceContext` devolve apenas `traceId`, `spanId`, `traceFlags` e `traceState`.
46
46
 
47
47
  ## Fronteira HTTP W3C
48
48
 
49
- Os drivers Fastify e Node oferecem propagação explícita, desativada por default:
49
+ Os drivers Fastify e Node oferecem propagação explícita, desativada por padrão:
50
50
 
51
51
  ```ts
52
- fastifyServer({ app, traceContext: 'w3c' })
53
- nodeServer({ traceContext: 'w3c' })
52
+ const server = fastifyServer({ app, traceContext: 'w3c' })
53
+ // nodeServer devolve { adapter, handler, hasRoute }; o runtime recebe `adapter`.
54
+ const node = nodeServer({ traceContext: 'w3c' })
54
55
  ```
55
56
 
56
57
  O opt-in extrai um `traceparent` v00 válido (e `tracestate` limitado) para
@@ -65,7 +66,7 @@ O driver OpenTelemetry não instrumenta HTTP automaticamente e não oferece back
65
66
  Use o opt-in dos drivers Fastify/Node acima ou forneça `ContextBase.trace` na instrumentação
66
67
  de outro transporte. A fronteira implementa somente `traceparent` v00 e uma validação
67
68
  defensiva limitada de `tracestate`; não é um propagator OTel genérico.
68
- Health do exporter/Collector só é reportado quando o app injeta `healthCheck`; o default
69
+ A saúde do exporter e do Collector só é reportada quando o app injeta `healthCheck`; o padrão
69
70
  confirma apenas que a API do driver está configurada.
70
71
  Sem um provider que produza spans válidos, o driver falha antes do callback e o runtime aplica
71
72
  a degradação lenient — executa a action uma vez, sem trace — em vez de propagar IDs inválidos.
@@ -13,9 +13,8 @@ A forma curta é o padrão para páginas comuns. Fora de `PageShell`, ela cria `
13
13
  `PageBody`. Dentro dele, cria `PageHeader` para as ações, `PageIntro` para o título e `PageBody`.
14
14
  A forma explícita exige exatamente um heading principal. Use `PageTitle` no header ou intro,
15
15
  `Content level={1}` no body, ou deixe um `PageState` ativo assumir esse papel. Assim, header e
16
- introdução podem ser omitidos quando o próprio conteúdo nomeia a página.
17
- Contadores e outros indicadores pertencem ao
18
- conteúdo que os explica.
16
+ introdução podem ser omitidos quando o próprio conteúdo nomeia a página. Contadores e outros
17
+ indicadores pertencem ao conteúdo que os explica.
19
18
 
20
19
  A `Page` é contínua por definição: separar o chrome do conteúdo é trabalho do shell, e não há
21
20
  divisores entre regiões para declarar. O `layout` da `Page` não é a anatomia de superfície de
@@ -51,7 +50,8 @@ render(
51
50
 
52
51
  Use `PageBack` em uma subpágina simples. O destino é explícito para continuar correto após refresh ou
53
52
  acesso por link direto; não derive esse retorno do histórico do navegador. No header padrão, o
54
- controle aparece antes do título como ícone com nome acessível e tooltip derivados do destino.
53
+ controle aparece antes do título como ícone; o nome acessível e a dica nativa do navegador (atributo
54
+ `title`) derivam do destino.
55
55
 
56
56
  ```tsx preview col
57
57
  <Page>
@@ -64,8 +64,30 @@ controle aparece antes do título como ícone com nome acessível e tooltip deri
64
64
  ```
65
65
 
66
66
  Não combine `PageBack` com breadcrumb. Use o retorno para um único pai conhecido. Quando houver
67
- mais de um ancestral relevante, envolva o `Breadcrumb` em `PageNavigation`; ele ocupa a mesma
68
- posição introdutória sem transformar a trilha em ação.
67
+ mais de um ancestral relevante, envolva o [`Breadcrumb`](/ui/breadcrumb) em `PageNavigation`; ele
68
+ ocupa a mesma posição introdutória sem transformar a trilha em ação.
69
+
70
+ ```tsx preview col
71
+ <Page>
72
+ <PageHeader>
73
+ <PageNavigation>
74
+ <Breadcrumb>
75
+ <BreadcrumbList>
76
+ <BreadcrumbItem>Vendas</BreadcrumbItem>
77
+ <BreadcrumbSeparator />
78
+ <BreadcrumbItem>Clientes</BreadcrumbItem>
79
+ <BreadcrumbSeparator />
80
+ <BreadcrumbPage>Carla Souza</BreadcrumbPage>
81
+ </BreadcrumbList>
82
+ </Breadcrumb>
83
+ </PageNavigation>
84
+ </PageHeader>
85
+ <PageIntro>
86
+ <PageTitle>Carla Souza</PageTitle>
87
+ </PageIntro>
88
+ <PageBody>Dados do cliente.</PageBody>
89
+ </Page>
90
+ ```
69
91
 
70
92
  ## Barra persistente do shell
71
93
 
@@ -149,8 +171,87 @@ render(
149
171
 
150
172
  Fora de `PageShell`, use `PageIntro` quando o conteúdo precisar de uma introdução separada do
151
173
  chrome. Dentro do shell, ele pode ser combinado com `PageHeader`: o primeiro permanece no conteúdo
152
- e o segundo alimenta a barra. Em uma página de listagem, `Content` pode fornecer o título e as ações
153
- da coleção, deixando `Page` apenas com `PageBody`.
174
+ e o segundo alimenta a barra. Em uma página de listagem, [`Content`](/ui/content) pode fornecer o
175
+ título e as ações da coleção, deixando `Page` apenas com `PageBody`.
176
+
177
+ ## Ações de etapa no rodapé
178
+
179
+ Use `PageFooter` quando a página for uma etapa de um fluxo e a decisão precisar ficar ao alcance ao
180
+ fim do conteúdo, como confirmar uma importação. O rodapé fica depois do body, com a mesma altura da
181
+ barra e as ações alinhadas no fim da linha.
182
+
183
+ ```tsx preview col
184
+ <Page>
185
+ <PageHeader>
186
+ <PageTitle>Importar clientes</PageTitle>
187
+ </PageHeader>
188
+ <PageBody>Revise os 128 registros antes de confirmar.</PageBody>
189
+ <PageFooter>
190
+ <ButtonGroup mode="spaced">
191
+ <Button variant="ghost">Cancelar</Button>
192
+ <Button>Importar</Button>
193
+ </ButtonGroup>
194
+ </PageFooter>
195
+ </Page>
196
+ ```
197
+
198
+ ## Ocupar a altura disponível
199
+
200
+ Use `layout="fill"` quando uma região interna, como uma tabela longa ou um split, precisar rolar
201
+ dentro da página em vez de a página inteira rolar. A página passa a ocupar exatamente a altura do
202
+ contêiner. Para uma região que você mesmo compõe, dê `min-h-0 flex-1` à que assume a folga e a
203
+ rolagem, como o `PageBody` do exemplo.
204
+
205
+ ```tsx preview col
206
+ render(
207
+ <div className="h-72 overflow-hidden rounded-lg border border-border">
208
+ <Page layout="fill">
209
+ <PageHeader>
210
+ <PageTitle>Ordens de serviço</PageTitle>
211
+ </PageHeader>
212
+ <PageBody className="min-h-0 flex-1 space-y-2 overflow-y-auto">
213
+ {['OS-1842', 'OS-1839', 'OS-1835', 'OS-1830', 'OS-1827', 'OS-1821', 'OS-1818'].map((id) => (
214
+ <div key={id} className="rounded-md border border-border p-3 text-sm">
215
+ {id}
216
+ </div>
217
+ ))}
218
+ </PageBody>
219
+ </Page>
220
+ </div>,
221
+ );
222
+ ```
223
+
224
+ Uma [`ActionList`](/ui/action-list) não precisa dessas classes. Filha de `PageBody`, ou de um
225
+ [`Content`](/ui/content) dentro dele, ela recebe a altura da página e assume `layout="fill"`
226
+ sozinha: `PageBody`, `Content` e `ContentBody` passam a repassar a altura até ela, o cabeçalho da
227
+ tabela fica fixo e só os resultados rolam. As demais seções do `PageBody` mantêm a altura do
228
+ próprio conteúdo, e uma página `fill` sem lista não muda.
229
+
230
+ ## Levar as ações para outra barra
231
+
232
+ `PageShell` já projeta as ações na barra persistente. Use `PageActionsTarget` somente quando um
233
+ hospedeiro próprio tiver outra barra para elas: as `PageActions` da árvore interna passam a ser
234
+ renderizadas no elemento indicado em `target`. Enquanto `target` for `null`, fora de um shell, as
235
+ ações continuam no cabeçalho da página.
236
+
237
+ ```tsx preview col
238
+ function Example() {
239
+ const [target, setTarget] = useState<HTMLElement | null>(null)
240
+
241
+ return (
242
+ <div className="space-y-3">
243
+ <div ref={setTarget} className="flex justify-end rounded-lg border border-border p-2" />
244
+ <PageActionsTarget target={target}>
245
+ <Page title="Clientes" actions={<Button variant="outline">Exportar</Button>}>
246
+ Conteúdo da listagem.
247
+ </Page>
248
+ </PageActionsTarget>
249
+ </div>
250
+ )
251
+ }
252
+
253
+ render(<Example />)
254
+ ```
154
255
 
155
256
  ## Estados integrais
156
257
 
@@ -195,7 +296,8 @@ e, sem `title`, valem `errorMessage` e `emptyMessage`.
195
296
  ```
196
297
 
197
298
  Não use `PageState` para uma falha parcial. Se outra parte da página continua utilizável, mantenha o
198
- estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert`.
299
+ estado na própria seção com [`DataState`](/ui/data-state), [`ActionView`](/ui/action-view),
300
+ [`ActionList`](/ui/action-list) ou [`Alert`](/ui/alert).
199
301
 
200
302
  ## Propriedades de Page
201
303
 
@@ -203,9 +305,10 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
203
305
  | ----------- | ----------- | ----------- | ----------------------------------------------------------------------------------------------- |
204
306
  | `title` | `ReactNode` | | O h1 da página. |
205
307
  | `actions` | `ReactNode` | | Ações contextuais no extremo oposto do cabeçalho. |
206
- | `layout` | `'flow' \| 'fill'` | `'flow'` | Ocupação de altura, não anatomia: `fill` limita a página ao shell para que uma região interna role. |
308
+ | `layout` | `'flow' \| 'fill'` | `'flow'` | Ocupação de altura, não anatomia: `fill` limita a página ao shell para que uma região interna role e leva essa altura até uma `ActionList` composta em `PageBody`. |
207
309
  | `className` | `string` | `max-w-7xl` | Classes do container para substituir o teto padrão de `80rem`. |
208
310
  | `children` | `ReactNode` | | O body da página — espaçamento e diagramação são seus. |
311
+ | demais | Atributos de `HTMLElement` | | Atributos nativos repassados ao elemento `main`, como `id` ou `aria-label`. `className` vai para o container interno. |
209
312
 
210
313
  ## Propriedades de PageShell
211
314
 
@@ -242,7 +345,7 @@ que o shell projeta na barra, e ignora `className` e os demais atributos.
242
345
  | Propriedade | Tipo | Padrão | Descrição |
243
346
  | ------------ | -------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------ |
244
347
  | `href` | `string` | obrigatório | Destino explícito da página pai. Sem ele o retorno não é tabulável nem tem nome acessível. |
245
- | `children` | `ReactNode` | obrigatório | Nome do destino pai usado no rótulo acessível e no tooltip. |
348
+ | `children` | `ReactNode` | obrigatório | Nome do destino pai usado no rótulo acessível e na dica nativa (`title`). |
246
349
  | `aria-label` | `string` | `Voltar para {children}` | Nome acessível; informe-o quando `children` não for texto simples. |
247
350
  | `onClick` | `MouseEventHandler<HTMLAnchorElement>` | | Integração opcional com o roteador do consumidor, junto do `href`, nunca no lugar dele. |
248
351
  | `className` | `string` | | Classes adicionais do link renderizado como botão `ghost`. |
@@ -259,7 +362,7 @@ que o shell projeta na barra, e ignora `className` e os demais atributos.
259
362
 
260
363
  | Propriedade | Tipo | Descrição |
261
364
  | ----------- | --------------------------------- | ----------------------------------------------- |
262
- | `children` | `ReactNode` | Título principal `h1`; ampliado em `PageIntro`. |
365
+ | `children` | `ReactNode` | Título principal `h1`, com a mesma escala em `PageHeader` e `PageIntro`. |
263
366
  | `className` | `string` | Classes adicionais do título. |
264
367
  | demais | Atributos de `HTMLHeadingElement` | Atributos nativos repassados ao heading. |
265
368
 
@@ -287,6 +390,9 @@ que o shell projeta na barra, e ignora `className` e os demais atributos.
287
390
  | `className` | `string` | Classes adicionais da região principal. |
288
391
  | demais | Atributos de `HTMLDivElement` | Atributos nativos repassados ao container. |
289
392
 
393
+ Em uma `Page` com `layout="fill"`, o `PageBody` vira coluna flexível sozinho quando contém uma `ActionList` que
394
+ ocupa a altura.
395
+
290
396
  ## Propriedades de PageActionsTarget
291
397
 
292
398
  | Propriedade | Tipo | Descrição |
@@ -1,9 +1,12 @@
1
1
  ## Navegação entre páginas
2
2
 
3
- Componha `Pagination`, `PaginationContent` e um `PaginationItem` para cada link. `page` fornece o
3
+ Use `Pagination` quando uma coleção for dividida em páginas e a pessoa precisar saltar entre elas.
4
+ Em uma listagem derivada de uma action, [`ActionList`](/ui/action-list) já monta esta paginação no
5
+ rodapé; componha-a diretamente apenas em coleções próprias.
6
+
7
+ Monte `Pagination`, `PaginationContent` e um `PaginationItem` para cada link. `page` fornece o
4
8
  número e o nome acessível; `isActive` identifica a página atual. Os controles mantêm altura fixa e
5
- a largura cresce quando o número precisa de mais espaço. `ActionList` usa esta mesma primitiva no
6
- rodapé.
9
+ a largura cresce quando o número precisa de mais espaço.
7
10
 
8
11
  ```tsx preview
9
12
  <Pagination>
@@ -30,8 +33,9 @@ rodapé.
30
33
  ## Números longos
31
34
 
32
35
  A largura mínima é quadrada e cresce conforme o conteúdo. Na escala densa de um rodapé, as setas e a
33
- elipse usam `size="icon-sm"` (o quadrado de 1.75rem da escala única, com o glifo de 0.875rem); os
34
- números, que não têm um degrau de 1.75rem na escala, ajustam por `className` (`h-7 min-w-7 text-xs`).
36
+ elipse usam `size="icon-sm"`, o quadrado de `1.75rem` da escala única, com ícone de `0.875rem`. Os
37
+ números não têm um degrau textual de `1.75rem` na escala e, por isso, recebem a mesma medida por
38
+ `className`, como no exemplo.
35
39
 
36
40
  ```tsx preview
37
41
  <Pagination>
@@ -148,3 +152,7 @@ render(
148
152
  |---|---|---|---|
149
153
  | `size` | `'icon-xs' \| 'icon-sm' \| 'icon' \| 'icon-lg'` | `'icon'` | Quadrado da escala única; use o mesmo tamanho das setas ao lado. |
150
154
  | `className` | `string` | | Classes adicionais aplicadas à elipse. |
155
+
156
+ `Pagination` renderiza um `<nav>` com o nome acessível “Paginação”, `PaginationContent` um `<ul>` e
157
+ `PaginationItem` um `<li>`. Os três não têm propriedades próprias e aceitam as props nativas do
158
+ respectivo elemento, incluindo `className`.
@@ -62,11 +62,55 @@ render(
62
62
 
63
63
  O cabeçalho reconhece quatro slots — `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions` —
64
64
  e monta a linha sozinho: mídia, título e descrição de um lado, ações no extremo oposto. A ordem em
65
- que os slots são escritos não altera a posição, e um nó que não seja um desses slots interrompe a
66
- renderização com um erro. Conteúdo livre vai para `PaneBody`.
65
+ que os slots são escritos não altera a posição. Cada slot aparece no máximo uma vez: repetir um
66
+ deles interrompe a renderização com um erro que lista os slots aceitos.
67
67
 
68
68
  Título e descrição truncam quando a coluna encolhe, em vez de quebrar a altura da faixa.
69
69
 
70
+ Nas ações do cabeçalho, use `Button` `ghost` no tamanho `icon-sm` (1.75rem), como nos exemplos. O
71
+ pane é uma superfície densa, com título e descrição em escala menor, e o quadrado menor acompanha
72
+ essa escala. [`Dialog`](/ui/dialog) e [`Drawer`](/ui/drawer) usam `icon` (2.25rem) no voltar e nas
73
+ ações do cabeçalho, e a [`Presentation`](/ui/presentation) usa o mesmo tamanho no voltar e no botão
74
+ que abre o assistente.
75
+
76
+ ## Busca ou marca no cabeçalho
77
+
78
+ Uma coluna às vezes precisa, no topo, de algo que não é título nem ação: uma busca que filtra a
79
+ lista, a marca de uma barra lateral. Escreva esse conteúdo direto dentro do `PaneHeader`, ao lado
80
+ dos slots. Só o `Pane` aceita conteúdo livre no cabeçalho; os headers de `Card`, `Dialog` e `Drawer`
81
+ aceitam apenas os próprios slots.
82
+
83
+ ```tsx preview
84
+ render(
85
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
86
+ <Split>
87
+ <Pane initialSize="18rem">
88
+ <PaneHeader>
89
+ <Input icon={<Search />} placeholder="Buscar conversa" aria-label="Buscar conversa" />
90
+ <PaneActions>
91
+ <Button variant="ghost" size="icon-sm" aria-label="Nova conversa">
92
+ <SquarePen />
93
+ </Button>
94
+ </PaneActions>
95
+ </PaneHeader>
96
+ <PaneBody className="space-y-1">
97
+ <p className="text-sm">Revisão do contrato</p>
98
+ <p className="text-sm text-muted-foreground">Importação de clientes</p>
99
+ </PaneBody>
100
+ </Pane>
101
+ <Pane grow>
102
+ <p className="text-sm text-muted-foreground">Conversa selecionada.</p>
103
+ </Pane>
104
+ </Split>
105
+ </div>,
106
+ );
107
+ ```
108
+
109
+ O conteúdo livre ocupa o lugar do título e absorve a largura disponível, por isso as ações
110
+ continuam no extremo oposto. Quando há título, o conteúdo livre vem sempre depois do título e da descrição,
111
+ qualquer que seja a ordem no JSX: uma busca escrita junto de um `PaneTitle` aparece abaixo dele, não
112
+ ao lado. Para algo antes do título, use `PaneMedia`; para algo ao lado, use `PaneActions`.
113
+
70
114
  ## Escolher entre seccionado e contínuo
71
115
 
72
116
  `layout` declara a anatomia da superfície inteira e fica no root; os slots leem esse valor e não o
@@ -86,6 +130,11 @@ Os divisores são derivados da vizinhança: cada região desenha a linha quando
86
130
  lado. Um cabeçalho sem corpo não fecha nada, e um cabeçalho seguido direto de um rodapé produz uma
87
131
  linha só. A tela não escreve `border-b` nem `border-t`.
88
132
 
133
+ No `continuous`, um cabeçalho sem corpo nem rodapé fecha o pane com o mesmo recuo que tem em cima.
134
+ Uma classe `pb-*` no `PaneHeader` não altera esse recuo: a regra só vale quando o cabeçalho é a
135
+ última região e tem precedência sobre o `className`. No `sectioned`, a faixa tem altura fixa e
136
+ centraliza a linha, então não há recuo de baixo a ajustar.
137
+
89
138
  ## Recuo do pane e recuo das regiões
90
139
 
91
140
  `inset` é o respiro interno do pane sem slots: `md` por padrão, com `none`, `sm` e `lg`
@@ -99,11 +148,12 @@ O ajuste enxerga só o primeiro nível. Se os slots vierem dentro de outro compo
99
148
 
100
149
  ## A matriz de layouts e slots
101
150
 
102
- As regiões são independentes e o recuo se ajusta a cada combinação, nos dois layouts.
151
+ As regiões são independentes: a coluna usa só as que precisa, nos dois layouts, e os divisores
152
+ acompanham as regiões presentes.
103
153
 
104
154
  ```tsx preview col
105
155
  render(
106
- <div className="grid gap-4 md:grid-cols-2">
156
+ <div className="grid grid-cols-2 gap-4">
107
157
  <div className="h-56 overflow-hidden rounded-lg border border-border">
108
158
  <Pane className="h-full">
109
159
  <PaneHeader>
@@ -198,7 +248,7 @@ documento não muda a aparência da faixa.
198
248
 
199
249
  | Propriedade | Tipo | Padrão | Descrição |
200
250
  |---|---|---|---|
201
- | `children` | `ReactNode` | | Somente `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions`, no máximo um de cada. |
251
+ | `children` | `ReactNode` | | `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions`, no máximo um de cada, e conteúdo livre, que aparece depois do título e da descrição. |
202
252
  | `className` | `string` | | Ajusta a faixa fixa sobre o recuo do layout. |
203
253
 
204
254
  ## Propriedades de PaneMedia