feat(tools): add conventional commits cheatsheet tool
Add commit-memo tool that provides a comprehensive cheatsheet for conventional commits including structure, examples, and common tools. - Implements markdown-based content display - Covers all conventional commit elements (type, scope, description, body, footer) - Includes examples and common tooling (commitizen, commitlint, husky, etc.) - Follows established tool architecture pattern
This commit is contained in:
		
							parent
							
								
									07eea0f484
								
							
						
					
					
						commit
						fcc3d3a3b7
					
				
							
								
								
									
										35
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -55,6 +55,8 @@ declare module '@vue/runtime-core' { | |||||||
|     ColoredCard: typeof import('./src/components/ColoredCard.vue')['default'] |     ColoredCard: typeof import('./src/components/ColoredCard.vue')['default'] | ||||||
|     CommandPalette: typeof import('./src/modules/command-palette/command-palette.vue')['default'] |     CommandPalette: typeof import('./src/modules/command-palette/command-palette.vue')['default'] | ||||||
|     CommandPaletteOption: typeof import('./src/modules/command-palette/components/command-palette-option.vue')['default'] |     CommandPaletteOption: typeof import('./src/modules/command-palette/components/command-palette-option.vue')['default'] | ||||||
|  |     CommitMemo: typeof import('./src/tools/commit-memo/commit-memo.vue')['default'] | ||||||
|  |     'CommitMemo.content': typeof import('./src/tools/commit-memo/commit-memo.content.md')['default'] | ||||||
|     CrontabGenerator: typeof import('./src/tools/crontab-generator/crontab-generator.vue')['default'] |     CrontabGenerator: typeof import('./src/tools/crontab-generator/crontab-generator.vue')['default'] | ||||||
|     CSelect: typeof import('./src/ui/c-select/c-select.vue')['default'] |     CSelect: typeof import('./src/ui/c-select/c-select.vue')['default'] | ||||||
|     'CSelect.demo': typeof import('./src/ui/c-select/c-select.demo.vue')['default'] |     'CSelect.demo': typeof import('./src/ui/c-select/c-select.demo.vue')['default'] | ||||||
| @ -90,17 +92,28 @@ declare module '@vue/runtime-core' { | |||||||
|     HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default'] |     HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default'] | ||||||
|     IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default'] |     IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default'] | ||||||
|     'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default'] |     'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default'] | ||||||
|  |     'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default'] | ||||||
|     'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default'] |     'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default'] | ||||||
|  |     IconMdiArrowDown: typeof import('~icons/mdi/arrow-down')['default'] | ||||||
|  |     IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default'] | ||||||
|  |     IconMdiCamera: typeof import('~icons/mdi/camera')['default'] | ||||||
|     IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default'] |     IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default'] | ||||||
|     IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] |     IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] | ||||||
|     IconMdiClose: typeof import('~icons/mdi/close')['default'] |     IconMdiClose: typeof import('~icons/mdi/close')['default'] | ||||||
|     IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default'] |     IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default'] | ||||||
|  |     IconMdiDeleteOutline: typeof import('~icons/mdi/delete-outline')['default'] | ||||||
|  |     IconMdiDownload: typeof import('~icons/mdi/download')['default'] | ||||||
|     IconMdiEye: typeof import('~icons/mdi/eye')['default'] |     IconMdiEye: typeof import('~icons/mdi/eye')['default'] | ||||||
|     IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default'] |     IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default'] | ||||||
|     IconMdiHeart: typeof import('~icons/mdi/heart')['default'] |     IconMdiHeart: typeof import('~icons/mdi/heart')['default'] | ||||||
|  |     IconMdiPause: typeof import('~icons/mdi/pause')['default'] | ||||||
|  |     IconMdiPlay: typeof import('~icons/mdi/play')['default'] | ||||||
|  |     IconMdiRecord: typeof import('~icons/mdi/record')['default'] | ||||||
|  |     IconMdiRefresh: typeof import('~icons/mdi/refresh')['default'] | ||||||
|     IconMdiSearch: typeof import('~icons/mdi/search')['default'] |     IconMdiSearch: typeof import('~icons/mdi/search')['default'] | ||||||
|     IconMdiTranslate: typeof import('~icons/mdi/translate')['default'] |     IconMdiTranslate: typeof import('~icons/mdi/translate')['default'] | ||||||
|     IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default'] |     IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default'] | ||||||
|  |     IconMdiVideo: typeof import('~icons/mdi/video')['default'] | ||||||
|     InputCopyable: typeof import('./src/components/InputCopyable.vue')['default'] |     InputCopyable: typeof import('./src/components/InputCopyable.vue')['default'] | ||||||
|     IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default'] |     IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default'] | ||||||
|     Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default'] |     Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default'] | ||||||
| @ -129,20 +142,42 @@ declare module '@vue/runtime-core' { | |||||||
|     MenuLayout: typeof import('./src/components/MenuLayout.vue')['default'] |     MenuLayout: typeof import('./src/components/MenuLayout.vue')['default'] | ||||||
|     MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] |     MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] | ||||||
|     MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] |     MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] | ||||||
|  |     NAlert: typeof import('naive-ui')['NAlert'] | ||||||
|     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] |     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] | ||||||
|  |     NButton: typeof import('naive-ui')['NButton'] | ||||||
|     NCheckbox: typeof import('naive-ui')['NCheckbox'] |     NCheckbox: typeof import('naive-ui')['NCheckbox'] | ||||||
|  |     NCode: typeof import('naive-ui')['NCode'] | ||||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] |     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||||
|  |     NColorPicker: typeof import('naive-ui')['NColorPicker'] | ||||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] |     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||||
|  |     NDatePicker: typeof import('naive-ui')['NDatePicker'] | ||||||
|     NDivider: typeof import('naive-ui')['NDivider'] |     NDivider: typeof import('naive-ui')['NDivider'] | ||||||
|  |     NDynamicInput: typeof import('naive-ui')['NDynamicInput'] | ||||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] |     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||||
|  |     NForm: typeof import('naive-ui')['NForm'] | ||||||
|  |     NFormItem: typeof import('naive-ui')['NFormItem'] | ||||||
|  |     NGi: typeof import('naive-ui')['NGi'] | ||||||
|  |     NGrid: typeof import('naive-ui')['NGrid'] | ||||||
|     NH1: typeof import('naive-ui')['NH1'] |     NH1: typeof import('naive-ui')['NH1'] | ||||||
|  |     NH2: typeof import('naive-ui')['NH2'] | ||||||
|     NH3: typeof import('naive-ui')['NH3'] |     NH3: typeof import('naive-ui')['NH3'] | ||||||
|     NIcon: typeof import('naive-ui')['NIcon'] |     NIcon: typeof import('naive-ui')['NIcon'] | ||||||
|  |     NImage: typeof import('naive-ui')['NImage'] | ||||||
|  |     NInputGroup: typeof import('naive-ui')['NInputGroup'] | ||||||
|  |     NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel'] | ||||||
|  |     NInputNumber: typeof import('naive-ui')['NInputNumber'] | ||||||
|     NLayout: typeof import('naive-ui')['NLayout'] |     NLayout: typeof import('naive-ui')['NLayout'] | ||||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] |     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||||
|     NMenu: typeof import('naive-ui')['NMenu'] |     NMenu: typeof import('naive-ui')['NMenu'] | ||||||
|  |     NProgress: typeof import('naive-ui')['NProgress'] | ||||||
|  |     NScrollbar: typeof import('naive-ui')['NScrollbar'] | ||||||
|  |     NSlider: typeof import('naive-ui')['NSlider'] | ||||||
|     NSpace: typeof import('naive-ui')['NSpace'] |     NSpace: typeof import('naive-ui')['NSpace'] | ||||||
|  |     NSpin: typeof import('naive-ui')['NSpin'] | ||||||
|  |     NStatistic: typeof import('naive-ui')['NStatistic'] | ||||||
|  |     NSwitch: typeof import('naive-ui')['NSwitch'] | ||||||
|     NTable: typeof import('naive-ui')['NTable'] |     NTable: typeof import('naive-ui')['NTable'] | ||||||
|  |     NTag: typeof import('naive-ui')['NTag'] | ||||||
|     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] |     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] | ||||||
|     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] |     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] | ||||||
|     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] |     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] | ||||||
|  | |||||||
| @ -392,3 +392,7 @@ tools: | |||||||
|   text-to-binary: |   text-to-binary: | ||||||
|     title: Text to ASCII binary |     title: Text to ASCII binary | ||||||
|     description: Convert text to its ASCII binary representation and vice-versa. |     description: Convert text to its ASCII binary representation and vice-versa. | ||||||
|  | 
 | ||||||
