├── .all-contributorsrc ├── .deepsource.toml ├── .env.example ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ └── feature.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── codeql-analysis.yml │ ├── commitlint.yml │ └── lint.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc.json ├── .snyk ├── .versionrc.json ├── @types └── html-to-jsx.d.ts ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── components ├── Common │ ├── ChangeColorModeButton.tsx │ ├── CopyIconButton.tsx │ ├── EditableControls.tsx │ ├── ExportAsPng.tsx │ ├── FileUpload.tsx │ └── Socials.tsx ├── HomeComponent │ ├── Tool.tsx │ └── index.tsx ├── SearchModal │ ├── ResultLink.tsx │ └── index.tsx ├── SidebarContent │ ├── CategoryComponent.tsx │ ├── SidebarLink.tsx │ └── index.tsx └── Tools │ ├── Color │ ├── ColorContrastChecker │ │ └── index.tsx │ └── GradientGenerator │ │ ├── CSSModal.tsx │ │ ├── Color.tsx │ │ ├── ExportAsUrl.tsx │ │ ├── GradientPreview.tsx │ │ └── index.tsx │ ├── Converters │ ├── Base64EncoderDecoder │ │ └── index.tsx │ ├── HtmlToJsx │ │ └── index.tsx │ ├── JsonYaml │ │ └── index.tsx │ └── UnixTime │ │ └── index.tsx │ ├── Generators │ └── RandomString │ │ └── index.tsx │ ├── Image │ └── ImageResizer │ │ └── index.tsx │ ├── Testers │ └── RegexTester │ │ └── index.tsx │ └── Video │ ├── GIFToMP4 │ └── index.tsx │ └── MP4ToGIF │ └── index.tsx ├── data ├── categories.ts ├── tools.ts ├── tools │ ├── color │ │ ├── colorContrastChecker.ts │ │ └── gradeintGenerator.ts │ ├── converters │ │ ├── HtmlToJsx.ts │ │ ├── JsonYaml.ts │ │ ├── UnixTime.ts │ │ └── base64EncoderDecoder.ts │ ├── generators │ │ └── randomString.ts │ ├── images │ │ └── imageResizer.tsx │ ├── testers │ │ └── regexTester.ts │ └── video │ │ ├── gifToMP4.ts │ │ └── mp4ToGif.ts └── types.ts ├── layouts └── WithSidebar │ └── index.tsx ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── [...slug].tsx ├── _app.tsx ├── _document.tsx ├── about.tsx ├── api │ └── hello.ts └── index.tsx ├── public ├── favicon.ico ├── logo-128x128.png ├── logo-128x128.svg ├── logo-192x192.png ├── logo-192x192.svg ├── logo-384x384.png ├── logo-384x384.svg ├── logo-512x512.png ├── logo-512x512.svg ├── manifest.json ├── maskable_icon_x128.png ├── maskable_icon_x192.png ├── maskable_icon_x384.png ├── maskable_icon_x512.png ├── og.jpg ├── osdd.xml ├── robots.txt └── vercel.svg ├── scripts └── prepare.js ├── seo.config.js ├── styles ├── globals.css ├── icons │ └── DevKitLogo.tsx └── theme.ts ├── tsconfig.json ├── utils ├── capitalize.ts ├── getAspectRatio.ts ├── getImageDimensions.ts ├── html-to-jsx.js ├── json-yaml.ts ├── range.ts ├── testDataTypes │ ├── base64.ts │ ├── hex.ts │ ├── index.ts │ ├── regex.ts │ └── text.ts └── unixtime.ts └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "gouravkhunger", 10 | "name": "Gourav Khunger", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/46792249?v=4", 12 | "profile": "https://gouravkhunger.xyz", 13 | "contributions": [ 14 | "content" 15 | ] 16 | }, 17 | { 18 | "login": "avneesh0612", 19 | "name": "Avneesh Agarwal", 20 | "avatar_url": "https://avatars.githubusercontent.com/u/76690419?v=4", 21 | "profile": "https://www.avneesh.tech/", 22 | "contributions": [ 23 | "code", 24 | "doc" 25 | ] 26 | }, 27 | { 28 | "login": "krishguptadev", 29 | "name": "Krish Gupta", 30 | "avatar_url": "https://avatars.githubusercontent.com/u/91655303?v=4", 31 | "profile": "https://krishguptadev.tech", 32 | "contributions": [ 33 | "infra" 34 | ] 35 | }, 36 | { 37 | "login": "diganta413", 38 | "name": "Diganta Ghosh", 39 | "avatar_url": "https://avatars.githubusercontent.com/u/69595396?v=4", 40 | "profile": "https://github.com/diganta413", 41 | "contributions": [ 42 | "tool" 43 | ] 44 | }, 45 | { 46 | "login": "kr-anurag", 47 | "name": "Anurag", 48 | "avatar_url": "https://avatars.githubusercontent.com/u/77309809?v=4", 49 | "profile": "http://twitter.com/kr_anurag_", 50 | "contributions": [ 51 | "projectManagement" 52 | ] 53 | }, 54 | { 55 | "login": "Panquesito7", 56 | "name": "David Leal", 57 | "avatar_url": "https://avatars.githubusercontent.com/u/51391473?v=4", 58 | "profile": "https://stackoverflow.com/story/panquesito7", 59 | "contributions": [ 60 | "doc" 61 | ] 62 | }, 63 | { 64 | "login": "shirou", 65 | "name": "shirou", 66 | "avatar_url": "https://avatars.githubusercontent.com/u/177213?v=4", 67 | "profile": "http://tdoc.info/en/blog/", 68 | "contributions": [ 69 | "code" 70 | ] 71 | } 72 | ], 73 | "contributorsPerLine": 7, 74 | "projectName": "devkit", 75 | "projectOwner": "AnishDe12020", 76 | "repoType": "github", 77 | "repoHost": "https://github.com", 78 | "skipCi": true 79 | } 80 | -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "javascript" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | plugins = ["react"] -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_UMAMI_ENABLE=false # SET THIS TO "true" TO ENABLE ANALYTICS. If you set this to true, be sure to setup Umami and fill in the script url and website id enviroment variables 2 | NEXT_PUBLIC_UMAMI_SCRIPT_URL= # Your Umami script URL 3 | NEXT_PUBLIC_UMAMI_WEBSITE_ID= # Your Umami Website ID -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .next 2 | next-env.d.ts 3 | node_modules 4 | yarn.lock 5 | package-lock.json 6 | public 7 | .github -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "next/core-web-vitals", 4 | "google", 5 | "prettier", 6 | "eslint:recommended", 7 | "plugin:react/recommended" 8 | ], 9 | "rules": { 10 | "react/react-in-jsx-scope": "off", 11 | "object-curly-spacing": [2, "always"], 12 | "quotes": [2, "double", { "avoidEscape": true }], 13 | "no-undef": "off", 14 | "require-jsdoc": "off", 15 | "new-cap": "off" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: 🐛Bug Report 2 | description: File a bug report here 3 | title: "[BUG]: " 4 | labels: ["bug"] 5 | assignees: ["AnishDe12020"] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Before opening a bug report, please search for the behaviour in the existing issues. 11 | 12 | --- 13 | 14 | Thank you for taking the time to file a bug report. To address this bug as fast as possible, we need some information. 15 | - type: input 16 | id: browser 17 | attributes: 18 | label: Broswer 19 | description: "Which Web Browser do you use? Please provide the version as well." 20 | placeholder: "Google Chrome 94" 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: current-behavior 25 | attributes: 26 | label: Current Behaviour 27 | description: What happened? 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: expected-behaviour 32 | attributes: 33 | label: Expected Behaviour 34 | description: What should be the expected behaviour? 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: steps 39 | attributes: 40 | label: Steps to reproduce 41 | description: Which steps do we need to take to reproduce this error? 42 | placeholder: | 43 | 1. Go to '...' 44 | 2. Click on '...' 45 | 3. Scroll down to '...' 46 | 4. See error 47 | validations: 48 | required: true 49 | - type: textarea 50 | id: additional-info 51 | attributes: 52 | label: Additional Information 53 | description: You can attach screenshots, screen recording, logs, or anything related 54 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: ✨Feature Request 2 | description: Request a new feature or enhancement 3 | labels: ["enhancement"] 4 | title: "[FEAT]: " 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Please make sure this feature request hasn't been already submitted by someone by looking through other open/closed issues 10 | 11 | - type: textarea 12 | id: description 13 | attributes: 14 | label: Description 15 | description: Give us a brief description of the feature or enhancement you would like 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | id: additional-information 21 | attributes: 22 | label: Additional Information 23 | description: Give us some additional information on the feature request like proposed solutions, links, screenshots, etc. 24 | 25 | - type: dropdown 26 | id: type-of-feature 27 | attributes: 28 | label: Type of feature 29 | options: 30 | - "New Tool" 31 | - "New Feature in Tool" 32 | - "New feature for the app as a whole" 33 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | resolves #`issueNumber` 4 | 5 | ## Checklist 6 | 7 | - [ ] You've followed the code structure 8 | - [ ] You've linked the PR to correct Issue 9 | - [ ] Passes the CI pipeline tests (lint, formatting, other ones) 10 | 11 | ## Proposed Changes 12 | 13 | 16 | 17 | ## Additonal Info 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '28 20 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: [pull_request, push] 3 | 4 | jobs: 5 | commitlint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | with: 10 | fetch-depth: 0 11 | - uses: wagoid/commitlint-github-action@v4 12 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | # Trigger the workflow on push or pull request, 5 | # but only for the main branch 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | run-linters: 15 | name: Run linters 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Check out Git repository 20 | uses: actions/checkout@v2 21 | 22 | - name: Set up Node.js 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: 12 26 | 27 | # ESLint and Prettier must be in `package.json` 28 | - name: Install Node.js dependencies 29 | run: yarn ci 30 | 31 | - name: Run linters 32 | run: | 33 | yarn lint 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # PWA files 37 | **/public/sw.js 38 | **/public/workbox-*.js 39 | **/public/worker-*.js 40 | **/public/sw.js.map 41 | **/public/workbox-*.js.map 42 | **/public/worker-*.js.map 43 | 44 | # Typescript 45 | tsconfig.tsbuildinfo -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | echo "💻 Running pre-commit hook..." 5 | 6 | # Check Prettier standards 7 | yarn format || 8 | ( 9 | echo "🚨 Prettier check failed. Run `yarn format:fix`, add and commit the changes."; 10 | false; 11 | ) 12 | 13 | # Check ESLint standards 14 | yarn lint || 15 | ( 16 | echo "🚨 ESLint check failed. Run `yarn lint:fix`, add and commit the changes. Note that some things cannot be fixed my eslint automatically so you might have to fix those on your own."; 17 | false; 18 | ) 19 | 20 | # Check tsconfig standards 21 | yarn types:check || 22 | ( 23 | echo "🚨 Type check failed. Please make the changes required above, add and commit the changes"; 24 | false; 25 | ) 26 | 27 | echo "✅ All check passed 28 | Committing..." -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .next 2 | next-env.d.ts 3 | node_modules 4 | yarn.lock 5 | package-lock.json 6 | public 7 | .github 8 | README.md 9 | CHANGELOG.md -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "arrowParens": "avoid", 4 | "tabWidth": 2, 5 | "singleQuote": false 6 | } 7 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.22.1 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | 'npm:lodash:20180130': 7 | - html-to-jsx > cheerio > lodash: 8 | patched: '2021-12-12T09:58:23.105Z' 9 | -------------------------------------------------------------------------------- /.versionrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "types": [ 3 | { "type": "feat", "section": "Features" }, 4 | { "type": "fix", "section": "Bug Fixes" }, 5 | { "type": "chore", "hidden": true }, 6 | { "type": "docs", "hidden": true }, 7 | { "type": "style", "hidden": true }, 8 | { "type": "refactor", "hidden": true }, 9 | { "type": "perf", "hidden": true }, 10 | { "type": "test", "hidden": true } 11 | ], 12 | "commitUrlFormat": "https://github.com/AnishDe12020/devkit/commits/{{hash}}", 13 | "compareUrlFormat": "https://github.com/AnishDe12020/devkit/compare/{{previousTag}}...{{currentTag}}" 14 | } 15 | -------------------------------------------------------------------------------- /@types/html-to-jsx.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'html-to-jsx'{}; 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [0.3.0](https://github.com/AnishDe12020/devkit/compare/v0.2.0...v0.3.0) (2021-11-20) 6 | 7 | 8 | ### Features 9 | 10 | * **homepage:** added links to all tools ([44954a4](https://github.com/AnishDe12020/devkit/commits/44954a4320edeb465352494cdb65d018555ceb43)) 11 | * **image-resizer:** added export as png ([3619dec](https://github.com/AnishDe12020/devkit/commits/3619dec82654207c3fa85c144d1d9c8c2e2f231d)) 12 | * **image-resizer:** added validation for width and height when resizing ([67177fe](https://github.com/AnishDe12020/devkit/commits/67177fefd6dca806fed6c7aec8b2ee0252f95551)) 13 | * **mp4-gif:** added a download gif button to download the gif file ([be4442d](https://github.com/AnishDe12020/devkit/commits/be4442dc1a75c0ddc0ed1ef9c258b04193c70596)) 14 | * **mp4-gif:** added an info alert showing the use of ffmpeg ([abc2ece](https://github.com/AnishDe12020/devkit/commits/abc2ecec976b9ea65c11355d4805c3c031bf1396)) 15 | * **mp4-gif:** added progress bar and conversion logs ([b7bee22](https://github.com/AnishDe12020/devkit/commits/b7bee2231a7c0f8826d9a4a535a95f2c9ed4a4dc)) 16 | * **mp4-gif:** disabled convert to gif button if video not there or ffmpeg not ready ([23badd0](https://github.com/AnishDe12020/devkit/commits/23badd0abfce1ffe0b20275c6735a0f0237e85f6)) 17 | * **search:** added a basic search for tools using fuse.js ([3fc1cfa](https://github.com/AnishDe12020/devkit/commits/3fc1cfa9cc28733d2b043a039b8abba27dbfa5ae)) 18 | * **tool:** new mp4 to gif converter ([4ef00a6](https://github.com/AnishDe12020/devkit/commits/4ef00a6cfa175638d9655c8f842ebe70d1da2d32)) 19 | * **tool:** new tool - images resizer ([2b9e73f](https://github.com/AnishDe12020/devkit/commits/2b9e73f58b8d5ac0875c7c59ff9a2dbcfc3149b8)) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * **color-mode:** the previous color mode is used as the default color mode and if not present system is used instead of always defaulting to dark ([c1ee7bb](https://github.com/AnishDe12020/devkit/commits/c1ee7bbd1e63ebead1672f2c725115abfcaaaed4)) 25 | * **homepage:** removed description title tag from tool and passed in otherprops to button ([b894d71](https://github.com/AnishDe12020/devkit/commits/b894d7165fe4d8d8ceb00ef2168c41f2fc0425aa)) 26 | * **image-resizer:** fixed images overflowing on mobile ([2bcb6a5](https://github.com/AnishDe12020/devkit/commits/2bcb6a50ce5110b650acf6af65e3af850bfbbff4)) 27 | * **image-resizer:** imported export as png dynamically ([4b1a3ab](https://github.com/AnishDe12020/devkit/commits/4b1a3abf5d3623bedbfa61e636fb328e8d450e8c)) 28 | * **image-resizer:** resize opration and export as png operation is disabled in certain cases ([09642c1](https://github.com/AnishDe12020/devkit/commits/09642c13ddb72687e9cc95c6f9195f6fee2d0303)) 29 | * **image-resizer:** switched to a better image dimension algorithm ([faa5cdc](https://github.com/AnishDe12020/devkit/commits/faa5cdc9cbe770a6a908db9f6b143a7a4a441560)) 30 | * **mp4-gif:** downgraded ffmpeg due to an error in dev environment ([f3fca0a](https://github.com/AnishDe12020/devkit/commits/f3fca0a43d8d97a126524476152eaf1461f69eed)) 31 | * **mp4-gif:** fixed the button not being disabled ([a0e3833](https://github.com/AnishDe12020/devkit/commits/a0e3833e71cf5b8c4517903088aaa486571bf566)) 32 | * package.json & yarn.lock to reduce vulnerabilities ([4992385](https://github.com/AnishDe12020/devkit/commits/4992385e5dc83cf9a84fed83a286e47ab3f9012f)) 33 | * **sidebar:** fixed color mode toggle getting ref upon open instead of close button ([ede33f3](https://github.com/AnishDe12020/devkit/commits/ede33f3416959e99198bf202839116eb72f19825)) 34 | * **sidebar:** fixed sidebar drawer not closing when navigating to a different page ([de202b9](https://github.com/AnishDe12020/devkit/commits/de202b944e5a6a29f61e4930f2064eca5948be13)) 35 | * **typo:** fixed typo in alert in mp4-gif ([a526505](https://github.com/AnishDe12020/devkit/commits/a526505896e2622c5d393824467e88320e4e5a6f)) 36 | * upgrade @ffmpeg/core from 0.8.5 to 0.10.0 ([96ccc6d](https://github.com/AnishDe12020/devkit/commits/96ccc6db31b14f313e56bd4404b84975934d9dd1)) 37 | * upgrade @ffmpeg/ffmpeg from 0.9.8 to 0.10.1 ([c8d07f1](https://github.com/AnishDe12020/devkit/commits/c8d07f1f5d74f183d11c2af94c740a86b10f894e)) 38 | 39 | ## [0.2.0](https://github.com/AnishDe12020/devkit/compare/v0.1.1...v0.2.0) (2021-11-13) 40 | 41 | 42 | ### Features 43 | 44 | * **sidebar:** added a close button to the sidebar in mobile view and it now spans across the screen ([230c391](https://github.com/AnishDe12020/devkit/commits/230c391edce6eec58504381b680e6754d9e66595)) 45 | * **sidebar:** took a more responsive and better approach by using a sidebar layout ([ba9746d](https://github.com/AnishDe12020/devkit/commits/ba9746dfa74dc49a764a4b66e3fade75ade184d5)) 46 | * **sidebar:** tool names show up on the header instead of inside the tool ([a3cda33](https://github.com/AnishDe12020/devkit/commits/a3cda33a9e4391f3b041d5a5f41318ed27e6eed7)) 47 | 48 | ### [0.1.1](https://github.com/AnishDe12020/devkit/compare/v0.1.0...v0.1.1) (2021-11-12) 49 | 50 | 51 | ### Features 52 | 53 | * **pwa:** added more meta tags for better compatiblity ([e8f5595](https://github.com/AnishDe12020/devkit/commits/e8f5595927fea9222d9e74a35c2fe5455fa2aa43)) 54 | 55 | 56 | ### Bug Fixes 57 | 58 | * **nextjs:** disabled esmModules to get rid of the occassional out of memory heap error ([3ae2b95](https://github.com/AnishDe12020/devkit/commits/3ae2b952e625f78bdcc149a4d71fc36149a7dcfc)) 59 | 60 | ## 0.1.0 (2021-11-12) 61 | 62 | This is the first release using standard-version. Previous logs are not available. 63 | 64 | ### Bug Fixes 65 | 66 | - **ci:** added yarn ci script ([1521ac9](https://github.com/AnishDe12020/devkit/commits/1521ac987ca0378754333976b0a45d4ab37061ba)) 67 | - **ci:** ignored prettier checks for README and formatted commitlint config properly ([e5b97ca](https://github.com/AnishDe12020/devkit/commits/e5b97ca0e25c8401e189ba4aa15871f1045dc140)) 68 | - **ci:** switched to yarn for installing ci dependencies ([f23d801](https://github.com/AnishDe12020/devkit/commits/f23d8012fdc9f5f5fb4a455acc3caab6b55b299f)) 69 | - drawer wouldn't span across the whole screen in gradient generator route ([7bb3f37](https://github.com/AnishDe12020/devkit/commits/7bb3f3703c58a89055746f3b811919adaa1606ea)) 70 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | contact@devkit.one. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevKit 2 | 3 | [![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) 4 | 5 | 6 | **Important: DevKit is no longer actively maintained. Bug reports and feature requests are still welcome but I cannot gurantee if they will be fixed/implemented.** 7 | 8 | **However, pull requests are welcome and will be merged once they look fine.** 9 | 10 | **Dependencies will be upgraded regularly (dependabot and snyk automatically opens pull requests for this)** 11 | 12 | DevKit is a simple progressive-web-application with tools a developer needs when coding. The project is completely free and open-source and does not need one to sign-up or provide it with any data, in fact we don't have a backend!!! 13 | 14 | > Note: This project is in a very early stage of development with only a few tools as of now but the core developer, that is me and contributors are making it better day-by-day 15 | 16 | # Contributing 17 | First let us go through the project structure - 18 | ``` 19 | /components 20 | /Tools 21 | / 22 | / 23 | /data 24 | /categories.ts 25 | /tools 26 | / 27 | /pages 28 | /[...slug].tsx // This is the page where all the tools live. The tool details are obtained from the slug and the respective component for that tool is dynamically imported and shown on the right-side of the sidebar 29 | ``` 30 | 31 | The `data` folder components metadata about all the tools and tool categories. 32 | The `components` folder contains some universal components for the apps and tool-specific components. There is one master component per tool which is mounted to the page when the tool is requested for. 33 | 34 | When adding a new tool, firstly put in the metadata of the tool in the `data/tools/` directory by creating a typescript file (javascript is allowed but will be converted into typescript sooner or later by a maintainer or contributor and is usually not recommended) there. Then the `categories.ts` file needs to be filled up (refer to the other categories to understand. This will be soon documented in deep.). Now, a master component needs to be made. `components/Tools//` is the syntax. Note that the category must match the directory name field in the categories file and the tool must correspond to the `componentFileName` field. 35 | 36 | Before opening a pull request, please manually test your code. 37 | 38 | Happy contributing!!! 39 | ## Contributors ✨ 40 | 41 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |

Gourav Khunger

🖋

Avneesh Agarwal

💻 📖

Krish Gupta

🚇

Diganta Ghosh

🔧

Anurag

📆

David Leal

📖

shirou

💻
57 | 58 | 59 | 60 | 61 | 62 | 63 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 64 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | rules: { 4 | "header-max-length": [2, "always", 250], 5 | "body-max-line-length": [2, "always", 150], 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /components/Common/ChangeColorModeButton.tsx: -------------------------------------------------------------------------------- 1 | import { IconButton, IconButtonProps, useColorMode } from "@chakra-ui/react"; 2 | import { FiMoon, FiSun } from "react-icons/fi"; 3 | 4 | const ChangeColorModeButton = (props: IconButtonProps) => { 5 | const { colorMode, toggleColorMode } = useColorMode(); 6 | 7 | return ( 8 | : } 11 | {...props} 12 | /> 13 | ); 14 | }; 15 | 16 | export default ChangeColorModeButton; 17 | -------------------------------------------------------------------------------- /components/Common/CopyIconButton.tsx: -------------------------------------------------------------------------------- 1 | interface CopyIconButtonProps { 2 | onCopy: () => void; 3 | hasCopied: boolean; 4 | ariaLabel?: string; 5 | [x: string]: any; 6 | } 7 | 8 | import { IconButton } from "@chakra-ui/react"; 9 | import { FiCopy, FiCheck } from "react-icons/fi"; 10 | 11 | const CopyIconButton = (props: CopyIconButtonProps): JSX.Element => { 12 | const { 13 | onCopy, 14 | hasCopied, 15 | ariaLabel = "Copy to Clipboard", 16 | ...otherProps 17 | } = props; 18 | return ( 19 | : } 23 | bg={hasCopied ? "green.500" : undefined} 24 | _hover={{ 25 | bg: hasCopied ? "green.500" : undefined, 26 | }} 27 | {...otherProps} 28 | /> 29 | ); 30 | }; 31 | 32 | export default CopyIconButton; 33 | -------------------------------------------------------------------------------- /components/Common/EditableControls.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ButtonGroup, 3 | IconButton, 4 | Flex, 5 | useEditableControls, 6 | } from "@chakra-ui/react"; 7 | import { FiCheck, FiX, FiEdit } from "react-icons/fi"; 8 | 9 | const EditableControls = () => { 10 | const { 11 | isEditing, 12 | getSubmitButtonProps, 13 | getCancelButtonProps, 14 | getEditButtonProps, 15 | } = useEditableControls(); 16 | 17 | return isEditing ? ( 18 | 19 | } 21 | {...getSubmitButtonProps()} 22 | aria-label="Submit" 23 | /> 24 | } 26 | {...getCancelButtonProps()} 27 | aria-label="Cancel" 28 | /> 29 | 30 | ) : ( 31 | 32 | } 34 | {...getEditButtonProps()} 35 | aria-label="Edit" 36 | /> 37 | 38 | ); 39 | }; 40 | 41 | export default EditableControls; 42 | -------------------------------------------------------------------------------- /components/Common/ExportAsPng.tsx: -------------------------------------------------------------------------------- 1 | import { exportComponentAsPNG } from "react-component-export-image"; 2 | import { Button } from "@chakra-ui/react"; 3 | import { RefObject } from "react"; 4 | 5 | interface ExportAsPngProps { 6 | componentRef: RefObject; 7 | [x: string]: any; 8 | } 9 | 10 | const ExportAsPng = (props: ExportAsPngProps): JSX.Element => { 11 | const { componentRef, ...otherProps } = props; 12 | const handleExport = () => { 13 | exportComponentAsPNG(componentRef, { 14 | fileName: "gradient.png", 15 | }); 16 | }; 17 | 18 | return ( 19 | 22 | ); 23 | }; 24 | 25 | export default ExportAsPng; 26 | -------------------------------------------------------------------------------- /components/Common/FileUpload.tsx: -------------------------------------------------------------------------------- 1 | import { ChangeEvent, useRef } from "react"; 2 | import { InputGroup, Button, Input } from "@chakra-ui/react"; 3 | import { FiUpload } from "react-icons/fi"; 4 | 5 | interface FileUploadProps { 6 | onChange: (event: ChangeEvent) => void; 7 | label: string; 8 | accept?: string; 9 | multiple?: boolean; 10 | [x: string]: any; 11 | } 12 | 13 | const FileUpload = ({ 14 | onChange, 15 | label, 16 | accept = "*", 17 | multiple = false, 18 | ...otherProps 19 | }: FileUploadProps): JSX.Element => { 20 | const inputElRef = useRef(null); 21 | 22 | return ( 23 | 24 | 32 | 40 | 41 | ); 42 | }; 43 | 44 | export default FileUpload; 45 | -------------------------------------------------------------------------------- /components/Common/Socials.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | HStack, 3 | Icon, 4 | Link, 5 | StackProps, 6 | useColorModeValue, 7 | } from "@chakra-ui/react"; 8 | import { SiGithub, SiTwitter } from "react-icons/si"; 9 | import { HiMail } from "react-icons/hi"; 10 | 11 | const Socials = (props: StackProps): JSX.Element => ( 12 | 13 | 18 | 29 | 30 | 31 | 42 | 43 | 44 | 55 | 56 | 57 | ); 58 | 59 | export default Socials; 60 | -------------------------------------------------------------------------------- /components/HomeComponent/Tool.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Link, ButtonProps, useColorModeValue } from "@chakra-ui/react"; 2 | import NextLink from "next/link"; 3 | 4 | interface ToolComponentProps extends ButtonProps { 5 | toolName: string; 6 | href: string; 7 | } 8 | 9 | const Tool = ({ toolName, href, ...otherProps }: ToolComponentProps) => ( 10 | 11 | 27 | 28 | ); 29 | 30 | export default Tool; 31 | -------------------------------------------------------------------------------- /components/HomeComponent/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Grid, chakra, useColorModeValue } from "@chakra-ui/react"; 2 | import DevKitLogo from "@/styles/icons/DevKitLogo"; 3 | import Tool from "./Tool"; 4 | import tools from "@/data/tools"; 5 | 6 | interface HomeComponentProps { 7 | [x: string]: any; 8 | } 9 | 10 | const HomeComponent = (props: HomeComponentProps): JSX.Element => ( 11 | 12 | 17 | 23 | 28 | 29 | 35 | 36 | Tools for{" "} 37 | 38 | 42 | Developers 43 | 44 | 45 | 50 | 51 | By the{" "} 52 | 53 | 57 | Developer Community 58 | 59 | 60 | 61 | 66 | {tools.map(tool => ( 67 | 72 | ))} 73 | 74 | 75 | 76 | 77 | ); 78 | 79 | export default HomeComponent; 80 | -------------------------------------------------------------------------------- /components/SearchModal/ResultLink.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Kbd, useColorModeValue } from "@chakra-ui/react"; 2 | import NextLink from "next/link"; 3 | import { ReactNode } from "react"; 4 | 5 | interface ResultLinkProps { 6 | href: string; 7 | active: boolean; 8 | children: ReactNode; 9 | key?: any; 10 | onClose: () => void; 11 | [key: string]: any; 12 | } 13 | 14 | const ResultLink = ({ 15 | href, 16 | active, 17 | children, 18 | onClose, 19 | ...otherProps 20 | }: ResultLinkProps): JSX.Element => { 21 | const primaryColor = useColorModeValue("green.700", "green.300"); 22 | const secondaryBg = useColorModeValue("green.600", "green.400"); 23 | const secondaryColor = useColorModeValue("white", "gray.800"); 24 | 25 | return ( 26 | 27 | 47 | {children} 48 | {active && Enter} 49 | 50 | 51 | ); 52 | }; 53 | 54 | export default ResultLink; 55 | -------------------------------------------------------------------------------- /components/SearchModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Modal, 3 | ModalOverlay, 4 | ModalContent, 5 | ModalBody, 6 | Input, 7 | useDisclosure, 8 | useEventListener, 9 | Flex, 10 | Button, 11 | HStack, 12 | Kbd, 13 | } from "@chakra-ui/react"; 14 | import tools from "@/data/tools"; 15 | import Fuse from "fuse.js"; 16 | import { useState, useEffect, ChangeEvent, KeyboardEventHandler } from "react"; 17 | import ResultLink from "@/components/SearchModal/ResultLink"; 18 | import router from "next/router"; 19 | import { FiSearch } from "react-icons/fi"; 20 | 21 | const SearchModal = () => { 22 | const { isOpen, onOpen, onClose } = useDisclosure(); 23 | const [query, setQuery] = useState(""); 24 | const [searchResults, setSearchResults] = useState([]); 25 | const [activeSearchResultIndex, setActiveSearchResultIndex] = 26 | useState(0); 27 | 28 | const [isApple, setIsApple] = useState(false); 29 | 30 | useEffect(() => { 31 | setIsApple(/(Mac|iPhone|iPad|iPod)/i.test(navigator.userAgent)); 32 | }, [isApple]); 33 | 34 | useEventListener("keydown", e => { 35 | const hotkey = isApple ? "metaKey" : "ctrlKey"; 36 | if (e.key.toLowerCase() === "k" && e[hotkey]) { 37 | e.preventDefault(); 38 | isOpen ? onClose() : onOpen(); 39 | } 40 | }); 41 | 42 | const handleKeyDown: KeyboardEventHandler = e => { 43 | if (e.key.toLowerCase() === "enter") { 44 | e.preventDefault(); 45 | console.log(searchResults[activeSearchResultIndex]); 46 | const activeResultItem = searchResults[activeSearchResultIndex].item; 47 | 48 | console.log(activeResultItem); 49 | 50 | router.push(`${activeResultItem.categorySlug}/${activeResultItem.slug}`); 51 | onClose(); 52 | } else if (e.key === "ArrowUp") { 53 | e.preventDefault(); 54 | if (activeSearchResultIndex > 0) { 55 | setActiveSearchResultIndex(activeSearchResultIndex - 1); 56 | } 57 | } else if (e.key === "ArrowDown") { 58 | e.preventDefault(); 59 | if (activeSearchResultIndex < searchResults.length - 1) { 60 | setActiveSearchResultIndex(activeSearchResultIndex + 1); 61 | } 62 | } 63 | }; 64 | 65 | const handleQueryChange = (e: ChangeEvent): void => { 66 | setQuery(e.target.value); 67 | }; 68 | 69 | useEffect(() => { 70 | const fuse = new Fuse(tools, { 71 | keys: ["name", "slug", "description"], 72 | }); 73 | 74 | const results = fuse.search(query); 75 | 76 | const resultsWithIndex = results.map((item, index) => { 77 | return { 78 | ...item, 79 | index, 80 | }; 81 | }); 82 | 83 | setSearchResults(resultsWithIndex); 84 | }, [query]); 85 | 86 | return ( 87 | <> 88 | 102 | 103 | 104 | 105 | 106 | 116 | 117 | {searchResults.length > 0 && 118 | searchResults.map(tool => ( 119 | 125 | {tool.item.name} 126 | 127 | ))} 128 | 129 | 130 | 131 | 132 | 133 | ); 134 | }; 135 | 136 | export default SearchModal; 137 | -------------------------------------------------------------------------------- /components/SidebarContent/CategoryComponent.tsx: -------------------------------------------------------------------------------- 1 | import { Category } from "@/data/types"; 2 | import SidebarLink from "@/components/SidebarContent/SidebarLink"; 3 | import { Box, Text, VStack, useColorModeValue } from "@chakra-ui/react"; 4 | import { useRouter } from "next/router"; 5 | 6 | interface CategoryProps { 7 | category: Category; 8 | onClose: () => void; 9 | } 10 | 11 | const CategoryComponent = ({ 12 | category, 13 | onClose, 14 | }: CategoryProps): JSX.Element => { 15 | const categoryTitle = useColorModeValue("gray.800", "gray.200"); 16 | const router = useRouter(); 17 | return ( 18 | 19 | 20 | 26 | {category.name} 27 | 28 | {category.children.map(tool => ( 29 | 35 | {tool.name} 36 | 37 | ))} 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default CategoryComponent; 44 | -------------------------------------------------------------------------------- /components/SidebarContent/SidebarLink.tsx: -------------------------------------------------------------------------------- 1 | import { Link, useColorModeValue } from "@chakra-ui/react"; 2 | import NextLink from "next/link"; 3 | import { ReactNode } from "react"; 4 | 5 | interface SidebarLinkProps { 6 | href: string; 7 | active: boolean; 8 | children: ReactNode; 9 | key?: any; 10 | onClose: () => void; 11 | [key: string]: any; 12 | } 13 | 14 | const SidebarLink = ({ 15 | href, 16 | active, 17 | children, 18 | onClose, 19 | ...otherProps 20 | }: SidebarLinkProps): JSX.Element => { 21 | const primaryColor = useColorModeValue("green.700", "green.300"); 22 | const secondaryBg = useColorModeValue("green.600", "green.400"); 23 | const secondaryColor = useColorModeValue("white", "gray.800"); 24 | 25 | return ( 26 | 27 | 44 | {children} 45 | 46 | 47 | ); 48 | }; 49 | 50 | export default SidebarLink; 51 | -------------------------------------------------------------------------------- /components/SidebarContent/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Flex, 4 | CloseButton, 5 | BoxProps, 6 | useBreakpointValue, 7 | } from "@chakra-ui/react"; 8 | 9 | import { useRouter } from "next/router"; 10 | import { RefObject } from "react"; 11 | import categories from "@/data/categories"; 12 | 13 | import CategoryComponent from "@/components/SidebarContent/CategoryComponent"; 14 | import SidebarLink from "@/components/SidebarContent/SidebarLink"; 15 | import ChangeColorModeButton from "@/components/Common/ChangeColorModeButton"; 16 | import Socials from "@/components/Common/Socials"; 17 | import SearchModal from "@/components/SearchModal"; 18 | 19 | interface SidebarContentProps extends BoxProps { 20 | onClose: () => void; 21 | closeButtonRef?: RefObject; 22 | } 23 | 24 | const SidebarContent = ({ 25 | onClose, 26 | closeButtonRef, 27 | ...otherProps 28 | }: SidebarContentProps): JSX.Element => { 29 | const router = useRouter(); 30 | 31 | return ( 32 | 43 | 50 | 51 | 57 | 58 | 59 | 60 | 67 | Home 68 | 69 | 70 | 71 | {categories.map(category => ( 72 | 77 | ))} 78 | 79 | 80 | ); 81 | }; 82 | 83 | export default SidebarContent; 84 | -------------------------------------------------------------------------------- /components/Tools/Color/ColorContrastChecker/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Grid, 4 | Input, 5 | InputGroup, 6 | Center, 7 | InputRightAddon, 8 | Text, 9 | Editable, 10 | EditablePreview, 11 | EditableInput, 12 | Flex, 13 | Divider, 14 | useColorModeValue, 15 | } from "@chakra-ui/react"; 16 | import { useState } from "react"; 17 | import chroma from "chroma-js"; 18 | import { useRouter } from "next/router"; 19 | 20 | interface IWCAGTestResults { 21 | smallAA: boolean; 22 | largeAA: boolean; 23 | smallAAA: boolean; 24 | largeAAA: boolean; 25 | } 26 | 27 | const ColorContrastChecker = (): JSX.Element => { 28 | const router = useRouter(); 29 | 30 | const [color1, setColor1] = useState( 31 | router?.query.color1 ? "#" + router?.query.color1 : "#000000" 32 | ); 33 | const [color2, setColor2] = useState( 34 | router?.query.color2 ? "#" + router?.query.color2 : "#ffffff" 35 | ); 36 | 37 | const [contrastRatio, setContrastRatio] = useState( 38 | chroma.contrast(color1, color2) 39 | ); 40 | 41 | const [wcagTestResults, setWcagTestResults] = useState({ 42 | smallAA: contrastRatio >= 4.5, 43 | largeAA: contrastRatio >= 3, 44 | smallAAA: contrastRatio >= 7, 45 | largeAAA: contrastRatio >= 4.5, 46 | }); 47 | 48 | const handleColor1Update = (e: React.ChangeEvent): void => { 49 | const value = e.target.value; 50 | setColor1(value); 51 | handleColorUpdate(value, color2); 52 | }; 53 | 54 | const handleColor2Update = (e: React.ChangeEvent): void => { 55 | const value = e.target.value; 56 | setColor2(value); 57 | handleColorUpdate(color1, value); 58 | }; 59 | 60 | const handleColorUpdate = (c1: string, c2: string) => { 61 | const newContrastRatio = chroma.contrast(c1, c2); 62 | setContrastRatio(newContrastRatio); 63 | setWcagTestResults({ 64 | smallAA: newContrastRatio >= 4.5, 65 | largeAA: newContrastRatio >= 3, 66 | smallAAA: newContrastRatio >= 7, 67 | largeAAA: newContrastRatio >= 4.5, 68 | }); 69 | }; 70 | 71 | const wcagTestPassColor = useColorModeValue("green.500", "green.400"); 72 | const wcagTestFailColor = useColorModeValue("red.500", "red.400"); 73 | 74 | return ( 75 | 76 | 77 | 78 | 85 |
86 | {color1} 87 |
88 |
89 | 90 | 97 |
98 | {color2} 99 |
100 |
101 |
102 |
103 | 104 | {contrastRatio.toFixed(2)} 105 | 106 |
107 | 108 |
109 | 110 | WCAG Tests 111 | 112 |
113 | 122 | 123 | 124 | 125 | 126 | 132 | AA-level: {wcagTestResults.smallAA ? "PASS" : "FAIL"} 133 | 134 | 140 | AAA-level: {wcagTestResults.smallAAA ? "PASS" : "FAIL"} 141 | 142 | 143 | 153 | 154 | 155 | 156 | 157 | 163 | AA-level: {wcagTestResults.largeAA ? "PASS" : "FAIL"} 164 | 165 | 171 | AAA-level: {wcagTestResults.largeAAA ? "PASS" : "FAIL"} 172 | 173 | 174 |
175 | ); 176 | }; 177 | 178 | export default ColorContrastChecker; 179 | -------------------------------------------------------------------------------- /components/Tools/Color/GradientGenerator/CSSModal.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Modal, 3 | ModalOverlay, 4 | ModalContent, 5 | ModalHeader, 6 | ModalBody, 7 | ModalCloseButton, 8 | Button, 9 | IconButton, 10 | Flex, 11 | useDisclosure, 12 | useClipboard, 13 | chakra, 14 | } from "@chakra-ui/react"; 15 | import { ReactNode } from "react"; 16 | import { FiDownload } from "react-icons/fi"; 17 | import { saveAs } from "file-saver"; 18 | 19 | import "codemirror/lib/codemirror.css"; 20 | import "codemirror/theme/material.css"; 21 | import "codemirror/mode/css/css"; 22 | import { UnControlled as CodeMirror } from "react-codemirror2"; 23 | import CopyIconButton from "@/components/Common/CopyIconButton"; 24 | 25 | interface CSSModalProps { 26 | children: ReactNode; 27 | colors: string[]; 28 | direction: number; 29 | [x: string]: any; 30 | } 31 | 32 | const ChakraCodeMirror = chakra(CodeMirror, { 33 | baseStyle: { 34 | width: "100%", 35 | height: "100%", 36 | fontSize: "lg", 37 | }, 38 | }); 39 | 40 | const CSSModal = (props: CSSModalProps): JSX.Element => { 41 | const { colors, direction, children, ...otherProps } = props; 42 | const { isOpen, onOpen, onClose } = useDisclosure(); 43 | const css = `background: linear-gradient(${direction}deg, ${colors.join( 44 | ", " 45 | )}); 46 | background: -webkit-linear-gradient(${direction}deg, ${colors.join(", ")}); 47 | background: -o-linear-gradient(${direction}deg, ${colors.join(", ")}); 48 | background: -moz-linear-gradient(${direction}deg, ${colors.join(", ")}); 49 | background: -ms-linear-gradient(${direction}deg, ${colors.join(", ")});`; 50 | 51 | const downloadCSS = () => { 52 | const fileName = "gradient.css"; 53 | const blob = new Blob([css], { type: "text/css" }); 54 | 55 | saveAs(blob, fileName); 56 | }; 57 | 58 | const { hasCopied, onCopy } = useClipboard(css); 59 | return ( 60 | <> 61 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 82 | 83 | 84 | } 86 | aria-label="Download Gradient CSS" 87 | onClick={downloadCSS} 88 | mr={4} 89 | /> 90 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | ); 102 | }; 103 | 104 | export default CSSModal; 105 | -------------------------------------------------------------------------------- /components/Tools/Color/GradientGenerator/Color.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Center, 4 | IconButton, 5 | Input, 6 | InputGroup, 7 | InputRightAddon, 8 | Text, 9 | } from "@chakra-ui/react"; 10 | import { FiX } from "react-icons/fi"; 11 | 12 | interface ColorProps { 13 | color: string; 14 | index: number; 15 | handleColorUpdate: (e: any, index: number) => void; 16 | handleColorDelete: (index: number) => void; 17 | } 18 | 19 | const Color = ({ 20 | color, 21 | index, 22 | handleColorUpdate, 23 | handleColorDelete, 24 | }: ColorProps): JSX.Element => { 25 | const handleDelete = () => { 26 | handleColorDelete(index); 27 | }; 28 | return ( 29 | 30 | Color {index + 1} 31 | 32 | handleColorUpdate(e.target.value, index)} 36 | cursor="pointer" 37 | variant="filled" 38 | /> 39 |
40 | {color} 41 |
42 | } 44 | onClick={handleDelete} 45 | aria-label="Delete color" 46 | ml={2} 47 | /> 48 |
49 |
50 | ); 51 | }; 52 | export default Color; 53 | -------------------------------------------------------------------------------- /components/Tools/Color/GradientGenerator/ExportAsUrl.tsx: -------------------------------------------------------------------------------- 1 | import CopyIconButton from "@/components/Common/CopyIconButton"; 2 | import { 3 | Button, 4 | Modal, 5 | ModalOverlay, 6 | ModalContent, 7 | ModalHeader, 8 | ModalBody, 9 | ModalCloseButton, 10 | Textarea, 11 | Flex, 12 | useClipboard, 13 | useDisclosure, 14 | } from "@chakra-ui/react"; 15 | import { ReactNode } from "react"; 16 | 17 | interface ExportAsUrlProps { 18 | children: ReactNode; 19 | colors: string[]; 20 | direction: number; 21 | [x: string]: any; 22 | } 23 | 24 | const BASE_URL = "https://www.devkit.one/color/gradient-generator"; 25 | 26 | const ExportAsUrl = (props: ExportAsUrlProps): JSX.Element => { 27 | let { colors, direction, children, ...otherProps } = props; 28 | 29 | colors = colors.map(color => color.replace("#", "")); 30 | const url = `${BASE_URL}?colors=${colors.join("-")}&direction=${direction}`; 31 | 32 | const { isOpen, onOpen, onClose } = useDisclosure(); 33 | const { hasCopied, onCopy } = useClipboard(url); 34 | 35 | return ( 36 | <> 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | }; 61 | 62 | export default ExportAsUrl; 63 | -------------------------------------------------------------------------------- /components/Tools/Color/GradientGenerator/GradientPreview.tsx: -------------------------------------------------------------------------------- 1 | import EditableControls from "@/components/Common/EditableControls"; 2 | import { 3 | Box, 4 | Tabs, 5 | TabList, 6 | TabPanels, 7 | Tab, 8 | TabPanel, 9 | Editable, 10 | EditablePreview, 11 | EditableInput, 12 | } from "@chakra-ui/react"; 13 | import { RefObject } from "react"; 14 | 15 | interface GradientPreviewProps { 16 | gradientCSS: string; 17 | gradientComponentRef: RefObject; 18 | } 19 | 20 | const GradientPreview = ({ 21 | gradientCSS, 22 | gradientComponentRef, 23 | }: GradientPreviewProps): JSX.Element => ( 24 | 25 | 26 | Box 27 | Text 28 | 29 | 30 | 31 | 32 | 40 | 41 | 42 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | export default GradientPreview; 61 | -------------------------------------------------------------------------------- /components/Tools/Color/GradientGenerator/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Box, 3 | Center, 4 | Flex, 5 | Input, 6 | Slider, 7 | SliderTrack, 8 | SliderFilledTrack, 9 | SliderThumb, 10 | Button, 11 | Grid, 12 | Text, 13 | HStack, 14 | chakra, 15 | } from "@chakra-ui/react"; 16 | import { useState, useRef } from "react"; 17 | import Color from "@/components/Tools/Color/GradientGenerator/Color"; 18 | import { useRouter } from "next/router"; 19 | import dynamic from "next/dynamic"; 20 | import ExportAsUrl from "@/components/Tools/Color/GradientGenerator/ExportAsUrl"; 21 | import GradientPreview from "./GradientPreview"; 22 | 23 | const CSSModal = dynamic( 24 | () => import("@/components/Tools/Color/GradientGenerator/CSSModal"), 25 | { ssr: false } 26 | ); 27 | const ExportAsPng = dynamic(() => import("@/components/Common/ExportAsPng"), { 28 | ssr: false, 29 | }); 30 | 31 | interface IGradient { 32 | css: string; 33 | colors: string[]; 34 | direction: number; 35 | } 36 | 37 | const Gradient = (): JSX.Element => { 38 | const router = useRouter(); 39 | 40 | const gradientComponentRef = useRef(null); 41 | 42 | let colors: string[] | undefined = (router.query.colors as string)?.split( 43 | "-" 44 | ) as string[]; 45 | colors = colors?.map(color => color.trim()); 46 | colors = colors?.map(color => `#${color}`); 47 | const direction: number | undefined = parseInt( 48 | router.query?.direction as string 49 | ); 50 | 51 | const [gradient, setGradient] = useState({ 52 | css: `linear-gradient(${direction ? direction : 90}deg, ${ 53 | colors ? colors.join(", ") : "#ff008c, #d30916" 54 | });`, 55 | colors: colors ? colors : ["#ff008c", "#d30916"], 56 | direction: direction ? direction : 90, 57 | }); 58 | 59 | const handleCSSUpdate = (value: string): void => { 60 | const direction: number = value 61 | .split("(")[1] 62 | .split("deg")[0] as unknown as number; 63 | const colors: string[] = value.split("deg")[1].split(","); 64 | colors.shift(); 65 | colors.forEach((color, index) => { 66 | color = color.trim(); 67 | color = color.replace(")", ""); 68 | colors[index] = color; 69 | }); 70 | setGradient({ 71 | direction, 72 | colors, 73 | css: value, 74 | }); 75 | }; 76 | 77 | const handleColorUpdate = (value: string, index: number): void => { 78 | const colors: string[] = gradient.colors.map((c, i) => 79 | i === index ? value : c 80 | ); 81 | setGradient({ 82 | ...gradient, 83 | css: `linear-gradient(${gradient.direction}deg, ${colors.join(", ")})`, 84 | colors, 85 | }); 86 | }; 87 | 88 | const handleColorDelete = (index: number): void => { 89 | const colors: string[] = gradient.colors.filter((c, i) => i !== index); 90 | setGradient({ 91 | ...gradient, 92 | css: `linear-gradient(${gradient.direction}deg, ${colors.join(", ")})`, 93 | colors, 94 | }); 95 | }; 96 | 97 | const handleDirectionUpdate = (value: number): void => { 98 | setGradient({ 99 | ...gradient, 100 | css: `linear-gradient(${value}deg, ${gradient.colors.join(", ")})`, 101 | direction: value, 102 | }); 103 | }; 104 | 105 | const handleAddColor = (): void => { 106 | const colors: string[] = [...gradient.colors, "#000000"]; 107 | setGradient({ 108 | ...gradient, 109 | colors, 110 | css: `linear-gradient(${gradient.direction}deg, ${colors.join(", ")})`, 111 | }); 112 | }; 113 | 114 | return ( 115 |
116 | 117 | 121 | 122 | 123 | Raw CSS 124 | handleCSSUpdate(e.target.value)} 128 | /> 129 | 130 | 131 | Gradient Direction 132 | handleDirectionUpdate(value)} 137 | focusThumbOnChange={false} 138 | > 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | {gradient.colors.map((color, index) => ( 147 | 154 | ))} 155 | 156 | 159 | 160 | 161 | 162 | 163 | 168 | Export as URL 169 | 170 | 175 | Copy CSS 176 | 177 | 181 | 182 | 183 |
184 | ); 185 | }; 186 | 187 | export default Gradient; 188 | -------------------------------------------------------------------------------- /components/Tools/Converters/Base64EncoderDecoder/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Textarea, Flex, useToast } from "@chakra-ui/react"; 2 | import { useState } from "react"; 3 | import { useRouter } from "next/router"; 4 | 5 | const Base64EncoderDecoder = () => { 6 | const router = useRouter(); 7 | 8 | const [input, setInput] = useState( 9 | (router?.query.input as string) || "" 10 | ); 11 | const [output, setOutput] = useState(""); 12 | 13 | const toast = useToast(); 14 | 15 | const handleChange = (e: React.ChangeEvent) => { 16 | setInput(e.target.value); 17 | }; 18 | 19 | const isBase64 = (str: string): boolean => { 20 | const base64regex = 21 | /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; 22 | return base64regex.test(str); 23 | }; 24 | 25 | const handleBase64EncodeClick = () => { 26 | if (!input) return toast({ title: "No input", status: "error" }); 27 | if (isBase64(input)) 28 | return toast({ title: "Input is already base64", status: "error" }); 29 | const encoded = Buffer.from(input).toString("base64"); 30 | setOutput(encoded); 31 | }; 32 | 33 | const handleBase64DecodeClick = () => { 34 | if (!input) return toast({ title: "No input", status: "error" }); 35 | if (!isBase64(input)) 36 | return toast({ title: "Input is not base64", status: "error" }); 37 | const decoded = Buffer.from(input, "base64").toString(); 38 | setOutput(decoded); 39 | }; 40 | 41 | return ( 42 | <> 43 |