@softize/opus 12.11.0 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/bin/lib/check.mjs +2 -7
  3. package/bin/lib/copy.mjs +1 -5
  4. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +93 -10
  5. package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
  6. package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
  7. package/docs/radius-scale.md +1 -1
  8. package/package.json +1 -1
  9. package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
  10. package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
  11. package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
  12. package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
  13. package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
  14. package/src/ui/components/patterns/confirm.tsx +140 -40
  15. package/src/ui/components/patterns/list.tsx +35 -40
  16. package/src/ui/components/patterns/page-state.tsx +2 -2
  17. package/src/ui/components/patterns/sidebar.tsx +26 -26
  18. package/src/ui/components/patterns/trigger.tsx +25 -22
  19. package/src/ui/components/primitives/alert.tsx +3 -3
  20. package/src/ui/components/primitives/dialog.tsx +196 -39
  21. package/src/ui/components/primitives/drawer.tsx +8 -5
  22. package/src/ui/components/primitives/empty.tsx +3 -3
  23. package/src/ui/components/primitives/item.tsx +3 -3
  24. package/src/ui/components/primitives/sonner.tsx +187 -8
  25. package/src/ui/docs/DocBrowser.tsx +102 -23
  26. package/src/ui/docs/content/accordion.md +22 -16
  27. package/src/ui/docs/content/action-form-card.md +8 -8
  28. package/src/ui/docs/content/action-form-dialog.md +9 -9
  29. package/src/ui/docs/content/action-form.md +28 -34
  30. package/src/ui/docs/content/action-list-dialog.md +11 -6
  31. package/src/ui/docs/content/action-list.md +64 -39
  32. package/src/ui/docs/content/action-trigger.md +21 -14
  33. package/src/ui/docs/content/action-view.md +8 -8
  34. package/src/ui/docs/content/actions.md +9 -9
  35. package/src/ui/docs/content/ai.md +3 -3
  36. package/src/ui/docs/content/alert.md +14 -12
  37. package/src/ui/docs/content/aspect-ratio.md +4 -4
  38. package/src/ui/docs/content/audit.md +2 -2
  39. package/src/ui/docs/content/auth.md +3 -3
  40. package/src/ui/docs/content/avatar.md +34 -14
  41. package/src/ui/docs/content/badge.md +3 -3
  42. package/src/ui/docs/content/breadcrumb.md +13 -8
  43. package/src/ui/docs/content/button.md +81 -6
  44. package/src/ui/docs/content/calendar.md +5 -5
  45. package/src/ui/docs/content/card.md +1 -1
  46. package/src/ui/docs/content/carousel.md +16 -11
  47. package/src/ui/docs/content/chat.md +3 -3
  48. package/src/ui/docs/content/checkbox.md +7 -7
  49. package/src/ui/docs/content/cli.md +5 -5
  50. package/src/ui/docs/content/collapsible.md +8 -8
  51. package/src/ui/docs/content/command.md +16 -8
  52. package/src/ui/docs/content/composer.md +2 -2
  53. package/src/ui/docs/content/content.md +2 -2
  54. package/src/ui/docs/content/copyable.md +4 -3
  55. package/src/ui/docs/content/customization.md +5 -5
  56. package/src/ui/docs/content/cycle.md +3 -3
  57. package/src/ui/docs/content/data-state.md +11 -12
  58. package/src/ui/docs/content/data.md +26 -33
  59. package/src/ui/docs/content/detail.md +3 -3
  60. package/src/ui/docs/content/dialog.md +339 -31
  61. package/src/ui/docs/content/dictionary-value.md +8 -8
  62. package/src/ui/docs/content/dock.md +3 -3
  63. package/src/ui/docs/content/drawer.md +27 -14
  64. package/src/ui/docs/content/empty-value.md +2 -2
  65. package/src/ui/docs/content/empty.md +19 -12
  66. package/src/ui/docs/content/events.md +4 -4
  67. package/src/ui/docs/content/field.md +34 -12
  68. package/src/ui/docs/content/getting-started.md +1 -1
  69. package/src/ui/docs/content/icon-picker.md +8 -4
  70. package/src/ui/docs/content/input-otp.md +20 -12
  71. package/src/ui/docs/content/input.md +121 -9
  72. package/src/ui/docs/content/item.md +27 -13
  73. package/src/ui/docs/content/kbd.md +19 -11
  74. package/src/ui/docs/content/label.md +5 -3
  75. package/src/ui/docs/content/log.md +4 -4
  76. package/src/ui/docs/content/markdown.md +7 -6
  77. package/src/ui/docs/content/mcp.md +13 -15
  78. package/src/ui/docs/content/menu.md +34 -16
  79. package/src/ui/docs/content/observability.md +2 -2
  80. package/src/ui/docs/content/page.md +51 -6
  81. package/src/ui/docs/content/pagination.md +22 -17
  82. package/src/ui/docs/content/popover.md +16 -8
  83. package/src/ui/docs/content/progress.md +7 -5
  84. package/src/ui/docs/content/queue.md +5 -5
  85. package/src/ui/docs/content/radio-group.md +20 -12
  86. package/src/ui/docs/content/router.md +11 -6
  87. package/src/ui/docs/content/scheduler.md +4 -5
  88. package/src/ui/docs/content/scroll-area.md +12 -7
  89. package/src/ui/docs/content/select.md +42 -29
  90. package/src/ui/docs/content/separator.md +5 -5
  91. package/src/ui/docs/content/sidebar.md +323 -54
  92. package/src/ui/docs/content/skeleton.md +3 -2
  93. package/src/ui/docs/content/slider.md +8 -7
  94. package/src/ui/docs/content/spinner.md +8 -8
  95. package/src/ui/docs/content/split.md +8 -5
  96. package/src/ui/docs/content/storage.md +6 -8
  97. package/src/ui/docs/content/switch.md +8 -7
  98. package/src/ui/docs/content/table.md +13 -3
  99. package/src/ui/docs/content/tabs.md +28 -14
  100. package/src/ui/docs/content/testing.md +9 -11
  101. package/src/ui/docs/content/textarea.md +5 -4
  102. package/src/ui/docs/content/toast.md +47 -13
  103. package/src/ui/docs/content/toggle.md +75 -7
  104. package/src/ui/docs/content/tokens.md +3 -3
  105. package/src/ui/docs/content/tooltip.md +19 -11
  106. package/src/ui/docs/content/truncate.md +7 -8
  107. package/src/ui/docs/content/ui.md +10 -9
  108. package/src/ui/docs/content/upgrading.md +7 -8
  109. package/src/ui/docs/registry.tsx +20 -37
  110. package/src/ui/meta.ts +64 -94
  111. package/src/ui/react.tsx +15 -16
  112. package/src/ui/theme.css +50 -0
  113. package/src/ui/components/primitives/alert-dialog.tsx +0 -192
  114. package/src/ui/docs/content/alert-dialog.md +0 -73
  115. package/src/ui/docs/content/button-group.md +0 -71
  116. package/src/ui/docs/content/confirm.md +0 -120
  117. package/src/ui/docs/content/input-group.md +0 -79
  118. package/src/ui/docs/content/page-state.md +0 -45
  119. package/src/ui/docs/content/toggle-group.md +0 -81
