+
+
+
+ {{ $t('home.follow.p1') }}
+ GitHub
+ {{ $t('home.follow.p2') }}
+ Twitter.
+ {{ $t('home.follow.thankYou') }}
+
+
+
testing this
@@ -23,49 +43,35 @@ const { t } = useI18n();
-
{{ $t('home.categories.favoriteTools') }}
-
-
-
-
-
+
+ {{ $t('home.categories.favoriteTools') }}
+
+
+
+
-
{{ t('home.categories.newestTools') }}
-
-
-
-
-
+
+ {{ t('home.categories.newestTools') }}
+
+
+
+
- {{ $t('home.categories.allTools') }}
-
-
-
-
-
-
-
+
+ {{ $t('home.categories.allTools') }}
+
+
+
+
diff --git a/src/utils/base64.test.ts b/src/utils/base64.test.ts
index 994f1b1b..51d15239 100644
--- a/src/utils/base64.test.ts
+++ b/src/utils/base64.test.ts
@@ -38,7 +38,8 @@ describe('base64 utils', () => {
it('should throw for incorrect base64 string', () => {
expect(() => base64ToText('a')).to.throw('Incorrect base64 string');
- expect(() => base64ToText(' ')).to.throw('Incorrect base64 string');
+ // should not really be false because trimming of space is now implied
+ // expect(() => base64ToText(' ')).to.throw('Incorrect base64 string');
expect(() => base64ToText('é')).to.throw('Incorrect base64 string');
// missing final '='
expect(() => base64ToText('bG9yZW0gaXBzdW0')).to.throw('Incorrect base64 string');
@@ -56,17 +57,17 @@ describe('base64 utils', () => {
it('should return false for incorrect base64 string', () => {
expect(isValidBase64('a')).to.eql(false);
- expect(isValidBase64(' ')).to.eql(false);
expect(isValidBase64('é')).to.eql(false);
expect(isValidBase64('data:text/plain;notbase64,YQ==')).to.eql(false);
// missing final '='
expect(isValidBase64('bG9yZW0gaXBzdW0')).to.eql(false);
});
- it('should return false for untrimmed correct base64 string', () => {
- expect(isValidBase64('bG9yZW0gaXBzdW0= ')).to.eql(false);
- expect(isValidBase64(' LTE=')).to.eql(false);
- expect(isValidBase64(' YQ== ')).to.eql(false);
+ it('should return true for untrimmed correct base64 string', () => {
+ expect(isValidBase64('bG9yZW0gaXBzdW0= ')).to.eql(true);
+ expect(isValidBase64(' LTE=')).to.eql(true);
+ expect(isValidBase64(' YQ== ')).to.eql(true);
+ expect(isValidBase64(' ')).to.eql(true);
});
});
diff --git a/src/utils/base64.ts b/src/utils/base64.ts
index 16912ee3..44e59f41 100644
--- a/src/utils/base64.ts
+++ b/src/utils/base64.ts
@@ -1,7 +1,9 @@
+import { Base64 } from 'js-base64';
+
export { textToBase64, base64ToText, isValidBase64, removePotentialDataAndMimePrefix };
function textToBase64(str: string, { makeUrlSafe = false }: { makeUrlSafe?: boolean } = {}) {
- const encoded = window.btoa(str);
+ const encoded = Base64.encode(str);
return makeUrlSafe ? makeUriSafe(encoded) : encoded;
}
@@ -16,7 +18,7 @@ function base64ToText(str: string, { makeUrlSafe = false }: { makeUrlSafe?: bool
}
try {
- return window.atob(cleanStr);
+ return Base64.decode(cleanStr);
}
catch (_) {
throw new Error('Incorrect base64 string');
@@ -34,10 +36,11 @@ function isValidBase64(str: string, { makeUrlSafe = false }: { makeUrlSafe?: boo
}
try {
+ const reEncodedBase64 = Base64.fromUint8Array(Base64.toUint8Array(cleanStr));
if (makeUrlSafe) {
- return removePotentialPadding(window.btoa(window.atob(cleanStr))) === cleanStr;
+ return removePotentialPadding(reEncodedBase64) === cleanStr;
}
- return window.btoa(window.atob(cleanStr)) === cleanStr;
+ return reEncodedBase64 === cleanStr.replace(/\s/g, '');
}
catch (err) {
return false;
diff --git a/vite.config.ts b/vite.config.ts
index 00f90c33..42a2cb29 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,20 +1,20 @@
-import { URL, fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
+import { URL, fileURLToPath } from 'node:url';
-import { defineConfig } from 'vite';
+import VueI18n from '@intlify/unplugin-vue-i18n/vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
+import Unocss from 'unocss/vite';
+import AutoImport from 'unplugin-auto-import/vite';
+import IconsResolver from 'unplugin-icons/resolver';
+import Icons from 'unplugin-icons/vite';
+import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
+import Components from 'unplugin-vue-components/vite';
+import { defineConfig } from 'vite';
+import { VitePWA } from 'vite-plugin-pwa';
import markdown from 'vite-plugin-vue-markdown';
import svgLoader from 'vite-svg-loader';
-import { VitePWA } from 'vite-plugin-pwa';
-import AutoImport from 'unplugin-auto-import/vite';
-import Components from 'unplugin-vue-components/vite';
-import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
-import Unocss from 'unocss/vite';
import { configDefaults } from 'vitest/config';
-import Icons from 'unplugin-icons/vite';
-import IconsResolver from 'unplugin-icons/resolver';
-import VueI18n from '@intlify/unplugin-vue-i18n/vite';
const baseUrl = process.env.BASE_URL ?? '/';
@@ -23,9 +23,13 @@ export default defineConfig({
plugins: [
VueI18n({
runtimeOnly: true,
+ jitCompilation: true,
compositionOnly: true,
fullInstall: true,
- include: [resolve(__dirname, 'locales/**')],
+ strictMessage: false,
+ include: [
+ resolve(__dirname, 'locales/**'),
+ ],
}),
AutoImport({
imports: [