sl :)
This commit is contained in:
@ -116,6 +116,175 @@ const TerminalComponent: React.FC<TerminalProps> = ({
|
||||
xtermRef.current.write(`\r\nls: cannot access '${currentPath.current}': Not a directory`);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pwd':
|
||||
xtermRef.current.write('\r\n' + currentPath.current);
|
||||
break;
|
||||
|
||||
case 'find':
|
||||
if (args.length === 0) {
|
||||
xtermRef.current.write('\r\nfind: missing path\r\nUsage: find <path> [-name <pattern>]');
|
||||
break;
|
||||
}
|
||||
|
||||
const findPath = args[0];
|
||||
const nameIndex = args.indexOf('-name');
|
||||
const namePattern = nameIndex !== -1 && args[nameIndex + 1] ? args[nameIndex + 1] : '';
|
||||
|
||||
const findResults: string[] = [];
|
||||
|
||||
// Simple recursive search through filesystem
|
||||
const searchInPath = (searchPath: string, depth: number = 0) => {
|
||||
if (depth > 10) return; // Prevent infinite recursion
|
||||
|
||||
const pathContent = fileSystem[searchPath];
|
||||
if (Array.isArray(pathContent)) {
|
||||
pathContent.forEach(item => {
|
||||
const itemPath = searchPath === '/' ? '/' + item : searchPath + '/' + item;
|
||||
|
||||
// Check if item matches pattern (if specified)
|
||||
if (!namePattern || item.includes(namePattern.replace(/\*/g, ''))) {
|
||||
findResults.push(itemPath);
|
||||
}
|
||||
|
||||
// Recursively search subdirectories
|
||||
if (Array.isArray(fileSystem[itemPath])) {
|
||||
searchInPath(itemPath, depth + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (fileSystem[findPath] !== undefined) {
|
||||
if (!namePattern) {
|
||||
findResults.push(findPath);
|
||||
}
|
||||
searchInPath(findPath);
|
||||
|
||||
if (findResults.length > 0) {
|
||||
xtermRef.current.write('\r\n' + findResults.join('\r\n'));
|
||||
} else {
|
||||
xtermRef.current.write(`\r\nfind: no files found`);
|
||||
}
|
||||
} else {
|
||||
xtermRef.current.write(`\r\nfind: ${findPath}: No such file or directory`);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'head':
|
||||
const headLines = args[0] === '-n' && args[1] ? parseInt(args[1]) : 10;
|
||||
const headFile = args[0] === '-n' ? args[2] : args[0];
|
||||
|
||||
if (!headFile) {
|
||||
xtermRef.current.write('\r\nhead: missing file operand');
|
||||
break;
|
||||
}
|
||||
|
||||
let headFilePath: string;
|
||||
if (headFile.startsWith('/')) {
|
||||
headFilePath = headFile;
|
||||
} else {
|
||||
headFilePath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + headFile;
|
||||
}
|
||||
|
||||
const headContent = fileSystem[headFilePath];
|
||||
if (headContent === undefined) {
|
||||
xtermRef.current.write(`\r\nhead: ${headFile}: No such file or directory`);
|
||||
} else if (Array.isArray(headContent)) {
|
||||
xtermRef.current.write(`\r\nhead: ${headFile}: Is a directory`);
|
||||
} else {
|
||||
const lines = headContent.split('\n');
|
||||
const displayLines = lines.slice(0, headLines);
|
||||
xtermRef.current.write('\r\n' + displayLines.join('\r\n'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'tail':
|
||||
const tailLines = args[0] === '-n' && args[1] ? parseInt(args[1]) : 10;
|
||||
const tailFile = args[0] === '-n' ? args[2] : args[0];
|
||||
|
||||
if (!tailFile) {
|
||||
xtermRef.current.write('\r\ntail: missing file operand');
|
||||
break;
|
||||
}
|
||||
|
||||
let tailFilePath: string;
|
||||
if (tailFile.startsWith('/')) {
|
||||
tailFilePath = tailFile;
|
||||
} else {
|
||||
tailFilePath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + tailFile;
|
||||
}
|
||||
|
||||
const tailContent = fileSystem[tailFilePath];
|
||||
if (tailContent === undefined) {
|
||||
xtermRef.current.write(`\r\ntail: ${tailFile}: No such file or directory`);
|
||||
} else if (Array.isArray(tailContent)) {
|
||||
xtermRef.current.write(`\r\ntail: ${tailFile}: Is a directory`);
|
||||
} else {
|
||||
const lines = tailContent.split('\n');
|
||||
const displayLines = lines.slice(-tailLines);
|
||||
xtermRef.current.write('\r\n' + displayLines.join('\r\n'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'sl':
|
||||
// Easter egg: Steam Locomotive animation
|
||||
xtermRef.current.write('\r\n');
|
||||
const trainFrames = [
|
||||
" ==== ________ ___________",
|
||||
" _D _| |_______/ \\__I_I_____===__|_________|",
|
||||
" |(_)--- | H\\________/ | | =|___ ___|",
|
||||
" / | | H | | | | ||_| |_||",
|
||||
" | | | H |__--------------------| [___] |",
|
||||
" | ________|___H__/__|_____/[][]~\\_______| |",
|
||||
" |/ | |-----------I_____I [][] [] D |=======|__",
|
||||
"__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__|",
|
||||
" |/-=|___|= || || || |_____/~\\___/",
|
||||
" \\_/ \\O=====O=====O=====O_/ \\_/"
|
||||
];
|
||||
|
||||
// Calculate train width (length of longest line)
|
||||
const trainWidth = Math.max(...trainFrames.map(line => line.length));
|
||||
|
||||
// Start position: completely off-screen to the right
|
||||
let position = xtermRef.current.cols + 10;
|
||||
|
||||
const animateTrain = () => {
|
||||
// Stop when train is completely off-screen to the left
|
||||
if (position < -trainWidth - 5) {
|
||||
xtermRef.current?.write('\x1b[2J\x1b[H'); // Clear screen one final time
|
||||
prompt();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear screen and position cursor at top
|
||||
xtermRef.current?.write('\x1b[2J\x1b[H');
|
||||
|
||||
// Draw each line of the train with proper spacing
|
||||
trainFrames.forEach((line) => {
|
||||
if (position >= 0) {
|
||||
// Train is partially or fully on screen from the right
|
||||
const spaces = ' '.repeat(position);
|
||||
const visiblePart = line.substring(0, xtermRef.current!.cols - position);
|
||||
xtermRef.current?.write(spaces + visiblePart + '\r\n');
|
||||
} else if (position + line.length > 0) {
|
||||
// Train is partially off-screen to the left
|
||||
const startIdx = Math.abs(position);
|
||||
const visiblePart = line.substring(startIdx, startIdx + xtermRef.current!.cols);
|
||||
xtermRef.current?.write(visiblePart + '\r\n');
|
||||
} else {
|
||||
// Train is completely off-screen to the left, just write empty line
|
||||
xtermRef.current?.write('\r\n');
|
||||
}
|
||||
});
|
||||
|
||||
position -= 3; // Move train left by 3 characters each frame
|
||||
setTimeout(animateTrain, 120); // Slightly slower for smoother animation
|
||||
};
|
||||
|
||||
animateTrain();
|
||||
return; // Don't call prompt() immediately
|
||||
|
||||
case 'cd':
|
||||
const targetDir = args[0];
|
||||
if (!targetDir) {
|
||||
@ -188,8 +357,12 @@ const TerminalComponent: React.FC<TerminalProps> = ({
|
||||
const helpText =
|
||||
"Available Commands:\r\n" +
|
||||
" ls List directory contents\r\n" +
|
||||
" pwd Print working directory\r\n" +
|
||||
" cd <directory> Change directory\r\n" +
|
||||
" cat <file> Display file contents\r\n" +
|
||||
" find <path> [-name <pattern>] Search for files\r\n" +
|
||||
" head [-n <num>] <file> Show first lines of file\r\n" +
|
||||
" tail [-n <num>] <file> Show last lines of file\r\n" +
|
||||
" clear Clear the screen\r\n" +
|
||||
" help Display this help message\r\n\r\n" +
|
||||
"Tips:\r\n" +
|
||||
|
Reference in New Issue
Block a user