gitspace 0.2.0-rc.1

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 (318) hide show
  1. package/.claude/settings.local.json +21 -0
  2. package/.gitspace/bundle.json +50 -0
  3. package/.gitspace/select/01-status.sh +40 -0
  4. package/.gitspace/setup/01-install-deps.sh +12 -0
  5. package/.gitspace/setup/02-typecheck.sh +16 -0
  6. package/AGENTS.md +439 -0
  7. package/CLAUDE.md +1 -0
  8. package/LICENSE +25 -0
  9. package/README.md +607 -0
  10. package/bin/gssh +62 -0
  11. package/bun.lock +647 -0
  12. package/docs/CONNECTION.md +623 -0
  13. package/docs/GATEWAY-WORKER.md +319 -0
  14. package/docs/GETTING-STARTED.md +448 -0
  15. package/docs/GITSPACE-PLATFORM.md +1819 -0
  16. package/docs/INFRASTRUCTURE.md +1347 -0
  17. package/docs/PROTOCOL.md +619 -0
  18. package/docs/QUICKSTART.md +174 -0
  19. package/docs/RELAY.md +327 -0
  20. package/docs/REMOTE-DESIGN.md +549 -0
  21. package/docs/ROADMAP.md +564 -0
  22. package/docs/SITE_DOCS_FIGMA_MAKE.md +1167 -0
  23. package/docs/STACK-DESIGN.md +588 -0
  24. package/docs/UNIFIED_ARCHITECTURE.md +292 -0
  25. package/experiments/pty-benchmark.ts +148 -0
  26. package/experiments/pty-latency.ts +100 -0
  27. package/experiments/router/client.ts +199 -0
  28. package/experiments/router/protocol.ts +74 -0
  29. package/experiments/router/router.ts +217 -0
  30. package/experiments/router/session.ts +180 -0
  31. package/experiments/router/test.ts +133 -0
  32. package/experiments/socket-bandwidth.ts +77 -0
  33. package/homebrew/gitspace.rb +45 -0
  34. package/landing-page/ATTRIBUTIONS.md +3 -0
  35. package/landing-page/README.md +11 -0
  36. package/landing-page/bun.lock +801 -0
  37. package/landing-page/guidelines/Guidelines.md +61 -0
  38. package/landing-page/index.html +37 -0
  39. package/landing-page/package.json +90 -0
  40. package/landing-page/postcss.config.mjs +15 -0
  41. package/landing-page/public/_redirects +1 -0
  42. package/landing-page/public/favicon.png +0 -0
  43. package/landing-page/src/app/App.tsx +53 -0
  44. package/landing-page/src/app/components/figma/ImageWithFallback.tsx +27 -0
  45. package/landing-page/src/app/components/ui/accordion.tsx +66 -0
  46. package/landing-page/src/app/components/ui/alert-dialog.tsx +157 -0
  47. package/landing-page/src/app/components/ui/alert.tsx +66 -0
  48. package/landing-page/src/app/components/ui/aspect-ratio.tsx +11 -0
  49. package/landing-page/src/app/components/ui/avatar.tsx +53 -0
  50. package/landing-page/src/app/components/ui/badge.tsx +46 -0
  51. package/landing-page/src/app/components/ui/breadcrumb.tsx +109 -0
  52. package/landing-page/src/app/components/ui/button.tsx +57 -0
  53. package/landing-page/src/app/components/ui/calendar.tsx +75 -0
  54. package/landing-page/src/app/components/ui/card.tsx +92 -0
  55. package/landing-page/src/app/components/ui/carousel.tsx +241 -0
  56. package/landing-page/src/app/components/ui/chart.tsx +353 -0
  57. package/landing-page/src/app/components/ui/checkbox.tsx +32 -0
  58. package/landing-page/src/app/components/ui/collapsible.tsx +33 -0
  59. package/landing-page/src/app/components/ui/command.tsx +177 -0
  60. package/landing-page/src/app/components/ui/context-menu.tsx +252 -0
  61. package/landing-page/src/app/components/ui/dialog.tsx +135 -0
  62. package/landing-page/src/app/components/ui/drawer.tsx +132 -0
  63. package/landing-page/src/app/components/ui/dropdown-menu.tsx +257 -0
  64. package/landing-page/src/app/components/ui/form.tsx +168 -0
  65. package/landing-page/src/app/components/ui/hover-card.tsx +44 -0
  66. package/landing-page/src/app/components/ui/input-otp.tsx +77 -0
  67. package/landing-page/src/app/components/ui/input.tsx +21 -0
  68. package/landing-page/src/app/components/ui/label.tsx +24 -0
  69. package/landing-page/src/app/components/ui/menubar.tsx +276 -0
  70. package/landing-page/src/app/components/ui/navigation-menu.tsx +168 -0
  71. package/landing-page/src/app/components/ui/pagination.tsx +127 -0
  72. package/landing-page/src/app/components/ui/popover.tsx +48 -0
  73. package/landing-page/src/app/components/ui/progress.tsx +31 -0
  74. package/landing-page/src/app/components/ui/radio-group.tsx +45 -0
  75. package/landing-page/src/app/components/ui/resizable.tsx +56 -0
  76. package/landing-page/src/app/components/ui/scroll-area.tsx +58 -0
  77. package/landing-page/src/app/components/ui/select.tsx +189 -0
  78. package/landing-page/src/app/components/ui/separator.tsx +28 -0
  79. package/landing-page/src/app/components/ui/sheet.tsx +139 -0
  80. package/landing-page/src/app/components/ui/sidebar.tsx +726 -0
  81. package/landing-page/src/app/components/ui/skeleton.tsx +13 -0
  82. package/landing-page/src/app/components/ui/slider.tsx +63 -0
  83. package/landing-page/src/app/components/ui/sonner.tsx +25 -0
  84. package/landing-page/src/app/components/ui/switch.tsx +31 -0
  85. package/landing-page/src/app/components/ui/table.tsx +116 -0
  86. package/landing-page/src/app/components/ui/tabs.tsx +66 -0
  87. package/landing-page/src/app/components/ui/textarea.tsx +18 -0
  88. package/landing-page/src/app/components/ui/toggle-group.tsx +73 -0
  89. package/landing-page/src/app/components/ui/toggle.tsx +47 -0
  90. package/landing-page/src/app/components/ui/tooltip.tsx +61 -0
  91. package/landing-page/src/app/components/ui/use-mobile.ts +21 -0
  92. package/landing-page/src/app/components/ui/utils.ts +6 -0
  93. package/landing-page/src/components/docs/DocsContent.tsx +718 -0
  94. package/landing-page/src/components/docs/DocsSidebar.tsx +84 -0
  95. package/landing-page/src/components/landing/CTA.tsx +59 -0
  96. package/landing-page/src/components/landing/Comparison.tsx +84 -0
  97. package/landing-page/src/components/landing/FaultyTerminal.tsx +424 -0
  98. package/landing-page/src/components/landing/Features.tsx +201 -0
  99. package/landing-page/src/components/landing/Hero.tsx +142 -0
  100. package/landing-page/src/components/landing/Pricing.tsx +140 -0
  101. package/landing-page/src/components/landing/Roadmap.tsx +86 -0
  102. package/landing-page/src/components/landing/Security.tsx +81 -0
  103. package/landing-page/src/components/landing/TerminalWindow.tsx +27 -0
  104. package/landing-page/src/components/landing/UseCases.tsx +55 -0
  105. package/landing-page/src/components/landing/Workflow.tsx +101 -0
  106. package/landing-page/src/components/layout/DashboardNavbar.tsx +37 -0
  107. package/landing-page/src/components/layout/Footer.tsx +55 -0
  108. package/landing-page/src/components/layout/LandingNavbar.tsx +82 -0
  109. package/landing-page/src/components/ui/badge.tsx +39 -0
  110. package/landing-page/src/components/ui/breadcrumb.tsx +115 -0
  111. package/landing-page/src/components/ui/button.tsx +57 -0
  112. package/landing-page/src/components/ui/card.tsx +79 -0
  113. package/landing-page/src/components/ui/mock-terminal.tsx +68 -0
  114. package/landing-page/src/components/ui/separator.tsx +28 -0
  115. package/landing-page/src/lib/utils.ts +6 -0
  116. package/landing-page/src/main.tsx +10 -0
  117. package/landing-page/src/pages/Dashboard.tsx +133 -0
  118. package/landing-page/src/pages/DocsPage.tsx +79 -0
  119. package/landing-page/src/pages/LandingPage.tsx +31 -0
  120. package/landing-page/src/pages/TerminalView.tsx +106 -0
  121. package/landing-page/src/styles/fonts.css +0 -0
  122. package/landing-page/src/styles/index.css +3 -0
  123. package/landing-page/src/styles/tailwind.css +4 -0
  124. package/landing-page/src/styles/theme.css +181 -0
  125. package/landing-page/vite.config.ts +19 -0
  126. package/npm/darwin-arm64/bin/gssh +0 -0
  127. package/npm/darwin-arm64/package.json +20 -0
  128. package/package.json +74 -0
  129. package/scripts/build.ts +284 -0
  130. package/scripts/release.ts +140 -0
  131. package/src/__tests__/test-utils.ts +298 -0
  132. package/src/commands/__tests__/serve-messages.test.ts +190 -0
  133. package/src/commands/access.ts +298 -0
  134. package/src/commands/add.ts +452 -0
  135. package/src/commands/auth.ts +364 -0
  136. package/src/commands/connect.ts +287 -0
  137. package/src/commands/directory.ts +16 -0
  138. package/src/commands/host.ts +396 -0
  139. package/src/commands/identity.ts +184 -0
  140. package/src/commands/list.ts +200 -0
  141. package/src/commands/relay.ts +315 -0
  142. package/src/commands/remove.ts +241 -0
  143. package/src/commands/serve.ts +1493 -0
  144. package/src/commands/share.ts +456 -0
  145. package/src/commands/status.ts +125 -0
  146. package/src/commands/switch.ts +353 -0
  147. package/src/commands/tmux.ts +317 -0
  148. package/src/core/__tests__/access.test.ts +240 -0
  149. package/src/core/access.ts +277 -0
  150. package/src/core/bundle.ts +342 -0
  151. package/src/core/config.ts +510 -0
  152. package/src/core/git.ts +317 -0
  153. package/src/core/github.ts +151 -0
  154. package/src/core/identity.ts +631 -0
  155. package/src/core/linear.ts +225 -0
  156. package/src/core/shell.ts +161 -0
  157. package/src/core/trusted-relays.ts +315 -0
  158. package/src/index.ts +821 -0
  159. package/src/lib/remote-session/index.ts +7 -0
  160. package/src/lib/remote-session/protocol.ts +267 -0
  161. package/src/lib/remote-session/session-handler.ts +581 -0
  162. package/src/lib/remote-session/workspace-scanner.ts +167 -0
  163. package/src/lib/tmux-lite/README.md +81 -0
  164. package/src/lib/tmux-lite/cli.ts +796 -0
  165. package/src/lib/tmux-lite/crypto/__tests__/helpers/handshake-runner.ts +349 -0
  166. package/src/lib/tmux-lite/crypto/__tests__/helpers/mock-relay.ts +291 -0
  167. package/src/lib/tmux-lite/crypto/__tests__/helpers/test-identities.ts +142 -0
  168. package/src/lib/tmux-lite/crypto/__tests__/integration/authorization.integration.test.ts +339 -0
  169. package/src/lib/tmux-lite/crypto/__tests__/integration/e2e-communication.integration.test.ts +477 -0
  170. package/src/lib/tmux-lite/crypto/__tests__/integration/error-handling.integration.test.ts +499 -0
  171. package/src/lib/tmux-lite/crypto/__tests__/integration/handshake.integration.test.ts +371 -0
  172. package/src/lib/tmux-lite/crypto/__tests__/integration/security.integration.test.ts +573 -0
  173. package/src/lib/tmux-lite/crypto/access-control.test.ts +512 -0
  174. package/src/lib/tmux-lite/crypto/access-control.ts +320 -0
  175. package/src/lib/tmux-lite/crypto/frames.test.ts +262 -0
  176. package/src/lib/tmux-lite/crypto/frames.ts +141 -0
  177. package/src/lib/tmux-lite/crypto/handshake.ts +894 -0
  178. package/src/lib/tmux-lite/crypto/identity.test.ts +220 -0
  179. package/src/lib/tmux-lite/crypto/identity.ts +286 -0
  180. package/src/lib/tmux-lite/crypto/index.ts +51 -0
  181. package/src/lib/tmux-lite/crypto/invites.test.ts +381 -0
  182. package/src/lib/tmux-lite/crypto/invites.ts +215 -0
  183. package/src/lib/tmux-lite/crypto/keyexchange.ts +435 -0
  184. package/src/lib/tmux-lite/crypto/keys.test.ts +58 -0
  185. package/src/lib/tmux-lite/crypto/keys.ts +47 -0
  186. package/src/lib/tmux-lite/crypto/secretbox.test.ts +169 -0
  187. package/src/lib/tmux-lite/crypto/secretbox.ts +124 -0
  188. package/src/lib/tmux-lite/handshake-handler.ts +451 -0
  189. package/src/lib/tmux-lite/protocol.test.ts +307 -0
  190. package/src/lib/tmux-lite/protocol.ts +266 -0
  191. package/src/lib/tmux-lite/relay-client.ts +506 -0
  192. package/src/lib/tmux-lite/server.ts +1250 -0
  193. package/src/lib/tmux-lite/shell-integration.sh +37 -0
  194. package/src/lib/tmux-lite/terminal-queries.test.ts +54 -0
  195. package/src/lib/tmux-lite/terminal-queries.ts +49 -0
  196. package/src/relay/__tests__/e2e-flow.test.ts +1284 -0
  197. package/src/relay/__tests__/helpers/auth.ts +354 -0
  198. package/src/relay/__tests__/helpers/ports.ts +51 -0
  199. package/src/relay/__tests__/protocol-validation.test.ts +265 -0
  200. package/src/relay/authorization.ts +303 -0
  201. package/src/relay/embedded-assets.generated.d.ts +15 -0
  202. package/src/relay/identity.ts +352 -0
  203. package/src/relay/index.ts +57 -0
  204. package/src/relay/pipes.test.ts +427 -0
  205. package/src/relay/pipes.ts +195 -0
  206. package/src/relay/protocol.ts +804 -0
  207. package/src/relay/registries.test.ts +437 -0
  208. package/src/relay/registries.ts +593 -0
  209. package/src/relay/server.test.ts +1323 -0
  210. package/src/relay/server.ts +1092 -0
  211. package/src/relay/signing.ts +238 -0
  212. package/src/relay/types.ts +69 -0
  213. package/src/serve/client-session-manager.ts +622 -0
  214. package/src/serve/daemon.ts +497 -0
  215. package/src/serve/pty-session.ts +236 -0
  216. package/src/serve/types.ts +169 -0
  217. package/src/shared/components/Flow.tsx +453 -0
  218. package/src/shared/components/Flow.tui.tsx +343 -0
  219. package/src/shared/components/Flow.web.tsx +442 -0
  220. package/src/shared/components/Inbox.tsx +446 -0
  221. package/src/shared/components/Inbox.tui.tsx +262 -0
  222. package/src/shared/components/Inbox.web.tsx +329 -0
  223. package/src/shared/components/MachineList.tsx +187 -0
  224. package/src/shared/components/MachineList.tui.tsx +161 -0
  225. package/src/shared/components/MachineList.web.tsx +210 -0
  226. package/src/shared/components/ProjectList.tsx +176 -0
  227. package/src/shared/components/ProjectList.tui.tsx +109 -0
  228. package/src/shared/components/ProjectList.web.tsx +143 -0
  229. package/src/shared/components/SpacesBrowser.tsx +332 -0
  230. package/src/shared/components/SpacesBrowser.tui.tsx +163 -0
  231. package/src/shared/components/SpacesBrowser.web.tsx +221 -0
  232. package/src/shared/components/index.ts +103 -0
  233. package/src/shared/hooks/index.ts +16 -0
  234. package/src/shared/hooks/useNavigation.ts +226 -0
  235. package/src/shared/index.ts +122 -0
  236. package/src/shared/providers/LocalMachineProvider.ts +425 -0
  237. package/src/shared/providers/MachineProvider.ts +165 -0
  238. package/src/shared/providers/RemoteMachineProvider.ts +444 -0
  239. package/src/shared/providers/index.ts +26 -0
  240. package/src/shared/types.ts +145 -0
  241. package/src/tui/adapters.ts +120 -0
  242. package/src/tui/app.tsx +1816 -0
  243. package/src/tui/components/Terminal.tsx +580 -0
  244. package/src/tui/hooks/index.ts +35 -0
  245. package/src/tui/hooks/useAppState.ts +314 -0
  246. package/src/tui/hooks/useDaemonStatus.ts +174 -0
  247. package/src/tui/hooks/useInboxTUI.ts +113 -0
  248. package/src/tui/hooks/useRemoteMachines.ts +209 -0
  249. package/src/tui/index.ts +24 -0
  250. package/src/tui/state.ts +299 -0
  251. package/src/tui/terminal-bracketed-paste.test.ts +45 -0
  252. package/src/tui/terminal-bracketed-paste.ts +47 -0
  253. package/src/types/bundle.ts +112 -0
  254. package/src/types/config.ts +89 -0
  255. package/src/types/errors.ts +206 -0
  256. package/src/types/identity.ts +284 -0
  257. package/src/types/workspace-fuzzy.ts +49 -0
  258. package/src/types/workspace.ts +151 -0
  259. package/src/utils/bun-socket-writer.ts +80 -0
  260. package/src/utils/deps.ts +127 -0
  261. package/src/utils/fuzzy-match.ts +125 -0
  262. package/src/utils/logger.ts +127 -0
  263. package/src/utils/markdown.ts +254 -0
  264. package/src/utils/onboarding.ts +229 -0
  265. package/src/utils/prompts.ts +114 -0
  266. package/src/utils/run-commands.ts +112 -0
  267. package/src/utils/run-scripts.ts +142 -0
  268. package/src/utils/sanitize.ts +98 -0
  269. package/src/utils/secrets.ts +122 -0
  270. package/src/utils/shell-escape.ts +40 -0
  271. package/src/utils/utf8.ts +79 -0
  272. package/src/utils/workspace-state.ts +47 -0
  273. package/src/web/README.md +73 -0
  274. package/src/web/bun.lock +575 -0
  275. package/src/web/eslint.config.js +23 -0
  276. package/src/web/index.html +16 -0
  277. package/src/web/package.json +37 -0
  278. package/src/web/public/vite.svg +1 -0
  279. package/src/web/src/App.tsx +604 -0
  280. package/src/web/src/assets/react.svg +1 -0
  281. package/src/web/src/components/Terminal.tsx +207 -0
  282. package/src/web/src/hooks/useRelayConnection.ts +224 -0
  283. package/src/web/src/hooks/useTerminal.ts +699 -0
  284. package/src/web/src/index.css +55 -0
  285. package/src/web/src/lib/crypto/__tests__/web-terminal.test.ts +1158 -0
  286. package/src/web/src/lib/crypto/frames.ts +205 -0
  287. package/src/web/src/lib/crypto/handshake.ts +396 -0
  288. package/src/web/src/lib/crypto/identity.ts +128 -0
  289. package/src/web/src/lib/crypto/keyexchange.ts +246 -0
  290. package/src/web/src/lib/crypto/relay-signing.ts +53 -0
  291. package/src/web/src/lib/invite.ts +58 -0
  292. package/src/web/src/lib/storage/identity-store.ts +94 -0
  293. package/src/web/src/main.tsx +10 -0
  294. package/src/web/src/types/identity.ts +45 -0
  295. package/src/web/tsconfig.app.json +28 -0
  296. package/src/web/tsconfig.json +7 -0
  297. package/src/web/tsconfig.node.json +26 -0
  298. package/src/web/vite.config.ts +31 -0
  299. package/todo-security.md +92 -0
  300. package/tsconfig.json +23 -0
  301. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite +0 -0
  302. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-shm +0 -0
  303. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-wal +0 -0
  304. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite +0 -0
  305. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-shm +0 -0
  306. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-wal +0 -0
  307. package/worker/bun.lock +237 -0
  308. package/worker/package.json +22 -0
  309. package/worker/schema.sql +96 -0
  310. package/worker/src/handlers/auth.ts +451 -0
  311. package/worker/src/handlers/subdomains.ts +376 -0
  312. package/worker/src/handlers/user.ts +98 -0
  313. package/worker/src/index.ts +70 -0
  314. package/worker/src/middleware/auth.ts +152 -0
  315. package/worker/src/services/cloudflare.ts +609 -0
  316. package/worker/src/types.ts +96 -0
  317. package/worker/tsconfig.json +15 -0
  318. package/worker/wrangler.toml +26 -0
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { cn } from "../../lib/utils";
3
+
4
+ interface TerminalWindowProps {
5
+ children: React.ReactNode;
6
+ title?: string;
7
+ className?: string;
8
+ }
9
+
10
+ export function TerminalWindow({ children, title = "bash", className }: TerminalWindowProps) {
11
+ return (
12
+ <div className={cn("rounded-lg overflow-hidden border border-zinc-800 bg-black/90 font-mono shadow-2xl", className)}>
13
+ <div className="flex items-center justify-between px-4 py-2 bg-zinc-900/50 border-b border-zinc-800">
14
+ <div className="flex space-x-2">
15
+ <div className="w-3 h-3 rounded-full bg-red-500/20 border border-red-500/50" />
16
+ <div className="w-3 h-3 rounded-full bg-yellow-500/20 border border-yellow-500/50" />
17
+ <div className="w-3 h-3 rounded-full bg-green-500/20 border border-green-500/50" />
18
+ </div>
19
+ <div className="text-xs text-zinc-500 font-medium">{title}</div>
20
+ <div className="w-16" /> {/* Spacer for centering */}
21
+ </div>
22
+ <div className="p-4 text-sm md:text-base text-zinc-300 overflow-x-auto">
23
+ {children}
24
+ </div>
25
+ </div>
26
+ );
27
+ }
@@ -0,0 +1,55 @@
1
+ import { Quote, Star } from "lucide-react";
2
+ import { Card, CardContent } from "../../app/components/ui/card";
3
+
4
+ export function UseCases() {
5
+ const testimonials = [
6
+ {
7
+ quote: "I run 3-4 Claude agents in parallel now. Each in its own space. I check progress from my phone while making dinner. When they're done, git stack turns the mess into reviewable PRs.",
8
+ author: "Solo founder",
9
+ role: "Shipping 3x faster",
10
+ stars: 5
11
+ },
12
+ {
13
+ quote: "We share view-only links in Slack. Everyone can watch the agent work without stepping on each other. Game changer for debugging.",
14
+ author: "Tech lead",
15
+ role: "8-person startup",
16
+ stars: 5
17
+ },
18
+ {
19
+ quote: "No more 'hold on, let me stash my changes.' I have 6 workspaces open right now. Context switching is instant.",
20
+ author: "Senior engineer",
21
+ role: "Enterprise",
22
+ stars: 5
23
+ }
24
+ ];
25
+
26
+ return (
27
+ <section className="py-24 bg-zinc-900/20">
28
+ <div className="container px-4 mx-auto">
29
+ <h2 className="text-3xl md:text-5xl font-bold text-center mb-16">How developers use GitSpace</h2>
30
+
31
+ <div className="grid md:grid-cols-3 gap-8">
32
+ {testimonials.map((item, i) => (
33
+ <Card key={i} className="bg-black/40 border-zinc-800 hover:border-zinc-700 transition-colors">
34
+ <CardContent className="pt-6">
35
+ <div className="flex gap-1 mb-4">
36
+ {[...Array(item.stars)].map((_, i) => (
37
+ <Star key={i} className="w-4 h-4 fill-green-500 text-green-500" />
38
+ ))}
39
+ </div>
40
+ <Quote className="w-8 h-8 text-zinc-700 mb-4" />
41
+ <p className="text-zinc-300 leading-relaxed mb-6 italic">
42
+ "{item.quote}"
43
+ </p>
44
+ <div className="border-t border-zinc-800 pt-4">
45
+ <p className="font-bold text-white">{item.author}</p>
46
+ <p className="text-sm text-zinc-500">{item.role}</p>
47
+ </div>
48
+ </CardContent>
49
+ </Card>
50
+ ))}
51
+ </div>
52
+ </div>
53
+ </section>
54
+ );
55
+ }
@@ -0,0 +1,101 @@
1
+ import { Plus, Play, Smartphone, GitMerge, ArrowRight } from "lucide-react";
2
+
3
+ export function Workflow() {
4
+ return (
5
+ <section id="workflow" className="py-24 bg-zinc-950 relative overflow-hidden">
6
+ <div className="container px-4 mx-auto">
7
+ <div className="text-center mb-16">
8
+ <h2 className="text-3xl md:text-5xl font-bold mb-4">Built for how AI agents work</h2>
9
+ <p className="text-zinc-400 max-w-2xl mx-auto">
10
+ The old way: One branch. Wait for agent. Messy commits. Manual cleanup.<br />
11
+ <span className="text-green-400">The GitSpace way: Parallel work. Remote monitoring. Secure relay.</span>
12
+ </p>
13
+ </div>
14
+
15
+ <div className="relative">
16
+ {/* Connecting Line (Desktop) */}
17
+ <div className="hidden md:block absolute top-12 left-0 w-full h-0.5 bg-gradient-to-r from-transparent via-zinc-700 to-transparent" />
18
+
19
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-8 relative z-10">
20
+ <div className="flex flex-col items-center text-center group">
21
+ <div className="w-24 h-24 rounded-2xl bg-black border border-zinc-800 flex items-center justify-center mb-6 shadow-xl group-hover:border-green-500/50 group-hover:shadow-green-500/10 transition-all duration-300 relative">
22
+ <Plus className="w-10 h-10 text-zinc-400 group-hover:text-green-400 transition-colors" />
23
+ <div className="absolute -bottom-3 bg-zinc-900 px-3 py-1 rounded-full text-xs font-bold border border-zinc-800 text-zinc-300">
24
+ CREATE
25
+ </div>
26
+ </div>
27
+
28
+ <code className="bg-black/50 px-3 py-1 rounded text-sm font-mono text-green-400/80 mb-2 border border-white/5 w-full md:w-auto truncate max-w-[200px]">
29
+ gssh add feature-x
30
+ </code>
31
+
32
+ <p className="text-zinc-500 text-sm">
33
+ Isolated workspace
34
+ </p>
35
+
36
+ {/* Mobile Arrow */}
37
+ <ArrowRight className="md:hidden w-6 h-6 text-zinc-700 my-4" />
38
+ </div>
39
+
40
+ <div className="flex flex-col items-center text-center group">
41
+ <div className="w-24 h-24 rounded-2xl bg-black border border-zinc-800 flex items-center justify-center mb-6 shadow-xl group-hover:border-green-500/50 group-hover:shadow-green-500/10 transition-all duration-300 relative">
42
+ <Play className="w-10 h-10 text-zinc-400 group-hover:text-green-400 transition-colors" />
43
+ <div className="absolute -bottom-3 bg-zinc-900 px-3 py-1 rounded-full text-xs font-bold border border-zinc-800 text-zinc-300">
44
+ RUN
45
+ </div>
46
+ </div>
47
+
48
+ <code className="bg-black/50 px-3 py-1 rounded text-sm font-mono text-green-400/80 mb-2 border border-white/5 w-full md:w-auto truncate max-w-[200px]">
49
+ claude "add auth system"
50
+ </code>
51
+
52
+ <p className="text-zinc-500 text-sm">
53
+ Agent runs for hours
54
+ </p>
55
+
56
+ {/* Mobile Arrow */}
57
+ <ArrowRight className="md:hidden w-6 h-6 text-zinc-700 my-4" />
58
+ </div>
59
+
60
+ <div className="flex flex-col items-center text-center group">
61
+ <div className="w-24 h-24 rounded-2xl bg-black border border-zinc-800 flex items-center justify-center mb-6 shadow-xl group-hover:border-green-500/50 group-hover:shadow-green-500/10 transition-all duration-300 relative">
62
+ <Smartphone className="w-10 h-10 text-zinc-400 group-hover:text-green-400 transition-colors" />
63
+ <div className="absolute -bottom-3 bg-zinc-900 px-3 py-1 rounded-full text-xs font-bold border border-zinc-800 text-zinc-300">
64
+ MONITOR
65
+ </div>
66
+ </div>
67
+
68
+ <code className="bg-black/50 px-3 py-1 rounded text-sm font-mono text-green-400/80 mb-2 border border-white/5 w-full md:w-auto truncate max-w-[200px]">
69
+ Check in from your phone
70
+ </code>
71
+
72
+ <p className="text-zinc-500 text-sm">
73
+ Get notified when done
74
+ </p>
75
+
76
+ {/* Mobile Arrow */}
77
+ <ArrowRight className="md:hidden w-6 h-6 text-zinc-700 my-4" />
78
+ </div>
79
+
80
+ <div className="flex flex-col items-center text-center group">
81
+ <div className="w-24 h-24 rounded-2xl bg-black border border-zinc-800 flex items-center justify-center mb-6 shadow-xl group-hover:border-green-500/50 group-hover:shadow-green-500/10 transition-all duration-300 relative">
82
+ <GitMerge className="w-10 h-10 text-zinc-400 group-hover:text-green-400 transition-colors" />
83
+ <div className="absolute -bottom-3 bg-zinc-900 px-3 py-1 rounded-full text-xs font-bold border border-zinc-800 text-zinc-300">
84
+ SHIP
85
+ </div>
86
+ </div>
87
+
88
+ <code className="bg-black/50 px-3 py-1 rounded text-sm font-mono text-green-400/80 mb-2 border border-white/5 w-full md:w-auto truncate max-w-[200px]">
89
+ gssh switch feature-x
90
+ </code>
91
+
92
+ <p className="text-zinc-500 text-sm">
93
+ Instant context switch
94
+ </p>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </section>
100
+ );
101
+ }
@@ -0,0 +1,37 @@
1
+ import { Link } from "react-router-dom";
2
+ import { Terminal, Bell, ChevronDown } from "lucide-react";
3
+ import { Button } from "../../app/components/ui/button";
4
+
5
+ export function DashboardNavbar() {
6
+ return (
7
+ <nav className="border-b border-white/10 bg-black">
8
+ <div className="container mx-auto px-4 h-14 flex items-center justify-between">
9
+ <Link to="/dashboard" className="flex items-center gap-2 font-semibold text-lg tracking-tight">
10
+ <Terminal className="h-5 w-5 text-green-500" />
11
+ <span>gitspace.sh</span>
12
+ </Link>
13
+
14
+ <div className="flex items-center gap-4">
15
+ <div className="flex items-center gap-2 text-sm font-medium hover:bg-white/5 px-2 py-1.5 rounded-md cursor-pointer transition-colors">
16
+ <div className="h-6 w-6 rounded-full bg-green-500/20 flex items-center justify-center text-green-500 text-xs">B</div>
17
+ <span>brad</span>
18
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
19
+ </div>
20
+
21
+ <div className="h-8 w-[1px] bg-white/10" />
22
+
23
+ <Button variant="ghost" size="icon" className="relative text-muted-foreground hover:text-foreground">
24
+ <Bell className="h-5 w-5" />
25
+ <span className="absolute top-2 right-2 h-2 w-2 rounded-full bg-red-500 border-2 border-black" />
26
+ </Button>
27
+
28
+ <Link to="/">
29
+ <Button variant="ghost" size="sm" className="text-muted-foreground hover:text-foreground">
30
+ Logout
31
+ </Button>
32
+ </Link>
33
+ </div>
34
+ </div>
35
+ </nav>
36
+ );
37
+ }
@@ -0,0 +1,55 @@
1
+ import { Link } from "react-router-dom";
2
+ import { Terminal } from "lucide-react";
3
+
4
+ export function Footer() {
5
+ return (
6
+ <footer className="border-t border-white/10 bg-black py-12 text-sm">
7
+ <div className="container mx-auto px-4">
8
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-8 mb-12">
9
+ <div className="space-y-4">
10
+ <div className="flex items-center gap-2 font-semibold text-lg tracking-tight">
11
+ <Terminal className="h-5 w-5 text-green-500" />
12
+ <span>gitspace.sh</span>
13
+ </div>
14
+ </div>
15
+
16
+ <div className="space-y-4">
17
+ <h4 className="font-semibold">Product</h4>
18
+ <ul className="space-y-2 text-muted-foreground">
19
+ <li><Link to="/#features" className="hover:text-foreground">Features</Link></li>
20
+ <li><Link to="/#pricing" className="hover:text-foreground">Pricing</Link></li>
21
+ <li><Link to="/docs?section=security-notes" className="hover:text-foreground">Security</Link></li>
22
+ <li><Link to="/#workflow" className="hover:text-foreground">Workflow</Link></li>
23
+ </ul>
24
+ </div>
25
+
26
+ <div className="space-y-4">
27
+ <h4 className="font-semibold">Resources</h4>
28
+ <ul className="space-y-2 text-muted-foreground">
29
+ <li><Link to="/docs" className="hover:text-foreground">Docs</Link></li>
30
+ <li><a href="https://github.com/inkibra/gitspace.sh" target="_blank" rel="noopener noreferrer" className="hover:text-foreground">GitHub</a></li>
31
+ <li><a href="https://discord.gg/kHRWYPnR" target="_blank" rel="noopener noreferrer" className="hover:text-foreground">Discord</a></li>
32
+ </ul>
33
+ </div>
34
+
35
+ <div className="space-y-4">
36
+ <h4 className="font-semibold">Company</h4>
37
+ <ul className="space-y-2 text-muted-foreground">
38
+ <li><a href="https://www.inkibra.com/ink" target="_blank" rel="noopener noreferrer" className="hover:text-foreground">About</a></li>
39
+ <li><a href="https://www.inkibra.com/ink/blog" target="_blank" rel="noopener noreferrer" className="hover:text-foreground">Blog</a></li>
40
+ </ul>
41
+ </div>
42
+ </div>
43
+
44
+ <div className="pt-8 border-t border-white/10 flex flex-col md:flex-row justify-between items-center gap-4 text-muted-foreground">
45
+ <p>© 2026 inkibra, Inc.</p>
46
+ <div className="flex gap-6">
47
+ <a href="https://www.inkibra.com/legal/privacy-policy" target="_blank" rel="noopener noreferrer" className="hover:text-foreground">Privacy</a>
48
+ <a href="https://www.inkibra.com/legal/terms-of-service" target="_blank" rel="noopener noreferrer" className="hover:text-foreground">Terms</a>
49
+ <Link to="/docs?section=security-notes" className="hover:text-foreground">Security</Link>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </footer>
54
+ );
55
+ }
@@ -0,0 +1,82 @@
1
+ import { Link } from "react-router-dom";
2
+ import { Button } from "../../app/components/ui/button";
3
+ import { Terminal, Menu, X, Github } from "lucide-react";
4
+ import { useState } from "react";
5
+ import { motion, AnimatePresence } from "motion/react";
6
+
7
+ export function LandingNavbar() {
8
+ const [isOpen, setIsOpen] = useState(false);
9
+
10
+ const navLinks = [
11
+ { name: "Features", href: "/#features" },
12
+ { name: "Pricing", href: "/#pricing" },
13
+ { name: "Docs", href: "/docs" },
14
+ ];
15
+
16
+ return (
17
+ <nav className="border-b border-white/10 bg-black/50 backdrop-blur-md sticky top-0 z-50">
18
+ <div className="container mx-auto px-4 h-14 flex items-center justify-between">
19
+ <Link to="/" className="flex items-center gap-2 font-semibold text-lg tracking-tight z-50">
20
+ <Terminal className="h-5 w-5 text-green-500" />
21
+ <span>gitspace.sh</span>
22
+ </Link>
23
+
24
+ {/* Desktop Nav */}
25
+ <div className="hidden md:flex items-center gap-6 text-sm font-medium text-muted-foreground">
26
+ {navLinks.map((link) => (
27
+ <Link key={link.name} to={link.href} className="hover:text-foreground transition-colors">
28
+ {link.name}
29
+ </Link>
30
+ ))}
31
+ <a href="https://github.com/inkibra/gitspace.sh" target="_blank" rel="noopener noreferrer">
32
+ <Button size="sm" className="bg-white text-black hover:bg-gray-200">
33
+ <Github className="w-4 h-4 mr-2" />
34
+ Star on GitHub
35
+ </Button>
36
+ </a>
37
+ </div>
38
+
39
+ {/* Mobile Menu Toggle */}
40
+ <button
41
+ className="md:hidden z-50 p-2 text-zinc-400 hover:text-white"
42
+ onClick={() => setIsOpen(!isOpen)}
43
+ >
44
+ {isOpen ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
45
+ </button>
46
+ </div>
47
+
48
+ {/* Mobile Nav Overlay */}
49
+ <AnimatePresence>
50
+ {isOpen && (
51
+ <motion.div
52
+ initial={{ opacity: 0, y: -20 }}
53
+ animate={{ opacity: 1, y: 0 }}
54
+ exit={{ opacity: 0, y: -20 }}
55
+ transition={{ duration: 0.2 }}
56
+ className="fixed inset-0 z-40 md:hidden bg-black/95 backdrop-blur-xl pt-24 px-6 flex flex-col gap-8"
57
+ >
58
+ <div className="flex flex-col gap-6 text-xl font-medium text-zinc-400">
59
+ {navLinks.map((link) => (
60
+ <Link
61
+ key={link.name}
62
+ to={link.href}
63
+ onClick={() => setIsOpen(false)}
64
+ className="hover:text-white transition-colors"
65
+ >
66
+ {link.name}
67
+ </Link>
68
+ ))}
69
+ </div>
70
+
71
+ <a href="https://github.com/inkibra/gitspace.sh" target="_blank" rel="noopener noreferrer" onClick={() => setIsOpen(false)} className="mt-auto mb-12">
72
+ <Button size="lg" className="w-full bg-white text-black hover:bg-gray-200 text-base py-6">
73
+ <Github className="w-5 h-5 mr-2" />
74
+ Star on GitHub
75
+ </Button>
76
+ </a>
77
+ </motion.div>
78
+ )}
79
+ </AnimatePresence>
80
+ </nav>
81
+ );
82
+ }
@@ -0,0 +1,39 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "../../lib/utils"
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13
+ secondary:
14
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
+ destructive:
16
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
+ outline: "text-foreground",
18
+ success: "border-transparent bg-green-500/15 text-green-500 hover:bg-green-500/25",
19
+ warning: "border-transparent bg-amber-500/15 text-amber-500 hover:bg-amber-500/25",
20
+ error: "border-transparent bg-red-500/15 text-red-500 hover:bg-red-500/25",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: "default",
25
+ },
26
+ }
27
+ )
28
+
29
+ export interface BadgeProps
30
+ extends React.HTMLAttributes<HTMLDivElement>,
31
+ VariantProps<typeof badgeVariants> {}
32
+
33
+ function Badge({ className, variant, ...props }: BadgeProps) {
34
+ return (
35
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
36
+ )
37
+ }
38
+
39
+ export { Badge, badgeVariants }
@@ -0,0 +1,115 @@
1
+ import * as React from "react"
2
+ import { ChevronRight, MoreHorizontal } from "lucide-react"
3
+ import { Slot } from "@radix-ui/react-slot"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ const Breadcrumb = React.forwardRef<
8
+ HTMLElement,
9
+ React.ComponentPropsWithoutRef<"nav"> & {
10
+ separator?: React.ReactNode
11
+ }
12
+ >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
13
+ Breadcrumb.displayName = "Breadcrumb"
14
+
15
+ const BreadcrumbList = React.forwardRef<
16
+ HTMLOListElement,
17
+ React.ComponentPropsWithoutRef<"ol">
18
+ >(({ className, ...props }, ref) => (
19
+ <ol
20
+ ref={ref}
21
+ className={cn(
22
+ "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ ))
28
+ BreadcrumbList.displayName = "BreadcrumbList"
29
+
30
+ const BreadcrumbItem = React.forwardRef<
31
+ HTMLLIElement,
32
+ React.ComponentPropsWithoutRef<"li">
33
+ >(({ className, ...props }, ref) => (
34
+ <li
35
+ ref={ref}
36
+ className={cn("inline-flex items-center gap-1.5", className)}
37
+ {...props}
38
+ />
39
+ ))
40
+ BreadcrumbItem.displayName = "BreadcrumbItem"
41
+
42
+ const BreadcrumbLink = React.forwardRef<
43
+ HTMLAnchorElement,
44
+ React.ComponentPropsWithoutRef<"a"> & {
45
+ asChild?: boolean
46
+ }
47
+ >(({ asChild, className, ...props }, ref) => {
48
+ const Comp = asChild ? Slot : "a"
49
+
50
+ return (
51
+ <Comp
52
+ ref={ref}
53
+ className={cn("transition-colors hover:text-foreground", className)}
54
+ {...props}
55
+ />
56
+ )
57
+ })
58
+ BreadcrumbLink.displayName = "BreadcrumbLink"
59
+
60
+ const BreadcrumbPage = React.forwardRef<
61
+ HTMLSpanElement,
62
+ React.ComponentPropsWithoutRef<"span">
63
+ >(({ className, ...props }, ref) => (
64
+ <span
65
+ ref={ref}
66
+ role="link"
67
+ aria-disabled="true"
68
+ aria-current="page"
69
+ className={cn("font-normal text-foreground", className)}
70
+ {...props}
71
+ />
72
+ ))
73
+ BreadcrumbPage.displayName = "BreadcrumbPage"
74
+
75
+ const BreadcrumbSeparator = ({
76
+ children,
77
+ className,
78
+ ...props
79
+ }: React.ComponentProps<"li">) => (
80
+ <li
81
+ role="presentation"
82
+ aria-hidden="true"
83
+ className={cn("[&>svg]:size-3.5", className)}
84
+ {...props}
85
+ >
86
+ {children ?? <ChevronRight />}
87
+ </li>
88
+ )
89
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
90
+
91
+ const BreadcrumbEllipsis = ({
92
+ className,
93
+ ...props
94
+ }: React.ComponentProps<"span">) => (
95
+ <span
96
+ role="presentation"
97
+ aria-hidden="true"
98
+ className={cn("flex h-9 w-9 items-center justify-center", className)}
99
+ {...props}
100
+ >
101
+ <MoreHorizontal className="h-4 w-4" />
102
+ <span className="sr-only">More</span>
103
+ </span>
104
+ )
105
+ BreadcrumbEllipsis.displayName = "BreadcrumbEllipsis"
106
+
107
+ export {
108
+ Breadcrumb,
109
+ BreadcrumbList,
110
+ BreadcrumbItem,
111
+ BreadcrumbLink,
112
+ BreadcrumbPage,
113
+ BreadcrumbSeparator,
114
+ BreadcrumbEllipsis,
115
+ }
@@ -0,0 +1,57 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
+ outline:
16
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost: "hover:bg-accent hover:text-accent-foreground",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ terminal: "bg-green-600 text-black hover:bg-green-500 font-mono",
22
+ },
23
+ size: {
24
+ default: "h-10 px-4 py-2",
25
+ sm: "h-9 rounded-md px-3",
26
+ lg: "h-11 rounded-md px-8",
27
+ icon: "h-10 w-10",
28
+ },
29
+ },
30
+ defaultVariants: {
31
+ variant: "default",
32
+ size: "default",
33
+ },
34
+ }
35
+ )
36
+
37
+ export interface ButtonProps
38
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39
+ VariantProps<typeof buttonVariants> {
40
+ asChild?: boolean
41
+ }
42
+
43
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
44
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
45
+ const Comp = asChild ? Slot : "button"
46
+ return (
47
+ <Comp
48
+ className={cn(buttonVariants({ variant, size, className }))}
49
+ ref={ref}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+ )
55
+ Button.displayName = "Button"
56
+
57
+ export { Button, buttonVariants }
@@ -0,0 +1,79 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "../../lib/utils"
4
+
5
+ const Card = React.forwardRef<
6
+ HTMLDivElement,
7
+ React.HTMLAttributes<HTMLDivElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div
10
+ ref={ref}
11
+ className={cn(
12
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ CardHeader.displayName = "CardHeader"
31
+
32
+ const CardTitle = React.forwardRef<
33
+ HTMLParagraphElement,
34
+ React.HTMLAttributes<HTMLHeadingElement>
35
+ >(({ className, ...props }, ref) => (
36
+ <h3
37
+ ref={ref}
38
+ className={cn(
39
+ "text-2xl font-semibold leading-none tracking-tight",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ ))
45
+ CardTitle.displayName = "CardTitle"
46
+
47
+ const CardDescription = React.forwardRef<
48
+ HTMLParagraphElement,
49
+ React.HTMLAttributes<HTMLParagraphElement>
50
+ >(({ className, ...props }, ref) => (
51
+ <p
52
+ ref={ref}
53
+ className={cn("text-sm text-muted-foreground", className)}
54
+ {...props}
55
+ />
56
+ ))
57
+ CardDescription.displayName = "CardDescription"
58
+
59
+ const CardContent = React.forwardRef<
60
+ HTMLDivElement,
61
+ React.HTMLAttributes<HTMLDivElement>
62
+ >(({ className, ...props }, ref) => (
63
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
64
+ ))
65
+ CardContent.displayName = "CardContent"
66
+
67
+ const CardFooter = React.forwardRef<
68
+ HTMLDivElement,
69
+ React.HTMLAttributes<HTMLDivElement>
70
+ >(({ className, ...props }, ref) => (
71
+ <div
72
+ ref={ref}
73
+ className={cn("flex items-center p-6 pt-0", className)}
74
+ {...props}
75
+ />
76
+ ))
77
+ CardFooter.displayName = "CardFooter"
78
+
79
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }