feat(desktop): gate traffic-lights behind window-controls style setting

Add a Window controls Style setting ('classic' | 'traffic-lights',
default 'classic') next to the existing Position setting. All four
position×style combinations render correctly with side-driven order
(close,min,max left; min,max,close right).

- Restore deleted left-classic branch (h-8 w-8 rounded-md buttons)
- Add right-traffic-lights branch (cluster with ml-1 container)
- Branch on style instead of side in WindowsWindowControls
- Store field + setter with 'classic' default (no migration needed;
  Zustand persist shallow-merges over initial state)
- Settings UI: SettingsTwoColumn with Position chips + Style dropdown
- Sanitize on client (persistence.ts) and server (settings-helpers.js)
- 10 locale dictionaries updated (add-only, no existing keys changed)
- Traffic-light button spacing 8px→10px edge-to-edge
- Delete dead DESKTOP_WINDOW_CONTROLS_WIDTH_PX constant
This commit is contained in:
Pablo Gonzalez
2026-07-29 19:38:48 +02:00
parent 029f1705db
commit 9001e38c54
19 changed files with 80 additions and 162 deletions
@@ -751,10 +751,6 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
const option = MOBILE_KEYBOARD_MODE_OPTIONS.find((item) => item.id === mobileKeyboardMode);
return option ? tUnsafe(option.labelKey) : undefined;
}, [mobileKeyboardMode, tUnsafe]);
const selectedWindowControlsStyleLabel = React.useMemo(() => {
const option = WINDOW_CONTROLS_STYLE_OPTIONS.find((item) => item.id === desktopWindowControlsStyle);
return option ? tUnsafe(option.labelKey) : undefined;
}, [desktopWindowControlsStyle, tUnsafe]);
const handleMobileLayoutPreferenceChange = React.useCallback((value: MobileLayoutPreference) => {
if (value === mobileLayoutPreference) {
@@ -1028,7 +1024,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
{showWindowControlsPositionSetting && (
<SettingsSection
title={t('settings.openchamber.desktopNetwork.field.windowControls')}
info={t('settings.openchamber.desktopNetwork.field.windowControlsDescription')}
info={t('settings.openchamber.desktopNetwork.field.windowControlsPositionDescription')}
divider={hasThemeSettings}
>
<SettingsTwoColumn>
@@ -1050,16 +1046,15 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
label={t('settings.openchamber.desktopNetwork.field.windowControlsStyle')}
settingsItem="sessions.desktop-window-controls-style"
>
<Select value={desktopWindowControlsStyle} onValueChange={(value: DesktopWindowControlsStyle) => handleWindowControlsStyleChange(value)}>
<SelectTrigger aria-label={t('settings.openchamber.desktopNetwork.field.windowControlsStyleAria')} size={SETTINGS_SELECT_SIZE} className={SETTINGS_SELECT_TRIGGER_CLASS}>
<SelectValue>{selectedWindowControlsStyleLabel}</SelectValue>
</SelectTrigger>
<SelectContent>
{WINDOW_CONTROLS_STYLE_OPTIONS.map((option) => (
<SelectItem key={option.id} value={option.id}>{tUnsafe(option.labelKey)}</SelectItem>
))}
</SelectContent>
</Select>
<SettingsChipGroup
value={desktopWindowControlsStyle}
options={WINDOW_CONTROLS_STYLE_OPTIONS.map((option) => ({
value: option.id,
label: tUnsafe(option.labelKey),
}))}
onChange={handleWindowControlsStyleChange}
aria-label={t('settings.openchamber.desktopNetwork.field.windowControlsStyleAria')}
/>
</SettingsStackedField>
</SettingsTwoColumn>
</SettingsSection>