@softize/opus 12.10.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 (154) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/bin/lib/check.mjs +1098 -310
  3. package/bin/lib/copy.mjs +12 -5
  4. package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
  5. package/docs/adr/0004-page-content-state-is-composed.md +65 -0
  6. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +180 -0
  7. package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
  8. package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
  9. package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
  10. package/docs/radius-scale.md +1 -1
  11. package/package.json +1 -1
  12. package/registry/instructions/opus.md +5 -0
  13. package/registry/skills/build-opus-ui/SKILL.md +27 -16
  14. package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
  15. package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
  16. package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
  17. package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
  18. package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
  19. package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
  20. package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
  21. package/registry/skills/model-opus-dictionary/SKILL.md +4 -2
  22. package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
  23. package/registry/templates/app/src/App.tsx +1 -1
  24. package/src/core/dictionary.ts +52 -14
  25. package/src/core/index.ts +10 -0
  26. package/src/core/ui-context.ts +29 -0
  27. package/src/schema/drivers/zod.ts +17 -8
  28. package/src/ui/components/patterns/action-form-card.tsx +18 -12
  29. package/src/ui/components/patterns/confirm.tsx +163 -40
  30. package/src/ui/components/patterns/content-header.tsx +335 -61
  31. package/src/ui/components/patterns/data-state.tsx +23 -10
  32. package/src/ui/components/patterns/list.tsx +1097 -783
  33. package/src/ui/components/patterns/page-state.tsx +115 -0
  34. package/src/ui/components/patterns/page.tsx +231 -41
  35. package/src/ui/components/patterns/sidebar.tsx +357 -83
  36. package/src/ui/components/patterns/trigger.tsx +37 -30
  37. package/src/ui/components/patterns/view.tsx +7 -11
  38. package/src/ui/components/primitives/alert.tsx +298 -110
  39. package/src/ui/components/primitives/ask.tsx +2 -1
  40. package/src/ui/components/primitives/badge.tsx +91 -30
  41. package/src/ui/components/primitives/button.tsx +99 -60
  42. package/src/ui/components/primitives/calendar.tsx +39 -39
  43. package/src/ui/components/primitives/card.tsx +96 -23
  44. package/src/ui/components/primitives/detail.tsx +2 -2
  45. package/src/ui/components/primitives/dialog.tsx +196 -39
  46. package/src/ui/components/primitives/dictionary-value.tsx +9 -14
  47. package/src/ui/components/primitives/dot.tsx +74 -21
  48. package/src/ui/components/primitives/drawer.tsx +40 -24
  49. package/src/ui/components/primitives/empty.tsx +3 -3
  50. package/src/ui/components/primitives/item.tsx +135 -79
  51. package/src/ui/components/primitives/menu.tsx +11 -3
  52. package/src/ui/components/primitives/metric-card.tsx +133 -0
  53. package/src/ui/components/primitives/sonner.tsx +187 -8
  54. package/src/ui/components/primitives/table.tsx +2 -2
  55. package/src/ui/docs/DocBrowser.tsx +104 -25
  56. package/src/ui/docs/changelog.tsx +1 -1
  57. package/src/ui/docs/content/accordion.md +22 -16
  58. package/src/ui/docs/content/action-form-card.md +8 -8
  59. package/src/ui/docs/content/action-form-dialog.md +9 -9
  60. package/src/ui/docs/content/action-form.md +28 -34
  61. package/src/ui/docs/content/action-list-dialog.md +11 -6
  62. package/src/ui/docs/content/action-list.md +64 -39
  63. package/src/ui/docs/content/action-trigger.md +21 -14
  64. package/src/ui/docs/content/action-view.md +8 -8
  65. package/src/ui/docs/content/actions.md +9 -9
  66. package/src/ui/docs/content/ai.md +3 -3
  67. package/src/ui/docs/content/alert.md +54 -28
  68. package/src/ui/docs/content/aspect-ratio.md +4 -4
  69. package/src/ui/docs/content/audit.md +2 -2
  70. package/src/ui/docs/content/auth.md +3 -3
  71. package/src/ui/docs/content/avatar.md +34 -14
  72. package/src/ui/docs/content/badge.md +21 -22
  73. package/src/ui/docs/content/breadcrumb.md +13 -8
  74. package/src/ui/docs/content/button.md +93 -15
  75. package/src/ui/docs/content/calendar.md +5 -5
  76. package/src/ui/docs/content/card.md +6 -6
  77. package/src/ui/docs/content/carousel.md +16 -11
  78. package/src/ui/docs/content/chat.md +3 -3
  79. package/src/ui/docs/content/checkbox.md +7 -7
  80. package/src/ui/docs/content/cli.md +5 -5
  81. package/src/ui/docs/content/collapsible.md +8 -8
  82. package/src/ui/docs/content/command.md +16 -8
  83. package/src/ui/docs/content/composer.md +2 -2
  84. package/src/ui/docs/content/content.md +44 -0
  85. package/src/ui/docs/content/copyable.md +4 -3
  86. package/src/ui/docs/content/customization.md +7 -7
  87. package/src/ui/docs/content/cycle.md +3 -3
  88. package/src/ui/docs/content/data-state.md +11 -12
  89. package/src/ui/docs/content/data.md +26 -33
  90. package/src/ui/docs/content/detail.md +8 -5
  91. package/src/ui/docs/content/dialog.md +339 -31
  92. package/src/ui/docs/content/dictionary-value.md +19 -18
  93. package/src/ui/docs/content/dock.md +3 -3
  94. package/src/ui/docs/content/dot.md +7 -7
  95. package/src/ui/docs/content/drawer.md +32 -16
  96. package/src/ui/docs/content/empty-value.md +2 -2
  97. package/src/ui/docs/content/empty.md +19 -12
  98. package/src/ui/docs/content/events.md +4 -4
  99. package/src/ui/docs/content/field.md +34 -12
  100. package/src/ui/docs/content/getting-started.md +1 -1
  101. package/src/ui/docs/content/icon-picker.md +8 -4
  102. package/src/ui/docs/content/input-otp.md +20 -12
  103. package/src/ui/docs/content/input.md +121 -9
  104. package/src/ui/docs/content/item.md +64 -24
  105. package/src/ui/docs/content/kbd.md +19 -11
  106. package/src/ui/docs/content/label.md +5 -3
  107. package/src/ui/docs/content/log.md +4 -4
  108. package/src/ui/docs/content/markdown.md +7 -6
  109. package/src/ui/docs/content/mcp.md +13 -15
  110. package/src/ui/docs/content/menu.md +36 -17
  111. package/src/ui/docs/content/metric-card.md +41 -0
  112. package/src/ui/docs/content/observability.md +2 -2
  113. package/src/ui/docs/content/page.md +93 -10
  114. package/src/ui/docs/content/pagination.md +22 -17
  115. package/src/ui/docs/content/popover.md +16 -8
  116. package/src/ui/docs/content/progress.md +7 -5
  117. package/src/ui/docs/content/queue.md +5 -5
  118. package/src/ui/docs/content/radio-group.md +20 -12
  119. package/src/ui/docs/content/router.md +11 -6
  120. package/src/ui/docs/content/scheduler.md +4 -5
  121. package/src/ui/docs/content/scroll-area.md +12 -7
  122. package/src/ui/docs/content/select.md +42 -29
  123. package/src/ui/docs/content/semantic-context.md +63 -0
  124. package/src/ui/docs/content/separator.md +5 -5
  125. package/src/ui/docs/content/sidebar.md +325 -56
  126. package/src/ui/docs/content/skeleton.md +5 -4
  127. package/src/ui/docs/content/slider.md +8 -7
  128. package/src/ui/docs/content/spinner.md +8 -8
  129. package/src/ui/docs/content/split.md +8 -5
  130. package/src/ui/docs/content/storage.md +6 -8
  131. package/src/ui/docs/content/switch.md +8 -7
  132. package/src/ui/docs/content/table.md +16 -6
  133. package/src/ui/docs/content/tabs.md +28 -14
  134. package/src/ui/docs/content/testing.md +9 -11
  135. package/src/ui/docs/content/textarea.md +5 -4
  136. package/src/ui/docs/content/toast.md +47 -13
  137. package/src/ui/docs/content/toggle.md +75 -7
  138. package/src/ui/docs/content/tokens.md +31 -3
  139. package/src/ui/docs/content/tooltip.md +19 -11
  140. package/src/ui/docs/content/truncate.md +7 -8
  141. package/src/ui/docs/content/ui.md +10 -9
  142. package/src/ui/docs/content/upgrading.md +7 -8
  143. package/src/ui/docs/doc-client.tsx +2 -2
  144. package/src/ui/docs/registry.tsx +580 -229
  145. package/src/ui/lib/semantic-context.ts +30 -0
  146. package/src/ui/meta.ts +278 -286
  147. package/src/ui/react.tsx +377 -111
  148. package/src/ui/theme.css +116 -0
  149. package/src/ui/components/primitives/alert-dialog.tsx +0 -190
  150. package/src/ui/docs/content/alert-dialog.md +0 -73
  151. package/src/ui/docs/content/button-group.md +0 -71
  152. package/src/ui/docs/content/confirm.md +0 -120
  153. package/src/ui/docs/content/input-group.md +0 -78
  154. package/src/ui/docs/content/toggle-group.md +0 -81
