@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +60 -18
  2. package/bin/lib/check.mjs +98 -15
  3. package/bin/lib/copy.mjs +811 -314
  4. package/bin/lib/gen-manifest.mjs +24 -23
  5. package/bin/lib/gen-runner.mjs +188 -148
  6. package/docs/adr/0010-page-header-owns-page-chrome.md +3 -4
  7. package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +5 -3
  8. package/docs/adr/0012-modal-header-only-names-the-surface.md +45 -0
  9. package/docs/adr/0013-presentation-is-a-portable-action-oriented-artifact.md +92 -0
  10. package/docs/code-style.md +24 -19
  11. package/package.json +5 -1
  12. package/registry/skills/build-opus-ui/references/ui-patterns.md +43 -26
  13. package/src/core/presentation.ts +512 -0
  14. package/src/presentation/index.ts +1 -0
  15. package/src/ui/components/patterns/action-list-dialog.tsx +26 -9
  16. package/src/ui/components/patterns/confirm.tsx +34 -29
  17. package/src/ui/components/patterns/form-dialog.tsx +20 -8
  18. package/src/ui/components/patterns/list.tsx +26 -9
  19. package/src/ui/components/patterns/page.tsx +99 -139
  20. package/src/ui/components/patterns/presentation.tsx +539 -0
  21. package/src/ui/components/patterns/sidebar.tsx +3 -3
  22. package/src/ui/components/patterns/trigger.tsx +38 -17
  23. package/src/ui/components/primitives/button-group.tsx +53 -43
  24. package/src/ui/components/primitives/command.tsx +30 -72
  25. package/src/ui/components/primitives/dialog.tsx +23 -89
  26. package/src/ui/components/primitives/drawer.tsx +8 -34
  27. package/src/ui/docs/content/action-form-dialog.md +12 -10
  28. package/src/ui/docs/content/action-list-dialog.md +22 -17
  29. package/src/ui/docs/content/button.md +52 -35
  30. package/src/ui/docs/content/communication.md +26 -26
  31. package/src/ui/docs/content/dialog.md +173 -154
  32. package/src/ui/docs/content/drawer.md +12 -11
  33. package/src/ui/docs/content/page.md +72 -91
  34. package/src/ui/docs/content/presentation.md +188 -0
  35. package/src/ui/docs/registry.tsx +6 -0
  36. package/src/ui/meta.ts +8 -2
  37. 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)}><Plus /> Novo workspace</Button>
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 | Tipo | Padrão | Descrição |
28
- |---|---|---|---|
29
- | `open / onOpenChange` | `boolean / (open: boolean) => void` | | Estado controlado do modal. `onOpenChange(false)` é chamado no sucesso e ao cancelar. |
30
- | `title / description` | `string` | | Conteúdo do cabeçalho; `description` é opcional. |
31
- | `submitLabel` | `string` | `'Salvar'` | Resultado da ação principal. Em criação, informe `Criar {recurso}`; em edição, `Salvar alterações`. |
32
- | `…ActionFormProps` | `action, defaultValues, onSuccess, fieldOptions…` | | Demais propriedades repassadas ao `ActionForm` interno. |
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 key={w.id} className="flex items-center gap-2 rounded-md border border-border p-3">
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">{w.agents} agentes</span>
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 | Tipo | Padrão | Descrição |
63
- |---|---|---|---|
64
- | `action / input` | `ListAction / TInput` | | O contrato e os filtros — mudou o input, re-busca; `invalidates` de forms/triggers refaz sozinho. |
65
- | `open / onOpenChange` | `boolean / (open) => void` | | Controle do modal — de quem orquestra. |
66
- | `title / description` | `string` | | O DialogHeader fixo (em geral, o que é a lista e de quem). |
67
- | `children` | `(items, refetch) => ReactNode` | | O layout dos itens (cards/linhas) os estados saíram daqui. |
68
- | `note` | `ReactNode \| (items) => ReactNode` | `"N no total"` | Nota à esquerda da toolbaro default é derivado dos itens. |
69
- | `actions` | `ReactNode` | | Ação à direita da toolbar — em geral o botão de criar. |
70
- | `empty` | `(items) => boolean` | `items.length === 0` | Sobrepõe o vazio derivado. |
71
- | `loading` | `boolean` | | Carga extra agregada à do fetch (query irmã). |
72
- | `emptyMessage / errorMessage / retryLabel` | `string` | | Textos dos estados; `retryLabel` nomeia a ação somente com ícone e seu tooltip. |
73
- | `className` | `string` | `sm:max-w-3xl` | Largura do DialogContent. |
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 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 | 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`. |
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` | 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`. |
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()}>Abrir documentação</a>
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 | Padrão | Descrição |
73
- |---|---|---|---|
74
- | `context` | `'neutral' \| 'primary' \| 'danger'` | `'primary'` | A hierarquia ou o risco comunicado pela ação. |
75
- | `variant` | `'solid' \| 'subtle' \| 'outline' \| 'ghost' \| 'link'` | `'solid'` | O tratamento visual aplicado ao contexto. |
76
- | `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`. |
77
- | `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — para âncoras e afins. |
78
- | `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). |
79
- | `icon` | `React.ReactNode` | | Ícone à esquerda, como nó (ex.: `icon={<Plus />}`); o glifo segue o `size`. No busy é trocado pelo Spinner — não soma. |
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"><RotateCw /></Button>
104
- <Button variant="ghost" size="icon" aria-label="Exibição"><SlidersHorizontal /></Button>
105
- <Button context="primary" size="icon" aria-label="Novo item"><Plus /></Button>
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 />}>Sincronizar</Button>
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"><Play /></Button>
146
- <Button variant="outline" size="icon" aria-label="Pausar sessão"><Pause /></Button>
147
- <Button variant="outline" size="icon" aria-label="Reiniciar sessão"><RotateCw /></Button>
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 | Tipo | Padrão | Descrição |
154
- |---|---|---|---|
155
- | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco e do colapso das bordas. |
156
- | `mode` | `'connected' \| 'spaced'` | `'connected'` | Conecta as bordas dos controles ou preserva cada controle com intervalo compacto. |
157
- | `shape` | `'default' \| 'pill'` | `'default'` | Geometria das extremidades externas do grupo. |
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 | Tipo | Padrão | Descrição |
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 | Padrão | Descrição |
168
- |---|---|---|---|
169
- | `asChild` | `boolean` | `false` | Renderiza como o filho para assumir outra semântica sem perder o estilo. |
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 | 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 |
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 | 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`, `DialogDescription` | `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`, `description`/`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` |
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, description, action })` e as demais
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 (`description: describe(nome)`) gera o diagnóstico `content` habitual e
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": "<DialogDescription> children",
124
+ "field": "dialog.confirm().body",
125
125
  "kind": "message-template",
126
126
  "role": "dialog-body",
127
127
  "text": "Excluir {nome}?",