diff --git a/ui/src/components/JobsTable.tsx b/ui/src/components/JobsTable.tsx
index e7ae3980..7548ce8c 100644
--- a/ui/src/components/JobsTable.tsx
+++ b/ui/src/components/JobsTable.tsx
@@ -144,37 +144,37 @@ export default function JobsTable({ onlyActive = false }: JobsTableProps) {
-
{jobsDict[gpuKey].name}
+ {jobsDict[gpuKey].name}
# {queue?.gpu_ids}
{queue?.is_running ? (
<>
- Queue Running
+ Queue Running
>
) : (
<>
- Queue Stopped
+ Queue Stopped
@@ -187,7 +187,11 @@ export default function JobsTable({ onlyActive = false }: JobsTableProps) {
rows={jobsDict[gpuKey].jobs}
isLoading={isLoading}
onRefresh={refresh}
- theadClassName={queue?.is_running ? 'bg-green-950' : 'bg-red-950'}
+ theadClassName={
+ queue?.is_running
+ ? 'bg-green-700 dark:bg-green-950 text-white dark:text-gray-400'
+ : 'bg-red-700 dark:bg-red-950 text-white dark:text-gray-400'
+ }
/>
);
diff --git a/ui/src/components/Sidebar.tsx b/ui/src/components/Sidebar.tsx
index ca6a21ca..fa947100 100644
--- a/ui/src/components/Sidebar.tsx
+++ b/ui/src/components/Sidebar.tsx
@@ -1,6 +1,8 @@
import Link from 'next/link';
import { Home, Settings, BrainCircuit, Images, Plus } from 'lucide-react';
import { FaXTwitter, FaDiscord, FaYoutube } from 'react-icons/fa6';
+import ThemeToggle from './ThemeToggle';
+import ThemeLogo from './ThemeLogo';
const Sidebar = () => {
const navigation = [
@@ -19,7 +21,7 @@ const Sidebar = () => {
-
+
Ostris
AI-Toolkit
@@ -60,7 +62,7 @@ const Sidebar = () => {
{/* Social links grid */}
diff --git a/ui/src/components/ThemeLogo.tsx b/ui/src/components/ThemeLogo.tsx
new file mode 100644
index 00000000..53809fe7
--- /dev/null
+++ b/ui/src/components/ThemeLogo.tsx
@@ -0,0 +1,12 @@
+'use client';
+
+import { useTheme } from './ThemeProvider';
+
+const ThemeLogo = () => {
+ const { theme } = useTheme();
+ const src = theme === 'dark' ? '/ostris_logo.png' : '/ostris_logo_black.png';
+
+ return

;
+};
+
+export default ThemeLogo;
diff --git a/ui/src/components/ThemeProvider.tsx b/ui/src/components/ThemeProvider.tsx
index 3834f889..ffdca58a 100644
--- a/ui/src/components/ThemeProvider.tsx
+++ b/ui/src/components/ThemeProvider.tsx
@@ -2,10 +2,40 @@
import { createContext, useContext, useEffect, useState } from 'react';
-const ThemeContext = createContext({ isDark: true });
+type Theme = 'light' | 'dark';
+
+interface ThemeContextValue {
+ theme: Theme;
+ toggleTheme: () => void;
+}
+
+const ThemeContext = createContext
({
+ theme: 'dark',
+ toggleTheme: () => {},
+});
+
+export const useTheme = () => useContext(ThemeContext);
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
- const [isDark, setIsDark] = useState(true);
+ const [theme, setTheme] = useState('dark');
+ const [mounted, setMounted] = useState(false);
- return {children};
+ useEffect(() => {
+ const stored = localStorage.getItem('theme') as Theme | null;
+ const initial = stored || 'dark';
+ setTheme(initial);
+ document.documentElement.classList.toggle('dark', initial === 'dark');
+ setMounted(true);
+ }, []);
+
+ const toggleTheme = () => {
+ const next = theme === 'dark' ? 'light' : 'dark';
+ setTheme(next);
+ localStorage.setItem('theme', next);
+ document.documentElement.classList.toggle('dark', next === 'dark');
+ };
+
+ if (!mounted) return null;
+
+ return {children};
};
diff --git a/ui/src/components/ThemeToggle.tsx b/ui/src/components/ThemeToggle.tsx
new file mode 100644
index 00000000..9014abe9
--- /dev/null
+++ b/ui/src/components/ThemeToggle.tsx
@@ -0,0 +1,26 @@
+'use client';
+
+import { Moon, Sun } from 'lucide-react';
+import { useTheme } from './ThemeProvider';
+
+const ThemeToggle = () => {
+ const { theme, toggleTheme } = useTheme();
+
+ // Button styled as the opposite theme so it stands out
+ const buttonClass =
+ theme === 'dark'
+ ? 'bg-[rgb(215,215,215)] hover:bg-[rgb(195,195,195)] text-[rgb(82,82,82)]'
+ : 'bg-[rgb(23,23,23)] hover:bg-[rgb(38,38,38)] text-[rgb(163,163,163)]';
+
+ return (
+
+ );
+};
+
+export default ThemeToggle;
diff --git a/ui/src/components/formInputs.tsx b/ui/src/components/formInputs.tsx
index 46ec7918..36c2ae5f 100644
--- a/ui/src/components/formInputs.tsx
+++ b/ui/src/components/formInputs.tsx
@@ -232,7 +232,7 @@ export const Checkbox = (props: CheckboxProps) => {
onClick={() => !disabled && onChange(!checked)}
className={classNames(
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2',
- checked ? 'bg-blue-600' : 'bg-gray-700',
+ checked ? 'bg-blue-500' : 'bg-gray-600',
disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-opacity-80',
)}
>
@@ -408,7 +408,7 @@ export const SliderInput: React.FC = props => {
{/* Fill */}
diff --git a/ui/src/components/layout.tsx b/ui/src/components/layout.tsx
index c5cbef55..f8a19932 100644
--- a/ui/src/components/layout.tsx
+++ b/ui/src/components/layout.tsx
@@ -9,7 +9,7 @@ export const TopBar: React.FC = ({ children, className }) => {
return (
diff --git a/ui/tailwind.config.ts b/ui/tailwind.config.ts
index 1c76d0ff..cbd2e340 100644
--- a/ui/tailwind.config.ts
+++ b/ui/tailwind.config.ts
@@ -11,16 +11,16 @@ const config: Config = {
extend: {
colors: {
gray: {
- 950: '#0a0a0a',
- 900: '#171717',
- 800: '#262626',
- 700: '#404040',
- 600: '#525252',
- 500: '#737373',
- 400: '#a3a3a3',
- 300: '#d4d4d4',
- 200: '#e5e5e5',
- 100: '#f5f5f5',
+ 950: 'rgb(var(--gray-950) /
)',
+ 900: 'rgb(var(--gray-900) / )',
+ 800: 'rgb(var(--gray-800) / )',
+ 700: 'rgb(var(--gray-700) / )',
+ 600: 'rgb(var(--gray-600) / )',
+ 500: 'rgb(var(--gray-500) / )',
+ 400: 'rgb(var(--gray-400) / )',
+ 300: 'rgb(var(--gray-300) / )',
+ 200: 'rgb(var(--gray-200) / )',
+ 100: 'rgb(var(--gray-100) / )',
},
yellow: {
diff --git a/version.py b/version.py
index 26df0edc..a6cfa12f 100644
--- a/version.py
+++ b/version.py
@@ -1 +1 @@
-VERSION = "0.8.1"
+VERSION = "0.8.2"