@softize/opus 22.1.0 → 23.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 +74 -1
- package/bin/cli.mjs +6 -5
- package/bin/lib/check.mjs +23 -8
- package/bin/lib/copy.mjs +1 -1
- package/bin/lib/gen-manifest.mjs +2 -0
- package/bin/lib/gen-runner.mjs +7 -0
- package/bin/lib/gen-stubs.mjs +3 -1
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +1 -1
- package/docs/adr/0032-tab-baseline-respects-the-container-inset.md +16 -12
- package/docs/adr/0034-tabs-can-own-a-frame-without-owning-a-surface.md +8 -6
- package/docs/adr/0036-tabs-declares-trigger-distribution.md +11 -8
- package/docs/adr/0037-design-data-lives-outside-the-contract.md +1 -1
- package/docs/adr/0038-content-level-defines-primary-presentation.md +62 -0
- package/docs/adr/0039-filter-presentation-is-independent-from-filter-type.md +62 -0
- package/docs/adr/0040-tabs-use-a-single-navigation-presentation.md +60 -0
- package/docs/adr/0041-actions-declare-effect-and-personal-data.md +139 -0
- package/docs/code-style.md +1 -1
- package/docs/protocol.md +15 -2
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/references/ui-patterns.md +1 -1
- package/registry/skills/create-opus-action/SKILL.md +7 -2
- package/registry/skills/create-opus-action/references/contract-and-binding.md +10 -5
- package/registry/skills/create-opus-action/references/evaluations.md +3 -0
- package/registry/skills/create-opus-action/scripts/scaffold.mjs +6 -0
- package/registry/templates/app/src/domains/tasks/actions/list.ts +2 -0
- package/src/core/action-nature.ts +40 -0
- package/src/core/audit.ts +12 -4
- package/src/core/contracts.ts +1 -1
- package/src/core/index.ts +2 -0
- package/src/core/runtime.ts +57 -35
- package/src/core/types.ts +45 -23
- package/src/design/index.ts +14 -3
- package/src/mcp/index.ts +6 -0
- package/src/testing/index.ts +6 -26
- package/src/ui/components/patterns/content-header.tsx +5 -9
- package/src/ui/components/patterns/list.tsx +55 -1
- package/src/ui/components/patterns/presentation.tsx +1 -1
- package/src/ui/components/primitives/icon-picker.tsx +6 -0
- package/src/ui/components/primitives/tabs.tsx +55 -87
- package/src/ui/components/primitives/toggle.tsx +1 -1
- package/src/ui/docs/content/action-list.md +13 -4
- package/src/ui/docs/content/actions.md +36 -8
- package/src/ui/docs/content/content.md +5 -6
- package/src/ui/docs/content/mcp.md +6 -0
- package/src/ui/docs/content/semantic-context.md +0 -1
- package/src/ui/docs/content/tabs.md +68 -69
- package/src/ui/docs/content/testing.md +2 -3
- package/src/ui/docs/content/toggle.md +15 -10
- package/src/ui/drivers/react.tsx +4 -3
- package/src/ui/meta.ts +2 -2
- package/src/ui/react.tsx +2 -3
- package/src/vite/design.ts +23 -21
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
## Alternar painéis relacionados
|
|
2
2
|
|
|
3
|
-
Use `Tabs` para alternar painéis relacionados no mesmo contexto. O `value` de cada `
|
|
4
|
-
corresponde ao `
|
|
3
|
+
Use `Tabs` para alternar painéis relacionados no mesmo contexto. O `value` de cada `TabsListItem`
|
|
4
|
+
corresponde ao `TabsPanel` que ele abre. `defaultValue` define a aba inicial no modo não
|
|
5
5
|
controlado.
|
|
6
6
|
|
|
7
7
|
`icon` posiciona um ícone decorativo antes do rótulo e normaliza SVGs para `1rem`. Quando a aba
|
|
@@ -10,31 +10,31 @@ mostrar somente o ícone, declare `aria-label` para preservar seu nome acessíve
|
|
|
10
10
|
```tsx preview col
|
|
11
11
|
<Tabs defaultValue="sessions">
|
|
12
12
|
<TabsList>
|
|
13
|
-
<
|
|
13
|
+
<TabsListItem value="overview" icon={<LayoutDashboard />}>
|
|
14
14
|
Visão geral
|
|
15
|
-
</
|
|
16
|
-
<
|
|
15
|
+
</TabsListItem>
|
|
16
|
+
<TabsListItem value="sessions" icon={<History />}>
|
|
17
17
|
Sessões
|
|
18
|
-
</
|
|
19
|
-
<
|
|
18
|
+
</TabsListItem>
|
|
19
|
+
<TabsListItem value="skills" icon={<Settings />}>
|
|
20
20
|
Skills
|
|
21
|
-
</
|
|
21
|
+
</TabsListItem>
|
|
22
22
|
</TabsList>
|
|
23
|
-
<
|
|
23
|
+
<TabsPanel value="overview">
|
|
24
24
|
<p className="text-sm text-muted-foreground">
|
|
25
25
|
Resumo do workspace Empresa X.
|
|
26
26
|
</p>
|
|
27
|
-
</
|
|
28
|
-
<
|
|
27
|
+
</TabsPanel>
|
|
28
|
+
<TabsPanel value="sessions">
|
|
29
29
|
<p className="text-sm text-muted-foreground">
|
|
30
30
|
3 sessões nesta semana, 1 em andamento.
|
|
31
31
|
</p>
|
|
32
|
-
</
|
|
33
|
-
<
|
|
32
|
+
</TabsPanel>
|
|
33
|
+
<TabsPanel value="skills">
|
|
34
34
|
<p className="text-sm text-muted-foreground">
|
|
35
35
|
6 skills vinculadas aos agentes do workspace.
|
|
36
36
|
</p>
|
|
37
|
-
</
|
|
37
|
+
</TabsPanel>
|
|
38
38
|
</Tabs>
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -47,20 +47,20 @@ moldura.
|
|
|
47
47
|
|
|
48
48
|
```tsx preview col
|
|
49
49
|
<Tabs defaultValue="overview" framed className="gap-0">
|
|
50
|
-
<TabsList
|
|
51
|
-
<
|
|
52
|
-
<
|
|
50
|
+
<TabsList className="px-2 pt-2">
|
|
51
|
+
<TabsListItem value="overview">Visão geral</TabsListItem>
|
|
52
|
+
<TabsListItem value="history">Histórico</TabsListItem>
|
|
53
53
|
</TabsList>
|
|
54
|
-
<
|
|
54
|
+
<TabsPanel value="overview" className="p-4">
|
|
55
55
|
<p className="text-sm text-muted-foreground">
|
|
56
56
|
Resumo do workspace Empresa X.
|
|
57
57
|
</p>
|
|
58
|
-
</
|
|
59
|
-
<
|
|
58
|
+
</TabsPanel>
|
|
59
|
+
<TabsPanel value="history" className="p-4">
|
|
60
60
|
<p className="text-sm text-muted-foreground">
|
|
61
61
|
Alterações recentes do workspace.
|
|
62
62
|
</p>
|
|
63
|
-
</
|
|
63
|
+
</TabsPanel>
|
|
64
64
|
</Tabs>
|
|
65
65
|
```
|
|
66
66
|
|
|
@@ -73,24 +73,23 @@ altera os painéis associados.
|
|
|
73
73
|
|
|
74
74
|
```tsx preview col
|
|
75
75
|
<Tabs defaultValue="overview" distribution="equal">
|
|
76
|
-
<TabsList
|
|
77
|
-
<
|
|
78
|
-
<
|
|
79
|
-
<
|
|
76
|
+
<TabsList>
|
|
77
|
+
<TabsListItem value="overview">Visão geral</TabsListItem>
|
|
78
|
+
<TabsListItem value="contacts">Contatos</TabsListItem>
|
|
79
|
+
<TabsListItem value="history">Histórico</TabsListItem>
|
|
80
80
|
</TabsList>
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
<
|
|
81
|
+
<TabsPanel value="overview">Resumo do atendimento.</TabsPanel>
|
|
82
|
+
<TabsPanel value="contacts">Tentativas de contato.</TabsPanel>
|
|
83
|
+
<TabsPanel value="history">Alterações registradas.</TabsPanel>
|
|
84
84
|
</Tabs>
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
##
|
|
87
|
+
## Linha de navegação
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
sem alcançar as laterais da superfície.
|
|
89
|
+
`TabsList` usa uma linha-base porque representa navegação entre painéis, não uma escolha de valor.
|
|
90
|
+
Na orientação horizontal, a lista ocupa toda a largura disponível e a aba ativa é marcada sobre
|
|
91
|
+
essa linha. A linha respeita o recuo do contêiner; dentro de `DrawerBody` e `DialogBody`, ela
|
|
92
|
+
acompanha o alinhamento do restante do corpo sem alcançar as laterais da superfície.
|
|
94
93
|
Cada item contém uma área interativa com a mesma altura de um Button do tamanho correspondente. A
|
|
95
94
|
área permanece transparente e recebe fundo apenas no hover; a aba ativa é indicada pela linha
|
|
96
95
|
primária inferior. Um respiro de `0.375rem` separa o controle dessa linha; em `Tabs framed`, ele
|
|
@@ -98,69 +97,68 @@ aumenta para `0.5rem` e acompanha o padding superior da moldura.
|
|
|
98
97
|
|
|
99
98
|
```tsx preview col
|
|
100
99
|
<Tabs defaultValue="agents">
|
|
101
|
-
<TabsList
|
|
102
|
-
<
|
|
103
|
-
<
|
|
104
|
-
<
|
|
100
|
+
<TabsList>
|
|
101
|
+
<TabsListItem value="agents">Agentes</TabsListItem>
|
|
102
|
+
<TabsListItem value="repos">Repositórios</TabsListItem>
|
|
103
|
+
<TabsListItem value="settings">Configurações</TabsListItem>
|
|
105
104
|
</TabsList>
|
|
106
|
-
<
|
|
105
|
+
<TabsPanel value="agents">
|
|
107
106
|
<p className="text-sm text-muted-foreground">
|
|
108
107
|
developer, reviewer e designer ativos.
|
|
109
108
|
</p>
|
|
110
|
-
</
|
|
111
|
-
<
|
|
109
|
+
</TabsPanel>
|
|
110
|
+
<TabsPanel value="repos">
|
|
112
111
|
<p className="text-sm text-muted-foreground">
|
|
113
112
|
empresa-x-api e empresa-x-web vinculados.
|
|
114
113
|
</p>
|
|
115
|
-
</
|
|
116
|
-
<
|
|
114
|
+
</TabsPanel>
|
|
115
|
+
<TabsPanel value="settings">
|
|
117
116
|
<p className="text-sm text-muted-foreground">Preferências do workspace.</p>
|
|
118
|
-
</
|
|
117
|
+
</TabsPanel>
|
|
119
118
|
</Tabs>
|
|
120
119
|
```
|
|
121
120
|
|
|
122
121
|
## Vertical
|
|
123
122
|
|
|
124
|
-
Com `orientation="vertical"`, a lista forma uma coluna
|
|
125
|
-
|
|
123
|
+
Com `orientation="vertical"`, a lista forma uma coluna e a marca da aba ativa passa para a lateral
|
|
124
|
+
do gatilho.
|
|
126
125
|
|
|
127
126
|
```tsx preview col
|
|
128
127
|
<Tabs defaultValue="prompt" orientation="vertical">
|
|
129
128
|
<TabsList>
|
|
130
|
-
<
|
|
131
|
-
<
|
|
132
|
-
<
|
|
129
|
+
<TabsListItem value="prompt">Prompt</TabsListItem>
|
|
130
|
+
<TabsListItem value="skills">Skills</TabsListItem>
|
|
131
|
+
<TabsListItem value="history">Histórico</TabsListItem>
|
|
133
132
|
</TabsList>
|
|
134
|
-
<
|
|
133
|
+
<TabsPanel value="prompt">
|
|
135
134
|
<p className="text-sm text-muted-foreground">
|
|
136
135
|
O prompt do agente developer.
|
|
137
136
|
</p>
|
|
138
|
-
</
|
|
139
|
-
<
|
|
137
|
+
</TabsPanel>
|
|
138
|
+
<TabsPanel value="skills">
|
|
140
139
|
<p className="text-sm text-muted-foreground">
|
|
141
140
|
Skills vinculadas a este agente.
|
|
142
141
|
</p>
|
|
143
|
-
</
|
|
144
|
-
<
|
|
142
|
+
</TabsPanel>
|
|
143
|
+
<TabsPanel value="history">
|
|
145
144
|
<p className="text-sm text-muted-foreground">
|
|
146
145
|
Sessões anteriores deste agente.
|
|
147
146
|
</p>
|
|
148
|
-
</
|
|
147
|
+
</TabsPanel>
|
|
149
148
|
</Tabs>
|
|
150
149
|
```
|
|
151
150
|
|
|
152
151
|
## Tamanho
|
|
153
152
|
|
|
154
|
-
`size="sm"` no `Tabs` reduz a
|
|
155
|
-
`Select` para uma fileira densa, como uma toolbar ou um cabeçalho
|
|
156
|
-
mais alto que os elementos vizinhos.
|
|
153
|
+
`size="sm"` no `Tabs` reduz a área interativa de `2.25rem` para `2rem`. É o par do `sm` de `Button` e
|
|
154
|
+
`Select` para uma fileira densa, como uma toolbar ou um cabeçalho.
|
|
157
155
|
|
|
158
156
|
```tsx preview col
|
|
159
157
|
<Tabs defaultValue="preview" size="sm">
|
|
160
158
|
<TabsList>
|
|
161
|
-
<
|
|
162
|
-
<
|
|
163
|
-
<
|
|
159
|
+
<TabsListItem value="preview">Preview</TabsListItem>
|
|
160
|
+
<TabsListItem value="code">Código</TabsListItem>
|
|
161
|
+
<TabsListItem value="ai">IA</TabsListItem>
|
|
164
162
|
</TabsList>
|
|
165
163
|
</Tabs>
|
|
166
164
|
```
|
|
@@ -172,28 +170,29 @@ mais alto que os elementos vizinhos.
|
|
|
172
170
|
| `defaultValue` | `string` | | Aba inicial no modo não controlado. |
|
|
173
171
|
| `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
|
|
174
172
|
| `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
|
|
175
|
-
| `size` | `'default' \| 'sm'` | `'default'` | Altura da
|
|
173
|
+
| `size` | `'default' \| 'sm'` | `'default'` | Altura da área interativa na escala única dos controles (2.25 e 2rem), aplicada aos gatilhos. |
|
|
176
174
|
| `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou divide igualmente a largura disponível entre os gatilhos. |
|
|
177
175
|
| `framed` | `boolean` | `false` | Aplica borda, raio e recorte ao conjunto sem definir fundo ou foreground. |
|
|
178
176
|
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
|
|
179
177
|
|
|
180
178
|
## Propriedades de TabsList
|
|
181
179
|
|
|
182
|
-
| Propriedade
|
|
183
|
-
|
|
|
184
|
-
| `
|
|
180
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
181
|
+
| ------------ | -------- | ------ | --------------------------------------------------------------- |
|
|
182
|
+
| `aria-label` | `string` | | Nomeia a navegação quando não houver um rótulo visível associado. |
|
|
183
|
+
| `className` | `string` | | Ajusta a composição externa sem substituir a linha de navegação. |
|
|
185
184
|
|
|
186
|
-
## Propriedades de
|
|
185
|
+
## Propriedades de TabsListItem
|
|
187
186
|
|
|
188
187
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
189
188
|
| ----------- | ----------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
190
|
-
| `value` | `string` | | Identificador que associa o gatilho ao `
|
|
189
|
+
| `value` | `string` | | Identificador que associa o gatilho ao `TabsPanel` correspondente. |
|
|
191
190
|
| `icon` | `ReactNode` | | Ícone decorativo antes do rótulo, em `1rem`. Em uma aba sem texto, declare também `aria-label`. |
|
|
192
191
|
| `disabled` | `boolean` | `false` | Impede a seleção da aba. |
|
|
193
192
|
|
|
194
|
-
## Propriedades de
|
|
193
|
+
## Propriedades de TabsPanel
|
|
195
194
|
|
|
196
195
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
197
196
|
| ------------ | -------- | ------ | ---------------------------------------------------------------------- |
|
|
198
|
-
| `value` | `string` | | Identificador do painel, igual ao `value` do `
|
|
197
|
+
| `value` | `string` | | Identificador do painel, igual ao `value` do `TabsListItem` que o abre. |
|
|
199
198
|
| `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa. |
|
|
@@ -90,9 +90,8 @@ expect(emitted).toEqual([{ event: 'task.done', data: { id: '1' } }])
|
|
|
90
90
|
|
|
91
91
|
Os dados que o modo design mostra não são comportamento da action e não precisam de teste de
|
|
92
92
|
unidade: o plugin `opusDesign()` os gera a partir do contrato (ver
|
|
93
|
-
[Actions](/docs/actions)).
|
|
94
|
-
|
|
95
|
-
handler em vez dele.
|
|
93
|
+
[Actions](/docs/actions)). `runAction` executa somente a action ligada ao handler; use cenários do
|
|
94
|
+
plugin para respostas específicas do modo design.
|
|
96
95
|
|
|
97
96
|
## memStorage
|
|
98
97
|
|
|
@@ -30,8 +30,8 @@ render(
|
|
|
30
30
|
|
|
31
31
|
## Variantes e tamanhos
|
|
32
32
|
|
|
33
|
-
Na variante `default`, o fundo aparece somente quando o controle está ativo
|
|
34
|
-
borda. `size` ajusta a altura.
|
|
33
|
+
Na variante `default`, o fundo aparece somente quando o controle está ativo. `outline` mantém a
|
|
34
|
+
borda e usa um fundo sutil distinto dela no estado ativo. `size` ajusta a altura.
|
|
35
35
|
|
|
36
36
|
```tsx preview
|
|
37
37
|
<Toggle aria-label="Quebra de linha">
|
|
@@ -140,19 +140,24 @@ render(
|
|
|
140
140
|
)
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
###
|
|
143
|
+
### Grupo conectado com borda
|
|
144
144
|
|
|
145
145
|
`variant`, `size` e `shape` definidos no grupo chegam aos itens por contexto. `spacing` separa os
|
|
146
|
-
itens
|
|
146
|
+
itens. Use `variant="outline"` com `spacing={0}` para formar um controle conectado com contorno
|
|
147
|
+
único. A borda pertence aos itens: o grupo remove as bordas internas duplicadas, arredonda apenas
|
|
148
|
+
as extremidades e preserva o contorno quando um item está ativo.
|
|
147
149
|
|
|
148
150
|
```tsx preview
|
|
149
|
-
<ToggleGroup type="single" variant="outline" spacing={
|
|
150
|
-
<ToggleGroupItem value="
|
|
151
|
-
<ToggleGroupItem value="
|
|
152
|
-
<ToggleGroupItem value="
|
|
151
|
+
<ToggleGroup type="single" variant="outline" spacing={0} defaultValue="call">
|
|
152
|
+
<ToggleGroupItem value="call"><Phone /> Telefone</ToggleGroupItem>
|
|
153
|
+
<ToggleGroupItem value="email"><Mail /> E-mail</ToggleGroupItem>
|
|
154
|
+
<ToggleGroupItem value="whatsapp"><MessageCircle /> WhatsApp</ToggleGroupItem>
|
|
153
155
|
</ToggleGroup>
|
|
154
156
|
```
|
|
155
157
|
|
|
158
|
+
Use um valor maior que zero quando cada item precisar preservar sua própria borda e seu próprio
|
|
159
|
+
raio, sem formar um bloco contínuo.
|
|
160
|
+
|
|
156
161
|
### Propriedades de ToggleGroup
|
|
157
162
|
|
|
158
163
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
@@ -161,10 +166,10 @@ itens; com zero, eles formam um bloco contínuo.
|
|
|
161
166
|
| `value` | `string \| string[]` | | Seleção controlada; o tipo acompanha `type`. |
|
|
162
167
|
| `onValueChange` | `(value: string \| string[]) => void` | | Informa a nova seleção. No modo single, uma string vazia representa nenhum item ativo. |
|
|
163
168
|
| `defaultValue` | `string \| string[]` | | Seleção inicial no modo não controlado. |
|
|
164
|
-
| `variant` | `'default' \| 'outline'` | `'default'` | Tratamento visual repassado aos itens. |
|
|
169
|
+
| `variant` | `'default' \| 'outline'` | `'default'` | Tratamento visual repassado aos itens. `outline` aplica borda a cada item. |
|
|
165
170
|
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Tamanho repassado aos itens. |
|
|
166
171
|
| `shape` | `'default' \| 'pill'` | `'default'` | Geometria do grupo e de suas extremidades. |
|
|
167
|
-
| `spacing` | `number` | `0` | Distância entre os itens em unidades de spacing. |
|
|
172
|
+
| `spacing` | `number` | `0` | Distância entre os itens em unidades de spacing. Com zero, remove bordas internas duplicadas e conecta o grupo. |
|
|
168
173
|
|
|
169
174
|
### Propriedades de ToggleGroupItem
|
|
170
175
|
|
package/src/ui/drivers/react.tsx
CHANGED
|
@@ -51,11 +51,12 @@ import { objectSchemaShape } from '../lib/object-schema.ts'
|
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Dicionário estrutural pro resolvedor de `options: { kind: 'dictionary', ref }`:
|
|
54
|
-
* qualquer coisa com `options()` → `{ value, label }`. O `DictType` do `t.dict`
|
|
55
|
-
* satisfaz direto — sem este driver importar o driver de schema.
|
|
54
|
+
* qualquer coisa com `options()` → `{ value, label, icon? }`. O `DictType` do `t.dict`
|
|
55
|
+
* satisfaz direto — sem este driver importar o driver de schema. A metadata opcional
|
|
56
|
+
* continua sendo declarada pelo dicionário; o provider não inventa apresentação.
|
|
56
57
|
*/
|
|
57
58
|
export interface DictLike {
|
|
58
|
-
options(): Array<{ value: string; label: string }>
|
|
59
|
+
options(): Array<{ value: string; label: string; icon?: string }>
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
interface OpusContextValue {
|
package/src/ui/meta.ts
CHANGED
|
@@ -201,7 +201,7 @@ export const componentMeta = {
|
|
|
201
201
|
name: "tabs",
|
|
202
202
|
ancestry: "shadcn",
|
|
203
203
|
whenToUse:
|
|
204
|
-
'
|
|
204
|
+
'Navegue entre painéis relacionados no mesmo nível. Cada TabsListItem aponta para um TabsPanel pelo mesmo `value`; TabsList usa uma linha para indicar a aba ativa, enquanto Tabs define orientação, escala, distribuição e moldura. Use ToggleGroup para escolher valores sem navegar entre painéis.',
|
|
205
205
|
},
|
|
206
206
|
textarea: {
|
|
207
207
|
name: "textarea",
|
|
@@ -363,7 +363,7 @@ export const componentMeta = {
|
|
|
363
363
|
name: "toggle",
|
|
364
364
|
ancestry: "shadcn",
|
|
365
365
|
whenToUse:
|
|
366
|
-
|
|
366
|
+
'Alterne um estado diretamente no contexto atual. ToggleGroup reúne escolhas únicas ou múltiplas e compartilha aparência e escala entre os itens. Use `variant="outline"` com `spacing={0}` para formar um grupo conectado com borda. Para navegar entre painéis, use Tabs.',
|
|
367
367
|
},
|
|
368
368
|
|
|
369
369
|
confirm: {
|
package/src/ui/react.tsx
CHANGED
|
@@ -192,8 +192,8 @@ export { Separator } from "./components/primitives/separator.tsx";
|
|
|
192
192
|
export {
|
|
193
193
|
Tabs,
|
|
194
194
|
TabsList,
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
TabsListItem,
|
|
196
|
+
TabsPanel,
|
|
197
197
|
} from "./components/primitives/tabs.tsx";
|
|
198
198
|
|
|
199
199
|
export { Skeleton } from "./components/primitives/skeleton.tsx";
|
|
@@ -499,7 +499,6 @@ export type {
|
|
|
499
499
|
ContentElement,
|
|
500
500
|
ContentFrameVariant,
|
|
501
501
|
ContentHeaderLevel,
|
|
502
|
-
ContentHeaderVariant,
|
|
503
502
|
} from "./components/patterns/content-header.tsx";
|
|
504
503
|
|
|
505
504
|
// Layout composicional: Split decide a relação espacial; Pane carrega conteúdo com inset.
|
package/src/vite/design.ts
CHANGED
|
@@ -88,8 +88,8 @@ export interface OpusDesignOptions {
|
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* Cenários de design (ADR 0037): módulos que exportam `defineDesignScenario(contrato, resposta)`.
|
|
91
|
-
* Aceita caminhos e globs, como `entry`. O cenário vence
|
|
92
|
-
*
|
|
91
|
+
* Aceita caminhos e globs, como `entry`. O cenário vence a resposta padrão gerada pelo `fake`;
|
|
92
|
+
* cenário de action inexistente impede o boot, com a lista no erro.
|
|
93
93
|
* Sem default: o projeto decide onde os cenários ficam e se são versionados.
|
|
94
94
|
*/
|
|
95
95
|
scenarios?: string | string[]
|
|
@@ -116,7 +116,7 @@ export interface OpusDesignOptions {
|
|
|
116
116
|
stubs?: Record<string, DesignStubHandler>
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* Monta o /api também no dev NORMAL
|
|
119
|
+
* Monta o /api também no dev NORMAL, com os handlers reais.
|
|
120
120
|
* Default false — devBackend puro não é design (sem sonda, proxy intacto).
|
|
121
121
|
*/
|
|
122
122
|
devBackend?: boolean
|
|
@@ -156,7 +156,7 @@ const PROBE_PATH = '/__opus/design'
|
|
|
156
156
|
const RUNTIME_ENDPOINT_PATHS = new Set(['/health', '/ready', '/openapi.json'])
|
|
157
157
|
|
|
158
158
|
/** De onde vem a resposta de cada action no modo design, na ordem de precedência. */
|
|
159
|
-
type DesignSource = 'scenario' | '
|
|
159
|
+
type DesignSource = 'scenario' | 'handler' | 'generated'
|
|
160
160
|
|
|
161
161
|
interface DesignState {
|
|
162
162
|
handle: NodeServerHandle
|
|
@@ -214,9 +214,8 @@ export function opusDesign(options: OpusDesignOptions = {}): Plugin {
|
|
|
214
214
|
exProxyPrefixes = Object.keys(config.server?.proxy ?? {})
|
|
215
215
|
// 2) Mata o proxy: fecha o vazamento preview→prod pra QUALQUER prefixo.
|
|
216
216
|
if (config.server?.proxy !== undefined) config.server.proxy = undefined
|
|
217
|
-
// 3) O
|
|
218
|
-
//
|
|
219
|
-
process.env.OPUS_MODE = 'design'
|
|
217
|
+
// 3) O boot liga em cada contrato um handler de cenário ou resposta gerada,
|
|
218
|
+
// e o preview fica autossuficiente sem alterar o runtime de produção.
|
|
220
219
|
},
|
|
221
220
|
|
|
222
221
|
configureServer(server) {
|
|
@@ -362,15 +361,14 @@ export function opusDesign(options: OpusDesignOptions = {}): Plugin {
|
|
|
362
361
|
const named = (source: DesignSource): string[] =>
|
|
363
362
|
state.actions.filter((a) => state.sources.get(a.name) === source).map((a) => a.name)
|
|
364
363
|
const scenarios = named('scenario')
|
|
365
|
-
const mocks = named('mock')
|
|
366
364
|
payload.runtime = {
|
|
367
365
|
prefix: apiPrefix,
|
|
368
366
|
actions: state.actions.length,
|
|
369
|
-
// `mocked`/`unmocked`
|
|
370
|
-
//
|
|
371
|
-
mocked: scenarios.length
|
|
367
|
+
// `mocked`/`unmocked` preservam o formato histórico da sonda: cenário escrito
|
|
368
|
+
// à mão contra respostas vindas de handler real ou geradas pelo `fake`.
|
|
369
|
+
mocked: scenarios.length,
|
|
372
370
|
unmocked: state.actions
|
|
373
|
-
.filter((a) =>
|
|
371
|
+
.filter((a) => state.sources.get(a.name) !== 'scenario')
|
|
374
372
|
.map((a) => a.name),
|
|
375
373
|
scenarios,
|
|
376
374
|
generated: named('generated'),
|
|
@@ -687,8 +685,8 @@ function scenariosOfModule(mod: Record<string, unknown>): DesignScenario[] {
|
|
|
687
685
|
}
|
|
688
686
|
|
|
689
687
|
/**
|
|
690
|
-
* Liga a cada action a resposta de design, nesta precedência: cenário,
|
|
691
|
-
*
|
|
688
|
+
* Liga a cada action a resposta de design, nesta precedência: cenário, handler real
|
|
689
|
+
* (`bindAction`) e, por fim, a resposta padrão gerada pelo `fake`.
|
|
692
690
|
*/
|
|
693
691
|
function prepareDesignActions(
|
|
694
692
|
actions: ActionDef[],
|
|
@@ -699,18 +697,23 @@ function prepareDesignActions(
|
|
|
699
697
|
const scenario = scenarios.get(action.name)
|
|
700
698
|
if (scenario !== undefined) {
|
|
701
699
|
sources.set(action.name, 'scenario')
|
|
702
|
-
return {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
700
|
+
return {
|
|
701
|
+
...action,
|
|
702
|
+
// Resposta simulada não representa exposição real de dado pessoal (ADR 0041).
|
|
703
|
+
personalData: false,
|
|
704
|
+
handler: (ctx: ActionContext, input: unknown) => scenario.respond(input, ctx),
|
|
705
|
+
} as ActionDef
|
|
707
706
|
}
|
|
708
707
|
if (typeof (action as { handler?: unknown }).handler === 'function') {
|
|
709
708
|
sources.set(action.name, 'handler')
|
|
710
709
|
return action
|
|
711
710
|
}
|
|
712
711
|
sources.set(action.name, 'generated')
|
|
713
|
-
return {
|
|
712
|
+
return {
|
|
713
|
+
...action,
|
|
714
|
+
personalData: false,
|
|
715
|
+
handler: async (_ctx: ActionContext, input: unknown) => designResponse(action, input),
|
|
716
|
+
} as ActionDef
|
|
714
717
|
})
|
|
715
718
|
return { actions: prepared, sources }
|
|
716
719
|
}
|
|
@@ -759,4 +762,3 @@ function sendEnvelope(res: ServerResponse, status: number, input: ErrorInput): v
|
|
|
759
762
|
),
|
|
760
763
|
)
|
|
761
764
|
}
|
|
762
|
-
|