Compare commits

..

No commits in common. "5e20066c3c119b6586d5b2d7f63658e96adf6719" and "e3b91e1094f80f6bee79a3db6b0e61e3d9c37980" have entirely different histories.

6 changed files with 89 additions and 218 deletions

View File

@ -3,7 +3,7 @@
pkgs.mkShell { pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
# Node.js and package managers # Node.js and package managers
nodejs_23 nodejs_20
nodePackages.npm nodePackages.npm
nodePackages.yarn nodePackages.yarn

View File

@ -149,8 +149,8 @@ function App() {
{/* Your gothic header */} {/* Your gothic header */}
<div className="fixed top-0 left-0 w-full text-center pt-8 z-10 gothic-text"> <div className="fixed top-0 left-0 w-full text-center pt-8 z-10 gothic-text">
<h1 className="text-6xl text-purple-300 mb-2">Npc About Me</h1> <h1 className="text-6xl text-purple-300 mb-2">Npc-About-Me</h1>
<h2 className="text-3xl text-purple-400">Console View</h2> <h2 className="text-xl text-purple-400">Console View</h2>
</div> </div>
{/* Your terminal component */} {/* Your terminal component */}
@ -179,13 +179,13 @@ function App() {
<div className="h-3 w-3 bg-yellow-500 rounded-full hover:bg-yellow-600 transition-colors"></div> <div className="h-3 w-3 bg-yellow-500 rounded-full hover:bg-yellow-600 transition-colors"></div>
<div className="h-3 w-3 bg-green-500 rounded-full hover:bg-green-600 transition-colors"></div> <div className="h-3 w-3 bg-green-500 rounded-full hover:bg-green-600 transition-colors"></div>
</div> </div>
<span className="mx-auto text-gray-300 gothic-text text-xl"> <span className="mx-auto text-sm text-gray-300 font-medium">
terminal@npc-about-me terminal@npc-about-me
</span> </span>
</div> </div>
{/* Terminal component with better background */} {/* Terminal component with better background */}
<div className="bg-terminal-bg w-full h-[calc(100%-2.5rem)] overflow-hidden"> <div className="bg-terminal-bg w-full h-[calc(100%-2.5rem)] overflow-hidden bg-[#0d1117]">
<Terminal /> <Terminal />
</div> </div>

View File

@ -17,67 +17,22 @@ const BackgroundImages = () => {
"/bg-ims/blahaj.png", "/bg-ims/blahaj.png",
]; ];
// Function to generate random values within a range
const randomRange = (min, max) => Math.random() * (max - min) + min;
// Function to generate new position and animation values
const generateImageData = () => {
// Generate random movement offsets
const moveX1 = randomRange(-20, 20);
const moveY1 = randomRange(-20, 20);
const moveX2 = randomRange(-20, 20);
const moveY2 = randomRange(-20, 20);
const moveX3 = randomRange(-20, 20);
const moveY3 = randomRange(-20, 20);
const rotateDeg = randomRange(-20, 20);
return {
src: bgImages[Math.floor(Math.random() * bgImages.length)],
size: randomRange(50, 150),
opacity: randomRange(0.1, 0.3),
// Animation durations (in seconds)
moveDuration: randomRange(60, 120),
rotateDuration: randomRange(30, 60),
// Starting position
startTop: randomRange(5, 90),
startLeft: randomRange(5, 90),
// Pre-calculated movement values
moveX1,
moveY1,
moveX2,
moveY2,
moveX3,
moveY3,
rotateDeg,
};
};
useEffect(() => { useEffect(() => {
// Generate initial images // Generate 30 random positioned images
const initialImages = Array(30).fill(null).map(generateImageData); const randomImages = [];
setImages(initialImages); for (let i = 0; i < 30; i++) {
randomImages.push({
src: bgImages[Math.floor(Math.random() * bgImages.length)],
top: Math.random() * 90 + 5, // 5-95% from top
left: Math.random() * 90 + 5, // 5-95% from left
size: Math.random() * 100 + 50, // 50-150px
rotation: Math.random() * 40 - 20, // -20 to 20 degrees
opacity: Math.random() * 0.2 + 0.1, // 0.1-0.3 opacity
});
}
setImages(randomImages);
}, []); }, []);
// Helper to create the keyframes CSS
const generateKeyframesStyles = () => {
return images
.map(
(img, index) => `
@keyframes float-${index} {
0%, 100% { transform: translate(-50%, -50%) translate(0, 0); }
25% { transform: translate(-50%, -50%) translate(${img.moveX1}px, ${img.moveY1}px); }
50% { transform: translate(-50%, -50%) translate(${img.moveX2}px, ${img.moveY2}px); }
75% { transform: translate(-50%, -50%) translate(${img.moveX3}px, ${img.moveY3}px); }
}
@keyframes rotate-${index} {
0%, 100% { transform: translate(-50%, -50%) rotate(0deg); }
50% { transform: translate(-50%, -50%) rotate(${img.rotateDeg}deg); }
}
`
)
.join("\n");
};
return ( return (
<div className="fixed inset-0 overflow-hidden pointer-events-none z-0"> <div className="fixed inset-0 overflow-hidden pointer-events-none z-0">
{images.map((img, index) => ( {images.map((img, index) => (
@ -85,13 +40,9 @@ const BackgroundImages = () => {
key={index} key={index}
className="absolute" className="absolute"
style={{ style={{
top: `${img.startTop}%`, top: `${img.top}%`,
left: `${img.startLeft}%`, left: `${img.left}%`,
transform: "translate(-50%, -50%)", transform: `translate(-50%, -50%) rotate(${img.rotation}deg)`,
animation: `
float-${index} ${img.moveDuration}s infinite ease-in-out,
rotate-${index} ${img.rotateDuration}s infinite ease-in-out
`,
}} }}
> >
<img <img
@ -106,8 +57,6 @@ const BackgroundImages = () => {
/> />
</div> </div>
))} ))}
<style dangerouslySetInnerHTML={{ __html: generateKeyframesStyles() }} />
</div> </div>
); );
}; };

View File

@ -8,7 +8,6 @@ export default function Terminal() {
const inputRef = useRef(null); const inputRef = useRef(null);
const terminalRef = useRef(null); const terminalRef = useRef(null);
const [hasBeenCleared, setHasBeenCleared] = useState(false); const [hasBeenCleared, setHasBeenCleared] = useState(false);
const [historyIndex, setHistoryIndex] = useState(-1);
// Focus the input when the component mounts and on click // Focus the input when the component mounts and on click
useEffect(() => { useEffect(() => {
@ -43,12 +42,6 @@ export default function Terminal() {
handleClear(); handleClear();
setInputValue(""); setInputValue("");
return; return;
} else if (e.key === "ArrowDown") {
e.preventDefault();
navigateHistory(-1);
} else if (e.key === "ArrowUp") {
e.preventDefault();
navigateHistory(1);
} }
}; };
@ -141,7 +134,6 @@ export default function Terminal() {
"help", "help",
"clear", "clear",
"view", "view",
"tree",
]; ];
const matchingCommands = commands.filter((cmd) => const matchingCommands = commands.filter((cmd) =>
cmd.startsWith(command) cmd.startsWith(command)
@ -154,7 +146,7 @@ export default function Terminal() {
} }
// If completing a path/file/directory // If completing a path/file/directory
if (["cd", "cat", "ls", "view", "tree"].includes(command)) { if (["cd", "cat", "ls", "view"].includes(command)) {
const partialPath = parts[parts.length - 1]; const partialPath = parts[parts.length - 1];
const suggestions = getPathSuggestions(partialPath); const suggestions = getPathSuggestions(partialPath);
@ -196,7 +188,6 @@ export default function Terminal() {
const executeCommand = () => { const executeCommand = () => {
if (!inputValue.trim()) return; if (!inputValue.trim()) return;
setHistoryIndex(-1);
// Create a new history entry with the CURRENT PATH at execution time // Create a new history entry with the CURRENT PATH at execution time
const newHistoryEntry = { const newHistoryEntry = {
command: inputValue, command: inputValue,
@ -238,7 +229,6 @@ export default function Terminal() {
" view [image] - Display image files\n" + " view [image] - Display image files\n" +
" pwd - Print working directory\n" + " pwd - Print working directory\n" +
" clear - Clear the terminal\n" + " clear - Clear the terminal\n" +
" tree [directory] - Display directory structure\n" +
" help - Display this help message\n\n" + " help - Display this help message\n\n" +
"Shortcuts:\n" + "Shortcuts:\n" +
" Tab - Autocomplete commands and paths\n" + " Tab - Autocomplete commands and paths\n" +
@ -248,9 +238,6 @@ export default function Terminal() {
clearTerminal(); clearTerminal();
setInputValue(""); setInputValue("");
return; return;
case "tree":
output = handleTree(args);
break;
default: default:
output = `Command not found: ${command}`; output = `Command not found: ${command}`;
} }
@ -273,48 +260,23 @@ export default function Terminal() {
// Handle ls command // Handle ls command
const handleLs = (args) => { const handleLs = (args) => {
let targetPath = currentPath; let targetPath = currentPath;
let showHidden = false;
let pathArgs = [];
// Parse arguments if (args.length > 0) {
args.forEach((arg) => { // Handle path argument
if (arg.startsWith("-")) { targetPath = resolvePath(args[0]);
// Handle flags
if (arg === "-a" || arg === "--all") {
showHidden = true;
} else if (arg === "--help") {
return `Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically.
-a, --all do not ignore entries starting with .
--help display this help and exit`;
}
} else {
// It's a path argument
pathArgs.push(arg);
}
});
// Handle path argument if provided
if (pathArgs.length > 0) {
targetPath = resolvePath(pathArgs[0]);
} }
const item = getItemAtPath(targetPath); const item = getItemAtPath(targetPath);
if (!item) { if (!item) {
return `ls: ${pathArgs[0]}: No such file or directory`; return `ls: ${args[0]}: No such file or directory`;
} }
if (item.type !== "directory") { if (item.type !== "directory") {
return `ls: ${pathArgs[0]}: Not a directory`; return `ls: ${args[0]}: Not a directory`;
} }
// Get and filter children based on showHidden flag const children = Object.keys(item.children).sort();
const children = Object.keys(item.children)
.filter((name) => showHidden || !name.startsWith("."))
.sort();
if (children.length === 0) { if (children.length === 0) {
return "<span class='text-gray-400'>Empty directory</span>"; return "<span class='text-gray-400'>Empty directory</span>";
@ -383,36 +345,20 @@ Sort entries alphabetically.
} }
const fileName = args[0]; const fileName = args[0];
let targetPath = fileName; const currentDir = getItemAtPath(currentPath);
// If it's not an absolute path, resolve it relative to current directory if (!currentDir || currentDir.type !== "directory") {
if (!fileName.startsWith("/")) { return "Current location is not a directory";
targetPath = resolvePath(fileName);
} }
// Get the directory and file name from the path if (
const lastSlashIndex = targetPath.lastIndexOf("/"); fileName in currentDir.children &&
const dirPath = currentDir.children[fileName].type === "file"
lastSlashIndex === -1 ) {
? currentPath return currentDir.children[fileName].content;
: targetPath.substring(0, lastSlashIndex);
const targetFileName =
lastSlashIndex === -1
? targetPath
: targetPath.substring(lastSlashIndex + 1);
const dir = getItemAtPath(dirPath);
if (!dir || dir.type !== "directory") {
return `cat: ${fileName}: No such file or directory`;
} }
const file = dir.children[targetFileName];
if (!file || file.type !== "file") {
return `cat: ${fileName}: No such file`; return `cat: ${fileName}: No such file`;
}
return file.content;
}; };
// Handle view command for images // Handle view command for images
@ -461,60 +407,6 @@ Sort entries alphabetically.
</div>`; </div>`;
}; };
// Add tree command handler
const handleTree = (args) => {
const path = args[0] || currentPath;
const resolvedPath = resolvePath(path);
const item = getItemAtPath(resolvedPath);
if (!item) {
return `tree: ${path}: No such directory`;
}
if (item.type !== "directory") {
return `tree: ${path}: Not a directory`;
}
const output = [];
const printTree = (node, name, prefix = "", isLast = true) => {
const isDir = node.type === "directory";
const icon = isDir ? "📁" : "📄";
output.push(`${prefix}${isLast ? "└── " : "├── "}${icon} ${name}`);
if (isDir && node.children) {
const entries = Object.entries(node.children);
entries.forEach(([key, value], index) => {
const isLastEntry = index === entries.length - 1;
const newPrefix = prefix + (isLast ? " " : "│ ");
printTree(value, key, newPrefix, isLastEntry);
});
}
};
// Start with the root directory name
const rootName = resolvedPath.split("/").pop() || "/";
printTree(item, rootName);
return output.join("\n");
};
// Function to navigate command history
const navigateHistory = (direction) => {
if (commandHistory.length === 0) return;
const newIndex = historyIndex + direction;
if (newIndex >= -1 && newIndex < commandHistory.length) {
setHistoryIndex(newIndex);
if (newIndex === -1) {
setInputValue("");
} else {
setInputValue(
commandHistory[commandHistory.length - 1 - newIndex].command
);
}
}
};
return ( return (
<div <div
className="h-full text-terminal-text font-mono p-4 overflow-hidden flex flex-col text-left bg-gradient-to-b from-gray-900 to-black rounded-b-lg" className="h-full text-terminal-text font-mono p-4 overflow-hidden flex flex-col text-left bg-gradient-to-b from-gray-900 to-black rounded-b-lg"
@ -533,13 +425,20 @@ Sort entries alphabetically.
> >
{!hasBeenCleared && ( {!hasBeenCleared && (
<div className="text-lg mb-4 text-terminal-accent text-left"> <div className="text-lg mb-4 text-terminal-accent text-left">
<div className="text-purple-300 gothic-text text-4xl tracking-wider"> <pre className="text-purple-300 font-bold">
NPC ABOUT ME {`
</div> _ _ ____ ____ _ ____ ___ _ _ _____ __ __ _____
<div className="text-green-400 gothic-text font-semibold mb-2 text-2xl"> | \\ | | _ \\ / ___| / \\ | __ ) / _ \\| | | |_ _| | \\/ | ____|
| \\| | |_) | | / _ \\ | _ \\| | | | | | | | | | |\\/| | _|
| |\\ | __/| |___ / ___ \\| |_) | |_| | |_| | | | | | | | |___
|_| \\_|_| \\____| /_/ \\_\\____/ \\___/ \\___/ |_| |_| |_|_____|
`}
</pre>
<div className="text-green-400 font-semibold mb-2">
Welcome to my interactive terminal! Welcome to my interactive terminal!
</div> </div>
<div className="text-amber-300 gothic-text mb-3 text-xl"> <div className="text-amber-300 mb-3">
Type{" "} Type{" "}
<span className="bg-gray-800 px-1 rounded text-white">help</span>{" "} <span className="bg-gray-800 px-1 rounded text-white">help</span>{" "}
to see available commands. to see available commands.
@ -550,7 +449,7 @@ Sort entries alphabetically.
{commandHistory.map((entry, index) => ( {commandHistory.map((entry, index) => (
<div key={index} className="mb-2 text-left"> <div key={index} className="mb-2 text-left">
<div className="flex"> <div className="flex">
<span className="text-terminal-prompt mr-2 gothic-text text-xl"> <span className="text-terminal-prompt mr-2">
guest@npc-shell: guest@npc-shell:
{entry.path === "/home/guest" {entry.path === "/home/guest"
? "~" ? "~"
@ -580,7 +479,7 @@ Sort entries alphabetically.
className="w-full" className="w-full"
> >
<div className="flex items-center"> <div className="flex items-center">
<span className="text-terminal-prompt mr-2 flex items-center gothic-text text-xl"> <span className="text-terminal-prompt mr-2 flex items-center">
<span className="text-green-400">guest</span> <span className="text-green-400">guest</span>
<span className="text-gray-500">@</span> <span className="text-gray-500">@</span>
<span className="text-purple-400">npc-shell</span> <span className="text-purple-400">npc-shell</span>

View File

@ -17,12 +17,7 @@ export const fileSystem = {
"about.txt": { "about.txt": {
type: "file", type: "file",
content: content:
"I'm the average Internet person. I currently code and play video games :) I especially play Euro Truck Sim 2 and Roblox, Phantom Forces to be exact. Sometimes I play Minecraft with my friends :D.\n\nI realy love Ikea's stuffed animals\n\nI can Program in html, css, python, c++, c#, c and maybe i can do minecraft mods too :]", "Hi there! I'm a developer passionate about creating interactive web experiences.\n\nI enjoy working with modern JavaScript frameworks, building intuitive UIs, and exploring new technologies.\n\nWhen I'm not coding, you can find me gaming, reading sci-fi, or hanging out with my Blahaj.",
},
"status.txt": {
type: "file",
content:
"Status :)\nHi there! Here are some details about me:\n\nAge: 14\nPronouns: He/Him ^_^\nRelationship Status: Single :D\nFun Fact 1: I'm an NPC! :O Just doing my thing, not needing to be in the center of attention or talk much. :|\nFun Fact 2: I really like the Ikea Plushies. (BLÅHAJ on top)",
}, },
".secret": { ".secret": {
type: "file", type: "file",
@ -37,9 +32,30 @@ export const fileSystem = {
content: content:
"Terminal Portfolio\n------------------\n\nAn interactive terminal-style portfolio website built with React and Tailwind CSS.\n\nFeatures:\n- Fully functional command line interface\n- Virtual filesystem navigation\n- Custom commands\n- Responsive design", "Terminal Portfolio\n------------------\n\nAn interactive terminal-style portfolio website built with React and Tailwind CSS.\n\nFeatures:\n- Fully functional command line interface\n- Virtual filesystem navigation\n- Custom commands\n- Responsive design",
}, },
"project1.txt": {
type: "file",
content:
"Project 1\n---------\n\nA full-stack web application for task management.\n\nTechnologies:\n- React\n- Node.js\n- MongoDB\n- Express",
},
"project2.txt": {
type: "file",
content:
"Project 2\n---------\n\nAn e-commerce platform with real-time inventory tracking.\n\nTechnologies:\n- Vue.js\n- Firebase\n- Stripe API\n- Tailwind CSS",
},
screenshots: { screenshots: {
type: "directory", type: "directory",
children: {}, children: {
"project1.png": {
type: "image",
src: "/images/project1.png",
description: "Screenshot of Project 1",
},
"project2.png": {
type: "image",
src: "/images/project2.png",
description: "Screenshot of Project 2",
},
},
}, },
}, },
}, },
@ -49,17 +65,17 @@ export const fileSystem = {
"frontend.txt": { "frontend.txt": {
type: "file", type: "file",
content: content:
"Frontend Skills\n--------------\n\n- HTML5, CSS, JavaScript, TypeScript\n- React, Vue.js\n- Tailwind CSS, Bootstrap\n- Responsive Design\n- Vite", "Frontend Skills\n--------------\n\n- HTML5, CSS3, JavaScript\n- React, Vue.js\n- Tailwind CSS, SASS\n- TypeScript\n- Responsive Design\n- Webpack, Vite",
}, },
"backend.txt": { "backend.txt": {
type: "file", type: "file",
content: content:
"Backend Skills\n-------------\n\n- Node.js\n- Python\n- Flask APIs\n- ChartJS\n- MariaDB, PostgreSQL\n- Docker, Proxmox", "Backend Skills\n-------------\n\n- Node.js, Express\n- Python, Django\n- RESTful APIs\n- GraphQL\n- MongoDB, PostgreSQL\n- Firebase, AWS",
}, },
"tools.txt": { "tools.txt": {
type: "file", type: "file",
content: content:
"Development Tools\n----------------\n\n- Gitea, GitHub\n- Docker\n- VS Code/Cursor\n- Proxmox, Linux", "Development Tools\n----------------\n\n- Git, GitHub\n- Docker\n- VS Code\n- Figma\n- Jest, Cypress\n- CI/CD Pipelines",
}, },
}, },
}, },
@ -68,15 +84,15 @@ export const fileSystem = {
children: { children: {
"email.txt": { "email.txt": {
type: "file", type: "file",
content: "Email: npc.lele@gmail.com", content: "Email: example@domain.com",
}, },
"github.txt": { "github.txt": {
type: "file", type: "file",
content: "GitHub: https://github.com/Lele7663", content: "GitHub: https://github.com/yourusername",
}, },
"discord.txt": { "linkedin.txt": {
type: "file", type: "file",
content: "jxstnpc", content: "LinkedIn: https://linkedin.com/in/yourprofile",
}, },
}, },
}, },
@ -131,7 +147,7 @@ export const fileSystem = {
"coding-music.txt": { "coding-music.txt": {
type: "file", type: "file",
content: content:
"Best Music for Coding\n------------------\n- Anything that sounds good!\n(based answer, i know)", "Best Music for Coding\n------------------\n\n- Lo-fi beats\n- Ambient electronic\n- Video game soundtracks\n- Classical music\n- Synthwave\n\nPersonally, I find that music without lyrics helps me focus best when tackling complex problems.",
}, },
}, },
}, },

View File

@ -132,6 +132,13 @@ button:focus-visible {
} }
/* Add these styles to your index.css */ /* Add these styles to your index.css */
@font-face {
font-family: "CloisterBlack";
src: url("/CloisterBlack.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
.terminal-window { .terminal-window {
transition: box-shadow 0.3s ease; transition: box-shadow 0.3s ease;
} }