@softize/opus 18.0.1 → 18.1.1

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 (129) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/PROMOTED.md +4 -5
  3. package/README.md +5 -4
  4. package/bin/cli.mjs +4 -0
  5. package/docs/adr/0004-page-content-state-is-composed.md +3 -0
  6. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +6 -2
  7. package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +6 -1
  8. package/docs/adr/0014-structural-headers-do-not-carry-description.md +2 -2
  9. package/docs/adr/0015-action-size-follows-interaction-density.md +4 -3
  10. package/docs/adr/0017-ui-assumes-a-fixed-desktop-layout.md +54 -0
  11. package/docs/adr/{0012-modal-header-only-names-the-surface.md → 0018-modal-header-only-names-the-surface.md} +4 -1
  12. package/docs/adr/0019-productive-surfaces-use-compact-density.md +65 -0
  13. package/docs/code-style.md +2 -2
  14. package/docs/consumer-upgrade-propagation.md +1 -1
  15. package/docs/data-products.md +5 -3
  16. package/docs/protocol.md +6 -6
  17. package/docs/relative-unit-scale.md +9 -2
  18. package/docs/releasing.md +28 -4
  19. package/package.json +1 -1
  20. package/registry/skills/build-opus-ui/references/evaluations.md +3 -1
  21. package/registry/skills/build-opus-ui/references/ui-patterns.md +20 -4
  22. package/src/auth/drivers/jwt.ts +2 -1
  23. package/src/core/runtime.ts +32 -5
  24. package/src/core/types.ts +16 -7
  25. package/src/mcp/index.ts +13 -1
  26. package/src/ui/components/patterns/action-list-dialog.tsx +2 -2
  27. package/src/ui/components/patterns/content-header.tsx +2 -2
  28. package/src/ui/components/patterns/form-dialog.tsx +7 -2
  29. package/src/ui/components/patterns/form.tsx +1 -1
  30. package/src/ui/components/patterns/list.tsx +239 -47
  31. package/src/ui/components/patterns/presentation.tsx +7 -5
  32. package/src/ui/components/patterns/sidebar.tsx +1 -1
  33. package/src/ui/components/patterns/state-surface.tsx +2 -2
  34. package/src/ui/components/patterns/surface-header.tsx +4 -4
  35. package/src/ui/components/primitives/alert.tsx +2 -2
  36. package/src/ui/components/primitives/breadcrumb.tsx +1 -1
  37. package/src/ui/components/primitives/button-group.tsx +1 -1
  38. package/src/ui/components/primitives/button.tsx +3 -3
  39. package/src/ui/components/primitives/calendar.tsx +1 -1
  40. package/src/ui/components/primitives/card.tsx +1 -1
  41. package/src/ui/components/primitives/close-button.tsx +40 -0
  42. package/src/ui/components/primitives/detail.tsx +68 -30
  43. package/src/ui/components/primitives/dialog.tsx +36 -21
  44. package/src/ui/components/primitives/drawer.tsx +26 -19
  45. package/src/ui/components/primitives/empty-value.tsx +3 -3
  46. package/src/ui/components/primitives/empty.tsx +1 -1
  47. package/src/ui/components/primitives/field.tsx +12 -12
  48. package/src/ui/components/primitives/icon-picker.tsx +1 -1
  49. package/src/ui/components/primitives/input-group.tsx +1 -1
  50. package/src/ui/components/primitives/input.tsx +2 -2
  51. package/src/ui/components/primitives/item.tsx +5 -5
  52. package/src/ui/components/primitives/pagination.tsx +4 -4
  53. package/src/ui/components/primitives/radio-group.tsx +1 -1
  54. package/src/ui/components/primitives/select.tsx +3 -3
  55. package/src/ui/components/primitives/table.tsx +26 -17
  56. package/src/ui/components/primitives/tabs.tsx +80 -23
  57. package/src/ui/components/primitives/textarea.tsx +1 -1
  58. package/src/ui/components/primitives/toggle-group.tsx +9 -2
  59. package/src/ui/docs/content/action-form-dialog.md +11 -4
  60. package/src/ui/docs/content/action-form.md +13 -3
  61. package/src/ui/docs/content/action-list-dialog.md +5 -7
  62. package/src/ui/docs/content/action-list.md +53 -5
  63. package/src/ui/docs/content/action-trigger.md +9 -5
  64. package/src/ui/docs/content/action-view.md +12 -8
  65. package/src/ui/docs/content/actions.md +36 -13
  66. package/src/ui/docs/content/ai.md +26 -7
  67. package/src/ui/docs/content/alert.md +6 -3
  68. package/src/ui/docs/content/aspect-ratio.md +2 -2
  69. package/src/ui/docs/content/auth.md +25 -10
  70. package/src/ui/docs/content/avatar.md +1 -1
  71. package/src/ui/docs/content/badge.md +2 -2
  72. package/src/ui/docs/content/breadcrumb.md +3 -2
  73. package/src/ui/docs/content/button.md +33 -8
  74. package/src/ui/docs/content/calendar.md +1 -1
  75. package/src/ui/docs/content/card.md +1 -1
  76. package/src/ui/docs/content/carousel.md +14 -3
  77. package/src/ui/docs/content/chat.md +1 -1
  78. package/src/ui/docs/content/cli.md +13 -7
  79. package/src/ui/docs/content/command.md +34 -2
  80. package/src/ui/docs/content/composer.md +1 -1
  81. package/src/ui/docs/content/content.md +5 -4
  82. package/src/ui/docs/content/customization.md +12 -2
  83. package/src/ui/docs/content/cycle.md +7 -5
  84. package/src/ui/docs/content/data-state.md +6 -5
  85. package/src/ui/docs/content/data.md +3 -3
  86. package/src/ui/docs/content/detail.md +12 -10
  87. package/src/ui/docs/content/dialog.md +14 -7
  88. package/src/ui/docs/content/dictionary-value.md +1 -1
  89. package/src/ui/docs/content/dock.md +23 -2
  90. package/src/ui/docs/content/dot.md +0 -2
  91. package/src/ui/docs/content/drawer.md +7 -4
  92. package/src/ui/docs/content/empty-value.md +4 -4
  93. package/src/ui/docs/content/empty.md +1 -4
  94. package/src/ui/docs/content/events.md +1 -1
  95. package/src/ui/docs/content/field.md +21 -12
  96. package/src/ui/docs/content/getting-started.md +4 -2
  97. package/src/ui/docs/content/icon-picker.md +2 -2
  98. package/src/ui/docs/content/input-otp.md +2 -0
  99. package/src/ui/docs/content/input.md +2 -3
  100. package/src/ui/docs/content/item.md +6 -3
  101. package/src/ui/docs/content/kbd.md +2 -1
  102. package/src/ui/docs/content/mcp.md +10 -4
  103. package/src/ui/docs/content/menu.md +27 -0
  104. package/src/ui/docs/content/page.md +20 -6
  105. package/src/ui/docs/content/pagination.md +9 -2
  106. package/src/ui/docs/content/popover.md +2 -2
  107. package/src/ui/docs/content/presentation.md +48 -47
  108. package/src/ui/docs/content/progress.md +2 -6
  109. package/src/ui/docs/content/runtime.md +8 -5
  110. package/src/ui/docs/content/scheduler.md +1 -1
  111. package/src/ui/docs/content/select.md +13 -8
  112. package/src/ui/docs/content/sidebar.md +3 -2
  113. package/src/ui/docs/content/skeleton.md +1 -1
  114. package/src/ui/docs/content/slider.md +4 -4
  115. package/src/ui/docs/content/spinner.md +3 -3
  116. package/src/ui/docs/content/tabs.md +22 -12
  117. package/src/ui/docs/content/testing.md +4 -2
  118. package/src/ui/docs/content/toast.md +5 -6
  119. package/src/ui/docs/content/toggle.md +37 -0
  120. package/src/ui/docs/content/tokens.md +45 -2
  121. package/src/ui/docs/content/tooltip.md +4 -3
  122. package/src/ui/docs/content/truncate.md +3 -2
  123. package/src/ui/docs/content/ui.md +3 -1
  124. package/src/ui/docs/content/upgrading.md +43 -13
  125. package/src/ui/docs/doc-client.tsx +1 -1
  126. package/src/ui/docs/registry.tsx +30 -5
  127. package/src/ui/meta.ts +4 -4
  128. package/src/ui/react.tsx +1 -0
  129. package/src/ui/theme.css +3 -0
