comment preview foobar

This commit is contained in:
2026-07-16 21:16:32 +02:00
parent 7e2ef4fc0f
commit b370510ecd
5 changed files with 49 additions and 16 deletions

View File

@@ -1750,7 +1750,7 @@ class CommentSystem {
const imageRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/${safeS}+\\.(?:jpg|jpeg|png|gif|webp)(?:\\?${safeS}+)?(?:#gif)?))(?![\\)\\]])`, 'gi'); const imageRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/${safeS}+\\.(?:jpg|jpeg|png|gif|webp)(?:\\?${safeS}+)?(?:#gif)?))(?![\\)\\]])`, 'gi');
const rawVideoRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/[^\\s\\[\\]\\(\\)]*\\.(?:mp4|webm|ogv|mov)(?:\\?[^\\s\\[\\]\\(\\)]+)?(?:#gif)?))`, 'gi'); const rawVideoRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/[^\\s\\[\\]\\(\\)]*\\.(?:mp4|webm|ogv|mov)(?:\\?[^\\s\\[\\]\\(\\)]+)?(?:#gif)?))`, 'gi');
const rawAudioRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/[^\\s\\[\\]\\(\\)]*\\.(?:mp3|ogg|wav|flac|aac|opus|m4a)(?:\\?[^\\s\\[\\]\\(\\)]+)?))`, 'gi'); const rawAudioRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/[^\\s\\[\\]\\(\\)]*\\.(?:mp3|ogg|wav|flac|aac|opus|m4a)(?:\\?[^\\s\\[\\]\\(\\)]+)?))`, 'gi');
const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]/g; const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]\(\/user\/([^)]+)\)|\[@([^\]]+)\]/g;
// Line-by-line processing: handles quotes, mentions, images, and basic markdown safely // Line-by-line processing: handles quotes, mentions, images, and basic markdown safely
const renderedLines = escaped.split('\n').map(line => { const renderedLines = escaped.split('\n').map(line => {
@@ -1779,8 +1779,8 @@ class CommentSystem {
// Handle Mentions — use placeholders so the markdown escape pass (step 3) // Handle Mentions — use placeholders so the markdown escape pass (step 3)
// never corrupts usernames that contain underscores or dots. // never corrupts usernames that contain underscores or dots.
const mentionStore = []; const mentionStore = [];
processedLine = processedLine.replace(mentionRegex, (match, g1, g2) => { processedLine = processedLine.replace(mentionRegex, (match, g1, g2, g3, g4) => {
const user = g1 || g2; const user = g1 || g2 || g4;
const html = `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`; const html = `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`;
const idx = mentionStore.length; const idx = mentionStore.length;
mentionStore.push(html); mentionStore.push(html);

View File

@@ -8507,9 +8507,18 @@ class NotificationSystem {
}; };
// Pre-process mentions before marked (skip greentext lines) // Pre-process mentions before marked (skip greentext lines)
const mentionStore = [];
const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]\(\/user\/([^)]+)\)|\[@([^\]]+)\]/g;
safe = safe.split('\n').map(line => { safe = safe.split('\n').map(line => {
if (line.trimStart().startsWith('>')) return line; if (line.trimStart().startsWith('>')) return line;
return line.replace(/(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])/g, '[@$1](/user/$1)'); return line.replace(mentionRegex, (match, g1, g2, g3, g4) => {
const user = g1 || g2 || g4;
const html = `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`;
const idx = mentionStore.length;
mentionStore.push(html);
return `\x02MNT${idx}\x03`;
});
}).join('\n'); }).join('\n');
content = marked.parse(safe, { content = marked.parse(safe, {
@@ -8517,6 +8526,9 @@ class NotificationSystem {
breaks: true, breaks: true,
renderer: renderer renderer: renderer
}); });
// Restore mention placeholders
content = content.replace(/\x02MNT(\d+)\x03/g, (_, i) => mentionStore[+i]);
} catch (e) { } catch (e) {
console.error('Marked error in w0bm.js', e); console.error('Marked error in w0bm.js', e);
// Fallback // Fallback

View File

@@ -640,7 +640,7 @@ if (window.__dmLoaded) {
let escaped = escHtml(normalized).replace(/&gt;/g, ">"); let escaped = escHtml(normalized).replace(/&gt;/g, ">");
// 3. Mentions // 3. Mentions
const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]/g; const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]\(\/user\/([^)]+)\)|\[@([^\]]+)\]/g;
const siteOrigin = window.location.origin; const siteOrigin = window.location.origin;
const renderer = new marked.Renderer(); const renderer = new marked.Renderer();
@@ -719,9 +719,13 @@ if (window.__dmLoaded) {
if (line.length > 10000) return line; if (line.length > 10000) return line;
if (!line.trim()) return '&nbsp;'; if (!line.trim()) return '&nbsp;';
let processedLine = line.replace(mentionRegex, (match, g1, g2) => { const mentionStore = [];
const user = g1 || g2; let processedLine = line.replace(mentionRegex, (match, g1, g2, g3, g4) => {
return `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`; const user = g1 || g2 || g4;
const html = `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`;
const idx = mentionStore.length;
mentionStore.push(html);
return `\x02MNT${idx}\x03`;
}); });
const escapedSiteHost = window.location.host.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const escapedSiteHost = window.location.host.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -735,6 +739,9 @@ if (window.__dmLoaded) {
const escapedAsterisks = processedLine.replace(/\*/g, '\\*'); const escapedAsterisks = processedLine.replace(/\*/g, '\\*');
let rendered = marked.parseInline ? marked.parseInline(escapedAsterisks, { renderer: renderer }) : marked.parse(escapedAsterisks, { renderer: renderer }).replace(/<p>|<\/p>/g, ''); let rendered = marked.parseInline ? marked.parseInline(escapedAsterisks, { renderer: renderer }) : marked.parse(escapedAsterisks, { renderer: renderer }).replace(/<p>|<\/p>/g, '');
// Restore Mentions
rendered = rendered.replace(/\x02MNT(\d+)\x03/g, (_, i) => mentionStore[+i]);
return rendered; return rendered;
}); });

