diff --git a/.eslintrc-auto-import.json b/.eslintrc-auto-import.json
index 90487551..e52fba3c 100644
--- a/.eslintrc-auto-import.json
+++ b/.eslintrc-auto-import.json
@@ -293,6 +293,7 @@
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
- "WritableComputedRef": true
+ "WritableComputedRef": true,
+ "useClipboardItems": true
}
}
diff --git a/components.d.ts b/components.d.ts
index 52c1dbf2..1c8e1232 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -71,6 +71,7 @@ declare module '@vue/runtime-core' {
CTooltip: typeof import('./src/ui/c-tooltip/c-tooltip.vue')['default']
'CTooltip.demo': typeof import('./src/ui/c-tooltip/c-tooltip.demo.vue')['default']
CurlConverter: typeof import('./src/tools/curl-converter/curl-converter.vue')['default']
+ CurrencyConverter: typeof import('./src/tools/currency-converter/currency-converter.vue')['default']
DateTimeConverter: typeof import('./src/tools/date-time-converter/date-time-converter.vue')['default']
'DemoHome.page': typeof import('./src/ui/demo/demo-home.page.vue')['default']
DemoWrapper: typeof import('./src/ui/demo/demo-wrapper.vue')['default']
@@ -148,6 +149,7 @@ declare module '@vue/runtime-core' {
JsUnobfuscator: typeof import('./src/tools/js-unobfuscator/js-unobfuscator.vue')['default']
JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default']
KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
+ ListComparer: typeof import('./src/tools/list-comparer/list-comparer.vue')['default']
ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default']
LiveCode: typeof import('./src/tools/live-code/live-code.vue')['default']
LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default']
diff --git a/package.json b/package.json
index feaf03e1..795d03c7 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"@tiptap/pm": "2.1.6",
"@tiptap/starter-kit": "2.1.6",
"@tiptap/vue-3": "2.0.3",
+ "@types/arr-diff": "^4.0.3",
"@types/figlet": "^1.5.8",
"@types/markdown-it": "^13.0.7",
"@types/memorystream": "^0.3.4",
@@ -54,6 +55,7 @@
"@vueuse/core": "^10.11.1",
"@vueuse/head": "^1.0.0",
"@vueuse/router": "^10.0.0",
+ "arr-diff": "^4.0.0",
"bcryptjs": "^2.4.3",
"change-case": "^4.1.2",
"cidr-tools": "^7.0.4",
@@ -65,6 +67,8 @@
"cronstrue": "^2.26.0",
"crypto-js": "^4.1.1",
"curlconverter": "^4.10.1",
+ "currency-codes-ts": "^3.0.0",
+ "currency-exchanger-js": "^1.0.4",
"date-fns": "^2.29.3",
"decomposerize": "^1.4.1",
"dns-query": "^0.11.2",
@@ -74,6 +78,7 @@
"exif-be-gone": "^1.5.1",
"exifreader": "^4.20.0",
"fflate": "^0.8.2",
+ "fast_array_intersect": "^1.1.0",
"figlet": "^1.7.0",
"figue": "^1.2.0",
"fuse.js": "^6.6.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 90477ad0..6e58cf29 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -35,6 +35,9 @@ importers:
'@tiptap/vue-3':
specifier: 2.0.3
version: 2.0.3(@tiptap/core@2.1.12(@tiptap/pm@2.1.6))(@tiptap/pm@2.1.6)(vue@3.3.4)
+ '@types/arr-diff':
+ specifier: ^4.0.3
+ version: 4.0.3
'@types/figlet':
specifier: ^1.5.8
version: 1.5.8
@@ -62,6 +65,9 @@ importers:
'@vueuse/router':
specifier: ^10.0.0
version: 10.0.0(vue-router@4.1.6(vue@3.3.4))(vue@3.3.4)
+ arr-diff:
+ specifier: ^4.0.0
+ version: 4.0.0
bcryptjs:
specifier: ^2.4.3
version: 2.4.3
@@ -95,6 +101,12 @@ importers:
curlconverter:
specifier: ^4.10.1
version: 4.11.0
+ currency-codes-ts:
+ specifier: ^3.0.0
+ version: 3.0.0
+ currency-exchanger-js:
+ specifier: ^1.0.4
+ version: 1.0.4
date-fns:
specifier: ^2.29.3
version: 2.29.3
@@ -119,6 +131,9 @@ importers:
exifreader:
specifier: ^4.20.0
version: 4.25.0
+ fast_array_intersect:
+ specifier: ^1.1.0
+ version: 1.1.0
fflate:
specifier: ^0.8.2
version: 0.8.2
@@ -2127,6 +2142,9 @@ packages:
'@tsconfig/node18@18.2.0':
resolution: {integrity: sha512-yhxwIlFVSVcMym3O31HoMnRXpoenmpIxcj4Yoes2DUpe+xCJnA7ECQP1Vw889V0jTt/2nzvpLQ/UuMYCd3JPIg==}
+ '@types/arr-diff@4.0.3':
+ resolution: {integrity: sha512-oIBe7qtc48Q1JPNuqSIUYCNMQxYzTkQAEw07b0NEZpwKbUeOswzex3qfvXCLrZDIQ9t3ucIxH6JqFmjRTPbDEg==}
+
'@types/bcryptjs@2.4.2':
resolution: {integrity: sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==}
@@ -2764,6 +2782,10 @@ packages:
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
engines: {node: '>= 0.4'}
+ arr-diff@4.0.0:
+ resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==}
+ engines: {node: '>=0.10.0'}
+
array-buffer-byte-length@1.0.0:
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
@@ -3220,6 +3242,9 @@ packages:
resolution: {integrity: sha512-M1VdV3hpBAsd1Zzvqcvf63wgDpcwCuS4WiNEVFpJ0s33MGO2sVDTfswYq0EPypCmESrCzmgL8h68DTzJuSDbVA==}
hasBin: true
+ cross-fetch@3.1.8:
+ resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==}
+
cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -3282,6 +3307,13 @@ packages:
resolution: {integrity: sha512-jBSvfDN10L6rGWVlkAYgtkIG8lYprDvtBgos7mafxtv15keYeQWsxUgnzns3JmqEcGJMeaGlDNdRUszURPCUaw==}
hasBin: true
+ currency-codes-ts@3.0.0:
+ resolution: {integrity: sha512-ZJeCpq5uY2t8dDl4xdF15shkp5o8jrHcD4lHftK/O8j8xTHlXg0E5YhpZbRJvnLRaKe+JQh1/q1AI9Wc2Dl3Nw==}
+ engines: {node: '>=14'}
+
+ currency-exchanger-js@1.0.4:
+ resolution: {integrity: sha512-znd30Ueuf3IFTwNEuSy45uBEZZXJIUG9Z4Cpym5VxSxafx/CazPCRx5NMrqOFE2ZYPXezo7FfJLa8SuES3PnYw==}
+
dash-get@1.0.2:
resolution: {integrity: sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==}
@@ -3831,6 +3863,9 @@ packages:
fast-uri@3.0.3:
resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==}
+ fast_array_intersect@1.1.0:
+ resolution: {integrity: sha512-/DCilZlUdz2XyNDF+ASs0PwY+RKG9Y4Silp/gbS72Cvbg4oibc778xcecg+pnNyiNHYgh/TApsiDTjpdniyShw==}
+
fastq@1.15.0:
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
@@ -8844,6 +8879,8 @@ snapshots:
'@tsconfig/node18@18.2.0': {}
+ '@types/arr-diff@4.0.3': {}
+
'@types/bcryptjs@2.4.2': {}
'@types/chai-subset@1.3.3':
@@ -9719,6 +9756,8 @@ snapshots:
aria-query@5.3.2: {}
+ arr-diff@4.0.0: {}
+
array-buffer-byte-length@1.0.0:
dependencies:
call-bind: 1.0.7
@@ -10273,6 +10312,12 @@ snapshots:
cronstrue@2.26.0: {}
+ cross-fetch@3.1.8:
+ dependencies:
+ node-fetch: 2.7.0
+ transitivePeerDependencies:
+ - encoding
+
cross-spawn@7.0.3:
dependencies:
path-key: 3.1.1
@@ -10357,6 +10402,16 @@ snapshots:
transitivePeerDependencies:
- tree_sitter
+ currency-codes-ts@3.0.0:
+ dependencies:
+ lodash-es: 4.17.21
+
+ currency-exchanger-js@1.0.4:
+ dependencies:
+ cross-fetch: 3.1.8
+ transitivePeerDependencies:
+ - encoding
+
dash-get@1.0.2: {}
data-uri-to-buffer@4.0.1: {}
@@ -11017,6 +11072,8 @@ snapshots:
fast-uri@3.0.3: {}
+ fast_array_intersect@1.1.0: {}
+
fastq@1.15.0:
dependencies:
reusify: 1.0.4
diff --git a/src/tools/currency-converter/currency-converter.vue b/src/tools/currency-converter/currency-converter.vue
new file mode 100644
index 00000000..eee3696f
--- /dev/null
+++ b/src/tools/currency-converter/currency-converter.vue
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ {{ currency.currency }} [{{ currency.code }}/{{ currency.number }} - {{ currency.digits }}digits] (also in: {{ currency.countries?.join(', ') }})
+
+
+
+
+
+
+
+
+
+
+ {{ currencyToCountriesOutput.currency }} [{{ currencyToCountriesOutput.code }}/{{ currencyToCountriesOutput.number }} - {{ currencyToCountriesOutput.digits }}digits]
+
+
+
+ -
+ {{ countryName }}
+
+
+
+
+
diff --git a/src/tools/currency-converter/currency-exchanger-js.d.ts b/src/tools/currency-converter/currency-exchanger-js.d.ts
new file mode 100644
index 00000000..8e94badc
--- /dev/null
+++ b/src/tools/currency-converter/currency-exchanger-js.d.ts
@@ -0,0 +1,4 @@
+declare module 'currency-exchanger-js'{
+ export function convertOnDate(value: number,fromCurrency: string,toCurrency: string,inputDate: Date): number;
+ export function convert(value: number,fromCurrency: string,toCurrency: string): number;
+}
diff --git a/src/tools/currency-converter/index.ts b/src/tools/currency-converter/index.ts
new file mode 100644
index 00000000..92401531
--- /dev/null
+++ b/src/tools/currency-converter/index.ts
@@ -0,0 +1,12 @@
+import { Currency } from '@vicons/tabler';
+import { defineTool } from '../tool';
+
+export const tool = defineTool({
+ name: 'Currency Converter',
+ path: '/currency-converter',
+ description: 'Convert currency values using ExchangeRate-API',
+ keywords: ['currency', 'converter'],
+ component: () => import('./currency-converter.vue'),
+ icon: Currency,
+ createdAt: new Date('2024-08-15'),
+});
diff --git a/src/tools/currency-converter/moneys.json b/src/tools/currency-converter/moneys.json
new file mode 100644
index 00000000..d8e52071
--- /dev/null
+++ b/src/tools/currency-converter/moneys.json
@@ -0,0 +1 @@
+{ "1000sats": "", "1inch": "1inch", "aave": "Aave", "ada": "Cardano", "aed": "Emirati Dirham", "afn": "Afghan Afghani", "agix": "SingularityNET", "akt": "Akash Network", "algo": "Algorand", "all": "Albanian Lek", "amd": "Armenian Dram", "amp": "Amp", "ang": "Dutch Guilder", "aoa": "Angolan Kwanza", "ape": "ApeCoin", "apt": "Aptos", "ar": "Arweave", "arb": "Arbitrum", "ars": "Argentine Peso", "atom": "Cosmos", "ats": "Austrian Schilling", "aud": "Australian Dollar", "avax": "Avalanche", "awg": "Aruban or Dutch Guilder", "axs": "Axie Infinity", "azm": "Azerbaijani Manat", "azn": "Azerbaijan Manat", "bake": "BakeryToken", "bam": "Bosnian Convertible Mark", "bat": "Basic Attention Token", "bbd": "Barbadian or Bajan Dollar", "bch": "Bitcoin Cash", "bdt": "Bangladeshi Taka", "bef": "Belgian Franc", "bgn": "Bulgarian Lev", "bhd": "Bahraini Dinar", "bif": "Burundian Franc", "bmd": "Bermudian Dollar", "bnb": "Binance Coin", "bnd": "Bruneian Dollar", "bob": "Bolivian Bolíviano", "brl": "Brazilian Real", "bsd": "Bahamian Dollar", "bsv": "Bitcoin SV", "bsw": "Biswap", "btc": "Bitcoin", "btcb": "Bitcoin BEP2", "btg": "Bitcoin Gold", "btn": "Bhutanese Ngultrum", "btt": "BitTorrent", "busd": "Binance USD", "bwp": "Botswana Pula", "byn": "Belarusian Ruble", "byr": "Belarusian Ruble", "bzd": "Belizean Dollar", "cad": "Canadian Dollar", "cake": "PancakeSwap", "cdf": "Congolese Franc", "celo": "Celo", "cfx": "Conflux", "chf": "Swiss Franc", "chz": "Chiliz", "clp": "Chilean Peso", "cnh": "Chinese Yuan Renminbi Offshore", "cny": "Chinese Yuan Renminbi", "comp": "Compound", "cop": "Colombian Peso", "crc": "Costa Rican Colon", "cro": "Crypto.com Chain", "crv": "Curve DAO Token", "cspr": "Casper", "cuc": "Cuban Convertible Peso", "cup": "Cuban Peso", "cve": "Cape Verdean Escudo", "cvx": "Convex Finance", "cyp": "Cypriot Pound", "czk": "Czech Koruna", "dai": "DAI", "dash": "Digital Cash", "dcr": "Decred", "dem": "German Deutsche Mark", "dfi": "DfiStarter", "djf": "Djiboutian Franc", "dkk": "Danish Krone", "doge": "Dogecoin", "dop": "Dominican Peso", "dot": "Polkadot", "dydx": "dYdX", "dzd": "Algerian Dinar", "eek": "Estonian Kroon", "egld": "Elrond", "egp": "Egyptian Pound", "enj": "Enjin Coin", "eos": "EOS", "ern": "Eritrean Nakfa", "esp": "Spanish Peseta", "etb": "Ethiopian Birr", "etc": "Ethereum Classic", "eth": "Ethereum", "eur": "Euro", "fei": "Fei USD", "fil": "Filecoin", "fim": "Finnish Markka", "fjd": "Fijian Dollar", "fkp": "Falkland Island Pound", "flow": "Flow", "flr": "FLARE", "frax": "Frax", "frf": "French Franc", "ftm": "Fantom", "ftt": "FarmaTrust", "fxs": "Frax Share", "gala": "Gala", "gbp": "British Pound", "gel": "Georgian Lari", "ggp": "Guernsey Pound", "ghc": "Ghanaian Cedi", "ghs": "Ghanaian Cedi", "gip": "Gibraltar Pound", "gmd": "Gambian Dalasi", "gmx": "Goldmaxcoin", "gnf": "Guinean Franc", "gno": "Gnosis", "grd": "Greek Drachma", "grt": "The Graph", "gt": "GateToken", "gtq": "Guatemalan Quetzal", "gusd": "Gemini US Dollar", "gyd": "Guyanese Dollar", "hbar": "Hedera", "hkd": "Hong Kong Dollar", "hnl": "Honduran Lempira", "hnt": "Helium", "hot": "Hydro Protocol", "hrk": "Croatian Kuna", "ht": "Huobi Token", "htg": "Haitian Gourde", "huf": "Hungarian Forint", "icp": "Internet Computer", "idr": "Indonesian Rupiah", "iep": "Irish Pound", "ils": "Israeli Shekel", "imp": "Isle of Man Pound", "imx": "Immutable X", "inj": "Injective", "inr": "Indian Rupee", "iqd": "Iraqi Dinar", "irr": "Iranian Rial", "isk": "Icelandic Krona", "itl": "Italian Lira", "jep": "Jersey Pound", "jmd": "Jamaican Dollar", "jod": "Jordanian Dinar", "jpy": "Japanese Yen", "kas": "", "kava": "Kava", "kcs": "Kucoin", "kda": "Kadena", "kes": "Kenyan Shilling", "kgs": "Kyrgyzstani Som", "khr": "Cambodian Riel", "klay": "Klaytn", "kmf": "Comorian Franc", "knc": "Kyber Network Crystals", "kpw": "North Korean Won", "krw": "South Korean Won", "ksm": "Kusama", "kwd": "Kuwaiti Dinar", "kyd": "Caymanian Dollar", "kzt": "Kazakhstani Tenge", "lak": "Lao Kip", "lbp": "Lebanese Pound", "ldo": "Lido DAO Token", "leo": "LEOcoin", "link": "Chainlink", "lkr": "Sri Lankan Rupee", "lrc": "Loopring", "lrd": "Liberian Dollar", "lsl": "Basotho Loti", "ltc": "Litecoin", "ltl": "Lithuanian Litas", "luf": "Luxembourg Franc", "luna": "Terra", "lunc": "", "lvl": "Latvian Lat", "lyd": "Libyan Dinar", "mad": "Moroccan Dirham", "mana": "Mana Coin Decentraland", "matic": "Polygon", "mbx": "MobieCoin", "mdl": "Moldovan Leu", "mga": "Malagasy Ariary", "mgf": "Malagasy Franc", "mina": "Mina", "mkd": "Macedonian Denar", "mkr": "Maker", "mmk": "Burmese Kyat", "mnt": "Mongolian Tughrik", "mop": "Macau Pataca", "mro": "Mauritanian Ouguiya", "mru": "Mauritanian Ouguiya", "mtl": "Maltese Lira", "mur": "Mauritian Rupee", "mvr": "Maldivian Rufiyaa", "mwk": "Malawian Kwacha", "mxn": "Mexican Peso", "mxv": "", "myr": "Malaysian Ringgit", "mzm": "Mozambican Metical", "mzn": "Mozambican Metical", "nad": "Namibian Dollar", "near": "NEAR Protocol", "neo": "NEO", "nexo": "NEXO", "nft": "NFT", "ngn": "Nigerian Naira", "nio": "Nicaraguan Cordoba", "nlg": "Dutch Guilder", "nok": "Norwegian Krone", "npr": "Nepalese Rupee", "nzd": "New Zealand Dollar", "okb": "Okex", "omr": "Omani Rial", "one": "Menlo One", "op": "Optimism", "ordi": "", "pab": "Panamanian Balboa", "paxg": "PAX Gold", "pen": "Peruvian Sol", "pepe": "", "pgk": "Papua New Guinean Kina", "php": "Philippine Peso", "pkr": "Pakistani Rupee", "pln": "Polish Zloty", "pte": "Portuguese Escudo", "pyg": "Paraguayan Guarani", "qar": "Qatari Riyal", "qnt": "Quant", "qtum": "QTUM", "rol": "Romanian Leu", "ron": "Romanian Leu", "rpl": "Rocket Pool", "rsd": "Serbian Dinar", "rub": "Russian Ruble", "rune": "THORChain (ERC20)", "rvn": "Ravencoin", "rwf": "Rwandan Franc", "sand": "The Sandbox", "sar": "Saudi Arabian Riyal", "sbd": "Solomon Islander Dollar", "scr": "Seychellois Rupee", "sdd": "Sudanese Dinar", "sdg": "Sudanese Pound", "sek": "Swedish Krona", "sgd": "Singapore Dollar", "shib": "Shiba Inu", "shp": "Saint Helenian Pound", "sit": "Slovenian Tolar", "skk": "Slovak Koruna", "sle": "Sierra Leonean Leone", "sll": "Sierra Leonean Leone", "snx": "Synthetix Network", "sol": "Solana", "sos": "Somali Shilling", "spl": "Seborgan Luigino", "srd": "Surinamese Dollar", "srg": "Surinamese Guilder", "std": "Sao Tomean Dobra", "stn": "Sao Tomean Dobra", "stx": "Stacks", "sui": "Sui", "svc": "Salvadoran Colon", "syp": "Syrian Pound", "szl": "Swazi Lilangeni", "thb": "Thai Baht", "theta": "Theta", "tjs": "Tajikistani Somoni", "tmm": "Turkmenistani Manat", "tmt": "Turkmenistani Manat", "tnd": "Tunisian Dinar", "ton": "Tokamak Network", "top": "Tongan Pa'anga", "trl": "Turkish Lira", "trx": "TRON", "try": "Turkish Lira", "ttd": "Trinidadian Dollar", "tusd": "True USD", "tvd": "Tuvaluan Dollar", "twd": "Taiwan New Dollar", "twt": "Trust Wallet Token", "tzs": "Tanzanian Shilling", "uah": "Ukrainian Hryvnia", "ugx": "Ugandan Shilling", "uni": "Uniswap", "usd": "US Dollar", "usdc": "USDC", "usdd": "", "usdp": "USDP Stablecoin", "usdt": "Tether", "uyu": "Uruguayan Peso", "uzs": "Uzbekistani Som", "val": "Vatican City Lira", "veb": "Venezuelan Bolívar", "ved": "", "vef": "Venezuelan Bolívar", "ves": "Venezuelan Bolívar", "vet": "Vechain", "vnd": "Vietnamese Dong", "vuv": "Ni-Vanuatu Vatu", "waves": "Waves", "wemix": "WEMIX", "woo": "WOO Network", "wst": "Samoan Tala", "xaf": "Central African CFA Franc BEAC", "xag": "Silver Ounce", "xau": "Gold Ounce", "xaut": "Tether Gold", "xbt": "", "xcd": "East Caribbean Dollar", "xch": "Chia", "xdc": "XDC Network", "xdr": "IMF Special Drawing Rights", "xec": "Eternal Coin", "xem": "NEM", "xlm": "Stellar Lumen", "xmr": "Monero", "xof": "CFA Franc", "xpd": "Palladium Ounce", "xpf": "CFP Franc", "xpt": "Platinum Ounce", "xrp": "Ripple", "xtz": "Tezos", "yer": "Yemeni Rial", "zar": "South African Rand", "zec": "ZCash", "zil": "Zilliqa", "zmk": "Zambian Kwacha", "zmw": "Zambian Kwacha", "zwd": "Zimbabwean Dollar", "zwg": "", "zwl": "Zimbabwean Dollar" }
diff --git a/src/tools/index.ts b/src/tools/index.ts
index ea2c581c..8d283ebd 100644
--- a/src/tools/index.ts
+++ b/src/tools/index.ts
@@ -16,6 +16,7 @@ import { tool as jsUnobfuscator } from './js-unobfuscator';
import { tool as extractTextFromHtml } from './extract-text-from-html';
import { tool as colorWheel } from './color-wheel';
import { tool as codeHighlighter } from './code-highlighter';
+import { tool as currencyConverter } from './currency-converter';
import { tool as asciiTextDrawer } from './ascii-text-drawer';
import { tool as textToUnicode } from './text-to-unicode';
@@ -52,6 +53,7 @@ import { tool as tomlToJson } from './toml-to-json';
import { tool as jsonToCsv } from './json-to-csv';
import { tool as cameraRecorder } from './camera-recorder';
import { tool as listConverter } from './list-converter';
+import { tool as listComparer } from './list-comparer';
import { tool as phoneParserAndFormatter } from './phone-parser-and-formatter';
import { tool as jsonDiff } from './json-diff';
import { tool as ipv4RangeExpander } from './ipv4-range-expander';
@@ -151,10 +153,12 @@ export const toolsByCategory: ToolCategory[] = [
jsonToYaml,
jsonToToml,
listConverter,
+ listComparer,
tomlToJson,
tomlToYaml,
xmlToJson,
jsonToXml,
+ currencyConverter,
markdownToHtml,
gzipConverter,
uuidConverter,
diff --git a/src/tools/list-comparer/index.ts b/src/tools/list-comparer/index.ts
new file mode 100644
index 00000000..88262c14
--- /dev/null
+++ b/src/tools/list-comparer/index.ts
@@ -0,0 +1,12 @@
+import { List } from '@vicons/tabler';
+import { defineTool } from '../tool';
+
+export const tool = defineTool({
+ name: 'Lists Comparer',
+ path: '/list-comparer',
+ description: 'Compare two list items',
+ keywords: ['list', 'comparer'],
+ component: () => import('./list-comparer.vue'),
+ icon: List,
+ createdAt: new Date('2024-08-15'),
+});
diff --git a/src/tools/list-comparer/list-comparer.service.test.ts b/src/tools/list-comparer/list-comparer.service.test.ts
new file mode 100644
index 00000000..98affcf7
--- /dev/null
+++ b/src/tools/list-comparer/list-comparer.service.test.ts
@@ -0,0 +1,97 @@
+import { describe, expect, it } from 'vitest';
+import { compareLists } from './list-comparer.service';
+
+describe('list-comparer', () => {
+ describe('compareLists', () => {
+ it('return correct comparaison', () => {
+ expect(compareLists({
+ list1: '1\n 2\n3\n4\n5\n4\n7\nA',
+ list2: '1\n2\n3\n4\n6\n4\n7\na',
+ trimItems: true,
+ ignoreCase: true,
+ })).to.deep.eq({
+ list1Not2: [
+ '5',
+ ],
+ list2Not1: [
+ '6',
+ ],
+ same: [
+ '1',
+ '2',
+ '3',
+ '4',
+ '7',
+ 'a',
+ ],
+ });
+
+ expect(compareLists({
+ list1: '1\n 2\n3\n4\n5\n4\n7\nA',
+ list2: '1\n2\n3\n4\n6\n4\n7\na',
+ trimItems: false,
+ ignoreCase: false,
+ })).to.deep.eq({
+ list1Not2: [
+ ' 2',
+ '5',
+ 'A',
+ ],
+ list2Not1: [
+ '2',
+ '6',
+ 'a',
+ ],
+ same: [
+ '1',
+ '3',
+ '4',
+ '7',
+ ],
+ });
+
+ expect(compareLists({
+ list1: '1, 2,3,4,5\n4,7,A,A',
+ list2: '1\n2\n3\n4\n6\n4\n7\na',
+ trimItems: false,
+ ignoreCase: false,
+ separator: ',',
+ })).to.deep.eq({
+ list1Not2: [
+ ' 2',
+ '5',
+ 'A',
+ ],
+ list2Not1: [
+ '2',
+ '6',
+ 'a',
+ ],
+ same: [
+ '1',
+ '3',
+ '4',
+ '7',
+ ],
+ });
+
+ expect(compareLists({
+ list1: '10\n20\n20\n30',
+ list2: '30\n20\n40',
+ trimItems: false,
+ ignoreCase: false,
+ })).to.deep.eq({
+ list1Not2: [
+ '10',
+ ],
+ list2Not1: [
+ '40',
+ ],
+ same: [
+ '30',
+ '20',
+ ],
+ });
+ });
+ });
+});
diff --git a/src/tools/list-comparer/list-comparer.service.ts b/src/tools/list-comparer/list-comparer.service.ts
new file mode 100644
index 00000000..c0ca4a45
--- /dev/null
+++ b/src/tools/list-comparer/list-comparer.service.ts
@@ -0,0 +1,36 @@
+import _ from 'lodash';
+import intersect from 'fast_array_intersect';
+import diff from 'arr-diff';
+
+export function compareLists({
+ list1,
+ list2,
+ ignoreCase = false,
+ trimItems = true,
+ separator = '',
+}: {
+ list1: string
+ list2: string
+ separator?: string
+ ignoreCase?: boolean
+ trimItems?: boolean
+}) {
+ const splitSep = separator ? `${separator}|` : '';
+ const splitRegExp = new RegExp(`(?:${splitSep}\\n)`, 'g');
+
+ const prepareList = (list: string) =>
+ _.chain(list ?? '')
+ .thru(text => ignoreCase ? text.toLowerCase() : text)
+ .split(splitRegExp)
+ .map(text => trimItems ? text.trim() : text)
+ .value();
+
+ const list1Arr = prepareList(list1);
+ const list2Arr = prepareList(list2);
+
+ return {
+ same: [...new Set(intersect([list1Arr, list2Arr]))],
+ list2Not1: [...new Set(diff(list2Arr, list1Arr))],
+ list1Not2: [...new Set(diff(list1Arr, list2Arr))],
+ };
+}
diff --git a/src/tools/list-comparer/list-comparer.vue b/src/tools/list-comparer/list-comparer.vue
new file mode 100644
index 00000000..94caa5a4
--- /dev/null
+++ b/src/tools/list-comparer/list-comparer.vue
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+