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