@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/src/ui/index.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @softize/opus/ui — shared types for UI drivers.
|
|
3
|
+
*
|
|
4
|
+
* Driver específico (react, vue) implementa Provider + hooks + componentes.
|
|
5
|
+
* Aqui ficam apenas types/contratos compartilhados.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ActionError, Paginated } from '../core/index.ts'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Metadado de descoberta de um componente — cada componente co-localiza um `meta`
|
|
12
|
+
* com este shape; o manifesto/MCP lê isso da fonte (em vez de parsear cva via AST).
|
|
13
|
+
*/
|
|
14
|
+
export interface ComponentMeta {
|
|
15
|
+
/** Nome canônico (kebab), ex.: 'button', 'dropdown-menu'. */
|
|
16
|
+
name: string
|
|
17
|
+
/** Origem: descendente do shadcn (forkado) ou nativo do Opus. */
|
|
18
|
+
ancestry: 'shadcn' | 'opus'
|
|
19
|
+
/** Quando usar / quando não — guia o agente de design a escolher certo. */
|
|
20
|
+
whenToUse: string
|
|
21
|
+
/** Aviso de retirada no catálogo, com o caminho de migração. */
|
|
22
|
+
deprecated?: {
|
|
23
|
+
/** Alternativa recomendada; aceita `código` inline como `whenToUse`. */
|
|
24
|
+
alternative: string
|
|
25
|
+
/** Versão em que a retirada começou, quando houver. */
|
|
26
|
+
since?: string
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Estado de invocação de uma action (sync). Hooks de UI driver expõem
|
|
32
|
+
* shape derivado disto.
|
|
33
|
+
*/
|
|
34
|
+
export interface ActionState<TData> {
|
|
35
|
+
data: TData | undefined
|
|
36
|
+
error: ActionError | undefined
|
|
37
|
+
isIdle: boolean
|
|
38
|
+
isLoading: boolean
|
|
39
|
+
isSuccess: boolean
|
|
40
|
+
isError: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ListState<TItem> {
|
|
44
|
+
items: TItem[]
|
|
45
|
+
cursor: { next: string | null; prev?: string | null }
|
|
46
|
+
total: number | undefined
|
|
47
|
+
error: ActionError | undefined
|
|
48
|
+
isIdle: boolean
|
|
49
|
+
isLoading: boolean
|
|
50
|
+
isSuccess: boolean
|
|
51
|
+
isError: boolean
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Wrapper conveniente pra zerar estado quando handler chama `reset()`.
|
|
56
|
+
*/
|
|
57
|
+
export function idleAction<T>(): ActionState<T> {
|
|
58
|
+
return {
|
|
59
|
+
data: undefined,
|
|
60
|
+
error: undefined,
|
|
61
|
+
isIdle: true,
|
|
62
|
+
isLoading: false,
|
|
63
|
+
isSuccess: false,
|
|
64
|
+
isError: false,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function idleLookup<T>(): ListState<T> {
|
|
69
|
+
return {
|
|
70
|
+
items: [],
|
|
71
|
+
cursor: { next: null },
|
|
72
|
+
total: undefined,
|
|
73
|
+
error: undefined,
|
|
74
|
+
isIdle: true,
|
|
75
|
+
isLoading: false,
|
|
76
|
+
isSuccess: false,
|
|
77
|
+
isError: false,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Helper pra checar se um output tem shape `Paginated`.
|
|
83
|
+
*/
|
|
84
|
+
export function isPaginated(value: unknown): value is Paginated<unknown> {
|
|
85
|
+
return (
|
|
86
|
+
typeof value === 'object' &&
|
|
87
|
+
value !== null &&
|
|
88
|
+
'items' in value &&
|
|
89
|
+
Array.isArray((value as { items: unknown }).items) &&
|
|
90
|
+
'cursor' in value
|
|
91
|
+
)
|
|
92
|
+
}
|
package/src/ui/lib/cn.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from 'clsx'
|
|
2
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Junta classes condicionais (clsx) e resolve conflitos do Tailwind (twMerge).
|
|
6
|
+
* Canônico do Opus — o app importa daqui em vez de duplicar.
|
|
7
|
+
*/
|
|
8
|
+
export function cn(...inputs: ClassValue[]): string {
|
|
9
|
+
return twMerge(clsx(inputs))
|
|
10
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dicionário de mensagens de validação em pt-BR (zod errorMap).
|
|
3
|
+
*
|
|
4
|
+
* O zod emite mensagens em inglês por default ("String must contain at least…");
|
|
5
|
+
* este mapa traduz os códigos de issue pra microcopy da casa. Aplicado pelo
|
|
6
|
+
* useFormAction no zodResolver — todo ActionForm valida em pt-BR sem que cada
|
|
7
|
+
* schema precise declarar mensagem campo a campo (schema declara só a REGRA).
|
|
8
|
+
* Pra mensagem específica, o schema ainda pode passar a sua (ex.: .min(8, '…')),
|
|
9
|
+
* que tem precedência sobre o mapa.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod'
|
|
12
|
+
|
|
13
|
+
export const zodErrorMapPtBr: z.ZodErrorMap = (issue, ctx) => {
|
|
14
|
+
switch (issue.code) {
|
|
15
|
+
case z.ZodIssueCode.invalid_type:
|
|
16
|
+
if (issue.received === 'undefined' || issue.received === 'null') return { message: 'Obrigatório.' }
|
|
17
|
+
return { message: 'Valor inválido.' }
|
|
18
|
+
case z.ZodIssueCode.too_small:
|
|
19
|
+
if (issue.type === 'string') {
|
|
20
|
+
return { message: Number(issue.minimum) <= 1 ? 'Obrigatório.' : `Mínimo de ${issue.minimum} caracteres.` }
|
|
21
|
+
}
|
|
22
|
+
if (issue.type === 'array') return { message: `Selecione pelo menos ${issue.minimum}.` }
|
|
23
|
+
return { message: `Valor mínimo: ${issue.minimum}.` }
|
|
24
|
+
case z.ZodIssueCode.too_big:
|
|
25
|
+
if (issue.type === 'string') return { message: `Máximo de ${issue.maximum} caracteres.` }
|
|
26
|
+
if (issue.type === 'array') return { message: `Selecione no máximo ${issue.maximum}.` }
|
|
27
|
+
return { message: `Valor máximo: ${issue.maximum}.` }
|
|
28
|
+
case z.ZodIssueCode.invalid_string:
|
|
29
|
+
if (issue.validation === 'email') return { message: 'Email inválido.' }
|
|
30
|
+
if (issue.validation === 'url') return { message: 'URL inválida.' }
|
|
31
|
+
if (issue.validation === 'uuid') return { message: 'Identificador inválido.' }
|
|
32
|
+
return { message: 'Formato inválido.' }
|
|
33
|
+
case z.ZodIssueCode.invalid_enum_value:
|
|
34
|
+
return { message: 'Escolha uma das opções.' }
|
|
35
|
+
default:
|
|
36
|
+
return { message: ctx.defaultError }
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/ui/meta.ts
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @softize/opus/ui/meta — o mapa CENTRAL de `meta` de todos os componentes.
|
|
3
|
+
*
|
|
4
|
+
* O componente portado é byte-fiel ao shadcn (sem `meta` co-localizado, pra re-sincronizar
|
|
5
|
+
* limpo); o metadado de descoberta (whenToUse/ancestry) mora AQUI, fora do arquivo. Quem
|
|
6
|
+
* precisa do meta em RUNTIME — a doc de UI (DocBrowser), o manifesto, ferramentas — importa
|
|
7
|
+
* este subpath explicitamente (`@softize/opus/ui/meta`); o barrel de runtime (react.tsx) não
|
|
8
|
+
* o re-exporta de propósito (metadado de build-time, fora do bundle dos apps).
|
|
9
|
+
*/
|
|
10
|
+
import type { ComponentMeta } from './index.ts'
|
|
11
|
+
|
|
12
|
+
/** Todos os metas, chaveados pelo nome canônico (kebab). Fonte única — zero duplicação. */
|
|
13
|
+
export const componentMeta = {
|
|
14
|
+
'alert': {
|
|
15
|
+
name: 'alert',
|
|
16
|
+
ancestry: 'opus',
|
|
17
|
+
whenToUse:
|
|
18
|
+
'Aviso INLINE no fluxo da página (o erro do login, o "sincronizado" da lista) — `role="alert"`, então o leitor de tela anuncia sozinho. Forma CURTA (o caso comum): `<Alert title description icon variant />` numa linha; composição (`AlertTitle`/`AlertDescription`) só quando o conteúdo é rico (parágrafos, link, ação). `variant` default/destructive/success. Pra INTERROMPER cobrando decisão, use `confirm()`; pra recado passageiro, `toast`.',
|
|
19
|
+
},
|
|
20
|
+
'badge': {
|
|
21
|
+
name: 'badge',
|
|
22
|
+
ancestry: 'shadcn',
|
|
23
|
+
whenToUse:
|
|
24
|
+
'Rótulo curto de status/categoria. `variant` pra intenção (default/secondary/destructive/outline) ou tom de status (success/warning/info, fill tingido). Não-interativo — pra clique, use Button ou `asChild` num <a>.',
|
|
25
|
+
},
|
|
26
|
+
'button': {
|
|
27
|
+
name: 'button',
|
|
28
|
+
ancestry: 'shadcn',
|
|
29
|
+
whenToUse:
|
|
30
|
+
'Ação clicável. `variant` pra intenção (default/secondary/outline/ghost/destructive/link), `size` pro tamanho (default/sm/lg · icon/icon-sm/icon-xs pros só-ícone, 36/32/24px — o icon-xs é a ação DENTRO de um campo, o trailing do Input/Select). `busy` pra ação em andamento (spinner + desabilita — é o "depois" do clique, não o "carregando" de conteúdo, que é Spinner/Skeleton num nível de página). `asChild` pra renderizar como outro elemento (ex.: âncora com cara de botão).',
|
|
31
|
+
},
|
|
32
|
+
'card': {
|
|
33
|
+
name: 'card',
|
|
34
|
+
ancestry: 'shadcn',
|
|
35
|
+
whenToUse:
|
|
36
|
+
'Superfície da casa (bg-card + borda + rounded-lg, flat). Box simples: `<Card className="p-4">…</Card>`. Estruturado: Card > CardHeader(CardTitle/CardDescription) + CardContent + CardFooter — o padding mora nos slots (como o Dialog). Divergência declarada vs shadcn: rounded-lg, sem shadow, sem flex/gap forçados (o upstream brigava com card-box simples).',
|
|
37
|
+
},
|
|
38
|
+
'chat': {
|
|
39
|
+
name: 'chat',
|
|
40
|
+
ancestry: 'opus',
|
|
41
|
+
whenToUse:
|
|
42
|
+
'Chat da casa (lista de mensagens + composer) que gerencia a conversa por dentro (estado/loading/auto-scroll; Enter envia, Shift+Enter quebra linha). A inteligência vem da prop `send` — resposta inteira (Promise<string>) OU streaming (AsyncIterable<ChatEvent>: texto incremental, indicador vivo do tool, artefato via renderArtifact). No Opus, o backend liga em `runtime.aiFor(base).run(...)` ou `.runStream(...)`. Dê altura ao container (ex.: `h-full`).',
|
|
43
|
+
},
|
|
44
|
+
'checkbox': {
|
|
45
|
+
name: 'checkbox',
|
|
46
|
+
ancestry: 'shadcn',
|
|
47
|
+
whenToUse:
|
|
48
|
+
'Caixa de marcação booleana (Radix). Controlado por `checked`/`onCheckedChange`. Parear com Label. Pra escolha única de várias opções, use Select/RadioGroup.',
|
|
49
|
+
},
|
|
50
|
+
'icon-picker': {
|
|
51
|
+
name: 'icon-picker',
|
|
52
|
+
ancestry: 'opus',
|
|
53
|
+
whenToUse:
|
|
54
|
+
'Seletor de ícone: gatilho com o ícone corrente + lista buscável (mesma receita Popover+Command do Select buscável). O value é o NOME do ícone (kebab-case) — renderize com a mesma paleta (iconPickerIcons[name] ?? fallback). Paleta default curada (~40, lucide); vocabulário próprio via prop icons. Pra personalização de item criado pelo usuário (relatório, projeto, pasta).',
|
|
55
|
+
},
|
|
56
|
+
'command': {
|
|
57
|
+
name: 'command',
|
|
58
|
+
ancestry: 'shadcn',
|
|
59
|
+
whenToUse:
|
|
60
|
+
'Lista filtrável com teclado (cmdk) — base de command-palettes. Use CommandDialog pra palette modal (⌘K). Pra escolha simples, use o Select direto.',
|
|
61
|
+
},
|
|
62
|
+
'composer': {
|
|
63
|
+
name: 'composer',
|
|
64
|
+
ancestry: 'opus',
|
|
65
|
+
whenToUse:
|
|
66
|
+
'A caixa de escrever da casa (o composer do Chat, extraído): textarea numa pílula elevada, Enter envia / Shift+Enter quebra linha, enviar dentro. Use SOZINHO quando há entrada de texto mas não um chat — ex.: o composer de criação de sessão do Maestro (sem histórico). Com `actions`, ganha uma barra embaixo pra seletores discretos à esquerda (app, agente, contexto…) — o mesmo lugar onde o Maestro põe app/task e a GB poria o agente. Sem `actions`, é a linha única de sempre. Controlado (`value`/`onChange`/`onSubmit`); `submitDisabled` gateia além de vazio/busy. Pra um chat completo (mensagens + este composer), use Chat.',
|
|
67
|
+
},
|
|
68
|
+
'copyable': {
|
|
69
|
+
name: 'copyable',
|
|
70
|
+
ancestry: 'opus',
|
|
71
|
+
whenToUse:
|
|
72
|
+
'Clicar-pra-copiar com feedback: copia `value` pro clipboard e o ícone vira check por ~1.5s. Sem filhos é um botão-ícone (toolbar/célula); com filhos, o rótulo visível + o ícone. Pra IDs, tokens, slugs, URLs. No-op silencioso se o clipboard não existir (contexto inseguro/SSR).',
|
|
73
|
+
},
|
|
74
|
+
'dialog': {
|
|
75
|
+
name: 'dialog',
|
|
76
|
+
ancestry: 'shadcn',
|
|
77
|
+
whenToUse:
|
|
78
|
+
'Janela modal com overlay e trap de foco (Radix). O DialogContent é a superfície PURA (sem padding); o espaço mora nos slots: DialogHeader (fixo) > DialogTitle/Description, DialogBody (rola; opcional) e DialogFooter (faixa de ação; opcional). `showCloseButton={false}` no DialogContent esconde o "X" (modal que exige ação). Pra menu de ações, use Menu; pra ancorado sem modal, Popover.',
|
|
79
|
+
},
|
|
80
|
+
'input': {
|
|
81
|
+
name: 'input',
|
|
82
|
+
ancestry: 'shadcn',
|
|
83
|
+
whenToUse:
|
|
84
|
+
'Campo de texto de uma linha. Aceita todos os atributos nativos de <input> (type, placeholder, disabled). `icon` (ícone leading, identidade) e `trailing` (ação no fim — limpar, mostrar senha) são adornos por PROP, as mesmas do Select (adorno de campo é prop, não composição). Pra rótulo, parear com Label; pra addon rico (botão no fim, prefixo de texto, múltiplos), InputGroup.',
|
|
85
|
+
},
|
|
86
|
+
'label': {
|
|
87
|
+
name: 'label',
|
|
88
|
+
ancestry: 'shadcn',
|
|
89
|
+
whenToUse: 'Rótulo acessível de um campo. `htmlFor` aponta pro id do controle. Parear com Input/Textarea/Select.',
|
|
90
|
+
},
|
|
91
|
+
'markdown': {
|
|
92
|
+
name: 'markdown',
|
|
93
|
+
ancestry: 'opus',
|
|
94
|
+
whenToUse:
|
|
95
|
+
'Renderiza markdown como HTML semântico (motor markdown-it — o MESMO da doc; o parser de regex saiu em 7.1.0). `html: false`: tag no fonte é escapada, então serve pra texto de gente e de modelo. A tipografia vem do `prose` mapeado nos tokens da casa (theme.css) — não passe classe de tipografia por fora; o 1º/último bloco já não empurram a caixa em volta. Pra código com destaque e cópia, CodeBlock.',
|
|
96
|
+
},
|
|
97
|
+
'menu': {
|
|
98
|
+
name: 'menu',
|
|
99
|
+
ancestry: 'opus',
|
|
100
|
+
whenToUse:
|
|
101
|
+
'O menu de AÇÕES da casa: lista flutuante ancorada num gatilho (Radix DropdownMenu). Compõe Menu > MenuTrigger (`asChild` funde no Button) + MenuContent > (MenuItem, MenuCheckboxItem, MenuRadioGroup>MenuRadioItem, MenuLabel, MenuSeparator, MenuShortcut, MenuSub>MenuSubTrigger+MenuSubContent). `variant="destructive"` no item para a ação que apaga. É o único menu (5.0.0): ContextMenu, Menubar e NavigationMenu saíram — mesmo papel com outro gatilho, zero uso. Pra ESCOLHER um valor, use Select; pra busca por teclado, Command; pra conteúdo livre ancorado, Popover.',
|
|
102
|
+
},
|
|
103
|
+
'popover': {
|
|
104
|
+
name: 'popover',
|
|
105
|
+
ancestry: 'shadcn',
|
|
106
|
+
whenToUse:
|
|
107
|
+
'Painel flutuante ancorado num gatilho, sem modal (Radix). Pra conteúdo livre (form curto, detalhes). Use PopoverHeader/PopoverTitle/PopoverDescription pra estruturar. Pra lista de ações, use Menu; pra modal, Dialog.',
|
|
108
|
+
},
|
|
109
|
+
'select': {
|
|
110
|
+
name: 'select',
|
|
111
|
+
ancestry: 'opus',
|
|
112
|
+
whenToUse:
|
|
113
|
+
'O seletor ÚNICO da casa: toda escolha em lista. Data-driven (`options` = { value, label, hint?, content?, group? }), nunca JSX de item. Modos por prop, não por componente: `searchable` (busca; a régua é ACHABILIDADE — lista longa sem busca é rolar até cansar), `multiple` (chips + Selecionar tudo), `onSearch` (busca no servidor), `native` (o <select> do sistema — lista curta e sabida, picker do SO), `variant="ghost"` (a barra do composer, com `triggerLabel` encurtando o gatilho). Também: `icon` (leading), `trailing` (ação custom no fim do campo, ex.: um botão que age sobre o valor), `size` (default/sm), `clearable`. Absorveu Combobox, NativeSelect, ComposerSelect e o Select do Radix (3.0.0). Pra ação, Menu; pra 2–3 opções sempre visíveis, RadioGroup.',
|
|
114
|
+
},
|
|
115
|
+
'separator': {
|
|
116
|
+
name: 'separator',
|
|
117
|
+
ancestry: 'shadcn',
|
|
118
|
+
whenToUse: 'Linha divisória entre seções/itens (Radix). `orientation` horizontal|vertical. Decorativa por padrão (a11y).',
|
|
119
|
+
},
|
|
120
|
+
'skeleton': {
|
|
121
|
+
name: 'skeleton',
|
|
122
|
+
ancestry: 'shadcn',
|
|
123
|
+
whenToUse: 'Placeholder pulsante de carregamento. Dê o tamanho via className (h-4 w-32). Pra estado de loading antes do conteúdo chegar.',
|
|
124
|
+
},
|
|
125
|
+
'spinner': {
|
|
126
|
+
name: 'spinner',
|
|
127
|
+
ancestry: 'shadcn',
|
|
128
|
+
whenToUse: 'Loading girando (ação em andamento: botão, fetch). Pra placeholder com forma de conteúdo, use Skeleton.',
|
|
129
|
+
},
|
|
130
|
+
'table': {
|
|
131
|
+
name: 'table',
|
|
132
|
+
ancestry: 'shadcn',
|
|
133
|
+
whenToUse:
|
|
134
|
+
'Tabela de dados. Compõe Table > (TableHeader > TableRow > TableHead, TableBody > TableRow > TableCell). Vem SEM borda externa — o datagrid da casa é a Table num wrapper "overflow-hidden rounded-lg border border-border". Pra listagem tabular — pro pattern de search use ActionList (que já monta a tabela emoldurada do spec).',
|
|
135
|
+
},
|
|
136
|
+
'tabs': {
|
|
137
|
+
name: 'tabs',
|
|
138
|
+
ancestry: 'shadcn',
|
|
139
|
+
whenToUse:
|
|
140
|
+
'Abas pra alternar entre painéis de conteúdo (Radix). Compõe Tabs > (TabsList > TabsTrigger + TabsContent), pareando `value` do trigger com o do content. `variant` na TabsList: `default` (pill) ou `line` (barra sublinhada). `size` no Tabs (default h-9 / sm h-8 — o par do sm de Button/Select, pra fileira densa). `orientation` (horizontal/vertical).',
|
|
141
|
+
},
|
|
142
|
+
'textarea': {
|
|
143
|
+
name: 'textarea',
|
|
144
|
+
ancestry: 'shadcn',
|
|
145
|
+
whenToUse:
|
|
146
|
+
'Campo de texto multilinha. Aceita os atributos nativos de <textarea> (rows, placeholder, disabled). Cresce com o conteúdo via `field-sizing-content`.',
|
|
147
|
+
},
|
|
148
|
+
'toast': {
|
|
149
|
+
name: 'toast',
|
|
150
|
+
ancestry: 'shadcn',
|
|
151
|
+
whenToUse:
|
|
152
|
+
'Notificação efêmera (sonner). Monte `<Toaster />` 1x no root e dispare com `toast.success/error/message(...)`. Pra mensagem persistente inline, use Alert.',
|
|
153
|
+
},
|
|
154
|
+
'tooltip': {
|
|
155
|
+
name: 'tooltip',
|
|
156
|
+
ancestry: 'shadcn',
|
|
157
|
+
whenToUse:
|
|
158
|
+
'Dica curta no hover/foco de um elemento (Radix). Envolver a árvore num TooltipProvider. Só texto auxiliar — nunca pôr ação ou conteúdo essencial aqui.',
|
|
159
|
+
},
|
|
160
|
+
'truncate': {
|
|
161
|
+
name: 'truncate',
|
|
162
|
+
ancestry: 'opus',
|
|
163
|
+
whenToUse:
|
|
164
|
+
'Texto truncado com tooltip SÓ quando transborda (medição do overflow, re-medida em resize) — substitui a composição `block truncate` + `title` sempre presente, que mostra dica até em texto que não corta. `tooltip` sobrepõe o conteúdo da dica (default: os children). Requer TooltipProvider na raiz. Pra célula de tabela, nome de arquivo, URL — qualquer linha única que pode estourar.',
|
|
165
|
+
},
|
|
166
|
+
'accordion': {
|
|
167
|
+
name: 'accordion',
|
|
168
|
+
ancestry: 'shadcn',
|
|
169
|
+
whenToUse:
|
|
170
|
+
'Lista de seções empilhadas que abrem/fecham (Radix). `type` single (um painel por vez — combine com `collapsible` pra permitir fechar todos) ou multiple (vários abertos). Cada AccordionItem precisa de `value`; o chevron já vem no AccordionTrigger. Pra alternar conteúdo lado a lado, use Tabs; pra um único bloco recolhível solto, use Collapsible.',
|
|
171
|
+
},
|
|
172
|
+
'alert-dialog': {
|
|
173
|
+
name: 'alert-dialog',
|
|
174
|
+
ancestry: 'opus',
|
|
175
|
+
whenToUse:
|
|
176
|
+
'Diálogo modal que INTERROMPE pra cobrar decisão e NÃO fecha clicando fora (`role="alertdialog"`) — a confirmação destrutiva. Desenho ÚNICO e compacto (6.0.0: a prop `size` saiu, o largo não existe mais). Compõe AlertDialog > AlertDialogTrigger (asChild) + AlertDialogContent > AlertDialogHeader(AlertDialogMedia? + AlertDialogTitle/Description) + AlertDialogFooter(Cancel/Action). ATENÇÃO: Action e Cancel do Radix FECHAM ao clicar — pra ação async que só some no sucesso, use Button no footer. Na prática você quase nunca monta isto à mão: `confirm()` já faz, e o DeleteButton/ActionTrigger vêm prontos.',
|
|
177
|
+
},
|
|
178
|
+
'aspect-ratio': {
|
|
179
|
+
name: 'aspect-ratio',
|
|
180
|
+
ancestry: 'shadcn',
|
|
181
|
+
whenToUse:
|
|
182
|
+
'Trava a proporção de um bloco (Radix) — a largura vem do pai e a altura é derivada de `ratio` (16/9 pra vídeo/preview, 1 pra quadrado, 4/3 clássico). Use pra mídia, thumbnails e previews de worktree não pularem o layout enquanto carregam. Borda/rounded/overflow-hidden moram no AspectRatio; o filho preenche com h-full w-full object-cover. Pra largura fixa em si, é o contêiner que decide, não este componente.',
|
|
183
|
+
},
|
|
184
|
+
'avatar': {
|
|
185
|
+
name: 'avatar',
|
|
186
|
+
ancestry: 'shadcn',
|
|
187
|
+
whenToUse:
|
|
188
|
+
'Retrato de uma pessoa ou agente (Radix). AvatarImage (src/alt) + AvatarFallback (iniciais ou ícone) — o fallback cobre o carregamento e a falha da imagem. `size` sm/default/lg. AvatarBadge é o selo de status no canto (tinja o fundo). Pra a pilha de membros, envolva os Avatar num AvatarGroup e feche o excedente com AvatarGroupCount ("+N").',
|
|
189
|
+
},
|
|
190
|
+
'breadcrumb': {
|
|
191
|
+
name: 'breadcrumb',
|
|
192
|
+
ancestry: 'shadcn',
|
|
193
|
+
whenToUse:
|
|
194
|
+
'Trilha de navegação hierárquica (workspace → repositório → sessão): mostra onde o usuário está e o caminho de volta. `BreadcrumbLink` pros níveis navegáveis, `BreadcrumbPage` pro atual (não clicável), `BreadcrumbSeparator` entre eles e `BreadcrumbEllipsis` pra colapsar trilhas longas. Pra alternar painéis no mesmo nível, use Tabs.',
|
|
195
|
+
},
|
|
196
|
+
'button-group': {
|
|
197
|
+
name: 'button-group',
|
|
198
|
+
ancestry: 'shadcn',
|
|
199
|
+
whenToUse:
|
|
200
|
+
'Junta botões (e afins) num bloco coeso — bordas internas colapsadas e cantos arredondados só nas pontas. `orientation` horizontal/vertical. ButtonGroupText pra um rótulo/prefixo colado ao grupo; ButtonGroupSeparator pra cortar visualmente entre ações. Pra um menu de opções, prefira Menu; pra campo com addon, InputGroup.',
|
|
201
|
+
},
|
|
202
|
+
'calendar': {
|
|
203
|
+
name: 'calendar',
|
|
204
|
+
ancestry: 'shadcn',
|
|
205
|
+
whenToUse:
|
|
206
|
+
'Grade de datas (react-day-picker) pra escolher um dia ou um intervalo. `mode` define a seleção (single/multiple/range) e o formato de `selected`/`onSelect` (Date, Date[] ou { from, to }). `captionLayout="dropdown"` troca o título do mês por seletores de mês/ano (pular pra um período distante); `numberOfMonths` mostra meses lado a lado; `disabled` (Matcher) corta datas. Pra exibir num popover de campo, ancore no Popover.',
|
|
207
|
+
},
|
|
208
|
+
'carousel': {
|
|
209
|
+
name: 'carousel',
|
|
210
|
+
ancestry: 'shadcn',
|
|
211
|
+
whenToUse:
|
|
212
|
+
'Trilho de slides deslizáveis (embla). Compõe Carousel > CarouselContent > CarouselItem + CarouselPrevious/CarouselNext; o `basis` do item controla quantos cabem na vista. `orientation` (horizontal/vertical), `opts` repassa o embla (loop, align), `setApi` expõe a instância. Pra lista paginada de dados, use Table; pra navegação entre painéis, Tabs.',
|
|
213
|
+
},
|
|
214
|
+
'collapsible': {
|
|
215
|
+
name: 'collapsible',
|
|
216
|
+
ancestry: 'shadcn',
|
|
217
|
+
whenToUse:
|
|
218
|
+
'Seção que abre e fecha (Radix): um CollapsibleTrigger revela ou esconde o CollapsibleContent. Compõe Collapsible > (CollapsibleTrigger + CollapsibleContent) — o Trigger já é o `<button>`. `defaultOpen` pro modo não controlado; `open`/`onOpenChange` pro controlado (ex.: girar o chevron); `disabled` trava o gatilho. Pra alternar entre vários painéis, use Tabs; pra menu de ações ancorado, Menu.',
|
|
219
|
+
},
|
|
220
|
+
'drawer': {
|
|
221
|
+
name: 'drawer',
|
|
222
|
+
ancestry: 'opus',
|
|
223
|
+
whenToUse:
|
|
224
|
+
'O painel que desliza de uma borda da tela (Radix Dialog: overlay + trap de foco + ESC), pra detalhe/edição lateral sem trocar de tela. Compõe Drawer > DrawerTrigger + DrawerContent (side="right|left|top|bottom") > (DrawerHeader > DrawerTitle/DrawerDescription, conteúdo, DrawerFooter); DrawerClose fecha, `asChild` funde no Button. É o port do `sheet` do shadcn com o nome que o ecossistema React usa — o `drawer` do registry (vaul, com gesto de arrastar) foi removido em 4.0.0: mesmo papel, zero uso. Pra modal centrado, Dialog; pra menu de ações, Menu.',
|
|
225
|
+
},
|
|
226
|
+
'empty': {
|
|
227
|
+
name: 'empty',
|
|
228
|
+
ancestry: 'shadcn',
|
|
229
|
+
whenToUse:
|
|
230
|
+
'Estado vazio dentro de uma região: moldura tracejada centrada com `EmptyHeader` (mídia + `EmptyTitle` + `EmptyDescription`) e `EmptyContent` pras ações. `EmptyMedia variant` icon (quadrado muted) ou default (sem fundo). Pra erro inline use Alert; pra carregamento, Skeleton.',
|
|
231
|
+
},
|
|
232
|
+
'field': {
|
|
233
|
+
name: 'field',
|
|
234
|
+
ancestry: 'shadcn',
|
|
235
|
+
whenToUse:
|
|
236
|
+
'O esqueleto de um campo de formulário: rótulo, controle, descrição e erro compostos com espaçamento consistente. Field empilha (orientation vertical) ou põe o controle ao lado (horizontal/responsive, bom pra toggle); FieldLabel (htmlFor↔id), FieldDescription (ajuda) e FieldError (mensagem só quando há erro, ou uma lista de errors) preenchem. Agrupe campos relacionados num FieldSet > FieldLegend + FieldGroup, com FieldSeparator entre eles; FieldContent + FieldTitle dão o bloco texto quando o controle não é um <label>. É layout — o estado e a validação ficam no seu form (ou no ActionForm, que já monta tudo isto).',
|
|
237
|
+
},
|
|
238
|
+
'input-group': {
|
|
239
|
+
name: 'input-group',
|
|
240
|
+
ancestry: 'shadcn',
|
|
241
|
+
whenToUse:
|
|
242
|
+
'Campo composto: cola ícones, texto e botões a um InputGroupInput/InputGroupTextarea numa única moldura (foco e erro propagam pro grupo todo). InputGroupAddon ancora os adornos — `align` inline-start/inline-end nas laterais, block-start/block-end empilha acima/abaixo (exige a moldura alta do InputGroupTextarea, é o padrão do composer). InputGroupText é o rótulo inerte (prefixo/sufixo); InputGroupButton é o botão embutido (ghost por padrão, `size` xs/sm/icon-xs/icon-sm). Só um ÍCONE leading não precisa disto — é a prop `icon` do Input (uma forma só pra adorno de campo, igual ao Select). InputGroup é pros casos que a prop não cobre: botão no fim, prefixo de texto, adorno em bloco, múltiplos. Pra agrupar botões soltos, ButtonGroup.',
|
|
243
|
+
},
|
|
244
|
+
'input-otp': {
|
|
245
|
+
name: 'input-otp',
|
|
246
|
+
ancestry: 'shadcn',
|
|
247
|
+
whenToUse:
|
|
248
|
+
'Campo de código em casas (one-time password) montado sobre input-otp: InputOTP define `maxLength`, cada InputOTPSlot recebe seu `index`, InputOTPGroup agrupa as casas e InputOTPSeparator divide em blocos. Controle por `value`/`onChange`. Use pra confirmar acesso/2FA com código numérico; pra texto livre, use Input.',
|
|
249
|
+
},
|
|
250
|
+
'item': {
|
|
251
|
+
name: 'item',
|
|
252
|
+
ancestry: 'shadcn',
|
|
253
|
+
whenToUse:
|
|
254
|
+
'Linha de conteúdo composta — mídia + título/descrição + ações numa linha clicável ou estática. Item é o container (`variant` default/outline/muted, `size` default/sm, `asChild` pra virar link/botão); componha ItemMedia (`variant` icon/image), ItemContent (ItemTitle + ItemDescription), ItemActions e, na borda, ItemHeader/ItemFooter. Empilhe vários num ItemGroup separados por ItemSeparator — o padrão pra listas de workspaces, agentes, repositórios e sessões.',
|
|
255
|
+
},
|
|
256
|
+
'kbd': {
|
|
257
|
+
name: 'kbd',
|
|
258
|
+
ancestry: 'shadcn',
|
|
259
|
+
whenToUse:
|
|
260
|
+
'Tecla ou combinação de teclas num atalho (renderiza `<kbd>`). `Kbd` é uma tecla; envolva várias num `KbdGroup` pra formar o combo (ex.: ⌘ + K), com o conector ("+") como texto entre elas. Estiliza, não captura — o handler do atalho é seu. Aceita ícone (lucide) como filho. Dentro de um TooltipContent ganha o tom invertido automaticamente.',
|
|
261
|
+
},
|
|
262
|
+
'pagination': {
|
|
263
|
+
name: 'pagination',
|
|
264
|
+
ancestry: 'shadcn',
|
|
265
|
+
whenToUse:
|
|
266
|
+
'Navegação entre páginas montada por composição (shadcn): Pagination › PaginationContent › PaginationItem com PaginationLink, mais PaginationPrevious/PaginationNext e PaginationEllipsis. Marque a página atual com `isActive` no PaginationLink; os links são `<a>`, então use `href` pra navegação real ou `onClick` no modo controlado. Pra rolagem infinita ou listas curtas, dispense a barra.',
|
|
267
|
+
},
|
|
268
|
+
'progress': {
|
|
269
|
+
name: 'progress',
|
|
270
|
+
ancestry: 'shadcn',
|
|
271
|
+
whenToUse:
|
|
272
|
+
'Barra de progresso determinada (Radix): mostra o quanto de uma tarefa já foi feito num valor de 0 a 100 em `value`. Pra etapas de um processo conhecido — opus check, sincronização, cobertura. O preenchimento anima a cada mudança de `value`; sem `value` (ou null) fica vazia. Pra carga sem percentual (girando até chegar), use Spinner; pra placeholder com forma de conteúdo, Skeleton.',
|
|
273
|
+
},
|
|
274
|
+
'radio-group': {
|
|
275
|
+
name: 'radio-group',
|
|
276
|
+
ancestry: 'shadcn',
|
|
277
|
+
whenToUse:
|
|
278
|
+
'Escolha única entre opções mutuamente exclusivas, todas visíveis ao mesmo tempo (Radix). Cada RadioGroupItem tem um `value`; o item escolhido vira o `value` do RadioGroup, controlado por `value`/`onValueChange` (ou `defaultValue` no modo não controlado). Pareie cada item com um Label. Pra poucas opções que cabem na tela; com muitas, prefira Select; pra ligar/desligar um único item, Checkbox ou Switch.',
|
|
279
|
+
},
|
|
280
|
+
'resizable': {
|
|
281
|
+
name: 'resizable',
|
|
282
|
+
ancestry: 'shadcn',
|
|
283
|
+
whenToUse:
|
|
284
|
+
'Painéis redimensionáveis por arraste (react-resizable-panels). Compõe ResizablePanelGroup > ResizablePanel + ResizableHandle entre eles; `orientation` (horizontal/vertical) define a direção do arraste, `defaultSize`/`minSize`/`maxSize` (em %) limitam cada painel e `withHandle` desenha a pega na divisória. O grupo ocupa a altura do pai (h-full), então dê tamanho ao container. Pra alternar entre painéis sem dividir a vista, use Tabs.',
|
|
285
|
+
deprecated: { alternative: '`Split resizable` + `Pane`', since: 'próxima versão' },
|
|
286
|
+
},
|
|
287
|
+
'split': {
|
|
288
|
+
name: 'split',
|
|
289
|
+
ancestry: 'opus',
|
|
290
|
+
whenToUse:
|
|
291
|
+
'Divide uma área em panes em sequência horizontal ou vertical. Use `resizable` quando a pessoa deve ajustar a fronteira; o mesmo `<Split>` vira flex simples sem ele. Cada `<Pane>` declara tamanho inicial/mínimo e inset. É o mecanismo espacial para sidebar, conteúdo e rail — não use AppShell/SectionShell novos.',
|
|
292
|
+
},
|
|
293
|
+
'sidebar': {
|
|
294
|
+
name: 'sidebar',
|
|
295
|
+
ancestry: 'opus',
|
|
296
|
+
whenToUse:
|
|
297
|
+
'Chrome e navegação de uma coluna lateral, encaixada onde um Split decidir. `Sidebar` possui o colapso; `PaneHeader`, `PaneContent` e `PaneFooter` estruturam qualquer pane, e `SidebarNav`/`SidebarItem` apresentam a navegação. Serve tanto a barra global quanto uma nav contextual; em Split redimensionável passe `divider={false}` para não duplicar a divisória. `SidebarHeader`, `SidebarContent` e `SidebarFooter` são aliases deprecated.',
|
|
298
|
+
},
|
|
299
|
+
'scroll-area': {
|
|
300
|
+
name: 'scroll-area',
|
|
301
|
+
ancestry: 'shadcn',
|
|
302
|
+
whenToUse:
|
|
303
|
+
'Região rolável com barra estilizada da casa (Radix), no lugar da scrollbar do sistema. Dê altura (ou largura) ao ScrollArea via className e ponha o conteúdo dentro; a barra vertical já vem por padrão. Pra rolagem horizontal, acrescente `<ScrollBar orientation="horizontal" />` como filho. Pra a página inteira rolar, deixe o navegador cuidar — isto é pra um painel com altura fixa (lista de sessões, log, trilho de skills).',
|
|
304
|
+
},
|
|
305
|
+
'slider': {
|
|
306
|
+
name: 'slider',
|
|
307
|
+
ancestry: 'shadcn',
|
|
308
|
+
whenToUse:
|
|
309
|
+
'Controle de valor numa faixa contínua, arrastado pelo thumb (Radix). `min`/`max`/`step` delimitam a faixa; `value`/`onValueChange` controlam (array de números — `[n]` pra um thumb, `[a, b]` pra um intervalo) ou `defaultValue` no modo não controlado. `orientation` horizontal/vertical, `disabled` esmaece. Pra um número exato digitado, use Input type=number; pra ligar/desligar, Switch.',
|
|
310
|
+
},
|
|
311
|
+
'switch': {
|
|
312
|
+
name: 'switch',
|
|
313
|
+
ancestry: 'shadcn',
|
|
314
|
+
whenToUse:
|
|
315
|
+
'Liga/desliga imediato de uma preferência booleana (Radix). Controlado por `checked`/`onCheckedChange` (boolean) e em par com Label. Use pra estado que vale na hora (ativar agente, sincronizar); pra confirmar dentro de um formulário, prefira Checkbox.',
|
|
316
|
+
},
|
|
317
|
+
'toggle': {
|
|
318
|
+
name: 'toggle',
|
|
319
|
+
ancestry: 'shadcn',
|
|
320
|
+
whenToUse:
|
|
321
|
+
'Botão de duas posições — liga/desliga um estado in-loco, sem sair da tela (Radix). `variant` default (fundo só quando ativo) ou outline (com borda); `size` sm/default/lg. Controlado por `pressed`/`onPressedChange` (ou `defaultPressed` no modo não controlado); ótimo pra alternar uma opção numa toolbar (negrito, quebra de linha, modo somente-leitura). Pra um conjunto de toggles mutuamente exclusivos ou um grupo de formatação, use ToggleGroup; pra um booleano com rótulo num formulário, prefira Switch ou Checkbox.',
|
|
322
|
+
},
|
|
323
|
+
'toggle-group': {
|
|
324
|
+
name: 'toggle-group',
|
|
325
|
+
ancestry: 'shadcn',
|
|
326
|
+
whenToUse:
|
|
327
|
+
'Grupo de botões de alternância (Radix). `type` single (um ativo, value: string) ou multiple (vários, value: string[]). Controlado por `value`/`onValueChange`. `variant` default|outline, `size` default|sm|lg e `spacing` descem pros itens via contexto. Pra alternar a visão de uma seção ou montar uma barra de formatação. Pra escolha única num form com rótulos longos, RadioGroup.',
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
'confirm': {
|
|
331
|
+
name: 'dialog',
|
|
332
|
+
ancestry: 'opus',
|
|
333
|
+
whenToUse:
|
|
334
|
+
'O trio imperativo `dialog.alert` (Promise<void>, reconhecimento obrigatório) · `dialog.confirm` (Promise<boolean>, com slot `body` pra corpo próprio) · `dialog.prompt` (Promise<string|null>, um input). Superfície imperativa como o `toast`, mas que RESPONDE — exige `<DialogHost />` no shell (sem ele LANÇA, em vez de pendurar a promise). Namespace de propósito: `window.alert/confirm/prompt` são globais do browser e um import esquecido cai no nativo; `window.dialog` não existe. Por baixo é o AlertDialog (role=alertdialog, não fecha fora); fila de uma por vez. `confirm()`/`<ConfirmHost/>` seguem como aliases. Pra excluir por contrato, ActionTrigger; pra form de verdade, ActionFormDialog; mais de duas ações, componha o AlertDialog.',
|
|
335
|
+
},
|
|
336
|
+
'action-form': {
|
|
337
|
+
name: 'action-form',
|
|
338
|
+
ancestry: 'opus',
|
|
339
|
+
whenToUse:
|
|
340
|
+
'Form de uma FormAction do Opus — submit + validação + toast encapsulados. AUTO (sem children): campos auto-detectados do Zod na ordem do contrato. COMPOSIÇÃO (children): diagrame com <ActionFormField name/> — label/widget/erro/asterisco vêm do contrato, o layout é seu. Pra ação sem form, ActionTrigger.',
|
|
341
|
+
},
|
|
342
|
+
'action-form-dialog': {
|
|
343
|
+
name: 'action-form-dialog',
|
|
344
|
+
ancestry: 'opus',
|
|
345
|
+
whenToUse:
|
|
346
|
+
'ActionForm dentro de um Dialog (form em modal) — controla open/onOpenChange + title; fecha no sucesso. Aceita children (modo composição) como o ActionForm. Pra form inline numa página, use ActionForm direto.',
|
|
347
|
+
},
|
|
348
|
+
'action-form-card': {
|
|
349
|
+
name: 'action-form-card',
|
|
350
|
+
ancestry: 'opus',
|
|
351
|
+
whenToUse:
|
|
352
|
+
'O ActionForm dentro de um Card do Opus (header/conteúdo/rodapé) — pra estruturar uma seção da página como painel. Segue o padrão do Card (sem divisor nem faixa de modal, com o respiro do Card). Pra form em overlay, ActionFormDialog; pra form cru sem chrome, ActionForm direto.',
|
|
353
|
+
},
|
|
354
|
+
'action-list': {
|
|
355
|
+
name: 'action-list',
|
|
356
|
+
ancestry: 'opus',
|
|
357
|
+
whenToUse:
|
|
358
|
+
'A listagem padronizada de uma ListAction — DECLARATIVA pelo contrato: `columns` (tipos, sortable, hidden) vira a tabela (com column picker); `filters` vira a toolbar (inline + `advanced` em modal + chips); `periods` vira o controle de período (presets + Personalizado com calendário → from/to); `text` liga a busca (q, à direita); sort escreve `sort: chave:dir`. `views` = renderers alternativos (board/galeria/lista) com segment — mesma fonte e filtros. Paginação server-driven (limit/page → total no rodapé + pager) e `batch` = multi-seleção com `can` (elegibilidade por item governa checkbox, selecionar-todos e o run). Células via `cells`; URL sync com listParamsToState/listStateToParams. Pra detalhe de 1 recurso, ActionView.',
|
|
359
|
+
},
|
|
360
|
+
'action-trigger': {
|
|
361
|
+
name: 'action-trigger',
|
|
362
|
+
ancestry: 'opus',
|
|
363
|
+
whenToUse:
|
|
364
|
+
'Botão que dispara uma SimpleAction do Opus (sem form): assign, close, archive, excluir. Loading + toast + confirmação (do `action.confirm` do contrato, ou pela prop). `icon` faz o botão virar icon-only com tooltip — a ação que mora NO item (linha, card), sem vazar o clique pro item; `itemLabel` nomeia o alvo na pergunta. Erro de NEGÓCIO (conflict/validation/not_found) mostra a frase do servidor; o resto cai no rótulo do contrato, pra não vazar texto técnico. ATENÇÃO: contrato sem `confirm` dispara DIRETO — a confirmação de ação destrutiva se declara no contrato. Absorveu o DeleteButton (7.0.0). Pra mutação com campos, ActionForm.',
|
|
365
|
+
},
|
|
366
|
+
'action-view': {
|
|
367
|
+
name: 'action-view',
|
|
368
|
+
ancestry: 'opus',
|
|
369
|
+
whenToUse:
|
|
370
|
+
'Carrega e exibe 1 recurso de uma ViewAction do Opus — loading/error/empty encapsulados; o layout vem por children `(data, refetch) => nó` (render segue como alias). Pra listagem, ActionList.',
|
|
371
|
+
},
|
|
372
|
+
'page': {
|
|
373
|
+
name: 'page',
|
|
374
|
+
ancestry: 'opus',
|
|
375
|
+
whenToUse:
|
|
376
|
+
'O esqueleto de página do back-office: <main> + container de largura cheia (className="max-w-5xl" estreita e centra) + header com título, descrição e ação à direita (em geral o criar). Presentacional (a página agrega N fontes); o action-driven mora dentro. Pra listagem em modal, ActionListDialog.',
|
|
377
|
+
},
|
|
378
|
+
'app-shell': {
|
|
379
|
+
name: 'app-shell',
|
|
380
|
+
ancestry: 'opus',
|
|
381
|
+
whenToUse:
|
|
382
|
+
'O chrome da aplicação: sidebar de navegação (header + nav rolável + rodapé ancorado) + conteúdo, com rail opcional à direita (chat de agente, inspetor) em painéis redimensionáveis. É o quadro — o que vai em cada slot é do app. Pro esqueleto de UMA página (título, descrição, ação), Page. Chrome `flush` opcional (full-bleed, filete à esquerda) com <AppShellBar> — a faixa h-12 border-b usada na sidebar (marca) e no topo do conteúdo pra linha do header atravessar a tela. Sidebar recolhível com `collapsible` (opt-in): o shell controla a largura e publica `data-collapsed` no aside; o rótulo some por CSS (`group-data-[collapsed=true]/sidebar:hidden`) porque o nav é do app. O botão é o <AppShellTrigger />, posicionado pelo consumidor; `useAppShell()` dá o estado em JS.',
|
|
383
|
+
deprecated: { alternative: '`Split` + `Pane` + `Sidebar`', since: 'próxima versão' },
|
|
384
|
+
},
|
|
385
|
+
'section-shell': {
|
|
386
|
+
name: 'section-shell',
|
|
387
|
+
ancestry: 'opus',
|
|
388
|
+
whenToUse:
|
|
389
|
+
'Uma SEÇÃO com navegação própria — o nível entre AppShell (o app) e Page (uma tela): nav w-56 com filete + painel, pras telas irmãs de Configurações, Relatórios ou doc. Use quando a seção é visitada raro e navegada por dentro quando visitada (não vale queimar item na sidebar do app), ou quando a lista é dinâmica demais pra um nav estático. Pra facetas do MESMO objeto, Tabs. Controlado (activeId + onSelect): o roteamento é do app. Grupos aceitam `items` e/ou `subgroups` (o 3º nível, ex.: sub-pasta na doc). O painel remonta quando o `activeId` muda — é o que zera o scroll; `scrollResetKey` sobrescreve a chave nos dois sentidos: constante = nunca remonta (preserva o scroll), mais fina que o activeId = remonta também dentro da mesma tela.',
|
|
390
|
+
deprecated: { alternative: '`Split` + `Pane` + `Sidebar`', since: 'próxima versão' },
|
|
391
|
+
},
|
|
392
|
+
'router': {
|
|
393
|
+
name: 'router',
|
|
394
|
+
ancestry: 'opus',
|
|
395
|
+
whenToUse:
|
|
396
|
+
'Roteamento history-based sem dependência: `usePathname`/`useSegments`/`useSearchParams` (leitura reativa da URL) + `navigate(path, { replace? })`. O pathname É o estado, então deep-link, reload e o botão voltar funcionam sem um segundo lugar guardando "onde estou". Escopo PEQUENO de propósito: não há tabela de rotas, params tipados nem data loader — quem decide o que renderizar é o app, com if/switch sobre os segmentos. Precisa casar padrão (`/users/:id/posts/:postId`) ou carregar dado por rota? O caso pede uma biblioteca de rotas, não isto. `navigate` é no-op em destino igual (senão o "voltar" não sai do lugar) e usa useSyncExternalStore (useState sofre tearing em concurrent).',
|
|
397
|
+
},
|
|
398
|
+
'data-state': {
|
|
399
|
+
name: 'data-state',
|
|
400
|
+
ancestry: 'opus',
|
|
401
|
+
whenToUse:
|
|
402
|
+
'O estado "carregando" (ANTES do conteúdo): orquestra erro/carregando/vazio/conteúdo de uma carga assíncrona num só lugar — Spinner centralizado no loading, texto no vazio, aviso calmo no erro. Pra "processando" (ação em andamento DEPOIS do clique), use o `busy` do Button. Pra placeholder com forma, Skeleton.',
|
|
403
|
+
},
|
|
404
|
+
'action-list-dialog': {
|
|
405
|
+
name: 'action-list-dialog',
|
|
406
|
+
ancestry: 'opus',
|
|
407
|
+
whenToUse:
|
|
408
|
+
'Uma ListAction em modal: lista query-backed (busca no mount, refaz via invalidates) + chrome padronizado (título/descrição, toolbar com nota "N no total" derivada + ação de criar). Children (items, refetch) diagrama os itens. `loading` agrega a query irmã; `empty` sobrepõe o vazio derivado (ex.: form inline aberto). Pra tabela numa página, ActionList; pra form em modal, ActionFormDialog.',
|
|
409
|
+
},
|
|
410
|
+
} as const satisfies Record<string, ComponentMeta>
|
|
411
|
+
|
|
412
|
+
export type ComponentMetaKey = keyof typeof componentMeta
|