@softize/opus 12.11.0 → 13.1.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 (213) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/PROMOTED.md +46 -0
  3. package/README.md +28 -19
  4. package/bin/cli.mjs +87 -216
  5. package/bin/lib/check.mjs +2 -7
  6. package/bin/lib/cli-shared.mjs +131 -0
  7. package/bin/lib/copy.mjs +1 -5
  8. package/bin/lib/db.mjs +16 -74
  9. package/bin/lib/gen-openapi.mjs +3 -3
  10. package/bin/lib/gen-runner.mjs +1 -1
  11. package/bin/lib/gen.mjs +14 -69
  12. package/bin/lib/mcp.mjs +3 -1
  13. package/bin/lib/seed.mjs +5 -62
  14. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +93 -10
  15. package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
  16. package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
  17. package/docs/ownership-vs-shadcn-lock.md +2 -3
  18. package/docs/protocol.md +7 -7
  19. package/docs/radius-scale.md +1 -1
  20. package/docs/releasing.md +8 -2
  21. package/package.json +7 -3
  22. package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
  23. package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
  24. package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
  25. package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
  26. package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
  27. package/registry/templates/app/package.json +1 -1
  28. package/registry/templates/app/src/main.tsx +4 -4
  29. package/src/audit/drivers/console.ts +1 -0
  30. package/src/auth/drivers/better-auth.ts +1 -0
  31. package/src/auth/drivers/jwt.ts +1 -0
  32. package/src/cache/drivers/memory.ts +1 -0
  33. package/src/client/drivers/fetch.ts +2 -1
  34. package/src/core/actions.ts +6 -1
  35. package/src/core/audit.ts +9 -3
  36. package/src/core/contracts.ts +7 -0
  37. package/src/core/domain.ts +1 -1
  38. package/src/core/errors.ts +18 -15
  39. package/src/core/index.ts +4 -2
  40. package/src/core/package-version.ts +26 -0
  41. package/src/core/reactions.ts +1 -1
  42. package/src/core/runtime.ts +33 -23
  43. package/src/core/schedules.ts +1 -1
  44. package/src/core/types.ts +2 -2
  45. package/src/dsl/eval.ts +2 -2
  46. package/src/dsl/kysely.ts +2 -2
  47. package/src/dsl/loads.ts +1 -1
  48. package/src/dsl/parser.ts +5 -5
  49. package/src/events/drivers/mitt.ts +1 -0
  50. package/src/mcp/index.ts +2 -1
  51. package/src/observability/drivers/opentelemetry.ts +1 -0
  52. package/src/queue/drivers/bullmq.ts +3 -3
  53. package/src/scheduler/drivers/node-cron.ts +3 -2
  54. package/src/scheduler/every.ts +7 -7
  55. package/src/schema/openapi.ts +3 -3
  56. package/src/seed/index.ts +29 -0
  57. package/src/server/drivers/fastify.ts +5 -2
  58. package/src/server/drivers/node.ts +9 -6
  59. package/src/server/index.ts +3 -1
  60. package/src/storage/drivers/fs.ts +1 -0
  61. package/src/testing/index.ts +3 -3
  62. package/src/ui/components/patterns/confirm.tsx +142 -42
  63. package/src/ui/components/patterns/content-header.tsx +7 -1
  64. package/src/ui/components/patterns/data-state.tsx +1 -1
  65. package/src/ui/components/patterns/dock.tsx +20 -3
  66. package/src/ui/components/patterns/form.tsx +12 -8
  67. package/src/ui/components/patterns/list.tsx +36 -41
  68. package/src/ui/components/patterns/page-state.tsx +2 -2
  69. package/src/ui/components/patterns/page.tsx +19 -1
  70. package/src/ui/components/patterns/shell-nav.tsx +10 -3
  71. package/src/ui/components/patterns/sidebar.tsx +43 -32
  72. package/src/ui/components/patterns/trigger.tsx +39 -38
  73. package/src/ui/components/patterns/view.tsx +26 -17
  74. package/src/ui/components/primitives/alert.tsx +14 -8
  75. package/src/ui/components/primitives/ask.tsx +3 -3
  76. package/src/ui/components/primitives/badge.tsx +11 -6
  77. package/src/ui/components/primitives/breadcrumb.tsx +2 -2
  78. package/src/ui/components/primitives/button.tsx +16 -3
  79. package/src/ui/components/primitives/calendar.tsx +28 -2
  80. package/src/ui/components/primitives/carousel.tsx +3 -3
  81. package/src/ui/components/primitives/chat.tsx +1 -1
  82. package/src/ui/components/primitives/checkbox.tsx +1 -1
  83. package/src/ui/components/primitives/command.tsx +2 -2
  84. package/src/ui/components/primitives/control.ts +12 -0
  85. package/src/ui/components/primitives/copyable.tsx +1 -1
  86. package/src/ui/components/primitives/dialog.tsx +202 -40
  87. package/src/ui/components/primitives/dot.tsx +5 -0
  88. package/src/ui/components/primitives/drawer.tsx +18 -8
  89. package/src/ui/components/primitives/empty.tsx +3 -3
  90. package/src/ui/components/primitives/field.tsx +3 -3
  91. package/src/ui/components/primitives/icon-picker.tsx +3 -1
  92. package/src/ui/components/primitives/input-group.tsx +1 -1
  93. package/src/ui/components/primitives/input-otp.tsx +1 -1
  94. package/src/ui/components/primitives/input.tsx +2 -2
  95. package/src/ui/components/primitives/item.tsx +3 -3
  96. package/src/ui/components/primitives/progress.tsx +32 -3
  97. package/src/ui/components/primitives/radio-group.tsx +1 -1
  98. package/src/ui/components/primitives/resizable.tsx +3 -1
  99. package/src/ui/components/primitives/select.tsx +5 -5
  100. package/src/ui/components/primitives/slider.tsx +5 -1
  101. package/src/ui/components/primitives/sonner.tsx +190 -8
  102. package/src/ui/components/primitives/switch.tsx +1 -0
  103. package/src/ui/components/primitives/tabs.tsx +1 -0
  104. package/src/ui/components/primitives/textarea.tsx +1 -1
  105. package/src/ui/components/primitives/toggle.tsx +1 -1
  106. package/src/ui/components/primitives/tooltip.tsx +1 -0
  107. package/src/ui/docs/DocBrowser.tsx +102 -23
  108. package/src/ui/docs/changelog.tsx +1 -1
  109. package/src/ui/docs/content/accordion.md +22 -16
  110. package/src/ui/docs/content/action-form-card.md +8 -8
  111. package/src/ui/docs/content/action-form-dialog.md +9 -9
  112. package/src/ui/docs/content/action-form.md +37 -36
  113. package/src/ui/docs/content/action-list-dialog.md +11 -6
  114. package/src/ui/docs/content/action-list.md +73 -39
  115. package/src/ui/docs/content/action-trigger.md +29 -15
  116. package/src/ui/docs/content/action-view.md +17 -10
  117. package/src/ui/docs/content/actions.md +9 -9
  118. package/src/ui/docs/content/ai.md +3 -3
  119. package/src/ui/docs/content/alert.md +14 -12
  120. package/src/ui/docs/content/ask.md +11 -0
  121. package/src/ui/docs/content/aspect-ratio.md +4 -4
  122. package/src/ui/docs/content/audit.md +2 -2
  123. package/src/ui/docs/content/auth.md +3 -3
  124. package/src/ui/docs/content/avatar.md +34 -14
  125. package/src/ui/docs/content/badge.md +3 -3
  126. package/src/ui/docs/content/breadcrumb.md +13 -8
  127. package/src/ui/docs/content/button.md +81 -6
  128. package/src/ui/docs/content/calendar.md +18 -5
  129. package/src/ui/docs/content/card.md +27 -1
  130. package/src/ui/docs/content/carousel.md +16 -11
  131. package/src/ui/docs/content/chat.md +23 -3
  132. package/src/ui/docs/content/checkbox.md +7 -7
  133. package/src/ui/docs/content/cli.md +74 -22
  134. package/src/ui/docs/content/collapsible.md +8 -8
  135. package/src/ui/docs/content/command.md +16 -8
  136. package/src/ui/docs/content/composer.md +17 -2
  137. package/src/ui/docs/content/content.md +17 -2
  138. package/src/ui/docs/content/copyable.md +12 -3
  139. package/src/ui/docs/content/customization.md +5 -5
  140. package/src/ui/docs/content/cycle.md +3 -3
  141. package/src/ui/docs/content/data-state.md +11 -12
  142. package/src/ui/docs/content/data.md +26 -33
  143. package/src/ui/docs/content/detail.md +22 -4
  144. package/src/ui/docs/content/dialog.md +339 -31
  145. package/src/ui/docs/content/dictionary-value.md +17 -10
  146. package/src/ui/docs/content/dock.md +11 -3
  147. package/src/ui/docs/content/dot.md +8 -0
  148. package/src/ui/docs/content/drawer.md +27 -14
  149. package/src/ui/docs/content/empty-value.md +2 -2
  150. package/src/ui/docs/content/empty.md +19 -12
  151. package/src/ui/docs/content/events.md +4 -4
  152. package/src/ui/docs/content/field.md +34 -12
  153. package/src/ui/docs/content/getting-started.md +3 -3
  154. package/src/ui/docs/content/icon-picker.md +19 -4
  155. package/src/ui/docs/content/input-otp.md +20 -12
  156. package/src/ui/docs/content/input.md +121 -9
  157. package/src/ui/docs/content/item.md +27 -13
  158. package/src/ui/docs/content/kbd.md +19 -11
  159. package/src/ui/docs/content/label.md +12 -3
  160. package/src/ui/docs/content/log.md +4 -4
  161. package/src/ui/docs/content/markdown.md +7 -6
  162. package/src/ui/docs/content/mcp.md +13 -15
  163. package/src/ui/docs/content/menu.md +40 -16
  164. package/src/ui/docs/content/metric-card.md +13 -0
  165. package/src/ui/docs/content/observability.md +2 -2
  166. package/src/ui/docs/content/page.md +59 -6
  167. package/src/ui/docs/content/pagination.md +22 -17
  168. package/src/ui/docs/content/popover.md +22 -8
  169. package/src/ui/docs/content/progress.md +15 -16
  170. package/src/ui/docs/content/queue.md +5 -5
  171. package/src/ui/docs/content/radio-group.md +20 -12
  172. package/src/ui/docs/content/router.md +11 -6
  173. package/src/ui/docs/content/scheduler.md +4 -5
  174. package/src/ui/docs/content/scroll-area.md +12 -7
  175. package/src/ui/docs/content/select.md +47 -34
  176. package/src/ui/docs/content/semantic-context.md +2 -2
  177. package/src/ui/docs/content/separator.md +5 -5
  178. package/src/ui/docs/content/sidebar.md +329 -54
  179. package/src/ui/docs/content/skeleton.md +9 -2
  180. package/src/ui/docs/content/slider.md +8 -7
  181. package/src/ui/docs/content/spinner.md +8 -8
  182. package/src/ui/docs/content/split.md +29 -5
  183. package/src/ui/docs/content/storage.md +6 -8
  184. package/src/ui/docs/content/switch.md +8 -7
  185. package/src/ui/docs/content/table.md +13 -3
  186. package/src/ui/docs/content/tabs.md +28 -14
  187. package/src/ui/docs/content/testing.md +9 -11
  188. package/src/ui/docs/content/textarea.md +12 -4
  189. package/src/ui/docs/content/toast.md +47 -13
  190. package/src/ui/docs/content/toggle.md +75 -7
  191. package/src/ui/docs/content/tokens.md +7 -7
  192. package/src/ui/docs/content/tooltip.md +19 -11
  193. package/src/ui/docs/content/truncate.md +15 -8
  194. package/src/ui/docs/content/ui.md +24 -9
  195. package/src/ui/docs/content/upgrading.md +7 -8
  196. package/src/ui/docs/doc-client.tsx +5 -5
  197. package/src/ui/docs/doc.tsx +26 -14
  198. package/src/ui/docs/registry.tsx +25 -42
  199. package/src/ui/docs/standalone.tsx +2 -2
  200. package/src/ui/drivers/react.tsx +17 -12
  201. package/src/ui/lib/action-errors.ts +45 -0
  202. package/src/ui/lib/zod-pt-br.ts +31 -4
  203. package/src/ui/meta.ts +65 -95
  204. package/src/ui/react.tsx +17 -16
  205. package/src/ui/theme.css +60 -8
  206. package/src/vite/design.ts +6 -18
  207. package/src/ui/components/primitives/alert-dialog.tsx +0 -192
  208. package/src/ui/docs/content/alert-dialog.md +0 -73
  209. package/src/ui/docs/content/button-group.md +0 -71
  210. package/src/ui/docs/content/confirm.md +0 -120
  211. package/src/ui/docs/content/input-group.md +0 -79
  212. package/src/ui/docs/content/page-state.md +0 -45
  213. package/src/ui/docs/content/toggle-group.md +0 -81
