@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.
Files changed (119) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/bin/lib/check.mjs +2 -7
  3. package/bin/lib/copy.mjs +1 -5
  4. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +93 -10
  5. package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
  6. package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
  7. package/docs/radius-scale.md +1 -1
  8. package/package.json +1 -1
  9. package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
  10. package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
  11. package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
  12. package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
  13. package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
  14. package/src/ui/components/patterns/confirm.tsx +140 -40
  15. package/src/ui/components/patterns/list.tsx +35 -40
  16. package/src/ui/components/patterns/page-state.tsx +2 -2
  17. package/src/ui/components/patterns/sidebar.tsx +26 -26
  18. package/src/ui/components/patterns/trigger.tsx +25 -22
  19. package/src/ui/components/primitives/alert.tsx +3 -3
  20. package/src/ui/components/primitives/dialog.tsx +196 -39
  21. package/src/ui/components/primitives/drawer.tsx +8 -5
  22. package/src/ui/components/primitives/empty.tsx +3 -3
  23. package/src/ui/components/primitives/item.tsx +3 -3
  24. package/src/ui/components/primitives/sonner.tsx +187 -8
  25. package/src/ui/docs/DocBrowser.tsx +102 -23
  26. package/src/ui/docs/content/accordion.md +22 -16
  27. package/src/ui/docs/content/action-form-card.md +8 -8
  28. package/src/ui/docs/content/action-form-dialog.md +9 -9
  29. package/src/ui/docs/content/action-form.md +28 -34
  30. package/src/ui/docs/content/action-list-dialog.md +11 -6
  31. package/src/ui/docs/content/action-list.md +64 -39
  32. package/src/ui/docs/content/action-trigger.md +21 -14
  33. package/src/ui/docs/content/action-view.md +8 -8
  34. package/src/ui/docs/content/actions.md +9 -9
  35. package/src/ui/docs/content/ai.md +3 -3
  36. package/src/ui/docs/content/alert.md +14 -12
  37. package/src/ui/docs/content/aspect-ratio.md +4 -4
  38. package/src/ui/docs/content/audit.md +2 -2
  39. package/src/ui/docs/content/auth.md +3 -3
  40. package/src/ui/docs/content/avatar.md +34 -14
  41. package/src/ui/docs/content/badge.md +3 -3
  42. package/src/ui/docs/content/breadcrumb.md +13 -8
  43. package/src/ui/docs/content/button.md +81 -6
  44. package/src/ui/docs/content/calendar.md +5 -5
  45. package/src/ui/docs/content/card.md +1 -1
  46. package/src/ui/docs/content/carousel.md +16 -11
  47. package/src/ui/docs/content/chat.md +3 -3
  48. package/src/ui/docs/content/checkbox.md +7 -7
  49. package/src/ui/docs/content/cli.md +5 -5
  50. package/src/ui/docs/content/collapsible.md +8 -8
  51. package/src/ui/docs/content/command.md +16 -8
  52. package/src/ui/docs/content/composer.md +2 -2
  53. package/src/ui/docs/content/content.md +2 -2
  54. package/src/ui/docs/content/copyable.md +4 -3
  55. package/src/ui/docs/content/customization.md +5 -5
  56. package/src/ui/docs/content/cycle.md +3 -3
  57. package/src/ui/docs/content/data-state.md +11 -12
  58. package/src/ui/docs/content/data.md +26 -33
  59. package/src/ui/docs/content/detail.md +3 -3
  60. package/src/ui/docs/content/dialog.md +339 -31
  61. package/src/ui/docs/content/dictionary-value.md +8 -8
  62. package/src/ui/docs/content/dock.md +3 -3
  63. package/src/ui/docs/content/drawer.md +27 -14
  64. package/src/ui/docs/content/empty-value.md +2 -2
  65. package/src/ui/docs/content/empty.md +19 -12
  66. package/src/ui/docs/content/events.md +4 -4
  67. package/src/ui/docs/content/field.md +34 -12
  68. package/src/ui/docs/content/getting-started.md +1 -1
  69. package/src/ui/docs/content/icon-picker.md +8 -4
  70. package/src/ui/docs/content/input-otp.md +20 -12
  71. package/src/ui/docs/content/input.md +121 -9
  72. package/src/ui/docs/content/item.md +27 -13
  73. package/src/ui/docs/content/kbd.md +19 -11
  74. package/src/ui/docs/content/label.md +5 -3
  75. package/src/ui/docs/content/log.md +4 -4
  76. package/src/ui/docs/content/markdown.md +7 -6
  77. package/src/ui/docs/content/mcp.md +13 -15
  78. package/src/ui/docs/content/menu.md +34 -16
  79. package/src/ui/docs/content/observability.md +2 -2
  80. package/src/ui/docs/content/page.md +51 -6
  81. package/src/ui/docs/content/pagination.md +22 -17
  82. package/src/ui/docs/content/popover.md +16 -8
  83. package/src/ui/docs/content/progress.md +7 -5
  84. package/src/ui/docs/content/queue.md +5 -5
  85. package/src/ui/docs/content/radio-group.md +20 -12
  86. package/src/ui/docs/content/router.md +11 -6
  87. package/src/ui/docs/content/scheduler.md +4 -5
  88. package/src/ui/docs/content/scroll-area.md +12 -7
  89. package/src/ui/docs/content/select.md +42 -29
  90. package/src/ui/docs/content/separator.md +5 -5
  91. package/src/ui/docs/content/sidebar.md +323 -54
  92. package/src/ui/docs/content/skeleton.md +3 -2
  93. package/src/ui/docs/content/slider.md +8 -7
  94. package/src/ui/docs/content/spinner.md +8 -8
  95. package/src/ui/docs/content/split.md +8 -5
  96. package/src/ui/docs/content/storage.md +6 -8
  97. package/src/ui/docs/content/switch.md +8 -7
  98. package/src/ui/docs/content/table.md +13 -3
  99. package/src/ui/docs/content/tabs.md +28 -14
  100. package/src/ui/docs/content/testing.md +9 -11
  101. package/src/ui/docs/content/textarea.md +5 -4
  102. package/src/ui/docs/content/toast.md +47 -13
  103. package/src/ui/docs/content/toggle.md +75 -7
  104. package/src/ui/docs/content/tokens.md +3 -3
  105. package/src/ui/docs/content/tooltip.md +19 -11
  106. package/src/ui/docs/content/truncate.md +7 -8
  107. package/src/ui/docs/content/ui.md +10 -9
  108. package/src/ui/docs/content/upgrading.md +7 -8
  109. package/src/ui/docs/registry.tsx +20 -37
  110. package/src/ui/meta.ts +64 -94
  111. package/src/ui/react.tsx +15 -16
  112. package/src/ui/theme.css +50 -0
  113. package/src/ui/components/primitives/alert-dialog.tsx +0 -192
  114. package/src/ui/docs/content/alert-dialog.md +0 -73
  115. package/src/ui/docs/content/button-group.md +0 -71
  116. package/src/ui/docs/content/confirm.md +0 -120
  117. package/src/ui/docs/content/input-group.md +0 -79
  118. package/src/ui/docs/content/page-state.md +0 -45
  119. package/src/ui/docs/content/toggle-group.md +0 -81
