Skip to content

Commit c96f770

Browse files
author
Dominique Chuo
committed
website redesign
1 parent 0d151f0 commit c96f770

File tree

24 files changed

+1237
-47
lines changed

24 files changed

+1237
-47
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { useState, useEffect } from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { Navbar, Footer } from '@/components/layout';
4+
5+
const messages = [
6+
'Initializing documentation system...',
7+
'Loading ReifyDB knowledge base...',
8+
'Preparing RQL tutorials...',
9+
'Documentation coming soon...',
10+
];
11+
12+
export function DocsWipTerminal() {
13+
const [messageIndex, setMessageIndex] = useState(0);
14+
const [displayText, setDisplayText] = useState('');
15+
const [charIndex, setCharIndex] = useState(0);
16+
const [showCursor, setShowCursor] = useState(true);
17+
const [phase, setPhase] = useState<'typing' | 'pause' | 'clearing'>('typing');
18+
19+
// Typing effect
20+
useEffect(() => {
21+
const currentMessage = messages[messageIndex];
22+
23+
if (phase === 'typing') {
24+
if (charIndex < currentMessage.length) {
25+
const timeout = setTimeout(() => {
26+
setDisplayText(currentMessage.substring(0, charIndex + 1));
27+
setCharIndex(charIndex + 1);
28+
}, 60);
29+
return () => clearTimeout(timeout);
30+
} else {
31+
// Finished typing
32+
if (messageIndex === messages.length - 1) {
33+
// Stay on the last message
34+
return;
35+
}
36+
const timeout = setTimeout(() => {
37+
setPhase('clearing');
38+
}, 1500);
39+
return () => clearTimeout(timeout);
40+
}
41+
} else if (phase === 'clearing') {
42+
if (charIndex > 0) {
43+
const timeout = setTimeout(() => {
44+
setDisplayText(currentMessage.substring(0, charIndex - 1));
45+
setCharIndex(charIndex - 1);
46+
}, 25);
47+
return () => clearTimeout(timeout);
48+
} else {
49+
setMessageIndex((prev) => prev + 1);
50+
setPhase('typing');
51+
}
52+
}
53+
}, [charIndex, phase, messageIndex]);
54+
55+
// Blinking cursor effect
56+
useEffect(() => {
57+
const cursorInterval = setInterval(() => {
58+
setShowCursor((prev) => !prev);
59+
}, 530);
60+
return () => clearInterval(cursorInterval);
61+
}, []);
62+
63+
return (
64+
<div className="min-h-screen flex flex-col">
65+
<Navbar />
66+
67+
<main className="flex-1 flex items-center justify-center bg-bg-primary px-6 py-16">
68+
<div className="w-full max-w-2xl">
69+
{/* Terminal Window */}
70+
<div className="bg-white border-2 border-border-default rounded-lg overflow-hidden shadow-minimal">
71+
{/* macOS Chrome Header */}
72+
<div className="h-10 sm:h-12 bg-bg-primary border-b-2 border-border-default flex items-center px-3 sm:px-4 gap-2">
73+
<div className="flex gap-1.5">
74+
<div
75+
className="w-3 h-3 rounded-full"
76+
style={{ backgroundColor: 'var(--color-status-error)' }}
77+
/>
78+
<div
79+
className="w-3 h-3 rounded-full"
80+
style={{ backgroundColor: 'var(--color-accent-yellow)' }}
81+
/>
82+
<div
83+
className="w-3 h-3 rounded-full"
84+
style={{ backgroundColor: 'var(--color-feature-green)' }}
85+
/>
86+
</div>
87+
<span className="text-xs font-bold tracking-wide ml-2">reifydb@docs</span>
88+
</div>
89+
90+
{/* Terminal Body */}
91+
<div className="p-6 sm:p-8 font-mono text-sm sm:text-base leading-relaxed bg-white text-text-primary min-h-[200px]">
92+
{/* Previous completed messages */}
93+
{messages.slice(0, messageIndex).map((msg, idx) => (
94+
<div key={idx} className="flex items-center mb-2 text-text-muted">
95+
<span style={{ color: 'var(--color-feature-teal)' }}>$</span>
96+
<span className="ml-2">{msg}</span>
97+
<span className="ml-2" style={{ color: 'var(--color-feature-green)' }}>
98+
OK
99+
</span>
100+
</div>
101+
))}
102+
103+
{/* Current typing line */}
104+
<div className="flex items-center">
105+
<span style={{ color: 'var(--color-feature-teal)' }}>$</span>
106+
<span className="ml-2">{displayText}</span>
107+
<span
108+
className={`ml-0.5 transition-opacity ${showCursor ? 'opacity-100' : 'opacity-0'}`}
109+
>
110+
111+
</span>
112+
</div>
113+
</div>
114+
</div>
115+
116+
{/* Info text below terminal */}
117+
<div className="mt-8 text-center">
118+
<p className="text-text-secondary mb-4">
119+
We're actively writing comprehensive documentation for ReifyDB.
120+
</p>
121+
<p className="text-text-muted text-sm mb-6">
122+
In the meantime, check out our GitHub repository for examples and source code.
123+
</p>
124+
125+
{/* Action buttons */}
126+
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
127+
<a
128+
href="https://github.com/reifydb/reifydb"
129+
target="_blank"
130+
rel="noopener noreferrer"
131+
className="inline-flex items-center gap-2 px-6 py-3 bg-border-default text-white font-bold border-2 border-border-default rounded hover:shadow-minimal-md transition-all"
132+
>
133+
View on GitHub
134+
</a>
135+
<Link
136+
to="/"
137+
className="inline-flex items-center gap-2 px-6 py-3 bg-white text-text-primary font-bold border-2 border-border-default rounded hover:shadow-minimal-md transition-all"
138+
>
139+
Back to Home
140+
</Link>
141+
</div>
142+
</div>
143+
</div>
144+
</main>
145+
146+
<Footer />
147+
</div>
148+
);
149+
}

