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">
  import { onMount } from "svelte";
  import { git } from "../../../stores/git.svelte.js";
  import AppIcon from "../../layout/AppIcon.svelte";

  let {
    repoPath,
    fleet = false,
    onclose,
  }: {
    repoPath?: string;
    fleet?: boolean;
    onclose: () => void;
  } = $props();

  let branches = $state<string[]>([]);
  let current = $state<string | null>(null);
  let selected = $state("");
  let newBranchName = $state("");
  let createNew = $state(false);
  let loading = $state(true);
  let error: string | null = $state(null);

  onMount(async () => {
    try {
      const result = await git.getBranches(repoPath);
      branches = result.branches;
      current = result.current;
      if (result.current) {
        selected = result.current;
      } else if (branches.length > 0) {
        selected = branches[0]!;
      }
    } catch (e) {
      error = e instanceof Error ? e.message : String(e);
    } finally {
      loading = false;
    }
  });

  function handleSubmit() {
    let accepted: boolean;
    if (createNew) {
      const name = newBranchName.trim();
      if (!name) return;
      accepted = fleet ? git.checkoutAll(name, true) : git.checkout(name, repoPath, true);
    } else {
      if (!selected) return;
      accepted = fleet ? git.checkoutAll(selected) : git.checkout(selected, repoPath);
    }
    if (accepted) onclose();
  }

  function handleKeydown(e: KeyboardEvent) {
    if (e.key === "Escape") onclose();
    if (e.key === "Enter" && !e.shiftKey) handleSubmit();
  }
</script>

<div class="modal-overlay" onclick={onclose} onkeydown={handleKeydown} role="presentation">
  <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
  <div
    class="modal-panel"
    onclick={(e) => e.stopPropagation()}
    onkeydown={handleKeydown}
    role="dialog"
    aria-label="Checkout Branch"
    tabindex="0"
  >
    <div class="modal-header">
      <h2 class="modal-title">{fleet ? "Fleet Checkout" : "Checkout Branch"}</h2>
      <button class="modal-close" onclick={onclose} aria-label="Close">
        <AppIcon name="x" size={14} />
      </button>
    </div>
    <div class="modal-body">
      {#if error}
        <div class="modal-error">{error}</div>
      {/if}

      {#if loading}
        <div class="loading">Loading branches...</div>
      {:else}
        <div class="toggle-row">
          <button
            class="toggle-btn"
            class:active={!createNew}
            onclick={() => (createNew = false)}
          >Existing Branch</button>
          <button
            class="toggle-btn"
            class:active={createNew}
            onclick={() => (createNew = true)}
          >Create New</button>
        </div>

        {#if createNew}
          <label class="field">
            <span class="field-label">New branch name</span>
            <input
              class="field-input"
              type="text"
              placeholder="feature/my-branch"
              bind:value={newBranchName}
            />
          </label>
        {:else}
          <label class="field">
            <span class="field-label">Branch{current ? ` (current: ${current})` : ""}</span>
            <select class="field-select" bind:value={selected}>
              {#each branches as branch}
                <option value={branch}>{branch}{branch === current ? " ✓" : ""}</option>
              {/each}
            </select>
          </label>
        {/if}

        <div class="modal-actions">
          <button class="btn-cancel" onclick={onclose}>Cancel</button>
          <button
            class="btn-submit"
            disabled={createNew ? !newBranchName.trim() : !selected || selected === current}
            onclick={handleSubmit}
          >
            {createNew ? "Create & Checkout" : "Checkout"}
          </button>
        </div>
      {/if}
    </div>
  </div>
</div>

<style>
  .field {
    display: flex;
    flex-direction: column;
    gap: var(--wb-spacing-4);
    margin-bottom: var(--wb-spacing-12);
  }

  .field-label {
    font-size: var(--wb-font-size-11);
    font-weight: 600;
    color: var(--vscode-foreground);
  }

  .field-input,
  .field-select {
    padding: var(--wb-spacing-8) var(--wb-spacing-10);
    font-size: var(--wb-font-size-13);
    font-family: var(--wsb-font-sans);
    color: var(--vscode-editor-foreground);
    background: var(--vscode-input-background);
    border: 1px solid var(--vscode-widget-border);
    border-radius: var(--wsb-radius-sm);
  }

  .field-input:focus,
  .field-select:focus {
    outline: none;
    border-color: var(--vscode-focusBorder);
  }

  .loading {
    padding: var(--wb-spacing-12) 0;
    font-size: var(--wb-font-size-12);
    color: var(--vscode-disabledForeground);
  }

  .toggle-row {
    display: flex;
    gap: var(--wb-spacing-4);
    margin-bottom: var(--wb-spacing-12);
  }

  .toggle-btn {
    flex: 1;
    padding: var(--wb-spacing-6) var(--wb-spacing-12);
    font-size: var(--wb-font-size-12);
    font-weight: 500;
    color: var(--vscode-foreground);
    background: var(--vscode-input-background);
    border: 1px solid var(--vscode-widget-border);
    border-radius: var(--wsb-radius-sm);
    cursor: pointer;
  }

  .toggle-btn.active {
    background: var(--vscode-focusBorder);
    color: white;
    border-color: var(--vscode-focusBorder);
  }

  .modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--wb-spacing-8);
    margin-top: var(--wb-spacing-16);
  }

  .btn-cancel {
    height: var(--wb-control-size-32);
    padding: 0 var(--wb-spacing-14);
    font-size: var(--wb-font-size-12);
    font-weight: 500;
    color: var(--vscode-foreground);
    background: var(--vscode-input-background);
    border: 1px solid var(--vscode-widget-border);
    border-radius: var(--wsb-radius-sm);
    cursor: pointer;
  }

  .btn-cancel:hover {
    background: var(--vscode-list-hoverBackground);
  }

  .btn-submit {
    height: var(--wb-control-size-32);
    padding: 0 var(--wb-spacing-14);
    font-size: var(--wb-font-size-12);
    font-weight: 600;
    color: white;
    background: var(--vscode-testing-iconPassed);
    border-radius: var(--wsb-radius-sm);
    cursor: pointer;
  }

  .btn-submit:disabled {
    opacity: 0.5;
    cursor: default;
  }

  .btn-submit:hover:not(:disabled) {
    opacity: 0.9;
  }
</style>