vibecarbon 0.8.0 → 0.10.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 (121) hide show
  1. package/LICENSE +110 -663
  2. package/README.md +4 -4
  3. package/carbon/.env.example +11 -0
  4. package/carbon/backup/compose-backup.sh +8 -0
  5. package/carbon/docker-compose.yml +11 -0
  6. package/carbon/ha/primary-init.sql +10 -2
  7. package/carbon/k8s/base/backup/cronjob.yaml +9 -0
  8. package/carbon/k8s/base/repl-gateway/kustomization.yaml +11 -0
  9. package/carbon/k8s/base/repl-gateway/repl-gateway.yaml +92 -0
  10. package/carbon/k8s/values/supabase.values.yaml +6 -1
  11. package/carbon/scripts/dev-init.js +6 -2
  12. package/carbon/src/client/assets/vibecarbon-icon.svg +6 -6
  13. package/carbon/src/client/assets/vibecarbon-wordmark-text-dark.svg +12 -0
  14. package/carbon/src/client/assets/vibecarbon-wordmark-text-light.svg +12 -0
  15. package/carbon/src/client/components/AIIntegrationSection.tsx +1 -3
  16. package/carbon/src/client/components/AppSidebar.tsx +35 -28
  17. package/carbon/src/client/components/CTAFooter.tsx +1 -1
  18. package/carbon/src/client/components/ComparisonSection.tsx +1 -3
  19. package/carbon/src/client/components/ContentPanel.tsx +1 -1
  20. package/carbon/src/client/components/DeploymentScenariosSection.tsx +1 -3
  21. package/carbon/src/client/components/FAQSection.tsx +1 -1
  22. package/carbon/src/client/components/Hero.tsx +13 -1
  23. package/carbon/src/client/components/Logo.tsx +66 -49
  24. package/carbon/src/client/components/Nav.tsx +1 -1
  25. package/carbon/src/client/components/NewsletterSignup.tsx +1 -1
  26. package/carbon/src/client/components/NotificationDrawer.tsx +3 -3
  27. package/carbon/src/client/components/PageHeader.tsx +1 -1
  28. package/carbon/src/client/components/PricingSection.tsx +2 -6
  29. package/carbon/src/client/components/TechStackSection.tsx +1 -3
  30. package/carbon/src/client/components/effects/FilmGrainOverlay.tsx +5 -0
  31. package/carbon/src/client/components/ui/button.tsx +1 -1
  32. package/carbon/src/client/components/ui/card.tsx +1 -1
  33. package/carbon/src/client/components/ui/dialog.tsx +1 -1
  34. package/carbon/src/client/components/ui/dropdown-menu.tsx +2 -2
  35. package/carbon/src/client/components/ui/popover.tsx +1 -1
  36. package/carbon/src/client/components/ui/select.tsx +1 -1
  37. package/carbon/src/client/components/ui/sidebar.tsx +1 -1
  38. package/carbon/src/client/index.css +186 -104
  39. package/carbon/src/client/lib/supabase.ts +41 -0
  40. package/carbon/src/client/lib/theme.ts +14 -14
  41. package/carbon/src/client/locales/de.json +27 -12
  42. package/carbon/src/client/locales/en.json +33 -18
  43. package/carbon/src/client/locales/es.json +27 -12
  44. package/carbon/src/client/locales/fr.json +27 -12
  45. package/carbon/src/client/locales/pt.json +27 -12
  46. package/carbon/src/client/pages/Blog.tsx +1 -1
  47. package/carbon/src/client/pages/Changelog.tsx +1 -1
  48. package/carbon/src/client/pages/Checkout.tsx +1 -1
  49. package/carbon/src/client/pages/Dashboard.tsx +33 -40
  50. package/carbon/src/client/pages/Docs.tsx +1 -1
  51. package/carbon/src/client/pages/Home.tsx +1 -1
  52. package/carbon/src/client/pages/Legal.tsx +1 -1
  53. package/carbon/src/server/billing/providers/paddle.ts +21 -2
  54. package/carbon/src/server/billing/providers/polar.ts +14 -0
  55. package/carbon/src/server/index.ts +12 -9
  56. package/carbon/src/server/lib/client-ip.ts +68 -0
  57. package/carbon/src/server/lib/env.ts +20 -6
  58. package/carbon/src/server/lib/rate-limiter.ts +11 -65
  59. package/carbon/src/server/lib/supabase.ts +10 -4
  60. package/carbon/src/server/middleware/requireOrgRole.ts +76 -0
  61. package/carbon/src/server/middleware/requirePlan.ts +64 -34
  62. package/carbon/src/server/middleware/requireSuperAdmin.ts +26 -0
  63. package/carbon/src/server/routes/v1/admin/contact.ts +5 -20
  64. package/carbon/src/server/routes/v1/admin/jobs.ts +6 -20
  65. package/carbon/src/server/routes/v1/admin/newsletter.ts +26 -23
  66. package/carbon/src/server/routes/v1/auth.ts +5 -2
  67. package/carbon/src/server/routes/v1/billing.ts +24 -64
  68. package/carbon/src/server/routes/v1/index.ts +8 -1
  69. package/carbon/src/server/routes/v1/newsletter.ts +18 -12
  70. package/carbon/src/server/routes/v1/theme.ts +36 -15
  71. package/carbon/supabase/migrations/00003_pg_cron.sql +2 -2
  72. package/carbon/supabase/migrations/00004_contact_newsletter.sql +24 -14
  73. package/carbon/tests/component/supabase-env-guard.test.ts +54 -0
  74. package/carbon/tests/integration/server/middleware/rate-limit-ip-spoof.test.ts +48 -0
  75. package/carbon/tests/integration/server/middleware/require-plan.test.ts +101 -0
  76. package/carbon/tests/integration/server/routes/admin-newsletter.test.ts +93 -0
  77. package/carbon/tests/integration/server/routes/billing-subscription-idor.test.ts +92 -0
  78. package/carbon/tests/integration/server/routes/newsletter-unsubscribe.test.ts +65 -0
  79. package/carbon/tests/integration/server/routes/theme-validation.test.ts +66 -0
  80. package/carbon/tests/unit/server/client-ip.test.ts +76 -0
  81. package/carbon/tests/unit/server/env-bool.test.ts +63 -0
  82. package/carbon/tests/unit/server/paddle-webhook.test.ts +57 -0
  83. package/carbon/tests/unit/server/polar-webhook.test.ts +63 -0
  84. package/carbon/tests/unit/server/supabase-client.test.ts +30 -0
  85. package/carbon/volumes/db/wal-archive.sh +36 -0
  86. package/package.json +2 -2
  87. package/src/backup.js +0 -2
  88. package/src/cli.js +16 -0
  89. package/src/deploy.js +16 -5
  90. package/src/destroy.js +78 -0
  91. package/src/failover.js +118 -237
  92. package/src/lib/command.js +19 -1
  93. package/src/lib/deploy/compose/ha.js +180 -102
  94. package/src/lib/deploy/compose/index.js +63 -20
  95. package/src/lib/deploy/image.js +19 -23
  96. package/src/lib/deploy/k8s/ha/index.js +397 -150
  97. package/src/lib/deploy/k8s/k3s.js +65 -14
  98. package/src/lib/deploy/orchestrator.js +176 -6
  99. package/src/lib/deploy/remote-build.js +8 -1
  100. package/src/lib/deploy/replication.js +540 -0
  101. package/src/lib/deploy/state.js +62 -5
  102. package/src/lib/deploy/utils.js +30 -2
  103. package/src/lib/deploy/wireguard.js +146 -0
  104. package/src/lib/host-keys.js +120 -5
  105. package/src/lib/iac/index.js +57 -23
  106. package/src/lib/licensing/gate.js +59 -0
  107. package/src/lib/licensing/index.js +1 -1
  108. package/src/lib/licensing/tiers.js +6 -5
  109. package/src/lib/prod-confirm.js +65 -0
  110. package/src/lib/providers/hetzner-s3.js +85 -0
  111. package/src/lib/ssh.js +35 -35
  112. package/src/restore.js +102 -14
  113. package/src/scale.js +4 -2
  114. package/src/status.js +122 -0
  115. package/src/upgrade.js +0 -4
  116. package/carbon/ha/activate-standby.sh +0 -52
  117. package/carbon/ha/standby-init.sh +0 -74
  118. package/carbon/src/client/assets/vibecarbon-logo-dark.svg +0 -29
  119. package/carbon/src/client/assets/vibecarbon-logo-light.svg +0 -29
  120. package/carbon/src/server/lib/request.ts +0 -34
  121. package/src/lib/pod-backups.js +0 -53
