wsb-daemon 0.275.7622

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
<!--
  What the project commits about itself in its main repo:
  `.wsb/project.json` — the boards it works from, and the submodules wsb
  leaves alone. Saved whole, as the file is; the change is in the next
  commit like any other.
-->
<script lang="ts">
  import type { ProjectSettings } from "../../api/client.js";
  import { settings } from "../../stores/settings.svelte.js";
  import SettingsSection from "./SettingsSection.svelte";

  /** One per line, blank lines dropped. */
  function lines(text: string): string[] {
    return text
      .split("\n")
      .map((line) => line.trim())
      .filter((line) => line.length > 0);
  }

  function draftOf(held: ProjectSettings | undefined) {
    return {
      boards: (held?.boards ?? []).join("\n"),
      ignored: (held?.ignored_submodules ?? []).join("\n"),
    };
  }

  const held = $derived(settings.project?.settings);
  let draft = $state(draftOf(undefined));
  $effect(() => {
    draft = draftOf(held);
  });
  const dirty = $derived(JSON.stringify(draftOf(held)) !== JSON.stringify(draft));

  async function save() {
    await settings.save({
      project: { boards: lines(draft.boards), ignored_submodules: lines(draft.ignored) },
    });
  }
</script>

<SettingsSection
  title="Project"
  description="What the project commits about itself, in {settings.project?.path ?? '.wsb/project.json'}."
>
  <div class="settings-form">
    <label class="field">
      <span class="field-label">Boards it works from, as owner/number, one per line</span>
      <textarea class="field-input" rows="3" bind:value={draft.boards}></textarea>
    </label>
    <label class="field">
      <span class="field-label">Submodules wsb leaves alone, relative to the main repo, one per line</span>
      <textarea class="field-input" rows="3" bind:value={draft.ignored}></textarea>
    </label>
    {#if dirty}
      <div class="button-row">
        <button class="primary" disabled={settings.saving} onclick={() => void save()}>
          {settings.saving ? "Saving…" : "Save"}
        </button>
        <button class="secondary" onclick={() => (draft = draftOf(held))}>Discard</button>
      </div>
    {/if}
  </div>
</SettingsSection>

<style>
  .settings-form {
    display: flex;
    flex-direction: column;
    gap: var(--wb-spacing-10);
  }
</style>