From 709c0022f0321e6bd5809c1adb669c28128397fe Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Thu, 30 Jul 2026 23:56:39 +0200 Subject: [PATCH] Improved Channels for Phone & Desktop Fixed channels thumbnail layout issue Added same comments design from boards to channels --- webui-src/app/boards/board_kanban.js | 25 +- webui-src/app/boards/board_view.js | 43 +- webui-src/app/boards/boards_util.js | 17 +- webui-src/app/channels/channel_view.js | 392 +- webui-src/app/channels/channels_util.js | 4 +- webui-src/app/people/people_util.js | 20 +- webui-src/app/scss/pages/_board.scss | 17 +- webui-src/app/scss/pages/_channel.scss | 110 + webui-src/styles.css | 6098 +++++++++++++++++++++++ 9 files changed, 6559 insertions(+), 167 deletions(-) diff --git a/webui-src/app/boards/board_kanban.js b/webui-src/app/boards/board_kanban.js index cbc742c..32c89ae 100644 --- a/webui-src/app/boards/board_kanban.js +++ b/webui-src/app/boards/board_kanban.js @@ -13,24 +13,15 @@ function numberValue(value) { * Fallback SVG Thumbnail when no image is available */ const FallbackImage = () => - m( - 'svg.board-card__placeholder-img', - { - xmlns: 'http://www.w3.org/2000/svg', - viewBox: '0 0 24 24', - fill: 'none', - stroke: 'currentColor', - strokeWidth: '1.5', - strokeLinecap: 'round', - strokeLinejoin: 'round', - 'aria-hidden': 'true', + m('.board-card__placeholder-content', { + style: { + display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', + gap: '.3rem', color: '#64748b', fontSize: '.72rem', fontWeight: '600', textAlign: 'center', }, - [ - m('rect', { x: '3', y: '3', width: '18', height: '18', rx: '2', ry: '2' }), - m('circle', { cx: '8.5', cy: '8.5', r: '1.5' }), - m('polyline', { points: '21 15 16 10 5 21' }), - ] - ); + }, [ + m('i.fas.fa-image[aria-hidden=true]', { style: { fontSize: '1.35rem' } }), + m('span', 'No image'), + ]); /** * Check if notes string contains non-whitespace text diff --git a/webui-src/app/boards/board_view.js b/webui-src/app/boards/board_view.js index 5229af1..9cc4041 100644 --- a/webui-src/app/boards/board_view.js +++ b/webui-src/app/boards/board_view.js @@ -3,6 +3,7 @@ const util = require('boards/boards_util'); const boardKanban = require('boards/board_kanban'); const rs = require('rswebui'); const peopleUtil = require('people/people_util'); +const chatEmoji = require('chat/chat_emoji'); const Data = util.Data; function createboard() { @@ -275,10 +276,13 @@ function PostView() { let loadingComments = true; let identities = []; let authorId = null; + let voteIdentity = null; let replyTo = null; let composerText = ''; let submitting = false; let submitError = ''; + let notesExpanded = false; + let showEmojiPicker = false; const expandedReplies = {}; const metaOf = (comment) => (comment && comment.mMeta) || {}; @@ -372,6 +376,7 @@ function PostView() { peopleUtil.ownIds((ids) => { identities = (ids || []).filter((id) => Number(id) !== 0); authorId = identities[0] || null; + voteIdentity = identities[0] || null; m.redraw(); }); }, @@ -384,6 +389,7 @@ function PostView() { const title = meta.mMsgName || p.mMsgName || p.title || 'Post'; const notes = util.plainText(p.mNotes || p.mBody || p.notes || p.body || ''); + const hasLongNotes = notes.length > 280; const author = meta.mAuthorId ? meta.mAuthorId.substring(0, 10) : 'Unknown'; const publishTs = meta.mPublishTs || p.mPublishTs || null; const dateStr = publishTs @@ -425,26 +431,46 @@ function PostView() { m('b', author), dateStr ? m('span', ` • ${dateStr}`) : null, ]), - notes ? m('p', { style: { whiteSpace: 'pre-wrap', margin: '1rem 0' } }, notes) : null, + notes ? m('.post-description.board-post-description', [ + m('.post-description__text', { class: notesExpanded ? '' : 'post-description__text--collapsed', style: { whiteSpace: 'pre-wrap', maxHeight: notesExpanded ? 'none' : '4.5em', overflow: 'hidden', lineHeight: '1.5' } }, notes), + hasLongNotes ? m('button.post-description__toggle[type=button]', { onclick: () => { notesExpanded = !notesExpanded; } }, notesExpanded ? 'Show less' : '…more') : null, + ]) : null, m('hr'), m('.board-comments', [ - m('.board-comments__heading', [m('h3', `${comments.length} Comment${comments.length === 1 ? '' : 's'}`), m('span', [m('i.fas.fa-sort-amount-down'), ' Oldest first'])]), + m('.board-comments__heading', [ + m('h3', `${comments.length} Comment${comments.length === 1 ? '' : 's'}`), + m('span', [m('i.fas.fa-sort-amount-down'), ' Oldest first']), + m('.board-comments__voter', [ + m('label[for=board-comment-voter]', 'Voter identity'), + m('select#board-comment-voter', { + value: voteIdentity || '', + disabled: identities.length === 0, + onchange: (e) => { voteIdentity = e.target.value; }, + }, identities.length + ? identities.map((id) => m('option', { value: id }, nameOf(id))) + : m('option', { value: '' }, 'Loading identities…')), + ]), + ]), m('.board-comment-composer', [ m('.board-comment-avatar', initials(nameOf(authorId))), m('.board-comment-composer__body', [ replyTo ? m('.board-comment-composer__replying', ['Replying to ', m('b', nameOf(metaOf(replyTo).mAuthorId)), m('button[type=button][aria-label=Cancel reply]', { onclick: () => { replyTo = null; composerText = ''; } }, m('i.fas.fa-times'))]) : null, - identities.length > 1 ? m('select.board-comment-composer__identity', { value: authorId, onchange: (e) => { authorId = e.target.value; } }, identities.map((id) => m('option', { value: id }, nameOf(id)))) : null, + identities.length ? m('select.board-comment-composer__identity', { value: authorId, onchange: (e) => { authorId = e.target.value; } }, identities.map((id) => m('option', { value: id }, nameOf(id)))) : null, m('textarea.board-comment-composer__input[rows=1][placeholder=Add a comment…]', { value: composerText, disabled: !authorId || submitting, oninput: (e) => { composerText = e.target.value; }, onkeydown: (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') submitComment(forumId, msgId); } }), !authorId ? m('p.board-comment-composer__hint', 'Create or select an identity to post a comment.') : null, submitError ? m('p.board-comment-composer__error', submitError) : null, m('.board-comment-composer__actions', [ + m('.board-comment-composer__emoji', { style: { position: 'relative', marginRight: 'auto' } }, [ + m('button[type=button][title=Insert emoji][aria-label=Insert emoji]', { style: { width: '32px', height: '32px', padding: '0', borderRadius: '50%', border: '0', boxShadow: 'none', background: showEmojiPicker ? '#e0f2fe' : 'transparent', color: '#475569', fontSize: '1.15rem' }, onclick: () => { showEmojiPicker = !showEmojiPicker; } }, m('i.fas.fa-smile')), + showEmojiPicker ? m('.board-comment-emoji-popover', { style: { position: 'absolute', zIndex: '20', top: '38px', left: '0', width: '250px', maxHeight: '180px', overflowY: 'auto', padding: '.5rem', display: 'grid', gridTemplateColumns: 'repeat(8, 1fr)', gap: '.2rem', background: '#fff', border: '1px solid #cbd5e1', borderRadius: '8px', boxShadow: '0 8px 20px rgba(0,0,0,.16)' } }, chatEmoji.EMOJI_DATA.Smileys.slice(0, 48).map((emoji) => m('button[type=button]', { style: { width: '28px', height: '28px', padding: '0', border: '0', boxShadow: 'none', background: 'transparent', fontSize: '1.1rem' }, onclick: () => { composerText += emoji; showEmojiPicker = false; } }, emoji))) : null, + ]), composerText || replyTo ? m('button.board-comment-composer__cancel[type=button]', { onclick: () => { composerText = ''; replyTo = null; submitError = ''; } }, 'Cancel') : null, m('button.board-comment-composer__submit[type=button]', { disabled: !composerText.trim() || !authorId || submitting, onclick: () => submitComment(forumId, msgId) }, submitting ? 'Posting…' : 'Comment') ]) ]) ]), loadingComments ? m('.board-comments__status', [m('i.fas.fa-spinner.fa-spin'), ' Loading comments…']) - : comments.length === 0 ? m('.board-comments__empty', [m('i.far.fa-comment'), m('p', 'No comments yet. Start the conversation.')]) + : comments.length === 0 ? m('.board-comments__empty', [m('i.fas.fa-comment'), m('p', 'No comments yet. Start the conversation.')]) : m('.board-comments__list', treeOfComments().map((node) => renderComment(node, 0))), ]), ]), @@ -468,7 +494,14 @@ function PostView() { ]), m('p.board-comment__text', textOf(comment)), m('.board-comment__actions', [ - m('span.board-comment__like', [m('i.far.fa-thumbs-up'), ' Like']), + m('button[type=button]', { + disabled: !voteIdentity, + onclick: () => util.voteForPost(forumId, key, util.GXS_VOTE_UP, voteIdentity), + }, [m('i.fas.fa-thumbs-up'), ` ${comment.mUpVotes || 0}`]), + m('button[type=button]', { + disabled: !voteIdentity, + onclick: () => util.voteForPost(forumId, key, util.GXS_VOTE_DOWN, voteIdentity), + }, m('i.fas.fa-thumbs-down')), m('button[type=button]', { onclick: () => { replyTo = comment; composerText = ''; submitError = ''; } }, 'Reply') ]), repliesCount ? m('button.board-comment__replies-toggle[type=button]', { diff --git a/webui-src/app/boards/boards_util.js b/webui-src/app/boards/boards_util.js index 7002920..74225b7 100644 --- a/webui-src/app/boards/boards_util.js +++ b/webui-src/app/boards/boards_util.js @@ -245,15 +245,18 @@ function popupmessage(message) { ); } -async function voteForPost(postGrpId, postMsgId, voteType) { +async function voteForPost(postGrpId, postMsgId, voteType, voterId = null) { try { - const resId = await rs.rsJsonApiRequest('/rsIdentity/getOwnIds', {}); - const ownIds = (resId && resId.body && resId.body.ids) ? resId.body.ids : []; - if (ownIds.length === 0) { - alert('No identity found to vote.'); - return false; + let authorId = voterId; + if (!authorId) { + const resId = await rs.rsJsonApiRequest('/rsIdentity/getOwnIds', {}); + const ownIds = (resId && resId.body && resId.body.ids) ? resId.body.ids : []; + if (ownIds.length === 0) { + alert('No identity found to vote.'); + return false; + } + authorId = ownIds[0]; } - const authorId = ownIds[0]; const res = await rs.rsJsonApiRequest('/rsPosted/voteForPost', { postGrpId: postGrpId, diff --git a/webui-src/app/channels/channel_view.js b/webui-src/app/channels/channel_view.js index 81adda2..0ca79df 100644 --- a/webui-src/app/channels/channel_view.js +++ b/webui-src/app/channels/channel_view.js @@ -7,6 +7,7 @@ const peopleUtil = require('people/people_util'); const sha1 = require('channels/sha1'); const fileUtil = require('files/files_util'); const fileDown = require('files/files_downloads'); +const chatEmoji = require('chat/chat_emoji'); const filesUploadHashes = { // figure out a better way later. @@ -14,6 +15,29 @@ const filesUploadHashes = { Thumbnail: [], }; +function channelThumbnailSrc(post) { + const thumbnail = post && (post.mThumbnail || post.thumbnail || post.mImage); + const base64 = thumbnail && thumbnail.mData && thumbnail.mData.base64 + ? thumbnail.mData.base64 + : typeof thumbnail === 'string' + ? thumbnail + : thumbnail && thumbnail.base64; + if (!base64 || !String(base64).trim()) return ''; + return String(base64).startsWith('data:') ? base64 : `data:image/png;base64,${base64}`; +} + +const ChannelFallbackThumbnail = () => ({ + view: (vnode) => m('.channel-post__placeholder', { style: { + display: vnode.attrs.hidden ? 'none' : 'flex', flex: '1 1 auto', minHeight: '0', + flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '.35rem', + color: '#64748b', background: 'linear-gradient(135deg, #f8fafc, #dbe5f1)', + } }, [ + m('i.fas.fa-image[aria-hidden=true]', { style: { fontSize: '1.35rem', color: '#64748b' } }), + m('span', { style: { fontSize: '2rem', fontWeight: '700', color: '#2563eb' } }, (vnode.attrs.title || 'Post').trim().slice(0, 1).toUpperCase()), + m('small', { style: { fontSize: '.72rem', fontWeight: '600' } }, 'No image'), + ]), +}); + async function parsefile(file, type) { const fileSize = file.size; const chunkSize = 1024 * 1024; // bytes @@ -428,7 +452,14 @@ const ChannelView = () => { m( '.posts-container-card', { - style: 'display: ' + (plist[key].isSearched ? 'flex' : 'none'), // for search + style: { + display: plist[key].isSearched ? 'flex' : 'none', // for search + height: '240px', + minHeight: '0', + overflow: 'hidden', + flexDirection: 'column', + alignSelf: 'start', + }, onclick: () => { m.route.set('/channels/:tab/:mGroupId/:mMsgId', { tab: m.route.param().tab, @@ -438,13 +469,19 @@ const ChannelView = () => { }, }, [ - m('img', { - src: - plist[key].post.mThumbnail.mData.base64 === '' - ? 'data/streaming.png' - : 'data:image/png;base64,' + plist[key].post.mThumbnail.mData.base64, - alt: 'No Thumbnail', - }), + channelThumbnailSrc(plist[key].post) + ? [ + m('img', { + src: channelThumbnailSrc(plist[key].post), + alt: plist[key].post.mMeta.mMsgName || 'Post thumbnail', + onerror: (e) => { + e.target.style.display = 'none'; + if (e.target.nextSibling) e.target.nextSibling.style.display = 'flex'; + }, + }), + m(ChannelFallbackThumbnail, { title: plist[key].post.mMeta.mMsgName, hidden: true }), + ] + : m(ChannelFallbackThumbnail, { title: plist[key].post.mMeta.mMsgName }), m('p', plist[key].post.mMeta.mMsgName), ] ), @@ -665,20 +702,156 @@ function displaycomment() { }; } +/* Modern threaded comment experience for channel posts. */ +const ChannelComments = () => { + let replyTo = null; + let text = ''; + let identity = null; + let submitting = false; + let error = ''; + let showEmojiPicker = false; + const expandedReplies = {}; + + const metaOf = (comment) => (comment && comment.mMeta) || {}; + const idOf = (comment) => metaOf(comment).mMsgId || comment.msgId; + const nameOf = (id) => rs.userList.username(id) || rs.userList.userMap[id] || `${String(id || 'Unknown').slice(0, 10)}…`; + const initials = (name) => String(name || '?').split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join('').toUpperCase(); + const dateOf = (value) => { + const seconds = value && typeof value === 'object' ? value.xint64 : value; + return Number(seconds) ? new Date(Number(seconds) * 1000).toLocaleString() : ''; + }; + + function tree(threadId) { + const nodes = {}; + const roots = []; + Object.keys(Data.Comments[threadId] || {}).forEach((key) => { + const entry = Data.Comments[threadId][key]; + const comment = entry.comment || entry; + if (idOf(comment)) nodes[idOf(comment)] = { comment, children: [] }; + }); + Object.keys(nodes).forEach((key) => { + const node = nodes[key]; + const parent = metaOf(node.comment).mParentId; + if (parent && parent !== threadId && nodes[parent]) nodes[parent].children.push(node); + else roots.push(node); + }); + const chronological = (a, b) => Number(metaOf(a.comment).mPublishTs && (metaOf(a.comment).mPublishTs.xint64 || metaOf(a.comment).mPublishTs)) - Number(metaOf(b.comment).mPublishTs && (metaOf(b.comment).mPublishTs.xint64 || metaOf(b.comment).mPublishTs)); + roots.sort(chronological); + Object.keys(nodes).forEach((key) => nodes[key].children.sort(chronological)); + return roots; + } + + async function submit(vnode) { + const comment = text.trim(); + if (!comment || !identity || submitting) return; + submitting = true; + error = ''; + try { + const res = await rs.rsJsonApiRequest('/rsgxschannels/createCommentV2', { + channelId: vnode.attrs.channelId, + threadId: vnode.attrs.threadId, + comment, + authorId: identity, + parentId: replyTo ? idOf(replyTo) : vnode.attrs.threadId, + }); + if (!res || !res.body || res.body.retval === false) { + error = (res && res.body && res.body.errorMessage) || 'Your comment could not be posted.'; + return; + } + text = ''; + replyTo = null; + await util.updatedisplaychannels(vnode.attrs.channelId); + } catch (submitError) { + console.warn('Channel comment submission failed', submitError); + error = 'Your comment could not be posted. Please try again.'; + } finally { + submitting = false; + m.redraw(); + } + } + + function renderComment(node, vnode) { + const comment = node.comment; + const meta = metaOf(comment); + const id = idOf(comment); + const name = nameOf(meta.mAuthorId); + const votes = (Data.Votes[meta.mThreadId] && Data.Votes[meta.mThreadId][id]) || { upvotes: 0, downvotes: 0 }; + const repliesExpanded = expandedReplies[id] === true; + return m('.board-comment', { key: id }, [ + m('.board-comment-avatar', initials(name)), + m('.board-comment__content', [ + m('.board-comment__header', [ + m('.board-comment__meta', [m('b', name), dateOf(meta.mPublishTs) ? m('span', dateOf(meta.mPublishTs)) : null]), + m('button.board-comment__menu[type=button][aria-label=Comment options]', m('i.fas.fa-ellipsis-v')), + ]), + m('p.board-comment__text', comment.mComment || comment.comment || ''), + m('.board-comment__actions', [ + m('button[type=button]', { disabled: !vnode.attrs.voteIdentity, onclick: () => addvote(util.GXS_VOTE_UP, vnode.attrs.channelId, vnode.attrs.threadId, vnode.attrs.voteIdentity, id) }, [m('i.fas.fa-thumbs-up'), ` ${votes.upvotes || 0}`]), + m('button[type=button]', { disabled: !vnode.attrs.voteIdentity, onclick: () => addvote(util.GXS_VOTE_DOWN, vnode.attrs.channelId, vnode.attrs.threadId, vnode.attrs.voteIdentity, id) }, m('i.fas.fa-thumbs-down')), + m('button[type=button]', { onclick: () => { replyTo = comment; text = ''; error = ''; } }, 'Reply'), + ]), + node.children.length ? m('button.board-comment__replies-toggle[type=button]', { 'aria-expanded': repliesExpanded, onclick: () => { expandedReplies[id] = !repliesExpanded; } }, [`${node.children.length} ${node.children.length === 1 ? 'reply' : 'replies'} `, m('i.fas', { class: repliesExpanded ? 'fa-chevron-up' : 'fa-chevron-down' })]) : null, + node.children.length && repliesExpanded ? m('.board-comment__replies', node.children.map((child) => renderComment(child, vnode))) : null, + ]), + ]); + } + + return { + view: (vnode) => { + const identities = (vnode.attrs.identities || []).filter((id) => Number(id) !== 0); + if (!identity && identities.length) identity = identities[0]; + const comments = tree(vnode.attrs.threadId); + return m('.board-comments.channel-comments', [ + m('.board-comments__heading', [ + m('h3', `${Object.keys(Data.Comments[vnode.attrs.threadId] || {}).length} Comment${Object.keys(Data.Comments[vnode.attrs.threadId] || {}).length === 1 ? '' : 's'}`), + m('span', [m('i.fas.fa-sort-amount-down'), ' Oldest first']), + m('.board-comments__voter', [ + m('label[for=channel-comment-voter]', 'Voter identity'), + m('select#channel-comment-voter', { + value: vnode.attrs.voteIdentity || '', + disabled: identities.length === 0, + onchange: (e) => vnode.attrs.onVoteIdentity(e.target.value), + }, identities.length + ? identities.map((id) => m('option', { value: id }, nameOf(id))) + : m('option', { value: '' }, vnode.attrs.identitiesLoading ? 'Loading identities…' : 'No identity available')), + ]), + ]), + m('.board-comment-composer', [ + m('.board-comment-avatar', initials(nameOf(identity))), + m('.board-comment-composer__body', [ + replyTo ? m('.board-comment-composer__replying', ['Replying to ', m('b', nameOf(metaOf(replyTo).mAuthorId)), m('button[type=button][aria-label=Cancel reply]', { onclick: () => { replyTo = null; text = ''; } }, m('i.fas.fa-times'))]) : null, + identities.length ? m('select.board-comment-composer__identity', { value: identity, onchange: (e) => { identity = e.target.value; } }, identities.map((id) => m('option', { value: id }, nameOf(id)))) : null, + m('textarea.board-comment-composer__input[rows=1][placeholder=Add a comment…]', { value: text, disabled: !identity || submitting, oninput: (e) => { text = e.target.value; }, onkeydown: (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') submit(vnode); } }), + !identity ? m('p.board-comment-composer__hint', vnode.attrs.identitiesLoading ? 'Loading identities…' : 'Create or select an identity to post a comment.') : null, + error ? m('p.board-comment-composer__error', error) : null, + m('.board-comment-composer__actions', [ + m('.board-comment-composer__emoji', { style: { position: 'relative', marginRight: 'auto' } }, [ + m('button[type=button][title=Insert emoji][aria-label=Insert emoji]', { style: { width: '32px', height: '32px', padding: '0', borderRadius: '50%', border: '0', boxShadow: 'none', background: showEmojiPicker ? '#e0f2fe' : 'transparent', color: '#475569', fontSize: '1.15rem' }, onclick: () => { showEmojiPicker = !showEmojiPicker; } }, m('i.fas.fa-smile')), + showEmojiPicker ? m('.board-comment-emoji-popover', { style: { position: 'absolute', zIndex: '20', top: '38px', left: '0', width: '250px', maxHeight: '180px', overflowY: 'auto', padding: '.5rem', display: 'grid', gridTemplateColumns: 'repeat(8, 1fr)', gap: '.2rem', background: '#fff', border: '1px solid #cbd5e1', borderRadius: '8px', boxShadow: '0 8px 20px rgba(0,0,0,.16)' } }, chatEmoji.EMOJI_DATA.Smileys.slice(0, 48).map((emoji) => m('button[type=button]', { style: { width: '28px', height: '28px', padding: '0', border: '0', boxShadow: 'none', background: 'transparent', fontSize: '1.1rem' }, onclick: () => { text += emoji; showEmojiPicker = false; } }, emoji))) : null, + ]), + text || replyTo ? m('button.board-comment-composer__cancel[type=button]', { onclick: () => { text = ''; replyTo = null; error = ''; } }, 'Cancel') : null, + m('button.board-comment-composer__submit[type=button]', { disabled: !text.trim() || !identity || submitting, onclick: () => submit(vnode) }, submitting ? 'Posting…' : 'Comment'), + ]), + ]), + ]), + comments.length ? m('.board-comments__list', comments.map((node) => renderComment(node, vnode))) : m('.board-comments__empty', [m('i.fas.fa-comment'), m('p', 'No comments yet. Start the conversation.')]), + ]); + }, + }; +}; + const PostView = () => { let post = {}; - let topComments = {}; const filesInfo = {}; let voteIdentity; let ownId; + let identitiesLoading = true; + let messageExpanded = false; return { oninit: async (v) => { if (Data.Posts[v.attrs.channelId] && Data.Posts[v.attrs.channelId][v.attrs.msgId]) { post = Data.Posts[v.attrs.channelId][v.attrs.msgId].post; } - if (Data.TopComments[v.attrs.msgId]) { - topComments = Data.TopComments[v.attrs.msgId]; // get all the top level parent comments - } if (post) { post.mFiles.map(async (file) => { const res = await rs.rsJsonApiRequest('/rsfiles/alreadyHaveFile', { @@ -696,10 +869,16 @@ const PostView = () => { } } voteIdentity = ownId[0]; + identitiesLoading = false; }); fileDown.Downloads.loadStatus(); // for retrieving downloading files. }, - view: (v) => [ + view: (v) => { + const message = post.mMsg || ''; + const messageText = String(message).replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); + const hasEmbeddedImage = / 280 || hasEmbeddedImage; + return [ m( 'a[title=Back]', { @@ -713,7 +892,19 @@ const PostView = () => { ), m('.widget__heading', m('h3', post.mMeta.mMsgName)), m('.widget__body', [ - m('p', { style: { whiteSpace: 'normal' } }, m.trust(post.mMsg)), + message ? m('.post-description', [ + m('.post-description__text', { + style: { + maxHeight: messageExpanded ? 'none' : '4.5em', + overflow: 'hidden', + lineHeight: '1.5', + }, + }, m.trust(message)), + hasLongMessage ? m('button.post-description__toggle[type=button]', { + style: { marginTop: '.35rem', padding: '0', border: '0', boxShadow: 'none', background: 'transparent', color: '#0f172a', fontSize: '.85rem', fontWeight: '700' }, + onclick: () => { messageExpanded = !messageExpanded; }, + }, messageExpanded ? 'Show less' : '…more') : null, + ]) : null, m('.file-section', [ m('h3', 'Files(' + post.mAttachmentCount + ')'), m( @@ -722,132 +913,77 @@ const PostView = () => { 'tbody', post.mFiles.map((file) => m('tr', [ - m('td', file.mName), - m('td', rs.formatBytes(file.mSize.xint64)), - m( - 'button', - { - style: { fontSize: '0.9em' }, - onclick: async () => - widget.popupMessage([ - m('p', 'Start Download?'), - m( - 'button', - { - onclick: async () => { - if (filesInfo[file.mHash] && !filesInfo[file.mHash].retval) { - const res = await rs.rsJsonApiRequest('/rsFiles/FileRequest', { - fileName: file.mName, - hash: file.mHash, - flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING, - size: { - xstr64: file.mSize.xstr64, - }, - }); - res.body.retval === false - ? widget.popupMessage([ - m('h3', 'Error'), - m('hr'), - m('p', res.body.errorMessage), - ]) - : widget.popupMessage([ - m('h3', 'Success'), - m('hr'), - m('p', 'Download Started'), - ]); - m.redraw(); - } + m('td.channel-file__name[data-label=File name]', file.mName), + m('td.channel-file__size[data-label=Size]', rs.formatBytes(file.mSize.xint64)), + m('td.channel-file__action[data-label=Download]', [ + m( + 'button', + { + style: { fontSize: '0.9em' }, + onclick: async () => + widget.popupMessage([ + m('p', 'Start Download?'), + m( + 'button', + { + onclick: async () => { + if (filesInfo[file.mHash] && !filesInfo[file.mHash].retval) { + const res = await rs.rsJsonApiRequest('/rsFiles/FileRequest', { + fileName: file.mName, + hash: file.mHash, + flags: util.RS_FILE_REQ_ANONYMOUS_ROUTING, + size: { + xstr64: file.mSize.xstr64, + }, + }); + res.body.retval === false + ? widget.popupMessage([ + m('h3', 'Error'), + m('hr'), + m('p', res.body.errorMessage), + ]) + : widget.popupMessage([ + m('h3', 'Success'), + m('hr'), + m('p', 'Download Started'), + ]); + m.redraw(); + } + }, }, - }, - 'Start Download' - ), - ]), - }, - filesInfo[file.mHash] - ? filesInfo[file.mHash].retval - ? 'Open File' - : ['Download', m('i.fas.fa-download')] - : 'Please Wait...' - ), - fileDown.list[file.mHash] && // using the file from files_util to display download. - m(fileUtil.File, { + 'Start Download' + ), + ]), + }, + filesInfo[file.mHash] + ? filesInfo[file.mHash].retval + ? 'Open File' + : ['Download ', m('i.fas.fa-download')] + : 'Please Wait...' + ), + fileDown.list[file.mHash] && m(fileUtil.File, { info: fileDown.list[file.mHash], direction: 'down', transferred: fileDown.list[file.mHash].transfered.xint64, parts: [], }), + ]), ]) ) ) ), ]), - m('.comments-section', [ - m('h3', 'Comments'), - m('.comments-section__menu', [ - m( - 'button', - { - onclick: () => { - widget.popupMessage( - m(AddComment, { - parent_comment: '', - channelId: v.attrs.channelId, - authorId: ownId, - threadId: v.attrs.msgId, - parentId: v.attrs.msgId, - }) - ); - }, - }, - 'Add Comment' - ), - m('.comments-section__menu-id', [ - m('label[for=idtags', 'Voter ID: '), - m( - 'select[id=idtags]', - { - value: voteIdentity, - onchange: (e) => { - voteIdentity = ownId[e.target.selectedIndex]; - }, - }, - [ - ownId && - ownId.map((o) => - m( - 'option', - { value: o }, - `${rs.userList.userMap[o].toLocaleString()} (${o.slice(0, 8)}...)` - ) - ), - ] - ), - ]), - ]), - ]), - m( - util.CommentsTable, - m( - 'tbody', - Object.keys(topComments).map((key, index) => - Data.Comments[topComments[key].mMeta.mThreadId] && - Data.Comments[topComments[key].mMeta.mThreadId][topComments[key].mMeta.mMsgId] - ? m(displaycomment, { - // calls the recursive function for all the parents. - identity: ownId, - voteIdentity, - commentStruct: - Data.Comments[topComments[key].mMeta.mThreadId][ - topComments[key].mMeta.mMsgId - ], - replyDepth: 0, - }) - : '' - ) - ) - ), + m(ChannelComments, { + channelId: v.attrs.channelId, + threadId: v.attrs.msgId, + identities: ownId, + voteIdentity, + identitiesLoading, + onVoteIdentity: (id) => { voteIdentity = id; }, + }), ]), - ], + ]; + }, }; }; diff --git a/webui-src/app/channels/channels_util.js b/webui-src/app/channels/channels_util.js index 9e272d8..022d94b 100644 --- a/webui-src/app/channels/channels_util.js +++ b/webui-src/app/channels/channels_util.js @@ -173,8 +173,8 @@ const FilesTable = () => { return { oninit: (v) => {}, view: (v) => - m('table.files', [ - m('tr', [m('th', 'File Name'), m('th', 'Size'), m('th', m('i.fas.fa-download'))]), + m('table.files.channel-files', [ + m('thead', m('tr', [m('th', 'File Name'), m('th', 'Size'), m('th', m('i.fas.fa-download'))])), v.children, ]), }; diff --git a/webui-src/app/people/people_util.js b/webui-src/app/people/people_util.js index c14e9a9..6a764ce 100644 --- a/webui-src/app/people/people_util.js +++ b/webui-src/app/people/people_util.js @@ -137,15 +137,21 @@ function sortIds(list) { } async function ownIds(consumer = () => { }, onlySigned = false) { - await rs.rsJsonApiRequest('/rsIdentity/getOwnSignedIds', {}, (owns) => { + try { + const signedResponse = await rs.rsJsonApiRequest('/rsIdentity/getOwnSignedIds', {}); + const signedIds = (signedResponse && signedResponse.body && signedResponse.body.ids) || []; if (onlySigned) { - consumer(sortIds(owns.ids)); - } else { - rs.rsJsonApiRequest('/rsIdentity/getOwnPseudonimousIds', {}, (pseudo) => { - if (pseudo.ids) consumer(sortIds(pseudo.ids.concat(owns.ids))); - }); + consumer(sortIds(signedIds)); + return; } - }); + + const pseudonymousResponse = await rs.rsJsonApiRequest('/rsIdentity/getOwnPseudonimousIds', {}); + const pseudonymousIds = (pseudonymousResponse && pseudonymousResponse.body && pseudonymousResponse.body.ids) || []; + consumer(sortIds(pseudonymousIds.concat(signedIds))); + } catch (error) { + console.warn('Unable to load own identities', error); + consumer([]); + } } const SearchBar = () => { let searchString = ''; diff --git a/webui-src/app/scss/pages/_board.scss b/webui-src/app/scss/pages/_board.scss index b47e628..b94937d 100644 --- a/webui-src/app/scss/pages/_board.scss +++ b/webui-src/app/scss/pages/_board.scss @@ -240,6 +240,8 @@ .board-comments__heading h3 { margin: 0; font-size: 1.15rem; } .board-comments__heading span { color: #64748b; font-size: .8rem; font-weight: 600; } .board-comments__heading i { margin-right: .35rem; } +.board-comments__voter { display: inline-flex; align-items: center; gap: .4rem; margin-left: auto; color: #64748b; font-size: .75rem; font-weight: 600; white-space: nowrap; } +.board-comments__voter select { max-width: 180px; padding: .25rem .4rem; font-size: .78rem; } .board-comment-composer, .board-comment { @@ -355,7 +357,9 @@ .board-comments__empty i { font-size: 1.5rem; } @media (max-width: 560px) { - .board-comments__heading { justify-content: space-between; } + .board-comments__heading { flex-wrap: wrap; justify-content: space-between; gap: .5rem; } + .board-comments__voter { width: 100%; margin-left: 0; } + .board-comments__voter select { flex: 1; max-width: none; } .board-comment-composer, .board-comment { gap: .6rem; } .board-comment-avatar { flex-basis: 32px; width: 32px; height: 32px; font-size: .68rem; } .board-comment__replies { padding-left: .6rem; } @@ -604,6 +608,17 @@ button.board-card__vote-btn, background: linear-gradient(135deg, #e2e8f0 0%, #cbd5e1 100%) !important; } +.board-card__placeholder-content { + display: flex; + flex-direction: column; + align-items: center; + gap: .3rem; + color: #64748b; + font-size: .72rem; + font-weight: 600; +} +.board-card__placeholder-content i { font-size: 1.35rem; } + .board-card--compact .board-card__placeholder-img { width: 24px !important; height: 24px !important; diff --git a/webui-src/app/scss/pages/_channel.scss b/webui-src/app/scss/pages/_channel.scss index 962f99d..0179a89 100644 --- a/webui-src/app/scss/pages/_channel.scss +++ b/webui-src/app/scss/pages/_channel.scss @@ -78,6 +78,116 @@ table { } } } + +.posts-container-card .channel-post__placeholder { + flex: 1 1 auto; + min-height: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: .35rem; + color: #64748b; + background: linear-gradient(135deg, #f8fafc, #dbe5f1); +} +.channel-post__placeholder i { font-size: 1.35rem; color: #64748b; } +.channel-post__placeholder span { font-size: 2rem; font-weight: 700; color: #2563eb; } +.channel-post__placeholder small { font-size: .72rem; font-weight: 600; } + +/* Compact YouTube-style post description with an opt-in expansion control. */ +.post-description { + margin: 1rem 0; + padding: .85rem 1rem; + border-radius: 10px; + background: #f1f5f9; + color: #1e293b; +} +.post-description__text { overflow-wrap: anywhere; line-height: 1.5; } +.post-description__text > :first-child { margin-top: 0; } +.post-description__text > :last-child { margin-bottom: 0; } +.post-description__text--collapsed { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; +} +.post-description__toggle { + margin-top: .35rem; + padding: 0 !important; + border: 0 !important; + box-shadow: none !important; + background: transparent !important; + color: #0f172a !important; + font-size: .85rem !important; + font-weight: 700 !important; +} +.post-description__toggle:hover { color: #2563eb !important; text-decoration: underline; } + +/* Keep image and generated-thumbnail cards the same size instead of stretching + * a sparse grid row to the full height of the Posts panel. */ +.posts-container { align-content: start; grid-auto-rows: 240px; } +.posts-container-card { height: 240px; min-height: 0; overflow: hidden; } +.posts-container-card > img, +.posts-container-card > .channel-post__placeholder { min-height: 0; } + +@media (max-width: 700px) { + .posts-container { grid-auto-rows: 210px; gap: 1rem; } + .posts-container-card { height: 210px; } +} + +/* Channel attachment table -> compact cards on phones. */ +@media (max-width: 700px) { + .file-section { margin-top: 1.25rem; } + + table.channel-files, + table.channel-files tbody, + table.channel-files tr, + table.channel-files td { + display: block; + width: 100% !important; + box-sizing: border-box; + } + + table.channel-files { padding: 0; table-layout: auto; } + table.channel-files thead { display: none; } + + table.channel-files tr { + margin: 0 0 .7rem; + padding: .7rem; + border: 1px solid #dbe3ef; + border-radius: 8px; + background: #fff; + } + + table.channel-files td { + min-width: 0; + padding: .25rem 0; + border: 0; + text-align: left; + } + + table.channel-files td::before { + display: block; + margin-bottom: .1rem; + color: #64748b; + content: attr(data-label); + font-size: .72rem; + font-weight: 700; + text-transform: uppercase; + } + + table.channel-files .channel-file__name { + overflow-wrap: anywhere; + color: #0f172a; + font-weight: 600; + line-height: 1.35; + } + + table.channel-files .channel-file__size { color: #475569; } + table.channel-files .channel-file__action { padding-top: .5rem; } + table.channel-files .channel-file__action > button { min-width: 116px; } + table.channel-files .channel-file__action .file-view { margin-top: .6rem; } +} /* #options{ width: 100px; text-align: center; diff --git a/webui-src/styles.css b/webui-src/styles.css index e49f240..e1e6b1c 100644 --- a/webui-src/styles.css +++ b/webui-src/styles.css @@ -5978,3 +5978,6101 @@ button.red { + +/* ========================================================= + Global Modal & Lightbox Overlay (#popupmessage) + ========================================================= */ + +#popupmessage { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + width: 100vw !important; + height: 100vh !important; + background-color: rgba(15, 23, 42, 0.75) !important; + backdrop-filter: blur(4px) !important; + z-index: 999999 !important; + display: none; + align-items: center !important; + justify-content: center !important; + box-sizing: border-box !important; +} + +.popup { + position: fixed !important; + top: 50% !important; + left: 50% !important; + transform: translate(-50%, -50%) !important; + z-index: 1000000 !important; + max-width: 90vw !important; + max-height: 90vh !important; + display: flex !important; + flex-direction: column !important; + box-sizing: border-box !important; +} + +.popup-content { + position: relative !important; + background: #ffffff !important; + border-radius: 12px !important; + padding: 1rem !important; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2) !important; + max-width: 90vw !important; + max-height: 90vh !important; + overflow: auto !important; + box-sizing: border-box !important; +} + +.popup-content span.close { + position: absolute !important; + top: 0.5rem !important; + right: 0.75rem !important; + font-size: 1.75rem !important; + font-weight: 700 !important; + color: #64748b !important; + cursor: pointer !important; + line-height: 1 !important; + z-index: 10 !important; + transition: color 0.15s ease !important; +} + +.popup-content span.close:hover { + color: #ef4444 !important; +} + +.board-view-container { + display: flex !important; + flex-direction: column !important; + gap: 1rem !important; + width: 100% !important; + max-width: 100% !important; + overflow-x: hidden !important; + padding: 0.5rem 0 !important; + box-sizing: border-box !important; +} + +.board-table { + width: 100% !important; + border-collapse: collapse !important; +} + +.board-table th, +.board-table td { + text-align: left !important; + padding: 0.65rem 0.85rem !important; +} + +/* Toolbar Header Controls */ +.board-toolbar { + display: flex !important; + flex-direction: row !important; + align-items: center !important; + justify-content: space-between !important; + gap: 1rem !important; + width: 100% !important; + padding: 0.5rem 0.85rem !important; + background-color: #ffffff !important; + border: 1px solid #cbd5e1 !important; + border-radius: 8px !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05) !important; + box-sizing: border-box !important; +} + +.board-toolbar__left { + display: flex !important; + flex-direction: row !important; + align-items: center !important; + gap: 0.75rem !important; + flex: 1 !important; + min-width: 0 !important; +} + +.board-toolbar__right { + display: flex !important; + flex-direction: row !important; + align-items: center !important; + gap: 0.65rem !important; + flex-shrink: 0 !important; +} + +.board-toolbar__count-badge { + display: inline-flex !important; + align-items: center !important; + gap: 0.4rem !important; + padding: 0.3rem 0.65rem !important; + background-color: #f1f5f9 !important; + color: #475569 !important; + font-size: 0.825rem !important; + font-weight: 600 !important; + border-radius: 16px !important; + border: 1px solid #e2e8f0 !important; + white-space: nowrap !important; +} + +.board-toolbar__count-badge i { + color: #007bff !important; +} + +.board-toolbar__search { + position: relative !important; + display: flex !important; + align-items: center !important; + flex: 1 !important; + max-width: 380px !important; + min-width: 160px !important; +} + +.board-toolbar__search-icon { + position: absolute !important; + left: 0.75rem !important; + color: #94a3b8 !important; + font-size: 0.85rem !important; + pointer-events: none !important; +} + +.board-toolbar__search-input { + width: 100% !important; + padding: 0.35rem 0.65rem 0.35rem 2.1rem !important; + border: 1px solid #cbd5e1 !important; + border-radius: 6px !important; + font-size: 0.85rem !important; + background-color: #ffffff !important; + color: #1e293b !important; +} + +.board-toolbar__search-input:focus { + outline: none !important; + border-color: #007bff !important; + box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.15) !important; +} + +.board-toolbar__view-toggle { + display: inline-flex !important; + flex-direction: row !important; + align-items: center !important; + background-color: #f1f5f9 !important; + padding: 2px !important; + border-radius: 6px !important; + border: 1px solid #cbd5e1 !important; +} + +.board-toolbar__toggle-btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + gap: 0.35rem !important; + padding: 0.3rem 0.65rem !important; + border: none !important; + background: transparent !important; + color: #64748b !important; + font-size: 0.825rem !important; + font-weight: 500 !important; + border-radius: 4px !important; + cursor: pointer !important; + white-space: nowrap !important; + transition: all 0.2s ease !important; +} + +.board-toolbar__toggle-btn i { + font-size: 0.85rem !important; +} + +.board-toolbar__toggle-btn:hover { + color: #1e293b !important; + background-color: rgba(255, 255, 255, 0.6) !important; +} + +.board-toolbar__toggle-btn--active { + background-color: #007bff !important; + color: #ffffff !important; + font-weight: 600 !important; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important; +} + +.board-toolbar__toggle-btn--active i { + color: #ffffff !important; +} + +@media (max-width: 768px) { + .board-toolbar__toggle-btn span { + display: none !important; + } + .board-toolbar__toggle-btn { + padding: 0.35rem 0.55rem !important; + } +} + +/* YouTube-style discussion thread on a board post. */ +.board-comments { + margin-top: 1.5rem; + max-width: 900px; + color: #0f172a; +} + +.board-comments__heading { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1.25rem; +} + +.board-comments__heading h3 { margin: 0; font-size: 1.15rem; } +.board-comments__heading span { color: #64748b; font-size: .8rem; font-weight: 600; } +.board-comments__heading i { margin-right: .35rem; } +.board-comments__voter { display: inline-flex; align-items: center; gap: .4rem; margin-left: auto; color: #64748b; font-size: .75rem; font-weight: 600; white-space: nowrap; } +.board-comments__voter select { max-width: 180px; padding: .25rem .4rem; font-size: .78rem; } + +.board-comment-composer, +.board-comment { + display: flex; + gap: .8rem; +} + +.board-comment-avatar { + display: flex; + flex: 0 0 38px; + width: 38px; + height: 38px; + align-items: center; + justify-content: center; + border-radius: 50%; + background: linear-gradient(135deg, #2563eb, #7c3aed); + color: #fff; + font-size: .78rem; + font-weight: 700; +} + +.board-comment-composer__body, +.board-comment__content { min-width: 0; flex: 1; } +.board-comment-composer__identity { max-width: 240px; margin-bottom: .45rem; font-size: .8rem; } + +.board-comment-composer__input { + width: 100%; + min-height: 36px; + padding: .45rem 0; + resize: vertical; + border: 0; + border-bottom: 1px solid #94a3b8; + border-radius: 0; + background: transparent; + color: #0f172a; + font: inherit; + line-height: 1.4; + box-sizing: border-box; +} + +.board-comment-composer__input:focus { outline: 0; border-bottom: 2px solid #2563eb; } +.board-comment-composer__input:disabled { cursor: not-allowed; opacity: .6; } + +.board-comment-composer__actions, +.board-comment__actions { + display: flex; + align-items: center; + gap: .45rem; + margin-top: .55rem; +} + +.board-comment-composer__actions { justify-content: flex-end; } +.board-comment-composer__actions button, +.board-comment__actions button, +.board-comment__like, +.board-comment-composer__replying button { + border: 0; + background: transparent; + color: #475569; + cursor: pointer; + font-size: .78rem; + font-weight: 700; +} + +.board-comment-composer__submit { + padding: .45rem .85rem !important; + border-radius: 999px !important; + background: #2563eb !important; + color: #fff !important; +} +.board-comment-composer__submit:disabled { background: #dbe3ef !important; color: #94a3b8 !important; cursor: not-allowed; } +.board-comment-composer__cancel:hover, +.board-comment__actions button:hover { color: #2563eb; } + +.board-comment-composer__replying { display: flex; align-items: center; gap: .25rem; margin-bottom: .35rem; color: #64748b; font-size: .8rem; } +.board-comment-composer__replying button { margin-left: .3rem; } +.board-comment-composer__hint, +.board-comment-composer__error { margin: .4rem 0 0; font-size: .78rem; } +.board-comment-composer__hint { color: #64748b; } +.board-comment-composer__error { color: #dc2626; } + +.board-comments__list { margin-top: 1.8rem; } +.board-comment { margin-top: 1.35rem; } +.board-comment__header { display: flex; align-items: center; justify-content: space-between; min-height: 18px; } +.board-comment__meta { display: flex; align-items: baseline; gap: .55rem; font-size: .8rem; } +.board-comment__meta b { color: #1e293b; } +.board-comment__meta span { color: #64748b; font-size: .75rem; } +.board-comment__menu { width: 28px; height: 28px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: #0f172a; cursor: pointer; opacity: 0; } +.board-comment:hover .board-comment__menu, .board-comment__menu:focus { opacity: 1; } +.board-comment__menu:hover { background: #f1f5f9; } +.board-comment__text { margin: .2rem 0 0; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.45; } +.board-comment__actions { gap: .65rem; margin-top: .35rem; } +.board-comment__actions button { padding: .25rem .2rem; color: #0f172a; } +.board-comment__like { padding: .25rem .35rem; color: #94a3b8; } +.board-comment__actions i { margin-right: .2rem; } +.board-comment__replies-toggle { + position: relative; + margin-top: .3rem; + padding: .25rem .35rem; + border: 0; + background: transparent; + color: #2563eb; + cursor: pointer; + font-size: .78rem; + font-weight: 700; +} +.board-comment__replies-toggle:hover { background: #eff6ff; border-radius: 4px; } +.board-comment__replies-toggle i { margin-left: .15rem; } +.board-comment__replies-toggle::before { content: ''; position: absolute; left: -3.15rem; bottom: .8rem; width: 1.5rem; height: 2.2rem; border-left: 1px solid #e2e8f0; border-bottom: 1px solid #e2e8f0; border-radius: 0 0 0 .75rem; pointer-events: none; } +.board-comment__replies { margin-top: .2rem; padding-left: 1rem; border-left: 2px solid #e2e8f0; } +.board-comments__status, +.board-comments__empty { margin: 2rem 0; color: #64748b; text-align: center; } +.board-comments__empty i { font-size: 1.5rem; } + +@media (max-width: 560px) { + .board-comments__heading { flex-wrap: wrap; justify-content: space-between; gap: .5rem; } + .board-comments__voter { width: 100%; margin-left: 0; } + .board-comments__voter select { flex: 1; max-width: none; } + .board-comment-composer, .board-comment { gap: .6rem; } + .board-comment-avatar { flex-basis: 32px; width: 32px; height: 32px; font-size: .68rem; } + .board-comment__replies { padding-left: .6rem; } + .board-comment__replies-toggle::before { left: -2.65rem; width: 1.2rem; } + .board-card__notes-btn span { display: none; } + .board-card__notes-btn { width: 30px; height: 30px; justify-content: center; padding: 0 !important; } + .board-card__notes-btn i { margin: 0 !important; } +} + +/* Pagination Controls (< 1 - 25 >) on exact same level right next to toggle */ +.board-pagination { + display: inline-flex !important; + flex-direction: row !important; + align-items: center !important; + gap: 0.25rem !important; + background-color: #ffffff !important; + padding: 2px 4px !important; + border-radius: 6px !important; + border: 1px solid #cbd5e1 !important; +} + +.board-pagination__btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + width: 26px !important; + height: 26px !important; + min-width: 26px !important; + min-height: 26px !important; + padding: 0 !important; + margin: 0 !important; + border: 1px solid #cbd5e1 !important; + border-radius: 4px !important; + background: #ffffff !important; + color: #007bff !important; + font-size: 0.825rem !important; + cursor: pointer !important; + transition: all 0.15s ease !important; +} + +.board-pagination__btn i { + font-size: 0.825rem !important; + color: #007bff !important; +} + +.board-pagination__btn:hover:not(:disabled) { + background-color: #007bff !important; + color: #ffffff !important; + border-color: #007bff !important; +} + +.board-pagination__btn:hover:not(:disabled) i { + color: #ffffff !important; +} + +.board-pagination__btn:disabled { + opacity: 0.4 !important; + cursor: not-allowed !important; + color: #94a3b8 !important; + border-color: #e2e8f0 !important; + background-color: #f1f5f9 !important; +} + +.board-pagination__btn:disabled i { + color: #94a3b8 !important; +} + +.board-pagination__label { + font-size: 0.825rem !important; + font-weight: 700 !important; + color: #334155 !important; + padding: 0 0.35rem !important; + white-space: nowrap !important; + user-select: none !important; +} + +/* Bottom Pagination Container */ +.board-view-footer { + display: flex !important; + justify-content: center !important; + align-items: center !important; + padding: 1rem 0 0.5rem 0 !important; + width: 100% !important; +} + +/* Board Grid Layout Modes */ +.board-grid { + display: flex !important; + flex-direction: column !important; + width: 100% !important; + box-sizing: border-box !important; +} + +.board-grid--compact { + display: flex !important; + flex-direction: column !important; + gap: 0.5rem !important; + width: 100% !important; +} + +.board-grid--card { + display: grid !important; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)) !important; + gap: 1.25rem !important; + width: 100% !important; +} + +.board-grid__empty { + display: flex !important; + flex-direction: column !important; + align-items: center !important; + justify-content: center !important; + padding: 4rem 2rem !important; + text-align: center !important; + background: #ffffff !important; + border: 2px dashed #cbd5e1 !important; + border-radius: 12px !important; + width: 100% !important; + box-sizing: border-box !important; +} + +.board-grid__empty-icon { + font-size: 3rem !important; + color: #cbd5e1 !important; + margin-bottom: 1rem !important; +} + +.board-grid__empty-title { + font-size: 1.15rem !important; + font-weight: 600 !important; + color: #475569 !important; + margin: 0 0 0.5rem 0 !important; +} + +.board-grid__empty-desc { + font-size: 0.9rem !important; + color: #94a3b8 !important; + margin: 0 !important; +} + +/* =================================================== + COMPACT VIEW MODE: FULL-WIDTH HORIZONTAL ROW BANNER + (Identical to Retroshare C++ Qt GUI Posted design) + =================================================== */ +.board-card { + box-sizing: border-box !important; +} + +.board-card--compact { + display: flex !important; + flex-direction: row !important; + align-items: center !important; + width: 100% !important; + min-height: 70px !important; + padding: 0.45rem 0.75rem !important; + background-color: #eef2f5 !important; + border: 1px solid #d1d5db !important; + border-radius: 4px !important; + gap: 0.75rem !important; + box-sizing: border-box !important; + margin-bottom: 0.35rem !important; +} + +.board-card--compact:hover { + background-color: #e2e8f0 !important; + border-color: #9ca3af !important; +} + +/* Vote Pill (Matching Comments Button style with normal-sized icons) */ +.board-card__vote-pill { + display: inline-flex !important; + align-items: center !important; + gap: 0.4rem !important; + padding: 0.2rem 0.55rem !important; + background-color: #f1f5f9 !important; + border: 1px solid #cbd5e1 !important; + border-radius: 20px !important; + box-sizing: border-box !important; +} + +button.board-card__vote-btn, +.board-card__vote-pill .board-card__vote-btn { + background: transparent !important; + border: none !important; + box-shadow: none !important; + padding: 0.1rem 0.2rem !important; + margin: 0 !important; + cursor: pointer !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + line-height: 1 !important; + border-radius: 4px !important; + transition: background-color 0.15s ease !important; + outline: none !important; +} + +.board-card__vote-pill .board-card__vote-btn:hover { + background-color: #e2e8f0 !important; + box-shadow: none !important; +} + +.board-card__vote-pill .board-card__vote-btn--up i { + color: #16a34a !important; + font-size: 1.15rem !important; +} + +.board-card__vote-pill .board-card__vote-btn--down i { + color: #dc2626 !important; + font-size: 1.15rem !important; +} + +.board-card__vote-pill .board-card__vote-score { + font-size: 0.9rem !important; + font-weight: 700 !important; + color: #1e293b !important; + padding: 0 0.15rem !important; + line-height: 1 !important; + min-width: 1rem !important; + text-align: center !important; +} + +/* Thumbnail Image Container */ +.board-card--compact .board-card__image-container { + width: 110px !important; + height: 62px !important; + flex-shrink: 0 !important; + border-radius: 4px !important; + overflow: hidden !important; + background-color: #cbd5e1 !important; +} + +.board-card--compact .board-card__image { + width: 100% !important; + height: 100% !important; + object-fit: cover !important; + display: block !important; +} + +.board-card--compact .board-card__placeholder-wrapper { + width: 100% !important; + height: 100% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + background: linear-gradient(135deg, #e2e8f0 0%, #cbd5e1 100%) !important; +} + +.board-card__placeholder-content { + display: flex; + flex-direction: column; + align-items: center; + gap: .3rem; + color: #64748b; + font-size: .72rem; + font-weight: 600; +} +.board-card__placeholder-content i { font-size: 1.35rem; } + +.board-card--compact .board-card__placeholder-img { + width: 24px !important; + height: 24px !important; + color: #64748b !important; +} + +/* Main Details Section */ +.board-card--compact .board-card__content { + display: flex !important; + flex-direction: column !important; + justify-content: center !important; + flex: 1 !important; + min-width: 0 !important; + padding: 0 !important; +} + +/* Title: Blue bold underlined italicized link (Matches Qt GUI) */ +.board-card--compact .board-card__title { + font-size: 1.05rem !important; + font-weight: 700 !important; + color: #2255aa !important; + text-decoration: underline !important; + font-style: italic !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + margin: 0 0 0.15rem 0 !important; + cursor: pointer !important; +} + +.board-card--compact .board-card__title:hover { + color: #1d4ed8 !important; +} + +/* Meta line: Posted by */ +.board-card--compact .board-card__meta { + font-size: 0.8rem !important; + color: #475569 !important; + margin-bottom: 0.2rem !important; +} + +.board-card--compact .board-card__meta b { + color: #1e293b !important; +} + +/* Footer: Comment button matching Qt GUI */ +.board-card--compact .board-card__footer { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; + padding: 0 !important; + border: none !important; + margin: 0 !important; +} + +button.board-card__comments-btn, +.board-card__comments-btn, +.board-card--compact .board-card__comments-btn { + display: inline-flex !important; + align-items: center !important; + gap: 0.35rem !important; + padding: 0.2rem 0.5rem !important; + background: transparent !important; + border: none !important; + box-shadow: none !important; + color: #64748b !important; + font-size: 0.85rem !important; + font-weight: 500 !important; + cursor: pointer !important; + outline: none !important; +} + +button.board-card__comments-btn:hover, +.board-card__comments-btn:hover, +.board-card--compact .board-card__comments-btn:hover { + color: #007bff !important; + text-decoration: underline !important; + box-shadow: none !important; +} + +button.board-card__notes-btn, +.board-card__notes-btn { + display: inline-flex !important; + align-items: center !important; + gap: 0.35rem !important; + padding: 0.2rem 0.5rem !important; + background: transparent !important; + border: none !important; + box-shadow: none !important; + color: #64748b !important; + font-size: 0.85rem !important; + font-weight: 500 !important; + cursor: pointer !important; +} + +.board-card__notes-btn:hover { color: #007bff !important; text-decoration: underline !important; } + +/* =================================================== + CARD VIEW MODE: ELEVATED GRID CARD + =================================================== */ +.board-card--card { + display: flex !important; + flex-direction: column !important; + background-color: #ffffff !important; + border: 1px solid #e2e8f0 !important; + border-radius: 10px !important; + overflow: hidden !important; + transition: transform 0.2s ease, box-shadow 0.2s ease !important; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04) !important; +} + +.board-card--card:hover { + transform: translateY(-3px) !important; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08) !important; + border-color: #cbd5e1 !important; +} + +.board-card--card .board-card__vote-col { + display: none !important; +} + +.board-card--card .board-card__image-container { + width: 100% !important; + height: 170px !important; + overflow: hidden !important; + background-color: #f1f5f9 !important; +} + +.board-card--card .board-card__image { + width: 100% !important; + height: 100% !important; + object-fit: cover !important; + display: block !important; +} + +.board-card--card .board-card__placeholder-wrapper { + width: 100% !important; + height: 100% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%) !important; +} + +.board-card--card .board-card__placeholder-img { + width: 36px !important; + height: 36px !important; + color: #94a3b8 !important; +} + +.board-card--card .board-card__content { + display: flex !important; + flex-direction: column !important; + flex: 1 !important; + padding: 1rem !important; +} + +.board-card--card .board-card__title { + font-size: 1.05rem !important; + font-weight: 700 !important; + color: #0f172a !important; + margin: 0 0 0.4rem 0 !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + cursor: pointer !important; +} + +.board-card--card .board-card__title:hover { + color: #007bff !important; +} + +.board-card--card .board-card__meta { + font-size: 0.8rem !important; + color: #64748b !important; + margin-bottom: 0.5rem !important; +} + +.board-card--card .board-card__notes-wrapper { + display: flex !important; + flex-direction: column !important; + gap: 0.35rem !important; + margin-bottom: 0.75rem !important; +} + +.board-card--card .board-card__notes { + font-size: 0.85rem !important; + line-height: 1.45 !important; + color: #475569 !important; + background-color: #f8fafc !important; + border-left: 3px solid #cbd5e1 !important; + padding: 0.4rem 0.6rem !important; + word-break: break-word !important; +} + +.board-card--card .board-card__notes--clamped { + display: -webkit-box !important; + -webkit-line-clamp: 3 !important; + -webkit-box-orient: vertical !important; + overflow: hidden !important; + white-space: pre-line !important; +} + +.board-card--card .board-card__notes--expanded { + display: block !important; + white-space: pre-line !important; +} + +.board-card--card .board-card__notes-toggle { + align-self: flex-start !important; + background: none !important; + border: none !important; + padding: 0.1rem 0.3rem !important; + color: #007bff !important; + font-size: 0.775rem !important; + font-weight: 600 !important; + cursor: pointer !important; +} + +.board-card--card .board-card__notes-toggle:hover { + text-decoration: underline !important; +} + +.board-card--card .board-card__footer { + display: flex !important; + align-items: center !important; + justify-content: flex-end !important; + margin-top: auto !important; + padding-top: 0.65rem !important; + border-top: 1px solid #f1f5f9 !important; +} + +.board-card--card .board-card__comments-btn { + display: inline-flex !important; + align-items: center !important; + gap: 0.4rem !important; + padding: 0.3rem 0.6rem !important; + background-color: #f1f5f9 !important; + color: #475569 !important; + border: 1px solid #e2e8f0 !important; + border-radius: 6px !important; + font-size: 0.8rem !important; + font-weight: 500 !important; + cursor: pointer !important; +} + +.board-card--card .board-card__comments-btn:hover { + background-color: #007bff !important; + color: #ffffff !important; + border-color: #007bff !important; +} + +.board-card--card .board-card__comments-btn:hover i { + color: #ffffff !important; +} + +.board-card--card .board-card__notes-btn { + display: inline-flex !important; + align-items: center !important; + gap: 0.4rem !important; + padding: 0.3rem 0.6rem !important; + background-color: #f8fafc !important; + color: #475569 !important; + border: 1px solid #e2e8f0 !important; + border-radius: 6px !important; + font-size: 0.8rem !important; + font-weight: 500 !important; +} + +.board-card--card .board-card__notes-btn:hover { background-color: #e0f2fe !important; color: #0369a1 !important; border-color: #7dd3fc !important; text-decoration: none !important; } + +.board-notes-dialog { min-width: min(560px, 75vw); max-width: 75vw; } +.board-notes-dialog h3 { margin: 0 2rem .8rem 0; color: #0f172a; } +.board-notes-dialog__label { margin: 0 0 .35rem; color: #64748b; font-size: .78rem; font-weight: 700; text-transform: uppercase; } +.board-notes-dialog__content { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: #1e293b; line-height: 1.55; } + +/* ========================================================= + PhotoView Lightbox Modal Styles (Matching Qt GUI PhotoView) + ========================================================= */ + +/* Fullscreen overlay element appended to body by openPhotoModal() */ +#photo-view-overlay { + display: none; + position: fixed; + inset: 0; + z-index: 999999; + background-color: rgba(0, 0, 0, 0.85); + align-items: center; + justify-content: center; +} + +.photo-view-dialog { + background-color: #f8fafc !important; + border-radius: 8px !important; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5) !important; + display: flex !important; + flex-direction: column !important; + max-width: 90vw !important; + max-height: 90vh !important; + width: 820px !important; + overflow: hidden !important; + position: relative !important; + z-index: 1 !important; +} + +.photo-view-header { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + padding: 0.75rem 1rem !important; + background-color: #ffffff !important; + border-bottom: 1px solid #e2e8f0 !important; +} + +.photo-view-title { + font-size: 1.05rem !important; + font-style: italic !important; + font-weight: 700 !important; + color: #1e293b !important; + margin: 0 !important; + flex: 1 !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; +} + +.photo-view-close-btn { + background: none !important; + border: none !important; + font-size: 1.6rem !important; + line-height: 1 !important; + color: #64748b !important; + cursor: pointer !important; + padding: 0 0.4rem !important; +} + +.photo-view-close-btn:hover { + color: #ef4444 !important; +} + +.photo-view-body { + display: flex !important; + flex-direction: row !important; + align-items: stretch !important; + background-color: #0f172a !important; + flex: 1 !important; + min-height: 380px !important; + max-height: 68vh !important; + overflow: hidden !important; +} + +/* Left/right nav columns (fixed 56px wide, vertically center the button) */ +.photo-view-nav-col { + width: 56px !important; + flex-shrink: 0 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + background-color: rgba(0, 0, 0, 0.25) !important; +} + +/* Image wrapper fills remaining space */ +.photo-view-img-wrap { + flex: 1 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + min-width: 0 !important; + padding: 0.75rem !important; +} + +.photo-view-img { + max-width: 100% !important; + max-height: 65vh !important; + object-fit: contain !important; + display: block !important; + border-radius: 4px !important; +} + +.photo-view-no-img { + color: #94a3b8 !important; + font-size: 0.95rem !important; +} + +/* Nav button — static in flex column, no absolute positioning */ +.photo-view-nav-btn { + width: 38px !important; + height: 38px !important; + background-color: #ffffff !important; + border: 1px solid #cbd5e1 !important; + border-radius: 6px !important; + color: #007bff !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + cursor: pointer !important; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3) !important; + transition: all 0.15s ease !important; + flex-shrink: 0 !important; +} + +.photo-view-nav-btn i { + font-size: 1rem !important; + color: #007bff !important; +} + +.photo-view-nav-btn:hover { + background-color: #007bff !important; + color: #ffffff !important; + border-color: #007bff !important; +} + +.photo-view-nav-btn:hover i { + color: #ffffff !important; +} + +.photo-view-footer { + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + padding: 0.75rem 1rem !important; + background-color: #ffffff !important; + border-top: 1px solid #e2e8f0 !important; +} + +.photo-view-meta { + font-size: 0.875rem !important; + color: #475569 !important; +} + +.photo-view-meta b { + color: #0f172a !important; +} + +/* Responsive: Hide board header description on mobile screens */ +@media (max-width: 768px) { + .media-item__desc { + display: none !important; + } + .media-item__details { + flex-basis: 100% !important; + width: 100% !important; + } +} + + + +@use '../abstracts' as *; + +.file-section { + margin-top: 2rem; + @include flex(column); +} + +.comments-section { + margin-top: 2rem; + @include flex($justify: space-between); + &__menu { + @include flex($gap: 1rem); + &-id { + @include flex($gap: 0.25rem, $align: center); + } + } +} + +#toggleunsub { + position: relative; + background: gray; +} + +/* subject */ +table.channels { + & th:nth-child(1) { + width: 50%; + text-align: start; + } + /* subject */ + & td:nth-child(1) { + text-align: start; + /* Truncate text with '...' */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + & tr:hover { + background-color: $light-color; + cursor: pointer; + } + & tr.hidden { + display: none; + } +} + +/* subject */ +table { + padding: 0.5rem; + &.comments { + border: 1px solid #eee; + & th { + height: 40px; + &:nth-child(1) { + width: 2%; + } + &:nth-child(2) { + width: 40%; + } + } + & td { + word-wrap: break-word; + &:nth-child(2) { + text-align: start; + } + } + } + &.files { + & th:first-child { + text-align: start; + width: 60%; + } + & tr td:first-child { + text-align: start; + } + & td { + word-wrap: break-word; + } + } +} + +.posts-container-card .channel-post__placeholder { + flex: 1 1 auto; + min-height: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: .35rem; + color: #64748b; + background: linear-gradient(135deg, #f8fafc, #dbe5f1); +} +.channel-post__placeholder i { font-size: 1.35rem; color: #64748b; } +.channel-post__placeholder span { font-size: 2rem; font-weight: 700; color: #2563eb; } +.channel-post__placeholder small { font-size: .72rem; font-weight: 600; } + +/* Compact YouTube-style post description with an opt-in expansion control. */ +.post-description { + margin: 1rem 0; + padding: .85rem 1rem; + border-radius: 10px; + background: #f1f5f9; + color: #1e293b; +} +.post-description__text { overflow-wrap: anywhere; line-height: 1.5; } +.post-description__text > :first-child { margin-top: 0; } +.post-description__text > :last-child { margin-bottom: 0; } +.post-description__text--collapsed { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; +} +.post-description__toggle { + margin-top: .35rem; + padding: 0 !important; + border: 0 !important; + box-shadow: none !important; + background: transparent !important; + color: #0f172a !important; + font-size: .85rem !important; + font-weight: 700 !important; +} +.post-description__toggle:hover { color: #2563eb !important; text-decoration: underline; } + +/* Keep image and generated-thumbnail cards the same size instead of stretching + * a sparse grid row to the full height of the Posts panel. */ +.posts-container { align-content: start; grid-auto-rows: 240px; } +.posts-container-card { height: 240px; min-height: 0; overflow: hidden; } +.posts-container-card > img, +.posts-container-card > .channel-post__placeholder { min-height: 0; } + +@media (max-width: 700px) { + .posts-container { grid-auto-rows: 210px; gap: 1rem; } + .posts-container-card { height: 210px; } +} + +/* Channel attachment table -> compact cards on phones. */ +@media (max-width: 700px) { + .file-section { margin-top: 1.25rem; } + + table.channel-files, + table.channel-files tbody, + table.channel-files tr, + table.channel-files td { + display: block; + width: 100% !important; + box-sizing: border-box; + } + + table.channel-files { padding: 0; table-layout: auto; } + table.channel-files thead { display: none; } + + table.channel-files tr { + margin: 0 0 .7rem; + padding: .7rem; + border: 1px solid #dbe3ef; + border-radius: 8px; + background: #fff; + } + + table.channel-files td { + min-width: 0; + padding: .25rem 0; + border: 0; + text-align: left; + } + + table.channel-files td::before { + display: block; + margin-bottom: .1rem; + color: #64748b; + content: attr(data-label); + font-size: .72rem; + font-weight: 700; + text-transform: uppercase; + } + + table.channel-files .channel-file__name { + overflow-wrap: anywhere; + color: #0f172a; + font-weight: 600; + line-height: 1.35; + } + + table.channel-files .channel-file__size { color: #475569; } + table.channel-files .channel-file__action { padding-top: .5rem; } + table.channel-files .channel-file__action > button { min-width: 116px; } + table.channel-files .channel-file__action .file-view { margin-top: .6rem; } +} +/* #options{ + width: 100px; + text-align: center; + font-size: medium; + margin-left: 20px; + height: 40px; +} */ +#mtags { + width: 160px; + text-align: center; + font-size: medium; + margin-left: 10px; + height: 40px; +} + + + +@use '../abstracts' as *; + +.lobby { + margin: 10px; + border: 1px solid #aaa; + border-radius: 20px; +} + +.lobby .mainname { + margin: 20px; + font-weight: 100; + font-size: 1.2em; +} + +.topic { + color: #666; +} + +.lobby>.topic { + font-size: 0.95em; + margin-left: 25px; + margin-bottom: 5px; +} + +.lefttitle { + margin-top: 15px; + margin-bottom: 0; + font-weight: 100; + font-size: 1.2em; +} + +.leftname { + margin-top: 5px; + margin-bottom: 5px; + padding: 5px; + font-weight: 100; + font-size: 1em; +} + +.leftlobby>.topic { + font-size: 0.75em; + margin-left: 15px; + margin-bottom: 5px; +} + +.subscribed, +.public { + cursor: pointer; +} + +.leftlobby { + border: 1px solid #aaa; + border-radius: 10px; + margin-top: 5px; + background-color: white; +} + +.leftlobby.selected-lobby, +.selectedidentity { + color: white; + background-color: #3ba4d7; +} + +.rightbar { + position: absolute; + width: 185px; + background-color: white; + overflow: auto; + top: 130px; + bottom: 15px; + right: 15px; +} + +.user { + padding: 5px; +} + +.lobbyName { + padding: 15px; + margin-top: 2rem; +} + +.lobbies { + position: absolute; + width: 185px; + left: 165px; + bottom: 15px; + top: 130px; + overflow: auto; +} + +.messages, +.setup { + position: absolute; + background-color: white; + top: 130px; + left: 360px; + right: 215px; + overflow: auto; +} + +.messages { + bottom: 115px; +} + +.messagetext { + white-space: break-spaces; + margin-right: 5px; +} + +.message>* { + margin-left: 5px; +} + +.username { + color: darkgreen; + font-weight: bolder; +} + +.chatMessage { + position: absolute; + background-color: white; + height: 85px; + bottom: 15px; + right: 215px; + left: 360px; +} + +textarea.chatMsg { + height: 100%; + width: 100%; +} + +.chatatchar { + margin-left: 0.2em; + margin-right: 0.2em; + color: silver; +} + +.setupicon { + margin-left: 1em; + cursor: pointer; +} + +.leaveicon { + margin-left: 1em; + cursor: pointer; + color: #d40000; +} + +.selectidentity { + margin: 15px; + font-size: 1.2em; +} + +.setup>.identity { + cursor: pointer; +} + +.setup { + bottom: 15px; +} + +.createDistantChat { + margin-top: 1em; +} + +.no-lobbies { + + .messages, + .chatMessage, + .setup { + left: 165px; + } +} + +/* CHAT ROOM (Single Chat) - Desktop Grid Layout */ +@media (min-width: 900px) { + .node-panel.chat-room { + display: grid !important; + grid-template-columns: 250px 1fr 200px !important; + /* Lobbies, Chat, Users */ + grid-template-rows: auto 1fr auto !important; + /* Header, Messages, Input */ + grid-template-areas: + "lobbies header rightbar" + "lobbies messages rightbar" + "lobbies input rightbar" !important; + padding: 0 !important; + height: 100% !important; + } + + .node-panel.chat-room .lobbyName { + grid-area: header; + padding: 10px; + border-bottom: 1px solid #eee; + margin: 0; + z-index: 10; + background: white; + } + + .node-panel.chat-room .lobbies { + grid-area: lobbies; + position: static !important; + width: auto !important; + height: auto !important; + border-right: 1px solid #ccc; + overflow-y: auto; + display: block !important; + top: auto !important; + bottom: auto !important; + left: auto !important; + } + + .node-panel.chat-room .messages { + grid-area: messages; + position: static !important; + width: auto !important; + height: auto !important; + overflow-y: auto; + padding: 10px; + left: auto !important; + right: auto !important; + top: auto !important; + bottom: auto !important; + margin: 0 !important; + } + + .node-panel.chat-room .rightbar { + grid-area: rightbar; + position: static !important; + width: auto !important; + border-left: 1px solid #ccc; + overflow-y: auto; + display: block !important; + } + + .node-panel.chat-room .chatMessage { + grid-area: input; + position: static !important; + width: auto !important; + height: auto !important; + border-top: 1px solid #eee; + left: auto !important; + right: auto !important; + bottom: auto !important; + flex: 0 0 auto; + padding: 10px !important; + background: white; + z-index: 10; + } +} + +/* Mobile Overrides - Ensure Flex Column */ +@media (max-width: 899px) { + .node-panel.chat-room { + display: flex !important; + flex-direction: column !important; + height: 100% !important; + position: relative !important; + } + + .node-panel.chat-room .lobbyName { + flex: 0 0 auto; + } + + .node-panel.chat-room .messages { + flex: 1 !important; + overflow-y: auto !important; + position: relative !important; + top: 0 !important; + bottom: 0 !important; + left: 0 !important; + right: 0 !important; + width: 100% !important; + height: auto !important; + margin: 0 !important; + } + + .node-panel.chat-room .chatMessage { + flex: 0 0 auto !important; + position: relative !important; + bottom: 0 !important; + left: 0 !important; + right: 0 !important; + width: 100% !important; + height: auto !important; + z-index: 100; + } + + .node-panel.chat-room .rightbar, + .node-panel.chat-room .lobbies { + display: none !important; + position: fixed !important; + top: 60px !important; + bottom: 0 !important; + width: 80% !important; + background: white !important; + z-index: 200 !important; + box-shadow: 2px 0 10px rgba(0, 0, 0, 0.2) !important; + } + + .node-panel.chat-room.show-lobbies .lobbies { + display: block !important; + left: 0 !important; + } + + .node-panel.chat-room.show-users .rightbar { + display: block !important; + right: 0 !important; + } + + .chat-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 150; + } + + .show-lobbies .chat-overlay, + .show-users .chat-overlay { + display: block; + } + + /* Mobile Icons in Header */ + .mobile-menu-icons { + display: flex; + gap: 15px; + font-size: 1.2rem; + } + + .mobile-menu-icons i { + cursor: pointer; + padding: 5px; + } +} + +@media (min-width: 900px) { + .mobile-menu-icons { + display: none; + } +} + +/* ===================================================== + CHAT HUB - Two-Pane Layout (matching Network page) + ===================================================== */ + +.chat-hub-container { + display: flex; + height: 100%; + width: 100%; + overflow: hidden; + background-color: #f1f5f9; +} + +.chat-hub-left-pane { + width: 320px; + min-width: 300px; + max-width: 350px; + border-right: 1px solid #cbd5e1; + display: flex; + flex-direction: column; + background: #ffffff; + box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05); +} + +.chat-own-profile-card { + padding: 0.85rem 1.25rem !important; + border-bottom: 1px solid #e2e8f0 !important; + background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important; + display: flex !important; + align-items: center !important; + justify-content: space-between !important; + gap: 0.75rem !important; + + .profile-header { + display: flex !important; + align-items: center !important; + gap: 0.75rem !important; + flex: 1 !important; + min-width: 0 !important; + } + + .chat-create-lobby-btn { + display: flex !important; + align-items: center !important; + gap: 0.35rem !important; + padding: 0.4rem 0.85rem !important; + font-size: 0.85rem !important; + font-weight: 600 !important; + border-radius: 0.375rem !important; + cursor: pointer !important; + flex-shrink: 0 !important; + white-space: nowrap !important; + } + + .profile-info { + display: flex; + flex-direction: column; + flex: 1; + overflow: hidden; + + .profile-name { + font-weight: 700; + color: #1e293b; + font-size: 1.1rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .profile-status { + font-size: 0.85rem; + color: #10b981; + font-weight: 500; + display: flex; + align-items: center; + gap: 0.35rem; + + &::before { + content: ''; + display: inline-block; + width: 8px; + height: 8px; + background-color: #10b981; + border-radius: 50%; + } + } + } +} + +.chat-rooms-list-container { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + + .searchbar-container { + padding: 0.75rem 1rem; + border-bottom: 1px solid #e2e8f0; + + input.searchbar { + width: 100%; + padding: 0.5rem 0.75rem; + font-size: 0.9rem; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + background-color: #f8fafc; + outline: none; + transition: all 0.2s; + + &:focus { + background-color: #ffffff; + border-color: #3ba4d7; + box-shadow: 0 0 0 3px rgba(59, 164, 215, 0.15); + } + } + } + + .rooms-scroll { + flex: 1; + overflow-y: auto; + padding: 0.5rem 0; + } +} + +.rooms-section-title { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem 0.375rem; + font-size: 0.75rem; + font-weight: 700; + color: #64748b; + text-transform: uppercase; + letter-spacing: 0.05em; + + i { + font-size: 0.7rem; + color: #94a3b8; + } +} + +.chat-room-list-item { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1rem; + margin: 0.125rem 0.5rem; + border-radius: 0.5rem; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background-color: #f1f5f9; + } + + &.selected { + background-color: #e0f2fe; + + .room-name { + color: #0369a1; + font-weight: 600; + } + } + + .room-icon { + flex-shrink: 0; + width: 36px; + height: 36px; + border-radius: 0.5rem; + background: linear-gradient(135deg, #3ba4d7, #0ea5e9); + display: flex; + align-items: center; + justify-content: center; + color: #ffffff; + font-size: 1.35rem; + } + + &.public-room .room-icon { + background: linear-gradient(135deg, #10b981, #059669); + } + + .room-meta { + flex: 1; + min-width: 0; + + .room-name { + font-size: 0.95rem; + color: #334155; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + transition: color 0.2s; + } + + .room-topic { + font-size: 0.8rem; + color: #94a3b8; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .room-badge { + flex-shrink: 0; + min-width: 24px; + height: 24px; + border-radius: 12px; + background-color: #e2e8f0; + color: #475569; + font-size: 0.75rem; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + padding: 0 0.375rem; + } +} + +.chat-hub-right-pane { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + background-color: #f8fafc; +} + +.chat-pane-placeholder { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: #94a3b8; + gap: 1rem; + padding: 2rem; + text-align: center; + + i { + font-size: 4rem; + color: #cbd5e1; + } + + p { + font-size: 1.1rem; + max-width: 400px; + } +} + +.chat-hub-tab-content { + flex: 1; + overflow-y: auto; + padding: 1.5rem; +} + +.chat-room-detail-view { + display: flex; + flex-direction: column; + gap: 1.5rem; + + .detail-header { + display: flex; + align-items: flex-start; + gap: 1.5rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid #e2e8f0; + flex-wrap: wrap; + + .detail-title { + flex: 1; + min-width: 200px; + + h2 { + font-size: 1.75rem; + font-weight: 800; + color: #1e293b; + margin-bottom: 0.25rem; + } + + .detail-subtitle { + font-size: 0.9rem; + color: #64748b; + display: flex; + align-items: center; + gap: 0.5rem; + } + } + + .detail-actions { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + + button { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + font-size: 0.9rem; + } + } + } + + .detail-section { + background-color: #ffffff; + border-radius: 0.5rem; + border: 1px solid #e2e8f0; + padding: 1.25rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02); + + h3 { + font-size: 1.1rem; + font-weight: 700; + color: #334155; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #f1f5f9; + } + + .info-grid { + display: grid; + grid-template-columns: 130px 1fr; + row-gap: 0.75rem; + font-size: 0.9rem; + + .info-label { + font-weight: 600; + color: #64748b; + } + + .info-value { + color: #1e293b; + word-break: break-all; + } + } + } +} + +.participants-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 0.5rem; +} + +.participant-card { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + background-color: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 0.375rem; + + .participant-name { + font-size: 0.875rem; + color: #334155; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.no-participants { + color: #94a3b8; + font-size: 0.9rem; + font-style: italic; +} + +.detail-actions-footer { + display: flex; + gap: 0.75rem; + + button { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + font-size: 0.9rem; + } +} + +.join-description { + color: #64748b; + font-size: 0.9rem; + margin-bottom: 1rem; +} + +.identities-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 0.75rem; +} + +.identity-card { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + background-color: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 0.5rem; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background-color: #e0f2fe; + border-color: #3ba4d7; + } + + .identity-name { + font-size: 0.95rem; + font-weight: 600; + color: #334155; + } + + i { + color: #3ba4d7; + font-size: 0.9rem; + } +} + +.no-rooms { + padding: 1rem; + color: #94a3b8; + text-align: center; + font-style: italic; +} + +/* Chat Hub Responsive - Mobile */ +@media (max-width: 899px) { + .chat-hub-container { + flex-direction: column; + } + + .chat-hub-left-pane { + width: 100%; + min-width: 0; + max-width: none; + max-height: 45%; + border-right: none; + border-bottom: 1px solid #cbd5e1; + } + + .chat-hub-right-pane { + flex: 1; + min-height: 0; + } +} + +/* ===================================================== + CHAT HUB - Right Pane Conversation & Tabs Styling + ===================================================== */ + +.chat-hub-header-bar { + padding: 0.75rem 1.5rem; + background-color: #ffffff; + border-bottom: 1px solid #e2e8f0; + display: flex; + align-items: center; + justify-content: space-between; + height: 65px; + flex-shrink: 0; +} + +.chat-hub-header-bar .chat-header-info { + display: flex; + flex-direction: column; + overflow: hidden; +} + +.chat-hub-header-bar .chat-header-info .chat-header-name { + font-size: 1.15rem; + font-weight: 800; + color: #1e293b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.chat-hub-header-bar .chat-header-info .chat-header-topic { + font-size: 0.85rem; + color: #64748b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-top: 0.125rem; +} + +.chat-hub-header-bar .chat-header-actions { + display: flex; + gap: 0.5rem; +} + +.chat-hub-header-bar .chat-header-actions button { + display: flex; + align-items: center; + gap: 0.35rem; + padding: 0.375rem 0.75rem; + font-size: 0.85rem; +} + +.chat-hub-tabs-container { + background-color: #ffffff; + border-bottom: 1px solid #cbd5e1; + padding: 0.5rem 1.5rem 0; +} + +.chat-hub-tabs { + display: flex; + gap: 0.5rem; +} + +.chat-hub-tabs .tab-btn { + padding: 0.625rem 1.25rem; + font-size: 0.95rem; + font-weight: 600; + color: #64748b; + background: transparent; + border: none; + border-radius: 0.375rem 0.375rem 0 0; + border-bottom: 3px solid transparent; + cursor: pointer; + box-shadow: none; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.chat-hub-tabs .tab-btn:hover { + color: #334155; + background-color: #f1f5f9; +} + +.chat-hub-tabs .tab-btn.active { + color: #3ba4d7; + border-bottom-color: #3ba4d7; + background-color: transparent; +} + +.chat-hub-tab-content { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + background-color: #f8fafc; +} + +.chat-hub-conversation-layout { + display: flex; + flex-direction: row; + height: 100%; + width: 100%; + overflow: hidden; +} + +.chat-hub-conversation-main { + display: flex; + flex-direction: column; + flex: 1; + height: 100%; + overflow: hidden; +} + +.chat-hub-rightbar { + width: 200px; + border-left: 1px solid #cbd5e1; + background-color: #ffffff; + display: flex; + flex-direction: column; + flex-shrink: 0; +} + +.chat-hub-rightbar .rightbar-title { + padding: 0.75rem 1rem; + font-size: 0.85rem; + font-weight: 700; + color: #64748b; + text-transform: uppercase; + letter-spacing: 0.05em; + border-bottom: 1px solid #e2e8f0; +} + +.chat-hub-rightbar .rightbar-users-list { + flex: 1; + overflow-y: auto; + padding: 0.5rem; +} + +.chat-hub-rightbar .user { + padding: 0.5rem 0.75rem; + font-size: 0.9rem; + color: #334155; + border-radius: 0.375rem; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 0.5rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.chat-hub-rightbar .user:hover { + background-color: #f1f5f9; + color: #0f172a; +} + +.chat-hub-rightbar .user .defaultAvatar { + width: 2rem; + height: 2rem; + font-size: 0.9rem; + flex-shrink: 0; +} + +.chat-hub-rightbar .user img.avatar { + width: 2rem; + height: 2rem; + flex-shrink: 0; +} + +@media (max-width: 899px) { + .chat-hub-rightbar { + display: none; + } +} + +.chat-hub-messages { + flex: 1; + overflow-y: auto; + padding: 1.25rem 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +/* Chat bubble overrides for two-pane layout */ +.chat-hub-messages .message { + display: flex; + flex-direction: column; + max-width: 70%; + padding: 0.625rem 0.875rem; + border-radius: 0.75rem; + font-size: 0.925rem; + line-height: 1.4; + word-break: break-word; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.chat-hub-messages .message.incoming { + align-self: flex-start; + align-items: flex-start; + background-color: #ffffff; + color: #1e293b; + border: 1px solid #e2e8f0; + border-bottom-left-radius: 0.125rem; +} + +.chat-hub-messages .message.outgoing { + align-self: flex-end; + align-items: flex-end; + background-color: #3ba4d7; + color: #ffffff; + border-bottom-right-radius: 0.125rem; +} + +.chat-hub-messages .message .username { + font-size: 0.75rem; + margin-bottom: 0.25rem; + padding: 0 0.125rem; + font-weight: 700; +} + +.chat-hub-messages .message.incoming .username { + color: #0369a1; +} + +.chat-hub-messages .message.outgoing .username { + color: #e0f2fe; +} + +.chat-hub-messages .message .messagetext { + white-space: break-spaces; + margin: 0; +} + +.chat-hub-messages .message .datetime { + font-size: 0.7rem; + margin-top: 0.25rem; + padding: 0 0.125rem; + opacity: 0.8; +} + +.chat-hub-messages .message.incoming .datetime { + color: #64748b; +} + +.chat-hub-messages .message.outgoing .datetime { + color: #f1f5f9; +} + +.chat-hub-input-area { + padding: 0.75rem 1.5rem; + background-color: #ffffff; + border-top: 1px solid #cbd5e1; + display: flex; + gap: 0.75rem; + align-items: flex-end; + flex-shrink: 0; +} + +.chat-hub-input-area textarea.chat-hub-textarea { + flex: 1; + resize: vertical; + min-height: 40px; + max-height: 250px; + height: 40px; + padding: 0.5rem 0.75rem; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + font-size: 0.9rem; + outline: none; + transition: border-color 0.2s, box-shadow 0.2s; + background-color: #f8fafc; +} + +.chat-hub-input-area textarea.chat-hub-textarea:focus { + background-color: #ffffff; + border-color: #3ba4d7; + box-shadow: 0 0 0 3px rgba(59, 164, 215, 0.15); +} + +.chat-hub-input-area button.chat-hub-send-btn { + padding: 0.5rem 1.25rem; + font-size: 0.9rem; + height: 40px; + display: flex; + align-items: center; + gap: 0.5rem; + border-radius: 0.375rem; +} + +button.chat-hub-action-btn, +label.chat-hub-action-btn, +.chat-hub-action-btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + width: 36px !important; + height: 36px !important; + min-width: 36px !important; + padding: 0 !important; + margin: 0 !important; + background: transparent !important; + border: none !important; + box-shadow: none !important; + color: #64748b !important; + font-size: 1.15rem !important; + border-radius: 0.375rem !important; + cursor: pointer !important; + transition: all 0.15s ease !important; + outline: none !important; +} + +button.chat-hub-action-btn:hover, +label.chat-hub-action-btn:hover, +.chat-hub-action-btn:hover { + background-color: #e2e8f0 !important; + color: #3b82f6 !important; + box-shadow: none !important; +} + +button.chat-hub-action-btn i, +label.chat-hub-action-btn i, +.chat-hub-action-btn i { + font-size: 1.15rem !important; + color: inherit !important; +} + +/* Compact Room Chat Style (No bubbles, unique nickname colors, IRC-style) */ +.chat-hub-messages.compact-container, +.messages.compact-container { + gap: 0 !important; + padding: 0.75rem 1rem !important; + background-color: #ffffff !important; + display: flex !important; + flex-direction: column !important; +} + +.chat-hub-messages.compact-container .message.compact, +.messages.compact-container .message.compact { + display: block !important; + max-width: 100% !important; + padding: 0.1rem 0 !important; + border-radius: 0 !important; + background-color: transparent !important; + border: none !important; + box-shadow: none !important; + align-self: flex-start !important; + font-size: 0.875rem !important; + line-height: 1.45 !important; + margin: 0 !important; + white-space: nowrap !important; +} + +.chat-hub-messages.compact-container .message.compact:hover, +.messages.compact-container .message.compact:hover { + background-color: #f8fafc !important; + overflow: visible !important; + white-space: normal !important; +} + +.chat-hub-messages.compact-container .message.compact .datetime, +.messages.compact-container .message.compact .datetime { + color: #a0a0a0 !important; + margin-right: 0.4rem !important; + font-size: 0.78rem !important; + font-family: monospace !important; + opacity: 1 !important; + display: inline !important; +} + +.chat-hub-messages.compact-container .message.compact .username, +.messages.compact-container .message.compact .username { + font-weight: bold !important; + margin-right: 0.2rem !important; + font-size: 0.875rem !important; + display: inline !important; +} + +.chat-hub-messages.compact-container .message.compact .messagetext, +.messages.compact-container .message.compact .messagetext { + color: #1e293b !important; + white-space: normal !important; + word-break: break-word !important; + display: inline !important; + margin: 0 !important; +} + +/* User Tooltip Styling (Standalone Fixed Floating Tooltip) */ +.user-tooltip { + position: fixed !important; + width: 280px !important; + background-color: #ffffe1 !important; + border: 1px solid #7f7f7f !important; + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25) !important; + padding: 0.5rem !important; + border-radius: 0.25rem !important; + z-index: 10000 !important; + white-space: normal !important; + display: flex !important; + gap: 0.5rem !important; + align-items: flex-start !important; + color: #000000 !important; + font-size: 0.8rem !important; + text-align: left !important; + pointer-events: none !important; +} + +.user-tooltip .tooltip-avatar { + flex-shrink: 0 !important; +} + +.user-tooltip .tooltip-avatar .jdenticon-avatar, +.user-tooltip .tooltip-avatar .defaultAvatar, +.user-tooltip .tooltip-avatar img.avatar { + width: 56px !important; + height: 56px !important; + min-width: 56px !important; + min-height: 56px !important; + border-radius: 2px !important; + border: 1px solid #999999 !important; + box-shadow: none !important; + object-fit: cover !important; +} + +.user-tooltip .tooltip-details { + display: flex !important; + flex-direction: column !important; + gap: 0.2rem !important; + min-width: 0 !important; + flex: 1 !important; +} + +.user-tooltip .tooltip-details .tooltip-row { + line-height: 1.2 !important; + display: flex !important; + flex-direction: row !important; + align-items: baseline !important; + gap: 0.35rem !important; + white-space: normal !important; + word-break: break-all !important; +} + +.user-tooltip .tooltip-details .tooltip-row .tooltip-label { + font-weight: bold !important; + color: #000000 !important; + font-size: 0.8rem !important; + flex-shrink: 0 !important; +} + +.user-tooltip .tooltip-details .tooltip-row .tooltip-value { + font-weight: normal !important; + color: #000000 !important; + font-size: 0.8rem !important; + overflow: hidden !important; + text-overflow: ellipsis !important; +} + +.user-tooltip .tooltip-details .tooltip-row .tooltip-value.tooltip-id { + font-family: monospace !important; + font-size: 0.75rem !important; + color: #0000bb !important; +} + +/* Chat Modal Dialogs (Create Room, Attach File, Invite Friends) */ +.attach-modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(15, 23, 42, 0.5); + backdrop-filter: blur(4px); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.attach-modal { + background: #ffffff; + border-radius: 0.5rem; + width: 480px; + max-width: 92vw; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.1); + padding: 1.5rem; + display: flex; + flex-direction: column; + color: #1e293b; + box-sizing: border-box; +} + +.attach-modal h4 { + margin: 0 0 1rem 0; + font-size: 1.15rem; + font-weight: 700; + color: #0f172a; +} + +.attach-modal .attach-modal-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.attach-modal .attach-modal-header h4 { + margin: 0; +} + +.attach-modal .attach-modal-header .attach-modal-icon { + font-size: 1.25rem; + color: #3b82f6; +} + +/* Emoji Picker Dropdown & Grid Styling */ +.emoji-picker-wrapper { + position: relative; + display: inline-flex; +} + +.emoji-picker { + position: absolute; + bottom: 48px; + left: 0; + z-index: 9999; + width: 320px; + background: #ffffff; + border: 1px solid #cbd5e1; + border-radius: 12px; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.1); + padding: 0.75rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + font-family: inherit; + + .emoji-search-row { + position: relative; + display: flex; + align-items: center; + + .emoji-search-icon { + position: absolute; + left: 0.6rem; + color: #94a3b8; + font-size: 0.85rem; + pointer-events: none; + } + + input.emoji-search-input { + width: 100%; + padding: 0.4rem 1.8rem 0.4rem 1.8rem; + font-size: 0.85rem; + border: 1px solid #cbd5e1; + border-radius: 6px; + outline: none; + background-color: #f8fafc; + transition: border-color 0.15s ease; + + &:focus { + border-color: #3b82f6; + background-color: #ffffff; + } + } + + .emoji-search-clear { + position: absolute; + right: 0.5rem; + background: transparent; + border: none; + color: #94a3b8; + cursor: pointer; + padding: 0.2rem; + font-size: 0.85rem; + + &:hover { + color: #ef4444; + } + } + } + + .emoji-categories { + display: flex; + justify-content: space-between; + padding-bottom: 0.4rem; + border-bottom: 1px solid #f1f5f9; + margin-bottom: 0.25rem; + + .emoji-cat-btn { + background: transparent !important; + border: none !important; + font-size: 1.1rem !important; + padding: 0.25rem 0.35rem !important; + border-radius: 6px !important; + cursor: pointer !important; + transition: background-color 0.15s ease, transform 0.1s ease !important; + line-height: 1 !important; + width: auto !important; + height: auto !important; + min-width: unset !important; + + &:hover { + background-color: #f1f5f9 !important; + transform: scale(1.15); + } + + &.active { + background-color: #e0f2fe !important; + border-radius: 6px !important; + } + } + } + + .emoji-grid { + display: grid !important; + grid-template-columns: repeat(8, 1fr) !important; + gap: 2px !important; + max-height: 220px !important; + overflow-y: auto !important; + padding-right: 2px !important; + + &::-webkit-scrollbar { + width: 5px; + } + &::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 3px; + } + + .emoji-btn { + background: transparent !important; + border: none !important; + font-size: 1.25rem !important; + padding: 0.35rem 0 !important; + cursor: pointer !important; + border-radius: 6px !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + width: auto !important; + height: auto !important; + min-width: unset !important; + transition: background-color 0.1s ease, transform 0.1s ease !important; + + &:hover { + background-color: #e2e8f0 !important; + transform: scale(1.2); + } + } + } +} + +/* Rightbar / Chat Participants Right-Click Context Menu */ +.rightbar-context-menu, +.chat-msg-context-menu { + position: absolute !important; + z-index: 9999 !important; + right: 10px !important; + background-color: #ffffff !important; + border: 1px solid #cbd5e1 !important; + border-radius: 8px !important; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.1) !important; + padding: 0.35rem 0 !important; + min-width: 220px !important; + font-family: inherit !important; + overflow: hidden !important; + + .menu-item, + .context-menu-item { + display: flex !important; + align-items: center !important; + padding: 0.55rem 0.85rem !important; + font-size: 0.875rem !important; + font-weight: 500 !important; + color: #1e293b !important; + cursor: pointer !important; + transition: background-color 0.15s ease, color 0.15s ease !important; + user-select: none !important; + + &:hover { + background-color: #f1f5f9 !important; + color: #0284c7 !important; + } + + i { + font-size: 0.95rem !important; + width: 1.25rem !important; + text-align: center !important; + } + } +} + +/* User Avatar Hover Tooltip */ +.user-tooltip { + position: fixed !important; + z-index: 10000 !important; + background-color: #ffffff !important; + border: 1px solid #cbd5e1 !important; + border-radius: 12px !important; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.1) !important; + padding: 0.85rem 1rem !important; + display: flex !important; + gap: 0.85rem !important; + align-items: center !important; + min-width: 280px !important; + max-width: 380px !important; + pointer-events: none !important; + font-family: inherit !important; + + .tooltip-avatar { + flex-shrink: 0 !important; + } + + .tooltip-details { + display: flex !important; + flex-direction: column !important; + gap: 0.3rem !important; + flex: 1 !important; + min-width: 0 !important; + + .tooltip-row { + display: flex !important; + align-items: center !important; + font-size: 0.85rem !important; + line-height: 1.3 !important; + + .tooltip-label { + font-weight: 600 !important; + color: #64748b !important; + margin-right: 0.35rem !important; + flex-shrink: 0 !important; + } + + .tooltip-value { + color: #1e293b !important; + font-weight: 500 !important; + word-break: break-all !important; + + &.tooltip-id { + font-family: monospace !important; + font-size: 0.78rem !important; + color: #475569 !important; + } + } + } + } +} + + + + + +@use '../abstracts' as *; + +.mail { + .permission-flag { + margin-bottom: 1rem; + @include flex($gap: 1rem); + } + &-tags { + padding: 0.5rem; + border: 1px solid transparentize($dark-color, 0.8); + border-radius: 6px; + &__container { + @include flex(column); + & .tag-item { + @include flex($align: center, $gap: 4px); + border-bottom: 1px solid transparentize($dark-color, 0.9); + padding: 2px 0; + + &:last-child { + border: none; + } + &__color { + width: 1.25rem; + height: 1.25rem; + aspect-ratio: 1; + } + &__name { + font-size: 1.125rem; + } + &__modify { + margin-left: auto; + font-size: 0.75rem; + @include flex($gap: 4px); + } + &:hover { + background-color: $light-color; + } + & button, + & button.red { + padding: 0.25rem 0.6rem; + } + } + } + } +} + +.mail-tags-form .input-field { + margin-bottom: 0.5rem; + label { + margin-right: 0.5rem; + } +} + +.external-address { + margin: 0; + padding-left: 1rem; + height: 100px; + overflow: hidden auto; + &::-webkit-scrollbar { + display: none; + } +} + +.proxy-server { + @include flex(column, $gap: 4px); + &__tor, + &__i2p { + & > h4 { + margin-bottom: 0.25rem; + } + & > input { + margin-right: 0.5rem; + } + .proxy-outgoing { + display: inline-flex; + align-items: center; + gap: 0.5rem; + &__status { + width: 1rem; + height: 1rem; + aspect-ratio: 1; + border: 1px solid black; + border-radius: 50%; + } + } + } +} + +.config-files { + @include flex(column, $gap: 1rem); +} + +/* Hidden Service Configuration layout overrides */ +.proxy-server-container { + width: 100%; + @include flex(column, $gap: 1rem); +} + +.proxy-description { + color: #334155; + font-size: 0.95rem; + margin-bottom: 0.5rem; +} + +.proxy-rows-container { + @include flex(column, $gap: 0.75rem); + width: 100%; +} + +.proxy-row { + display: grid; + grid-template-columns: 160px 220px 220px auto; + gap: 0.75rem; + align-items: center; + width: 100%; +} + +.proxy-label { + font-size: 0.95rem; + font-weight: 500; + color: #1e293b; +} + +.proxy-addr-input, +.proxy-port-input { + width: 100% !important; + max-width: none !important; +} + +.proxy-status-container { + @include flex($align: center, $gap: 0.5rem); +} + +.proxy-status-bullet { + width: 14px; + height: 14px; + border-radius: 50%; + display: inline-block; + border: 1px solid #475569; +} + +.proxy-status-text { + font-size: 0.95rem; + color: #1e293b; +} + + + + +@use '../abstracts/' as *; + +.file-view { + width: 100%; + padding: 1rem; + margin-top: 1.5rem; + border-radius: 8px; + border: 1px solid #ccc; + animation: fadein 0.5s; + &__heading { + @include flex($justify: space-between); + margin-bottom: 0.5rem; + &-chunk { + @include flex($gap: 1rem); + } + } + &__body { + @include flex(column, $gap: 1rem); + &-details { + @include flex($align: center); + &-stat { + width: 100%; + display: grid; + grid-template-columns: repeat(5, 1fr); + & span > i { + margin-right: 0.5rem; + } + } + &-action { + @include flex($gap: 1rem); + height: 100%; + & button, + & button.red { + padding: 0.25rem 0.75rem; + } + } + } + } +} + +table.myfiles td { + word-wrap: break-word; +} +table.myfiles th:nth-child(1) { + width: 2%; +} +table.myfiles th:nth-child(2) { + width: 50%; +} +table.myfiles td:nth-child(2) { + text-align: start; +} +table.friendsfiles td { + word-wrap: break-word; +} +table.friendsfiles th:nth-child(1) { + width: 2%; +} +table.friendsfiles th:nth-child(2) { + width: 50%; +} +table.friendsfiles th:nth-child(4) { + width: 40%; +} +table.friendsfiles td:nth-child(2) { + text-align: start; +} + +// File Search +.file-search-container { + margin-top: 1rem; + padding: 8px; + @include flex($gap: 8px); + border: 1px solid transparentize($dark-color, 0.8); + border-radius: 6px; + height: 100%; + overflow: auto; + + &__keywords { + flex-basis: 15%; + padding-right: 0.25rem; + border-right: 1px solid transparentize($dark-color, 0.9); + + & .keywords-container { + @include flex(column); + border-top: 2.5px solid transparentize($dark-color, 0.92); + margin-top: 0.125rem; + padding-top: 0.25rem; + + & a { + font-size: 1.2rem; + text-decoration: none; + color: $dark-color; + + &.selected { + color: $primary-color; + } + } + } + } + + &__results { + flex-basis: 85%; + height: 100%; + overflow: auto; + + & .results-container { + & .results-header { + & tr { + display: flex; + & th { + font-size: 1.25rem; + font-weight: bold; + text-align: left; + + &:nth-child(1) { + flex-basis: 40%; + } + &:nth-child(2) { + flex-basis: 10%; + text-align: center; + } + &:nth-child(3) { + flex-basis: 40%; + } + &:nth-child(4) { + flex-basis: 10%; + } + } + } + } + & .results { + height: 100%; + overflow: auto; + & tr { + display: flex; + & .results { + &__hash, + &__name { + text-align: left; + flex-basis: 40%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + span { + margin-left: 8px; + } + } + &__size { + flex-basis: 10%; + } + &__download { + flex-basis: 10%; + @include flex($justify: start, $align: center); + } + } + } + } + } + } +} + +.search-form { + display: flex; + width: 40%; + input { + width: 100%; + } + button { + margin-left: 0.5rem; + } +} + +.shareManagerPopupOverlay { + @include popupOverlay; + .shareManagerPopup { + position: absolute; + inset: 0; + margin: auto; + width: 80%; + height: 90%; + & > .widget { + padding: 1.5rem; + } + & .close-btn { + position: absolute; + top: 1.5rem; + right: 1.5rem; + } + } +} + +.share-manager { + @include flex(column, $justify: space-between); + &__table { + margin: 1rem 0 auto; + thead { + font-weight: bold; + text-align: left; + td:nth-child(1), + td:nth-child(2) { + padding-left: 0.5rem; + } + td:nth-child(3), + td:nth-child(4) { + & .tooltip { + font-weight: normal; + font-size: 1rem; + } + } + } + tbody { + text-align: left; + td:nth-child(4) { + font-size: 1rem; + } + } + td { + input { + border: 0 !important; + &[type='text'] { + width: 100%; + } + } + &:nth-child(1) { + width: 45%; + } + &:nth-child(2) { + width: 20%; + } + &:nth-child(3) { + width: 10%; + } + &:nth-child(4) { + width: 25%; + } + } + } + &__actions { + @include flex($justify: space-between); + } + &__form { + @include flex(column, $gap: 0.5rem); + &_input { + @include flex(column, $gap: 0.5rem); + input { + flex-grow: 1; + } + } + } + + .share-flags { + /* hide checkbox */ + input.share-flags-check { + display: none; + /* use label with 'for' to manipulate checkbox */ + & + label.share-flags-label { + color: grey; + margin-right: 0.25rem; + padding: 0.25rem 0.25rem 0.125rem; + border: 1px solid #6d6d6d; + border-radius: 0.5rem; + } + &:checked + label.share-flags-label { + color: $primary-retro-color; + } + } + } + label span { + display: inline-block; + width: 1.125rem; + } +} +.manage-visibility { + label { + width: 100%; + cursor: pointer; + } + @include flex($justify: space-between); +} + +/* FILES MODULE RESPONSIVENESS */ +@media (max-width: 700px) { + + /* General Files Layout */ + .file-view__body-details { + flex-direction: column; + align-items: flex-start; + gap: 1rem; + } + + /* File Detail Grid (Downloads/Uploads) */ + .file-view__body-details-stat { + grid-template-columns: 1fr; + gap: 0.5rem; + } + + .file-view__body-details-stat span { + display: flex; + align-items: center; + } + + /* Share Manager Table -> Cards */ + .share-manager__table, + .share-manager__table thead, + .share-manager__table tbody, + .share-manager__table tr, + .share-manager__table td { + display: block; + width: 100% !important; + } + + .share-manager__table thead { + display: none; + /* Hide header on mobile */ + } + + .share-manager__table tr { + border: 1px solid #ccc; + border-radius: 8px; + margin-bottom: 1rem; + padding: 0.5rem; + background: white; + } + + .share-manager__table td { + margin-bottom: 0.5rem; + border: none !important; + padding-left: 0 !important; + } + + /* My Files / Friends Files Tables -> Cards */ + table.myfiles, + table.myfiles tr, + table.myfiles td, + table.friendsfiles, + table.friendsfiles tr, + table.friendsfiles td { + display: block; + width: 100% !important; + } + + table.myfiles th, + table.friendsfiles th { + display: none; + } + + table.myfiles tr, + table.friendsfiles tr { + border: 1px solid #ccc; + border-radius: 8px; + margin-bottom: 1rem; + padding: 0.5rem; + background: white; + } + + /* Search Container Layout */ + .file-search-container { + flex-direction: column; + } + + .file-search-container__keywords { + flex-basis: auto; + width: 100%; + border-right: none; + border-bottom: 1px solid rgba(20, 20, 27, 0.1); + padding-bottom: 1rem; + margin-bottom: 1rem; + } + + /* Search results table -> cards */ + .results-container, + .results-container thead, + .results-container tbody, + .results-container tr, + .results-container td { + display: block; + width: 100% !important; + } + + .results-container thead { + display: none; + } + + .results-container tr { + border-bottom: 1px solid #eee; + padding: 1rem 0; + } + + .results-container td { + margin-bottom: 0.5rem; + word-break: break-all; + } +} + + + +.forums-node-panel { + position: relative; + bottom: 200px; + margin-left: 200px; + animation: fadein 0.5s; +} + +/* subject */ +table.forums th:nth-child(1) { + width: 50%; + text-align: start; +} +/* subject */ +table.forums td:nth-child(1) { + text-align: start; + /* Truncate text with '...' */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +table.forums tr:hover { + background-color: #eef3f6; + cursor: pointer; +} + +table.forums tr.hidden { + display: none; +} + +#searchforum { + position: relative; + margin-left: 250px; + /* top: 50px; */ +} + +#forumdetails { + position: relative; + padding: 10px; +} +.p { + margin: 0; +} +#toggleunsub { + position: relative; + background: gray; +} + +table.threads tr:hover { + background-color: #eef3f6; + cursor: pointer; +} +table.threads td { + word-wrap: break-word; +} +table.threadreply th:nth-child(2) { + width: 50%; +} +table.threadreply th:nth-child(1) { + width: 2%; +} +table.threadreply td:nth-child(2) { + width: 50%; + text-align: start; +} +table.threadreply td { + word-wrap: break-word; +} +table.threadreply tr:hover { + background-color: #eef3f6; + cursor: pointer; +} + + + +@use 'sass:color'; +@use '../abstracts' as *; + +.homepage { + margin: 2rem auto 0; + @include flex(column, $gap: 4rem); + + .logo { + @include flex($justify: center, $align: center); + + & img { + width: 90px; + } + + .retroshareText { + @include flex(column, $align: center); + + & .retrotext { + font-size: 36px; + font-weight: 600; + line-height: 1.125; + + &>span { + color: $primary-retro-color; + } + } + + &>b { + font-size: 14px; + line-height: 1; + } + } + } + + .certificate { + @include flex(column, $gap: 4rem); + + &__heading { + text-align: center; + + &>h1 { + margin-bottom: 1rem; + } + } + + &__content { + @include flex(column, $gap: 2rem); + padding: 2rem; + text-align: center; + border: 1.5px solid color.adjust($primary-retro-color, $alpha: -0.8); + border-radius: 6px; + box-shadow: 0px 0px 8px 2px color.adjust($dark-color, $alpha: -0.95); + + .rsId>p { + margin-bottom: 0.5rem; + color: $primary-retro-color; + } + + .retroshareID { + padding: 0.25rem; + @include flex($align: center); + justify-self: start; + font-size: 1.25rem; + border-radius: 4px; + background: color.adjust($dark-color, $alpha: -0.95); + + & .textArea { + padding: 0; + width: 100%; + height: auto; + font-size: 1rem; + font-family: monospace; + background: transparent; + border: none; + resize: none; + overflow: hidden; + field-sizing: content; + } + + & i { + color: $primary-retro-color; + } + + &>i { + margin: 0 0.5rem; + cursor: pointer; + } + } + + .webhelp { + padding: 0.5rem; + background: whitesmoke; + @include flex($justify: center, $align: center, $gap: 0.5rem); + border-radius: 4px; + border: 1px solid color.adjust($dark-color, $alpha: -0.5); + width: fit-content; + cursor: pointer; + + &-container { + display: grid; + place-items: center; + } + + &:hover { + background: $light-color; + border: 1px solid $dark-color; + } + + &>i { + font-size: 1.2rem; + color: green; + } + } + + .add-friend>h6, + .webhelp-container>h6 { + font-weight: normal; + margin-bottom: 0.5rem; + } + } + } +} + +/* Responsive Mobile Home Page Styles */ +@media (max-width: 768px) { + .homepage { + margin: 1rem auto !important; + padding: 0 1rem !important; + gap: 2rem !important; + max-width: 100% !important; + box-sizing: border-box !important; + + .logo { + flex-direction: column !important; + gap: 0.5rem !important; + text-align: center !important; + + & img { + width: 60px !important; + } + + .retroshareText { + .retrotext { + font-size: 1.6rem !important; + } + & > b { + font-size: 0.75rem !important; + } + } + } + + .certificate { + gap: 2rem !important; + + &__heading { + & > h1 { + font-size: 1.35rem !important; + margin-bottom: 0.5rem !important; + } + font-size: 0.85rem !important; + } + + &__content { + padding: 1rem 0.75rem !important; + gap: 1.25rem !important; + + .retroshareID { + padding: 0.5rem !important; + font-size: 0.85rem !important; + max-width: 100% !important; + + .textArea { + font-size: 0.8rem !important; + word-break: break-all !important; + overflow-wrap: anywhere !important; + } + } + } + } + } +} + + +@forward "login"; +@forward "home"; +@forward "network"; +@forward "people"; +@forward "chat"; +@forward "mail"; +@forward "files"; +@forward "channel"; +@forward "forums"; +@forward "board"; +@forward "config"; + + + +@use 'sass:color'; +@use '../abstracts' as *; + +.login-page { + background-image: linear-gradient(-45deg, + color.adjust($primary-color, $alpha: -0.25), + color.adjust($primary-retro-color, $alpha: -0.25)); + height: 100%; + animation: fadein 0.5s; + + .login-container { + background-color: white; + box-shadow: 3px 3px 5px color.adjust($dark-color, $alpha: -0.6); + margin: auto; + position: relative; + top: 100px; + max-width: 400px; + max-height: 500px; + border-radius: 5px; + @include flex(column, $align: center); + + & input { + padding: 0.375rem 0.75rem; + border-radius: 0.275rem; + } + + & * { + margin-bottom: 1rem; + } + + &>img { + margin: 1rem 0 2rem; + } + + & extra { + margin: 0; + } + + &>a { + text-decoration: underline; + cursor: pointer; + } + } + + .extra>label, + .extra>br, + .extra>input { + margin-bottom: 0; + } +} + + +@use '../abstracts/' as *; + +.side-bar { + @include flex(column); + background: white; + .mail-compose-btn { + width: 96%; + margin: 0.25rem; + padding: 0.75rem 0; + } + + .sidebar a, + .sidebarquickview a { + display: flex; + align-items: center; + gap: 0.5rem; + } + + .sidebar-badge { + margin-left: auto; + background-color: #3b82f6; + color: #ffffff; + font-size: 0.725rem; + font-weight: 700; + padding: 0.15rem 0.45rem; + border-radius: 999px; + line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 1.2rem; + } +} + +.compose-mail { + &__from { + @include flex($justify: flex-start, $align: center, $gap: 0.5rem); + padding-bottom: 0.5rem; + border-bottom: 2px solid $light-color; + } + &__recipients { + padding: 0.5rem 0; + @include flex(column, $gap: 0.5rem); + border-bottom: 2px solid $light-color; + &__container { + @include flex($gap: 0.5rem); + & > label { + text-transform: capitalize; + } + .recipients { + width: 100%; + @include flex($gap: 0.5rem); + flex-wrap: wrap; + &__selected { + padding: 0.125rem 0.5rem; + @include flex($align: center, $gap: 0.5rem); + border: 1px solid $light-color; + border-radius: 3px; + cursor: default; + & i { + cursor: pointer; + padding: 0.25rem; + } + } + &__input { + display: flex; + position: relative; + flex-grow: 1; + &-field { + flex-grow: 1; + min-width: 200px; + padding: 0; + border: none; + box-shadow: none; + &:focus + .recipients__input-list { + display: flex; + } + } + &-list { + z-index: 1; + position: absolute; + top: 1rem; + padding: 0; + width: 100%; + max-height: 15rem; + flex-direction: column; + overflow: auto; + display: none; + background: white; + border-top: 1px solid $light-color; + border-bottom: 1px solid $light-color; + &:hover { + display: flex; + } + & li { + list-style: none; + padding: 0.25rem 0.5rem; + cursor: pointer; + background: white; + border: 1px solid $light-color; + border-top: 0px; + &:hover { + background: $light-color; + } + &:last-child { + border-bottom: 0px; + } + } + } + } + } + } + .remove-recipient { + padding: 0.125rem 0.5rem; + } + } + input[type='text'].compose-mail__subject { + padding: 0.5rem 0; + border: none; + box-shadow: none; + border-bottom: 2px solid $light-color; + border-radius: 0; + } + &__message { + margin: 0.5rem 0; + height: 100%; + @include flex(column); + overflow: auto; + &-body { + height: 100%; + outline: transparent; + } + } + &__send-btn { + @include flex($align: center, $gap: 0.5rem); + & i { + transform: translateY(-1px); + } + } +} + +.msg-view { + height: 100%; + @include flex(column, $gap: 1rem); + overflow: auto; + &-nav { + @include flex($justify: space-between, $align: column); + &__action { + @include flex($gap: 0.5rem); + } + } + &__header { + @include flex(column, $gap: 1rem); + & > h3 { + line-height: 1; + } + & .msg-details { + @include flex($gap: 1rem); + &__avatar { + height: max-content; + } + &__info { + @include flex(column); + &-item { + @include flex($gap: 0.5rem); + } + } + } + } + &__body { + height: 100%; + overflow: auto; + font-size: 14px !important; + } + &__attachment { + height: 50%; + overflow: auto; + @include flex(column); + &-items { + height: 100%; + overflow: auto; + } + } +} + +.mail-tag { + width: 8rem; + padding: 0.5rem; +} +.msgHeader { + display: flex; +} +.msgHeaderDetails { + @include flex(column); +} + +table.mails { + & th { + /* star */ + &:nth-child(1) { + width: 5%; + color: $golden-yellow-color; + } + /* attachments */ + &:nth-child(2) { + width: 5%; + color: darken($light-color, 50%); + } + /* subject */ + &:nth-child(3) { + width: 50%; + text-align: start; + } + /* From */ + &:nth-child(4), + /* date */ + &:nth-child(5) { + width: 20%; + text-align: start; + } + } + + & td { + /* subject */ + &:nth-child(3) { + text-align: start; + /* Truncate text with '...' */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + /* From */ + &:nth-child(4), + /* date */ + &:nth-child(5) { + text-align: start; + } + } + + & tr { + &:hover { + background-color: $light-color; + cursor: pointer; + } + &.unread { + color: black; + background-color: $light-color; + } + } + + //Remove hover effects from the table head + & > tr:hover { + cursor: auto; + background-color: white; + } + + & th.sortable-th { + cursor: pointer; + user-select: none; + transition: background-color 0.2s, color 0.2s; + + &:hover { + background-color: $light-color; + color: darken($light-color, 80%); + } + } +} + +/* hide checkbox */ +input.star-check { + display: none; + /* use label with 'for' to manipulate checkbox */ + & + label.star-check { + color: grey; + } + &:checked + label.star-check { + color: $golden-yellow-color; + } +} + +/* mail_util.js styles */ +#truncate { + height: 6rem; + overflow: auto; + &.truncated-view { + height: 1.75rem; + overflow: hidden; + } +} +.toggle-truncate { + font-size: 0.75rem; + padding: 0 0.25rem; + background: #999; + color: $dark-color; + box-shadow: none; + border-radius: 2px; +} + +// Normal mail attachment view +table.attachment-container { + padding: 0; + + & > tr { + border: 0; + } + + .attachment-header { + width: 100%; + @include flex($justify: space-between); + + th { + text-align: start; + + &:nth-child(1) { + flex-basis: 45%; + } + &:nth-child(2) { + flex-basis: 15%; + } + &:nth-child(3) { + flex-basis: 10%; + } + &:nth-child(4) { + flex-basis: 20%; + } + &:nth-child(5) { + text-align: center; + flex-basis: 10%; + } + } + } + + .attachment { + width: 100%; + @include flex($justify: space-between); + text-align: start; + + &__name { + flex-basis: 45%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + span { + margin-left: 8px; + } + } + + &__from { + flex-basis: 15%; + } + &__size { + flex-basis: 10%; + } + &__date { + flex-basis: 20%; + } + & td:nth-child(5) { + @include flex($justify: center, $align: center); + flex-basis: 10%; + + & button { + font-size: 0.875rem; + } + } + } +} + +// Attachment Section attachment view +.view-toggle { + height: max-content; + border: 1px solid $primary-color; + border-radius: 4px; + display: flex; + + & * { + padding: 4px 12px; + border-radius: 4px; + } +} + +.composePopupOverlay { + @include popupOverlay; + .composePopup { + position: absolute; + inset: 0; + margin: auto; + width: 80%; + height: 90%; + & > .widget { + padding: 2rem; + } + & .close-btn { + position: absolute; + top: 1.5rem; + right: 1.5rem; + } + } +} + +/* Modern Attachment Card Styling */ +.attachments-wrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-top: 0.5rem; +} + +.attachment-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 0.5rem; + padding: 0.75rem 1rem; + display: flex; + align-items: center; + gap: 0.75rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02); + + .attachment-icon { + width: 40px; + height: 40px; + border-radius: 0.5rem; + background: #eff6ff; + color: #3b82f6; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; + flex-shrink: 0; + } + + .attachment-info { + flex: 1; + min-width: 0; + + .attachment-name { + font-weight: 600; + font-size: 0.9rem; + color: #1e293b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .attachment-size { + font-size: 0.75rem; + color: #64748b; + } + } + + .btn-attachment-download { + padding: 0.4rem 0.75rem; + font-size: 0.8rem; + height: 34px; + } +} + +.mobile-fab-compose { + display: none; +} + +/* Mobile Responsiveness for Mail UI */ +@media (max-width: 768px) { + .side-bar { + width: 100% !important; + flex-direction: row !important; + overflow-x: auto !important; + overflow-y: hidden !important; + white-space: nowrap !important; + border-bottom: 1px solid #cbd5e1 !important; + padding: 0.5rem !important; + flex-shrink: 0 !important; + background: #ffffff !important; + } + + .side-bar .mail-compose-btn { + display: none !important; + } + + .side-bar .sidebar { + width: auto !important; + flex-direction: row !important; + gap: 0.25rem !important; + } + + .side-bar .sidebar a { + padding: 0.5rem 0.75rem !important; + font-size: 0.85rem !important; + border-radius: 0.375rem !important; + border-left: none !important; + border-bottom: none !important; + display: inline-flex !important; + align-items: center !important; + gap: 0.35rem !important; + } + + .side-bar .sidebar a .sidebar-link-text { + display: none !important; + } + + .side-bar .sidebar a i { + margin-right: 0 !important; + font-size: 1.1rem !important; + } + + .side-bar .sidebar a .sidebar-badge { + margin-left: 0 !important; + background: #3b82f6 !important; + color: #ffffff !important; + font-size: 0.7rem !important; + font-weight: 700 !important; + padding: 0.15rem 0.4rem !important; + border-radius: 999px !important; + line-height: 1 !important; + } + + .side-bar .sidebar a.selected-sidebar-link { + background-color: #e0f2fe !important; + color: #0369a1 !important; + border-left: none !important; + font-weight: 600 !important; + } + + .side-bar .sidebarquickview { + display: none !important; /* Hide quickview tags on mobile bar; filter via colored dropdown */ + } + + select.mail-tag { + padding: 0.45rem 0.75rem !important; + border-radius: 0.5rem !important; + border: 1px solid #cbd5e1 !important; + font-size: 0.85rem !important; + font-weight: 600 !important; + background-color: #ffffff !important; + color: #1e293b !important; + outline: none !important; + cursor: pointer !important; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important; + } + + .node-panel { + width: 100% !important; + flex: 1 !important; + padding: 0.5rem !important; + overflow: auto !important; + } + + /* Mail List Table -> Mobile Card List View */ + .table-pagination-container table.mails { + display: block !important; + width: 100% !important; + } + + .table-pagination-container table.mails tr:first-child { + display: none !important; + } + + .table-pagination-container table.mails tbody { + display: flex !important; + flex-direction: column !important; + gap: 0.5rem !important; + } + + .table-pagination-container table.mails tr.msgbody { + display: flex !important; + flex-direction: column !important; + padding: 0.85rem !important; + background: #ffffff !important; + border: 1px solid #e2e8f0 !important; + border-radius: 0.65rem !important; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03) !important; + position: relative !important; + margin-bottom: 0.25rem !important; + transition: all 0.2s ease !important; + } + + .table-pagination-container table.mails tr.msgbody.unread { + background: #f0f9ff !important; + border-color: #bae6fd !important; + border-left: 4px solid #0284c7 !important; + } + + .table-pagination-container table.mails tr.msgbody td { + display: block !important; + padding: 0 !important; + border: none !important; + } + + /* Top Row: Sender Info & Date */ + .table-pagination-container table.mails tr.msgbody td.cell-from { + order: 1 !important; + margin-bottom: 0.35rem !important; + padding-right: 5rem !important; + } + + .table-pagination-container table.mails tr.msgbody td.cell-from div { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; + width: 100% !important; + } + + .table-pagination-container table.mails tr.msgbody td.cell-from div .jdenticon-avatar, + .table-pagination-container table.mails tr.msgbody td.cell-from div .defaultAvatar, + .table-pagination-container table.mails tr.msgbody td.cell-from div img.avatar { + flex-shrink: 0 !important; + width: 28px !important; + height: 28px !important; + min-width: 28px !important; + min-height: 28px !important; + aspect-ratio: 1 / 1 !important; + object-fit: cover !important; + border-radius: 50% !important; + } + + .table-pagination-container table.mails tr.msgbody td.cell-from div span { + font-weight: 700 !important; + font-size: 0.95rem !important; + color: #1e293b !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + } + + .table-pagination-container table.mails tr.msgbody td.cell-date { + order: 2 !important; + position: absolute !important; + top: 0.85rem !important; + right: 0.85rem !important; + font-size: 0.75rem !important; + font-weight: 600 !important; + color: #64748b !important; + white-space: nowrap !important; + } + + /* Subject Row */ + .table-pagination-container table.mails tr.msgbody td.cell-subject { + order: 3 !important; + margin-bottom: 0.25rem !important; + font-size: 0.9rem !important; + color: #334155 !important; + } + + .table-pagination-container table.mails tr.msgbody td.cell-subject span { + font-weight: 600 !important; + } + + /* Star & Attachments */ + .table-pagination-container table.mails tr.msgbody td.cell-star { + order: 4 !important; + position: absolute !important; + bottom: 0.75rem !important; + right: 0.85rem !important; + } + + .table-pagination-container table.mails tr.msgbody td.cell-attachment { + order: 5 !important; + position: absolute !important; + bottom: 0.75rem !important; + right: 2.5rem !important; + color: #64748b !important; + } + + /* Mail Reader (MessageView) Mobile Layout */ + .msg-view { + padding: 0.5rem !important; + gap: 0.75rem !important; + } + + .msg-view .msg-view-nav { + background: #ffffff !important; + padding: 0.5rem 0.75rem !important; + border-radius: 0.5rem !important; + border: 1px solid #e2e8f0 !important; + } + + .msg-view .msg-view-nav__action button { + padding: 0.45rem 0.65rem !important; + min-width: 38px !important; + height: 38px !important; + justify-content: center !important; + } + + .msg-view .msg-view-nav__action button .btn-text { + display: none !important; + } + + .msg-view .msg-view-nav__action button i { + margin: 0 !important; + font-size: 1rem !important; + } + + .msg-view__header { + background: #ffffff !important; + padding: 1rem !important; + border-radius: 0.5rem !important; + border: 1px solid #e2e8f0 !important; + } + + .msg-view__header h3 { + font-size: 1.25rem !important; + font-weight: 800 !important; + color: #0f172a !important; + line-height: 1.3 !important; + } + + .msg-view__body { + background: #ffffff !important; + padding: 1rem !important; + border-radius: 0.5rem !important; + border: 1px solid #e2e8f0 !important; + font-size: 0.95rem !important; + line-height: 1.6 !important; + color: #1e293b !important; + } + + .msg-view__attachment { + height: auto !important; + } + + /* Floating Action Button (Compose Mail) */ + .mobile-fab-compose { + position: fixed !important; + bottom: 1.75rem !important; + right: 1.5rem !important; + width: 54px !important; + height: 54px !important; + border-radius: 50% !important; + background: linear-gradient(135deg, #3b82f6, #2563eb) !important; + color: #ffffff !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 1.35rem !important; + box-shadow: 0 4px 14px rgba(37, 99, 235, 0.45) !important; + border: none !important; + z-index: 1000 !important; + cursor: pointer !important; + transition: transform 0.2s ease !important; + } + + .mobile-fab-compose:active { + transform: scale(0.92) !important; + } + + .composePopupOverlay .composePopup { + width: 95% !important; + height: 95% !important; + } +} + +.mail-mobile-nav-toggle { display: none; } +.mail-nav-drawer { display: contents; } +.mail-mobile-box-title { display: none; } + +/* Mail folders become a left drawer on phones. */ +@media (max-width: 768px) { + .side-bar { + height: 44px !important; + padding: .25rem !important; + overflow: visible !important; + } + + .mail-mobile-nav-toggle { + display: inline-flex !important; + width: 40px; + height: 40px; + align-items: center; + justify-content: center; + border: 0; + border-radius: 6px; + background: #ffffff; + color: #0f172a; + cursor: pointer; + font-size: 1.15rem; + } + + .mail-nav-drawer { + position: fixed; + top: 0; + left: 0; + z-index: 1100; + display: flex !important; + flex-direction: column; + width: min(82vw, 300px); + height: 100dvh; + padding: 1rem 0; + overflow-y: auto; + background: #ffffff; + border-right: 1px solid #e2e8f0; + box-shadow: 8px 0 24px rgba(15, 23, 42, .16); + transform: translateX(-105%); + transition: transform 180ms ease; + } + + .mail-nav-drawer--open { transform: translateX(0); } + .mail-mobile-box-title { + display: flex !important; + align-items: center; + gap: .55rem; + margin: .35rem 0 .55rem; + color: #0f172a; + font-size: 1.45rem; + font-weight: 600; + } + .mail-mobile-box-title i { color: #3b82f6; font-size: 1.35rem; } + .mail-box-content > .widget__heading { display: none; } + .mail-nav-drawer .mail-compose-btn { display: flex !important; margin: 0 .85rem .75rem !important; } + .mail-nav-drawer .sidebar, + .mail-nav-drawer .sidebarquickview { display: flex !important; flex-direction: column !important; width: 100% !important; } + .mail-nav-drawer .sidebarquickview { margin-top: .5rem; border-top: 1px solid #e2e8f0; padding-top: .5rem; } + .mail-nav-drawer .sidebarquickview h6 { display: block !important; margin: 0 .9rem .35rem; color: #64748b; } + .mail-nav-drawer .sidebar a, + .mail-nav-drawer .sidebarquickview a { display: flex !important; width: auto; padding: .7rem 1rem !important; border-radius: 0 !important; } + .mail-nav-drawer .sidebar a .sidebar-link-text { display: inline !important; } + .mail-nav-drawer .sidebar a i { margin-right: .75rem !important; font-size: 1.1rem !important; } +} + + + +@use '../abstracts' as *; + +.network-container { + display: flex; + height: 100%; + width: 100%; + overflow: hidden; + background-color: #f1f5f9; +} + +.network-left-pane { + width: 320px; + min-width: 300px; + max-width: 350px; + border-right: 1px solid #cbd5e1; + display: flex; + flex-direction: column; + background: #ffffff; + box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05); +} + +.own-profile-card { + padding: 1.25rem; + border-bottom: 1px solid #e2e8f0; + background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); + display: flex; + flex-direction: column; + gap: 0.75rem; + + .profile-header { + display: flex; + align-items: center; + gap: 1rem; + } + + .profile-info { + display: flex; + flex-direction: column; + flex: 1; + overflow: hidden; + + .profile-name { + font-weight: 700; + color: #1e293b; + font-size: 1.1rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .profile-status { + font-size: 0.85rem; + color: #10b981; + font-weight: 500; + display: flex; + align-items: center; + gap: 0.35rem; + + &::before { + content: ''; + display: inline-block; + width: 8px; + height: 8px; + background-color: #10b981; + border-radius: 50%; + } + } + } + + .own-identity-select-container { + display: flex; + flex-direction: column; + gap: 0.25rem; + + label { + font-size: 0.75rem; + color: #64748b; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + } + + select.own-identity-select { + width: 100%; + padding: 0.375rem 0.5rem; + font-size: 0.85rem; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + background-color: #ffffff; + color: #334155; + outline: none; + cursor: pointer; + transition: border-color 0.2s; + + &:focus { + border-color: #3ba4d7; + } + } + } +} + +.friends-list-container { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + + .searchbar-container { + padding: 0.75rem 1rem; + border-bottom: 1px solid #e2e8f0; + + input.searchbar { + width: 100%; + padding: 0.5rem 0.75rem; + font-size: 0.9rem; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + background-color: #f8fafc; + outline: none; + transition: all 0.2s; + + &:focus { + background-color: #ffffff; + border-color: #3ba4d7; + box-shadow: 0 0 0 3px rgba(59, 164, 215, 0.15); + } + } + } + + .friends-scroll { + flex: 1; + overflow-y: auto; + padding: 0.5rem 0; + } +} + +.friend-list-item { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1rem; + margin: 0.125rem 0.5rem; + border-radius: 0.5rem; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background-color: #f1f5f9; + } + + &.selected { + background-color: #e0f2fe; + + .friend-name { + color: #0369a1; + font-weight: 600; + } + } + + .friend-avatar { + flex-shrink: 0; + } + + .friend-meta { + flex: 1; + min-width: 0; + + .friend-name { + font-size: 0.95rem; + color: #334155; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + transition: color 0.2s; + } + + .friend-status { + font-size: 0.8rem; + color: #94a3b8; + + &.online { + color: #10b981; + font-weight: 500; + } + } + } +} + +.network-right-pane { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + background-color: #f8fafc; +} + +.network-pane-placeholder { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: #94a3b8; + gap: 1rem; + padding: 2rem; + text-align: center; + + i { + font-size: 4rem; + color: #cbd5e1; + } + + p { + font-size: 1.1rem; + max-width: 400px; + } +} + +.network-tabs { + display: flex; + background-color: #ffffff; + border-bottom: 1px solid #cbd5e1; + padding: 0.5rem 1rem 0; + gap: 0.5rem; + + .tab-btn { + padding: 0.625rem 1.25rem; + font-size: 0.95rem; + font-weight: 600; + color: #64748b; + background: transparent; + border: none; + border-radius: 0.375rem 0.375rem 0 0; + border-bottom: 3px solid transparent; + cursor: pointer; + box-shadow: none; + transition: all 0.2s; + + &:hover { + color: #334155; + background-color: #f1f5f9; + } + + &.active { + color: #3ba4d7; + border-bottom-color: #3ba4d7; + background-color: transparent; + } + } +} + +.network-tab-content { + flex: 1; + overflow-y: auto; + padding: 1.5rem; +} + +.network-detail-view { + display: flex; + flex-direction: column; + gap: 1.5rem; + + .detail-header { + display: flex; + align-items: center; + gap: 1.5rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid #e2e8f0; + + .detail-title { + flex: 1; + + h2 { + font-size: 1.75rem; + font-weight: 800; + color: #1e293b; + margin-bottom: 0.25rem; + } + + .detail-subtitle { + font-size: 0.9rem; + color: #64748b; + display: flex; + align-items: center; + gap: 0.5rem; + } + } + + .detail-actions { + display: flex; + gap: 0.75rem; + + button { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + font-size: 0.9rem; + } + } + } + + .detail-section { + background-color: #ffffff; + border-radius: 0.5rem; + border: 1px solid #e2e8f0; + padding: 1.25rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02); + + h3 { + font-size: 1.1rem; + font-weight: 700; + color: #334155; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #f1f5f9; + } + + .info-grid { + display: grid; + grid-template-columns: 120px 1fr; + row-gap: 0.75rem; + font-size: 0.9rem; + + .info-label { + font-weight: 600; + color: #64748b; + } + + .info-value { + color: #1e293b; + word-break: break-all; + } + } + } + + .locations-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1rem; + } + + .location-card { + background-color: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 0.5rem; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02); + + .loc-header { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #f1f5f9; + padding-bottom: 0.5rem; + margin-bottom: 0.25rem; + + .loc-name { + font-weight: 700; + color: #334155; + font-size: 0.95rem; + } + + .loc-status { + font-size: 0.75rem; + font-weight: 600; + padding: 0.125rem 0.5rem; + border-radius: 0.25rem; + + &.online { + background-color: #d1fae5; + color: #065f46; + } + + &.offline { + background-color: #f1f5f9; + color: #475569; + } + } + } + + .loc-body { + font-size: 0.85rem; + display: grid; + grid-template-columns: 80px 1fr; + row-gap: 0.25rem; + + .loc-label { + color: #64748b; + } + + .loc-val { + color: #334155; + word-break: break-all; + } + } + + .loc-footer { + margin-top: 0.5rem; + display: flex; + justify-content: flex-end; + + button { + font-size: 0.8rem; + padding: 0.25rem 0.75rem; + } + } + } +} + +.network-chat-view { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + background-color: #f8fafc; + + .chat-messages { + flex: 1; + overflow-y: auto; + padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 1rem; + } + + .chat-bubble-container { + display: flex; + flex-direction: column; + max-width: 70%; + + &.outgoing { + align-self: flex-end; + align-items: flex-end; + + .chat-bubble { + background-color: #3ba4d7; + color: #ffffff; + border-bottom-right-radius: 0.125rem; + } + } + + &.incoming { + align-self: flex-start; + align-items: flex-start; + + .chat-bubble { + background-color: #ffffff; + color: #1e293b; + border: 1px solid #e2e8f0; + border-bottom-left-radius: 0.125rem; + } + } + + .chat-sender { + font-size: 0.75rem; + color: #64748b; + margin-bottom: 0.25rem; + padding: 0 0.25rem; + } + + .chat-bubble { + padding: 0.625rem 0.875rem; + border-radius: 0.75rem; + font-size: 0.925rem; + line-height: 1.4; + white-space: break-spaces; + word-break: break-word; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + } + + .chat-time { + font-size: 0.7rem; + color: #94a3b8; + margin-top: 0.25rem; + padding: 0 0.25rem; + } + } + + .chat-input-area { + padding: 1rem; + background-color: #ffffff; + border-top: 1px solid #cbd5e1; + display: flex; + gap: 0.75rem; + align-items: center; + + textarea.chat-textarea { + flex: 1; + resize: none; + height: 40px; + padding: 0.5rem 0.75rem; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + font-size: 0.9rem; + outline: none; + transition: all 0.2s; + + &:focus { + border-color: #3ba4d7; + box-shadow: 0 0 0 3px rgba(59, 164, 215, 0.15); + } + } + + button.send-btn { + padding: 0.5rem 1.25rem; + font-size: 0.9rem; + height: 40px; + display: flex; + align-items: center; + gap: 0.5rem; + } + } + + .chat-warning { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: #64748b; + text-align: center; + padding: 2rem; + gap: 1rem; + + i { + font-size: 3rem; + color: #cbd5e1; + } + + h4 { + font-weight: 700; + color: #334155; + } + + p { + max-width: 350px; + font-size: 0.9rem; + } + } +} + +/* Responsive Layout for Mobile/Small Screens */ +@media (max-width: 768px) { + .network-container { + flex-direction: column !important; + } + + .network-left-pane { + width: 100% !important; + max-width: none !important; + height: 45% !important; + border-right: none !important; + border-bottom: 1px solid #cbd5e1 !important; + } + + .network-right-pane { + height: 55% !important; + flex: 1 !important; + } + + /* Hide text labels on mobile screens so profile action buttons show icons only */ + .detail-actions button .btn-text, + .detail-header .detail-actions button .btn-text { + display: none !important; + } + + .detail-actions button, + .detail-header .detail-actions button { + padding: 0.45rem 0.65rem !important; + min-width: 38px !important; + height: 38px !important; + justify-content: center !important; + align-items: center !important; + } + + .detail-actions button i, + .detail-header .detail-actions button i { + margin: 0 !important; + font-size: 1.05rem !important; + } + + .network-detail-view .detail-header { + flex-direction: column !important; + align-items: flex-start !important; + gap: 1rem !important; + + .friend-avatar { + margin-bottom: 0.25rem !important; + } + } + + .locations-grid { + grid-template-columns: 1fr !important; + } +} + + + +.identity { + color: #444; + font-size: 1.1em; + margin: 20px; + padding: 10px; + border: 1px solid #aaa; + border-radius: 20px; + & > h4 { + margin: 5px; + font-size: 1.3em; + } + & button { + font-size: 0.9em; + } + & .details { + display: grid; + grid-template-columns: 140px auto; + grid-row-gap: 5px; + justify-content: left; + } +} + +.defaultAvatar { + width: 3rem; + height: 3rem; + aspect-ratio: 1; + background: lightsteelblue; + border-radius: 50%; + display: grid; + place-items: center; + & p { + font-weight: 900; + color: #666f7f; + transform: translateY(1px); + } +} +img.avatar { + display: block; + width: 3rem; + height: max-content; + aspect-ratio: 1; + margin-right: 0.3em; + border-radius: 50%; +} + +.counter { + margin-left: 0.5em; + &:before { + content: '('; + } + &:after { + content: ')'; + } +} + +.chatInit { + margin-left: 0.5em; + color: green; + cursor: pointer; +} + +.people-sidebar-header { + display: flex; + flex-direction: column; + padding: 0.75rem 1rem 0.5rem 1rem; + gap: 0.75rem; + border-bottom: 1px solid #e2e8f0; + background-color: #ffffff; + + .searchbar-wrapper { + position: relative; + display: flex; + align-items: center; + + i.fa-search { + position: absolute; + left: 0.85rem; + color: #94a3b8; + font-size: 0.9rem; + } + + input.searchbar-input { + width: 100%; + padding: 0.5rem 0.75rem 0.5rem 2.25rem; + border: 1px solid #e2e8f0; + border-radius: 0.5rem; + font-size: 0.9rem; + background-color: #f8fafc; + color: #1e293b; + outline: none; + transition: all 0.2s ease; + + &:focus { + border-color: #3b82f6; + background-color: #ffffff; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); + } + } + } + + .segmented-control { + display: flex; + background-color: #f1f5f9; + padding: 3px; + border-radius: 0.5rem; + gap: 4px; + + button.segment-tab { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + font-size: 0.9rem; + font-weight: 600; + color: #64748b; + background: transparent; + border: none; + border-radius: 0.375rem; + cursor: pointer; + box-shadow: none; + transition: all 0.2s ease; + + &:hover { + color: #1e293b; + } + + &.active { + background-color: #ffffff; + color: #0f172a; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06); + + .segment-badge { + background-color: #019dff; + color: #ffffff; + } + } + + .segment-badge { + display: inline-flex; + align-items: center; + justify-content: center; + background-color: #cbd5e1; + color: #334155; + font-size: 0.75rem; + font-weight: 700; + min-width: 1.25rem; + height: 1.25rem; + padding: 0 0.35rem; + border-radius: 9999px; + line-height: 1; + transition: all 0.2s ease; + } + } + } + + .sub-filter-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + min-height: 32px; + + select.filter-select { + padding: 0.35rem 0.6rem; + border: 1px solid #cbd5e1; + border-radius: 0.375rem; + font-size: 0.85rem; + font-weight: 600; + color: #475569; + background-color: #ffffff; + cursor: pointer; + outline: none; + } + + .btn-add-id { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 0.375rem; + background-color: #3b82f6; + color: #ffffff; + border: none; + cursor: pointer; + font-size: 0.9rem; + transition: background-color 0.2s; + + &:hover { + background-color: #2563eb; + } + } + } +} + +/* Main People Page Flex Layout Container */ +.people-container { + display: flex !important; + flex-direction: row !important; + height: 100% !important; + width: 100% !important; + overflow: hidden !important; + background-color: #f1f5f9 !important; +} + +/* Left Sidebar Pane (320px fixed width on Desktop) */ +.people-left-pane { + width: 320px !important; + min-width: 300px !important; + max-width: 350px !important; + height: 100% !important; + border-right: 1px solid #cbd5e1 !important; + display: flex !important; + flex-direction: column !important; + background: #ffffff !important; + box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05) !important; + flex-shrink: 0 !important; + overflow: hidden !important; +} + +/* Right Content Details Pane */ +.people-right-pane { + flex: 1 !important; + min-width: 0 !important; + height: 100% !important; + display: flex !important; + flex-direction: column !important; + overflow: hidden !important; + background-color: #f8fafc !important; +} + +/* Scrollable list section inside left sidebar */ +.people-list-container { + flex: 1 !important; + overflow-y: auto !important; + padding: 0.5rem 0 !important; +} + +/* Active Chats list item */ +.chat-item { + display: flex !important; + align-items: center !important; + gap: 0.75rem !important; + padding: 0.65rem 0.85rem !important; + margin: 0.2rem 0.5rem !important; + border-radius: 0.5rem !important; + cursor: pointer !important; + transition: all 0.2s ease !important; + position: relative !important; + + &:hover { + background-color: #f1f5f9 !important; + } + + &.selected { + background-color: #e0f2fe !important; + + .chat-name { + color: #0369a1 !important; + font-weight: 700 !important; + } + } + + .chat-avatar-wrapper { + position: relative !important; + flex-shrink: 0 !important; + + .status-dot { + position: absolute !important; + bottom: 0 !important; + right: 0 !important; + width: 10px !important; + height: 10px !important; + border-radius: 50% !important; + border: 2px solid #ffffff !important; + } + } + + .chat-info { + flex: 1 !important; + min-width: 0 !important; + display: flex !important; + flex-direction: column !important; + + .chat-name { + font-size: 0.95rem !important; + font-weight: 600 !important; + color: #1e293b !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + } + + .chat-last-msg { + font-size: 0.825rem !important; + color: #64748b !important; + white-space: nowrap !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + margin-top: 0.1rem !important; + } + } + + .chat-meta { + display: flex !important; + flex-direction: column !important; + align-items: flex-end !important; + flex-shrink: 0 !important; + + .chat-time { + font-size: 0.75rem !important; + color: #94a3b8 !important; + font-weight: 500 !important; + } + } +} + +/* Responsive Layout for Mobile/Small Screens */ +@media (max-width: 768px) { + .people-container { + flex-direction: column !important; + } + .people-left-pane { + width: 100% !important; + max-width: none !important; + height: 45% !important; + border-right: none !important; + border-bottom: 1px solid #cbd5e1 !important; + } + .people-right-pane { + height: 55% !important; + } + + /* Hide text labels on mobile screens so profile action buttons show icons only */ + .detail-actions button .btn-text, + .detail-header .detail-actions button .btn-text { + display: none !important; + } + + /* Hide Distant Chat Tunnel and Chatting as text on mobile screens to save header space */ + .chat-tunnel-status .tunnel-label, + .select-own-profile .chatting-as-label { + display: none !important; + } + + .detail-actions button, + .detail-header .detail-actions button { + padding: 0.45rem 0.65rem !important; + min-width: 38px !important; + height: 38px !important; + justify-content: center !important; + align-items: center !important; + } + + +/* People Sidebar Right-Click Context Menu */ +.people-context-menu { + position: absolute !important; + z-index: 9999 !important; + background-color: #ffffff !important; + border: 1px solid #cbd5e1 !important; + border-radius: 8px !important; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 8px 10px -6px rgba(0, 0, 0, 0.1) !important; + padding: 0.35rem 0 !important; + min-width: 180px !important; + font-family: inherit !important; + overflow: hidden !important; + + .menu-item { + display: flex !important; + align-items: center !important; + padding: 0.6rem 0.9rem !important; + font-size: 0.875rem !important; + font-weight: 500 !important; + color: #1e293b !important; + cursor: pointer !important; + transition: background-color 0.15s ease, color 0.15s ease !important; + user-select: none !important; + + &:hover { + background-color: #f1f5f9 !important; + color: #0284c7 !important; + } + + i { + font-size: 1rem !important; + width: 1.25rem !important; + text-align: center !important; + } + } +} + + + + +// ----------------------------------------------------------------------------- +// This file contains all application-wide button styles. +// ----------------------------------------------------------------------------- +@use "../abstracts" as *; + +button { + @include button($primary-color); + white-space: nowrap !important; + flex-shrink: 0 !important; +} +button.red { + @include button($red-color); + white-space: nowrap !important; + flex-shrink: 0 !important; +} + + + + +@forward 'buttons'; +@forward 'media'; +@forward 'navbar'; +@forward 'posts'; +@forward 'progress-bar'; +@forward 'statusbar'; + + + + +@use 'sass:color'; +@use '../abstracts' as *; + +.media-item { + display: flex; + margin-top: 0.5rem; + padding: 1rem; + border: 1px solid color.adjust($dark-color, $alpha: -0.9); + border-radius: 4px; + + &__details { + flex-basis: 40%; + @include flex($align: start, $gap: 0.5rem); + + & img { + width: 6rem; + object-fit: contain; + } + } + + &__desc { + flex-basis: 60%; + } + + @media (max-width: 768px) { + &__desc { + display: none !important; + } + + &__details { + flex-basis: 100% !important; + width: 100% !important; + } + } +} + + +@use 'sass:color'; +@use '../abstracts' as *; + +.active-link { + background: rgba(255, 255, 255, 0.1) !important; +} + +/* Navbar */ +.nav-menu { + background-color: $dark-color; + box-shadow: 0 5px 5px #222; + @include flex(column, $align: center); + height: 100%; + padding: 0.25rem; + margin-right: 0rem; + + &__logo { + padding: 1.2rem 0; + @include flex($align: center, $gap: 0.3rem); + + img { + width: 1.6rem; + } + + h5 { + line-height: 1; + color: white; + } + } + + &__box { + padding: 2rem 0.125rem; + @include flex(column, $gap: 0.5rem); + position: relative; + + .item { + margin: 0; + padding: 0.675rem 0.5rem; + width: 10rem; + @include flex($align: center); + line-height: 1; + border-radius: 0.5rem; + text-decoration: none; + color: #ccc; + text-transform: capitalize; + transition: 0ms; + + &:hover { + background-color: color.adjust($light-color, $alpha: -0.85); + } + + i.sidenav-icon { + width: 2.5rem; + height: 1.4rem; + display: grid; + place-items: center; + } + } + + .item.item-selected { + color: $primary-light-color; + background-color: color.adjust($primary-light-color, $alpha: -0.85); + font-weight: medium; + } + + button.toggle-nav { + display: none; + position: absolute; + padding: 0; + top: 0; + right: -1rem; + background: color.adjust($primary-color, $lightness: 15%); + width: 1.5rem; + height: 1.5rem; + aspect-ratio: 1; + justify-content: center; + align-items: center; + border-radius: 50%; + box-shadow: none; + } + } + + &.collapsed { + .nav-menu__logo { + .logo-container { + @include flex(column, $align: center, $gap: 0.5rem); + + &>*:not(img) { + display: block; + } + } + + & .nav-menu__logo-text { + display: none !important; + } + } + + .nav-menu__box { + .item { + padding: 0.675rem 0; + width: 2.5rem; + justify-content: center; + transition: 300ms; + + & span, + & p { + display: none !important; + } + } + } + + button i { + rotate: 180deg; + } + } + + // only show toggle button when sidebar is hovered + &:hover button.toggle-nav { + display: flex; + } +} + +.sidebar { + width: 13rem; + background-color: white; + @include flex(column); + + & a { + text-decoration: none; + text-transform: capitalize; + padding: 1rem; + cursor: pointer; + color: #999; + + &:hover { + color: #222; + } + } + + & .selected-sidebar-link { + font-weight: bold; + color: #222; + border-left: 5px solid #3ba4d7; + animation: expand-left-border 0.1s; + } +} + +.sidebar-mobile-toggle, +.sidebar-drawer__title, +.sidebar-drawer__backdrop { display: none !important; } + +.sidebarquickview { + &>h6 { + padding: 0.5rem; + } + + & a { + text-decoration: none; + text-transform: capitalize; + padding: 0.5rem 1rem; + display: block; + color: #999; + + & a:hover { + color: #222; + } + } + + & .selected-sidebarquickview-link { + font-weight: bold; + color: #222; + border-left: 5px solid #3ba4d7; + animation: expand-left-border 0.1s; + } +} + +/* Content adjacent to sidebar */ +.node-panel { + width: 100%; + padding: 0.5rem; + animation: fadein 0.5s; +} + +@keyframes expand-left-border { + from { + border-left: 0; + } + + to { + border-left: 5px solid #3ba4d7; + } +} + +/* GLOBAL SIDEBAR TO TABS */ +@media (max-width: 700px) { + .tab-content { + flex-direction: column; + } + + .sidebar { + width: 100% !important; + flex-direction: row !important; + overflow-x: auto !important; + overflow-y: hidden !important; + white-space: nowrap !important; + border-bottom: 1px solid rgba(20, 20, 27, 0.1) !important; + background: white !important; + z-index: 50 !important; + flex-shrink: 0 !important; + height: auto !important; + padding: 0 !important; + } + + .sidebar a { + display: inline-block !important; + padding: 0.8rem 1.2rem !important; + border-bottom: 3px solid transparent !important; + border-left: none !important; + } + + .sidebar .selected-sidebar-link { + border-left: none !important; + border-bottom: 3px solid #3ba4d7 !important; + animation: none !important; + } + + /* Hide quickview headings on mobile to save space if needed */ + .sidebarquickview>h4, + .sidebarquickview>h6 { + display: none !important; + } + + /* Boards, Channels and Forums use a drawer instead of the horizontal tabs. */ + .sidebar-drawer { + display: block; + position: relative; + width: 100%; + height: 44px; + z-index: 1000; + } + + .sidebar-mobile-toggle { + display: inline-flex !important; + width: 40px; + height: 40px; + align-items: center; + justify-content: center; + border: 0; + border-radius: 6px; + background: #ffffff; + color: #0f172a; + cursor: pointer; + font-size: 1.15rem; + } + + .sidebar-mobile-toggle:hover { background: #f1f5f9; } + + .sidebar-drawer .sidebar { + position: fixed !important; + top: 0; + left: 0; + display: flex !important; + flex-direction: column !important; + width: min(82vw, 300px) !important; + height: 100dvh !important; + padding: 1rem 0 !important; + overflow-y: auto !important; + overflow-x: hidden !important; + transform: translateX(-105%); + transition: transform 180ms ease; + border: 0 !important; + border-right: 1px solid #e2e8f0 !important; + background: #ffffff !important; + opacity: 1 !important; + pointer-events: auto !important; + box-shadow: 8px 0 24px rgba(15, 23, 42, 0.16); + white-space: normal !important; + z-index: 1002; + } + + .sidebar-drawer .sidebar.sidebar--mobile-open { transform: translateX(0); } + .sidebar-drawer .sidebar a { + display: block !important; + padding: .8rem 1.25rem !important; + border: 0 !important; + border-left: 4px solid transparent !important; + color: #334155 !important; + font-size: .95rem; + } + + .sidebar-drawer .sidebar .selected-sidebar-link { + border-left-color: #3ba4d7 !important; + border-bottom: 0 !important; + background: #f0f9ff; + color: #0f172a !important; + } + + .sidebar-drawer__title { + display: block !important; + margin: 0 1.25rem .65rem; + padding-bottom: .75rem; + border-bottom: 1px solid #e2e8f0; + color: #64748b; + font-size: .75rem; + font-weight: 700; + letter-spacing: .06em; + text-transform: uppercase; + } + + .sidebar-drawer__backdrop { + display: none !important; + position: fixed; + inset: 0; + background: rgba(15, 23, 42, .35); + /* The drawer itself owns navigation input; this layer is visual only. */ + pointer-events: none; + z-index: 1001; + } +} + +@media (min-width: 701px) { + .sidebar-drawer { display: contents; } + .sidebar-mobile-toggle, + .sidebar-drawer__title, + .sidebar-drawer__backdrop { display: none !important; } +} + + + +@use 'sass:color'; +@use '../abstracts' as *; + +.posts { + height: 100%; + margin-top: 1rem; + flex-direction: column; + overflow: auto; + + &__heading { + @include flex(column, $justify: space-between); + } + + &-container { + height: 100%; + padding: 1rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 2rem; + border: 1px solid color.adjust($dark-color, $alpha: -0.9); + border-radius: 4px; + overflow: auto; + + &-card { + min-height: 240px; + flex-direction: column; + border: 1px solid color.adjust($dark-color, $alpha: -0.5); + border-radius: 4px; + cursor: pointer; + text-align: center; + + & img { + flex-basis: 90%; + object-fit: cover; + } + + & p { + padding: 0 0.125rem; + flex-basis: 10%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } +} + + +@use 'sass:color'; +@use '../abstracts/colors' as *; + +.progress-bar { + width: 100%; + height: 2rem; + position: relative; + text-align: center; + background-color: $light-color; + border-radius: 20px; + overflow: hidden; + + &__status { + position: absolute; + top: 0; + left: 0; + height: 100%; + color: $dark-color; + background-color: $primary-color; + } + + &__percent { + position: absolute; + inset: 0; + margin: auto; + width: fit-content; + height: fit-content; + } + + &-chunks { + position: relative; + margin-top: 0.5rem; + width: 100%; + height: 2rem; + display: flex; + border-radius: 0.25rem; + overflow: hidden; + background-color: $light-color; + + & .chunk { + width: 100%; + + &[data-chunkVal='0'] { + background-color: color.adjust($primary-light-color, $alpha: -0.8); + } + + &[data-chunkVal='1'] { + background-color: $red-color; + } + + &[data-chunkVal='2'] { + background-color: $primary-color; + } + + &[data-chunkVal='3'] { + background-color: $golden-yellow-color; + } + } + + &__percent { + position: absolute; + inset: 0; + margin: auto; + width: fit-content; + height: fit-content; + } + } +} + + +@use '../abstracts' as *; + +/* Status Bar Styles */ +.statusbar { + @include flex($justify: space-between, $align: center); + height: 28px; + background-color: $dark-color; + border-top: 1px solid #2e2e38; + padding: 0 1rem; + font-size: 0.8rem; + color: #94a3b8; + z-index: 100; + box-sizing: border-box; + user-select: none; + flex-shrink: 0; + + &-left { + @include flex($align: center, $gap: 0.75rem); + } + + &-right { + @include flex($align: center, $gap: 0.75rem); + } + + &-item { + @include flex($align: center); + } + + &-divider { + width: 1px; + height: 14px; + background-color: #2e2e38; + } +} + +.status-bullet { + width: 8px; + height: 8px; + border-radius: 50%; + display: inline-block; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.5); +} + +@media (max-width: 768px) { + .statusbar { + height: 22px !important; + padding: 0 0.4rem !important; + font-size: 0.72rem !important; + + .statusbar-left, + .statusbar-right { + gap: 0.35rem !important; + } + + .statusbar-label, + .statusbar-total-bytes, + .statusbar-extra-info { + display: none !important; + } + + .statusbar-divider { + height: 10px !important; + margin: 0 0.1rem !important; + } + } +} + + +