Split up the commands into individual files, made it smoother.

This commit is contained in:
Ronnie 2024-11-20 21:43:48 -05:00
parent 0749795395
commit 39b5d22512
5 changed files with 220 additions and 220 deletions

16
src/app/commands/help.tsx Normal file
View file

@ -0,0 +1,16 @@
const helpCommand = () => [
<div key="help">
<strong className="text-yellow-400">📖 Help Menu:</strong>
<ul className="mt-2 space-y-1">
<li>🆘 <strong className="text-green-400">help</strong>: Display this help menu.</li>
<li>📡 <strong className="text-green-400">socials</strong>: View my social links.</li>
<li>🌐 <strong className="text-green-400">selfhosted</strong>: View what I self host in my homelab.</li>
<li> <strong className="text-green-400">exit</strong>: Close the terminal (browser tab).</li>
</ul>
<p className="mt-4 text-gray-400">
💡 Pro Tip: Use <span className="text-green-400"></span> and <span className="text-green-400"></span> to navigate through your command history.
</p>
</div>,
];
export default helpCommand;

View file

@ -0,0 +1,39 @@
import dynamic from "next/dynamic";
// Self-hosted Services
const SiDocker = dynamic(() => import("react-icons/si").then((mod) => mod.SiDocker), { ssr: false });
const SiPlex = dynamic(() => import("react-icons/si").then((mod) => mod.SiPlex), { ssr: false });
const SiProxmox = dynamic(() => import("react-icons/si").then((mod) => mod.SiProxmox), { ssr: false });
const SiHomeassistant = dynamic(() => import("react-icons/si").then((mod) => mod.SiHomeassistant), { ssr: false });
const SiPaperlessngx = dynamic(() => import("react-icons/si").then((mod) => mod.SiPaperlessngx), { ssr: false });
const selfhostedCommand= (showHeader = true) => [
<div key="selfHosted">
{showHeader && (
<strong className="text-yellow-400"> SELF-HOSTED TOOLS</strong>
)}
<div className="text-white flex items-center mt-2">
<SiProxmox className="text-orange-500 mr-2"/>
Proxmox: Virtual machines and containers, all in one place.
</div>
<div className="text-white flex items-center mt-2">
<SiDocker className="text-blue-500 mr-2"/>
Portainer: Orchestrating Docker containers effortlessly.
</div>
<div className="text-white flex items-center mt-2">
<SiPlex className="text-blue-400 mr-2"/>
Plex: Streaming my curated media library.
</div>
<div className="text-white flex items-center mt-2">
<SiHomeassistant className="text-teal-400 mr-2"/>
Home Assistant: Automating everything smart at home.
</div>
<div className="text-white flex items-center mt-2">
<SiPaperlessngx className="text-green-500 mr-2"/>
Paperless-ngx: Turning paper piles into searchable archives.
</div>
</div>,
];
export default selfhostedCommand;

View file

@ -0,0 +1,70 @@
import dynamic from "next/dynamic";
// Dynamically import React Icons with client-side rendering
const FaYoutube = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaYoutube), { ssr: false });
const FaBluesky = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaBluesky), { ssr: false });
const FaDiscord = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaDiscord), { ssr: false });
const FaGithub = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaGithub), { ssr: false });
const FaReddit = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaReddit), { ssr: false });
const socialsCommand = (showHeader = true) => [
<div key="socials">
{showHeader && (
<strong className="text-yellow-400">📡 Social Links:</strong>
)}
<div className="text-white flex items-center mt-2">
<FaGithub className="text-gray-400 mr-2" />
GitHub:{" "}
<a
href="https://github.com/Ronniie"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Ronniie
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaBluesky className="text-blue-400 mr-2" />
BlueSky:{" "}
<a
href="https://bsky.app/profile/ronniie.dev"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
@ronniie.dev
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaDiscord className="text-blue-500 mr-2" />
Discord: ronniie.
</div>
<div className="text-white flex items-center mt-2">
<FaYoutube className="text-red-500 mr-2" />
YouTube:{" "}
<a
href="https://www.youtube.com/@Zotechz"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
@Zotechz
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaReddit className="text-orange-500 mr-2" />
Reddit:{" "}
<a
href="https://www.reddit.com/user/Zotechz/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
u/Zotechz
</a>
</div>
</div>,
];
export default socialsCommand;

View file

@ -0,0 +1,10 @@
const unknownCommand = (input: string) => () => [
<div key="unknown-command" className="text-red-500">
Unknown command: <span className="font-bold">{input}</span>
</div>,
<div key="suggestion" className="text-green-400">
Type <span className="font-bold">help</span> to see available commands.
</div>,
];
export default unknownCommand;

View file

