@softize/opus 19.0.0 → 20.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 (148) hide show
  1. package/CHANGELOG.md +239 -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/0020-icon-tiles-are-explicitly-composed.md +8 -0
  7. package/docs/adr/0021-content-carries-structure-and-semantic-element.md +7 -0
  8. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +19 -1
  9. package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
  10. package/docs/adr/0025-content-frame-treatment-does-not-change-semantics.md +134 -0
  11. package/docs/data-products.md +15 -9
  12. package/docs/protocol.md +3 -4
  13. package/docs/releasing.md +2 -2
  14. package/package.json +2 -2
  15. package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
  16. package/registry/templates/app/package.json +1 -1
  17. package/src/core/runtime.ts +96 -8
  18. package/src/core/types.ts +5 -2
  19. package/src/ui/components/patterns/action-form-card.tsx +11 -1
  20. package/src/ui/components/patterns/content-header.tsx +109 -18
  21. package/src/ui/components/patterns/fill-region.ts +150 -0
  22. package/src/ui/components/patterns/list.tsx +91 -92
  23. package/src/ui/components/patterns/page.tsx +20 -4
  24. package/src/ui/components/patterns/presentation.tsx +52 -37
  25. package/src/ui/components/patterns/split.tsx +44 -4
  26. package/src/ui/components/patterns/surface-anatomy.ts +35 -8
  27. package/src/ui/components/patterns/surface-header.tsx +58 -25
  28. package/src/ui/components/patterns/trigger.tsx +12 -2
  29. package/src/ui/components/primitives/alert.tsx +9 -1
  30. package/src/ui/components/primitives/avatar.tsx +7 -3
  31. package/src/ui/components/primitives/card.tsx +1 -1
  32. package/src/ui/components/primitives/carousel.tsx +34 -5
  33. package/src/ui/components/primitives/checkbox.tsx +11 -3
  34. package/src/ui/components/primitives/command.tsx +2 -1
  35. package/src/ui/components/primitives/copyable.tsx +120 -10
  36. package/src/ui/components/primitives/dialog.tsx +42 -11
  37. package/src/ui/components/primitives/drawer.tsx +10 -2
  38. package/src/ui/components/primitives/empty.tsx +1 -1
  39. package/src/ui/components/primitives/icon-picker.tsx +6 -2
  40. package/src/ui/components/primitives/icon-tile.tsx +7 -0
  41. package/src/ui/components/primitives/input-group.tsx +9 -2
  42. package/src/ui/components/primitives/input-otp.tsx +1 -1
  43. package/src/ui/components/primitives/kbd.tsx +1 -1
  44. package/src/ui/components/primitives/label.tsx +3 -0
  45. package/src/ui/components/primitives/menu.tsx +5 -0
  46. package/src/ui/components/primitives/popover.tsx +1 -1
  47. package/src/ui/components/primitives/progress.tsx +9 -1
  48. package/src/ui/components/primitives/slider.tsx +62 -11
  49. package/src/ui/components/primitives/truncate.tsx +10 -1
  50. package/src/ui/docs/content/accordion.md +74 -27
  51. package/src/ui/docs/content/action-form-card.md +24 -15
  52. package/src/ui/docs/content/action-form-dialog.md +17 -7
  53. package/src/ui/docs/content/action-form.md +92 -77
  54. package/src/ui/docs/content/action-list-dialog.md +75 -38
  55. package/src/ui/docs/content/action-list.md +141 -58
  56. package/src/ui/docs/content/action-trigger.md +27 -10
  57. package/src/ui/docs/content/action-view.md +14 -14
  58. package/src/ui/docs/content/actions.md +161 -66
  59. package/src/ui/docs/content/ai.md +43 -44
  60. package/src/ui/docs/content/alert.md +83 -29
  61. package/src/ui/docs/content/ask.md +51 -26
  62. package/src/ui/docs/content/aspect-ratio.md +22 -24
  63. package/src/ui/docs/content/audit.md +23 -19
  64. package/src/ui/docs/content/auth.md +38 -19
  65. package/src/ui/docs/content/avatar.md +54 -5
  66. package/src/ui/docs/content/badge.md +29 -9
  67. package/src/ui/docs/content/breadcrumb.md +21 -8
  68. package/src/ui/docs/content/button.md +73 -43
  69. package/src/ui/docs/content/calendar.md +134 -26
  70. package/src/ui/docs/content/card.md +41 -15
  71. package/src/ui/docs/content/carousel.md +37 -6
  72. package/src/ui/docs/content/chat.md +118 -37
  73. package/src/ui/docs/content/checkbox.md +73 -7
  74. package/src/ui/docs/content/cli.md +94 -55
  75. package/src/ui/docs/content/collapsible.md +43 -19
  76. package/src/ui/docs/content/command.md +30 -8
  77. package/src/ui/docs/content/composer.md +23 -8
  78. package/src/ui/docs/content/content.md +119 -27
  79. package/src/ui/docs/content/copyable.md +39 -8
  80. package/src/ui/docs/content/customization.md +55 -46
  81. package/src/ui/docs/content/cycle.md +33 -18
  82. package/src/ui/docs/content/data-products.md +115 -0
  83. package/src/ui/docs/content/data-state.md +20 -6
  84. package/src/ui/docs/content/data.md +20 -19
  85. package/src/ui/docs/content/detail.md +12 -5
  86. package/src/ui/docs/content/dialog.md +107 -24
  87. package/src/ui/docs/content/dictionary-value.md +4 -4
  88. package/src/ui/docs/content/dock.md +54 -19
  89. package/src/ui/docs/content/dot.md +35 -5
  90. package/src/ui/docs/content/drawer.md +63 -19
  91. package/src/ui/docs/content/empty-value.md +20 -6
  92. package/src/ui/docs/content/empty.md +24 -3
  93. package/src/ui/docs/content/events.md +76 -16
  94. package/src/ui/docs/content/executions.md +9 -9
  95. package/src/ui/docs/content/field.md +21 -6
  96. package/src/ui/docs/content/getting-started.md +44 -34
  97. package/src/ui/docs/content/icon-picker.md +19 -13
  98. package/src/ui/docs/content/icon-tile.md +38 -15
  99. package/src/ui/docs/content/input-otp.md +58 -7
  100. package/src/ui/docs/content/input.md +22 -3
  101. package/src/ui/docs/content/item.md +53 -6
  102. package/src/ui/docs/content/kbd.md +5 -1
  103. package/src/ui/docs/content/label.md +13 -4
  104. package/src/ui/docs/content/log.md +5 -5
  105. package/src/ui/docs/content/markdown.md +5 -4
  106. package/src/ui/docs/content/mcp.md +49 -23
  107. package/src/ui/docs/content/menu.md +95 -10
  108. package/src/ui/docs/content/metric-card.md +12 -4
  109. package/src/ui/docs/content/observability.md +10 -9
  110. package/src/ui/docs/content/page.md +118 -12
  111. package/src/ui/docs/content/pagination.md +13 -5
  112. package/src/ui/docs/content/pane.md +55 -5
  113. package/src/ui/docs/content/popover.md +42 -5
  114. package/src/ui/docs/content/presentation.md +91 -47
  115. package/src/ui/docs/content/progress.md +27 -14
  116. package/src/ui/docs/content/queue.md +39 -21
  117. package/src/ui/docs/content/radio-group.md +26 -4
  118. package/src/ui/docs/content/router.md +29 -10
  119. package/src/ui/docs/content/runtime.md +90 -54
  120. package/src/ui/docs/content/scheduler.md +53 -21
  121. package/src/ui/docs/content/scroll-area.md +42 -35
  122. package/src/ui/docs/content/select.md +78 -33
  123. package/src/ui/docs/content/semantic-context.md +21 -2
  124. package/src/ui/docs/content/separator.md +15 -9
  125. package/src/ui/docs/content/sidebar.md +3 -2
  126. package/src/ui/docs/content/skeleton.md +6 -1
  127. package/src/ui/docs/content/slider.md +37 -9
  128. package/src/ui/docs/content/spinner.md +4 -3
  129. package/src/ui/docs/content/split.md +19 -0
  130. package/src/ui/docs/content/storage.md +8 -9
  131. package/src/ui/docs/content/switch.md +10 -7
  132. package/src/ui/docs/content/table.md +46 -8
  133. package/src/ui/docs/content/tabs.md +14 -4
  134. package/src/ui/docs/content/testing.md +76 -31
  135. package/src/ui/docs/content/textarea.md +5 -4
  136. package/src/ui/docs/content/toast.md +48 -7
  137. package/src/ui/docs/content/toggle.md +3 -3
  138. package/src/ui/docs/content/tokens.md +15 -26
  139. package/src/ui/docs/content/tooltip.md +18 -2
  140. package/src/ui/docs/content/truncate.md +36 -6
  141. package/src/ui/docs/content/ui.md +74 -22
  142. package/src/ui/docs/content/upgrading.md +45 -36
  143. package/src/ui/docs/plugin.ts +36 -2
  144. package/src/ui/docs/registry.tsx +6 -0
  145. package/src/ui/drivers/react.tsx +16 -8
  146. package/src/ui/meta.ts +32 -32
  147. package/src/ui/react.tsx +1 -0
  148. package/src/vite/design.ts +16 -4