@@ -1,6 +1,12 @@
1
- ## Declarativo pelo contrato (a diagramação da casa)
1
+ ## Listagem derivada do contrato
2
2
 
3
- O contrato descreve, a UI deriva — zero configuração no call site. `columns` vira a tabela emoldurada (o datagrid da casa; tipos text/number/date/badge, `fit`, `hidden`, headers ordenáveis que escrevem `sort: 'chave:dir'` no input); `filters` vira a toolbar (os não-avançados inline, os `advanced: true` no modal "Filtros" com contador); `text` liga a busca (param `q`); filtros do modal aplicados viram chips removíveis. A linha é RESPONSIVA: a busca tem largura auto (encolhe primeiro) e filtro inline que não cabe migra pro modal — medição real, re-avaliada no resize; só no caso extremo (nada mais a ceder) a linha quebra. O handler implementa o que o input diz (orderBy, where) — mesmo modelo do resto do Opus.
3
+ Use `ActionList` para apresentar uma coleção pesquisável descrita por uma `ListAction`. O contrato
4
+ define colunas, busca, filtros, período, ordenação e paginação; a interface materializa esses
5
+ recursos e envia o estado correspondente no input. Filtros avançados aparecem no modal “Filtros” e
6
+ os filtros aplicados permanecem visíveis como chips removíveis.
7
+
8
+ A barra se adapta ao espaço disponível. A busca cede largura primeiro e filtros que deixam de caber
9
+ migram para o modal; a linha só quebra quando nenhum controle restante puder ceder espaço.
4
10
 