@@ -1,8 +1,8 @@
1
- ## Detalhe com estados padronizados
1
+ ## Carregar um recurso
2
2
 
3
- Troque o workspace pra ver o skeleton; o inexistente mostra o erro padrão com o Tentar de novo. O
4
- preview roda num client de mentira no app, cada feature embrulha (TicketView…) delegando o
5
- fetching pra cá.
3
+ Use `ActionView` para carregar um recurso por uma `ViewAction` e manter carregamento, erro e vazio no
4
+ mesmo fluxo. No exemplo, alterne entre os workspaces para observar o carregamento e a recuperação de
5
+ erro. O consumidor compõe somente o conteúdo disponível.
6
6
 
7
7
  ```tsx preview col
8
8
  const [id, setId] = useState('empresa-x')
@@ -35,13 +35,20 @@ render(
35
35
  )
36
36
  ```
37
37
 
38
- ## Props
38
+ ## useViewAction
39
39
 
40
- | Prop | Tipo | Default | Descrição |
40
+ `ActionView` é uma composição sobre `useViewAction(action, input)`, que devolve `data`, `error`,
41
+ `isLoading` e `refetch` sob o `QueryClientProvider`. Use o hook direto quando o recurso alimenta
42
+ mais de uma região da tela ou quando o carregamento precisa ser orquestrado por outro componente.
43
+
44
+ ## Propriedades de ActionView
45
+
46
+ | Propriedade | Tipo | Padrão | Descrição |
41
47
  |---|---|---|---|
