@softize/opus 12.9.0 → 12.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/bin/lib/check.mjs +1103 -310
  3. package/bin/lib/copy.mjs +74 -6
  4. package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
  5. package/docs/adr/0004-page-content-state-is-composed.md +65 -0
  6. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +97 -0
  7. package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
  8. package/docs/code-style.md +6 -2
  9. package/package.json +1 -1
  10. package/registry/instructions/opus.md +5 -0
  11. package/registry/skills/build-opus-ui/SKILL.md +27 -16
  12. package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
  13. package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
  14. package/registry/skills/model-opus-dictionary/SKILL.md +4 -2
  15. package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
  16. package/registry/templates/app/src/App.tsx +1 -1
  17. package/src/core/dictionary.ts +52 -14
  18. package/src/core/index.ts +10 -0
  19. package/src/core/ui-context.ts +29 -0
  20. package/src/schema/drivers/zod.ts +34 -19
  21. package/src/ui/components/patterns/action-form-card.tsx +18 -12
  22. package/src/ui/components/patterns/confirm.tsx +26 -3
  23. package/src/ui/components/patterns/content-header.tsx +335 -61
  24. package/src/ui/components/patterns/data-state.tsx +23 -10
  25. package/src/ui/components/patterns/form.tsx +1 -1
  26. package/src/ui/components/patterns/list.tsx +1096 -777
  27. package/src/ui/components/patterns/page-state.tsx +115 -0
  28. package/src/ui/components/patterns/page.tsx +231 -41
  29. package/src/ui/components/patterns/sidebar.tsx +354 -80
  30. package/src/ui/components/patterns/trigger.tsx +13 -9
  31. package/src/ui/components/patterns/view.tsx +7 -11
  32. package/src/ui/components/primitives/alert-dialog.tsx +7 -5
  33. package/src/ui/components/primitives/alert.tsx +298 -80
  34. package/src/ui/components/primitives/ask.tsx +2 -1
  35. package/src/ui/components/primitives/badge.tsx +91 -30
  36. package/src/ui/components/primitives/button.tsx +99 -60
  37. package/src/ui/components/primitives/calendar.tsx +39 -39
  38. package/src/ui/components/primitives/card.tsx +96 -23
  39. package/src/ui/components/primitives/detail.tsx +2 -2
  40. package/src/ui/components/primitives/dictionary-value.tsx +9 -14
  41. package/src/ui/components/primitives/dot.tsx +74 -21
  42. package/src/ui/components/primitives/drawer.tsx +33 -20
  43. package/src/ui/components/primitives/field.tsx +4 -4
  44. package/src/ui/components/primitives/item.tsx +137 -81
  45. package/src/ui/components/primitives/menu.tsx +11 -3
  46. package/src/ui/components/primitives/metric-card.tsx +133 -0
  47. package/src/ui/components/primitives/table.tsx +2 -2
  48. package/src/ui/components/primitives/tooltip.tsx +1 -1
  49. package/src/ui/docs/DocBrowser.tsx +3 -3
  50. package/src/ui/docs/changelog.tsx +1 -1
  51. package/src/ui/docs/content/action-form-card.md +1 -1
  52. package/src/ui/docs/content/alert-dialog.md +8 -8
  53. package/src/ui/docs/content/alert.md +54 -23
  54. package/src/ui/docs/content/badge.md +18 -19
  55. package/src/ui/docs/content/button.md +12 -9
  56. package/src/ui/docs/content/card.md +5 -5
  57. package/src/ui/docs/content/content.md +44 -0
  58. package/src/ui/docs/content/customization.md +2 -2
  59. package/src/ui/docs/content/detail.md +5 -2
  60. package/src/ui/docs/content/dialog.md +2 -2
  61. package/src/ui/docs/content/dictionary-value.md +11 -10
  62. package/src/ui/docs/content/dot.md +7 -7
  63. package/src/ui/docs/content/drawer.md +6 -3
  64. package/src/ui/docs/content/field.md +1 -1
  65. package/src/ui/docs/content/input-group.md +3 -2
  66. package/src/ui/docs/content/item.md +47 -21
  67. package/src/ui/docs/content/menu.md +5 -4
  68. package/src/ui/docs/content/metric-card.md +41 -0
  69. package/src/ui/docs/content/page-state.md +45 -0
  70. package/src/ui/docs/content/page.md +48 -10
  71. package/src/ui/docs/content/semantic-context.md +63 -0
  72. package/src/ui/docs/content/sidebar.md +4 -4
  73. package/src/ui/docs/content/skeleton.md +2 -2
  74. package/src/ui/docs/content/table.md +3 -3
  75. package/src/ui/docs/content/tokens.md +28 -0
  76. package/src/ui/docs/content/tooltip.md +15 -1
  77. package/src/ui/docs/doc-client.tsx +2 -2
  78. package/src/ui/docs/registry.tsx +596 -228
  79. package/src/ui/lib/semantic-context.ts +30 -0
  80. package/src/ui/meta.ts +292 -270
  81. package/src/ui/react.tsx +378 -111
  82. package/src/ui/theme.css +66 -0
package/src/ui/meta.ts CHANGED
@@ -7,427 +7,449 @@
7
7
  * este subpath explicitamente (`@softize/opus/ui/meta`); o barrel de runtime (react.tsx) não
8
8
  * o re-exporta de propósito (metadado de build-time, fora do bundle dos apps).
9
9
  */
10
- import type { ComponentMeta } from './index.ts'
10
+ import type { ComponentMeta } from "./index.ts";
11
11
 
12
12
  /** Todos os metas, chaveados pelo nome canônico (kebab). Fonte única — zero duplicação. */
