@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
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tokens & Tema
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Tokens & Tema
|
|
6
|
+
|
|
7
|
+
O tema canônico vive no Opus (`theme.css`): tokens com a cor inteira na var, mapeados pro
|
|
8
|
+
Tailwind via `@theme inline`. Os swatches abaixo leem as vars **ao vivo** — troque o tema do app
|
|
9
|
+
e a página acompanha.
|
|
10
|
+
|
|
11
|
+
## Superfícies e intenções
|
|
12
|
+
|
|
13
|
+
> Cada token de superfície tem o seu par de texto (`-foreground`). Use o par, nunca uma cor solta.
|
|
14
|
+
|
|
15
|
+
```tsx preview
|
|
16
|
+
const SURFACES = [
|
|
17
|
+
['background', 'foreground', 'O fundo da página e o texto padrão.'],
|
|
18
|
+
['card', 'card-foreground', 'Superfície elevada (Card, painéis).'],
|
|
19
|
+
['popover', 'popover-foreground', 'Superfície flutuante (menus, popovers, dialogs).'],
|
|
20
|
+
['muted', 'muted-foreground', 'Fundo discreto e texto secundário.'],
|
|
21
|
+
['accent', 'accent-foreground', 'Hover e seleção.'],
|
|
22
|
+
['secondary', 'secondary-foreground', 'Ação secundária.'],
|
|
23
|
+
['primary', 'primary-foreground', 'A ação principal — alto contraste.'],
|
|
24
|
+
['destructive', 'destructive-foreground', 'Ação destrutiva e erro.'],
|
|
25
|
+
]
|
|
26
|
+
render(
|
|
27
|
+
<div className="w-full overflow-hidden rounded-lg border border-border">
|
|
28
|
+
{SURFACES.map(([token, fg, desc]) => (
|
|
29
|
+
<div key={token} className="flex items-center gap-4 border-b border-border px-4 py-3 last:border-b-0">
|
|
30
|
+
<div
|
|
31
|
+
className="flex h-10 w-28 shrink-0 items-center justify-center rounded-md border border-border text-xs font-medium"
|
|
32
|
+
style={{ backgroundColor: `var(--${token})`, color: `var(--${fg})` }}
|
|
33
|
+
>
|
|
34
|
+
Texto
|
|
35
|
+
</div>
|
|
36
|
+
<div className="min-w-0">
|
|
37
|
+
<div className="font-mono text-xs">--{token} · --{fg}</div>
|
|
38
|
+
<div className="text-xs text-muted-foreground">{desc}</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
))}
|
|
42
|
+
</div>,
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Linhas e foco
|
|
47
|
+
|
|
48
|
+
> Bordas e o anel de foco também são tokens — nada de cinza hardcoded. A aresta de superfície
|
|
49
|
+
> elevada (`--edge`) mora na seção Elevação por papel.
|
|
50
|
+
|
|
51
|
+
```tsx preview
|
|
52
|
+
const LINES = [
|
|
53
|
+
['border', 'Bordas de contêineres (cards, tabelas, separadores).'],
|
|
54
|
+
['input', 'Borda de campos de formulário.'],
|
|
55
|
+
['ring', 'O anel de foco (focus-visible).'],
|
|
56
|
+
]
|
|
57
|
+
render(
|
|
58
|
+
<div className="w-full overflow-hidden rounded-lg border border-border">
|
|
59
|
+
{LINES.map(([token, desc]) => (
|
|
60
|
+
<div key={token} className="flex items-center gap-4 border-b border-border px-4 py-3 last:border-b-0">
|
|
61
|
+
<div className="h-10 w-28 shrink-0 rounded-md border-2" style={{ borderColor: `var(--${token})` }} />
|
|
62
|
+
<div className="min-w-0">
|
|
63
|
+
<div className="font-mono text-xs">--{token}</div>
|
|
64
|
+
<div className="text-xs text-muted-foreground">{desc}</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
))}
|
|
68
|
+
</div>,
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Elevação por papel
|
|
73
|
+
|
|
74
|
+
> A sombra segue o mesmo desenho das cores: token por PAPEL, não por tamanho — o papel de
|
|
75
|
+
> superfície carrega cor, elevação e forma (`bg-card` · `shadow-card` · `rounded-card`). Em superfície elevada, a aresta é o anel hairline `ring-1 ring-edge`
|
|
76
|
+
> no lugar da borda (nunca os dois juntos); borda sólida fica onde é affordance de interação
|
|
77
|
+
> (inputs, controles — que são flat). Os defaults `shadow-*` do Tailwind seguem intactos: um
|
|
78
|
+
> `shadow-md` cru no código é sinal de review, não vocabulário da casa.
|
|
79
|
+
|
|
80
|
+
```tsx preview
|
|
81
|
+
const ROLES = [
|
|
82
|
+
['card', 'Conteúdo em repouso (Card, composer do Chat).'],
|
|
83
|
+
['popover', 'Flutuante leve (menus, selects, popovers).'],
|
|
84
|
+
['dialog', 'Flutuante modal (dialogs, sheets).'],
|
|
85
|
+
]
|
|
86
|
+
render(
|
|
87
|
+
<div className="flex flex-wrap gap-8 py-4">
|
|
88
|
+
{ROLES.map(([role, desc]) => (
|
|
89
|
+
<div key={role} className="flex flex-col items-center gap-3">
|
|
90
|
+
<div
|
|
91
|
+
className={'flex h-24 w-40 items-center justify-center bg-card text-xs ring-1 ring-edge rounded-' + role + ' shadow-' + role}
|
|
92
|
+
>
|
|
93
|
+
Texto
|
|
94
|
+
</div>
|
|
95
|
+
<div className="text-center">
|
|
96
|
+
<div className="font-mono text-xs">shadow-{role}</div>
|
|
97
|
+
<div className="max-w-44 text-xs text-muted-foreground">{desc}</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
))}
|
|
101
|
+
</div>,
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Densidade
|
|
106
|
+
|
|
107
|
+
> A base é 15px: o `html` da casa fixa o rem, e TODA a escala (texto, spacing, radius) deriva
|
|
108
|
+
> dele — a UI densifica ~6% vs o default do browser (16px). Nada de font-size solto em px nos
|
|
109
|
+
> componentes; quem quiser outra densidade sobrescreve o `html` no app.
|
|
110
|
+
|
|
111
|
+
## Radius
|
|
112
|
+
|
|
113
|
+
> Escala mapeada no `@theme inline` preservando o sentido pré-v4 dos utilitários `rounded-*`
|
|
114
|
+
> (neutraliza o rename do Tailwind v4). E a forma também tem papel: `rounded-card/popover/dialog`
|
|
115
|
+
> são a forma das superfícies elevadas (par do `bg-*`/`shadow-*`), derivados de `--radius` — a
|
|
116
|
+
> identidade por app escala tudo junto. Controles (botões, inputs) seguem na escala sm–xl.
|
|
117
|
+
|
|
118
|
+
```tsx preview
|
|
119
|
+
render(
|
|
120
|
+
<div className="flex flex-wrap gap-4">
|
|
121
|
+
{[
|
|
122
|
+
['rounded-sm', 'radius − 4px'],
|
|
123
|
+
['rounded-md', 'radius − 2px'],
|
|
124
|
+
['rounded-lg', 'radius (0.625rem)'],
|
|
125
|
+
['rounded-xl', 'radius + 4px'],
|
|
126
|
+
['rounded-popover', 'papel popover (radius − 2px)'],
|
|
127
|
+
['rounded-dialog', 'papel dialog (radius + 6px)'],
|
|
128
|
+
['rounded-card', 'papel card (radius + 6px)'],
|
|
129
|
+
].map(([cls, calc]) => (
|
|
130
|
+
<div key={cls} className="flex items-center gap-3">
|
|
131
|
+
<div className={'h-12 w-12 border-2 border-foreground/30 bg-muted ' + cls} />
|
|
132
|
+
<div>
|
|
133
|
+
<div className="font-mono text-xs">{cls}</div>
|
|
134
|
+
<div className="text-xs text-muted-foreground">{calc}</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
))}
|
|
138
|
+
</div>,
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Divergências declaradas vs shadcn
|
|
143
|
+
|
|
144
|
+
> A regra anti-drift: o que não está declarado aqui (e no `theme.css`) é drift e deve ser
|
|
145
|
+
> sincronizado — skill `opus`.
|
|
146
|
+
|
|
147
|
+
1. Formato HSL nos tokens (o upstream migrou pra oklch) — legibilidade e ferramentas nossas; os
|
|
148
|
+
valores acompanham o upstream, só o formato difere.
|
|
149
|
+
2. Elevação no dark: `--card`/`--popover` ficam ACIMA de `--background` (10% vs 3.9%) — identidade
|
|
150
|
+
da casa; dialog e card "sobem" da página de verdade.
|
|
151
|
+
3. Radius mapeado (sm/md/lg/xl) preservando o sentido pré-v4 dos utilitários `rounded-*`.
|
|
152
|
+
|
|
153
|
+
(A antiga nº 4 — escala `shadow-*` sobrescrita — foi aposentada: a elevação da casa vive em
|
|
154
|
+
namespace próprio, `shadow-card/popover/dialog` + `ring-edge`, sem tocar os defaults do Tailwind.)
|
|
155
|
+
|
|
156
|
+
5. Bordas mais leves no light: `--border`/`--input` em 92.5% (upstream 89.8%) — par do canvas
|
|
157
|
+
quase-branco do body; a aresta de superfície elevada já é o `ring-edge`.
|
|
158
|
+
|
|
159
|
+
## Identidade por app
|
|
160
|
+
|
|
161
|
+
> O tema do Opus é a base; cada app sobrescreve as vars pra ter a própria identidade sem forkar componente.
|
|
162
|
+
|
|
163
|
+
```css
|
|
164
|
+
/* index.css do app — depois do import do tema. */
|
|
165
|
+
@import '@softize/opus/ui/theme.css';
|
|
166
|
+
|
|
167
|
+
:root {
|
|
168
|
+
--primary: hsl(262 83% 58%); /* A cor da marca do projeto. */
|
|
169
|
+
--ring: hsl(262 83% 58%);
|
|
170
|
+
}
|
|
171
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
## Básico
|
|
2
|
+
|
|
3
|
+
Texto auxiliar, nunca essencial — quem navega por toque não vê tooltip. Em botão só de ícone, o aria-label continua obrigatório.
|
|
4
|
+
|
|
5
|
+
```tsx preview
|
|
6
|
+
<Tooltip>
|
|
7
|
+
<TooltipTrigger asChild>
|
|
8
|
+
<Button variant="ghost" size="icon" aria-label="Nova sessão"><Plus /></Button>
|
|
9
|
+
</TooltipTrigger>
|
|
10
|
+
<TooltipContent>Nova sessão.</TooltipContent>
|
|
11
|
+
</Tooltip>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Lados
|
|
15
|
+
|
|
16
|
+
side escolhe o lado preferido; o Radix inverte sozinho quando falta espaço.
|
|
17
|
+
|
|
18
|
+
```tsx preview
|
|
19
|
+
<Tooltip>
|
|
20
|
+
<TooltipTrigger asChild><Button variant="ghost">Em cima</Button></TooltipTrigger>
|
|
21
|
+
<TooltipContent side="top">Lado padrão.</TooltipContent>
|
|
22
|
+
</Tooltip>
|
|
23
|
+
<Tooltip>
|
|
24
|
+
<TooltipTrigger asChild><Button variant="ghost">Direita</Button></TooltipTrigger>
|
|
25
|
+
<TooltipContent side="right">side="right".</TooltipContent>
|
|
26
|
+
</Tooltip>
|
|
27
|
+
<Tooltip>
|
|
28
|
+
<TooltipTrigger asChild><Button variant="ghost">Embaixo</Button></TooltipTrigger>
|
|
29
|
+
<TooltipContent side="bottom">side="bottom".</TooltipContent>
|
|
30
|
+
</Tooltip>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Provider no root
|
|
34
|
+
|
|
35
|
+
O TooltipProvider embrulha o app uma vez (delay compartilhado); cada Tooltip dispensa provider próprio. Este app já monta o seu no main.tsx.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
/* main.tsx do app — uma vez, no root. */
|
|
39
|
+
<TooltipProvider delayDuration={200}>
|
|
40
|
+
<App />
|
|
41
|
+
</TooltipProvider>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Props
|
|
45
|
+
|
|
46
|
+
| Prop | Tipo | Default | Descrição |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `TooltipProvider.delayDuration` | `number` | `0` | Atraso (ms) até aparecer — compartilhado por todos os tooltips do app. |
|
|
49
|
+
| `TooltipContent.side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'top'` | Lado preferido; inverte sozinho sem espaço (Radix). |
|
|
50
|
+
| `TooltipContent.sideOffset` | `number` | `4` | Distância (px) entre gatilho e dica. |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
## Básico
|
|
2
|
+
|
|
3
|
+
Trunca numa linha e mostra tooltip **só quando o texto realmente corta** (medição do
|
|
4
|
+
overflow, re-medida em resize). Texto que cabe não ganha dica — diferente do `title`
|
|
5
|
+
sempre presente, que vira ruído.
|
|
6
|
+
|
|
7
|
+
```tsx preview
|
|
8
|
+
<div className="w-48 rounded-md border p-2">
|
|
9
|
+
<Truncate>relatorio-consolidado-vendas-pos-vendas-2026-julho-final-v3.xlsx</Truncate>
|
|
10
|
+
</div>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quando cabe, nada acontece
|
|
14
|
+
|
|
15
|
+
O mesmo componente, com espaço de sobra: sem tooltip, sem atributo, só o texto.
|
|
16
|
+
|
|
17
|
+
```tsx preview
|
|
18
|
+
<div className="w-96 rounded-md border p-2">
|
|
19
|
+
<Truncate>resumo.pdf</Truncate>
|
|
20
|
+
</div>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Conteúdo da dica
|
|
24
|
+
|
|
25
|
+
`tooltip` sobrepõe o conteúdo mostrado no hover (default: os próprios children) — útil
|
|
26
|
+
quando a dica precisa de mais contexto que o texto cortado.
|
|
27
|
+
|
|
28
|
+
```tsx preview
|
|
29
|
+
<div className="w-48 rounded-md border p-2">
|
|
30
|
+
<Truncate tooltip="apps/platform/src/domains/aftersales/actions/schedule-followup.ts">
|
|
31
|
+
schedule-followup.ts — apps/platform/src/domains/aftersales
|
|
32
|
+
</Truncate>
|
|
33
|
+
</div>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Requer `TooltipProvider` na raiz (o esqueleto do `opus create` já monta). Largura vem do
|
|
37
|
+
container ou de `className` (`max-w-*`) — o span é `block truncate`.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Como consumir
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Como consumir a UI
|
|
6
|
+
|
|
7
|
+
A verdade visual da casa: os componentes do `@softize/opus/ui` renderizados com o nosso tema. O
|
|
8
|
+
que você vê aqui é o que os apps entregam — a referência é esta doc, não o site do shadcn.
|
|
9
|
+
|
|
10
|
+
## Por que esta doc existe
|
|
11
|
+
|
|
12
|
+
> O site do shadcn é catálogo do que existe lá; os exemplos de lá decoram composições que não
|
|
13
|
+
> existem no nosso registry. A expectativa visual e de uso nasce aqui.
|
|
14
|
+
|
|
15
|
+
Cada página de componente mostra exemplos vivos por variante (renderizados com o tema da casa), o
|
|
16
|
+
snippet de como se escreve e as props que importam. O texto de quando usar vem do próprio
|
|
17
|
+
componente (o `meta` co-localizado na fonte) — a doc deriva, não duplica.
|
|
18
|
+
|
|
19
|
+
## Como consumir
|
|
20
|
+
|
|
21
|
+
> Um barrel só pra componentes e hooks; o tema entra por CSS. Nada de copiar componente pra dentro do app.
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
// Componentes e hooks — tudo do mesmo barrel.
|
|
25
|
+
import { Button, Dialog, useAction } from '@softize/opus/ui/react'
|
|
26
|
+
|
|
27
|
+
/* index.css — o tema canônico + os componentes do Opus no scan do Tailwind. */
|
|
28
|
+
@import '@softize/opus/ui/theme.css';
|
|
29
|
+
@source '../node_modules/@softize/opus/src/ui';
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Origem e anti-drift
|
|
33
|
+
|
|
34
|
+
> Cada componente declara de onde veio — e a regra de sincronização vem junto.
|
|
35
|
+
|
|
36
|
+
- **Origem shadcn** — port curado do shadcn (Radix + cmdk). Toda divergência do upstream é
|
|
37
|
+
DECLARADA no componente ou no tema — o que diverge sem declaração é drift e deve ser
|
|
38
|
+
sincronizado (skill `opus`).
|
|
39
|
+
- **Nativo do Opus** — nasceu aqui (os patterns de action). Não tem upstream pra acompanhar; a
|
|
40
|
+
referência é esta doc.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Atualizar o Opus
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Atualizar o Opus
|
|
6
|
+
|
|
7
|
+
Projeto em produção fica meses sem tocar no Opus — e um dia precisa pular várias
|
|
8
|
+
versões de uma vez. O ritual é o mesmo para humano e IA, e não depende de memória:
|
|
9
|
+
cada peça do caminho está escrita ou é um gate que quebra alto.
|
|
10
|
+
|
|
11
|
+
## 1. Saber que está atrás
|
|
12
|
+
|
|
13
|
+
O **Maestro** compara o pin (`opus.json`) com a versão instalada e com a fonte a cada
|
|
14
|
+
sessão — o alerta no rodapé diz a distância (`v2.9.0 → v2.15.2`) e o **Atualizar** faz
|
|
15
|
+
a parte mecânica: bump da dependência, re-materialização da camada de IA (skills,
|
|
16
|
+
hooks, agentes) e re-sync do bloco gerenciado do CLAUDE.md. Fora do Maestro:
|
|
17
|
+
`pnpm outdated @softize/opus`.
|
|
18
|
+
|
|
19
|
+
## 2. Saber o que mudou
|
|
20
|
+
|
|
21
|
+
O changelog **viaja no pacote** — depois do bump, está em
|
|
22
|
+
`node_modules/@softize/opus/CHANGELOG.md`. Uma entrada por versão; quando quebra,
|
|
23
|
+
a seção **Breaking** diz a migração (o que renomeou, o que fazer no seu código).
|
|
24
|
+
A esteira de release **recusa** publicar versão sem entrada — o arquivo não defasa.
|
|
25
|
+
|
|
26
|
+
- **minor** (2.9 → 2.10): compatível; leia por curiosidade.
|
|
27
|
+
- **major** (2.x → 3.0): pare nas seções Breaking de cada versão pulada, na ordem.
|
|
28
|
+
|
|
29
|
+
## 3. Deixar os gates cobrarem o resto
|
|
30
|
+
|
|
31
|
+
O Opus ship source: o que quebrou aparece com arquivo e linha. Rode, na ordem:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm typecheck # a API nova cobra os tipos
|
|
35
|
+
pnpm exec opus check # convenções (regras novas inclusas)
|
|
36
|
+
pnpm test # comportamento
|
|
37
|
+
pnpm manifest:check # a spec projetada ficou fresca?
|
|
38
|
+
opus db migrate # schema em dia (drift-check embutido)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Verde em tudo = atualizado de verdade. O bump entra no PR como qualquer mudança de
|
|
42
|
+
código — atualização de base passa por review, não por fé.
|
|
43
|
+
|
|
44
|
+
## Pulou muitas versões?
|
|
45
|
+
|
|
46
|
+
O ritual não muda, só o volume: leia as seções Breaking acumuladas (o changelog é
|
|
47
|
+
uma lista, não um diff), aplique na ordem e deixe os gates validarem o conjunto.
|
|
48
|
+
Não há caminho especial — é o mesmo laço, com mais iterações.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* doc-client.tsx — o PALCO dos exemplos vivos dos patterns. Client fake + contratos
|
|
3
|
+
* de mentira pra doc demonstrar ActionForm/Search/View/Trigger sem servidor.
|
|
4
|
+
* Nada daqui vai pra produção: no app real, o contrato vem do spec compartilhado e o
|
|
5
|
+
* client fala com a API (ver apps/admin/web/src/adapter.ts, o consumidor canônico).
|
|
6
|
+
*/
|
|
7
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
8
|
+
import { z } from 'zod'
|
|
9
|
+
import { defineContract, error } from '../../core/index.ts'
|
|
10
|
+
import type { ActionDef, ActionResult, ClientAdapter, Paginated } from '../../core/index.ts'
|
|
11
|
+
import { TbdlibProvider } from '../react.tsx'
|
|
12
|
+
|
|
13
|
+
// — Dados enlatados (domínio da casa) —
|
|
14
|
+
export interface DocBrowserWorkspace {
|
|
15
|
+
id: string
|
|
16
|
+
name: string
|
|
17
|
+
client: string
|
|
18
|
+
status: 'active' | 'onboarding' | 'paused'
|
|
19
|
+
agents: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const WORKSPACES: DocBrowserWorkspace[] = [
|
|
23
|
+
{ id: 'empresa-x', name: 'Empresa X', client: 'Empresa X', status: 'active', agents: 3 },
|
|
24
|
+
{ id: 'empresa-w', name: 'Empresa W', client: 'Empresa W', status: 'active', agents: 4 },
|
|
25
|
+
{ id: 'empresa-y', name: 'Empresa Y', client: 'Empresa Y', status: 'onboarding', agents: 1 },
|
|
26
|
+
{ id: 'empresa-z', name: 'Empresa Z', client: 'Empresa Z', status: 'paused', agents: 2 },
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const STATUS = ['active', 'onboarding', 'paused'] as const
|
|
30
|
+
|
|
31
|
+
// — Contratos fake (mesmo shape dos reais; ver apps/admin/shared/src/workspaces.ts) —
|
|
32
|
+
export const docWorkspaceCreate = defineContract({
|
|
33
|
+
name: 'workspace.create',
|
|
34
|
+
kind: 'form',
|
|
35
|
+
label: 'Novo workspace',
|
|
36
|
+
messages: { success: 'Workspace criado', error: 'Falha ao criar workspace' },
|
|
37
|
+
input: z.object({
|
|
38
|
+
name: z.string().min(2).max(120),
|
|
39
|
+
status: z.enum(STATUS).default('onboarding'),
|
|
40
|
+
contact: z.string().max(200).optional(),
|
|
41
|
+
notes: z.string().max(2000).optional(),
|
|
42
|
+
}),
|
|
43
|
+
output: z.object({ id: z.string() }),
|
|
44
|
+
fields: {
|
|
45
|
+
name: { label: 'Nome', placeholder: 'Ex.: Empresa X', hint: 'Digite "Softize" pra ver o erro de servidor.' },
|
|
46
|
+
status: { label: 'Status' },
|
|
47
|
+
contact: { label: 'Contato', placeholder: 'email@cliente.com' },
|
|
48
|
+
notes: { label: 'Observações', placeholder: 'Contexto do onboarding…' },
|
|
49
|
+
},
|
|
50
|
+
invalidates: ['workspace.list'],
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
export const docWorkspaceList = defineContract({
|
|
54
|
+
name: 'workspace.list',
|
|
55
|
+
kind: 'list',
|
|
56
|
+
summary: 'Lista os workspaces do palco da doc',
|
|
57
|
+
input: z.object({
|
|
58
|
+
q: z.string().optional(),
|
|
59
|
+
sort: z.string().optional(),
|
|
60
|
+
status: z.enum(STATUS).optional(),
|
|
61
|
+
client: z.string().optional(),
|
|
62
|
+
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
63
|
+
}),
|
|
64
|
+
output: z.object({
|
|
65
|
+
id: z.string(),
|
|
66
|
+
name: z.string(),
|
|
67
|
+
client: z.string(),
|
|
68
|
+
status: z.enum(STATUS),
|
|
69
|
+
agents: z.number(),
|
|
70
|
+
}),
|
|
71
|
+
// O modo DECLARATIVO do ActionList: a tabela e a toolbar derivam daqui.
|
|
72
|
+
columns: [
|
|
73
|
+
{ key: 'name', label: 'Workspace', sortable: true },
|
|
74
|
+
{ key: 'client', label: 'Cliente' },
|
|
75
|
+
{ key: 'status', label: 'Status', type: 'badge', fit: true },
|
|
76
|
+
{ key: 'agents', label: 'Agentes', type: 'number', fit: true },
|
|
77
|
+
],
|
|
78
|
+
filters: {
|
|
79
|
+
status: {
|
|
80
|
+
label: 'Status',
|
|
81
|
+
type: 'select',
|
|
82
|
+
options: {
|
|
83
|
+
kind: 'static',
|
|
84
|
+
items: [
|
|
85
|
+
{ value: 'active', label: 'Ativo' },
|
|
86
|
+
{ value: 'onboarding', label: 'Onboarding' },
|
|
87
|
+
{ value: 'paused', label: 'Pausado' },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
client: { label: 'Cliente', type: 'text', advanced: true },
|
|
92
|
+
},
|
|
93
|
+
text: { fields: ['name'] },
|
|
94
|
+
sort: { fields: ['name'] },
|
|
95
|
+
paginate: 'cursor',
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
export const docWorkspaceView = defineContract({
|
|
99
|
+
name: 'workspace.view',
|
|
100
|
+
kind: 'view',
|
|
101
|
+
summary: 'Detalhe de um workspace do palco da doc',
|
|
102
|
+
input: z.object({ id: z.string() }),
|
|
103
|
+
output: z.object({
|
|
104
|
+
id: z.string(),
|
|
105
|
+
name: z.string(),
|
|
106
|
+
client: z.string(),
|
|
107
|
+
status: z.enum(STATUS),
|
|
108
|
+
agents: z.number(),
|
|
109
|
+
}),
|
|
110
|
+
projection: ['id', 'name', 'client', 'status', 'agents'],
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
export const docSessionArchive = defineContract({
|
|
114
|
+
name: 'session.archive',
|
|
115
|
+
kind: 'simple',
|
|
116
|
+
label: 'Arquivar sessão',
|
|
117
|
+
messages: { success: 'Sessão arquivada', error: 'Falha ao arquivar' },
|
|
118
|
+
input: z.object({ id: z.string() }),
|
|
119
|
+
output: z.object({ ok: z.boolean() }),
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
export const docSessionDelete = defineContract({
|
|
123
|
+
name: 'session.delete',
|
|
124
|
+
kind: 'simple',
|
|
125
|
+
label: 'Excluir sessão',
|
|
126
|
+
messages: { success: 'Sessão excluída', error: 'Falha ao excluir' },
|
|
127
|
+
// ConfirmSpec DECLARADO no contrato: o ActionTrigger monta o dialog sozinho.
|
|
128
|
+
confirm: {
|
|
129
|
+
title: 'Excluir a sessão?',
|
|
130
|
+
message: 'O worktree e o preview desta sessão serão removidos.',
|
|
131
|
+
confirmLabel: 'Excluir',
|
|
132
|
+
destructive: true,
|
|
133
|
+
},
|
|
134
|
+
input: z.object({ id: z.string() }),
|
|
135
|
+
output: z.object({ ok: z.boolean() }),
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))
|
|
139
|
+
|
|
140
|
+
// — Client fake: pattern-match no action.name, latência pra dar tempo de VER o loading —
|
|
141
|
+
const docClient: ClientAdapter = {
|
|
142
|
+
name: 'doc-client',
|
|
143
|
+
kind: 'client',
|
|
144
|
+
async run<T>(action: ActionDef, input: unknown): Promise<ActionResult<T>> {
|
|
145
|
+
await sleep(400)
|
|
146
|
+
const meta = { actionId: '', action: action.name, durationMs: 400 }
|
|
147
|
+
switch (action.name) {
|
|
148
|
+
case 'workspace.create': {
|
|
149
|
+
const { name } = input as { name: string }
|
|
150
|
+
// Caminho de erro demonstrável: o nome reservado simula conflito no servidor.
|
|
151
|
+
if (name.trim().toLowerCase() === 'softize') {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
error: error({ code: 'conflict', category: 'conflict', message: 'Já existe um workspace com este nome.' }),
|
|
155
|
+
meta,
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { ok: true, data: { id: name.toLowerCase().replace(/\s+/g, '-') } as T, meta }
|
|
159
|
+
}
|
|
160
|
+
case 'workspace.list': {
|
|
161
|
+
const { q, status, client, sort } = input as {
|
|
162
|
+
q?: string
|
|
163
|
+
status?: DocBrowserWorkspace['status']
|
|
164
|
+
client?: string
|
|
165
|
+
sort?: string
|
|
166
|
+
}
|
|
167
|
+
const items = WORKSPACES.filter(
|
|
168
|
+
(w) =>
|
|
169
|
+
(q === undefined || q === '' || w.name.toLowerCase().includes(q.toLowerCase())) &&
|
|
170
|
+
(status === undefined || w.status === status) &&
|
|
171
|
+
(client === undefined || client === '' || w.client.toLowerCase().includes(client.toLowerCase())),
|
|
172
|
+
)
|
|
173
|
+
// Convenção do sort declarativo: '<key>:<dir>' — o handler implementa o orderBy.
|
|
174
|
+
if (sort !== undefined) {
|
|
175
|
+
const [field, dir] = sort.split(':')
|
|
176
|
+
if (field === 'name') items.sort((a, b) => (dir === 'desc' ? b.name.localeCompare(a.name) : a.name.localeCompare(b.name)))
|
|
177
|
+
}
|
|
178
|
+
const page: Paginated<DocBrowserWorkspace> = { items, cursor: { next: null }, total: items.length }
|
|
179
|
+
return { ok: true, data: page as T, meta }
|
|
180
|
+
}
|
|
181
|
+
case 'workspace.view': {
|
|
182
|
+
const { id } = input as { id: string }
|
|
183
|
+
const ws = WORKSPACES.find((w) => w.id === id)
|
|
184
|
+
if (ws === undefined) {
|
|
185
|
+
return {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: error({ code: 'not_found', category: 'not_found', message: 'Workspace não encontrado.' }),
|
|
188
|
+
meta,
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return { ok: true, data: ws as T, meta }
|
|
192
|
+
}
|
|
193
|
+
case 'session.archive':
|
|
194
|
+
case 'session.delete':
|
|
195
|
+
return { ok: true, data: { ok: true } as T, meta }
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
ok: false,
|
|
199
|
+
error: error({ code: 'not_found', category: 'not_found', message: 'Action sem resposta enlatada no palco da doc.' }),
|
|
200
|
+
meta,
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const queryClient = new QueryClient()
|
|
206
|
+
|
|
207
|
+
/** Embrulha os exemplos de pattern: react-query + driver do Opus com o client fake. */
|
|
208
|
+
export function DocBrowserActionProvider({ children }: { children: React.ReactNode }): React.ReactElement {
|
|
209
|
+
return (
|
|
210
|
+
<QueryClientProvider client={queryClient}>
|
|
211
|
+
<TbdlibProvider client={docClient}>{children}</TbdlibProvider>
|
|
212
|
+
</QueryClientProvider>
|
|
213
|
+
)
|
|
214
|
+
}
|