Compare commits

...

9 Commits

Author SHA1 Message Date
Corentin Thomasset
cf5e4d9056 chore(release): 2.10.3 2022-08-14 10:55:06 +02:00
Corentin Thomasset
992f96b48a refactor(share): updated twitter meta tags 2022-08-14 10:53:45 +02:00
Corentin Thomasset
fcf4cfe64d refactor(share): new share banner 2022-08-14 10:53:39 +02:00
Corentin Thomasset
f54223fb0a refactor(validation): simplified validation management with helpers 2022-08-04 21:59:48 +02:00
Corentin Thomasset
b38ab82d05 chore(release): 2.10.2 2022-08-04 23:29:47 +02:00
Corentin Thomasset
f6cd9b76d3 refactor(dry): mutualised duplicated code with withDefaultOnError 2022-08-04 23:14:32 +02:00
Corentin Thomasset
208a373fd0 refactor(lint): added import rules 2022-08-04 22:46:50 +02:00
Corentin Thomasset
8089c60000 refactor(json-prettifier): more permissive json parser 2022-08-04 22:18:15 +02:00
Corentin Thomasset
d30cd8a9ab refactor(home): removed new tool first sort 2022-08-04 21:59:22 +02:00
26 changed files with 923 additions and 115 deletions

View File

