- Implemented.

Rebuilt embed-example.html with inline, full-screen, and popup examples.
Fixed host-page/frame scrolling and contained reply navigation.
New logins now transactionally replace prior sessions; displaced clients 
return to login.
Added per-user session/IP locking with automatic SQLite migration.
Added modular Spam Control dashboard for locks, honeypot, and CAPTCHA 
settings.
Moved CAPTCHA controls out of Plans & Platform.
This commit is contained in:
Ty Clifford
2026-06-09 18:54:25 -04:00
parent 5ca55bf7c1
commit 36c488ca1e
12 changed files with 599 additions and 169 deletions
+11 -1
View File
@@ -55,6 +55,7 @@
});
const data = await response.json().catch(() => ({ error: 'The server returned an invalid response' }));
if (!response.ok && !data.error) data.error = 'Request failed';
if (data && typeof data === 'object') data.http_status = response.status;
return data;
}
function feature(key, fallback = false) {
@@ -631,6 +632,11 @@
if (!state.poll.active || state.poll.generation !== generation || state.view !== 'chat') {
return { ok: false, hasMore: false };
}
if (result?.http_status === 401 || result?.http_status === 423) {
await logout(false);
toast('Your session ended or was replaced. Sign in again.', 'info');
return { ok: false, hasMore: false };
}
if (!result || result.error || !Array.isArray(result.messages)) {
throw new Error(result?.error || 'Invalid poll response');
}
@@ -798,7 +804,11 @@
const parentId = Number(event.currentTarget.dataset.parentId);
const parent = $(`.cc-msg[data-id="${parentId}"]`);
if (!parent) return toast('Original message is outside the current view', 'info');
parent.scrollIntoView({ behavior: 'smooth', block: 'center' });
const list = $('#messages');
if (list) {
const top = parent.offsetTop - ((list.clientHeight - parent.offsetHeight) / 2);
list.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
}
parent.classList.add('cc-msg-focus');
setTimeout(() => parent.classList.remove('cc-msg-focus'), 1200);
});