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

This commit is contained in:
Pablo Gonzalez
2026-07-29 19:38:48 +02:00
parent 76b2ab2588
commit 029f1705db
21 changed files with 370 additions and 45 deletions
+33 -3
View File
@@ -41,6 +41,7 @@ export type SkillCatalogConfig = {
export type DesktopWindowControlsPosition = 'left' | 'right';
export type DesktopWindowControlsSide = 'left' | 'right';
export type DesktopWindowControlAction = 'close' | 'minimize' | 'maximize';
export type DesktopWindowControlsStyle = 'classic' | 'traffic-lights';
export type DesktopSettings = {
themeId?: string;
@@ -142,6 +143,7 @@ export type DesktopSettings = {
pwaOrientation?: 'system' | 'portrait' | 'landscape';
mobileKeyboardMode?: MobileKeyboardMode;
desktopWindowControlsPosition?: DesktopWindowControlsPosition;
desktopWindowControlsStyle?: DesktopWindowControlsStyle;
inputSpellcheckEnabled?: boolean;
showOpenCodeUpdateNotifications?: boolean;
agentControlToolEnabled?: boolean;
@@ -252,9 +254,6 @@ export const getElectronPlatform = (): string | null => {
return typeof platform === 'string' ? platform : null;
};
/** Width of the three in-app window control buttons when placed on the left (3 × w-8). */
export const DESKTOP_WINDOW_CONTROLS_WIDTH_PX = 96;
/** Default side for in-app window controls (Windows-style, right). */
export const DEFAULT_DESKTOP_WINDOW_CONTROLS_POSITION: DesktopWindowControlsPosition = 'right';
@@ -301,6 +300,37 @@ export const hasDesktopInvoke = (): boolean => {
return typeof getDesktopBridge()?.invoke === 'function';
};
/**
* Linux frameless Electron windows show a 1px CSS border on #root (see
* index.css). This function flips the attribute on <html> so the rule
* matches, and tracks `data-oc-window-maximized` so the border drops when
* maximized (a 1px line inset from every screen edge would look off).
* No-op outside Linux frameless Electron.
*/
export const initLinuxWindowBorder = (): void => {
if (typeof window === 'undefined' || typeof document === 'undefined') return;
if (!usesFramelessElectronChrome()) return;
const platform = getElectronPlatform();
if (platform !== 'linux') return;
const root = document.documentElement;
root.setAttribute('data-oc-window-border', '');
const applyMaximized = (maximized: boolean) => {
root.toggleAttribute('data-oc-window-maximized', maximized);
};
void invokeDesktop<{ maximized?: boolean }>('desktop_get_current_window_state')
.then((state) => applyMaximized(Boolean(state?.maximized)))
.catch(() => {});
const onMaximizedChange = (event: Event) => {
const detail = (event as CustomEvent<{ maximized?: boolean }>).detail;
applyMaximized(Boolean(detail?.maximized));
};
window.addEventListener('openchamber:window-maximized-changed', onMaximizedChange);
};
export const canUseElectronDesktopIPC = (): boolean => isElectronShell() && hasDesktopInvoke();
export const invokeDesktop = async <T = unknown>(command: string, args?: Record<string, unknown>): Promise<T | null> => {
@@ -6,6 +6,7 @@ import {
normalizeDesktopWindowControlsPosition,
resolveDesktopWindowControlsSide,
} from './desktop';
import { useUIStore } from '@/stores/useUIStore';
describe('desktop window controls position', () => {
test('defaults to right', () => {
@@ -30,3 +31,24 @@ describe('desktop window controls position', () => {
expect(getDesktopWindowControlsOrder('right')).toEqual(['minimize', 'maximize', 'close']);
});
});
describe('useUIStore v12→v13 migration', () => {
test('introduces classic style while preserving position', () => {
const migrate = useUIStore.persist.getOptions().migrate!;
const result = migrate({ desktopWindowControlsPosition: 'left' }, 12) as Record<string, unknown>;
expect(result.desktopWindowControlsPosition).toBe('left');
expect(result.desktopWindowControlsStyle).toBe('classic');
});
test('does not overwrite a persisted style', () => {
const migrate = useUIStore.persist.getOptions().migrate!;
const result = migrate({ desktopWindowControlsStyle: 'traffic-lights' }, 12) as Record<string, unknown>;
expect(result.desktopWindowControlsStyle).toBe('traffic-lights');
});
test('coerces an invalid persisted style to classic', () => {
const migrate = useUIStore.persist.getOptions().migrate!;
const result = migrate({ desktopWindowControlsStyle: 'macos' }, 12) as Record<string, unknown>;
expect(result.desktopWindowControlsStyle).toBe('classic');
});
});
@@ -945,11 +945,16 @@ export const settingsDict = {
'settings.openchamber.sessionRetention.toast.failedArchiveCount': 'Failed to archive {count} session(s)',
'settings.openchamber.sessionRetention.toast.failedDeleteCount': 'Failed to delete {count} session(s)',
'settings.openchamber.desktopNetwork.title': 'Desktop Network Access',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': 'Window controls position',
'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription': 'Choose where minimize, maximize, and close buttons appear. Defaults to the right.',
'settings.openchamber.desktopNetwork.field.windowControls': 'Window controls',
'settings.openchamber.desktopNetwork.field.windowControlsDescription': 'Choose where window controls appear and how they look.',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': 'Position',
'settings.openchamber.desktopNetwork.field.windowControlsPositionAria': 'Window controls position',
'settings.openchamber.desktopNetwork.field.windowControlsStyle': 'Style',
'settings.openchamber.desktopNetwork.field.windowControlsStyleAria': 'Window controls style',
'settings.openchamber.desktopNetwork.option.windowControlsLeft': 'Left',
'settings.openchamber.desktopNetwork.option.windowControlsRight': 'Right',
'settings.openchamber.desktopNetwork.option.windowControlsClassic': 'Classic',
'settings.openchamber.desktopNetwork.option.windowControlsTrafficLights': 'Traffic lights',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'Start OpenChamber at login',
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'Start OpenChamber when you log in',
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Starts the app in the background without opening a window. Use the desktop status icon to open it.',
@@ -912,11 +912,16 @@ export const settingsDict = {
"settings.openchamber.sessionRetention.toast.failedArchiveCount": "No se pudo archivar {count} sesión(es)",
"settings.openchamber.sessionRetention.toast.failedDeleteCount": "No se pudo eliminar {count} sesión(es)",
"settings.openchamber.desktopNetwork.title": "Acceso de red de escritorio",
"settings.openchamber.desktopNetwork.field.windowControlsPosition": "Posición de los controles de ventana",
"settings.openchamber.desktopNetwork.field.windowControlsPositionDescription": "Elige dónde aparecen los botones de minimizar, maximizar y cerrar. Por defecto a la derecha.",
"settings.openchamber.desktopNetwork.field.windowControls": "Controles de ventana",
"settings.openchamber.desktopNetwork.field.windowControlsDescription": "Elige dónde aparecen los controles de ventana y su apariencia.",
"settings.openchamber.desktopNetwork.field.windowControlsPosition": "Posición",
"settings.openchamber.desktopNetwork.field.windowControlsPositionAria": "Posición de los controles de ventana",
"settings.openchamber.desktopNetwork.field.windowControlsStyle": "Estilo",
"settings.openchamber.desktopNetwork.field.windowControlsStyleAria": "Estilo de los controles de ventana",
"settings.openchamber.desktopNetwork.option.windowControlsLeft": "Izquierda",
"settings.openchamber.desktopNetwork.option.windowControlsRight": "Derecha",
"settings.openchamber.desktopNetwork.option.windowControlsClassic": "Clásico",
"settings.openchamber.desktopNetwork.option.windowControlsTrafficLights": "Semáforo",
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Iniciar OpenChamber al iniciar sesión",
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Iniciar OpenChamber al iniciar sesión",
"settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia la app en segundo plano sin abrir una ventana. Usa el icono de estado del escritorio para abrirla.",
@@ -833,11 +833,16 @@ export const settingsDict = {
'settings.openchamber.sessionRetention.toast.failedArchiveCount': 'Échec de l\'archivage des sessions {count}',
'settings.openchamber.sessionRetention.toast.failedDeleteCount': 'Échec de la suppression des sessions {count}',
'settings.openchamber.desktopNetwork.title': 'Accès au réseau de bureau',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': 'Position des contrôles de fenêtre',
'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription': 'Choisissez où apparaissent les boutons Réduire, Agrandir et Fermer. Par défaut à droite.',
'settings.openchamber.desktopNetwork.field.windowControls': 'Contrôles de fenêtre',
'settings.openchamber.desktopNetwork.field.windowControlsDescription': 'Choisissez où apparaissent les contrôles de fenêtre et leur apparence.',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': 'Position',
'settings.openchamber.desktopNetwork.field.windowControlsPositionAria': 'Position des contrôles de fenêtre',
'settings.openchamber.desktopNetwork.field.windowControlsStyle': 'Style',
'settings.openchamber.desktopNetwork.field.windowControlsStyleAria': 'Style des contrôles de fenêtre',
'settings.openchamber.desktopNetwork.option.windowControlsLeft': 'Gauche',
'settings.openchamber.desktopNetwork.option.windowControlsRight': 'Droite',
'settings.openchamber.desktopNetwork.option.windowControlsClassic': 'Classique',
'settings.openchamber.desktopNetwork.option.windowControlsTrafficLights': 'Feux de circulation',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'Démarrez OpenChamber lors de la connexion',
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'Démarrez OpenChamber lorsque vous vous connectez',
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'Démarre l\'application en arrière-plan sans ouvrir de fenêtre. Utilisez l\'icône d\'état du bureau pour l\'ouvrir.',
@@ -945,11 +945,16 @@ export const settingsDict = {
'settings.openchamber.sessionRetention.toast.failedArchiveCount': '{count} 個の Session のアーカイブに失敗しました',
'settings.openchamber.sessionRetention.toast.failedDeleteCount': '{count} 個の Session の削除に失敗しました',
'settings.openchamber.desktopNetwork.title': 'Desktop ネットワークアクセス',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': 'ウィンドウコントロールの位置',
'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription': '最小化・最大化・閉じるボタンの表示位置を選びます。デフォルトは右側です。',
'settings.openchamber.desktopNetwork.field.windowControls': 'ウィンドウコントロール',
'settings.openchamber.desktopNetwork.field.windowControlsDescription': 'ウィンドウコントロールの表示位置とスタイルを選びます。',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': '位置',
'settings.openchamber.desktopNetwork.field.windowControlsPositionAria': 'ウィンドウコントロールの位置',
'settings.openchamber.desktopNetwork.field.windowControlsStyle': 'スタイル',
'settings.openchamber.desktopNetwork.field.windowControlsStyleAria': 'ウィンドウコントロールのスタイル',
'settings.openchamber.desktopNetwork.option.windowControlsLeft': '左',
'settings.openchamber.desktopNetwork.option.windowControlsRight': '右',
'settings.openchamber.desktopNetwork.option.windowControlsClassic': 'クラシック',
'settings.openchamber.desktopNetwork.option.windowControlsTrafficLights': 'トラフィックライト',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': 'ログイン時に OpenChamber を起動',
'settings.openchamber.desktopNetwork.field.launchAtLogin': 'ログイン時に OpenChamber を起動',
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': 'ウィンドウを開かずにバックグラウンドでアプリを起動します。デスクトップのステータスアイコンから開けます。',
@@ -912,11 +912,16 @@ export const settingsDict = {
'settings.openchamber.sessionRetention.toast.failedArchiveCount': '세션 {count}개를 보관하지 못했습니다',
'settings.openchamber.sessionRetention.toast.failedDeleteCount': '세션 {count}개를 삭제하지 못했습니다',
'settings.openchamber.desktopNetwork.title': 'Desktop 네트워크 접속',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': '창 컨트롤 위치',
'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription': '최소화, 최대화, 닫기 버튼이 표시될 위치를 선택합니다. 기본값은 오른쪽입니다.',
'settings.openchamber.desktopNetwork.field.windowControls': '창 컨트롤',
'settings.openchamber.desktopNetwork.field.windowControlsDescription': '창 컨트롤의 위치와 모양을 선택합니다.',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': '위치',
'settings.openchamber.desktopNetwork.field.windowControlsPositionAria': '창 컨트롤 위치',
'settings.openchamber.desktopNetwork.field.windowControlsStyle': '스타일',
'settings.openchamber.desktopNetwork.field.windowControlsStyleAria': '창 컨트롤 스타일',
'settings.openchamber.desktopNetwork.option.windowControlsLeft': '왼쪽',
'settings.openchamber.desktopNetwork.option.windowControlsRight': '오른쪽',
'settings.openchamber.desktopNetwork.option.windowControlsClassic': '클래식',
'settings.openchamber.desktopNetwork.option.windowControlsTrafficLights': '트래픽 라이트',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': '로그인 시 OpenChamber 시작',
'settings.openchamber.desktopNetwork.field.launchAtLogin': '로그인 시 OpenChamber 시작',
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '창을 열지 않고 백그라운드에서 앱을 시작합니다. 데스크톱 상태 아이콘으로 열 수 있습니다.',
@@ -780,11 +780,16 @@ export const settingsDict = {
'settings.openchamber.desktopNetwork.hint.openAfterRestart': 'Po restarcie otwórz z innego urządzenia: ',
'settings.openchamber.desktopNetwork.hint.openNow': 'Otwórz z innego urządzenia: ',
'settings.openchamber.desktopNetwork.title': 'Dostęp sieciowy pulpitu',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': 'Pozycja elementów sterujących oknem',
'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription': 'Wybierz, gdzie mają się pojawiać przyciski minimalizacji, maksymalizacji i zamykania. Domyślnie po prawej.',
'settings.openchamber.desktopNetwork.field.windowControls': 'Elementy sterujące oknem',
'settings.openchamber.desktopNetwork.field.windowControlsDescription': 'Wybierz, gdzie mają się pojawiać elementy sterujące oknem i jak mają wyglądać.',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': 'Pozycja',
'settings.openchamber.desktopNetwork.field.windowControlsPositionAria': 'Pozycja elementów sterujących oknem',
'settings.openchamber.desktopNetwork.field.windowControlsStyle': 'Styl',
'settings.openchamber.desktopNetwork.field.windowControlsStyleAria': 'Styl elementów sterujących oknem',
'settings.openchamber.desktopNetwork.option.windowControlsLeft': 'Lewo',
'settings.openchamber.desktopNetwork.option.windowControlsRight': 'Prawo',
'settings.openchamber.desktopNetwork.option.windowControlsClassic': 'Klasyczny',
'settings.openchamber.desktopNetwork.option.windowControlsTrafficLights': 'Sygnalizacja świetlna',
'settings.openchamber.git.changesViewAria': 'Tryb widoku zmian Git',
'settings.openchamber.git.changesViewTitle': 'Widok zmian',
'settings.openchamber.git.enableGitmoji': 'Włącz wybieranie Gitmoji',
@@ -912,11 +912,16 @@ export const settingsDict = {
"settings.openchamber.sessionRetention.toast.failedArchiveCount": "Não foi possível arquivar {count} sessão(es)",
"settings.openchamber.sessionRetention.toast.failedDeleteCount": "Não foi possível excluir {count} sessão(es)",
"settings.openchamber.desktopNetwork.title": "Acesso de rede do desktop",
"settings.openchamber.desktopNetwork.field.windowControlsPosition": "Posição dos controles da janela",
"settings.openchamber.desktopNetwork.field.windowControlsPositionDescription": "Escolha onde os botões de minimizar, maximizar e fechar aparecem. O padrão é à direita.",
"settings.openchamber.desktopNetwork.field.windowControls": "Controles da janela",
"settings.openchamber.desktopNetwork.field.windowControlsDescription": "Escolha onde os controles da janela aparecem e como eles são exibidos.",
"settings.openchamber.desktopNetwork.field.windowControlsPosition": "Posição",
"settings.openchamber.desktopNetwork.field.windowControlsPositionAria": "Posição dos controles da janela",
"settings.openchamber.desktopNetwork.field.windowControlsStyle": "Estilo",
"settings.openchamber.desktopNetwork.field.windowControlsStyleAria": "Estilo dos controles da janela",
"settings.openchamber.desktopNetwork.option.windowControlsLeft": "Esquerda",
"settings.openchamber.desktopNetwork.option.windowControlsRight": "Direita",
"settings.openchamber.desktopNetwork.option.windowControlsClassic": "Clássico",
"settings.openchamber.desktopNetwork.option.windowControlsTrafficLights": "Semáforo",
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Iniciar o OpenChamber ao fazer login",
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Iniciar o OpenChamber ao fazer login",
"settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Inicia o app em segundo plano sem abrir uma janela. Use o ícone de status da área de trabalho para abrir.",
@@ -912,11 +912,16 @@ export const settingsDict = {
"settings.openchamber.sessionRetention.toast.failedArchiveCount": "Не вдалося заархівувати сесій: {count}",
"settings.openchamber.sessionRetention.toast.failedDeleteCount": "Не вдалося видалити сесій: {count}",
"settings.openchamber.desktopNetwork.title": "Мережевий доступ десктопного застосунку",
"settings.openchamber.desktopNetwork.field.windowControlsPosition": "Позиція елементів керування вікном",
"settings.openchamber.desktopNetwork.field.windowControlsPositionDescription": "Виберіть, де з’являються кнопки згортання, розгортання та закриття. За замовчуванням справа.",
"settings.openchamber.desktopNetwork.field.windowControls": "Елементи керування вікном",
"settings.openchamber.desktopNetwork.field.windowControlsDescription": "Виберіть, де з’являються елементи керування вікном і як вони виглядають.",
"settings.openchamber.desktopNetwork.field.windowControlsPosition": "Позиція",
"settings.openchamber.desktopNetwork.field.windowControlsPositionAria": "Позиція елементів керування вікном",
"settings.openchamber.desktopNetwork.field.windowControlsStyle": "Стиль",
"settings.openchamber.desktopNetwork.field.windowControlsStyleAria": "Стиль елементів керування вікном",
"settings.openchamber.desktopNetwork.option.windowControlsLeft": "Зліва",
"settings.openchamber.desktopNetwork.option.windowControlsRight": "Справа",
"settings.openchamber.desktopNetwork.option.windowControlsClassic": "Класичний",
"settings.openchamber.desktopNetwork.option.windowControlsTrafficLights": "Світлофор",
"settings.openchamber.desktopNetwork.field.launchAtLoginAria": "Запускати OpenChamber під час входу в систему",
"settings.openchamber.desktopNetwork.field.launchAtLogin": "Запускати OpenChamber під час входу в систему",
"settings.openchamber.desktopNetwork.field.launchAtLoginDescription": "Запускає застосунок у фоні без відкриття вікна. Відкрийте його через піктограму стану на робочому столі.",
@@ -912,11 +912,16 @@ export const settingsDict = {
'settings.openchamber.sessionRetention.toast.failedArchiveCount': '归档 {count} 个会话失败',
'settings.openchamber.sessionRetention.toast.failedDeleteCount': '删除 {count} 个会话失败',
'settings.openchamber.desktopNetwork.title': '桌面端网络访问',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': '窗口控件位置',
'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription': '选择最小化、最大化和关闭按钮的显示位置。默认在右侧。',
'settings.openchamber.desktopNetwork.field.windowControls': '窗口控件',
'settings.openchamber.desktopNetwork.field.windowControlsDescription': '选择窗口控件的位置和外观。',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': '位置',
'settings.openchamber.desktopNetwork.field.windowControlsPositionAria': '窗口控件位置',
'settings.openchamber.desktopNetwork.field.windowControlsStyle': '样式',
'settings.openchamber.desktopNetwork.field.windowControlsStyleAria': '窗口控件样式',
'settings.openchamber.desktopNetwork.option.windowControlsLeft': '左侧',
'settings.openchamber.desktopNetwork.option.windowControlsRight': '右侧',
'settings.openchamber.desktopNetwork.option.windowControlsClassic': '经典',
'settings.openchamber.desktopNetwork.option.windowControlsTrafficLights': '红绿灯',
'settings.openchamber.desktopNetwork.field.launchAtLoginAria': '登录时启动 OpenChamber',
'settings.openchamber.desktopNetwork.field.launchAtLogin': '登录时启动 OpenChamber',
'settings.openchamber.desktopNetwork.field.launchAtLoginDescription': '在后台启动应用且不打开窗口。可通过桌面状态图标打开。',
@@ -909,11 +909,16 @@
'settings.openchamber.sessionRetention.toast.failedArchiveCount': '封存 {count} 個工作階段失敗',
'settings.openchamber.sessionRetention.toast.failedDeleteCount': '刪除 {count} 個工作階段失敗',
'settings.openchamber.desktopNetwork.title': '桌面端網路存取',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': '視窗控制項位置',
'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription': '選擇最小化、最大化和關閉按鈕的顯示位置。預設在右側。',
'settings.openchamber.desktopNetwork.field.windowControls': '視窗控制項',
'settings.openchamber.desktopNetwork.field.windowControlsDescription': '選擇視窗控制項的位置和外觀。',
'settings.openchamber.desktopNetwork.field.windowControlsPosition': '位置',
'settings.openchamber.desktopNetwork.field.windowControlsPositionAria': '視窗控制項位置',
'settings.openchamber.desktopNetwork.field.windowControlsStyle': '樣式',
'settings.openchamber.desktopNetwork.field.windowControlsStyleAria': '視窗控制項樣式',
'settings.openchamber.desktopNetwork.option.windowControlsLeft': '左側',
'settings.openchamber.desktopNetwork.option.windowControlsRight': '右側',
'settings.openchamber.desktopNetwork.option.windowControlsClassic': '經典',
'settings.openchamber.desktopNetwork.option.windowControlsTrafficLights': '紅綠燈',
'settings.openchamber.desktopNetwork.field.allowLanAccessAria': '允許桌面 sidecar 區域網路存取',
'settings.openchamber.desktopNetwork.field.allowLanAccess': '允許你本機網路中的其他裝置開啟此應用程式',
'settings.openchamber.desktopNetwork.field.allowLanAccessDescription': '會重新啟動應用程式,以便手機、平板和同一 Wi‑Fi 下的其他電腦存取。',
+16
View File
@@ -557,6 +557,7 @@ const materializeAuthoritativeUiSettings = (settings: DesktopSettings): DesktopS
timeFormatPreference: defaults.timeFormatPreference,
weekStartPreference: defaults.weekStartPreference,
desktopWindowControlsPosition: defaults.desktopWindowControlsPosition,
desktopWindowControlsStyle: defaults.desktopWindowControlsStyle,
chatRenderMode: defaults.chatRenderMode,
activityRenderMode: defaults.activityRenderMode,
mermaidRenderingMode: defaults.mermaidRenderingMode,
@@ -748,6 +749,16 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
store.setDesktopWindowControlsPosition(nextPosition);
}
}
if (typeof settings.desktopWindowControlsStyle === 'string') {
const nextStyle = settings.desktopWindowControlsStyle === 'traffic-lights'
? 'traffic-lights'
: settings.desktopWindowControlsStyle === 'classic'
? 'classic'
: null;
if (nextStyle && nextStyle !== store.desktopWindowControlsStyle) {
store.setDesktopWindowControlsStyle(nextStyle);
}
}
if (typeof settings.chatRenderMode === 'string'
&& (settings.chatRenderMode === 'sorted' || settings.chatRenderMode === 'live')) {
if (settings.chatRenderMode !== store.chatRenderMode) {
@@ -1379,6 +1390,11 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
result.desktopWindowControlsPosition = 'right';
}
}
if (typeof candidate.desktopWindowControlsStyle === 'string') {
if (candidate.desktopWindowControlsStyle === 'classic' || candidate.desktopWindowControlsStyle === 'traffic-lights') {
result.desktopWindowControlsStyle = candidate.desktopWindowControlsStyle;
}
}
if (typeof candidate.chatRenderMode === 'string'
&& (candidate.chatRenderMode === 'sorted' || candidate.chatRenderMode === 'live')) {
result.chatRenderMode = candidate.chatRenderMode;
+10 -3
View File
@@ -399,9 +399,16 @@ const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
{
id: 'sessions.desktop-window-controls-position',
page: 'appearance',
titleKey: 'settings.openchamber.desktopNetwork.field.windowControlsPosition',
descriptionKey: 'settings.openchamber.desktopNetwork.field.windowControlsPositionDescription',
keywords: ['desktop', 'window', 'controls', 'minimize', 'maximize', 'close', 'titlebar', 'linux', 'windows'],
titleKey: 'settings.openchamber.desktopNetwork.field.windowControls',
descriptionKey: 'settings.openchamber.desktopNetwork.field.windowControlsDescription',
keywords: ['desktop', 'window', 'controls', 'minimize', 'maximize', 'close', 'titlebar', 'linux', 'windows', 'position'],
isAvailable: (ctx) => ctx.isDesktop && (ctx.isWindows || !ctx.isMac),
},
{
id: 'sessions.desktop-window-controls-style',
page: 'appearance',
titleKey: 'settings.openchamber.desktopNetwork.field.windowControlsStyle',
keywords: ['desktop', 'window', 'controls', 'style', 'traffic', 'lights', 'classic', 'macos', 'titlebar'],
isAvailable: (ctx) => ctx.isDesktop && (ctx.isWindows || !ctx.isMac),
},
{