@@ -1,6 +1,7 @@
1
- ## Básico
1
+ ## Um valor na faixa
2
2
 
3
- defaultValue é um array — [n] pra um thumb. min, max e step delimitam a faixa; aqui de 0 a 100 em passos de 5.
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 leia value[0] pra um thumb. Bom pra mostrar o valor escolhido ao lado do rótulo.
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
- value com dois números rende dois thumbs e a faixa fica entre eles — o jeito de filtrar por um intervalo, como o custo estimado de uma task.
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
- ## Props
54
+ ## Propriedades de Slider
54
55
 
55
- | Prop | Tipo | Default | Descrição |
56
+ | Propriedade | Tipo | Padrão | Descrição |
56
57
  |---|---|---|---|
57
- | `value` | `number[]` | | O valor no modo controlado — [n] pra um thumb, [a, b] pra um intervalo. Pareie com onValueChange. |
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
- ## Tamanhos
1
+ ## Carregamento sem progresso conhecido
2
2
 
3
- Divergência da casa: o shadcn atual tem size-4 fixo; mantivemos sm/default/lg pra cobrir botão
4
- (sm) e estados maiores (lg). O aria-label Carregando vem embutido.
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 componentepossui 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
- Ação em andamento: disabled + Spinner sm + verbo no gerúndio substitui o Loader2Icon inline
15
- repetido.
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
- ## Props
33
+ ## Propriedades de Spinner
34
34
 
