Show SPA e621 request times in the status pill

- New e621 metrics store tracks request count, cumulative time and the
  last request (path + duration) for the session
- The e621 client measures each request around fetch and JSON parsing
- Status pill appends a teal 'e621: <total>' segment with a tooltip
  showing request count and the last request; a server-side e621 time
  is shown separately if the backend ever reports one
This commit is contained in:
2026-09-17 10:32:51 -05:00
parent ebac3ac922
commit afca89de8f
3 changed files with 81 additions and 20 deletions
+24 -2
View File
@@ -2,6 +2,7 @@ import { Laptop, Monitor, Terminal, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/cn";
import type { SystemStatus } from "@/lib/types";
import { useE621Metrics } from "@/store/e621Metrics";
const osIcons: Record<string, LucideIcon> = {
linux: Terminal,
@@ -10,10 +11,16 @@ const osIcons: Record<string, LucideIcon> = {
};
function formatMs(value: number): string {
return value >= 100 ? `${Math.round(value)}ms` : `${value.toFixed(1)}ms`;
if (value >= 1000) return `${(value / 1000).toFixed(1)}s`;
if (value >= 100) return `${Math.round(value)}ms`;
return `${value.toFixed(1)}ms`;
}
export function StatusPill({ status }: { status?: SystemStatus }) {
const requestCount = useE621Metrics((state) => state.requestCount);
const totalMs = useE621Metrics((state) => state.totalMs);
const last = useE621Metrics((state) => state.last);
if (!status) return null;
const OsIcon = osIcons[status.os.icon] ?? Monitor;
@@ -37,10 +44,25 @@ export function StatusPill({ status }: { status?: SystemStatus }) {
<span>{status.os.name}</span>
<span className="text-ctp-surface2">·</span>
<span>{formatMs(status.server_time_ms)}</span>
{requestCount > 0 ? (
<>
<span className="text-ctp-surface2">·</span>
<span
className="text-ctp-teal"
title={`${requestCount} e621 request${
requestCount === 1 ? "" : "s"
} this session · last ${last ? `${formatMs(last.ms)} (${last.path})` : "—"}`}
>
e621: {formatMs(totalMs)}
</span>
</>
) : null}
{status.e621_time_ms !== null ? (
<>
<span className="text-ctp-surface2">·</span>
<span>e621: {formatMs(status.e621_time_ms)}</span>
<span title="Server-side e621 time">
e621 (server): {formatMs(status.e621_time_ms)}
</span>
</>
) : null}
</span>
+26 -18
View File
@@ -1,10 +1,11 @@
import { useE621Metrics } from "@/store/e621Metrics";
export interface E621Credentials {
username: string;
api_key: string;
base_url: string;
configured: boolean;
}
export interface E621User {
id: number;
name: string;
@@ -141,26 +142,33 @@ export function e621Request<T>(
);
}
const response = await fetch(url, {
method: options.method ?? "GET",
headers,
body: options.body,
signal: options.signal,
});
const startedAt = performance.now();
try {
const response = await fetch(url, {
method: options.method ?? "GET",
headers,
body: options.body,
signal: options.signal,
});
if (!response.ok) {
let data: unknown = null;
try {
data = await response.json();
} catch {
data = null;
if (!response.ok) {
let data: unknown = null;
try {
data = await response.json();
} catch {
data = null;
}
throw new E621Error(response.status, data);
}
throw new E621Error(response.status, data);
if (response.status === 204) {
return undefined as T;
}
return (await response.json()) as T;
} finally {
useE621Metrics
.getState()
.record(path, performance.now() - startedAt);
}
if (response.status === 204) {
return undefined as T;
}
return (await response.json()) as T;
});
}
+31
View File
@@ -0,0 +1,31 @@
import { create } from "zustand";
export interface E621RequestSample {
path: string;
ms: number;
at: number;
}
interface E621MetricsState {
requestCount: number;
totalMs: number;
last: E621RequestSample | null;
record: (path: string, ms: number) => void;
reset: () => void;
}
/** Rolling e621 request timings for the current SPA session. */
export const useE621Metrics = create<E621MetricsState>((set) => ({
requestCount: 0,
totalMs: 0,
last: null,
record: (path, ms) =>
set((state) => ({
requestCount: state.requestCount + 1,
totalMs: state.totalMs + ms,
last: { path, ms, at: Date.now() },
})),
reset: () => set({ requestCount: 0, totalMs: 0, last: null }),
}));