Merge branch 'main' into tools/mac-address-generator

This commit is contained in:
Istvan Olah 2023-10-16 11:20:41 +03:00 committed by GitHub
commit f4d5bb8cb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 774 additions and 332 deletions

View file

@ -6,7 +6,7 @@ on:
- main
jobs:
test:
timeout-minutes: 60
timeout-minutes: 10
runs-on: ubuntu-latest
strategy:
matrix:

View file

@ -0,0 +1,6 @@
---
to: src/ui/<%= h.changeCase.param(name) %>/<%= h.changeCase.param(name) %>.demo.vue
---
<template>
<<%= h.changeCase.param(name) %> />
</template>

View file

@ -0,0 +1,13 @@
---
to: src/ui/<%= h.changeCase.param(name) %>/<%= h.changeCase.param(name) %>.vue
---
<script lang="ts" setup>
const props = withDefaults(defineProps<{ prop?: string }>(), { prop: '' });
const { prop } = toRefs(props);
</script>
<template>
<div>
{{ prop }}
</div>
</template>

6
components.d.ts vendored
View file

@ -49,6 +49,8 @@ declare module '@vue/runtime-core' {
CrontabGenerator: typeof import('./src/tools/crontab-generator/crontab-generator.vue')['default']
CSelect: typeof import('./src/ui/c-select/c-select.vue')['default']
'CSelect.demo': typeof import('./src/ui/c-select/c-select.demo.vue')['default']
CTable: typeof import('./src/ui/c-table/c-table.vue')['default']
'CTable.demo': typeof import('./src/ui/c-table/c-table.demo.vue')['default']
CTextCopyable: typeof import('./src/ui/c-text-copyable/c-text-copyable.vue')['default']
'CTextCopyable.demo': typeof import('./src/ui/c-text-copyable/c-text-copyable.demo.vue')['default']
CTooltip: typeof import('./src/ui/c-tooltip/c-tooltip.vue')['default']
@ -90,6 +92,8 @@ declare module '@vue/runtime-core' {
IconMdiDownload: typeof import('~icons/mdi/download')['default']
IconMdiEye: typeof import('~icons/mdi/eye')['default']
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
IconMdiFavoriteFilled: typeof import('~icons/mdi/favorite-filled')['default']
IconMdiHeart: typeof import('~icons/mdi/heart')['default']
IconMdiPause: typeof import('~icons/mdi/pause')['default']
IconMdiPlay: typeof import('~icons/mdi/play')['default']
IconMdiRecord: typeof import('~icons/mdi/record')['default']
@ -179,6 +183,7 @@ declare module '@vue/runtime-core' {
TextareaCopyable: typeof import('./src/components/TextareaCopyable.vue')['default']
TextDiff: typeof import('./src/tools/text-diff/text-diff.vue')['default']
TextStatistics: typeof import('./src/tools/text-statistics/text-statistics.vue')['default']
TextToBinary: typeof import('./src/tools/text-to-binary/text-to-binary.vue')['default']
TextToNatoAlphabet: typeof import('./src/tools/text-to-nato-alphabet/text-to-nato-alphabet.vue')['default']
TokenDisplay: typeof import('./src/tools/otp-code-generator-and-validator/token-display.vue')['default']
'TokenGenerator.tool': typeof import('./src/tools/token-generator/token-generator.tool.vue')['default']
@ -187,6 +192,7 @@ declare module '@vue/runtime-core' {
'Tool.layout': typeof import('./src/layouts/tool.layout.vue')['default']
ToolCard: typeof import('./src/components/ToolCard.vue')['default']
UlidGenerator: typeof import('./src/tools/ulid-generator/ulid-generator.vue')['default']
Unnamed: typeof import('./src/ui/unnamed/unnamed.vue')['default']
UrlEncoder: typeof import('./src/tools/url-encoder/url-encoder.vue')['default']
UrlParser: typeof import('./src/tools/url-parser/url-parser.vue')['default']
UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default']

View file

@ -30,7 +30,8 @@
"coverage": "vitest run --coverage",
"typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --ignore-path .gitignore",
"script:create-new-tool": "node scripts/create-tool.mjs",
"script:create:tool": "node scripts/create-tool.mjs",
"script:create:ui": "hygen generator ui-component",
"release": "node ./scripts/release.mjs"
},
"dependencies": {
@ -117,6 +118,7 @@
"c8": "^8.0.0",
"consola": "^3.0.2",
"eslint": "^8.47.0",
"hygen": "^6.2.11",
"jsdom": "^22.0.0",
"less": "^4.1.3",
"prettier": "^3.0.0",

345
pnpm-lock.yaml generated
View file

@ -250,6 +250,9 @@ devDependencies:
eslint:
specifier: ^8.47.0
version: 8.47.0
hygen:
specifier: ^6.2.11
version: 6.2.11
jsdom:
specifier: ^22.0.0
version: 22.0.0
@ -2918,6 +2921,10 @@ packages:
'@types/node': 18.15.11
dev: true
/@types/node@17.0.45:
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
dev: true
/@types/node@18.15.11:
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
dev: true
@ -3272,7 +3279,7 @@ packages:
dependencies:
'@unhead/dom': 0.5.1
'@unhead/schema': 0.5.1
'@vueuse/shared': 10.4.1(vue@3.3.4)
'@vueuse/shared': 10.5.0(vue@3.3.4)
unhead: 0.5.1
vue: 3.3.4
transitivePeerDependencies:
@ -3854,10 +3861,10 @@ packages:
- vue
dev: false
/@vueuse/shared@10.4.1(vue@3.3.4):
resolution: {integrity: sha512-vz5hbAM4qA0lDKmcr2y3pPdU+2EVw/yzfRsBdu+6+USGa4PxqSQRYIUC9/NcT06y+ZgaTsyURw2I9qOFaaXHAg==}
/@vueuse/shared@10.5.0(vue@3.3.4):
resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==}
dependencies:
vue-demi: 0.14.5(vue@3.3.4)
vue-demi: 0.14.6(vue@3.3.4)
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@ -3934,6 +3941,11 @@ packages:
uri-js: 4.4.1
dev: true
/ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
dev: true
/ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@ -4069,6 +4081,10 @@ packages:
/balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true
/bcryptjs@2.4.3:
resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
dev: false
@ -4077,6 +4093,14 @@ packages:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
/bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
dependencies:
buffer: 5.7.1
inherits: 2.0.4
readable-stream: 3.6.2
dev: true
/boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
dev: true
@ -4122,6 +4146,13 @@ packages:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
dev: true
/buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
dev: true
/builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
engines: {node: '>=6'}
@ -4168,6 +4199,13 @@ packages:
engines: {node: '>=6'}
dev: true
/camel-case@3.0.0:
resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==}
dependencies:
no-case: 2.3.2
upper-case: 1.1.3
dev: true
/camel-case@4.1.2:
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
dependencies:
@ -4239,6 +4277,29 @@ packages:
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
dev: true
/change-case@3.1.0:
resolution: {integrity: sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==}
dependencies:
camel-case: 3.0.0
constant-case: 2.0.0
dot-case: 2.1.1
header-case: 1.0.1
is-lower-case: 1.1.3
is-upper-case: 1.1.2
lower-case: 1.1.4
lower-case-first: 1.0.2
no-case: 2.3.2
param-case: 2.1.1
pascal-case: 2.0.1
path-case: 2.1.1
sentence-case: 2.1.1
snake-case: 2.1.0
swap-case: 1.1.2
title-case: 2.1.1
upper-case: 1.1.3
upper-case-first: 1.1.2
dev: true
/change-case@4.1.2:
resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==}
dependencies:
@ -4302,6 +4363,18 @@ packages:
escape-string-regexp: 1.0.5
dev: true
/cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
dependencies:
restore-cursor: 3.1.0
dev: true
/cli-spinners@2.9.1:
resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==}
engines: {node: '>=6'}
dev: true
/cliui@6.0.0:
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
dependencies:
@ -4318,6 +4391,11 @@ packages:
wrap-ansi: 7.0.0
dev: true
/clone@1.0.4:
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
engines: {node: '>=0.8'}
dev: true
/color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
@ -4402,6 +4480,13 @@ packages:
engines: {node: ^14.18.0 || >=16.10.0}
dev: true
/constant-case@2.0.0:
resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==}
dependencies:
snake-case: 2.1.0
upper-case: 1.1.3
dev: true
/constant-case@3.0.4:
resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==}
dependencies:
@ -4625,6 +4710,12 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
/defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
dependencies:
clone: 1.0.4
dev: true
/define-lazy-prop@2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
engines: {node: '>=8'}
@ -4642,6 +4733,12 @@ packages:
resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==}
dev: true
/degit@2.8.4:
resolution: {integrity: sha512-vqYuzmSA5I50J882jd+AbAhQtgK6bdKUJIex1JNfEUPENCgYsxugzKVZlFyMwV4i06MmnV47/Iqi5Io86zf3Ng==}
engines: {node: '>=8.0.0'}
hasBin: true
dev: true
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@ -4724,6 +4821,12 @@ packages:
domhandler: 5.0.3
dev: true
/dot-case@2.1.1:
resolution: {integrity: sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==}
dependencies:
no-case: 2.3.2
dev: true
/dot-case@3.0.4:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
dependencies:
@ -4774,6 +4877,14 @@ packages:
resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
dev: false
/enquirer@2.4.1:
resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
engines: {node: '>=8.6'}
dependencies:
ansi-colors: 4.1.3
strip-ansi: 6.0.1
dev: true
/entities@3.0.1:
resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==}
engines: {node: '>=0.12'}
@ -5483,6 +5594,21 @@ packages:
resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==}
dev: true
/front-matter@4.0.2:
resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==}
dependencies:
js-yaml: 3.14.1
dev: true
/fs-extra@10.1.0:
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
engines: {node: '>=12'}
dependencies:
graceful-fs: 4.2.11
jsonfile: 6.1.0
universalify: 2.0.0
dev: true
/fs-extra@11.1.1:
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
engines: {node: '>=14.14'}
@ -5753,6 +5879,13 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
dev: false
/header-case@1.0.1:
resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==}
dependencies:
no-case: 2.3.2
upper-case: 1.1.3
dev: true
/header-case@2.0.4:
resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==}
dependencies:
@ -5840,6 +5973,28 @@ packages:
ms: 2.1.3
dev: false
/hygen@6.2.11:
resolution: {integrity: sha512-t6/zLI2XozP5gvV74nnl8LZSbwpVNFUkUs/O9DwuOdiiBbws5k4AQNVwKZ9FGzcKjdJ5EBBYkVzlcUHkLyY0FQ==}
hasBin: true
dependencies:
'@types/node': 17.0.45
chalk: 4.1.2
change-case: 3.1.0
debug: 4.3.4
degit: 2.8.4
ejs: 3.1.9
enquirer: 2.4.1
execa: 5.1.1
front-matter: 4.0.2
fs-extra: 10.1.0
ignore-walk: 4.0.1
inflection: 1.13.4
ora: 5.4.1
yargs-parser: 21.1.1
transitivePeerDependencies:
- supports-color
dev: true
/iarna-toml-esm@3.0.5:
resolution: {integrity: sha512-CgeDbPohnFG827UoRaCqKxJ8idiIDZDWlcHf5hUReQnZ8jHnNnhN4QJFiY12fKvr0LvuDuKAimqQfrmQnacbtw==}
dependencies:
@ -5861,6 +6016,17 @@ packages:
resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
dev: true
/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: true
/ignore-walk@4.0.1:
resolution: {integrity: sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==}
engines: {node: '>=10'}
dependencies:
minimatch: 3.1.2
dev: true
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
@ -5891,6 +6057,11 @@ packages:
engines: {node: '>=8'}
dev: true
/inflection@1.13.4:
resolution: {integrity: sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==}
engines: {'0': node >= 0.4.0}
dev: true
/inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
dependencies:
@ -6036,6 +6207,17 @@ packages:
resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==}
dev: true
/is-interactive@1.0.0:
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
engines: {node: '>=8'}
dev: true
/is-lower-case@1.1.3:
resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==}
dependencies:
lower-case: 1.1.4
dev: true
/is-module@1.0.0:
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
dev: true
@ -6127,6 +6309,17 @@ packages:
which-typed-array: 1.1.11
dev: true
/is-unicode-supported@0.1.0:
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
engines: {node: '>=10'}
dev: true
/is-upper-case@1.1.2:
resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==}
dependencies:
upper-case: 1.1.3
dev: true
/is-weakref@1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
dependencies:
@ -6458,12 +6651,30 @@ packages:
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
/log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
engines: {node: '>=10'}
dependencies:
chalk: 4.1.2
is-unicode-supported: 0.1.0
dev: true
/loupe@2.3.6:
resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==}
dependencies:
get-func-name: 2.0.0
dev: true
/lower-case-first@1.0.2:
resolution: {integrity: sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==}
dependencies:
lower-case: 1.1.4
dev: true
/lower-case@1.1.4:
resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==}
dev: true
/lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
dependencies:
@ -6747,6 +6958,12 @@ packages:
engines: {node: '>= 0.4.0'}
dev: false
/no-case@2.3.2:
resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==}
dependencies:
lower-case: 1.1.4
dev: true
/no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
dependencies:
@ -6919,6 +7136,21 @@ packages:
type-check: 0.4.0
dev: true
/ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
dependencies:
bl: 4.1.0
chalk: 4.1.2
cli-cursor: 3.1.0
cli-spinners: 2.9.1
is-interactive: 1.0.0
is-unicode-supported: 0.1.0
log-symbols: 4.1.0
strip-ansi: 6.0.1
wcwidth: 1.0.1
dev: true
/orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
dev: false
@ -6976,6 +7208,12 @@ packages:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
/param-case@2.1.1:
resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==}
dependencies:
no-case: 2.3.2
dev: true
/param-case@3.0.4:
resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
dependencies:
@ -7022,6 +7260,13 @@ packages:
entities: 4.5.0
dev: true
/pascal-case@2.0.1:
resolution: {integrity: sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==}
dependencies:
camel-case: 3.0.0
upper-case-first: 1.1.2
dev: true
/pascal-case@3.1.2:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
dependencies:
@ -7029,6 +7274,12 @@ packages:
tslib: 2.5.0
dev: false
/path-case@2.1.1:
resolution: {integrity: sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q==}
dependencies:
no-case: 2.3.2
dev: true
/path-case@3.0.4:
resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==}
dependencies:
@ -7435,6 +7686,15 @@ packages:
type-fest: 0.6.0
dev: true
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
dependencies:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
dev: true
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
@ -7548,6 +7808,14 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
/restore-cursor@3.1.0:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'}
dependencies:
onetime: 5.1.2
signal-exit: 3.0.7
dev: true
/ret@0.1.15:
resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==}
engines: {node: '>=0.12'}
@ -7684,6 +7952,13 @@ packages:
lru-cache: 6.0.0
dev: true
/sentence-case@2.1.1:
resolution: {integrity: sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==}
dependencies:
no-case: 2.3.2
upper-case-first: 1.1.2
dev: true
/sentence-case@3.0.4:
resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==}
dependencies:
@ -7761,6 +8036,12 @@ packages:
engines: {node: '>=12'}
dev: true
/snake-case@2.1.0:
resolution: {integrity: sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==}
dependencies:
no-case: 2.3.2
dev: true
/snake-case@3.0.4:
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
dependencies:
@ -7904,6 +8185,12 @@ packages:
es-abstract: 1.22.1
dev: true
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
safe-buffer: 5.2.1
dev: true
/stringify-object@3.3.0:
resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
engines: {node: '>=4'}
@ -7985,6 +8272,13 @@ packages:
picocolors: 1.0.0
dev: true
/swap-case@1.1.2:
resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==}
dependencies:
lower-case: 1.1.4
upper-case: 1.1.3
dev: true
/symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
dev: true
@ -8075,6 +8369,13 @@ packages:
'@popperjs/core': 2.11.8
dev: false
/title-case@2.1.1:
resolution: {integrity: sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==}
dependencies:
no-case: 2.3.2
upper-case: 1.1.3
dev: true
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
@ -8527,12 +8828,22 @@ packages:
escalade: 3.1.1
picocolors: 1.0.0
/upper-case-first@1.1.2:
resolution: {integrity: sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==}
dependencies:
upper-case: 1.1.3
dev: true
/upper-case-first@2.0.2:
resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==}
dependencies:
tslib: 2.5.0
dev: false
/upper-case@1.1.3:
resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==}
dev: true
/upper-case@2.0.2:
resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==}
dependencies:
@ -8790,6 +9101,21 @@ packages:
vue: 3.3.4
dev: false
/vue-demi@0.14.6(vue@3.3.4):
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
dependencies:
vue: 3.3.4
dev: false
/vue-eslint-parser@9.3.1(eslint@8.47.0):
resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
engines: {node: ^14.17.0 || >=16.0.0}
@ -8883,6 +9209,12 @@ packages:
xml-name-validator: 4.0.0
dev: true
/wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
dependencies:
defaults: 1.0.4
dev: true
/web-streams-polyfill@3.2.1:
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
engines: {node: '>= 8'}
@ -9252,6 +9584,11 @@ packages:
engines: {node: '>=10'}
dev: true
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
dev: true
/yargs@15.4.1:
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
engines: {node: '>=8'}

