kimhyunwoo commited on
Commit
ce2c049
Β·
verified Β·
1 Parent(s): 85f43bb

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +349 -0
index.html ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI μ½”λ”© λ„μš°λ―Έ (단일 파일)</title>
7
+ <!-- Monaco Editor λ‘œλ” CSS -->
8
+ <link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/editor/editor.main.min.css">
9
+ <style>
10
+ /* 이전 style.css λ‚΄μš© 전체λ₯Ό 여기에 λΆ™μ—¬λ„£μŠ΅λ‹ˆλ‹€. */
11
+ body {
12
+ font-family: sans-serif;
13
+ margin: 0;
14
+ background-color: #f4f4f4;
15
+ color: #333;
16
+ padding: 20px;
17
+ }
18
+
19
+ .container {
20
+ max-width: 1200px;
21
+ margin: auto;
22
+ background-color: #fff;
23
+ padding: 20px;
24
+ border-radius: 8px;
25
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
26
+ }
27
+
28
+ h1, h2 {
29
+ color: #333;
30
+ text-align: center;
31
+ margin-bottom: 20px;
32
+ }
33
+
34
+ .main-layout {
35
+ display: flex;
36
+ flex-wrap: wrap; /* μž‘μ€ ν™”λ©΄μ—μ„œ μ€„λ°”κΏˆλ˜λ„λ‘ */
37
+ gap: 20px;
38
+ margin-bottom: 20px;
39
+ }
40
+
41
+ .editor-pane, .control-pane {
42
+ flex: 1;
43
+ min-width: 300px; /* μ΅œμ†Œ λ„ˆλΉ„ μ§€μ • */
44
+ padding: 15px;
45
+ border: 1px solid #ddd;
46
+ border-radius: 5px;
47
+ background-color: #f9f9f9;
48
+ }
49
+
50
+ .editor-pane h2, .control-pane h2 {
51
+ margin-top: 0;
52
+ text-align: left;
53
+ }
54
+
55
+ textarea {
56
+ width: calc(100% - 22px); /* padding and border */
57
+ min-height: 80px;
58
+ padding: 10px;
59
+ margin-bottom: 10px;
60
+ border: 1px solid #ccc;
61
+ border-radius: 4px;
62
+ font-size: 14px;
63
+ box-sizing: border-box; /* λ„ˆλΉ„ 계산에 padding, border 포함 */
64
+ }
65
+
66
+ button {
67
+ display: block;
68
+ width: 100%;
69
+ padding: 10px;
70
+ background-color: #007bff;
71
+ color: white;
72
+ border: none;
73
+ border-radius: 4px;
74
+ cursor: pointer;
75
+ font-size: 16px;
76
+ margin-bottom: 10px;
77
+ box-sizing: border-box;
78
+ }
79
+
80
+ button:hover {
81
+ background-color: #0056b3;
82
+ }
83
+
84
+ .editor-actions {
85
+ margin-top: 10px;
86
+ display: flex;
87
+ gap: 10px;
88
+ align-items: center;
89
+ }
90
+
91
+ .editor-actions button {
92
+ width: auto;
93
+ flex-grow: 1;
94
+ }
95
+
96
+ #languageSelect {
97
+ padding: 10px;
98
+ border-radius: 4px;
99
+ border: 1px solid #ccc;
100
+ height: 40px; /* λ²„νŠΌκ³Ό 높이 맞좀 */
101
+ box-sizing: border-box;
102
+ }
103
+
104
+ .preview-pane {
105
+ margin-top: 20px;
106
+ padding: 15px;
107
+ border: 1px solid #ddd;
108
+ border-radius: 5px;
109
+ background-color: #f9f9f9;
110
+ }
111
+
112
+ #html-preview {
113
+ width: 100%;
114
+ height: 400px;
115
+ border: 1px solid #ccc;
116
+ background-color: #fff;
117
+ }
118
+ /* λ‘œλ”© μŠ€ν”Όλ„ˆ μŠ€νƒ€μΌ */
119
+ .loader {
120
+ border: 5px solid #f3f3f3; /* Light grey */
121
+ border-top: 5px solid #3498db; /* Blue */
122
+ border-radius: 50%;
123
+ width: 30px;
124
+ height: 30px;
125
+ animation: spin 1s linear infinite;
126
+ margin: 10px auto;
127
+ display: none; /* κΈ°λ³Έ μˆ¨κΉ€ */
128
+ }
129
+
130
+ @keyframes spin {
131
+ 0% { transform: rotate(0deg); }
132
+ 100% { transform: rotate(360deg); }
133
+ }
134
+ </style>
135
+ </head>
136
+ <body>
137
+ <div class="container">
138
+ <h1>AI μ½”λ”© λ„μš°λ―Έ (단일 파일)</h1>
139
+
140
+ <div class="main-layout">
141
+ <div class="editor-pane">
142
+ <h2>μ½”λ“œ 에디터</h2>
143
+ <div id="editor-container" style="width:100%;height:400px;border:1px solid #ccc;"></div>
144
+ <div class="editor-actions">
145
+ <button id="getCompletionBtn">μ½”λ“œ μžλ™μ™„μ„± (FIM)</button>
146
+ <select id="languageSelect">
147
+ <option value="python">Python</option>
148
+ <option value="javascript" selected>JavaScript</option>
149
+ <option value="html">HTML</option>
150
+ <option value="css">CSS</option>
151
+ <option value="java">Java</option>
152
+ <option value="csharp">C#</option>
153
+ </select>
154
+ </div>
155
+ </div>
156
+
157
+ <div class="control-pane">
158
+ <h2>HTML 생성 (λŒ€ν™”ν˜•)</h2>
159
+ <textarea id="html-prompt" placeholder="예: '빨간색 배경의 ν™˜μ˜ λ©”μ‹œμ§€ νŽ˜μ΄μ§€ λ§Œλ“€μ–΄μ€˜'"></textarea>
160
+ <button id="generateHtmlBtn">HTML 생성 및 미리보기</button>
161
+ <div id="htmlLoader" class="loader"></div>
162
+
163
+ <h2>일반 μ½”λ“œ 생성 (λͺ…λ Ήν˜•)</h2>
164
+ <textarea id="code-prompt" placeholder="예: 'μ£Όμ–΄μ§„ 리슀트λ₯Ό μ •λ ¬ν•˜λŠ” 파이썬 ν•¨μˆ˜ λ§Œλ“€μ–΄μ€˜'"></textarea>
165
+ <button id="generateCodeBtn">μ½”λ“œ 생성</button>
166
+ <div id="codeLoader" class="loader"></div>
167
+ </div>
168
+ </div>
169
+ <div id="completionLoader" class="loader" style="text-align: center; width: 100%;"></div>
170
+
171
+
172
+ <div class="preview-pane">
173
+ <h2>HTML 미리보기</h2>
174
+ <iframe id="html-preview" sandbox="allow-scripts allow-same-origin"></iframe>
175
+ </div>
176
+
177
+ </div>
178
+
179
+ <!-- Monaco Editor λ‘œλ” JS -->
180
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.min.js"></script>
181
+ <script>
182
+ // 이전 script.js λ‚΄μš© 전체λ₯Ό 여기에 λΆ™μ—¬λ„£μŠ΅λ‹ˆλ‹€.
183
+ let editor;
184
+
185
+ // λ‘œλ” μš”μ†Œ κ°€μ Έμ˜€κΈ°
186
+ const htmlLoader = document.getElementById('htmlLoader');
187
+ const codeLoader = document.getElementById('codeLoader');
188
+ const completionLoader = document.getElementById('completionLoader');
189
+
190
+ function showLoader(loaderElement) {
191
+ if (loaderElement) loaderElement.style.display = 'block';
192
+ }
193
+
194
+ function hideLoader(loaderElement) {
195
+ if (loaderElement) loaderElement.style.display = 'none';
196
+ }
197
+
198
+
199
+ require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs' }});
200
+ require(['vs/editor/editor.main'], function() {
201
+ editor = monaco.editor.create(document.getElementById('editor-container'), {
202
+ value: [
203
+ '// 여기에 μ½”λ“œλ₯Ό μž…λ ₯ν•˜κ±°λ‚˜ AIμ—κ²Œ 생성을 μš”μ²­ν•˜μ„Έμš”.',
204
+ '// HTML 생성 μ‹œ 이곳과 미리보기 창에 κ²°κ³Όκ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.'
205
+ ].join('\n'),
206
+ language: 'javascript', // κΈ°λ³Έ μ–Έμ–΄
207
+ theme: 'vs-dark',
208
+ automaticLayout: true,
209
+ wordWrap: 'on', // μžλ™ μ€„λ°”κΏˆ
210
+ minimap: { enabled: false } // λ―Έλ‹ˆλ§΅ λΉ„ν™œμ„±ν™” (선택 사항)
211
+ });
212
+ });
213
+
214
+ const htmlPromptEl = document.getElementById('html-prompt');
215
+ const codePromptEl = document.getElementById('code-prompt');
216
+ const generateHtmlBtn = document.getElementById('generateHtmlBtn');
217
+ const generateCodeBtn = document.getElementById('generateCodeBtn');
218
+ const getCompletionBtn = document.getElementById('getCompletionBtn');
219
+ const languageSelect = document.getElementById('languageSelect');
220
+ const htmlPreviewEl = document.getElementById('html-preview');
221
+
222
+ languageSelect.addEventListener('change', (event) => {
223
+ if (editor) {
224
+ monaco.editor.setModelLanguage(editor.getModel(), event.target.value);
225
+ }
226
+ });
227
+
228
+ generateHtmlBtn.addEventListener('click', async () => {
229
+ const prompt = htmlPromptEl.value.trim();
230
+ if (!prompt) {
231
+ alert('HTML 생성 μš”μ²­ λ‚΄μš©μ„ μž…λ ₯ν•΄μ£Όμ„Έμš”.');
232
+ return;
233
+ }
234
+ showLoader(htmlLoader);
235
+ generateHtmlBtn.disabled = true;
236
+
237
+ try {
238
+ const response = await fetch('/api/generate-html', {
239
+ method: 'POST',
240
+ headers: { 'Content-Type': 'application/json' },
241
+ body: JSON.stringify({ prompt })
242
+ });
243
+ if (!response.ok) {
244
+ const errorData = await response.json();
245
+ throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
246
+ }
247
+ const data = await response.json();
248
+ if (editor) {
249
+ editor.setValue(data.html);
250
+ monaco.editor.setModelLanguage(editor.getModel(), 'html');
251
+ }
252
+ htmlPreviewEl.srcdoc = data.html;
253
+ } catch (error) {
254
+ console.error('HTML 생성 였λ₯˜:', error);
255
+ alert(`HTML 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: ${error.message}`);
256
+ if (editor) editor.setValue(`// HTML 생성 였λ₯˜: ${error.message}`);
257
+ htmlPreviewEl.srcdoc = `<h3>였λ₯˜ λ°œμƒ</h3><p>${error.message}</p>`;
258
+ } finally {
259
+ hideLoader(htmlLoader);
260
+ generateHtmlBtn.disabled = false;
261
+ }
262
+ });
263
+
264
+ generateCodeBtn.addEventListener('click', async () => {
265
+ const prompt = codePromptEl.value.trim();
266
+ const language = languageSelect.value;
267
+ if (!prompt) {
268
+ alert('μ½”λ“œ 생성 μš”μ²­ λ‚΄μš©μ„ μž…λ ₯ν•΄μ£Όμ„Έμš”.');
269
+ return;
270
+ }
271
+ showLoader(codeLoader);
272
+ generateCodeBtn.disabled = true;
273
+
274
+ try {
275
+ const response = await fetch('/api/generate-code', {
276
+ method: 'POST',
277
+ headers: { 'Content-Type': 'application/json' },
278
+ body: JSON.stringify({ prompt, language })
279
+ });
280
+ if (!response.ok) {
281
+ const errorData = await response.json();
282
+ throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
283
+ }
284
+ const data = await response.json();
285
+ if (editor) {
286
+ editor.setValue(data.code);
287
+ monaco.editor.setModelLanguage(editor.getModel(), language);
288
+ }
289
+ } catch (error) {
290
+ console.error('μ½”λ“œ 생성 였λ₯˜:', error);
291
+ alert(`μ½”λ“œ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: ${error.message}`);
292
+ if (editor) editor.setValue(`// μ½”λ“œ 생성 였λ₯˜: ${error.message}`);
293
+ } finally {
294
+ hideLoader(codeLoader);
295
+ generateCodeBtn.disabled = false;
296
+ }
297
+ });
298
+
299
+ getCompletionBtn.addEventListener('click', async () => {
300
+ if (!editor) return;
301
+
302
+ const model = editor.getModel();
303
+ const position = editor.getPosition();
304
+ const fullCode = model.getValue();
305
+ // FIM APIλŠ” 빈 prefix/suffix도 ν—ˆμš©ν•˜λ―€λ‘œ, μ»€μ„œ μœ„μΉ˜ κΈ°μ€€μœΌλ‘œ λ‚˜λˆ”
306
+ const offset = model.getOffsetAt(position);
307
+ const prefix = fullCode.substring(0, offset);
308
+ const suffix = fullCode.substring(offset);
309
+ const language = languageSelect.value; // ν˜„μž¬ μ„ νƒλœ 에디터 μ–Έμ–΄
310
+
311
+ // FIM은 빈 λ‚΄μš©μ—μ„œλ„ μž‘λ™ν•  수 μžˆμœΌλ‚˜, μ‚¬μš©μžκ°€ ν˜Όλž€μŠ€λŸ¬μšΈ 수 μžˆμœΌλ―€λ‘œ μ΅œμ†Œν•œμ˜ μž…λ ₯ μœ λ„ (선택사항)
312
+ // if (prefix.trim() === '' && suffix.trim() === '') {
313
+ // alert('μžλ™μ™„μ„±μ„ μœ„ν•΄ μ½”λ“œ 일뢀λ₯Ό μž…λ ₯ν•˜κ±°λ‚˜ μ»€μ„œλ₯Ό νŠΉμ • μœ„μΉ˜μ— λ‘μ„Έμš”.');
314
+ // return;
315
+ // }
316
+ showLoader(completionLoader);
317
+ getCompletionBtn.disabled = true;
318
+
319
+ try {
320
+ const response = await fetch('/api/complete-code', {
321
+ method: 'POST',
322
+ headers: { 'Content-Type': 'application/json' },
323
+ body: JSON.stringify({ prefix, suffix, language }) // languageλŠ” FIM APIμ—μ„œ 직접 μ‚¬μš© μ•ˆλ  수 있음
324
+ });
325
+ if (!response.ok) {
326
+ const errorData = await response.json();
327
+ throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
328
+ }
329
+ const data = await response.json();
330
+
331
+ // ν˜„μž¬ μ»€μ„œ μœ„μΉ˜μ— μ œμ•ˆλœ μ½”λ“œλ₯Ό μ‚½μž…
332
+ editor.executeEdits("api-completion", [{
333
+ range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
334
+ text: data.completion
335
+ }]);
336
+ // μ‚½μž… ν›„ μ»€μ„œλ₯Ό μ œμ•ˆλœ ν…μŠ€νŠΈμ˜ 끝으둜 이동 (선택 사항)
337
+ // editor.setPosition(editor.getModel().getFullModelRange().getEndPosition());
338
+
339
+ } catch (error) {
340
+ console.error('μ½”λ“œ μžλ™μ™„μ„± 였λ₯˜:', error);
341
+ alert(`μ½”λ“œ μžλ™μ™„μ„± 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: ${error.message}`);
342
+ } finally {
343
+ hideLoader(completionLoader);
344
+ getCompletionBtn.disabled = false;
345
+ }
346
+ });
347
+ </script>
348
+ </body>
349
+ </html>