@softize/opus 19.0.0 → 20.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.
- package/CHANGELOG.md +239 -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/0020-icon-tiles-are-explicitly-composed.md +8 -0
- package/docs/adr/0021-content-carries-structure-and-semantic-element.md +7 -0
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +19 -1
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
- package/docs/adr/0025-content-frame-treatment-does-not-change-semantics.md +134 -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 +109 -18
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/list.tsx +91 -92
- 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/surface-header.tsx +58 -25
- 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/card.tsx +1 -1
- 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 +42 -11
- package/src/ui/components/primitives/drawer.tsx +10 -2
- 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 +141 -58
- 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 +119 -27
- 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 +38 -15
- 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 +32 -32
- package/src/ui/react.tsx +1 -0
- package/src/vite/design.ts +16 -4
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
## Navegação entre páginas
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Pagination` quando uma coleção for dividida em páginas e a pessoa precisar saltar entre elas.
|
|
4
|
+
Em uma listagem derivada de uma action, [`ActionList`](/ui/action-list) já monta esta paginação no
|
|
5
|
+
rodapé; componha-a diretamente apenas em coleções próprias.
|
|
6
|
+
|
|
7
|
+
Monte `Pagination`, `PaginationContent` e um `PaginationItem` para cada link. `page` fornece o
|
|
4
8
|
número e o nome acessível; `isActive` identifica a página atual. Os controles mantêm altura fixa e
|
|
5
|
-
a largura cresce quando o número precisa de mais espaço.
|
|
6
|
-
rodapé.
|
|
9
|
+
a largura cresce quando o número precisa de mais espaço.
|
|
7
10
|
|
|
8
11
|
```tsx preview
|
|
9
12
|
<Pagination>
|
|
@@ -30,8 +33,9 @@ rodapé.
|
|
|
30
33
|
## Números longos
|
|
31
34
|
|
|
32
35
|
A largura mínima é quadrada e cresce conforme o conteúdo. Na escala densa de um rodapé, as setas e a
|
|
33
|
-
elipse usam `size="icon-sm"
|
|
34
|
-
números
|
|
36
|
+
elipse usam `size="icon-sm"`, o quadrado de `1.75rem` da escala única, com ícone de `0.875rem`. Os
|
|
37
|
+
números não têm um degrau textual de `1.75rem` na escala e, por isso, recebem a mesma medida por
|
|
38
|
+
`className`, como no exemplo.
|
|
35
39
|
|
|
36
40
|
```tsx preview
|
|
37
41
|
<Pagination>
|
|
@@ -148,3 +152,7 @@ render(
|
|
|
148
152
|
|---|---|---|---|
|
|
149
153
|
| `size` | `'icon-xs' \| 'icon-sm' \| 'icon' \| 'icon-lg'` | `'icon'` | Quadrado da escala única; use o mesmo tamanho das setas ao lado. |
|
|
150
154
|
| `className` | `string` | | Classes adicionais aplicadas à elipse. |
|
|
155
|
+
|
|
156
|
+
`Pagination` renderiza um `<nav>` com o nome acessível “Paginação”, `PaginationContent` um `<ul>` e
|
|
157
|
+
`PaginationItem` um `<li>`. Os três não têm propriedades próprias e aceitam as props nativas do
|
|
158
|
+
respectivo elemento, incluindo `className`.
|
|
@@ -62,11 +62,55 @@ render(
|
|
|
62
62
|
|
|
63
63
|
O cabeçalho reconhece quatro slots — `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions` —
|
|
64
64
|
e monta a linha sozinho: mídia, título e descrição de um lado, ações no extremo oposto. A ordem em
|
|
65
|
-
que os slots são escritos não altera a posição
|
|
66
|
-
renderização com um erro
|
|
65
|
+
que os slots são escritos não altera a posição. Cada slot aparece no máximo uma vez: repetir um
|
|
66
|
+
deles interrompe a renderização com um erro que lista os slots aceitos.
|
|
67
67
|
|
|
68
68
|
Título e descrição truncam quando a coluna encolhe, em vez de quebrar a altura da faixa.
|
|
69
69
|
|
|
70
|
+
Nas ações do cabeçalho, use `Button` `ghost` no tamanho `icon-sm` (1.75rem), como nos exemplos. O
|
|
71
|
+
pane é uma superfície densa, com título e descrição em escala menor, e o quadrado menor acompanha
|
|
72
|
+
essa escala. [`Dialog`](/ui/dialog) e [`Drawer`](/ui/drawer) usam `icon` (2.25rem) no voltar e nas
|
|
73
|
+
ações do cabeçalho, e a [`Presentation`](/ui/presentation) usa o mesmo tamanho no voltar e no botão
|
|
74
|
+
que abre o assistente.
|
|
75
|
+
|
|
76
|
+
## Busca ou marca no cabeçalho
|
|
77
|
+
|
|
78
|
+
Uma coluna às vezes precisa, no topo, de algo que não é título nem ação: uma busca que filtra a
|
|
79
|
+
lista, a marca de uma barra lateral. Escreva esse conteúdo direto dentro do `PaneHeader`, ao lado
|
|
80
|
+
dos slots. Só o `Pane` aceita conteúdo livre no cabeçalho; os headers de `Card`, `Dialog` e `Drawer`
|
|
81
|
+
aceitam apenas os próprios slots.
|
|
82
|
+
|
|
83
|
+
```tsx preview
|
|
84
|
+
render(
|
|
85
|
+
<div className="h-56 overflow-hidden rounded-lg border border-border">
|
|
86
|
+
<Split>
|
|
87
|
+
<Pane initialSize="18rem">
|
|
88
|
+
<PaneHeader>
|
|
89
|
+
<Input icon={<Search />} placeholder="Buscar conversa" aria-label="Buscar conversa" />
|
|
90
|
+
<PaneActions>
|
|
91
|
+
<Button variant="ghost" size="icon-sm" aria-label="Nova conversa">
|
|
92
|
+
<SquarePen />
|
|
93
|
+
</Button>
|
|
94
|
+
</PaneActions>
|
|
95
|
+
</PaneHeader>
|
|
96
|
+
<PaneBody className="space-y-1">
|
|
97
|
+
<p className="text-sm">Revisão do contrato</p>
|
|
98
|
+
<p className="text-sm text-muted-foreground">Importação de clientes</p>
|
|
99
|
+
</PaneBody>
|
|
100
|
+
</Pane>
|
|
101
|
+
<Pane grow>
|
|
102
|
+
<p className="text-sm text-muted-foreground">Conversa selecionada.</p>
|
|
103
|
+
</Pane>
|
|
104
|
+
</Split>
|
|
105
|
+
</div>,
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
O conteúdo livre ocupa o lugar do título e absorve a largura disponível, por isso as ações
|
|
110
|
+
continuam no extremo oposto. Quando há título, o conteúdo livre vem sempre depois do título e da descrição,
|
|
111
|
+
qualquer que seja a ordem no JSX: uma busca escrita junto de um `PaneTitle` aparece abaixo dele, não
|
|
112
|
+
ao lado. Para algo antes do título, use `PaneMedia`; para algo ao lado, use `PaneActions`.
|
|
113
|
+
|
|
70
114
|
## Escolher entre seccionado e contínuo
|
|
71
115
|
|
|
72
116
|
`layout` declara a anatomia da superfície inteira e fica no root; os slots leem esse valor e não o
|
|
@@ -86,6 +130,11 @@ Os divisores são derivados da vizinhança: cada região desenha a linha quando
|
|
|
86
130
|
lado. Um cabeçalho sem corpo não fecha nada, e um cabeçalho seguido direto de um rodapé produz uma
|
|
87
131
|
linha só. A tela não escreve `border-b` nem `border-t`.
|
|
88
132
|
|
|
133
|
+
No `continuous`, um cabeçalho sem corpo nem rodapé fecha o pane com o mesmo recuo que tem em cima.
|
|
134
|
+
Uma classe `pb-*` no `PaneHeader` não altera esse recuo: a regra só vale quando o cabeçalho é a
|
|
135
|
+
última região e tem precedência sobre o `className`. No `sectioned`, a faixa tem altura fixa e
|
|
136
|
+
centraliza a linha, então não há recuo de baixo a ajustar.
|
|
137
|
+
|
|
89
138
|
## Recuo do pane e recuo das regiões
|
|
90
139
|
|
|
91
140
|
`inset` é o respiro interno do pane sem slots: `md` por padrão, com `none`, `sm` e `lg`
|
|
@@ -99,11 +148,12 @@ O ajuste enxerga só o primeiro nível. Se os slots vierem dentro de outro compo
|
|
|
99
148
|
|
|
100
149
|
## A matriz de layouts e slots
|
|
101
150
|
|
|
102
|
-
As regiões são independentes
|
|
151
|
+
As regiões são independentes: a coluna usa só as que precisa, nos dois layouts, e os divisores
|
|
152
|
+
acompanham as regiões presentes.
|
|
103
153
|
|
|
104
154
|
```tsx preview col
|
|
105
155
|
render(
|
|
106
|
-
<div className="grid
|
|
156
|
+
<div className="grid grid-cols-2 gap-4">
|
|
107
157
|
<div className="h-56 overflow-hidden rounded-lg border border-border">
|
|
108
158
|
<Pane className="h-full">
|
|
109
159
|
<PaneHeader>
|
|
@@ -198,7 +248,7 @@ documento não muda a aparência da faixa.
|
|
|
198
248
|
|
|
199
249
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
200
250
|
|---|---|---|---|
|
|
201
|
-
| `children` | `ReactNode` | |
|
|
251
|
+
| `children` | `ReactNode` | | `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions`, no máximo um de cada, e conteúdo livre, que aparece depois do título e da descrição. |
|
|
202
252
|
| `className` | `string` | | Ajusta a faixa fixa sobre o recuo do layout. |
|
|
203
253
|
|
|
204
254
|
## Propriedades de PaneMedia
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Use `Popover` para apresentar conteúdo livre junto a um gatilho, sem abrir um modal.
|
|
4
4
|
`PopoverHeader` agrupa título e descrição; o restante do painel aceita formulários curtos ou
|
|
5
|
-
detalhes.
|
|
5
|
+
detalhes. Para uma lista de ações, use [`Menu`](/ui/menu); para uma dica curta ao passar o ponteiro,
|
|
6
|
+
use [`Tooltip`](/ui/tooltip).
|
|
6
7
|
|
|
7
8
|
```tsx preview
|
|
8
9
|
<Popover>
|
|
@@ -12,7 +13,7 @@ detalhes.
|
|
|
12
13
|
<PopoverContent align="start">
|
|
13
14
|
<PopoverHeader>
|
|
14
15
|
<PopoverTitle>Renomear</PopoverTitle>
|
|
15
|
-
<PopoverDescription>O
|
|
16
|
+
<PopoverDescription>O endereço da sessão continua o mesmo; só o título muda.</PopoverDescription>
|
|
16
17
|
</PopoverHeader>
|
|
17
18
|
<div className="mt-3 space-y-2">
|
|
18
19
|
<Label htmlFor="pop-titulo">Título</Label>
|
|
@@ -23,9 +24,14 @@ detalhes.
|
|
|
23
24
|
</Popover>
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
O painel mede `18rem` de largura por padrão e abre abaixo do gatilho. Clicar fora ou pressionar
|
|
28
|
+
Esc fecha o painel.
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
## Posição e largura
|
|
31
|
+
|
|
32
|
+
`side` escolhe o lado preferido, `align` alinha o painel ao gatilho e `sideOffset` define a
|
|
33
|
+
distância entre eles. Quando não há espaço no lado escolhido, o painel abre no lado oposto. Ajuste
|
|
34
|
+
a largura com `className`, como `w-56` para `14rem`.
|
|
29
35
|
|
|
30
36
|
```tsx preview
|
|
31
37
|
<Popover>
|
|
@@ -40,6 +46,12 @@ detalhes.
|
|
|
40
46
|
<PopoverDescription>Alinhado à borda direita do gatilho.</PopoverDescription>
|
|
41
47
|
</PopoverContent>
|
|
42
48
|
</Popover>
|
|
49
|
+
<Popover>
|
|
50
|
+
<PopoverTrigger asChild><Button variant="ghost">À direita</Button></PopoverTrigger>
|
|
51
|
+
<PopoverContent side="right" className="w-56">
|
|
52
|
+
<PopoverDescription>Abre ao lado direito do gatilho.</PopoverDescription>
|
|
53
|
+
</PopoverContent>
|
|
54
|
+
</Popover>
|
|
43
55
|
```
|
|
44
56
|
|
|
45
57
|
## Propriedades de Popover
|
|
@@ -47,7 +59,15 @@ detalhes.
|
|
|
47
59
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
48
60
|
|---|---|---|---|
|
|
49
61
|
| `open` | `boolean` | | Estado no modo controlado. |
|
|
50
|
-
| `onOpenChange` | `(open: boolean) => void` | |
|
|
62
|
+
| `onOpenChange` | `(open: boolean) => void` | | Chamado quando o painel abre ou fecha; atualize `open` com o valor recebido. |
|
|
63
|
+
| `defaultOpen` | `boolean` | `false` | Estado inicial no modo não controlado. |
|
|
64
|
+
| `modal` | `boolean` | `false` | Com `true`, prende o foco no painel e bloqueia a interação com o restante da página enquanto ele está aberto. |
|
|
65
|
+
|
|
66
|
+
## Propriedades de PopoverTrigger
|
|
67
|
+
|
|
68
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| `asChild` | `boolean` | `false` | Faz do filho (por exemplo, um `Button`) o gatilho, em vez de renderizar outro `<button>`. |
|
|
51
71
|
|
|
52
72
|
## Propriedades de PopoverAnchor
|
|
53
73
|
|
|
@@ -59,5 +79,22 @@ não introduzir um nó extra; sem ele, o `PopoverTrigger` é a âncora.
|
|
|
59
79
|
|
|
60
80
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
61
81
|
|---|---|---|---|
|
|
82
|
+
| `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` | Lado preferido do painel; sem espaço, ele abre no lado oposto. |
|
|
62
83
|
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alinhamento do painel em relação ao gatilho. |
|
|
63
84
|
| `sideOffset` | `number` | `4` | Distância entre o gatilho e o painel. |
|
|
85
|
+
| `className` | `string` | | Classes do painel. A largura padrão é `w-72` (`18rem`). |
|
|
86
|
+
|
|
87
|
+
## Propriedades de PopoverHeader
|
|
88
|
+
|
|
89
|
+
`PopoverHeader` empilha `PopoverTitle` e `PopoverDescription` com espaçamento curto. Não tem props
|
|
90
|
+
próprias; aceita as de um elemento `div`.
|
|
91
|
+
|
|
92
|
+
## Propriedades de PopoverTitle
|
|
93
|
+
|
|
94
|
+
`PopoverTitle` destaca o nome do painel com peso médio. Ele renderiza um `div`, sem nível de
|
|
95
|
+
heading. Não tem props próprias; aceita as de um elemento `div`.
|
|
96
|
+
|
|
97
|
+
## Propriedades de PopoverDescription
|
|
98
|
+
|
|
99
|
+
`PopoverDescription` apresenta o texto de apoio em cor atenuada. Não tem props próprias; aceita as
|
|
100
|
+
de um elemento `p`.
|
|
@@ -1,45 +1,11 @@
|
|
|
1
|
-
`Presentation`
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Dialog e Drawer mantêm no cabeçalho a ordem horizontal de navegação ou retorno, título e ações.
|
|
6
|
-
O retorno aparece em uma surface modal somente
|
|
7
|
-
quando ela foi aberta sobre outro Dialog ou Drawer; a Page ao fundo sustenta o modal, mas não cria
|
|
8
|
-
uma etapa de navegação. Formulários modais dimensionam o footer pelo conteúdo, com `Cancelar` em
|
|
9
|
-
`ghost` e a ação principal sólida.
|
|
10
|
-
Em Page hospedada por `PageShell`, a barra concentra navegação e ações globais. Uma Presentation
|
|
11
|
-
de listagem materializa título e comandos de header no `Content` que envolve o `ActionList`; assim
|
|
12
|
-
a criação permanece próxima da coleção. Esses comandos usam o tamanho `default`: o que abre outra
|
|
13
|
-
Presentation é um `Button` na variante padrão (`solid`), e uma action `simple` vira um
|
|
14
|
-
`ActionTrigger` na variante `ghost`. Nas demais Presentations, o título abre o conteúdo em
|
|
15
|
-
`PageIntro` e os comandos da superfície seguem a mesma regra.
|
|
16
|
-
|
|
17
|
-
Use esse padrão quando uma lista puder abrir um detalhe lateral, quando a mesma edição precisar
|
|
18
|
-
funcionar em modal e em rota própria ou quando um fluxo começar compacto e crescer sem ganhar uma
|
|
19
|
-
segunda implementação.
|
|
20
|
-
|
|
21
|
-
Dentro de `PageShell`, a superfície `page` usa a anatomia real da aplicação: a Presentation projeta
|
|
22
|
-
navigation e actions globais por `PageHeader`, mantém a introdução no conteúdo quando existir e
|
|
23
|
-
compõe `PageBody` e, quando necessário, `PageFooter`. Para uma action `list`, `PageBody` recebe
|
|
24
|
-
`Content + ActionList`; o header da coleção recebe o título e os comandos declarados no placement
|
|
25
|
-
`header`. Dialog e Drawer continuam usando o header da própria superfície.
|
|
26
|
-
Os comandos da coleção ficam no extremo oposto ao título e permanecem textuais, mesmo quando a
|
|
27
|
-
action declara um ícone. A criação usa `Criar recurso`, sem ícone, em um `Button` na variante
|
|
28
|
-
padrão (`solid`) e no tamanho `default`.
|
|
29
|
-
Não envolva a Presentation em um card para simular a página; valide proporção, rolagem e ações no
|
|
30
|
-
shell que efetivamente hospeda a rota.
|
|
1
|
+
Use `Presentation` quando o mesmo recurso precisa aparecer ora como página, ora como dialog, ora
|
|
2
|
+
como drawer: uma lista que abre o detalhe ao lado, uma edição que funciona em modal e em rota
|
|
3
|
+
própria, um fluxo que começa compacto e cresce sem ganhar uma segunda implementação. A superfície
|
|
4
|
+
decide quanto espaço ocupar; o recurso mantém a mesma navegação, cabeçalho, corpo e rodapé.
|
|
31
5
|
|
|
32
|
-
O exemplo declara
|
|
6
|
+
O exemplo declara uma Presentation sobre um contrato de formulário e alterna a superfície. O
|
|
33
7
|
`DocBrowserActionProvider` simula o cliente de actions; na aplicação, esse papel é do provider real.
|
|
34
8
|
|
|
35
|
-
Quando o recurso aceita assistência contextual, declare somente o gatilho serializável em
|
|
36
|
-
`assistant`. O renderer coloca esse gatilho no cabeçalho e abre o painel à direita do body da
|
|
37
|
-
própria Page, Dialog ou Drawer. Cabeçalho e rodapé continuam ocupando toda a largura da superfície;
|
|
38
|
-
enquanto o painel está aberto, o gatilho sai do cabeçalho e o próprio painel assume sua identidade
|
|
39
|
-
visual. O dado vivo do recurso não entra na definição: a página que o carregou fornece
|
|
40
|
-
`assistant.render`, captura identidade, nome e demais dados autorizados e monta o chat. Assim,
|
|
41
|
-
contexto de conversa não vira autorização nem dado persistido no manifest.
|
|
42
|
-
|
|
43
9
|
```tsx live
|
|
44
10
|
const workspacePresentation = definePresentation({
|
|
45
11
|
schemaVersion: 1,
|
|
@@ -64,7 +30,7 @@ function Example() {
|
|
|
64
30
|
<DocBrowserActionProvider>
|
|
65
31
|
<div className="space-y-4">
|
|
66
32
|
<ButtonGroup mode="spaced">
|
|
67
|
-
{["page", "dialog", "drawer"].map((value) => (
|
|
33
|
+
{(["page", "dialog", "drawer"] as const).map((value) => (
|
|
68
34
|
<Button
|
|
69
35
|
key={value}
|
|
70
36
|
size="sm"
|
|
@@ -115,14 +81,48 @@ function Example() {
|
|
|
115
81
|
render(<Example />);
|
|
116
82
|
```
|
|
117
83
|
|
|
84
|
+
## Como cada superfície apresenta o recurso
|
|
85
|
+
|
|
86
|
+
Em Dialog e Drawer, o cabeçalho da própria superfície reúne, nesta ordem, o retorno, o título e as
|
|
87
|
+
ações. O retorno só aparece quando o modal foi aberto sobre outro Dialog ou Drawer: a página ao
|
|
88
|
+
fundo sustenta o modal, mas não é uma etapa para onde voltar. Formulários modais dimensionam o
|
|
89
|
+
rodapé pelo conteúdo, com `Cancelar` em `ghost` e a ação principal sólida.
|
|
90
|
+
|
|
91
|
+
Dentro de [`PageShell`](/ui/page), a superfície `page` usa a anatomia real da aplicação. O
|
|
92
|
+
`PageHeader` recebe só a navegação e as ações globais; o título abre o conteúdo em `PageIntro`, e o
|
|
93
|
+
corpo vai para `PageBody`, com `PageFooter` quando há ações de rodapé.
|
|
94
|
+
|
|
95
|
+
Uma Presentation de listagem exibida como página muda essa distribuição para manter a criação perto
|
|
96
|
+
da coleção. O `PageBody` recebe um [`Content`](/ui/content) com a
|
|
97
|
+
[`ActionList`](/ui/action-list), e o cabeçalho desse `Content` recebe o título e os comandos
|
|
98
|
+
declarados com `placement: "header"`, no extremo oposto ao título. Os comandos de cabeçalho usam o
|
|
99
|
+
tamanho `default` e permanecem textuais, mesmo quando a action declara um ícone: o que abre outra
|
|
100
|
+
Presentation é um `Button` sólido, e uma action `simple` vira um
|
|
101
|
+
[`ActionTrigger`](/ui/action-trigger) `ghost`. O rótulo vem do `label` da action, como `Criar workspace`.
|
|
102
|
+
|
|
103
|
+
Não envolva a Presentation em um card para simular a página. Valide proporção, rolagem e ações no
|
|
104
|
+
shell que efetivamente hospeda a rota.
|
|
105
|
+
|
|
106
|
+
## Assistência contextual
|
|
107
|
+
|
|
108
|
+
Quando o recurso aceita uma conversa sobre ele, declare em `assistant` somente o gatilho
|
|
109
|
+
serializável, como no exemplo acima. O renderer coloca o gatilho no cabeçalho e abre o painel à
|
|
110
|
+
direita do corpo da própria página, dialog ou drawer. Cabeçalho e rodapé continuam ocupando toda a
|
|
111
|
+
largura; enquanto o painel está aberto, o gatilho sai do cabeçalho e o painel assume a identidade
|
|
112
|
+
visual da assistência.
|
|
113
|
+
|
|
114
|
+
O dado vivo do recurso não entra na definição. A página que carregou o recurso fornece
|
|
115
|
+
`assistant.render`, reúne identidade, nome e demais dados autorizados e monta o chat. Assim, o
|
|
116
|
+
contexto da conversa não vira autorização nem dado persistido no manifest.
|
|
117
|
+
|
|
118
118
|
## A declaração
|
|
119
119
|
|
|
120
120
|
`definePresentation` cria o artefato serializável. O body recebe uma action `form`, `list` ou
|
|
121
|
-
`view
|
|
122
|
-
`body.submitLabel` sobrescreve o padrão
|
|
123
|
-
como “Renomear” ou “Criar nova versão”.
|
|
124
|
-
interface abrem outra Presentation. Bindings fornecem valores vindos da
|
|
125
|
-
seleção, da sessão ou do resultado anterior sem guardar callbacks no JSON.
|
|
121
|
+
`view`, ou um componente React, descrito em “Conteúdo React no body”; uma action `simple` aparece
|
|
122
|
+
como comando no cabeçalho ou no rodapé. Em formulário, `body.submitLabel` sobrescreve o padrão
|
|
123
|
+
“Salvar” quando a operação tem outro resultado observável, como “Renomear” ou “Criar nova versão”.
|
|
124
|
+
Actions que precisam de interface abrem outra Presentation. Bindings fornecem valores vindos da
|
|
125
|
+
rota, do registro, da seleção, da sessão ou do resultado anterior sem guardar callbacks no JSON.
|
|
126
126
|
|
|
127
127
|
Quando a exibição precisa sobreviver a refresh, deep link ou histórico, declare `route` na própria
|
|
128
128
|
Presentation. O `path` aceita segmentos fixos e parâmetros como `:id`; `surface` determina como a
|
|
@@ -154,6 +154,48 @@ const workspaceList = definePresentation({
|
|
|
154
154
|
});
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
## Conteúdo React no body
|
|
158
|
+
|
|
159
|
+
Uma tela que já existe em React pode adotar a casca da Presentation — rota, superfície, título,
|
|
160
|
+
navegação e ações — sem migrar o miolo para actions declarativas de uma vez. Declare
|
|
161
|
+
`body: { component }` com um identificador estável para esse conteúdo e passe o conteúdo como
|
|
162
|
+
`children` do `Presentation`:
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
const appearance = definePresentation({
|
|
166
|
+
schemaVersion: 1,
|
|
167
|
+
id: "settings.appearance",
|
|
168
|
+
title: "Aparência",
|
|
169
|
+
route: { path: "/settings/appearance", surface: "page" },
|
|
170
|
+
body: { component: "settings.appearance.content" },
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
<Presentation
|
|
174
|
+
definition={appearance}
|
|
175
|
+
definitions={[appearance]}
|
|
176
|
+
actions={{}}
|
|
177
|
+
invocation={invocation}
|
|
178
|
+
>
|
|
179
|
+
<AppearanceSettings />
|
|
180
|
+
</Presentation>;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
O identificador de `component` vai para o manifest; o JSX, não. Uma Presentation com body de
|
|
184
|
+
componente exige `children`, e uma com body de action os recusa: nos dois casos, a renderização
|
|
185
|
+
lança um erro que nomeia a Presentation.
|
|
186
|
+
|
|
187
|
+
Com `heading: "shell"`, o padrão, a casca emite o título da definição. Quando o conteúdo React já
|
|
188
|
+
declara o heading principal da página, como um `Content level={1}`, use `heading: "content"`: dentro
|
|
189
|
+
de `PageShell`, a casca deixa de emitir o `PageIntro` e o documento continua com um único `h1`. Em
|
|
190
|
+
Dialog e Drawer, o título continua no cabeçalho da superfície.
|
|
191
|
+
|
|
192
|
+
Se o conteúdo precisa ocupar a altura da página e controlar a própria rolagem, como uma lista com
|
|
193
|
+
cabeçalho fixo, passe `layout="fill"` ao `Presentation`. Com o padrão `flow`, a página rola como um
|
|
194
|
+
documento. Uma [`ActionList`](/ui/action-list) filha direta do conteúdo React recebe essa altura e
|
|
195
|
+
assume `fill` sozinha.
|
|
196
|
+
|
|
197
|
+
## Registrar as declarações
|
|
198
|
+
|
|
157
199
|
Registre as declarações em `presentations` no `opus.config.ts`. `opus gen` valida as referências e
|
|
158
200
|
projeta o JSON no manifest; a Lens pode então exibir o artefato completo sem reconstruí-lo a partir
|
|
159
201
|
do JSX.
|
|
@@ -161,7 +203,7 @@ do JSX.
|
|
|
161
203
|
## A invocação
|
|
162
204
|
|
|
163
205
|
`definePresentationInvocation` cria o estado vivo de uma exibição. Ele contém somente valores JSON,
|
|
164
|
-
a
|
|
206
|
+
a superfície atual e a pilha necessária para voltar. A definição continua estática no manifest; input
|
|
165
207
|
de rota, sessão e resultado existe apenas na invocação.
|
|
166
208
|
|
|
167
209
|
```ts
|
|
@@ -194,7 +236,7 @@ no manifest e concentre a inspeção na Lens em vez de criar um launcher flutuan
|
|
|
194
236
|
| `definition` | `PresentationDefinition` | | Artefato estático que descreve o recurso. |
|
|
195
237
|
| `definitions` | `PresentationDefinition[]` | | Registry alcançável por navegação, abertura e retorno. |
|
|
196
238
|
| `actions` | `PresentationActionRegistry` | | Contratos compartilháveis referenciados pelo artefato. |
|
|
197
|
-
| `invocation` | `PresentationInvocation` | |
|
|
239
|
+
| `invocation` | `PresentationInvocation` | | Superfície, input e pilha da exibição atual. |
|
|
198
240
|
| `bindingContext` | `PresentationBindingContext` | | Rota, item, seleção, sessão e resultado disponíveis. |
|
|
199
241
|
| `assistant` | `PresentationAssistantRuntime` | | Painel contextual fornecido pela superfície hospedeira. |
|
|
200
242
|
| `onInvocationChange` | `(next: PresentationInvocation \| null) => void` | | Recebe navegação, retorno e fechamento. |
|
|
@@ -205,6 +247,8 @@ no manifest e concentre a inspeção na Lens em vez de criar um launcher flutuan
|
|
|
205
247
|
| `onOpenChange` | `(open: boolean) => void` | | Notifica abertura e fechamento da superfície modal. |
|
|
206
248
|
| `className` | `string` | | Classes adicionais da superfície. |
|
|
207
249
|
| `bodyClassName` | `string` | | Classes adicionais do body, aplicadas uma única vez. |
|
|
250
|
+
| `layout` | `'flow' \| 'fill'` | `'flow'` | Em `page`, `fill` limita a altura ao shell e entrega a rolagem ao conteúdo do body; a lista de um body `list` ocupa essa altura e rola só os resultados. Dialog e Drawer ignoram. |
|
|
251
|
+
| `children` | `ReactNode` | | Conteúdo React do body declarado com `body.component`. Obrigatório nesse caso e recusado com `body.action`. |
|
|
208
252
|
|
|
209
253
|
## Propriedades de PresentationInspector
|
|
210
254
|
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
## Progresso determinado
|
|
2
2
|
|
|
3
|
-
Use `Progress` quando a tarefa informar
|
|
4
|
-
100 e anima o preenchimento a cada mudança.
|
|
3
|
+
Use `Progress` quando a tarefa informar quanto já foi concluído. `value` aceita valores de 0 a
|
|
4
|
+
`max`, que por padrão é 100, e anima o preenchimento a cada mudança. Para contar etapas, declare
|
|
5
|
+
`max` com o total: `value={3} max={4}` preenche três quartos da barra. Quando a duração da espera
|
|
6
|
+
não é conhecida, use [`Spinner`](/ui/spinner).
|
|
7
|
+
|
|
8
|
+
A barra é exposta aos leitores de tela como `progressbar`, com o valor atual, mas o texto ao lado
|
|
9
|
+
dela não a nomeia automaticamente. Ligue o rótulo visível à barra com `aria-labelledby` ou, sem
|
|
10
|
+
rótulo visível, passe `aria-label`.
|
|
5
11
|
|
|
6
12
|
```tsx preview col
|
|
7
13
|
<div className="w-full max-w-sm space-y-2">
|
|
8
14
|
<div className="flex items-center justify-between text-sm">
|
|
9
|
-
<span>opus check</span>
|
|
15
|
+
<span id="progress-check">opus check</span>
|
|
10
16
|
<span className="text-muted-foreground">65%</span>
|
|
11
17
|
</div>
|
|
12
|
-
<Progress value={65} />
|
|
18
|
+
<Progress value={65} aria-labelledby="progress-check" />
|
|
13
19
|
</div>
|
|
14
20
|
```
|
|
15
21
|
|
|
22
|
+
Sem `value`, ou com `null`, a barra fica vazia e é exposta como progresso indeterminado.
|
|
23
|
+
|
|
16
24
|
## Controlado
|
|
17
25
|
|
|
18
26
|
Controle `value` externamente quando o progresso acompanhar uma tarefa em andamento. No exemplo,
|
|
@@ -20,15 +28,16 @@ cada clique avança uma etapa da sincronização.
|
|
|
20
28
|
|
|
21
29
|
```tsx preview col
|
|
22
30
|
const [step, setStep] = useState(40)
|
|
31
|
+
const labelId = useId()
|
|
23
32
|
const advance = (): void => setStep((prev) => Math.min(prev + 20, 100))
|
|
24
33
|
|
|
25
34
|
render(
|
|
26
35
|
<div className="w-full max-w-sm space-y-3">
|
|
27
36
|
<div className="flex items-center justify-between text-sm">
|
|
28
|
-
<span>Sincronizar Empresa X</span>
|
|
37
|
+
<span id={labelId}>Sincronizar Empresa X</span>
|
|
29
38
|
<span className="text-muted-foreground">{step}%</span>
|
|
30
39
|
</div>
|
|
31
|
-
<Progress value={step} />
|
|
40
|
+
<Progress value={step} aria-labelledby={labelId} />
|
|
32
41
|
<Button variant="outline" size="sm" onClick={advance}>
|
|
33
42
|
Avançar etapa
|
|
34
43
|
</Button>
|
|
@@ -39,18 +48,19 @@ render(
|
|
|
39
48
|
## Altura e contexto
|
|
40
49
|
|
|
41
50
|
`context` diz o que o andamento comunica — `success` para meta atingida, `warning` ou `danger`
|
|
42
|
-
para atenção — e tinge trilha e preenchimento com a família semântica.
|
|
43
|
-
|
|
51
|
+
para atenção — e tinge trilha e preenchimento com a família semântica. A cor reforça o significado,
|
|
52
|
+
mas não o substitui: mantenha o rótulo e o valor legíveis. `className` compõe sobre o padrão para
|
|
53
|
+
ajustar a altura.
|
|
44
54
|
|
|
45
55
|
```tsx preview col
|
|
46
56
|
<div className="w-full max-w-sm space-y-4">
|
|
47
57
|
<div className="space-y-1.5">
|
|
48
|
-
<span className="text-sm">Cobertura de testes</span>
|
|
49
|
-
<Progress value={92} context="success" className="h-1.5" />
|
|
58
|
+
<span id="progress-coverage" className="text-sm">Cobertura de testes</span>
|
|
59
|
+
<Progress value={92} context="success" className="h-1.5" aria-labelledby="progress-coverage" />
|
|
50
60
|
</div>
|
|
51
61
|
<div className="space-y-1.5">
|
|
52
|
-
<span className="text-sm">Skills cobertas pelo revisor</span>
|
|
53
|
-
<Progress value={45} context="warning" className="h-3" />
|
|
62
|
+
<span id="progress-review" className="text-sm">Skills cobertas pelo revisor</span>
|
|
63
|
+
<Progress value={45} context="warning" className="h-3" aria-labelledby="progress-review" />
|
|
54
64
|
</div>
|
|
55
65
|
</div>
|
|
56
66
|
```
|
|
@@ -59,6 +69,9 @@ padrão para ajustar a altura.
|
|
|
59
69
|
|
|
60
70
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
61
71
|
|---|---|---|---|
|
|
62
|
-
| `value` | `number \| null` | | O progresso de 0 a
|
|
72
|
+
| `value` | `number \| null` | | O progresso de 0 a `max`. O preenchimento anima a cada mudança; sem valor, a barra fica vazia e indeterminada. |
|
|
73
|
+
| `max` | `number` | `100` | O valor que representa a conclusão. O preenchimento é a fração `value / max`. |
|
|
63
74
|
| `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'primary'` | O que o andamento comunica; tinge trilha e preenchimento pela família `--context-*`. |
|
|
64
|
-
| `
|
|
75
|
+
| `aria-label` | `string` | | Nome acessível da barra quando não há rótulo visível. |
|
|
76
|
+
| `aria-labelledby` | `string` | | `id` do rótulo visível que nomeia a barra. |
|
|
77
|
+
| `className` | `string` | | Compõe sobre o padrão — ajuste a altura (`h-1.5`, `h-3`). |
|
|
@@ -5,7 +5,7 @@ title: Filas
|
|
|
5
5
|
# Filas
|
|
6
6
|
|
|
7
7
|
Use uma action em background quando a operação não puder manter a requisição aberta. O
|
|
8
|
-
`QueueAdapter` enfileira o trabalho e devolve um `JobHandle` para
|
|
8
|
+
`QueueAdapter` enfileira o trabalho e devolve um `JobHandle` para acompanhar o andamento.
|
|
9
9
|
|
|
10
10
|
## O contrato
|
|
11
11
|
|
|
@@ -21,18 +21,21 @@ interface JobHandle<T = unknown> {
|
|
|
21
21
|
jobId: string
|
|
22
22
|
action: string
|
|
23
23
|
status: 'queued' | 'running' | 'done' | 'failed' | 'cancelled'
|
|
24
|
-
progress?: { percent?: number; message?: string }
|
|
24
|
+
progress?: { percent?: number; message?: string; data?: unknown }
|
|
25
25
|
data?: T
|
|
26
26
|
error?: ActionError
|
|
27
|
+
enqueuedAt: string
|
|
28
|
+
startedAt?: string
|
|
29
|
+
finishedAt?: string
|
|
27
30
|
attempts: number
|
|
28
31
|
trace?: TraceContext
|
|
29
32
|
}
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
`runtime.execute()` valida a chamada, cria o `JobSpec` e inclui o trace efetivo, request ID e
|
|
33
|
-
provenance original. O adapter persiste
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
`runtime.execute()` valida a chamada, cria o `JobSpec` e inclui o trace efetivo, o request ID e a
|
|
36
|
+
provenance original. O adapter persiste esse envelope, e o handle também expõe o trace. No worker,
|
|
37
|
+
`runtime.executeJob()` revalida o envelope e executa um span filho, preservando a cadeia requisição
|
|
38
|
+
→ enfileiramento → job sem aceitar baggage ou campos extras.
|
|
36
39
|
|
|
37
40
|
## Driver
|
|
38
41
|
|
|
@@ -43,26 +46,41 @@ import { bullmqQueue } from '@softize/opus/queue/bullmq'
|
|
|
43
46
|
const queue = bullmqQueue({ queue: new Queue('opus', { connection: { host: 'localhost' } }) })
|
|
44
47
|
```
|
|
45
48
|
|
|
46
|
-
Redis
|
|
47
|
-
|
|
49
|
+
O driver usa Redis por meio do BullMQ, com durabilidade, entrega entre processos e novas tentativas.
|
|
50
|
+
`bullmq` é dependência opcional: só precisa ser instalado por quem usa o driver.
|
|
48
51
|
|
|
49
|
-
##
|
|
52
|
+
## Declarar a action em background
|
|
53
|
+
|
|
54
|
+
Configure a fila no runtime e declare `background` no binding da action:
|
|
50
55
|
|
|
51
56
|
```ts
|
|
52
57
|
const runtime = createRuntime({
|
|
53
58
|
// …server/data…
|
|
54
59
|
queue,
|
|
55
60
|
})
|
|
61
|
+
|
|
62
|
+
export const relatorioGerar = bindAction(relatorioGerarContract, {
|
|
63
|
+
background: { enabled: true, retry: { attempts: 3 }, progress: true },
|
|
64
|
+
handler: async (ctx, input, loaded, progress) => {
|
|
65
|
+
await progress?.report({ percent: 50, message: 'Consultando dados' })
|
|
66
|
+
return gerarRelatorio(input)
|
|
67
|
+
},
|
|
68
|
+
})
|
|
56
69
|
```
|
|
57
70
|
|
|
58
|
-
|
|
59
|
-
`
|
|
60
|
-
`
|
|
71
|
+
Só actions `simple` e `form` executam em background; além de `retry` e `progress`, a configuração
|
|
72
|
+
aceita `queue`, `priority` e `timeout`. Em uma action `list` ou `view`, o tipo do binding aceita
|
|
73
|
+
`background`, mas o runtime o ignora e executa a action na própria requisição. Para evitar essa
|
|
74
|
+
surpresa, o runtime avisa ao registrar a action fora de produção (`runtime.action_field_ignored`), e
|
|
75
|
+
o [`opus check`](/docs/cli) aponta a regra `ignored-by-kind`. Sem `QueueAdapter`, a chamada falha com
|
|
76
|
+
`runtime.queue_required`. Chamar a action devolve um `JobHandle` em vez do resultado. O Opus não
|
|
77
|
+
monta uma rota de acompanhamento: exponha `queue.status(jobId)` ou `queue.subscribe` por uma action
|
|
78
|
+
ou rota do próprio app até o job chegar a `done` ou `failed`.
|
|
61
79
|
|
|
62
80
|
## Worker BullMQ
|
|
63
81
|
|
|
64
|
-
O app continua dono do `Worker` e
|
|
65
|
-
`executeJob()
|
|
82
|
+
O app continua dono do `Worker` e de reconstruir a autenticação de quem enfileirou. O processor
|
|
83
|
+
chama `executeJob()`, e não `execute()`, que criaria outro job:
|
|
66
84
|
|
|
67
85
|
```ts
|
|
68
86
|
new Worker('opus', async (job) => {
|
|
@@ -76,13 +94,13 @@ new Worker('opus', async (job) => {
|
|
|
76
94
|
}, { connection })
|
|
77
95
|
```
|
|
78
96
|
|
|
79
|
-
O runtime confirma que o
|
|
80
|
-
trace e provenance vêm do envelope. O driver
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
envelope para o worker aplicar,
|
|
97
|
+
O runtime confirma que o usuário reconstruído corresponde a `spec.ctx.userId`; tenant, request,
|
|
98
|
+
trace e provenance vêm do envelope. O driver traduz `attempts`, o backoff nativo e a prioridade para
|
|
99
|
+
o BullMQ. Um backoff exponencial com multiplicador diferente de 2 ou com `maxMs` exige uma
|
|
100
|
+
`backoffStrategy` própria no worker, e o driver o recusa em vez de ignorá-lo em silêncio. `timeout`
|
|
101
|
+
permanece no envelope para o worker aplicar, porque não é uma opção de execução do `Queue.add`.
|
|
84
102
|
|
|
85
103
|
## Limites atuais
|
|
86
104
|
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
O único driver é o `bullmq`. Uma fila em memória para desenvolvimento e outros backends, como SQS e
|
|
106
|
+
pg-boss, ainda não existem.
|