@softize/opus 12.10.0 → 12.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/bin/lib/check.mjs +1103 -310
- package/bin/lib/copy.mjs +11 -0
- package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
- package/docs/adr/0004-page-content-state-is-composed.md +65 -0
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +97 -0
- package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
- package/package.json +1 -1
- package/registry/instructions/opus.md +5 -0
- package/registry/skills/build-opus-ui/SKILL.md +27 -16
- package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
- package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
- package/registry/skills/model-opus-dictionary/SKILL.md +4 -2
- package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
- package/registry/templates/app/src/App.tsx +1 -1
- package/src/core/dictionary.ts +52 -14
- package/src/core/index.ts +10 -0
- package/src/core/ui-context.ts +29 -0
- package/src/schema/drivers/zod.ts +17 -8
- package/src/ui/components/patterns/action-form-card.tsx +18 -12
- package/src/ui/components/patterns/confirm.tsx +26 -3
- package/src/ui/components/patterns/content-header.tsx +335 -61
- package/src/ui/components/patterns/data-state.tsx +23 -10
- package/src/ui/components/patterns/list.tsx +1096 -777
- package/src/ui/components/patterns/page-state.tsx +115 -0
- package/src/ui/components/patterns/page.tsx +231 -41
- package/src/ui/components/patterns/sidebar.tsx +354 -80
- package/src/ui/components/patterns/trigger.tsx +13 -9
- package/src/ui/components/patterns/view.tsx +7 -11
- package/src/ui/components/primitives/alert-dialog.tsx +7 -5
- package/src/ui/components/primitives/alert.tsx +298 -110
- package/src/ui/components/primitives/ask.tsx +2 -1
- package/src/ui/components/primitives/badge.tsx +91 -30
- package/src/ui/components/primitives/button.tsx +99 -60
- package/src/ui/components/primitives/calendar.tsx +39 -39
- package/src/ui/components/primitives/card.tsx +96 -23
- package/src/ui/components/primitives/detail.tsx +2 -2
- package/src/ui/components/primitives/dictionary-value.tsx +9 -14
- package/src/ui/components/primitives/dot.tsx +74 -21
- package/src/ui/components/primitives/drawer.tsx +33 -20
- package/src/ui/components/primitives/item.tsx +137 -81
- package/src/ui/components/primitives/menu.tsx +11 -3
- package/src/ui/components/primitives/metric-card.tsx +133 -0
- package/src/ui/components/primitives/table.tsx +2 -2
- package/src/ui/docs/DocBrowser.tsx +3 -3
- package/src/ui/docs/changelog.tsx +1 -1
- package/src/ui/docs/content/action-form-card.md +1 -1
- package/src/ui/docs/content/alert-dialog.md +8 -8
- package/src/ui/docs/content/alert.md +49 -25
- package/src/ui/docs/content/badge.md +18 -19
- package/src/ui/docs/content/button.md +12 -9
- package/src/ui/docs/content/card.md +5 -5
- package/src/ui/docs/content/content.md +44 -0
- package/src/ui/docs/content/customization.md +2 -2
- package/src/ui/docs/content/detail.md +5 -2
- package/src/ui/docs/content/dialog.md +2 -2
- package/src/ui/docs/content/dictionary-value.md +11 -10
- package/src/ui/docs/content/dot.md +7 -7
- package/src/ui/docs/content/drawer.md +6 -3
- package/src/ui/docs/content/input-group.md +3 -2
- package/src/ui/docs/content/item.md +47 -21
- package/src/ui/docs/content/menu.md +5 -4
- package/src/ui/docs/content/metric-card.md +41 -0
- package/src/ui/docs/content/page-state.md +45 -0
- package/src/ui/docs/content/page.md +48 -10
- package/src/ui/docs/content/semantic-context.md +63 -0
- package/src/ui/docs/content/sidebar.md +4 -4
- package/src/ui/docs/content/skeleton.md +2 -2
- package/src/ui/docs/content/table.md +3 -3
- package/src/ui/docs/content/tokens.md +28 -0
- package/src/ui/docs/doc-client.tsx +2 -2
- package/src/ui/docs/registry.tsx +596 -228
- package/src/ui/lib/semantic-context.ts +30 -0
- package/src/ui/meta.ts +292 -270
- package/src/ui/react.tsx +378 -111
- package/src/ui/theme.css +66 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import type { UiContext } from '../../../core/ui-context.ts'
|
|
3
|
+
import { cn } from '../../lib/cn.ts'
|
|
4
|
+
import { Card } from './card.tsx'
|
|
5
|
+
import { Skeleton } from './skeleton.tsx'
|
|
6
|
+
|
|
7
|
+
/** @deprecated Use `MetricCardContext`. */
|
|
8
|
+
export type MetricCardTone = 'default' | 'warning'
|
|
9
|
+
export type MetricCardContext = Exclude<UiContext, 'primary'>
|
|
10
|
+
|
|
11
|
+
const iconContextClasses: Record<MetricCardContext, string> = {
|
|
12
|
+
neutral: 'bg-context-neutral-subtle text-context-neutral-emphasis',
|
|
13
|
+
info: 'bg-context-info-subtle text-context-info-emphasis',
|
|
14
|
+
success: 'bg-context-success-subtle text-context-success-emphasis',
|
|
15
|
+
warning: 'bg-context-warning-subtle text-context-warning-emphasis',
|
|
16
|
+
danger: 'bg-context-danger-subtle text-context-danger-emphasis',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface MetricCardBaseProps extends Omit<React.ComponentProps<typeof Card>, 'children'> {
|
|
20
|
+
/** Controle complementar exibido ao lado do rótulo, como uma explicação em tooltip. */
|
|
21
|
+
labelAction?: React.ReactNode
|
|
22
|
+
/** Contexto que explica recorte, proporção ou significado do valor. */
|
|
23
|
+
description?: React.ReactNode
|
|
24
|
+
/** Ícone decorativo que identifica a natureza da medida. */
|
|
25
|
+
icon?: React.ReactNode
|
|
26
|
+
/** Contexto semântico do ícone; não altera a superfície do card. */
|
|
27
|
+
context?: MetricCardContext
|
|
28
|
+
/** @deprecated Use `context`. Compatibilidade temporária da ADR 0006. */
|
|
29
|
+
tone?: MetricCardTone
|
|
30
|
+
/** Ação relacionada diretamente à medida. */
|
|
31
|
+
action?: React.ReactNode
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type MetricCardProps = MetricCardBaseProps &
|
|
35
|
+
(
|
|
36
|
+
| {
|
|
37
|
+
/** Nome curto da medida apresentada. */
|
|
38
|
+
label: React.ReactNode
|
|
39
|
+
/** Valor já formatado pelo consumidor. */
|
|
40
|
+
value: React.ReactNode
|
|
41
|
+
loading?: false
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
/** O rótulo pode ser omitido porque o placeholder ocupa toda a composição. */
|
|
45
|
+
label?: React.ReactNode
|
|
46
|
+
/** O valor ainda não está disponível durante o carregamento. */
|
|
47
|
+
value?: React.ReactNode
|
|
48
|
+
/** Substitui o conteúdo por placeholders preservando a geometria do card. */
|
|
49
|
+
loading: true
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* MetricCard apresenta uma medida resumida. Formatação, cálculo e carregamento dos dados
|
|
55
|
+
* continuam sob responsabilidade do consumidor; o componente padroniza apenas a composição.
|
|
56
|
+
*/
|
|
57
|
+
export function MetricCard({
|
|
58
|
+
label,
|
|
59
|
+
labelAction,
|
|
60
|
+
value,
|
|
61
|
+
description,
|
|
62
|
+
icon,
|
|
63
|
+
context,
|
|
64
|
+
tone,
|
|
65
|
+
action,
|
|
66
|
+
loading = false,
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: MetricCardProps): React.ReactElement {
|
|
70
|
+
if (context !== undefined && tone !== undefined) {
|
|
71
|
+
throw new Error('MetricCard não permite combinar context com tone legado.')
|
|
72
|
+
}
|
|
73
|
+
const hasIcon = icon !== undefined && icon !== null
|
|
74
|
+
const resolvedContext = context ?? (tone === 'warning' ? 'warning' : 'neutral')
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Card
|
|
78
|
+
data-slot="metric-card"
|
|
79
|
+
aria-busy={loading || undefined}
|
|
80
|
+
className={cn('min-w-0 p-4', className)}
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
{loading ? (
|
|
84
|
+
<div className="space-y-3">
|
|
85
|
+
<Skeleton className="h-4 w-28" />
|
|
86
|
+
<Skeleton className="h-8 w-20" />
|
|
87
|
+
<Skeleton className="h-3 w-full" />
|
|
88
|
+
</div>
|
|
89
|
+
) : (
|
|
90
|
+
<>
|
|
91
|
+
<div className="flex items-start justify-between gap-3">
|
|
92
|
+
<div className="flex min-w-0 items-center gap-1.5">
|
|
93
|
+
<div data-slot="metric-card-label" className="min-w-0 truncate text-sm font-medium text-muted-foreground">
|
|
94
|
+
{label}
|
|
95
|
+
</div>
|
|
96
|
+
{labelAction !== undefined && (
|
|
97
|
+
<div data-slot="metric-card-label-action" className="shrink-0">
|
|
98
|
+
{labelAction}
|
|
99
|
+
</div>
|
|
100
|
+
)}
|
|
101
|
+
</div>
|
|
102
|
+
{hasIcon && (
|
|
103
|
+
<span
|
|
104
|
+
data-slot="metric-card-icon"
|
|
105
|
+
data-context={resolvedContext}
|
|
106
|
+
aria-hidden="true"
|
|
107
|
+
className={cn(
|
|
108
|
+
'flex size-8 shrink-0 items-center justify-center rounded-md [&_svg]:size-4',
|
|
109
|
+
iconContextClasses[resolvedContext],
|
|
110
|
+
)}
|
|
111
|
+
>
|
|
112
|
+
{icon}
|
|
113
|
+
</span>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
<div data-slot="metric-card-value" className="mt-3 text-2xl font-semibold tracking-tight tabular-nums">
|
|
117
|
+
{value}
|
|
118
|
+
</div>
|
|
119
|
+
{description !== undefined && (
|
|
120
|
+
<div data-slot="metric-card-description" className="mt-1 text-xs text-muted-foreground">
|
|
121
|
+
{description}
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
{action !== undefined && (
|
|
125
|
+
<div data-slot="metric-card-action" className="mt-3">
|
|
126
|
+
{action}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</>
|
|
130
|
+
)}
|
|
131
|
+
</Card>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
@@ -78,7 +78,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
|
78
78
|
<th
|
|
79
79
|
data-slot="table-head"
|
|
80
80
|
className={cn(
|
|
81
|
-
"h-10 px-
|
|
81
|
+
"h-10 px-3 text-left align-middle font-medium whitespace-nowrap text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[0.125rem]",
|
|
82
82
|
className
|
|
83
83
|
)}
|
|
84
84
|
{...props}
|
|
@@ -91,7 +91,7 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
|
91
91
|
<td
|
|
92
92
|
data-slot="table-cell"
|
|
93
93
|
className={cn(
|
|
94
|
-
"
|
|
94
|
+
"px-3 py-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[0.125rem]",
|
|
95
95
|
className
|
|
96
96
|
)}
|
|
97
97
|
{...props}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { useEffect } from 'react'
|
|
16
16
|
import { DOC_SECTIONS, type DocSection, type DocEntry } from './registry'
|
|
17
17
|
import { Pane, Split } from '../components/patterns/split.tsx'
|
|
18
|
-
import {
|
|
18
|
+
import { PaneBody, Sidebar, SidebarNav, type SidebarNavGroup } from '../components/patterns/sidebar.tsx'
|
|
19
19
|
import { navigate, usePathname } from '../router.ts'
|
|
20
20
|
|
|
21
21
|
function findPage(sections: DocSection[], slug: string): DocEntry | undefined {
|
|
@@ -95,14 +95,14 @@ export function DocBrowser({
|
|
|
95
95
|
<Split className="h-full">
|
|
96
96
|
<Pane inset="none" className="w-56">
|
|
97
97
|
<Sidebar className="w-full">
|
|
98
|
-
<
|
|
98
|
+
<PaneBody>
|
|
99
99
|
<SidebarNav
|
|
100
100
|
groups={navGroups}
|
|
101
101
|
activeId={page?.slug}
|
|
102
102
|
navLabel="Navegação da documentação"
|
|
103
103
|
onSelect={(slug) => go(`${basePath}/${slug}`)}
|
|
104
104
|
/>
|
|
105
|
-
</
|
|
105
|
+
</PaneBody>
|
|
106
106
|
</Sidebar>
|
|
107
107
|
</Pane>
|
|
108
108
|
<Pane key={page?.slug} grow inset="none" className="overflow-y-auto">
|
|
@@ -54,7 +54,7 @@ export function ChangelogView({ source }: { source: string }): React.ReactElemen
|
|
|
54
54
|
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
|
|
55
55
|
<h2 className="text-xl font-semibold tracking-tight">{v.version}</h2>
|
|
56
56
|
{v.date !== undefined && <span className="text-sm text-muted-foreground">{v.date}</span>}
|
|
57
|
-
{v.breaking !== undefined && <Badge variant="
|
|
57
|
+
{v.breaking !== undefined && <Badge context="danger" variant="solid">Breaking</Badge>}
|
|
58
58
|
</div>
|
|
59
59
|
{v.body.length > 0 && (
|
|
60
60
|
<div className="mt-3">
|
|
@@ -20,5 +20,5 @@ faixa de modal). Pra estruturar uma seção da página como painel. O título é
|
|
|
20
20
|
|---|---|---|---|
|
|
21
21
|
| `title` | `string` | | Título do header (com divisor embaixo). Sem ele, o card começa direto no corpo. |
|
|
22
22
|
| `description` | `string` | | Subtítulo opcional, abaixo do título. |
|
|
23
|
-
| `action / defaultValues / fieldOptions / submitLabel / onSuccess / onCancel` | `— (iguais ao ActionForm)` | | O resto é o ActionForm — o card injeta o
|
|
23
|
+
| `action / defaultValues / fieldOptions / submitLabel / onSuccess / onCancel` | `— (iguais ao ActionForm)` | | O resto é o ActionForm — o card injeta o corpo (CardBody) e o rodapé (CardFooter) por baixo. |
|
|
24
24
|
| `cardClassName` | `string` | | Classes da SUPERFÍCIE do card (ex.: largura). `className` vai pro `<form>`. |
|
|
@@ -38,15 +38,15 @@ Dialog, **não fecha clicando fora** — exige uma escolha.
|
|
|
38
38
|
## A estrutura e o botão destrutivo
|
|
39
39
|
|
|
40
40
|
`AlertDialogHeader` (media/título/descrição) + `AlertDialogFooter` (as ações).
|
|
41
|
-
`
|
|
42
|
-
|
|
43
|
-
`dialog.confirm({
|
|
44
|
-
|
|
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
45
|
|
|
46
46
|
```tsx preview
|
|
47
47
|
<AlertDialog>
|
|
48
48
|
<AlertDialogTrigger asChild>
|
|
49
|
-
<Button
|
|
49
|
+
<Button context="danger">Excluir repositório</Button>
|
|
50
50
|
</AlertDialogTrigger>
|
|
51
51
|
<AlertDialogContent>
|
|
52
52
|
<AlertDialogHeader>
|
|
@@ -57,7 +57,7 @@ composição e onde o `variant` entra:
|
|
|
57
57
|
</AlertDialogHeader>
|
|
58
58
|
<AlertDialogFooter>
|
|
59
59
|
<AlertDialogCancel>Cancelar</AlertDialogCancel>
|
|
60
|
-
<AlertDialogAction
|
|
60
|
+
<AlertDialogAction context="danger">Excluir</AlertDialogAction>
|
|
61
61
|
</AlertDialogFooter>
|
|
62
62
|
</AlertDialogContent>
|
|
63
63
|
</AlertDialog>
|
|
@@ -69,5 +69,5 @@ composição e onde o `variant` entra:
|
|
|
69
69
|
|---|---|---|---|
|
|
70
70
|
| `open` (AlertDialog) | `boolean` | | Estado de aberto no modo controlado — pareie com onOpenChange. No padrão, o Trigger cuida disso. |
|
|
71
71
|
| `onOpenChange` (AlertDialog) | `(open: boolean) => void` | | Chamado quando o diálogo abre ou fecha (Trigger, Cancel, Action ou Esc). |
|
|
72
|
-
| `
|
|
73
|
-
| `variant` (
|
|
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. |
|
|
@@ -5,19 +5,19 @@ Quase todo alert é ícone + título + uma frase — então isso é UMA linha: `
|
|
|
5
5
|
```tsx preview col
|
|
6
6
|
<Alert icon={<Info />} title="Opus 2.8.0" description="Este workspace usa a versão pinada em opus.json." />
|
|
7
7
|
<Alert
|
|
8
|
-
|
|
8
|
+
context="danger"
|
|
9
9
|
icon={<CircleAlert />}
|
|
10
10
|
title="Sessão encerrada"
|
|
11
11
|
description="O agente parou antes de concluir. Veja o detalhe no log da sessão."
|
|
12
12
|
/>
|
|
13
13
|
<Alert
|
|
14
|
-
|
|
14
|
+
context="success"
|
|
15
15
|
icon={<CircleCheck />}
|
|
16
16
|
title="Skill publicada"
|
|
17
17
|
description="Os agentes do workspace já enxergam a nova versão."
|
|
18
18
|
/>
|
|
19
19
|
<Alert
|
|
20
|
-
|
|
20
|
+
context="warning"
|
|
21
21
|
icon={<CircleAlert />}
|
|
22
22
|
title="Revisão necessária"
|
|
23
23
|
description="Confira os dados antes de continuar."
|
|
@@ -26,51 +26,75 @@ Quase todo alert é ícone + título + uma frase — então isso é UMA linha: `
|
|
|
26
26
|
|
|
27
27
|
## Só a frase
|
|
28
28
|
|
|
29
|
-
Título é opcional — o aviso de uma linha dispensa.
|
|
29
|
+
Título é opcional — o aviso de uma linha dispensa. O contexto continua visível por superfície,
|
|
30
|
+
borda e texto; o conteúdo comunica o significado sem depender somente da cor.
|
|
30
31
|
|
|
31
32
|
```tsx preview col
|
|
32
33
|
<Alert description="Nenhuma sessão aberta neste repositório." />
|
|
33
|
-
<Alert
|
|
34
|
+
<Alert context="success" icon={<CircleCheck />} description="Workspace Empresa X sincronizado." />
|
|
34
35
|
```
|
|
35
36
|
|
|
36
|
-
##
|
|
37
|
+
## Mídia é opcional
|
|
37
38
|
|
|
38
|
-
Sem `icon` o alert
|
|
39
|
+
Sem `icon` o alert mantém somente a coluna de texto. Com ele, a forma curta materializa
|
|
40
|
+
`AlertMedia` à esquerda e `AlertHeader` à direita. A mídia tem largura estável e acompanha a
|
|
41
|
+
altura útil do header; com título e descrição de uma linha, a moldura termina junto do texto,
|
|
42
|
+
sem sobra inferior. Texto solto como filho também vale (`<Alert>Sincronizado.</Alert>`) e se torna
|
|
43
|
+
uma descrição.
|
|
39
44
|
|
|
40
45
|
```tsx preview col
|
|
41
46
|
<Alert title="Sem provider próprio" description="As conversas usam o padrão do sistema." />
|
|
42
|
-
<Alert
|
|
47
|
+
<Alert context="success">Workspace Empresa X sincronizado.</Alert>
|
|
43
48
|
```
|
|
44
49
|
|
|
45
50
|
## Composição (conteúdo rico)
|
|
46
51
|
|
|
47
|
-
Quando a
|
|
52
|
+
Quando a mensagem precisa de conteúdo rico, componha os slots da família. `AlertMedia`,
|
|
53
|
+
`AlertHeader` e `AlertActions` são filhos diretos de `Alert`; `AlertTitle` e
|
|
54
|
+
`AlertDescription` pertencem ao header.
|
|
48
55
|
|
|
49
56
|
```tsx preview col
|
|
50
|
-
<Alert
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
<Alert context="danger">
|
|
58
|
+
<AlertMedia>
|
|
59
|
+
<CircleAlert />
|
|
60
|
+
</AlertMedia>
|
|
61
|
+
<AlertHeader>
|
|
62
|
+
<AlertTitle>Não deu pra publicar</AlertTitle>
|
|
63
|
+
<AlertDescription>
|
|
64
|
+
<p>O registry recusou a versão 3.0.0 — ela já existe.</p>
|
|
65
|
+
<p>Suba o patch e tente de novo.</p>
|
|
66
|
+
</AlertDescription>
|
|
67
|
+
</AlertHeader>
|
|
68
|
+
<AlertActions>
|
|
69
|
+
<Button size="sm" variant="outline">
|
|
70
|
+
Tentar novamente
|
|
71
|
+
</Button>
|
|
72
|
+
</AlertActions>
|
|
56
73
|
</Alert>
|
|
57
74
|
```
|
|
58
75
|
|
|
59
76
|
Os dois modos convivem: com `title`/`description` preenchidos, `children` entra DEPOIS da frase — é onde vai a ação.
|
|
60
77
|
|
|
61
78
|
```tsx preview col
|
|
62
|
-
<Alert
|
|
63
|
-
<
|
|
79
|
+
<Alert
|
|
80
|
+
icon={<CircleAlert />}
|
|
81
|
+
title="Sessão presa"
|
|
82
|
+
description="O ambiente não subiu no tempo esperado."
|
|
83
|
+
>
|
|
84
|
+
<Button size="sm" variant="outline">
|
|
85
|
+
Reiniciar
|
|
86
|
+
</Button>
|
|
64
87
|
</Alert>
|
|
65
88
|
```
|
|
66
89
|
|
|
67
90
|
## Props
|
|
68
91
|
|
|
69
|
-
| Prop
|
|
70
|
-
|
|
71
|
-
| `title`
|
|
72
|
-
| `description` | `React.ReactNode`
|
|
73
|
-
| `icon`
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
92
|
+
| Prop | Tipo | Default | Descrição |
|
|
93
|
+
| ------------- | ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `title` | `React.ReactNode` | | Título do alert (a forma curta). Não é o atributo `title` do HTML — esse é tooltip nativo, banido na casa, e o componente não o aceita. |
|
|
95
|
+
| `description` | `React.ReactNode` | | A frase. Sozinha, dispensa título. |
|
|
96
|
+
| `icon` | `React.ReactNode` | | Ícone à esquerda; materializa `AlertMedia` com realce tonal. Decorativo — quem nomeia é o título. |
|
|
97
|
+
| `context` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O significado da mensagem e do realce do ícone. |
|
|
98
|
+
| `variant` | `'subtle' \| 'outline'` | `'subtle'` | O tratamento visual aplicado ao contexto. |
|
|
99
|
+
| `children` | `React.ReactNode` | | Composição explícita, texto cru ou, junto da forma curta, ações exibidas após a mensagem. |
|
|
100
|
+
| `style` | `React.CSSProperties` | | Ajuste excepcional da raiz; medidas escaláveis usam `rem`. |
|
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Contexto e variante
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Badge é não-interativo — para clique, use Button. `context` comunica o significado; `variant`
|
|
4
|
+
escolhe entre preenchimento sólido, superfície sutil ou contorno.
|
|
5
5
|
|
|
6
6
|
```tsx preview
|
|
7
|
-
<Badge>Agente</Badge>
|
|
8
|
-
<Badge variant="
|
|
9
|
-
<Badge variant="
|
|
10
|
-
<Badge variant="destructive">Arquivado</Badge>
|
|
7
|
+
<Badge context="neutral">Agente</Badge>
|
|
8
|
+
<Badge context="neutral" variant="outline">Monorepo</Badge>
|
|
9
|
+
<Badge context="danger" variant="solid">Arquivado</Badge>
|
|
11
10
|
```
|
|
12
11
|
|
|
13
|
-
##
|
|
12
|
+
## Contextos de status
|
|
14
13
|
|
|
15
|
-
success
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
`DictionaryValue`, que escolhe o tom pela metadata em vez de repetir esta escolha em cada tela.
|
|
14
|
+
`success`, `warning`, `info` e `danger` usam `subtle` por padrão. Valor de dicionário com papel
|
|
15
|
+
declarado usa `DictionaryValue`, que escolhe o contexto pela metadata em vez de repetir esta
|
|
16
|
+
decisão em cada tela.
|
|
19
17
|
|
|
20
18
|
```tsx preview
|
|
21
|
-
<Badge
|
|
22
|
-
<Badge
|
|
23
|
-
<Badge
|
|
24
|
-
<Badge
|
|
19
|
+
<Badge context="success">Ativa</Badge>
|
|
20
|
+
<Badge context="warning">Aguardando revisor</Badge>
|
|
21
|
+
<Badge context="info">Em sessão</Badge>
|
|
22
|
+
<Badge context="danger">Bloqueada</Badge>
|
|
25
23
|
```
|
|
26
24
|
|
|
27
25
|
## Com ícone
|
|
@@ -29,7 +27,7 @@ semântico, igual ao KindBadge do Maestro. Não-interativos. `danger` é o tom t
|
|
|
29
27
|
Um svg filho ganha size-3 automaticamente — bom pra reforçar o estado sem crescer o rótulo.
|
|
30
28
|
|
|
31
29
|
```tsx preview
|
|
32
|
-
<Badge
|
|
30
|
+
<Badge context="success"><CircleCheck /> Regressão verde</Badge>
|
|
33
31
|
```
|
|
34
32
|
|
|
35
33
|
## Como link (asChild)
|
|
@@ -38,7 +36,7 @@ asChild renderiza o filho (Radix Slot) — um `<a>` com cara de badge, com hover
|
|
|
38
36
|
variantes.
|
|
39
37
|
|
|
40
38
|
```tsx preview
|
|
41
|
-
<Badge asChild variant="
|
|
39
|
+
<Badge asChild context="neutral" variant="solid">
|
|
42
40
|
<a href="#" onClick={(e) => e.preventDefault()}>Empresa X</a>
|
|
43
41
|
</Badge>
|
|
44
42
|
```
|
|
@@ -47,5 +45,6 @@ variantes.
|
|
|
47
45
|
|
|
48
46
|
| Prop | Tipo | Default | Descrição |
|
|
49
47
|
|---|---|---|---|
|
|
50
|
-
| `
|
|
48
|
+
| `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O significado ou destaque contextual. |
|
|
49
|
+
| `variant` | `'solid' \| 'subtle' \| 'outline'` | `'subtle'` | O tratamento visual aplicado ao contexto. |
|
|
51
50
|
| `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<span>` — ex.: um `<a>` com cara de badge. |
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Contexto e variante
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
`context` declara a hierarquia ou o risco da ação; `variant` escolhe o tratamento visual. Use
|
|
4
|
+
`primary` para a ação principal, `neutral` para ações de apoio e `danger` quando a ação tiver uma
|
|
5
|
+
consequência perigosa. `default`, `secondary` e `destructive` permanecem apenas como aliases de
|
|
6
|
+
compatibilidade.
|
|
5
7
|
|
|
6
8
|
```tsx preview
|
|
7
9
|
<Button>Criar workspace</Button>
|
|
8
|
-
<Button variant="
|
|
9
|
-
<Button variant="outline">Ver prévia</Button>
|
|
10
|
-
<Button variant="ghost">Cancelar</Button>
|
|
11
|
-
<Button variant="
|
|
12
|
-
<Button variant="link">Ver documentação</Button>
|
|
10
|
+
<Button context="neutral" variant="solid">Duplicar</Button>
|
|
11
|
+
<Button context="neutral" variant="outline">Ver prévia</Button>
|
|
12
|
+
<Button context="neutral" variant="ghost">Cancelar</Button>
|
|
13
|
+
<Button context="danger" variant="solid">Excluir</Button>
|
|
14
|
+
<Button context="primary" variant="link">Ver documentação</Button>
|
|
13
15
|
```
|
|
14
16
|
|
|
15
17
|
## Tamanhos
|
|
@@ -53,7 +55,8 @@ buttonVariants serve pro caso sem filho único.
|
|
|
53
55
|
|
|
54
56
|
| Prop | Tipo | Default | Descrição |
|
|
55
57
|
|---|---|---|---|
|
|
56
|
-
| `
|
|
58
|
+
| `context` | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | A hierarquia ou o risco comunicado pela ação. |
|
|
59
|
+
| `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'solid'` | O tratamento visual aplicado ao contexto. |
|
|
57
60
|
| `size` | `'default' \| 'sm' \| 'lg' \| 'icon' \| 'icon-sm' \| 'icon-xs'` | `'default'` | O tamanho. Os icon* são quadrados (2.25/2/1.5rem) para botões só de ícone, com `aria-label`. |
|
|
58
61
|
| `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — pra âncoras e afins. |
|
|
59
62
|
| `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton num nível de página). |
|
|
@@ -18,9 +18,9 @@ Pra painel com estrutura: cada slot é dono do próprio padding (como o Dialog).
|
|
|
18
18
|
<CardTitle>Empresa X</CardTitle>
|
|
19
19
|
<CardDescription>2 repositórios · 3 agentes vinculados.</CardDescription>
|
|
20
20
|
</CardHeader>
|
|
21
|
-
<
|
|
21
|
+
<CardBody>
|
|
22
22
|
<p className="text-sm text-muted-foreground">Última sessão concluída há 2 horas.</p>
|
|
23
|
-
</
|
|
23
|
+
</CardBody>
|
|
24
24
|
<CardFooter className="gap-2">
|
|
25
25
|
<Button size="sm">Abrir sessão</Button>
|
|
26
26
|
<Button size="sm" variant="outline">Ver repositórios</Button>
|
|
@@ -38,11 +38,11 @@ Pra painel com estrutura: cada slot é dono do próprio padding (como o Dialog).
|
|
|
38
38
|
<CardTitle>Skill code-style</CardTitle>
|
|
39
39
|
<CardDescription>Padrão softize de código e microcopy.</CardDescription>
|
|
40
40
|
</CardHeader>
|
|
41
|
-
<
|
|
41
|
+
<CardBody>
|
|
42
42
|
<p className="text-sm text-muted-foreground">Vinculada a 4 agentes neste workspace.</p>
|
|
43
|
-
</
|
|
43
|
+
</CardBody>
|
|
44
44
|
<CardFooter className="justify-between border-t">
|
|
45
|
-
<Badge
|
|
45
|
+
<Badge context="success">Publicada</Badge>
|
|
46
46
|
<Button size="sm" variant="outline">Editar skill</Button>
|
|
47
47
|
</CardFooter>
|
|
48
48
|
</Card>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
## Região de conteúdo
|
|
2
|
+
|
|
3
|
+
Use `Content` para dar título e estrutura a uma região dentro de `PageBody`, `CardBody`,
|
|
4
|
+
`DialogBody` ou outra superfície. Ele renderiza uma `section` ligada ao próprio título e mantém o
|
|
5
|
+
espaçamento entre header e body. `ContentHeader` pertence sempre a essa estrutura; não o use solto.
|
|
6
|
+
|
|
7
|
+
No caso comum, prefira o shorthand:
|
|
8
|
+
|
|
9
|
+
```tsx preview col
|
|
10
|
+
render(
|
|
11
|
+
<Content
|
|
12
|
+
title="Dispositivos conectados"
|
|
13
|
+
description="Sessões com acesso à sua conta."
|
|
14
|
+
actions={<Button variant="outline">Encerrar outras sessões</Button>}
|
|
15
|
+
>
|
|
16
|
+
<div className="rounded-lg border border-border p-4">MacBook Pro · ativo agora</div>
|
|
17
|
+
</Content>,
|
|
18
|
+
)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Composição explícita
|
|
22
|
+
|
|
23
|
+
Use os slots quando o header precisar de composição própria. A árvore aceita exatamente um
|
|
24
|
+
`ContentHeader` e um `ContentBody` como filhos diretos. O header exige um `ContentTitle` e aceita
|
|
25
|
+
uma descrição, um metadado e uma região de ações.
|
|
26
|
+
|
|
27
|
+
```tsx preview col
|
|
28
|
+
render(
|
|
29
|
+
<Content level={2}>
|
|
30
|
+
<ContentHeader>
|
|
31
|
+
<ContentTitle>Dispositivos conectados</ContentTitle>
|
|
32
|
+
<ContentMeta>3</ContentMeta>
|
|
33
|
+
<ContentDescription>Sessões com acesso à sua conta.</ContentDescription>
|
|
34
|
+
<ContentActions><Button variant="outline">Atualizar</Button></ContentActions>
|
|
35
|
+
</ContentHeader>
|
|
36
|
+
<ContentBody>
|
|
37
|
+
<div className="rounded-lg border border-border p-4">MacBook Pro · ativo agora</div>
|
|
38
|
+
</ContentBody>
|
|
39
|
+
</Content>,
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
As duas formas geram os mesmos elementos, estilos e `data-slot`. `opus check` reprova slots fora
|
|
44
|
+
do pai correto, filhos estruturais indiretos e a mistura de shorthand com composição explícita.
|
|
@@ -59,7 +59,7 @@ de 16, sem transformar esse valor em uma regra da biblioteca.
|
|
|
59
59
|
// Slots: só o que a tela pede.
|
|
60
60
|
<Card>
|
|
61
61
|
<CardHeader><CardTitle>Workspace</CardTitle></CardHeader>
|
|
62
|
-
<
|
|
62
|
+
<CardBody>…</CardBody>
|
|
63
63
|
</Card>
|
|
64
64
|
|
|
65
65
|
// asChild: o filho VIRA o botão (sem forkar estilo).
|
|
@@ -110,7 +110,7 @@ const [open, setOpen] = useState(false)
|
|
|
110
110
|
> do botão, a seta do tooltip — é identidade da casa, igual em todo projeto.
|
|
111
111
|
|
|
112
112
|
```tsx preview
|
|
113
|
-
<Badge
|
|
113
|
+
<Badge context="success">Cabe nas alavancas</Badge>
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
Se uma necessidade real não cabe nas alavancas (tokens · className · slots/asChild · props ·
|
|
@@ -9,7 +9,7 @@ para entrada e validação; use DetailField quando a pessoa apenas consulta um v
|
|
|
9
9
|
label="Estágio"
|
|
10
10
|
value={
|
|
11
11
|
<DictionaryValue
|
|
12
|
-
dict={{ keys: ['prospect', 'customer'], entries: { prospect: { label: 'Prospect' }, customer: { label: 'Cliente',
|
|
12
|
+
dict={{ keys: ['prospect', 'customer'], entries: { prospect: { label: 'Prospect' }, customer: { label: 'Cliente', context: 'success' } }, presentation: 'stage' }}
|
|
13
13
|
value="prospect"
|
|
14
14
|
/>
|
|
15
15
|
}
|
|
@@ -39,7 +39,10 @@ significado no domínio. `0` e `false` seguem como valores. Ver `EmptyValue`.
|
|
|
39
39
|
|
|
40
40
|
`variant="framed"` adiciona a superfície e a borda externa. `dividers` desenha apenas as
|
|
41
41
|
divisórias internas; as duas opções são independentes e podem ser combinadas. `orientation`
|
|
42
|
-
define se a chave fica sobre o valor ou ao lado dele.
|
|
42
|
+
define se a chave fica sobre o valor ou ao lado dele. Na orientação horizontal, todos os valores
|
|
43
|
+
começam depois da mesma coluna de rótulo, com largura padrão de `7rem`. Quando a superfície exigir
|
|
44
|
+
mais espaço para os rótulos, ajuste a variável no grupo, por exemplo com
|
|
45
|
+
`className="[--detail-label-width:9rem]"`.
|
|
43
46
|
|
|
44
47
|
```tsx preview col
|
|
45
48
|
<DetailGroup columns={2} orientation="horizontal" variant="framed" dividers>
|
|
@@ -33,7 +33,7 @@ showCloseButton={false} esconde o X: o usuário decide pelos botões — pra con
|
|
|
33
33
|
```tsx preview
|
|
34
34
|
<Dialog>
|
|
35
35
|
<DialogTrigger asChild>
|
|
36
|
-
<Button
|
|
36
|
+
<Button context="danger">Excluir sessão</Button>
|
|
37
37
|
</DialogTrigger>
|
|
38
38
|
<DialogContent showCloseButton={false}>
|
|
39
39
|
<DialogHeader>
|
|
@@ -45,7 +45,7 @@ showCloseButton={false} esconde o X: o usuário decide pelos botões — pra con
|
|
|
45
45
|
<Button variant="ghost" size="sm">Cancelar</Button>
|
|
46
46
|
</DialogClose>
|
|
47
47
|
<DialogClose asChild>
|
|
48
|
-
<Button
|
|
48
|
+
<Button context="danger" size="sm">Excluir</Button>
|
|
49
49
|
</DialogClose>
|
|
50
50
|
</DialogFooter>
|
|
51
51
|
</DialogContent>
|