Skip to content
Merged
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
12 changes: 4 additions & 8 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"url": "git+https://github.com/opencor/webapp.git"
},
"type": "module",
"version": "1.20261016.3",
"version": "1.20261016.4",
"engines": {
"bun": ">=1.2.0"
},
Expand Down Expand Up @@ -77,7 +77,7 @@
"@wasm-fmt/clang-format": "^23.1.1",
"autoprefixer": "^10.6.1",
"cmake-js": "^8.0.0",
"electron": "^44.7.2",
"electron": "^44.8.0",
"electron-builder": "^26.15.3",
"electron-conf": "^1.3.0",
"electron-updater": "^6.8.9",
Expand Down
8 changes: 8 additions & 0 deletions src/main/MainWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { autoUpdater, type ProgressInfo, type UpdateCheckResult } from 'electron
import fs from 'node:fs';
import path from 'node:path';

import type { OpenCORTheme } from '../renderer/index';
import { formatError, isDataUrlFileName, type ISettings, isUrl } from '../renderer/src/common/common';
import { FULL_URI_SCHEME, LONG_DELAY } from '../renderer/src/common/constants';
import { appImagePath, isMacOs, isPackaged } from '../renderer/src/common/electron';
Expand Down Expand Up @@ -153,6 +154,13 @@ export const saveSettings = (settings: ISettings): void => {
electronConf.set('settings', settings);
};

export const applyTheme = (theme: OpenCORTheme = electronConf.get('settings.general.theme')): void => {
// Apply the given theme (or the one in our settings if none is given) to OpenCOR, so that it is used by our renderer
// and by Electron (e.g., for its dialogs).

electron.nativeTheme.themeSource = theme === 'light' || theme === 'dark' ? theme : 'system';
};

let _resetAll = false;

export const resetAll = (): void => {
Expand Down
12 changes: 11 additions & 1 deletion src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';

import type { OpenCORTheme } from '../renderer/index';
import { formatError, type ISettings, isObject } from '../renderer/src/common/common';
import { SHORT_DELAY, URI_SCHEME } from '../renderer/src/common/constants';
import { appImagePath, isLinux, isPackaged, isWindows } from '../renderer/src/common/electron';
Expand All @@ -22,6 +23,7 @@ import { startRendererServer, stopRendererServer } from '../renderer/src/common/

import { enableDisableFileCloseAndCloseAllMenuItems, enableDisableMainMenu } from './MainMenu';
import {
applyTheme,
checkForUpdates,
downloadAndInstallUpdate,
fileClosed,
Expand Down Expand Up @@ -370,7 +372,8 @@ electron.app
},
settings: {
general: {
checkForUpdatesAtStartup: true
checkForUpdatesAtStartup: true,
theme: 'system'
}
}
});
Expand Down Expand Up @@ -399,6 +402,10 @@ electron.app
});
}

// Apply our theme, so that our splash screen window and main window use it from the start.

applyTheme();

// Create our splash window.

const splashScreenWindow = new SplashScreenWindow();
Expand Down Expand Up @@ -448,6 +455,9 @@ electron.app

// Handle some requests from our renderer process.

electron.ipcMain.handle('apply-theme', (_event, theme: OpenCORTheme) => {
applyTheme(theme);
});
electron.ipcMain.handle('check-for-updates', (_event, atStartup: boolean) => {
checkForUpdates(atStartup);
});
Expand Down
2 changes: 2 additions & 0 deletions src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { promises as fs } from 'node:fs';

import loc from '../../dist/libOpenCOR/Release/libOpenCOR.node';

import type { OpenCORTheme } from '../renderer/index';
import type { ISettings } from '../renderer/src/common/common';

