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

  function formatNum(n: number): string {
    return n.toLocaleString();
  }

function pct(n: number): string {
  return `${(n * 100).toFixed(1)}%`;
}

  interface Card {
    label: string;
    value: () => string;
    sub?: () => string;
  }

  const cards: Card[] = [
  ];
</script>

<div class="summary-cards">
  {#each cards as card}
    <div class="card" class:loading={analytics.loading.summary}>
      {#if analytics.loading.summary}
        <div class="skeleton-value"></div>
        <div class="skeleton-label"></div>
      {:else if analytics.errors.summary}
        <span class="card-value error">--</span>
        <span class="card-label">{card.label}</span>
      {:else}
        <span class="card-value">
          {card.value()}
        </span>
        <span class="card-label">{card.label}</span>
        {#if card.sub}
          {@const subtext = card.sub()}
          {#if subtext}
            <span class="card-sub">{subtext}</span>
          {/if}
        {/if}
      {/if}
    </div>
  {/each}
</div>

{#if analytics.errors.summary}
  <div class="error-bar">
    <span>{analytics.errors.summary}</span>
    <button
      class="retry-btn"
      onclick={() => analytics.fetchSummary()}
    >
      Retry
    </button>
  </div>
{/if}

<style>
  .summary-cards {
    display: flex;
    gap: var(--wb-spacing-8);
    flex-wrap: wrap;
  }

  .card {
    flex: 1;
    min-width: var(--wb-control-size-120);
    padding: var(--wb-spacing-12);
    background: var(--vscode-sideBar-background);
    border: 1px solid var(--vscode-widget-border);
    border-radius: var(--wsb-radius-md);
    display: flex;
    flex-direction: column;
    gap: var(--wb-spacing-2);
  }

  .card-value {
    font-size: var(--wb-font-size-20);
    font-weight: 600;
    color: var(--vscode-editor-foreground);
    line-height: 1.2;
  }

  .card-value.error {
    color: var(--vscode-disabledForeground);
  }

  .card-label {
    font-size: var(--wb-font-size-11);
    color: var(--vscode-disabledForeground);
    font-weight: 500;
  }

  .card-sub {
    font-size: var(--wb-font-size-10);
    color: var(--vscode-disabledForeground);
    margin-top: var(--wb-spacing-2);
  }

  .skeleton-value {
    width: var(--wb-control-size-60);
    height: var(--wb-control-size-20);
    background: var(--vscode-input-background);
    border-radius: var(--wsb-radius-sm);
  }

  .skeleton-label {
    width: var(--wb-control-size-80);
    height: var(--wb-control-size-12);
    background: var(--vscode-input-background);
    border-radius: var(--wsb-radius-sm);
  }

  .error-bar {
    display: flex;
    align-items: center;
    gap: var(--wb-spacing-8);
    padding: var(--wb-spacing-8) var(--wb-spacing-12);
    background: var(--vscode-sideBar-background);
    border: 1px solid var(--vscode-errorForeground);
    border-radius: var(--wsb-radius-sm);
    font-size: var(--wb-font-size-11);
    color: var(--vscode-errorForeground);
  }

  .retry-btn {
    padding: var(--wb-spacing-2) var(--wb-spacing-8);
    border: 1px solid var(--vscode-errorForeground);
    border-radius: var(--wsb-radius-sm);
    font-size: var(--wb-font-size-11);
    color: var(--vscode-errorForeground);
    cursor: pointer;
  }

  .retry-btn:hover {
    background: var(--vscode-errorForeground);
    color: #fff;
  }
</style>