@softize/opus 16.0.0 → 17.0.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 (31) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/bin/lib/check.mjs +9 -13
  3. package/bin/lib/copy.mjs +29 -4
  4. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +6 -3
  5. package/docs/adr/0009-page-title-does-not-carry-a-counter.md +5 -2
  6. package/docs/adr/0011-page-shell-coordinates-persistent-page-chrome.md +12 -14
  7. package/docs/adr/0012-modal-header-only-names-the-surface.md +8 -4
  8. package/docs/adr/0013-presentation-is-a-portable-action-oriented-artifact.md +7 -4
  9. package/docs/adr/0014-structural-headers-do-not-carry-description.md +35 -0
  10. package/package.json +2 -2
  11. package/registry/skills/build-opus-ui/references/evaluations.md +1 -1
  12. package/registry/skills/build-opus-ui/references/ui-patterns.md +16 -17
  13. package/registry/templates/app/package.json +1 -1
  14. package/src/core/presentation.ts +142 -13
  15. package/src/ui/components/patterns/form.tsx +29 -10
  16. package/src/ui/components/patterns/page.tsx +93 -48
  17. package/src/ui/components/patterns/presentation.tsx +487 -134
  18. package/src/ui/components/patterns/surface-header.tsx +4 -3
  19. package/src/ui/components/patterns/trigger.tsx +8 -1
  20. package/src/ui/components/patterns/view.tsx +2 -2
  21. package/src/ui/components/primitives/command.tsx +82 -42
  22. package/src/ui/components/primitives/dialog.tsx +180 -97
  23. package/src/ui/components/primitives/drawer.tsx +63 -22
  24. package/src/ui/docs/content/command.md +3 -1
  25. package/src/ui/docs/content/content.md +1 -1
  26. package/src/ui/docs/content/dialog.md +9 -9
  27. package/src/ui/docs/content/drawer.md +4 -4
  28. package/src/ui/docs/content/page.md +14 -29
  29. package/src/ui/docs/content/presentation.md +54 -46
  30. package/src/ui/meta.ts +2 -2
  31. package/src/ui/react.tsx +1 -2
@@ -2,17 +2,16 @@
2
2
 
3
3
  Use `Page` para manter título, ações, estado e conteúdo na mesma anatomia. Sozinha, ela apresenta
4
4
  `PageHeader` dentro do container. Quando a aplicação possui uma barra persistente, envolva a rota
5
- com `PageShell`: o shell fornece a navegação, a página fornece as ações e o título passa a
6
- `PageIntro` dentro do conteúdo.
5
+ com `PageShell`: o shell fornece a navegação e a página projeta título e ações na barra.
7
6
 
8
7
  `PageHeader` mantém navegação, título e ações na mesma linha. O container é centralizado e ocupa a
9
8
  largura disponível até `80rem` (`max-w-7xl`). Use `className` somente quando a composição pedir
10
9
  outro teto ou largura total.
11
10
 
12
11
  A forma curta é o padrão para páginas comuns. Fora de `PageShell`, ela cria `PageHeader` e
13
- `PageBody`. Dentro dele, cria `PageIntro` e `PageBody`, enquanto envia as ações para a barra. A forma
14
- explícita permite escolher a região adequada: `PageHeader` reúne navegação e contexto; `PageIntro`
15
- dá mais presença ao título dentro do conteúdo. Contadores e outros indicadores pertencem ao
12
+ `PageBody`. Dentro dele, cria `PageHeader` e `PageBody`, projetando o header na barra. A forma
13
+ explícita permite acrescentar um `PageIntro` opcional no conteúdo sem duplicar o título estrutural.
14
+ Contadores e outros indicadores pertencem ao
16
15
  conteúdo que os explica.
17
16
 
18
17
  `PageShell` não inclui sidebar nem inventa breadcrumb. Ele ocupa o painel principal já delimitado e