@@ -1,9 +1,11 @@
1
1
  ## Campo empilhado
2
2
 
3
- Use `Field` para manter rótulo, controle, ajuda e erro com espaçamento consistente. Na orientação
4
- vertical, `FieldLabel` e o controle ficam empilhados. A ajuda fica junto ao rótulo, com `LabelHelp`,
5
- o mesmo ícone que o `ActionForm` usa para `FieldSpec.help`. Use `FieldDescription` somente quando a
6
- frase precisar ficar visível o tempo todo abaixo do controle.
3
+ Use `Field` para manter rótulo, controle, ajuda e erro com espaçamento consistente em um formulário
4
+ montado à mão. Quando o formulário executa uma action, prefira [`ActionForm`](/ui/action-form), que
5
+ monta os mesmos campos a partir do contrato. Na orientação vertical, `FieldLabel` e o controle ficam
6
+ empilhados. A ajuda fica junto ao rótulo, com [`LabelHelp`](/ui/action-form), o mesmo ícone que o
7
+ `ActionForm` usa para `FieldSpec.help`. Use `FieldDescription` somente quando a frase precisar
8
+ ficar visível o tempo todo abaixo do controle.
7
9
 
8
10
  ```tsx preview col md
9
11
  <Field>
@@ -15,7 +17,7 @@ frase precisar ficar visível o tempo todo abaixo do controle.
15
17
  </Field>
16
18
  ```
17
19
 
18
- ## Horizontal com toggle
20
+ ## Horizontal com Switch
19
21
 
20
22
  `orientation="horizontal"` posiciona o controle ao lado do texto. `FieldContent` agrupa título e
21
23
  descrição; `FieldTitle` exibe o título quando o texto não puder ser um `<label>`. Como ele não se
@@ -35,7 +37,7 @@ vincula ao controle, ligue-o ao controle por `aria-labelledby` para manter o nom
35
37
 
36
38
  ## Conjunto com legenda e erro
37
39
 
38
- Use `Content as="fieldset"` para nomear um conjunto de campos relacionados. O título vira a legenda
40
+ Use [`Content`](/ui/content) com `as="fieldset"` para nomear um conjunto de campos relacionados. O título vira a legenda
39
41
  nativa, enquanto `FieldGroup` define o espaçamento e `FieldSeparator` marca uma divisão.
40
42
  `FieldError` aparece somente quando existe mensagem de erro. Declare `data-invalid="true"` no
41
43
  `Field` para que o rótulo acompanhe o contexto de erro; `aria-invalid` marca apenas o controle. O
@@ -70,6 +72,18 @@ nativa, enquanto `FieldGroup` define o espaçamento e `FieldSeparator` marca uma
70
72
  </Content>
