Compare commits

...

7 Commits

Author SHA1 Message Date
9b9ab8593f call to action for chat 2026-03-07 12:51:25 +11:00
6529eb2e6b neo update, bridge too 2026-03-07 12:49:27 +11:00
7d9a182b4d added bridge 2026-03-07 12:38:53 +11:00
864b1e3ecc update the message autocomplete + change up logo 2026-03-07 11:51:03 +11:00
628a0cb948 Added sounds + mentions 2026-03-07 09:52:42 +11:00
3d987dced1 Update favicon paths and enhance chat functionality with public message fetching 2026-03-07 09:16:57 +11:00
288b48ddab Add terminal commands for enhanced functionality
- Implemented 'about' command to provide information about the terminal.
- Created 'banner' command to display a welcome message based on terminal width.
- Added 'bluesky' command to fetch and display recent posts from Bluesky.
- Introduced 'clear' command to clear the terminal screen.
- Developed 'color' command to change the terminal's color scheme.
- Added 'contact' command to display contact information.
- Implemented 'date' command to show the current date and time.
- Created 'echo' command to echo back user messages.
- Added 'github' command to simulate opening the GitHub repository.
- Implemented 'help' command to list available commands.
- Developed 'history' command to show command history.
- Added 'privacy' command to display the privacy policy.
- Implemented 'whoami' command to show user information.
2026-03-07 06:22:10 +11:00
37 changed files with 2018 additions and 309 deletions

View File

