mirror of
https://github.com/litruv/lit.ruv.wtf.git
synced 2026-07-24 02:36:02 +10:00
Implement BIOS boot animation and enhance terminal interactivity with clickable commands
This commit is contained in:
81
website/boot.js
Normal file
81
website/boot.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* BIOS Boot Animation
|
||||||
|
* Displays a retro BIOS-style boot sequence before the terminal loads
|
||||||
|
*/
|
||||||
|
|
||||||
|
const BIOS_LINES = [
|
||||||
|
{ text: 'LitRuv BIOS v4.20.69', class: 'white', delay: 300 },
|
||||||
|
{ text: 'Copyright (C) 2024-2026 LitRuv Industries', class: '', delay: 200 },
|
||||||
|
{ text: '', delay: 400 },
|
||||||
|
{ text: 'CPU: Quantum Core i9-42069 @ 6.9GHz', class: '', delay: 300 },
|
||||||
|
{ text: 'Memory Test: ', class: '', inline: true, delay: 200 },
|
||||||
|
{ text: '65536K OK', class: 'highlight', delay: 600 },
|
||||||
|
{ text: '', delay: 300 },
|
||||||
|
{ text: 'Detecting Primary Master... LitDrive SSD 2TB', class: '', delay: 400 },
|
||||||
|
{ text: 'Detecting Primary Slave... None', class: '', delay: 300 },
|
||||||
|
{ text: '', delay: 400 },
|
||||||
|
{ text: 'Initializing Terminal Interface...', class: 'highlight', delay: 500 },
|
||||||
|
{ text: 'Loading modules: [', class: '', inline: true, delay: 200 },
|
||||||
|
{ text: '████████████████████', class: 'highlight', inline: true, delay: 800 },
|
||||||
|
{ text: '] 100%', class: '', delay: 300 },
|
||||||
|
{ text: '', delay: 200 },
|
||||||
|
{ text: 'Starting LIT.RUV.WTF Terminal...', class: 'highlight', delay: 400 }
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the BIOS boot animation
|
||||||
|
* @returns {Promise} Resolves when animation is complete
|
||||||
|
*/
|
||||||
|
function runBootAnimation() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const bootScreen = document.getElementById('bootScreen');
|
||||||
|
const biosText = document.getElementById('biosText');
|
||||||
|
const pageFade = document.getElementById('pageFade');
|
||||||
|
|
||||||
|
if (!bootScreen || !biosText) {
|
||||||
|
if (pageFade) pageFade.classList.add('fade-out');
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start fade in from black after brief delay
|
||||||
|
setTimeout(() => {
|
||||||
|
if (pageFade) pageFade.classList.add('fade-out');
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
let lineIndex = 0;
|
||||||
|
let totalDelay = 800; // Start after fade begins
|
||||||
|
const baseDelay = 150;
|
||||||
|
|
||||||
|
BIOS_LINES.forEach((line, index) => {
|
||||||
|
const delay = totalDelay;
|
||||||
|
totalDelay += line.delay || baseDelay;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const span = document.createElement('span');
|
||||||
|
if (line.class) {
|
||||||
|
span.className = line.class;
|
||||||
|
}
|
||||||
|
span.textContent = line.text;
|
||||||
|
biosText.appendChild(span);
|
||||||
|
|
||||||
|
if (!line.inline) {
|
||||||
|
biosText.appendChild(document.createTextNode('\n'));
|
||||||
|
}
|
||||||
|
}, delay);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fade out and remove boot screen
|
||||||
|
setTimeout(() => {
|
||||||
|
bootScreen.classList.add('fade-out');
|
||||||
|
setTimeout(() => {
|
||||||
|
bootScreen.style.display = 'none';
|
||||||
|
if (pageFade) pageFade.style.display = 'none';
|
||||||
|
resolve();
|
||||||
|
}, 500);
|
||||||
|
}, totalDelay + 300);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for use in terminal.js
|
||||||
|
window.runBootAnimation = runBootAnimation;
|
||||||
@@ -30,6 +30,10 @@
|
|||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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="crt-border-overlay"></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="terminal-header">
|
<div class="terminal-header">
|
||||||
@@ -48,13 +52,21 @@
|
|||||||
<div id="terminal"></div>
|
<div id="terminal"></div>
|
||||||
<div class="status-bar">
|
<div class="status-bar">
|
||||||
<span class="status-left">Ready</span>
|
<span class="status-left">Ready</span>
|
||||||
<span class="status-right">Type 'help' for available commands</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('projects')" title="View projects">projects</button>
|
||||||
|
<button class="quick-cmd" onclick="runQuickCommand('chat')" title="Join chat room">chat</button>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</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@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-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="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 src="terminal.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -6,13 +6,69 @@
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Courier New', Courier, monospace;
|
font-family: 'Courier New', Courier, monospace;
|
||||||
background: #000;
|
background: #001800;
|
||||||
color: #0f0;
|
color: #0f0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Initial black screen fade-in */
|
||||||
|
.page-fade-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #000;
|
||||||
|
z-index: 20000;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-fade-overlay.fade-out {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Boot screen */
|
||||||
|
.boot-screen {
|
||||||
|
position: absolute;
|
||||||
|
top: 80px;
|
||||||
|
left: 80px;
|
||||||
|
right: 80px;
|
||||||
|
bottom: 100px;
|
||||||
|
background: #001800;
|
||||||
|
z-index: 100;
|
||||||
|
padding: 20px;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #aaa;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: opacity 0.5s ease-out;
|
||||||
|
border: 3px solid #0f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boot-screen.fade-out {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boot-screen pre {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boot-screen .highlight {
|
||||||
|
color: #0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boot-screen .white {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.crt-border-overlay {
|
.crt-border-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -115,6 +171,11 @@ body {
|
|||||||
cursor: text;
|
cursor: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide xterm cursor completely */
|
||||||
|
.xterm-cursor-layer {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Inline terminal input */
|
/* Inline terminal input */
|
||||||
.terminal-inline-input {
|
.terminal-inline-input {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -131,6 +192,22 @@ body {
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
min-width: 50%;
|
min-width: 50%;
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clickable commands in terminal */
|
||||||
|
.xterm-screen a,
|
||||||
|
.xterm .xterm-link-layer a {
|
||||||
|
color: #0f0 !important;
|
||||||
|
text-decoration: underline !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
text-shadow: 0 0 5px #0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-screen a:hover,
|
||||||
|
.xterm .xterm-link-layer a:hover {
|
||||||
|
color: #0ff !important;
|
||||||
|
text-shadow: 0 0 8px #0ff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-inline-input::placeholder {
|
.terminal-inline-input::placeholder {
|
||||||
@@ -155,6 +232,36 @@ body {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Quick command buttons */
|
||||||
|
.quick-commands {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-cmd {
|
||||||
|
background: transparent;
|
||||||
|
color: #0f0;
|
||||||
|
border: 1px solid #0f0;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
text-shadow: 0 0 3px #0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-cmd:hover {
|
||||||
|
background: #0f0;
|
||||||
|
color: #001800;
|
||||||
|
box-shadow: 0 0 8px #0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-cmd:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
/* CRT scanlines effect */
|
/* CRT scanlines effect */
|
||||||
.container::before {
|
.container::before {
|
||||||
content: " ";
|
content: " ";
|
||||||
@@ -189,7 +296,8 @@ body {
|
|||||||
border-image-width: 40px;
|
border-image-width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container,
|
||||||
|
.boot-screen {
|
||||||
top: 40px;
|
top: 40px;
|
||||||
left: 40px;
|
left: 40px;
|
||||||
right: 40px;
|
right: 40px;
|
||||||
@@ -219,6 +327,15 @@ body {
|
|||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quick-commands {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-cmd {
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
@@ -227,13 +344,19 @@ body {
|
|||||||
border-image-width: 25px;
|
border-image-width: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container,
|
||||||
|
.boot-screen {
|
||||||
top: 25px;
|
top: 25px;
|
||||||
left: 25px;
|
left: 25px;
|
||||||
right: 25px;
|
right: 25px;
|
||||||
bottom: 35px;
|
bottom: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.boot-screen {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-title {
|
.terminal-title {
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
|||||||
@@ -13,14 +13,15 @@ inlineInput.spellcheck = false;
|
|||||||
// Initialize xterm.js terminal
|
// Initialize xterm.js terminal
|
||||||
const term = new Terminal({
|
const term = new Terminal({
|
||||||
cursorBlink: false,
|
cursorBlink: false,
|
||||||
cursorStyle: 'bar',
|
cursorStyle: 'underline',
|
||||||
|
cursorInactiveStyle: 'none',
|
||||||
fontFamily: '"Courier New", Courier, monospace',
|
fontFamily: '"Courier New", Courier, monospace',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
theme: {
|
theme: {
|
||||||
background: '#001800',
|
background: '#001800',
|
||||||
foreground: '#00ff00',
|
foreground: '#00ff00',
|
||||||
cursor: 'transparent',
|
cursor: 'transparent',
|
||||||
cursorAccent: '#001800',
|
cursorAccent: 'transparent',
|
||||||
selection: 'rgba(0, 255, 0, 0.3)',
|
selection: 'rgba(0, 255, 0, 0.3)',
|
||||||
black: '#000000',
|
black: '#000000',
|
||||||
red: '#ff0000',
|
red: '#ff0000',
|
||||||
@@ -55,6 +56,55 @@ term.loadAddon(webLinksAddon);
|
|||||||
term.open(document.getElementById('terminal'));
|
term.open(document.getElementById('terminal'));
|
||||||
fitAddon.fit();
|
fitAddon.fit();
|
||||||
|
|
||||||
|
// Register custom link provider for clickable commands
|
||||||
|
term.registerLinkProvider({
|
||||||
|
provideLinks: (bufferLineNumber, callback) => {
|
||||||
|
const line = term.buffer.active.getLine(bufferLineNumber - 1);
|
||||||
|
if (!line) {
|
||||||
|
callback(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineText = line.translateToString();
|
||||||
|
const links = [];
|
||||||
|
|
||||||
|
// Find all command names that match our known commands
|
||||||
|
const commandNames = ['help', 'about', 'clear', 'echo', 'date', 'whoami', 'history', 'color', 'banner', 'bluesky', 'chat', 'github', 'contact', 'privacy'];
|
||||||
|
|
||||||
|
commandNames.forEach(cmd => {
|
||||||
|
let startIndex = 0;
|
||||||
|
while (true) {
|
||||||
|
const index = lineText.indexOf(cmd, startIndex);
|
||||||
|
if (index === -1) break;
|
||||||
|
|
||||||
|
// Check if it's a standalone command word (surrounded by spaces, brackets, or at start/end)
|
||||||
|
const charBefore = index > 0 ? lineText[index - 1] : ' ';
|
||||||
|
const charAfter = index + cmd.length < lineText.length ? lineText[index + cmd.length] : ' ';
|
||||||
|
|
||||||
|
const validBefore = /[\s\[\]]/.test(charBefore);
|
||||||
|
const validAfter = /[\s\[\]\-]/.test(charAfter);
|
||||||
|
|
||||||
|
if (validBefore && validAfter) {
|
||||||
|
links.push({
|
||||||
|
range: {
|
||||||
|
start: { x: index + 1, y: bufferLineNumber },
|
||||||
|
end: { x: index + cmd.length + 1, y: bufferLineNumber }
|
||||||
|
},
|
||||||
|
text: cmd,
|
||||||
|
activate: () => {
|
||||||
|
runQuickCommand(cmd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex = index + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(links.length > 0 ? links : undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Adjust font size based on screen width
|
// Adjust font size based on screen width
|
||||||
function adjustFontSize() {
|
function adjustFontSize() {
|
||||||
const width = window.innerWidth;
|
const width = window.innerWidth;
|
||||||
@@ -362,31 +412,30 @@ const commands = {
|
|||||||
help: {
|
help: {
|
||||||
description: 'Display available commands',
|
description: 'Display available commands',
|
||||||
execute: () => {
|
execute: () => {
|
||||||
return [
|
term.writeln('');
|
||||||
'',
|
term.writeln('╔════════════════════════════════════════════════════════════╗');
|
||||||
'╔════════════════════════════════════════════════════════════╗',
|
term.writeln('║ AVAILABLE COMMANDS ║');
|
||||||
'║ AVAILABLE COMMANDS ║',
|
term.writeln('╚════════════════════════════════════════════════════════════╝');
|
||||||
'╚════════════════════════════════════════════════════════════╝',
|
term.writeln('');
|
||||||
'',
|
writeClickable(' [command=help] - Display this help message');
|
||||||
' help - Display this help message',
|
writeClickable(' [command=about] - Information about this terminal');
|
||||||
' about - Information about this terminal',
|
writeClickable(' [command=clear] - Clear the terminal screen');
|
||||||
' clear - Clear the terminal screen',
|
term.writeln(' echo - Echo back your message (usage: echo [message])');
|
||||||
' echo - Echo back your message (usage: echo [message])',
|
writeClickable(' [command=date] - Display current date and time');
|
||||||
' date - Display current date and time',
|
writeClickable(' [command=whoami] - Display current user information');
|
||||||
' whoami - Display current user information',
|
writeClickable(' [command=history] - Show command history');
|
||||||
' history - Show command history',
|
writeClickable(' [command=color] - Change terminal color scheme');
|
||||||
' color - Change terminal color scheme',
|
writeClickable(' [command=banner] - Display welcome banner');
|
||||||
' banner - Display welcome banner',
|
writeClickable(' [command=bluesky] - Fetch recent posts from Bluesky');
|
||||||
' bluesky - Fetch recent posts from Bluesky',
|
writeClickable(' [command=chat] - Enter interactive chat (type /quit to exit)');
|
||||||
' chat - Enter interactive chat (type /quit to exit)',
|
writeClickable(' [command=github] - Visit GitHub repository');
|
||||||
' github - Visit GitHub repository',
|
writeClickable(' [command=contact] - Display contact information');
|
||||||
' contact - Display contact information',
|
writeClickable(' [command=privacy] - Display privacy policy');
|
||||||
' privacy - Display privacy policy',
|
term.writeln('');
|
||||||
'',
|
term.writeln('Navigate: Use ↑/↓ arrows for command history');
|
||||||
'Navigate: Use ↑/↓ arrows for command history',
|
term.writeln('Mouse: Click commands to run them');
|
||||||
'Mouse: Click to position cursor, scroll to view history',
|
term.writeln('');
|
||||||
''
|
return null;
|
||||||
].join('\r\n');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
@@ -505,7 +554,22 @@ const commands = {
|
|||||||
banner: {
|
banner: {
|
||||||
description: 'Display welcome banner',
|
description: 'Display welcome banner',
|
||||||
execute: () => {
|
execute: () => {
|
||||||
return getWelcomeBanner();
|
const cols = term.cols;
|
||||||
|
if (cols >= 62) {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
github: {
|
github: {
|
||||||
@@ -842,25 +906,33 @@ const commands = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Welcome banner - full size (62 chars wide)
|
// Welcome banner - full size (62 chars wide) - NFO style
|
||||||
const welcomeBannerFull = [
|
const welcomeBannerFull = [
|
||||||
'',
|
'',
|
||||||
'╔════════════════════════════════════════════════════════════╗',
|
' ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄',
|
||||||
'║ ║',
|
' ▄█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█▄',
|
||||||
'║ ██╗ ██╗████████╗ ██████╗ ██╗ ██╗██╗ ██╗ ║',
|
' ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██',
|
||||||
'║ ██║ ██║╚══██╔══╝ ██╔══██╗██║ ██║██║ ██║ ║',
|
' ██░ ░██',
|
||||||
'║ ██║ ██║ ██║ ██████╔╝██║ ██║██║ ██║ ║',
|
' ██░ ██╗ ██╗████████╗ ██████╗ ██╗ ██╗ ░██',
|
||||||
'║ ██║ ██║ ██║ ██╗ ██╔══██╗██║ ██║╚██╗ ██╔╝ ║',
|
' ██░ ██║ ██║╚══██╔══╝ ██╔══██╗██║ ██║ ░██',
|
||||||
'║ ███████╗██║ ██║ ╚═╝ ██║ ██║╚██████╔╝ ╚████╔╝ ║',
|
' ██░ ██║ ██║ ██║ ██████╔╝██║ ██║ ░██',
|
||||||
'║ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═══╝ ║',
|
' ██░ ██║ ██║ ██║ ██╔══██╗██║ ██║ ░██',
|
||||||
'║ ██╗ ██╗████████╗███████╗ ║',
|
' ██░ ███████╗██║ ██║ ██╗██║ ██║╚██████╔╝ ░██',
|
||||||
'║ ██║ ██║╚══██╔══╝██╔════╝ ║',
|
' ██░ ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ░██',
|
||||||
'║ ██║ █╗ ██║ ██║ █████╗ ║',
|
' ██░ ───────────────────────────────────────────── ░██',
|
||||||
'║ ██║███╗██║ ██║ ██╔══╝ ║',
|
' ██░ ██╗ ██╗████████╗███████╗ ░██',
|
||||||
'║ ╚███╔███╔╝ ██║ ██║ ║',
|
' ██░ ██║ ██║╚══██╔══╝██╔════╝ ░██',
|
||||||
'║ ╚══╝╚══╝ ╚═╝ ╚═╝ ║',
|
' ██░ ██║ █╗ ██║ ██║ █████╗ ░██',
|
||||||
'║ ║',
|
' ██░ ██║███╗██║ ██║ ██╔══╝ ░██',
|
||||||
'╚════════════════════════════════════════════════════════════╝',
|
' ██░ ╚███╔███╔╝ ██║ ██║ ░██',
|
||||||
|
' ██░ ╚══╝╚══╝ ╚═╝ ╚═╝ ░██',
|
||||||
|
' ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██',
|
||||||
|
' ▀█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█▀',
|
||||||
|
' ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
|
||||||
|
'',
|
||||||
|
' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓',
|
||||||
|
' ░░ TERMINAL v' + VERSION + ' · EST 2024 · LITRUV ░░',
|
||||||
|
' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓',
|
||||||
'',
|
'',
|
||||||
' Type "help" for available commands.',
|
' Type "help" for available commands.',
|
||||||
' Use ↑/↓ arrows to navigate command history.',
|
' Use ↑/↓ arrows to navigate command history.',
|
||||||
@@ -870,9 +942,13 @@ const welcomeBannerFull = [
|
|||||||
// Welcome banner - compact (40 chars wide)
|
// Welcome banner - compact (40 chars wide)
|
||||||
const welcomeBannerCompact = [
|
const welcomeBannerCompact = [
|
||||||
'',
|
'',
|
||||||
'╔══════════════════════════════════════╗',
|
' ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄',
|
||||||
`║ LIT.RUV.WTF TERMINAL v${VERSION} ║`,
|
' ▄█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█▄',
|
||||||
'╚══════════════════════════════════════╝',
|
' ██░ LIT.RUV.WTF TERMINAL ░██',
|
||||||
|
' ██░ ═══════════════════════ ░██',
|
||||||
|
' ██░ v' + VERSION + ' · Est 2024 · LitRuv ░██',
|
||||||
|
' ▀█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█▀',
|
||||||
|
' ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
|
||||||
'',
|
'',
|
||||||
' Welcome! Type "help" for commands.',
|
' Welcome! Type "help" for commands.',
|
||||||
''
|
''
|
||||||
@@ -881,9 +957,10 @@ const welcomeBannerCompact = [
|
|||||||
// Welcome banner - minimal (25 chars wide)
|
// Welcome banner - minimal (25 chars wide)
|
||||||
const welcomeBannerMinimal = [
|
const welcomeBannerMinimal = [
|
||||||
'',
|
'',
|
||||||
' ═══════════════════════',
|
' ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄',
|
||||||
' LIT.RUV.WTF TERMINAL',
|
' █░ LIT.RUV.WTF ░█',
|
||||||
' ═══════════════════════',
|
' █░ TERMINAL v' + VERSION + ' ░█',
|
||||||
|
' ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀',
|
||||||
'',
|
'',
|
||||||
' Type "help"',
|
' Type "help"',
|
||||||
''
|
''
|
||||||
@@ -918,11 +995,11 @@ function positionInlineInput() {
|
|||||||
const charWidth = term._core._renderService.dimensions.css.cell.width;
|
const charWidth = term._core._renderService.dimensions.css.cell.width;
|
||||||
const charHeight = term._core._renderService.dimensions.css.cell.height;
|
const charHeight = term._core._renderService.dimensions.css.cell.height;
|
||||||
|
|
||||||
// Get cursor position (rows from bottom for scrollback)
|
// Get cursor position
|
||||||
const cursorY = term.buffer.active.cursorY;
|
const cursorY = term.buffer.active.cursorY;
|
||||||
const cursorX = term.buffer.active.cursorX;
|
const cursorX = term.buffer.active.cursorX;
|
||||||
|
|
||||||
// Position input
|
// Position input at cursor (xterm-screen handles positioning)
|
||||||
inlineInput.style.left = (cursorX * charWidth) + 'px';
|
inlineInput.style.left = (cursorX * charWidth) + 'px';
|
||||||
inlineInput.style.top = (cursorY * charHeight) + 'px';
|
inlineInput.style.top = (cursorY * charHeight) + 'px';
|
||||||
inlineInput.style.height = charHeight + 'px';
|
inlineInput.style.height = charHeight + 'px';
|
||||||
@@ -937,7 +1014,7 @@ function showInlineInput() {
|
|||||||
const terminalEl = document.getElementById('terminal');
|
const terminalEl = document.getElementById('terminal');
|
||||||
const xtermEl = terminalEl.querySelector('.xterm-screen');
|
const xtermEl = terminalEl.querySelector('.xterm-screen');
|
||||||
|
|
||||||
if (!xtermEl.contains(inlineInput)) {
|
if (xtermEl && !xtermEl.contains(inlineInput)) {
|
||||||
xtermEl.appendChild(inlineInput);
|
xtermEl.appendChild(inlineInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1048,7 +1125,10 @@ async function submitInlineInput() {
|
|||||||
// Show prompt if not in chat mode
|
// Show prompt if not in chat mode
|
||||||
if (!chatMode.active) {
|
if (!chatMode.active) {
|
||||||
term.write(promptColored);
|
term.write(promptColored);
|
||||||
showInlineInput();
|
// Delay to ensure terminal has rendered before positioning
|
||||||
|
setTimeout(() => {
|
||||||
|
positionInlineInput();
|
||||||
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
term.scrollToBottom();
|
term.scrollToBottom();
|
||||||
@@ -1056,7 +1136,23 @@ async function submitInlineInput() {
|
|||||||
|
|
||||||
// Initialize terminal
|
// Initialize terminal
|
||||||
function init() {
|
function init() {
|
||||||
term.writeln(getWelcomeBanner());
|
// Display welcome banner with clickable commands
|
||||||
|
const cols = term.cols;
|
||||||
|
if (cols >= 62) {
|
||||||
|
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('');
|
||||||
|
}
|
||||||
|
|
||||||
term.write(promptColored);
|
term.write(promptColored);
|
||||||
|
|
||||||
// Add inline input after a short delay to ensure terminal is rendered
|
// Add inline input after a short delay to ensure terminal is rendered
|
||||||
@@ -1109,6 +1205,70 @@ function init() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a quick command from a button click
|
||||||
|
* @param {string} command - The command to execute
|
||||||
|
*/
|
||||||
|
async function runQuickCommand(command) {
|
||||||
|
if (!command) return;
|
||||||
|
|
||||||
|
// Write command to terminal
|
||||||
|
term.writeln(`\x1b[1;32m${promptText}\x1b[0m ${command}`);
|
||||||
|
|
||||||
|
// Execute the command
|
||||||
|
await executeCommand(command.trim());
|
||||||
|
|
||||||
|
// Show prompt if not in chat mode
|
||||||
|
if (!chatMode.active) {
|
||||||
|
term.write(promptColored);
|
||||||
|
// Delay to ensure terminal has rendered before positioning
|
||||||
|
setTimeout(() => {
|
||||||
|
positionInlineInput();
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
term.scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make function available globally for onclick handlers
|
||||||
|
window.runQuickCommand = runQuickCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write text to terminal with clickable commands
|
||||||
|
* Parses [command=X] syntax to create clickable command links
|
||||||
|
* The link provider makes these clickable automatically
|
||||||
|
* @param {string} text - Text to write (can include [command=X] syntax)
|
||||||
|
* @param {boolean} newline - Whether to add newline after text
|
||||||
|
*/
|
||||||
|
function writeClickable(text, newline = true) {
|
||||||
|
// Parse for [command=X] syntax
|
||||||
|
const regex = /\[command=([^\]]+)\]/g;
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = regex.exec(text)) !== null) {
|
||||||
|
// Write text before the match
|
||||||
|
if (match.index > lastIndex) {
|
||||||
|
term.write(text.substring(lastIndex, match.index));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write command with underline styling (link provider makes it clickable)
|
||||||
|
const command = match[1];
|
||||||
|
term.write(`\x1b[4;32m${command}\x1b[0m`);
|
||||||
|
|
||||||
|
lastIndex = regex.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write remaining text
|
||||||
|
if (lastIndex < text.length) {
|
||||||
|
term.write(text.substring(lastIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newline) {
|
||||||
|
term.write('\r\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute command
|
// Execute command
|
||||||
async function executeCommand(input) {
|
async function executeCommand(input) {
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
@@ -1130,9 +1290,13 @@ async function executeCommand(input) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
term.writeln(`\r\n Command not found: ${cmd}`);
|
term.writeln(`\r\n Command not found: ${cmd}`);
|
||||||
term.writeln(' Type "help" for available commands.\r\n');
|
writeClickable(' Type [command=help] for available commands.\r\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize when loaded
|
// Initialize when loaded - wait for boot animation first
|
||||||
init();
|
if (window.runBootAnimation) {
|
||||||
|
runBootAnimation().then(init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user