From c333fcec32b1f90bf0da6bb14d2609c20e38a74f Mon Sep 17 00:00:00 2001
From: Adam Malczewski <github@tradam.dev>
Date: Fri, 26 Jun 2026 22:21:55 +0900
Subject: style: switch from tabs to 2-space indentation (incl. svelte)

---
 .../cache-warming/ui/CacheWarmingView.svelte       | 468 +++++++++++----------
 1 file changed, 239 insertions(+), 229 deletions(-)

(limited to 'src/features/cache-warming/ui/CacheWarmingView.svelte')

diff --git a/src/features/cache-warming/ui/CacheWarmingView.svelte b/src/features/cache-warming/ui/CacheWarmingView.svelte
index ced5e99..9b3d694 100644
--- a/src/features/cache-warming/ui/CacheWarmingView.svelte
+++ b/src/features/cache-warming/ui/CacheWarmingView.svelte
@@ -1,234 +1,244 @@
 <script lang="ts">
-	import type { InvokeMessage, SurfaceSpec } from "@dispatch/ui-contract";
-	import { onMount, untrack } from "svelte";
-	import {
-		clampMinutes,
-		clampSeconds,
-		colorClass,
-		formatCountdown,
-		formatWarmLabel,
-		fromMinSec,
-		initialWarmingState,
-		observeWarm,
-		parseControls,
-		secondsUntilNext,
-		statusForPct,
-		toMinSec,
-		type WarmingViewState,
-		type WarmNow,
-	} from "../logic/view-model";
-
-	let {
-		spec,
-		canWarm,
-		onInvoke,
-		warmNow,
-	}: {
-		/** The cache-warming surface spec for the focused conversation, or null. */
-		spec: SurfaceSpec | null;
-		/** Whether a real conversation is focused (a draft has nothing to warm). */
-		canWarm: boolean;
-		onInvoke: (msg: InvokeMessage) => void;
-		warmNow: WarmNow;
-	} = $props();
-
-	const controls = $derived(parseControls(spec));
-
-	// View-model state (pure reducer) + the injected clock — owned here, not ambient.
-	let vm = $state<WarmingViewState>(initialWarmingState());
-	let now = $state(Date.now());
-	let warming = $state(false);
-	let errorText = $state<string | null>(null);
-	// Transient result of the most recent manual warm (immediate feedback; history
-	// itself is driven authoritatively by the surface's `lastWarmAt`).
-	let manualResult = $state<{ cachePct: number; expectedCacheRate: number } | null>(null);
-
-	// Local interval inputs, seeded from the surface and re-seeded only when the
-	// surface's interval differs from what's shown (so a stray update mid-edit
-	// doesn't clobber typing).
-	let minutes = $state(0);
-	let seconds = $state(0);
-
-	onMount(() => {
-		const id = setInterval(() => {
-			now = Date.now();
-		}, 1000);
-		return () => clearInterval(id);
-	});
-
-	// Fold each authoritative warm (new `lastWarmAt`) into history.
-	$effect(() => {
-		const at = controls.lastWarmAt;
-		const pct = controls.lastPct;
-		untrack(() => {
-			vm = observeWarm(vm, at, pct);
-		});
-	});
-
-	// Keep the min/sec inputs in sync with the surface's interval.
-	$effect(() => {
-		const target = controls.intervalSeconds;
-		untrack(() => {
-			if (fromMinSec(minutes, seconds) !== target) {
-				const ms = toMinSec(target);
-				minutes = ms.minutes;
-				seconds = ms.seconds;
-			}
-		});
-	});
-
-	const remaining = $derived(secondsUntilNext(controls.nextWarmAt, now));
-	const history = $derived(vm.history);
-	const latest = $derived(history[0] ?? null);
-	const earlier = $derived(history.slice(1));
-
-	function commitInterval() {
-		const actionId = controls.setIntervalActionId;
-		if (actionId === null || spec === null) return;
-		onInvoke({ type: "invoke", surfaceId: spec.id, actionId, payload: fromMinSec(minutes, seconds) });
-	}
-
-	function onMinutes(event: Event) {
-		const next = (event.target as HTMLInputElement).valueAsNumber;
-		if (Number.isNaN(next)) return; // empty input — ignore, don't clobber to 0
-		minutes = clampMinutes(next);
-		commitInterval();
-	}
-
-	function onSeconds(event: Event) {
-		const next = (event.target as HTMLInputElement).valueAsNumber;
-		if (Number.isNaN(next)) return; // empty input — ignore, don't clobber to 0
-		seconds = clampSeconds(next);
-		commitInterval();
-	}
-
-	function onToggle() {
-		const actionId = controls.toggleActionId;
-		if (actionId === null || spec === null) return;
-		// The toggle action FLIPS server-side; no payload.
-		onInvoke({ type: "invoke", surfaceId: spec.id, actionId });
-	}
-
-	async function handleWarm() {
-		if (warming) return;
-		warming = true;
-		errorText = null;
-		const result = await warmNow();
-		warming = false;
-		if (result === null) return;
-		if (result.ok) {
-			// Immediate feedback only — the authoritative surface `update` (new
-			// `lastWarmAt`) drives the history via `observeWarm`.
-			manualResult = { cachePct: result.cachePct, expectedCacheRate: result.expectedCacheRate };
-		} else {
-			manualResult = null;
-			errorText = result.error;
-		}
-	}
+  import type { InvokeMessage, SurfaceSpec } from "@dispatch/ui-contract";
+  import { onMount, untrack } from "svelte";
+  import {
+    clampMinutes,
+    clampSeconds,
+    colorClass,
+    formatCountdown,
+    formatWarmLabel,
+    fromMinSec,
+    initialWarmingState,
+    observeWarm,
+    parseControls,
+    secondsUntilNext,
+    statusForPct,
+    toMinSec,
+    type WarmingViewState,
+    type WarmNow,
+  } from "../logic/view-model";
+
+  let {
+    spec,
+    canWarm,
+    onInvoke,
+    warmNow,
+  }: {
+    /** The cache-warming surface spec for the focused conversation, or null. */
+    spec: SurfaceSpec | null;
+    /** Whether a real conversation is focused (a draft has nothing to warm). */
+    canWarm: boolean;
+    onInvoke: (msg: InvokeMessage) => void;
+    warmNow: WarmNow;
+  } = $props();
+
+  const controls = $derived(parseControls(spec));
+
+  // View-model state (pure reducer) + the injected clock — owned here, not ambient.
+  let vm = $state<WarmingViewState>(initialWarmingState());
+  let now = $state(Date.now());
+  let warming = $state(false);
+  let errorText = $state<string | null>(null);
+  // Transient result of the most recent manual warm (immediate feedback; history
+  // itself is driven authoritatively by the surface's `lastWarmAt`).
+  let manualResult = $state<{ cachePct: number; expectedCacheRate: number } | null>(null);
+
+  // Local interval inputs, seeded from the surface and re-seeded only when the
+  // surface's interval differs from what's shown (so a stray update mid-edit
+  // doesn't clobber typing).
+  let minutes = $state(0);
+  let seconds = $state(0);
+
+  onMount(() => {
+    const id = setInterval(() => {
+      now = Date.now();
+    }, 1000);
+    return () => clearInterval(id);
+  });
+
+  // Fold each authoritative warm (new `lastWarmAt`) into history.
+  $effect(() => {
+    const at = controls.lastWarmAt;
+    const pct = controls.lastPct;
+    untrack(() => {
+      vm = observeWarm(vm, at, pct);
+    });
+  });
+
+  // Keep the min/sec inputs in sync with the surface's interval.
+  $effect(() => {
+    const target = controls.intervalSeconds;
+    untrack(() => {
+      if (fromMinSec(minutes, seconds) !== target) {
+        const ms = toMinSec(target);
+        minutes = ms.minutes;
+        seconds = ms.seconds;
+      }
+    });
+  });
+
+  const remaining = $derived(secondsUntilNext(controls.nextWarmAt, now));
+  const history = $derived(vm.history);
+  const latest = $derived(history[0] ?? null);
+  const earlier = $derived(history.slice(1));
+
+  function commitInterval() {
+    const actionId = controls.setIntervalActionId;
+    if (actionId === null || spec === null) return;
+    onInvoke({
+      type: "invoke",
+      surfaceId: spec.id,
+      actionId,
+      payload: fromMinSec(minutes, seconds),
+    });
+  }
+
+  function onMinutes(event: Event) {
+    const next = (event.target as HTMLInputElement).valueAsNumber;
+    if (Number.isNaN(next)) return; // empty input — ignore, don't clobber to 0
+    minutes = clampMinutes(next);
+    commitInterval();
+  }
+
+  function onSeconds(event: Event) {
+    const next = (event.target as HTMLInputElement).valueAsNumber;
+    if (Number.isNaN(next)) return; // empty input — ignore, don't clobber to 0
+    seconds = clampSeconds(next);
+    commitInterval();
+  }
+
+  function onToggle() {
+    const actionId = controls.toggleActionId;
+    if (actionId === null || spec === null) return;
+    // The toggle action FLIPS server-side; no payload.
+    onInvoke({ type: "invoke", surfaceId: spec.id, actionId });
+  }
+
+  async function handleWarm() {
+    if (warming) return;
+    warming = true;
+    errorText = null;
+    const result = await warmNow();
+    warming = false;
+    if (result === null) return;
+    if (result.ok) {
+      // Immediate feedback only — the authoritative surface `update` (new
+      // `lastWarmAt`) drives the history via `observeWarm`.
+      manualResult = { cachePct: result.cachePct, expectedCacheRate: result.expectedCacheRate };
+    } else {
+      manualResult = null;
+      errorText = result.error;
+    }
+  }
 </script>
 
 <div class="flex flex-col gap-3">
