diff --git a/web/src/helpers/base64.js b/web/src/helpers/base64.js
new file mode 100644
index 00000000..1e15d6b9
--- /dev/null
+++ b/web/src/helpers/base64.js
@@ -0,0 +1,56 @@
+/*
+Copyright (C) 2025 QuantumNous
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+const toBinaryString = (text) => {
+ if (typeof TextEncoder !== 'undefined') {
+ const bytes = new TextEncoder().encode(text);
+ let binary = '';
+
+ bytes.forEach((byte) => {
+ binary += String.fromCharCode(byte);
+ });
+
+ return binary;
+ }
+
+ return encodeURIComponent(text).replace(/%([0-9A-F]{2})/g, (_, hex) =>
+ String.fromCharCode(parseInt(hex, 16)),
+ );
+};
+
+export const encodeToBase64 = (value) => {
+ const input = value == null ? '' : String(value);
+
+ if (typeof window === 'undefined') {
+ if (typeof Buffer !== 'undefined') {
+ return Buffer.from(input, 'utf-8').toString('base64');
+ }
+ if (
+ typeof globalThis !== 'undefined' &&
+ typeof globalThis.btoa === 'function'
+ ) {
+ return globalThis.btoa(toBinaryString(input));
+ }
+ throw new Error(
+ 'Base64 encoding is unavailable in the current environment',
+ );
+ }
+
+ return window.btoa(toBinaryString(input));
+};
diff --git a/web/src/helpers/index.js b/web/src/helpers/index.js
index 26ebc649..cfb0270e 100644
--- a/web/src/helpers/index.js
+++ b/web/src/helpers/index.js
@@ -20,6 +20,7 @@ For commercial licensing, please contact support@quantumnous.com
export * from './history';
export * from './auth';
export * from './utils';
+export * from './base64';
export * from './api';
export * from './render';
export * from './log';
diff --git a/web/src/hooks/tokens/useTokensData.jsx b/web/src/hooks/tokens/useTokensData.jsx
index 9b3a071c..a34508f4 100644
--- a/web/src/hooks/tokens/useTokensData.jsx
+++ b/web/src/hooks/tokens/useTokensData.jsx
@@ -20,7 +20,13 @@ For commercial licensing, please contact support@quantumnous.com
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Modal } from '@douyinfe/semi-ui';
-import { API, copy, showError, showSuccess } from '../../helpers';
+import {
+ API,
+ copy,
+ showError,
+ showSuccess,
+ encodeToBase64,
+} from '../../helpers';
import { ITEMS_PER_PAGE } from '../../constants';
import { useTableCompactMode } from '../common/useTableCompactMode';
@@ -136,7 +142,7 @@ export const useTokensData = (openFluentNotification) => {
apiKey: 'sk-' + record.key,
};
let encodedConfig = encodeURIComponent(
- btoa(JSON.stringify(cherryConfig)),
+ encodeToBase64(JSON.stringify(cherryConfig)),
);
url = url.replaceAll('{cherryConfig}', encodedConfig);
} else {
diff --git a/web/src/pages/Playground/index.jsx b/web/src/pages/Playground/index.jsx
index dcf8367c..c2083fde 100644
--- a/web/src/pages/Playground/index.jsx
+++ b/web/src/pages/Playground/index.jsx
@@ -47,6 +47,7 @@ import {
createLoadingAssistantMessage,
getTextContent,
buildApiPayload,
+ encodeToBase64,
} from '../../helpers';
// Components
@@ -72,7 +73,7 @@ const generateAvatarDataUrl = (username) => {
${firstLetter}
`;
- return `data:image/svg+xml;base64,${btoa(svg)}`;
+ return `data:image/svg+xml;base64,${encodeToBase64(svg)}`;
};
const Playground = () => {