From 4f598f74221db8872ee6825f7a78b8c9e14aae02 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Sun, 9 Aug 2026 11:09:04 -0400 Subject: [PATCH] Hold Conversations and Tokens to the Assistant's height A stretched grid item is measured before it is stretched, so a long card grew the row it was meant to fit inside and then filled it; the ceiling is taken from the Assistant instead. --- Plugin/unraid/css/varaverk.css | 8 +++++- Plugin/unraid/pages/monitor.php | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Plugin/unraid/css/varaverk.css b/Plugin/unraid/css/varaverk.css index ed91489..e1b713d 100644 --- a/Plugin/unraid/css/varaverk.css +++ b/Plugin/unraid/css/varaverk.css @@ -256,7 +256,13 @@ body.vv-fullscreen #displaybox { padding-left: 1rem !important; padding-top: .5r there it inherited a cap from `.vv-card > div { flex:1; overflow-y:auto }`. Out here both are content-driven, so forty saved chats — or a ledger with both hosts and a footer — would make the card the tallest thing in its column and set the height it is supposed to be following. - The h3 pins and the body takes what is left, the same shape the monitor grid above uses. */ + The h3 pins and the body takes what is left, the same shape the monitor grid above uses. + + align-self alone does not finish the job, which is why vvAiRowSync() in monitor.php sets a + max-height on both. A stretched item's own content still feeds grid track sizing — the track + is sized from content first and stretched afterwards — so a card long enough grows the row it + was meant to fit inside, and stretching to that row then changes nothing. The cap is measured + from the Assistant, which is the one card here whose height is its own business. */ #vv-monitor-ai #vv-ai-chats-card, #vv-monitor-ai #vv-ai-tokens-card { align-self: stretch; diff --git a/Plugin/unraid/pages/monitor.php b/Plugin/unraid/pages/monitor.php index 184c3b1..b463473 100644 --- a/Plugin/unraid/pages/monitor.php +++ b/Plugin/unraid/pages/monitor.php @@ -2087,6 +2087,36 @@ function vvRenderTokens() { box.innerHTML = h; } +// ── AI row height ──────────────────────────────────────────────────────────── +// Conversations and Tokens are capped to the Assistant and scroll inside that cap. CSS cannot +// state this on its own: align-self:stretch makes an item fill its grid track, but the track was +// sized from the item's own content first, so a card with a long list simply grows the row and +// then dutifully fills the bigger row. Measuring the Assistant breaks that circle — it is the +// only card in the row whose height is decided by nothing but itself. +// +// Applied only in the eight-column layout. Below 1025px the Assistant drops to a row of its own +// and stops being what these two sit beside, so a cap taken from it would describe nothing. +function vvAiRowSync() { + const asst = document.getElementById('vv-ai-assistant-card'); + const chats = document.getElementById('vv-ai-chats-card'); + const toks = document.getElementById('vv-ai-tokens-card'); + const stats = document.getElementById('vv-ai-stats-card'); + if (!asst || !chats) return; + + if (!window.matchMedia('(min-width: 1025px)').matches) { + chats.style.maxHeight = ''; + if (toks) toks.style.maxHeight = ''; + return; + } + + const h = asst.offsetHeight; + chats.style.maxHeight = h + 'px'; + // Tokens is in the second row, under AI — its share is what the Assistant leaves once the + // first row and the 12px gap are taken. Floored so it stays a card rather than a sliver on a + // short viewport where the AI card happens to be tall. + if (toks && stats) toks.style.maxHeight = Math.max(140, h - stats.offsetHeight - 12) + 'px'; +} + // Delegated on the container, which survives every render — the rows inside do not, and binding // per row would rebind the whole list on each poll to no purpose. if (document.getElementById('vv-ai-tokens-body')) { @@ -2822,5 +2852,20 @@ if (document.getElementById('vv-mon-ai-chat')) { }); vvMonChatList = VvAiChatList({ into: 'vv-mon-ai-chats', chat: vvMonChat }); vvMonChatList.reload(); + + // Re-measured rather than computed once. The Assistant changes height when the transcript is + // expanded, and the AI card above changes when its first real payload replaces "Loading..."; + // both move the ceiling these two cards sit under. Observing the two sources covers each + // without watching the cards being adjusted, so there is no feedback loop. The resize listener + // is for crossing the 1025px breakpoint, which changes whether a cap applies at all. + vvAiRowSync(); + if (window.ResizeObserver) { + const ro = new ResizeObserver(vvAiRowSync); + ['vv-ai-assistant-card', 'vv-ai-stats-card'].forEach(id => { + const el = document.getElementById(id); + if (el) ro.observe(el); + }); + } + window.addEventListener('resize', vvAiRowSync); }