Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 7 additions & 20 deletions src/main/lib/ipc/registerSettingsHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
ipcMain, nativeTheme,
ipcMain,
sources, settings, i18n,
getAppVersion, resolveTheme,
_onLocaleChanged, _onThemeChanged, _broadcastToRenderer,
_onLocaleChanged, _broadcastToRenderer,
} from './shared'
import { updateTitleBarOverlay } from '../titleBarOverlay'
import * as mainTelemetry from '../telemetry'
import { detectFirstUseState } from '../firstUseDetection'
import * as updater from '../updater'
Expand All @@ -23,12 +22,8 @@ export function registerSettingsHandlers(): void {
fields: [
{ id: 'language', label: i18n.t('settings.language'), type: 'select', value: s.language || i18n.getLocale(),
options: i18n.getAvailableLocales() },
{ id: 'theme', label: i18n.t('settings.theme'), type: 'select', value: s.theme || 'system',
options: [
{ value: 'system', label: i18n.t('settings.themeSystem') },
{ value: 'dark', label: i18n.t('settings.themeDark') },
{ value: 'light', label: i18n.t('settings.themeLight') },
] },
// Theme picker removed — the launcher is dark-only by design
// (see resolveTheme() in src/main/lib/ipc/shared.ts).
// Issue #488 — auto-check loop always runs; this toggle
// controls whether updates install silently vs prompt the
// user. The `autoUpdate` key is retained in the schema (no
Expand Down Expand Up @@ -119,11 +114,6 @@ export function registerSettingsHandlers(): void {

ipcMain.handle('set-setting', (_event, key: string, value: unknown) => {
settings.set(key, value)
if (key === 'theme') {
_broadcastToRenderer('theme-changed', resolveTheme())
updateTitleBarOverlay()
if (_onThemeChanged) _onThemeChanged()
}
if (key === 'language') {
i18n.init(value as string)
_broadcastToRenderer('locale-changed', i18n.getMessages())
Expand Down Expand Up @@ -172,10 +162,7 @@ export function registerSettingsHandlers(): void {

ipcMain.handle('get-resolved-theme', () => resolveTheme())

nativeTheme.on('updated', () => {
if (((settings.get('theme') as string | undefined) || 'system') !== 'system') return
_broadcastToRenderer('theme-changed', resolveTheme())
updateTitleBarOverlay()
if (_onThemeChanged) _onThemeChanged()
})
// Launcher is dark-only — no nativeTheme listener and no theme-changed
// broadcast. The IPC channel + preload subscription remain for ABI
// compatibility but are never fired.
}
9 changes: 4 additions & 5 deletions src/main/lib/ipc/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,12 +572,11 @@ export async function migrateDefaults(): Promise<void> {
}
}

const VALID_THEMES: readonly string[] = ['system', 'dark', 'light'] satisfies readonly Theme[]

/** Launcher is dark-only by design. The Theme / ResolvedTheme types
* are kept on the IPC contract so a future re-introduction wouldn't
* break the renderer / preload, but every caller resolves to 'dark'. */
export function resolveTheme(): ResolvedTheme {
const raw = settings.get('theme') as string | undefined
const theme: Theme = raw && VALID_THEMES.includes(raw) ? (raw as Theme) : 'system'
return theme === 'system' ? (nativeTheme.shouldUseDarkColors ? 'dark' : 'light') : theme
return 'dark'
}

export async function checkInstallationUpdates(): Promise<void> {
Expand Down
24 changes: 4 additions & 20 deletions src/renderer/src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,10 @@
--overlay-bg-opaque: var(--bg);
}

[data-theme="light"] {
--bg: #ffffff;
--text: #202121;
--text-muted: #a0a0a0;
--text-faint: #b4b4b4;
--accent: #3b82f6;
--accent-hover: #2563eb;
--selected: rgba(0, 0, 0, 0.7);
--surface: #e9e9e9;
--border: #b4b4b4;
--border-hover: #d9d9d9;
--danger: #f75951;
--warning: #fcbf64;
--success: #00cd72;
--info: #2563eb;
--terminal-bg: #f3f3f3;
--overlay-bg: rgba(0, 0, 0, 0.35);
--overlay-bg-heavy: rgba(0, 0, 0, 0.55);
--overlay-bg-opaque: var(--bg);
}
/* Light theme block removed — the launcher is dark-only by design.
* The Theme / ResolvedTheme types are kept for IPC compatibility but
* resolveTheme() in src/main/lib/ipc/shared.ts always returns 'dark'
* and useTheme() always sets [data-theme='dark'] on the documentElement. */

/* ================================================================
* Base styles from renderer/styles.css
Expand Down
27 changes: 11 additions & 16 deletions src/renderer/src/composables/useTheme.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { ref, onMounted } from 'vue'
import { useElectronApi } from './useElectronApi'
import { ref } from 'vue'
import type { ResolvedTheme } from '../../../types/ipc'

/**
* Dark-only launcher theme. The Theme / ResolvedTheme types remain for
* IPC compatibility (the title-bar bridge still pushes a per-page bg /
* text colour for the install's own ComfyUI theme), but the launcher
* UI itself is dark-only — no system / light variants.
*
* This composable just stamps `data-theme="dark"` on the documentElement
* once on mount. There's no listener to drop and no IPC roundtrip needed.
*/
export function useTheme() {
const { api, listen } = useElectronApi()
const theme = ref<ResolvedTheme>('dark')

function applyTheme(newTheme: ResolvedTheme): void {
theme.value = newTheme || 'dark'
document.documentElement.setAttribute('data-theme', theme.value)
}

onMounted(async () => {
const resolved = await api.getResolvedTheme()
applyTheme(resolved)
})

listen(api.onThemeChanged, applyTheme)

document.documentElement.setAttribute('data-theme', 'dark')
return { theme }
}
Loading