package/src/ui/theme.css CHANGED
@@ -47,6 +47,38 @@
47
47
  --accent-foreground: hsl(0 0% 9%);
48
48
  --destructive: hsl(0 84.2% 60.2%);
49
49
  --destructive-foreground: hsl(0 0% 98%);
50
+ /* Contextos semânticos (ADR 0006): família independente do tratamento
51
+ solid/subtle/outline escolhido por cada primitive. */
52
+ --context-neutral: var(--secondary);
53
+ --context-neutral-foreground: var(--secondary-foreground);
54
+ --context-neutral-subtle: var(--muted);
55
+ --context-neutral-emphasis: var(--muted-foreground);
56
+ --context-neutral-border: var(--border);
57
+ --context-primary: var(--primary);
58
+ --context-primary-foreground: var(--primary-foreground);
59
+ --context-primary-subtle: color-mix(in oklab, var(--primary) 8%, transparent);
60
+ --context-primary-emphasis: var(--foreground);
61
+ --context-primary-border: color-mix(in oklab, var(--primary) 25%, transparent);
62
+ --context-info: hsl(217 91% 60%);
63
+ --context-info-foreground: hsl(0 0% 100%);
64
+ --context-info-subtle: hsl(217 91% 60% / 0.1);
65
+ --context-info-emphasis: hsl(221 83% 40%);
66
+ --context-info-border: hsl(217 91% 60% / 0.3);
67
+ --context-success: hsl(142 71% 45%);
68
+ --context-success-foreground: hsl(0 0% 100%);
69
+ --context-success-subtle: hsl(142 71% 45% / 0.1);
70
+ --context-success-emphasis: hsl(142 72% 29%);
71
+ --context-success-border: hsl(142 71% 45% / 0.3);
72
+ --context-warning: hsl(38 92% 50%);
73
+ --context-warning-foreground: hsl(26 83% 14%);
74
+ --context-warning-subtle: hsl(38 92% 50% / 0.12);
75
+ --context-warning-emphasis: hsl(26 90% 32%);
76
+ --context-warning-border: hsl(38 92% 50% / 0.35);
77
+ --context-danger: var(--destructive);
78
+ --context-danger-foreground: var(--destructive-foreground);
79
+ --context-danger-subtle: color-mix(in oklab, var(--destructive) 10%, transparent);
80
+ --context-danger-emphasis: hsl(0 72% 45%);
81
+ --context-danger-border: color-mix(in oklab, var(--destructive) 35%, transparent);
50
82
  --border: hsl(0 0% 92.5%);
