@softize/opus 18.0.1 → 18.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 (58) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/docs/adr/0016-productive-surfaces-use-compact-density.md +62 -0
  3. package/docs/adr/0017-ui-assumes-a-fixed-desktop-layout.md +54 -0
  4. package/docs/relative-unit-scale.md +9 -2
  5. package/package.json +1 -1
  6. package/registry/skills/build-opus-ui/references/evaluations.md +3 -1
  7. package/registry/skills/build-opus-ui/references/ui-patterns.md +9 -3
  8. package/src/core/runtime.ts +7 -1
  9. package/src/core/types.ts +12 -3
  10. package/src/mcp/index.ts +4 -1
  11. package/src/ui/components/patterns/action-list-dialog.tsx +2 -2
  12. package/src/ui/components/patterns/content-header.tsx +1 -1
  13. package/src/ui/components/patterns/form-dialog.tsx +7 -2
  14. package/src/ui/components/patterns/list.tsx +239 -47
  15. package/src/ui/components/patterns/presentation.tsx +7 -5
  16. package/src/ui/components/patterns/state-surface.tsx +2 -2
  17. package/src/ui/components/patterns/surface-header.tsx +4 -4
  18. package/src/ui/components/primitives/alert.tsx +2 -2
  19. package/src/ui/components/primitives/breadcrumb.tsx +1 -1
  20. package/src/ui/components/primitives/button-group.tsx +1 -1
  21. package/src/ui/components/primitives/button.tsx +3 -3
  22. package/src/ui/components/primitives/calendar.tsx +1 -1
  23. package/src/ui/components/primitives/close-button.tsx +40 -0
  24. package/src/ui/components/primitives/detail.tsx +66 -28
  25. package/src/ui/components/primitives/dialog.tsx +36 -21
  26. package/src/ui/components/primitives/drawer.tsx +26 -19
  27. package/src/ui/components/primitives/empty-value.tsx +3 -3
  28. package/src/ui/components/primitives/empty.tsx +1 -1
  29. package/src/ui/components/primitives/field.tsx +12 -12
  30. package/src/ui/components/primitives/icon-picker.tsx +1 -1
  31. package/src/ui/components/primitives/input-group.tsx +1 -1
  32. package/src/ui/components/primitives/input.tsx +2 -2
  33. package/src/ui/components/primitives/item.tsx +5 -5
  34. package/src/ui/components/primitives/pagination.tsx +4 -4
  35. package/src/ui/components/primitives/select.tsx +2 -2
  36. package/src/ui/components/primitives/table.tsx +26 -17
  37. package/src/ui/components/primitives/tabs.tsx +80 -23
  38. package/src/ui/components/primitives/textarea.tsx +1 -1
  39. package/src/ui/components/primitives/toggle-group.tsx +9 -2
  40. package/src/ui/docs/content/action-form.md +19 -0
  41. package/src/ui/docs/content/action-list-dialog.md +1 -1
  42. package/src/ui/docs/content/action-list.md +7 -1
  43. package/src/ui/docs/content/alert.md +2 -0
  44. package/src/ui/docs/content/button.md +2 -1
  45. package/src/ui/docs/content/customization.md +11 -1
  46. package/src/ui/docs/content/detail.md +9 -8
  47. package/src/ui/docs/content/dialog.md +12 -5
  48. package/src/ui/docs/content/drawer.md +6 -3
  49. package/src/ui/docs/content/empty-value.md +4 -4
  50. package/src/ui/docs/content/field.md +1 -1
  51. package/src/ui/docs/content/item.md +2 -0
  52. package/src/ui/docs/content/page.md +1 -1
  53. package/src/ui/docs/content/presentation.md +2 -2
  54. package/src/ui/docs/content/tabs.md +16 -6
  55. package/src/ui/docs/content/toast.md +2 -1
  56. package/src/ui/docs/content/tokens.md +45 -2
  57. package/src/ui/react.tsx +1 -0
  58. package/src/ui/theme.css +3 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,53 @@ Depois de qualquer bump, rode os gates (`typecheck` · `test` · `opus check` ·
7
7
  `opus copy --check` · `base copy check` · `manifest:check`) — eles apontam o que a
