mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-17 04:59:34 +02:00
feat: sum numbers init
This commit is contained in:
@@ -43,6 +43,7 @@
|
||||
"tailwindcss/no-custom-classname": "warn",
|
||||
"tailwindcss/no-contradicting-classname": "error",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off"
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
}
|
||||
}
|
||||
|
48
.idea/workspace.xml
generated
48
.idea/workspace.xml
generated
@@ -4,20 +4,24 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: change color in png finished">
|
||||
<change afterPath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/index.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/meta.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/service.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/string/to-morse/to-morse.service.test.ts" afterDir="false" />
|
||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: string to morse">
|
||||
<change afterPath="$PROJECT_DIR$/src/components/options/SimpleRadio.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/number/index.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/number/sum/index.tsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/number/sum/meta.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/number/sum/service.ts" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/pages/number/sum/sum.service.test.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.eslintrc" beforeDir="false" afterPath="$PROJECT_DIR$/.eslintrc" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" 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$/scripts/create-tool.mjs" beforeDir="false" afterPath="$PROJECT_DIR$/scripts/create-tool.mjs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/options/ColorSelector.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ColorSelector.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/options/RadioWithTextField.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/RadioWithTextField.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/options/ToolOptionGroups.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/config/uiConfig.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/config/uiConfig.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/home/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/home/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/string/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/index.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/string/join/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/join/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/string/split/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/split/index.tsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/pages/string/to-morse/meta.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/string/to-morse/meta.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/tools/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/index.ts" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -187,15 +191,7 @@
|
||||
<workItem from="1719166718305" duration="1783000" />
|
||||
<workItem from="1719168519203" duration="17675000" />
|
||||
<workItem from="1719197816332" duration="1453000" />
|
||||
<workItem from="1719273044735" duration="4589000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="feat: use vite and ts">
|
||||
<option name="closed" value="true" />
|
||||
<created>1718816900959</created>
|
||||
<option name="number" value="00001" />
|
||||
<option name="presentableId" value="LOCAL-00001" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1718816900959</updated>
|
||||
<workItem from="1719273044735" duration="8300000" />
|
||||
</task>
|
||||
<task id="LOCAL-00002" summary="feat: initial commit">
|
||||
<option name="closed" value="true" />
|
||||
@@ -581,7 +577,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1719275214988</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="50" />
|
||||
<task id="LOCAL-00050" summary="feat: string to morse">
|
||||
<option name="closed" value="true" />
|
||||
<created>1719277679968</created>
|
||||
<option name="number" value="00050" />
|
||||
<option name="presentableId" value="LOCAL-00050" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1719277679969</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="51" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
@@ -602,7 +606,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="chore: remove prebuild" />
|
||||
<MESSAGE value="chore: idea config" />
|
||||
<MESSAGE value="feat: react helmet" />
|
||||
<MESSAGE value="feat: tools normalized" />
|
||||
@@ -627,7 +630,8 @@
|
||||
<MESSAGE value="feat: change colors in png" />
|
||||
<MESSAGE value="chore: ResultFooter" />
|
||||
<MESSAGE value="feat: change color in png finished" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat: change color in png finished" />
|
||||
<MESSAGE value="feat: string to morse" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="feat: string to morse" />
|
||||
</component>
|
||||
<component name="XSLT-Support.FileAssociations.UIState">
|
||||
<expand />
|
||||
|
@@ -30,7 +30,7 @@ function createFolderStructure(basePath, foldersToCreateIndexCount) {
|
||||
}
|
||||
const indexPath = join(currentPath, 'index.ts')
|
||||
if (!fs.existsSync(indexPath) && index < folderArray.length - 1 && index >= folderArray.length - 1 - foldersToCreateIndexCount) {
|
||||
fs.writeFileSync(indexPath, '// index.ts file')
|
||||
fs.writeFileSync(indexPath, `export const ${currentPath.split(sep)[currentPath.split(sep).length - 1]}Tools = [];\n`)
|
||||
console.log(`File created: ${indexPath}`)
|
||||
}
|
||||
// Recursively create the next folder
|
||||
|
@@ -3,7 +3,7 @@ import { Box, Stack, TextField } from '@mui/material';
|
||||
import PaletteIcon from '@mui/icons-material/Palette';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { descriptionFontSize } from '../../config/uiConfig';
|
||||
import { globalDescriptionFontSize } from '../../config/uiConfig';
|
||||
|
||||
interface ColorSelectorProps {
|
||||
value: string;
|
||||
@@ -44,7 +44,9 @@ const ColorSelector: React.FC<ColorSelectorProps> = ({
|
||||
onChange={handleColorChange}
|
||||
/>
|
||||
</Stack>
|
||||
<Typography fontSize={descriptionFontSize}>{description}</Typography>
|
||||
<Typography fontSize={globalDescriptionFontSize}>
|
||||
{description}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@@ -4,37 +4,38 @@ import { Field } from 'formik';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import React from 'react';
|
||||
import TextFieldWithDesc from './TextFieldWithDesc';
|
||||
import { globalDescriptionFontSize } from '../../config/uiConfig';
|
||||
import SimpleRadio from './SimpleRadio';
|
||||
|
||||
const RadioWithTextField = <T,>({
|
||||
fieldName,
|
||||
type,
|
||||
radioValue,
|
||||
title,
|
||||
onTypeChange,
|
||||
onRadioChange,
|
||||
value,
|
||||
description,
|
||||
onTextChange
|
||||
onTextChange,
|
||||
typeDescription
|
||||
}: {
|
||||
fieldName: string;
|
||||
title: string;
|
||||
type: T;
|
||||
onTypeChange: (val: T) => void;
|
||||
radioValue: T;
|
||||
onRadioChange: (val: T) => void;
|
||||
value: string;
|
||||
description: string;
|
||||
onTextChange: (value: string) => void;
|
||||
typeDescription?: string;
|
||||
}) => {
|
||||
const onChange = () => onTypeChange(type);
|
||||
const onChange = () => onRadioChange(radioValue);
|
||||
return (
|
||||
<Box>
|
||||
<Stack
|
||||
direction={'row'}
|
||||
sx={{ mt: 2, mb: 1, cursor: 'pointer' }}
|
||||
onClick={onChange}
|
||||
alignItems={'center'}
|
||||
spacing={1}
|
||||
>
|
||||
<Field type="radio" name={fieldName} value={type} onChange={onChange} />
|
||||
<Typography>{title}</Typography>
|
||||
</Stack>
|
||||
<SimpleRadio
|
||||
value={radioValue}
|
||||
onChange={onChange}
|
||||
fieldName={fieldName}
|
||||
title={title}
|
||||
description={typeDescription}
|
||||
/>
|
||||
<TextFieldWithDesc
|
||||
value={value}
|
||||
onChange={onTextChange}
|
||||
|
46
src/components/options/SimpleRadio.tsx
Normal file
46
src/components/options/SimpleRadio.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Box, Stack } from '@mui/material';
|
||||
import { Field } from 'formik';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { globalDescriptionFontSize } from '../../config/uiConfig';
|
||||
import React from 'react';
|
||||
|
||||
interface SimpleRadioProps {
|
||||
onChange: () => void;
|
||||
fieldName: string;
|
||||
value: any;
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export default function SimpleRadio({
|
||||
onChange,
|
||||
fieldName,
|
||||
value,
|
||||
title,
|
||||
description
|
||||
}: SimpleRadioProps) {
|
||||
return (
|
||||
<Box>
|
||||
<Stack
|
||||
direction={'row'}
|
||||
sx={{ mt: 2, mb: 1, cursor: 'pointer' }}
|
||||
onClick={onChange}
|
||||
alignItems={'center'}
|
||||
spacing={1}
|
||||
>
|
||||
<Field
|
||||
type="radio"
|
||||
name={fieldName}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<Typography>{title}</Typography>
|
||||
</Stack>
|
||||
{description && (
|
||||
<Typography ml={2} fontSize={globalDescriptionFontSize}>
|
||||
{description}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
@@ -11,7 +11,9 @@ export default function ToolOptionGroups({
|
||||
<Stack direction={'row'} spacing={2}>
|
||||
{groups.map((group) => (
|
||||
<Box key={group.title}>
|
||||
<Typography fontSize={22}>{group.title}</Typography>
|
||||
<Typography mb={1} fontSize={22}>
|
||||
{group.title}
|
||||
</Typography>
|
||||
{group.component}
|
||||
</Box>
|
||||
))}
|
||||
|
@@ -1,2 +1,2 @@
|
||||
export const globalInputHeight = 300;
|
||||
export const descriptionFontSize = 12;
|
||||
export const globalDescriptionFontSize = 12;
|
||||
|
@@ -26,7 +26,7 @@ const exampleTools: { label: string; url: string }[] = [
|
||||
{ label: 'Find and replace text', url: '' },
|
||||
{ label: 'Convert emoji to image', url: '' },
|
||||
{ label: 'Split a string', url: '/string/split' },
|
||||
{ label: 'Calculate number sum', url: '' },
|
||||
{ label: 'Calculate number sum', url: '/number/sum' },
|
||||
{ label: 'Pixelate an image', url: '' }
|
||||
];
|
||||
export default function Home() {
|
||||
|
3
src/pages/number/index.ts
Normal file
3
src/pages/number/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { tool as numberSum } from './sum/meta';
|
||||
|
||||
export const numberTools = [numberSum];
|
157
src/pages/number/sum/index.tsx
Normal file
157
src/pages/number/sum/index.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import { Box, Stack } from '@mui/material';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import ToolTextInput from '../../../components/input/ToolTextInput';
|
||||
import ToolTextResult from '../../../components/result/ToolTextResult';
|
||||
import { Formik, useFormikContext } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import ToolOptions from '../../../components/options/ToolOptions';
|
||||
import { compute, NumberExtractionType } from './service';
|
||||
import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext';
|
||||
import RadioWithTextField from '../../../components/options/RadioWithTextField';
|
||||
import ToolOptionGroups from '../../../components/options/ToolOptionGroups';
|
||||
import SimpleRadio from '../../../components/options/SimpleRadio';
|
||||
import CheckboxWithDesc from '../../../components/options/CheckboxWithDesc';
|
||||
|
||||
const initialValues = {
|
||||
extractionType: 'smart' as NumberExtractionType,
|
||||
separator: '\\n',
|
||||
printRunningSum: false
|
||||
};
|
||||
const extractionTypes: {
|
||||
title: string;
|
||||
description: string;
|
||||
type: NumberExtractionType;
|
||||
withTextField: boolean;
|
||||
textValueAccessor?: keyof typeof initialValues;
|
||||
}[] = [
|
||||
{
|
||||
title: 'Smart sum',
|
||||
description: 'Auto detect numbers in the input.',
|
||||
type: 'smart',
|
||||
withTextField: false
|
||||
},
|
||||
{
|
||||
title: 'Number Delimiter',
|
||||
type: 'delimiter',
|
||||
description:
|
||||
'Input SeparatorCustomize the number separator here. (By default a line break.)',
|
||||
withTextField: true,
|
||||
textValueAccessor: 'separator'
|
||||
}
|
||||
];
|
||||
|
||||
export default function SplitText() {
|
||||
const [input, setInput] = useState<string>('');
|
||||
const [result, setResult] = useState<string>('');
|
||||
// const formRef = useRef<FormikProps<typeof initialValues>>(null);
|
||||
const { showSnackBar } = useContext(CustomSnackBarContext);
|
||||
|
||||
const FormikListenerComponent = () => {
|
||||
const { values } = useFormikContext<typeof initialValues>();
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const { extractionType, printRunningSum, separator } = values;
|
||||
|
||||
setResult(compute(input, extractionType, printRunningSum, separator));
|
||||
} catch (exception: unknown) {
|
||||
if (exception instanceof Error)
|
||||
showSnackBar(exception.message, 'error');
|
||||
}
|
||||
}, [values, input]);
|
||||
|
||||
return null; // This component doesn't render anything
|
||||
};
|
||||
const validationSchema = Yup.object({
|
||||
// splitSeparator: Yup.string().required('The separator is required')
|
||||
});
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<ToolTextInput value={input} onChange={setInput} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<ToolTextResult title={'Text pieces'} value={result} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ToolOptions>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={() => {}}
|
||||
>
|
||||
{({ setFieldValue, values }) => (
|
||||
<Stack direction={'row'} spacing={2}>
|
||||
<FormikListenerComponent />
|
||||
<ToolOptionGroups
|
||||
groups={[
|
||||
{
|
||||
title: 'Number extraction',
|
||||
component: extractionTypes.map(
|
||||
({
|
||||
title,
|
||||
description,
|
||||
type,
|
||||
withTextField,
|
||||
textValueAccessor
|
||||
}) =>
|
||||
withTextField ? (
|
||||
<RadioWithTextField
|
||||
key={type}
|
||||
radioValue={type}
|
||||
title={title}
|
||||
fieldName={'extractionType'}
|
||||
description={description}
|
||||
value={
|
||||
textValueAccessor
|
||||
? values[textValueAccessor].toString()
|
||||
: ''
|
||||
}
|
||||
onRadioChange={(type) =>
|
||||
setFieldValue('extractionType', type)
|
||||
}
|
||||
onTextChange={(val) =>
|
||||
setFieldValue(textValueAccessor ?? '', val)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<SimpleRadio
|
||||
key={title}
|
||||
onChange={() =>
|
||||
setFieldValue('extractionType', type)
|
||||
}
|
||||
fieldName={'extractionType'}
|
||||
value={values.extractionType}
|
||||
description={description}
|
||||
title={title}
|
||||
/>
|
||||
)
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Running Sum',
|
||||
component: (
|
||||
<CheckboxWithDesc
|
||||
title={'Print Running Sum'}
|
||||
description={
|
||||
"Display the sum as it's calculated step by step."
|
||||
}
|
||||
checked={values.printRunningSum}
|
||||
onChange={(value) =>
|
||||
setFieldValue('printRunningSum', value)
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</Formik>
|
||||
</ToolOptions>
|
||||
</Box>
|
||||
);
|
||||
}
|
13
src/pages/number/sum/meta.ts
Normal file
13
src/pages/number/sum/meta.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineTool } from '@tools/defineTool';
|
||||
import { lazy } from 'react';
|
||||
// import image from '@assets/text.png';
|
||||
|
||||
export const tool = defineTool('number', {
|
||||
name: 'Number Sum Calculator',
|
||||
path: 'sum',
|
||||
// image,
|
||||
description:
|
||||
'Quickly calculate the sum of numbers in your browser. To get your sum, just enter your list of numbers in the input field, adjust the separator between the numbers in the options below, and this utility will add up all these numbers.',
|
||||
keywords: ['sum'],
|
||||
component: lazy(() => import('./index'))
|
||||
});
|
29
src/pages/number/sum/service.ts
Normal file
29
src/pages/number/sum/service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export type NumberExtractionType = 'smart' | 'delimiter';
|
||||
|
||||
function getAllNumbers(text: string): number[] {
|
||||
const regex = /\d+/g;
|
||||
const matches = text.match(regex);
|
||||
return matches ? matches.map(Number) : [];
|
||||
}
|
||||
|
||||
export const compute = (
|
||||
input: string,
|
||||
extractionType: NumberExtractionType,
|
||||
printRunningSum: boolean,
|
||||
separator: string
|
||||
) => {
|
||||
let numbers: number[] = [];
|
||||
if (extractionType === 'smart') {
|
||||
numbers = getAllNumbers(input);
|
||||
} else {
|
||||
const parts = input.split(separator);
|
||||
// Filter out and convert parts that are numbers
|
||||
numbers = parts
|
||||
.filter((part) => !isNaN(Number(part)) && part.trim() !== '')
|
||||
.map(Number);
|
||||
}
|
||||
return numbers.reduce(
|
||||
(previousValue, currentValue) => previousValue + currentValue,
|
||||
0
|
||||
);
|
||||
};
|
6
src/pages/number/sum/sum.service.test.ts
Normal file
6
src/pages/number/sum/sum.service.test.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { expect, describe, it } from 'vitest';
|
||||
// import { } from './service';
|
||||
//
|
||||
// describe('sum', () => {
|
||||
//
|
||||
// })
|
@@ -151,12 +151,12 @@ export default function SplitText() {
|
||||
({ title, description, type }) => (
|
||||
<RadioWithTextField
|
||||
key={type}
|
||||
type={type}
|
||||
radioValue={type}
|
||||
title={title}
|
||||
fieldName={'splitSeparatorType'}
|
||||
description={description}
|
||||
value={values[`${type}Value`]}
|
||||
onTypeChange={(type) =>
|
||||
onRadioChange={(type) =>
|
||||
setFieldValue('splitSeparatorType', type)
|
||||
}
|
||||
onTextChange={(val) =>
|
||||
|
@@ -6,7 +6,8 @@ export const tool = defineTool('string', {
|
||||
name: 'String To morse',
|
||||
path: 'to-morse',
|
||||
// image,
|
||||
description: '',
|
||||
description:
|
||||
"World's simplest browser-based utility for converting text to Morse code. Load your text in the input form on the left and you'll instantly get Morse code in the output area. Powerful, free, and fast. Load text – get Morse code.",
|
||||
keywords: ['to', 'morse'],
|
||||
component: lazy(() => import('./index'))
|
||||
});
|
||||
|
@@ -2,8 +2,13 @@ import { stringTools } from '../pages/string';
|
||||
import { imageTools } from '../pages/image';
|
||||
import { DefinedTool } from './defineTool';
|
||||
import { capitalizeFirstLetter } from '../utils/string';
|
||||
import { numberTools } from '../pages/number';
|
||||
|
||||
export const tools: DefinedTool[] = [...imageTools, ...stringTools];
|
||||
export const tools: DefinedTool[] = [
|
||||
...imageTools,
|
||||
...stringTools,
|
||||
...numberTools
|
||||
];
|
||||
const categoriesDescriptions: { type: string; value: string }[] = [
|
||||
{
|
||||
type: 'string',
|
||||
@@ -14,6 +19,11 @@ const categoriesDescriptions: { type: string; value: string }[] = [
|
||||
type: 'png',
|
||||
value:
|
||||
'Tools for working with PNG images – convert PNGs to JPGs, create transparent PNGs, change PNG colors, crop, rotate, resize PNGs, and much more.'
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
value:
|
||||
'Tools for working with numbers – generate number sequences, convert numbers to words and words to numbers, sort, round, factor numbers, and much more.'
|
||||
}
|
||||
];
|
||||
export const filterTools = (
|
||||
|
Reference in New Issue
Block a user