@softize/opus 15.2.2 → 16.1.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 +60 -18
- package/bin/lib/check.mjs +98 -15
- package/bin/lib/copy.mjs +811 -314
- package/bin/lib/gen-manifest.mjs +24 -23
- package/bin/lib/gen-runner.mjs +188 -148
- package/docs/adr/0010-page-header-owns-page-chrome.md +3 -4
- package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +5 -3
- package/docs/adr/0012-modal-header-only-names-the-surface.md +45 -0
- package/docs/adr/0013-presentation-is-a-portable-action-oriented-artifact.md +92 -0
- package/docs/code-style.md +24 -19
- package/package.json +5 -1
- package/registry/skills/build-opus-ui/references/ui-patterns.md +43 -26
- package/src/core/presentation.ts +512 -0
- package/src/presentation/index.ts +1 -0
- package/src/ui/components/patterns/action-list-dialog.tsx +26 -9
- package/src/ui/components/patterns/confirm.tsx +34 -29
- package/src/ui/components/patterns/form-dialog.tsx +20 -8
- package/src/ui/components/patterns/list.tsx +26 -9
- package/src/ui/components/patterns/page.tsx +99 -139
- package/src/ui/components/patterns/presentation.tsx +539 -0
- package/src/ui/components/patterns/sidebar.tsx +3 -3
- package/src/ui/components/patterns/trigger.tsx +38 -17
- package/src/ui/components/primitives/button-group.tsx +53 -43
- package/src/ui/components/primitives/command.tsx +30 -72
- package/src/ui/components/primitives/dialog.tsx +23 -89
- package/src/ui/components/primitives/drawer.tsx +8 -34
- package/src/ui/docs/content/action-form-dialog.md +12 -10
- package/src/ui/docs/content/action-list-dialog.md +22 -17
- package/src/ui/docs/content/button.md +52 -35
- package/src/ui/docs/content/communication.md +26 -26
- package/src/ui/docs/content/dialog.md +173 -154
- package/src/ui/docs/content/drawer.md +12 -11
- package/src/ui/docs/content/page.md +72 -91
- package/src/ui/docs/content/presentation.md +188 -0
- package/src/ui/docs/registry.tsx +6 -0
- package/src/ui/meta.ts +8 -2
- package/src/ui/react.tsx +16 -3
|
@@ -6,27 +6,19 @@ import { cn } from "../../lib/cn.ts";
|
|
|
6
6
|
import { focusRing } from "./control.ts";
|
|
7
7
|
import { overlayClasses } from "./dialog.tsx";
|
|
8
8
|
|
|
9
|
-
function Drawer({
|
|
10
|
-
...props
|
|
11
|
-
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
|
9
|
+
function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
|
12
10
|
return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
function DrawerTrigger({
|
|
16
|
-
...props
|
|
17
|
-
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
|
|
13
|
+
function DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
|
|
18
14
|
return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
function DrawerClose({
|
|
22
|
-
...props
|
|
23
|
-
}: React.ComponentProps<typeof DrawerPrimitive.Close>) {
|
|
17
|
+
function DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
|
|
24
18
|
return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
function DrawerPortal({
|
|
28
|
-
...props
|
|
29
|
-
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
|
|
21
|
+
function DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
|
|
30
22
|
return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
|
|
31
23
|
}
|
|
32
24
|
|
|
@@ -37,10 +29,7 @@ function DrawerOverlay({
|
|
|
37
29
|
return (
|
|
38
30
|
<DrawerPrimitive.Overlay
|
|
39
31
|
data-slot="drawer-overlay"
|
|
40
|
-
className={cn(
|
|
41
|
-
overlayClasses,
|
|
42
|
-
className,
|
|
43
|
-
)}
|
|
32
|
+
className={cn(overlayClasses, className)}
|
|
44
33
|
{...props}
|
|
45
34
|
/>
|
|
46
35
|
);
|
|
@@ -51,6 +40,7 @@ function DrawerContent({
|
|
|
51
40
|
children,
|
|
52
41
|
side = "right",
|
|
53
42
|
showCloseButton = true,
|
|
43
|
+
"aria-describedby": ariaDescribedBy,
|
|
54
44
|
...props
|
|
55
45
|
}: React.ComponentProps<typeof DrawerPrimitive.Content> & {
|
|
56
46
|
side?: "top" | "right" | "bottom" | "left";
|
|
@@ -73,6 +63,7 @@ function DrawerContent({
|
|
|
73
63
|
"inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
74
64
|
className,
|
|
75
65
|
)}
|
|
66
|
+
aria-describedby={ariaDescribedBy}
|
|
76
67
|
{...props}
|
|
77
68
|
>
|
|
78
69
|
{children}
|
|
@@ -125,10 +116,7 @@ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
125
116
|
);
|
|
126
117
|
}
|
|
127
118
|
|
|
128
|
-
function DrawerTitle({
|
|
129
|
-
className,
|
|
130
|
-
...props
|
|
131
|
-
}: React.ComponentProps<typeof DrawerPrimitive.Title>) {
|
|
119
|
+
function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
|
|
132
120
|
return (
|
|
133
121
|
<DrawerPrimitive.Title
|
|
134
122
|
data-slot="drawer-title"
|
|
@@ -138,19 +126,6 @@ function DrawerTitle({
|
|
|
138
126
|
);
|
|
139
127
|
}
|
|
140
128
|
|
|
141
|
-
function DrawerDescription({
|
|
142
|
-
className,
|
|
143
|
-
...props
|
|
144
|
-
}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
|
|
145
|
-
return (
|
|
146
|
-
<DrawerPrimitive.Description
|
|
147
|
-
data-slot="drawer-description"
|
|
148
|
-
className={cn("text-sm text-muted-foreground", className)}
|
|
149
|
-
{...props}
|
|
150
|
-
/>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
129
|
export {
|
|
155
130
|
Drawer,
|
|
156
131
|
DrawerTrigger,
|
|
@@ -160,5 +135,4 @@ export {
|
|
|
160
135
|
DrawerBody,
|
|
161
136
|
DrawerFooter,
|
|
162
137
|
DrawerTitle,
|
|
163
|
-
DrawerDescription,
|
|
164
138
|
};
|
|
@@ -5,28 +5,30 @@ para outra página. O consumidor controla `open`; depois de uma execução bem-s
|
|
|
5
5
|
fecha o modal. O cabeçalho e o rodapé permanecem visíveis enquanto os campos podem rolar.
|
|
6
6
|
|
|
7
7
|
```tsx preview
|
|
8
|
-
const [open, setOpen] = useState(false)
|
|
8
|
+
const [open, setOpen] = useState(false);
|
|
9
9
|
|
|
10
10
|
render(
|
|
11
11
|
<DocBrowserActionProvider>
|
|
12
|
-
<Button onClick={() => setOpen(true)}
|
|
12
|
+
<Button onClick={() => setOpen(true)}>
|
|
13
|
+
<Plus /> Novo workspace
|
|
14
|
+
</Button>
|
|
13
15
|
<ActionFormDialog
|
|
14
16
|
open={open}
|
|
15
17
|
onOpenChange={setOpen}
|
|
16
18
|
title="Novo workspace"
|
|
17
|
-
description="O workspace agrupa os repositórios e agentes do cliente."
|
|
18
19
|
action={docWorkspaceCreate}
|
|
19
20
|
submitLabel="Criar workspace"
|
|
20
21
|
/>
|
|
21
22
|
</DocBrowserActionProvider>,
|
|
22
|
-
)
|
|
23
|
+
);
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
## Propriedades de ActionFormDialog
|
|
26
27
|
|
|
27
|
-
| Propriedade
|
|
28
|
-
|
|
29
|
-
| `open / onOpenChange` | `boolean / (open: boolean) => void`
|
|
30
|
-
| `title
|
|
31
|
-
| `
|
|
32
|
-
|
|
|
28
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
29
|
+
| --------------------- | ------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `open / onOpenChange` | `boolean / (open: boolean) => void` | | Estado controlado do modal. `onOpenChange(false)` é chamado no sucesso e ao cancelar. |
|
|
31
|
+
| `title` | `string` | | Nome da tarefa exibido no cabeçalho. |
|
|
32
|
+
| `intro` | `ReactNode` | | Contexto relevante no início do corpo, como texto ou `Alert`. |
|
|
33
|
+
| `submitLabel` | `string` | `'Salvar'` | Resultado da ação principal. Em criação, informe `Criar {recurso}`; em edição, `Salvar alterações`. |
|
|
34
|
+
| `…ActionFormProps` | `action, defaultValues, onSuccess, fieldOptions…` | | Demais propriedades repassadas ao `ActionForm` interno. |
|
|
@@ -6,7 +6,7 @@ outras actions. O consumidor compõe os itens por `children`; o modal mantém os
|
|
|
6
6
|
ações.
|
|
7
7
|
|
|
8
8
|
```tsx preview col
|
|
9
|
-
const [open, setOpen] = useState(false)
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
10
|
|
|
11
11
|
render(
|
|
12
12
|
<DocBrowserActionProvider>
|
|
@@ -17,7 +17,6 @@ render(
|
|
|
17
17
|
open={open}
|
|
18
18
|
onOpenChange={setOpen}
|
|
19
19
|
title="Workspaces"
|
|
20
|
-
description="Back-office"
|
|
21
20
|
actions={
|
|
22
21
|
<Button size="sm" variant="outline">
|
|
23
22
|
<Plus /> Workspace
|
|
@@ -28,17 +27,22 @@ render(
|
|
|
28
27
|
{(workspaces) => (
|
|
29
28
|
<div className="space-y-2">
|
|
30
29
|
{workspaces.map((w) => (
|
|
31
|
-
<div
|
|
30
|
+
<div
|
|
31
|
+
key={w.id}
|
|
32
|
+
className="flex items-center gap-2 rounded-md border border-border p-3"
|
|
33
|
+
>
|
|
32
34
|
<span className="text-sm font-semibold">{w.name}</span>
|
|
33
35
|
<DictionaryValue dict={docWorkspaceStatus} value={w.status} />
|
|
34
|
-
<span className="ml-auto text-xs text-muted-foreground">
|
|
36
|
+
<span className="ml-auto text-xs text-muted-foreground">
|
|
37
|
+
{w.agents} agentes
|
|
38
|
+
</span>
|
|
35
39
|
</div>
|
|
36
40
|
))}
|
|
37
41
|
</div>
|
|
38
42
|
)}
|
|
39
43
|
</ActionListDialog>
|
|
40
44
|
</DocBrowserActionProvider>,
|
|
41
|
-
)
|
|
45
|
+
);
|
|
42
46
|
```
|
|
43
47
|
|
|
44
48
|
## Estado adicional do consumidor
|
|
@@ -59,15 +63,16 @@ corpo do modal.
|
|
|
59
63
|
|
|
60
64
|
## Propriedades de ActionListDialog
|
|
61
65
|
|
|
62
|
-
| Propriedade
|
|
63
|
-
|
|
64
|
-
| `action / input`
|
|
65
|
-
| `open / onOpenChange`
|
|
66
|
-
| `title
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
66
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
67
|
+
| ------------------------------------------ | ----------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `action / input` | `ListAction / TInput` | | O contrato e os filtros — mudou o input, re-busca; `invalidates` de forms/triggers refaz sozinho. |
|
|
69
|
+
| `open / onOpenChange` | `boolean / (open) => void` | | Controle do modal — de quem orquestra. |
|
|
70
|
+
| `title` | `string` | | Nome da lista exibido no cabeçalho fixo. |
|
|
71
|
+
| `intro` | `ReactNode` | | Contexto relevante no início do corpo, como texto ou `Alert`. |
|
|
72
|
+
| `children` | `(items, refetch) => ReactNode` | | O layout dos itens (cards/linhas) — os estados já saíram daqui. |
|
|
73
|
+
| `note` | `ReactNode \| (items) => ReactNode` | `"N no total"` | Nota à esquerda da toolbar — o default é derivado dos itens. |
|
|
74
|
+
| `actions` | `ReactNode` | | Ação à direita da toolbar — em geral o botão de criar. |
|
|
75
|
+
| `empty` | `(items) => boolean` | `items.length === 0` | Sobrepõe o vazio derivado. |
|
|
76
|
+
| `loading` | `boolean` | | Carga extra agregada à do fetch (query irmã). |
|
|
77
|
+
| `emptyMessage / errorMessage / retryLabel` | `string` | | Textos dos estados; `retryLabel` nomeia a ação somente com ícone e seu tooltip. |
|
|
78
|
+
| `className` | `string` | `sm:max-w-3xl` | Largura do DialogContent. |
|
|
@@ -21,16 +21,16 @@ altura da linha; os `icon-*` são quadrados para botões só de ícone, que exig
|
|
|
21
21
|
há texto visível. O glifo dentro do controle acompanha o tamanho (0.875rem em `xs` e `sm`, 1rem em
|
|
22
22
|
`default`, 1.25rem em `lg`), a menos que o ícone traga um `size-*` próprio.
|
|
23
23
|
|
|
24
|
-
| Nome
|
|
25
|
-
|
|
26
|
-
| `xs`
|
|
27
|
-
| `sm`
|
|
28
|
-
| `default` | 2.25rem | A linha padrão, a mesma de `Input`.
|
|
29
|
-
| `lg`
|
|
30
|
-
| `icon-xs` | 1.5rem
|
|
24
|
+
| Nome | Medida | Uso |
|
|
25
|
+
| --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
26
|
+
| `xs` | 1.5rem | Ação dentro de um campo (`InputGroupButton`). |
|
|
27
|
+
| `sm` | 2rem | Toolbar e cabeçalho densos, ao lado de `Select` e `Tabs` `sm`. |
|
|
28
|
+
| `default` | 2.25rem | A linha padrão, a mesma de `Input`. |
|
|
29
|
+
| `lg` | 2.5rem | Chamada principal com mais área de toque. |
|
|
30
|
+
| `icon-xs` | 1.5rem | Ação só de ícone dentro de um campo ou de uma linha densa; é o quadrado de `ActionTrigger` com `icon`. |
|
|
31
31
|
| `icon-sm` | 1.75rem | Ação só de ícone em composições compactas que precisam de mais presença; também é o quadrado das setas do pager de `ActionList`. |
|
|
32
|
-
| `icon`
|
|
33
|
-
| `icon-lg` | 2.5rem
|
|
32
|
+
| `icon` | 2.25rem | Ação só de ícone na linha padrão. |
|
|
33
|
+
| `icon-lg` | 2.5rem | Ação só de ícone ao lado de um `lg`. |
|
|
34
34
|
|
|
35
35
|
```tsx preview
|
|
36
36
|
<Button size="xs">Mínimo</Button>
|
|
@@ -63,20 +63,22 @@ buttonVariants serve para o caso sem filho único.
|
|
|
63
63
|
|
|
64
64
|
```tsx preview
|
|
65
65
|
<Button asChild variant="outline">
|
|
66
|
-
<a href="#" onClick={(e) => e.preventDefault()}>
|
|
66
|
+
<a href="#" onClick={(e) => e.preventDefault()}>
|
|
67
|
+
Abrir documentação
|
|
68
|
+
</a>
|
|
67
69
|
</Button>
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
## Propriedades de Button
|
|
71
73
|
|
|
72
|
-
| Propriedade | Tipo
|
|
73
|
-
|
|
74
|
-
| `context`
|
|
75
|
-
| `variant`
|
|
76
|
-
| `size`
|
|
77
|
-
| `asChild`
|
|
78
|
-
| `busy`
|
|
79
|
-
| `icon`
|
|
74
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
75
|
+
| ----------- | ------------------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `context` | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | A hierarquia ou o risco comunicado pela ação. |
|
|
77
|
+
| `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'solid'` | O tratamento visual aplicado ao contexto. |
|
|
78
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg' \| 'icon-xs' \| 'icon-sm' \| 'icon' \| 'icon-lg'` | `'default'` | A medida na escala única dos controles (tabela acima). Os `icon-*` são quadrados para botões só de ícone, com `aria-label`. |
|
|
79
|
+
| `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — para âncoras e afins. |
|
|
80
|
+
| `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton em um nível de página). |
|
|
81
|
+
| `icon` | `React.ReactNode` | | Ícone à esquerda, como nó (ex.: `icon={<Plus />}`); o glifo segue o `size`. No busy é trocado pelo Spinner — não soma. |
|
|
80
82
|
|
|
81
83
|
## ButtonGroup
|
|
82
84
|
|
|
@@ -100,9 +102,15 @@ conectar bordas. Esse modo atende ações icon-only em barras e linhas de listag
|
|
|
100
102
|
|
|
101
103
|
```tsx preview
|
|
102
104
|
<ButtonGroup mode="spaced">
|
|
103
|
-
<Button variant="ghost" size="icon" aria-label="Recarregar"
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
<Button variant="ghost" size="icon" aria-label="Recarregar">
|
|
106
|
+
<RotateCw />
|
|
107
|
+
</Button>
|
|
108
|
+
<Button variant="ghost" size="icon" aria-label="Exibição">
|
|
109
|
+
<SlidersHorizontal />
|
|
110
|
+
</Button>
|
|
111
|
+
<Button context="primary" size="icon" aria-label="Novo item">
|
|
112
|
+
<Plus />
|
|
113
|
+
</Button>
|
|
106
114
|
</ButtonGroup>
|
|
107
115
|
```
|
|
108
116
|
|
|
@@ -132,7 +140,9 @@ semântica de outro elemento, como `label`.
|
|
|
132
140
|
<GitBranch />
|
|
133
141
|
empresa-x-api
|
|
134
142
|
</ButtonGroupText>
|
|
135
|
-
<Button variant="outline" icon={<RotateCw />}>
|
|
143
|
+
<Button variant="outline" icon={<RotateCw />}>
|
|
144
|
+
Sincronizar
|
|
145
|
+
</Button>
|
|
136
146
|
</ButtonGroup>
|
|
137
147
|
```
|
|
138
148
|
|
|
@@ -142,28 +152,35 @@ semântica de outro elemento, como `label`.
|
|
|
142
152
|
|
|
143
153
|
```tsx preview col-start
|
|
144
154
|
<ButtonGroup orientation="vertical">
|
|
145
|
-
<Button variant="outline" size="icon" aria-label="Rodar sessão"
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
<Button variant="outline" size="icon" aria-label="Rodar sessão">
|
|
156
|
+
<Play />
|
|
157
|
+
</Button>
|
|
158
|
+
<Button variant="outline" size="icon" aria-label="Pausar sessão">
|
|
159
|
+
<Pause />
|
|
160
|
+
</Button>
|
|
161
|
+
<Button variant="outline" size="icon" aria-label="Reiniciar sessão">
|
|
162
|
+
<RotateCw />
|
|
163
|
+
</Button>
|
|
148
164
|
</ButtonGroup>
|
|
149
165
|
```
|
|
150
166
|
|
|
151
167
|
### Propriedades de ButtonGroup
|
|
152
168
|
|
|
153
|
-
| Propriedade
|
|
154
|
-
|
|
155
|
-
| `orientation`
|
|
156
|
-
| `mode`
|
|
157
|
-
| `shape`
|
|
169
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
170
|
+
| -------------- | ---------------------------- | -------------- | ----------------------------------------------------------------------------------------- |
|
|
171
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco e do colapso das bordas. |
|
|
172
|
+
| `mode` | `'connected' \| 'spaced'` | `'connected'` | Conecta as bordas dos controles ou preserva cada controle com intervalo compacto. |
|
|
173
|
+
| `shape` | `'default' \| 'pill'` | `'default'` | Geometria das extremidades externas do grupo. |
|
|
174
|
+
| `distribution` | `'content' \| 'equal'` | `'content'` | Mantém a largura do conteúdo ou divide igualmente o espaço disponível entre os controles. |
|
|
158
175
|
|
|
159
176
|
### Propriedades de ButtonGroupSeparator
|
|
160
177
|
|
|
161
|
-
| Propriedade
|
|
162
|
-
|
|
178
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
179
|
+
| ------------- | ---------------------------- | ------------ | ------------------------------------------------------------- |
|
|
163
180
|
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | Direção do traço divisor; use vertical em grupos horizontais. |
|
|
164
181
|
|
|
165
182
|
### Propriedades de ButtonGroupText
|
|
166
183
|
|
|
167
|
-
| Propriedade | Tipo
|
|
168
|
-
|
|
169
|
-
| `asChild`
|
|
184
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
185
|
+
| ----------- | --------- | ------- | ------------------------------------------------------------------------ |
|
|
186
|
+
| `asChild` | `boolean` | `false` | Renderiza como o filho para assumir outra semântica sem perder o estilo. |
|
|
@@ -16,14 +16,14 @@ muda apenas quando o produto ou os componentes Opus mudam.
|
|
|
16
16
|
|
|
17
17
|
## Língua e termos no Opus
|
|
18
18
|
|
|
19
|
-
| Camada
|
|
20
|
-
|
|
21
|
-
| Identificadores de código, arquivos, diretórios e domínios | Inglês
|
|
22
|
-
| Nomes de action e rota
|
|
23
|
-
| Colunas e tabelas de banco
|
|
24
|
-
| Enums, status e chaves de dicionário
|
|
25
|
-
| Texto de UI visível
|
|
26
|
-
| Comentários e documentação deste repositório
|
|
19
|
+
| Camada | Convenção local |
|
|
20
|
+
| ---------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
21
|
+
| Identificadores de código, arquivos, diretórios e domínios | Inglês |
|
|
22
|
+
| Nomes de action e rota | Inglês, no formato `resource.verb` |
|
|
23
|
+
| Colunas e tabelas de banco | Inglês, em `snake_case` |
|
|
24
|
+
| Enums, status e chaves de dicionário | Inglês, inclusive em exemplos e fixtures (`active`, não `ativo`) |
|
|
25
|
+
| Texto de UI visível | pt-BR; o contrato fornece o label humano para valores técnicos |
|
|
26
|
+
| Comentários e documentação deste repositório | pt-BR |
|
|
27
27
|
|
|
28
28
|
`Opus` e `Softize` são nomes próprios em prosa (`o Opus`, `a Softize`). Tokens técnicos
|
|
29
29
|
preservam a grafia de código: `opus check`, `@softize/opus`, `opus.json` e
|
|
@@ -39,21 +39,21 @@ O componente ou o contrato Opus classifica cada texto antes de a Base aplicar a
|
|
|
39
39
|
Essa classificação decide, por exemplo, se o conteúdo é um fragmento estrutural ou uma
|
|
40
40
|
frase. Não copie as regras da Base para cá.
|
|
41
41
|
|
|
42
|
-
| Superfície Opus
|
|
43
|
-
|
|
44
|
-
| `label`, botão de confirmação e filhos de `Button`
|
|
45
|
-
| `title`, `CardTitle`, `DialogTitle`
|
|
46
|
-
| `description`, `hint`, `help`
|
|
47
|
-
| `messages.success` e `messages.error`
|
|
48
|
-
| `confirm.message`, `
|
|
49
|
-
| label de campo, filtro ou opção
|
|
50
|
-
| placeholder de campo ou busca
|
|
51
|
-
| `Select.emptyText` e grupos de opção
|
|
52
|
-
| confirmação local do `ActionTrigger`
|
|
53
|
-
| `dialog.alert/confirm/prompt/choose` — `title`, `
|
|
54
|
-
| filhos de `TooltipContent`
|
|
55
|
-
| `LabelHelp.help`
|
|
56
|
-
| `emptyMessage`, `errorMessage`, `retryLabel` de `DataState`, `PageState`, `ActionList`, `ActionListDialog` e `ActionView` | `empty-state`, `error`, `button`
|
|
42
|
+
| Superfície Opus | Papel enviado à Base |
|
|
43
|
+
| ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
44
|
+
| `label`, botão de confirmação e filhos de `Button` | `button` |
|
|
45
|
+
| `title`, `CardTitle`, `DialogTitle` | `title` |
|
|
46
|
+
| `description`, `hint`, `help` | `description` ou `helper-text` |
|
|
47
|
+
| `messages.success` e `messages.error` | `success` e `error` |
|
|
48
|
+
| `confirm.message`, `dialog.*().body` e texto relevante no `DialogBody` | `dialog-body` |
|
|
49
|
+
| label de campo, filtro ou opção | `label` ou `menu-item` |
|
|
50
|
+
| placeholder de campo ou busca | `placeholder` |
|
|
51
|
+
| `Select.emptyText` e grupos de opção | `empty-state` e `heading` |
|
|
52
|
+
| confirmação local do `ActionTrigger` | `title`, `dialog-body` e `button` |
|
|
53
|
+
| `dialog.alert/confirm/prompt/choose` — `title`, `body`, `action`/`cancel`, `actions[].label` | `title`, `dialog-body`, `button` (e `placeholder` no `prompt`) |
|
|
54
|
+
| filhos de `TooltipContent` | `label` |
|
|
55
|
+
| `LabelHelp.help` | `helper-text` |
|
|
56
|
+
| `emptyMessage`, `errorMessage`, `retryLabel` de `DataState`, `PageState`, `ActionList`, `ActionListDialog` e `ActionView` | `empty-state`, `error`, `button` |
|
|
57
57
|
|
|
58
58
|
O tooltip entra como `label` porque nomeia um controle icon-only: é um fragmento curto, sem
|
|
59
59
|
ponto final, e a Base não define um papel próprio para tooltip. Texto de ajuda que precisa de
|
|
@@ -64,11 +64,11 @@ frase completa pertence ao `help` de um campo (contrato ou `LabelHelp`), não ao
|
|
|
64
64
|
Quem vê o gate reprovar ou precisa de uma dispensa deve saber o que o extrator alcança sem
|
|
65
65
|
declaração adicional:
|
|
66
66
|
|
|
67
|
-
- **Diálogos imperativos.** `dialog.confirm({ title,
|
|
67
|
+
- **Diálogos imperativos.** `dialog.confirm({ title, body, action })` e as demais
|
|
68
68
|
respostas (`alert`, `prompt`, `choose`) entram no inventário como se fossem props de um
|
|
69
69
|
`ActionFormDialog`; o campo do diagnóstico é `dialog.confirm().title`,
|
|
70
70
|
`dialog.choose().actions[0].label` etc. Constantes locais resolvem normalmente. Uma função
|
|
71
|
-
que devolve template (`
|
|
71
|
+
que devolve template (`body: describe(nome)`) gera o diagnóstico `content` habitual e
|
|
72
72
|
se declara em `copy.dynamic` como `message-template`. Opções construídas em tempo de execução
|
|
73
73
|
ou mutadas depois de declaradas reprovam como estrutura, porque nenhum texto pode ser
|
|
74
74
|
atribuído a elas.
|
|
@@ -121,7 +121,7 @@ dispensados por `copy.dynamic`; ausência de conteúdo também continua bloquean
|
|
|
121
121
|
{
|
|
122
122
|
"source": "src/sessions.tsx",
|
|
123
123
|
"line": 58,
|
|
124
|
-
"field": "
|
|
124
|
+
"field": "dialog.confirm().body",
|
|
125
125
|
"kind": "message-template",
|
|
126
126
|
"role": "dialog-body",
|
|
127
127
|
"text": "Excluir {nome}?",
|