adam-os 0.1.5 → 0.2.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.
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ export default function CopyButton({ text }: { text: string }) {
6
+ const [copied, setCopied] = useState(false);
7
+
8
+ async function handleCopy() {
9
+ try {
10
+ await navigator.clipboard.writeText(text);
11
+ setCopied(true);
12
+ setTimeout(() => setCopied(false), 2000);
13
+ } catch {
14
+ // fallback: select a hidden textarea
15
+ }
16
+ }
17
+
18
+ return (
19
+ <button
20
+ onClick={handleCopy}
21
+ className="win95-btn"
22
+ style={{ fontSize: 11, padding: "3px 10px", minHeight: 24 }}
23
+ >
24
+ {copied ? "Copied!" : "Copy"}
25
+ </button>
26
+ );
27
+ }
@@ -0,0 +1,33 @@
1
+ import type { Metadata } from "next";
2
+ import { Inter, JetBrains_Mono } from "next/font/google";
3
+ import "@/app/globals.css";
4
+
5
+ // Isolated layout — no WindowManagerProvider, no Desktop/Taskbar chrome.
6
+ // Must define its own <html>/<body> to opt out of the root layout tree.
7
+
8
+ const inter = Inter({
9
+ subsets: ["latin"],
10
+ variable: "--font-inter",
11
+ display: "swap",
12
+ });
13
+
14
+ const jetbrainsMono = JetBrains_Mono({
15
+ subsets: ["latin"],
16
+ variable: "--font-jetbrains",
17
+ display: "swap",
18
+ weight: ["400", "500", "700"],
19
+ });
20
+
21
+ export const metadata: Metadata = {
22
+ title: "AdamOS — Get the App",
23
+ description:
24
+ "Open AdamOS in your browser or run it locally via the CLI. Windows 95-style portfolio by Adam Bush.",
25
+ };
26
+
27
+ export default function GetLayout({ children }: { children: React.ReactNode }) {
28
+ return (
29
+ <html lang="en" className={`${inter.variable} ${jetbrainsMono.variable}`}>
30
+ <body>{children}</body>
31
+ </html>
32
+ );
33
+ }
@@ -0,0 +1,332 @@
1
+ import CopyButton from "./CopyButton";
2
+
3
+ const LIVE_URL = "https://adam-bush.vercel.app/";
4
+
5
+ const COMMANDS = {
6
+ npxMac: "npx adam-os",
7
+ npxWin: "npx adam-os",
8
+ installMac:
9
+ "curl -fsSL https://raw.githubusercontent.com/iamwitness/portfolio-adam-bush-96/main/install.sh | bash",
10
+ installWin:
11
+ "irm https://raw.githubusercontent.com/iamwitness/portfolio-adam-bush-96/main/install.ps1 | iex",
12
+ };
13
+
14
+ // ── Sub-components ────────────────────────────────────────────────────────────
15
+
16
+ function CodeBlock({
17
+ label,
18
+ icon,
19
+ command,
20
+ }: {
21
+ label: string;
22
+ icon: string;
23
+ command: string;
24
+ }) {
25
+ return (
26
+ <div style={{ marginBottom: 10 }}>
27
+ <div
28
+ style={{
29
+ display: "flex",
30
+ alignItems: "center",
31
+ gap: 6,
32
+ marginBottom: 4,
33
+ fontSize: 11,
34
+ color: "var(--win95-text-disabled)",
35
+ fontFamily: "var(--font-body)",
36
+ fontWeight: 600,
37
+ textTransform: "uppercase",
38
+ letterSpacing: "0.05em",
39
+ }}
40
+ >
41
+ <span>{icon}</span>
42
+ {label}
43
+ </div>
44
+ <div
45
+ className="win95-inset"
46
+ style={{
47
+ display: "flex",
48
+ alignItems: "center",
49
+ justifyContent: "space-between",
50
+ gap: 8,
51
+ padding: "8px 10px",
52
+ background: "#1a1a1a",
53
+ borderColor: "#555",
54
+ }}
55
+ >
56
+ <code
57
+ style={{
58
+ fontFamily: "var(--font-mono)",
59
+ fontSize: 11,
60
+ color: "#d4f5a0",
61
+ wordBreak: "break-all",
62
+ flex: 1,
63
+ }}
64
+ >
65
+ {command}
66
+ </code>
67
+ <CopyButton text={command} />
68
+ </div>
69
+ </div>
70
+ );
71
+ }
72
+
73
+ function SectionLabel({ children }: { children: React.ReactNode }) {
74
+ return (
75
+ <p
76
+ style={{
77
+ fontSize: 11,
78
+ fontWeight: 700,
79
+ color: "var(--win95-text)",
80
+ marginBottom: 8,
81
+ display: "flex",
82
+ alignItems: "center",
83
+ gap: 6,
84
+ }}
85
+ >
86
+ {children}
87
+ </p>
88
+ );
89
+ }
90
+
91
+ function Divider() {
92
+ return (
93
+ <hr
94
+ style={{
95
+ border: "none",
96
+ borderTop: "1px solid var(--win95-border-dark)",
97
+ margin: "14px 0",
98
+ opacity: 0.4,
99
+ }}
100
+ />
101
+ );
102
+ }
103
+
104
+ // ── Page ──────────────────────────────────────────────────────────────────────
105
+
106
+ export default function GetPage() {
107
+ return (
108
+ <div
109
+ style={{
110
+ width: "100vw",
111
+ height: "100vh",
112
+ display: "flex",
113
+ alignItems: "center",
114
+ justifyContent: "center",
115
+ background: "var(--win95-bg)",
116
+ padding: "16px",
117
+ overflow: "auto",
118
+ }}
119
+ >
120
+ {/* Window chrome */}
121
+ <div
122
+ style={{
123
+ background: "var(--win95-chrome)",
124
+ borderRadius: "var(--radius-md)",
125
+ boxShadow: "var(--shadow-window)",
126
+ width: "100%",
127
+ maxWidth: 860,
128
+ overflow: "hidden",
129
+ }}
130
+ >
131
+ {/* Title bar */}
132
+ <div className="win95-titlebar">
133
+ <span style={{ fontSize: 16, lineHeight: 1 }}>🖥</span>
134
+ <span className="win95-titlebar-title">
135
+ AdamOS — Choose Your Experience
136
+ </span>
137
+ </div>
138
+
139
+ {/* Body */}
140
+ <div style={{ padding: "20px 20px 16px" }}>
141
+ <p
142
+ style={{
143
+ fontSize: "var(--font-size-sm)",
144
+ marginBottom: 16,
145
+ color: "var(--win95-text-disabled)",
146
+ }}
147
+ >
148
+ How would you like to run AdamOS?
149
+ </p>
150
+
151
+ {/* Two-panel layout */}
152
+ <div
153
+ style={{
154
+ display: "grid",
155
+ gridTemplateColumns: "1fr 1fr",
156
+ gap: 12,
157
+ }}
158
+ className="get-panels"
159
+ >
160
+ {/* ── Left: Open in Browser ── */}
161
+ <div
162
+ className="win95-raised"
163
+ style={{
164
+ padding: "20px",
165
+ display: "flex",
166
+ flexDirection: "column",
167
+ gap: 12,
168
+ background: "var(--win95-window-bg)",
169
+ }}
170
+ >
171
+ <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
172
+ <span style={{ fontSize: 24 }}>🌐</span>
173
+ <div>
174
+ <p
175
+ style={{
176
+ fontWeight: 700,
177
+ fontSize: "var(--font-size-base)",
178
+ }}
179
+ >
180
+ Open in Browser
181
+ </p>
182
+ <p
183
+ style={{
184
+ fontSize: 11,
185
+ color: "var(--win95-text-disabled)",
186
+ marginTop: 2,
187
+ }}
188
+ >
189
+ No install required
190
+ </p>
191
+ </div>
192
+ </div>
193
+
194
+ <p
195
+ style={{
196
+ fontSize: "var(--font-size-sm)",
197
+ color: "var(--win95-text)",
198
+ lineHeight: 1.5,
199
+ }}
200
+ >
201
+ The full Win95 desktop experience, live in your browser.
202
+ Nothing to install.
203
+ </p>
204
+
205
+ <a
206
+ href={LIVE_URL}
207
+ target="_blank"
208
+ rel="noopener noreferrer"
209
+ className="win95-btn"
210
+ style={{
211
+ marginTop: "auto",
212
+ textDecoration: "none",
213
+ background: "var(--win95-highlight)",
214
+ color: "var(--win95-highlight-text)",
215
+ fontWeight: 600,
216
+ fontSize: "var(--font-size-sm)",
217
+ padding: "8px 20px",
218
+ justifyContent: "center",
219
+ }}
220
+ >
221
+ Launch AdamOS →
222
+ </a>
223
+ </div>
224
+
225
+ {/* ── Right: Run Locally ── */}
226
+ <div
227
+ className="win95-raised"
228
+ style={{
229
+ padding: "20px",
230
+ display: "flex",
231
+ flexDirection: "column",
232
+ gap: 0,
233
+ background: "var(--win95-window-bg)",
234
+ }}
235
+ >
236
+ <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 14 }}>
237
+ <span style={{ fontSize: 24 }}>💻</span>
238
+ <div>
239
+ <p
240
+ style={{
241
+ fontWeight: 700,
242
+ fontSize: "var(--font-size-base)",
243
+ }}
244
+ >
245
+ Run Locally
246
+ </p>
247
+ <p
248
+ style={{
249
+ fontSize: 11,
250
+ color: "var(--win95-text-disabled)",
251
+ marginTop: 2,
252
+ }}
253
+ >
254
+ Runs on your machine
255
+ </p>
256
+ </div>
257
+ </div>
258
+
259
+ {/* Section A: Have Node.js */}
260
+ <SectionLabel>
261
+ <span
262
+ style={{
263
+ display: "inline-block",
264
+ width: 6,
265
+ height: 6,
266
+ borderRadius: "50%",
267
+ background: "#4caf50",
268
+ flexShrink: 0,
269
+ }}
270
+ />
271
+ Have Node.js ≥ 18? Run directly:
272
+ </SectionLabel>
273
+
274
+ <CodeBlock label="macOS · Terminal" icon="🍎" command={COMMANDS.npxMac} />
275
+ <CodeBlock label="Windows · PowerShell" icon="🪟" command={COMMANDS.npxWin} />
276
+
277
+ <Divider />
278
+
279
+ {/* Section B: Full installer */}
280
+ <SectionLabel>
281
+ <span
282
+ style={{
283
+ display: "inline-block",
284
+ width: 6,
285
+ height: 6,
286
+ borderRadius: "50%",
287
+ background: "#ff9800",
288
+ flexShrink: 0,
289
+ }}
290
+ />
291
+ Don&apos;t have Node.js? Full install:
292
+ </SectionLabel>
293
+
294
+ <CodeBlock
295
+ label="macOS · Terminal"
296
+ icon="🍎"
297
+ command={COMMANDS.installMac}
298
+ />
299
+ <CodeBlock
300
+ label="Windows · PowerShell"
301
+ icon="🪟"
302
+ command={COMMANDS.installWin}
303
+ />
304
+ </div>
305
+ </div>
306
+
307
+ {/* Footer */}
308
+ <p
309
+ style={{
310
+ fontSize: 11,
311
+ color: "var(--win95-text-disabled)",
312
+ marginTop: 12,
313
+ textAlign: "center",
314
+ }}
315
+ >
316
+ CLI mode requires Node.js ≥ 18 · The full installer handles
317
+ everything automatically.
318
+ </p>
319
+ </div>
320
+ </div>
321
+
322
+ {/* Responsive: stack panels on small screens */}
323
+ <style>{`
324
+ @media (max-width: 600px) {
325
+ .get-panels {
326
+ grid-template-columns: 1fr !important;
327
+ }
328
+ }
329
+ `}</style>
330
+ </div>
331
+ );
332
+ }
package/bin/adam-os.js CHANGED
@@ -116,7 +116,7 @@ function printBanner(url, mode) {
116
116
  async function main() {
117
117
  checkNodeVersion();
118
118
 
119
- const isProd = process.argv.includes("--prod");
119
+ const isDev = process.argv.includes("--dev");
120
120
  const portArg = process.argv.find((a) => a.startsWith("--port="));
121
121
  const basePort = portArg ? parseInt(portArg.split("=")[1], 10) : 3000;
122
122
 
@@ -137,12 +137,19 @@ async function main() {
137
137
  process.on("SIGINT", () => process.exit(0));
138
138
  process.on("SIGTERM", () => process.exit(0));
139
139
 
140
- if (isProd) {
140
+ if (isDev) {
141
+ printBanner(url, "Development (live reload)");
142
+ const server = spawn(nextBin, ["dev", "--port", String(port)], {
143
+ cwd: PKG_ROOT, stdio: "inherit", env,
144
+ });
145
+ setTimeout(() => openBrowser(url), 3500);
146
+ server.on("exit", (code) => process.exit(code ?? 0));
147
+ } else {
141
148
  console.log("\n Building production bundle (this takes ~30 seconds)...\n");
142
149
  try {
143
150
  execSync(`"${nextBin}" build`, { cwd: PKG_ROOT, stdio: "inherit", env });
144
151
  } catch (_) {
145
- console.error("\n ✗ Build failed. Try dev mode: npx adam-os\n");
152
+ console.error("\n ✗ Build failed. Try dev mode: npx adam-os --dev\n");
146
153
  process.exit(1);
147
154
  }
148
155
  printBanner(url, "Production");
@@ -151,13 +158,6 @@ async function main() {
151
158
  });
152
159
  setTimeout(() => openBrowser(url), 2000);
153
160
  server.on("exit", (code) => process.exit(code ?? 0));
154
- } else {
155
- printBanner(url, "Development (live reload)");
156
- const server = spawn(nextBin, ["dev", "--port", String(port)], {
157
- cwd: PKG_ROOT, stdio: "inherit", env,
158
- });
159
- setTimeout(() => openBrowser(url), 3500);
160
- server.on("exit", (code) => process.exit(code ?? 0));
161
161
  }
162
162
  }
163
163
 
@@ -29,16 +29,16 @@ export function ContactForm() {
29
29
  </div>
30
30
  ) : (
31
31
  <form
32
- onSubmit={(e) => {
33
- e.preventDefault();
34
- setSent(true);
35
- }}
32
+ action={`mailto:${siteConfig.email}`}
33
+ method="get"
34
+ encType="text/plain"
36
35
  style={{ display: "flex", flexDirection: "column", gap: 12 }}
37
36
  >
38
37
  <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
39
38
  <span>Name</span>
40
39
  <input
41
40
  type="text"
41
+ name="Name"
42
42
  className="win95-inset"
43
43
  required
44
44
  style={{
@@ -54,6 +54,7 @@ export function ContactForm() {
54
54
  <span>Email</span>
55
55
  <input
56
56
  type="email"
57
+ name="Email"
57
58
  className="win95-inset"
58
59
  required
59
60
  style={{
@@ -68,6 +69,7 @@ export function ContactForm() {
68
69
  <label style={{ display: "flex", flexDirection: "column", gap: 4 }}>
69
70
  <span>Message</span>
70
71
  <textarea
72
+ name="Message"
71
73
  className="win95-inset"
72
74
  required
73
75
  rows={5}