5
11
  ```tsx preview col
6
12
  render(
@@ -28,18 +34,16 @@ superfícies que não são listagens, como relatórios. O estado é controlado p
28
34
  />
29
35
  ```
30
36
 
31
- Selects inline têm largura fixa e previsível (`w-40`; lookup e múltiplo `w-52`): nem a label nem
32
- o valor selecionado alargam o controle o rótulo trunca e a opção inteira continua na lista. No
33
- modal de filtros avançados, o select ocupa a largura toda. Busca e filtros são renderizados
34
- somente quando declarados. Períodos incluem os presets do
35
- contrato e o intervalo personalizado no mesmo calendário usado pela `ActionList`.
37
+ Seletores inline mantêm largura previsível (`w-40`; lookup e múltiplo usam `w-52`). Rótulos longos
38
+ são truncados no controle, mas permanecem completos na lista. No modal de filtros avançados, o
39
+ seletor ocupa toda a largura. Busca, filtros e período aparecem somente quando declarados.
36
40
 
37
41
  ## Colunas de dicionário
38
42
 
39
- Coluna cujo campo no schema de saída é um `t.dict().zod()` renderiza `DictionaryValue` com a
40
- apresentação que o dicionário declarou (`presentation`: classificação em badge `outline`, status
41
- e estágio em badge tonal, `plain` ou ausente como texto). Nada a configurar na tela. Quando o
42
- campo de saída é uma string comum, a coluna nomeia o dicionário registrado no provider:
43
+ Quando o campo de saída usa `t.dict().zod()`, a coluna apresenta o valor com `DictionaryValue` e
44
+ respeita o papel declarado pelo dicionário. Classificações usam badge `outline`; status e estágio
45
+ usam badge tonal; `plain` e papéis ausentes permanecem como texto. Para um campo `string`, a coluna
46
+ pode indicar um dicionário registrado no provider:
43
47
  `{ key: 'source', label: 'Fonte', dictionary: 'customerSource' }`. Dimensões independentes, como
44
48
  tipo e estágio, ficam em colunas distintas — a leitura de comparação depende disso. `type:
45
49
  'badge'` continua sendo o chip `outline` legado; com dicionário, ele mostra o rótulo em vez do
@@ -56,7 +60,8 @@ Renderer customizado reutiliza `EmptyValue`.
56
60
 
57
61
  ## Células custom (cells)
58
62
 
59
- As colunas do contrato descrevem DADOS; apresentação especial entra por cima com `cells` (chave = key da coluna). É onde vivem links, composições e a coluna de ações; valor de dicionário já resolve sozinho pela coluna, sem `cells`.
63
+ Use `cells` somente quando uma coluna precisar de apresentação própria, como link, composição ou
64
+ ação. A chave corresponde à `key` da coluna. Valores de dicionário não precisam desse override.
60
65
 
61
66
  ```tsx preview col
62
67
  render(
@@ -78,9 +83,12 @@ render(
78
83
  )
79
84
  ```
80
85
 
81
- ## Período (o campo dinâmico)
86
+ ## Período obrigatório
82
87
 
