@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.
Files changed (286) hide show
  1. package/CHANGELOG.md +1616 -0
  2. package/LICENSE +21 -0
  3. package/README.md +113 -0
  4. package/bin/cli.mjs +528 -0
  5. package/bin/lib/check.mjs +307 -0
  6. package/bin/lib/components.mjs +151 -0
  7. package/bin/lib/create.mjs +208 -0
  8. package/bin/lib/db-check-runner.mjs +86 -0
  9. package/bin/lib/db-migrate-runner.mjs +89 -0
  10. package/bin/lib/db-scaffold-runner.mjs +84 -0
  11. package/bin/lib/db.mjs +261 -0
  12. package/bin/lib/docs-include.mjs +48 -0
  13. package/bin/lib/gen-dicts.mjs +134 -0
  14. package/bin/lib/gen-docs.mjs +288 -0
  15. package/bin/lib/gen-manifest.mjs +102 -0
  16. package/bin/lib/gen-openapi.mjs +195 -0
  17. package/bin/lib/gen-runner.mjs +472 -0
  18. package/bin/lib/gen-stubs.mjs +463 -0
  19. package/bin/lib/gen.mjs +311 -0
  20. package/bin/lib/init.mjs +514 -0
  21. package/bin/lib/introspect.mjs +107 -0
  22. package/bin/lib/mcp.mjs +85 -0
  23. package/bin/lib/postinstall.mjs +56 -0
  24. package/docs/chat-event-protocol.md +85 -0
  25. package/docs/code-style.md +16 -0
  26. package/docs/data-layer.md +246 -0
  27. package/docs/ownership-vs-shadcn-lock.md +102 -0
  28. package/docs/protocol.md +2053 -0
  29. package/docs/releasing.md +110 -0
  30. package/docs/shellnav.md +131 -0
  31. package/package.json +338 -0
  32. package/registry/hooks/hooks.json +26 -0
  33. package/registry/hooks/link-memory-on-start.mjs +46 -0
  34. package/registry/hooks/opus-check-on-stop.mjs +114 -0
  35. package/registry/skills/create-action/SKILL.md +49 -0
  36. package/registry/skills/create-action/scaffold.mjs +122 -0
  37. package/registry/templates/app/_gitignore +3 -0
  38. package/registry/templates/app/_npmrc +1 -0
  39. package/registry/templates/app/_opus/_gitignore +5 -0
  40. package/registry/templates/app/_prettierrc.json +6 -0
  41. package/registry/templates/app/index.html +13 -0
  42. package/registry/templates/app/opus.config.ts +16 -0
  43. package/registry/templates/app/package.json +43 -0
  44. package/registry/templates/app/pnpm-workspace.yaml +11 -0
  45. package/registry/templates/app/public/favicon.svg +4 -0
  46. package/registry/templates/app/src/App.tsx +37 -0
  47. package/registry/templates/app/src/domains/tasks/actions/list.test.ts +34 -0
  48. package/registry/templates/app/src/domains/tasks/actions/list.ts +33 -0
  49. package/registry/templates/app/src/domains/tasks/index.ts +13 -0
  50. package/registry/templates/app/src/index.css +18 -0
  51. package/registry/templates/app/src/main.tsx +25 -0
  52. package/registry/templates/app/tsconfig.json +20 -0
  53. package/registry/templates/app/vite.config.ts +46 -0
  54. package/registry/templates/monorepo/_gitignore +3 -0
  55. package/registry/templates/monorepo/_npmrc +1 -0
  56. package/registry/templates/monorepo/package.json +9 -0
  57. package/registry/templates/monorepo/pnpm-workspace.yaml +14 -0
  58. package/src/ai/ask.ts +64 -0
  59. package/src/ai/drivers/anthropic.ts +309 -0
  60. package/src/ai/index.ts +17 -0
  61. package/src/audit/drivers/console.ts +117 -0
  62. package/src/audit/drivers/pg.ts +172 -0
  63. package/src/audit/index.ts +51 -0
  64. package/src/auth/drivers/better-auth.ts +103 -0
  65. package/src/auth/drivers/jwt.ts +188 -0
  66. package/src/auth/index.ts +9 -0
  67. package/src/client/drivers/fetch.ts +202 -0
  68. package/src/client/index.ts +22 -0
  69. package/src/core/actions.ts +110 -0
  70. package/src/core/audit.ts +239 -0
  71. package/src/core/contracts.ts +137 -0
  72. package/src/core/domain.ts +310 -0
  73. package/src/core/errors.ts +181 -0
  74. package/src/core/index.ts +174 -0
  75. package/src/core/logical-type.ts +31 -0
  76. package/src/core/reactions.ts +81 -0
  77. package/src/core/runtime.ts +1167 -0
  78. package/src/core/schedules.ts +41 -0
  79. package/src/core/types.ts +1356 -0
  80. package/src/data/drivers/kysely.ts +389 -0
  81. package/src/data/index.ts +10 -0
  82. package/src/data/readonly-pool.ts +160 -0
  83. package/src/dsl/eval.ts +136 -0
  84. package/src/dsl/index.ts +29 -0
  85. package/src/dsl/kysely.ts +230 -0
  86. package/src/dsl/loads.ts +123 -0
  87. package/src/dsl/parser.ts +423 -0
  88. package/src/dsl/types.ts +113 -0
  89. package/src/events/drivers/mitt.ts +70 -0
  90. package/src/events/index.ts +9 -0
  91. package/src/log/drivers/pino.ts +57 -0
  92. package/src/log/index.ts +9 -0
  93. package/src/mcp/index.ts +62 -0
  94. package/src/queue/drivers/bullmq.ts +190 -0
  95. package/src/queue/index.ts +9 -0
  96. package/src/scheduler/drivers/node-cron.ts +93 -0
  97. package/src/scheduler/every.ts +45 -0
  98. package/src/scheduler/index.ts +9 -0
  99. package/src/schema/drivers/zod.ts +765 -0
  100. package/src/schema/entity.ts +439 -0
  101. package/src/schema/format/locale.ts +144 -0
  102. package/src/schema/index.ts +65 -0
  103. package/src/schema/openapi.ts +302 -0
  104. package/src/schema/scaffold.ts +160 -0
  105. package/src/server/drivers/fastify.ts +224 -0
  106. package/src/server/drivers/node.ts +386 -0
  107. package/src/server/index.ts +142 -0
  108. package/src/storage/drivers/fs.ts +90 -0
  109. package/src/storage/drivers/s3.ts +117 -0
  110. package/src/storage/index.ts +27 -0
  111. package/src/testing/fake.ts +298 -0
  112. package/src/testing/index.ts +324 -0
  113. package/src/ui/components/patterns/action-form-card.tsx +48 -0
  114. package/src/ui/components/patterns/action-list-dialog.tsx +93 -0
  115. package/src/ui/components/patterns/app-shell.tsx +227 -0
  116. package/src/ui/components/patterns/confirm.tsx +226 -0
  117. package/src/ui/components/patterns/data-state.tsx +75 -0
  118. package/src/ui/components/patterns/form-dialog.tsx +64 -0
  119. package/src/ui/components/patterns/form.tsx +584 -0
  120. package/src/ui/components/patterns/list.tsx +1488 -0
  121. package/src/ui/components/patterns/page.tsx +46 -0
  122. package/src/ui/components/patterns/section-shell.tsx +246 -0
  123. package/src/ui/components/patterns/shell-nav.tsx +150 -0
  124. package/src/ui/components/patterns/sidebar.tsx +89 -0
  125. package/src/ui/components/patterns/split.tsx +93 -0
  126. package/src/ui/components/patterns/trigger.tsx +196 -0
  127. package/src/ui/components/patterns/view.tsx +84 -0
  128. package/src/ui/components/primitives/accordion.tsx +64 -0
  129. package/src/ui/components/primitives/alert-dialog.tsx +190 -0
  130. package/src/ui/components/primitives/alert.tsx +116 -0
  131. package/src/ui/components/primitives/aspect-ratio.tsx +9 -0
  132. package/src/ui/components/primitives/avatar.tsx +107 -0
  133. package/src/ui/components/primitives/badge.tsx +37 -0
  134. package/src/ui/components/primitives/breadcrumb.tsx +109 -0
  135. package/src/ui/components/primitives/button-group.tsx +83 -0
  136. package/src/ui/components/primitives/button.tsx +102 -0
  137. package/src/ui/components/primitives/calendar.tsx +218 -0
  138. package/src/ui/components/primitives/card.tsx +56 -0
  139. package/src/ui/components/primitives/carousel.tsx +239 -0
  140. package/src/ui/components/primitives/chat.tsx +407 -0
  141. package/src/ui/components/primitives/checkbox.tsx +30 -0
  142. package/src/ui/components/primitives/collapsible.tsx +31 -0
  143. package/src/ui/components/primitives/command.tsx +182 -0
  144. package/src/ui/components/primitives/composer.tsx +121 -0
  145. package/src/ui/components/primitives/copyable.tsx +50 -0
  146. package/src/ui/components/primitives/dialog.tsx +147 -0
  147. package/src/ui/components/primitives/drawer.tsx +141 -0
  148. package/src/ui/components/primitives/empty.tsx +104 -0
  149. package/src/ui/components/primitives/field.tsx +246 -0
  150. package/src/ui/components/primitives/icon-picker.tsx +180 -0
  151. package/src/ui/components/primitives/input-group.tsx +168 -0
  152. package/src/ui/components/primitives/input-otp.tsx +75 -0
  153. package/src/ui/components/primitives/input.tsx +72 -0
  154. package/src/ui/components/primitives/item.tsx +193 -0
  155. package/src/ui/components/primitives/kbd.tsx +28 -0
  156. package/src/ui/components/primitives/label.tsx +22 -0
  157. package/src/ui/components/primitives/markdown.tsx +35 -0
  158. package/src/ui/components/primitives/menu.tsx +255 -0
  159. package/src/ui/components/primitives/pagination.tsx +127 -0
  160. package/src/ui/components/primitives/popover.tsx +87 -0
  161. package/src/ui/components/primitives/progress.tsx +29 -0
  162. package/src/ui/components/primitives/radio-group.tsx +43 -0
  163. package/src/ui/components/primitives/resizable.tsx +51 -0
  164. package/src/ui/components/primitives/scroll-area.tsx +56 -0
  165. package/src/ui/components/primitives/select.tsx +479 -0
  166. package/src/ui/components/primitives/separator.tsx +26 -0
  167. package/src/ui/components/primitives/skeleton.tsx +13 -0
  168. package/src/ui/components/primitives/slider.tsx +61 -0
  169. package/src/ui/components/primitives/sonner.tsx +46 -0
  170. package/src/ui/components/primitives/spinner.tsx +29 -0
  171. package/src/ui/components/primitives/switch.tsx +33 -0
  172. package/src/ui/components/primitives/table.tsx +114 -0
  173. package/src/ui/components/primitives/tabs.tsx +104 -0
  174. package/src/ui/components/primitives/textarea.tsx +18 -0
  175. package/src/ui/components/primitives/toggle-group.tsx +81 -0
  176. package/src/ui/components/primitives/toggle.tsx +45 -0
  177. package/src/ui/components/primitives/tooltip.tsx +55 -0
  178. package/src/ui/components/primitives/truncate.tsx +49 -0
  179. package/src/ui/docs/DocBrowser.tsx +90 -0
  180. package/src/ui/docs/changelog.tsx +80 -0
  181. package/src/ui/docs/content/accordion.md +86 -0
  182. package/src/ui/docs/content/action-form-card.md +24 -0
  183. package/src/ui/docs/content/action-form-dialog.md +30 -0
  184. package/src/ui/docs/content/action-form.md +125 -0
  185. package/src/ui/docs/content/action-list-dialog.md +68 -0
  186. package/src/ui/docs/content/action-list.md +194 -0
  187. package/src/ui/docs/content/action-trigger.md +72 -0
  188. package/src/ui/docs/content/action-view.md +47 -0
  189. package/src/ui/docs/content/actions.md +138 -0
  190. package/src/ui/docs/content/ai.md +112 -0
  191. package/src/ui/docs/content/alert-dialog.md +73 -0
  192. package/src/ui/docs/content/alert.md +69 -0
  193. package/src/ui/docs/content/app-shell.md +155 -0
  194. package/src/ui/docs/content/aspect-ratio.md +66 -0
  195. package/src/ui/docs/content/audit.md +84 -0
  196. package/src/ui/docs/content/auth.md +70 -0
  197. package/src/ui/docs/content/avatar.md +94 -0
  198. package/src/ui/docs/content/badge.md +48 -0
  199. package/src/ui/docs/content/breadcrumb.md +87 -0
  200. package/src/ui/docs/content/button-group.md +71 -0
  201. package/src/ui/docs/content/button.md +60 -0
  202. package/src/ui/docs/content/calendar.md +62 -0
  203. package/src/ui/docs/content/card.md +49 -0
  204. package/src/ui/docs/content/carousel.md +85 -0
  205. package/src/ui/docs/content/chat.md +69 -0
  206. package/src/ui/docs/content/checkbox.md +75 -0
  207. package/src/ui/docs/content/cli.md +58 -0
  208. package/src/ui/docs/content/collapsible.md +64 -0
  209. package/src/ui/docs/content/command.md +56 -0
  210. package/src/ui/docs/content/composer.md +50 -0
  211. package/src/ui/docs/content/confirm.md +120 -0
  212. package/src/ui/docs/content/copyable.md +30 -0
  213. package/src/ui/docs/content/customization.md +110 -0
  214. package/src/ui/docs/content/cycle.md +34 -0
  215. package/src/ui/docs/content/data-state.md +47 -0
  216. package/src/ui/docs/content/data.md +99 -0
  217. package/src/ui/docs/content/dialog.md +60 -0
  218. package/src/ui/docs/content/drawer.md +55 -0
  219. package/src/ui/docs/content/empty.md +66 -0
  220. package/src/ui/docs/content/events.md +61 -0
  221. package/src/ui/docs/content/field.md +58 -0
  222. package/src/ui/docs/content/getting-started.md +109 -0
  223. package/src/ui/docs/content/icon-picker.md +51 -0
  224. package/src/ui/docs/content/input-group.md +78 -0
  225. package/src/ui/docs/content/input-otp.md +72 -0
  226. package/src/ui/docs/content/input.md +78 -0
  227. package/src/ui/docs/content/item.md +84 -0
  228. package/src/ui/docs/content/kbd.md +62 -0
  229. package/src/ui/docs/content/label.md +32 -0
  230. package/src/ui/docs/content/log.md +55 -0
  231. package/src/ui/docs/content/markdown.md +41 -0
  232. package/src/ui/docs/content/mcp.md +44 -0
  233. package/src/ui/docs/content/menu.md +114 -0
  234. package/src/ui/docs/content/microcopy.md +83 -0
  235. package/src/ui/docs/content/page.md +34 -0
  236. package/src/ui/docs/content/pagination.md +99 -0
  237. package/src/ui/docs/content/popover.md +49 -0
  238. package/src/ui/docs/content/progress.md +69 -0
  239. package/src/ui/docs/content/queue.md +62 -0
  240. package/src/ui/docs/content/radio-group.md +77 -0
  241. package/src/ui/docs/content/resizable.md +86 -0
  242. package/src/ui/docs/content/router.md +56 -0
  243. package/src/ui/docs/content/runtime.md +77 -0
  244. package/src/ui/docs/content/scheduler.md +66 -0
  245. package/src/ui/docs/content/scroll-area.md +89 -0
  246. package/src/ui/docs/content/section-shell.md +121 -0
  247. package/src/ui/docs/content/select.md +342 -0
  248. package/src/ui/docs/content/separator.md +33 -0
  249. package/src/ui/docs/content/sidebar.md +38 -0
  250. package/src/ui/docs/content/skeleton.md +34 -0
  251. package/src/ui/docs/content/slider.md +64 -0
  252. package/src/ui/docs/content/spinner.md +37 -0
  253. package/src/ui/docs/content/split.md +33 -0
  254. package/src/ui/docs/content/storage.md +69 -0
  255. package/src/ui/docs/content/switch.md +69 -0
  256. package/src/ui/docs/content/table.md +102 -0
  257. package/src/ui/docs/content/tabs.md +94 -0
  258. package/src/ui/docs/content/testing.md +89 -0
  259. package/src/ui/docs/content/textarea.md +30 -0
  260. package/src/ui/docs/content/toast.md +67 -0
  261. package/src/ui/docs/content/toggle-group.md +81 -0
  262. package/src/ui/docs/content/toggle.md +72 -0
  263. package/src/ui/docs/content/tokens.md +171 -0
  264. package/src/ui/docs/content/tooltip.md +50 -0
  265. package/src/ui/docs/content/truncate.md +37 -0
  266. package/src/ui/docs/content/ui.md +40 -0
  267. package/src/ui/docs/content/upgrading.md +48 -0
  268. package/src/ui/docs/doc-client.tsx +214 -0
  269. package/src/ui/docs/doc.tsx +301 -0
  270. package/src/ui/docs/folder.tsx +149 -0
  271. package/src/ui/docs/index.ts +21 -0
  272. package/src/ui/docs/markdown.tsx +130 -0
  273. package/src/ui/docs/md-raw.d.ts +4 -0
  274. package/src/ui/docs/plugin.ts +104 -0
  275. package/src/ui/docs/registry.tsx +424 -0
  276. package/src/ui/docs/standalone.tsx +107 -0
  277. package/src/ui/drivers/react.tsx +627 -0
  278. package/src/ui/index.ts +92 -0
  279. package/src/ui/lib/cn.ts +10 -0
  280. package/src/ui/lib/zod-pt-br.ts +38 -0
  281. package/src/ui/meta.ts +412 -0
  282. package/src/ui/react.tsx +235 -0
  283. package/src/ui/router.ts +96 -0
  284. package/src/ui/theme.css +234 -0
  285. package/src/vite/design.ts +652 -0
  286. package/src/vite/index.ts +8 -0
