diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 9fb89b5..d18ac21 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,18 @@
-
+
+
+
+
+
+
+
-
+
+
+
+
@@ -48,7 +57,7 @@
"git-widget-placeholder": "main",
"ignore.virus.scanning.warn.message": "true",
"kotlin-language-version-configured": "true",
- "last_opened_file_path": "C:/Users/HP/IdeaProjects/omni-tools/src/pages/string",
+ "last_opened_file_path": "C:/Users/HP/IdeaProjects/omni-tools/src/assets",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
@@ -78,11 +87,11 @@
+
-
@@ -160,7 +169,7 @@
-
+
@@ -514,7 +523,15 @@
1719187190823
-
+
+
+ 1719188162583
+
+
+
+ 1719188162583
+
+
@@ -558,9 +575,9 @@
-
-
+
+
diff --git a/src/assets/grey-pattern.png b/src/assets/grey-pattern.png
new file mode 100644
index 0000000..b734005
Binary files /dev/null and b/src/assets/grey-pattern.png differ
diff --git a/src/assets/image.png b/src/assets/image.png
new file mode 100644
index 0000000..5e01b1e
Binary files /dev/null and b/src/assets/image.png differ
diff --git a/src/components/InputHeader.tsx b/src/components/InputHeader.tsx
new file mode 100644
index 0000000..de0884a
--- /dev/null
+++ b/src/components/InputHeader.tsx
@@ -0,0 +1,10 @@
+import Typography from '@mui/material/Typography';
+import React from 'react';
+
+export default function InputHeader({ title }: { title: string }) {
+ return (
+
+ {title}
+
+ );
+}
diff --git a/src/components/ToolHeader.tsx b/src/components/ToolHeader.tsx
index 570f11b..ded1fff 100644
--- a/src/components/ToolHeader.tsx
+++ b/src/components/ToolHeader.tsx
@@ -29,7 +29,7 @@ export default function ToolHeader({
description
}: ToolHeaderProps) {
return (
-
+
{title}
diff --git a/src/components/input/InputFooter.tsx b/src/components/input/InputFooter.tsx
new file mode 100644
index 0000000..c8e93f6
--- /dev/null
+++ b/src/components/input/InputFooter.tsx
@@ -0,0 +1,24 @@
+import { Stack } from '@mui/material';
+import Button from '@mui/material/Button';
+import PublishIcon from '@mui/icons-material/Publish';
+import ContentPasteIcon from '@mui/icons-material/ContentPaste';
+import React from 'react';
+
+export default function InputFooter({
+ handleImport,
+ handleCopy
+}: {
+ handleImport: () => void;
+ handleCopy: () => void;
+}) {
+ return (
+
+ }>
+ Import from file
+
+ }>
+ Copy to clipboard
+
+
+ );
+}
diff --git a/src/components/input/ToolFileInput.tsx b/src/components/input/ToolFileInput.tsx
new file mode 100644
index 0000000..ced405f
--- /dev/null
+++ b/src/components/input/ToolFileInput.tsx
@@ -0,0 +1,114 @@
+import { Box, styled, TextField, useTheme } from '@mui/material';
+import Typography from '@mui/material/Typography';
+import React, { useContext, useEffect, useRef, useState } from 'react';
+import InputHeader from '../InputHeader';
+import InputFooter from './InputFooter';
+import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
+import greyPattern from '@assets/grey-pattern.png';
+
+interface ToolFileInputProps {
+ value: File | null;
+ onChange: (file: File) => void;
+ accept: string[];
+ title?: string;
+}
+
+export default function ToolFileInput({
+ value,
+ onChange,
+ accept,
+ title = 'File'
+}: ToolFileInputProps) {
+ const [preview, setPreview] = useState(null);
+ const theme = useTheme();
+ const { showSnackBar } = useContext(CustomSnackBarContext);
+ const fileInputRef = useRef(null);
+
+ const handleCopy = () => {
+ navigator.clipboard
+ .writeText(value?.name ?? '')
+ .then(() => showSnackBar('Text 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);
+ }
+ }, [value]);
+
+ const handleFileChange = (event: React.ChangeEvent) => {
+ const file = event.target.files?.[0];
+ if (file) onChange(file);
+ };
+ const handleImportClick = () => {
+ fileInputRef.current?.click();
+ };
+ return (
+
+
+
+ {preview ? (
+
+
+
+ ) : (
+
+
+ Click here to select an image from your device, press Ctrl+V to
+ use an image from your clipboard, drag and drop a file from
+ desktop
+
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/components/input/ToolTextInput.tsx b/src/components/input/ToolTextInput.tsx
index b086c3e..d5a666d 100644
--- a/src/components/input/ToolTextInput.tsx
+++ b/src/components/input/ToolTextInput.tsx
@@ -5,6 +5,8 @@ import PublishIcon from '@mui/icons-material/Publish';
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
import React, { useContext, useRef } from 'react';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
+import InputHeader from '../InputHeader';
+import InputFooter from './InputFooter';
export default function ToolTextInput({
value,
@@ -45,9 +47,7 @@ export default function ToolTextInput({
};
return (
-
- {title}
-
+
onChange(event.target.value)}
@@ -55,14 +55,7 @@ export default function ToolTextInput({
multiline
rows={10}
/>
-
- }>
- Import from file
-
- }>
- Copy to clipboard
-
-
+
(null);
+
+ React.useEffect(() => {
+ if (value) {
+ const objectUrl = URL.createObjectURL(value);
+ setPreview(objectUrl);
+
+ return () => URL.revokeObjectURL(objectUrl);
+ } else {
+ setPreview(null);
+ }
+ }, [value]);
+
+ return (
+
+
+
+ {preview && (
+
+
+
+ )}
+
+
+ );
+}
diff --git a/src/components/result/ToolTextResult.tsx b/src/components/result/ToolTextResult.tsx
index 3a4c80b..efaec01 100644
--- a/src/components/result/ToolTextResult.tsx
+++ b/src/components/result/ToolTextResult.tsx
@@ -5,6 +5,7 @@ import DownloadIcon from '@mui/icons-material/Download';
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
import React, { useContext } from 'react';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
+import InputHeader from '../InputHeader';
export default function ToolTextResult({
title = 'Result',
@@ -37,9 +38,7 @@ export default function ToolTextResult({
};
return (
-
- {title}
-
+
}>
diff --git a/src/pages/image/png/change-colors-in-png/index.tsx b/src/pages/image/png/change-colors-in-png/index.tsx
index b08b174..59da401 100644
--- a/src/pages/image/png/change-colors-in-png/index.tsx
+++ b/src/pages/image/png/change-colors-in-png/index.tsx
@@ -1,11 +1,76 @@
import { Box } from '@mui/material';
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import * as Yup from 'yup';
+import Grid from '@mui/material/Grid';
+import ToolTextInput from '../../../../components/input/ToolTextInput';
+import ToolTextResult from '../../../../components/result/ToolTextResult';
+import ToolFileInput from '../../../../components/input/ToolFileInput';
+import ToolFileResult from '../../../../components/result/ToolFileResult';
-const initialValues = {};
+const initialValues = {
+ rgba: [0, 0, 0, 0]
+};
const validationSchema = Yup.object({
// splitSeparator: Yup.string().required('The separator is required')
});
export default function ChangeColorsInPng() {
- return Lorem ipsum;
-}
\ No newline at end of file
+ const [input, setInput] = useState(null);
+ const [result, setResult] = useState(null);
+
+ useEffect(() => {
+ if (input) {
+ const processImage = async (file: File) => {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ if (ctx == null) return;
+ const img = new Image();
+
+ img.src = URL.createObjectURL(file);
+ await img.decode();
+
+ canvas.width = img.width;
+ canvas.height = img.height;
+ ctx.drawImage(img, 0, 0);
+
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ const data: Uint8ClampedArray = imageData.data;
+ for (let i = 0; i < data.length; i += 4) {
+ // Check for white pixel
+ if (data[i] === 255 && data[i + 1] === 255 && data[i + 2] === 255) {
+ data[i] = 255; // Red
+ data[i + 1] = 0; // Green
+ data[i + 2] = 0; // Blue
+ }
+ }
+
+ ctx.putImageData(imageData, 0, 0);
+
+ canvas.toBlob((blob) => {
+ if (blob) {
+ const newFile = new File([blob], file.name, { type: 'image/png' });
+ setResult(newFile);
+ }
+ }, 'image/png');
+ };
+
+ processImage(input);
+ }
+ }, [input]);
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/image/png/change-colors-in-png/meta.ts b/src/pages/image/png/change-colors-in-png/meta.ts
index 4bd2f83..c6aac38 100644
--- a/src/pages/image/png/change-colors-in-png/meta.ts
+++ b/src/pages/image/png/change-colors-in-png/meta.ts
@@ -1,6 +1,6 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
-import image from '@assets/text.png';
+import image from '@assets/image.png';
export const tool = defineTool('png', {
name: 'Change colors in png',