This commit is contained in:
parent
c65967acd6
commit
cba741a94f
4 changed files with 80 additions and 8 deletions
|
|
@ -93,6 +93,9 @@ function cleanTextForTTS(text) {
|
|||
clean = clean.replace(/~~(.+?)~~/g, '$1'); // strikethrough
|
||||
clean = clean.replace(/`(.+?)`/g, '$1'); // code
|
||||
|
||||
// Remove markdown heading markers (# ## ### etc.)
|
||||
clean = clean.replace(/^#{1,6}\s*/gm, '');
|
||||
|
||||
// Remove URLs completely (don't read them aloud)
|
||||
clean = clean.replace(/https?:\/\/\S+/g, '');
|
||||
|
||||
|
|
|
|||
|
|
@ -176,6 +176,15 @@
|
|||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
// Replace <marquee><h1>:sticker: with marquee-wrapped h1 sticker
|
||||
msg = msg.replace(/^<marquee><h1>:(\w+):$/gim, (match, stickerName) => {
|
||||
const stickerKey = stickerName.toLowerCase();
|
||||
if (stickersMap[stickerKey]) {
|
||||
return `<marquee><h1><img src="${stickersMap[stickerKey]}" alt="${stickerName}" title="${stickerName}" data-sticker="${stickerName}" class="sticker-img" onerror="this.onerror=null;this.src='/dlive2.gif';" /></h1></marquee>`;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
}
|
||||
|
||||
// Replace inline :sticker: with img tags (all modes with stickers enabled)
|
||||
|
|
@ -203,7 +212,7 @@
|
|||
|
||||
// Step 5: Sanitize with DOMPurify - allow img tags and safe CSS
|
||||
html = DOMPurify.sanitize(html, {
|
||||
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'img', 'br', 'strong', 'em', 'code', 'pre', 'del', 'span'],
|
||||
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'img', 'br', 'strong', 'em', 'code', 'pre', 'del', 'span', 'marquee'],
|
||||
ALLOWED_ATTR: ['src', 'alt', 'title', 'class', 'style', 'data-sticker', 'onerror'],
|
||||
FORBID_TAGS: ['a', 'button', 'script'],
|
||||
ALLOW_DATA_ATTR: false
|
||||
|
|
@ -901,6 +910,25 @@
|
|||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/* Marquee stickers */
|
||||
.message-content :global(marquee) {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.message-content :global(marquee h1) {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.message-content :global(marquee h1 img) {
|
||||
max-width: 475px;
|
||||
max-height: none;
|
||||
height: auto;
|
||||
width: auto;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.mod-menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
|
|
|||
|
|
@ -251,20 +251,58 @@
|
|||
}
|
||||
});
|
||||
|
||||
// Watch for content size changes (e.g., when images load)
|
||||
// Watch for images loading inside messages (e.g., stickers)
|
||||
// This ensures autoscroll works even when images finish loading after message is added
|
||||
let resizeObserver;
|
||||
let mutationObserver;
|
||||
if (messagesContainer) {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
scrollToBottom();
|
||||
// Track images we've already added listeners to
|
||||
const trackedImages = new WeakSet();
|
||||
|
||||
const attachImageLoadListeners = (container) => {
|
||||
const images = container.querySelectorAll('img');
|
||||
images.forEach(img => {
|
||||
if (!trackedImages.has(img)) {
|
||||
trackedImages.add(img);
|
||||
if (!img.complete) {
|
||||
img.addEventListener('load', scrollToBottom, { once: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Attach to existing images
|
||||
attachImageLoadListeners(messagesContainer);
|
||||
|
||||
// Watch for new images being added
|
||||
mutationObserver = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
if (mutation.type === 'childList') {
|
||||
mutation.addedNodes.forEach(node => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
attachImageLoadListeners(node);
|
||||
// Also check if the node itself is an image
|
||||
if (node.tagName === 'IMG' && !trackedImages.has(node)) {
|
||||
trackedImages.add(node);
|
||||
if (!node.complete) {
|
||||
node.addEventListener('load', scrollToBottom, { once: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mutationObserver.observe(messagesContainer, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
resizeObserver.observe(messagesContainer);
|
||||
}
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
if (mutationObserver) {
|
||||
mutationObserver.disconnect();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue