@softize/opus 19.0.0 → 20.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.
- package/CHANGELOG.md +193 -0
- package/bin/cli.mjs +5 -0
- package/bin/lib/check.mjs +198 -10
- package/bin/lib/copy.mjs +29 -0
- package/bin/lib/mcp.mjs +1 -1
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +5 -0
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
- package/docs/data-products.md +15 -9
- package/docs/protocol.md +3 -4
- package/docs/releasing.md +2 -2
- package/package.json +2 -2
- package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
- package/registry/templates/app/package.json +1 -1
- package/src/core/runtime.ts +96 -8
- package/src/core/types.ts +5 -2
- package/src/ui/components/patterns/action-form-card.tsx +11 -1
- package/src/ui/components/patterns/content-header.tsx +15 -3
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/list.tsx +89 -78
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/presentation.tsx +52 -37
- package/src/ui/components/patterns/split.tsx +44 -4
- package/src/ui/components/patterns/surface-anatomy.ts +35 -8
- package/src/ui/components/patterns/trigger.tsx +12 -2
- package/src/ui/components/primitives/alert.tsx +9 -1
- package/src/ui/components/primitives/avatar.tsx +7 -3
- package/src/ui/components/primitives/carousel.tsx +34 -5
- package/src/ui/components/primitives/checkbox.tsx +11 -3
- package/src/ui/components/primitives/command.tsx +2 -1
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/dialog.tsx +41 -10
- package/src/ui/components/primitives/drawer.tsx +9 -1
- package/src/ui/components/primitives/empty.tsx +1 -1
- package/src/ui/components/primitives/icon-picker.tsx +6 -2
- package/src/ui/components/primitives/icon-tile.tsx +7 -0
- package/src/ui/components/primitives/input-group.tsx +9 -2
- package/src/ui/components/primitives/input-otp.tsx +1 -1
- package/src/ui/components/primitives/kbd.tsx +1 -1
- package/src/ui/components/primitives/label.tsx +3 -0
- package/src/ui/components/primitives/menu.tsx +5 -0
- package/src/ui/components/primitives/popover.tsx +1 -1
- package/src/ui/components/primitives/progress.tsx +9 -1
- package/src/ui/components/primitives/slider.tsx +62 -11
- package/src/ui/components/primitives/truncate.tsx +10 -1
- package/src/ui/docs/content/accordion.md +74 -27
- package/src/ui/docs/content/action-form-card.md +24 -15
- package/src/ui/docs/content/action-form-dialog.md +17 -7
- package/src/ui/docs/content/action-form.md +92 -77
- package/src/ui/docs/content/action-list-dialog.md +75 -38
- package/src/ui/docs/content/action-list.md +134 -52
- package/src/ui/docs/content/action-trigger.md +27 -10
- package/src/ui/docs/content/action-view.md +14 -14
- package/src/ui/docs/content/actions.md +161 -66
- package/src/ui/docs/content/ai.md +43 -44
- package/src/ui/docs/content/alert.md +83 -29
- package/src/ui/docs/content/ask.md +51 -26
- package/src/ui/docs/content/aspect-ratio.md +22 -24
- package/src/ui/docs/content/audit.md +23 -19
- package/src/ui/docs/content/auth.md +38 -19
- package/src/ui/docs/content/avatar.md +54 -5
- package/src/ui/docs/content/badge.md +29 -9
- package/src/ui/docs/content/breadcrumb.md +21 -8
- package/src/ui/docs/content/button.md +73 -43
- package/src/ui/docs/content/calendar.md +134 -26
- package/src/ui/docs/content/card.md +41 -15
- package/src/ui/docs/content/carousel.md +37 -6
- package/src/ui/docs/content/chat.md +118 -37
- package/src/ui/docs/content/checkbox.md +73 -7
- package/src/ui/docs/content/cli.md +94 -55
- package/src/ui/docs/content/collapsible.md +43 -19
- package/src/ui/docs/content/command.md +30 -8
- package/src/ui/docs/content/composer.md +23 -8
- package/src/ui/docs/content/content.md +31 -7
- package/src/ui/docs/content/copyable.md +39 -8
- package/src/ui/docs/content/customization.md +55 -46
- package/src/ui/docs/content/cycle.md +33 -18
- package/src/ui/docs/content/data-products.md +115 -0
- package/src/ui/docs/content/data-state.md +20 -6
- package/src/ui/docs/content/data.md +20 -19
- package/src/ui/docs/content/detail.md +12 -5
- package/src/ui/docs/content/dialog.md +107 -24
- package/src/ui/docs/content/dictionary-value.md +4 -4
- package/src/ui/docs/content/dock.md +54 -19
- package/src/ui/docs/content/dot.md +35 -5
- package/src/ui/docs/content/drawer.md +63 -19
- package/src/ui/docs/content/empty-value.md +20 -6
- package/src/ui/docs/content/empty.md +24 -3
- package/src/ui/docs/content/events.md +76 -16
- package/src/ui/docs/content/executions.md +9 -9
- package/src/ui/docs/content/field.md +21 -6
- package/src/ui/docs/content/getting-started.md +44 -34
- package/src/ui/docs/content/icon-picker.md +19 -13
- package/src/ui/docs/content/icon-tile.md +28 -5
- package/src/ui/docs/content/input-otp.md +58 -7
- package/src/ui/docs/content/input.md +22 -3
- package/src/ui/docs/content/item.md +53 -6
- package/src/ui/docs/content/kbd.md +5 -1
- package/src/ui/docs/content/label.md +13 -4
- package/src/ui/docs/content/log.md +5 -5
- package/src/ui/docs/content/markdown.md +5 -4
- package/src/ui/docs/content/mcp.md +49 -23
- package/src/ui/docs/content/menu.md +95 -10
- package/src/ui/docs/content/metric-card.md +12 -4
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +118 -12
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +55 -5
- package/src/ui/docs/content/popover.md +42 -5
- package/src/ui/docs/content/presentation.md +91 -47
- package/src/ui/docs/content/progress.md +27 -14
- package/src/ui/docs/content/queue.md +39 -21
- package/src/ui/docs/content/radio-group.md +26 -4
- package/src/ui/docs/content/router.md +29 -10
- package/src/ui/docs/content/runtime.md +90 -54
- package/src/ui/docs/content/scheduler.md +53 -21
- package/src/ui/docs/content/scroll-area.md +42 -35
- package/src/ui/docs/content/select.md +78 -33
- package/src/ui/docs/content/semantic-context.md +21 -2
- package/src/ui/docs/content/separator.md +15 -9
- package/src/ui/docs/content/sidebar.md +3 -2
- package/src/ui/docs/content/skeleton.md +6 -1
- package/src/ui/docs/content/slider.md +37 -9
- package/src/ui/docs/content/spinner.md +4 -3
- package/src/ui/docs/content/split.md +19 -0
- package/src/ui/docs/content/storage.md +8 -9
- package/src/ui/docs/content/switch.md +10 -7
- package/src/ui/docs/content/table.md +46 -8
- package/src/ui/docs/content/tabs.md +14 -4
- package/src/ui/docs/content/testing.md +76 -31
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +48 -7
- package/src/ui/docs/content/toggle.md +3 -3
- package/src/ui/docs/content/tokens.md +15 -26
- package/src/ui/docs/content/tooltip.md +18 -2
- package/src/ui/docs/content/truncate.md +36 -6
- package/src/ui/docs/content/ui.md +74 -22
- package/src/ui/docs/content/upgrading.md +45 -36
- package/src/ui/docs/plugin.ts +36 -2
- package/src/ui/docs/registry.tsx +6 -0
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +31 -31
- package/src/vite/design.ts +16 -4
|
@@ -5,53 +5,105 @@ title: Como consumir
|
|
|
5
5
|
# Como consumir a UI
|
|
6
6
|
|
|
7
7
|
Esta documentação é a referência visual e de uso de `@softize/opus/ui`. Os exemplos são renderizados
|
|
8
|
-
com o tema e
|
|
8
|
+
com o mesmo tema e os mesmos componentes entregues aos aplicativos.
|
|
9
9
|
|
|
10
|
-
## Por que esta
|
|
10
|
+
## Por que esta documentação existe
|
|
11
11
|
|
|
12
|
-
O catálogo do shadcn documenta
|
|
12
|
+
O catálogo do shadcn documenta o próprio código e pode mostrar composições que não fazem parte do
|
|
13
13
|
Opus. Consulte estas páginas para conhecer os componentes, as variações e os comportamentos
|
|
14
14
|
suportados pelo pacote.
|
|
15
15
|
|
|
16
16
|
Cada página combina exemplos vivos, código e uma referência de propriedades por componente. A
|
|
17
|
-
orientação inicial vem de `componentMeta`, a mesma fonte usada pelo catálogo e
|
|
17
|
+
orientação inicial de cada componente vem de `componentMeta`, a mesma fonte usada pelo catálogo e
|
|
18
|
+
pelas ferramentas.
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
+
## Importar componentes e tema
|
|
20
21
|
|
|
21
22
|
Importe componentes e hooks de `@softize/opus/ui/react` e o tema por CSS. Não copie a implementação
|
|
22
23
|
para o aplicativo quando a API pública já atender ao caso.
|
|
23
24
|
|
|
24
25
|
```tsx
|
|
25
|
-
// Componentes e hooks
|
|
26
|
+
// Componentes e hooks vêm do mesmo ponto de entrada.
|
|
26
27
|
import { Button, Dialog, useAction } from '@softize/opus/ui/react'
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
```css
|
|
30
|
-
/* index.css — o tema
|
|
31
|
+
/* index.css — o tema do Opus e os componentes do pacote no scan do Tailwind. */
|
|
31
32
|
@import '@softize/opus/ui/theme.css';
|
|
32
33
|
@source '../node_modules/@softize/opus/src/ui/**/*.{ts,tsx}';
|
|
33
34
|
```
|
|
34
35
|
|
|
36
|
+
## Montar os providers
|
|
37
|
+
|
|
38
|
+
Os hooks de action e os componentes que executam actions dependem de três providers na raiz da
|
|
39
|
+
aplicação. O app criado por `opus create` já nasce com eles:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// main.tsx
|
|
43
|
+
import { StrictMode } from 'react'
|
|
44
|
+
import { createRoot } from 'react-dom/client'
|
|
45
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
46
|
+
import { OpusProvider, TooltipProvider } from '@softize/opus/ui/react'
|
|
47
|
+
import { fetchClient } from '@softize/opus/client/fetch'
|
|
48
|
+
import { App } from './App.tsx'
|
|
49
|
+
|
|
50
|
+
const queryClient = new QueryClient()
|
|
51
|
+
// Mesma origem: as actions respondem em /api. Aponte baseUrl para outro servidor quando houver.
|
|
52
|
+
const client = fetchClient({ baseUrl: '' })
|
|
53
|
+
|
|
54
|
+
createRoot(document.getElementById('root')!).render(
|
|
55
|
+
<StrictMode>
|
|
56
|
+
<QueryClientProvider client={queryClient}>
|
|
57
|
+
<OpusProvider client={client}>
|
|
58
|
+
<TooltipProvider>
|
|
59
|
+
<App />
|
|
60
|
+
</TooltipProvider>
|
|
61
|
+
</OpusProvider>
|
|
62
|
+
</QueryClientProvider>
|
|
63
|
+
</StrictMode>,
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- `QueryClientProvider` guarda o cache das consultas; `useListAction` e as invalidações entre
|
|
68
|
+
actions dependem dele.
|
|
69
|
+
- `OpusProvider` informa como chamar as actions. Fora dele, os hooks de action lançam erro ao
|
|
70
|
+
montar.
|
|
71
|
+
- `TooltipProvider` é exigido pelos tooltips da aplicação e por componentes do Opus que usam
|
|
72
|
+
tooltip, como `Dock` e `Truncate`.
|
|
73
|
+
|
|
74
|
+
`fetchClient` chama cada action por HTTP em `<baseUrl><apiPrefix>`; `apiPrefix` tem padrão `/api`,
|
|
75
|
+
o mesmo dos servidores do Opus. `headers` e `beforeRequest` acrescentam cabeçalhos fixos ou
|
|
76
|
+
calculados a cada chamada, como um token de acesso.
|
|
77
|
+
|
|
78
|
+
## Propriedades de OpusProvider
|
|
79
|
+
|
|
80
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
81
|
+
| --- | --- | --- | --- |
|
|
82
|
+
| `client` | `ClientAdapter` | obrigatório | Executa as actions chamadas pelos hooks e componentes; normalmente `fetchClient`. |
|
|
83
|
+
| `dicts` | `Record<string, DictLike>` | `{}` | Dicionários por referência, usados por `options: { kind: 'dictionary', ref }` em campos e filtros. Sem registro, o campo usa os dados do `t.dict` que viajam no schema do contrato. |
|
|
84
|
+
| `children` | `ReactNode` | obrigatório | A árvore que usa as actions. |
|
|
85
|
+
|
|
35
86
|
## Prebundle do Vite
|
|
36
87
|
|
|
37
|
-
A entrada `@softize/opus/ui/react` é um `.tsx`, e o prebundle do Vite só considera entradas
|
|
38
|
-
`.js
|
|
39
|
-
|
|
88
|
+
A entrada `@softize/opus/ui/react` é um arquivo `.tsx`, e o prebundle do Vite só considera entradas
|
|
89
|
+
`.js` e `.ts`. Sem ajuste, o dev server avisa `Cannot optimize dependency` e serve os arquivos do
|
|
90
|
+
Opus um a um na primeira carga. Há duas saídas, cada uma com um custo conhecido:
|
|
40
91
|
|
|
41
92
|
- `optimizeDeps.include: ['@softize/opus/ui/react']` com `extensions: ['.tsx']` pré-empacota o
|
|
42
|
-
Opus, mas pode duplicar `@tanstack/react-query` quando o app também o importa
|
|
43
|
-
`QueryClient
|
|
44
|
-
- `optimizeDeps.exclude: ['@softize/opus/ui/react']` evita a duplicação e
|
|
93
|
+
Opus, mas pode duplicar `@tanstack/react-query` quando o app também o importa diretamente. O
|
|
94
|
+
resultado são dois `QueryClient` e hooks que não encontram o provider.
|
|
95
|
+
- `optimizeDeps.exclude: ['@softize/opus/ui/react']` evita a duplicação e deixa a primeira carga
|
|
96
|
+
mais lenta.
|
|
45
97
|
|
|
46
|
-
Escolha pelo sintoma que aparece no seu app e registre a decisão no `vite.config.ts`.
|
|
47
|
-
|
|
98
|
+
Escolha pelo sintoma que aparece no seu app e registre a decisão no `vite.config.ts`. Uma entrada
|
|
99
|
+
`.ts` no pacote, que resolveria os dois custos, ainda não existe.
|
|
48
100
|
|
|
49
|
-
## Origem
|
|
101
|
+
## Origem dos componentes
|
|
50
102
|
|
|
51
|
-
|
|
103
|
+
Cada componente declara de onde veio, e essa origem define como ele acompanha mudanças:
|
|
52
104
|
|
|
53
|
-
- **Origem shadcn
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- **Nativo do Opus
|
|
57
|
-
referência é esta
|
|
105
|
+
- **Origem shadcn:** adaptação do shadcn (Radix e cmdk). Toda divergência do original é declarada
|
|
106
|
+
no componente ou no tema. Uma divergência sem declaração deve ser sincronizada; a skill
|
|
107
|
+
`$build-opus-ui` orienta esse trabalho.
|
|
108
|
+
- **Nativo do Opus:** criado no próprio Opus, como os componentes de action. Não há versão externa a
|
|
109
|
+
acompanhar; a referência é esta documentação.
|
|
@@ -4,74 +4,83 @@ title: Atualizar o Opus
|
|
|
4
4
|
|
|
5
5
|
# Atualizar o Opus
|
|
6
6
|
|
|
7
|
-
Use este fluxo para atualizar o Opus por uma ou várias versões. O changelog explica
|
|
8
|
-
gates apontam as adaptações necessárias no projeto.
|
|
7
|
+
Use este fluxo para atualizar o Opus por uma ou várias versões. O [changelog](/changelog) explica
|
|
8
|
+
as mudanças, e os gates apontam as adaptações necessárias no projeto. Em projetos que usam as
|
|
9
|
+
skills do Opus, a skill `$upgrade-opus` conduz as mesmas etapas: lê o changelog, atualiza a
|
|
10
|
+
dependência, materializa os artefatos e aplica as migrações.
|
|
9
11
|
|
|
10
|
-
## 1.
|
|
12
|
+
## 1. Atualizar a dependência
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
a parte mecânica: bump da dependência, re-materialização da camada de IA (skills,
|
|
15
|
-
hooks, agentes) e re-sync do bloco gerenciado do CLAUDE.md.
|
|
16
|
-
|
|
17
|
-
Fora do Maestro, faça essa parte à mão:
|
|
14
|
+
A versão adotada pelo projeto fica no `package.json` e no `opus.json` de cada app. Compare-a com a
|
|
15
|
+
versão publicada e atualize:
|
|
18
16
|
|
|
19
17
|
```bash
|
|
20
18
|
pnpm outdated @softize/opus # distância até a versão publicada
|
|
21
|
-
pnpm -r up @softize/opus@<versão> # em todo workspace que declara o Opus
|
|
22
|
-
pnpm run setup # opus setup && base setup:
|
|
19
|
+
pnpm -r up @softize/opus@<versão> # em todo pacote do workspace que declara o Opus
|
|
20
|
+
pnpm run setup # opus setup && base setup: materializa base.json e skills
|
|
23
21
|
```
|
|
24
22
|
|
|
25
|
-
Rode `setup` no diretório de cada app que tem `opus.json
|
|
23
|
+
Rode `setup` no diretório de cada app que tem `opus.json`. Num monorepo criado por
|
|
26
24
|
`opus create --monorepo`, a raiz não tem esse script, e `pnpm -r run setup` alcança todos os apps.
|
|
27
|
-
O postinstall do pacote tenta materializar sozinho, mas
|
|
28
|
-
ainda corresponder à versão anterior, o `opus check`
|
|
25
|
+
O postinstall do pacote tenta materializar os artefatos sozinho, mas não garante o resultado. Se a
|
|
26
|
+
camada materializada ainda corresponder à versão anterior, o `opus check` para com
|
|
29
27
|
`base.json: Opus aplicado X, instalado Y`.
|
|
30
28
|
|
|
29
|
+
Interfaces de desenvolvimento podem mostrar a diferença entre a versão aplicada na
|
|
30
|
+
sua cópia de trabalho e a adotada pela branch principal. Esse aviso não atualiza o pacote; a
|
|
31
|
+
atualização continua sendo este fluxo.
|
|
32
|
+
|
|
31
33
|
## 2. Ler as mudanças acumuladas
|
|
32
34
|
|
|
33
|
-
O changelog
|
|
34
|
-
`node_modules/@softize/opus/CHANGELOG.md`.
|
|
35
|
-
a
|
|
36
|
-
|
|
35
|
+
O changelog também acompanha o pacote: depois da atualização, ele está em
|
|
36
|
+
`node_modules/@softize/opus/CHANGELOG.md`. Cada versão tem uma entrada, e a publicação é recusada
|
|
37
|
+
quando a entrada falta. Quando uma versão quebra compatibilidade, a seção **Breaking** descreve o
|
|
38
|
+
que mudou e como migrar o seu código.
|
|
37
39
|
|
|
38
|
-
- **
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
- **Minor e patch** (18.0 → 18.5): leia todas as entradas intermediárias, porque elas também podem
|
|
41
|
+
mudar comportamento observável. Na 18.1.1, por exemplo, uma action que não é `public` passou a
|
|
42
|
+
exigir usuário antes de executar os `loads`.
|
|
43
|
+
- **Major** (18.x → 19.0): além disso, aplique as seções Breaking de cada versão pulada, na ordem.
|
|
44
|
+
Na 19.0.0, por exemplo, os headers de `Card`, `Dialog` e `Drawer` passaram a aceitar somente os
|
|
45
|
+
slots da própria família.
|
|
41
46
|
|
|
42
47
|
## 3. Executar as verificações
|
|
43
48
|
|
|
44
|
-
O Opus é publicado como código-fonte TypeScript
|
|
45
|
-
Rode, na ordem:
|
|
49
|
+
O Opus é publicado como código-fonte TypeScript, então uma incompatibilidade aparece com arquivo e
|
|
50
|
+
linha. Rode, na ordem:
|
|
46
51
|
|
|
47
52
|
```bash
|
|
48
|
-
pnpm typecheck # a API nova
|
|
49
|
-
pnpm exec opus check # convenções
|
|
50
|
-
pnpm exec opus copy --check # inventário de copy
|
|
51
|
-
pnpm exec base copy check # política de copy da
|
|
53
|
+
pnpm typecheck # a API nova confere os tipos
|
|
54
|
+
pnpm exec opus check # convenções, inclusive regras novas
|
|
55
|
+
pnpm exec opus copy --check # inventário de copy atualizado
|
|
56
|
+
pnpm exec base copy check # política de copy da Base
|
|
52
57
|
pnpm test # comportamento
|
|
53
|
-
pnpm manifest:check #
|
|
58
|
+
pnpm manifest:check # o manifest projetado continua atualizado
|
|
54
59
|
```
|
|
55
60
|
|
|
56
|
-
Num monorepo, `typecheck` e `test` existem na raiz, mas `manifest:check` é script de cada app:
|
|
57
|
-
rode-o no diretório do app ou com `pnpm -r run manifest:check`. Nenhum desses comandos
|
|
61
|
+
Num monorepo, `typecheck` e `test` existem na raiz, mas `manifest:check` é um script de cada app:
|
|
62
|
+
rode-o no diretório do app ou com `pnpm -r run manifest:check`. Nenhum desses comandos altera o
|
|
58
63
|
banco. Se o projeto tiver testes de integração, eles usam o banco que o próprio projeto configurar.
|
|
59
64
|
|
|
60
65
|
## 4. Migrar o banco, se houver
|
|
61
66
|
|
|
62
67
|
Se o `opus.config.ts` declara `database`, confira o schema e aplique a migração como uma etapa
|
|
63
|
-
separada
|
|
68
|
+
separada:
|
|
64
69
|
|
|
65
70
|
```bash
|
|
66
71
|
pnpm exec opus db check # compara entidades e banco, sem escrever
|
|
67
72
|
pnpm exec opus db migrate # aplica o schema idempotente; escreve no banco
|
|
68
73
|
```
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
de
|
|
75
|
+
Os comandos `opus db` carregam o `.env` que está ao lado do `opus.config.ts` antes de abrir a
|
|
76
|
+
conexão, e uma variável já definida no ambiente vence o arquivo. Antes de migrar, confirme para
|
|
77
|
+
qual banco a factory `database` vai apontar. Para migrar outro ambiente, informe a variável no
|
|
78
|
+
próprio comando, como `DATABASE_URL=… pnpm exec opus db migrate` quando a factory lê essa variável.
|
|
79
|
+
|
|
80
|
+
Com todas as verificações aprovadas, revise a atualização como qualquer outra mudança de código
|
|
81
|
+
antes de entregá-la.
|
|
72
82
|
|
|
73
83
|
## Pulou muitas versões?
|
|
74
84
|
|
|
75
|
-
O
|
|
76
|
-
|
|
77
|
-
Não há caminho especial — é o mesmo laço, com mais iterações.
|
|
85
|
+
O fluxo é o mesmo, com mais entradas para ler. Leia todas as entradas acumuladas, não só as seções
|
|
86
|
+
Breaking, aplique as migrações na ordem das versões e deixe os gates validarem o conjunto.
|
package/src/ui/docs/plugin.ts
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* `mountDocs`. Importando o MESMO CSS do app, a doc herda identidade e utilitários de
|
|
12
12
|
* graça. É dev-only (apply: 'serve') — nada vai pro bundle de produção.
|
|
13
13
|
*
|
|
14
|
+
* O CSS entra por um entry CSS virtual que importa o do app e acrescenta um `@source` para
|
|
15
|
+
* as páginas `.md` da doc do Opus. Os exemplos ```tsx preview usam classes que só aparecem
|
|
16
|
+
* nesses `.md`, e o Tailwind do app não varre o pacote instalado. O `@source` fica só nesse
|
|
17
|
+
* entry: o CSS de produção do app não muda.
|
|
18
|
+
*
|
|
14
19
|
* Uso (vite.config.ts do app):
|
|
15
20
|
* import { opusDocs } from '@softize/opus/ui/docs/plugin'
|
|
16
21
|
* export default defineConfig({ plugins: [react(), tailwindcss(), opusDocs()] })
|
|
@@ -36,6 +41,29 @@ const VIRTUAL_ID = 'virtual:opus-docs-entry'
|
|
|
36
41
|
// O `/@id/` é como o vite serve módulos por id; o `\0` (convenção de virtual) vira `__x00__` na URL.
|
|
37
42
|
const RESOLVED_ID = '\0' + VIRTUAL_ID
|
|
38
43
|
const ENTRY_URL = '/@id/__x00__' + VIRTUAL_ID
|
|
44
|
+
// A extensão `.css` faz o Vite e o @tailwindcss/vite tratarem o módulo virtual como folha de estilo.
|
|
45
|
+
const VIRTUAL_CSS_ID = 'virtual:opus-docs-entry.css'
|
|
46
|
+
const RESOLVED_CSS_ID = '\0' + VIRTUAL_CSS_ID
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Pasta das páginas `.md` da doc do Opus, ao lado deste arquivo. O Node carrega o plugin pelo
|
|
50
|
+
* caminho real (resolve o symlink do pnpm/workspace), então `import.meta.url` já aponta para
|
|
51
|
+
* onde o scanner do Tailwind encontra os arquivos.
|
|
52
|
+
*
|
|
53
|
+
* Sem builtins do Node: o arquivo mora em `ui/`, e o gate de fronteira proíbe `node:*` ali.
|
|
54
|
+
* Sem `new URL('./content', import.meta.url)`: o Vite reescreve esse padrão como asset.
|
|
55
|
+
*/
|
|
56
|
+
export function opusDocsContentDir(moduleUrl: string = import.meta.url): string {
|
|
57
|
+
const folder = moduleUrl.slice(0, moduleUrl.lastIndexOf('/'))
|
|
58
|
+
const path = decodeURIComponent(new URL(folder).pathname)
|
|
59
|
+
// No Windows o pathname vem como `/C:/…`; o Tailwind espera `C:/…`.
|
|
60
|
+
return (/^\/[A-Za-z]:\//.test(path) ? path.slice(1) : path) + '/content'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** O CSS do entry virtual: o do app (tema + tailwind) + a varredura dos `.md` da doc. */
|
|
64
|
+
export function opusDocsEntryCss(appCss: string, contentDir: string = opusDocsContentDir()): string {
|
|
65
|
+
return [`@import ${JSON.stringify(appCss)};`, `@source ${JSON.stringify(contentDir + '/*.md')};`].join('\n')
|
|
66
|
+
}
|
|
39
67
|
|
|
40
68
|
export function opusDocs(options: opusDocsOptions = {}): Plugin {
|
|
41
69
|
const basePath = options.basePath ?? '/__docs'
|
|
@@ -48,12 +76,18 @@ export function opusDocs(options: opusDocsOptions = {}): Plugin {
|
|
|
48
76
|
apply: 'serve',
|
|
49
77
|
resolveId(id) {
|
|
50
78
|
if (id === VIRTUAL_ID) return RESOLVED_ID
|
|
79
|
+
if (id === VIRTUAL_CSS_ID) return RESOLVED_CSS_ID
|
|
51
80
|
return undefined
|
|
52
81
|
},
|
|
53
|
-
load(id) {
|
|
82
|
+
async load(id) {
|
|
83
|
+
if (id === RESOLVED_CSS_ID) {
|
|
84
|
+
// O `@import` do entry virtual não tem pasta de referência: vai com o caminho resolvido.
|
|
85
|
+
const resolved = await this.resolve(css)
|
|
86
|
+
return opusDocsEntryCss(resolved?.id ?? css)
|
|
87
|
+
}
|
|
54
88
|
if (id !== RESOLVED_ID) return undefined
|
|
55
89
|
// Sempre importa o CSS do app (tema + tailwind) antes de subir a doc.
|
|
56
|
-
const head = `import ${JSON.stringify(
|
|
90
|
+
const head = `import ${JSON.stringify(VIRTUAL_CSS_ID)}`
|
|
57
91
|
if (source === undefined || source === '') {
|
|
58
92
|
// Sem `source`: a doc do próprio Opus (DOC_SECTIONS), como sempre.
|
|
59
93
|
return [head, `import { mountDocs } from '@softize/opus/ui/docs/standalone'`, `mountDocs({ basePath: ${JSON.stringify(basePath)} })`].join('\n')
|
package/src/ui/docs/registry.tsx
CHANGED
|
@@ -37,6 +37,7 @@ import executionsMd from "./content/executions.md?raw";
|
|
|
37
37
|
import schedulerMd from "./content/scheduler.md?raw";
|
|
38
38
|
import actionsMd from "./content/actions.md?raw";
|
|
39
39
|
import dataMd from "./content/data.md?raw";
|
|
40
|
+
import dataProductsMd from "./content/data-products.md?raw";
|
|
40
41
|
import storageMd from "./content/storage.md?raw";
|
|
41
42
|
import aiMd from "./content/ai.md?raw";
|
|
42
43
|
import mcpMd from "./content/mcp.md?raw";
|
|
@@ -276,6 +277,11 @@ export const PROTOCOL_SECTIONS: DocSection[] = [
|
|
|
276
277
|
render: doc(runtimeMd),
|
|
277
278
|
},
|
|
278
279
|
{ slug: "data", title: "Camada de dados", render: doc(dataMd) },
|
|
280
|
+
{
|
|
281
|
+
slug: "data-products",
|
|
282
|
+
title: "Produtos de Dados",
|
|
283
|
+
render: doc(dataProductsMd),
|
|
284
|
+
},
|
|
279
285
|
{
|
|
280
286
|
slug: "testing",
|
|
281
287
|
title: "Testes de action",
|
package/src/ui/drivers/react.tsx
CHANGED
|
@@ -83,11 +83,19 @@ export function OpusProvider({
|
|
|
83
83
|
return <OpusContext.Provider value={value}>{children}</OpusContext.Provider>
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
/**
|
|
87
|
+
* O adapter do provider. `hook` nomeia quem chamou, para a mensagem apontar o hook que a
|
|
88
|
+
* pessoa vê no stack — muitas vezes chamado por dentro de ActionForm, ActionList ou
|
|
89
|
+
* ActionView, e não diretamente pela tela.
|
|
90
|
+
*/
|
|
91
|
+
function useClient(hook: string): ClientAdapter {
|
|
87
92
|
const ctx = useContext(OpusContext)
|
|
88
93
|
if (ctx === null) {
|
|
89
94
|
throw new Error(
|
|
90
|
-
|
|
95
|
+
`${hook} foi chamado fora de um <OpusProvider> e não tem como executar a action. ` +
|
|
96
|
+
'Os hooks de action e os componentes que os usam, como ActionForm, ActionList e ActionView, ' +
|
|
97
|
+
'precisam de um <OpusProvider client={...}> acima deles na árvore. Envolva a aplicação no provider; ' +
|
|
98
|
+
'em testes e previews, envolva o componente renderizado.',
|
|
91
99
|
)
|
|
92
100
|
}
|
|
93
101
|
return ctx.client
|
|
@@ -113,7 +121,7 @@ export interface UseActionResult<TInput, TData> extends ActionState<TData> {
|
|
|
113
121
|
export function useAction<TInput = unknown, TData = unknown>(
|
|
114
122
|
action: ActionDef,
|
|
115
123
|
): UseActionResult<TInput, TData> {
|
|
116
|
-
const client = useClient()
|
|
124
|
+
const client = useClient('useAction')
|
|
117
125
|
const [state, setState] = useState<ActionState<TData>>(() => idleAction<TData>())
|
|
118
126
|
|
|
119
127
|
const run = useCallback(
|
|
@@ -173,7 +181,7 @@ export interface UseLookupActionResult<TInput, TItem>
|
|
|
173
181
|
export function useLookupAction<TInput = unknown, TItem = unknown>(
|
|
174
182
|
action: ActionDef,
|
|
175
183
|
): UseLookupActionResult<TInput, TItem> {
|
|
176
|
-
const client = useClient()
|
|
184
|
+
const client = useClient('useLookupAction')
|
|
177
185
|
const [state, setState] = useState<ListState<TItem>>(() => idleLookup<TItem>())
|
|
178
186
|
|
|
179
187
|
const run = useCallback(
|
|
@@ -257,7 +265,7 @@ export function useListAction<TItem = unknown, TInput extends Record<string, unk
|
|
|
257
265
|
action: { name: string; kind: 'list' },
|
|
258
266
|
input?: TInput,
|
|
259
267
|
): UseListActionResult<TItem> {
|
|
260
|
-
const client = useClient()
|
|
268
|
+
const client = useClient('useListAction')
|
|
261
269
|
|
|
262
270
|
const query = useQuery<Paginated<TItem>, ActionError>({
|
|
263
271
|
queryKey: [action.name, input ?? {}],
|
|
@@ -358,7 +366,7 @@ export function useFormAction<
|
|
|
358
366
|
action: FormContract<TInput, TData>,
|
|
359
367
|
options: UseFormActionOptions<TInput, TData> = {},
|
|
360
368
|
): UseFormActionResult<TInput, TData> {
|
|
361
|
-
const client = useClient()
|
|
369
|
+
const client = useClient('useFormAction')
|
|
362
370
|
const queryClient = useQueryClient()
|
|
363
371
|
|
|
364
372
|
const resolver = useMemo(() => {
|
|
@@ -484,7 +492,7 @@ export function useViewAction<TInput = unknown, TData = unknown>(
|
|
|
484
492
|
action: { name: string; kind: 'view' },
|
|
485
493
|
input: TInput,
|
|
486
494
|
): UseViewActionResult<TData> {
|
|
487
|
-
const client = useClient()
|
|
495
|
+
const client = useClient('useViewAction')
|
|
488
496
|
|
|
489
497
|
// TanStack Query serializa queryKey → dedupe automático mesmo com input
|
|
490
498
|
// inline (referência nova a cada render). Sem isso entraria em loop.
|
|
@@ -551,7 +559,7 @@ export function useTriggerAction<TInput = unknown, TData = unknown>(
|
|
|
551
559
|
action: ActionContract<TInput, TData>,
|
|
552
560
|
options: UseTriggerActionOptions<TInput, TData> = {},
|
|
553
561
|
): UseTriggerActionResult<TInput, TData> {
|
|
554
|
-
const client = useClient()
|
|
562
|
+
const client = useClient('useTriggerAction')
|
|
555
563
|
const queryClient = useQueryClient()
|
|
556
564
|
const [state, setState] = useState<ActionState<TData>>(() =>
|
|
557
565
|
idleAction<TData>(),
|