71
73
  ```
72
74
 
75
+ ## Campo desabilitado
76
+
77
+ Declare `disabled` no controle para bloquear a edição. Para que o rótulo acompanhe o estado,
78
+ declare também `data-disabled="true"` no `Field`: `FieldLabel` e `FieldTitle` esmaecem junto.
79
+
80
+ ```tsx preview col md
81
+ <Field data-disabled="true">
82
+ <FieldLabel htmlFor="workspace-client">Cliente</FieldLabel>
83
+ <Input id="workspace-client" defaultValue="Empresa X" disabled />
84
+ </Field>
85
+ ```
86
+
73
87
  ## Conjunto com moldura
74
88
 
75
89
  Use a moldura para distinguir grupos vizinhos que continuem pertencendo ao mesmo formulário. O
@@ -117,6 +131,7 @@ demais regiões nomeadas. A migração pode ser gradual porque os controles e o
117
131
  | -------------- | -------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- |
118
132
  | `orientation` | `'vertical' \| 'horizontal' \| 'responsive'` | `'vertical'` | Direção fixa do campo. `responsive` permanece como alias compatível de `horizontal`. |
119
133
  | `data-invalid` | `boolean \| 'true' \| 'false'` | | Com `true`, aplica o contexto de erro ao campo e ao rótulo. O `ActionForm` define o atributo sozinho. |
134
+ | `data-disabled` | `'true' \| 'false'` | | Com `true`, esmaece `FieldLabel` e `FieldTitle`. Declare `disabled` também no controle. |
120
135
 
121
136
  ## Propriedades de FieldLegend
122
137
 
@@ -10,20 +10,18 @@ as ferramentas usam essas definições sem exigir schemas e tipos paralelos.
10
10
 
11
11
  ## O modelo
12
12
 
13
- > Comece pelo problema que o modelo compartilhado resolve antes de conhecer seus arquivos.
14
-
15
13
  As regras de uma aplicação costumam aparecer em vários lugares: validação, transporte, API,
16
- formulários e documentação. No Opus, entidades, dicts e contratos de action descrevem essas
14
+ formulários e documentação. No Opus, entidades, dicionários e contratos de action descrevem essas
17
15
  regras uma vez. A API e a web consomem a mesma definição, enquanto runtime, adapters, UI, CLI
18
16
  e verificações ajudam a mantê-la consistente. Cada projeto acrescenta seu domínio sobre essa
19
17
  base versionada.
20
18
 
21
19
  ## Começar do zero
22
20
 
23
- > Projeto novo não se monta à mão: o esqueleto canônico sai do `opus create`, com os
24
- > pré-requisitos do protocolo, da UI e do preview já plugados — e os gates verdes.
21
+ Para um projeto novo, use `opus create`: ele gera o app com os pré-requisitos do protocolo, da UI e
22
+ do preview já configurados, e os gates passam desde o início.
25
23
 
26
- O Opus e a Base ficam no npm público: leitura não exige token nem configuração de escopo.
24
+ O Opus e a Base ficam no npm público: a instalação não exige token nem configuração de escopo.
27
25
  Crie o projeto e materialize as duas camadas antes dos gates:
28
26
 
29
27
  ```bash
@@ -43,15 +41,21 @@ com `pnpm dlx @softize/opus@<versão> create meu-app` ou desative a quarentena a
43
41
  com `pnpm --config.minimum-release-age=0 dlx …`. Dentro do projeto, o workspace exclui Opus e Base
44
42
  da quarentena para preservar o par validado.
45
43
 
46
- Nasce com: `opus.config.ts` + domínio-exemplo canônico (0 violações, spec documentada),
47
- vite + react + tema do Opus (Tailwind v4 CSS-first), dev server na porta que o Maestro
48
- injeta no preview, `opus.json` (pin), instruções, skills e hooks gerenciados. A Base é
49
- dona do método geral, memória e revisão; o Opus acrescenta somente o conhecimento do SDK.
44
+ O app criado inclui:
45
+
46
+ - `opus.config.ts` e um domínio de exemplo documentado, sem violações no `opus check`;
47
+ - Vite, React e o tema do Opus sobre Tailwind v4, configurado por CSS;
48
+ - um dev server que respeita a variável `PORT`, para que um orquestrador de preview escolha a
49
+ porta;
50
+ - `opus.json`, com a versão adotada, e as instruções, skills e hooks gerenciados.
51
+
52
+ A Base cuida do método geral de trabalho, da memória e da revisão; o Opus acrescenta o conhecimento
53
+ específico do SDK.
50
54
 
51
- **Monorepo:** a raiz nasce do `--monorepo` e cada app nasce dentro dela — o create
52
- detecta o `pnpm-workspace.yaml` e gera só o que é do app (sem workspace yaml aninhado),
53
- **avisando** o que a raiz precisa ter (glob de packages, allowBuilds e exclusões de
54
- quarentena para Opus/Base) sem tocar nos seus arquivos:
55
+ Em um monorepo, crie primeiro a raiz com `--monorepo` e depois cada app dentro dela. O `create`
56
+ detecta o `pnpm-workspace.yaml` e gera só os arquivos do app, sem um workspace aninhado. O que a raiz
57
+ precisa ter, como o glob de packages, `allowBuilds` e as exclusões de quarentena para Opus e Base,
58
+ aparece como aviso, sem alterar os arquivos da raiz:
55
59
 
