mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-03-28 17:42:45 +01:00
- Add server-side API for console history (GET/POST/DELETE) - Add history dropdown button with clock icon - Save commands to server after execution - Load history from server on page load - History persists between sessions and works across devices - Max 50 commands stored, duplicates moved to end - Dropdown shows most recent commands first Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
307 lines
8.9 KiB
HTML
307 lines
8.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<title>Console - mc-webui</title>
|
|
|
|
<!-- Favicon -->
|
|
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='images/apple-touch-icon.png') }}">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='images/favicon-32x32.png') }}">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', filename='images/favicon-16x16.png') }}">
|
|
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
|
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
|
|
|
<!-- Bootstrap 5 CSS (local) -->
|
|
<link href="{{ url_for('static', filename='vendor/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">
|
|
<!-- Bootstrap Icons (local) -->
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='vendor/bootstrap-icons/bootstrap-icons.css') }}">
|
|
<!-- Custom CSS -->
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
|
|
|
<style>
|
|
html, body {
|
|
height: 100%;
|
|
margin: 0;
|
|
background-color: #1a1a2e;
|
|
}
|
|
|
|
.console-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100vh;
|
|
}
|
|
|
|
.console-header {
|
|
background-color: #16213e;
|
|
border-bottom: 1px solid #0f3460;
|
|
padding: 0.75rem 1rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.console-messages {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 1rem;
|
|
background-color: #1a1a2e;
|
|
min-height: 0;
|
|
}
|
|
|
|
.console-message {
|
|
margin-bottom: 1rem;
|
|
font-family: 'Courier New', Consolas, monospace;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.console-message.command {
|
|
color: #00ff88;
|
|
}
|
|
|
|
.console-message.command::before {
|
|
content: '> ';
|
|
color: #888;
|
|
}
|
|
|
|
.console-message.response {
|
|
color: #e0e0e0;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
background-color: #16213e;
|
|
padding: 0.5rem;
|
|
border-radius: 0.25rem;
|
|
border-left: 3px solid #0f3460;
|
|
}
|
|
|
|
.console-message.error {
|
|
color: #ff6b6b;
|
|
}
|
|
|
|
.console-message.system {
|
|
color: #4ecdc4;
|
|
font-style: italic;
|
|
}
|
|
|
|
.console-input-area {
|
|
background-color: #16213e;
|
|
border-top: 1px solid #0f3460;
|
|
padding: 0.75rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.console-input {
|
|
background-color: #0f3460;
|
|
border: 1px solid #1a1a2e;
|
|
color: #00ff88;
|
|
font-family: 'Courier New', Consolas, monospace;
|
|
}
|
|
|
|
.console-input:focus {
|
|
background-color: #0f3460;
|
|
border-color: #00ff88;
|
|
color: #00ff88;
|
|
box-shadow: 0 0 0 0.2rem rgba(0, 255, 136, 0.25);
|
|
}
|
|
|
|
.console-input::placeholder {
|
|
color: #666;
|
|
}
|
|
|
|
.console-input:disabled {
|
|
background-color: #0a1628;
|
|
color: #444;
|
|
}
|
|
|
|
.status-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
}
|
|
|
|
.status-dot.connected {
|
|
background-color: #00ff88;
|
|
}
|
|
|
|
.status-dot.disconnected {
|
|
background-color: #ff6b6b;
|
|
}
|
|
|
|
.status-dot.connecting {
|
|
background-color: #ffd93d;
|
|
animation: pulse 1s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
|
|
/* Loading spinner for pending commands */
|
|
.console-message.pending::after {
|
|
content: '';
|
|
display: inline-block;
|
|
width: 12px;
|
|
height: 12px;
|
|
border: 2px solid #4ecdc4;
|
|
border-top-color: transparent;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin-left: 0.5rem;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* History dropdown */
|
|
.history-dropdown {
|
|
position: relative;
|
|
}
|
|
|
|
.history-btn {
|
|
background-color: #0f3460;
|
|
border: 1px solid #1a1a2e;
|
|
color: #4ecdc4;
|
|
}
|
|
|
|
.history-btn:hover, .history-btn:focus {
|
|
background-color: #1a1a4e;
|
|
border-color: #4ecdc4;
|
|
color: #4ecdc4;
|
|
}
|
|
|
|
.history-btn:disabled {
|
|
background-color: #0a1628;
|
|
color: #444;
|
|
}
|
|
|
|
.history-menu {
|
|
position: absolute;
|
|
bottom: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
min-width: 250px;
|
|
max-width: 100%;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
background-color: #16213e;
|
|
border: 1px solid #0f3460;
|
|
border-radius: 0.375rem;
|
|
margin-bottom: 0.25rem;
|
|
display: none;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.history-menu.show {
|
|
display: block;
|
|
}
|
|
|
|
.history-item {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 0.5rem 0.75rem;
|
|
color: #e0e0e0;
|
|
text-decoration: none;
|
|
font-family: 'Courier New', Consolas, monospace;
|
|
font-size: 0.85rem;
|
|
border: none;
|
|
background: none;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.history-item:hover {
|
|
background-color: #0f3460;
|
|
color: #00ff88;
|
|
}
|
|
|
|
.history-empty {
|
|
padding: 0.75rem;
|
|
color: #666;
|
|
text-align: center;
|
|
font-style: italic;
|
|
}
|
|
|
|
/* Mobile adjustments */
|
|
@media (max-width: 576px) {
|
|
.console-header {
|
|
padding: 0.5rem 0.75rem;
|
|
}
|
|
|
|
.console-header h6 {
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.console-messages {
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
.console-message {
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.console-input-area {
|
|
padding: 0.5rem;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="console-container">
|
|
<!-- Header with status indicator -->
|
|
<div class="console-header d-flex justify-content-between align-items-center">
|
|
<small class="text-muted">{{ device_name }}</small>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="status-dot" id="statusDot"></span>
|
|
<small class="text-muted" id="statusText">Connecting...</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Messages Area -->
|
|
<div class="console-messages" id="consoleMessages">
|
|
<div class="console-message system">
|
|
Type a meshcli command and press Enter.
|
|
Examples: infos, contacts, help
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Input Area -->
|
|
<div class="console-input-area">
|
|
<form id="consoleForm" class="d-flex gap-2">
|
|
<!-- History dropdown -->
|
|
<div class="history-dropdown">
|
|
<button type="button" class="btn history-btn" id="historyBtn" title="Command history" disabled>
|
|
<i class="bi bi-clock-history"></i>
|
|
</button>
|
|
<div class="history-menu" id="historyMenu">
|
|
<div class="history-empty">No commands in history</div>
|
|
</div>
|
|
</div>
|
|
<input type="text"
|
|
id="commandInput"
|
|
class="form-control console-input"
|
|
placeholder="Enter command..."
|
|
autocomplete="off"
|
|
autocapitalize="off"
|
|
spellcheck="false"
|
|
disabled>
|
|
<button type="submit" class="btn btn-success" id="sendBtn" disabled>
|
|
<i class="bi bi-send"></i>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Socket.IO client -->
|
|
<script src="{{ url_for('static', filename='vendor/socket.io/socket.io.min.js') }}"></script>
|
|
<!-- Bootstrap JS Bundle (local) -->
|
|
<script src="{{ url_for('static', filename='vendor/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
|
|
<!-- Console JS -->
|
|
<script src="{{ url_for('static', filename='js/console.js') }}"></script>
|
|
</body>
|
|
</html>
|