Made some MASSIVE improvements
This commit is contained in:
@ -6,3 +6,6 @@ S3_BUCKET_NAME=your_bucket_name
|
|||||||
SECRET_CODE=super_secret_code
|
SECRET_CODE=super_secret_code
|
||||||
ADMIN_CODE=super_secret_code
|
ADMIN_CODE=super_secret_code
|
||||||
JWT_SECRET=your_jwt_secret_key_here_make_it_long_and_random
|
JWT_SECRET=your_jwt_secret_key_here_make_it_long_and_random
|
||||||
|
SOLUTION_1=29
|
||||||
|
SOLUTION_2=31
|
||||||
|
SOLUTION_3=5
|
@ -15,4 +15,8 @@ services:
|
|||||||
SECRET_CODE: ${SECRET_CODE}
|
SECRET_CODE: ${SECRET_CODE}
|
||||||
ADMIN_CODE: ${ADMIN_CODE}
|
ADMIN_CODE: ${ADMIN_CODE}
|
||||||
JWT_SECRET: ${JWT_SECRET}
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
|
# Puzzle Solutions (private)
|
||||||
|
SOLUTION_1: ${SOLUTION_1}
|
||||||
|
SOLUTION_2: ${SOLUTION_2}
|
||||||
|
SOLUTION_3: ${SOLUTION_3}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
@ -9,12 +9,16 @@ type FileSystemStructure = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface TerminalProps {
|
interface TerminalProps {
|
||||||
solution1: number;
|
puzzle1Content: string;
|
||||||
solution2: number;
|
puzzle2Content: string;
|
||||||
solution3: number;
|
puzzle3Content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TerminalComponent: React.FC<TerminalProps> = ({ solution1 = 42, solution2 = 87, solution3 = 5 }) => {
|
const TerminalComponent: React.FC<TerminalProps> = ({
|
||||||
|
puzzle1Content,
|
||||||
|
puzzle2Content,
|
||||||
|
puzzle3Content
|
||||||
|
}) => {
|
||||||
const terminalRef = useRef<HTMLDivElement>(null);
|
const terminalRef = useRef<HTMLDivElement>(null);
|
||||||
const xtermRef = useRef<Terminal | null>(null);
|
const xtermRef = useRef<Terminal | null>(null);
|
||||||
const fitAddonRef = useRef<FitAddon | null>(null);
|
const fitAddonRef = useRef<FitAddon | null>(null);
|
||||||
@ -57,35 +61,13 @@ const TerminalComponent: React.FC<TerminalProps> = ({ solution1 = 42, solution2
|
|||||||
"- /usr: System-wide shared resources and executables (read-only for you!).\n",
|
"- /usr: System-wide shared resources and executables (read-only for you!).\n",
|
||||||
|
|
||||||
// Puzzle 1: Number Base
|
// Puzzle 1: Number Base
|
||||||
'/etc/sys_data/core_access.bin': `System memory address reference:\n` +
|
'/etc/sys_data/core_access.bin': puzzle1Content,
|
||||||
`Register B1: ${solution1.toString(2).padStart(8, '0')}\n` +
|
|
||||||
`Note: Always decode binary values when transferring between subsystems.`,
|
|
||||||
|
|
||||||
// Puzzle 2: Hex Code (Color)
|
// Puzzle 2: Hex Code (Color)
|
||||||
'/etc/config/color_palette.config': `# Display Color Settings (RGB hex format)
|
'/etc/config/color_palette.config': puzzle2Content,
|
||||||
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
|
// Puzzle 3: Log File Analysis
|
||||||
'/var/log/critical_errors.log': "[ERROR 001] System boot failure sequence A.\n" +
|
'/var/log/critical_errors.log': puzzle3Content,
|
||||||
"[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 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!",
|
'/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!",
|
||||||
@ -103,7 +85,7 @@ ALERT_COLOR=#FF0000
|
|||||||
'/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/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."
|
'/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]);
|
}), [puzzle1Content, puzzle2Content, puzzle3Content]);
|
||||||
const currentPath = useRef<string>('/');
|
const currentPath = useRef<string>('/');
|
||||||
|
|
||||||
const prompt = () => {
|
const prompt = () => {
|
||||||
@ -237,7 +219,10 @@ ALERT_COLOR=#FF0000
|
|||||||
background: '#1e1e1e',
|
background: '#1e1e1e',
|
||||||
foreground: '#d4d4d4',
|
foreground: '#d4d4d4',
|
||||||
cursor: '#d4d4d4',
|
cursor: '#d4d4d4',
|
||||||
}
|
},
|
||||||
|
allowTransparency: true,
|
||||||
|
scrollback: 1000,
|
||||||
|
rightClickSelectsWord: true
|
||||||
});
|
});
|
||||||
const fit = new FitAddon();
|
const fit = new FitAddon();
|
||||||
const webLinks = new WebLinksAddon();
|
const webLinks = new WebLinksAddon();
|
||||||
@ -250,11 +235,164 @@ ALERT_COLOR=#FF0000
|
|||||||
term.open(terminalRef.current);
|
term.open(terminalRef.current);
|
||||||
fit.fit();
|
fit.fit();
|
||||||
|
|
||||||
|
// Enhanced paste handler for Ctrl+V and Cmd+V
|
||||||
|
term.attachCustomKeyEventHandler((e) => {
|
||||||
|
// Handle paste with Ctrl+V (Windows/Linux) or Cmd+V (Mac)
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
|
||||||
|
e.preventDefault();
|
||||||
|
navigator.clipboard.readText().then(text => {
|
||||||
|
// Process the pasted text character by character to update currentLine
|
||||||
|
const cleanText = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||||
|
for (const char of cleanText) {
|
||||||
|
if (char === '\n') {
|
||||||
|
// Handle newline as Enter key
|
||||||
|
if (currentLine.current.trim()) {
|
||||||
|
commandHistory.current.push(currentLine.current);
|
||||||
|
}
|
||||||
|
historyIndex.current = commandHistory.current.length;
|
||||||
|
processCommand(currentLine.current);
|
||||||
|
currentLine.current = '';
|
||||||
|
} else {
|
||||||
|
// Add printable characters to current line and display
|
||||||
|
term.write(char);
|
||||||
|
currentLine.current += char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.warn('Failed to read clipboard:', err);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle copy with Ctrl+C or Cmd+C (when text is selected)
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
|
||||||
|
const selection = term.getSelection();
|
||||||
|
if (selection) {
|
||||||
|
navigator.clipboard.writeText(selection).catch(err => {
|
||||||
|
console.warn('Failed to write to clipboard:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true; // Don't prevent default for copy
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add right-click context menu support
|
||||||
|
term.element?.addEventListener('contextmenu', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Create context menu
|
||||||
|
const existingMenu = document.getElementById('terminal-context-menu');
|
||||||
|
if (existingMenu) {
|
||||||
|
existingMenu.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const menu = document.createElement('div');
|
||||||
|
menu.id = 'terminal-context-menu';
|
||||||
|
menu.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: ${e.clientY}px;
|
||||||
|
left: ${e.clientX}px;
|
||||||
|
background: #2d2d2d;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 0;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
||||||
|
z-index: 1000;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
min-width: 120px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const selection = term.getSelection();
|
||||||
|
|
||||||
|
// Copy option (only if text is selected)
|
||||||
|
if (selection) {
|
||||||
|
const copyItem = document.createElement('div');
|
||||||
|
copyItem.textContent = 'Copy';
|
||||||
|
copyItem.style.cssText = `
|
||||||
|
padding: 8px 16px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.1s;
|
||||||
|
`;
|
||||||
|
copyItem.addEventListener('mouseenter', () => {
|
||||||
|
copyItem.style.backgroundColor = '#404040';
|
||||||
|
});
|
||||||
|
copyItem.addEventListener('mouseleave', () => {
|
||||||
|
copyItem.style.backgroundColor = 'transparent';
|
||||||
|
});
|
||||||
|
copyItem.addEventListener('click', () => {
|
||||||
|
navigator.clipboard.writeText(selection).catch(err => {
|
||||||
|
console.warn('Failed to write to clipboard:', err);
|
||||||
|
});
|
||||||
|
menu.remove();
|
||||||
|
});
|
||||||
|
menu.appendChild(copyItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paste option
|
||||||
|
const pasteItem = document.createElement('div');
|
||||||
|
pasteItem.textContent = 'Paste';
|
||||||
|
pasteItem.style.cssText = `
|
||||||
|
padding: 8px 16px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.1s;
|
||||||
|
`;
|
||||||
|
pasteItem.addEventListener('mouseenter', () => {
|
||||||
|
pasteItem.style.backgroundColor = '#404040';
|
||||||
|
});
|
||||||
|
pasteItem.addEventListener('mouseleave', () => {
|
||||||
|
pasteItem.style.backgroundColor = 'transparent';
|
||||||
|
});
|
||||||
|
pasteItem.addEventListener('click', () => {
|
||||||
|
navigator.clipboard.readText().then(text => {
|
||||||
|
// Process the pasted text character by character to update currentLine
|
||||||
|
const cleanText = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
||||||
|
for (const char of cleanText) {
|
||||||
|
if (char === '\n') {
|
||||||
|
// Handle newline as Enter key
|
||||||
|
if (currentLine.current.trim()) {
|
||||||
|
commandHistory.current.push(currentLine.current);
|
||||||
|
}
|
||||||
|
historyIndex.current = commandHistory.current.length;
|
||||||
|
processCommand(currentLine.current);
|
||||||
|
currentLine.current = '';
|
||||||
|
} else {
|
||||||
|
// Add printable characters to current line and display
|
||||||
|
term.write(char);
|
||||||
|
currentLine.current += char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.warn('Failed to read clipboard:', err);
|
||||||
|
});
|
||||||
|
menu.remove();
|
||||||
|
});
|
||||||
|
menu.appendChild(pasteItem);
|
||||||
|
|
||||||
|
document.body.appendChild(menu);
|
||||||
|
|
||||||
|
// Remove menu when clicking elsewhere
|
||||||
|
const removeMenu = (event: MouseEvent) => {
|
||||||
|
if (!menu.contains(event.target as Node)) {
|
||||||
|
menu.remove();
|
||||||
|
document.removeEventListener('click', removeMenu);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use setTimeout to avoid immediate removal
|
||||||
|
setTimeout(() => {
|
||||||
|
document.addEventListener('click', removeMenu);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
term.write('Welcome to Ixabata-shell!\r\n');
|
term.write('Welcome to Ixabata-shell!\r\n');
|
||||||
term.write('Type a command to begin. (Try ls, cd, cat)\r\n');
|
term.write('Type a command to begin. (Try ls, cd, cat)\r\n');
|
||||||
prompt();
|
prompt();
|
||||||
|
|
||||||
|
|
||||||
term.onKey(({ key, domEvent }) => {
|
term.onKey(({ key, domEvent }) => {
|
||||||
if (!xtermRef.current) return;
|
if (!xtermRef.current) return;
|
||||||
|
|
||||||
@ -305,40 +443,142 @@ ALERT_COLOR=#FF0000
|
|||||||
let suggestions: string[] = [];
|
let suggestions: string[] = [];
|
||||||
const availableCommands = ['ls', 'cd', 'cat', 'clear', 'help'];
|
const availableCommands = ['ls', 'cd', 'cat', 'clear', 'help'];
|
||||||
|
|
||||||
|
// If we're completing a command (first word)
|
||||||
if (inputParts.length === 0 && !currentLine.current.includes(' ')) {
|
if (inputParts.length === 0 && !currentLine.current.includes(' ')) {
|
||||||
suggestions = availableCommands.filter(c => c.startsWith(currentArgument));
|
suggestions = availableCommands.filter(c => c.startsWith(currentArgument));
|
||||||
} else {
|
} else {
|
||||||
const currentDirItems = Array.isArray(fileSystem[currentPath.current]) ? fileSystem[currentPath.current] as string[] : [];
|
// For path completion (after a command)
|
||||||
|
const command = inputParts[0];
|
||||||
|
|
||||||
|
// Only complete paths for commands that accept file paths
|
||||||
|
if (['ls', 'cd', 'cat'].includes(command)) {
|
||||||
|
// Handle absolute paths
|
||||||
|
if (currentArgument.startsWith('/')) {
|
||||||
|
const pathParts = currentArgument.split('/');
|
||||||
|
const lastPart = pathParts.pop() || '';
|
||||||
|
const basePath = pathParts.join('/') || '/';
|
||||||
|
|
||||||
|
if (fileSystem[basePath] && Array.isArray(fileSystem[basePath])) {
|
||||||
|
suggestions = (fileSystem[basePath] as string[])
|
||||||
|
.filter(item => item.startsWith(lastPart))
|
||||||
|
.map(item => {
|
||||||
|
// Return the full path for absolute completions
|
||||||
|
return basePath === '/' ? '/' + item : basePath + '/' + item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle relative paths
|
||||||
|
const currentDirItems = Array.isArray(fileSystem[currentPath.current])
|
||||||
|
? fileSystem[currentPath.current] as string[]
|
||||||
|
: [];
|
||||||
suggestions = currentDirItems.filter(item => item.startsWith(currentArgument));
|
suggestions = currentDirItems.filter(item => item.startsWith(currentArgument));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (suggestions.length === 1) {
|
if (suggestions.length === 1) {
|
||||||
const suggestion = suggestions[0];
|
const suggestion = suggestions[0];
|
||||||
let resolvedSuggestionPath = suggestion;
|
let resolvedSuggestionPath = suggestion;
|
||||||
if (!suggestion.startsWith('/')) {
|
|
||||||
|
// Handle path resolution for the suggestion
|
||||||
|
if (currentArgument.startsWith('/')) {
|
||||||
|
// For absolute paths, the suggestion is already the full path
|
||||||
|
resolvedSuggestionPath = suggestion;
|
||||||
|
} else {
|
||||||
|
// For relative paths, use the current directory
|
||||||
resolvedSuggestionPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + suggestion;
|
resolvedSuggestionPath = (currentPath.current === '/' ? '' : currentPath.current) + '/' + suggestion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize path
|
||||||
if (resolvedSuggestionPath !== '/' && resolvedSuggestionPath.endsWith('/')) {
|
if (resolvedSuggestionPath !== '/' && resolvedSuggestionPath.endsWith('/')) {
|
||||||
resolvedSuggestionPath = resolvedSuggestionPath.slice(0, -1);
|
resolvedSuggestionPath = resolvedSuggestionPath.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDirectory = Array.isArray(fileSystem[resolvedSuggestionPath]);
|
const isDirectory = Array.isArray(fileSystem[resolvedSuggestionPath]);
|
||||||
const completion = suggestion + (isDirectory ? '/' : ' ');
|
|
||||||
|
|
||||||
|
let completion: string;
|
||||||
|
if (currentArgument.startsWith('/')) {
|
||||||
|
// For absolute paths, replace the entire argument
|
||||||
|
completion = suggestion + (isDirectory ? '/' : ' ');
|
||||||
|
xtermRef.current.write('\x1b[2K\r'); // Clear the line
|
||||||
|
xtermRef.current.write(`root@ixabatasha.life:${currentPath.current}# `);
|
||||||
|
xtermRef.current.write((commandPart ? commandPart + ' ' : '') + completion);
|
||||||
|
currentLine.current = (commandPart ? commandPart + ' ' : '') + completion;
|
||||||
|
} else {
|
||||||
|
// For relative paths, append to the existing argument
|
||||||
|
completion = suggestion + (isDirectory ? '/' : ' ');
|
||||||
xtermRef.current.write(completion.substring(currentArgument.length));
|
xtermRef.current.write(completion.substring(currentArgument.length));
|
||||||
currentLine.current = (commandPart ? commandPart + ' ' : '') + completion;
|
currentLine.current = (commandPart ? commandPart + ' ' : '') + completion;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isDirectory || !completion.endsWith('/')) {
|
if (!isDirectory || !completion.endsWith('/')) {
|
||||||
currentLine.current = currentLine.current.trimEnd();
|
currentLine.current = currentLine.current.trimEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else if (suggestions.length > 1) {
|
} else if (suggestions.length > 1) {
|
||||||
xtermRef.current.write('\r\n' + suggestions.join('\t') + '\r\n');
|
// Show all possible completions in a bash-like format
|
||||||
|
xtermRef.current.write('\r\n');
|
||||||
|
|
||||||
|
// For display purposes, show just the item names, not full paths
|
||||||
|
const displaySuggestions = suggestions.map(s => {
|
||||||
|
if (currentArgument.startsWith('/')) {
|
||||||
|
return s.split('/').pop() || s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate the longest common prefix
|
||||||
|
let commonPrefix = '';
|
||||||
|
if (displaySuggestions.length > 0) {
|
||||||
|
const first = displaySuggestions[0];
|
||||||
|
for (let i = 0; i < first.length; i++) {
|
||||||
|
if (displaySuggestions.every(s => s[i] === first[i])) {
|
||||||
|
commonPrefix += first[i];
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a common prefix, complete up to that point
|
||||||
|
if (commonPrefix.length > (currentArgument.startsWith('/') ? currentArgument.split('/').pop()?.length || 0 : currentArgument.length)) {
|
||||||
|
if (currentArgument.startsWith('/')) {
|
||||||
|
const pathParts = currentArgument.split('/');
|
||||||
|
pathParts.pop();
|
||||||
|
const newPath = pathParts.join('/') + '/' + commonPrefix;
|
||||||
|
xtermRef.current.write('\x1b[2K\r'); // Clear the line
|
||||||
|
xtermRef.current.write(`root@ixabatasha.life:${currentPath.current}# `);
|
||||||
|
xtermRef.current.write((commandPart ? commandPart + ' ' : '') + newPath);
|
||||||
|
currentLine.current = (commandPart ? commandPart + ' ' : '') + newPath;
|
||||||
|
} else {
|
||||||
|
xtermRef.current.write(commonPrefix.substring(currentArgument.length));
|
||||||
|
currentLine.current = (commandPart ? commandPart + ' ' : '') + commonPrefix;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Display all options in columns
|
||||||
|
const maxLength = Math.max(...displaySuggestions.map(s => s.length)) + 2;
|
||||||
|
const columns = Math.floor(term.cols / maxLength);
|
||||||
|
const rows = Math.ceil(displaySuggestions.length / columns);
|
||||||
|
|
||||||
|
for (let row = 0; row < rows; row++) {
|
||||||
|
let line = '';
|
||||||
|
for (let col = 0; col < columns; col++) {
|
||||||
|
const index = row + (col * rows);
|
||||||
|
if (index < displaySuggestions.length) {
|
||||||
|
const item = displaySuggestions[index];
|
||||||
|
const fullPath = suggestions[index];
|
||||||
|
const isDir = Array.isArray(fileSystem[fullPath.startsWith('/') ? fullPath : (currentPath.current === '/' ? '' : currentPath.current) + '/' + fullPath]);
|
||||||
|
line += item + (isDir ? '/' : '') + ' '.repeat(maxLength - item.length - (isDir ? 1 : 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xtermRef.current.write(line + '\r\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reprint the prompt and current input
|
||||||
prompt();
|
prompt();
|
||||||
xtermRef.current.write(currentLine.current);
|
xtermRef.current.write(currentLine.current);
|
||||||
}
|
}
|
||||||
// If no suggestions, do nothing (or beep: xtermRef.current.write('\x07');)
|
}
|
||||||
|
// If no suggestions, do nothing
|
||||||
} else if (printable && key.length === 1) { // Ensure it's a single character, not a special key like 'Shift'
|
} else if (printable && key.length === 1) { // Ensure it's a single character, not a special key like 'Shift'
|
||||||
xtermRef.current.write(key);
|
xtermRef.current.write(key);
|
||||||
currentLine.current += key;
|
currentLine.current += key;
|
||||||
|
@ -26,10 +26,39 @@ const title = "root@ixabatasha.life";
|
|||||||
- How to find: Look for the "max retries" count in the security_daemon error
|
- How to find: Look for the "max retries" count in the security_daemon error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Define the solutions for the combination lock
|
// Get solutions from environment variables (server-side only)
|
||||||
const solution1 = 29; // Binary puzzle solution
|
const solution1 = parseInt(process.env.SOLUTION_1 || import.meta.env.SOLUTION_1 || '29');
|
||||||
const solution2 = 31; // Hex color code puzzle solution
|
const solution2 = parseInt(process.env.SOLUTION_2 || import.meta.env.SOLUTION_2 || '31');
|
||||||
const solution3 = 5; // Log file analysis puzzle solution
|
const solution3 = parseInt(process.env.SOLUTION_3 || import.meta.env.SOLUTION_3 || '5');
|
||||||
|
|
||||||
|
// Generate puzzle content server-side
|
||||||
|
const puzzle1Content = `System memory address reference:
|
||||||
|
Register B1: ${solution1.toString(2).padStart(8, '0')}
|
||||||
|
Note: Always decode binary values when transferring between subsystems.`;
|
||||||
|
|
||||||
|
const puzzle2Content = `# 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.`;
|
||||||
|
|
||||||
|
const puzzle3Content = `[ERROR 001] System boot failure sequence A.
|
||||||
|
[INFO 002] Retrying boot sequence B.
|
||||||
|
[ERROR 003] Disk read error on /dev/sda1.
|
||||||
|
[WARN 004] Network interface eth0 flapping.
|
||||||
|
[INFO 005] System recovered.
|
||||||
|
[DEBUG 006] User 'admin' login attempt.
|
||||||
|
[ERROR 007] Authentication failure for user 'admin'.
|
||||||
|
[INFO 008] User 'hacker' login successful.
|
||||||
|
[WARN 009] Unusual activity detected from IP 192.168.1.101.
|
||||||
|
[ERROR 010] Critical process 'security_daemon' terminated unexpectedly.
|
||||||
|
[INFO 011] Attempting to restart 'security_daemon'.
|
||||||
|
[ERROR 012] Failed to restart 'security_daemon'. Max retries (${solution3}) reached.
|
||||||
|
[ERROR 014] System shutting down due to critical errors.
|
||||||
|
[RECOVER] Backup facility requires retry count for authentication sequence.`;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@ -48,9 +77,9 @@ const solution3 = 5; // Log file analysis puzzle solution
|
|||||||
<div id="terminal-container" class="w-full h-full">
|
<div id="terminal-container" class="w-full h-full">
|
||||||
<TerminalComponent
|
<TerminalComponent
|
||||||
client:only="react"
|
client:only="react"
|
||||||
solution1={solution1}
|
puzzle1Content={puzzle1Content}
|
||||||
solution2={solution2}
|
puzzle2Content={puzzle2Content}
|
||||||
solution3={solution3}
|
puzzle3Content={puzzle3Content}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
Reference in New Issue
Block a user