@softize/opus 18.5.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 (151) hide show
  1. package/CHANGELOG.md +275 -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 +298 -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 +16 -12
  18. package/src/ui/components/patterns/fill-region.ts +150 -0
  19. package/src/ui/components/patterns/heading-level.ts +16 -0
  20. package/src/ui/components/patterns/list.tsx +103 -73
  21. package/src/ui/components/patterns/page.tsx +20 -4
  22. package/src/ui/components/patterns/pane.tsx +294 -0
  23. package/src/ui/components/patterns/presentation.tsx +81 -54
  24. package/src/ui/components/patterns/sidebar.tsx +13 -72
  25. package/src/ui/components/patterns/split.tsx +51 -38
  26. package/src/ui/components/patterns/surface-anatomy.ts +246 -0
  27. package/src/ui/components/patterns/surface-header.tsx +51 -6
  28. package/src/ui/components/patterns/surface-media.tsx +69 -0
  29. package/src/ui/components/patterns/trigger.tsx +12 -2
  30. package/src/ui/components/primitives/alert.tsx +9 -1
  31. package/src/ui/components/primitives/avatar.tsx +7 -3
  32. package/src/ui/components/primitives/card.tsx +91 -29
  33. package/src/ui/components/primitives/carousel.tsx +34 -5
  34. package/src/ui/components/primitives/checkbox.tsx +11 -3
  35. package/src/ui/components/primitives/command.tsx +14 -5
  36. package/src/ui/components/primitives/copyable.tsx +120 -10
  37. package/src/ui/components/primitives/detail.tsx +10 -13
  38. package/src/ui/components/primitives/dialog.tsx +179 -45
  39. package/src/ui/components/primitives/drawer.tsx +131 -24
  40. package/src/ui/components/primitives/empty.tsx +1 -1
  41. package/src/ui/components/primitives/icon-picker.tsx +6 -2
  42. package/src/ui/components/primitives/icon-tile.tsx +7 -0
  43. package/src/ui/components/primitives/input-group.tsx +9 -2
  44. package/src/ui/components/primitives/input-otp.tsx +1 -1
  45. package/src/ui/components/primitives/kbd.tsx +1 -1
  46. package/src/ui/components/primitives/label.tsx +3 -0
  47. package/src/ui/components/primitives/menu.tsx +5 -0
  48. package/src/ui/components/primitives/metric-card.tsx +98 -39
  49. package/src/ui/components/primitives/popover.tsx +1 -1
  50. package/src/ui/components/primitives/progress.tsx +9 -1
  51. package/src/ui/components/primitives/slider.tsx +62 -11
  52. package/src/ui/components/primitives/truncate.tsx +10 -1
  53. package/src/ui/docs/content/accordion.md +74 -27
  54. package/src/ui/docs/content/action-form-card.md +24 -15
  55. package/src/ui/docs/content/action-form-dialog.md +17 -7
  56. package/src/ui/docs/content/action-form.md +92 -77
  57. package/src/ui/docs/content/action-list-dialog.md +75 -38
  58. package/src/ui/docs/content/action-list.md +134 -52
  59. package/src/ui/docs/content/action-trigger.md +27 -10
  60. package/src/ui/docs/content/action-view.md +14 -14
  61. package/src/ui/docs/content/actions.md +161 -66
  62. package/src/ui/docs/content/ai.md +43 -44
  63. package/src/ui/docs/content/alert.md +83 -29
  64. package/src/ui/docs/content/ask.md +51 -26
  65. package/src/ui/docs/content/aspect-ratio.md +22 -24
  66. package/src/ui/docs/content/audit.md +23 -19
  67. package/src/ui/docs/content/auth.md +38 -19
  68. package/src/ui/docs/content/avatar.md +54 -5
  69. package/src/ui/docs/content/badge.md +29 -9
  70. package/src/ui/docs/content/breadcrumb.md +21 -8
  71. package/src/ui/docs/content/button.md +73 -43
  72. package/src/ui/docs/content/calendar.md +134 -26
  73. package/src/ui/docs/content/card.md +252 -35
  74. package/src/ui/docs/content/carousel.md +37 -6
  75. package/src/ui/docs/content/chat.md +118 -37
  76. package/src/ui/docs/content/checkbox.md +73 -7
  77. package/src/ui/docs/content/cli.md +94 -55
  78. package/src/ui/docs/content/collapsible.md +43 -19
  79. package/src/ui/docs/content/command.md +30 -8
  80. package/src/ui/docs/content/composer.md +23 -8
  81. package/src/ui/docs/content/content.md +37 -7
  82. package/src/ui/docs/content/copyable.md +39 -8
  83. package/src/ui/docs/content/customization.md +55 -46
  84. package/src/ui/docs/content/cycle.md +33 -18
  85. package/src/ui/docs/content/data-products.md +115 -0
  86. package/src/ui/docs/content/data-state.md +20 -6
  87. package/src/ui/docs/content/data.md +20 -19
  88. package/src/ui/docs/content/detail.md +12 -5
  89. package/src/ui/docs/content/dialog.md +219 -28
  90. package/src/ui/docs/content/dictionary-value.md +4 -4
  91. package/src/ui/docs/content/dock.md +54 -19
  92. package/src/ui/docs/content/dot.md +35 -5
  93. package/src/ui/docs/content/drawer.md +193 -22
  94. package/src/ui/docs/content/empty-value.md +20 -6
  95. package/src/ui/docs/content/empty.md +24 -3
  96. package/src/ui/docs/content/events.md +76 -16
  97. package/src/ui/docs/content/executions.md +9 -9
  98. package/src/ui/docs/content/field.md +21 -6
  99. package/src/ui/docs/content/getting-started.md +44 -34
  100. package/src/ui/docs/content/icon-picker.md +19 -13
  101. package/src/ui/docs/content/icon-tile.md +28 -5
  102. package/src/ui/docs/content/input-otp.md +58 -7
  103. package/src/ui/docs/content/input.md +22 -3
  104. package/src/ui/docs/content/item.md +53 -6
  105. package/src/ui/docs/content/kbd.md +5 -1
  106. package/src/ui/docs/content/label.md +13 -4
  107. package/src/ui/docs/content/log.md +5 -5
  108. package/src/ui/docs/content/markdown.md +5 -4
  109. package/src/ui/docs/content/mcp.md +49 -23
  110. package/src/ui/docs/content/menu.md +95 -10
  111. package/src/ui/docs/content/metric-card.md +68 -21
  112. package/src/ui/docs/content/observability.md +10 -9
  113. package/src/ui/docs/content/page.md +125 -11
  114. package/src/ui/docs/content/pagination.md +13 -5
  115. package/src/ui/docs/content/pane.md +300 -0
  116. package/src/ui/docs/content/popover.md +42 -5
  117. package/src/ui/docs/content/presentation.md +91 -47
  118. package/src/ui/docs/content/progress.md +27 -14
  119. package/src/ui/docs/content/queue.md +39 -21
  120. package/src/ui/docs/content/radio-group.md +26 -4
  121. package/src/ui/docs/content/router.md +29 -10
  122. package/src/ui/docs/content/runtime.md +90 -54
  123. package/src/ui/docs/content/scheduler.md +53 -21
  124. package/src/ui/docs/content/scroll-area.md +42 -35
  125. package/src/ui/docs/content/select.md +78 -33
  126. package/src/ui/docs/content/semantic-context.md +21 -2
  127. package/src/ui/docs/content/separator.md +15 -9
  128. package/src/ui/docs/content/sidebar.md +25 -45
  129. package/src/ui/docs/content/skeleton.md +13 -4
  130. package/src/ui/docs/content/slider.md +37 -9
  131. package/src/ui/docs/content/spinner.md +4 -3
  132. package/src/ui/docs/content/split.md +34 -20
  133. package/src/ui/docs/content/storage.md +8 -9
  134. package/src/ui/docs/content/switch.md +10 -7
  135. package/src/ui/docs/content/table.md +46 -8
  136. package/src/ui/docs/content/tabs.md +14 -4
  137. package/src/ui/docs/content/testing.md +76 -31
  138. package/src/ui/docs/content/textarea.md +5 -4
  139. package/src/ui/docs/content/toast.md +48 -7
  140. package/src/ui/docs/content/toggle.md +3 -3
  141. package/src/ui/docs/content/tokens.md +15 -26
  142. package/src/ui/docs/content/tooltip.md +18 -2
  143. package/src/ui/docs/content/truncate.md +36 -6
  144. package/src/ui/docs/content/ui.md +74 -22
  145. package/src/ui/docs/content/upgrading.md +45 -36
  146. package/src/ui/docs/plugin.ts +36 -2
  147. package/src/ui/docs/registry.tsx +14 -2
  148. package/src/ui/drivers/react.tsx +16 -8
  149. package/src/ui/meta.ts +38 -32
  150. package/src/ui/react.tsx +14 -0
  151. package/src/vite/design.ts +16 -4