42
48
  | `action` | `ViewAction<TInput, TData>` | | A ViewAction do Opus (kind view, 1 recurso). |
43
49
  | `input` | `TInput` | | Geralmente { id } — mudou, recarrega. |
44
- | `children` | `(data: TData, refetch) => ReactNode` | | O estado feliz layout 100% do consumidor; refetch pra recarregar por código. |
45
- | `render` | `(data: TData, refetch) => ReactNode` | | Alias de children (a API original). Children tem precedência. |
46
- | `loading / empty` | `ReactNode` | `3 skeletons / nada` | Sobrescreve os estados padrão quando o contexto pedir. |
47
- | `error` | `(err, retry) => ReactNode` | `mensagem + Tentar de novo` | Sobrescreve o estado de erro padrão. |
50
+ | `children` | `(data: TData, refetch) => ReactNode` | | Conteúdo apresentado quando os dados estão disponíveis. `refetch` permite recarregar por código. |
51
+ | `render` | `(data: TData, refetch) => ReactNode` | | Alias de compatibilidade de `children`; `children` tem precedência. |
52
+ | `loading` | `ReactNode \| boolean` | `3 skeletons` | Sobrescreve o carregamento: um próprio, `true` para o padrão ou `false` para não renderizar nada enquanto carrega. |
53
+ | `empty` | `ReactNode` | `nada` | Sobrescreve o estado vazio (200 sem dado). |
54
+ | `error` | `(err, retry) => ReactNode` | `"Não foi possível carregar" + Tentar de novo` | Sobrescreve o estado de erro padrão. Sem ele, a frase do servidor só aparece quando é legível pela pessoa (`conflict`, `validation`, `not_found`, `authorization`, `authentication`); código técnico nunca vira título, e o botão de tentar de novo some quando o problema é de permissão ou sessão. |
@@ -10,7 +10,7 @@ o handler e a web usa a mesma definição para executar ou renderizar a operaç
10
10
 
11
11
  ## Contrato primeiro
12
12
 
13
- > `defineContract` no `shared/` — entidades, schemas (zod + `t.*` pra tipos lógicos) e os
13
+ > `defineContract` no `shared/` — entidades, schemas (zod + `t.*` para tipos lógicos) e os
14
14
  > metadados da action. `api` e `web` importam; nada se duplica.
15
15
 
16
16
  ```ts
@@ -64,18 +64,18 @@ name → kind → (label / summary / messages / tags…)
64
64
  → invalidates
65
65
  ```
66
66
 
67
- Fail-closed por padrão: `input`/`output` via `z.object` + `t.*` pros tipos lógicos; `authorize`
67
+ Fail-closed por padrão: `input`/`output` via `z.object` + `t.*` para os tipos lógicos; `authorize`
68
68
  declarado ou gate explícito do adapter.
69
69
 
70
- Os átomos do catálogo servem TAMBÉM dentro dos schemas de input/output — não re-escreva
70
+ Os átomos do catálogo servem também dentro dos schemas de input/output — não re-escreva
71
71
  regex do que o Opus já valida: `z.object({ tag: t.slug().zod(), contato: t.email().zod() })`.
72
72
  O `.zod()` devolve o schema zod subjacente do tipo lógico (slug, email, phone, money…).
73
73
 
74
74
  ## Modo design — o mock mora no contrato
75
75
 
76
76
  Um `mockHandler` no contrato deixa a UI rodar com dado realista **sem backend nem banco**: em
77
- `OPUS_MODE=design` o runtime roteia o `execute` pro `mockHandler` (cai no `handler` real se
78
- ausente). Como ele fica no CONTRATO (não no `bindAction`), é isomorfo — o design autora, o
77
+ `OPUS_MODE=design` o runtime roteia o `execute` para o `mockHandler` (cai no `handler` real se
78
+ ausente). Como ele fica no contrato, e não no `bindAction`, é isomorfo — o design autora, o
79
79
  handoff pluga o handler, e o mock nem toca `ctx.db`.
80
80
 
81
81
  Alimente com `fake`/`fakeMany` (fixtures determinísticas do próprio schema — ver [Testes](testing)):
@@ -92,15 +92,15 @@ export const eventGet = defineContract({
92
92
  })
93
93
  // listas: `mockHandler: () => fakeMany(EventEntity.zod(), 20)`
94
94
 
95
- // o handoff, depois, só pluga o real — MESMO contrato:
95
+ // o handoff, depois, só pluga o real — mesmo contrato:
96
96
  export const eventGetImpl = bindAction(eventGet, { handler: async (ctx, input) => { /* db */ } })
97
97
  ```
98
98
 
99
99
  O `mockHandler` roda no **servidor em modo design** (`OPUS_MODE=design`), não no cliente — e
100
100
  quem sobe esse servidor é o próprio dev server: o plugin `opusDesign()` (`@softize/opus/vite`)
101
101
  monta o runtime Opus DENTRO do vite e serve `/api` in-process, com os mocks respondendo e
102
- qualquer `server.proxy` pra backend externo desligado (prefixo ex-proxy sem cobertura responde
103
- 503 em envelope — nada vaza pra prod). A SPA chama `/api` normal e recebe o dado fake, isolado:
102
+ qualquer `server.proxy` para backend externo desligado (prefixo ex-proxy sem cobertura responde
103
+ 503 em envelope — nada vaza para prod). A SPA chama `/api` normal e recebe o dado fake, isolado:
104
104
 
105
105
  ```ts
