mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-08-07 17:22:57 +02:00
feat: Redesign UI for mobile with offcanvas menu and reduced message limit
- Change message byte limit from 200 to 140 bytes - Remove keyboard hint text to save space - Simplify navbar to show only notification bell and channel selector - Add slide-out offcanvas menu for less-used options: * Refresh Messages * Manage Channels * Message History (archive selector) * Settings - Increase font sizes in dropdowns and menu for better readability - Add icons and text labels to all menu items - Auto-close menu after selecting options - Update clipboard API to modern navigator.clipboard with fallback 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -81,4 +81,5 @@ Thumbs.db
|
||||
# ============================================
|
||||
# Miscellaneous
|
||||
# ============================================
|
||||
.claude/
|
||||
.claude/
|
||||
technotes/prompt-dms.md
|
||||
|
||||
+40
-12
@@ -151,6 +151,41 @@ main {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
/* Channel selector and date selector - larger font size */
|
||||
#channelSelector,
|
||||
#dateSelector {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Offcanvas menu styling */
|
||||
.offcanvas {
|
||||
width: 280px !important;
|
||||
}
|
||||
|
||||
.offcanvas-body .list-group-item {
|
||||
padding: 1rem 0.75rem;
|
||||
border: none;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.offcanvas-body .list-group-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.offcanvas-body .list-group-item i {
|
||||
color: #0d6efd;
|
||||
min-width: 1.5rem;
|
||||
}
|
||||
|
||||
.offcanvas-body .list-group-item span {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.offcanvas-body .list-group-item .form-select {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.message {
|
||||
@@ -176,22 +211,15 @@ main {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Navbar: Hide "Refresh" text on mobile, show only icon */
|
||||
#refreshBtn .btn-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Navbar: Reduce gap between elements */
|
||||
.navbar .d-flex {
|
||||
gap: 0.25rem !important;
|
||||
}
|
||||
|
||||
/* Navbar: Smaller selectors on mobile */
|
||||
#channelSelector,
|
||||
#dateSelector {
|
||||
min-width: 80px !important;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
/* Navbar: Channel selector on mobile */
|
||||
#channelSelector {
|
||||
min-width: 100px !important;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Navbar: Smaller buttons */
|
||||
@@ -216,7 +244,7 @@ main {
|
||||
}
|
||||
|
||||
/* Modal: Compact channel list */
|
||||
.list-group-item {
|
||||
.modal .list-group-item {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
+28
-5
@@ -74,12 +74,24 @@ function setupEventListeners() {
|
||||
document.getElementById('refreshBtn').addEventListener('click', async function() {
|
||||
await loadMessages();
|
||||
await checkForUpdates();
|
||||
|
||||
// Close offcanvas menu after refresh
|
||||
const offcanvas = bootstrap.Offcanvas.getInstance(document.getElementById('mainMenu'));
|
||||
if (offcanvas) {
|
||||
offcanvas.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Date selector (archive selection)
|
||||
document.getElementById('dateSelector').addEventListener('change', function(e) {
|
||||
currentArchiveDate = e.target.value || null;
|
||||
loadMessages();
|
||||
|
||||
// Close offcanvas menu after selecting date
|
||||
const offcanvas = bootstrap.Offcanvas.getInstance(document.getElementById('mainMenu'));
|
||||
if (offcanvas) {
|
||||
offcanvas.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup contacts button
|
||||
@@ -536,7 +548,7 @@ function updateCharCounter() {
|
||||
// Count UTF-8 bytes, not Unicode characters
|
||||
const encoder = new TextEncoder();
|
||||
const byteLength = encoder.encode(input.value).length;
|
||||
const maxBytes = 200;
|
||||
const maxBytes = 140;
|
||||
|
||||
counter.textContent = `${byteLength} / ${maxBytes}`;
|
||||
|
||||
@@ -1045,9 +1057,20 @@ async function shareChannel(index) {
|
||||
/**
|
||||
* Copy channel key to clipboard
|
||||
*/
|
||||
function copyChannelKey() {
|
||||
async function copyChannelKey() {
|
||||
const input = document.getElementById('shareChannelKey');
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
showNotification('Channel key copied to clipboard!', 'success');
|
||||
try {
|
||||
// Use modern Clipboard API
|
||||
await navigator.clipboard.writeText(input.value);
|
||||
showNotification('Channel key copied to clipboard!', 'success');
|
||||
} catch (error) {
|
||||
// Fallback for older browsers
|
||||
input.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
showNotification('Channel key copied to clipboard!', 'success');
|
||||
} catch (fallbackError) {
|
||||
showNotification('Failed to copy to clipboard', 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-14
@@ -29,34 +29,58 @@
|
||||
<span class="navbar-brand mb-0 h1">
|
||||
<i class="bi bi-broadcast"></i> mc-webui
|
||||
{% if device_name %}
|
||||
<small class="text-white-50">- {{ device_name }}</small>
|
||||
<small class="text-white-50 d-none d-sm-inline">- {{ device_name }}</small>
|
||||
{% endif %}
|
||||
</span>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button class="btn btn-outline-light btn-sm" id="refreshBtn" title="Refresh messages">
|
||||
<i class="bi bi-arrow-clockwise"></i> <span class="btn-text">Refresh</span>
|
||||
</button>
|
||||
<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: 120px;" title="Select channel">
|
||||
<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="modal" data-bs-target="#channelsModal" title="Manage Channels">
|
||||
<i class="bi bi-broadcast-pin"></i>
|
||||
</button>
|
||||
<select id="dateSelector" class="form-select form-select-sm" style="width: auto; min-width: 150px;" title="Select date">
|
||||
<option value="">Today (Live)</option>
|
||||
<!-- Archive dates loaded dynamically via JavaScript -->
|
||||
</select>
|
||||
<button class="btn btn-outline-light btn-sm" data-bs-toggle="modal" data-bs-target="#settingsModal" title="Settings">
|
||||
<i class="bi bi-gear"></i>
|
||||
<button class="btn btn-outline-light btn-sm" data-bs-toggle="offcanvas" data-bs-target="#mainMenu" title="Menu">
|
||||
<i class="bi bi-list" style="font-size: 1.25rem;"></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>
|
||||
<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 %}
|
||||
|
||||
@@ -86,9 +86,8 @@
|
||||
<!-- Emoji picker popup (hidden by default) -->
|
||||
<div id="emojiPickerPopup" class="emoji-picker-popup hidden"></div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<small class="text-muted">Shift+Enter: new line, Enter: send</small>
|
||||
<small id="charCounter" class="text-muted">0 / 200</small>
|
||||
<div class="d-flex justify-content-end">
|
||||
<small id="charCounter" class="text-muted">0 / 140</small>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user