ui: finish shadcn migration and remove legacy shim

Convert the remaining .btn usages (Concurrency, Performance, CaptureDialog)
to shadcn Button, fix CaptureDialog/PerformanceChart styles to shadcn
tokens, and remove the transitional legacy palette aliases and component
classes from index.css. Drop the now-unused lucide-svelte and shadcn-svelte
dependencies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UmuGqwNBJNEAMaWsdCDqUC
This commit is contained in:
Claude
2026-06-27 12:10:56 +00:00
parent b20be6dcd1
commit 0ab9e74333
7 changed files with 46 additions and 165 deletions
-47
View File
@@ -11,7 +11,6 @@
"chart.js": "4.5.1",
"highlight.js": "^11.11.1",
"katex": "^0.16.28",
"lucide-svelte": "^0.563.0",
"rehype-katex": "^7.0.1",
"rehype-stringify": "^10.0.1",
"remark-gfm": "^4.0.1",
@@ -33,7 +32,6 @@
"bits-ui": "^2.18.1",
"clsx": "^2.1.1",
"paneforge": "^1.0.2",
"shadcn-svelte": "^1.3.0",
"svelte": "^5.46.4",
"svelte-check": "^4.1.4",
"tailwind-merge": "^3.6.0",
@@ -1980,15 +1978,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/lucide-svelte": {
"version": "0.563.0",
"resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.563.0.tgz",
"integrity": "sha512-pjZKw7TpQcamfQrx7YdbOHgmrcNeKiGGMD0tKZQaVktwSsbqw28CsKc2Q97ttwjytiCWkJyOa8ij2Q+Og0nPfQ==",
"license": "ISC",
"peerDependencies": {
"svelte": "^3 || ^4 || ^5.0.0-next.42"
}
},
"node_modules/lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
@@ -2865,13 +2854,6 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/node-fetch-native": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
"dev": true,
"license": "MIT"
},
"node_modules/obug": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
@@ -3240,35 +3222,6 @@
"node": ">=6"
}
},
"node_modules/shadcn-svelte": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/shadcn-svelte/-/shadcn-svelte-1.3.0.tgz",
"integrity": "sha512-Pd4ICWTkTks/b2YU4c9vF2XsX1x5HFPRl5bKszS1LcnWS83x+7T4WiIvbWz8Qh9knkcGZ+SCz1+Dmhdq+AYooA==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "^14.0.0",
"node-fetch-native": "^1.6.4",
"postcss": "^8.5.5",
"tailwind-merge": "^3.0.0"
},
"bin": {
"shadcn-svelte": "dist/index.mjs"
},
"peerDependencies": {
"svelte": "^5.0.0"
}
},
"node_modules/shadcn-svelte/node_modules/commander": {
"version": "14.0.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
-2
View File
@@ -22,7 +22,6 @@
"bits-ui": "^2.18.1",
"clsx": "^2.1.1",
"paneforge": "^1.0.2",
"shadcn-svelte": "^1.3.0",
"svelte": "^5.46.4",
"svelte-check": "^4.1.4",
"tailwind-merge": "^3.6.0",
@@ -38,7 +37,6 @@
"chart.js": "4.5.1",
"highlight.js": "^11.11.1",
"katex": "^0.16.28",
"lucide-svelte": "^0.563.0",
"rehype-katex": "^7.0.1",
"rehype-stringify": "^10.0.1",
"remark-gfm": "^4.0.1",
+21 -20
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import type { ReqRespCapture } from "../lib/types";
import { Button } from "$lib/components/ui/button/index.js";
interface Props {
capture: ReqRespCapture | null;
@@ -198,7 +199,7 @@
{#if capture}
<div class="flex flex-col max-h-[90vh]">
<div
class="flex justify-between items-center p-4 border-b border-card-border"
class="flex justify-between items-center p-4 border-b border-border"
>
<h2 class="text-xl font-bold pb-0">Capture #{capture.id + 1}{#if capture.req_path} <span class="text-base font-mono font-normal text-muted-foreground">{capture.req_path}</span>{/if}</h2>
<button
@@ -218,12 +219,12 @@
Request Headers
</summary>
<div
class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-48"
class="mt-2 bg-background rounded border border-border overflow-auto max-h-48"
>
<table class="w-full text-sm">
<tbody>
{#each Object.entries(capture.req_headers || {}) as [key, value]}
<tr class="border-b border-card-border-inner last:border-0">
<tr class="border-b border-border last:border-0">
<td class="px-3 py-1 font-mono text-primary whitespace-nowrap"
>{key}</td
>
@@ -271,14 +272,14 @@
</button>
</div>
<div
class="mt-1 bg-background rounded border border-card-border overflow-auto max-h-96"
class="mt-1 bg-background rounded border border-border overflow-auto max-h-96"
>
<pre
class="p-3 text-sm font-mono whitespace-pre-wrap break-all">{displayedRequestBody}</pre>
</div>
{:else}
<div
class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-96"
class="mt-2 bg-background rounded border border-border overflow-auto max-h-96"
>
<pre class="p-3 text-sm font-mono whitespace-pre-wrap break-all"
>(empty)</pre
@@ -295,12 +296,12 @@
Response Headers
</summary>
<div
class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-48"
class="mt-2 bg-background rounded border border-border overflow-auto max-h-48"
>
<table class="w-full text-sm">
<tbody>
{#each Object.entries(capture.resp_headers || {}) as [key, value]}
<tr class="border-b border-card-border-inner last:border-0">
<tr class="border-b border-border last:border-0">
<td class="px-3 py-1 font-mono text-primary whitespace-nowrap"
>{key}</td
>
@@ -321,7 +322,7 @@
</summary>
{#if isResponseImage && capture.resp_body}
<div
class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-96"
class="mt-2 bg-background rounded border border-border overflow-auto max-h-96"
>
<div class="p-3 flex justify-center">
<img
@@ -368,7 +369,7 @@
</button>
</div>
<div
class="mt-1 bg-background rounded border border-card-border overflow-auto max-h-96"
class="mt-1 bg-background rounded border border-border overflow-auto max-h-96"
>
{#if respBodyTab === "chat"}
<div class="p-3 text-sm space-y-3">
@@ -407,7 +408,7 @@
</div>
{:else if responseBodyRaw}
<div
class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-96"
class="mt-2 bg-background rounded border border-border overflow-auto max-h-96"
>
<div class="p-3 text-sm text-muted-foreground italic">
(binary data - {responseContentType || "unknown content type"})
@@ -415,7 +416,7 @@
</div>
{:else}
<div
class="mt-2 bg-background rounded border border-card-border overflow-auto max-h-96"
class="mt-2 bg-background rounded border border-border overflow-auto max-h-96"
>
<pre class="p-3 text-sm font-mono">(empty)</pre>
</div>
@@ -423,8 +424,8 @@
</details>
</div>
<div class="p-4 border-t border-card-border flex justify-end">
<button onclick={() => dialogEl?.close()} class="btn"> Close </button>
<div class="p-4 border-t border-border flex justify-end">
<Button variant="outline" onclick={() => dialogEl?.close()}>Close</Button>
</div>
</div>
{:else}
@@ -432,7 +433,7 @@
<p class="text-lg text-muted-foreground">Capture not found</p>
<p class="text-sm text-muted-foreground mt-1">The capture may have expired or was never recorded.</p>
<div class="mt-4">
<button onclick={() => dialogEl?.close()} class="btn">Close</button>
<Button variant="outline" onclick={() => dialogEl?.close()}>Close</Button>
</div>
</div>
{/if}
@@ -443,19 +444,19 @@
padding: 2px 10px;
font-size: 0.75rem;
border-radius: 4px;
color: var(--color-txtsecondary);
color: var(--muted-foreground);
cursor: pointer;
border: 1px solid transparent;
background: transparent;
transition: all 0.15s;
}
.tab-btn:hover {
color: var(--color-txtmain);
background: var(--color-secondary);
color: var(--foreground);
background: var(--accent);
}
.tab-btn-active {
color: var(--color-primary);
background: color-mix(in srgb, var(--color-primary) 12%, transparent);
border-color: color-mix(in srgb, var(--color-primary) 25%, transparent);
color: var(--primary);
background: color-mix(in srgb, var(--primary) 12%, transparent);
border-color: color-mix(in srgb, var(--primary) 25%, transparent);
}
</style>
@@ -143,6 +143,6 @@
});
</script>
<div class="card p-4 h-[300px]">
<div class="bg-card text-card-foreground h-[300px] rounded-xl border p-4 shadow-sm">
<canvas bind:this={canvas}></canvas>
</div>
@@ -2,6 +2,7 @@
import { models } from "../../stores/api";
import { persistentStore } from "../../stores/persistent";
import { streamChatCompletion } from "../../lib/chatApi";
import { Button } from "$lib/components/ui/button/index.js";
type Status = "waiting" | "streaming" | "done" | "error";
type Phase = "waiting" | "loading" | "reasoning" | "content";
@@ -366,22 +367,21 @@
<!-- Run controls -->
<div class="flex items-center gap-2">
{#if isRunning}
<button class="btn bg-red-500 hover:bg-red-600 text-white border-red-500" onclick={stop}>
<span class="inline-block w-3 h-3 bg-white align-middle mr-2"></span>Stop
</button>
<Button variant="destructive" onclick={stop}>
<span class="mr-1 inline-block h-3 w-3 bg-current align-middle"></span>Stop
</Button>
{:else}
<button
class="btn bg-primary text-primary-foreground hover:opacity-90"
<Button
onclick={run}
disabled={!canRun}
title={$testListStore.length === 0 ? "Add models from the list below" : "Run concurrent requests"}
>
<span class="inline-block align-middle mr-2" aria-hidden="true"></span>Go
</button>
<span class="mr-1 inline-block align-middle" aria-hidden="true"></span>Go
</Button>
{/if}
<button class="btn btn--sm" onclick={clearAll} disabled={isRunning || $testListStore.length === 0}>
<Button variant="outline" size="sm" onclick={clearAll} disabled={isRunning || $testListStore.length === 0}>
Clear ({$testListStore.length})
</button>
</Button>
</div>
<!-- Available models -->
+1 -57
View File
@@ -136,16 +136,6 @@
--color-warning: var(--warning);
--color-info: var(--info);
--color-error: var(--destructive);
/* legacy aliases (transitional — removed as views migrate) */
--color-surface: var(--card);
--color-txtmain: var(--foreground);
--color-txtsecondary: var(--muted-foreground);
--color-navlink-active: var(--primary-foreground);
--color-card-border: var(--border);
--color-card-border-inner: var(--border);
--color-btn-border: var(--border);
--color-btn-primary-text: var(--primary-foreground);
}
@layer base {
@@ -177,60 +167,14 @@
}
}
/* Transitional component classes — kept until all views move to shadcn primitives. */
@layer components {
/* default padding for ad-hoc tables (header/detail views) */
table th {
@apply p-2 font-semibold;
}
table td {
@apply p-2;
}
.navlink {
@apply text-muted-foreground hover:bg-accent hover:text-foreground rounded-lg p-2;
}
.navlink.active {
@apply bg-primary text-primary-foreground;
}
.card {
@apply bg-card text-card-foreground rounded-xl border shadow-sm overflow-hidden p-4;
}
.card__body {
@apply p-4;
}
.card__header,
.card__footer {
@apply p-4 border-b;
}
.status {
@apply inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-md;
}
.status--ready {
@apply bg-success/10 text-success;
}
.status--starting,
.status--stopping,
.status--queued {
@apply bg-warning/10 text-warning;
}
.status--stopped {
@apply bg-destructive/10 text-destructive;
}
.btn {
@apply bg-background py-2 px-4 text-sm rounded-md border transition-colors duration-200;
}
.btn:hover {
@apply bg-muted cursor-pointer;
}
.btn--sm {
@apply px-2 py-0.5 text-xs;
}
.btn:disabled {
@apply opacity-50 cursor-not-allowed;
}
}
@utility activity-link {
+14 -29
View File
@@ -4,6 +4,8 @@
import { persistentStore } from "../stores/persistent";
import type { SysStat, GpuStat } from "../lib/types";
import PerformanceChart from "../components/PerformanceChart.svelte";
import { Button } from "$lib/components/ui/button/index.js";
import { RefreshCw } from "@lucide/svelte";
const COLORS = [
"#3b82f6",
@@ -356,47 +358,30 @@
<div class="flex items-center gap-4">
<div class="flex items-center gap-1">
{#each WINDOWS as win, i}
<button
class="btn btn--sm"
class:bg-primary={$selectedWindow === i}
class:text-primary-foreground={$selectedWindow === i}
<Button
variant={$selectedWindow === i ? "default" : "outline"}
size="sm"
onclick={() => ($selectedWindow = i)}
>
{win.label}
</button>
</Button>
{/each}
</div>
<div class="flex items-center gap-1">
<span class="text-xs text-muted-foreground mr-1">Refresh:</span>
{#each INTERVALS as intv, i}
<button
class="btn btn--sm"
class:bg-primary={$selectedInterval === i}
class:text-primary-foreground={$selectedInterval === i}
<Button
variant={$selectedInterval === i ? "default" : "outline"}
size="sm"
onclick={() => handleIntervalChange(i)}
>
{intv.label}
</button>
</Button>
{/each}
</div>
<button class="btn btn--sm p-1" title="Refresh" onclick={manualRefresh} disabled={refreshing}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-4 h-4"
class:animate-spin={refreshing}
>
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
<path d="M3 3v5h5" />
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" />
<path d="M16 16h5v5" />
</svg>
</button>
<Button variant="outline" size="icon-sm" title="Refresh" onclick={manualRefresh} disabled={refreshing}>
<RefreshCw class={refreshing ? "animate-spin" : ""} />
</Button>
</div>
</div>
<p class="text-sm text-muted-foreground">
@@ -410,7 +395,7 @@
<section class="space-y-4">
<h3 class="text-lg font-medium text-foreground">GPU</h3>
{#if !hasGpuData}
<p class="text-muted-foreground card p-4">No GPU data available</p>
<p class="text-muted-foreground bg-card rounded-xl border p-4 shadow-sm">No GPU data available</p>
{:else}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<PerformanceChart