Fixed ASCII Art & Added Auto-suggest
This commit is contained in:
parent
ef797b704f
commit
360c0fd579
1 changed files with 59 additions and 16 deletions
|
@ -3,24 +3,30 @@
|
||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import helpCommand from "../app/commands/help";
|
import helpCommand from "../app/commands/help";
|
||||||
import socialsCommand from "../app/commands/socials";
|
import socialsCommand from "../app/commands/socials";
|
||||||
import selfhostedCommand from "../app/commands/selfhosted"
|
import selfhostedCommand from "../app/commands/selfhosted";
|
||||||
import exitCommand from "../app/commands/exit";
|
import exitCommand from "../app/commands/exit";
|
||||||
import unknownCommand from "../app/commands/unknown";
|
import unknownCommand from "../app/commands/unknown";
|
||||||
// Dynamically import React Icons with client-side rendering
|
|
||||||
|
|
||||||
const Terminal: React.FC = () => {
|
const Terminal: React.FC = () => {
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
const [output, setOutput] = useState<JSX.Element[]>([<MOTD key="motd" />]);
|
const [output, setOutput] = useState<JSX.Element[]>([<MOTD key="motd" />]);
|
||||||
const [history, setHistory] = useState<string[]>([]);
|
const [history, setHistory] = useState<string[]>([]);
|
||||||
const [historyIndex, setHistoryIndex] = useState<number | null>(null);
|
const [historyIndex, setHistoryIndex] = useState<number | null>(null);
|
||||||
|
const [suggestion, setSuggestion] = useState<string | null>(null); // For live suggestions
|
||||||
|
|
||||||
const terminalEndRef = useRef<HTMLDivElement>(null);
|
const terminalEndRef = useRef<HTMLDivElement>(null);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const commandsList = ["help", "socials", "exit", "selfhosted"];
|
||||||
|
useEffect(() => {
|
||||||
|
window.scrollTo(0, 0); // Automatically scroll to the top
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const handleInput = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleInput = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// If the input is empty, add a blank line and return
|
// If the input is empty, add a blank line
|
||||||
if (input.trim() === "") {
|
if (input.trim() === "") {
|
||||||
setOutput((prev) => [
|
setOutput((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
|
@ -49,10 +55,18 @@ const Terminal: React.FC = () => {
|
||||||
setHistory((prev) => [...prev, input]);
|
setHistory((prev) => [...prev, input]);
|
||||||
setHistoryIndex(null);
|
setHistoryIndex(null);
|
||||||
setInput("");
|
setInput("");
|
||||||
|
setSuggestion(null); // Clear suggestion after submission
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "Tab") {
|
||||||
|
e.preventDefault(); // Prevent browser default behavior
|
||||||
|
if (suggestion) {
|
||||||
|
setInput(suggestion);
|
||||||
|
setSuggestion(null); // Clear suggestion after Tab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (history.length === 0) return;
|
if (history.length === 0) return;
|
||||||
|
|
||||||
if (e.key === "ArrowUp") {
|
if (e.key === "ArrowUp") {
|
||||||
|
@ -84,6 +98,17 @@ const Terminal: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (value: string) => {
|
||||||
|
setInput(value);
|
||||||
|
|
||||||
|
// Suggest commands dynamically
|
||||||
|
const normalizedInput = value.toLowerCase();
|
||||||
|
const matchedCommand = commandsList.find((command) =>
|
||||||
|
command.startsWith(normalizedInput)
|
||||||
|
);
|
||||||
|
setSuggestion(matchedCommand || null);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
terminalEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
terminalEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
}, [output]);
|
}, [output]);
|
||||||
|
@ -100,24 +125,40 @@ 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 relative">
|
||||||
<span className="text-green-500">$</span>
|
<span className="text-green-500">$</span>
|
||||||
|
<div className="relative flex-1 ml-2">
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
className="bg-black text-white outline-none flex-1 ml-2"
|
className="bg-black text-white outline-none w-full"
|
||||||
value={input}
|
value={input}
|
||||||
onChange={(e) => setInput(e.target.value)}
|
onChange={(e) => handleInputChange(e.target.value)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
|
{suggestion && input.trim() !== suggestion && (
|
||||||
|
<div
|
||||||
|
className="absolute top-0 left-0 text-gray-500"
|
||||||
|
style={{
|
||||||
|
opacity: 0.5,
|
||||||
|
pointerEvents: "none",
|
||||||
|
paddingLeft: "2px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{input}
|
||||||
|
<span className="text-gray-400">
|
||||||
|
{suggestion.slice(input.length)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div ref={terminalEndRef}></div>
|
<div ref={terminalEndRef}></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const MOTD = () => {
|
const MOTD = () => {
|
||||||
const socialsOutput = socialsCommand(false);
|
const socialsOutput = socialsCommand(false);
|
||||||
const selfHostedOutput = selfhostedCommand(false);
|
const selfHostedOutput = selfhostedCommand(false);
|
||||||
|
@ -125,7 +166,9 @@ const MOTD = () => {
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<>
|
<>
|
||||||
<div className="text-green-400">
|
<div
|
||||||
|
className="overflow-x-auto whitespace-pre font-mono text-xs sm:text-sm md:text-base text-green-400"
|
||||||
|
>
|
||||||
{` _____ _ _ _ `}
|
{` _____ _ _ _ `}
|
||||||
{`\n | __ \\ (_|_) | | `}
|
{`\n | __ \\ (_|_) | | `}
|
||||||
{`\n | |__) |___ _ __ _ __ _ _ ___ __| | _____ __`}
|
{`\n | |__) |___ _ __ _ __ _ _ ___ __| | _____ __`}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue