feat: change pgn opacity

This commit is contained in:
Ibrahima G. Coulibaly
2025-03-08 08:38:35 +00:00
parent fc58d7f9ee
commit 5d04e5794a
11 changed files with 237 additions and 69 deletions

83
.idea/workspace.xml generated
View File

@@ -4,21 +4,18 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: stringify json">
<change afterPath="$PROJECT_DIR$/.codebuddy/docs/Generate an Arithmetic Progression - Online Number Tools.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.codebuddy/docs/Stringify JSON - Online JSON Tools.txt" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/arithmetic-sequence.service.test.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/index.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/meta.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/number/arithmetic-sequence/service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.codebuddy/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.codebuddy/.gitignore" afterDir="false" />
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="chore: update meta">
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/png/change-opacity/index.tsx" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/png/change-opacity/meta.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/png/change-opacity/service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolContent.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolContent.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/examples/ExampleCard.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/examples/ExampleCard.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/examples/ToolExamples.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/examples/ToolExamples.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/json/stringify/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/json/stringify/meta.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/number/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/number/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/options/ToolOptions.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ToolOptions.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/image/png/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/image/png/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/list/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/list/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/string/reverse/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/reverse/meta.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/typed/jimp.d.ts" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -341,30 +338,6 @@
<workItem from="1740923024259" duration="23000" />
<workItem from="1740933006573" duration="3679000" />
</task>
<task id="LOCAL-00096" summary="chore: format number">
<option name="closed" value="true" />
<created>1720730102816</created>
<option name="number" value="00096" />
<option name="presentableId" value="LOCAL-00096" />
<option name="project" value="LOCAL" />
<updated>1720730102817</updated>
</task>
<task id="LOCAL-00097" summary="feat: rotate ui">
<option name="closed" value="true" />
<created>1720913013183</created>
<option name="number" value="00097" />
<option name="presentableId" value="LOCAL-00097" />
<option name="project" value="LOCAL" />
<updated>1720913013183</updated>
</task>
<task id="LOCAL-00098" summary="feat: shuffle ui">
<option name="closed" value="true" />
<created>1720913810733</created>
<option name="number" value="00098" />
<option name="presentableId" value="LOCAL-00098" />
<option name="project" value="LOCAL" />
<updated>1720913810733</updated>
</task>
<task id="LOCAL-00099" summary="refactor: remove validation schema">
<option name="closed" value="true" />
<created>1720914492812</created>
@@ -733,7 +706,31 @@
<option name="project" value="LOCAL" />
<updated>1741417920442</updated>
</task>
<option name="localTasksCounter" value="145" />
<task id="LOCAL-00145" summary="feat: arithmetic sequence">
<option name="closed" value="true" />
<created>1741419142510</created>
<option name="number" value="00145" />
<option name="presentableId" value="LOCAL-00145" />
<option name="project" value="LOCAL" />
<updated>1741419142510</updated>
</task>
<task id="LOCAL-00146" summary="style: tools height">
<option name="closed" value="true" />
<created>1741419188990</created>
<option name="number" value="00146" />
<option name="presentableId" value="LOCAL-00146" />
<option name="project" value="LOCAL" />
<updated>1741419188990</updated>
</task>
<task id="LOCAL-00147" summary="chore: update meta">
<option name="closed" value="true" />
<created>1741419527557</created>
<option name="number" value="00147" />
<option name="presentableId" value="LOCAL-00147" />
<option name="project" value="LOCAL" />
<updated>1741419527557</updated>
</task>
<option name="localTasksCounter" value="148" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -792,9 +789,6 @@
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="fix: broken links" />
<MESSAGE value="chore: style buttons" />
<MESSAGE value="chore: style" />
<MESSAGE value="style: background svg" />
<MESSAGE value="docs: img" />
<MESSAGE value="fix: bg" />
@@ -817,7 +811,10 @@
<MESSAGE value="chore: smooth scroll for use this tool and examles" />
<MESSAGE value="feat: minify json" />
<MESSAGE value="feat: stringify json" />
<option name="LAST_COMMIT_MESSAGE" value="feat: stringify json" />
<MESSAGE value="feat: arithmetic sequence" />
<MESSAGE value="style: tools height" />
<MESSAGE value="chore: update meta" />
<option name="LAST_COMMIT_MESSAGE" value="chore: update meta" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