View file

@ -1,6 +1,4 @@
<script setup lang="ts">
import { FavoriteFilled } from '@vicons/material';
import { useToolStore } from '@/tools/tools.store';
import type { Tool } from '@/tools/tools.types';
@ -26,8 +24,7 @@ function toggleFavorite(event: MouseEvent) {
</script>
<template>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip :tooltip="isFavorite ? 'Remove from favorites' : 'Add to favorites' ">
<c-button
variant="text"
circle
@ -35,9 +32,7 @@ function toggleFavorite(event: MouseEvent) {
:style="{ opacity: isFavorite ? 1 : 0.2 }"
@click="toggleFavorite"
>
<n-icon :component="FavoriteFilled" />
<icon-mdi-heart />
</c-button>
</template>
{{ isFavorite ? 'Remove from favorites' : 'Add to favorites' }}
</n-tooltip>
</c-tooltip>
</template>

View file

@ -13,14 +13,11 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : 'Copy to cli
<template>
<c-input-text v-model:value="value">
<template #suffix>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip :tooltip="tooltipText">
<c-button circle variant="text" size="small" @click="copy()">
<icon-mdi-content-copy />
</c-button>
</template>
{{ tooltipText }}
</n-tooltip>
</c-tooltip>
</template>
</c-input-text>
</template>

View file

@ -7,8 +7,7 @@ const { isDarkTheme } = toRefs(styleStore);
</script>
<template>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip tooltip="Github repository" position="bottom">
<c-button
circle
variant="text"
@ -19,12 +18,9 @@ const { isDarkTheme } = toRefs(styleStore);
>
<n-icon size="25" :component="BrandGithub" />
</c-button>
</template>
Github repository
</n-tooltip>
</c-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip tooltip="Twitter account" position="bottom">
<c-button
circle
variant="text"
@ -35,28 +31,19 @@ const { isDarkTheme } = toRefs(styleStore);
>
<n-icon size="25" :component="BrandTwitter" />
</c-button>
</template>
IT Tools' Twitter account
</n-tooltip>
</c-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip tooltip="About IT-Tools" position="bottom">
<c-button circle variant="text" to="/about" aria-label="About">
<n-icon size="25" :component="InfoCircle" />
</c-button>
</template>
About
</n-tooltip>
<n-tooltip trigger="hover">
<template #trigger>
</c-tooltip>
<c-tooltip :tooltip="isDarkTheme ? 'Light mode' : 'Dark mode'" position="bottom">
<c-button circle variant="text" aria-label="Toggle dark/light mode" @click="() => styleStore.toggleDark()">
<n-icon v-if="isDarkTheme" size="25" :component="Sun" />
<n-icon v-else size="25" :component="Moon" />
</c-button>
</template>
<span v-if="isDarkTheme">Light mode</span>
<span v-else>Dark mode</span>
</n-tooltip>
</c-tooltip>
</template>
<style lang="less" scoped>

View file

@ -11,17 +11,7 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : initialText)
</script>
<template>
<n-tooltip trigger="hover">
<template #trigger>
<span class="value" @click="copy()">{{ value }}</span>
<c-tooltip :tooltip="tooltipText">
<span cursor-pointer font-mono @click="copy()">{{ value }}</span>
</c-tooltip>
</template>
{{ tooltipText }}
</n-tooltip>
</template>
<style scoped lang="less">
.value {
cursor: pointer;
font-family: monospace;
}
</style>

View file

@ -40,7 +40,7 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
<template>
<div style="overflow-x: hidden; width: 100%">
<c-card class="result-card">
<c-card relative>
<n-scrollbar
x-scrollable
trigger="none"
@ -50,16 +50,13 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
<n-code :code="value" :language="language" :trim="false" data-test-id="area-content" />
</n-config-provider>
</n-scrollbar>
<n-tooltip v-if="value" trigger="hover">
<template #trigger>
<div class="copy-button" :class="[copyPlacement]">
<div absolute right-10px top-10px>
<c-tooltip v-if="value" :tooltip="tooltipText" position="left">
<c-button circle important:h-10 important:w-10 @click="copy()">
<n-icon size="22" :component="Copy" />
</c-button>
</c-tooltip>
</div>
</template>
<span>{{ tooltipText }}</span>
</n-tooltip>
</c-card>
<div v-if="copyPlacement === 'outside'" mt-4 flex justify-center>
<c-button @click="copy()">
@ -74,25 +71,4 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
padding-bottom: 10px;
margin-bottom: -10px;
}
.result-card {
position: relative;
.copy-button {
position: absolute;
opacity: 1;
&.top-right {
top: 10px;
right: 10px;
}
&.bottom-right {
bottom: 10px;
right: 10px;
}
&.outside,
&.none {
display: none;
}
}
}
</style>