@@ -22,12 +22,65 @@ const BIOS_LINES = [
{ text: 'Starting LIT.RUV.WTF Terminal...', class: 'highlight', delay: 400 }
];
const BOOT_STARTUP_SOUND_PATH = window.location.hostname.endsWith('neocities.org')
? 'https://lit.ruv.wtf/sounds/551405__nakkivene66__old-pc-startup-idle-shutdown.wav'
: 'sounds/551405__nakkivene66__old-pc-startup-idle-shutdown.wav';
let bootStartupSoundPlayed = false;
let bootStartupSoundUnlockBound = false;
/**
* Play the boot startup sound as early as possible.
* Falls back to first user interaction when autoplay is blocked.
* @returns {Promise<void>}
*/
async function playBootStartupSound() {
if (bootStartupSoundPlayed) {
return;
}
try {
const startupSound = new Audio(BOOT_STARTUP_SOUND_PATH);
startupSound.preload = 'auto';
startupSound.volume = 0.55;
await startupSound.play();
bootStartupSoundPlayed = true;
bootStartupSoundUnlockBound = false;
return;
} catch (error) {
if (bootStartupSoundUnlockBound) {
return;
}
bootStartupSoundUnlockBound = true;
const unlockAndPlay = async () => {
if (bootStartupSoundPlayed) {
return;
}
try {
const deferredSound = new Audio(BOOT_STARTUP_SOUND_PATH);
deferredSound.preload = 'auto';
deferredSound.volume = 0.55;
await deferredSound.play();
bootStartupSoundPlayed = true;
} catch (retryError) {
// Keep boot sequence running even if sound cannot play.
}
};
document.addEventListener('pointerdown', unlockAndPlay, { once: true });
document.addEventListener('keydown', unlockAndPlay, { once: true });
}
}
/**
* Run the BIOS boot animation
* @returns {Promise} Resolves when animation is complete
*/
function runBootAnimation() {
return new Promise((resolve) => {
void playBootStartupSound();
const bootScreen = document.getElementById('bootScreen');
const biosText = document.getElementById('biosText');
const pageFade = document.getElementById('pageFade');

View File

@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>user@litruv.neocities.org - Terminal</title>
<meta name="description" content="Interactive terminal interface for Lit.ruv.wtf - A retro-styled command line experience with chat, documentation, and more.">
<!-- Favicons -->
<link rel="icon" type="image/png" sizes="16x16" href="logos/16px.png">
<link rel="icon" type="image/png" sizes="32x32" href="logos/32px.png">
<link rel="icon" type="image/png" sizes="48x48" href="logos/48px.png">
<link rel="icon" type="image/png" sizes="64x64" href="logos/64px.png">
<link rel="icon" type="image/png" sizes="128x128" href="logos/128px.png">
<link rel="icon" type="image/png" sizes="256x256" href="logos/256px.png">
<link rel="apple-touch-icon" sizes="180x180" href="logos/256px.png">
<!-- Open Graph / Social Media -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://litruv.neocities.org/">
<meta property="og:title" content="user@litruv.neocities.org - Terminal">
<meta property="og:description" content="Interactive terminal interface with chat, docs, and retro vibes">
<meta property="og:image" content="https://lit.ruv.wtf/banner.webp">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="https://litruv.neocities.org/">
<meta name="twitter:title" content="user@litruv.neocities.org - Terminal">
<meta name="twitter:description" content="Interactive terminal interface with chat, docs, and retro vibes">
<meta name="twitter:image" content="https://lit.ruv.wtf/banner.webp">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.min.css" />
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="page-fade-overlay" id="pageFade"></div>
<div class="boot-screen" id="bootScreen">
<pre id="biosText"></pre>
</div>
<div class="crt-border-overlay"></div>
<div class="container">
<div class="terminal-header">
<div class="header-left">
<span class="terminal-title">LITRUV.NEOCITIES.ORG TERMINAL</span>
<span class="header-links">
<a href="https://lit.ruv.wtf/docs/" target="_blank" class="header-link">Docs</a>
<a href="https://github.com/litruv" target="_blank" class="header-link">GitHub</a>
<a href="https://bsky.app/profile/lit.mates.dev" target="_blank" class="header-link">Bluesky</a>
</span>
</div>
<div class="header-right">
<span class="system-time" id="systemTime"></span>
</div>
</div>
<div id="terminal"></div>
<div class="status-bar">
<span class="status-left">Ready</span>
<span class="status-right">
<span class="quick-commands">
<button class="quick-cmd" onclick="runQuickCommand('help')" title="Show all commands">help</button>
<button class="quick-cmd" onclick="runQuickCommand('about')" title="About this terminal">about</button>
<button class="quick-cmd" onclick="runQuickCommand('chat')" title="Join chat room">chat</button>
<button class="quick-cmd" onclick="runQuickCommand('clear')" title="Clear screen">clear</button>
</span>
</span>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.9.0/lib/xterm-addon-web-links.min.js"></script>
<script src="boot.js"></script>
<script type="module" src="terminal.js"></script>
</body>
</html>

View File

@@ -7,13 +7,13 @@
<meta name="description" content="Interactive terminal interface for Lit.ruv.wtf - A retro-styled command line experience with chat, documentation, and more.">
<!-- Favicons -->
<link rel="icon" type="image/png" sizes="16x16" href="/logos/16px.png">
<link rel="icon" type="image/png" sizes="32x32" href="/logos/32px.png">
<link rel="icon" type="image/png" sizes="48x48" href="/logos/48px.png">
<link rel="icon" type="image/png" sizes="64x64" href="/logos/64px.png">
<link rel="icon" type="image/png" sizes="128x128" href="/logos/128px.png">
<link rel="icon" type="image/png" sizes="256x256" href="/logos/256px.png">
<link rel="apple-touch-icon" sizes="180x180" href="/logos/256px.png">
<link rel="icon" type="image/png" sizes="16x16" href="logos/16px.png">
<link rel="icon" type="image/png" sizes="32x32" href="logos/32px.png">
<link rel="icon" type="image/png" sizes="48x48" href="logos/48px.png">
<link rel="icon" type="image/png" sizes="64x64" href="logos/64px.png">
<link rel="icon" type="image/png" sizes="128x128" href="logos/128px.png">
<link rel="icon" type="image/png" sizes="256x256" href="logos/256px.png">
<link rel="apple-touch-icon" sizes="180x180" href="logos/256px.png">
<!-- Open Graph / Social Media -->
<meta property="og:type" content="website">
@@ -70,6 +70,6 @@
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.9.0/lib/xterm-addon-web-links.min.js"></script>
<script src="boot.js"></script>
<script src="terminal.js"></script>
<script type="module" src="terminal.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Matrix Chat Bridge</title>
<meta name="robots" content="noindex, nofollow">
</head>
<body>
<!-- This page acts as a bridge for Matrix API calls from CSP-restricted hosts -->
<script src="matrix-bridge.js"></script>
</body>
</html>

234
website/matrix-bridge.js Normal file
View File

@@ -0,0 +1,234 @@
/**
* Matrix Chat Bridge - Iframe communication layer for CSP-restricted hosts
* Handles Matrix API calls via postMessage from parent window
*/
// Allow requests from these origins
const ALLOWED_ORIGINS = [
'https://litruv.neocities.org',
'https://lit.ruv.wtf',
'http://localhost:3000',
'http://localhost:8080',
'http://127.0.0.1:3000',
'http://127.0.0.1:8080'
];
/**
* Matrix API configuration
*/
const MATRIX_CONFIG = {
homeserver: 'https://chat.ruv.wtf',
publicReadToken: 'syt_Z2VuZXJhbGNoYXQtcmVhZG9ubHk_sikLltUtfbHlztnanEVm_2icJ1o'
};
/**
* Make a Matrix API call
* @param {string} endpoint - API endpoint path
* @param {object} options - Fetch options
* @param {string|null} accessToken - Optional access token
* @returns {Promise<any>}
*/
async function matrixApiFetch(endpoint, options = {}, accessToken = null) {
const url = `${MATRIX_CONFIG.homeserver}${endpoint}`;
const headers = {
'Content-Type': 'application/json',
...options.headers
};
if (accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`;
}
try {
const response = await fetch(url, {
...options,
headers
});
const data = await response.json();
if (!response.ok) {
return { error: true, ...data };
}
return data;
} catch (error) {
return {
error: true,
errcode: 'M_NETWORK_ERROR',
error: error.message
};
}
}
/**
* Handle Matrix API requests from parent window
*/
window.addEventListener('message', async (event) => {
// Verify origin
if (!ALLOWED_ORIGINS.includes(event.origin)) {
console.warn('[Matrix Bridge] Rejected message from unauthorized origin:', event.origin);
return;
}
const { type, requestId, payload } = event.data;
if (!type || !requestId) {
return;
}
let result;
try {
switch (type) {
case 'matrix:api':
result = await handleApiRequest(payload);
break;
case 'matrix:auth':
result = await handleAuth(payload);
break;
case 'matrix:sendMessage':
result = await handleSendMessage(payload);
break;
case 'matrix:fetchPresence':
result = await handleFetchPresence(payload);
break;
case 'matrix:fetchLastMessage':
result = await handleFetchLastMessage(payload);
break;
case 'matrix:sync':
result = await handleSync(payload);
break;
case 'matrix:resolveAlias':
result = await handleResolveAlias(payload);
break;
default:
result = {
error: true,
errcode: 'M_UNKNOWN_REQUEST',
error: `Unknown request type: ${type}`
};
}
} catch (error) {
result = {
error: true,
errcode: 'M_BRIDGE_ERROR',
error: error.message
};
}
// Send response back to parent
event.source.postMessage({
type: `${type}:response`,
requestId,
payload: result
}, event.origin);
});
/**
* Handle generic Matrix API request
*/
async function handleApiRequest({ endpoint, method = 'GET', body = null, accessToken = null }) {
return await matrixApiFetch(
`/_matrix/client/r0${endpoint}`,
{
method,
body: body ? JSON.stringify(body) : undefined
},
accessToken
);
}
/**
* Handle authentication request
*/
async function handleAuth({ username, password }) {
return await matrixApiFetch('/_matrix/client/r0/login', {
method: 'POST',
body: JSON.stringify({
type: 'm.login.password',
identifier: {
type: 'm.id.user',
user: username
},
password: password
})
});
}
/**
* Handle send message request
*/
async function handleSendMessage({ roomId, content, accessToken, txnId }) {
const msgtype = content.msgtype || 'm.text';
const endpoint = `/_matrix/client/r0/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${encodeURIComponent(txnId)}`;
return await matrixApiFetch(endpoint, {
method: 'PUT',
body: JSON.stringify(content)
}, accessToken);
}
/**
* Handle fetch presence request
*/
async function handleFetchPresence({ userId }) {
return await matrixApiFetch(
`/_matrix/client/r0/presence/${encodeURIComponent(userId)}/status`,
{ method: 'GET' },
MATRIX_CONFIG.publicReadToken
);
}
/**
* Handle fetch last message request
*/
async function handleFetchLastMessage({ roomId }) {
return await matrixApiFetch(
`/_matrix/client/r0/rooms/${encodeURIComponent(roomId)}/messages?dir=b&limit=1`,
{ method: 'GET' },
MATRIX_CONFIG.publicReadToken
);
}
/**
* Handle sync request
*/
async function handleSync({ accessToken, since, timeout = 0 }) {
let endpoint = `/_matrix/client/r0/sync?timeout=${timeout}`;
if (since) {
endpoint += `&since=${encodeURIComponent(since)}`;
}
return await matrixApiFetch(endpoint, {
method: 'GET'
}, accessToken);
}
/**
* Handle room alias resolution
*/
async function handleResolveAlias({ roomAlias }) {
return await matrixApiFetch(
`/_matrix/client/r0/directory/room/${encodeURIComponent(roomAlias)}`,
{ method: 'GET' },
MATRIX_CONFIG.publicReadToken
);
}
// Notify parent that bridge is ready
window.addEventListener('load', () => {
if (window.parent !== window) {
window.parent.postMessage({
type: 'matrix:bridge:ready'
}, '*');
}
console.log('[Matrix Bridge] Ready and listening for requests');
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 KiB

After

Width:  |  Height:  |  Size: 268 KiB

View File

@@ -0,0 +1,25 @@
/**
* About command - Information about this terminal
*/
export default {
description: 'About this terminal',
execute: (term, writeClickable, VERSION) => {
return [
'',
'╔════════════════════════════════════════════════════════════╗',
'║ LIT.RUV.WTF TERMINAL ║',
'╚════════════════════════════════════════════════════════════╝',
'',
' A classic terminal interface built with xterm.js',
' Features: Keyboard navigation, Mouse support, CRT effects',
' Version: ' + VERSION,
' Built: ' + new Date().getFullYear(),
'',
' Technologies:',
' • xterm.js - Terminal emulator',
' • JavaScript - Terminal logic',
' • CSS3 - Classic CRT styling',
''
].join('\r\n');
}
};

View File

@@ -0,0 +1,24 @@
/**
* Banner command - Display welcome banner
*/
export default {
description: 'Display welcome banner',
execute: (term, writeClickable, VERSION, args, commandHistory, welcomeBannerFull, welcomeBannerCompact, welcomeBannerMinimal) => {
const cols = term.cols;
if (cols >= 78) {
term.writeln(welcomeBannerFull.split('\r\n').slice(0, -3).join('\r\n'));
writeClickable(' Type [command=help] for available commands.');
term.writeln(' Use ↑/↓ arrows to navigate command history.');
term.writeln('');
} else if (cols >= 40) {
term.writeln(welcomeBannerCompact.split('\r\n').slice(0, -2).join('\r\n'));
writeClickable(' Welcome! Type [command=help] for commands.');
term.writeln('');
} else {
term.writeln(welcomeBannerMinimal.split('\r\n').slice(0, -2).join('\r\n'));
writeClickable(' Type [command=help]');
term.writeln('');
}
return null;
}
};

View File

@@ -0,0 +1,79 @@
/**
* Bluesky command - Fetch recent posts from Bluesky
*/
export default {
description: 'Fetch recent posts from Bluesky',
execute: async (term, writeClickable, VERSION, args) => {
const actor = 'lit.mates.dev';
const limit = args[0] ? parseInt(args[0]) : 5;
try {
term.writeln('\r\n Fetching posts from Bluesky...\r\n');
const response = await fetch(
`https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${actor}&limit=${limit}`
);
if (!response.ok) {
return ' Error: Unable to fetch Bluesky posts';
}
const data = await response.json();
if (!data.feed || data.feed.length === 0) {
return ' No posts found.';
}
let output = [' ╔════════════════════════════════════════════════════╗'];
output.push(' ║ BLUESKY POSTS - @lit.mates.dev ║');
output.push(' ╚════════════════════════════════════════════════════╝');
output.push('');
// Reverse to show oldest first, latest at bottom
const reversedFeed = [...data.feed].reverse();
reversedFeed.forEach((item, idx) => {
const post = item.post;
const text = post.record.text;
const createdAt = new Date(post.record.createdAt);
const date = createdAt.toLocaleDateString();
const time = createdAt.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit'
});
// Extract post ID from URI (at://did:plc:xxx/app.bsky.feed.post/{postId})
const postId = post.uri.split('/').pop();
const postUrl = `https://bsky.app/profile/${actor}/post/${postId}`;
output.push(` [${idx + 1}] ${date} ${time}`);
output.push(` ${postUrl}`);
output.push(' ────────────────────────────────────────');
// Wrap text to max 50 chars
const words = text.split(' ');
let line = ' ';
words.forEach(word => {
if (line.length + word.length + 1 > 52) {
output.push(line);
line = ' ' + word;
} else {
line += (line.length > 2 ? ' ' : '') + word;
}
});
if (line.length > 2) output.push(line);
output.push('');
output.push(`${post.likeCount || 0}${post.repostCount || 0} 💬 ${post.replyCount || 0}`);
output.push('');
});
output.push(` Usage: bluesky [count] (default: 5, max: 20)`);
output.push('');
return output.join('\r\n');
} catch (error) {
return ' Error: Failed to connect to Bluesky API';
}
}
};

