mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-11-03 17:22:20 +01:00
Groundwork for translating the interface. No strings are extracted yet — this stage only adds the mechanism, so the UI is byte-identical in English. Translations are one flat JSON file per language. An admin can add a language by dropping <lang>.json into $MC_CONFIG_DIR/translations/ and picking it in Settings > Appearance — no rebuild, no restart, no compile step. A drop-in file overrides a built-in one of the same name. Built-ins: en (source) and pl. How it works: - app/i18n.py loads and merges catalogs over en.json, so per-key English fallback is baked in server-side and the JS runtime needs no fallback logic. Catalogs are fingerprinted with stat() per render, so a dropped-in file is live on the next refresh rather than the next restart — which matters, because restarting drops the device connection for up to 60s. - Template text renders server-side via t()/t_html()/tn() from inject_globals(), so the first paint is already correct. That context processor covers every render_template() in the app, including the six standalone iframe pages, so routes/views.py needed no changes. - JS text comes from /i18n/<lang>.<hash8>.js, a blocking immutable script shared by all 8 entry points. The hash is in the path, not a query string, so intermediary caches and the service worker bust reliably. - Language = per-browser mc_lang cookie over a server-wide DB default. The cookie is what makes iframes work: they are same-origin, so they send it automatically and the existing modal-open reload wiring needs no changes. t() deliberately does not escape — Jinja autoescape handles that, and escaping here would double-escape every French apostrophe. t_html()/tHtml() escape their params but trust catalog markup. Also fixed along the way: - save_ui_settings() replaced the whole settings blob instead of merging, so a language-only POST would have wiped the toast settings. It worked before only because the one form always submitted every key. - `const t = document.getElementById(...)` in populateUiSettingsForm shadowed the global translation helper; same for `var t` in six theme IIFEs. scripts/i18n_check.py now fails the build if that pattern comes back. - Removed app/templates/contacts.html, dead since the contacts pages were split (no render_template reference anywhere), and two stale CSS comments. Verified locally: catalog route headers/security, drop-in without restart, malformed catalogs logged and skipped rather than offered in the picker, cookie propagation into iframes, and 360px layout. Python and JS agree on interpolation, escaping and Polish plural categories. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2281 lines
48 KiB
CSS
2281 lines
48 KiB
CSS
/* mc-webui Custom Styles */
|
|
/* Colors are defined as CSS custom properties in theme.css */
|
|
|
|
/* Page Layout */
|
|
html, body {
|
|
height: 100vh; /* Fallback for older browsers */
|
|
height: 100dvh; /* Dynamic viewport height (mobile-friendly) */
|
|
margin: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: var(--bg-body);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
main {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0; /* Important for flex children */
|
|
}
|
|
|
|
/* =============================================================================
|
|
Channel Sidebar (Group Chat) - visible on lg+ screens
|
|
============================================================================= */
|
|
|
|
.channel-sidebar {
|
|
display: none;
|
|
width: 250px;
|
|
flex-shrink: 0;
|
|
border-right: 1px solid var(--border-color);
|
|
background: var(--bg-surface);
|
|
flex-direction: column;
|
|
}
|
|
|
|
.channel-sidebar-header {
|
|
padding: 0.6rem 0.75rem;
|
|
font-weight: 600;
|
|
font-size: 0.85rem;
|
|
border-bottom: 1px solid var(--border-color);
|
|
color: var(--text-secondary);
|
|
background: var(--bg-surface-alt);
|
|
}
|
|
|
|
.channel-sidebar-list {
|
|
overflow-y: auto;
|
|
flex: 1;
|
|
}
|
|
|
|
.channel-sidebar-item {
|
|
padding: 0.45rem 0.75rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 0;
|
|
border-bottom: 1px solid var(--border-light);
|
|
font-size: 0.85rem;
|
|
transition: background-color 0.15s;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Top row: name + time + unread badge */
|
|
.channel-item-top {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
min-width: 0;
|
|
}
|
|
|
|
.channel-last-time {
|
|
font-size: 0.72rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.channel-item-preview {
|
|
font-size: 0.72rem;
|
|
color: var(--text-muted);
|
|
overflow: hidden;
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 2;
|
|
line-height: 1.3;
|
|
margin-top: 0.1rem;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.channel-selector-item .channel-item-preview {
|
|
-webkit-line-clamp: 1;
|
|
}
|
|
|
|
.channel-sidebar-item:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
|
|
.channel-sidebar-item.active {
|
|
background-color: var(--bg-active);
|
|
border-left: 3px solid var(--sender-color);
|
|
padding-left: calc(0.75rem - 3px);
|
|
font-weight: 500;
|
|
color: var(--sender-color);
|
|
}
|
|
|
|
.channel-sidebar-item.muted {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.channel-sidebar-item .channel-name {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Same badge in the sidebar (wide) and the navbar dropdown (narrow) */
|
|
.channel-sidebar-item .sidebar-unread-badge,
|
|
.channel-selector-item .sidebar-unread-badge {
|
|
background-color: var(--sender-color);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 1px 6px;
|
|
font-size: 0.7rem;
|
|
font-weight: bold;
|
|
min-width: 18px;
|
|
text-align: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Show sidebar and hide dropdown on wide screens (toggled by JS via .layout-wide on <html>) */
|
|
.layout-wide .channel-sidebar {
|
|
display: flex;
|
|
}
|
|
.layout-wide #channelSelectorWrapper {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Channel Selector Dropdown (base.html navbar, narrow screens) */
|
|
.channel-selector-dropdown {
|
|
position: absolute;
|
|
top: 100%;
|
|
right: 0;
|
|
min-width: 200px;
|
|
z-index: 1050;
|
|
max-height: 60vh;
|
|
overflow-y: auto;
|
|
background: var(--dropdown-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 0.375rem;
|
|
box-shadow: var(--popup-shadow);
|
|
}
|
|
|
|
.channel-selector-item {
|
|
padding: 0.45rem 0.75rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 0;
|
|
border-bottom: 1px solid var(--border-light);
|
|
font-size: 0.88rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.channel-selector-item:hover,
|
|
.channel-selector-item.active {
|
|
background-color: var(--dropdown-item-hover);
|
|
}
|
|
|
|
.channel-selector-item.muted {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.channel-selector-item .channel-name {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* =============================================================================
|
|
DM Sidebar (Direct Messages) - visible on lg+ screens
|
|
============================================================================= */
|
|
|
|
.dm-sidebar {
|
|
display: none;
|
|
width: 280px;
|
|
flex-shrink: 0;
|
|
border-right: 1px solid var(--border-color);
|
|
background: var(--bg-surface);
|
|
flex-direction: column;
|
|
}
|
|
|
|
.dm-sidebar-header {
|
|
padding: 0.5rem;
|
|
border-bottom: 1px solid var(--border-color);
|
|
background: var(--bg-surface-alt);
|
|
}
|
|
|
|
.dm-sidebar-list {
|
|
overflow-y: auto;
|
|
flex: 1;
|
|
}
|
|
|
|
.dm-sidebar-separator {
|
|
padding: 0.25rem 0.75rem;
|
|
font-size: 0.7rem;
|
|
color: var(--text-muted);
|
|
background: var(--bg-surface-alt);
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.03em;
|
|
}
|
|
|
|
.dm-sidebar-item {
|
|
padding: 0.5rem 0.75rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
border-bottom: 1px solid var(--border-light);
|
|
font-size: 0.85rem;
|
|
transition: background-color 0.15s;
|
|
}
|
|
|
|
.dm-sidebar-item:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
|
|
.dm-sidebar-item.active {
|
|
background-color: #e7f5ee;
|
|
border-left: 3px solid #198754;
|
|
padding-left: calc(0.75rem - 3px);
|
|
font-weight: 500;
|
|
}
|
|
|
|
[data-theme="dark"] .dm-sidebar-item.active {
|
|
background-color: rgba(16, 185, 129, 0.15);
|
|
border-left-color: #10b981;
|
|
}
|
|
|
|
.dm-sidebar-item .contact-name {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-size: 0.88rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.dm-sidebar-item .sidebar-unread-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
background-color: var(--sender-color);
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.dm-sidebar-item .badge {
|
|
font-size: 0.65rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Show DM sidebar and hide mobile selector on wide screens (toggled by JS via .layout-wide on <html>) */
|
|
.layout-wide .dm-sidebar {
|
|
display: flex;
|
|
}
|
|
.layout-wide .dm-mobile-selector {
|
|
display: none !important;
|
|
}
|
|
.layout-wide .dm-desktop-header {
|
|
display: block !important;
|
|
}
|
|
|
|
.dm-desktop-header {
|
|
display: none;
|
|
}
|
|
|
|
/* Messages Container */
|
|
.messages-container {
|
|
background-color: var(--bg-messages);
|
|
padding-left: 0.25rem !important;
|
|
padding-right: 0.25rem !important;
|
|
}
|
|
|
|
#messagesList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Message Wrapper - New Layout with Avatar
|
|
============================================================================= */
|
|
|
|
/* Message wrapper - contains avatar + message container */
|
|
.message-wrapper {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.5rem;
|
|
max-width: 85%;
|
|
animation: fadeIn 0.3s ease-in;
|
|
}
|
|
|
|
.message-wrapper.own {
|
|
align-self: flex-end;
|
|
flex-direction: row-reverse;
|
|
}
|
|
|
|
.message-wrapper.other {
|
|
align-self: flex-start;
|
|
}
|
|
|
|
/* Avatar circle */
|
|
.message-avatar {
|
|
flex-shrink: 0;
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
color: white;
|
|
text-transform: uppercase;
|
|
margin-top: 0.1rem;
|
|
}
|
|
|
|
.message-avatar.emoji {
|
|
font-size: 1.25rem;
|
|
background-color: transparent !important;
|
|
border: 1.5px solid;
|
|
line-height: 1;
|
|
padding-bottom: 1px;
|
|
}
|
|
|
|
/* Message container - holds sender row + bubble + actions */
|
|
.message-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0; /* Allow text truncation */
|
|
}
|
|
|
|
/* Sender row - name and time above bubble */
|
|
.message-sender-row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 0.5rem;
|
|
margin-bottom: 0.25rem;
|
|
padding-left: 0.25rem;
|
|
}
|
|
|
|
.message-sender {
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
color: var(--sender-color);
|
|
}
|
|
|
|
.message-sender-row .message-time {
|
|
font-size: 0.7rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Message footer for own messages (name + time above bubble) */
|
|
.message-footer {
|
|
display: flex;
|
|
align-items: baseline;
|
|
padding-right: 0.5rem;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.message-footer.own {
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.message-footer .message-time {
|
|
font-size: 0.7rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.message-footer .message-sender.own {
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
color: var(--sender-own-color);
|
|
margin-right: 0.5rem;
|
|
}
|
|
|
|
/* Message action buttons container */
|
|
.message-actions {
|
|
display: flex;
|
|
gap: 0.25rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
/* Message Bubbles */
|
|
.message {
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 1rem;
|
|
border: 1px solid var(--msg-border);
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
/* Own Messages (right-aligned) */
|
|
.message.own {
|
|
background-color: var(--msg-own-bg);
|
|
border-color: var(--msg-own-border);
|
|
}
|
|
|
|
/* Other Messages (left-aligned) */
|
|
.message.other {
|
|
background-color: var(--msg-other-bg);
|
|
}
|
|
|
|
.message-time {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Message Content */
|
|
.message-content {
|
|
margin: 0;
|
|
white-space: pre-wrap;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Message Metadata */
|
|
.message-meta {
|
|
font-size: 0.7rem;
|
|
color: var(--text-meta);
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
/* Reply Button */
|
|
.btn-reply {
|
|
font-size: 0.7rem;
|
|
padding: 0.1rem 0.4rem;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
/* Send Form */
|
|
#messageInput,
|
|
#dmMessageInput {
|
|
resize: none;
|
|
border-radius: 0.5rem 0 0 0.5rem;
|
|
}
|
|
|
|
#sendBtn,
|
|
#dmSendBtn {
|
|
border-radius: 0 0.5rem 0.5rem 0;
|
|
}
|
|
|
|
/* Status Indicators */
|
|
.status-connected {
|
|
color: #198754 !important;
|
|
}
|
|
|
|
.status-disconnected {
|
|
color: #dc3545 !important;
|
|
}
|
|
|
|
.status-connecting {
|
|
color: #ffc107 !important;
|
|
}
|
|
|
|
/* Scrollbar Styling */
|
|
.messages-container::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
.messages-container::-webkit-scrollbar-track {
|
|
background: var(--scrollbar-track);
|
|
}
|
|
|
|
.messages-container::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-thumb);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.messages-container::-webkit-scrollbar-thumb:hover {
|
|
background: var(--scrollbar-thumb-hover);
|
|
}
|
|
|
|
/* Date selector - larger font size */
|
|
#dateSelector {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
/* Navbar touch targets - larger for mobile */
|
|
.navbar-touch-btn {
|
|
min-width: 40px;
|
|
min-height: 40px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.1rem;
|
|
padding: 0.35rem 0.6rem;
|
|
}
|
|
|
|
.navbar-touch-select {
|
|
min-height: 40px;
|
|
font-size: 1rem;
|
|
padding: 0.35rem 1.5rem 0.35rem 0.5rem;
|
|
}
|
|
|
|
/* Offcanvas menu styling */
|
|
.offcanvas {
|
|
width: 300px !important;
|
|
}
|
|
|
|
.offcanvas-body .list-group-item {
|
|
padding: 1rem 0.75rem;
|
|
border: none;
|
|
border-bottom: 1px solid var(--offcanvas-item-border);
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.offcanvas-body .list-group-item:hover {
|
|
background-color: var(--offcanvas-item-hover);
|
|
}
|
|
|
|
.offcanvas-body .list-group-item i {
|
|
color: var(--offcanvas-icon-color);
|
|
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-wrapper {
|
|
max-width: 90%;
|
|
}
|
|
|
|
.message-avatar {
|
|
width: 32px;
|
|
height: 32px;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.message-avatar.emoji {
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.message-sender {
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.dm-message-wrapper {
|
|
max-width: 85%;
|
|
}
|
|
|
|
#messageInput,
|
|
#dmMessageInput {
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* Reduce padding on mobile to save vertical space */
|
|
#sendMessageForm,
|
|
#dmSendForm {
|
|
padding: 0.5rem !important;
|
|
}
|
|
|
|
/* Make status bar more compact */
|
|
.row.border-top .p-2 {
|
|
padding: 0.3rem !important;
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
/* Navbar: Reduce gap between elements */
|
|
.navbar .d-flex {
|
|
gap: 0.25rem !important;
|
|
}
|
|
|
|
/* Navbar: Force one-row layout (Bootstrap default is flex-wrap: wrap) */
|
|
.navbar,
|
|
.navbar > .container-fluid {
|
|
flex-wrap: nowrap !important;
|
|
}
|
|
|
|
/* Navbar: Shrink brand title and let it truncate if needed */
|
|
.navbar-brand.h1 {
|
|
font-size: 1rem;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
margin-right: 0.5rem;
|
|
}
|
|
|
|
/* Navbar: Channel selector on mobile */
|
|
#channelSelectorInput {
|
|
min-width: 0 !important;
|
|
width: 100%;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* DM: Contact search input on mobile */
|
|
#dmContactSearchInput {
|
|
min-width: 100px !important;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* Navbar: Smaller buttons */
|
|
.navbar .btn-sm {
|
|
padding: 0.25rem 0.4rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
/* Modal: Better mobile layout */
|
|
.modal-dialog {
|
|
margin: 0.5rem;
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
/* Modal: Compact channel list */
|
|
.modal .list-group-item {
|
|
padding: 0.5rem;
|
|
}
|
|
}
|
|
|
|
/* Small phone screens: icon-only buttons in contact cards */
|
|
@media (max-width: 428px) {
|
|
.pending-contact-card .btn-label,
|
|
.existing-contact-card .btn-label {
|
|
display: none;
|
|
}
|
|
|
|
.pending-contact-card .d-flex.gap-2,
|
|
.existing-contact-card .d-flex.gap-2 {
|
|
flex-wrap: wrap;
|
|
}
|
|
}
|
|
|
|
/* Loading State */
|
|
.loading-spinner {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
|
|
/* Spin animation for update check button */
|
|
.spin {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Empty State */
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 3rem 1rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.empty-state i {
|
|
font-size: 3rem;
|
|
margin-bottom: 1rem;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* Notification Bell Badge */
|
|
.notification-badge {
|
|
position: absolute;
|
|
top: -8px;
|
|
right: -8px;
|
|
background-color: #dc3545;
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 2px 6px;
|
|
font-size: 0.65rem;
|
|
font-weight: bold;
|
|
line-height: 1;
|
|
min-width: 18px;
|
|
text-align: center;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
z-index: 10;
|
|
}
|
|
|
|
/* Bell Ring Animation */
|
|
@keyframes bell-ring {
|
|
0% { transform: rotate(0deg); }
|
|
10% { transform: rotate(14deg); }
|
|
20% { transform: rotate(-8deg); }
|
|
30% { transform: rotate(14deg); }
|
|
40% { transform: rotate(-4deg); }
|
|
50% { transform: rotate(10deg); }
|
|
60% { transform: rotate(0deg); }
|
|
100% { transform: rotate(0deg); }
|
|
}
|
|
|
|
.bell-ring {
|
|
animation: bell-ring 1s ease-in-out;
|
|
transform-origin: 50% 4px;
|
|
}
|
|
|
|
/* Notification Bell Container */
|
|
#notificationBell {
|
|
position: relative;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
#notificationBell:hover .notification-badge {
|
|
transform: scale(1.1);
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Direct Messages (DM) Styles
|
|
============================================================================= */
|
|
|
|
/* DM Badge on notification bell (secondary badge, bottom-right, green) */
|
|
.notification-badge-dm {
|
|
position: absolute;
|
|
bottom: -6px;
|
|
right: -6px;
|
|
background-color: #198754;
|
|
color: white;
|
|
border-radius: 8px;
|
|
padding: 1px 4px;
|
|
font-size: 0.6rem;
|
|
font-weight: bold;
|
|
min-width: 14px;
|
|
text-align: center;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
z-index: 10;
|
|
}
|
|
|
|
/* DM Messages Container */
|
|
.dm-messages-container {
|
|
height: 50vh;
|
|
overflow-y: auto;
|
|
padding: 0.5rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
background-color: var(--bg-dm-messages);
|
|
}
|
|
|
|
/* DM Messages List (flex container for message alignment) */
|
|
#dmMessagesList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.dm-messages-container {
|
|
height: calc(100vh - 200px);
|
|
}
|
|
}
|
|
|
|
/* DM Message Wrapper - timestamp + bubble */
|
|
.dm-message-wrapper {
|
|
max-width: 70%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
animation: fadeIn 0.3s ease-in;
|
|
}
|
|
|
|
.dm-message-wrapper.own {
|
|
align-self: flex-end;
|
|
}
|
|
|
|
.dm-message-wrapper.other {
|
|
align-self: flex-start;
|
|
}
|
|
|
|
/* DM Time Row - above bubble */
|
|
.dm-time-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
font-size: 0.7rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 0.2rem;
|
|
padding: 0 0.25rem;
|
|
}
|
|
|
|
.dm-message-wrapper.own .dm-time-row {
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
/* DM Message Bubbles */
|
|
.dm-message {
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 1rem;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
.dm-message.own {
|
|
background-color: var(--msg-own-bg);
|
|
border: 1px solid var(--msg-own-border);
|
|
}
|
|
|
|
.dm-message.other {
|
|
background-color: var(--msg-other-bg);
|
|
border: 1px solid var(--msg-border);
|
|
}
|
|
|
|
.dm-content {
|
|
margin: 0;
|
|
white-space: pre-wrap;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* DM Message Metadata */
|
|
.dm-meta {
|
|
font-size: 0.75rem;
|
|
color: var(--dm-meta-color);
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
/* DM Status Indicators */
|
|
.dm-status {
|
|
font-size: 0.7rem;
|
|
margin-left: 0.25rem;
|
|
}
|
|
|
|
.dm-status.pending {
|
|
color: #ffc107;
|
|
}
|
|
|
|
.dm-status.delivered {
|
|
color: #198754;
|
|
}
|
|
|
|
.dm-status.timeout {
|
|
color: #dc3545;
|
|
}
|
|
|
|
.dm-status.unknown {
|
|
color: var(--text-meta);
|
|
}
|
|
|
|
.dm-status-unknown {
|
|
cursor: pointer;
|
|
position: relative;
|
|
}
|
|
|
|
.dm-retry-info:empty {
|
|
display: none;
|
|
}
|
|
|
|
.dm-delivery-meta {
|
|
font-size: 0.75rem;
|
|
color: var(--dm-meta-color);
|
|
margin-top: 0.1rem;
|
|
}
|
|
|
|
.dm-route-link {
|
|
cursor: pointer;
|
|
text-decoration: underline;
|
|
text-decoration-style: dotted;
|
|
position: relative;
|
|
}
|
|
|
|
.dm-delivery-popup {
|
|
position: absolute;
|
|
bottom: 100%;
|
|
right: 0;
|
|
background-color: #333;
|
|
color: #fff;
|
|
padding: 0.35rem 0.6rem;
|
|
border-radius: 0.375rem;
|
|
font-size: 0.7rem;
|
|
white-space: nowrap;
|
|
z-index: 1000;
|
|
pointer-events: none;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
/* DM Action Buttons */
|
|
.dm-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.dm-action-btn {
|
|
padding: 0.15rem 0.4rem;
|
|
font-size: 0.75rem;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* DM Conversation List Item */
|
|
.dm-conversation-item {
|
|
padding: 0.75rem;
|
|
border-bottom: 1px solid var(--conversation-border);
|
|
cursor: pointer;
|
|
transition: background-color 0.15s ease;
|
|
}
|
|
|
|
.dm-conversation-item:hover {
|
|
background-color: var(--conversation-hover);
|
|
}
|
|
|
|
.dm-conversation-item.unread {
|
|
background-color: var(--conversation-unread);
|
|
}
|
|
|
|
.dm-conversation-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
/* DM Preview Text */
|
|
.dm-preview {
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 100%;
|
|
}
|
|
|
|
/* DM Button on channel messages - uses same class as Reply (.btn-reply) */
|
|
|
|
/* DM Empty State */
|
|
.dm-empty-state {
|
|
text-align: center;
|
|
padding: 2rem 1rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.dm-empty-state i {
|
|
font-size: 2.5rem;
|
|
margin-bottom: 0.75rem;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
/* DM Scrollbar */
|
|
.dm-messages-container::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.dm-messages-container::-webkit-scrollbar-track {
|
|
background: var(--scrollbar-track);
|
|
}
|
|
|
|
.dm-messages-container::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-thumb-light);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.dm-messages-container::-webkit-scrollbar-thumb:hover {
|
|
background: var(--scrollbar-thumb-light-hover);
|
|
}
|
|
|
|
/* =============================================================================
|
|
Message Content Enhancements (Mentions, Links, Images)
|
|
============================================================================= */
|
|
|
|
/* Mention Badge (similar to Android Meshcore app) */
|
|
.mention-badge {
|
|
display: inline-block;
|
|
background-color: var(--mention-bg);
|
|
color: white;
|
|
padding: 0.15rem 0.5rem;
|
|
border-radius: 0.75rem;
|
|
font-size: 0.85em;
|
|
font-weight: 500;
|
|
margin: 0 0.1rem;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.message.own .mention-badge {
|
|
background-color: var(--mention-own-bg);
|
|
}
|
|
|
|
/* Channel Links (#channelname) */
|
|
.channel-link {
|
|
display: inline-block;
|
|
background-color: var(--channel-link-bg);
|
|
color: white;
|
|
padding: 0.15rem 0.5rem;
|
|
border-radius: 0.75rem;
|
|
font-size: 0.85em;
|
|
font-weight: 500;
|
|
margin: 0 0.1rem;
|
|
white-space: nowrap;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
transition: background-color 0.15s ease;
|
|
}
|
|
|
|
.channel-link:hover {
|
|
background-color: var(--channel-link-hover);
|
|
color: white;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.message.own .channel-link {
|
|
background-color: var(--channel-link-own-bg);
|
|
}
|
|
|
|
.message.own .channel-link:hover {
|
|
background-color: var(--channel-link-own-hover);
|
|
}
|
|
|
|
.channel-link.loading {
|
|
opacity: 0.7;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.channel-link.loading::after {
|
|
content: '...';
|
|
}
|
|
|
|
/* Quoted Text */
|
|
.quote-text {
|
|
font-style: italic;
|
|
color: var(--quote-color);
|
|
background-color: var(--quote-bg);
|
|
padding: 0.1rem 0.3rem;
|
|
border-radius: 0.25rem;
|
|
border-left: 2px solid var(--quote-border);
|
|
}
|
|
|
|
.message.own .quote-text {
|
|
color: var(--quote-own-color);
|
|
background-color: var(--quote-own-bg);
|
|
border-left-color: var(--quote-own-border);
|
|
}
|
|
|
|
/* Large Emoji for emoji-only messages */
|
|
.emoji-large {
|
|
font-size: 2.5rem;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
/* Clickable Links in Messages */
|
|
.message-link {
|
|
color: var(--link-color);
|
|
text-decoration: underline;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.message-link:hover {
|
|
color: var(--link-hover);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.message.own .message-link {
|
|
color: var(--link-own-color);
|
|
}
|
|
|
|
.message.own .message-link:hover {
|
|
color: var(--link-own-hover);
|
|
}
|
|
|
|
/* Image Thumbnails in Messages */
|
|
.message-image-container {
|
|
margin-top: 0.5rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.message-image-thumbnail {
|
|
max-width: 300px;
|
|
max-height: 200px;
|
|
border-radius: 0.5rem;
|
|
cursor: pointer;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
border: 1px solid var(--image-border);
|
|
}
|
|
|
|
.message-image-thumbnail:hover {
|
|
transform: scale(1.02);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.message-image-url {
|
|
font-size: 0.75rem;
|
|
word-break: break-all;
|
|
}
|
|
|
|
/* Responsive image sizes on mobile */
|
|
@media (max-width: 576px) {
|
|
.message-image-thumbnail {
|
|
max-width: 200px;
|
|
max-height: 150px;
|
|
}
|
|
}
|
|
|
|
/* Image Preview Modal */
|
|
#imagePreviewModal .modal-content {
|
|
background-color: rgba(0, 0, 0, 0.95);
|
|
}
|
|
|
|
#imagePreviewModal .modal-body {
|
|
padding: 1rem;
|
|
}
|
|
|
|
#imagePreviewImg {
|
|
border-radius: 0.5rem;
|
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
/* =============================================================================
|
|
Floating Action Buttons (FAB)
|
|
============================================================================= */
|
|
|
|
.fab-container {
|
|
position: fixed;
|
|
right: 16px;
|
|
top: 80px;
|
|
z-index: 900; /* Lower than offcanvas (1045) but higher than content */
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--fab-custom-gap, 12px);
|
|
touch-action: none; /* Prevent scroll during drag */
|
|
}
|
|
|
|
.fab {
|
|
position: relative; /* For badge positioning */
|
|
width: var(--fab-custom-size, 56px);
|
|
height: var(--fab-custom-size, 56px);
|
|
border-radius: 50%;
|
|
border: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: calc(var(--fab-custom-size, 56px) * 0.43);
|
|
cursor: pointer;
|
|
box-shadow: var(--fab-shadow);
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
|
|
.fab:hover {
|
|
transform: scale(1.1);
|
|
box-shadow: var(--fab-shadow-hover);
|
|
}
|
|
|
|
.fab:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.fab-dm {
|
|
background: linear-gradient(135deg, #198754 0%, #157347 100%);
|
|
color: white;
|
|
}
|
|
|
|
.fab-contacts {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0a58ca 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* FAB Badge (base style for all badges on FAB buttons) */
|
|
.fab-badge {
|
|
position: absolute;
|
|
top: -4px;
|
|
right: -4px;
|
|
min-width: 20px;
|
|
height: 20px;
|
|
border-radius: 10px;
|
|
padding: 2px 6px;
|
|
font-size: 0.65rem;
|
|
font-weight: bold;
|
|
line-height: 1.4;
|
|
text-align: center;
|
|
color: white;
|
|
display: none;
|
|
z-index: 1;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
/* FAB Badge for Pending Contacts (red for consistency and contrast) */
|
|
.fab-badge-pending {
|
|
background-color: #dc3545;
|
|
}
|
|
|
|
/* FAB Badge for Direct Messages (red for consistency and contrast) */
|
|
.fab-badge-dm {
|
|
background-color: #dc3545;
|
|
}
|
|
|
|
/* Animation on hover */
|
|
.fab:hover .fab-badge {
|
|
transform: scale(1.1);
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
/* FAB toggle button (smaller, semi-transparent) — not affected by custom size */
|
|
.fab-toggle {
|
|
width: 32px !important;
|
|
height: 32px !important;
|
|
background: rgba(108, 117, 125, 0.6);
|
|
color: white;
|
|
font-size: 0.85rem !important;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
cursor: grab;
|
|
}
|
|
|
|
.fab-toggle:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.fab-toggle:hover {
|
|
background: rgba(108, 117, 125, 0.9);
|
|
}
|
|
|
|
/* Collapsed state - hide all FABs except toggle */
|
|
.fab-container.collapsed .fab:not(.fab-toggle) {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transform: scale(0);
|
|
height: 0;
|
|
width: 0;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.fab-container.collapsed {
|
|
gap: 0;
|
|
}
|
|
|
|
/* Smooth transitions for collapse */
|
|
.fab-container .fab:not(.fab-toggle) {
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease, height 0.2s ease, width 0.2s ease;
|
|
}
|
|
|
|
.fab-toggle i {
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.fab-container.collapsed .fab-toggle i {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
/* Mobile optimization */
|
|
@media (max-width: 768px) {
|
|
.fab-container {
|
|
right: 12px;
|
|
top: 70px;
|
|
gap: var(--fab-custom-gap, 10px);
|
|
}
|
|
|
|
.fab {
|
|
width: var(--fab-custom-size, 48px);
|
|
height: var(--fab-custom-size, 48px);
|
|
font-size: calc(var(--fab-custom-size, 48px) * 0.43);
|
|
}
|
|
|
|
.fab-toggle {
|
|
width: 28px !important;
|
|
height: 28px !important;
|
|
font-size: 0.75rem !important;
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
PWA Safe Area Handling
|
|
============================================================================= */
|
|
|
|
/* Ensure bottom UI is visible above system bars on mobile devices */
|
|
@supports (padding-bottom: env(safe-area-inset-bottom)) {
|
|
body {
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
PWA Notifications
|
|
============================================================================= */
|
|
|
|
/* Notification toggle disabled state */
|
|
#notificationsToggle:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Leaflet Map Modal
|
|
============================================================================= */
|
|
|
|
#mapModal .modal-body {
|
|
min-height: 400px;
|
|
}
|
|
|
|
#leafletMap {
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Fix Leaflet controls z-index in Bootstrap modal */
|
|
.leaflet-top,
|
|
.leaflet-bottom {
|
|
z-index: 1000;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Message Action Buttons
|
|
============================================================================= */
|
|
|
|
/* Icon-only action buttons on message bubbles */
|
|
.btn-msg-action {
|
|
width: 28px;
|
|
height: 28px;
|
|
padding: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Map Filter Badges
|
|
============================================================================= */
|
|
|
|
/* Clickable map filter badges */
|
|
.map-filter-badge {
|
|
display: inline-block;
|
|
padding: 0.25rem 0.6rem;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
border-radius: 0.375rem;
|
|
cursor: pointer;
|
|
transition: all 0.15s ease-in-out;
|
|
user-select: none;
|
|
}
|
|
|
|
.map-filter-badge[data-type="1"] {
|
|
color: #2196F3;
|
|
background-color: var(--map-badge-inactive-bg);
|
|
border: 2px solid #2196F3;
|
|
}
|
|
.map-filter-badge[data-type="1"].active {
|
|
color: white;
|
|
background-color: #2196F3;
|
|
}
|
|
|
|
.map-filter-badge[data-type="2"] {
|
|
color: #4CAF50;
|
|
background-color: var(--map-badge-inactive-bg);
|
|
border: 2px solid #4CAF50;
|
|
}
|
|
.map-filter-badge[data-type="2"].active {
|
|
color: white;
|
|
background-color: #4CAF50;
|
|
}
|
|
|
|
.map-filter-badge[data-type="3"] {
|
|
color: #9C27B0;
|
|
background-color: var(--map-badge-inactive-bg);
|
|
border: 2px solid #9C27B0;
|
|
}
|
|
.map-filter-badge[data-type="3"].active {
|
|
color: white;
|
|
background-color: #9C27B0;
|
|
}
|
|
|
|
.map-filter-badge[data-type="4"] {
|
|
color: #FF9800;
|
|
background-color: var(--map-badge-inactive-bg);
|
|
border: 2px solid #FF9800;
|
|
}
|
|
.map-filter-badge[data-type="4"].active {
|
|
color: #212529;
|
|
background-color: #FF9800;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Mentions Autocomplete Popup
|
|
============================================================================= */
|
|
|
|
.mentions-popup {
|
|
position: absolute;
|
|
bottom: 100%;
|
|
left: 0;
|
|
z-index: 1001;
|
|
margin-bottom: 0.5rem;
|
|
max-height: 200px;
|
|
width: 280px;
|
|
overflow-y: auto;
|
|
background-color: var(--popup-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 0.5rem;
|
|
box-shadow: var(--popup-shadow);
|
|
}
|
|
|
|
.mentions-popup.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.mentions-list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.mention-item {
|
|
padding: 0.5rem 0.75rem;
|
|
cursor: pointer;
|
|
border-bottom: 1px solid var(--mention-item-border);
|
|
font-size: 0.9rem;
|
|
transition: background-color 0.1s ease;
|
|
}
|
|
|
|
.mention-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.mention-item:hover,
|
|
.mention-item.highlighted {
|
|
background-color: var(--mention-item-highlight);
|
|
}
|
|
|
|
.mention-item-name {
|
|
font-weight: 500;
|
|
}
|
|
|
|
.mentions-empty {
|
|
padding: 0.75rem;
|
|
text-align: center;
|
|
color: var(--text-muted);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
/* Mobile responsive */
|
|
@media (max-width: 576px) {
|
|
.mentions-popup {
|
|
width: 100%;
|
|
max-width: none;
|
|
left: 0;
|
|
right: 0;
|
|
}
|
|
}
|
|
|
|
/* Mentions popup scrollbar */
|
|
.mentions-popup::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.mentions-popup::-webkit-scrollbar-track {
|
|
background: var(--scrollbar-track);
|
|
}
|
|
|
|
.mentions-popup::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-thumb-light);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.mentions-popup::-webkit-scrollbar-thumb:hover {
|
|
background: var(--scrollbar-thumb-light-hover);
|
|
}
|
|
|
|
/* =============================================================================
|
|
Scroll to Bottom Button
|
|
============================================================================= */
|
|
|
|
.scroll-to-bottom-btn {
|
|
position: absolute;
|
|
bottom: 1rem;
|
|
right: 1.5rem;
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
background-color: rgba(var(--bs-primary-rgb), 0.7);
|
|
color: white;
|
|
font-size: 1.25rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: opacity 0.2s ease, visibility 0.2s ease, background-color 0.15s ease;
|
|
z-index: 100;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.scroll-to-bottom-btn.visible {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
}
|
|
|
|
.scroll-to-bottom-btn:hover {
|
|
background-color: rgba(var(--bs-primary-rgb), 0.9);
|
|
}
|
|
|
|
.scroll-to-bottom-btn:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
/* =============================================================================
|
|
Echo Badge - "Heard X repeats" indicator for sent messages
|
|
============================================================================= */
|
|
|
|
.echo-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
font-size: 0.7rem;
|
|
color: var(--echo-color);
|
|
padding: 0.2rem 0.4rem;
|
|
margin-right: 0.25rem;
|
|
border-radius: 0.25rem;
|
|
background-color: var(--echo-bg);
|
|
}
|
|
|
|
.echo-badge i {
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
/* Path info in message meta (incoming messages) */
|
|
.path-info {
|
|
cursor: pointer;
|
|
border-bottom: 1px dotted currentColor;
|
|
position: relative;
|
|
}
|
|
|
|
/* Path popup (mobile-friendly, multi-path) */
|
|
.path-popup {
|
|
position: absolute;
|
|
bottom: 100%;
|
|
right: 0;
|
|
left: auto;
|
|
background-color: #333;
|
|
color: #fff;
|
|
padding: 0.4rem 0.6rem;
|
|
border-radius: 0.375rem;
|
|
font-size: 0.7rem;
|
|
white-space: normal;
|
|
z-index: 1000;
|
|
pointer-events: auto;
|
|
margin-bottom: 4px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
|
min-width: 180px;
|
|
max-width: min(320px, calc(100vw - 1.5rem));
|
|
}
|
|
|
|
.path-popup .path-entry {
|
|
padding: 0.15rem 0;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
|
word-break: break-all;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.path-popup .path-route {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
.path-popup .path-copy {
|
|
flex: 0 0 auto;
|
|
padding: 0.1rem 0.15rem;
|
|
opacity: 0.7;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.path-popup .path-copy:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.path-popup .path-entry:active {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.path-popup .path-entry:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.path-popup .path-detail {
|
|
display: block;
|
|
opacity: 0.7;
|
|
font-size: 0.6rem;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Chat Filter
|
|
============================================================================= */
|
|
|
|
/* Filter FAB button (gray gradient) */
|
|
.fab-filter {
|
|
background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Search FAB button (muted teal) */
|
|
.fab-search {
|
|
background: linear-gradient(135deg, #5fa8a0 0%, #4a8f88 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Settings FAB button (warm orange) */
|
|
.fab-settings {
|
|
background: linear-gradient(135deg, #fd7e14 0%, #e06b0a 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Advert FAB button (muted teal) */
|
|
.fab-advert {
|
|
background: linear-gradient(135deg, #5fa8a0 0%, #4a8f88 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Flood Advert FAB button (warning yellow) */
|
|
.fab-floodadvert {
|
|
background: linear-gradient(135deg, #ffc107 0%, #d39e00 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Map FAB button (muted teal) */
|
|
.fab-map {
|
|
background: linear-gradient(135deg, #5fa8a0 0%, #4a8f88 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Console FAB button (blue) */
|
|
.fab-console {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0a58ca 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Device Info FAB button (muted teal) */
|
|
.fab-deviceinfo {
|
|
background: linear-gradient(135deg, #5fa8a0 0%, #4a8f88 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* System Log FAB button (blue) */
|
|
.fab-syslog {
|
|
background: linear-gradient(135deg, #0d6efd 0%, #0a58ca 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* My Repeaters FAB button (REP green) */
|
|
.fab-repeaters {
|
|
background: linear-gradient(135deg, #198754 0%, #146c43 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Path Analyzer FAB button + modal header (purple) */
|
|
.fab-pathanalyzer,
|
|
.pathanalyzer-header {
|
|
background: linear-gradient(135deg, #6f42c1 0%, #59359a 100%);
|
|
color: white;
|
|
}
|
|
|
|
/* Filter bar overlay - slides down from top of chat area */
|
|
.filter-bar {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 1002; /* Above mentions popup (1001) */
|
|
background-color: var(--filter-bg);
|
|
border-bottom: 1px solid var(--border-color);
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
padding: 0.75rem;
|
|
transform: translateY(-100%);
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: transform 0.3s ease, opacity 0.3s ease, visibility 0.3s ease;
|
|
}
|
|
|
|
.filter-bar.visible {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
visibility: visible;
|
|
}
|
|
|
|
/* Push messages container down when filter bar is visible */
|
|
.messages-container {
|
|
transition: padding-top 0.3s ease;
|
|
}
|
|
|
|
.filter-bar.visible ~ .messages-container {
|
|
padding-top: calc(1rem + 52px) !important; /* 52px ~ filter bar height */
|
|
}
|
|
|
|
/* Filter bar inner layout */
|
|
.filter-bar-inner {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.filter-bar-input {
|
|
border-radius: 0.375rem;
|
|
border: 1px solid var(--filter-input-border);
|
|
padding: 0.5rem 0.75rem;
|
|
font-size: 0.9rem;
|
|
background-color: var(--bg-body);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.filter-bar-input:focus {
|
|
outline: none;
|
|
border-color: #86b7fe;
|
|
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
|
}
|
|
|
|
[data-theme="dark"] .filter-bar-input:focus {
|
|
border-color: #3b82f6;
|
|
box-shadow: 0 0 0 0.2rem rgba(59, 130, 246, 0.25);
|
|
}
|
|
|
|
/* Filter bar buttons */
|
|
.filter-bar-btn {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
transition: background-color 0.15s ease;
|
|
}
|
|
|
|
.filter-bar-btn-me {
|
|
background-color: var(--filter-btn-me-bg);
|
|
color: var(--filter-btn-me-color);
|
|
}
|
|
|
|
.filter-bar-btn-me:hover {
|
|
background-color: var(--filter-btn-me-hover);
|
|
}
|
|
|
|
.filter-bar-btn-clear {
|
|
background-color: var(--filter-btn-clear-bg);
|
|
color: var(--filter-btn-clear-color);
|
|
}
|
|
|
|
.filter-bar-btn-clear:hover {
|
|
background-color: var(--filter-btn-clear-hover);
|
|
}
|
|
|
|
.filter-bar-btn-close {
|
|
background-color: #dc3545;
|
|
color: white;
|
|
}
|
|
|
|
.filter-bar-btn-close:hover {
|
|
background-color: #bb2d3b;
|
|
}
|
|
|
|
/* Filter match count indicator */
|
|
.filter-match-count {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
white-space: nowrap;
|
|
padding: 0 0.5rem;
|
|
}
|
|
|
|
/* Highlighted text in filtered messages */
|
|
.filter-highlight {
|
|
background-color: var(--filter-highlight);
|
|
border-radius: 0.2rem;
|
|
padding: 0 0.1rem;
|
|
}
|
|
|
|
/* Hidden messages when filtering */
|
|
.message-wrapper.filter-hidden,
|
|
.dm-message-wrapper.filter-hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
/* No matches message */
|
|
.filter-no-matches {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.filter-no-matches i {
|
|
font-size: 2rem;
|
|
margin-bottom: 0.5rem;
|
|
display: block;
|
|
}
|
|
|
|
/* Filter input wrapper for mentions popup positioning */
|
|
.filter-input-wrapper {
|
|
flex: 1;
|
|
position: relative;
|
|
}
|
|
|
|
.filter-input-wrapper .filter-bar-input {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Filter mentions popup - appears below input (not above like message input) */
|
|
.filter-mentions-popup {
|
|
bottom: auto !important;
|
|
top: 100% !important;
|
|
margin-top: 0.25rem;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* Mobile responsive filter bar */
|
|
@media (max-width: 576px) {
|
|
.filter-bar {
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
.filter-bar-input {
|
|
font-size: 0.85rem;
|
|
padding: 0.4rem 0.6rem;
|
|
}
|
|
|
|
.filter-bar-btn {
|
|
width: 32px;
|
|
height: 32px;
|
|
font-size: 0.9rem;
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
Global Search
|
|
============================================================================= */
|
|
|
|
#searchResults {
|
|
max-height: 60vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
#searchResults .list-group-item {
|
|
border-left: 3px solid transparent;
|
|
}
|
|
|
|
#searchResults .list-group-item:hover {
|
|
border-left-color: var(--bs-primary);
|
|
}
|
|
|
|
#searchResults mark {
|
|
background-color: var(--search-mark-bg);
|
|
padding: 0 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
/* Own device map marker */
|
|
.own-device-marker {
|
|
background: transparent;
|
|
border: none;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Emoji Picker (shared between index.html and dm.html)
|
|
============================================================================= */
|
|
|
|
emoji-picker {
|
|
--emoji-size: 1.5rem;
|
|
--num-columns: 8;
|
|
}
|
|
|
|
.emoji-picker-container {
|
|
position: relative;
|
|
}
|
|
|
|
.emoji-picker-popup {
|
|
position: absolute;
|
|
bottom: 100%;
|
|
right: 0;
|
|
z-index: 1000;
|
|
margin-bottom: 0.5rem;
|
|
box-shadow: var(--popup-shadow);
|
|
border-radius: 0.5rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.emoji-picker-popup.hidden {
|
|
display: none;
|
|
}
|
|
|
|
/* Mobile responsive adjustments */
|
|
@media (max-width: 576px) {
|
|
emoji-picker {
|
|
--emoji-size: 1.25rem;
|
|
--num-columns: 6;
|
|
}
|
|
.emoji-picker-popup {
|
|
right: auto;
|
|
left: 0;
|
|
width: 100%;
|
|
max-width: 100%;
|
|
}
|
|
}
|
|
|
|
/* Modal fullscreen - remove all margins and padding */
|
|
#dmModal .modal-dialog.modal-fullscreen,
|
|
#contactsModal .modal-dialog.modal-fullscreen,
|
|
#logsModal .modal-dialog.modal-fullscreen,
|
|
#consoleModal .modal-dialog.modal-fullscreen,
|
|
#repeatersModal .modal-dialog.modal-fullscreen,
|
|
#pathAnalyzerModal .modal-dialog.modal-fullscreen {
|
|
margin: 0 !important;
|
|
width: 100vw !important;
|
|
max-width: 100vw !important;
|
|
height: 100vh !important;
|
|
max-height: 100vh !important;
|
|
}
|
|
|
|
#dmModal .modal-content,
|
|
#contactsModal .modal-content,
|
|
#logsModal .modal-content,
|
|
#consoleModal .modal-content,
|
|
#repeatersModal .modal-content,
|
|
#pathAnalyzerModal .modal-content {
|
|
border: none !important;
|
|
border-radius: 0 !important;
|
|
height: 100vh !important;
|
|
}
|
|
|
|
#dmModal .modal-body,
|
|
#contactsModal .modal-body,
|
|
#logsModal .modal-body,
|
|
#consoleModal .modal-body,
|
|
#repeatersModal .modal-body,
|
|
#pathAnalyzerModal .modal-body {
|
|
overflow: hidden !important;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Contact Management Styles (shared by contacts_base.html and its contacts-* pages)
|
|
============================================================================= */
|
|
|
|
.compact-setting {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.75rem;
|
|
background-color: var(--bg-surface);
|
|
border-radius: 0.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.info-icon {
|
|
color: var(--text-muted);
|
|
cursor: help;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.info-icon:hover {
|
|
color: var(--sender-color);
|
|
}
|
|
|
|
.pending-contact-card {
|
|
background-color: var(--card-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 0.5rem;
|
|
padding: 1rem;
|
|
margin-bottom: 0.75rem;
|
|
box-shadow: var(--card-shadow);
|
|
}
|
|
|
|
.contact-name {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
margin-bottom: 0.1rem;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
.contact-key {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
word-break: break-all;
|
|
margin-bottom: 0.15rem;
|
|
}
|
|
|
|
.contact-key.clickable-key {
|
|
cursor: pointer;
|
|
transition: color 0.15s, background-color 0.15s;
|
|
padding: 0.15rem 0.3rem;
|
|
margin-left: -0.3rem;
|
|
border-radius: 0.25rem;
|
|
}
|
|
|
|
.contact-key.clickable-key:hover {
|
|
color: var(--key-hover-color);
|
|
background-color: var(--key-hover-bg);
|
|
}
|
|
|
|
.contact-key.clickable-key.copied {
|
|
color: var(--key-copied-color);
|
|
background-color: var(--key-copied-bg);
|
|
}
|
|
|
|
.btn-action {
|
|
min-height: 44px; /* Touch-friendly size */
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.info-badge {
|
|
display: inline-block;
|
|
background-color: var(--info-badge-bg);
|
|
color: var(--info-badge-color);
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.375rem;
|
|
font-size: 0.9rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
/* Existing Contacts Styles */
|
|
.existing-contact-card {
|
|
background-color: var(--card-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 0.5rem;
|
|
padding: 1rem;
|
|
margin-bottom: 0.75rem;
|
|
box-shadow: var(--card-shadow);
|
|
transition: box-shadow 0.2s;
|
|
}
|
|
|
|
.existing-contact-card:hover {
|
|
box-shadow: var(--card-shadow-hover);
|
|
}
|
|
|
|
.type-badge {
|
|
font-size: 0.75rem;
|
|
padding: 0.25rem 0.5rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* =============================================================================
|
|
DM Contact Dropdown (dm.html)
|
|
============================================================================= */
|
|
|
|
.dm-contact-dropdown {
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 1050;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
background: var(--dropdown-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-top: none;
|
|
border-radius: 0 0 0.375rem 0.375rem;
|
|
box-shadow: var(--popup-shadow);
|
|
}
|
|
|
|
.dm-contact-item {
|
|
padding: 0.5rem 0.75rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
border-bottom: 1px solid var(--border-light);
|
|
font-size: 0.88rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.dm-contact-item:hover,
|
|
.dm-contact-item.active {
|
|
background-color: var(--dropdown-item-hover);
|
|
}
|
|
|
|
.dm-contact-item .contact-name {
|
|
flex-grow: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-size: 0.88rem;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.dm-contact-item .badge {
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.dm-dropdown-separator {
|
|
padding: 0.25rem 0.75rem;
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
background: var(--dropdown-separator-bg);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Path Management (dm.html)
|
|
============================================================================= */
|
|
|
|
.path-list-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
padding: 0.35rem 0.5rem;
|
|
border: 1px solid var(--path-item-border);
|
|
border-radius: 0.25rem;
|
|
margin-bottom: 0.25rem;
|
|
font-size: 0.8rem;
|
|
background: var(--path-item-bg);
|
|
}
|
|
|
|
.path-list-item.primary {
|
|
border-color: var(--path-item-primary-border);
|
|
background: var(--path-item-primary-bg);
|
|
}
|
|
|
|
.path-list-item .path-hex {
|
|
font-family: monospace;
|
|
font-size: 0.75rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.path-list-item .path-label {
|
|
color: var(--text-muted);
|
|
font-size: 0.7rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.path-list-item .path-actions {
|
|
margin-left: auto;
|
|
display: flex;
|
|
gap: 0.15rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.path-list-item .path-actions .btn {
|
|
padding: 0 0.25rem;
|
|
font-size: 0.7rem;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.path-section-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.path-section-header h6 {
|
|
font-size: 0.85rem;
|
|
margin: 0;
|
|
}
|
|
|
|
.repeater-picker-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.4rem 0.5rem;
|
|
cursor: pointer;
|
|
border-bottom: 1px solid var(--border-light);
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.repeater-picker-item:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
|
|
.repeater-picker-item .badge {
|
|
font-family: monospace;
|
|
font-size: 0.7rem;
|
|
}
|
|
|
|
.path-uniqueness-warning {
|
|
color: #dc3545;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
/* Leaflet z-index fix for Bootstrap modal (DM repeater map) */
|
|
#rptLeafletMap { z-index: 1; }
|
|
#rptLeafletMap .leaflet-top,
|
|
#rptLeafletMap .leaflet-bottom { z-index: 1000; }
|
|
|
|
/* =============================================================================
|
|
Contact Management - Page-specific Styles (contacts-* pages)
|
|
============================================================================= */
|
|
|
|
.contact-info-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
margin-bottom: 0.5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.counter-badge {
|
|
font-size: 1rem;
|
|
padding: 0.35rem 0.75rem;
|
|
}
|
|
|
|
.counter-ok {
|
|
background-color: #28a745;
|
|
}
|
|
|
|
.counter-warning {
|
|
background-color: #ffc107;
|
|
color: #212529;
|
|
}
|
|
|
|
.counter-alarm {
|
|
background-color: #dc3545;
|
|
animation: counterPulse 1.5s infinite;
|
|
}
|
|
|
|
@keyframes counterPulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.7; }
|
|
}
|
|
|
|
.search-toolbar {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-bottom: 1rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.search-toolbar input,
|
|
.search-toolbar select {
|
|
flex: 1;
|
|
min-width: 150px;
|
|
}
|
|
|
|
/* Scrollable contacts lists - height managed by flexbox in contacts_base */
|
|
#pendingList,
|
|
#existingList {
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
#existingList::-webkit-scrollbar,
|
|
#pendingList::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
#existingList::-webkit-scrollbar-track,
|
|
#pendingList::-webkit-scrollbar-track {
|
|
background: var(--scrollbar-track);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
#existingList::-webkit-scrollbar-thumb,
|
|
#pendingList::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-thumb);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
#existingList::-webkit-scrollbar-thumb:hover,
|
|
#pendingList::-webkit-scrollbar-thumb:hover {
|
|
background: var(--scrollbar-thumb-hover);
|
|
}
|
|
|
|
.section-compact {
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
/* Device settings sub-tabs */
|
|
#tabSettingsDevice .nav-pills .nav-link {
|
|
font-size: 0.8rem;
|
|
padding: 0.25rem 0.75rem;
|
|
}
|