From ac9a1014a69cb55e142f53c9ce51caa370c0d6e1 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Mon, 20 Jul 2026 13:40:16 -0500 Subject: [PATCH] =?UTF-8?q?refactor(desktop):=20drop=20System=20settings?= =?UTF-8?q?=20section;=20keep-awake=20=E2=86=92=20Advanced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert the dedicated System section: Window Translucency + UI Scale move back to Appearance, and Haptics returns to its titlebar-only home. Keep computer awake now lives as a device-local toggle at the top of Advanced (a ConfigSettings section-specific extra, like the Model block), keeping the statusbar quick-toggle. Relocated i18n back to settings.appearance / settings.config across all four locales. --- .../src/app/settings/appearance-settings.tsx | 61 +++++++++++++ .../src/app/settings/config-settings.tsx | 10 ++- apps/desktop/src/app/settings/index.tsx | 12 --- .../src/app/settings/system-settings.tsx | 90 ------------------- apps/desktop/src/app/settings/types.ts | 1 - apps/desktop/src/i18n/en.ts | 25 ++---- apps/desktop/src/i18n/ja.ts | 24 ++--- apps/desktop/src/i18n/types.ts | 19 ++-- apps/desktop/src/i18n/zh-hant.ts | 24 ++--- apps/desktop/src/i18n/zh.ts | 24 ++--- 10 files changed, 112 insertions(+), 178 deletions(-) delete mode 100644 apps/desktop/src/app/settings/system-settings.tsx diff --git a/apps/desktop/src/app/settings/appearance-settings.tsx b/apps/desktop/src/app/settings/appearance-settings.tsx index 7b37f6844dd..32deb34d2d4 100644 --- a/apps/desktop/src/app/settings/appearance-settings.tsx +++ b/apps/desktop/src/app/settings/appearance-settings.tsx @@ -16,6 +16,8 @@ import { $backdrop, setBackdrop } from '@/store/backdrop' import { $embedAllowed, $embedMode, clearEmbedAllowed, type EmbedMode, setEmbedMode } from '@/store/embed-consent' import { $activeGatewayProfile, $profiles, normalizeProfileKey } from '@/store/profile' import { $toolViewMode, setToolViewMode } from '@/store/tool-view' +import { $translucency, setTranslucency } from '@/store/translucency' +import { $zoomPercent, setZoomPercent } from '@/store/zoom' import { getBaseColors, useTheme } from '@/themes/context' import { installVscodeThemeFromMarketplace } from '@/themes/install' import type { DesktopTheme } from '@/themes/types' @@ -62,6 +64,18 @@ function ThemePreview({ name, mode }: { name: string; mode: 'light' | 'dark' }) ) } +// UI scale presets, as zoom percentages. 100 is the browser-default size; +// the ids double as the percent values sent to the main process. A Cmd/Ctrl +// +/- step landing between presets highlights nothing, and the row +// description keeps showing the exact current percent. +const UI_SCALE_PRESETS = ['90', '100', '110', '125', '150', '175'] as const + +type UiScalePreset = (typeof UI_SCALE_PRESETS)[number] + +function matchUiScalePreset(percent: number): UiScalePreset | null { + return UI_SCALE_PRESETS.find(preset => Number(preset) === percent) ?? null +} + function useDebounced(value: T, delayMs: number): T { const [debounced, setDebounced] = useState(value) @@ -231,8 +245,10 @@ export function AppearanceSettings() { const { t, isSavingLocale } = useI18n() const { themeName, mode, resolvedMode, availableThemes, setTheme, setMode } = useTheme() const toolViewMode = useStore($toolViewMode) + const zoomPercent = useStore($zoomPercent) const embedMode = useStore($embedMode) const embedAllowed = useStore($embedAllowed) + const translucency = useStore($translucency) const backdrop = useStore($backdrop) const installs = useStore($marketplaceInstalls) const profiles = useStore($profiles) @@ -277,6 +293,10 @@ export function AppearanceSettings() { { id: 'off', label: a.embedsOff } ] as const satisfies readonly { id: EmbedMode; label: string }[] + const uiScaleOptions = UI_SCALE_PRESETS.map(preset => ({ id: preset, label: `${preset}%` })) + + const matchedScalePreset = matchUiScalePreset(zoomPercent) + return (
@@ -392,6 +412,47 @@ export function AppearanceSettings() { wide /> + { + triggerHaptic('selection') + setZoomPercent(Number(id)) + }} + options={uiScaleOptions} + value={matchedScalePreset ?? ('' as UiScalePreset)} + /> + } + description={a.uiScaleDesc(zoomPercent)} + title={a.uiScaleTitle} + /> + + + { + triggerHaptic('selection') + setTranslucency(Number(event.target.value)) + }} + step={5} + style={{ accentColor: 'var(--dt-primary)' }} + type="range" + value={translucency} + /> + + {translucency}% + +
+ } + description={a.translucencyDesc} + title={a.translucencyTitle} + /> + )} + {/* Device-local desktop pref (not config.yaml) — lives here since keeping + the machine awake is a power-user knob. */} + {activeSectionId === 'advanced' && ( + + )} {visibleFields.length === 0 ? ( ) : ( diff --git a/apps/desktop/src/app/settings/index.tsx b/apps/desktop/src/app/settings/index.tsx index 0637eda06fc..f51cca30f94 100644 --- a/apps/desktop/src/app/settings/index.tsx +++ b/apps/desktop/src/app/settings/index.tsx @@ -10,7 +10,6 @@ import { Archive, BarChart3, Bell, - Cpu, Download, Globe, Info, @@ -43,7 +42,6 @@ import { NotificationsSettings } from './notifications-settings' import { PluginsSettings } from './plugins-settings' import { PROVIDER_VIEWS, ProvidersSettings, type ProviderView } from './providers-settings' import { SessionsSettings } from './sessions-settings' -import { SystemSettings } from './system-settings' import type { SettingsPageProps, SettingsView as SettingsViewId } from './types' const SETTINGS_VIEWS: readonly SettingsViewId[] = [ @@ -56,7 +54,6 @@ const SETTINGS_VIEWS: readonly SettingsViewId[] = [ 'billing', 'plugins', 'sessions', - 'system', 'about' ] @@ -156,13 +153,6 @@ export function SettingsView({ onClose, onConfigSaved, onMainModelChanged }: Set label: t.settings.nav.notifications, onSelect: () => setActiveView('notifications') }, - { - active: activeView === 'system', - icon: Cpu, - id: 'system', - label: t.settings.nav.system, - onSelect: () => setActiveView('system') - }, { active: activeView === 'billing', icon: BarChart3, @@ -326,8 +316,6 @@ export function SettingsView({ onClose, onConfigSaved, onMainModelChanged }: Set ) : activeView === 'notifications' ? ( - ) : activeView === 'system' ? ( - ) : activeView === 'billing' ? ( ) : activeView === 'plugins' ? ( diff --git a/apps/desktop/src/app/settings/system-settings.tsx b/apps/desktop/src/app/settings/system-settings.tsx deleted file mode 100644 index d338f5962a6..00000000000 --- a/apps/desktop/src/app/settings/system-settings.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { useStore } from '@nanostores/react' - -import { SegmentedControl } from '@/components/ui/segmented-control' -import { useI18n } from '@/i18n' -import { triggerHaptic } from '@/lib/haptics' -import { Cpu } from '@/lib/icons' -import { $hapticsMuted, setHapticsMuted } from '@/store/haptics' -import { $keepAwake, setKeepAwake } from '@/store/keep-awake' -import { $translucency, setTranslucency } from '@/store/translucency' -import { $zoomPercent, setZoomPercent } from '@/store/zoom' - -import { ListRow, SectionHeading, SettingsContent, ToggleRow } from './primitives' - -// UI scale presets as zoom percentages (100 = browser default); the ids double -// as the percent sent to main. A Cmd/Ctrl +/- step between presets highlights -// nothing, and the row description keeps showing the exact current percent. -const UI_SCALE_PRESETS = ['90', '100', '110', '125', '150', '175'] as const - -type UiScalePreset = (typeof UI_SCALE_PRESETS)[number] - -export function SystemSettings() { - const { t } = useI18n() - const s = t.settings.system - const keepAwake = useStore($keepAwake) - const translucency = useStore($translucency) - const zoomPercent = useStore($zoomPercent) - const hapticsMuted = useStore($hapticsMuted) - - const uiScaleOptions = UI_SCALE_PRESETS.map(preset => ({ id: preset, label: `${preset}%` })) - const matchedScale = UI_SCALE_PRESETS.find(preset => Number(preset) === zoomPercent) ?? ('' as UiScalePreset) - - return ( - - -

- {s.intro} -

- - - - { - triggerHaptic('selection') - setZoomPercent(Number(id)) - }} - options={uiScaleOptions} - value={matchedScale} - /> - } - description={s.uiScaleDesc(zoomPercent)} - title={s.uiScaleTitle} - /> - - - { - triggerHaptic('selection') - setTranslucency(Number(event.target.value)) - }} - step={5} - style={{ accentColor: 'var(--dt-primary)' }} - type="range" - value={translucency} - /> - - {translucency}% - - - } - description={s.translucencyDesc} - title={s.translucencyTitle} - /> - - setHapticsMuted(!on)} - /> -
- ) -} diff --git a/apps/desktop/src/app/settings/types.ts b/apps/desktop/src/app/settings/types.ts index bc726d221bb..2828609ef63 100644 --- a/apps/desktop/src/app/settings/types.ts +++ b/apps/desktop/src/app/settings/types.ts @@ -14,7 +14,6 @@ export type SettingsView = | 'plugins' | 'providers' | 'sessions' - | 'system' | `config:${string}` export type EnvPatch = Partial> diff --git a/apps/desktop/src/i18n/en.ts b/apps/desktop/src/i18n/en.ts index 94a5a71da82..3202e977934 100644 --- a/apps/desktop/src/i18n/en.ts +++ b/apps/desktop/src/i18n/en.ts @@ -325,8 +325,7 @@ export const en: Translations = { about: 'About', billing: 'Billing', notifications: 'Notifications', - plugins: 'Plugins', - system: 'System' + plugins: 'Plugins' }, plugins: { title: 'Desktop plugins', @@ -380,19 +379,6 @@ export const en: Translations = { completionSoundDesc: 'Plays when an agent turn finishes. Pick a preset and preview it here.', completionSoundPreview: 'Preview' }, - system: { - title: 'System', - intro: 'How Hermes behaves on this machine. These are device-local — each computer keeps its own settings.', - keepAwakeTitle: 'Keep computer awake', - keepAwakeDesc: 'Stop this machine from sleeping so long or overnight runs keep going. The display can still dim.', - translucencyTitle: 'Window Translucency', - translucencyDesc: 'See your desktop through the whole window. macOS and Windows only.', - uiScaleTitle: 'UI Scale', - uiScaleDesc: (percent: number) => - `Scales text and controls across the whole app. Cmd/Ctrl with +, - and 0 also works. Current: ${percent}%.`, - hapticsTitle: 'Haptic Feedback', - hapticsDesc: 'Trackpad taps on actions and toggles. Supported hardware only (macOS).' - }, sections: { model: 'Model', chat: 'Chat', @@ -424,6 +410,11 @@ export const en: Translations = { colorModeDesc: 'Pick a fixed mode or let Hermes follow your system setting.', toolViewTitle: 'Tool Call Display', toolViewDesc: 'Product hides raw tool payloads; Technical shows full input/output.', + uiScaleTitle: 'UI Scale', + uiScaleDesc: (percent: number) => + `Scales text and controls across the whole app. Cmd/Ctrl with +, - and 0 also works. Current: ${percent}%.`, + translucencyTitle: 'Window Translucency', + translucencyDesc: 'See your desktop through the whole window. macOS and Windows only.', backdropTitle: 'Chat Backdrop', backdropDesc: 'The faint statue image behind the conversation.', embedsTitle: 'Inline Embeds', @@ -532,7 +523,9 @@ export const en: Translations = { failedLoad: 'Settings failed to load', autosaveFailed: 'Autosave failed', imported: 'Config imported', - invalidJson: 'Invalid config JSON' + invalidJson: 'Invalid config JSON', + keepAwakeTitle: 'Keep computer awake', + keepAwakeDesc: 'Stop this machine from sleeping so long or overnight runs keep going. The display can still dim.' }, credentials: { pasteKey: 'Paste key', diff --git a/apps/desktop/src/i18n/ja.ts b/apps/desktop/src/i18n/ja.ts index d8f7473fb4f..fabc1bc7b70 100644 --- a/apps/desktop/src/i18n/ja.ts +++ b/apps/desktop/src/i18n/ja.ts @@ -225,8 +225,7 @@ export const ja = defineLocale({ archivedChats: 'アーカイブ済みチャット', about: '情報', billing: '請求', - notifications: '通知', - system: 'システム' + notifications: '通知' }, notifications: { title: '通知', @@ -266,18 +265,6 @@ export const ja = defineLocale({ completionSoundDesc: 'エージェントのターン終了時に再生されます。プリセットを選んでここで試聴できます。', completionSoundPreview: '試聴' }, - system: { - title: 'システム', - intro: 'この端末での Hermes の動作。設定は端末ごとに保存されます。', - keepAwakeTitle: 'コンピューターをスリープさせない', - keepAwakeDesc: '本体のスリープを防ぎ、長時間や夜通しの実行を継続します。画面は暗転できます。', - translucencyTitle: 'ウィンドウの透過', - translucencyDesc: 'ウィンドウ全体を透過させてデスクトップを表示します。macOS と Windows のみ。', - uiScaleTitle: 'UI スケール', - uiScaleDesc: (percent: number) => `アプリ全体の文字と UI を拡大縮小します。Cmd/Ctrl と +、-、0 でも変更できます。現在: ${percent}%`, - hapticsTitle: '触覚フィードバック', - hapticsDesc: '操作やトグル時にトラックパッドの触覚を返します。対応ハードウェアのみ(macOS)。' - }, sections: { model: 'モデル', chat: 'チャット', @@ -309,6 +296,11 @@ export const ja = defineLocale({ colorModeDesc: '固定モードを選ぶか、Hermes をシステム設定に合わせます。', toolViewTitle: 'ツール呼び出しの表示', toolViewDesc: 'プロダクト表示は生のツールペイロードを隠し、テクニカル表示は入出力をすべて表示します。', + uiScaleTitle: 'UI スケール', + uiScaleDesc: (percent: number) => + `アプリ全体の文字と UI を拡大縮小します。Cmd/Ctrl と +、-、0 でも変更できます。現在: ${percent}%`, + translucencyTitle: 'ウィンドウの透過', + translucencyDesc: 'ウィンドウ全体を透過させてデスクトップを表示します。macOS と Windows のみ。', backdropTitle: 'チャット背景', backdropDesc: '会話の背後に表示される淡い彫像の画像。', embedsTitle: 'インライン埋め込み', @@ -629,7 +621,9 @@ export const ja = defineLocale({ failedLoad: '設定の読み込みに失敗しました', autosaveFailed: '自動保存に失敗しました', imported: '設定をインポートしました', - invalidJson: '設定 JSON が無効です' + invalidJson: '設定 JSON が無効です', + keepAwakeTitle: 'コンピューターをスリープさせない', + keepAwakeDesc: '本体のスリープを防ぎ、長時間や夜通しの実行を継続します。画面は暗転できます。' }, credentials: { pasteKey: 'キーを貼り付け', diff --git a/apps/desktop/src/i18n/types.ts b/apps/desktop/src/i18n/types.ts index 646901b4f81..c479706e29d 100644 --- a/apps/desktop/src/i18n/types.ts +++ b/apps/desktop/src/i18n/types.ts @@ -284,7 +284,6 @@ export interface Translations { billing: string notifications: string plugins: string - system: string } plugins: { title: string @@ -318,18 +317,6 @@ export interface Translations { completionSoundDesc: string completionSoundPreview: string } - system: { - title: string - intro: string - keepAwakeTitle: string - keepAwakeDesc: string - translucencyTitle: string - translucencyDesc: string - uiScaleTitle: string - uiScaleDesc: (percent: number) => string - hapticsTitle: string - hapticsDesc: string - } sections: Record searchPlaceholder: Record<'about' | 'config' | 'gateway' | 'keys' | 'mcp' | 'sessions', string> modeOptions: Record<'light' | 'dark' | 'system', ModeOptionCopy> @@ -340,6 +327,10 @@ export interface Translations { colorModeDesc: string toolViewTitle: string toolViewDesc: string + uiScaleTitle: string + uiScaleDesc: (percent: number) => string + translucencyTitle: string + translucencyDesc: string backdropTitle: string backdropDesc: string embedsTitle: string @@ -444,6 +435,8 @@ export interface Translations { autosaveFailed: string imported: string invalidJson: string + keepAwakeTitle: string + keepAwakeDesc: string } credentials: { pasteKey: string diff --git a/apps/desktop/src/i18n/zh-hant.ts b/apps/desktop/src/i18n/zh-hant.ts index 345a7935ac7..986bf379235 100644 --- a/apps/desktop/src/i18n/zh-hant.ts +++ b/apps/desktop/src/i18n/zh-hant.ts @@ -219,8 +219,7 @@ export const zhHant = defineLocale({ archivedChats: '已封存聊天', about: '關於', billing: '帳單', - notifications: '通知', - system: '系統' + notifications: '通知' }, notifications: { title: '通知', @@ -259,18 +258,6 @@ export const zhHant = defineLocale({ completionSoundDesc: '代理回合結束時播放。可在此選擇預設並預覽。', completionSoundPreview: '預覽' }, - system: { - title: '系統', - intro: 'Hermes 在這台電腦上的行為。設定會依裝置保存,每台電腦各自獨立。', - keepAwakeTitle: '保持電腦喚醒', - keepAwakeDesc: '阻止本機睡眠,讓長時間或整夜執行持續進行。螢幕仍可變暗。', - translucencyTitle: '視窗透明', - translucencyDesc: '讓整個視窗透出桌面。僅支援 macOS 與 Windows。', - uiScaleTitle: '介面縮放', - uiScaleDesc: (percent: number) => `縮放整個應用程式的文字與介面。也可使用 Cmd/Ctrl 加 +、- 或 0 調整。目前:${percent}%`, - hapticsTitle: '觸覺回饋', - hapticsDesc: '在操作與開關時觸發觸控板輕觸。僅支援對應硬體(macOS)。' - }, sections: { model: '模型', chat: '聊天', @@ -301,6 +288,11 @@ export const zhHant = defineLocale({ colorModeDesc: '選擇固定模式,或讓 Hermes 跟隨系統設定。', toolViewTitle: '工具呼叫顯示', toolViewDesc: '產品模式會隱藏原始工具 payload;技術模式會顯示完整輸入/輸出。', + uiScaleTitle: '介面縮放', + uiScaleDesc: (percent: number) => + `縮放整個應用程式的文字與介面。也可使用 Cmd/Ctrl 加 +、- 或 0 調整。目前:${percent}%`, + translucencyTitle: '視窗透明', + translucencyDesc: '讓整個視窗透出桌面。僅支援 macOS 與 Windows。', backdropTitle: '聊天背景', backdropDesc: '對話後方那張淡淡的雕像圖片。', embedsTitle: '內嵌預覽', @@ -617,7 +609,9 @@ export const zhHant = defineLocale({ failedLoad: '設定載入失敗', autosaveFailed: '自動儲存失敗', imported: '設定已匯入', - invalidJson: '設定 JSON 無效' + invalidJson: '設定 JSON 無效', + keepAwakeTitle: '保持電腦喚醒', + keepAwakeDesc: '阻止本機睡眠,讓長時間或整夜執行持續進行。螢幕仍可變暗。' }, credentials: { pasteKey: '貼上金鑰', diff --git a/apps/desktop/src/i18n/zh.ts b/apps/desktop/src/i18n/zh.ts index a019bf3992d..deb75047850 100644 --- a/apps/desktop/src/i18n/zh.ts +++ b/apps/desktop/src/i18n/zh.ts @@ -316,8 +316,7 @@ export const zh: Translations = { about: '关于', billing: '账单', notifications: '通知', - plugins: '插件', - system: '系统' + plugins: '插件' }, plugins: { title: '桌面插件', @@ -370,18 +369,6 @@ export const zh: Translations = { completionSoundDesc: '智能体回合结束时播放。可在此选择预设并预览。', completionSoundPreview: '预览' }, - system: { - title: '系统', - intro: 'Hermes 在这台电脑上的行为。设置按设备保存,每台电脑各自独立。', - keepAwakeTitle: '保持电脑唤醒', - keepAwakeDesc: '阻止本机休眠,让长时间或通宵运行继续进行。屏幕仍可变暗。', - translucencyTitle: '窗口透明', - translucencyDesc: '让整个窗口透出桌面。仅支持 macOS 和 Windows。', - uiScaleTitle: '界面缩放', - uiScaleDesc: (percent: number) => `缩放整个应用的文字和界面。也可使用 Cmd/Ctrl 加 +、- 或 0 调整。当前:${percent}%`, - hapticsTitle: '触感反馈', - hapticsDesc: '在操作和开关时触发触控板轻触。仅支持相应硬件(macOS)。' - }, sections: { model: '模型', chat: '对话', @@ -412,6 +399,11 @@ export const zh: Translations = { colorModeDesc: '选择固定模式,或让 Hermes 跟随系统设置。', toolViewTitle: '工具调用显示', toolViewDesc: '产品模式隐藏原始工具数据;技术模式显示完整输入/输出。', + uiScaleTitle: '界面缩放', + uiScaleDesc: (percent: number) => + `缩放整个应用的文字和界面。也可使用 Cmd/Ctrl 加 +、- 或 0 调整。当前:${percent}%`, + translucencyTitle: '窗口透明', + translucencyDesc: '让整个窗口透出桌面。仅支持 macOS 和 Windows。', backdropTitle: '聊天背景', backdropDesc: '对话后方那张淡淡的雕像图片。', embedsTitle: '内嵌预览', @@ -728,7 +720,9 @@ export const zh: Translations = { failedLoad: '设置加载失败', autosaveFailed: '自动保存失败', imported: '配置已导入', - invalidJson: '配置 JSON 无效' + invalidJson: '配置 JSON 无效', + keepAwakeTitle: '保持电脑唤醒', + keepAwakeDesc: '阻止本机休眠,让长时间或通宵运行继续进行。屏幕仍可变暗。' }, credentials: { pasteKey: '粘贴密钥',