@@ -7,15 +7,29 @@ module.exports = {
'plugin:vue/vue3-essential',
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:vue/vue3-recommended',
'@vue/eslint-config-typescript/recommended',
'@vue/eslint-config-prettier',
'plugin:import/recommended',
],
settings: {
'import/resolver': { typescript: { project: './tsconfig.app.json' } },
},
env: {
'vue/setup-compiler-macros': true,
},
rules: {
'vue/multi-word-component-names': ['off'],
'prettier/prettier': ['error'],
'import/no-duplicates': ['error', { considerQueryString: true }],
'import/order': ['error', { groups: [['builtin', 'external', 'internal']] }],
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
ts: 'never',
},
],
},
};

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.10.3](https://github.com/CorentinTh/it-tools/compare/v2.10.2...v2.10.3) (2022-08-14)
### Refactors
* **share:** new share banner ([fcf4cfe](https://github.com/CorentinTh/it-tools/commit/fcf4cfe64d4c1c3814137c8ff23b83a1ca0d502d))
* **share:** updated twitter meta tags ([992f96b](https://github.com/CorentinTh/it-tools/commit/992f96b48a89e2793ccf75fb9e28b2ec7b7f62b6))
* **validation:** simplified validation management with helpers ([f54223f](https://github.com/CorentinTh/it-tools/commit/f54223fb0aaedbd101b5d3dc4176053533bb936a))
### [2.10.2](https://github.com/CorentinTh/it-tools/compare/v2.10.1...v2.10.2) (2022-08-04)
### Refactors
* **dry:** mutualised duplicated code with withDefaultOnError ([f6cd9b7](https://github.com/CorentinTh/it-tools/commit/f6cd9b76d38800e1a1f63d07152fc96cda562795))
* **home:** removed new tool first sort ([d30cd8a](https://github.com/CorentinTh/it-tools/commit/d30cd8a9abc3298c0a0b05f249e54318bb4537f2))
* **json-prettifier:** more permissive json parser ([8089c60](https://github.com/CorentinTh/it-tools/commit/8089c60000000c42c821c6586c128d3d2b248885))
* **lint:** added import rules ([208a373](https://github.com/CorentinTh/it-tools/commit/208a373fd08ac550778745eb6e4536bf02537da7))
### [2.10.1](https://github.com/CorentinTh/it-tools/compare/v2.10.0...v2.10.1) (2022-08-04)

View File

@@ -31,17 +31,21 @@
property="og:description"
content="Collection of handy online tools for developers, with great UX. IT Tools is a free and open-source collection of handy online tools for developers & people working in IT."
/>
<meta property="og:image" content="/banner.png" />
<meta property="og:image" content="https://it-tools.tech/banner.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="twitter:domain" content="it-tools.tech" />
<meta property="twitter:url" content="https://it-tools.tech/" />
<meta name="twitter:domain" content="it-tools.tech" />
<meta name="twitter:url" content="https://it-tools.tech/" />
<meta name="twitter:site" content="@ittoolsdottech" />
<meta name="twitter:creator" content="@cthmsst" />
<meta name="twitter:title" content="IT Tools - Handy online tools for developers" />
<meta
name="twitter:description"
content="Collection of handy online tools for developers, with great UX. IT Tools is a free and open-source collection of handy online tools for developers & people working in IT."
/>
<meta name="twitter:image" content="/banner.png" />
<meta name="twitter:image" content="https://it-tools.tech/banner.png" />
<meta name="twitter:image:alt" content="IT Tools - Handy online tools for developers" />
</head>
<body>
<div id="app"></div>

808
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "it-tools",
"version": "2.10.1",
"version": "2.10.3",
"description": "Collection of handy online tools for developers, with great UX. ",
"keywords": [
"productivity",
@@ -68,6 +68,7 @@
"@types/qrcode": "^1.4.2",
"@types/randombytes": "^2.0.0",
"@types/uuid": "^8.3.4",
"@typescript-eslint/parser": "^5.32.0",
"@vitejs/plugin-vue": "^2.2.2",
"@vitejs/plugin-vue-jsx": "^1.3.7",
"@vue/eslint-config-prettier": "^7.0.0",
@@ -77,6 +78,8 @@
"c8": "^7.11.0",
"eslint": "^8.5.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.4.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-vue": "^8.2.0",
"jsdom": "^19.0.0",
"less": "^4.1.2",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 155 KiB

View File

@@ -1,9 +1,9 @@
<script setup lang="ts">
import { layouts } from './layouts';
import { computed } from 'vue';
import { useRoute, RouterView } from 'vue-router';
import { darkThemeOverrides, lightThemeOverrides } from './themes';
import { darkTheme, NGlobalStyle, NMessageProvider } from 'naive-ui';
import { darkThemeOverrides, lightThemeOverrides } from './themes';
import { layouts } from './layouts';
import { useStyleStore } from './stores/style.store';
const route = useRoute();

View File

@@ -4,14 +4,14 @@ import { h } from 'vue';
import { RouterLink, useRoute } from 'vue-router';
import { Heart, Menu2, Home2 } from '@vicons/tabler';
import { toolsByCategory } from '@/tools';
import SearchBar from '../components/SearchBar.vue';
import { useStyleStore } from '@/stores/style.store';
import HeroGradient from '../assets/hero-gradient.svg?component';
import MenuLayout from '../components/MenuLayout.vue';
import NavbarButtons from '../components/NavbarButtons.vue';
import { config } from '@/config';
import MenuIconItem from '@/components/MenuIconItem.vue';
import type { ITool } from '@/tools/tool';
import SearchBar from '../components/SearchBar.vue';
import HeroGradient from '../assets/hero-gradient.svg?component';
import MenuLayout from '../components/MenuLayout.vue';
import NavbarButtons from '../components/NavbarButtons.vue';
const themeVars = useThemeVars();
const route = useRoute();

View File

@@ -1,10 +1,10 @@
<script lang="ts" setup>
import { useRoute } from 'vue-router';
import BaseLayout from './base.layout.vue';
import { useHead } from '@vueuse/head';
import type { HeadObject } from '@vueuse/head';
import { computed } from 'vue';
import { useThemeVars } from 'naive-ui';
import BaseLayout from './base.layout.vue';
const route = useRoute();
const theme = useThemeVars();

View File

@@ -1,6 +1,7 @@
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { createHead } from '@vueuse/head';
// eslint-disable-next-line import/no-unresolved
import { registerSW } from 'virtual:pwa-register';
import { plausible } from './plugins/plausible.plugin';

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { toolsWithCategory } from '@/tools';
import ToolCard from '../components/ToolCard.vue';
import { useHead } from '@vueuse/head';
import ToolCard from '../components/ToolCard.vue';
useHead({ title: 'IT Tools - Handy online tools for developers' });
</script>
@@ -9,13 +9,7 @@ useHead({ title: 'IT Tools - Handy online tools for developers' });
<template>
<div class="home-page">
<n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
<n-gi
v-for="tool in [
...toolsWithCategory.filter(({ isNew }) => isNew),
...toolsWithCategory.filter(({ isNew }) => !isNew),
]"
:key="tool.name"
>
<n-gi v-for="tool in toolsWithCategory" :key="tool.name">
<tool-card :tool="tool" />
</n-gi>
</n-grid>

View File

@@ -1,5 +1,5 @@
import { layouts } from './layouts/index';
import { createRouter, createWebHistory } from 'vue-router';
import { layouts } from './layouts/index';
import HomePage from './pages/Home.page.vue';
import NotFound from './pages/404.page.vue';
import { tools } from './tools';

View File

@@ -60,6 +60,8 @@
<script setup lang="ts">
import { useCopy } from '@/composable/copy';
import { useValidation } from '@/composable/validation';
import { isNotThrowing } from '@/utils/boolean';
import { withDefaultOnError } from '@/utils/defaults';
import {
chineseSimplifiedWordList,
chineseTraditionalWordList,
@@ -97,20 +99,11 @@ const passphraseInput = ref('');
const language = ref<keyof typeof languages>('English');
const passphrase = computed({
get() {
try {
return entropyToMnemonic(entropy.value, languages[language.value]);
} catch (_) {
return passphraseInput.value;
}
return withDefaultOnError(() => entropyToMnemonic(entropy.value, languages[language.value]), passphraseInput.value);
},
set(value: string) {
passphraseInput.value = value;
try {
entropy.value = mnemonicToEntropy(value, languages[language.value]);
} catch (_) {
entropy.value = '';
}
entropy.value = withDefaultOnError(() => mnemonicToEntropy(value, languages[language.value]), '');
},
});
@@ -132,14 +125,7 @@ const mnemonicValidation = useValidation({
source: passphrase,
rules: [
{
validator: (value) => {
try {
mnemonicToEntropy(value, languages[language.value]);
return true;
} catch (_) {
return false;
}
},
validator: (value) => isNotThrowing(() => mnemonicToEntropy(value, languages[language.value])),
message: 'Invalid mnemonic',
},
],

View File

@@ -46,8 +46,6 @@
<script setup lang="ts">
import { ref } from 'vue';
import InputCopyable from '../../components/InputCopyable.vue';
import {
camelCase,
capitalCase,
@@ -61,6 +59,7 @@ import {
sentenceCase,
snakeCase,
} from 'change-case';
import InputCopyable from '../../components/InputCopyable.vue';
const input = ref('lorem ipsum dolor sit amet');
</script>

View File

@@ -36,12 +36,12 @@
<script setup lang="ts">
import { ref } from 'vue';
import { colord, extend } from 'colord';
import InputCopyable from '../../components/InputCopyable.vue';
import cmykPlugin from 'colord/plugins/cmyk';
import hwbPlugin from 'colord/plugins/hwb';
import namesPlugin from 'colord/plugins/names';
import lchPlugin from 'colord/plugins/lch';
import InputCopyable from '../../components/InputCopyable.vue';
extend([cmykPlugin, hwbPlugin, namesPlugin, lchPlugin]);

View File

@@ -51,7 +51,10 @@
</template>
<script setup lang="ts">
// Duplicate issue with sub directory
// eslint-disable-next-line import/no-duplicates
import { addMilliseconds, formatRelative } from 'date-fns';
// eslint-disable-next-line import/no-duplicates
import { enGB } from 'date-fns/locale';
import { computed, ref } from 'vue';
import { formatMsDuration } from './eta-calculator.service';

View File

@@ -5,8 +5,8 @@
</template>
<script setup lang="ts">
import Memo from './git-memo.md';
import { useThemeVars } from 'naive-ui';
import Memo from './git-memo.md';
const themeVars = useThemeVars();
</script>

View File

@@ -67,9 +67,9 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useStyleStore } from '@/stores/style.store';
import { convertBase } from './integer-base-converter.model';
import InputCopyable from '../../components/InputCopyable.vue';
import { useStyleStore } from '@/stores/style.store';
const styleStore = useStyleStore();

View File

@@ -24,25 +24,21 @@
<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('{"hello": "world"}');
const cleanJson = computed(() => {
try {
return JSON.stringify(JSON.parse(rawJson.value), null, 3);
} catch (_) {
return '';
}
});
const cleanJson = computed(() => withDefaultOnError(() => JSON.stringify(JSON5.parse(rawJson.value), null, 3), ''));
const rawJsonValidation = useValidation({
source: rawJson,
rules: [
{
validator: (v) => v === '' || JSON.parse(v),
message: 'Invalid json',
validator: (v) => v === '' || JSON5.parse(v),
message: 'Provided JSON is not valid.',
},
],
});

View File

@@ -30,8 +30,8 @@
<script setup lang="ts">
import { useCopy } from '@/composable/copy';
import { ref, computed } from 'vue';
import { generateLoremIpsum } from './lorem-ipsum-generator.service';
import { randIntFromInterval } from '@/utils/random';
import { generateLoremIpsum } from './lorem-ipsum-generator.service';
const paragraphs = ref(1);
const sentences = ref([3, 8]);

View File

@@ -21,18 +21,13 @@
</template>
<script setup lang="ts">
import { withDefaultOnError } from '@/utils/defaults';
import { evaluate } from 'mathjs';
import { computed, ref } from 'vue';
const expression = ref('');
const result = computed(() => {
try {
return evaluate(expression.value) ?? '';
} catch (_) {
return '';
}
});
const result = computed(() => withDefaultOnError(() => evaluate(expression.value) ?? '', ''));
</script>
<style lang="less" scoped></style>

View File

@@ -32,9 +32,9 @@
<script setup lang="ts">
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
import { useQRCode } from './useQRCode';
import { ref } from 'vue';
import type { QRCodeErrorCorrectionLevel } from 'qrcode';
import { useQRCode } from './useQRCode';
const foreground = ref('#000000ff');
const background = ref('#ffffffff');

View File

@@ -60,29 +60,18 @@
<script setup lang="ts">
import { useCopy } from '@/composable/copy';
import { useValidation } from '@/composable/validation';
import { isNotThrowing } from '@/utils/boolean';
import { withDefaultOnError } from '@/utils/defaults';
import { computed, ref } from 'vue';
const encodeInput = ref('Hello world :)');
const encodeOutput = computed(() => {
try {
return encodeURIComponent(encodeInput.value);
} catch (_) {
return '';
}
});
const encodeOutput = computed(() => withDefaultOnError(() => encodeURIComponent(encodeInput.value), ''));
const encodedValidation = useValidation({
source: encodeInput,
rules: [
{
validator: (value) => {
try {
encodeURIComponent(value);
return true;
} catch (_) {
return false;
}
},
validator: (value) => isNotThrowing(() => encodeURIComponent(value)),
message: 'Impossible to parse this string',
},
],
@@ -91,27 +80,13 @@ const encodedValidation = useValidation({
const { copy: copyEncoded } = useCopy({ source: encodeOutput, text: 'Encoded string copied to the clipboard' });
const decodeInput = ref('Hello%20world%20%3A)');
const decodeOutput = computed(() => {
try {
return decodeURIComponent(decodeInput.value);
} catch (_) {
return '';
}
});
const decodeOutput = computed(() => withDefaultOnError(() => decodeURIComponent(decodeInput.value), ''));
const decodeValidation = useValidation({
source: encodeInput,
rules: [
{
validator: (value) => {
try {
decodeURIComponent(value);
return true;
} catch (_) {
return false;
}
},
validator: (value) => isNotThrowing(() => decodeURIComponent(value)),
message: 'Impossible to parse this string',
},
],

View File

@@ -27,31 +27,21 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { SubdirectoryArrowRightRound } from '@vicons/material';
import InputCopyable from '../../components/InputCopyable.vue';
import { useValidation } from '@/composable/validation';
import { isNotThrowing } from '@/utils/boolean';
import { withDefaultOnError } from '@/utils/defaults';
import { SubdirectoryArrowRightRound } from '@vicons/material';
import { computed, ref } from 'vue';
import InputCopyable from '../../components/InputCopyable.vue';
const urlToParse = ref('https://me:pwd@it-tools.tech:3000/url-parser?key1=value&key2=value2#the-hash');
const urlParsed = computed<URL | undefined>(() => {
try {
return new URL(urlToParse.value);
} catch (_) {
return undefined;
}
});
const urlParsed = computed(() => withDefaultOnError(() => new URL(urlToParse.value), undefined));
const validation = useValidation({
source: urlToParse,
rules: [
{
validator: (value) => {
try {
new URL(value);
return true;
} catch (_) {
return false;
}
},
validator: (value) => isNotThrowing(() => new URL(value)),
message: 'Invalid url',
},
],

15
src/utils/boolean.test.ts Normal file
View File

@@ -0,0 +1,15 @@
import { describe, expect, it } from 'vitest';
import { isNotThrowing } from './boolean';
describe('boolean utils', () => {
describe('isNotThrowing', () => {
it('should return if the call throws or false otherwise', () => {
expect(isNotThrowing(() => {})).to.eql(true);
expect(
isNotThrowing(() => {
throw new Error();
}),
).to.eql(false);
});
});
});

10
src/utils/boolean.ts Normal file
View File

@@ -0,0 +1,10 @@
export { isNotThrowing };
function isNotThrowing(cb: () => unknown): boolean {
try {
cb();
return true;
} catch (_) {
return false;
}
}