Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156<script lang="ts"> import type { Snippet } from 'svelte';
// A title-bar icon that lists things: the session's background jobs, the // artifacts it made, the agents it started. The list closes on a pick, a click elsewhere or Escape; // `up` opens it above the icon, for the phone's composer; `inline` renders the list // straight into the body of an undocked chip window. let { label, count = 0, live = false, up = false, inline = false, open = $bindable(false), icon, children, }: { label: string; count?: number; live?: boolean; up?: boolean; inline?: boolean; open?: boolean; icon: Snippet; children: Snippet } = $props(); let element = $state<HTMLElement>(); let list = $state<HTMLElement>(); // The list hangs left from the icon's right edge, moved right by as much as // it would otherwise stick out past the left edge of its window. let shift = $state(0);
$effect(() => { if (!open || inline || up || !list || !element) return; const frame = (element.closest('.svelte-flow__node') ?? document.body).getBoundingClientRect(); const icon = element.getBoundingClientRect(); // The canvas scales a window; CSS lengths are unscaled. const scale = icon.width / element.offsetWidth || 1; const past = frame.left + 8 - (icon.right - list.getBoundingClientRect().width); shift = Math.max(0, past) / scale; });
$effect(() => { if (!open || inline) return; const away = (event: Event) => event.target instanceof Node && !element?.contains(event.target) && (open = false); const escape = (event: KeyboardEvent) => event.key === 'Escape' && (open = false); addEventListener('pointerdown', away, true); addEventListener('keydown', escape); return () => { removeEventListener('pointerdown', away, true); removeEventListener('keydown', escape); }; });</script>
{#if inline} <ul class="list inline nowheel holds-wheel"> {@render children()} </ul>{:else} <span class="menu nodrag" class:open bind:this={element}> <button class="icon" class:live aria-expanded={open} aria-label={label} title={label} onclick={() => (open = !open)}> {@render icon()} {#if count}<span class="count">{count}</span>{/if} </button> {#if open} <ul class="list nowheel holds-wheel" class:up style:right={shift ? `${-shift}px` : undefined} bind:this={list}> {@render children()} </ul> {/if} </span>{/if}
<style> .menu { position: relative; display: inline-flex; } .menu.open { z-index: 30; } .icon { display: inline-flex; align-items: center; gap: 4px; height: 26px; padding: 0 7px; color: var(--muted); font: inherit; font-size: calc(11.5px * var(--ui-scale)); background: var(--well); border: 0; border-radius: 8px; cursor: pointer; transition: background-color 120ms ease, color 120ms ease; } .icon:hover, .icon[aria-expanded='true'] { color: var(--text); background: var(--field); } .icon.live { color: var(--accent); } .count { font-variant-numeric: tabular-nums; } .list { position: absolute; top: calc(100% + 6px); right: 0; z-index: 20; width: min(340px, 80vw); max-height: 260px; margin: 0; padding: 4px; overflow-y: auto; overscroll-behavior: contain; list-style: none; background: var(--surface); border-radius: 12px; box-shadow: var(--shadow-raised); } .list.up { top: auto; bottom: calc(100% + 6px); right: auto; left: 0; } .list.inline { position: static; width: 100%; max-height: 280px; padding: 4px 6px 8px; background: none; border-radius: 0 0 14px 14px; box-shadow: none; } .list :global(li > button) { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: baseline; gap: 10px; width: 100%; padding: 7px 10px; color: var(--text); font: inherit; font-size: calc(12.5px * var(--ui-scale)); text-align: left; background: none; border: 0; border-radius: 8px; cursor: pointer; } .list :global(li > button:hover) { background: var(--hover); } .list :global(.when) { color: var(--muted); font-size: calc(11.5px * var(--ui-scale)); font-variant-numeric: tabular-nums; white-space: nowrap; }</style>