mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-04 07:31:58 +00:00
refactor(web): dashboard typography & contrast pass
Removes the global `uppercase` + `font-mondwest` from the App.tsx root
that forced every page to opt-out, replaces stacked-alpha text colors
with semantic tokens for WCAG-AA contrast across all 7 themes, and
applies the new `text-display` utility from @nous-research/ui@0.16.0
on intentional brand chrome (page titles, sidebar headings, segmented
filters) only. Bumps every sub-12px arbitrary text size to text-xs.
Also widens the dashboard plugin routes (/api/dashboard/agent-plugins/
{name:path}/...) so category-namespaced plugins like observability/
langfuse and image_gen/openai can be enable/disabled from the dashboard
— previously the FE encodeURIComponent-ed the slash and the backend
{name} route rejected it. _validate_plugin_name still blocks .. and
backslash, and strips leading/trailing slash.
Touches sessions/env/keys page chrome and adds two new i18n keys
(`overview`, `showMore`/`showLess`) across all 18 locales.
Squashes 19 commits from PR #28832.
Co-authored-by: Hermes <noreply@nousresearch.com>
This commit is contained in:
parent
dc4b0465b5
commit
487c398dcf
54 changed files with 988 additions and 735 deletions
|
|
@ -11,8 +11,8 @@ function FieldHint({ schema, schemaKey }: { schema: Record<string, unknown>; sch
|
|||
|
||||
return (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
{keyPath && <span className="text-[10px] font-mono text-muted-foreground/50">{keyPath}</span>}
|
||||
{description && <span className="text-xs text-muted-foreground/70">{description}</span>}
|
||||
{keyPath && <span className="text-xs font-mono text-text-tertiary">{keyPath}</span>}
|
||||
{description && <span className="text-xs text-text-secondary">{description}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { Typography } from "@/components/NouiTypography";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, themedBody } from "@/lib/utils";
|
||||
|
||||
const CLOSE_DRAG_MIN_PX = 72;
|
||||
const CLOSE_DRAG_RATIO = 0.18;
|
||||
|
|
@ -168,6 +168,7 @@ export function BottomPickSheet({
|
|||
aria-modal="true"
|
||||
ref={sheetRef}
|
||||
className={cn(
|
||||
themedBody,
|
||||
"relative flex max-h-[85dvh] min-h-0 flex-col rounded-t-xl border border-current/20",
|
||||
"bg-background-base/98 pb-[max(1rem,env(safe-area-inset-bottom))]",
|
||||
"shadow-[0_-12px_40px_-8px_rgba(0,0,0,0.55)] backdrop-blur-md",
|
||||
|
|
@ -200,7 +201,7 @@ export function BottomPickSheet({
|
|||
|
||||
<Typography
|
||||
mondwest
|
||||
className="text-[0.65rem] tracking-[0.15em] uppercase text-midground/70"
|
||||
className="text-display text-xs tracking-[0.12em] text-text-tertiary"
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
|
|
|
|||
|
|
@ -304,13 +304,13 @@ export function ChatSidebar({ channel, className }: ChatSidebarProps) {
|
|||
return (
|
||||
<aside
|
||||
className={cn(
|
||||
"flex h-full w-full min-w-0 shrink-0 flex-col gap-3 overflow-y-auto overflow-x-hidden pr-1 normal-case lg:w-80",
|
||||
"flex h-full w-full min-w-0 shrink-0 flex-col gap-3 overflow-y-auto overflow-x-hidden pr-1 lg:w-80",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Card className="flex items-center justify-between gap-2 px-3 py-2">
|
||||
<div className="min-w-0">
|
||||
<div className="text-xs uppercase tracking-wider text-muted-foreground">
|
||||
<div className="text-display text-xs tracking-wider text-text-tertiary">
|
||||
model
|
||||
</div>
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ export function ChatSidebar({ channel, className }: ChatSidebarProps) {
|
|||
onClick={() => setModelOpen(true)}
|
||||
suffix={
|
||||
canPickModel ? (
|
||||
<ChevronDown className="opacity-60" />
|
||||
<ChevronDown className="text-text-secondary" />
|
||||
) : undefined
|
||||
}
|
||||
className="self-start min-w-0 px-0 py-0 normal-case tracking-normal text-sm font-medium hover:underline disabled:no-underline"
|
||||
|
|
@ -357,13 +357,13 @@ export function ChatSidebar({ channel, className }: ChatSidebarProps) {
|
|||
)}
|
||||
|
||||
<Card className="flex min-h-0 flex-none flex-col px-2 py-2">
|
||||
<div className="px-1 pb-2 text-xs uppercase tracking-wider text-muted-foreground">
|
||||
<div className="text-display px-1 pb-2 text-xs tracking-wider text-text-tertiary">
|
||||
tools
|
||||
</div>
|
||||
|
||||
<div className="flex min-h-0 flex-col gap-1.5">
|
||||
{tools.length === 0 ? (
|
||||
<div className="px-2 py-4 text-center text-xs text-muted-foreground">
|
||||
<div className="px-2 py-4 text-center text-xs text-text-secondary">
|
||||
no tool calls yet
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -69,12 +69,12 @@ export function LanguageSwitcher({ dropUp = false }: LanguageSwitcherProps) {
|
|||
aria-label={t.language.switchTo}
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded={open}
|
||||
className="px-2 py-1 normal-case tracking-normal font-normal text-xs text-muted-foreground hover:text-foreground"
|
||||
className="px-2 py-1 normal-case tracking-normal font-normal text-xs text-text-secondary hover:text-foreground"
|
||||
>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Typography
|
||||
mondwest
|
||||
className="hidden sm:inline tracking-wide uppercase text-[0.65rem]"
|
||||
className="hidden sm:inline text-display tracking-wide text-xs"
|
||||
>
|
||||
{locale === "en" ? "EN" : current.name}
|
||||
</Typography>
|
||||
|
|
|
|||
|
|
@ -60,11 +60,11 @@ export function ModelInfoCard({
|
|||
{formatTokenCount(info.effective_context_length)}
|
||||
</span>
|
||||
{info.config_context_length > 0 ? (
|
||||
<span className="text-amber-500/80 text-[10px]">
|
||||
<span className="text-amber-500 text-xs">
|
||||
(override — auto: {formatTokenCount(info.auto_context_length)})
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground/60 text-[10px]">
|
||||
<span className="text-text-tertiary text-xs">
|
||||
auto-detected
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -86,22 +86,22 @@ export function ModelInfoCard({
|
|||
{hasCaps && (
|
||||
<div className="flex flex-wrap items-center gap-1.5 pt-0.5">
|
||||
{caps.supports_tools && (
|
||||
<span className="inline-flex items-center gap-1 bg-emerald-500/10 px-2 py-0.5 text-[10px] font-medium text-emerald-600 dark:text-emerald-400">
|
||||
<span className="inline-flex items-center gap-1 bg-emerald-500/10 px-2 py-0.5 text-xs font-medium text-emerald-600 dark:text-emerald-400">
|
||||
<Wrench className="h-2.5 w-2.5" /> Tools
|
||||
</span>
|
||||
)}
|
||||
{caps.supports_vision && (
|
||||
<span className="inline-flex items-center gap-1 bg-blue-500/10 px-2 py-0.5 text-[10px] font-medium text-blue-600 dark:text-blue-400">
|
||||
<span className="inline-flex items-center gap-1 bg-blue-500/10 px-2 py-0.5 text-xs font-medium text-blue-600 dark:text-blue-400">
|
||||
<Eye className="h-2.5 w-2.5" /> Vision
|
||||
</span>
|
||||
)}
|
||||
{caps.supports_reasoning && (
|
||||
<span className="inline-flex items-center gap-1 bg-purple-500/10 px-2 py-0.5 text-[10px] font-medium text-purple-600 dark:text-purple-400">
|
||||
<span className="inline-flex items-center gap-1 bg-purple-500/10 px-2 py-0.5 text-xs font-medium text-purple-600 dark:text-purple-400">
|
||||
<Brain className="h-2.5 w-2.5" /> Reasoning
|
||||
</span>
|
||||
)}
|
||||
{caps.model_family && (
|
||||
<span className="inline-flex items-center gap-1 bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
|
||||
<span className="inline-flex items-center gap-1 bg-muted px-2 py-0.5 text-xs font-medium text-text-secondary">
|
||||
{caps.model_family}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type { GatewayClient } from "@/lib/gatewayClient";
|
|||
import { Check, Search, X } from "lucide-react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { cn, themedBody } from "@/lib/utils";
|
||||
|
||||
/**
|
||||
* Two-stage model picker modal.
|
||||
|
|
@ -212,7 +213,7 @@ export function ModelPickerDialog(props: Props) {
|
|||
aria-modal="true"
|
||||
aria-labelledby="model-picker-title"
|
||||
>
|
||||
<div className="relative w-full max-w-3xl max-h-[80vh] border border-border bg-card shadow-2xl flex flex-col">
|
||||
<div className={cn(themedBody, "relative w-full max-w-3xl max-h-[80vh] border border-border bg-card shadow-2xl flex flex-col")}>
|
||||
<Button
|
||||
ghost
|
||||
size="icon"
|
||||
|
|
@ -226,7 +227,7 @@ export function ModelPickerDialog(props: Props) {
|
|||
<header className="p-5 pb-3 border-b border-border">
|
||||
<h2
|
||||
id="model-picker-title"
|
||||
className="font-display text-base tracking-wider uppercase"
|
||||
className="font-mondwest text-display text-base tracking-wider"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
|
|
@ -295,7 +296,7 @@ export function ModelPickerDialog(props: Props) {
|
|||
/>
|
||||
|
||||
<Label
|
||||
className="font-sans normal-case tracking-normal text-xs text-muted-foreground cursor-pointer"
|
||||
className="font-mondwest normal-case tracking-normal text-xs text-muted-foreground cursor-pointer"
|
||||
htmlFor="model-picker-persist-global"
|
||||
>
|
||||
Persist globally (otherwise this session only)
|
||||
|
|
@ -375,7 +376,7 @@ function ProviderColumn({
|
|||
<span className="font-medium truncate">{p.name}</span>
|
||||
{p.is_current && <CurrentTag />}
|
||||
</div>
|
||||
<div className="text-[0.65rem] text-muted-foreground/80 font-mono truncate">
|
||||
<div className="text-xs text-text-secondary font-mono truncate">
|
||||
{p.slug} · {p.total_models ?? p.models?.length ?? 0} models
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -462,7 +463,7 @@ function ModelColumn({
|
|||
|
||||
function CurrentTag() {
|
||||
return (
|
||||
<span className="text-[0.6rem] uppercase tracking-wider text-primary/80 shrink-0">
|
||||
<span className="text-display text-xs tracking-wider text-primary shrink-0">
|
||||
current
|
||||
</span>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { H2 } from "@/components/NouiTypography";
|
|||
import { api, type OAuthProvider, type OAuthStartResponse } from "@/lib/api";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useI18n } from "@/i18n";
|
||||
import { cn, themedBody } from "@/lib/utils";
|
||||
|
||||
interface Props {
|
||||
provider: OAuthProvider;
|
||||
|
|
@ -169,7 +170,7 @@ export function OAuthLoginModal({ provider, onClose, onSuccess }: Props) {
|
|||
aria-modal="true"
|
||||
aria-labelledby="oauth-modal-title"
|
||||
>
|
||||
<div className="relative w-full max-w-md border border-border bg-card shadow-2xl">
|
||||
<div className={cn(themedBody, "relative w-full max-w-md border border-border bg-card shadow-2xl")}>
|
||||
<Button
|
||||
ghost
|
||||
size="icon"
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import {
|
|||
ShieldOff,
|
||||
ExternalLink,
|
||||
RefreshCw,
|
||||
LogOut,
|
||||
Terminal,
|
||||
LogIn,
|
||||
} from "lucide-react";
|
||||
import { api, type OAuthProvider } from "@/lib/api";
|
||||
import { Button } from "@nous-research/ui/ui/components/button";
|
||||
|
|
@ -105,13 +103,14 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
|
|||
</CardTitle>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
outlined
|
||||
ghost
|
||||
size="icon"
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
onClick={refresh}
|
||||
disabled={loading}
|
||||
prefix={loading ? <Spinner /> : <RefreshCw />}
|
||||
aria-label={t.common.refresh}
|
||||
>
|
||||
{t.common.refresh}
|
||||
{loading ? <Spinner /> : <RefreshCw />}
|
||||
</Button>
|
||||
</div>
|
||||
<CardDescription>
|
||||
|
|
@ -154,46 +153,57 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
|
|||
<span className="font-medium text-sm">{p.name}</span>
|
||||
<Badge
|
||||
tone="outline"
|
||||
className="text-[11px] uppercase tracking-wide"
|
||||
className="text-xs tracking-wide"
|
||||
>
|
||||
{t.oauth.flowLabels[p.flow]}
|
||||
</Badge>
|
||||
{p.status.logged_in && (
|
||||
<Badge tone="success" className="text-[11px]">
|
||||
<Badge tone="success" className="text-xs">
|
||||
{t.oauth.connected}
|
||||
</Badge>
|
||||
)}
|
||||
{expiresLabel === "expired" && (
|
||||
<Badge tone="destructive" className="text-[11px]">
|
||||
<Badge tone="destructive" className="text-xs">
|
||||
{t.oauth.expired}
|
||||
</Badge>
|
||||
)}
|
||||
{expiresLabel && expiresLabel !== "expired" && (
|
||||
<Badge tone="outline" className="text-[11px]">
|
||||
<Badge tone="outline" className="text-xs">
|
||||
{expiresLabel}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{p.status.logged_in && p.status.token_preview && (
|
||||
<code className="text-xs font-mono-ui truncate">
|
||||
<span className="opacity-50">token </span>
|
||||
<span className="truncate text-xs font-mono-ui text-text-secondary">
|
||||
<span className="text-text-tertiary">token </span>
|
||||
{p.status.token_preview}
|
||||
{p.status.source_label && (
|
||||
<span className="opacity-40">
|
||||
<span className="text-text-tertiary">
|
||||
{" "}
|
||||
· {p.status.source_label}
|
||||
</span>
|
||||
)}
|
||||
</code>
|
||||
</span>
|
||||
)}
|
||||
{!p.status.logged_in && (
|
||||
<span className="text-xs text-muted-foreground/80">
|
||||
{t.oauth.notConnected.split("{command}")[0]}
|
||||
<code className="text-foreground bg-secondary/40 px-1">
|
||||
{p.cli_command}
|
||||
</code>
|
||||
{t.oauth.notConnected.split("{command}")[1]}
|
||||
</span>
|
||||
<>
|
||||
<span className="text-xs text-text-secondary">
|
||||
{t.oauth.notConnected.split("{command}")[0].trimEnd()}
|
||||
{t.oauth.notConnected.split("{command}")[1] ?? ""}
|
||||
</span>
|
||||
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<code className="font-courier truncate text-xs opacity-60">
|
||||
{p.cli_command}
|
||||
</code>
|
||||
|
||||
<CopyButton
|
||||
text={p.cli_command}
|
||||
label={t.oauth.cli}
|
||||
copiedLabel={t.oauth.copied}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{p.status.error && (
|
||||
<span className="text-xs text-destructive">
|
||||
|
|
@ -220,32 +230,26 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
|
|||
{!p.status.logged_in && p.flow !== "external" && (
|
||||
<Button
|
||||
size="sm"
|
||||
className="uppercase"
|
||||
onClick={() => setLoginFor(p)}
|
||||
prefix={<LogIn />}
|
||||
>
|
||||
{t.oauth.login}
|
||||
</Button>
|
||||
)}
|
||||
{!p.status.logged_in && (
|
||||
<CopyButton
|
||||
text={p.cli_command}
|
||||
label={t.oauth.cli}
|
||||
copiedLabel={t.oauth.copied}
|
||||
/>
|
||||
)}
|
||||
{p.status.logged_in && p.flow !== "external" && (
|
||||
<Button
|
||||
size="sm"
|
||||
outlined
|
||||
className="uppercase"
|
||||
onClick={() => setDisconnectTarget(p)}
|
||||
disabled={isBusy}
|
||||
prefix={isBusy ? <Spinner /> : <LogOut />}
|
||||
prefix={isBusy ? <Spinner /> : undefined}
|
||||
>
|
||||
{t.oauth.disconnect}
|
||||
</Button>
|
||||
)}
|
||||
{p.status.logged_in && p.flow === "external" && (
|
||||
<span className="text-[11px] text-muted-foreground italic px-2">
|
||||
<span className="text-xs text-text-tertiary italic px-2">
|
||||
<Terminal className="h-3 w-3 inline mr-0.5" />
|
||||
{t.oauth.managedExternally}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -57,18 +57,18 @@ export function PlatformsCard({ platforms }: PlatformsCardProps) {
|
|||
/>
|
||||
|
||||
<div className="flex flex-col gap-0.5 min-w-0">
|
||||
<span className="text-sm font-medium capitalize truncate">
|
||||
<span className="font-mondwest normal-case text-sm font-medium capitalize truncate">
|
||||
{name}
|
||||
</span>
|
||||
|
||||
{info.error_message && (
|
||||
<span className="text-xs text-destructive">
|
||||
<span className="font-mondwest normal-case text-xs text-destructive">
|
||||
{info.error_message}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{info.updated_at && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
<span className="font-mondwest normal-case text-xs text-muted-foreground">
|
||||
{t.status.lastUpdate}: {isoTimeAgo(info.updated_at)}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ export function SidebarFooter() {
|
|||
)}
|
||||
>
|
||||
<Typography
|
||||
mondwest
|
||||
className="font-mono-ui text-[0.7rem] tabular-nums tracking-[0.1em] text-muted-foreground/70 lowercase"
|
||||
className="font-mono-ui text-xs tabular-nums tracking-[0.08em] text-text-tertiary lowercase"
|
||||
>
|
||||
{status?.version != null ? `v${status.version}` : "—"}
|
||||
</Typography>
|
||||
|
|
@ -27,7 +26,7 @@ export function SidebarFooter() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
"font-mondwest text-[0.65rem] tracking-[0.15em] text-midground",
|
||||
"font-mondwest text-display text-xs tracking-[0.12em] text-midground",
|
||||
"transition-opacity hover:opacity-90",
|
||||
"focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground/40",
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -27,21 +27,21 @@ export function SidebarStatusStrip() {
|
|||
className={cn(
|
||||
"block text-left",
|
||||
"px-5 pb-2 pt-0.5",
|
||||
"text-muted-foreground/70",
|
||||
"transition-colors hover:text-muted-foreground/90",
|
||||
"text-text-secondary",
|
||||
"transition-colors hover:text-midground",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground/40",
|
||||
"focus-visible:ring-inset",
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col gap-1 font-mondwest text-[0.55rem] leading-snug tracking-[0.12em]">
|
||||
<div className="flex flex-col gap-1 font-mondwest text-xs leading-snug tracking-[0.08em]">
|
||||
<p className="break-words">
|
||||
<span className="text-muted-foreground/50">{gatewayStatusLabel}</span>{" "}
|
||||
<span className="text-text-tertiary">{gatewayStatusLabel}</span>{" "}
|
||||
<span className={cn("font-medium", gw.tone)}>{gw.label}</span>
|
||||
</p>
|
||||
|
||||
<p className="break-words">
|
||||
<span className="text-muted-foreground/50">{activeSessionsLabel}</span>{" "}
|
||||
<span className="tabular-nums text-muted-foreground/70">
|
||||
<span className="text-text-tertiary">{activeSessionsLabel}</span>{" "}
|
||||
<span className="tabular-nums text-text-secondary">
|
||||
{status.active_sessions}
|
||||
</span>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ export const SlashPopover = forwardRef<SlashPopoverHandle, Props>(
|
|||
</span>
|
||||
|
||||
{it.meta && (
|
||||
<span className="text-[0.7rem] text-muted-foreground/70 truncate ml-auto">
|
||||
<span className="text-xs text-text-tertiary truncate ml-auto">
|
||||
{it.meta}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
|
|||
<Button
|
||||
ghost
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className="px-2 py-1 normal-case tracking-normal font-normal text-xs text-muted-foreground hover:text-foreground"
|
||||
className="px-2 py-1 normal-case tracking-normal font-normal text-xs text-text-secondary hover:text-foreground"
|
||||
title={t.theme?.switchTheme ?? "Switch theme"}
|
||||
aria-label={t.theme?.switchTheme ?? "Switch theme"}
|
||||
aria-expanded={open}
|
||||
|
|
@ -76,7 +76,7 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
|
|||
|
||||
<Typography
|
||||
mondwest
|
||||
className="hidden sm:inline tracking-wide uppercase text-[0.65rem]"
|
||||
className="hidden sm:inline text-display tracking-wide text-xs"
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
|
|
@ -115,7 +115,7 @@ export function ThemeSwitcher({ dropUp = false }: ThemeSwitcherProps) {
|
|||
<div className="border-b border-current/20 px-3 py-2">
|
||||
<Typography
|
||||
mondwest
|
||||
className="text-[0.65rem] tracking-[0.15em] uppercase text-midground/70"
|
||||
className="text-display text-xs tracking-[0.12em] text-text-tertiary"
|
||||
>
|
||||
{sheetTitle}
|
||||
</Typography>
|
||||
|
|
@ -166,12 +166,12 @@ function ThemeSwitcherOptions({
|
|||
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<Typography
|
||||
mondwest
|
||||
className="truncate text-[0.75rem] tracking-wide uppercase"
|
||||
className="truncate text-display text-xs tracking-wide"
|
||||
>
|
||||
{th.label}
|
||||
</Typography>
|
||||
{th.description && (
|
||||
<Typography className="truncate text-[0.65rem] normal-case tracking-normal text-midground/50">
|
||||
<Typography className="truncate text-xs tracking-normal text-text-tertiary">
|
||||
{th.description}
|
||||
</Typography>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ export function ToolCall({ tool }: { tool: ToolEntry }) {
|
|||
|
||||
<span className="font-mono font-medium shrink-0">{tool.name}</span>
|
||||
|
||||
<span className="font-mono text-muted-foreground/80 truncate min-w-0 flex-1">
|
||||
<span className="font-mono text-text-secondary truncate min-w-0 flex-1">
|
||||
{tool.context ?? ""}
|
||||
</span>
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ export function ToolCall({ tool }: { tool: ToolEntry }) {
|
|||
)}
|
||||
|
||||
{elapsed && (
|
||||
<span className="font-mono text-[0.65rem] text-muted-foreground tabular-nums shrink-0">
|
||||
<span className="font-mono text-xs text-text-tertiary tabular-nums shrink-0">
|
||||
{elapsed}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -186,8 +186,8 @@ function Section({
|
|||
return (
|
||||
<div className="flex gap-3">
|
||||
<span
|
||||
className={`uppercase tracking-wider text-[0.6rem] shrink-0 w-14 pt-0.5 ${
|
||||
tone === "error" ? "text-destructive/80" : "text-muted-foreground/60"
|
||||
className={`text-display font-mondwest tracking-wider text-xs shrink-0 w-20 pt-0.5 ${
|
||||
tone === "error" ? "text-destructive" : "text-text-tertiary"
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
|
|
@ -224,5 +224,5 @@ function diffLineClass(line: string): string {
|
|||
if (line.startsWith("-") && !line.startsWith("---"))
|
||||
return "text-destructive";
|
||||
if (line.startsWith("@@")) return "text-primary";
|
||||
return "text-muted-foreground/80";
|
||||
return "text-text-secondary";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cn } from "@/lib/utils";
|
||||
import { cn, themedBody } from "@/lib/utils";
|
||||
|
||||
/**
|
||||
* Themed card primitive. Themes can restyle every card without touching
|
||||
|
|
@ -27,6 +27,7 @@ export function Card({ className, style, ...props }: React.HTMLAttributes<HTMLDi
|
|||
<div
|
||||
className={cn(
|
||||
"border border-border bg-card/80 text-card-foreground w-full",
|
||||
themedBody,
|
||||
className,
|
||||
)}
|
||||
style={{ ...CARD_STYLE, ...style }}
|
||||
|
|
@ -40,11 +41,21 @@ export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDiv
|
|||
}
|
||||
|
||||
export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
|
||||
return <h3 className={cn("font-expanded text-sm font-bold tracking-[0.08em] uppercase blend-lighter", className)} {...props} />;
|
||||
return (
|
||||
<h3
|
||||
className={cn(
|
||||
"font-mondwest text-display text-sm tracking-[0.12em] text-text-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
||||
return <p className={cn("font-mondwest text-xs text-muted-foreground", className)} {...props} />;
|
||||
return (
|
||||
<p className={cn("font-mondwest normal-case text-xs text-muted-foreground", className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useEffect, useRef } from "react";
|
|||
import { createPortal } from "react-dom";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { Button } from "@nous-research/ui/ui/components/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, themedBody } from "@/lib/utils";
|
||||
|
||||
export function ConfirmDialog({
|
||||
cancelLabel = "Cancel",
|
||||
|
|
@ -64,6 +64,7 @@ export function ConfirmDialog({
|
|||
<div
|
||||
ref={dialogRef}
|
||||
className={cn(
|
||||
themedBody,
|
||||
"relative w-full max-w-md mx-4",
|
||||
"border border-border bg-card shadow-lg",
|
||||
"animate-[dialog-in_180ms_ease-out]",
|
||||
|
|
@ -82,7 +83,7 @@ export function ConfirmDialog({
|
|||
<div className="flex-1 min-w-0 flex flex-col gap-1">
|
||||
<h2
|
||||
id="confirm-dialog-title"
|
||||
className="font-expanded text-sm font-bold tracking-[0.08em] uppercase blend-lighter"
|
||||
className="font-mondwest text-display text-sm font-bold tracking-[0.12em] blend-lighter"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
|
|
@ -90,7 +91,7 @@ export function ConfirmDialog({
|
|||
{description && (
|
||||
<p
|
||||
id="confirm-dialog-desc"
|
||||
className="font-mondwest text-xs text-muted-foreground leading-relaxed"
|
||||
className="font-mondwest normal-case text-xs text-muted-foreground leading-relaxed"
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue