update visual design system
This commit is contained in:
47
src/About.js
47
src/About.js
@@ -2,20 +2,34 @@ import React from 'react';
|
|||||||
import { Typography, Box, Paper, Divider, Link, Grid, Card, CardContent, List, ListItem, ListItemIcon, ListItemText, Stack, Chip, Button } from '@mui/material';
|
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 packageInfo from '../package.json';
|
||||||
import vimdronesLogo from './image/vimdrones_logo.png';
|
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 CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
|
||||||
import UsbIcon from '@mui/icons-material/Usb';
|
import UsbIcon from '@mui/icons-material/Usb';
|
||||||
import WifiIcon from '@mui/icons-material/Wifi';
|
import WifiIcon from '@mui/icons-material/Wifi';
|
||||||
import ComputerIcon from '@mui/icons-material/Computer';
|
import ComputerIcon from '@mui/icons-material/Computer';
|
||||||
|
|
||||||
// Remove the DiscordIcon import since we're now using a custom image
|
|
||||||
|
|
||||||
const About = () => {
|
const About = () => {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ mx: 'auto', p: 2, width: '100%', flexGrow: 1, display: 'flex', flexDirection: 'column', gap: 1 }} component={Paper} elevation={2}>
|
<Box
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '100%', mb: 1 }}>
|
component={Paper}
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'baseline' }}>
|
elevation={0}
|
||||||
<Typography variant="h4" component="h1" sx={{ fontWeight: 600 }}>
|
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
|
DroneCAN Web Tools
|
||||||
</Typography>
|
</Typography>
|
||||||
<Chip
|
<Chip
|
||||||
@@ -23,14 +37,16 @@ const About = () => {
|
|||||||
size="small"
|
size="small"
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{ ml: 2 }}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</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>
|
</Box>
|
||||||
|
|
||||||
<Card variant="outlined">
|
<Card variant="outlined" sx={{ backgroundColor: 'background.default' }}>
|
||||||
<CardContent sx={{ pb: 1, '&:last-child': { pb: 1 } }}>
|
<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 container spacing={1}>
|
||||||
<Grid item xs={12} sm={6}>
|
<Grid item xs={12} sm={6}>
|
||||||
<List dense disablePadding>
|
<List dense disablePadding>
|
||||||
@@ -61,20 +77,20 @@ const About = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Stack spacing={2} direction={{ xs: 'column', md: 'row' }}>
|
<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 } }}>
|
<CardContent sx={{ pb: 1, '&:last-child': { pb: 1 } }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||||
<WifiIcon color="primary" sx={{ mr: 1 }} />
|
<WifiIcon color="primary" sx={{ mr: 1 }} />
|
||||||
<Typography variant="h6" sx={{ fontWeight: 600 }}>WebSocket via MAVProxy</Typography>
|
<Typography variant="h6" sx={{ fontWeight: 600 }}>WebSocket via MAVProxy</Typography>
|
||||||
</Box>
|
</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
|
mavproxy.py --master /dev/tty.usbmodem111401,115200 --out wsserver:127.0.0.1:5555
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ mt: 1, color: 'text.secondary' }}>
|
<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>
|
</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 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 0.5 }}>
|
||||||
<ComputerIcon color="info" sx={{ mr: 1, fontSize: '1rem' }} />
|
<ComputerIcon color="info" sx={{ mr: 1, fontSize: '1rem' }} />
|
||||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
||||||
@@ -88,7 +104,7 @@ const About = () => {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card variant="outlined" sx={{ flex: 1 }}>
|
<Card variant="outlined" sx={{ flex: 1, backgroundColor: 'background.paper' }}>
|
||||||
<CardContent sx={{ pb: 1, '&:last-child': { pb: 1 } }}>
|
<CardContent sx={{ pb: 1, '&:last-child': { pb: 1 } }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||||
<UsbIcon color="primary" sx={{ mr: 1 }} />
|
<UsbIcon color="primary" sx={{ mr: 1 }} />
|
||||||
@@ -109,7 +125,7 @@ const About = () => {
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem disablePadding sx={{ py: 0.25 }}>
|
<ListItem disablePadding sx={{ py: 0.25 }}>
|
||||||
<ListItemIcon sx={{ minWidth: 30 }}>
|
<ListItemIcon sx={{ minWidth: 30 }}>
|
||||||
<CheckCircleOutlineIcon color="success" fontSize="small" /> {/* Changed from info to success */}
|
<CheckCircleOutlineIcon color="success" fontSize="small" />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary="Standalone MAVCAN USB Adaptor"
|
primary="Standalone MAVCAN USB Adaptor"
|
||||||
@@ -139,7 +155,6 @@ const About = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
</Stack>
|
</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 }} />
|
<Divider sx={{ my: 1.5 }} />
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
|
|||||||
47
src/App.js
47
src/App.js
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
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 MavlinkSession from './mavlink_session';
|
||||||
import dronecan from './dronecan';
|
import dronecan from './dronecan';
|
||||||
import theme from './theme';
|
import theme from './theme';
|
||||||
@@ -140,21 +140,23 @@ const AppContent = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const surfaceBorder = '1px solid rgba(230, 223, 216, 0.95)';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppBar position="static">
|
<AppBar position="static" color="transparent" elevation={0} sx={{ borderBottom: surfaceBorder }}>
|
||||||
<Toolbar variant="dense" sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<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' }}>
|
<Box sx={{ width: '30%', flexGrow: 1, display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center' }}>
|
||||||
<ToolsMenu openWindow={openWindow.bind(this)} />
|
<ToolsMenu openWindow={openWindow} />
|
||||||
<PanelsMenu openWindow={openWindow.bind(this)} />
|
<PanelsMenu openWindow={openWindow} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{flexGrow: 2, display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center'}}>
|
<Box sx={{ flexGrow: 2, display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', gap: 1 }}>
|
||||||
<Box sx={{display: 'flex', flexDirection: 'row', alignItems: 'center'}} ml={0.5} mr={0.5}>
|
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
<a href="https://dev.vimdrones.com" target="_blank" rel="noreferrer" style={{height: 30}}>
|
<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 }} />
|
<img src={DronecanLogo} alt="DroneCAN" style={{ height: 30 }} />
|
||||||
</a>
|
</a>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="caption">
|
<Typography variant="body2" sx={{ fontWeight: 600, letterSpacing: 0.2 }}>
|
||||||
{t('app.title')}
|
{t('app.title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -168,9 +170,10 @@ const AppContent = () => {
|
|||||||
<FormControl
|
<FormControl
|
||||||
size="small"
|
size="small"
|
||||||
sx={{
|
sx={{
|
||||||
minWidth: 80,
|
minWidth: 92,
|
||||||
'& .MuiOutlinedInput-root': {
|
'& .MuiOutlinedInput-root': {
|
||||||
height: 30,
|
height: 32,
|
||||||
|
backgroundColor: '#faf9f5',
|
||||||
},
|
},
|
||||||
'& .MuiSelect-select': {
|
'& .MuiSelect-select': {
|
||||||
paddingTop: 0.5,
|
paddingTop: 0.5,
|
||||||
@@ -197,7 +200,7 @@ const AppContent = () => {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color={dnaServerActive ? "success" : "primary"}
|
color={dnaServerActive ? 'success' : 'primary'}
|
||||||
startIcon={
|
startIcon={
|
||||||
dnaServerActive ?
|
dnaServerActive ?
|
||||||
<DnsIcon sx={{
|
<DnsIcon sx={{
|
||||||
@@ -214,7 +217,7 @@ const AppContent = () => {
|
|||||||
sx={dnaServerActive ? {
|
sx={dnaServerActive ? {
|
||||||
borderColor: 'success.main',
|
borderColor: 'success.main',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'rgba(76, 175, 80, 0.08)',
|
backgroundColor: 'rgba(93, 184, 114, 0.08)',
|
||||||
borderColor: 'success.dark'
|
borderColor: 'success.dark'
|
||||||
}
|
}
|
||||||
} : {}}
|
} : {}}
|
||||||
@@ -227,7 +230,7 @@ const AppContent = () => {
|
|||||||
size="small"
|
size="small"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
onClick={() => setLanguage(language === 'en' ? 'zh' : 'en')}
|
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" />
|
<LanguageIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -244,7 +247,7 @@ const AppContent = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'row', flexGrow: 1, height: '95vh', gap: 0.5, p: 1 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'row', flexGrow: 1, minHeight: 'calc(100vh - 64px)', gap: 0.75, p: 1, backgroundColor: '#faf9f5' }}>
|
||||||
<CompactSidebar
|
<CompactSidebar
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
selectedNodeId={selectedNodeId}
|
selectedNodeId={selectedNodeId}
|
||||||
@@ -257,13 +260,13 @@ const AppContent = () => {
|
|||||||
minWidth: 550,
|
minWidth: 550,
|
||||||
display: { xs: 'none', md: 'flex' },
|
display: { xs: 'none', md: 'flex' },
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: 0.5,
|
gap: 0.75,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NodeList
|
<NodeList
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
selectedNodeId={selectedNodeId}
|
selectedNodeId={selectedNodeId}
|
||||||
setSelectedNodeId={setSelectedNodeId.bind(this)}
|
setSelectedNodeId={setSelectedNodeId}
|
||||||
nodesUpdateTimestamp={nodesUpdateTimestamp}
|
nodesUpdateTimestamp={nodesUpdateTimestamp}
|
||||||
/>
|
/>
|
||||||
<NodeLogs/>
|
<NodeLogs/>
|
||||||
@@ -273,18 +276,18 @@ const AppContent = () => {
|
|||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
sx={{
|
sx={{
|
||||||
gap: 0.5,
|
gap: 0.75,
|
||||||
ml: { xs: 1, md: 0 },
|
ml: { xs: 1, md: 0 },
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{selectedNodeId && (
|
{selectedNodeId && (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1, gap: 0.5 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1, gap: 0.75 }}>
|
||||||
<NodeProperties
|
<NodeProperties
|
||||||
nodeId={selectedNodeId}
|
nodeId={selectedNodeId}
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
multiNodeEditorEnable={multiNodeEditorEnable}
|
multiNodeEditorEnable={multiNodeEditorEnable}
|
||||||
setMultiNodeEditorEnable={setMultiNodeEditorEnable.bind(this)}
|
setMultiNodeEditorEnable={setMultiNodeEditorEnable}
|
||||||
nodesUpdateTimestamp={nodesUpdateTimestamp}
|
nodesUpdateTimestamp={nodesUpdateTimestamp}
|
||||||
/>
|
/>
|
||||||
<NodeParam
|
<NodeParam
|
||||||
@@ -304,7 +307,7 @@ const AppContent = () => {
|
|||||||
open={modalOpen}
|
open={modalOpen}
|
||||||
onClose={handleCloseModal}
|
onClose={handleCloseModal}
|
||||||
onConnectionStatusChange={handleConnectionStatusChange}
|
onConnectionStatusChange={handleConnectionStatusChange}
|
||||||
showMessage={showMessage.bind(this)}
|
showMessage={showMessage}
|
||||||
selectedBus={selectedBus}
|
selectedBus={selectedBus}
|
||||||
onBusChange={handleBusChange}
|
onBusChange={handleBusChange}
|
||||||
/>
|
/>
|
||||||
@@ -314,7 +317,7 @@ const AppContent = () => {
|
|||||||
onClose={() => setSnackbarOpen(false)}
|
onClose={() => setSnackbarOpen(false)}
|
||||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||||
>
|
>
|
||||||
<Alert onClose={() => setSnackbarOpen(false)} severity={snackbarSeverity}>
|
<Alert onClose={() => setSnackbarOpen(false)} severity={snackbarSeverity} variant="filled">
|
||||||
{snackbarMessage}
|
{snackbarMessage}
|
||||||
</Alert>
|
</Alert>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
|
|||||||
@@ -10,18 +10,14 @@ const CompactSidebar = ({
|
|||||||
const [logCounts, setLogCounts] = useState({});
|
const [logCounts, setLogCounts] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Function to get log counts from the window.localNode events
|
|
||||||
const updateLogCounts = () => {
|
const updateLogCounts = () => {
|
||||||
const counts = {};
|
const counts = {};
|
||||||
|
|
||||||
// Initialize counts for all nodes to 0
|
|
||||||
Object.keys(nodes).forEach(nodeId => {
|
Object.keys(nodes).forEach(nodeId => {
|
||||||
counts[nodeId] = 0;
|
counts[nodeId] = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
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)) {
|
if (window.dronecanLogs && Array.isArray(window.dronecanLogs)) {
|
||||||
window.dronecanLogs.forEach(log => {
|
window.dronecanLogs.forEach(log => {
|
||||||
if (log.id) {
|
if (log.id) {
|
||||||
@@ -29,8 +25,6 @@ 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) {
|
} catch (error) {
|
||||||
console.error("Error accessing logs:", error);
|
console.error("Error accessing logs:", error);
|
||||||
}
|
}
|
||||||
@@ -38,54 +32,47 @@ const CompactSidebar = ({
|
|||||||
setLogCounts(counts);
|
setLogCounts(counts);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create log listener and counter
|
|
||||||
let lastLogCounts = {};
|
let lastLogCounts = {};
|
||||||
|
|
||||||
const handleLog = (transfer) => {
|
const handleLog = (transfer) => {
|
||||||
const sourceNodeId = transfer.sourceNodeId;
|
const sourceNodeId = transfer.sourceNodeId;
|
||||||
if (sourceNodeId) {
|
if (sourceNodeId) {
|
||||||
// Update count for this node
|
|
||||||
lastLogCounts[sourceNodeId] = (lastLogCounts[sourceNodeId] || 0) + 1;
|
lastLogCounts[sourceNodeId] = (lastLogCounts[sourceNodeId] || 0) + 1;
|
||||||
setLogCounts({...lastLogCounts});
|
setLogCounts({...lastLogCounts});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Listen for log messages
|
|
||||||
const localNode = window.localNode;
|
const localNode = window.localNode;
|
||||||
if (localNode) {
|
if (localNode) {
|
||||||
localNode.on('uavcan.protocol.debug.LogMessage', handleLog);
|
localNode.on('uavcan.protocol.debug.LogMessage', handleLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial update
|
|
||||||
updateLogCounts();
|
updateLogCounts();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// Remove event listener on cleanup
|
|
||||||
if (localNode) {
|
if (localNode) {
|
||||||
localNode.off('uavcan.protocol.debug.LogMessage', handleLog);
|
localNode.off('uavcan.protocol.debug.LogMessage', handleLog);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [nodes]);
|
}, [nodes]);
|
||||||
|
|
||||||
// Get color based on node mode (matching the same logic as NodeList)
|
|
||||||
const getModeColor = (mode) => {
|
const getModeColor = (mode) => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'OPERATIONAL':
|
case 'OPERATIONAL':
|
||||||
return '#f5f5f5'; // Light gray for operational (subtle)
|
return '#5db872';
|
||||||
case 'INITIALIZATION':
|
case 'INITIALIZATION':
|
||||||
return '#ffb74d'; // Warning color
|
return '#d4a017';
|
||||||
case 'MAINTENANCE':
|
case 'MAINTENANCE':
|
||||||
return '#9c27b0'; // Secondary/purple
|
return '#5db8a6';
|
||||||
case 'SOFTWARE_UPDATE':
|
case 'SOFTWARE_UPDATE':
|
||||||
return '#4caf50'; // Success/green
|
return '#5db872';
|
||||||
case 'OFFLINE':
|
case 'OFFLINE':
|
||||||
return '#f44336'; // Error/red
|
return '#c64545';
|
||||||
default:
|
default:
|
||||||
return '#f44336'; // Default to error color
|
return '#c64545';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle click on a node
|
|
||||||
const handleNodeClick = (nodeId) => {
|
const handleNodeClick = (nodeId) => {
|
||||||
if (nodeId === selectedNodeId) {
|
if (nodeId === selectedNodeId) {
|
||||||
setSelectedNodeId(null);
|
setSelectedNodeId(null);
|
||||||
@@ -105,7 +92,8 @@ const CompactSidebar = ({
|
|||||||
p: 1,
|
p: 1,
|
||||||
gap: 1,
|
gap: 1,
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
height: '100%'
|
height: '100%',
|
||||||
|
backgroundColor: 'background.paper',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@@ -114,8 +102,9 @@ const CompactSidebar = ({
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
mb: 0.5,
|
mb: 0.5,
|
||||||
fontWeight: 'bold',
|
fontWeight: 700,
|
||||||
fontSize: '0.5rem'
|
fontSize: '0.5rem',
|
||||||
|
color: 'text.secondary',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
NODES
|
NODES
|
||||||
@@ -130,6 +119,7 @@ const CompactSidebar = ({
|
|||||||
const node = nodes[nodeId];
|
const node = nodes[nodeId];
|
||||||
const mode = node.status.getConstant('mode');
|
const mode = node.status.getConstant('mode');
|
||||||
const logCount = logCounts[nodeId] || 0;
|
const logCount = logCounts[nodeId] || 0;
|
||||||
|
const selected = selectedNodeId === Number(nodeId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -148,20 +138,20 @@ const CompactSidebar = ({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: selectedNodeId === Number(nodeId) ? 'primary.main' : 'divider',
|
borderColor: selected ? 'primary.main' : 'divider',
|
||||||
backgroundColor: 'background.paper',
|
backgroundColor: selected ? 'rgba(204, 120, 92, 0.08)' : 'background.default',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'action.hover',
|
backgroundColor: 'rgba(245, 240, 232, 0.9)',
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onClick={() => handleNodeClick(Number(nodeId))}
|
onClick={() => handleNodeClick(Number(nodeId))}
|
||||||
>
|
>
|
||||||
{/* Make NID larger and more prominent */}
|
|
||||||
<Typography
|
<Typography
|
||||||
variant='caption'
|
variant='caption'
|
||||||
sx={{
|
sx={{
|
||||||
color: getModeColor(mode),
|
color: getModeColor(mode),
|
||||||
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{nodeId}
|
{nodeId}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
|||||||
clearTimeout(txTimeout.current);
|
clearTimeout(txTimeout.current);
|
||||||
txTimeout.current = setTimeout(() => {
|
txTimeout.current = setTimeout(() => {
|
||||||
setTxActive(false);
|
setTxActive(false);
|
||||||
}, 100); // Blink for 200ms
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFrameReceive = () => {
|
const handleFrameReceive = () => {
|
||||||
@@ -22,7 +22,7 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
|||||||
clearTimeout(rxTimeout.current);
|
clearTimeout(rxTimeout.current);
|
||||||
rxTimeout.current = setTimeout(() => {
|
rxTimeout.current = setTimeout(() => {
|
||||||
setRxActive(false);
|
setRxActive(false);
|
||||||
}, 100); // Blink for 200ms
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (mavlinkSession) {
|
if (mavlinkSession) {
|
||||||
@@ -41,12 +41,12 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
|||||||
}, [localNode, mavlinkSession]);
|
}, [localNode, mavlinkSession]);
|
||||||
|
|
||||||
return (
|
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 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', opacity: isConnected ? 1 : 0.3 }}>
|
||||||
<CircleIcon
|
<CircleIcon
|
||||||
fontSize="small"
|
fontSize="small"
|
||||||
sx={{
|
sx={{
|
||||||
color: txActive && isConnected ? '#4caf50' : '#7e7e7e',
|
color: txActive && isConnected ? '#5db872' : '#8e8b82',
|
||||||
width: '12px',
|
width: '12px',
|
||||||
height: '12px',
|
height: '12px',
|
||||||
transition: 'color 0.1s ease'
|
transition: 'color 0.1s ease'
|
||||||
@@ -59,7 +59,7 @@ const ConnectionIndicators = ({ isConnected, mavlinkSession, localNode }) => {
|
|||||||
<CircleIcon
|
<CircleIcon
|
||||||
fontSize="small"
|
fontSize="small"
|
||||||
sx={{
|
sx={{
|
||||||
color: rxActive && isConnected ? '#2196f3' : '#7e7e7e',
|
color: rxActive && isConnected ? '#5db8a6' : '#8e8b82',
|
||||||
width: '12px',
|
width: '12px',
|
||||||
height: '12px',
|
height: '12px',
|
||||||
transition: 'color 0.1s ease'
|
transition: 'color 0.1s ease'
|
||||||
|
|||||||
@@ -29,16 +29,27 @@ const NodeList = ({ nodes, selectedNodeId, setSelectedNodeId }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderNodeRow = (key) => {
|
const renderNodeRow = (key) => {
|
||||||
let node = nodes[key];
|
const node = nodes[key];
|
||||||
let status = node.status;
|
const status = node.status;
|
||||||
let health = status.getConstant('health');
|
const health = status.getConstant('health');
|
||||||
let mode = status.getConstant('mode');
|
const mode = status.getConstant('mode');
|
||||||
|
const isSelected = Number(key) === Number(selectedNodeId);
|
||||||
return (
|
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>{key}</TableCell>
|
||||||
<TableCell>{node.name}</TableCell>
|
<TableCell sx={{ width: 150 }}>{node.name}</TableCell>
|
||||||
<TableCell>{health}</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>{secondsToTime(status.uptime_sec)}</TableCell>
|
||||||
<TableCell>{node.status.vendor_specific_status_code}</TableCell>
|
<TableCell>{node.status.vendor_specific_status_code}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -48,25 +59,17 @@ const NodeList = ({ nodes, selectedNodeId, setSelectedNodeId }) => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
component={Paper}
|
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 }}>
|
<Box margin={1} sx={{ height: 20 }}>
|
||||||
<Typography variant="caption">Online Nodes</Typography>
|
<Typography variant="caption" sx={{ color: 'text.secondary', fontWeight: 600, letterSpacing: 0.2 }}>Online Nodes</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<TableContainer
|
<TableContainer sx={{ overflow: 'auto' }}>
|
||||||
sx={{ overflow: 'auto' }}
|
|
||||||
>
|
|
||||||
<Table stickyHeader size="small">
|
<Table stickyHeader size="small">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>NID</TableCell>
|
<TableCell>NID</TableCell>
|
||||||
<TableCell
|
<TableCell>Name</TableCell>
|
||||||
sx={{
|
|
||||||
width: 150,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>Health</TableCell>
|
<TableCell>Health</TableCell>
|
||||||
<TableCell>Mode</TableCell>
|
<TableCell>Mode</TableCell>
|
||||||
<TableCell>Uptime</TableCell>
|
<TableCell>Uptime</TableCell>
|
||||||
@@ -74,9 +77,7 @@ const NodeList = ({ nodes, selectedNodeId, setSelectedNodeId }) => {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{Object.keys(nodes).map((key) => (
|
{Object.keys(nodes).map((key) => renderNodeRow(key))}
|
||||||
renderNodeRow(key)
|
|
||||||
))}
|
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const NodeLogs = () => {
|
|||||||
if (paused) {
|
if (paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// console.log(transfer);
|
|
||||||
const msg = transfer.payload;
|
const msg = transfer.payload;
|
||||||
const msgObj = msg.toObj();
|
const msgObj = msg.toObj();
|
||||||
setLogs((logs) => [...logs, {
|
setLogs((logs) => [...logs, {
|
||||||
@@ -51,7 +50,7 @@ const NodeLogs = () => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
component={Paper}
|
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={{
|
<Box sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -59,13 +58,14 @@ const NodeLogs = () => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: 20
|
height: 20
|
||||||
}} margin={1}>
|
}} 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 }}>
|
<Box sx={{ display: 'flex', gap: 0.5 }}>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
padding: 0
|
padding: 0,
|
||||||
|
color: 'text.secondary',
|
||||||
}}
|
}}
|
||||||
size='small'
|
size='small'
|
||||||
onClick={() => setPaused(!paused)}
|
onClick={() => setPaused(!paused)}
|
||||||
@@ -76,19 +76,17 @@ const NodeLogs = () => {
|
|||||||
sx={{
|
sx={{
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
padding: 0
|
padding: 0,
|
||||||
|
color: 'warning.main',
|
||||||
}}
|
}}
|
||||||
size='small'
|
size='small'
|
||||||
color="warning"
|
|
||||||
onClick={() => setLogs([])}
|
onClick={() => setLogs([])}
|
||||||
>
|
>
|
||||||
<CleaningServicesIcon sx={{ fontSize: 16 }} />
|
<CleaningServicesIcon sx={{ fontSize: 16 }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<TableContainer
|
<TableContainer sx={{ overflow: 'auto' }}>
|
||||||
sx={{ overflow: 'auto' }}
|
|
||||||
>
|
|
||||||
<Table stickyHeader size="small">
|
<Table stickyHeader size="small">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -101,10 +99,10 @@ const NodeLogs = () => {
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{logs.map((log, index) => (
|
{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.id}</TableCell>
|
||||||
<TableCell>{log.localTime}</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}
|
{log.level}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{log.source}</TableCell>
|
<TableCell>{log.source}</TableCell>
|
||||||
|
|||||||
@@ -67,16 +67,16 @@ const NodeProperties = ({ nodeId, nodes, multiNodeEditorEnable, setMultiNodeEdit
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<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}
|
component={Paper}
|
||||||
p={1}
|
p={1}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
<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')}
|
{t('props.title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
|
<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) }} />
|
<Switch checked={multiNodeEditorEnable} onChange={(e) => { setMultiNodeEditorEnable(e.target.checked) }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -199,7 +199,7 @@ const NodeProperties = ({ nodeId, nodes, multiNodeEditorEnable, setMultiNodeEdit
|
|||||||
>
|
>
|
||||||
{t('props.controls')}
|
{t('props.controls')}
|
||||||
</Typography>
|
</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
|
<Button
|
||||||
sx={{ mr: 1 }}
|
sx={{ mr: 1 }}
|
||||||
color="error"
|
color="error"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
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 VideogameAssetIcon from '@mui/icons-material/VideogameAsset';
|
||||||
import { useTranslation } from './i18n/LanguageContext';
|
import { useTranslation } from './i18n/LanguageContext';
|
||||||
|
|
||||||
@@ -41,8 +41,12 @@ const PanelsMenu = ({openWindow}) => {
|
|||||||
aria-expanded={open ? 'true' : undefined}
|
aria-expanded={open ? 'true' : undefined}
|
||||||
disableElevation
|
disableElevation
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
color="default"
|
color="inherit"
|
||||||
startIcon={<VideogameAssetIcon />}
|
startIcon={<VideogameAssetIcon />}
|
||||||
|
sx={{
|
||||||
|
border: '1px solid rgba(230, 223, 216, 0.95)',
|
||||||
|
backgroundColor: 'background.default',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{t('panels.title')}
|
{t('panels.title')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
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 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';
|
import { useTranslation } from './i18n/LanguageContext';
|
||||||
|
|
||||||
const ToolsMenu =({openWindow}) => {
|
const ToolsMenu =({openWindow}) => {
|
||||||
@@ -43,8 +41,12 @@ const ToolsMenu =({openWindow}) => {
|
|||||||
aria-expanded={open ? 'true' : undefined}
|
aria-expanded={open ? 'true' : undefined}
|
||||||
disableElevation
|
disableElevation
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
color="default"
|
color="inherit"
|
||||||
startIcon={<BuildIcon />}
|
startIcon={<BuildIcon />}
|
||||||
|
sx={{
|
||||||
|
border: '1px solid rgba(230, 223, 216, 0.95)',
|
||||||
|
backgroundColor: 'background.default',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{t('tools.title')}
|
{t('tools.title')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: rgb(22, 13, 13);
|
background-color: #faf9f5;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
color: #141413;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
|
|||||||
260
src/theme.js
260
src/theme.js
@@ -1,49 +1,164 @@
|
|||||||
import { createTheme } from '@mui/material/styles';
|
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({
|
const theme = createTheme({
|
||||||
palette: {
|
palette: {
|
||||||
mode: 'dark',
|
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: {
|
text: {
|
||||||
primary: '#ffffff', // Set the primary text color to white
|
primary: ink,
|
||||||
|
secondary: body,
|
||||||
|
disabled: mutedSoft,
|
||||||
},
|
},
|
||||||
|
divider: hairline,
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
borderRadius: 12,
|
||||||
},
|
},
|
||||||
// Add typography settings to make all fonts smaller
|
|
||||||
typography: {
|
typography: {
|
||||||
fontSize: 12, // Default font size in px (reduced from the default 14px)
|
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||||
htmlFontSize: 14, // Base HTML font-size (was 16px by default)
|
fontSize: 13,
|
||||||
// Customize specific variants
|
htmlFontSize: 14,
|
||||||
h1: {
|
h1: {
|
||||||
fontSize: '2rem', // 24px
|
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||||
|
fontSize: '2rem',
|
||||||
|
fontWeight: 400,
|
||||||
|
letterSpacing: '-0.04em',
|
||||||
},
|
},
|
||||||
h2: {
|
h2: {
|
||||||
fontSize: '1.75rem', // 21px
|
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||||
|
fontSize: '1.75rem',
|
||||||
|
fontWeight: 400,
|
||||||
|
letterSpacing: '-0.03em',
|
||||||
},
|
},
|
||||||
h3: {
|
h3: {
|
||||||
fontSize: '1.5rem', // 18px
|
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||||
|
fontSize: '1.5rem',
|
||||||
|
fontWeight: 400,
|
||||||
|
letterSpacing: '-0.02em',
|
||||||
},
|
},
|
||||||
h4: {
|
h4: {
|
||||||
fontSize: '1.25rem', // 15px
|
fontFamily: 'Cormorant Garamond, Georgia, serif',
|
||||||
|
fontSize: '1.25rem',
|
||||||
|
fontWeight: 400,
|
||||||
|
letterSpacing: '-0.02em',
|
||||||
},
|
},
|
||||||
h5: {
|
h5: {
|
||||||
fontSize: '1.1rem', // 13.2px
|
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||||
|
fontSize: '1.05rem',
|
||||||
|
fontWeight: 500,
|
||||||
},
|
},
|
||||||
h6: {
|
h6: {
|
||||||
fontSize: '1rem', // 12px
|
fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||||
|
fontSize: '0.95rem',
|
||||||
|
fontWeight: 500,
|
||||||
},
|
},
|
||||||
body1: {
|
body1: {
|
||||||
fontSize: '0.875rem', // 10.5px
|
fontSize: '0.9rem',
|
||||||
},
|
},
|
||||||
body2: {
|
body2: {
|
||||||
fontSize: '0.825rem', // 9.9px
|
fontSize: '0.82rem',
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
fontSize: '0.825rem', // 9.9px
|
fontSize: '0.82rem',
|
||||||
|
fontWeight: 500,
|
||||||
|
textTransform: 'none',
|
||||||
},
|
},
|
||||||
caption: {
|
caption: {
|
||||||
fontSize: '0.75rem', // 9px
|
fontSize: '0.75rem',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
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: {
|
MuiButton: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
size: 'small',
|
size: 'small',
|
||||||
@@ -51,19 +166,55 @@ const theme = createTheme({
|
|||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
textTransform: 'none',
|
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: {
|
MuiTableCell: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
fontSize: '0.8rem',
|
fontSize: '0.8rem',
|
||||||
padding: '4px 8px',
|
padding: '6px 10px',
|
||||||
|
borderBottom: `1px solid ${hairlineSoft}`,
|
||||||
},
|
},
|
||||||
head: {
|
head: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 600,
|
||||||
fontSize: '0.8rem',
|
color: muted,
|
||||||
|
backgroundColor: surfaceSoft,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiTableRow: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: surfaceSoft,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -74,25 +225,19 @@ const theme = createTheme({
|
|||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
'& .MuiInputBase-root': {
|
'& .MuiInputBase-root': {
|
||||||
fontSize: '0.8rem',
|
fontSize: '0.82rem',
|
||||||
|
backgroundColor: canvas,
|
||||||
},
|
},
|
||||||
'& .MuiInputLabel-root': {
|
'& .MuiInputLabel-root': {
|
||||||
fontSize: '0.8rem',
|
fontSize: '0.8rem',
|
||||||
transform: 'translate(14px, 9px) scale(1)',
|
|
||||||
},
|
|
||||||
'& .MuiInputLabel-shrink': {
|
|
||||||
transform: 'translate(14px, -6px) scale(0.75)',
|
|
||||||
},
|
},
|
||||||
'& .MuiOutlinedInput-root': {
|
'& .MuiOutlinedInput-root': {
|
||||||
padding: '4px 8px',
|
borderRadius: 8,
|
||||||
},
|
|
||||||
'& .MuiOutlinedInput-input': {
|
|
||||||
padding: '4px',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiTable: {
|
MuiFormControl: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
size: 'small',
|
size: 'small',
|
||||||
},
|
},
|
||||||
@@ -102,62 +247,55 @@ const theme = createTheme({
|
|||||||
size: 'small',
|
size: 'small',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiFormControl: {
|
|
||||||
defaultProps: {
|
|
||||||
size: 'small',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiInputLabel: {
|
MuiInputLabel: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
size: 'small',
|
size: 'small',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiIconButton: {
|
MuiMenuItem: {
|
||||||
defaultProps: {
|
styleOverrides: {
|
||||||
size: 'small',
|
root: {
|
||||||
|
fontSize: '0.85rem',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiFab: {
|
},
|
||||||
defaultProps: {
|
MuiDivider: {
|
||||||
size: 'small',
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderColor: hairline,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiCheckbox: {
|
|
||||||
defaultProps: {
|
|
||||||
size: 'small',
|
|
||||||
},
|
},
|
||||||
|
MuiDialog: {
|
||||||
|
styleOverrides: {
|
||||||
|
paper: {
|
||||||
|
backgroundColor: surfaceCard,
|
||||||
|
border: `1px solid ${hairline}`,
|
||||||
|
boxShadow: '0 12px 32px rgba(20, 20, 19, 0.12)',
|
||||||
},
|
},
|
||||||
MuiRadio: {
|
|
||||||
defaultProps: {
|
|
||||||
size: 'small',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiSwitch: {
|
|
||||||
defaultProps: {
|
|
||||||
size: 'small',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiDialogTitle: {
|
MuiDialogTitle: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
fontSize: '1.25rem', // Set your desired font size here
|
color: ink,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiDialogContent: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
color: body,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiTypography: {
|
MuiTypography: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
color: '#ffffff', // Set the default text color to white
|
color: ink,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AppBar: {
|
|
||||||
styleOverrides: {
|
|
||||||
defaultProps: {
|
|
||||||
size: 'small',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user