@softize/opus 18.1.0 → 18.2.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.
- package/CHANGELOG.md +75 -0
- package/PROMOTED.md +4 -5
- package/README.md +5 -4
- package/bin/cli.mjs +4 -0
- package/docs/adr/0004-page-content-state-is-composed.md +3 -0
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +6 -2
- package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +6 -1
- package/docs/adr/0014-structural-headers-do-not-carry-description.md +2 -2
- package/docs/adr/0015-action-size-follows-interaction-density.md +4 -3
- package/docs/adr/{0012-modal-header-only-names-the-surface.md → 0018-modal-header-only-names-the-surface.md} +4 -1
- package/docs/adr/{0016-productive-surfaces-use-compact-density.md → 0019-productive-surfaces-use-compact-density.md} +4 -1
- package/docs/code-style.md +2 -2
- package/docs/consumer-upgrade-propagation.md +1 -1
- package/docs/data-products.md +5 -3
- package/docs/protocol.md +6 -6
- package/docs/releasing.md +28 -4
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/references/ui-patterns.md +11 -1
- package/src/auth/drivers/jwt.ts +2 -1
- package/src/core/presentation.ts +143 -58
- package/src/core/runtime.ts +25 -4
- package/src/core/types.ts +4 -4
- package/src/mcp/index.ts +9 -0
- package/src/ui/components/patterns/content-header.tsx +1 -1
- package/src/ui/components/patterns/form.tsx +1 -1
- package/src/ui/components/patterns/presentation.tsx +199 -60
- package/src/ui/components/patterns/sidebar.tsx +1 -1
- package/src/ui/components/patterns/split.tsx +5 -2
- package/src/ui/components/patterns/surface-assistant.tsx +74 -0
- package/src/ui/components/primitives/card.tsx +1 -1
- package/src/ui/components/primitives/detail.tsx +7 -7
- package/src/ui/components/primitives/radio-group.tsx +1 -1
- package/src/ui/components/primitives/select.tsx +1 -1
- package/src/ui/docs/content/action-form-dialog.md +11 -4
- package/src/ui/docs/content/action-form.md +7 -16
- package/src/ui/docs/content/action-list-dialog.md +4 -6
- package/src/ui/docs/content/action-list.md +46 -4
- package/src/ui/docs/content/action-trigger.md +9 -5
- package/src/ui/docs/content/action-view.md +12 -8
- package/src/ui/docs/content/actions.md +36 -13
- package/src/ui/docs/content/ai.md +26 -7
- package/src/ui/docs/content/alert.md +4 -3
- package/src/ui/docs/content/aspect-ratio.md +2 -2
- package/src/ui/docs/content/auth.md +25 -10
- package/src/ui/docs/content/avatar.md +1 -1
- package/src/ui/docs/content/badge.md +2 -2
- package/src/ui/docs/content/breadcrumb.md +3 -2
- package/src/ui/docs/content/button.md +31 -7
- package/src/ui/docs/content/calendar.md +1 -1
- package/src/ui/docs/content/card.md +1 -1
- package/src/ui/docs/content/carousel.md +14 -3
- package/src/ui/docs/content/chat.md +1 -1
- package/src/ui/docs/content/cli.md +13 -7
- package/src/ui/docs/content/command.md +34 -2
- package/src/ui/docs/content/composer.md +1 -1
- package/src/ui/docs/content/content.md +5 -4
- package/src/ui/docs/content/customization.md +1 -1
- package/src/ui/docs/content/cycle.md +7 -5
- package/src/ui/docs/content/data-state.md +6 -5
- package/src/ui/docs/content/data.md +3 -3
- package/src/ui/docs/content/detail.md +3 -2
- package/src/ui/docs/content/dialog.md +2 -2
- package/src/ui/docs/content/dictionary-value.md +1 -1
- package/src/ui/docs/content/dock.md +23 -2
- package/src/ui/docs/content/dot.md +0 -2
- package/src/ui/docs/content/drawer.md +1 -1
- package/src/ui/docs/content/empty.md +1 -4
- package/src/ui/docs/content/events.md +1 -1
- package/src/ui/docs/content/field.md +20 -11
- package/src/ui/docs/content/getting-started.md +4 -2
- package/src/ui/docs/content/icon-picker.md +2 -2
- package/src/ui/docs/content/input-otp.md +2 -0
- package/src/ui/docs/content/input.md +2 -3
- package/src/ui/docs/content/item.md +5 -4
- package/src/ui/docs/content/kbd.md +2 -1
- package/src/ui/docs/content/mcp.md +10 -4
- package/src/ui/docs/content/menu.md +27 -0
- package/src/ui/docs/content/page.md +19 -5
- package/src/ui/docs/content/pagination.md +9 -2
- package/src/ui/docs/content/popover.md +2 -2
- package/src/ui/docs/content/presentation.md +85 -58
- package/src/ui/docs/content/progress.md +2 -6
- package/src/ui/docs/content/runtime.md +8 -5
- package/src/ui/docs/content/scheduler.md +1 -1
- package/src/ui/docs/content/select.md +13 -8
- package/src/ui/docs/content/sidebar.md +3 -2
- package/src/ui/docs/content/skeleton.md +1 -1
- package/src/ui/docs/content/slider.md +4 -4
- package/src/ui/docs/content/spinner.md +3 -3
- package/src/ui/docs/content/split.md +80 -19
- package/src/ui/docs/content/tabs.md +6 -6
- package/src/ui/docs/content/testing.md +4 -2
- package/src/ui/docs/content/toast.md +3 -5
- package/src/ui/docs/content/toggle.md +37 -0
- package/src/ui/docs/content/tooltip.md +4 -3
- package/src/ui/docs/content/truncate.md +3 -2
- package/src/ui/docs/content/ui.md +3 -1
- package/src/ui/docs/content/upgrading.md +43 -13
- package/src/ui/docs/doc-client.tsx +1 -1
- package/src/ui/docs/registry.tsx +30 -5
- package/src/ui/meta.ts +5 -5
- package/src/ui/react.tsx +5 -0
|
@@ -22,13 +22,13 @@ salvo em listas, cards e árvores.
|
|
|
22
22
|
|
|
23
23
|
```tsx preview col md
|
|
24
24
|
const [icon, setIcon] = useState('store')
|
|
25
|
-
const
|
|
25
|
+
const SelectedIcon = iconPickerIcons[icon] ?? FileText
|
|
26
26
|
|
|
27
27
|
render(
|
|
28
28
|
<div className="flex items-center gap-3">
|
|
29
29
|
<IconPicker value={icon} onChange={setIcon} className="w-56" />
|
|
30
30
|
<span className="flex items-center gap-2 rounded-md border px-3 py-2 text-sm">
|
|
31
|
-
<
|
|
31
|
+
<SelectedIcon className="size-4 text-muted-foreground" />
|
|
32
32
|
Painel comercial
|
|
33
33
|
</span>
|
|
34
34
|
</div>,
|
|
@@ -71,6 +71,8 @@ render(
|
|
|
71
71
|
| `maxLength` | `number` | | Quantidade de posições do código. Defina um `InputOTPSlot` para cada posição. |
|
|
72
72
|
| `value` | `string` | | Código no modo controlado. Use com `onChange`. |
|
|
73
73
|
| `onChange` | `(value: string) => void` | | Chamado a cada entrada com o código acumulado. |
|
|
74
|
+
| `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+$'`. |
|
|
74
76
|
| `disabled` | `boolean` | `false` | Desabilita a digitação em todo o conjunto. |
|
|
75
77
|
|
|
76
78
|
## Propriedades de InputOTPSlot
|
|
@@ -21,7 +21,6 @@ como calendário em uma data; o `Label` continua responsável pelo nome acessív
|
|
|
21
21
|
|
|
22
22
|
Quando o campo precisar combinar vários adornos, prefixos ou botões, use `InputGroup`.
|
|
23
23
|
|
|
24
|
-
|
|
25
24
|
## Ação no fim do campo (trailing)
|
|
26
25
|
|
|
27
26
|
`trailing` posiciona no fim do campo uma ação relacionada ao valor, como limpar ou mostrar uma
|
|
@@ -37,7 +36,7 @@ function Demo() {
|
|
|
37
36
|
onChange={(e) => setQ(e.target.value)}
|
|
38
37
|
trailing={
|
|
39
38
|
q ? (
|
|
40
|
-
<Button variant="ghost" size="icon-xs" onClick={() => setQ('')}>
|
|
39
|
+
<Button variant="ghost" size="icon-xs" aria-label="Limpar busca" onClick={() => setQ('')}>
|
|
41
40
|
<X />
|
|
42
41
|
</Button>
|
|
43
42
|
) : undefined
|
|
@@ -167,7 +166,7 @@ Com `InputGroupTextarea`, um addon em `block-end` forma uma região de ações a
|
|
|
167
166
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
168
167
|
|---|---|---|---|
|
|
169
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. |
|
|
170
|
-
| `context` | `'neutral' \| 'primary' \| 'danger'` | `'
|
|
169
|
+
| `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'`. |
|
|
171
170
|
| `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'ghost'` | Tratamento visual; use `solid` quando a ação precisar de ênfase. |
|
|
172
171
|
|
|
173
172
|
### Propriedades de InputGroupInput
|
|
@@ -4,7 +4,8 @@ A composição completa usa `ItemMedia` à esquerda, `ItemHeader` (`ItemTitle` +
|
|
|
4
4
|
`ItemDescription`) no meio e `ItemActions` à direita. Ícone e imagem mantêm uma moldura quadrada
|
|
5
5
|
alinhada ao topo, mesmo quando a descrição ocupa mais linhas. `variant="outline"` desenha a borda.
|
|
6
6
|
O tamanho padrão usa corpo produtivo, `0.75rem` de padding e `0.75rem` entre regiões. `size="sm"`
|
|
7
|
-
mantém o
|
|
7
|
+
mantém o padding horizontal, reduz o padding vertical para `0.5rem` e o espaço entre regiões para
|
|
8
|
+
`0.625rem` em listas densas.
|
|
8
9
|
|
|
9
10
|
```tsx preview col
|
|
10
11
|
<Item variant="outline">
|
|
@@ -61,7 +62,7 @@ mantém o alinhamento horizontal e reduz o padding vertical para `0.5rem` em lis
|
|
|
61
62
|
|
|
62
63
|
## Clicável e compacto
|
|
63
64
|
|
|
64
|
-
asChild funde o Item em um
|
|
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.
|
|
65
66
|
|
|
66
67
|
```tsx preview col
|
|
67
68
|
<Item asChild size="sm" variant="outline">
|
|
@@ -99,7 +100,7 @@ Use `ItemBody` quando a linha também apresenta um valor ou controle que não pe
|
|
|
99
100
|
</Item>
|
|
100
101
|
```
|
|
101
102
|
|
|
102
|
-
`ItemContent` permanece exportado
|
|
103
|
+
`ItemContent` permanece exportado apenas por compatibilidade e está obsoleto. Código novo
|
|
103
104
|
usa `ItemHeader` ou `ItemBody` conforme o papel do conteúdo.
|
|
104
105
|
|
|
105
106
|
## Propriedades de Item
|
|
@@ -124,4 +125,4 @@ usa `ItemHeader` ou `ItemBody` conforme o papel do conteúdo.
|
|
|
124
125
|
|
|
125
126
|
`ItemHeader`, `ItemTitle`, `ItemDescription`, `ItemBody`, `ItemActions`, `ItemFooter` e
|
|
126
127
|
`ItemSeparator` aceitam as props nativas dos respectivos elementos e não adicionam propriedades
|
|
127
|
-
próprias. `ItemContent` preserva esse mesmo contrato
|
|
128
|
+
próprias. O `ItemContent` obsoleto preserva esse mesmo contrato por compatibilidade.
|
|
@@ -27,7 +27,7 @@ reconhecível que o nome da tecla.
|
|
|
27
27
|
```tsx preview col-start
|
|
28
28
|
<KbdGroup>
|
|
29
29
|
<Kbd>
|
|
30
|
-
<
|
|
30
|
+
<CommandIcon />
|
|
31
31
|
</Kbd>
|
|
32
32
|
<Kbd>
|
|
33
33
|
<CornerDownLeft />
|
|
@@ -49,6 +49,7 @@ ação antes de informar seu atalho.
|
|
|
49
49
|
Buscar workspace
|
|
50
50
|
<KbdGroup>
|
|
51
51
|
<Kbd>⌘</Kbd>
|
|
52
|
+
<span>+</span>
|
|
52
53
|
<Kbd>K</Kbd>
|
|
53
54
|
</KbdGroup>
|
|
54
55
|
</TooltipContent>
|
|
@@ -17,16 +17,22 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
17
17
|
const server = createOpusMcpServer(runtime, {
|
|
18
18
|
name: 'meu-app',
|
|
19
19
|
// Valida a identidade da requisição (IdP) → contexto do runtime. Sem isso, roda anônimo
|
|
20
|
-
// (
|
|
20
|
+
// (sem usuário, só actions `public` rodam). Com isso, o agente age COMO O USUÁRIO.
|
|
21
21
|
resolveContext: async (extra) => auth.resolveFromMcp(extra),
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
await server.connect(new StdioServerTransport()) // local; HTTP/SSE para remoto
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
`
|
|
29
|
-
|
|
27
|
+
Só actions marcadas com `ai: { enabled: true }` ficam disponíveis, tanto para listar quanto para
|
|
28
|
+
executar. `ListTools` devolve cada uma com `name` (identificador técnico), `title` (a `label`
|
|
29
|
+
legível, quando existe), descrição, JSON Schema do input e, em `_meta`, os Produtos de Dados
|
|
30
|
+
relacionados: `com.softize.opus/data-products` (identificadores) e
|
|
31
|
+
`com.softize.opus/data-product-labels` (nome legível por identificador). `CallTool` recusa, com
|
|
32
|
+
erro, qualquer nome fora dessa lista; os demais seguem pelo `runtime.execute` — validação,
|
|
33
|
+
autorização e audit, tudo igual a uma chamada normal. Read-only? Marque só actions de leitura
|
|
34
|
+
com `ai:enabled`: a marcação decide o que o cliente MCP pode executar, e a autorização do
|
|
35
|
+
contexto continua valendo por cima.
|
|
30
36
|
|
|
31
37
|
## Escolher entre integração interna e MCP
|
|
32
38
|
|
|
@@ -104,6 +104,14 @@ MenuSub aninha um nível; inset alinha itens sem ícone com os que têm.
|
|
|
104
104
|
</Menu>
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
+
## Propriedades de Menu
|
|
108
|
+
|
|
109
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `open` | `boolean` | | Estado do painel no modo controlado, como no menu de contexto. |
|
|
112
|
+
| `onOpenChange` | `(open: boolean) => void` | | Chamado quando o painel abre ou fecha. |
|
|
113
|
+
| `defaultOpen` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
114
|
+
|
|
107
115
|
## Propriedades de MenuItem
|
|
108
116
|
|
|
109
117
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
@@ -137,3 +145,22 @@ próprias além das de DOM.
|
|
|
137
145
|
|---|---|---|---|
|
|
138
146
|
| `value` | `string` | | Valor selecionado no grupo. |
|
|
139
147
|
| `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outro `MenuRadioItem`. |
|
|
148
|
+
|
|
149
|
+
## Propriedades de MenuRadioItem
|
|
150
|
+
|
|
151
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
152
|
+
|---|---|---|---|
|
|
153
|
+
| `value` | `string` | | Valor que o item entrega ao `MenuRadioGroup` quando é selecionado. |
|
|
154
|
+
| `disabled` | `boolean` | `false` | Impede a seleção do item. |
|
|
155
|
+
|
|
156
|
+
## Propriedades de MenuLabel
|
|
157
|
+
|
|
158
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
159
|
+
|---|---|---|---|
|
|
160
|
+
| `inset` | `boolean` | | Alinha o rótulo com itens que possuem ícone. |
|
|
161
|
+
|
|
162
|
+
## Propriedades de MenuSubTrigger
|
|
163
|
+
|
|
164
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
165
|
+
|---|---|---|---|
|
|
166
|
+
| `inset` | `boolean` | | Alinha o gatilho do submenu sem ícone com os itens que possuem ícone. |
|
|
@@ -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
|
|
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>
|
|
185
|
+
action={<Button>Criar relatório</Button>}
|
|
176
186
|
/>
|
|
177
187
|
</Page>
|
|
178
188
|
```
|
|
@@ -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` |
|
|
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` |
|
|
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`.
|
|
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">
|
|
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">
|
|
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>
|
|
@@ -9,8 +9,10 @@ uma etapa de navegação. Formulários modais dimensionam o footer pelo conteúd
|
|
|
9
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
|
|
13
|
-
|
|
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,29 @@ 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
|
|
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
|
-
|
|
30
|
-
|
|
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
|
+
Quando o recurso aceita assistência contextual, declare somente o gatilho serializável em
|
|
36
|
+
`assistant`. O renderer coloca esse gatilho no cabeçalho e abre o painel à direita do body da
|
|
37
|
+
própria Page, Dialog ou Drawer. Cabeçalho e rodapé continuam ocupando toda a largura da superfície;
|
|
38
|
+
enquanto o painel está aberto, o gatilho sai do cabeçalho e o próprio painel assume sua identidade
|
|
39
|
+
visual. O dado vivo do recurso não entra na definição: a página que o carregou fornece
|
|
40
|
+
`assistant.render`, captura identidade, nome e demais dados autorizados e monta o chat. Assim,
|
|
41
|
+
contexto de conversa não vira autorização nem dado persistido no manifest.
|
|
42
|
+
|
|
43
|
+
```tsx live
|
|
39
44
|
const workspacePresentation = definePresentation({
|
|
40
45
|
schemaVersion: 1,
|
|
41
|
-
id: "workspace.
|
|
42
|
-
title: "
|
|
43
|
-
|
|
46
|
+
id: "workspace.create",
|
|
47
|
+
title: "Criar workspace",
|
|
48
|
+
assistant: { triggerLabel: "Conversar sobre este workspace" },
|
|
49
|
+
body: { action: docWorkspaceCreate.name },
|
|
44
50
|
});
|
|
45
51
|
|
|
46
52
|
function Example() {
|
|
@@ -55,36 +61,54 @@ function Example() {
|
|
|
55
61
|
);
|
|
56
62
|
|
|
57
63
|
return (
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
64
|
+
<DocBrowserActionProvider>
|
|
65
|
+
<div className="space-y-4">
|
|
66
|
+
<ButtonGroup mode="spaced">
|
|
67
|
+
{["page", "dialog", "drawer"].map((value) => (
|
|
68
|
+
<Button
|
|
69
|
+
key={value}
|
|
70
|
+
size="sm"
|
|
71
|
+
variant={invocation.surface === value ? "solid" : "outline"}
|
|
72
|
+
onClick={() => {
|
|
73
|
+
setInvocation({ ...invocation, surface: value });
|
|
74
|
+
setOpen(true);
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{value}
|
|
78
|
+
</Button>
|
|
79
|
+
))}
|
|
80
|
+
</ButtonGroup>
|
|
81
|
+
|
|
82
|
+
<Presentation
|
|
83
|
+
definition={workspacePresentation}
|
|
84
|
+
definitions={[workspacePresentation]}
|
|
85
|
+
actions={{ [docWorkspaceCreate.name]: docWorkspaceCreate }}
|
|
86
|
+
invocation={invocation}
|
|
87
|
+
open={invocation.surface === "page" || open}
|
|
88
|
+
onOpenChange={setOpen}
|
|
89
|
+
onInvocationChange={(next) => {
|
|
90
|
+
if (next) setInvocation(next);
|
|
91
|
+
else setOpen(false);
|
|
92
|
+
}}
|
|
93
|
+
assistant={{
|
|
94
|
+
render: ({ close }) => (
|
|
95
|
+
<div className="flex h-full flex-col p-3">
|
|
96
|
+
<p className="text-sm">
|
|
97
|
+
Conversa vinculada ao workspace carregado pela página.
|
|
98
|
+
</p>
|
|
99
|
+
<Button
|
|
100
|
+
className="mt-3 self-start"
|
|
101
|
+
variant="ghost"
|
|
102
|
+
onClick={close}
|
|
103
|
+
>
|
|
104
|
+
Fechar
|
|
105
|
+
</Button>
|
|
106
|
+
</div>
|
|
107
|
+
),
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
</DocBrowserActionProvider>
|
|
88
112
|
);
|
|
89
113
|
}
|
|
90
114
|
|
|
@@ -165,19 +189,22 @@ no manifest e concentre a inspeção na Lens em vez de criar um launcher flutuan
|
|
|
165
189
|
|
|
166
190
|
## Propriedades de Presentation
|
|
167
191
|
|
|
168
|
-
| Propriedade | Tipo | Padrão | Descrição
|
|
169
|
-
| -------------------- | ------------------------------------------------ | ------ |
|
|
170
|
-
| `definition` | `PresentationDefinition` | | Artefato estático que descreve o recurso.
|
|
171
|
-
| `definitions` | `PresentationDefinition[]` | | Registry alcançável por navegação, abertura e retorno.
|
|
172
|
-
| `actions` | `PresentationActionRegistry` | | Contratos compartilháveis referenciados pelo artefato.
|
|
173
|
-
| `invocation` | `PresentationInvocation` | | Surface, input e pilha da exibição atual.
|
|
174
|
-
| `bindingContext` | `PresentationBindingContext` | | Rota, item, seleção, sessão e resultado disponíveis.
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
192
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
193
|
+
| -------------------- | ------------------------------------------------ | ------ | --------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| `definition` | `PresentationDefinition` | | Artefato estático que descreve o recurso. |
|
|
195
|
+
| `definitions` | `PresentationDefinition[]` | | Registry alcançável por navegação, abertura e retorno. |
|
|
196
|
+
| `actions` | `PresentationActionRegistry` | | Contratos compartilháveis referenciados pelo artefato. |
|
|
197
|
+
| `invocation` | `PresentationInvocation` | | Surface, input e pilha da exibição atual. |
|
|
198
|
+
| `bindingContext` | `PresentationBindingContext` | | Rota, item, seleção, sessão e resultado disponíveis. |
|
|
199
|
+
| `assistant` | `PresentationAssistantRuntime` | | Painel contextual fornecido pela superfície hospedeira. |
|
|
200
|
+
| `onInvocationChange` | `(next: PresentationInvocation \| null) => void` | | Recebe navegação, retorno e fechamento. |
|
|
201
|
+
| `onRefresh` | `(action: string \| null) => void` | | Recebe invalidações declaradas após sucesso. |
|
|
202
|
+
| `listState` | `ActionListState` | | Recorte controlado da action `list` do body, quando a aplicação o sincroniza com a URL. |
|
|
203
|
+
| `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. |
|
|
204
|
+
| `open` | `boolean` | `true` | Estado controlado de Dialog ou Drawer. |
|
|
205
|
+
| `onOpenChange` | `(open: boolean) => void` | | Notifica abertura e fechamento da superfície modal. |
|
|
206
|
+
| `className` | `string` | | Classes adicionais da superfície. |
|
|
207
|
+
| `bodyClassName` | `string` | | Classes adicionais do body, aplicadas uma única vez. |
|
|
181
208
|
|
|
182
209
|
## Propriedades de PresentationInspector
|
|
183
210
|
|
|
@@ -29,13 +29,9 @@ render(
|
|
|
29
29
|
<span className="text-muted-foreground">{step}%</span>
|
|
30
30
|
</div>
|
|
31
31
|
<Progress value={step} />
|
|
32
|
-
<
|
|
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
|
-
</
|
|
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
|
-
| `
|
|
46
|
-
| `observability` | span ativo + propagação de trace |
|
|
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
|
|
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' (
|
|
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
|
|
30
|
-
|
|
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="
|
|
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,
|
|
342
|
-
| `searchable` | `boolean` | `false` |
|
|
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
|
|
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
|
|
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`
|
|
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>
|