Files
mc-webui/app/templates/base.html
T
MarekWo 66f16609e5 fix: Remove non-functional Node Discovery feature
Removed Node Discovery feature that was experiencing persistent timeout
issues. Feature attempted to scan mesh network for nearby repeaters but
consistently failed due to bridge timing constraints.

Changes:
- Remove node_discover() function from cli.py
- Remove 'node_discover' from SPECIAL_COMMANDS in api.py
- Remove Discover Nodes button and modal from base.html
- Remove discoverNodes() and displayNodeDiscoveryResults() from app.js
- Remove Discover Nodes documentation from README.md

IMPORTANT: Advert message cleanup ("Advert sent") is preserved and
working correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-03 10:46:05 +01:00

283 lines
14 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>{% block title %}mc-webui{% endblock %}</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 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
{% block extra_head %}{% endblock %}
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-dark bg-primary">
<div class="container-fluid">
<span class="navbar-brand mb-0 h1">
<i class="bi bi-broadcast"></i> mc-webui
{% if device_name %}
<small class="text-white-50 d-none d-sm-inline">- {{ device_name }}</small>
{% endif %}
</span>
<div class="d-flex align-items-center gap-2">
<div id="notificationBell" class="btn btn-outline-light btn-sm position-relative" style="cursor: default;" title="Unread messages">
<i class="bi bi-bell"></i>
</div>
<select id="channelSelector" class="form-select form-select-sm" style="width: auto; min-width: 100px;" title="Select channel">
<option value="0">Public</option>
<!-- Channels loaded dynamically via JavaScript -->
</select>
<button class="btn btn-outline-light btn-sm" data-bs-toggle="offcanvas" data-bs-target="#mainMenu" title="Menu">
<i class="bi bi-list"></i>
</button>
</div>
</div>
</nav>
<!-- Offcanvas Menu -->
<div class="offcanvas offcanvas-end" tabindex="-1" id="mainMenu">
<div class="offcanvas-header">
<h5 class="offcanvas-title"><i class="bi bi-menu-button-wide"></i> Menu</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<div class="list-group list-group-flush">
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" id="refreshBtn">
<i class="bi bi-arrow-clockwise" style="font-size: 1.5rem;"></i>
<span>Refresh Messages</span>
</button>
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" data-bs-toggle="modal" data-bs-target="#channelsModal" data-bs-dismiss="offcanvas">
<i class="bi bi-broadcast-pin" style="font-size: 1.5rem;"></i>
<span>Manage Channels</span>
</button>
<div class="list-group-item">
<div class="d-flex align-items-center gap-3 mb-2">
<i class="bi bi-calendar3" style="font-size: 1.5rem;"></i>
<label for="dateSelector" class="form-label mb-0">Message History</label>
</div>
<select id="dateSelector" class="form-select" title="Select date">
<option value="">Today (Live)</option>
<!-- Archive dates loaded dynamically via JavaScript -->
</select>
</div>
<!-- Network Commands Section -->
<div class="list-group-item py-2 mt-2">
<small class="text-muted fw-bold text-uppercase">Network Commands</small>
</div>
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" id="advertBtn" title="Send single advertisement (recommended for normal operation)">
<i class="bi bi-megaphone" style="font-size: 1.5rem;"></i>
<div>
<span>Send Advert</span>
<small class="d-block text-muted">Announce presence (normal)</small>
</div>
</button>
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3 text-warning" id="floodadvBtn" title="Flood advertisement - use sparingly! High airtime usage.">
<i class="bi bi-broadcast" style="font-size: 1.5rem;"></i>
<div>
<span>Flood Advert</span>
<small class="d-block text-muted">Network recovery only!</small>
</div>
</button>
<div class="list-group-item py-2 mt-2">
<small class="text-muted fw-bold text-uppercase">Configuration</small>
</div>
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" data-bs-toggle="modal" data-bs-target="#settingsModal" data-bs-dismiss="offcanvas">
<i class="bi bi-gear" style="font-size: 1.5rem;"></i>
<span>Settings</span>
</button>
</div>
</div>
</div>
<!-- Main Content -->
<main>
{% block content %}{% endblock %}
</main>
<!-- Channels Management Modal -->
<div class="modal fade" id="channelsModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-broadcast-pin"></i> Manage Channels</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Channel List -->
<h6>Your Channels</h6>
<div id="channelsList" class="list-group mb-3">
<div class="text-center text-muted py-3">
<div class="spinner-border spinner-border-sm"></div> Loading...
</div>
</div>
<!-- Actions -->
<div class="btn-group w-100 mb-3" role="group">
<button type="button" class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#addChannelForm">
<i class="bi bi-plus-circle"></i> Add New Channel
</button>
<button type="button" class="btn btn-success" data-bs-toggle="collapse" data-bs-target="#joinChannelForm">
<i class="bi bi-box-arrow-in-right"></i> Join Existing
</button>
</div>
<!-- Add Channel Form (collapsed) -->
<div class="collapse" id="addChannelForm">
<div class="card card-body mb-3">
<h6>Create New Channel</h6>
<form id="createChannelForm">
<div class="mb-2">
<label for="newChannelName" class="form-label">Channel Name</label>
<input type="text" class="form-control" id="newChannelName"
placeholder="e.g., Malopolska"
pattern="[a-zA-Z0-9_\-]+"
required>
<small class="text-muted">Only letters, numbers, _ and -</small>
</div>
<button type="submit" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Create & Auto-generate Key
</button>
</form>
</div>
</div>
<!-- Join Channel Form (collapsed) -->
<div class="collapse" id="joinChannelForm">
<div class="card card-body mb-3">
<h6>Join Existing Channel</h6>
<form id="joinChannelFormSubmit">
<div class="mb-2">
<label for="joinChannelName" class="form-label">Channel Name</label>
<input type="text" class="form-control" id="joinChannelName" required>
</div>
<div class="mb-2">
<label for="joinChannelKey" class="form-label">Channel Key (32 hex chars) <small class="text-muted">- optional for channels starting with #</small></label>
<input type="text" class="form-control" id="joinChannelKey"
placeholder="485af7e164459d280d8818d9c99fb30d (leave empty for # channels)"
pattern="[a-fA-F0-9]{32}"
maxlength="32">
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-success">
<i class="bi bi-box-arrow-in-right"></i> Join Channel
</button>
<button type="button" class="btn btn-secondary" id="scanQRBtn">
<i class="bi bi-qr-code-scan"></i> Scan QR Code
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Share Channel Modal -->
<div class="modal fade" id="shareChannelModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-share"></i> Share Channel</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<h6 id="shareChannelName"></h6>
<!-- QR Code -->
<div class="text-center mb-3">
<img id="shareChannelQR" src="" alt="QR Code" class="img-fluid" style="max-width: 300px;">
</div>
<!-- Channel Details -->
<div class="mb-3">
<label class="form-label fw-bold">Channel Key:</label>
<div class="input-group">
<input type="text" class="form-control font-monospace" id="shareChannelKey" readonly>
<button class="btn btn-outline-secondary" type="button" onclick="copyChannelKey()">
<i class="bi bi-clipboard"></i> Copy
</button>
</div>
</div>
<div class="alert alert-info small">
<i class="bi bi-info-circle"></i>
Share this QR code or key with others to let them join this channel.
</div>
</div>
</div>
</div>
</div>
<!-- Settings Modal -->
<div class="modal fade" id="settingsModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-gear"></i> Settings</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<h6>Device Information</h6>
<div id="deviceInfo" class="small text-muted">
Loading...
</div>
</div>
</div>
</div>
</div>
<!-- Toast container for notifications -->
<div class="toast-container position-fixed top-0 start-0 p-3">
<div id="notificationToast" class="toast" role="alert">
<div class="toast-header">
<strong class="me-auto">mc-webui</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast"></button>
</div>
<div class="toast-body"></div>
</div>
</div>
<!-- Bootstrap 5 JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Message Content Processing Utilities (must load before app.js and dm.js) -->
<script src="{{ url_for('static', filename='js/message-utils.js') }}"></script>
<!-- Custom JS -->
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
<!-- PWA Viewport Fix for Android -->
<script>
// Fix viewport corruption on Android PWA after page reload
window.addEventListener('load', function() {
// Force viewport recalculation without modifying body height
setTimeout(() => {
window.scrollTo(0, 0);
// Force reflow using a less invasive method
void(document.documentElement.offsetHeight);
// Trigger resize event to recalculate layout
window.dispatchEvent(new Event('resize'));
}, 150);
});
</script>
{% block extra_scripts %}{% endblock %}
</body>
</html>