ui: reorganize sidebar and add Settings page
Reorder sidebar menu to Activity, Playground, Models, Logs. Remove the ll header icon and replace it with the connection status indicator moved from the footer. Add a Settings page (gear icon) at the bottom that surfaces the build information that was previously hidden behind the status indicator's tooltip. - move ConnectionStatus into Sidebar.Header, drop build-info tooltip - add Settings.svelte route showing version/commit/build date - register /settings route and title in App.svelte
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
import Performance from "./routes/Performance.svelte";
|
||||
import Playground from "./routes/Playground.svelte";
|
||||
import PlaygroundStub from "./routes/PlaygroundStub.svelte";
|
||||
import Settings from "./routes/Settings.svelte";
|
||||
import * as Sidebar from "$lib/components/ui/sidebar/index.js";
|
||||
import * as Tooltip from "$lib/components/ui/tooltip/index.js";
|
||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||
@@ -23,6 +24,7 @@
|
||||
"/models/:id": ModelDetail,
|
||||
"/logs": LogViewer,
|
||||
"/activity": Activity,
|
||||
"/settings": Settings,
|
||||
"/performance": Performance,
|
||||
"*": PlaygroundStub,
|
||||
};
|
||||
@@ -32,6 +34,7 @@
|
||||
"/models": "Models",
|
||||
"/activity": "Activity",
|
||||
"/logs": "Logs",
|
||||
"/settings": "Settings",
|
||||
"/performance": "Performance",
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { link } from "svelte-spa-router";
|
||||
import { House, Boxes, Activity, ScrollText, Gauge, Sun, Moon, Monitor, ChevronRight } from "@lucide/svelte";
|
||||
import { House, Boxes, Activity, ScrollText, Gauge, Sun, Moon, Monitor, ChevronRight, Settings } from "@lucide/svelte";
|
||||
import * as Sidebar from "$lib/components/ui/sidebar/index.js";
|
||||
import * as Collapsible from "$lib/components/ui/collapsible/index.js";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
@@ -53,10 +53,8 @@
|
||||
<Sidebar.Root collapsible="icon">
|
||||
<Sidebar.Header>
|
||||
<div class="flex items-center gap-2 px-2 py-1.5">
|
||||
<div
|
||||
class="bg-primary text-primary-foreground flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg font-bold"
|
||||
>
|
||||
ll
|
||||
<div class="flex shrink-0 items-center justify-center">
|
||||
<ConnectionStatus />
|
||||
</div>
|
||||
<h1
|
||||
contenteditable="true"
|
||||
@@ -74,61 +72,14 @@
|
||||
<Sidebar.GroupContent>
|
||||
<Sidebar.Menu class="gap-1">
|
||||
<Sidebar.MenuItem>
|
||||
<Collapsible.Root
|
||||
open={$modelsMenuOpen}
|
||||
onOpenChange={(v) => modelsMenuOpen.set(v)}
|
||||
class="gap-0"
|
||||
>
|
||||
<Sidebar.MenuButton
|
||||
isActive={$currentRoute.startsWith("/models")}
|
||||
tooltipContent="Models"
|
||||
>
|
||||
{#snippet child({ props })}
|
||||
<a href="/models" use:link {...props}>
|
||||
<Boxes />
|
||||
<span>Models</span>
|
||||
<span
|
||||
class="ml-auto transition-transform duration-200 {$modelsMenuOpen ? 'rotate-90' : ''}"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Toggle models section"
|
||||
onclick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
modelsMenuOpen.update((v) => !v);
|
||||
}}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
modelsMenuOpen.update((v) => !v);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ChevronRight />
|
||||
</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
<Collapsible.Content>
|
||||
<Sidebar.MenuSub>
|
||||
{#each $models as model (model.id)}
|
||||
<Sidebar.MenuSubItem>
|
||||
<Sidebar.MenuSubButton
|
||||
isActive={$currentRoute === `/models/${encodeURIComponent(model.id)}`}
|
||||
>
|
||||
{#snippet child({ props })}
|
||||
<a href="/models/{encodeURIComponent(model.id)}" use:link {...props}>
|
||||
<span class={`size-2 shrink-0 rounded-full ${dotClass[statusDotColor(model)]}`}></span>
|
||||
<span class="flex-1 truncate">{model.id}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuSubButton>
|
||||
</Sidebar.MenuSubItem>
|
||||
{/each}
|
||||
</Sidebar.MenuSub>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
<Sidebar.MenuButton isActive={isActive("/activity", $currentRoute)} tooltipContent="Activity">
|
||||
{#snippet child({ props })}
|
||||
<a href="/activity" use:link {...props}>
|
||||
<Activity />
|
||||
<span>Activity</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
|
||||
<Sidebar.MenuItem>
|
||||
@@ -194,14 +145,61 @@
|
||||
</Sidebar.MenuItem>
|
||||
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton isActive={isActive("/activity", $currentRoute)} tooltipContent="Activity">
|
||||
{#snippet child({ props })}
|
||||
<a href="/activity" use:link {...props}>
|
||||
<Activity />
|
||||
<span>Activity</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
<Collapsible.Root
|
||||
open={$modelsMenuOpen}
|
||||
onOpenChange={(v) => modelsMenuOpen.set(v)}
|
||||
class="gap-0"
|
||||
>
|
||||
<Sidebar.MenuButton
|
||||
isActive={$currentRoute.startsWith("/models")}
|
||||
tooltipContent="Models"
|
||||
>
|
||||
{#snippet child({ props })}
|
||||
<a href="/models" use:link {...props}>
|
||||
<Boxes />
|
||||
<span>Models</span>
|
||||
<span
|
||||
class="ml-auto transition-transform duration-200 {$modelsMenuOpen ? 'rotate-90' : ''}"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Toggle models section"
|
||||
onclick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
modelsMenuOpen.update((v) => !v);
|
||||
}}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
modelsMenuOpen.update((v) => !v);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ChevronRight />
|
||||
</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
<Collapsible.Content>
|
||||
<Sidebar.MenuSub>
|
||||
{#each $models as model (model.id)}
|
||||
<Sidebar.MenuSubItem>
|
||||
<Sidebar.MenuSubButton
|
||||
isActive={$currentRoute === `/models/${encodeURIComponent(model.id)}`}
|
||||
>
|
||||
{#snippet child({ props })}
|
||||
<a href="/models/{encodeURIComponent(model.id)}" use:link {...props}>
|
||||
<span class={`size-2 shrink-0 rounded-full ${dotClass[statusDotColor(model)]}`}></span>
|
||||
<span class="flex-1 truncate">{model.id}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuSubButton>
|
||||
</Sidebar.MenuSubItem>
|
||||
{/each}
|
||||
</Sidebar.MenuSub>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
</Sidebar.MenuItem>
|
||||
|
||||
<Sidebar.MenuItem>
|
||||
@@ -236,9 +234,17 @@
|
||||
<div
|
||||
class="flex items-center justify-between gap-2 px-1 group-data-[collapsible=icon]:flex-col-reverse"
|
||||
>
|
||||
<div class="flex items-center gap-2 px-1">
|
||||
<ConnectionStatus />
|
||||
</div>
|
||||
<Sidebar.MenuButton
|
||||
isActive={isActive("/settings", $currentRoute)}
|
||||
tooltipContent="Settings"
|
||||
>
|
||||
{#snippet child({ props })}
|
||||
<a href="/settings" use:link {...props}>
|
||||
<Settings />
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { connectionState } from "../stores/theme";
|
||||
import { versionInfo } from "../stores/api";
|
||||
|
||||
let eventStatusColor = $derived.by(() => {
|
||||
switch ($connectionState) {
|
||||
@@ -14,9 +13,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
let tooltipText = $derived(
|
||||
`Event Stream: ${$connectionState ?? "unknown"}\nAPI Version: ${$versionInfo?.version ?? "unknown"}\nCommit Hash: ${$versionInfo?.commit?.substring(0, 7) ?? "unknown"}\nBuild Date: ${$versionInfo?.build_date ?? "unknown"}`
|
||||
);
|
||||
let tooltipText = $derived(`Event Stream: ${$connectionState ?? "unknown"}`);
|
||||
</script>
|
||||
|
||||
<div class="flex items-center" title={tooltipText}>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { connectionState } from "../stores/theme";
|
||||
import { versionInfo } from "../stores/api";
|
||||
</script>
|
||||
|
||||
<div class="p-2">
|
||||
<div class="mt-4 mb-4">
|
||||
<h3 class="text-lg font-semibold">Settings</h3>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4 space-y-2 max-w-md">
|
||||
<h4 class="text-sm font-semibold text-muted-foreground">Build Information</h4>
|
||||
<dl class="text-sm space-y-1">
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-muted-foreground">Event Stream</dt>
|
||||
<dd class="font-medium">{$connectionState ?? "unknown"}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-muted-foreground">API Version</dt>
|
||||
<dd class="font-medium">{$versionInfo?.version ?? "unknown"}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-muted-foreground">Commit Hash</dt>
|
||||
<dd class="font-medium">{$versionInfo?.commit?.substring(0, 7) ?? "unknown"}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<dt class="text-muted-foreground">Build Date</dt>
|
||||
<dd class="font-medium">{$versionInfo?.build_date ?? "unknown"}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user