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