Compare commits

...

5 Commits

Author SHA1 Message Date
Corentin Thomasset
072083832d chore(release): 2.19.0 2023-02-06 23:14:52 +01:00
Corentin Thomasset
c934c4e50c feat(new-tool): keycode info 2023-02-06 23:13:20 +01:00
Corentin Thomasset
4a5734d4a3 chore(release): 2.18.0 2023-02-04 09:59:56 +01:00
Tsonglew
f708f5091e feat(new-tool): json minify (#265)
Co-authored-by: Corentin THOMASSET <corentin.thomasset74@gmail.com>
2023-02-04 09:56:17 +01:00
Corentin Thomasset
db817a2459 refactor(tools): config in query params 2023-01-28 21:06:16 +01:00
12 changed files with 311 additions and 26 deletions

View File

@@ -2,6 +2,25 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [2.19.0](https://github.com/CorentinTh/it-tools/compare/v2.18.0...v2.19.0) (2023-02-06)
### Features
* **new-tool:** keycode info ([c934c4e](https://github.com/CorentinTh/it-tools/commit/c934c4e50ca1a129b80b786a5d9a7dbc33ad9ba3))
## [2.18.0](https://github.com/CorentinTh/it-tools/compare/v2.17.0...v2.18.0) (2023-02-04)
### Features
* **new-tool:** json minify ([#265](https://github.com/CorentinTh/it-tools/issues/265)) ([f708f50](https://github.com/CorentinTh/it-tools/commit/f708f5091e2182fc88e7cf3e7d23b3d05edc04da))
### Refactors
* **tools:** config in query params ([db817a2](https://github.com/CorentinTh/it-tools/commit/db817a2459e23bd096274a7f91815d613d5f7ff4))
## [2.17.0](https://github.com/CorentinTh/it-tools/compare/v2.16.0...v2.17.0) (2023-01-13)

View File

@@ -1,6 +1,6 @@
{
"name": "it-tools",
"version": "2.17.0",
"version": "2.19.0",
"description": "Collection of handy online tools for developers, with great UX. ",
"keywords": [
"productivity",
@@ -37,6 +37,7 @@
"@vicons/tabler": "^0.12.0",
"@vueuse/core": "^8.9.4",
"@vueuse/head": "^0.7.13",
"@vueuse/router": "^9.11.0",
"bcryptjs": "^2.4.3",
"change-case": "^4.1.2",
"colord": "^2.9.3",

93
pnpm-lock.yaml generated
View File

@@ -24,6 +24,7 @@ specifiers:
'@vue/tsconfig': ^0.1.3
'@vueuse/core': ^8.9.4
'@vueuse/head': ^0.7.13
'@vueuse/router': ^9.11.0
bcryptjs: ^2.4.3
c8: ^7.12.0
change-case: ^4.1.2
@@ -76,6 +77,7 @@ dependencies:
'@vicons/tabler': 0.12.0
'@vueuse/core': 8.9.4_vue@3.2.45
'@vueuse/head': 0.7.13_vue@3.2.45
'@vueuse/router': 9.11.0_xsxatmlnmmg5bcuv3xdnj6fj7y
bcryptjs: 2.4.3
change-case: 4.1.2
colord: 2.9.3
@@ -124,7 +126,7 @@ devDependencies:
eslint: 8.27.0
eslint-config-prettier: 8.5.0_eslint@8.27.0
eslint-import-resolver-typescript: 3.5.2_dcpv4nbdr5ks2h5677xdltrk6e
eslint-plugin-import: 2.26.0_eslint@8.27.0
eslint-plugin-import: 2.26.0_gbipkkcbnjmysmpjttq6vkmfqq
eslint-plugin-vue: 8.7.1_eslint@8.27.0
jsdom: 19.0.0
less: 4.1.3
@@ -134,7 +136,7 @@ devDependencies:
typescript: 4.5.5
vite: 2.9.15_less@4.1.3
vite-plugin-md: 0.12.4_vite@2.9.15
vite-plugin-pwa: 0.11.13_vite@2.9.15
vite-plugin-pwa: 0.11.13_7mbbuzxp22mje5bxdolj2b6yg4
vite-svg-loader: 3.6.0
vitest: 0.13.1_uwxj23d3xojfwkqpytqc7pyhry
vue-tsc: 0.31.4_typescript@4.5.5
@@ -2068,6 +2070,10 @@ packages:
peerDependencies:
eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
eslint-plugin-vue: ^8.0.1
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/eslint-plugin': 5.42.1_vfr6z4qvdp6defk3ked6x75zyi
'@typescript-eslint/parser': 5.42.1_4rqwsplhh2ekz63wktwk7d7ht4
@@ -2077,7 +2083,6 @@ packages:
vue-eslint-parser: 8.3.0_eslint@8.27.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@vue/reactivity-transform/3.2.45:
@@ -2106,7 +2111,6 @@ packages:
'@vue/runtime-core': 3.2.45
'@vue/shared': 3.2.45
csstype: 2.6.21
dev: false
/@vue/server-renderer/3.2.45_vue@3.2.45:
resolution: {integrity: sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==}
@@ -2116,7 +2120,6 @@ packages:
'@vue/compiler-ssr': 3.2.45
'@vue/shared': 3.2.45
vue: 3.2.45
dev: false
/@vue/shared/3.2.45:
resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
@@ -2173,6 +2176,19 @@ packages:
resolution: {integrity: sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw==}
dev: false
/@vueuse/router/9.11.0_xsxatmlnmmg5bcuv3xdnj6fj7y:
resolution: {integrity: sha512-AlaQzbUy3XeqeoIapQxLvJjPzbdiO/Dt1thQhebP4iFUjc61a7WrboULxNBY+jEH0XipwK/T/b9mBnmn/jEStA==}
peerDependencies:
vue-router: '>=4.0.0-rc.1'
dependencies:
'@vueuse/shared': 9.11.0_vue@3.2.45
vue-demi: 0.13.11_vue@3.2.45
vue-router: 4.1.6_vue@3.2.45
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/@vueuse/shared/8.9.4_vue@3.2.45:
resolution: {integrity: sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag==}
peerDependencies:
@@ -2188,6 +2204,15 @@ packages:
vue-demi: 0.13.11_vue@3.2.45
dev: false
/@vueuse/shared/9.11.0_vue@3.2.45:
resolution: {integrity: sha512-8lO7wD5abYxupKy2KynH1pSgP715ky6iCrWYb8aX2AuAVi9uHXj7qE1dw6BnmArSaLHci4x9iuzWPCpAzUkC/A==}
dependencies:
vue-demi: 0.13.11_vue@3.2.45
transitivePeerDependencies:
- '@vue/composition-api'
- vue
dev: false
/@vueuse/shared/9.5.0_vue@3.2.45:
resolution: {integrity: sha512-HnnCWU1Vg9CVWRCcI8ohDKDRB2Sc4bTgT1XAIaoLSfVHHn+TKbrox6pd3klCSw4UDxkhDfOk8cAdcK+Z5KleCA==}
dependencies:
@@ -2861,8 +2886,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
dependencies:
is-text-path: 1.0.1
JSONStream: 1.3.5
is-text-path: 1.0.1
lodash: 4.17.21
meow: 8.1.2
split2: 3.2.2
@@ -2991,7 +3016,6 @@ packages:
/csstype/2.6.21:
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
dev: false
/csstype/3.0.11:
resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==}
@@ -3030,12 +3054,22 @@ packages:
/debug/2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.0.0
dev: true
/debug/3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.3
dev: true
@@ -3574,6 +3608,8 @@ packages:
dependencies:
debug: 3.2.7
resolve: 1.22.1
transitivePeerDependencies:
- supports-color
dev: true
/eslint-import-resolver-typescript/3.5.2_dcpv4nbdr5ks2h5677xdltrk6e:
@@ -3586,7 +3622,7 @@ packages:
debug: 4.3.4
enhanced-resolve: 5.10.0
eslint: 8.27.0
eslint-plugin-import: 2.26.0_eslint@8.27.0
eslint-plugin-import: 2.26.0_gbipkkcbnjmysmpjttq6vkmfqq
get-tsconfig: 4.2.0
globby: 13.1.2
is-core-module: 2.11.0
@@ -3596,32 +3632,54 @@ packages:
- supports-color
dev: true
/eslint-module-utils/2.7.4_eslint@8.27.0:
/eslint-module-utils/2.7.4_idrr6ghswzssuopqxluk4kfum4:
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: '*'
eslint-import-resolver-node: '*'
eslint-import-resolver-typescript: '*'
eslint-import-resolver-webpack: '*'
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
eslint:
optional: true
eslint-import-resolver-node:
optional: true
eslint-import-resolver-typescript:
optional: true
eslint-import-resolver-webpack:
optional: true
dependencies:
'@typescript-eslint/parser': 5.42.1_4rqwsplhh2ekz63wktwk7d7ht4
debug: 3.2.7
eslint: 8.27.0
eslint-import-resolver-node: 0.3.6
eslint-import-resolver-typescript: 3.5.2_dcpv4nbdr5ks2h5677xdltrk6e
transitivePeerDependencies:
- supports-color
dev: true
/eslint-plugin-import/2.26.0_eslint@8.27.0:
/eslint-plugin-import/2.26.0_gbipkkcbnjmysmpjttq6vkmfqq:
resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==}
engines: {node: '>=4'}
peerDependencies:
'@typescript-eslint/parser': '*'
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
peerDependenciesMeta:
'@typescript-eslint/parser':
optional: true
dependencies:
'@typescript-eslint/parser': 5.42.1_4rqwsplhh2ekz63wktwk7d7ht4
array-includes: 3.1.6
array.prototype.flat: 1.3.1
debug: 2.6.9
doctrine: 2.1.0
eslint: 8.27.0
eslint-import-resolver-node: 0.3.6
eslint-module-utils: 2.7.4_eslint@8.27.0
eslint-module-utils: 2.7.4_idrr6ghswzssuopqxluk4kfum4
has: 1.0.3
is-core-module: 2.11.0
is-glob: 4.0.3
@@ -3629,6 +3687,10 @@ packages:
object.values: 1.1.6
resolve: 1.22.1
tsconfig-paths: 3.14.1
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
dev: true
/eslint-plugin-prettier/4.2.1_v7o5sx5x3wbs57ifz6wc4f76we:
@@ -4834,6 +4896,8 @@ packages:
mime: 1.6.0
needle: 3.1.0
source-map: 0.6.1
transitivePeerDependencies:
- supports-color
dev: true
/leven/3.1.0:
@@ -5176,6 +5240,8 @@ packages:
debug: 3.2.7
iconv-lite: 0.6.3
sax: 1.2.4
transitivePeerDependencies:
- supports-color
dev: true
optional: true
@@ -6542,7 +6608,6 @@ packages:
resolution: {integrity: sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/uc.micro/1.0.6:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
@@ -6699,10 +6764,11 @@ packages:
vite: 2.9.15_less@4.1.3
dev: true
/vite-plugin-pwa/0.11.13_vite@2.9.15:
/vite-plugin-pwa/0.11.13_7mbbuzxp22mje5bxdolj2b6yg4:
resolution: {integrity: sha512-Ssj14m3TRVLfkFEAWSMcFE2d1cSdEZyrVTzfY2lSL+umHYvcIFHVDAY143sygtBCb44OPczsAOmWwBTxwOvh7g==}
peerDependencies:
vite: ^2.0.0
workbox-window: ^6.4.0
dependencies:
debug: 4.3.4
fast-glob: 3.2.12
@@ -6968,7 +7034,6 @@ packages:
'@vue/runtime-dom': 3.2.45
'@vue/server-renderer': 3.2.45_vue@3.2.45
'@vue/shared': 3.2.45
dev: false
/vueuc/0.4.49_vue@3.2.45:
resolution: {integrity: sha512-WarAC44a/Yx78CxkAgROYLq+LkAeCGA/6wHidVoFmHLbzyF3SiP2nzRNGD/8zJeJInXv18EnWK6A//eGgMMq8w==}

View File

@@ -0,0 +1,35 @@
import { useRouteQuery } from '@vueuse/router';
import { computed } from 'vue';
export { useQueryParam };
const transformers = {
number: {
fromQuery: (value: string) => Number(value),
toQuery: (value: number) => String(value),
},
string: {
fromQuery: (value: string) => value,
toQuery: (value: string) => value,
},
boolean: {
fromQuery: (value: string) => value.toLowerCase() === 'true',
toQuery: (value: boolean) => (value ? 'true' : 'false'),
},
};
function useQueryParam<T>({ name, defaultValue }: { name: string; defaultValue: T }) {
const type = typeof defaultValue;
const transformer = transformers[type as keyof typeof transformers] ?? transformers.string;
const proxy = useRouteQuery(name, transformer.toQuery(defaultValue as never));
return computed<T>({
get() {
return transformer.fromQuery(proxy.value) as T;
},
set(value) {
proxy.value = transformer.toQuery(value as never);
},
});
}

View File

@@ -40,6 +40,7 @@
</template>
<script setup lang="ts">
import { useQueryParam } from '@/composable/queryParams';
import { enc, lib, MD5, RIPEMD160, SHA1, SHA224, SHA256, SHA3, SHA384, SHA512 } from 'crypto-js';
import { ref } from 'vue';
import InputCopyable from '../../components/InputCopyable.vue';
@@ -59,7 +60,7 @@ const algos = {
type AlgoNames = keyof typeof algos;
type Encoding = keyof typeof enc | 'Bin';
const algoNames = Object.keys(algos) as AlgoNames[];
const encoding = ref<Encoding>('Hex');
const encoding = useQueryParam<Encoding>({ defaultValue: 'Hex', name: 'encoding' });
const clearText = ref('');
function formatWithEncoding(words: lib.WordArray, encoding: Encoding) {

View File

@@ -1,6 +1,8 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
import { tool as keycodeInfo } from './keycode-info';
import { tool as jsonMinify } from './json-minify';
import { tool as bcrypt } from './bcrypt';
import { tool as bip39 } from './bip39-generator';
import { tool as caseConverter } from './case-converter';
@@ -66,6 +68,7 @@ export const toolsByCategory: ToolCategory[] = [
otpCodeGeneratorAndValidator,
mimeTypes,
jwtParser,
keycodeInfo,
],
},
{
@@ -74,7 +77,7 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Development',
components: [gitMemo, randomPortGenerator, crontabGenerator, jsonViewer, sqlPrettify, chmodCalculator],
components: [gitMemo, randomPortGenerator, crontabGenerator, jsonViewer, jsonMinify, sqlPrettify, chmodCalculator],
},
{
name: 'Math',

View File

@@ -0,0 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'JSON minify',
path: '/json-minify',
description: 'Minify and compress your JSON by removing unnecessary white spaces.',
keywords: ['json', 'minify', 'format'],
component: () => import('./json-minify.vue'),
icon: Braces,
});

View File

@@ -0,0 +1,57 @@
<template>
<n-form-item
label="Your raw json"
:feedback="rawJsonValidation.message"
:validation-status="rawJsonValidation.status"
>
<n-input
ref="inputElement"
v-model:value="rawJson"
placeholder="Paste your raw json here..."
type="textarea"
rows="20"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
/>
</n-form-item>
<n-form-item label="Minify version of your JSON">
<textarea-copyable :value="cleanJson" language="json" :follow-height-of="inputElement" />
</n-form-item>
</template>
<script setup lang="ts">
import TextareaCopyable from '@/components/TextareaCopyable.vue';
import { useValidation } from '@/composable/validation';
import { withDefaultOnError } from '@/utils/defaults';
import JSON5 from 'json5';
import { computed, ref } from 'vue';
const inputElement = ref<HTMLElement>();
const rawJson = ref('{\n\t"hello": [\n\t\t"world"\n\t]\n}');
const cleanJson = computed(() => withDefaultOnError(() => JSON.stringify(JSON5.parse(rawJson.value), null, 0), ''));
const rawJsonValidation = useValidation({
source: rawJson,
rules: [
{
validator: (v) => v === '' || JSON5.parse(v),
message: 'Provided JSON is not valid.',
},
],
});
</script>
<style lang="less" scoped>
.result-card {
position: relative;
.copy-button {
position: absolute;
top: 10px;
right: 10px;
}
}
</style>

View File

@@ -0,0 +1,26 @@
import { Keyboard } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'Keycode info',
path: '/keycode-info',
description: 'Find the javascript keycode, code, location and modifiers of any pressed key.',
keywords: [
'keycode',
'info',
'code',
'javascript',
'event',
'keycodes',
'which',
'keyboard',
'press',
'modifier',
'alt',
'ctrl',
'meta',
'shift',
],
component: () => import('./keycode-info.vue'),
icon: Keyboard,
});

View File

@@ -0,0 +1,67 @@
<template>
<div>
<n-card style="text-align: center; padding: 40px 0; margin-bottom: 26px">
<n-h2 v-if="event">{{ event.key }}</n-h2>
<n-text strong depth="3">Press the key on your keyboard you want to now stuff</n-text>
</n-card>
<n-input-group v-for="({ label, value, placeholder }, i) of fields" :key="i" style="margin-bottom: 5px">
<n-input-group-label style="flex: 0 0 150px"> {{ label }} </n-input-group-label>
<input-copyable :value="value" readonly :placeholder="placeholder" />
</n-input-group>
</div>
</template>
<script setup lang="ts">
import { useEventListener } from '@vueuse/core';
import { computed, ref } from 'vue';
import InputCopyable from '../../components/InputCopyable.vue';
const event = ref<KeyboardEvent>();
useEventListener(document, 'keydown', (e) => {
event.value = e;
});
const fields = computed(() => {
if (!event.value) return [];
return [
{
label: 'Key :',
value: event.value.key,
placeholder: 'Key name...',
},
{
label: 'Keycode :',
value: String(event.value.keyCode),
placeholder: 'Keycode...',
},
{
label: 'Code :',
value: event.value.code,
placeholder: 'Code...',
},
{
label: 'Location :',
value: String(event.value.location),
placeholder: 'Code...',
},
{
label: 'Modifiers :',
value: [
event.value.metaKey && 'Meta',
event.value.shiftKey && 'Shift',
event.value.ctrlKey && 'Ctrl',
event.value.altKey && 'Alt',
]
.filter(Boolean)
.join(' + '),
placeholder: 'None',
},
];
});
</script>
<style lang="less" scoped></style>

View File

@@ -54,18 +54,19 @@
<script setup lang="ts">
import { useCopy } from '@/composable/copy';
import { ref, watch } from 'vue';
import { useQueryParam } from '@/composable/queryParams';
import { createToken } from './token-generator.service';
const token = ref('');
const length = ref(64);
const length = useQueryParam({ name: 'length', defaultValue: 64 });
const { copy } = useCopy({ source: token, text: 'Token copied to the clipboard' });
const withUppercase = ref(true);
const withLowercase = ref(true);
const withNumbers = ref(true);
const withSymbols = ref(false);
const withUppercase = useQueryParam({ name: 'uppercase', defaultValue: true });
const withLowercase = useQueryParam({ name: 'lowercase', defaultValue: true });
const withNumbers = useQueryParam({ name: 'numbers', defaultValue: true });
const withSymbols = useQueryParam({ name: 'symbols', defaultValue: false });
watch([withUppercase, withLowercase, withNumbers, withSymbols, length], refreshToken);
watch([withUppercase, withLowercase, withNumbers, withSymbols, length], refreshToken, { immediate: true });
function refreshToken() {
token.value = createToken({
@@ -76,6 +77,4 @@ function refreshToken() {
withSymbols: withSymbols.value,
});
}
refreshToken();
</script>

View File

@@ -32,8 +32,9 @@
import { useCopy } from '@/composable/copy';
import { ref, watch } from 'vue';
import { v4 as generateUUID } from 'uuid';
import { useQueryParam } from '@/composable/queryParams';
const count = ref(1);
const count = useQueryParam({ defaultValue: 1, name: 'count' });
const uuids = ref('');