35
- | Prop | Tipo | Default | Descrição |
35
+ | Propriedade | Tipo | Padrão | Descrição |
36
36
  |---|---|---|---|
37
- | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | O tamanho — sm pra dentro de botão, lg pra estados de página. |
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
- ## Split & Pane
1
+ ## Dividir uma área
2
2
 
3
- `Split` organiza áreas lado a lado ou uma sobre a outra. A ordem dos `Pane` define a posição de cada área. Sem `resizable`, o layout usa flex; com essa opção, a pessoa também pode ajustar as divisórias com ponteiro ou teclado.
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
- ## Inset
22
+ ## Espaçamento interno
21
23
 
22
- O `Pane` traz `inset="md"` por default. Use `none` para chrome, navegação e conteúdo que já possui padding próprio; `sm` e `lg` atendem densidades comuns. `className` sempre pode ajustar o caso específico.
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
- ## Rail
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
- Upload, download, remoção e URL de acesso o que backoffice precisa de arquivos
11
- (anexo de nota, foto de produto, contrato). O contrato é um adapter do core
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
- Keys passam pela mesma régua em todo driver (`normalizeKey`): relativas, sem `..`,
27
- sem `\` traversal é barrado no contrato, não em cada driver.
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` pro
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 (por enquanto)
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
- ## Básico
1
+ ## Alternar uma preferência
2
2
 
3
- Sempre em par com Label (htmlFor↔id) clicar no texto alterna a chave. defaultChecked pro modo não controlado.
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
- onCheckedChange recebe um boolean pareie com checked pra guardar o estado no componente que decide.
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 encolhe a chave — pra densidade em linha de lista, como cada repositório do workspace.
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
- ## Props
62
+ ## Propriedades de Switch
62
63
 
63
- | Prop | Tipo | Default | Descrição |
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 pra densidade em linha de lista. |
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
- A Table vem SEM borda externa as divisórias de linha (a última o TableBody zera). Coluna numérica alinha à direita (text-right no TableHead E no TableCell). A TableCaption fica embaixo (caption-bottom) e descreve a tabela.
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
- ## Moldura (o datagrid da casa)
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 fecha a tabela com a linha de agregação fundo muted e peso de fonte já vêm prontos.
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
- ## Padrão (pill)
1
+ ## Alternar painéis relacionados
2
2
 
3
- O value do TabsTrigger pareia com o do TabsContent. defaultValue deixa o estado com o componente; pra controlar, use value + onValueChange.
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 na TabsList: fundo transparente, o ativo é marcado pelo traço embaixo — bom pra cabeçalho de página, onde o pill pesaria.
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 no Tabs: a lista vira coluna e o traço da variante line migra pra lateral direita do trigger.
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
- ## Densidade (size)
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
- ## Props
88
+ ## Propriedades de Tabs
85
89
 
86
- | Prop | Tipo | Default | Descrição |
90
+ | Propriedade | Tipo | Padrão | Descrição |
87
91
  |---|---|---|---|