View File

@@ -154,7 +154,7 @@
const domainOrRelative = `(?:(?:https?:\\/\\/|\\/\\/)?(?:${hostsRegexPart})|(?:(?<!\\S)|(?<=\\]))(?=\\/[a-zA-Z0-9_\\-]))`; const domainOrRelative = `(?:(?:https?:\\/\\/|\\/\\/)?(?:${hostsRegexPart})|(?:(?<!\\S)|(?<=\\]))(?=\\/[a-zA-Z0-9_\\-]))`;
const imageRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/${safeS}+\\.(?:jpg|jpeg|png|gif|webp)(?:\\?${safeS}+)?))(?![\\)\\]])`, 'gi'); const imageRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/${safeS}+\\.(?:jpg|jpeg|png|gif|webp)(?:\\?${safeS}+)?))(?![\\)\\]])`, 'gi');
const rawVideoRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/[^\\s\\[\\]\\(\\)]*\\.(?:mp4|webm|ogv|mov)(?:\\?[^\\s\\[\\]\\(\\)]+)?(?:#gif)?))`, 'gi'); const rawVideoRegex = new RegExp(`(?<![\\(\\[])(${domainOrRelative}(?:\\/[^\\s\\[\\]\\(\\)]*\\.(?:mp4|webm|ogv|mov)(?:\\?[^\\s\\[\\]\\(\\)]+)?(?:#gif)?))`, 'gi');
const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]/g; const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]\(\/user\/([^)]+)\)|\[@([^\]]+)\]/g;
const renderer = new marked.Renderer(); const renderer = new marked.Renderer();
renderer.blockquote = function (quote) { renderer.blockquote = function (quote) {
@@ -245,9 +245,13 @@
let processedLine = line; let processedLine = line;
// Handle Mentions // Handle Mentions
processedLine = processedLine.replace(mentionRegex, (match, g1, g2) => { const mentionStore = [];
const user = g1 || g2; processedLine = processedLine.replace(mentionRegex, (match, g1, g2, g3, g4) => {
return `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`; const user = g1 || g2 || g4;
const html = `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`;
const idx = mentionStore.length;
mentionStore.push(html);
return `\x02MNT${idx}\x03`;
}); });
// Handle Comment Context Links (>>ID) // Handle Comment Context Links (>>ID)
@@ -294,6 +298,9 @@
let rendered = marked.parseInline ? marked.parseInline(mdSafe, { renderer: renderer }) : marked.parse(mdSafe, { renderer: renderer }).replace(/<p>|<\/p>/g, ''); let rendered = marked.parseInline ? marked.parseInline(mdSafe, { renderer: renderer }) : marked.parse(mdSafe, { renderer: renderer }).replace(/<p>|<\/p>/g, '');
// Restore Mentions
rendered = rendered.replace(/\x02MNT(\d+)\x03/g, (_, i) => mentionStore[+i]);
// Render emojis ONLY if this is NOT a quote line OR if the user prefers it // Render emojis ONLY if this is NOT a quote line OR if the user prefers it
const quoteEmojis = window.f0ckSession?.quote_emojis === true; const quoteEmojis = window.f0ckSession?.quote_emojis === true;
if (!trimmed.startsWith('>') || quoteEmojis) { if (!trimmed.startsWith('>') || quoteEmojis) {

View File

@@ -221,7 +221,7 @@ if (!window.UserCommentSystem) {
let escaped = this.escapeHtml(content).replace(/&gt;/g, ">"); let escaped = this.escapeHtml(content).replace(/&gt;/g, ">");
// 2. Mentions // 2. Mentions
const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]/g; const mentionRegex = /(?<!\[)@([a-zA-Z0-9_\-\.]+)(?!\])|\[@([^\]]+)\]\(\/user\/([^)]+)\)|\[@([^\]]+)\]/g;
const siteOrigin = window.location.origin; const siteOrigin = window.location.origin;
const renderer = new marked.Renderer(); const renderer = new marked.Renderer();
@@ -301,9 +301,13 @@ if (!window.UserCommentSystem) {
let processedLine = line; let processedLine = line;
// Handle Mentions // Handle Mentions
processedLine = processedLine.replace(mentionRegex, (match, g1, g2) => { const mentionStore = [];
const user = g1 || g2; processedLine = processedLine.replace(mentionRegex, (match, g1, g2, g3, g4) => {
return `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`; const user = g1 || g2 || g4;
const html = `<a href="/user/${encodeURIComponent(user)}" class="mention">@${user}</a>`;
const idx = mentionStore.length;
mentionStore.push(html);
return `\x02MNT${idx}\x03`;
}); });
// Handle Comment Context Links (>>ID) // Handle Comment Context Links (>>ID)
@@ -337,6 +341,9 @@ if (!window.UserCommentSystem) {
const escapedAsterisks = processedLine.replace(/\*/g, '\\*'); const escapedAsterisks = processedLine.replace(/\*/g, '\\*');
let rendered = marked.parseInline ? marked.parseInline(escapedAsterisks, { renderer: renderer }) : marked.parse(escapedAsterisks, { renderer: renderer }).replace(/<p>|<\/p>/g, ''); let rendered = marked.parseInline ? marked.parseInline(escapedAsterisks, { renderer: renderer }) : marked.parse(escapedAsterisks, { renderer: renderer }).replace(/<p>|<\/p>/g, '');
// Restore Mentions
rendered = rendered.replace(/\x02MNT(\d+)\x03/g, (_, i) => mentionStore[+i]);
// Render emojis ONLY if this is NOT a quote line OR if the user prefers it // Render emojis ONLY if this is NOT a quote line OR if the user prefers it
const quoteEmojis = window.f0ckSession?.quote_emojis === true; const quoteEmojis = window.f0ckSession?.quote_emojis === true;
if (!trimmed.startsWith('>') || quoteEmojis) { if (!trimmed.startsWith('>') || quoteEmojis) {