| 4bb999b | | | 1 | "use client"; |
| 4bb999b | | | 2 | |
| 4bb999b | | | 3 | import { useEffect, useRef, useState } from "react"; |
| 4bb999b | | | 4 | import { GroveLogo } from "@/app/components/grove-logo"; |
| 44863ab | | | 5 | import { useTheme } from "@/lib/theme"; |
| 4bb999b | | | 6 | |
| 4bb999b | | | 7 | export function LandingPage() { |
| 4bb999b | | | 8 | const [islDomain, setIslDomain] = useState(""); |
| 44863ab | | | 9 | const { theme } = useTheme(); |
| 44863ab | | | 10 | const islRef = useRef<HTMLIFrameElement>(null); |
| 4bb999b | | | 11 | |
| 4bb999b | | | 12 | useEffect(() => { |
| 4bb999b | | | 13 | const host = window.location.hostname; |
| 4bb999b | | | 14 | if (host !== "localhost" && !host.match(/^\d/)) { |
| 7e5aa77 | | | 15 | setIslDomain("https://isl.grove.host"); |
| 4bb999b | | | 16 | } |
| 4bb999b | | | 17 | }, []); |
| 4bb999b | | | 18 | |
| 44863ab | | | 19 | // Send theme changes to ISL iframe via postMessage |
| 44863ab | | | 20 | useEffect(() => { |
| 44863ab | | | 21 | if (islRef.current?.contentWindow) { |
| 44863ab | | | 22 | islRef.current.contentWindow.postMessage({ type: "theme", value: theme }, "*"); |
| 44863ab | | | 23 | } |
| 44863ab | | | 24 | }, [theme]); |
| 44863ab | | | 25 | |
| 4bb999b | | | 26 | return ( |
| d6f1620 | | | 27 | <div style={{ display: "flex", flexDirection: "column", height: "100%" }}> |
| 44863ab | | | 28 | {/* Hero + Terminal + Features */} |
| 44863ab | | | 29 | <div style={{ maxWidth: "800px", margin: "0 auto", padding: "60px 24px 0", width: "100%" }}> |
| 44863ab | | | 30 | |
| 4bb999b | | | 31 | {/* Hero */} |
| 4bb999b | | | 32 | <div style={{ textAlign: "center", marginBottom: "64px" }}> |
| 4bb999b | | | 33 | <div style={{ display: "inline-block", marginBottom: "24px" }}> |
| 4bb999b | | | 34 | <GroveLogo size={64} /> |
| 4bb999b | | | 35 | </div> |
| 4bb999b | | | 36 | <h1 |
| 4bb999b | | | 37 | style={{ |
| 4bb999b | | | 38 | fontSize: "2rem", |
| 4bb999b | | | 39 | fontWeight: 400, |
| 4bb999b | | | 40 | color: "var(--text-primary)", |
| 4bb999b | | | 41 | marginBottom: "12px", |
| 4bb999b | | | 42 | }} |
| 4bb999b | | | 43 | > |
| 4bb999b | | | 44 | Source control, self-hosted |
| 4bb999b | | | 45 | </h1> |
| 4bb999b | | | 46 | <p |
| 4bb999b | | | 47 | style={{ |
| 4bb999b | | | 48 | fontSize: "1rem", |
| 4bb999b | | | 49 | color: "var(--text-muted)", |
| 4bb999b | | | 50 | maxWidth: "480px", |
| 4bb999b | | | 51 | margin: "0 auto 32px", |
| 4bb999b | | | 52 | lineHeight: 1.6, |
| 4bb999b | | | 53 | }} |
| 4bb999b | | | 54 | > |
| 4bb999b | | | 55 | Grove is a complete code hosting platform built on Sapling SCM and Mononoke. |
| 4bb999b | | | 56 | Stacked diffs, interactive smartlog, CI pipelines — on your own infrastructure. |
| 4bb999b | | | 57 | </p> |
| 4bb999b | | | 58 | <a |
| 4bb999b | | | 59 | href="/login" |
| 4bb999b | | | 60 | style={{ |
| 4bb999b | | | 61 | display: "inline-block", |
| 4bb999b | | | 62 | padding: "10px 24px", |
| 4bb999b | | | 63 | backgroundColor: "var(--accent)", |
| 4bb999b | | | 64 | color: "var(--accent-text)", |
| 4bb999b | | | 65 | fontSize: "0.875rem", |
| 4bb999b | | | 66 | textDecoration: "none", |
| 4bb999b | | | 67 | }} |
| 4bb999b | | | 68 | > |
| 4bb999b | | | 69 | Get started |
| 4bb999b | | | 70 | </a> |
| 4bb999b | | | 71 | </div> |
| 4bb999b | | | 72 | |
| d8281c0 | | | 73 | {/* Install */} |
| d8281c0 | | | 74 | <div |
| d8281c0 | | | 75 | style={{ |
| d8281c0 | | | 76 | maxWidth: "480px", |
| d8281c0 | | | 77 | margin: "0 auto", |
| d8281c0 | | | 78 | padding: "0 0 64px", |
| d8281c0 | | | 79 | width: "100%", |
| d8281c0 | | | 80 | }} |
| d8281c0 | | | 81 | > |
| d8281c0 | | | 82 | <div |
| d8281c0 | | | 83 | style={{ |
| d8281c0 | | | 84 | backgroundColor: "var(--bg-card)", |
| d8281c0 | | | 85 | border: "1px solid var(--border-subtle)", |
| d8281c0 | | | 86 | padding: "16px 20px", |
| d8281c0 | | | 87 | fontFamily: "var(--font-mono, monospace)", |
| d8281c0 | | | 88 | fontSize: "0.875rem", |
| d8281c0 | | | 89 | color: "var(--text-muted)", |
| d8281c0 | | | 90 | display: "flex", |
| d8281c0 | | | 91 | flexDirection: "column", |
| d8281c0 | | | 92 | gap: "4px", |
| d8281c0 | | | 93 | }} |
| d8281c0 | | | 94 | > |
| d8281c0 | | | 95 | <span><span style={{ color: "var(--text-faint)" }}>$</span> npm i -g @letterpress-labs/grove-scm</span> |
| d8281c0 | | | 96 | <span><span style={{ color: "var(--text-faint)" }}>$</span> grove auth login</span> |
| d8281c0 | | | 97 | <span><span style={{ color: "var(--text-faint)" }}>$</span> grove clone owner/repo</span> |
| d8281c0 | | | 98 | </div> |
| d8281c0 | | | 99 | </div> |
| d8281c0 | | | 100 | |
| 44863ab | | | 101 | </div>{/* end centered wrapper */} |
| 44863ab | | | 102 | |
| 44863ab | | | 103 | {/* ISL — fills remaining viewport */} |
| 44863ab | | | 104 | {islDomain && ( |
| d6f1620 | | | 105 | <section |
| d6f1620 | | | 106 | style={{ |
| d6f1620 | | | 107 | padding: "24px", |
| d6f1620 | | | 108 | }} |
| d6f1620 | | | 109 | > |
| d6f1620 | | | 110 | <iframe |
| d6f1620 | | | 111 | ref={islRef} |
| d6f1620 | | | 112 | src={`${islDomain}?theme=${theme}`} |
| 44863ab | | | 113 | style={{ |
| d6f1620 | | | 114 | width: "100%", |
| d6f1620 | | | 115 | height: "calc(100vh - 3.5rem)", |
| d6f1620 | | | 116 | border: "1px solid var(--border-subtle)", |
| d6f1620 | | | 117 | display: "block", |
| d6f1620 | | | 118 | }} |
| d6f1620 | | | 119 | title="Interactive Smartlog" |
| d6f1620 | | | 120 | onLoad={() => { |
| d6f1620 | | | 121 | islRef.current?.contentWindow?.postMessage({ type: "theme", value: theme }, "*"); |
| 44863ab | | | 122 | }} |
| d6f1620 | | | 123 | /> |
| 44863ab | | | 124 | </section> |
| 44863ab | | | 125 | )} |
| 4bb999b | | | 126 | </div> |
| 4bb999b | | | 127 | ); |
| 4bb999b | | | 128 | } |