51
83
  --input: hsl(0 0% 92.5%);
52
84
  /* Ring de foco LEVE (pedido da casa): aro sutil, não um halo. Com ring-[0.1875rem] + /50,
@@ -81,6 +113,10 @@
81
113
  cima, como o shadcn novo. */
82
114
  --destructive: hsl(0 72% 58%);
83
115
  --destructive-foreground: hsl(0 0% 98%);
116
+ --context-info-emphasis: hsl(213 94% 68%);
117
+ --context-success-emphasis: hsl(142 69% 58%);
118
+ --context-warning-emphasis: hsl(43 96% 56%);
119
+ --context-danger-emphasis: hsl(0 91% 71%);
84
120
  --border: hsl(0 0% 16.5%);
85
121
  --input: hsl(0 0% 16.5%);
86
122
  /* No escuro o 83.1% virava um halo quase branco; ~36% marca o foco sem brilhar. */
@@ -130,6 +166,36 @@
130
166
  --color-accent-foreground: var(--accent-foreground);
131
167
  --color-destructive: var(--destructive);
132
168
  --color-destructive-foreground: var(--destructive-foreground);
169
+ --color-context-neutral: var(--context-neutral);
170
+ --color-context-neutral-foreground: var(--context-neutral-foreground);
171
+ --color-context-neutral-subtle: var(--context-neutral-subtle);
172
+ --color-context-neutral-emphasis: var(--context-neutral-emphasis);
173
+ --color-context-neutral-border: var(--context-neutral-border);
174
+ --color-context-primary: var(--context-primary);
175
+ --color-context-primary-foreground: var(--context-primary-foreground);
176
+ --color-context-primary-subtle: var(--context-primary-subtle);
177
+ --color-context-primary-emphasis: var(--context-primary-emphasis);
178
+ --color-context-primary-border: var(--context-primary-border);
179
+ --color-context-info: var(--context-info);
180
+ --color-context-info-foreground: var(--context-info-foreground);
181
+ --color-context-info-subtle: var(--context-info-subtle);
182
+ --color-context-info-emphasis: var(--context-info-emphasis);
183
+ --color-context-info-border: var(--context-info-border);
184
+ --color-context-success: var(--context-success);
185
+ --color-context-success-foreground: var(--context-success-foreground);
186
+ --color-context-success-subtle: var(--context-success-subtle);
187
+ --color-context-success-emphasis: var(--context-success-emphasis);
188
+ --color-context-success-border: var(--context-success-border);
189
+ --color-context-warning: var(--context-warning);
190
+ --color-context-warning-foreground: var(--context-warning-foreground);
191
+ --color-context-warning-subtle: var(--context-warning-subtle);
192
+ --color-context-warning-emphasis: var(--context-warning-emphasis);
193
+ --color-context-warning-border: var(--context-warning-border);
194
+ --color-context-danger: var(--context-danger);
195
+ --color-context-danger-foreground: var(--context-danger-foreground);
196
+ --color-context-danger-subtle: var(--context-danger-subtle);
197
+ --color-context-danger-emphasis: var(--context-danger-emphasis);
198
+ --color-context-danger-border: var(--context-danger-border);
133
199
  --color-border: var(--border);
