feat: externalized tool configuration

This commit is contained in:
Corentin Thomasset 2021-05-28 19:44:27 +02:00
parent c3adfe30ec
commit 690bd099ef
No known key found for this signature in database
GPG key ID: DBD997E935996158
31 changed files with 387 additions and 300 deletions

View file

@ -0,0 +1,85 @@
import {readdirSync, readFileSync} from 'fs'
import path, {join} from 'path'
import {Module} from '@nuxt/types'
import {NuxtRouteConfig} from '@nuxt/types/config/router'
import YAML from 'yaml'
import {capitalise} from '../../utils/string'
const toolDirName = 'tools'
const rootDir = join(__dirname, '..', '..')
const toolsDir = join(rootDir, toolDirName)
interface toolConfigModuleOptions {
}
function getTools() {
const categories = readdirSync(toolsDir)
const toolList: { [key: string]: any[] } = {}
for (const category of categories) {
const categoryDir = join(toolsDir, category)
const categoryFormatted = capitalise(category)
toolList[categoryFormatted] = readdirSync(categoryDir).map((toolFileName) => {
const toolPath = join(categoryDir, toolFileName)
const contentMatch = readFileSync(toolPath, 'utf8').match(/<tool(\s[^>\s]*)*>([\S\s.]*?)<\/tool>/)
return contentMatch
? {
...YAML.parse(contentMatch[2]),
componentPath: join(toolDirName, category, toolFileName)
}
: null
}).filter(v => v !== null)
}
return toolList
}
const toolConfigModule: Module<toolConfigModuleOptions> = function () {
const {nuxt, extendBuild, addPlugin} = this
const toolList = getTools()
const toolListFlat = Object.values(toolList).flat()
nuxt.hook('build:extendRoutes', (routes: NuxtRouteConfig[]) => {
toolListFlat.forEach((toolConfig) => {
const {path = '', title, componentPath} = toolConfig
const name = title.toLowerCase().split(/\s/).join('-').replace(/\.vue$/, '')
const newRoute: NuxtRouteConfig = {
name,
path,
component: join(rootDir, componentPath),
chunkName: componentPath.replace(/\.vue$/, '')
}
routes.push(newRoute)
})
nuxt.options.publicRuntimeConfig.toolList = toolList
})
extendBuild((config) => {
if (!config.module) {
// eslint-disable-next-line no-console
console.warn('Failed to register the tool-config module.')
return
}
config.module.rules.push({
resourceQuery: /blockType=tool/,
loader: require.resolve('./loader.js')
})
})
addPlugin({
src: path.resolve(__dirname, 'plugin.ts'),
fileName: 'tool-config/plugin.ts',
options: {
toolList,
toolListFlat
}
})
}
export default toolConfigModule

View file

@ -0,0 +1,13 @@
const YAML = require('yaml')
const loader = function (source, map) {
this.callback(
null,
`export default function (Component) {
Component.options.__toolConfig = ${JSON.stringify(YAML.parse(source))}
}`,
map
)
}
module.exports = loader

View file

@ -0,0 +1,16 @@
// @ts-nocheck
import {Plugin} from '@nuxt/types'
import type {ToolRouteConfig} from '~/types/ToolConfig';
declare module 'vue/types/vue' {
interface Vue {
$toolListFlat: ToolRouteConfig[]
$toolList: { [key: string]: ToolRouteConfig[] }
}
}
const plugin: Plugin = (_, inject) => {
inject('toolListFlat', <%= serialize(options.toolListFlat) %>)
inject('toolList', <%= serialize(options.toolList) %>)
}
export default plugin

View file

@ -1,6 +1,6 @@
<template>
<div class="memo">
<ToolHeader :config="memoConfig" />
<ToolHeader :config="$toolConfig" />
Warning: le style/aspect est toujours en wip, so focus on content <br><br>

View file

