This commit is contained in:
2026-08-19 23:01:06 +02:00
parent 3359e9f0d8
commit 11a4535ca9
5 changed files with 69 additions and 13 deletions

View File

@@ -3110,6 +3110,7 @@ body.layout-legacy .scroll-to-bottom svg {
isolation: isolate; isolation: isolate;
padding-bottom: 25px; padding-bottom: 25px;
/* Room for absolute permalink */ /* Room for absolute permalink */
overflow: hidden;
} }
.comment > * { .comment > * {
@@ -3128,14 +3129,19 @@ body.layout-legacy .scroll-to-bottom svg {
max-height: var(--comment-banner-max-height, 300px); max-height: var(--comment-banner-max-height, 300px);
background-image: var(--author-banner, none); background-image: var(--author-banner, none);
background-position: var(--author-banner-position, center top); background-position: var(--author-banner-position, center top);
background-size: var(--author-banner-size, cover); background-size: var(--author-banner-size, 100% auto);
background-repeat: var(--author-banner-repeat, repeat); background-repeat: var(--author-banner-repeat, no-repeat);
opacity: var(--author-banner-opacity, 0.25); opacity: var(--author-banner-opacity, 0.25);
z-index: 0; z-index: 0;
border-radius: inherit; border-radius: inherit;
pointer-events: none; pointer-events: none;
-webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%); -webkit-mask-image: var(--comment-banner-mask, linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 100%));
mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%); mask-image: var(--comment-banner-mask, linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 100%));
}
.comment.tall-comment::before {
-webkit-mask-image: var(--comment-banner-mask, linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 50%));
mask-image: var(--comment-banner-mask, linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 50%));
} }
.comment.deleted { .comment.deleted {

View File

@@ -472,8 +472,8 @@ class CommentSystem {
if (el && data.banner_file && data.banner_file !== 'null') { if (el && data.banner_file && data.banner_file !== 'null') {
el.style.setProperty('--author-banner', `url('/a/${data.banner_file}')`); el.style.setProperty('--author-banner', `url('/a/${data.banner_file}')`);
el.style.setProperty('--author-banner-position', data.banner_position === 'center' ? 'center top' : (data.banner_position || 'center top')); el.style.setProperty('--author-banner-position', data.banner_position === 'center' ? 'center top' : (data.banner_position || 'center top'));
el.style.setProperty('--author-banner-size', data.banner_size || 'cover'); el.style.setProperty('--author-banner-size', (data.banner_size && data.banner_size !== 'cover') ? data.banner_size : '100% auto');
el.style.setProperty('--author-banner-repeat', data.banner_repeat || 'repeat'); el.style.setProperty('--author-banner-repeat', 'no-repeat');
} }
if (data.body && data.body.endsWith('\u2026')) { if (data.body && data.body.endsWith('\u2026')) {
console.log('[handleLiveComment] duplicate+truncated, ensuring button for', data.id); console.log('[handleLiveComment] duplicate+truncated, ensuring button for', data.id);
@@ -615,8 +615,8 @@ class CommentSystem {
if (json.comment && json.comment.banner_file) { if (json.comment && json.comment.banner_file) {
el.style.setProperty('--author-banner', `url('/a/${json.comment.banner_file}')`); el.style.setProperty('--author-banner', `url('/a/${json.comment.banner_file}')`);
el.style.setProperty('--author-banner-position', json.comment.banner_position === 'center' ? 'center top' : (json.comment.banner_position || 'center top')); el.style.setProperty('--author-banner-position', json.comment.banner_position === 'center' ? 'center top' : (json.comment.banner_position || 'center top'));
el.style.setProperty('--author-banner-size', json.comment.banner_size || 'cover'); el.style.setProperty('--author-banner-size', (json.comment.banner_size && json.comment.banner_size !== 'cover') ? json.comment.banner_size : '100% auto');
el.style.setProperty('--author-banner-repeat', json.comment.banner_repeat || 'repeat'); el.style.setProperty('--author-banner-repeat', 'no-repeat');
} }
contentEl.dataset.raw = fullContent; contentEl.dataset.raw = fullContent;
@@ -1537,8 +1537,8 @@ class CommentSystem {
if (incoming && incoming.banner_file && incoming.banner_file !== 'null') { if (incoming && incoming.banner_file && incoming.banner_file !== 'null') {
el.style.setProperty('--author-banner', `url('/a/${incoming.banner_file}')`); el.style.setProperty('--author-banner', `url('/a/${incoming.banner_file}')`);
el.style.setProperty('--author-banner-position', incoming.banner_position === 'center' ? 'center top' : (incoming.banner_position || 'center top')); el.style.setProperty('--author-banner-position', incoming.banner_position === 'center' ? 'center top' : (incoming.banner_position || 'center top'));
el.style.setProperty('--author-banner-size', incoming.banner_size || 'cover'); el.style.setProperty('--author-banner-size', (incoming.banner_size && incoming.banner_size !== 'cover') ? incoming.banner_size : '100% auto');
el.style.setProperty('--author-banner-repeat', incoming.banner_repeat || 'repeat'); el.style.setProperty('--author-banner-repeat', 'no-repeat');
} }
} }
}); });
@@ -2210,7 +2210,7 @@ class CommentSystem {
const bannerEnabled = window.f0ckCommentBannerEnabled !== false && window.f0ckSession?.comment_banner_enabled !== false; const bannerEnabled = window.f0ckCommentBannerEnabled !== false && window.f0ckSession?.comment_banner_enabled !== false;
const bannerStyle = (bannerEnabled && comment.banner_file && comment.banner_file !== 'null') const bannerStyle = (bannerEnabled && comment.banner_file && comment.banner_file !== 'null')
? `style="--author-banner: url('/a/${comment.banner_file}'); --author-banner-position: ${comment.banner_position === 'center' ? 'center top' : (comment.banner_position || 'center top')}; --author-banner-size: ${comment.banner_size || 'cover'}; --author-banner-repeat: ${comment.banner_repeat || 'repeat'};"` ? `style="--author-banner: url('/a/${comment.banner_file}'); --author-banner-position: ${comment.banner_position === 'center' ? 'center top' : (comment.banner_position || 'center top')}; --author-banner-size: ${(comment.banner_size && comment.banner_size !== 'cover') ? comment.banner_size : '100% auto'}; --author-banner-repeat: no-repeat;"`
: ''; : '';
return `<div class="${commentClass} ${isDeleted ? 'deleted' : ''} ${isPinned ? 'pinned' : ''}" id="c${comment.id}" ${bannerStyle}><div class="comment-avatar">${comment.username ? `<a href="/user/${comment.username}">` : ''}<img src="${comment.avatar_file ? `/a/${comment.avatar_file}` : (comment.avatar ? `/t/${comment.avatar}.webp` : '/a/default.png')}">${comment.username ? `</a>` : ''}</div><div class="comment-body"><div class="comment-header"><div class="comment-header-left">${pinnedBadge}${comment.username ? `<a href="/user/${comment.username}" class="comment-author" tooltip="ID: ${comment.user_id}" ${comment.username_color ? `style="color: ${comment.username_color}"` : ''}>${this.escapeHtml(comment.display_name || comment.username)}</a>` : '<span class="comment-author">System</span>'}${contextMarker}${backlinkHtml}</div><a href="#c${comment.id}" class="comment-time timeago" tooltip="${fullDate}" data-iso="${isoDate}" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">${timeAgo}</a></div><div class="comment-content" data-raw="${this.escapeHtml(comment.content)}">${content}</div>${this.renderCommentAttachments(comment.files, comment.content)}${this.renderCommentPoll(comment.poll, comment.id, comment.username)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${!isDeleted && currentUserId ? `<button class="reply-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Reply"><i class="fa-solid fa-reply"></i></button><button class="quote-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Quote with Text"><i class="fa-solid fa-quote-left"></i></button><button class="report-comment-btn" data-id="${comment.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><i class="fa-solid fa-triangle-exclamation"></i></button>` : ''}${adminButtons}${userDeleteButton}</div></div></div></div><a href="#c${comment.id}" class="comment-permalink" title="Permalink" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">#${comment.id}</a></div>${repliesHtml}`; return `<div class="${commentClass} ${isDeleted ? 'deleted' : ''} ${isPinned ? 'pinned' : ''}" id="c${comment.id}" ${bannerStyle}><div class="comment-avatar">${comment.username ? `<a href="/user/${comment.username}">` : ''}<img src="${comment.avatar_file ? `/a/${comment.avatar_file}` : (comment.avatar ? `/t/${comment.avatar}.webp` : '/a/default.png')}">${comment.username ? `</a>` : ''}</div><div class="comment-body"><div class="comment-header"><div class="comment-header-left">${pinnedBadge}${comment.username ? `<a href="/user/${comment.username}" class="comment-author" tooltip="ID: ${comment.user_id}" ${comment.username_color ? `style="color: ${comment.username_color}"` : ''}>${this.escapeHtml(comment.display_name || comment.username)}</a>` : '<span class="comment-author">System</span>'}${contextMarker}${backlinkHtml}</div><a href="#c${comment.id}" class="comment-time timeago" tooltip="${fullDate}" data-iso="${isoDate}" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">${timeAgo}</a></div><div class="comment-content" data-raw="${this.escapeHtml(comment.content)}">${content}</div>${this.renderCommentAttachments(comment.files, comment.content)}${this.renderCommentPoll(comment.poll, comment.id, comment.username)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${!isDeleted && currentUserId ? `<button class="reply-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Reply"><i class="fa-solid fa-reply"></i></button><button class="quote-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Quote with Text"><i class="fa-solid fa-quote-left"></i></button><button class="report-comment-btn" data-id="${comment.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><i class="fa-solid fa-triangle-exclamation"></i></button>` : ''}${adminButtons}${userDeleteButton}</div></div></div></div><a href="#c${comment.id}" class="comment-permalink" title="Permalink" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">#${comment.id}</a></div>${repliesHtml}`;
@@ -4850,3 +4850,44 @@ document.addEventListener('f0ck:contentLoaded', () => {
// If f0ck.js uses custom navigation without valid events, we might need MutationObserver or hook into `getContent` // If f0ck.js uses custom navigation without valid events, we might need MutationObserver or hook into `getContent`
// Looking at f0ck.js, it seems to just replace innerHTML. // Looking at f0ck.js, it seems to just replace innerHTML.
(function initCommentHeightObserver() {
if (typeof window === 'undefined') return;
if (window.ResizeObserver) {
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const height = entry.contentRect ? entry.contentRect.height : entry.target.offsetHeight;
if (height > 90) {
entry.target.classList.add('tall-comment');
} else {
entry.target.classList.remove('tall-comment');
}
}
});
const observeAll = (root = document) => {
root.querySelectorAll('.comment').forEach(el => observer.observe(el));
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => observeAll());
} else {
observeAll();
}
if (window.MutationObserver) {
const mutObs = new MutationObserver(mutations => {
for (const m of mutations) {
m.addedNodes.forEach(node => {
if (node.nodeType === 1) {
if (node.classList && node.classList.contains('comment')) observer.observe(node);
if (node.querySelectorAll) node.querySelectorAll('.comment').forEach(el => observer.observe(el));
}
});
}
});
mutObs.observe(document.body, { childList: true, subtree: true });
}
}
})();

