feat(ui,api): strip board redesign - island, rail by day, context view, server search, vault graph
This commit is contained in:
@@ -2,15 +2,12 @@
|
||||
import { onMount, untrack } from "svelte";
|
||||
import type { ApiClient } from "$lib/api/client";
|
||||
import ErrorNote from "$lib/components/error-note.svelte";
|
||||
import { Skeleton } from "$lib/components/ui/skeleton";
|
||||
import * as Tabs from "$lib/components/ui/tabs";
|
||||
import ActivityFeed from "./activity-feed.svelte";
|
||||
import BindingsList from "./bindings-list.svelte";
|
||||
import ChatView from "./chat-view.svelte";
|
||||
import Composer from "./composer.svelte";
|
||||
import ContextView from "./context-view.svelte";
|
||||
import { ConversationFeed } from "./conversation.svelte";
|
||||
import ConversationHeader from "./conversation-header.svelte";
|
||||
import QueueList from "./queue-list.svelte";
|
||||
import RawEntries from "./raw-entries.svelte";
|
||||
|
||||
let {
|
||||
@@ -19,6 +16,7 @@
|
||||
href = null,
|
||||
onOpen,
|
||||
showTitle = true,
|
||||
initialView = "chat",
|
||||
}: {
|
||||
client: ApiClient;
|
||||
id: string;
|
||||
@@ -26,13 +24,15 @@
|
||||
onOpen: (id: string) => void;
|
||||
// Off when a switcher above the thread already names it.
|
||||
showTitle?: boolean;
|
||||
// "activity" beside a deep chat's own note: the file is the thread.
|
||||
initialView?: string;
|
||||
} = $props();
|
||||
|
||||
const feed = new ConversationFeed(
|
||||
() => client,
|
||||
untrack(() => id)
|
||||
);
|
||||
let tab = $state("chat");
|
||||
let view = $state(untrack(() => initialView));
|
||||
let historyKey = $state(0);
|
||||
|
||||
onMount(() => {
|
||||
@@ -61,9 +61,10 @@
|
||||
{#if feed.error && !info}
|
||||
<div class="p-4"><ErrorNote message={feed.error} retry={refresh} /></div>
|
||||
{:else if !info}
|
||||
<div class="flex flex-col gap-3 p-4">
|
||||
<Skeleton class="h-8 w-1/2" />
|
||||
<Skeleton class="h-24 w-full" />
|
||||
<div class="flex flex-1 items-center justify-center p-4">
|
||||
<span
|
||||
class="size-2 animate-pulse-dot rounded-full bg-muted-foreground/40"
|
||||
></span>
|
||||
</div>
|
||||
{:else}
|
||||
<ConversationHeader
|
||||
@@ -75,49 +76,36 @@
|
||||
onChanged={refresh}
|
||||
{onOpen}
|
||||
{showTitle}
|
||||
bind:view
|
||||
/>
|
||||
<div class="flex min-h-0 flex-1 flex-col">
|
||||
<Tabs.Root class="flex min-h-0 flex-1 flex-col gap-0" bind:value={tab}>
|
||||
<div class="border-b px-3 py-2 @md:px-6">
|
||||
<Tabs.List class="w-fit max-w-full">
|
||||
<Tabs.Trigger value="chat">Chat</Tabs.Trigger>
|
||||
<Tabs.Trigger value="activity">Activity</Tabs.Trigger>
|
||||
<Tabs.Trigger value="raw">Raw</Tabs.Trigger>
|
||||
<Tabs.Trigger value="meta">Meta</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</div>
|
||||
<Tabs.Content class="flex min-h-0 flex-1 flex-col" value="chat">
|
||||
<ChatView
|
||||
{client}
|
||||
conversationId={id}
|
||||
model={feed.model}
|
||||
refreshKey={historyKey}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content
|
||||
class="min-h-0 flex-1 overflow-y-auto px-3 py-3 @md:px-6"
|
||||
value="activity"
|
||||
>
|
||||
{#if view === "chat"}
|
||||
<ChatView
|
||||
{client}
|
||||
conversationId={id}
|
||||
model={feed.model}
|
||||
refreshKey={historyKey}
|
||||
/>
|
||||
{:else if view === "activity"}
|
||||
<div class="min-h-0 flex-1 overflow-y-auto px-3 py-3 @md:px-6">
|
||||
<ActivityFeed
|
||||
{client}
|
||||
connected={feed.live.state === "open"}
|
||||
conversationId={id}
|
||||
model={feed.model}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content
|
||||
class="min-h-0 flex-1 overflow-y-auto px-3 py-3 @md:px-6"
|
||||
value="raw"
|
||||
>
|
||||
</div>
|
||||
{:else if view === "context"}
|
||||
<div class="min-h-0 flex-1 overflow-y-auto px-3 py-4 @md:px-6">
|
||||
{#key historyKey}
|
||||
<ContextView {client} conversationId={id} />
|
||||
{/key}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="min-h-0 flex-1 overflow-y-auto px-3 py-3 @md:px-6">
|
||||
<RawEntries {client} conversationId={id} />
|
||||
</Tabs.Content>
|
||||
<Tabs.Content
|
||||
class="min-h-0 flex-1 overflow-y-auto px-3 py-3 @md:px-6"
|
||||
value="meta"
|
||||
>
|
||||
{@render meta()}
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
{/if}
|
||||
<Composer
|
||||
{client}
|
||||
conversationId={id}
|
||||
@@ -126,60 +114,3 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#snippet meta()}
|
||||
{#if info}
|
||||
<div class="flex max-w-3xl flex-col gap-5">
|
||||
<section class="flex flex-col gap-1.5">
|
||||
<h2
|
||||
class="font-medium text-muted-foreground text-xs uppercase tracking-wide"
|
||||
>
|
||||
Queue
|
||||
</h2>
|
||||
<QueueList items={info.queue} />
|
||||
</section>
|
||||
<section class="flex flex-col gap-1.5">
|
||||
<h2
|
||||
class="font-medium text-muted-foreground text-xs uppercase tracking-wide"
|
||||
>
|
||||
Windows
|
||||
</h2>
|
||||
<BindingsList
|
||||
bindings={info.bindings}
|
||||
{client}
|
||||
conversationId={id}
|
||||
onChanged={refresh}
|
||||
/>
|
||||
</section>
|
||||
<section class="flex flex-col gap-1.5">
|
||||
<h2
|
||||
class="font-medium text-muted-foreground text-xs uppercase tracking-wide"
|
||||
>
|
||||
Flags
|
||||
</h2>
|
||||
{#if Object.keys(info.flags).length === 0}
|
||||
<p class="text-muted-foreground text-xs">No flags set.</p>
|
||||
{:else}
|
||||
<dl
|
||||
class="grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-xs"
|
||||
>
|
||||
{#each Object.entries(info.flags) as [key, value] (key)}
|
||||
<dt class="text-muted-foreground">{key}</dt>
|
||||
<dd class="truncate">{JSON.stringify(value)}</dd>
|
||||
{/each}
|
||||
</dl>
|
||||
{/if}
|
||||
</section>
|
||||
<section class="flex flex-col gap-1.5">
|
||||
<h2
|
||||
class="font-medium text-muted-foreground text-xs uppercase tracking-wide"
|
||||
>
|
||||
Session
|
||||
</h2>
|
||||
<p class="tabular break-all text-muted-foreground text-xs">
|
||||
{info.session_id ?? "no session yet"}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
Reference in New Issue
Block a user