mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-20 06:55:06 -04:00
Merge branch 'main' into feat/list-converter-enh
This commit is contained in:
commit
a3206b4249
33 changed files with 834 additions and 39 deletions
11
.github/fern-banner.svg
vendored
Normal file
11
.github/fern-banner.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 62 KiB |
BIN
.github/logo-dark.png
vendored
Normal file
BIN
.github/logo-dark.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
.github/logo-white.png
vendored
Normal file
BIN
.github/logo-white.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
.github/logo.png
vendored
BIN
.github/logo.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 7.8 KiB |
12
README.md
12
README.md
|
@ -1,7 +1,15 @@
|
|||

|
||||
<picture>
|
||||
<source srcset="./.github/logo-dark.png" media="(prefers-color-scheme: light)">
|
||||
<source srcset="./.github/logo-white.png" media="(prefers-color-scheme: dark)">
|
||||
<img src="./.github/logo-dark.png" alt="logo">
|
||||
</picture>
|
||||
|
||||
Useful tools for developer and people working in IT. [Have a look !](https://it-tools.tech).
|
||||
|
||||
## Sponsors
|
||||
|
||||
[](https://bit.ly/3zBl7DG)
|
||||
|
||||
## Functionalities and roadmap
|
||||
|
||||
Please check the [issues](https://github.com/CorentinTh/it-tools/issues) to see if some feature listed to be implemented.
|
||||
|
@ -113,7 +121,7 @@ Big thanks to all the people who have already contributed!
|
|||
|
||||
## Credits
|
||||
|
||||
Coded with ❤️ by [Corentin Thomasset](//corentin-thomasset.fr).
|
||||
Coded with ❤️ by [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=readme).
|
||||
|
||||
This project is continuously deployed using [vercel.com](https://vercel.com).
|
||||
|
||||
|
|
9
components.d.ts
vendored
9
components.d.ts
vendored
|
@ -121,6 +121,7 @@ declare module '@vue/runtime-core' {
|
|||
LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
|
||||
MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default']
|
||||
MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']
|
||||
MarkdownToHtml: typeof import('./src/tools/markdown-to-html/markdown-to-html.vue')['default']
|
||||
MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default']
|
||||
MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default']
|
||||
MenuBarItem: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue')['default']
|
||||
|
@ -129,17 +130,14 @@ declare module '@vue/runtime-core' {
|
|||
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
|
||||
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
|
||||
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
|
||||
NCode: typeof import('naive-ui')['NCode']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NH1: typeof import('naive-ui')['NH1']
|
||||
NH3: typeof import('naive-ui')['NH3']
|
||||
NIcon: typeof import('naive-ui')['NIcon']
|
||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
NLayout: typeof import('naive-ui')['NLayout']
|
||||
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||
NMenu: typeof import('naive-ui')['NMenu']
|
||||
|
@ -156,6 +154,9 @@ declare module '@vue/runtime-core' {
|
|||
PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default']
|
||||
QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
|
||||
RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
|
||||
RegexMemo: typeof import('./src/tools/regex-memo/regex-memo.vue')['default']
|
||||
'RegexMemo.content': typeof import('./src/tools/regex-memo/regex-memo.content.md')['default']
|
||||
RegexTester: typeof import('./src/tools/regex-tester/regex-tester.vue')['default']
|
||||
ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
|
||||
RomanNumeralConverter: typeof import('./src/tools/roman-numeral-converter/roman-numeral-converter.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
|
|
@ -38,7 +38,7 @@ about:
|
|||
# Über IT-Tools
|
||||
|
||||
Diese wunderbare Website, erstellt mit ❤ von [Corentin
|
||||
Thomasset](https://github.com/CorentinTh), sammelt nützliche Tools für
|
||||
Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about), sammelt nützliche Tools für
|
||||
Entwickler und Menschen, die in der IT arbeiten. Wenn du sie nützlich
|
||||
findest, teile sie gerne mit Personen, von denen du denkst, dass sie sie
|
||||
ebenfalls nützlich finden könnten, und vergiss nicht, sie in deiner
|
||||
|
|
|
@ -30,7 +30,7 @@ about:
|
|||
content: >
|
||||
# About IT-Tools
|
||||
|
||||
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!
|
||||
This wonderful website, made with ❤ by [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about) , 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!
|
||||
|
||||
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).
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ about:
|
|||
content: >
|
||||
# Sobre IT-Tools
|
||||
|
||||
Este maravilloso sitio web, hecho con ❤ por [Corentin Thomasset](https://github.com/CorentinTh) , agrega herramientas útiles para desarrolladores y personas que trabajan en IT. Si lo encuentra útil, no dude en compartirlo con las personas que crea que también pueden encontrarlo útil y ¡no olvide marcarlo como favorito en su barra de accesos directos!
|
||||
Este maravilloso sitio web, hecho con ❤ por [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about) , agrega herramientas útiles para desarrolladores y personas que trabajan en IT. Si lo encuentra útil, no dude en compartirlo con las personas que crea que también pueden encontrarlo útil y ¡no olvide marcarlo como favorito en su barra de accesos directos!
|
||||
|
||||
IT Tools es de código abierto (under the MIT license) y gratis, y siempre lo será, pero me cuesta dinero alojar y renovar el nombre de dominio. Si desea apoyar mi trabajo y animarme a agregar más herramientas, considere apoyarme a través de[sponsoring me](https://www.buymeacoffee.com/cthmsst).
|
||||
|
||||
|
@ -39,7 +39,7 @@ about:
|
|||
IT Tools está creado en Vue.js (Vue 3) con la biblioteca de componentes Naive UI y Vercel lo aloja y lo implementa continuamente. En algunas herramientas se utilizan bibliotecas de código abierto de terceros; puede encontrar la lista completa en [package.json](https://github.com/CorentinTh/it-tools/blob/main/package.json) archivo del repositorio.
|
||||
|
||||
## ¿Encontraste un error? ¿Falta una herramienta?
|
||||
|
||||
|
||||
Si necesita una herramienta que actualmente no está presente aquí y cree que puede ser útil, puede enviar una solicitud de función en el [issues section](https://github.com/CorentinTh/it-tools/issues/new/choose) en el repositorio de GitHub.
|
||||
|
||||
Y si encontró un error o algo no funciona como se esperaba, presente un reporte de error en el [issues section](https://github.com/CorentinTh/it-tools/issues/new/choose) en el repositorio de GitHub.
|
||||
|
@ -68,4 +68,4 @@ tools:
|
|||
math: Math
|
||||
measurement: Measurement
|
||||
text: Text
|
||||
data: Data
|
||||
data: Data
|
||||
|
|
|
@ -29,7 +29,7 @@ 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 !
|
||||
Ce merveilleux site, fait avec ❤ par [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about), 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).
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ about:
|
|||
content: >
|
||||
# Sobre o IT-Tools
|
||||
|
||||
Este site maravilhoso, feito com ❤ por [Corentin Thomasset](https://github.com/CorentinTh), junta ferramentas úteis para desenvolvedores e outras pessoas que trabalham com TI. Se você achar o site útil, fique à vontade para compartilhar com quem também possa gostar e não esqueça de salvar o bookmark na sua barra de atalhos!
|
||||
Este site maravilhoso, feito com ❤ por [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about), junta ferramentas úteis para desenvolvedores e outras pessoas que trabalham com TI. Se você achar o site útil, fique à vontade para compartilhar com quem também possa gostar e não esqueça de salvar o bookmark na sua barra de atalhos!
|
||||
|
||||
O IT Tools é código aberto (sob a licença MIT), é gratuito, e sempre será, mas custa dinheiro para hospedar e renovar o domínio. Se quiser apoiar meu trabalho e me encorajar a adicionar mais ferramentas, por favor considere [ser patrocinador](https://www.buymeacoffee.com/cthmsst).
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ about:
|
|||
content: >
|
||||
# Про IT-Tools
|
||||
|
||||
Цей чудовий вебсайт, створений з ❤ [Corentin Thomasset](https://github.com/CorentinTh), агрегує корисні інструменти для розробників і людей, які працюють в сфері IT. Якщо вам це корисно, будь ласка, поділіться цим з людьми, які, на вашу думку, також можуть знайти його корисним, і не забудьте додати його до закладок у вашій панелі швидкого доступу!
|
||||
Цей чудовий вебсайт, створений з ❤ [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about), агрегує корисні інструменти для розробників і людей, які працюють в сфері IT. Якщо вам це корисно, будь ласка, поділіться цим з людьми, які, на вашу думку, також можуть знайти його корисним, і не забудьте додати його до закладок у вашій панелі швидкого доступу!
|
||||
|
||||
IT Tools є відкритим програмним забезпеченням (під ліцензією MIT) і безкоштовним, і завжди буде таким, але мені коштує гроші для хостингу і продовження доменного імені. Якщо ви хочете підтримати мою роботу і підтримати мене у додаванні нових інструментів, розгляньте можливість підтримки, [спонсоруючи мене](https://www.buymeacoffee.com/cthmsst).
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ about:
|
|||
content: >
|
||||
# Về IT-Tools
|
||||
|
||||
Website tuyệt vời này, được tạo ra bằng ❤ bởi [Corentin Thomasset](https://github.com/CorentinTh), tổng hợp các công cụ hữu ích cho nhà phát triển và những người làm việc trong lĩnh vực IT. Nếu bạn thấy nó hữu ích, xin đừng ngần ngại chia sẻ cho những người mà bạn nghĩ sẽ thấy nó hữu ích và đừng quên đánh dấu nó trong thanh lối tắt của bạn!
|
||||
Website tuyệt vời này, được tạo ra bằng ❤ bởi [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about), tổng hợp các công cụ hữu ích cho nhà phát triển và những người làm việc trong lĩnh vực IT. Nếu bạn thấy nó hữu ích, xin đừng ngần ngại chia sẻ cho những người mà bạn nghĩ sẽ thấy nó hữu ích và đừng quên đánh dấu nó trong thanh lối tắt của bạn!
|
||||
|
||||
IT Tools là mã nguồn mở (dưới giấy phép MIT) và miễn phí, và sẽ luôn như vậy, nhưng tôi phải trả tiền để lưu trữ và gia hạn tên miền. Nếu bạn muốn hỗ trợ công việc của tôi, và khích lệ tôi thêm nhiều công cụ hơn, hãy xem xét hỗ trợ bằng cách [tài trợ cho tôi](https://www.buymeacoffee.com/cthmsst).
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ about:
|
|||
content: >
|
||||
# 关于 IT-Tools
|
||||
|
||||
IT-Tools 由 [Corentin Thomasset](https://github.com/CorentinTh) 用 ❤ 开发,汇集了对开发人员和 IT 从业者有用的工具。如果对您有帮助,请将其分享给您的朋友,并且添加到收藏夹中!
|
||||
IT-Tools 由 [Corentin Thomasset](https://corentin.tech?utm_source=it-tools&utm_medium=about) 用 ❤ 开发,汇集了对开发人员和 IT 从业者有用的工具。如果对您有帮助,请将其分享给您的朋友,并且添加到收藏夹中!
|
||||
|
||||
IT-Tools 永久免费且开源(MIT 许可证),但需要资金用于托管和续订域名。如果您想支持我的工作,并鼓励我添加更多工具,请考虑通过 [赞助我](https://www.buymeacoffee.com/cthmsst) 进行支持。
|
||||
|
||||
|
|
21
package.json
21
package.json
|
@ -1,7 +1,14 @@
|
|||
{
|
||||
"name": "it-tools",
|
||||
"version": "2024.5.13-a0bc346",
|
||||
"packageManager": "pnpm@8.15.3",
|
||||
"description": "Collection of handy online tools for developers, with great UX. ",
|
||||
"author": "Corentin Th <corentin.thomasset74+it-tools@gmail.com> (https://corentin.tech)",
|
||||
"license": "GNU GPLv3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CorentinTh/it-tools"
|
||||
},
|
||||
"keywords": [
|
||||
"productivity",
|
||||
"converter",
|
||||
|
@ -13,12 +20,6 @@
|
|||
"developer-tools",
|
||||
"developer-productivity"
|
||||
],
|
||||
"author": "Corentin Th <corentin.thomasset74+it-tools@gmail.com> (https://github.com/CorentinTh)",
|
||||
"license": "GNU GPLv3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CorentinTh/it-tools"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc --noEmit && NODE_OPTIONS=--max_old_space_size=4096 vite build",
|
||||
|
@ -37,11 +38,13 @@
|
|||
"dependencies": {
|
||||
"@it-tools/bip39": "^0.0.4",
|
||||
"@it-tools/oggen": "^1.3.0",
|
||||
"@regexper/render": "^1.0.0",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"@tiptap/pm": "2.1.6",
|
||||
"@tiptap/starter-kit": "2.1.6",
|
||||
"@tiptap/vue-3": "2.0.3",
|
||||
"@types/figlet": "^1.5.8",
|
||||
"@types/markdown-it": "^13.0.7",
|
||||
"@vicons/material": "^0.12.0",
|
||||
"@vicons/tabler": "^0.12.0",
|
||||
"@vueuse/core": "^10.3.0",
|
||||
|
@ -70,6 +73,7 @@
|
|||
"jwt-decode": "^3.1.2",
|
||||
"libphonenumber-js": "^1.10.28",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^14.0.0",
|
||||
"marked": "^10.0.0",
|
||||
"mathjs": "^11.9.1",
|
||||
"mime-types": "^2.1.35",
|
||||
|
@ -82,6 +86,7 @@
|
|||
"pinia": "^2.0.34",
|
||||
"plausible-tracker": "^0.3.8",
|
||||
"qrcode": "^1.5.1",
|
||||
"randexp": "^0.5.3",
|
||||
"sql-formatter": "^13.0.0",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"ulid": "^2.3.0",
|
||||
|
@ -91,6 +96,7 @@
|
|||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.9.1",
|
||||
"vue-router": "^4.1.6",
|
||||
"vue-shadow-dom": "^4.2.0",
|
||||
"vue-tsc": "^1.8.1",
|
||||
"xml-formatter": "^3.3.2",
|
||||
"xml-js": "^1.6.11",
|
||||
|
@ -140,6 +146,5 @@
|
|||
"vitest": "^0.34.0",
|
||||
"workbox-window": "^7.0.0",
|
||||
"zx": "^7.2.1"
|
||||
},
|
||||
"packageManager": "pnpm@8.15.3"
|
||||
}
|
||||
}
|
||||
|
|
107
pnpm-lock.yaml
generated
107
pnpm-lock.yaml
generated
|
@ -11,6 +11,9 @@ dependencies:
|
|||
'@it-tools/oggen':
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
'@regexper/render':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@sindresorhus/slugify':
|
||||
specifier: ^2.2.1
|
||||
version: 2.2.1
|
||||
|
@ -26,6 +29,9 @@ dependencies:
|
|||
'@types/figlet':
|
||||
specifier: ^1.5.8
|
||||
version: 1.5.8
|
||||
'@types/markdown-it':
|
||||
specifier: ^13.0.7
|
||||
version: 13.0.9
|
||||
'@vicons/material':
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0
|
||||
|
@ -110,6 +116,9 @@ dependencies:
|
|||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
markdown-it:
|
||||
specifier: ^14.0.0
|
||||
version: 14.1.0
|
||||
marked:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
|
@ -146,6 +155,9 @@ dependencies:
|
|||
qrcode:
|
||||
specifier: ^1.5.1
|
||||
version: 1.5.1
|
||||
randexp:
|
||||
specifier: ^0.5.3
|
||||
version: 0.5.3
|
||||
sql-formatter:
|
||||
specifier: ^13.0.0
|
||||
version: 13.0.0
|
||||
|
@ -173,6 +185,9 @@ dependencies:
|
|||
vue-router:
|
||||
specifier: ^4.1.6
|
||||
version: 4.1.6(vue@3.3.4)
|
||||
vue-shadow-dom:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
vue-tsc:
|
||||
specifier: ^1.8.1
|
||||
version: 1.8.1(typescript@5.2.2)
|
||||
|
@ -2474,6 +2489,17 @@ packages:
|
|||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
dev: false
|
||||
|
||||
/@regexper/parser@1.0.0:
|
||||
resolution: {integrity: sha512-S8AWIGpCNdl9PNHdbhI6TpXZsPk6FDU/RTZI+6UFF4rVFqDQKjCIbZSgFu7NihoEZKq57wKFPbbT1EzrjVvPHA==}
|
||||
dev: false
|
||||
|
||||
/@regexper/render@1.0.0:
|
||||
resolution: {integrity: sha512-xYm9RUgnhhZotTtf8UZpK1PG2CcTRXQ3JPwfTlYUZsy2J+UcTVc7BaO/MJadpMoVuT8jrIyptH4Y0HLzqhI3hQ==}
|
||||
dependencies:
|
||||
'@regexper/parser': 1.0.0
|
||||
'@svgdotjs/svg.js': 3.2.4
|
||||
dev: false
|
||||
|
||||
/@remirror/core-constants@2.0.1:
|
||||
resolution: {integrity: sha512-ZR4aihtnnT9lMbhh5DEbsriJRlukRXmLZe7HmM+6ufJNNUDoazc75UX26xbgQlNUqgAqMcUdGFAnPc1JwgAdLQ==}
|
||||
dependencies:
|
||||
|
@ -2623,6 +2649,10 @@ packages:
|
|||
string.prototype.matchall: 4.0.10
|
||||
dev: true
|
||||
|
||||
/@svgdotjs/svg.js@3.2.4:
|
||||
resolution: {integrity: sha512-BjJ/7vWNowlX3Z8O4ywT58DqbNRyYlkk6Yz/D13aB7hGmfQTvGX4Tkgtm/ApYlu9M7lCQi15xUEidqMUmdMYwg==}
|
||||
dev: false
|
||||
|
||||
/@tiptap/core@2.1.12(@tiptap/pm@2.1.6):
|
||||
resolution: {integrity: sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ==}
|
||||
peerDependencies:
|
||||
|
@ -2952,7 +2982,6 @@ packages:
|
|||
|
||||
/@types/linkify-it@3.0.2:
|
||||
resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==}
|
||||
dev: true
|
||||
|
||||
/@types/lodash-es@4.17.10:
|
||||
resolution: {integrity: sha512-YJP+w/2khSBwbUSFdGsSqmDvmnN3cCKoPOL7Zjle6s30ZtemkkqhjVfFqGwPN7ASil5VyjE2GtyU/yqYY6mC0A==}
|
||||
|
@ -2974,6 +3003,13 @@ packages:
|
|||
'@types/mdurl': 1.0.2
|
||||
dev: true
|
||||
|
||||
/@types/markdown-it@13.0.9:
|
||||
resolution: {integrity: sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==}
|
||||
dependencies:
|
||||
'@types/linkify-it': 3.0.2
|
||||
'@types/mdurl': 1.0.2
|
||||
dev: false
|
||||
|
||||
/@types/mdast@3.0.11:
|
||||
resolution: {integrity: sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==}
|
||||
dependencies:
|
||||
|
@ -2982,7 +3018,6 @@ packages:
|
|||
|
||||
/@types/mdurl@1.0.2:
|
||||
resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==}
|
||||
dev: true
|
||||
|
||||
/@types/mime-types@2.1.1:
|
||||
resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==}
|
||||
|
@ -3360,7 +3395,7 @@ packages:
|
|||
dependencies:
|
||||
'@unhead/dom': 0.5.1
|
||||
'@unhead/schema': 0.5.1
|
||||
'@vueuse/shared': 10.11.1(vue@3.3.4)
|
||||
'@vueuse/shared': 11.0.3(vue@3.3.4)
|
||||
unhead: 0.5.1
|
||||
vue: 3.3.4
|
||||
transitivePeerDependencies:
|
||||
|
@ -3993,19 +4028,19 @@ packages:
|
|||
- vue
|
||||
dev: false
|
||||
|
||||
/@vueuse/shared@10.11.1(vue@3.3.4):
|
||||
resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
|
||||
/@vueuse/shared@10.3.0(vue@3.3.4):
|
||||
resolution: {integrity: sha512-kGqCTEuFPMK4+fNWy6dUOiYmxGcUbtznMwBZLC1PubidF4VZY05B+Oht7Jh7/6x4VOWGpvu3R37WHi81cKpiqg==}
|
||||
dependencies:
|
||||
vue-demi: 0.14.10(vue@3.3.4)
|
||||
vue-demi: 0.14.5(vue@3.3.4)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
dev: false
|
||||
|
||||
/@vueuse/shared@10.3.0(vue@3.3.4):
|
||||
resolution: {integrity: sha512-kGqCTEuFPMK4+fNWy6dUOiYmxGcUbtznMwBZLC1PubidF4VZY05B+Oht7Jh7/6x4VOWGpvu3R37WHi81cKpiqg==}
|
||||
/@vueuse/shared@11.0.3(vue@3.3.4):
|
||||
resolution: {integrity: sha512-0rY2m6HS5t27n/Vp5cTDsKTlNnimCqsbh/fmT2LgE+aaU42EMfXo8+bNX91W9I7DDmxfuACXMmrd7d79JxkqWA==}
|
||||
dependencies:
|
||||
vue-demi: 0.14.5(vue@3.3.4)
|
||||
vue-demi: 0.14.10(vue@3.3.4)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
@ -4951,6 +4986,11 @@ packages:
|
|||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/drange@1.1.1:
|
||||
resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==}
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/duplexer@0.1.2:
|
||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||
dev: true
|
||||
|
@ -5016,7 +5056,6 @@ packages:
|
|||
/entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
dev: true
|
||||
|
||||
/errno@0.1.8:
|
||||
resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
|
||||
|
@ -6688,6 +6727,12 @@ packages:
|
|||
dependencies:
|
||||
uc.micro: 1.0.6
|
||||
|
||||
/linkify-it@5.0.0:
|
||||
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
|
||||
dependencies:
|
||||
uc.micro: 2.1.0
|
||||
dev: false
|
||||
|
||||
/local-pkg@0.4.3:
|
||||
resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==}
|
||||
engines: {node: '>=14'}
|
||||
|
@ -6825,6 +6870,18 @@ packages:
|
|||
mdurl: 1.0.1
|
||||
uc.micro: 1.0.6
|
||||
|
||||
/markdown-it@14.1.0:
|
||||
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
entities: 4.5.0
|
||||
linkify-it: 5.0.0
|
||||
mdurl: 2.0.0
|
||||
punycode.js: 2.3.1
|
||||
uc.micro: 2.1.0
|
||||
dev: false
|
||||
|
||||
/marked@10.0.0:
|
||||
resolution: {integrity: sha512-YiGcYcWj50YrwBgNzFoYhQ1hT6GmQbFG8SksnYJX1z4BXTHSOrz1GB5/Jm2yQvMg4nN1FHP4M6r03R10KrVUiA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
@ -6873,6 +6930,10 @@ packages:
|
|||
/mdurl@1.0.1:
|
||||
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
|
||||
|
||||
/mdurl@2.0.0:
|
||||
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
|
||||
dev: false
|
||||
|
||||
/merge-stream@2.0.0:
|
||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||
dev: true
|
||||
|
@ -7682,6 +7743,11 @@ packages:
|
|||
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
||||
dev: true
|
||||
|
||||
/punycode.js@2.3.1:
|
||||
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/punycode@2.3.0:
|
||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -7721,6 +7787,14 @@ packages:
|
|||
ret: 0.1.15
|
||||
dev: false
|
||||
|
||||
/randexp@0.5.3:
|
||||
resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==}
|
||||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
drange: 1.1.1
|
||||
ret: 0.2.2
|
||||
dev: false
|
||||
|
||||
/randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
dependencies:
|
||||
|
@ -7891,6 +7965,11 @@ packages:
|
|||
engines: {node: '>=0.12'}
|
||||
dev: false
|
||||
|
||||
/ret@0.2.2:
|
||||
resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==}
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/reusify@1.0.4:
|
||||
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
@ -8626,6 +8705,10 @@ packages:
|
|||
/uc.micro@1.0.6:
|
||||
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
|
||||
|
||||
/uc.micro@2.1.0:
|
||||
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
|
||||
dev: false
|
||||
|
||||
/ufo@1.1.2:
|
||||
resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==}
|
||||
|
||||
|
@ -9227,6 +9310,10 @@ packages:
|
|||
vue: 3.3.4
|
||||
dev: false
|
||||
|
||||
/vue-shadow-dom@4.2.0:
|
||||
resolution: {integrity: sha512-lguI064rT2HT/dxqSmXtz860KOvCq+W3nU1jMqroTmX3K1H46q22BMR4emh/Ld3ozy35XJKOaNGcr6mkJ/t/yg==}
|
||||
dev: false
|
||||
|
||||
/vue-template-compiler@2.7.14:
|
||||
resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
|
||||
dependencies:
|
||||
|
|
|
@ -7,6 +7,7 @@ import sqlHljs from 'highlight.js/lib/languages/sql';
|
|||
import xmlHljs from 'highlight.js/lib/languages/xml';
|
||||
import yamlHljs from 'highlight.js/lib/languages/yaml';
|
||||
import iniHljs from 'highlight.js/lib/languages/ini';
|
||||
import markdownHljs from 'highlight.js/lib/languages/markdown';
|
||||
import { useCopy } from '@/composable/copy';
|
||||
|
||||
const props = withDefaults(
|
||||
|
@ -30,6 +31,7 @@ hljs.registerLanguage('html', xmlHljs);
|
|||
hljs.registerLanguage('xml', xmlHljs);
|
||||
hljs.registerLanguage('yaml', yamlHljs);
|
||||
hljs.registerLanguage('toml', iniHljs);
|
||||
hljs.registerLanguage('markdown', markdownHljs);
|
||||
|
||||
const { value, language, followHeightOf, copyPlacement, copyMessage } = toRefs(props);
|
||||
const { height } = followHeightOf.value ? useElementSize(followHeightOf) : { height: ref(null) };
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useRouteQuery } from '@vueuse/router';
|
||||
import { computed } from 'vue';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
|
||||
export { useQueryParam };
|
||||
export { useQueryParam, useQueryParamOrStorage };
|
||||
|
||||
const transformers = {
|
||||
number: {
|
||||
|
@ -16,6 +17,12 @@ const transformers = {
|
|||
fromQuery: (value: string) => value.toLowerCase() === 'true',
|
||||
toQuery: (value: boolean) => (value ? 'true' : 'false'),
|
||||
},
|
||||
object: {
|
||||
fromQuery: (value: string) => {
|
||||
return JSON.parse(value);
|
||||
},
|
||||
toQuery: (value: object) => JSON.stringify(value),
|
||||
},
|
||||
};
|
||||
|
||||
function useQueryParam<T>({ name, defaultValue }: { name: string; defaultValue: T }) {
|
||||
|
@ -33,3 +40,27 @@ function useQueryParam<T>({ name, defaultValue }: { name: string; defaultValue:
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
function useQueryParamOrStorage<T>({ name, storageName, defaultValue }: { name: string; storageName: string; defaultValue: T }) {
|
||||
const type = typeof defaultValue;
|
||||
const transformer = transformers[type as keyof typeof transformers] ?? transformers.string;
|
||||
|
||||
const storageRef = useStorage(storageName, defaultValue);
|
||||
const proxyDefaultValue = transformer.toQuery(defaultValue as never);
|
||||
const proxy = useRouteQuery(name, proxyDefaultValue);
|
||||
|
||||
const r = ref(defaultValue);
|
||||
|
||||
watch(r,
|
||||
(value) => {
|
||||
proxy.value = transformer.toQuery(value as never);
|
||||
storageRef.value = value as never;
|
||||
},
|
||||
{ deep: true });
|
||||
|
||||
r.value = (proxy.value && proxy.value !== proxyDefaultValue
|
||||
? transformer.fromQuery(proxy.value) as unknown as T
|
||||
: storageRef.value as T) as never;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@ import _ from 'lodash';
|
|||
import { type Ref, reactive, watch } from 'vue';
|
||||
|
||||
type ValidatorReturnType = unknown;
|
||||
type GetErrorMessageReturnType = string;
|
||||
|
||||
export interface UseValidationRule<T> {
|
||||
validator: (value: T) => ValidatorReturnType
|
||||
getErrorMessage?: (value: T) => GetErrorMessageReturnType
|
||||
message: string
|
||||
}
|
||||
|
||||
|
@ -24,6 +26,15 @@ export function isFalsyOrHasThrown(cb: () => ValidatorReturnType): boolean {
|
|||
}
|
||||
}
|
||||
|
||||
export function getErrorMessageOrThrown(cb: () => GetErrorMessageReturnType): string {
|
||||
try {
|
||||
return cb() || '';
|
||||
}
|
||||
catch (e: any) {
|
||||
return e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export interface ValidationAttrs {
|
||||
feedback: string
|
||||
validationStatus: string | undefined
|
||||
|
@ -61,7 +72,13 @@ export function useValidation<T>({
|
|||
|
||||
for (const rule of get(rules)) {
|
||||
if (isFalsyOrHasThrown(() => rule.validator(source.value))) {
|
||||
state.message = rule.message;
|
||||
if (rule.getErrorMessage) {
|
||||
const getErrorMessage = rule.getErrorMessage;
|
||||
state.message = rule.message.replace('{0}', getErrorMessageOrThrown(() => getErrorMessage(source.value)));
|
||||
}
|
||||
else {
|
||||
state.message = rule.message;
|
||||
}
|
||||
state.status = 'error';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,12 @@ export const config = figue({
|
|||
default: false,
|
||||
env: 'VITE_SHOW_BANNER',
|
||||
},
|
||||
showSponsorBanner: {
|
||||
doc: 'Show the sponsor banner',
|
||||
format: 'boolean',
|
||||
default: false,
|
||||
env: 'VITE_SHOW_SPONSOR_BANNER',
|
||||
},
|
||||
})
|
||||
.loadEnv({
|
||||
...import.meta.env,
|
||||
|
|
|
@ -81,7 +81,7 @@ const tools = computed<ToolCategory[]>(() => [
|
|||
</div>
|
||||
<div>
|
||||
© {{ new Date().getFullYear() }}
|
||||
<c-link target="_blank" rel="noopener" href="https://github.com/CorentinTh">
|
||||
<c-link target="_blank" rel="noopener" href="https://corentin.tech?utm_source=it-tools&utm_medium=footer">
|
||||
Corentin Thomasset
|
||||
</c-link>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { createPinia } from 'pinia';
|
|||
import { createHead } from '@vueuse/head';
|
||||
|
||||
import { registerSW } from 'virtual:pwa-register';
|
||||
import shadow from 'vue-shadow-dom';
|
||||
import { plausible } from './plugins/plausible.plugin';
|
||||
|
||||
import 'virtual:uno.css';
|
||||
|
@ -23,5 +24,6 @@ app.use(i18nPlugin);
|
|||
app.use(router);
|
||||
app.use(naive);
|
||||
app.use(plausible);
|
||||
app.use(shadow);
|
||||
|
||||
app.mount('#app');
|
||||
|
|
|
@ -15,8 +15,8 @@ const { t } = useI18n();
|
|||
<template>
|
||||
<div class="pt-50px">
|
||||
<div class="grid-wrapper">
|
||||
<div v-if="config.showBanner" class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<ColoredCard :title="$t('home.follow.title')" :icon="Heart">
|
||||
<div class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<ColoredCard v-if="config.showBanner" :title="$t('home.follow.title')" :icon="Heart">
|
||||
{{ $t('home.follow.p1') }}
|
||||
<a
|
||||
href="https://github.com/CorentinTh/it-tools"
|
||||
|
@ -34,6 +34,32 @@ const { t } = useI18n();
|
|||
{{ $t('home.follow.thankYou') }}
|
||||
<n-icon :component="Heart" />
|
||||
</ColoredCard>
|
||||
|
||||
<a href="https://bit.ly/3zBl7DG" target="_blank" rel="noopener" class="text-current decoration-none">
|
||||
<c-card v-if="config.showSponsorBanner" class="cursor-pointer !border-2px !hover:border-primary">
|
||||
<div class="dark:hidden">
|
||||
<svg width="40" height="40" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M458.592 250.639C425.688 222.824 376.118 211.673 332.186 244.145C330.164 245.615 327.651 243.103 329.183 241.143C339.6 227.725 351.67 213.266 361.413 198.746C371.339 183.858 386.167 173.198 403.262 167.99C494.253 140.42 466.925 6 466.925 6C466.925 6 326.365 15.0675 343.705 136.315C346.585 156.594 341.193 177.241 328.509 193.354C312.946 213.021 294.87 231.83 281.758 245.431C279.001 248.25 274.344 245.554 275.447 241.755C288.13 199.052 297.383 133.007 253.45 90.4259L191.625 39.0842L179.738 54.7685C144.384 101.393 154.739 167.132 201.429 202.422C228.205 222.64 240.337 244.635 238.438 268.774C237.274 283.233 230.718 296.773 220.914 307.495C202.471 327.713 185.253 349.402 171.957 374.521C170.119 378.013 164.788 376.665 164.972 372.683C166.871 331.205 162.888 237.712 93.037 204.321L14.8527 174.117L8.78665 192.19C-10.882 250.517 21.2863 312.825 79.557 332.614C130.23 349.83 148.305 382.486 136.112 431.438C135.561 433.215 126.737 483.638 127.963 506H184.15C186.049 471.323 222.446 448.532 254.001 462.684C262.886 466.667 272.016 472.364 281.39 479.717C331.634 519.295 405.652 509.921 445.173 459.621L456.447 445.284L385.371 394.249C336.597 355.896 271.525 373.234 223.365 406.074C219.321 408.831 214.174 404.419 216.441 400.008C274.65 285.806 350.322 286.051 379.979 311.416C415.946 342.172 470.418 336.658 500.932 300.572L509.694 290.218L458.531 250.639H458.592Z" fill="#aaaaaa" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="hidden dark:block">
|
||||
<svg width="40" height="40" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M458.592 250.639C425.688 222.824 376.118 211.673 332.186 244.145C330.164 245.615 327.651 243.103 329.183 241.143C339.6 227.725 351.67 213.266 361.413 198.746C371.339 183.858 386.167 173.198 403.262 167.99C494.253 140.42 466.925 6 466.925 6C466.925 6 326.365 15.0675 343.705 136.315C346.585 156.594 341.193 177.241 328.509 193.354C312.946 213.021 294.87 231.83 281.758 245.431C279.001 248.25 274.344 245.554 275.447 241.755C288.13 199.052 297.383 133.007 253.45 90.4259L191.625 39.0842L179.738 54.7685C144.384 101.393 154.739 167.132 201.429 202.422C228.205 222.64 240.337 244.635 238.438 268.774C237.274 283.233 230.718 296.773 220.914 307.495C202.471 327.713 185.253 349.402 171.957 374.521C170.119 378.013 164.788 376.665 164.972 372.683C166.871 331.205 162.888 237.712 93.037 204.321L14.8527 174.117L8.78665 192.19C-10.882 250.517 21.2863 312.825 79.557 332.614C130.23 349.83 148.305 382.486 136.112 431.438C135.561 433.215 126.737 483.638 127.963 506H184.15C186.049 471.323 222.446 448.532 254.001 462.684C262.886 466.667 272.016 472.364 281.39 479.717C331.634 519.295 405.652 509.921 445.173 459.621L456.447 445.284L385.371 394.249C336.597 355.896 271.525 373.234 223.365 406.074C219.321 408.831 214.174 404.419 216.441 400.008C274.65 285.806 350.322 286.051 379.979 311.416C415.946 342.172 470.418 336.658 500.932 300.572L509.694 290.218L458.531 250.639H458.592Z" fill="#505050" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="my-5px flex items-baseline gap-4 text-lg text-black dark:text-white">
|
||||
Fern <div class="rounded-full bg-#eeeeee px-10px py-2px text-xs text-black dark:bg-#333333 dark:text-white">
|
||||
Sponsor
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-neutral-500 dark:text-neutral-400">
|
||||
Offer developer documentation that looks as good as Stripe's using Fern. <a href="https://bit.ly/3zBl7DG" target="_blank" rel="noopener" class="font-bold text-current transition hover:text-primary">Request a preview</a> of your docs on Fern.
|
||||
</div>
|
||||
</c-card>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<transition name="height">
|
||||
|
|
|
@ -9,6 +9,9 @@ import { tool as textToUnicode } from './text-to-unicode';
|
|||
import { tool as safelinkDecoder } from './safelink-decoder';
|
||||
import { tool as xmlToJson } from './xml-to-json';
|
||||
import { tool as jsonToXml } from './json-to-xml';
|
||||
import { tool as regexTester } from './regex-tester';
|
||||
import { tool as regexMemo } from './regex-memo';
|
||||
import { tool as markdownToHtml } from './markdown-to-html';
|
||||
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
|
||||
import { tool as numeronymGenerator } from './numeronym-generator';
|
||||
import { tool as macAddressGenerator } from './mac-address-generator';
|
||||
|
@ -112,6 +115,7 @@ export const toolsByCategory: ToolCategory[] = [
|
|||
tomlToYaml,
|
||||
xmlToJson,
|
||||
jsonToXml,
|
||||
markdownToHtml,
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -154,6 +158,8 @@ export const toolsByCategory: ToolCategory[] = [
|
|||
xmlFormatter,
|
||||
yamlViewer,
|
||||
emailNormalizer,
|
||||
regexTester,
|
||||
regexMemo,
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
12
src/tools/markdown-to-html/index.ts
Normal file
12
src/tools/markdown-to-html/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Markdown } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Markdown to HTML',
|
||||
path: '/markdown-to-html',
|
||||
description: 'Convert Markdown to Html and allow to print (as PDF)',
|
||||
keywords: ['markdown', 'html', 'converter', 'pdf'],
|
||||
component: () => import('./markdown-to-html.vue'),
|
||||
icon: Markdown,
|
||||
createdAt: new Date('2024-08-25'),
|
||||
});
|
44
src/tools/markdown-to-html/markdown-to-html.vue
Normal file
44
src/tools/markdown-to-html/markdown-to-html.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<script setup lang="ts">
|
||||
import markdownit from 'markdown-it';
|
||||
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||
|
||||
const inputMarkdown = ref('');
|
||||
const outputHtml = computed(() => {
|
||||
const md = markdownit();
|
||||
return md.render(inputMarkdown.value);
|
||||
});
|
||||
|
||||
function printHtml() {
|
||||
const w = window.open();
|
||||
if (w === null) {
|
||||
return;
|
||||
}
|
||||
w.document.body.innerHTML = outputHtml.value;
|
||||
w.print();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<c-input-text
|
||||
v-model:value="inputMarkdown"
|
||||
multiline raw-text
|
||||
placeholder="Your Markdown content..."
|
||||
rows="8"
|
||||
autofocus
|
||||
label="Your Markdown to convert:"
|
||||
/>
|
||||
|
||||
<n-divider />
|
||||
|
||||
<n-form-item label="Output HTML:">
|
||||
<TextareaCopyable :value="outputHtml" :word-wrap="true" language="html" />
|
||||
</n-form-item>
|
||||
|
||||
<div flex justify-center>
|
||||
<n-button @click="printHtml">
|
||||
Print as PDF
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
12
src/tools/regex-memo/index.ts
Normal file
12
src/tools/regex-memo/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { BrandJavascript } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Regex cheatsheet',
|
||||
path: '/regex-memo',
|
||||
description: 'Javascript Regex/Regular Expression cheatsheet',
|
||||
keywords: ['regex', 'regular', 'expression', 'javascript', 'memo', 'cheatsheet'],
|
||||
component: () => import('./regex-memo.vue'),
|
||||
icon: BrandJavascript,
|
||||
createdAt: new Date('2024-09-20'),
|
||||
});
|
121
src/tools/regex-memo/regex-memo.content.md
Normal file
121
src/tools/regex-memo/regex-memo.content.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
### Normal characters
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`.` or `[^\n\r]` | any character *excluding* a newline or carriage return
|
||||
`[A-Za-z]` | alphabet
|
||||
`[a-z]` | lowercase alphabet
|
||||
`[A-Z]` | uppercase alphabet
|
||||
`\d` or `[0-9]` | digit
|
||||
`\D` or `[^0-9]` | non-digit
|
||||
`_` | underscore
|
||||
`\w` or `[A-Za-z0-9_]` | alphabet, digit or underscore
|
||||
`\W` or `[^A-Za-z0-9_]` | inverse of `\w`
|
||||
`\S` | inverse of `\s`
|
||||
|
||||
### Whitespace characters
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
` ` | space
|
||||
`\t` | tab
|
||||
`\n` | newline
|
||||
`\r` | carriage return
|
||||
`\s` | space, tab, newline or carriage return
|
||||
|
||||
### Character set
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`[xyz]` | either `x`, `y` or `z`
|
||||
`[^xyz]` | neither `x`, `y` nor `z`
|
||||
`[1-3]` | either `1`, `2` or `3`
|
||||
`[^1-3]` | neither `1`, `2` nor `3`
|
||||
|
||||
- Think of a character set as an `OR` operation on the single characters that are enclosed between the square brackets.
|
||||
- Use `^` after the opening `[` to “negate” the character set.
|
||||
- Within a character set, `.` means a literal period.
|
||||
|
||||
### Characters that require escaping
|
||||
|
||||
#### Outside a character set
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`\.` | period
|
||||
`\^` | caret
|
||||
`\$` | dollar sign
|
||||
`\|` | pipe
|
||||
`\\` | back slash
|
||||
`\/` | forward slash
|
||||
`\(` | opening bracket
|
||||
`\)` | closing bracket
|
||||
`\[` | opening square bracket
|
||||
`\]` | closing square bracket
|
||||
`\{` | opening curly bracket
|
||||
`\}` | closing curly bracket
|
||||
|
||||
#### Inside a character set
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`\\` | back slash
|
||||
`\]` | closing square bracket
|
||||
|
||||
- A `^` must be escaped only if it occurs immediately after the opening `[` of the character set.
|
||||
- A `-` must be escaped only if it occurs between two alphabets or two digits.
|
||||
|
||||
### Quantifiers
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`{2}` | exactly 2
|
||||
`{2,}` | at least 2
|
||||
`{2,7}` | at least 2 but no more than 7
|
||||
`*` | 0 or more
|
||||
`+` | 1 or more
|
||||
`?` | exactly 0 or 1
|
||||
|
||||
- The quantifier goes *after* the expression to be quantified.
|
||||
|
||||
### Boundaries
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`^` | start of string
|
||||
`$` | end of string
|
||||
`\b` | word boundary
|
||||
|
||||
- How word boundary matching works:
|
||||
- At the beginning of the string if the first character is `\w`.
|
||||
- Between two adjacent characters within the string, if the first character is `\w` and the second character is `\W`.
|
||||
- At the end of the string if the last character is `\w`.
|
||||
|
||||
### Matching
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`foo\|bar` | match either `foo` or `bar`
|
||||
`foo(?=bar)` | match `foo` if it’s before `bar`
|
||||
`foo(?!bar)` | match `foo` if it’s *not* before `bar`
|
||||
`(?<=bar)foo` | match `foo` if it’s after `bar`
|
||||
`(?<!bar)foo` | match `foo` if it’s *not* after `bar`
|
||||
|
||||
### Grouping and capturing
|
||||
|
||||
Expression | Description
|
||||
:--|:--
|
||||
`(foo)` | capturing group; match and capture `foo`
|
||||
`(?:foo)` | non-capturing group; match `foo` but *without* capturing `foo`
|
||||
`(foo)bar\1` | `\1` is a backreference to the 1st capturing group; match `foobarfoo`
|
||||
|
||||
- Capturing groups are only relevant in the following methods:
|
||||
- `string.match(regexp)`
|
||||
- `string.matchAll(regexp)`
|
||||
- `string.replace(regexp, callback)`
|
||||
- `\N` is a backreference to the `Nth` capturing group. Capturing groups are numbered starting from 1.
|
||||
|
||||
## References and tools
|
||||
|
||||
- [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
|
||||
- [RegExplained](https://leaverou.github.io/regexplained/)
|
32
src/tools/regex-memo/regex-memo.vue
Normal file
32
src/tools/regex-memo/regex-memo.vue
Normal file
|
@ -0,0 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
import { useThemeVars } from 'naive-ui';
|
||||
import Memo from './regex-memo.content.md';
|
||||
|
||||
const themeVars = useThemeVars();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Memo />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
::v-deep(pre) {
|
||||
margin: 0;
|
||||
padding: 15px 22px;
|
||||
background-color: v-bind('themeVars.cardColor');
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
}
|
||||
::v-deep(table) {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
::v-deep(table), ::v-deep(td), ::v-deep(th) {
|
||||
border: 1px solid v-bind('themeVars.textColor1');
|
||||
padding: 5px;
|
||||
}
|
||||
::v-deep(a) {
|
||||
color: v-bind('themeVars.textColor1');
|
||||
}
|
||||
</style>
|
12
src/tools/regex-tester/index.ts
Normal file
12
src/tools/regex-tester/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Language } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Regex Tester',
|
||||
path: '/regex-tester',
|
||||
description: 'Test your regular expressions with sample text.',
|
||||
keywords: ['regex', 'tester', 'sample', 'expression'],
|
||||
component: () => import('./regex-tester.vue'),
|
||||
icon: Language,
|
||||
createdAt: new Date('2024-09-20'),
|
||||
});
|
106
src/tools/regex-tester/regex-tester.service.test.ts
Normal file
106
src/tools/regex-tester/regex-tester.service.test.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { matchRegex } from './regex-tester.service';
|
||||
|
||||
const regexesData = [
|
||||
{
|
||||
regex: '',
|
||||
text: '',
|
||||
flags: '',
|
||||
result: [],
|
||||
},
|
||||
{
|
||||
regex: '.*',
|
||||
text: '',
|
||||
flags: '',
|
||||
result: [],
|
||||
},
|
||||
{
|
||||
regex: '',
|
||||
text: 'aaa',
|
||||
flags: '',
|
||||
result: [],
|
||||
},
|
||||
{
|
||||
regex: 'a',
|
||||
text: 'baaa',
|
||||
flags: '',
|
||||
result: [
|
||||
{
|
||||
captures: [],
|
||||
groups: [],
|
||||
index: 1,
|
||||
value: 'a',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
regex: '(.)(?<g>r)',
|
||||
text: 'azertyr',
|
||||
flags: 'g',
|
||||
result: [
|
||||
{
|
||||
captures: [
|
||||
{
|
||||
end: 3,
|
||||
name: '1',
|
||||
start: 2,
|
||||
value: 'e',
|
||||
},
|
||||
{
|
||||
end: 4,
|
||||
name: '2',
|
||||
start: 3,
|
||||
value: 'r',
|
||||
},
|
||||
],
|
||||
groups: [
|
||||
{
|
||||
end: 4,
|
||||
name: 'g',
|
||||
start: 3,
|
||||
value: 'r',
|
||||
},
|
||||
],
|
||||
index: 2,
|
||||
value: 'er',
|
||||
},
|
||||
{
|
||||
captures: [
|
||||
{
|
||||
end: 6,
|
||||
name: '1',
|
||||
start: 5,
|
||||
value: 'y',
|
||||
},
|
||||
{
|
||||
end: 7,
|
||||
name: '2',
|
||||
start: 6,
|
||||
value: 'r',
|
||||
},
|
||||
],
|
||||
groups: [
|
||||
{
|
||||
end: 7,
|
||||
name: 'g',
|
||||
start: 6,
|
||||
value: 'r',
|
||||
},
|
||||
],
|
||||
index: 5,
|
||||
value: 'yr',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
describe('regex-tester', () => {
|
||||
for (const reg of regexesData) {
|
||||
const { regex, text, flags, result: expected_result } = reg;
|
||||
it(`Should matchRegex("${regex}","${text}","${flags}") return correct result`, async () => {
|
||||
const result = matchRegex(regex, text, `${flags}d`);
|
||||
|
||||
expect(result).to.deep.equal(expected_result);
|
||||
});
|
||||
}
|
||||
});
|
61
src/tools/regex-tester/regex-tester.service.ts
Normal file
61
src/tools/regex-tester/regex-tester.service.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
interface RegExpGroupIndices {
|
||||
[name: string]: [number, number]
|
||||
}
|
||||
interface RegExpIndices extends Array<[number, number]> {
|
||||
groups: RegExpGroupIndices
|
||||
}
|
||||
interface RegExpExecArrayWithIndices extends RegExpExecArray {
|
||||
indices: RegExpIndices
|
||||
}
|
||||
interface GroupCapture {
|
||||
name: string
|
||||
value: string
|
||||
start: number
|
||||
end: number
|
||||
};
|
||||
|
||||
export function matchRegex(regex: string, text: string, flags: string) {
|
||||
// if (regex === '' || text === '') {
|
||||
// return [];
|
||||
// }
|
||||
|
||||
let lastIndex = -1;
|
||||
const re = new RegExp(regex, flags);
|
||||
const results = [];
|
||||
let match = re.exec(text) as RegExpExecArrayWithIndices;
|
||||
while (match !== null) {
|
||||
if (re.lastIndex === lastIndex || match[0] === '') {
|
||||
break;
|
||||
}
|
||||
const indices = match.indices;
|
||||
const captures: Array<GroupCapture> = [];
|
||||
Object.entries(match).forEach(([captureName, captureValue]) => {
|
||||
if (captureName !== '0' && captureName.match(/\d+/)) {
|
||||
captures.push({
|
||||
name: captureName,
|
||||
value: captureValue,
|
||||
start: indices[Number(captureName)][0],
|
||||
end: indices[Number(captureName)][1],
|
||||
});
|
||||
}
|
||||
});
|
||||
const groups: Array<GroupCapture> = [];
|
||||
Object.entries(match.groups || {}).forEach(([groupName, groupValue]) => {
|
||||
groups.push({
|
||||
name: groupName,
|
||||
value: groupValue,
|
||||
start: indices.groups[groupName][0],
|
||||
end: indices.groups[groupName][1],
|
||||
});
|
||||
});
|
||||
results.push({
|
||||
index: match.index,
|
||||
value: match[0],
|
||||
captures,
|
||||
groups,
|
||||
});
|
||||
lastIndex = re.lastIndex;
|
||||
match = re.exec(text) as RegExpExecArrayWithIndices;
|
||||
}
|
||||
return results;
|
||||
}
|
193
src/tools/regex-tester/regex-tester.vue
Normal file
193
src/tools/regex-tester/regex-tester.vue
Normal file
|
@ -0,0 +1,193 @@
|
|||
<script setup lang="ts">
|
||||
import RandExp from 'randexp';
|
||||
import { render } from '@regexper/render';
|
||||
import type { ShadowRootExpose } from 'vue-shadow-dom';
|
||||
import { matchRegex } from './regex-tester.service';
|
||||
import { useValidation } from '@/composable/validation';
|
||||
import { useQueryParamOrStorage } from '@/composable/queryParams';
|
||||
|
||||
const regex = useQueryParamOrStorage({ name: 'regex', storageName: 'regex-tester:regex', defaultValue: '' });
|
||||
const text = ref('');
|
||||
const global = ref(true);
|
||||
const ignoreCase = ref(false);
|
||||
const multiline = ref(false);
|
||||
const dotAll = ref(true);
|
||||
const unicode = ref(true);
|
||||
const unicodeSets = ref(false);
|
||||
const visualizerSVG = ref<ShadowRootExpose>();
|
||||
|
||||
const regexValidation = useValidation({
|
||||
source: regex,
|
||||
rules: [
|
||||
{
|
||||
message: 'Invalid regex: {0}',
|
||||
validator: value => new RegExp(value),
|
||||
getErrorMessage: (value) => {
|
||||
const _ = new RegExp(value);
|
||||
return '';
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const results = computed(() => {
|
||||
let flags = 'd';
|
||||
if (global.value) {
|
||||
flags += 'g';
|
||||
}
|
||||
if (ignoreCase.value) {
|
||||
flags += 'i';
|
||||
}
|
||||
if (multiline.value) {
|
||||
flags += 'm';
|
||||
}
|
||||
if (dotAll.value) {
|
||||
flags += 's';
|
||||
}
|
||||
if (unicode.value) {
|
||||
flags += 'u';
|
||||
}
|
||||
else if (unicodeSets.value) {
|
||||
flags += 'v';
|
||||
}
|
||||
|
||||
try {
|
||||
return matchRegex(regex.value, text.value, flags);
|
||||
}
|
||||
catch (_) {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const sample = computed(() => {
|
||||
try {
|
||||
const randexp = new RandExp(new RegExp(regex.value.replace(/\(\?\<[^\>]*\>/g, '(?:')));
|
||||
return randexp.gen();
|
||||
}
|
||||
catch (_) {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(
|
||||
async () => {
|
||||
const regexValue = regex.value;
|
||||
// shadow root is required:
|
||||
// @regexper/render append a <defs><style> that broke svg transparency of icons in the whole site
|
||||
const visualizer = visualizerSVG.value?.shadow_root;
|
||||
if (visualizer) {
|
||||
while (visualizer.lastChild) {
|
||||
visualizer.removeChild(visualizer.lastChild);
|
||||
}
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
try {
|
||||
await render(regexValue, svg);
|
||||
}
|
||||
catch (_) {
|
||||
}
|
||||
visualizer.appendChild(svg);
|
||||
}
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div max-w-600px>
|
||||
<c-card title="Regex" mb-1>
|
||||
<c-input-text
|
||||
v-model:value="regex"
|
||||
label="Regex to test:"
|
||||
placeholder="Put the regex to test"
|
||||
multiline
|
||||
rows="3"
|
||||
:validation="regexValidation"
|
||||
/>
|
||||
<router-link target="_blank" to="/regex-memo" mb-1 mt-1>
|
||||
See Regular Expression Cheatsheet
|
||||
</router-link>
|
||||
<n-space>
|
||||
<n-checkbox v-model:checked="global">
|
||||
<span title="Global search">Global search. (<code>g</code>)</span>
|
||||
</n-checkbox>
|
||||
<n-checkbox v-model:checked="ignoreCase">
|
||||
<span title="Case-insensitive search">Case-insensitive search. (<code>i</code>)</span>
|
||||
</n-checkbox>
|
||||
<n-checkbox v-model:checked="multiline">
|
||||
<span title="Allows ^ and $ to match next to newline characters.">Multiline(<code>m</code>)</span>
|
||||
</n-checkbox>
|
||||
<n-checkbox v-model:checked="dotAll">
|
||||
<span title="Allows . to match newline characters.">Singleline(<code>s</code>)</span>
|
||||
</n-checkbox>
|
||||
<n-checkbox v-model:checked="unicode">
|
||||
<span title="Unicode; treat a pattern as a sequence of Unicode code points.">Unicode(<code>u</code>)</span>
|
||||
</n-checkbox>
|
||||
<n-checkbox v-model:checked="unicodeSets">
|
||||
<span title="An upgrade to the u mode with more Unicode features.">Unicode Sets (<code>v</code>)</span>
|
||||
</n-checkbox>
|
||||
</n-space>
|
||||
|
||||
<n-divider />
|
||||
|
||||
<c-input-text
|
||||
v-model:value="text"
|
||||
label="Text to match:"
|
||||
placeholder="Put the text to match"
|
||||
multiline
|
||||
rows="5"
|
||||
/>
|
||||
</c-card>
|
||||
|
||||
<c-card title="Matches" mb-1 mt-3>
|
||||
<n-table v-if="results?.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
Index in text
|
||||
</th>
|
||||
<th scope="col">
|
||||
Value
|
||||
</th>
|
||||
<th scope="col">
|
||||
Captures
|
||||
</th>
|
||||
<th scope="col">
|
||||
Groups
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="match of results" :key="match.index">
|
||||
<td>{{ match.index }}</td>
|
||||
<td>{{ match.value }}</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li v-for="capture in match.captures" :key="capture.name">
|
||||
"{{ capture.name }}" = {{ capture.value }} [{{ capture.start }} - {{ capture.end }}]
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li v-for="group in match.groups" :key="group.name">
|
||||
"{{ group.name }}" = {{ group.value }} [{{ group.start }} - {{ group.end }}]
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</n-table>
|
||||
<c-alert v-else>
|
||||
No match
|
||||
</c-alert>
|
||||
</c-card>
|
||||
|
||||
<c-card title="Sample matching text" mt-3>
|
||||
<pre style="white-space: pre-wrap; word-break: break-all;">{{ sample }}</pre>
|
||||
</c-card>
|
||||
|
||||
<c-card title="Regex Diagram" style="overflow-x: scroll;" mt-3>
|
||||
<shadow-root ref="visualizerSVG">
|
||||
 
|
||||
</shadow-root>
|
||||
</c-card>
|
||||
</div>
|
||||
</template>
|
Loading…
Add table
Add a link
Reference in a new issue