// A helper function to listen to a channel from the main process.
Expand Down Expand Up @@ -84,6 +85,7 @@ electron.contextBridge.exposeInMainWorld('electronApi', {

// Renderer process asking the main process to do something for it.

applyTheme: (theme: OpenCORTheme) => electron.ipcRenderer.invoke('apply-theme', theme),
checkForUpdates: (atStartup: boolean) => electron.ipcRenderer.invoke('check-for-updates', atStartup),
clearGitHubCache: (): Promise<void> => electron.ipcRenderer.invoke('clear-github-cache'),
deleteGitHubAccessToken: (): Promise<boolean> => electron.ipcRenderer.invoke('delete-github-access-token'),
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
overflow: hidden;
}
@media (prefers-color-scheme: dark) {
body {
:root:not(.opencor-light-mode) body {
background-color: #18181b; /* Dark version of --p-content-background. */
color: #ffffff; /* Dark version of --p-content-color. */
}
}
:root.opencor-dark-mode body {
background-color: #18181b; /* Dark version of --p-content-background. */
color: #ffffff; /* Dark version of --p-content-color. */
}
</style>
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
},
"./style.css": "./dist/opencor.css"
},
"version": "1.20261016.3",
"version": "1.20261016.4",
"libopencorVersion": "1.20261016.1",
"scripts": {
"build": "vite build && bun scripts/generate.version.ts",
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/src/assets/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
/* biome-ignore lint/correctness/noInvalidPositionAtImportRule: ignored using `--suppress` */
@import "primeicons/primeicons.css";

/* Make Tailwind CSS's dark variant follow our theme rather than (necessarily) the system's theme. */

@custom-variant dark (&:where(.opencor-dark-mode, .opencor-dark-mode *));

body {
*,
::before,
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/src/common/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IOpenCORSimulationDataValue, OpenCORSimulationData } from '../../index';
import type { IOpenCORSimulationDataValue, OpenCORSimulationData, OpenCORTheme } from '../../index';

import * as dependencies from './dependencies';
import { electronApi } from './electronApi';
Expand All @@ -7,6 +7,7 @@ import { electronApi } from './electronApi';

export interface ISettingsGeneral {
checkForUpdatesAtStartup: boolean;
theme: OpenCORTheme;
}

export interface ISettings {
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/src/common/electronApi.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { OpenCORTheme } from '../../index';

import type { ISettings } from './common';

interface IElectronApi {
Expand All @@ -9,6 +11,7 @@ interface IElectronApi {

// Renderer process asking the main process to do something for it.

applyTheme: (theme: OpenCORTheme) => void;
checkForUpdates: (atStartup: boolean) => void;
clearGitHubCache: () => Promise<void>;
deleteGitHubAccessToken: () => Promise<boolean>;
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/src/common/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { electronApi } from './electronApi';
const defaultSettings = (): ISettings => {
return {
general: {
checkForUpdatesAtStartup: true
checkForUpdatesAtStartup: true,
theme: 'system'
}
};
};
Expand Down
17 changes: 16 additions & 1 deletion src/renderer/src/common/vueCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { OpenCORTheme } from '../../index';
import * as locSedApi from '../libopencor/locSedApi';

import { MEDIUM_DELAY, VERY_SHORT_DELAY } from './constants';
import { electronApi } from './electronApi';

// A constant to know the UID of the active instance of OpenCOR.

Expand All @@ -26,6 +27,7 @@ export const useTheme = vueusecore.createGlobalState(() => {
};

const updateDocumentClasses = () => {
document.documentElement.classList.toggle('opencor-light-mode', isLightMode.value);
document.documentElement.classList.toggle('opencor-dark-mode', isDarkMode.value);
};

Expand All @@ -41,7 +43,10 @@ export const useTheme = vueusecore.createGlobalState(() => {
};

const setTheme = (newTheme: OpenCORTheme | undefined) => {
_theme.value = newTheme ?? 'system';
// Note: the given theme may come from our settings, which may have been edited by hand, so we fall back on the
// system theme for anything that is not a valid theme.

_theme.value = newTheme === 'light' || newTheme === 'dark' ? newTheme : 'system';

if (_theme.value === 'light') {
isLightMode.value = true;
Expand All @@ -56,6 +61,15 @@ export const useTheme = vueusecore.createGlobalState(() => {
updateDocumentClasses();
};

const applyTheme = (newTheme: OpenCORTheme) => {
// Set the given theme and let Electron know about it (e.g., for its dialogs), if we are running in Electron.
// Note: unlike setTheme(), this is to be used when the user changes our theme, not when we initialise it.

setTheme(newTheme);

electronApi?.applyTheme(_theme.value);
};

const useLightMode = (): boolean => {
return isLightMode.value;
};
Expand All @@ -67,6 +81,7 @@ export const useTheme = vueusecore.createGlobalState(() => {
return {
theme,
setTheme,
applyTheme,
useLightMode,
useDarkMode
};
Expand Down
6 changes: 2 additions & 4 deletions src/renderer/src/components/ContentsComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -742,9 +742,7 @@ if (common.isDesktop()) {
color: var(--p-red-50);
}

@media (prefers-color-scheme: dark) {
.remove-button:hover {
background-color: var(--p-red-400);
}
.opencor-dark-mode .remove-button:hover {
background-color: var(--p-red-400);
}
</style>
25 changes: 24 additions & 1 deletion src/renderer/src/components/MainMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
</a>
</template>
<template #end>
<button v-if="updateAvailable" class="update-link flex items-center px-1.5 py-0.5 text-xs cursor-pointer rounded-full transition-colors duration-150 mr-1"
<button v-if="updateAvailable" class="update-link absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center whitespace-nowrap px-1.5 py-0.5 text-xs cursor-pointer rounded-full transition-colors duration-150"
type="button"
@click="emit('updateAvailable')"
>
New version available!
</button>
<ThemeWidget v-if="props.themeCanBeChanged" :theme="theme.theme()" @switch="onThemeSwitch" />
</template>
</Menubar>
</template>
Expand All @@ -37,14 +38,19 @@ import * as vueusecore from '@vueuse/core';
import type Menubar from 'primevue/menubar';
import * as vue from 'vue';

import type { OpenCORTheme } from '../../index';

import * as common from '../common/common';
import { settings } from '../common/settings';
import * as version from '../common/version';
import * as vueCommon from '../common/vueCommon';

const props = defineProps<{
id?: string;
isActiveApp: boolean;
hasFiles: boolean;
uiEnabled: boolean;
themeCanBeChanged: boolean;
}>();

const emit = defineEmits<{
Expand All @@ -62,6 +68,22 @@ const isWindowsOrLinux = common.isWindows() || common.isLinux();
const isMacOs = common.isMacOs();
const updateAvailable = version.updateAvailable;

// Theme button, which allows the user to cycle through the system, light, and dark themes.

const theme = vueCommon.useTheme();

const onThemeSwitch = (newTheme: OpenCORTheme) => {
// Switch to the next theme, apply it, and save it in our settings.

theme.applyTheme(newTheme);

settings.general.theme = newTheme;

settings.save();
};

// Menu items, which are computed so that they can be reactive to changes in our props.

const items = vue.computed(() => [
{
label: 'File',
Expand Down Expand Up @@ -232,6 +254,7 @@ if (common.isDesktop()) {

<style scoped>
.p-menubar {
position: relative;
padding: 0.1rem;
border: none;
border-radius: 0;
Expand Down
Loading
Loading