diff --git a/gui/src/components/OnnxWeb.tsx b/gui/src/components/OnnxWeb.tsx
index 6eee4329..8fec1da6 100644
--- a/gui/src/components/OnnxWeb.tsx
+++ b/gui/src/components/OnnxWeb.tsx
@@ -1,9 +1,13 @@
-import { doesExist } from '@apextoaster/js-utils';
+import { mustExist } from '@apextoaster/js-utils';
import { TabContext, TabList, TabPanel } from '@mui/lab';
-import { Box, Container, Divider, Tab } from '@mui/material';
+import { Box, Container, Divider, PaletteMode, Tab, useMediaQuery, CssBaseline } from '@mui/material';
+import { createTheme, ThemeProvider } from '@mui/material/styles';
import * as React from 'react';
+import { useContext, useMemo } from 'react';
import { useHash } from 'react-use/lib/useHash';
+import { useStore } from 'zustand';
+import { StateContext } from '../state.js';
import { ModelControl } from './control/ModelControl.js';
import { ImageHistory } from './ImageHistory.js';
import { Logo } from './Logo.js';
@@ -13,50 +17,68 @@ import { Inpaint } from './tab/Inpaint.js';
import { Settings } from './tab/Settings.js';
import { Txt2Img } from './tab/Txt2Img.js';
import { Upscale } from './tab/Upscale.js';
-import { getTab, TAB_LABELS } from './utils.js';
+import { getTheme, getTab, TAB_LABELS } from './utils.js';
export function OnnxWeb() {
+ /* checks for system light/dark mode preference */
+ const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
+ const state = useStore(mustExist(useContext(StateContext)));
+
+ const theme = useMemo(
+ () => createTheme({
+ palette: {
+ mode: getTheme(state.theme, prefersDarkMode) as PaletteMode
+ }
+ }),
+ [prefersDarkMode, state.theme],
+ );
+
const [hash, setHash] = useHash();
return (
-
-
-
-
-
-
-
-
-
- {
- setHash(idx);
- }}>
- {TAB_LABELS.map((name) => )}
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ {
+ setHash(idx);
+ }}>
+ {TAB_LABELS.map((name) => )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/gui/src/components/tab/Settings.tsx b/gui/src/components/tab/Settings.tsx
index 030a8880..5f09c1a2 100644
--- a/gui/src/components/tab/Settings.tsx
+++ b/gui/src/components/tab/Settings.tsx
@@ -1,6 +1,6 @@
import { doesExist, mustExist } from '@apextoaster/js-utils';
import { Refresh } from '@mui/icons-material';
-import { Alert, Button, Stack, TextField } from '@mui/material';
+import { Alert, Button, FormControlLabel, Stack, Switch, TextField, useMediaQuery } from '@mui/material';
import * as React from 'react';
import { useContext, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -8,6 +8,7 @@ import { useStore } from 'zustand';
import { getApiRoot } from '../../config.js';
import { ConfigContext, StateContext, STATE_KEY } from '../../state.js';
+import { getTheme } from '../utils.js';
import { NumericField } from '../input/NumericField.js';
function removeBlobs(key: string, value: unknown): unknown {
@@ -28,8 +29,10 @@ function removeBlobs(key: string, value: unknown): unknown {
}
export function Settings() {
+ const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const config = mustExist(useContext(ConfigContext));
const state = useStore(mustExist(useContext(StateContext)));
+ const theme = getTheme(state.theme, prefersDarkMode);
const [json, setJson] = useState(JSON.stringify(state, removeBlobs));
const [root, setRoot] = useState(getApiRoot(config));
@@ -80,6 +83,17 @@ export function Settings() {
{t('setting.loadState')}
+ {
+ if (theme === 'light') {
+ state.setTheme('dark');
+ } else {
+ state.setTheme('light');
+ }
+ }}
+ />
+ } label={t('setting.darkMode')} />
diff --git a/gui/src/components/utils.ts b/gui/src/components/utils.ts
index db59ffbd..2dc64cd1 100644
--- a/gui/src/components/utils.ts
+++ b/gui/src/components/utils.ts
@@ -17,3 +17,13 @@ export function getTab(hash: string): string {
return TAB_LABELS[0];
}
+
+export function getTheme(currentTheme: string, preferDark: boolean): string {
+ if (currentTheme === '') {
+ if (preferDark) {
+ return 'dark';
+ }
+ return 'light';
+ }
+ return currentTheme;
+}
diff --git a/gui/src/state.ts b/gui/src/state.ts
index 73123454..07184332 100644
--- a/gui/src/state.ts
+++ b/gui/src/state.ts
@@ -43,8 +43,10 @@ interface BrushSlice {
interface DefaultSlice {
defaults: TabState;
+ theme: string;
setDefaults(param: Partial): void;
+ setTheme(theme: string): void;
}
interface HistorySlice {
@@ -490,6 +492,7 @@ export function createStateSlices(server: ServerParams) {
defaults: {
...base,
},
+ theme: '',
setDefaults(params) {
set((prev) => ({
defaults: {
@@ -498,6 +501,11 @@ export function createStateSlices(server: ServerParams) {
}
}));
},
+ setTheme(theme) {
+ set((prev) => ({
+ theme,
+ }));
+ }
});
const createModelSlice: Slice = (set) => ({
diff --git a/gui/src/strings/de.ts b/gui/src/strings/de.ts
index 6b8a0fcd..92b8b138 100644
--- a/gui/src/strings/de.ts
+++ b/gui/src/strings/de.ts
@@ -164,6 +164,7 @@ export const I18N_STRINGS_DE = {
scheduler: 'Standardplaner',
server: 'API-Server',
state: 'Kundenstatus',
+ darkMode: 'Dunkelmodus',
},
sourceFilter: {
none: '',
diff --git a/gui/src/strings/en.ts b/gui/src/strings/en.ts
index a7f9a644..ee382f78 100644
--- a/gui/src/strings/en.ts
+++ b/gui/src/strings/en.ts
@@ -226,6 +226,7 @@ export const I18N_STRINGS_EN = {
scheduler: 'Default Scheduler',
server: 'API Server',
state: 'Client State',
+ darkMode: 'Dark Mode',
},
scheduler: {
'ddim': 'DDIM',
diff --git a/gui/src/strings/es.ts b/gui/src/strings/es.ts
index 1bc0de07..e46fc0a8 100644
--- a/gui/src/strings/es.ts
+++ b/gui/src/strings/es.ts
@@ -164,6 +164,7 @@ export const I18N_STRINGS_ES = {
scheduler: 'Programador predeterminado',
server: 'Servidor API',
state: 'Estado del cliente',
+ darkMode: 'Modo Oscuro',
},
sourceFilter: {
none: '',
diff --git a/gui/src/strings/fr.ts b/gui/src/strings/fr.ts
index 3d983f0a..1c6a7e80 100644
--- a/gui/src/strings/fr.ts
+++ b/gui/src/strings/fr.ts
@@ -164,6 +164,7 @@ export const I18N_STRINGS_FR = {
scheduler: '',
server: '',
state: '',
+ darkMode: 'Mode Sombre',
},
sourceFilter: {
none: '',