8
8
  mudança cobra do seu código.
9
9
 
10
+ ## 18.1.0 — 2026-09-15
11
+
12
+ A UI passa a assumir layout desktop fixo com largura mínima suportada de `64rem`. Breakpoints de
13
+ viewport e container queries isoladas saem dos componentes; grids, dialogs, drawers e campos
14
+ preservam o estado correspondente a essa largura. Consultas de acessibilidade e capacidade, limites
15
+ contra a viewport e overflow continuam ativos. `ActionFormDialog` e Presentations modais passam a
16
+ dimensionar o footer pelo conteúdo, com `Cancelar` em `ghost`; `footerDistribution="equal"`
17
+ permanece disponível como escolha explícita e usa cancelamento `outline`.
18
+
19
+ Filtros de list actions passam a declarar `placement: 'inline' | 'advanced' | 'external'`.
20
+ Filtros `external` continuam no estado navegável e no input, mas deixam a apresentação para o
21
+ consumidor; filtros avançados podem ser agrupados por `section`, distribuídos em até três colunas
22
+ com `advancedFilters.columns` e usam calendário nos campos de data. A propriedade `advanced`
23
+ continua aceita durante a migração e equivale a `placement: 'advanced'`.
24
+
25
+ Tools de IA e MCP passam a projetar também as labels dos Produtos de Dados relacionados, mantendo
26
+ os identificadores técnicos existentes. Hosts podem apresentar nomes legíveis sem perder a
27
+ rastreabilidade usada na execução e na auditoria.
28
+
29
+ Alert, Toast e Item passam a compartilhar a densidade compacta das superfícies horizontais:
30
+ `0.75rem` de padding no tamanho padrão e corpo produtivo de `0.875rem`. Cada componente declara o
31
+ corpo produtivo na própria raiz, sem alterar a tipografia de `html` ou `body`; folhas internas de
32
+ Alert e Item deixam de repetir a classe já estabelecida pela raiz do componente. `Item size="sm"`
33
+ reduz somente o padding vertical para `0.5rem`. Rótulos de formulário também usam explicitamente
34
+ o corpo produtivo; `text-xs` permanece reservado a metadados compactos, como rótulos de detalhes.
35
+
36
+ `ToggleGroupItem` preserva seu estado selecionado quando compõe um `TooltipTrigger` com `asChild`.
37
+ O tooltip continua disponível sem substituir os atributos que identificam e marcam visualmente o
38
+ item ativo.
39
+
40
+ `TabsList variant="line"` passa a desenhar uma linha-base por toda a largura disponível na
41
+ orientação horizontal, mantendo as abas compactas e o indicador ativo sobre a borda. Dentro de
42
+ `DrawerBody` ou `DialogBody`, a linha atravessa os gutters até as bordas sem deslocar os rótulos. Os itens usam
43
+ padding horizontal zero, padding vertical de `0.75rem` e gap de `1.25rem`.
44
+ A altura do `line` horizontal passa a acompanhar o conteúdo, permitindo que esse padding defina
45
+ a área interativa em vez de ficar contido pela altura da escala de controles. A orientação vertical
46
+ preserva seu padding anterior. `Field orientation="responsive"` continua aceito como alias do layout
47
+ desktop horizontal, sem reintroduzir breakpoints.
48
+
49
+ Campos de entrada passam a usar `bg-muted/30` no tema claro, preservando no escuro o contraste de
50
+ `bg-input/30`. O cabeçalho da `Table framed` compartilha essa superfície. O footer de `Dialog`
51
+ usa uma versão mais suave desse mesmo fundo. A moldura do modal fica transparente no light e sutil
52
+ no dark; o backdrop volta a escurecer a página sem aplicar blur.
53
+
54
+ `Button variant="outline"` passa a ter fundo transparente em repouso nos contextos neutral,
55
+ primary e danger. A borda, o texto semântico e o preenchimento de hover permanecem inalterados.
56
+
10
57
  ## 18.0.1 — 2026-09-13