@@ -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`.
@@ -0,0 +1,300 @@
1
+ ## A coluna encaixada em um layout
2
+
3
+ Use `Pane` para a coluna ou faixa que ocupa uma das posições de um [`Split`](/ui/split): a
4
+ navegação à esquerda, o inspetor à direita, a lista que abre o detalhe ao lado. O `Split` decide a
5
+ relação espacial e o redimensionamento; o `Pane` decide o que existe dentro da área recebida.
6
+
7
+ No formato mais simples, o pane é uma caixa com recuo e o conteúdo é seu.
8
+
9
+ ```tsx preview
10
+ render(
11
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
12
+ <Split>
13
+ <Pane initialSize="16rem" className="bg-muted/30">
14
+ <p className="text-sm text-muted-foreground">Navegação.</p>
15
+ </Pane>
16
+ <Pane grow>
17
+ <p className="text-sm text-muted-foreground">Conteúdo.</p>
18
+ </Pane>
19
+ </Split>
20
+ </div>,
21
+ );
22
+ ```
23
+
24
+ ## Cabeçalho, corpo e rodapé
25
+
26
+ Quando a área precisa de regiões fixas e de um corpo que rola, componha os slots. `PaneHeader`
27
+ nomeia a região, `PaneBody` ocupa o espaço restante e concentra a rolagem vertical, e `PaneFooter`
28
+ mantém à vista o que não pode sair da tela.
29
+
30
+ ```tsx preview
31
+ render(
32
+ <div className="h-72 overflow-hidden rounded-lg border border-border">
33
+ <Split>
34
+ <Pane initialSize="18rem">
35
+ <PaneHeader>
36
+ <PaneMedia>
37
+ <MessagesSquare />
38
+ </PaneMedia>
39
+ <PaneTitle>Conversas</PaneTitle>
40
+ <PaneActions>
41
+ <Button variant="ghost" size="icon-sm" aria-label="Nova conversa">
42
+ <SquarePen />
43
+ </Button>
44
+ </PaneActions>
45
+ </PaneHeader>
46
+ <PaneBody className="space-y-1">
47
+ <p className="text-sm">Revisão do contrato</p>
48
+ <p className="text-sm text-muted-foreground">Importação de clientes</p>
49
+ <p className="text-sm text-muted-foreground">Fila de execuções</p>
50
+ </PaneBody>
51
+ <PaneFooter>
52
+ <Button variant="ghost" size="sm">Ver arquivadas</Button>
53
+ </PaneFooter>
54
+ </Pane>
55
+ <Pane grow>
56
+ <p className="text-sm text-muted-foreground">Conversa selecionada.</p>
57
+ </Pane>
58
+ </Split>
59
+ </div>,
60
+ );
61
+ ```
62
+
63
+ O cabeçalho reconhece quatro slots — `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions` —
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. 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
+
68
+ Título e descrição truncam quando a coluna encolhe, em vez de quebrar a altura da faixa.
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
+
114
+ ## Escolher entre seccionado e contínuo
115
+
116
+ `layout` declara a anatomia da superfície inteira e fica no root; os slots leem esse valor e não o
117
+ recebem. Cada valor é um pacote de recuo, ritmo vertical e divisores.
118
+
119
+ | Aspecto | `sectioned` (padrão) | `continuous` |
120
+ | --- | --- | --- |
121
+ | Divisores | entre as regiões vizinhas | nenhum |
122
+ | Faixa do cabeçalho | altura fixa de `3.75rem` | acompanha o conteúdo |
123
+ | Ritmo vertical | regiões simétricas | o cabeçalho se aproxima do corpo |
124
+
125
+ O padrão `sectioned` é o pane de operação: a faixa do cabeçalho tem a mesma altura da barra do
126
+ [`PageShell`](/ui/page), então uma navegação lateral e o chrome da página se alinham na mesma
127
+ linha. Use `continuous` quando o pane for um bloco de leitura contínuo, sem fronteiras a marcar.
128
+
129
+ Os divisores são derivados da vizinhança: cada região desenha a linha quando há uma região do outro
130
+ lado. Um cabeçalho sem corpo não fecha nada, e um cabeçalho seguido direto de um rodapé produz uma
131
+ linha só. A tela não escreve `border-b` nem `border-t`.
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
+
138
+ ## Recuo do pane e recuo das regiões
139
+
140
+ `inset` é o respiro interno do pane sem slots: `md` por padrão, com `none`, `sm` e `lg`
141
+ disponíveis. Quando `PaneHeader`, `PaneBody` ou `PaneFooter` são filhos **diretos** do pane, o recuo
142
+ passa a ser de cada região e o `inset` vira `none` sozinho — é o que permite ao divisor alcançar a
143
+ borda da coluna.
144
+
145
+ O ajuste enxerga só o primeiro nível. Se os slots vierem dentro de outro componente — é o caso da
146
+ `Sidebar`, que traz a superfície e o colapso —, o pane não sabe que eles existem: declare
147
+ `inset="none"` você mesmo, ou a coluna ganha um recuo que empurra o divisor para dentro.
148
+
149
+ ## A matriz de layouts e slots
150
+
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.
153
+
154
+ ```tsx preview col
155
+ render(
156
+ <div className="grid grid-cols-2 gap-4">
157
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
158
+ <Pane className="h-full">
159
+ <PaneHeader>
160
+ <PaneTitle>Seccionado</PaneTitle>
161
+ <PaneActions>
162
+ <Button variant="ghost" size="icon-sm" aria-label="Mais ações">
163
+ <Ellipsis />
164
+ </Button>
165
+ </PaneActions>
166
+ </PaneHeader>
167
+ <PaneBody>
168
+ <p className="text-sm text-muted-foreground">Cabeçalho, corpo e rodapé.</p>
169
+ </PaneBody>
170
+ <PaneFooter>
171
+ <Button variant="ghost" size="sm">Configurações</Button>
172
+ </PaneFooter>
173
+ </Pane>
174
+ </div>
175
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
176
+ <Pane layout="continuous" className="h-full">
177
+ <PaneHeader>
178
+ <PaneMedia>
179
+ <ChartNoAxesColumn />
180
+ </PaneMedia>
181
+ <PaneTitle>Contínuo com mídia</PaneTitle>
182
+ <PaneDescription>O espaço separa as regiões.</PaneDescription>
183
+ </PaneHeader>
184
+ <PaneBody>
185
+ <p className="text-sm text-muted-foreground">Sem rodapé e sem divisores.</p>
186
+ </PaneBody>
187
+ </Pane>
188
+ </div>
189
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
190
+ <Pane className="h-full">
191
+ <PaneBody>
192
+ <p className="text-sm text-muted-foreground">Só corpo: nenhuma fronteira a marcar.</p>
193
+ </PaneBody>
194
+ </Pane>
195
+ </div>
196
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
197
+ <Pane className="h-full">
198
+ <PaneHeader>
199
+ <PaneMedia context="primary">
200
+ <Folder />
201
+ </PaneMedia>
202
+ <PaneTitle>Sem corpo</PaneTitle>
203
+ </PaneHeader>
204
+ <PaneFooter>
205
+ <Button variant="ghost" size="sm">Ação persistente</Button>
206
+ </PaneFooter>
207
+ </Pane>
208
+ </div>
209
+ </div>,
210
+ );
211
+ ```
212
+
213
+ ## Onde termina o Pane
214
+
215
+ Três componentes dividem a responsabilidade de uma coluna, e trocá-los de papel é a origem mais
216
+ comum de layout duplicado:
217
+
218
+ | Componente | Responsabilidade |
219
+ | --- | --- |
220
+ | [`Split`](/ui/split) | Relação espacial entre as áreas e, com `resizable`, o arraste da fronteira. |
221
+ | `Pane` | Tamanho recebido, recuo e as regiões internas: cabeçalho, corpo e rodapé. |
222
+ | [`Sidebar`](/ui/sidebar) | Superfície da navegação lateral e o estado de colapso dos destinos. |
223
+
224
+ Tamanho inicial, limites e persistência do layout são declarados no `Pane` e operados pelo `Split`
225
+ — a [página do Split](/ui/split) trata deles. Uma `Sidebar` é composta dentro de um `Pane` e usa os
226
+ mesmos slots para o próprio cabeçalho e rodapé; ela não tem regiões próprias.
227
+
228
+ ## Hierarquia do título
229
+
230
+ `PaneTitle` é um heading. `level` escolhe a hierarquia do documento, de `1` a `6`, com `2` como
231
+ padrão. O tamanho visual vem da família e do `layout`, não do nível, então corrigir a estrutura do
232
+ documento não muda a aparência da faixa.
233
+
234
+ ## Propriedades de Pane
235
+
236
+ | Propriedade | Tipo | Padrão | Descrição |
237
+ |---|---|---|---|
238
+ | `layout` | `'continuous' \| 'sectioned'` | `'sectioned'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. |
239
+ | `inset` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'md'`, ou `'none'` com slots diretos | Respiro interno do pane sem slots; slots dentro de outro componente exigem declarar. |
240
+ | `id` | `string` | | Identidade estável usada pelo layout redimensionável e persistido. |
241
+ | `initialSize` | `number \| string` | | Tamanho inicial; número é porcentagem, string aceita `%`, `rem`, `em`, `vh`, `vw` e `px`. |
242
+ | `minSize` / `maxSize` | `number \| string` | | Limites aplicados pelo `Split` quando ele é redimensionável. |
243
+ | `grow` | `boolean` | `false` | Ocupa o espaço remanescente no layout simples. |
244
+ | `className` | `string` | | Ajusta a coluna. |
245
+ | `children` | `ReactNode` | | Conteúdo direto ou os slots estruturais. |
246
+
247
+ ## Propriedades de PaneHeader
248
+
249
+ | Propriedade | Tipo | Padrão | Descrição |
250
+ |---|---|---|---|
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. |
252
+ | `className` | `string` | | Ajusta a faixa fixa sobre o recuo do layout. |
253
+
254
+ ## Propriedades de PaneMedia
255
+
256
+ | Propriedade | Tipo | Padrão | Descrição |
257
+ |---|---|---|---|
258
+ | `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'xs'` | Tamanho da moldura e do glifo na escala compartilhada. |
259
+ | `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Declara o significado da cor. |
260
+ | `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
261
+ | `className` | `string` | | Ajusta a moldura sem substituir a escala. |
262
+ | `asChild` | `boolean` | `false` | Cede o elemento para uma marca com visual próprio — um orbe, um logotipo — que ocupa o slot sem ganhar a moldura por cima. |
263
+
264
+ ## Propriedades de PaneTitle
265
+
266
+ | Propriedade | Tipo | Padrão | Descrição |
267
+ |---|---|---|---|
268
+ | `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `2` | Hierarquia do documento. O tamanho vem do `layout`, nunca do nível. |
269
+ | `className` | `string` | | Ajusta o título, que trunca quando não cabe. |
270
+
271
+ ## Propriedades de PaneDescription
272
+
273
+ | Propriedade | Tipo | Padrão | Descrição |
274
+ |---|---|---|---|
275
+ | `children` | `ReactNode` | | Frase curta de apoio sob o título; trunca quando não cabe. |
276
+ | `className` | `string` | | Ajusta a descrição. |
277
+
278
+ ## Propriedades de PaneActions
279
+
280
+ | Propriedade | Tipo | Padrão | Descrição |
281
+ |---|---|---|---|
282
+ | `children` | `ReactNode` | | Controles no extremo oposto ao título. |
283
+ | `className` | `string` | | Ajusta o agrupamento das ações. |
284
+
285
+ ## Propriedades de PaneBody
286
+
287
+ | Propriedade | Tipo | Padrão | Descrição |
288
+ |---|---|---|---|
289
+ | `children` | `ReactNode` | | Conteúdo que ocupa o espaço entre cabeçalho e rodapé. |
290
+ | `className` | `string` | | Ajusta a região flexível e rolável. |
291
+
292
+ `PaneContent` continua exportado como alias depreciado de `PaneBody` e sai em uma próxima versão
293
+ maior. Código novo usa `PaneBody`.
294
+
295
+ ## Propriedades de PaneFooter
296
+
297
+ | Propriedade | Tipo | Padrão | Descrição |
298
+ |---|---|---|---|
299
+ | `children` | `ReactNode` | | Navegação ou ações que precisam permanecer acessíveis no rodapé. |
300
+ | `className` | `string` | | Ajusta a faixa fixa sobre o recuo do layout. |
@@ -2,7 +2,8 @@
2
2
 
