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.
This commit is contained in:
@@ -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
|
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
|
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 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-chats-card,
|
||||||
#vv-monitor-ai #vv-ai-tokens-card {
|
#vv-monitor-ai #vv-ai-tokens-card {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
|
|||||||
@@ -2087,6 +2087,36 @@ function vvRenderTokens() {
|
|||||||
box.innerHTML = h;
|
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
|
// 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.
|
// per row would rebind the whole list on each poll to no purpose.
|
||||||
if (document.getElementById('vv-ai-tokens-body')) {
|
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 = VvAiChatList({ into: 'vv-mon-ai-chats', chat: vvMonChat });
|
||||||
vvMonChatList.reload();
|
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);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user