83
- Com `periods` no contrato, a toolbar ganha o controle de período: um popover com os presets e, no "Personalizado", o calendário de range ali mesmo. Período é recorte OBRIGATÓRIO do caso de uso: o default (o preset com `default: true`, ou o primeiro) já vem aplicado e não existe "sem período". O preset fica RELATIVO na URL (`?period=last7`; o default é omitido); no custom o range vai direto no param (`?period=2026-07-01..2026-07-07` — o "custom" é implícito). No fetch, o pattern materializa o range nos params `from`/`to` — o handler implementa o recorte.
88
+ Quando o contrato declara `periods`, a listagem sempre possui um recorte temporal. O preset marcado
89
+ com `default: true`, ou o primeiro da coleção, começa aplicado. Presets usam um valor relativo na
90
+ URL, como `?period=last7`; o padrão é omitido. Um intervalo personalizado usa diretamente as datas,
91
+ como `?period=2026-07-01..2026-07-07`, e chega ao handler pelos parâmetros `from` e `to`.
84
92
 
85
93
  ```tsx
86
94
  periods: [
@@ -91,13 +99,20 @@ periods: [
91
99
  // Presets computáveis: today · yesterday · last7 · last30 · thisMonth · lastMonth.
92
100
  ```
93
101
 
94
- ## Paginação e loading
102
+ ## Paginação e carregamento
95
103
 
96
- O pattern manda `limit` (= `pageSize`, default 50) e `page` no input; o handler implementa o OFFSET e devolve `total` no Paginated. O rodapé (Página X de Y · páginas numeradas com reticências · "N itens no total") compõe a primitiva `Pagination` na escala densa — números com largura mínima que cresce com os dígitos, setas quadradas, nome acessível por página — e aparece sempre que há total; mudar filtro/busca/sort/período volta pra página 1 (a página vive na URL: `?page=2`). No primeiro carregamento, `DataState` centraliza o spinner; no refetch com a lista já na tela, o corpo esmaece (`aria-busy`) até os dados chegarem.
104
+ `ActionList` envia `limit`, definido por `pageSize`, e `page` no input. O handler aplica o recorte e
105
+ devolve `total` na resposta paginada. Quando existe total, o rodapé mostra a página atual, os links
106
+ disponíveis e a quantidade de itens. Alterar busca, filtro, ordenação ou período retorna à primeira
107
+ página. Durante a primeira consulta, `DataState` apresenta o carregamento; em atualizações
108
+ posteriores, a lista permanece visível com `aria-busy`.
97
109
 
98
- ## Multi-seleção com can (batch)
110
+ ## Ações em lote
99
111
 
100
- `batch` liga a coluna de checkbox na tabela. O `can(item)` é a fonte única de elegibilidade: linha inelegível não marca, o "selecionar todos" pega só os elegíveis, o botão mostra quantos dos selecionados valem e o `run` recebe apenas esses. Com `confirm`, um dialog pede confirmação; ao resolver, a seleção limpa e a lista refaz.
112
+ `batch` acrescenta seleção à tabela. `can(item)` determina quais linhas podem participar: itens
113
+ inelegíveis não são selecionáveis, “Selecionar todos” inclui somente os elegíveis e `run` recebe
114
+ apenas essa seleção. Com `confirm`, o componente pede confirmação antes de executar. Ao concluir, a
115
+ seleção é limpa e a consulta é refeita.
101
116
 
102
117
  ```tsx
103
118
  <ActionList action={runList} input={{}}
@@ -110,9 +125,12 @@ O pattern manda `limit` (= `pageSize`, default 50) e `page` no input; o handler
110
125
  />
111
126
  ```
112
127
 
113
- ## Views (a mesma listagem, outro renderer)
128
+ ## Outras visualizações
114
129
 
115
- Nem toda listagem é tabela: board, galeria, lista, calendário A view é APRESENTAÇÃO de quem chama (`render`); o pattern dá o segment na toolbar (ToggleGroup, ICON-ONLY: declare `icon` — o label vira title/aria; sem ícone cai pro texto), o estado (`view` na URL) e os dados — a mesma fonte, os mesmos filtros. A tabela ('Tabela') participa quando há columns.
130
+ Uma coleção também pode aparecer como quadro, galeria, lista ou calendário. Cada entrada de `views`
131
+ fornece um `render`; `ActionList` preserva a mesma fonte de dados, filtros e estado na URL. Declare
132
+ `icon` para controles somente com ícone; sem ele, o rótulo permanece visível. A visualização
133
+ “Tabela” aparece quando existem colunas.
116
134
 