56
60
  ```bash
57
61
  pnpm dlx @softize/opus create meu-cliente --monorepo # a raiz do workspace
@@ -61,13 +65,10 @@ pnpm dlx @softize/opus create apps/portal # o app (modo detectado)
61
65
 
62
66
  ## Carregar o Opus em um projeto existente
63
67
 
64
- > O `opus.json` registra a versão usada pelo projeto. Se o arquivo ainda não existe, o setup
65
- > cria a estrutura necessária antes das demais etapas.
66
-
67
- A versão do Opus fica pinada em `opus.json`. O `opus setup` é per-app: grava o marcador,
68
- o inventário de copy e as projeções específicas do SDK. O `base setup` materializa o
69
- método geral, memória, agentes e revisão na raiz do repositório. Os componentes vêm do
70
- barrel `@softize/opus/ui/react`; o tema, por CSS.
68
+ O `opus.json` registra a versão do Opus adotada pelo app; se ele ainda não existe, o setup o cria.
69
+ `opus setup` roda em cada app e grava o marcador, o inventário de copy e as projeções específicas do
70
+ SDK. `base setup` materializa na raiz do repositório o método geral, a memória, os agentes e a
71
+ revisão. Os componentes vêm de `@softize/opus/ui/react`, e o tema, de um arquivo CSS.
71
72
 
72
73
  ```bash
73
74
  # Bootstrap idempotente de um projeto existente.
@@ -78,39 +79,48 @@ pnpm exec base setup
78
79
  ```
79
80
 
80
81
  ```css
81
- /* index.css do app — o tema canônico + os componentes do Opus no scan do Tailwind. */
82
+ /* index.css do app — o tema do Opus e os componentes do pacote no scan do Tailwind. */
82
83
  @import '@softize/opus/ui/theme.css';
83
84
  @source '../node_modules/@softize/opus/src/ui/**/*.{ts,tsx}';
84
85
  ```
85
86
 
86
- ## A stack do runtime
87
+ ## Montar o runtime
87
88
 
88
- > `createRuntime` compõe a stack a partir de adapters plugáveis. Cada capacidade (data, auth,
89
- > audit…) é um adapter — troca a implementação sem tocar o domínio.
89
+ No servidor, `createRuntime` reúne as capacidades que o app usa. Cada capacidade, como banco,
90
+ autenticação ou auditoria, é um adapter que pode ser trocado sem alterar o domínio. Os providers da
91
+ interface estão em [Como consumir](/ui/como-consumir).
90
92
 
91
93
  ```ts
92
94
  import { createRuntime } from '@softize/opus/core'
95
+ import { jwtAuth } from '@softize/opus/auth/jwt'
93
96
  import { kyselyData } from '@softize/opus/data/kysely'
94
97
  import { fastifyServer } from '@softize/opus/server/fastify'
95
98
  import { pgAudit } from '@softize/opus/audit/pg'
99
+ import { workspaceDomain } from './domains/workspace/index.ts'
96
100
 
97
101
  const runtime = createRuntime({
98
102
  server: fastifyServer({ app }),
99
- data: kyselyData({ db }), // Kysely + Postgres
100
- auth: makeAuthAdapter(), // gate (IdP)
103
+ data: kyselyData({ db }), // Kysely e Postgres
104
+ auth: jwtAuth({ secret: process.env.JWT_SECRET!, mapUser: (p) => ({ id: String(p.sub) }) }),
101
105
  audit: [pgAudit({ pool })], // trilha de auditoria
102
106
  config: { env, i18n: { defaultLocale: 'pt-BR' } },
103
107
  })
104
108
 
105
- // Domínios = grupos de actions. Não registrado = não existe.
106
- runtime.register([workspacesDomain, agentsDomain, skillsDomain])
109
+ // Só o que foi registrado existe para o runtime.
110
+ runtime.register([workspaceDomain])
107
111
  await runtime.start()
108
112
  ```
109
113
 
110
- ## Antes de mexer
114
+ Cada capacidade e o registro de domínios estão detalhados em [Runtime & adapters](/docs/runtime).
115
+
116
+ ## Antes de alterar o projeto
111
117
 
112
- > O protocolo de leitura: a verdade local mora nos docs do repo, não nesta página.
118
+ A documentação do próprio repositório descreve o que é específico dele; esta página cobre só o que
119
+ vale para todo projeto Opus.
113
120
 
114
- - `CLAUDE.md` do repo e do app — o índice local: mapa, gates, gotchas.
115
- - As `description` das entidades/actions — a spec de negócio, projetada no manifest pelo `opus gen`.
116
- - Gate de build: `opus check` (convenções das actions e da UI) + `opus copy --check` e `base copy check` (inventário e política de copy) + `opus db check` (drift entidade ↔ banco). Rode antes de entregar.
121
+ - O `CLAUDE.md` do repositório e de cada app reúne o mapa do código, os gates e os cuidados locais.
122
+ - A `description` de entidades e actions registra a regra de negócio, que `opus gen` projeta no
123
+ manifest.
124
+ - Antes de entregar, rode `opus check` (convenções das actions e da UI), `opus copy --check` e
125
+ `base copy check` (inventário e política de copy) e, com banco, `opus db check` (divergência entre
126
+ entidades e banco).
@@ -1,9 +1,9 @@
1
- Use `IconPicker` para selecionar um ícone por seu nome em kebab-case. O consumidor renderiza o
2
- valor com a mesma paleta, por exemplo `iconPickerIcons[name] ?? FallbackIcon`. Selecionar novamente o
3
- ícone atual limpa o valor.
4
-
5
1
  ## Escolher na paleta padrão
6
2
 
3
+ Use `IconPicker` quando a pessoa puder escolher o ícone que identifica um registro, como um
4
+ relatório ou um painel. A lista abre em um popover com busca, e o valor guardado é o nome do ícone em
5
+ kebab-case, como `chart-line`. Selecionar novamente o ícone atual limpa o valor.
6
+
7
7
  ```tsx preview col md
8
8
  const [icon, setIcon] = useState('chart-line')
9
9
 
@@ -15,10 +15,11 @@ render(
15
15
  )
16
16
  ```
17
17
 
18
- ## Renderizando o escolhido
18
+ ## Apresentar o ícone escolhido
19
19
 
20
- A paleta padrão é exportada como `iconPickerIcons`. Use a mesma coleção para apresentar o ícone
21
- salvo em listas, cards e árvores.
20
+ O valor é somente um nome: quem apresenta o registro renderiza o ícone com a mesma paleta. A paleta
21
+ padrão é exportada como `iconPickerIcons`; use-a em listas, cards e árvores, com um ícone de reserva
22
+ para nomes que não existam mais, como em `iconPickerIcons[name] ?? FileText`.
22
23
 
23
24
  ```tsx preview col md