View File

@@ -0,0 +1,10 @@
/**
* Clear command - Clear terminal screen
*/
export default {
description: 'Clear terminal screen',
execute: (term) => {
term.clear();
return null;
}
};

View File

@@ -0,0 +1,38 @@
/**
* Color command - Change color scheme
*/
export default {
description: 'Change color scheme',
execute: (term, writeClickable, VERSION, args) => {
const scheme = args[0] || '';
const schemes = {
green: { bg: '#001800', fg: '#00ff00', border: '#0f0' },
amber: { bg: '#1a0f00', fg: '#ffb000', border: '#ffb000' },
blue: { bg: '#000818', fg: '#00a0ff', border: '#00a0ff' },
white: { bg: '#0a0a0a', fg: '#e0e0e0', border: '#999' }
};
if (!scheme || !schemes[scheme]) {
return [
'',
' Available color schemes:',
' • green - Classic green terminal',
' • amber - Amber monochrome',
' • blue - IBM blue',
' • white - White phosphor',
'',
' Usage: color [scheme]',
''
].join('\r\n');
}
const colors = schemes[scheme];
term.options.theme.background = colors.bg;
term.options.theme.foreground = colors.fg;
document.querySelector('.container').style.borderColor = colors.border;
document.querySelector('.container').style.background = colors.bg;
document.body.style.color = colors.fg;
return `\r\n Color scheme changed to: ${scheme}\r\n`;
}
};

