File size: 3,702 Bytes
2b19ed8
 
a27839e
2b19ed8
 
a27839e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2b19ed8
 
a27839e
 
 
 
 
 
 
 
2b19ed8
 
 
 
 
a27839e
2b19ed8
 
 
 
 
 
 
a27839e
2b19ed8
 
a27839e
2b19ed8
 
 
 
 
 
 
 
a27839e
2b19ed8
 
193d4ed
2b19ed8
a27839e
2b19ed8
 
a27839e
2b19ed8
 
 
 
 
 
 
 
a27839e
 
2b19ed8
 
 
 
 
 
a27839e
2b19ed8
a27839e
 
 
 
 
 
 
 
 
2b19ed8
 
 
 
a27839e
 
2b19ed8
 
 
a27839e
2b19ed8
 
a27839e
2b19ed8
a27839e
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// 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;
  }
};