add man pages for shell commands and implement mkdir, ping, matrix, and about commands
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| import React, { useState, useRef, useEffect } from 'react'; | ||||
| import { Terminal } from 'lucide-react'; | ||||
| import { System } from '../shell/system'; | ||||
| import '../styles/matrix.css'; | ||||
|  | ||||
| const TerminalShell: React.FC = () => { | ||||
|     const [history, setHistory] = useState<string[]>(['Welcome to the terminal! Type "help" for commands.']); | ||||
| @@ -20,12 +21,13 @@ const TerminalShell: React.FC = () => { | ||||
|             if (system.knowsCommand(cmdName)) { | ||||
|                 const output = system.executeCommand(cmdName, args); | ||||
|  | ||||
|                 const newLines = output.split('\\n').filter(l => l); | ||||
|                 const newLines = output.split('\n').filter(l => l); | ||||
|  | ||||
|                 setHistory(prev => [...prev, `$ ${command}`, ...newLines]); | ||||
|             } else { | ||||
|                 setHistory(prev => [...prev, `$ ${command}`, `Command not found: ${cmdName}`]); | ||||
|             } | ||||
|  | ||||
|             setCurrentCommand(''); | ||||
|         } | ||||
|     }; | ||||
| @@ -34,8 +36,8 @@ const TerminalShell: React.FC = () => { | ||||
|         <div className="h-full flex flex-col"> | ||||
|             <div className="flex-1 overflow-y-auto mb-4"> | ||||
|                 {history.map((line, index) => ( | ||||
|                     <div key={index} className="mb-2"> | ||||
|                         {line} | ||||
|                     <div key={index} className="mb-2" dangerouslySetInnerHTML={{ __html: line }}> | ||||
|                         {/* Dangerously set inner HTML */} | ||||
|                     </div> | ||||
|                 ))} | ||||
|                 <div ref={bottomRef} /> | ||||
|   | ||||
| @@ -106,4 +106,6 @@ export class FileSystem { | ||||
|         current.children!.delete(fileName); | ||||
|         console.log(`[FileSystem] File deleted: ${path}`); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -40,4 +40,8 @@ export class ShellSyscall { | ||||
|     deleteFile(path: string): void { | ||||
|         this.fs.deleteFile(`${this.cwd}/${path}`); | ||||
|     } | ||||
|  | ||||
|     createDirectory(dirname: string): void { | ||||
|         this.fs.createDirectory(`${this.cwd}/${dirname}`); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| export interface IShellCommand { | ||||
|     getName(): string; | ||||
|     getManPage(): string; | ||||
|     execute(input: string | string[]): string; | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/shell/commands/about.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/shell/commands/about.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| import { IShellCommand } from "./IShellCommand"; | ||||
|  | ||||
| export class about implements IShellCommand { | ||||
|     getName(): string { | ||||
|         return 'about'; | ||||
|     } | ||||
|  | ||||
|     execute(_: string | string[]): string { | ||||
|         let output = 'Really cool terminal ;)'; | ||||
|         output += '\n\n'; | ||||
|         output += 'Built by ivanch'; | ||||
|         output += '\n'; | ||||
|  | ||||
|         return output; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'about - about this terminal'; | ||||
|     } | ||||
| } | ||||
| @@ -12,4 +12,8 @@ export class cat implements IShellCommand { | ||||
|         if (args.length === 0) return "cat: missing file operand"; | ||||
|         return this.syscall.readFile(args[0]); | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return "cat - concatenate files and print on the standard output"; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -13,4 +13,8 @@ export class cd implements IShellCommand { | ||||
|         const success = this.syscall.changeDirectory(path); | ||||
|         return success ? '' : 'Invalid directory'; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return "cd - change the shell working directory"; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -11,4 +11,8 @@ export class echo implements IShellCommand { | ||||
|         } | ||||
|         return input.trim(); | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'echo - echo a line of text'; | ||||
|     } | ||||
| } | ||||
| @@ -12,4 +12,8 @@ export class ls implements IShellCommand { | ||||
|         const files = this.syscall.listCurrentDirectory(); | ||||
|         return files.join('\n'); | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'ls - list directory contents'; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										27
									
								
								src/shell/commands/man.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/shell/commands/man.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| import { IShellCommand } from "./IShellCommand"; | ||||
| import { System } from "../system"; | ||||
|  | ||||
| export class man implements IShellCommand { | ||||
|     getName(): string { | ||||
|         return 'man'; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'man - display system reference manuals\n\nUsage: man command_name'; | ||||
|     } | ||||
|  | ||||
|     execute(args: string[]): string { | ||||
|         if (args.length !== 1) { | ||||
|             return 'Usage: man command_name'; | ||||
|         } | ||||
|  | ||||
|         const system = System.getInstance(); | ||||
|         const command = system['commands'].find(c => c.getName() === args[0]); | ||||
|  | ||||
|         if (!command) { | ||||
|             return `No manual entry for ${args[0]}`; | ||||
|         } | ||||
|  | ||||
|         return command.getManPage(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										33
									
								
								src/shell/commands/matrix.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/shell/commands/matrix.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import { IShellCommand } from "./IShellCommand"; | ||||
|  | ||||
| export class matrix implements IShellCommand { | ||||
|     getName(): string { | ||||
|         return 'matrix'; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'matrix - displays a matrix-style text animation\n\nUsage: matrix'; | ||||
|     } | ||||
|  | ||||
|     execute(): string { | ||||
|         const width = 40; | ||||
|         const height = 15; | ||||
|         const characters = '日ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘ'; | ||||
|  | ||||
|         const matrixHtml = ` | ||||
|             <div class="matrix-text-animation"> | ||||
|                 ${Array(height).fill(0).map(() => ` | ||||
|                     <div class="matrix-line"> | ||||
|                         ${Array(width).fill(0).map(() => ` | ||||
|                             <span class="matrix-char" style="animation-delay: ${Math.random() * 5}s"> | ||||
|                                 ${characters[Math.floor(Math.random() * characters.length)]} | ||||
|                             </span> | ||||
|                         `).join('')} | ||||
|                     </div> | ||||
|                 `).join('')} | ||||
|             </div> | ||||
|         `; | ||||
|  | ||||
|         return matrixHtml; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/shell/commands/mkdir.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/shell/commands/mkdir.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import { IShellCommand } from "./IShellCommand"; | ||||
| import { ShellSyscall } from "../ShellSyscall"; | ||||
|  | ||||
| export class mkdir implements IShellCommand { | ||||
|     constructor(private syscall: ShellSyscall) {} | ||||
|  | ||||
|     getName(): string { | ||||
|         return 'mkdir'; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'mkdir - make directories\n\nUsage: mkdir directory_name'; | ||||
|     } | ||||
|  | ||||
|     execute(args: string[]): string { | ||||
|         if (args.length !== 1) { | ||||
|             return 'Usage: mkdir directory_name'; | ||||
|         } | ||||
|  | ||||
|         this.syscall.createDirectory(args[0]); | ||||
|         return ''; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/shell/commands/ping.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/shell/commands/ping.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import { IShellCommand } from "./IShellCommand"; | ||||
|  | ||||
| export class ping implements IShellCommand { | ||||
|     getName(): string { | ||||
|         return 'ping'; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'ping - send ICMP ECHO_REQUEST to network hosts\n\nUsage: ping [ip-address]'; | ||||
|     } | ||||
|  | ||||
|     execute(args: string[]): string { | ||||
|         if (args.length !== 1) { | ||||
|             return 'Usage: ping [ip-address]'; | ||||
|         } | ||||
|  | ||||
|         const ip = args[0]; | ||||
|         let output = `PING ${ip} (${ip}) 56(84) bytes of data.\n`; | ||||
|  | ||||
|         for (let i = 0; i < 4; i++) { | ||||
|             const time = Math.random() * 50 + 20; | ||||
|             output += `64 bytes from ${ip}: icmp_seq=${i + 1} ttl=64 time=${time.toFixed(1)} ms\n`; | ||||
|         } | ||||
|  | ||||
|         output += '\n--- ping statistics ---\n'; | ||||
|         output += `4 packets transmitted, 4 received, 0% packet loss, time 3000ms\n`; | ||||
|         output += `rtt min/avg/max/mdev = 20.0/30.0/40.0/5.0 ms\n`; | ||||
|  | ||||
|         return output; | ||||
|     } | ||||
| } | ||||
| @@ -11,4 +11,8 @@ export class pwd implements IShellCommand { | ||||
|     execute(_: string[]): string { | ||||
|         return this.syscall.getCurrentDirectory(); | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return "pwd - print name of current/working directory"; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,4 +21,8 @@ export class rm implements IShellCommand { | ||||
|         this.syscall.deleteFile(filename); | ||||
|         return `Removed ${filename}`; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return "rm - remove files or directories"; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										15
									
								
								src/shell/commands/sudo.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/shell/commands/sudo.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| import { IShellCommand } from "./IShellCommand"; | ||||
|  | ||||
| export class sudo implements IShellCommand { | ||||
|     getName(): string { | ||||
|         return 'sudo'; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return 'sudo - execute a command as another user\n\nUsage: sudo command'; | ||||
|     } | ||||
|  | ||||
|     execute(args: string[]): string { | ||||
|         return 'Permission denied'; | ||||
|     } | ||||
| } | ||||
| @@ -13,4 +13,8 @@ export class touch implements IShellCommand { | ||||
|         this.syscall.createFile(args[0]); | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
|     getManPage(): string { | ||||
|         return "touch - create an empty file"; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,9 +5,15 @@ import { cd } from "./commands/cd"; | ||||
| import { touch } from "./commands/touch"; | ||||
| import { cat } from "./commands/cat"; | ||||
| import { rm } from "./commands/rm"; | ||||
| import { ping } from "./commands/ping"; | ||||
| import { mkdir } from "./commands/mkdir"; | ||||
| import { man } from "./commands/man"; | ||||
| import { sudo } from "./commands/sudo"; | ||||
| import { IShellCommand } from "./commands/IShellCommand"; | ||||
| import { FileSystem } from "./FileSystem"; | ||||
| import { ShellSyscall } from "./ShellSyscall"; | ||||
| import { about } from "./commands/about"; | ||||
| import { matrix } from "./commands/matrix"; | ||||
|  | ||||
| // System class to manage commands | ||||
| class System { | ||||
| @@ -32,6 +38,12 @@ class System { | ||||
|         this.commands.push(new touch(syscall)); | ||||
|         this.commands.push(new cat(syscall)); | ||||
|         this.commands.push(new rm(syscall)); | ||||
|         this.commands.push(new ping()); | ||||
|         this.commands.push(new mkdir(syscall)); | ||||
|         this.commands.push(new man()); | ||||
|         this.commands.push(new sudo()); | ||||
|         this.commands.push(new about()); | ||||
|         // this.commands.push(new matrix()); | ||||
|     } | ||||
|  | ||||
|     public static getInstance(): System { | ||||
| @@ -42,10 +54,14 @@ class System { | ||||
|     } | ||||
|  | ||||
|     knowsCommand(commandName: string): boolean { | ||||
|         return this.commands.some(c => c.getName() === commandName); | ||||
|         return commandName === 'help' || this.commands.some(c => c.getName() === commandName); | ||||
|     } | ||||
|  | ||||
|     executeCommand(commandName: string, args: string[]): string { | ||||
|         if (commandName === 'help') { | ||||
|             return this.commands.map(c => c.getName()).join('\n'); | ||||
|         } | ||||
|  | ||||
|         const command = this.commands.find(c => c.getName() === commandName); | ||||
|         if (command) { | ||||
|             return command.execute(args); | ||||
|   | ||||
							
								
								
									
										30
									
								
								src/styles/matrix.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/styles/matrix.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| .matrix-text-animation { | ||||
|     color: #0F0; | ||||
|     font-family: monospace; | ||||
|     line-height: 1; | ||||
|     display: inline-block; | ||||
| } | ||||
| .matrix-line { | ||||
|     white-space: pre; | ||||
| } | ||||
| .matrix-char { | ||||
|     opacity: 0; | ||||
|     display: inline-block; | ||||
|     animation: matrix-fade 3s infinite; | ||||
| } | ||||
| @keyframes matrix-fade { | ||||
|     0%, 100% { opacity: 0; } | ||||
|     50% { opacity: 1; } | ||||
| } | ||||
| .matrix-text-animation::after { | ||||
|     content: "Wake up, Neo..."; | ||||
|     display: block; | ||||
|     color: #0F0; | ||||
|     font-weight: bold; | ||||
|     margin-top: 1em; | ||||
|     animation: text-fade 4s infinite; | ||||
| } | ||||
| @keyframes text-fade { | ||||
|     0%, 100% { opacity: 0; } | ||||
|     50% { opacity: 1; } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user