|  |   commit-memo: | ||||||
|  |     title: Conventional Commit Cheatsheet | ||||||
|  |     description: Conventional commits cheatsheet with examples and common tools | ||||||
|  | |||||||
							
								
								
									
										163
									
								
								src/tools/commit-memo/commit-memo.content.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/tools/commit-memo/commit-memo.content.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,163 @@ | |||||||
|  | # Conventional Commits Cheatsheet | ||||||
|  | 
 | ||||||
|  | ## Structure | ||||||
|  | 
 | ||||||
|  | A conventional commit message follows this structure: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | <type>[optional scope]: <description> | ||||||
|  | 
 | ||||||
|  | [optional body] | ||||||
|  | 
 | ||||||
|  | [optional footer(s)] | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Elements | ||||||
|  | 
 | ||||||
|  | ### Type (Required) | ||||||
|  | The type describes the kind of change being made. Common types include: | ||||||
|  | 
 | ||||||
|  | - **feat**: A new feature for the user | ||||||
|  | - **fix**: A bug fix for the user | ||||||
|  | - **docs**: Documentation changes | ||||||
|  | - **style**: Code style changes (formatting, missing semicolons, etc.) | ||||||
|  | - **refactor**: Code changes that neither fix a bug nor add a feature | ||||||
|  | - **test**: Adding or updating tests | ||||||
|  | - **chore**: Maintenance tasks, dependency updates, build changes | ||||||
|  | - **perf**: Performance improvements | ||||||
|  | - **ci**: Changes to CI/CD configuration | ||||||
|  | - **build**: Changes to build system or external dependencies | ||||||
|  | - **revert**: Reverting a previous commit | ||||||
|  | 
 | ||||||