106
106
  // vite.config.ts
@@ -118,7 +118,7 @@ persistindo, o envelope traz a mensagem-guia apontando o entry.
118
118
 
119
119
  Como o contrato é isomorfo, o `mockHandler` também acompanha o **bundle web** — peso morto lá
120
120
  (a SPA nunca o chama; o dado é fake). Tirá-lo do bundle de prod é um transform de build
121
- (deferido); **não** dá pra gatear no call-site com `import.meta.env` sem quebrar o servidor
121
+ (deferido); **não** dá para gatear no call-site com `import.meta.env` sem quebrar o servidor
122
122
  (não existe em Node). É a materialização Opus-nativa do "mock = contrato": o design não é
123
123
  descartável — vira o contrato que o backend honra.
124
124
 
@@ -64,10 +64,10 @@ handler: async (ctx, input) => {
64
64
  Marque uma action com `ai: { enabled: true }` no contrato e ela vira uma **tool** que o
65
65
  modelo pode chamar. Aí `ctx.ai.run(prompt)` roda o loop agêntico sobre as actions `ai:enabled`:
66
66
  o modelo escolhe a tool → o runtime executa a action **como o usuário logado** (limitado pelo
67
- `ctx.can`) → o resultado volta pro modelo → repete até a resposta em texto.
67
+ `ctx.can`) → o resultado volta para o modelo → repete até a resposta em texto.
68
68
 
69
69
  ```ts
70
- // A action opta por entrar o contrato tem o schema, que vira a tool spec de graça:
70
+ // A action opta por entrar; o schema do contrato também descreve a ferramenta:
71
71
  export const buscarNotas = defineContract({
72
72
  name: 'nota.buscar',
73
73
  kind: 'list',
@@ -76,7 +76,7 @@ export const buscarNotas = defineContract({
76
76
  ai: { enabled: true, description: 'Busca notas por cliente e mês.' },
77
77
  })
78
78
 
79
- // No handler — ou fora dele, via runtime.aiFor(base), pro backend de um chat:
79
+ // No handler — ou fora dele, via runtime.aiFor(base), para o backend de um chat:
80
80
  const { text } = await ctx.ai!.run('Quantas notas a Empresa X emitiu em junho?')
81
81
  // o modelo chamou nota.buscar sozinho, como o usuário logado, e respondeu em texto.
82
82
  ```
@@ -1,6 +1,7 @@
1
1
  ## Forma curta
2
2
 
3
- Quase todo alert é ícone + título + uma frase então isso é UMA linha: `title`, `description` e `icon` como props. O componente monta os slots e a a11y (`role="alert"`, que faz o leitor de tela anunciar sozinho).
3
+ Use a forma curta quando o aviso tiver ícone, título e uma frase. Declare `title`, `description` e
4
+ `icon`; o componente monta os slots e anuncia o conteúdo com `role="alert"`.
4
5
 
5
6
  ```tsx preview col
6
7
  <Alert icon={<Info />} title="Opus 2.8.0" description="Este workspace usa a versão pinada em opus.json." />
@@ -37,10 +38,9 @@ borda e texto; o conteúdo comunica o significado sem depender somente da cor.
37
38
  ## Mídia é opcional
38
39
 
39
40
  Sem `icon` o alert mantém somente a coluna de texto. Com ele, a forma curta materializa
40
- `AlertMedia` à esquerda e `AlertHeader` à direita. A mídia tem largura estável e acompanha a
41
- altura útil do header; com título e descrição de uma linha, a moldura termina junto do texto,
42
- sem sobra inferior. Texto solto como filho também vale (`<Alert>Sincronizado.</Alert>`) e se torna
43
- uma descrição.
41
+ `AlertMedia` à esquerda e `AlertHeader` à direita. A mídia mantém uma moldura quadrada de tamanho
42
+ estável, mesmo quando o título ou a descrição ocupam mais linhas. Texto solto como filho também
43
+ vale (`<Alert>Sincronizado.</Alert>`) e se torna uma descrição.
44
44
 
45
45
  ```tsx preview col
46
46
  <Alert title="Sem provider próprio" description="As conversas usam o padrão do sistema." />
@@ -51,7 +51,8 @@ uma descrição.
51
51
 
52
52
  Quando a mensagem precisa de conteúdo rico, componha os slots da família. `AlertMedia`,
53
53
  `AlertHeader` e `AlertActions` são filhos diretos de `Alert`; `AlertTitle` e
54
- `AlertDescription` pertencem ao header.
54
+ `AlertDescription` pertencem ao header. As ações ficam no fim lógico da superfície e centralizadas
55
+ verticalmente na mesma linha do conteúdo — a mesma posição usada pelas ações do Toast.
55
56
 
56
57
  ```tsx preview col
57
58
  <Alert context="danger">
@@ -59,21 +60,22 @@ Quando a mensagem precisa de conteúdo rico, componha os slots da família. `Ale
59
60
  <CircleAlert />
60
61
  </AlertMedia>
61
62
  <AlertHeader>
62
- <AlertTitle>Não deu pra publicar</AlertTitle>
63
+ <AlertTitle>Não deu para publicar</AlertTitle>
63
64
  <AlertDescription>
64
65
  <p>O registry recusou a versão 3.0.0 — ela já existe.</p>
65
66
  <p>Suba o patch e tente de novo.</p>
66
67
  </AlertDescription>
67
68
  </AlertHeader>
68
69
  <AlertActions>
69
- <Button size="sm" variant="outline">
70
+ <Button size="sm">
70
71
  Tentar novamente
71
72
  </Button>
72
73
  </AlertActions>
73
74
  </Alert>
74
75
  ```
75
76
 
76
- Os dois modos convivem: com `title`/`description` preenchidos, `children` entra DEPOIS da frase é onde vai a ação.
77
+ Os dois modos convivem: com `title` ou `description`, `children` entra depois da mensagem e recebe a
78
+ ação.
77
79
 
78
80
  ```tsx preview col
79
81
  <Alert
@@ -81,15 +83,15 @@ Os dois modos convivem: com `title`/`description` preenchidos, `children` entra
81
83
  title="Sessão presa"
82
84
  description="O ambiente não subiu no tempo esperado."
83
85
  >