@@ -92,7 +92,17 @@ Na composição explícita, combine somente as regiões necessárias. Este exemp
92
92
  uma ação global e o intro para o título:
93
93
 
94
94
  ```tsx preview col
95
- <PageShell navigation={<Breadcrumb>...</Breadcrumb>}>
95
+ <PageShell
96
+ navigation={
97
+ <Breadcrumb>
98
+ <BreadcrumbList>
99
+ <BreadcrumbItem>Vendas</BreadcrumbItem>
100
+ <BreadcrumbSeparator />
101
+ <BreadcrumbPage>Clientes</BreadcrumbPage>
102
+ </BreadcrumbList>
103
+ </Breadcrumb>
104
+ }
105
+ >
96
106
  <Page>
97
107
  <PageHeader>
98
108
  <PageActions>
@@ -172,7 +182,7 @@ e, sem `title`, valem `errorMessage` e `emptyMessage`.
172
182
  status="empty"
173
183
  title="Nenhum relatório"
174
184
  description="Crie o primeiro relatório para começar."
175
- action={<Button>Novo relatório</Button>}
185
+ action={<Button>Criar relatório</Button>}
176
186
  />
177
187
  </Page>
178
188
  ```
@@ -185,7 +195,7 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
185
195
  | Propriedade | Tipo | Padrão | Descrição |
186
196
  | ----------- | ----------- | ----------- | ----------------------------------------------------------------------------------------------- |
187
197
  | `title` | `ReactNode` | | O h1 da página. |
188
- | `actions` | `ReactNode` | | Ações contextuais no extremo oposto do cabeçalho; em telas estreitas, ficam abaixo do contexto. |
198
+ | `actions` | `ReactNode` | | Ações contextuais no extremo oposto do cabeçalho. |
189
199
  | `className` | `string` | `max-w-7xl` | Classes do container para substituir o teto padrão de `80rem`. |
190
200
  | `children` | `ReactNode` | | O body da página — espaçamento e diagramação são seus. |
191
201
 
@@ -204,7 +214,7 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
204
214
 
205
215
  | Propriedade | Tipo | Descrição |
206
216
  | ----------- | ----------------------------- | --------------------------------------------------------------------- |
207
- | `children` | `ReactNode` | Título introdutório e, quando necessário, ações ligadas à introdução. |
217
+ | `children` | `ReactNode` | `PageBack` ou `PageNavigation`, título introdutório e, quando necessário, ações ligadas à introdução. |
208
218
  | `className` | `string` | Classes adicionais da região introdutória. |
209
219
  | demais | Atributos de `HTMLDivElement` | Atributos nativos repassados à região introdutória. |
210
220
 
@@ -212,15 +222,19 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
212
222
 
213
223
  | Propriedade | Tipo | Descrição |
214
224
  | ----------- | ----------- | ---------------------------------------------------------------------------------------- |
215
- | `className` | `string` | Classes adicionais da região externa do cabeçalho. |
225
+ | `className` | `string` | Classes adicionais da região externa do cabeçalho, fora de `PageShell`. |
216
226
  | `children` | `ReactNode` | `PageBack` ou `PageNavigation`, `PageActions` e, fora do shell, um `PageTitle` opcional. |
227
+ | demais | Atributos de `HTMLDivElement` | Atributos nativos repassados à região externa do cabeçalho, fora de `PageShell`. |
228
+
229
+ Dentro de `PageShell`, `PageHeader` não renderiza uma região própria: devolve somente os filhos,
230
+ que o shell projeta na barra, e ignora `className` e os demais atributos.
217
231
 
218
232
  ## Propriedades de PageBack
219
233
 
220
234
  | Propriedade | Tipo | Padrão | Descrição |
221
235
  | ------------ | -------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------ |
222
236
  | `href` | `string` | obrigatório | Destino explícito da página pai. Sem ele o retorno não é tabulável nem tem nome acessível. |
