beads-kanban-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/.designs/beads-kanban-ui-bj0.md +73 -0
  2. package/.designs/beads-kanban-ui-qxq.md +144 -0
  3. package/.designs/epic-support.md +282 -0
  4. package/.env.local.example +2 -0
  5. package/.eslintrc.json +3 -0
  6. package/.gitattributes +3 -0
  7. package/.github/workflows/release.yml +123 -0
  8. package/.history/README_20260121193710.md +227 -0
  9. package/.history/README_20260121193918.md +227 -0
  10. package/.history/README_20260121193921.md +227 -0
  11. package/.history/README_20260121193933.md +227 -0
  12. package/.history/README_20260121193934.md +227 -0
  13. package/.history/README_20260121193944.md +227 -0
  14. package/.history/README_20260121193953.md +227 -0
  15. package/.history/src/app/page_20260121133429.tsx +134 -0
  16. package/.history/src/app/page_20260121133928.tsx +134 -0
  17. package/.history/src/app/page_20260121144850.tsx +138 -0
  18. package/.history/src/app/page_20260121144854.tsx +138 -0
  19. package/.history/src/app/page_20260121144858.tsx +138 -0
  20. package/.history/src/app/page_20260121144902.tsx +138 -0
  21. package/.history/src/app/page_20260121144906.tsx +138 -0
  22. package/.history/src/app/page_20260121144911.tsx +138 -0
  23. package/.history/src/app/page_20260121144928.tsx +138 -0
  24. package/.playwright-mcp/.playwright-mcp/morphing-dialog-wheel-scroll-fix.png +0 -0
  25. package/.playwright-mcp/beams-test.png +0 -0
  26. package/.playwright-mcp/card-verification.png +0 -0
  27. package/.playwright-mcp/design-doc-dialog-fix-verification.png +0 -0
  28. package/.playwright-mcp/dialog-width-test.png +0 -0
  29. package/.playwright-mcp/homepage.png +0 -0
  30. package/.playwright-mcp/morphing-dialog-expanded.png +0 -0
  31. package/.playwright-mcp/morphing-dialog-fixes-final.png +0 -0
  32. package/.playwright-mcp/morphing-dialog-open.png +0 -0
  33. package/.playwright-mcp/page-2026-01-21T14-08-31-529Z.png +0 -0
  34. package/.playwright-mcp/page-2026-01-21T14-09-23-431Z.png +0 -0
  35. package/.playwright-mcp/page-2026-01-21T14-10-28-773Z.png +0 -0
  36. package/.playwright-mcp/page-2026-01-21T14-10-47-432Z.png +0 -0
  37. package/.playwright-mcp/page-2026-01-21T14-11-12-350Z.png +0 -0
  38. package/.playwright-mcp/screenshot-after-click.png +0 -0
  39. package/.playwright-mcp/screenshot-after-dialog-click.png +0 -0
  40. package/.playwright-mcp/sheet-restored-after-dialog-close.png +0 -0
  41. package/.playwright-mcp/test-1-sheet-open-with-overlay.png +0 -0
  42. package/.playwright-mcp/test-2-morphing-dialog-with-overlay.png +0 -0
  43. package/.playwright-mcp/test-3-sheet-open-dark-overlay.png +0 -0
  44. package/.playwright-mcp/test-4-morphing-dialog-with-dark-overlay.png +0 -0
  45. package/.playwright-mcp/test-5-morphing-dialog-scrolled.png +0 -0
  46. package/.playwright-mcp/test-6-sheet-restored-after-dialog-close.png +0 -0
  47. package/.playwright-mcp/wheel-scroll-fixed.png +0 -0
  48. package/README.md +243 -0
  49. package/Screenshots/bead-detail.png +0 -0
  50. package/Screenshots/dashboard.png +0 -0
  51. package/Screenshots/kanban-board.png +0 -0
  52. package/components.json +27 -0
  53. package/logo/logo.svg +1 -0
  54. package/next.config.js +9 -0
  55. package/npm/README.md +37 -0
  56. package/npm/bin/cli.js +107 -0
  57. package/npm/package.json +20 -0
  58. package/npm/scripts/postinstall.js +132 -0
  59. package/package.json +62 -0
  60. package/postcss.config.js +6 -0
  61. package/public/logo.svg +1 -0
  62. package/restart.sh +5 -0
  63. package/server/Cargo.lock +1685 -0
  64. package/server/Cargo.toml +24 -0
  65. package/server/src/db.rs +570 -0
  66. package/server/src/main.rs +141 -0
  67. package/server/src/routes/beads.rs +413 -0
  68. package/server/src/routes/cli.rs +150 -0
  69. package/server/src/routes/fs.rs +360 -0
  70. package/server/src/routes/git.rs +169 -0
  71. package/server/src/routes/mod.rs +107 -0
  72. package/server/src/routes/projects.rs +177 -0
  73. package/server/src/routes/watch.rs +211 -0
  74. package/src/app/globals.css +101 -0
  75. package/src/app/layout.tsx +36 -0
  76. package/src/app/page.tsx +348 -0
  77. package/src/app/project/kanban-board.tsx +356 -0
  78. package/src/app/project/page.tsx +18 -0
  79. package/src/app/settings/page.tsx +224 -0
  80. package/src/components/Beams.css +5 -0
  81. package/src/components/Beams.jsx +307 -0
  82. package/src/components/Galaxy.css +5 -0
  83. package/src/components/Galaxy.jsx +333 -0
  84. package/src/components/activity-timeline.tsx +172 -0
  85. package/src/components/add-project-dialog.tsx +219 -0
  86. package/src/components/bead-card.tsx +196 -0
  87. package/src/components/bead-detail.tsx +306 -0
  88. package/src/components/color-picker.tsx +101 -0
  89. package/src/components/comment-input.tsx +155 -0
  90. package/src/components/comment-list.tsx +147 -0
  91. package/src/components/dependency-badge.tsx +106 -0
  92. package/src/components/design-doc-dialog.tsx +58 -0
  93. package/src/components/design-doc-preview.tsx +97 -0
  94. package/src/components/design-doc-viewer.tsx +199 -0
  95. package/src/components/editable-project-name.tsx +178 -0
  96. package/src/components/epic-card.tsx +263 -0
  97. package/src/components/folder-browser.tsx +273 -0
  98. package/src/components/footer.tsx +27 -0
  99. package/src/components/kanban/default.tsx +184 -0
  100. package/src/components/kanban-column.tsx +167 -0
  101. package/src/components/project-card.tsx +191 -0
  102. package/src/components/quick-filter-bar.tsx +279 -0
  103. package/src/components/scan-directory-dialog.tsx +368 -0
  104. package/src/components/status-donut.tsx +197 -0
  105. package/src/components/subtask-list.tsx +128 -0
  106. package/src/components/tag-picker.tsx +252 -0
  107. package/src/components/ui/.gitkeep +0 -0
  108. package/src/components/ui/alert-dialog.tsx +141 -0
  109. package/src/components/ui/avatar.tsx +67 -0
  110. package/src/components/ui/badge.tsx +230 -0
  111. package/src/components/ui/button.tsx +433 -0
  112. package/src/components/ui/card/index.tsx +24 -0
  113. package/src/components/ui/card/roiui-card.module.css +197 -0
  114. package/src/components/ui/card/roiui-card.tsx +154 -0
  115. package/src/components/ui/card/shadcn-card.tsx +76 -0
  116. package/src/components/ui/chart.tsx +369 -0
  117. package/src/components/ui/dialog.tsx +122 -0
  118. package/src/components/ui/dropdown-menu.tsx +201 -0
  119. package/src/components/ui/input.tsx +22 -0
  120. package/src/components/ui/kanban.tsx +522 -0
  121. package/src/components/ui/morphing-dialog.tsx +457 -0
  122. package/src/components/ui/popover.tsx +33 -0
  123. package/src/components/ui/progress.tsx +28 -0
  124. package/src/components/ui/scroll-area.tsx +48 -0
  125. package/src/components/ui/select.tsx +159 -0
  126. package/src/components/ui/separator.tsx +31 -0
  127. package/src/components/ui/sheet.tsx +142 -0
  128. package/src/components/ui/skeleton.tsx +15 -0
  129. package/src/components/ui/toast.tsx +129 -0
  130. package/src/components/ui/toaster.tsx +35 -0
  131. package/src/components/ui/tooltip.tsx +30 -0
  132. package/src/hooks/.gitkeep +0 -0
  133. package/src/hooks/use-bead-filters.ts +261 -0
  134. package/src/hooks/use-beads.ts +162 -0
  135. package/src/hooks/use-branch-statuses.ts +161 -0
  136. package/src/hooks/use-epics.ts +173 -0
  137. package/src/hooks/use-file-watcher.ts +111 -0
  138. package/src/hooks/use-keyboard-navigation.ts +282 -0
  139. package/src/hooks/use-project.ts +61 -0
  140. package/src/hooks/use-projects.ts +93 -0
  141. package/src/hooks/use-toast.ts +194 -0
  142. package/src/hooks/useClickOutside.tsx +26 -0
  143. package/src/lib/.gitkeep +0 -0
  144. package/src/lib/api.ts +186 -0
  145. package/src/lib/beads-parser.ts +252 -0
  146. package/src/lib/cli.ts +193 -0
  147. package/src/lib/db.ts +145 -0
  148. package/src/lib/design-doc.ts +74 -0
  149. package/src/lib/epic-parser.ts +242 -0
  150. package/src/lib/git.ts +102 -0
  151. package/src/lib/utils.ts +12 -0
  152. package/src/types/index.ts +107 -0
  153. package/tailwind.config.ts +85 -0
  154. package/tsconfig.json +26 -0