|  | ### Scope (Optional) | ||||||
|  | The scope provides additional context about what part of the codebase is affected: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | feat(auth): add OAuth2 integration | ||||||
|  | fix(api): resolve timeout issues | ||||||
|  | docs(readme): update installation instructions | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Description (Required) | ||||||
|  | A brief description of the change: | ||||||
|  | 
 | ||||||
|  | - Use imperative mood ("add" not "added" or "adds") | ||||||
|  | - Keep it concise (50 characters or less recommended) | ||||||
|  | - Don't capitalize the first letter | ||||||
|  | - Don't end with a period | ||||||
|  | 
 | ||||||
|  | ### Body (Optional) | ||||||
|  | Provides more detailed explanation of the change: | ||||||
|  | 
 | ||||||
|  | - Separate from description with a blank line | ||||||
|  | - Explain the motivation and contrast with previous behavior | ||||||
|  | - Use imperative mood | ||||||
|  | 
 | ||||||
|  | ### Footer (Optional) | ||||||
|  | Contains metadata about the commit: | ||||||
|  | 
 | ||||||
|  | - **Breaking changes**: Start with `BREAKING CHANGE:` | ||||||
|  | - **Issue references**: `Closes #123`, `Fixes #456` | ||||||
|  | - **Co-authors**: `Co-authored-by: Name <email>` | ||||||
|  | 
 | ||||||
|  | ## Examples | ||||||
|  | 
 | ||||||
|  | ### Simple commit | ||||||
|  | ``` | ||||||
|  | feat: add user authentication | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### With scope | ||||||
|  | ``` | ||||||
|  | fix(parser): handle edge case in JSON parsing | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### With body | ||||||
|  | ``` | ||||||
|  | feat: add email notifications | ||||||
|  | 
 | ||||||
|  | Users can now receive email notifications for important events. | ||||||
|  | This includes account changes, security alerts, and system updates. | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### With footer | ||||||
|  | ``` | ||||||
|  | fix: prevent racing of requests | ||||||
|  | 
 | ||||||
|  | Introduce a request id and a reference to latest request. Dismiss | ||||||
|  | incoming responses other than from latest request. | ||||||
|  | 
 | ||||||
|  | Closes #123 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Breaking change | ||||||
|  | ``` | ||||||
|  | feat!: send an email to the customer when a product is shipped | ||||||
|  | 
 | ||||||
|  | BREAKING CHANGE: The shipping service now requires an email address | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Full example | ||||||
|  | ``` | ||||||
|  | feat(shopping cart): add ability to remove items | ||||||
|  | 
 | ||||||
|  | Users can now remove items from their shopping cart by clicking | ||||||
|  | the remove button next to each item. This improves the user | ||||||
|  | experience by allowing corrections without starting over. | ||||||
|  | 
 | ||||||
