Merge branch 'main' of https://github.com/iib0011/omni-tools into generic-calc

# Conflicts:
#	.idea/workspace.xml
#	src/tools/defineTool.tsx
This commit is contained in:
Ibrahima G. Coulibaly
2025-04-17 08:12:08 +01:00
92 changed files with 9631 additions and 589 deletions

View File

@@ -12,7 +12,7 @@ import { Icon } from '@iconify/react';
const exampleTools: { label: string; url: string }[] = [
{
label: 'Create a transparent image',
url: '/png/create-transparent'
url: '/image-generic/create-transparent'
},
{ label: 'Prettify JSON', url: '/json/prettify' },
{ label: 'Change GIF speed', url: '/gif/change-speed' },
@@ -35,7 +35,7 @@ export default function Hero() {
newInputValue: string
) => {
setInputValue(newInputValue);
setFilteredTools(_.shuffle(filterTools(tools, newInputValue)));
setFilteredTools(filterTools(tools, newInputValue));
};
return (

View File

@@ -5,6 +5,7 @@ import { capitalizeFirstLetter } from '../utils/string';
import Grid from '@mui/material/Grid';
import { Icon, IconifyIcon } from '@iconify/react';
import { categoriesColors } from '../config/uiConfig';
import { getToolsByCategory } from '@tools/index';
const StyledButton = styled(Button)(({ theme }) => ({
backgroundColor: 'white',
@@ -70,7 +71,9 @@ export default function ToolHeader({
items={[
{ title: 'All tools', link: '/' },
{
title: capitalizeFirstLetter(type),
title: getToolsByCategory().find(
(category) => category.type === type
)!.rawTitle,
link: '/categories/' + type
},
{ title }

View File

@@ -53,7 +53,10 @@ export default function ToolLayout({
{children}
<Separator backgroundColor="#5581b5" margin="50px" />
<AllTools
title={`All ${capitalizeFirstLetter(type)} tools`}
title={`All ${capitalizeFirstLetter(
getToolsByCategory().find((category) => category.type === type)!
.rawTitle
)} tools`}
toolCards={otherCategoryTools}
/>
</Box>

View File

@@ -7,11 +7,13 @@ import React from 'react';
export default function ResultFooter({
handleDownload,
handleCopy,
disabled
disabled,
hideCopy
}: {
handleDownload: () => void;
handleCopy: () => void;
disabled?: boolean;
hideCopy?: boolean;
}) {
return (
<Stack mt={1} direction={'row'} spacing={2}>
@@ -22,13 +24,15 @@ export default function ResultFooter({
>
Save as
</Button>
<Button
disabled={disabled}
onClick={handleCopy}
startIcon={<ContentPasteIcon />}
>
Copy to clipboard
</Button>
{!hideCopy && (
<Button
disabled={disabled}
onClick={handleCopy}
startIcon={<ContentPasteIcon />}
>
Copy to clipboard
</Button>
)}
</Stack>
);
}

View File

@@ -15,7 +15,7 @@ export default function ToolFileResult({
}: {
title?: string;
value: File | null;
extension: string;
extension?: string;
loading?: boolean;
loadingText?: string;
}) {
@@ -50,9 +50,20 @@ export default function ToolFileResult({
const handleDownload = () => {
if (value) {
const hasExtension = value.name.includes('.');
const filename = hasExtension ? value.name : `${value.name}.${extension}`;
let filename: string = value.name;
if (extension) {
// Split at the last period to separate filename and extension
const parts = filename.split('.');
// If there's more than one part (meaning there was a period)
if (parts.length > 1) {
// Remove the last part (the extension) and add the new extension
parts.pop();
filename = `${parts.join('.')}.${extension}`;
} else {
// No extension exists, just add it
filename = `${filename}.${extension}`;
}
}
const blob = new Blob([value], { type: value.type });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
@@ -162,6 +173,7 @@ export default function ToolFileResult({
disabled={!value}
handleCopy={handleCopy}
handleDownload={handleDownload}
hideCopy={fileType === 'video' || fileType === 'audio'}
/>
</Box>
);

View File

@@ -1,21 +1,24 @@
import { Box, TextField } from '@mui/material';
import { Box, CircularProgress, TextField, Typography } from '@mui/material';
import React, { useContext } from 'react';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
import InputHeader from '../InputHeader';
import ResultFooter from './ResultFooter';
import { replaceSpecialCharacters } from '@utils/string';
import mime from 'mime';
import { globalInputHeight } from '../../config/uiConfig';
export default function ToolTextResult({
title = 'Result',
value,
extension = 'txt',
keepSpecialCharacters
keepSpecialCharacters,
loading
}: {
title?: string;
value: string;
extension?: string;
keepSpecialCharacters?: boolean;
loading?: boolean;
}) {
const { showSnackBar } = useContext(CustomSnackBarContext);
const handleCopy = () => {
@@ -46,18 +49,37 @@ export default function ToolTextResult({
return (
<Box>
<InputHeader title={title} />
<TextField
value={keepSpecialCharacters ? value : replaceSpecialCharacters(value)}
fullWidth
multiline
sx={{
'&.MuiTextField-root': {
backgroundColor: 'background.paper'
{loading ? (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: globalInputHeight
}}
>
<CircularProgress />
<Typography variant="body2" sx={{ mt: 2 }}>
Loading... This may take a moment.
</Typography>
</Box>
) : (
<TextField
value={
keepSpecialCharacters ? value : replaceSpecialCharacters(value)
}
}}
rows={10}
inputProps={{ 'data-testid': 'text-result' }}
/>
fullWidth
multiline
sx={{
'&.MuiTextField-root': {
backgroundColor: 'background.paper'
}
}}
rows={10}
inputProps={{ 'data-testid': 'text-result' }}
/>
)}
<ResultFooter handleCopy={handleCopy} handleDownload={handleDownload} />
</Box>
);