@softize/opus 12.11.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/bin/lib/check.mjs +2 -7
- package/bin/lib/copy.mjs +1 -5
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +93 -10
- package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
- package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
- package/docs/radius-scale.md +1 -1
- package/package.json +1 -1
- package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
- package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
- package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
- package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
- package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
- package/src/ui/components/patterns/confirm.tsx +140 -40
- package/src/ui/components/patterns/list.tsx +35 -40
- package/src/ui/components/patterns/page-state.tsx +2 -2
- package/src/ui/components/patterns/sidebar.tsx +26 -26
- package/src/ui/components/patterns/trigger.tsx +25 -22
- package/src/ui/components/primitives/alert.tsx +3 -3
- package/src/ui/components/primitives/dialog.tsx +196 -39
- package/src/ui/components/primitives/drawer.tsx +8 -5
- package/src/ui/components/primitives/empty.tsx +3 -3
- package/src/ui/components/primitives/item.tsx +3 -3
- package/src/ui/components/primitives/sonner.tsx +187 -8
- package/src/ui/docs/DocBrowser.tsx +102 -23
- package/src/ui/docs/content/accordion.md +22 -16
- package/src/ui/docs/content/action-form-card.md +8 -8
- package/src/ui/docs/content/action-form-dialog.md +9 -9
- package/src/ui/docs/content/action-form.md +28 -34
- package/src/ui/docs/content/action-list-dialog.md +11 -6
- package/src/ui/docs/content/action-list.md +64 -39
- package/src/ui/docs/content/action-trigger.md +21 -14
- package/src/ui/docs/content/action-view.md +8 -8
- package/src/ui/docs/content/actions.md +9 -9
- package/src/ui/docs/content/ai.md +3 -3
- package/src/ui/docs/content/alert.md +14 -12
- package/src/ui/docs/content/aspect-ratio.md +4 -4
- package/src/ui/docs/content/audit.md +2 -2
- package/src/ui/docs/content/auth.md +3 -3
- package/src/ui/docs/content/avatar.md +34 -14
- package/src/ui/docs/content/badge.md +3 -3
- package/src/ui/docs/content/breadcrumb.md +13 -8
- package/src/ui/docs/content/button.md +81 -6
- package/src/ui/docs/content/calendar.md +5 -5
- package/src/ui/docs/content/card.md +1 -1
- package/src/ui/docs/content/carousel.md +16 -11
- package/src/ui/docs/content/chat.md +3 -3
- package/src/ui/docs/content/checkbox.md +7 -7
- package/src/ui/docs/content/cli.md +5 -5
- package/src/ui/docs/content/collapsible.md +8 -8
- package/src/ui/docs/content/command.md +16 -8
- package/src/ui/docs/content/composer.md +2 -2
- package/src/ui/docs/content/content.md +2 -2
- package/src/ui/docs/content/copyable.md +4 -3
- package/src/ui/docs/content/customization.md +5 -5
- package/src/ui/docs/content/cycle.md +3 -3
- package/src/ui/docs/content/data-state.md +11 -12
- package/src/ui/docs/content/data.md +26 -33
- package/src/ui/docs/content/detail.md +3 -3
- package/src/ui/docs/content/dialog.md +339 -31
- package/src/ui/docs/content/dictionary-value.md +8 -8
- package/src/ui/docs/content/dock.md +3 -3
- package/src/ui/docs/content/drawer.md +27 -14
- package/src/ui/docs/content/empty-value.md +2 -2
- package/src/ui/docs/content/empty.md +19 -12
- package/src/ui/docs/content/events.md +4 -4
- package/src/ui/docs/content/field.md +34 -12
- package/src/ui/docs/content/getting-started.md +1 -1
- package/src/ui/docs/content/icon-picker.md +8 -4
- package/src/ui/docs/content/input-otp.md +20 -12
- package/src/ui/docs/content/input.md +121 -9
- package/src/ui/docs/content/item.md +27 -13
- package/src/ui/docs/content/kbd.md +19 -11
- package/src/ui/docs/content/label.md +5 -3
- package/src/ui/docs/content/log.md +4 -4
- package/src/ui/docs/content/markdown.md +7 -6
- package/src/ui/docs/content/mcp.md +13 -15
- package/src/ui/docs/content/menu.md +34 -16
- package/src/ui/docs/content/observability.md +2 -2
- package/src/ui/docs/content/page.md +51 -6
- package/src/ui/docs/content/pagination.md +22 -17
- package/src/ui/docs/content/popover.md +16 -8
- package/src/ui/docs/content/progress.md +7 -5
- package/src/ui/docs/content/queue.md +5 -5
- package/src/ui/docs/content/radio-group.md +20 -12
- package/src/ui/docs/content/router.md +11 -6
- package/src/ui/docs/content/scheduler.md +4 -5
- package/src/ui/docs/content/scroll-area.md +12 -7
- package/src/ui/docs/content/select.md +42 -29
- package/src/ui/docs/content/separator.md +5 -5
- package/src/ui/docs/content/sidebar.md +323 -54
- package/src/ui/docs/content/skeleton.md +3 -2
- package/src/ui/docs/content/slider.md +8 -7
- package/src/ui/docs/content/spinner.md +8 -8
- package/src/ui/docs/content/split.md +8 -5
- package/src/ui/docs/content/storage.md +6 -8
- package/src/ui/docs/content/switch.md +8 -7
- package/src/ui/docs/content/table.md +13 -3
- package/src/ui/docs/content/tabs.md +28 -14
- package/src/ui/docs/content/testing.md +9 -11
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +47 -13
- package/src/ui/docs/content/toggle.md +75 -7
- package/src/ui/docs/content/tokens.md +3 -3
- package/src/ui/docs/content/tooltip.md +19 -11
- package/src/ui/docs/content/truncate.md +7 -8
- package/src/ui/docs/content/ui.md +10 -9
- package/src/ui/docs/content/upgrading.md +7 -8
- package/src/ui/docs/registry.tsx +20 -37
- package/src/ui/meta.ts +64 -94
- package/src/ui/react.tsx +15 -16
- package/src/ui/theme.css +50 -0
- package/src/ui/components/primitives/alert-dialog.tsx +0 -192
- package/src/ui/docs/content/alert-dialog.md +0 -73
- package/src/ui/docs/content/button-group.md +0 -71
- package/src/ui/docs/content/confirm.md +0 -120
- package/src/ui/docs/content/input-group.md +0 -79
- package/src/ui/docs/content/page-state.md +0 -45
- package/src/ui/docs/content/toggle-group.md +0 -81
|
@@ -24,7 +24,7 @@ decisão em cada tela.
|
|
|
24
24
|
|
|
25
25
|
## Com ícone
|
|
26
26
|
|
|
27
|
-
Um svg filho ganha size-3 automaticamente — bom
|
|
27
|
+
Um svg filho ganha size-3 automaticamente — bom para reforçar o estado sem crescer o rótulo.
|
|
28
28
|
|
|
29
29
|
```tsx preview
|
|
30
30
|
<Badge context="success"><CircleCheck /> Regressão verde</Badge>
|
|
@@ -41,9 +41,9 @@ variantes.
|
|
|
41
41
|
</Badge>
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## Propriedades de Badge
|
|
45
45
|
|
|
46
|
-
|
|
|
46
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
47
47
|
|---|---|---|---|
|
|
48
48
|
| `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O significado ou destaque contextual. |
|
|
49
49
|
| `variant` | `'solid' \| 'subtle' \| 'outline'` | `'subtle'` | O tratamento visual aplicado ao contexto. |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Caminho atual
|
|
2
2
|
|
|
3
3
|
A composição é manual: BreadcrumbLink nos níveis navegáveis, BreadcrumbPage no atual (não
|
|
4
4
|
clicável, aria-current=page) e um BreadcrumbSeparator entre cada item.
|
|
@@ -23,7 +23,7 @@ clicável, aria-current=page) e um BreadcrumbSeparator entre cada item.
|
|
|
23
23
|
|
|
24
24
|
## Com ícone e separador custom
|
|
25
25
|
|
|
26
|
-
O primeiro nível pode levar um ícone do lucide. BreadcrumbSeparator aceita children
|
|
26
|
+
O primeiro nível pode levar um ícone do lucide. BreadcrumbSeparator aceita children para trocar o
|
|
27
27
|
chevron padrão por outro glifo (aqui, uma barra).
|
|
28
28
|
|
|
29
29
|
```tsx preview col-start
|
|
@@ -53,7 +53,7 @@ chevron padrão por outro glifo (aqui, uma barra).
|
|
|
53
53
|
|
|
54
54
|
## Colapsado
|
|
55
55
|
|
|
56
|
-
Trilha funda demais
|
|
56
|
+
Trilha funda demais para o espaço: BreadcrumbEllipsis substitui os níveis do meio (que viram um
|
|
57
57
|
menu/popover) e mantém só a raiz e o destino.
|
|
58
58
|
|
|
59
59
|
```tsx preview col-start
|
|
@@ -78,10 +78,15 @@ menu/popover) e mantém só a raiz e o destino.
|
|
|
78
78
|
</Breadcrumb>
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
##
|
|
81
|
+
## Propriedades de BreadcrumbLink
|
|
82
82
|
|
|
83
|
-
|
|
|
83
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
84
84
|
|---|---|---|---|
|
|
85
|
-
| `asChild`
|
|
86
|
-
| `href`
|
|
87
|
-
|
|
85
|
+
| `asChild` | `boolean` | `false` | Repassa as propriedades ao filho para integrar o link do roteador no lugar de `<a>`. |
|
|
86
|
+
| `href` | `string` | | Destino do nível navegável. |
|
|
87
|
+
|
|
88
|
+
## Propriedades de BreadcrumbSeparator
|
|
89
|
+
|
|
90
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| `children` | `React.ReactNode` | `<ChevronRight />` | Elemento entre os itens. Omita para usar o chevron padrão ou passe outro ícone. |
|
|
@@ -31,7 +31,7 @@ O tamanho icon exige `aria-label`, porque não há texto visível. Os botões s
|
|
|
31
31
|
|
|
32
32
|
busy = ação em andamento (DEPOIS do clique): o Spinner e o disabled vêm do botão. Com icon, o
|
|
33
33
|
Spinner TROCA o ícone (não soma). Não confunda com carregar conteúdo (ANTES) — isso é Spinner
|
|
34
|
-
centralizado/Skeleton
|
|
34
|
+
centralizado/Skeleton em um nível de página.
|
|
35
35
|
|
|
36
36
|
```tsx preview
|
|
37
37
|
<Button disabled>Desabilitado</Button>
|
|
@@ -43,7 +43,7 @@ centralizado/Skeleton num nível de página.
|
|
|
43
43
|
## Como outro elemento (asChild)
|
|
44
44
|
|
|
45
45
|
Âncora com cara de botão: asChild renderiza o filho (Radix Slot) — sem forkar estilo.
|
|
46
|
-
buttonVariants serve
|
|
46
|
+
buttonVariants serve para o caso sem filho único.
|
|
47
47
|
|
|
48
48
|
```tsx preview
|
|
49
49
|
<Button asChild variant="outline">
|
|
@@ -51,13 +51,88 @@ buttonVariants serve pro caso sem filho único.
|
|
|
51
51
|
</Button>
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
##
|
|
54
|
+
## Propriedades de Button
|
|
55
55
|
|
|
56
|
-
|
|
|
56
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
57
57
|
|---|---|---|---|
|
|
58
58
|
| `context` | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | A hierarquia ou o risco comunicado pela ação. |
|
|
59
59
|
| `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'solid'` | O tratamento visual aplicado ao contexto. |
|
|
60
60
|
| `size` | `'default' \| 'sm' \| 'lg' \| 'icon' \| 'icon-sm' \| 'icon-xs'` | `'default'` | O tamanho. Os icon* são quadrados (2.25/2/1.5rem) para botões só de ícone, com `aria-label`. |
|
|
61
|
-
| `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` —
|
|
62
|
-
| `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton
|
|
61
|
+
| `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — para âncoras e afins. |
|
|
62
|
+
| `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton em um nível de página). |
|
|
63
63
|
| `icon` | `React.ElementType` | | Ícone à esquerda (ex.: icon={Plus}). No busy é trocado pelo Spinner — não soma. |
|
|
64
|
+
|
|
65
|
+
## ButtonGroup
|
|
66
|
+
|
|
67
|
+
Use `ButtonGroup` quando ações relacionadas precisarem formar um bloco contínuo. As bordas internas
|
|
68
|
+
colapsam e somente as pontas externas permanecem arredondadas. Mantenha a mesma variante nos filhos
|
|
69
|
+
para preservar a unidade visual.
|
|
70
|
+
|
|
71
|
+
```tsx preview
|
|
72
|
+
<ButtonGroup>
|
|
73
|
+
<Button variant="outline">Visão geral</Button>
|
|
74
|
+
<Button variant="outline">Sessões</Button>
|
|
75
|
+
<Button variant="outline">Habilidades</Button>
|
|
76
|
+
</ButtonGroup>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Ação dividida
|
|
80
|
+
|
|
81
|
+
Combine a ação principal, um separador e um botão de ícone quando o mesmo comando oferecer
|
|
82
|
+
variações.
|
|
83
|
+
|
|
84
|
+
```tsx preview
|
|
85
|
+
<ButtonGroup>
|
|
86
|
+
<Button icon={Play}>Rodar agente developer</Button>
|
|
87
|
+
<ButtonGroupSeparator />
|
|
88
|
+
<Button size="icon" aria-label="Mais opções">
|
|
89
|
+
<ChevronDown />
|
|
90
|
+
</Button>
|
|
91
|
+
</ButtonGroup>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Com rótulo
|
|
95
|
+
|
|
96
|
+
`ButtonGroupText` adiciona um contexto inerte ao grupo. Ele também aceita `asChild` para assumir a
|
|
97
|
+
semântica de outro elemento, como `label`.
|
|
98
|
+
|
|
99
|
+
```tsx preview
|
|
100
|
+
<ButtonGroup>
|
|
101
|
+
<ButtonGroupText>
|
|
102
|
+
<GitBranch />
|
|
103
|
+
empresa-x-api
|
|
104
|
+
</ButtonGroupText>
|
|
105
|
+
<Button variant="outline" icon={RotateCw}>Sincronizar</Button>
|
|
106
|
+
</ButtonGroup>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Vertical
|
|
110
|
+
|
|
111
|
+
`orientation="vertical"` empilha os filhos e transfere a junção das bordas para o eixo vertical.
|
|
112
|
+
|
|
113
|
+
```tsx preview col-start
|
|
114
|
+
<ButtonGroup orientation="vertical">
|
|
115
|
+
<Button variant="outline" size="icon" aria-label="Rodar sessão"><Play /></Button>
|
|
116
|
+
<Button variant="outline" size="icon" aria-label="Pausar sessão"><Pause /></Button>
|
|
117
|
+
<Button variant="outline" size="icon" aria-label="Reiniciar sessão"><RotateCw /></Button>
|
|
118
|
+
</ButtonGroup>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Propriedades de ButtonGroup
|
|
122
|
+
|
|
123
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
124
|
+
|---|---|---|---|
|
|
125
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco e do colapso das bordas. |
|
|
126
|
+
| `shape` | `'default' \| 'pill'` | `'default'` | Geometria das extremidades externas do grupo. |
|
|
127
|
+
|
|
128
|
+
### Propriedades de ButtonGroupSeparator
|
|
129
|
+
|
|
130
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | Direção do traço divisor; use vertical em grupos horizontais. |
|
|
133
|
+
|
|
134
|
+
### Propriedades de ButtonGroupText
|
|
135
|
+
|
|
136
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
137
|
+
|---|---|---|---|
|
|
138
|
+
| `asChild` | `boolean` | `false` | Renderiza como o filho para assumir outra semântica sem perder o estilo. |
|
|
@@ -17,7 +17,7 @@ render(
|
|
|
17
17
|
|
|
18
18
|
## Intervalo
|
|
19
19
|
|
|
20
|
-
mode=range guarda { from, to } — o padrão
|
|
20
|
+
mode=range guarda { from, to } — o padrão para filtrar sessões por janela de datas. O primeiro clique fixa o início; o segundo, o fim.
|
|
21
21
|
|
|
22
22
|
```tsx preview
|
|
23
23
|
const [week, setWeek] = useState<DateRange | undefined>({
|
|
@@ -37,7 +37,7 @@ render(
|
|
|
37
37
|
|
|
38
38
|
## Navegação por dropdown
|
|
39
39
|
|
|
40
|
-
captionLayout=dropdown troca o título do mês por seletores de mês e ano — bom
|
|
40
|
+
captionLayout=dropdown troca o título do mês por seletores de mês e ano — bom para pular para um período distante (ex.: histórico de um repositório) sem clicar mês a mês.
|
|
41
41
|
|
|
42
42
|
```tsx preview
|
|
43
43
|
<Calendar
|
|
@@ -47,16 +47,16 @@ captionLayout=dropdown troca o título do mês por seletores de mês e ano — b
|
|
|
47
47
|
/>
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
##
|
|
50
|
+
## Propriedades de Calendar
|
|
51
51
|
|
|
52
|
-
|
|
|
52
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
53
53
|
|---|---|---|---|
|
|
54
54
|
| `mode` | `'single' \| 'multiple' \| 'range'` | | O tipo de seleção — define o formato de selected/onSelect (Date, Date[] ou { from, to }). |
|
|
55
55
|
| `selected` | `Date \| Date[] \| DateRange` | | A seleção atual no modo controlado — o formato segue o mode. Pareie com onSelect. |
|
|
56
56
|
| `onSelect` | `(selected) => void` | | Chamado quando o usuário escolhe uma data. O argumento segue o mode. |
|
|
57
57
|
| `defaultMonth` | `Date` | | O mês exibido ao montar, sem afetar a seleção. |
|
|
58
58
|
| `captionLayout` | `'label' \| 'dropdown' \| 'dropdown-months' \| 'dropdown-years'` | `'label'` | Como o título do mês aparece — label é texto fixo; dropdown vira seletores de mês e ano. |
|
|
59
|
-
| `numberOfMonths` | `number` | `1` | Quantos meses mostrar lado a lado — útil
|
|
59
|
+
| `numberOfMonths` | `number` | `1` | Quantos meses mostrar lado a lado — útil para escolher um intervalo longo. |
|
|
60
60
|
| `disabled` | `Matcher` | | Datas não selecionáveis — uma Date, um array, um { from, to } ou um predicado (date) => boolean. |
|
|
61
61
|
| `buttonVariant` | `Button['variant']` | `'ghost'` | A variante dos botões de navegação (anterior/próximo). |
|
|
62
62
|
| `showOutsideDays` | `boolean` | `true` | Mostra os dias do mês vizinho que completam a primeira e a última semana. |
|
|
@@ -10,7 +10,7 @@ O uso mais comum: a superfície de conteúdo em repouso (`rounded-xl` + `border`
|
|
|
10
10
|
|
|
11
11
|
## Estruturado (header / conteúdo / rodapé)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Para painel com estrutura: cada slot é dono do próprio padding (como o Dialog). CardTitle/CardDescription no header; CardFooter alinha as ações.
|
|
14
14
|
|
|
15
15
|
```tsx preview col
|
|
16
16
|
<Card>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Um slide por vez
|
|
2
2
|
|
|
3
|
-
Compõe Carousel>CarouselContent>CarouselItem, com CarouselPrevious/CarouselNext
|
|
3
|
+
Compõe Carousel>CarouselContent>CarouselItem, com CarouselPrevious/CarouselNext para navegar. As setas ficam fora do trilho (-left-12/-right-12), então reserve a margem lateral no entorno.
|
|
4
4
|
|
|
5
5
|
```tsx preview
|
|
6
6
|
<Carousel className="mx-12 w-full max-w-xs">
|
|
@@ -28,7 +28,7 @@ Compõe Carousel>CarouselContent>CarouselItem, com CarouselPrevious/CarouselNext
|
|
|
28
28
|
|
|
29
29
|
## Vários por vista
|
|
30
30
|
|
|
31
|
-
O basis do CarouselItem decide quantos cabem na vista — basis-1/3 mostra três slides por vez. Bom
|
|
31
|
+
O basis do CarouselItem decide quantos cabem na vista — basis-1/3 mostra três slides por vez. Bom para galeria de workspaces ou repositórios.
|
|
32
32
|
|
|
33
33
|
```tsx preview
|
|
34
34
|
<Carousel className="mx-12 w-full max-w-sm" opts={{ align: 'start' }}>
|
|
@@ -48,7 +48,7 @@ O basis do CarouselItem decide quantos cabem na vista — basis-1/3 mostra três
|
|
|
48
48
|
|
|
49
49
|
## Vertical
|
|
50
50
|
|
|
51
|
-
orientation=vertical empilha os slides; as setas migram
|
|
51
|
+
orientation=vertical empilha os slides; as setas migram para cima e para baixo (-top-12/-bottom-12). Dê uma altura ao CarouselContent para delimitar a vista.
|
|
52
52
|
|
|
53
53
|
```tsx preview
|
|
54
54
|
<Carousel className="w-full max-w-xs" orientation="vertical">
|
|
@@ -74,12 +74,17 @@ orientation=vertical empilha os slides; as setas migram pra cima e pra baixo (-t
|
|
|
74
74
|
</Carousel>
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
##
|
|
77
|
+
## Propriedades de Carousel
|
|
78
78
|
|
|
79
|
-
|
|
|
79
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
80
80
|
|---|---|---|---|
|
|
81
|
-
| `orientation
|
|
82
|
-
| `opts
|
|
83
|
-
| `setApi
|
|
84
|
-
| `plugins
|
|
85
|
-
|
|
81
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Eixo do deslocamento. No modo vertical, os slides são empilhados e as setas apontam para cima e para baixo. |
|
|
82
|
+
| `opts` | `CarouselOptions` | | Opções repassadas ao Embla, como `{ loop: true }` ou `{ align: 'start' }`. |
|
|
83
|
+
| `setApi` | `(api: CarouselApi) => void` | | Recebe a instância para controle externo, como navegar com `scrollTo` ou ler o slide ativo. |
|
|
84
|
+
| `plugins` | `CarouselPlugin[]` | | Plugins do Embla associados ao carrossel, como autoplay. |
|
|
85
|
+
|
|
86
|
+
## Propriedades de CarouselItem
|
|
87
|
+
|
|
88
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| `className` | `string` | | Classes de dimensão; a base define quantos slides cabem na área visível. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Conversa com resposta integral
|
|
2
2
|
|
|
3
|
-
Um chat mínimo: lista de mensagens + composer. A conversa é gerenciada por dentro (estado, loading, auto-scroll; **Enter** envia, **Shift+Enter** quebra linha) — a inteligência vem da prop `send`. Com o composer vazio, **↑** recupera as mensagens anteriores do usuário e **↓** volta em direção ao rascunho; durante a edição, as setas continuam movendo o cursor normalmente. O `greeting` é o estado vazio (centrado; some quando a conversa começa e
|
|
3
|
+
Um chat mínimo: lista de mensagens + composer. A conversa é gerenciada por dentro (estado, loading, auto-scroll; **Enter** envia, **Shift+Enter** quebra linha) — a inteligência vem da prop `send`. Com o composer vazio, **↑** recupera as mensagens anteriores do usuário e **↓** volta em direção ao rascunho; durante a edição, as setas continuam movendo o cursor normalmente. O `greeting` é o estado vazio (centrado; some quando a conversa começa e não entra no transcript). Dê altura ao container.
|
|
4
4
|
|
|
5
5
|
```tsx preview
|
|
6
6
|
<div className="h-96 rounded-lg border">
|
|
@@ -9,7 +9,7 @@ Um chat mínimo: lista de mensagens + composer. A conversa é gerenciada por den
|
|
|
9
9
|
send={async (messages) => {
|
|
10
10
|
await new Promise((r) => setTimeout(r, 500))
|
|
11
11
|
const last = messages[messages.length - 1]
|
|
12
|
-
return `Você disse: "${last.content}". (
|
|
12
|
+
return `Você disse: "${last.content}". (Em um app real, aqui rodaria o agente.)`
|
|
13
13
|
}}
|
|
14
14
|
/>
|
|
15
15
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Escolha booleana
|
|
2
2
|
|
|
3
|
-
Sempre em par com Label (htmlFor↔id) — clicar no texto alterna a caixa. defaultChecked
|
|
3
|
+
Sempre em par com Label (htmlFor↔id) — clicar no texto alterna a caixa. defaultChecked para o modo não controlado.
|
|
4
4
|
|
|
5
5
|
```tsx preview
|
|
6
6
|
<div className="flex items-center gap-2">
|
|
@@ -11,7 +11,7 @@ Sempre em par com Label (htmlFor↔id) — clicar no texto alterna a caixa. defa
|
|
|
11
11
|
|
|
12
12
|
## Controlado
|
|
13
13
|
|
|
14
|
-
onCheckedChange recebe boolean | 'indeterminate' — compare com true
|
|
14
|
+
onCheckedChange recebe boolean | 'indeterminate' — compare com true para guardar um boolean.
|
|
15
15
|
|
|
16
16
|
```tsx preview
|
|
17
17
|
const [autoReview, setAutoReview] = useState(true)
|
|
@@ -30,7 +30,7 @@ render(
|
|
|
30
30
|
|
|
31
31
|
## Lista de opções
|
|
32
32
|
|
|
33
|
-
Várias caixas, um estado: o conjunto marcado é a lista de valores — o padrão
|
|
33
|
+
Várias caixas, um estado: o conjunto marcado é a lista de valores — o padrão para anexar skills a um agente.
|
|
34
34
|
|
|
35
35
|
```tsx preview col-start
|
|
36
36
|
const [skills, setSkills] = useState(['clean-code', 'test'])
|
|
@@ -65,11 +65,11 @@ disabled esmaece a caixa e o rótulo em par (peer-disabled no Label) — marcado
|
|
|
65
65
|
</div>
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
##
|
|
68
|
+
## Propriedades de Checkbox
|
|
69
69
|
|
|
70
|
-
|
|
|
70
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
71
71
|
|---|---|---|---|
|
|
72
72
|
| `checked` | `boolean \| 'indeterminate'` | | O estado, no modo controlado — parear com onCheckedChange. |
|
|
73
|
-
| `onCheckedChange` | `(checked: boolean \| 'indeterminate') => void` | | Chamado a cada alternância.
|
|
73
|
+
| `onCheckedChange` | `(checked: boolean \| 'indeterminate') => void` | | Chamado a cada alternância. Para guardar um boolean, compare com true. |
|
|
74
74
|
| `defaultChecked` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
75
75
|
| `disabled` | `boolean` | `false` | Esmaece e bloqueia — o Label em par esmaece junto (peer-disabled). |
|
|
@@ -5,7 +5,7 @@ title: CLI opus
|
|
|
5
5
|
# CLI opus
|
|
6
6
|
|
|
7
7
|
O Opus traz um CLI que cobre o ciclo: faz o bootstrap, gera artefatos a partir das declarações,
|
|
8
|
-
valida as convenções e expõe o estado vivo
|
|
8
|
+
valida as convenções e expõe o estado vivo para os agentes via MCP.
|
|
9
9
|
|
|
10
10
|
## Gates
|
|
11
11
|
|
|
@@ -38,7 +38,7 @@ mesma CLI.
|
|
|
38
38
|
## Geração e introspecção
|
|
39
39
|
|
|
40
40
|
> As declarações (`description` de entidades/actions) são a fonte; o `gen` as projeta. O manifest
|
|
41
|
-
> é um lockfile
|
|
41
|
+
> é um lockfile versionado. A diferença do manifest torna a revisão objetiva.
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
opus gen # manifest / openapi / docs / stubs a partir do opus.config.ts
|
|
@@ -57,17 +57,17 @@ opus create meu-cliente --monorepo # a RAIZ de um workspace (apps/* + packages/
|
|
|
57
57
|
opus create apps/portal # dentro de um workspace: só o app (modo detectado)
|
|
58
58
|
opus setup # grava opus.json e materializa a camada específica do SDK
|
|
59
59
|
opus list # lista os templates disponíveis
|
|
60
|
-
opus add action-form # copia um template do catálogo
|
|
60
|
+
opus add action-form # copia um template do catálogo para o projeto
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
O esqueleto do `create` versiona com o Opus (sai do mesmo pacote que o SDK que ele
|
|
64
64
|
configura) e nasce com os gates verdes: domínio-exemplo canônico, teste, manifest e o dev
|
|
65
|
-
server pronto
|
|
65
|
+
server pronto para o preview do Maestro. O método geral, a memória e a revisão vêm da Base
|
|
66
66
|
depois de `pnpm run setup`. Dois modos, por detecção:
|
|
67
67
|
repo standalone (template inteiro) ou **app em monorepo** (dentro de um workspace pnpm:
|
|
68
68
|
só os arquivos do app; o que a raiz precisa ter vira aviso, sem clobber).
|
|
69
69
|
|
|
70
|
-
## MCP — estado vivo
|
|
70
|
+
## MCP — estado vivo para os agentes
|
|
71
71
|
|
|
72
72
|
> O server MCP expõe introspecção, check e scaffold de action. É como um agente lê a estrutura e cria
|
|
73
73
|
> action no formato canônico sem decorar convenção.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Seção recolhível
|
|
2
2
|
|
|
3
3
|
CollapsibleTrigger alterna o CollapsibleContent — o Trigger já é o <button>. defaultOpen deixa o estado com o componente.
|
|
4
4
|
|
|
@@ -18,7 +18,7 @@ CollapsibleTrigger alterna o CollapsibleContent — o Trigger já é o <button>.
|
|
|
18
18
|
|
|
19
19
|
## Controlado
|
|
20
20
|
|
|
21
|
-
open + onOpenChange põem o estado nas suas mãos — dá
|
|
21
|
+
open + onOpenChange põem o estado nas suas mãos — dá para refletir no gatilho (aqui o chevron gira) ou guardar a preferência.
|
|
22
22
|
|
|
23
23
|
```tsx preview col
|
|
24
24
|
const [open, setOpen] = useState(false)
|
|
@@ -54,11 +54,11 @@ disabled no Collapsible trava o gatilho — a seção fica fixa no estado atual
|
|
|
54
54
|
</Collapsible>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
##
|
|
57
|
+
## Propriedades de Collapsible
|
|
58
58
|
|
|
59
|
-
|
|
|
59
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
60
60
|
|---|---|---|---|
|
|
61
|
-
| `defaultOpen
|
|
62
|
-
| `open
|
|
63
|
-
| `onOpenChange
|
|
64
|
-
| `disabled
|
|
61
|
+
| `defaultOpen` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
62
|
+
| `open` | `boolean` | | Estado no modo controlado. Use com `onOpenChange`. |
|
|
63
|
+
| `onOpenChange` | `(open: boolean) => void` | | Chamado a cada abertura ou fechamento. |
|
|
64
|
+
| `disabled` | `boolean` | `false` | Bloqueia o gatilho e mantém a seção no estado atual. |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
## Lista filtrável inline
|
|
2
2
|
|
|
3
|
-
Digite
|
|
3
|
+
Digite para filtrar uma coleção com navegação por teclado, grupos e estado vazio. Command é a base
|
|
4
|
+
do Select pesquisável; para uma escolha em formulário, use Select.
|
|
4
5
|
|
|
5
6
|
```tsx preview
|
|
6
7
|
<Command className="max-w-sm rounded-lg border border-border">
|
|
@@ -22,7 +23,7 @@ Digite pra filtrar — navegação por teclado, grupos e CommandEmpty de graça
|
|
|
22
23
|
|
|
23
24
|
## Palette modal (CommandDialog)
|
|
24
25
|
|
|
25
|
-
O Command embrulhado
|
|
26
|
+
O Command embrulhado em um Dialog, com header sr-only para a11y. O atalho ⌘K (keydown no app) só troca o open — o conteúdo é o mesmo do inline.
|
|
26
27
|
|
|
27
28
|
```tsx preview
|
|
28
29
|
const [open, setOpen] = useState(false)
|
|
@@ -46,11 +47,18 @@ render(
|
|
|
46
47
|
)
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
##
|
|
50
|
+
## Propriedades de CommandDialog
|
|
50
51
|
|
|
51
|
-
|
|
|
52
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
52
53
|
|---|---|---|---|
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
54
|
+
| `open` | `boolean` | | Estado do modal no modo controlado. |
|
|
55
|
+
| `onOpenChange` | `(open: boolean) => void` | | Atualiza o estado do modal; pode ser conectado ao atalho do aplicativo. |
|
|
56
|
+
| `title` | `string` | `'Comandos'` | Nome acessível do diálogo, disponível para leitura assistiva. |
|
|
57
|
+
| `description` | `string` | `'Busque um comando para executar.'` | Descrição acessível do diálogo. |
|
|
58
|
+
| `showCloseButton` | `boolean` | `true` | Exibe o botão de fechamento. |
|
|
59
|
+
|
|
60
|
+
## Propriedades de CommandItem
|
|
61
|
+
|
|
62
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `onSelect` | `(value: string) => void` | | Chamado ao selecionar o item por clique ou teclado. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Envio de texto
|
|
2
2
|
|
|
3
|
-
A caixa de escrever da casa: textarea
|
|
3
|
+
A caixa de escrever da casa: textarea em uma pílula elevada (`rounded-xl` + `border` + `shadow-sm`), **Enter** envia / **Shift+Enter** quebra linha, enviar dentro. É o composer do [Chat](/components/chat) extraído — use sozinho quando há entrada de texto mas não um chat (ex.: criar uma sessão). Controlado: o dono do texto é você. Os callbacks opcionais `onHistoryPrevious` e `onHistoryNext` permitem que esse dono consuma **↑/↓**; sem eles, as setas mantêm o comportamento nativo da textarea.
|
|
4
4
|
|
|
5
5
|
```tsx preview col
|
|
6
6
|
const [text, setText] = React.useState('')
|
|
@@ -13,7 +13,7 @@ render(
|
|
|
13
13
|
description="Sessões com acesso à sua conta."
|
|
14
14
|
actions={<Button variant="outline">Encerrar outras sessões</Button>}
|
|
15
15
|
>
|
|
16
|
-
<div className="rounded-lg border border-border p-4">MacBook
|
|
16
|
+
<div className="rounded-lg border border-border p-4">MacBook Para o · ativo agora</div>
|
|
17
17
|
</Content>,
|
|
18
18
|
)
|
|
19
19
|
```
|
|
@@ -34,7 +34,7 @@ render(
|
|
|
34
34
|
<ContentActions><Button variant="outline">Atualizar</Button></ContentActions>
|
|
35
35
|
</ContentHeader>
|
|
36
36
|
<ContentBody>
|
|
37
|
-
<div className="rounded-lg border border-border p-4">MacBook
|
|
37
|
+
<div className="rounded-lg border border-border p-4">MacBook Para o · ativo agora</div>
|
|
38
38
|
</ContentBody>
|
|
39
39
|
</Content>,
|
|
40
40
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Copiar por ícone
|
|
2
2
|
|
|
3
|
-
Sem filhos, é um botão-ícone: copia `value` e o ícone vira um check por ~1.5s. Bom
|
|
3
|
+
Sem filhos, é um botão-ícone: copia `value` e o ícone vira um check por ~1.5s. Bom para toolbar ou célula estreita, ao lado de um ID/token/slug.
|
|
4
4
|
|
|
5
5
|
```tsx preview
|
|
6
6
|
<Copyable value="opus_sk_1a2b3c4d5e6f" className="text-muted-foreground hover:text-foreground" />
|
|
@@ -21,7 +21,8 @@ Com filhos, o valor visível fica à esquerda e o ícone à direita — clicar n
|
|
|
21
21
|
|
|
22
22
|
## Duração do feedback
|
|
23
23
|
|
|
24
|
-
`feedbackMs` ajusta quanto
|
|
24
|
+
`feedbackMs` ajusta por quanto tempo a confirmação aparece; o padrão é 1500 milissegundos. Se a
|
|
25
|
+
área de transferência não estiver disponível, a ação não produz efeito.
|
|
25
26
|
|
|
26
27
|
```tsx preview
|
|
27
28
|
<Copyable value="copiado devagar" feedbackMs={3000} className="text-muted-foreground hover:text-foreground">
|
|
@@ -6,7 +6,7 @@ title: Customização
|
|
|
6
6
|
|
|
7
7
|
Cinco alavancas, do global ao pontual — e um limite de propósito. O caminho previsto é compor
|
|
8
8
|
e configurar, nunca forkar componente: o que não cabe nas alavancas evolui no Opus (com
|
|
9
|
-
divergência declarada),
|
|
9
|
+
divergência declarada), para valer para casa toda.
|
|
10
10
|
|
|
11
11
|
## 1 · Identidade por tokens
|
|
12
12
|
|
|
@@ -36,7 +36,7 @@ de 16, sem transformar esse valor em uma regra da biblioteca.
|
|
|
36
36
|
## 2 · className em tudo
|
|
37
37
|
|
|
38
38
|
> Todo componente termina em `cn(base, className)` com tailwind-merge: o utilitário do consumidor
|
|
39
|
-
> vence o conflito.
|
|
39
|
+
> vence o conflito. Para layout local (largura, margem, grid) — não para repintar o visual da casa.
|
|
40
40
|
|
|
41
41
|
```tsx
|
|
42
42
|
<Button className="w-full">Continuar</Button>
|
|
@@ -47,7 +47,7 @@ de 16, sem transformar esse valor em uma regra da biblioteca.
|
|
|
47
47
|
## 3 · Recomposição estrutural
|
|
48
48
|
|
|
49
49
|
> Os componentes são explodidos em slots. `asChild` (Radix Slot) renderiza como outro elemento
|
|
50
|
-
> mantendo estilo e comportamento; os `*Variants` aplicam a cara da casa
|
|
50
|
+
> mantendo estilo e comportamento; os `*Variants` aplicam a cara da casa em um elemento arbitrário.
|
|
51
51
|
|
|
52
52
|
```tsx preview
|
|
53
53
|
<Button asChild variant="outline">
|
|
@@ -65,7 +65,7 @@ de 16, sem transformar esse valor em uma regra da biblioteca.
|
|
|
65
65
|
// asChild: o filho VIRA o botão (sem forkar estilo).
|
|
66
66
|
<Button asChild><a href="/docs">Abrir documentação</a></Button>
|
|
67
67
|
|
|
68
|
-
// buttonVariants: a cara da casa
|
|
68
|
+
// buttonVariants: a cara da casa em um elemento qualquer.
|
|
69
69
|
<a className={buttonVariants({ variant: 'outline' })}>Link estilizado</a>
|
|
70
70
|
```
|
|
71
71
|
|
|
@@ -115,4 +115,4 @@ const [open, setOpen] = useState(false)
|
|
|
115
115
|
|
|
116
116
|
Se uma necessidade real não cabe nas alavancas (tokens · className · slots/asChild · props ·
|
|
117
117
|
contrato), o movimento não é dialeto local: é evoluir o componente **no Opus**, com a divergência
|
|
118
|
-
declarada (skill `build-opus-ui`) — assim a mudança vale
|
|
118
|
+
declarada (skill `build-opus-ui`) — assim a mudança vale para casa toda, e esta doc passa a mostrá-la.
|
|
@@ -16,9 +16,9 @@ No seu projeto, uma linha por apontamento em `.opus/issues.jsonl` na raiz do rep
|
|
|
16
16
|
|
|
17
17
|
## Não trave esperando
|
|
18
18
|
|
|
19
|
-
O ponto do ciclo é **não bloquear a entrega**. Bateu
|
|
19
|
+
O ponto do ciclo é **não bloquear a entrega**. Bateu em um gap ou em um bug do Opus:
|
|
20
20
|
|
|
21
|
-
1. **Contorne local** — componha um wrapper no seu projeto. O Opus entrega _source_, então dá
|
|
21
|
+
1. **Contorne local** — componha um wrapper no seu projeto. O Opus entrega _source_, então dá para embrulhar qualquer superfície dele. Nunca edite `node_modules` (some no próximo install).
|
|
22
22
|
2. **Entregue** a feature com o workaround.
|
|
23
23
|
3. **Aponte** no `.opus/issues.jsonl` e siga em frente.
|
|
24
24
|
|
|
@@ -28,7 +28,7 @@ O "depois" — o conserto no Opus — corre em paralelo. Ele não segura o seu t
|
|
|
28
28
|
|
|
29
29
|
O apontamento é colhido e abre uma **Issue no repositório do Opus**, onde a triagem acontece (deduplicada — re-apontar o mesmo é idempotente):
|
|
30
30
|
|
|
31
|
-
- **Enhancement** aceito (com reincidência) → implementado no Opus → sai
|
|
31
|
+
- **Enhancement** aceito (com reincidência) → implementado no Opus → sai em um _bump_ → seu projeto atualiza o pin (`opus.json`) e troca o workaround pelo import.
|
|
32
32
|
- **Bug** → vira _fix_ + entrada no `CHANGELOG` → no _bump_, o workaround sai.
|
|
33
33
|
|
|
34
34
|
A régua e a decisão ficam com quem mantém o Opus — hoje, a **Softize**. O registro curado das promoções vive no `PROMOTED.md` do pacote.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
## Estados
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Use `DataState` para apresentar carregamento, erro, vazio e conteúdo de uma mesma consulta. O
|
|
4
|
+
carregamento usa `Spinner`; o vazio preserva uma moldura sólida; e o erro apresenta uma mensagem
|
|
5
|
+
segura, sem expor detalhes técnicos. Para uma ação em andamento depois do clique, use `busy` em
|
|
6
|
+
`Button`.
|
|
7
7
|
|
|
8
8
|
```tsx preview col
|
|
9
9
|
<div className="w-full space-y-3">
|
|
@@ -19,12 +19,11 @@ pro "depois" (ação em andamento), use o busy do Button.
|
|
|
19
19
|
</div>
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Dentro de uma tabela
|
|
23
23
|
|
|
24
|
-
Em
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
ou vínculo, com título, descrição ou ação, use `Empty`, cuja moldura é tracejada.
|
|
24
|
+
Em uma tabela já emoldurada, passe `colSpan` para ocupar uma linha inteira dentro de `<tbody>`. A
|
|
25
|
+
tabela continua responsável pela borda, evitando uma segunda moldura no estado vazio. Para uma
|
|
26
|
+
região disponível para criação ou vínculo, use `Empty`.
|
|
28
27
|
|
|
29
28
|
```tsx preview col
|
|
30
29
|
<table className="w-full overflow-hidden rounded-lg border border-border text-sm">
|
|
@@ -38,13 +37,13 @@ ou vínculo, com título, descrição ou ação, use `Empty`, cuja moldura é tr
|
|
|
38
37
|
</table>
|
|
39
38
|
```
|
|
40
39
|
|
|
41
|
-
##
|
|
40
|
+
## Propriedades de DataState
|
|
42
41
|
|
|
43
|
-
|
|
|
42
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
44
43
|
|---|---|---|---|
|
|
45
44
|
| `loading` | `boolean` | | Carregando (antes do conteúdo) — mostra o Spinner centralizado. |
|
|
46
45
|
| `empty` | `boolean` | | Sem itens — mostra o emptyText. |
|
|
47
46
|
| `emptyText` | `string` | | Texto do vazio (pt-BR, ex.: "Nenhum papel."). |
|
|
48
|
-
| `error` | `{ message?: string } \| null` | | Erro da carga — aviso calmo. A mensagem técnica
|
|
47
|
+
| `error` | `{ message?: string } \| null` | | Erro da carga — aviso calmo. A mensagem técnica não vai para tela (use errorText). |
|
|
49
48
|
| `errorText` | `string` | `'Não foi possível carregar.'` | Aviso de erro, orientado ao usuário. |
|
|
50
49
|
| `colSpan` | `number` | | Em tabela: renderiza o estado como `<tr><td colSpan>` (cabe direto no tbody). |
|