From 90ed7aede2869eea22d796b272f1e78846957b53 Mon Sep 17 00:00:00 2001 From: Ronnie Date: Wed, 20 Nov 2024 19:26:35 -0500 Subject: [PATCH] Started my dev site, terminal theme --- package.json | 9 +- src/app/globals.css | 2 +- src/app/layout.tsx | 4 +- src/app/page.tsx | 102 +----------- src/components/Terminal.tsx | 320 ++++++++++++++++++++++++++++++++++++ 5 files changed, 333 insertions(+), 104 deletions(-) create mode 100644 src/components/Terminal.tsx diff --git a/package.json b/package.json index a789539..858c873 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,19 @@ "lint": "next lint" }, "dependencies": { + "next": "15.0.3", "react": "19.0.0-rc-66855b96-20241106", "react-dom": "19.0.0-rc-66855b96-20241106", - "next": "15.0.3" + "react-icons": "^5.3.0" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "15.0.3", "postcss": "^8", "tailwindcss": "^3.4.1", - "eslint": "^8", - "eslint-config-next": "15.0.3" + "typescript": "^5" } } diff --git a/src/app/globals.css b/src/app/globals.css index 6b717ad..7025961 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -17,5 +17,5 @@ body { color: var(--foreground); background: var(--background); - font-family: Arial, Helvetica, sans-serif; + font-family: 'Courier New', Courier, monospace; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a36cde0..c20d815 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -14,8 +14,8 @@ const geistMono = localFont({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Ronniie.dev", + description: "Welcome to Ronnie's Development Terminal", }; export default function RootLayout({ diff --git a/src/app/page.tsx b/src/app/page.tsx index 3eee014..dcc7198 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,101 +1,9 @@ -import Image from "next/image"; +import Terminal from "../components/Terminal"; -export default function Home() { +export default function TerminalPage() { return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - src/app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
- -
- - Vercel logomark - Deploy now - - - Read our docs - -
-
- -
+
+ +
); } diff --git a/src/components/Terminal.tsx b/src/components/Terminal.tsx new file mode 100644 index 0000000..6171c76 --- /dev/null +++ b/src/components/Terminal.tsx @@ -0,0 +1,320 @@ +"use client"; + +import { useState, useRef, useEffect } from "react"; +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 }); + +// 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 [input, setInput] = useState(""); + const [output, setOutput] = useState([]); + const [history, setHistory] = useState([]); + const [historyIndex, setHistoryIndex] = useState(null); + + const terminalEndRef = useRef(null); + const inputRef = useRef(null); + + const handleInput = (e: React.FormEvent) => { + e.preventDefault(); + + if (input.trim() === "") return; + + const commands: { [key: string]: JSX.Element[] } = { + help: [ +
+ πŸ“– Help Menu: +
    +
  • πŸ†˜ help: Display this help menu.
  • +
  • πŸ“‘ socials: View my social links.
  • +
  • ❌ exit: Exit this terminal.
  • +
+

+ πŸ’‘ Pro Tip: Use ↑ and ↓ to navigate through your command history. +

+
, + ], + socials: [ +
+ πŸ“‘ Social Links: +
+ + GitHub:{" "} + + Ronniie + +
+ +
+ + BlueSky:{" "} + + @ronniie.dev + +
+
+ + Discord:{" "} + ronniie. +
+ +
+ + YouTube:{" "} + + @Zotechz + +
+
+ + Reddit:{" "} + + u/Zotechz + +
+
, + ], + }; + + const commandOutput = commands[input] || [ +
+ Unknown command: {input} +
, + ]; + + setOutput((prev) => [...prev,
$ {input}
, ...commandOutput]); + setHistory((prev) => [...prev, input]); + setHistoryIndex(null); // Reset the history index + setInput(""); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (history.length === 0) return; + + if (e.key === "ArrowUp") { + e.preventDefault(); + if (historyIndex === null) { + setHistoryIndex(history.length - 1); + setInput(history[history.length - 1]); + } else if (historyIndex > 0) { + setHistoryIndex((prev) => { + const newIndex = prev! - 1; + setInput(history[newIndex]); + return newIndex; + }); + } + } + + if (e.key === "ArrowDown") { + e.preventDefault(); + if (historyIndex !== null && historyIndex < history.length - 1) { + setHistoryIndex((prev) => { + const newIndex = prev! + 1; + setInput(history[newIndex]); + return newIndex; + }); + } else { + setHistoryIndex(null); + setInput(""); + } + } + }; + + const handleClick = (e: React.MouseEvent) => { + const selection = window.getSelection(); + if (selection && selection.toString()) { + return; + } + inputRef.current?.focus(); + }; + + useEffect(() => { + terminalEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [output]); + + return ( +
+
+ {output.map((line, index) => ( +
+ {line} +
+ ))} +
+ +
+ $ + setInput(e.target.value)} + onKeyDown={handleKeyDown} + autoFocus + /> +
+
+
+ ); +}; + + +const MOTD = () => ( + <> +
+ {` _____ _ _ _ `} + {`\n | __ \\ (_|_) | | `} + {`\n | |__) |___ _ __ _ __ _ _ ___ __| | _____ __`} + {`\n | _ // _ \\| '_ \\| '_ \\| | |/ _ \\ / _\` |/ _ \\ \\ / /`} + {`\n | | \\ \\ (_) | | | | | | | | | __/| (_| | __/\\ V / `} + {`\n |_| \\_\\___/|_| |_|_| |_|_|_|\\___(_)__,_|\\___| \\_/ `} +
+
+ Welcome to Ronnie's Development Terminal! +
+ +
+ 🎩 ABOUT THE DEV +

+ Hi! I'm Ronnieβ€”a developer passionate about blending code and design. +

+

+ This site is my playground for testing and crafting innovative projects. +

+

+ When I'm not tinkering here, I'm programming for NullDaily LLC, building + open-source tools to empower creators and developers. +

+ +
+ πŸ“‘ SOCIALS +
+ + GitHub:{" "} + + Ronniie + +
+ +
+ + BlueSky:{" "} + + @ronniie.dev + +
+
+ + Discord:{" "} + ronniie. +
+ +
+ + YouTube:{" "} + + @Zotechz + +
+
+ + Reddit:{" "} + + u/Zotechz + +
+ +
+ βš™οΈ SELF-HOSTED TOOLS +
+ + Proxmox: Virtual machines and containers, all in one place. +
+
+ + Portainer: Orchestrating Docker containers effortlessly. +
+
+ + Plex: Streaming my curated media library. +
+
+ + Home Assistant: Automating everything smart at home. +
+
+ + Paperless-ngx: Turning paper piles into searchable archives. +
+ +
+

+ 🌟 "Code, automate, and create with purpose. This isn't just development; + it's an adventure." 🌟 +

+ + +
+
+ Type 'help' to see what you can do. Let’s explore together! +
+ +); + +export default Terminal;