24
25
  const [icon, setIcon] = useState('store')
@@ -27,10 +28,14 @@ const SelectedIcon = iconPickerIcons[icon] ?? FileText
27
28
  render(
28
29
  <div className="flex items-center gap-3">
29
30
  <IconPicker value={icon} onChange={setIcon} className="w-56" />
30
- <span className="flex items-center gap-2 rounded-md border px-3 py-2 text-sm">
31
- <SelectedIcon className="size-4 text-muted-foreground" />
32
- Painel comercial
33
- </span>
31
+ <Item variant="outline" size="sm">
32
+ <ItemMedia variant="icon">
33
+ <SelectedIcon />
34
+ </ItemMedia>
35
+ <ItemHeader>
36
+ <ItemTitle>Painel comercial</ItemTitle>
37
+ </ItemHeader>
38
+ </Item>
34
39
  </div>,
35
40
  )
36
41
  ```
@@ -62,5 +67,6 @@ render(
62
67
  | `onChange` | `(name: string) => void` | | Recebe o nome escolhido. |
63
68
  | `icons` | `Record<string, LucideIcon>` | `iconPickerIcons` | Paleta nome → componente. |
64
69
  | `placeholder` | `string` | `'Selecione um ícone…'` | Texto do gatilho sem seleção. |
65
- | `disabled` | `boolean` | | Desabilita o gatilho. |
66
- | `id`, `aria-invalid`, `aria-describedby` | | | Integração com `Field`/`ActionForm`: o gatilho recebe a identidade e o estado de erro do campo. |
70
+ | `disabled` | `boolean` | `false` | Desabilita o gatilho. |
71
+ | `className` | `string` | | Classes do gatilho, em geral para ajustar a largura. Sem elas, o gatilho ocupa toda a largura disponível. |
72
+ | `id`, `aria-invalid`, `aria-describedby` | `string`, `boolean`, `string` | | Integração com `Field` e `ActionForm`: o gatilho recebe a identidade do campo, o estado inválido e a mensagem associada. Com `aria-invalid`, a moldura assume o contexto de erro, como no `Input`. |
@@ -1,8 +1,8 @@
1
1
  ## Moldura decorativa para ícone
2
2
 
3
3
  Use `IconTile` quando um ícone precisar de uma pequena superfície para ganhar hierarquia em uma
4
- legenda, item ou resumo. O componente não é interativo; para uma ação, use `Button` com tamanho de
5
- ícone e um nome acessível.
4
+ legenda, item ou resumo. O componente não é interativo; para uma ação, use [`Button`](/ui/button)
5
+ com tamanho de ícone e um nome acessível.
6
6
 
7
7
  ```tsx preview row
8
8
  <IconTile size="xs"><UserRound /></IconTile>
@@ -13,30 +13,35 @@ legenda, item ou resumo. O componente não é interativo; para uma ação, use `
13
13
  O tamanho controla a caixa e o glifo pela escala compartilhada dos controles. Um ícone que declara
14
14
  uma classe `size-*` preserva a própria medida.
15
15
 
16
- ## Em uma legenda
16
+ ## Em slots de ícone e de mídia
17
17
 
18
- Slots de ícone não criam a moldura automaticamente. Componha `IconTile` somente quando ela fizer
19
- sentido para a superfície.
18
+ Slots genéricos de ícone, como `FieldLegend.icon`, não criam a moldura: passe o glifo quando quiser
19
+ só o ícone e componha `IconTile` quando a moldura reforçar a hierarquia.
20
+
21
+ Os slots de mídia das superfícies já são um `IconTile`: `CardMedia`, `ContentMedia`, `DialogMedia`,
22
+ `DrawerMedia` e `PaneMedia` desenham a moldura sozinhos. Passe só o glifo e escolha `size`,
23
+ `context` e `variant` no próprio slot. Em [`Content`](/ui/content), um `IconTile` passado em
24
+ `media` é adotado pelo slot, que herda o que ele declara, em vez de desenhar uma moldura dentro da
25
+ outra.
20
26
 
21
27
  ```tsx preview col md
22
28
  <Content
23
29
  as="fieldset"
24
30
  framed
25
31
  frameVariant="legend"
26
- title={
27
- <span className="flex items-center gap-2">
28
- <IconTile size="xs">
29
- <CarFront />
30
- </IconTile>
31
- Veículo
32
- </span>
33
- }
32
+ title="Veículo"
33
+ media={<CarFront aria-hidden="true" />}
34
34
  >
35
- <FieldGroup />
35
+ <FieldGroup>
36
+ <Field>
37
+ <FieldLabel htmlFor="icon-tile-plate">Placa</FieldLabel>
38
+ <Input id="icon-tile-plate" />
39
+ </Field>
40
+ </FieldGroup>
36
41
  </Content>
37
42
  ```
38
43
 
39
- ## Propriedades
44
+ ## Propriedades de IconTile
40
45
 
41
46
  | Propriedade | Tipo | Padrão | Descrição |
42
47
  | ----------- | ------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------- |
@@ -45,3 +50,21 @@ sentido para a superfície.
45
50
  | `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
46
51
 
47
52
  As demais propriedades são as propriedades nativas de `span`.
53
+
54
+ ## Função iconTileVariants
55
+
56
+ `iconTileVariants({ context, variant, size })` retorna as classes da moldura para um elemento que
57
+ não é renderizado pelo `IconTile`. Os padrões são os mesmos do componente: `neutral`, `subtle` e
58
+ `default`.
59
+
60
+ ```ts
61
+ function iconTileVariants(options?: {
62
+ context?: IconTileContext | null
63
+ variant?: IconTileVariant | null
64
+ size?: IconTileSize | null
65
+ }): string
66
+ ```
67
+
68
+ `resolveIconTileStyle` continua exportado, mas está depreciado e sai em uma próxima versão maior. Ele
69
+ só preenche os padrões acima, que `IconTile` e `iconTileVariants` já aplicam. Código novo usa um dos
70
+ dois.
@@ -1,10 +1,15 @@
1
1
  ## Código em uma sequência
