Give the token ledger its own card on the dashboard

The AI card answers whether the model is healthy now; what it has spent over a month was a
different question crowding it out one line at a time.
This commit is contained in:
Gmer4Lfe
2026-08-09 11:00:00 -04:00
parent 33e696071e
commit 1afeaf0e15
4 changed files with 226 additions and 48 deletions
+85 -25
View File
@@ -237,29 +237,42 @@ body.vv-fullscreen #displaybox { padding-left: 1rem !important; padding-top: .5r
align-items: start;
}
/* Conversations takes its height from the Assistant rather than setting its own. align-self
overrides the row's align-items:start for this one card, so it fills the track the Assistant
defines — level at rest, and level again after expanding, since the track grows with it.
/* Explicit placement, two rows. The left column carries AI over Tokens; Conversations and the
Assistant span both rows, so the Assistant sets the height and the two stacked cards divide
what it sets. This has to be written as row+column rather than the spans that were inline on
the markup: a span says how wide, never which row, and auto-placement would drop Tokens into
the first gap it found rather than under AI. */
#vv-ai-stats-card { grid-column: 1 / span 1; grid-row: 1; }
#vv-ai-tokens-card { grid-column: 1 / span 1; grid-row: 2; }
#vv-ai-chats-card { grid-column: 2 / span 2; grid-row: 1 / span 2; }
#vv-ai-assistant-card { grid-column: 4 / span 5; grid-row: 1 / span 2; }
It needs its own scroll to do that, which it did not need inside #vv-monitor: there it
inherited a cap from `.vv-card > div { flex:1; overflow-y:auto }`. Out here the list is
content-driven, so an install with forty saved chats would render forty rows and become the
tallest card itself — setting the row height instead of following it, which is the inverse of
what this rule is for. The h3 pins and the list takes what is left, the same shape the monitor
grid above uses. */
#vv-monitor-ai #vv-ai-chats-card {
/* Conversations and Tokens take their height from the Assistant rather than setting their own.
align-self overrides the row's align-items:start for these two, so they fill the track the
Assistant defines — level at rest, and level again after expanding, since the track grows
with it.
They need their own scroll to do that, which Conversations did not need inside #vv-monitor:
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. */
#vv-monitor-ai #vv-ai-chats-card,
#vv-monitor-ai #vv-ai-tokens-card {
align-self: stretch;
display: flex;
flex-direction: column;
overflow: hidden;
}
#vv-monitor-ai #vv-ai-chats-card h3 { flex-shrink: 0; }
/* height:0 with flex:1 is the part that makes this follow rather than lead. flex-basis is
already 0, so it changes nothing about the layout; what it changes is the height the list
#vv-monitor-ai #vv-ai-chats-card h3,
#vv-monitor-ai #vv-ai-tokens-card h3 { flex-shrink: 0; }
/* height:0 with flex:1 is the part that makes these follow rather than lead. flex-basis is
already 0, so it changes nothing about the layout; what it changes is the height the body
reports upward. Without it the card can still hand the auto-sized grid track a max-content
height of the whole list, and forty conversations would set the row that the Assistant is
supposed to set. It grows back to fill the track through flex, then scrolls. */
#vv-monitor-ai #vv-ai-chats-card > div {
height of its whole contents, and the track it was meant to fit inside grows to fit it
instead. It grows back to fill the track through flex, then scrolls. */
#vv-monitor-ai #vv-ai-chats-card > div,
#vv-monitor-ai #vv-ai-tokens-card > div {
flex: 1;
height: 0;
min-height: 0;
@@ -267,15 +280,57 @@ body.vv-fullscreen #displaybox { padding-left: 1rem !important; padding-top: .5r
-ms-overflow-style: none;
scrollbar-width: none;
}
#vv-monitor-ai #vv-ai-chats-card > div::-webkit-scrollbar { display: none; }
#vv-monitor-ai #vv-ai-chats-card > div::-webkit-scrollbar,
#vv-monitor-ai #vv-ai-tokens-card > div::-webkit-scrollbar { display: none; }
/* ── AI token ledger — shared by the AI tab and the Monitor card ──────────────
Moved out of pages/ai.php so both surfaces can draw the same rows. A tab's inline <style> is
only present while that tab is, and the Monitor renders this ledger too. The AI panel's own
chrome (.vv-ai-tok, the two-column split) stayed behind — it is that panel's frame, not part
of the ledger. */
.vv-ai-hostrow { display:flex; align-items:center; gap:8px; font-size:11px; padding:4px 6px;
border-radius:4px; cursor:pointer; border:1px solid transparent; }
.vv-ai-hostrow:hover { background:#141414; }
.vv-ai-hostrow.active { background:#14202c; border-color:#2d4a6a; }
.vv-ai-hostrow-n { color:#8a8a8a; }
.vv-ai-hostrow-h { color:#4a4a4a; font-family:monospace; font-size:10px; }
.vv-ai-hostrow-v { margin-left:auto; color:#c8c8c8; font-family:monospace; font-size:11px; flex-shrink:0; }
.vv-ai-hostrow-v.none { color:#3a3a3a; font-style:italic; font-family:inherit; font-size:10px; }
.vv-ai-tok-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:12px; }
.vv-ai-tok-l { font-size:9px; letter-spacing:.08em; text-transform:uppercase; color:#4a4a4a; }
.vv-ai-tok-v { font-size:17px; font-weight:bold; color:#c8c8c8; font-family:monospace; line-height:1.3; }
.vv-ai-tok-s { font-size:10px; color:#5a5a5a; }
.vv-ai-tok-foot { margin-top:9px; padding-top:7px; border-top:1px solid #1a1a1a; font-size:10px;
color:#4a4a4a; display:flex; gap:14px; flex-wrap:wrap; }
/* The ledger in a 1-of-8 column. Every rule here is the same content given a sixth of the width
it has on the AI tab: the tiles stop sitting side by side, the host id drops under its name
instead of competing with the total for one line, and the footer stacks. */
/* Name and total keep the first line, the host id takes the second on its own. Ordered rather
than reordered in the markup, because the AI tab wants all three on one line and this is the
same HTML. flex-basis:100% on the id is what forces the break; without the explicit order the
total would follow it onto a third line instead of staying beside the name. */
#vv-ai-tokens-card .vv-ai-hostrow { flex-wrap: wrap; gap: 1px 6px; padding: 4px 5px; }
#vv-ai-tokens-card .vv-ai-hostrow-v { order: 1; }
#vv-ai-tokens-card .vv-ai-hostrow-h { order: 2; flex-basis: 100%; }
#vv-ai-tokens-card .vv-ai-tok-grid { grid-template-columns: 1fr; gap: 9px; }
#vv-ai-tokens-card .vv-ai-tok-v { font-size: 15px; }
#vv-ai-tokens-card .vv-ai-tok-foot { flex-direction: column; gap: 3px; }
/* Section headings inside the card. .vv-ai-tok-l is already the 9px uppercase label this wants,
so it is reused rather than restated — with room above it to separate the two halves. */
#vv-ai-tokens-card .vv-ai-tok-head { margin: 11px 0 5px; }
#vv-ai-tokens-card .vv-ai-tok-head:first-child { margin-top: 0; }
/* Medium width — 4 columns, mirroring #vv-monitor's own breakpoint. The 1/2/5 spans do not
survive the narrower grid: 5 of 4 would silently overflow the track. */
@media (max-width: 1024px) {
#vv-monitor-ai { grid-template-columns: repeat(4, 1fr) !important; }
#vv-ai-stats-card { grid-column: span 1 !important; }
#vv-ai-chats-card { grid-column: span 3 !important; }
#vv-ai-assistant-card { grid-column: span 4 !important; }
#vv-ai-stats-card { grid-column: 1 / span 1 !important; grid-row: 1 !important; }
#vv-ai-tokens-card { grid-column: 1 / span 1 !important; grid-row: 2 !important; }
#vv-ai-chats-card { grid-column: 2 / span 3 !important; grid-row: 1 / span 2 !important; }
/* The Assistant drops to its own full-width row here. Five columns of eight is a readable
transcript; three of four beside a list is not, and it is the card you sit and read. */
#vv-ai-assistant-card { grid-column: 1 / span 4 !important; grid-row: 3 !important; }
}
/* Phone — single column, natural heights, page scrolls. Matching heights is meaningless once
@@ -283,11 +338,16 @@ body.vv-fullscreen #displaybox { padding-left: 1rem !important; padding-top: .5r
a scroll region inside it is a trap for a thumb. */
@media (max-width: 480px) {
#vv-monitor-ai { grid-template-columns: 1fr !important; }
#vv-monitor-ai > .vv-card { grid-column: 1 / -1 !important; }
#vv-monitor-ai #vv-ai-chats-card { display: block; overflow: visible; }
/* height:0 is only inert while this is a flex item — as a plain block it would collapse the
list to nothing, so it goes back with the flex context that justified it. */
#vv-monitor-ai #vv-ai-chats-card > div { height: auto; min-height: auto; overflow-y: visible; }
/* grid-row has to be released along with grid-column. The explicit rows above are the whole
reason Tokens sits under AI, and left in place on a single-column grid they would stack
cards into the same cell on top of each other rather than in sequence. */
#vv-monitor-ai > .vv-card { grid-column: 1 / -1 !important; grid-row: auto !important; }
#vv-monitor-ai #vv-ai-chats-card,
#vv-monitor-ai #vv-ai-tokens-card { display: block; overflow: visible; }
/* height:0 is only inert while these are flex items — as plain blocks it would collapse the
body to nothing, so it goes back with the flex context that justified it. */
#vv-monitor-ai #vv-ai-chats-card > div,
#vv-monitor-ai #vv-ai-tokens-card > div { height: auto; min-height: auto; overflow-y: visible; }
}
/* Monitor responsive — 4-column grid at medium width */