feat: added components
							
								
								
									
										68
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,68 @@ | ||||
| # Changelog | ||||
| All notable changes to this project will be documented in this file. | ||||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||
| 
 | ||||
| ## Next | ||||
| ### Changed | ||||
| - Switched to [Nuxt.js](//nuxtjs.org) | ||||
|     - Server-Side rendered static app  | ||||
|     - Better SEO | ||||
| - Switched to Typescript using class components decorators from [nuxt-property-decorator](https://github.com/nuxt-community/nuxt-property-decorator) | ||||
| - UI and theme reworked | ||||
| - URL path changed | ||||
|     - `/hash` -> [`/hash-text`](https://it-tools.tech/hash-text) | ||||
|     - `/cypher` -> [`/cypher-uncyfer-text`](https://it-tools.tech/cypher-uncyfer-text) | ||||
| 
 | ||||
| ### Added | ||||
| - Added [/how-to-report-bug-or-request](/how-to-report-bug-or-request) route to explain how to report bug and request features | ||||
| 
 | ||||
| ## 1.7.0 | ||||
| - [feat] [Crontab friendly generator](https://it-tools.tech/crontab-generator) | ||||
| 
 | ||||
| ## 1.6.0 | ||||
| - [feat] [BIP39 generator](https://it-tools.tech/bip39-generator) | ||||
| - [feat] [Base 64 converter](https://it-tools.tech/base64-string-converter) | ||||
| 
 | ||||
| ## 1.5.2 | ||||
| - [feat] [humans.txt](https://it-tools.tech/humans.txt) | ||||
| - [feat] pwa auto update on new changes | ||||
| 
 | ||||
| ## 1.5.1 | ||||
| - [feat] switched back to history mode (no more '#' in url) | ||||
| 
 | ||||
| ## 1.5.0 | ||||
| - [feat] added [qr-code generator](https://it-tools.tech/qrcode-generator) | ||||
| 
 | ||||
| ## 1.4.0 | ||||
| - [ui] condensed + colored sidenav | ||||
| - [feat] added [git memo](https://it-tools.tech/git-memo) | ||||
| - [refactor] changed app title | ||||
| 
 | ||||
| ## 1.3.0 | ||||
| - [fix] [GithubContributors] ordered contributors by contribution count | ||||
| - [refactor] used vue-typecasting for number inputs | ||||
| - [feat] lazy loading tools routes | ||||
| - [feat] added [markdown editor](https://it-tools.tech/markdown-editor) | ||||
| - [feat] added [lorem ipsum generator](https://it-tools.tech/lorem-ipsum-generator) | ||||
| 
 | ||||
| ## 1.2.1 | ||||
| - [fix] [UuidGenerator] added quantity validation rules | ||||
| - [refactor] better isInt checker | ||||
| 
 | ||||
| ## 1.2.0 | ||||
| - [feat] [UuidGenerator] can generate multiple uuids  | ||||
| 
 | ||||
| ## 1.1.0 | ||||
| - [feat] 404 route + page | ||||
| - [feat] changelog in the About page  | ||||
| - [feat] contributors list in the About page  | ||||
| - [fix] [ColorConverter] color picker now updates fields  | ||||
| 
 | ||||
| ## 1.0.1 | ||||
| - [chore] added changelog | ||||
| - [fix] [BaseConverter] prevented non-integer bases | ||||
| - [fix] remove history move (incompatible with vercel.com) | ||||
| 
 | ||||
| ## 1.0.0 | ||||
| - First release | ||||
| @ -3,3 +3,4 @@ | ||||
| // The variables you want to modify | ||||
| // $font-size-root: 20px; | ||||
| 
 | ||||
| $test: linear-gradient(90deg, rgba(37, 99, 108, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(71, 177, 113, 1) 100%) | ||||
|  | ||||
| @ -2,14 +2,15 @@ | ||||
|   <v-autocomplete | ||||
|     label="Search..." | ||||
|     single-line | ||||
|     append-icon="fa-search" | ||||
|     append-icon="mdi-magnify" | ||||
|     color="white" | ||||
|     hide-details | ||||
|     :items="items" | ||||
|     item-text="text" | ||||
|     :items="toolRoutesFlat" | ||||
|     :item-text="item => item.config.title" | ||||
|     item-value="path" | ||||
|     solo-inverted | ||||
|     :filter="filter" | ||||
|     dense | ||||
|     :filter="filterItems" | ||||
|     clearable | ||||
|     cache-items | ||||
|     @change="choose" | ||||
| @ -24,23 +25,39 @@ | ||||
|   </v-autocomplete> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import {Component, Vue} from 'nuxt-property-decorator' | ||||
| import {ToolRoutes} from '~/mixins/tool-routes' | ||||
| <script lang="ts"> | ||||
| import {Component, mixins} from 'nuxt-property-decorator' | ||||
| import {ToolRoutesMixin} from '@/mixins/tool-routes.mixin' | ||||
| import {ToolRouteConfig} from '~/types/ToolConfig' | ||||
| 
 | ||||
| @Component({ | ||||
|   mixins: [ToolRoutes] | ||||
| }) | ||||
| export default class SearchBar extends Vue { | ||||
|   title = 'IT - Tools' | ||||
|   drawer = false | ||||
|   items = [] | ||||
| @Component | ||||
| export default class SearchBar extends mixins(ToolRoutesMixin) { | ||||
|   choose(path:string) { | ||||
|     this.$router.push({path}) | ||||
|   } | ||||
| 
 | ||||
|   filterItems(item:ToolRouteConfig, queryText:string, itemText:string) { | ||||
|     const query = queryText.trim().toLowerCase() | ||||
|     const nameContainsText = itemText.toLowerCase().includes(query) | ||||
|     const keywordContainsText = item?.config?.keywords.some((keyword:string) => keyword.toLowerCase().includes(query)) ?? false | ||||
|     return nameContainsText || keywordContainsText | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="less"> | ||||
| ::v-deep .v-list-item__mask{ | ||||
|   color: inherit !important; | ||||
|   background: inherit !important; | ||||
| ::v-deep { | ||||
|   .v-input__slot{ | ||||
|     background: var(--v-primary-base) !important; | ||||
|     background: linear-gradient(90deg, rgba(37,99,108,1) 0%, rgba(59,149,111,1) 60%, rgba(71,177,113,1) 100%) !important; | ||||
|     input { | ||||
|       color: #ffffff !important; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| .v-list{ | ||||
|   background: var(--v-foreground-base) !important; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <template> | ||||
|   <div class="tool-wrapper"> | ||||
|     <v-row no-gutters justify="center" align="center"> | ||||
|       <v-col cols="12" lg="6"> | ||||
|       <v-col cols="12" xl="6" lg="8" md="10"> | ||||
|         <div class="tool-wrapper-info"> | ||||
|           <h1>{{ config.title }}</h1> | ||||
|           <div class="spacer" /> | ||||
| @ -50,9 +50,10 @@ export default class ToolWrapper extends Vue { | ||||
|   } | ||||
| 
 | ||||
|   .spacer{ | ||||
|     width: 130px; | ||||
|     height: 1px; | ||||
|     background-color: var(--v-primary-base); | ||||
|     width: 200px; | ||||
|     height: 2px; | ||||
|     background: var(--v-primary-base); | ||||
|     background: linear-gradient(90deg, rgba(71, 177, 113, 1) 0%, rgba(59, 149, 111, 1) 60%, rgba(37, 99, 108, 1) 200%); | ||||
|     margin-bottom: 10px; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -17,6 +17,8 @@ | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|       <SearchBar class="hidden-sm-and-up" /> | ||||
| 
 | ||||
|       <v-list> | ||||
|         <div v-for="(items, section) in toolRoutesSections" :key="section"> | ||||
|           <v-subheader class="mt-4 pl-4"> | ||||
| @ -26,13 +28,15 @@ | ||||
|           <v-list-item | ||||
|             v-for="(item, i) in items" | ||||
|             :key="i" | ||||
|             :to="items.path" | ||||
|             :to="item.path" | ||||
|             router | ||||
|             exact | ||||
|             dense | ||||
|           > | ||||
|             <v-list-item-action> | ||||
|               <v-icon>{{ item.config.icon }}</v-icon> | ||||
|               <v-icon color="primary"> | ||||
|                 {{ item.config.icon }} | ||||
|               </v-icon> | ||||
|             </v-list-item-action> | ||||
|             <v-list-item-content> | ||||
|               <v-list-item-title v-text="item.config.title" /> | ||||
| @ -48,8 +52,24 @@ | ||||
|       height="60px" | ||||
|     > | ||||
|       <v-app-bar-nav-icon @click.stop="drawer = !drawer" /> | ||||
|       <v-toolbar-title v-if="!drawer" v-text="title" /> | ||||
|       <v-toolbar-title> | ||||
|         <NuxtLink to="/" class="title"> | ||||
|           {{ title }} | ||||
|         </NuxtLink> | ||||
|       </v-toolbar-title> | ||||
|       <v-spacer /> | ||||
|       <SearchBar class="hidden-sm-and-down" /> | ||||
|       <v-spacer /> | ||||
| 
 | ||||
|       <NuxtLink to="/how-to-report-bug-or-request"> | ||||
|         Bug / Request | ||||
|       </NuxtLink> | ||||
|       <NuxtLink to="/about"> | ||||
|         About | ||||
|       </NuxtLink> | ||||
|       <a href="https://github.com/CorentinTh/it-tools" target="_blank" class="github-link"> | ||||
|         <v-icon>mdi-github</v-icon> | ||||
|       </a> | ||||
|     </v-app-bar> | ||||
| 
 | ||||
|     <v-main> | ||||
| @ -57,25 +77,27 @@ | ||||
|         <nuxt /> | ||||
|       </v-container> | ||||
|     </v-main> | ||||
|     <!--    <v-footer app>--> | ||||
|     <!--      <span>© {{ new Date().getFullYear() }}</span>--> | ||||
|     <!--    </v-footer>--> | ||||
|     <!--<v-footer app>--> | ||||
|     <!--  <span>© {{ new Date().getFullYear() }}</span>--> | ||||
|     <!--</v-footer>--> | ||||
|   </v-app> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import {Component, mixins} from 'nuxt-property-decorator' | ||||
| import {ToolRoutes} from '~/mixins/tool-routes' | ||||
| 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' | ||||
| 
 | ||||
| @Component({ | ||||
|   components: { | ||||
|     LogoOutlined, | ||||
|     HeroGradient | ||||
|     HeroGradient, | ||||
|     SearchBar | ||||
|   } | ||||
| }) | ||||
| export default class DefaultLayout extends mixins(ToolRoutes) { | ||||
| export default class DefaultLayout extends mixins(ToolRoutesMixin) { | ||||
|   title = 'IT - Tools' | ||||
|   drawer = false | ||||
|   items = [] | ||||
| @ -84,6 +106,40 @@ export default class DefaultLayout extends mixins(ToolRoutes) { | ||||
| 
 | ||||
| <style lang="less"> | ||||
| 
 | ||||
| .v-toolbar__content { | ||||
|   a { | ||||
|     color: #ffffff; | ||||
|     text-decoration: none; | ||||
|     transition: all ease 0.2s; | ||||
|     margin: 0 10px; | ||||
|     opacity: 0.5; | ||||
|     font-size: 15px; | ||||
| 
 | ||||
|     &.title{ | ||||
|       opacity: 1; | ||||
|     } | ||||
| 
 | ||||
|     &:hover { | ||||
|       opacity: 1; | ||||
|       color: var(--v-primary-base); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .github-link { | ||||
|     border-bottom: none; | ||||
|     margin-left: 10px; | ||||
|     transition: all ease 0.2s; | ||||
| 
 | ||||
|     .v-icon { | ||||
|       font-size: 37px !important; | ||||
|       color: var(--v-primary-base); | ||||
|       transition: all ease 0.2s; | ||||
|       transition: all ease 0.2s; | ||||
| 
 | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .small-hero { | ||||
|   position: relative; | ||||
| 
 | ||||
| @ -96,18 +152,26 @@ export default class DefaultLayout extends mixins(ToolRoutes) { | ||||
|     width: 100%; | ||||
| 
 | ||||
|     .small-hero-content-logo { | ||||
|       width: 30%; | ||||
|       width: 25%; | ||||
|       margin: 0 auto; | ||||
|     } | ||||
| 
 | ||||
|     .small-hero-content-title { | ||||
|       font-size: 30px; | ||||
|       margin-top: 10px; | ||||
|       font-size: 25px; | ||||
|       font-weight: 600; | ||||
|       font-family: Ubuntu, Roboto, sans-serif; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .v-navigation-drawer__content{ | ||||
|   .v-list-item--active{ | ||||
|     color: var(--v-anchor-base); | ||||
|     border-left: 3px solid var(--v-primary-base); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .v-application { | ||||
|   background-color: var(--v-background-base, #121212) !important; | ||||
| } | ||||
|  | ||||
| @ -11,7 +11,7 @@ const copyToClipboard = (text: string) => { | ||||
| } | ||||
| 
 | ||||
| @Component | ||||
| export class Copyable extends Vue { | ||||
| export class CopyableMixin extends Vue { | ||||
|   copy(text: string, toastText = 'Copied to clipboard !') { | ||||
|     copyToClipboard(text) | ||||
|     console.log(toastText) | ||||
| @ -1,33 +1,36 @@ | ||||
| import {Component, Vue} from 'nuxt-property-decorator' | ||||
| import {RouteConfig} from '@nuxt/types/config/router' | ||||
| import {ToolConfig} from '~/types/ToolConfig' | ||||
| import {ToolRouteConfig} from '~/types/ToolConfig' | ||||
| import {capitalise} from '~/utils/string' | ||||
| 
 | ||||
| export type ToolRouteConfig = RouteConfig & {config: ToolConfig} | ||||
| 
 | ||||
| @Component | ||||
| export class ToolRoutes extends Vue { | ||||
| export class ToolRoutesMixin extends Vue { | ||||
|   toolRoutesFlat : ToolRouteConfig[] = [] | ||||
|   toolRoutesSections : {[key: string]: ToolRouteConfig[]} = {} | ||||
| 
 | ||||
|   async mounted() { | ||||
|   async created() { | ||||
|     const routes = this.$router.options.routes?.filter(r => r.meta?.isTool) || [] | ||||
|     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 | ||||
|         this.toolRoutesFlat.push(routeConfig) | ||||
|         flat.push(routeConfig) | ||||
| 
 | ||||
|         const sectionKey = capitalise(route.meta.section).replace(/_/g, ' ') | ||||
| 
 | ||||
|         if (!(sectionKey in this.toolRoutesSections)) { | ||||
|           this.toolRoutesSections[sectionKey] = [] | ||||
|         if (!(sectionKey in sections)) { | ||||
|           sections[sectionKey] = [] | ||||
|         } | ||||
| 
 | ||||
|         this.toolRoutesSections[sectionKey].push(routeConfig) | ||||
|         sections[sectionKey].push(routeConfig) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     this.toolRoutesSections = sections | ||||
|     this.toolRoutesFlat = flat | ||||
|   } | ||||
| } | ||||
							
								
								
									
										3514
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						| @ -13,11 +13,13 @@ | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@nuxt/typescript-runtime": "^2.0.1", | ||||
|     "@nuxtjs/axios": "^5.12.2", | ||||
|     "@nuxtjs/axios": "^5.13.1", | ||||
|     "@nuxtjs/pwa": "^3.0.2", | ||||
|     "@nuxtjs/toast": "^3.3.1", | ||||
|     "core-js": "^3.6.5", | ||||
|     "crypto-js": "^4.0.0", | ||||
|     "nuxt": "^2.14.12", | ||||
|     "vuetify": "^2.4.5", | ||||
|     "vuetify-toast-snackbar": "^0.6.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
| @ -28,11 +30,12 @@ | ||||
|     "@nuxtjs/eslint-module": "^2.0.0", | ||||
|     "@nuxtjs/svg": "^0.1.12", | ||||
|     "@nuxtjs/vuetify": "^1.11.2", | ||||
|     "@types/crypto-js": "^4.0.1", | ||||
|     "@vue/test-utils": "^1.1.0", | ||||
|     "babel-core": "7.0.0-bridge.0", | ||||
|     "babel-eslint": "^10.1.0", | ||||
|     "babel-jest": "^26.5.0", | ||||
|     "eslint": "^7.10.0", | ||||
|     "eslint": "^7.20.0", | ||||
|     "eslint-config-prettier": "^6.12.0", | ||||
|     "eslint-plugin-nuxt": "^1.0.0", | ||||
|     "eslint-plugin-prettier": "^3.1.4", | ||||
| @ -40,7 +43,7 @@ | ||||
|     "less": "^4.0.0", | ||||
|     "less-loader": "^7.1.0", | ||||
|     "nuxt-property-decorator": "^2.9.1", | ||||
|     "ts-jest": "^26.4.1", | ||||
|     "ts-jest": "^26.5.1", | ||||
|     "vue-jest": "^3.0.4" | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										20
									
								
								pages/about.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,20 @@ | ||||
| <template> | ||||
|   <v-row justify="center" align="center"> | ||||
|     <v-col cols="12" sm="12" md="8"> | ||||
|       <h1>Yolo</h1> | ||||
|     </v-col> | ||||
|   </v-row> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import {Component, Vue} from 'nuxt-property-decorator' | ||||
| 
 | ||||
| @Component | ||||
| export default class About extends Vue { | ||||
| 
 | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less"> | ||||
| 
 | ||||
| </style> | ||||
							
								
								
									
										20
									
								
								pages/how-to-report-bug-or-request.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,20 @@ | ||||
| <template> | ||||
|   <v-row justify="center" align="center"> | ||||
|     <v-col cols="12" sm="12" md="8"> | ||||
|       <h1>How-to-report-bug-or-request</h1> | ||||
|     </v-col> | ||||
|   </v-row> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import {Component, Vue} from 'nuxt-property-decorator' | ||||
| 
 | ||||
| @Component | ||||
| export default class HowToReportBugOrRequest extends Vue { | ||||
| 
 | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less"> | ||||
| 
 | ||||
| </style> | ||||
| @ -2,17 +2,57 @@ | ||||
|   <v-row justify="center" align="center"> | ||||
|     <v-col cols="12" sm="12" md="8"> | ||||
|       <h1>Yolo</h1> | ||||
| 
 | ||||
|       <v-card v-for="(items, section) in toolRoutesSections" :key="section"> | ||||
|         <v-card-title>{{ section }}</v-card-title> | ||||
|         <v-card-text> | ||||
|           <v-list> | ||||
|             <v-list-item | ||||
|               v-for="(item, i) in items" | ||||
|               :key="i" | ||||
|               :to="item.path" | ||||
|               router | ||||
|               exact | ||||
|             > | ||||
|               <v-list-item-action> | ||||
|                 <v-icon>{{ item.config.icon }}</v-icon> | ||||
|               </v-list-item-action> | ||||
|               <v-list-item-content> | ||||
|                 <v-list-item-title v-text="item.config.title" /> | ||||
|               </v-list-item-content> | ||||
|             </v-list-item> | ||||
|           </v-list> | ||||
|         </v-card-text> | ||||
|       </v-card> | ||||
|     </v-col> | ||||
|   </v-row> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import {Component, mixins} from 'nuxt-property-decorator' | ||||
| import {ToolRoutesMixin} from '@/mixins/tool-routes.mixin' | ||||
| 
 | ||||
| export default { | ||||
|   components: {} | ||||
| @Component | ||||
| export default class Index extends mixins(ToolRoutesMixin) { | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less"> | ||||
| <style scopedlang="less"> | ||||
|   .v-list{ | ||||
|     background: transparent !important; | ||||
|   } | ||||
| 
 | ||||
|   .v-card__title{ | ||||
|     background: var(--v-primary-base) !important; | ||||
|     background: linear-gradient(90deg, rgba(37,99,108,1) 0%, rgba(59,149,111,1) 60%, rgba(71,177,113,1) 100%) !important; | ||||
|     padding-left: 33px; | ||||
|   } | ||||
| 
 | ||||
|   .v-list-item{ | ||||
|     padding-left: 31px; | ||||
|   } | ||||
| 
 | ||||
|   .v-card__text{ | ||||
|     padding: 0; | ||||
|   } | ||||
| </style> | ||||
|  | ||||
							
								
								
									
										106
									
								
								pages/tools/crypto/cypher-uncyfer-text.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,106 @@ | ||||
| <template> | ||||
|   <ToolWrapper :config="config()"> | ||||
|     <v-row justify="center" align="center"> | ||||
|       <v-col cols="12" lg="8" md="12"> | ||||
|         <v-textarea | ||||
|           v-model="key" | ||||
|           outlined | ||||
|           label="Encryption key" | ||||
|           rows="1" | ||||
|           @input="encrypt" | ||||
|         /> | ||||
|       </v-col> | ||||
|       <v-col cols="12" lg="4" md="12"> | ||||
|         <v-select | ||||
|           v-model="algorithm" | ||||
|           :items="Object.keys(algorithms)" | ||||
|           label="Algorithm" | ||||
|           outlined | ||||
|           @change="encrypt" | ||||
|         /> | ||||
|       </v-col> | ||||
|     </v-row> | ||||
| 
 | ||||
|     <v-textarea | ||||
|       v-model="decrypted" | ||||
|       outlined | ||||
|       label="Clear text" | ||||
|       @input="encrypt" | ||||
|     /> | ||||
| 
 | ||||
|     <v-textarea | ||||
|       v-model="encrypted" | ||||
|       outlined | ||||
|       label="Cyphered text" | ||||
|       @input="decrypt" | ||||
|     /> | ||||
|     <div class="text-center"> | ||||
|       <v-btn depressed @click="copy(encrypted)"> | ||||
|         Copy result | ||||
|       </v-btn> | ||||
|     </div> | ||||
|   </ToolWrapper> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import {Component} from 'nuxt-property-decorator' | ||||
| import {CopyableMixin} from '@/mixins/copyable.mixin' | ||||
| import Tool from '@/components/Tool' | ||||
| import {ToolConfig} from '@/types/ToolConfig' | ||||
| import CryptoJS from 'crypto-js' | ||||
| 
 | ||||
| const algos = { | ||||
|   AES: CryptoJS.AES, | ||||
|   TripleDES: CryptoJS.TripleDES, | ||||
|   Rabbit: CryptoJS.Rabbit, | ||||
|   RabbitLegacy: CryptoJS.RabbitLegacy, | ||||
|   RC4: CryptoJS.RC4 | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   mixins: [CopyableMixin] | ||||
| }) | ||||
| export default class CypherUncyferText extends Tool { | ||||
|   algorithm: keyof typeof algos = 'AES' | ||||
|   algorithms: typeof algos = algos | ||||
|   key = 'sup3r s3cr3t k3y' | ||||
|   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() | ||||
|   } | ||||
| 
 | ||||
|   encrypt() { | ||||
|     try { | ||||
|       this.encrypted = this.algorithms[this.algorithm] | ||||
|         .encrypt(this.decrypted.trim(), this.key) | ||||
|         .toString() | ||||
|     } catch (ignored) { | ||||
|       // ignored | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   decrypt() { | ||||
|     try { | ||||
|       this.decrypted = this.algorithms[this.algorithm] | ||||
|         .decrypt(this.encrypted.trim(), this.key) | ||||
|         .toString(CryptoJS.enc.Utf8) | ||||
|     } catch (ignored) { | ||||
|       // ignored | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less"> | ||||
| </style> | ||||
							
								
								
									
										78
									
								
								pages/tools/crypto/hash-text.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,78 @@ | ||||
| <template> | ||||
|   <ToolWrapper :config="config()"> | ||||
|     <v-textarea | ||||
|       v-model="inputText" | ||||
|       outlined | ||||
|       label="Text to hash" | ||||
|     /> | ||||
| 
 | ||||
|     <v-select | ||||
|       v-model="algorithm" | ||||
|       :items="Object.keys(algorithms)" | ||||
|       label="Algorithm" | ||||
|       outlined | ||||
|     /> | ||||
| 
 | ||||
|     <v-textarea | ||||
|       v-model="hashed" | ||||
|       outlined | ||||
|       readonly | ||||
|       label="Hashed text" | ||||
|     /> | ||||
|     <div class="text-center"> | ||||
|       <v-btn depressed @click="copy(hashed)"> | ||||
|         Copy hash | ||||
|       </v-btn> | ||||
|     </div> | ||||
|   </ToolWrapper> | ||||
| </template> | ||||
| 
 | ||||
| <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 {ToolConfig} from '~/types/ToolConfig' | ||||
| 
 | ||||
| const algos = { | ||||
|   MD5: CryptoJS.MD5, | ||||
|   SHA1: CryptoJS.SHA1, | ||||
|   SHA256: CryptoJS.SHA256, | ||||
|   SHA224: CryptoJS.SHA224, | ||||
|   SHA512: CryptoJS.SHA512, | ||||
|   SHA384: CryptoJS.SHA384, | ||||
|   SHA3: CryptoJS.SHA3, | ||||
|   RIPEMD160: CryptoJS.RIPEMD160 | ||||
| } | ||||
| 
 | ||||
| @Component({ | ||||
|   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 | ||||
| 
 | ||||
|   get hashed() { | ||||
|     if (this.algorithms[this.algorithm]) { | ||||
|       return this.algorithms[this.algorithm](this.inputText).toString() | ||||
|     } else { | ||||
|       this.$toast.error('Invalid algorithm.') | ||||
|       return '' | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| </style> | ||||
| @ -11,7 +11,7 @@ | ||||
|       </v-col> | ||||
|     </v-row> | ||||
| 
 | ||||
|     <v-slider v-model="length" :label="`Length (${length})`" min="1" max="256" /> | ||||
|     <v-slider v-model="length" :label="`Length (${length})`" min="1" max="512" /> | ||||
| 
 | ||||
|     <v-textarea v-model="token" outlined /> | ||||
| 
 | ||||
| @ -30,16 +30,16 @@ | ||||
| import {Component} from 'nuxt-property-decorator' | ||||
| import Tool from '~/components/Tool.vue' | ||||
| import {ToolConfig} from '~/types/ToolConfig' | ||||
| import {Copyable} from '~/mixins/copyable' | ||||
| import {CopyableMixin} from '~/mixins/copyable.mixin' | ||||
| import {shuffle} from '~/utils/string' | ||||
| 
 | ||||
| const shuffle = (s: string) => s.split('').sort(() => 0.5 - Math.random()).join('') | ||||
| const lowercase = 'abcdefghijklmopqrstuvwxyz' | ||||
| const uppercase = 'ABCDEFGHIJKLMOPQRSTUVWXYZ' | ||||
| const numbers = '0123456789' | ||||
| const specials = '.,;:!?./-"\'#{([-|\\@)]=}*+' | ||||
| 
 | ||||
| @Component({ | ||||
|   mixins: [Copyable] | ||||
|   mixins: [CopyableMixin] | ||||
| }) | ||||
| export default class TokenGenerator extends Tool { | ||||
|   config(): ToolConfig { | ||||
|  | ||||
							
								
								
									
										92
									
								
								pages/tools/crypto/uuid-generator.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,92 @@ | ||||
| <template> | ||||
|   <ToolWrapper :config="config()"> | ||||
|     <v-text-field | ||||
|       v-model.number="quantity" | ||||
|       outlined | ||||
|       type="number" | ||||
|       label="Quantity" | ||||
|       dense | ||||
|       class="quantity" | ||||
|       :rules="rules.quantity" | ||||
|     /> | ||||
|     <v-textarea | ||||
|       v-model="token" | ||||
|       outlined | ||||
|       class="centered-input" | ||||
|       :rows="quantity <= 10 ? quantity : 10" | ||||
|       readonly | ||||
|     /> | ||||
| 
 | ||||
|     <div class="text-center"> | ||||
|       <v-btn depressed class="mr-4" @click="computeToken"> | ||||
|         Refresh | ||||
|       </v-btn> | ||||
|       <v-btn depressed @click="copy(token, `UUID${quantity > 1 ? 's' : ''} copied !`)"> | ||||
|         Copy UUID{{ quantity > 1 ? 's' : '' }} | ||||
|       </v-btn> | ||||
|     </div> | ||||
|   </ToolWrapper> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| 
 | ||||
| import {Component, Ref, Watch} from 'nuxt-property-decorator' | ||||
| import {CopyableMixin} from '@/mixins/copyable.mixin' | ||||
| import {ToolConfig} from '@/types/ToolConfig' | ||||
| import { VTextField } from 'vuetify/lib' | ||||
| import Tool from '~/components/Tool.vue' | ||||
| 
 | ||||
| const generateUuid = () => '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, c => ((c as unknown as number) ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> (c as unknown as number) / 4).toString(16)) | ||||
| 
 | ||||
| @Component({ | ||||
|   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 | ||||
|   rules = { | ||||
|     quantity: [ | ||||
|       (v: any) => !!v || 'Quantity is required', | ||||
|       (v: any) => (v > 0 && v <= 50) || 'Quantity should be > 0 and <= 50', | ||||
|       (v: any) => Number.isInteger(v) || 'Quantity should be an integer' | ||||
|     ] | ||||
|   } | ||||
| 
 | ||||
|   mounted() { | ||||
|     this.computeToken() | ||||
|   } | ||||
| 
 | ||||
|   @Watch('quantity') | ||||
|   computeToken() { | ||||
|     this.token = Array.from({length: this.quantity}, generateUuid).join('\n') | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped lang="less"> | ||||
| .quantity { | ||||
|   width: 100px; | ||||
|   margin: auto; | ||||
|   text-align: center; | ||||
| 
 | ||||
|   ::v-deep input { | ||||
|     text-align: center; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| ::v-deep .centered-input textarea { | ||||
|   text-align: center; | ||||
|   margin-top: 13px !important; | ||||
|   font-family: Consolas, monospace; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										
											BIN
										
									
								
								static/android-chrome-192x192.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								static/android-chrome-512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 24 KiB | 
							
								
								
									
										
											BIN
										
									
								
								static/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.2 KiB | 
							
								
								
									
										9
									
								
								static/browserconfig.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <browserconfig> | ||||
|     <msapplication> | ||||
|         <tile> | ||||
|             <square150x150logo src="/mstile-150x150.png"/> | ||||
|             <TileColor>#2b5797</TileColor> | ||||
|         </tile> | ||||
|     </msapplication> | ||||
| </browserconfig> | ||||
							
								
								
									
										
											BIN
										
									
								
								static/favicon-16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								static/favicon-32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.9 KiB | 
| Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										
											BIN
										
									
								
								static/icon.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 12 KiB | 
							
								
								
									
										
											BIN
										
									
								
								static/mstile-150x150.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.0 KiB | 
							
								
								
									
										83
									
								
								static/safari-pinned-tab.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,83 @@ | ||||
| <?xml version="1.0" standalone="no"?> | ||||
| <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" | ||||
|  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> | ||||
| <svg version="1.0" xmlns="http://www.w3.org/2000/svg" | ||||
|  width="726.000000pt" height="726.000000pt" viewBox="0 0 726.000000 726.000000" | ||||
|  preserveAspectRatio="xMidYMid meet"> | ||||
| <metadata> | ||||
| Created by potrace 1.11, written by Peter Selinger 2001-2013 | ||||
| </metadata> | ||||
| <g transform="translate(0.000000,726.000000) scale(0.100000,-0.100000)" | ||||
| fill="#000000" stroke="none"> | ||||
| <path d="M3255 7239 c-112 -39 -191 -120 -230 -238 -14 -43 -17 -88 -17 -237 | ||||
| 0 -144 -3 -186 -14 -195 -8 -6 -14 -9 -14 -6 0 3 -23 -1 -52 -9 -28 -9 -60 | ||||
| -17 -71 -19 -10 -3 -28 -7 -40 -11 -12 -5 -26 -8 -32 -9 -5 -1 -17 -4 -25 -7 | ||||
| -8 -3 -53 -19 -100 -34 -47 -15 -89 -31 -94 -36 -6 -4 -16 -8 -24 -8 -15 0 | ||||
| -68 -22 -114 -46 -16 -9 -28 -12 -28 -7 0 4 -4 4 -8 -2 -6 -9 -103 -58 -109 | ||||
| -56 -2 0 -46 -24 -99 -54 -53 -30 -100 -55 -105 -55 -5 0 -9 -3 -9 -7 0 -9 | ||||
| -57 -43 -71 -43 -5 0 -69 60 -142 133 -132 130 -163 152 -264 185 -76 24 -223 | ||||
| -4 -292 -57 -56 -42 -441 -432 -466 -471 -60 -96 -75 -246 -32 -325 6 -11 12 | ||||
| -25 13 -32 4 -23 60 -88 177 -205 64 -64 117 -120 117 -123 0 -4 -13 -25 -29 | ||||
| -48 -15 -23 -31 -52 -35 -64 -4 -13 -11 -23 -15 -23 -5 0 -14 -15 -22 -32 -7 | ||||
| -18 -16 -35 -19 -38 -5 -4 -82 -157 -85 -170 -1 -3 -9 -23 -19 -45 -41 -92 | ||||
| -103 -255 -111 -290 -1 -5 -5 -17 -8 -25 -9 -20 -47 -164 -59 -222 -6 -27 -13 | ||||
| -48 -17 -49 -3 0 -91 -2 -196 -3 -105 -1 -208 -8 -231 -14 -120 -35 -230 -160 | ||||
| -251 -287 -5 -27 -8 -185 -7 -350 1 -282 3 -303 23 -357 41 -106 113 -177 222 | ||||
| -220 48 -18 77 -21 249 -22 l195 -1 13 -50 c7 -27 15 -58 17 -68 2 -10 7 -27 | ||||
| 10 -37 3 -10 7 -27 9 -37 15 -75 116 -345 128 -341 5 2 186 180 402 396 l393 | ||||
| 393 -8 44 c-24 126 -32 424 -15 550 5 33 9 71 10 85 10 99 86 364 138 480 70 | ||||
| 156 158 310 222 392 12 14 32 41 46 59 85 113 253 277 380 372 116 87 304 191 | ||||
| 440 244 120 47 141 55 155 58 6 2 35 10 65 19 30 8 73 18 95 22 22 3 44 8 48 | ||||
| 11 4 2 22 7 40 9 18 3 43 7 57 9 112 18 365 24 485 11 299 -32 547 -112 810 | ||||
| -260 39 -22 72 -43 75 -46 3 -3 26 -19 52 -35 27 -17 48 -33 48 -37 0 -5 5 -8 | ||||
| 11 -8 11 0 84 -59 168 -135 41 -37 58 -54 141 -145 50 -54 142 -178 194 -258 | ||||
| 90 -142 216 -429 241 -552 2 -8 8 -33 13 -55 6 -22 13 -53 16 -70 3 -16 7 -41 | ||||
| 10 -55 7 -36 17 -109 21 -155 6 -61 6 -315 0 -360 -25 -201 -29 -221 -80 -405 | ||||
| -31 -114 -117 -310 -183 -420 -12 -19 -29 -48 -38 -65 -9 -16 -20 -32 -23 -35 | ||||
| -3 -3 -19 -24 -34 -48 -32 -50 -172 -217 -215 -259 -95 -90 -183 -165 -242 | ||||
| -206 -154 -107 -272 -173 -410 -230 -79 -33 -223 -82 -266 -92 -228 -50 -308 | ||||
| -60 -499 -59 -170 0 -297 10 -355 29 -14 4 -111 -87 -414 -390 -218 -218 -396 | ||||
| -400 -396 -405 0 -4 24 -16 53 -26 28 -9 57 -20 62 -24 6 -4 57 -22 115 -39 | ||||
| 58 -18 116 -37 130 -41 14 -5 50 -14 80 -20 106 -23 97 -1 98 -223 0 -122 5 | ||||
| -203 12 -216 6 -12 8 -21 5 -21 -3 0 6 -22 20 -49 27 -53 103 -141 123 -141 7 | ||||
| 0 12 -4 12 -9 0 -5 17 -15 38 -22 20 -6 43 -17 51 -23 10 -8 117 -11 350 -11 | ||||
| 307 0 341 2 394 20 31 10 57 23 57 27 0 4 6 8 14 8 20 0 112 96 131 137 36 75 | ||||
| 42 119 42 307 1 103 2 189 5 191 2 2 15 6 28 9 140 29 398 113 533 174 37 17 | ||||
| 67 28 67 24 0 -4 4 -2 8 3 4 6 43 28 87 49 44 22 82 42 85 45 3 4 17 12 32 18 | ||||
| 15 7 46 25 70 40 98 64 83 69 228 -76 138 -139 184 -172 270 -193 92 -23 172 | ||||
| -12 265 36 40 21 440 412 487 476 77 107 85 261 19 386 -12 23 -83 103 -157 | ||||
| 178 l-136 137 19 26 c22 32 48 75 76 128 12 22 24 42 28 45 6 6 102 202 103 | ||||
| 211 1 3 12 30 25 60 13 30 27 63 32 74 17 42 71 203 85 255 19 69 45 170 48 | ||||
| 190 2 13 32 15 187 16 201 2 230 5 296 35 46 21 70 38 110 79 24 24 75 104 80 | ||||
| 125 1 5 7 31 13 56 12 49 9 646 -3 688 -33 117 -150 231 -262 257 -16 3 -115 | ||||
| 7 -219 8 -211 3 -199 -2 -216 83 -5 26 -19 80 -30 118 -12 39 -22 77 -25 85 | ||||
| -2 8 -4 16 -5 18 -2 1 -3 5 -5 10 -1 4 -5 14 -8 22 -4 8 -14 38 -23 65 -9 28 | ||||
| -21 61 -27 75 -5 14 -11 30 -12 36 -7 29 -149 311 -202 400 l-60 102 136 138 | ||||
| c91 92 144 155 159 187 68 144 44 300 -63 415 -114 123 -402 403 -435 424 -95 | ||||
| 58 -234 73 -325 34 -22 -9 -42 -17 -45 -18 -13 -1 -86 -67 -191 -172 -64 -64 | ||||
| -124 -116 -132 -116 -9 0 -25 10 -37 22 -11 12 -20 18 -20 12 0 -5 -4 -4 -8 2 | ||||
| -9 13 -185 111 -277 154 -73 34 -263 111 -280 114 -5 1 -14 4 -20 7 -5 4 -37 | ||||
| 14 -70 24 -33 10 -87 26 -120 36 -33 10 -85 23 -115 29 l-55 12 0 207 -1 206 | ||||
| -29 60 c-29 60 -125 165 -151 165 -8 0 -14 4 -14 8 0 5 -26 16 -57 25 -48 13 | ||||
| -115 16 -373 16 -293 1 -319 -1 -375 -20z"/> | ||||
| <path d="M3577 5134 c-1 -1 -44 -4 -94 -8 -51 -4 -98 -8 -105 -11 -7 -2 -24 | ||||
| -6 -38 -9 -106 -19 -283 -79 -375 -126 -65 -34 -177 -100 -185 -109 -3 -4 -23 | ||||
| -18 -45 -33 -22 -15 -56 -42 -76 -60 -20 -18 -47 -43 -60 -55 -74 -66 -182 | ||||
| -197 -237 -288 -56 -90 -130 -249 -152 -325 -13 -41 -27 -80 -31 -86 -5 -6 -7 | ||||
| -13 -4 -16 3 -2 0 -20 -5 -39 -6 -19 -13 -52 -16 -74 -3 -22 -8 -51 -10 -65 | ||||
| -3 -14 -6 -88 -8 -165 -3 -123 7 -245 28 -345 3 -14 8 -35 10 -48 3 -12 15 | ||||
| -54 28 -92 l22 -71 -1037 -1037 c-570 -570 -1053 -1059 -1072 -1086 -19 -27 | ||||
| -35 -55 -35 -63 0 -7 -3 -13 -8 -13 -8 0 -31 -48 -37 -79 -2 -12 -6 -25 -9 | ||||
| -29 -21 -34 -29 -211 -14 -291 9 -51 55 -180 67 -191 3 -3 12 -17 19 -31 17 | ||||
| -33 116 -137 162 -171 110 -80 245 -120 388 -114 110 5 176 22 277 74 65 33 | ||||
| 151 116 1128 1092 l1058 1058 49 -19 c27 -10 59 -20 72 -23 13 -3 32 -7 43 | ||||
| -10 148 -34 189 -39 350 -39 150 -1 226 7 345 34 82 19 233 70 275 92 11 6 22 | ||||
| 12 25 12 23 4 240 133 260 155 3 3 25 21 50 41 134 108 265 260 348 404 49 86 | ||||
| 121 250 137 315 1 3 4 12 7 20 10 24 37 146 44 195 11 70 14 116 15 215 0 101 | ||||
| -8 237 -16 250 -2 3 -6 26 -10 50 -22 151 -160 233 -287 171 -28 -14 -133 | ||||
| -111 -303 -282 -143 -144 -282 -279 -309 -301 -131 -104 -308 -135 -469 -81 | ||||
| -39 14 -74 28 -77 31 -3 4 -17 13 -32 21 -39 20 -109 89 -144 142 -106 157 | ||||
| -115 340 -26 513 31 59 78 111 328 362 160 161 298 306 307 322 8 16 14 57 15 | ||||
| 90 0 63 -24 113 -73 154 -34 29 -172 62 -280 68 -69 4 -175 6 -178 4z"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 5.6 KiB | 
							
								
								
									
										19
									
								
								static/site.webmanifest
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,19 @@ | ||||
| { | ||||
|     "name": "IT - Tools", | ||||
|     "short_name": "IT Tools", | ||||
|     "icons": [ | ||||
|         { | ||||
|             "src": "/android-chrome-192x192.png", | ||||
|             "sizes": "192x192", | ||||
|             "type": "image/png" | ||||
|         }, | ||||
|         { | ||||
|             "src": "/android-chrome-512x512.png", | ||||
|             "sizes": "512x512", | ||||
|             "type": "image/png" | ||||
|         } | ||||
|     ], | ||||
|     "theme_color": "#ffffff", | ||||
|     "background_color": "#ffffff", | ||||
|     "display": "standalone" | ||||
| } | ||||
| @ -28,7 +28,8 @@ | ||||
|       "@types/node", | ||||
|       "@nuxtjs/toast", | ||||
|       "@nuxt/types", | ||||
|       "~/types/custom.d.ts" | ||||
|       "~/types/custom.d.ts", | ||||
|       "vuetify" | ||||
|     ] | ||||
|   }, | ||||
|   "exclude": [ | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| import {RouteConfig} from '@nuxt/types/config/router'; | ||||
| 
 | ||||
| interface ToolConfig { | ||||
|   title: string; | ||||
|   description: string; | ||||
| @ -6,5 +8,6 @@ interface ToolConfig { | ||||
| } | ||||
| 
 | ||||
| type ToolConfigMethod = () => ToolConfig; | ||||
| type ToolRouteConfig = RouteConfig & {config: ToolConfig} | ||||
| 
 | ||||
| export {ToolConfig, ToolConfigMethod} | ||||
| export {ToolConfig, ToolConfigMethod, ToolRouteConfig} | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| function capitalise(s: string) { | ||||
|   return s.charAt(0).toUpperCase() + s.slice(1) | ||||
| } | ||||
| const capitalise = (s: string) => s.charAt(0).toUpperCase() + s.slice(1) | ||||
| 
 | ||||
| export {capitalise} | ||||
| const shuffle = (s: string) => s.split('').sort(() => 0.5 - Math.random()).join('') | ||||
| 
 | ||||
| export {capitalise, shuffle} | ||||
|  | ||||