View File

@@ -0,0 +1,16 @@
/**
* Contact command - Contact information
*/
export default {
description: 'Contact information',
execute: () => {
return [
'',
' Contact Information:',
' ────────────────────',
' Email: contact@lit.ruv.wtf',
' Web: https://lit.ruv.wtf',
''
].join('\r\n');
}
};

View File

@@ -0,0 +1,20 @@
/**
* Date command - Display current date and time
*/
export default {
description: 'Display current date and time',
execute: () => {
const now = new Date();
return [
'',
' ' + now.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}),
' ' + now.toLocaleTimeString('en-US'),
''
].join('\r\n');
}
};

View File

@@ -0,0 +1,9 @@
/**
* Echo command - Echo back message
*/
export default {
description: 'Echo back message',
execute: (term, writeClickable, VERSION, args) => {
return args.join(' ') || '';
}
};

View File

@@ -0,0 +1,9 @@
/**
* GitHub command - Open GitHub repository
*/
export default {
description: 'Open GitHub repository',
execute: () => {
return '\r\n Opening GitHub...\r\n (This would open your repository URL)\r\n';
}
};

View File

@@ -0,0 +1,32 @@
/**
* Help command - Display available commands
*/
export default {
description: 'Display available commands',
execute: (term, writeClickable) => {
term.writeln('');
term.writeln('╔════════════════════════════════════════════════════════════╗');
term.writeln('║ AVAILABLE COMMANDS ║');
term.writeln('╚════════════════════════════════════════════════════════════╝');
term.writeln('');
writeClickable(' [command=help] - Display this help message');
writeClickable(' [command=about] - Information about this terminal');
writeClickable(' [command=clear] - Clear the terminal screen');
term.writeln(' echo - Echo back your message (usage: echo [message])');
writeClickable(' [command=date] - Display current date and time');
writeClickable(' [command=whoami] - Display current user information');
writeClickable(' [command=history] - Show command history');
writeClickable(' [command=color] - Change terminal color scheme');
writeClickable(' [command=banner] - Display welcome banner');
writeClickable(' [command=bluesky] - Fetch recent posts from Bluesky');
writeClickable(' [command=chat] - Enter interactive chat (type /quit to exit)');
writeClickable(' [command=github] - Visit GitHub repository');
writeClickable(' [command=contact] - Display contact information');
writeClickable(' [command=privacy] - Display privacy policy');
term.writeln('');
term.writeln('Navigate: Use ↑/↓ arrows for command history');
term.writeln('Mouse: Click commands to run them');
term.writeln('');
return null;
}
};