117
135
  ```tsx preview col
118
136
  render(
@@ -146,13 +164,16 @@ render(
146
164
  )
147
165
  ```
148
166
 
149
- ## Filtros dependentes, dictionary e lookup
167
+ ## Filtros dependentes e opções remotas
150
168
 
151
- Três recursos do FilterSpec que a toolbar honra:
169
+ `FilterSpec` também cobre dependências, dicionários e consultas remotas:
152
170
 
153
- - **`depends: ['outroFiltro']`** o filtro dependente fica DESABILITADO até o pai ter valor, e mudar o pai limpa o dependente em cascata (o recorte perde o sentido quando o pai muda). Vale inline e no modal.
154
- - **`options: { kind: 'dictionary', ref: 'sessionStatus' }`** — as opções vêm de um DICT registrado no `<TbdlibProvider dicts={{ sessionStatus: statusDict }}>` (o `DictType` do `t.dict` encaixa direto). Labels do vocabulário no filtro E nos chips; runtime (`filterOptions`) sobrepõe.
155
- - **`options: { kind: 'lookup', source: 'x.lookup' }`** as opções vêm de uma ACTION (server-side): o campo vira Select typeahead que chama a `source` (debounced) com `{ q }` — e com os valores dos `depends` no input. Convenção: a action de lookup devolve itens `{ value, label }`. Com valor aplicado mas opções ainda não carregadas (URL, chip), o chip mostra o próprio value.
171
+ - **`depends: ['outroFiltro']`:** desabilita o filtro até que suas dependências tenham valor e o
172
+ limpa quando uma delas muda.
173
+ - **`options: { kind: 'dictionary', ref: 'sessionStatus' }`:** resolve opções e rótulos por um
174
+ dicionário registrado no `TbdlibProvider`. `filterOptions` substitui essa fonte quando informado.
175
+ - **`options: { kind: 'lookup', source: 'x.lookup' }`:** consulta uma action com `{ q }` e os valores
176
+ das dependências. A resposta segue o formato `{ value, label }`.
156
177
 
157
178
  ```tsx
158
179
  filters: {
@@ -166,9 +187,12 @@ filters: {
166
187
  }
167
188
  ```
168
189
 
169
- ## Exibição e URL sync
190
+ ## Exibição e estado na URL
170
191
 
171
- O botão de exibição (engrenagem) abre o popover de configuração da listagem — presente em QUALQUER view: colunas (liga/desliga, incluindo as `hidden` do contrato; só com a tabela ativa) e itens por página (o default do caller fica fora da URL) — e é a casa do que vier depois. E o estado inteiro da toolbar (q, sort, filtros, view, colunas, limit) serializa pra querystring com os helpers padrão:
192
+ O botão de exibição abre as preferências da listagem em qualquer visualização. Na tabela, permite
193
+ mostrar ou ocultar colunas, inclusive as declaradas com `hidden`; em todas as visualizações, permite
194
+ alterar a quantidade de itens por página. Busca, ordenação, filtros, visualização, colunas e limite
195
+ podem ser serializados na query string pelos helpers do componente:
172
196
 
173
197
  ```tsx
174
198
  <ActionList
@@ -181,9 +205,10 @@ O botão de exibição (engrenagem) abre o popover de configuração da listagem
181
205
 
182
206
  Convenção compacta (defaults omitidos): `?q=…&sort=chave:dir&status=…&view=board&cols=a,b,c&limit=25`.
183
207
 
184
- ## Composição (layout livre)
208
+ ## Composição dos itens
185
209
 
186
- Com children, a tabela sai de cena e o layout dos itens é seu (cards, grid, o que for) — a toolbar declarativa e os estados (skeleton/erro/vazio) seguem daqui. Mesmo princípio do ActionForm com children.
210
+ Passe `children` para substituir a tabela por uma composição própria. A barra de filtros e os
211
+ estados de carregamento, erro e vazio continuam sendo tratados por `ActionList`.
187
212
 
188
213
  ```tsx preview col
