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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118<script lang="ts"> import { frameUrl, latestFrameUrl } from '../api'; import { artifacts } from '../artifacts.svelte'; import type { ArtifactWindow, WindowProps } from '../board.svelte'; import { boards } from '../boards.svelte'; import { ago } from '../format'; import MenuButton from '../MenuButton.svelte'; import ArtifactView from '../views/ArtifactView.svelte'; import WindowFrame from './WindowFrame.svelte';
// A page the agent made, beside the chat it came from. It follows the // newest version unless pinned to one from the version menu. let { id, data, selected }: WindowProps<ArtifactWindow> = $props(); const detail = $derived(artifacts.get(data.artifact)); const shown = $derived(data.version ?? detail?.version); const entry = $derived(detail?.history.find((item) => item.version === shown)?.entry ?? detail?.entry ?? 'index.html'); // A tab follows as the window does: the latest address reloads itself. const tab = $derived(detail && shown ? (data.version === null ? latestFrameUrl(detail) : frameUrl(detail, shown, entry)) : null); let reloads = $state(0); let failures = $state<string[]>([]); let listed = $state(false);
/** Follows the newest version, or pins `version`. */ function pin(version: number | null) { listed = false; boards.board?.pinArtifact(id, version); }</script>
<WindowFrame {id} selected={!!selected} title={detail?.title ?? 'artifact'}> {#snippet meta()} {#if failures.length} <span class="failed" title={failures.join('\n')}>{failures.length === 1 ? 'an error' : `${failures.length} errors`}</span> {/if} {#if detail && shown} <span class="page nodrag"> <MenuButton label={data.version === null ? 'version: following the newest' : 'version: pinned'} bind:open={listed}> {#snippet icon()} v{shown}{#if detail.versions > 1 && data.version !== null}<span class="how">pinned</span>{/if} {/snippet} <li> <button class:on={data.version === null} onclick={() => pin(null)}> <span>follow the newest</span><span class="when">v{detail.version}</span><span class="check">✓</span> </button> </li> {#each detail.history as item (item.version)} <li> <button class:on={data.version === item.version} onclick={() => pin(item.version)}> <span>v{item.version}</span><span class="when">{ago(new Date(item.created_at).toISOString())}</span><span class="check">✓</span> </button> </li> {/each} </MenuButton> <a class="tab" href={tab} target="_blank" rel="noopener noreferrer" aria-label="Open in a browser tab" title={data.version === null ? 'Open in a browser tab, following new versions' : `Open v${shown} in a browser tab`}> <svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true"><path d="M9 3h4v4M13 3L7.5 8.5M11 9.5V12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h2.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg> </a> </span> {/if} {/snippet}
{#snippet menu()} {#if detail && shown} <button class="item" role="menuitem" onclick={() => reloads++}> <svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true"><path d="M13 8a5 5 0 1 1-1.46-3.54M13 3v3h-3" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>reload the page </button> {/if} {/snippet}
<ArtifactView windowId={id} artifactId={data.artifact} sessionId={data.sessionId} latest={data.latest} version={data.version} {reloads} onfailures={(found) => (failures = found)} /></WindowFrame>
<style> .failed { color: var(--danger); } /* The version and the tab it opens, one soft pill. */ .page { display: inline-flex; align-items: center; height: 24px; background: color-mix(in oklab, var(--text) 6%, transparent); border-radius: 999px; } .page :global(.icon) { height: 24px; padding: 0 4px 0 10px; background: none; border-radius: 999px 0 0 999px; } .page :global(.icon:hover), .page :global(.icon[aria-expanded='true']), .tab:hover { color: var(--text); background: var(--hover); } .tab { display: grid; place-items: center; width: 26px; height: 24px; padding-right: 2px; color: var(--muted); border-radius: 0 999px 999px 0; transition: background-color 120ms ease, color 120ms ease; }
.how { color: var(--faint); } .check { visibility: hidden; color: var(--accent); } .on .check { visibility: visible; }</style>