mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-08 07:11:03 -04:00
Merge branch 'CorentinTh:main' into main
This commit is contained in:
commit
ee9bbcb327
21 changed files with 655 additions and 475 deletions
|
@ -100,7 +100,7 @@ pnpm lint
|
||||||
To create a new tool, there is a script that generate the boilerplate of the new tool, simply run:
|
To create a new tool, there is a script that generate the boilerplate of the new tool, simply run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pnpm run script:create-new-tool my-tool-name
|
pnpm run script:create:tool my-tool-name
|
||||||
```
|
```
|
||||||
|
|
||||||
It will create a directory in `src/tools` with the correct files, and a the import in `src/tools/index.ts`. You will just need to add the imported tool in the proper category and develop the tool.
|
It will create a directory in `src/tools` with the correct files, and a the import in `src/tools/index.ts`. You will just need to add the imported tool in the proper category and develop the tool.
|
||||||
|
|
2
components.d.ts
vendored
2
components.d.ts
vendored
|
@ -43,6 +43,8 @@ declare module '@vue/runtime-core' {
|
||||||
CLabel: typeof import('./src/ui/c-label/c-label.vue')['default']
|
CLabel: typeof import('./src/ui/c-label/c-label.vue')['default']
|
||||||
CLink: typeof import('./src/ui/c-link/c-link.vue')['default']
|
CLink: typeof import('./src/ui/c-link/c-link.vue')['default']
|
||||||
'CLink.demo': typeof import('./src/ui/c-link/c-link.demo.vue')['default']
|
'CLink.demo': typeof import('./src/ui/c-link/c-link.demo.vue')['default']
|
||||||
|
CMarkdown: typeof import('./src/ui/c-markdown/c-markdown.vue')['default']
|
||||||
|
'CMarkdown.demo': typeof import('./src/ui/c-markdown/c-markdown.demo.vue')['default']
|
||||||
CModal: typeof import('./src/ui/c-modal/c-modal.vue')['default']
|
CModal: typeof import('./src/ui/c-modal/c-modal.vue')['default']
|
||||||
'CModal.demo': typeof import('./src/ui/c-modal/c-modal.demo.vue')['default']
|
'CModal.demo': typeof import('./src/ui/c-modal/c-modal.demo.vue')['default']
|
||||||
CModalValue: typeof import('./src/ui/c-modal-value/c-modal-value.vue')['default']
|
CModalValue: typeof import('./src/ui/c-modal-value/c-modal-value.vue')['default']
|
||||||
|
|
|
@ -7,6 +7,7 @@ home:
|
||||||
toggleMenu: 'Toggle menu'
|
toggleMenu: 'Toggle menu'
|
||||||
home: Home
|
home: Home
|
||||||
uiLib: 'UI Lib'
|
uiLib: 'UI Lib'
|
||||||
|
support: 'Support IT Tools development'
|
||||||
buyMeACoffee: 'Buy me a coffee'
|
buyMeACoffee: 'Buy me a coffee'
|
||||||
follow:
|
follow:
|
||||||
title: 'You like it-tools?'
|
title: 'You like it-tools?'
|
||||||
|
@ -26,26 +27,31 @@ home:
|
||||||
lightMode: 'Light mode'
|
lightMode: 'Light mode'
|
||||||
mode: 'Toggle dark/light mode'
|
mode: 'Toggle dark/light mode'
|
||||||
about:
|
about:
|
||||||
h1: 'About IT-Tools'
|
content: >
|
||||||
h1p1: 'This wonderful website, made with ❤ by'
|
# About IT-Tools
|
||||||
h1p2: ", 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!"
|
|
||||||
h1p3: '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'
|
This wonderful website, made with ❤ by [Corentin Thomasset](https://github.com/CorentinTh) , 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!
|
||||||
h1p4: 'sponsoring me'
|
|
||||||
h2: Technologies
|
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 [sponsoring me](https://www.buymeacoffee.com/cthmsst).
|
||||||
h2p1: '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'
|
|
||||||
h2p2: 'file of the repository.'
|
## Technologies
|
||||||
h3: 'Found a bug? A tool is missing?'
|
|
||||||
h3p1: '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'
|
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 [package.json](https://github.com/CorentinTh/it-tools/blob/main/package.json) file of the repository.
|
||||||
h3p2: 'issues section'
|
|
||||||
h3p3: 'in the GitHub repository.'
|
## Found a bug? A tool is missing?
|
||||||
h3p4: "And if you found a bug, or something doesn''t work as expected, please file a bug report in the"
|
|
||||||
h3p5: 'issues section'
|
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 [issues section](https://github.com/CorentinTh/it-tools/issues/new/choose) in the GitHub repository.
|
||||||
h3p6: 'in the GitHub repository.'
|
|
||||||
|
And if you found a bug, or something doesn't work as expected, please file a bug report in the [issues section](https://github.com/CorentinTh/it-tools/issues/new/choose) in the GitHub repository.
|
||||||
|
|
||||||
404:
|
404:
|
||||||
notFound: '404 Not Found'
|
notFound: '404 Not Found'
|
||||||
sorry: 'Sorry, this page does not seem to exist'
|
sorry: 'Sorry, this page does not seem to exist'
|
||||||
maybe: 'Maybe the cache is doing tricky things, try force-refreshing?'
|
maybe: 'Maybe the cache is doing tricky things, try force-refreshing?'
|
||||||
backHome: 'Back home'
|
backHome: 'Back home'
|
||||||
|
favoriteButton:
|
||||||
|
remove: 'Remove from favorites'
|
||||||
|
add: 'Add to favorites'
|
||||||
toolCard:
|
toolCard:
|
||||||
new: New
|
new: New
|
||||||
search:
|
search:
|
||||||
|
|
|
@ -25,6 +25,22 @@ home:
|
||||||
darkMode: 'Mode sombre'
|
darkMode: 'Mode sombre'
|
||||||
lightMode: 'Mode clair'
|
lightMode: 'Mode clair'
|
||||||
mode: 'Basculer le mode sombre/clair'
|
mode: 'Basculer le mode sombre/clair'
|
||||||
|
about:
|
||||||
|
content: >
|
||||||
|
# À propos de IT-Tools
|
||||||
|
|
||||||
|
Ce merveilleux site, fait avec ❤ par [Corentin Thomasset](https://github.com/CorentinTh), regroupe des outils utiles pour les développeurs et les personnes travaillant dans l'informatique. Si vous le trouvez utile, n'hésitez pas à le partager et n'oubliez pas de le mettre dans vos favoris !
|
||||||
|
|
||||||
|
IT Tools est open-source (sous licence MIT) et gratuit, et le restera toujours, mais cela me coûte de l'argent pour l'héberger et renouveler le nom de domaine. Si vous voulez soutenir mon travail, et m'encourager à ajouter plus d'outils, n'hésitez pas à me [soutenir](https://www.buymeacoffee.com/cthmsst).
|
||||||
|
|
||||||
|
## Technologies
|
||||||
|
|
||||||
|
IT Tools est fait en Vue.js (Vue 3) avec la bibliothèque de composants Naive UI et est hébergé et déployé en continu par Vercel. Des bibliothèques open-source tierces sont utilisées dans certains outils, vous pouvez trouver la liste complète dans le fichier [package.json](https://github.com/CorentinTh/it-tools/blob/main/package.json) du dépôt.
|
||||||
|
|
||||||
|
## Vous avez trouvé un bug ? Un outil manque ?
|
||||||
|
|
||||||
|
Si vous avez besoin d'un outil qui n'est pas encore présent ici, et que vous pensez qu'il peut être utile, vous êtes invité à soumettre une demande de fonctionnalité dans la [section issue](https://github.com/CorentinTh/it-tools/issues/new/choose) du dépôt GitHub.
|
||||||
|
|
||||||
404:
|
404:
|
||||||
notFound: '404 Not Found'
|
notFound: '404 Not Found'
|
||||||
sorry: "Désolé, cette page n'existe pas"
|
sorry: "Désolé, cette page n'existe pas"
|
||||||
|
|
71
locales/zh.yml
Normal file
71
locales/zh.yml
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
home:
|
||||||
|
categories:
|
||||||
|
newestTools: '最新工具'
|
||||||
|
favoriteTools: '我的收藏'
|
||||||
|
allTools: '全部工具'
|
||||||
|
subtitle: '助力开发人员和 IT 工作者'
|
||||||
|
toggleMenu: '切换菜单'
|
||||||
|
home: '主页'
|
||||||
|
uiLib: 'UI 库'
|
||||||
|
support: '支持 IT 工具开发'
|
||||||
|
buyMeACoffee: '赞助'
|
||||||
|
follow:
|
||||||
|
title: '关注我们'
|
||||||
|
p1: '给我们 Star'
|
||||||
|
githubRepository: 'GitHub 仓库'
|
||||||
|
p2: '关注我们的'
|
||||||
|
twitterAccount: 'Twitter'
|
||||||
|
thankYou: '感谢您的支持!'
|
||||||
|
nav:
|
||||||
|
github: 'GitHub 仓库'
|
||||||
|
githubRepository: 'GitHub 仓库'
|
||||||
|
twitter: 'Twitter 账号'
|
||||||
|
twitterAccount: 'Twitter 账号'
|
||||||
|
about: '关于 IT-Tools'
|
||||||
|
aboutLabel: '关于'
|
||||||
|
darkMode: '深色模式'
|
||||||
|
lightMode: '浅色模式'
|
||||||
|
mode: '颜色模式'
|
||||||
|
about:
|
||||||
|
content: >
|
||||||
|
# 关于 IT-Tools
|
||||||
|
|
||||||
|
IT-Tools 由 [Corentin Thomasset](https://github.com/CorentinTh) 用 ❤ 开发,汇集了对开发人员和 IT 从业者有用的工具。如果对您有帮助,请将其分享给您的朋友,并且添加到收藏夹中!
|
||||||
|
|
||||||
|
IT-Tools 永久免费且开源(MIT 许可证),但需要资金用于托管和续订域名。如果您想支持我的工作,并鼓励我添加更多工具,请考虑通过 [赞助我](https://www.buymeacoffee.com/cthmsst) 进行支持。
|
||||||
|
|
||||||
|
## 技术
|
||||||
|
|
||||||
|
IT-Tools 采用 Vue.js(Vue 3)和 Naive UI 组件库开发,并由 Vercel 托管和持续部署。某些工具使用了第三方开源库,您可以在仓库的 [package.json](https://github.com/CorentinTh/it-tools/blob/main/package.json) 文件中找到完整的列表。
|
||||||
|
|
||||||
|
## 发现了 Bug?缺少工具?
|
||||||
|
|
||||||
|
如果目前这里没有您需要的工具,并且您认为它可能有用,欢迎在 GitHub 仓库的 [issues](https://github.com/CorentinTh/it-tools/issues/new/choose) 中提交新增功能的请求。
|
||||||
|
|
||||||
|
如果您发现了 Bug,或者某些功能未能按预期工作,请在 GitHub 仓库的 [issues](https://github.com/CorentinTh/it-tools/issues/new/choose) 中提交错误报告。
|
||||||
|
|
||||||
|
404:
|
||||||
|
notFound: '404 页面不存在'
|
||||||
|
sorry: '抱歉,该页面似乎不存在'
|
||||||
|
maybe: '也许缓存出现了一些问题,试试强制刷新页面?'
|
||||||
|
backHome: '返回主页'
|
||||||
|
favoriteButton:
|
||||||
|
remove: '取消收藏'
|
||||||
|
add: '加入收藏'
|
||||||
|
toolCard:
|
||||||
|
new: '新'
|
||||||
|
search:
|
||||||
|
label: '搜索'
|
||||||
|
tools:
|
||||||
|
categories:
|
||||||
|
favorite-tools: '我的收藏'
|
||||||
|
crypto: '加密'
|
||||||
|
converter: '转换器'
|
||||||
|
web: Web
|
||||||
|
images and videos: '图片和视频'
|
||||||
|
development: '开发'
|
||||||
|
network: '网络'
|
||||||
|
math: '数学'
|
||||||
|
measurement: '测量'
|
||||||
|
text: '文本'
|
||||||
|
data: '数据'
|
|
@ -55,6 +55,7 @@
|
||||||
"cronstrue": "^2.26.0",
|
"cronstrue": "^2.26.0",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
|
"dompurify": "^3.0.6",
|
||||||
"emojilib": "^3.0.10",
|
"emojilib": "^3.0.10",
|
||||||
"figue": "^1.2.0",
|
"figue": "^1.2.0",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
|
@ -65,13 +66,14 @@
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"libphonenumber-js": "^1.10.28",
|
"libphonenumber-js": "^1.10.28",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"marked": "^10.0.0",
|
||||||
"mathjs": "^11.9.1",
|
"mathjs": "^11.9.1",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"monaco-editor": "^0.43.0",
|
"monaco-editor": "^0.43.0",
|
||||||
"naive-ui": "^2.35.0",
|
"naive-ui": "^2.35.0",
|
||||||
"netmask": "^2.0.2",
|
"netmask": "^2.0.2",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"oui": "^12.0.52",
|
"oui-data": "^1.0.10",
|
||||||
"pdf-signature-reader": "^1.4.2",
|
"pdf-signature-reader": "^1.4.2",
|
||||||
"pinia": "^2.0.34",
|
"pinia": "^2.0.34",
|
||||||
"plausible-tracker": "^0.3.8",
|
"plausible-tracker": "^0.3.8",
|
||||||
|
@ -98,6 +100,7 @@
|
||||||
"@tsconfig/node18": "^18.2.0",
|
"@tsconfig/node18": "^18.2.0",
|
||||||
"@types/bcryptjs": "^2.4.2",
|
"@types/bcryptjs": "^2.4.2",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/jsdom": "^21.0.0",
|
"@types/jsdom": "^21.0.0",
|
||||||
"@types/lodash": "^4.14.192",
|
"@types/lodash": "^4.14.192",
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
|
@ -107,7 +110,7 @@
|
||||||
"@types/qrcode": "^1.5.0",
|
"@types/qrcode": "^1.5.0",
|
||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@types/uuid": "^9.0.0",
|
"@types/uuid": "^9.0.0",
|
||||||
"@unocss/eslint-config": "^0.55.0",
|
"@unocss/eslint-config": "^0.57.0",
|
||||||
"@vitejs/plugin-vue": "^4.3.2",
|
"@vitejs/plugin-vue": "^4.3.2",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
||||||
"@vue/compiler-sfc": "^3.2.47",
|
"@vue/compiler-sfc": "^3.2.47",
|
||||||
|
@ -121,7 +124,7 @@
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"typescript": "~5.2.0",
|
"typescript": "~5.2.0",
|
||||||
"unocss": "^0.55.0",
|
"unocss": "^0.57.0",
|
||||||
"unocss-preset-scrollbar": "^0.2.1",
|
"unocss-preset-scrollbar": "^0.2.1",
|
||||||
"unplugin-icons": "^0.17.0",
|
"unplugin-icons": "^0.17.0",
|
||||||
"unplugin-vue-components": "^0.25.0",
|
"unplugin-vue-components": "^0.25.0",
|
||||||
|
|
668
pnpm-lock.yaml
generated
668
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,7 @@ function toggleFavorite(event: MouseEvent) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<c-tooltip :tooltip="isFavorite ? 'Remove from favorites' : 'Add to favorites' ">
|
<c-tooltip :tooltip="isFavorite ? $t('favoriteButton.remove') : $t('favoriteButton.add') ">
|
||||||
<c-button
|
<c-button
|
||||||
variant="text"
|
variant="text"
|
||||||
circle
|
circle
|
||||||
|
|
32
src/composable/downloadBase64.test.ts
Normal file
32
src/composable/downloadBase64.test.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { getMimeTypeFromBase64 } from './downloadBase64';
|
||||||
|
|
||||||
|
describe('downloadBase64', () => {
|
||||||
|
describe('getMimeTypeFromBase64', () => {
|
||||||
|
it('when the base64 string has a data URI, it returns the mime type', () => {
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA' })).to.deep.equal({ mimeType: 'image/png' });
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: 'data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA' })).to.deep.equal({ mimeType: 'image/jpg' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when the base64 string has no data URI, it try to infer the mime type from the signature', () => {
|
||||||
|
// https://en.wikipedia.org/wiki/List_of_file_signatures
|
||||||
|
|
||||||
|
// PNG
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: 'iVBORw0KGgoAAAANSUhEUgAAAAUA' })).to.deep.equal({ mimeType: 'image/png' });
|
||||||
|
|
||||||
|
// GIF
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: 'R0lGODdh' })).to.deep.equal({ mimeType: 'image/gif' });
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: 'R0lGODlh' })).to.deep.equal({ mimeType: 'image/gif' });
|
||||||
|
|
||||||
|
// JPG
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: '/9j/' })).to.deep.equal({ mimeType: 'image/jpg' });
|
||||||
|
|
||||||
|
// PDF
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: 'JVBERi0' })).to.deep.equal({ mimeType: 'application/pdf' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when the base64 string has no data URI and no signature, it returns an undefined mimeType', () => {
|
||||||
|
expect(getMimeTypeFromBase64({ base64String: 'JVBERi' })).to.deep.equal({ mimeType: undefined });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,32 +1,60 @@
|
||||||
import { extension as getExtensionFromMime } from 'mime-types';
|
import { extension as getExtensionFromMime } from 'mime-types';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
function getFileExtensionFromBase64({
|
export { getMimeTypeFromBase64, useDownloadFileFromBase64 };
|
||||||
base64String,
|
|
||||||
|
const commonMimeTypesSignatures = {
|
||||||
|
'JVBERi0': 'application/pdf',
|
||||||
|
'R0lGODdh': 'image/gif',
|
||||||
|
'R0lGODlh': 'image/gif',
|
||||||
|
'iVBORw0KGgo': 'image/png',
|
||||||
|
'/9j/': 'image/jpg',
|
||||||
|
};
|
||||||
|
|
||||||
|
function getMimeTypeFromBase64({ base64String }: { base64String: string }) {
|
||||||
|
const [,mimeTypeFromBase64] = base64String.match(/data:(.*?);base64/i) ?? [];
|
||||||
|
|
||||||
|
if (mimeTypeFromBase64) {
|
||||||
|
return { mimeType: mimeTypeFromBase64 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const inferredMimeType = _.find(commonMimeTypesSignatures, (_mimeType, signature) => base64String.startsWith(signature));
|
||||||
|
|
||||||
|
if (inferredMimeType) {
|
||||||
|
return { mimeType: inferredMimeType };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { mimeType: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileExtensionFromMimeType({
|
||||||
|
mimeType,
|
||||||
defaultExtension = 'txt',
|
defaultExtension = 'txt',
|
||||||
}: {
|
}: {
|
||||||
base64String: string
|
mimeType: string | undefined
|
||||||
defaultExtension?: string
|
defaultExtension?: string
|
||||||
}) {
|
}) {
|
||||||
const hasMimeType = base64String.match(/data:(.*?);base64/i);
|
if (mimeType) {
|
||||||
|
return getExtensionFromMime(mimeType) ?? defaultExtension;
|
||||||
if (hasMimeType) {
|
|
||||||
return getExtensionFromMime(hasMimeType[1]) || defaultExtension;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultExtension;
|
return defaultExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) {
|
function useDownloadFileFromBase64({ source, filename }: { source: Ref<string>; filename?: string }) {
|
||||||
return {
|
return {
|
||||||
download() {
|
download() {
|
||||||
const base64String = source.value;
|
if (source.value === '') {
|
||||||
|
|
||||||
if (base64String === '') {
|
|
||||||
throw new Error('Base64 string is empty');
|
throw new Error('Base64 string is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanFileName = filename ?? `file.${getFileExtensionFromBase64({ base64String })}`;
|
const { mimeType } = getMimeTypeFromBase64({ base64String: source.value });
|
||||||
|
const base64String = mimeType
|
||||||
|
? source.value
|
||||||
|
: `data:text/plain;base64,${source.value}`;
|
||||||
|
|
||||||
|
const cleanFileName = filename ?? `file.${getFileExtensionFromMimeType({ mimeType })}`;
|
||||||
|
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = base64String;
|
a.href = base64String;
|
||||||
|
|
|
@ -100,13 +100,13 @@ const tools = computed<ToolCategory[]>(() => [
|
||||||
<NIcon size="25" :component="Menu2" />
|
<NIcon size="25" :component="Menu2" />
|
||||||
</c-button>
|
</c-button>
|
||||||
|
|
||||||
<c-tooltip tooltip="Home" position="bottom">
|
<c-tooltip :tooltip="$t('home.home')" position="bottom">
|
||||||
<c-button to="/" circle variant="text" :aria-label="$t('home.home')">
|
<c-button to="/" circle variant="text" :aria-label="$t('home.home')">
|
||||||
<NIcon size="25" :component="Home2" />
|
<NIcon size="25" :component="Home2" />
|
||||||
</c-button>
|
</c-button>
|
||||||
</c-tooltip>
|
</c-tooltip>
|
||||||
|
|
||||||
<c-tooltip tooltip="UI Lib" position="bottom">
|
<c-tooltip :tooltip="$t('home.uiLib')" position="bottom">
|
||||||
<c-button v-if="config.app.env === 'development'" to="/c-lib" circle variant="text" :aria-label="$t('home.uiLib')">
|
<c-button v-if="config.app.env === 'development'" to="/c-lib" circle variant="text" :aria-label="$t('home.uiLib')">
|
||||||
<icon-mdi:brush-variant text-20px />
|
<icon-mdi:brush-variant text-20px />
|
||||||
</c-button>
|
</c-button>
|
||||||
|
@ -120,7 +120,7 @@ const tools = computed<ToolCategory[]>(() => [
|
||||||
<NavbarButtons v-if="!styleStore.isSmallScreen" />
|
<NavbarButtons v-if="!styleStore.isSmallScreen" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<c-tooltip position="bottom" tooltip="Support IT Tools development">
|
<c-tooltip position="bottom" :tooltip="$t('home.support')">
|
||||||
<c-button
|
<c-button
|
||||||
round
|
round
|
||||||
href="https://www.buymeacoffee.com/cthmsst"
|
href="https://www.buymeacoffee.com/cthmsst"
|
||||||
|
|
|
@ -1,63 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useHead } from '@vueuse/head';
|
import { useHead } from '@vueuse/head';
|
||||||
import { useTracker } from '@/modules/tracker/tracker.services';
|
|
||||||
|
|
||||||
useHead({ title: 'About - IT Tools' });
|
useHead({ title: 'About - IT Tools' });
|
||||||
const { tracker } = useTracker();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div mx-auto mt-50px max-w-600px>
|
<c-markdown :markdown="$t('about.content')" mx-auto mt-50px max-w-600px />
|
||||||
<h1>{{ $t('about.h1') }}</h1>
|
|
||||||
<p text-justify>
|
|
||||||
{{ $t('about.h1p1') }}
|
|
||||||
<c-link href="https://github.com/CorentinTh" target="_blank" rel="noopener">
|
|
||||||
Corentin Thomasset
|
|
||||||
</c-link>{{ $t('about.h1p2') }}
|
|
||||||
</p>
|
|
||||||
<p text-justify>
|
|
||||||
{{ $t('about.h1p3') }}
|
|
||||||
<c-link
|
|
||||||
href="https://www.buymeacoffee.com/cthmsst"
|
|
||||||
rel="noopener"
|
|
||||||
target="_blank"
|
|
||||||
@click="() => tracker.trackEvent({ eventName: 'Support button clicked' })"
|
|
||||||
>
|
|
||||||
{{ $t('about.h1p4') }}
|
|
||||||
</c-link>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>{{ $t('about.h2') }}</h2>
|
|
||||||
<p text-justify>
|
|
||||||
{{ $t('about.h2p1') }}
|
|
||||||
<c-link href="https://github.com/CorentinTh/it-tools/blob/main/package.json" rel="noopener" target="_blank">
|
|
||||||
package.json
|
|
||||||
</c-link>
|
|
||||||
{{ $t('about.h2p2') }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>{{ $t('about.h3') }}</h2>
|
|
||||||
<p text-justify>
|
|
||||||
{{ $t('about.h3p1') }}
|
|
||||||
<c-link
|
|
||||||
href="https://github.com/CorentinTh/it-tools/issues/new/choose"
|
|
||||||
rel="noopener"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ $t('about.h3p2') }}
|
|
||||||
</c-link>
|
|
||||||
{{ $t('about.h3p3') }}
|
|
||||||
</p>
|
|
||||||
<p text-justify>
|
|
||||||
{{ $t('about.h3p4') }}
|
|
||||||
<c-link
|
|
||||||
href="https://github.com/CorentinTh/it-tools/issues/new/choose"
|
|
||||||
rel="noopener"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ $t('about.h3p5') }}
|
|
||||||
</c-link>
|
|
||||||
{{ $t('about.h3p6') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import db from 'oui/oui.json';
|
import db from 'oui-data';
|
||||||
import { macAddressValidationRules } from '@/utils/macAddress';
|
import { macAddressValidationRules } from '@/utils/macAddress';
|
||||||
import { useCopy } from '@/composable/copy';
|
import { useCopy } from '@/composable/copy';
|
||||||
|
|
||||||
const getVendorValue = (address: string) => address.trim().replace(/[.:-]/g, '').toUpperCase().substring(0, 6);
|
const getVendorValue = (address: string) => address.trim().replace(/[.:-]/g, '').toUpperCase().substring(0, 6);
|
||||||
|
|
||||||
const macAddress = ref('20:37:06:12:34:56');
|
const macAddress = ref('20:37:06:12:34:56');
|
||||||
const details = computed<string | undefined>(() => db[getVendorValue(macAddress.value)]);
|
const details = computed<string | undefined>(() => (db as Record<string, string>)[getVendorValue(macAddress.value)]);
|
||||||
|
|
||||||
const { copy } = useCopy({ source: () => details.value ?? '', text: 'Vendor info copied to the clipboard' });
|
const { copy } = useCopy({ source: () => details.value ?? '', text: 'Vendor info copied to the clipboard' });
|
||||||
</script>
|
</script>
|
||||||
|
|
4
src/tools/mac-address-lookup/oui.d.ts
vendored
4
src/tools/mac-address-lookup/oui.d.ts
vendored
|
@ -1,4 +0,0 @@
|
||||||
declare module 'oui/oui.json' {
|
|
||||||
const db: Record<string, string>;
|
|
||||||
export default db;
|
|
||||||
}
|
|
|
@ -1,9 +1,16 @@
|
||||||
tools:
|
tools:
|
||||||
token-generator:
|
token-generator:
|
||||||
title: Générateur de token
|
title: Générateur de token
|
||||||
description: Génère une chaîne aléatoire avec les caractères que vous voulez, lettres majuscules ou minuscules, chiffres et/ou symboles.
|
description: >-
|
||||||
|
Génère une chaîne aléatoire avec les caractères que vous voulez, lettres
|
||||||
|
majuscules ou minuscules, chiffres et/ou symboles.
|
||||||
uppercase: Majuscules (ABC...)
|
uppercase: Majuscules (ABC...)
|
||||||
lowercase: Minuscules (abc...)
|
lowercase: Minuscules (abc...)
|
||||||
numbers: Chiffres (123...)
|
numbers: Chiffres (123...)
|
||||||
symbols: Symboles (!-;...)
|
symbols: Symboles (!-;...)
|
||||||
|
button:
|
||||||
|
copy: Copier
|
||||||
|
refresh: Rafraichir
|
||||||
|
copied: Le token a été copié
|
||||||
|
length: Longueur
|
||||||
|
tokenPlaceholder: Le token...
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { Fingerprint } from '@vicons/tabler';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
|
|
||||||
export const tool = defineTool({
|
export const tool = defineTool({
|
||||||
name: 'UUIDs v4 generator',
|
name: 'UUIDs generator',
|
||||||
path: '/uuid-generator',
|
path: '/uuid-generator',
|
||||||
description:
|
description:
|
||||||
'A Universally Unique Identifier (UUID) is a 128-bit number used to identify information in computer systems. The number of possible UUIDs is 16^32, which is 2^128 or about 3.4x10^38 (which is a lot!).',
|
'A Universally Unique Identifier (UUID) is a 128-bit number used to identify information in computer systems. The number of possible UUIDs is 16^32, which is 2^128 or about 3.4x10^38 (which is a lot!).',
|
||||||
keywords: ['uuid', 'v4', 'random', 'id', 'alphanumeric', 'identity', 'token', 'string', 'identifier', 'unique'],
|
keywords: ['uuid', 'v4', 'random', 'id', 'alphanumeric', 'identity', 'token', 'string', 'identifier', 'unique', 'v1', 'v3', 'v5', 'nil'],
|
||||||
component: () => import('./uuid-generator.vue'),
|
component: () => import('./uuid-generator.vue'),
|
||||||
icon: Fingerprint,
|
icon: Fingerprint,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,22 +1,94 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { v4 as generateUUID } from 'uuid';
|
import { v1 as generateUuidV1, v3 as generateUuidV3, v4 as generateUuidV4, v5 as generateUuidV5, NIL as nilUuid } from 'uuid';
|
||||||
import { useCopy } from '@/composable/copy';
|
import { useCopy } from '@/composable/copy';
|
||||||
import { computedRefreshable } from '@/composable/computedRefreshable';
|
import { computedRefreshable } from '@/composable/computedRefreshable';
|
||||||
|
import { withDefaultOnError } from '@/utils/defaults';
|
||||||
|
|
||||||
|
const versions = ['NIL', 'v1', 'v3', 'v4', 'v5'] as const;
|
||||||
|
|
||||||
|
const version = useStorage<typeof versions[number]>('uuid-generator:version', 'v4');
|
||||||
const count = useStorage('uuid-generator:quantity', 1);
|
const count = useStorage('uuid-generator:quantity', 1);
|
||||||
|
const v35Args = ref({ namespace: '6ba7b811-9dad-11d1-80b4-00c04fd430c8', name: '' });
|
||||||
|
|
||||||
const [uuids, refreshUUIDs] = computedRefreshable(() =>
|
const validUuidRules = [
|
||||||
Array.from({ length: count.value }, () => generateUUID()).join('\n'),
|
{
|
||||||
);
|
message: 'Invalid UUID',
|
||||||
|
validator: (value: string) => {
|
||||||
|
if (value === nilUuid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Boolean(value.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const generators = {
|
||||||
|
NIL: () => nilUuid,
|
||||||
|
v1: (index: number) => generateUuidV1({
|
||||||
|
clockseq: index,
|
||||||
|
msecs: Date.now(),
|
||||||
|
nsecs: Math.floor(Math.random() * 10000),
|
||||||
|
node: Array.from({ length: 6 }, () => Math.floor(Math.random() * 256)),
|
||||||
|
}),
|
||||||
|
v3: () => generateUuidV3(v35Args.value.name, v35Args.value.namespace),
|
||||||
|
v4: () => generateUuidV4(),
|
||||||
|
v5: () => generateUuidV5(v35Args.value.name, v35Args.value.namespace),
|
||||||
|
};
|
||||||
|
|
||||||
|
const [uuids, refreshUUIDs] = computedRefreshable(() => withDefaultOnError(() =>
|
||||||
|
Array.from({ length: count.value }, (_ignored, index) => {
|
||||||
|
const generator = generators[version.value] ?? generators.NIL;
|
||||||
|
return generator(index);
|
||||||
|
}).join('\n'), ''));
|
||||||
|
|
||||||
const { copy } = useCopy({ source: uuids, text: 'UUIDs copied to the clipboard' });
|
const { copy } = useCopy({ source: uuids, text: 'UUIDs copied to the clipboard' });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div flex items-center justify-center gap-3>
|
<c-buttons-select v-model:value="version" :options="versions" label="UUID version" label-width="100px" mb-2 />
|
||||||
Quantity :
|
|
||||||
<n-input-number v-model:value="count" :min="1" :max="50" placeholder="UUID quantity" />
|
<div mb-2 flex items-center>
|
||||||
|
<span w-100px>Quantity </span>
|
||||||
|
<n-input-number v-model:value="count" flex-1 :min="1" :max="50" placeholder="UUID quantity" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="version === 'v3' || version === 'v5'">
|
||||||
|
<div>
|
||||||
|
<c-buttons-select
|
||||||
|
v-model:value="v35Args.namespace"
|
||||||
|
:options="{
|
||||||
|
DNS: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
|
||||||
|
URL: '6ba7b811-9dad-11d1-80b4-00c04fd430c8',
|
||||||
|
OID: '6ba7b812-9dad-11d1-80b4-00c04fd430c8',
|
||||||
|
X500: '6ba7b814-9dad-11d1-80b4-00c04fd430c8',
|
||||||
|
}"
|
||||||
|
label="Namespace"
|
||||||
|
label-width="100px"
|
||||||
|
mb-2
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div flex-1>
|
||||||
|
<c-input-text
|
||||||
|
v-model:value="v35Args.namespace"
|
||||||
|
placeholder="Namespace"
|
||||||
|
label-width="100px"
|
||||||
|
label-position="left"
|
||||||
|
label=" "
|
||||||
|
:validation-rules="validUuidRules"
|
||||||
|
mb-2
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<c-input-text
|
||||||
|
v-model:value="v35Args.name"
|
||||||
|
placeholder="Name"
|
||||||
|
label="Name"
|
||||||
|
label-width="100px"
|
||||||
|
label-position="left"
|
||||||
|
mb-2
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<c-input-text
|
<c-input-text
|
||||||
|
|
|
@ -4,11 +4,18 @@ const optionsA = [
|
||||||
{ label: 'Option B', value: 'b', tooltip: 'This is a tooltip' },
|
{ label: 'Option B', value: 'b', tooltip: 'This is a tooltip' },
|
||||||
{ label: 'Option C', value: 'c' },
|
{ label: 'Option C', value: 'c' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const optionB = {
|
||||||
|
'Option A': 'a',
|
||||||
|
'Option B': 'b',
|
||||||
|
'Option C': 'c',
|
||||||
|
};
|
||||||
|
|
||||||
const valueA = ref('a');
|
const valueA = ref('a');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<c-buttons-select v-model:value="valueA" :options="optionsA" label="Label: " />
|
<c-buttons-select v-model:value="valueA" :options="optionsA" label="Label: " />
|
||||||
<c-buttons-select v-model:value="valueA" :options="optionsA" label="Label: " label-position="left" mt-2 />
|
<c-buttons-select v-model:value="valueA" :options="optionsA" label="Label: " label-position="left" mt-2 />
|
||||||
<c-buttons-select v-model:value="valueA" :options="optionsA" label="Label: " label-position="left" mt-2 />
|
<c-buttons-select v-model:value="valueA" :options="optionB" label="Options object: " />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script setup lang="ts" generic="T extends unknown">
|
<script setup lang="ts" generic="T extends unknown">
|
||||||
|
import _ from 'lodash';
|
||||||
import type { CLabelProps } from '../c-label/c-label.types';
|
import type { CLabelProps } from '../c-label/c-label.types';
|
||||||
import type { CButtonSelectOption } from './c-buttons-select.types';
|
import type { CButtonSelectOption } from './c-buttons-select.types';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
options?: CButtonSelectOption<T>[] | string[]
|
options?: CButtonSelectOption<T>[] | string[] | Record<string, T>
|
||||||
value?: T
|
value?: T
|
||||||
size?: 'small' | 'medium' | 'large'
|
size?: 'small' | 'medium' | 'large'
|
||||||
} & CLabelProps >(),
|
} & CLabelProps >(),
|
||||||
|
@ -20,14 +21,18 @@ const emits = defineEmits(['update:value']);
|
||||||
|
|
||||||
const { options: rawOptions, size } = toRefs(props);
|
const { options: rawOptions, size } = toRefs(props);
|
||||||
|
|
||||||
const options = computed(() => {
|
const options = computed<CButtonSelectOption<T>[]>(() => {
|
||||||
return rawOptions.value.map((option: string | CButtonSelectOption<T>) => {
|
if (_.isArray(rawOptions.value)) {
|
||||||
if (typeof option === 'string') {
|
return rawOptions.value.map((option: string | CButtonSelectOption<T>) => {
|
||||||
return { label: option, value: option };
|
if (typeof option === 'string') {
|
||||||
}
|
return { label: option, value: option };
|
||||||
|
}
|
||||||
|
|
||||||
return option;
|
return option;
|
||||||
});
|
}) as CButtonSelectOption<T>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.map(rawOptions.value, (value, label) => ({ label, value })) as CButtonSelectOption<T>[];
|
||||||
});
|
});
|
||||||
|
|
||||||
const value = useVModel(props, 'value', emits);
|
const value = useVModel(props, 'value', emits);
|
||||||
|
|
18
src/ui/c-markdown/c-markdown.demo.vue
Normal file
18
src/ui/c-markdown/c-markdown.demo.vue
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const md = `
|
||||||
|
# IT Tools
|
||||||
|
|
||||||
|
## About
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, nisl quis
|
||||||
|
mollis blandit, nunc nisl aliquam nunc, vitae aliquam nisl nunc vitae nisl.
|
||||||
|
|
||||||
|
- Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
- Sed euismod, nisl quis mollis blandit, nunc nisl aliquam nunc, vitae aliquam nisl nunc vitae nisl.
|
||||||
|
|
||||||
|
[it-tools](https://it-tools.tech)
|
||||||
|
`;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<c-markdown :markdown="md" />
|
||||||
|
</template>
|
21
src/ui/c-markdown/c-markdown.vue
Normal file
21
src/ui/c-markdown/c-markdown.vue
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { marked } from 'marked';
|
||||||
|
import DomPurify from 'dompurify';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{ markdown?: string }>(), { markdown: '' });
|
||||||
|
const { markdown } = toRefs(props);
|
||||||
|
|
||||||
|
marked.use({
|
||||||
|
renderer: {
|
||||||
|
link(href, title, text) {
|
||||||
|
return `<a class="text-primary transition decoration-none hover:underline" href="${href}" target="_blank" rel="noopener">${text}</a>`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const html = computed(() => DomPurify.sanitize(marked(markdown.value), { ADD_ATTR: ['target'] }));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-html="html" />
|
||||||
|
</template>
|
Loading…
Add table
Add a link
Reference in a new issue