fix: organize appearance settings sections

Separated theme and localization controls
Added localized section title
This commit is contained in:
Bohdan Triapitsyn
2026-04-26 17:21:52 +03:00
parent 7205d7a34f
commit be723fa58b
6 changed files with 147 additions and 130 deletions
@@ -445,6 +445,8 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
const isVSCode = isVSCodeRuntime(); const isVSCode = isVSCodeRuntime();
const hasAppearanceSettings = (shouldShow('theme') || shouldShow('pwaInstallName') || shouldShow('pwaOrientation') || shouldShow('timeFormat') || shouldShow('weekStart')) && !isVSCode; const hasAppearanceSettings = (shouldShow('theme') || shouldShow('pwaInstallName') || shouldShow('pwaOrientation') || shouldShow('timeFormat') || shouldShow('weekStart')) && !isVSCode;
const hasThemeSettings = shouldShow('theme') && !isVSCode;
const hasLocalizationSettings = (shouldShow('theme') || shouldShow('timeFormat') || shouldShow('weekStart')) && !isVSCode;
const hasLayoutSettings = shouldShow('fontSize') || shouldShow('terminalFontSize') || shouldShow('spacing') || shouldShow('inputBarOffset'); const hasLayoutSettings = shouldShow('fontSize') || shouldShow('terminalFontSize') || shouldShow('spacing') || shouldShow('inputBarOffset');
const hasNavigationSettings = shouldShow('terminalQuickKeys') && !isMobile; const hasNavigationSettings = shouldShow('terminalQuickKeys') && !isMobile;
const hasBehaviorSettings = shouldShow('mermaidRendering') const hasBehaviorSettings = shouldShow('mermaidRendering')
@@ -580,10 +582,9 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
{/* --- Appearance & Themes --- */} {/* --- Appearance & Themes --- */}
{hasAppearanceSettings && ( {hasAppearanceSettings && (
<div className="mb-8 space-y-3"> <div className="mb-8 space-y-6">
<section className="px-2 pb-2 pt-0 space-y-0.5"> {hasThemeSettings && (
<section className="px-2 pb-2 pt-0 space-y-2">
<div className="pb-1.5">
<div className="flex min-w-0 flex-col gap-1.5"> <div className="flex min-w-0 flex-col gap-1.5">
<span className="typography-ui-header font-medium text-foreground">{t('settings.openchamber.visual.section.colorMode')}</span> <span className="typography-ui-header font-medium text-foreground">{t('settings.openchamber.visual.section.colorMode')}</span>
<div className="flex flex-wrap items-center gap-1"> <div className="flex flex-wrap items-center gap-1">
@@ -601,28 +602,8 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
))} ))}
</div> </div>
</div> </div>
</div>
<div className="mt-2 grid grid-cols-1 gap-2 py-1.5 md:grid-cols-[14rem_auto] md:gap-x-8 md:gap-y-2"> <div className="grid grid-cols-1 gap-2 py-1.5 md:grid-cols-[14rem_auto] md:gap-x-8 md:gap-y-2">
<div className="flex min-w-0 flex-col">
<span className="typography-ui-label text-foreground shrink-0">{t('settings.appearance.language.label')}</span>
<span className="typography-meta text-muted-foreground">{t('settings.appearance.language.description')}</span>
</div>
<Select value={locale} onValueChange={(value) => setLocale(value as Locale)}>
<SelectTrigger aria-label={t('settings.appearance.language.select')} className="w-fit">
<SelectValue>{label(locale)}</SelectValue>
</SelectTrigger>
<SelectContent>
{locales.map((availableLocale) => (
<SelectItem key={availableLocale} value={availableLocale}>
{label(availableLocale)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="mt-2 grid grid-cols-1 gap-2 py-1.5 md:grid-cols-[14rem_auto] md:gap-x-8 md:gap-y-2">
<div className="flex min-w-0 items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
<span className="typography-ui-label text-foreground shrink-0">{t('settings.openchamber.visual.field.lightTheme')}</span> <span className="typography-ui-label text-foreground shrink-0">{t('settings.openchamber.visual.field.lightTheme')}</span>
<Select value={selectedLightTheme?.metadata.id ?? ''} onValueChange={setLightThemePreference}> <Select value={selectedLightTheme?.metadata.id ?? ''} onValueChange={setLightThemePreference}>
@@ -663,42 +644,6 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
</div> </div>
</div> </div>
{(shouldShow('timeFormat') || shouldShow('weekStart')) && (
<div className="mt-1 grid grid-cols-1 gap-2 py-1.5 md:grid-cols-[14rem_auto] md:gap-x-8 md:gap-y-2">
{shouldShow('timeFormat') && (
<div className="flex min-w-0 items-center gap-2">
<span className="typography-ui-label text-foreground shrink-0">{t('settings.openchamber.visual.field.timeFormat')}</span>
<Select value={timeFormatPreference} onValueChange={(value: 'auto' | '12h' | '24h') => handleTimeFormatPreferenceChange(value)}>
<SelectTrigger aria-label={t('settings.openchamber.visual.field.selectTimeFormatAria')} className="w-fit">
<SelectValue>{selectedTimeFormatLabel}</SelectValue>
</SelectTrigger>
<SelectContent>
{TIME_FORMAT_OPTIONS.map((option) => (
<SelectItem key={option.id} value={option.id}>{tUnsafe(option.labelKey)}</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{shouldShow('weekStart') && (
<div className="flex min-w-0 items-center gap-2">
<span className="typography-ui-label text-foreground shrink-0">{t('settings.openchamber.visual.field.weekStartsOn')}</span>
<Select value={weekStartPreference} onValueChange={(value: 'auto' | 'monday' | 'sunday') => handleWeekStartPreferenceChange(value)}>
<SelectTrigger aria-label={t('settings.openchamber.visual.field.selectWeekStartAria')} className="w-fit">
<SelectValue>{selectedWeekStartLabel}</SelectValue>
</SelectTrigger>
<SelectContent>
{WEEK_START_OPTIONS.map((option) => (
<SelectItem key={option.id} value={option.id}>{tUnsafe(option.labelKey)}</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
</div>
)}
<div className="flex items-center gap-2 py-1.5"> <div className="flex items-center gap-2 py-1.5">
<button <button
type="button" type="button"
@@ -736,6 +681,72 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</section>
)}
{hasLocalizationSettings && (
<section className="px-2 pb-2 pt-0 space-y-2">
<h4 className="typography-ui-header font-medium text-foreground">{t('settings.openchamber.visual.section.localization')}</h4>
<div className="grid grid-cols-1 gap-2 py-1.5 md:grid-cols-[14rem_auto] md:gap-x-8 md:gap-y-2">
<div className="flex min-w-0 flex-col">
<span className="typography-ui-label text-foreground shrink-0">{t('settings.appearance.language.label')}</span>
<span className="typography-meta text-muted-foreground">{t('settings.appearance.language.description')}</span>
</div>
<Select value={locale} onValueChange={(value) => setLocale(value as Locale)}>
<SelectTrigger aria-label={t('settings.appearance.language.select')} className="w-fit">
<SelectValue>{label(locale)}</SelectValue>
</SelectTrigger>
<SelectContent>
{locales.map((availableLocale) => (
<SelectItem key={availableLocale} value={availableLocale}>
{label(availableLocale)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{(shouldShow('timeFormat') || shouldShow('weekStart')) && (
<div className="grid grid-cols-1 gap-2 py-1.5 md:grid-cols-[14rem_auto] md:gap-x-8 md:gap-y-2">
{shouldShow('timeFormat') && (
<div className="flex min-w-0 items-center gap-2">
<span className="typography-ui-label text-foreground shrink-0">{t('settings.openchamber.visual.field.timeFormat')}</span>
<Select value={timeFormatPreference} onValueChange={(value: 'auto' | '12h' | '24h') => handleTimeFormatPreferenceChange(value)}>
<SelectTrigger aria-label={t('settings.openchamber.visual.field.selectTimeFormatAria')} className="w-fit">
<SelectValue>{selectedTimeFormatLabel}</SelectValue>
</SelectTrigger>
<SelectContent>
{TIME_FORMAT_OPTIONS.map((option) => (
<SelectItem key={option.id} value={option.id}>{tUnsafe(option.labelKey)}</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{shouldShow('weekStart') && (
<div className="flex min-w-0 items-center gap-2">
<span className="typography-ui-label text-foreground shrink-0">{t('settings.openchamber.visual.field.weekStartsOn')}</span>
<Select value={weekStartPreference} onValueChange={(value: 'auto' | 'monday' | 'sunday') => handleWeekStartPreferenceChange(value)}>
<SelectTrigger aria-label={t('settings.openchamber.visual.field.selectWeekStartAria')} className="w-fit">
<SelectValue>{selectedWeekStartLabel}</SelectValue>
</SelectTrigger>
<SelectContent>
{WEEK_START_OPTIONS.map((option) => (
<SelectItem key={option.id} value={option.id}>{tUnsafe(option.labelKey)}</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
</div>
)}
</section>
)}
{(showPwaInstallNameSetting || showPwaOrientationSetting) && (
<section className="px-2 pb-2 pt-0 space-y-2">
{showPwaInstallNameSetting && ( {showPwaInstallNameSetting && (
<div className="py-1.5 space-y-1.5"> <div className="py-1.5 space-y-1.5">
@@ -825,6 +836,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
</div> </div>
)} )}
</section> </section>
)}
</div> </div>
)} )}
@@ -1322,6 +1322,7 @@ export const settingsDict = {
'settings.voice.page.preview.voiceLine': 'Hello! I\'m {voiceName}. This is how I sound.', 'settings.voice.page.preview.voiceLine': 'Hello! I\'m {voiceName}. This is how I sound.',
'settings.voice.page.preview.customServerLine': 'Hello! This is a preview of the custom TTS server.', 'settings.voice.page.preview.customServerLine': 'Hello! This is a preview of the custom TTS server.',
'settings.openchamber.visual.section.colorMode': 'Color Mode', 'settings.openchamber.visual.section.colorMode': 'Color Mode',
'settings.openchamber.visual.section.localization': 'Localization',
'settings.openchamber.visual.section.spacingAndLayout': 'Spacing & Layout', 'settings.openchamber.visual.section.spacingAndLayout': 'Spacing & Layout',
'settings.openchamber.visual.section.navigation': 'Navigation', 'settings.openchamber.visual.section.navigation': 'Navigation',
'settings.openchamber.visual.section.chatRenderMode': 'Chat Render Mode', 'settings.openchamber.visual.section.chatRenderMode': 'Chat Render Mode',
@@ -1322,6 +1322,7 @@ export const settingsDict = {
"settings.voice.page.preview.voiceLine": "¡Hola! Soy {voiceName}. Así suena mi voz.", "settings.voice.page.preview.voiceLine": "¡Hola! Soy {voiceName}. Así suena mi voz.",
"settings.voice.page.preview.customServerLine": "¡Hola! Esta es una previsualización del servidor TTS personalizado.", "settings.voice.page.preview.customServerLine": "¡Hola! Esta es una previsualización del servidor TTS personalizado.",
"settings.openchamber.visual.section.colorMode": "Modo de color", "settings.openchamber.visual.section.colorMode": "Modo de color",
"settings.openchamber.visual.section.localization": "Localización",
"settings.openchamber.visual.section.spacingAndLayout": "Espaciado y diseño", "settings.openchamber.visual.section.spacingAndLayout": "Espaciado y diseño",
"settings.openchamber.visual.section.navigation": "Navegación", "settings.openchamber.visual.section.navigation": "Navegación",
"settings.openchamber.visual.section.chatRenderMode": "Modo de renderizado de chat", "settings.openchamber.visual.section.chatRenderMode": "Modo de renderizado de chat",
@@ -1322,6 +1322,7 @@ export const settingsDict = {
"settings.voice.page.preview.voiceLine": "Olá! Sou {voiceName}. É assim que minha voz soa.", "settings.voice.page.preview.voiceLine": "Olá! Sou {voiceName}. É assim que minha voz soa.",
"settings.voice.page.preview.customServerLine": "Olá! Esta é uma prévia do servidor TTS personalizado.", "settings.voice.page.preview.customServerLine": "Olá! Esta é uma prévia do servidor TTS personalizado.",
"settings.openchamber.visual.section.colorMode": "Modo de cor", "settings.openchamber.visual.section.colorMode": "Modo de cor",
"settings.openchamber.visual.section.localization": "Localização",
"settings.openchamber.visual.section.spacingAndLayout": "Espaçamento e layout", "settings.openchamber.visual.section.spacingAndLayout": "Espaçamento e layout",
"settings.openchamber.visual.section.navigation": "Navegação", "settings.openchamber.visual.section.navigation": "Navegação",
"settings.openchamber.visual.section.chatRenderMode": "Modo de renderizado de chat", "settings.openchamber.visual.section.chatRenderMode": "Modo de renderizado de chat",
@@ -1322,6 +1322,7 @@ export const settingsDict = {
"settings.voice.page.preview.voiceLine": "Привіт! Я {voiceName}. Ось як я звучу.", "settings.voice.page.preview.voiceLine": "Привіт! Я {voiceName}. Ось як я звучу.",
"settings.voice.page.preview.customServerLine": "Привіт! Це попередній перегляд спеціального сервера TTS.", "settings.voice.page.preview.customServerLine": "Привіт! Це попередній перегляд спеціального сервера TTS.",
"settings.openchamber.visual.section.colorMode": "Режим теми", "settings.openchamber.visual.section.colorMode": "Режим теми",
"settings.openchamber.visual.section.localization": "Локалізація",
"settings.openchamber.visual.section.spacingAndLayout": "Відступи й компонування", "settings.openchamber.visual.section.spacingAndLayout": "Відступи й компонування",
"settings.openchamber.visual.section.navigation": "Навігація", "settings.openchamber.visual.section.navigation": "Навігація",
"settings.openchamber.visual.section.chatRenderMode": "Режим візуалізації чату", "settings.openchamber.visual.section.chatRenderMode": "Режим візуалізації чату",
@@ -1322,6 +1322,7 @@ export const settingsDict = {
'settings.voice.page.preview.voiceLine': '你好!我是 {voiceName}。这是我的声音效果。', 'settings.voice.page.preview.voiceLine': '你好!我是 {voiceName}。这是我的声音效果。',
'settings.voice.page.preview.customServerLine': '你好!这是自定义 TTS 服务器的预览。', 'settings.voice.page.preview.customServerLine': '你好!这是自定义 TTS 服务器的预览。',
'settings.openchamber.visual.section.colorMode': '颜色模式', 'settings.openchamber.visual.section.colorMode': '颜色模式',
'settings.openchamber.visual.section.localization': '本地化',
'settings.openchamber.visual.section.spacingAndLayout': '间距与布局', 'settings.openchamber.visual.section.spacingAndLayout': '间距与布局',
'settings.openchamber.visual.section.navigation': '导航', 'settings.openchamber.visual.section.navigation': '导航',
'settings.openchamber.visual.section.chatRenderMode': '聊天渲染模式', 'settings.openchamber.visual.section.chatRenderMode': '聊天渲染模式',