3
3
  Use `Popover` para apresentar conteúdo livre junto a um gatilho, sem abrir um modal.
4
4
  `PopoverHeader` agrupa título e descrição; o restante do painel aceita formulários curtos ou
5
- detalhes.
5
+ detalhes. Para uma lista de ações, use [`Menu`](/ui/menu); para uma dica curta ao passar o ponteiro,
6
+ use [`Tooltip`](/ui/tooltip).
6
7
 
7
8
  ```tsx preview
8
9
  <Popover>
@@ -12,7 +13,7 @@ detalhes.
12
13
  <PopoverContent align="start">
13
14
  <PopoverHeader>
14
15
  <PopoverTitle>Renomear</PopoverTitle>
15
- <PopoverDescription>O slug técnico não muda — só o título.</PopoverDescription>
16
+ <PopoverDescription>O endereço da sessão continua o mesmo; só o título muda.</PopoverDescription>
16
17
  </PopoverHeader>
17
18
  <div className="mt-3 space-y-2">
18
19
  <Label htmlFor="pop-titulo">Título</Label>
@@ -23,9 +24,14 @@ detalhes.
23
24
  </Popover>
24
25
  ```
25
26
 
26
- ## Alinhamento
27
+ O painel mede `18rem` de largura por padrão e abre abaixo do gatilho. Clicar fora ou pressionar
28
+ Esc fecha o painel.
27
29
 
