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.
Files changed (160) hide show
  1. package/.claude/worktrees/competent-burnell-8d1330/README.md +138 -0
  2. package/.claude/worktrees/competent-burnell-8d1330/cli/package.json +35 -0
  3. package/.claude/worktrees/competent-burnell-8d1330/cli/scripts/copy-web-assets.mjs +12 -0
  4. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/logout.ts +12 -0
  5. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/open.ts +19 -0
  6. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/start.ts +97 -0
  7. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/status.ts +23 -0
  8. package/.claude/worktrees/competent-burnell-8d1330/cli/src/commands/userinfo.ts +47 -0
  9. package/.claude/worktrees/competent-burnell-8d1330/cli/src/index.ts +23 -0
  10. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/auth.ts +84 -0
  11. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/browser.ts +37 -0
  12. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/localState.ts +80 -0
  13. package/.claude/worktrees/competent-burnell-8d1330/cli/src/lib/runtime.ts +203 -0
  14. package/.claude/worktrees/competent-burnell-8d1330/cli/src/runtime/index.ts +36 -0
  15. package/.claude/worktrees/competent-burnell-8d1330/cli/src/types/inquirer.d.ts +9 -0
  16. package/.claude/worktrees/competent-burnell-8d1330/cli/tsconfig.json +19 -0
  17. package/.claude/worktrees/competent-burnell-8d1330/client/index.html +15 -0
  18. package/.claude/worktrees/competent-burnell-8d1330/client/package.json +27 -0
  19. package/.claude/worktrees/competent-burnell-8d1330/client/postcss.config.cjs +7 -0
  20. package/.claude/worktrees/competent-burnell-8d1330/client/src/App.tsx +57 -0
  21. package/.claude/worktrees/competent-burnell-8d1330/client/src/components/CliAuthPage.tsx +385 -0
  22. package/.claude/worktrees/competent-burnell-8d1330/client/src/components/ManifestoPage.tsx +946 -0
  23. package/.claude/worktrees/competent-burnell-8d1330/client/src/components/TrackCard.tsx +19 -0
  24. package/.claude/worktrees/competent-burnell-8d1330/client/src/lib/api.ts +58 -0
  25. package/.claude/worktrees/competent-burnell-8d1330/client/src/main.tsx +11 -0
  26. package/.claude/worktrees/competent-burnell-8d1330/client/src/styles/index.css +33 -0
  27. package/.claude/worktrees/competent-burnell-8d1330/client/src/styles/manifesto.css +1398 -0
  28. package/.claude/worktrees/competent-burnell-8d1330/client/tailwind.config.ts +36 -0
  29. package/.claude/worktrees/competent-burnell-8d1330/client/tsconfig.json +25 -0
  30. package/.claude/worktrees/competent-burnell-8d1330/client/vite.config.ts +20 -0
  31. package/.claude/worktrees/competent-burnell-8d1330/package-lock.json +5278 -0
  32. package/.claude/worktrees/competent-burnell-8d1330/package.json +24 -0
  33. package/.claude/worktrees/competent-burnell-8d1330/server/package.json +25 -0
  34. package/.claude/worktrees/competent-burnell-8d1330/server/src/app.ts +71 -0
  35. package/.claude/worktrees/competent-burnell-8d1330/server/src/config/env.ts +14 -0
  36. package/.claude/worktrees/competent-burnell-8d1330/server/src/features/auth/sessionStore.ts +74 -0
  37. package/.claude/worktrees/competent-burnell-8d1330/server/src/index.ts +8 -0
  38. package/.claude/worktrees/competent-burnell-8d1330/server/src/routes/auth.ts +112 -0
  39. package/.claude/worktrees/competent-burnell-8d1330/server/src/routes/health.ts +14 -0
  40. package/.claude/worktrees/competent-burnell-8d1330/server/tsconfig.json +19 -0
  41. package/.claude/worktrees/competent-burnell-8d1330/shared/package.json +24 -0
  42. package/.claude/worktrees/competent-burnell-8d1330/shared/src/index.ts +91 -0
  43. package/.claude/worktrees/competent-burnell-8d1330/shared/tsconfig.json +16 -0
  44. package/.claude/worktrees/competent-burnell-8d1330/tsconfig.base.json +12 -0
  45. package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/index.html +392 -0
  46. package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/script.js +146 -0
  47. package/.claude/worktrees/competent-burnell-8d1330/visionos-manifesto/styles.css +1082 -0
  48. package/.claude/worktrees/vigilant-napier-0de76f/README.md +138 -0
  49. package/.claude/worktrees/vigilant-napier-0de76f/cli/package.json +35 -0
  50. package/.claude/worktrees/vigilant-napier-0de76f/cli/scripts/copy-web-assets.mjs +12 -0
  51. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/logout.ts +12 -0
  52. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/open.ts +19 -0
  53. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/start.ts +97 -0
  54. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/status.ts +23 -0
  55. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/commands/userinfo.ts +47 -0
  56. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/index.ts +23 -0
  57. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/auth.ts +84 -0
  58. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/browser.ts +37 -0
  59. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/localState.ts +80 -0
  60. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/lib/runtime.ts +203 -0
  61. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/runtime/index.ts +36 -0
  62. package/.claude/worktrees/vigilant-napier-0de76f/cli/src/types/inquirer.d.ts +9 -0
  63. package/.claude/worktrees/vigilant-napier-0de76f/cli/tsconfig.json +19 -0
  64. package/.claude/worktrees/vigilant-napier-0de76f/client/index.html +15 -0
  65. package/.claude/worktrees/vigilant-napier-0de76f/client/package.json +27 -0
  66. package/.claude/worktrees/vigilant-napier-0de76f/client/postcss.config.cjs +7 -0
  67. package/.claude/worktrees/vigilant-napier-0de76f/client/src/App.tsx +57 -0
  68. package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/CliAuthPage.tsx +385 -0
  69. package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/ManifestoPage.tsx +946 -0
  70. package/.claude/worktrees/vigilant-napier-0de76f/client/src/components/TrackCard.tsx +19 -0
  71. package/.claude/worktrees/vigilant-napier-0de76f/client/src/lib/api.ts +58 -0
  72. package/.claude/worktrees/vigilant-napier-0de76f/client/src/main.tsx +11 -0
  73. package/.claude/worktrees/vigilant-napier-0de76f/client/src/styles/index.css +33 -0
  74. package/.claude/worktrees/vigilant-napier-0de76f/client/src/styles/manifesto.css +1398 -0
  75. package/.claude/worktrees/vigilant-napier-0de76f/client/tailwind.config.ts +36 -0
  76. package/.claude/worktrees/vigilant-napier-0de76f/client/tsconfig.json +25 -0
  77. package/.claude/worktrees/vigilant-napier-0de76f/client/vite.config.ts +20 -0
  78. package/.claude/worktrees/vigilant-napier-0de76f/package-lock.json +5278 -0
  79. package/.claude/worktrees/vigilant-napier-0de76f/package.json +24 -0
  80. package/.claude/worktrees/vigilant-napier-0de76f/server/package.json +25 -0
  81. package/.claude/worktrees/vigilant-napier-0de76f/server/src/app.ts +71 -0
  82. package/.claude/worktrees/vigilant-napier-0de76f/server/src/config/env.ts +14 -0
  83. package/.claude/worktrees/vigilant-napier-0de76f/server/src/features/auth/sessionStore.ts +74 -0
  84. package/.claude/worktrees/vigilant-napier-0de76f/server/src/index.ts +8 -0
  85. package/.claude/worktrees/vigilant-napier-0de76f/server/src/routes/auth.ts +112 -0
  86. package/.claude/worktrees/vigilant-napier-0de76f/server/src/routes/health.ts +14 -0
  87. package/.claude/worktrees/vigilant-napier-0de76f/server/tsconfig.json +19 -0
  88. package/.claude/worktrees/vigilant-napier-0de76f/shared/package.json +24 -0
  89. package/.claude/worktrees/vigilant-napier-0de76f/shared/src/index.ts +91 -0
  90. package/.claude/worktrees/vigilant-napier-0de76f/shared/tsconfig.json +16 -0
  91. package/.claude/worktrees/vigilant-napier-0de76f/tsconfig.base.json +12 -0
  92. package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/index.html +392 -0
  93. package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/script.js +146 -0
  94. package/.claude/worktrees/vigilant-napier-0de76f/visionos-manifesto/styles.css +1082 -0
  95. package/.github/workflows/publish.yml +30 -0
  96. package/README.md +175 -0
  97. package/cli/README.md +165 -0
  98. package/cli/package.json +36 -0
  99. package/cli/scripts/copy-web-assets.mjs +12 -0
  100. package/cli/src/commands/lessons.ts +68 -0
  101. package/cli/src/commands/login.ts +46 -0
  102. package/cli/src/commands/logout.ts +12 -0
  103. package/cli/src/commands/open.ts +29 -0
  104. package/cli/src/commands/start.ts +146 -0
  105. package/cli/src/commands/status.ts +28 -0
  106. package/cli/src/commands/userinfo.ts +59 -0
  107. package/cli/src/index.ts +109 -0
  108. package/cli/src/lib/auth.ts +84 -0
  109. package/cli/src/lib/browser.ts +37 -0
  110. package/cli/src/lib/content.ts +57 -0
  111. package/cli/src/lib/lessonPrinter.ts +38 -0
  112. package/cli/src/lib/lessonRunner.ts +381 -0
  113. package/cli/src/lib/localState.ts +114 -0
  114. package/cli/src/lib/loginFlow.ts +74 -0
  115. package/cli/src/lib/progress.ts +94 -0
  116. package/cli/src/lib/runtime.ts +220 -0
  117. package/cli/src/lib/validator.ts +401 -0
  118. package/cli/src/runtime/index.ts +108 -0
  119. package/cli/src/types/inquirer.d.ts +9 -0
  120. package/cli/tsconfig.json +19 -0
  121. package/client/index.html +15 -0
  122. package/client/package.json +27 -0
  123. package/client/postcss.config.cjs +7 -0
  124. package/client/src/App.tsx +102 -0
  125. package/client/src/components/AccountPage.tsx +79 -0
  126. package/client/src/components/AuthPanel.tsx +312 -0
  127. package/client/src/components/CliAuthPage.tsx +367 -0
  128. package/client/src/components/CreatorPortal.tsx +885 -0
  129. package/client/src/components/ErrorBoundary.tsx +92 -0
  130. package/client/src/components/ManifestoPage.tsx +1126 -0
  131. package/client/src/components/TrackCard.tsx +19 -0
  132. package/client/src/lib/api.ts +215 -0
  133. package/client/src/main.tsx +14 -0
  134. package/client/src/styles/index.css +33 -0
  135. package/client/src/styles/manifesto.css +1828 -0
  136. package/client/tailwind.config.ts +36 -0
  137. package/client/tsconfig.json +25 -0
  138. package/client/vercel.json +8 -0
  139. package/client/vite.config.ts +33 -0
  140. package/package.json +27 -0
  141. package/server/package.json +26 -0
  142. package/server/src/app.ts +132 -0
  143. package/server/src/config/env.ts +135 -0
  144. package/server/src/features/accounts/accountStore.ts +359 -0
  145. package/server/src/features/accounts/contentStore.ts +264 -0
  146. package/server/src/features/accounts/password.ts +26 -0
  147. package/server/src/features/auth/sessionStore.ts +79 -0
  148. package/server/src/index.ts +8 -0
  149. package/server/src/routes/auth.ts +328 -0
  150. package/server/src/routes/content.ts +174 -0
  151. package/server/src/routes/health.ts +14 -0
  152. package/server/src/routes/progress.ts +105 -0
  153. package/server/tsconfig.json +19 -0
  154. package/shared/package.json +24 -0
  155. package/shared/src/index.ts +455 -0
  156. package/shared/tsconfig.json +16 -0
  157. package/tsconfig.base.json +12 -0
  158. package/visionos-manifesto/index.html +392 -0
  159. package/visionos-manifesto/script.js +146 -0
  160. package/visionos-manifesto/styles.css +1082 -0
