mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-19 05:59:34 +02:00
feat: dark mode
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import { BrowserRouter, useRoutes } from 'react-router-dom';
|
||||
import routesConfig from '../config/routesConfig';
|
||||
import Navbar from './Navbar';
|
||||
import { Suspense } from 'react';
|
||||
import { Suspense, useMemo, useState } from 'react';
|
||||
import Loading from './Loading';
|
||||
import { ThemeProvider } from '@mui/material';
|
||||
import theme from '../config/muiConfig';
|
||||
import { CssBaseline, ThemeProvider } from '@mui/material';
|
||||
import { CustomSnackBarProvider } from '../contexts/CustomSnackBarContext';
|
||||
import { SnackbarProvider } from 'notistack';
|
||||
import { tools } from '../tools';
|
||||
import './index.css';
|
||||
import { darkTheme, lightTheme } from '../config/muiConfig';
|
||||
|
||||
const AppRoutes = () => {
|
||||
const updatedRoutesConfig = [...routesConfig];
|
||||
@@ -19,21 +19,29 @@ const AppRoutes = () => {
|
||||
};
|
||||
|
||||
function App() {
|
||||
const [darkMode, setDarkMode] = useState<boolean>(() => {
|
||||
return localStorage.getItem('theme') === 'dark';
|
||||
});
|
||||
const theme = useMemo(() => (darkMode ? darkTheme : lightTheme), [darkMode]);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<SnackbarProvider
|
||||
maxSnack={5}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right'
|
||||
}}
|
||||
classes={{
|
||||
containerRoot: 'bottom-0 right-0 mb-52 md:mb-68 mr-8 lg:mr-80 z-99'
|
||||
}}
|
||||
>
|
||||
<CustomSnackBarProvider>
|
||||
<BrowserRouter>
|
||||
<Navbar />
|
||||
<Navbar
|
||||
onSwitchTheme={() => {
|
||||
setDarkMode((prevState) => !prevState);
|
||||
localStorage.setItem('theme', darkMode ? 'light' : 'dark');
|
||||
}}
|
||||
/>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<AppRoutes />
|
||||
</Suspense>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Autocomplete, Box, Stack, TextField } from '@mui/material';
|
||||
import { Autocomplete, Box, Stack, TextField, useTheme } from '@mui/material';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import Grid from '@mui/material/Grid';
|
||||
@@ -25,6 +25,7 @@ const exampleTools: { label: string; url: string }[] = [
|
||||
];
|
||||
export default function Hero() {
|
||||
const [inputValue, setInputValue] = useState<string>('');
|
||||
const theme = useTheme();
|
||||
const [filteredTools, setFilteredTools] = useState<DefinedTool[]>(
|
||||
_.shuffle(tools)
|
||||
);
|
||||
@@ -78,7 +79,7 @@ export default function Hero() {
|
||||
endAdornment: <SearchIcon />,
|
||||
sx: {
|
||||
borderRadius: 4,
|
||||
backgroundColor: 'white'
|
||||
backgroundColor: 'background.paper'
|
||||
}
|
||||
}}
|
||||
onChange={(event) => handleInputChange(event, event.target.value)}
|
||||
@@ -125,11 +126,13 @@ export default function Hero() {
|
||||
borderWidth: 1,
|
||||
padding: 1,
|
||||
borderRadius: 3,
|
||||
borderColor: 'grey',
|
||||
borderColor: theme.palette.mode === 'dark' ? '#363b41' : 'grey',
|
||||
borderStyle: 'solid',
|
||||
backgroundColor: 'white',
|
||||
backgroundColor: 'background.paper',
|
||||
cursor: 'pointer',
|
||||
'&:hover': { backgroundColor: '#FAFAFD' }
|
||||
'&:hover': {
|
||||
backgroundColor: 'background.hover'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography>{tool.label}</Typography>
|
||||
|
@@ -17,8 +17,13 @@ import {
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Icon } from '@iconify/react';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
|
||||
const Navbar: React.FC = () => {
|
||||
interface NavbarProps {
|
||||
onSwitchTheme: () => void;
|
||||
}
|
||||
|
||||
const Navbar: React.FC<NavbarProps> = ({ onSwitchTheme }) => {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||
@@ -30,7 +35,9 @@ const Navbar: React.FC = () => {
|
||||
// { label: 'Features', path: '/features' }
|
||||
// { label: 'About Us', path: '/about-us' }
|
||||
];
|
||||
|
||||
const buttons: ReactNode[] = [
|
||||
<DarkModeIcon onClick={onSwitchTheme} style={{ cursor: 'pointer' }} />,
|
||||
<Icon
|
||||
onClick={() => window.open('https://discord.gg/SDbbn3hT4b', '_blank')}
|
||||
style={{ cursor: 'pointer' }}
|
||||
@@ -81,9 +88,10 @@ const Navbar: React.FC = () => {
|
||||
return (
|
||||
<AppBar
|
||||
position="static"
|
||||
style={{
|
||||
backgroundColor: '#F5F5FA',
|
||||
color: 'black'
|
||||
sx={{
|
||||
background: 'transparent',
|
||||
boxShadow: 'none',
|
||||
color: 'text.primary'
|
||||
}}
|
||||
>
|
||||
<Toolbar
|
||||
|
@@ -31,7 +31,7 @@ function ToolLinks() {
|
||||
<Grid container spacing={2} mt={1}>
|
||||
<Grid item md={12} lg={6}>
|
||||
<StyledButton
|
||||
sx={{ backgroundColor: 'white' }}
|
||||
sx={{ backgroundColor: 'background.paper' }}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
onClick={() => scrollToElement('tool')}
|
||||
@@ -43,6 +43,7 @@ function ToolLinks() {
|
||||
<StyledButton
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
sx={{ backgroundColor: 'background.paper' }}
|
||||
onClick={() => scrollToElement('examples')}
|
||||
>
|
||||
See Examples
|
||||
|
@@ -38,7 +38,7 @@ export default function ToolLayout({
|
||||
display={'flex'}
|
||||
flexDirection={'column'}
|
||||
alignItems={'center'}
|
||||
sx={{ backgroundColor: '#F5F5FA' }}
|
||||
sx={{ backgroundColor: 'background.default' }}
|
||||
>
|
||||
<Helmet>
|
||||
<title>{`${title} - Omni Tools`}</title>
|
||||
|
@@ -1,4 +1,12 @@
|
||||
import { Box, Card, CardContent, Link, Stack, Typography } from '@mui/material';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
CardContent,
|
||||
Link,
|
||||
Stack,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
import { ToolCardProps } from './AllTools';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@@ -10,6 +18,7 @@ export default function ToolCard({
|
||||
link,
|
||||
icon
|
||||
}: ToolCardProps) {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Card
|
||||
@@ -17,10 +26,13 @@ export default function ToolCard({
|
||||
raised
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
bgcolor: '#5581b5',
|
||||
borderColor: '#5581b5',
|
||||
bgcolor: 'background.darkSecondary',
|
||||
borderColor: 'background.darkSecondary',
|
||||
color: '#fff',
|
||||
boxShadow: '6px 6px 12px #b8b9be, -6px -6px 12px #fff',
|
||||
boxShadow:
|
||||
theme.palette.mode === 'dark'
|
||||
? null
|
||||
: '6px 6px 12px #b8b9be, -6px -6px 12px #fff',
|
||||
cursor: 'pointer',
|
||||
height: '100%',
|
||||
'&:hover': {
|
||||
|
@@ -38,14 +38,16 @@ export default function ExampleCard<T>({
|
||||
changeInputResult(sampleText, sampleOptions);
|
||||
}}
|
||||
sx={{
|
||||
bgcolor: theme.palette.background.default,
|
||||
bgcolor: 'background.lightSecondary',
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 2,
|
||||
transition: 'background-color 0.3s ease',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
boxShadow: '12px 9px 11px 2px #b8b9be, -6px -6px 12px #fff'
|
||||
boxShadow: `12px 9px 11px 2px ${
|
||||
theme.palette.mode === 'dark' ? theme.palette.grey[900] : '#b8b9be'
|
||||
}, -6px -6px 12px ${theme.palette.mode === 'dark' ? 'black' : '#fff'}`
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@@ -94,7 +94,7 @@ export default function BaseFileInput({
|
||||
border: preview ? 0 : 1,
|
||||
borderRadius: 2,
|
||||
boxShadow: '5',
|
||||
bgcolor: 'white',
|
||||
bgcolor: 'background.paper',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
@@ -106,7 +106,8 @@ export default function BaseFileInput({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundImage: `url(${greyPattern})`,
|
||||
backgroundImage:
|
||||
theme.palette.mode === 'dark' ? null : `url(${greyPattern})`,
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
@@ -126,7 +127,13 @@ export default function BaseFileInput({
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<Typography color={theme.palette.grey['600']}>
|
||||
<Typography
|
||||
color={
|
||||
theme.palette.mode === 'dark'
|
||||
? theme.palette.grey['300']
|
||||
: theme.palette.grey['600']
|
||||
}
|
||||
>
|
||||
Click here to select a {type} from your device, press Ctrl+V to
|
||||
use a {type} from your clipboard, drag and drop a file from
|
||||
desktop
|
||||
|
@@ -1,377 +0,0 @@
|
||||
import { Box, useTheme } from '@mui/material';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import ReactCrop, { Crop, PixelCrop } from 'react-image-crop';
|
||||
import 'react-image-crop/dist/ReactCrop.css';
|
||||
import InputHeader from '../InputHeader';
|
||||
import InputFooter from './InputFooter';
|
||||
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
|
||||
import greyPattern from '@assets/grey-pattern.png';
|
||||
import { globalInputHeight } from '../../config/uiConfig';
|
||||
import Slider from 'rc-slider';
|
||||
import 'rc-slider/assets/index.css';
|
||||
|
||||
interface ToolFileInputProps {
|
||||
value: File | null;
|
||||
onChange: (file: File) => void;
|
||||
accept: string[];
|
||||
title?: string;
|
||||
showCropOverlay?: boolean;
|
||||
cropShape?: 'rectangular' | 'circular';
|
||||
cropPosition?: { x: number; y: number };
|
||||
cropSize?: { width: number; height: number };
|
||||
onCropChange?: (
|
||||
position: { x: number; y: number },
|
||||
size: { width: number; height: number }
|
||||
) => void;
|
||||
type?: 'image' | 'video' | 'audio';
|
||||
// Video specific props
|
||||
showTrimControls?: boolean;
|
||||
onTrimChange?: (trimStart: number, trimEnd: number) => void;
|
||||
trimStart?: number;
|
||||
trimEnd?: number;
|
||||
}
|
||||
|
||||
export default function ToolFileInput({
|
||||
value,
|
||||
onChange,
|
||||
accept,
|
||||
title = 'File',
|
||||
showCropOverlay = false,
|
||||
cropShape = 'rectangular',
|
||||
cropPosition = { x: 0, y: 0 },
|
||||
cropSize = { width: 100, height: 100 },
|
||||
onCropChange,
|
||||
type = 'image',
|
||||
showTrimControls = false,
|
||||
onTrimChange,
|
||||
trimStart = 0,
|
||||
trimEnd = 100
|
||||
}: ToolFileInputProps) {
|
||||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const theme = useTheme();
|
||||
const { showSnackBar } = useContext(CustomSnackBarContext);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const imageRef = useRef<HTMLImageElement>(null);
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const [imgWidth, setImgWidth] = useState(0);
|
||||
const [imgHeight, setImgHeight] = useState(0);
|
||||
const [videoDuration, setVideoDuration] = useState(0);
|
||||
|
||||
// Convert position and size to crop format used by ReactCrop
|
||||
const [crop, setCrop] = useState<Crop>({
|
||||
unit: 'px',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
});
|
||||
|
||||
const RATIO = imageRef.current ? imgWidth / imageRef.current.width : 1;
|
||||
|
||||
useEffect(() => {
|
||||
if (imgWidth && imgHeight) {
|
||||
setCrop({
|
||||
unit: 'px',
|
||||
x: cropPosition.x / RATIO,
|
||||
y: cropPosition.y / RATIO,
|
||||
width: cropSize.width / RATIO,
|
||||
height: cropSize.height / RATIO
|
||||
});
|
||||
}
|
||||
}, [cropPosition, cropSize, imgWidth, imgHeight]);
|
||||
|
||||
const handleCopy = () => {
|
||||
if (value) {
|
||||
const blob = new Blob([value], { type: value.type });
|
||||
const clipboardItem = new ClipboardItem({ [value.type]: blob });
|
||||
|
||||
navigator.clipboard
|
||||
.write([clipboardItem])
|
||||
.then(() => showSnackBar('File copied', 'success'))
|
||||
.catch((err) => {
|
||||
showSnackBar('Failed to copy: ' + err, 'error');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
const objectUrl = URL.createObjectURL(value);
|
||||
setPreview(objectUrl);
|
||||
|
||||
// Clean up memory when the component is unmounted or the file changes
|
||||
return () => URL.revokeObjectURL(objectUrl);
|
||||
} else {
|
||||
setPreview(null);
|
||||
setImgWidth(0);
|
||||
setImgHeight(0);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) onChange(file);
|
||||
};
|
||||
|
||||
const handleImportClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
// Handle image load to set dimensions
|
||||
const onImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {
|
||||
const { naturalWidth: width, naturalHeight: height } = e.currentTarget;
|
||||
setImgWidth(width);
|
||||
setImgHeight(height);
|
||||
|
||||
// Initialize crop with a centered default crop if needed
|
||||
if (!crop.width && !crop.height && onCropChange) {
|
||||
const initialCrop: Crop = {
|
||||
unit: 'px',
|
||||
x: Math.floor(width / 4),
|
||||
y: Math.floor(height / 4),
|
||||
width: Math.floor(width / 2),
|
||||
height: Math.floor(height / 2)
|
||||
};
|
||||
|
||||
setCrop(initialCrop);
|
||||
|
||||
// Notify parent component of initial crop
|
||||
onCropChange(
|
||||
{ x: initialCrop.x, y: initialCrop.y },
|
||||
{ width: initialCrop.width, height: initialCrop.height }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle video load to set duration
|
||||
const onVideoLoad = (e: React.SyntheticEvent<HTMLVideoElement>) => {
|
||||
const duration = e.currentTarget.duration;
|
||||
setVideoDuration(duration);
|
||||
|
||||
// Initialize trim with full duration if needed
|
||||
if (onTrimChange && trimStart === 0 && trimEnd === 100) {
|
||||
onTrimChange(0, duration);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCropChange = (newCrop: Crop) => {
|
||||
setCrop(newCrop);
|
||||
};
|
||||
|
||||
const handleCropComplete = (crop: PixelCrop) => {
|
||||
if (onCropChange) {
|
||||
onCropChange(
|
||||
{ x: Math.round(crop.x * RATIO), y: Math.round(crop.y * RATIO) },
|
||||
{
|
||||
width: Math.round(crop.width * RATIO),
|
||||
height: Math.round(crop.height * RATIO)
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTrimChange = (start: number, end: number) => {
|
||||
if (onTrimChange) {
|
||||
onTrimChange(start, end);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handlePaste = (event: ClipboardEvent) => {
|
||||
const clipboardItems = event.clipboardData?.items ?? [];
|
||||
const item = clipboardItems[0];
|
||||
if (
|
||||
item &&
|
||||
(item.type.includes('image') || item.type.includes('video'))
|
||||
) {
|
||||
const file = item.getAsFile();
|
||||
if (file) onChange(file);
|
||||
}
|
||||
};
|
||||
window.addEventListener('paste', handlePaste);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('paste', handlePaste);
|
||||
};
|
||||
}, [onChange]);
|
||||
|
||||
// Format seconds to MM:SS format
|
||||
const formatTime = (seconds: number) => {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = Math.floor(seconds % 60);
|
||||
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds
|
||||
.toString()
|
||||
.padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<InputHeader title={title} />
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: globalInputHeight,
|
||||
border: preview ? 0 : 1,
|
||||
borderRadius: 2,
|
||||
boxShadow: '5',
|
||||
bgcolor: 'white',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
{preview ? (
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundImage: `url(${greyPattern})`,
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
{type === 'image' &&
|
||||
(showCropOverlay ? (
|
||||
<ReactCrop
|
||||
crop={crop}
|
||||
onChange={handleCropChange}
|
||||
onComplete={handleCropComplete}
|
||||
circularCrop={cropShape === 'circular'}
|
||||
style={{ maxWidth: '100%', maxHeight: globalInputHeight }}
|
||||
>
|
||||
<img
|
||||
ref={imageRef}
|
||||
src={preview}
|
||||
alt="Preview"
|
||||
style={{ maxWidth: '100%', maxHeight: globalInputHeight }}
|
||||
onLoad={onImageLoad}
|
||||
/>
|
||||
</ReactCrop>
|
||||
) : (
|
||||
<img
|
||||
ref={imageRef}
|
||||
src={preview}
|
||||
alt="Preview"
|
||||
style={{ maxWidth: '100%', maxHeight: globalInputHeight }}
|
||||
onLoad={onImageLoad}
|
||||
/>
|
||||
))}
|
||||
{type === 'video' && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={preview}
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: showTrimControls ? 'calc(100% - 50px)' : '100%'
|
||||
}}
|
||||
onLoadedMetadata={onVideoLoad}
|
||||
controls={!showTrimControls}
|
||||
/>
|
||||
|
||||
{showTrimControls && videoDuration > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
padding: '10px 20px',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
color: 'white',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Typography variant="caption">
|
||||
Start: {formatTime(trimStart || 0)}
|
||||
</Typography>
|
||||
<Typography variant="caption">
|
||||
End: {formatTime(trimEnd || videoDuration)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<div
|
||||
className="range-slider-container"
|
||||
style={{ margin: '20px 0', width: '100%' }}
|
||||
>
|
||||
<Slider
|
||||
range
|
||||
min={0}
|
||||
max={videoDuration}
|
||||
step={0.1}
|
||||
value={[trimStart || 0, trimEnd || videoDuration]}
|
||||
onChange={(values) => {
|
||||
if (Array.isArray(values)) {
|
||||
handleTrimChange(values[0], values[1]);
|
||||
}
|
||||
}}
|
||||
allowCross={false}
|
||||
pushable={0.1} // Minimum distance between handles
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{type === 'audio' && (
|
||||
<audio
|
||||
src={preview}
|
||||
controls
|
||||
style={{ width: '100%', maxWidth: '500px' }}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
onClick={handleImportClick}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 5,
|
||||
height: '100%',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<Typography color={theme.palette.grey['600']}>
|
||||
Click here to select a {type} from your device, press Ctrl+V to
|
||||
use a {type} from your clipboard, drag and drop a file from
|
||||
desktop
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<InputFooter handleCopy={handleCopy} handleImport={handleImportClick} />
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
style={{ display: 'none' }}
|
||||
type="file"
|
||||
accept={accept.join(',')}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
@@ -52,7 +52,7 @@ export default function ToolTextInput({
|
||||
rows={10}
|
||||
sx={{
|
||||
'&.MuiTextField-root': {
|
||||
backgroundColor: 'white'
|
||||
backgroundColor: 'background.paper'
|
||||
}
|
||||
}}
|
||||
inputProps={{
|
||||
|
@@ -30,7 +30,7 @@ const ColorSelector: React.FC<ColorSelectorProps & TextFieldProps> = ({
|
||||
<Box mb={1}>
|
||||
<Stack direction={'row'}>
|
||||
<TextField
|
||||
sx={{ backgroundColor: 'white' }}
|
||||
sx={{ backgroundColor: 'background.paper' }}
|
||||
value={color}
|
||||
onChange={handleColorChange}
|
||||
{...props}
|
||||
|
@@ -19,7 +19,7 @@ const TextFieldWithDesc = ({
|
||||
<Box>
|
||||
<TextField
|
||||
placeholder={placeholder}
|
||||
sx={{ backgroundColor: 'white' }}
|
||||
sx={{ backgroundColor: 'background.paper' }}
|
||||
value={value}
|
||||
onChange={(event) => onOwnChange(event.target.value)}
|
||||
{...props}
|
||||
|
@@ -36,7 +36,7 @@ export default function ToolOptions<T extends FormikValues>({
|
||||
mb: 2,
|
||||
borderRadius: 2,
|
||||
padding: 2,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
backgroundColor: 'background.lightSecondary',
|
||||
boxShadow: '2'
|
||||
}}
|
||||
mt={2}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Box, CircularProgress, Typography } from '@mui/material';
|
||||
import { Box, CircularProgress, Typography, useTheme } from '@mui/material';
|
||||
import React, { useContext } from 'react';
|
||||
import InputHeader from '../InputHeader';
|
||||
import greyPattern from '@assets/grey-pattern.png';
|
||||
@@ -21,6 +21,7 @@ export default function ToolFileResult({
|
||||
}) {
|
||||
const [preview, setPreview] = React.useState<string | null>(null);
|
||||
const { showSnackBar } = useContext(CustomSnackBarContext);
|
||||
const theme = useTheme();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (value) {
|
||||
@@ -87,7 +88,7 @@ export default function ToolFileResult({
|
||||
border: preview ? 0 : 1,
|
||||
borderRadius: 2,
|
||||
boxShadow: '5',
|
||||
bgcolor: 'white'
|
||||
bgcolor: 'background.paper'
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
@@ -114,7 +115,8 @@ export default function ToolFileResult({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundImage: `url(${greyPattern})`
|
||||
backgroundImage:
|
||||
theme.palette.mode === 'dark' ? null : `url(${greyPattern})`
|
||||
}}
|
||||
>
|
||||
{fileType === 'image' && (
|
||||
|
@@ -52,7 +52,7 @@ export default function ToolTextResult({
|
||||
multiline
|
||||
sx={{
|
||||
'&.MuiTextField-root': {
|
||||
backgroundColor: 'white'
|
||||
backgroundColor: 'background.paper'
|
||||
}
|
||||
}}
|
||||
rows={10}
|
||||
|
Reference in New Issue
Block a user