/** * @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} 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 { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[match]; }); } // ... 其他未来可能添加的工具函数 ...