@@ -0,0 +1,433 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { ChevronDown, LucideIcon } from 'lucide-react';
4
+ import { Slot as SlotPrimitive } from 'radix-ui';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ const buttonVariants = cva(
8
+ 'cursor-pointer group whitespace-nowrap focus-visible:outline-hidden inline-flex items-center justify-center has-data-[arrow=true]:justify-between whitespace-nowrap text-sm font-medium ring-offset-background transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-60 [&_svg]:shrink-0',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ primary: 'bg-primary text-primary-foreground hover:bg-primary/90 data-[state=open]:bg-primary/90',
13
+ mono: 'bg-zinc-950 text-white dark:bg-zinc-300 dark:text-black hover:bg-zinc-950/90 dark:hover:bg-zinc-300/90 data-[state=open]:bg-zinc-950/90 dark:data-[state=open]:bg-zinc-300/90',
14
+ destructive:
15
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90 data-[state=open]:bg-destructive/90',
16
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/90 data-[state=open]:bg-secondary/90',
17
+ outline: 'bg-background text-accent-foreground border border-input hover:bg-accent data-[state=open]:bg-accent',
18
+ dashed:
19
+ 'text-accent-foreground border border-input border-dashed bg-background hover:bg-accent hover:text-accent-foreground data-[state=open]:text-accent-foreground',
20
+ ghost:
21
+ 'text-accent-foreground hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
22
+ dim: 'text-muted-foreground hover:text-foreground data-[state=open]:text-foreground',
23
+ foreground: '',
24
+ inverse: '',
25
+ },
26
+ appearance: {
27
+ default: '',
28
+ ghost: '',
29
+ },
30
+ underline: {
31
+ solid: '',
32
+ dashed: '',
33
+ },
34
+ underlined: {
35
+ solid: '',
36
+ dashed: '',
37
+ },
38
+ size: {
39
+ lg: 'h-10 px-4 text-sm gap-1.5 [&_svg:not([class*=size-])]:size-4',
40
+ md: 'h-9 px-3 gap-1.5 text-sm [&_svg:not([class*=size-])]:size-4',
41
+ sm: 'h-8 px-2.5 gap-1.25 text-xs [&_svg:not([class*=size-])]:size-3.5',
42
+ xs: 'h-7 px-2 gap-1 text-xs [&_svg:not([class*=size-])]:size-3.5',
43
+ icon: 'size-9 [&_svg:not([class*=size-])]:size-4 shrink-0',
44
+ },
45
+ autoHeight: {
46
+ true: '',
47
+ false: '',
48
+ },
49
+ radius: {
50
+ md: 'rounded-md',
51
+ full: 'rounded-full',
52
+ },
53
+ mode: {
54
+ default: 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
55
+ icon: 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 shrink-0',
56
+ link: 'text-primary h-auto p-0 bg-transparent rounded-none hover:bg-transparent data-[state=open]:bg-transparent',
57
+ input: `
58
+ justify-start font-normal hover:bg-background [&_svg]:transition-colors [&_svg]:hover:text-foreground data-[state=open]:bg-background
59
+ focus-visible:border-ring focus-visible:outline-hidden focus-visible:ring-[3px] focus-visible:ring-ring/30
60
+ [[data-state=open]>&]:border-ring [[data-state=open]>&]:outline-hidden [[data-state=open]>&]:ring-[3px]
61
+ [[data-state=open]>&]:ring-ring/30
62
+ aria-invalid:border-destructive/60 aria-invalid:ring-destructive/10 dark:aria-invalid:border-destructive dark:aria-invalid:ring-destructive/20
63
+ in-data-[invalid=true]:border-destructive/60 in-data-[invalid=true]:ring-destructive/10 dark:in-data-[invalid=true]:border-destructive dark:in-data-[invalid=true]:ring-destructive/20
64
+ `,
65
+ },
66
+ placeholder: {
67
+ true: 'text-muted-foreground',
68
+ false: '',
69
+ },
70
+ },
71
+ compoundVariants: [
72
+ // Icons opacity for default mode
73
+ {
74
+ variant: 'ghost',
75
+ mode: 'default',
76
+ className: '[&_svg:not([role=img]):not([class*=text-]):not([class*=opacity-])]:opacity-60',
77
+ },
78
+ {
79
+ variant: 'outline',
80
+ mode: 'default',
81
+ className: '[&_svg:not([role=img]):not([class*=text-]):not([class*=opacity-])]:opacity-60',
82
+ },
83
+ {
84
+ variant: 'dashed',
85
+ mode: 'default',
86
+ className: '[&_svg:not([role=img]):not([class*=text-]):not([class*=opacity-])]:opacity-60',
87
+ },
88
+ {
89
+ variant: 'secondary',
90
+ mode: 'default',
91
+ className: '[&_svg:not([role=img]):not([class*=text-]):not([class*=opacity-])]:opacity-60',
92
+ },
93
+
94
+ // Icons opacity for default mode
95
+ {
96
+ variant: 'outline',
97
+ mode: 'input',
98
+ className: '[&_svg:not([role=img]):not([class*=text-]):not([class*=opacity-])]:opacity-60',
99
+ },
100
+ {
101
+ variant: 'outline',
102
+ mode: 'icon',
103
+ className: '[&_svg:not([role=img]):not([class*=text-]):not([class*=opacity-])]:opacity-60',
104
+ },
105
+
106
+ // Auto height
107
+ {
108
+ size: 'xs',
109
+ autoHeight: true,
110
+ className: 'h-auto min-h-7',
111
+ },
112
+ {
113
+ size: 'md',
114
+ autoHeight: true,
115
+ className: 'h-auto min-h-9',
116
+ },
117
+ {
118
+ size: 'sm',
119
+ autoHeight: true,
120
+ className: 'h-auto min-h-8',
121
+ },
122
+ {
123
+ size: 'lg',
124
+ autoHeight: true,
125
+ className: 'h-auto min-h-10',
126
+ },
127
+
128
+ // Shadow support
129
+ {
130
+ variant: 'primary',
131
+ mode: 'default',
132
+ appearance: 'default',
133
+ className: 'shadow-xs shadow-black/5',
134
+ },
135
+ {
136
+ variant: 'mono',
137
+ mode: 'default',
138
+ appearance: 'default',
139
+ className: 'shadow-xs shadow-black/5',
140
+ },
141
+ {
142
+ variant: 'secondary',
143
+ mode: 'default',
144
+ appearance: 'default',
145
+ className: 'shadow-xs shadow-black/5',
146
+ },
147
+ {
148
+ variant: 'outline',
149
+ mode: 'default',
150
+ appearance: 'default',
151
+ className: 'shadow-xs shadow-black/5',
152
+ },
153
+ {
154
+ variant: 'dashed',
155
+ mode: 'default',
156
+ appearance: 'default',
157
+ className: 'shadow-xs shadow-black/5',
158
+ },
159
+ {
160
+ variant: 'destructive',
161
+ mode: 'default',
162
+ appearance: 'default',
163
+ className: 'shadow-xs shadow-black/5',
164
+ },
165
+
166
+ // Shadow support
167
+ {
168
+ variant: 'primary',
169
+ mode: 'icon',
170
+ appearance: 'default',
171
+ className: 'shadow-xs shadow-black/5',
172
+ },
173
+ {
174
+ variant: 'mono',
175
+ mode: 'icon',
176
+ appearance: 'default',
177
+ className: 'shadow-xs shadow-black/5',
178
+ },
179
+ {
180
+ variant: 'secondary',
181
+ mode: 'icon',
182
+ appearance: 'default',
183
+ className: 'shadow-xs shadow-black/5',
184
+ },
185
+ {
186
+ variant: 'outline',
187
+ mode: 'icon',
188
+ appearance: 'default',
189
+ className: 'shadow-xs shadow-black/5',
190
+ },
191
+ {
192
+ variant: 'dashed',
193
+ mode: 'icon',
194
+ appearance: 'default',
195
+ className: 'shadow-xs shadow-black/5',
196
+ },
197
+ {
198
+ variant: 'destructive',
199
+ mode: 'icon',
200
+ appearance: 'default',
201
+ className: 'shadow-xs shadow-black/5',
202
+ },
203
+
204
+ // Link
205
+ {
206
+ variant: 'primary',
207
+ mode: 'link',
208
+ underline: 'solid',
209
+ className:
210
+ 'font-medium text-primary hover:text-primary/90 [&_svg:not([role=img]):not([class*=text-])]:opacity-60 hover:underline hover:underline-offset-4 hover:decoration-solid',
211
+ },
212
+ {
213
+ variant: 'primary',
214
+ mode: 'link',
215
+ underline: 'dashed',
216
+ className:
217
+ 'font-medium text-primary hover:text-primary/90 [&_svg:not([role=img]):not([class*=text-])]:opacity-60 hover:underline hover:underline-offset-4 hover:decoration-dashed decoration-1',
218
+ },
219
+ {
220
+ variant: 'primary',
221
+ mode: 'link',
222
+ underlined: 'solid',
223
+ className:
224
+ 'font-medium text-primary hover:text-primary/90 [&_svg:not([role=img]):not([class*=text-])]:opacity-60 underline underline-offset-4 decoration-solid',
225
+ },
226
+ {
227
+ variant: 'primary',
228
+ mode: 'link',
229
+ underlined: 'dashed',
230
+ className:
231
+ 'font-medium text-primary hover:text-primary/90 [&_svg]:opacity-60 underline underline-offset-4 decoration-dashed decoration-1',
232
+ },
233
+
234
+ {
235
+ variant: 'inverse',
236
+ mode: 'link',
237
+ underline: 'solid',
238
+ className:
239
+ 'font-medium text-inherit [&_svg:not([role=img]):not([class*=text-])]:opacity-60 hover:underline hover:underline-offset-4 hover:decoration-solid',
240
+ },
241
+ {
242
+ variant: 'inverse',
243
+ mode: 'link',
244
+ underline: 'dashed',
245
+ className:
246
+ 'font-medium text-inherit [&_svg:not([role=img]):not([class*=text-])]:opacity-60 hover:underline hover:underline-offset-4 hover:decoration-dashed decoration-1',
247
+ },
248
+ {
249
+ variant: 'inverse',
250
+ mode: 'link',
251
+ underlined: 'solid',
252
+ className:
253
+ 'font-medium text-inherit [&_svg:not([role=img]):not([class*=text-])]:opacity-60 underline underline-offset-4 decoration-solid',
254
+ },
255
+ {
256
+ variant: 'inverse',
257
+ mode: 'link',
258
+ underlined: 'dashed',
259
+ className:
260
+ 'font-medium text-inherit [&_svg:not([role=img]):not([class*=text-])]:opacity-60 underline underline-offset-4 decoration-dashed decoration-1',
261
+ },
262
+
263
+ {
264
+ variant: 'foreground',
265
+ mode: 'link',
266
+ underline: 'solid',
267
+ className:
268
+ 'font-medium text-foreground [&_svg:not([role=img]):not([class*=text-])]:opacity-60 hover:underline hover:underline-offset-4 hover:decoration-solid',
269
+ },
270
+ {
271
+ variant: 'foreground',
272
+ mode: 'link',
273
+ underline: 'dashed',
274
+ className:
275
+ 'font-medium text-foreground [&_svg:not([role=img]):not([class*=text-])]:opacity-60 hover:underline hover:underline-offset-4 hover:decoration-dashed decoration-1',
276
+ },
277
+ {
278
+ variant: 'foreground',
279
+ mode: 'link',
280
+ underlined: 'solid',
281
+ className:
282
+ 'font-medium text-foreground [&_svg:not([role=img]):not([class*=text-])]:opacity-60 underline underline-offset-4 decoration-solid',
283
+ },
284
+ {
285
+ variant: 'foreground',
286
+ mode: 'link',
287
+ underlined: 'dashed',
288
+ className:
289
+ 'font-medium text-foreground [&_svg:not([role=img]):not([class*=text-])]:opacity-60 underline underline-offset-4 decoration-dashed decoration-1',
290
+ },
291
+
292
+ // Ghost
293
+ {
294
+ variant: 'primary',
295
+ appearance: 'ghost',
296
+ className: 'bg-transparent text-primary/90 hover:bg-primary/5 data-[state=open]:bg-primary/5',
297
+ },
298
+ {
299
+ variant: 'destructive',
300
+ appearance: 'ghost',
301
+ className: 'bg-transparent text-destructive/90 hover:bg-destructive/5 data-[state=open]:bg-destructive/5',
302
+ },
303
+ {
304
+ variant: 'ghost',
305
+ mode: 'icon',
306
+ className: 'text-muted-foreground',
307
+ },
308
+
309
+ // Size
310
+ {
311
+ size: 'xs',
312
+ mode: 'icon',
313
+ className: 'w-7 h-7 p-0 [[&_svg:not([class*=size-])]:size-3.5',
314
+ },
315
+ {
316
+ size: 'sm',
317
+ mode: 'icon',
318
+ className: 'w-8 h-8 p-0 [[&_svg:not([class*=size-])]:size-3.5',
319
+ },
320
+ {
321
+ size: 'md',
322
+ mode: 'icon',
323
+ className: 'w-9 h-9 p-0 [&_svg:not([class*=size-])]:size-4',
324
+ },
325
+ {
326
+ size: 'icon',
327
+ className: 'w-9 h-9 p-0 [&_svg:not([class*=size-])]:size-4',
328
+ },
329
+ {
330
+ size: 'lg',
331
+ mode: 'icon',
332
+ className: 'w-10 h-10 p-0 [&_svg:not([class*=size-])]:size-4',
333
+ },
334
+
335
+ // Input mode
336
+ {
337
+ mode: 'input',
338
+ placeholder: true,
339
+ variant: 'outline',
340
+ className: 'font-normal text-muted-foreground',
341
+ },
342
+ {
343
+ mode: 'input',
344
+ variant: 'outline',
345
+ size: 'sm',
346
+ className: 'gap-1.25',
347
+ },
348
+ {
349
+ mode: 'input',
350
+ variant: 'outline',
351
+ size: 'md',
352
+ className: 'gap-1.5',
353
+ },
354
+ {
355
+ mode: 'input',
356
+ variant: 'outline',
357
+ size: 'lg',
358
+ className: 'gap-1.5',
359
+ },
360
+ ],
361
+ defaultVariants: {
362
+ variant: 'primary',
363
+ mode: 'default',
364
+ size: 'md',
365
+ radius: 'md',
366
+ appearance: 'default',
367
+ },
368
+ },
369
+ );
370
+
371
+ const Button = React.forwardRef<
372
+ HTMLButtonElement,
373
+ React.ComponentProps<'button'> &
374
+ VariantProps<typeof buttonVariants> & {
375
+ selected?: boolean;
376
+ asChild?: boolean;
377
+ }
378
+ >(
379
+ (
380
+ {
381
+ className,
382
+ selected,
383
+ variant,
384
+ radius,
385
+ appearance,
386
+ mode,
387
+ size,
388
+ autoHeight,
389
+ underlined,
390
+ underline,
391
+ asChild = false,
392
+ placeholder = false,
393
+ ...props
394
+ },
395
+ ref
396
+ ) => {
397
+ const Comp = asChild ? SlotPrimitive.Slot : 'button';
398
+ return (
399
+ <Comp
400
+ ref={ref}
401
+ data-slot="button"
402
+ className={cn(
403
+ buttonVariants({
404
+ variant,
405
+ size,
406
+ radius,
407
+ appearance,
408
+ mode,
409
+ autoHeight,
410
+ placeholder,
411
+ underlined,
412
+ underline,
413
+ className,
414
+ }),
415
+ asChild && props.disabled && 'pointer-events-none opacity-50',
416
+ )}
417
+ {...(selected && { 'data-state': 'open' })}
418
+ {...props}
419
+ />
420
+ );
421
+ }
422
+ );
423
+ Button.displayName = 'Button';
424
+
425
+ interface ButtonArrowProps extends React.SVGProps<SVGSVGElement> {
426
+ icon?: LucideIcon; // Allows passing any Lucide icon
427
+ }
428
+
429
+ function ButtonArrow({ icon: Icon = ChevronDown, className, ...props }: ButtonArrowProps) {
430
+ return <Icon data-slot="button-arrow" className={cn('ms-auto -me-1', className)} {...props} />;
431
+ }
432
+
433
+ export { Button, ButtonArrow, buttonVariants };
@@ -0,0 +1,24 @@
1
+ // Re-export both shadcn and roiui card components
2
+ // Original shadcn card (for backward compatibility)
3
+ export {
4
+ Card,
5
+ CardHeader,
6
+ CardFooter,
7
+ CardTitle,
8
+ CardDescription,
9
+ CardContent,
10
+ } from "./shadcn-card";
11
+
12
+ // Roiui card (newer, with variants and lift effect)
13
+ export {
14
+ Card as RoiuiCard,
15
+ CardAction as RoiuiCardAction,
16
+ CardContent as RoiuiCardContent,
17
+ CardDescription as RoiuiCardDescription,
18
+ CardFooter as RoiuiCardFooter,
19
+ CardHeader as RoiuiCardHeader,
20
+ CardIcon as RoiuiCardIcon,
21
+ CardImage as RoiuiCardImage,
22
+ CardImageContent as RoiuiCardImageContent,
23
+ CardTitle as RoiuiCardTitle,
24
+ } from "./roiui-card";
@@ -0,0 +1,197 @@
1
+ .card {
2
+ border-radius: var(--radius);
3
+ color: hsl(var(--foreground));
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 1.5rem;
7
+ padding: 1.5rem;
8
+ background-color: hsl(var(--card) / 0.95);
9
+ border: 1px solid hsl(var(--border) / 0.6);
10
+ position: relative;
11
+ justify-content: space-between;
12
+ box-shadow: 0 1px 3px hsl(var(--foreground) / 0.08);
13
+ backdrop-filter: blur(12px);
14
+ -webkit-backdrop-filter: blur(12px);
15
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
16
+ }
17
+
18
+ .card:hover {
19
+ transform: translateY(-2px);
20
+ box-shadow:
21
+ 0 0 0 1px hsl(var(--border) / 0.6),
22
+ 0 10px 30px -10px rgba(0, 0, 0, 0.3),
23
+ 0 4px 6px -2px rgba(0, 0, 0, 0.1),
24
+ 0 0 20px -5px rgba(168, 85, 247, 0.15);
25
+ }
26
+
27
+ .header {
28
+ display: grid;
29
+ grid-template-columns: 1fr auto;
30
+ gap: 0.5rem;
31
+ align-items: center;
32
+ max-width: 100%;
33
+ }
34
+
35
+ .title {
36
+ font-size: 1.25rem;
37
+ font-weight: 500;
38
+ line-height: 1;
39
+ letter-spacing: -0.025em;
40
+ margin: 0;
41
+ }
42
+
43
+ .description {
44
+ font-size: 0.875rem;
45
+ line-height: 1.25rem;
46
+ color: hsl(var(--muted-foreground));
47
+ margin: 0;
48
+ grid-column: 1 / -1;
49
+ white-space: unset;
50
+ }
51
+
52
+ .content {
53
+ display: flex;
54
+ flex-direction: column;
55
+ gap: 0.75rem;
56
+ }
57
+
58
+ .cardLift {
59
+ overflow: hidden;
60
+ padding-bottom: 0;
61
+ gap: 0;
62
+ }
63
+
64
+ .cardLift .content {
65
+ padding: 1.5rem 1.5rem 2rem;
66
+ transition: transform 0.25s var(--ease-in-out-quad);
67
+ background-color: var(--mix-card-33-bg);
68
+ width: calc(100% + 3rem);
69
+ margin: 0 -1.5rem;
70
+ }
71
+
72
+ .textContent {
73
+ display: flex;
74
+ flex-direction: column;
75
+ gap: 0.375rem;
76
+ }
77
+
78
+ .footer {
79
+ display: flex;
80
+ align-items: center;
81
+ }
82
+
83
+ .image {
84
+ width: calc(100% + 3rem);
85
+ margin: -1.5rem -1.5rem 0;
86
+ object-fit: cover;
87
+ height: 300px;
88
+ aspect-ratio: 16 / 9;
89
+ transition: transform 0.25s var(--ease-in-out-quad);
90
+ border-radius: var(--radius) var(--radius) 0 0;
91
+ max-width: 100vw;
92
+ box-sizing: border-box;
93
+ }
94
+
95
+ .cardLift .footer {
96
+ opacity: 0;
97
+ transition:
98
+ opacity 0.25s var(--ease-in-out-quad),
99
+ transform 0.25s var(--ease-in-out-quad);
100
+ position: absolute;
101
+ bottom: 0;
102
+ left: 1.5rem;
103
+ right: 1.5rem;
104
+ }
105
+
106
+ .cardLift:hover .content {
107
+ transform: translateY(-2rem);
108
+ }
109
+
110
+ .cardLift:hover .footer {
111
+ opacity: 1;
112
+ transform: translateY(-1rem);
113
+ }
114
+
115
+ .cardLift:hover .image {
116
+ transform: scale(1.04);
117
+ }
118
+
119
+ @media (max-width: 768px) {
120
+ .image {
121
+ width: 100%;
122
+ margin: -1.5rem 0 0;
123
+ max-width: none;
124
+ height: 250px;
125
+ }
126
+
127
+ .cardLift .image {
128
+ margin-left: -1.5rem;
129
+ margin-right: -1.5rem;
130
+ width: calc(100% + 3rem);
131
+ }
132
+ }
133
+
134
+ .imageContent {
135
+ position: absolute;
136
+ inset: 0;
137
+ display: flex;
138
+ flex-direction: column;
139
+ justify-content: flex-end;
140
+
141
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
142
+ color: white;
143
+ }
144
+
145
+ .default {
146
+ background-color: var(--background-muted);
147
+ }
148
+
149
+ .icon {
150
+ background-color: hsl(var(--muted));
151
+ border-radius: var(--radius);
152
+ border: 1px solid hsl(var(--border) / 0.5);
153
+ box-shadow: 0 1px 2px hsl(var(--foreground) / 0.04);
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
157
+ position: relative;
158
+ z-index: 1;
159
+ padding: 6px;
160
+ margin-bottom: 1.125rem;
161
+ width: 32px;
162
+ height: 32px;
163
+ }
164
+
165
+ .action {
166
+ grid-column: 2;
167
+ grid-row: 1;
168
+ align-self: start;
169
+ justify-self: end;
170
+ }
171
+
172
+ @media (max-width: 640px) {
173
+ .card {
174
+ padding: 1.25rem;
175
+ gap: 1.25rem;
176
+ }
177
+
178
+ .title {
179
+ font-size: 1.125rem;
180
+ }
181
+
182
+ .description {
183
+ font-size: 0.9375rem;
184
+ line-height: 1.5;
185
+ }
186
+
187
+ .content {
188
+ font-size: 0.9375rem;
189
+ line-height: 1.5;
190
+ }
191
+
192
+ .icon {
193
+ width: 28px;
194
+ height: 28px;
195
+ margin-bottom: 1rem;
196
+ }
197
+ }