mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-03-28 17:42:45 +01:00
Replace unreliable timestamp-based heuristic (±10s window) with exact cryptographic matching for incoming channel message routes. Compute pkt_payload by reconstructing the AES-128-ECB encrypted packet from message data (sender_timestamp, txt_type, text) + channel secret, then match against echo data by exact key lookup. Also accumulate ALL route paths per message (previously only last path was kept due to dict overwrite), and display them in a multi-path popup showing SNR and hops for each route. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1327 lines
27 KiB
CSS
1327 lines
27 KiB
CSS
/* mc-webui Custom Styles */
|
|
|
|
:root {
|
|
--msg-own-bg: #e7f1ff;
|
|
--msg-other-bg: #f8f9fa;
|
|
--msg-border: #dee2e6;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
main {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0; /* Important for flex children */
|
|
}
|
|
|
|
/* Messages Container */
|
|
.messages-container {
|
|
background-color: #ffffff;
|
|
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: #0d6efd;
|
|
}
|
|
|
|
.message-sender-row .message-time {
|
|
font-size: 0.7rem;
|
|
color: #6c757d;
|
|
}
|
|
|
|
/* 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: #6c757d;
|
|
}
|
|
|
|
.message-footer .message-sender.own {
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
color: #084298;
|
|
margin-right: 0.5rem;
|
|
}
|
|
|
|
/* Message action buttons container */
|
|
.message-actions {
|
|
display: flex;
|
|
gap: 0.25rem;
|
|
margin-top: 0.5rem;
|
|
padding-top: 0.5rem;
|
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* 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: #b8daff;
|
|
}
|
|
|
|
/* Other Messages (left-aligned) */
|
|
.message.other {
|
|
background-color: var(--msg-other-bg);
|
|
}
|
|
|
|
.message-time {
|
|
font-size: 0.75rem;
|
|
color: #6c757d;
|
|
}
|
|
|
|
/* Message Content */
|
|
.message-content {
|
|
margin: 0;
|
|
white-space: pre-wrap;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Message Metadata */
|
|
.message-meta {
|
|
font-size: 0.7rem;
|
|
color: #adb5bd;
|
|
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: #f1f1f1;
|
|
}
|
|
|
|
.messages-container::-webkit-scrollbar-thumb {
|
|
background: #888;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.messages-container::-webkit-scrollbar-thumb:hover {
|
|
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-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 {
|
|
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: Channel selector on mobile */
|
|
#channelSelector,
|
|
#dmConversationSelector {
|
|
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: Smaller channel keys on mobile */
|
|
.list-group-item small {
|
|
font-size: 0.65rem;
|
|
word-break: break-all;
|
|
}
|
|
|
|
/* Modal: Compact channel list */
|
|
.modal .list-group-item {
|
|
padding: 0.5rem;
|
|
}
|
|
|
|
/* Modal: Stack buttons vertically on very small screens */
|
|
@media (max-width: 400px) {
|
|
.btn-group {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.btn-group .btn {
|
|
border-radius: 0.375rem !important;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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: #6c757d;
|
|
}
|
|
|
|
.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: #fafafa;
|
|
}
|
|
|
|
/* 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 Bubbles */
|
|
.dm-message {
|
|
max-width: 70%;
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 1rem;
|
|
word-wrap: break-word;
|
|
animation: fadeIn 0.3s ease-in;
|
|
}
|
|
|
|
.dm-message.own {
|
|
align-self: flex-end;
|
|
background-color: var(--msg-own-bg);
|
|
border: 1px solid #b8daff;
|
|
}
|
|
|
|
.dm-message.other {
|
|
align-self: flex-start;
|
|
background-color: var(--msg-other-bg);
|
|
border: 1px solid var(--msg-border);
|
|
}
|
|
|
|
/* DM Message Metadata */
|
|
.dm-meta {
|
|
font-size: 0.65rem;
|
|
color: #adb5bd;
|
|
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: #adb5bd;
|
|
}
|
|
|
|
.dm-status-unknown {
|
|
cursor: pointer;
|
|
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 #dee2e6;
|
|
cursor: pointer;
|
|
transition: background-color 0.15s ease;
|
|
}
|
|
|
|
.dm-conversation-item:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.dm-conversation-item.unread {
|
|
background-color: #e7f1ff;
|
|
}
|
|
|
|
.dm-conversation-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
/* DM Preview Text */
|
|
.dm-preview {
|
|
font-size: 0.85rem;
|
|
color: #6c757d;
|
|
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: #6c757d;
|
|
}
|
|
|
|
.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: #f1f1f1;
|
|
}
|
|
|
|
.dm-messages-container::-webkit-scrollbar-thumb {
|
|
background: #ccc;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.dm-messages-container::-webkit-scrollbar-thumb:hover {
|
|
background: #aaa;
|
|
}
|
|
|
|
/* =============================================================================
|
|
Message Content Enhancements (Mentions, Links, Images)
|
|
============================================================================= */
|
|
|
|
/* Mention Badge (similar to Android Meshcore app) */
|
|
.mention-badge {
|
|
display: inline-block;
|
|
background-color: #0d6efd;
|
|
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: #084298;
|
|
}
|
|
|
|
/* Channel Links (#channelname) */
|
|
.channel-link {
|
|
display: inline-block;
|
|
background-color: #198754;
|
|
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: #157347;
|
|
color: white;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.message.own .channel-link {
|
|
background-color: #0f5132;
|
|
}
|
|
|
|
.message.own .channel-link:hover {
|
|
background-color: #0d4429;
|
|
}
|
|
|
|
.channel-link.loading {
|
|
opacity: 0.7;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.channel-link.loading::after {
|
|
content: '...';
|
|
}
|
|
|
|
/* Quoted Text (»text«) */
|
|
.quote-text {
|
|
font-style: italic;
|
|
color: #6c757d;
|
|
background-color: rgba(108, 117, 125, 0.1);
|
|
padding: 0.1rem 0.3rem;
|
|
border-radius: 0.25rem;
|
|
border-left: 2px solid #6c757d;
|
|
}
|
|
|
|
.message.own .quote-text {
|
|
color: #495057;
|
|
background-color: rgba(8, 66, 152, 0.1);
|
|
border-left-color: #084298;
|
|
}
|
|
|
|
/* Large Emoji for emoji-only messages */
|
|
.emoji-large {
|
|
font-size: 2.5rem;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
/* Clickable Links in Messages */
|
|
.message-link {
|
|
color: #0d6efd;
|
|
text-decoration: underline;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.message-link:hover {
|
|
color: #0a58ca;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.message.own .message-link {
|
|
color: #084298;
|
|
}
|
|
|
|
.message.own .message-link:hover {
|
|
color: #052c65;
|
|
}
|
|
|
|
/* 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 #dee2e6;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
/* =============================================================================
|
|
TEST: 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: 12px;
|
|
}
|
|
|
|
.fab {
|
|
position: relative; /* For badge positioning */
|
|
width: 56px;
|
|
height: 56px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.5rem;
|
|
cursor: pointer;
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
|
|
.fab:hover {
|
|
transform: scale(1.1);
|
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.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; /* Bootstrap danger red - same as notification bell */
|
|
}
|
|
|
|
/* FAB Badge for Direct Messages (red for consistency and contrast) */
|
|
.fab-badge-dm {
|
|
background-color: #dc3545; /* Bootstrap danger red - same as notification bell */
|
|
}
|
|
|
|
/* Animation on hover */
|
|
.fab:hover .fab-badge {
|
|
transform: scale(1.1);
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
/* Mobile optimization */
|
|
@media (max-width: 768px) {
|
|
.fab-container {
|
|
right: 12px;
|
|
top: 70px;
|
|
gap: 10px;
|
|
}
|
|
|
|
.fab {
|
|
width: 48px;
|
|
height: 48px;
|
|
font-size: 1.25rem;
|
|
}
|
|
}
|
|
|
|
/* =============================================================================
|
|
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: white;
|
|
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: white;
|
|
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: white;
|
|
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: white;
|
|
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: white;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.5rem;
|
|
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.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 #f0f0f0;
|
|
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: #e7f1ff;
|
|
}
|
|
|
|
.mention-item-name {
|
|
font-weight: 500;
|
|
}
|
|
|
|
.mentions-empty {
|
|
padding: 0.75rem;
|
|
text-align: center;
|
|
color: #6c757d;
|
|
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: #f1f1f1;
|
|
}
|
|
|
|
.mentions-popup::-webkit-scrollbar-thumb {
|
|
background: #ccc;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.mentions-popup::-webkit-scrollbar-thumb:hover {
|
|
background: #aaa;
|
|
}
|
|
|
|
/* =============================================================================
|
|
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: #198754;
|
|
padding: 0.2rem 0.4rem;
|
|
margin-right: 0.25rem;
|
|
border-radius: 0.25rem;
|
|
background-color: rgba(25, 135, 84, 0.1);
|
|
}
|
|
|
|
.echo-badge i {
|
|
font-size: 0.65rem;
|
|
}
|
|
|
|
/* Dark mode support */
|
|
[data-bs-theme="dark"] .echo-badge {
|
|
color: #75b798;
|
|
background-color: rgba(117, 183, 152, 0.15);
|
|
}
|
|
|
|
/* 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%;
|
|
left: 0;
|
|
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: 320px;
|
|
}
|
|
|
|
.path-popup .path-entry {
|
|
padding: 0.15rem 0;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
|
|
word-break: break-all;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
/* 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: #ffffff;
|
|
border-bottom: 1px solid #dee2e6;
|
|
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;
|
|
}
|
|
|
|
/* Filter bar inner layout */
|
|
.filter-bar-inner {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.filter-bar-input {
|
|
flex: 1;
|
|
border-radius: 0.375rem;
|
|
border: 1px solid #ced4da;
|
|
padding: 0.5rem 0.75rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.filter-bar-input:focus {
|
|
outline: none;
|
|
border-color: #86b7fe;
|
|
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 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-clear {
|
|
background-color: #f8f9fa;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.filter-bar-btn-clear:hover {
|
|
background-color: #e9ecef;
|
|
}
|
|
|
|
.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: #6c757d;
|
|
white-space: nowrap;
|
|
padding: 0 0.5rem;
|
|
}
|
|
|
|
/* Highlighted text in filtered messages */
|
|
.filter-highlight {
|
|
background-color: #fff3cd;
|
|
border-radius: 0.2rem;
|
|
padding: 0 0.1rem;
|
|
}
|
|
|
|
/* Hidden messages when filtering */
|
|
.message-wrapper.filter-hidden,
|
|
.dm-message.filter-hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
/* No matches message */
|
|
.filter-no-matches {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.filter-no-matches i {
|
|
font-size: 2rem;
|
|
margin-bottom: 0.5rem;
|
|
display: block;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|