vibecarbon 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 (495) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +188 -0
  3. package/carbon/.claude/hooks/task-completed-gate.sh +19 -0
  4. package/carbon/.claude/hooks/teammate-idle-gate.sh +29 -0
  5. package/carbon/.claude/settings.json +29 -0
  6. package/carbon/.cursor/rules/vibecarbon.mdc +6 -0
  7. package/carbon/.dockerignore +57 -0
  8. package/carbon/.env.example +219 -0
  9. package/carbon/.github/copilot-instructions.md +3 -0
  10. package/carbon/.github/workflows/deploy.yml +346 -0
  11. package/carbon/.github/workflows/vibecarbon-build.yml +81 -0
  12. package/carbon/.windsurfrules +74 -0
  13. package/carbon/AGENTS.md +422 -0
  14. package/carbon/CLAUDE.md +3 -0
  15. package/carbon/DEVELOPMENT.md +187 -0
  16. package/carbon/Dockerfile +98 -0
  17. package/carbon/LICENSE +21 -0
  18. package/carbon/PRODUCTION.md +364 -0
  19. package/carbon/README.md +193 -0
  20. package/carbon/TESTING.md +138 -0
  21. package/carbon/backup/Dockerfile +75 -0
  22. package/carbon/backup/backup.sh +95 -0
  23. package/carbon/backup/compose-backup.sh +140 -0
  24. package/carbon/biome.json +83 -0
  25. package/carbon/cloud-init/docker-ce-setup.yaml +55 -0
  26. package/carbon/cloud-init/k3s/master-init.sh +215 -0
  27. package/carbon/cloud-init/k3s/supabase-init.sh +167 -0
  28. package/carbon/cloud-init/k3s/worker-init.sh +147 -0
  29. package/carbon/components.json +24 -0
  30. package/carbon/content/blog/authentication-guide.mdx +34 -0
  31. package/carbon/content/blog/getting-started.mdx +41 -0
  32. package/carbon/content/changelog/v0-1-0.mdx +40 -0
  33. package/carbon/content/docs/analytics.mdx +90 -0
  34. package/carbon/content/docs/authentication.mdx +231 -0
  35. package/carbon/content/docs/background-jobs.mdx +116 -0
  36. package/carbon/content/docs/cli.mdx +630 -0
  37. package/carbon/content/docs/database.mdx +236 -0
  38. package/carbon/content/docs/deployment.mdx +227 -0
  39. package/carbon/content/docs/development.mdx +238 -0
  40. package/carbon/content/docs/environments.mdx +84 -0
  41. package/carbon/content/docs/getting-started.mdx +112 -0
  42. package/carbon/content/docs/legal/privacy-policy.mdx +89 -0
  43. package/carbon/content/docs/legal/terms-of-service.mdx +99 -0
  44. package/carbon/content/docs/optional-services.mdx +160 -0
  45. package/carbon/db/Dockerfile +23 -0
  46. package/carbon/docker-compose.dev-init.yml +5 -0
  47. package/carbon/docker-compose.metabase.override.yml +14 -0
  48. package/carbon/docker-compose.metabase.prod.yml +28 -0
  49. package/carbon/docker-compose.metabase.yml +84 -0
  50. package/carbon/docker-compose.n8n.override.yml +14 -0
  51. package/carbon/docker-compose.n8n.prod.yml +31 -0
  52. package/carbon/docker-compose.n8n.yml +97 -0
  53. package/carbon/docker-compose.observability.override.yml +18 -0
  54. package/carbon/docker-compose.observability.prod.yml +28 -0
  55. package/carbon/docker-compose.observability.yml +125 -0
  56. package/carbon/docker-compose.override.yml +28 -0
  57. package/carbon/docker-compose.prod.yml +294 -0
  58. package/carbon/docker-compose.yml +508 -0
  59. package/carbon/docker-entrypoint.sh +12 -0
  60. package/carbon/ha/activate-standby.sh +52 -0
  61. package/carbon/ha/primary-init.sql +36 -0
  62. package/carbon/ha/standby-init.sh +74 -0
  63. package/carbon/k8s/LICENSE +99 -0
  64. package/carbon/k8s/README.md +272 -0
  65. package/carbon/k8s/base/app/deployment.yaml +130 -0
  66. package/carbon/k8s/base/app/hpa.yaml +44 -0
  67. package/carbon/k8s/base/app/kustomization.yaml +10 -0
  68. package/carbon/k8s/base/app/network-policy.yaml +124 -0
  69. package/carbon/k8s/base/app/pdb.yaml +14 -0
  70. package/carbon/k8s/base/app/rbac.yaml +36 -0
  71. package/carbon/k8s/base/app/service.yaml +16 -0
  72. package/carbon/k8s/base/backup/cronjob.yaml +120 -0
  73. package/carbon/k8s/base/backup/kustomization.yaml +7 -0
  74. package/carbon/k8s/base/backup/network-policy.yaml +31 -0
  75. package/carbon/k8s/base/backup/rbac.yaml +7 -0
  76. package/carbon/k8s/base/cluster-autoscaler/deployment.yaml +103 -0
  77. package/carbon/k8s/base/cluster-autoscaler/kustomization.yaml +17 -0
  78. package/carbon/k8s/base/cluster-autoscaler/rbac.yaml +109 -0
  79. package/carbon/k8s/base/config/configmap.yaml +48 -0
  80. package/carbon/k8s/base/config/kustomization.yaml +8 -0
  81. package/carbon/k8s/base/hetzner-ccm/kustomization.yaml +9 -0
  82. package/carbon/k8s/base/hetzner-csi/kustomization.yaml +9 -0
  83. package/carbon/k8s/base/kustomization.yaml +43 -0
  84. package/carbon/k8s/base/namespace.yaml +7 -0
  85. package/carbon/k8s/base/network-policies.yaml +95 -0
  86. package/carbon/k8s/base/registry/kustomization.yaml +5 -0
  87. package/carbon/k8s/base/registry/local-registry.yaml +193 -0
  88. package/carbon/k8s/base/traefik/certificate.yaml +21 -0
  89. package/carbon/k8s/base/traefik/configmap.yaml +13 -0
  90. package/carbon/k8s/base/traefik/deployment.yaml +165 -0
  91. package/carbon/k8s/base/traefik/ingressroute.yaml +141 -0
  92. package/carbon/k8s/base/traefik/kustomization.yaml +11 -0
  93. package/carbon/k8s/base/traefik/middleware.yaml +109 -0
  94. package/carbon/k8s/base/traefik/network-policy.yaml +92 -0
  95. package/carbon/k8s/base/traefik/service.yaml +38 -0
  96. package/carbon/k8s/flux/README.md +49 -0
  97. package/carbon/k8s/flux/clusters/primary/vibecarbon.yaml +128 -0
  98. package/carbon/k8s/flux/clusters/standby/vibecarbon.yaml +83 -0
  99. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-release.yaml +38 -0
  100. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-repository.yaml +16 -0
  101. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/kustomization.yaml +10 -0
  102. package/carbon/k8s/gitops/supabase/helm-release.yaml +66 -0
  103. package/carbon/k8s/gitops/supabase/helm-repository.yaml +18 -0
  104. package/carbon/k8s/gitops/supabase/kustomization.yaml +33 -0
  105. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-cloudflare.yaml +51 -0
  106. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-hetzner.yaml +59 -0
  107. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-manual.yaml +43 -0
  108. package/carbon/k8s/infra/cert-manager-resources/kustomization.yaml +15 -0
  109. package/carbon/k8s/infra/kustomization.yaml +21 -0
  110. package/carbon/k8s/infra/traefik-crds/kustomization.yaml +10 -0
  111. package/carbon/k8s/overlays/local/configmap.yaml +23 -0
  112. package/carbon/k8s/overlays/local/ingressroute-studio.yaml +104 -0
  113. package/carbon/k8s/overlays/local/kustomization.yaml +215 -0
  114. package/carbon/k8s/overlays/local/namespace.yaml +8 -0
  115. package/carbon/k8s/overlays/local/secrets.yaml +32 -0
  116. package/carbon/k8s/overlays/production/kustomization.yaml +34 -0
  117. package/carbon/k8s/test-local.sh +318 -0
  118. package/carbon/k8s/values/supabase.values.yaml +133 -0
  119. package/carbon/package.json +154 -0
  120. package/carbon/runtime.Dockerfile +17 -0
  121. package/carbon/scripts/_dev-jwt.mjs +45 -0
  122. package/carbon/scripts/check-rls.ts +53 -0
  123. package/carbon/scripts/dev-init.js +191 -0
  124. package/carbon/scripts/dev.js +191 -0
  125. package/carbon/scripts/docker-down.js +63 -0
  126. package/carbon/scripts/docker-logs.js +59 -0
  127. package/carbon/scripts/docker-up.js +222 -0
  128. package/carbon/scripts/generate-dev-configs.sh +131 -0
  129. package/carbon/scripts/generate-rss.ts +116 -0
  130. package/carbon/scripts/generate-sitemap.ts +102 -0
  131. package/carbon/scripts/k8s-apply.js +71 -0
  132. package/carbon/scripts/k8s-delete.js +75 -0
  133. package/carbon/scripts/lib/manifest.js +176 -0
  134. package/carbon/scripts/secret-scan.mjs +278 -0
  135. package/carbon/scripts/validate-dev-configs.sh +101 -0
  136. package/carbon/src/client/App.tsx +202 -0
  137. package/carbon/src/client/assets/hyperformant-dark.svg +29 -0
  138. package/carbon/src/client/assets/hyperformant-light.svg +29 -0
  139. package/carbon/src/client/assets/logos/aider.svg +1 -0
  140. package/carbon/src/client/assets/logos/antigravity.svg +1 -0
  141. package/carbon/src/client/assets/logos/bolt.svg +1 -0
  142. package/carbon/src/client/assets/logos/claude-code.svg +1 -0
  143. package/carbon/src/client/assets/logos/copilot.svg +1 -0
  144. package/carbon/src/client/assets/logos/cursor.svg +1 -0
  145. package/carbon/src/client/assets/logos/gemini-cli.svg +1 -0
  146. package/carbon/src/client/assets/logos/lovable.svg +1 -0
  147. package/carbon/src/client/assets/logos/openai-codex.svg +1 -0
  148. package/carbon/src/client/assets/logos/v0.svg +1 -0
  149. package/carbon/src/client/assets/logos/windsurf.svg +1 -0
  150. package/carbon/src/client/assets/vibecarbon-icon.svg +19 -0
  151. package/carbon/src/client/assets/vibecarbon-logo-dark.svg +29 -0
  152. package/carbon/src/client/assets/vibecarbon-logo-light.svg +29 -0
  153. package/carbon/src/client/components/AIIntegrationSection.tsx +120 -0
  154. package/carbon/src/client/components/AppSidebar.tsx +760 -0
  155. package/carbon/src/client/components/ArchitectureSection.tsx +46 -0
  156. package/carbon/src/client/components/CTAFooter.tsx +59 -0
  157. package/carbon/src/client/components/ComparisonSection.tsx +132 -0
  158. package/carbon/src/client/components/ContentPanel.tsx +66 -0
  159. package/carbon/src/client/components/ContentSkeleton.tsx +21 -0
  160. package/carbon/src/client/components/ErrorBoundary.tsx +46 -0
  161. package/carbon/src/client/components/FAQSection.tsx +76 -0
  162. package/carbon/src/client/components/FileUpload.tsx +210 -0
  163. package/carbon/src/client/components/HeaderActions.tsx +17 -0
  164. package/carbon/src/client/components/Hero.tsx +608 -0
  165. package/carbon/src/client/components/ImpersonationBanner.tsx +31 -0
  166. package/carbon/src/client/components/LanguageSwitcher.tsx +67 -0
  167. package/carbon/src/client/components/Logo.tsx +87 -0
  168. package/carbon/src/client/components/LogoStrip.tsx +76 -0
  169. package/carbon/src/client/components/MetricsStrip.tsx +48 -0
  170. package/carbon/src/client/components/Nav.tsx +195 -0
  171. package/carbon/src/client/components/NewsletterSignup.tsx +147 -0
  172. package/carbon/src/client/components/NotificationDrawer.tsx +171 -0
  173. package/carbon/src/client/components/PageHeader.tsx +24 -0
  174. package/carbon/src/client/components/PlanGate.tsx +36 -0
  175. package/carbon/src/client/components/PricingSection.tsx +371 -0
  176. package/carbon/src/client/components/SEO.tsx +34 -0
  177. package/carbon/src/client/components/SmoothScroll.tsx +45 -0
  178. package/carbon/src/client/components/TechStackSection.tsx +165 -0
  179. package/carbon/src/client/components/WorkflowSection.tsx +604 -0
  180. package/carbon/src/client/components/admin/DockerLogs.tsx +276 -0
  181. package/carbon/src/client/components/admin/PerformanceMetrics.tsx +230 -0
  182. package/carbon/src/client/components/admin/ServiceCard.tsx +45 -0
  183. package/carbon/src/client/components/admin/ServicesStatus.tsx +296 -0
  184. package/carbon/src/client/components/architecture/ArchitectureDiagram.tsx +413 -0
  185. package/carbon/src/client/components/auth/AuthProvider.tsx +466 -0
  186. package/carbon/src/client/components/effects/FilmGrainOverlay.tsx +35 -0
  187. package/carbon/src/client/components/effects/FresnelEdge.tsx +49 -0
  188. package/carbon/src/client/components/effects/GlowTracker.tsx +46 -0
  189. package/carbon/src/client/components/effects/SparkBurst.tsx +145 -0
  190. package/carbon/src/client/components/effects/VGlowEffect.tsx +83 -0
  191. package/carbon/src/client/components/effects/index.ts +2 -0
  192. package/carbon/src/client/components/layouts/SidebarLayout.tsx +20 -0
  193. package/carbon/src/client/components/scroll/ScrollSection.tsx +76 -0
  194. package/carbon/src/client/components/scroll/ScrollytellingProvider.tsx +81 -0
  195. package/carbon/src/client/components/scroll/index.ts +2 -0
  196. package/carbon/src/client/components/ui/accordion.tsx +71 -0
  197. package/carbon/src/client/components/ui/alert-dialog.tsx +162 -0
  198. package/carbon/src/client/components/ui/alert.tsx +73 -0
  199. package/carbon/src/client/components/ui/aspect-ratio.tsx +22 -0
  200. package/carbon/src/client/components/ui/avatar.tsx +91 -0
  201. package/carbon/src/client/components/ui/badge.tsx +50 -0
  202. package/carbon/src/client/components/ui/breadcrumb.tsx +100 -0
  203. package/carbon/src/client/components/ui/button-group.tsx +78 -0
  204. package/carbon/src/client/components/ui/button.tsx +120 -0
  205. package/carbon/src/client/components/ui/calendar.tsx +182 -0
  206. package/carbon/src/client/components/ui/card.tsx +85 -0
  207. package/carbon/src/client/components/ui/carousel.tsx +227 -0
  208. package/carbon/src/client/components/ui/chart.tsx +357 -0
  209. package/carbon/src/client/components/ui/checkbox.tsx +27 -0
  210. package/carbon/src/client/components/ui/collapsible.tsx +15 -0
  211. package/carbon/src/client/components/ui/command.tsx +178 -0
  212. package/carbon/src/client/components/ui/context-menu.tsx +233 -0
  213. package/carbon/src/client/components/ui/dialog.tsx +132 -0
  214. package/carbon/src/client/components/ui/drawer.tsx +118 -0
  215. package/carbon/src/client/components/ui/dropdown-menu.tsx +242 -0
  216. package/carbon/src/client/components/ui/empty.tsx +94 -0
  217. package/carbon/src/client/components/ui/field.tsx +226 -0
  218. package/carbon/src/client/components/ui/hover-card.tsx +44 -0
  219. package/carbon/src/client/components/ui/input-group.tsx +146 -0
  220. package/carbon/src/client/components/ui/input-otp.tsx +83 -0
  221. package/carbon/src/client/components/ui/input.tsx +20 -0
  222. package/carbon/src/client/components/ui/item.tsx +188 -0
  223. package/carbon/src/client/components/ui/kbd.tsx +26 -0
  224. package/carbon/src/client/components/ui/label.tsx +21 -0
  225. package/carbon/src/client/components/ui/menubar.tsx +250 -0
  226. package/carbon/src/client/components/ui/navigation-menu.tsx +155 -0
  227. package/carbon/src/client/components/ui/pagination.tsx +102 -0
  228. package/carbon/src/client/components/ui/popover.tsx +75 -0
  229. package/carbon/src/client/components/ui/progress.tsx +66 -0
  230. package/carbon/src/client/components/ui/radio-group.tsx +36 -0
  231. package/carbon/src/client/components/ui/resizable.tsx +46 -0
  232. package/carbon/src/client/components/ui/scroll-area.tsx +48 -0
  233. package/carbon/src/client/components/ui/select.tsx +186 -0
  234. package/carbon/src/client/components/ui/separator.tsx +21 -0
  235. package/carbon/src/client/components/ui/sheet.tsx +124 -0
  236. package/carbon/src/client/components/ui/sidebar.tsx +702 -0
  237. package/carbon/src/client/components/ui/skeleton.tsx +13 -0
  238. package/carbon/src/client/components/ui/slider.tsx +57 -0
  239. package/carbon/src/client/components/ui/sonner.tsx +45 -0
  240. package/carbon/src/client/components/ui/spinner.tsx +15 -0
  241. package/carbon/src/client/components/ui/switch.tsx +30 -0
  242. package/carbon/src/client/components/ui/table.tsx +89 -0
  243. package/carbon/src/client/components/ui/tabs.tsx +73 -0
  244. package/carbon/src/client/components/ui/textarea.tsx +18 -0
  245. package/carbon/src/client/components/ui/toggle-group.tsx +87 -0
  246. package/carbon/src/client/components/ui/toggle.tsx +44 -0
  247. package/carbon/src/client/components/ui/tooltip.tsx +56 -0
  248. package/carbon/src/client/hooks/api/index.ts +14 -0
  249. package/carbon/src/client/hooks/api/useAuthSettings.ts +80 -0
  250. package/carbon/src/client/hooks/api/useSubscription.ts +87 -0
  251. package/carbon/src/client/hooks/use-mobile.ts +21 -0
  252. package/carbon/src/client/hooks/useMousePosition.ts +91 -0
  253. package/carbon/src/client/hooks/useNotifications.tsx +124 -0
  254. package/carbon/src/client/hooks/useOrganizationMembers.ts +127 -0
  255. package/carbon/src/client/hooks/useOrganizations.tsx +230 -0
  256. package/carbon/src/client/hooks/usePackageManager.ts +69 -0
  257. package/carbon/src/client/hooks/useReducedMotion.ts +20 -0
  258. package/carbon/src/client/hooks/useRunningServices.ts +114 -0
  259. package/carbon/src/client/hooks/useScrollProgress.ts +56 -0
  260. package/carbon/src/client/index.css +467 -0
  261. package/carbon/src/client/index.html +56 -0
  262. package/carbon/src/client/lib/admin-services.ts +151 -0
  263. package/carbon/src/client/lib/api.ts +32 -0
  264. package/carbon/src/client/lib/blog.ts +35 -0
  265. package/carbon/src/client/lib/changelog.ts +33 -0
  266. package/carbon/src/client/lib/docs-search.ts +101 -0
  267. package/carbon/src/client/lib/docs.ts +37 -0
  268. package/carbon/src/client/lib/i18n.ts +32 -0
  269. package/carbon/src/client/lib/supabase.ts +72 -0
  270. package/carbon/src/client/lib/tailwind-colors.ts +357 -0
  271. package/carbon/src/client/lib/theme.ts +117 -0
  272. package/carbon/src/client/lib/utils.ts +22 -0
  273. package/carbon/src/client/locales/de.json +529 -0
  274. package/carbon/src/client/locales/en.json +461 -0
  275. package/carbon/src/client/locales/es.json +529 -0
  276. package/carbon/src/client/locales/fr.json +529 -0
  277. package/carbon/src/client/locales/pt.json +529 -0
  278. package/carbon/src/client/main.tsx +56 -0
  279. package/carbon/src/client/mdx.d.ts +13 -0
  280. package/carbon/src/client/pages/ApiDocs.tsx +76 -0
  281. package/carbon/src/client/pages/AuthCallback.tsx +34 -0
  282. package/carbon/src/client/pages/Blog.tsx +167 -0
  283. package/carbon/src/client/pages/Changelog.tsx +171 -0
  284. package/carbon/src/client/pages/Charts.tsx +388 -0
  285. package/carbon/src/client/pages/Checkout.tsx +227 -0
  286. package/carbon/src/client/pages/Contact.tsx +174 -0
  287. package/carbon/src/client/pages/Dashboard.tsx +368 -0
  288. package/carbon/src/client/pages/Docs.tsx +372 -0
  289. package/carbon/src/client/pages/ForgotPassword.tsx +111 -0
  290. package/carbon/src/client/pages/Home.tsx +187 -0
  291. package/carbon/src/client/pages/Legal.tsx +100 -0
  292. package/carbon/src/client/pages/Login.tsx +408 -0
  293. package/carbon/src/client/pages/MFAVerify.tsx +156 -0
  294. package/carbon/src/client/pages/NotFound.tsx +21 -0
  295. package/carbon/src/client/pages/Onboarding.tsx +246 -0
  296. package/carbon/src/client/pages/ResetPassword.tsx +200 -0
  297. package/carbon/src/client/pages/UIComponents.tsx +390 -0
  298. package/carbon/src/client/pages/admin/ContactSubmissions.tsx +220 -0
  299. package/carbon/src/client/pages/admin/Dashboard.tsx +24 -0
  300. package/carbon/src/client/pages/admin/Infrastructure.tsx +257 -0
  301. package/carbon/src/client/pages/admin/Jobs.tsx +225 -0
  302. package/carbon/src/client/pages/admin/Logs.tsx +18 -0
  303. package/carbon/src/client/pages/admin/Newsletter.tsx +300 -0
  304. package/carbon/src/client/pages/admin/Notifications.tsx +603 -0
  305. package/carbon/src/client/pages/admin/Organizations.tsx +306 -0
  306. package/carbon/src/client/pages/admin/Settings.tsx +314 -0
  307. package/carbon/src/client/pages/admin/Theme.tsx +465 -0
  308. package/carbon/src/client/pages/admin/Users.tsx +365 -0
  309. package/carbon/src/client/pages/api-docs.css +156 -0
  310. package/carbon/src/client/pages/organizations/Details.tsx +200 -0
  311. package/carbon/src/client/pages/organizations/Members.tsx +402 -0
  312. package/carbon/src/client/pages/settings/Billing.tsx +473 -0
  313. package/carbon/src/client/pages/settings/Profile.tsx +160 -0
  314. package/carbon/src/client/pages/settings/Security.tsx +341 -0
  315. package/carbon/src/client/public/favicon.svg +19 -0
  316. package/carbon/src/client/public/robots.txt +8 -0
  317. package/carbon/src/server/billing/index.ts +104 -0
  318. package/carbon/src/server/billing/provider.ts +81 -0
  319. package/carbon/src/server/billing/providers/paddle.ts +314 -0
  320. package/carbon/src/server/billing/providers/polar.ts +325 -0
  321. package/carbon/src/server/billing/providers/stripe.ts +233 -0
  322. package/carbon/src/server/emails/templates.ts +116 -0
  323. package/carbon/src/server/index.ts +554 -0
  324. package/carbon/src/server/lib/auth.ts +6 -0
  325. package/carbon/src/server/lib/email.ts +64 -0
  326. package/carbon/src/server/lib/env.ts +112 -0
  327. package/carbon/src/server/lib/errors.ts +21 -0
  328. package/carbon/src/server/lib/logger.ts +17 -0
  329. package/carbon/src/server/lib/rate-limiter.ts +288 -0
  330. package/carbon/src/server/lib/request.ts +34 -0
  331. package/carbon/src/server/lib/stripe.ts +42 -0
  332. package/carbon/src/server/lib/supabase.ts +65 -0
  333. package/carbon/src/server/middleware/requirePlan.ts +80 -0
  334. package/carbon/src/server/routes/_internal/services-status.ts +958 -0
  335. package/carbon/src/server/routes/_internal/verify-role.ts +185 -0
  336. package/carbon/src/server/routes/health.ts +48 -0
  337. package/carbon/src/server/routes/v1/admin/contact.ts +128 -0
  338. package/carbon/src/server/routes/v1/admin/jobs.ts +171 -0
  339. package/carbon/src/server/routes/v1/admin/newsletter.ts +237 -0
  340. package/carbon/src/server/routes/v1/auth.ts +390 -0
  341. package/carbon/src/server/routes/v1/billing.ts +718 -0
  342. package/carbon/src/server/routes/v1/contact.ts +93 -0
  343. package/carbon/src/server/routes/v1/index.ts +1333 -0
  344. package/carbon/src/server/routes/v1/newsletter.ts +181 -0
  345. package/carbon/src/server/routes/v1/performance.ts +157 -0
  346. package/carbon/src/server/routes/v1/stats.ts +170 -0
  347. package/carbon/src/server/routes/v1/theme.ts +106 -0
  348. package/carbon/src/server/routes/webhooks/billing.ts +376 -0
  349. package/carbon/src/server/routes/webhooks/stripe.ts +276 -0
  350. package/carbon/src/server/types.ts +11 -0
  351. package/carbon/src/shared/pricing.ts +155 -0
  352. package/carbon/src/shared/types.ts +338 -0
  353. package/carbon/supabase/migrations/00001_init.sql +717 -0
  354. package/carbon/supabase/migrations/00002_theme_settings.sql +13 -0
  355. package/carbon/supabase/migrations/00003_pg_cron.sql +121 -0
  356. package/carbon/supabase/migrations/00004_contact_newsletter.sql +81 -0
  357. package/carbon/supabase/migrations/00005_localization_languages.sql +22 -0
  358. package/carbon/supabase/seed.sql +16 -0
  359. package/carbon/tests/_helpers/app.ts +45 -0
  360. package/carbon/tests/_helpers/env.ts +37 -0
  361. package/carbon/tests/_helpers/factories.ts +69 -0
  362. package/carbon/tests/_helpers/jwt.ts +23 -0
  363. package/carbon/tests/_helpers/setup-integration.ts +20 -0
  364. package/carbon/tests/_helpers/setup-rtl.ts +12 -0
  365. package/carbon/tests/component/ErrorBoundary.test.tsx +53 -0
  366. package/carbon/tests/component/use-auth-settings.test.tsx +119 -0
  367. package/carbon/tests/integration/server/routes/contact.test.ts +162 -0
  368. package/carbon/tests/integration/server/routes/health.test.ts +61 -0
  369. package/carbon/tests/structural/i18n-parity.test.ts +42 -0
  370. package/carbon/tests/unit/client/utils.test.ts +49 -0
  371. package/carbon/tests/unit/shared/pricing.test.ts +93 -0
  372. package/carbon/tsconfig.json +27 -0
  373. package/carbon/tsconfig.server.json +27 -0
  374. package/carbon/tsconfig.test.json +9 -0
  375. package/carbon/vite.config.ts +110 -0
  376. package/carbon/vitest.config.ts +74 -0
  377. package/carbon/volumes/db/jwt.sql +57 -0
  378. package/carbon/volumes/db/metabase-init.sh +29 -0
  379. package/carbon/volumes/db/n8n-init.sh +25 -0
  380. package/carbon/volumes/db/realtime.sql +33 -0
  381. package/carbon/volumes/db/roles.sql +93 -0
  382. package/carbon/volumes/db/set-passwords.sh +12 -0
  383. package/carbon/volumes/db/super-admin.dev.sql +113 -0
  384. package/carbon/volumes/db/super-admin.generated.sql +113 -0
  385. package/carbon/volumes/db/super-admin.sql +114 -0
  386. package/carbon/volumes/grafana/dashboards/logs.json +179 -0
  387. package/carbon/volumes/grafana/dashboards/overview.json +523 -0
  388. package/carbon/volumes/grafana/dashboards/postgresql.json +337 -0
  389. package/carbon/volumes/grafana/dashboards.dev/logs.json +179 -0
  390. package/carbon/volumes/grafana/dashboards.dev/overview.json +156 -0
  391. package/carbon/volumes/grafana/dashboards.dev/postgresql.json +337 -0
  392. package/carbon/volumes/grafana/provisioning/dashboards/dashboards.dev.yml +16 -0
  393. package/carbon/volumes/grafana/provisioning/dashboards/dashboards.yml +16 -0
  394. package/carbon/volumes/grafana/provisioning/datasources/datasources.yml +43 -0
  395. package/carbon/volumes/kong/docker-entrypoint.sh +9 -0
  396. package/carbon/volumes/kong/kong.yml +208 -0
  397. package/carbon/volumes/loki/loki-config.yml +58 -0
  398. package/carbon/volumes/n8n/hooks.js +131 -0
  399. package/carbon/volumes/n8n/scripts/setup.sh +66 -0
  400. package/carbon/volumes/prometheus/prometheus.yml +43 -0
  401. package/carbon/volumes/promtail/promtail-config.yml +64 -0
  402. package/carbon/volumes/traefik/middlewares.dev.yml +83 -0
  403. package/carbon/volumes/traefik/middlewares.yml +95 -0
  404. package/carbon/volumes/traefik/vite-dev.yml +20 -0
  405. package/package.json +95 -0
  406. package/src/access.js +354 -0
  407. package/src/activate.js +187 -0
  408. package/src/add.js +718 -0
  409. package/src/backup.js +786 -0
  410. package/src/cli.js +350 -0
  411. package/src/configure.js +967 -0
  412. package/src/console.js +155 -0
  413. package/src/create.js +1499 -0
  414. package/src/deploy.js +311 -0
  415. package/src/destroy.js +2033 -0
  416. package/src/diagnose.js +735 -0
  417. package/src/down.js +80 -0
  418. package/src/failover.js +1032 -0
  419. package/src/lib/backup-s3.js +179 -0
  420. package/src/lib/build.js +33 -0
  421. package/src/lib/checksum.js +28 -0
  422. package/src/lib/ci-setup.js +666 -0
  423. package/src/lib/cli/help.js +129 -0
  424. package/src/lib/cli/parse-flags.js +160 -0
  425. package/src/lib/cli/select-action.js +65 -0
  426. package/src/lib/cli/select-environment.js +108 -0
  427. package/src/lib/cli/tty-guard.js +75 -0
  428. package/src/lib/cloudflare.js +447 -0
  429. package/src/lib/colors.js +52 -0
  430. package/src/lib/command.js +361 -0
  431. package/src/lib/config.js +359 -0
  432. package/src/lib/cost.js +103 -0
  433. package/src/lib/deploy/bundle.js +231 -0
  434. package/src/lib/deploy/compose/acme-verify.js +121 -0
  435. package/src/lib/deploy/compose/build-args.js +78 -0
  436. package/src/lib/deploy/compose/ha.js +1874 -0
  437. package/src/lib/deploy/compose/index.js +1294 -0
  438. package/src/lib/deploy/compose/reconcile.js +74 -0
  439. package/src/lib/deploy/github.js +382 -0
  440. package/src/lib/deploy/image.js +191 -0
  441. package/src/lib/deploy/index.js +119 -0
  442. package/src/lib/deploy/k8s/LICENSE +99 -0
  443. package/src/lib/deploy/k8s/gitops-deploy.js +298 -0
  444. package/src/lib/deploy/k8s/ha/index.js +1000 -0
  445. package/src/lib/deploy/k8s/index.js +62 -0
  446. package/src/lib/deploy/k8s/k3s.js +2515 -0
  447. package/src/lib/deploy/orchestrator.js +1401 -0
  448. package/src/lib/deploy/prompts.js +545 -0
  449. package/src/lib/deploy/remote-build.js +130 -0
  450. package/src/lib/deploy/state.js +120 -0
  451. package/src/lib/deploy/utils.js +328 -0
  452. package/src/lib/deploy-logger.js +93 -0
  453. package/src/lib/dns-propagation.js +48 -0
  454. package/src/lib/environment.js +58 -0
  455. package/src/lib/fetch-retry.js +103 -0
  456. package/src/lib/github-environments.js +335 -0
  457. package/src/lib/hetzner-dns.js +377 -0
  458. package/src/lib/hetzner-guided-setup.js +275 -0
  459. package/src/lib/host-keys.js +37 -0
  460. package/src/lib/iac/cloud-init.js +52 -0
  461. package/src/lib/iac/index.js +325 -0
  462. package/src/lib/iac/programs/hetzner-compose.js +130 -0
  463. package/src/lib/iac/programs/hetzner-k8s.js +320 -0
  464. package/src/lib/kubectl.js +81 -0
  465. package/src/lib/licensing/index.js +259 -0
  466. package/src/lib/licensing/tiers.js +181 -0
  467. package/src/lib/licensing/validator.js +171 -0
  468. package/src/lib/merge-package-json.js +90 -0
  469. package/src/lib/operator-ip.js +381 -0
  470. package/src/lib/package-manager.js +111 -0
  471. package/src/lib/perf.js +71 -0
  472. package/src/lib/project-guard.js +39 -0
  473. package/src/lib/project.js +334 -0
  474. package/src/lib/providers/base.js +276 -0
  475. package/src/lib/providers/hetzner-s3.js +656 -0
  476. package/src/lib/providers/hetzner.js +755 -0
  477. package/src/lib/providers/index.js +164 -0
  478. package/src/lib/s3.js +510 -0
  479. package/src/lib/secret-scan.js +583 -0
  480. package/src/lib/secrets.js +63 -0
  481. package/src/lib/server-types.js +195 -0
  482. package/src/lib/shell.js +91 -0
  483. package/src/lib/ssh.js +241 -0
  484. package/src/lib/tracker.js +242 -0
  485. package/src/lib/upgrade-policy.js +170 -0
  486. package/src/lib/validators.js +105 -0
  487. package/src/lib/version.js +5 -0
  488. package/src/remove.js +292 -0
  489. package/src/reset.js +97 -0
  490. package/src/restore.js +871 -0
  491. package/src/scale.js +1734 -0
  492. package/src/shell.js +222 -0
  493. package/src/status.js +981 -0
  494. package/src/up.js +264 -0
  495. package/src/upgrade.js +721 -0