28
- `align` posiciona o painel em relação ao gatilho e `sideOffset` define a distância entre eles.
30
+ ## Posição e largura
31
+
32
+ `side` escolhe o lado preferido, `align` alinha o painel ao gatilho e `sideOffset` define a
33
+ distância entre eles. Quando não há espaço no lado escolhido, o painel abre no lado oposto. Ajuste
34
+ a largura com `className`, como `w-56` para `14rem`.
29
35
 
30
36
  ```tsx preview
31
37
  <Popover>
@@ -40,6 +46,12 @@ detalhes.
40
46
  <PopoverDescription>Alinhado à borda direita do gatilho.</PopoverDescription>
41
47
  </PopoverContent>
42
48
  </Popover>
49
+ <Popover>
50
+ <PopoverTrigger asChild><Button variant="ghost">À direita</Button></PopoverTrigger>
51
+ <PopoverContent side="right" className="w-56">
52
+ <PopoverDescription>Abre ao lado direito do gatilho.</PopoverDescription>
53
+ </PopoverContent>
54
+ </Popover>
43
55
  ```
44
56
 
45
57
  ## Propriedades de Popover
@@ -47,7 +59,15 @@ detalhes.
47
59
  | Propriedade | Tipo | Padrão | Descrição |
48
60
  |---|---|---|---|
