mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-05 12:42:30 +00:00
Merge pull request #55468 from NousResearch/bb/desktop-icon-size-token
refactor(desktop): add iconSize token, migrate ad-hoc icon sizes onto it
This commit is contained in:
commit
8f8487b54f
5 changed files with 42 additions and 34 deletions
|
|
@ -4,7 +4,7 @@ import { KbdCombo } from '@/components/ui/kbd'
|
|||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
import { AudioLines, Layers3, Loader2, Square, SteeringWheel, Volume2, VolumeX } from '@/lib/icons'
|
||||
import { AudioLines, iconSize, Layers3, Loader2, Square, SteeringWheel, Volume2, VolumeX } from '@/lib/icons'
|
||||
import { formatCombo } from '@/lib/keybinds/combo'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ export function ComposerControls({
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<SteeringWheel size={14} />
|
||||
<SteeringWheel className={iconSize.sm} />
|
||||
</Button>
|
||||
</Tip>
|
||||
) : (
|
||||
|
|
@ -123,7 +123,7 @@ export function ComposerControls({
|
|||
size="icon"
|
||||
type="button"
|
||||
>
|
||||
<AudioLines size={15} />
|
||||
<AudioLines className={iconSize.sm} />
|
||||
</Button>
|
||||
</Tip>
|
||||
) : (
|
||||
|
|
@ -136,7 +136,7 @@ export function ComposerControls({
|
|||
>
|
||||
{busy ? (
|
||||
busyAction === 'queue' ? (
|
||||
<Layers3 size={14} />
|
||||
<Layers3 className={iconSize.sm} />
|
||||
) : (
|
||||
<span className="block size-2.5 rounded-[0.1875rem] bg-current" />
|
||||
)
|
||||
|
|
@ -207,7 +207,7 @@ function ConversationPill({
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<Square className="fill-current" size={11} />
|
||||
<Square className={cn('fill-current', iconSize.xs)} />
|
||||
<span>{c.stopShort}</span>
|
||||
</Button>
|
||||
)}
|
||||
|
|
@ -242,7 +242,7 @@ function ConversationIndicator({
|
|||
speaking: boolean
|
||||
}) {
|
||||
if (speaking) {
|
||||
return <Loader2 className="animate-spin" size={12} />
|
||||
return <Loader2 className={cn('animate-spin', iconSize.xs)} />
|
||||
}
|
||||
|
||||
const bars = [0.55, 0.85, 1, 0.85, 0.55]
|
||||
|
|
@ -262,15 +262,7 @@ function ConversationIndicator({
|
|||
// Pure-TTS toggle: type normally, but have every assistant reply read aloud —
|
||||
// no dictation, no full conversation loop. Filled/accent when on, mirroring the
|
||||
// muted-mic pressed state above. Driven by (and persisted to) `voice.auto_tts`.
|
||||
function AutoSpeakButton({
|
||||
active,
|
||||
disabled,
|
||||
onToggle
|
||||
}: {
|
||||
active: boolean
|
||||
disabled: boolean
|
||||
onToggle: () => void
|
||||
}) {
|
||||
function AutoSpeakButton({ active, disabled, onToggle }: { active: boolean; disabled: boolean; onToggle: () => void }) {
|
||||
const { t } = useI18n()
|
||||
const c = t.composer
|
||||
const label = active ? c.stopSpeakingReplies : c.speakReplies
|
||||
|
|
@ -294,7 +286,7 @@ function AutoSpeakButton({
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
{active ? <Volume2 size={14} /> : <VolumeX size={14} />}
|
||||
{active ? <Volume2 className={iconSize.sm} /> : <VolumeX className={iconSize.sm} />}
|
||||
</Button>
|
||||
</Tip>
|
||||
)
|
||||
|
|
@ -341,9 +333,9 @@ function DictationButton({
|
|||
variant="ghost"
|
||||
>
|
||||
{status === 'recording' ? (
|
||||
<Square className="fill-current" size={11} />
|
||||
<Square className={cn('fill-current', iconSize.xs)} />
|
||||
) : status === 'transcribing' ? (
|
||||
<Loader2 className="animate-spin" size={14} />
|
||||
<Loader2 className={cn('animate-spin', iconSize.sm)} />
|
||||
) : (
|
||||
<Codicon name="mic" size="0.875rem" />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { StatusSection } from '@/components/chat/status-section'
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Tip } from '@/components/ui/tooltip'
|
||||
import { type Translations, useI18n } from '@/i18n'
|
||||
import { ArrowUp, Pencil, Trash2 } from '@/lib/icons'
|
||||
import { ArrowUp, iconSize, Pencil, Trash2 } from '@/lib/icons'
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { QueuedPromptEntry } from '@/store/composer-queue'
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ export function QueuePanel({ busy, editingId, entries, onDelete, onEdit, onSendN
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<Pencil size={11} />
|
||||
<Pencil className={iconSize.xs} />
|
||||
</Button>
|
||||
</Tip>
|
||||
<Tip label={busy ? c.queueSendNext : c.queueSend}>
|
||||
|
|
@ -65,7 +65,7 @@ export function QueuePanel({ busy, editingId, entries, onDelete, onEdit, onSendN
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<ArrowUp size={11} />
|
||||
<ArrowUp className={iconSize.xs} />
|
||||
</Button>
|
||||
</Tip>
|
||||
<Tip label={c.queueDelete}>
|
||||
|
|
@ -77,7 +77,7 @@ export function QueuePanel({ busy, editingId, entries, onDelete, onEdit, onSendN
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<Trash2 size={11} />
|
||||
<Trash2 className={iconSize.xs} />
|
||||
</Button>
|
||||
</Tip>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useEffect, useRef } from 'react'
|
|||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { Loader2, Mic, Volume2, VolumeX } from '@/lib/icons'
|
||||
import { iconSize, Loader2, Mic, Volume2, VolumeX } from '@/lib/icons'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { stopVoicePlayback } from '@/lib/voice-playback'
|
||||
import { $voicePlayback } from '@/store/voice-playback'
|
||||
|
|
@ -188,7 +188,7 @@ export function VoiceActivity({ state }: { state: VoiceActivityState }) {
|
|||
recording ? 'bg-primary/15 text-primary' : 'bg-primary/10 text-primary'
|
||||
)}
|
||||
>
|
||||
{recording ? <Mic size={12} /> : <Loader2 className="animate-spin" size={12} />}
|
||||
{recording ? <Mic className={iconSize.xs} /> : <Loader2 className={cn('animate-spin', iconSize.xs)} />}
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||
|
|
@ -229,7 +229,7 @@ export function VoicePlaybackActivity() {
|
|||
role="status"
|
||||
>
|
||||
<div className="flex size-5 shrink-0 items-center justify-center rounded-full bg-primary/15 text-primary">
|
||||
{preparing ? <Loader2 className="animate-spin" size={12} /> : <Volume2 size={12} />}
|
||||
{preparing ? <Loader2 className={cn('animate-spin', iconSize.xs)} /> : <Volume2 className={iconSize.xs} />}
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||
|
|
@ -244,7 +244,7 @@ export function VoicePlaybackActivity() {
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<VolumeX size={12} />
|
||||
<VolumeX className={iconSize.xs} />
|
||||
Stop
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import type {
|
|||
DesktopBootstrapState
|
||||
} from '@/global'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { AlertTriangle, Check, ChevronDown, ChevronRight, Loader2 } from '@/lib/icons'
|
||||
import { AlertTriangle, Check, ChevronDown, ChevronRight, iconSize, Loader2 } from '@/lib/icons'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
|
|
@ -109,16 +109,16 @@ function StageRow({ descriptor, result, isCurrent, now }: StageRowProps) {
|
|||
const icon = useMemo(() => {
|
||||
switch (state) {
|
||||
case 'running':
|
||||
return <Loader2 className="h-4 w-4 animate-spin text-primary" />
|
||||
return <Loader2 className={cn(iconSize.md, 'animate-spin text-primary')} />
|
||||
|
||||
case 'succeeded':
|
||||
return <Check className="h-4 w-4 text-emerald-600" />
|
||||
return <Check className={cn(iconSize.md, 'text-emerald-600')} />
|
||||
|
||||
case 'skipped':
|
||||
return <Check className="h-4 w-4 text-muted-foreground" />
|
||||
return <Check className={cn(iconSize.md, 'text-muted-foreground')} />
|
||||
|
||||
case 'failed':
|
||||
return <AlertTriangle className="h-4 w-4 text-destructive" />
|
||||
return <AlertTriangle className={cn(iconSize.md, 'text-destructive')} />
|
||||
|
||||
case 'pending':
|
||||
|
||||
|
|
@ -455,7 +455,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
|
|||
{failed && state.error && (
|
||||
<div className="mb-4 rounded-md border border-destructive/30 bg-destructive/10 p-3 text-sm">
|
||||
<div className="mb-1 flex items-center gap-1.5 font-medium text-destructive">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertTriangle className={iconSize.md} />
|
||||
<span>{copy.error}</span>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap break-words text-foreground/90">{state.error}</p>
|
||||
|
|
@ -484,7 +484,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
|
|||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
{logOpen ? <ChevronDown className="h-3.5 w-3.5" /> : <ChevronRight className="h-3.5 w-3.5" />}
|
||||
{logOpen ? <ChevronDown className={iconSize.sm} /> : <ChevronRight className={iconSize.sm} />}
|
||||
<span>{logOpen ? copy.hideOutput : copy.showOutput}</span>
|
||||
<span className="ml-1 tabular-nums">({copy.lines(state.log.length)})</span>
|
||||
</Button>
|
||||
|
|
@ -527,7 +527,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
|
|||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
{cancelling ? <Loader2 className="h-4 w-4 animate-spin" /> : null}
|
||||
{cancelling ? <Loader2 className={cn(iconSize.md, 'animate-spin')} /> : null}
|
||||
{cancelling ? copy.cancelling : copy.cancelInstall}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -225,3 +225,19 @@ export {
|
|||
}
|
||||
|
||||
export type { Icon as IconComponent } from '@tabler/icons-react'
|
||||
|
||||
/**
|
||||
* Named icon-size scale — the single source of truth for SVG icon dimensions,
|
||||
* replacing ad-hoc `h-N w-N` / `size={N}`. Use `<Icon className={iconSize.sm} />`
|
||||
* (Tailwind `size-*` sets w+h and beats the icon's default 24px); compose with
|
||||
* `cn()` for colour/animation classes.
|
||||
*/
|
||||
export const iconSize = {
|
||||
xs: 'size-3', // 12px
|
||||
sm: 'size-3.5', // 14px
|
||||
md: 'size-4', // 16px
|
||||
lg: 'size-5', // 20px
|
||||
xl: 'size-6' // 24px
|
||||
} as const
|
||||
|
||||
export type IconSize = keyof typeof iconSize
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue