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 table a command reported, laid out by the page rather than drawn with
  // box characters by the command. The status column is coloured from the same
  // closed vocabulary the terminal renderer uses, so a word outside it reads
  // as "not asserted" rather than as "fine".
  import type { TableView } from "../../stores/op-output-view.js";

  let { table }: { table: TableView } = $props();

  const OK = ["OK", "VALID", "PRESENT", "SIGNED", "FRESH", "FOUND", "YES", "MATCH", "CLEAN", "UP-TO-DATE", "PASS"];
  const BAD = ["MISSING", "INVALID", "CHANGED", "NO", "FAIL", "UNSIGNED", "STALE", "ABSENT", "ERROR", "NONE"];
  const WARN = ["WARN", "UNKNOWN", "SKIP", "PARTIAL", "OUTDATED"];

  function statusClass(value: string): string {
    const word = value.trim().toUpperCase();
    if (OK.includes(word)) return "ok";
    if (BAD.includes(word)) return "bad";
    if (WARN.includes(word)) return "warn";
    return "plain";
  }

  /** The column that carries a status word, when the table has one. */
  const statusColumn = $derived(
    table.columns.findIndex((name) => name.toLowerCase() === "status"),
  );
</script>

<div class="record-table">
  <div class="table-title">{table.title}</div>
  <table>
    <thead>
      <tr>
        {#each table.columns as column (column)}
          <th>{column}</th>
        {/each}
      </tr>
    </thead>
    <tbody>
      {#each table.rows as row, index (index)}
        <tr>
          {#each row as cell, column (column)}
            <td class={column === statusColumn ? statusClass(cell) : "plain"}>{cell}</td>
          {/each}
        </tr>
      {/each}
    </tbody>
  </table>
  {#if table.verdict}
    <div class="verdict verdict-{table.verdict.level}">{table.verdict.message}</div>
  {/if}
</div>

<style>
  .record-table {
    padding: var(--wb-spacing-6) var(--wb-spacing-10) var(--wb-spacing-10);
    font-size: var(--wb-font-size-11);
    overflow-x: auto;
  }

  .table-title {
    font-weight: 600;
    padding-bottom: var(--wb-spacing-4);
  }

  table {
    border-collapse: collapse;
    width: 100%;
  }

  th {
    text-align: left;
    font-weight: 600;
    opacity: 0.7;
    padding: var(--wb-spacing-2) var(--wb-spacing-10) var(--wb-spacing-2) 0;
    border-bottom: 1px solid var(--vscode-panel-border);
  }

  td {
    padding: var(--wb-spacing-1) var(--wb-spacing-10) var(--wb-spacing-1) 0;
    vertical-align: top;
    white-space: pre-wrap;
  }

  .ok {
    color: var(--vscode-testing-iconPassed, #4caf50);
  }

  .bad {
    color: var(--vscode-errorForeground, #f14c4c);
  }

  .warn {
    color: var(--vscode-editorWarning-foreground, #cca700);
  }

  .verdict {
    padding-top: var(--wb-spacing-4);
  }

  .verdict-info {
    color: var(--vscode-testing-iconPassed, #4caf50);
  }

  .verdict-warn {
    color: var(--vscode-editorWarning-foreground, #cca700);
  }

  .verdict-error {
    color: var(--vscode-errorForeground, #f14c4c);
  }
</style>