@@ -0,0 +1,608 @@
1
+ import { type ClassValue, clsx } from 'clsx';
2
+ import { AnimatePresence, motion } from 'framer-motion';
3
+ import { MoreHorizontal, Plus, Settings, Users } from 'lucide-react';
4
+ import { useCallback, useEffect, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { twMerge } from 'tailwind-merge';
7
+ import { SparkBurst } from './effects/SparkBurst';
8
+ import { Nav } from './Nav';
9
+ import { NewsletterSignup } from './NewsletterSignup';
10
+
11
+ function cn(...inputs: ClassValue[]) {
12
+ return twMerge(clsx(inputs));
13
+ }
14
+
15
+ // Animation phases for the CLI
16
+ type Phase =
17
+ | 'typing'
18
+ | 'intro'
19
+ | 'projectName'
20
+ | 'packageManager'
21
+ | 'git'
22
+ | 'config'
23
+ | 'spinner'
24
+ | 'success'
25
+ | 'nextSteps'
26
+ | 'outro';
27
+
28
+ // Clack-style CLI animation
29
+ const TerminalWindow = ({ onComplete }: { onComplete: () => void }) => {
30
+ const [typed, setTyped] = useState('');
31
+ const [phase, setPhase] = useState<Phase>('typing');
32
+ const [spinnerStep, setSpinnerStep] = useState(0);
33
+ const [spinnerChar, setSpinnerChar] = useState(0);
34
+ const [selectedPm, setSelectedPm] = useState(0);
35
+
36
+ const command = 'npx vibecarbon create';
37
+ const spinnerChars = ['◒', '◐', '◓', '◑'];
38
+ const spinnerSteps = [
39
+ 'Creating project structure',
40
+ 'Generating configuration files',
41
+ 'Generating Docker configuration',
42
+ 'Generating backend files',
43
+ 'Installing dependencies with pnpm',
44
+ 'Initializing git repository',
45
+ ];
46
+ const pmOptions = ['pnpm', 'npm', 'bun'];
47
+
48
+ // Typing animation
49
+ useEffect(() => {
50
+ if (phase !== 'typing') return;
51
+ if (typed.length < command.length) {
52
+ const timeout = setTimeout(
53
+ () => {
54
+ setTyped(command.slice(0, typed.length + 1));
55
+ },
56
+ 30 + Math.random() * 20
57
+ );
58
+ return () => clearTimeout(timeout);
59
+ }
60
+ const timeout = setTimeout(() => setPhase('intro'), 400);
61
+ return () => clearTimeout(timeout);
62
+ }, [typed, phase]);
63
+
64
+ // Phase progression
65
+ useEffect(() => {
66
+ if (phase === 'intro') {
67
+ const timeout = setTimeout(() => setPhase('projectName'), 600);
68
+ return () => clearTimeout(timeout);
69
+ }
70
+ if (phase === 'projectName') {
71
+ const timeout = setTimeout(() => setPhase('packageManager'), 800);
72
+ return () => clearTimeout(timeout);
73
+ }
74
+ if (phase === 'packageManager') {
75
+ // Animate through package manager selection
76
+ const selectTimeout = setTimeout(() => {
77
+ setSelectedPm(0);
78
+ setTimeout(() => setPhase('git'), 400);
79
+ }, 600);
80
+ return () => clearTimeout(selectTimeout);
81
+ }
82
+ if (phase === 'git') {
83
+ const timeout = setTimeout(() => setPhase('config'), 600);
84
+ return () => clearTimeout(timeout);
85
+ }
86
+ if (phase === 'config') {
87
+ const timeout = setTimeout(() => setPhase('spinner'), 800);
88
+ return () => clearTimeout(timeout);
89
+ }
90
+ if (phase === 'spinner') {
91
+ if (spinnerStep < spinnerSteps.length) {
92
+ const timeout = setTimeout(() => {
93
+ setSpinnerStep((s) => s + 1);
94
+ }, 350);
95
+ return () => clearTimeout(timeout);
96
+ }
97
+ const timeout = setTimeout(() => setPhase('success'), 300);
98
+ return () => clearTimeout(timeout);
99
+ }
100
+ if (phase === 'success') {
101
+ const timeout = setTimeout(() => setPhase('nextSteps'), 400);
102
+ return () => clearTimeout(timeout);
103
+ }
104
+ if (phase === 'nextSteps') {
105
+ const timeout = setTimeout(() => setPhase('outro'), 600);
106
+ return () => clearTimeout(timeout);
107
+ }
108
+ if (phase === 'outro') {
109
+ const timeout = setTimeout(onComplete, 1200);
110
+ return () => clearTimeout(timeout);
111
+ }
112
+ }, [phase, spinnerStep, onComplete, spinnerSteps.length]);
113
+
114
+ // Spinner character animation
115
+ useEffect(() => {
116
+ if (phase !== 'spinner' || spinnerStep >= spinnerSteps.length) return;
117
+ const interval = setInterval(() => {
118
+ setSpinnerChar((c) => (c + 1) % 4);
119
+ }, 80);
120
+ return () => clearInterval(interval);
121
+ }, [phase, spinnerStep, spinnerSteps.length]);
122
+
123
+ const getPhaseIndex = (p: Phase) => {
124
+ const phases: Phase[] = [
125
+ 'typing',
126
+ 'intro',
127
+ 'projectName',
128
+ 'packageManager',
129
+ 'git',
130
+ 'config',
131
+ 'spinner',
132
+ 'success',
133
+ 'nextSteps',
134
+ 'outro',
135
+ ];
136
+ return phases.indexOf(p);
137
+ };
138
+
139
+ const isPastPhase = (p: Phase) => getPhaseIndex(phase) > getPhaseIndex(p);
140
+ const isAtOrPastPhase = (p: Phase) => getPhaseIndex(phase) >= getPhaseIndex(p);
141
+
142
+ return (
143
+ <div className="w-full h-full font-mono text-[13px] leading-relaxed p-4 flex flex-col overflow-hidden">
144
+ {/* Terminal Header */}
145
+ <div className="flex items-center justify-between mb-3 opacity-60">
146
+ <div className="flex space-x-2">
147
+ <div className="w-3 h-3 rounded-full bg-red-500/70" />
148
+ <div className="w-3 h-3 rounded-full bg-yellow-500/70" />
149
+ <div className="w-3 h-3 rounded-full bg-green-500/70" />
150
+ </div>
151
+ <div className="text-xs text-muted-foreground">Terminal</div>
152
+ </div>
153
+
154
+ {/* CLI Content */}
155
+ <div className="flex-1 overflow-hidden">
156
+ {/* Command line */}
157
+ <div className="flex items-center text-foreground">
158
+ <span className="text-primary mr-2">$</span>
159
+ <span>{typed}</span>
160
+ {phase === 'typing' && typed.length < command.length && (
161
+ <span className="w-2 h-4 bg-primary ml-0.5 animate-pulse" />
162
+ )}
163
+ </div>
164
+
165
+ {/* Intro */}
166
+ {isAtOrPastPhase('intro') && (
167
+ <motion.div
168
+ initial={{ opacity: 0 }}
169
+ animate={{ opacity: 1 }}
170
+ className="mt-2 text-muted-foreground"
171
+ >
172
+ <span className="text-secondary-accent">◆</span> npx vibecarbon create
173
+ </motion.div>
174
+ )}
175
+
176
+ {/* Project name prompt */}
177
+ {isAtOrPastPhase('projectName') && (
178
+ <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="mt-1">
179
+ <span className="text-muted-foreground/50">│</span>
180
+ <div className="ml-2 inline">
181
+ {isPastPhase('projectName') ? (
182
+ <>
183
+ <span className="text-primary">◇</span>{' '}
184
+ <span className="text-muted-foreground">What is your project name?</span>{' '}
185
+ <span className="text-foreground">my-app</span>
186
+ </>
187
+ ) : (
188
+ <>
189
+ <span className="text-primary">◆</span>{' '}
190
+ <span className="text-foreground">What is your project name?</span>
191
+ <span className="ml-2 text-muted-foreground">my-app</span>
192
+ <span className="w-2 h-4 bg-primary ml-0.5 inline-block animate-pulse align-middle" />
193
+ </>
194
+ )}
195
+ </div>
196
+ </motion.div>
197
+ )}
198
+
199
+ {/* Package manager select */}
200
+ {isAtOrPastPhase('packageManager') && (
201
+ <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="mt-1">
202
+ <span className="text-muted-foreground/50">│</span>
203
+ <div className="ml-2 inline">
204
+ {isPastPhase('packageManager') ? (
205
+ <>
206
+ <span className="text-primary">◇</span>{' '}
207
+ <span className="text-muted-foreground">Which package manager?</span>{' '}
208
+ <span className="text-foreground">pnpm</span>
209
+ </>
210
+ ) : (
211
+ <>
212
+ <span className="text-primary">◆</span>{' '}
213
+ <span className="text-foreground">Which package manager?</span>
214
+ <div className="ml-4 mt-1 space-y-0.5">
215
+ {pmOptions.map((pm, i) => (
216
+ <div key={pm} className="flex items-center gap-2">
217
+ <span
218
+ className={i === selectedPm ? 'text-primary' : 'text-muted-foreground/50'}
219
+ >
220
+ {i === selectedPm ? '●' : '○'}
221
+ </span>
222
+ <span
223
+ className={i === selectedPm ? 'text-foreground' : 'text-muted-foreground'}
224
+ >
225
+ {pm}
226
+ </span>
227
+ {i === 0 && (
228
+ <span className="text-muted-foreground/50 text-xs">(recommended)</span>
229
+ )}
230
+ </div>
231
+ ))}
232
+ </div>
233
+ </>
234
+ )}
235
+ </div>
236
+ </motion.div>
237
+ )}
238
+
239
+ {/* Git confirm */}
240
+ {isAtOrPastPhase('git') && (
241
+ <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="mt-1">
242
+ <span className="text-muted-foreground/50">│</span>
243
+ <div className="ml-2 inline">
244
+ <span className="text-primary">◇</span>{' '}
245
+ <span className="text-muted-foreground">Initialize a git repository?</span>{' '}
246
+ <span className="text-foreground">Yes</span>
247
+ </div>
248
+ </motion.div>
249
+ )}
250
+
251
+ {/* Configuration box */}
252
+ {isAtOrPastPhase('config') && (
253
+ <motion.div
254
+ initial={{ opacity: 0 }}
255
+ animate={{ opacity: 1 }}
256
+ className="mt-2 border border-border/40 rounded px-3 py-2 bg-muted/50 dark:bg-white/[0.02] text-xs"
257
+ >
258
+ <div className="text-muted-foreground mb-1.5">Configuration</div>
259
+ <div className="space-y-0.5 text-foreground">
260
+ <div>
261
+ Project: <span className="text-muted-foreground">my-app</span>
262
+ </div>
263
+ <div>
264
+ Package manager: <span className="text-muted-foreground">pnpm</span>
265
+ </div>
266
+ <div>
267
+ Git: <span className="text-muted-foreground">yes</span>
268
+ </div>
269
+ </div>
270
+ </motion.div>
271
+ )}
272
+
273
+ {/* Spinner steps */}
274
+ {isAtOrPastPhase('spinner') && (
275
+ <div className="mt-2 space-y-0.5">
276
+ {spinnerSteps.map((step, i) => {
277
+ if (i > spinnerStep) return null;
278
+ const isComplete = i < spinnerStep;
279
+ const isCurrent = i === spinnerStep && phase === 'spinner';
280
+ return (
281
+ <motion.div
282
+ key={step}
283
+ initial={{ opacity: 0 }}
284
+ animate={{ opacity: 1 }}
285
+ className="flex items-center gap-2"
286
+ >
287
+ {isComplete ? (
288
+ <span className="text-primary">✓</span>
289
+ ) : isCurrent ? (
290
+ <span className="text-primary">{spinnerChars[spinnerChar]}</span>
291
+ ) : (
292
+ <span className="text-primary">✓</span>
293
+ )}
294
+ <span className={isComplete ? 'text-muted-foreground' : 'text-foreground'}>
295
+ {step}
296
+ </span>
297
+ </motion.div>
298
+ );
299
+ })}
300
+ </div>
301
+ )}
302
+
303
+ {/* Success */}
304
+ {isAtOrPastPhase('success') && (
305
+ <motion.div
306
+ initial={{ opacity: 0 }}
307
+ animate={{ opacity: 1 }}
308
+ className="mt-2 text-primary"
309
+ >
310
+ <span>◇</span> Project created successfully
311
+ </motion.div>
312
+ )}
313
+
314
+ {/* Next steps */}
315
+ {isAtOrPastPhase('nextSteps') && (
316
+ <motion.div
317
+ initial={{ opacity: 0 }}
318
+ animate={{ opacity: 1 }}
319
+ className="mt-2 border border-border/40 rounded px-3 py-2 bg-muted/50 dark:bg-white/[0.02] text-xs"
320
+ >
321
+ <div className="text-muted-foreground mb-1">Next steps</div>
322
+ <div className="text-foreground space-y-0.5">
323
+ <div>cd my-app</div>
324
+ <div>pnpm dev:start</div>
325
+ </div>
326
+ </motion.div>
327
+ )}
328
+
329
+ {/* Outro */}
330
+ {isAtOrPastPhase('outro') && (
331
+ <motion.div
332
+ initial={{ opacity: 0 }}
333
+ animate={{ opacity: 1 }}
334
+ className="mt-2 text-muted-foreground"
335
+ >
336
+ <span className="text-secondary-accent">◇</span> Happy building!
337
+ </motion.div>
338
+ )}
339
+ </div>
340
+ </div>
341
+ );
342
+ };
343
+
344
+ // SaaS Dashboard Preview
345
+ const DashboardPreview = () => {
346
+ const projects = [
347
+ { name: 'my-app', env: 'production', status: 'healthy', users: '1.2k' },
348
+ { name: 'staging', env: 'staging', status: 'healthy', users: '45' },
349
+ ];
350
+
351
+ return (
352
+ <div className="w-full h-full flex text-xs">
353
+ {/* Sidebar */}
354
+ <div className="w-14 border-r border-border/50 flex flex-col items-center py-3 gap-3 bg-black/5 dark:bg-black/20">
355
+ <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-primary to-primary/60 flex items-center justify-center text-background font-bold text-sm">
356
+ C
357
+ </div>
358
+ <div className="flex-1 flex flex-col gap-2 mt-2">
359
+ <div className="w-8 h-8 rounded-lg bg-primary/20 flex items-center justify-center text-primary">
360
+ <svg
361
+ className="w-4 h-4"
362
+ fill="none"
363
+ viewBox="0 0 24 24"
364
+ stroke="currentColor"
365
+ strokeWidth={2}
366
+ aria-hidden="true"
367
+ >
368
+ <path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h7" />
369
+ </svg>
370
+ </div>
371
+ <div className="w-8 h-8 rounded-lg hover:bg-black/5 dark:hover:bg-white/5 flex items-center justify-center text-muted-foreground">
372
+ <Users className="w-4 h-4" />
373
+ </div>
374
+ <div className="w-8 h-8 rounded-lg hover:bg-black/5 dark:hover:bg-white/5 flex items-center justify-center text-muted-foreground">
375
+ <Settings className="w-4 h-4" />
376
+ </div>
377
+ </div>
378
+ <div className="w-7 h-7 rounded-full bg-secondary-accent/20 flex items-center justify-center text-secondary-accent text-[10px] font-medium">
379
+ JD
380
+ </div>
381
+ </div>
382
+
383
+ {/* Main content */}
384
+ <div className="flex-1 p-4 overflow-hidden">
385
+ {/* Header */}
386
+ <div className="flex items-center justify-between mb-4">
387
+ <div>
388
+ <h2 className="text-foreground font-semibold text-sm">Projects</h2>
389
+ <p className="text-muted-foreground text-[10px]">Manage your deployments</p>
390
+ </div>
391
+ <button
392
+ type="button"
393
+ className="flex items-center gap-1 px-2.5 py-1.5 rounded-full bg-primary text-white text-[10px] font-medium"
394
+ >
395
+ <Plus className="w-3 h-3" />
396
+ New Project
397
+ </button>
398
+ </div>
399
+
400
+ {/* Projects list */}
401
+ <div className="space-y-2">
402
+ {projects.map((project) => (
403
+ <div
404
+ key={project.name}
405
+ className="flex items-center justify-between p-3 rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02] hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors"
406
+ >
407
+ <div className="flex items-center gap-3">
408
+ <div className="w-8 h-8 rounded-md bg-gradient-to-br from-primary/20 to-secondary-accent/20 flex items-center justify-center text-foreground font-medium text-[10px]">
409
+ {project.name[0].toUpperCase()}
410
+ </div>
411
+ <div>
412
+ <div className="text-foreground font-medium text-xs">{project.name}</div>
413
+ <div className="text-muted-foreground text-[10px]">{project.env}</div>
414
+ </div>
415
+ </div>
416
+ <div className="flex items-center gap-4">
417
+ <div className="text-right">
418
+ <div className="flex items-center gap-1">
419
+ <div className="w-1.5 h-1.5 rounded-full bg-green-500" />
420
+ <span className="text-muted-foreground text-[10px]">{project.status}</span>
421
+ </div>
422
+ <div className="text-muted-foreground text-[10px]">{project.users} users</div>
423
+ </div>
424
+ <button
425
+ type="button"
426
+ className="p-1 hover:bg-black/10 dark:hover:bg-white/10 rounded"
427
+ >
428
+ <MoreHorizontal className="w-4 h-4 text-muted-foreground" />
429
+ </button>
430
+ </div>
431
+ </div>
432
+ ))}
433
+ </div>
434
+
435
+ {/* Stats row */}
436
+ <div className="grid grid-cols-3 gap-2 mt-4">
437
+ {[
438
+ { label: 'Total Users', value: '1,247', change: '+12%' },
439
+ { label: 'API Calls', value: '847K', change: '+8%' },
440
+ { label: 'Avg Latency', value: '24ms', change: '-5%' },
441
+ ].map((stat) => (
442
+ <div
443
+ key={stat.label}
444
+ className="p-2.5 rounded-lg border border-border/50 bg-black/[0.02] dark:bg-white/[0.02]"
445
+ >
446
+ <div className="text-muted-foreground text-[10px]">{stat.label}</div>
447
+ <div className="text-foreground font-semibold text-sm">{stat.value}</div>
448
+ <div
449
+ className={`text-[10px] ${stat.change.startsWith('+') ? 'text-green-400' : 'text-primary'}`}
450
+ >
451
+ {stat.change}
452
+ </div>
453
+ </div>
454
+ ))}
455
+ </div>
456
+ </div>
457
+ </div>
458
+ );
459
+ };
460
+
461
+ export default function Hero() {
462
+ const { t } = useTranslation();
463
+ const [terminalComplete, setTerminalComplete] = useState(false);
464
+ const [animationKey, setAnimationKey] = useState(0);
465
+ const [pillSparkTrigger, setPillSparkTrigger] = useState(0);
466
+
467
+ // Re-fire pill sparks every 10 seconds
468
+ useEffect(() => {
469
+ const interval = setInterval(() => setPillSparkTrigger((t) => t + 1), 10000);
470
+ return () => clearInterval(interval);
471
+ }, []);
472
+
473
+ const handleTerminalComplete = useCallback(() => {
474
+ setTerminalComplete(true);
475
+ }, []);
476
+
477
+ useEffect(() => {
478
+ if (terminalComplete) {
479
+ const timeout = setTimeout(() => {
480
+ setTerminalComplete(false);
481
+ setAnimationKey((k) => k + 1);
482
+ }, 6000);
483
+ return () => clearTimeout(timeout);
484
+ }
485
+ }, [terminalComplete]);
486
+
487
+ return (
488
+ <div className="relative min-h-screen w-full bg-background text-foreground overflow-x-hidden selection:bg-primary/30">
489
+ <Nav />
490
+
491
+ <main className="relative z-10 flex flex-col items-center justify-center min-h-screen px-4 pt-32 pb-32">
492
+ <div className="text-center max-w-4xl mx-auto mb-6 relative">
493
+ <motion.div
494
+ initial={{ opacity: 0 }}
495
+ animate={{ opacity: 1 }}
496
+ transition={{ duration: 0.8, delay: 0.3 }}
497
+ className="flex items-center justify-center gap-3 select-none cursor-default mb-6"
498
+ >
499
+ {/* biome-ignore lint/a11y/noStaticElementInteractions: decorative hover spark effect */}
500
+ <span
501
+ className="relative px-4 py-1.5 rounded-full text-sm font-medium overflow-visible group"
502
+ onMouseEnter={() => setPillSparkTrigger((t) => t + 1)}
503
+ >
504
+ {/* Animated gradient border */}
505
+ <span className="absolute inset-0 rounded-full bg-gradient-to-r from-violet-500 via-fuchsia-400 via-amber-300 to-violet-500 badge-shimmer" />
506
+ {/* Inner fill */}
507
+ <span className="absolute inset-[1.5px] rounded-full bg-gradient-to-r from-violet-950/90 via-fuchsia-950/80 to-violet-950/90 dark:from-violet-950/90 dark:via-fuchsia-950/80 dark:to-violet-950/90" />
508
+ {/* Shimmer highlight overlay */}
509
+ <span className="absolute inset-[1.5px] rounded-full bg-gradient-to-r from-transparent via-white/10 to-transparent badge-shimmer opacity-60" />
510
+ {/* Spark burst: fires on mount, every 10s, and on hover */}
511
+ <SparkBurst delay={1.1} palette="fuchsia" trigger={pillSparkTrigger} />
512
+ {/* Text content */}
513
+ <span className="relative z-10 bg-gradient-to-r from-violet-400 via-fuchsia-300 to-amber-300 dark:from-violet-300 dark:via-fuchsia-200 dark:to-amber-200 bg-clip-text text-transparent">
514
+ {t('landing.hero.badge')}
515
+ </span>
516
+ </span>
517
+ </motion.div>
518
+
519
+ <motion.h1
520
+ initial={{ opacity: 0, y: 20 }}
521
+ animate={{ opacity: 1, y: 0 }}
522
+ transition={{ duration: 0.8, delay: 0.1 }}
523
+ className="text-5xl md:text-7xl lg:text-8xl font-black tracking-tight mb-6"
524
+ >
525
+ <span className="block text-foreground">{t('landing.hero.headline1')}</span>
526
+ <span className="block text-primary pb-2">Vibecoding</span>
527
+ </motion.h1>
528
+
529
+ <motion.p
530
+ initial={{ opacity: 0 }}
531
+ animate={{ opacity: 1 }}
532
+ transition={{ duration: 0.8, delay: 0.2 }}
533
+ className="text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto mb-6"
534
+ >
535
+ <span className="text-foreground/80">{t('landing.hero.subheading')}</span>
536
+ </motion.p>
537
+ </div>
538
+
539
+ <motion.div
540
+ initial={{ opacity: 0, y: 10 }}
541
+ animate={{ opacity: 1, y: 0 }}
542
+ transition={{ duration: 0.6, delay: 0.4 }}
543
+ className="mb-10"
544
+ >
545
+ <NewsletterSignup className="justify-center" buttonText={t('landing.hero.getUpdates')} />
546
+ </motion.div>
547
+
548
+ <div className="w-full max-w-3xl relative overflow-visible">
549
+ {/* Ambient glow orb — 3 layers for depth */}
550
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 -z-10 pointer-events-none">
551
+ <div className="hero-orb w-[700px] h-[400px] rounded-full bg-primary/8 blur-[100px]" />
552
+ <div className="absolute inset-[80px] rounded-full bg-primary/15 blur-[60px]" />
553
+ <div className="absolute inset-[160px] rounded-full bg-primary/30 blur-[30px]" />
554
+ <div className="absolute top-[40%] left-[20%] w-[200px] h-[200px] rounded-full bg-secondary-accent/10 blur-[80px]" />
555
+ </div>
556
+
557
+ <div className="h-[380px] relative">
558
+ <motion.div
559
+ initial={false}
560
+ className={cn(
561
+ 'absolute inset-0 overflow-hidden border transition-colors duration-1000 rounded-2xl shadow-2xl backdrop-blur-md',
562
+ terminalComplete ? 'bg-card/70 border-border' : 'bg-background/65 border-border/50'
563
+ )}
564
+ >
565
+ {/* Glass spotlight from above */}
566
+ <div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-primary to-transparent z-20" />
567
+ <div
568
+ className="absolute inset-x-0 top-0 h-[220px] pointer-events-none z-10"
569
+ style={{
570
+ background:
571
+ 'radial-gradient(ellipse 70% 55% at 50% 0%, oklch(0.82 0.14 192 / 0.30) 0%, transparent 100%)',
572
+ }}
573
+ />
574
+ <div className="spotlight-particles absolute inset-0 pointer-events-none opacity-50 z-10" />
575
+
576
+ <div className="absolute inset-0 bg-gradient-to-br from-black/5 dark:from-white/5 to-transparent pointer-events-none z-20 rounded-2xl" />
577
+
578
+ <AnimatePresence mode="wait">
579
+ {!terminalComplete ? (
580
+ <motion.div
581
+ key={`terminal-${animationKey}`}
582
+ initial={{ opacity: 0 }}
583
+ animate={{ opacity: 1 }}
584
+ exit={{ opacity: 0, filter: 'blur(10px)' }}
585
+ transition={{ duration: 0.5 }}
586
+ className="h-full"
587
+ >
588
+ <TerminalWindow onComplete={handleTerminalComplete} />
589
+ </motion.div>
590
+ ) : (
591
+ <motion.div
592
+ key={`dashboard-${animationKey}`}
593
+ initial={{ opacity: 0, scale: 0.95, filter: 'blur(10px)' }}
594
+ animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
595
+ transition={{ duration: 0.8 }}
596
+ className="h-full"
597
+ >
598
+ <DashboardPreview />
599
+ </motion.div>
600
+ )}
601
+ </AnimatePresence>
602
+ </motion.div>
603
+ </div>
604
+ </div>
605
+ </main>
606
+ </div>
607
+ );
608
+ }
@@ -0,0 +1,31 @@
1
+ import { Eye, X } from 'lucide-react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useAuth } from './auth/AuthProvider';
4
+ import { Button } from './ui/button';
5
+
6
+ export function ImpersonationBanner() {
7
+ const { t } = useTranslation();
8
+ const { isImpersonating, impersonationTarget, stopImpersonating } = useAuth();
9
+
10
+ if (!isImpersonating || !impersonationTarget) return null;
11
+
12
+ return (
13
+ <div className="fixed top-0 inset-x-0 z-[100] bg-amber-500 text-black px-4 py-1.5 flex items-center justify-center gap-3 text-sm font-medium">
14
+ <Eye className="size-4 shrink-0" />
15
+ <span>
16
+ {t('admin.impersonating', {
17
+ email: impersonationTarget.email,
18
+ })}
19
+ </span>
20
+ <Button
21
+ variant="ghost"
22
+ size="xs"
23
+ className="h-6 px-2 text-black hover:bg-amber-600 hover:text-black"
24
+ onClick={stopImpersonating}
25
+ >
26
+ <X className="size-3 mr-1" />
27
+ {t('admin.stopImpersonating')}
28
+ </Button>
29
+ </div>
30
+ );
31
+ }