@@ -0,0 +1,385 @@
1
+ import { useEffect, useState, type FormEvent } from "react";
2
+ import {
3
+ LEARNING_TRACKS,
4
+ type CliAuthSession,
5
+ type CliAuthSessionStatus
6
+ } from "../../../shared/src/index.js";
7
+ import {
8
+ VisionOsApiError,
9
+ completeCliAuthSession,
10
+ getCliAuthSession
11
+ } from "../lib/api.js";
12
+
13
+ interface CliAuthPageProps {
14
+ sessionId: string;
15
+ }
16
+
17
+ function formatSessionTime(value: string) {
18
+ return new Date(value).toLocaleString(undefined, {
19
+ dateStyle: "medium",
20
+ timeStyle: "short"
21
+ });
22
+ }
23
+
24
+ function getStatusCopy(status: CliAuthSessionStatus) {
25
+ if (status === "authenticated") return "Authenticated";
26
+ if (status === "expired") return "Expired";
27
+ return "Awaiting confirmation";
28
+ }
29
+
30
+ export function CliAuthPage({ sessionId }: CliAuthPageProps) {
31
+ const [session, setSession] = useState<CliAuthSession | null>(null);
32
+ const [learnerName, setLearnerName] = useState("");
33
+ const [learnerEmail, setLearnerEmail] = useState("");
34
+ const [loading, setLoading] = useState(true);
35
+ const [submitting, setSubmitting] = useState(false);
36
+ const [errorMessage, setErrorMessage] = useState<string | null>(null);
37
+
38
+ useEffect(() => {
39
+ let cancelled = false;
40
+
41
+ async function loadSession() {
42
+ setLoading(true);
43
+ setErrorMessage(null);
44
+
45
+ try {
46
+ const payload = await getCliAuthSession(sessionId);
47
+
48
+ if (cancelled) return;
49
+
50
+ setSession(payload.session);
51
+ setLearnerName(payload.session.learner?.name ?? "");
52
+ setLearnerEmail(payload.session.learner?.email ?? "");
53
+ } catch (error) {
54
+ if (cancelled) return;
55
+
56
+ if (error instanceof VisionOsApiError) {
57
+ setErrorMessage(error.message);
58
+ } else if (error instanceof Error) {
59
+ setErrorMessage(error.message);
60
+ } else {
61
+ setErrorMessage("Unable to load the CLI authentication session.");
62
+ }
63
+ } finally {
64
+ if (!cancelled) setLoading(false);
65
+ }
66
+ }
67
+
68
+ void loadSession();
69
+
70
+ return () => {
71
+ cancelled = true;
72
+ };
73
+ }, [sessionId]);
74
+
75
+ async function handleSubmit(event: FormEvent<HTMLFormElement>) {
76
+ event.preventDefault();
77
+
78
+ const trimmedName = learnerName.trim();
79
+ const trimmedEmail = learnerEmail.trim().toLowerCase();
80
+
81
+ if (!trimmedName || !trimmedEmail) {
82
+ setErrorMessage("Enter your name and email address to continue.");
83
+ return;
84
+ }
85
+
86
+ setSubmitting(true);
87
+ setErrorMessage(null);
88
+
89
+ try {
90
+ const payload = await completeCliAuthSession(sessionId, {
91
+ learnerName: trimmedName,
92
+ learnerEmail: trimmedEmail
93
+ });
94
+
95
+ setSession(payload.session);
96
+ setLearnerName(payload.session.learner?.name ?? trimmedName);
97
+ setLearnerEmail(payload.session.learner?.email ?? trimmedEmail);
98
+ } catch (error) {
99
+ if (error instanceof VisionOsApiError) {
100
+ setErrorMessage(error.message);
101
+ } else if (error instanceof Error) {
102
+ setErrorMessage(error.message);
103
+ } else {
104
+ setErrorMessage("Unable to complete CLI authentication.");
105
+ }
106
+ } finally {
107
+ setSubmitting(false);
108
+ }
109
+ }
110
+
111
+ const track = session
112
+ ? LEARNING_TRACKS.find((learningTrack) => learningTrack.id === session.trackId)
113
+ : undefined;
114
+
115
+ return (
116
+ <div className="monitor-bezel">
117
+ <div className="monitor-screen" style={{ display: "flex", flexDirection: "column", minHeight: "calc(100vh - 48px)" }}>
118
+ {/* ── Header ─────────────────────── */}
119
+ <header className="m-site-header">
120
+ <div className="header-left">
121
+ <span className="header-brand">VisionOS</span>
122
+ <span className="header-meta">CLI Browser Handoff</span>
123
+ </div>
124
+ <nav className="header-nav">
125
+ <a href="/">Home <span className="arrow">↗</span></a>
126
+ </nav>
127
+ </header>
128
+
129
+ {/* ── Main Content ───────────────── */}
130
+ <main style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "40px 60px" }}>
131
+ <div style={{ display: "grid", gridTemplateColumns: "1.1fr 0.9fr", gap: 48, width: "100%", maxWidth: 1100, alignItems: "center" }}>
132
+ {/* Left Column */}
133
+ <div>
134
+ <p className="text-label" style={{ fontFamily: "var(--font-mono)" }}>
135
+ ← cli handoff
136
+ </p>
137
+ <h1 className="text-section-title" style={{ marginTop: 12 }}>
138
+ Finish sign-in<br />
139
+ <span style={{ fontWeight: 400, color: "var(--ink-muted)" }}>for your terminal.</span>
140
+ </h1>
141
+ <p className="m-text-body" style={{ marginTop: 20 }}>
142
+ This browser step confirms your identity for the waiting CLI session. Once complete, return to your terminal.
143
+ </p>
144
+
145
+ {/* Session info cards */}
146
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 32 }}>
147
+ <div style={{ padding: "20px 16px", background: "var(--bg-beige-light)", borderRadius: "var(--radius-lg)", border: "1px solid rgba(0,0,0,0.04)" }}>
148
+ <span className="text-label">Session ID</span>
149
+ <p style={{ marginTop: 8, fontFamily: "var(--font-mono)", fontSize: "0.72rem", color: "var(--ink-soft)", wordBreak: "break-all" }}>
150
+ {sessionId}
151
+ </p>
152
+ </div>
153
+ <div style={{ padding: "20px 16px", background: "var(--bg-beige-light)", borderRadius: "var(--radius-lg)", border: "1px solid rgba(0,0,0,0.04)" }}>
154
+ <span className="text-label">Learning Track</span>
155
+ <p style={{ marginTop: 8, fontSize: "1.05rem", fontWeight: 700, color: "var(--m-ink)" }}>
156
+ {track?.label ?? session?.trackId ?? "Loading…"}
157
+ </p>
158
+ {track && (
159
+ <p style={{ marginTop: 6, fontSize: "0.78rem", color: "var(--ink-muted)", lineHeight: 1.5 }}>
160
+ {track.description}
161
+ </p>
162
+ )}
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ {/* Right Column — Auth Card */}
168
+ <div style={{
169
+ background: "var(--m-white)",
170
+ borderRadius: "var(--radius-lg)",
171
+ padding: 32,
172
+ border: "1px solid rgba(0,0,0,0.06)",
173
+ boxShadow: "0 4px 24px rgba(0,0,0,0.08)",
174
+ position: "relative",
175
+ overflow: "hidden"
176
+ }}>
177
+ {/* Top stripe for visual interest */}
178
+ <div style={{
179
+ position: "absolute",
180
+ top: 0,
181
+ left: 0,
182
+ right: 0,
183
+ height: 4,
184
+ background: session?.status === "authenticated"
185
+ ? "var(--accent-green)"
186
+ : session?.status === "expired"
187
+ ? "var(--accent-red)"
188
+ : "var(--accent-teal)"
189
+ }} />
190
+
191
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
192
+ <div>
193
+ <span className="text-label" style={{ fontFamily: "var(--font-mono)", color: "var(--accent-teal)" }}>
194
+ sign-in status
195
+ </span>
196
+ <h2 style={{ marginTop: 8, fontSize: "1.6rem", fontWeight: 800, letterSpacing: "-0.02em", color: "var(--m-ink)", fontFamily: "var(--font-sans)" }}>
197
+ {loading ? "Loading session…" : getStatusCopy(session?.status ?? "pending")}
198
+ </h2>
199
+ </div>
200
+ </div>
201
+
202
+ {loading && (
203
+ <p style={{ marginTop: 20, fontSize: "0.88rem", color: "var(--ink-muted)", lineHeight: 1.6 }}>
204
+ Fetching the auth session so this browser can confirm the waiting CLI.
205
+ </p>
206
+ )}
207
+
208
+ {!loading && errorMessage && (
209
+ <div style={{
210
+ marginTop: 20,
211
+ padding: "12px 16px",
212
+ background: "rgba(224, 90, 58, 0.08)",
213
+ border: "1px solid rgba(224, 90, 58, 0.2)",
214
+ borderRadius: "var(--radius-md)",
215
+ fontSize: "0.85rem",
216
+ color: "var(--accent-red)"
217
+ }}>
218
+ {errorMessage}
219
+ </div>
220
+ )}
221
+
222
+ {!loading && session && (
223
+ <div style={{ marginTop: 24 }}>
224
+ {/* Timestamps */}
225
+ <div style={{
226
+ display: "grid",
227
+ gridTemplateColumns: "1fr 1fr",
228
+ gap: 12,
229
+ padding: "16px",
230
+ background: "var(--bg-beige-light)",
231
+ borderRadius: "var(--radius-md)",
232
+ marginBottom: 24
233
+ }}>
234
+ <div>
235
+ <span style={{ fontSize: "0.65rem", color: "var(--ink-muted)", textTransform: "uppercase", letterSpacing: "0.05em" }}>Created</span>
236
+ <p style={{ marginTop: 4, fontFamily: "var(--font-mono)", fontSize: "0.78rem", fontWeight: 600, color: "var(--m-ink)" }}>
237
+ {formatSessionTime(session.createdAt)}
238
+ </p>
239
+ </div>
240
+ <div>
241
+ <span style={{ fontSize: "0.65rem", color: "var(--ink-muted)", textTransform: "uppercase", letterSpacing: "0.05em" }}>Expires</span>
242
+ <p style={{ marginTop: 4, fontFamily: "var(--font-mono)", fontSize: "0.78rem", fontWeight: 600, color: "var(--m-ink)" }}>
243
+ {formatSessionTime(session.expiresAt)}
244
+ </p>
245
+ </div>
246
+ </div>
247
+
248
+ {/* Pending → Form */}
249
+ {session.status === "pending" && (
250
+ <form style={{ display: "flex", flexDirection: "column", gap: 16 }} onSubmit={(event) => void handleSubmit(event)}>
251
+ <div>
252
+ <label htmlFor="learnerName" style={{ display: "block", fontSize: "0.75rem", fontWeight: 600, color: "var(--ink-soft)", marginBottom: 6 }}>
253
+ Name
254
+ </label>
255
+ <input
256
+ id="learnerName"
257
+ style={{
258
+ width: "100%",
259
+ padding: "12px 16px",
260
+ border: "1px solid rgba(0,0,0,0.1)",
261
+ borderRadius: "var(--radius-md)",
262
+ background: "var(--m-white)",
263
+ fontFamily: "var(--font-sans)",
264
+ fontSize: "0.9rem",
265
+ color: "var(--m-ink)",
266
+ outline: "none",
267
+ transition: "border-color 0.3s ease"
268
+ }}
269
+ value={learnerName}
270
+ onChange={(event) => setLearnerName(event.target.value)}
271
+ placeholder="Ada Lovelace"
272
+ autoComplete="name"
273
+ />
274
+ </div>
275
+ <div>
276
+ <label htmlFor="learnerEmail" style={{ display: "block", fontSize: "0.75rem", fontWeight: 600, color: "var(--ink-soft)", marginBottom: 6 }}>
277
+ Email
278
+ </label>
279
+ <input
280
+ id="learnerEmail"
281
+ style={{
282
+ width: "100%",
283
+ padding: "12px 16px",
284
+ border: "1px solid rgba(0,0,0,0.1)",
285
+ borderRadius: "var(--radius-md)",
286
+ background: "var(--m-white)",
287
+ fontFamily: "var(--font-sans)",
288
+ fontSize: "0.9rem",
289
+ color: "var(--m-ink)",
290
+ outline: "none",
291
+ transition: "border-color 0.3s ease"
292
+ }}
293
+ value={learnerEmail}
294
+ onChange={(event) => setLearnerEmail(event.target.value)}
295
+ placeholder="ada@example.com"
296
+ autoComplete="email"
297
+ type="email"
298
+ />
299
+ </div>
300
+ <button
301
+ type="submit"
302
+ disabled={submitting}
303
+ style={{
304
+ width: "100%",
305
+ padding: "14px 20px",
306
+ background: "var(--m-ink)",
307
+ color: "var(--m-white)",
308
+ border: "none",
309
+ borderRadius: "var(--radius-md)",
310
+ fontFamily: "var(--font-sans)",
311
+ fontSize: "0.9rem",
312
+ fontWeight: 700,
313
+ cursor: submitting ? "not-allowed" : "pointer",
314
+ opacity: submitting ? 0.6 : 1,
315
+ transition: "all 0.3s ease"
316
+ }}
317
+ >
318
+ {submitting ? "Confirming…" : "Continue in CLI →"}
319
+ </button>
320
+ <p style={{ fontSize: "0.72rem", color: "var(--ink-muted)", lineHeight: 1.6 }}>
321
+ This development flow uses an API-issued temporary auth session. A real identity provider can replace this step.
322
+ </p>
323
+ </form>
324
+ )}
325
+
326
+ {/* Authenticated → Success */}
327
+ {session.status === "authenticated" && (
328
+ <div style={{
329
+ padding: 20,
330
+ background: "rgba(45, 138, 86, 0.08)",
331
+ border: "1px solid rgba(45, 138, 86, 0.2)",
332
+ borderRadius: "var(--radius-md)"
333
+ }}>
334
+ <span style={{ display: "block", fontSize: "0.65rem", fontWeight: 600, color: "var(--accent-green)", textTransform: "uppercase", letterSpacing: "0.1em" }}>
335
+ CLI sign-in complete
336
+ </span>
337
+ <p style={{ marginTop: 8, fontSize: "1rem", fontWeight: 700, color: "var(--m-ink)" }}>
338
+ {session.learner?.name} is authenticated for the {track?.label ?? "selected"} track.
339
+ </p>
340
+ <p style={{ marginTop: 8, fontSize: "0.82rem", color: "var(--ink-soft)", lineHeight: 1.6 }}>
341
+ You can return to the terminal. The waiting CLI session will continue automatically.
342
+ </p>
343
+ </div>
344
+ )}
345
+
346
+ {/* Expired */}
347
+ {session.status === "expired" && (
348
+ <div style={{
349
+ padding: 20,
350
+ background: "rgba(224, 90, 58, 0.06)",
351
+ border: "1px solid rgba(224, 90, 58, 0.15)",
352
+ borderRadius: "var(--radius-md)"
353
+ }}>
354
+ <span style={{ display: "block", fontSize: "0.65rem", fontWeight: 600, color: "var(--accent-red)", textTransform: "uppercase", letterSpacing: "0.1em" }}>
355
+ Session expired
356
+ </span>
357
+ <p style={{ marginTop: 8, fontSize: "0.85rem", color: "var(--ink-soft)", lineHeight: 1.6 }}>
358
+ This browser confirmation took too long. Start a new terminal session with <code style={{ fontFamily: "var(--font-mono)", fontWeight: 600 }}>visionos start</code> to generate a fresh auth link.
359
+ </p>
360
+ </div>
361
+ )}
362
+ </div>
363
+ )}
364
+ </div>
365
+ </div>
366
+ </main>
367
+
368
+ {/* ── Footer ─────────────────────── */}
369
+ <footer className="m-site-footer">
370
+ <div className="header-left">
371
+ <span className="header-brand">VisionOS</span>
372
+ <span className="header-meta">CLI Auth · Secure Session</span>
373
+ </div>
374
+ <nav className="header-nav">
375
+ <a href="/">← Back to home</a>
376
+ </nav>
377
+ </footer>
378
+ </div>
379
+
380
+ <div className="bezel-notch">
381
+ <div className="bezel-notch-inner" />
382
+ </div>
383
+ </div>
384
+ );
385
+ }