@@ -28,7 +27,6 @@ render(
28
27
  <div className="w-full overflow-hidden rounded-lg border border-border">
29
28
  <Page
30
29
  title="Workspaces"
31
- description="Ambientes compartilhados pela equipe."
32
30
  actions={
33
31
  <Button>
34
32
  <Plus /> Novo workspace
@@ -54,7 +52,6 @@ controle aparece antes do título como ícone com nome acessível e tooltip deri
54
52
  <PageHeader>
55
53
  <PageBack href="/customers">Clientes</PageBack>
56
54
  <PageTitle>Qualidade da base</PageTitle>
57
- <PageDescription>Revise conflitos e canais de contato.</PageDescription>
58
55
  </PageHeader>
59
56
  <PageBody>Conteúdo da análise.</PageBody>
60
57
  </Page>
@@ -67,8 +64,8 @@ posição introdutória sem transformar a trilha em ação.
67
64
  ## Barra persistente do shell
68
65
 
69
66
  Use `PageShell` quando o shell conhece o breadcrumb e a rota conhece título e ações. Não crie um
70
- `PaneHeader` paralelo nem esconda slots de `Page` com CSS. O Opus projeta `PageActions` na barra e
71
- transforma a introdução da forma curta em `PageIntro`.
67
+ `PaneHeader` paralelo nem esconda slots de `Page` com CSS. O Opus projeta `PageTitle` e
68
+ `PageActions` na barra.
72
69
 
73
70
  ```tsx preview col
74
71
  <PageShell
@@ -95,21 +92,20 @@ transforma a introdução da forma curta em `PageIntro`.
95
92
  </PageShell>
96
93
  ```
97
94
 
98
- A barra permanece visível durante `PageState`; somente a introdução desaparece. Se uma ação não
95
+ A barra permanece visível durante `PageState`; uma introdução opcional desaparece. Se uma ação não
99
96
  puder ser executada sem o conteúdo, a própria rota deve omiti-la naquele estado.
100
97
 
101
- Na composição explícita dentro do shell, use `PageIntro` e `PageBody`:
98
+ Na composição explícita dentro do shell, use `PageHeader` e `PageBody`:
102
99
 
103
100
  ```tsx preview col
104
101
  <PageShell navigation={<Breadcrumb>...</Breadcrumb>}>
105
102
  <Page>
106
- <PageIntro>
103
+ <PageHeader>
107
104
  <PageTitle>Clientes</PageTitle>
108
- <PageDescription>Cadastros disponíveis para atendimento.</PageDescription>
109
105
  <PageActions>
110
106
  <Button size="sm">Novo cliente</Button>
111
107
  </PageActions>
112
- </PageIntro>
108
+ </PageHeader>
113
109
  <PageBody>Conteúdo da listagem.</PageBody>
114
110
  </Page>
115
111
  </PageShell>
@@ -125,7 +121,6 @@ render(
125
121
  <Page>
126
122
  <PageHeader>
127
123
  <PageTitle>Workspaces</PageTitle>
128
- <PageDescription>Ambientes compartilhados pela equipe.</PageDescription>
129
124
  <PageActions>
130
125
  <Button>
131
126
  <Plus /> Novo workspace
@@ -141,9 +136,8 @@ render(
141
136
  );
142
137
  ```
143
138
 
144
- Use `PageIntro` no lugar de `PageHeader` quando a página precisar apenas de uma introdução no
145
- conteúdo, sem navegação própria. Dentro de `PageShell`, essa é a única composição explícita válida;
146
- fora dele, as duas formas são aceitas porque cumprem papéis diferentes.
139
+ Use `PageIntro` somente quando o conteúdo precisar de uma introdução adicional. Ele não substitui
140
+ o `PageHeader`, não repete o título da página e pode ser omitido.
147
141
 
148
142
  ## Estados integrais
149
143
 
@@ -195,7 +189,6 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
195
189
  | Propriedade | Tipo | Padrão | Descrição |
196
190
  | ------------- | ----------- | ----------- | ----------------------------------------------------------------------------------------------- |
197
191
  | `title` | `ReactNode` | | O h1 da página. |
198
- | `description` | `ReactNode` | | Linha de contexto sob o título (ex.: "N no total · X ativos"). |
199
192
  | `actions` | `ReactNode` | | Ações contextuais no extremo oposto do cabeçalho; em telas estreitas, ficam abaixo do contexto. |
200
193
  | `className` | `string` | `max-w-7xl` | Classes do container para substituir o teto padrão de `80rem`. |
201
194
  | `children` | `ReactNode` | | O body da página — espaçamento e diagramação são seus. |
@@ -215,7 +208,7 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
215
208
 
216
209
  | Propriedade | Tipo | Descrição |
217
210
  | ----------- | ----------------------------- | ------------------------------------------------------------- |
218
- | `children` | `ReactNode` | Um `PageTitle` e, opcionalmente, `PageBack` ou `PageNavigation`, descrição e ações. |
211
+ | `children` | `ReactNode` | Conteúdo introdutório opcional do body. |
219
212
  | `className` | `string` | Classes adicionais da região introdutória. |
220
213
  | demais | Atributos de `HTMLDivElement` | Atributos nativos repassados à região introdutória. |
221
214
 
@@ -224,7 +217,7 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
224
217
  | Propriedade | Tipo | Descrição |
225
218
  | ----------- | ----------- | ----------------------------------------------------------------------------------- |
226
219
  | `className` | `string` | Classes adicionais da região externa do cabeçalho. |
227
- | `children` | `ReactNode` | Um `PageTitle` e, opcionalmente, `PageBack` ou `PageNavigation`, descrição e ações. |
220
+ | `children` | `ReactNode` | Um `PageTitle` e, opcionalmente, `PageBack` ou `PageNavigation` e ações. |
228
221
 
229
222
  ## Propriedades de PageBack
230
223
 
@@ -252,14 +245,6 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
252
245
  | `className` | `string` | Classes adicionais do título. |
253
246
  | demais | Atributos de `HTMLHeadingElement` | Atributos nativos repassados ao heading. |
254
247
 
255
- ## Propriedades de PageDescription
256
-
257
- | Propriedade | Tipo | Descrição |
258
- | ----------- | ----------------------------------- | ------------------------------------------ |
259
- | `children` | `ReactNode` | Contexto apresentado abaixo do título. |
260
- | `className` | `string` | Classes adicionais da descrição. |
261
- | demais | Atributos de `HTMLParagraphElement` | Atributos nativos repassados ao parágrafo. |
262
-
263
248
  ## Propriedades de PageActions
264
249
 
265
250
  | Propriedade | Tipo | Descrição |
@@ -1,4 +1,4 @@
1
- `Presentation` preserva a identidade de um recurso quando a experiência pede uma página, um dialog ou
1
+ `Presentation` preserva a identidade de um recurso quando o contexto pede uma página, um dialog ou
2
2
  um drawer. A superfície decide quanto espaço ocupar; o recurso continua com a mesma navegação,
3
3
  cabeçalho, body e rodapé.
4
4
 
@@ -11,15 +11,38 @@ Use esse padrão quando uma lista puder abrir um detalhe lateral, quando a mesma
11
11
  funcionar em modal e em rota própria ou quando um fluxo começar compacto e crescer sem ganhar uma
12
12
  segunda implementação.
13
13
 
14
- Dentro de `PageShell`, a superfície `page` usa a anatomia real da aplicação: o shell preserva sua
15
- barra, e a Presentation compõe `Page`, `PageIntro`, `PageBody` e, quando necessário, `PageFooter`.
14
+ Dentro de `PageShell`, a superfície `page` usa a anatomia real da aplicação: a Presentation projeta
15
+ navigation, title e actions no header da barra e compõe `PageBody` e, quando necessário, `PageFooter`.
16
16
  Fora do shell, ela materializa o `PageHeader` completo para exemplos e superfícies independentes.
17
17
  Não envolva a Presentation em um card para simular a página; valide proporção, rolagem e ações no
18
18
  shell que efetivamente hospeda a rota.
19
19
 
20
20
  ```tsx live
21
+ const workspaceUpdate = defineContract({
22
+ name: "workspace.update",
23
+ kind: "form",
24
+ label: "Salvar workspace",
25
+ input: z.object({ name: z.string() }),
26
+ output: z.object({ id: z.string() }),
27
+ fields: { name: { label: "Nome" } },
28
+ });
29
+
30
+ const workspacePresentation = definePresentation({
31
+ schemaVersion: 1,
32
+ id: "workspace.update",
33
+ title: "Workspace",
34
+ body: { action: workspaceUpdate.name },
35
+ });
36
+
21
37
  function Example() {
22
- const [surface, setSurface] = React.useState("page");
38
+ const [invocation, setInvocation] = React.useState(() =>
39
+ definePresentationInvocation({
40
+ schemaVersion: 1,
41
+ presentationId: workspacePresentation.id,
42
+ surface: "page",
43
+ input: {},
44
+ }),
45
+ );
23
46
 
24
47
  return (
25
48
  <div className="space-y-4">
@@ -28,8 +51,8 @@ function Example() {
28
51
  <Button
29
52
  key={value}
30
53
  size="sm"
31
- variant={surface === value ? "solid" : "outline"}
32
- onClick={() => setSurface(value)}
54
+ variant={invocation.surface === value ? "solid" : "outline"}
55
+ onClick={() => setInvocation({ ...invocation, surface: value })}
33
56
  >
34
57
  {value}
35
58
  </Button>
@@ -37,25 +60,12 @@ function Example() {
37
60
  </ButtonGroup>
38
61
 
39
62
  <Presentation
40
- surface={surface}
41
- title="Workspace"
42
- headerActions={
43
- <Button size="sm" variant="ghost">
44
- Arquivar
45
- </Button>
46
- }
47
- footerActions={
48
- <>
49
- <Button variant="outline">Cancelar</Button>
50
- <Button context="primary">Salvar</Button>
51
- </>
52
- }
53
- >
54
- <Content title="Dados gerais">
55
- O renderer injeta aqui o formulário, a lista ou a visualização ligada
56
- à action.
57
- </Content>
58
- </Presentation>
63
+ definition={workspacePresentation}
64
+ definitions={[workspacePresentation]}
65
+ actions={{ [workspaceUpdate.name]: workspaceUpdate }}
66
+ invocation={invocation}
67
+ onInvocationChange={(next) => next && setInvocation(next)}
68
+ />
59
69
  </div>
60
70
  );
61
71
  }
@@ -66,7 +76,9 @@ render(<Example />);
66
76
  ## A declaração
67
77
 
68
78
  `definePresentation` cria o artefato serializável. O body recebe uma action `form`, `list` ou
69
- `view`; uma action `simple` aparece como comando no cabeçalho ou no rodapé. Actions que precisam de
79
+ `view`; uma action `simple` aparece como comando no cabeçalho ou no rodapé. Em formulário,
80
+ `body.submitLabel` nomeia a ação conforme o resultado esperado, como “Criar departamento” ou
81
+ “Salvar alterações”. Actions que precisam de
70
82
  interface abrem outra Presentation. Bindings fornecem valores vindos da rota, do registro, da
71
83
  seleção, da sessão ou do resultado anterior sem guardar callbacks no JSON.
72
84
 
@@ -121,28 +133,25 @@ refresh, deep link ou histórico.
121
133
 
122
134
  `PresentationInspector` renderiza um botão somente com ícone. Ao acioná-lo, reúne definição,
123
135
  invocação, bindings resolvidos e diagnósticos em um único JSON e oferece a cópia do conteúdo. Chaves
124
- sensíveis são mascaradas. Use a ferramenta em ambientes de desenvolvimento ou no Studio; a Lens
125
- expõe somente a definição estática e a ferramenta não faz parte da navegação do recurso para a
126
- pessoa usuária.
127
-
128
- Use `floating` quando o inspetor precisar ficar disponível sem ocupar o cabeçalho. O componente
129
- aplica tamanho, forma, borda e elevação de FAB; `className` define a âncora na superfície consumidora.
130
- Reserve espaço para outros controles flutuantes do shell em vez de sobrepô-los.
136
+ sensíveis são mascaradas. Use a ferramenta em uma bancada de desenvolvimento ou na Lens; ela não
137
+ faz parte da navegação do recurso para a pessoa usuária. Na aplicação final, publique as definições
138
+ no manifest e concentre a inspeção na Lens em vez de criar um launcher flutuante no shell.
131
139
 
132
140
  ## Propriedades de Presentation
133
141
 
134
- | Propriedade | Tipo | Padrão | Descrição |
135
- | --------------- | -------------------------------- | ------ | --------------------------------------------------------------------- |
136
- | `surface` | `'page' \| 'dialog' \| 'drawer'` | | Superfície que hospeda o recurso. |
137
- | `title` | `ReactNode` | | Nome da Presentation no cabeçalho. |
138
- | `navigation` | `ReactNode` | | Voltar ou navegação relativa, quando existir. |
139
- | `headerActions` | `ReactNode` | | Comandos contextuais no cabeçalho. |
140
- | `footerActions` | `ReactNode` | | Comandos de progressão; Dialog e Drawer dividem a largura disponível. |
141
- | `children` | `ReactNode` | | Formulário, lista ou visualização do body. |
142
- | `open` | `boolean` | | Estado controlado de Dialog ou Drawer; omitido, começa aberto e fecha internamente. |
143
- | `onOpenChange` | `(open: boolean) => void` | | Notifica abertura e fechamento da superfície modal. |
144
- | `className` | `string` | | Classes adicionais da superfície. |
145
- | `bodyClassName` | `string` | | Classes adicionais do body. |
142
+ | Propriedade | Tipo | Padrão | Descrição |
143
+ | -------------------- | ----------------------------------------- | ------ | ----------------------------------------------------------- |
144
+ | `definition` | `PresentationDefinition` | | Artefato estático que descreve o recurso. |
145
+ | `definitions` | `PresentationDefinition[]` | | Registry alcançável por navegação, abertura e retorno. |
146
+ | `actions` | `PresentationActionRegistry` | | Contratos compartilháveis referenciados pelo artefato. |
147
+ | `invocation` | `PresentationInvocation` | | Surface, input e pilha da exibição atual. |
148
+ | `bindingContext` | `PresentationBindingContext` | | Rota, item, seleção, sessão e resultado disponíveis. |
149
+ | `onInvocationChange` | `(next: PresentationInvocation \| null) => void` | | Recebe navegação, retorno e fechamento. |
150
+ | `onRefresh` | `(action: string \| null) => void` | | Recebe invalidações declaradas após sucesso. |
151
+ | `open` | `boolean` | `true` | Estado controlado de Dialog ou Drawer. |
152
+ | `onOpenChange` | `(open: boolean) => void` | | Notifica abertura e fechamento da superfície modal. |
153
+ | `className` | `string` | | Classes adicionais da superfície. |
154
+ | `bodyClassName` | `string` | | Classes adicionais do body, aplicadas uma única vez. |
146
155
 
147
156
  ## Propriedades de PresentationInspector
148
157
 
@@ -154,5 +163,4 @@ Reserve espaço para outros controles flutuantes do shell em vez de sobrepô-los
154
163
  | `diagnostics` | `PresentationDiagnostic[]` | | Diagnósticos da definição ou execução. |
155
164
  | `triggerLabel` | `string` | `Ver JSON da Presentation` | Nome acessível e tooltip do botão. |
156
165
  | `title` | `string` | `JSON da Presentation` | Título do dialog de inspeção. |
157
- | `floating` | `boolean` | `false` | Apresenta o gatilho com aparência de FAB. |
158
166
  | `className` | `string` | | Classes adicionais do gatilho. |
package/src/ui/meta.ts CHANGED
@@ -400,13 +400,13 @@ export const componentMeta = {
400
400
  name: "page",
401
401
  ancestry: "opus",
402
402
  whenToUse:
403
- "O esqueleto de página do back-office: <main> + container centralizado com teto padrão de 80rem (`max-w-7xl`). O shorthand cobre título, descrição e ações. Quando shell e rota conhecem partes diferentes da página, PageShell mantém a barra de 3rem, recebe a navegação e projeta as ações da Page; título e descrição formam PageIntro no conteúdo. Fora dele, a forma explícita escolhe PageHeader para o cabeçalho completo ou PageIntro para uma introdução sem navegação. PageActionsTarget fica restrito a workspaces imersivos sem PageShell. PageState oculta o header da Page isolada ou somente PageIntro dentro de PageShell. Para uma região disponível à criação ou vínculo, use Empty.",
403
+ "O esqueleto de página do back-office: <main> + container centralizado com teto padrão de 80rem (`max-w-7xl`). O shorthand cobre título e ações. Quando shell e rota conhecem partes diferentes da página, PageShell mantém a barra de 3rem e recebe navegação, título e ações da Page. PageIntro é um bloco opcional de introdução no conteúdo. Fora do shell, a forma explícita usa PageHeader para o cabeçalho completo. PageActionsTarget fica restrito a workspaces imersivos sem PageShell. PageState oculta somente PageIntro; informações importantes entram no corpo como Alert ou texto. Para uma região disponível à criação ou vínculo, use Empty.",
404
404
  },
405
405
  presentation: {
406
406
  name: "presentation",
407
407
  ancestry: "opus",
408
408
  whenToUse:
409
- "Descreva um recurso que precisa manter a mesma anatomia ao aparecer como Page, Dialog ou Drawer. Presentation organiza navegação, título, ações de cabeçalho, body e ações de rodapé sem redefinir o conteúdo para cada superfície. A definição persistente liga esses slots a actions Opus; a invocação carrega o estado JSON da execução. PresentationInspector reúne definição, invocação, bindings resolvidos e diagnósticos com dados sensíveis mascarados.",
409
+ "Descreva um recurso que precisa manter a mesma anatomia ao aparecer como Page, Dialog ou Drawer. Presentation organiza navegação, título, ações de cabeçalho, body e ações de rodapé sem redefinir o conteúdo para cada superfície. A definição persistente liga esses slots a actions Opus; a invocação carrega o estado JSON da execução. PresentationInspector abre um snapshot isolado, enquanto a Lens apresenta o inventário estático projetado no manifest.",
410
410
  },
411
411
  router: {
412
412
  name: "router",
package/src/ui/react.tsx CHANGED
@@ -427,7 +427,7 @@ export type {
427
427
  PresentationInspectorProps,
428
428
  } from "./components/patterns/presentation.tsx";
429
429
 
430
- // Esqueleto de página do back-office (main + container + header título/descrição/ação).
430
+ // Esqueleto de página do back-office (main + container + header com navegação, título e ações).
431
431
  export {
432
432
  PageShell,
433
433
  Page,
@@ -436,7 +436,6 @@ export {
436
436
  PageNavigation,
437
437
  PageBack,
438
438
  PageTitle,
439
- PageDescription,
440
439
  PageActions,
441
440
  PageActionsTarget,
442
441
  PageBody,