feat: glass ui pass

This commit is contained in:
Brooklyn Nicholson
2026-05-16 19:21:33 -05:00
parent 062eed654d
commit d67a438fec
104 changed files with 5173 additions and 1919 deletions
@@ -58,16 +58,16 @@ export function AppearanceSettings() {
return (
<SettingsContent>
<div className="space-y-7">
<div className="space-y-5">
<div>
<SectionHeading icon={Palette} title="Appearance" />
<p className="max-w-2xl text-sm leading-6 text-muted-foreground">
<p className="max-w-2xl text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">
These are desktop-only display preferences. Mode controls brightness; theme controls the accent palette and
chat surface styling.
</p>
</div>
<section className="rounded-2xl border border-border/50 bg-card/55 p-4 shadow-sm">
<section className="rounded-xl border border-(--ui-stroke-tertiary) bg-(--glass-chat-bubble-background) p-3 shadow-sm">
<div className="mb-3 flex items-center justify-between gap-3">
<div>
<div className="text-sm font-medium">Color Mode</div>
@@ -84,8 +84,8 @@ export function AppearanceSettings() {
return (
<button
className={cn(
'group rounded-xl border border-border/45 bg-background/55 p-3 text-left transition hover:border-primary/35 hover:bg-accent/45',
active && 'border-primary/65 bg-primary/8 ring-2 ring-primary/25'
'group rounded-lg border border-(--ui-stroke-tertiary) bg-(--ui-bg-quinary) p-2.5 text-left transition hover:bg-(--chrome-action-hover)',
active && 'border-(--ui-stroke-secondary) bg-(--ui-bg-tertiary)'
)}
key={id}
onClick={() => {
@@ -104,15 +104,15 @@ export function AppearanceSettings() {
</span>
)}
</div>
<div className="mt-3 text-sm font-medium">{label}</div>
<div className="mt-1 text-xs leading-5 text-muted-foreground">{description}</div>
<div className="mt-2 text-[length:var(--conversation-text-font-size)] font-medium">{label}</div>
<div className="mt-1 text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">{description}</div>
</button>
)
})}
</div>
</section>
<section className="rounded-2xl border border-border/50 bg-card/55 p-4 shadow-sm">
<section className="rounded-xl border border-(--ui-stroke-tertiary) bg-(--glass-chat-bubble-background) p-3 shadow-sm">
<div className="mb-3 flex items-center justify-between gap-3">
<div>
<div className="text-sm font-medium">Tool Call Display</div>
@@ -142,8 +142,8 @@ export function AppearanceSettings() {
return (
<button
className={cn(
'group rounded-xl border border-border/45 bg-background/55 p-3 text-left transition hover:border-primary/35 hover:bg-accent/45',
active && 'border-primary/65 bg-primary/8 ring-2 ring-primary/25'
'group rounded-lg border border-(--ui-stroke-tertiary) bg-(--ui-bg-quinary) p-2.5 text-left transition hover:bg-(--chrome-action-hover)',
active && 'border-(--ui-stroke-secondary) bg-(--ui-bg-tertiary)'
)}
key={option.id}
onClick={() => {
@@ -153,21 +153,21 @@ export function AppearanceSettings() {
type="button"
>
<div className="flex items-start justify-between gap-3">
<div className="text-sm font-medium">{option.label}</div>
<div className="text-[length:var(--conversation-text-font-size)] font-medium">{option.label}</div>
{active && (
<span className="grid size-5 place-items-center rounded-full bg-primary text-primary-foreground">
<Check className="size-3.5" />
</span>
)}
</div>
<div className="mt-1 text-xs leading-5 text-muted-foreground">{option.description}</div>
<div className="mt-1 text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">{option.description}</div>
</button>
)
})}
</div>
</section>
<section className="rounded-2xl border border-border/50 bg-card/55 p-4 shadow-sm">
<section className="rounded-xl border border-(--ui-stroke-tertiary) bg-(--glass-chat-bubble-background) p-3 shadow-sm">
<div className="mb-3 flex items-center justify-between gap-3">
<div>
<div className="text-sm font-medium">Theme</div>
@@ -184,8 +184,8 @@ export function AppearanceSettings() {
return (
<button
className={cn(
'rounded-2xl border border-border/45 bg-background/50 p-2.5 text-left transition hover:border-primary/35 hover:bg-accent/35',
active && 'border-primary/65 bg-primary/8 ring-2 ring-primary/25'
'rounded-lg border border-(--ui-stroke-tertiary) bg-(--ui-bg-quinary) p-2 text-left transition hover:bg-(--chrome-action-hover)',
active && 'border-(--ui-stroke-secondary) bg-(--ui-bg-tertiary)'
)}
key={theme.name}
onClick={() => {
@@ -197,8 +197,8 @@ export function AppearanceSettings() {
<ThemePreview name={theme.name} />
<div className="mt-3 flex items-start justify-between gap-3 px-1">
<div className="min-w-0">
<div className="truncate text-sm font-medium">{theme.label}</div>
<div className="mt-0.5 line-clamp-2 text-xs leading-5 text-muted-foreground">
<div className="truncate text-[length:var(--conversation-text-font-size)] font-medium">{theme.label}</div>
<div className="mt-0.5 line-clamp-2 text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">
{theme.description}
</div>
</div>
+2 -2
View File
@@ -1,7 +1,7 @@
import {
Brain,
Lock,
type LucideIcon,
type IconComponent,
MessageCircle,
Mic,
Monitor,
@@ -302,7 +302,7 @@ export interface ModeOption {
id: ThemeMode
label: string
description: string
icon: LucideIcon
icon: IconComponent
}
export const MODE_OPTIONS: ModeOption[] = [
@@ -45,22 +45,22 @@ function ModeCard({
return (
<button
className={cn(
'rounded-2xl border p-4 text-left transition',
'rounded-xl border p-3 text-left transition',
active
? 'border-primary bg-primary/10 ring-2 ring-primary/15'
: 'border-border bg-background/60 hover:bg-muted/40',
? 'border-(--ui-stroke-secondary) bg-(--ui-bg-tertiary)'
: 'border-(--ui-stroke-tertiary) bg-(--ui-bg-quinary) hover:bg-(--chrome-action-hover)',
disabled && 'cursor-not-allowed opacity-50'
)}
disabled={disabled}
onClick={onSelect}
type="button"
>
<div className="flex items-center gap-2 text-sm font-medium">
<div className="flex items-center gap-2 text-[length:var(--conversation-text-font-size)] font-medium">
<Icon className="size-4 text-muted-foreground" />
<span>{title}</span>
{active ? <Check className="ml-auto size-4 text-primary" /> : null}
</div>
<p className="mt-2 text-xs leading-5 text-muted-foreground">{description}</p>
<p className="mt-1.5 text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">{description}</p>
</button>
)
}
@@ -191,20 +191,20 @@ export function GatewaySettings() {
return (
<SettingsContent>
<div className="mb-6">
<div className="flex items-center gap-2 text-sm font-medium">
<div className="mb-5">
<div className="flex items-center gap-2 text-[length:var(--conversation-text-font-size)] font-medium">
<Globe className="size-4 text-muted-foreground" />
Gateway Connection
{state.envOverride ? <Pill tone="primary">env override</Pill> : null}
</div>
<p className="mt-2 max-w-2xl text-xs leading-5 text-muted-foreground">
<p className="mt-2 max-w-2xl text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">
Hermes Desktop starts its own local gateway by default. Use a remote gateway when you want this app to control
an already-running Hermes backend on another machine or behind a trusted proxy.
</p>
</div>
{state.envOverride ? (
<div className="mb-5 flex items-start gap-2 rounded-2xl border border-destructive/30 bg-destructive/10 px-4 py-3 text-xs text-destructive">
<div className="mb-5 flex items-start gap-2 rounded-xl border border-destructive/30 bg-destructive/10 px-3 py-2.5 text-[length:var(--conversation-caption-font-size)] text-destructive">
<AlertCircle className="mt-0.5 size-4 shrink-0" />
<div>
<div className="font-medium">Environment variables are controlling this desktop session.</div>
@@ -3,7 +3,8 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { deleteEnvVar, getEnvVars, revealEnvVar, setEnvVar } from '@/hermes'
import { Check, Eye, EyeOff, Save, Settings2, Trash2, X, Zap } from '@/lib/icons'
import { Codicon } from '@/components/ui/codicon'
import { Check, Eye, EyeOff, Save, Settings2, Trash2, Zap } from '@/lib/icons'
import { cn } from '@/lib/utils'
import { notify, notifyError } from '@/store/notifications'
import type { EnvVarInfo } from '@/types/hermes'
@@ -169,7 +170,7 @@ function EnvVarRow({
{saving === varKey ? 'Saving' : 'Save'}
</Button>
<Button onClick={() => setEdits(c => withoutKey(c, varKey))} size="sm" variant="outline">
<X />
<Codicon name="close" />
Cancel
</Button>
</div>
+12 -12
View File
@@ -2,14 +2,14 @@ import type { ReactNode } from 'react'
import { PageLoader } from '@/components/page-loader'
import { Button } from '@/components/ui/button'
import type { LucideIcon } from '@/lib/icons'
import type { IconComponent } from '@/lib/icons'
import { cn } from '@/lib/utils'
export function SettingsContent({ children }: { children: ReactNode }) {
return (
<section className="min-h-0 overflow-hidden">
<div className="h-full min-h-0 overflow-y-auto px-8 py-6 pb-24">
<div className="mx-auto w-full max-w-5xl">{children}</div>
<div className="h-full min-h-0 overflow-y-auto px-5 py-4 pb-20">
<div className="mx-auto w-full max-w-4xl">{children}</div>
</div>
</section>
)
@@ -19,7 +19,7 @@ export function Pill({ tone = 'muted', children }: { tone?: 'muted' | 'primary';
return (
<span
className={cn(
'inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[0.66rem]',
'inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[0.6875rem]',
tone === 'primary' ? 'bg-primary/10 text-primary' : 'bg-muted text-muted-foreground'
)}
>
@@ -28,9 +28,9 @@ export function Pill({ tone = 'muted', children }: { tone?: 'muted' | 'primary';
)
}
export function SectionHeading({ icon: Icon, title, meta }: { icon: LucideIcon; title: string; meta?: string }) {
export function SectionHeading({ icon: Icon, title, meta }: { icon: IconComponent; title: string; meta?: string }) {
return (
<div className="mb-3 flex items-center gap-2 pt-3.5 text-sm font-medium">
<div className="mb-2.5 flex items-center gap-2 pt-2 text-[length:var(--conversation-text-font-size)] font-medium">
<Icon className="size-4 text-muted-foreground" />
<span>{title}</span>
{meta && <Pill>{meta}</Pill>}
@@ -44,7 +44,7 @@ export function NavLink({
active,
onClick
}: {
icon: LucideIcon
icon: IconComponent
label: string
active: boolean
onClick: () => void
@@ -52,8 +52,8 @@ export function NavLink({
return (
<Button
className={cn(
'flex min-h-8 w-full justify-start gap-2 rounded-lg px-2.5 text-left text-sm transition',
active ? 'bg-muted text-foreground' : 'text-foreground/80 hover:bg-muted/70'
'flex min-h-7 w-full justify-start gap-2 rounded-md px-2 text-left text-[length:var(--conversation-text-font-size)] transition',
active ? 'bg-(--ui-bg-tertiary) text-foreground' : 'text-(--ui-text-secondary) hover:bg-(--chrome-action-hover) hover:text-foreground'
)}
onClick={onClick}
size="sm"
@@ -84,13 +84,13 @@ export function ListRow({
return (
<div
className={cn(
'grid gap-4 py-3.5 sm:grid-cols-[minmax(0,1fr)_minmax(15rem,22rem)] sm:items-center',
'grid gap-3 py-3 sm:grid-cols-[minmax(0,1fr)_minmax(15rem,22rem)] sm:items-center',
wide && 'sm:grid-cols-1 sm:items-start'
)}
>
<div className="min-w-0">
<div className="text-sm font-medium text-foreground">{title}</div>
{description && <div className="mt-1 text-xs leading-5 text-muted-foreground">{description}</div>}
<div className="text-[length:var(--conversation-text-font-size)] font-medium text-foreground">{title}</div>
{description && <div className="mt-1 text-[length:var(--conversation-caption-font-size)] leading-(--conversation-caption-line-height) text-(--ui-text-tertiary)">{description}</div>}
{hint && <div className="mt-1 block font-mono text-[0.68rem] text-muted-foreground/45">{hint}</div>}
{below}
</div>
+2 -2
View File
@@ -1,7 +1,7 @@
import type { Dispatch, SetStateAction } from 'react'
import type { HermesGateway } from '@/hermes'
import type { LucideIcon } from '@/lib/icons'
import type { IconComponent } from '@/lib/icons'
import type { EnvVarInfo } from '@/types/hermes'
export type SettingsView = 'about' | 'gateway' | 'keys' | 'mcp' | 'tools' | `config:${string}`
@@ -28,7 +28,7 @@ export interface ProviderGroup {
export interface DesktopConfigSection {
id: string
label: string
icon: LucideIcon
icon: IconComponent
keys: string[]
}