-	<!-- Enabled -->
-	<label class="flex items-center justify-between gap-2 text-sm">
-		<span>Enabled</span>
-		<input
-			type="checkbox"
-			class="toggle toggle-sm toggle-success"
-			checked={controls.enabled}
-			disabled={spec === null}
-			onchange={onToggle}
-		/>
-	</label>
-
-	<!-- Refresh interval: minutes + seconds (seconds capped at 59) -->
-	<div class="flex items-center justify-between gap-2 text-sm">
-		<span>Refresh interval</span>
-		<span class="flex items-center gap-1">
-			<input
-				type="number"
-				class="input input-bordered input-sm w-16"
-				min="0"
-				value={minutes}
-				disabled={spec === null}
-				onchange={onMinutes}
-				aria-label="Interval minutes"
-			/>
-			<span class="opacity-60">m</span>
-			<input
-				type="number"
-				class="input input-bordered input-sm w-16"
-				min="0"
-				max="59"
-				value={seconds}
-				disabled={spec === null}
-				onchange={onSeconds}
-				aria-label="Interval seconds"
-			/>
-			<span class="opacity-60">s</span>
-		</span>
-	</div>
-
-	<!-- Countdown to the next automatic warm (authoritative: driven by nextWarmAt) -->
-	{#if !controls.enabled}
-		<p class="text-xs opacity-50">Warming paused.</p>
-	{:else if remaining !== null}
-		<p class="text-xs opacity-70">Next warm in {formatCountdown(remaining)}</p>
-	{:else}
-		<p class="text-xs opacity-50">Next warm: waiting…</p>
-	{/if}
-
-	<!-- Cross-turn retention (the "is warming working?" health signal) -->
-	{#if controls.retentionPct !== null}
-		<p class="text-xs {colorClass(statusForPct(controls.retentionPct))}">
-			Cache retention: {controls.retentionPct}%
-		</p>
-	{/if}
-
-	<!-- Manual trigger -->
-	<button
-		type="button"
-		class="btn btn-sm btn-outline"
-		disabled={!canWarm || warming}
-		onclick={handleWarm}
-	>
-		{#if warming}
-			<span class="loading loading-spinner loading-xs"></span>
-			Warming…
-		{:else}
-			Warm now
-		{/if}
-	</button>
-
-	{#if !canWarm}
-		<p class="text-xs opacity-60">Open or start a conversation to control its cache warming.</p>
-	{:else if errorText}
-		<p class="text-xs text-error">{errorText}</p>
-	{:else if manualResult}
-		<!-- Headline the retention (cache health) over the raw hit %. -->
-		<p class="text-xs {colorClass(statusForPct(manualResult.expectedCacheRate))}">
-			Warmed — {manualResult.expectedCacheRate}% retained ({manualResult.cachePct}% of prompt cached)
-		</p>
-	{/if}
-
-	<!-- Warming history: collapse whose title is the most recent warm, coloured by
-	     hit %, with the earlier warmings inside. -->
-	{#if latest}
-		<div class="collapse collapse-arrow bg-base-200">
-			<input type="checkbox" aria-label="Toggle warming history" />
-			<div class="collapse-title min-h-0 py-2 font-normal text-sm {colorClass(statusForPct(latest.pct))}">
-				{formatWarmLabel(latest.pct)}
-			</div>
-			<div class="collapse-content flex flex-col gap-1 text-sm">
-				{#if earlier.length > 0}
-					{#each earlier as entry, i (i)}
-						<p class={colorClass(statusForPct(entry.pct))}>{formatWarmLabel(entry.pct)}</p>
-					{/each}
-				{:else}
-					<p class="text-xs opacity-60">No earlier warmings.</p>
-				{/if}
-			</div>
-		</div>
-	{:else}
-		<p class="text-xs opacity-60">No warming yet.</p>
-	{/if}
+  <!-- Enabled -->
+  <label class="flex items-center justify-between gap-2 text-sm">
+    <span>Enabled</span>
+    <input
+      type="checkbox"
+      class="toggle toggle-sm toggle-success"
+      checked={controls.enabled}
+      disabled={spec === null}
+      onchange={onToggle}
+    />
+  </label>
+
+  <!-- Refresh interval: minutes + seconds (seconds capped at 59) -->
+  <div class="flex items-center justify-between gap-2 text-sm">
+    <span>Refresh interval</span>
+    <span class="flex items-center gap-1">
+      <input
+        type="number"
+        class="input input-bordered input-sm w-16"
+        min="0"
+        value={minutes}
+        disabled={spec === null}
+        onchange={onMinutes}
+        aria-label="Interval minutes"
+      />
+      <span class="opacity-60">m</span>
+      <input
+        type="number"
+        class="input input-bordered input-sm w-16"
+        min="0"
+        max="59"
+        value={seconds}
+        disabled={spec === null}
+        onchange={onSeconds}
+        aria-label="Interval seconds"
+      />
+      <span class="opacity-60">s</span>
+    </span>
+  </div>
+
+  <!-- Countdown to the next automatic warm (authoritative: driven by nextWarmAt) -->
+  {#if !controls.enabled}
+    <p class="text-xs opacity-50">Warming paused.</p>
+  {:else if remaining !== null}
+    <p class="text-xs opacity-70">Next warm in {formatCountdown(remaining)}</p>
+  {:else}
+    <p class="text-xs opacity-50">Next warm: waiting…</p>
+  {/if}
+
+  <!-- Cross-turn retention (the "is warming working?" health signal) -->
+  {#if controls.retentionPct !== null}
+    <p class="text-xs {colorClass(statusForPct(controls.retentionPct))}">
+      Cache retention: {controls.retentionPct}%
+    </p>
+  {/if}
+
+  <!-- Manual trigger -->
+  <button
+    type="button"
+    class="btn btn-sm btn-outline"
+    disabled={!canWarm || warming}
+    onclick={handleWarm}
+  >
+    {#if warming}
+      <span class="loading loading-spinner loading-xs"></span>
+      Warming…
+    {:else}
+      Warm now
+    {/if}
+  </button>
+
+  {#if !canWarm}
+    <p class="text-xs opacity-60">Open or start a conversation to control its cache warming.</p>
+  {:else if errorText}
+    <p class="text-xs text-error">{errorText}</p>
+  {:else if manualResult}
+    <!-- Headline the retention (cache health) over the raw hit %. -->
+    <p class="text-xs {colorClass(statusForPct(manualResult.expectedCacheRate))}">
+      Warmed — {manualResult.expectedCacheRate}% retained ({manualResult.cachePct}% of prompt
+      cached)
+    </p>
+  {/if}
+
+  <!-- Warming history: collapse whose title is the most recent warm, coloured by
+       hit %, with the earlier warmings inside. -->
+  {#if latest}
+    <div class="collapse collapse-arrow bg-base-200">
+      <input type="checkbox" aria-label="Toggle warming history" />
+      <div
+        class="collapse-title min-h-0 py-2 font-normal text-sm {colorClass(
+          statusForPct(latest.pct),
+        )}"
+      >
+        {formatWarmLabel(latest.pct)}
+      </div>
+      <div class="collapse-content flex flex-col gap-1 text-sm">
+        {#if earlier.length > 0}
+          {#each earlier as entry, i (i)}
+            <p class={colorClass(statusForPct(entry.pct))}>{formatWarmLabel(entry.pct)}</p>
+          {/each}
+        {:else}
+          <p class="text-xs opacity-60">No earlier warmings.</p>
+        {/if}
+      </div>
+    </div>
+  {:else}
+    <p class="text-xs opacity-60">No warming yet.</p>
+  {/if}
 </div>
-- 
cgit v1.2.3