@ -1,22 +1,12 @@
"use client"; "use client";
import { useState, useRef, useEffect } from "react"; import { useState, useRef, useEffect } from "react";
import dynamic from "next/dynamic"; import helpCommand from "../app/commands/help";
import socialsCommand from "../app/commands/socials";
import selfhostedCommand from "../app/commands/selfhosted"
import exitCommand from "../app/commands/exit";
import unknownCommand from "../app/commands/unknown";
// Dynamically import React Icons with client-side rendering // Dynamically import React Icons with client-side rendering
const FaYoutube = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaYoutube), { ssr: false });
const FaBluesky = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaBluesky), { ssr: false });
const FaDiscord = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaDiscord), { ssr: false });
const FaGithub = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaGithub), { ssr: false });
const FaReddit = dynamic(() => import("react-icons/fa6").then((mod) => mod.FaReddit), { ssr: false });
// Self-hosted Services
const SiDocker = dynamic(() => import("react-icons/si").then((mod) => mod.SiDocker), { ssr: false });
const SiPlex = dynamic(() => import("react-icons/si").then((mod) => mod.SiPlex), { ssr: false });
const SiProxmox = dynamic(() => import("react-icons/si").then((mod) => mod.SiProxmox), { ssr: false });
const SiHomeassistant = dynamic(() => import("react-icons/si").then((mod) => mod.SiHomeassistant), { ssr: false });
const SiPaperlessngx = dynamic(() => import("react-icons/si").then((mod) => mod.SiPaperlessngx), { ssr: false });
const Terminal: React.FC = () => { const Terminal: React.FC = () => {
const [input, setInput] = useState(""); const [input, setInput] = useState("");
@ -30,96 +20,38 @@ const Terminal: React.FC = () => {
const handleInput = (e: React.FormEvent<HTMLFormElement>) => { const handleInput = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
if (input.trim() === "") return; // If the input is empty, add a blank line and return
if (input.trim() === "") {
setOutput((prev) => [
...prev,
<div key={`blank-${prev.length}`}>&nbsp;</div>, // Blank line
]);
setInput(""); // Clear the input field
return;
}
const commands: { [key: string]: JSX.Element[] } = { const normalizedInput = input.toLowerCase();
help: [ const commands: { [key: string]: () => JSX.Element[] } = {
<div key="help"> help: helpCommand,
<strong className="text-yellow-400">📖 Help Menu:</strong> socials: socialsCommand,
<ul className="mt-2 space-y-1"> exit: exitCommand,
<li>🆘 <strong className="text-green-400">help</strong>: Display this help menu.</li> selfhosted: selfhostedCommand,
<li>📡 <strong className="text-green-400">socials</strong>: View my social links.</li>
<li> <strong className="text-green-400">exit</strong>: Exit this terminal.</li>
</ul>
<p className="mt-4 text-gray-400">
💡 Pro Tip: Use <span className="text-green-400"></span> and <span className="text-green-400"></span> to navigate through your command history.
</p>
</div>,
],
socials: [
<div key="socials">
<strong className="text-yellow-400">📡 Social Links:</strong>
<div className="text-white flex items-center mt-2">
<FaGithub className="text-gray-400 mr-2"/>
GitHub:{" "}
<a
href="https://github.com/Ronniie"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Ronniie
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaBluesky className="text-blue-400 mr-2"/>
BlueSky:{" "}
<a
href="https://bsky.app/profile/ronniie.dev"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
@ronniie.dev
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaDiscord className="text-blue-500 mr-2"/>
Discord:{" "}
ronniie.
</div>
<div className="text-white flex items-center mt-2">
<FaYoutube className="text-red-500 mr-2"/>
YouTube:{" "}
<a
href="https://www.youtube.com/@Zotechz"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
@Zotechz
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaReddit className="text-orange-500 mr-2"/>
Reddit:{" "}
<a
href="https://www.reddit.com/user/Zotechz/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
u/Zotechz
</a>
</div>
</div>,
],
}; };
const commandOutput = commands[input] || [ const commandOutput =
<div key="unknown" className="text-red-400"> commands[normalizedInput] || unknownCommand(normalizedInput);
Unknown command: {input}
</div>,
];
setOutput((prev) => [...prev, <div key={`cmd-${input}`}>$ {input}</div>, ...commandOutput]); setOutput((prev) => [
...prev,
<div key={`cmd-${input}`}>$ {input}</div>,
...commandOutput(),
]);
setHistory((prev) => [...prev, input]); setHistory((prev) => [...prev, input]);
setHistoryIndex(null); // Reset the history index setHistoryIndex(null);
setInput(""); setInput("");
}; };
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (history.length === 0) return; if (history.length === 0) return;
@ -152,14 +84,6 @@ const Terminal: React.FC = () => {
} }
}; };
const handleClick = () => {
const selection = window.getSelection();
if (selection && selection.toString()) {
return;
}
inputRef.current?.focus();
};
useEffect(() => { useEffect(() => {
terminalEndRef.current?.scrollIntoView({ behavior: "smooth" }); terminalEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [output]); }, [output]);
@ -167,7 +91,7 @@ const Terminal: React.FC = () => {
return ( return (
<div <div
className="bg-black text-white font-mono w-full h-screen overflow-y-auto p-4" className="bg-black text-white font-mono w-full h-screen overflow-y-auto p-4"
onClick={handleClick} onClick={() => inputRef.current?.focus()}
> >
<div> <div>
{output.map((line, index) => ( {output.map((line, index) => (
@ -176,7 +100,6 @@ const Terminal: React.FC = () => {
</div> </div>
))} ))}
</div> </div>
<form onSubmit={handleInput} className="flex items-center mt-2"> <form onSubmit={handleInput} className="flex items-center mt-2">
<span className="text-green-500">$</span> <span className="text-green-500">$</span>
<input <input
@ -195,7 +118,12 @@ const Terminal: React.FC = () => {
}; };
const MOTD = () => ( const MOTD = () => {
const socialsOutput = socialsCommand(false);
const selfHostedOutput = selfhostedCommand(false);
return (
<> <>
<div className="text-green-400"> <div className="text-green-400">
{` _____ _ _ _ `} {` _____ _ _ _ `}
@ -224,83 +152,18 @@ const MOTD = () => (
<div className="border-t border-gray-700 my-4"/> <div className="border-t border-gray-700 my-4"/>
<strong className="text-yellow-400">📡 SOCIALS</strong> <strong className="text-yellow-400">📡 SOCIALS</strong>
<div className="text-white flex items-center mt-2"> <div>
<FaGithub className="text-gray-400 mr-2"/> {socialsOutput.map((line, index) => (
GitHub:{" "} <div key={index}>{line}</div>
<a ))}
href="https://github.com/Ronniie"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Ronniie
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaBluesky className="text-blue-400 mr-2"/>
BlueSky:{" "}
<a
href="https://bsky.app/profile/ronniie.dev"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
@ronniie.dev
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaDiscord className="text-blue-500 mr-2"/>
Discord:{" "}
ronniie.
</div>
<div className="text-white flex items-center mt-2">
<FaYoutube className="text-red-500 mr-2"/>
YouTube:{" "}
<a
href="https://www.youtube.com/@Zotechz"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
@Zotechz
</a>
</div>
<div className="text-white flex items-center mt-2">
<FaReddit className="text-orange-500 mr-2"/>
Reddit:{" "}
<a
href="https://www.reddit.com/user/Zotechz/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
u/Zotechz
</a>
</div> </div>
<div className="border-t border-gray-700 my-4"/> <div className="border-t border-gray-700 my-4"/>
<strong className="text-yellow-400"> SELF-HOSTED TOOLS</strong> <strong className="text-yellow-400"> SELF-HOSTED TOOLS</strong>
<div className="text-white flex items-center mt-2"> <div>
<SiProxmox className="text-orange-500 mr-2"/> {selfHostedOutput.map((line, index) => (
Proxmox: Virtual machines and containers, all in one place. <div key={index}>{line}</div>
</div> ))}
<div className="text-white flex items-center mt-2">
<SiDocker className="text-blue-500 mr-2"/>
Portainer: Orchestrating Docker containers effortlessly.
</div>
<div className="text-white flex items-center mt-2">
<SiPlex className="text-blue-400 mr-2"/>
Plex: Streaming my curated media library.
</div>
<div className="text-white flex items-center mt-2">
<SiHomeassistant className="text-teal-400 mr-2"/>
Home Assistant: Automating everything smart at home.
</div>
<div className="text-white flex items-center mt-2">
<SiPaperlessngx className="text-green-500 mr-2"/>
Paperless-ngx: Turning paper piles into searchable archives.
</div> </div>
<div className="border-t border-gray-700 my-4"/> <div className="border-t border-gray-700 my-4"/>
@ -312,9 +175,11 @@ const MOTD = () => (
<div className="border-t border-gray-700 my-4"/> <div className="border-t border-gray-700 my-4"/>
<div className="text-gray-400 mt-2"> <div className="text-gray-400 mt-2">
Type <span className="text-green-400">&#39;help&#39;</span> to see what you can do. Lets explore together! Type <span className="text-green-400">&#39;help&#39;</span> to see what you can do. Lets explore
together!
</div> </div>
</> </>
); );
}
export default Terminal; export default Terminal;