@ -5,8 +5,8 @@
append-icon="mdi-magnify"
color="white"
hide-details
:items="toolRoutesFlat"
:item-text="item => item.config.title"
:items="$toolListFlat"
:item-text="item => item.title"
item-value="path"
solo-inverted
dense
@ -26,12 +26,11 @@
</template>
<script lang="ts">
import {Component, mixins} from 'nuxt-property-decorator'
import {ToolRoutesMixin} from '@/mixins/tool-routes.mixin'
import {Component, Vue} from 'nuxt-property-decorator'
import {ToolRouteConfig} from '~/types/ToolConfig'
@Component
export default class SearchBar extends mixins(ToolRoutesMixin) {
export default class SearchBar extends Vue {
choose(path:string) {
this.$router.push({path})
}
@ -39,7 +38,7 @@ export default class SearchBar extends mixins(ToolRoutesMixin) {
filterItems(item:ToolRouteConfig, queryText:string, itemText:string) {
const query = queryText.trim().toLowerCase()
const nameContainsText = itemText.toLowerCase().includes(query)
const keywordContainsText = item?.config?.keywords.join(' ').toLowerCase().includes(query) ?? false
const keywordContainsText = item?.keywords.join(' ').toLowerCase().includes(query) ?? false
return nameContainsText || keywordContainsText
}
}

View file

@ -1,16 +1,21 @@
<script lang="ts">
import {Component, Vue} from 'nuxt-property-decorator'
import {Component, mixins} from 'nuxt-property-decorator'
import ToolWrapper from '~/components/ToolWrapper.vue'
import type {ToolConfig} from '~/types/ToolConfig'
import {ToolConfigMixin} from '~/mixins/tool-config.mixin'
@Component({components: {ToolWrapper}})
export default class Tool extends Vue {
@Component({
components: {ToolWrapper}
})
export default class Tool extends mixins(ToolConfigMixin) {
config(): ToolConfig {
throw new Error('You need to specify a config() method your custom Tool.')
return {
title: 'ADD A <tool> TAG'
} as unknown as ToolConfig
};
public head() {
const {title, description, keywords} = this.config()
const {title, description, keywords} = this.$toolConfig
const uniqueKeywordsCleaned = [...new Set([...keywords, ...title.split(/\s+/)].map(s => s.trim().toLowerCase()))]

View file

@ -22,7 +22,7 @@
<SearchBar class="hidden-sm-and-up" />
<v-list>
<div v-for="(items, section) in toolRoutesSections" :key="section">
<div v-for="(items, section) in $toolList" :key="section">
<v-subheader class="mt-4 pl-4">
{{ section }}
</v-subheader>
@ -37,11 +37,11 @@
>
<v-list-item-action>
<v-icon color="primary">
{{ item.config.icon }}
{{ item.icon }}
</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.config.title" />
<v-list-item-title v-text="item.title" />
</v-list-item-content>
</v-list-item>
</div>
@ -103,9 +103,8 @@
</template>
<script lang="ts">
import {Component, mixins} from 'nuxt-property-decorator'
import {Component, Vue} from 'nuxt-property-decorator'
import {version} from '../package.json'
import {ToolRoutesMixin} from '~/mixins/tool-routes.mixin'
import LogoOutlined from '~/assets/logo-outlined.svg?inline'
import HeroGradient from '~/assets/small-hero-gradient.svg?inline'
import SearchBar from '~/components/SearchBar.vue'
@ -117,7 +116,7 @@ import SearchBar from '~/components/SearchBar.vue'
SearchBar
}
})
export default class DefaultLayout extends mixins(ToolRoutesMixin) {
export default class DefaultLayout extends Vue {
title = 'IT - Tools'
drawer = false
items = []

View file

@ -0,0 +1,12 @@
import {Component, Vue} from 'nuxt-property-decorator'
import {ToolConfig} from '~/types/ToolConfig'
@Component
export class ToolConfigMixin extends Vue {
public $toolConfig!: ToolConfig;
beforeCreate() {
// @ts-ignore
this.$toolConfig = this.$options.__toolConfig
}
}

View file

@ -1,37 +0,0 @@
import {Component, Vue} from 'nuxt-property-decorator'
import {ToolRouteConfig} from '~/types/ToolConfig'
import {capitalise} from '~/utils/string'
@Component
export class ToolRoutesMixin extends Vue {
toolRoutesFlat : ToolRouteConfig[] = []
toolRoutesSections : {[key: string]: ToolRouteConfig[]} = {}
async created() {
const routes = this.$router.options.routes?.filter((r1, i, a) => r1.meta?.isTool && a.findIndex(r2 => r2.path.split('/').pop() === r1.path.split('/').pop()) === i) || []
const flat: ToolRouteConfig[] = []
const sections: { [key: string]: ToolRouteConfig[] } = {}
for (const route of routes) {
if ('component' in route) {
// @ts-ignore
const component = await route.component()
const routeConfig = {...route, config: component.options.methods.config()} as ToolRouteConfig
flat.push(routeConfig)
const sectionKey = capitalise(route.meta.section).replace(/_/g, ' ')
if (!(sectionKey in sections)) {
sections[sectionKey] = []
}
sections[sectionKey].push(routeConfig)
}
}
this.toolRoutesSections = sections
this.toolRoutesFlat = flat
}
}

View file

@ -36,7 +36,9 @@ export default {
// https://go.nuxtjs.dev/typescript
'@nuxt/typescript-build',
// https://go.nuxtjs.dev/vuetify
'@nuxtjs/vuetify'
'@nuxtjs/vuetify',
// '@nuxtjs/router-extras'
'~/buildModules/tool-config'
],
// Modules (https://go.nuxtjs.dev/config-modules)
@ -48,6 +50,8 @@ export default {
'@nuxtjs/svg',
'nuxt-i18n',
'@nuxtjs/markdownit'
// '~/buildModules/tool-config'
],
// Axios module configuration (https://go.nuxtjs.dev/config-axios)

92
package-lock.json generated
View file

@ -3583,15 +3583,6 @@
"typescript": "~4.2"
}
},
"@nuxt/typescript-runtime": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@nuxt/typescript-runtime/-/typescript-runtime-2.1.0.tgz",
"integrity": "sha512-quzxXiWq+jGdnCedDIYuBiExrfIJL3VL9o4gJyq6QzthKLYSvLzB6w4RiH6UbLtnNJyDoO9ywyNSI2+RUJr/Ng==",
"requires": {
"ts-node": "^9.1.1",
"typescript": "~4.2"
}
},
"@nuxt/utils": {
"version": "2.15.6",
"resolved": "https://registry.npmjs.org/@nuxt/utils/-/utils-2.15.6.tgz",
@ -4499,6 +4490,16 @@
"workbox-cdn": "^5.1.4"
}
},
"@nuxtjs/router-extras": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@nuxtjs/router-extras/-/router-extras-1.1.1.tgz",
"integrity": "sha512-WUBT6ig0MweXZVC3bYbhQ6cxvIRLBCCcIy/nZxUEw1zAWwrPWX12l3b5zRTLDFVhGUQfio25GWkNh8UWHWK7kw==",
"dev": true,
"requires": {
"chokidar": "^3.4.3",
"gray-matter": "^4.0.2"
}
},
"@nuxtjs/svg": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/@nuxtjs/svg/-/svg-0.1.12.tgz",
@ -5842,11 +5843,6 @@
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@ -8302,11 +8298,6 @@
"resolved": "https://registry.npmjs.org/devalue/-/devalue-2.0.1.tgz",
"integrity": "sha512-I2TiqT5iWBEyB8GRfTDP0hiLZ0YeDJZ+upDxjBfOC2lebO5LezQMv7QvIUTzdb64jQyAKLf1AHADtGN+jw6v8Q=="
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
},
"diff-sequences": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
@ -10251,6 +10242,18 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
},
"gray-matter": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
"integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
"dev": true,
"requires": {
"js-yaml": "^3.13.1",
"kind-of": "^6.0.2",
"section-matter": "^1.0.0",
"strip-bom-string": "^1.0.0"
}
},
"growly": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
@ -13152,7 +13155,8 @@
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"makeerror": {
"version": "1.0.11",
@ -16775,6 +16779,27 @@
"resolved": "https://registry.npmjs.org/scule/-/scule-0.2.1.tgz",
"integrity": "sha512-M9gnWtn3J0W+UhJOHmBxBTwv8mZCan5i1Himp60t6vvZcor0wr+IM0URKmIglsWJ7bRujNAVVN77fp+uZaWoKg=="
},
"section-matter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
"integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
"kind-of": "^6.0.0"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@ -17427,6 +17452,12 @@
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
"strip-bom-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
"integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=",
"dev": true
},
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
@ -18021,19 +18052,6 @@
}
}
},
"ts-node": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
"requires": {
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
}
},
"ts-pnp": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
@ -18161,7 +18179,8 @@
"typescript": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg=="
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.28",
@ -19811,11 +19830,6 @@
"decamelize": "^1.2.0"
}
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
},
"yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View file