View file

@ -94,18 +94,17 @@ const tools = computed<ToolCategory[]>(() => [
<NIcon size="25" :component="Menu2" />
</c-button>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip tooltip="Home" position="bottom">
<c-button to="/" circle variant="text" aria-label="Home">
<NIcon size="25" :component="Home2" />
</c-button>
</template>
Home
</n-tooltip>
</c-tooltip>
<c-tooltip tooltip="UI Lib" position="bottom">
<c-button v-if="config.app.env === 'development'" to="/c-lib" circle variant="text" aria-label="UI Lib">
<icon-mdi:brush-variant text-20px />
</c-button>
</c-tooltip>
<command-palette />
@ -113,8 +112,7 @@ const tools = computed<ToolCategory[]>(() => [
<NavbarButtons v-if="!styleStore.isSmallScreen" />
</div>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip position="bottom" tooltip="Support IT Tools development">
<c-button
round
href="https://www.buymeacoffee.com/cthmsst"
@ -127,9 +125,7 @@ const tools = computed<ToolCategory[]>(() => [
Buy me a coffee
<NIcon v-if="!styleStore.isSmallScreen" :component="Heart" ml-2 />
</c-button>
</template>
Support IT Tools development !
</n-tooltip>
</c-tooltip>
</div>
<slot />
</template>

View file

@ -7,17 +7,17 @@ const { tracker } = useTracker();
</script>
<template>
<div class="about-page">
<n-h1>About</n-h1>
<n-p>
<div mx-auto mt-50px max-w-600px>
<h1>About IT-Tools</h1>
<p text-justify>
This wonderful website, made with by
<c-link href="https://github.com/CorentinTh" target="_blank" rel="noopener">
Corentin Thomasset
</c-link>,
aggregates useful tools for developer and people working in IT. If you find it useful, please feel free to share
it to people you think may find it useful too and don't forget to bookmark it in your shortcut bar!
</n-p>
<n-p>
</p>
<p text-justify>
IT Tools is open-source (under the MIT license) and free, and will always be, but it costs me money to host and
renew the domain name. If you want to support my work, and encourage me to add more tools, please consider
supporting by
@ -29,20 +29,20 @@ const { tracker } = useTracker();
>
sponsoring me
</c-link>.
</n-p>
</p>
<n-h2>Technologies</n-h2>
<n-p>
<h2>Technologies</h2>
<p text-justify>
IT Tools is made in Vue.js (Vue 3) with the the Naive UI component library and is hosted and continuously deployed
by Vercel. Third-party open-source libraries are used in some tools, you may find the complete list in the
<c-link href="https://github.com/CorentinTh/it-tools/blob/main/package.json" rel="noopener" target="_blank">
package.json
</c-link>
file of the repository.
</n-p>
</p>
<n-h2>Found a bug? A tool is missing?</n-h2>
<n-p>
<h2>Found a bug? A tool is missing?</h2>
<p text-justify>
If you need a tool that is currently not present here, and you think can be useful, you are welcome to submit a
feature request in the
<c-link
@ -53,8 +53,8 @@ const { tracker } = useTracker();
issues section
</c-link>
in the GitHub repository.
</n-p>
<n-p>
</p>
<p text-justify>
And if you found a bug, or something doesn't work as expected, please file a bug report in the
<c-link
href="https://github.com/CorentinTh/it-tools/issues/new/choose"
@ -64,22 +64,6 @@ const { tracker } = useTracker();
issues section
</c-link>
in the GitHub repository.
</n-p>
</p>
</div>
</template>
<style scoped lang="less">
.about-page {
max-width: 600px;
margin: 50px auto;
box-sizing: border-box;
.n-h2 {
margin-bottom: 0px;
}
.n-p {
text-align: justify;
}
}
</style>

View file

@ -51,11 +51,11 @@ const results = computed(() => {
const { copy } = useCopy({ createToast: false });
const header = {
position: 'Position',
title: 'Suite',
size: 'Samples',
mean: 'Mean',
variance: 'Variance',
position: 'Position',
};
function copyAsMarkdown() {
@ -131,26 +131,8 @@ function copyAsBulletList() {
</c-button>
</div>
<n-table>
<thead>
<tr>
<th>{{ header.position }}</th>
<th>{{ header.title }}</th>
<th>{{ header.size }}</th>
<th>{{ header.mean }}</th>
<th>{{ header.variance }}</th>
</tr>
</thead>
<tbody>
<tr v-for="{ title, size, mean, variance, position } of results" :key="title">
<td>{{ position }}</td>
<td>{{ title }}</td>
<td>{{ size }}</td>
<td>{{ mean }}</td>
<td>{{ variance }}</td>
</tr>
</tbody>
</n-table>
<c-table :data="results" :headers="header" />
<div mt-5 flex justify-center gap-3>
<c-button @click="copyAsMarkdown()">
Copy as markdown table

View file

@ -39,14 +39,11 @@ function onInputEnter(index: number) {
autofocus
@keydown.enter="onInputEnter(index)"
/>
<n-tooltip>
<template #trigger>
<c-tooltip tooltip="Delete this value">
<c-button circle variant="text" @click="values.splice(index, 1)">
<n-icon :component="Trash" depth="3" size="18" />
</c-button>
</template>
Delete value
</n-tooltip>
</c-tooltip>
</div>
<c-button @click="addValue">

View file

@ -167,34 +167,8 @@ const cronValidationRules = [
</div>
</c-card>
</div>
<n-table v-else size="small">
<thead>
<tr>
<th class="text-left" scope="col">
Symbol
</th>
<th class="text-left" scope="col">
Meaning
</th>
<th class="text-left" scope="col">
Example
</th>
<th class="text-left" scope="col">
Equivalent
</th>
</tr>
</thead>
<tbody>
<tr v-for="{ symbol, meaning, example, equivalent } in helpers" :key="symbol">
<td>{{ symbol }}</td>
<td>{{ meaning }}</td>
<td>
<code>{{ example }}</code>
</td>
<td>{{ equivalent }}</td>
</tr>
</tbody>
</n-table>
<c-table v-else :data="helpers" />
</c-card>
</template>

View file

@ -6,13 +6,9 @@ const { icon, title, action, isActive } = toRefs(props);
</script>
<template>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip :tooltip="title">
<c-button circle variant="text" :type="isActive?.() ? 'primary' : 'default'" @click="action">
<n-icon :component="icon" />
</c-button>
</template>
{{ title }}
</n-tooltip>
</c-tooltip>
</template>

View file

@ -2,6 +2,7 @@ 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 macAddressGenerator } from './mac-address-generator';
import { tool as textToBinary } from './text-to-binary';
import { tool as ulidGenerator } from './ulid-generator';
import { tool as ibanValidatorAndParser } from './iban-validator-and-parser';
import { tool as stringObfuscator } from './string-obfuscator';
@ -89,6 +90,7 @@ export const toolsByCategory: ToolCategory[] = [
colorConverter,
caseConverter,
textToNatoAlphabet,
textToBinary,
yamlToJson,
yamlToToml,
jsonToYaml,

View file

@ -61,19 +61,16 @@ const secretValidationRules = [
:validation-rules="secretValidationRules"
>
<template #suffix>
<n-tooltip trigger="hover">
<template #trigger>
<c-tooltip tooltip="Generate a new random secret">
<c-button circle variant="text" size="small" @click="refreshSecret">
<icon-mdi-refresh />
</c-button>
</template>
Generate secret token
</n-tooltip>
</c-tooltip>
</template>
</c-input-text>
<div>
<TokenDisplay :tokens="tokens" style="margin-top: 2px" />
<TokenDisplay :tokens="tokens" />
<n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" />
<div style="text-align: center">

View file

@ -11,7 +11,7 @@ const { tokens } = toRefs(props);
<template>
<div>
<div class="labels" w-full flex items-center>
<div mb-5px w-full flex items-center>
<div flex-1 text-left>
Previous
</div>
@ -22,60 +22,24 @@ const { tokens } = toRefs(props);
Next
</div>
</div>
<n-input-group>
<n-tooltip trigger="hover" placement="bottom">
<template #trigger>
<c-button important:h-12 data-test-id="previous-otp" @click.prevent="copyPrevious(tokens.previous)">
<div flex items-center>
<c-tooltip :tooltip="previousCopied ? 'Copied !' : 'Copy previous OTP'" position="bottom" flex-1>
<c-button data-test-id="previous-otp" w-full important:h-12 important:rounded-r-none important:font-mono @click.prevent="copyPrevious(tokens.previous)">
{{ tokens.previous }}
</c-button>
</template>
<div>{{ previousCopied ? 'Copied !' : 'Copy previous OTP' }}</div>
</n-tooltip>
<n-tooltip trigger="hover" placement="bottom">
<template #trigger>
</c-tooltip>
<c-tooltip :tooltip="currentCopied ? 'Copied !' : 'Copy current OTP'" position="bottom" flex-1 flex-basis-5xl>
<c-button
data-test-id="current-otp"
class="current-otp"
important:h-12
@click.prevent="copyCurrent(tokens.current)"
data-test-id="current-otp" w-full important:border-x="1px solid gray op-40" important:h-12 important:rounded-0 important:text-22px @click.prevent="copyCurrent(tokens.current)"
>
{{ tokens.current }}
</c-button>
</template>
<div>{{ currentCopied ? 'Copied !' : 'Copy current OTP' }}</div>
</n-tooltip>
<n-tooltip trigger="hover" placement="bottom">
<template #trigger>
<c-button important:h-12 data-test-id="next-otp" @click.prevent="copyNext(tokens.next)">
{{
tokens.next
}}
</c-tooltip>
<c-tooltip :tooltip="nextCopied ? 'Copied !' : 'Copy next OTP'" position="bottom" flex-1>
<c-button data-test-id="next-otp" w-full important:h-12 important:rounded-l-none @click.prevent="copyNext(tokens.next)">
{{ tokens.next }}
</c-button>
</template>
<div>{{ nextCopied ? 'Copied !' : 'Copy next OTP' }}</div>
</n-tooltip>
</n-input-group>
</c-tooltip>
</div>
</div>
</template>
<style scoped lang="less">
.current-otp {
font-size: 22px;
flex: 1 0 35% !important;
}
.n-button {
height: 45px;
}
.labels {
div {
padding: 0 2px 6px 2px;
line-height: 1.25;
}
}
.n-input-group > * {
flex: 1 0 0;
}
</style>

View file

@ -0,0 +1,12 @@
import { Binary } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'Text to ASCII binary',
path: '/text-to-binary',
description: 'Convert text to its ASCII binary representation and vice versa.',
keywords: ['text', 'to', 'binary', 'converter', 'encode', 'decode', 'ascii'],
component: () => import('./text-to-binary.vue'),
icon: Binary,
createdAt: new Date('2023-10-15'),
});

View file

@ -0,0 +1,25 @@
import { expect, test } from '@playwright/test';
test.describe('Tool - Text to ASCII binary', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/text-to-binary');
});
test('Has correct title', async ({ page }) => {
await expect(page).toHaveTitle('Text to ASCII binary - IT Tools');
});
test('Text to binary conversion', async ({ page }) => {
await page.getByTestId('text-to-binary-input').fill('it-tools');
const binary = await page.getByTestId('text-to-binary-output').inputValue();
expect(binary).toEqual('01101001 01110100 00101101 01110100 01101111 01101111 01101100 01110011');
});
test('Binary to text conversion', async ({ page }) => {
await page.getByTestId('binary-to-text-input').fill('01101001 01110100 00101101 01110100 01101111 01101111 01101100 01110011');
const text = await page.getByTestId('binary-to-text-output').inputValue();
expect(text).toEqual('it-tools');
});
});

View file

@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest';
import { convertAsciiBinaryToText, convertTextToAsciiBinary } from './text-to-binary.models';
describe('text-to-binary', () => {
describe('convertTextToAsciiBinary', () => {
it('a text string is converted to its ascii binary representation', () => {
expect(convertTextToAsciiBinary('A')).toBe('01000001');
expect(convertTextToAsciiBinary('hello')).toBe('01101000 01100101 01101100 01101100 01101111');
expect(convertTextToAsciiBinary('')).toBe('');
});
it('the separator between octets can be changed', () => {
expect(convertTextToAsciiBinary('hello', { separator: '' })).toBe('0110100001100101011011000110110001101111');
});
});
describe('convertAsciiBinaryToText', () => {
it('an ascii binary string is converted to its text representation', () => {
expect(convertAsciiBinaryToText('01101000 01100101 01101100 01101100 01101111')).toBe('hello');
expect(convertAsciiBinaryToText('01000001')).toBe('A');
expect(convertTextToAsciiBinary('')).toBe('');
});
it('the given binary string is cleaned before conversion', () => {
expect(convertAsciiBinaryToText(' 01000 001garbage')).toBe('A');
});
it('throws an error if the given binary string as no complete octet', () => {
expect(() => convertAsciiBinaryToText('010000011')).toThrow('Invalid binary string');
expect(() => convertAsciiBinaryToText('1')).toThrow('Invalid binary string');
});
});
});

View file

@ -0,0 +1,22 @@
export { convertTextToAsciiBinary, convertAsciiBinaryToText };
function convertTextToAsciiBinary(text: string, { separator = ' ' }: { separator?: string } = {}): string {
return text
.split('')
.map(char => char.charCodeAt(0).toString(2).padStart(8, '0'))
.join(separator);
}
function convertAsciiBinaryToText(binary: string): string {
const cleanBinary = binary.replace(/[^01]/g, '');
if (cleanBinary.length % 8) {
throw new Error('Invalid binary string');
}
return cleanBinary
.split(/(\d{8})/)
.filter(Boolean)
.map(binary => String.fromCharCode(Number.parseInt(binary, 2)))
.join('');
}

View file

@ -0,0 +1,42 @@
<script setup lang="ts">
import { convertAsciiBinaryToText, convertTextToAsciiBinary } from './text-to-binary.models';
import { withDefaultOnError } from '@/utils/defaults';
import { useCopy } from '@/composable/copy';
import { isNotThrowing } from '@/utils/boolean';
const inputText = ref('');
const binaryFromText = computed(() => convertTextToAsciiBinary(inputText.value));
const { copy: copyBinary } = useCopy({ source: binaryFromText });
const inputBinary = ref('');
const textFromBinary = computed(() => withDefaultOnError(() => convertAsciiBinaryToText(inputBinary.value), ''));
const inputBinaryValidationRules = [
{
validator: (value: string) => isNotThrowing(() => convertAsciiBinaryToText(value)),
message: 'Binary should be a valid ASCII binary string with multiples of 8 bits',
},
];
const { copy: copyText } = useCopy({ source: textFromBinary });
</script>
<template>
<c-card title="Text to ASCII binary">
<c-input-text v-model:value="inputText" multiline placeholder="e.g. 'Hello world'" label="Enter text to convert to binary" autosize autofocus raw-text test-id="text-to-binary-input" />
<c-input-text v-model:value="binaryFromText" label="Binary from your text" multiline raw-text readonly mt-2 placeholder="The binary representation of your text will be here" test-id="text-to-binary-output" />
<div mt-2 flex justify-center>
<c-button :disabled="!binaryFromText" @click="copyBinary()">
Copy binary to clipboard
</c-button>
</div>
</c-card>
<c-card title="ASCII binary to text">
<c-input-text v-model:value="inputBinary" multiline placeholder="e.g. '01001000 01100101 01101100 01101100 01101111'" label="Enter binary to convert to text" autosize raw-text :validation-rules="inputBinaryValidationRules" test-id="binary-to-text-input" />
<c-input-text v-model:value="textFromBinary" label="Text from your binary" multiline raw-text readonly mt-2 placeholder="The text representation of your binary will be here" test-id="binary-to-text-output" />
<div mt-2 flex justify-center>
<c-button :disabled="!textFromBinary" @click="copyText()">
Copy text to clipboard
</c-button>
</div>
</c-card>
</template>

View file

@ -14,25 +14,18 @@ const { userAgentInfo, sections } = toRefs(props);
<n-grid :x-gap="12" :y-gap="8" cols="1 s:2" responsive="screen">
<n-gi v-for="{ heading, icon, content } in sections" :key="heading">
<c-card h-full>
<n-page-header>
<template #title>
{{ heading }}
</template>
<template v-if="icon" #avatar>
<div flex items-center gap-3>
<n-icon size="30" :component="icon" :depth="3" />
</template>
</n-page-header>
<span text-lg>{{ heading }}</span>
</div>
<div mt-5 flex gap-2>
<span v-for="{ label, getValue } in content" :key="label">
<n-tooltip v-if="getValue(userAgentInfo)" trigger="hover">
<template #trigger>
<c-tooltip v-if="getValue(userAgentInfo)" :tooltip="label">
<n-tag type="success" size="large" round :bordered="false">
{{ getValue(userAgentInfo) }}
</n-tag>
</template>
{{ label }}
</n-tooltip>
</c-tooltip>
</span>
</div>
<div flex flex-col>

View file

@ -0,0 +1,20 @@
<script lang="ts" setup>
const data = ref([
{ name: 'John', age: 20 },
{ name: 'Jane', age: 24 },
{ name: 'Joe', age: 30 },
]);
</script>
<template>
<c-table :data="data" mb-2 />
<c-table :data="data" hide-headers mb-2 />
<c-table :data="data" :headers="['age', 'name']" mb-2 />
<c-table :data="data" :headers="['age', { key: 'name', label: 'Full name' }]" mb-2 />
<c-table :data="data" :headers="{ name: 'full name' }" mb-2 />
<c-table :data="data" :headers="['age', 'name']">
<template #age="{ value }">
{{ value }}yo
</template>
</c-table>
</template>

View file

@ -0,0 +1,4 @@
export type HeaderConfiguration = (string | {
key: string
label?: string
})[] | Record<string, string>;

View file

@ -0,0 +1,65 @@
<script lang="ts" setup>
import _ from 'lodash';
import type { HeaderConfiguration } from './c-table.types';
const props = withDefaults(defineProps<{ data?: Record<string, unknown>[]; headers?: HeaderConfiguration ; hideHeaders?: boolean; description?: string }>(), { data: () => [], headers: undefined, hideHeaders: false, description: 'Data table' });
const { data, headers: rawHeaders, hideHeaders } = toRefs(props);
const headers = computed(() => {
if (rawHeaders.value) {
if (Array.isArray(rawHeaders.value)) {
return rawHeaders.value.map((value) => {
if (typeof value === 'string') {
return { key: value, label: value };
}
const { key, label } = value;
return {
key,
label: label ?? key,
};
});
}
return _.map(rawHeaders.value, (value, key) => ({
key, label: value,
}));
}
return _.chain(data.value)
.map(row => Object.keys(row))
.flatten()
.uniq()
.map(key => ({ key, label: key }))
.value();
});
</script>
<template>
<div class="relative overflow-x-auto rounded">
<table class="w-full border-collapse text-left text-sm text-gray-500 dark:text-gray-400" role="table" :aria-label="description">
<thead v-if="!hideHeaders" class="bg-#ffffff uppercase text-gray-700 dark:bg-#333333 dark:text-gray-400">
<tr>
<th v-for="header in headers" :key="header.key" scope="col" class="px-6 py-3 text-xs">
{{ header.label }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(row, i) in data" :key="i" border-b="1px solid dark:#282828 #efeff5" class="bg-white dark:bg-#232323"
:class="{
'important:border-b-none': i === data.length - 1,
}"
>
<td v-for="header in headers" :key="header.key" class="px-6 py-4">
<slot :name="header" :row="row" :headers="headers" :value="row[header.key]">
{{ row[header.key] }}
</slot>
</td>
</tr>
</tbody>
</table>
</div>
</template>

View file

@ -1,3 +1,7 @@
<script lang="ts" setup>
const positions = ['top', 'bottom', 'left', 'right'] as const;
</script>
<template>
<div>
<c-tooltip>
@ -14,4 +18,18 @@
Hover me
</c-tooltip>
</div>
<div mt-5>
<h2>Tooltip positions</h2>
<div class="flex flex-wrap gap-4">
<div v-for="position in positions" :key="position">
<c-tooltip :position="position" :tooltip="`Tooltip ${position}`">
<c-button>
{{ position }}
</c-button>
</c-tooltip>
</div>
</div>
</div>
</template>

View file

@ -1,23 +1,30 @@
<script setup lang="ts">
const props = withDefaults(defineProps<{ tooltip?: string }>(), { tooltip: '' });
const { tooltip } = toRefs(props);
const props = withDefaults(defineProps<{ tooltip?: string; position?: 'top' | 'bottom' | 'left' | 'right' }>(), {
tooltip: undefined,
position: 'top',
});
const { tooltip, position } = toRefs(props);
const targetRef = ref();
const isTargetHovered = useElementHover(targetRef);
</script>
<template>
<div class="relative" inline-block>
<div relative inline-block>
<div ref="targetRef">
<slot />
</div>
<div
v-if="tooltip || $slots.tooltip"
class="absolute bottom-100% left-50% z-10 mb-5px whitespace-nowrap rounded bg-black px-12px py-6px text-sm text-white shadow-lg transition transition transition-duration-0.2s -translate-x-1/2"
class="absolute z-10 whitespace-nowrap rounded bg-black px-12px py-6px text-sm text-white shadow-lg transition transition transition-duration-0.2s"
:class="{
'op-0 scale-0': isTargetHovered === false,
'op-100 scale-100': isTargetHovered,
'bottom-100% left-50% -translate-x-1/2 mb-5px': position === 'top',
'top-100% left-50% -translate-x-1/2 mt-5px': position === 'bottom',
'right-100% top-50% -translate-y-1/2 mr-5px': position === 'left',
'left-100% top-50% -translate-y-1/2 ml-5px': position === 'right',
}"
>
<slot

View file

@ -20,5 +20,6 @@ export default defineConfig({
shortcuts: {
'pretty-scrollbar': 'scrollbar scrollbar-rounded scrollbar-thumb-color-gray-300 scrollbar-track-color-gray-100 dark:scrollbar-thumb-color-#424242 dark:scrollbar-track-color-#686868',
'divider': 'h-1px bg-current op-10',
'bg-surface': 'bg-#ffffff dark:bg-#232323',
},
});