11
58
 
12
59
  Controles somente com ícone que pertencem ao chrome da superfície, como voltar e fechar, usam o
@@ -0,0 +1,62 @@
1
+ # ADR 0016 — Superfícies produtivas usam densidade compacta
2
+
3
+ - **Status:** aceita.
4
+ - **Data:** 2026-09-14.
5
+
6
+ ## Contexto
7
+
8
+ Alert, Toast e Item compartilham uma composição horizontal de mídia, texto e ações, mas nasceram
9
+ com medidas internas diferentes ou herdadas de dependências. Alert e Item usavam `1rem` de padding;
10
+ o Toast preservava `1rem` e corpo de `0.8125rem` do Sonner. Consumidores já reconstruíam mensagens
11
+ parecidas com `0.75rem`, sinal de que a densidade da família não estava definida pela biblioteca.
12
+
13
+ A tipografia também repetia `text-sm` em raiz e folhas do mesmo componente. Essa repetição não
14
+ distinguia uma fronteira tipográfica de um detalhe interno e tornava difícil saber quando uma classe
15
+ era contrato ou apenas redundância.
16
+
17
+ ## Decisão
18
+
19
+ O tema não redefine a tipografia de `html`, `:root` ou `body`. Componentes que constituem uma
20
+ fronteira reutilizável declaram `text-sm`; folhas internas herdam essa medida, salvo quando
21
+ restauram o corpo produtivo dentro de um contexto tipográfico diferente.
22
+
23
+ Superfícies horizontais produtivas usam `0.75rem` de padding no tamanho padrão:
24
+
25
+ - Alert usa `p-3` e mantém mídia, texto e ações na mesma anatomia;
26
+ - Toast sobrescreve o padding e o corpo nativos do Sonner para repetir a medida do Alert;
27
+ - Item usa `p-3` e `gap-3`; `size="sm"` conserva `0.75rem` na horizontal e reduz a vertical para
28
+ `0.5rem`.
29
+
30
+ Mais espaço permanece deliberado quando o componente representa outra tarefa: Empty cria uma
31
+ pausa de estado na região disponível; Dialog e Drawer estruturam faixas e corpo; Card deixa o
32
+ padding sob responsabilidade dos seus slots ou do consumidor; conteúdo editorial usa `text-base`
33
+ para leitura prolongada.
34
+
35
+ ## Consequências
36
+
37
+ - Alert, Toast e Item passam a parecer membros da mesma família sem virar o mesmo componente.
38
+ - Texto solto preserva o tamanho do documento; uma fronteira reutilizável não depende
39
+ acidentalmente do tamanho escolhido pelo consumidor.
40
+ - `p-4` deixa de ser o default de mensagens e linhas, mas continua válido em contêineres
41
+ estruturais cujo papel exige mais respiro.
42
+ - A integração com Sonner precisa de teste de contrato porque seus defaults deixam de governar a
43
+ tipografia e o padding do Toast.
44
+
45
+ ## Alternativas descartadas
46
+
47
+ - **Reduzir a fonte raiz:** compactaria também espaços, dimensões, radius e elevação, misturando
48
+ densidade global com o papel do texto.
49
+ - **Criar aliases como `text-body` ou `surface-padding`:** esconderia os mesmos degraus sem
50
+ esclarecer onde a decisão pertence.
51
+ - **Remover toda ocorrência de `text-sm`:** faria componentes reutilizáveis dependerem do contexto
52
+ externo e impediria a restauração explícita do corpo produtivo.
53
+ - **Aplicar `p-3` a toda superfície:** reduziria modais, estados vazios e conteúdo editorial sem
54
+ relação com a composição horizontal que motivou a decisão.
55
+
56
+ ## Verificação
57
+
58
+ - testes estruturais conferem padding e herança em Alert e Item;
59
+ - o teste da integração com Sonner fixa padding, corpo e entrelinha do Toast;
60
+ - o contrato de unidades relativas confirma a ausência de tipografia global em `html`, `:root`
61
+ e `body`;
62
+ - documentação de Alert, Toast, Item e tokens classifica os defaults e as exceções.
@@ -0,0 +1,54 @@
1
+ # ADR 0017 — A UI assume layout desktop fixo
2
+
3
+ - **Status:** aceita.
4
+ - **Data:** 2026-09-15.
5
+
6
+ ## Contexto
7
+
8
+ A UI acumulou adaptações isoladas por viewport em dialogs, drawers, campos, detalhes, paginação e
9
+ grids. O shell, as sidebars e os splits continuaram desktop-only. Essa combinação não formava um
10
+ contrato responsivo: abaixo da largura de trabalho, algumas partes mudavam de forma enquanto a
11
+ estrutura principal permanecia fixa.
12
+
13
+ ## Decisão
14
+
15
+ O Opus assume uma largura mínima suportada de `64rem`. Componentes usam um layout fixo equivalente
16
+ ao estado que os breakpoints `sm`, `md` e `lg` produziam nessa largura. Estados exclusivos de
17
+ `xl` e `2xl` não alteram mais a composição.
18
+
19
+ A UI viva não usa media queries de largura, variantes responsivas do Tailwind nem container queries.
20
+ Uma futura responsividade baseada no espaço do container exige uma decisão própria e uma aplicação
21
+ sistêmica; não permanece ativa de forma incidental em componentes isolados.
22
+
23
+ Consultas de preferência ou capacidade, como `prefers-reduced-motion`, `forced-colors` e
24
+ `hover`, continuam válidas. Overflow, limites de altura e posicionamento contra a viewport também
25
+ continuam protegendo conteúdo; essas medidas não prometem uma composição alternativa.
26
+
27
+ Footers de formulário modal dimensionam as ações pelo conteúdo e usam `Cancelar` em `ghost`.
28
+ `distribution="equal"` permanece uma escolha explícita para decisões de peso equivalente e,
29
+ nesse caso, o cancelamento usa `outline`.
30
+
31
+ ## Consequências
32
+
33
+ - O mesmo componente preserva a composição a partir de `64rem`, inclusive dentro de panes,
34
+ drawers e splits.
35
+ - Abaixo da largura suportada, compressão ou rolagem não constitui regressão do contrato atual.
36
+ - Dialogs mantêm limites fixos de largura; tabelas preservam rolagem horizontal.
37
+ - A remoção de breakpoints não remove adaptações de acessibilidade nem contenção contra overflow.
38
+ - Uma iniciativa responsiva futura começa pelo shell e define containers, navegação, tabelas e
39
+ testes de largura antes de adaptar componentes.
40
+
41
+ ## Alternativas descartadas
42
+
43
+ - **Manter adaptações pontuais:** conserva algum conforto em telas estreitas, mas continua sugerindo
44
+ um suporte que o shell não oferece.
45
+ - **Completar responsividade por viewport agora:** amplia a mudança para navegação, splits, tabelas
46
+ e fluxos que ainda não possuem requisitos de produto.
47
+ - **Preservar container queries isoladas:** usa uma técnica adequada para o futuro, mas mantém hoje
48
+ um contrato parcial sem cobertura sistêmica.
49
+
50
+ ## Verificação
51
+
52
+ - Um teste de inventário reprova variantes de largura e container queries na UI viva.
53
+ - Testes estruturais fixam layouts, larguras de modal e distribuição dos footers.
54
+ - Documentação pública declara a largura mínima, o default por conteúdo e a exceção `equal`.
@@ -15,12 +15,17 @@ compacto deixavam de acompanhar a raiz mesmo quando a aplicação assumia essa r
15
15
 