88
- | `defaultValue (Tabs)` | `string` | | A aba inicial no modo não-controlado. |
89
- | `value (Tabs)` | `string` | | A aba ativa no modo controlado pareie com onValueChange. |
90
- | `onValueChange (Tabs)` | `(value: string) => void` | | Chamado quando o usuário troca de aba. |
91
- | `size (Tabs)` | `'default' \| 'sm'` | `'default'` | Altura da lista: default (h-9) ou sm (h-8), o par do sm de Button/Select. Flui pra TabsList por contexto. |
92
- | `orientation (Tabs)` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas — vertical vira coluna lateral. |
93
- | `variant (TabsList)` | `'default' \| 'line'` | `'default'` | default é o pill (fundo muted); line é a barra sublinhada, sem fundo. |
94
- | `value (TabsTrigger/TabsContent)` | `string` | | Identificador que pareia o trigger com o painel correspondente. |
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
- A unidade natural de teste no Opus é a **action** o contrato: input → output/erro,
8
- bordas do schema, autorização fail-closed. `@softize/opus/testing` roda esse contrato
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
- ## runAction o contrato em unidade
10
+ ## Executar o contrato em unidade
12
11
 
13
- O harness executa o **mesmo pipeline do runtime**: valida o input cobra o gate
14
- `public` avalia o `authorize` (string DSL ou closure, semântica idêntica) → roda o
15
- handler → valida o output. Falha estoura o mesmo `ActionError` tipado do runtime
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`, pra quem quer só o ctx) devolve os efeitos capturados —
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 — pra handler
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
- ## O que fica de fora
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
- ## Básico
1
+ ## Texto com várias linhas
2
2
 
3
- Cresce com o conteúdo a partir da altura mínima (field-sizing-content) digite e veja.
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
- O mesmo par htmlFor↔id dos outros campos o overview do handoff é o caso típico.
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 pinta borda e anel destructive; disabled esmaece e bloqueia a edição.
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
- ## Tipos
1
+ ## Notificação temporária
2
2
 
3
- Cada tipo vem com o ícone lucide do Opus. O título é label (sem ponto); a description, frase (com ponto).
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 pra ver a sessão.' })}
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
- ## Ação e progresso
25
+ ## Ações e progresso
23
26
 
24
- action põe um botão no toast (ex.: desfazer); toast.promise acompanha uma operação loading vira sucesso ou erro sozinho.
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
- action: { label: 'Desfazer', onClick: () => toast.success('Sessão restaurada') },
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 no root
66
+ ## Toaster na raiz
52
67
 
53
- Monte uma vez, fora do App. Divergência declarada: sem next-themes — o tema vem por prop (default 'system'); a elevação dark usa bg-popover.
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
- ## Props
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
- | Prop | Tipo | Default | Descrição |
94
+ | Propriedade | Tipo | Padrão | Descrição |
64
95
  |---|---|---|---|
65
- | `Toaster.theme` | `'light' \| 'dark' \| 'system'` | `'system'` | Força o tema dos toasts — sem next-themes, a escolha é do app. |
66
- | `Toaster.position` | `'bottom-right' \| 'top-center' \| …` | `'bottom-right'` | Canto onde os toasts aparecem (sonner). |
67
- | `toast(title, opts)` | `{ description?, action?, duration?, }` | | A API de disparo (sonner): descrição, botão de ação e duração por toast. |
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
- ## Básico
1
+ ## Alternar um estado
2
2
 
3
- Um botão que lembra se está ligado. defaultPressed deixa o estado com o componente; o filho costuma ser um ícone.
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 + onPressedChange tiram o estado do componente — o booleano fica no seu store. Bom pra alternar o modo somente-leitura de uma sessão.
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
- variant default não tem borda (o fundo aparece ativo); outline carrega a borda. size sm/default/lg ajusta a altura.
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
- ## Props
65
+ ## Propriedades de Toggle
64
66
 
65
- | Prop | Tipo | Default | Descrição |
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 pra toolbar densa, lg pra alvo mais confortável. |
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 pro
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 pra oklch) — legibilidade e ferramentas nossas; os
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 pra ter a própria identidade sem forkar componente.
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. */