View File

@@ -549,8 +549,13 @@
} }
const bannerEnabled = window.f0ckCommentBannerEnabled !== false && window.f0ckSession?.comment_banner_enabled !== false;
const bannerStyle = (bannerEnabled && c.banner_file && c.banner_file !== 'null')
? `style="--author-banner: url('/a/${c.banner_file}'); --author-banner-position: ${c.banner_position === 'center' ? 'center top' : (c.banner_position || 'center top')}; --author-banner-size: ${(c.banner_size && c.banner_size !== 'cover') ? c.banner_size : '100% auto'}; --author-banner-repeat: no-repeat;"`
: '';
return ` return `
<div class="comment" id="sc${c.id}"> <div class="comment" id="sc${c.id}" ${bannerStyle}>
<div class="comment-body"> <div class="comment-body">
<div class="comment-header"> <div class="comment-header">
<div class="comment-header-left"> <div class="comment-header-left">

View File

@@ -416,7 +416,7 @@ if (!window.UserCommentSystem) {
const content = this.renderCommentContent(c.content, itemKey); const content = this.renderCommentContent(c.content, itemKey);
const bannerStyle = (c.banner_file && c.banner_file !== 'null') const bannerStyle = (c.banner_file && c.banner_file !== 'null')
? `style="--author-banner: url('/a/${c.banner_file}'); --author-banner-position: ${c.banner_position === 'center' ? 'center top' : (c.banner_position || 'center top')}; --author-banner-size: ${c.banner_size || 'cover'}; --author-banner-repeat: ${c.banner_repeat || 'repeat'};"` ? `style="--author-banner: url('/a/${c.banner_file}'); --author-banner-position: ${c.banner_position === 'center' ? 'center top' : (c.banner_position || 'center top')}; --author-banner-size: ${(c.banner_size && c.banner_size !== 'cover') ? c.banner_size : '100% auto'}; --author-banner-repeat: no-repeat;"`
: ''; : '';
return `<div class="comment" id="c${c.id}" ${bannerStyle}><div class="comment-avatar"><a href="/${itemKey}"><img src="/t/${c.item_id}.webp" alt=""></a></div><div class="comment-body"><div class="comment-header"><div class="comment-header-left"><span class="comment-author" tooltip="ID: ${c.user_id}" ${this.userColor ? `style="color: ${this.userColor}"` : ''}>${this.username}</span></div><span class="comment-time timeago" title="${fullDate}">${timeAgo}</span></div><div class="comment-content" data-raw="${this.escapeHtml(c.content)}">${content}</div>${this.renderCommentAttachments(c.files, c.content)}${this.renderCommentPoll(c.poll, c.id)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${window.f0ckSession && window.f0ckSession.logged_in ? `<button class="report-comment-btn" data-id="${c.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 512 512" fill="currentColor"><path d="M506.3 417l-213.3-364c-16.3-28-57.5-28-73.8 0l-213.2 364C-10.6 445.1 9.7 480 42.7 480h426.6C502.5 480 522.6 445.1 506.3 417zM256 384c-14.1 0-25.6-11.5-25.6-25.6 0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6C281.6 372.5 270.1 384 256 384zM281.6 264.4c0 14.1-11.5 25.6-25.6 25.6-14.1 0-25.6-11.5-25.6-25.6v-96c0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6V264.4z"/></svg></button>` : ''}</div></div></div></div><a href="/${itemKey}#c${c.id}" class="comment-permalink" title="Permalink">#${c.id}</a></div>`; return `<div class="comment" id="c${c.id}" ${bannerStyle}><div class="comment-avatar"><a href="/${itemKey}"><img src="/t/${c.item_id}.webp" alt=""></a></div><div class="comment-body"><div class="comment-header"><div class="comment-header-left"><span class="comment-author" tooltip="ID: ${c.user_id}" ${this.userColor ? `style="color: ${this.userColor}"` : ''}>${this.username}</span></div><span class="comment-time timeago" title="${fullDate}">${timeAgo}</span></div><div class="comment-content" data-raw="${this.escapeHtml(c.content)}">${content}</div>${this.renderCommentAttachments(c.files, c.content)}${this.renderCommentPoll(c.poll, c.id)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${window.f0ckSession && window.f0ckSession.logged_in ? `<button class="report-comment-btn" data-id="${c.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 512 512" fill="currentColor"><path d="M506.3 417l-213.3-364c-16.3-28-57.5-28-73.8 0l-213.2 364C-10.6 445.1 9.7 480 42.7 480h426.6C502.5 480 522.6 445.1 506.3 417zM256 384c-14.1 0-25.6-11.5-25.6-25.6 0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6C281.6 372.5 270.1 384 256 384zM281.6 264.4c0 14.1-11.5 25.6-25.6 25.6-14.1 0-25.6-11.5-25.6-25.6v-96c0-14.1 11.5-25.6 25.6-25.6 14.1 0 25.6 11.5 25.6 25.6V264.4z"/></svg></button>` : ''}</div></div></div></div><a href="/${itemKey}#c${c.id}" class="comment-permalink" title="Permalink">#${c.id}</a></div>`;

View File

@@ -1042,6 +1042,10 @@ export default (router, tpl) => {
u.user as username, u.user as username,
uo.avatar, uo.avatar,
uo.avatar_file, uo.avatar_file,
uo.banner_file,
uo.banner_position,
uo.banner_size,
uo.banner_repeat,
uo.username_color, uo.username_color,
uo.display_name uo.display_name
FROM comments c FROM comments c