mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-05-02 03:22:40 +02:00
style(dm): move timestamp above bubble, improve meta readability
Move DM timestamp+status row above the message bubble (consistent with group messages). Increase delivery/SNR meta font size and adjust color for better readability in both light and dark themes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -492,7 +492,7 @@ main {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.dm-message {
|
||||
.dm-message-wrapper {
|
||||
max-width: 85%;
|
||||
}
|
||||
|
||||
@@ -683,31 +683,58 @@ main {
|
||||
}
|
||||
}
|
||||
|
||||
/* DM Message Bubbles */
|
||||
.dm-message {
|
||||
/* DM Message Wrapper - timestamp + bubble */
|
||||
.dm-message-wrapper {
|
||||
max-width: 70%;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 1rem;
|
||||
word-wrap: break-word;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
animation: fadeIn 0.3s ease-in;
|
||||
}
|
||||
|
||||
.dm-message.own {
|
||||
.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 {
|
||||
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: var(--text-meta);
|
||||
font-size: 0.75rem;
|
||||
color: var(--dm-meta-color);
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
@@ -743,8 +770,8 @@ main {
|
||||
}
|
||||
|
||||
.dm-delivery-meta {
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-meta);
|
||||
font-size: 0.75rem;
|
||||
color: var(--dm-meta-color);
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
@@ -1577,7 +1604,7 @@ main {
|
||||
|
||||
/* Hidden messages when filtering */
|
||||
.message-wrapper.filter-hidden,
|
||||
.dm-message.filter-hidden {
|
||||
.dm-message-wrapper.filter-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
--text-secondary: #495057;
|
||||
--text-muted: #6c757d;
|
||||
--text-meta: #adb5bd;
|
||||
--dm-meta-color: #8b939b;
|
||||
|
||||
/* Borders */
|
||||
--border-color: #dee2e6;
|
||||
@@ -176,6 +177,7 @@
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #64748b;
|
||||
--text-meta: #475569;
|
||||
--dm-meta-color: #5c6d82;
|
||||
|
||||
/* Borders */
|
||||
--border-color: #334155;
|
||||
|
||||
@@ -103,7 +103,7 @@ function connectChatSocket() {
|
||||
if (!data.expected_ack) return;
|
||||
|
||||
// Find message with matching expected_ack in DOM and update status
|
||||
const msgElements = document.querySelectorAll('#dmMessagesList .dm-message.own');
|
||||
const msgElements = document.querySelectorAll('#dmMessagesList .dm-message-wrapper.own');
|
||||
msgElements.forEach(el => {
|
||||
const statusEl = el.querySelector(`.dm-status[data-ack="${data.expected_ack}"]`);
|
||||
if (statusEl) {
|
||||
@@ -1142,8 +1142,11 @@ function displayMessages(messages) {
|
||||
container.innerHTML = '';
|
||||
|
||||
messages.forEach(msg => {
|
||||
const div = document.createElement('div');
|
||||
div.className = `dm-message ${msg.is_own ? 'own' : 'other'}`;
|
||||
const side = msg.is_own ? 'own' : 'other';
|
||||
|
||||
// Wrapper: time row + bubble
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = `dm-message-wrapper ${side}`;
|
||||
|
||||
// Status icon for own messages
|
||||
let statusIcon = '';
|
||||
@@ -1217,19 +1220,25 @@ function displayMessages(messages) {
|
||||
</div>
|
||||
` : '';
|
||||
|
||||
div.innerHTML = `
|
||||
<div class="d-flex justify-content-between align-items-center" style="font-size: 0.7rem;">
|
||||
<span class="text-muted">${formatTime(msg.timestamp)}</span>
|
||||
${statusIcon}
|
||||
</div>
|
||||
<div>${processMessageContent(msg.content)}</div>
|
||||
// Time row above bubble
|
||||
const timeRow = document.createElement('div');
|
||||
timeRow.className = 'dm-time-row';
|
||||
timeRow.innerHTML = `<span>${formatTime(msg.timestamp)}</span>${statusIcon}`;
|
||||
wrapper.appendChild(timeRow);
|
||||
|
||||
// Message bubble
|
||||
const bubble = document.createElement('div');
|
||||
bubble.className = `dm-message ${side}`;
|
||||
bubble.innerHTML = `
|
||||
<div class="dm-content">${processMessageContent(msg.content)}</div>
|
||||
${deliveryMeta}
|
||||
${retryInfo}
|
||||
${meta}
|
||||
${resendBtn}
|
||||
`;
|
||||
wrapper.appendChild(bubble);
|
||||
|
||||
container.appendChild(div);
|
||||
container.appendChild(wrapper);
|
||||
});
|
||||
|
||||
// Scroll to bottom
|
||||
@@ -1838,7 +1847,7 @@ function closeDmFilterBar() {
|
||||
function applyDmFilter(query) {
|
||||
currentDmFilterQuery = query.trim();
|
||||
const container = document.getElementById('dmMessagesList');
|
||||
const messages = container.querySelectorAll('.dm-message');
|
||||
const messages = container.querySelectorAll('.dm-message-wrapper');
|
||||
const matchCountEl = document.getElementById('dmFilterMatchCount');
|
||||
|
||||
// Remove any existing no-matches message
|
||||
@@ -1886,72 +1895,48 @@ function applyDmFilter(query) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get text content from a DM message
|
||||
* DM structure: timestamp div, then content div, then meta/actions
|
||||
* @param {HTMLElement} msgEl - DM message element
|
||||
* Get text content from a DM message wrapper
|
||||
* @param {HTMLElement} wrapperEl - DM message wrapper element
|
||||
* @returns {string} - Text content
|
||||
*/
|
||||
function getDmMessageText(msgEl) {
|
||||
// The message content is in a div that is not the timestamp row, meta, or actions
|
||||
const children = msgEl.children;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
// Skip timestamp row (has d-flex class), meta, and actions
|
||||
if (!child.classList.contains('d-flex') &&
|
||||
!child.classList.contains('dm-meta') &&
|
||||
!child.classList.contains('dm-actions')) {
|
||||
return child.textContent || '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
function getDmMessageText(wrapperEl) {
|
||||
const content = wrapperEl.querySelector('.dm-content');
|
||||
return content ? content.textContent || '' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight matching text in a DM message
|
||||
* @param {HTMLElement} msgEl - DM message element
|
||||
* @param {HTMLElement} wrapperEl - DM message wrapper element
|
||||
* @param {number} index - Message index for tracking
|
||||
*/
|
||||
function highlightDmMessageContent(msgEl, index) {
|
||||
function highlightDmMessageContent(wrapperEl, index) {
|
||||
const msgId = 'dm_msg_' + index;
|
||||
const content = wrapperEl.querySelector('.dm-content');
|
||||
if (!content) return;
|
||||
|
||||
// Find content div (not timestamp, not meta, not actions)
|
||||
const children = Array.from(msgEl.children);
|
||||
for (const child of children) {
|
||||
if (!child.classList.contains('d-flex') &&
|
||||
!child.classList.contains('dm-meta') &&
|
||||
!child.classList.contains('dm-actions')) {
|
||||
|
||||
if (!originalDmMessageContents.has(msgId)) {
|
||||
originalDmMessageContents.set(msgId, child.innerHTML);
|
||||
}
|
||||
|
||||
const originalHtml = originalDmMessageContents.get(msgId);
|
||||
child.innerHTML = FilterUtils.highlightMatches(originalHtml, currentDmFilterQuery);
|
||||
break;
|
||||
}
|
||||
if (!originalDmMessageContents.has(msgId)) {
|
||||
originalDmMessageContents.set(msgId, content.innerHTML);
|
||||
}
|
||||
|
||||
const originalHtml = originalDmMessageContents.get(msgId);
|
||||
content.innerHTML = FilterUtils.highlightMatches(originalHtml, currentDmFilterQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore original DM message content
|
||||
* @param {HTMLElement} msgEl - DM message element
|
||||
* @param {HTMLElement} wrapperEl - DM message wrapper element
|
||||
*/
|
||||
function restoreDmOriginalContent(msgEl) {
|
||||
function restoreDmOriginalContent(wrapperEl) {
|
||||
const container = document.getElementById('dmMessagesList');
|
||||
const messages = Array.from(container.querySelectorAll('.dm-message'));
|
||||
const index = messages.indexOf(msgEl);
|
||||
const wrappers = Array.from(container.querySelectorAll('.dm-message-wrapper'));
|
||||
const index = wrappers.indexOf(wrapperEl);
|
||||
const msgId = 'dm_msg_' + index;
|
||||
|
||||
if (!originalDmMessageContents.has(msgId)) return;
|
||||
|
||||
const children = Array.from(msgEl.children);
|
||||
for (const child of children) {
|
||||
if (!child.classList.contains('d-flex') &&
|
||||
!child.classList.contains('dm-meta') &&
|
||||
!child.classList.contains('dm-actions')) {
|
||||
child.innerHTML = originalDmMessageContents.get(msgId);
|
||||
break;
|
||||
}
|
||||
const content = wrapperEl.querySelector('.dm-content');
|
||||
if (content) {
|
||||
content.innerHTML = originalDmMessageContents.get(msgId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user