<script lang="ts">
import { appStore } from '$lib/store.svelte.js';
import { layoutStore, type Mode } from '$lib/stores/layout.svelte.js';
import { projectsStore } from '$lib/stores/projects.svelte.js';
const MODES: Mode[] = ['code', 'ask', 'architect'];
function cycleMode() {
const idx = MODES.indexOf(layoutStore.mode);
layoutStore.mode = MODES[(idx + 1) % MODES.length];
}
function formatElapsed(secs: number): string {
const m = Math.floor(secs / 60);
const s = Math.floor(secs % 60);
return m > 0 ? `${m}m${s.toString().padStart(2, '0')}s` : `${s}s`;
}
function formatTokens(n: number): string {
if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
return String(n);
}
function contextPercent(): number {
const s = appStore.status;
const max = appStore.contextMax || 128000;
if (!s || !s.context_tokens) return 0;
return Math.round((s.context_tokens / max) * 100);
}
function ctxBar(): string {
const pct = contextPercent();
const filled = Math.round((pct / 100) * 10);
return '█'.repeat(filled) + '░'.repeat(10 - filled);
}
function totalTokens(): number {
const s = appStore.status;
if (!s) return 0;
return s.prompt_tokens + s.completion_tokens;
}
function activeProjectName(): string {
const proj = projectsStore.projects.find(
(p) => p.id === projectsStore.activeProjectId
);
if (!proj?.working_dir) return '';
const parts = proj.working_dir.replace(/\/+$/, '').split('/');
return parts[parts.length - 1] || proj.working_dir;
}
const modeColors: Record<Mode, string> = {
code: 'var(--red)',
ask: 'var(--cyan)',
architect: 'var(--magenta)'
};
</script>
<div class="status-bar">
<!-- Mode badge (clickable to cycle) -->
<button
class="mode-badge"
style:background="color-mix(in srgb, {modeColors[layoutStore.mode]} 20%, transparent)"
style:color={modeColors[layoutStore.mode]}
style:border-color="color-mix(in srgb, {modeColors[layoutStore.mode]} 40%, transparent)"
onclick={cycleMode}
title="Click to cycle mode"
aria-label="Agent mode: {layoutStore.mode}"
>
{layoutStore.mode}
</button>
<span class="sep">·</span>
{#if appStore.agentActive}
<span class="spinner" aria-label="Agent running"></span>
{#if appStore.phase}
<span class="phase">{appStore.phase}</span>
<span class="sep">·</span>
{/if}
{/if}
<!-- Context bar -->
<span class="ctx-label">ctx</span>
<span class="ctx-bar">{ctxBar()}</span>
<span class="ctx-pct">{contextPercent()}%</span>
{#if appStore.status}
<span class="sep">·</span>
<span class="stat">#{appStore.status.iteration}</span>
<span class="stat">{formatElapsed(appStore.status.elapsed_secs)}</span>
<span class="stat">T:{formatTokens(totalTokens())}</span>
{/if}
<span class="spacer"></span>
<!-- Project name -->
{#if activeProjectName()}
<span class="project-name">{activeProjectName()}</span>
<span class="sep">·</span>
{/if}
<!-- Model -->
{#if appStore.model}
<span class="model-name">{appStore.model}</span>
<span class="sep">·</span>
{/if}
<!-- Version -->
{#if appStore.serverVersion}
<span class="version">v{appStore.serverVersion}</span>
{/if}
<!-- Queue indicator -->
{#if appStore.messageQueue.length > 0}
<span class="sep">·</span>
<span class="queue-count" title="{appStore.messageQueue.length} message(s) queued">
⟳{appStore.messageQueue.length}
</span>
{/if}
</div>
<style>
.status-bar {
display: flex;
align-items: center;
gap: 0.375rem;
width: 100%;
padding: 0.1875rem 0.625rem;
font-family: var(--font-mono);
font-size: 0.75rem;
color: var(--fg-dim);
background: var(--bg-surface);
border-top: 1px solid var(--border);
user-select: none;
flex-shrink: 0;
box-sizing: border-box;
overflow: hidden;
}
.mode-badge {
all: unset;
cursor: pointer;
padding: 0.0625rem 0.375rem;
border-radius: 3px;
border: 1px solid;
font-size: 0.6875rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
flex-shrink: 0;
}
.mode-badge:hover {
filter: brightness(1.15);
}
.sep {
color: var(--border);
flex-shrink: 0;
}
.spinner {
width: 0.5rem;
height: 0.5rem;
border: 1.5px solid var(--accent);
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.7s linear infinite;
flex-shrink: 0;
}
.phase {
color: var(--yellow);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 10rem;
}
.ctx-label {
color: var(--fg-dim);
}
.ctx-bar {
color: var(--accent);
letter-spacing: -0.08em;
font-size: 0.625rem;
}
.ctx-pct {
min-width: 2.5ch;
}
.stat {
flex-shrink: 0;
}
.spacer {
flex: 1;
}
.project-name {
color: var(--fg);
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 10rem;
}
.model-name {
color: var(--fg-dim);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 8rem;
}
.version {
color: var(--fg-dim);
flex-shrink: 0;
}
.queue-count {
color: var(--yellow);
flex-shrink: 0;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>