189
214
  render(
@@ -209,30 +234,30 @@ render(
209
234
  )
210
235
  ```
211
236
 
212
- ## No contrato (ListAction)
237
+ ## Declaração na ListAction
213
238
 
214
239
  | Chave | O que declara |
215
240
  |---|---|
216
241
  | `columns` | `{ key, label, type?, sortable?, fit?, hidden?, dateFormat?, dictionary?, empty? }` — a tabela. `hidden` fica fora (base do futuro column picker); `dictionary` nomeia o dicionário do provider; `empty` dá o significado da ausência. |
217
- | `filters` | `{ [nome]: { label, type, options?, multiple?, advanced?, depends?, … } }` — a toolbar. `advanced` vai pro modal; `depends` desabilita/cascateia; options `kind: 'lookup'` busca numa action. Valor aplicado entra no input com o MESMO nome. |
242
+ | `filters` | `{ [nome]: { label, type, options?, multiple?, advanced?, depends?, … } }` — a toolbar. `advanced` vai para o modal; `depends` desabilita/cascateia; options `kind: 'lookup'` busca em uma action. Valor aplicado entra no input com o mesmo nome. |
218
243
  | `text` | `{ fields }` — liga a busca; convenção: param `q` no input. Com período/filtros no contrato ela fica à direita; sendo a ÚNICA forma de recorte, abre a linha. |
219
244
  | `sort` | `{ fields, default }` — ordenação inicial; header ordenável escreve `sort: 'chave:dir'`. |
220
245
  | `periods` | `{ value, label }[]` — o controle de período (presets + Personalizado com calendário); materializa em `from`/`to` no input. |
221
246
 
222
- ## Props
247
+ ## Propriedades de ActionList
223
248
 
224
- | Prop | Tipo | Default | Descrição |
249
+ | Propriedade | Tipo | Padrão | Descrição |
225
250
  |---|---|---|---|
226
251
  | `action` | `ListAction<TInput, TItem>` | | A ListAction do Opus (kind list, output = item, paginate cursor). |
227
252
  | `input` | `TInput` | | O ESCOPO BASE (ex.: { workspaceId }) — a toolbar soma por cima, nunca sobrescreve. |
228
253
  | `cells` | `Record<string, (item) => ReactNode>` | | Células custom por cima das colunas do contrato (chave = column.key). |
229
- | `filterOptions` | `Record<string, SelectOption[]>` | | Opções de runtime pros filtros select/lookup (chave = nome do filtro). |
254
+ | `filterOptions` | `Record<string, SelectOption[]>` | | Opções de runtime para os filtros select/lookup (chave = nome do filtro). |
230
255
  | `columns` | `ActionListColumn<TItem>[]` | | Tabela EXPLÍCITA — sobrepõe as colunas do contrato (escape hatch). |
231
- | `children` | `(items, refetch) => ReactNode` | | Modo COMPOSIÇÃO: layout livre; a toolbar segue. Tem precedência sobre columns. |
256
+ | `children` | `(items, refetch) => ReactNode` | | Modo composição: layout livre; a toolbar segue. Tem precedência sobre columns. |
232
257
  | `batch` | `ActionListBatchAction[]` | | Ações em lote: `{ label, can?, run, confirm?, destructive? }` — liga a multi-seleção. |
233
- | `rowId` | `(item) => string` | `item.id` | Identidade da linha pra seleção. |
234
- | `pageSize` | `number` | `50` | Itens por página DEFAULT (vira `limit`/`page` no input; handler devolve `total`). O usuário troca no popover de exibição. |
235
- | `state / onStateChange` | `ActionListState / (next) => void` | interno | Estado da toolbar controlado — pra quem embala sincronizar com a URL. |
258
+ | `rowId` | `(item) => string` | `item.id` | Identidade da linha para seleção. |
259
+ | `pageSize` | `number` | `50` | Itens por página padrão (vira `limit`/`page` no input; handler devolve `total`). O usuário troca no popover de exibição. |
260
+ | `state / onStateChange` | `ActionListState / (next) => void` | interno | Estado da toolbar controlado — para quem embala sincronizar com a URL. |
236
261
  | `emptyMessage` | `string` | `'Nenhum resultado.'` | O texto do estado vazio. |
237
- | `onRowClick` | `(item: TItem) => void` | | Clique na linha (ex.: navegar pro detalhe) — só na tabela. |
262
+ | `onRowClick` | `(item: TItem) => void` | | Clique na linha (ex.: navegar para o detalhe) — só na tabela. |
238
263
  | `rowActions` | `(item: TItem) => ReactNode` | | Ações por linha (coluna final, à direita) — apresentação de quem chama, como `cells`; cliques ali não disparam o `onRowClick`. |
@@ -1,7 +1,8 @@
1
- ## Disparo direto
1
+ ## Executar uma action
2
2
 
3
- Sem confirm, dispara no clique: desabilita enquanto roda e toca o toast do contrato
4
- (messages.success/error). O label default vem do action.label.
3
+ Use `ActionTrigger` para executar uma `SimpleAction` a partir de um botão. Sem confirmação, o clique
4
+ inicia a operação, desabilita o controle durante a execução e apresenta as mensagens do contrato em
5
+ um toast. O rótulo padrão vem de `action.label`.
5
6
 
6
7
  ```tsx preview
7
8
  <DocBrowserActionProvider>
@@ -11,8 +12,9 @@ Sem confirm, dispara no clique: desabilita enquanto roda e toca o toast do contr
11
12
 
12
13
  ## Confirmação declarada no contrato
13
14
 
14
- O ConfirmSpec mora na action (title/message/destructive) o pattern monta o Dialog sozinho e o
15
- destructive tonaliza o botão. Prop confirm sobrepõe quando a tela precisar de outro texto.
15
+ Quando a action declara `confirm`, o componente monta o diálogo e aplica o contexto `danger` se a
16
+ operação for destrutiva. Use a propriedade `confirm` somente quando esta ocorrência precisar de uma
17
+ mensagem diferente da declarada no contrato.
16
18
 
17
19
  ```tsx preview
18
20
  <DocBrowserActionProvider>
@@ -33,9 +35,11 @@ confirm: {
33
35
  <ActionTrigger action={sessionDeleteContract} input={{ id: session.id }} />
34
36
  ```
35
37
 
36
- ## A ação que mora no item (icon)
38
+ ## Ação somente com ícone
37
39
 
38
- Com `icon`, o botão vira icon-only: o `label` (ou o `action.label`) migra pro tooltip e pro `aria-label`, o visual fica discreto (ghost, e vermelho no hover quando o contrato marca `destructive`) e o clique **não vaza** pro item em volta — é o que permite viver dentro de uma linha ou card clicável. `itemLabel` nomeia o alvo na pergunta.
40
+ Com `icon`, o botão exibe somente o ícone e usa `label`, ou `action.label`, no tooltip e no nome
41
+ acessível. O clique não aciona o item clicável ao redor. `itemLabel` identifica o registro na
42
+ mensagem de confirmação.
39
43
 
40
44
  ```tsx
41
45
  <ActionTrigger
@@ -47,19 +51,22 @@ Com `icon`, o botão vira icon-only: o `label` (ou o `action.label`) migra pro t
47
51
  />
48
52
  ```
49
53
 
50
- > Isto absorveu o antigo `DeleteButton` (7.0.0), que era exatamente este componente com uma lixeira dentro. Se você tinha `<DeleteButton action input itemLabel />`, troque por `<ActionTrigger … icon={<Trash2 />} />` — **e confira se o contrato declara `confirm`**: sem ele o disparo é direto, e o DeleteButton perguntava sempre.
54
+ > `ActionTrigger` substitui o antigo `DeleteButton`. Ao migrar, declare `confirm` no contrato para
55
+ > preservar a confirmação que o componente anterior sempre apresentava.
51
56
 
52
57
  ## Erro que a pessoa entende
53
58
 
54
- Quando a action falha, a frase do SERVIDOR vence o rótulo do contrato se o erro for de negócio — `conflict`, `validation`, `not_found`:
59
+ Quando a action falha, uma mensagem do servidor substitui o texto genérico somente nos erros de
60
+ negócio `conflict`, `validation` e `not_found`:
55
61
 
56
62
  > Este agente tem 3 conversas — desabilite em vez de excluir.
57
63
 
58
- Isso é acionável. Erro inesperado carrega texto técnico cru: sem a allowlist, uma violação de FK viraria `violates foreign key constraint "user_unit_id_fkey"` no toast de quem só clicou num botão. Por isso allowlist e não denylist — categoria desconhecida cai no rótulo genérico, que é o lado seguro de errar.
64
+ Erros inesperados mantêm a mensagem genérica do contrato, evitando expor detalhes de banco ou
65
+ infraestrutura. O servidor registra o detalhe técnico para diagnóstico.
59
66
 
60
- ## Props
67
+ ## Propriedades de ActionTrigger
61
68
 
62
- | Prop | Tipo | Default | Descrição |
69
+ | Propriedade | Tipo | Padrão | Descrição |
63
70
  |---|---|---|---|
64
71
  | `action` | `SimpleContract<TInput, TData>` | | A SimpleAction do Opus — label, messages e confirm vêm do contrato. |
65
72
  | `input` | `TInput` | | O que a action recebe — geralmente { id }. |
@@ -67,6 +74,6 @@ Isso é acionável. Erro inesperado carrega texto técnico cru: sem a allowlist,
67
74
  | `variant / size` | `variants do Button` | `'default' (ou 'destructive' se o confirm declarar) / 'default'` | Visual do botão — o destructive do ConfirmSpec já escolhe sozinho. |
68
75
  | `confirm` | `{ title, description?, actionLabel?, cancelLabel? }` | | Confirmação via prop — sobrepõe o ConfirmSpec do contrato. |
69
76
  | `onSuccess` | `(data: TData) => void` | | Pós-sucesso (cache já invalidado pelo action.invalidates). |
70
- | `icon` | `React.ReactNode` | | Torna o botão icon-only: rótulo no tooltip e no `aria-label`, clique que não vaza pro item. |
77
+ | `icon` | `React.ReactNode` | | Torna o botão icon-only: rótulo no tooltip e no `aria-label`, clique que não vaza para o item. |
71
78
  | `itemLabel` | `string` | | Nome do alvo na pergunta (sai entre aspas, em destaque, antes da mensagem do contrato). |
72
- | `className` | `string` | | Classes do botão (ex.: apertar o tamanho numa linha densa). |
79
+ | `className` | `string` | | Classes do botão (ex.: apertar o tamanho em uma linha densa). |
@@ -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,13 @@ render(
35
35
  )
36
36
  ```
37
37
 
38
- ## Props
38
+ ## Propriedades de ActionView
39
39
 
40
- | Prop | Tipo | Default | Descrição |
40
+ | Propriedade | Tipo | Padrão | Descrição |
41
41
  |---|---|---|---|
42
42
  | `action` | `ViewAction<TInput, TData>` | | A ViewAction do Opus (kind view, 1 recurso). |
43
43
  | `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. |
44
+ | `children` | `(data: TData, refetch) => ReactNode` | | Conteúdo apresentado quando os dados estão disponíveis. `refetch` permite recarregar por código. |
45
+ | `render` | `(data: TData, refetch) => ReactNode` | | Alias de compatibilidade de `children`; `children` tem precedência. |
46
46
  | `loading / empty` | `ReactNode` | `3 skeletons / nada` | Sobrescreve os estados padrão quando o contexto pedir. |
47
47
  | `error` | `(err, retry) => ReactNode` | `mensagem + Tentar de novo` | Sobrescreve o estado de erro padrã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. |
@@ -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`. |