134
200
  --color-input: var(--input);
135
201
  --color-ring: var(--ring);
@@ -258,3 +324,53 @@
258
324
  .prose :where(pre):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
259
325
  border: 1px solid var(--border);
260
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,190 +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
- variant = "default",
144
- size = "default",
145
- ...props
146
- }: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
147
- Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
148
- return (
149
- <Button variant={variant} size={size} asChild>
150
- <AlertDialogPrimitive.Action
151
- data-slot="alert-dialog-action"
152
- className={cn(className)}
153
- {...props}
154
- />
155
- </Button>
156
- )
157
- }
158
-
159
- function AlertDialogCancel({
160
- className,
161
- variant = "ghost",
162
- size = "default",
163
- ...props
164
- }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
165
- Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
166
- return (
167
- <Button variant={variant} size={size} asChild>
168
- <AlertDialogPrimitive.Cancel
169
- data-slot="alert-dialog-cancel"
170
- className={cn(className)}
171
- {...props}
172
- />
173
- </Button>
174
- )
175
- }
176
-
177
- export {
178
- AlertDialog,
179
- AlertDialogAction,
180
- AlertDialogCancel,
181
- AlertDialogContent,
182
- AlertDialogDescription,
183
- AlertDialogFooter,
184
- AlertDialogHeader,
185
- AlertDialogMedia,
186
- AlertDialogOverlay,
187
- AlertDialogPortal,
188
- AlertDialogTitle,
189
- AlertDialogTrigger,
190
- }
@@ -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
- `variant="destructive"` no `AlertDialogAction` (ele herda variant/size do Button) pinta o
42
- confirmar de vermelho. Uma confirmação destrutiva **binária** é só
43
- `dialog.confirm({ variant: 'destructive' })` — o exemplo abaixo é só pra mostrar a
44
- composição e onde o `variant` entra:
45
-
46
- ```tsx preview
47
- <AlertDialog>
48
- <AlertDialogTrigger asChild>
49
- <Button variant="destructive">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 variant="destructive">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
- | `variant` (AlertDialogAction) | `'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'` | `'default'` | Intenção do botão de confirmar (herdada do Button) — destructive pra ação perigosa. |
73
- | `variant` (AlertDialogCancel) | `'default' \| 'destructive' \| 'outline' \| 'secondary' \| 'ghost' \| 'link'` | `'ghost'` | Intenção do botão de cancelar (herdada 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,78 +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" variant="default" 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
- | `variant` (InputGroupButton) | `'default' \| 'secondary' \| 'outline' \| 'ghost' \| 'destructive' \| 'link'` | `'ghost'` | Intenção do botão (herda do Button). O padrão ghost some na moldura; use default pra a ação primária. |
78
- | `placeholder / defaultValue / value` (InputGroupInput, InputGroupTextarea) | `string` | | Props nativas do <input>/<textarea> repassadas ao controle. Controle como faria com Input/Textarea (value + onChange). |