Skip to content

Commit

Permalink
feat(theme): implement dark mode toggle and update theme styles acros…
Browse files Browse the repository at this point in the history
…s components
  • Loading branch information
mert-ergun committed Feb 13, 2025
1 parent d5451a8 commit d2d8a90
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 46 deletions.
43 changes: 41 additions & 2 deletions crossbar_llm/frontend/src/App.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useState, useEffect } from 'react';
import { ThemeProvider, CssBaseline, Box, Container, Tabs, Tab, Grid2, Modal, Typography, Button, IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import theme from './theme';
import Brightness4Icon from '@mui/icons-material/Brightness4';
import Brightness7Icon from '@mui/icons-material/Brightness7';
import { getTheme } from './theme';
import QueryInput from './components/QueryInput';
import ResultsDisplay from './components/ResultsDisplay';
import About from './components/About';
Expand All @@ -19,6 +21,26 @@ function App() {
const [provider, setProvider] = useState('');
const [llmType, setLlmType] = useState('');
const [apiKey, setApiKey] = useState('');
const [mode, setMode] = useState(() => {
const savedMode = localStorage.getItem('theme-mode');
if (savedMode) {
return savedMode;
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
});

const theme = React.useMemo(() => getTheme(mode), [mode]);

useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = (e) => {
if (!localStorage.getItem('theme-mode')) {
setMode(e.matches ? 'dark' : 'light');
}
};
mediaQuery.addListener(handleChange);
return () => mediaQuery.removeListener(handleChange);
}, []);

useEffect(() => {
axios.get('/csrf-token/', { withCredentials: true })
Expand Down Expand Up @@ -60,10 +82,27 @@ function App() {
setSelectedQuery(query);
}

const toggleColorMode = () => {
const newMode = mode === 'light' ? 'dark' : 'light';
setMode(newMode);
localStorage.setItem('theme-mode', newMode);
};

return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Box sx={{ minHeight: '100vh', backgroundColor: 'background.default', mt: { xs: 4, sm: 6, md: 10 }, mb: { xs: 4, sm: 6, md: 10 } }}>
<Box sx={{ minHeight: '100vh', backgroundColor: 'background.default', mt: { xs: 4, sm: 6, md: 10 }, mb: { xs: 4, sm: 6, md: 10 }, position: 'relative' }}>
<IconButton
onClick={toggleColorMode}
sx={{
position: 'absolute',
right: 16,
top: 16,
color: 'text.primary'
}}
>
{mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
</IconButton>
<Container maxWidth="lg" sx={{ px: { xs: 2, sm: 3, md: 4 } }}>
<Typography
variant="h2"
Expand Down
2 changes: 2 additions & 0 deletions crossbar_llm/frontend/src/components/LatestQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ function LatestQueries({ queries, onSelectQuery }) {
onRowClick={handleRowClick}
rowsPerPageOptions={[5]}
disableSelectionOnClick
getRowHeight={() => 'auto'}
sx={{ '& .MuiDataGrid-cell': { borderRight: '1px solid rgba(0, 0, 0, 0.12)' } }}
/>
</div>
);
Expand Down
8 changes: 5 additions & 3 deletions crossbar_llm/frontend/src/components/QueryInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
FormControlLabel,
Checkbox,
CircularProgress,
useTheme,
} from '@mui/material';
import AutocompleteTextField from './AutocompleteTextField';
import axios from '../services/api';
Expand Down Expand Up @@ -38,7 +39,7 @@ function QueryInput({
const [showWarning, setShowWarning] = useState(false);
const [realtimeLogs, setRealtimeLogs] = useState('');
const eventSourceRef = useRef(null);

const theme = useTheme();

const modelChoices = {
OpenAI: [
Expand Down Expand Up @@ -425,20 +426,21 @@ function QueryInput({
{logs && (
<Box
sx={{
backgroundColor: '#2d2d2d',
backgroundColor: theme.palette.mode === 'dark' ? '#1e1e1e' : '#f5f5f5',
padding: 2,
borderRadius: 1,
overflow: 'auto',
maxHeight: 200,
mt: 2,
border: `1px solid ${theme.palette.divider}`
}}
>
<pre
style={{
margin: 0,
fontFamily: 'monospace',
fontSize: 12,
color: '#cccccc',
color: theme.palette.text.primary,
}}
>
{logs}
Expand Down
45 changes: 33 additions & 12 deletions crossbar_llm/frontend/src/components/ResultsDisplay.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from 'react';
import { Typography, Card, CardContent, Box } from '@mui/material';
import { Typography, Card, CardContent, Box, useTheme } from '@mui/material';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { dracula } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import { docco, dracula } from 'react-syntax-highlighter/dist/esm/styles/hljs';

function ResultsDisplay({ queryResult, executionResult, realtimeLogs }) {
const theme = useTheme();
const syntaxTheme = theme.palette.mode === 'dark' ? dracula : docco;

if (!queryResult && !executionResult) {
return null;
}
Expand All @@ -14,7 +17,13 @@ function ResultsDisplay({ queryResult, executionResult, realtimeLogs }) {
<Card sx={{ mb: 2 }}>
<CardContent>
<Typography variant="h6">Generated Cypher Query:</Typography>
<SyntaxHighlighter language="cypher" style={dracula}>
<SyntaxHighlighter
language="cypher"
style={syntaxTheme}
customStyle={{
backgroundColor: theme.palette.mode === 'dark' ? '#1e1e1e' : '#f5f5f5'
}}
>
{queryResult}
</SyntaxHighlighter>
</CardContent>
Expand All @@ -25,14 +34,20 @@ function ResultsDisplay({ queryResult, executionResult, realtimeLogs }) {
<CardContent>
<Typography variant="h6">Results:</Typography>
{realtimeLogs && (
<Card sx={{ mb: 2 }}>
<CardContent>
<Typography variant="h6">Real-time Logs:</Typography>
<SyntaxHighlighter language="plaintext" style={dracula}>
{realtimeLogs}
</SyntaxHighlighter>
</CardContent>
</Card>
<Card sx={{ mb: 2 }}>
<CardContent>
<Typography variant="h6">Real-time Logs:</Typography>
<SyntaxHighlighter
language="plaintext"
style={syntaxTheme}
customStyle={{
backgroundColor: theme.palette.mode === 'dark' ? '#1e1e1e' : '#f5f5f5'
}}
>
{realtimeLogs}
</SyntaxHighlighter>
</CardContent>
</Card>
)}
<Typography variant="body1" sx={{ mt: 2, fontWeight: 'bold' }}>
Natural Language Response:
Expand All @@ -44,7 +59,13 @@ function ResultsDisplay({ queryResult, executionResult, realtimeLogs }) {
<Typography variant="subtitle1" sx={{ mt: 2 }}>
Raw Query Output:
</Typography>
<SyntaxHighlighter language="json" style={dracula}>
<SyntaxHighlighter
language="json"
style={syntaxTheme}
customStyle={{
backgroundColor: theme.palette.mode === 'dark' ? '#1e1e1e' : '#f5f5f5'
}}
>
{JSON.stringify(executionResult.result, null, 2)}
</SyntaxHighlighter>
</CardContent>
Expand Down
11 changes: 4 additions & 7 deletions crossbar_llm/frontend/src/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import ReactDOM from 'react-dom/client';
import App from './App';
import theme from './theme';
import { ThemeProvider, CssBaseline } from '@mui/material';
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ThemeProvider theme={theme}>
<CssBaseline />
<React.StrictMode>
<App />
</ThemeProvider>,
document.getElementById('root')
</React.StrictMode>
);
42 changes: 20 additions & 22 deletions crossbar_llm/frontend/src/theme.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
import { createTheme } from '@mui/material/styles';

const theme = createTheme({
export const getTheme = (mode) => createTheme({
palette: {
mode: 'light',
mode,
primary: {
main: '#1976d2',
main: '#1976d2',
},
secondary: {
main: '#ffffff',
main: mode === 'dark' ? '#333333' : '#ffffff',
},
background: {
default: '#f5f5f5',
paper: '#ffffff',
default: mode === 'dark' ? '#121212' : '#f5f5f5',
paper: mode === 'dark' ? '#1e1e1e' : '#ffffff',
},
text: {
primary: '#000000',
secondary: '#555555',
primary: mode === 'dark' ? '#ffffff' : '#000000',
secondary: mode === 'dark' ? '#a0a0a0' : '#555555',
},
},
typography: {
fontFamily: 'Inter, sans-serif',
fontFamily: 'Inter, sans-serif',
h5: {
fontWeight: 600,
fontWeight: 600,
},
subtitle1: {
color: '#888888',
color: mode === 'dark' ? '#888888' : '#666666',
},
},
components: {
MuiButton: {
styleOverrides: {
root: {
borderRadius: '8px',
textTransform: 'none',
boxShadow: 'none',
borderRadius: '8px',
textTransform: 'none',
boxShadow: 'none',
},
containedPrimary: {
backgroundColor: '#000000',
color: '#ffffff',
backgroundColor: mode === 'dark' ? '#1976d2' : '#000000',
color: '#ffffff',
'&:hover': {
backgroundColor: '#333333',
backgroundColor: mode === 'dark' ? '#1565c0' : '#333333',
},
},
},
},
MuiTextField: {
styleOverrides: {
root: {
backgroundColor: '#ffffff',
backgroundColor: mode === 'dark' ? '#1e1e1e' : '#ffffff',
borderRadius: '8px',
},
},
Expand All @@ -59,12 +59,10 @@ const theme = createTheme({
MuiSelect: {
styleOverrides: {
select: {
backgroundColor: '#ffffff',
backgroundColor: mode === 'dark' ? '#1e1e1e' : '#ffffff',
borderRadius: '8px',
},
},
},
},
});

export default theme;
});

0 comments on commit d2d8a90

Please sign in to comment.