@softize/opus 8.6.6
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 +1616 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/bin/cli.mjs +528 -0
- package/bin/lib/check.mjs +307 -0
- package/bin/lib/components.mjs +151 -0
- package/bin/lib/create.mjs +208 -0
- package/bin/lib/db-check-runner.mjs +86 -0
- package/bin/lib/db-migrate-runner.mjs +89 -0
- package/bin/lib/db-scaffold-runner.mjs +84 -0
- package/bin/lib/db.mjs +261 -0
- package/bin/lib/docs-include.mjs +48 -0
- package/bin/lib/gen-dicts.mjs +134 -0
- package/bin/lib/gen-docs.mjs +288 -0
- package/bin/lib/gen-manifest.mjs +102 -0
- package/bin/lib/gen-openapi.mjs +195 -0
- package/bin/lib/gen-runner.mjs +472 -0
- package/bin/lib/gen-stubs.mjs +463 -0
- package/bin/lib/gen.mjs +311 -0
- package/bin/lib/init.mjs +514 -0
- package/bin/lib/introspect.mjs +107 -0
- package/bin/lib/mcp.mjs +85 -0
- package/bin/lib/postinstall.mjs +56 -0
- package/docs/chat-event-protocol.md +85 -0
- package/docs/code-style.md +16 -0
- package/docs/data-layer.md +246 -0
- package/docs/ownership-vs-shadcn-lock.md +102 -0
- package/docs/protocol.md +2053 -0
- package/docs/releasing.md +110 -0
- package/docs/shellnav.md +131 -0
- package/package.json +338 -0
- package/registry/hooks/hooks.json +26 -0
- package/registry/hooks/link-memory-on-start.mjs +46 -0
- package/registry/hooks/opus-check-on-stop.mjs +114 -0
- package/registry/skills/create-action/SKILL.md +49 -0
- package/registry/skills/create-action/scaffold.mjs +122 -0
- package/registry/templates/app/_gitignore +3 -0
- package/registry/templates/app/_npmrc +1 -0
- package/registry/templates/app/_opus/_gitignore +5 -0
- package/registry/templates/app/_prettierrc.json +6 -0
- package/registry/templates/app/index.html +13 -0
- package/registry/templates/app/opus.config.ts +16 -0
- package/registry/templates/app/package.json +43 -0
- package/registry/templates/app/pnpm-workspace.yaml +11 -0
- package/registry/templates/app/public/favicon.svg +4 -0
- package/registry/templates/app/src/App.tsx +37 -0
- package/registry/templates/app/src/domains/tasks/actions/list.test.ts +34 -0
- package/registry/templates/app/src/domains/tasks/actions/list.ts +33 -0
- package/registry/templates/app/src/domains/tasks/index.ts +13 -0
- package/registry/templates/app/src/index.css +18 -0
- package/registry/templates/app/src/main.tsx +25 -0
- package/registry/templates/app/tsconfig.json +20 -0
- package/registry/templates/app/vite.config.ts +46 -0
- package/registry/templates/monorepo/_gitignore +3 -0
- package/registry/templates/monorepo/_npmrc +1 -0
- package/registry/templates/monorepo/package.json +9 -0
- package/registry/templates/monorepo/pnpm-workspace.yaml +14 -0
- package/src/ai/ask.ts +64 -0
- package/src/ai/drivers/anthropic.ts +309 -0
- package/src/ai/index.ts +17 -0
- package/src/audit/drivers/console.ts +117 -0
- package/src/audit/drivers/pg.ts +172 -0
- package/src/audit/index.ts +51 -0
- package/src/auth/drivers/better-auth.ts +103 -0
- package/src/auth/drivers/jwt.ts +188 -0
- package/src/auth/index.ts +9 -0
- package/src/client/drivers/fetch.ts +202 -0
- package/src/client/index.ts +22 -0
- package/src/core/actions.ts +110 -0
- package/src/core/audit.ts +239 -0
- package/src/core/contracts.ts +137 -0
- package/src/core/domain.ts +310 -0
- package/src/core/errors.ts +181 -0
- package/src/core/index.ts +174 -0
- package/src/core/logical-type.ts +31 -0
- package/src/core/reactions.ts +81 -0
- package/src/core/runtime.ts +1167 -0
- package/src/core/schedules.ts +41 -0
- package/src/core/types.ts +1356 -0
- package/src/data/drivers/kysely.ts +389 -0
- package/src/data/index.ts +10 -0
- package/src/data/readonly-pool.ts +160 -0
- package/src/dsl/eval.ts +136 -0
- package/src/dsl/index.ts +29 -0
- package/src/dsl/kysely.ts +230 -0
- package/src/dsl/loads.ts +123 -0
- package/src/dsl/parser.ts +423 -0
- package/src/dsl/types.ts +113 -0
- package/src/events/drivers/mitt.ts +70 -0
- package/src/events/index.ts +9 -0
- package/src/log/drivers/pino.ts +57 -0
- package/src/log/index.ts +9 -0
- package/src/mcp/index.ts +62 -0
- package/src/queue/drivers/bullmq.ts +190 -0
- package/src/queue/index.ts +9 -0
- package/src/scheduler/drivers/node-cron.ts +93 -0
- package/src/scheduler/every.ts +45 -0
- package/src/scheduler/index.ts +9 -0
- package/src/schema/drivers/zod.ts +765 -0
- package/src/schema/entity.ts +439 -0
- package/src/schema/format/locale.ts +144 -0
- package/src/schema/index.ts +65 -0
- package/src/schema/openapi.ts +302 -0
- package/src/schema/scaffold.ts +160 -0
- package/src/server/drivers/fastify.ts +224 -0
- package/src/server/drivers/node.ts +386 -0
- package/src/server/index.ts +142 -0
- package/src/storage/drivers/fs.ts +90 -0
- package/src/storage/drivers/s3.ts +117 -0
- package/src/storage/index.ts +27 -0
- package/src/testing/fake.ts +298 -0
- package/src/testing/index.ts +324 -0
- package/src/ui/components/patterns/action-form-card.tsx +48 -0
- package/src/ui/components/patterns/action-list-dialog.tsx +93 -0
- package/src/ui/components/patterns/app-shell.tsx +227 -0
- package/src/ui/components/patterns/confirm.tsx +226 -0
- package/src/ui/components/patterns/data-state.tsx +75 -0
- package/src/ui/components/patterns/form-dialog.tsx +64 -0
- package/src/ui/components/patterns/form.tsx +584 -0
- package/src/ui/components/patterns/list.tsx +1488 -0
- package/src/ui/components/patterns/page.tsx +46 -0
- package/src/ui/components/patterns/section-shell.tsx +246 -0
- package/src/ui/components/patterns/shell-nav.tsx +150 -0
- package/src/ui/components/patterns/sidebar.tsx +89 -0
- package/src/ui/components/patterns/split.tsx +93 -0
- package/src/ui/components/patterns/trigger.tsx +196 -0
- package/src/ui/components/patterns/view.tsx +84 -0
- package/src/ui/components/primitives/accordion.tsx +64 -0
- package/src/ui/components/primitives/alert-dialog.tsx +190 -0
- package/src/ui/components/primitives/alert.tsx +116 -0
- package/src/ui/components/primitives/aspect-ratio.tsx +9 -0
- package/src/ui/components/primitives/avatar.tsx +107 -0
- package/src/ui/components/primitives/badge.tsx +37 -0
- package/src/ui/components/primitives/breadcrumb.tsx +109 -0
- package/src/ui/components/primitives/button-group.tsx +83 -0
- package/src/ui/components/primitives/button.tsx +102 -0
- package/src/ui/components/primitives/calendar.tsx +218 -0
- package/src/ui/components/primitives/card.tsx +56 -0
- package/src/ui/components/primitives/carousel.tsx +239 -0
- package/src/ui/components/primitives/chat.tsx +407 -0
- package/src/ui/components/primitives/checkbox.tsx +30 -0
- package/src/ui/components/primitives/collapsible.tsx +31 -0
- package/src/ui/components/primitives/command.tsx +182 -0
- package/src/ui/components/primitives/composer.tsx +121 -0
- package/src/ui/components/primitives/copyable.tsx +50 -0
- package/src/ui/components/primitives/dialog.tsx +147 -0
- package/src/ui/components/primitives/drawer.tsx +141 -0
- package/src/ui/components/primitives/empty.tsx +104 -0
- package/src/ui/components/primitives/field.tsx +246 -0
- package/src/ui/components/primitives/icon-picker.tsx +180 -0
- package/src/ui/components/primitives/input-group.tsx +168 -0
- package/src/ui/components/primitives/input-otp.tsx +75 -0
- package/src/ui/components/primitives/input.tsx +72 -0
- package/src/ui/components/primitives/item.tsx +193 -0
- package/src/ui/components/primitives/kbd.tsx +28 -0
- package/src/ui/components/primitives/label.tsx +22 -0
- package/src/ui/components/primitives/markdown.tsx +35 -0
- package/src/ui/components/primitives/menu.tsx +255 -0
- package/src/ui/components/primitives/pagination.tsx +127 -0
- package/src/ui/components/primitives/popover.tsx +87 -0
- package/src/ui/components/primitives/progress.tsx +29 -0
- package/src/ui/components/primitives/radio-group.tsx +43 -0
- package/src/ui/components/primitives/resizable.tsx +51 -0
- package/src/ui/components/primitives/scroll-area.tsx +56 -0
- package/src/ui/components/primitives/select.tsx +479 -0
- package/src/ui/components/primitives/separator.tsx +26 -0
- package/src/ui/components/primitives/skeleton.tsx +13 -0
- package/src/ui/components/primitives/slider.tsx +61 -0
- package/src/ui/components/primitives/sonner.tsx +46 -0
- package/src/ui/components/primitives/spinner.tsx +29 -0
- package/src/ui/components/primitives/switch.tsx +33 -0
- package/src/ui/components/primitives/table.tsx +114 -0
- package/src/ui/components/primitives/tabs.tsx +104 -0
- package/src/ui/components/primitives/textarea.tsx +18 -0
- package/src/ui/components/primitives/toggle-group.tsx +81 -0
- package/src/ui/components/primitives/toggle.tsx +45 -0
- package/src/ui/components/primitives/tooltip.tsx +55 -0
- package/src/ui/components/primitives/truncate.tsx +49 -0
- package/src/ui/docs/DocBrowser.tsx +90 -0
- package/src/ui/docs/changelog.tsx +80 -0
- package/src/ui/docs/content/accordion.md +86 -0
- package/src/ui/docs/content/action-form-card.md +24 -0
- package/src/ui/docs/content/action-form-dialog.md +30 -0
- package/src/ui/docs/content/action-form.md +125 -0
- package/src/ui/docs/content/action-list-dialog.md +68 -0
- package/src/ui/docs/content/action-list.md +194 -0
- package/src/ui/docs/content/action-trigger.md +72 -0
- package/src/ui/docs/content/action-view.md +47 -0
- package/src/ui/docs/content/actions.md +138 -0
- package/src/ui/docs/content/ai.md +112 -0
- package/src/ui/docs/content/alert-dialog.md +73 -0
- package/src/ui/docs/content/alert.md +69 -0
- package/src/ui/docs/content/app-shell.md +155 -0
- package/src/ui/docs/content/aspect-ratio.md +66 -0
- package/src/ui/docs/content/audit.md +84 -0
- package/src/ui/docs/content/auth.md +70 -0
- package/src/ui/docs/content/avatar.md +94 -0
- package/src/ui/docs/content/badge.md +48 -0
- package/src/ui/docs/content/breadcrumb.md +87 -0
- package/src/ui/docs/content/button-group.md +71 -0
- package/src/ui/docs/content/button.md +60 -0
- package/src/ui/docs/content/calendar.md +62 -0
- package/src/ui/docs/content/card.md +49 -0
- package/src/ui/docs/content/carousel.md +85 -0
- package/src/ui/docs/content/chat.md +69 -0
- package/src/ui/docs/content/checkbox.md +75 -0
- package/src/ui/docs/content/cli.md +58 -0
- package/src/ui/docs/content/collapsible.md +64 -0
- package/src/ui/docs/content/command.md +56 -0
- package/src/ui/docs/content/composer.md +50 -0
- package/src/ui/docs/content/confirm.md +120 -0
- package/src/ui/docs/content/copyable.md +30 -0
- package/src/ui/docs/content/customization.md +110 -0
- package/src/ui/docs/content/cycle.md +34 -0
- package/src/ui/docs/content/data-state.md +47 -0
- package/src/ui/docs/content/data.md +99 -0
- package/src/ui/docs/content/dialog.md +60 -0
- package/src/ui/docs/content/drawer.md +55 -0
- package/src/ui/docs/content/empty.md +66 -0
- package/src/ui/docs/content/events.md +61 -0
- package/src/ui/docs/content/field.md +58 -0
- package/src/ui/docs/content/getting-started.md +109 -0
- package/src/ui/docs/content/icon-picker.md +51 -0
- package/src/ui/docs/content/input-group.md +78 -0
- package/src/ui/docs/content/input-otp.md +72 -0
- package/src/ui/docs/content/input.md +78 -0
- package/src/ui/docs/content/item.md +84 -0
- package/src/ui/docs/content/kbd.md +62 -0
- package/src/ui/docs/content/label.md +32 -0
- package/src/ui/docs/content/log.md +55 -0
- package/src/ui/docs/content/markdown.md +41 -0
- package/src/ui/docs/content/mcp.md +44 -0
- package/src/ui/docs/content/menu.md +114 -0
- package/src/ui/docs/content/microcopy.md +83 -0
- package/src/ui/docs/content/page.md +34 -0
- package/src/ui/docs/content/pagination.md +99 -0
- package/src/ui/docs/content/popover.md +49 -0
- package/src/ui/docs/content/progress.md +69 -0
- package/src/ui/docs/content/queue.md +62 -0
- package/src/ui/docs/content/radio-group.md +77 -0
- package/src/ui/docs/content/resizable.md +86 -0
- package/src/ui/docs/content/router.md +56 -0
- package/src/ui/docs/content/runtime.md +77 -0
- package/src/ui/docs/content/scheduler.md +66 -0
- package/src/ui/docs/content/scroll-area.md +89 -0
- package/src/ui/docs/content/section-shell.md +121 -0
- package/src/ui/docs/content/select.md +342 -0
- package/src/ui/docs/content/separator.md +33 -0
- package/src/ui/docs/content/sidebar.md +38 -0
- package/src/ui/docs/content/skeleton.md +34 -0
- package/src/ui/docs/content/slider.md +64 -0
- package/src/ui/docs/content/spinner.md +37 -0
- package/src/ui/docs/content/split.md +33 -0
- package/src/ui/docs/content/storage.md +69 -0
- package/src/ui/docs/content/switch.md +69 -0
- package/src/ui/docs/content/table.md +102 -0
- package/src/ui/docs/content/tabs.md +94 -0
- package/src/ui/docs/content/testing.md +89 -0
- package/src/ui/docs/content/textarea.md +30 -0
- package/src/ui/docs/content/toast.md +67 -0
- package/src/ui/docs/content/toggle-group.md +81 -0
- package/src/ui/docs/content/toggle.md +72 -0
- package/src/ui/docs/content/tokens.md +171 -0
- package/src/ui/docs/content/tooltip.md +50 -0
- package/src/ui/docs/content/truncate.md +37 -0
- package/src/ui/docs/content/ui.md +40 -0
- package/src/ui/docs/content/upgrading.md +48 -0
- package/src/ui/docs/doc-client.tsx +214 -0
- package/src/ui/docs/doc.tsx +301 -0
- package/src/ui/docs/folder.tsx +149 -0
- package/src/ui/docs/index.ts +21 -0
- package/src/ui/docs/markdown.tsx +130 -0
- package/src/ui/docs/md-raw.d.ts +4 -0
- package/src/ui/docs/plugin.ts +104 -0
- package/src/ui/docs/registry.tsx +424 -0
- package/src/ui/docs/standalone.tsx +107 -0
- package/src/ui/drivers/react.tsx +627 -0
- package/src/ui/index.ts +92 -0
- package/src/ui/lib/cn.ts +10 -0
- package/src/ui/lib/zod-pt-br.ts +38 -0
- package/src/ui/meta.ts +412 -0
- package/src/ui/react.tsx +235 -0
- package/src/ui/router.ts +96 -0
- package/src/ui/theme.css +234 -0
- package/src/vite/design.ts +652 -0
- package/src/vite/index.ts +8 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1616 @@
|
|
|
1
|
+
# Changelog do @softize/opus
|
|
2
|
+
|
|
3
|
+
O que muda em cada versão — e, quando quebra, **o que fazer**. Regras de leitura:
|
|
4
|
+
minor = novidade compatível; major = breaking (a seção **Breaking** diz a migração).
|
|
5
|
+
Este arquivo viaja no pacote: num projeto, leia `node_modules/@softize/opus/CHANGELOG.md`.
|
|
6
|
+
Depois de qualquer bump, rode os gates (`typecheck` · `test` · `opus check` ·
|
|
7
|
+
`manifest:check`) — eles apontam o que a mudança cobra do seu código.
|
|
8
|
+
|
|
9
|
+
> As entradas 2.30.1–2.31.5 foram **reconstruídas do git** (o release publicava sem
|
|
10
|
+
> passar por aqui). Uma delas — a 2.31.5, que mudou o rem base — é visual GLOBAL e
|
|
11
|
+
> tinha ficado sem registro nenhum, o que é exatamente o caso que este arquivo existe
|
|
12
|
+
> pra cobrir.
|
|
13
|
+
|
|
14
|
+
## 8.6.6 — 2026-07-30
|
|
15
|
+
|
|
16
|
+
**O pacote passa a ser publicado no npm PÚBLICO.** O registry próprio
|
|
17
|
+
(`registry.softize.com.br`) vivia na VPS, e a VPS não volta — em vez de reconstruir o
|
|
18
|
+
serviço, ele sai da infra. Consequência prática boa: máquina nova instala **sem token
|
|
19
|
+
nenhum**, o que é o que faz um servidor poder ser destruído e recriado à vontade.
|
|
20
|
+
|
|
21
|
+
Nos projetos, apague a linha `@softize:registry=` do `.npmrc` — sem ela o npm público
|
|
22
|
+
é o padrão, e é onde o pacote está.
|
|
23
|
+
|
|
24
|
+
**Nome de cliente saiu do que é publicado.** Como o pacote virou público, as menções a um
|
|
25
|
+
cliente real no CHANGELOG, no `shellnav.md`, no `section-shell`, no `router` e no
|
|
26
|
+
`theme.css` viraram **Empresa X** — o placeholder que a documentação já usava. Nenhum
|
|
27
|
+
comportamento muda; é texto. O repositório segue privado, então o histórico não vai junto.
|
|
28
|
+
|
|
29
|
+
## 8.6.5 — 2026-07-28
|
|
30
|
+
|
|
31
|
+
**`<Chat>`: a fala do usuário ganha teto de altura.** O card do usuário é `sticky` — gruda
|
|
32
|
+
no topo enquanto o turno está em vista. Sem teto, uma mensagem longa (o caso normal quando
|
|
33
|
+
a primeira fala é um enunciado de tarefa inteiro) ocupava a tela toda e ficava lá, cobrindo
|
|
34
|
+
a resposta. Agora corta em `max-h-40`, com esmaecido e um **Mostrar mais**.
|
|
35
|
+
|
|
36
|
+
O expandido TAMBÉM tem teto, e ele sai do scroll container (60% da altura dele, medida),
|
|
37
|
+
não da viewport: soltar a altura devolvia o defeito atrás de um clique, e um teto em `vh`
|
|
38
|
+
devolvia o mesmo num `<Chat>` embutido em painel mais baixo que a tela — nos dois casos o
|
|
39
|
+
botão de fechar sai de vista e não volta enquanto o turno estiver em cima.
|
|
40
|
+
|
|
41
|
+
**Vale pra todo consumidor no bump** — é o comportamento do componente, não há prop pra
|
|
42
|
+
desligar. Ganchos novos: `data-slot` `chat-user`, `chat-user-body` e `chat-scroll`.
|
|
43
|
+
|
|
44
|
+
Um contrato que já existia de fato passa a ter consequência: **o `<Chat>` precisa de altura
|
|
45
|
+
limitada pelo pai**. Sem isso o container passa a ser dimensionado pelo conteúdo e o teto
|
|
46
|
+
se realimenta. Se o seu `<Chat>` está num pai de altura automática, limite-o no bump.
|
|
47
|
+
|
|
48
|
+
## 8.6.4 — 2026-07-26
|
|
49
|
+
|
|
50
|
+
**`Split resizable` volta a respeitar percentuais.** `react-resizable-panels` v4
|
|
51
|
+
interpreta valores numéricos como pixels; o adaptador agora envia
|
|
52
|
+
`initialSize`/`minSize`/`maxSize` com a unidade `%` explícita. Um pane declarado com
|
|
53
|
+
`initialSize={30}` deixa de nascer com 30 px e volta a ocupar 30% do split.
|
|
54
|
+
|
|
55
|
+
## 8.6.3 — 2026-07-26
|
|
56
|
+
|
|
57
|
+
**Rail colapsado segue a Empresa X.** `SidebarItem` usa um botão centralizado de
|
|
58
|
+
`size-9` quando a sidebar está recolhida, em vez de ocupar toda a largura disponível.
|
|
59
|
+
Ícone, área ativa e raio ficam idênticos ao padrão que originou o rail.
|
|
60
|
+
|
|
61
|
+
## 8.6.2 — 2026-07-26
|
|
62
|
+
|
|
63
|
+
**Docs de `Sidebar` migradas para slots de pane.** Os exemplos agora usam
|
|
64
|
+
`PaneHeader`, `PaneContent` e `PaneFooter`, e explicam a compatibilidade temporária dos
|
|
65
|
+
aliases `Sidebar*`.
|
|
66
|
+
|
|
67
|
+
## 8.6.1 — 2026-07-26
|
|
68
|
+
|
|
69
|
+
**Cabeçalho, conteúdo e rodapé pertencem ao pane, não à sidebar.** Entram
|
|
70
|
+
`PaneHeader`, `PaneContent` e `PaneFooter`: os três organizam qualquer pane, inclusive
|
|
71
|
+
um `Sidebar` encaixado em um `Split`. `SidebarHeader`, `SidebarContent` e
|
|
72
|
+
`SidebarFooter` continuam exportados como aliases deprecated para uma migração gradual.
|
|
73
|
+
|
|
74
|
+
## 8.6.0 — 2026-07-26
|
|
75
|
+
|
|
76
|
+
**Estrutura composicional de tela: `Split`/`Pane` e `Sidebar`.** `Split` concentra a
|
|
77
|
+
divisão horizontal ou vertical e recebe `resizable`; `Pane` define os espaços. `Sidebar`
|
|
78
|
+
cuida apenas do chrome de navegação, com `SidebarHeader`, `SidebarContent`,
|
|
79
|
+
`SidebarFooter`, `SidebarNav` e modo `collapsed`. `AppShell`, `SectionShell` e
|
|
80
|
+
`Resizable*` passam a estar marcados como deprecated nas docs, com os caminhos de migração.
|
|
81
|
+
|
|
82
|
+
**`opusDesign` deixa de perder domínio novo — watcher mais largo, 404 que se cura e entry
|
|
83
|
+
glob.** O episódio real: numa sessão de design o agente criou um domínio, levou 404 e
|
|
84
|
+
concluiu "precisa de restart" (não precisava — faltou registrar no entry). Três fixes
|
|
85
|
+
matam a classe:
|
|
86
|
+
|
|
87
|
+
- **`add`/`unlink` invalidam sempre** (sob o root, fora node_modules/.git): arquivo novo
|
|
88
|
+
nunca importado não está no module graph — e é exatamente ele que um entry glob precisa
|
|
89
|
+
enxergar. `change` mantém o critério do graph. Rebuild segue lazy; over-invalidation é
|
|
90
|
+
barata.
|
|
91
|
+
- **404 que se cura e ensina.** Request sob o `/api` sem rota força UM rebuild por ciclo
|
|
92
|
+
de staleness (mutex no boot — sem rebuild-storm de 404 legítimo) e re-tenta o match;
|
|
93
|
+
persistindo, o envelope vem com mensagem-guia: action não registrada no runtime —
|
|
94
|
+
confira o entry (path incluso) ou use entry glob. O driver node ganhou
|
|
95
|
+
`hasRoute(method, pathname)` no handle pra viabilizar o pre-match.
|
|
96
|
+
- **`entry` aceita glob** (ex.: `'src/domains/*/contract.ts'`; pode misturar com entries
|
|
97
|
+
estáticos no array): re-expandido a CADA rebuild — domínio novo entra no ar ao salvar o
|
|
98
|
+
arquivo, sem editar entry nem reiniciar o dev server. Dos módulos casados entram os
|
|
99
|
+
exports (default e named) com shape de registrável; schemas/helpers exportados junto
|
|
100
|
+
ficam de fora. Matcher próprio sem dep nova: `*` casa um segmento, `**` qualquer
|
|
101
|
+
profundidade. Entry estático segue funcionando exatamente como antes.
|
|
102
|
+
|
|
103
|
+
## 8.5.1 — 2026-07-24
|
|
104
|
+
|
|
105
|
+
**Um X só de clear (fantasma) e o trailing-botão balanceado no canto.** Dois acertos de
|
|
106
|
+
consistência nos adornos de campo:
|
|
107
|
+
|
|
108
|
+
- **O clear do Select vira X fantasma.** Era um badge circular CHEIO (`bg-muted-foreground`,
|
|
109
|
+
X branco) — destoava de todo o resto dos adornos, que são ghost-muted no slot. Agora é um
|
|
110
|
+
X `text-muted-foreground hover:text-foreground`, igual ao remover-chip do multi e ao que um
|
|
111
|
+
`trailing` de limpar renderia num Input. Um tratamento de X, não três.
|
|
112
|
+
- **Trailing que é BOTÃO encosta no canto (~6px), não flutua a 12px.** Um botão-ícone num
|
|
113
|
+
campo `h-9` tem 6px de folga em cima/embaixo; a da direita era o `px-3` do texto (12px),
|
|
114
|
+
deixando o botão torto. Agora o slot trailing puxa `has-[button]:-mr-1.5` (Input sempre;
|
|
115
|
+
Select só no buscável — com chevron o `gap` fica) pra o inset direito casar com o vertical.
|
|
116
|
+
Mesmo balanceamento do `InputGroup`. Trailing de texto puro segue alinhado ao texto (12px).
|
|
117
|
+
|
|
118
|
+
## 8.5.0 — 2026-07-24
|
|
119
|
+
|
|
120
|
+
**`Input` adornado (`icon`/`trailing`) passa pro modelo flex — o mesmo do `InputGroup`/`Select`.**
|
|
121
|
+
Antes o adorno era SOBREPOSTO (`absolute`) e o texto abria espaço com `pl-9`/`pr-9`; a
|
|
122
|
+
distância do trailing à borda (6px) divergia da do Select (12px). Agora, com adorno, a
|
|
123
|
+
BORDA e o anel de foco moram no WRAPPER (disparados pelo `:focus-visible` do `<input>`
|
|
124
|
+
interno — foco de teclado), o `<input>` fica sem borda e CRESCE (`flex-1`), e os adornos
|
|
125
|
+
são irmãos ao lado: o `px-3`/`gap` posicionam sozinhos (trailing a 12px, casando com o
|
|
126
|
+
Select). Some a conta de `pl-9`/`pr-9`. **Nota de call site:** o `className` de um Input
|
|
127
|
+
ADORNADO agora estiliza o CAMPO (o wrapper — largura, raio, fundo, fonte), como no Select;
|
|
128
|
+
a fonte/cor cascateiam pro texto digitado. O Input CRU (sem `icon`/`trailing`) segue com o
|
|
129
|
+
`className` no próprio `<input>`, borda e ref nele — zero mudança. O adorno era novidade
|
|
130
|
+
recente (8.3.x) e só a barra de endereço do Maestro o usava.
|
|
131
|
+
|
|
132
|
+
**O preview de design vira in-process: driver `server/node` + plugin `opusDesign()`.** A
|
|
133
|
+
Trilha A do isolamento preview×prod (ADR 0004 da softize): desenhar tela não pode depender
|
|
134
|
+
de backend de pé — nem, pior, falar com prod por um proxy esquecido no vite.config.
|
|
135
|
+
|
|
136
|
+
- **`@softize/opus/server/node`** — `nodeServer()`: `ServerAdapter` connect-style sem
|
|
137
|
+
framework. Devolve `{ adapter, handler }`: o adapter vai no `createRuntime({ server })`;
|
|
138
|
+
o handler `(req, res, next?)` encaixa em qualquer stack node — middlewares do vite,
|
|
139
|
+
express, `http.createServer` direto. Mesmo protocolo do driver fastify (helpers
|
|
140
|
+
compartilhados de `@softize/opus/server`): rotas por convenção, envelopes, status por
|
|
141
|
+
categoria, endpoints health/ready/openapi.
|
|
142
|
+
- **`@softize/opus/vite`** — `opusDesign()`: com `vite --mode design`, carrega o entry
|
|
143
|
+
(`./opus.config.ts` por default, via SSR do vite — aliases do app valem), registra as
|
|
144
|
+
actions num runtime local (SÓ actions: reactions/schedules ficam de fora — o runtime de
|
|
145
|
+
design é request/response) e serve `/api` dentro do próprio dev server, com
|
|
146
|
+
`OPUS_MODE=design` fazendo os `mockHandler` responderem e `designAuth` dando a sessão
|
|
147
|
+
fixa (`{ id: 'design' }`). Qualquer `server.proxy` do config é DESLIGADO — prefixo
|
|
148
|
+
ex-proxy sem cobertura responde 503 em envelope (`design.no_backend`), nunca vaza pra
|
|
149
|
+
fora. `stubs` por prefixo cobrem o que não é opus (ex.: `/api/auth`, podem streamar);
|
|
150
|
+
`GET /__opus/design` é a sonda do Maestro (versão, contagem de actions/mocks, stubs);
|
|
151
|
+
HMR reconstrói o runtime quando qualquer módulo carregado muda. `devBackend: true`
|
|
152
|
+
monta o `/api` real também no dev normal (sem modo design).
|
|
153
|
+
- **`runAction` (testing) respeita o modo design.** Mesma régua do `execute()` (fonte
|
|
154
|
+
única): com `OPUS_MODE=design` e `mockHandler` presente, o mock roda; sem o env, o
|
|
155
|
+
handler real. Contrato puro (sem handler) vira TESTÁVEL em design — os testes-de-contrato
|
|
156
|
+
da sessão de design deixam de ser impossíveis; fora de design, erro claro
|
|
157
|
+
(`testing.no_handler`) em vez de estouro obscuro.
|
|
158
|
+
- **O esqueleto do `opus create` nasce contract-first.** `dev:design` vira
|
|
159
|
+
`vite --mode design`, o vite.config traz `opusDesign({ devBackend: true })` (dev normal
|
|
160
|
+
serve o handler real via /api; design roteia pro mock) e o domínio-exemplo mostra o
|
|
161
|
+
split canônico: `defineContract` com `mockHandler` alimentado pelo schema (`fakeMany`)
|
|
162
|
+
+ `bindAction` com o handler in-memory — o App consome o CONTRATO via `useListAction`
|
|
163
|
+
(fim do "chama o handler direto, sem servidor ainda"), e o teste cobre o bound E o
|
|
164
|
+
contrato puro em design. O plugin também marca `@softize/opus` como `ssr.noExternal`:
|
|
165
|
+
o pacote embarca TS source e, externalizado, o node recusa TS sob node_modules no
|
|
166
|
+
load SSR do entry.
|
|
167
|
+
- **`options: { kind: 'dictionary', ref }` agora resolve.** O `TbdlibProvider` ganha
|
|
168
|
+
`dicts` (`Record<ref, DictLike>` — o `DictType` do `t.dict` encaixa direto) e a UI
|
|
169
|
+
resolve a origem declarada no contrato: no `ActionList`, filtro e chips mostram os
|
|
170
|
+
labels do vocabulário; no `ActionForm`, a precedência é prop `options` do campo >
|
|
171
|
+
`fieldOptions` do form > `options` do FieldSpec (dictionary/static) > **meta do
|
|
172
|
+
`t.dict` no schema** (zero-config: campo dict — ou multiselect de dict — resolve
|
|
173
|
+
value→label pela meta que viaja no contrato, sem registry) > chaves cruas do z.enum.
|
|
174
|
+
Campo texto com opções declaradas vira single-select por-id. Habilitador por baixo:
|
|
175
|
+
`attachLogicalType`/`getLogicalType` moveram pro core (o `@softize/opus/schema`
|
|
176
|
+
re-exporta — API igual) porque a fronteira SPA só deixa a UI tocar ui/lib/core.
|
|
177
|
+
|
|
178
|
+
**Diálogos imperativos: o trio `dialog`.** `dialog.alert` (Promise<void>, reconhecimento
|
|
179
|
+
obrigatório) · `dialog.confirm` (Promise<boolean>, agora com slot `body` pra corpo próprio) ·
|
|
180
|
+
`dialog.prompt` (Promise<string|null>, um input). O `confirm()` vira o namespace `dialog` —
|
|
181
|
+
`window.alert/confirm/prompt` são globais do browser e um import esquecido cai no nativo;
|
|
182
|
+
`window.dialog` não existe. Tudo sobre o mesmo `AlertDialog` (role=alertdialog, não fecha
|
|
183
|
+
fora) e a mesma fila; `confirm()` e `<ConfirmHost/>` seguem como aliases de `dialog.confirm`
|
|
184
|
+
e `<DialogHost/>` (zero migração). A doc do AlertDialog passa a mostrar o caso manual real
|
|
185
|
+
(mais de duas ações), já que o `body` cobriu o "corpo próprio".
|
|
186
|
+
|
|
187
|
+
## 8.4.1 — 2026-07-24
|
|
188
|
+
|
|
189
|
+
**O tom da ação `trailing` mora no SLOT, não no call site.** Uma ação no fim do campo é
|
|
190
|
+
SECUNDÁRIA — então o `text-muted-foreground` passou pro slot `trailing` (do Input e do
|
|
191
|
+
Select), e o botão ghost herda. Antes cada call site sprinklava `className="text-muted-foreground"`
|
|
192
|
+
no botão (inerte quando o slot já daria o tom); agora o trailing nasce muted e acende no
|
|
193
|
+
hover pelo próprio ghost, sem cor no call site.
|
|
194
|
+
|
|
195
|
+
## 8.4.0 — 2026-07-24
|
|
196
|
+
|
|
197
|
+
**`Button` ganha `icon-xs` (24px) — a ação DENTRO de um campo.** Fecha a escala dos botões
|
|
198
|
+
só-ícone: `icon` (36) · `icon-sm` (32) · `icon-xs` (24). O `icon-xs` é o par do `icon-xs`
|
|
199
|
+
que o `InputGroupButton` já tinha — pra um clear/mostrar-senha no `trailing` do Input/Select
|
|
200
|
+
caber folgado num h-9. Antes o exemplo virava `className="size-6"` (override na mão, fora
|
|
201
|
+
da escala); agora é `size="icon-xs"`.
|
|
202
|
+
|
|
203
|
+
Doc afinada junto: o exemplo de "limpar" do `InputGroup` deixou de ensinar a fazer à mão o
|
|
204
|
+
que o `<Input trailing={…} />` já resolve — o InputGroup agora mostra o caso que é DELE
|
|
205
|
+
(ícone leading + botão no fim juntos, a busca do ActionList). Uma ação simples é prop;
|
|
206
|
+
adornos que se acumulam, InputGroup.
|
|
207
|
+
|
|
208
|
+
## 8.3.2 — 2026-07-24
|
|
209
|
+
|
|
210
|
+
**`Input` ganha `trailing` — fecha a simetria com o Select.** O 8.3.0 unificou o LEADING
|
|
211
|
+
(`icon` no Input, Select e Alert); este fecha o TRAILING, que estava pela metade (Select
|
|
212
|
+
fazia ação-no-fim por prop, Input só por `InputGroup`). Agora `<Input trailing={<Button/>} />`,
|
|
213
|
+
o par do `icon`. Regra: **um ícone e/ou uma ação → props (`icon`/`trailing`); mais de um
|
|
214
|
+
adorno junto, prefixo de texto ou adorno em bloco → `InputGroup`.** Sem breaking.
|
|
215
|
+
|
|
216
|
+
(Saiu como 8.3.2 — o `release patch` num momento de versionamento cruzado com outra sessão;
|
|
217
|
+
por conteúdo é feature, valeria um minor, mas está publicada e é o que vale.)
|
|
218
|
+
|
|
219
|
+
## 8.3.1 — 2026-07-24
|
|
220
|
+
|
|
221
|
+
**Destructive legível no escuro + a ação do Alert no lugar.** Duas correções que vieram
|
|
222
|
+
de olhar o dark com atenção:
|
|
223
|
+
|
|
224
|
+
- **`--destructive` do dark clareia (`hsl(0 62.8% 30.6%)` → `hsl(0 72% 58%)`).** O valor
|
|
225
|
+
antigo era calibrado só pra FUNDO (com texto branco por cima), mas menu, alert, field
|
|
226
|
+
e erros de form usam `text-destructive` como FOREGROUND — e um vermelho de 30,6% de
|
|
227
|
+
luminosidade sobre o popover dava ~1,7:1, abaixo do AA (medido). Agora passa como texto
|
|
228
|
+
e alinha o destructive ao que o success já fazia (`dark:text-emerald-400` pula pro tom
|
|
229
|
+
claro). **Efeito nos fundos sólidos** (Button/Badge/toast destructive): ficam mais vivos
|
|
230
|
+
no dark — o branco por cima segue perfeitamente legível (validado no visual), e o
|
|
231
|
+
resultado é consistente com o light e com o shadcn novo. Light **não muda** (só o bloco
|
|
232
|
+
`.dark` foi tocado).
|
|
233
|
+
- **Ação do Alert alinha na coluna de conteúdo.** No Alert com ícone (grid de 2 colunas),
|
|
234
|
+
`AlertTitle`/`AlertDescription` têm `col-start-2`, mas a AÇÃO (children depois da frase,
|
|
235
|
+
ex.: um `<Button>Reiniciar</Button>`) não tinha — caía na coluna do ícone (~1rem) e era
|
|
236
|
+
espremida pra esquerda. Agora vai num wrapper `col-start-2`, alinhada sob o texto.
|
|
237
|
+
|
|
238
|
+
## 8.3.0 — 2026-07-24
|
|
239
|
+
|
|
240
|
+
**`Input` ganha `icon` — adorno de campo vira prop, unificado com Select e Alert.** O ícone
|
|
241
|
+
leading passa a ser a MESMA prop nos três: `<Input icon={<Building2 />} />`. Antes o Input só
|
|
242
|
+
tinha o `InputGroup` (compositivo, verboso, divergente do Select/Alert); agora adorno simples
|
|
243
|
+
é prop e o `InputGroup` fica pro avançado (botão no fim, prefixo, bloco, múltiplos). Sem
|
|
244
|
+
breaking: prop opcional, o `<input>` cru segue igual sem ela.
|
|
245
|
+
|
|
246
|
+
## 8.2.1 — 2026-07-24
|
|
247
|
+
|
|
248
|
+
**Remove a auto-dependência `file:` do `package.json` do próprio opus (publish quebrado).**
|
|
249
|
+
Uma sessão paralela injetou `"@softize/opus": "file:…/scratchpad/opus.tgz"` no package.json
|
|
250
|
+
do pacote (provável `pnpm add` do tarball local pra testar), e isso foi publicado da 8.0.3 até
|
|
251
|
+
a 8.2.0: todo consumidor que instalava puxava um `file:` pra um scratchpad que só existia
|
|
252
|
+
naquela máquina — deploy quebrava com ENOENT. 8.2.1 sai limpo.
|
|
253
|
+
|
|
254
|
+
## 8.2.0 — 2026-07-24
|
|
255
|
+
|
|
256
|
+
**`Select` ganha `trailing`.** Um slot pra uma ação custom DENTRO do controle, no fim (antes
|
|
257
|
+
do chevron) — um botão que age sobre o valor escolhido, em vez de ficar como irmão solto ao
|
|
258
|
+
lado do campo. O clique no trailing **não** abre a lista (o slot para a propagação pro
|
|
259
|
+
controle). Pareia com o `icon` (leading) que já existia.
|
|
260
|
+
|
|
261
|
+
## 8.1.0 — 2026-07-24
|
|
262
|
+
|
|
263
|
+
**`Tabs` ganha `size`, `Button` ganha `icon-sm` — o `sm` que faltava pra fileira densa.**
|
|
264
|
+
Button e Select tinham `sm` (h-8, 32px); o `Tabs` era h-9 cravado e o `Button` só-ícone só
|
|
265
|
+
tinha `icon` (36px). Numa toolbar/header onde os controles são `sm`, o segmento de abas e um
|
|
266
|
+
botão de ícone ficavam 4px mais altos que os irmãos — e a saída era override de altura na
|
|
267
|
+
mão, que foge da escala do sistema.
|
|
268
|
+
|
|
269
|
+
- `<Tabs size="sm">` baixa a lista pra h-8 (o size flui pra TabsList por contexto). Isto
|
|
270
|
+
**ejetou** o Tabs (o shadcn não tem size aqui) — divergência declarada no lock.
|
|
271
|
+
- `<Button size="icon-sm">` é o quadrado de 32px (`size-8`), par do `sm`.
|
|
272
|
+
|
|
273
|
+
Sem breaking: props novas, opcionais. Quem quiser uma fileira toda em 32px agora usa só
|
|
274
|
+
props de size, sem tocar em altura.
|
|
275
|
+
|
|
276
|
+
## 8.0.4 — 2026-07-24
|
|
277
|
+
|
|
278
|
+
**A "dançada" do menu, resolvida DE VERDADE — o culpado era o botão.** A pista final
|
|
279
|
+
veio do João: o `active:scale-95` do Button (toque da casa, com `transition-all`).
|
|
280
|
+
No clique HUMANO — que pressiona e segura ~100ms, diferente do clique sintético dos
|
|
281
|
+
testes — o menu abre e se ancora enquanto o gatilho ainda está ENCOLHENDO; o
|
|
282
|
+
posicionador do Radix acompanha o rect a cada frame, então o painel recém-aberto
|
|
283
|
+
**persegue o botão** em passos de 0,5px (~2px de deriva) e desanda na soltura.
|
|
284
|
+
Medido com clique segurado; com o scale removido, âncora imóvel do press à soltura.
|
|
285
|
+
|
|
286
|
+
- **Press do Button vira `active:brightness-90`.** Feedback de clique continua
|
|
287
|
+
(escurece de leve), mas NÃO-GEOMÉTRICO: o rect não muda, nenhum popover ancorado
|
|
288
|
+
(Menu, Popover, Select…) tem por que dançar. Regra que fica: press feedback em
|
|
289
|
+
gatilho não pode mexer em geometria.
|
|
290
|
+
- **A entrada do menu volta a fade + zoom ancorado** (a escolha da 8.0.2 — sem
|
|
291
|
+
slide — fica; o fade-only experimental não chegou a publicar). O `will-change`
|
|
292
|
+
da 8.0.3 também fica: animação no compositor é robustez de graça.
|
|
293
|
+
- As 8.0.2/8.0.3 não eram a causa, mas seguem valendo como melhorias: coreografia
|
|
294
|
+
mais simples e animação imune a stall de main thread na 1ª abertura.
|
|
295
|
+
|
|
296
|
+
## 8.0.3 — 2026-07-24
|
|
297
|
+
|
|
298
|
+
**A "dançada" de verdade: era só na PRIMEIRA abertura — e agora tem causa medida e
|
|
299
|
+
correção.** A 8.0.2 simplificou a coreografia (decisão que fica), mas o sintoma
|
|
300
|
+
persistia, e o detalhe "só na primeira vez" fechou o diagnóstico: a 1ª abertura da
|
|
301
|
+
página paga a inicialização única (portal + focus-scope + scroll-lock + primeiro
|
|
302
|
+
raster do painel), que trava o main thread NO MEIO dos 150ms — medido em Chrome com
|
|
303
|
+
janela real/Retina: **congelamento de até 219ms** com o menu parado no meio do voo,
|
|
304
|
+
que completava depois com um tranco. Da 2ª abertura em diante, tudo quente → suave.
|
|
305
|
+
É por isso que nenhum teste sintético "médio" pegava.
|
|
306
|
+
|
|
307
|
+
- **Correção: `will-change: transform, opacity` no conteúdo do menu e do submenu.**
|
|
308
|
+
Promove a layer ao compositor ANTES de animar — com a animação rodando no
|
|
309
|
+
compositor, o main thread pode travar que ela segue tocando. Medido pós-fix
|
|
310
|
+
(4 rodadas, browser frio): zero stalls no meio da animação, cadência 16-17ms.
|
|
311
|
+
O resto do custo de init vira só ~50ms até o primeiro paint — e como o fade começa
|
|
312
|
+
em opacity 0, esse início nem é visível.
|
|
313
|
+
- Custo: uma layer composited por menu aberto — irrelevante nesse tamanho.
|
|
314
|
+
- Nota: Popover/Select/Tooltip têm a mesma anatomia de animação e podem herdar o
|
|
315
|
+
mesmo tratamento — ficam pra quando (se) o sintoma aparecer lá; Select e Popover
|
|
316
|
+
são componentes TRAVADOS (byte-fiéis), então a mudança lá passa por ejetar.
|
|
317
|
+
|
|
318
|
+
## 8.0.2 — 2026-07-24
|
|
319
|
+
|
|
320
|
+
**Menu abre sem "dançada" + a doc ganha o clique-direito.** Dois refinamentos no Menu:
|
|
321
|
+
|
|
322
|
+
- **Entrada sem slide.** A coreografia herdada do shadcn compunha TRÊS movimentos em
|
|
323
|
+
150ms — descer 7,5px (slide) + inchar 5% (zoom) + fade — e o conjunto lia como
|
|
324
|
+
"dançada" na abertura. Investigação instrumentada (posição frame a frame, câmera
|
|
325
|
+
lenta, screencast em velocidade real, reopen no meio do animate-out) confirmou: não
|
|
326
|
+
há pulo de posição nem bug de layout — a impressão vinha da própria coreografia.
|
|
327
|
+
Agora a entrada é **fade + zoom ancorado no gatilho** (o que o Radix Themes faz);
|
|
328
|
+
a saída já era simétrica e fica como está. Vale pro conteúdo do menu E do submenu.
|
|
329
|
+
- **Doc: "Contexto: clique direito".** O papel do ContextMenu (aposentado na 5.0.0)
|
|
330
|
+
documentado com preview funcional: gatilho CONTROLADO ancorado no ponteiro
|
|
331
|
+
(`onContextMenu` guarda a posição; um span `position: fixed` invisível naquele ponto
|
|
332
|
+
ancora o conteúdo). A página do Menu agora cobre o caso que justificou a unificação.
|
|
333
|
+
|
|
334
|
+
## 8.0.1 — 2026-07-24
|
|
335
|
+
|
|
336
|
+
**Menu: o hover do item destructive volta a tingir o fundo.** A decisão de 17/jul
|
|
337
|
+
(padrão Notion: hover muda SÓ a cor do texto/ícone, bg segue o accent) foi testada no
|
|
338
|
+
uso real e revertida: **no dark, texto destructive sobre o accent do hover não lia bem**
|
|
339
|
+
— o vermelho perdia contraste justamente no item que mais precisa ser inconfundível.
|
|
340
|
+
Volta o comportamento anterior, byte-fiel ao que era antes da decisão:
|
|
341
|
+
|
|
342
|
+
- Em repouso o item segue NEUTRO (a decisão de 2.24.1, essa fica);
|
|
343
|
+
- No hover/focus, texto/ícone destructive **sobre `bg-destructive/10`**
|
|
344
|
+
(dark: `bg-destructive/20`) — o fundo tingido devolve a leitura no escuro.
|
|
345
|
+
|
|
346
|
+
Visual, só no `<Menu>`; nenhuma API muda. O delta do lock registra o teste e a reversão
|
|
347
|
+
— se o padrão Notion voltar, volta sabendo por que saiu.
|
|
348
|
+
|
|
349
|
+
## 8.0.0 — 2026-07-24
|
|
350
|
+
|
|
351
|
+
**A densidade do markdown sai — por ora.** O `size` do `<Markdown>` e a regra
|
|
352
|
+
`.prose[data-size='sm']` do theme.css foram revertidos: no uso real quase toda mensagem
|
|
353
|
+
tem um parágrafo só, e ali o `[&>:first-child]:mt-0 [&>:last-child]:mb-0` já zerava tudo —
|
|
354
|
+
a regra só atuava nas mensagens com vários blocos, que são minoria. O que de fato apertava
|
|
355
|
+
o transcript era o vão ENTRE mensagens (resolvido no 7.2.0, que fica). Sobrar uma prop de
|
|
356
|
+
efeito quase nulo é pior que não ter: vira decisão a mais em cada call site.
|
|
357
|
+
|
|
358
|
+
Fica o que se pagou: **motor único** (markdown-it no chat e na doc), a correção do respiro
|
|
359
|
+
fantasma e a hierarquia de gaps do Chat.
|
|
360
|
+
|
|
361
|
+
Se a densidade voltar, volta com o problema entendido — e a discussão em aberto é se o
|
|
362
|
+
`prose` (régua de artigo) deve mesmo estar no chat, ou se ali cabe estilo próprio.
|
|
363
|
+
|
|
364
|
+
### Breaking
|
|
365
|
+
|
|
366
|
+
`MarkdownProps.size` saiu. Quem passava `size="sm"` só remove a prop — o render é o mesmo
|
|
367
|
+
de antes, já que a regra que dava efeito a ela também saiu.
|
|
368
|
+
|
|
369
|
+
## 7.2.0 — 2026-07-24
|
|
370
|
+
|
|
371
|
+
**Hierarquia no espaço do transcript.** O `Chat` usava o MESMO `gap-2` nos dois níveis:
|
|
372
|
+
entre turnos e entre as falas de dentro de um turno. Como o agente narra o que vai fazendo
|
|
373
|
+
("Vou ler o arquivo…", "A estrutura está correta…"), um turno junta oito falas curtas — e
|
|
374
|
+
com o gap igual nos dois níveis o transcript virava uma parede uniforme, sem começo nem fim
|
|
375
|
+
visíveis, gastando ~56px só de vão.
|
|
376
|
+
|
|
377
|
+
Agora: **`gap-1` dentro do turno** (as falas são um raciocínio contínuo, andam juntas) e
|
|
378
|
+
**`gap-3` entre turnos** (aí sim separa pergunta de pergunta). Num turno de oito falas são
|
|
379
|
+
~28px a menos, e de quebra dá pra bater o olho e ver onde cada turno começa.
|
|
380
|
+
|
|
381
|
+
Isto é o par da densidade do 7.1.0, e ataca o que aquela mudança NÃO alcançava: mensagem de
|
|
382
|
+
um parágrafo só já tinha margem interna zerada — o que ocupava espaço ali era o vão entre
|
|
383
|
+
mensagens, que é do Chat, não do markdown.
|
|
384
|
+
|
|
385
|
+
## 7.1.1 — 2026-07-24
|
|
386
|
+
|
|
387
|
+
**`@types/markdown-it` vai pra `dependencies`.** O pacote distribui o SOURCE (`.tsx`), então
|
|
388
|
+
o typecheck de quem consome atravessa nossos arquivos: com os tipos só em `devDependencies`,
|
|
389
|
+
todo app quebrava com `TS7016: Could not find a declaration file for module 'markdown-it'` —
|
|
390
|
+
e agora atinge todos, porque quem importa é o primitivo `Markdown`, que o `Chat` usa. Regra
|
|
391
|
+
que fica: **dependência que aparece em `import` de arquivo publicado precisa dos tipos em
|
|
392
|
+
`dependencies`**, não em dev.
|
|
393
|
+
|
|
394
|
+
## 7.1.0 — 2026-07-24
|
|
395
|
+
|
|
396
|
+
**Um motor de markdown só.** Havia dois: o `Markdown` (primitivo, usado pelo `Chat`) era um
|
|
397
|
+
parser de **regex à mão**, e a doc renderizava com **markdown-it**. Mesma mensagem, render
|
|
398
|
+
diferente conforme onde caísse — e o argumento que justificava o parser caseiro ("sem
|
|
399
|
+
dependência de runtime") não valia mais: o markdown-it já era `dependency` do pacote por
|
|
400
|
+
causa da doc. Agora o primitivo usa markdown-it, e a doc importa a **mesma instância** (não
|
|
401
|
+
duas com opções próprias, que divergiriam no primeiro edge case).
|
|
402
|
+
|
|
403
|
+
Em prática, o chat ganha o que o regex não cobria: itálico, listas aninhadas, referências,
|
|
404
|
+
ênfase combinada, todo o CommonMark. `html: false` segue valendo — tag HTML no fonte é
|
|
405
|
+
escapada, o que é o que torna seguro renderizar texto de usuário e de modelo.
|
|
406
|
+
|
|
407
|
+
**`<Markdown size="sm">`** encolhe o RESPIRO entre blocos mantendo o corpo em 14px — mesma
|
|
408
|
+
semântica do `size` no Button (encolhe o espaço, não a letra). O `<Chat>` passou a usar.
|
|
409
|
+
A régua do `prose` é de artigo: 16px em volta de cada parágrafo, 20px no bloco de código e
|
|
410
|
+
**40px em volta de cada `<hr>`** — numa bolha, uma linha divisória custa mais que a mensagem.
|
|
411
|
+
|
|
412
|
+
O espaçamento mora no `theme.css` (`.prose[data-size='sm']`), junto do resto da tipografia,
|
|
413
|
+
e o seletor é **universal** (`> *`), não uma lista de tags — a primeira versão desta regra
|
|
414
|
+
enumerava p/ul/ol/pre/blockquote/h1-h4/li e deixava passar justamente o `hr`, o maior de
|
|
415
|
+
todos. Elemento novo (table, figure, dl) entra sozinho.
|
|
416
|
+
|
|
417
|
+
### Corrigido
|
|
418
|
+
|
|
419
|
+
**O respiro fantasma nas mensagens.** As bolhas do `Chat` tentavam zerar a margem do
|
|
420
|
+
primeiro e do último bloco com `[&>*:first-child]:mt-0`, mas o primeiro filho da bolha é o
|
|
421
|
+
*wrapper* do `prose` — que não tem margem; quem tem é o `<p>` um nível abaixo. A
|
|
422
|
+
compensação nunca surtiu efeito, e cada mensagem carregava ~16px invisíveis em cima e
|
|
423
|
+
embaixo. Agora mora dentro do `Markdown`, onde alcança o bloco certo.
|
|
424
|
+
|
|
425
|
+
## 7.0.1 — 2026-07-23
|
|
426
|
+
|
|
427
|
+
**Ponteiros mortos pro `DeleteButton`.** Duas páginas (`confirm` e `alert-dialog`) ainda
|
|
428
|
+
mandavam usá-lo pra excluir por contrato, um release depois de ele sair. O gate de doc
|
|
429
|
+
(`content-scope`) só enxerga JSX nos previews — prosa que cita componente inexistente passa,
|
|
430
|
+
e essa é a terceira vez no dia que a varredura à mão é que pega.
|
|
431
|
+
|
|
432
|
+
## 7.0.0 — 2026-07-23
|
|
433
|
+
|
|
434
|
+
**O `DeleteButton` durou um release.** Ele entrou no 6.0.0 e sai agora: era o
|
|
435
|
+
`ActionTrigger` com uma lixeira dentro — mesmas props (`action`, `input`, `label`,
|
|
436
|
+
`onSuccess`, `className`), e o trigger já tinha `variant`, `size: 'icon'` e a confirmação
|
|
437
|
+
do contrato. Pior: o nome furava a convenção da casa, onde contract-driven com `action` na
|
|
438
|
+
primeira prop é a família `Action*`. Criar componente onde cabia prop é o que os releases 3
|
|
439
|
+
a 5 desfizeram — repeti o erro e desfaço.
|
|
440
|
+
|
|
441
|
+
As três coisas que só ele tinha subiram pro `ActionTrigger`:
|
|
442
|
+
|
|
443
|
+
- **`icon`** — o botão vira icon-only, com o rótulo no tooltip e no `aria-label`, visual
|
|
444
|
+
discreto (vermelho no hover quando o contrato marca `destructive`) e clique que **não
|
|
445
|
+
vaza** pro item em volta.
|
|
446
|
+
- **`itemLabel`** — nomeia o alvo na pergunta, entre aspas, antes da mensagem do contrato.
|
|
447
|
+
- **Erro de negócio falado** — a frase do servidor vence o rótulo genérico em
|
|
448
|
+
`conflict`/`validation`/`not_found`; o resto cai no rótulo, pra não vazar
|
|
449
|
+
`violates foreign key constraint …` pra quem só clicou num botão. Agora vale pra QUALQUER
|
|
450
|
+
ação de item (arquivar, duplicar, reprocessar), não só excluir.
|
|
451
|
+
|
|
452
|
+
### Breaking
|
|
453
|
+
|
|
454
|
+
`DeleteButton` e `DeleteButtonProps` saíram.
|
|
455
|
+
|
|
456
|
+
```tsx
|
|
457
|
+
// Antes
|
|
458
|
+
<DeleteButton action={unitDelete} input={{ id: u.id }} itemLabel={u.name} />
|
|
459
|
+
// Depois
|
|
460
|
+
<ActionTrigger action={unitDelete} input={{ id: u.id }} icon={<Trash2 />} label="Excluir" itemLabel={u.name} />
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**Confira se o contrato declara `confirm`.** O `DeleteButton` perguntava SEMPRE (montava a
|
|
464
|
+
caixa com textos default); o `ActionTrigger` segue a regra da casa — sem `action.confirm`
|
|
465
|
+
(ou a prop `confirm`), o disparo é **direto**. Um delete sem `confirm` declarado passaria a
|
|
466
|
+
apagar sem perguntar. A confirmação é spec: declare no contrato.
|
|
467
|
+
|
|
468
|
+
## 6.0.3 — 2026-07-23
|
|
469
|
+
|
|
470
|
+
**A página do AlertDialog mostra o caminho curto.** Ela dizia em texto que `confirm()` faz
|
|
471
|
+
a mesma pergunta em uma linha, mas não mostrava — quem chegasse por ali montava as 15
|
|
472
|
+
linhas assim mesmo. Agora tem preview vivo e clicável.
|
|
473
|
+
|
|
474
|
+
## 6.0.2 — 2026-07-23
|
|
475
|
+
|
|
476
|
+
**`confirm()` avisa da colisão com o `window.confirm`.** Esquecer o import não dá erro: o
|
|
477
|
+
TypeScript resolve pro global e o código passa a abrir a caixa cinza do sistema (que
|
|
478
|
+
webview suprime e não tem tema). A doc agora abre com esse aviso e sugere
|
|
479
|
+
`no-restricted-globals` no ESLint.
|
|
480
|
+
|
|
481
|
+
## 6.0.1 — 2026-07-23
|
|
482
|
+
|
|
483
|
+
**Import órfão que quebrava o typecheck de quem consome.** A migração da confirmação do
|
|
484
|
+
`ActionList` pro AlertDialog deixou `DialogDescription` importado sem uso — inofensivo
|
|
485
|
+
aqui, erro (`TS6133`) em todo app com `noUnusedLocals`, que é o caso dos nossos. O gate do
|
|
486
|
+
pacote não pegava porque o tsconfig DELE não tinha a regra: agora tem
|
|
487
|
+
(`noUnusedLocals` + `noUnusedParameters`), então o próximo órfão morre antes de publicar.
|
|
488
|
+
|
|
489
|
+
## 6.0.0 — 2026-07-23
|
|
490
|
+
|
|
491
|
+
**`confirm()` — a confirmação em uma linha.**
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
if (await confirm({ title: 'Excluir a sessão?', action: 'Excluir', variant: 'destructive' })) …
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Mesma forma do `toast` (superfície imperativa + host no shell), com a diferença de que esta
|
|
498
|
+
**responde** (`Promise<boolean>`). Nasceu de um levantamento constrangedor: a casa
|
|
499
|
+
confirmava exclusão de QUATRO jeitos — um `DeleteButton` copiado entre dois repos (116 e
|
|
500
|
+
130 linhas, já divergentes), dialogs montados à mão em três telas, um botão de "dois
|
|
501
|
+
cliques" e o confirm declarativo dos patterns. Todos porque montar a pergunta custava ~15
|
|
502
|
+
linhas de JSX.
|
|
503
|
+
|
|
504
|
+
Requer `<ConfirmHost />` no shell, ao lado do `<Toaster />`. Sem ele **lança** em vez de
|
|
505
|
+
devolver promise pendurada — clique sem efeito é o pior desfecho pra uma pergunta
|
|
506
|
+
destrutiva (mesma guarda do `onAsk` no driver de IA).
|
|
507
|
+
|
|
508
|
+
**`<DeleteButton action input />` sobe pro pacote.** É a cópia que existia nos dois repos,
|
|
509
|
+
na versão mais madura: allowlist de erro de NEGÓCIO (a frase do servidor só vence o rótulo
|
|
510
|
+
genérico em `conflict`/`validation`/`not_found` — erro inesperado traria
|
|
511
|
+
`violates foreign key constraint …` pro toast de quem só clicou em excluir).
|
|
512
|
+
|
|
513
|
+
**Toda confirmação da casa passa a ser o AlertDialog compacto.** `ActionTrigger` e
|
|
514
|
+
`ActionList` confirmavam com `Dialog` comum — agora usam `AlertDialog`: `role="alertdialog"`,
|
|
515
|
+
não fecha no clique fora. O compacto virou o desenho ÚNICO.
|
|
516
|
+
|
|
517
|
+
**`<Alert>` ganha forma curta:** `<Alert icon={<Info />} title="…" description="…" />` numa
|
|
518
|
+
linha; a composição segue pro conteúdo rico.
|
|
519
|
+
|
|
520
|
+
### Corrigido: o alert saía uma palavra por linha
|
|
521
|
+
|
|
522
|
+
`<Alert>Workspace Empresa X sincronizado.</Alert>` renderizava a frase quebrada, uma
|
|
523
|
+
palavra por linha — e a doc ensinava exatamente esse uso. Duas causas somadas: o alert era
|
|
524
|
+
SEMPRE `grid grid-cols-[0_1fr]`, então texto solto virava item anônimo na 1ª coluna, de
|
|
525
|
+
largura **zero**; e o `AlertDescription` era `grid justify-items-start`, onde cada nó de
|
|
526
|
+
uma frase interpolada (`Workspace {cliente} {nome} sincronizado.`) virava um item empilhado.
|
|
527
|
+
Agora o grid só existe quando há ícone, a descrição não é grid, e texto cru como filho cai
|
|
528
|
+
no slot de descrição sozinho.
|
|
529
|
+
|
|
530
|
+
### Breaking
|
|
531
|
+
|
|
532
|
+
**`size` saiu do `AlertDialogContent`** — o compacto (`max-w-xs`, footer em duas colunas) é
|
|
533
|
+
o único desenho. Quem passava `size="sm"` só apaga a prop; quem dependia do largo
|
|
534
|
+
(`sm:max-w-lg`) precisa decidir: confirmação curta cabe no compacto, e conteúdo que não
|
|
535
|
+
cabe provavelmente não era uma confirmação — é `Dialog`.
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
// Antes // Depois
|
|
539
|
+
<AlertDialogContent size="sm"> <AlertDialogContent>
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
O `Alert` deixou de aceitar o atributo `title` do HTML (agora `title` é o título do alert).
|
|
543
|
+
Era tooltip nativo, que a casa já bania em favor do `Tooltip`.
|
|
544
|
+
|
|
545
|
+
## 5.0.1 — 2026-07-23
|
|
546
|
+
|
|
547
|
+
**Nome morto na doc, e o gate que faltava.** O preview do Composer ainda montava
|
|
548
|
+
`<DropdownMenu>` — transpila igual, e explodia no browser. Sete `whenToUse` do catálogo
|
|
549
|
+
também apontavam pra ele ("pra menu de ações, use DropdownMenu"), mandando quem lesse
|
|
550
|
+
importar um nome que não existe mais.
|
|
551
|
+
|
|
552
|
+
O `content-fences` provava que todo fence TRANSPILA; agora um irmão (`content-scope`)
|
|
553
|
+
prova que **todo componente citado num preview existe no scope** — foi ele que teria
|
|
554
|
+
pegado isto sozinho, e é o que cobre o próximo rename.
|
|
555
|
+
|
|
556
|
+
## 5.0.0 — 2026-07-23
|
|
557
|
+
|
|
558
|
+
**Cinco menus viram um: `Menu`.** O catálogo tinha `DropdownMenu`, `ContextMenu`,
|
|
559
|
+
`Menubar`, `NavigationMenu` e `Command` — e a decisão "qual dos cinco" caía na tela toda
|
|
560
|
+
vez. Medindo o uso real (softize + cliente + o próprio Opus): DropdownMenu em 4 telas,
|
|
561
|
+
Command como motor do Select e do IconPicker, e **zero** para os outros três.
|
|
562
|
+
|
|
563
|
+
- **Saem `ContextMenu` e `Menubar`**: é o mesmo menu com outro gatilho — o ContextMenu é o
|
|
564
|
+
dropdown no botão direito; o Menubar são N dropdowns numa barra, vocabulário de app
|
|
565
|
+
desktop (Arquivo/Editar/Ver) que nenhuma tela nossa tem.
|
|
566
|
+
- **Sai `NavigationMenu`**: mega-menu de LINKS de site (semântica `<nav>`, não
|
|
567
|
+
`role="menu"`). Papel legítimo — que a casa não exerce.
|
|
568
|
+
- **`Command` fica**: não é menu, é busca por teclado (e é o motor do Select).
|
|
569
|
+
- **`DropdownMenu` vira `Menu`**: sem irmãos pra distinguir, o qualificador não
|
|
570
|
+
distinguia de nada. "Dropdown" ainda por cima descreve como abre, não o que é — e no uso
|
|
571
|
+
corrente costuma significar o `Select`.
|
|
572
|
+
|
|
573
|
+
Componente portado não se cria, se baixa: no dia que existir a primeira tela com menu de
|
|
574
|
+
contexto, `scripts/port-shadcn.mjs context-menu` traz o ContextMenu byte-fiel de volta em
|
|
575
|
+
um comando. Até lá, cada entrada a mais no catálogo só cobra pedágio na decisão.
|
|
576
|
+
|
|
577
|
+
### Breaking
|
|
578
|
+
|
|
579
|
+
`DropdownMenu*` → `Menu*` (rename puro, mesma API):
|
|
580
|
+
|
|
581
|
+
```tsx
|
|
582
|
+
// Antes // Depois
|
|
583
|
+
<DropdownMenu> <Menu>
|
|
584
|
+
<DropdownMenuTrigger asChild> <MenuTrigger asChild>
|
|
585
|
+
<DropdownMenuContent align="end"> <MenuContent align="end">
|
|
586
|
+
<DropdownMenuItem … <MenuItem …
|
|
587
|
+
<DropdownMenuCheckboxItem … <MenuCheckboxItem …
|
|
588
|
+
<DropdownMenuSeparator /> <MenuSeparator />
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Vale pros 15 subcomponentes (`Trigger`, `Content`, `Item`, `CheckboxItem`, `RadioGroup`,
|
|
592
|
+
`RadioItem`, `Label`, `Separator`, `Shortcut`, `Group`, `Portal`, `Sub`, `SubTrigger`,
|
|
593
|
+
`SubContent`). `data-slot`: `dropdown-menu-*` → `menu-*`.
|
|
594
|
+
|
|
595
|
+
`ContextMenu*`, `Menubar*` e `NavigationMenu*` **não existem mais**. Menu de contexto e
|
|
596
|
+
barra de menus se fazem com `Menu`; navegação de links, com `<nav>` + `Button`/`Link` (ou
|
|
597
|
+
re-porte o componente do registry shadcn se o caso for mesmo um mega-menu).
|
|
598
|
+
|
|
599
|
+
## 4.0.0 — 2026-07-23
|
|
600
|
+
|
|
601
|
+
**Sheet e Drawer eram o mesmo componente com dois motores.** Painel que desliza de uma
|
|
602
|
+
borda: o `Sheet` fazia isso com Radix Dialog, o `Drawer` com [vaul](https://vaul.emilkowal.ski/)
|
|
603
|
+
(que acrescenta gesto de arrastar e snap points). Mesmo papel, duas entradas no catálogo —
|
|
604
|
+
e a decisão "qual dos dois" caindo na tela toda vez. Prova de que não funcionava: o único
|
|
605
|
+
consumidor da casa importava o `Sheet` e batizou o wrapper local de `Drawer`.
|
|
606
|
+
|
|
607
|
+
Fica **um**, com o motor Radix (mesma família do Dialog: overlay, trap de foco, ESC) e o
|
|
608
|
+
nome **`Drawer`** — que é como MUI, Ant Design, Chakra e Mantine chamam o painel de borda;
|
|
609
|
+
"sheet" é vocabulário do shadcn/Apple. O `vaul` sai das dependências. Se um dia aparecer o
|
|
610
|
+
caso real de bottom sheet arrastável, ele volta como PROP do Drawer, não como componente
|
|
611
|
+
irmão.
|
|
612
|
+
|
|
613
|
+
Limpeza junto: `@radix-ui/react-select` continuava declarado desde o 3.0.0, sem ninguém
|
|
614
|
+
importar. Saiu.
|
|
615
|
+
|
|
616
|
+
### Breaking
|
|
617
|
+
|
|
618
|
+
`Sheet`, `SheetTrigger`, `SheetClose`, `SheetContent`, `SheetHeader`, `SheetFooter`,
|
|
619
|
+
`SheetTitle` e `SheetDescription` **saíram**: troque o prefixo por `Drawer*`. A API é a
|
|
620
|
+
mesma (`side`, `showCloseButton`, `open`/`onOpenChange`, `asChild`) — é rename, não
|
|
621
|
+
redesenho.
|
|
622
|
+
|
|
623
|
+
```tsx
|
|
624
|
+
// Antes // Depois
|
|
625
|
+
<Sheet open={o} onOpenChange={setO}> <Drawer open={o} onOpenChange={setO}>
|
|
626
|
+
<SheetContent side="right"> <DrawerContent side="right">
|
|
627
|
+
<SheetHeader><SheetTitle>… <DrawerHeader><DrawerTitle>…
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Quem usava o `Drawer` do vaul: a API de composição é a mesma, mas **`direction` virou
|
|
631
|
+
`side`**, e `DrawerPortal`/`DrawerOverlay` deixaram de ser exportados (o `DrawerContent`
|
|
632
|
+
já os inclui). O gesto de arrastar e os snap points não existem mais.
|
|
633
|
+
|
|
634
|
+
`data-slot`: `sheet-*` → `drawer-*`.
|
|
635
|
+
|
|
636
|
+
## 3.0.1 — 2026-07-23
|
|
637
|
+
|
|
638
|
+
**A lista do `variant="ghost"` ganha largura própria.** Ela seguia a largura do controle
|
|
639
|
+
(certo no campo de form, errado na barra do composer: o gatilho ali é do tamanho de uma
|
|
640
|
+
chave curta como `GB-42`, e a lista saía com 160px cortando o título inteiro). Agora o
|
|
641
|
+
ghost abre em `min-w-56 max-w-80` — o resto do modo default segue acompanhando o campo.
|
|
642
|
+
|
|
643
|
+
## 3.0.0 — 2026-07-23
|
|
644
|
+
|
|
645
|
+
**Um Select só.** Escolher em lista era QUATRO componentes com TRÊS APIs diferentes
|
|
646
|
+
(`Select` composicional do Radix, `NativeSelect` com `NativeSelectOption` como filho,
|
|
647
|
+
`Combobox` data-driven, `ComposerSelect` data-driven com outro nome de props) — e a
|
|
648
|
+
decisão "qual dos quatro" caía na tela, toda vez. Agora é **um componente, modos por
|
|
649
|
+
prop**: `<Select>` com `options` (dado, nunca JSX de item) + `searchable` · `multiple` ·
|
|
650
|
+
`native` · `variant="ghost"`. A régua que importava (achabilidade: lista longa precisa de
|
|
651
|
+
busca) virou UMA prop, não a escolha do componente.
|
|
652
|
+
|
|
653
|
+
**Corrigido junto: o `id` agora chega ao campo.** O cmdk sobrescrevia o `id` do input, e
|
|
654
|
+
o `htmlFor` do Label não achava controle nenhum — clicar no rótulo não focava. O Select
|
|
655
|
+
reaplica o próprio id; o `ActionFormField` perdeu o fallback que existia só por causa
|
|
656
|
+
disso.
|
|
657
|
+
|
|
658
|
+
**Nav da doc por categoria.** O catálogo de UI (`/ui`) deixou de ser uma lista alfabética
|
|
659
|
+
de 67 itens + uma seção "Patterns" à parte: são dez grupos (Estrutura, Actions, IA,
|
|
660
|
+
Formulário, Botões, Navegação, Exibição, Feedback, Sobreposição, Layout), e o item de
|
|
661
|
+
subgrupo rotulado agora **recua** sob o rótulo no `SectionShell` (três níveis na mesma
|
|
662
|
+
margem não eram hierarquia).
|
|
663
|
+
|
|
664
|
+
### Breaking
|
|
665
|
+
|
|
666
|
+
`Combobox`, `NativeSelect`, `NativeSelectOption`, `NativeSelectOptGroup`, `ComposerSelect`,
|
|
667
|
+
`SelectClear` e toda a API composicional do Radix (`SelectTrigger`, `SelectValue`,
|
|
668
|
+
`SelectContent`, `SelectItem`, `SelectGroup`, `SelectLabel`, `SelectSeparator`,
|
|
669
|
+
`SelectScrollUpButton`, `SelectScrollDownButton`) **saíram do pacote**. `ComboboxOption`
|
|
670
|
+
virou `SelectOption`.
|
|
671
|
+
|
|
672
|
+
```tsx
|
|
673
|
+
// Antes — Select do Radix (composicional)
|
|
674
|
+
<Select value={v} onValueChange={setV}>
|
|
675
|
+
<SelectTrigger id="papel"><SelectValue placeholder="Selecione" /></SelectTrigger>
|
|
676
|
+
<SelectContent>
|
|
677
|
+
<SelectItem value="dev">Developer</SelectItem>
|
|
678
|
+
</SelectContent>
|
|
679
|
+
</Select>
|
|
680
|
+
// Depois
|
|
681
|
+
<Select id="papel" value={v} onChange={setV} placeholder="Selecione"
|
|
682
|
+
options={[{ value: 'dev', label: 'Developer' }]} />
|
|
683
|
+
|
|
684
|
+
// Antes — NativeSelect
|
|
685
|
+
<NativeSelect value={v} onChange={(e) => setV(e.target.value)}>
|
|
686
|
+
<NativeSelectOption value="dev">Developer</NativeSelectOption>
|
|
687
|
+
</NativeSelect>
|
|
688
|
+
// Depois
|
|
689
|
+
<Select native value={v} onChange={setV} options={[{ value: 'dev', label: 'Developer' }]} />
|
|
690
|
+
|
|
691
|
+
// Antes — Combobox → Depois
|
|
692
|
+
<Combobox value={v} onChange={setV} options={o} />
|
|
693
|
+
<Select searchable value={v} onChange={setV} options={o} />
|
|
694
|
+
|
|
695
|
+
// Antes — ComposerSelect → Depois
|
|
696
|
+
<ComposerSelect value={v} onChange={setV} options={o} />
|
|
697
|
+
<Select variant="ghost" value={v} onChange={setV} options={o} />
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Ponto a ponto:
|
|
701
|
+
|
|
702
|
+
- **`onChange` recebe o VALUE**, não o evento — inclusive no `native` (era
|
|
703
|
+
`e.target.value`).
|
|
704
|
+
- **Grupo é dado**: `group` na opção (vira `<optgroup>` no native, heading no custom);
|
|
705
|
+
`SelectGroup`/`SelectLabel`/`SelectSeparator` não existem mais.
|
|
706
|
+
- **`clearable` é prop** (o `SelectClear` por composição saiu).
|
|
707
|
+
- **Item rico**: `label` é sempre string (campo, busca, a11y) e o JSX vai em `content`.
|
|
708
|
+
`triggerLabel` (do ComposerSelect) segue na opção.
|
|
709
|
+
- **`defaultValue` saiu**: o Select é controlado (`value` + `onChange`).
|
|
710
|
+
- **Largura**: o trigger do Radix era `w-fit`; o Select é `min-w-40` e cresce com o pai —
|
|
711
|
+
quem passava `w-full` em form pode tirar.
|
|
712
|
+
- **`data-slot`**: `combobox-*` → `select-*` (`select-control`, `select-input`,
|
|
713
|
+
`select-item`, `select-chip`, `select-clear`, `select-toggle-all`). Teste E2E que mirava
|
|
714
|
+
`[data-slot="select-trigger"]` passa a mirar `[data-slot="select-control"]`.
|
|
715
|
+
- **Visual**: o campo não-buscável agora é um input readOnly (mesma altura e moldura de
|
|
716
|
+
antes); o Radix abria a lista alinhada ao item selecionado, o novo abre ancorado no
|
|
717
|
+
campo.
|
|
718
|
+
|
|
719
|
+
Os patterns da casa (`ActionForm`, `ActionList`, filtros, `Composer`) já vêm migrados —
|
|
720
|
+
`fieldOptions`/`filterOptions` agora tipam `SelectOption[]`.
|
|
721
|
+
|
|
722
|
+
## 2.45.0 — 2026-07-23
|
|
723
|
+
|
|
724
|
+
**SectionShell: nav redimensionável (`resizable`).** A divisa arrastável que o `rail` do
|
|
725
|
+
AppShell já tinha desce pro terceiro nível — quando os rótulos da nav variam de tamanho,
|
|
726
|
+
o `w-56` fixo ora aperta ora sobra. Opt-in (default segue a coluna fixa); ligado, a
|
|
727
|
+
largura vira % (`navDefaultSize` 18, `navMinSize` 12).
|
|
728
|
+
|
|
729
|
+
**O Combobox volta pra régua de altura da casa** — e ganha `size`. Ele era o único
|
|
730
|
+
controle de form em `min-h-10`: numa toolbar ao lado de Button/Select/Input (todos h-9)
|
|
731
|
+
ficava visivelmente mais alto, "pegando carona" no flex. Agora o default é **`min-h-9`**
|
|
732
|
+
(a altura do DS) e existe **`size="sm"`** (h-8) pra barra de filtros densa — o mesmo par
|
|
733
|
+
que `Select` e `NativeSelect` já tinham. **Sem breaking de API** (prop opcional), mas é
|
|
734
|
+
**mudança VISUAL**: todo Combobox encolhe 4px por padrão. Se alguma tela dependia dos
|
|
735
|
+
40px, passe `className` com a altura desejada.
|
|
736
|
+
|
|
737
|
+
## 2.44.0 — 2026-07-23
|
|
738
|
+
|
|
739
|
+
**Ícone DENTRO do campo, uniforme.** `Select` (via `SelectTrigger`), `NativeSelect` e
|
|
740
|
+
`Combobox` ganham o prop **`icon`** — um ícone leading dentro do controle, herdando
|
|
741
|
+
`size-4` + `text-muted-foreground` como o resto (mesma linguagem do `InputGroupAddon`,
|
|
742
|
+
que já cobria os `<Input>` de texto). Nasceu de uma tela real construída no Maestro que
|
|
743
|
+
ficou com os ícones (calendário/prédio/pessoa) **jogados ao lado** dos filtros porque os
|
|
744
|
+
selects não tinham onde pôr — n≥2 (todo filtro faz isso). **Sem breaking** (prop
|
|
745
|
+
opcional; sem `icon`, nada muda). Uso: `<NativeSelect icon={<Building2 />}>…`,
|
|
746
|
+
`<SelectTrigger icon={<CalendarDays />}>`, `<Combobox icon={<Ticket />} … />`. Pros
|
|
747
|
+
inputs de texto, siga usando `InputGroup` + `InputGroupAddon align="inline-start"`.
|
|
748
|
+
|
|
749
|
+
## 2.43.0 — 2026-07-23
|
|
750
|
+
|
|
751
|
+
**Terceira leva: os promovidos por reincidência.** `<Truncate>`, marshalling de `t.json()`
|
|
752
|
+
no repo, e o `readOnlyContextPool` — a candidatura de segurança do "SQL escrito por LLM"
|
|
753
|
+
(n=2 na casa) vira primitivo da base. **Sem breaking** (exports/comportamentos aditivos;
|
|
754
|
+
ver a nota de migração do t.json()).
|
|
755
|
+
|
|
756
|
+
- **`<Truncate>` (novo primitivo, `ui/react`).** Texto truncado com tooltip SÓ quando
|
|
757
|
+
transborda (medição do overflow via ResizeObserver, re-medida em resize) — aposenta a
|
|
758
|
+
composição `block truncate` + `title` sempre presente, que mostra dica até em texto
|
|
759
|
+
que não corta. `tooltip` sobrepõe o conteúdo da dica (default: os children). Requer
|
|
760
|
+
`TooltipProvider` na raiz (o esqueleto já monta). Promovido por reincidência (Ai.tsx
|
|
761
|
+
do Maestro, duas tabelas).
|
|
762
|
+
- **`kyselyRepo` marshalla campo `t.json()` (objeto→jsonb, guiado pela declaração).**
|
|
763
|
+
A dupla `ColumnType<unknown, string, never>` + `JSON.stringify` na mão morre no
|
|
764
|
+
caminho do repo: objeto E array são serializados na escrita (insert/update). O caso
|
|
765
|
+
traiçoeiro era o ARRAY — o pg stringifica objeto plano sozinho, mas array vira
|
|
766
|
+
literal de array do PG (errado pra jsonb) sem quebrar typecheck. **Migração:** quem
|
|
767
|
+
já passava STRING pré-serializada segue valendo (string passa direto — sem
|
|
768
|
+
double-encode); quem usava o repo com objeto ganha o conserto de graça. Query à mão
|
|
769
|
+
(fora do repo) continua responsável pelo próprio stringify.
|
|
770
|
+
- **`readOnlyContextPool` (`@softize/opus/data/readonly-pool`).** SQL escrito por LLM
|
|
771
|
+
com as QUATRO defesas dos ADRs 0003/0007 — todas no BANCO, nenhuma em regex: pool com
|
|
772
|
+
teto (`max` do pool e/ou `maxConcurrent` com fila própria), `BEGIN TRANSACTION READ
|
|
773
|
+
ONLY`, `SET LOCAL ROLE` por transação (alcance do contexto; roles/grants
|
|
774
|
+
default-fechado são migração sua) e protocolo estendido (o SQL roda sempre com array
|
|
775
|
+
de valores — multi-sentença é recusada pelo protocolo). Saindo, `DISCARD ALL` devolve
|
|
776
|
+
a conexão limpa; limpeza que falha DESTRÓI a conexão (nunca volta suja ao pool).
|
|
777
|
+
`statement_timeout` local por transação (default 15s). Zero-dep: aceita qualquer
|
|
778
|
+
pool pg-like (`connect()`), sem dependência direta de `pg`.
|
|
779
|
+
|
|
780
|
+
## 2.42.1 — 2026-07-23
|
|
781
|
+
|
|
782
|
+
Destrava a publicação da 2.42.0, que o gate do release barrou ANTES do registry (a
|
|
783
|
+
tag existe; a versão nunca publicou — **instale esta**). Mesmo conteúdo, mais o conserto:
|
|
784
|
+
|
|
785
|
+
- **Import órfão reprovava o smoke do esqueleto.** A ampliação do `useTriggerAction`
|
|
786
|
+
deixou `SimpleContract` importado sem uso em `ui/drivers/react.tsx`. O tsconfig do
|
|
787
|
+
PACOTE não cobra unused — o do app escafoldado cobra (`noUnusedLocals`), e como o
|
|
788
|
+
Opus ship source, o tsc do consumidor compila a base: o smoke do release
|
|
789
|
+
(`opus create` + typecheck) barrou o tarball, exatamente como devia. Import
|
|
790
|
+
removido; o smoke roda limpo de ponta a ponta.
|
|
791
|
+
|
|
792
|
+
## 2.42.0 — 2026-07-23
|
|
793
|
+
|
|
794
|
+
> ⚠️ **Não publicou** — o smoke do release barrou o tarball (import órfão; o
|
|
795
|
+
> diagnóstico está na 2.42.1). O conteúdo abaixo vale e saiu na **2.42.1**.
|
|
796
|
+
|
|
797
|
+
**Segunda leva do 1º app real: UI composável + audit pronto pra dado sensível.** Fecha
|
|
798
|
+
quatro issues do consumidor (GB). **Sem breaking** (tipos ampliados e opções/exports
|
|
799
|
+
novos, todos aditivos).
|
|
800
|
+
|
|
801
|
+
- **`useTriggerAction` aceita QUALQUER contrato.** Era tipado só pra `SimpleContract`,
|
|
802
|
+
mas roda qualquer um (o hook usa name/kind e lê `invalidates`) — disparar um
|
|
803
|
+
FormContract fora de form (modal composto que submete `role.update`) exigia
|
|
804
|
+
`as unknown as SimpleContract<...>`. Agora o parâmetro é `ActionContract<TInput,
|
|
805
|
+
TData>`; o cast morre. Nenhuma chamada existente muda.
|
|
806
|
+
- **`useActionFormContext()` (novo export, `ui/react`).** A válvula de escape pra
|
|
807
|
+
CONTROLE CUSTOM no modo composição do `<ActionForm>`: campo com UI própria (a grade
|
|
808
|
+
de permissões que forçou abandonar o form inteiro) participa do form do contrato —
|
|
809
|
+
`form.watch`/`form.setValue`/`formState.errors` — mantendo zod-resolver, erro inline
|
|
810
|
+
e submit do contrato. Type `ActionFormContextValue` exportado junto.
|
|
811
|
+
- **Redator global plugável nos sinks de audit (`redact` em `pgAudit`/`consoleAudit`) +
|
|
812
|
+
`redactDeep` (`@softize/opus/audit`).** O pgAudit persistia input E output CRUS —
|
|
813
|
+
`user.create`/`setPassword` gravaria senha em claro; o `AuditConfig.redact` por action
|
|
814
|
+
só cobre dot-paths estáticos. O `redactDeep` é o redator recursivo por nome de chave
|
|
815
|
+
(default `/password|passwd|secret|token|authorization|api[-_]?key|credential/i`, em
|
|
816
|
+
qualquer profundidade, arrays inclusos): `pgAudit({ pool, redact: redactDeep })` e o
|
|
817
|
+
sink fica utilizável com dado sensível. `redactRecord` e `SENSITIVE_KEY_PATTERN`
|
|
818
|
+
exportados pra sink próprio reusar.
|
|
819
|
+
- **`AuditRecord` carrega `actionKind` e `actor.meta` populado.** Sink que filtra
|
|
820
|
+
leitura (kind list/view = ruído) decidia recebendo os DOMAINS por fora pra montar Set
|
|
821
|
+
de nomes — agora o record diz o kind. E `actor.meta` (que existia no tipo mas nunca
|
|
822
|
+
vinha) chega com `name`/`email` do user resolvido, quando presentes — só os dois
|
|
823
|
+
campos de identificação; o resto do `User` (shape aberto) não vaza pro log.
|
|
824
|
+
|
|
825
|
+
## 2.41.0 — 2026-07-23
|
|
826
|
+
|
|
827
|
+
**O `opus check` volta a ser gate de verdade + a leva de correções do 1º app real.** Fecha
|
|
828
|
+
seis issues do consumidor (GB): o check enxerga o split contrato/bind e não passa mais
|
|
829
|
+
verde vazio, `requires` sem `authorize` vira violação, o `gen` projeta o `requires` fiel
|
|
830
|
+
e poda órfãos, o `create` tolera repo recém-iniciado e o template nasce com os providers.
|
|
831
|
+
**Sem breaking de API** — mas o check pode ficar VERMELHO onde antes passava vacuamente
|
|
832
|
+
(é o conserto; ver o 1º e o 2º bullets).
|
|
833
|
+
|
|
834
|
+
- **`opus check` enxerga `defineContract` + `bindAction`.** Era cego: só `defineAction`,
|
|
835
|
+
e app refatorado pro split saía INTEIRO do radar ("Nenhuma action encontrada", exit 0 —
|
|
836
|
+
gate vazio passando verde por 200+ arquivos). Contrato passa pelas mesmas regras
|
|
837
|
+
(name/kind/ordem/`export const`); bind conta como action e cobra `export const`.
|
|
838
|
+
⚠️ Comportamento: **0 actions agora é exit ≠ 0** — gate sem nada pra checar não é
|
|
839
|
+
aprovação; a mensagem diz o que ele procura (e acusa contrato sem bind no diretório).
|
|
840
|
+
O hook `opus-check-on-stop` herda isso (bloqueia gate vazio).
|
|
841
|
+
- **Nova regra `requires-sem-authorize`.** `requires` é DECLARATIVO (o runtime 2.x não o
|
|
842
|
+
executa — e dev que confia nele deixa a action ABERTA; aconteceu de verdade na
|
|
843
|
+
graduação do reports). No `defineAction` acusa direto; no split, o join contrato↔bind é
|
|
844
|
+
cross-file por identificador exportado — `authorize` no contrato OU no binding
|
|
845
|
+
satisfaz; referência ambígua/não encontrada não acusa (sem falso-positivo). Runtime
|
|
846
|
+
executar `requires` como authorize default segue em discussão; esta régua fecha o
|
|
847
|
+
buraco sem mudar runtime.
|
|
848
|
+
- **`opus gen` poda órfãos.** `.md`/`.ts` que a rodada anterior gerou e esta não gerou
|
|
849
|
+
saem de `docs/`, `client-stubs/` e `client-stubs/dicts/` — removido um domínio, a doc
|
|
850
|
+
que o AGENTE lê parava anunciando endpoint 404. Só as extensões geradas nos dirs
|
|
851
|
+
gerenciados; qualquer outro arquivo fica. O sumário lista o que podou.
|
|
852
|
+
- **`opus gen` projeta o `requires` FIEL no manifest.** `requires: ['sales','hr']`
|
|
853
|
+
(semântica ANY) saía `"permission": "sales"` — a spec mentia por omissão. Agora lista
|
|
854
|
+
sai lista (`permission: string | string[] | null`); a doc mostra
|
|
855
|
+
`` `sales` | `hr` (qualquer uma) ``; os stubs já emitiam o JSON certo.
|
|
856
|
+
- **`opus create` tolera repo recém-iniciado.** Dir com só `.git` (e/ou README/LICENSE)
|
|
857
|
+
não é mais "já existe e não está vazio" — `git init` + remote ANTES do scaffold é o
|
|
858
|
+
ponto de partida normal de repo-cliente. Vale pro `--monorepo` e pro modo app; os
|
|
859
|
+
templates não têm esses arquivos, então segue sem clobber por construção.
|
|
860
|
+
- **Template do app nasce com o trio de providers e favicon.** `QueryClientProvider` →
|
|
861
|
+
`TbdlibProvider` → `TooltipProvider` no `main.tsx` (qualquer Tooltip da base explodia
|
|
862
|
+
no 1º uso; os hooks de action exigem os outros dois) e `public/favicon.svg` + link no
|
|
863
|
+
`index.html` (some o 404 do console).
|
|
864
|
+
|
|
865
|
+
## 2.40.0 — 2026-07-22
|
|
866
|
+
|
|
867
|
+
**Modo design: `fake`/`fakeMany` + split de reação.** Duas peças aditivas que fundam o preview
|
|
868
|
+
UI-only-com-dado — o `mockHandler`/`OPUS_MODE=design` (que já existia) agora tem com o que ser
|
|
869
|
+
alimentado — e deixam a REAÇÃO autorável como contrato. **Sem breaking** (só exports novos).
|
|
870
|
+
|
|
871
|
+
- **`fake(schema)` / `fakeMany(schema, n)` (`@softize/opus/testing`).** Dado sintético
|
|
872
|
+
DETERMINÍSTICO (PRNG semeado, sem `Math.random` → preview reproduzível) que SATISFAZ o schema
|
|
873
|
+
zod — matéria-prima do `mockHandler` no modo design e de fixtures de teste. Heurística por
|
|
874
|
+
check do zod (email length-aware, uuid, url, datetime) e por nome de campo (id, data,
|
|
875
|
+
telefone, cpf/cnpj, nome…), sabor pt-BR; respeita min/max/int/length/enum/literal (formato por
|
|
876
|
+
NOME cede pro length quando há restrição).
|
|
877
|
+
- **Split `defineReactionContract` → `bindReaction` (`@softize/opus/core`).** Espelha
|
|
878
|
+
`defineContract`/`bindAction` pras reações: a DECLARAÇÃO (`name`/`on`/`description`/`authorize`)
|
|
879
|
+
é isomorfa e autorável no design; `handler` + entrega (`retry`/`timeout`/`dedup`/`concurrency`)
|
|
880
|
+
vão no `bindReaction`, que produz uma `ReactionDef` normal (o runtime não sabe do split).
|
|
881
|
+
- **Docs.** `mockHandler`/`OPUS_MODE=design` e `fake` documentados (estavam implementados mas
|
|
882
|
+
invisíveis). Nota: o `mockHandler` roda no server de design; tirá-lo do bundle web é transform
|
|
883
|
+
deferido (o contrato é isomorfo — gate por `import.meta.env` estouraria no server).
|
|
884
|
+
|
|
885
|
+
## 2.39.0 — 2026-07-22
|
|
886
|
+
|
|
887
|
+
**Elicitação: o agente pergunta à pessoa no meio do turno, seguro por construção.** Fecha a
|
|
888
|
+
pegadinha vivida no chat do Maestro — o `AskUserQuestion` embutido do Claude Code, em modo
|
|
889
|
+
headless, volta VAZIO e a pergunta some pro usuário (o agente "infere sozinho"). O contrato +
|
|
890
|
+
o guard agora moram na base, então qualquer consumidor de IA do Opus nasce protegido. **Sem
|
|
891
|
+
breaking** (tipos/campos/exports novos, aditivos).
|
|
892
|
+
|
|
893
|
+
- **Contrato de pergunta (novos tipos, `@softize/opus`).** `AskQuestion` (enunciado + `header`
|
|
894
|
+
+ `options` + `multiSelect`), `AskOption` e `AskAnswer` (rótulos escolhidos + texto livre) —
|
|
895
|
+
a forma única que todo mundo fala. Espelha o AskUserQuestion do Claude Code, mas servida por
|
|
896
|
+
um round-trip REAL.
|
|
897
|
+
- **`AiRunOptions.onAsk` / `BoundAiRunOptions.onAsk` (novo hook).** O round-trip de "perguntar
|
|
898
|
+
à pessoa": com ele, o driver INTERCEPTA a tool reservada `ask_user` (injetada automaticamente
|
|
899
|
+
no catálogo) e chama o handler, devolvendo as respostas ao modelo. Passa direto por `ctx.ai`
|
|
900
|
+
(o `bindAi` já espalha as opções).
|
|
901
|
+
- **Guard anti dead-end no driver `ai/anthropic` (`run` + `runStream`).** SEM `onAsk`, uma
|
|
902
|
+
chamada a `ask_user` é RECUSADA com erro acionável ("pergunte em texto e siga") em vez de
|
|
903
|
+
cair no `execute` do consumidor — que, num loop headless/one-shot (o caso dos copilots
|
|
904
|
+
`services/ai`), resolveria contra o servidor MCP e morreria em silêncio. Segurança por
|
|
905
|
+
construção: um ask-tool não vira dead-end silencioso.
|
|
906
|
+
- **Novos exports em `@softize/opus/ai`.** `ASK_USER_TOOL` (nome reservado), `ASK_INPUT_SCHEMA`
|
|
907
|
+
(JSON Schema da tool), `ASK_TOOL_DESCRIPTION` e `formatAnswers()` — pra quem serve o tool por
|
|
908
|
+
fora (ex.: o chat do Maestro, que roda via `claude` CLI e monta o `ask_user` como MCP).
|
|
909
|
+
|
|
910
|
+
## 2.38.0 — 2026-07-22
|
|
911
|
+
|
|
912
|
+
O **menu vira primitivo pivotável** (`<ShellNav>`), fechando o gap "cada app copia a sidebar
|
|
913
|
+
à mão" — Maestro, back-office e GB reescreviam o `<button>` do item de menu (3 produtos).
|
|
914
|
+
**Sem breaking** (componentes/props novos).
|
|
915
|
+
|
|
916
|
+
- **`<ShellNav>` (novo primitivo PIVOTÁVEL).** O menu — grupos → itens, com `heading` e
|
|
917
|
+
âncora inferior (`footer`) — que serve os DOIS lares do chrome sem flag: na sidebar do
|
|
918
|
+
`<AppShell>` recolhe pra ícone-só (com tooltip) quando a sidebar recolhe; dentro do
|
|
919
|
+
conteúdo (num `<SectionShell>`) fica expandido. Sabe onde está pelo novo contexto de slot
|
|
920
|
+
do AppShell (`useSidebarSlot`) — o app não passa o estado de rail em duas mãos (fecha a
|
|
921
|
+
nota `n=2` que o `gb-nav-rail` deixou anotada). v1 PLANA; aninhamento/árvore + DnD COMPOSTO
|
|
922
|
+
vêm quando o 1º consumidor de árvore migrar. Acompanha `<ShellNavHeading>` (título + slot
|
|
923
|
+
de ação, o "+" de criar). Desenho em `docs/shellnav.md`.
|
|
924
|
+
- **`<AppShell>` publica o slot da sidebar (`useSidebarSlot`).** `useAppShell` não distinguia
|
|
925
|
+
sidebar × conteúdo (o colapso vale a árvore toda); o novo contexto existe SÓ dentro dos
|
|
926
|
+
slots da sidebar (header/nav/rodapé), `null` no conteúdo. É o que torna o `<ShellNav>`
|
|
927
|
+
pivotável de verdade — e o que o `gb-nav-rail` previu virar prop do Opus quando n=2.
|
|
928
|
+
- **`<Composer>`: textarea plana também no escuro.** Faltava `dark:bg-transparent` — o
|
|
929
|
+
`dark:bg-input/30` do primitivo `Textarea` vazava dentro da pílula, dando um fundo de input
|
|
930
|
+
à área de escrita no tema escuro. Agora a textarea herda o `bg-card` do composer nos dois temas.
|
|
931
|
+
|
|
932
|
+
## 2.37.0 — 2026-07-22
|
|
933
|
+
|
|
934
|
+
Fecha o desenho da 2.36.0: os seletores da barra viram componente, e o `<Chat>` passa a
|
|
935
|
+
hospedá-los. **Sem breaking** (props novas, opcionais).
|
|
936
|
+
|
|
937
|
+
- **`<ComposerSelect>` (novo primitivo).** O seletor discreto que vive na barra de ações
|
|
938
|
+
do composer: gatilho ghost (rótulo + chevron) que abre um menu com `Check` no ativo.
|
|
939
|
+
Era o padrão que o **Maestro** escrevia à mão — DUAS vezes (app + task) — e que a GB ia
|
|
940
|
+
copiar pro agente: a mesma receita (trigger + DropdownMenu + Check), agora uma vez só.
|
|
941
|
+
Controlado (`value`/`onChange`), opções `{ value, label, hint?, triggerLabel? }` — `hint`
|
|
942
|
+
é o detalhe à direita (a chave da task em mono) e `triggerLabel` encurta o gatilho quando
|
|
943
|
+
o rótulo da lista é longo (a task mostra o título na lista, só a chave no gatilho). Pra um
|
|
944
|
+
select de formulário (moldura/label), siga com `Select`/`Combobox` — este é calibrado pra
|
|
945
|
+
barra: discreto, sem borda, some no fundo até o hover.
|
|
946
|
+
- **`<Chat composerActions>` — o chat também hospeda a barra.** A 2.36.0 pôs o slot
|
|
947
|
+
`actions` no `<Composer>`, mas deixou o `<Chat>` "inalterado por fora": quem tinha um chat
|
|
948
|
+
(não um composer nu) não alcançava a barra — o comentário mandava "usar o `<Composer>`
|
|
949
|
+
direto", o que num chat significaria reimplementar o transcript. Agora o `<Chat>` repassa
|
|
950
|
+
`composerActions` pro Composer interno. É o que faltava pro rail do Copilot da GB tirar o
|
|
951
|
+
seletor de agente do slot `notice` (acima, deslocado) e pô-lo na barra, onde o Maestro já
|
|
952
|
+
põe app + task. `notice` volta a ser só aviso; `composerActions`, só controle.
|
|
953
|
+
|
|
954
|
+
## 2.36.0 — 2026-07-21
|
|
955
|
+
|
|
956
|
+
Novo `<Composer>` — a caixa de escrever extraída do `<Chat>`, reusável sozinha.
|
|
957
|
+
|
|
958
|
+
- **`<Composer>` (novo primitivo).** O composer do `<Chat>` (textarea numa pílula elevada,
|
|
959
|
+
Enter envia / Shift+Enter quebra linha, enviar dentro) virou componente próprio, pra usar
|
|
960
|
+
ONDE HÁ ENTRADA DE TEXTO MAS NÃO UM CHAT — o caso do composer de criação de sessão do
|
|
961
|
+
Maestro (sem histórico). Controlado (`value`/`onChange`/`onSubmit`); `submitDisabled` trava
|
|
962
|
+
o enviar além de vazio/busy (ex.: falta escolher o app); `busy` vira spinner no enviar.
|
|
963
|
+
- **Slot `actions` — seletores discretos na barra.** Com `actions`, o composer vira duas
|
|
964
|
+
linhas: textarea em cima, uma barra limpa embaixo com os controles à esquerda e o enviar à
|
|
965
|
+
direita. É onde o Maestro prende app + task e a GB, o agente — o padrão que os apps
|
|
966
|
+
resolviam cada um do seu jeito (GB no slot `notice` acima; Maestro à mão) agora tem lugar.
|
|
967
|
+
- **`<Chat>` inalterado por fora.** Ele passa a usar o `<Composer>` por dentro; sem `actions`,
|
|
968
|
+
o composer é byte-a-byte o de antes. Única mudança visível: o enviar mostra spinner enquanto
|
|
969
|
+
`busy` (era só desabilitado). Nenhuma migração.
|
|
970
|
+
|
|
971
|
+
## 2.35.1 — 2026-07-21
|
|
972
|
+
|
|
973
|
+
Correções da 2.35.0 (que saiu antes de passar pelo review). **Prefira esta à 2.35.0.**
|
|
974
|
+
|
|
975
|
+
- **`AppShell` recolhido não estreitava se o app passasse `sidebarClassName` com largura.** O
|
|
976
|
+
`sidebarClassName` (ex.: `w-72`) vencia a largura do recolhido no `twMerge`, então recolher
|
|
977
|
+
escondia o rótulo por CSS mas o aside ficava largo — meio-recolhido, sem erro. Agora a
|
|
978
|
+
largura do recolhido é aplicada por último: `sidebarClassName` manda no expandido, o estado
|
|
979
|
+
manda no recolhido (quem quer outra largura recolhida usa `sidebarCollapsedClassName`).
|
|
980
|
+
- **Doc e registro do rem, alinhados ao CSS.** O `tokens.md` ainda dizia "14px / ~12,5%"
|
|
981
|
+
(contradizia o `theme.css`, que já publicava 15px), o comentário do `theme.css` contava uma
|
|
982
|
+
história falsa (dizia "revert" — 15px é valor inédito, antes da 2.31.5 valia o 16px do
|
|
983
|
+
browser) e a entrada da 2.31.5 tinha dois números derivados de uma baseline que nunca
|
|
984
|
+
existiu. Tudo conferido no git e corrigido. Sem efeito em runtime — o rem segue 15px.
|
|
985
|
+
|
|
986
|
+
## 2.35.0 — 2026-07-21
|
|
987
|
+
|
|
988
|
+
Sidebar recolhível no `AppShell` e o **rem base passa a 15px**. *(Publicada com os defeitos
|
|
989
|
+
que a 2.35.1 conserta — use a 2.35.1.)*
|
|
990
|
+
|
|
991
|
+
- **Base do rem em 15px. Visual: GLOBAL — e o delta depende de onde você está.** Todo `rem`
|
|
992
|
+
deriva daqui, então tipografia, espaçamento e tamanho de controle mudam em toda tela no
|
|
993
|
+
bump. Confira sua origem:
|
|
994
|
+
- **vindo de 2.31.5–2.34.2** (base 14px): tudo **cresce ~7,1%** (0.875rem: 12.25px → 13.125px);
|
|
995
|
+
- **vindo de ≤ 2.31.4** (o pacote não fixava rem; valia o 16px do browser): tudo
|
|
996
|
+
**encolhe ~6,25%** — direção oposta, não pule este bullet.
|
|
997
|
+
|
|
998
|
+
Isto **não é um revert** da 2.31.5: 15px é valor inédito (antes dela não havia regra de
|
|
999
|
+
rem alguma). O número veio da prática — a Empresa X escolheu 15px por conta própria em
|
|
1000
|
+
**dois** apps, e a exceção reincidente virou o default (régua da casa: promove na
|
|
1001
|
+
reincidência). Quem quiser a escala densa fixa `html { font-size: 14px }` no próprio entry.
|
|
1002
|
+
**Consumidor que já sobrescrevia pra 15px pode remover o override** — vira redundante, não
|
|
1003
|
+
conflitante (mesmo valor), então dá pra limpar sem pressa.
|
|
1004
|
+
<br>*Correção de registro:* a entrada da 2.31.5 saiu com dois erros (chamava 15px de "a
|
|
1005
|
+
escala anterior" e dizia "encolhe ~6,7%"), ambos por supor uma baseline de 15px que nunca
|
|
1006
|
+
existiu. Corrigidos lá, com nota.
|
|
1007
|
+
- **`AppShell` ganha `collapsible` (opt-in).** Sem a prop, nada muda. Com ela, o shell
|
|
1008
|
+
controla a **largura** e publica o estado; **o que some é decisão do conteúdo**, porque o
|
|
1009
|
+
`sidebarNav` é do consumidor. Em vez de exigir estado no app, o aside expõe
|
|
1010
|
+
`data-collapsed` e o grupo `sidebar`: o rótulo some por CSS —
|
|
1011
|
+
`className="group-data-[collapsed=true]/sidebar:hidden"`. O botão é o novo
|
|
1012
|
+
**`<AppShellTrigger />`**, posicionado por você (some sozinho quando o shell não é
|
|
1013
|
+
`collapsible`, então dispensa condicional); **`useAppShell()`** dá o estado em JS. Pra
|
|
1014
|
+
persistir a preferência, controle de fora com `collapsed` + `onCollapsedChange`;
|
|
1015
|
+
`sidebarCollapsedClassName` ajusta a largura recolhida (default `w-14`).
|
|
1016
|
+
|
|
1017
|
+
## 2.34.2 — 2026-07-21
|
|
1018
|
+
|
|
1019
|
+
O `opus setup` passa a conhecer o Tailwind v4. **Sem breaking** (o caminho v3 é o mesmo).
|
|
1020
|
+
|
|
1021
|
+
- **Não nasce mais `tailwind.config.js` fantasma.** O setup criava o config sempre que o
|
|
1022
|
+
app tinha `index.html` e nenhum config — sem nunca olhar a versão do Tailwind. Na v4 não
|
|
1023
|
+
existe config (tema e escaneamento moram no CSS: `@import`, `@theme`, `@source`), então o
|
|
1024
|
+
arquivo nascia órfão: a build ignora, mas ele parece oficial pra quem abrir. Agora o
|
|
1025
|
+
setup lê a geração do consumidor e só semeia o config até a v3.
|
|
1026
|
+
- **E o config que já nasceu agora é avisado.** Quem tomou o arquivo fantasma nas versões
|
|
1027
|
+
anteriores ouve, numa build v4, que ele é inerte e pode ser apagado. O setup não apaga
|
|
1028
|
+
nada — arquivo do projeto é do projeto —, mas parar de criar sem apontar o que já criou
|
|
1029
|
+
deixaria o problema no disco em silêncio. Config plugado de propósito com `@config` não
|
|
1030
|
+
é avisado, e a busca por esse `@config` varre **todos** os CSS do app, não só o de
|
|
1031
|
+
entrada: ele pode morar num parcial, e errar aí mandaria apagar um arquivo em uso.
|
|
1032
|
+
- **Some o aviso falso do tema.** A checagem do `@softize/opus/ui/theme.css` olhava só o
|
|
1033
|
+
entry `.tsx`. App v4 importa o tema pelo CSS, então quem já fazia certo ouvia "importe o
|
|
1034
|
+
tema" a cada install — aviso que ensina a duplicar o que já está feito. Na v4 a
|
|
1035
|
+
checagem passa a olhar o CSS de entrada, e o CSS é **encontrado varrendo `src/`**, não
|
|
1036
|
+
casando uma lista de nomes: `src/styles/app.css` vale tanto quanto `src/index.css`, e
|
|
1037
|
+
entre vários vence o que importa o tema (num app com reset + entry, é esse que responde).
|
|
1038
|
+
- **Ganha um aviso que faltava:** app v4 sem `@source` apontando
|
|
1039
|
+
`node_modules/@softize/opus/src/ui` renderiza cru, porque o Tailwind v4 não escaneia
|
|
1040
|
+
`node_modules`. Era o outro lado do `content` glob da v3, e ninguém avisava.
|
|
1041
|
+
- **Detecção pela versão INSTALADA**, com o range declarado só como palpite de reserva. A
|
|
1042
|
+
pergunta real é "que Tailwind essa build roda?", e o range não responde: `catalog:` (o
|
|
1043
|
+
protocolo de catálogo do pnpm), `workspace:*`, `latest` e `*` não têm dígito nenhum, e
|
|
1044
|
+
`>=3` pode ter resolvido 4.x. Ler o range primeiro erraria bem no formato de monorepo
|
|
1045
|
+
pnpm que originou este achado. A busca **sobe a árvore** (`node_modules` da raiz, para o
|
|
1046
|
+
monorepo com hoisting), e `peerDependencies`/`optionalDependencies` entram na conta do
|
|
1047
|
+
fallback. Só quando não há Tailwind instalado **e** o range não tem número nenhum
|
|
1048
|
+
(`catalog:`, `workspace:*`, `latest`, `*`) o setup mantém o caminho v3 — aí seria palpite.
|
|
1049
|
+
- Achado ao dar `postinstall: opus setup` aos apps de um monorepo pnpm: lá o `INIT_CWD` é
|
|
1050
|
+
a RAIZ, que não tem a dep, então o setup nunca rodava pros apps — e o `opus.json` de um
|
|
1051
|
+
deles envelhecia desde 2.28.0. Com o setup rodando de verdade, o defeito apareceu.
|
|
1052
|
+
- 41 testes novos em `tests/init` — a fundação de UI não tinha nenhum.
|
|
1053
|
+
|
|
1054
|
+
## 2.34.1 — 2026-07-21
|
|
1055
|
+
|
|
1056
|
+
Conserta a regressão que a 2.34.0 introduziu no `DocBrowser`. **Suba da 2.33.x direto
|
|
1057
|
+
para cá.**
|
|
1058
|
+
|
|
1059
|
+
- **`navigate` volta a notificar por evento.** Ele avisava só uma lista de assinantes em
|
|
1060
|
+
escopo de módulo, então quem escuta `popstate` na unha — a forma de todas as cópias que
|
|
1061
|
+
a primitiva substituiu — deixou de ser avisado. Agora dispara
|
|
1062
|
+
`new PopStateEvent('popstate')` no `window`. Como o `window` é um só, isso também faz
|
|
1063
|
+
duas cópias do pacote no `node_modules` se enxergarem; a lista privada não fazia.
|
|
1064
|
+
- **Quem estava quebrado:** app que renderiza `<DocBrowser basePath="…" />` **sem** a prop
|
|
1065
|
+
`path` e guarda o path por conta própria. Clicar numa página trocava a URL e o corpo,
|
|
1066
|
+
mas o realce do menu do host congelava — sem erro, sem aviso.
|
|
1067
|
+
- **O no-op do `navigate` passa a normalizar o destino** (`new URL(...).href` dos dois
|
|
1068
|
+
lados) em vez de comparar strings cruas. Isso conserta dois furos de uma vez: o **hash**
|
|
1069
|
+
(estando em `/a#secao`, `navigate('/a')` virava no-op e a âncora nunca saía) e o
|
|
1070
|
+
**encoding** — `window.location` devolve `/relatórios` percent-encodado, então o no-op
|
|
1071
|
+
nunca disparava para rota acentuada ou query com espaço, e cada clique repetido
|
|
1072
|
+
empilhava uma entrada. Era o caso pt-BR inteiro.
|
|
1073
|
+
- Some a última cópia à mão do mecanismo, que tinha sobrado dentro do próprio
|
|
1074
|
+
`registry.tsx` (o redirect de `/docs/ui` pra `/ui` — agora `navigate('/ui', { replace:
|
|
1075
|
+
true })`, porque redirect não merece entrada no histórico).
|
|
1076
|
+
|
|
1077
|
+
## 2.34.0 — 2026-07-21
|
|
1078
|
+
|
|
1079
|
+
> ⚠️ **Regressão — use a 2.34.1.** A frase "nada existente muda de comportamento" logo
|
|
1080
|
+
> abaixo é FALSA: o `DocBrowser` standalone parou de notificar o host. O diagnóstico e o
|
|
1081
|
+
> conserto estão na 2.34.1. A entrada fica porque a versão foi publicada.
|
|
1082
|
+
|
|
1083
|
+
Roteamento vira primitiva da casa. **Sem breaking** (módulo novo; nada existente muda de
|
|
1084
|
+
comportamento).
|
|
1085
|
+
|
|
1086
|
+
- **`navigate` / `usePathname` / `useSegments` / `useSearchParams`** (`ui/react`):
|
|
1087
|
+
history-based, sem dependência — o pathname É o estado, então deep-link, reload e o
|
|
1088
|
+
botão voltar funcionam sem um segundo lugar guardando "onde estou".
|
|
1089
|
+
- Nasceu por **reincidência**, não por gosto: o mesmo mecanismo estava reimplementado à
|
|
1090
|
+
mão em quatro lugares, TRÊS deles dentro da casa (o `DocBrowser` daqui, o site do Opus
|
|
1091
|
+
e o back-office da Empresa X). O `DocBrowser` e o site já consomem a primitiva — a
|
|
1092
|
+
doc é o primeiro consumidor, como no `SectionShell`.
|
|
1093
|
+
- As cópias divergiam no que importa: só uma usava `useSyncExternalStore` (as outras leem
|
|
1094
|
+
`window.location` em `useState`, que sofre **tearing** no modo concurrent), e o no-op de
|
|
1095
|
+
destino repetido — sem ele, clicar duas vezes no mesmo item empilha entradas idênticas e
|
|
1096
|
+
o "voltar" não sai do lugar — não estava em todas.
|
|
1097
|
+
- **Escopo pequeno de propósito**: não há tabela de rotas, `<Route>`, params tipados nem
|
|
1098
|
+
data loader. A doc traz a régua de quando isto NÃO serve (casamento de padrão, params,
|
|
1099
|
+
carregamento por rota → o caso pede uma biblioteca de rotas).
|
|
1100
|
+
- Compatibilidade: `subscribe` escuta `popstate`, então quem ainda notifica por evento
|
|
1101
|
+
sintético (`dispatchEvent(new PopStateEvent('popstate'))`) segue funcionando. ⚠️ A mão
|
|
1102
|
+
inversa é que faltava — ver 2.34.1.
|
|
1103
|
+
- SSR: snapshot de servidor constante (`/`), reconciliado no 1º render do cliente.
|
|
1104
|
+
|
|
1105
|
+
## 2.33.0 — 2026-07-20
|
|
1106
|
+
|
|
1107
|
+
Conserta a regressão da 2.32.0 na nav da doc — **não use a 2.32.0** se você serve doc
|
|
1108
|
+
por pasta. **Sem breaking** (as props novas são aditivas).
|
|
1109
|
+
|
|
1110
|
+
### O que quebrou na 2.32.0
|
|
1111
|
+
|
|
1112
|
+
Ao passar o `DocBrowser` pro `<SectionShell>`, o mapeamento achatou o tier
|
|
1113
|
+
`DocGroup.label`. A justificativa registrada era falsa: o tier parecia morto porque o
|
|
1114
|
+
catálogo hardcoded (`DOC_SECTIONS`) nunca o preenche — mas `docSectionsFromFolder` o
|
|
1115
|
+
preenche a partir de sub-pasta (ou do frontmatter `group:`), que é o caminho do
|
|
1116
|
+
`opusDocs({ source })`. **Sintoma:** projeto que serve `docs/` com sub-pasta perdia os
|
|
1117
|
+
sub-cabeçalhos, e as páginas de subgrupos distintos viravam uma lista indistinguível.
|
|
1118
|
+
A entrada da 2.32.0 diz "saída visual idêntica" — vale só pro catálogo do próprio Opus.
|
|
1119
|
+
|
|
1120
|
+
- **`SectionNavGroup.subgroups`** (novo): o 3º nível — rótulo mais fraco e mais
|
|
1121
|
+
indentado, renderizado DEPOIS dos `items` soltos do grupo. `items` virou opcional
|
|
1122
|
+
(quem já passava segue igual). O `DocBrowser` mapeia seção → grupo → subgrupo e a
|
|
1123
|
+
nav de 3 níveis volta ao que era.
|
|
1124
|
+
- **`navLabel`** (novo, default `Navegação da seção`): rotula a landmark `<nav>`. Sem
|
|
1125
|
+
isso, um app com nav na sidebar + nav de seção anuncia "navigation" duas vezes e o
|
|
1126
|
+
leitor de tela não distingue.
|
|
1127
|
+
- **`scrollResetKey` — a doc estava invertida.** Dizia pra passar "algo mais fino"
|
|
1128
|
+
quando a tela pagina no lugar, e o exemplo (`scrollResetKey={id}` com `id` = o
|
|
1129
|
+
`activeId`) era um no-op: paginar não muda o `activeId`, então o default já resolvia.
|
|
1130
|
+
Os dois usos reais estão documentados agora — chave CONSTANTE pra nunca remontar,
|
|
1131
|
+
chave MAIS FINA pra remontar dentro da mesma tela.
|
|
1132
|
+
- Rótulo vazio (`''`) passa a contar como ausente no grupo e no subgrupo (antes saía um
|
|
1133
|
+
cabeçalho com padding e sem texto); a normalização saiu dos consumidores.
|
|
1134
|
+
- `id` de item agora está documentado como único em TODA a nav — repetido, marca dois
|
|
1135
|
+
itens com `aria-current` e o painel não remonta ao alternar.
|
|
1136
|
+
- Testes: regressão do tier de grupo no nível do `DocBrowser` (inclusive pelo caminho
|
|
1137
|
+
real do `docSectionsFromFolder`), os dois sentidos do `scrollResetKey`, subgrupos,
|
|
1138
|
+
rótulo vazio, `icon`/`badge`/`navHeader`/`navFooter` e a landmark rotulada.
|
|
1139
|
+
|
|
1140
|
+
## 2.32.0 — 2026-07-20
|
|
1141
|
+
|
|
1142
|
+
`<SectionShell>` — o nível que faltava entre o `AppShell` e a `Page`. **Sem breaking**
|
|
1143
|
+
(componente novo; nada existente muda de comportamento).
|
|
1144
|
+
|
|
1145
|
+
- **`<SectionShell>` (ui)**: uma SEÇÃO com navegação própria — nav `w-56` com filete +
|
|
1146
|
+
painel — pras telas irmãs de Configurações, Relatórios ou doc. Controlado
|
|
1147
|
+
(`activeId` + `onSelect`): o roteamento é do app, mesma regra do `AppShell`. Trocar de
|
|
1148
|
+
item REMONTA o painel (é o que zera o scroll); `scrollResetKey` fixa a chave quando a
|
|
1149
|
+
mesma tela pagina no lugar. Itens aceitam `icon`, `badge` e `disabled`; o ativo ganha
|
|
1150
|
+
`aria-current="page"`.
|
|
1151
|
+
- Nasceu por **reincidência**, não por gosto: o mesmo casco `nav + painel` já estava
|
|
1152
|
+
copiado à mão no `DocBrowser`, no site do Opus e no back-office da Empresa X — três
|
|
1153
|
+
vezes as mesmas classes. O `DocBrowser` agora **consome** o componente (a doc é o
|
|
1154
|
+
primeiro consumidor; saída visual idêntica — ⚠️ **falso**: regrediu a nav de quem
|
|
1155
|
+
serve doc por pasta, ver 2.33.0).
|
|
1156
|
+
- A doc do pattern traz a régua que faltava: **quando é sidebar do app, quando é seção,
|
|
1157
|
+
quando é tab**. Sintoma de seção que devia sair da sidebar: o item de topo não tem
|
|
1158
|
+
tela própria e só existe pra abrir um accordion.
|
|
1159
|
+
|
|
1160
|
+
## 2.31.5 — 2026-07-17
|
|
1161
|
+
|
|
1162
|
+
**Base do rem em 14px** — a escala inteira densifica. **Visual: GLOBAL.** Todo `rem`
|
|
1163
|
+
encolhe ~12,5% (0.875rem = 12.25px, e assim por diante), então tipografia, espaçamento e
|
|
1164
|
+
tamanho de controle mudam em toda tela de todo consumidor no bump. Antes desta versão o
|
|
1165
|
+
pacote não fixava rem: valia o 16px do browser. Projeto que quiser a escala larga
|
|
1166
|
+
sobrescreve `html { font-size: 15px }` no próprio entry (é o que o back-office do Grand
|
|
1167
|
+
Brasil faz).
|
|
1168
|
+
|
|
1169
|
+
> **Corrigido em 2.35.0.** Esta entrada é do lote reconstruído do git e saiu com dois erros,
|
|
1170
|
+
> ambos derivados de supor uma baseline de 15px que nunca existiu: dizia "encolhe ~6,7%"
|
|
1171
|
+
> (é 14/15; contra o 16px real são ~12,5% — o comentário no código sempre disse 12,5%) e
|
|
1172
|
+
> chamava 15px de "a escala anterior" (a anterior era 16px; 15px é escolha da Empresa X).
|
|
1173
|
+
|
|
1174
|
+
## 2.31.4 — 2026-07-17
|
|
1175
|
+
|
|
1176
|
+
Blur do backdrop volta pro degrau `xs`: a escala do Tailwind v4 renomeou os degraus (o
|
|
1177
|
+
antigo `sm` virou 8px), e o backdrop tinha herdado o valor errado no rename.
|
|
1178
|
+
|
|
1179
|
+
## 2.31.3 — 2026-07-17
|
|
1180
|
+
|
|
1181
|
+
Backdrop de modal mais claro, com blur; header do `Dialog` com o mesmo padding do corpo.
|
|
1182
|
+
|
|
1183
|
+
## 2.31.2 — 2026-07-17
|
|
1184
|
+
|
|
1185
|
+
Canvas quase-branco (tinte de 25% do muted) e bordas mais leves (92.5%) — a divergência
|
|
1186
|
+
nº 5 da casa em relação ao stock do shadcn.
|
|
1187
|
+
|
|
1188
|
+
## 2.31.1 — 2026-07-17
|
|
1189
|
+
|
|
1190
|
+
`Dialog` se aproxima do `Card`: mesma forma (radius + 6px) e sombra pela metade.
|
|
1191
|
+
|
|
1192
|
+
## 2.31.0 — 2026-07-17
|
|
1193
|
+
|
|
1194
|
+
**Forma por PAPEL**: `rounded-card` / `rounded-popover` / `rounded-dialog`, na mesma
|
|
1195
|
+
lógica dos tokens de elevação da 2.30.0 — papel novo ganha token novo. Entra junto a doc
|
|
1196
|
+
do sistema de elevação.
|
|
1197
|
+
|
|
1198
|
+
## 2.30.2 — 2026-07-17
|
|
1199
|
+
|
|
1200
|
+
`shadow-card` menor na elevação de repouso (1px/3px).
|
|
1201
|
+
|
|
1202
|
+
## 2.30.1 — 2026-07-17
|
|
1203
|
+
|
|
1204
|
+
Idem 2.30.2 — degrau intermediário do mesmo ajuste.
|
|
1205
|
+
|
|
1206
|
+
## 2.30.0 — 2026-07-17
|
|
1207
|
+
|
|
1208
|
+
Elevação POR PAPEL + aresta — e os defaults do Tailwind de volta. **Visual: superfície
|
|
1209
|
+
elevada troca a borda pelo anel; `shadow-*` cru volta a ser o stock do TW.**
|
|
1210
|
+
|
|
1211
|
+
- Tokens em namespace PRÓPRIO (sem pegadinha): `shadow-card` / `shadow-popover` /
|
|
1212
|
+
`shadow-dialog` (família Notion — curta+longa, com dark de verdade via vars) e
|
|
1213
|
+
`ring-edge`/`border-edge` (`--color-edge`: o anel hairline que SUBSTITUI a borda em
|
|
1214
|
+
superfície elevada — não combine com `border`). Papel novo = token novo (ex.: um
|
|
1215
|
+
futuro `shadow-input` nasce com demanda).
|
|
1216
|
+
- A sobrescrita da escala `--shadow-*` (antiga divergência nº 4) foi APOSENTADA:
|
|
1217
|
+
`shadow-md` volta a significar exatamente o que a doc do Tailwind diz.
|
|
1218
|
+
- Migrados: popover, select, dropdown-menu, context-menu, menubar, navigation-menu
|
|
1219
|
+
(`ring-edge` + `shadow-popover`); dialog, alert-dialog, sheet (`shadow-dialog`);
|
|
1220
|
+
`Card` (rounded-2xl + `ring-edge` + `shadow-card` — o flat aposentado); composer e
|
|
1221
|
+
card de fala do `<Chat>` (`shadow-card`).
|
|
1222
|
+
|
|
1223
|
+
## 2.29.5 — 2026-07-17
|
|
1224
|
+
|
|
1225
|
+
Degraus baixos da sombra com ALCANCE menor (geometria: xs 1px/4px, sm 3px/8px, base
|
|
1226
|
+
5px/12px) — opacidade aprovada fica. **Sem breaking**; flutuantes intactos.
|
|
1227
|
+
|
|
1228
|
+
## 2.29.4 — 2026-07-17
|
|
1229
|
+
|
|
1230
|
+
Degraus baixos da sombra MAIS CLAROS (correção de rumo: xs 2.5%/3.5%, abaixo até do
|
|
1231
|
+
original 3%/4%). **Sem breaking**; flutuantes intactos.
|
|
1232
|
+
|
|
1233
|
+
## 2.29.3 — 2026-07-17
|
|
1234
|
+
|
|
1235
|
+
Degraus baixos da sombra: mais um ponto de presença (xs 5%/7%, sm 5.5%/8%). **Sem
|
|
1236
|
+
breaking** (só visual; flutuantes seguem intactos).
|
|
1237
|
+
|
|
1238
|
+
## 2.29.2 — 2026-07-17
|
|
1239
|
+
|
|
1240
|
+
Degraus BAIXOS da sombra mais presentes. **Sem breaking** (só visual).
|
|
1241
|
+
|
|
1242
|
+
- `2xs`/`xs`/`sm`/`shadow` sobem de opacidade (xs 3.5%/5.5%, sm 4%/6%…) — cards e
|
|
1243
|
+
superfícies apoiadas ficavam invisíveis sobre o canvas. `md`/`lg`/`xl`/`2xl`
|
|
1244
|
+
(flutuantes) NÃO mudam — calibração aprovada; progressão da escala preservada.
|
|
1245
|
+
|
|
1246
|
+
## 2.29.1 — 2026-07-17
|
|
1247
|
+
|
|
1248
|
+
O flush completa a promessa do shell transparente. **Visual: consumidores flush que
|
|
1249
|
+
querem miolo branco pintam `bg-background` no próprio contêiner.**
|
|
1250
|
+
|
|
1251
|
+
- `<AppShell flush>`: o main deixa de pintar `bg-background` — o shell NÃO pinta nada
|
|
1252
|
+
(canvas do body em tudo; o filete `border-l` fica). Superfície é decisão do conteúdo.
|
|
1253
|
+
|
|
1254
|
+
## 2.29.0 — 2026-07-17
|
|
1255
|
+
|
|
1256
|
+
Conversa que começa pelo assistente. **Sem breaking.**
|
|
1257
|
+
|
|
1258
|
+
- `<Chat kickoff>`: no modo autogerenciado, roda uma vez no mount quando o transcript
|
|
1259
|
+
nasce vazio — o agente abre a conversa (proatividade: relatório recém-criado, sessão
|
|
1260
|
+
nova). Mesmo contrato do `send` (string ou stream de ChatEvent), sem mensagem de
|
|
1261
|
+
usuário; com `initialMessages` (histórico) não dispara. 2 testes.
|
|
1262
|
+
|
|
1263
|
+
## 2.28.0 — 2026-07-17
|
|
1264
|
+
|
|
1265
|
+
O canvas é do BODY; o shell fica transparente. **Sem breaking** (mesmo tom).
|
|
1266
|
+
|
|
1267
|
+
- `theme.css`: o body ganha o cinza clarinho da casa (`color-mix` de muted 40% sobre
|
|
1268
|
+
background — o tom que o AppShell pintava); overscroll/áreas fora do shell param de
|
|
1269
|
+
aparecer brancas, e o canvas segue o tema (light/dark).
|
|
1270
|
+
- `<AppShell>`: perde o `bg-muted/40` da raiz (transparente) — as superfícies pintam
|
|
1271
|
+
`bg-background` por cima, como já faziam.
|
|
1272
|
+
|
|
1273
|
+
## 2.27.2 — 2026-07-17
|
|
1274
|
+
|
|
1275
|
+
Escala de sombra COMPLETA da casa. **Sem breaking** (só visual).
|
|
1276
|
+
|
|
1277
|
+
- Todos os degraus (`2xs`→`2xl`) redefinidos no theme.css com a curva leve de duas
|
|
1278
|
+
camadas (estilo Notion) — antes só `md`/`lg`; agora QUALQUER `shadow-*` em qualquer
|
|
1279
|
+
consumidor sai sem o peso default do Tailwind, preservando o sentido da progressão.
|
|
1280
|
+
|
|
1281
|
+
## 2.27.1 — 2026-07-17
|
|
1282
|
+
|
|
1283
|
+
Reset de borda no tema — bordas pretas em consumidor novo. **Sem breaking.**
|
|
1284
|
+
|
|
1285
|
+
- `theme.css` ganha o base layer canônico `* { border-color: var(--border) }` (o
|
|
1286
|
+
globals.css do shadcn): sem ele, todo consumidor NOVO do opus/ui nascia com bordas
|
|
1287
|
+
`currentColor` (pretas) até copiar o reset à mão no entry CSS. App com a cópia local
|
|
1288
|
+
segue funcionando (redundante, inofensivo — pode remover).
|
|
1289
|
+
|
|
1290
|
+
## 2.27.0 — 2026-07-17
|
|
1291
|
+
|
|
1292
|
+
Chrome flush no `<AppShell>` (padrão Vetra BI). **Sem breaking** (default inalterado).
|
|
1293
|
+
|
|
1294
|
+
- `flush` (prop): sidebar sem o padding do shell e conteúdo full-bleed — encosta no
|
|
1295
|
+
browser, com filete à esquerda (`border-l`) sobre `bg-background`. O chrome clássico
|
|
1296
|
+
(miolo em card sobre o muted) segue sendo o default.
|
|
1297
|
+
- `<AppShellBar>`: a faixa h-12 com `border-b` — uma na sidebar (marca) e outra no topo
|
|
1298
|
+
do conteúdo (breadcrumb/ações); as alturas casam e a linha do header atravessa a tela
|
|
1299
|
+
inteira. Doc com a receita completa + 3 testes.
|
|
1300
|
+
|
|
1301
|
+
## 2.26.2 — 2026-07-17
|
|
1302
|
+
|
|
1303
|
+
Sombra dos flutuantes: calibração fina pra baixo (md 3%/7.5%, lg 4%/9.5%). **Sem
|
|
1304
|
+
breaking** (só visual).
|
|
1305
|
+
|
|
1306
|
+
## 2.26.1 — 2026-07-17
|
|
1307
|
+
|
|
1308
|
+
Tabela larga rola nela mesma. **Sem breaking.**
|
|
1309
|
+
|
|
1310
|
+
- `<Markdown>`: a `<table>` ganha invólucro `overflow-x-auto` — tabela mais larga que o
|
|
1311
|
+
bloco (ex.: ranking no `<Chat>`) rola horizontalmente NELA, sem arrastar o scroll do
|
|
1312
|
+
chat/doc inteiro.
|
|
1313
|
+
|
|
1314
|
+
## 2.26.0 — 2026-07-17
|
|
1315
|
+
|
|
1316
|
+
Tabela GFM no `<Markdown>` (e, por tabela, no `<Chat>`). **Sem breaking.**
|
|
1317
|
+
|
|
1318
|
+
- O renderer nativo (zero-dep) ganha tabela GFM: linha de células `| a | b |` +
|
|
1319
|
+
separadora `|---|` viram `<table>` semântica (thead/tbody), células com inline
|
|
1320
|
+
(bold/code/link) — resposta de agente com ranking/tabela renderiza no chat. Linha
|
|
1321
|
+
com `|` sem separadora segue parágrafo.
|
|
1322
|
+
|
|
1323
|
+
## 2.25.2 — 2026-07-17
|
|
1324
|
+
|
|
1325
|
+
Calibrações de UI (3ª rodada no olho do dono). **Sem breaking** (só visual).
|
|
1326
|
+
|
|
1327
|
+
- Sombras `md`/`lg` um pouco mais opacas (md 3.5%/8.5%, lg 4.5%/10.5%) — meio-termo
|
|
1328
|
+
entre a 2.24.3 (pesada) e a 2.25.1 (leve demais).
|
|
1329
|
+
- `DropdownMenuContent`/`ContextMenuContent` com `min-w` 10rem (era 8rem).
|
|
1330
|
+
|
|
1331
|
+
## 2.25.1 — 2026-07-17
|
|
1332
|
+
|
|
1333
|
+
Sombras dos flutuantes ainda mais leves (2ª calibração no olho do dono). **Sem
|
|
1334
|
+
breaking** (só visual).
|
|
1335
|
+
|
|
1336
|
+
- `md` 3%/7% · `lg` 4%/9% — mesma expansão, menos peso.
|
|
1337
|
+
|
|
1338
|
+
## 2.25.0 — 2026-07-17
|
|
1339
|
+
|
|
1340
|
+
Form contract-driven ganha o campo de ícone. **Sem breaking.**
|
|
1341
|
+
|
|
1342
|
+
- `widget: 'icon'` no vocabulário de `fields`: string com o nome kebab-case da paleta
|
|
1343
|
+
da casa — o `ActionForm`/`ActionFormDialog` renderiza o `<IconPicker>` (2.24.0) no
|
|
1344
|
+
campo. Fecha o ciclo: o seletor de ícone entra no form declarado no contrato, sem
|
|
1345
|
+
form à mão.
|
|
1346
|
+
|
|
1347
|
+
## 2.24.3 — 2026-07-17
|
|
1348
|
+
|
|
1349
|
+
Sombras dos flutuantes mais leves. **Sem breaking** (só visual).
|
|
1350
|
+
|
|
1351
|
+
- `md`/`lg` com opacidade reduzida (camada curta 4–5%, longa 10–12%) — a 2.24.1 tinha
|
|
1352
|
+
ficado mais pesada que a referência (Notion); a expansão fica, o peso sai.
|
|
1353
|
+
|
|
1354
|
+
## 2.24.2 — 2026-07-17
|
|
1355
|
+
|
|
1356
|
+
Destructive dos menus: hover muda **só a cor**. **Sem breaking** (só visual).
|
|
1357
|
+
|
|
1358
|
+
- `DropdownMenuItem`/`ContextMenuItem` `variant="destructive"`: no hover/focus o fundo
|
|
1359
|
+
segue o accent NORMAL dos itens — só texto/ícone pintam de destructive (refinamento
|
|
1360
|
+
do 2.24.1, que tingia o fundo de vermelho).
|
|
1361
|
+
|
|
1362
|
+
## 2.24.1 — 2026-07-17
|
|
1363
|
+
|
|
1364
|
+
Elevação estilo Notion + destructive discreto. **Sem breaking** (só visual).
|
|
1365
|
+
|
|
1366
|
+
- Sombras `md`/`lg` viram duas camadas difusas e expandidas (curta pra descolar +
|
|
1367
|
+
longa pra flutuar) nos FLUTUANTES (menus, popovers, selects, dialogs) — divergência
|
|
1368
|
+
declarada nº 4 no `theme.css`. `xs`/`sm` ficam (superfícies apoiadas).
|
|
1369
|
+
- `DropdownMenuItem`/`ContextMenuItem` `variant="destructive"`: NEUTRO em repouso —
|
|
1370
|
+
vermelho (texto/ícone/fundo) só no hover/focus, como o "Move to Trash" do Notion.
|
|
1371
|
+
`dropdown-menu` e `context-menu` viram **ejected** no lock (delta declarado).
|
|
1372
|
+
|
|
1373
|
+
## 2.24.0 — 2026-07-17
|
|
1374
|
+
|
|
1375
|
+
Seletor de ícone da casa. **Sem breaking.**
|
|
1376
|
+
|
|
1377
|
+
- `<IconPicker>` (ui): gatilho com o ícone corrente + lista buscável (Popover+Command,
|
|
1378
|
+
a receita do Combobox). O value é o **nome** do ícone (kebab-case) — renderize com a
|
|
1379
|
+
mesma paleta (`iconPickerIcons[name] ?? fallback`). Paleta default curada (~40,
|
|
1380
|
+
lucide); vocabulário próprio via prop `icons`; selecionar o corrente desmarca.
|
|
1381
|
+
Caso de uso: personalização de item criado pelo usuário (relatório, projeto, pasta).
|
|
1382
|
+
|
|
1383
|
+
## 2.23.0 — 2026-07-17
|
|
1384
|
+
|
|
1385
|
+
`<Chat>` em modo CONTROLADO. **Sem breaking** (o modo autogerenciado segue igual).
|
|
1386
|
+
|
|
1387
|
+
- `messages` (transcript por prop, `ChatTranscriptItem[]` com item `error`), `onSend`
|
|
1388
|
+
(retornar `false` devolve o texto ao composer), `busy` e `activity` (`undefined`
|
|
1389
|
+
esconde o indicador, `null` = "Pensando…", string = rótulo) — pro app dono do estado
|
|
1390
|
+
(transporte próprio, ex.: SSE server-autoritativo com replay, como o ChatRail do
|
|
1391
|
+
Maestro). `notice` renderiza avisos do app acima do composer.
|
|
1392
|
+
|
|
1393
|
+
## 2.22.1 – 2.22.4 — 2026-07-17
|
|
1394
|
+
|
|
1395
|
+
Absorção do design do ChatRail no `<Chat>` (patches; o design do Maestro virou o
|
|
1396
|
+
default do componente):
|
|
1397
|
+
|
|
1398
|
+
- **2.22.1** — `greeting` vira estado vazio centrado (✦, some quando a conversa
|
|
1399
|
+
começa); era falsa 1ª fala do assistente e entrava no transcript do `send`.
|
|
1400
|
+
- **2.22.2** — composer pílula flutuante: enviar dentro do campo, focus ring.
|
|
1401
|
+
- **2.22.3** — transcript em turnos: mensagem do usuário em card com Markdown,
|
|
1402
|
+
cabeçalho do turno sticky; balão preenchido aposentado.
|
|
1403
|
+
- **2.22.4** — linha do composer centralizada verticalmente (py-2 fecha os 36px do
|
|
1404
|
+
botão).
|
|
1405
|
+
|
|
1406
|
+
## 2.22.0 — 2026-07-17
|
|
1407
|
+
|
|
1408
|
+
Protocolo de eventos de conversa (streaming). **Sem breaking.**
|
|
1409
|
+
|
|
1410
|
+
- `ChatEvent` no core: `text{delta}` · `tool{name,detail?}` · `artifact{kind,ref,title?}`
|
|
1411
|
+
· `done{ok,error?}` — contrato em `docs/chat-event-protocol.md`.
|
|
1412
|
+
- `runStream` aditivo no driver anthropic (token a token com `client.messages.stream`;
|
|
1413
|
+
fallback `create` = delta único) e no `BoundAi` (mesmo execute-como-usuário/confirm).
|
|
1414
|
+
- `<Chat>` aceita os dois modos no `send`: `Promise<string>` (request/response) OU
|
|
1415
|
+
`AsyncIterable<ChatEvent>` (streaming) — tool vira indicador vivo humanizado
|
|
1416
|
+
(`humanizeTool`), artifact via `renderArtifact`, `initialMessages` reidrata (troque a
|
|
1417
|
+
`key` ao trocar de conversa).
|
|
1418
|
+
|
|
1419
|
+
## 2.21.0 — 2026-07-16
|
|
1420
|
+
|
|
1421
|
+
Chrome de aplicação. **Sem breaking.**
|
|
1422
|
+
|
|
1423
|
+
- `<AppShell>` (ui): sidebar (header/nav/footer) + conteúdo + rail opcional
|
|
1424
|
+
redimensionável (Resizable) — o esqueleto que admin, Maestro e Vetra BI copiavam à
|
|
1425
|
+
mão, agora slot-based (`sidebarHeader`/`sidebarNav`/`sidebarFooter`/`rail`).
|
|
1426
|
+
|
|
1427
|
+
## 2.20.1 — 2026-07-14
|
|
1428
|
+
|
|
1429
|
+
Controles flat, agora HONESTO: a sombra sai de DENTRO dos componentes, não por token
|
|
1430
|
+
mentiroso. **Sem breaking, mesmo visual da 2.20.0.**
|
|
1431
|
+
|
|
1432
|
+
- Reverte o `--shadow-xs: 0 0 #0000` da 2.20.0 — redefinir um token de escala pra "nada"
|
|
1433
|
+
mentia sobre o que a variável contém. O token volta ao valor real do shadcn.
|
|
1434
|
+
- `shadow-xs` removido de dentro dos 16 controles (button, input, select, textarea,
|
|
1435
|
+
checkbox, switch, radio-group, toggle, toggle-group, input-group, input-otp,
|
|
1436
|
+
native-select, calendar, menubar, button-group, combobox) — a intenção "flat" mora
|
|
1437
|
+
onde o componente é definido.
|
|
1438
|
+
- Os 14 que eram byte-fiéis ao shadcn viram **ejected** (assumidos pela casa). O lock
|
|
1439
|
+
guarda a proveniência (`upstreamHash` = merge-base) pro re-sync futuro por diff.
|
|
1440
|
+
|
|
1441
|
+
## 2.20.0 — 2026-07-14
|
|
1442
|
+
|
|
1443
|
+
Controles **flat**: a sombra sutil (`shadow-xs`) dos controles zerada por token do tema.
|
|
1444
|
+
**Sem breaking.**
|
|
1445
|
+
|
|
1446
|
+
- `--shadow-xs: 0 0 #0000` no tema — button, input, select (trigger), input-group,
|
|
1447
|
+
textarea, native-select e o toggle-group perdem a sombra, pareando com os Cards flat
|
|
1448
|
+
da casa e com o ToggleGroup "junto" (que já era flat). Divergência declarada vs shadcn.
|
|
1449
|
+
- Superfícies elevadas (dialog, popover, dropdown, card — `shadow-md`/`lg`) NÃO mudam.
|
|
1450
|
+
|
|
1451
|
+
## 2.19.0 — 2026-07-14
|
|
1452
|
+
|
|
1453
|
+
Toolbar do `ActionList` em **altura default (36px)**, alinhada com a ação primária da
|
|
1454
|
+
página. **Sem breaking.**
|
|
1455
|
+
|
|
1456
|
+
- Os controles da toolbar (período, filtros, busca, segment de views, recarregar,
|
|
1457
|
+
exibição) saem de `sm` (32px) pra `default` (36px) — mais respiro e legibilidade; a
|
|
1458
|
+
ação primária da página (o `Page` header) e a toolbar passam a ter o mesmo peso.
|
|
1459
|
+
- Simplifica: a busca deixa de precisar do nivelamento manual (`h-8`/`h-full`) que o
|
|
1460
|
+
`sm` exigia (o `Input` não tem variante `sm`) — em default tudo alinha nativamente.
|
|
1461
|
+
|
|
1462
|
+
## 2.18.0 — 2026-07-13
|
|
1463
|
+
|
|
1464
|
+
Novo subpath **`@softize/opus/mcp`** — o MCP server de runtime. **Sem breaking.**
|
|
1465
|
+
|
|
1466
|
+
- `createOpusMcpServer(runtime, { resolveContext })`: expõe as actions `ai:enabled` como
|
|
1467
|
+
**tools MCP** (ListTools) e as executa via `runtime.execute` **como o usuário resolvido**
|
|
1468
|
+
(CallTool) — a porta pra uma IA de FORA alcançar o app pelo protocolo. Mesmo bridge do
|
|
1469
|
+
agente co-locado; o MCP é só o transporte (você monta stdio/HTTP).
|
|
1470
|
+
- `runtime.aiTools()` agora é **público** — o bridge actions `ai:enabled` → tool specs,
|
|
1471
|
+
reusado pela cola do `ctx.ai` E pelo MCP server.
|
|
1472
|
+
|
|
1473
|
+
## 2.17.0 — 2026-07-13
|
|
1474
|
+
|
|
1475
|
+
Novo componente **`Chat`** (`@softize/opus/ui/react`) — **sem breaking**.
|
|
1476
|
+
|
|
1477
|
+
- `Chat`: a UI reutilizável do agente — lista de mensagens + composer, self-managed
|
|
1478
|
+
(estado, loading, auto-scroll; Enter envia, Shift+Enter quebra linha). A inteligência
|
|
1479
|
+
vem da prop `send`; no Opus, o backend liga numa rota que chama `runtime.aiFor(base).run`
|
|
1480
|
+
(o agente da 2.16 sobre as actions `ai:enabled`, como o usuário). Tipos: `ChatProps`,
|
|
1481
|
+
`ChatMessage`.
|
|
1482
|
+
|
|
1483
|
+
## 2.16.0 — 2026-07-13
|
|
1484
|
+
|
|
1485
|
+
Loop **AGÊNTICO** no recurso `ai` — as actions viram tools do modelo. **Sem breaking.**
|
|
1486
|
+
|
|
1487
|
+
- `AiAdapter` ganha `run?(input, { tools, execute, maxSteps })` (opcional, aditivo): o loop
|
|
1488
|
+
de tool-use multi-turn, PURO (o driver não conhece o registry — tools e execute vêm por
|
|
1489
|
+
DI). Implementado no driver `anthropic`.
|
|
1490
|
+
- Uma action com `ai: { enabled: true }` no contrato vira uma **tool**. `ctx.ai.run(prompt)`
|
|
1491
|
+
— ou `runtime.aiFor(base).run(historico)` fora do handler — roda o agente sobre essas
|
|
1492
|
+
actions, executando-as **COMO O USUÁRIO** (limitado pelo `ctx.can`). O que é `destructive`
|
|
1493
|
+
ou pede `requiresConfirmation` não roda sem `run(prompt, { confirm })`.
|
|
1494
|
+
- `ctx.ai` passa a ser `BoundAi` (complete/extract iguais; `run` com tools+execute
|
|
1495
|
+
pré-injetados). Novos tipos: `AiTool`, `AiMessage`, `AiRunOptions`, `AiRunResult`,
|
|
1496
|
+
`BoundAi`, `BoundAiRunOptions`.
|
|
1497
|
+
|
|
1498
|
+
## 2.15.0 — 2026-07-13
|
|
1499
|
+
|
|
1500
|
+
Novo componente de UI **`Copyable`** (nativo) — **sem breaking**.
|
|
1501
|
+
|
|
1502
|
+
- **`Copyable`** (`@softize/opus/ui/react`): clicar-pra-copiar com feedback — copia `value`
|
|
1503
|
+
pro clipboard e o ícone vira um check por ~1.5s (`feedbackMs` ajusta). Sem filhos é um
|
|
1504
|
+
botão-ícone (toolbar/célula); com filhos, o rótulo visível + o ícone. Pra IDs, tokens,
|
|
1505
|
+
slugs, URLs. No-op silencioso sem clipboard (contexto inseguro/SSR).
|
|
1506
|
+
- Promovido pela régua da reincidência (o funil "Issues do Opus"): apontado como enhancement
|
|
1507
|
+
num projeto-cliente (issue #2/#4 em `softize-dev/opus`), aceito na triagem — sai da espera
|
|
1508
|
+
do `PROMOTED.md` pras Promovidas. Projeto que compunha um copiar-com-feedback à mão troca
|
|
1509
|
+
pelo import.
|
|
1510
|
+
|
|
1511
|
+
## 2.14.1 — 2026-07-12
|
|
1512
|
+
|
|
1513
|
+
Primeiro release do repo próprio do Opus (`github.com/softize-dev/opus`). Só correções
|
|
1514
|
+
de doc/vocabulário desde 2.14.0 — **sem mudança de API**.
|
|
1515
|
+
|
|
1516
|
+
- Vocabulário "a base Opus" → "**o Opus**" no bloco gerenciado do CLAUDE.md e nas docs
|
|
1517
|
+
(o `opus setup` re-sincroniza o bloco; nada quebra); "camada-base" → "camada materializada".
|
|
1518
|
+
- Correção de doc: o kind `search` (morto desde o rename pra `list`) sobrevivia em
|
|
1519
|
+
`actions.md`, `action-list.md` e na skill `create-action` (prosa + exemplos + `scaffold.mjs`)
|
|
1520
|
+
→ tudo `list`.
|
|
1521
|
+
- Doc self-contained: o exemplo de `bindAction` deixou de importar de pacote interno;
|
|
1522
|
+
`create-action` aponta a página Actions em vez de reproduzir a ordem canônica dos campos.
|
|
1523
|
+
|
|
1524
|
+
## 2.14.0 — 2026-07-11
|
|
1525
|
+
|
|
1526
|
+
Primeira rodada do funil de feedback da base: os 4 apontamentos do projeto-exercício
|
|
1527
|
+
(`fieldnotes`, via `.opus/base-feedback.jsonl` → Radar) triados e resolvidos.
|
|
1528
|
+
|
|
1529
|
+
- Handler (e `authorize`) agora recebem o tipo PARSEADO do schema de input: campo com
|
|
1530
|
+
`.default()` deixa de chegar `| undefined` — remova os `?? fallback` redundantes.
|
|
1531
|
+
- `ViewAction.projection` virou opcional: view simples sem expand não paga mais o
|
|
1532
|
+
boilerplate `projection: []`.
|
|
1533
|
+
- `opus create`/`setup` semeiam as skills DO PACOTE (`registry/skills`) em
|
|
1534
|
+
`.claude/skills/` — esqueleto standalone não nasce mais sem o que o tarball carrega
|
|
1535
|
+
(untracked via `info/exclude`; o Maestro segue sendo o reconciliador). O bloco do
|
|
1536
|
+
CLAUDE.md agora diz o que só chega quando o Maestro rege.
|
|
1537
|
+
- Átomos do catálogo em input de action: `t.slug().zod()` (e afins) documentado na
|
|
1538
|
+
página de actions e travado por teste como superfície pública — não re-escreva
|
|
1539
|
+
regex do que a base valida.
|
|
1540
|
+
|
|
1541
|
+
## 2.13.1 — 2026-07-11
|
|
1542
|
+
|
|
1543
|
+
- Fix visual no DocBrowser: as regras custom de `code`/`pre` do prose (theme.css)
|
|
1544
|
+
vazavam pra dentro de `not-prose` — o `<pre>` do CodeBlock ganhava uma segunda
|
|
1545
|
+
borda colada na do contêiner (a "borda duplicada", gritante no dark). Guard
|
|
1546
|
+
`[class~='not-prose']` espelhando o escopo do plugin typography.
|
|
1547
|
+
|
|
1548
|
+
## 2.13.0 — 2026-07-11
|
|
1549
|
+
|
|
1550
|
+
- `@softize/opus/testing` (EXPERIMENTAL): harness de teste do protocolo —
|
|
1551
|
+
`runAction` roda uma action em UNIDADE com o mesmo pipeline do runtime (valida
|
|
1552
|
+
input, cobra `public`/`authorize` com semântica idêntica incl. DSL e `loaded`,
|
|
1553
|
+
valida output, mesmos `ActionError`); `testContext` (ctx com emit/log capturados,
|
|
1554
|
+
`can` configurável) e `memStorage` (StorageAdapter em memória). O template do
|
|
1555
|
+
`opus create` já testa com ele.
|
|
1556
|
+
- `ctx.ai` (EXPERIMENTAL): IA generativa no protocolo — `AiAdapter`
|
|
1557
|
+
(`complete`/`extract`) com driver `@softize/opus/ai/anthropic` (peer
|
|
1558
|
+
`@anthropic-ai/sdk` opcional, client injetável, default haiku). O diferencial do
|
|
1559
|
+
`extract`: o mesmo `Schema` das actions vira o contrato da resposta do modelo —
|
|
1560
|
+
saída estruturada forçada e VALIDADA pelo schema antes de devolver.
|
|
1561
|
+
- Docs novas na surface SDK: "Testes de action" e "IA generativa"; a skill `test`
|
|
1562
|
+
da metodologia passa a transcluir a página do harness.
|
|
1563
|
+
|
|
1564
|
+
### Breaking
|
|
1565
|
+
|
|
1566
|
+
- `ActionContext`/`ReactionContext` ganharam `ai` — só afeta quem CONSTRÓI o
|
|
1567
|
+
contexto à mão: adicione `ai: null`, ou troque pra `testContext()` do novo
|
|
1568
|
+
`@softize/opus/testing` (que já vem completo e observável).
|
|
1569
|
+
|
|
1570
|
+
## 2.12.0 — 2026-07-11
|
|
1571
|
+
|
|
1572
|
+
- `opus create` ficou workspace-aware: dentro de um monorepo gera só os arquivos do app
|
|
1573
|
+
(nada de `.npmrc`/workspace yaml aninhado) e avisa o que a raiz precisa ter.
|
|
1574
|
+
- `opus create <dir> --monorepo`: cria a RAIZ canônica de um workspace (apps/* +
|
|
1575
|
+
packages/*, allowBuilds da base, escopo do registry); o primeiro app vem de
|
|
1576
|
+
`opus create apps/<nome>` na raiz.
|
|
1577
|
+
|
|
1578
|
+
## 2.11.0 — 2026-07-11
|
|
1579
|
+
|
|
1580
|
+
- `ctx.storage` (EXPERIMENTAL): storage de arquivos no protocolo — `StorageAdapter`
|
|
1581
|
+
(put/get/delete/url) com drivers `@softize/opus/storage/fs` (disco local) e
|
|
1582
|
+
`@softize/opus/storage/s3` (S3-compatível; peers `@aws-sdk/*` opcionais).
|
|
1583
|
+
- `opus db migrate` aplica o SCHEMA IDEMPOTENTE (config `schema`, script SQL evolutivo)
|
|
1584
|
+
e roda o drift-check entidade ↔ banco na sequência (exit ≠ 0 se divergir).
|
|
1585
|
+
|
|
1586
|
+
### Breaking
|
|
1587
|
+
|
|
1588
|
+
- **`opus db migrate` não roda mais migrations Kysely** (`migrations/*.ts` com
|
|
1589
|
+
`up`/`down`) e **`migrate down` foi removido**. Migração: converta o schema num
|
|
1590
|
+
script SQL idempotente (`CREATE IF NOT EXISTS` + guards `DO $$ IF EXISTS`), salve
|
|
1591
|
+
em `db/schema.sql` (ou aponte via `schema:` no opus.config.ts) e delete os
|
|
1592
|
+
`migrations/*.ts`. Rollback passa a ser: editar o script e re-rodar.
|
|
1593
|
+
- `ActionContext`/`ReactionContext` ganharam `storage` — só afeta quem CONSTRÓI o
|
|
1594
|
+
contexto à mão (testes): adicione `storage: null`.
|
|
1595
|
+
|
|
1596
|
+
## 2.10.0 — 2026-07-10
|
|
1597
|
+
|
|
1598
|
+
- `opus create <dir>`: scaffold do app canônico (vite + react + tema v4 CSS-first,
|
|
1599
|
+
domínio-exemplo com gates verdes, dia zero completo, dev server na porta do preview).
|
|
1600
|
+
- Template com `.prettierrc.json` (espelho executável da seção Formatação da skill
|
|
1601
|
+
code-style) + `format`/`format:check` no CI de fábrica.
|
|
1602
|
+
- Correção de empacotamento: `@tailwindcss/typography` virou dependency (a theme.css
|
|
1603
|
+
o exige — consumidor standalone quebrava no build).
|
|
1604
|
+
|
|
1605
|
+
## 2.9.0 — 2026-07-10
|
|
1606
|
+
|
|
1607
|
+
- Hooks da base no registry (`registry/hooks`): `opus-check-on-stop` (gate do
|
|
1608
|
+
`opus check` no fim do turno, escopado aos apps alterados) e `link-memory-on-start`
|
|
1609
|
+
(liga a memória versionada do repo à sessão). Materializados pelo Maestro.
|
|
1610
|
+
- `opus setup`: CLAUDE.md com BLOCO GERENCIADO (`<!-- opus:base -->…`) — o setup
|
|
1611
|
+
re-sinca o bloco quando a base evolui; fora dele o arquivo é seu. Dia zero:
|
|
1612
|
+
semente de `.claude/memory/` + CI de fábrica + aviso de script `test` ausente.
|
|
1613
|
+
|
|
1614
|
+
## 2.8.0 e anteriores
|
|
1615
|
+
|
|
1616
|
+
Sem changelog (a disciplina começou na 2.9.0). Referência: o histórico do monorepo.
|