updating from dev

This commit is contained in:
2026-05-04 04:24:18 +02:00
parent 46afca976d
commit 2f1e42343b
76 changed files with 5554 additions and 2527 deletions

View File

@@ -15,7 +15,7 @@
const MAX_VISIBLE_MSGS = 100;
const RATE_LIMIT_MS = 800;
let isMinimized = localStorage.getItem('f0ck_chat_minimized') === '1';
let isMinimized = localStorage.getItem('f0ck_chat_minimized') !== '0';
let isClosed = localStorage.getItem('f0ck_chat_closed') === '1';
let lastSent = 0;
let customEmojis = null; // name → url
@@ -224,6 +224,13 @@
`</div>` +
`</a>`;
});
// 6d.1 Vocaroo URLs → iframe embed
const vocarooRegex = /https?:\/\/(?:www\.)?(?:voca\.ro|vocaroo\.com)\/([a-zA-Z0-9_-]+)[^\s]*/gi;
html = html.replace(vocarooRegex, (match, vocarooId) => {
if (['upload', 'contact', 'privacy', 'tos', 'about'].includes(vocarooId.toLowerCase())) return match;
return `<span class="vocaroo-embed-wrap gchat-embed-audio"><iframe src="https://vocaroo.com/embed/${vocarooId}?autoplay=0" width="300" height="60" frameborder="0" allow="autoplay"></iframe></span>`;
});
// 6d.5 Same-site item page links → post preview card (resolved async)
// Only catches /digits paths — direct media file URLs are handled by 6a-6c & 6e.
@@ -1133,8 +1140,18 @@
panel.style.top = newY + 'px';
};
const onEnd = () => {
const curY = parseInt(panel.style.top);
localStorage.setItem('f0ck_chat_float_x', parseInt(panel.style.left));
localStorage.setItem('f0ck_chat_float_y', parseInt(panel.style.top));
localStorage.setItem('f0ck_chat_float_y', curY);
if (isMinimized) {
const topBound = getTopBound();
const bottomBound = window.innerHeight;
const distToTop = Math.abs(curY - topBound);
const distToBottom = Math.abs(bottomBound - (curY + 42));
localStorage.setItem('f0ck_chat_anchor_top', distToTop < distToBottom ? '1' : '0');
}
document.removeEventListener('mousemove', mouseMove);
document.removeEventListener('mouseup', mouseEnd);
document.removeEventListener('touchmove', touchMove);
@@ -1169,28 +1186,59 @@
// Minimize toggle — in float mode anchor to bottom edge in both directions
function toggleMinimized() {
const willExpand = isMinimized; // about to expand
if (!willExpand && isFloating) {
// About to minimize: capture bottom edge BEFORE shrinking
if (isFloating) {
const r = panel.getBoundingClientRect();
const curBottom = r.top + r.height;
setMinimized(true);
// After CSS applies 42px height, shift top so bottom edge is preserved
requestAnimationFrame(() => {
const newTop = Math.min(window.innerHeight - 42, Math.max(getTopBound(), curBottom - 42));
panel.style.top = newTop + 'px';
localStorage.setItem('f0ck_chat_float_y', newTop);
});
} else {
setMinimized(!isMinimized);
if (willExpand && isFloating) {
// Expanding: shift top UP to maintain bottom edge
const topBound = getTopBound();
const bottomBound = window.innerHeight;
if (!willExpand) {
// About to minimize: decide anchor based on proximity
const distToTop = Math.abs(r.top - topBound);
const distToBottom = Math.abs(bottomBound - (r.top + r.height));
const anchorTop = distToTop < distToBottom;
localStorage.setItem('f0ck_chat_anchor_top', anchorTop ? '1' : '0');
const curTop = r.top;
const curBottom = r.top + r.height;
setMinimized(true);
requestAnimationFrame(() => {
const fullH = panel.getBoundingClientRect().height;
const curTop = parseInt(panel.style.top) || 0;
const newTop = Math.max(getTopBound(), curTop - (fullH - 42));
let newTop;
if (anchorTop) {
// Anchor to top: keep current top
newTop = Math.max(topBound, curTop);
} else {
// Anchor to bottom: keep current bottom (existing behavior)
newTop = Math.min(bottomBound - 42, Math.max(topBound, curBottom - 42));
}
panel.style.top = newTop + 'px';
localStorage.setItem('f0ck_chat_float_y', newTop);
});
} else {
// Expanding: use saved anchor or default to bottom if not set
const wasAnchorTop = localStorage.getItem('f0ck_chat_anchor_top') === '1';
setMinimized(false);
requestAnimationFrame(() => {
const fullH = panel.getBoundingClientRect().height;
const curTop = parseInt(panel.style.top) || 0;
let newTop;
if (wasAnchorTop) {
// Expanding from top anchor: top stays same
newTop = Math.max(topBound, curTop);
} else {
// Expanding from bottom anchor: shift top UP (existing behavior)
newTop = Math.max(topBound, curTop - (fullH - 42));
}
panel.style.top = newTop + 'px';
localStorage.setItem('f0ck_chat_float_y', newTop);
});
}
} else {
setMinimized(!isMinimized);
if (!isMinimized) {
// Normal docked expand
loadHistory();
}
}
}
@@ -1341,10 +1389,10 @@
});
// ── Online users bar ─────────────────────────────────────────────────
function renderOnline(users) {
function renderOnline(users, guestCount = 0) {
const el = document.getElementById('gchat-online');
if (!el) return;
if (!users || users.length === 0) {
if ((!users || users.length === 0) && guestCount === 0) {
el.innerHTML = '';
el.style.display = 'none';
return;
@@ -1361,11 +1409,16 @@
return `<img src="${src}" class="gchat-online-avatar" title="${name}" alt="${name}" loading="lazy" ${colorStyle}>`;
}).join('');
const extraPill = extra > 0 ? `<span class="gchat-online-extra">+${extra}</span>` : '';
const countLabel = `<span class="gchat-online-count">${users.length} online</span>`;
let countText = `${users.length} online`;
if (guestCount > 0 && (window.f0ckSession.is_admin || window.f0ckSession.is_moderator)) {
countText += ` (${guestCount} guests)`;
}
const countLabel = `<span class="gchat-online-count">${countText}</span>`;
el.innerHTML = `<div class="gchat-online-inner">${countLabel}<div class="gchat-online-avatars">${avatarHTML}${extraPill}</div></div>`;
}
document.addEventListener('f0ck:global_chat_presence', (e) => {
renderOnline(e.detail?.users || []);
renderOnline(e.detail?.users || [], e.detail?.guestCount || 0);
});
// Event delegation: reply + admin delete buttons inside #gchat-messages