View File

@@ -0,0 +1,17 @@
/**
* History command - Show command history
*/
export default {
description: 'Show command history',
execute: (term, writeClickable, VERSION, args, commandHistory) => {
if (commandHistory.length === 0) {
return '\r\n No command history yet.\r\n';
}
let output = ['\r\n Command History:', ' ───────────────'];
commandHistory.forEach((cmd, idx) => {
output.push(` ${(idx + 1).toString().padStart(3, ' ')} ${cmd}`);
});
output.push('');
return output.join('\r\n');
}
};

View File

@@ -0,0 +1,36 @@
/**
* Privacy command - Privacy policy
*/
export default {
description: 'Privacy policy',
execute: () => {
return [
'',
'╔════════════════════════════════════════════════════════════╗',
'║ PRIVACY POLICY ║',
'╚════════════════════════════════════════════════════════════╝',
'',
' Data Collection:',
' ────────────────',
' • This terminal uses localStorage to save your chat session',
' • Chat messages are stored on our Matrix homeserver',
' • No cookies or tracking scripts are used',
' • No analytics or third-party tracking',
'',
' Matrix Chat:',
' ────────────',
' • Chat credentials stored locally in your browser',
' • Messages sent through Matrix protocol (b.ruv.wtf)',
' • Use "chat disconnect" to clear stored credentials',
'',
' Your Rights:',
' ────────────',
' • Clear localStorage anytime via browser settings',
' • Request data deletion: contact@lit.ruv.wtf',
' • All code is open source and auditable',
'',
' Updates: Privacy policy last updated March 2026',
''
].join('\r\n');
}
};

