Spaces:
Running
Running
| // Removed direct use of @google/generative-ai on the client | |
| // and switched to calling backend endpoints that securely use the API key at runtime. | |
| // Keep js-beautify formatting utilities and transformers for compatibility | |
| import { html as formatHtml } from "js-beautify"; | |
| export interface ChatMessage { | |
| role: string; | |
| content: string; | |
| } | |
| // Helper function to format HTML with proper indentation | |
| const formatCode = (code: string): string => { | |
| try { | |
| return formatHtml(code, { | |
| indent_size: 2, | |
| indent_char: ' ', | |
| max_preserve_newlines: 2, | |
| preserve_newlines: true, | |
| indent_inner_html: true, | |
| brace_style: 'collapse', | |
| indent_scripts: 'normal', | |
| wrap_line_length: 120, | |
| unformatted: ['code', 'pre', 'em', 'strong', 'span'], | |
| content_unformatted: ['pre', 'code'], | |
| }); | |
| } catch (error) { | |
| console.error('Error formatting code:', error); | |
| return code; | |
| } | |
| }; | |
| // Helper function to transform the AI's array response into the object format our app uses. | |
| const transformApiResponse = (response: { files: { fileName: string; code: string }[] }): Record<string, string> => { | |
| const filesRecord: Record<string, string> = {}; | |
| if (response && (response as any).files && Array.isArray((response as any).files)) { | |
| for (const file of (response as any).files) { | |
| if (file.fileName && file.code) { | |
| filesRecord[file.fileName] = formatCode(file.code); | |
| } | |
| } | |
| } | |
| return filesRecord; | |
| }; | |
| // Normalize server response to the same shape as before | |
| const normalizeResponse = (data: any): Record<string, string> => { | |
| // If server already returns a Record<string, string> | |
| if (data && typeof data === 'object' && !Array.isArray(data) && !('files' in data)) { | |
| return data as Record<string, string>; | |
| } | |
| // If server returns { files: [...] } | |
| if (data && typeof data === 'object' && 'files' in data) { | |
| return transformApiResponse(data as { files: { fileName: string; code: string }[] }); | |
| } | |
| // Fallback to empty object | |
| return {}; | |
| }; | |
| const handleError = async (res: Response) => { | |
| let message = 'Unknown error'; | |
| try { | |
| const body = await res.json(); | |
| if (body && body.error) { | |
| message = String(body.error); | |
| } | |
| } catch { | |
| // ignore JSON parse errors | |
| message = res.statusText || message; | |
| } | |
| // Preserve prior UX message for missing key to avoid UI changes | |
| if (message.toLowerCase().includes('gemini api key is not configured')) { | |
| throw new Error('Gemini API key is not configured. Please set GEMINI_API_KEY in your Hugging Face Space secrets.'); | |
| } | |
| throw new Error(message); | |
| }; | |
| export const generateCode = async ( | |
| messages: ChatMessage[] | |
| ): Promise<Record<string, string>> => { | |
| try { | |
| const res = await fetch('/api/generate-code', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ messages }), | |
| }); | |
| if (!res.ok) { | |
| await handleError(res); | |
| } | |
| const data = await res.json(); | |
| return normalizeResponse(data); | |
| } catch (error) { | |
| console.error('Error in generateCode:', error); | |
| throw error; | |
| } | |
| }; | |
| export const modifyCode = async ( | |
| currentFiles: Record<string, string>, | |
| modificationRequest: string | |
| ): Promise<Record<string, string>> => { | |
| try { | |
| const res = await fetch('/api/modify-code', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ currentFiles, modificationRequest }), | |
| }); | |
| if (!res.ok) { | |
| await handleError(res); | |
| } | |
| const data = await res.json(); | |
| return normalizeResponse(data); | |
| } catch (error) { | |
| console.error('Error in modifyCode:', error); | |
| throw error; | |
| } | |
| }; |