2
2
 
3
- `maxLength` define o comprimento do código. Cada `InputOTPSlot` recebe o índice de uma posição e
4
- `InputOTPGroup` mantém a sequência agrupada.
3
+ Use `InputOTP` quando a pessoa precisar digitar um código curto recebido por outro canal, como a
4
+ confirmação de acesso enviada por e-mail ou por um aplicativo autenticador. Cada caractere ocupa uma
5
+ posição própria, e o foco avança sozinho durante a digitação ou ao colar o código inteiro.
6
+
7
+ `maxLength` define o comprimento. Cada `InputOTPSlot` recebe o índice de uma posição, e
8
+ `InputOTPGroup` reúne as posições em um bloco. `pattern` restringe os caracteres aceitos; no
9
+ exemplo, somente dígitos.
5
10
 
6
11
  ```tsx preview
7
- <InputOTP maxLength={6}>
12
+ <InputOTP maxLength={6} pattern="^\d+$">
8
13
  <InputOTPGroup>
9
14
  <InputOTPSlot index={0} />
10
15
  <InputOTPSlot index={1} />
@@ -40,14 +45,14 @@ Use `InputOTPSeparator` entre grupos para dividir visualmente o código. Os índ
40
45
  ## Controlado
41
46
 
42
47
  Use `value` e `onChange` quando o estado externo precisar validar o comprimento antes de liberar a
43
- próxima ação.
48
+ próxima ação. `onComplete` é chamado quando todas as posições estão preenchidas.
44
49
 
45
50
  ```tsx preview col-start
46
51
  const [code, setCode] = useState('')
47
52
 