16
16
  ## Decisão
17
17
 
18
- - O tema do Opus não declara `font-size` em `html` nem em `:root`. O navegador e a aplicação
19
- definem a fonte raiz.
18
+ - O tema do Opus não declara `font-size` em `html`, `:root` nem `body`. O navegador e a aplicação
19
+ definem a fonte raiz e o texto geral do documento.
20
20
  - Medidas que pertencem à identidade e à escala da interface usam `rem`, diretamente ou pela
21
21
  escala do Tailwind, que também é relativa à raiz.
22
22
  - Aplicações ajustam a densidade no próprio CSS. Para conservar a proporção que antes resultava
23
23
  de uma raiz de 15 sobre a base usual de 16, podem usar `html { font-size: 93.75%; }`.
24
+ - Ajustar a raiz é uma decisão de escala da interface inteira, não uma forma de renomear papéis
25
+ tipográficos. Na escala produtiva recomendada, `text-sm` (`0.875rem`) é o corpo comum da
26
+ interface e deve ser declarado na raiz controlada do componente ou da região. `text-base`
27
+ (`1rem`) fica reservado à leitura mais confortável; a documentação de tokens apresenta a regra
28
+ de composição e herança.
24
29
  - `px` fica restrito a encaixes técnicos que não devem crescer com a tipografia: espessura de
