<script lang="ts">
// One worktree's logs: where they are, and what they hold against the
// workshop's limit.
//
// What it manages is stated per stream rather than as one number: an
// operator deciding whether 2 GB is too much needs to know whether it is
// test sessions or their own build logs. The limit itself is the workshop's,
// set in Workshop settings.
import SettingsSection from "./SettingsSection.svelte";
import { settings } from "../../stores/settings.svelte.js";
import { workbench } from "../../stores/workbench.svelte.js";
import { human } from "../../stores/host.svelte.js";
/** What each stream directory is, for somebody who has not read the code. */
const STREAMS: Record<string, string> = {
ops: "operations run from here",
test: "test sessions",
daemon: "the daemon's own log",
};
let copied = $state(false);
const usage = $derived(settings.logs.usage);
/** How full it is, against the limit that is actually in force. */
const fraction = $derived(
settings.logs.max_bytes > 0
? Math.min(1, usage.total_bytes / settings.logs.max_bytes)
: 0,
);
async function copyRoot() {
try {
await navigator.clipboard.writeText(settings.logs.root);
copied = true;
setTimeout(() => (copied = false), 1500);
} catch {
// The path is on screen and selectable; a clipboard the browser
// withheld is not worth an error.
}
}
</script>
<SettingsSection
title="Logs"
description="What ran in this worktree, one file per operation and per test session."
>
<div class="setting-row column">
<span class="setting-label">Directory</span>
<div class="path-row">
<code class="path">{settings.logs.root}</code>
<button class="path-copy" onclick={() => void copyRoot()}>
{copied ? "copied" : "copy"}
</button>
</div>
</div>
<div class="setting-row column">
<span class="setting-label">
Holding {human(usage.total_bytes)} of {human(settings.logs.max_bytes)}
</span>
<div class="meter" class:meter-full={fraction > 0.9}>
<div class="meter-fill" style="width: {Math.round(fraction * 100)}%"></div>
</div>
<div class="streams">
{#each Object.entries(usage.by_group) as [name, bytes] (name)}
<span class="stream">
<span class="stream-name">{name}</span>
<span class="stream-what">{STREAMS[name] ?? ""}</span>
<span class="stream-bytes">{human(bytes)}</span>
</span>
{/each}
{#if usage.unmanaged_bytes > 0}
<span class="stream">
<span class="stream-name">other</span>
<span class="stream-what">not written here — counted, never removed</span>
<span class="stream-bytes">{human(usage.unmanaged_bytes)}</span>
</span>
{/if}
</div>
</div>
<p class="setting-note">
{human(settings.logs.max_bytes)} is the workshop's limit for each worktree's
logs.
<button class="path-copy" onclick={() => workbench.openSettings("settings-logs")}>change it</button>
</p>
</SettingsSection>
<style>
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--wb-spacing-12);
padding: var(--wb-spacing-8) 0;
}
.setting-row.column {
flex-direction: column;
align-items: stretch;
gap: var(--wb-spacing-6);
}
.setting-label {
font-size: var(--wb-font-size-12);
color: var(--vscode-foreground);
}
.path-row {
display: flex;
align-items: center;
gap: var(--wb-spacing-8);
min-width: 0;
}
.path {
font-family: var(--wsb-font-mono, monospace);
font-size: var(--wb-font-size-11);
color: var(--vscode-descriptionForeground, #999);
overflow-x: auto;
white-space: nowrap;
user-select: text;
min-width: 0;
}
.path-copy {
background: none;
border: 1px solid var(--vscode-widget-border);
border-radius: var(--wsb-radius-sm);
color: var(--vscode-descriptionForeground, #999);
cursor: pointer;
font-size: var(--wb-font-size-10);
padding: var(--wb-spacing-1) var(--wb-spacing-6);
flex-shrink: 0;
}
.path-copy:hover {
color: var(--vscode-foreground);
border-color: var(--vscode-focusBorder);
}
.meter {
height: var(--wb-control-size-6);
border-radius: var(--wb-corner-radius-3);
background: var(--vscode-input-background);
border: 1px solid var(--vscode-widget-border);
overflow: hidden;
}
.meter-fill {
height: 100%;
background: var(--vscode-focusBorder);
transition: width 0.2s;
}
.meter-full .meter-fill {
background: var(--vscode-errorForeground, #f14c4c);
}
.streams {
display: flex;
flex-direction: column;
gap: var(--wb-spacing-2);
margin-top: var(--wb-spacing-4);
}
.stream {
display: flex;
align-items: baseline;
gap: var(--wb-spacing-8);
font-size: var(--wb-font-size-11);
color: var(--vscode-descriptionForeground, #999);
}
.stream-name {
font-family: var(--wsb-font-mono, monospace);
min-width: var(--wb-control-size-56);
}
.stream-what {
flex: 1;
opacity: 0.8;
}
.stream-bytes {
font-variant-numeric: tabular-nums;
}
.setting-note {
margin: var(--wb-spacing-4) 0 0;
font-size: var(--wb-font-size-11);
color: var(--vscode-descriptionForeground, #999);
}
</style>