- 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 -4
View File
@@ -67,7 +67,7 @@ input, button { font-family: inherit; }
flex-direction: column;
width: 100%;
height: 100%;
min-height: 360px;
min-height: 0;
background: var(--bg0);
color: var(--t0);
font-family: var(--ui);
@@ -76,6 +76,8 @@ input, button { font-family: inherit; }
border: 1px solid var(--bd);
border-radius: var(--r2);
overflow: hidden;
overscroll-behavior: contain;
contain: layout paint;
position: relative;
-webkit-font-smoothing: antialiased;
}
@@ -222,6 +224,7 @@ input, button { font-family: inherit; }
/* ─── Body area ──────────────────────────────────────────────────────────── */
.cc-body {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
@@ -629,7 +632,10 @@ input, button { font-family: inherit; }
/* Messages */
.cc-messages {
flex: 1;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-gutter: stable;
display: flex;
flex-direction: column;
padding: 6px 0 4px;
@@ -1239,14 +1245,15 @@ input, button { font-family: inherit; }
.cc-auth-wrap { align-items: flex-start; }
}
/* ─── Full-page (index.html) embed ───────────────────────────────────────── */
html, body {
/* ─── Full-page (index.html) shell ───────────────────────────────────────── */
html.cc-fullpage-document,
body.cc-fullpage {
width: 100%; height: 100%;
margin: 0; padding: 0;
overflow: hidden;
}
#app {
body.cc-fullpage #app {
width: 100%; height: 100%;
}
+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);
});