|  | Closes #456 | ||||||
|  | Co-authored-by: Jane Doe <jane@example.com> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Common Tools | ||||||
|  | 
 | ||||||
|  | ### Commitizen | ||||||
|  | Interactive tool for creating conventional commits: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | npm install -g commitizen | ||||||
|  | npm install -g cz-conventional-changelog | ||||||
|  | echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Usage: | ||||||
|  | ```shell | ||||||
|  | git cz | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Commitlint | ||||||
|  | Lints commit messages to ensure they follow conventional format: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | npm install --save-dev @commitlint/config-conventional @commitlint/cli | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Configuration in `.commitlintrc.json`: | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "extends": ["@commitlint/config-conventional"] | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Husky | ||||||
|  | Git hooks to enforce commit message format: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | npm install --save-dev husky | ||||||
|  | npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}' | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Semantic Release | ||||||
|  | Automatically generates releases based on conventional commits: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | npm install --save-dev semantic-release | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Conventional Changelog | ||||||
|  | Generates changelogs from conventional commits: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | npm install -g conventional-changelog-cli | ||||||
|  | conventional-changelog -p angular -i CHANGELOG.md -s | ||||||
|  | ``` | ||||||
							
								
								
									
										15
									
								
								src/tools/commit-memo/commit-memo.e2e.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/tools/commit-memo/commit-memo.e2e.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | import { test, expect } from '@playwright/test'; | ||||||
|  | 
 | ||||||
|  | test.describe('Tool - Conventional Commit Cheatsheet', () => { | ||||||
|  |   test.beforeEach(async ({ page }) => { | ||||||
|  |     await page.goto('/commit-memo'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   test('Has correct title', async ({ page }) => { | ||||||
|  |     await expect(page).toHaveTitle('Conventional Commit Cheatsheet - IT Tools'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   test('', async ({ page }) => { | ||||||
|  | 
 | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										22
									
								
								src/tools/commit-memo/commit-memo.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/tools/commit-memo/commit-memo.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import { useThemeVars } from 'naive-ui'; | ||||||
|  | import Memo from './commit-memo.content.md'; | ||||||
|  | 
 | ||||||
|  | const themeVars = useThemeVars(); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <Memo /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped> | ||||||
|  | ::v-deep(pre) { | ||||||
|  |   margin: 0; | ||||||
|  |   padding: 15px 22px; | ||||||
|  |   background-color: v-bind('themeVars.cardColor'); | ||||||
|  |   border-radius: 4px; | ||||||
|  |   overflow: auto; | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										13
									
								
								src/tools/commit-memo/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/tools/commit-memo/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | import { GitCommit } from '@vicons/tabler'; | ||||||
|  | import { defineTool } from '../tool'; | ||||||
|  | import { translate } from '@/plugins/i18n.plugin'; | ||||||
|  | 
 | ||||||
|  | export const tool = defineTool({ | ||||||
|  |   name: translate('tools.commit-memo.title'), | ||||||
|  |   path: '/commit-memo', | ||||||
|  |   description: translate('tools.commit-memo.description'), | ||||||
|  |   keywords: ['commit', 'conventional', 'git', 'changelog', 'semantic', 'commitizen', 'commitlint'], | ||||||
|  |   component: () => import('./commit-memo.vue'), | ||||||
|  |   icon: GitCommit, | ||||||
|  |   createdAt: new Date('2025-08-07'), | ||||||
|  | }); | ||||||
| @ -1,6 +1,7 @@ | |||||||
| import { tool as base64FileConverter } from './base64-file-converter'; | import { tool as base64FileConverter } from './base64-file-converter'; | ||||||
| import { tool as base64StringConverter } from './base64-string-converter'; | import { tool as base64StringConverter } from './base64-string-converter'; | ||||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||||
|  | import { tool as commitMemo } from './commit-memo'; | ||||||
| import { tool as emailNormalizer } from './email-normalizer'; | import { tool as emailNormalizer } from './email-normalizer'; | ||||||
| 
 | 
 | ||||||
| import { tool as asciiTextDrawer } from './ascii-text-drawer'; | import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||||
| @ -147,6 +148,7 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|     name: 'Development', |     name: 'Development', | ||||||
|     components: [ |     components: [ | ||||||
|       gitMemo, |       gitMemo, | ||||||
|  |       commitMemo, | ||||||
|       randomPortGenerator, |       randomPortGenerator, | ||||||
|       crontabGenerator, |       crontabGenerator, | ||||||
|       jsonViewer, |       jsonViewer, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user