@@ -0,0 +1,84 @@
1
+ ---
2
+ title: Auditoria
3
+ ---
4
+
5
+ # Auditoria
6
+
7
+ Toda action executada vira um registro imutável: quem, o quê, quando, com que entrada e
8
+ resultado. O runtime emite um `AuditRecord` por execução pro(s) sink(s) configurado(s) — de
9
+ graça, sem o handler pedir. O contrato é o `AuditSink`.
10
+
11
+ ## O contrato
12
+
13
+ ```ts
14
+ interface AuditSink {
15
+ emit(record: AuditRecord): Promise<void> | void
16
+ }
17
+
18
+ interface AuditRecord {
19
+ id: string
20
+ timestamp: string
21
+ action: string
22
+ actionKind?: 'simple' | 'form' | 'list' | 'view' // sink filtra leitura sem tabela por fora
23
+ outcome: 'success' | 'error'
24
+ durationMs: number
25
+ provenance: Provenance // origem do trigger (http, schedule, reaction, ai…)
26
+ // actor.meta chega com name/email do user resolvido (quando presentes) —
27
+ // sink não re-resolve id→nome.
28
+ actor: { id: string | null; type?: 'user' | 'system' | 'integration'; meta?: Record<string, unknown> }
29
+ tenant?: string | null
30
+ input: unknown
31
+ output?: unknown
32
+ error?: ActionError
33
+ severity: 'info' | 'warning' | 'error'
34
+ meta?: Record<string, unknown>
35
+ }
36
+ ```
37
+
38
+ ## Drivers
39
+
40
+ ```ts
41
+ import { consoleAudit } from '@softize/opus/audit/console'
42
+ import { pgAudit } from '@softize/opus/audit/pg'
43
+
44
+ // Dev: uma linha por action (pretty num TTY, json senão).
45
+ const dev = consoleAudit()
46
+
47
+ // Produção: grava na tabela audit_log (Postgres).
48
+ const prod = pgAudit({ pool, table: 'audit_log' })
49
+ ```
50
+
51
+ ## Dado sensível: o redator global
52
+
53
+ Sem redator, os sinks persistem input e output **crus** — `user.create`/`setPassword`
54
+ gravaria senha em claro. Os dois drivers aceitam `redact` (função `unknown → unknown`
55
+ aplicada a input/output antes de persistir/imprimir), e a base traz o redator recursivo:
56
+
57
+ ```ts
58
+ import { redactDeep } from '@softize/opus/audit'
59
+
60
+ // Qualquer profundidade, arrays inclusos; chave que casa
61
+ // /password|passwd|secret|token|authorization|api[-_]?key|credential/i vira '[REDACTED]'.
62
+ const prod = pgAudit({ pool, redact: redactDeep })
63
+ ```
64
+
65
+ Sink próprio reusa as peças: `redactRecord(record, redator)` e `SENSITIVE_KEY_PATTERN`
66
+ saem do mesmo módulo. O `audit: { redact: [...] }` por action (dot-paths) continua
67
+ valendo — o redator do sink é o cinto de segurança global, não o substituto.
68
+
69
+ ## No runtime
70
+
71
+ ```ts
72
+ const runtime = createRuntime({
73
+ // …server/data/auth…
74
+ audit: prod, // um sink, ou vários: [consoleAudit(), pgAudit({ pool })]
75
+ })
76
+ ```
77
+
78
+ O que cada action captura se ajusta no contrato (`audit: false` desliga; `audit: { redact:
79
+ ['senha'], output: false }` refina). O default captura tudo — input, output e provenance.
80
+
81
+ ## Limites (por enquanto)
82
+
83
+ Sink append-only (sem query/retenção embutida — isso é do seu banco). Drivers hoje:
84
+ `console` e `pg`; outros (arquivo, OpenTelemetry…) entram por reincidência.
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: Autenticação
3
+ ---
4
+
5
+ # Autenticação
6
+
7
+ Quem é o usuário, de qual tenant, e o que ele pode. O contrato é um adapter do core
8
+ (`AuthAdapter`): a cada request o runtime chama `resolveContext` e injeta o resultado no
9
+ handler — `ctx.user`, `ctx.tenantId`, `ctx.can`. O Opus **não** implementa RBAC/ABAC; ele
10
+ delega a decisão pro `can` que o driver pluga.
11
+
12
+ ## O contrato
13
+
14
+ ```ts
15
+ interface AuthAdapter {
16
+ resolveContext(req): Promise<{
17
+ user: User | null // { id, ...claims }
18
+ tenantId?: string | null // multi-tenant (opcional)
19
+ can: CanFn // (permission, resource?) => boolean | Promise<boolean>
20
+ }>
21
+ }
22
+ ```
23
+
24
+ `req` é o request cru do `ServerAdapter`. O `user` sai dos claims/sessão; o `can` é a régua
25
+ de autorização — o Opus só chama, não decide.
26
+
27
+ ## Drivers
28
+
29
+ ```ts
30
+ import { jwtAuth } from '@softize/opus/auth/jwt'
31
+ import { betterAuthSession } from '@softize/opus/auth/better-auth'
32
+
33
+ // JWT: valida o token (HS256 por padrão) e mapeia o payload pro User.
34
+ const jwt = jwtAuth({ secret: process.env.JWT_SECRET! })
35
+
36
+ // better-auth: valida a sessão contra um IdP better-auth remoto.
37
+ const idp = betterAuthSession({
38
+ baseURL: 'https://auth.example.com',
39
+ mapUser: (s) => ({ id: s.user.id, email: s.user.email }),
40
+ can: (user) => (perm) => (user.roles as string[] | undefined)?.includes(perm) ?? false,
41
+ })
42
+ ```
43
+
44
+ O `jwt` procura o token no `Authorization: Bearer`, em cookie ou custom (via `getToken`); o
45
+ `secret` pode ser string ou um resolver async por `kid`. O `betterAuthSession` faz fetch da
46
+ sessão no IdP (`timeoutMs`, default 5s) e mapeia pro `User`/tenant/can (o `can` default nega
47
+ tudo — plugue o seu).
48
+
49
+ ## No runtime
50
+
51
+ ```ts
52
+ const runtime = createRuntime({
53
+ // …server/data…
54
+ auth: idp,
55
+ })
56
+
57
+ // No handler: o contexto já vem resolvido.
58
+ handler: async (ctx, input) => {
59
+ if (!(await ctx.can('nota.emitir'))) throw error({ code: 'forbidden' })
60
+ return repo.create({ ...input, tenantId: ctx.tenantId })
61
+ }
62
+ ```
63
+
64
+ A action declara o gate no contrato (`auth: 'nota.emitir'`) e o runtime cobra antes do
65
+ handler. Sem `auth` configurado, `ctx.user` é null.
66
+
67
+ ## Limites (por enquanto)
68
+
69
+ O Opus não modela papéis/permissões — isso é do seu `can`. Drivers hoje: `jwt` e
70
+ `better-auth`; outros (Clerk, Auth.js…) entram por reincidência.
@@ -0,0 +1,94 @@
1
+ ## Básico
2
+
3
+ AvatarImage com src/alt e AvatarFallback com as iniciais — o fallback aparece enquanto a imagem
4
+ carrega ou se ela falha.
5
+
6
+ ```tsx preview
7
+ <Avatar>
8
+ <AvatarImage src="https://github.com/joaocampos.png" alt="João Campos" />
9
+ <AvatarFallback>JC</AvatarFallback>
10
+ </Avatar>
11
+ ```
12
+
13
+ ## Fallback de iniciais
14
+
15
+ Sem AvatarImage (ou com src quebrado), só o AvatarFallback renderiza — iniciais pra pessoa, ícone
16
+ pra agente.
17
+
18
+ ```tsx preview
19
+ <Avatar>
20
+ <AvatarFallback>AL</AvatarFallback>
21
+ </Avatar>
22
+ <Avatar>
23
+ <AvatarFallback>
24
+ <Bot className="size-4" />
25
+ </AvatarFallback>
26
+ </Avatar>
27
+ ```
28
+
29
+ ## Tamanhos
30
+
31
+ size sm/default/lg — o fallback acompanha o tamanho. sm pra listas densas, lg pra cabeçalho de
32
+ workspace.
33
+
34
+ ```tsx preview
35
+ <Avatar size="sm">
36
+ <AvatarFallback>AL</AvatarFallback>
37
+ </Avatar>
38
+ <Avatar size="default">
39
+ <AvatarFallback>AL</AvatarFallback>
40
+ </Avatar>
41
+ <Avatar size="lg">
42
+ <AvatarFallback>AL</AvatarFallback>
43
+ </Avatar>
44
+ ```
45
+
46
+ ## Com selo de status
47
+
48
+ AvatarBadge é o ponto no canto inferior — verde pro agente developer rodando uma sessão, cinza
49
+ pro ocioso. Tinja com className.
50
+
51
+ ```tsx preview
52
+ <Avatar>
53
+ <AvatarFallback>
54
+ <Bot className="size-4" />
55
+ </AvatarFallback>
56
+ <AvatarBadge className="bg-emerald-500" />
57
+ </Avatar>
58
+ <Avatar>
59
+ <AvatarFallback>RV</AvatarFallback>
60
+ <AvatarBadge className="bg-muted-foreground" />
61
+ </Avatar>
62
+ ```
63
+
64
+ ## Grupo sobreposto
65
+
66
+ AvatarGroup sobrepõe os avatares (com anel de fundo) e AvatarGroupCount fecha a pilha com o
67
+ excedente — os membros do workspace Empresa X.
68
+
69
+ ```tsx preview
70
+ <AvatarGroup>
71
+ <Avatar>
72
+ <AvatarImage src="https://github.com/joaocampos.png" alt="João Campos" />
73
+ <AvatarFallback>JC</AvatarFallback>
74
+ </Avatar>
75
+ <Avatar>
76
+ <AvatarFallback>DV</AvatarFallback>
77
+ </Avatar>
78
+ <Avatar>
79
+ <AvatarFallback>RV</AvatarFallback>
80
+ </Avatar>
81
+ <AvatarGroupCount>+3</AvatarGroupCount>
82
+ </AvatarGroup>
83
+ ```
84
+
85
+ ## Props
86
+
87
+ | Prop | Tipo | Default | Descrição |
88
+ |---|---|---|---|
89
+ | `size` (Avatar) | `'sm' \| 'default' \| 'lg'` | `'default'` | Diâmetro do retrato — o fallback e o selo acompanham. |
90
+ | `src` (AvatarImage) | `string` | | URL da imagem. Enquanto carrega (ou se falha), o AvatarFallback fica no lugar. |
91
+ | `alt` (AvatarImage) | `string` | | Texto alternativo da imagem — o nome da pessoa ou do agente. |
92
+ | `children` (AvatarFallback) | `React.ReactNode` | | O que aparece sem imagem: iniciais (pessoa) ou ícone (agente). |
93
+ | `className` (AvatarBadge) | `string` | | Selo no canto inferior — tinja o fundo (ex.: bg-emerald-500) pra refletir o status. |
94
+ | `children` (AvatarGroupCount) | `React.ReactNode` | | O excedente da pilha (ex.: "+3"), fechando o AvatarGroup. |
@@ -0,0 +1,48 @@
1
+ ## Intenção
2
+
3
+ As quatro variantes do shadcn: categoria, papel, alerta. Badge é não-interativo — pra clique, use
4
+ Button.
5
+
6
+ ```tsx preview
7
+ <Badge>Agente</Badge>
8
+ <Badge variant="secondary">Skill</Badge>
9
+ <Badge variant="outline">Monorepo</Badge>
10
+ <Badge variant="destructive">Arquivado</Badge>
11
+ ```
12
+
13
+ ## Tons de status da casa
14
+
15
+ success/warning/info (fill tingido + borda translúcida) — convenção da casa pra estado semântico,
16
+ igual ao KindBadge do Maestro. Não-interativos.
17
+
18
+ ```tsx preview
19
+ <Badge variant="success">Ativa</Badge>
20
+ <Badge variant="warning">Aguardando revisor</Badge>
21
+ <Badge variant="info">Em sessão</Badge>
22
+ ```
23
+
24
+ ## Com ícone
25
+
26
+ Um svg filho ganha size-3 automaticamente — bom pra reforçar o estado sem crescer o rótulo.
27
+
28
+ ```tsx preview
29
+ <Badge variant="success"><CircleCheck /> Regressão verde</Badge>
30
+ ```
31
+
32
+ ## Como link (asChild)
33
+
34
+ asChild renderiza o filho (Radix Slot) — um `<a>` com cara de badge, com hover próprio das
35
+ variantes.
36
+
37
+ ```tsx preview
38
+ <Badge asChild variant="secondary">
39
+ <a href="#" onClick={(e) => e.preventDefault()}>Empresa X</a>
40
+ </Badge>
41
+ ```
42
+
43
+ ## Props
44
+
45
+ | Prop | Tipo | Default | Descrição |
46
+ |---|---|---|---|
47
+ | `variant` | `'default' \| 'secondary' \| 'destructive' \| 'outline' \| 'success' \| 'warning' \| 'info'` | `'default'` | Intenção (default/secondary/destructive/outline) ou tom de status da casa (success/warning/info). |
48
+ | `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<span>` — ex.: um `<a>` com cara de badge. |
@@ -0,0 +1,87 @@
1
+ ## Básico
2
+
3
+ A composição é manual: BreadcrumbLink nos níveis navegáveis, BreadcrumbPage no atual (não
4
+ clicável, aria-current=page) e um BreadcrumbSeparator entre cada item.
5
+
6
+ ```tsx preview col-start
7
+ <Breadcrumb>
8
+ <BreadcrumbList>
9
+ <BreadcrumbItem>
10
+ <BreadcrumbLink href="#">Empresa X</BreadcrumbLink>
11
+ </BreadcrumbItem>
12
+ <BreadcrumbSeparator />
13
+ <BreadcrumbItem>
14
+ <BreadcrumbLink href="#">empresa-x-api</BreadcrumbLink>
15
+ </BreadcrumbItem>
16
+ <BreadcrumbSeparator />
17
+ <BreadcrumbItem>
18
+ <BreadcrumbPage>Sessão #142</BreadcrumbPage>
19
+ </BreadcrumbItem>
20
+ </BreadcrumbList>
21
+ </Breadcrumb>
22
+ ```
23
+
24
+ ## Com ícone e separador custom
25
+
26
+ O primeiro nível pode levar um ícone do lucide. BreadcrumbSeparator aceita children pra trocar o
27
+ chevron padrão por outro glifo (aqui, uma barra).
28
+
29
+ ```tsx preview col-start
30
+ <Breadcrumb>
31
+ <BreadcrumbList>
32
+ <BreadcrumbItem>
33
+ <BreadcrumbLink href="#" className="flex items-center gap-1.5">
34
+ <Home className="size-3.5" />
35
+ Workspaces
36
+ </BreadcrumbLink>
37
+ </BreadcrumbItem>
38
+ <BreadcrumbSeparator>
39
+ <Slash />
40
+ </BreadcrumbSeparator>
41
+ <BreadcrumbItem>
42
+ <BreadcrumbLink href="#">Empresa X</BreadcrumbLink>
43
+ </BreadcrumbItem>
44
+ <BreadcrumbSeparator>
45
+ <Slash />
46
+ </BreadcrumbSeparator>
47
+ <BreadcrumbItem>
48
+ <BreadcrumbPage>Agentes</BreadcrumbPage>
49
+ </BreadcrumbItem>
50
+ </BreadcrumbList>
51
+ </Breadcrumb>
52
+ ```
53
+
54
+ ## Colapsado
55
+
56
+ Trilha funda demais pro espaço: BreadcrumbEllipsis substitui os níveis do meio (que viram um
57
+ menu/popover) e mantém só a raiz e o destino.
58
+
59
+ ```tsx preview col-start
60
+ <Breadcrumb>
61
+ <BreadcrumbList>
62
+ <BreadcrumbItem>
63
+ <BreadcrumbLink href="#">Empresa X</BreadcrumbLink>
64
+ </BreadcrumbItem>
65
+ <BreadcrumbSeparator />
66
+ <BreadcrumbItem>
67
+ <BreadcrumbEllipsis />
68
+ </BreadcrumbItem>
69
+ <BreadcrumbSeparator />
70
+ <BreadcrumbItem>
71
+ <BreadcrumbLink href="#">empresa-x-web</BreadcrumbLink>
72
+ </BreadcrumbItem>
73
+ <BreadcrumbSeparator />
74
+ <BreadcrumbItem>
75
+ <BreadcrumbPage>Skill clean-code</BreadcrumbPage>
76
+ </BreadcrumbItem>
77
+ </BreadcrumbList>
78
+ </Breadcrumb>
79
+ ```
80
+
81
+ ## Props
82
+
83
+ | Prop | Tipo | Default | Descrição |
84
+ |---|---|---|---|
85
+ | `asChild` (BreadcrumbLink) | `boolean` | `false` | Funde as props no filho (via Slot) — use pra integrar o Link do seu roteador no lugar do `<a>` nativo. |
86
+ | `href` (BreadcrumbLink) | `string` | | Destino do nível navegável — o que o consumidor decide por item. |
87
+ | `children` (BreadcrumbSeparator) | `React.ReactNode` | `<ChevronRight />` | Glifo entre os itens. Omita pro chevron padrão ou passe outro ícone. |
@@ -0,0 +1,71 @@
1
+ ## Básico
2
+
3
+ Envolva os botões no ButtonGroup: as bordas internas colapsam e só as pontas ficam arredondadas.
4
+ Use a mesma variant nos filhos pra manter a barra uniforme.
5
+
6
+ ```tsx preview
7
+ <ButtonGroup>
8
+ <Button variant="outline">Visão geral</Button>
9
+ <Button variant="outline">Sessões</Button>
10
+ <Button variant="outline">Skills</Button>
11
+ </ButtonGroup>
12
+ ```
13
+
14
+ ## Ação dividida
15
+
16
+ O padrão da casa pra ação primária + variações: o botão principal, um separador e um gatilho de
17
+ menu (size=icon).
18
+
19
+ ```tsx preview
20
+ <ButtonGroup>
21
+ <Button icon={Play}>Rodar agente developer</Button>
22
+ <ButtonGroupSeparator />
23
+ <Button size="icon" aria-label="Mais opções">
24
+ <ChevronDown />
25
+ </Button>
26
+ </ButtonGroup>
27
+ ```
28
+
29
+ ## Com rótulo
30
+
31
+ ButtonGroupText cola um rótulo (fundo muted) ao grupo — bom pra prefixar o contexto. Aceita ícone
32
+ via [&_svg] e asChild pra virar `<label>`.
33
+
34
+ ```tsx preview
35
+ <ButtonGroup>
36
+ <ButtonGroupText>
37
+ <GitBranch />
38
+ empresa-x-api
39
+ </ButtonGroupText>
40
+ <Button variant="outline" icon={RotateCw}>
41
+ Sincronizar
42
+ </Button>
43
+ </ButtonGroup>
44
+ ```
45
+
46
+ ## Vertical
47
+
48
+ orientation=vertical empilha os filhos em coluna — as bordas que colapsam passam a ser as de
49
+ cima/baixo. Combina com size=icon pra uma mini-toolbar.
50
+
51
+ ```tsx preview col-start
52
+ <ButtonGroup orientation="vertical">
53
+ <Button variant="outline" size="icon" aria-label="Rodar sessão">
54
+ <Play />
55
+ </Button>
56
+ <Button variant="outline" size="icon" aria-label="Pausar sessão">
57
+ <Pause />
58
+ </Button>
59
+ <Button variant="outline" size="icon" aria-label="Reiniciar sessão">
60
+ <RotateCw />
61
+ </Button>
62
+ </ButtonGroup>
63
+ ```
64
+
65
+ ## Props
66
+
67
+ | Prop | Tipo | Default | Descrição |
68
+ |---|---|---|---|
69
+ | `orientation` (ButtonGroup) | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção do bloco — vertical empilha os filhos em coluna. |
70
+ | `orientation` (ButtonGroupSeparator) | `'horizontal' \| 'vertical'` | `'vertical'` | Direção do traço divisor — herde do grupo (vertical no horizontal). |
71
+ | `asChild` (ButtonGroupText) | `boolean` | `false` | Renderiza como o filho (Radix Slot) — ex.: virar `<label>` sem forkar o estilo. |
@@ -0,0 +1,60 @@
1
+ ## Variantes
2
+
3
+ variant é intenção, não decoração: default pra ação principal (uma por tela), destructive só
4
+ quando destrói de verdade.
5
+
6
+ ```tsx preview
7
+ <Button>Salvar</Button>
8
+ <Button variant="secondary">Duplicar</Button>
9
+ <Button variant="outline">Cancelar</Button>
10
+ <Button variant="ghost">Detalhes</Button>
11
+ <Button variant="destructive">Excluir</Button>
12
+ <Button variant="link">Ver documentação</Button>
13
+ ```
14
+
15
+ ## Tamanhos
16
+
17
+ size icon exige aria-label (não tem texto pra ler). Os só-ícone vêm em três: `icon` (36px), `icon-sm` (32px, pra fileira densa como o header) e `icon-xs` (24px, pra ação DENTRO de um campo — o `trailing` do Input/Select).
18
+
19
+ ```tsx preview
20
+ <Button size="sm">Pequeno</Button>
21
+ <Button>Padrão</Button>
22
+ <Button size="lg">Grande</Button>
23
+ <Button size="icon" aria-label="Novo"><Plus /></Button>
24
+ <Button size="icon-sm" variant="ghost" aria-label="Novo"><Plus /></Button>
25
+ <Button size="icon-xs" variant="ghost" aria-label="Novo"><Plus /></Button>
26
+ ```
27
+
28
+ ## Estados
29
+
30
+ busy = ação em andamento (DEPOIS do clique): o Spinner e o disabled vêm do botão. Com icon, o
31
+ Spinner TROCA o ícone (não soma). Não confunda com carregar conteúdo (ANTES) — isso é Spinner
32
+ centralizado/Skeleton num nível de página.
33
+
34
+ ```tsx preview
35
+ <Button disabled>Desabilitado</Button>
36
+ <Button busy>Salvar</Button>
37
+ <Button icon={Plus}>Novo</Button>
38
+ <Button icon={Plus} busy>Novo</Button>
39
+ ```
40
+
41
+ ## Como outro elemento (asChild)
42
+
43
+ Âncora com cara de botão: asChild renderiza o filho (Radix Slot) — sem forkar estilo.
44
+ buttonVariants serve pro caso sem filho único.
45
+
46
+ ```tsx preview
47
+ <Button asChild variant="outline">
48
+ <a href="#" onClick={(e) => e.preventDefault()}>Abrir documentação</a>
49
+ </Button>
50
+ ```
51
+
52
+ ## Props
53
+
54
+ | Prop | Tipo | Default | Descrição |
55
+ |---|---|---|---|
56
+ | `variant` | `'default' \| 'secondary' \| 'outline' \| 'ghost' \| 'destructive' \| 'link'` | `'default'` | A intenção da ação — define cor e peso visual. |
57
+ | `size` | `'default' \| 'sm' \| 'lg' \| 'icon' \| 'icon-sm' \| 'icon-xs'` | `'default'` | O tamanho. Os icon* são quadrados (36/32/24px), pra botão só de ícone (com aria-label). |
58
+ | `asChild` | `boolean` | `false` | Renderiza como o filho (Radix Slot) em vez de `<button>` — pra âncoras e afins. |
59
+ | `busy` | `boolean` | `false` | Ação em andamento (depois do clique): mostra Spinner + desabilita. Não é "carregando" de conteúdo (que é Spinner/Skeleton num nível de página). |
60
+ | `icon` | `React.ElementType` | | Ícone à esquerda (ex.: icon={Plus}). No busy é trocado pelo Spinner — não soma. |
@@ -0,0 +1,62 @@
1
+ ## Dia único
2
+
3
+ mode=single guarda uma Date — controle por selected/onSelect. defaultMonth abre o calendário no mês certo sem mexer na seleção.
4
+
5
+ ```tsx preview
6
+ const [day, setDay] = useState<Date | undefined>(new Date(2026, 5, 22))
7
+
8
+ render(
9
+ <Calendar
10
+ mode="single"
11
+ selected={day}
12
+ onSelect={setDay}
13
+ defaultMonth={new Date(2026, 5, 1)}
14
+ />,
15
+ )
16
+ ```
17
+
18
+ ## Intervalo
19
+
20
+ mode=range guarda { from, to } — o padrão pra filtrar sessões por janela de datas. O primeiro clique fixa o início; o segundo, o fim.
21
+
22
+ ```tsx preview
23
+ const [week, setWeek] = useState<DateRange | undefined>({
24
+ from: new Date(2026, 5, 15),
25
+ to: new Date(2026, 5, 19),
26
+ })
27
+
28
+ render(
29
+ <Calendar
30
+ mode="range"
31
+ selected={week}
32
+ onSelect={setWeek}
33
+ defaultMonth={new Date(2026, 5, 1)}
34
+ />,
35
+ )
36
+ ```
37
+
38
+ ## Navegação por dropdown
39
+
40
+ captionLayout=dropdown troca o título do mês por seletores de mês e ano — bom pra pular pra um período distante (ex.: histórico de um repositório) sem clicar mês a mês.
41
+
42
+ ```tsx preview
43
+ <Calendar
44
+ mode="single"
45
+ captionLayout="dropdown"
46
+ defaultMonth={new Date(2026, 5, 1)}
47
+ />
48
+ ```
49
+
50
+ ## Props
51
+
52
+ | Prop | Tipo | Default | Descrição |
53
+ |---|---|---|---|
54
+ | `mode` | `'single' \| 'multiple' \| 'range'` | | O tipo de seleção — define o formato de selected/onSelect (Date, Date[] ou { from, to }). |
55
+ | `selected` | `Date \| Date[] \| DateRange` | | A seleção atual no modo controlado — o formato segue o mode. Pareie com onSelect. |
56
+ | `onSelect` | `(selected) => void` | | Chamado quando o usuário escolhe uma data. O argumento segue o mode. |
57
+ | `defaultMonth` | `Date` | | O mês exibido ao montar, sem afetar a seleção. |
58
+ | `captionLayout` | `'label' \| 'dropdown' \| 'dropdown-months' \| 'dropdown-years'` | `'label'` | Como o título do mês aparece — label é texto fixo; dropdown vira seletores de mês e ano. |
59
+ | `numberOfMonths` | `number` | `1` | Quantos meses mostrar lado a lado — útil pra escolher um intervalo longo. |
60
+ | `disabled` | `Matcher` | | Datas não selecionáveis — uma Date, um array, um { from, to } ou um predicado (date) => boolean. |
61
+ | `buttonVariant` | `Button['variant']` | `'ghost'` | A variante dos botões de navegação (anterior/próximo). |
62
+ | `showOutsideDays` | `boolean` | `true` | Mostra os dias do mês vizinho que completam a primeira e a última semana. |
@@ -0,0 +1,49 @@
1
+ ## Box simples
2
+
3
+ O uso mais comum: a superfície de conteúdo em repouso (rounded-card + ring-edge + shadow-card — ver Tokens & Tema → Elevação por papel) com o padding pelo className. O Card não força flex/gap/padding — você compõe o miolo (sem brigar com space-y/flex seu).
4
+
5
+ ```tsx preview col
6
+ <Card className="p-4">
7
+ <p className="text-sm text-muted-foreground">3 sessões ativas neste momento.</p>
8
+ </Card>
9
+ ```
10
+
11
+ ## Estruturado (header / conteúdo / rodapé)
12
+
13
+ Pra painel com estrutura: cada slot é dono do próprio padding (como o Dialog). CardTitle/CardDescription no header; CardFooter alinha as ações.
14
+
15
+ ```tsx preview col
16
+ <Card>
17
+ <CardHeader>
18
+ <CardTitle>Empresa X</CardTitle>
19
+ <CardDescription>2 repositórios · 3 agentes vinculados.</CardDescription>
20
+ </CardHeader>
21
+ <CardContent>
22
+ <p className="text-sm text-muted-foreground">Última sessão concluída há 2 horas.</p>
23
+ </CardContent>
24
+ <CardFooter className="gap-2">
25
+ <Button size="sm">Abrir sessão</Button>
26
+ <Button size="sm" variant="outline">Ver repositórios</Button>
27
+ </CardFooter>
28
+ </Card>
29
+ ```
30
+
31
+ ## Com divisor
32
+
33
+ border-b no header / border-t no footer dão o divisor quando a estrutura pede separação visível. A aresta externa é o anel (`ring-edge`), não uma borda — divergência declarada vs shadcn (border + shadow-sm); os divisores internos seguem sendo `border`.
34
+
35
+ ```tsx preview col
36
+ <Card>
37
+ <CardHeader className="border-b">
38
+ <CardTitle>Skill code-style</CardTitle>
39
+ <CardDescription>Padrão softize de código e microcopy.</CardDescription>
40
+ </CardHeader>
41
+ <CardContent>
42
+ <p className="text-sm text-muted-foreground">Vinculada a 4 agentes neste workspace.</p>
43
+ </CardContent>
44
+ <CardFooter className="justify-between border-t">
45
+ <Badge variant="success">Publicada</Badge>
46
+ <Button size="sm" variant="outline">Editar skill</Button>
47
+ </CardFooter>
48
+ </Card>
49
+ ```