src/components/demo/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { HeroTerminal } from './hero-terminal';
22
export { Typewriter } from './typewriter';
3+
export { DocsWipTerminal } from './docs-wip-terminal';

src/components/docs-gate.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useIsLocalhost } from '@/hooks';
2+
import { DocsWipTerminal } from '@/components/demo/docs-wip-terminal';
3+
import type {ReactNode} from "react";
4+
5+
interface DocsGateProps {
6+
children: ReactNode;
7+
}
8+
9+
/**
10+
* Conditionally renders documentation content based on environment.
11+
* - Localhost: Shows actual documentation (children)
12+
* - Production: Shows WIP terminal page
13+
*
14+
* To disable this gate and show docs in production, set SHOW_DOCS_IN_PRODUCTION to true.
15+
*/
16+
const SHOW_DOCS_IN_PRODUCTION = false;
17+
18+
export function DocsGate({ children }: DocsGateProps) {
19+
const isLocalhost = useIsLocalhost();
20+
21+
if (isLocalhost || SHOW_DOCS_IN_PRODUCTION) {
22+
return <>{children}</>;
23+
}
24+
25+
return <DocsWipTerminal />;
26+
}

src/components/layout/navbar.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from 'react';
22
import { Link, useLocation } from 'react-router-dom';
3-
import { Button } from '@/components/ui';
3+
import { Button, GitHubStars } from '@/components/ui';
44
import { cn } from '@/lib';
55

66
export function Navbar() {
@@ -70,8 +70,9 @@ export function Navbar() {
7070
</nav>
7171

7272
<div className="flex items-center gap-3">
73-
{/* Desktop CTA */}
74-
<div className="hidden lg:block">
73+
{/* Desktop GitHub + CTA */}
74+
<div className="hidden lg:flex items-center gap-3">
75+
<GitHubStars />
7576
<Button href="/docs" size="sm">
7677
Read Documentation
7778
</Button>

src/components/ui/github-stars.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Github, Star } from 'lucide-react';
2+
import { useGitHubStars } from '@/hooks/use-github-stars';
3+
4+
export function GitHubStars() {
5+
const { formatted, loading } = useGitHubStars();
6+
7+
return (
8+
<a
9+
href="https://github.com/reifydb/reifydb"
10+
target="_blank"
11+
rel="noopener noreferrer"
12+
className="flex items-center gap-2 px-3 py-2 border-2 border-border-default rounded bg-white hover:bg-bg-tertiary transition-colors"
13+
>
14+
<Github size={18} className="text-border-default" />
15+
{!loading && formatted && (
16+
<span className="flex items-center gap-1 text-sm font-bold text-text-secondary">
17+
<Star size={14} className="fill-current" />
18+
{formatted}
19+
</span>
20+
)}
21+
</a>
22+
);
23+
}

src/components/ui/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { Button } from './button'
22
export { CodeViewer } from './code-viewer'
33
export { CtaSection } from './cta-section'
4+
export { GitHubStars } from './github-stars'

src/hooks/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { useGitHubStars } from './use-github-stars';
2+
export { useIsLocalhost } from './use-is-localhost';

src/hooks/use-github-stars.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useState, useEffect } from 'react';
2+
3+
let cachedStars: number | null = null;
4+
let hasFailed = false;
5+
6+
export function useGitHubStars() {
7+
const [stars, setStars] = useState<number | null>(cachedStars);
8+
const [loading, setLoading] = useState(!cachedStars && !hasFailed);
9+
10+
useEffect(() => {
11+
if (cachedStars !== null || hasFailed) return;
12+
13+
const fetchStars = async () => {
14+
try {
15+
const response = await fetch('https://api.github.com/repos/reifydb/reifydb');
16+
if (response.status === 403) {
17+
hasFailed = true;
18+
setLoading(false);
19+
return;
20+
}
21+
if (response.ok) {
22+
const data = await response.json();
23+
cachedStars = data.stargazers_count;
24+
setStars(cachedStars);
25+
}
26+
} catch {
27+
hasFailed = true;
28+
} finally {
29+
setLoading(false);
30+
}
31+
};
32+
33+
fetchStars();
34+
}, []);
35+
36+
const formatted = stars !== null
37+
? stars >= 1000
38+
? (stars / 1000).toFixed(1) + 'k'
39+
: stars.toString()
40+
: null;
41+
42+
return { stars, formatted, loading };
43+
}

src/hooks/use-is-localhost.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useState, useEffect } from 'react';
2+
3+
/**
4+
* Detects if the application is running on localhost.
5+
* Handles localhost, 127.0.0.1, ::1 (IPv6), and *.localhost subdomains.
6+
*/
7+
export function useIsLocalhost(): boolean {
8+
const [isLocalhost, setIsLocalhost] = useState<boolean>(() => {
9+
if (typeof window === 'undefined') return false;
10+
const hostname = window.location.hostname;
11+
return (
12+
hostname === 'localhost' ||
13+
hostname === '127.0.0.1' ||
14+
hostname === '::1' ||
15+
hostname.endsWith('.localhost')
16+
);
17+
});
18+
19+
useEffect(() => {
20+
const hostname = window.location.hostname;
21+
setIsLocalhost(
22+
hostname === 'localhost' ||
23+
hostname === '127.0.0.1' ||
24+
hostname === '::1' ||
25+
hostname.endsWith('.localhost')
26+
);
27+
}, []);
28+
29+
return isLocalhost;
30+
}

0 commit comments

Comments
 (0)