mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-09-15 03:59:31 +02:00
fix: misc
This commit is contained in:
44
.idea/workspace.xml
generated
44
.idea/workspace.xml
generated
@@ -4,10 +4,20 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="fix: ctrl v">
|
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: update readme">
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/assets/logo.png" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/pages/home/Categories.tsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" 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/Hero.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Hero.tsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/Navbar/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Navbar/index.tsx" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/components/Navbar/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Navbar/index.tsx" 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/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/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/string/index.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/tools/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/index.ts" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -78,7 +88,7 @@
|
|||||||
"git-widget-placeholder": "main",
|
"git-widget-placeholder": "main",
|
||||||
"ignore.virus.scanning.warn.message": "true",
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
"kotlin-language-version-configured": "true",
|
"kotlin-language-version-configured": "true",
|
||||||
"last_opened_file_path": "C:/Users/HP/IdeaProjects/omni-tools/src/components/options",
|
"last_opened_file_path": "C:/Users/Ibrahima/IdeaProjects/omni-tools/src/assets",
|
||||||
"node.js.detected.package.eslint": "true",
|
"node.js.detected.package.eslint": "true",
|
||||||
"node.js.detected.package.tslint": "true",
|
"node.js.detected.package.tslint": "true",
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
@@ -111,11 +121,11 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="RecentsManager">
|
<component name="RecentsManager">
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="C:\Users\Ibrahima\IdeaProjects\omni-tools\src\assets" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\components\options" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\components\options" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\assets" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\assets" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string\split" />
|
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\string\split" />
|
||||||
<recent name="C:\Users\HP\IdeaProjects\omni-tools\src\pages\images" />
|
|
||||||
</key>
|
</key>
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
<recent name="C:\Users\Ibrahima\IdeaProjects\omni-tools\src\pages\tools" />
|
<recent name="C:\Users\Ibrahima\IdeaProjects\omni-tools\src\pages\tools" />
|
||||||
@@ -250,15 +260,11 @@
|
|||||||
<workItem from="1720912096050" duration="3065000" />
|
<workItem from="1720912096050" duration="3065000" />
|
||||||
<workItem from="1740259920741" duration="7742000" />
|
<workItem from="1740259920741" duration="7742000" />
|
||||||
<workItem from="1740270391152" duration="690000" />
|
<workItem from="1740270391152" duration="690000" />
|
||||||
<workItem from="1740274898695" duration="1168000" />
|
<workItem from="1740274898695" duration="2231000" />
|
||||||
</task>
|
<workItem from="1740295530385" duration="1120000" />
|
||||||
<task id="LOCAL-00054" summary="chore: sum tests">
|
<workItem from="1740300354462" duration="1059000" />
|
||||||
<option name="closed" value="true" />
|
<workItem from="1740301493702" duration="8924000" />
|
||||||
<created>1719282131977</created>
|
<workItem from="1740318886545" duration="856000" />
|
||||||
<option name="number" value="00054" />
|
|
||||||
<option name="presentableId" value="LOCAL-00054" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1719282131977</updated>
|
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00055" summary="fix: readme">
|
<task id="LOCAL-00055" summary="fix: readme">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -644,7 +650,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1740267666455</updated>
|
<updated>1740267666455</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="103" />
|
<task id="LOCAL-00103" summary="feat: update readme">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1740276092528</created>
|
||||||
|
<option name="number" value="00103" />
|
||||||
|
<option name="presentableId" value="LOCAL-00103" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1740276092528</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="104" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@@ -676,7 +690,6 @@
|
|||||||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
||||||
<option name="CHECK_NEW_TODO" value="false" />
|
<option name="CHECK_NEW_TODO" value="false" />
|
||||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||||
<MESSAGE value="chore: remove unused deps" />
|
|
||||||
<MESSAGE value="style: lint" />
|
<MESSAGE value="style: lint" />
|
||||||
<MESSAGE value="fix: radio and list sort init" />
|
<MESSAGE value="fix: radio and list sort init" />
|
||||||
<MESSAGE value="chore: formik updateField" />
|
<MESSAGE value="chore: formik updateField" />
|
||||||
@@ -701,7 +714,8 @@
|
|||||||
<MESSAGE value="refactor: optimize imports" />
|
<MESSAGE value="refactor: optimize imports" />
|
||||||
<MESSAGE value="chore: use string tools" />
|
<MESSAGE value="chore: use string tools" />
|
||||||
<MESSAGE value="fix: ctrl v" />
|
<MESSAGE value="fix: ctrl v" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="fix: ctrl v" />
|
<MESSAGE value="feat: update readme" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="feat: update readme" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XSLT-Support.FileAssociations.UIState">
|
<component name="XSLT-Support.FileAssociations.UIState">
|
||||||
<expand />
|
<expand />
|
||||||
|
52
README.md
52
README.md
@@ -1,9 +1,13 @@
|
|||||||
# OmniTools
|
<p align="center"><img src="src/assets/logo.png" width="80"></p>
|
||||||
|
<h1 align="center">OmniTools</h1>
|
||||||
|
|
||||||
Welcome to **OmniTools**, a self-hosted alternative to PineTools.com.
|
[//]: # ([](https://hub.docker.com/r/iib0011/omni-tools))
|
||||||
|
|
||||||
This project offers a variety of online tools to help with everyday tasks,
|
[](https://discord.gg/SDbbn3hT4b)
|
||||||
all available for free and open for community contributions. Whether you are manipulating images, crunching numbers, or coding, OmniTools has you covered. Please don't forget to star the
|
|
||||||
|
Welcome to OmniTools, a self-hosted platform offering a variety of online tools to simplify everyday tasks.
|
||||||
|
Whether you are manipulating images, crunching numbers, or
|
||||||
|
coding, OmniTools has you covered. Please don't forget to star the
|
||||||
repo to support us.
|
repo to support us.
|
||||||
Here is the [demo](https://omnitools.netlify.app/) website.
|
Here is the [demo](https://omnitools.netlify.app/) website.
|
||||||
|
|
||||||
@@ -19,27 +23,43 @@ Here is the [demo](https://omnitools.netlify.app/) website.
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
OmniTools includes a variety of tools, such as:
|
We strive to offer a variety of tools, including:
|
||||||
|
|
||||||
1. **Image/Video/Binary tools**
|
## **Image/Video/Binary Tools**
|
||||||
|
|
||||||
- Image Resizer, Image converter, Video trimmer, video reverser, etc.
|
- Image Resizer
|
||||||
|
- Image Converter
|
||||||
|
- Video Trimmer
|
||||||
|
- Video Reverser
|
||||||
|
- And more...
|
||||||
|
|
||||||
2. **Math tools**
|
## **String/List Tools**
|
||||||
|
|
||||||
- Generate prime numbers, generate perfect numbers etc.
|
- Case Converters
|
||||||
|
- List Shuffler
|
||||||
|
- Text Formatters
|
||||||
|
- And more...
|
||||||
|
|
||||||
3. **String/List Tools**
|
## **Date and Time Tools**
|
||||||
|
|
||||||
- Case converters, shuffle list, text formatters, etc.
|
- Date Calculators
|
||||||
|
- Time Zone Converters
|
||||||
|
- And more...
|
||||||
|
|
||||||
4. **Date and Time Tools**
|
## **Math Tools**
|
||||||
|
|
||||||
- Date calculators, time zone converters, etc.
|
- Generate Prime Numbers
|
||||||
|
- Generate Perfect Numbers
|
||||||
|
- And more...
|
||||||
|
|
||||||
5. **Miscellaneous Tools**
|
## **Miscellaneous Tools**
|
||||||
|
|
||||||
- JSON, XML tools, CSV tools etc.
|
- JSON Tools
|
||||||
|
- XML Tools
|
||||||
|
- CSV Tools
|
||||||
|
- And more...
|
||||||
|
|
||||||
|
Stay tuned as we continue to expand and improve our collection!
|
||||||
|
|
||||||
## Self-host/Run
|
## Self-host/Run
|
||||||
|
|
||||||
@@ -49,6 +69,8 @@ docker run -d --name omni-tools --restart unless-stopped -p 8080:80 iib0011/omni
|
|||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
|
This is a React Project with Typescript Material UI.
|
||||||
|
|
||||||
### Project setup
|
### Project setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@@ -10,6 +10,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
|
"@hugeicons/core-free-icons": "^1.0.10",
|
||||||
|
"@hugeicons/react": "^1.0.3",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/material": "^5.15.20",
|
||||||
"@playwright/test": "^1.45.0",
|
"@playwright/test": "^1.45.0",
|
||||||
@@ -1398,6 +1400,20 @@
|
|||||||
"@hapi/hoek": "^9.0.0"
|
"@hapi/hoek": "^9.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@hugeicons/core-free-icons": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-XMjwTffefQGJ0B3gjnS9IV2UqM5qYT4WUJjD+cD7x6TfwE8rSAb+foGNbcyCjpXKVOnuyaJa+y4ukrPyNY/DBw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@hugeicons/react": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hugeicons/react/-/react-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-NJN8PmxTZlkt3T9a7uNZLhkJlIyQUt+sMxM5Qa/UH1qC1fBkwI7C7HSY/y4f7jjo5SQl7zRkm3hWH9tpWuHmWw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.14",
|
"version": "0.11.14",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||||
|
@@ -27,6 +27,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
|
"@hugeicons/core-free-icons": "^1.0.10",
|
||||||
|
"@hugeicons/react": "^1.0.3",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/material": "^5.15.20",
|
||||||
"@playwright/test": "^1.45.0",
|
"@playwright/test": "^1.45.0",
|
||||||
|
@@ -1,59 +1,75 @@
|
|||||||
import { readFile, writeFile } from 'fs/promises'
|
import { readFile, writeFile } from 'fs/promises';
|
||||||
import fs from 'fs'
|
import fs from 'fs';
|
||||||
import { dirname, join, sep } from 'path'
|
import { dirname, join, sep } from 'path';
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
const currentDirname = dirname(fileURLToPath(import.meta.url))
|
const currentDirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const toolName = process.argv[2]
|
const toolName = process.argv[2];
|
||||||
const folder = process.argv[3]
|
const folder = process.argv[3];
|
||||||
|
|
||||||
const toolsDir = join(currentDirname, '..', 'src', 'pages', folder ?? '')
|
const toolsDir = join(
|
||||||
|
currentDirname,
|
||||||
|
'..',
|
||||||
|
'src',
|
||||||
|
'pages',
|
||||||
|
'tools',
|
||||||
|
folder ?? ''
|
||||||
|
);
|
||||||
if (!toolName) {
|
if (!toolName) {
|
||||||
throw new Error('Please specify a toolname.')
|
throw new Error('Please specify a toolname.');
|
||||||
}
|
}
|
||||||
|
|
||||||
function capitalizeFirstLetter(string) {
|
function capitalizeFirstLetter(string) {
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFolderStructure(basePath, foldersToCreateIndexCount) {
|
function createFolderStructure(basePath, foldersToCreateIndexCount) {
|
||||||
const folderArray = basePath.split(sep)
|
const folderArray = basePath.split(sep);
|
||||||
|
|
||||||
function recursiveCreate(currentBase, index) {
|
function recursiveCreate(currentBase, index) {
|
||||||
if (index >= folderArray.length) {
|
if (index >= folderArray.length) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const currentPath = join(currentBase, folderArray[index])
|
const currentPath = join(currentBase, folderArray[index]);
|
||||||
if (!fs.existsSync(currentPath)) {
|
if (!fs.existsSync(currentPath)) {
|
||||||
fs.mkdirSync(currentPath, { recursive: true })
|
fs.mkdirSync(currentPath, { recursive: true });
|
||||||
}
|
}
|
||||||
const indexPath = join(currentPath, 'index.ts')
|
const indexPath = join(currentPath, 'index.ts');
|
||||||
if (!fs.existsSync(indexPath) && index < folderArray.length - 1 && index >= folderArray.length - 1 - foldersToCreateIndexCount) {
|
if (
|
||||||
fs.writeFileSync(indexPath, `export const ${currentPath.split(sep)[currentPath.split(sep).length - 1]}Tools = [];\n`)
|
!fs.existsSync(indexPath) &&
|
||||||
console.log(`File created: ${indexPath}`)
|
index < folderArray.length - 1 &&
|
||||||
|
index >= folderArray.length - 1 - foldersToCreateIndexCount
|
||||||
|
) {
|
||||||
|
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
|
// Recursively create the next folder
|
||||||
recursiveCreate(currentPath, index + 1)
|
recursiveCreate(currentPath, index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the recursive folder creation
|
// Start the recursive folder creation
|
||||||
recursiveCreate('.', 0)
|
recursiveCreate('.', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolNameCamelCase = toolName.replace(/-./g, (x) => x[1].toUpperCase())
|
const toolNameCamelCase = toolName.replace(/-./g, (x) => x[1].toUpperCase());
|
||||||
const toolNameTitleCase =
|
const toolNameTitleCase =
|
||||||
toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, ' ')
|
toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, ' ');
|
||||||
const toolDir = join(toolsDir, toolName)
|
const toolDir = join(toolsDir, toolName);
|
||||||
const type = folder.split(sep)[folder.split(sep).length - 1]
|
const type = folder.split(sep)[folder.split(sep).length - 1];
|
||||||
await createFolderStructure(toolDir, folder.split(sep).length)
|
await createFolderStructure(toolDir, folder.split(sep).length);
|
||||||
console.log(`Directory created: ${toolDir}`)
|
console.log(`Directory created: ${toolDir}`);
|
||||||
|
|
||||||
const createToolFile = async (name, content) => {
|
const createToolFile = async (name, content) => {
|
||||||
const filePath = join(toolDir, name)
|
const filePath = join(toolDir, name);
|
||||||
await writeFile(filePath, content.trim())
|
await writeFile(filePath, content.trim());
|
||||||
console.log(`File created: ${filePath}`)
|
console.log(`File created: ${filePath}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
createToolFile(
|
createToolFile(
|
||||||
`index.tsx`,
|
`index.tsx`,
|
||||||
@@ -70,7 +86,7 @@ export default function ${capitalizeFirstLetter(toolNameCamelCase)}() {
|
|||||||
return <Box>Lorem ipsum</Box>;
|
return <Box>Lorem ipsum</Box>;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
)
|
);
|
||||||
createToolFile(
|
createToolFile(
|
||||||
`meta.ts`,
|
`meta.ts`,
|
||||||
`
|
`
|
||||||
@@ -84,13 +100,13 @@ export const tool = defineTool('${type}', {
|
|||||||
// image,
|
// image,
|
||||||
description: '',
|
description: '',
|
||||||
shortDescription: '',
|
shortDescription: '',
|
||||||
keywords: ['${toolName.split('-').join('\', \'')}'],
|
keywords: ['${toolName.split('-').join("', '")}'],
|
||||||
component: lazy(() => import('./index'))
|
component: lazy(() => import('./index'))
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
)
|
);
|
||||||
|
|
||||||
createToolFile(`service.ts`, ``)
|
createToolFile(`service.ts`, ``);
|
||||||
createToolFile(
|
createToolFile(
|
||||||
`${toolName}.service.test.ts`,
|
`${toolName}.service.test.ts`,
|
||||||
`
|
`
|
||||||
@@ -101,7 +117,7 @@ import { expect, describe, it } from 'vitest';
|
|||||||
//
|
//
|
||||||
// })
|
// })
|
||||||
`
|
`
|
||||||
)
|
);
|
||||||
|
|
||||||
// createToolFile(
|
// createToolFile(
|
||||||
// `${toolName}.e2e.spec.ts`,
|
// `${toolName}.e2e.spec.ts`,
|
||||||
@@ -125,15 +141,17 @@ import { expect, describe, it } from 'vitest';
|
|||||||
// `
|
// `
|
||||||
// )
|
// )
|
||||||
|
|
||||||
const toolsIndex = join(toolsDir, 'index.ts')
|
const toolsIndex = join(toolsDir, 'index.ts');
|
||||||
const indexContent = await readFile(toolsIndex, { encoding: 'utf-8' }).then(
|
const indexContent = await readFile(toolsIndex, { encoding: 'utf-8' }).then(
|
||||||
(r) => r.split('\n')
|
(r) => r.split('\n')
|
||||||
)
|
);
|
||||||
|
|
||||||
indexContent.splice(
|
indexContent.splice(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
`import { tool as ${type}${capitalizeFirstLetter(toolNameCamelCase)} } from './${toolName}/meta';`
|
`import { tool as ${type}${capitalizeFirstLetter(
|
||||||
)
|
toolNameCamelCase
|
||||||
writeFile(toolsIndex, indexContent.join('\n'))
|
)} } from './${toolName}/meta';`
|
||||||
console.log(`Added import in: ${toolsIndex}`)
|
);
|
||||||
|
writeFile(toolsIndex, indexContent.join('\n'));
|
||||||
|
console.log(`Added import in: ${toolsIndex}`);
|
||||||
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
@@ -71,10 +71,12 @@ export default function Hero() {
|
|||||||
{...params}
|
{...params}
|
||||||
fullWidth
|
fullWidth
|
||||||
placeholder={'Search all tools'}
|
placeholder={'Search all tools'}
|
||||||
sx={{ borderRadius: 2 }}
|
|
||||||
InputProps={{
|
InputProps={{
|
||||||
...params.InputProps,
|
...params.InputProps,
|
||||||
endAdornment: <SearchIcon />
|
endAdornment: <SearchIcon />,
|
||||||
|
sx: {
|
||||||
|
borderRadius: 4
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onChange={(event) => handleInputChange(event, event.target.value)}
|
onChange={(event) => handleInputChange(event, event.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -112,7 +114,8 @@ export default function Hero() {
|
|||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
borderColor: 'grey',
|
borderColor: 'grey',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
cursor: 'pointer'
|
cursor: 'pointer',
|
||||||
|
'&:hover': { backgroundColor: '#FAFAFD' }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{tool.label}</Typography>
|
<Typography>{tool.label}</Typography>
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import MenuIcon from '@mui/icons-material/Menu';
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import githubIcon from '@assets/github-mark.png'; // Adjust the path to your GitHub icon
|
import logo from 'assets/logo.png';
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
List,
|
List,
|
||||||
@@ -22,7 +21,6 @@ const Navbar: React.FC = () => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||||
|
|
||||||
const toggleDrawer = (open: boolean) => () => {
|
const toggleDrawer = (open: boolean) => () => {
|
||||||
setDrawerOpen(open);
|
setDrawerOpen(open);
|
||||||
};
|
};
|
||||||
@@ -55,17 +53,12 @@ const Navbar: React.FC = () => {
|
|||||||
style={{ backgroundColor: 'white', color: 'black' }}
|
style={{ backgroundColor: 'white', color: 'black' }}
|
||||||
>
|
>
|
||||||
<Toolbar sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
<Toolbar sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<Typography
|
<img
|
||||||
onClick={() => navigate('/')}
|
onClick={() => navigate('/')}
|
||||||
fontSize={25}
|
style={{ cursor: 'pointer' }}
|
||||||
sx={{
|
src={logo}
|
||||||
cursor: 'pointer',
|
width={isMobile ? '80px' : '150px'}
|
||||||
textShadow: '1px 1px 2px rgba(0,0,0,0.2)'
|
/>
|
||||||
}}
|
|
||||||
color={'primary'}
|
|
||||||
>
|
|
||||||
OmniTools
|
|
||||||
</Typography>
|
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
<>
|
<>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
86
src/pages/home/Categories.tsx
Normal file
86
src/pages/home/Categories.tsx
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { getToolsByCategory } from '@tools/index';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import { Card, CardContent, Stack } from '@mui/material';
|
||||||
|
import { HugeiconsIcon } from '@hugeicons/react';
|
||||||
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
type ArrayElement<ArrayType extends readonly unknown[]> =
|
||||||
|
ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
|
||||||
|
|
||||||
|
const SingleCategory = function ({
|
||||||
|
category,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
category: ArrayElement<ReturnType<typeof getToolsByCategory>>;
|
||||||
|
index: number;
|
||||||
|
}) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [hovered, setHovered] = useState<boolean>(false);
|
||||||
|
const Icon = category.icon;
|
||||||
|
const toggleHover = () => setHovered((prevState) => !prevState);
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
onMouseEnter={toggleHover}
|
||||||
|
onMouseLeave={toggleHover}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: hovered ? '#FAFAFD' : 'white'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Stack direction={'row'} spacing={2} alignItems={'center'}>
|
||||||
|
<HugeiconsIcon
|
||||||
|
icon={Icon}
|
||||||
|
style={{
|
||||||
|
transform: `scale(${hovered ? 1.1 : 1}`
|
||||||
|
}}
|
||||||
|
color={categoriesColors[index % categoriesColors.length]}
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
style={{ fontSize: 20, fontWeight: 700, color: 'black' }}
|
||||||
|
to={'/categories/' + category.type}
|
||||||
|
>
|
||||||
|
{category.title}
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
<Typography sx={{ mt: 2 }}>{category.description}</Typography>
|
||||||
|
<Grid mt={1} container spacing={2}>
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
onClick={() => navigate('/categories/' + category.type)}
|
||||||
|
variant={'contained'}
|
||||||
|
>{`See all ${category.title}`}</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Button
|
||||||
|
sx={{ backgroundColor: 'white' }}
|
||||||
|
fullWidth
|
||||||
|
onClick={() => navigate(category.example.path)}
|
||||||
|
variant={'outlined'}
|
||||||
|
>{`Try ${category.example.title}`}</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const categoriesColors: string[] = ['#8FBC5D', '#3CB6E2', '#FFD400', '#AB6993'];
|
||||||
|
export default function Categories() {
|
||||||
|
return (
|
||||||
|
<Grid width={'80%'} container mt={2} spacing={2}>
|
||||||
|
{getToolsByCategory().map((category, index) => (
|
||||||
|
<SingleCategory key={category.type} category={category} index={index} />
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,14 +1,8 @@
|
|||||||
import { Box, Card, CardContent } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import Grid from '@mui/material/Grid';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
|
||||||
import { getToolsByCategory } from '../../tools';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import Hero from 'components/Hero';
|
import Hero from 'components/Hero';
|
||||||
|
import Categories from './Categories';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
padding={{ xs: 1, md: 3, lg: 5 }}
|
padding={{ xs: 1, md: 3, lg: 5 }}
|
||||||
@@ -19,39 +13,7 @@ export default function Home() {
|
|||||||
width={'100%'}
|
width={'100%'}
|
||||||
>
|
>
|
||||||
<Hero />
|
<Hero />
|
||||||
<Grid width={'80%'} container mt={2} spacing={2}>
|
<Categories />
|
||||||
{getToolsByCategory().map((category) => (
|
|
||||||
<Grid key={category.type} item xs={12} md={6}>
|
|
||||||
<Card sx={{ height: '100%' }}>
|
|
||||||
<CardContent>
|
|
||||||
<Link
|
|
||||||
style={{ fontSize: 20 }}
|
|
||||||
to={'/categories/' + category.type}
|
|
||||||
>
|
|
||||||
{category.title}
|
|
||||||
</Link>
|
|
||||||
<Typography sx={{ mt: 2 }}>{category.description}</Typography>
|
|
||||||
<Grid mt={1} container spacing={2}>
|
|
||||||
<Grid item xs={12} md={6}>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
onClick={() => navigate('/categories/' + category.type)}
|
|
||||||
variant={'contained'}
|
|
||||||
>{`See all ${category.title}`}</Button>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={6}>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
onClick={() => navigate(category.example.path)}
|
|
||||||
variant={'outlined'}
|
|
||||||
>{`Try ${category.example.title}`}</Button>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,9 @@ export const listTools = [
|
|||||||
listFindUnique,
|
listFindUnique,
|
||||||
listFindMostPopular,
|
listFindMostPopular,
|
||||||
listGroup,
|
listGroup,
|
||||||
listWrap,
|
// listWrap,
|
||||||
listRotate,
|
listRotate,
|
||||||
listShuffle,
|
listShuffle
|
||||||
listTruncate,
|
// listTruncate,
|
||||||
listDuplicate
|
// listDuplicate
|
||||||
];
|
];
|
||||||
|
@@ -11,11 +11,11 @@ import { tool as stringJoin } from './join/meta';
|
|||||||
export const stringTools = [
|
export const stringTools = [
|
||||||
stringSplit,
|
stringSplit,
|
||||||
stringJoin,
|
stringJoin,
|
||||||
stringToMorse,
|
stringToMorse
|
||||||
stringReverse,
|
// stringReverse,
|
||||||
stringRandomizeCase,
|
// stringRandomizeCase,
|
||||||
stringUppercase,
|
// stringUppercase,
|
||||||
stringExtractSubstring,
|
// stringExtractSubstring,
|
||||||
stringCreatePalindrome,
|
// stringCreatePalindrome,
|
||||||
stringPalindrome
|
// stringPalindrome
|
||||||
];
|
];
|
||||||
|
@@ -6,42 +6,56 @@ import { numberTools } from '../pages/tools/number';
|
|||||||
import { videoTools } from '../pages/tools/video';
|
import { videoTools } from '../pages/tools/video';
|
||||||
import { listTools } from '../pages/tools/list';
|
import { listTools } from '../pages/tools/list';
|
||||||
import { Entries } from 'type-fest';
|
import { Entries } from 'type-fest';
|
||||||
|
import {
|
||||||
|
ArrangeByNumbers19Icon,
|
||||||
|
Gif01Icon,
|
||||||
|
HugeiconsIcon,
|
||||||
|
LeftToRightListBulletIcon,
|
||||||
|
Png01Icon,
|
||||||
|
TextIcon
|
||||||
|
} from '@hugeicons/core-free-icons';
|
||||||
|
|
||||||
export const tools: DefinedTool[] = [
|
export const tools: DefinedTool[] = [
|
||||||
...imageTools,
|
...imageTools,
|
||||||
...stringTools,
|
...stringTools,
|
||||||
...numberTools,
|
...listTools,
|
||||||
...videoTools,
|
...videoTools,
|
||||||
...listTools
|
...numberTools
|
||||||
];
|
];
|
||||||
const categoriesConfig: {
|
const categoriesConfig: {
|
||||||
type: ToolCategory;
|
type: ToolCategory;
|
||||||
value: string;
|
value: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
icon: typeof HugeiconsIcon;
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: 'Text',
|
title: 'Text',
|
||||||
|
icon: TextIcon,
|
||||||
value:
|
value:
|
||||||
'Tools for working with text – convert text to images, find and replace text, split text into fragments, join text lines, repeat text, and much more.'
|
'Tools for working with text – convert text to images, find and replace text, split text into fragments, join text lines, repeat text, and much more.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'png',
|
type: 'png',
|
||||||
|
icon: Png01Icon,
|
||||||
value:
|
value:
|
||||||
'Tools for working with PNG images – convert PNGs to JPGs, create transparent PNGs, change PNG colors, crop, rotate, resize PNGs, and much more.'
|
'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',
|
type: 'number',
|
||||||
|
icon: ArrangeByNumbers19Icon,
|
||||||
value:
|
value:
|
||||||
'Tools for working with numbers – generate number sequences, convert numbers to words and words to numbers, sort, round, factor numbers, and much more.'
|
'Tools for working with numbers – generate number sequences, convert numbers to words and words to numbers, sort, round, factor numbers, and much more.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'gif',
|
type: 'gif',
|
||||||
|
icon: Gif01Icon,
|
||||||
value:
|
value:
|
||||||
'Tools for working with GIF animations – create transparent GIFs, extract GIF frames, add text to GIF, crop, rotate, reverse GIFs, and much more.'
|
'Tools for working with GIF animations – create transparent GIFs, extract GIF frames, add text to GIF, crop, rotate, reverse GIFs, and much more.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'list',
|
type: 'list',
|
||||||
|
icon: LeftToRightListBulletIcon,
|
||||||
value:
|
value:
|
||||||
'Tools for working with lists – sort, reverse, randomize lists, find unique and duplicate list items, change list item separators, and much more.'
|
'Tools for working with lists – sort, reverse, randomize lists, find unique and duplicate list items, change list item separators, and much more.'
|
||||||
}
|
}
|
||||||
@@ -68,6 +82,7 @@ export const filterTools = (
|
|||||||
export const getToolsByCategory = (): {
|
export const getToolsByCategory = (): {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
icon: typeof HugeiconsIcon;
|
||||||
type: string;
|
type: string;
|
||||||
example: { title: string; path: string };
|
example: { title: string; path: string };
|
||||||
tools: DefinedTool[];
|
tools: DefinedTool[];
|
||||||
@@ -76,14 +91,14 @@ export const getToolsByCategory = (): {
|
|||||||
Object.groupBy(tools, ({ type }) => type);
|
Object.groupBy(tools, ({ type }) => type);
|
||||||
return (Object.entries(groupedByType) as Entries<typeof groupedByType>).map(
|
return (Object.entries(groupedByType) as Entries<typeof groupedByType>).map(
|
||||||
([type, tools]) => {
|
([type, tools]) => {
|
||||||
|
const categoryConfig = categoriesConfig.find(
|
||||||
|
(config) => config.type === type
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
title: `${
|
title: `${categoryConfig?.title ?? capitalizeFirstLetter(type)} Tools`,
|
||||||
categoriesConfig.find((config) => config.type === type)?.title ??
|
description: categoryConfig?.value ?? '',
|
||||||
capitalizeFirstLetter(type)
|
|
||||||
} Tools`,
|
|
||||||
description:
|
|
||||||
categoriesConfig.find((desc) => desc.type === type)?.value ?? '',
|
|
||||||
type,
|
type,
|
||||||
|
icon: categoryConfig!.icon,
|
||||||
tools: tools ?? [],
|
tools: tools ?? [],
|
||||||
example: tools
|
example: tools
|
||||||
? { title: tools[0].name, path: tools[0].path }
|
? { title: tools[0].name, path: tools[0].path }
|
||||||
|
Reference in New Issue
Block a user