@ -3,17 +3,16 @@
"version": "2.0.0-beta.0",
"private": true,
"scripts": {
"dev": "nuxt-ts",
"dev": "nuxt",
"build": "npm run generate",
"start": "nuxt-ts start",
"generate": "nuxt-ts generate",
"start": "nuxt start",
"generate": "nuxt generate",
"lint:commit": "commitlint --from $(git rev-list --max-parents=0 HEAD)",
"lint:js": "eslint --ext .js,.vue --ignore-path=.gitignore --max-warnings=0 .",
"lint": "npm run lint:js && npm run lint:commit",
"test": "jest"
},
"dependencies": {
"@nuxt/typescript-runtime": "^2.1.0",
"@nuxtjs/axios": "^5.13.5",
"@nuxtjs/markdownit": "^2.0.0",
"@nuxtjs/pwa": "^3.0.2",
@ -57,6 +56,7 @@
"less-loader": "^7.1.0",
"nuxt-property-decorator": "^2.9.1",
"ts-jest": "^26.5.6",
"vue-jest": "^3.0.4"
"vue-jest": "^3.0.4",
"yaml": "^1.10.2"
}
}

View file

@ -5,7 +5,7 @@
<v-row>
<v-col
v-for="(items, section) in toolRoutesSections"
v-for="(items, section) in $toolList"
:key="section"
cols="12"
sm="12"
@ -24,10 +24,10 @@
exact
>
<v-list-item-action>
<v-icon>{{ item.config.icon }}</v-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.config.title" />
<v-list-item-title v-text="item.title" />
</v-list-item-content>
</v-list-item>
</v-list>
@ -40,11 +40,10 @@
</template>
<script>
import {Component, mixins} from 'nuxt-property-decorator'
import {ToolRoutesMixin} from '@/mixins/tool-routes.mixin'
import {Component, Vue} from 'nuxt-property-decorator'
@Component
export default class Index extends mixins(ToolRoutesMixin) {
export default class Index extends Vue {
}
</script>

BIN
static/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-row>
<v-col cols="12" sm="4">
<v-text-field
@ -50,12 +50,19 @@
</ToolWrapper>
</template>
<tool>
title: 'Base converter'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-swap-horizontal'
keywords: ['base', 'converter']
path: '/color-picker-converter'
</tool>
<script lang="ts">
import {Component, Ref} from 'nuxt-property-decorator'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import {ToolConfig} from '~/types/ToolConfig'
import type {VForm} from '~/types/VForm'
const convertBase = (value: string, fromBase: number, toBase: number) => {
@ -83,15 +90,6 @@ const convertBase = (value: string, fromBase: number, toBase: number) => {
mixins: [CopyableMixin]
})
export default class BaseConverter extends Tool {
config(): ToolConfig {
return {
title: 'Base converter',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-swap-horizontal',
keywords: ['base', 'converter']
}
}
@Ref() readonly inputBaseRef!: VForm
@Ref() readonly outputBaseRef!: VForm

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-textarea
v-model="clearText"
outlined
@ -23,26 +23,24 @@
</ToolWrapper>
</template>
<tool>
title: 'Base64 string converter'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-text-box-outline'
keywords: ['base64', 'base', '64', 'converter']
path: '/base64-string-converter'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import type {ToolConfig} from '~/types/ToolConfig'
import {base64ToString, stringToBase64} from '~/utils/convert'
@Component({
mixins: [CopyableMixin]
})
export default class Base64StringConverter extends Tool {
config(): ToolConfig {
return {
title: 'Base64 string converter',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-text-box-outline',
keywords: ['base64', 'base', '64', 'converter']
}
}
clearText = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.'
get base64Text() {

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-row no-gutters align="center" align-content="center" justify="center">
<v-col cols="12" sm="6" align="center">
<v-color-picker
@ -59,6 +59,14 @@
</ToolWrapper>
</template>
<tool>
title: 'Color picker/converter'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-palette'
keywords: ['rgb', 'hsl', 'hex', 'keyword', 'css', 'picker']
path: '/color-picker-converter'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
@ -66,7 +74,6 @@ import colors from 'color-name'
import convert from 'color-convert'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import {ToolConfig} from '~/types/ToolConfig'
import type {VForm} from '~/types/VForm'
const required = (v: unknown) => !!v || 'A value is required'
@ -75,15 +82,6 @@ const required = (v: unknown) => !!v || 'A value is required'
mixins: [CopyableMixin]
})
export default class ColorPickerConverter extends Tool {
config(): ToolConfig {
return {
title: 'Color picker/converter',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-palette',
keywords: ['rgb', 'hsl', 'hex', 'keyword', 'css', 'picker']
}
}
rgbPicker = {
r: 76,
g: 175,

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-row>
<v-col md="3" sm="12" class="pt-0 pb-0">
<div class="text-center">
@ -49,26 +49,24 @@
</ToolWrapper>
</template>
<tool>
title: 'Date/Time converter'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-calendar-range'
keywords: ['date', 'time', 'converter', 'iso']
path: '/date-converter'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import Tool from '@/components/Tool.vue'
import {ToolConfig} from '@/types/ToolConfig'
@Component({
mixins: [CopyableMixin]
})
export default class DateConverter extends Tool {
config(): ToolConfig {
return {
title: 'Date/Time converter',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-calendar-range',
keywords: ['date', 'time', 'converter', 'iso']
}
}
inputString = ''
inputFormatterTitle: string | null = null
useCurrentDate = true

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-select
v-model="language"
outlined
@ -33,13 +33,20 @@
</ToolWrapper>
</template>
<tool>
title: 'BIP39 passphrase generator'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-message-text-lock-outline'
keywords: ['BIP39', 'passphrase', 'generator']
path: '/bip39-generator'
</tool>
<script lang="ts">
import * as bip39 from 'bip39'
import {shuffle} from '@/utils/string'
import {Component, Ref} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import Tool from '@/components/Tool.vue'
import {ToolConfig} from '@/types/ToolConfig'
import type {VForm} from '~/types/VForm'
const getRandomBuffer = () => Buffer.from(shuffle('0123456789abcdef'.repeat(16)).substring(0, 32), 'hex')
@ -48,15 +55,6 @@ const getRandomBuffer = () => Buffer.from(shuffle('0123456789abcdef'.repeat(16))
mixins: [CopyableMixin]
})
export default class Bip39Generator extends Tool {
config(): ToolConfig {
return {
title: 'BIP39 passphrase generator',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-message-text-lock-outline',
keywords: ['BIP39', 'passphrase', 'generator']
}
}
@Ref() readonly entropyRef!: VForm
@Ref() readonly passphraseRef!: VForm
buffer = getRandomBuffer()

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-row justify="center" align="center">
<v-col cols="12" lg="8" md="12">
<v-textarea
@ -42,11 +42,18 @@
</ToolWrapper>
</template>
<tool>
title: 'Cypher / uncypher text'
description: 'Cypher and uncyfer text.'
icon: 'mdi-lock-open'
keywords: ['cypher', 'uncypher', 'text', 'AES', 'TripleDES', 'Rabbit', 'RabbitLegacy', 'RC4']
path: '/cypher-uncyfer-text'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import Tool from '@/components/Tool.vue'
import type {ToolConfig} from '@/types/ToolConfig'
import CryptoJS from 'crypto-js'
const algos = {
@ -67,15 +74,6 @@ export default class CypherUncyferText extends Tool {
decrypted = 'Lorem ipsum dolor sit amet.'
encrypted = ''
config(): ToolConfig {
return {
title: 'Cypher / uncypher text',
description: 'Cypher and uncyfer text.',
icon: 'mdi-lock-open',
keywords: ['cypher', 'uncypher', 'text', ...Object.keys(algos).map(s => s.toLowerCase())]
}
}
mounted() {
this.encrypt()
}

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-textarea
v-model="inputText"
outlined
@ -27,12 +27,20 @@
</ToolWrapper>
</template>
<tool>
title: 'Hash text'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-script-text-play'
keywords: ['hash', 'text', 'MD5', 'SHA1', 'SHA256', 'SHA224', 'SHA512', 'SHA384', 'SHA3', 'RIPEMD160']
path: '/hash-text'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import CryptoJS from 'crypto-js'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import type {ToolConfig} from '~/types/ToolConfig'
const algos = {
MD5: CryptoJS.MD5,
SHA1: CryptoJS.SHA1,
@ -48,15 +56,6 @@ const algos = {
mixins: [CopyableMixin]
})
export default class HashText extends Tool {
config(): ToolConfig {
return {
title: 'Hash text',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-script-text-play',
keywords: ['hash', 'text', ...Object.keys(algos).map(s => s.toLowerCase())]
}
}
inputText = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.'
algorithm: keyof typeof algos = 'SHA256'
algorithms: typeof algos = algos

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-row no-gutters>
<v-col lg="6" md="12">
<v-switch v-model="withLowercase" label="Lowercase (abc...)" />
@ -26,10 +26,17 @@
</ToolWrapper>
</template>
<tool>
title: 'Token generator'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-key-chain-variant'
keywords: ['token', 'random', 'string', 'alphanumeric', 'symbols']
path: '/token-generator'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import Tool from '~/components/Tool.vue'
import type {ToolConfig} from '~/types/ToolConfig'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import {shuffle} from '~/utils/string'
@ -42,15 +49,6 @@ const specials = '.,;:!?./-"\'#{([-|\\@)]=}*+'
mixins: [CopyableMixin]
})
export default class TokenGenerator extends Tool {
config(): ToolConfig {
return {
title: 'Token generator',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-key-chain-variant',
keywords: ['token', 'random', 'string', 'alphanumeric', 'symbols']
}
}
withNumbers = true;
withLowercase = true;
withUppercase = true;

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-text-field
v-model.number="quantity"
outlined
@ -28,11 +28,18 @@
</ToolWrapper>
</template>
<tool>
title: 'UUIDs generator'
description: 'A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems. '
icon: 'mdi-fingerprint'
keywords: ['uuid', 'v4', 'random', 'id', 'alphanumeric', 'identity']
path: '/uuid-generator'
</tool>
<script lang="ts">
import {Component, Ref, Watch} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import type {ToolConfig} from '@/types/ToolConfig'
import { VTextField } from 'vuetify/lib'
import Tool from '~/components/Tool.vue'
@ -42,15 +49,6 @@ const generateUuid = () => '10000000-1000-4000-8000-100000000000'.replace(/[018]
mixins: [CopyableMixin]
})
export default class UuidGenerator extends Tool {
config(): ToolConfig {
return {
title: 'UUIDs generator',
description: 'A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems. ',
icon: 'mdi-fingerprint',
keywords: ['uuid', 'v4', 'random', 'id', 'alphanumeric', 'identity']
}
}
@Ref() readonly quantityEl! : typeof VTextField
token = ''
quantity = 1

View file

@ -1,18 +1,17 @@
<tool>
title: 'Git memo'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-git'
keywords: ['git', 'memo', 'cheat', 'sheet']
path: '/git-memo'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import type {ToolConfig} from '@/types/ToolConfig'
import Memo from '~/components/Memo.vue'
@Component
export default class GitMemo extends Memo {
config(): ToolConfig {
return {
title: 'Git memo',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-git',
keywords: ['git', 'memo', 'cheat', 'sheet']
}
}
}
</script>

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<div class="result">
{{ cronString }}
</div>
@ -162,27 +162,25 @@
</ToolWrapper>
</template>
<tool>
title: 'Crontab generator'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-calendar-clock'
keywords: ['year', 'month', 'week', 'day', 'minute', 'second']
path: '/crontab-generator'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import cronstrue from 'cronstrue'
import {isValidCron} from 'cron-validator'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import type {ToolConfig} from '~/types/ToolConfig'
@Component({
mixins: [CopyableMixin]
})
export default class CrontabGenerator extends Tool {
config(): ToolConfig {
return {
title: 'Crontab generator',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-calendar-clock',
keywords: ['year', 'month', 'week', 'day', 'minute', 'second']
}
}
cron = '* * * * *'
cronstrueConfig = {
verbose: true,

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-text-field
v-model="port"
outlined
@ -18,11 +18,18 @@
</ToolWrapper>
</template>
<tool>
title: 'Random port generator'
description: 'Random port generator without the range of "known" ports (0-1023).'
icon: 'mdi-lan-pending'
keywords: ['system', 'port', 'lan']
path: '/random-port-generator'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import type {ToolConfig} from '@/types/ToolConfig'
import Tool from '~/components/Tool.vue'
import {randIntFromInterval} from '~/utils/random'
@ -32,15 +39,6 @@ const generatePort = () => randIntFromInterval(1024, 65535)
mixins: [CopyableMixin]
})
export default class RandomPortGenerator extends Tool {
config(): ToolConfig {
return {
title: 'Random port generator',
description: 'Random port generator without the range of "known" ports (0-1023).',
icon: 'mdi-lan-pending',
keywords: ['system', 'port', 'lan']
}
}
port!: number
created() {

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-slider v-model="paragraphs" min="1" max="20" label="Paragraphs" thumb-label />
<v-range-slider
v-model="sentencePerParagraph"
@ -35,11 +35,18 @@
</ToolWrapper>
</template>
<tool>
title: 'Lorem ipsum generator'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-comment-text'
keywords: ['Lorem', 'ipsum', 'dolor', 'sit', 'amet']
path: '/lorem-ipsum-generator'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import type {ToolConfig} from '~/types/ToolConfig'
import {randFromArray, randIntFromInterval} from '~/utils/random'
const vocabulary = ['a', 'ac', 'accumsan', 'ad', 'adipiscing', 'aenean', 'aliquam', 'aliquet', 'amet', 'ante', 'aptent', 'arcu', 'at', 'auctor', 'bibendum', 'blandit', 'class', 'commodo', 'condimentum', 'congue', 'consectetur', 'consequat', 'conubia', 'convallis', 'cras', 'cubilia', 'cum', 'curabitur', 'curae', 'dapibus', 'diam', 'dictum', 'dictumst', 'dignissim', 'dolor', 'donec', 'dui', 'duis', 'egestas', 'eget', 'eleifend', 'elementum', 'elit', 'enim', 'erat', 'eros', 'est', 'et', 'etiam', 'eu', 'euismod', 'facilisi', 'faucibus', 'felis', 'fermentum', 'feugiat', 'fringilla', 'fusce', 'gravida', 'habitant', 'habitasse', 'hac', 'hendrerit', 'himenaeos', 'iaculis', 'id', 'imperdiet', 'in', 'inceptos', 'integer', 'interdum', 'ipsum', 'justo', 'lacinia', 'lacus', 'laoreet', 'lectus', 'leo', 'ligula', 'litora', 'lobortis', 'lorem', 'luctus', 'maecenas', 'magna', 'magnis', 'malesuada', 'massa', 'mattis', 'mauris', 'metus', 'mi', 'molestie', 'mollis', 'montes', 'morbi', 'mus', 'nam', 'nascetur', 'natoque', 'nec', 'neque', 'netus', 'nisi', 'nisl', 'non', 'nostra', 'nulla', 'nullam', 'nunc', 'odio', 'orci', 'ornare', 'parturient', 'pellentesque', 'penatibus', 'per', 'pharetra', 'phasellus', 'placerat', 'platea', 'porta', 'porttitor', 'posuere', 'potenti', 'praesent', 'pretium', 'primis', 'proin', 'pulvinar', 'purus', 'quam', 'quis', 'quisque', 'rhoncus', 'ridiculus', 'risus', 'rutrum', 'sagittis', 'sapien', 'scelerisque', 'sed', 'sem', 'semper', 'senectus', 'sit', 'sociis', 'sociosqu', 'sodales', 'sollicitudin', 'suscipit', 'suspendisse', 'taciti', 'tellus', 'tempor', 'tempus', 'tincidunt', 'torquent', 'tortor', 'turpis', 'ullamcorper', 'ultrices', 'ultricies', 'urna', 'varius', 'vehicula', 'vel', 'velit', 'venenatis', 'vestibulum', 'vitae', 'vivamus', 'viverra', 'volutpat', 'vulputate']
@ -54,15 +61,6 @@ const generateSentence = (length: number) => {
mixins: [CopyableMixin]
})
export default class LoremIpsumGenerator extends Tool {
config(): ToolConfig {
return {
title: 'Lorem ipsum generator',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-comment-text',
keywords: ['Lorem', 'ipsum', 'dolor', 'sit', 'amet']
}
}
paragraphs = 1
sentencePerParagraph = [3, 8]
wordPerSentence = [8, 15]

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-textarea
v-model="text"
outlined
@ -8,7 +8,10 @@
auto-grow
/>
<div>{{ $toolListFlat }}</div>
<table>
<tbody>
<tr>
<td><strong>Character count:</strong></td>
<td>{{ textLength }}</td>
@ -25,32 +28,34 @@
<td><strong>Byte size:</strong></td>
<td>{{ textSize }}</td>
</tr>
</tbody>
</table>
</ToolWrapper>
</template>
<tool>
title: 'Text stats'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: mdi-text
keywords: [ length, character, count ]
path: '/text-stats'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import type {ToolConfig} from '~/types/ToolConfig'
import {formatBytes} from '~/utils/convert'
@Component({
mixins: [CopyableMixin]
})
export default class TextStats extends Tool {
config(): ToolConfig {
return {
title: 'Text stats',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-text',
keywords: ['length', 'character', 'count']
}
}
text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.'
created() {
}
get textLength() {
return this.text.length
}

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()" no-card="true">
<ToolWrapper :config="$toolConfig" no-card="true">
<FileUploader v-model="file" />
<div v-if="base64 || loading" class="mt-10">
@ -25,11 +25,18 @@
</ToolWrapper>
</template>
<tool>
title: 'File to base64'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-file-link-outline'
keywords: ['file', 'base64']
path: '/file-to-base64'
</tool>
<script lang="ts">
import {Component, Watch} from 'nuxt-property-decorator'
import {CopyableMixin} from '@/mixins/copyable.mixin'
import Tool from '@/components/Tool.vue'
import type {ToolConfig} from '@/types/ToolConfig'
import FileUploader from '~/components/FileUploader.vue'
@Component({
@ -37,15 +44,6 @@ import FileUploader from '~/components/FileUploader.vue'
components: {FileUploader}
})
export default class FileToBase64 extends Tool {
config(): ToolConfig {
return {
title: 'File to base64',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-file-link-outline',
keywords: ['file', 'base64']
}
}
file: Blob | null = null
loading = false
base64 = ''

View file

@ -1,5 +1,5 @@
<template>
<ToolWrapper :config="config()">
<ToolWrapper :config="$toolConfig">
<v-row justify="center" align="center">
<v-col cols="12" lg="6" sm="12">
<v-text-field
@ -49,13 +49,20 @@
</ToolWrapper>
</template>
<tool>
title: 'QR-code generator'
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.'
icon: 'mdi-qrcode'
keywords: ['editor']
path: '/qrcode-generator'
</tool>
<script lang="ts">
import {Component} from 'nuxt-property-decorator'
import QrcodeVue from 'qrcode.vue'
import colors from 'color-name'
import {CopyableMixin} from '~/mixins/copyable.mixin'
import Tool from '~/components/Tool.vue'
import type {ToolConfig} from '~/types/ToolConfig'
import {downloadBase64File} from '~/utils/file'
import {stringToBase64} from '~/utils/convert'
import ColorInput from '~/components/ColorInput.vue'
@ -65,15 +72,6 @@ import ColorInput from '~/components/ColorInput.vue'
mixins: [CopyableMixin]
})
export default class QrcodeGenerator extends Tool {
config(): ToolConfig {
return {
title: 'QR-code generator',
description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus distinctio dolor dolorum eaque eligendi, facilis impedit laboriosam odit placeat.',
icon: 'mdi-qrcode',
keywords: ['editor']
}
}
value = 'https://it-tools.tech'
size = 300
level = 'M'

View file

@ -1,13 +1,14 @@
import {RouteConfig} from '@nuxt/types/config/router';
interface ToolConfig {
title: string;
description: string;
icon: string;
keywords: string[];
path?: string
}
type ToolConfigMethod = () => ToolConfig;
type ToolRouteConfig = RouteConfig & {config: ToolConfig}
interface ToolRouteConfig extends ToolConfig{
componentPath: string
}
export {ToolConfig, ToolConfigMethod, ToolRouteConfig}
export {ToolConfig, ToolRouteConfig}