25
30
  hairline, compensação exata de uma borda, ajuste óptico preso a esse traço e o raio efetivamente
26
31
  infinito do scrollbar nativo. Cada ocorrência integra a allowlist do teste de contrato.
@@ -31,6 +36,8 @@ compacto deixavam de acompanhar a raiz mesmo quando a aplicação assumia essa r
31
36
 
32
37
  - Sem uma regra da aplicação, `1rem` segue a configuração do navegador. A UI pode ficar maior
33
38
  para consumidores que recebiam os antigos 15px; essa mudança é visual e intencional.
39
+ - Texto solto, fora de uma raiz que declare seu papel tipográfico, permanece no tamanho geral do
40
+ documento; componentes produtivos não dependem de uma redução global para chegar a `0.875rem`.
34
41
  - Preferências de acessibilidade e identidades próprias deixam de disputar com um override da
35
42
  biblioteca.
36
43
  - Alterar a raiz na aplicação escala texto, espaçamento, radius, foco, elevação e limites
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softize/opus",
3
- "version": "18.0.1",
3
+ "version": "18.1.0",
4
4
  "description": "End-to-end action protocol for TypeScript. Single package with subpath exports (core + adapters).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,7 +12,9 @@
12
12
  `ActionFilterBar` separado do renderer, `ItemGroup` para
13
13
  uma coleção secundária e um `ActionFormDialog`; provar teto padrão de `80rem`, hierarquia por
14
14
  `level`, vazio estrutural sólido, `Empty` apenas para criação ou vínculo, números sem alinhamento
15
- inferido e cancelamento `outline` no footer 50/50 do modal.
15
+ inferido e footer modal com largura natural, alinhado à direita e cancelamento `ghost`; incluir
16
+ separadamente um caso deliberadamente equivalente com distribuição 50/50 e cancelamento
17
+ `outline` explícitos.
16
18
  - Execução roteável: declarar Page, criação e edição com `Presentation.route`; provar deep link,
17
19
  hidratação do registro, fechamento para a lista e projeção integral no manifest.
18
20
  - Execução abreviada: montar outra página com `<Page title actions>` e uma seção com
@@ -56,8 +56,11 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
56
56
  `*Content` permanece reservado a raízes técnicas ou painéis cujo papel não é o corpo de uma
57
57
  estrutura, como `DialogContent`, `PopoverContent` e `TabsContent`.
58
58
  - Componentes compartilhados não impõem margem externa; páginas e shells compõem layout.
59
- - A fonte raiz pertence ao navegador e à aplicação. Medidas escaláveis usam `rem` ou a escala
60
- relativa do Tailwind; `px` fica restrito a hairlines e compensações ligadas a essas bordas.
59
+ - A UI assume layout desktop fixo e largura mínima suportada de `64rem`. Não introduzir
60
+ breakpoints de viewport ou container queries isoladas; uma futura responsividade precisa começar
61
+ pelo shell e formar um contrato sistêmico. A fonte raiz pertence ao navegador e à aplicação.
62
+ Medidas escaláveis usam `rem` ou a escala relativa do Tailwind; `px` fica restrito a hairlines
63
+ e compensações ligadas a essas bordas.
61
64
  - Superfície semântica e foreground são um par local (`bg-card text-card-foreground`,
62
65
  `bg-popover text-popover-foreground`); herança do foreground global não substitui o par.