66
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@jimp/types": "^1.6.0",
"@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.20",
"@playwright/test": "^1.45.0",
@@ -1540,6 +1541,7 @@
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.12.tgz",
"integrity": "sha512-aeI64HD0npropd+AR76MCcvvRaa+Qck6loCOS03CkkxGHN5/r336qTM5HPUdHKMDOGzqknuVPA8+kK1t03z12g==",
"license": "MIT",
"dependencies": {
"@jimp/utils": "^0.22.12",
"bmp-js": "^0.1.0"
@@ -1575,6 +1577,7 @@
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.12.tgz",
"integrity": "sha512-y6BFTJgch9mbor2H234VSjd9iwAhaNf/t3US5qpYIs0TSbAvM02Fbc28IaDETj9+4YB4676sz4RcN/zwhfu1pg==",
"license": "MIT",
"dependencies": {
"@jimp/utils": "^0.22.12",
"gifwrap": "^0.10.1",
@@ -1588,6 +1591,7 @@
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.12.tgz",
"integrity": "sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q==",
"license": "MIT",
"dependencies": {
"@jimp/utils": "^0.22.12",
"jpeg-js": "^0.4.4"
@@ -1881,6 +1885,7 @@
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.12.tgz",
"integrity": "sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg==",
"license": "MIT",
"dependencies": {
"@jimp/utils": "^0.22.12",
"pngjs": "^6.0.0"
@@ -1893,6 +1898,7 @@
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.12.tgz",
"integrity": "sha512-E1LtMh4RyJsoCAfAkBRVSYyZDTtLq9p9LUiiYP0vPtXyxX4BiYBUYihTLSBlCQg5nF2e4OpQg7SPrLdJ66u7jg==",
"license": "MIT",
"dependencies": {
"utif2": "^4.0.1"
},
@@ -1901,19 +1907,15 @@
}
},
"node_modules/@jimp/types": {
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.12.tgz",
"integrity": "sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz",
"integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==",
"license": "MIT",
"dependencies": {
"@jimp/bmp": "^0.22.12",
"@jimp/gif": "^0.22.12",
"@jimp/jpeg": "^0.22.12",
"@jimp/png": "^0.22.12",
"@jimp/tiff": "^0.22.12",
"timm": "^1.6.1"
"zod": "^3.23.8"
},
"peerDependencies": {
"@jimp/custom": ">=0.3.5"
"engines": {
"node": ">=18"
}
},
"node_modules/@jimp/utils": {
@@ -3846,7 +3848,8 @@
"node_modules/bmp-js": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
"integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw=="
"integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "2.0.1",
@@ -5647,6 +5650,7 @@
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz",
"integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==",
"license": "MIT",
"dependencies": {
"image-q": "^4.0.0",
"omggif": "^1.0.10"
@@ -6019,6 +6023,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz",
"integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==",
"license": "MIT",
"dependencies": {
"@types/node": "16.9.1"
}
@@ -6026,7 +6031,8 @@
"node_modules/image-q/node_modules/@types/node": {
"version": "16.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
"license": "MIT"
},
"node_modules/import-fresh": {
"version": "3.3.0",
@@ -6598,6 +6604,23 @@
"regenerator-runtime": "^0.13.3"
}
},
"node_modules/jimp/node_modules/@jimp/types": {
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.12.tgz",
"integrity": "sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA==",
"license": "MIT",
"dependencies": {
"@jimp/bmp": "^0.22.12",
"@jimp/gif": "^0.22.12",
"@jimp/jpeg": "^0.22.12",
"@jimp/png": "^0.22.12",
"@jimp/tiff": "^0.22.12",
"timm": "^1.6.1"
},
"peerDependencies": {
"@jimp/custom": ">=0.3.5"
}
},
"node_modules/jimp/node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
@@ -6628,7 +6651,8 @@
"node_modules/jpeg-js": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
"license": "BSD-3-Clause"
},
"node_modules/js-tokens": {
"version": "4.0.0",
@@ -7448,7 +7472,8 @@
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
"node_modules/parent-module": {
"version": "1.0.1",
@@ -7710,6 +7735,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz",
"integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==",
"license": "MIT",
"engines": {
"node": ">=12.13.0"
}
@@ -9578,6 +9604,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz",
"integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.11"
}
@@ -10186,6 +10213,15 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zod": {
"version": "3.24.2",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@@ -27,6 +27,7 @@
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@jimp/types": "^1.6.0",
"@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.20",
"@playwright/test": "^1.45.0",

View File

@@ -5,7 +5,6 @@ import React, { ReactNode, RefObject, useContext, useEffect } from 'react';
import { Formik, FormikProps, FormikValues, useFormikContext } from 'formik';
import ToolOptionGroups, { ToolOptionGroup } from './ToolOptionGroups';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
import * as Yup from 'yup';
export type UpdateField<T> = <Y extends keyof T>(field: Y, value: T[Y]) => void;
@@ -26,6 +25,7 @@ const FormikListenerComponent = <T,>({
compute(values, input);
} catch (exception: unknown) {
if (exception instanceof Error) showSnackBar(exception.message, 'error');
else console.error(exception);
}
}, [values, input]);

View File

@@ -0,0 +1,87 @@
import React, { useEffect, useState } from 'react';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { changeOpacity } from './service';
import ToolContent from '@components/ToolContent';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { updateNumberField } from '@utils/string';
type InitialValuesType = {
opacity: number;
};
const initialValues: InitialValuesType = {
opacity: 1
};
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Semi-transparent PNG',
description: 'Make an image 50% transparent',
sampleOptions: {
opacity: 0.5
},
sampleResult: ''
},
{
title: 'Slightly Faded PNG',
description: 'Create a subtle transparency effect',
sampleOptions: {
opacity: 0.8
},
sampleResult: ''
}
];
export default function ChangeOpacity({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const compute = (values: InitialValuesType, input: any) => {
if (input) {
changeOpacity(input, values.opacity).then(setResult);
}
};
return (
<ToolContent
title={title}
input={input}
inputComponent={
<ToolFileInput
value={input}
onChange={setInput}
accept={['image/png']}
title={'Input PNG'}
/>
}
resultComponent={
<ToolFileResult
title={'Changed PNG'}
value={result}
extension={'png'}
/>
}
initialValues={initialValues}
exampleCards={exampleCards}
getGroups={({ values, updateField }) => [
{
title: 'Opacity Settings',
component: (
<TextFieldWithDesc
description="Set opacity between 0 (transparent) and 1 (opaque)"
value={values.opacity}
onOwnChange={(val) =>
updateNumberField(val, 'opacity', updateField)
}
type="number"
inputProps={{ step: 0.1, min: 0, max: 1 }}
/>
)
}
]}
compute={compute}
/>
);
}

View File

@@ -0,0 +1,12 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('png', {
name: 'Change PNG Opacity',
path: 'change-opacity',
icon: 'material-symbols:opacity',
description: 'Easily adjust the transparency of your PNG images. Simply upload your PNG file, use the slider to set the desired opacity level between 0 (fully transparent) and 1 (fully opaque), and download the modified image.',
shortDescription: 'Adjust transparency of PNG images',
keywords: ['opacity', 'transparency', 'png', 'alpha'],
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,39 @@
export async function changeOpacity(
file: File,
opacity: number
): Promise<File> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error('Canvas context not supported'));
return;
}
canvas.width = img.width;
canvas.height = img.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = opacity;
ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
if (blob) {
const newFile = new File([blob], file.name, { type: 'image/png' });
resolve(newFile);
} else {
reject(new Error('Failed to generate image blob'));
}
}, 'image/png');
};
img.onerror = () => reject(new Error('Failed to load image'));
img.src = <string>event.target?.result;
};
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsDataURL(file);
});
}

