404lab 2.0.1 → 2.0.2

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.
@@ -2,264 +2,168 @@
2
2
 
3
3
  import { useEffect, useRef, useState, KeyboardEvent } from "react";
4
4
  import { useRouter } from "next/navigation";
5
- import Link from "next/link";
5
+ import { motion, AnimatePresence } from "framer-motion";
6
+ import { cn } from "@/components/ui/cn";
6
7
 
7
8
  type OutputLine = {
8
- type: "command" | "output" | "error";
9
+ type: "command" | "output" | "error" | "system" | "warning";
9
10
  text: string;
10
11
  };
11
12
 
12
- type CommandKey =
13
- | "help"
14
- | "exit"
15
- | "close"
16
- | "report"
17
- | "commands"
18
- | "clear"
19
- | "cls"
20
- | "about"
21
- | "date"
22
- | "echo";
23
-
24
- const COMMANDS: Record<
25
- Exclude<CommandKey, "exit" | "close" | "clear" | "cls" | "date" | "echo">,
26
- string
27
- > = {
28
- help: `This page does not exist. Type <span class="text-yellow-300">commands</span> to see available commands.`,
29
- report: `<span class="text-green-400">Report sent successfully.</span>`,
30
- commands: `
31
- <span class="text-yellow-300">help</span>,
32
- <span class="text-yellow-300">about</span>,
33
- <span class="text-yellow-300">date</span>,
34
- <span class="text-yellow-300">echo</span>,
35
- <span class="text-yellow-300">clear</span>,
36
- <span class="text-yellow-300">exit</span>
37
- `,
38
- about: `<span class="text-cyan-400">Terminal 404</span> — Interactive MacOS-style terminal.`,
39
- };
40
-
41
- const bootScript = [
42
- { text: "user@macbook ~ % pnpm run dev", tone: "text-emerald-400" },
43
- { text: "> Starting development server...", tone: "text-white/60" },
44
- { text: "✖ Error: Page Not Found (404)", tone: "text-red-400 font-semibold" },
45
- ];
46
-
47
- const MacOs = () => {
13
+ const MacOs = ({ className }: { className?: string }) => {
48
14
  const router = useRouter();
49
15
  const inputRef = useRef<HTMLInputElement>(null);
50
16
  const terminalRef = useRef<HTMLDivElement>(null);
51
17
 
52
- const [printed, setPrinted] = useState<string[]>([]);
53
- const [typing, setTyping] = useState("");
54
- const [line, setLine] = useState(0);
55
- const [char, setChar] = useState(0);
56
18
  const [bootDone, setBootDone] = useState(false);
57
-
58
19
  const [input, setInput] = useState("");
59
- const [output, setOutput] = useState<OutputLine[]>([]);
20
+ const [output, setOutput] = useState<OutputLine[]>([
21
+ { type: "system", text: "Last login: " + new Date().toUTCString().split(' ').slice(0, 4).join(' ') + " on ttys001" },
22
+ { type: "system", text: "Restoring session... " },
23
+ { type: "error", text: "zsh: error 404: route not found" },
24
+ { type: "warning", text: "Available commands: help, clear, exit, date, whoami, ls, sudo" }
25
+ ]);
60
26
 
61
27
  useEffect(() => {
62
- if (line >= bootScript.length) {
63
- const t = setTimeout(() => {
64
- setBootDone(true);
65
- inputRef.current?.focus();
66
- }, 400);
67
- return () => clearTimeout(t);
68
- }
28
+ const timer = setTimeout(() => setBootDone(true), 1200);
29
+ return () => clearTimeout(timer);
30
+ }, []);
69
31
 
70
- if (char < bootScript[line].text.length) {
71
- const t = setTimeout(() => {
72
- setTyping((p) => p + bootScript[line].text[char]);
73
- setChar((c) => c + 1);
74
- }, 26);
75
- return () => clearTimeout(t);
32
+ useEffect(() => {
33
+ if (bootDone && inputRef.current && window.self === window.top) {
34
+ inputRef.current.focus();
76
35
  }
77
-
78
- const t = setTimeout(() => {
79
- setPrinted((p) => [...p, typing]);
80
- setTyping("");
81
- setChar(0);
82
- setLine((l) => l + 1);
83
- }, 350);
84
-
85
- return () => clearTimeout(t);
86
- }, [char, line, typing]);
36
+ }, [bootDone]);
87
37
 
88
38
  useEffect(() => {
89
- terminalRef.current?.scrollTo({
90
- top: terminalRef.current.scrollHeight,
91
- behavior: "smooth",
92
- });
39
+ if (terminalRef.current) {
40
+ terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
41
+ }
93
42
  }, [output]);
94
43
 
95
44
  const runCommand = (cmd: string) => {
96
- const trimmed = cmd.trim();
45
+ const trimmed = cmd.trim().toLowerCase();
97
46
  if (!trimmed) return;
98
47
 
99
- const [raw, ...rest] = trimmed.split(" ");
100
- const command = raw.toLowerCase() as CommandKey;
101
- const args = rest.join(" ");
102
-
103
48
  setOutput((o) => [...o, { type: "command", text: cmd }]);
104
49
 
105
- if (command === "clear" || command === "cls") {
106
- setOutput([]);
107
- setInput("");
108
- return;
50
+ const commands: Record<string, () => void> = {
51
+ help: () => setOutput(o => [...o, { type: "output", text: "Available commands: help, clear, exit, date, whoami, ls, sudo" }]),
52
+ clear: () => setOutput([]),
53
+ exit: () => router.push("/"),
54
+ date: () => setOutput(o => [...o, { type: "output", text: new Date().toString() }]),
55
+ whoami: () => setOutput(o => [...o, { type: "output", text: "guest@macbook-pro" }]),
56
+ ls: () => setOutput(o => [...o, { type: "output", text: "Applications Documents Downloads Public Desktop .hidden_404_key" }]),
57
+ sudo: () => setOutput(o => [...o, { type: "error", text: "Nice try, but you don't have root access to this void." }]),
58
+ };
59
+
60
+ if (commands[trimmed]) {
61
+ commands[trimmed]();
62
+ } else {
63
+ setOutput(o => [...o, { type: "error", text: `zsh: command not found: ${trimmed}` }]);
109
64
  }
110
-
111
- if (command === "exit" || command === "close") {
112
- router.push("/");
113
- return;
114
- }
115
-
116
- if (command === "date") {
117
- setOutput((o) => [
118
- ...o,
119
- { type: "output", text: new Date().toLocaleString() },
120
- ]);
121
- setInput("");
122
- return;
123
- }
124
-
125
- if (command === "echo") {
126
- setOutput((o) => [...o, { type: "output", text: args }]);
127
- setInput("");
128
- return;
129
- }
130
-
131
- if (!(command in COMMANDS)) {
132
- setOutput((o) => [
133
- ...o,
134
- {
135
- type: "error",
136
- text: `Command not found. Type <span class="text-yellow-300">commands</span>.`,
137
- },
138
- ]);
139
- setInput("");
140
- return;
141
- }
142
-
143
- setOutput((o) => [
144
- ...o,
145
- { type: "output", text: COMMANDS[command as keyof typeof COMMANDS] },
146
- ]);
147
-
148
65
  setInput("");
149
66
  };
150
67
 
151
- const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
152
- if (e.key === "Enter") {
153
- e.preventDefault();
154
- runCommand(input);
155
- }
156
-
157
- if (e.ctrlKey && e.key.toLowerCase() === "l") {
158
- e.preventDefault();
159
- setOutput([]);
160
- setInput("");
161
- }
162
- };
163
-
164
68
  return (
165
- <main className="min-h-screen flex items-center justify-center bg-[#0b0b0f] px-2 sm:px-4 font-mono text-[#d4d4d4]">
166
- <div className="w-full max-w-5xl rounded-2xl overflow-hidden border border-white/10 shadow-[0_60px_160px_-30px_rgba(0,0,0,0.9)]">
167
- <div className="flex items-center gap-2 px-3 sm:px-4 py-2 bg-[#2a2a2e] border-b border-white/10">
168
- <span className="h-3 w-3 rounded-full bg-red-500" />
169
- <span className="h-3 w-3 rounded-full bg-yellow-400" />
170
- <span className="h-3 w-3 rounded-full bg-green-500" />
171
- <span className="ml-2 sm:ml-4 text-xs sm:text-sm text-white/60">
172
- user@MacBook-Pro — zsh
173
- </span>
174
- </div>
175
-
176
- <div
177
- ref={terminalRef}
178
- onClick={() => inputRef.current?.focus()}
179
- className="p-3 sm:p-6 bg-[#0f0f14] text-xs sm:text-sm space-y-2 cursor-text min-h-[60vh] max-h-[80vh] overflow-y-auto"
180
- >
181
- {printed.map((l, i) => (
182
- <p key={i} className={bootScript[i].tone}>
183
- {l}
184
- </p>
185
- ))}
186
-
187
- {!bootDone && (
188
- <p className={bootScript[line]?.tone}>
189
- {typing}
190
- <span className="ml-1 inline-block h-4 w-2 bg-current animate-pulse" />
191
- </p>
192
- )}
193
-
194
- {bootDone && (
195
- <>
196
- <p className="text-white/50 italic">
197
- Tip: type{" "}
198
- <span className="text-yellow-300 not-italic">commands</span> to
199
- see available options
200
- </p>
69
+ <main className={cn("min-h-screen flex items-center justify-center bg-[#0d1117] relative overflow-hidden font-mono", className)}>
70
+ <div className="absolute inset-0 z-0">
71
+ <div className="absolute top-[-10%] right-[-10%] w-[500px] h-[500px] bg-purple-600/10 rounded-full blur-[120px]" />
72
+ <div className="absolute bottom-[-10%] left-[-10%] w-[500px] h-[500px] bg-blue-600/10 rounded-full blur-[120px]" />
73
+ </div>
201
74
 
75
+ <motion.div
76
+ initial={{ opacity: 0, y: 40, scale: 0.95 }}
77
+ animate={{ opacity: 1, y: 0, scale: 1 }}
78
+ transition={{ duration: 0.8, ease: [0.16, 1, 0.3, 1] }}
79
+ className="w-full max-w-4xl mx-4 z-10"
80
+ >
81
+ <div className="rounded-xl overflow-hidden shadow-[0_30px_100px_rgba(0,0,0,0.6)] border border-white/10 backdrop-blur-3xl bg-[#1c1c1e]/80">
82
+ <div className="flex items-center justify-between px-4 py-3 bg-white/5 border-b border-white/5">
83
+ <div className="flex items-center gap-2">
84
+ <div className="w-3.5 h-3.5 rounded-full bg-[#ff5f57] shadow-inner" />
85
+ <div className="w-3.5 h-3.5 rounded-full bg-[#febc2e] shadow-inner" />
86
+ <div className="w-3.5 h-3.5 rounded-full bg-[#28c840] shadow-inner" />
87
+ </div>
88
+ <div className="text-[13px] text-white/40 font-medium tracking-wide">
89
+ guest — zsh — 80×24
90
+ </div>
91
+ <div className="w-12" />
92
+ </div>
93
+
94
+ <div
95
+ ref={terminalRef}
96
+ onClick={() => inputRef.current?.focus()}
97
+ className="p-6 h-[500px] overflow-y-auto custom-scrollbar text-[14px] leading-relaxed selection:bg-blue-500/30"
98
+ >
99
+ <AnimatePresence>
202
100
  {output.map((line, i) => (
203
- <div key={i}>
101
+ <motion.div
102
+ key={i}
103
+ initial={{ opacity: 0, x: -5 }}
104
+ animate={{ opacity: 1, x: 0 }}
105
+ className="mb-1"
106
+ >
204
107
  {line.type === "command" && (
205
- <p className="text-emerald-400">
206
- user@macbook ~ % {line.text}
207
- </p>
108
+ <div className="flex items-center gap-2">
109
+ <span className="text-[#32d74b] font-bold">➜</span>
110
+ <span className="text-[#64d2ff] font-bold">~</span>
111
+ <span className="text-white">{line.text}</span>
112
+ </div>
113
+ )}
114
+ {line.type === "output" && (
115
+ <div className="text-white/80 pl-6">{line.text}</div>
116
+ )}
117
+ {line.type === "system" && (
118
+ <div className="text-white/40 italic">{line.text}</div>
208
119
  )}
209
- {line.type !== "command" && (
210
- <p
211
- className={
212
- line.type === "error" ? "text-red-400" : "text-white/70"
213
- }
214
- dangerouslySetInnerHTML={{ __html: line.text }}
215
- />
120
+ {line.type === "error" && (
121
+ <div className="text-[#ff453a] pl-6 font-medium">{line.text}</div>
216
122
  )}
217
- </div>
123
+ {line.type === "warning" && (
124
+ <div className="text-[#febc2e] pl-6">{line.text}</div>
125
+ )}
126
+ </motion.div>
218
127
  ))}
128
+ </AnimatePresence>
219
129
 
220
- <div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 sm:gap-2 text-emerald-400">
221
- <span className="whitespace-nowrap">user@macbook ~ %</span>
130
+ {bootDone && (
131
+ <div className="flex items-center gap-2 mt-1">
132
+ <span className="text-[#32d74b] font-bold">➜</span>
133
+ <span className="text-[#64d2ff] font-bold">~</span>
222
134
  <input
223
135
  ref={inputRef}
224
136
  value={input}
225
137
  onChange={(e) => setInput(e.target.value)}
226
- onKeyDown={onKeyDown}
227
- autoFocus
138
+ onKeyDown={(e) => e.key === "Enter" && runCommand(input)}
139
+ className="bg-transparent border-none outline-none flex-1 text-white caret-[#32d74b]"
228
140
  spellCheck={false}
229
- className="
230
- bg-transparent
231
- border-0
232
- outline-none
233
- ring-0
234
- shadow-none
235
- focus:outline-none
236
- focus:ring-0
237
- focus:border-0
238
- appearance-none
239
- caret-emerald-400
240
- flex-1
241
- text-white
242
- min-w-0
243
- py-1
244
- text-xs sm:text-sm
245
- "
246
141
  />
247
142
  </div>
248
-
249
- <div className="mt-4 sm:mt-6">
250
- <Link
251
- href="/"
252
- className="text-sky-400 hover:underline text-base sm:text-lg"
253
- >
254
- → Return Home
255
- </Link>
256
- </div>
257
- </>
258
- )}
143
+ )}
144
+ </div>
259
145
  </div>
260
- </div>
146
+
147
+ </motion.div>
148
+
149
+ <style jsx>{`
150
+ .custom-scrollbar::-webkit-scrollbar {
151
+ width: 8px;
152
+ }
153
+ .custom-scrollbar::-webkit-scrollbar-track {
154
+ background: transparent;
155
+ }
156
+ .custom-scrollbar::-webkit-scrollbar-thumb {
157
+ background: rgba(255, 255, 255, 0.1);
158
+ border-radius: 10px;
159
+ }
160
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover {
161
+ background: rgba(255, 255, 255, 0.2);
162
+ }
163
+ `}</style>
261
164
  </main>
262
165
  );
263
166
  };
264
167
 
265
168
  export default MacOs;
169
+
@@ -1,20 +1,82 @@
1
+ "use client";
2
+
3
+ import { motion } from "framer-motion";
1
4
  import Link from "next/link";
5
+ import { cn } from "@/components/ui/cn";
2
6
 
3
- const ModernPage = () => {
7
+ const ModernPage = ({ className }: { className?: string }) => {
4
8
  return (
5
- <div className="flex min-h-screen items-center justify-center bg-background">
6
- <div className="surface rounded-2xl p-8 text-center">
7
- <h1 className="text-7xl font-extrabold tracking-tight">404</h1>
8
- <p className="mt-2 text-muted">This page could not be found.</p>
9
- <Link
10
- href="/"
11
- className="mt-6 inline-block rounded-lg bg-foreground px-4 py-2 text-background"
12
- >
13
- Back to home
14
- </Link>
9
+ <div
10
+ className={cn(
11
+ "min-h-screen bg-[#050505] flex items-center justify-center p-6 overflow-hidden relative font-sans",
12
+ className
13
+ )}
14
+ >
15
+ {/* Mesh Gradient Background */}
16
+ <div className="absolute inset-0 overflow-hidden pointer-events-none">
17
+ <div className="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-purple-900/20 blur-[120px] rounded-full animate-pulse" />
18
+ <div className="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] bg-blue-900/20 blur-[120px] rounded-full animate-pulse [animation-delay:2s]" />
15
19
  </div>
20
+
21
+ <motion.div
22
+ initial={{ opacity: 0, y: 20 }}
23
+ animate={{ opacity: 1, y: 0 }}
24
+ transition={{ duration: 0.8, ease: "easeOut" }}
25
+ className="relative z-10 w-full max-w-2xl"
26
+ >
27
+ <div className="bg-white/[0.03] backdrop-blur-xl border border-white/10 rounded-[2.5rem] p-12 md:p-20 text-center shadow-2xl overflow-hidden group">
28
+ <div className="absolute inset-0 bg-gradient-to-br from-white/[0.05] to-transparent pointer-events-none" />
29
+
30
+ <motion.div
31
+ initial={{ scale: 0.9, opacity: 0 }}
32
+ animate={{ scale: 1, opacity: 1 }}
33
+ transition={{ delay: 0.2, duration: 0.5 }}
34
+ className="relative inline-block mb-8"
35
+ >
36
+ <h1 className="text-[8rem] md:text-[12rem] font-black leading-none tracking-tighter bg-gradient-to-b from-white to-white/20 bg-clip-text text-transparent select-none">
37
+ 404
38
+ </h1>
39
+ <div className="absolute -inset-4 bg-white/5 blur-3xl rounded-full z-[-1] group-hover:bg-white/10 transition-colors" />
40
+ </motion.div>
41
+
42
+ <motion.div
43
+ initial={{ opacity: 0, y: 10 }}
44
+ animate={{ opacity: 1, y: 0 }}
45
+ transition={{ delay: 0.4 }}
46
+ >
47
+ <h2 className="text-2xl md:text-3xl font-bold text-white mb-4 tracking-tight">
48
+ Lost in the Digital Ether.
49
+ </h2>
50
+ <p className="text-gray-400 text-lg mb-12 max-w-md mx-auto leading-relaxed">
51
+ The page you are seeking has drifted beyond our reach.
52
+ Let&apos;s navigate you back to solid ground.
53
+ </p>
54
+
55
+ <div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
56
+ <Link
57
+ href="/"
58
+ className="w-full sm:w-auto px-10 py-4 bg-white text-black font-bold rounded-2xl hover:bg-gray-200 transition-all transform hover:-translate-y-1 active:scale-95 shadow-xl shadow-white/5"
59
+ >
60
+ Go Home
61
+ </Link>
62
+ <button className="w-full sm:w-auto px-10 py-4 border border-white/10 text-white font-bold rounded-2xl hover:bg-white/5 transition-all active:scale-95">
63
+ Take a Tour
64
+ </button>
65
+ </div>
66
+ </motion.div>
67
+ </div>
68
+
69
+ <div className="mt-12 flex justify-center gap-8 text-[10px] text-white/20 font-bold uppercase tracking-[0.3em]">
70
+ <span>Security Verified</span>
71
+ <span>•</span>
72
+ <span>Node 404_X</span>
73
+ <span>•</span>
74
+ <span>Stable Connection</span>
75
+ </div>
76
+ </motion.div>
16
77
  </div>
17
78
  );
18
79
  };
19
80
 
20
81
  export default ModernPage;
82
+