ModsCode / Gallery / Designs / Context window meter
Context window meter
One stacked bar of the context window split into system, tools, MCP, memory and messages, with free space hollow and the autocompact line.


hooks/designs/context-window.mjs
// context-window: one session's 200K context window as a single stacked bar, in candle's look. The
// sample is a working session: system prompt 3.1K, tools 14.2K, MCP tools 9.6K, memory files 6.8K and
// messages 98.4K, so 132.1K is used (66%) and 67.9K is free. The four fixed parts take the ink steps
// 0.9 / 0.65 / 0.42 / 0.22 and messages, the only part the person can clear, takes the one Claude orange at the
// body strength 0.55, not 0.9: it is half the bar, and a full-strength slab that wide would out-shout
// every other mark. Free space is a hollow outline so it never reads as a fifth fill. Autocompact at 80% (160K) is a dashed
// reference line at 35% ink, labelled with the 27.9K left before it fires. Segments sit 1px apart and
// every value is in the legend, since the small parts are too narrow to carry their own label.
// Sample data only.
const W = 380
const H = 120
const STAGE = '#FAF9F5'
const INK = '#141413'
const MUTED = '#73726C'
const CLAUDE = '#D77757'
const FONT = "'Inter Tight','Segoe UI',system-ui,sans-serif"
const BAR = { left: 12, right: 368, top: 40, height: 14 }
const GAP = 1 // px of stage between two segments
const WINDOW = 200000
const AUTOCOMPACT = 0.8
const PARTS = [
{ name: 'System', tokens: 3100, fill: INK, opacity: 0.9 },
{ name: 'Tools', tokens: 14200, fill: INK, opacity: 0.65 },
{ name: 'MCP', tokens: 9600, fill: INK, opacity: 0.42 },
{ name: 'Memory', tokens: 6800, fill: INK, opacity: 0.22 },
{ name: 'Messages', tokens: 98400, fill: CLAUDE, opacity: 0.55 },
]
const f = n => n.toFixed(1)
const k = v => (v / 1000).toFixed(1) + 'K'
const text = (x, y, s, { size = 9, fill = MUTED, anchor = 'start' } = {}) =>
`<text x="${f(x)}" y="${f(y)}"${anchor === 'start' ? '' : ` text-anchor="${anchor}"`} font-size="${size}" font-family="${FONT}" fill="${fill}">${s}</text>`
function draw() {
const used = PARTS.reduce((s, p) => s + p.tokens, 0)
const free = WINDOW - used
const span = BAR.right - BAR.left
const x = t => BAR.left + (span * t) / WINDOW
const yb = BAR.top + BAR.height
const parts = [`<rect width="${W}" height="${H}" rx="4" fill="${STAGE}"/>`]
parts.push(text(12, 28, Math.round((used / WINDOW) * 100) + '%', { size: 18, fill: INK }))
parts.push(text(54, 28, `${k(used)} OF ${WINDOW / 1000}K TOKENS`))
// Used parts left to right; half the gap comes off each side of every edge between two segments
let at = 0
for (const p of PARTS) {
const x0 = x(at) + (at ? GAP / 2 : 0)
const x1 = x(at + p.tokens) - GAP / 2
parts.push(`<rect x="${f(x0)}" y="${BAR.top}" width="${f(x1 - x0)}" height="${BAR.height}" fill="${p.fill}" fill-opacity="${p.opacity}"/>`)
at += p.tokens
}
// Free space: an outline inset half a pixel so its 1px stroke lands on whole pixels
const fx = x(at) + GAP / 2 + 0.5
parts.push(`<rect x="${f(fx)}" y="${BAR.top + 0.5}" width="${f(BAR.right - 0.5 - fx)}" height="${BAR.height - 1}" fill="none" stroke="${INK}" stroke-opacity="0.18"/>`)
// Autocompact: a reference line that reaches 6px past the bar, its label above
const ax = x(WINDOW * AUTOCOMPACT)
parts.push(`<line x1="${f(ax)}" x2="${f(ax)}" y1="${BAR.top - 6}" y2="${yb + 6}" stroke="${INK}" stroke-opacity="0.35" stroke-dasharray="3 3"/>`)
parts.push(text(ax, 28, `AUTOCOMPACT IN ${k(WINDOW * AUTOCOMPACT - used)}`, { anchor: 'middle' }))
// Legend: three columns, two rows, every part with its tokens; free is the hollow swatch
const rows = [...PARTS, { name: 'Free', tokens: free }]
const colW = (span - 32) / 3
rows.forEach((p, i) => {
const cx = BAR.left + (i % 3) * (colW + 16)
const ly = i < 3 ? 80 : 96
parts.push(p.fill
? `<rect x="${f(cx)}" y="${ly - 7}" width="6" height="6" fill="${p.fill}" fill-opacity="${p.opacity}"/>`
: `<rect x="${f(cx + 0.5)}" y="${ly - 6.5}" width="5" height="5" fill="none" stroke="${INK}" stroke-opacity="0.35"/>`)
parts.push(text(cx + 14, ly, p.name, { size: 11, fill: INK }))
parts.push(text(cx + colW, ly, k(p.tokens), { size: 11, fill: INK, anchor: 'end' }))
})
parts.push(text(12, H - 8, 'SAMPLE SESSION CONTEXT'))
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}">${parts.join('')}</svg>`
}
let source // drawn on first use and kept: the pane loads every design, so none pays at load time
export default {
id: 'context-window',
title: 'Context window meter',
category: 'usage',
description: 'One stacked bar of the context window split into system, tools, MCP, memory and messages, with free space hollow and the autocompact line.',
element: ui => ui.Svg({ source: (source ??= draw()), alt: 'Stacked bar of the context window by part, with free space, the autocompact line and a legend of token counts, sample data', width: W, height: H }),
}
To use it in a mod: copy the file to your mod's hooks/designs/ and return its element(ui) from a pane's ui.render. How a pane is built.
Code: PolyForm Noncommercial License 1.0.0: noncommercial use only. Required Notice: Copyright 2026 Haruki Nakada (https://modscode.com).