@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
package/src/ui/react.tsx CHANGED
@@ -125,11 +125,17 @@ export {
125
125
  DialogOverlay,
126
126
  DialogContent,
127
127
  DialogHeader,
128
+ DialogMedia,
128
129
  DialogBody,
129
130
  DialogFooter,
130
131
  DialogTitle,
131
132
  DialogDescription,
132
133
  } from "./components/primitives/dialog.tsx";
134
+ export type {
135
+ DialogProps,
136
+ DialogCloseProps,
137
+ DialogMode,
138
+ } from "./components/primitives/dialog.tsx";
133
139
 
134
140
  export {
135
141
  Menu,
@@ -203,6 +209,10 @@ export type {
203
209
  } from "./components/primitives/select.tsx";
204
210
 
205
211
  export { Toaster, toast } from "./components/primitives/sonner.tsx";
212
+ export type {
213
+ ToastAction,
214
+ ToastOptions,
215
+ } from "./components/primitives/sonner.tsx";
206
216
  export { Markdown } from "./components/primitives/markdown.tsx";
207
217
 
208
218
  // shadcn (lote inicial — byte-fiéis via registry JSON, locked no registry.lock.json)
@@ -212,20 +222,6 @@ export {
212
222
  AccordionItem,
213
223
  AccordionTrigger,
214
224
  } from "./components/primitives/accordion.tsx";
215
- export {
216
- AlertDialog,
217
- AlertDialogAction,
218
- AlertDialogCancel,
219
- AlertDialogContent,
220
- AlertDialogDescription,
221
- AlertDialogFooter,
222
- AlertDialogHeader,
223
- AlertDialogMedia,
224
- AlertDialogOverlay,
225
- AlertDialogPortal,
226
- AlertDialogTitle,
227
- AlertDialogTrigger,
228
- } from "./components/primitives/alert-dialog.tsx";
229
225
  export { AspectRatio } from "./components/primitives/aspect-ratio.tsx";
230
226
  export {
231
227
  Avatar,
@@ -279,7 +275,7 @@ export {
279
275
  } from "./components/primitives/drawer.tsx";
280
276
  export {
281
277
  Empty,
282
- EmptyContent,
278
+ EmptyActions,
283
279
  EmptyDescription,
284
280
  EmptyHeader,
285
281
  EmptyMedia,
@@ -407,7 +403,9 @@ export {
407
403
  } from "./components/patterns/confirm.tsx";
408
404
  export type {
409
405
  AlertOptions,
406
+ ChooseOptions,
410
407
  ConfirmOptions,
408
+ DialogChoice,
411
409
  PromptOptions,
412
410
  } from "./components/patterns/confirm.tsx";
413
411
  export type { ActionTriggerProps } from "./components/patterns/trigger.tsx";
@@ -488,13 +486,14 @@ export {
488
486
  Sidebar,
489
487
  SidebarItem,
490
488
  SidebarGroupLabel,
489
+ SidebarTreeGroup,
491
490
  SidebarNav,
492
491
  } from "./components/patterns/sidebar.tsx";
493
492
  export type {
494
493
  SidebarProps,
495
494
  SidebarItemProps,
495
+ SidebarTreeGroupProps,
496
496
  SidebarNavGroup,
497
- SidebarNavSubgroup,
498
497
  SidebarNavItem,
499
498
  } from "./components/patterns/sidebar.tsx";
500
499
 
package/src/ui/theme.css CHANGED
@@ -324,3 +324,53 @@
324
324
  .prose :where(pre):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
325
325
  border: 1px solid var(--border);
326
326
  }
327
+
328
+ /* O Sonner reserva por padrão apenas 1rem para a mídia. A moldura semântica do
329
+ * Opus ocupa 2.25rem; a coluna precisa ter a mesma medida para não avançar sob
330
+ * título e descrição. Estas classes são a fronteira estável com o CSS interno
331
+ * do Sonner, independentemente da ordem em que a biblioteca injeta seus estilos. */
332
+ .opus-toast {
333
+ gap: 0.75rem;
334
+ }
335
+
336
+ .opus-toast .opus-toast-icon {
337
+ width: 2.25rem;
338
+ height: 2.25rem;
339
+ margin: 0;
340
+ align-self: flex-start;
341
+ align-items: center;
342
+ justify-content: center;
343
+ }
344
+
345
+ .toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button]:focus-visible {
346
+ box-shadow: 0 0 0 0.125rem color-mix(in srgb, var(--ring) 50%, transparent);
347
+ }
348
+
349
+ .toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button][data-disabled='true'] {
350
+ pointer-events: none;
351
+ opacity: 0.5;
352
+ }
353
+
354
+ /* Fechar continua sendo um controle circular de ícone, mas deixa de herdar a
355
+ * paleta cinza, a medida e o foco particulares do Sonner. */
356
+ .toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button] {
357
+ box-sizing: border-box;
358
+ width: 1.5rem;
359
+ height: 1.5rem;
360
+ border: 0;
361
+ border-radius: 9999rem;
362
+ background: var(--popover);
363
+ color: var(--muted-foreground);
364
+ outline: none;
365
+ transition: color 150ms, background-color 150ms, box-shadow 150ms;
366
+ }
367
+
368
+ .toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button]:hover {
369
+ background: var(--accent);
370
+ color: var(--accent-foreground);
371
+ }
372
+
373
+ .toaster .opus-toast[data-sonner-toast] .opus-toast-close[data-close-button] svg {
374
+ width: 1rem;
375
+ height: 1rem;
376
+ }
@@ -1,192 +0,0 @@
1
- import * as React from "react"
2
- import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
3
-
4
- import { cn } from '../../lib/cn.ts'
5
- import { Button } from './button.tsx'
6
-
7
- function AlertDialog({
8
- ...props
9
- }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
10
- return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
11
- }
12
-
13
- function AlertDialogTrigger({
14
- ...props
15
- }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
16
- return (
17
- <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
18
- )
19
- }
20
-
21
- function AlertDialogPortal({
22
- ...props
23
- }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
24
- return (
25
- <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
26
- )
27
- }
28
-
29
- function AlertDialogOverlay({
30
- className,
31
- ...props
32
- }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
33
- return (
34
- <AlertDialogPrimitive.Overlay
35
- data-slot="alert-dialog-overlay"
36
- className={cn(
37
- "fixed inset-0 z-50 bg-black/30 backdrop-blur-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
38
- className
39
- )}
40
- {...props}
41
- />
42
- )
43
- }
44
-
45
- function AlertDialogContent({
46
- className,
47
- ...props
48
- }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
49
- return (
50
- <AlertDialogPortal>
51
- <AlertDialogOverlay />
52
- <AlertDialogPrimitive.Content
53
- data-slot="alert-dialog-content"
54
- className={cn(
55
- "group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-xs translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl border border-border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
56
- className
57
- )}
58
- {...props}
59
- />
60
- </AlertDialogPortal>
61
- )
62
- }
63
-
64
- function AlertDialogHeader({
65
- className,
66
- ...props
67
- }: React.ComponentProps<"div">) {
68
- return (
69
- <div
70
- data-slot="alert-dialog-header"
71
- className={cn(
72
- "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr]",
73
- className
74
- )}
75
- {...props}
76
- />
77
- )
78
- }
79
-
80
- function AlertDialogFooter({
81
- className,
82
- ...props
83
- }: React.ComponentProps<"div">) {
84
- return (
85
- <div
86
- data-slot="alert-dialog-footer"
87
- className={cn(
88
- "grid grid-cols-2 gap-2",
89
- className
90
- )}
91
- {...props}
92
- />
93
- )
94
- }
95
-
96
- function AlertDialogTitle({
97
- className,
98
- ...props
99
- }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
100
- return (
101
- <AlertDialogPrimitive.Title
102
- data-slot="alert-dialog-title"
103
- className={cn(
104
- "text-lg font-semibold",
105
- className
106
- )}
107
- {...props}
108
- />
109
- )
110
- }
111
-
112
- function AlertDialogDescription({
113
- className,
114
- ...props
115
- }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
116
- return (
117
- <AlertDialogPrimitive.Description
118
- data-slot="alert-dialog-description"
119
- className={cn("text-sm text-muted-foreground", className)}
120
- {...props}
121
- />
122
- )
123
- }
124
-
125
- function AlertDialogMedia({
126
- className,
127
- ...props
128
- }: React.ComponentProps<"div">) {
129
- return (
130
- <div
131
- data-slot="alert-dialog-media"
132
- className={cn(
133
- "mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted *:[svg:not([class*='size-'])]:size-8",
134
- className
135
- )}
136
- {...props}
137
- />
138
- )
139
- }
140
-
141
- function AlertDialogAction({
142
- className,
143
- context = "primary",
144
- variant = "solid",
145
- size = "default",
146
- ...props
147
- }: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
148
- Pick<React.ComponentProps<typeof Button>, "context" | "variant" | "size">) {
149
- return (
150
- <Button context={context} variant={variant} size={size} asChild>
151
- <AlertDialogPrimitive.Action
152
- data-slot="alert-dialog-action"
153
- className={cn(className)}
154
- {...props}
155
- />
156
- </Button>
157
- )
158
- }
159
-
160
- function AlertDialogCancel({
161
- className,
162
- context = "neutral",
163
- variant = "ghost",
164
- size = "default",
165
- ...props
166
- }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
167
- Pick<React.ComponentProps<typeof Button>, "context" | "variant" | "size">) {
168
- return (
169
- <Button context={context} variant={variant} size={size} asChild>
170
- <AlertDialogPrimitive.Cancel
171
- data-slot="alert-dialog-cancel"
172
- className={cn(className)}
173
- {...props}
174
- />
175
- </Button>
176
- )
177
- }
178
-
179
- export {
180
- AlertDialog,
181
- AlertDialogAction,
182
- AlertDialogCancel,
183
- AlertDialogContent,
184
- AlertDialogDescription,
185
- AlertDialogFooter,
186
- AlertDialogHeader,
187
- AlertDialogMedia,
188
- AlertDialogOverlay,
189
- AlertDialogPortal,
190
- AlertDialogTitle,
191
- AlertDialogTrigger,
192
- }
@@ -1,73 +0,0 @@
1
- ## Quase sempre é o `dialog`
2
-
3
- Pergunta de sim/não, um aviso a reconhecer, uma string — **não monte isto à mão**: o trio
4
- `dialog.confirm/alert/prompt` faz em uma linha, e o `body` cobre até corpo com conteúdo
5
- próprio (lista, detalhe). A demo viva mora na [página do dialog](/ui/confirm). Este
6
- componente é o **primitivo por baixo deles**.
7
-
8
- Então **componha o AlertDialog à mão só quando o trio não alcança**: mais de duas ações, ou
9
- um layout totalmente custom. É o que as seções abaixo mostram.
10
-
11
- ## Mais de duas ações
12
-
13
- O caso que o `confirm` (binário) não expressa: três saídas. `AlertDialogTrigger` (asChild
14
- com Button) abre; cada `AlertDialogAction`/`AlertDialogCancel` é uma saída. Diferente do
15
- Dialog, **não fecha clicando fora** — exige uma escolha.
16
-
17
- ```tsx preview
18
- <AlertDialog>
19
- <AlertDialogTrigger asChild>
20
- <Button variant="outline">Fechar editor</Button>
21
- </AlertDialogTrigger>
22
- <AlertDialogContent>
23
- <AlertDialogHeader>
24
- <AlertDialogTitle>Alterações não salvas</AlertDialogTitle>
25
- <AlertDialogDescription>
26
- Você editou o contrato e ainda não salvou. O que fazer antes de fechar?
27
- </AlertDialogDescription>
28
- </AlertDialogHeader>
29
- <AlertDialogFooter>
30
- <AlertDialogCancel>Continuar editando</AlertDialogCancel>
31
- <AlertDialogAction variant="outline">Descartar</AlertDialogAction>
32
- <AlertDialogAction>Salvar e fechar</AlertDialogAction>
33
- </AlertDialogFooter>
34
- </AlertDialogContent>
35
- </AlertDialog>
36
- ```
37
-
38
- ## A estrutura e o botão destrutivo
39
-
40
- `AlertDialogHeader` (media/título/descrição) + `AlertDialogFooter` (as ações).
41
- `context="danger"` no `AlertDialogAction` (ele herda contexto, variante e tamanho do Button)
42
- comunica que a confirmação é perigosa. Uma confirmação destrutiva **binária** é só
43
- `dialog.confirm({ context: 'danger' })` — o exemplo abaixo é só para mostrar a composição e
44
- onde o `context` entra:
45
-
46
- ```tsx preview
47
- <AlertDialog>
48
- <AlertDialogTrigger asChild>
49
- <Button context="danger">Excluir repositório</Button>
50
- </AlertDialogTrigger>
51
- <AlertDialogContent>
52
- <AlertDialogHeader>
53
- <AlertDialogTitle>Excluir empresa-x-api?</AlertDialogTitle>
54
- <AlertDialogDescription>
55
- O repositório sai do workspace e os agentes vinculados perdem o acesso ao código. Não dá pra desfazer.
56
- </AlertDialogDescription>
57
- </AlertDialogHeader>
58
- <AlertDialogFooter>
59
- <AlertDialogCancel>Cancelar</AlertDialogCancel>
60
- <AlertDialogAction context="danger">Excluir</AlertDialogAction>
61
- </AlertDialogFooter>
62
- </AlertDialogContent>
63
- </AlertDialog>
64
- ```
65
-
66
- ## Props
67
-
68
- | Prop | Tipo | Default | Descrição |
69
- |---|---|---|---|
70
- | `open` (AlertDialog) | `boolean` | | Estado de aberto no modo controlado — pareie com onOpenChange. No padrão, o Trigger cuida disso. |
71
- | `onOpenChange` (AlertDialog) | `(open: boolean) => void` | | Chamado quando o diálogo abre ou fecha (Trigger, Cancel, Action ou Esc). |
72
- | `context` (Action/Cancel) | `'neutral' \| 'primary' \| 'danger'` | `primary` / `neutral` | Significado semântico herdado do Button. |
73
- | `variant` (Action/Cancel) | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `solid` / `ghost` | Tratamento visual herdado do Button. |
@@ -1,71 +0,0 @@
1
- ## Básico
2
-
3
- Envolva os botões no ButtonGroup: as bordas internas colapsam e só as pontas ficam arredondadas.
4
- Use a mesma variant nos filhos pra manter a barra uniforme.
5
-
6
- ```tsx preview
7
- <ButtonGroup>
8
- <Button variant="outline">Visão geral</Button>
9
- <Button variant="outline">Sessões</Button>
10
- <Button variant="outline">Skills</Button>
11
- </ButtonGroup>
12
- ```
13
-
14
- ## Ação dividida
15
-
16
- O padrão da casa pra ação primária + variações: o botão principal, um separador e um gatilho de
17
- menu (size=icon).
18
-
19
- ```tsx preview
20
- <ButtonGroup>
21
- <Button icon={Play}>Rodar agente developer</Button>
22
- <ButtonGroupSeparator />
23
- <Button size="icon" aria-label="Mais opções">
24
- <ChevronDown />
25
- </Button>
26
- </ButtonGroup>
27
- ```
28
-
29
- ## Com rótulo
30
-
31
- ButtonGroupText cola um rótulo (fundo muted) ao grupo — bom pra prefixar o contexto. Aceita ícone
32
- via [&_svg] e asChild pra virar `<label>`.
33
-
34
- ```tsx preview
35
- <ButtonGroup>
36
- <ButtonGroupText>
37
- <GitBranch />
38
- empresa-x-api
39
- </ButtonGroupText>
40
- <Button variant="outline" icon={RotateCw}>
41
- Sincronizar
42
- </Button>
43
- </ButtonGroup>
44
- ```
45
-
46
- ## Vertical
47
-
48
- orientation=vertical empilha os filhos em coluna — as bordas que colapsam passam a ser as de
49
- cima/baixo. Combina com size=icon pra uma mini-toolbar.
50
-
51
- ```tsx preview col-start
52
- <ButtonGroup orientation="vertical">
53
- <Button variant="outline" size="icon" aria-label="Rodar sessão">
54
- <Play />
55
- </Button>
56
- <Button variant="outline" size="icon" aria-label="Pausar sessão">
57
- <Pause />
58
- </Button>
59
- <Button variant="outline" size="icon" aria-label="Reiniciar sessão">
60
- <RotateCw />
61
- </Button>
62
- </ButtonGroup>
63
- ```
64
-
65
- ## Props
66
-
67
- | Prop | Tipo | Default | Descrição |
68
- |---|---|---|---|
69
- | `orientation` (ButtonGroup) | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco — vertical empilha os filhos em coluna. |
70
- | `orientation` (ButtonGroupSeparator) | `'horizontal' \| 'vertical'` | `'vertical'` | Direção do traço divisor — herde do grupo (vertical no horizontal). |
71
- | `asChild` (ButtonGroupText) | `boolean` | `false` | Renderiza como o filho (Radix Slot) — ex.: virar `<label>` sem forkar o estilo. |
@@ -1,120 +0,0 @@
1
- ## O trio imperativo: `dialog`
2
-
3
- `dialog.alert` · `dialog.confirm` · `dialog.prompt` — o trio do browser (`alert`/`confirm`/`prompt`), mas **assíncrono, com tema e a11y-correto**. Cada um pergunta e **espera a resposta**:
4
-
5
- ```tsx
6
- await dialog.alert({ title: 'Sessão expirada', description: 'Você será desconectado.' }) // Promise<void>
7
- const ok = await dialog.confirm({ title: 'Excluir a sessão?', variant: 'destructive' }) // Promise<boolean>
8
- const nome = await dialog.prompt({ title: 'Nome do snapshot', placeholder: 'ex.: v1-final' }) // Promise<string | null>
9
- ```
10
-
11
- É a mesma forma do `toast` (superfície imperativa, host no shell), mas que **responde** — e é isso que tira as ~15 linhas de JSX que cada tela montava. Por baixo é o `AlertDialog`: `role="alertdialog"`, foco preso, **não fecha no clique fora** — a semântica de uma interrupção que EXIGE resposta. Fechar por ESC/Cancelar resolve o "não" de cada tipo (`void`, `false`, `null`).
12
-
13
- ```tsx preview
14
- function Demo() {
15
- const [last, setLast] = useState('—')
16
- return (
17
- <div className="flex flex-wrap items-center gap-3">
18
- <DialogHost />
19
- <Button variant="outline" size="sm" onClick={async () => {
20
- await dialog.alert({ title: 'Sessão expirada', description: 'Você será desconectado.' })
21
- setLast('alert → OK')
22
- }}>alert</Button>
23
- <Button variant="outline" size="sm" onClick={async () => {
24
- const ok = await dialog.confirm({ title: 'Excluir a sessão?', description: 'Some o worktree e o preview.', action: 'Excluir', variant: 'destructive' })
25
- setLast('confirm → ' + (ok ? 'excluída' : 'mantida'))
26
- }}>confirm</Button>
27
- <Button variant="outline" size="sm" onClick={async () => {
28
- const name = await dialog.prompt({ title: 'Nome do snapshot', placeholder: 'ex.: v1-final' })
29
- setLast('prompt → ' + (name === null ? 'cancelado' : name))
30
- }}>prompt</Button>
31
- <span className="text-sm text-muted-foreground">Última: {last}</span>
32
- </div>
33
- )
34
- }
35
- render(<Demo />)
36
- ```
37
-
38
- ## Por que namespace, e não `confirm()` solto
39
-
40
- `alert`/`confirm`/`prompt` são **globais do browser**. Como função solta, esquecer o import não daria erro — o TypeScript resolveria pro global e o código abriria a caixa cinza do sistema (sem tema, que webview suprime). `window.dialog` não existe, então o namespace **imuniza** a família contra esse footgun. `confirm()` segue exportado como alias (retrocompat), mas o novo código usa `dialog.confirm`.
41
-
42
- ## `body`: corpo com conteúdo próprio
43
-
44
- Os três aceitam `body` — conteúdo que a `description` de texto não expressa (uma lista do que será afetado, um detalhe destacado). É o que antes obrigava a compor o `AlertDialog` à mão:
45
-
46
- ```tsx preview
47
- function Demo() {
48
- const [status, setStatus] = useState('—')
49
- return (
50
- <div className="flex items-center gap-3">
51
- <DialogHost />
52
- <Button variant="outline" size="sm" onClick={async () => {
53
- const ok = await dialog.confirm({
54
- title: 'Encerrar 3 sessões?',
55
- description: 'Os agentes abaixo serão interrompidos:',
56
- body: (
57
- <ul className="list-disc space-y-1 pl-5 text-sm text-muted-foreground">
58
- <li><span className="font-medium text-foreground">developer</span> — empresa-x-api</li>
59
- <li><span className="font-medium text-foreground">reviewer</span> — empresa-x-web</li>
60
- </ul>
61
- ),
62
- action: 'Encerrar todas',
63
- })
64
- setStatus(ok ? 'encerradas' : 'mantidas')
65
- }}>Encerrar sessões</Button>
66
- <span className="text-sm text-muted-foreground">Resposta: {status}</span>
67
- </div>
68
- )
69
- }
70
- render(<Demo />)
71
- ```
72
-
73
- ## O host
74
-
75
- Monte **um** `<DialogHost />` no shell do app, ao lado do `<Toaster />`:
76
-
77
- ```tsx
78
- <>
79
- {rotas}
80
- <Toaster />
81
- <DialogHost />
82
- </>
83
- ```
84
-
85
- Sem ele, os três **lançam** — em vez de devolver uma promise que nunca resolve. Promise pendurada viraria clique sem efeito, o pior desfecho pra uma interrupção que exige resposta: a pessoa acha que respondeu, ou clica de novo. (`<ConfirmHost />` segue valendo como alias de `<DialogHost />`.)
86
-
87
- Várias perguntas seguidas entram numa **fila** e aparecem uma por vez — cada `await` recebe a resposta do SEU diálogo (duas caixas empilhadas seriam ambíguas sobre qual clique respondeu o quê).
88
-
89
- ## Quando NÃO usar
90
-
91
- - **Excluir uma entidade por contrato** → `ActionTrigger` com `icon={<Trash2 />}`: lê o `action.confirm`, dispara a action e fala o erro de negócio.
92
- - **Um form de verdade** (validação, vários campos, submete uma action) → `ActionFormDialog`. O `dialog.prompt` é só pra UMA string livre.
93
- - **Mais de duas ações** (ex.: Salvar / Descartar / Cancelar) ou **layout totalmente custom** → componha o `AlertDialog` à mão.
94
- - **Só avisar, sem cobrar decisão** → `toast` (passageiro) ou `Alert` (fica na página). O `dialog.alert` é pro aviso que **bloqueia** e exige um "OK".
95
-
96
- ## Opções
97
-
98
- Comuns aos três (`dialog.alert`/`confirm`/`prompt`):
99
-
100
- | Opção | Tipo | Default | Descrição |
101
- |---|---|---|---|
102
- | `title` | `React.ReactNode` | | O enunciado. Obrigatório. |
103
- | `description` | `React.ReactNode` | | O que a pessoa precisa saber pra decidir — a CONSEQUÊNCIA, não o óbvio. |
104
- | `body` | `React.ReactNode` | | Conteúdo próprio entre a descrição e os botões (lista, detalhe). |
105
- | `media` | `React.ReactNode` | | Ícone grande acima do título (o quadro do `AlertDialogMedia`). |
106
- | `action` | `string` | `'OK'` / `'Confirmar'` | Rótulo do botão que confirma (default `'OK'` no alert, `'Confirmar'` nos outros). |
107
-
108
- Só `confirm` e `prompt`:
109
-
110
- | Opção | Tipo | Default | Descrição |
111
- |---|---|---|---|
112
- | `cancel` | `string` | `'Cancelar'` | Rótulo do botão que recusa. |
113
- | `variant` | `'default' \| 'destructive'` | `'default'` | `destructive` pinta o confirmar — o que apaga tem que parecer o que apaga. |
114
-
115
- Só `prompt`:
116
-
117
- | Opção | Tipo | Default | Descrição |
118
- |---|---|---|---|
119
- | `placeholder` | `string` | | Texto-fantasma do input. |
120
- | `defaultValue` | `string` | | Valor inicial (o input abre focado e selecionado). |
@@ -1,79 +0,0 @@
1
- > Só um **ícone leading**? Não precisa do InputGroup — é a prop `icon` do `Input` (a mesma do Select e do Alert). O InputGroup é pros casos que a prop não cobre: botão no fim, prefixo de texto, adorno em bloco, múltiplos.
2
-
3
- ## Com ícone (addon)
4
-
5
- InputGroupAddon na borda (align inline-start por padrão) cola um ícone ao InputGroupInput dentro da mesma moldura. Clicar no addon foca o campo. Pra um leading simples, prefira `<Input icon={…} />`; aqui é quando o ícone convive com outros adornos.
6
-
7
- ```tsx preview col
8
- <InputGroup>
9
- <InputGroupAddon>
10
- <Search />
11
- </InputGroupAddon>
12
- <InputGroupInput placeholder="Buscar sessões do workspace" />
13
- </InputGroup>
14
- ```
15
-
16
- ## Ícone + ação juntos (o caso do InputGroup)
17
-
18
- > Só uma ação no fim (um limpar, um mostrar-senha)? Isso é `<Input trailing={…} />` — não precisa do InputGroup. O InputGroup entra quando os adornos se ACUMULAM: aqui, um ícone leading E um botão no fim, na mesma moldura (a busca do `ActionList` é assim). `align=inline-end` joga o addon pro fim; `InputGroupButton` já vem ghost, tamanho `icon-xs`.
19
-
20
- ```tsx preview col
21
- <InputGroup>
22
- <InputGroupAddon>
23
- <Search />
24
- </InputGroupAddon>
25
- <InputGroupInput placeholder="Buscar agente" defaultValue="reviewer" />
26
- <InputGroupAddon align="inline-end">
27
- <InputGroupButton size="icon-xs" aria-label="Limpar filtro">
28
- <X />
29
- </InputGroupButton>
30
- </InputGroupAddon>
31
- </InputGroup>
32
- ```
33
-
34
- ## Prefixo e sufixo
35
-
36
- InputGroupText é o rótulo inerte na borda — prefixo ou sufixo. Aqui emoldura o slug do repositório entre o host fixo e o sufixo .git.
37
-
38
- ```tsx preview col
39
- <InputGroup>
40
- <InputGroupAddon>
41
- <GitBranch />
42
- <InputGroupText>github.com/softize/</InputGroupText>
43
- </InputGroupAddon>
44
- <InputGroupInput placeholder="empresa-x-api" />
45
- <InputGroupAddon align="inline-end">
46
- <InputGroupText>.git</InputGroupText>
47
- </InputGroupAddon>
48
- </InputGroup>
49
- ```
50
-
51
- ## Composer com rodapé
52
-
53
- Com InputGroupTextarea a moldura cresce; align=block-end empilha um rodapé de ações embaixo do campo — o padrão do composer de brief do agente.
54
-
55
- ```tsx preview col
56
- <InputGroup>
57
- <InputGroupTextarea placeholder="Descreva a task para o agente developer" rows={3} />
58
- <InputGroupAddon align="block-end">
59
- <InputGroupText>
60
- <Sparkles />
61
- Título gerado pela IA
62
- </InputGroupText>
63
- <InputGroupButton size="sm" context="primary" variant="solid" className="ml-auto" aria-label="Enviar brief">
64
- Enviar
65
- <ArrowUp />
66
- </InputGroupButton>
67
- </InputGroupAddon>
68
- </InputGroup>
69
- ```
70
-
71
- ## Props
72
-
73
- | Prop | Tipo | Default | Descrição |
74
- |---|---|---|---|
75
- | `align` (InputGroupAddon) | `'inline-start' \| 'inline-end' \| 'block-start' \| 'block-end'` | `'inline-start'` | Onde o addon ancora: inline nas laterais; block empilha (vira coluna) acima ou abaixo — block exige a moldura alta de um InputGroupTextarea. |
76
- | `size` (InputGroupButton) | `'xs' \| 'sm' \| 'icon-xs' \| 'icon-sm'` | `'xs'` | Tamanho do botão embutido. As variantes icon-* são quadradas, pra um botão só de ícone. |
77
- | `context` (InputGroupButton) | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | Significado semântico herdado do Button. |
78
- | `variant` (InputGroupButton) | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'ghost'` | Tratamento visual herdado do Button. O padrão ghost some na moldura; use `solid` para dar ênfase à ação. |
79
- | `placeholder / defaultValue / value` (InputGroupInput, InputGroupTextarea) | `string` | | Props nativas do <input>/<textarea> repassadas ao controle. Controle como faria com Input/Textarea (value + onChange). |