good work
This commit is contained in:
+345
-83
@@ -1,4 +1,4 @@
|
||||
export type LineColor = 'green' | 'amber' | 'red' | 'dim';
|
||||
export type LineColor = "green" | "amber" | "red" | "dim";
|
||||
|
||||
export interface BootLine {
|
||||
text: string;
|
||||
@@ -9,96 +9,358 @@ export interface BootLine {
|
||||
|
||||
export const BOOT_SEQUENCE: BootLine[] = [
|
||||
// Phase 1 - BIOS POST
|
||||
{ text: 'AWARD BIOS v6.9.420 (c) 2024 Stift15 Systems Ltd.', color: 'dim', delay: 300 },
|
||||
{ text: 'Performing Power-On Self Test...', color: 'dim', delay: 200 },
|
||||
{ text: '', color: 'dim', delay: 100 },
|
||||
{ text: 'CPU: AMD Ryzen 9 9950X "GamerFuel Edition" @ 5.7 GHz .......... OK', color: 'green', delay: 150 },
|
||||
{ text: 'RAM: 65536 MB DDR5-6000 ....................................... OK', color: 'green', delay: 120 },
|
||||
{ text: 'GPU: NVIDIA RTX 5090 "Mortgage Edition" ........................ OK', color: 'green', delay: 120 },
|
||||
{ text: 'RGB Subsystem .................................................. SYNCED (mood: aggressive)', color: 'green', delay: 100 },
|
||||
{ text: 'Gamer Chair Lumbar Support ..................................... RECLINED', color: 'green', delay: 100 },
|
||||
{ text: '', color: 'dim', delay: 80 },
|
||||
{ text: 'Detecting boot devices...', color: 'dim', delay: 400 },
|
||||
{ text: 'Boot device: /dev/sda1 (labeled "DO_NOT_DELETE_MOM")', color: 'dim', delay: 300 },
|
||||
{ text: '', color: 'dim', delay: 200 },
|
||||
{
|
||||
text: "AWARD BIOS v6.9.420 (c) 2024 Stift15 Systems Ltd.",
|
||||
color: "dim",
|
||||
delay: 300,
|
||||
},
|
||||
{ text: "Performing Power-On Self Test...", color: "dim", delay: 200 },
|
||||
{ text: "", color: "dim", delay: 100 },
|
||||
{
|
||||
text: 'CPU: AMD Ryzen 9 9950X "GamerFuel Edition" @ 5.7 GHz .......... OK',
|
||||
color: "green",
|
||||
delay: 150,
|
||||
},
|
||||
{
|
||||
text: "RAM: 65536 MB DDR5-6000 ....................................... OK",
|
||||
color: "green",
|
||||
delay: 120,
|
||||
},
|
||||
{
|
||||
text: 'GPU: NVIDIA RTX 5090 "Mortgage Edition" ........................ OK',
|
||||
color: "green",
|
||||
delay: 120,
|
||||
},
|
||||
{
|
||||
text: "RGB Subsystem .................................................. SYNCED (mood: aggressive)",
|
||||
color: "green",
|
||||
delay: 100,
|
||||
},
|
||||
{
|
||||
text: "Gamer Chair Lumbar Support ..................................... RECLINED",
|
||||
color: "green",
|
||||
delay: 100,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 80 },
|
||||
{ text: "Detecting boot devices...", color: "dim", delay: 400 },
|
||||
{
|
||||
text: 'Boot device: /dev/sda1 (labeled "DO_NOT_DELETE_MOM")',
|
||||
color: "dim",
|
||||
delay: 300,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 200 },
|
||||
|
||||
// Phase 2 - Kernel Boot
|
||||
{ text: 'Loading GRUB 2.12 ...', color: 'green', delay: 300 },
|
||||
{ text: 'kernel: Linux 6.9.0-gamer-generic loading...', color: 'green', delay: 200 },
|
||||
{ text: '[ 0.000000] Command line: root=/dev/sda1 ro quiet splash=off fps_unlock=true', color: 'dim', delay: 80 },
|
||||
{ text: '[ 0.004200] Initializing gaming subsystems...', color: 'dim', delay: 80 },
|
||||
{ text: '[ 0.013370] RGB controller mapped to /dev/rgb0', color: 'dim', delay: 60 },
|
||||
{ text: '[ 0.024601] Registering thermal zone: "CPU Package" (target: yes)', color: 'dim', delay: 60 },
|
||||
{ text: '[ 0.031337] Loading module: gamer_posture.ko', color: 'dim', delay: 60 },
|
||||
{ text: '[ 0.042069] Mounting /dev/fridge (read-only, sadness mode)', color: 'dim', delay: 60 },
|
||||
{ text: '', color: 'dim', delay: 200 },
|
||||
{ text: "Loading GRUB 2.12 ...", color: "green", delay: 300 },
|
||||
{
|
||||
text: "kernel: Linux 6.9.0-gamer-generic loading...",
|
||||
color: "green",
|
||||
delay: 200,
|
||||
},
|
||||
{
|
||||
text: "[ 0.000000] Command line: root=/dev/sda1 ro quiet splash=off fps_unlock=true",
|
||||
color: "dim",
|
||||
delay: 80,
|
||||
},
|
||||
{
|
||||
text: "[ 0.004200] Initializing gaming subsystems...",
|
||||
color: "dim",
|
||||
delay: 80,
|
||||
},
|
||||
{
|
||||
text: "[ 0.013370] RGB controller mapped to /dev/rgb0",
|
||||
color: "dim",
|
||||
delay: 60,
|
||||
},
|
||||
{
|
||||
text: '[ 0.024601] Registering thermal zone: "CPU Package" (target: yes)',
|
||||
color: "dim",
|
||||
delay: 60,
|
||||
},
|
||||
{
|
||||
text: "[ 0.031337] Loading module: gamer_posture.ko",
|
||||
color: "dim",
|
||||
delay: 60,
|
||||
},
|
||||
{
|
||||
text: "[ 0.042069] Mounting /dev/fridge (read-only, sadness mode)",
|
||||
color: "dim",
|
||||
delay: 60,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 200 },
|
||||
|
||||
// Phase 3 - Services Starting
|
||||
{ text: '[ OK ] Started Gamer Posture Reminder Service', color: 'green', delay: 150 },
|
||||
{ text: '[ OK ] Started Mountain Dew Level Monitor', color: 'green', delay: 120 },
|
||||
{ text: '[ OK ] Started Discord Rich Presence Daemon', color: 'green', delay: 120 },
|
||||
{ text: '[ OK ] Started Mechanical Keyboard Click Amplifier', color: 'green', delay: 100 },
|
||||
{ text: '[ OK ] Started Rage Quit Prevention System (v0.1-beta)', color: 'green', delay: 100 },
|
||||
{ text: '[WARN] Hot Pocket Proximity Sensor returned NaN', color: 'amber', delay: 300 },
|
||||
{ text: '[ OK ] Started Frank\'s "Temporary" Minecraft Server (uptime: 847 days)', color: 'green', delay: 150 },
|
||||
{ text: '[ OK ] Started nginx web server on port 80', color: 'green', delay: 100 },
|
||||
{ text: '[ OK ] Started Node.js application server on port 3000', color: 'green', delay: 100 },
|
||||
{ text: '[WARN] server.js has mass of 2.1 GB (node_modules singularity detected)', color: 'amber', delay: 400 },
|
||||
{ text: '[WARN] Gravitational pull from node_modules is affecting nearby files', color: 'amber', delay: 200 },
|
||||
{ text: '', color: 'dim', delay: 300 },
|
||||
{
|
||||
text: "[ OK ] Started Gamer Posture Reminder Service",
|
||||
color: "green",
|
||||
delay: 150,
|
||||
},
|
||||
{
|
||||
text: "[ OK ] Started Mountain Dew Level Monitor",
|
||||
color: "green",
|
||||
delay: 120,
|
||||
},
|
||||
{
|
||||
text: "[ OK ] Started Teamspeak Rich Presence Daemon",
|
||||
color: "green",
|
||||
delay: 120,
|
||||
},
|
||||
{
|
||||
text: "[ OK ] Started Mechanical Keyboard Click Amplifier",
|
||||
color: "green",
|
||||
delay: 100,
|
||||
},
|
||||
{
|
||||
text: "[ OK ] Started Rage Quit Prevention System (v0.1-beta)",
|
||||
color: "green",
|
||||
delay: 100,
|
||||
},
|
||||
{
|
||||
text: "[WARN] Hot Pocket Proximity Sensor returned NaN",
|
||||
color: "amber",
|
||||
delay: 300,
|
||||
},
|
||||
{
|
||||
text: '[ OK ] Started Frank\'s "Temporary" Minecraft Server (uptime: 847 days)',
|
||||
color: "green",
|
||||
delay: 150,
|
||||
},
|
||||
{
|
||||
text: "[ OK ] Started nginx web server on port 80",
|
||||
color: "green",
|
||||
delay: 100,
|
||||
},
|
||||
{
|
||||
text: "[ OK ] Started Node.js application server on port 3000",
|
||||
color: "green",
|
||||
delay: 100,
|
||||
},
|
||||
{
|
||||
text: "[WARN] server.js has mass of 2.1 GB (node_modules singularity detected)",
|
||||
color: "amber",
|
||||
delay: 400,
|
||||
},
|
||||
{
|
||||
text: "[WARN] Gravitational pull from node_modules is affecting nearby files",
|
||||
color: "amber",
|
||||
delay: 200,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 300 },
|
||||
|
||||
// Phase 4 - The Crash
|
||||
{ text: '[FAIL] HTTP Health Check: GET /api/status returned... nothing', color: 'red', delay: 600 },
|
||||
{ text: '[FAIL] The backend is not responding.', color: 'red', delay: 400 },
|
||||
{ text: '[FAIL] The backend has never responded. To anything. Ever.', color: 'red', delay: 500 },
|
||||
{ text: '', color: 'dim', delay: 200 },
|
||||
{ text: '[WARN] Attempting emergency restart...', color: 'amber', delay: 800 },
|
||||
{ text: '[ 4.040404] Process \'server.js\' invoked OOM killer', color: 'red', delay: 300 },
|
||||
{ text: '[ 4.040405] OOM killer selected process \'stift15-game-server\' (adj 1000)', color: 'red', delay: 150 },
|
||||
{ text: '[ 4.040406] Out of memory: Killed process 1337 (stift15-game-server)', color: 'red', delay: 150 },
|
||||
{ text: '[ 4.040407] Reason: Frank downloaded the entire Steam Workshop into /tmp', color: 'red', delay: 300 },
|
||||
{ text: '', color: 'dim', delay: 200 },
|
||||
{ text: '[FAIL] Emergency restart failed. Exit code: 418 (I\'m a teapot)', color: 'red', delay: 600 },
|
||||
{ text: '', color: 'dim', delay: 800 },
|
||||
{
|
||||
text: "[FAIL] HTTP Health Check: GET /api/status returned... nothing",
|
||||
color: "red",
|
||||
delay: 600,
|
||||
},
|
||||
{ text: "[FAIL] The backend is not responding.", color: "red", delay: 400 },
|
||||
{
|
||||
text: "[FAIL] The backend has never responded. To anything. Ever.",
|
||||
color: "red",
|
||||
delay: 500,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 200 },
|
||||
{
|
||||
text: "[WARN] Attempting emergency restart...",
|
||||
color: "amber",
|
||||
delay: 800,
|
||||
},
|
||||
{
|
||||
text: "[ 4.040404] Process 'server.js' invoked OOM killer",
|
||||
color: "red",
|
||||
delay: 300,
|
||||
},
|
||||
{
|
||||
text: "[ 4.040405] OOM killer selected process 'stift15-game-server' (adj 1000)",
|
||||
color: "red",
|
||||
delay: 150,
|
||||
},
|
||||
{
|
||||
text: "[ 4.040406] Out of memory: Killed process 1337 (stift15-game-server)",
|
||||
color: "red",
|
||||
delay: 150,
|
||||
},
|
||||
{
|
||||
text: "[ 4.040407] Reason: Frank downloaded the entire Steam Workshop into /tmp",
|
||||
color: "red",
|
||||
delay: 300,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 200 },
|
||||
{
|
||||
text: "[FAIL] Emergency restart failed. Exit code: 418 (I'm a teapot)",
|
||||
color: "red",
|
||||
delay: 600,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 800 },
|
||||
|
||||
// Phase 5 - The 503 Error (typewriter lines)
|
||||
{ text: '============================================================', color: 'red', delay: 400, typewriter: true },
|
||||
{ text: ' ERROR 503 - SERVICE UNAVAILABLE', color: 'red', delay: 100, typewriter: true },
|
||||
{ text: '============================================================', color: 'red', delay: 100, typewriter: true },
|
||||
{ text: '', color: 'dim', delay: 200 },
|
||||
{ text: ' The server hosting the Stift15 gaming group has encountered', color: 'green', delay: 60, typewriter: true },
|
||||
{ text: ' a fatal error and is currently taking a rage-quit break.', color: 'green', delay: 60, typewriter: true },
|
||||
{ text: '', color: 'dim', delay: 300 },
|
||||
{ text: ' DIAGNOSTIC SUMMARY:', color: 'amber', delay: 200, typewriter: true },
|
||||
{ text: ' - Last stable connection: "lol"', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' - Uptime before crash: 4h 20m 69s', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' - Packets lost: all of them', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' - Root cause: someone git pushed node_modules to prod', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' - Secondary cause: Frank', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' - Tertiary cause: also Frank', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' - Frank\'s response: "works on my machine"', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: '', color: 'dim', delay: 300 },
|
||||
{ text: ' RECOMMENDED ACTIONS:', color: 'amber', delay: 200, typewriter: true },
|
||||
{ text: ' 1. Touch grass (estimated ETA: never)', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' 2. Check if Frank pushed to main again', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' 3. Blame the lag', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' 4. Alt+F4 your expectations', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: ' 5. Have you tried turning Frank off and on again?', color: 'green', delay: 80, typewriter: true },
|
||||
{ text: '', color: 'dim', delay: 300 },
|
||||
{ text: ' If this error persists, please scream into the void or', color: 'green', delay: 60, typewriter: true },
|
||||
{ text: ' message the server admin, who is also screaming into the void.', color: 'green', delay: 60, typewriter: true },
|
||||
{ text: '', color: 'dim', delay: 400 },
|
||||
{ text: '============================================================', color: 'red', delay: 100, typewriter: true },
|
||||
{ text: ' SERVER ADMIN NOTE:', color: 'amber', delay: 200, typewriter: true },
|
||||
{ text: ' No, I will not fix this at 3 AM. I have work tomorrow.', color: 'green', delay: 60, typewriter: true },
|
||||
{ text: ' The server can stay dead. It builds character.', color: 'green', delay: 60, typewriter: true },
|
||||
{ text: ' Also Frank still owes me for last month\'s hosting.', color: 'green', delay: 60, typewriter: true },
|
||||
{ text: '============================================================', color: 'red', delay: 100, typewriter: true },
|
||||
{ text: '', color: 'dim', delay: 1500 },
|
||||
{
|
||||
text: "============================================================",
|
||||
color: "red",
|
||||
delay: 400,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " ERROR 503 - SERVICE UNAVAILABLE",
|
||||
color: "red",
|
||||
delay: 100,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: "============================================================",
|
||||
color: "red",
|
||||
delay: 100,
|
||||
typewriter: true,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 200 },
|
||||
{
|
||||
text: " The server hosting Stift15.de has encountered",
|
||||
color: "green",
|
||||
delay: 60,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " a fatal error and is currently taking a rage-quit break.",
|
||||
color: "green",
|
||||
delay: 60,
|
||||
typewriter: true,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 300 },
|
||||
{
|
||||
text: " DIAGNOSTIC SUMMARY:",
|
||||
color: "amber",
|
||||
delay: 200,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: ' - Last stable connection: "lol"',
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " - Uptime before crash: 4h 20m 69s",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " - Packets lost: all of them",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " - Root cause: someone git pushed node_modules to prod",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " - Secondary cause: Frank",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " - Tertiary cause: also Frank",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: ' - Frank\'s response: "works on my machine"',
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 300 },
|
||||
{
|
||||
text: " RECOMMENDED ACTIONS:",
|
||||
color: "amber",
|
||||
delay: 200,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " 1. Touch grass (estimated ETA: never)",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " 2. Check if Frank pushed to main again",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{ text: " 3. Blame the lag", color: "green", delay: 80, typewriter: true },
|
||||
{
|
||||
text: " 4. Alt+F4 your expectations",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " 5. Have you tried turning Frank off and on again?",
|
||||
color: "green",
|
||||
delay: 80,
|
||||
typewriter: true,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 300 },
|
||||
{
|
||||
text: " If this error persists, please scream into the void or",
|
||||
color: "green",
|
||||
delay: 60,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " message the server admin, who is also screaming into the void.",
|
||||
color: "green",
|
||||
delay: 60,
|
||||
typewriter: true,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 400 },
|
||||
{
|
||||
text: "============================================================",
|
||||
color: "red",
|
||||
delay: 100,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " SERVER ADMIN NOTE:",
|
||||
color: "amber",
|
||||
delay: 200,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " No, I will not fix this at 3 AM. I have work tomorrow.",
|
||||
color: "green",
|
||||
delay: 60,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " The server can stay dead. It builds character.",
|
||||
color: "green",
|
||||
delay: 60,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: " Also Frank still owes me for last month's hosting.",
|
||||
color: "green",
|
||||
delay: 60,
|
||||
typewriter: true,
|
||||
},
|
||||
{
|
||||
text: "============================================================",
|
||||
color: "red",
|
||||
delay: 100,
|
||||
typewriter: true,
|
||||
},
|
||||
{ text: "", color: "dim", delay: 1500 },
|
||||
|
||||
// Phase 6 - Reconnect
|
||||
{ text: '> Reconnecting in 3 seconds...', color: 'dim', delay: 0 },
|
||||
{ text: '> ...', color: 'dim', delay: 1000 },
|
||||
{ text: '> ...', color: 'dim', delay: 1000 },
|
||||
{ text: '> CONNECTION ESTABLISHED', color: 'green', delay: 1000 },
|
||||
{ text: "> Reconnecting in 3 seconds...", color: "dim", delay: 0 },
|
||||
{ text: "> ...", color: "dim", delay: 1000 },
|
||||
{ text: "> ...", color: "dim", delay: 1000 },
|
||||
{ text: "> CONNECTION ESTABLISHED", color: "green", delay: 1000 },
|
||||
];
|
||||
|
||||
@@ -20,7 +20,7 @@ export function CommandPrompt({ onReboot, onBrick }: CommandPromptProps) {
|
||||
const [input, setInput] = useState('');
|
||||
const [history, setHistory] = useState<string[]>([]);
|
||||
const [historyIndex, setHistoryIndex] = useState(-1);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scrollToBottom = useCallback(() => {
|
||||
@@ -29,9 +29,13 @@ export function CommandPrompt({ onReboot, onBrick }: CommandPromptProps) {
|
||||
|
||||
useEffect(scrollToBottom, [outputLines.length, scrollToBottom]);
|
||||
|
||||
// Focus container on mount
|
||||
// Keep input focused
|
||||
useEffect(() => {
|
||||
containerRef.current?.focus();
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const focusInput = useCallback(() => {
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const executeCommand = useCallback(
|
||||
@@ -82,12 +86,11 @@ export function CommandPrompt({ onReboot, onBrick }: CommandPromptProps) {
|
||||
);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
executeCommand(input);
|
||||
setInput('');
|
||||
} else if (e.key === 'Backspace') {
|
||||
setInput((prev) => prev.slice(0, -1));
|
||||
setHistoryIndex(-1);
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (history.length === 0) return;
|
||||
@@ -105,8 +108,6 @@ export function CommandPrompt({ onReboot, onBrick }: CommandPromptProps) {
|
||||
setHistoryIndex(newIndex);
|
||||
setInput(history[newIndex]);
|
||||
}
|
||||
} else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
|
||||
setInput((prev) => prev + e.key);
|
||||
}
|
||||
},
|
||||
[input, executeCommand, history, historyIndex]
|
||||
@@ -121,11 +122,8 @@ export function CommandPrompt({ onReboot, onBrick }: CommandPromptProps) {
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="absolute inset-0 z-0 overflow-y-auto p-4 font-mono text-sm outline-none"
|
||||
tabIndex={0}
|
||||
onKeyDown={handleKeyDown}
|
||||
onClick={() => containerRef.current?.focus()}
|
||||
className="absolute inset-0 z-0 overflow-y-auto p-4 font-mono text-sm outline-none cursor-text"
|
||||
onClick={focusInput}
|
||||
>
|
||||
{outputLines.map((line, i) => (
|
||||
<div
|
||||
@@ -137,13 +135,27 @@ export function CommandPrompt({ onReboot, onBrick }: CommandPromptProps) {
|
||||
))}
|
||||
|
||||
{/* Input line */}
|
||||
<div className="text-term-green whitespace-pre leading-[1.4]">
|
||||
{PROMPT}
|
||||
{input}
|
||||
<div className="text-term-green whitespace-pre leading-[1.4] flex items-center">
|
||||
<span>{PROMPT}</span>
|
||||
<span>{input}</span>
|
||||
<span
|
||||
className="inline-block w-[0.6em] h-[1em] bg-term-green ml-0.5 align-middle"
|
||||
className="inline-block w-[0.6em] h-[1em] bg-term-green ml-0.5"
|
||||
style={{ animation: 'blink 1s step-end infinite' }}
|
||||
/>
|
||||
{/* Hidden native input for reliable focus and mobile keyboard support */}
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={focusInput}
|
||||
className="absolute opacity-0 w-0 h-0"
|
||||
autoCapitalize="off"
|
||||
autoCorrect="off"
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div ref={bottomRef} />
|
||||
|
||||
Reference in New Issue
Block a user