methunraj
feat: initialize project structure with core components
cfeb3a6
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manus AI Terminal</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
background: #0d1117;
color: #c9d1d9;
height: 100vh;
overflow: hidden;
}
.terminal-container {
display: flex;
flex-direction: column;
height: 100vh;
background: linear-gradient(135deg, #0d1117 0%, #161b22 100%);
border: 1px solid #30363d;
}
.terminal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: #161b22;
border-bottom: 1px solid #30363d;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.terminal-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: #f0f6fc;
}
.terminal-icon {
width: 16px;
height: 16px;
background: #238636;
border-radius: 50%;
position: relative;
}
.terminal-icon::after {
content: '>';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 10px;
color: white;
font-weight: bold;
}
.terminal-controls {
display: flex;
gap: 8px;
}
.control-btn {
width: 12px;
height: 12px;
border-radius: 50%;
border: none;
cursor: pointer;
transition: opacity 0.2s;
}
.control-btn:hover {
opacity: 0.8;
}
.close { background: #ff5f56; }
.minimize { background: #ffbd2e; }
.maximize { background: #27ca3f; }
.terminal-body {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.terminal-output {
flex: 1;
padding: 16px;
overflow-y: auto;
font-size: 13px;
line-height: 1.4;
background: #0d1117;
scrollbar-width: thin;
scrollbar-color: #30363d #0d1117;
}
.terminal-output::-webkit-scrollbar {
width: 8px;
}
.terminal-output::-webkit-scrollbar-track {
background: #0d1117;
}
.terminal-output::-webkit-scrollbar-thumb {
background: #30363d;
border-radius: 4px;
}
.terminal-output::-webkit-scrollbar-thumb:hover {
background: #484f58;
}
.terminal-line {
margin-bottom: 2px;
white-space: pre-wrap;
word-wrap: break-word;
}
.command-line {
color: #58a6ff;
font-weight: 600;
}
.output-line {
color: #c9d1d9;
}
.error-line {
color: #f85149;
}
.success-line {
color: #56d364;
}
.system-line {
color: #ffa657;
font-style: italic;
}
.timestamp {
color: #7d8590;
font-size: 11px;
margin-right: 8px;
}
.terminal-input {
display: flex;
align-items: center;
padding: 12px 16px;
background: #161b22;
border-top: 1px solid #30363d;
}
.prompt {
color: #58a6ff;
margin-right: 8px;
font-weight: 600;
}
.input-field {
flex: 1;
background: transparent;
border: none;
color: #c9d1d9;
font-family: inherit;
font-size: 13px;
outline: none;
}
.input-field::placeholder {
color: #7d8590;
}
.status-indicator {
display: flex;
align-items: center;
gap: 8px;
margin-left: 12px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #7d8590;
transition: background-color 0.3s;
}
.status-dot.connected {
background: #56d364;
box-shadow: 0 0 8px rgba(86, 211, 100, 0.5);
}
.status-dot.running {
background: #ffa657;
animation: pulse 1.5s infinite;
}
.status-dot.error {
background: #f85149;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.typing-indicator {
display: none;
color: #7d8590;
font-style: italic;
animation: blink 1s infinite;
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
.command-history {
position: absolute;
bottom: 60px;
left: 16px;
right: 16px;
background: #21262d;
border: 1px solid #30363d;
border-radius: 6px;
max-height: 200px;
overflow-y: auto;
display: none;
z-index: 1000;
}
.history-item {
padding: 8px 12px;
cursor: pointer;
border-bottom: 1px solid #30363d;
transition: background-color 0.2s;
}
.history-item:hover {
background: #30363d;
}
.history-item:last-child {
border-bottom: none;
}
/* Responsive design */
@media (max-width: 768px) {
.terminal-header {
padding: 8px 12px;
}
.terminal-output {
padding: 12px;
font-size: 12px;
}
.terminal-input {
padding: 8px 12px;
}
}
</style>
</head>
<body>
<div class="terminal-container">
<div class="terminal-header">
<div class="terminal-title">
<div class="terminal-icon"></div>
<span>Manus AI Terminal</span>
</div>
<div class="terminal-controls">
<button class="control-btn close" onclick="closeTerminal()"></button>
<button class="control-btn minimize" onclick="minimizeTerminal()"></button>
<button class="control-btn maximize" onclick="maximizeTerminal()"></button>
</div>
</div>
<div class="terminal-body">
<div class="terminal-output" id="output"></div>
<div class="command-history" id="history"></div>
<div class="terminal-input">
<span class="prompt">$</span>
<input type="text" class="input-field" id="commandInput"
placeholder="Type a command and press Enter..."
autocomplete="off" spellcheck="false">
<div class="status-indicator">
<div class="status-dot" id="statusDot"></div>
<span id="statusText">Disconnected</span>
</div>
</div>
</div>
</div>
<script>
class ManusTerminal {
constructor() {
this.ws = null;
this.output = document.getElementById('output');
this.input = document.getElementById('commandInput');
this.statusDot = document.getElementById('statusDot');
this.statusText = document.getElementById('statusText');
this.history = document.getElementById('history');
this.commandHistory = [];
this.historyIndex = -1;
this.isConnected = false;
this.isRunning = false;
this.init();
}
init() {
this.setupEventListeners();
this.connect();
this.addWelcomeMessage();
}
setupEventListeners() {
this.input.addEventListener('keydown', (e) => this.handleKeyDown(e));
this.input.addEventListener('keyup', (e) => this.handleKeyUp(e));
// Auto-reconnect on window focus
window.addEventListener('focus', () => {
if (!this.isConnected) {
this.connect();
}
});
}
connect() {
try {
this.ws = new WebSocket('ws://localhost:8765');
this.ws.onopen = () => {
this.isConnected = true;
this.updateStatus('connected', 'Connected');
this.addSystemMessage('πŸš€ Connected to terminal server');
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.ws.onclose = () => {
this.isConnected = false;
this.isRunning = false;
this.updateStatus('error', 'Disconnected');
this.addSystemMessage('❌ Connection lost. Attempting to reconnect...');
// Auto-reconnect after 3 seconds
setTimeout(() => this.connect(), 3000);
};
this.ws.onerror = (error) => {
this.addSystemMessage('⚠️ Connection error. Check if the server is running.');
};
} catch (error) {
this.addSystemMessage('❌ Failed to connect to terminal server');
}
}
handleMessage(data) {
const timestamp = new Date(data.timestamp).toLocaleTimeString();
switch (data.type) {
case 'connected':
this.addSystemMessage(data.message);
break;
case 'command_start':
this.isRunning = true;
this.updateStatus('running', 'Running');
this.addCommandLine(data.message);
break;
case 'output':
this.addOutputLine(data.data, data.stream);
break;
case 'command_complete':
this.isRunning = false;
this.updateStatus('connected', 'Connected');
this.addSystemMessage(`Process completed with exit code ${data.exit_code}`);
break;
case 'error':
this.addErrorLine(data.data);
break;
case 'interrupted':
this.isRunning = false;
this.updateStatus('connected', 'Connected');
this.addSystemMessage(data.message);
break;
}
}
handleKeyDown(e) {
switch (e.key) {
case 'Enter':
e.preventDefault();
this.executeCommand();
break;
case 'ArrowUp':
e.preventDefault();
this.navigateHistory(-1);
break;
case 'ArrowDown':
e.preventDefault();
this.navigateHistory(1);
break;
case 'Tab':
e.preventDefault();
// TODO: Implement command completion
break;
case 'c':
if (e.ctrlKey) {
e.preventDefault();
this.interruptCommand();
}
break;
}
}
handleKeyUp(e) {
// Show typing indicator
if (e.target.value.length > 0) {
// TODO: Implement typing indicator
}
}
executeCommand() {
const command = this.input.value.trim();
if (!command || !this.isConnected) return;
// Add to history
if (this.commandHistory[this.commandHistory.length - 1] !== command) {
this.commandHistory.push(command);
}
this.historyIndex = this.commandHistory.length;
// Send command
this.ws.send(JSON.stringify({
type: 'command',
command: command
}));
// Clear input
this.input.value = '';
}
interruptCommand() {
if (this.isRunning && this.isConnected) {
this.ws.send(JSON.stringify({
type: 'interrupt'
}));
}
}
navigateHistory(direction) {
if (this.commandHistory.length === 0) return;
this.historyIndex += direction;
if (this.historyIndex < 0) {
this.historyIndex = 0;
} else if (this.historyIndex >= this.commandHistory.length) {
this.historyIndex = this.commandHistory.length;
this.input.value = '';
return;
}
this.input.value = this.commandHistory[this.historyIndex] || '';
}
updateStatus(status, text) {
this.statusDot.className = `status-dot ${status}`;
this.statusText.textContent = text;
}
addWelcomeMessage() {
this.addSystemMessage('🎯 Manus AI Terminal - Ready for commands');
this.addSystemMessage('πŸ’‘ Use Ctrl+C to interrupt running commands');
this.addSystemMessage('πŸ“š Use ↑/↓ arrows to navigate command history');
}
addCommandLine(text) {
this.addLine(text, 'command-line');
}
addOutputLine(text, stream = 'stdout') {
const className = stream === 'stderr' ? 'error-line' : 'output-line';
this.addLine(text, className);
}
addErrorLine(text) {
this.addLine(text, 'error-line');
}
addSystemMessage(text) {
this.addLine(text, 'system-line');
}
addLine(text, className = 'output-line') {
const line = document.createElement('div');
line.className = `terminal-line ${className}`;
const timestamp = document.createElement('span');
timestamp.className = 'timestamp';
timestamp.textContent = new Date().toLocaleTimeString();
const content = document.createElement('span');
content.textContent = text;
line.appendChild(timestamp);
line.appendChild(content);
this.output.appendChild(line);
this.scrollToBottom();
}
scrollToBottom() {
this.output.scrollTop = this.output.scrollHeight;
}
clear() {
this.output.innerHTML = '';
this.addWelcomeMessage();
}
}
// Terminal control functions
function closeTerminal() {
if (confirm('Are you sure you want to close the terminal?')) {
window.close();
}
}
function minimizeTerminal() {
// Implement minimize functionality
console.log('Minimize terminal');
}
function maximizeTerminal() {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
document.documentElement.requestFullscreen();
}
}
// Initialize terminal when page loads
document.addEventListener('DOMContentLoaded', () => {
window.terminal = new ManusTerminal();
});
// Add global commands
window.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'l') {
e.preventDefault();
window.terminal.clear();
}
});
</script>
</body>
</html>