48
53
  render(
49
54
  <>
50
- <InputOTP maxLength={6} value={code} onChange={setCode}>
55
+ <InputOTP maxLength={6} pattern="^\d+$" value={code} onChange={setCode}>
51
56
  <InputOTPGroup>
52
57
  <InputOTPSlot index={0} />
53
58
  <InputOTPSlot index={1} />
@@ -64,6 +69,41 @@ render(
64
69
  )
65
70
  ```
66
71
 
72
+ ## Código recusado
73
+
74
+ Quando o código for recusado, declare `aria-invalid` no `InputOTP`, que o repassa ao campo que
75
+ recebe a digitação, para que leitores de tela anunciem o erro. Declare-o também em cada
76
+ `InputOTPSlot` para aplicar a borda de erro e explique o problema em um texto associado por
77
+ `aria-describedby`. `disabled` no `InputOTP` bloqueia a digitação e
78
+ esmaece o conjunto, por exemplo enquanto o código é conferido.
79
+
80
+ ```tsx preview col-start
81
+ const [code, setCode] = useState('1234')
82
+
83
+ render(
84
+ <>
85
+ <InputOTP
86
+ maxLength={4}
87
+ pattern="^\d+$"
88
+ value={code}
89
+ onChange={setCode}
90
+ aria-invalid
91
+ aria-describedby="otp-error"
92
+ >
93
+ <InputOTPGroup>
94
+ <InputOTPSlot index={0} aria-invalid />
95
+ <InputOTPSlot index={1} aria-invalid />
96
+ <InputOTPSlot index={2} aria-invalid />
97
+ <InputOTPSlot index={3} aria-invalid />
98
+ </InputOTPGroup>
99
+ </InputOTP>
100
+ <p id="otp-error" className="text-sm text-context-danger-emphasis">
101
+ Código expirado. Peça um novo código para continuar.
102
+ </p>
103
+ </>,
104
+ )
105
+ ```
106
+
67
107
  ## Propriedades de InputOTP
68
108
 
69
109
  | Propriedade | Tipo | Padrão | Descrição |
@@ -72,11 +112,22 @@ render(
72
112
  | `value` | `string` | | Código no modo controlado. Use com `onChange`. |
73
113
  | `onChange` | `(value: string) => void` | | Chamado a cada entrada com o código acumulado. |
74
114
  | `onComplete` | `(value: string) => void` | | Chamado quando o código atinge `maxLength`. |
75
- | `pattern` | `string` | | Expressão regular que o código precisa satisfazer; entradas que não casam são descartadas. Sem `pattern`, qualquer caractere é aceito, inclusive letras. Para somente dígitos, use `'^\\d+$'`. |
76
- | `disabled` | `boolean` | `false` | Desabilita a digitação em todo o conjunto. |
115
+ | `pattern` | `string` | | Expressão regular que o código precisa satisfazer; entradas que não casam são descartadas. Sem `pattern`, qualquer caractere é aceito, inclusive letras. Para somente dígitos, use `pattern="^\d+$"` em JSX; em uma string JavaScript, a barra é escapada: `'^\\d+$'`. |
116
+ | `disabled` | `boolean` | `false` | Desabilita a digitação e esmaece todo o conjunto. |
117
+ | `containerClassName` | `string` | | Classes do contêiner que alinha grupos e separadores. `className` vai para o campo de texto oculto que recebe a digitação. |
77
118
 
78
119
  ## Propriedades de InputOTPSlot
79
120
 
80
121
  | Propriedade | Tipo | Padrão | Descrição |
81
122
  |---|---|---|---|
82
123
  | `index` | `number` | | Posição do caractere, começando em zero. |
124
+ | `aria-invalid` | `boolean` | | Aplica a borda de erro à posição. |
125
+
126
+ ## Propriedades de InputOTPGroup
127
+
128
+ `InputOTPGroup` não tem propriedades próprias e aceita as props nativas de `<div>`.
129
+
130
+ ## Propriedades de InputOTPSeparator
131
+
132
+ `InputOTPSeparator` desenha um traço com `role="separator"` entre grupos. Não tem propriedades
133
+ próprias e aceita as props nativas de `<div>`.
@@ -99,8 +99,8 @@ Use `InputGroup` quando o campo precisar combinar vários adornos, prefixos, suf
99
99
 
100
100
  ### Com ícone e ação
101
101
 
102
- `InputGroupAddon` ancora os adornos nas extremidades. O clique em uma área inerte do addon transfere
103
- o foco para o campo.
102
+ `InputGroupAddon` ancora os adornos nas extremidades. Um clique em uma área inerte do addon, fora
103
+ dos botões, transfere o foco para o campo, seja `InputGroupInput` ou `InputGroupTextarea`.
104
104
 
105
105
  ```tsx preview col
106
106
  <InputGroup>
@@ -149,11 +149,30 @@ Com `InputGroupTextarea`, um addon em `block-end` forma uma região de ações a
149
149
  </InputGroup>
150
150
  ```
151
151
 
152
+ ### Estados do grupo
153
+
154
+ `aria-invalid` no controle interno aplica o contexto de erro à moldura inteira. Para desabilitar,
155
+ basta declarar `disabled` no controle: o campo esmaece pelo próprio estado e os addons acompanham.
156
+ Um `InputGroupButton` desabilitado não apaga o grupo. Quando os addons precisarem esmaecer com o
157
+ controle ativo, declare `data-disabled="true"` no `InputGroup`.
158
+
159
+ ```tsx preview col
160
+ <InputGroup>
161
+ <InputGroupAddon><AtSign /></InputGroupAddon>
162
+ <InputGroupInput aria-invalid defaultValue="joao@" placeholder="E-mail do responsável" />
163
+ </InputGroup>
164
+ <InputGroup>
165
+ <InputGroupAddon><AtSign /></InputGroupAddon>
166
+ <InputGroupInput disabled defaultValue="joao@softize.com.br" />
167
+ </InputGroup>
168
+ ```
169
+
152
170
  ### Propriedades de InputGroup
153
171
 
154
172
  | Propriedade | Tipo | Padrão | Descrição |
155
173
  |---|---|---|---|
156
174
  | `shape` | `'default' \| 'pill'` | `'default'` | Geometria da moldura compartilhada. |
175
+ | `data-disabled` | `'true' \| 'false'` | | Com `true`, esmaece os addons mesmo com o controle ativo. Com o controle desabilitado, os addons já esmaecem sem ele. |
157
176
 
158
177
  ### Propriedades de InputGroupAddon
159
178
 
@@ -165,7 +184,7 @@ Com `InputGroupTextarea`, um addon em `block-end` forma uma região de ações a
165
184
 
166
185
  | Propriedade | Tipo | Padrão | Descrição |
167
186
  |---|---|---|---|
168
- | `size` | `'xs' \| 'sm' \| 'icon-xs' \| 'icon-sm'` | `'xs'` | O subconjunto da escala única que cabe num campo (1.5 · 2 · 1.5 · 1.75rem); os `icon-*` são quadrados. |
187
+ | `size` | `'xs' \| 'sm' \| 'icon-xs' \| 'icon-sm'` | `'xs'` | O subconjunto da escala única que cabe em um campo (1.5 · 2 · 1.5 · 1.75rem); os `icon-*` são quadrados. |
169
188
  | `context` | `'neutral' \| 'primary' \| 'danger'` | `'neutral'` | Contexto semântico herdado de Button. Com `variant` `solid` ou `subtle` sem contexto declarado, o padrão passa a `'primary'`. |
170
189
  | `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'ghost'` | Tratamento visual; use `solid` quando a ação precisar de ênfase. |
171
190
 
@@ -1,7 +1,13 @@
1
1
  ## Linha composta
2
2
 
3
+ Use `Item` para uma linha de lista que identifica algo — um repositório, uma pessoa, uma
4
+ configuração — e oferece o que fazer com ele. Para dados em colunas comparáveis, use
5
+ [`Table`](/ui/table); para uma coleção carregada por uma action `kind: 'list'`, use
6
+ [`ActionList`](/ui/action-list).
7
+
3
8
  A composição completa usa `ItemMedia` à esquerda, `ItemHeader` (`ItemTitle` +
4
- `ItemDescription`) no meio e `ItemActions` à direita. Ícone e imagem mantêm uma moldura quadrada
9
+ `ItemDescription`) no meio e `ItemActions` à direita. A descrição mostra no máximo duas linhas e
10
+ termina em reticências quando passa disso. Ícone e imagem mantêm uma moldura quadrada
5
11
  alinhada ao topo, mesmo quando a descrição ocupa mais linhas. `variant="outline"` desenha a borda.
6
12
  O tamanho padrão usa corpo produtivo, `0.75rem` de padding e `0.75rem` entre regiões. `size="sm"`
7
13
  mantém o padding horizontal, reduz o padding vertical para `0.5rem` e o espaço entre regiões para
@@ -28,7 +34,10 @@ mantém o padding horizontal, reduz o padding vertical para `0.5rem` e o espaço
28
34
 
29
35
  ## Lista emoldurada
30
36
 
31
- `ItemGroup variant="framed"` aplica a superfície canônica. Os divisores continuam explícitos com `ItemSeparator`, tanto no modo `framed` quanto no `plain` — o padrão para listas de repositórios, agentes ou sessões.
37
+ `ItemGroup variant="framed"` aplica a superfície canônica. Os divisores continuam explícitos com
38
+ `ItemSeparator`, tanto no modo `framed` quanto no `plain` — o padrão para listas de repositórios,
39
+ agentes ou sessões. O grupo é anunciado como lista, e cada `Item` dentro dele, como um item da
40
+ lista.
32
41
 
33
42
  ```tsx preview col
34
43
  <ItemGroup variant="framed">
@@ -38,7 +47,7 @@ mantém o padding horizontal, reduz o padding vertical para `0.5rem` e o espaço
38
47
  </ItemMedia>
39
48
  <ItemHeader>
40
49
  <ItemTitle>empresa-x-web</ItemTitle>
41
- <ItemDescription>Front do workspace Empresa X.</ItemDescription>
50
+ <ItemDescription>Frontend do workspace Empresa X.</ItemDescription>
42
51
  </ItemHeader>
43
52
  <ItemActions>
44
53
  <Badge context="success">Sincronizado</Badge>
@@ -62,7 +71,9 @@ mantém o padding horizontal, reduz o padding vertical para `0.5rem` e o espaço
62
71
 
63
72
  ## Clicável e compacto
64
73
 
65
- `asChild` funde o Item em um `<a>` — a linha inteira vira alvo (hover no fundo). `size="sm"` aperta o respiro; `ItemMedia` na variante padrão recebe um `Avatar`, que já traz a própria moldura.
74
+ `asChild` funde o Item em um `<a>`: a linha inteira vira alvo e o fundo muda ao passar o ponteiro.
75
+ `size="sm"` aperta o respiro; `ItemMedia` na variante padrão recebe um [`Avatar`](/ui/avatar), que
76
+ já traz a própria moldura.
66
77
 
67
78
  ```tsx preview col
68
79
  <Item asChild size="sm" variant="outline">
@@ -103,6 +114,36 @@ Use `ItemBody` quando a linha também apresenta um valor ou controle que não pe
103
114
  `ItemContent` permanece exportado apenas por compatibilidade e está obsoleto. Código novo
104
115
  usa `ItemHeader` ou `ItemBody` conforme o papel do conteúdo.
105
116
 
117
+ ## Rodapé
118
+
119
+ `ItemFooter` ocupa uma linha inteira abaixo das demais regiões, com o conteúdo distribuído entre as
120
+ pontas. Use-o para metadados ou estado que complementam a linha.
121
+
122
+ ```tsx preview col
123
+ <Item variant="outline">
124
+ <ItemMedia variant="icon">
125
+ <FolderGit2 />
126
+ </ItemMedia>
127
+ <ItemHeader>
128
+ <ItemTitle>empresa-x-api</ItemTitle>
129
+ <ItemDescription>Repositório do backend do workspace Empresa X.</ItemDescription>
130
+ </ItemHeader>
131
+ <ItemActions>
132
+ <Button size="sm" variant="outline">Abrir</Button>
133
+ </ItemActions>
134
+ <ItemFooter>
135
+ <span className="text-xs text-muted-foreground">Atualizado há 2 horas</span>
136
+ <Badge context="success">Sincronizado</Badge>
137
+ </ItemFooter>
138
+ </Item>
139
+ ```
140
+
141
+ ## Composições que lançam erro
142
+
143
+ As partes conferem o próprio lugar ao renderizar. `ItemMedia`, `ItemHeader`, `ItemBody`,
144
+ `ItemActions` e `ItemFooter` lançam erro fora de um `Item`; `ItemTitle` e `ItemDescription` lançam
145
+ erro fora de um `ItemHeader` ou do `ItemContent` obsoleto. A mensagem nomeia o pai esperado.
146
+
106
147
  ## Propriedades de Item
107
148
 
108
149
  | Propriedade | Tipo | Padrão | Descrição |
@@ -117,12 +158,18 @@ usa `ItemHeader` ou `ItemBody` conforme o papel do conteúdo.
117
158
  |---|---|---|---|
118
159
  | `variant` | `'plain' \| 'framed'` | `'plain'` | `framed` aplica moldura e superfície; os divisores permanecem explícitos nos dois modos. |
119
160
 
161
+ O grupo renderiza um `div` com `role="list"` e aceita as props nativas desse elemento.
162
+
120
163
  ## Propriedades de ItemMedia
121
164
 
122
165
  | Propriedade | Tipo | Padrão | Descrição |
123
166
  |---|---|---|---|
124
167
  | `variant` | `'default' \| 'icon' \| 'image'` | `'default'` | Define mídia livre ou uma moldura quadrada para ícone ou imagem. |
125
168
 
169
+ ## Propriedades das demais partes
170
+
126
171
  `ItemHeader`, `ItemTitle`, `ItemDescription`, `ItemBody`, `ItemActions`, `ItemFooter` e
127
- `ItemSeparator` aceitam as props nativas dos respectivos elementos e não adicionam propriedades
128
- próprias. O `ItemContent` obsoleto preserva esse mesmo contrato por compatibilidade.
172
+ `ItemSeparator` não têm props próprias. Cada um aceita as props nativas do elemento que renderiza:
173
+ `div`, exceto `ItemDescription`, que renderiza `p`, e `ItemSeparator`, que aceita as props de
174
+ [`Separator`](/ui/separator). O `ItemContent` obsoleto preserva esse mesmo contrato por
175
+ compatibilidade.
@@ -1,7 +1,7 @@
1
1
  ## Uma tecla
2
2
 
3
3
  Use um `Kbd` para representar cada tecla. O componente apresenta o atalho; a captura do teclado
4
- continua no handler do aplicativo.
4
+ continua no handler do aplicativo. Em um item de [`Menu`](/ui/menu), o atalho usa `MenuShortcut`.
5
5
 
6
6
  ```tsx preview
7
7
  <Kbd>Esc</Kbd>
@@ -29,6 +29,7 @@ reconhecível que o nome da tecla.
29
29
  <Kbd>
30
30
  <CommandIcon />
31
31
  </Kbd>
32
+ <span className="text-xs text-muted-foreground">+</span>
32
33
  <Kbd>
33
34
  <CornerDownLeft />
34
35
  </Kbd>
@@ -69,3 +70,6 @@ ação antes de informar seu atalho.
69
70
  |---|---|---|---|
70
71
  | `children` | `React.ReactNode` | | Teclas da combinação e conectores, como `+`, entre elas. |
71
72
  | `className` | `string` | | Classes adicionais aplicadas ao agrupador. |
73
+
74
+ `KbdGroup` renderiza um `kbd` que envolve as teclas, a marcação HTML de uma combinação, e aceita as
75
+ props desse elemento.