This commit is contained in:
XOF
2025-11-20 12:24:05 +08:00
commit f28bdc751f
164 changed files with 64248 additions and 0 deletions

123
frontend/js/utils/utils.js Normal file
View File

@@ -0,0 +1,123 @@
/**
* @file utils/utils.js
* @description Provides a collection of common, reusable helper functions.
*/
/**
* A debounce utility to delay function execution, now with a cancel method.
* @param {Function} func The function to debounce.
* @param {number} wait The delay in milliseconds.
* @returns {Function} The new debounced function with a `.cancel()` method attached.
*/
export function debounce(func, wait) {
let timeout;
// create a named function expression here instead of returning an anonymous one directly.
const debounced = function(...args) {
// Store the context of 'this' in case it's needed inside the debounced function.
const context = this;
// The core logic remains the same.
const later = () => {
clearTimeout(timeout);
// Use .apply() to preserve the original 'this' context.
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
// Attach a 'cancel' method to the debounced function.
// This allows us to abort a pending execution.
debounced.cancel = () => {
clearTimeout(timeout);
};
return debounced;
}
/**
* Asynchronously copies a given string of text to the user's clipboard.
* @param {string} text The text to be copied.
* @returns {Promise<void>} A promise that resolves on success, rejects on failure.
*/
export function copyToClipboard(text) {
// Use the modern Clipboard API if available
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
// Fallback for older browsers
return new Promise((resolve, reject) => {
const textArea = document.createElement("textarea");
textArea.value = text;
// Make the textarea invisible and prevent scrolling
textArea.style.position = "fixed";
textArea.style.top = "-9999px";
textArea.style.left = "-9999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand("copy");
if (successful) {
resolve();
} else {
reject(new Error("Fallback: Unable to copy text to clipboard."));
}
} catch (err) {
reject(err);
} finally {
document.body.removeChild(textArea);
}
});
}
/**
* Validates a single API Key against a list of known formats.
* @param {string} key - The API key string to validate.
* @returns {boolean} - True if the key matches any of the known formats.
*/
export function isValidApiKeyFormat(key) {
// [核心] 这是一个正则表达式列表。未来支持新的Key格式只需在此处添加新的正则即可。
const patterns = [
// Google Gemini API Key: AIzaSy + 33 characters (alphanumeric, _, -)
/^AIzaSy[\w-]{33}$/,
// OpenAI API Key (新格式): sk- + 48 alphanumeric characters
/^sk-[\w]{48}$/,
// Google AI Studio Key: gsk_ + alphanumeric & hyphens
/^gsk_[\w-]{40,}$/,
// Anthropic API Key (示例): sk-ant-api03- + long string
/^sk-ant-api\d{2}-[\w-]{80,}$/,
// Fallback for other potential "sk-" keys with a reasonable length
/^sk-[\w-]{20,}$/
];
// 使用 .some() 方法只要key匹配列表中的任意一个模式就返回true。
return patterns.some(pattern => pattern.test(key));
}
/**
* [NEW] A simple utility to escape HTML special characters from a string.
* This is a critical security function to prevent XSS attacks when using .innerHTML.
* @param {any} str The input string to escape. If not a string, it's returned as is.
* @returns {string} The escaped, HTML-safe string.
*/
export function escapeHTML(str) {
if (typeof str !== 'string') {
return str;
}
return str.replace(/[&<>"']/g, function(match) {
return {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
}[match];
});
}
// ... 其他未来可能添加的工具函数 ...