49
61
  | `open` | `boolean` | | Estado no modo controlado. |
50
- | `onOpenChange` | `(open: boolean) => void` | | Atualiza o estado para permitir abertura ou fechamento por código. |
62
+ | `onOpenChange` | `(open: boolean) => void` | | Chamado quando o painel abre ou fecha; atualize `open` com o valor recebido. |
63
+ | `defaultOpen` | `boolean` | `false` | Estado inicial no modo não controlado. |
64
+ | `modal` | `boolean` | `false` | Com `true`, prende o foco no painel e bloqueia a interação com o restante da página enquanto ele está aberto. |
65
+
66
+ ## Propriedades de PopoverTrigger
67
+
68
+ | Propriedade | Tipo | Padrão | Descrição |
69
+ |---|---|---|---|
70
+ | `asChild` | `boolean` | `false` | Faz do filho (por exemplo, um `Button`) o gatilho, em vez de renderizar outro `<button>`. |
51
71
 
52
72
  ## Propriedades de PopoverAnchor
53
73
 
@@ -59,5 +79,22 @@ não introduzir um nó extra; sem ele, o `PopoverTrigger` é a âncora.
59
79
 
60
80
  | Propriedade | Tipo | Padrão | Descrição |
61
81
  |---|---|---|---|
82
+ | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` | Lado preferido do painel; sem espaço, ele abre no lado oposto. |
62
83
  | `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alinhamento do painel em relação ao gatilho. |
