diff --git a/apps/desktop/src/app/chat/composer/controls.tsx b/apps/desktop/src/app/chat/composer/controls.tsx
index 364ee6d3b83..8bc7abc4acc 100644
--- a/apps/desktop/src/app/chat/composer/controls.tsx
+++ b/apps/desktop/src/app/chat/composer/controls.tsx
@@ -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"
>
-
+
) : (
@@ -123,7 +123,7 @@ export function ComposerControls({
size="icon"
type="button"
>
-
+
) : (
@@ -136,7 +136,7 @@ export function ComposerControls({
>
{busy ? (
busyAction === 'queue' ? (
-
+
) : (
)
@@ -207,7 +207,7 @@ function ConversationPill({
type="button"
variant="ghost"
>
-
+
{c.stopShort}
)}
@@ -242,7 +242,7 @@ function ConversationIndicator({
speaking: boolean
}) {
if (speaking) {
- return
+ return
}
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 ? : }
+ {active ? : }
)
@@ -341,9 +333,9 @@ function DictationButton({
variant="ghost"
>
{status === 'recording' ? (
-
+
) : status === 'transcribing' ? (
-
+
) : (
)}
diff --git a/apps/desktop/src/app/chat/composer/queue-panel.tsx b/apps/desktop/src/app/chat/composer/queue-panel.tsx
index fb4365506c0..8c9bc084f59 100644
--- a/apps/desktop/src/app/chat/composer/queue-panel.tsx
+++ b/apps/desktop/src/app/chat/composer/queue-panel.tsx
@@ -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"
>
-
+
@@ -65,7 +65,7 @@ export function QueuePanel({ busy, editingId, entries, onDelete, onEdit, onSendN
type="button"
variant="ghost"
>
-
+
@@ -77,7 +77,7 @@ export function QueuePanel({ busy, editingId, entries, onDelete, onEdit, onSendN
type="button"
variant="ghost"
>
-
+
>
diff --git a/apps/desktop/src/app/chat/composer/voice-activity.tsx b/apps/desktop/src/app/chat/composer/voice-activity.tsx
index 535d1422e45..bd1f3303621 100644
--- a/apps/desktop/src/app/chat/composer/voice-activity.tsx
+++ b/apps/desktop/src/app/chat/composer/voice-activity.tsx
@@ -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 ? : }
+ {recording ? : }
@@ -229,7 +229,7 @@ export function VoicePlaybackActivity() {
role="status"
>
- {preparing ? : }
+ {preparing ? : }
@@ -244,7 +244,7 @@ export function VoicePlaybackActivity() {
type="button"
variant="ghost"
>
-
+
Stop
diff --git a/apps/desktop/src/components/desktop-install-overlay.tsx b/apps/desktop/src/components/desktop-install-overlay.tsx
index 0aa75668b2d..e11ae6de0e3 100644
--- a/apps/desktop/src/components/desktop-install-overlay.tsx
+++ b/apps/desktop/src/components/desktop-install-overlay.tsx
@@ -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
+ return
case 'succeeded':
- return
+ return
case 'skipped':
- return
+ return
case 'failed':
- return
+ return
case 'pending':
@@ -455,7 +455,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
{failed && state.error && (
{state.error}
@@ -484,7 +484,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
type="button"
variant="ghost"
>
- {logOpen ?
:
}
+ {logOpen ?
:
}
{logOpen ? copy.hideOutput : copy.showOutput}
({copy.lines(state.log.length)})
@@ -527,7 +527,7 @@ export function DesktopInstallOverlay({ enabled = true }: DesktopInstallOverlayP
size="sm"
variant="ghost"
>
- {cancelling ?
: null}
+ {cancelling ?
: null}
{cancelling ? copy.cancelling : copy.cancelInstall}
diff --git a/apps/desktop/src/lib/icons.ts b/apps/desktop/src/lib/icons.ts
index f7a825fda15..38e6da317ff 100644
--- a/apps/desktop/src/lib/icons.ts
+++ b/apps/desktop/src/lib/icons.ts
@@ -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 `
`
+ * (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