View File

@@ -2,10 +2,12 @@ import { tool as pngCompressPng } from './compress-png/meta';
import { tool as convertJgpToPng } from './convert-jgp-to-png/meta';
import { tool as pngCreateTransparent } from './create-transparent/meta';
import { tool as changeColorsInPng } from './change-colors-in-png/meta';
import { tool as changeOpacity } from './change-opacity/meta';
export const pngTools = [
pngCompressPng,
pngCreateTransparent,
changeColorsInPng,
convertJgpToPng
convertJgpToPng,
changeOpacity
];

View File

@@ -12,7 +12,7 @@ import { tool as listSort } from './sort/meta';
export const listTools = [
listSort,
listUnwrap,
// listUnwrap,
listReverse,
listFindUnique,
listFindMostPopular,

View File

@@ -5,7 +5,8 @@ export const tool = defineTool('string', {
name: 'Reverse',
path: 'reverse',
icon: '',
description: "World's simplest browser-based utility for reversing text. Input any text and get it instantly reversed, character by character. Perfect for creating mirror text, analyzing palindromes, or playing with text patterns. Preserves spaces and special characters while reversing.",
description:
"World's simplest browser-based utility for reversing text. Input any text and get it instantly reversed, character by character. Perfect for creating mirror text, analyzing palindromes, or playing with text patterns. Preserves spaces and special characters while reversing.",
shortDescription: 'Reverse any text character by character',
keywords: ['reverse'],
component: lazy(() => import('./index'))

7
src/typed/jimp.d.ts vendored
View File

@@ -1,7 +0,0 @@
declare module 'jimp' {
class JimpImage {
getPixelColor: (x: number, y: number) => number;
}
export function read(buffer: Buffer): Promise<JimpImage>;
}