Sync expand state between the two chat modes, and soften the mark

To the operator there is one box on screen, so expanding while reading the assistant and finding the mesh collapsed reads as the card forgetting.
This commit is contained in:
Gmer4Lfe
2026-08-17 17:56:32 -04:00
parent 74bd40ba78
commit 2e646f82b4
2 changed files with 55 additions and 13 deletions
+2 -2
View File
@@ -162,8 +162,8 @@ function vv_ai_chat_assets(): void {
.vv-ai-chat-wrap { background:#0b0b0b; border-radius:6px; } .vv-ai-chat-wrap { background:#0b0b0b; border-radius:6px; }
.vv-ai-chat-wrap::before { .vv-ai-chat-wrap::before {
content:''; position:absolute; inset:0; z-index:0; pointer-events:none; content:''; position:absolute; inset:0; z-index:0; pointer-events:none;
background:url('/plugins/varaverk/icons/varaverk.png') center/min(78%,460px) no-repeat; background:url('/plugins/varaverk/icons/varaverk.png') center 58%/min(84%,520px) no-repeat;
opacity:.05; filter:grayscale(1); opacity:.032; filter:grayscale(1);
} }
.vv-ai-empty { color:#3a3a3a; font-size:12px; text-align:center; padding:60px 20px; line-height:1.7; } .vv-ai-empty { color:#3a3a3a; font-size:12px; text-align:center; padding:60px 20px; line-height:1.7; }
.vv-ai-msg { margin-bottom:16px; } .vv-ai-msg { margin-bottom:16px; }
+53 -11
View File
@@ -260,8 +260,8 @@ function vv_nc_pane_markup(string $prefix, bool $meshDefault = false,
it can carry the badge without competing with the text. --> it can carry the badge without competing with the text. -->
<div style="position:relative;"> <div style="position:relative;">
<img src="/plugins/varaverk/icons/varaverk.png" alt="" aria-hidden="true" <img src="/plugins/varaverk/icons/varaverk.png" alt="" aria-hidden="true"
style="position:absolute;left:50%;top:50%;transform:translate(-50%,-50%); style="position:absolute;left:50%;top:58%;transform:translate(-50%,-50%);
width:min(88%,460px);opacity:.055;pointer-events:none;filter:grayscale(1); width:min(94%,520px);opacity:.038;pointer-events:none;filter:grayscale(1);
z-index:0;"> z-index:0;">
<div id="vv-nc-log" <div id="vv-nc-log"
style="position:relative;z-index:1;height:var(--vv-nc-h,300px);overflow-y:auto; style="position:relative;z-index:1;height:var(--vv-nc-h,300px);overflow-y:auto;
@@ -515,25 +515,46 @@ function vvNcRender() {
// Expand/collapse, mirroring the assistant's control down to the glyph swap and the lit state. // Expand/collapse, mirroring the assistant's control down to the glyph swap and the lit state.
// Both heights are carried on the pane as data attributes, so the card's own mount options decide // Both heights are carried on the pane as data attributes, so the card's own mount options decide
// how big "more room" is rather than this file inventing a number. // how big "more room" is rather than this file inventing a number.
let _vvNcBig = false; // One card, one size. Expanding while reading the assistant and finding the mesh collapsed on
function vvNcGrow() { // switching — or the reverse — reads as the card forgetting, because to the operator there is only
// one box on screen. The two controls therefore drive each other.
//
// _vvNcSyncing guards the round trip: each side reacts to the other by clicking its button, and
// without the flag the first press would ping-pong.
let _vvNcBig = false, _vvNcSyncing = false;
function _vvNcApplyBig() {
const pane = document.getElementById(_vvNcPrefix + '-pane-mesh'); const pane = document.getElementById(_vvNcPrefix + '-pane-mesh');
const btn = document.getElementById('vv-nc-grow'); const btn = document.getElementById('vv-nc-grow');
if (!pane) return; if (pane) pane.style.setProperty('--vv-nc-h',
_vvNcBig = !_vvNcBig; _vvNcBig ? (pane.dataset.hTall || pane.dataset.h) : pane.dataset.h);
const h = _vvNcBig ? (pane.dataset.hTall || pane.dataset.h) : pane.dataset.h;
pane.style.setProperty('--vv-nc-h', h);
if (btn) { if (btn) {
btn.textContent = _vvNcBig ? '⤡' : '⤢'; btn.textContent = _vvNcBig ? '⤡' : '⤢';
btn.title = _vvNcBig ? 'Back to the smaller view' : 'Give the conversation more room'; btn.title = _vvNcBig ? 'Back to the smaller view' : 'Give the conversation more room';
btn.classList.toggle('vv-ai-grow-on', _vvNcBig); btn.classList.toggle('vv-ai-grow-on', _vvNcBig);
} }
try { localStorage.setItem('vvNcBig:' + _vvNcPrefix, _vvNcBig ? '1' : '0'); } catch (_) {}
// Growing reveals rows that were scrolled out of view; land where the reader expects.
const box = document.getElementById('vv-nc-log'); const box = document.getElementById('vv-nc-log');
if (box && document.getElementById('vv-nc-follow')?.checked) box.scrollTop = box.scrollHeight; if (box && document.getElementById('vv-nc-follow')?.checked) box.scrollTop = box.scrollHeight;
} }
// fromAi: the assistant's control moved first and has already resized itself, so mirror its state
// rather than toggling and pushing back.
function vvNcGrow(fromAi) {
if (fromAi === undefined) _vvNcBig = !_vvNcBig;
else _vvNcBig = !!fromAi;
_vvNcApplyBig();
try { localStorage.setItem('vvNcBig:' + _vvNcPrefix, _vvNcBig ? '1' : '0'); } catch (_) {}
// Push the change onto the assistant, unless it is the one that sent it.
if (fromAi === undefined && !_vvNcSyncing) {
const aiBtn = document.getElementById(_vvNcPrefix + '-grow');
if (aiBtn) {
const aiBig = aiBtn.classList.contains('vv-ai-grow-on');
if (aiBig !== _vvNcBig) { _vvNcSyncing = true; aiBtn.click(); _vvNcSyncing = false; }
}
}
}
function vvNcEmoji(sel) { function vvNcEmoji(sel) {
const t = document.getElementById('vv-nc-text'); const t = document.getElementById('vv-nc-text');
if (t && sel.value) { t.value += sel.value; t.focus(); } if (t && sel.value) { t.value += sel.value; t.focus(); }
@@ -622,8 +643,29 @@ function vvNcInit(prefix, meshDefault) {
} catch (_) {} } catch (_) {}
} }
_vvNcLoadPrefs(); _vvNcLoadPrefs();
// The assistant's control is the other half of the same size. Listening rather than wrapping its
// handler: VvAiChat owns that button, and reading the class it sets afterwards is the one signal
// that cannot drift from what it actually did.
const aiBtn = document.getElementById(prefix + '-grow');
if (aiBtn) {
aiBtn.addEventListener('click', () => {
if (_vvNcSyncing) return;
// After its own handler has run, so the class reflects the new state.
setTimeout(() => vvNcGrow(aiBtn.classList.contains('vv-ai-grow-on')), 0);
});
}
// Restore the expanded state before the first paint, so the card does not visibly resize. // Restore the expanded state before the first paint, so the card does not visibly resize.
try { if (localStorage.getItem('vvNcBig:' + prefix) === '1') vvNcGrow(); } catch (_) {} try {
if (localStorage.getItem('vvNcBig:' + prefix) === '1') {
_vvNcBig = true;
_vvNcApplyBig();
// Bring the assistant along, so both halves start the session agreeing.
if (aiBtn && !aiBtn.classList.contains('vv-ai-grow-on')) {
_vvNcSyncing = true; aiBtn.click(); _vvNcSyncing = false;
}
}
} catch (_) {}
vvNcMode(want); vvNcMode(want);
vvNcChans(); vvNcChans();
_vvNcSchedule(); _vvNcSchedule();