@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,121 @@
1
+ import * as React from 'react'
2
+ import { SendHorizontalIcon } from 'lucide-react'
3
+ import { cn } from '../../lib/cn.ts'
4
+ import { Button } from './button.tsx'
5
+ import { Spinner } from './spinner.tsx'
6
+ import { Textarea } from './textarea.tsx'
7
+
8
+ export interface ComposerProps {
9
+ /** Texto (controlado): o dono do estado é o consumidor — o `<Chat>` por dentro, ou o app
10
+ * (ex.: o composer de criação de sessão do Maestro, que nem tem histórico). */
11
+ value: string
12
+ onChange: (value: string) => void
13
+ /** Enter (sem Shift) ou o botão enviar. Só dispara quando dá pra enviar (ver `submitDisabled`). */
14
+ onSubmit: () => void
15
+ /** Trava o composer enquanto o turno/ação corre — o enviar vira spinner. */
16
+ busy?: boolean
17
+ /** Gate EXTRA de envio além de "vazio" e "busy" (ex.: falta escolher o app). Desabilita o
18
+ * enviar sem esconder a barra. */
19
+ submitDisabled?: boolean
20
+ placeholder?: string
21
+ rows?: number
22
+ autoFocus?: boolean
23
+ /** Controles discretos na barra de ações, à ESQUERDA do enviar (seletores de app, agente,
24
+ * contexto…). Presente → o composer vira DUAS LINHAS (textarea em cima, barra limpa
25
+ * embaixo); ausente → linha única (textarea + enviar inline), o composer clássico do Chat.
26
+ * Estilize os controles como discretos (ghost/sem borda) — a barra não é um formulário. */
27
+ actions?: React.ReactNode
28
+ className?: string
29
+ }
30
+
31
+ /**
32
+ * <Composer /> — a caixa de escrever da casa: textarea (Enter envia / Shift+Enter quebra
33
+ * linha) numa pílula elevada (rounded-card + ring-edge + shadow-card), com o enviar dentro.
34
+ * É o composer do `<Chat>` extraído pra ser reusável SOZINHO — o Maestro cria sessão com
35
+ * ele (sem chat nenhum), e a GB prende seletores nele. Com `actions`, ganha uma barra
36
+ * embaixo pros seletores; sem, é a linha única de sempre.
37
+ */
38
+ export function Composer({
39
+ value,
40
+ onChange,
41
+ onSubmit,
42
+ busy = false,
43
+ submitDisabled = false,
44
+ placeholder = 'Escreva uma mensagem…',
45
+ rows = 1,
46
+ autoFocus,
47
+ actions,
48
+ className,
49
+ }: ComposerProps): React.ReactElement {
50
+ const canSubmit = value.trim() !== '' && !busy && !submitDisabled
51
+ const fire = (): void => {
52
+ if (canSubmit) onSubmit()
53
+ }
54
+ const twoRow = actions !== undefined
55
+
56
+ const textarea = (
57
+ <Textarea
58
+ autoFocus={autoFocus}
59
+ value={value}
60
+ onChange={(e) => onChange(e.target.value)}
61
+ onKeyDown={(e) => {
62
+ if (e.key === 'Enter' && !e.shiftKey) {
63
+ e.preventDefault()
64
+ fire()
65
+ }
66
+ }}
67
+ placeholder={placeholder}
68
+ rows={rows}
69
+ className={cn(
70
+ // `dark:bg-transparent` sobrescreve o `dark:bg-input/30` do primitivo Textarea —
71
+ // dentro da pílula do composer a textarea é PLANA (herda o bg-card), sem fundo de
72
+ // input próprio no escuro (o `bg-transparent` sozinho não pega a variante dark).
73
+ 'max-h-40 min-h-0 resize-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',
74
+ twoRow ? 'w-full px-1.5 py-1.5' : 'flex-1 px-0 py-2',
75
+ )}
76
+ />
77
+ )
78
+ const send = (
79
+ <Button
80
+ type="submit"
81
+ size="icon"
82
+ variant="ghost"
83
+ disabled={!canSubmit}
84
+ aria-label="Enviar"
85
+ className="shrink-0 text-muted-foreground hover:text-foreground"
86
+ >
87
+ {busy ? <Spinner size="sm" /> : <SendHorizontalIcon className="size-4" />}
88
+ </Button>
89
+ )
90
+
91
+ return (
92
+ <form
93
+ data-slot="composer"
94
+ onSubmit={(e) => {
95
+ e.preventDefault()
96
+ fire()
97
+ }}
98
+ className={cn(
99
+ 'rounded-card bg-card ring-1 ring-edge shadow-card transition focus-within:ring-2 focus-within:ring-primary/15',
100
+ twoRow ? 'p-2' : 'relative flex items-end gap-2 p-2 pl-3.5',
101
+ className,
102
+ )}
103
+ >
104
+ {twoRow ? (
105
+ <>
106
+ {textarea}
107
+ <div data-slot="composer-actions" className="mt-1 flex items-center justify-between gap-2">
108
+ {/* Seletores à esquerda (discretos), enviar à direita. */}
109
+ <div className="flex min-w-0 items-center gap-1">{actions}</div>
110
+ {send}
111
+ </div>
112
+ </>
113
+ ) : (
114
+ <>
115
+ {textarea}
116
+ {send}
117
+ </>
118
+ )}
119
+ </form>
120
+ )
121
+ }
@@ -0,0 +1,50 @@
1
+ import * as React from 'react'
2
+ import { CheckIcon, CopyIcon } from 'lucide-react'
3
+ import { cn } from '../../lib/cn.ts'
4
+
5
+ export interface CopyableProps extends Omit<React.ComponentProps<'button'>, 'value'> {
6
+ /** O texto que vai pro clipboard no clique. */
7
+ value: string
8
+ /** Quanto tempo (ms) o feedback de "copiado" dura. Default 1500. */
9
+ feedbackMs?: number
10
+ }
11
+
12
+ /**
13
+ * Clicar-pra-copiar com feedback: copia `value` pro clipboard e troca o ícone por um
14
+ * check por ~1.5s. Sem filhos é um botão-ícone (bom pra toolbar/célula); com filhos, o
15
+ * rótulo visível + o ícone à direita. Pra IDs, tokens, slugs, URLs. No-op silencioso se
16
+ * o clipboard não estiver disponível (contexto inseguro/SSR).
17
+ */
18
+ export function Copyable({
19
+ value,
20
+ feedbackMs = 1500,
21
+ children,
22
+ className,
23
+ onClick,
24
+ ...props
25
+ }: CopyableProps): React.ReactElement {
26
+ const [copied, setCopied] = React.useState(false)
27
+ const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
28
+ React.useEffect(() => () => clearTimeout(timer.current), [])
29
+ return (
30
+ <button
31
+ type="button"
32
+ data-slot="copyable"
33
+ data-copied={copied || undefined}
34
+ aria-label={copied ? 'Copiado' : 'Copiar'}
35
+ onClick={(e) => {
36
+ onClick?.(e)
37
+ void navigator.clipboard?.writeText(value).then(() => {
38
+ setCopied(true)
39
+ clearTimeout(timer.current)
40
+ timer.current = setTimeout(() => setCopied(false), feedbackMs)
41
+ })
42
+ }}
43
+ className={cn('inline-flex items-center gap-1.5', className)}
44
+ {...props}
45
+ >
46
+ {children}
47
+ {copied ? <CheckIcon className="size-3.5 text-emerald-500" /> : <CopyIcon className="size-3.5" />}
48
+ </button>
49
+ )
50
+ }
@@ -0,0 +1,147 @@
1
+ import * as React from 'react'
2
+ import * as DialogPrimitive from '@radix-ui/react-dialog'
3
+ import { XIcon } from 'lucide-react'
4
+ import { cn } from '../../lib/cn.ts'
5
+
6
+ export function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
7
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
8
+ }
9
+
10
+ export function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
11
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
12
+ }
13
+
14
+ export function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
15
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
16
+ }
17
+
18
+ export function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
19
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
20
+ }
21
+
22
+ export function DialogOverlay({
23
+ className,
24
+ ...props
25
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
26
+ return (
27
+ <DialogPrimitive.Overlay
28
+ data-slot="dialog-overlay"
29
+ className={cn(
30
+ 'fixed inset-0 z-50 bg-black/30 backdrop-blur-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',
31
+ className,
32
+ )}
33
+ {...props}
34
+ />
35
+ )
36
+ }
37
+
38
+ export function DialogContent({
39
+ className,
40
+ children,
41
+ showCloseButton = true,
42
+ ...props
43
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
44
+ /** Mostra o "X" de fechar no canto. Desligue pra um modal que exige ação explícita. */
45
+ showCloseButton?: boolean
46
+ }) {
47
+ return (
48
+ <DialogPortal>
49
+ <DialogOverlay />
50
+ {/* Superfície elevada: mantemos bg-card (divergência da casa; o shadcn usa bg-background). */}
51
+ <DialogPrimitive.Content
52
+ data-slot="dialog-content"
53
+ className={cn(
54
+ // Superfície PURA: só a casca (borda/bg/raio/sombra) + coluna flex com teto de
55
+ // altura. O padding mora nos SLOTS (Header/Body/Footer), não aqui — assim ninguém
56
+ // precisa negar margem pra escapar de um p-6 embutido.
57
+ "fixed left-[50%] top-[50%] z-50 flex max-h-[85vh] w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] flex-col overflow-hidden rounded-dialog bg-card ring-1 ring-edge shadow-dialog duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
58
+ className,
59
+ )}
60
+ {...props}
61
+ >
62
+ {children}
63
+ {showCloseButton && (
64
+ <DialogPrimitive.Close
65
+ data-slot="dialog-close"
66
+ className="absolute right-4 top-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
67
+ >
68
+ <XIcon />
69
+ <span className="sr-only">Fechar</span>
70
+ </DialogPrimitive.Close>
71
+ )}
72
+ </DialogPrimitive.Content>
73
+ </DialogPortal>
74
+ )
75
+ }
76
+
77
+ export function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
78
+ return (
79
+ <div
80
+ data-slot="dialog-header"
81
+ // Fixo (shrink-0), com divisor embaixo (border-b). p-5: padding igual nos quatro
82
+ // lados, casando com o body. O :has tira o divisor quando o header é seguido
83
+ // DIRETO pelo footer (confirm sem body) — senão o border-b dele encostaria no
84
+ // border-t do footer (2px); aí o divisor é só o do footer.
85
+ className={cn(
86
+ 'flex shrink-0 flex-col gap-2 border-b p-5 text-center sm:text-left',
87
+ '[&:has(+[data-slot=dialog-footer])]:border-b-0',
88
+ className,
89
+ )}
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ export function DialogBody({ className, ...props }: React.ComponentProps<'div'>) {
96
+ return (
97
+ <div
98
+ data-slot="dialog-body"
99
+ // O miolo que ROLA: cresce, scrolla e padda a si mesmo. Pra corpo edge-to-edge
100
+ // (cmdk, iframe de lente) passe `className` zerando o padding.
101
+ className={cn('min-h-0 flex-1 overflow-y-auto p-5', className)}
102
+ {...props}
103
+ />
104
+ )
105
+ }
106
+
107
+ export function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
108
+ return (
109
+ <div
110
+ data-slot="dialog-footer"
111
+ // Faixa destacada (divergência da casa, ao estilo do site do shadcn): borda em cima
112
+ // + fundo muted. Sangra até as bordas sozinha — o content não tem padding, então não
113
+ // há -mx pra compensar; o overflow-hidden do content arredonda o canto de baixo.
114
+ className={cn(
115
+ 'flex shrink-0 flex-col-reverse gap-2 border-t bg-muted/30 px-5 py-4 sm:flex-row sm:justify-end',
116
+ className,
117
+ )}
118
+ {...props}
119
+ />
120
+ )
121
+ }
122
+
123
+ export function DialogTitle({
124
+ className,
125
+ ...props
126
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
127
+ return (
128
+ <DialogPrimitive.Title
129
+ data-slot="dialog-title"
130
+ className={cn('text-lg font-semibold leading-none', className)}
131
+ {...props}
132
+ />
133
+ )
134
+ }
135
+
136
+ export function DialogDescription({
137
+ className,
138
+ ...props
139
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
140
+ return (
141
+ <DialogPrimitive.Description
142
+ data-slot="dialog-description"
143
+ className={cn('text-sm text-muted-foreground', className)}
144
+ {...props}
145
+ />
146
+ )
147
+ }
@@ -0,0 +1,141 @@
1
+ import * as React from "react"
2
+ import { XIcon } from "lucide-react"
3
+ import { Dialog as DrawerPrimitive } from "radix-ui"
4
+
5
+ import { cn } from '../../lib/cn.ts'
6
+
7
+ function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
8
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />
9
+ }
10
+
11
+ function DrawerTrigger({
12
+ ...props
13
+ }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
14
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
15
+ }
16
+
17
+ function DrawerClose({
18
+ ...props
19
+ }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
20
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
21
+ }
22
+
23
+ function DrawerPortal({
24
+ ...props
25
+ }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
26
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
27
+ }
28
+
29
+ function DrawerOverlay({
30
+ className,
31
+ ...props
32
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
33
+ return (
34
+ <DrawerPrimitive.Overlay
35
+ data-slot="drawer-overlay"
36
+ className={cn(
37
+ "fixed inset-0 z-50 bg-black/30 backdrop-blur-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
38
+ className
39
+ )}
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ function DrawerContent({
46
+ className,
47
+ children,
48
+ side = "right",
49
+ showCloseButton = true,
50
+ ...props
51
+ }: React.ComponentProps<typeof DrawerPrimitive.Content> & {
52
+ side?: "top" | "right" | "bottom" | "left"
53
+ showCloseButton?: boolean
54
+ }) {
55
+ return (
56
+ <DrawerPortal>
57
+ <DrawerOverlay />
58
+ <DrawerPrimitive.Content
59
+ data-slot="drawer-content"
60
+ className={cn(
61
+ "fixed z-50 flex flex-col gap-4 bg-background shadow-dialog transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
62
+ side === "right" &&
63
+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
64
+ side === "left" &&
65
+ "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
66
+ side === "top" &&
67
+ "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
68
+ side === "bottom" &&
69
+ "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
70
+ className
71
+ )}
72
+ {...props}
73
+ >
74
+ {children}
75
+ {showCloseButton && (
76
+ <DrawerPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary">
77
+ <XIcon className="size-4" />
78
+ <span className="sr-only">Close</span>
79
+ </DrawerPrimitive.Close>
80
+ )}
81
+ </DrawerPrimitive.Content>
82
+ </DrawerPortal>
83
+ )
84
+ }
85
+
86
+ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
87
+ return (
88
+ <div
89
+ data-slot="drawer-header"
90
+ className={cn("flex flex-col gap-1.5 p-4", className)}
91
+ {...props}
92
+ />
93
+ )
94
+ }
95
+
96
+ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
97
+ return (
98
+ <div
99
+ data-slot="drawer-footer"
100
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
101
+ {...props}
102
+ />
103
+ )
104
+ }
105
+
106
+ function DrawerTitle({
107
+ className,
108
+ ...props
109
+ }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
110
+ return (
111
+ <DrawerPrimitive.Title
112
+ data-slot="drawer-title"
113
+ className={cn("font-semibold text-foreground", className)}
114
+ {...props}
115
+ />
116
+ )
117
+ }
118
+
119
+ function DrawerDescription({
120
+ className,
121
+ ...props
122
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
123
+ return (
124
+ <DrawerPrimitive.Description
125
+ data-slot="drawer-description"
126
+ className={cn("text-sm text-muted-foreground", className)}
127
+ {...props}
128
+ />
129
+ )
130
+ }
131
+
132
+ export {
133
+ Drawer,
134
+ DrawerTrigger,
135
+ DrawerClose,
136
+ DrawerContent,
137
+ DrawerHeader,
138
+ DrawerFooter,
139
+ DrawerTitle,
140
+ DrawerDescription,
141
+ }
@@ -0,0 +1,104 @@
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+
3
+ import { cn } from '../../lib/cn.ts'
4
+
5
+ function Empty({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="empty"
9
+ className={cn(
10
+ "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="empty-header"
22
+ className={cn(
23
+ "flex max-w-sm flex-col items-center gap-2 text-center",
24
+ className
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ const emptyMediaVariants = cva(
32
+ "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
33
+ {
34
+ variants: {
35
+ variant: {
36
+ default: "bg-transparent",
37
+ icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6",
38
+ },
39
+ },
40
+ defaultVariants: {
41
+ variant: "default",
42
+ },
43
+ }
44
+ )
45
+
46
+ function EmptyMedia({
47
+ className,
48
+ variant = "default",
49
+ ...props
50
+ }: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
51
+ return (
52
+ <div
53
+ data-slot="empty-icon"
54
+ data-variant={variant}
55
+ className={cn(emptyMediaVariants({ variant, className }))}
56
+ {...props}
57
+ />
58
+ )
59
+ }
60
+
61
+ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
62
+ return (
63
+ <div
64
+ data-slot="empty-title"
65
+ className={cn("text-lg font-medium tracking-tight", className)}
66
+ {...props}
67
+ />
68
+ )
69
+ }
70
+
71
+ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
72
+ return (
73
+ <div
74
+ data-slot="empty-description"
75
+ className={cn(
76
+ "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
85
+ return (
86
+ <div
87
+ data-slot="empty-content"
88
+ className={cn(
89
+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
90
+ className
91
+ )}
92
+ {...props}
93
+ />
94
+ )
95
+ }
96
+
97
+ export {
98
+ Empty,
99
+ EmptyHeader,
100
+ EmptyTitle,
101
+ EmptyDescription,
102
+ EmptyContent,
103
+ EmptyMedia,
104
+ }