Merge branch 'main' into feat/crc-calculator

This commit is contained in:
ShareVB 2024-06-09 14:04:26 +02:00
commit 3d91a295b9
105 changed files with 1903 additions and 451 deletions

View file

@ -1,78 +1,41 @@
<script setup lang="ts">
import { useThemeVars } from 'naive-ui';
import FavoriteButton from './FavoriteButton.vue';
import { useAppTheme } from '@/ui/theme/themes';
import type { Tool } from '@/tools/tools.types';
const props = defineProps<{ tool: Tool & { category: string } }>();
const { tool } = toRefs(props);
const theme = useThemeVars();
const appTheme = useAppTheme();
</script>
<template>
<router-link :to="tool.path">
<c-card class="tool-card">
<router-link :to="tool.path" class="decoration-none">
<c-card class="h-full transition transition-duration-0.5s !border-2px !hover:border-primary">
<div flex items-center justify-between>
<n-icon class="icon" size="40" :component="tool.icon" />
<n-icon class="text-neutral-400 dark:text-neutral-600" size="40" :component="tool.icon" />
<div flex items-center gap-8px>
<n-tag
<div
v-if="tool.isNew"
size="small"
class="badge-new"
round
type="success"
:bordered="false"
:color="{ color: theme.primaryColor, textColor: theme.tagColor }"
class="rounded-full px-8px py-3px text-xs text-white dark:text-neutral-800"
:style="{
'background-color': theme.primaryColor,
}"
>
{{ $t('toolCard.new') }}
</n-tag>
</div>
<FavoriteButton :tool="tool" />
</div>
</div>
<n-h3 class="title">
<n-ellipsis>{{ tool.name }}</n-ellipsis>
</n-h3>
<div class="description">
<n-ellipsis :line-clamp="2" :tooltip="false" style="min-height: 44.78px">
{{ tool.description }}
<br>&nbsp;
</n-ellipsis>
<div class="truncat my-5px text-lg text-black dark:text-white">
{{ tool.name }}
</div>
<div class="line-clamp-2 text-neutral-500 dark:text-neutral-400">
{{ tool.description }}
</div>
</c-card>
</router-link>
</template>
<style lang="less" scoped>
a {
text-decoration: none;
}
.tool-card {
transition: border-color ease 0.5s;
border-width: 2px !important;
color: transparent;
&:hover {
border-color: v-bind('appTheme.primary.colorHover');
}
.icon {
opacity: 0.6;
color: v-bind('theme.textColorBase');
}
.title {
margin: 5px 0;
}
.description {
opacity: 0.6;
color: v-bind('theme.textColorBase');
margin: 5px 0;
}
}
</style>

View file

