// frontend/js/utils/utils.js function debounce(func, wait) { let timeout; const debounced = function(...args) { const context = this; const later = () => { clearTimeout(timeout); func.apply(context, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; debounced.cancel = () => { clearTimeout(timeout); }; return debounced; } function isValidApiKeyFormat(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,}$/ ]; return patterns.some((pattern) => pattern.test(key)); } function escapeHTML(str) { if (typeof str !== "string") { return str; } return str.replace(/[&<>"']/g, function(match) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[match]; }); } export { debounce, isValidApiKeyFormat, escapeHTML };