63
84
  | `sideOffset` | `number` | `4` | Distância entre o gatilho e o painel. |
85
+ | `className` | `string` | | Classes do painel. A largura padrão é `w-72` (`18rem`). |
86
+
87
+ ## Propriedades de PopoverHeader
88
+
89
+ `PopoverHeader` empilha `PopoverTitle` e `PopoverDescription` com espaçamento curto. Não tem props
90
+ próprias; aceita as de um elemento `div`.
91
+
92
+ ## Propriedades de PopoverTitle
93
+
94
+ `PopoverTitle` destaca o nome do painel com peso médio. Ele renderiza um `div`, sem nível de
95
+ heading. Não tem props próprias; aceita as de um elemento `div`.
96
+
97
+ ## Propriedades de PopoverDescription
98
+
99
+ `PopoverDescription` apresenta o texto de apoio em cor atenuada. Não tem props próprias; aceita as
100
+ de um elemento `p`.
@@ -1,45 +1,11 @@
1
- `Presentation` preserva a identidade de um recurso quando o contexto pede uma página, um dialog ou
2
- um drawer. A superfície decide quanto espaço ocupar; o recurso continua com a mesma navegação,
3
- cabeçalho, body e rodapé.
4
-
5
- Dialog e Drawer mantêm no cabeçalho a ordem horizontal de navegação ou retorno, título e ações.
6
- O retorno aparece em uma surface modal somente
7
- quando ela foi aberta sobre outro Dialog ou Drawer; a Page ao fundo sustenta o modal, mas não cria
8
- uma etapa de navegação. Formulários modais dimensionam o footer pelo conteúdo, com `Cancelar` em
9
- `ghost` e a ação principal sólida.
10
- Em Page hospedada por `PageShell`, a barra concentra navegação e ações globais. Uma Presentation
11
- de listagem materializa título e comandos de header no `Content` que envolve o `ActionList`; assim
12
- a criação permanece próxima da coleção. Esses comandos usam o tamanho `default`: o que abre outra
13
- Presentation é um `Button` na variante padrão (`solid`), e uma action `simple` vira um
14
- `ActionTrigger` na variante `ghost`. Nas demais Presentations, o título abre o conteúdo em
15
- `PageIntro` e os comandos da superfície seguem a mesma regra.
16
-
17
- Use esse padrão quando uma lista puder abrir um detalhe lateral, quando a mesma edição precisar
18
- funcionar em modal e em rota própria ou quando um fluxo começar compacto e crescer sem ganhar uma
19
- segunda implementação.
20
-
21
- Dentro de `PageShell`, a superfície `page` usa a anatomia real da aplicação: a Presentation projeta
22
- navigation e actions globais por `PageHeader`, mantém a introdução no conteúdo quando existir e
23
- compõe `PageBody` e, quando necessário, `PageFooter`. Para uma action `list`, `PageBody` recebe
24
- `Content + ActionList`; o header da coleção recebe o título e os comandos declarados no placement
25
- `header`. Dialog e Drawer continuam usando o header da própria superfície.
26
- Os comandos da coleção ficam no extremo oposto ao título e permanecem textuais, mesmo quando a
27
- action declara um ícone. A criação usa `Criar recurso`, sem ícone, em um `Button` na variante
28
- padrão (`solid`) e no tamanho `default`.
29
- Não envolva a Presentation em um card para simular a página; valide proporção, rolagem e ações no
30
- shell que efetivamente hospeda a rota.
1
+ Use `Presentation` quando o mesmo recurso precisa aparecer ora como página, ora como dialog, ora
2
+ como drawer: uma lista que abre o detalhe ao lado, uma edição que funciona em modal e em rota
3
+ própria, um fluxo que começa compacto e cresce sem ganhar uma segunda implementação. A superfície
4
+ decide quanto espaço ocupar; o recurso mantém a mesma navegação, cabeçalho, corpo e rodapé.
31
5
 
32
- O exemplo declara a Presentation sobre um contrato de formulário e alterna a superfície. O
6
+ O exemplo declara uma Presentation sobre um contrato de formulário e alterna a superfície. O
33
7
  `DocBrowserActionProvider` simula o cliente de actions; na aplicação, esse papel é do provider real.
34
8
 
