👀
This commit is contained in:
31
package.json
31
package.json
@ -9,25 +9,28 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/node": "^9.1.1",
|
"@astrojs/node": "^9.2.1",
|
||||||
"@astrojs/react": "^4.2.0",
|
"@astrojs/react": "^4.2.7",
|
||||||
"@aws-sdk/client-s3": "^3.750.0",
|
"@aws-sdk/client-s3": "^3.812.0",
|
||||||
"@tailwindcss/vite": "^4.0.8",
|
"@tailwindcss/vite": "^4.1.7",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.9",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.15.19",
|
||||||
"@types/react": "^19.0.10",
|
"@types/react": "^19.1.4",
|
||||||
"@types/react-dom": "^19.0.4",
|
"@types/react-dom": "^19.1.5",
|
||||||
"astro": "^5.3.1",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
|
"@xterm/addon-web-links": "^0.11.0",
|
||||||
|
"@xterm/xterm": "^5.5.0",
|
||||||
|
"astro": "^5.7.13",
|
||||||
"astro-robots": "^2.3.1",
|
"astro-robots": "^2.3.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.1.0",
|
||||||
"tailwindcss": "^4.0.8",
|
"tailwindcss": "^4.1.7",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.8.3",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"daisyui": "5.0.0-beta.6"
|
"daisyui": "5.0.35"
|
||||||
}
|
}
|
||||||
}
|
}
|
3210
pnpm-lock.yaml
generated
3210
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
365
src/components/Terminal.tsx
Normal file
365
src/components/Terminal.tsx
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
import React, { useEffect, useRef, useMemo } from 'react';
|
||||||
|
import { Terminal } from '@xterm/xterm';
|
||||||
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
|
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||||
|
import '@xterm/xterm/css/xterm.css';
|
||||||
|
|
||||||
|
type FileSystemStructure = {
|
||||||
|
[key: string]: string[] | string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TerminalProps {
|
||||||
|
solution1: number;
|
||||||
|
solution2: number;
|
||||||
|
solution3: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TerminalComponent: React.FC<TerminalProps> = ({ solution1 = 42, solution2 = 87, solution3 = 5 }) => {
|
||||||
|
const terminalRef = useRef<HTMLDivElement>(null);
|
||||||
|
const xtermRef = useRef<Terminal | null>(null);
|
||||||
|
const fitAddonRef = useRef<FitAddon | null>(null);
|
||||||
|
const commandHistory = useRef<string[]>([]);
|
||||||
|
const historyIndex = useRef<number>(-1);
|
||||||
|
const currentLine = useRef<string>('');
|
||||||
|
|
||||||
|
const fileSystem: FileSystemStructure = useMemo(() => ({
|
||||||
|
'/': ['home', 'documents', 'etc', 'var', 'usr', 'README.txt', 'secret.txt'],
|
||||||
|
'/home': ['user', 'guest'],
|
||||||
|
'/home/user': ['puzzle_progress.txt'],
|
||||||
|
'/home/guest': [],
|
||||||
|
'/documents': ['report.doc', 'notes.txt'],
|
||||||
|
'/etc': ['config', 'sys_data'],
|
||||||
|
'/etc/config': ['network.conf', 'color_palette.config'],
|
||||||
|
'/etc/sys_data': ['core_access.bin'],
|
||||||
|
'/var': ['log', 'tmp'],
|
||||||
|
'/var/log': ['system.log', 'critical_errors.log'],
|
||||||
|
'/usr': ['bin', 'share'],
|
||||||
|
'/usr/bin': ['common_commands.txt'],
|
||||||
|
'/usr/share': ['docs', 'misc'],
|
||||||
|
'/usr/share/docs': ['filesystem_layout.txt'],
|
||||||
|
'/usr/share/misc': ['old_data.bak'],
|
||||||
|
|
||||||
|
// File Contents
|
||||||
|
'/README.txt': "Welcome, challenger. Your mission, should you choose to accept it...\n" +
|
||||||
|
"Is to find three numbers to unlock a combination lock.\n" +
|
||||||
|
"These numbers are hidden within this simulated filesystem.\n" +
|
||||||
|
"Use commands like 'ls', 'cd', and 'cat' to explore.\n" +
|
||||||
|
"A good starting point might be to understand the lay of the land: 'cat /usr/share/docs/filesystem_layout.txt'\n" +
|
||||||
|
"Good luck!",
|
||||||
|
'/secret.txt': "SECURITY NOTICE:\n\nSystem access restricted to authorized personnel only.\nAll activities on this terminal are logged and monitored.\n\nNote to admins: The master backup codes have been moved to a more secure location.\nPlease refer to the new security protocol documentation.\n\nCheck system configuration in /etc and examine logs in /var/log if issues arise.",
|
||||||
|
'/usr/share/docs/filesystem_layout.txt': "System Architecture Overview:\n" +
|
||||||
|
"- /home: User-specific files.\n" +
|
||||||
|
"- /etc: System configuration files and critical data.\n" +
|
||||||
|
" |- /config: Display and network settings.\n" +
|
||||||
|
" |- /sys_data: Core system binary data.\n" +
|
||||||
|
"- /var: Variable data, such as logs and temporary files.\n" +
|
||||||
|
" |- /log: System logs and error reports.\n" +
|
||||||
|
"- /usr: System-wide shared resources and executables (read-only for you!).\n",
|
||||||
|
|
||||||
|
// Puzzle 1: Number Base
|
||||||
|
'/etc/sys_data/core_access.bin': `System memory address reference:\n` +
|
||||||
|
`Register B1: ${solution1.toString(2).padStart(8, '0')}\n` +
|
||||||
|
`Note: Always decode binary values when transferring between subsystems.`,
|
||||||
|
|
||||||
|
// Puzzle 2: Hex Code (Color)
|
||||||
|
'/etc/config/color_palette.config': `# Display Color Settings (RGB hex format)
|
||||||
|
PRIMARY_COLOR=#FF${solution2.toString(16).padStart(2, '0')}33
|
||||||
|
SECONDARY_COLOR=#33FF57
|
||||||
|
ALERT_COLOR=#FF0000
|
||||||
|
|
||||||
|
# System Note:
|
||||||
|
# Color channels must be isolated when importing to legacy devices.
|
||||||
|
# Remember middle values carry essential security data.`,
|
||||||
|
|
||||||
|
// Puzzle 3: Log File Analysis
|
||||||
|
'/var/log/critical_errors.log': "[ERROR 001] System boot failure sequence A.\n" +
|
||||||
|
"[INFO 002] Retrying boot sequence B.\n" +
|
||||||
|
"[ERROR 003] Disk read error on /dev/sda1.\n" +
|
||||||
|
"[WARN 004] Network interface eth0 flapping.\n" +
|
||||||
|
"[INFO 005] System recovered.\n" +
|
||||||
|
"[DEBUG 006] User 'admin' login attempt.\n" +
|
||||||
|
"[ERROR 007] Authentication failure for user 'admin'.\n" +
|
||||||
|
"[INFO 008] User 'hacker' login successful.\n" +
|
||||||
|
"[WARN 009] Unusual activity detected from IP 192.168.1.101.\n" +
|
||||||
|
"[ERROR 010] Critical process 'security_daemon' terminated unexpectedly.\n" +
|
||||||
|
"[INFO 011] Attempting to restart 'security_daemon'.\n" +
|
||||||
|
`[ERROR 012] Failed to restart 'security_daemon'. Max retries (${solution3}) reached.\n` +
|
||||||
|
"[ERROR 014] System shutting down due to critical errors.\n" +
|
||||||
|
"[RECOVER] Backup facility requires retry count for authentication sequence.",
|
||||||
|
|
||||||
|
// Home directory files with hints
|
||||||
|
'/home/user/puzzle_progress.txt': "My notes on the combination lock:\n- First number: __ (Check core system data)\n- Second number: __ (UI color settings have relevant info)\n- Third number: __ (Error logs contain critical values)\n\nReminder: Always convert between number formats as needed!",
|
||||||
|
|
||||||
|
// Document files with hints
|
||||||
|
'/documents/report.doc': "Project Phoenix - Q3 Financial Report Summary\n\nStrictly Confidential\n\nNote: System core access requires binary authentication module as referenced in maintenance guide section 4.2.1",
|
||||||
|
'/documents/notes.txt': "Grocery List:\n- Milk\n- Eggs\n- Bread\n- Mystery item #42 (NOT the combination number!)\n- Remember to check system logs for maintenance schedule\n\nReminders:\n- Review critical error logs for system failures\n- Update color palette settings per design team request",
|
||||||
|
|
||||||
|
// Network config with hint
|
||||||
|
'/etc/config/network.conf': "HOSTNAME=ixabatasha-virtual-env\nIP_ADDR=10.1.33.7\nGATEWAY=10.1.33.1\nDNS_SERVER=8.8.8.8\n\n# Note: Display color palette in the same directory needs to be accessible for UI integration",
|
||||||
|
|
||||||
|
// System log with hint
|
||||||
|
'/var/log/system.log': "[INFO] System startup sequence initiated.\n[INFO] All core services loaded successfully.\n[WARN] Low virtual memory. Consider rebooting non-essential modules.\n[INFO] User 'root' logged in from console.\n[NOTICE] Combination lock module initialized with secure parameters.\n[DEBUG] Numbers not stored in plaintext for security reasons.\n[INFO] Memory address values in binary format stored in /etc/sys_data\n[WARN] Check critical_errors.log for security daemon failures",
|
||||||
|
|
||||||
|
'/usr/bin/common_commands.txt': "Available commands: ls, cd, cat, clear, help (for README)\n\nImportant system paths:\n- Main config: /etc/config/\n- System data: /etc/sys_data/\n- Error logs: /var/log/",
|
||||||
|
|
||||||
|
'/usr/share/misc/old_data.bak': "ARCHIVE FILE: Contains outdated research notes from Project Chimera. Access restricted.\n\nPossible combination values tested:\n- 12-45-78 (Failed)\n- 24-68-99 (Failed)\n- 11-22-33 (Failed)\n\nNOTE: New secure generation algorithm implemented. Old combinations no longer valid.\n\nCurrent security relies on: core_access biometrics, color_palette UI safeguards, and error logging thresholds."
|
||||||
|
}), [solution1, solution2, solution3]);
|
||||||
|
const currentPath = useRef<string>('/');
|
||||||
|
|
||||||
|
const prompt = () => {
|
||||||
|
if (xtermRef.current) {
|
||||||
|
xtermRef.current.write(`\r\nroot@ixabatasha:${currentPath.current}# `);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const processCommand = (command: string) => {
|
||||||
|
if (!xtermRef.current) return;
|
||||||
|
const [cmd, ...args] = command.trim().split(' ');
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case 'ls':
|
||||||
|
const currentDirContent = fileSystem[currentPath.current];
|
||||||
|
if (Array.isArray(currentDirContent)) {
|
||||||
|
const displayItems = currentDirContent.map(item => {
|
||||||
|
let itemFullPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + item;
|
||||||
|
if (itemFullPath !== '/' && itemFullPath.endsWith('/')) itemFullPath = itemFullPath.slice(0, -1);
|
||||||
|
|
||||||
|
if (Array.isArray(fileSystem[itemFullPath])) {
|
||||||
|
return item + '/';
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
xtermRef.current.write('\r\n' + displayItems.join('\t'));
|
||||||
|
} else {
|
||||||
|
xtermRef.current.write(`\r\nls: cannot access '${currentPath.current}': Not a directory`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'cd':
|
||||||
|
const targetDir = args[0];
|
||||||
|
if (!targetDir) {
|
||||||
|
// cd to root
|
||||||
|
currentPath.current = '/';
|
||||||
|
} else if (targetDir === '..') {
|
||||||
|
const parts = currentPath.current.split('/').filter(p => p);
|
||||||
|
parts.pop();
|
||||||
|
currentPath.current = '/' + parts.join('/');
|
||||||
|
if (currentPath.current === '//') currentPath.current = '/';
|
||||||
|
} else {
|
||||||
|
let newPath: string;
|
||||||
|
if (targetDir.startsWith('/')) {
|
||||||
|
newPath = targetDir;
|
||||||
|
} else {
|
||||||
|
newPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + targetDir;
|
||||||
|
}
|
||||||
|
// Normalize path (remove trailing slashes unless it's the root)
|
||||||
|
if (newPath !== '/' && newPath.endsWith('/')) {
|
||||||
|
newPath = newPath.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSystem[newPath] !== undefined) {
|
||||||
|
if (Array.isArray(fileSystem[newPath])) {
|
||||||
|
currentPath.current = newPath;
|
||||||
|
} else {
|
||||||
|
xtermRef.current.write(`\r\ncd: ${targetDir}: Not a directory`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xtermRef.current.write(`\r\ncd: ${targetDir}: No such file or directory`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'cat':
|
||||||
|
const targetFileArg = args[0];
|
||||||
|
if (!targetFileArg) {
|
||||||
|
xtermRef.current.write('\r\ncat: missing file operand');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fullTargetPath: string;
|
||||||
|
if (targetFileArg.startsWith('/')) {
|
||||||
|
fullTargetPath = targetFileArg;
|
||||||
|
} else {
|
||||||
|
fullTargetPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + targetFileArg;
|
||||||
|
}
|
||||||
|
// Normalize path
|
||||||
|
if (fullTargetPath !== '/' && fullTargetPath.endsWith('/')) {
|
||||||
|
fullTargetPath = fullTargetPath.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileContent = fileSystem[fullTargetPath];
|
||||||
|
|
||||||
|
if (fileContent === undefined) {
|
||||||
|
xtermRef.current.write(`\r\ncat: ${targetFileArg}: No such file or directory`);
|
||||||
|
} else if (Array.isArray(fileContent)) {
|
||||||
|
xtermRef.current.write(`\r\ncat: ${targetFileArg}: Is a directory`);
|
||||||
|
} else if (typeof fileContent === 'string') {
|
||||||
|
xtermRef.current.write('\r\n' + fileContent.split('\n').join('\r\n'));
|
||||||
|
} else {
|
||||||
|
// Should not happen with current structure
|
||||||
|
xtermRef.current.write(`\r\ncat: ${targetFileArg}: Cannot read this type of file`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'clear':
|
||||||
|
xtermRef.current.clear();
|
||||||
|
break;
|
||||||
|
case 'help':
|
||||||
|
// Display available commands and tips
|
||||||
|
const helpText =
|
||||||
|
"Available Commands:\n" +
|
||||||
|
" ls List directory contents\n" +
|
||||||
|
" cd <directory> Change directory\n" +
|
||||||
|
" cat <file> Display file contents\n" +
|
||||||
|
" clear Clear the screen\n" +
|
||||||
|
" help Display this help message\n\n" +
|
||||||
|
"Tips:\n" +
|
||||||
|
"- Use Tab for command/path completion\n" +
|
||||||
|
"- Up/Down arrows navigate command history\n" +
|
||||||
|
"- Type 'cat /usr/bin/common_commands.txt' for more system information\n" +
|
||||||
|
"- Read the README.txt for mission details";
|
||||||
|
|
||||||
|
xtermRef.current.write('\r\n' + helpText);
|
||||||
|
break;
|
||||||
|
case '': // Handle empty command
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
xtermRef.current.write(`\r\ncommand not found: ${cmd}\r\nTry 'help' for a list of available commands`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prompt();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (terminalRef.current && !xtermRef.current) {
|
||||||
|
const term = new Terminal({
|
||||||
|
cursorBlink: true,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: 14,
|
||||||
|
theme: {
|
||||||
|
background: '#1e1e1e',
|
||||||
|
foreground: '#d4d4d4',
|
||||||
|
cursor: '#d4d4d4',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const fit = new FitAddon();
|
||||||
|
const webLinks = new WebLinksAddon();
|
||||||
|
|
||||||
|
xtermRef.current = term;
|
||||||
|
fitAddonRef.current = fit;
|
||||||
|
|
||||||
|
term.loadAddon(fit);
|
||||||
|
term.loadAddon(webLinks);
|
||||||
|
term.open(terminalRef.current);
|
||||||
|
fit.fit();
|
||||||
|
|
||||||
|
term.write('Welcome to Ixabata-shell!\r\n');
|
||||||
|
term.write('Type a command to begin. (Try ls, cd, cat)\r\n');
|
||||||
|
prompt();
|
||||||
|
|
||||||
|
|
||||||
|
term.onKey(({ key, domEvent }) => {
|
||||||
|
if (!xtermRef.current) return;
|
||||||
|
|
||||||
|
const printable = !domEvent.altKey && !domEvent.ctrlKey && !domEvent.metaKey;
|
||||||
|
|
||||||
|
if (domEvent.key === 'Enter') {
|
||||||
|
if (currentLine.current.trim()) {
|
||||||
|
commandHistory.current.push(currentLine.current);
|
||||||
|
}
|
||||||
|
historyIndex.current = commandHistory.current.length;
|
||||||
|
processCommand(currentLine.current);
|
||||||
|
currentLine.current = '';
|
||||||
|
} else if (domEvent.key === 'Backspace') {
|
||||||
|
if (currentLine.current.length > 0) {
|
||||||
|
xtermRef.current.write('\b \b'); // Erase character
|
||||||
|
currentLine.current = currentLine.current.slice(0, -1);
|
||||||
|
}
|
||||||
|
} else if (domEvent.key === 'ArrowUp') {
|
||||||
|
if (commandHistory.current.length > 0 && historyIndex.current > 0) {
|
||||||
|
historyIndex.current--;
|
||||||
|
// Clear current line and write prompt + history item
|
||||||
|
xtermRef.current.write('\x1b[2K\r');
|
||||||
|
xtermRef.current.write(`root@ixabatasha:${currentPath.current}# `);
|
||||||
|
xtermRef.current.write(commandHistory.current[historyIndex.current]);
|
||||||
|
currentLine.current = commandHistory.current[historyIndex.current];
|
||||||
|
}
|
||||||
|
} else if (domEvent.key === 'ArrowDown') {
|
||||||
|
if (commandHistory.current.length > 0 && historyIndex.current < commandHistory.current.length - 1) {
|
||||||
|
historyIndex.current++;
|
||||||
|
// Clear current line and write prompt + history item
|
||||||
|
xtermRef.current.write('\x1b[2K\r');
|
||||||
|
xtermRef.current.write(`root@ixabatasha:${currentPath.current}# `);
|
||||||
|
xtermRef.current.write(commandHistory.current[historyIndex.current]);
|
||||||
|
currentLine.current = commandHistory.current[historyIndex.current];
|
||||||
|
} else if (historyIndex.current === commandHistory.current.length - 1) {
|
||||||
|
historyIndex.current++;
|
||||||
|
// Clear current line and write just the prompt
|
||||||
|
xtermRef.current.write('\x1b[2K\r');
|
||||||
|
xtermRef.current.write(`root@ixabatasha:${currentPath.current}# `);
|
||||||
|
currentLine.current = "";
|
||||||
|
}
|
||||||
|
} else if (domEvent.key === 'Tab') {
|
||||||
|
domEvent.preventDefault(); // Prevent default tab behavior (focus change)
|
||||||
|
const inputParts = currentLine.current.split(' ');
|
||||||
|
const currentArgument = inputParts.pop() || '';
|
||||||
|
const commandPart = inputParts.join(' ');
|
||||||
|
|
||||||
|
let suggestions: string[] = [];
|
||||||
|
const availableCommands = ['ls', 'cd', 'cat', 'clear', 'help'];
|
||||||
|
|
||||||
|
if (inputParts.length === 0 && !currentLine.current.includes(' ')) {
|
||||||
|
suggestions = availableCommands.filter(c => c.startsWith(currentArgument));
|
||||||
|
} else {
|
||||||
|
const currentDirItems = Array.isArray(fileSystem[currentPath.current]) ? fileSystem[currentPath.current] as string[] : [];
|
||||||
|
suggestions = currentDirItems.filter(item => item.startsWith(currentArgument));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suggestions.length === 1) {
|
||||||
|
const suggestion = suggestions[0];
|
||||||
|
let resolvedSuggestionPath = suggestion;
|
||||||
|
if (!suggestion.startsWith('/')) {
|
||||||
|
resolvedSuggestionPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + suggestion;
|
||||||
|
}
|
||||||
|
if (resolvedSuggestionPath !== '/' && resolvedSuggestionPath.endsWith('/')) {
|
||||||
|
resolvedSuggestionPath = resolvedSuggestionPath.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDirectory = Array.isArray(fileSystem[resolvedSuggestionPath]);
|
||||||
|
const completion = suggestion + (isDirectory ? '/' : ' ');
|
||||||
|
|
||||||
|
xtermRef.current.write(completion.substring(currentArgument.length));
|
||||||
|
currentLine.current = (commandPart ? commandPart + ' ' : '') + completion;
|
||||||
|
if (!isDirectory || !completion.endsWith('/')) {
|
||||||
|
currentLine.current = currentLine.current.trimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else if (suggestions.length > 1) {
|
||||||
|
xtermRef.current.write('\r\n' + suggestions.join('\t') + '\r\n');
|
||||||
|
prompt();
|
||||||
|
xtermRef.current.write(currentLine.current);
|
||||||
|
}
|
||||||
|
// If no suggestions, do nothing (or beep: xtermRef.current.write('\x07');)
|
||||||
|
|
||||||
|
} else if (printable && key.length === 1) { // Ensure it's a single character, not a special key like 'Shift'
|
||||||
|
xtermRef.current.write(key);
|
||||||
|
currentLine.current += key;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle resize
|
||||||
|
const handleResize = () => {
|
||||||
|
fitAddonRef.current?.fit();
|
||||||
|
};
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
term.dispose();
|
||||||
|
xtermRef.current = null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div ref={terminalRef} style={{ width: '100%', height: '100%' }} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TerminalComponent;
|
57
src/pages/secret-terminal.astro
Normal file
57
src/pages/secret-terminal.astro
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
import TerminalComponent from '../components/Terminal.tsx';
|
||||||
|
const title = "Secret Terminal";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Escape Room Terminal Puzzle
|
||||||
|
==========================
|
||||||
|
|
||||||
|
This virtual Linux terminal contains three puzzles that reveal the
|
||||||
|
combination lock code. Each number can be configured below.
|
||||||
|
|
||||||
|
Puzzle Locations & Solutions:
|
||||||
|
|
||||||
|
1. Binary Number Challenge
|
||||||
|
- Location: /etc/sys_data/core_access.bin
|
||||||
|
- How to find: Look for binary data in system files and convert to decimal
|
||||||
|
|
||||||
|
2. Hex Color Code Challenge
|
||||||
|
- Location: /etc/config/color_palette.config
|
||||||
|
- How to find: Extract the green component from a hex color code (FF**XX**33)
|
||||||
|
and convert from hex to decimal
|
||||||
|
|
||||||
|
3. Log File Analysis Challenge
|
||||||
|
- Location: /var/log/critical_errors.log
|
||||||
|
- How to find: Look for the "max retries" count in the security_daemon error
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Define the solutions for the combination lock
|
||||||
|
const solution1 = 29; // Binary puzzle solution
|
||||||
|
const solution2 = 31; // Hex color code puzzle solution
|
||||||
|
const solution3 = 5; // Log file analysis puzzle solution
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en" class="h-full m-0 p-0 overflow-hidden">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🤫</text></svg>"
|
||||||
|
/>
|
||||||
|
<meta name="generator" content={Astro.generator} />
|
||||||
|
<title>{title}</title>
|
||||||
|
<link rel="stylesheet" href="/src/styles/global.css" />
|
||||||
|
</head>
|
||||||
|
<body class="h-full m-0 p-0 overflow-hidden bg-black">
|
||||||
|
<div id="terminal-container" class="w-full h-full">
|
||||||
|
<TerminalComponent
|
||||||
|
client:only="react"
|
||||||
|
solution1={solution1}
|
||||||
|
solution2={solution2}
|
||||||
|
solution3={solution3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user