@@ -59,7 +59,7 @@ export function ContentPanel({
59
59
  const resolvedWidth = maxWidth ?? variantToWidth[variant];
60
60
 
61
61
  return (
62
- <div className="flex-1 pl-1 pr-6 py-6">
62
+ <div className="flex-1 pr-8 py-6">
63
63
  <div className={cn(maxWidthClasses[resolvedWidth], 'space-y-6', className)}>{children}</div>
64
64
  </div>
65
65
  );
@@ -106,9 +106,7 @@ export function DeploymentScenariosSection() {
106
106
  </motion.div>
107
107
  <h2 className="mb-4 text-3xl font-black tracking-tight md:text-5xl">
108
108
  {t('landing.deploy.headline')}{' '}
109
- <span className="bg-gradient-to-r from-primary via-cyan-400 to-secondary-accent bg-clip-text text-transparent">
110
- {t('landing.deploy.headlineHighlight')}
111
- </span>
109
+ <span className="text-primary">{t('landing.deploy.headlineHighlight')}</span>
112
110
  </h2>
113
111
  <p className="mx-auto max-w-2xl text-lg text-muted-foreground">
114
112
  {t('landing.deploy.subheading')}
@@ -60,7 +60,7 @@ export function FAQSection() {
60
60
  >
61
61
  <AccordionItem
62
62
  value={faq.question}
63
- className="border border-white/8 dark:border-white/8 bg-white/[0.02] dark:bg-white/[0.02] rounded-xl mb-3 overflow-hidden backdrop-blur-sm"
63
+ className="border border-border bg-card/40 dark:border-white/8 dark:bg-white/[0.02] rounded-xl mb-3 overflow-hidden backdrop-blur-sm"
64
64
  >
65
65
  <AccordionTrigger>{faq.question}</AccordionTrigger>
66
66
  <AccordionContent>
@@ -532,7 +532,19 @@ export default function Hero() {
532
532
  transition={{ duration: 0.8, delay: 0.2 }}
533
533
  className="text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto mb-6"
534
534
  >
535
- <span className="text-foreground/80">{t('landing.hero.subheading')}</span>
535
+ <span className="text-foreground/80">
536
+ {(() => {
537
+ const subheading = t('landing.hero.subheading');
538
+ const [first, second] = subheading.split(/\.\s+/);
539
+ if (second === undefined) return subheading;
540
+ return (
541
+ <>
542
+ {first}.<br />
543
+ {second}
544
+ </>
545
+ );
546
+ })()}
547
+ </span>
536
548
  </motion.p>
537
549
  </div>
538
550
 
@@ -1,4 +1,8 @@
1
+ import { useTheme } from 'next-themes';
1
2
  import { cn } from '@/lib/utils';
3
+ import vibecarbonIconUrl from '../assets/vibecarbon-icon.svg';
4
+ import vibecarbonWordmarkTextDarkUrl from '../assets/vibecarbon-wordmark-text-dark.svg';
5
+ import vibecarbonWordmarkTextLightUrl from '../assets/vibecarbon-wordmark-text-light.svg';
2
6
 
3
7
  // Split across two lines so the formatted output is stable regardless of the
4
8
  // rendered project-name length (biome wraps >100-char lines, which caused CI
@@ -6,51 +10,71 @@ import { cn } from '@/lib/utils';
6
10
  const DEFAULT_PROJECT_NAME = '{{PROJECT_NAME}}';
7
11
  export const PROJECT_NAME = import.meta.env.VITE_PROJECT_NAME ?? DEFAULT_PROJECT_NAME;
8
12
 
13
+ // Set only in vibecarbon.com's own local .env/.env.local (never scaffolded
14
+ // into user projects) to swap in the hand-vectorized "vibecarbon" lockup in
15
+ // place of the generic icon+PROJECT_NAME composition below.
16
+ const BRAND_LOCKUP_ENABLED = import.meta.env.VITE_BRAND_LOCKUP === 'true';
17
+
9
18
  interface LogoProps {
10
19
  size?: 'sm' | 'default' | 'lg';
11
20
  className?: string;
12
21
  }
13
22
 
23
+ // `w-auto` derives width from the icon's own intrinsic aspect ratio instead
24
+ // of a hand-computed guess — replace the icon below with your own brand mark.
14
25
  const sizeClasses = {
15
- sm: 'size-6',
16
- default: 'size-8',
17
- lg: 'size-10',
26
+ sm: 'h-6 w-auto',
27
+ default: 'h-8 w-auto',
28
+ lg: 'h-10 w-auto',
18
29
  };
19
30
 
20
- /**
21
- * Default logo mark — replace the SVG below with your own brand icon.
22
- * Liquid glass hex with circular cutout — gradient fill + specular highlight.
23
- */
31
+ /** Default logo mark — replace the SVG asset below with your own brand icon. */
24
32
  export function Logo({ size = 'default', className }: LogoProps) {
25
33
  return (
26
- <svg
27
- viewBox="0 0 64 64"
28
- fill="none"
29
- xmlns="http://www.w3.org/2000/svg"
30
- className={cn('shrink-0', sizeClasses[size], className)}
34
+ <img
35
+ src={vibecarbonIconUrl}
36
+ alt=""
31
37
  aria-hidden="true"
32
- >
33
- <defs>
34
- <linearGradient id="logo-fill" x1="0" y1="0" x2="0" y2="1">
35
- <stop offset="0%" stopColor="#5eead4" />
36
- <stop offset="100%" stopColor="#0d9488" />
37
- </linearGradient>
38
- <linearGradient id="logo-shine" x1="0.2" y1="0" x2="0.8" y2="0.6">
39
- <stop offset="0%" stopColor="white" stopOpacity="0.5" />
40
- <stop offset="100%" stopColor="white" stopOpacity="0" />
41
- </linearGradient>
42
- </defs>
43
- <path
44
- fillRule="evenodd"
45
- clipRule="evenodd"
46
- d="M32 2L58 17V47L32 62L6 47V17Z M48 32A16 16 0 1 1 16 32A16 16 0 1 1 48 32Z"
47
- fill="url(#logo-fill)"
48
- />
49
- <path d="M32 2L58 17V30L32 38L6 30V17Z" fill="url(#logo-shine)" />
50
- </svg>
38
+ className={cn('shrink-0', sizeClasses[size], className)}
39
+ />
51
40
  );
52
41
  }
53
42
 
43
+ const wordmarkTextConfig = {
44
+ sm: { text: 'text-sm font-medium' },
45
+ default: { text: 'text-[1.24rem] font-semibold tracking-tight' },
46
+ lg: { text: 'text-[1.375rem] font-semibold tracking-tight' },
47
+ };
48
+
49
+ interface WordmarkTextProps {
50
+ size?: 'sm' | 'default' | 'lg';
51
+ className?: string;
52
+ /** Overrides theme-based asset selection; branded path only. */
53
+ forceTheme?: 'light' | 'dark';
54
+ }
55
+
56
+ /**
57
+ * Text-only half of the wordmark, exported at the same height as <Logo> for
58
+ * each size tier so the two compose edge-to-edge with a plain gap — no
59
+ * fused-image or margin tuning needed. Pair with a standalone <Logo> for a
60
+ * collapsible sidebar header: the icon stays a single, never-swapped element
61
+ * (the standard collapse pattern — see shadcn/ui's TeamSwitcher), and only
62
+ * this text piece toggles visibility via `group-data-[collapsible=icon]`.
63
+ */
64
+ export function WordmarkText({ size = 'default', className, forceTheme }: WordmarkTextProps) {
65
+ const { resolvedTheme } = useTheme();
66
+
67
+ if (BRAND_LOCKUP_ENABLED) {
68
+ const theme = forceTheme ?? resolvedTheme;
69
+ const src = theme === 'light' ? vibecarbonWordmarkTextLightUrl : vibecarbonWordmarkTextDarkUrl;
70
+ return (
71
+ <img src={src} alt={PROJECT_NAME} className={cn('shrink-0', sizeClasses[size], className)} />
72
+ );
73
+ }
74
+
75
+ return <span className={cn(wordmarkTextConfig[size].text, className)}>{PROJECT_NAME}</span>;
76
+ }
77
+
54
78
  /**
55
79
  * Logo mark + project name text.
56
80
  * Sizes: sm (footers), default (nav/sidebar), lg (auth hero pages).
@@ -60,28 +84,21 @@ interface WordmarkProps {
60
84
  size?: 'sm' | 'default' | 'lg';
61
85
  className?: string;
62
86
  textClassName?: string;
87
+ forceTheme?: 'light' | 'dark';
63
88
  }
64
89
 
65
- const wordmarkConfig = {
66
- sm: { logoSize: 'sm' as const, text: 'text-sm font-medium', gap: 'gap-1.5' },
67
- default: {
68
- logoSize: 'lg' as const,
69
- text: 'text-[1.24rem] font-semibold tracking-tight',
70
- gap: 'gap-1',
71
- },
72
- lg: {
73
- logoSize: 'lg' as const,
74
- text: 'text-[1.375rem] font-semibold tracking-tight',
75
- gap: 'gap-1',
76
- },
77
- };
90
+ const wordmarkGap = { sm: 'gap-1.5', default: 'gap-2', lg: 'gap-2' };
78
91
 
79
- export function Wordmark({ size = 'default', className, textClassName }: WordmarkProps) {
80
- const { logoSize, text, gap } = wordmarkConfig[size];
92
+ export function Wordmark({
93
+ size = 'default',
94
+ className,
95
+ textClassName,
96
+ forceTheme,
97
+ }: WordmarkProps) {
81
98
  return (
82
- <span className={cn('inline-flex items-center', gap, className)}>
83
- <Logo size={logoSize} />
84
- <span className={cn(text, textClassName)}>{PROJECT_NAME}</span>
99
+ <span className={cn('inline-flex items-center', wordmarkGap[size], className)}>
100
+ <Logo size={size} />
101
+ <WordmarkText size={size} className={textClassName} forceTheme={forceTheme} />
85
102
  </span>
86
103
  );
87
104
  }
@@ -89,7 +89,7 @@ export function Nav() {
89
89
  <div className="relative mx-auto max-w-7xl h-full px-6 flex items-center justify-between">
90
90
  {/* Logo & Brand */}
91
91
  <Link to="/" className="group">
92
- <Wordmark size="lg" />
92
+ <Wordmark size="sm" />
93
93
  </Link>
94
94
 
95
95
  {/* Desktop nav */}
@@ -131,8 +131,8 @@ export function NewsletterSignup({
131
131
  onClick={handleSubmit}
132
132
  disabled={subscribeMutation.isPending}
133
133
  size="default"
134
- sparkle
135
134
  className="w-full sm:w-auto"
135
+ sparkle
136
136
  >
137
137
  {subscribeMutation.isPending && <Loader2 className="size-3 mr-1 animate-spin" />}
138
138
  {resolvedButtonText}
@@ -22,11 +22,11 @@ function getNotificationStyles(type: NotificationType) {
22
22
  case 'error':
23
23
  return 'bg-destructive border-destructive text-white';
24
24
  case 'warning':
25
- return 'bg-warning border-warning text-white';
25
+ return 'bg-warning border-warning text-black/90';
26
26
  case 'success':
27
- return 'bg-success border-success text-white';
27
+ return 'bg-success border-success text-white dark:text-black/90';
28
28
  default:
29
- return 'bg-primary border-primary text-white';
29
+ return 'bg-primary border-primary text-primary-foreground';
30
30
  }
31
31
  }
32
32
 
@@ -7,7 +7,7 @@ interface PageHeaderProps {
7
7
 
8
8
  export function PageHeader({ title, description }: PageHeaderProps) {
9
9
  return (
10
- <header className="flex h-10 items-center justify-between pl-1 pr-6 mt-6">
10
+ <header className="flex h-10 items-center justify-between pr-8 mt-6">
11
11
  <div className="flex items-center gap-3">
12
12
  <div className="relative">
13
13
  <h1 className="text-lg font-semibold leading-tight">{title}</h1>
@@ -201,13 +201,9 @@ export function PricingSection() {
201
201
  </motion.div>
202
202
  <h2 className="mb-4 text-3xl font-black tracking-tight md:text-5xl">
203
203
  {t('landing.pricing.headline1')}{' '}
204
- <span className="bg-gradient-to-r from-muted-foreground to-foreground bg-clip-text text-transparent">
205
- {t('landing.pricing.headlineSketch')}
206
- </span>{' '}
204
+ <span className="text-foreground">{t('landing.pricing.headlineSketch')}</span>{' '}
207
205
  {t('landing.pricing.headline2')}{' '}
208
- <span className="bg-gradient-to-r from-primary to-secondary-accent bg-clip-text text-transparent">
209
- {t('landing.pricing.headlineScale')}
210
- </span>
206
+ <span className="text-primary">{t('landing.pricing.headlineScale')}</span>
211
207
  </h2>
212
208
  <p className="text-lg text-muted-foreground">{t('landing.pricing.subheading')}</p>
213
209
  </motion.div>
@@ -60,9 +60,7 @@ export function TechStackSection() {
60
60
  </motion.div>
61
61
  <h2 className="text-3xl md:text-5xl font-black tracking-tight mb-4">
62
62
  {t('landing.techStack.headline')}{' '}
63
- <span className="bg-gradient-to-r from-primary via-cyan-400 to-secondary-accent bg-clip-text text-transparent">
64
- {t('landing.techStack.headlineHighlight')}
65
- </span>
63
+ <span className="text-primary">{t('landing.techStack.headlineHighlight')}</span>
66
64
  </h2>
67
65
  <p className="text-lg text-muted-foreground max-w-2xl mx-auto">
68
66
  {t('landing.techStack.subheading')}
@@ -1,3 +1,4 @@
1
+ import { useTheme } from 'next-themes';
1
2
  import { useReducedMotion } from '../../hooks/useReducedMotion';
2
3
 
3
4
  interface FilmGrainOverlayProps {
@@ -8,11 +9,15 @@ interface FilmGrainOverlayProps {
8
9
  /**
9
10
  * Film grain overlay for anodized metal/industrial feel
10
11
  * Uses SVG noise filter for subtle texture
12
+ * Dark mode only — grain reads as dirt on light surfaces
11
13
  */
12
14
  export function FilmGrainOverlay({ opacity = 0.03, animated = false }: FilmGrainOverlayProps) {
13
15
  const prefersReducedMotion = useReducedMotion();
16
+ const { resolvedTheme } = useTheme();
14
17
  const shouldAnimate = animated && !prefersReducedMotion;
15
18
 
19
+ if (resolvedTheme !== 'dark') return null;
20
+
16
21
  return (
17
22
  <div
18
23
  className="fixed inset-0 pointer-events-none z-[9999]"
@@ -18,7 +18,7 @@ const buttonVariants = cva(
18
18
  secondary:
19
19
  'border-transparent bg-secondary text-secondary-foreground hover:brightness-125 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
20
20
  ghost:
21
- 'border-transparent hover:bg-white/10 hover:text-foreground hover:brightness-125 aria-expanded:bg-muted aria-expanded:text-foreground',
21
+ 'border-transparent hover:bg-foreground/8 hover:text-foreground dark:hover:brightness-125 aria-expanded:bg-muted aria-expanded:text-foreground',
22
22
  destructive:
23
23
  'border-transparent bg-destructive/10 hover:bg-destructive/25 hover:brightness-115 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40',
24
24
  link: 'border-transparent text-primary underline-offset-4 hover:underline hover:brightness-115',
@@ -12,7 +12,7 @@ function Card({
12
12
  data-slot="card"
13
13
  data-size={size}
14
14
  className={cn(
15
- 'bg-card text-card-foreground gap-6 overflow-hidden rounded-[var(--radius-card)] py-6 text-sm ring-1 ring-border has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-[var(--radius-card)] *:[img:last-child]:rounded-b-[var(--radius-card)] group/card flex flex-col backdrop-blur-sm',
15
+ 'bg-card text-card-foreground gap-6 overflow-hidden rounded-[var(--radius-card)] py-6 text-sm ring-1 ring-border shadow-card has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-[var(--radius-card)] *:[img:last-child]:rounded-b-[var(--radius-card)] group/card flex flex-col',
16
16
  className
17
17
  )}
18
18
  {...props}
@@ -47,7 +47,7 @@ function DialogContent({
47
47
  <DialogPrimitive.Popup
48
48
  data-slot="dialog-content"
49
49
  className={cn(
50
- 'bg-card text-card-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-border grid max-w-[calc(100%-2rem)] gap-6 rounded-[var(--radius-card)] p-6 text-sm ring-1 duration-100 sm:max-w-md fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none',
50
+ 'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-border grid max-w-[calc(100%-2rem)] gap-6 rounded-[var(--radius-card)] p-6 text-sm ring-1 shadow-pop duration-100 sm:max-w-md fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none',
51
51
  className
52
52
  )}
53
53
  {...props}
@@ -38,7 +38,7 @@ function DropdownMenuContent({
38
38
  <MenuPrimitive.Popup
39
39
  data-slot="dropdown-menu-content"
40
40
  className={cn(
41
- 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 border border-border bg-popover text-popover-foreground min-w-48 rounded-md p-1 shadow-lg duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden',
41
+ 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 border border-border bg-popover text-popover-foreground min-w-48 rounded-md p-1 shadow-pop duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden',
42
42
  className
43
43
  )}
44
44
  {...props}
@@ -132,7 +132,7 @@ function DropdownMenuSubContent({
132
132
  <DropdownMenuContent
133
133
  data-slot="dropdown-menu-sub-content"
134
134
  className={cn(
135
- 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 border border-border bg-popover text-popover-foreground min-w-36 rounded-md p-1 shadow-lg duration-100 w-auto',
135
+ 'data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 border border-border bg-popover text-popover-foreground min-w-36 rounded-md p-1 shadow-pop duration-100 w-auto',
136
136
  className
137
137
  )}
138
138
  align={align}
@@ -32,7 +32,7 @@ function PopoverContent({
32
32
  <PopoverPrimitive.Popup
33
33
  data-slot="popover-content"
34
34
  className={cn(
35
- 'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 ring-foreground/5 flex flex-col gap-4 rounded-2xl p-4 text-sm shadow-2xl ring-1 duration-100 z-50 w-72 origin-(--transform-origin) outline-hidden',
35
+ 'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 ring-foreground/5 flex flex-col gap-4 rounded-2xl p-4 text-sm shadow-pop ring-1 duration-100 z-50 w-72 origin-(--transform-origin) outline-hidden',
36
36
  className
37
37
  )}
38
38
  {...props}
@@ -78,7 +78,7 @@ function SelectContent({
78
78
  <SelectPrimitive.Popup
79
79
  data-slot="select-content"
80
80
  className={cn(
81
- 'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 ring-foreground/5 min-w-36 rounded-2xl shadow-2xl ring-1 duration-100 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto',
81
+ 'bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open: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 ring-foreground/5 min-w-36 rounded-2xl shadow-pop ring-1 duration-100 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto',
82
82
  className
83
83
  )}
84
84
  {...props}
@@ -245,7 +245,7 @@ function Sidebar({
245
245
  <div
246
246
  data-sidebar="sidebar"
247
247
  data-slot="sidebar-inner"
248
- className="bg-sidebar group-data-[variant=floating]:ring-sidebar-border group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 flex size-full flex-col gap-6 p-6"
248
+ className="bg-sidebar backdrop-blur-md dark:backdrop-blur-none group-data-[variant=floating]:ring-sidebar-border group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 flex size-full flex-col gap-6 p-6"
249
249
  >
250
250
  {children}
251
251
  </div>