View File

@@ -0,0 +1,15 @@
/**
* Whoami command - Display user information
*/
export default {
description: 'Display user information',
execute: () => {
return [
'',
' User: visitor@lit.ruv.wtf',
' Session: ' + Date.now(),
' Terminal: xterm.js',
''
].join('\r\n');
}
};

Binary file not shown.

BIN
website/sounds/poweroff.mp3 Normal file

Binary file not shown.

BIN
website/sounds/poweron.mp3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -214,6 +214,32 @@ body {
color: rgba(0, 255, 0, 0.3);
}
.mention-suggestions {
position: absolute;
z-index: 11;
display: flex;
gap: 6px;
max-width: 85%;
padding: 1px 4px;
border: 1px solid rgba(0, 255, 0, 0.45);
background: rgba(0, 24, 0, 0.92);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
pointer-events: none;
}
.mention-suggestion {
color: #66ff66;
opacity: 0.9;
}
.mention-suggestion.selected {
color: #001800;
background: #66ff66;
padding: 0 2px;
}
.status-bar {
background: transparent;
border-top: 2px solid #0f0;
@@ -489,6 +515,29 @@ body.keyboard-open .status-bar {
padding: 3px 8px;
}
/* Small viewport (under 1280x720) - detected via JS */
body.small-viewport .crt-border-overlay {
display: none;
}
body.small-viewport .container,
body.small-viewport .boot-screen {
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 0;
border: none;
}
body.small-viewport .terminal-header {
border-bottom: 1px solid #0f0;
}
body.small-viewport .status-bar {
border-top: 1px solid #0f0;
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 8px;

File diff suppressed because it is too large Load Diff