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 type { TreeItemEntry } from "../../../api/types/workbench.js";
  import { TreeItemCollapsibleState } from "../../../api/types/workbench.js";
  import { commands } from "../../../stores/commands.svelte.js";
  import AppIcon from "../../layout/AppIcon.svelte";
  import TreeView from "./TreeView.svelte";

  interface Props {
    items: TreeItemEntry[];
    depth?: number;
  }

  let { items, depth = 0 }: Props = $props();

  // Track user overrides. Key = item.id, value = true (expanded) or false (collapsed).
  // Items not in this map use their initial collapsible_state.
  let overrides: Map<string, boolean> = $state(new Map());

  function toggle(item: TreeItemEntry) {
    if (item.collapsible_state === TreeItemCollapsibleState.None) return;
    const next = new Map(overrides);
    const currentlyExpanded = isExpanded(item);
    next.set(item.id, !currentlyExpanded);
    overrides = next;
  }

  function isExpanded(item: TreeItemEntry): boolean {
    const override = overrides.get(item.id);
    if (override !== undefined) return override;
    return item.collapsible_state === TreeItemCollapsibleState.Expanded;
  }

  function activate(item: TreeItemEntry) {
    if (item.command) {
      void commands.execute(item.command);
    }
  }
</script>

<ul class="tree-list" style:--depth={depth}>
  {#each items as item (item.id)}
    {@const expanded = isExpanded(item)}
    {@const hasChildren = item.collapsible_state !== TreeItemCollapsibleState.None}
    <li class="tree-item">
      <button
        class="tree-item-row"
        onclick={() => {
          if (hasChildren) toggle(item);
          else activate(item);
        }}
        title={item.tooltip ?? item.label}
      >
        {#if hasChildren}
          <span class="tree-chevron" class:expanded>
            <AppIcon name="chevron-right" size={12} />
          </span>
        {:else}
          <span class="tree-chevron-spacer"></span>
        {/if}
        <span class="tree-label">{item.label}</span>
        {#if item.description}
          <span class="tree-description">{item.description}</span>
        {/if}
      </button>
      {#if hasChildren && expanded && item.children.length > 0}
        <TreeView items={item.children} depth={depth + 1} />
      {/if}
    </li>
  {/each}
</ul>

<style>
  .tree-list {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .tree-item {
    margin: 0;
    padding: 0;
  }

  .tree-item-row {
    width: 100%;
    display: flex;
    align-items: center;
    gap: var(--wb-spacing-4);
    height: var(--wb-control-size-22);
    font-size: var(--wb-font-size-12);
    color: var(--vscode-foreground);
    cursor: pointer;
    text-align: left;
    padding-left: calc(
      var(--wb-tree-indent-base) +
      var(--depth) * var(--wb-tree-indent-step)
    );
  }

  .tree-item-row:hover {
    background: var(--vscode-list-hoverBackground);
    color: var(--vscode-editor-foreground);
  }

  .tree-chevron {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--wb-control-size-16);
    flex-shrink: 0;
    transition: transform 0.1s;
    color: var(--vscode-disabledForeground);
  }

  .tree-chevron.expanded {
    transform: rotate(90deg);
  }

  .tree-chevron-spacer {
    width: var(--wb-control-size-16);
    flex-shrink: 0;
  }

  .tree-label {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }

  .tree-description {
    color: var(--vscode-disabledForeground);
    font-size: var(--wb-font-size-11);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex-shrink: 1;
    min-width: 0;
  }
</style>