wsb-daemon 0.275.7622

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
<script lang="ts">
  // A directory's size cap, chosen on a slider.
  //
  // It used to be four or five buttons, which meant the cap could only ever be
  // one of four or five numbers — 64 GB or 128 GB and nothing between. The
  // steps are a ladder rather than a linear range because the useful sizes
  // span three orders of magnitude, and a linear slider over that spends its
  // whole travel at the top end.
  import { human } from "../../stores/host.svelte.js";
  import { sizeLadder, nearestStep } from "../../utils/size-ladder.js";

  let {
    value,
    min,
    max,
    onchange,
  }: {
    /** The current cap, in bytes. */
    value: number;
    /** The smallest the daemon will accept. */
    min: number;
    /** The largest worth offering. */
    max: number;
    onchange: (bytes: number) => void;
  } = $props();

  const ladder = $derived(sizeLadder(min, max));
  const index = $derived(nearestStep(ladder, value));

  function move(event: Event) {
    const next = ladder[Number((event.target as HTMLInputElement).value)];
    if (next !== undefined) onchange(next);
  }
</script>

<div class="size-field">
  <div class="size-slider">
    <input
      type="range"
      min="0"
      max={ladder.length - 1}
      step="1"
      value={index}
      oninput={move}
      aria-label="maximum size"
      aria-valuetext={human(ladder[index] ?? value)}
    />
    <output class="size-value">{human(ladder[index] ?? value)}</output>
  </div>
  <div class="size-ends">
    <span>{human(min)}</span>
    <span>{human(max)}</span>
  </div>
</div>

<style>
  /* One root element: this sits inside a flex row beside its label, and two
     roots would put the end labels next to the slider instead of under it. */
  .size-field {
    display: flex;
    flex-direction: column;
    gap: var(--wb-spacing-2);
    flex: 1;
    min-width: 0;
  }

  .size-slider {
    display: flex;
    align-items: center;
    gap: var(--wb-spacing-10);
    flex: 1;
    min-width: 0;
  }

  input[type="range"] {
    flex: 1;
    min-width: 0;
    accent-color: var(--vscode-button-background, #0e639c);
  }

  .size-value {
    /* Fixed width and tabular figures: the number changes as the handle
       moves, and a value that reflows the row under the cursor is a value
       that is hard to land on. */
    min-width: var(--wb-control-size-68);
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
  }

  .size-ends {
    display: flex;
    justify-content: space-between;
    /* Aligned to the track, not the row: the value readout sits to the right
       of the slider and the end labels belong under the slider itself. */
    margin-right: var(--wb-spacing-78);
    opacity: 0.55;
    font-size: 0.9em;
  }
</style>