13
13
  export const componentMeta = {
14
- 'ask': {
15
- name: 'ask',
16
- ancestry: 'opus',
14
+ content: {
15
+ name: "content",
16
+ ancestry: "opus",
17
17
  whenToUse:
18
- 'Elicitação estruturada controlada para 1–4 perguntas `AskQuestion`: opções single/multi em pills, texto livre opcional, validação e submit de `AskAnswer[]`. Não faz transporte, persistência nem integração automática com ChatEvent; o consumidor controla `answers`/`onChange` e conecta `onSubmit` ao canal apropriado.',
18
+ "Região semântica nomeada dentro de uma página ou outra superfície. O shorthand cria ContentHeader e ContentBody; a forma explícita compõe Content > ContentHeader(ContentTitle/ContentDescription/ContentMeta/ContentActions) + ContentBody. ContentHeader nunca fica solto. Para o cabeçalho principal, use Page.",
19
19
  },
20
- 'alert': {
21
- name: 'alert',
22
- ancestry: 'opus',
20
+ ask: {
21
+ name: "ask",
22
+ ancestry: "opus",
23
23
  whenToUse:
24
- '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`) 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`.',
24
+ "Elicitação estruturada controlada para 1–4 perguntas `AskQuestion`: opções single/multi em pills, texto livre opcional, validação e submit de `AskAnswer[]`. Não faz transporte, persistência nem integração automática com ChatEvent; o consumidor controla `answers`/`onChange` e conecta `onSubmit` ao canal apropriado.",
25
25
  },
26
- 'badge': {
27
- name: 'badge',
28
- ancestry: 'shadcn',
26
+ alert: {
27
+ name: "alert",
28
+ ancestry: "opus",
29
29
  whenToUse:
30
- 'Rótulo curto de status/categoria. `variant` pra intenção (default/secondary/destructive/outline) ou tom de status (success/warning/info/danger, fill tingido). Não-interativo pra clique, use Button ou `asChild` num <a>. Valor de dicionário com papel declarado usa DictionaryValue em vez de escolher a variante à mão.',
30
+ "Aviso inline no fluxo da página — `context` declara neutral/info/success/warning/danger e `variant` escolhe subtle/outline (ADR 0006). Forma curta: `<Alert title description icon context />`; para conteúdo rico, componha AlertMedia + AlertHeader (AlertTitle e AlertDescription) + AlertActions. A mídia acompanha a altura útil do header. O texto comunica o significado sem depender da cor. Para interromper cobrando decisão, use `dialog.confirm()`; para recado passageiro, `toast`.",
31
31
  },
32
- 'empty-value': {
33
- name: 'empty-value',
34
- ancestry: 'opus',
32
+ badge: {
33
+ name: "badge",
34
+ ancestry: "shadcn",
35
35
  whenToUse:
36
- 'Representação padrão de valor ausente (`null`, `undefined`, string vazia ou só espaços): em célula compacta (`compact`) mostra o travessão e reserva “Não informado” à leitura assistiva; em texto corrido mostra o rótulo. `label` troca o significado da ausência no domínio (“Nunca enviado”, “Sem vencimento”). `0` e `false` são valores, nunca ausência. Colunas de ActionList e DetailField a usam; renderer customizado reutiliza em vez de repetir a condicional.',
36
+ "Rótulo curto de status/categoria. `context` declara neutral/primary/info/success/warning/danger; `variant` escolhe solid/subtle/outline (ADR 0006). Não-interativo para clique, use Button ou `asChild` num <a>. Valor de dicionário com papel declarado usa DictionaryValue em vez de escolher contexto ou variante na tela.",
37
37
  },
38
- 'dictionary-value': {
39
- name: 'dictionary-value',
40
- ancestry: 'opus',
38
+ "empty-value": {
39
+ name: "empty-value",
40
+ ancestry: "opus",
41
41
  whenToUse:
42
- 'Valor de um `t.dict` com a apresentação que o dicionário declarou (ADR 0003): `classification` vira Badge outline, `status`/`stage` viram Badge tonal pelo `tone` da entrada, `plain` ou sem papel vira texto. Ícone quando a entrada declara `icon` do catálogo; tooltip quando `description` acrescenta ao rótulo. O texto está sempre presente. Overrides explícitos por `presentation`, `variant`, `icon`, `tooltip` e `fallback`; não inferir badge, cor ou ícone a partir do nome do dicionário. Colunas de ActionList cujo campo é dicionário usam este renderer.',
42
+ "Representação padrão de valor ausente (`null`, `undefined`, string vazia ou espaços): em célula compacta (`compact`) mostra o travessão e reserva “Não informado” à leitura assistiva; em texto corrido mostra o rótulo. `label` troca o significado da ausência no domínio (“Nunca enviado”, “Sem vencimento”). `0` e `false` são valores, nunca ausência. Colunas de ActionList e DetailField a usam; renderer customizado reutiliza em vez de repetir a condicional.",
43
43
  },
44
- 'dot': {
45
- name: 'dot',
46
- ancestry: 'opus',
44
+ "dictionary-value": {
45
+ name: "dictionary-value",
46
+ ancestry: "opus",
47
47
  whenToUse:
48
- 'Sinal compacto de estado quando o contexto ou o nome acessível explica o significado. `variant` segue a intenção semântica (default/secondary/destructive/outline/success/warning/info). Passe `label` quando a cor comunicar informação; sem label, o ponto é decorativo. Para texto visível, use Badge.',
48
+ "Valor de um `t.dict` com apresentação declarada: `classification` vira Badge neutral/outline, `status` e `stage` viram Badge context/subtle, `plain` vira texto (ADRs 0003 e 0006). Ícone e tooltip vêm da metadata e o texto permanece presente. Overrides explícitos por `presentation`, `context`, `variant`, `icon`, `tooltip` e `fallback`; não inferir contexto, badge, cor ou ícone pelo nome do dicionário.",
49
49
  },
50
- 'detail': {
51
- name: 'detail',
52
- ancestry: 'opus',
50
+ dot: {
51
+ name: "dot",
52
+ ancestry: "opus",
53
53
  whenToUse:
54
- 'Dados somente leitura em pares chave/valor. Compõe DetailGroup > DetailField com semântica de lista de definições; `columns` controla a grade, `orientation` posiciona chave e valor, `variant="framed"` aplica moldura e `dividers` acrescenta somente as divisórias internas. Para entrada, validação e erro, use Field; para uma coleção pesquisável, use ActionList.',
54
+ "Sinal compacto de estado. `context` declara neutral/primary/info/success/warning/danger e `variant` escolhe solid/outline. Passe `label` quando a cor reforçar informação; sem label, o ponto é decorativo. Para texto visível, use Badge.",
55
55
  },
56
- 'dock': {
57
- name: 'dock',
58
- ancestry: 'opus',
56
+ detail: {
57
+ name: "detail",
58
+ ancestry: "opus",
59
59
  whenToUse:
60
- 'Barra de ferramentas ancorada a uma superfície de trabalho canvas, editor, preview —, quando um cabeçalho empilharia mais uma faixa de chrome sobre a trilha. Compõe Dock > DockGroup > DockAction; o estado da superfície (salvamento, versão) fica no SurfaceStatus, que flutua num canto e não pertence à barra. `position` escolhe a aresta; a divisória entre grupos é do componente. Requer TooltipProvider na raiz. Para ações de uma PÁGINA, use as `actions` do Page; para um conjunto de toggles exclusivos, ToggleGroup.',
60
+ 'Dados somente leitura em pares chave/valor. Compõe DetailGroup > DetailField com semântica de lista de definições; `columns` controla a grade, `orientation` posiciona chave e valor e alinha uma coluna compartilhada de rótulos no modo horizontal, `variant="framed"` aplica moldura e `dividers` acrescenta somente as divisórias internas. Para entrada, validação e erro, use Field; para uma coleção pesquisável, use ActionList.',
61
61
  },
62
- 'button': {
63
- name: 'button',
64
- ancestry: 'shadcn',
62
+ dock: {
63
+ name: "dock",
64
+ ancestry: "opus",
65
65
  whenToUse:
66
- 'Ação clicável. `variant` define intenção (default/secondary/outline/ghost/destructive/link), `size` define altura (default/sm/lg · icon/icon-sm/icon-xs para os casos só-ícone, 2.25/2/1.5rem) e `shape="pill"` troca somente a geometria, sem alterar cor ou hierarquia. `busy` mostra spinner e desabilita. `asChild` renderiza como outro elemento.',
66
+ "Barra de ferramentas ancorada a uma superfície de trabalho — canvas, editor, preview —, quando um cabeçalho empilharia mais uma faixa de chrome sobre a trilha. Compõe Dock > DockGroup > DockAction; o estado da superfície (salvamento, versão) fica no SurfaceStatus, que flutua num canto e não pertence à barra. `position` escolhe a aresta; a divisória entre grupos é do componente. Requer TooltipProvider na raiz. Para ações de uma PÁGINA, use as `actions` do Page; para um conjunto de toggles exclusivos, ToggleGroup.",
67
67
  },
68
- 'card': {
69
- name: 'card',
70
- ancestry: 'shadcn',
68
+ button: {
69
+ name: "button",
70
+ ancestry: "shadcn",
71
71
  whenToUse:
72
- 'Superfície da casa (bg-card + text-card-foreground + borda + rounded-xl, 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: sem shadow e sem flex/gap forçados (o upstream brigava com card-box simples).',
72
+ 'Ação clicável. `context` declara neutral/primary/danger e `variant` escolhe solid/subtle/outline/ghost/link (ADR 0006). `size` define altura (default/sm/lg · icon/icon-sm/icon-xs) e `shape="pill"` troca somente a geometria. `busy` mostra spinner e desabilita. Ação destrutiva usa `context="danger"`; `destructive` continua sendo metadata comportamental do contrato.',
73
73
  },
74
- 'chat': {
75
- name: 'chat',
76
- ancestry: 'opus',
74
+ card: {
75
+ name: "card",
76
+ ancestry: "shadcn",
77
77
  whenToUse:
78
- '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(...)`. Estado vazio por `greeting` (frase) ou `empty` (nó composto com os slots de Empty). altura ao container (ex.: `h-full`).',
78
+ 'Superfície da casa (bg-card + text-card-foreground + borda + rounded-xl, flat). Box simples: `<Card className="p-4">…</Card>`. Estruturado: Card > CardHeader(CardTitle/CardDescription) + CardBody + CardFooter o padding mora nos slots (como o Dialog). `CardContent` é um alias temporário de compatibilidade. Divergência declarada vs shadcn: sem shadow e sem flex/gap forçados (o upstream brigava com card-box simples).',
79
79
  },
80
- 'checkbox': {
81
- name: 'checkbox',
82
- ancestry: 'shadcn',
80
+ "metric-card": {
81
+ name: "metric-card",
82
+ ancestry: "opus",
83
83
  whenToUse:
84
- 'Caixa de marcação booleana (Radix). Controlado por `checked`/`onCheckedChange`. Parear com Label. Pra escolha única de várias opções, use Select/RadioGroup.',
84
+ "Medida resumida com rótulo, valor em destaque e descrição opcional. `context` realça semanticamente somente o ícone; aceita ação relacionada e estado de carregamento. O consumidor calcula e formata o valor; para conteúdo geral ou estrutura livre, use Card.",
85
85
  },
86
- 'icon-picker': {
87
- name: 'icon-picker',
88
- ancestry: 'opus',
86
+ chat: {
87
+ name: "chat",
88
+ ancestry: "opus",
89
89
  whenToUse:
90
- '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).',
90
+ "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(...)`. Estado vazio por `greeting` (frase) ou `empty` (nó composto com os slots de Empty). altura ao container (ex.: `h-full`).",
91
91
  },
92
- 'command': {
93
- name: 'command',
94
- ancestry: 'shadcn',
92
+ checkbox: {
93
+ name: "checkbox",
94
+ ancestry: "shadcn",
95
95
  whenToUse:
96
- 'Lista filtrável com teclado (cmdk) — base de command-palettes. Use CommandDialog pra palette modal (⌘K). Pra escolha simples, use o Select direto.',
96
+ "Caixa de marcação booleana (Radix). Controlado por `checked`/`onCheckedChange`. Parear com Label. Pra escolha única de várias opções, use Select/RadioGroup.",
97
97
  },
98
- 'composer': {
99
- name: 'composer',
100
- ancestry: 'opus',
98
+ "icon-picker": {
99
+ name: "icon-picker",
100
+ ancestry: "opus",
101
101
  whenToUse:
102
- '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.',
102
+ "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).",
103
103
  },
104
- 'content-header': {
105
- name: 'content-header',
106
- ancestry: 'opus',
104
+ command: {
105
+ name: "command",
106
+ ancestry: "shadcn",
107
107
  whenToUse:
108
- 'Cabeçalho semântico de uma seção de conteúdo: título, descrição, meta e ações na mesma composição. Use `variant="section"` dentro de páginas e painéis; `Page` fornece o cabeçalho principal e não deve receber outro ContentHeader equivalente. O `level` controla a hierarquia do heading sem alterar a hierarquia visual.',
108
+ "Lista filtrável com teclado (cmdk) base de command-palettes. Use CommandDialog pra palette modal (⌘K). Pra escolha simples, use o Select direto.",
109
109
  },
110
- 'copyable': {
111
- name: 'copyable',
112
- ancestry: 'opus',
110
+ composer: {
111
+ name: "composer",
112
+ ancestry: "opus",
113
113
  whenToUse:
114
- '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).',
114
+ "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.",
115
115
  },
116
- 'dialog': {
117
- name: 'dialog',
118
- ancestry: 'shadcn',
116
+ "content-header": {
117
+ name: "content-header",
118
+ ancestry: "opus",
119
+ whenToUse:
120
+ "Header estrutural de Content: agrupa ContentTitle, ContentDescription, ContentMeta e ContentActions. Não use solto. Para o caso comum, declare title, description, meta e actions diretamente em Content; o level controla a hierarquia semântica do heading.",
121
+ },
122
+ copyable: {
123
+ name: "copyable",
124
+ ancestry: "opus",
125
+ whenToUse:
126
+ "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).",
127
+ },
128
+ dialog: {
129
+ name: "dialog",
130
+ ancestry: "shadcn",
119
131
  whenToUse:
120
132
  '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.',
121
133
  },
122
- 'input': {
123
- name: 'input',
124
- ancestry: 'shadcn',
134
+ input: {
135
+ name: "input",
136
+ ancestry: "shadcn",
125
137
  whenToUse:
126
- '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.',
138
+ "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.",
127
139
  },
128
- 'label': {
129
- name: 'label',
130
- ancestry: 'shadcn',
131
- whenToUse: 'Rótulo acessível de um campo. `htmlFor` aponta pro id do controle. Parear com Input/Textarea/Select.',
140
+ label: {
141
+ name: "label",
142
+ ancestry: "shadcn",
143
+ whenToUse:
144
+ "Rótulo acessível de um campo. `htmlFor` aponta pro id do controle. Parear com Input/Textarea/Select.",
132
145
  },
133
- 'markdown': {
134
- name: 'markdown',
135
- ancestry: 'opus',
146
+ markdown: {
147
+ name: "markdown",
148
+ ancestry: "opus",
136
149
  whenToUse:
137
- '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.',
150
+ "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.",
138
151
  },
139
- 'menu': {
140
- name: 'menu',
141
- ancestry: 'opus',
152
+ menu: {
153
+ name: "menu",
154
+ ancestry: "opus",
142
155
  whenToUse:
143
- '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.',
156
+ 'O menu de ações da casa: lista flutuante ancorada num gatilho (Radix DropdownMenu). Compõe Menu > MenuTrigger + MenuContent e seus itens. `context="danger"` sinaliza item com consequência perigosa; `destructive` permanece alias de migração. Para escolher um valor, use Select; para busca por teclado, Command; para conteúdo livre ancorado, Popover.',
144
157
  },
145
- 'popover': {
146
- name: 'popover',
147
- ancestry: 'shadcn',
158
+ popover: {
159
+ name: "popover",
160
+ ancestry: "shadcn",
148
161
  whenToUse:
149
- '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.',
162
+ "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.",
150
163
  },
151
- 'select': {
152
- name: 'select',
153
- ancestry: 'opus',
164
+ select: {
165
+ name: "select",
166
+ ancestry: "opus",
154
167
  whenToUse:
155
168
  'Seletor para escolhas em lista. Recebe `options` no formato { value, label, hint?, content?, group? }, sem JSX por item. Os modos são definidos por `searchable`, `multiple`, `onSearch` e `native`. `variant="default"` funciona como campo de formulário; `outline` envolve o conteúdo com borda; `ghost` o envolve sem borda. `size` segue a régua inline (default 2.25rem/sm 2rem), e `shape="pill"` altera somente a geometria. Também aceita `icon`, `trailing`, `triggerLabel` e `clearable`.',
156
169
  },
157
- 'separator': {
158
- name: 'separator',
159
- ancestry: 'shadcn',
160
- whenToUse: 'Linha divisória entre seções/itens (Radix). `orientation` horizontal|vertical. Decorativa por padrão (a11y).',
170
+ separator: {
171
+ name: "separator",
172
+ ancestry: "shadcn",
173
+ whenToUse:
174
+ "Linha divisória entre seções/itens (Radix). `orientation` horizontal|vertical. Decorativa por padrão (a11y).",
161
175
  },
162
- 'skeleton': {
163
- name: 'skeleton',
164
- ancestry: 'shadcn',
165
- whenToUse: 'Placeholder pulsante de carregamento. Dê o tamanho via className (h-4 w-32). Pra estado de loading antes do conteúdo chegar.',
176
+ skeleton: {
177
+ name: "skeleton",
178
+ ancestry: "shadcn",
179
+ whenToUse:
180
+ "Placeholder pulsante de carregamento. Dê o tamanho via className (h-4 w-32). Pra estado de loading antes do conteúdo chegar.",
166
181
  },
167
- 'spinner': {
168
- name: 'spinner',
169
- ancestry: 'shadcn',
170
- whenToUse: 'Loading girando (ação em andamento: botão, fetch). Pra placeholder com forma de conteúdo, use Skeleton.',
182
+ spinner: {
183
+ name: "spinner",
184
+ ancestry: "shadcn",
185
+ whenToUse:
186
+ "Loading girando (ação em andamento: botão, fetch). Pra placeholder com forma de conteúdo, use Skeleton.",
171
187
  },
172
- 'table': {
173
- name: 'table',
174
- ancestry: 'shadcn',
188
+ table: {
189
+ name: "table",
190
+ ancestry: "shadcn",
175
191
  whenToUse:
176
192
  'Tabela de dados. Compõe Table > (TableHeader > TableRow > TableHead, TableBody > TableRow > TableCell). `variant="plain"` vem sem borda externa; `variant="framed"` aplica a moldura canônica, recorta o scroll e destaca o cabeçalho. Pra listagem tabular — pro pattern de search use ActionList.',
177
193
  },
178
- 'tabs': {
179
- name: 'tabs',
180
- ancestry: 'shadcn',
194
+ tabs: {
195
+ name: "tabs",
196
+ ancestry: "shadcn",
181
197
  whenToUse:
182
- '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, ancorada na borda da lista). `size` no Tabs (default h-9 / sm h-8 — o par do sm de Button/Select, pra fileira densa); a altura pode ser substituída em `TabsList`. `orientation` (horizontal/vertical).',
198
+ "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, ancorada na borda da lista). `size` no Tabs (default h-9 / sm h-8 — o par do sm de Button/Select, pra fileira densa); a altura pode ser substituída em `TabsList`. `orientation` (horizontal/vertical).",
183
199
  },
184
- 'textarea': {
185
- name: 'textarea',
186
- ancestry: 'shadcn',
200
+ textarea: {
201
+ name: "textarea",
202
+ ancestry: "shadcn",
187
203
  whenToUse:
188
- 'Campo de texto multilinha. Aceita os atributos nativos de <textarea> (rows, placeholder, disabled). Cresce com o conteúdo via `field-sizing-content`.',
204
+ "Campo de texto multilinha. Aceita os atributos nativos de <textarea> (rows, placeholder, disabled). Cresce com o conteúdo via `field-sizing-content`.",
189
205
  },
190
- 'toast': {
191
- name: 'toast',
192
- ancestry: 'shadcn',
206
+ toast: {
207
+ name: "toast",
208
+ ancestry: "shadcn",
193
209
  whenToUse:
194
- 'Notificação efêmera (sonner). Monte `<Toaster />` 1x no root e dispare com `toast.success/error/message(...)`. Pra mensagem persistente inline, use Alert.',
210
+ "Notificação efêmera (sonner). Monte `<Toaster />` 1x no root e dispare com `toast.success/error/message(...)`. Pra mensagem persistente inline, use Alert.",
195
211
  },
196
- 'tooltip': {
197
- name: 'tooltip',
198
- ancestry: 'shadcn',
212
+ tooltip: {
213
+ name: "tooltip",
214
+ ancestry: "shadcn",
199
215
  whenToUse:
200
- '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.',
216
+ "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.",
201
217
  },
202
- 'truncate': {
203
- name: 'truncate',
204
- ancestry: 'opus',
218
+ truncate: {
219
+ name: "truncate",
220
+ ancestry: "opus",
205
221
  whenToUse:
206
- '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); `fade` troca as reticências por um esmaecimento até a borda, ligado pela mesma medição. Requer TooltipProvider na raiz. Pra célula de tabela, nome de arquivo, URL — qualquer linha única que pode estourar.',
222
+ "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); `fade` troca as reticências por um esmaecimento até a borda, ligado pela mesma medição. Requer TooltipProvider na raiz. Pra célula de tabela, nome de arquivo, URL — qualquer linha única que pode estourar.",
207
223
  },
208
- 'accordion': {
209
- name: 'accordion',
210
- ancestry: 'shadcn',
224
+ accordion: {
225
+ name: "accordion",
226
+ ancestry: "shadcn",
211
227
  whenToUse:
212
- '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.',
228
+ "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.",
213
229
  },
214
- 'alert-dialog': {
215
- name: 'alert-dialog',
216
- ancestry: 'opus',
230
+ "alert-dialog": {
231
+ name: "alert-dialog",
232
+ ancestry: "opus",
217
233
  whenToUse:
218
234
  '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.',
219
235
  },
220
- 'aspect-ratio': {
221
- name: 'aspect-ratio',
222
- ancestry: 'shadcn',
236
+ "aspect-ratio": {
237
+ name: "aspect-ratio",
238
+ ancestry: "shadcn",
223
239
  whenToUse:
224
- '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.',
240
+ "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.",
225
241
  },
226
- 'avatar': {
227
- name: 'avatar',
228
- ancestry: 'shadcn',
242
+ avatar: {
243
+ name: "avatar",
244
+ ancestry: "shadcn",
229
245
  whenToUse:
230
246
  '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").',
231
247
  },
232
- 'breadcrumb': {
233
- name: 'breadcrumb',
234
- ancestry: 'shadcn',
248
+ breadcrumb: {
249
+ name: "breadcrumb",
250
+ ancestry: "shadcn",
235
251
  whenToUse:
236
- '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.',
252
+ "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.",
237
253
  },
238
- 'button-group': {
239
- name: 'button-group',
240
- ancestry: 'shadcn',
254
+ "button-group": {
255
+ name: "button-group",
256
+ ancestry: "shadcn",
241
257
  whenToUse:
242
258
  'Junta botões (e Select) num bloco coeso — bordas internas colapsadas e cantos arredondados só nas pontas. `orientation` define o eixo e `shape="pill"` arredonda as extremidades externas sem reabrir a junção interna. ButtonGroupText adiciona rótulo/prefixo; ButtonGroupSeparator corta visualmente entre ações.',
243
259
  },
244
- 'calendar': {
245
- name: 'calendar',
246
- ancestry: 'shadcn',
260
+ calendar: {
261
+ name: "calendar",
262
+ ancestry: "shadcn",
247
263
  whenToUse:
248
264
  '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.',
249
265
  },
250
- 'carousel': {
251
- name: 'carousel',
252
- ancestry: 'shadcn',
266
+ carousel: {
267
+ name: "carousel",
268
+ ancestry: "shadcn",
253
269
  whenToUse:
254
- '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.',
270
+ "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.",
255
271
  },
256
- 'collapsible': {
257
- name: 'collapsible',
258
- ancestry: 'shadcn',
272
+ collapsible: {
273
+ name: "collapsible",
274
+ ancestry: "shadcn",
259
275
  whenToUse:
260
- '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.',
276
+ "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.",
261
277
  },
262
- 'drawer': {
263
- name: 'drawer',
264
- ancestry: 'opus',
278
+ drawer: {
279
+ name: "drawer",
280
+ ancestry: "opus",
265
281
  whenToUse:
266
- '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.',
282
+ '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) + DrawerBody + 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.',
267
283
  },
268
- 'empty': {
269
- name: 'empty',
270
- ancestry: 'shadcn',
284
+ empty: {
285
+ name: "empty",
286
+ ancestry: "shadcn",
271
287
  whenToUse:
272
- 'Região disponível para receber ou criar conteúdo: moldura tracejada centrada com `EmptyHeader` (mídia + `EmptyTitle` + `EmptyDescription`) e `EmptyContent` pras ações. Para lista ou tabela carregada sem registros, use DataState ou ActionList, que preservam a moldura sólida da estrutura. `EmptyMedia variant` icon (quadrado muted) ou default (sem fundo). Pra erro inline use Alert; pra carregamento, Skeleton.',
288
+ "Região disponível para receber ou criar conteúdo: moldura tracejada centrada com `EmptyHeader` (mídia + `EmptyTitle` + `EmptyDescription`) e `EmptyContent` pras ações. Para lista ou tabela carregada sem registros, use DataState ou ActionList, que preservam a moldura sólida da estrutura. `EmptyMedia variant` icon (quadrado muted) ou default (sem fundo). Pra erro inline use Alert; pra carregamento, Skeleton.",
273
289
  },
274
- 'field': {
275
- name: 'field',
276
- ancestry: 'shadcn',
290
+ field: {
291
+ name: "field",
292
+ ancestry: "shadcn",
277
293
  whenToUse:
278
- '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).',
294
+ "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).",
279
295
  },
280
- 'input-group': {
281
- name: 'input-group',
282
- ancestry: 'shadcn',
296
+ "input-group": {
297
+ name: "input-group",
298
+ ancestry: "shadcn",
283
299
  whenToUse:
284
300
  'Campo composto: cola ícones, texto e botões a um InputGroupInput/InputGroupTextarea numa única moldura (foco e erro propagam pro grupo todo). `shape="pill"` aplica a geometria arredondada à moldura. InputGroupAddon ancora adornos; InputGroupText é rótulo inerte; InputGroupButton é o botão embutido. Pra agrupar botões soltos, use ButtonGroup.',
285
301
  },
286
- 'input-otp': {
287
- name: 'input-otp',
288
- ancestry: 'shadcn',
302
+ "input-otp": {
303
+ name: "input-otp",
304
+ ancestry: "shadcn",
289
305
  whenToUse:
290
- '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.',
306
+ "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.",
291
307
  },
292
- 'item': {
293
- name: 'item',
294
- ancestry: 'shadcn',
308
+ item: {
309
+ name: "item",
310
+ ancestry: "shadcn",
295
311
  whenToUse:
296
- '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: `variant="framed"` aplica a moldura e a superfície; ItemSeparator declara os divisores internos tanto no modo framed quanto no plain. É o padrão pra listas de workspaces, agentes, repositórios e sessões.',
312
+ 'Linha de conteúdo composta — ItemMedia + ItemHeader (ItemTitle e ItemDescription) + ItemBody ou ItemActions, com ItemFooter opcional. A mídia acompanha a altura útil do header. Item é o container (`variant` default/outline/muted, `size` default/sm, `asChild` pra virar link/botão); `ItemContent` permanece como alias legado. Empilhe vários num ItemGroup: `variant="framed"` aplica a moldura e a superfície; ItemSeparator declara os divisores internos. É o padrão pra listas de workspaces, agentes, repositórios e sessões.',
297
313
  },
298
- 'kbd': {
299
- name: 'kbd',
300
- ancestry: 'shadcn',
314
+ kbd: {
315
+ name: "kbd",
316
+ ancestry: "shadcn",
301
317
  whenToUse:
302
318
  '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.',
303
319
  },
304
- 'pagination': {
305
- name: 'pagination',
306
- ancestry: 'shadcn',
320
+ pagination: {
321
+ name: "pagination",
322
+ ancestry: "shadcn",
307
323
  whenToUse:
308
- 'Navegação entre páginas montada por composição: Pagination › PaginationContent › PaginationItem com PaginationLink, mais PaginationPrevious/PaginationNext e PaginationEllipsis. `page` dá o número e o nome acessível (“Página N”); `isActive` marca a atual. Com `href` o link é `<a>`; sem `href` vira `<button>` (modo controlado, com foco e `disabled`). Os números têm largura mínima quadrada e crescem com os dígitos; as setas seguem quadradas (`iconOnly`). `label` localiza as setas. É o único paginador: ActionList compõe esta primitiva na escala densa do rodapé. Pra rolagem infinita ou listas curtas, dispense a barra.',
324
+ "Navegação entre páginas montada por composição: Pagination › PaginationContent › PaginationItem com PaginationLink, mais PaginationPrevious/PaginationNext e PaginationEllipsis. `page` dá o número e o nome acessível (“Página N”); `isActive` marca a atual. Com `href` o link é `<a>`; sem `href` vira `<button>` (modo controlado, com foco e `disabled`). Os números têm largura mínima quadrada e crescem com os dígitos; as setas seguem quadradas (`iconOnly`). `label` localiza as setas. É o único paginador: ActionList compõe esta primitiva na escala densa do rodapé. Pra rolagem infinita ou listas curtas, dispense a barra.",
309
325
  },
310
- 'progress': {
311
- name: 'progress',
312
- ancestry: 'shadcn',
326
+ progress: {
327
+ name: "progress",
328
+ ancestry: "shadcn",
313
329
  whenToUse:
314
- '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.',
330
+ "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.",
315
331
  },
316
- 'radio-group': {
317
- name: 'radio-group',
318
- ancestry: 'shadcn',
332
+ "radio-group": {
333
+ name: "radio-group",
334
+ ancestry: "shadcn",
319
335
  whenToUse:
320
- '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.',
336
+ "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.",
321
337
  },
322
- 'split': {
323
- name: 'split',
324
- ancestry: 'opus',
338
+ split: {
339
+ name: "split",
340
+ ancestry: "opus",
325
341
  whenToUse:
326
- '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.',
342
+ "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.",
327
343
  },
328
- 'sidebar': {
329
- name: 'sidebar',
330
- ancestry: 'opus',
344
+ sidebar: {
345
+ name: "sidebar",
346
+ ancestry: "opus",
331
347
  whenToUse:
332
- '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 navegação com grupos e subgrupos. Serve tanto a barra global quanto uma nav contextual; em Split redimensionável passe `divider={false}` para não duplicar a divisória.',
348
+ "Chrome e navegação de uma coluna lateral, encaixada onde um Split decidir. `Sidebar` possui o colapso; `PaneHeader`, `PaneBody` e `PaneFooter` estruturam qualquer pane, e `SidebarNav`/`SidebarItem` apresentam navegação com grupos e subgrupos. `PaneContent` é um alias temporário de compatibilidade. Serve tanto a barra global quanto uma nav contextual; em Split redimensionável passe `divider={false}` para não duplicar a divisória.",
333
349
  },
334
- 'scroll-area': {
335
- name: 'scroll-area',
336
- ancestry: 'shadcn',
350
+ "scroll-area": {
351
+ name: "scroll-area",
352
+ ancestry: "shadcn",
337
353
  whenToUse:
338
354
  '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).',
339
355
  },
340
- 'slider': {
341
- name: 'slider',
342
- ancestry: 'shadcn',
356
+ slider: {
357
+ name: "slider",
358
+ ancestry: "shadcn",
343
359
  whenToUse:
344
- '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.',
360
+ "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.",
345
361
  },
346
- 'switch': {
347
- name: 'switch',
348
- ancestry: 'shadcn',
362
+ switch: {
363
+ name: "switch",
364
+ ancestry: "shadcn",
349
365
  whenToUse:
350
- '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.',
366
+ "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.",
351
367
  },
352
- 'toggle': {
353
- name: 'toggle',
354
- ancestry: 'shadcn',
368
+ toggle: {
369
+ name: "toggle",
370
+ ancestry: "shadcn",
355
371
  whenToUse:
356
- '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.',
372
+ "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.",
357
373
  },
358
- 'toggle-group': {
359
- name: 'toggle-group',
360
- ancestry: 'shadcn',
374
+ "toggle-group": {
375
+ name: "toggle-group",
376
+ ancestry: "shadcn",
361
377
  whenToUse:
362
- '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. Serve para alternar a visão de uma seção, montar uma barra de formatação ou apresentar escolhas ricas em cards pelo `ActionForm` com `widget: toggle-group`. Para uma escolha textual comum em formulário, especialmente com rótulos longos sem conteúdo de apoio, prefira RadioGroup.',
378
+ "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. Serve para alternar a visão de uma seção, montar uma barra de formatação ou apresentar escolhas ricas em cards pelo `ActionForm` com `widget: toggle-group`. Para uma escolha textual comum em formulário, especialmente com rótulos longos sem conteúdo de apoio, prefira RadioGroup.",
363
379
  },
364
380
 
365
- 'confirm': {
366
- name: 'dialog',
367
- ancestry: 'opus',
381
+ confirm: {
382
+ name: "dialog",
383
+ ancestry: "opus",
384
+ whenToUse:
385
+ "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.",
386
+ },
387
+ "action-form": {
388
+ name: "action-form",
389
+ ancestry: "opus",
368
390
  whenToUse:
369
- '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.',
391
+ "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.",
370
392
  },
371
- 'action-form': {
372
- name: 'action-form',
373
- ancestry: 'opus',
393
+ "action-form-dialog": {
394
+ name: "action-form-dialog",
395
+ ancestry: "opus",
374
396
  whenToUse:
375
- 'Form de uma FormAction do Opussubmit + 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.',
397
+ "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.",
376
398
  },
377
- 'action-form-dialog': {
378
- name: 'action-form-dialog',
379
- ancestry: 'opus',
399
+ "action-form-card": {
400
+ name: "action-form-card",
401
+ ancestry: "opus",
380
402
  whenToUse:
381
- '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.',
403
+ "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.",
382
404
  },
383
- 'action-form-card': {
384
- name: 'action-form-card',
385
- ancestry: 'opus',
405
+ "action-list": {
406
+ name: "action-list",
407
+ ancestry: "opus",
386
408
  whenToUse:
387
- '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.',
409
+ "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.",
388
410
  },
389
- 'action-list': {
390
- name: 'action-list',
391
- ancestry: 'opus',
411
+ "action-trigger": {
412
+ name: "action-trigger",
413
+ ancestry: "opus",
392
414
  whenToUse:
393
- '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.',
415
+ "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.",
394
416
  },
395
- 'action-trigger': {
396
- name: 'action-trigger',
397
- ancestry: 'opus',
417
+ "action-view": {
418
+ name: "action-view",
419
+ ancestry: "opus",
398
420
  whenToUse:
399
- '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.',
421
+ "Carrega e exibe 1 recurso de uma ViewAction do Opusloading/error/empty encapsulados; o layout vem por children `(data, refetch) => nó` (render segue como alias). Pra listagem, ActionList.",
400
422
  },
401
- 'action-view': {
402
- name: 'action-view',
403
- ancestry: 'opus',
423
+ page: {
424
+ name: "page",
425
+ ancestry: "opus",
404
426
  whenToUse:
405
- '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.',
427
+ "O esqueleto de página do back-office: <main> + container centralizado com teto padrão de 80rem (`max-w-7xl`). O shorthand com title, description, count e actions cria a mesma anatomia de PageHeader(PageTitle/PageDescription/PageMeta/PageActions) + PageBody disponível na forma explícita. `className` substitui o teto quando a composição pede outra largura. Quando todo o body estiver carregando, falhar ou estiver vazio, use PageState. Pra listagem em modal, ActionListDialog.",
406
428
  },
407
- 'page': {
408
- name: 'page',
409
- ancestry: 'opus',
429
+ "page-state": {
430
+ name: "page-state",
431
+ ancestry: "opus",
410
432
  whenToUse:
411
- 'O esqueleto de página do back-office: <main> + container centralizado com teto padrão de 72rem (`max-w-6xl`) + header com título, descrição e ação à direita (em geral o criar). `className` substitui o teto quando a composição pede outra largura. Presentacional (a página agrega N fontes); o action-driven mora dentro. Pra listagem em modal, ActionListDialog.',
433
+ "Estado integral da área de conteúdo de Page: loading centralizado, error em Alert com recuperação aplicável, empty em Empty com contexto/ação e ready sem moldura adicional. Use como filho direto de Page somente quando o estado substitui TODO o conteúdo principal; pra seção ou coleção parcial, use DataState, ActionView ou ActionList.",
412
434
  },
413
- 'router': {
414
- name: 'router',
415
- ancestry: 'opus',
435
+ router: {
436
+ name: "router",
437
+ ancestry: "opus",
416
438
  whenToUse:
417
439
  '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).',
418
440
  },
419
- 'data-state': {
420
- name: 'data-state',
421
- ancestry: 'opus',
441
+ "data-state": {
442
+ name: "data-state",
443
+ ancestry: "opus",
422
444
  whenToUse:
423
445
  '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 em moldura sólida no vazio em bloco e aviso calmo no erro. Em tabela emoldurada, `colSpan` mantém a borda somente no pai. Pra uma região disponível para criação ou vínculo, use Empty, cuja moldura é tracejada. Pra "processando" (ação em andamento DEPOIS do clique), use o `busy` do Button. Pra placeholder com forma, Skeleton.',
424
446
  },
425
- 'action-list-dialog': {
426
- name: 'action-list-dialog',
427
- ancestry: 'opus',
447
+ "action-list-dialog": {
448
+ name: "action-list-dialog",
449
+ ancestry: "opus",
428
450
  whenToUse:
429
451
  '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.',
430
452
  },
431
- } as const satisfies Record<string, ComponentMeta>
453
+ } as const satisfies Record<string, ComponentMeta>;
432
454
 
433
- export type ComponentMetaKey = keyof typeof componentMeta
455
+ export type ComponentMetaKey = keyof typeof componentMeta;