84
- <Button size="sm" variant="outline">
86
+ <Button size="sm">
85
87
  Reiniciar
86
88
  </Button>
87
89
  </Alert>
88
90
  ```
89
91
 
90
- ## Props
92
+ ## Propriedades de Alert
91
93
 
92
- | Prop | Tipo | Default | Descrição |
94
+ | Propriedade | Tipo | Padrão | Descrição |
93
95
  | ------------- | ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- |
94
96
  | `title` | `React.ReactNode` | | Título do alert (a forma curta). Não é o atributo `title` do HTML — esse é tooltip nativo, banido na casa, e o componente não o aceita. |
95
97
  | `description` | `React.ReactNode` | | A frase. Sozinha, dispensa título. |
@@ -29,3 +29,14 @@ render(
29
29
  ```
30
30
 
31
31
  Cada pergunta exige pelo menos uma opção válida selecionada **ou** texto livre não vazio. `busy` mostra o progresso no botão e trava os controles; `disabled` apenas trava a interação. A ordem de `answers` acompanha a ordem de `questions`, e o `header` de cada resposta é derivado de `question.header` (ou do próprio enunciado quando o header estiver vazio).
32
+
33
+ ## Propriedades de Ask
34
+
35
+ | Propriedade | Tipo | Padrão | Descrição |
36
+ |---|---|---|---|
37
+ | `questions` | `AskQuestion[]` | | Uma a quatro perguntas do contrato de elicitação; fora dessa faixa o envio é bloqueado. |
38
+ | `answers` | `AskAnswer[]` | | Respostas controladas, na mesma ordem de `questions`; entradas ausentes contam como vazias. |
39
+ | `onChange` | `(answers: AskAnswer[]) => void` | | Recebe a lista completa a cada alteração. |
40
+ | `onSubmit` | `(answers: AskAnswer[]) => void` | | Dispara somente quando toda pergunta tem uma opção ou texto livre. |
41
+ | `disabled` | `boolean` | `false` | Trava as opções e o envio. |
42
+ | `busy` | `boolean` | `false` | Envio em andamento: o botão mostra o spinner e desabilita. |
@@ -15,7 +15,7 @@ contêiner (o pai): a altura o componente deriva sozinho.
15
15
 
16
16
  ## Quadrado (1/1)
17
17
 
18
- ratio={1} trava num quadrado — o formato dos avatares de agente e dos ícones de skill, onde a
18
+ ratio={1} trava em um quadrado — o formato dos avatares de agente e dos ícones de skill, onde a
19
19
  moldura precisa ser previsível.
20
20
 
21
21
  ```tsx preview col-start
@@ -57,10 +57,10 @@ layout quando o preview carrega.
57
57
  </div>
58
58
  ```
59
59
 
60
- ## Props
60
+ ## Propriedades de AspectRatio
61
61
 
62
- | Prop | Tipo | Default | Descrição |
62
+ | Propriedade | Tipo | Padrão | Descrição |
63
63
  |---|---|---|---|
64
- | `ratio` | `number` | `1` | A razão largura/altura. 16/9 pra vídeo/preview, 1 pra quadrado, 4/3 pra clássico. |
64
+ | `ratio` | `number` | `1` | A razão largura/altura. 16/9 para vídeo/preview, 1 para quadrado, 4/3 para clássico. |
65
65
  | `children` | `React.ReactNode` | | O conteúdo a enquadrar (img, iframe, div) — preencha com h-full w-full e object-cover. |
66
66
  | `className` | `string` | | Estilo do bloco — borda, rounded e overflow-hidden moram aqui, não no filho. |
@@ -5,7 +5,7 @@ title: Auditoria
5
5
  # Auditoria
6
6
 
7
7
  Toda action executada vira um registro imutável: quem, o quê, quando, com que entrada e
8
- resultado. O runtime emite um `AuditRecord` por execução pro(s) sink(s) configurado(s) — de
8
+ resultado. O runtime emite um `AuditRecord` por execução para o(s) sink(s) configurado(s) — de
9
9
  graça, sem o handler pedir. O contrato é o `AuditSink`.
10
10
 
11
11
  ## O contrato
