@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
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Um valor na faixa
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Slider` para escolher um valor dentro de uma faixa. `defaultValue` recebe um array; `[n]`
|
|
4
|
+
representa um único controle. `min`, `max` e `step` delimitam os valores disponíveis.
|
|
4
5
|
|
|
5
6
|
```tsx preview col
|
|
6
7
|
<Slider defaultValue={[40]} min={0} max={100} step={5} />
|
|
@@ -8,7 +9,7 @@ defaultValue é um array — [n] pra um thumb. min, max e step delimitam a faixa
|
|
|
8
9
|
|
|
9
10
|
## Controlado
|
|
10
11
|
|
|
11
|
-
onValueChange recebe o array atualizado
|
|
12
|
+
`onValueChange` recebe o array atualizado. Para um único controle, leia `value[0]`.
|
|
12
13
|
|
|
13
14
|
```tsx preview col
|
|
14
15
|
const [parallelism, setParallelism] = useState([4])
|
|
@@ -26,7 +27,7 @@ render(
|
|
|
26
27
|
|
|
27
28
|
## Intervalo
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
Dois números em `value` criam um intervalo selecionável entre dois controles.
|
|
30
31
|
|
|
31
32
|
```tsx preview col
|
|
32
33
|
const [budget, setBudget] = useState([20, 60])
|
|
@@ -50,11 +51,11 @@ disabled esmaece o trilho e o thumb e bloqueia o arraste — o valor fica congel
|
|
|
50
51
|
<Slider defaultValue={[70]} min={0} max={100} disabled />
|
|
51
52
|
```
|
|
52
53
|
|
|
53
|
-
##
|
|
54
|
+
## Propriedades de Slider
|
|
54
55
|
|
|
55
|
-
|
|
|
56
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
56
57
|
|---|---|---|---|
|
|
57
|
-
| `value` | `number[]` | | O valor no modo controlado — [n]
|
|
58
|
+
| `value` | `number[]` | | O valor no modo controlado — [n] para um thumb, [a, b] para um intervalo. Pareie com onValueChange. |
|
|
58
59
|
| `onValueChange` | `(value: number[]) => void` | | Chamado a cada arraste, com o array atualizado. |
|
|
59
60
|
| `defaultValue` | `number[]` | | Valor inicial no modo não controlado. |
|
|
60
61
|
| `min` | `number` | `0` | Limite inferior da faixa. |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Carregamento sem progresso conhecido
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Use `Spinner` quando a duração ou o progresso da espera não forem conhecidos. `sm` atende ações
|
|
4
|
+
compactas; `lg`, estados mais amplos. O componente já possui o nome acessível “Carregando”.
|
|
5
5
|
|
|
6
6
|
```tsx preview
|
|
7
7
|
<Spinner size="sm" />
|
|
@@ -11,8 +11,8 @@ Divergência da casa: o shadcn atual só tem size-4 fixo; mantivemos sm/default/
|
|
|
11
11
|
|
|
12
12
|
## No botão
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
Durante uma ação, combine o spinner compacto com o estado desabilitado e um rótulo que descreva o
|
|
15
|
+
andamento.
|
|
16
16
|
|
|
17
17
|
```tsx preview
|
|
18
18
|
<Button disabled><Spinner size="sm" /> Publicando…</Button>
|
|
@@ -30,8 +30,8 @@ Skeleton.
|
|
|
30
30
|
</div>
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## Propriedades de Spinner
|
|
34
34
|
|
|
35
|
-
|
|
|
35
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
36
36
|
|---|---|---|---|
|
|
37
|
-
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | O tamanho — sm
|
|
37
|
+
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | O tamanho — sm para dentro de botão, lg para estados de página. |
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Dividir uma área
|
|
2
2
|
|
|
3
|
-
`Split`
|
|
3
|
+
Use `Split` para organizar áreas lado a lado ou empilhadas. A ordem dos `Pane` define a posição. Com
|
|
4
|
+
`resizable`, a pessoa pode ajustar as divisórias por ponteiro ou teclado; sem essa propriedade, o
|
|
5
|
+
layout permanece estático.
|
|
4
6
|
|
|
5
7
|
```tsx preview
|
|
6
8
|
render(
|
|
@@ -17,11 +19,12 @@ render(
|
|
|
17
19
|
)
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## Espaçamento interno
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
`Pane` usa `inset="md"` por padrão. Use `none` para navegação e conteúdo que já controla o próprio
|
|
25
|
+
espaçamento; `sm` e `lg` atendem outras densidades.
|
|
23
26
|
|
|
24
|
-
##
|
|
27
|
+
## Largura inicial
|
|
25
28
|
|
|
26
29
|
Quando uma área precisa continuar legível em telas largas, use uma unidade absoluta para o tamanho inicial ou mínimo. Números continuam representando porcentagens; strings aceitam `%`, `rem`, `em`, `vh`, `vw` e `px`. Prefira `rem` para acompanhar a escala tipográfica configurada pela aplicação.
|
|
27
30
|
|
|
@@ -7,10 +7,8 @@ title: Armazenamento
|
|
|
7
7
|
> **Experimental.** Superfície mínima, sem consumidor interno ainda — cresce por
|
|
8
8
|
> reincidência de caso real, não por especulação.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(`StorageAdapter`): a key é um **caminho lógico** (`clientes/123/contrato.pdf`) e o
|
|
13
|
-
driver resolve onde ela mora.
|
|
10
|
+
Use `StorageAdapter` para upload, download, remoção e geração de URL de arquivos. A chave é um
|
|
11
|
+
caminho lógico, como `clientes/123/contrato.pdf`; cada driver decide onde o conteúdo é armazenado.
|
|
14
12
|
|
|
15
13
|
## O contrato
|
|
16
14
|
|
|
@@ -23,8 +21,8 @@ interface StorageAdapter {
|
|
|
23
21
|
}
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
`normalizeKey` aplica a mesma validação a todos os drivers: chaves são relativas e não aceitam
|
|
25
|
+
`..` nem `\`. Assim, tentativas de escapar do diretório são bloqueadas antes do armazenamento.
|
|
28
26
|
|
|
29
27
|
## Drivers
|
|
30
28
|
|
|
@@ -44,7 +42,7 @@ const prod = s3Storage({
|
|
|
44
42
|
})
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
Os peers do S3 são **opcionais** (`@aws-sdk/client-s3`; `s3-request-presigner`
|
|
45
|
+
Os peers do S3 são **opcionais** (`@aws-sdk/client-s3`; `s3-request-presigner` para o
|
|
48
46
|
`url()`): quem não usa o driver não os instala — importados sob demanda.
|
|
49
47
|
|
|
50
48
|
## No runtime
|
|
@@ -62,7 +60,7 @@ handler: async (ctx, input) => {
|
|
|
62
60
|
}
|
|
63
61
|
```
|
|
64
62
|
|
|
65
|
-
## Limites
|
|
63
|
+
## Limites atuais
|
|
66
64
|
|
|
67
65
|
Bytes em memória (`Uint8Array`), sem streams — arquivo gigante ainda não é o caso;
|
|
68
66
|
sem `list`, sem metadata rica. Cada um entra quando um projeto real cobrar — é a
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Alternar uma preferência
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Switch` para uma preferência booleana que entra em vigor imediatamente. Associe o controle a
|
|
4
|
+
`Label`; `defaultChecked` define o estado inicial no modo não controlado.
|
|
4
5
|
|
|
5
6
|
```tsx preview
|
|
6
7
|
<div className="flex items-center gap-2">
|
|
@@ -11,7 +12,7 @@ Sempre em par com Label (htmlFor↔id) — clicar no texto alterna a chave. defa
|
|
|
11
12
|
|
|
12
13
|
## Controlado
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Use `checked` e `onCheckedChange` quando o estado pertencer ao consumidor.
|
|
15
16
|
|
|
16
17
|
```tsx preview
|
|
17
18
|
const [autoReview, setAutoReview] = useState(true)
|
|
@@ -30,7 +31,7 @@ render(
|
|
|
30
31
|
|
|
31
32
|
## Tamanho sm
|
|
32
33
|
|
|
33
|
-
size=sm
|
|
34
|
+
Use `size="sm"` em linhas de lista e outras composições compactas.
|
|
34
35
|
|
|
35
36
|
```tsx preview col-start
|
|
36
37
|
<div className="flex items-center gap-2">
|
|
@@ -58,12 +59,12 @@ disabled esmaece e bloqueia a chave — ligada ou desligada — e o rótulo em p
|
|
|
58
59
|
</div>
|
|
59
60
|
```
|
|
60
61
|
|
|
61
|
-
##
|
|
62
|
+
## Propriedades de Switch
|
|
62
63
|
|
|
63
|
-
|
|
|
64
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
64
65
|
|---|---|---|---|
|
|
65
66
|
| `checked` | `boolean` | | O estado, no modo controlado — parear com onCheckedChange. |
|
|
66
67
|
| `onCheckedChange` | `(checked: boolean) => void` | | Chamado a cada alternância, com o novo estado. |
|
|
67
68
|
| `defaultChecked` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
68
|
-
| `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave — sm
|
|
69
|
+
| `size` | `'sm' \| 'default'` | `'default'` | Tamanho da chave — sm para densidade em linha de lista. |
|
|
69
70
|
| `disabled` | `boolean` | `false` | Esmaece e bloqueia — o Label em par esmaece junto (peer-disabled). |
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
## Tabela de domínio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Table` para dados organizados em linhas e colunas. A variante padrão não possui borda externa;
|
|
4
|
+
`TableBody` mantém somente as divisórias internas. Aplique `text-right` ao cabeçalho e às células
|
|
5
|
+
quando o domínio pedir alinhamento numérico. `TableCaption` descreve a tabela abaixo do conteúdo.
|
|
4
6
|
|
|
5
7
|
```tsx preview col
|
|
6
8
|
<Table>
|
|
@@ -36,7 +38,7 @@ A Table vem SEM borda externa — só as divisórias de linha (a última o Table
|
|
|
36
38
|
</Table>
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
##
|
|
41
|
+
## Tabela emoldurada
|
|
40
42
|
|
|
41
43
|
A variante `framed` aplica no próprio contêiner a borda externa, os cantos arredondados, o
|
|
42
44
|
scroll horizontal contido e o fundo discreto do cabeçalho. A última linha já vem sem divisória,
|
|
@@ -69,7 +71,8 @@ pesquisáveis derivadas de actions `kind: 'list'`.
|
|
|
69
71
|
|
|
70
72
|
## Com rodapé (TableFooter)
|
|
71
73
|
|
|
72
|
-
TableFooter
|
|
74
|
+
Use `TableFooter` para totais ou outras agregações. O componente aplica fundo muted e peso de fonte
|
|
75
|
+
adequados a essa região.
|
|
73
76
|
|
|
74
77
|
```tsx preview col
|
|
75
78
|
<Table>
|
|
@@ -101,3 +104,10 @@ TableFooter fecha a tabela com a linha de agregação — fundo muted e peso de
|
|
|
101
104
|
</TableFooter>
|
|
102
105
|
</Table>
|
|
103
106
|
```
|
|
107
|
+
|
|
108
|
+
## Propriedades de Table
|
|
109
|
+
|
|
110
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
111
|
+
|---|---|---|---|
|
|
112
|
+
| `variant` | `'plain' \| 'framed'` | `'plain'` | Escolhe entre a estrutura sem moldura externa e o contêiner emoldurado. |
|
|
113
|
+
| `className` | `string` | | Classes aplicadas ao elemento `table`. |
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Alternar painéis relacionados
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Tabs` para alternar painéis relacionados no mesmo contexto. O `value` de cada `TabsTrigger`
|
|
4
|
+
corresponde ao `TabsContent` que ele abre. `defaultValue` define a aba inicial no modo não
|
|
5
|
+
controlado.
|
|
4
6
|
|
|
5
7
|
```tsx preview col
|
|
6
8
|
<Tabs defaultValue="sessions">
|
|
@@ -23,7 +25,8 @@ O value do TabsTrigger pareia com o do TabsContent. defaultValue deixa o estado
|
|
|
23
25
|
|
|
24
26
|
## Variante line
|
|
25
27
|
|
|
26
|
-
variant=line
|
|
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.
|
|
27
30
|
|
|
28
31
|
```tsx preview col
|
|
29
32
|
<Tabs defaultValue="agents">
|
|
@@ -46,7 +49,8 @@ variant=line na TabsList: fundo transparente, o ativo é marcado pelo traço emb
|
|
|
46
49
|
|
|
47
50
|
## Vertical
|
|
48
51
|
|
|
49
|
-
orientation=vertical
|
|
52
|
+
Com `orientation="vertical"`, a lista forma uma coluna e a marca da variante `line` passa para a
|
|
53
|
+
lateral do gatilho.
|
|
50
54
|
|
|
51
55
|
```tsx preview col
|
|
52
56
|
<Tabs defaultValue="prompt" orientation="vertical">
|
|
@@ -67,7 +71,7 @@ orientation=vertical no Tabs: a lista vira coluna e o traço da variante line mi
|
|
|
67
71
|
</Tabs>
|
|
68
72
|
```
|
|
69
73
|
|
|
70
|
-
##
|
|
74
|
+
## Tamanho
|
|
71
75
|
|
|
72
76
|
`size="sm"` no `Tabs` reduz a lista de `h-9` (2.25rem) para `h-8` (2rem). É o par do `sm` de Button e Select para uma fileira densa, como uma toolbar ou um cabeçalho, em que o segmento não deve ficar mais alto que os elementos vizinhos.
|
|
73
77
|
|
|
@@ -81,14 +85,24 @@ orientation=vertical no Tabs: a lista vira coluna e o traço da variante line mi
|
|
|
81
85
|
</Tabs>
|
|
82
86
|
```
|
|
83
87
|
|
|
84
|
-
##
|
|
88
|
+
## Propriedades de Tabs
|
|
85
89
|
|
|
86
|
-
|
|
|
90
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
87
91
|
|---|---|---|---|
|
|
88
|
-
| `defaultValue
|
|
89
|
-
| `value
|
|
90
|
-
| `onValueChange
|
|
91
|
-
| `size
|
|
92
|
-
| `orientation
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
| `defaultValue` | `string` | | Aba inicial no modo não controlado. |
|
|
93
|
+
| `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
|
|
94
|
+
| `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
|
|
95
|
+
| `size` | `'default' \| 'sm'` | `'default'` | Escala de altura compartilhada com `TabsList`. |
|
|
96
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
|
|
97
|
+
|
|
98
|
+
## Propriedades de TabsList
|
|
99
|
+
|
|
100
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
| `variant` | `'default' \| 'line'` | `'default'` | `default` usa uma superfície preenchida; `line` marca a aba ativa junto à borda. |
|
|
103
|
+
|
|
104
|
+
## Propriedades de TabsTrigger e TabsContent
|
|
105
|
+
|
|
106
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `value` | `string` | | Identificador que associa o gatilho ao painel correspondente. |
|
|
@@ -4,16 +4,14 @@ title: Testes de action
|
|
|
4
4
|
|
|
5
5
|
# Testes de action
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
em unidade, sem montar runtime nem mockar contexto à mão.
|
|
7
|
+
Teste cada action pela fronteira do contrato: entrada, saída ou erro, limites do schema e
|
|
8
|
+
autorização. `@softize/opus/testing` executa esse fluxo em unidade sem montar o runtime completo.
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Executar o contrato em unidade
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
(`validation.invalid_input`, `auth.unauthenticated`, `auth.forbidden`…).
|
|
12
|
+
`runAction` valida a entrada, aplica o gate `public`, avalia `authorize`, executa o handler e valida
|
|
13
|
+
a saída. Falhas usam o mesmo `ActionError` tipado do runtime, como `validation.invalid_input`,
|
|
14
|
+
`auth.unauthenticated` e `auth.forbidden`.
|
|
17
15
|
|
|
18
16
|
```ts
|
|
19
17
|
import { runAction } from '@softize/opus/testing'
|
|
@@ -45,7 +43,7 @@ it('só a dona edita', async () => {
|
|
|
45
43
|
|
|
46
44
|
## Contexto observável
|
|
47
45
|
|
|
48
|
-
`runAction` (e `testContext`,
|
|
46
|
+
`runAction` (e `testContext`, para quem quer só o ctx) devolve os efeitos capturados —
|
|
49
47
|
o teste afirma o que importa:
|
|
50
48
|
|
|
51
49
|
```ts
|
|
@@ -55,7 +53,7 @@ expect(emitted).toEqual([{ event: 'task.done', data: { id: '1' } }])
|
|
|
55
53
|
|
|
56
54
|
## memStorage
|
|
57
55
|
|
|
58
|
-
`StorageAdapter` em memória com a mesma régua de key dos drivers reais —
|
|
56
|
+
`StorageAdapter` em memória com a mesma régua de key dos drivers reais — para handler
|
|
59
57
|
que anexa arquivo:
|
|
60
58
|
|
|
61
59
|
```ts
|
|
@@ -81,7 +79,7 @@ const many = fakeMany(EventEntity.zod(), 20) // 20, estáveis entre execuções
|
|
|
81
79
|
fake(schema, { seed: 7 }) // seed própria
|
|
82
80
|
```
|
|
83
81
|
|
|
84
|
-
##
|
|
82
|
+
## Limites do teste de unidade
|
|
85
83
|
|
|
86
84
|
De propósito — é harness de **unidade**: loaders reais (passe `loaded` pronto), audit,
|
|
87
85
|
reactions em cadeia e o servidor. Fluxo completo é teste de integração com o runtime.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Texto com várias linhas
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Textarea` para texto livre com várias linhas. O campo cresce com o conteúdo a partir de sua
|
|
4
|
+
altura mínima.
|
|
4
5
|
|
|
5
6
|
```tsx preview col md
|
|
6
7
|
<Textarea placeholder="Descreva o que o agente deve fazer nesta sessão." />
|
|
@@ -8,7 +9,7 @@ Cresce com o conteúdo a partir da altura mínima (field-sizing-content) — dig
|
|
|
8
9
|
|
|
9
10
|
## Com Label
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Associe `htmlFor` no `Label` ao `id` do campo para manter o rótulo acessível.
|
|
12
13
|
|
|
13
14
|
```tsx preview col md
|
|
14
15
|
<div className="grid gap-2">
|
|
@@ -22,7 +23,7 @@ O mesmo par htmlFor↔id dos outros campos — o overview do handoff é o caso t
|
|
|
22
23
|
|
|
23
24
|
## Estados
|
|
24
25
|
|
|
25
|
-
aria-invalid
|
|
26
|
+
`aria-invalid` comunica e apresenta o estado inválido; `disabled` bloqueia a edição.
|
|
26
27
|
|
|
27
28
|
```tsx preview col md
|
|
28
29
|
<Textarea aria-invalid placeholder="Conte o contexto da mudança." />
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Notificação temporária
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `toast` para informar o resultado temporário de uma ação sem interromper o fluxo. Cada tipo usa o
|
|
4
|
+
ícone correspondente do Opus. Escreva o título como rótulo, sem ponto final, e a descrição como uma
|
|
5
|
+
frase. A moldura do ícone permanece quadrada e alinhada à primeira linha mesmo quando a descrição
|
|
6
|
+
ocupa várias linhas.
|
|
4
7
|
|
|
5
8
|
```tsx preview
|
|
6
9
|
<Button variant="outline" onClick={() => toast.success('Workspace criado')}>Sucesso</Button>
|
|
@@ -13,22 +16,34 @@ Cada tipo já vem com o ícone lucide do Opus. O título é label (sem ponto); a
|
|
|
13
16
|
<Button variant="outline" onClick={() => toast.info('Base atualizada')}>Info</Button>
|
|
14
17
|
<Button
|
|
15
18
|
variant="outline"
|
|
16
|
-
onClick={() => toast.warning('Preview parado', { description: 'Inicie o ambiente
|
|
19
|
+
onClick={() => toast.warning('Preview parado', { description: 'Inicie o ambiente para ver a sessão.' })}
|
|
17
20
|
>
|
|
18
21
|
Aviso
|
|
19
22
|
</Button>
|
|
20
23
|
```
|
|
21
24
|
|
|
22
|
-
##
|
|
25
|
+
## Ações e progresso
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
`actions` recebe uma coleção ordenada de ações compactas. A última ação ganha destaque primário por
|
|
28
|
+
padrão; as anteriores usam tratamento neutro e podem declarar `context` e `variant` quando a
|
|
29
|
+
hierarquia precisar ser diferente. A região fica alinhada ao fim lógico da superfície, no mesmo
|
|
30
|
+
canto usado pelas ações do Alert. O clique fecha o toast, salvo quando o handler chama
|
|
31
|
+
`event.preventDefault()`.
|
|
32
|
+
|
|
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
|
+
`toast.promise` acompanha uma promessa e atualiza a mesma notificação nos estados de carregamento,
|
|
37
|
+
sucesso ou erro.
|
|
25
38
|
|
|
26
39
|
```tsx preview
|
|
27
40
|
<Button
|
|
28
41
|
variant="outline"
|
|
29
42
|
onClick={() =>
|
|
30
43
|
toast('Sessão arquivada', {
|
|
31
|
-
|
|
44
|
+
actions: [
|
|
45
|
+
{ label: 'Desfazer', onClick: () => toast.success('Sessão restaurada') },
|
|
46
|
+
],
|
|
32
47
|
})
|
|
33
48
|
}
|
|
34
49
|
>
|
|
@@ -48,9 +63,10 @@ action põe um botão no toast (ex.: desfazer); toast.promise acompanha uma oper
|
|
|
48
63
|
</Button>
|
|
49
64
|
```
|
|
50
65
|
|
|
51
|
-
## Toaster
|
|
66
|
+
## Toaster na raiz
|
|
52
67
|
|
|
53
|
-
Monte
|
|
68
|
+
Monte um único `Toaster` na raiz do aplicativo. O tema vem da propriedade `theme`, cujo padrão é
|
|
69
|
+
`system`; não há dependência de `next-themes`.
|
|
54
70
|
|
|
55
71
|
```tsx
|
|
56
72
|
/* main.tsx do app — uma vez. */
|
|
@@ -58,10 +74,28 @@ Monte uma vez, fora do App. Divergência declarada: sem next-themes — o tema v
|
|
|
58
74
|
<Toaster />
|
|
59
75
|
```
|
|
60
76
|
|
|
61
|
-
##
|
|
77
|
+
## Propriedades de Toaster
|
|
78
|
+
|
|
79
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
80
|
+
|---|---|---|---|
|
|
81
|
+
| `theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Define o tema das notificações; sem `next-themes`, a escolha pertence ao aplicativo. |
|
|
82
|
+
| `position` | `'bottom-right' \| 'top-center' \| …` | `'bottom-right'` | Região da tela onde as notificações aparecem. |
|
|
83
|
+
|
|
84
|
+
## Opções de toast
|
|
85
|
+
|
|
86
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
87
|
+
|---|---|---|---|
|
|
88
|
+
| `description` | `React.ReactNode` | | Complemento exibido abaixo do título. |
|
|
89
|
+
| `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. |
|
|
91
|
+
|
|
92
|
+
## Propriedades de ToastAction
|
|
62
93
|
|
|
63
|
-
|
|
|
94
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
64
95
|
|---|---|---|---|
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
96
|
+
| `label` | `React.ReactNode` | obrigatório | Conteúdo visível da ação. |
|
|
97
|
+
| `onClick` | `(event: React.MouseEvent<HTMLButtonElement>) => void` | obrigatório | Executa a ação. Chame `event.preventDefault()` para manter o toast aberto. |
|
|
98
|
+
| `context` | `ButtonContext` | última: `'primary'`; anteriores: `'neutral'` | Define a intenção semântica do botão. |
|
|
99
|
+
| `variant` | `ButtonVariant` | última: `'solid'`; anteriores: `'ghost'` | Define o tratamento visual do botão. |
|
|
100
|
+
| `disabled` | `boolean` | `false` | Impede a interação com a ação. |
|
|
101
|
+
| `key` | `React.Key` | posição na coleção | Mantém a identidade da ação entre renderizações. |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Alternar um estado
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Toggle` para uma ação que alterna entre ligada e desligada. `defaultPressed` define o estado
|
|
4
|
+
inicial no modo não controlado; o conteúdo pode ser texto ou ícone.
|
|
4
5
|
|
|
5
6
|
```tsx preview
|
|
6
7
|
<Toggle defaultPressed aria-label="Negrito">
|
|
@@ -10,7 +11,7 @@ Um botão que lembra se está ligado. defaultPressed deixa o estado com o compon
|
|
|
10
11
|
|
|
11
12
|
## Controlado
|
|
12
13
|
|
|
13
|
-
pressed
|
|
14
|
+
Use `pressed` e `onPressedChange` quando o estado pertencer ao consumidor.
|
|
14
15
|
|
|
15
16
|
```tsx preview
|
|
16
17
|
const [readOnly, setReadOnly] = useState(true)
|
|
@@ -29,7 +30,8 @@ render(
|
|
|
29
30
|
|
|
30
31
|
## Variantes e tamanhos
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
Na variante `default`, o fundo aparece somente quando o controle está ativo; `outline` mantém a
|
|
34
|
+
borda. `size` ajusta a altura.
|
|
33
35
|
|
|
34
36
|
```tsx preview
|
|
35
37
|
<Toggle aria-label="Quebra de linha">
|
|
@@ -60,13 +62,79 @@ disabled esmaece e bloqueia o clique — o estado pressed permanece visível.
|
|
|
60
62
|
</Toggle>
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
##
|
|
65
|
+
## Propriedades de Toggle
|
|
64
66
|
|
|
65
|
-
|
|
|
67
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
66
68
|
|---|---|---|---|
|
|
67
69
|
| `pressed` | `boolean` | | O estado ligado/desligado no modo controlado — parear com onPressedChange. |
|
|
68
70
|
| `onPressedChange` | `(pressed: boolean) => void` | | Chamado a cada alternância, com o novo estado. |
|
|
69
71
|
| `defaultPressed` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
70
72
|
| `variant` | `'default' \| 'outline'` | `'default'` | default não tem borda (fundo só quando ativo); outline carrega a borda. |
|
|
71
|
-
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Altura do botão — sm
|
|
73
|
+
| `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Altura do botão — sm para toolbar densa, lg para alvo mais confortável. |
|
|
72
74
|
| `disabled` | `boolean` | `false` | Esmaece e bloqueia o clique, preservando o estado visual. |
|
|
75
|
+
|
|
76
|
+
## ToggleGroup
|
|
77
|
+
|
|
78
|
+
Use `ToggleGroup` quando vários toggles formarem uma única escolha ou uma coleção de estados
|
|
79
|
+
relacionados. `type="single"` mantém um item ativo; `type="multiple"` aceita vários.
|
|
80
|
+
|
|
81
|
+
### Escolha única
|
|
82
|
+
|
|
83
|
+
```tsx preview
|
|
84
|
+
const [view, setView] = useState('sessions')
|
|
85
|
+
|
|
86
|
+
render(
|
|
87
|
+
<ToggleGroup type="single" value={view} onValueChange={(v) => v && setView(v)}>
|
|
88
|
+
<ToggleGroupItem value="overview">Visão geral</ToggleGroupItem>
|
|
89
|
+
<ToggleGroupItem value="sessions">Sessões</ToggleGroupItem>
|
|
90
|
+
<ToggleGroupItem value="skills">Habilidades</ToggleGroupItem>
|
|
91
|
+
</ToggleGroup>,
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Escolha múltipla
|
|
96
|
+
|
|
97
|
+
```tsx preview
|
|
98
|
+
const [marks, setMarks] = useState(['bold'])
|
|
99
|
+
|
|
100
|
+
render(
|
|
101
|
+
<ToggleGroup type="multiple" value={marks} onValueChange={setMarks}>
|
|
102
|
+
<ToggleGroupItem value="bold" aria-label="Negrito"><Bold /></ToggleGroupItem>
|
|
103
|
+
<ToggleGroupItem value="italic" aria-label="Itálico"><Italic /></ToggleGroupItem>
|
|
104
|
+
<ToggleGroupItem value="underline" aria-label="Sublinhado"><Underline /></ToggleGroupItem>
|
|
105
|
+
</ToggleGroup>,
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Variante e espaçamento
|
|
110
|
+
|
|
111
|
+
`variant`, `size` e `shape` definidos no grupo chegam aos itens por contexto. `spacing` separa os
|
|
112
|
+
itens; com zero, eles formam um bloco contínuo.
|
|
113
|
+
|
|
114
|
+
```tsx preview
|
|
115
|
+
<ToggleGroup type="single" variant="outline" spacing={2} defaultValue="developer">
|
|
116
|
+
<ToggleGroupItem value="developer">developer</ToggleGroupItem>
|
|
117
|
+
<ToggleGroupItem value="reviewer">reviewer</ToggleGroupItem>
|
|
118
|
+
<ToggleGroupItem value="designer">designer</ToggleGroupItem>
|
|
119
|
+
</ToggleGroup>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Propriedades de ToggleGroup
|
|
123
|
+
|
|
124
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
125
|
+
|---|---|---|---|
|
|
126
|
+
| `type` | `'single' \| 'multiple'` | | Define seleção única (`string`) ou múltipla (`string[]`). |
|
|
127
|
+
| `value` | `string \| string[]` | | Seleção controlada; o tipo acompanha `type`. |
|
|
128
|
+
| `onValueChange` | `(value: string \| string[]) => void` | | Informa a nova seleção. No modo single, uma string vazia representa nenhum item ativo. |
|
|
129
|
+
| `defaultValue` | `string \| string[]` | | Seleção inicial no modo não controlado. |
|
|
130
|
+
| `variant` | `'default' \| 'outline'` | `'default'` | Tratamento visual repassado aos itens. |
|
|
131
|
+
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Tamanho repassado aos itens. |
|
|
132
|
+
| `shape` | `'default' \| 'pill'` | `'default'` | Geometria do grupo e de suas extremidades. |
|
|
133
|
+
| `spacing` | `number` | `0` | Distância entre os itens em unidades de spacing. |
|
|
134
|
+
|
|
135
|
+
### Propriedades de ToggleGroupItem
|
|
136
|
+
|
|
137
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
138
|
+
|---|---|---|---|
|
|
139
|
+
| `value` | `string` | | Identificador que entra no valor do grupo quando o item é ativado. |
|
|
140
|
+
| `disabled` | `boolean` | `false` | Bloqueia somente este item e preserva seu estado visual. |
|
|
@@ -4,7 +4,7 @@ title: Tokens & Tema
|
|
|
4
4
|
|
|
5
5
|
# Tokens & Tema
|
|
6
6
|
|
|
7
|
-
O tema canônico vive no Opus (`theme.css`): tokens com a cor inteira na var, mapeados
|
|
7
|
+
O tema canônico vive no Opus (`theme.css`): tokens com a cor inteira na var, mapeados para o
|
|
8
8
|
Tailwind via `@theme inline`. Os swatches abaixo leem as vars **ao vivo** — troque o tema do app
|
|
9
9
|
e a página acompanha.
|
|
10
10
|
|
|
@@ -170,7 +170,7 @@ render(
|
|
|
170
170
|
> A regra anti-drift: o que não está declarado aqui (e no `theme.css`) é drift e deve ser
|
|
171
171
|
> sincronizado — skill `build-opus-ui`.
|
|
172
172
|
|
|
173
|
-
1. Formato HSL nos tokens (o upstream migrou
|
|
173
|
+
1. Formato HSL nos tokens (o upstream migrou para oklch) — legibilidade e ferramentas nossas; os
|
|
174
174
|
valores acompanham o upstream, só o formato difere.
|
|
175
175
|
2. Elevação no dark: `--card`/`--popover` ficam ACIMA de `--background` (10% vs 3.9%) — identidade
|
|
176
176
|
da casa; dialog e card "sobem" da página de verdade.
|
|
@@ -192,7 +192,7 @@ render(
|
|
|
192
192
|
|
|
193
193
|
## Identidade por app
|
|
194
194
|
|
|
195
|
-
> O tema do Opus é a base; cada app sobrescreve as vars
|
|
195
|
+
> O tema do Opus é a base; cada app sobrescreve as vars para ter a própria identidade sem forkar componente.
|
|
196
196
|
|
|
197
197
|
```css
|
|
198
198
|
/* index.css do app — depois do import do tema. */
|