update visual design system
This commit is contained in:
115
src/About.js
115
src/About.js
@@ -2,35 +2,51 @@ import React from 'react';
|
||||
import { Typography, Box, Paper, Divider, Link, Grid, Card, CardContent, List, ListItem, ListItemIcon, ListItemText, Stack, Chip, Button } from '@mui/material';
|
||||
import packageInfo from '../package.json';
|
||||
import vimdronesLogo from './image/vimdrones_logo.png';
|
||||
import discordLogo from './image/discord_logo.png'; // Import Discord logo
|
||||
import discordLogo from './image/discord_logo.png';
|
||||
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
|
||||
import UsbIcon from '@mui/icons-material/Usb';
|
||||
import WifiIcon from '@mui/icons-material/Wifi';
|
||||
import ComputerIcon from '@mui/icons-material/Computer';
|
||||
|
||||
// Remove the DiscordIcon import since we're now using a custom image
|
||||
|
||||
const About = () => {
|
||||
return (
|
||||
<Box sx={{ mx: 'auto', p: 2, width: '100%', flexGrow: 1, display: 'flex', flexDirection: 'column', gap: 1 }} component={Paper} elevation={2}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '100%', mb: 1 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'baseline' }}>
|
||||
<Typography variant="h4" component="h1" sx={{ fontWeight: 600 }}>
|
||||
<Box
|
||||
component={Paper}
|
||||
elevation={0}
|
||||
sx={{
|
||||
mx: 'auto',
|
||||
p: 3,
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
backgroundColor: 'background.paper',
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
borderRadius: 2,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '100%', mb: 0.5 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'baseline', gap: 1 }}>
|
||||
<Typography variant="h4" component="h1" sx={{ fontWeight: 400, letterSpacing: '-0.03em' }}>
|
||||
DroneCAN Web Tools
|
||||
</Typography>
|
||||
<Chip
|
||||
<Chip
|
||||
label={`v${packageInfo.version}`}
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
sx={{ ml: 2 }}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant="body2" sx={{ mt: 1, color: 'text.secondary', textAlign: 'center', maxWidth: 720 }}>
|
||||
A browser-based console for discovery, parameters, firmware, monitoring, and panel workflows.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Card variant="outlined">
|
||||
<Card variant="outlined" sx={{ backgroundColor: 'background.default' }}>
|
||||
<CardContent sx={{ pb: 1, '&:last-child': { pb: 1 } }}>
|
||||
<Typography variant="h6" sx={{ mb: 1.5, fontWeight: 500, color: 'primary.main' }}>Key Features</Typography>
|
||||
<Typography variant="h6" sx={{ mb: 1.5, fontWeight: 600, color: 'primary.main' }}>Key Features</Typography>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<List dense disablePadding>
|
||||
@@ -59,22 +75,22 @@ const About = () => {
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
<Stack spacing={2} direction={{ xs: 'column', md: 'row' }}>
|
||||
<Card variant="outlined" sx={{ flex: 1 }}>
|
||||
<Card variant="outlined" sx={{ flex: 1, backgroundColor: 'background.paper' }}>
|
||||
<CardContent sx={{ pb: 1, '&:last-child': { pb: 1 } }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||
<WifiIcon color="primary" sx={{ mr: 1 }} />
|
||||
<Typography variant="h6" sx={{ fontWeight: 600 }}>WebSocket via MAVProxy</Typography>
|
||||
</Box>
|
||||
<Typography variant="body2" sx={{ bgcolor: 'action.hover', p: 1, borderRadius: 1, fontFamily: 'monospace' }}>
|
||||
<Typography variant="body2" sx={{ bgcolor: 'background.default', p: 1.25, borderRadius: 1, fontFamily: 'monospace', border: '1px solid', borderColor: 'divider' }}>
|
||||
mavproxy.py --master /dev/tty.usbmodem111401,115200 --out wsserver:127.0.0.1:5555
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ mt: 1, color: 'text.secondary' }}>
|
||||
Connect to <Box component="span" sx={{ fontWeight: 'bold' }}>ws://127.0.0.1:5555</Box> in the Adapter Settings
|
||||
Connect to <Box component="span" sx={{ fontWeight: 'bold' }}>ws://127.0.0.1:5555</Box> in the Adapter Settings
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ mt: 2, pt: 1, borderTop: '1px dashed', borderColor: 'divider' }}>
|
||||
|
||||
<Box sx={{ mt: 2, pt: 1, borderTop: '1px solid', borderColor: 'divider' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 0.5 }}>
|
||||
<ComputerIcon color="info" sx={{ mr: 1, fontSize: '1rem' }} />
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
||||
@@ -87,8 +103,8 @@ const About = () => {
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card variant="outlined" sx={{ flex: 1 }}>
|
||||
|
||||
<Card variant="outlined" sx={{ flex: 1, backgroundColor: 'background.paper' }}>
|
||||
<CardContent sx={{ pb: 1, '&:last-child': { pb: 1 } }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||
<UsbIcon color="primary" sx={{ mr: 1 }} />
|
||||
@@ -109,23 +125,23 @@ const About = () => {
|
||||
</ListItem>
|
||||
<ListItem disablePadding sx={{ py: 0.25 }}>
|
||||
<ListItemIcon sx={{ minWidth: 30 }}>
|
||||
<CheckCircleOutlineIcon color="success" fontSize="small" /> {/* Changed from info to success */}
|
||||
<CheckCircleOutlineIcon color="success" fontSize="small" />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Standalone MAVCAN USB Adaptor"
|
||||
<ListItemText
|
||||
primary="Standalone MAVCAN USB Adaptor"
|
||||
secondary={
|
||||
<Box component="span">
|
||||
<Link
|
||||
<Link
|
||||
href="https://github.com/VimDrones/MAVCAN_Bridge"
|
||||
target="_blank"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
sx={{ fontSize: '0.75rem' }}
|
||||
>
|
||||
MAVCAN Bridge
|
||||
</Link>
|
||||
<Typography
|
||||
component="span"
|
||||
variant="caption"
|
||||
<Typography
|
||||
component="span"
|
||||
variant="caption"
|
||||
sx={{ ml: 0.5, fontSize: '0.75rem', color: 'text.secondary' }}
|
||||
>
|
||||
from Vimdrones
|
||||
@@ -138,22 +154,21 @@ const About = () => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Stack>
|
||||
|
||||
{/* Footer */}
|
||||
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end'}}>
|
||||
|
||||
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
|
||||
<Divider sx={{ my: 1.5 }} />
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', sm: 'row' },
|
||||
gap: 1,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: { xs: 'flex-start', sm: 'center' }
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', sm: 'row' },
|
||||
gap: 1,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: { xs: 'flex-start', sm: 'center' }
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, gap: 1, mr: 5, alignItems: { xs: 'flex-start', sm: 'center' } }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Author: <Link href="https://github.com/huibean" target="_blank" rel="noopener" sx={{ fontWeight: 500 }}>Huibean Luo</Link>
|
||||
</Typography>
|
||||
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
@@ -161,7 +176,7 @@ const About = () => {
|
||||
href="https://discord.gg/xxCKsZXU4K"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
sx={{
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
textTransform: 'none',
|
||||
fontSize: '0.75rem',
|
||||
@@ -194,10 +209,10 @@ const About = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<img
|
||||
<img
|
||||
style={{borderRadius: '50%'}}
|
||||
src={discordLogo}
|
||||
alt="Discord"
|
||||
src={discordLogo}
|
||||
alt="Discord"
|
||||
/>
|
||||
</Box>
|
||||
Vimdrones
|
||||
@@ -210,7 +225,7 @@ const About = () => {
|
||||
href="https://discord.gg/vz7a499KXN"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
sx={{
|
||||
sx={{
|
||||
borderRadius: 4,
|
||||
textTransform: 'none',
|
||||
fontSize: '0.75rem',
|
||||
@@ -243,24 +258,24 @@ const About = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<img
|
||||
<img
|
||||
style={{borderRadius: '50%'}}
|
||||
src={discordLogo}
|
||||
alt="Discord"
|
||||
src={discordLogo}
|
||||
alt="Discord"
|
||||
/>
|
||||
</Box>
|
||||
DroneCAN
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mr: 1 }}>
|
||||
Sponsored by
|
||||
</Typography>
|
||||
<Link href="https://dev.vimdrones.com" target="_blank" rel="noopener">
|
||||
<img
|
||||
src={vimdronesLogo}
|
||||
alt="Vimdrones"
|
||||
<img
|
||||
src={vimdronesLogo}
|
||||
alt="Vimdrones"
|
||||
style={{ height: '30px' }}
|
||||
/>
|
||||
</Link>
|
||||
@@ -271,4 +286,4 @@ const About = () => {
|
||||
);
|
||||
}
|
||||
|
||||
export default About;
|
||||
export default About;
|
||||
|
||||
107
src/App.js
107
src/App.js
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { AppBar, Toolbar, Typography, Box, Button, ThemeProvider, IconButton, Snackbar, Alert, Tooltip, FormControl, Select, MenuItem, InputLabel, Chip } from '@mui/material';
|
||||
import { AppBar, Toolbar, Typography, Box, Button, ThemeProvider, IconButton, Snackbar, Alert, Tooltip, FormControl, Select, MenuItem } from '@mui/material';
|
||||
import MavlinkSession from './mavlink_session';
|
||||
import dronecan from './dronecan';
|
||||
import theme from './theme';
|
||||
@@ -97,7 +97,7 @@ const AppContent = () => {
|
||||
const newBus = event.target.value;
|
||||
if (newBus === selectedBus) return;
|
||||
setSelectedBus(newBus);
|
||||
|
||||
|
||||
if (window.localNode) {
|
||||
window.localNode.changeBus(newBus);
|
||||
showMessage(t('app.bus_switched', { bus: newBus }), 'info');
|
||||
@@ -140,40 +140,43 @@ const AppContent = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const surfaceBorder = '1px solid rgba(230, 223, 216, 0.95)';
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBar position="static">
|
||||
<Toolbar variant="dense" sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Box sx={{width: '30%', flexGrow: 1, display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center'}}>
|
||||
<ToolsMenu openWindow={openWindow.bind(this)} />
|
||||
<PanelsMenu openWindow={openWindow.bind(this)} />
|
||||
<AppBar position="static" color="transparent" elevation={0} sx={{ borderBottom: surfaceBorder }}>
|
||||
<Toolbar variant="dense" sx={{ display: 'flex', justifyContent: 'space-between', minHeight: 64, px: 1.5, gap: 1 }}>
|
||||
<Box sx={{ width: '30%', flexGrow: 1, display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center' }}>
|
||||
<ToolsMenu openWindow={openWindow} />
|
||||
<PanelsMenu openWindow={openWindow} />
|
||||
</Box>
|
||||
<Box sx={{flexGrow: 2, display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center'}}>
|
||||
<Box sx={{display: 'flex', flexDirection: 'row', alignItems: 'center'}} ml={0.5} mr={0.5}>
|
||||
<a href="https://dev.vimdrones.com" target="_blank" rel="noreferrer" style={{height: 30}}>
|
||||
<img src={DronecanLogo} alt="DroneCAN" style={{ height: 30}} />
|
||||
<Box sx={{ flexGrow: 2, display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', gap: 1 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<a href="https://dev.vimdrones.com" target="_blank" rel="noreferrer" style={{ height: 30, display: 'flex', alignItems: 'center' }}>
|
||||
<img src={DronecanLogo} alt="DroneCAN" style={{ height: 30 }} />
|
||||
</a>
|
||||
</Box>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="body2" sx={{ fontWeight: 600, letterSpacing: 0.2 }}>
|
||||
{t('app.title')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{width: '30%', flexGrow: 1, display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', alignItems: 'center', gap: 1}}>
|
||||
<ConnectionIndicators
|
||||
<Box sx={{ width: '30%', flexGrow: 1, display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', alignItems: 'center', gap: 1 }}>
|
||||
<ConnectionIndicators
|
||||
isConnected={isConnected}
|
||||
mavlinkSession={window.mavlinkSession}
|
||||
localNode={window.localNode}
|
||||
/>
|
||||
|
||||
<FormControl
|
||||
size="small"
|
||||
sx={{
|
||||
minWidth: 80,
|
||||
|
||||
<FormControl
|
||||
size="small"
|
||||
sx={{
|
||||
minWidth: 92,
|
||||
'& .MuiOutlinedInput-root': {
|
||||
height: 30,
|
||||
height: 32,
|
||||
backgroundColor: '#faf9f5',
|
||||
},
|
||||
'& .MuiSelect-select': {
|
||||
paddingTop: 0.5,
|
||||
paddingTop: 0.5,
|
||||
paddingBottom: 0.5,
|
||||
fontSize: '0.8rem'
|
||||
}
|
||||
@@ -195,11 +198,11 @@ const AppContent = () => {
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
color={dnaServerActive ? "success" : "primary"}
|
||||
<Button
|
||||
variant="outlined"
|
||||
color={dnaServerActive ? 'success' : 'primary'}
|
||||
startIcon={
|
||||
dnaServerActive ?
|
||||
dnaServerActive ?
|
||||
<DnsIcon sx={{
|
||||
animation: 'pulse 1.5s infinite',
|
||||
'@keyframes pulse': {
|
||||
@@ -207,27 +210,27 @@ const AppContent = () => {
|
||||
'50%': { opacity: 1 },
|
||||
'100%': { opacity: 0.6 },
|
||||
}
|
||||
}} /> :
|
||||
}} /> :
|
||||
<DnsIcon />
|
||||
}
|
||||
onClick={handleToggleDnaServer}
|
||||
sx={dnaServerActive ? {
|
||||
borderColor: 'success.main',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(76, 175, 80, 0.08)',
|
||||
backgroundColor: 'rgba(93, 184, 114, 0.08)',
|
||||
borderColor: 'success.dark'
|
||||
}
|
||||
} : {}}
|
||||
>
|
||||
{t('app.dna')}
|
||||
</Button>
|
||||
|
||||
|
||||
<Tooltip title={language === 'en' ? '中文' : 'English'}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => setLanguage(language === 'en' ? 'zh' : 'en')}
|
||||
sx={{ mr: 0.5 }}
|
||||
sx={{ mr: 0.5, border: '1px solid rgba(230, 223, 216, 0.95)', backgroundColor: '#faf9f5' }}
|
||||
>
|
||||
<LanguageIcon fontSize="small" />
|
||||
</IconButton>
|
||||
@@ -244,47 +247,47 @@ const AppContent = () => {
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', flexGrow: 1, height: '95vh', gap: 0.5, p: 1 }}>
|
||||
<CompactSidebar
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', flexGrow: 1, minHeight: 'calc(100vh - 64px)', gap: 0.75, p: 1, backgroundColor: '#faf9f5' }}>
|
||||
<CompactSidebar
|
||||
nodes={nodes}
|
||||
selectedNodeId={selectedNodeId}
|
||||
setSelectedNodeId={setSelectedNodeId}
|
||||
nodesUpdateTimestamp={nodesUpdateTimestamp}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
minWidth: 550,
|
||||
display: { xs: 'none', md: 'flex' },
|
||||
flexDirection: 'column',
|
||||
gap: 0.5,
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
minWidth: 550,
|
||||
display: { xs: 'none', md: 'flex' },
|
||||
flexDirection: 'column',
|
||||
gap: 0.75,
|
||||
}}
|
||||
>
|
||||
<NodeList
|
||||
nodes={nodes}
|
||||
selectedNodeId={selectedNodeId}
|
||||
setSelectedNodeId={setSelectedNodeId.bind(this)}
|
||||
setSelectedNodeId={setSelectedNodeId}
|
||||
nodesUpdateTimestamp={nodesUpdateTimestamp}
|
||||
/>
|
||||
<NodeLogs/>
|
||||
<NodeLogs/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
sx={{
|
||||
gap: 0.5,
|
||||
gap: 0.75,
|
||||
ml: { xs: 1, md: 0 },
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
{selectedNodeId && (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1, gap: 0.5 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1, gap: 0.75 }}>
|
||||
<NodeProperties
|
||||
nodeId={selectedNodeId}
|
||||
nodes={nodes}
|
||||
multiNodeEditorEnable={multiNodeEditorEnable}
|
||||
setMultiNodeEditorEnable={setMultiNodeEditorEnable.bind(this)}
|
||||
setMultiNodeEditorEnable={setMultiNodeEditorEnable}
|
||||
nodesUpdateTimestamp={nodesUpdateTimestamp}
|
||||
/>
|
||||
<NodeParam
|
||||
@@ -304,17 +307,17 @@ const AppContent = () => {
|
||||
open={modalOpen}
|
||||
onClose={handleCloseModal}
|
||||
onConnectionStatusChange={handleConnectionStatusChange}
|
||||
showMessage={showMessage.bind(this)}
|
||||
showMessage={showMessage}
|
||||
selectedBus={selectedBus}
|
||||
onBusChange={handleBusChange}
|
||||
/>
|
||||
<Snackbar
|
||||
open={snackbarOpen}
|
||||
autoHideDuration={6000}
|
||||
open={snackbarOpen}
|
||||
autoHideDuration={6000}
|
||||
onClose={() => setSnackbarOpen(false)}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
>
|
||||
<Alert onClose={() => setSnackbarOpen(false)} severity={snackbarSeverity}>
|
||||
<Alert onClose={() => setSnackbarOpen(false)} severity={snackbarSeverity} variant="filled">
|
||||
{snackbarMessage}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
@@ -322,4 +325,4 @@ const AppContent = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
||||
@@ -2,26 +2,22 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Box, Badge, Tooltip, CircularProgress, Typography } from '@mui/material';
|
||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||
|
||||
const CompactSidebar = ({
|
||||
nodes,
|
||||
selectedNodeId,
|
||||
setSelectedNodeId
|
||||
const CompactSidebar = ({
|
||||
nodes,
|
||||
selectedNodeId,
|
||||
setSelectedNodeId
|
||||
}) => {
|
||||
const [logCounts, setLogCounts] = useState({});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Function to get log counts from the window.localNode events
|
||||
const updateLogCounts = () => {
|
||||
const counts = {};
|
||||
|
||||
// Initialize counts for all nodes to 0
|
||||
|
||||
Object.keys(nodes).forEach(nodeId => {
|
||||
counts[nodeId] = 0;
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
// Access logs if they're stored somewhere else in the app
|
||||
// Option 1: If logs are stored in a global state/context
|
||||
if (window.dronecanLogs && Array.isArray(window.dronecanLogs)) {
|
||||
window.dronecanLogs.forEach(log => {
|
||||
if (log.id) {
|
||||
@@ -29,63 +25,54 @@ const CompactSidebar = ({
|
||||
}
|
||||
});
|
||||
}
|
||||
// Option 2: Listen for log events and maintain our own count
|
||||
// This is already set up in the useEffect for event listening below
|
||||
} catch (error) {
|
||||
console.error("Error accessing logs:", error);
|
||||
}
|
||||
|
||||
|
||||
setLogCounts(counts);
|
||||
};
|
||||
|
||||
// Create log listener and counter
|
||||
|
||||
let lastLogCounts = {};
|
||||
|
||||
|
||||
const handleLog = (transfer) => {
|
||||
const sourceNodeId = transfer.sourceNodeId;
|
||||
if (sourceNodeId) {
|
||||
// Update count for this node
|
||||
lastLogCounts[sourceNodeId] = (lastLogCounts[sourceNodeId] || 0) + 1;
|
||||
setLogCounts({...lastLogCounts});
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for log messages
|
||||
|
||||
const localNode = window.localNode;
|
||||
if (localNode) {
|
||||
localNode.on('uavcan.protocol.debug.LogMessage', handleLog);
|
||||
}
|
||||
|
||||
// Initial update
|
||||
|
||||
updateLogCounts();
|
||||
|
||||
|
||||
return () => {
|
||||
// Remove event listener on cleanup
|
||||
if (localNode) {
|
||||
localNode.off('uavcan.protocol.debug.LogMessage', handleLog);
|
||||
}
|
||||
};
|
||||
}, [nodes]);
|
||||
|
||||
// Get color based on node mode (matching the same logic as NodeList)
|
||||
|
||||
const getModeColor = (mode) => {
|
||||
switch (mode) {
|
||||
case 'OPERATIONAL':
|
||||
return '#f5f5f5'; // Light gray for operational (subtle)
|
||||
return '#5db872';
|
||||
case 'INITIALIZATION':
|
||||
return '#ffb74d'; // Warning color
|
||||
return '#d4a017';
|
||||
case 'MAINTENANCE':
|
||||
return '#9c27b0'; // Secondary/purple
|
||||
return '#5db8a6';
|
||||
case 'SOFTWARE_UPDATE':
|
||||
return '#4caf50'; // Success/green
|
||||
return '#5db872';
|
||||
case 'OFFLINE':
|
||||
return '#f44336'; // Error/red
|
||||
return '#c64545';
|
||||
default:
|
||||
return '#f44336'; // Default to error color
|
||||
return '#c64545';
|
||||
}
|
||||
};
|
||||
|
||||
// Handle click on a node
|
||||
|
||||
const handleNodeClick = (nodeId) => {
|
||||
if (nodeId === selectedNodeId) {
|
||||
setSelectedNodeId(null);
|
||||
@@ -93,10 +80,10 @@ const CompactSidebar = ({
|
||||
setSelectedNodeId(Number(nodeId));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'flex', md: 'none', alignItems: 'center' },
|
||||
flexDirection: 'column',
|
||||
width: '60px',
|
||||
@@ -105,17 +92,19 @@ const CompactSidebar = ({
|
||||
p: 1,
|
||||
gap: 1,
|
||||
overflowY: 'auto',
|
||||
height: '100%'
|
||||
height: '100%',
|
||||
backgroundColor: 'background.paper',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
display: 'block',
|
||||
mb: 0.5,
|
||||
fontWeight: 'bold',
|
||||
fontSize: '0.5rem'
|
||||
fontWeight: 700,
|
||||
fontSize: '0.5rem',
|
||||
color: 'text.secondary',
|
||||
}}
|
||||
>
|
||||
NODES
|
||||
@@ -130,11 +119,12 @@ const CompactSidebar = ({
|
||||
const node = nodes[nodeId];
|
||||
const mode = node.status.getConstant('mode');
|
||||
const logCount = logCounts[nodeId] || 0;
|
||||
|
||||
const selected = selectedNodeId === Number(nodeId);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
<Tooltip
|
||||
key={nodeId}
|
||||
title={`${node.name || 'Unknown'} (${mode})`}
|
||||
title={`${node.name || 'Unknown'} (${mode})`}
|
||||
placement="right"
|
||||
>
|
||||
<Box
|
||||
@@ -148,25 +138,25 @@ const CompactSidebar = ({
|
||||
justifyContent: 'center',
|
||||
borderRadius: 1,
|
||||
border: '1px solid',
|
||||
borderColor: selectedNodeId === Number(nodeId) ? 'primary.main' : 'divider',
|
||||
backgroundColor: 'background.paper',
|
||||
borderColor: selected ? 'primary.main' : 'divider',
|
||||
backgroundColor: selected ? 'rgba(204, 120, 92, 0.08)' : 'background.default',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
backgroundColor: 'action.hover',
|
||||
backgroundColor: 'rgba(245, 240, 232, 0.9)',
|
||||
}
|
||||
}}
|
||||
onClick={() => handleNodeClick(Number(nodeId))}
|
||||
>
|
||||
{/* Make NID larger and more prominent */}
|
||||
<Typography
|
||||
<Typography
|
||||
variant='caption'
|
||||
sx={{
|
||||
color: getModeColor(mode),
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{nodeId}
|
||||
</Typography>
|
||||
|
||||
|
||||
{logCount > 0 && (
|
||||
<Badge
|
||||
badgeContent={logCount > 99 ? '99+' : logCount}
|
||||
@@ -190,4 +180,4 @@ const CompactSidebar = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default CompactSidebar;
|
||||
export default CompactSidebar;
|
||||
|
||||
@@ -7,29 +7,29 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
||||
const [rxActive, setRxActive] = useState(false);
|
||||
const txTimeout = useRef(null);
|
||||
const rxTimeout = useRef(null);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const handleFrameSend = () => {
|
||||
setTxActive(true);
|
||||
clearTimeout(txTimeout.current);
|
||||
txTimeout.current = setTimeout(() => {
|
||||
setTxActive(false);
|
||||
}, 100); // Blink for 200ms
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
const handleFrameReceive = () => {
|
||||
setRxActive(true);
|
||||
clearTimeout(rxTimeout.current);
|
||||
rxTimeout.current = setTimeout(() => {
|
||||
setRxActive(false);
|
||||
}, 100); // Blink for 200ms
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
if (mavlinkSession) {
|
||||
mavlinkSession.on('mav-tx', handleFrameSend);
|
||||
mavlinkSession.on('mav-rx', handleFrameReceive);
|
||||
}
|
||||
|
||||
|
||||
return () => {
|
||||
if (mavlinkSession) {
|
||||
mavlinkSession.removeListener('mav-tx', handleFrameSend);
|
||||
@@ -39,14 +39,14 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
||||
clearTimeout(rxTimeout.current);
|
||||
};
|
||||
}, [localNode, mavlinkSession]);
|
||||
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mr: 5 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mr: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', opacity: isConnected ? 1 : 0.3 }}>
|
||||
<CircleIcon
|
||||
fontSize="small"
|
||||
sx={{
|
||||
color: txActive && isConnected ? '#4caf50' : '#7e7e7e',
|
||||
<CircleIcon
|
||||
fontSize="small"
|
||||
sx={{
|
||||
color: txActive && isConnected ? '#5db872' : '#8e8b82',
|
||||
width: '12px',
|
||||
height: '12px',
|
||||
transition: 'color 0.1s ease'
|
||||
@@ -54,12 +54,12 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
||||
/>
|
||||
<Typography variant="caption" sx={{ ml: 0.5, color: 'text.secondary' }}>TX</Typography>
|
||||
</Box>
|
||||
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', opacity: isConnected ? 1 : 0.3 }}>
|
||||
<CircleIcon
|
||||
<CircleIcon
|
||||
fontSize="small"
|
||||
sx={{
|
||||
color: rxActive && isConnected ? '#2196f3' : '#7e7e7e',
|
||||
sx={{
|
||||
color: rxActive && isConnected ? '#5db8a6' : '#8e8b82',
|
||||
width: '12px',
|
||||
height: '12px',
|
||||
transition: 'color 0.1s ease'
|
||||
@@ -71,4 +71,4 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectionIndicators;
|
||||
export default ConnectionIndicators;
|
||||
|
||||
@@ -29,16 +29,27 @@ const NodeList = ({ nodes, selectedNodeId, setSelectedNodeId }) => {
|
||||
};
|
||||
|
||||
const renderNodeRow = (key) => {
|
||||
let node = nodes[key];
|
||||
let status = node.status;
|
||||
let health = status.getConstant('health');
|
||||
let mode = status.getConstant('mode');
|
||||
const node = nodes[key];
|
||||
const status = node.status;
|
||||
const health = status.getConstant('health');
|
||||
const mode = status.getConstant('mode');
|
||||
const isSelected = Number(key) === Number(selectedNodeId);
|
||||
return (
|
||||
<TableRow key={key} onClick={() => handleRowClick((Number(key)))} style={{ cursor: 'pointer' }}>
|
||||
<TableRow
|
||||
key={key}
|
||||
onClick={() => handleRowClick(Number(key))}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
backgroundColor: isSelected ? 'rgba(204, 120, 92, 0.08)' : 'transparent',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(245, 240, 232, 0.9)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TableCell>{key}</TableCell>
|
||||
<TableCell>{node.name}</TableCell>
|
||||
<TableCell sx={{ width: 150 }}>{node.name}</TableCell>
|
||||
<TableCell>{health}</TableCell>
|
||||
<TableCell sx={{bgcolor: getModeColor(mode)}}>{mode}</TableCell>
|
||||
<TableCell sx={{ bgcolor: getModeColor(mode), color: getModeColor(mode) ? 'common.white' : 'inherit' }}>{mode}</TableCell>
|
||||
<TableCell>{secondsToTime(status.uptime_sec)}</TableCell>
|
||||
<TableCell>{node.status.vendor_specific_status_code}</TableCell>
|
||||
</TableRow>
|
||||
@@ -48,25 +59,17 @@ const NodeList = ({ nodes, selectedNodeId, setSelectedNodeId }) => {
|
||||
return (
|
||||
<Box
|
||||
component={Paper}
|
||||
sx={{display: 'flex', flexDirection: 'column', flexGrow: 1, height: '50%'}}
|
||||
sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1, height: '50%', backgroundColor: 'background.paper', border: '1px solid', borderColor: 'divider' }}
|
||||
>
|
||||
<Box margin={1} sx={{height: 20}}>
|
||||
<Typography variant="caption">Online Nodes</Typography>
|
||||
<Box margin={1} sx={{ height: 20 }}>
|
||||
<Typography variant="caption" sx={{ color: 'text.secondary', fontWeight: 600, letterSpacing: 0.2 }}>Online Nodes</Typography>
|
||||
</Box>
|
||||
<TableContainer
|
||||
sx={{ overflow: 'auto' }}
|
||||
>
|
||||
<TableContainer sx={{ overflow: 'auto' }}>
|
||||
<Table stickyHeader size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>NID</TableCell>
|
||||
<TableCell
|
||||
sx={{
|
||||
width: 150,
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Health</TableCell>
|
||||
<TableCell>Mode</TableCell>
|
||||
<TableCell>Uptime</TableCell>
|
||||
@@ -74,9 +77,7 @@ const NodeList = ({ nodes, selectedNodeId, setSelectedNodeId }) => {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{Object.keys(nodes).map((key) => (
|
||||
renderNodeRow(key)
|
||||
))}
|
||||
{Object.keys(nodes).map((key) => renderNodeRow(key))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
@@ -84,4 +85,4 @@ const NodeList = ({ nodes, selectedNodeId, setSelectedNodeId }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default NodeList;
|
||||
export default NodeList;
|
||||
|
||||
@@ -16,7 +16,6 @@ const NodeLogs = () => {
|
||||
if (paused) {
|
||||
return;
|
||||
}
|
||||
// console.log(transfer);
|
||||
const msg = transfer.payload;
|
||||
const msgObj = msg.toObj();
|
||||
setLogs((logs) => [...logs, {
|
||||
@@ -24,7 +23,7 @@ const NodeLogs = () => {
|
||||
localTime: new Date().toLocaleTimeString(),
|
||||
level: msgObj.level.getConstant('value'),
|
||||
source: '',
|
||||
text: msgObj.text
|
||||
text: msgObj.text
|
||||
}]);
|
||||
};
|
||||
localNode.on('uavcan.protocol.debug.LogMessage', handleLog);
|
||||
@@ -51,21 +50,22 @@ const NodeLogs = () => {
|
||||
return (
|
||||
<Box
|
||||
component={Paper}
|
||||
sx={{display: 'flex', flexDirection: 'column', flexGrow: 1, height: '50%'}}
|
||||
sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1, height: '50%', backgroundColor: 'background.paper', border: '1px solid', borderColor: 'divider' }}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
alignItems: 'center',
|
||||
height: 20
|
||||
}} margin={1}>
|
||||
<Typography variant="caption" flexGrow={1}>{t('logs.title')}</Typography>
|
||||
<Typography variant="caption" flexGrow={1} sx={{ color: 'text.secondary', fontWeight: 600, letterSpacing: 0.2 }}>{t('logs.title')}</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
||||
<IconButton
|
||||
sx={{
|
||||
width: 20,
|
||||
sx={{
|
||||
width: 20,
|
||||
height: 20,
|
||||
padding: 0
|
||||
padding: 0,
|
||||
color: 'text.secondary',
|
||||
}}
|
||||
size='small'
|
||||
onClick={() => setPaused(!paused)}
|
||||
@@ -73,22 +73,20 @@ const NodeLogs = () => {
|
||||
{paused ? <PlayArrowIcon sx={{ fontSize: 16 }} /> : <PauseIcon sx={{ fontSize: 16 }} />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
width: 20,
|
||||
sx={{
|
||||
width: 20,
|
||||
height: 20,
|
||||
padding: 0
|
||||
padding: 0,
|
||||
color: 'warning.main',
|
||||
}}
|
||||
size='small'
|
||||
color="warning"
|
||||
onClick={() => setLogs([])}
|
||||
>
|
||||
<CleaningServicesIcon sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
<TableContainer
|
||||
sx={{ overflow: 'auto' }}
|
||||
>
|
||||
<TableContainer sx={{ overflow: 'auto' }}>
|
||||
<Table stickyHeader size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
@@ -101,10 +99,10 @@ const NodeLogs = () => {
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{logs.map((log, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableRow key={index} sx={{ '&:hover': { backgroundColor: 'rgba(245, 240, 232, 0.9)' } }}>
|
||||
<TableCell>{log.id}</TableCell>
|
||||
<TableCell>{log.localTime}</TableCell>
|
||||
<TableCell sx={{bgcolor: getLevelColor(log.level)}}>
|
||||
<TableCell sx={{ bgcolor: getLevelColor(log.level), color: getLevelColor(log.level) ? 'common.white' : 'inherit' }}>
|
||||
{log.level}
|
||||
</TableCell>
|
||||
<TableCell>{log.source}</TableCell>
|
||||
@@ -120,4 +118,4 @@ const NodeLogs = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default NodeLogs;
|
||||
export default NodeLogs;
|
||||
|
||||
@@ -67,16 +67,16 @@ const NodeProperties = ({ nodeId, nodes, multiNodeEditorEnable, setMultiNodeEdit
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{ flexGrow: 1, bgcolor: 'background.paper', height: 340}}
|
||||
sx={{ flexGrow: 1, bgcolor: 'background.paper', height: 340, border: '1px solid', borderColor: 'divider', borderRadius: 2 }}
|
||||
component={Paper}
|
||||
p={1}
|
||||
>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="caption">
|
||||
<Typography variant="caption" sx={{ color: 'text.secondary', fontWeight: 600, letterSpacing: 0.2 }}>
|
||||
{t('props.title')}
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
|
||||
<Typography variant="caption">{t('props.multi_editor')}</Typography>
|
||||
<Typography variant="caption" sx={{ color: 'text.secondary' }}>{t('props.multi_editor')}</Typography>
|
||||
<Switch checked={multiNodeEditorEnable} onChange={(e) => { setMultiNodeEditorEnable(e.target.checked) }} />
|
||||
</Stack>
|
||||
</Box>
|
||||
@@ -199,7 +199,7 @@ const NodeProperties = ({ nodeId, nodes, multiNodeEditorEnable, setMultiNodeEdit
|
||||
>
|
||||
{t('props.controls')}
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', flexGrow: 1, border: 1, borderColor: 'grey.500', borderRadius: 1, p: 0.5 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', flexGrow: 1, border: 1, borderColor: 'divider', borderRadius: 1, p: 0.5, backgroundColor: 'background.default' }}>
|
||||
<Button
|
||||
sx={{ mr: 1 }}
|
||||
color="error"
|
||||
@@ -226,10 +226,10 @@ const NodeProperties = ({ nodeId, nodes, multiNodeEditorEnable, setMultiNodeEdit
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
<FirmwareUpdateModal
|
||||
open={firmwareModalOpen}
|
||||
onClose={() => setFirmwareModalOpen(false)}
|
||||
targetNodeId={nodeId}
|
||||
<FirmwareUpdateModal
|
||||
open={firmwareModalOpen}
|
||||
onClose={() => setFirmwareModalOpen(false)}
|
||||
targetNodeId={nodeId}
|
||||
/>
|
||||
<ConfirmRestartModal
|
||||
open={restartModalOpen}
|
||||
@@ -240,4 +240,4 @@ const NodeProperties = ({ nodeId, nodes, multiNodeEditorEnable, setMultiNodeEdit
|
||||
);
|
||||
};
|
||||
|
||||
export default NodeProperties;
|
||||
export default NodeProperties;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Button, Menu, MenuItem, Divider } from '@mui/material';
|
||||
import { Box, Button, Menu, MenuItem } from '@mui/material';
|
||||
import VideogameAssetIcon from '@mui/icons-material/VideogameAsset';
|
||||
import { useTranslation } from './i18n/LanguageContext';
|
||||
|
||||
@@ -41,8 +41,12 @@ const PanelsMenu = ({openWindow}) => {
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
disableElevation
|
||||
onClick={handleClick}
|
||||
color="default"
|
||||
startIcon={<VideogameAssetIcon />}
|
||||
color="inherit"
|
||||
startIcon={<VideogameAssetIcon />}
|
||||
sx={{
|
||||
border: '1px solid rgba(230, 223, 216, 0.95)',
|
||||
backgroundColor: 'background.default',
|
||||
}}
|
||||
>
|
||||
{t('panels.title')}
|
||||
</Button>
|
||||
@@ -70,4 +74,4 @@ const PanelsMenu = ({openWindow}) => {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
export default PanelsMenu;
|
||||
export default PanelsMenu;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Button, Menu, MenuItem, Divider } from '@mui/material';
|
||||
import { Box, Button, Menu, MenuItem } from '@mui/material';
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
import MessageIcon from '@mui/icons-material/Message';
|
||||
import SettingsInputCompositeIcon from '@mui/icons-material/SettingsInputComposite';
|
||||
import { useTranslation } from './i18n/LanguageContext';
|
||||
|
||||
const ToolsMenu =({openWindow}) => {
|
||||
@@ -43,8 +41,12 @@ const ToolsMenu =({openWindow}) => {
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
disableElevation
|
||||
onClick={handleClick}
|
||||
color="default"
|
||||
startIcon={<BuildIcon />}
|
||||
color="inherit"
|
||||
startIcon={<BuildIcon />}
|
||||
sx={{
|
||||
border: '1px solid rgba(230, 223, 216, 0.95)',
|
||||
backgroundColor: 'background.default',
|
||||
}}
|
||||
>
|
||||
{t('tools.title')}
|
||||
</Button>
|
||||
@@ -73,4 +75,4 @@ const ToolsMenu =({openWindow}) => {
|
||||
);
|
||||
}
|
||||
|
||||
export default ToolsMenu;
|
||||
export default ToolsMenu;
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: rgb(22, 13, 13);
|
||||
background-color: #faf9f5;
|
||||
overflow: hidden;
|
||||
color: #141413;
|
||||
}
|
||||
|
||||
#root {
|
||||
|
||||
268
src/theme.js
268
src/theme.js
@@ -1,49 +1,164 @@
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
|
||||
const canvas = '#faf9f5';
|
||||
const surfaceSoft = '#f5f0e8';
|
||||
const surfaceCard = '#efe9de';
|
||||
const surfaceCreamStrong = '#e8e0d2';
|
||||
const surfaceDark = '#181715';
|
||||
const surfaceDarkElevated = '#252320';
|
||||
const surfaceDarkSoft = '#1f1e1b';
|
||||
const hairline = '#e6dfd8';
|
||||
const hairlineSoft = '#ebe6df';
|
||||
const ink = '#141413';
|
||||
const body = '#3d3d3a';
|
||||
const muted = '#6c6a64';
|
||||
const mutedSoft = '#8e8b82';
|
||||
const primary = '#cc785c';
|
||||
const primaryActive = '#a9583e';
|
||||
const primaryDisabled = '#e6dfd8';
|
||||
const onPrimary = '#ffffff';
|
||||
const success = '#5db872';
|
||||
const warning = '#d4a017';
|
||||
const error = '#c64545';
|
||||
const accentTeal = '#5db8a6';
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
text: {
|
||||
primary: '#ffffff', // Set the primary text color to white
|
||||
mode: 'light',
|
||||
primary: {
|
||||
main: primary,
|
||||
dark: primaryActive,
|
||||
light: '#d89278',
|
||||
contrastText: onPrimary,
|
||||
},
|
||||
secondary: {
|
||||
main: accentTeal,
|
||||
},
|
||||
success: {
|
||||
main: success,
|
||||
},
|
||||
warning: {
|
||||
main: warning,
|
||||
},
|
||||
error: {
|
||||
main: error,
|
||||
},
|
||||
background: {
|
||||
default: canvas,
|
||||
paper: surfaceCard,
|
||||
},
|
||||
text: {
|
||||
primary: ink,
|
||||
secondary: body,
|
||||
disabled: mutedSoft,
|
||||
},
|
||||
divider: hairline,
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 12,
|
||||
},
|
||||
// Add typography settings to make all fonts smaller
|
||||
typography: {
|
||||
fontSize: 12, // Default font size in px (reduced from the default 14px)
|
||||
htmlFontSize: 14, // Base HTML font-size (was 16px by default)
|
||||
// Customize specific variants
|
||||
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||
fontSize: 13,
|
||||
htmlFontSize: 14,
|
||||
h1: {
|
||||
fontSize: '2rem', // 24px
|
||||
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||
fontSize: '2rem',
|
||||
fontWeight: 400,
|
||||
letterSpacing: '-0.04em',
|
||||
},
|
||||
h2: {
|
||||
fontSize: '1.75rem', // 21px
|
||||
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||
fontSize: '1.75rem',
|
||||
fontWeight: 400,
|
||||
letterSpacing: '-0.03em',
|
||||
},
|
||||
h3: {
|
||||
fontSize: '1.5rem', // 18px
|
||||
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 400,
|
||||
letterSpacing: '-0.02em',
|
||||
},
|
||||
h4: {
|
||||
fontSize: '1.25rem', // 15px
|
||||
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 400,
|
||||
letterSpacing: '-0.02em',
|
||||
},
|
||||
h5: {
|
||||
fontSize: '1.1rem', // 13.2px
|
||||
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||
fontSize: '1.05rem',
|
||||
fontWeight: 500,
|
||||
},
|
||||
h6: {
|
||||
fontSize: '1rem', // 12px
|
||||
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||
fontSize: '0.95rem',
|
||||
fontWeight: 500,
|
||||
},
|
||||
body1: {
|
||||
fontSize: '0.875rem', // 10.5px
|
||||
fontSize: '0.9rem',
|
||||
},
|
||||
body2: {
|
||||
fontSize: '0.825rem', // 9.9px
|
||||
fontSize: '0.82rem',
|
||||
},
|
||||
button: {
|
||||
fontSize: '0.825rem', // 9.9px
|
||||
fontSize: '0.82rem',
|
||||
fontWeight: 500,
|
||||
textTransform: 'none',
|
||||
},
|
||||
caption: {
|
||||
fontSize: '0.75rem', // 9px
|
||||
fontSize: '0.75rem',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
body: {
|
||||
backgroundColor: canvas,
|
||||
color: ink,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAppBar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: canvas,
|
||||
color: ink,
|
||||
backgroundImage: 'none',
|
||||
borderBottom: `1px solid ${hairline}`,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiToolbar: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
minHeight: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundImage: 'none',
|
||||
backgroundColor: surfaceCard,
|
||||
color: ink,
|
||||
border: `1px solid ${hairline}`,
|
||||
},
|
||||
elevation1: {
|
||||
boxShadow: '0 1px 3px rgba(20, 20, 19, 0.08)',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCard: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundImage: 'none',
|
||||
backgroundColor: surfaceCard,
|
||||
border: `1px solid ${hairline}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiButton: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
@@ -51,19 +166,55 @@ const theme = createTheme({
|
||||
styleOverrides: {
|
||||
root: {
|
||||
textTransform: 'none',
|
||||
borderRadius: 8,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
containedPrimary: {
|
||||
backgroundColor: primary,
|
||||
color: onPrimary,
|
||||
'&:hover': {
|
||||
backgroundColor: primaryActive,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
outlined: {
|
||||
borderColor: hairline,
|
||||
color: ink,
|
||||
backgroundColor: canvas,
|
||||
'&:hover': {
|
||||
backgroundColor: surfaceSoft,
|
||||
borderColor: hairline,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: ink,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Add table cell specific overrides
|
||||
MuiTableCell: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
fontSize: '0.8rem',
|
||||
padding: '4px 8px',
|
||||
padding: '6px 10px',
|
||||
borderBottom: `1px solid ${hairlineSoft}`,
|
||||
},
|
||||
head: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: '0.8rem',
|
||||
fontWeight: 600,
|
||||
color: muted,
|
||||
backgroundColor: surfaceSoft,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTableRow: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: surfaceSoft,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -74,25 +225,19 @@ const theme = createTheme({
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'& .MuiInputBase-root': {
|
||||
fontSize: '0.8rem',
|
||||
fontSize: '0.82rem',
|
||||
backgroundColor: canvas,
|
||||
},
|
||||
'& .MuiInputLabel-root': {
|
||||
fontSize: '0.8rem',
|
||||
transform: 'translate(14px, 9px) scale(1)',
|
||||
},
|
||||
'& .MuiInputLabel-shrink': {
|
||||
transform: 'translate(14px, -6px) scale(0.75)',
|
||||
},
|
||||
'& .MuiOutlinedInput-root': {
|
||||
padding: '4px 8px',
|
||||
},
|
||||
'& .MuiOutlinedInput-input': {
|
||||
padding: '4px',
|
||||
borderRadius: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTable: {
|
||||
MuiFormControl: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
},
|
||||
@@ -102,63 +247,56 @@ const theme = createTheme({
|
||||
size: 'small',
|
||||
},
|
||||
},
|
||||
MuiFormControl: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
},
|
||||
},
|
||||
MuiInputLabel: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
MuiMenuItem: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
fontSize: '0.85rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiFab: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
MuiDivider: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderColor: hairline,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCheckbox: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
},
|
||||
},
|
||||
MuiRadio: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
},
|
||||
},
|
||||
MuiSwitch: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
MuiDialog: {
|
||||
styleOverrides: {
|
||||
paper: {
|
||||
backgroundColor: surfaceCard,
|
||||
border: `1px solid ${hairline}`,
|
||||
boxShadow: '0 12px 32px rgba(20, 20, 19, 0.12)',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDialogTitle: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
fontSize: '1.25rem', // Set your desired font size here
|
||||
color: ink,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDialogContent: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: body,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTypography: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: '#ffffff', // Set the default text color to white
|
||||
color: ink,
|
||||
},
|
||||
},
|
||||
},
|
||||
AppBar: {
|
||||
styleOverrides: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
export default theme;
|
||||
|
||||
Reference in New Issue
Block a user