35
- Quando o recurso aceita assistência contextual, declare somente o gatilho serializável em
36
- `assistant`. O renderer coloca esse gatilho no cabeçalho e abre o painel à direita do body da
37
- própria Page, Dialog ou Drawer. Cabeçalho e rodapé continuam ocupando toda a largura da superfície;
38
- enquanto o painel está aberto, o gatilho sai do cabeçalho e o próprio painel assume sua identidade
39
- visual. O dado vivo do recurso não entra na definição: a página que o carregou fornece
40
- `assistant.render`, captura identidade, nome e demais dados autorizados e monta o chat. Assim,
41
- contexto de conversa não vira autorização nem dado persistido no manifest.
42
-
43
9
  ```tsx live
44
10
  const workspacePresentation = definePresentation({
45
11
  schemaVersion: 1,
@@ -64,7 +30,7 @@ function Example() {
64
30
  <DocBrowserActionProvider>
65
31
  <div className="space-y-4">
66
32
  <ButtonGroup mode="spaced">
67
- {["page", "dialog", "drawer"].map((value) => (
33
+ {(["page", "dialog", "drawer"] as const).map((value) => (
68
34
  <Button
69
35
  key={value}
70
36
  size="sm"
@@ -115,14 +81,48 @@ function Example() {
115
81
  render(<Example />);
116
82
  ```
117
83
 
84
+ ## Como cada superfície apresenta o recurso
85
+
86
+ Em Dialog e Drawer, o cabeçalho da própria superfície reúne, nesta ordem, o retorno, o título e as
87
+ ações. O retorno só aparece quando o modal foi aberto sobre outro Dialog ou Drawer: a página ao
88
+ fundo sustenta o modal, mas não é uma etapa para onde voltar. Formulários modais dimensionam o
89
+ rodapé pelo conteúdo, com `Cancelar` em `ghost` e a ação principal sólida.
90
+
91
+ Dentro de [`PageShell`](/ui/page), a superfície `page` usa a anatomia real da aplicação. O
92
+ `PageHeader` recebe só a navegação e as ações globais; o título abre o conteúdo em `PageIntro`, e o
93
+ corpo vai para `PageBody`, com `PageFooter` quando há ações de rodapé.
94
+
95
+ Uma Presentation de listagem exibida como página muda essa distribuição para manter a criação perto
96
+ da coleção. O `PageBody` recebe um [`Content`](/ui/content) com a
97
+ [`ActionList`](/ui/action-list), e o cabeçalho desse `Content` recebe o título e os comandos
98
+ declarados com `placement: "header"`, no extremo oposto ao título. Os comandos de cabeçalho usam o
99
+ tamanho `default` e permanecem textuais, mesmo quando a action declara um ícone: o que abre outra
100
+ Presentation é um `Button` sólido, e uma action `simple` vira um
101
+ [`ActionTrigger`](/ui/action-trigger) `ghost`. O rótulo vem do `label` da action, como `Criar workspace`.
102
+
103
+ Não envolva a Presentation em um card para simular a página. Valide proporção, rolagem e ações no
104
+ shell que efetivamente hospeda a rota.
105
+
106
+ ## Assistência contextual
107
+
108
+ Quando o recurso aceita uma conversa sobre ele, declare em `assistant` somente o gatilho
109
+ serializável, como no exemplo acima. O renderer coloca o gatilho no cabeçalho e abre o painel à
110
+ direita do corpo da própria página, dialog ou drawer. Cabeçalho e rodapé continuam ocupando toda a
111
+ largura; enquanto o painel está aberto, o gatilho sai do cabeçalho e o painel assume a identidade
112
+ visual da assistência.
113
+
114
+ O dado vivo do recurso não entra na definição. A página que carregou o recurso fornece
115
+ `assistant.render`, reúne identidade, nome e demais dados autorizados e monta o chat. Assim, o
116
+ contexto da conversa não vira autorização nem dado persistido no manifest.
117
+
118
118
  ## A declaração
119
119
 
120
120
  `definePresentation` cria o artefato serializável. O body recebe uma action `form`, `list` ou
121
- `view`; uma action `simple` aparece como comando no cabeçalho ou no rodapé. Em formulário,
122
- `body.submitLabel` sobrescreve o padrão “Salvar” quando a operação tem outro resultado observável,
123
- como “Renomear” ou “Criar nova versão”. Actions que precisam de
124
- interface abrem outra Presentation. Bindings fornecem valores vindos da rota, do registro, da
125
- seleção, da sessão ou do resultado anterior sem guardar callbacks no JSON.
121
+ `view`, ou um componente React, descrito em “Conteúdo React no body”; uma action `simple` aparece
122
+ como comando no cabeçalho ou no rodapé. Em formulário, `body.submitLabel` sobrescreve o padrão
123
+ “Salvar” quando a operação tem outro resultado observável, como “Renomear” ou “Criar nova versão”.
124
+ Actions que precisam de interface abrem outra Presentation. Bindings fornecem valores vindos da
125
+ rota, do registro, da seleção, da sessão ou do resultado anterior sem guardar callbacks no JSON.
126
126
 
127
127
  Quando a exibição precisa sobreviver a refresh, deep link ou histórico, declare `route` na própria
