@softize/opus 18.0.1 → 18.1.1
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 +105 -0
- package/PROMOTED.md +4 -5
- package/README.md +5 -4
- package/bin/cli.mjs +4 -0
- package/docs/adr/0004-page-content-state-is-composed.md +3 -0
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +6 -2
- package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +6 -1
- package/docs/adr/0014-structural-headers-do-not-carry-description.md +2 -2
- package/docs/adr/0015-action-size-follows-interaction-density.md +4 -3
- package/docs/adr/0017-ui-assumes-a-fixed-desktop-layout.md +54 -0
- package/docs/adr/{0012-modal-header-only-names-the-surface.md → 0018-modal-header-only-names-the-surface.md} +4 -1
- package/docs/adr/0019-productive-surfaces-use-compact-density.md +65 -0
- package/docs/code-style.md +2 -2
- package/docs/consumer-upgrade-propagation.md +1 -1
- package/docs/data-products.md +5 -3
- package/docs/protocol.md +6 -6
- package/docs/relative-unit-scale.md +9 -2
- package/docs/releasing.md +28 -4
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/references/evaluations.md +3 -1
- package/registry/skills/build-opus-ui/references/ui-patterns.md +20 -4
- package/src/auth/drivers/jwt.ts +2 -1
- package/src/core/runtime.ts +32 -5
- package/src/core/types.ts +16 -7
- package/src/mcp/index.ts +13 -1
- package/src/ui/components/patterns/action-list-dialog.tsx +2 -2
- package/src/ui/components/patterns/content-header.tsx +2 -2
- package/src/ui/components/patterns/form-dialog.tsx +7 -2
- package/src/ui/components/patterns/form.tsx +1 -1
- package/src/ui/components/patterns/list.tsx +239 -47
- package/src/ui/components/patterns/presentation.tsx +7 -5
- package/src/ui/components/patterns/sidebar.tsx +1 -1
- package/src/ui/components/patterns/state-surface.tsx +2 -2
- package/src/ui/components/patterns/surface-header.tsx +4 -4
- package/src/ui/components/primitives/alert.tsx +2 -2
- package/src/ui/components/primitives/breadcrumb.tsx +1 -1
- package/src/ui/components/primitives/button-group.tsx +1 -1
- package/src/ui/components/primitives/button.tsx +3 -3
- package/src/ui/components/primitives/calendar.tsx +1 -1
- package/src/ui/components/primitives/card.tsx +1 -1
- package/src/ui/components/primitives/close-button.tsx +40 -0
- package/src/ui/components/primitives/detail.tsx +68 -30
- package/src/ui/components/primitives/dialog.tsx +36 -21
- package/src/ui/components/primitives/drawer.tsx +26 -19
- package/src/ui/components/primitives/empty-value.tsx +3 -3
- package/src/ui/components/primitives/empty.tsx +1 -1
- package/src/ui/components/primitives/field.tsx +12 -12
- package/src/ui/components/primitives/icon-picker.tsx +1 -1
- package/src/ui/components/primitives/input-group.tsx +1 -1
- package/src/ui/components/primitives/input.tsx +2 -2
- package/src/ui/components/primitives/item.tsx +5 -5
- package/src/ui/components/primitives/pagination.tsx +4 -4
- package/src/ui/components/primitives/radio-group.tsx +1 -1
- package/src/ui/components/primitives/select.tsx +3 -3
- package/src/ui/components/primitives/table.tsx +26 -17
- package/src/ui/components/primitives/tabs.tsx +80 -23
- package/src/ui/components/primitives/textarea.tsx +1 -1
- package/src/ui/components/primitives/toggle-group.tsx +9 -2
- package/src/ui/docs/content/action-form-dialog.md +11 -4
- package/src/ui/docs/content/action-form.md +13 -3
- package/src/ui/docs/content/action-list-dialog.md +5 -7
- package/src/ui/docs/content/action-list.md +53 -5
- package/src/ui/docs/content/action-trigger.md +9 -5
- package/src/ui/docs/content/action-view.md +12 -8
- package/src/ui/docs/content/actions.md +36 -13
- package/src/ui/docs/content/ai.md +26 -7
- package/src/ui/docs/content/alert.md +6 -3
- package/src/ui/docs/content/aspect-ratio.md +2 -2
- package/src/ui/docs/content/auth.md +25 -10
- package/src/ui/docs/content/avatar.md +1 -1
- package/src/ui/docs/content/badge.md +2 -2
- package/src/ui/docs/content/breadcrumb.md +3 -2
- package/src/ui/docs/content/button.md +33 -8
- package/src/ui/docs/content/calendar.md +1 -1
- package/src/ui/docs/content/card.md +1 -1
- package/src/ui/docs/content/carousel.md +14 -3
- package/src/ui/docs/content/chat.md +1 -1
- package/src/ui/docs/content/cli.md +13 -7
- package/src/ui/docs/content/command.md +34 -2
- package/src/ui/docs/content/composer.md +1 -1
- package/src/ui/docs/content/content.md +5 -4
- package/src/ui/docs/content/customization.md +12 -2
- package/src/ui/docs/content/cycle.md +7 -5
- package/src/ui/docs/content/data-state.md +6 -5
- package/src/ui/docs/content/data.md +3 -3
- package/src/ui/docs/content/detail.md +12 -10
- package/src/ui/docs/content/dialog.md +14 -7
- package/src/ui/docs/content/dictionary-value.md +1 -1
- package/src/ui/docs/content/dock.md +23 -2
- package/src/ui/docs/content/dot.md +0 -2
- package/src/ui/docs/content/drawer.md +7 -4
- package/src/ui/docs/content/empty-value.md +4 -4
- package/src/ui/docs/content/empty.md +1 -4
- package/src/ui/docs/content/events.md +1 -1
- package/src/ui/docs/content/field.md +21 -12
- package/src/ui/docs/content/getting-started.md +4 -2
- package/src/ui/docs/content/icon-picker.md +2 -2
- package/src/ui/docs/content/input-otp.md +2 -0
- package/src/ui/docs/content/input.md +2 -3
- package/src/ui/docs/content/item.md +6 -3
- package/src/ui/docs/content/kbd.md +2 -1
- package/src/ui/docs/content/mcp.md +10 -4
- package/src/ui/docs/content/menu.md +27 -0
- package/src/ui/docs/content/page.md +20 -6
- package/src/ui/docs/content/pagination.md +9 -2
- package/src/ui/docs/content/popover.md +2 -2
- package/src/ui/docs/content/presentation.md +48 -47
- package/src/ui/docs/content/progress.md +2 -6
- package/src/ui/docs/content/runtime.md +8 -5
- package/src/ui/docs/content/scheduler.md +1 -1
- package/src/ui/docs/content/select.md +13 -8
- package/src/ui/docs/content/sidebar.md +3 -2
- package/src/ui/docs/content/skeleton.md +1 -1
- package/src/ui/docs/content/slider.md +4 -4
- package/src/ui/docs/content/spinner.md +3 -3
- package/src/ui/docs/content/tabs.md +22 -12
- package/src/ui/docs/content/testing.md +4 -2
- package/src/ui/docs/content/toast.md +5 -6
- package/src/ui/docs/content/toggle.md +37 -0
- package/src/ui/docs/content/tokens.md +45 -2
- package/src/ui/docs/content/tooltip.md +4 -3
- package/src/ui/docs/content/truncate.md +3 -2
- package/src/ui/docs/content/ui.md +3 -1
- package/src/ui/docs/content/upgrading.md +43 -13
- package/src/ui/docs/doc-client.tsx +1 -1
- package/src/ui/docs/registry.tsx +30 -5
- package/src/ui/meta.ts +4 -4
- package/src/ui/react.tsx +1 -0
- package/src/ui/theme.css +3 -0
|
@@ -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
|
|
60
|
-
|
|
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.
|
|
@@ -70,6 +73,11 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
|
|
|
70
73
|
- `ActionFilterBar` renderiza busca, filtros, período e ações declarados por uma list action
|
|
71
74
|
quando a tela precisa da toolbar sem entregar os resultados a `ActionList`. Manter seu
|
|
72
75
|
`state` e `onStateChange` ligados à mesma projeção de URL usada pela superfície.
|
|
76
|
+
- Filtros de list action declaram onde aparecem com `placement`: `inline` fica na barra;
|
|
77
|
+
`advanced` vai para o painel de filtros avançados, agrupado por `section` e distribuído em até
|
|
78
|
+
três colunas com `advancedFilters.columns`; `external` continua no estado navegável e no input,
|
|
79
|
+
mas a tela apresenta o controle por conta própria. `advanced: true` segue aceito só durante a
|
|
80
|
+
migração; em código novo, declarar `placement`.
|
|
73
81
|
- `ActionForm` mantém validação, execução e estados do contrato nos dois modos: sem `children`,
|
|
74
82
|
renderiza os campos declarados; com `children`, o consumidor diagrama `ActionFormField` e
|
|
75
83
|
controles customizados pelo contexto. `ActionFormCard` e `ActionFormDialog` acrescentam a
|
|
@@ -78,8 +86,13 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
|
|
|
78
86
|
a ação principal mantém `solid`. Criação e edição comuns preservam o rótulo padrão `Salvar`;
|
|
79
87
|
`submitLabel` fica para efeitos específicos, como `Renomear` ou `Criar nova versão`. Em
|
|
80
88
|
`Dialog mode="alert"`, a saída segura também usa `outline`.
|
|
89
|
+
- `DialogFooter` e `DrawerFooter` cuidam só da faixa. Agrupar e distribuir as decisões cabe a um
|
|
90
|
+
`ButtonGroup` dentro deles, com `distribution="equal"` para o footer 50/50; botões soltos no
|
|
91
|
+
footer não são distribuídos. `ActionFormDialog` já compõe isso: por padrão o footer acompanha o
|
|
92
|
+
conteúdo com o cancelamento em `ghost`, e `footerDistribution="equal"` divide a faixa e passa o
|
|
93
|
+
cancelamento a `outline`.
|
|
81
94
|
- Cabeçalhos de `Dialog` e `Drawer` nomeiam a superfície com o título e organizam suas ações. O close
|
|
82
|
-
padrão é
|
|
95
|
+
padrão é o `CloseButton` (`neutral`, `subtle`, pílula) no final do header, sem posição flutuante
|
|
83
96
|
alternativa. Não preencher uma segunda linha por hábito. Consequência, restrição ou instrução que
|
|
84
97
|
realmente mude a tarefa entra no início de `DialogBody`/`DrawerBody`, como texto ou `Alert`;
|
|
85
98
|
wrappers usam `intro` e a API imperativa usa `body`. Relacione texto conciso por
|
|
@@ -90,7 +103,10 @@ ContentDescription, ContentMeta, ContentActions) + ContentBody`. `ContentHeader`
|
|
|
90
103
|
ou views nomeadas. Usar `ActionFilterBar` isoladamente só quando outra superfície assumir a
|
|
91
104
|
renderização dos resultados. Colunas numéricas não recebem alinhamento ou tipografia por
|
|
92
105
|
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.
|
|
106
|
+
modo que números preservem o alinhamento natural por padrão. O valor principal de uma célula
|
|
107
|
+
preserva `text-sm` e o foreground da superfície; `text-xs text-muted-foreground` fica restrito
|
|
108
|
+
a metadado subordinado a outro valor na mesma célula. Cabeçalhos e `EmptyValue` continuam muted;
|
|
109
|
+
não reduzir nem atenuar uma coluna inteira por ela ser técnica, temporal ou menos destacada.
|
|
94
110
|
- `ItemGroup` representa uma coleção e pode oferecer moldura explícita; `Item` já carrega
|
|
95
111
|
semântica de item de lista. Preferir essa composição a uma sequência visual sem estrutura.
|
|
96
112
|
- `Empty` com moldura tracejada comunica uma região disponível para criar ou vincular algo.
|
package/src/auth/drivers/jwt.ts
CHANGED
|
@@ -34,7 +34,8 @@ import type { AuthAdapter, CanFn, User } from '../../core/index.ts'
|
|
|
34
34
|
// =============================================================================
|
|
35
35
|
|
|
36
36
|
export interface JwtAuthOptions<P extends JwtPayload = JwtPayload> {
|
|
37
|
-
/** Secret HMAC ou public key
|
|
37
|
+
/** Secret HMAC ou public key, fixo ou por resolver (sync ou async). O resolver é chamado
|
|
38
|
+
* sem argumentos: a escolha da chave pelo `kid` do token ainda não é suportada. */
|
|
38
39
|
secret: string | Buffer | ((kid?: string) => Promise<string | Buffer> | string | Buffer)
|
|
39
40
|
|
|
40
41
|
/** Algorithms aceitos (default ['HS256']). */
|
package/src/core/runtime.ts
CHANGED
|
@@ -75,6 +75,23 @@ import type {
|
|
|
75
75
|
|
|
76
76
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Erro devolvido a um cliente de IA que chama uma tool fora do conjunto exposto.
|
|
80
|
+
*
|
|
81
|
+
* Quem executa tools (o agente co-locado e o servidor MCP) só pode rodar o que `aiTools()`
|
|
82
|
+
* anunciou. O modelo, ou um prompt injetado, pode emitir qualquer nome; sem este corte, uma
|
|
83
|
+
* action sem `ai.enabled` — inclusive destrutiva — rodaria dentro da autorização do contexto.
|
|
84
|
+
* A resposta é a mesma para nome inexistente e para action registrada mas não exposta, para
|
|
85
|
+
* que o cliente não descubra quais actions existem.
|
|
86
|
+
*/
|
|
87
|
+
export function aiToolNotFound(name: string): ReturnType<typeof error> {
|
|
88
|
+
return error({
|
|
89
|
+
code: 'runtime.action_not_found',
|
|
90
|
+
category: 'not_found',
|
|
91
|
+
message: `Tool "${name}" não está disponível.`,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
78
95
|
/** Normaliza o `ai` da action (`boolean | AIConfig`) → config, ou null se não exposta à IA. */
|
|
79
96
|
function normalizeAiConfig(ai: boolean | AIConfig | undefined): AIConfig | null {
|
|
80
97
|
if (ai === undefined || ai === false) return null
|
|
@@ -620,10 +637,10 @@ export class Runtime {
|
|
|
620
637
|
// — 1. Validate input ————————————————————————————————————————————————
|
|
621
638
|
const validatedInput = await this.validate(action.input, input, 'input')
|
|
622
639
|
|
|
623
|
-
// — 2.
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
//
|
|
640
|
+
// — 2. Authenticate (public-or-user) ——————————————————————————————————
|
|
641
|
+
// Antes dos loaders: eles consultam dados, e um chamador anônimo não pode nem disparar essas
|
|
642
|
+
// consultas nem distinguir, pelo erro de um loader, se um registro existe. A autenticação
|
|
643
|
+
// não depende de nada carregado; a autorização, sim, e por isso vem depois.
|
|
627
644
|
if (action.public !== true && ctx.user === null) {
|
|
628
645
|
throw error({
|
|
629
646
|
code: 'auth.unauthenticated',
|
|
@@ -632,6 +649,9 @@ export class Runtime {
|
|
|
632
649
|
})
|
|
633
650
|
}
|
|
634
651
|
|
|
652
|
+
// — 3. Load —————————————————————————————————————————————————————————
|
|
653
|
+
const loaded = await this.runLoaders(action, ctx, validatedInput)
|
|
654
|
+
|
|
635
655
|
// — 4. Authorize ——————————————————————————————————————————————————————
|
|
636
656
|
if (action.authorize !== undefined) {
|
|
637
657
|
const authorizeFn = this.compileAuthorize(action.authorize)
|
|
@@ -815,6 +835,7 @@ export class Runtime {
|
|
|
815
835
|
for (const action of this.actions.values()) {
|
|
816
836
|
const cfg = normalizeAiConfig(action.ai)
|
|
817
837
|
if (cfg === null) continue
|
|
838
|
+
const dataProducts = [...(this.actionDataProducts.get(action.name) ?? [])].sort()
|
|
818
839
|
tools.push({
|
|
819
840
|
name: action.name,
|
|
820
841
|
...(action.label !== undefined
|
|
@@ -822,7 +843,12 @@ export class Runtime {
|
|
|
822
843
|
: {}),
|
|
823
844
|
description: cfg.description ?? action.description ?? action.name,
|
|
824
845
|
inputSchema: action.input,
|
|
825
|
-
metadata: {
|
|
846
|
+
metadata: {
|
|
847
|
+
dataProducts,
|
|
848
|
+
dataProductLabels: Object.fromEntries(
|
|
849
|
+
dataProducts.map((id) => [id, this.dataProducts.get(id)?.label ?? id]),
|
|
850
|
+
),
|
|
851
|
+
},
|
|
826
852
|
})
|
|
827
853
|
}
|
|
828
854
|
this.aiToolsCache = tools
|
|
@@ -842,6 +868,7 @@ export class Runtime {
|
|
|
842
868
|
...opts,
|
|
843
869
|
tools,
|
|
844
870
|
execute: async (name, toolInput) => {
|
|
871
|
+
if (!tools.some((tool) => tool.name === name)) return { error: aiToolNotFound(name) }
|
|
845
872
|
const cfg = normalizeAiConfig(this.actions.get(name)?.ai)
|
|
846
873
|
if (cfg !== null && (cfg.destructive === true || cfg.requiresConfirmation === true)) {
|
|
847
874
|
const approved = opts?.confirm ? await opts.confirm({ name, input: toolInput }) : false
|
package/src/core/types.ts
CHANGED
|
@@ -602,8 +602,12 @@ export interface FilterSpec {
|
|
|
602
602
|
operators?: FilterOp[]
|
|
603
603
|
path?: string
|
|
604
604
|
|
|
605
|
-
/**
|
|
606
|
-
*
|
|
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
|
|
@@ -616,9 +620,9 @@ export interface FilterSpec {
|
|
|
616
620
|
|
|
617
621
|
/**
|
|
618
622
|
* Coluna declarativa de uma `ListAction` — a UI (ActionList) deriva a tabela
|
|
619
|
-
* daqui; células custom entram POR CIMA na UI (prop `cells`, chave = key).
|
|
620
|
-
*
|
|
621
|
-
*
|
|
623
|
+
* daqui; células custom entram POR CIMA na UI (prop `cells`, chave = key). O seletor
|
|
624
|
+
* de colunas parte desta lista: ela vive completa no contrato, e `hidden` marca as que
|
|
625
|
+
* nascem fora.
|
|
622
626
|
*/
|
|
623
627
|
export interface ListColumnSpec {
|
|
624
628
|
/** Chave do item (`Out`) que a coluna mostra. */
|
|
@@ -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?: {
|
|
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). */
|
|
@@ -1376,7 +1385,7 @@ export interface ScheduleDef {
|
|
|
1376
1385
|
cron?: string
|
|
1377
1386
|
/** Shorthand interval — ex: '1h', '30m', '15s'. */
|
|
1378
1387
|
every?: string
|
|
1379
|
-
/** Timezone IANA — ex: 'America/Sao_Paulo'.
|
|
1388
|
+
/** Timezone IANA — ex: 'America/Sao_Paulo'. Sem ela, vale a hora local do processo. */
|
|
1380
1389
|
timezone?: string
|
|
1381
1390
|
|
|
1382
1391
|
/** Input passado ao runtime.execute(); estático ou dinâmico. */
|
package/src/mcp/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
|
|
|
14
14
|
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
15
15
|
import type { ContextBase, Runtime } from '../core/index.ts'
|
|
16
16
|
import { readPackageVersion } from '../core/package-version.ts'
|
|
17
|
+
import { aiToolNotFound } from '../core/runtime.ts'
|
|
17
18
|
|
|
18
19
|
export interface OpusMcpOptions {
|
|
19
20
|
name?: string
|
|
@@ -47,11 +48,22 @@ export function createOpusMcpServer(runtime: Runtime, opts: OpusMcpOptions = {})
|
|
|
47
48
|
...(t.title !== undefined ? { title: t.title } : {}),
|
|
48
49
|
description: t.description,
|
|
49
50
|
inputSchema: toJsonSchema(t.inputSchema) as { type: 'object' },
|
|
50
|
-
_meta: {
|
|
51
|
+
_meta: {
|
|
52
|
+
'com.softize.opus/data-products': t.metadata?.dataProducts ?? [],
|
|
53
|
+
'com.softize.opus/data-product-labels': t.metadata?.dataProductLabels ?? {},
|
|
54
|
+
},
|
|
51
55
|
})),
|
|
52
56
|
}))
|
|
53
57
|
|
|
54
58
|
server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
|
|
59
|
+
// Executa só o que ListTools anunciou. Sem este corte, qualquer action registrada — sem
|
|
60
|
+
// `ai.enabled`, inclusive destrutiva — rodaria dentro da autorização do contexto.
|
|
61
|
+
if (!runtime.aiTools().some((tool) => tool.name === req.params.name)) {
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: 'text', text: JSON.stringify({ error: aiToolNotFound(req.params.name) }) }],
|
|
64
|
+
isError: true,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
55
67
|
const base = opts.resolveContext ? await opts.resolveContext(extra) : ANON
|
|
56
68
|
const result = await runtime.execute(req.params.name, req.params.arguments ?? {}, base)
|
|
57
69
|
const payload = result.ok ? result.data : { error: result.error }
|
|
@@ -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:
|
|
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('
|
|
81
|
+
className={cn('max-w-3xl', className)}
|
|
82
82
|
aria-describedby={intro === undefined ? undefined : introId}
|
|
83
83
|
>
|
|
84
84
|
<DialogHeader>
|
|
@@ -50,7 +50,7 @@ interface ContentBaseProps extends Omit<
|
|
|
50
50
|
|
|
51
51
|
interface ContentShorthandProps extends ContentBaseProps {
|
|
52
52
|
title: ReactNode;
|
|
53
|
-
/** Total de itens ao lado do título
|
|
53
|
+
/** Total de itens ao lado do título da seção. A página não carrega contador (ADR 0009). */
|
|
54
54
|
count?: number;
|
|
55
55
|
description?: ReactNode;
|
|
56
56
|
actions?: ReactNode;
|
|
@@ -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
|
|
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=
|
|
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={
|
|
88
|
+
cancelVariant={
|
|
89
|
+
cancelVariant ?? (footerDistribution === "equal" ? "outline" : "ghost")
|
|
90
|
+
}
|
|
86
91
|
/>
|
|
87
92
|
</DialogContent>
|
|
88
93
|
</Dialog>
|
|
@@ -702,7 +702,7 @@ export interface ActionFormProps<
|
|
|
702
702
|
onSuccess?: (data: TData) => void;
|
|
703
703
|
submitLabel?: string;
|
|
704
704
|
cancelLabel?: string;
|
|
705
|
-
/** Tratamento visual do cancelamento.
|
|
705
|
+
/** Tratamento visual do cancelamento. `ghost` por padrão; `outline` quando o footer distribui as ações por igual. */
|
|
706
706
|
cancelVariant?: Extract<ButtonVariant, "ghost" | "outline">;
|
|
707
707
|
onCancel?: () => void;
|
|
708
708
|
/** Bloqueia campos e ações sem desmontar o formulário. */
|