visionos-monorepo 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.
- package/.claude/worktrees/competent-burnell-8d1330/README.md +138 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/package.json +35 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/scripts/copy-web-assets.mjs +12 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/logout.ts +12 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/open.ts +19 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/start.ts +97 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/status.ts +23 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/userinfo.ts +47 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/index.ts +23 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/auth.ts +84 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/browser.ts +37 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/localState.ts +80 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/runtime.ts +203 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/runtime/index.ts +36 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/src/types/inquirer.d.ts +9 -0
- package/.claude/worktrees/competent-burnell-8d1330/cli/tsconfig.json +19 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/index.html +15 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/package.json +27 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/postcss.config.cjs +7 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/App.tsx +57 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/components/CliAuthPage.tsx +385 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/components/ManifestoPage.tsx +946 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/components/TrackCard.tsx +19 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/lib/api.ts +58 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/main.tsx +11 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/styles/index.css +33 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/src/styles/manifesto.css +1398 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/tailwind.config.ts +36 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/tsconfig.json +25 -0
- package/.claude/worktrees/competent-burnell-8d1330/client/vite.config.ts +20 -0
- package/.claude/worktrees/competent-burnell-8d1330/package-lock.json +5278 -0
- package/.claude/worktrees/competent-burnell-8d1330/package.json +24 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/package.json +25 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/src/app.ts +71 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/src/config/env.ts +14 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/src/features/auth/sessionStore.ts +74 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/src/index.ts +8 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/src/routes/auth.ts +112 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/src/routes/health.ts +14 -0
- package/.claude/worktrees/competent-burnell-8d1330/server/tsconfig.json +19 -0
- package/.claude/worktrees/competent-burnell-8d1330/shared/package.json +24 -0
- package/.claude/worktrees/competent-burnell-8d1330/shared/src/index.ts +91 -0
- package/.claude/worktrees/competent-burnell-8d1330/shared/tsconfig.json +16 -0
- package/.claude/worktrees/competent-burnell-8d1330/tsconfig.base.json +12 -0
- package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/index.html +392 -0
- package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/script.js +146 -0
- package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/styles.css +1082 -0
- package/.claude/worktrees/vigilant-napier-0de76f/README.md +138 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/package.json +35 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/scripts/copy-web-assets.mjs +12 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/logout.ts +12 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/open.ts +19 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/start.ts +97 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/status.ts +23 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/userinfo.ts +47 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/index.ts +23 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/auth.ts +84 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/browser.ts +37 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/localState.ts +80 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/runtime.ts +203 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/runtime/index.ts +36 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/src/types/inquirer.d.ts +9 -0
- package/.claude/worktrees/vigilant-napier-0de76f/cli/tsconfig.json +19 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/index.html +15 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/package.json +27 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/postcss.config.cjs +7 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/App.tsx +57 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/CliAuthPage.tsx +385 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/ManifestoPage.tsx +946 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/TrackCard.tsx +19 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/lib/api.ts +58 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/main.tsx +11 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/styles/index.css +33 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/src/styles/manifesto.css +1398 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/tailwind.config.ts +36 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/tsconfig.json +25 -0
- package/.claude/worktrees/vigilant-napier-0de76f/client/vite.config.ts +20 -0
- package/.claude/worktrees/vigilant-napier-0de76f/package-lock.json +5278 -0
- package/.claude/worktrees/vigilant-napier-0de76f/package.json +24 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/package.json +25 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/src/app.ts +71 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/src/config/env.ts +14 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/src/features/auth/sessionStore.ts +74 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/src/index.ts +8 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/src/routes/auth.ts +112 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/src/routes/health.ts +14 -0
- package/.claude/worktrees/vigilant-napier-0de76f/server/tsconfig.json +19 -0
- package/.claude/worktrees/vigilant-napier-0de76f/shared/package.json +24 -0
- package/.claude/worktrees/vigilant-napier-0de76f/shared/src/index.ts +91 -0
- package/.claude/worktrees/vigilant-napier-0de76f/shared/tsconfig.json +16 -0
- package/.claude/worktrees/vigilant-napier-0de76f/tsconfig.base.json +12 -0
- package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/index.html +392 -0
- package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/script.js +146 -0
- package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/styles.css +1082 -0
- package/.github/workflows/publish.yml +30 -0
- package/README.md +175 -0
- package/cli/README.md +165 -0
- package/cli/package.json +36 -0
- package/cli/scripts/copy-web-assets.mjs +12 -0
- package/cli/src/commands/lessons.ts +68 -0
- package/cli/src/commands/login.ts +46 -0
- package/cli/src/commands/logout.ts +12 -0
- package/cli/src/commands/open.ts +29 -0
- package/cli/src/commands/start.ts +146 -0
- package/cli/src/commands/status.ts +28 -0
- package/cli/src/commands/userinfo.ts +59 -0
- package/cli/src/index.ts +109 -0
- package/cli/src/lib/auth.ts +84 -0
- package/cli/src/lib/browser.ts +37 -0
- package/cli/src/lib/content.ts +57 -0
- package/cli/src/lib/lessonPrinter.ts +38 -0
- package/cli/src/lib/lessonRunner.ts +381 -0
- package/cli/src/lib/localState.ts +114 -0
- package/cli/src/lib/loginFlow.ts +74 -0
- package/cli/src/lib/progress.ts +94 -0
- package/cli/src/lib/runtime.ts +220 -0
- package/cli/src/lib/validator.ts +401 -0
- package/cli/src/runtime/index.ts +108 -0
- package/cli/src/types/inquirer.d.ts +9 -0
- package/cli/tsconfig.json +19 -0
- package/client/index.html +15 -0
- package/client/package.json +27 -0
- package/client/postcss.config.cjs +7 -0
- package/client/src/App.tsx +102 -0
- package/client/src/components/AccountPage.tsx +79 -0
- package/client/src/components/AuthPanel.tsx +312 -0
- package/client/src/components/CliAuthPage.tsx +367 -0
- package/client/src/components/CreatorPortal.tsx +885 -0
- package/client/src/components/ErrorBoundary.tsx +92 -0
- package/client/src/components/ManifestoPage.tsx +1126 -0
- package/client/src/components/TrackCard.tsx +19 -0
- package/client/src/lib/api.ts +215 -0
- package/client/src/main.tsx +14 -0
- package/client/src/styles/index.css +33 -0
- package/client/src/styles/manifesto.css +1828 -0
- package/client/tailwind.config.ts +36 -0
- package/client/tsconfig.json +25 -0
- package/client/vercel.json +8 -0
- package/client/vite.config.ts +33 -0
- package/package.json +27 -0
- package/server/package.json +26 -0
- package/server/src/app.ts +132 -0
- package/server/src/config/env.ts +135 -0
- package/server/src/features/accounts/accountStore.ts +359 -0
- package/server/src/features/accounts/contentStore.ts +264 -0
- package/server/src/features/accounts/password.ts +26 -0
- package/server/src/features/auth/sessionStore.ts +79 -0
- package/server/src/index.ts +8 -0
- package/server/src/routes/auth.ts +328 -0
- package/server/src/routes/content.ts +174 -0
- package/server/src/routes/health.ts +14 -0
- package/server/src/routes/progress.ts +105 -0
- package/server/tsconfig.json +19 -0
- package/shared/package.json +24 -0
- package/shared/src/index.ts +455 -0
- package/shared/tsconfig.json +16 -0
- package/tsconfig.base.json +12 -0
- package/visionos-manifesto/index.html +392 -0
- package/visionos-manifesto/script.js +146 -0
- package/visionos-manifesto/styles.css +1082 -0
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, useCallback } from "react";
|
|
2
|
+
import {
|
|
3
|
+
LEARNING_TRACKS,
|
|
4
|
+
type VisionOsUserProfile,
|
|
5
|
+
} from "../../../shared/src/index.js";
|
|
6
|
+
|
|
7
|
+
/* ────────────────────────────────────────────────────────────────
|
|
8
|
+
Sub-components
|
|
9
|
+
──────────────────────────────────────────────────────────────── */
|
|
10
|
+
|
|
11
|
+
function ScanlineDivider({
|
|
12
|
+
variant = "both",
|
|
13
|
+
height = 120,
|
|
14
|
+
style,
|
|
15
|
+
}: {
|
|
16
|
+
variant?: "both" | "fade-top" | "fade-bottom";
|
|
17
|
+
height?: number;
|
|
18
|
+
style?: React.CSSProperties;
|
|
19
|
+
}) {
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={`scanline-divider scanline-divider--${variant} scanline-reveal`}
|
|
23
|
+
style={{ height, ...style }}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function TerminalWindow({
|
|
29
|
+
children,
|
|
30
|
+
style,
|
|
31
|
+
}: {
|
|
32
|
+
children: React.ReactNode;
|
|
33
|
+
style?: React.CSSProperties;
|
|
34
|
+
}) {
|
|
35
|
+
return (
|
|
36
|
+
<div className="terminal-window" style={style}>
|
|
37
|
+
<div className="terminal-titlebar">
|
|
38
|
+
<span className="terminal-dot terminal-dot--red" />
|
|
39
|
+
<span className="terminal-dot terminal-dot--yellow" />
|
|
40
|
+
<span className="terminal-dot terminal-dot--green" />
|
|
41
|
+
</div>
|
|
42
|
+
<div className="terminal-body">{children}</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function TerminalLine({
|
|
48
|
+
type,
|
|
49
|
+
children,
|
|
50
|
+
}: {
|
|
51
|
+
type?: "prompt" | "output" | "success";
|
|
52
|
+
children: React.ReactNode;
|
|
53
|
+
}) {
|
|
54
|
+
return (
|
|
55
|
+
<span className="terminal-line">
|
|
56
|
+
{type === "prompt" ? (
|
|
57
|
+
<>
|
|
58
|
+
<span className="t-prompt">~$</span>{" "}
|
|
59
|
+
<span className="command">{children}</span>
|
|
60
|
+
</>
|
|
61
|
+
) : type === "output" ? (
|
|
62
|
+
<span className="output">{children}</span>
|
|
63
|
+
) : type === "success" ? (
|
|
64
|
+
<span className="success">{children}</span>
|
|
65
|
+
) : (
|
|
66
|
+
children
|
|
67
|
+
)}
|
|
68
|
+
</span>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Helper: wrap each word in a <span className="word"> for stagger animation */
|
|
73
|
+
function WordStagger({
|
|
74
|
+
text,
|
|
75
|
+
className = "",
|
|
76
|
+
tag: Tag = "p",
|
|
77
|
+
style,
|
|
78
|
+
}: {
|
|
79
|
+
text: string;
|
|
80
|
+
className?: string;
|
|
81
|
+
tag?: keyof JSX.IntrinsicElements;
|
|
82
|
+
style?: React.CSSProperties;
|
|
83
|
+
}) {
|
|
84
|
+
const TagComponent = Tag as any;
|
|
85
|
+
return (
|
|
86
|
+
<TagComponent className={`word-stagger ${className}`} style={style}>
|
|
87
|
+
{text.split(" ").map((word, i) => (
|
|
88
|
+
<span className="word" key={i}>
|
|
89
|
+
{word}
|
|
90
|
+
</span>
|
|
91
|
+
))}
|
|
92
|
+
</TagComponent>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ────────────────────────────────────────────────────────────────
|
|
97
|
+
ManifestoPage
|
|
98
|
+
──────────────────────────────────────────────────────────────── */
|
|
99
|
+
|
|
100
|
+
interface ManifestoPageProps {
|
|
101
|
+
user: VisionOsUserProfile | null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function ManifestoPage({ user }: ManifestoPageProps) {
|
|
105
|
+
const screenRef = useRef<HTMLDivElement>(null);
|
|
106
|
+
const [scrolled, setScrolled] = useState(false);
|
|
107
|
+
const [copied, setCopied] = useState(false);
|
|
108
|
+
const [scrollHidden, setScrollHidden] = useState(false);
|
|
109
|
+
|
|
110
|
+
// ── Smooth scroll behavior ────────────────────────────────────
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
document.documentElement.style.scrollBehavior = "smooth";
|
|
113
|
+
return () => {
|
|
114
|
+
document.documentElement.style.scrollBehavior = "";
|
|
115
|
+
};
|
|
116
|
+
}, []);
|
|
117
|
+
|
|
118
|
+
// ── Scroll Effects ────────────────────────────────────────────
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const handleScroll = () => {
|
|
121
|
+
setScrolled(window.scrollY > 50);
|
|
122
|
+
if (window.scrollY > 100) setScrollHidden(true);
|
|
123
|
+
};
|
|
124
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
125
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
// ── IntersectionObserver for ALL reveal animations ────────────
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
const els = screenRef.current?.querySelectorAll(
|
|
131
|
+
".reveal, .reveal-left, .reveal-right, .reveal-scale, .reveal-blur, .word-stagger, .hr-reveal, .scanline-reveal, .animated-separator"
|
|
132
|
+
);
|
|
133
|
+
if (!els) return;
|
|
134
|
+
|
|
135
|
+
const obs = new IntersectionObserver(
|
|
136
|
+
(entries) => {
|
|
137
|
+
entries.forEach((e) => {
|
|
138
|
+
if (e.isIntersecting) {
|
|
139
|
+
e.target.classList.add("visible");
|
|
140
|
+
obs.unobserve(e.target);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
|
|
145
|
+
);
|
|
146
|
+
els.forEach((el) => obs.observe(el));
|
|
147
|
+
|
|
148
|
+
return () => obs.disconnect();
|
|
149
|
+
}, []);
|
|
150
|
+
|
|
151
|
+
// ── Terminal line re-trigger on scroll ────────────────────────
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
const terminals = screenRef.current?.querySelectorAll(".terminal-window");
|
|
154
|
+
if (!terminals) return;
|
|
155
|
+
|
|
156
|
+
const obs = new IntersectionObserver(
|
|
157
|
+
(entries) => {
|
|
158
|
+
entries.forEach((e) => {
|
|
159
|
+
if (e.isIntersecting) {
|
|
160
|
+
const lines = e.target.querySelectorAll(".terminal-line");
|
|
161
|
+
lines.forEach((line) => {
|
|
162
|
+
(line as HTMLElement).style.animation = "none";
|
|
163
|
+
void (line as HTMLElement).offsetHeight;
|
|
164
|
+
(line as HTMLElement).style.animation = "";
|
|
165
|
+
});
|
|
166
|
+
obs.unobserve(e.target);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
{ threshold: 0.3 }
|
|
171
|
+
);
|
|
172
|
+
terminals.forEach((el) => obs.observe(el));
|
|
173
|
+
return () => obs.disconnect();
|
|
174
|
+
}, []);
|
|
175
|
+
|
|
176
|
+
// ── Copy handler ──────────────────────────────────────────────
|
|
177
|
+
const handleCopy = useCallback(async (text: string) => {
|
|
178
|
+
try {
|
|
179
|
+
await navigator.clipboard.writeText(text);
|
|
180
|
+
} catch {
|
|
181
|
+
const ta = document.createElement("textarea");
|
|
182
|
+
ta.value = text;
|
|
183
|
+
ta.style.position = "fixed";
|
|
184
|
+
ta.style.opacity = "0";
|
|
185
|
+
document.body.appendChild(ta);
|
|
186
|
+
ta.select();
|
|
187
|
+
document.execCommand("copy");
|
|
188
|
+
document.body.removeChild(ta);
|
|
189
|
+
}
|
|
190
|
+
setCopied(true);
|
|
191
|
+
setTimeout(() => setCopied(false), 1800);
|
|
192
|
+
}, []);
|
|
193
|
+
|
|
194
|
+
const today = new Date();
|
|
195
|
+
const formattedDate = `${String(today.getMonth() + 1).padStart(2, "0")}/${String(today.getDate()).padStart(2, "0")}/${String(today.getFullYear()).slice(-2)}`;
|
|
196
|
+
|
|
197
|
+
const trackIcons: Record<string, string> = {
|
|
198
|
+
git: "G",
|
|
199
|
+
linux: "L",
|
|
200
|
+
"react-cli": "R",
|
|
201
|
+
"os-commands": "OS",
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<div className="monitor-bezel">
|
|
206
|
+
<div className="monitor-screen" ref={screenRef}>
|
|
207
|
+
{/* ── STICKY HEADER ─────────────────────────────── */}
|
|
208
|
+
<header className={`m-site-header ${scrolled ? "scrolled" : ""}`}>
|
|
209
|
+
<div className="header-left">
|
|
210
|
+
<span className="header-brand">VisionOS</span>
|
|
211
|
+
<span className="header-meta">v0.1.0 — CLI-First Learning</span>
|
|
212
|
+
</div>
|
|
213
|
+
<nav className="header-nav">
|
|
214
|
+
<a href="#tracks">Tracks</a>
|
|
215
|
+
<a href="#commands">Commands</a>
|
|
216
|
+
<a
|
|
217
|
+
href="https://github.com"
|
|
218
|
+
target="_blank"
|
|
219
|
+
rel="noopener noreferrer"
|
|
220
|
+
>
|
|
221
|
+
GitHub <span className="arrow">↗</span>
|
|
222
|
+
</a>
|
|
223
|
+
</nav>
|
|
224
|
+
</header>
|
|
225
|
+
|
|
226
|
+
{/* ── LOGGED-IN TERMINAL SECTION (Image 1) ─────── */}
|
|
227
|
+
{user && (
|
|
228
|
+
<section className="logged-in-terminal-section">
|
|
229
|
+
<div className="logged-in-header">
|
|
230
|
+
<div className="logged-in-header-left">
|
|
231
|
+
<span className="logged-in-header-brand">VisionOS, Inc.</span>
|
|
232
|
+
<span className="logged-in-header-copy">
|
|
233
|
+
Copyright 2025
|
|
234
|
+
<br />
|
|
235
|
+
All Rights Reserved
|
|
236
|
+
</span>
|
|
237
|
+
</div>
|
|
238
|
+
<div className="logged-in-header-nav">
|
|
239
|
+
<a href="#tracks">
|
|
240
|
+
tracks <span className="arrow">↗</span>
|
|
241
|
+
</a>
|
|
242
|
+
<a href="#commands">
|
|
243
|
+
commands <span className="arrow">↗</span>
|
|
244
|
+
</a>
|
|
245
|
+
<a href="#hero">@visionos</a>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<div className="logged-in-quote reveal">
|
|
250
|
+
CD, RM, PING: COMMANDS WE MEMORIZED
|
|
251
|
+
<br />
|
|
252
|
+
TO TRANSLATE OUR DESIRES INTO ACTIONS...
|
|
253
|
+
<br />
|
|
254
|
+
IF WE CHOSE TO LEARN THEM.
|
|
255
|
+
<span className="typewriter-cursor typewriter-cursor--dark" />
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div className="logged-in-meta reveal delay-1">
|
|
259
|
+
Last login: {today.toDateString()}, {today.toLocaleTimeString()}
|
|
260
|
+
<br />
|
|
261
|
+
Welcome back, {user.name} · {user.email}
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
{/* ── User Log Details Panel ────────────────── */}
|
|
265
|
+
<div className="user-log-details reveal delay-2">
|
|
266
|
+
<div className="user-log-grid">
|
|
267
|
+
<div className="user-log-field">
|
|
268
|
+
<span className="user-log-label">Session ID</span>
|
|
269
|
+
<span className="user-log-value user-log-value--highlight">
|
|
270
|
+
{user.sessionId ? user.sessionId.slice(0, 12) + "..." : "ACTIVE"}
|
|
271
|
+
</span>
|
|
272
|
+
</div>
|
|
273
|
+
<div className="user-log-field">
|
|
274
|
+
<span className="user-log-label">User</span>
|
|
275
|
+
<span className="user-log-value">{user.name}</span>
|
|
276
|
+
</div>
|
|
277
|
+
<div className="user-log-field">
|
|
278
|
+
<span className="user-log-label">Email</span>
|
|
279
|
+
<span className="user-log-value">{user.email}</span>
|
|
280
|
+
</div>
|
|
281
|
+
<div className="user-log-field">
|
|
282
|
+
<span className="user-log-label">Status</span>
|
|
283
|
+
<span className="user-log-value user-log-value--green">
|
|
284
|
+
● ONLINE
|
|
285
|
+
</span>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<div className="user-log-divider" />
|
|
289
|
+
|
|
290
|
+
<div className="user-log-field">
|
|
291
|
+
<span className="user-log-label">Runtime</span>
|
|
292
|
+
<span className="user-log-value">Node.js v20+</span>
|
|
293
|
+
</div>
|
|
294
|
+
<div className="user-log-field">
|
|
295
|
+
<span className="user-log-label">Port</span>
|
|
296
|
+
<span className="user-log-value user-log-value--highlight">
|
|
297
|
+
:5173
|
|
298
|
+
</span>
|
|
299
|
+
</div>
|
|
300
|
+
<div className="user-log-field">
|
|
301
|
+
<span className="user-log-label">Tracks Available</span>
|
|
302
|
+
<span className="user-log-value">{LEARNING_TRACKS.length}</span>
|
|
303
|
+
</div>
|
|
304
|
+
<div className="user-log-field">
|
|
305
|
+
<span className="user-log-label">Login Time</span>
|
|
306
|
+
<span className="user-log-value">
|
|
307
|
+
{today.toLocaleTimeString()}
|
|
308
|
+
</span>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</section>
|
|
313
|
+
)}
|
|
314
|
+
|
|
315
|
+
{/* ── HERO ──────────────────────────────────────── */}
|
|
316
|
+
<section className="m-section m-section--hero" id="hero">
|
|
317
|
+
<div className="hero-content">
|
|
318
|
+
<p className="text-label reveal">
|
|
319
|
+
Once upon a time, we typed a command in
|
|
320
|
+
</p>
|
|
321
|
+
<div style={{ marginTop: 28, overflow: "hidden" }}>
|
|
322
|
+
<span className="scanline-text glitch-text reveal delay-1">
|
|
323
|
+
TERMIN<br className="mobile-break" />AL.
|
|
324
|
+
</span>
|
|
325
|
+
</div>
|
|
326
|
+
<p
|
|
327
|
+
className="text-hero-lead reveal-blur delay-3"
|
|
328
|
+
style={{ marginTop: 40 }}
|
|
329
|
+
>
|
|
330
|
+
The command line was the first interface.
|
|
331
|
+
<br />
|
|
332
|
+
It's time we <em>learned</em> it properly.
|
|
333
|
+
<span className="typewriter-cursor" />
|
|
334
|
+
</p>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
<div className="hero-prompt-container reveal delay-4">
|
|
338
|
+
<div className="hero-prompt">
|
|
339
|
+
<span>▸</span>
|
|
340
|
+
<span className="hero-prompt-key">enter</span>
|
|
341
|
+
<span>to continue</span>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
{/* Scroll Indicator */}
|
|
346
|
+
<div
|
|
347
|
+
className={`scroll-indicator ${scrollHidden ? "hidden" : ""}`}
|
|
348
|
+
>
|
|
349
|
+
<svg
|
|
350
|
+
viewBox="0 0 24 24"
|
|
351
|
+
fill="none"
|
|
352
|
+
stroke="currentColor"
|
|
353
|
+
strokeWidth="1.5"
|
|
354
|
+
strokeLinecap="round"
|
|
355
|
+
>
|
|
356
|
+
<rect x="7" y="2" width="10" height="16" rx="5" />
|
|
357
|
+
<line x1="12" y1="6" x2="12" y2="9" />
|
|
358
|
+
</svg>
|
|
359
|
+
<span className="arrow">↓</span>
|
|
360
|
+
</div>
|
|
361
|
+
</section>
|
|
362
|
+
|
|
363
|
+
{/* ── SCANLINE DIVIDER ──────────────────────────── */}
|
|
364
|
+
<ScanlineDivider />
|
|
365
|
+
|
|
366
|
+
{/* ── ANIMATED SEPARATOR ─────────────────────────── */}
|
|
367
|
+
<div className="animated-separator" />
|
|
368
|
+
|
|
369
|
+
{/* ── NARRATIVE 1 — THE ORIGIN ──────────────────── */}
|
|
370
|
+
<section className="m-section" id="origin">
|
|
371
|
+
<div className="split-layout split-layout--text-left split-layout--center">
|
|
372
|
+
<div className="reveal-left">
|
|
373
|
+
<WordStagger
|
|
374
|
+
text="The command line was the original interface."
|
|
375
|
+
className="text-narrative"
|
|
376
|
+
tag="p"
|
|
377
|
+
/>
|
|
378
|
+
<p
|
|
379
|
+
className="text-narrative text-narrative--muted reveal-blur delay-2"
|
|
380
|
+
style={{ marginTop: 20 }}
|
|
381
|
+
>
|
|
382
|
+
Raw, powerful, unfiltered. Before every GUI, before every app —
|
|
383
|
+
there was a blinking cursor and an empty prompt.
|
|
384
|
+
</p>
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<div className="reveal-right delay-2">
|
|
388
|
+
<TerminalWindow>
|
|
389
|
+
<TerminalLine type="prompt">whoami</TerminalLine>
|
|
390
|
+
<TerminalLine type="output">learner</TerminalLine>
|
|
391
|
+
<TerminalLine> </TerminalLine>
|
|
392
|
+
<TerminalLine type="prompt">
|
|
393
|
+
echo "hello, terminal"
|
|
394
|
+
</TerminalLine>
|
|
395
|
+
<TerminalLine type="success">hello, terminal</TerminalLine>
|
|
396
|
+
<TerminalLine> </TerminalLine>
|
|
397
|
+
<TerminalLine type="prompt">█</TerminalLine>
|
|
398
|
+
</TerminalWindow>
|
|
399
|
+
<div className="figure-caption" style={{ marginTop: 20 }}>
|
|
400
|
+
<span className="figure-label">Fig 1</span>
|
|
401
|
+
<span className="figure-desc">
|
|
402
|
+
The terminal — where computing began. A blinking cursor,
|
|
403
|
+
infinite possibility.
|
|
404
|
+
</span>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
</section>
|
|
409
|
+
|
|
410
|
+
{/* ── HR animated divider ────────────────────────── */}
|
|
411
|
+
<hr className="hr-reveal" />
|
|
412
|
+
|
|
413
|
+
{/* ── TRANSITION — GIANT TEXT ───────────────────── */}
|
|
414
|
+
<section
|
|
415
|
+
className="m-section"
|
|
416
|
+
style={{ padding: "var(--space-xxl) 0" }}
|
|
417
|
+
>
|
|
418
|
+
<ScanlineDivider
|
|
419
|
+
variant="fade-bottom"
|
|
420
|
+
height={60}
|
|
421
|
+
style={{ marginBottom: 32 }}
|
|
422
|
+
/>
|
|
423
|
+
<div className="scanline-band reveal-scale">
|
|
424
|
+
<span
|
|
425
|
+
className="scanline-text glitch-text"
|
|
426
|
+
style={{ textAlign: "center", display: "block" }}
|
|
427
|
+
>
|
|
428
|
+
PRACTICE
|
|
429
|
+
</span>
|
|
430
|
+
</div>
|
|
431
|
+
<ScanlineDivider
|
|
432
|
+
variant="fade-top"
|
|
433
|
+
height={60}
|
|
434
|
+
style={{ marginTop: 32 }}
|
|
435
|
+
/>
|
|
436
|
+
<div
|
|
437
|
+
style={{
|
|
438
|
+
textAlign: "center",
|
|
439
|
+
padding: "0 60px",
|
|
440
|
+
marginTop: "var(--space-lg)",
|
|
441
|
+
}}
|
|
442
|
+
>
|
|
443
|
+
<WordStagger
|
|
444
|
+
text="Now, we believe it's time for a future where the terminal becomes your classroom."
|
|
445
|
+
className="text-narrative"
|
|
446
|
+
tag="p"
|
|
447
|
+
style={{ maxWidth: 600, margin: "0 auto" }}
|
|
448
|
+
/>
|
|
449
|
+
</div>
|
|
450
|
+
</section>
|
|
451
|
+
|
|
452
|
+
{/* ── ANIMATED SEPARATOR ─────────────────────────── */}
|
|
453
|
+
<div className="animated-separator" />
|
|
454
|
+
|
|
455
|
+
{/* ── MEET VISIONOS ────────────────────────────── */}
|
|
456
|
+
<section className="m-section" id="about">
|
|
457
|
+
<div className="reveal">
|
|
458
|
+
<p className="text-label">Introducing</p>
|
|
459
|
+
<h2 className="text-section-title" style={{ marginTop: 8 }}>
|
|
460
|
+
Meet VisionOS
|
|
461
|
+
</h2>
|
|
462
|
+
<p className="text-section-subtitle" style={{ marginTop: 4 }}>
|
|
463
|
+
A CLI-first learning platform.
|
|
464
|
+
</p>
|
|
465
|
+
</div>
|
|
466
|
+
|
|
467
|
+
<div style={{ marginTop: "var(--space-lg)" }}>
|
|
468
|
+
<div className="split-layout split-layout--text-left split-layout--center">
|
|
469
|
+
<div className="reveal-left delay-1">
|
|
470
|
+
<WordStagger
|
|
471
|
+
text="The best way to learn terminal commands is by doing — not reading."
|
|
472
|
+
className="text-narrative"
|
|
473
|
+
tag="p"
|
|
474
|
+
/>
|
|
475
|
+
<p className="m-text-body reveal-blur delay-3" style={{ marginTop: 24 }}>
|
|
476
|
+
VisionOS is a guided, interactive learning environment that
|
|
477
|
+
lives right inside your terminal. Install it, pick a track,
|
|
478
|
+
and start typing real commands — with real-time validation,
|
|
479
|
+
progress tracking, and structured lessons.
|
|
480
|
+
</p>
|
|
481
|
+
</div>
|
|
482
|
+
<div className="reveal-right delay-2">
|
|
483
|
+
<TerminalWindow>
|
|
484
|
+
<TerminalLine type="prompt">
|
|
485
|
+
npm install -g visionos-cli
|
|
486
|
+
</TerminalLine>
|
|
487
|
+
<TerminalLine type="output">
|
|
488
|
+
+ visionos-cli@0.1.0 installed
|
|
489
|
+
</TerminalLine>
|
|
490
|
+
<TerminalLine> </TerminalLine>
|
|
491
|
+
<TerminalLine type="prompt">visionos start</TerminalLine>
|
|
492
|
+
<TerminalLine type="success">
|
|
493
|
+
✓ VisionOS runtime started
|
|
494
|
+
</TerminalLine>
|
|
495
|
+
<TerminalLine type="success">
|
|
496
|
+
✓ Server running at 127.0.0.1:5173
|
|
497
|
+
</TerminalLine>
|
|
498
|
+
<TerminalLine type="output">
|
|
499
|
+
→ Opening dashboard...
|
|
500
|
+
</TerminalLine>
|
|
501
|
+
</TerminalWindow>
|
|
502
|
+
<div className="figure-caption" style={{ marginTop: 20 }}>
|
|
503
|
+
<span className="figure-label">Fig 3</span>
|
|
504
|
+
<span className="figure-desc">
|
|
505
|
+
Two commands. That's all it takes to enter a guided terminal
|
|
506
|
+
classroom.
|
|
507
|
+
</span>
|
|
508
|
+
</div>
|
|
509
|
+
</div>
|
|
510
|
+
</div>
|
|
511
|
+
</div>
|
|
512
|
+
</section>
|
|
513
|
+
|
|
514
|
+
{/* ── SCANLINE DIVIDER ──────────────────────────── */}
|
|
515
|
+
<ScanlineDivider height={60} />
|
|
516
|
+
|
|
517
|
+
{/* ── HOW IT WORKS ─────────────────────────────── */}
|
|
518
|
+
<section className="m-section" id="how">
|
|
519
|
+
<div className="reveal">
|
|
520
|
+
<p className="text-label">How it works</p>
|
|
521
|
+
<h2 className="text-section-title" style={{ marginTop: 8 }}>
|
|
522
|
+
Three steps to mastery
|
|
523
|
+
</h2>
|
|
524
|
+
</div>
|
|
525
|
+
<div className="steps-grid">
|
|
526
|
+
{[
|
|
527
|
+
{
|
|
528
|
+
num: "01",
|
|
529
|
+
title: "Install",
|
|
530
|
+
desc: "Install VisionOS globally with a single npm command. No config, no fuss.",
|
|
531
|
+
code: "npm i -g visionos-cli",
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
num: "02",
|
|
535
|
+
title: "Start",
|
|
536
|
+
desc: "Launch the runtime. VisionOS spins up a local server and opens your dashboard.",
|
|
537
|
+
code: "visionos start",
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
num: "03",
|
|
541
|
+
title: "Learn",
|
|
542
|
+
desc: "Pick a learning track, follow guided lessons, and practice real commands with instant feedback.",
|
|
543
|
+
code: "visionos open",
|
|
544
|
+
},
|
|
545
|
+
].map((step, i) => (
|
|
546
|
+
<div key={step.num} className={`step-card magnetic-hover reveal delay-${i + 1}`}>
|
|
547
|
+
<span className="step-number">{step.num} —</span>
|
|
548
|
+
<h3 className="step-title">{step.title}</h3>
|
|
549
|
+
<p className="m-text-body" style={{ maxWidth: "none" }}>
|
|
550
|
+
{step.desc}
|
|
551
|
+
</p>
|
|
552
|
+
<code className="step-code">{step.code}</code>
|
|
553
|
+
</div>
|
|
554
|
+
))}
|
|
555
|
+
</div>
|
|
556
|
+
</section>
|
|
557
|
+
|
|
558
|
+
{/* ── HR animated divider ────────────────────────── */}
|
|
559
|
+
<hr className="hr-reveal" />
|
|
560
|
+
|
|
561
|
+
{/* ── LEARNING TRACKS ──────────────────────────── */}
|
|
562
|
+
<section className="m-section" id="tracks">
|
|
563
|
+
<div className="reveal">
|
|
564
|
+
<p className="text-label">Learning Tracks</p>
|
|
565
|
+
<h2 className="text-section-title" style={{ marginTop: 8 }}>
|
|
566
|
+
Pick your path
|
|
567
|
+
</h2>
|
|
568
|
+
<p className="m-text-body reveal-blur delay-1" style={{ marginTop: 16 }}>
|
|
569
|
+
Four structured tracks, each with guided lessons that build on
|
|
570
|
+
each other. Start from basics, reach mastery.
|
|
571
|
+
</p>
|
|
572
|
+
</div>
|
|
573
|
+
<div className="m-tracks-grid">
|
|
574
|
+
{LEARNING_TRACKS.map((track, i) => (
|
|
575
|
+
<div
|
|
576
|
+
key={track.id}
|
|
577
|
+
className={`m-track-card magnetic-hover reveal delay-${i + 1}`}
|
|
578
|
+
>
|
|
579
|
+
<div className="m-track-card-icon">
|
|
580
|
+
{trackIcons[track.id] ?? track.label[0]}
|
|
581
|
+
</div>
|
|
582
|
+
<h3 className="m-track-card-title">{track.label}</h3>
|
|
583
|
+
<p className="m-track-card-desc">{track.description}</p>
|
|
584
|
+
</div>
|
|
585
|
+
))}
|
|
586
|
+
</div>
|
|
587
|
+
</section>
|
|
588
|
+
|
|
589
|
+
{/* ── ANIMATED SEPARATOR ─────────────────────────── */}
|
|
590
|
+
<div className="animated-separator" />
|
|
591
|
+
|
|
592
|
+
{/* ── SCANLINE DIVIDER ──────────────────────────── */}
|
|
593
|
+
<ScanlineDivider height={60} />
|
|
594
|
+
|
|
595
|
+
{/* ── CLI COMMANDS REFERENCE ────────────────────── */}
|
|
596
|
+
<section className="m-section" id="commands">
|
|
597
|
+
<div className="split-layout split-layout--text-left">
|
|
598
|
+
<div>
|
|
599
|
+
<div className="reveal">
|
|
600
|
+
<p className="text-label">CLI Reference</p>
|
|
601
|
+
<h2 className="text-section-title" style={{ marginTop: 8 }}>
|
|
602
|
+
Built for
|
|
603
|
+
<br />
|
|
604
|
+
the terminal
|
|
605
|
+
</h2>
|
|
606
|
+
<p className="m-text-body reveal-blur delay-1" style={{ marginTop: 20 }}>
|
|
607
|
+
Every interaction happens through clean, memorable commands.
|
|
608
|
+
No GUI required.
|
|
609
|
+
</p>
|
|
610
|
+
</div>
|
|
611
|
+
<div
|
|
612
|
+
className="commands-list reveal delay-2"
|
|
613
|
+
style={{ marginTop: 36 }}
|
|
614
|
+
>
|
|
615
|
+
{[
|
|
616
|
+
{
|
|
617
|
+
cmd: "visionos start",
|
|
618
|
+
desc: "Start the local runtime & dashboard server",
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
cmd: "visionos open",
|
|
622
|
+
desc: "Open the learning dashboard in your browser",
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
cmd: "visionos userinfo",
|
|
626
|
+
desc: "Display your profile & learning progress",
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
cmd: "visionos status",
|
|
630
|
+
desc: "Check if the runtime server is running",
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
cmd: "visionos logout",
|
|
634
|
+
desc: "Sign out and clear session data",
|
|
635
|
+
},
|
|
636
|
+
].map((row) => (
|
|
637
|
+
<div key={row.cmd} className="command-row">
|
|
638
|
+
<span className="command-name">{row.cmd}</span>
|
|
639
|
+
<span className="command-desc">{row.desc}</span>
|
|
640
|
+
</div>
|
|
641
|
+
))}
|
|
642
|
+
</div>
|
|
643
|
+
</div>
|
|
644
|
+
|
|
645
|
+
<div
|
|
646
|
+
className="reveal-right delay-3"
|
|
647
|
+
style={{
|
|
648
|
+
display: "flex",
|
|
649
|
+
alignItems: "center",
|
|
650
|
+
justifyContent: "center",
|
|
651
|
+
}}
|
|
652
|
+
>
|
|
653
|
+
<div className="access-card">
|
|
654
|
+
<div className="access-card-stripe" />
|
|
655
|
+
<div className="access-card-content">
|
|
656
|
+
<div>
|
|
657
|
+
<span className="access-card-label">
|
|
658
|
+
Terminal Access Card
|
|
659
|
+
</span>
|
|
660
|
+
<span className="access-card-value">
|
|
661
|
+
VisionOS{" "}
|
|
662
|
+
<span style={{ color: "var(--accent-teal)" }}>CLI</span>
|
|
663
|
+
</span>
|
|
664
|
+
</div>
|
|
665
|
+
<div className="access-card-row">
|
|
666
|
+
<div className="access-card-field">
|
|
667
|
+
<span className="access-card-label">Version</span>
|
|
668
|
+
<span className="access-card-value access-card-mono">
|
|
669
|
+
0.1.0
|
|
670
|
+
</span>
|
|
671
|
+
</div>
|
|
672
|
+
<div className="access-card-field">
|
|
673
|
+
<span className="access-card-label">Runtime</span>
|
|
674
|
+
<span className="access-card-value access-card-mono">
|
|
675
|
+
Node.js
|
|
676
|
+
</span>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
<div className="access-card-row">
|
|
680
|
+
<div className="access-card-field">
|
|
681
|
+
<span className="access-card-label">Port</span>
|
|
682
|
+
<span className="access-card-value access-card-mono">
|
|
683
|
+
4000
|
|
684
|
+
</span>
|
|
685
|
+
</div>
|
|
686
|
+
<div className="access-card-field">
|
|
687
|
+
<span className="access-card-label">Tracks</span>
|
|
688
|
+
<span className="access-card-value access-card-mono">
|
|
689
|
+
4
|
|
690
|
+
</span>
|
|
691
|
+
</div>
|
|
692
|
+
</div>
|
|
693
|
+
</div>
|
|
694
|
+
</div>
|
|
695
|
+
</div>
|
|
696
|
+
</div>
|
|
697
|
+
</section>
|
|
698
|
+
|
|
699
|
+
{/* ── CTA — START YOUR JOURNEY ─────────────────── */}
|
|
700
|
+
<section
|
|
701
|
+
className="m-section"
|
|
702
|
+
style={{ paddingBottom: "var(--space-xxl)" }}
|
|
703
|
+
id="cta"
|
|
704
|
+
>
|
|
705
|
+
<div
|
|
706
|
+
className="scanline-band reveal-scale"
|
|
707
|
+
style={{ marginBottom: "var(--space-xl)" }}
|
|
708
|
+
>
|
|
709
|
+
<span
|
|
710
|
+
className="scanline-text glitch-text"
|
|
711
|
+
style={{
|
|
712
|
+
textAlign: "center",
|
|
713
|
+
display: "block",
|
|
714
|
+
fontSize: "clamp(3rem, 8vw, 7rem)",
|
|
715
|
+
}}
|
|
716
|
+
>
|
|
717
|
+
BEGIN.
|
|
718
|
+
</span>
|
|
719
|
+
</div>
|
|
720
|
+
|
|
721
|
+
<div className="split-layout split-layout--text-left split-layout--center">
|
|
722
|
+
<div className="reveal-left">
|
|
723
|
+
<p className="text-label">Get Started</p>
|
|
724
|
+
<h2 className="text-section-title" style={{ marginTop: 8 }}>
|
|
725
|
+
Start your
|
|
726
|
+
<br />
|
|
727
|
+
terminal journey
|
|
728
|
+
</h2>
|
|
729
|
+
<p className="m-text-body reveal-blur delay-1" style={{ marginTop: 20 }}>
|
|
730
|
+
One command to install. One command to learn. The terminal
|
|
731
|
+
awaits.
|
|
732
|
+
</p>
|
|
733
|
+
|
|
734
|
+
<div
|
|
735
|
+
className={`copy-command ${copied ? "copied" : ""}`}
|
|
736
|
+
role="button"
|
|
737
|
+
tabIndex={0}
|
|
738
|
+
aria-label="Copy install command"
|
|
739
|
+
onClick={() => void handleCopy("npm install -g visionos-cli")}
|
|
740
|
+
onKeyDown={(e) => {
|
|
741
|
+
if (e.key === "Enter" || e.key === " ")
|
|
742
|
+
void handleCopy("npm install -g visionos-cli");
|
|
743
|
+
}}
|
|
744
|
+
>
|
|
745
|
+
<span style={{ opacity: 0.4 }}>$</span>
|
|
746
|
+
<span>npm install -g visionos-cli</span>
|
|
747
|
+
<span className="copy-icon">
|
|
748
|
+
<svg
|
|
749
|
+
width="18"
|
|
750
|
+
height="18"
|
|
751
|
+
viewBox="0 0 24 24"
|
|
752
|
+
fill="none"
|
|
753
|
+
stroke="currentColor"
|
|
754
|
+
strokeWidth="2"
|
|
755
|
+
strokeLinecap="round"
|
|
756
|
+
>
|
|
757
|
+
<rect x="9" y="9" width="13" height="13" rx="2" />
|
|
758
|
+
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
|
|
759
|
+
</svg>
|
|
760
|
+
</span>
|
|
761
|
+
<span className="copied-tooltip">Copied!</span>
|
|
762
|
+
</div>
|
|
763
|
+
|
|
764
|
+
<p className="text-label" style={{ marginTop: 24 }}>
|
|
765
|
+
Then run{" "}
|
|
766
|
+
<strong style={{ color: "var(--m-ink)", fontWeight: 700 }}>
|
|
767
|
+
visionos start
|
|
768
|
+
</strong>
|
|
769
|
+
</p>
|
|
770
|
+
</div>
|
|
771
|
+
|
|
772
|
+
<div
|
|
773
|
+
className="reveal-right delay-2"
|
|
774
|
+
style={{
|
|
775
|
+
display: "flex",
|
|
776
|
+
alignItems: "center",
|
|
777
|
+
justifyContent: "center",
|
|
778
|
+
}}
|
|
779
|
+
>
|
|
780
|
+
<TerminalWindow style={{ width: "100%", maxWidth: 400 }}>
|
|
781
|
+
<TerminalLine type="prompt">
|
|
782
|
+
npm i -g visionos-cli
|
|
783
|
+
</TerminalLine>
|
|
784
|
+
<TerminalLine type="success">✓ installed globally</TerminalLine>
|
|
785
|
+
<TerminalLine> </TerminalLine>
|
|
786
|
+
<TerminalLine type="prompt">visionos start</TerminalLine>
|
|
787
|
+
<TerminalLine type="success">✓ Runtime active</TerminalLine>
|
|
788
|
+
<TerminalLine type="output">
|
|
789
|
+
Dashboard → http://127.0.0.1:5173
|
|
790
|
+
</TerminalLine>
|
|
791
|
+
<TerminalLine type="prompt">█</TerminalLine>
|
|
792
|
+
</TerminalWindow>
|
|
793
|
+
</div>
|
|
794
|
+
</div>
|
|
795
|
+
</section>
|
|
796
|
+
|
|
797
|
+
{/* ── FOOTER SHOWCASE SECTION (Image 2) ────────── */}
|
|
798
|
+
<ScanlineDivider height={60} />
|
|
799
|
+
<section className="m-footer-showcase">
|
|
800
|
+
{/* Header row */}
|
|
801
|
+
<div className="m-footer-showcase-header">
|
|
802
|
+
<div className="header-left">
|
|
803
|
+
<span className="header-brand">VisionOS, Inc.</span>
|
|
804
|
+
<span className="header-meta">
|
|
805
|
+
Copyright 2025
|
|
806
|
+
<br />
|
|
807
|
+
All Rights Reserved
|
|
808
|
+
</span>
|
|
809
|
+
</div>
|
|
810
|
+
<nav className="header-nav">
|
|
811
|
+
<a href="#tracks">
|
|
812
|
+
tracks <span className="arrow">↗</span>
|
|
813
|
+
</a>
|
|
814
|
+
<a href="#commands">
|
|
815
|
+
commands <span className="arrow">↗</span>
|
|
816
|
+
</a>
|
|
817
|
+
<a href="#hero">@visionos</a>
|
|
818
|
+
</nav>
|
|
819
|
+
</div>
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
<div className="m-footer-showcase-content">
|
|
824
|
+
{/* Left: Heading + CTA */}
|
|
825
|
+
<div className="reveal-left">
|
|
826
|
+
<h2 className="m-footer-heading">
|
|
827
|
+
Meet VisionOS
|
|
828
|
+
<span className="m-footer-heading-sub">
|
|
829
|
+
Interfaces for the future.
|
|
830
|
+
</span>
|
|
831
|
+
</h2>
|
|
832
|
+
|
|
833
|
+
<div className="m-footer-cta-area">
|
|
834
|
+
{/* Icon */}
|
|
835
|
+
<svg
|
|
836
|
+
className="m-footer-cta-icon float-gentle"
|
|
837
|
+
viewBox="0 0 64 64"
|
|
838
|
+
fill="none"
|
|
839
|
+
>
|
|
840
|
+
<rect
|
|
841
|
+
x="8"
|
|
842
|
+
y="14"
|
|
843
|
+
width="48"
|
|
844
|
+
height="36"
|
|
845
|
+
rx="4"
|
|
846
|
+
stroke="var(--m-ink)"
|
|
847
|
+
strokeWidth="3"
|
|
848
|
+
/>
|
|
849
|
+
<path
|
|
850
|
+
d="M20 30L28 38L44 22"
|
|
851
|
+
stroke="var(--m-ink)"
|
|
852
|
+
strokeWidth="3"
|
|
853
|
+
strokeLinecap="round"
|
|
854
|
+
strokeLinejoin="round"
|
|
855
|
+
/>
|
|
856
|
+
</svg>
|
|
857
|
+
<p className="m-footer-cta-text">
|
|
858
|
+
Claim your terminal access card.
|
|
859
|
+
</p>
|
|
860
|
+
<div className="m-footer-input-group">
|
|
861
|
+
<input
|
|
862
|
+
type="email"
|
|
863
|
+
className="m-footer-input"
|
|
864
|
+
placeholder="Enter your email address"
|
|
865
|
+
aria-label="Email address"
|
|
866
|
+
/>
|
|
867
|
+
<button className="m-footer-send-btn" aria-label="Submit">
|
|
868
|
+
<svg
|
|
869
|
+
width="20"
|
|
870
|
+
height="20"
|
|
871
|
+
viewBox="0 0 24 24"
|
|
872
|
+
fill="none"
|
|
873
|
+
stroke="currentColor"
|
|
874
|
+
strokeWidth="2"
|
|
875
|
+
strokeLinecap="round"
|
|
876
|
+
strokeLinejoin="round"
|
|
877
|
+
>
|
|
878
|
+
<line x1="22" y1="2" x2="11" y2="13" />
|
|
879
|
+
<polygon points="22 2 15 22 11 13 2 9 22 2" />
|
|
880
|
+
</svg>
|
|
881
|
+
</button>
|
|
882
|
+
</div>
|
|
883
|
+
</div>
|
|
884
|
+
</div>
|
|
885
|
+
|
|
886
|
+
{/* Right: ID Card */}
|
|
887
|
+
<div className="reveal-right delay-2">
|
|
888
|
+
<div className="m-id-card">
|
|
889
|
+
<div className="m-id-card-image">
|
|
890
|
+
<div className="m-id-card-image-inner">
|
|
891
|
+
{user ? user.name.charAt(0).toUpperCase() : "V"}
|
|
892
|
+
</div>
|
|
893
|
+
</div>
|
|
894
|
+
<div className="m-id-card-info" style={{ position: "relative" }}>
|
|
895
|
+
<div>
|
|
896
|
+
<span className="m-id-card-name-label">Name</span>
|
|
897
|
+
<span className="m-id-card-name-value">
|
|
898
|
+
{user ? user.name : "VisionOS"}
|
|
899
|
+
<br />
|
|
900
|
+
{user ? "Learner" : "Terminal"}
|
|
901
|
+
</span>
|
|
902
|
+
</div>
|
|
903
|
+
<div className="m-id-card-barcode">10. 0001</div>
|
|
904
|
+
<div className="m-id-card-footer">
|
|
905
|
+
<div className="m-id-card-footer-item">
|
|
906
|
+
<span className="m-id-card-footer-label">Cohort</span>
|
|
907
|
+
<span className="m-id-card-footer-value">000.1</span>
|
|
908
|
+
</div>
|
|
909
|
+
<div className="m-id-card-footer-item">
|
|
910
|
+
<span className="m-id-card-footer-label">Date</span>
|
|
911
|
+
<span className="m-id-card-footer-value">
|
|
912
|
+
{formattedDate}
|
|
913
|
+
</span>
|
|
914
|
+
</div>
|
|
915
|
+
</div>
|
|
916
|
+
</div>
|
|
917
|
+
</div>
|
|
918
|
+
</div>
|
|
919
|
+
</div>
|
|
920
|
+
</section>
|
|
921
|
+
|
|
922
|
+
{/* ── FOOTER ────────────────────────────────────── */}
|
|
923
|
+
<footer className="m-site-footer">
|
|
924
|
+
<div className="header-left">
|
|
925
|
+
<span className="header-brand">VisionOS</span>
|
|
926
|
+
<span className="header-meta">© 2025 · CLI-First Learning</span>
|
|
927
|
+
</div>
|
|
928
|
+
<nav className="header-nav">
|
|
929
|
+
<a href="#hero">Top ↑</a>
|
|
930
|
+
<a
|
|
931
|
+
href="https://github.com"
|
|
932
|
+
target="_blank"
|
|
933
|
+
rel="noopener noreferrer"
|
|
934
|
+
>
|
|
935
|
+
GitHub <span className="arrow">↗</span>
|
|
936
|
+
</a>
|
|
937
|
+
</nav>
|
|
938
|
+
</footer>
|
|
939
|
+
</div>
|
|
940
|
+
|
|
941
|
+
<div className="bezel-notch">
|
|
942
|
+
<div className="bezel-notch-inner" />
|
|
943
|
+
</div>
|
|
944
|
+
</div>
|
|
945
|
+
);
|
|
946
|
+
}
|