63
66
  - Forma usa a escala `rounded-*`; nome de componente não cria uma segunda escala de radius.
@@ -90,7 +93,10 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
90
93
  ou views nomeadas. Usar `ActionFilterBar` isoladamente só quando outra superfície assumir a
91
94
  renderização dos resultados. Colunas numéricas não recebem alinhamento ou tipografia por
92
95
  inferência do tipo: declarar `className` no header e na célula quando o produto exigir, de
93
- modo que números preservem o alinhamento natural por padrão.
96
+ modo que números preservem o alinhamento natural por padrão. O valor principal de uma célula
97
+ preserva `text-sm` e o foreground da superfície; `text-xs text-muted-foreground` fica restrito
98
+ a metadado subordinado a outro valor na mesma célula. Cabeçalhos e `EmptyValue` continuam muted;
99
+ não reduzir nem atenuar uma coluna inteira por ela ser técnica, temporal ou menos destacada.
94
100
  - `ItemGroup` representa uma coleção e pode oferecer moldura explícita; `Item` já carrega
95
101
  semântica de item de lista. Preferir essa composição a uma sequência visual sem estrutura.
96
102
  - `Empty` com moldura tracejada comunica uma região disponível para criar ou vincular algo.
@@ -815,6 +815,7 @@ export class Runtime {
815
815
  for (const action of this.actions.values()) {
816
816
  const cfg = normalizeAiConfig(action.ai)
817
817
  if (cfg === null) continue
818
+ const dataProducts = [...(this.actionDataProducts.get(action.name) ?? [])].sort()
818
819
  tools.push({
819
820
  name: action.name,
820
821
  ...(action.label !== undefined
@@ -822,7 +823,12 @@ export class Runtime {
822
823
  : {}),
823
824
  description: cfg.description ?? action.description ?? action.name,
824
825
  inputSchema: action.input,
825
- metadata: { dataProducts: [...(this.actionDataProducts.get(action.name) ?? [])].sort() },
826
+ metadata: {
827
+ dataProducts,
828
+ dataProductLabels: Object.fromEntries(
829
+ dataProducts.map((id) => [id, this.dataProducts.get(id)?.label ?? id]),
830
+ ),
831
+ },
826
832
  })
827
833
  }
828
834
  this.aiToolsCache = tools
package/src/core/types.ts CHANGED
@@ -602,8 +602,12 @@ export interface FilterSpec {
602
602
  operators?: FilterOp[]
603
603
  path?: string
604
604
 
605
- /** Filtro AVANÇADO: sai da barra inline e vai pro modal "Filtros" (com contador de
606
- * ativos no botão). Os não-avançados rendem inline na toolbar da listagem. */
605
+ /** Onde o filtro é controlado na interface. `external` mantém o filtro no contrato,
606
+ * no estado navegável e no input efetivo, mas deixa sua apresentação para outra
607
+ * superfície — por exemplo, cards de status acima da lista. */
608
+ placement?: 'inline' | 'advanced' | 'external'
609
+
610
+ /** @deprecated Use `placement: 'advanced'`. Mantido durante a migração dos contratos. */
607
611
  advanced?: boolean
608
612
 
609
613
  section?: string
@@ -1264,7 +1268,12 @@ export interface AiTool {
1264
1268
  description: string
1265
1269
  inputSchema: unknown
1266
1270
  /** Metadados de governança para o host; não fazem parte do prompt nem concedem acesso. */
1267
- metadata?: { dataProducts?: string[] }
1271
+ metadata?: {
1272
+ /** Identificadores técnicos dos Produtos de Dados relacionados. */
1273
+ dataProducts?: string[]
1274
+ /** Labels humanas por identificador; ausente para runtimes legados. */
1275
+ dataProductLabels?: Record<string, string>
1276
+ }
1268
1277
  }
1269
1278
 
1270
1279
  /** Turno de conversa (histórico multi-turn do chat). */
package/src/mcp/index.ts CHANGED
@@ -47,7 +47,10 @@ export function createOpusMcpServer(runtime: Runtime, opts: OpusMcpOptions = {})
47
47
  ...(t.title !== undefined ? { title: t.title } : {}),
48
48
  description: t.description,
49
49
  inputSchema: toJsonSchema(t.inputSchema) as { type: 'object' },
50
- _meta: { 'com.softize.opus/data-products': t.metadata?.dataProducts ?? [] },
50
+ _meta: {
51
+ 'com.softize.opus/data-products': t.metadata?.dataProducts ?? [],
52
+ 'com.softize.opus/data-product-labels': t.metadata?.dataProductLabels ?? {},
53
+ },
51
54
  })),
