feat: Integrate Material UI theme provider and enhance UI components
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"server": "node server.js --dev",
|
"server": "node server.js --dev",
|
||||||
"dev": "concurrently \"npm start\" \"npm run server\"",
|
"dev": "concurrently \"npm start\" \"node --watch server.js --dev\"",
|
||||||
"build-image": "node scripts/build-image.js"
|
"build-image": "node scripts/build-image.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
40
src/App.jsx
40
src/App.jsx
@@ -1,12 +1,9 @@
|
|||||||
import React, { useState, useEffect, useMemo } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
ThemeProvider,
|
|
||||||
createTheme,
|
|
||||||
CssBaseline,
|
CssBaseline,
|
||||||
Box,
|
Box,
|
||||||
Container,
|
useColorScheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
||||||
import Header from "./components/Header";
|
import Header from "./components/Header";
|
||||||
import Footer from "./components/Footer";
|
import Footer from "./components/Footer";
|
||||||
import MainPage from "./components/MainPage";
|
import MainPage from "./components/MainPage";
|
||||||
@@ -78,19 +75,14 @@ function App() {
|
|||||||
"x-api-key": apiKey,
|
"x-api-key": apiKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
|
const { setMode } = useColorScheme();
|
||||||
|
|
||||||
const muiTheme = useMemo(() => {
|
|
||||||
return createTheme({
|
|
||||||
cssVariables: true,
|
|
||||||
colorSchemes: {
|
|
||||||
light: true,
|
|
||||||
dark: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
// Load sample data from API on startup and setup periodic state checking
|
// Load sample data from API on startup and setup periodic state checking
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Sync initial theme from localStorage with MUI color scheme
|
||||||
|
const initialMode = theme === 'auto' ? 'system' : theme;
|
||||||
|
setMode(initialMode);
|
||||||
|
|
||||||
loadSampleData();
|
loadSampleData();
|
||||||
|
|
||||||
// Check for state changes every 5 seconds
|
// Check for state changes every 5 seconds
|
||||||
@@ -156,20 +148,29 @@ function App() {
|
|||||||
|
|
||||||
const handleThemeChange = (newTheme) => {
|
const handleThemeChange = (newTheme) => {
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
|
const muiMode = newTheme === "auto" ? "system" : newTheme;
|
||||||
|
setMode(muiMode);
|
||||||
|
localStorage.setItem("theme", newTheme);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageChange = (newPage) => {
|
const handlePageChange = (newPage) => {
|
||||||
setCurrentPage(newPage);
|
setCurrentPage(newPage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShellTypeChange = (newShellType) => {
|
||||||
|
setShellType(newShellType);
|
||||||
|
localStorage.setItem("jmespath-shell-type", newShellType);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={muiTheme}>
|
<>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
minHeight: "100vh",
|
height: "100vh",
|
||||||
|
overflow: "hidden",
|
||||||
bgcolor: "background.default",
|
bgcolor: "background.default",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -188,6 +189,7 @@ function App() {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
|
height: "100%", // Force height for children
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{currentPage === "main" ? (
|
{currentPage === "main" ? (
|
||||||
@@ -205,14 +207,14 @@ function App() {
|
|||||||
apiKey={apiKey}
|
apiKey={apiKey}
|
||||||
onRegenerateApiKey={regenerateApiKey}
|
onRegenerateApiKey={regenerateApiKey}
|
||||||
shellType={shellType}
|
shellType={shellType}
|
||||||
onShellTypeChange={setShellType}
|
onShellTypeChange={handleShellTypeChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</Box>
|
</Box>
|
||||||
</ThemeProvider>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,12 +40,11 @@ function CodeBlock({ code }) {
|
|||||||
p: 2,
|
p: 2,
|
||||||
pr: 6,
|
pr: 6,
|
||||||
bgcolor: "action.hover",
|
bgcolor: "action.hover",
|
||||||
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
fontFamily: "'Noto Sans Mono', monospace",
|
||||||
fontSize: "0.85rem",
|
fontSize: "0.85rem",
|
||||||
whiteSpace: "pre-wrap",
|
whiteSpace: "pre-wrap",
|
||||||
wordBreak: "break-all",
|
wordBreak: "break-all",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
borderRadius: 2,
|
|
||||||
borderColor: "divider",
|
borderColor: "divider",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -92,10 +91,10 @@ function ApiKeyPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box component="main" sx={{ flexGrow: 1, py: 4, px: 2 }}>
|
<Box sx={{ flexGrow: 1, py: 4, px: 2 }}>
|
||||||
<Grid container justifyContent="center">
|
<Grid container justifyContent="center">
|
||||||
<Grid size={{ xs: 12, md: 8, lg: 6 }}>
|
<Grid size={{ xs: 12, md: 8, lg: 6 }}>
|
||||||
<Paper elevation={1} sx={{ p: { xs: 3, md: 5 }, borderRadius: 4, bgcolor: "background.paper", border: 1, borderColor: "divider" }}>
|
<Paper elevation={1} sx={{ p: { xs: 3, md: 5 }, bgcolor: "background.paper", border: 1, borderColor: "divider" }}>
|
||||||
<Typography variant="h5" gutterBottom sx={{ mb: 4, fontWeight: 700, display: "flex", alignItems: "center", gap: 1.5, color: "text.primary" }}>
|
<Typography variant="h5" gutterBottom sx={{ mb: 4, fontWeight: 700, display: "flex", alignItems: "center", gap: 1.5, color: "text.primary" }}>
|
||||||
<KeyIcon color="primary" /> API Key Management
|
<KeyIcon color="primary" /> API Key Management
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -111,11 +110,11 @@ function ApiKeyPage({
|
|||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
style: { fontFamily: "'JetBrains Mono', 'Fira Code', monospace", fontSize: "0.9rem" },
|
style: { fontFamily: "'Noto Sans Mono', monospace", fontSize: "0.9rem" },
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{ "& .MuiOutlinedInput-root": { borderRadius: 4, bgcolor: "background.paper" } }}
|
sx={{ "& .MuiOutlinedInput-root": { bgcolor: "background.paper" } }}
|
||||||
/>
|
/>
|
||||||
<Tooltip title="Copy API Key">
|
<Tooltip title="Copy API Key">
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -156,7 +155,8 @@ function ApiKeyPage({
|
|||||||
gap: 2,
|
gap: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h6" fontWeight="600" color="text.primary">📡 Remote Upload API</Typography>
|
<Typography variant="h6" fontWeight="600" color="text.primary">Remote Upload API</Typography>
|
||||||
|
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
size="small"
|
size="small"
|
||||||
value={shellType}
|
value={shellType}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ function Footer() {
|
|||||||
<Box
|
<Box
|
||||||
component="footer"
|
component="footer"
|
||||||
sx={{
|
sx={{
|
||||||
py: 2,
|
py: 1,
|
||||||
mt: 2,
|
|
||||||
borderTop: 1,
|
borderTop: 1,
|
||||||
borderColor: "divider",
|
borderColor: "divider",
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
Paper,
|
Paper,
|
||||||
TextField,
|
TextField,
|
||||||
Button,
|
Button,
|
||||||
Grid,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
IconButton,
|
IconButton,
|
||||||
Alert,
|
Alert,
|
||||||
@@ -26,6 +25,7 @@ import {
|
|||||||
Check as CheckIcon,
|
Check as CheckIcon,
|
||||||
Refresh as RefreshIcon,
|
Refresh as RefreshIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
import jmespath from "jmespath";
|
import jmespath from "jmespath";
|
||||||
|
|
||||||
function MainPage({
|
function MainPage({
|
||||||
@@ -187,13 +187,23 @@ function MainPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box component="main" sx={{ flexGrow: 1, py: 3, px: { xs: 2, md: 4 } }}>
|
<Box
|
||||||
<Box sx={{ mb: 4, maxWidth: 800, mx: "auto" }}>
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
pt: 1,
|
||||||
|
pb: 3,
|
||||||
|
px: { xs: 2, md: 4 },
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
minHeight: 0,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ mb: 2, flexShrink: 0 }}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body2"
|
||||||
color="text.secondary"
|
color="text.secondary"
|
||||||
align="center"
|
align="left"
|
||||||
sx={{ mb: 3 }}
|
|
||||||
>
|
>
|
||||||
Validate and test JMESPath expressions against JSON data in real-time.
|
Validate and test JMESPath expressions against JSON data in real-time.
|
||||||
Enter your JMESPath query and JSON data below to see the results
|
Enter your JMESPath query and JSON data below to see the results
|
||||||
@@ -203,166 +213,196 @@ function MainPage({
|
|||||||
|
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
mb: 4,
|
mb: 3,
|
||||||
p: 0,
|
flexShrink: 0,
|
||||||
borderRadius: 2,
|
|
||||||
overflow: "hidden",
|
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
|
p: 3,
|
||||||
|
border: 1,
|
||||||
|
borderColor: "divider",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
px: 3,
|
|
||||||
py: 1.5,
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 1.5,
|
justifyContent: "space-between",
|
||||||
bgcolor: "action.hover",
|
mb: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SearchIcon sx={{ fontSize: 24 }} color="primary" />
|
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
|
||||||
<Typography variant="subtitle1" fontWeight="600">
|
<SearchIcon sx={{ fontSize: 24 }} color="primary" />
|
||||||
JMESPath Expression
|
<Typography variant="h6" fontWeight="600">
|
||||||
</Typography>
|
JMESPath Expression
|
||||||
</Box>
|
</Typography>
|
||||||
<Box sx={{ px: 3, pb: 2 }}>
|
</Box>
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
placeholder="Enter JMESPath expression (e.g., people[*].name)"
|
|
||||||
value={jmespathExpression}
|
|
||||||
onChange={handleJmespathChange}
|
|
||||||
error={!!error}
|
|
||||||
helperText={error || " "}
|
|
||||||
FormHelperTextProps={{
|
|
||||||
sx: { color: error ? "error.main" : "success.main", fontWeight: 500 }
|
|
||||||
}}
|
|
||||||
slotProps={{
|
|
||||||
input: {
|
|
||||||
style: { fontFamily: "'JetBrains Mono', 'Fira Code', monospace", fontSize: "1rem" },
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
"& .MuiOutlinedInput-root": {
|
|
||||||
borderRadius: 3,
|
|
||||||
bgcolor: "background.paper",
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{showReloadButton && (
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "center", mt: -1, mb: 2 }}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="secondary"
|
|
||||||
onClick={onReloadSampleData}
|
|
||||||
startIcon={<RefreshIcon />}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
New data available - Reload
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
placeholder="Enter JMESPath expression (e.g., people[*].name)"
|
||||||
|
value={jmespathExpression}
|
||||||
|
onChange={handleJmespathChange}
|
||||||
|
error={!!error}
|
||||||
|
helperText={error || " "}
|
||||||
|
sx={{
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
fontFamily: "'Noto Sans Mono', monospace",
|
||||||
|
fontSize: "1.1rem",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Grid container spacing={3} sx={{ flexGrow: 1, minHeight: 0 }}>
|
<Grid container spacing={3} sx={{ flex: "1 1 0", minHeight: 0, height: 0 }}>
|
||||||
<Grid size={{ xs: 12, md: 6 }} sx={{ display: "flex", flexDirection: "column" }}>
|
<Grid size={{ xs: 12, md: 6 }} sx={{ display: "flex", flexDirection: "column", minHeight: 0 }}>
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
borderRadius: 4,
|
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
|
border: 1,
|
||||||
|
borderColor: "divider",
|
||||||
|
minHeight: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
px: 2,
|
px: 2,
|
||||||
py: 2,
|
py: 1.5,
|
||||||
bgcolor: "action.hover",
|
bgcolor: "action.hover",
|
||||||
borderBottom: 1,
|
borderBottom: 1,
|
||||||
borderColor: "divider",
|
borderColor: "divider",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexWrap: "wrap",
|
|
||||||
gap: 1,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
|
||||||
<DataObjectIcon sx={{ mr: 1, fontSize: 20 }} color="primary" />
|
<DataObjectIcon sx={{ fontSize: 20 }} color="primary" />
|
||||||
<Typography variant="subtitle2" color="text.primary">
|
<Typography variant="subtitle2" color="text.primary">
|
||||||
JSON Input
|
JSON Input
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{showReloadButton && (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="secondary"
|
||||||
|
onClick={onReloadSampleData}
|
||||||
|
startIcon={<RefreshIcon fontSize="inherit" />}
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
ml: 1,
|
||||||
|
px: 1,
|
||||||
|
py: 0.25,
|
||||||
|
fontSize: "0.65rem",
|
||||||
|
textTransform: "none",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
minWidth: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reload data
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Stack direction="row" spacing={1} flexWrap="wrap">
|
<Stack direction="row" spacing={1} alignItems="center">
|
||||||
<Tooltip title="Load from Disk">
|
<Tooltip title="Load from Disk">
|
||||||
<span>
|
<IconButton
|
||||||
<IconButton size="medium" onClick={loadFromDisk} color="primary" aria-label="Load from Disk">
|
size="small"
|
||||||
<FileOpenIcon fontSize="small" />
|
onClick={loadFromDisk}
|
||||||
</IconButton>
|
color="primary"
|
||||||
</span>
|
aria-label="Load from Disk"
|
||||||
|
>
|
||||||
|
<FileOpenIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="Load Logs">
|
<Tooltip title="Load Logs">
|
||||||
<span>
|
<IconButton
|
||||||
<IconButton size="medium" onClick={loadLogFile} color="primary" aria-label="Load Logs">
|
size="small"
|
||||||
<UploadFileIcon fontSize="small" />
|
onClick={loadLogFile}
|
||||||
</IconButton>
|
color="primary"
|
||||||
</span>
|
aria-label="Load Logs"
|
||||||
|
>
|
||||||
|
<UploadFileIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="Load Sample">
|
<Tooltip title="Load Sample">
|
||||||
<span>
|
<IconButton
|
||||||
<IconButton size="medium" onClick={loadSample} color="primary" aria-label="Load Sample">
|
size="small"
|
||||||
<RestoreIcon fontSize="small" />
|
onClick={loadSample}
|
||||||
</IconButton>
|
color="primary"
|
||||||
</span>
|
aria-label="Load Sample"
|
||||||
|
>
|
||||||
|
<RestoreIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="Format">
|
<Tooltip title="Format">
|
||||||
<span>
|
<IconButton
|
||||||
<IconButton size="medium" onClick={formatJson} color="primary" aria-label="Format">
|
size="small"
|
||||||
<FormatAlignLeftIcon fontSize="small" />
|
onClick={formatJson}
|
||||||
</IconButton>
|
color="primary"
|
||||||
</span>
|
aria-label="Format"
|
||||||
|
>
|
||||||
|
<FormatAlignLeftIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Divider orientation="vertical" flexItem sx={{ mx: 0.5 }} />
|
<Divider orientation="vertical" flexItem sx={{ mx: 0.5 }} />
|
||||||
<Tooltip title="Clear">
|
<Tooltip title="Clear all inputs">
|
||||||
<span>
|
<IconButton
|
||||||
<IconButton size="medium" onClick={clearAll} color="secondary" aria-label="Clear all inputs">
|
size="small"
|
||||||
<ClearIcon fontSize="small" />
|
onClick={clearAll}
|
||||||
</IconButton>
|
color="secondary"
|
||||||
</span>
|
aria-label="Clear all inputs"
|
||||||
|
>
|
||||||
|
<ClearIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ p: 2, flexGrow: 1, display: "flex", flexDirection: "column" }}>
|
<Box sx={{ p: 2, flex: "1 1 0", display: "flex", flexDirection: "column", minHeight: 0, overflow: "hidden" }}>
|
||||||
<TextField
|
<TextField
|
||||||
multiline
|
multiline
|
||||||
fullWidth
|
fullWidth
|
||||||
value={jsonData}
|
value={jsonData}
|
||||||
onChange={handleJsonChange}
|
onChange={handleJsonChange}
|
||||||
placeholder="Enter JSON data here..."
|
placeholder="Enter JSON data here..."
|
||||||
error={!!jsonError}
|
|
||||||
variant="standard"
|
variant="standard"
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
disableUnderline: true,
|
disableUnderline: true,
|
||||||
style: {
|
style: {
|
||||||
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
||||||
height: "100%",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
fontSize: "0.85rem",
|
fontSize: "0.85rem",
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
|
height: "100%",
|
||||||
|
boxSizing: "border-box",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
flexGrow: 1,
|
flex: "1 1 0",
|
||||||
"& .MuiInputBase-root": { height: "100%", overflow: "auto" },
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: 0,
|
||||||
|
minHeight: 0,
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
flex: "1 1 0",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "stretch",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: 0,
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
flexGrow: 1,
|
||||||
|
overflow: "auto !important",
|
||||||
|
height: "100% !important",
|
||||||
|
resize: "none",
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{jsonError && (
|
{jsonError && (
|
||||||
<Alert severity="error" sx={{ mt: 1, borderRadius: 2 }} variant="filled">
|
<Alert severity="error" sx={{ mt: 1, flexShrink: 0 }} variant="filled">
|
||||||
{jsonError}
|
{jsonError}
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
@@ -370,21 +410,23 @@ function MainPage({
|
|||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid size={{ xs: 12, md: 6 }} sx={{ display: "flex", flexDirection: "column" }}>
|
<Grid size={{ xs: 12, md: 6 }} sx={{ display: "flex", flexDirection: "column", minHeight: 0 }}>
|
||||||
<Paper
|
<Paper
|
||||||
sx={{
|
sx={{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
borderRadius: 4,
|
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
|
border: 1,
|
||||||
|
borderColor: "divider",
|
||||||
|
minHeight: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
px: 2,
|
px: 2,
|
||||||
py: 2,
|
py: 1.5,
|
||||||
bgcolor: "action.hover",
|
bgcolor: "action.hover",
|
||||||
borderBottom: 1,
|
borderBottom: 1,
|
||||||
borderColor: "divider",
|
borderColor: "divider",
|
||||||
@@ -403,7 +445,7 @@ function MainPage({
|
|||||||
<Tooltip title="Copy to Clipboard">
|
<Tooltip title="Copy to Clipboard">
|
||||||
<span>
|
<span>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="medium"
|
size="small"
|
||||||
onClick={copyToClipboard}
|
onClick={copyToClipboard}
|
||||||
disabled={!result || result === "null"}
|
disabled={!result || result === "null"}
|
||||||
color={copySuccess ? "success" : "primary"}
|
color={copySuccess ? "success" : "primary"}
|
||||||
@@ -415,7 +457,7 @@ function MainPage({
|
|||||||
<Tooltip title="Download Result">
|
<Tooltip title="Download Result">
|
||||||
<span>
|
<span>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="medium"
|
size="small"
|
||||||
onClick={downloadResult}
|
onClick={downloadResult}
|
||||||
disabled={!result || result === "null"}
|
disabled={!result || result === "null"}
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -426,29 +468,47 @@ function MainPage({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ p: 2, flexGrow: 1, display: "flex", flexDirection: "column" }}>
|
<Box sx={{ p: 2, flex: "1 1 0", display: "flex", flexDirection: "column", minHeight: 0, overflow: "hidden" }}>
|
||||||
<TextField
|
<TextField
|
||||||
multiline
|
multiline
|
||||||
fullWidth
|
fullWidth
|
||||||
value={result}
|
value={result}
|
||||||
|
variant="standard"
|
||||||
|
placeholder="Results will appear here..."
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
disableUnderline: true,
|
disableUnderline: true,
|
||||||
style: {
|
style: {
|
||||||
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
||||||
height: "100%",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
fontSize: "0.85rem",
|
fontSize: "0.85rem",
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
|
height: "100%",
|
||||||
|
boxSizing: "border-box",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
variant="standard"
|
|
||||||
placeholder="Results will appear here..."
|
|
||||||
sx={{
|
sx={{
|
||||||
flexGrow: 1,
|
flex: "1 1 0",
|
||||||
"& .MuiInputBase-root": { height: "100%", overflow: "auto" },
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
height: 0,
|
||||||
|
minHeight: 0,
|
||||||
|
"& .MuiInputBase-root": {
|
||||||
|
flex: "1 1 0",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "stretch",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: 0,
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
flexGrow: 1,
|
||||||
|
overflow: "auto !important",
|
||||||
|
height: "100% !important",
|
||||||
|
resize: "none",
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
|
import { ThemeProvider } from "@mui/material";
|
||||||
|
import theme from "./theme";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<ThemeProvider theme={theme} defaultMode="system">
|
||||||
|
<App />
|
||||||
|
</ThemeProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
13
src/theme.js
Normal file
13
src/theme.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { createTheme } from "@mui/material";
|
||||||
|
|
||||||
|
const theme = createTheme({
|
||||||
|
cssVariables: {
|
||||||
|
colorSchemeSelector: 'class',
|
||||||
|
},
|
||||||
|
colorSchemes: {
|
||||||
|
light: true,
|
||||||
|
dark: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default theme;
|
||||||
Reference in New Issue
Block a user