223
- | `children` | `ReactNode` | | Nome do destino pai usado no rótulo acessível e no tooltip. |
237
+ | `children` | `ReactNode` | obrigatório | Nome do destino pai usado no rótulo acessível e no tooltip. |
224
238
  | `aria-label` | `string` | `Voltar para {children}` | Nome acessível; informe-o quando `children` não for texto simples. |
225
239
  | `onClick` | `MouseEventHandler<HTMLAnchorElement>` | | Integração opcional com o roteador do consumidor, junto do `href`, nunca no lugar dele. |
226
240
  | `className` | `string` | | Classes adicionais do link renderizado como botão `ghost`. |
@@ -131,7 +131,7 @@ render(
131
131
  | `page` | `number` | | Número usado como conteúdo, quando `children` não é informado, e no nome acessível “Página N”. |
132
132
  | `isActive` | `boolean` | `false` | Marca a página atual com `aria-current="page"` e tratamento `outline`. |
133
133
  | `href` | `string` | | Renderiza um `<a>`. Sem `href`, o componente usa `<button>` e aceita `onClick` e `disabled`. |
134
- | `size` | `ButtonProps['size']` | `'default'` | A escala única, via `Button`. A largura mínima cresce para acomodar números longos; os `icon-*` são quadrados. |
134
+ | `size` | `ButtonProps['size']` | `'default'` | A escala única, via `Button`. Os `icon-*` são quadrados. Nos tamanhos textuais, a altura fica fixa em 2.25rem com largura mínima quadrada que cresce para números longos, então `size="sm"` não reduz a altura; para uma barra mais baixa, use um `icon-*` ou ajuste por `className` (ex.: `h-7 min-w-7 text-xs`). |
135
135
 
136
136
  ## Propriedades de PaginationPrevious e PaginationNext
137
137
 
@@ -139,5 +139,12 @@ render(
139
139
  |---|---|---|---|
140
140
  | `label` | `string` | `'Página anterior'` ou `'Próxima página'` | Nome acessível e texto visível da ação. |
141
141
  | `iconOnly` | `boolean` | `false` | Exibe somente a seta; `label` continua disponível para leitura assistiva. |
142
- | `size` | `ButtonProps['size']` | `'default'`; `'icon'` com `iconOnly` | A escala única; `icon-sm` para o rodapé denso. |
142
+ | `size` | `ButtonProps['size']` | `'default'`; `'icon'` com `iconOnly` | A escala única; `icon-sm` para o rodapé denso. Sem `iconOnly`, a altura permanece 2.25rem em qualquer tamanho textual, como em `PaginationLink`. |
143
143
  | `iconClassName` | `string` | | Classes aplicadas ao ícone da seta, quando o glifo da escala não servir. |
144
+
145
+ ## Propriedades de PaginationEllipsis
146
+
147
+ | Propriedade | Tipo | Padrão | Descrição |
148
+ |---|---|---|---|
149
+ | `size` | `'icon-xs' \| 'icon-sm' \| 'icon' \| 'icon-lg'` | `'icon'` | Quadrado da escala única; use o mesmo tamanho das setas ao lado. |
150
+ | `className` | `string` | | Classes adicionais aplicadas à elipse. |
@@ -29,13 +29,13 @@ detalhes.
29
29
 
30
30
  ```tsx preview
31
31
  <Popover>
32
- <PopoverTrigger asChild><Button variant="ghost">start</Button></PopoverTrigger>
32
+ <PopoverTrigger asChild><Button variant="ghost">Início</Button></PopoverTrigger>
33
33
  <PopoverContent align="start" className="w-56">
34
34
  <PopoverDescription>Alinhado à borda esquerda do gatilho.</PopoverDescription>
35
35
  </PopoverContent>
36
36
  </Popover>
37
37
  <Popover>
38
- <PopoverTrigger asChild><Button variant="ghost">end</Button></PopoverTrigger>
38
+ <PopoverTrigger asChild><Button variant="ghost">Fim</Button></PopoverTrigger>
39
39
  <PopoverContent align="end" className="w-56">
40
40
  <PopoverDescription>Alinhado à borda direita do gatilho.</PopoverDescription>
41
41
  </PopoverContent>
@@ -5,12 +5,14 @@ cabeçalho, body e rodapé.
5
5
  Dialog e Drawer mantêm no cabeçalho a ordem horizontal de navegação ou retorno, título e ações.
6
6
  O retorno aparece em uma surface modal somente
7
7
  quando ela foi aberta sobre outro Dialog ou Drawer; a Page ao fundo sustenta o modal, mas não cria
8
- uma etapa de navegação. Formulários modais encerram o footer dividido igualmente entre `Cancelar`
9
- em `outline` e a ação principal.
8
+ uma etapa de navegação. Formulários modais dimensionam o footer pelo conteúdo, com `Cancelar` em
9
+ `ghost` e a ação principal sólida.
10
10
  Em Page hospedada por `PageShell`, a barra concentra navegação e ações globais. Uma Presentation
11
11
  de listagem materializa título e comandos de header no `Content` que envolve o `ActionList`; assim
12
- a criação permanece próxima da coleção. Esses comandos usam `default`. Nas demais Presentations, o
13
- título abre o conteúdo em `PageIntro` e comandos da superfície usam `default`.
12
+ a criação permanece próxima da coleção. Esses comandos usam o tamanho `default`: o que abre outra
13
+ Presentation é um `Button` na variante padrão (`solid`), e uma action `simple` vira um
14
+ `ActionTrigger` na variante `ghost`. Nas demais Presentations, o título abre o conteúdo em
15
+ `PageIntro` e os comandos da superfície seguem a mesma regra.
14
16
 
15
17
  Use esse padrão quando uma lista puder abrir um detalhe lateral, quando a mesma edição precisar
16
18
  funcionar em modal e em rota própria ou quando um fluxo começar compacto e crescer sem ganhar uma
@@ -22,25 +24,20 @@ compõe `PageBody` e, quando necessário, `PageFooter`. Para uma action `list`,
22
24
  `Content + ActionList`; o header da coleção recebe o título e os comandos declarados no placement
23
25
  `header`. Dialog e Drawer continuam usando o header da própria superfície.
24
26
  Os comandos da coleção ficam no extremo oposto ao título e permanecem textuais, mesmo quando a
25
- action declara um ícone. A criação usa `Criar recurso`, sem ícone, em um botão `default`.
27
+ action declara um ícone. A criação usa `Criar recurso`, sem ícone, em um `Button` na variante
28
+ padrão (`solid`) e no tamanho `default`.
26
29
  Não envolva a Presentation em um card para simular a página; valide proporção, rolagem e ações no
27
30
  shell que efetivamente hospeda a rota.
28
31
 
29
- ```tsx live
30
- const workspaceUpdate = defineContract({
31
- name: "workspace.update",
32
- kind: "form",
33
- label: "Editar workspace",
34
- input: z.object({ name: z.string() }),
35
- output: z.object({ id: z.string() }),
36
- fields: { name: { label: "Nome" } },
37
- });
32
+ O exemplo declara a Presentation sobre um contrato de formulário e alterna a superfície. O
33
+ `DocBrowserActionProvider` simula o cliente de actions; na aplicação, esse papel é do provider real.
38
34
 
35
+ ```tsx live
39
36
  const workspacePresentation = definePresentation({
40
37
  schemaVersion: 1,
41
- id: "workspace.update",
42
- title: "Editar workspace",
43
- body: { action: workspaceUpdate.name },
38
+ id: "workspace.create",
39
+ title: "Criar workspace",
40
+ body: { action: docWorkspaceCreate.name },
44
41
  });
45
42
 
46
43
  function Example() {
@@ -55,36 +52,38 @@ function Example() {
55
52
  );
56
53
 
57
54
  return (
58
- <div className="space-y-4">
59
- <ButtonGroup mode="spaced">
60
- {["page", "dialog", "drawer"].map((value) => (
61
- <Button
62
- key={value}
63
- size="sm"
64
- variant={invocation.surface === value ? "solid" : "outline"}
65
- onClick={() => {
66
- setInvocation({ ...invocation, surface: value });
67
- setOpen(true);
68
- }}
69
- >
70
- {value}
71
- </Button>
72
- ))}
73
- </ButtonGroup>
74
-
75
- <Presentation
76
- definition={workspacePresentation}
77
- definitions={[workspacePresentation]}
78
- actions={{ [workspaceUpdate.name]: workspaceUpdate }}
79
- invocation={invocation}
80
- open={invocation.surface === "page" || open}
81
- onOpenChange={setOpen}
82
- onInvocationChange={(next) => {
83
- if (next) setInvocation(next);
84
- else setOpen(false);
85
- }}
86
- />
87
- </div>
55
+ <DocBrowserActionProvider>
56
+ <div className="space-y-4">
57
+ <ButtonGroup mode="spaced">
58
+ {["page", "dialog", "drawer"].map((value) => (
59
+ <Button
60
+ key={value}
61
+ size="sm"
62
+ variant={invocation.surface === value ? "solid" : "outline"}
63
+ onClick={() => {
64
+ setInvocation({ ...invocation, surface: value });
65
+ setOpen(true);
66
+ }}
67
+ >
68
+ {value}
69
+ </Button>
70
+ ))}
71
+ </ButtonGroup>
72
+
73
+ <Presentation
74
+ definition={workspacePresentation}
75
+ definitions={[workspacePresentation]}
76
+ actions={{ [docWorkspaceCreate.name]: docWorkspaceCreate }}
77
+ invocation={invocation}
78
+ open={invocation.surface === "page" || open}
79
+ onOpenChange={setOpen}
80
+ onInvocationChange={(next) => {
81
+ if (next) setInvocation(next);
82
+ else setOpen(false);
83
+ }}
84
+ />
85
+ </div>
86
+ </DocBrowserActionProvider>
88
87
  );
89
88
  }
90
89
 
@@ -174,6 +173,8 @@ no manifest e concentre a inspeção na Lens em vez de criar um launcher flutuan
174
173
  | `bindingContext` | `PresentationBindingContext` | | Rota, item, seleção, sessão e resultado disponíveis. |
175
174
  | `onInvocationChange` | `(next: PresentationInvocation \| null) => void` | | Recebe navegação, retorno e fechamento. |
176
175
  | `onRefresh` | `(action: string \| null) => void` | | Recebe invalidações declaradas após sucesso. |
176
+ | `listState` | `ActionListState` | | Recorte controlado da action `list` do body, quando a aplicação o sincroniza com a URL. |
177
+ | `onListStateChange` | `(state: ActionListState) => void` | | Recebe cada mudança de recorte ou exibição da lista do body, como busca, filtros, período e página. |
177
178
  | `open` | `boolean` | `true` | Estado controlado de Dialog ou Drawer. |
178
179
  | `onOpenChange` | `(open: boolean) => void` | | Notifica abertura e fechamento da superfície modal. |
179
180
  | `className` | `string` | | Classes adicionais da superfície. |
@@ -29,13 +29,9 @@ render(
29
29
  <span className="text-muted-foreground">{step}%</span>
30
30
  </div>
31
31
  <Progress value={step} />
32
- <button
33
- type="button"
34
- onClick={advance}
35
- className="rounded-md border px-3 py-1.5 text-sm hover:bg-muted"
36
- >
32
+ <Button variant="outline" size="sm" onClick={advance}>
37
33
  Avançar etapa
38
- </button>
34
+ </Button>
39
35
  </div>,
40
36
  )
41
37
  ```
@@ -38,16 +38,17 @@ await runtime.start()
38
38
 
39
39
  | Capacidade | Fornece | Driver pronto |
40
40
  |---|---|---|
41
- | `server` | monta as actions (HTTP) | `fastifyServer` — `@softize/opus/server/fastify` |
41
+ | `server` | monta as actions (HTTP) | `fastifyServer` — `@softize/opus/server/fastify` · `nodeServer` — `…/server/node` |
42
42
  | `data` | `ctx.db` + drift-check + CRUD | `kyselyData`, `kyselyRepo`, `crudActions` — `@softize/opus/data/kysely` |
43
43
  | `auth` | `user`/`tenantId`/`can` do contexto | `jwtAuth` — `…/auth/jwt` · `betterAuthSession` — `…/auth/better-auth` |
44
44
  | `audit` | trilha por execução de action | `pgAudit` — `…/audit/pg` · `consoleAudit` — `…/audit/console` |
45
- | `log` | `ctx.log` estruturado | `pinoLogger` — `@softize/opus/log/pino` |
46
- | `observability` | span ativo + propagação de trace | porta no core; driver opt-in |
45
+ | `logger` | `ctx.log` estruturado | `pinoLogger` — `@softize/opus/log/pino` |
46
+ | `observability` | span ativo + propagação de trace | `openTelemetryObservability` `@softize/opus/observability/opentelemetry` |
47
47
  | `eventBus` | `ctx.emit` + **reactions** | `mittEvents` — `@softize/opus/events/mitt` |
48
48
  | `queue` | jobs em background | `bullmqQueue` — `@softize/opus/queue/bullmq` |
49
49
  | `scheduler` | **schedules** (cron/intervalo) | `nodeCronScheduler` — `@softize/opus/scheduler/node-cron` |
50
50
  | `storage` | `ctx.storage` (arquivos) | `fsStorage` — `…/storage/fs` · `s3Storage` — `…/storage/s3` |
51
+ | `cache` | `ctx.cache` (leitura, experimental) | `memoryCache` — `@softize/opus/cache/memory` |
51
52
  | `ai` | `ctx.ai` (complete/extract) | `anthropicAi` — `@softize/opus/ai/anthropic` |
52
53
  | `client` | chamar actions de fora (stubs) | `fetchClient` — `@softize/opus/client/fetch` |
53
54
 
@@ -61,7 +62,7 @@ Fora do runtime, mas parte do protocolo: o harness de teste (`runAction`/`testCo
61
62
  > e o handler decide como degradar.
62
63
 
63
64
  `user`/`tenantId`/`can` (auth) · `db` (data) · `log` (logger) · `emit` (eventBus) ·
64
- `storage` (storage) · `ai` (ai) · `provenance` (quem disparou: http, schedule,
65
+ `storage` (storage) · `cache` (cache) · `ai` (ai) · `provenance` (quem disparou: http, schedule,
65
66
  reaction…) · `trace` (quando configurado) · `meta`.
66
67
 
67
68
  O core não depende de OpenTelemetry. O `ObservabilityAdapter` envolve actions e reactions
@@ -85,7 +86,9 @@ representa span de erro. Reactions usam `resultKind: 'void'` e rejeitam em falha
85
86
  > tempo dispara schedule (que executa uma action, com provenance própria).
86
87
 
87
88
  Declarados como as actions e registrados no mesmo `runtime.register` — o manifest
88
- projeta os três (o Maestro mostra o wiring em Visão geral).
89
+ projeta os três. `opus introspect` lista reactions, schedules e o wiring entre eles, mas por
90
+ enquanto só reconhece actions declaradas com `defineAction`: as do split `defineContract` +
91
+ `bindAction` ficam de fora do modelo que ele monta.
89
92
 
90
93
  ## Referência profunda
91
94
 
@@ -19,7 +19,7 @@ interface ScheduleDef {
19
19
  action: string // action do registry, executada quando dispara
20
20
  cron?: string // '0 9 * * *' (9h todo dia)
21
21
  every?: string // '1h', '30m', '15s' — atalho de intervalo
22
- timezone?: string // IANA, ex.: 'America/Sao_Paulo' (default UTC)
22
+ timezone?: string // IANA, ex.: 'America/Sao_Paulo' (sem ele: hora local do processo)
23
23
  input?: unknown | (() => unknown | Promise<unknown>) // estático ou dinâmico
24
24
  enabled?: boolean
25
25
  }
@@ -26,8 +26,9 @@ render(
26
26
 
27
27
  ## Lista pesquisável
28
28
 
29
- Use `searchable` quando a quantidade ou os rótulos dificultarem encontrar uma opção. O próprio
30
- campo passa a filtrar `label`, `hint` e `value` com a busca do cmdk.
29
+ Use `searchable` quando a quantidade ou os rótulos dificultarem encontrar uma opção. O gatilho
30
+ continua exibindo a escolha; a busca aparece em uma linha no topo da lista e filtra `label`, `hint`
31
+ e `value`.
31
32
 
32
33
  ```tsx preview col md
33
34
  const [issue, setIssue] = useState('')
@@ -40,7 +41,7 @@ render(
40
41
  id="issue"
41
42
  value={issue}
42
43
  onChange={setIssue}
43
- placeholder="Buscar item…"
44
+ placeholder="Selecione a issue"
44
45
  options={[
45
46
  { value: '412', label: 'Ajustar microcopy do handoff', hint: 'SOF-412' },
46
47
  { value: '418', label: 'Preview da sessão cai após deploy', hint: 'SOF-418' },
@@ -323,7 +324,7 @@ render(
323
324
  { value: 'gra-7', label: 'Filtro por filial', hint: 'GRA-7' },
324
325
  ]}
325
326
  trailing={
326
- <Button variant="ghost" size="icon-xs">
327
+ <Button variant="ghost" size="icon-xs" aria-label="Abrir tarefa">
327
328
  <FileText />
328
329
  </Button>
329
330
  }
@@ -338,12 +339,12 @@ render(
338
339
  | `options` | `SelectOption[]` | | As opções: `{ value, label, hint?, content?, triggerLabel?, group?, disabled? }`. |
339
340
  | `value` | `string \| string[]` | | O selecionado: string no single, string[] no multiple. |
340
341
  | `onChange` | `(value: string) => void \| (value: string[]) => void` | | Chamado ao escolher (e ao remover chip, no multiple) — a assinatura segue o modo. |
341
- | `native` | `boolean` | `false` | Renderiza o `<select>` do sistema. Exclui busca, multi e ghost (o browser é quem desenha a lista). |
342
- | `searchable` | `boolean` | `false` | O campo vira busca: filtra a lista enquanto digita. |
342
+ | `native` | `boolean` | `false` | Renderiza o `<select>` do sistema. Exclui busca, seleção múltipla e variantes visuais (o browser é quem desenha a lista). |
343
+ | `searchable` | `boolean` | `false` | Acrescenta uma linha de busca no topo da lista, que filtra as opções enquanto a pessoa digita. O gatilho continua exibindo a escolha. |
343
344
  | `multiple` | `boolean` | `false` | Chips removíveis, lista que permanece aberta e Selecionar tudo. |
344
- | `variant` | `'default' \| 'ghost'` | `'default'` | `ghost` = sem moldura, para barra do composer. |
345
+ | `variant` | `'default' \| 'outline' \| 'ghost'` | `'default'` | `outline` = compacto com borda; `ghost` = compacto sem borda nem fundo, para barra do composer. |
345
346
  | `placeholder` | `string` | `'Selecione…'` | Texto do campo vazio. |
346
- | `searchPlaceholder` | `string` | | Placeholder enquanto busca; cai para o `placeholder` se ausente. |
347
+ | `searchPlaceholder` | `string` | `'Buscar…'` | Placeholder da linha de busca dentro da lista. |
347
348
  | `emptyText` | `string` | `'Nada encontrado.'` | Mensagem quando a busca não acha nada. |
348
349
  | `onSearch` | `(query: string) => void` | | Busca server-side (debounced, ao abrir e ao digitar): desliga o filtro do cmdk — o pai atualiza `options`. |
349
350
  | `loading` | `boolean` | | Mostra Buscando… enquanto o fetch corre (use com `onSearch`). |
@@ -351,6 +352,10 @@ render(
351
352
  | `icon` | `React.ReactNode` | | Ícone leading DENTRO do controle (decorativo) — herda `size-4` e o tom muted. |
352
353
  | `trailing` | `React.ReactNode` | | Ação custom no FIM do controle (antes do chevron) — o clique não abre a lista. |
353
354
  | `size` | `'default' \| 'sm'` | `'default'` | Altura: default (h-9, a do Input e do Button) ou sm (h-8) para toolbar densa. |
355
+ | `shape` | `'default' \| 'pill'` | `'default'` | Geometria do controle; `pill` arredonda as extremidades e preserva a variante visual. |
354
356
  | `disabled` | `boolean` | `false` | Esmaece e trava o controle. |
355
357
  | `id` | `string` | | Vai para o campo — para parear com o `htmlFor` do Label. |
358
+ | `aria-label` | `string` | | Nome acessível quando não há `Label` associado. |
359
+ | `aria-invalid` | `boolean` | | Comunica e apresenta o estado inválido. |
360
+ | `aria-describedby` | `string` | | Liga o controle a uma ajuda ou mensagem de erro. |
356
361
  | `className` | `string` | | Classes da raiz do controle, incluindo campo, ícones e ações, em todos os modos. |
@@ -63,7 +63,7 @@ uma sidebar fixa, redimensionável ou recolhida.
63
63
  | `PaneFooter` | Mantém ações persistentes no rodapé. |
64
64
  | `SidebarNav` | Apresenta e controla os destinos de navegação. |
65
65
 
66
- `PaneContent` permanece como alias temporário de `PaneBody` durante a versão 12. Código novo usa
66
+ `PaneContent` continua exportado como alias depreciado de `PaneBody`. Código novo usa
67
67
  `PaneBody`.
68
68
 
69
69
  ## Escolher a navegação
@@ -138,7 +138,8 @@ páginas são folhas. Um grupo começa aberto e volta a abrir quando contém a p
138
138
 
139
139
  `collapsed` pertence à `Sidebar`. Nesse estado, `SidebarItem` e `SidebarNav` mantêm
140
140
  somente os ícones e expõem os rótulos em tooltips. Por isso, todo destino que aparece no modo
141
- recolhido precisa de um ícone reconhecível e de um `label` completo.
141
+ recolhido precisa de um ícone reconhecível e de um `label` completo. `SidebarNav` já monta o
142
+ `TooltipProvider`; um `SidebarItem` recolhido fora dele depende do provider na raiz do aplicativo.
142
143
 
143
144
  O slot de ícone do `SidebarItem` ocupa `1rem` nos dois estados e normaliza SVGs para essa medida.
144
145
  O consumidor escolhe o símbolo e sua cor sem precisar repetir largura ou altura em ícones SVG.
@@ -15,7 +15,7 @@ avatares e barras com dimensões próximas às linhas de texto esperadas.
15
15
 
16
16
  ## Card em carregamento
17
17
 
18
- O esqueleto reproduz o layout final do card — título, descrição, conteúdo e ações — para tela não pular quando os dados chegarem.
18
+ O esqueleto reproduz o layout final do card — título, descrição, conteúdo e ações — para a tela não pular quando os dados chegarem.
19
19
 
20
20
  ```tsx preview col
21
21
  <Card>
@@ -15,9 +15,9 @@ representa um único controle. `min`, `max` e `step` delimitam os valores dispon
15
15
  const [parallelism, setParallelism] = useState([4])
16
16
 
17
17
  render(
18
- <div className="grid w-full gap-3">
18
+ <div role="group" aria-labelledby="parallelism-label" className="grid w-full gap-3">
19
19
  <div className="flex items-center justify-between">
20
- <Label>Sessões em paralelo</Label>
20
+ <Label id="parallelism-label">Sessões em paralelo</Label>
21
21
  <span className="text-sm text-muted-foreground">{parallelism[0]}</span>
22
22
  </div>
23
23
  <Slider value={parallelism} onValueChange={setParallelism} min={1} max={8} step={1} />
@@ -33,9 +33,9 @@ Dois números em `value` criam um intervalo selecionável entre dois controles.
33
33
  const [budget, setBudget] = useState([20, 60])
34
34
 
35
35
  render(
36
- <div className="grid w-full gap-3">
36
+ <div role="group" aria-labelledby="budget-label" className="grid w-full gap-3">
37
37
  <div className="flex items-center justify-between">
38
- <Label>Custo estimado (US$)</Label>
38
+ <Label id="budget-label">Custo estimado (US$)</Label>
39
39
  <span className="text-sm text-muted-foreground">{budget[0]} – {budget[1]}</span>
40
40
  </div>
41
41
  <Slider value={budget} onValueChange={setBudget} min={0} max={100} step={5} />
@@ -14,11 +14,11 @@ de um `lg`. O ícone é decorativo; quem nomeia a espera é o contêiner (`role=
14
14
 
15
15
  ## No botão
16
16
 
17
- Durante uma ação, combine o spinner compacto com o estado desabilitado e um rótulo que descreva o
18
- andamento.
17
+ Durante uma ação, use `busy` no `Button` com um rótulo que descreva o andamento. O botão fica
18
+ desabilitado e o spinner ocupa o lugar do ícone, no glifo do tamanho do botão.
19
19
 
20
20
  ```tsx preview
21
- <Button disabled><Spinner size="sm" /> Publicando…</Button>
21
+ <Button busy>Publicando…</Button>
22
22
  ```
23
23
 
24
24
  ## Em carga de conteúdo
@@ -4,12 +4,15 @@ Use `Tabs` para alternar painéis relacionados no mesmo contexto. O `value` de c
4
4
  corresponde ao `TabsContent` que ele abre. `defaultValue` define a aba inicial no modo não
5
5
  controlado.
6
6
 
7
+ `icon` posiciona um ícone decorativo antes do rótulo e normaliza SVGs para `1rem`. Quando a aba
8
+ mostrar somente o ícone, declare `aria-label` para preservar seu nome acessível.
9
+
7
10
  ```tsx preview col
8
11
  <Tabs defaultValue="sessions">
9
12
  <TabsList>
10
- <TabsTrigger value="overview">Visão geral</TabsTrigger>
11
- <TabsTrigger value="sessions">Sessões</TabsTrigger>
12
- <TabsTrigger value="skills">Skills</TabsTrigger>
13
+ <TabsTrigger value="overview" icon={<LayoutDashboard />}>Visão geral</TabsTrigger>
14
+ <TabsTrigger value="sessions" icon={<History />}>Sessões</TabsTrigger>
15
+ <TabsTrigger value="skills" icon={<Settings />}>Skills</TabsTrigger>
13
16
  </TabsList>
14
17
  <TabsContent value="overview">
15
18
  <p className="text-sm text-muted-foreground">Resumo do workspace Empresa X.</p>
@@ -25,8 +28,14 @@ controlado.
25
28
 
26
29
  ## Variante line
27
30
 
28
- Use `variant="line"` em `TabsList` quando a lista precisar se integrar a uma borda, como em um
29
- cabeçalho. A aba ativa é marcada por uma linha em vez de uma superfície preenchida.
31
+ Use `variant="line"` em `TabsList` quando a navegação precisar de uma linha-base, como em um
32
+ cabeçalho. Na orientação horizontal, a lista ocupa toda a largura disponível e a aba ativa é
33
+ marcada sobre essa linha, em vez de usar uma superfície preenchida. Dentro de `DrawerBody`, a
34
+ linha atravessa o gutter até as bordas do drawer sem deslocar os rótulos. O mesmo comportamento
35
+ vale para `DialogBody`.
36
+ Os itens removem o padding horizontal, usam `0.75rem` de padding vertical e preservam `1.25rem`
37
+ de espaço entre si. Nessa orientação, a altura é definida pelo conteúdo para que o padding
38
+ vertical amplie a área interativa.
30
39
 
31
40
  ```tsx preview col
32
41
  <Tabs defaultValue="agents">
@@ -49,8 +58,8 @@ cabeçalho. A aba ativa é marcada por uma linha em vez de uma superfície preen
49
58
 
50
59
  ## Vertical
51
60
 
52
- Com `orientation="vertical"`, a lista forma uma coluna e a marca da variante `line` passa para a
53
- lateral do gatilho.
61
+ Com `orientation="vertical"`, a lista forma uma coluna. O exemplo usa a variante padrão; com
62
+ `variant="line"`, a marca da aba ativa passa para a lateral do gatilho.
54
63
 
55
64
  ```tsx preview col
56
65
  <Tabs defaultValue="prompt" orientation="vertical">
@@ -78,9 +87,9 @@ lateral do gatilho.
78
87
  ```tsx preview col
79
88
  <Tabs defaultValue="preview" size="sm">
80
89
  <TabsList>
81
- <TabsTrigger value="preview" className="text-xs">Preview</TabsTrigger>
82
- <TabsTrigger value="code" className="text-xs">Código</TabsTrigger>
83
- <TabsTrigger value="ai" className="text-xs">IA</TabsTrigger>
90
+ <TabsTrigger value="preview">Preview</TabsTrigger>
91
+ <TabsTrigger value="code">Código</TabsTrigger>
92
+ <TabsTrigger value="ai">IA</TabsTrigger>
84
93
  </TabsList>
85
94
  </Tabs>
86
95
  ```
@@ -92,17 +101,18 @@ lateral do gatilho.
92
101
  | `defaultValue` | `string` | | Aba inicial no modo não controlado. |
93
102
  | `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
94
103
  | `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
95
- | `size` | `'default' \| 'sm'` | `'default'` | Altura da lista na escala única dos controles (2.25 e 2rem), aplicada em `TabsList`. |
104
+ | `size` | `'default' \| 'sm'` | `'default'` | Altura da lista na escala única dos controles (2.25 e 2rem), aplicada em `TabsList`. Não altera a altura de `line` horizontal, que acompanha o conteúdo. |
96
105
  | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
97
106
 
98
107
  ## Propriedades de TabsList
99
108
 
100
109
  | Propriedade | Tipo | Padrão | Descrição |
101
110
  |---|---|---|---|
102
- | `variant` | `'default' \| 'line'` | `'default'` | `default` usa uma superfície preenchida; `line` marca a aba ativa junto à borda. |
111
+ | `variant` | `'default' \| 'line'` | `'default'` | `default` usa uma superfície preenchida; `line` cria uma linha-base horizontal de largura total e marca nela a aba ativa. |
103
112
 
104
113
  ## Propriedades de TabsTrigger e TabsContent
105
114
 
106
115
  | Propriedade | Tipo | Padrão | Descrição |
107
116
  |---|---|---|---|
108
117
  | `value` | `string` | | Identificador que associa o gatilho ao painel correspondente. |
118
+ | `icon` | `ReactNode` | | Ícone decorativo antes do rótulo; SVGs usam `1rem`. |
@@ -72,10 +72,12 @@ schema zod — matéria-prima do `mockHandler` (modo design) e de fixtures de te
72
72
  url, datetime) e por nome de campo (email, id, `*At`→data, telefone, cpf/cnpj, nome…), sabor pt-BR.
73
73
 
74
74
  ```ts
75
+ import { entityRowSchema } from '@softize/opus/schema'
75
76
  import { fake, fakeMany } from '@softize/opus/testing'
76
77
 
77
- const one = fake(EventEntity.zod()) // 1 evento válido (safeParse passa)
78
- const many = fakeMany(EventEntity.zod(), 20) // 20, estáveis entre execuções
78
+ const eventRowSchema = entityRowSchema(EventEntity) // schema da linha da entidade
79
+ const one = fake(eventRowSchema) // 1 evento válido (safeParse passa)
80
+ const many = fakeMany(eventRowSchema, 20) // 20, estáveis entre execuções
79
81
  fake(schema, { seed: 7 }) // seed própria
80
82
  ```
81
83
 
@@ -3,7 +3,8 @@
3
3
  Use `toast` para informar o resultado temporário de uma ação sem interromper o fluxo. Cada tipo usa o
4
4
  ícone correspondente do Opus. Escreva o título como rótulo, sem ponto final, e a descrição como uma
5
5
  frase. A moldura do ícone permanece quadrada e alinhada à primeira linha mesmo quando a descrição
6
- ocupa várias linhas.
6
+ ocupa várias linhas. Corpo e padding seguem os mesmos `0.875rem` e `0.75rem` do Alert; os defaults
7
+ internos do Sonner não definem a densidade da casa.
7
8
 
8
9
  ```tsx preview
9
10
  <Button variant="outline" onClick={() => toast.success('Workspace criado')}>Sucesso</Button>
@@ -30,11 +31,9 @@ hierarquia precisar ser diferente. A região fica alinhada ao fim lógico da sup
30
31
  canto usado pelas ações do Alert. O clique fecha o toast, salvo quando o handler chama
31
32
  `event.preventDefault()`.
32
33
 
33
- Na versão 13, `actions` substitui os campos `action` e `cancel` da versão 12. Migre cada controle
34
- para uma entrada da coleção e preserve a ordem visual desejada.
35
-
36
34
  `toast.promise` acompanha uma promessa e atualiza a mesma notificação nos estados de carregamento,
37
- sucesso ou erro.
35
+ sucesso ou erro. Ela não aceita `actions`; use `toast` com `actions` quando a notificação precisar
36
+ oferecer uma ação.
38
37
 
39
38
  ```tsx preview
40
39
  <Button
@@ -87,7 +86,7 @@ Monte um único `Toaster` na raiz do aplicativo. O tema vem da propriedade `them
87
86
  |---|---|---|---|
88
87
  | `description` | `React.ReactNode` | | Complemento exibido abaixo do título. |
89
88
  | `actions` | `ToastAction[]` | | Coleção ordenada de ações; a última recebe destaque primário por padrão. |
90
- | `duration` | `number` | do Sonner | Tempo de permanência da notificação. |
89
+ | `duration` | `number` | `4000` | Tempo de permanência da notificação, em milissegundos. O `Toaster` pode mudar o padrão, e `toast.loading` não expira sozinho. |
91
90
 
92
91
  ## Propriedades de ToastAction
93
92
 
@@ -71,6 +71,7 @@ disabled esmaece e bloqueia o clique — o estado pressed permanece visível.
71
71
  | `defaultPressed` | `boolean` | `false` | Estado inicial no modo não controlado. |
72
72
  | `variant` | `'default' \| 'outline'` | `'default'` | default não tem borda (fundo só quando ativo); outline carrega a borda. |
73
73
  | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Altura na escala única dos controles (2 · 2.25 · 2.5rem) — sm para toolbar densa, lg para alvo mais confortável. |
74
+ | `shape` | `'default' \| 'pill'` | `'default'` | Geometria do controle; `pill` arredonda as extremidades. |
74
75
  | `disabled` | `boolean` | `false` | Esmaece e bloqueia o clique, preservando o estado visual. |
75
76
 
76
77
  ## ToggleGroup
@@ -106,6 +107,39 @@ render(
106
107
  )
107
108
  ```
108
109
 
110
+ ### Itens só com ícone e tooltip
111
+
112
+ Quando o item mostra somente um ícone, mantenha o `aria-label` e acrescente uma dica visual. Envolva o
113
+ `ToggleGroupItem` em `TooltipTrigger asChild`: o item preserva o estado selecionado e seus atributos,
114
+ e a dica aparece sem criar outro botão. O `TooltipProvider` da raiz do aplicativo continua necessário.
115
+
116
+ ```tsx preview
117
+ const [align, setAlign] = useState('left')
118
+
119
+ render(
120
+ <ToggleGroup type="single" value={align} onValueChange={(v) => v && setAlign(v)}>
121
+ <Tooltip>
122
+ <TooltipTrigger asChild>
123
+ <ToggleGroupItem value="left" aria-label="Alinhar à esquerda"><AlignLeft /></ToggleGroupItem>
124
+ </TooltipTrigger>
125
+ <TooltipContent>Alinhar à esquerda</TooltipContent>
126
+ </Tooltip>
127
+ <Tooltip>
128
+ <TooltipTrigger asChild>
129
+ <ToggleGroupItem value="center" aria-label="Centralizar"><AlignCenter /></ToggleGroupItem>
130
+ </TooltipTrigger>
131
+ <TooltipContent>Centralizar</TooltipContent>
132
+ </Tooltip>
133
+ <Tooltip>
134
+ <TooltipTrigger asChild>
135
+ <ToggleGroupItem value="right" aria-label="Alinhar à direita"><AlignRight /></ToggleGroupItem>
136
+ </TooltipTrigger>
137
+ <TooltipContent>Alinhar à direita</TooltipContent>
138
+ </Tooltip>
139
+ </ToggleGroup>,
140
+ )
141
+ ```
142
+
109
143
  ### Variante e espaçamento
110
144
 
111
145
  `variant`, `size` e `shape` definidos no grupo chegam aos itens por contexto. `spacing` separa os
@@ -138,3 +172,6 @@ itens; com zero, eles formam um bloco contínuo.
138
172
  |---|---|---|---|
139
173
  | `value` | `string` | | Identificador que entra no valor do grupo quando o item é ativado. |
140
174
  | `disabled` | `boolean` | `false` | Bloqueia somente este item e preserva seu estado visual. |
175
+ | `variant` | `'default' \| 'outline'` | `'default'` | Tratamento do item quando o grupo não declara `variant`; o valor do grupo prevalece. |
176
+ | `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Tamanho do item quando o grupo não declara `size`; o valor do grupo prevalece. |
177
+ | `shape` | `'default' \| 'pill'` | shape do grupo | Geometria deste item; quando informada, prevalece sobre a do grupo. |