mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 13:35:42 +09:00
121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {createRequire} from 'node:module';
|
|
import {createChildLogger} from '@electron/common/Logger';
|
|
import type {LinuxAppearanceSnapshot} from '@electron/common/Types';
|
|
import {getLinuxPortalsMode} from '@electron/main/LaunchOptions';
|
|
|
|
export type {LinuxAppearanceSnapshot} from '@electron/common/Types';
|
|
|
|
const logger = createChildLogger('LinuxAppearance');
|
|
const requireModule = createRequire(import.meta.url);
|
|
|
|
interface SettingsChangeEvent {
|
|
namespace: string;
|
|
key: string;
|
|
uint32?: number;
|
|
accent?: {r: number; g: number; b: number};
|
|
}
|
|
|
|
interface SettingsCtor {
|
|
new (onChange: (event: SettingsChangeEvent) => void): {close: () => void};
|
|
}
|
|
|
|
interface PortalsModule {
|
|
readColorScheme: () => 'no-preference' | 'prefer-dark' | 'prefer-light';
|
|
readContrast: () => 'no-preference' | 'high';
|
|
readAccentColor: () => {r: number; g: number; b: number} | null;
|
|
Settings: SettingsCtor | null;
|
|
loadError: Error | null;
|
|
}
|
|
|
|
let cached: PortalsModule | null | undefined;
|
|
|
|
function loadAddon(): PortalsModule | null {
|
|
if (cached !== undefined) return cached;
|
|
if (process.platform !== 'linux') {
|
|
cached = null;
|
|
return cached;
|
|
}
|
|
if (getLinuxPortalsMode(process.argv) === 'off') {
|
|
logger.info('@fluxer/linux-portals disabled by launch diagnostics; appearance reads disabled');
|
|
cached = null;
|
|
return cached;
|
|
}
|
|
try {
|
|
const mod = requireModule('@fluxer/linux-portals') as PortalsModule;
|
|
if (mod.loadError) {
|
|
logger.info('@fluxer/linux-portals reported load error', {error: mod.loadError});
|
|
cached = null;
|
|
return cached;
|
|
}
|
|
cached = mod;
|
|
return cached;
|
|
} catch (error) {
|
|
logger.info('@fluxer/linux-portals not available; appearance reads disabled', {error});
|
|
cached = null;
|
|
return cached;
|
|
}
|
|
}
|
|
|
|
export function readLinuxAppearance(): LinuxAppearanceSnapshot {
|
|
const mod = loadAddon();
|
|
if (!mod) {
|
|
return {colorScheme: 'no-preference', contrast: 'no-preference', accent: null};
|
|
}
|
|
return {
|
|
colorScheme: mod.readColorScheme(),
|
|
contrast: mod.readContrast(),
|
|
accent: mod.readAccentColor(),
|
|
};
|
|
}
|
|
|
|
type LinuxAppearanceListener = (snapshot: LinuxAppearanceSnapshot) => void;
|
|
|
|
export interface LinuxAppearanceSubscription {
|
|
close(): void;
|
|
}
|
|
|
|
export function subscribeLinuxAppearance(listener: LinuxAppearanceListener): LinuxAppearanceSubscription | null {
|
|
const mod = loadAddon();
|
|
if (!mod?.Settings) return null;
|
|
let closed = false;
|
|
let instance: {close: () => void} | null = null;
|
|
try {
|
|
instance = new mod.Settings((event) => {
|
|
if (closed) return;
|
|
const snapshot: LinuxAppearanceSnapshot = {
|
|
colorScheme: mod.readColorScheme(),
|
|
contrast: mod.readContrast(),
|
|
accent: mod.readAccentColor(),
|
|
};
|
|
if (event.namespace === 'org.freedesktop.appearance') {
|
|
if (event.key === 'color-scheme' && event.uint32 != null) {
|
|
snapshot.colorScheme =
|
|
event.uint32 === 1 ? 'prefer-dark' : event.uint32 === 2 ? 'prefer-light' : 'no-preference';
|
|
} else if (event.key === 'contrast' && event.uint32 != null) {
|
|
snapshot.contrast = event.uint32 === 1 ? 'high' : 'no-preference';
|
|
} else if (event.key === 'accent-color') {
|
|
snapshot.accent =
|
|
event.accent && event.accent.r >= 0 && event.accent.g >= 0 && event.accent.b >= 0 ? event.accent : null;
|
|
}
|
|
}
|
|
listener(snapshot);
|
|
});
|
|
} catch (error) {
|
|
logger.warn('linux-portals Settings subscribe failed', {error});
|
|
return null;
|
|
}
|
|
return {
|
|
close: () => {
|
|
if (closed) return;
|
|
closed = true;
|
|
try {
|
|
instance?.close();
|
|
} catch (error) {
|
|
logger.warn('linux-portals Settings close threw', {error});
|
|
}
|
|
},
|
|
};
|
|
}
|