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 { ui } from "../../stores/ui.svelte.js";
  import { sync } from "../../stores/sync.svelte.js";

  type View = "confirm" | "progress" | "done" | "error";

  let view: View = $state("confirm");
  let errorMessage: string = $state("");

  function startResync() {
    const started = sync.triggerResync(
      () => {
        view = "done";
      },
      (err) => {
        errorMessage = err.message;
        view = "error";
      },
    );
    if (started) {
      view = "progress";
    } else {
      errorMessage = "A sync is already in progress.";
      view = "error";
    }
  }

  function close() {
    ui.activeModal = null;
  }

  function handleOverlayClick(e: MouseEvent) {
    if (
      view !== "progress" &&
      (e.target as HTMLElement).classList.contains(
        "modal-overlay",
      )
    ) {
      close();
    }
  }

  function handleKeydown(e: KeyboardEvent) {
    if (e.key === "Escape" && view !== "progress") {
      close();
    }
  }

  const progressPct = $derived(
    sync.progress
      ? sync.progress.sessions_total > 0
        ? (sync.progress.sessions_done /
            sync.progress.sessions_total) *
          100
        : 0
      : 0,
  );
</script>

<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
  class="modal-overlay"
  onclick={handleOverlayClick}
  onkeydown={handleKeydown}
>
  <div class="modal-panel resync-panel">
    <div class="modal-header">
      <h3 class="modal-title">Full Resync</h3>
      {#if view !== "progress"}
        <button class="modal-close" onclick={close}>
          &times;
        </button>
      {/if}
    </div>

    <div class="modal-body">
      {#if view === "confirm"}
        <p class="confirm-text">
          Re-parse all session files from scratch. Existing
          sessions will be updated in place &mdash; no data is
          deleted. Use this after upgrading or when sessions
          appear incorrect.
        </p>
        <div class="confirm-actions">
          <button class="modal-btn" onclick={close}>
            Cancel
          </button>
          <button
            class="modal-btn modal-btn-primary"
            onclick={startResync}
          >
            Start Full Resync
          </button>
        </div>

      {:else if view === "progress"}
        <div class="progress-view">
          <div class="modal-spinner"></div>
          <p class="progress-label">
            {#if sync.progress}
              Syncing {sync.progress.sessions_done}
              / {sync.progress.sessions_total} sessions...
            {:else}
              Preparing...
            {/if}
          </p>
          <div class="progress-bar-track">
            <div
              class="progress-bar-fill"
              style="width: {progressPct}%"
            ></div>
          </div>
        </div>

      {:else if view === "done"}
        <div class="done-view">
          {#if sync.lastSyncStats}
            <p class="done-summary">
              Sessions synced: {sync.lastSyncStats.synced}
            </p>
            {#if sync.lastSyncStats.failed > 0}
              <p class="done-warning">
                Failed: {sync.lastSyncStats.failed}
              </p>
            {/if}
          {/if}
          <div class="done-actions">
            <button
              class="modal-btn modal-btn-primary"
              onclick={close}
            >
              Close
            </button>
          </div>
        </div>

      {:else if view === "error"}
        <div class="error-view">
          <p class="modal-error">{errorMessage}</p>
          <div class="error-actions">
            <button
              class="modal-btn modal-btn-primary"
              onclick={startResync}
            >
              Retry
            </button>
            <button class="modal-btn" onclick={close}>
              Close
            </button>
          </div>
        </div>
      {/if}
    </div>
  </div>
</div>

<style>
  .resync-panel {
    width: 400px;
  }

  .confirm-text {
    font-size: var(--wb-font-size-12);
    color: var(--vscode-foreground);
    line-height: 1.5;
    margin-bottom: var(--wb-spacing-16);
  }

  .confirm-actions {
    display: flex;
    gap: var(--wb-spacing-8);
    justify-content: flex-end;
  }

  .progress-view {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--wb-spacing-12);
    padding: var(--wb-spacing-16) 0;
  }

  .progress-label {
    font-size: var(--wb-font-size-12);
    color: var(--vscode-foreground);
    font-variant-numeric: tabular-nums;
  }

  .progress-bar-track {
    width: 100%;
    height: var(--wb-control-size-4);
    background: var(--vscode-input-background);
    border-radius: var(--wb-corner-radius-2);
    overflow: hidden;
  }

  .progress-bar-fill {
    height: 100%;
    background: var(--vscode-focusBorder);
    border-radius: var(--wb-corner-radius-2);
    transition: width 0.3s;
  }

  .done-view {
    display: flex;
    flex-direction: column;
    gap: var(--wb-spacing-16);
  }

  .done-summary {
    font-size: var(--wb-font-size-12);
    color: var(--vscode-editor-foreground);
    font-variant-numeric: tabular-nums;
  }

  .done-warning {
    font-size: var(--wb-font-size-12);
    color: var(--wb-accent-orange, #e09040);
    font-variant-numeric: tabular-nums;
  }

  .done-actions {
    display: flex;
    justify-content: flex-end;
  }

  .error-view {
    display: flex;
    flex-direction: column;
    gap: var(--wb-spacing-12);
  }

  .error-actions {
    display: flex;
    gap: var(--wb-spacing-8);
    justify-content: flex-end;
  }
</style>