Dreambase Panel
A boxed multi-line panel with auto-sizing borders: a context section with a gradient bar and status word, a stats section (tokens, cost, duration, lines changed), and a repo section (model, git branch with file counts, clock, vim mode), in a terracotta Claude-brand palette.
MIT licensed · original source
Updated 2026-06-29
150 copies
Preview
Clean repo
╭─ CONTEXT ────────────────────────────────────────────╮
│ ████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Opus 4.8 · main · 16:21 │
╰──────────────────────────────────────────────────────╯
New session
╭─ CONTEXT ──────────────────────────────────────╮
│ ░░░░░░░░░░░░░░░░░░░░░░ 0% of 200K · GOOD │
├─ STATS ────────────────────────────────────────┤
│ ↓ 0 ↑ 0 │ $0.00 │ ⏱ 10m 12s │
├─ REPO ─────────────────────────────────────────┤
│ ◆ Opus 4.8 · main · 16:21 │
╰────────────────────────────────────────────────╯
Dirty branch
╭─ CONTEXT ────────────────────────────────────────────╮
│ ██████████▌░░░░░░░░░░░ 48% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 96.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Sonnet 4.6 · feat/auth ~1 ?1 · 16:21 I │
╰──────────────────────────────────────────────────────╯
Near-full
╭─ CONTEXT ─────────────────────────────────────────────╮
│ ████████████████████░░ 91% ⚠ of 200K · HIGH │
├─ STATS ───────────────────────────────────────────────┤
│ ↓ 182.0K ↑ 1.4K │ $4.12 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ────────────────────────────────────────────────┤
│ ◆ Opus 4.8 · main · 16:21 │
╰───────────────────────────────────────────────────────╯
1M context
╭─ CONTEXT ─────────────────────────────────────────────╮
│ ██████████████░░░░░░░░ 64% ⚠ of 1M · OK │
├─ STATS ───────────────────────────────────────────────┤
│ ↓ 640.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ────────────────────────────────────────────────┤
│ ◆ Fable 5 · main · 16:21 │
╰───────────────────────────────────────────────────────╯
Post-compact
╭─ CONTEXT ──────────────────────────────────────╮
│ ░░░░░░░░░░░░░░░░░░░░░░ 0% of 200K · GOOD │
├─ STATS ────────────────────────────────────────┤
│ ↓ 0 ↑ 0 │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ─────────────────────────────────────────┤
│ ◆ Haiku 4.5 · main · 16:21 N │
╰────────────────────────────────────────────────╯
Worktree
╭─ CONTEXT ────────────────────────────────────────────╮
│ ████████▏░░░░░░░░░░░░░ 37% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 74.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Opus 4.8 · 16:21 │
╰──────────────────────────────────────────────────────╯
Non-git
╭─ CONTEXT ────────────────────────────────────────────╮
│ ████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │
├─ STATS ──────────────────────────────────────────────┤
│ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │
├─ REPO ───────────────────────────────────────────────┤
│ ◆ Opus 4.8 · 16:21 │
╰──────────────────────────────────────────────────────╯
Source
#!/usr/bin/env python3
"""Claude Code Status Line — Dreambase Edition
A branded mini status app with box borders and section headers.
"""
import json
import os
import re
import subprocess
import sys
import time
data = json.load(sys.stdin)
# ── Extract data ──────────────────────────────────────────
model = data.get("model", {}).get("display_name", "—")
model_id = data.get("model", {}).get("id", "")
project_dir = data.get("workspace", {}).get("project_dir", ".")
current_dir = data.get("workspace", {}).get("current_dir", ".")
pct = int(float(data.get("context_window", {}).get("used_percentage") or 0))
ctx_size = int(data.get("context_window", {}).get("context_window_size") or 200000)
input_tokens = int(data.get("context_window", {}).get("total_input_tokens") or 0)
output_tokens = int(data.get("context_window", {}).get("total_output_tokens") or 0)
cost_usd = float(data.get("cost", {}).get("total_cost_usd") or 0)
duration_ms = int(data.get("cost", {}).get("total_duration_ms") or 0)
lines_added = int(data.get("cost", {}).get("total_lines_added") or 0)
lines_removed = int(data.get("cost", {}).get("total_lines_removed") or 0)
exceeds_200k = data.get("exceeds_200k_tokens", False)
vim_mode = data.get("vim", {}).get("mode", "")
# ── ANSI Colors ───────────────────────────────────────────
R = "\033[0m"
B = "\033[1m"
D = "\033[2m"
# Claude brand rust / terracotta
RUST = "\033[38;5;173m"
RUST_B = "\033[38;5;209m"
RUST_D = "\033[38;5;131m"
RUST_BG = "\033[48;5;52m"
# Functional
GRN = "\033[38;5;34m"
LIME = "\033[38;5;118m"
YEL = "\033[38;5;220m"
ORG = "\033[38;5;208m"
RED = "\033[38;5;196m"
CYN = "\033[38;5;81m"
BLU = "\033[38;5;69m"
PUR = "\033[38;5;141m"
WHT = "\033[38;5;255m"
GRY = "\033[38;5;243m"
# ── Layout engine ─────────────────────────────────────────
MIN_IW = 46 # minimum inner content width
def vlen(s):
"""Visible length excluding ANSI escapes."""
return len(re.sub(r"\033\[[^m]*m", "", s))
def make_renderer(content_rows):
"""Compute box width from widest content, return render functions."""
iw = max(MIN_IW, max(vlen(c) for c in content_rows))
tw = iw + 4
def pad(s):
return s + " " * max(0, iw - vlen(s))
def border_top(label=""):
if label:
fill = tw - 5 - len(label)
return f"{RUST}╭─ {RUST_B}{B}{label}{R}{RUST} {'─' * fill}╮{R}"
return f"{RUST}╭{'─' * (tw - 2)}╮{R}"
def border_mid(label=""):
if label:
fill = tw - 5 - len(label)
return f"{RUST}├─ {RUST_B}{B}{label}{R}{RUST} {'─' * fill}┤{R}"
return f"{RUST}├{'─' * (tw - 2)}┤{R}"
def border_bot():
return f"{RUST}╰{'─' * (tw - 2)}╯{R}"
def row(content):
return f"{RUST}│{R} {pad(content)} {RUST}│{R}"
return border_top, border_mid, border_bot, row
# ── Formatters ────────────────────────────────────────────
def fmt_tok(n):
if n >= 1_000_000:
return f"{n / 1_000_000:.1f}M"
if n >= 1_000:
return f"{n / 1_000:.1f}K"
return str(n)
def fmt_dur(ms):
s = ms // 1000
h, rem = divmod(s, 3600)
m, sec = divmod(rem, 60)
if h:
return f"{h}h {m}m"
if m:
return f"{m}m {sec}s"
return f"{sec}s"
def truncate(s, maxlen=18):
return s[: maxlen - 1] + "…" if len(s) > maxlen else s
# ── Progress bar ──────────────────────────────────────────
BAR_W = 22
BLOCKS = " ▏▎▍▌▋▊▉█"
if pct >= 95:
bar_clr = RED
status_label = f"{RED}{B}CRITICAL{R}"
elif pct >= 85:
bar_clr = ORG
status_label = f"{ORG}HIGH{R}"
elif pct >= 70:
bar_clr = YEL
status_label = f"{YEL}MODERATE{R}"
elif pct >= 50:
bar_clr = LIME
status_label = f"{LIME}OK{R}"
else:
bar_clr = GRN
status_label = f"{GRN}GOOD{R}"
eighths = pct * BAR_W * 8 // 100
full = eighths // 8
partial = eighths % 8
empty = BAR_W - full - (1 if partial else 0)
bar = "█" * full
if partial:
bar += BLOCKS[partial]
bar += "░" * empty
ctx_label = "1M" if ctx_size >= 1_000_000 else "200K"
warn = f" {RED}{B}⚠{R}" if exceeds_200k else ""
# ── Git info (cached for perf) ────────────────────────────
CACHE = "/tmp/claude-sl-git"
def git_info():
try:
if time.time() - os.path.getmtime(CACHE) < 5:
with open(CACHE) as f:
p = f.read().strip().split("|")
if len(p) == 4:
return p
except (OSError, ValueError):
pass
try:
br = subprocess.run(
["git", "-C", project_dir, "branch", "--show-current"],
capture_output=True, text=True, timeout=2,
).stdout.strip()
if not br:
return ["", "0", "0", "0"]
st = len(subprocess.run(
["git", "-C", project_dir, "diff", "--cached", "--numstat"],
capture_output=True, text=True, timeout=2,
).stdout.strip().splitlines())
mo = len(subprocess.run(
["git", "-C", project_dir, "diff", "--numstat"],
capture_output=True, text=True, timeout=2,
).stdout.strip().splitlines())
ut = len(subprocess.run(
["git", "-C", project_dir, "ls-files", "--others", "--exclude-standard"],
capture_output=True, text=True, timeout=2,
).stdout.strip().splitlines())
result = [br, str(st), str(mo), str(ut)]
with open(CACHE, "w") as f:
f.write("|".join(result))
return result
except Exception:
return ["", "0", "0", "0"]
branch, staged, modified, untracked = git_info()
staged, modified, untracked = int(staged), int(modified), int(untracked)
# ── Derived values ────────────────────────────────────────
in_t = fmt_tok(input_tokens)
out_t = fmt_tok(output_tokens)
cost_s = f"${cost_usd:.2f}"
dur_s = fmt_dur(duration_ms)
clock = time.strftime("%H:%M")
# Git indicators
git_ind = ""
if staged:
git_ind += f" {GRN}+{staged}{R}"
if modified:
git_ind += f" {YEL}~{modified}{R}"
if untracked:
git_ind += f" {GRY}?{untracked}{R}"
# Lines changed
lines_s = ""
if lines_added or lines_removed:
lines_s = f"{GRN}+{lines_added}{R}{D}/{R}{RED}-{lines_removed}{R}"
# Model diamond icon
if "opus" in model_id:
m_icon = f"{RUST_B}◆{R}"
elif "sonnet" in model_id:
m_icon = f"{BLU}◆{R}"
elif "haiku" in model_id:
m_icon = f"{GRN}◆{R}"
else:
m_icon = f"{GRY}◆{R}"
# Vim mode
vim_s = ""
if vim_mode == "NORMAL":
vim_s = f" {RUST_BG}{WHT}{B} N {R}"
elif vim_mode == "INSERT":
vim_s = f" {RUST_BG}{WHT}{B} I {R}"
# ── Build content strings ─────────────────────────────────
ctx_content = (
f"{bar_clr}{bar}{R} {WHT}{B}{pct}%{R}{warn}"
f" {D}of{R} {RUST}{ctx_label}{R} {D}·{R} {status_label}"
)
stats_content = (
f"{BLU}↓{R} {in_t} {PUR}↑{R} {out_t}"
f" {RUST_D}│{R} {YEL}{cost_s}{R}"
f" {RUST_D}│{R} {GRY}⏱ {dur_s}{R}"
+ (f" {RUST_D}│{R} {lines_s}" if lines_s else "")
)
repo_content = f"{m_icon} {RUST_B}{B}{model}{R}"
if branch:
repo_content += f" {RUST_D}·{R} {PUR}{truncate(branch)}{R}{git_ind}"
repo_content += f" {RUST_D}·{R} {GRY}{clock}{R}"
repo_content += vim_s
# ── Render (box auto-sizes to widest row) ─────────────────
border_top, border_mid, border_bot, row = make_renderer(
[ctx_content, stats_content, repo_content]
)
print(border_top("CONTEXT"))
print(row(ctx_content))
print(border_mid("STATS"))
print(row(stats_content))
print(border_mid("REPO"))
print(row(repo_content))
print(border_bot())
What it shows
- Context window usage as a percentage with a color-coded progress bar
- A usage status word (GOOD, OK, MODERATE, HIGH, or CRITICAL) based on the percentage
- The context window size as a 200K or 1M label
- A red warning symbol when the session exceeds 200K tokens
- Total input and output token counts, abbreviated with K or M suffixes
- Total session cost in US dollars
- Total session duration formatted as hours and minutes, minutes and seconds, or seconds alone depending on length
- Lines of code added and removed during the session
- The model display name with a diamond icon colored by model family
- The current git branch, truncated to 18 characters
- Counts of staged, modified, and untracked files in the git repo
- The current wall-clock time in HH:MM format
- A highlighted badge for vim NORMAL or INSERT mode
- Labeled CONTEXT, STATS, and REPO sections drawn inside a bordered box
Requirements
- Python 3 with only standard-library modules
- The git command on PATH for branch and file-status info (output degrades gracefully without it)
- A terminal font with Unicode box-drawing, block, and symbol characters (no Nerd Font glyphs required)
- A terminal that supports 256-color ANSI escape codes
- Write access to /tmp for a git info cache file
- No network access
Behavior notes
- The box width auto-sizes to the widest row, so the panel is narrower in sessions with less data, as in the brand-new-session preview
- The progress bar color and status word change with usage: green GOOD at 22 percent, lime OK at 64 percent, and orange HIGH at 91 percent in the previews
- The bar renders partial-width block characters, so 22 percent and 37 percent end in fractional blocks
- A red warning symbol appears next to the percentage only when exceeds_200k_tokens is true, as in the 91 percent and 1M-context previews
- The context label switches from 200K to 1M when the context window size reaches one million tokens
- A null used_percentage is treated as 0 percent, so the new-session and post-compact previews show an empty bar and GOOD
- The lines added/removed segment disappears when both counts are zero, as in the brand-new-session preview
- The git branch and file-count indicators disappear when git reports no branch, as in the worktree and scratch-directory previews
- Git file counts only show non-zero categories: the dirty-branch preview shows yellow ~1 and gray ?1 with no staged count
- The diamond icon color follows the model ID: terracotta for Opus, blue for Sonnet, green for Haiku, and gray for anything else, as with Fable 5
- The vim badge appears only for NORMAL (N) or INSERT (I) mode; the VISUAL and VISUAL LINE previews show no badge
- Effort level, thinking state, rate limits, PR info, output style, session name, and added directories appear in the input JSON but are never displayed
- Git info is cached in /tmp/claude-sl-git and reused for 5 seconds between renders
More status lines
Single-line usage
87 copies
app · main · Opus-4.8 · effort high · ctx 22% · 5h 26% ↻2h6m · 7d 7% ↻2d0hPace Dot Meters
63 copies
Opus 4.8 [high] ~/app main
16:20:55 | ⛁ ██░░░░░░░░ 22% | 5h ●◔○○○ 26% ↻2h7m | 7d ◔○○○○○○ 7% ↻2d1h │ $0.41 ⏱ 10m
Catppuccin Frappé
38 copies
Opus 4.8 │ main │ app │ ▓▓░░░░░░░░ 44k/200k │ $0.41
Activity Feed
36 copies
Opus 4.8 high ✦ │ ctx 22% │ app (main) │ PR #1287 ● │ $0.41 · 10m +128 -34
Session ● ● ● ● ● ● ● ● ○ ○ ○ 74% left Resets in 2h 6m
Weekly ● ● ● ● ● ● ● ● ● ● ○ 93% left Resets in 2d 0h
Fable ● ● ● ● ● ● ● ● ● ○ ○ 85% left Resets in 2d 0h
◐ Read:index.ts ✓ Read×1 ✓ Bash×1 ✓ Edit×1 ✓ Grep×1
◐ code-reviewer Review the change for regressions
Dreambase Flat
29 copies
████▊░░░░░░░░░░░░░░░░░ 22% of 200K · GOOD │ ↓ 44.0K ↑ 1.4K │ $0.41 │ ⏱ 10m 12s │ +128/-34 │ ◆ Opus 4.8 · main · 16:22
Color-Coded Context
25 copies
◆ Opus 4.8 │ ▪ app │ ⎇ main │ ◷ 16:20 │ ██▄░░░░░░░ 26% (44K/167K) │ 26% ↻2h6m │ 7% ⟳2d0h