@@ -43,7 +43,7 @@ interface AuditRecord {
43
43
  import { consoleAudit } from '@softize/opus/audit/console'
44
44
  import { pgAudit } from '@softize/opus/audit/pg'
45
45
 
46
- // Dev: uma linha por action (pretty num TTY, json senão).
46
+ // Dev: uma linha por action (pretty em um TTY, json senão).
47
47
  const dev = consoleAudit()
48
48
 
49
49
  // Produção: grava na tabela audit_log (Postgres).
@@ -7,7 +7,7 @@ title: Autenticação
7
7
  Quem é o usuário, de qual tenant, e o que ele pode. O contrato é um adapter do core
8
8
  (`AuthAdapter`): a cada request o runtime chama `resolveContext` e injeta o resultado no
9
9
  handler — `ctx.user`, `ctx.tenantId`, `ctx.can`. O Opus **não** implementa RBAC/ABAC; ele
10
- delega a decisão pro `can` que o driver pluga.
10
+ delega a decisão para o `can` que o driver pluga.
11
11
 
12
12
  ## O contrato
13
13
 
@@ -31,7 +31,7 @@ política usada por ela.
31
31
  import { jwtAuth } from '@softize/opus/auth/jwt'
32
32
  import { betterAuthSession } from '@softize/opus/auth/better-auth'
33
33
 
34
- // JWT: valida o token (HS256 por padrão) e mapeia o payload pro User.
34
+ // JWT: valida o token (HS256 por padrão) e mapeia o payload para o User.
35
35
  const jwt = jwtAuth({ secret: process.env.JWT_SECRET! })
36
36
 
37
37
  // better-auth: valida a sessão contra um IdP better-auth remoto.
@@ -44,7 +44,7 @@ const idp = betterAuthSession({
44
44
 
45
45
  O `jwt` procura o token no `Authorization: Bearer`, em cookie ou custom (via `getToken`); o
46
46
  `secret` pode ser string ou um resolver async por `kid`. O `betterAuthSession` faz fetch da
47
- sessão no IdP (`timeoutMs`, default 5s) e mapeia pro `User`/tenant/can (o `can` default nega
47
+ sessão no IdP (`timeoutMs`, default 5s) e mapeia para o `User`/tenant/can (o `can` default nega
48
48
  tudo — plugue o seu).
49
49
 
50
50
  ## No runtime
@@ -1,4 +1,4 @@
1
- ## Básico
1
+ ## Retrato com imagem
2
2
 
3
3
  AvatarImage com src/alt e AvatarFallback com as iniciais — o fallback aparece enquanto a imagem
4
4
  carrega ou se ela falha.
@@ -12,8 +12,8 @@ carrega ou se ela falha.
12
12
 
13
13
  ## Fallback de iniciais
14
14
 
15
- Sem AvatarImage (ou com src quebrado), só o AvatarFallback renderiza — iniciais pra pessoa, ícone
16
- pra agente.
15
+ Sem AvatarImage (ou com src quebrado), só o AvatarFallback renderiza — iniciais para pessoa, ícone
16
+ para agente.
17
17
 
18
18
  ```tsx preview
19
19
  <Avatar>
@@ -28,7 +28,7 @@ pra agente.
28
28
 
29
29
  ## Tamanhos
30
30
 
31
- size sm/default/lg — o fallback acompanha o tamanho. sm pra listas densas, lg pra cabeçalho de
31
+ size sm/default/lg — o fallback acompanha o tamanho. sm para listas densas, lg para cabeçalho de
32
32
  workspace.
33
33
 
34
34
  ```tsx preview
@@ -45,8 +45,8 @@ workspace.
45
45
 
46
46
  ## Com selo de status
47
47
 
48
- AvatarBadge é o ponto no canto inferior — verde pro agente developer rodando uma sessão, cinza
49
- pro ocioso. Tinja com className.
48
+ AvatarBadge é o ponto no canto inferior — verde para o agente developer rodando uma sessão, cinza
49
+ para o ocioso. Tinja com className.
50
50
 
51
51
  ```tsx preview
52
52
  <Avatar>
@@ -82,13 +82,33 @@ excedente — os membros do workspace Empresa X.
82
82
  </AvatarGroup>
83
83
  ```
84
84
 
85
- ## Props
85
+ ## Propriedades de Avatar
86
86
 
87
- | Prop | Tipo | Default | Descrição |
87
+ | Propriedade | Tipo | Padrão | Descrição |
88
88
  |---|---|---|---|
89
- | `size` (Avatar) | `'sm' \| 'default' \| 'lg'` | `'default'` | Diâmetro do retrato o fallback e o selo acompanham. |
90
- | `src` (AvatarImage) | `string` | | URL da imagem. Enquanto carrega (ou se falha), o AvatarFallback fica no lugar. |
91
- | `alt` (AvatarImage) | `string` | | Texto alternativo da imagem — o nome da pessoa ou do agente. |
92
- | `children` (AvatarFallback) | `React.ReactNode` | | O que aparece sem imagem: iniciais (pessoa) ou ícone (agente). |
93
- | `className` (AvatarBadge) | `string` | | Selo no canto inferior — tinja o fundo (ex.: bg-emerald-500) pra refletir o status. |
94
- | `children` (AvatarGroupCount) | `React.ReactNode` | | O excedente da pilha (ex.: "+3"), fechando o AvatarGroup. |
89
+ | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Diâmetro do retrato; o fallback e o selo acompanham a escala. |
90
+
91
+ ## Propriedades de AvatarImage
92
+
93
+ | Propriedade | Tipo | Padrão | Descrição |
94
+ |---|---|---|---|
95
+ | `src` | `string` | | URL da imagem. Enquanto ela carrega ou quando falha, `AvatarFallback` ocupa o lugar. |
96
+ | `alt` | `string` | | Texto alternativo que identifica a pessoa ou o agente. |
97
+
98
+ ## Propriedades de AvatarFallback
99
+
100
+ | Propriedade | Tipo | Padrão | Descrição |
101
+ |---|---|---|---|
102
+ | `children` | `React.ReactNode` | | Iniciais ou ícone exibido quando a imagem não está disponível. |
103
+
104
+ ## Propriedades de AvatarBadge
105
+
106
+ | Propriedade | Tipo | Padrão | Descrição |
107
+ |---|---|---|---|
108
+ | `className` | `string` | | Classes usadas para comunicar visualmente o status no selo. |
109
+
110
+ ## Propriedades de AvatarGroupCount
111
+
112
+ | Propriedade | Tipo | Padrão | Descrição |
113
+ |---|---|---|---|
114
+ | `children` | `React.ReactNode` | | Quantidade excedente no fim do grupo, como `+3`. |
@@ -24,7 +24,7 @@ decisão em cada tela.
24
24
 
25
25
  ## Com ícone
26
26
 
27
- Um svg filho ganha size-3 automaticamente — bom pra reforçar o estado sem crescer o rótulo.
27
+ Um svg filho ganha size-3 automaticamente — bom para reforçar o estado sem crescer o rótulo.
28
28
 
29
29
  ```tsx preview
30
30
  <Badge context="success"><CircleCheck /> Regressão verde</Badge>
@@ -41,9 +41,9 @@ variantes.
41
41
  </Badge>
42
42
  ```
43
43
 
44
- ## Props
44
+ ## Propriedades de Badge
45
45
 
46
- | Prop | Tipo | Default | Descrição |
46
+ | Propriedade | Tipo | Padrão | Descrição |
47
47
  |---|---|---|---|
48
48
  | `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O significado ou destaque contextual. |
49
49
  | `variant` | `'solid' \| 'subtle' \| 'outline'` | `'subtle'` | O tratamento visual aplicado ao contexto. |
@@ -1,4 +1,4 @@
1
- ## Básico
1
+ ## Caminho atual
2
2
 
3
3
  A composição é manual: BreadcrumbLink nos níveis navegáveis, BreadcrumbPage no atual (não
4
4
  clicável, aria-current=page) e um BreadcrumbSeparator entre cada item.
@@ -23,7 +23,7 @@ clicável, aria-current=page) e um BreadcrumbSeparator entre cada item.
23
23
 
24
24
  ## Com ícone e separador custom
25
25
 
26
- O primeiro nível pode levar um ícone do lucide. BreadcrumbSeparator aceita children pra trocar o
26
+ O primeiro nível pode levar um ícone do lucide. BreadcrumbSeparator aceita children para trocar o
27
27
  chevron padrão por outro glifo (aqui, uma barra).
28
28
 
29
29
  ```tsx preview col-start
@@ -53,7 +53,7 @@ chevron padrão por outro glifo (aqui, uma barra).
53
53
 
54
54
  ## Colapsado
55
55
 
56
- Trilha funda demais pro espaço: BreadcrumbEllipsis substitui os níveis do meio (que viram um
56
+ Trilha funda demais para o espaço: BreadcrumbEllipsis substitui os níveis do meio (que viram um
57
57
  menu/popover) e mantém só a raiz e o destino.
58
58
 
59
59
  ```tsx preview col-start
@@ -78,10 +78,15 @@ menu/popover) e mantém só a raiz e o destino.
78
78
  </Breadcrumb>
79
79
  ```
80
80
 
81
- ## Props
81
+ ## Propriedades de BreadcrumbLink
82
82
 
83
- | Prop | Tipo | Default | Descrição |
83
+ | Propriedade | Tipo | Padrão | Descrição |
84
84
  |---|---|---|---|
85
- | `asChild` (BreadcrumbLink) | `boolean` | `false` | Funde as props no filho (via Slot) — use pra integrar o Link do seu roteador no lugar do `<a>` nativo. |
86
- | `href` (BreadcrumbLink) | `string` | | Destino do nível navegável — o que o consumidor decide por item. |
87
- | `children` (BreadcrumbSeparator) | `React.ReactNode` | `<ChevronRight />` | Glifo entre os itens. Omita pro chevron padrão ou passe outro ícone. |
85
+ | `asChild` | `boolean` | `false` | Repassa as propriedades ao filho para integrar o link do roteador no lugar de `<a>`. |
86
+ | `href` | `string` | | Destino do nível navegável. |
87
+
88
+ ## Propriedades de BreadcrumbSeparator
89
+
90
+ | Propriedade | Tipo | Padrão | Descrição |
91
+ |---|---|---|---|
92
+ | `children` | `React.ReactNode` | `<ChevronRight />` | Elemento entre os itens. Omita para usar o chevron padrão ou passe outro ícone. |
@@ -31,7 +31,7 @@ O tamanho icon exige `aria-label`, porque não há texto visível. Os botões s
31
31
 
32
32
  busy = ação em andamento (DEPOIS do clique): o Spinner e o disabled vêm do botão. Com icon, o
33
33
  Spinner TROCA o ícone (não soma). Não confunda com carregar conteúdo (ANTES) — isso é Spinner
34
- centralizado/Skeleton num nível de página.
34
+ centralizado/Skeleton em um nível de página.
35
35
 
36
36
  ```tsx preview
37
37
  <Button disabled>Desabilitado</Button>
@@ -43,7 +43,7 @@ centralizado/Skeleton num nível de página.
43
43
  ## Como outro elemento (asChild)
44
44
 
45
45
  Âncora com cara de botão: asChild renderiza o filho (Radix Slot) — sem forkar estilo.
46
- buttonVariants serve pro caso sem filho único.
46
+ buttonVariants serve para o caso sem filho único.
47
47
 
48
48
  ```tsx preview
49
49
  <Button asChild variant="outline">
@@ -51,13 +51,88 @@ buttonVariants serve pro caso sem filho único.
51
51
  </Button>
52
52
  ```
53
53
 
54
- ## Props
54
+ ## Propriedades de Button
55
55
 
56
- | Prop | Tipo | Default | Descrição |
56
+ | Propriedade | Tipo | Padrão | Descrição |
57
57
  |---|---|---|---|
58
58
  | `context` | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | A hierarquia ou o risco comunicado pela ação. |
59
59
  | `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'solid'` | O tratamento visual aplicado ao contexto. |
60
60
  | `size` | `'default' \| 'sm' \| 'lg' \| 'icon' \| 'icon-sm' \| 'icon-xs'` | `'default'` | O tamanho. Os icon* são quadrados (2.25/2/1.5rem) para botões só de ícone, com `aria-label`. |
61
- | `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — pra âncoras e afins. |
62
- | `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton num nível de página). |
61
+ | `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — para âncoras e afins. |
62
+ | `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton em um nível de página). |
63
63
  | `icon` | `React.ElementType` | | Ícone à esquerda (ex.: icon={Plus}). No busy é trocado pelo Spinner — não soma. |
64
+
65
+ ## ButtonGroup
66
+
67
+ Use `ButtonGroup` quando ações relacionadas precisarem formar um bloco contínuo. As bordas internas
68
+ colapsam e somente as pontas externas permanecem arredondadas. Mantenha a mesma variante nos filhos
69
+ para preservar a unidade visual.
70
+
71
+ ```tsx preview
72
+ <ButtonGroup>
73
+ <Button variant="outline">Visão geral</Button>
74
+ <Button variant="outline">Sessões</Button>
75
+ <Button variant="outline">Habilidades</Button>
76
+ </ButtonGroup>
77
+ ```
78
+
79
+ ### Ação dividida
80
+
81
+ Combine a ação principal, um separador e um botão de ícone quando o mesmo comando oferecer
82
+ variações.
83
+
84
+ ```tsx preview
85
+ <ButtonGroup>
86
+ <Button icon={Play}>Rodar agente developer</Button>
87
+ <ButtonGroupSeparator />
88
+ <Button size="icon" aria-label="Mais opções">
89
+ <ChevronDown />
90
+ </Button>
91
+ </ButtonGroup>
92
+ ```
93
+
94
+ ### Com rótulo
95
+
96
+ `ButtonGroupText` adiciona um contexto inerte ao grupo. Ele também aceita `asChild` para assumir a
97
+ semântica de outro elemento, como `label`.
98
+
99
+ ```tsx preview
100
+ <ButtonGroup>
101
+ <ButtonGroupText>
102
+ <GitBranch />
103
+ empresa-x-api
104
+ </ButtonGroupText>
105
+ <Button variant="outline" icon={RotateCw}>Sincronizar</Button>
106
+ </ButtonGroup>
107
+ ```
108
+
109
+ ### Vertical
110
+
111
+ `orientation="vertical"` empilha os filhos e transfere a junção das bordas para o eixo vertical.
112
+
113
+ ```tsx preview col-start
114
+ <ButtonGroup orientation="vertical">
115
+ <Button variant="outline" size="icon" aria-label="Rodar sessão"><Play /></Button>
116
+ <Button variant="outline" size="icon" aria-label="Pausar sessão"><Pause /></Button>
117
+ <Button variant="outline" size="icon" aria-label="Reiniciar sessão"><RotateCw /></Button>
118
+ </ButtonGroup>
119
+ ```
120
+
121
+ ### Propriedades de ButtonGroup
122
+
123
+ | Propriedade | Tipo | Padrão | Descrição |
124
+ |---|---|---|---|
125
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco e do colapso das bordas. |
126
+ | `shape` | `'default' \| 'pill'` | `'default'` | Geometria das extremidades externas do grupo. |
127
+
128
+ ### Propriedades de ButtonGroupSeparator
129
+
130
+ | Propriedade | Tipo | Padrão | Descrição |
131
+ |---|---|---|---|
132
+ | `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | Direção do traço divisor; use vertical em grupos horizontais. |
133
+
134
+ ### Propriedades de ButtonGroupText
135
+
136
+ | Propriedade | Tipo | Padrão | Descrição |
137
+ |---|---|---|---|
138
+ | `asChild` | `boolean` | `false` | Renderiza como o filho para assumir outra semântica sem perder o estilo. |
@@ -1,3 +1,7 @@
1
+ O calendário fala pt-BR por padrão: nomes de mês e de dia vêm do `locale` (`ptBR` de
2
+ `date-fns/locale`) e os controles de navegação têm rótulos acessíveis em português. Passe outro
3
+ `locale` para trocar o idioma; `labels` sobrescreve rótulos individualmente.
4
+
1
5
  ## Dia único
2
6
 
3
7
  mode=single guarda uma Date — controle por selected/onSelect. defaultMonth abre o calendário no mês certo sem mexer na seleção.
@@ -17,7 +21,7 @@ render(
17
21
 
18
22
  ## Intervalo
19
23
 
20
- mode=range guarda { from, to } — o padrão pra filtrar sessões por janela de datas. O primeiro clique fixa o início; o segundo, o fim.
24
+ mode=range guarda { from, to } — o padrão para filtrar sessões por janela de datas. O primeiro clique fixa o início; o segundo, o fim.
21
25
 
22
26
  ```tsx preview
23
27
  const [week, setWeek] = useState<DateRange | undefined>({
@@ -37,7 +41,7 @@ render(
37
41
 
38
42
  ## Navegação por dropdown
39
43
 
40
- captionLayout=dropdown troca o título do mês por seletores de mês e ano — bom pra pular pra um período distante (ex.: histórico de um repositório) sem clicar mês a mês.
44
+ captionLayout=dropdown troca o título do mês por seletores de mês e ano — bom para pular para um período distante (ex.: histórico de um repositório) sem clicar mês a mês.
41
45
 
42
46
  ```tsx preview
43
47
  <Calendar
@@ -47,16 +51,25 @@ captionLayout=dropdown troca o título do mês por seletores de mês e ano — b
47
51
  />
48
52
  ```
49
53
 
50
- ## Props
54
+ ## Propriedades de Calendar
51
55
 
52
- | Prop | Tipo | Default | Descrição |
56
+ | Propriedade | Tipo | Padrão | Descrição |
53
57
  |---|---|---|---|
54
58
  | `mode` | `'single' \| 'multiple' \| 'range'` | | O tipo de seleção — define o formato de selected/onSelect (Date, Date[] ou { from, to }). |
55
59
  | `selected` | `Date \| Date[] \| DateRange` | | A seleção atual no modo controlado — o formato segue o mode. Pareie com onSelect. |
56
60
  | `onSelect` | `(selected) => void` | | Chamado quando o usuário escolhe uma data. O argumento segue o mode. |
57
61
  | `defaultMonth` | `Date` | | O mês exibido ao montar, sem afetar a seleção. |
58
62
  | `captionLayout` | `'label' \| 'dropdown' \| 'dropdown-months' \| 'dropdown-years'` | `'label'` | Como o título do mês aparece — label é texto fixo; dropdown vira seletores de mês e ano. |
59
- | `numberOfMonths` | `number` | `1` | Quantos meses mostrar lado a lado — útil pra escolher um intervalo longo. |
63
+ | `numberOfMonths` | `number` | `1` | Quantos meses mostrar lado a lado — útil para escolher um intervalo longo. |
60
64
  | `disabled` | `Matcher` | | Datas não selecionáveis — uma Date, um array, um { from, to } ou um predicado (date) => boolean. |
61
65
  | `buttonVariant` | `Button['variant']` | `'ghost'` | A variante dos botões de navegação (anterior/próximo). |
62
66
  | `showOutsideDays` | `boolean` | `true` | Mostra os dias do mês vizinho que completam a primeira e a última semana. |
67
+ | `locale` | `Locale` | `ptBR` | Idioma dos nomes de mês e de dia (um locale do date-fns). |
68
+ | `labels` | `Partial<Labels>` | rótulos em pt-BR | Rótulos acessíveis da navegação e dos seletores; mescla sobre o padrão. |
69
+
70
+ ## CalendarDayButton
71
+
72
+ Cada dia é um `CalendarDayButton` — um `Button` ghost quadrado que recebe os modificadores do
73
+ dia (`selected`, `range-start`, `today`…). Use `components={{ DayButton: … }}` para
74
+ decorar o dia (um marcador de evento, por exemplo) partindo dele em vez de reimplementar o
75
+ foco e a seleção.
@@ -10,7 +10,7 @@ O uso mais comum: a superfície de conteúdo em repouso (`rounded-xl` + `border`
10
10
 
11
11
  ## Estruturado (header / conteúdo / rodapé)
12
12
 
13
- Pra painel com estrutura: cada slot é dono do próprio padding (como o Dialog). CardTitle/CardDescription no header; CardFooter alinha as ações.
13
+ Para painel com estrutura: cada slot é dono do próprio padding (como o Dialog). CardTitle/CardDescription no header; CardFooter alinha as ações.
14
14
 
15
15
  ```tsx preview col
16
16
  <Card>
@@ -47,3 +47,29 @@ Pra painel com estrutura: cada slot é dono do próprio padding (como o Dialog).
47
47
  </CardFooter>
48
48
  </Card>
49
49
  ```
50
+
51
+ ## Ação no cabeçalho
52
+
53
+ `CardAction` é o slot de ação do header (um botão ou menu). Quem compõe posiciona — em geral um
54
+ `CardHeader` em `flex-row` com o título de um lado e a ação do outro.
55
+
56
+ ```tsx preview col
57
+ <Card>
58
+ <CardHeader className="flex-row items-start justify-between">
59
+ <div>
60
+ <CardTitle>Empresa X</CardTitle>
61
+ <CardDescription>3 agentes vinculados.</CardDescription>
62
+ </div>
63
+ <CardAction>
64
+ <Button size="sm" variant="outline">Editar</Button>
65
+ </CardAction>
66
+ </CardHeader>
67
+ </Card>
68
+ ```
69
+
70
+ ## Propriedades de Card
71
+
72
+ | Propriedade | Tipo | Padrão | Descrição |
73
+ |---|---|---|---|
74
+ | `asChild` | `boolean` | `false` | Renderiza o filho com a superfície do Card (ex.: um `<button>` clicável inteiro). |
75
+ | `className` | `string` | | Compõe sobre a superfície; os slots (`CardHeader`, `CardBody`, `CardContent`, `CardFooter`, `CardAction`) são donos do próprio padding. |