128
128
  Presentation. O `path` aceita segmentos fixos e parâmetros como `:id`; `surface` determina como a
@@ -154,6 +154,48 @@ const workspaceList = definePresentation({
154
154
  });
155
155
  ```
156
156
 
157
+ ## Conteúdo React no body
158
+
159
+ Uma tela que já existe em React pode adotar a casca da Presentation — rota, superfície, título,
160
+ navegação e ações — sem migrar o miolo para actions declarativas de uma vez. Declare
161
+ `body: { component }` com um identificador estável para esse conteúdo e passe o conteúdo como
162
+ `children` do `Presentation`:
163
+
164
+ ```tsx
165
+ const appearance = definePresentation({
166
+ schemaVersion: 1,
167
+ id: "settings.appearance",
168
+ title: "Aparência",
169
+ route: { path: "/settings/appearance", surface: "page" },
170
+ body: { component: "settings.appearance.content" },
171
+ });
172
+
173
+ <Presentation
174
+ definition={appearance}
175
+ definitions={[appearance]}
176
+ actions={{}}
177
+ invocation={invocation}
178
+ >
179
+ <AppearanceSettings />
180
+ </Presentation>;
181
+ ```
182
+
183
+ O identificador de `component` vai para o manifest; o JSX, não. Uma Presentation com body de
184
+ componente exige `children`, e uma com body de action os recusa: nos dois casos, a renderização
185
+ lança um erro que nomeia a Presentation.
186
+
187
+ Com `heading: "shell"`, o padrão, a casca emite o título da definição. Quando o conteúdo React já
188
+ declara o heading principal da página, como um `Content level={1}`, use `heading: "content"`: dentro
189
+ de `PageShell`, a casca deixa de emitir o `PageIntro` e o documento continua com um único `h1`. Em
190
+ Dialog e Drawer, o título continua no cabeçalho da superfície.
191
+
192
+ Se o conteúdo precisa ocupar a altura da página e controlar a própria rolagem, como uma lista com
193
+ cabeçalho fixo, passe `layout="fill"` ao `Presentation`. Com o padrão `flow`, a página rola como um
194
+ documento. Uma [`ActionList`](/ui/action-list) filha direta do conteúdo React recebe essa altura e
195
+ assume `fill` sozinha.
196
+
197
+ ## Registrar as declarações
198
+
157
199
  Registre as declarações em `presentations` no `opus.config.ts`. `opus gen` valida as referências e
158
200
  projeta o JSON no manifest; a Lens pode então exibir o artefato completo sem reconstruí-lo a partir
159
201
  do JSX.
@@ -161,7 +203,7 @@ do JSX.
161
203
  ## A invocação
162
204
 
163
205
  `definePresentationInvocation` cria o estado vivo de uma exibição. Ele contém somente valores JSON,
164
- a surface atual e a pilha necessária para voltar. A definição continua estática no manifest; input
206
+ a superfície atual e a pilha necessária para voltar. A definição continua estática no manifest; input
165
207
  de rota, sessão e resultado existe apenas na invocação.
166
208
 
167
209
  ```ts
@@ -194,7 +236,7 @@ no manifest e concentre a inspeção na Lens em vez de criar um launcher flutuan
194
236
  | `definition` | `PresentationDefinition` | | Artefato estático que descreve o recurso. |
195
237
  | `definitions` | `PresentationDefinition[]` | | Registry alcançável por navegação, abertura e retorno. |
196
238
  | `actions` | `PresentationActionRegistry` | | Contratos compartilháveis referenciados pelo artefato. |
197
- | `invocation` | `PresentationInvocation` | | Surface, input e pilha da exibição atual. |
239
+ | `invocation` | `PresentationInvocation` | | Superfície, input e pilha da exibição atual. |
198
240
  | `bindingContext` | `PresentationBindingContext` | | Rota, item, seleção, sessão e resultado disponíveis. |
199
241
  | `assistant` | `PresentationAssistantRuntime` | | Painel contextual fornecido pela superfície hospedeira. |
200
242
  | `onInvocationChange` | `(next: PresentationInvocation \| null) => void` | | Recebe navegação, retorno e fechamento. |
@@ -205,6 +247,8 @@ no manifest e concentre a inspeção na Lens em vez de criar um launcher flutuan
205
247
  | `onOpenChange` | `(open: boolean) => void` | | Notifica abertura e fechamento da superfície modal. |
206
248
  | `className` | `string` | | Classes adicionais da superfície. |
207
249
  | `bodyClassName` | `string` | | Classes adicionais do body, aplicadas uma única vez. |
250
+ | `layout` | `'flow' \| 'fill'` | `'flow'` | Em `page`, `fill` limita a altura ao shell e entrega a rolagem ao conteúdo do body; a lista de um body `list` ocupa essa altura e rola só os resultados. Dialog e Drawer ignoram. |
251
+ | `children` | `ReactNode` | | Conteúdo React do body declarado com `body.component`. Obrigatório nesse caso e recusado com `body.action`. |
208
252
 
209
253
  ## Propriedades de PresentationInspector
210
254