@ -7,7 +7,9 @@ const localesLong: Record<string, string> = {
fr: 'Français',
pt: 'Português',
ru: 'Русский',
uk: 'Українська',
zh: '中文',
vi: 'Tiếng Việt',
};
const localeOptions = computed(() =>

View file

@ -13,76 +13,60 @@ const { t } = useI18n();
</script>
<template>
<div class="home-page">
<div class="pt-50px">
<div class="grid-wrapper">
<n-grid v-if="config.showBanner" x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
<n-gi>
<ColoredCard :title="$t('home.follow.title')" :icon="Heart">
{{ $t('home.follow.p1') }}
<a
href="https://github.com/CorentinTh/it-tools"
rel="noopener"
target="_blank"
:aria-label="$t('home.follow.githubRepository')"
>GitHub</a>
{{ $t('home.follow.p2') }}
<a
href="https://twitter.com/ittoolsdottech"
rel="noopener"
target="_blank"
:aria-label="$t('home.follow.twitterAccount')"
>Twitter</a>.
{{ $t('home.follow.thankYou') }}
<n-icon :component="Heart" />
</ColoredCard>
</n-gi>
</n-grid>
<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">
{{ $t('home.follow.p1') }}
<a
href="https://github.com/CorentinTh/it-tools"
rel="noopener"
target="_blank"
:aria-label="$t('home.follow.githubRepository')"
>GitHub</a>
{{ $t('home.follow.p2') }}
<a
href="https://twitter.com/ittoolsdottech"
rel="noopener"
target="_blank"
:aria-label="$t('home.follow.twitterAccount')"
>Twitter</a>.
{{ $t('home.follow.thankYou') }}
<n-icon :component="Heart" />
</ColoredCard>
</div>
<transition name="height">
<div v-if="toolStore.favoriteTools.length > 0">
<n-h3>{{ $t('home.categories.favoriteTools') }}</n-h3>
<n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
<n-gi v-for="tool in toolStore.favoriteTools" :key="tool.name">
<ToolCard :tool="tool" />
</n-gi>
</n-grid>
<h3 class="mb-5px mt-25px font-500 text-neutral-400">
{{ $t('home.categories.favoriteTools') }}
</h3>
<div class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
<ToolCard v-for="tool in toolStore.favoriteTools" :key="tool.name" :tool="tool" />
</div>
</div>
</transition>
<div v-if="toolStore.newTools.length > 0">
<n-h3>{{ t('home.categories.newestTools') }}</n-h3>
<n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
<n-gi v-for="tool in toolStore.newTools" :key="tool.name">
<ToolCard :tool="tool" />
</n-gi>
</n-grid>
<h3 class="mb-5px mt-25px font-500 text-neutral-400">
{{ t('home.categories.newestTools') }}
</h3>
<div class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
<ToolCard v-for="tool in toolStore.newTools" :key="tool.name" :tool="tool" />
</div>
</div>
<n-h3>{{ $t('home.categories.allTools') }}</n-h3>
<n-grid x-gap="12" y-gap="12" cols="1 400:2 800:3 1200:4 2000:8">
<n-gi v-for="tool in toolStore.tools" :key="tool.name">
<transition>
<ToolCard :tool="tool" />
</transition>
</n-gi>
</n-grid>
<h3 class="mb-5px mt-25px font-500 text-neutral-400">
{{ $t('home.categories.allTools') }}
</h3>
<div class="grid grid-cols-1 gap-12px lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-4">
<ToolCard v-for="tool in toolStore.tools" :key="tool.name" :tool="tool" />
</div>
</div>
</div>
</template>
<style scoped lang="less">
.home-page {
padding-top: 50px;
}
.n-h3 {
margin-bottom: 10px;
}
::v-deep(.n-grid) {
margin-bottom: 30px;
}
.height-enter-active,
.height-leave-active {
transition: all 0.5s ease-in-out;

View file

@ -1,22 +1,7 @@
import messages from '@intlify/unplugin-vue-i18n/messages';
import { get } from '@vueuse/core';
import type { Plugin } from 'vue';
import { createI18n } from 'vue-i18n';
import baseMessages from '@intlify/unplugin-vue-i18n/messages';
import _ from 'lodash';
import { parse as parseYaml } from 'yaml';
const i18nFiles = import.meta.glob('../tools/*/locales/**.yml', { as: 'raw' });
const messagesByTools = await Promise.all(_.map(i18nFiles, async (fileDescriptor, path) => {
const [, locale] = path.match(/\.\/tools\/.*?\/locales\/(.*)\.ya?ml$/i) ?? [];
const content = parseYaml(await fileDescriptor());
return { [locale]: content };
}));
const messages = _.merge(
baseMessages,
_.merge({}, ...messagesByTools),
);
const i18n = createI18n({
legacy: false,
@ -31,7 +16,6 @@ export const i18nPlugin: Plugin = {
};
export const translate = function (localeKey: string) {
// @ts-expect-error global
const hasKey = i18n.global.te(localeKey, i18n.global.locale);
const hasKey = i18n.global.te(localeKey, get(i18n.global.locale));
return hasKey ? i18n.global.t(localeKey) : localeKey;
};

View file

@ -1,10 +1,11 @@
import { FileDigit } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Base64 file converter',
name: translate('tools.base64-file-converter.title'),
path: '/base64-file-converter',
description: 'Convert string, files or images into a it\'s base64 representation.',
description: translate('tools.base64-file-converter.description'),
keywords: ['base64', 'converter', 'upload', 'image', 'file', 'conversion', 'web', 'data', 'format'],
component: () => import('./base64-file-converter.vue'),
icon: FileDigit,

View file

@ -1,10 +1,11 @@
import { FileDigit } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Base64 string encoder/decoder',
name: translate('tools.base64-string-converter.title'),
path: '/base64-string-converter',
description: 'Simply encode and decode string into a their base64 representation.',
description: translate('tools.base64-string-converter.description'),
keywords: ['base64', 'converter', 'conversion', 'web', 'data', 'format', 'atob', 'btoa'],
component: () => import('./base64-string-converter.vue'),
icon: FileDigit,

View file

@ -1,10 +1,11 @@
import { PasswordRound } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Basic auth generator',
name: translate('tools.basic-auth-generator.title'),
path: '/basic-auth-generator',
description: 'Generate a base64 basic auth header from an username and a password.',
description: translate('tools.basic-auth-generator.description'),
keywords: [
'basic',
'auth',

View file

@ -1,11 +1,11 @@
import { LockSquare } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Bcrypt',
name: translate('tools.bcrypt.title'),
path: '/bcrypt',
description:
'Hash and compare text string using bcrypt. Bcrypt is a password-hashing function based on the Blowfish cipher.',
description: translate('tools.bcrypt.description'),
keywords: ['bcrypt', 'hash', 'compare', 'password', 'salt', 'round', 'storage', 'crypto'],
component: () => import('./bcrypt.vue'),
icon: LockSquare,

View file

@ -1,10 +1,11 @@
import { SpeedFilled } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Benchmark builder',
name: translate('tools.benchmark-builder.title'),
path: '/benchmark-builder',
description: 'Easily compare execution time of tasks with this very simple online benchmark builder.',
description: translate('tools.benchmark-builder.description'),
keywords: ['benchmark', 'builder', 'execution', 'duration', 'mean', 'variance'],
component: () => import('./benchmark-builder.vue'),
icon: SpeedFilled,

View file

@ -1,10 +1,11 @@
import { AlignJustified } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'BIP39 passphrase generator',
name: translate('tools.bip39-generator.title'),
path: '/bip39-generator',
description: 'Generate BIP39 passphrase from existing or random mnemonic, or get the mnemonic from the passphrase.',
description: translate('tools.bip39-generator.description'),
keywords: ['BIP39', 'passphrase', 'generator', 'mnemonic', 'entropy'],
component: () => import('./bip39-generator.vue'),
icon: AlignJustified,

View file

@ -1,10 +1,11 @@
import { Camera } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Camera recorder',
name: translate('tools.camera-recorder.title'),
path: '/camera-recorder',
description: 'Take a picture or record a video from your webcam or camera.',
description: translate('tools.camera-recorder.description'),
keywords: ['camera', 'recoder'],
component: () => import('./camera-recorder.vue'),
icon: Camera,

View file

@ -1,10 +1,11 @@
import { LetterCaseToggle } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Case converter',
name: translate('tools.case-converter.title'),
path: '/case-converter',
description: 'Change the case of a string and chose between different formats',
description: translate('tools.case-converter.description'),
keywords: [
'case',
'converter',

View file

@ -1,10 +1,11 @@
import { FileInvoice } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Chmod calculator',
name: translate('tools.chmod-calculator.title'),
path: '/chmod-calculator',
description: 'Compute your chmod permissions and commands with this online chmod calculator.',
description: translate('tools.chmod-calculator.description'),
keywords: [
'chmod',
'calculator',

View file

@ -1,10 +1,11 @@
import { TimerOutlined } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Chronometer',
name: translate('tools.chronometer.title'),
path: '/chronometer',
description: 'Monitor the duration of a thing. Basically a chronometer with simple chronometer features.',
description: translate('tools.chronometer.description'),
keywords: ['chronometer', 'time', 'lap', 'duration', 'measure', 'pause', 'resume', 'stopwatch'],
component: () => import('./chronometer.vue'),
icon: TimerOutlined,

View file

@ -1,10 +1,11 @@
import { Palette } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Color converter',
name: translate('tools.color-converter.title'),
path: '/color-converter',
description: 'Convert color between the different formats (hex, rgb, hsl and css name)',
description: translate('tools.color-converter.description'),
keywords: ['color', 'converter'],
component: () => import('./color-converter.vue'),
icon: Palette,

View file

@ -89,7 +89,7 @@ const CRCValues = computed(() => withDefaultOnError(() => {
watch(text,
(_, newValue) => {
if (newValue === ''){
if (newValue === '') {
return;
}

View file

@ -1,10 +1,11 @@
import { Alarm } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Crontab generator',
name: translate('tools.crontab-generator.title'),
path: '/crontab-generator',
description: 'Validate and generate crontab and get the human readable description of the cron schedule.',
description: translate('tools.crontab-generator.description'),
keywords: [
'crontab',
'generator',

View file

@ -1,10 +1,11 @@
import { Calendar } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Date-time converter',
name: translate('tools.date-converter.title'),
path: '/date-converter',
description: 'Convert date and time into the various different formats',
description: translate('tools.date-converter.description'),
keywords: ['date', 'time', 'converter', 'iso', 'utc', 'timezone', 'year', 'month', 'day', 'minute', 'seconde'],
component: () => import('./date-time-converter.vue'),
icon: Calendar,

View file

@ -1,10 +1,11 @@
import { DeviceDesktop } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Device information',
name: translate('tools.device-information.title'),
path: '/device-information',
description: 'Get information about your current device (screen size, pixel-ratio, user agent, ...)',
description: translate('tools.device-information.description'),
keywords: [
'device',
'information',

View file

@ -1,10 +1,11 @@
import { BrandDocker } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Docker run to Docker compose converter',
name: translate('tools.docker-run-to-docker-compose-converter.title'),
path: '/docker-run-to-docker-compose-converter',
description: 'Turns docker run commands into docker-compose files!',
description: translate('tools.docker-run-to-docker-compose-converter.description'),
keywords: ['docker', 'run', 'compose', 'yaml', 'yml', 'convert', 'deamon'],
component: () => import('./docker-run-to-docker-compose-converter.vue'),
icon: BrandDocker,

View file

@ -1,10 +1,11 @@
import { MoodSmile } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Emoji picker',
name: translate('tools.emoji-picker.title'),
path: '/emoji-picker',
description: 'Copy and paste emojis easily and get the unicode and code points value of each emoji.',
description: translate('tools.emoji-picker.description'),
keywords: ['emoji', 'picker', 'unicode', 'copy', 'paste'],
component: () => import('./emoji-picker.vue'),
icon: MoodSmile,

View file

@ -1,10 +1,11 @@
import { Lock } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Encrypt / decrypt text',
name: translate('tools.encryption.title'),
path: '/encryption',
description: 'Encrypt and decrypt text clear text using crypto algorithm like AES, TripleDES, Rabbit or RC4.',
description: translate('tools.encryption.description'),
keywords: ['cypher', 'encipher', 'text', 'AES', 'TripleDES', 'Rabbit', 'RC4'],
component: () => import('./encryption.vue'),
icon: Lock,

View file

@ -1,11 +1,11 @@
import { Hourglass } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'ETA calculator',
name: translate('tools.eta-calculator.title'),
path: '/eta-calculator',
description:
'An ETA (Estimated Time of Arrival) calculator to know the approximate end time of a task, for example the moment of ending of a download.',
description: translate('tools.eta-calculator.description'),
keywords: ['eta', 'calculator', 'estimated', 'time', 'arrival', 'average'],
component: () => import('./eta-calculator.vue'),
icon: Hourglass,

View file

@ -1,11 +1,11 @@
import { BrandGit } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Git cheatsheet',
name: translate('tools.git-memo.title'),
path: '/git-memo',
description:
'Git is a decentralized version management software. With this cheatsheet you will have a quick access to the most common git commands.',
description: translate('tools.git-memo.description'),
keywords: ['git', 'push', 'force', 'pull', 'commit', 'amend', 'rebase', 'merge', 'reset', 'soft', 'hard', 'lease'],
component: () => import('./git-memo.vue'),
icon: BrandGit,

View file

@ -1,11 +1,11 @@
import { EyeOff } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Hash text',
name: translate('tools.hash-text.title'),
path: '/hash-text',
description:
'Hash a text string using the function you need : MD5, SHA1, SHA256, SHA224, SHA512, SHA384, SHA3 or RIPEMD160',
description: translate('tools.hash-text.description'),
keywords: [
'hash',
'digest',

View file

@ -1,11 +1,11 @@
import { ShortTextRound } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Hmac generator',
name: translate('tools.hmac-generator.title'),
path: '/hmac-generator',
description:
'Computes a hash-based message authentication code (HMAC) using a secret key and your favorite hashing function.',
description: translate('tools.hmac-generator.description'),
keywords: ['hmac', 'generator', 'MD5', 'SHA1', 'SHA256', 'SHA224', 'SHA512', 'SHA384', 'SHA3', 'RIPEMD160'],
component: () => import('./hmac-generator.vue'),
icon: ShortTextRound,

View file

@ -1,10 +1,11 @@
import { Code } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Escape html entities',
name: translate('tools.html-entities.title'),
path: '/html-entities',
description: 'Escape or unescape html entities (replace <,>, &, " and \' to their html version)',
description: translate('tools.html-entities.description'),
keywords: ['html', 'entities', 'escape', 'unescape', 'special', 'characters', 'tags'],
component: () => import('./html-entities.vue'),
icon: Code,

View file

@ -1,10 +1,11 @@
import { Edit } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'HTML WYSIWYG editor',
name: translate('tools.html-wysiwyg-editor.title'),
path: '/html-wysiwyg-editor',
description: 'Online HTML editor with feature-rich WYSIWYG editor, get the source code of the content immediately.',
description: translate('tools.html-wysiwyg-editor.description'),
keywords: ['html', 'wysiwyg', 'editor', 'p', 'ul', 'ol', 'converter', 'live'],
component: () => import('./html-wysiwyg-editor.vue'),
icon: Edit,

View file

@ -2,11 +2,12 @@ import { HttpRound } from '@vicons/material';
import { defineTool } from '../tool';
import { codesByCategories } from './http-status-codes.constants';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'HTTP status codes',
name: translate('tools.http-status-codes.title'),
path: '/http-status-codes',
description: 'The list of all HTTP status codes their name and their meaning.',
description: translate('tools.http-status-codes.description'),
keywords: [
'http',
'status',

View file

@ -1,10 +1,11 @@
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
import Bank from '~icons/mdi/bank';
export const tool = defineTool({
name: 'IBAN validator and parser',
name: translate('tools.iban-validator-and-parser.title'),
path: '/iban-validator-and-parser',
description: 'Validate and parse IBAN numbers. Check if IBAN is valid and get the country, BBAN, if it is a QR-IBAN and the IBAN friendly format.',
description: translate('tools.iban-validator-and-parser.description'),
keywords: ['iban', 'validator', 'and', 'parser', 'bic', 'bank'],
component: () => import('./iban-validator-and-parser.vue'),
icon: Bank,

View file

@ -1,6 +1,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 textToUnicode } from './text-to-unicode';
import { tool as crcCalculator } from './crc-calculator';
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
import { tool as numeronymGenerator } from './numeronym-generator';
@ -76,6 +77,7 @@ import { tool as urlParser } from './url-parser';
import { tool as uuidGenerator } from './uuid-generator';
import { tool as macAddressLookup } from './mac-address-lookup';
import { tool as xmlFormatter } from './xml-formatter';
import { tool as yamlViewer } from './yaml-viewer';
export const toolsByCategory: ToolCategory[] = [
{
@ -107,6 +109,7 @@ export const toolsByCategory: ToolCategory[] = [
caseConverter,
textToNatoAlphabet,
textToBinary,
textToUnicode,
yamlToJson,
yamlToToml,
jsonToYaml,
@ -153,6 +156,7 @@ export const toolsByCategory: ToolCategory[] = [
chmodCalculator,
dockerRunToDockerComposeConverter,
xmlFormatter,
yamlViewer,
],
},
{

View file

@ -1,10 +1,11 @@
import { ArrowsLeftRight } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Integer base converter',
name: translate('tools.base-converter.title'),
path: '/base-converter',
description: 'Convert number between different bases (decimal, hexadecimal, binary, octal, base64, ...)',
description: translate('tools.base-converter.description'),
keywords: ['integer', 'number', 'base', 'conversion', 'decimal', 'hexadecimal', 'binary', 'octal', 'base64'],
component: () => import('./integer-base-converter.vue'),
icon: ArrowsLeftRight,

View file

@ -1,10 +1,11 @@
import { Binary } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Ipv4 address converter',
name: translate('tools.ipv4-address-converter.title'),
path: '/ipv4-address-converter',
description: 'Convert an ip address into decimal, binary, hexadecimal or event in ipv6',
description: translate('tools.ipv4-address-converter.description'),
keywords: ['ipv4', 'address', 'converter', 'decimal', 'hexadecimal', 'binary', 'ipv6'],
component: () => import('./ipv4-address-converter.vue'),
icon: Binary,

View file

@ -1,11 +1,11 @@
import { UnfoldMoreOutlined } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'IPv4 range expander',
name: translate('tools.ipv4-range-expander.title'),
path: '/ipv4-range-expander',
description:
'Given a start and an end IPv4 address this tool calculates a valid IPv4 network with its CIDR notation.',
description: translate('tools.ipv4-range-expander.description'),
keywords: ['ipv4', 'range', 'expander', 'subnet', 'creator', 'cidr'],
component: () => import('./ipv4-range-expander.vue'),
icon: UnfoldMoreOutlined,

View file

@ -1,10 +1,11 @@
import { RouterOutlined } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'IPv4 subnet calculator',
name: translate('tools.ipv4-subnet-calculator.title'),
path: '/ipv4-subnet-calculator',
description: 'Parse your IPv4 CIDR blocks and get all the info you need about your sub network.',
description: translate('tools.ipv4-subnet-calculator.description'),
keywords: ['ipv4', 'subnet', 'calculator', 'mask', 'network', 'cidr', 'netmask', 'bitmask', 'broadcast', 'address'],
component: () => import('./ipv4-subnet-calculator.vue'),
icon: RouterOutlined,

View file

@ -1,10 +1,11 @@
import { BuildingFactory } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'IPv6 ULA generator',
name: translate('tools.ipv6-ula-generator.title'),
path: '/ipv6-ula-generator',
description: 'Generate your own local, non-routable IP addresses on your network according to RFC4193.',
description: translate('tools.ipv6-ula-generator.description'),
keywords: ['ipv6', 'ula', 'generator', 'rfc4193', 'network', 'private'],
component: () => import('./ipv6-ula-generator.vue'),
icon: BuildingFactory,

View file

@ -1,10 +1,11 @@
import { CompareArrowsRound } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'JSON diff',
name: translate('tools.json-diff.title'),
path: '/json-diff',
description: 'Compare two JSON objects and get the differences between them.',
description: translate('tools.json-diff.description'),
keywords: ['json', 'diff', 'compare', 'difference', 'object', 'data'],
component: () => import('./json-diff.vue'),
icon: CompareArrowsRound,

View file

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

View file

@ -1,10 +1,11 @@
import { List } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'JSON to CSV',
name: translate('tools.json-to-csv.title'),
path: '/json-to-csv',
description: 'Convert JSON to CSV with automatic header detection.',
description: translate('tools.json-to-csv.description'),
keywords: ['json', 'to', 'csv', 'convert'],
component: () => import('./json-to-csv.vue'),
icon: List,

View file

@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'JSON to TOML',
name: translate('tools.json-to-toml.title'),
path: '/json-to-toml',
description: 'Parse and convert JSON to TOML.',
description: translate('tools.json-to-toml.description'),
keywords: ['json', 'parse', 'toml', 'convert', 'transform'],
component: () => import('./json-to-toml.vue'),
icon: Braces,

View file

@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'JSON to YAML converter',
name: translate('tools.json-to-yaml-converter.title'),
path: '/json-to-yaml-converter',
description: 'Simply convert JSON to YAML with this live online converter.',
description: translate('tools.json-to-yaml-converter.description'),
keywords: ['yaml', 'to', 'json'],
component: () => import('./json-to-yaml.vue'),
icon: Braces,

View file

@ -1,10 +1,11 @@
import { Braces } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'JSON prettify and format',
name: translate('tools.json-prettify.title'),
path: '/json-prettify',
description: 'Prettify your JSON string to a human friendly readable format.',
description: translate('tools.json-prettify.description'),
keywords: ['json', 'viewer', 'prettify', 'format'],
component: () => import('./json-viewer.vue'),
icon: Braces,

View file

@ -1,10 +1,11 @@
import { Key } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'JWT parser',
name: translate('tools.jwt-parser.title'),
path: '/jwt-parser',
description: 'Parse and decode your JSON Web Token (jwt) and display its content.',
description: translate('tools.jwt-parser.description'),
keywords: [
'jwt',
'parser',

View file

@ -19,7 +19,7 @@ function decodeJwt({ jwt }: { jwt: string }) {
function parseClaims({ claim, value }: { claim: string; value: unknown }) {
const claimDescription = CLAIM_DESCRIPTIONS[claim];
const formattedValue = _.isPlainObject(value) ? JSON.stringify(value, null, 3) : _.toString(value);
const formattedValue = _.isPlainObject(value) || _.isArray(value) ? JSON.stringify(value, null, 3) : _.toString(value);
const friendlyValue = getFriendlyValue({ claim, value });
return {

View file

@ -1,10 +1,11 @@
import { Keyboard } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Keycode info',
name: translate('tools.keycode-info.title'),
path: '/keycode-info',
description: 'Find the javascript keycode, code, location and modifiers of any pressed key.',
description: translate('tools.keycode-info.description'),
keywords: [
'keycode',
'info',

View file

@ -1,11 +1,11 @@
import { List } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'List converter',
name: translate('tools.list-converter.title'),
path: '/list-converter',
description:
'This tool can process column-based data and apply various changes (transpose, add prefix and suffix, reverse list, sort list, lowercase values, truncate values) to each row.',
description: translate('tools.list-converter.description'),
keywords: ['list', 'converter', 'sort', 'reverse', 'prefix', 'suffix', 'lowercase', 'truncate'],
component: () => import('./list-converter.vue'),
icon: List,

View file

@ -1,11 +1,11 @@
import { AlignJustified } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Lorem ipsum generator',
name: translate('tools.lorem-ipsum-generator.title'),
path: '/lorem-ipsum-generator',
description:
'Lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document or a typeface without relying on meaningful content',
description: translate('tools.lorem-ipsum-generator.description'),
keywords: ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'placeholder', 'text', 'filler', 'random', 'generator'],
component: () => import('./lorem-ipsum-generator.vue'),
icon: AlignJustified,

View file

@ -1,10 +1,11 @@
import { Devices } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'MAC address generator',
name: translate('tools.mac-address-generator.title'),
path: '/mac-address-generator',
description: 'Enter the quantity and prefix. MAC addresses will be generated in your chosen case (uppercase or lowercase)',
description: translate('tools.mac-address-generator.description'),
keywords: ['mac', 'address', 'generator', 'random', 'prefix'],
component: () => import('./mac-address-generator.vue'),
icon: Devices,

View file

@ -1,10 +1,11 @@
import { Devices } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'MAC address lookup',
name: translate('tools.mac-address-lookup.title'),
path: '/mac-address-lookup',
description: 'Find the vendor and manufacturer of a device by its MAC address.',
description: translate('tools.mac-address-lookup.description'),
keywords: ['mac', 'address', 'lookup', 'vendor', 'parser', 'manufacturer'],
component: () => import('./mac-address-lookup.vue'),
icon: Devices,

View file

@ -1,10 +1,11 @@
import { Math } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Math evaluator',
name: translate('tools.math-evaluator.title'),
path: '/math-evaluator',
description: 'A calculator for evaluating mathematical expressions. You can use functions like sqrt, cos, sin, abs, etc.',
description: translate('tools.math-evaluator.description'),
keywords: [
'math',
'evaluator',

View file

@ -1,10 +1,11 @@
import { Tags } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Open graph meta generator',
name: translate('tools.og-meta-generator.title'),
path: '/og-meta-generator',
description: 'Generate open-graph and socials html meta tags for your website.',
description: translate('tools.og-meta-generator.description'),
keywords: [
'meta',
'tag',

View file

@ -1,10 +1,11 @@
import { World } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Mime types',
name: translate('tools.mime-types.title'),
path: '/mime-types',
description: 'Convert mime types to extensions and vice-versa.',
description: translate('tools.mime-types.description'),
keywords: ['mime', 'types', 'extension', 'content', 'type'],
component: () => import('./mime-types.vue'),
icon: World,

View file

@ -1,10 +1,11 @@
import { defineTool } from '../tool';
import n7mIcon from './n7m-icon.svg?component';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Numeronym generator',
name: translate('tools.numeronym-generator.title'),
path: '/numeronym-generator',
description: 'A numeronym is a word where a number is used to form an abbreviation. For example, "i18n" is a numeronym of "internationalization" where 18 stands for the number of letters between the first i and the last n in the word.',
description: translate('tools.numeronym-generator.description'),
keywords: ['numeronym', 'generator', 'abbreviation', 'i18n', 'a11y', 'l10n'],
component: () => import('./numeronym-generator.vue'),
icon: n7mIcon,

View file

@ -1,10 +1,11 @@
import { DeviceMobile } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'OTP code generator',
name: translate('tools.otp-generator.title'),
path: '/otp-generator',
description: 'Generate and validate time-based OTP (one time password) for multi-factor authentication.',
description: translate('tools.otp-generator.description'),
keywords: [
'otp',
'code',

View file

@ -1,10 +1,11 @@
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
import PasswordIcon from '~icons/mdi/form-textbox-password';
export const tool = defineTool({
name: 'Password strength analyser',
name: translate('tools.password-strength-analyser.title'),
path: '/password-strength-analyser',
description: 'Discover the strength of your password with this client side only password strength analyser and crack time estimation tool.',
description: translate('tools.password-strength-analyser.description'),
keywords: ['password', 'strength', 'analyser', 'and', 'crack', 'time', 'estimation', 'brute', 'force', 'attack', 'entropy', 'cracking', 'hash', 'hashing', 'algorithm', 'algorithms', 'md5', 'sha1', 'sha256', 'sha512', 'bcrypt', 'scrypt', 'argon2', 'argon2id', 'argon2i', 'argon2d'],
component: () => import('./password-strength-analyser.vue'),
icon: PasswordIcon,

View file

@ -1,10 +1,11 @@
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
import FileCertIcon from '~icons/mdi/file-certificate-outline';
export const tool = defineTool({
name: 'PDF signature checker',
name: translate('tools.pdf-signature-checker.title'),
path: '/pdf-signature-checker',
description: 'Verify the signatures of a PDF file. A signed PDF file contains one or more signatures that may be used to determine whether the contents of the file have been altered since the file was signed.',
description: translate('tools.pdf-signature-checker.description'),
keywords: ['pdf', 'signature', 'checker', 'verify', 'validate', 'sign'],
component: () => import('./pdf-signature-checker.vue'),
icon: FileCertIcon,

View file

@ -1,10 +1,11 @@
import { Percentage } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Percentage calculator',
name: translate('tools.percentage-calculator.title'),
path: '/percentage-calculator',
description: 'Easily calculate percentages from a value to another value, or from a percentage to a value.',
description: translate('tools.percentage-calculator.description'),
keywords: ['percentage', 'calculator', 'calculate', 'value', 'number', '%'],
component: () => import('./percentage-calculator.vue'),
icon: Percentage,

View file

@ -1,11 +1,11 @@
import { Phone } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Phone parser and formatter',
name: translate('tools.phone-parser-and-formatter.title'),
path: '/phone-parser-and-formatter',
description:
'Parse, validate and format phone numbers. Get information about the phone number, like the country code, type, etc.',
description: translate('tools.phone-parser-and-formatter.description'),
keywords: [
'phone',
'parser',

View file

@ -1,11 +1,11 @@
import { Qrcode } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'QR Code generator',
name: translate('tools.qrcode-generator.title'),
path: '/qrcode-generator',
description:
'Generate and download QR-code for an url or just a text and customize the background and foreground colors.',
description: translate('tools.qrcode-generator.description'),
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent'],
component: () => import('./qr-code-generator.vue'),
icon: Qrcode,

View file

@ -1,10 +1,11 @@
import { Server } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Random port generator',
name: translate('tools.random-port-generator.title'),
path: '/random-port-generator',
description: 'Generate random port numbers outside of the range of "known" ports (0-1023).',
description: translate('tools.random-port-generator.description'),
keywords: ['system', 'port', 'lan', 'generator', 'random', 'development', 'computer'],
component: () => import('./random-port-generator.vue'),
icon: Server,

View file

@ -1,10 +1,11 @@
import { LetterX } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Roman numeral converter',
name: translate('tools.roman-numeral-converter.title'),
path: '/roman-numeral-converter',
description: 'Convert Roman numerals to numbers and convert numbers to Roman numerals.',
description: translate('tools.roman-numeral-converter.description'),
keywords: ['roman', 'arabic', 'converter', 'X', 'I', 'V', 'L', 'C', 'D', 'M'],
component: () => import('./roman-numeral-converter.vue'),
icon: LetterX,

View file

@ -1,10 +1,11 @@
import { Certificate } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'RSA key pair generator',
name: translate('tools.rsa-key-pair-generator.title'),
path: '/rsa-key-pair-generator',
description: 'Generate new random RSA private and public key pem certificates.',
description: translate('tools.rsa-key-pair-generator.description'),
keywords: ['rsa', 'key', 'pair', 'generator', 'public', 'private', 'secret', 'ssh', 'pem'],
component: () => import('./rsa-key-pair-generator.vue'),
icon: Certificate,

View file

@ -1,10 +1,11 @@
import { AbcRound } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Slugify string',
name: translate('tools.slugify-string.title'),
path: '/slugify-string',
description: 'Make a string url, filename and id safe.',
description: translate('tools.slugify-string.description'),
keywords: ['slugify', 'string', 'escape', 'emoji', 'special', 'character', 'space', 'trim'],
component: () => import('./slugify-string.vue'),
icon: AbcRound,

View file

@ -1,10 +1,11 @@
import { Database } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'SQL prettify and format',
name: translate('tools.sql-prettify.title'),
path: '/sql-prettify',
description: 'Format and prettify your SQL queries online (it supports various SQL dialects).',
description: translate('tools.sql-prettify.description'),
keywords: [
'sql',
'prettify',

View file

@ -1,10 +1,11 @@
import { EyeOff } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'String obfuscator',
name: translate('tools.string-obfuscator.title'),
path: '/string-obfuscator',
description: 'Obfuscate a string (like a secret, an IBAN, or a token) to make it shareable and identifiable without revealing its content.',
description: translate('tools.string-obfuscator.description'),
keywords: ['string', 'obfuscator', 'secret', 'token', 'hide', 'obscure', 'mask', 'masking'],
component: () => import('./string-obfuscator.vue'),
icon: EyeOff,

View file

@ -1,10 +1,11 @@
import { ImageOutlined } from '@vicons/material';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'SVG placeholder generator',
name: translate('tools.svg-placeholder-generator.title'),
path: '/svg-placeholder-generator',
description: 'Generate svg images to use as placeholder in your applications.',
description: translate('tools.svg-placeholder-generator.description'),
keywords: ['svg', 'placeholder', 'generator', 'image', 'size', 'mockup'],
component: () => import('./svg-placeholder-generator.vue'),
icon: ImageOutlined,

View file

@ -1,11 +1,11 @@
import { Temperature } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Temperature converter',
name: translate('tools.temperature-converter.title'),
path: '/temperature-converter',
description:
'Temperature degrees conversions for Kelvin, Celsius, Fahrenheit, Rankine, Delisle, Newton, Réaumur and Rømer.',
description: translate('tools.temperature-converter.description'),
keywords: [
'temperature',
'converter',

View file

@ -1,10 +1,11 @@
import { FileDiff } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Text diff',
name: translate('tools.text-diff.title'),
path: '/text-diff',
description: 'Compare two texts and see the differences between them.',
description: translate('tools.text-diff.description'),
keywords: ['text', 'diff', 'compare', 'string', 'text diff', 'code'],
component: () => import('./text-diff.vue'),
icon: FileDiff,

View file

@ -1,10 +1,11 @@
import { FileText } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Text statistics',
name: translate('tools.text-statistics.title'),
path: '/text-statistics',
description: 'Get information about a text, the amount of characters, the amount of words, it\'s size, ...',
description: translate('tools.text-statistics.description'),
keywords: ['text', 'statistics', 'length', 'characters', 'count', 'size', 'bytes'],
component: () => import('./text-statistics.vue'),
icon: FileText,

View file

@ -1,10 +1,11 @@
import { Binary } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Text to ASCII binary',
name: translate('tools.text-to-binary.title'),
path: '/text-to-binary',
description: 'Convert text to its ASCII binary representation and vice versa.',
description: translate('tools.text-to-binary.description'),
keywords: ['text', 'to', 'binary', 'converter', 'encode', 'decode', 'ascii'],
component: () => import('./text-to-binary.vue'),
icon: Binary,

View file

@ -1,10 +1,11 @@
import { Speakerphone } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Text to NATO alphabet',
name: translate('tools.text-to-nato-alphabet.title'),
path: '/text-to-nato-alphabet',
description: 'Transform text into NATO phonetic alphabet for oral transmission.',
description: translate('tools.text-to-nato-alphabet.description'),
keywords: ['string', 'nato', 'alphabet', 'phonetic', 'oral', 'transmission'],
component: () => import('./text-to-nato-alphabet.vue'),
icon: Speakerphone,

View file

@ -0,0 +1,13 @@
import { TextWrap } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: translate('tools.text-to-unicode.title'),
path: '/text-to-unicode',
description: translate('tools.text-to-unicode.description'),
keywords: ['text', 'to', 'unicode'],
component: () => import('./text-to-unicode.vue'),
icon: TextWrap,
createdAt: new Date('2024-01-31'),
});

View file

@ -0,0 +1,25 @@
import { expect, test } from '@playwright/test';
test.describe('Tool - Text to Unicode', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/text-to-unicode');
});
test('Has correct title', async ({ page }) => {
await expect(page).toHaveTitle('Text to Unicode - IT Tools');
});
test('Text to unicode conversion', async ({ page }) => {
await page.getByTestId('text-to-unicode-input').fill('it-tools');
const unicode = await page.getByTestId('text-to-unicode-output').inputValue();
expect(unicode).toEqual('&#105;&#116;&#45;&#116;&#111;&#111;&#108;&#115;');
});
test('Unicode to text conversion', async ({ page }) => {
await page.getByTestId('unicode-to-text-input').fill('&#105;&#116;&#45;&#116;&#111;&#111;&#108;&#115;');
const text = await page.getByTestId('unicode-to-text-output').inputValue();
expect(text).toEqual('it-tools');
});
});

View file

@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest';
import { convertTextToUnicode, convertUnicodeToText } from './text-to-unicode.service';
describe('text-to-unicode', () => {
describe('convertTextToUnicode', () => {
it('a text string is converted to unicode representation', () => {
expect(convertTextToUnicode('A')).toBe('&#65;');
expect(convertTextToUnicode('linke the string convert to unicode')).toBe('&#108;&#105;&#110;&#107;&#101;&#32;&#116;&#104;&#101;&#32;&#115;&#116;&#114;&#105;&#110;&#103;&#32;&#99;&#111;&#110;&#118;&#101;&#114;&#116;&#32;&#116;&#111;&#32;&#117;&#110;&#105;&#99;&#111;&#100;&#101;');
expect(convertTextToUnicode('')).toBe('');
});
});
describe('convertUnicodeToText', () => {
it('an unicode string is converted to its text representation', () => {
expect(convertUnicodeToText('&#65;')).toBe('A');
expect(convertUnicodeToText('&#108;&#105;&#110;&#107;&#101;&#32;&#116;&#104;&#101;&#32;&#115;&#116;&#114;&#105;&#110;&#103;&#32;&#99;&#111;&#110;&#118;&#101;&#114;&#116;&#32;&#116;&#111;&#32;&#117;&#110;&#105;&#99;&#111;&#100;&#101;')).toBe('linke the string convert to unicode');
expect(convertUnicodeToText('')).toBe('');
});
});
});

View file

@ -0,0 +1,9 @@
function convertTextToUnicode(text: string): string {
return text.split('').map(value => `&#${value.charCodeAt(0)};`).join('');
}
function convertUnicodeToText(unicodeStr: string): string {
return unicodeStr.replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec));
}
export { convertTextToUnicode, convertUnicodeToText };

View file

@ -0,0 +1,34 @@
<script setup lang="ts">
import { convertTextToUnicode, convertUnicodeToText } from './text-to-unicode.service';
import { useCopy } from '@/composable/copy';
const inputText = ref('');
const unicodeFromText = computed(() => inputText.value.trim() === '' ? '' : convertTextToUnicode(inputText.value));
const { copy: copyUnicode } = useCopy({ source: unicodeFromText });
const inputUnicode = ref('');
const textFromUnicode = computed(() => inputUnicode.value.trim() === '' ? '' : convertUnicodeToText(inputUnicode.value));
const { copy: copyText } = useCopy({ source: textFromUnicode });
</script>
<template>
<c-card title="Text to Unicode">
<c-input-text v-model:value="inputText" multiline placeholder="e.g. 'Hello Avengers'" label="Enter text to convert to unicode" autosize autofocus raw-text test-id="text-to-unicode-input" />
<c-input-text v-model:value="unicodeFromText" label="Unicode from your text" multiline raw-text readonly mt-2 placeholder="The unicode representation of your text will be here" test-id="text-to-unicode-output" />
<div mt-2 flex justify-center>
<c-button :disabled="!unicodeFromText" @click="copyUnicode()">
Copy unicode to clipboard
</c-button>
</div>
</c-card>
<c-card title="Unicode to Text">
<c-input-text v-model:value="inputUnicode" multiline placeholder="Input Unicode" label="Enter unicode to convert to text" autosize raw-text test-id="unicode-to-text-input" />
<c-input-text v-model:value="textFromUnicode" label="Text from your Unicode" multiline raw-text readonly mt-2 placeholder="The text representation of your unicode will be here" test-id="unicode-to-text-output" />
<div mt-2 flex justify-center>
<c-button :disabled="!textFromUnicode" @click="copyText()">
Copy text to clipboard
</c-button>
</div>
</c-card>
</template>

View file

@ -1,15 +0,0 @@
tools:
token-generator:
title: Token generator
description: Generate random string with the chars you want, uppercase or lowercase letters, numbers and/or symbols.
uppercase: Uppercase (ABC...)
lowercase: Lowercase (abc...)
numbers: Numbers (123...)
symbols: Symbols (!-;...)
length: Length
tokenPlaceholder: 'The token...'
copied: Token copied to the clipboard
button:
copy: Copy
refresh: Refresh

View file

@ -1,16 +0,0 @@
tools:
token-generator:
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.
uppercase: Majuscules (ABC...)
lowercase: Minuscules (abc...)
numbers: Chiffres (123...)
symbols: Symboles (!-;...)
button:
copy: Copier
refresh: Rafraichir
copied: Le token a été copié
length: Longueur
tokenPlaceholder: Le token...

View file

@ -1,11 +1,12 @@
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
import BracketIcon from '~icons/mdi/code-brackets';
export const tool = defineTool({
name: 'TOML to JSON',
name: translate('tools.toml-to-json.title'),
path: '/toml-to-json',
description: 'Parse and convert TOML to JSON.',
description: translate('tools.toml-to-json.description'),
keywords: ['toml', 'json', 'convert', 'online', 'transform', 'parser'],
component: () => import('./toml-to-json.vue'),
icon: BracketIcon,

View file

@ -1,10 +1,11 @@
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
import BracketIcon from '~icons/mdi/code-brackets';
export const tool = defineTool({
name: 'TOML to YAML',
name: translate('tools.toml-to-yaml.title'),
path: '/toml-to-yaml',
description: 'Parse and convert TOML to YAML.',
description: translate('tools.toml-to-yaml.description'),
keywords: ['toml', 'yaml', 'convert', 'online', 'transform', 'parse'],
component: () => import('./toml-to-yaml.vue'),
icon: BracketIcon,

View file

@ -1,10 +1,11 @@
import { SortDescendingNumbers } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'ULID generator',
name: translate('tools.ulid-generator.title'),
path: '/ulid-generator',
description: 'Generate random Universally Unique Lexicographically Sortable Identifier (ULID).',
description: translate('tools.ulid-generator.description'),
keywords: ['ulid', 'generator', 'random', 'id', 'alphanumeric', 'identity', 'token', 'string', 'identifier', 'unique'],
component: () => import('./ulid-generator.vue'),
icon: SortDescendingNumbers,

View file

@ -1,10 +1,11 @@
import { Link } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Encode/decode url formatted strings',
name: translate('tools.url-encoder.title'),
path: '/url-encoder',
description: 'Encode to url-encoded format (also known as "percent-encoded") or decode from it.',
description: translate('tools.url-encoder.description'),
keywords: ['url', 'encode', 'decode', 'percent', '%20', 'format'],
component: () => import('./url-encoder.vue'),
icon: Link,

View file

@ -1,11 +1,11 @@
import { Unlink } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'Url parser',
name: translate('tools.url-parser.title'),
path: '/url-parser',
description:
'Parse an url string to get all the different parts (protocol, origin, params, port, username-password, ...)',
description: translate('tools.url-parser.description'),
keywords: ['url', 'parser', 'protocol', 'origin', 'params', 'port', 'username', 'password', 'href'],
component: () => import('./url-parser.vue'),
icon: Unlink,

View file

@ -1,10 +1,11 @@
import { Browser } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'User-agent parser',
name: translate('tools.user-agent-parser.title'),
path: '/user-agent-parser',
description: 'Detect and parse Browser, Engine, OS, CPU, and Device type/model from an user-agent string.',
description: translate('tools.user-agent-parser.description'),
keywords: ['user', 'agent', 'parser', 'browser', 'engine', 'os', 'cpu', 'device', 'user-agent', 'client'],
component: () => import('./user-agent-parser.vue'),
icon: Browser,

View file

@ -1,11 +1,11 @@
import { Fingerprint } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'UUIDs generator',
name: translate('tools.uuid-generator.title'),
path: '/uuid-generator',
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!).',
description: translate('tools.uuid-generator.description'),
keywords: ['uuid', 'v4', 'random', 'id', 'alphanumeric', 'identity', 'token', 'string', 'identifier', 'unique', 'v1', 'v3', 'v5', 'nil'],
component: () => import('./uuid-generator.vue'),
icon: Fingerprint,

View file

@ -1,11 +1,11 @@
import { Qrcode } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'WiFi QR Code generator',
name: translate('tools.wifi-qrcode-generator.title'),
path: '/wifi-qrcode-generator',
description:
'Generate and download QR-codes for quick connections to WiFi networks.',
description: translate('tools.wifi-qrcode-generator.description'),
keywords: ['qr', 'code', 'generator', 'square', 'color', 'link', 'low', 'medium', 'quartile', 'high', 'transparent', 'wifi'],
component: () => import('./wifi-qr-code-generator.vue'),
icon: Qrcode,

View file

@ -1,10 +1,11 @@
import { Code } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'XML formatter',
name: translate('tools.xml-formatter.title'),
path: '/xml-formatter',
description: 'Prettify your XML string to a human friendly readable format.',
description: translate('tools.xml-formatter.description'),
keywords: ['xml', 'prettify', 'format'],
component: () => import('./xml-formatter.vue'),
icon: Code,

View file

@ -1,10 +1,11 @@
import { AlignJustified } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'YAML to JSON converter',
name: translate('tools.yaml-to-json-converter.title'),
path: '/yaml-to-json-converter',
description: 'Simply convert YAML to JSON with this live online converter.',
description: translate('tools.yaml-to-json-converter.description'),
keywords: ['yaml', 'to', 'json'],
component: () => import('./yaml-to-json.vue'),
icon: AlignJustified,

View file

@ -1,10 +1,11 @@
import { AlignJustified } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: 'YAML to TOML',
name: translate('tools.yaml-to-toml.title'),
path: '/yaml-to-toml',
description: 'Parse and convert YAML to TOML.',
description: translate('tools.yaml-to-toml.description'),
keywords: ['yaml', 'to', 'toml', 'convert', 'transform'],
component: () => import('./yaml-to-toml.vue'),
icon: AlignJustified,

View file

@ -0,0 +1,13 @@
import { AlignJustified } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: translate('tools.yaml-prettify.title'),
path: '/yaml-prettify',
description: translate('tools.yaml-prettify.description'),
keywords: ['yaml', 'viewer', 'prettify', 'format'],
component: () => import('./yaml-viewer.vue'),
icon: AlignJustified,
createdAt: new Date('2024-01-31'),
});

View file

@ -0,0 +1,24 @@
import { type MaybeRef, get } from '@vueuse/core';
import yaml from 'yaml';
export { formatYaml };
function formatYaml({
rawYaml,
sortKeys = false,
indentSize = 2,
}: {
rawYaml: MaybeRef<string>
sortKeys?: MaybeRef<boolean>
indentSize?: MaybeRef<number>
}) {
const parsedYaml = yaml.parse(get(rawYaml));
const formattedYAML = yaml.stringify(parsedYaml, {
sortMapEntries: get(sortKeys),
indent: get(indentSize),
});
return formattedYAML;
}

View file

@ -0,0 +1,72 @@
<script setup lang="ts">
import yaml from 'yaml';
import { useStorage } from '@vueuse/core';
import { formatYaml } from './yaml-models';
import { withDefaultOnError } from '@/utils/defaults';
import { useValidation } from '@/composable/validation';
import TextareaCopyable from '@/components/TextareaCopyable.vue';
const inputElement = ref<HTMLElement>();
const rawYaml = useStorage('yaml-prettify:raw-yaml', '');
const indentSize = useStorage('yaml-prettify:indent-size', 2);
const sortKeys = useStorage('yaml-prettify:sort-keys', false);
const cleanYaml = computed(() => withDefaultOnError(() => formatYaml({ rawYaml, indentSize, sortKeys }), ''));
const rawYamlValidation = useValidation({
source: rawYaml,
rules: [
{
validator: v => v === '' || yaml.parse(v),
message: 'Provided YAML is not valid.',
},
],
});
</script>
<template>
<div style="flex: 0 0 100%">
<div style="margin: 0 auto; max-width: 600px" flex justify-center gap-3>
<n-form-item label="Sort keys :" label-placement="left" label-width="100">
<n-switch v-model:value="sortKeys" />
</n-form-item>
<n-form-item label="Indent size :" label-placement="left" label-width="100" :show-feedback="false">
<n-input-number v-model:value="indentSize" min="1" max="10" style="width: 100px" />
</n-form-item>
</div>
</div>
<n-form-item
label="Your raw YAML"
:feedback="rawYamlValidation.message"
:validation-status="rawYamlValidation.status"
>
<c-input-text
ref="inputElement"
v-model:value="rawYaml"
placeholder="Paste your raw YAML here..."
rows="20"
multiline
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
monospace
/>
</n-form-item>
<n-form-item label="Prettified version of your YAML">
<TextareaCopyable :value="cleanYaml" language="yaml" :follow-height-of="inputElement" />
</n-form-item>
</template>
<style lang="less" scoped>
.result-card {
position: relative;
.copy-button {
position: absolute;
top: 10px;
right: 10px;
}
}
</style>