@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,33 @@
1
+ import * as React from "react"
2
+ import { Switch as SwitchPrimitive } from "radix-ui"
3
+
4
+ import { cn } from '../../lib/cn.ts'
5
+
6
+ function Switch({
7
+ className,
8
+ size = "default",
9
+ ...props
10
+ }: React.ComponentProps<typeof SwitchPrimitive.Root> & {
11
+ size?: "sm" | "default"
12
+ }) {
13
+ return (
14
+ <SwitchPrimitive.Root
15
+ data-slot="switch"
16
+ data-size={size}
17
+ className={cn(
18
+ "peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",
19
+ className
20
+ )}
21
+ {...props}
22
+ >
23
+ <SwitchPrimitive.Thumb
24
+ data-slot="switch-thumb"
25
+ className={cn(
26
+ "pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground"
27
+ )}
28
+ />
29
+ </SwitchPrimitive.Root>
30
+ )
31
+ }
32
+
33
+ export { Switch }
@@ -0,0 +1,114 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from '../../lib/cn.ts'
4
+
5
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
6
+ return (
7
+ <div
8
+ data-slot="table-container"
9
+ className="relative w-full overflow-x-auto"
10
+ >
11
+ <table
12
+ data-slot="table"
13
+ className={cn("w-full caption-bottom text-sm", className)}
14
+ {...props}
15
+ />
16
+ </div>
17
+ )
18
+ }
19
+
20
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
21
+ return (
22
+ <thead
23
+ data-slot="table-header"
24
+ className={cn("[&_tr]:border-b", className)}
25
+ {...props}
26
+ />
27
+ )
28
+ }
29
+
30
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
31
+ return (
32
+ <tbody
33
+ data-slot="table-body"
34
+ className={cn("[&_tr:last-child]:border-0", className)}
35
+ {...props}
36
+ />
37
+ )
38
+ }
39
+
40
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
41
+ return (
42
+ <tfoot
43
+ data-slot="table-footer"
44
+ className={cn(
45
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
54
+ return (
55
+ <tr
56
+ data-slot="table-row"
57
+ className={cn(
58
+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
59
+ className
60
+ )}
61
+ {...props}
62
+ />
63
+ )
64
+ }
65
+
66
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
67
+ return (
68
+ <th
69
+ data-slot="table-head"
70
+ className={cn(
71
+ "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
72
+ className
73
+ )}
74
+ {...props}
75
+ />
76
+ )
77
+ }
78
+
79
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
80
+ return (
81
+ <td
82
+ data-slot="table-cell"
83
+ className={cn(
84
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
85
+ className
86
+ )}
87
+ {...props}
88
+ />
89
+ )
90
+ }
91
+
92
+ function TableCaption({
93
+ className,
94
+ ...props
95
+ }: React.ComponentProps<"caption">) {
96
+ return (
97
+ <caption
98
+ data-slot="table-caption"
99
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
100
+ {...props}
101
+ />
102
+ )
103
+ }
104
+
105
+ export {
106
+ Table,
107
+ TableHeader,
108
+ TableBody,
109
+ TableFooter,
110
+ TableHead,
111
+ TableRow,
112
+ TableCell,
113
+ TableCaption,
114
+ }
@@ -0,0 +1,104 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { Tabs as TabsPrimitive } from "radix-ui"
4
+
5
+ import { cn } from '../../lib/cn.ts'
6
+
7
+ // Divergência da casa (ejetado): o Tabs ganhou `size` — Button e Select têm `sm`, o Tabs
8
+ // não tinha, e uma fileira densa (o header do Maestro) ficava com o segmento 4px mais alto
9
+ // que os irmãos. O tamanho flui pra TabsList via contexto (a altura é dela).
10
+ type TabsSize = "default" | "sm"
11
+ const TabsSizeContext = React.createContext<TabsSize>("default")
12
+
13
+ function Tabs({
14
+ className,
15
+ orientation = "horizontal",
16
+ size = "default",
17
+ ...props
18
+ }: React.ComponentProps<typeof TabsPrimitive.Root> & { size?: TabsSize }) {
19
+ return (
20
+ <TabsSizeContext.Provider value={size}>
21
+ <TabsPrimitive.Root
22
+ data-slot="tabs"
23
+ data-orientation={orientation}
24
+ data-size={size}
25
+ orientation={orientation}
26
+ className={cn(
27
+ "group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
28
+ className
29
+ )}
30
+ {...props}
31
+ />
32
+ </TabsSizeContext.Provider>
33
+ )
34
+ }
35
+
36
+ const tabsListVariants = cva(
37
+ // Altura: horizontal segue o size (h-9 default / h-8 sm — combina `data-[size]` da PRÓPRIA
38
+ // list com o `group-data-[orientation]` do ancestral); vertical é h-fit em qualquer size.
39
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:data-[size=default]:h-9 group-data-[orientation=horizontal]/tabs:data-[size=sm]:h-8 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
40
+ {
41
+ variants: {
42
+ variant: {
43
+ default: "bg-muted",
44
+ line: "gap-1 bg-transparent",
45
+ },
46
+ },
47
+ defaultVariants: {
48
+ variant: "default",
49
+ },
50
+ }
51
+ )
52
+
53
+ function TabsList({
54
+ className,
55
+ variant = "default",
56
+ ...props
57
+ }: React.ComponentProps<typeof TabsPrimitive.List> &
58
+ VariantProps<typeof tabsListVariants>) {
59
+ // O size vem do <Tabs> (contexto) — a altura mora aqui, na list.
60
+ const size = React.useContext(TabsSizeContext)
61
+ return (
62
+ <TabsPrimitive.List
63
+ data-slot="tabs-list"
64
+ data-variant={variant}
65
+ data-size={size}
66
+ className={cn(tabsListVariants({ variant }), className)}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function TabsTrigger({
73
+ className,
74
+ ...props
75
+ }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
76
+ return (
77
+ <TabsPrimitive.Trigger
78
+ data-slot="tabs-trigger"
79
+ className={cn(
80
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
81
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
82
+ "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground",
83
+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
84
+ className
85
+ )}
86
+ {...props}
87
+ />
88
+ )
89
+ }
90
+
91
+ function TabsContent({
92
+ className,
93
+ ...props
94
+ }: React.ComponentProps<typeof TabsPrimitive.Content>) {
95
+ return (
96
+ <TabsPrimitive.Content
97
+ data-slot="tabs-content"
98
+ className={cn("flex-1 outline-none", className)}
99
+ {...props}
100
+ />
101
+ )
102
+ }
103
+
104
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
@@ -0,0 +1,18 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from '../../lib/cn.ts'
4
+
5
+ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
+ return (
7
+ <textarea
8
+ data-slot="textarea"
9
+ className={cn(
10
+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ export { Textarea }
@@ -0,0 +1,81 @@
1
+ import * as React from "react"
2
+ import { type VariantProps } from "class-variance-authority"
3
+ import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
4
+
5
+ import { cn } from '../../lib/cn.ts'
6
+ import { toggleVariants } from './toggle.tsx'
7
+
8
+ const ToggleGroupContext = React.createContext<
9
+ VariantProps<typeof toggleVariants> & {
10
+ spacing?: number
11
+ }
12
+ >({
13
+ size: "default",
14
+ variant: "default",
15
+ spacing: 0,
16
+ })
17
+
18
+ function ToggleGroup({
19
+ className,
20
+ variant,
21
+ size,
22
+ spacing = 0,
23
+ children,
24
+ ...props
25
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
26
+ VariantProps<typeof toggleVariants> & {
27
+ spacing?: number
28
+ }) {
29
+ return (
30
+ <ToggleGroupPrimitive.Root
31
+ data-slot="toggle-group"
32
+ data-variant={variant}
33
+ data-size={size}
34
+ data-spacing={spacing}
35
+ style={{ "--gap": spacing } as React.CSSProperties}
36
+ className={cn(
37
+ "group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md",
38
+ className
39
+ )}
40
+ {...props}
41
+ >
42
+ <ToggleGroupContext.Provider value={{ variant, size, spacing }}>
43
+ {children}
44
+ </ToggleGroupContext.Provider>
45
+ </ToggleGroupPrimitive.Root>
46
+ )
47
+ }
48
+
49
+ function ToggleGroupItem({
50
+ className,
51
+ children,
52
+ variant,
53
+ size,
54
+ ...props
55
+ }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
56
+ VariantProps<typeof toggleVariants>) {
57
+ const context = React.useContext(ToggleGroupContext)
58
+
59
+ return (
60
+ <ToggleGroupPrimitive.Item
61
+ data-slot="toggle-group-item"
62
+ data-variant={context.variant || variant}
63
+ data-size={context.size || size}
64
+ data-spacing={context.spacing}
65
+ className={cn(
66
+ toggleVariants({
67
+ variant: context.variant || variant,
68
+ size: context.size || size,
69
+ }),
70
+ "w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10",
71
+ "data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
72
+ className
73
+ )}
74
+ {...props}
75
+ >
76
+ {children}
77
+ </ToggleGroupPrimitive.Item>
78
+ )
79
+ }
80
+
81
+ export { ToggleGroup, ToggleGroupItem }
@@ -0,0 +1,45 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { Toggle as TogglePrimitive } from "radix-ui"
4
+
5
+ import { cn } from '../../lib/cn.ts'
6
+
7
+ const toggleVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-transparent",
13
+ outline:
14
+ "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
15
+ },
16
+ size: {
17
+ default: "h-9 min-w-9 px-2",
18
+ sm: "h-8 min-w-8 px-1.5",
19
+ lg: "h-10 min-w-10 px-2.5",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ size: "default",
25
+ },
26
+ }
27
+ )
28
+
29
+ function Toggle({
30
+ className,
31
+ variant,
32
+ size,
33
+ ...props
34
+ }: React.ComponentProps<typeof TogglePrimitive.Root> &
35
+ VariantProps<typeof toggleVariants>) {
36
+ return (
37
+ <TogglePrimitive.Root
38
+ data-slot="toggle"
39
+ className={cn(toggleVariants({ variant, size, className }))}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ export { Toggle, toggleVariants }
@@ -0,0 +1,55 @@
1
+ import * as React from "react"
2
+ import { Tooltip as TooltipPrimitive } from "radix-ui"
3
+
4
+ import { cn } from '../../lib/cn.ts'
5
+
6
+ function TooltipProvider({
7
+ delayDuration = 0,
8
+ ...props
9
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
10
+ return (
11
+ <TooltipPrimitive.Provider
12
+ data-slot="tooltip-provider"
13
+ delayDuration={delayDuration}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ function Tooltip({
20
+ ...props
21
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
23
+ }
24
+
25
+ function TooltipTrigger({
26
+ ...props
27
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
28
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
29
+ }
30
+
31
+ function TooltipContent({
32
+ className,
33
+ sideOffset = 0,
34
+ children,
35
+ ...props
36
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
37
+ return (
38
+ <TooltipPrimitive.Portal>
39
+ <TooltipPrimitive.Content
40
+ data-slot="tooltip-content"
41
+ sideOffset={sideOffset}
42
+ className={cn(
43
+ "z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
44
+ className
45
+ )}
46
+ {...props}
47
+ >
48
+ {children}
49
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
50
+ </TooltipPrimitive.Content>
51
+ </TooltipPrimitive.Portal>
52
+ )
53
+ }
54
+
55
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -0,0 +1,49 @@
1
+ import * as React from 'react'
2
+ import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip.tsx'
3
+ import { cn } from '../../lib/cn.ts'
4
+
5
+ export interface TruncateProps extends React.ComponentProps<'span'> {
6
+ /** Conteúdo do tooltip quando transborda. Default: os próprios children. */
7
+ tooltip?: React.ReactNode
8
+ }
9
+
10
+ /**
11
+ * Texto truncado com tooltip SÓ quando transborda de verdade (medição do overflow:
12
+ * `scrollWidth > clientWidth`, re-medido em resize). Substitui a composição
13
+ * `block truncate` + `title` sempre presente — title em texto que não corta é ruído.
14
+ * Requer `<TooltipProvider>` na raiz do app (o esqueleto do `opus create` já monta).
15
+ */
16
+ export function Truncate({ tooltip, children, className, ...props }: TruncateProps): React.ReactElement {
17
+ const ref = React.useRef<HTMLSpanElement>(null)
18
+ const [overflowing, setOverflowing] = React.useState(false)
19
+
20
+ const measure = React.useCallback(() => {
21
+ const el = ref.current
22
+ if (el === null) return
23
+ setOverflowing(el.scrollWidth > el.clientWidth)
24
+ }, [])
25
+
26
+ React.useLayoutEffect(() => {
27
+ measure()
28
+ const el = ref.current
29
+ if (el === null || typeof ResizeObserver === 'undefined') return
30
+ const ro = new ResizeObserver(measure)
31
+ ro.observe(el)
32
+ return () => ro.disconnect()
33
+ // children na dependência: texto novo re-mede (o RO só vê mudança de CAIXA).
34
+ }, [measure, children])
35
+
36
+ const span = (
37
+ <span ref={ref} data-slot="truncate" className={cn('block truncate', className)} {...props}>
38
+ {children}
39
+ </span>
40
+ )
41
+
42
+ if (!overflowing) return span
43
+ return (
44
+ <Tooltip>
45
+ <TooltipTrigger asChild>{span}</TooltipTrigger>
46
+ <TooltipContent>{tooltip ?? children}</TooltipContent>
47
+ </Tooltip>
48
+ )
49
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * DocBrowser — a VERDADE VISUAL da casa, como doc completa. Seção multi-página: conceitos
3
+ * (Introdução, Tokens & Tema, Microcopy, Customização) + uma página por componente do
4
+ * opus/ui, agrupados por seção. O que você vê aqui é o que os apps entregam (o site
5
+ * do shadcn é catálogo do que existe; a expectativa visual nasce aqui).
6
+ *
7
+ * Dois modos de roteamento:
8
+ * - CONTROLADO (`path` vem de fora): o host (ex.: Maestro) é dono do path; o DocBrowser
9
+ * só lê e navega.
10
+ * - STANDALONE (`path` ausente): o path vem do router da casa — é o modo da rota dev
11
+ * `/__docs` de cada app (a doc com a identidade do projeto).
12
+ * Nos dois, navegar emite `popstate`: host que guarda o path por conta própria acompanha.
13
+ * `basePath` adapta o prefixo das URLs (`/docs` no Maestro, `/__docs` nos apps).
14
+ */
15
+ import { useEffect } from 'react'
16
+ import { DOC_SECTIONS, type DocSection, type DocEntry } from './registry'
17
+ import { SectionShell, type SectionNavGroup } from '../components/patterns/section-shell.tsx'
18
+ import { navigate, usePathname } from '../router.ts'
19
+
20
+ function findPage(sections: DocSection[], slug: string): DocEntry | undefined {
21
+ for (const s of sections) {
22
+ for (const g of s.groups) {
23
+ const p = g.pages.find((p) => p.slug === slug)
24
+ if (p) return p
25
+ }
26
+ }
27
+ return undefined
28
+ }
29
+
30
+ export interface DocBrowserProps {
31
+ /** Prefixo das rotas da doc. Default `/docs` (Maestro); `/__docs` na rota dev dos apps. */
32
+ basePath?: string
33
+ /** Path controlado pelo host (modo Maestro). Ausente = standalone (gerencia o próprio). */
34
+ path?: string
35
+ /** Seções a exibir (seção → grupo → página). Default = catálogo inteiro. */
36
+ sections?: DocSection[]
37
+ /** Monta o `document.title` a partir do título da página atual (ex.: `(t) => `${t} · Opus``).
38
+ * Ausente = não mexe no title. */
39
+ documentTitle?: (pageTitle: string) => string
40
+ }
41
+
42
+ export function DocBrowser({
43
+ basePath = '/docs',
44
+ path: controlledPath,
45
+ sections = DOC_SECTIONS,
46
+ documentTitle,
47
+ }: DocBrowserProps): React.ReactElement {
48
+ // Standalone: o path vem do router da casa (`usePathname` — pathname É o estado).
49
+ // Controlado: o host manda, e este valor é ignorado. O hook roda sempre (regra dos
50
+ // hooks); o que muda é quem vence no `??`.
51
+ const livePath = usePathname()
52
+ const path = controlledPath ?? livePath
53
+ // Default = 1ª página do 1º grupo da 1ª seção (a doc abre na 1ª página).
54
+ const defaultSlug = sections[0]?.groups[0]?.pages[0]?.slug ?? ''
55
+ // Slug = TODOS os segmentos após o basePath, unidos (rota aninhada: `/docs/guias/deploy`
56
+ // → `guias/deploy`). Slug flat de 1 segmento continua igual (`join` de 1 elemento = ele).
57
+ const rel = path.startsWith(basePath) ? path.slice(basePath.length) : ''
58
+ const slug = rel.split('/').filter(Boolean).join('/') || defaultSlug
59
+ const page = findPage(sections, slug) ?? findPage(sections, defaultSlug)
60
+
61
+ // `navigate` emite `popstate`, então o host é notificado nos dois modos — inclusive o
62
+ // que ainda escuta o evento na unha, que era como este componente avisava antes.
63
+ const go = (to: string): void => navigate(to)
64
+
65
+ // O title da aba acompanha a página atual (quando o host fornece o formato).
66
+ useEffect(() => {
67
+ if (page && documentTitle) document.title = documentTitle(page.title)
68
+ }, [page, documentTitle])
69
+
70
+ // Seção → grupo, e os grupos da seção → subgrupos. O tier do meio NÃO pode ser
71
+ // achatado: `docSectionsFromFolder` o preenche a partir de sub-pasta (ou do
72
+ // frontmatter `group:`), que é o caminho do `opusDocs({ source })`.
73
+ const navGroups: SectionNavGroup[] = sections.map((section) => ({
74
+ label: section.label,
75
+ subgroups: section.groups.map((g) => ({
76
+ label: g.label,
77
+ items: g.pages.map((p) => ({ id: p.slug, label: p.title, badge: p.badge })),
78
+ })),
79
+ }))
80
+
81
+ return (
82
+ <SectionShell
83
+ groups={navGroups}
84
+ activeId={page?.slug}
85
+ onSelect={(slug) => go(`${basePath}/${slug}`)}
86
+ >
87
+ <div className="mx-auto max-w-3xl px-8 py-8">{page?.render()}</div>
88
+ </SectionShell>
89
+ )
90
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ChangelogView — apresentação do CHANGELOG.md do pacote (a MESMA fonte que viaja em
3
+ * node_modules): linha do tempo por versão, data ao lado, e a seção Breaking em
4
+ * destaque (badge + contêiner) — o leitor que pula versões acha as quebras no olho.
5
+ * O parser é convenção-fina sobre o markdown (`## <versão> — <data>` + `### Breaking`);
6
+ * o corpo de cada trecho segue renderizado pelo DocMarkdown (fidelidade total).
7
+ */
8
+ import { Badge } from '../react.tsx'
9
+ import { DocMarkdown } from './markdown.tsx'
10
+ // A fonte é o CHANGELOG DO PACOTE (raiz) — a mesma que viaja em node_modules.
11
+ import changelogMd from '../../../CHANGELOG.md?raw'
12
+
13
+ export interface ChangelogVersion {
14
+ version: string
15
+ date?: string
16
+ body: string
17
+ breaking?: string
18
+ }
19
+
20
+ export interface ParsedChangelog {
21
+ intro: string
22
+ versions: ChangelogVersion[]
23
+ }
24
+
25
+ /** Parseia o changelog: intro (antes do 1º `## `) + versões (`## x — data`, `### Breaking`). */
26
+ export function parseChangelog(source: string): ParsedChangelog {
27
+ const chunks = `\n${source.trim()}`.split(/\n## /)
28
+ const intro = chunks[0]?.trim() ?? ''
29
+ const versions: ChangelogVersion[] = []
30
+ for (const chunk of chunks.slice(1)) {
31
+ const nl = chunk.indexOf('\n')
32
+ const header = (nl === -1 ? chunk : chunk.slice(0, nl)).trim()
33
+ const rest = nl === -1 ? '' : chunk.slice(nl + 1)
34
+ const [version, date] = header.split(/\s+—\s+/)
35
+ const [body, breaking] = rest.split(/\n### Breaking\s*\n/)
36
+ versions.push({
37
+ version: (version ?? header).trim(),
38
+ ...(date !== undefined ? { date: date.trim() } : {}),
39
+ body: (body ?? '').trim(),
40
+ ...(breaking !== undefined ? { breaking: breaking.trim() } : {}),
41
+ })
42
+ }
43
+ return { intro, versions }
44
+ }
45
+
46
+ export function ChangelogView({ source }: { source: string }): React.ReactElement {
47
+ const { intro, versions } = parseChangelog(source)
48
+ return (
49
+ <div>
50
+ {intro.length > 0 && <DocMarkdown content={intro} />}
51
+ <div className="mt-10 space-y-10">
52
+ {versions.map((v) => (
53
+ <section key={v.version} className="border-t border-border/60 pt-6">
54
+ <div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
55
+ <h2 className="text-xl font-semibold tracking-tight">{v.version}</h2>
56
+ {v.date !== undefined && <span className="text-sm text-muted-foreground">{v.date}</span>}
57
+ {v.breaking !== undefined && <Badge variant="destructive">Breaking</Badge>}
58
+ </div>
59
+ {v.body.length > 0 && (
60
+ <div className="mt-3">
61
+ <DocMarkdown content={v.body} />
62
+ </div>
63
+ )}
64
+ {v.breaking !== undefined && (
65
+ <div className="mt-4 rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3">
66
+ <div className="mb-2 text-xs font-medium text-destructive">Breaking — o que fazer</div>
67
+ <DocMarkdown content={v.breaking} />
68
+ </div>
69
+ )}
70
+ </section>
71
+ ))}
72
+ </div>
73
+ </div>
74
+ )
75
+ }
76
+
77
+ /** O changelog do Opus pronto pra montar (site: página de topo; Maestro: página da doc). */
78
+ export function OpusChangelog(): React.ReactElement {
79
+ return <ChangelogView source={changelogMd} />
80
+ }