52
55
  }))
53
56
 
@@ -48,7 +48,7 @@ export interface ActionListDialogProps<
48
48
  empty?: (items: TItem[]) => boolean
49
49
  /** Carga EXTRA agregada à do fetch (ex.: a query irmã que os children precisam). */
50
50
  loading?: boolean
51
- /** Classes do DialogContent (largura). Default: sm:max-w-3xl (lista pede espaço). */
51
+ /** Classes do DialogContent (largura). Default: max-w-3xl (lista pede espaço). */
52
52
  className?: string
53
53
  /** O layout dos itens (cards/linhas) — os estados já saíram daqui. */
54
54
  children: (items: TItem[], refetch: () => Promise<void>) => ReactNode
@@ -78,7 +78,7 @@ export function ActionListDialog<
78
78
  return (
79
79
  <Dialog open={open} onOpenChange={onOpenChange}>
80
80
  <DialogContent
81
- className={cn('sm:max-w-3xl', className)}
81
+ className={cn('max-w-3xl', className)}
82
82
  aria-describedby={intro === undefined ? undefined : introId}
83
83
  >
84
84
  <DialogHeader>
@@ -171,7 +171,7 @@ export function ContentHeader({
171
171
  }}
172
172
  className={cn(
173
173
  content.variant === "page" &&
174
- "flex-row items-center justify-start gap-3 sm:flex-row sm:items-center sm:justify-start",
174
+ "flex-row items-center justify-start gap-3",
175
175
  className,
176
176
  )}
177
177
  {...props}
@@ -29,6 +29,8 @@ export interface ActionFormDialogProps<
29
29
  title: string;
30
30
  /** Conteúdo relevante apresentado antes dos campos, como texto ou Alert. */
31
31
  intro?: ReactNode;
32
+ /** Distribui as ações pela largura disponível somente quando as decisões pedem peso equivalente. */
33
+ footerDistribution?: "content" | "equal";
32
34
  }
33
35
 
34
36
  export function ActionFormDialog<
@@ -42,6 +44,7 @@ export function ActionFormDialog<
42
44
  onSuccess,
43
45
  onCancel,
44
46
  cancelVariant,
47
+ footerDistribution = "content",
45
48
  ...rest
46
49
  }: ActionFormDialogProps<TInput, TData>) {
47
50
  const introId = useId();
@@ -72,7 +75,7 @@ export function ActionFormDialog<
72
75
  )}
73
76
  footer={(actions) => (
74
77
  <DialogFooter>
75
- <ButtonGroup mode="spaced" distribution="equal">
78
+ <ButtonGroup mode="spaced" distribution={footerDistribution}>
76
79
  {actions}
77
80
  </ButtonGroup>
78
81
  </DialogFooter>
@@ -82,7 +85,9 @@ export function ActionFormDialog<
82
85
  onOpenChange(false);
83
86
  }}
84
87
  onCancel={onCancel ?? (() => onOpenChange(false))}
85
- cancelVariant={cancelVariant ?? "outline"}
88
+ cancelVariant={
89
+ cancelVariant ?? (footerDistribution === "equal" ? "outline" : "ghost")
90
+ }
86
91
  />
87
92
  </DialogContent>
88
93
  </Dialog>