<script lang="ts">
// The status bar's readings: the machine and the context worktree at its
// start, the open file's language server, the work running and the build at
// its end. Every reading is a kit status item; the application surface lays
// them out (`at` says which end this is).
import { StatusItem } from "@wsb/uikit/components";
import { sync } from "../../stores/sync.svelte.js";
import { ui } from "../../stores/ui.svelte.js";
import { workbench } from "../../stores/workbench.svelte.js";
import { diagnostics } from "../../stores/diagnostics.svelte.js";
import { statusBar } from "../../stores/status-bar.svelte.js";
import { commands } from "../../stores/commands.svelte.js";
import { panel } from "../../stores/panel.svelte.js";
import { ops } from "../../stores/ops.svelte.js";
import { codeIntel } from "../../stores/code-intel.svelte.js";
import type { RootStatus } from "../../api/types/code.js";
import { formatRelativeTime } from "../../utils/format.js";
import HostStrip from "./HostStrip.svelte";
interface Props {
at: "start" | "end";
}
let { at }: Props = $props();
const isMac = navigator.platform.toUpperCase().includes("MAC");
const mod = isMac ? "Cmd" : "Ctrl";
const TOAST_TONES = { info: "info", success: "success", error: "danger" } as const;
function showAbout() {
if (ui.activeModal === "resync" && sync.syncing) return;
ui.activeModal = "about";
}
let progressText = $derived.by(() => {
if (!sync.syncing || !sync.progress) return null;
const p = sync.progress;
if (p.phase === "scan") {
return `Scanning ${p.current_project || ""}...`;
}
if (p.phase === "parse") {
const pct = p.sessions_total > 0
? Math.round((p.sessions_done / p.sessions_total) * 100)
: 0;
return `Syncing ${pct}% (${p.sessions_done}/${p.sessions_total})`;
}
return "Syncing...";
});
/** The context worktree's file in front, for where its language server stands. */
let activePath = $derived(workbench.activeFilePath);
/**
* omnicode, as it stands for the active file: its server's state, or why there is no
* `omni` to serve it. Nothing for a file omnicode does not answer for.
*/
let code = $derived.by((): { text: string; title: string; root: string | null; bad: boolean } | null => {
if (activePath === null) return null;
const omni = codeIntel.status?.omni;
if (omni && "unavailable" in omni) {
return { text: "omnicode unavailable", title: `${omni.message}\nClick to look again.`, root: null, bad: true };
}
const server = codeIntel.serverOf(activePath);
if (!server) return null;
return describeServer(server);
});
function describeServer(s: RootStatus): { text: string; title: string; root: string; bad: boolean } {
const at = s.root === "." ? "the worktree" : s.root;
const restart = "Click to restart it.";
switch (s.state) {
case "starting":
return { text: "omnicode starting", title: `omnicode is starting for ${at}.`, root: s.root, bad: false };
case "indexing": {
const pct = s.percentage === undefined ? "" : ` ${s.percentage}%`;
return { text: `omnicode indexing${pct}`, title: `${s.title}${s.message ? `: ${s.message}` : ""} (${at})\n${restart}`, root: s.root, bad: false };
}
case "ready":
return { text: "omnicode", title: `omnicode serves ${at}.${s.note ? `\n${s.note}` : ""}\n${restart}`, root: s.root, bad: false };
case "failed":
return { text: "omnicode failed", title: `${s.message}\n${restart}`, root: s.root, bad: true };
case "crashed":
return { text: "omnicode crashed", title: `omnicode for ${at} ended (${s.status}).\n${s.stderr.join("\n")}\n${restart}`, root: s.root, bad: true };
case "stopped":
return { text: "omnicode stopped", title: `omnicode for ${at} stopped.\n${restart}`, root: s.root, bad: false };
}
}
</script>
{#if at === "start"}
<!-- The machine, where movement is watched. -->
<HostStrip />
{#if diagnostics.errorCount > 0}
<StatusItem
handle="application.status.errors"
icon="danger"
tone="danger"
label={String(diagnostics.errorCount)}
title="{diagnostics.errorCount} error(s)"
onpress={() => void commands.execute("workbench.action.problems.focus")}
/>
{/if}
{#if diagnostics.warningCount > 0}
<StatusItem
handle="application.status.warnings"
icon="warning"
tone="warning"
label={String(diagnostics.warningCount)}
title="{diagnostics.warningCount} warning(s)"
onpress={() => void commands.execute("workbench.action.problems.focus")}
/>
{/if}
{#each statusBar.leftItems as item (item.id)}
<StatusItem
label={item.text}
title={item.tooltip}
onpress={item.command ? () => void commands.execute(item.command!) : undefined}
/>
{/each}
{#if statusBar.toast}
<StatusItem handle="application.status.toast" label={statusBar.toast.text} tone={TOAST_TONES[statusBar.toast.kind]} />
{/if}
{:else}
{#if code}
<StatusItem
handle="application.status.code"
label={code.text}
title={code.title}
tone={code.bad ? "danger" : "neutral"}
onpress={() => {
const root = code?.root;
if (root === null || root === undefined) void codeIntel.loadStatus();
else void codeIntel.restart(root);
}}
/>
{/if}
{#each statusBar.rightItems as item (item.id)}
<StatusItem
label={item.text}
title={item.tooltip}
onpress={item.command ? () => void commands.execute(item.command!) : undefined}
/>
{/each}
{#if ops.active.length > 0}
<StatusItem
handle="application.status.ops"
icon="refresh"
label={ops.active.length > 1 ? `${ops.active[0]!.label} +${ops.active.length - 1}` : ops.active[0]!.label}
title="{ops.active.length} background operation{ops.active.length === 1 ? '' : 's'} running"
onpress={() => {
panel.activateEntry("activity");
workbench.panelOpen = true;
}}
/>
{/if}
{#if sync.isDesktop}
<StatusItem handle="application.status.zoom-out" label="−" title="Zoom out ({mod}+-)" onpress={() => ui.zoomOut()} />
<StatusItem handle="application.status.zoom" label="{ui.zoomLevel}%" title="Reset zoom ({mod}+0)" onpress={() => ui.resetZoom()} />
<StatusItem handle="application.status.zoom-in" label="+" title="Zoom in ({mod}++)" onpress={() => ui.zoomIn()} />
{/if}
{#if sync.versionMismatch}
<StatusItem
handle="application.status.mismatch"
tone="warning"
label="version mismatch - reload"
title="Frontend and backend versions differ. Click to reload."
onpress={() => window.location.reload()}
/>
{/if}
{#if progressText}
<StatusItem handle="application.status.sync" label={progressText} />
{:else if sync.lastSync}
<StatusItem handle="application.status.sync" label="synced {formatRelativeTime(sync.lastSync)}" />
{/if}
{#if sync.serverVersion}
<StatusItem
handle="application.status.version"
label={sync.serverVersion.version}
title="Backend build: {sync.serverVersion.backend_build_id}"
onpress={showAbout}
/>
{/if}
{/if}