mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-11-03 17:22:20 +01:00
console.html and logs.html are always-dark terminal pages that deliberately do not load theme.css, so the var(--text-secondary) the notice asked for resolved to nothing there and the text fell back to Bootstrap's near-black body colour — roughly 1:1 against the navy background, i.e. invisible. Fixed twice over. Every colour in the demo CSS now carries a fallback, so a page without theme.css degrades to a readable light notice instead of to nothing — that closes the whole class of bug rather than this one instance. And console.html restyles the notice in its own palette, alongside the other hardcoded colours it already owns. Measured contrast: console 12.22:1, light theme 7.76:1, dark theme 5.71:1 (WCAG AA for small text is 4.5:1). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
388 lines
11 KiB
HTML
388 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="{{ lang }}">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<title>{{ t('console.title') }} - mc-webui</title>
|
|
|
|
{% include "_head_i18n.html" %}
|
|
|
|
<!-- 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;
|
|
}
|
|
|
|
.device-name {
|
|
color: #4ecdc4;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.messages-wrap {
|
|
flex: 1;
|
|
min-height: 0;
|
|
position: relative;
|
|
display: flex;
|
|
}
|
|
|
|
.console-messages {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 1rem;
|
|
background-color: #1a1a2e;
|
|
min-height: 0;
|
|
}
|
|
|
|
.scroll-bottom-btn {
|
|
position: absolute;
|
|
right: 1rem;
|
|
bottom: 1rem;
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
border: 1px solid #0f3460;
|
|
background-color: #16213e;
|
|
color: #4ecdc4;
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0;
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
|
|
cursor: pointer;
|
|
z-index: 5;
|
|
}
|
|
|
|
.scroll-bottom-btn.show {
|
|
display: flex;
|
|
}
|
|
|
|
.scroll-bottom-btn:hover {
|
|
background-color: #1a1a4e;
|
|
color: #00ff88;
|
|
}
|
|
|
|
.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-message.historic {
|
|
opacity: 0.55;
|
|
}
|
|
|
|
.history-divider {
|
|
border: none;
|
|
border-top: 1px dashed #0f3460;
|
|
margin: 0.5rem 0 1rem 0;
|
|
}
|
|
|
|
.console-input-area {
|
|
background-color: #16213e;
|
|
border-top: 1px solid #0f3460;
|
|
padding: 0.75rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Demo-mode notice, in this page's palette rather than the app theme's —
|
|
style.css styles it for a light page and this page is always dark. */
|
|
.demo-notice {
|
|
background: #1a1a4e;
|
|
border-color: #0f3460;
|
|
border-left-color: #ffc107;
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
.demo-notice code {
|
|
color: #4ecdc4;
|
|
background: transparent;
|
|
}
|
|
|
|
.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="device-name">{{ 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">{{ t('common.connecting') }}</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Messages Area -->
|
|
<div class="messages-wrap">
|
|
<div class="console-messages" id="consoleMessages">
|
|
{# 'help' is the literal command name — never translated. #}
|
|
<div class="console-message system">{{ t('console.help_hint') }}</div>
|
|
</div>
|
|
<button type="button" class="scroll-bottom-btn" id="scrollBottomBtn"
|
|
title="{{ t('console.scroll_bottom_title') }}" aria-label="{{ t('console.scroll_bottom_aria') }}">
|
|
<i class="bi bi-arrow-down"></i>
|
|
</button>
|
|
</div>
|
|
|
|
{% if demo_mode and not demo_unlocked %}
|
|
<div class="demo-notice mx-3 mb-0 mt-2" id="demoConsoleNotice">
|
|
<i class="bi bi-shield-lock"></i>
|
|
<span>{{ t_html('demo.console_notice') }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- 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="{{ t('console.history_title') }}" disabled>
|
|
<i class="bi bi-clock-history"></i>
|
|
</button>
|
|
<div class="history-menu" id="historyMenu">
|
|
<div class="history-empty">{{ t('console.history_empty') }}</div>
|
|
</div>
|
|
</div>
|
|
<!-- Clear output transcript -->
|
|
<button type="button" class="btn history-btn" id="clearOutputBtn" title="{{ t('console.clear_title') }}">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
<input type="text"
|
|
id="commandInput"
|
|
class="form-control console-input"
|
|
placeholder="{{ t('console.input_ph') }}"
|
|
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>
|