vg-coder-cli 2.0.6 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/server/api-server.js +3 -0
- package/src/server/views/dashboard.css +488 -0
- package/src/server/views/dashboard.html +86 -646
- package/src/server/views/dashboard.js +457 -0
- package/src/server/views/js/api.js +101 -0
- package/src/server/views/js/config.js +120 -0
- package/src/server/views/js/handlers.js +214 -0
- package/src/server/views/js/main.js +72 -0
- package/src/server/views/js/utils.js +72 -0
- package/vg-coder-cli-2.0.8.tgz +0 -0
- package/vg-coder-cli-1.0.17.tgz +0 -0
- package/vg-coder-cli-2.0.4.tgz +0 -0
- package/vg-coder-cli-2.0.5.tgz +0 -0
- package/vg-coder-cli-2.0.6.tgz +0 -0
|
@@ -7,673 +7,113 @@
|
|
|
7
7
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
8
8
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
9
9
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
10
|
-
<title>VG Coder
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
--ios-text-secondary: #8E8E93;
|
|
20
|
-
--ios-separator: #C6C6C8;
|
|
21
|
-
--ios-input-bg: #E5E5EA;
|
|
22
|
-
--font-stack: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
* {
|
|
26
|
-
margin: 0;
|
|
27
|
-
padding: 0;
|
|
28
|
-
box-sizing: border-box;
|
|
29
|
-
-webkit-tap-highlight-color: transparent;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
body {
|
|
33
|
-
font-family: var(--font-stack);
|
|
34
|
-
background-color: var(--ios-bg);
|
|
35
|
-
color: var(--ios-text-primary);
|
|
36
|
-
min-height: 100vh;
|
|
37
|
-
padding-bottom: 40px;
|
|
38
|
-
font-size: 17px;
|
|
39
|
-
line-height: 1.5;
|
|
40
|
-
-webkit-font-smoothing: antialiased;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/* Glass Header */
|
|
44
|
-
.header-nav {
|
|
45
|
-
position: sticky;
|
|
46
|
-
top: 0;
|
|
47
|
-
z-index: 100;
|
|
48
|
-
background: rgba(255, 255, 255, 0.85);
|
|
49
|
-
backdrop-filter: blur(20px);
|
|
50
|
-
-webkit-backdrop-filter: blur(20px);
|
|
51
|
-
border-bottom: 0.5px solid rgba(0, 0, 0, 0.1);
|
|
52
|
-
padding: 15px 20px;
|
|
53
|
-
display: flex;
|
|
54
|
-
justify-content: space-between;
|
|
55
|
-
align-items: center;
|
|
56
|
-
padding-top: max(15px, env(safe-area-inset-top));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.app-title {
|
|
60
|
-
font-weight: 700;
|
|
61
|
-
font-size: 1.2rem;
|
|
62
|
-
letter-spacing: -0.5px;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.status-badge {
|
|
66
|
-
font-size: 0.75rem;
|
|
67
|
-
font-weight: 600;
|
|
68
|
-
padding: 4px 10px;
|
|
69
|
-
border-radius: 20px;
|
|
70
|
-
background: var(--ios-green);
|
|
71
|
-
color: white;
|
|
72
|
-
box-shadow: 0 2px 4px rgba(52, 199, 89, 0.2);
|
|
73
|
-
transition: all 0.3s ease;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.container {
|
|
77
|
-
max-width: 800px;
|
|
78
|
-
margin: 0 auto;
|
|
79
|
-
padding: 20px;
|
|
80
|
-
padding-left: max(20px, env(safe-area-inset-left));
|
|
81
|
-
padding-right: max(20px, env(safe-area-inset-right));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* iOS Cards */
|
|
85
|
-
.card {
|
|
86
|
-
background: var(--ios-card);
|
|
87
|
-
border-radius: 20px;
|
|
88
|
-
padding: 24px;
|
|
89
|
-
margin-bottom: 24px;
|
|
90
|
-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
|
91
|
-
position: relative;
|
|
92
|
-
overflow: hidden;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.card-header {
|
|
96
|
-
display: flex;
|
|
97
|
-
align-items: center;
|
|
98
|
-
margin-bottom: 16px;
|
|
99
|
-
gap: 12px;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.card-icon {
|
|
103
|
-
width: 40px;
|
|
104
|
-
height: 40px;
|
|
105
|
-
border-radius: 12px;
|
|
106
|
-
background: var(--ios-bg);
|
|
107
|
-
display: flex;
|
|
108
|
-
align-items: center;
|
|
109
|
-
justify-content: center;
|
|
110
|
-
font-size: 1.2rem;
|
|
111
|
-
color: var(--ios-blue);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.card-title {
|
|
115
|
-
font-size: 1.1rem;
|
|
116
|
-
font-weight: 700;
|
|
117
|
-
letter-spacing: -0.3px;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.card-desc {
|
|
121
|
-
color: var(--ios-text-secondary);
|
|
122
|
-
font-size: 0.9rem;
|
|
123
|
-
margin-bottom: 20px;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/* Forms */
|
|
127
|
-
.form-group {
|
|
128
|
-
margin-bottom: 20px;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.form-label {
|
|
132
|
-
display: block;
|
|
133
|
-
font-size: 0.85rem;
|
|
134
|
-
font-weight: 600;
|
|
135
|
-
color: var(--ios-text-secondary);
|
|
136
|
-
margin-bottom: 8px;
|
|
137
|
-
text-transform: uppercase;
|
|
138
|
-
letter-spacing: 0.5px;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
input[type="text"],
|
|
142
|
-
textarea {
|
|
143
|
-
width: 100%;
|
|
144
|
-
background: var(--ios-input-bg);
|
|
145
|
-
border: none;
|
|
146
|
-
border-radius: 12px;
|
|
147
|
-
padding: 14px 16px;
|
|
148
|
-
font-size: 1rem;
|
|
149
|
-
font-family: inherit;
|
|
150
|
-
color: var(--ios-text-primary);
|
|
151
|
-
transition: all 0.2s;
|
|
152
|
-
-webkit-appearance: none;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
textarea {
|
|
156
|
-
min-height: 120px;
|
|
157
|
-
resize: vertical;
|
|
158
|
-
line-height: 1.4;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
input:focus,
|
|
162
|
-
textarea:focus {
|
|
163
|
-
outline: none;
|
|
164
|
-
background: #D1D1D6;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/* Buttons */
|
|
168
|
-
.btn-group {
|
|
169
|
-
display: grid;
|
|
170
|
-
grid-template-columns: 1fr 1fr;
|
|
171
|
-
gap: 12px;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.btn {
|
|
175
|
-
border: none;
|
|
176
|
-
background: var(--ios-blue);
|
|
177
|
-
color: white;
|
|
178
|
-
padding: 14px;
|
|
179
|
-
border-radius: 14px;
|
|
180
|
-
font-size: 1rem;
|
|
181
|
-
font-weight: 600;
|
|
182
|
-
cursor: pointer;
|
|
183
|
-
transition: transform 0.1s, opacity 0.2s;
|
|
184
|
-
display: flex;
|
|
185
|
-
align-items: center;
|
|
186
|
-
justify-content: center;
|
|
187
|
-
gap: 8px;
|
|
188
|
-
-webkit-appearance: none;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.btn:active {
|
|
192
|
-
transform: scale(0.98);
|
|
193
|
-
opacity: 0.8;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.btn-secondary {
|
|
197
|
-
background: rgba(0, 122, 255, 0.15);
|
|
198
|
-
color: var(--ios-blue);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/* Full width button */
|
|
202
|
-
.btn-full {
|
|
203
|
-
grid-column: 1 / -1;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
.btn-copy {
|
|
207
|
-
background: #E5E5EA;
|
|
208
|
-
color: black;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.btn-copy.copied {
|
|
212
|
-
background: var(--ios-green);
|
|
213
|
-
color: white;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/* System Prompt Accordion */
|
|
217
|
-
.prompt-accordion {
|
|
218
|
-
cursor: pointer;
|
|
219
|
-
user-select: none;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.prompt-header {
|
|
223
|
-
display: flex;
|
|
224
|
-
justify-content: space-between;
|
|
225
|
-
align-items: center;
|
|
226
|
-
padding: 4px 0;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.chevron {
|
|
230
|
-
color: var(--ios-text-secondary);
|
|
231
|
-
transition: transform 0.3s ease;
|
|
232
|
-
font-size: 0.8rem;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.prompt-content {
|
|
236
|
-
max-height: 0;
|
|
237
|
-
overflow: hidden;
|
|
238
|
-
transition: max-height 0.4s cubic-bezier(0.65, 0, 0.35, 1);
|
|
239
|
-
opacity: 0;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
.prompt-content.open {
|
|
243
|
-
max-height: 500px;
|
|
244
|
-
opacity: 1;
|
|
245
|
-
margin-top: 15px;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.code-block {
|
|
249
|
-
background: #2c2c2e;
|
|
250
|
-
color: #fff;
|
|
251
|
-
padding: 15px;
|
|
252
|
-
border-radius: 12px;
|
|
253
|
-
font-family: 'SF Mono', 'Menlo', monospace;
|
|
254
|
-
font-size: 0.85rem;
|
|
255
|
-
overflow-x: auto;
|
|
256
|
-
white-space: pre-wrap;
|
|
257
|
-
margin-bottom: 15px;
|
|
258
|
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/* Response Area */
|
|
262
|
-
.response {
|
|
263
|
-
margin-top: 20px;
|
|
264
|
-
background: #F9F9F9;
|
|
265
|
-
border-radius: 12px;
|
|
266
|
-
padding: 15px;
|
|
267
|
-
border-left: 5px solid var(--ios-text-secondary);
|
|
268
|
-
display: none;
|
|
269
|
-
animation: slideDown 0.3s ease;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.response.show {
|
|
273
|
-
display: block;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.response.success {
|
|
277
|
-
border-left-color: var(--ios-green);
|
|
278
|
-
background: #F0FFF4;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.response.error {
|
|
282
|
-
border-left-color: var(--ios-red);
|
|
283
|
-
background: #FFF5F5;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.response pre {
|
|
287
|
-
font-family: 'SF Mono', 'Menlo', monospace;
|
|
288
|
-
font-size: 0.85rem;
|
|
289
|
-
white-space: pre-wrap;
|
|
290
|
-
word-break: break-all;
|
|
291
|
-
color: #333;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/* Toast */
|
|
295
|
-
.toast {
|
|
296
|
-
position: fixed;
|
|
297
|
-
top: 20px;
|
|
298
|
-
left: 50%;
|
|
299
|
-
transform: translateX(-50%) translateY(-100px);
|
|
300
|
-
background: rgba(0, 0, 0, 0.8);
|
|
301
|
-
backdrop-filter: blur(10px);
|
|
302
|
-
color: white;
|
|
303
|
-
padding: 12px 24px;
|
|
304
|
-
border-radius: 50px;
|
|
305
|
-
font-weight: 600;
|
|
306
|
-
font-size: 0.95rem;
|
|
307
|
-
z-index: 1000;
|
|
308
|
-
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
309
|
-
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
310
|
-
display: flex;
|
|
311
|
-
align-items: center;
|
|
312
|
-
gap: 8px;
|
|
313
|
-
white-space: nowrap;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
.toast.show {
|
|
317
|
-
transform: translateX(-50%) translateY(0);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
.loading-spinner {
|
|
321
|
-
width: 18px;
|
|
322
|
-
height: 18px;
|
|
323
|
-
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
324
|
-
border-radius: 50%;
|
|
325
|
-
border-top-color: white;
|
|
326
|
-
animation: spin 0.8s linear infinite;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
@keyframes spin {
|
|
330
|
-
to {
|
|
331
|
-
transform: rotate(360deg);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
@keyframes slideDown {
|
|
336
|
-
from {
|
|
337
|
-
opacity: 0;
|
|
338
|
-
transform: translateY(-10px);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
to {
|
|
342
|
-
opacity: 1;
|
|
343
|
-
transform: translateY(0);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
</style>
|
|
10
|
+
<title>VG Coder API Dashboard</title>
|
|
11
|
+
<link rel="stylesheet" href="/dashboard.css">
|
|
12
|
+
<script>
|
|
13
|
+
// Pre-load theme to prevent flash
|
|
14
|
+
(function() {
|
|
15
|
+
const savedTheme = localStorage.getItem('theme') || 'light';
|
|
16
|
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
17
|
+
})();
|
|
18
|
+
</script>
|
|
347
19
|
</head>
|
|
348
20
|
|
|
349
21
|
<body>
|
|
350
|
-
<!-- Glass Header -->
|
|
351
|
-
<nav class="header-nav">
|
|
352
|
-
<div class="app-title">VG Coder</div>
|
|
353
|
-
<div id="status" class="status-badge">● Offline</div>
|
|
354
|
-
</nav>
|
|
355
|
-
|
|
356
22
|
<div class="container">
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
<
|
|
362
|
-
|
|
363
|
-
<div class="card-title">System Prompt</div>
|
|
364
|
-
</div>
|
|
365
|
-
<div class="chevron" id="toggle-icon">▼</div>
|
|
366
|
-
</div>
|
|
367
|
-
<div class="prompt-content" id="system-prompt-content">
|
|
368
|
-
<div class="code-block" id="prompt-text"></div>
|
|
369
|
-
<button class="btn btn-secondary btn-full" onclick="copySystemPrompt()">
|
|
370
|
-
<span id="copy-icon">📋</span>
|
|
371
|
-
<span id="copy-text">Copy Prompt</span>
|
|
372
|
-
</button>
|
|
23
|
+
<div class="header">
|
|
24
|
+
<div class="header-content">
|
|
25
|
+
<span class="status" id="status">● Server Starting...</span>
|
|
26
|
+
<div style="height: 10px;"></div>
|
|
27
|
+
<h1>VG Coder</h1>
|
|
28
|
+
<p>API Dashboard & Automation</p>
|
|
373
29
|
</div>
|
|
30
|
+
<button class="theme-toggle" id="theme-toggle" title="Toggle Dark Mode">
|
|
31
|
+
<span id="theme-icon">🌙</span>
|
|
32
|
+
</button>
|
|
374
33
|
</div>
|
|
375
34
|
|
|
376
|
-
<!--
|
|
377
|
-
<div class="card">
|
|
378
|
-
<div class="
|
|
379
|
-
<div class="
|
|
380
|
-
|
|
381
|
-
|
|
35
|
+
<!-- System Prompt Section -->
|
|
36
|
+
<div class="system-prompt-card">
|
|
37
|
+
<div class="system-prompt-header" onclick="toggleSystemPrompt()">
|
|
38
|
+
<div class="header-title-group">
|
|
39
|
+
<button class="btn-icon-head" onclick="copySystemPromptFromHeader(event)" title="Copy System Prompt">
|
|
40
|
+
📋
|
|
41
|
+
</button>
|
|
42
|
+
<h2>System Prompt</h2>
|
|
382
43
|
</div>
|
|
44
|
+
<span class="toggle-icon" id="toggle-icon">▼</span>
|
|
383
45
|
</div>
|
|
384
|
-
<div class="
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
</div>
|
|
390
|
-
|
|
391
|
-
<div class="btn-group">
|
|
392
|
-
<button class="btn" onclick="testAnalyze()">
|
|
393
|
-
<span>📥</span> Download
|
|
394
|
-
</button>
|
|
395
|
-
<button class="btn btn-secondary" onclick="copyAnalyzeResult()">
|
|
396
|
-
<span id="analyze-copy-icon">📋</span> Copy
|
|
46
|
+
<div class="system-prompt-content" id="system-prompt-content">
|
|
47
|
+
<div class="prompt-text" id="prompt-text"></div>
|
|
48
|
+
<button class="btn btn-copy" onclick="copySystemPrompt()">
|
|
49
|
+
<span id="copy-icon">📋</span>
|
|
50
|
+
<span id="copy-text">Copy System Prompt</span>
|
|
397
51
|
</button>
|
|
398
52
|
</div>
|
|
399
|
-
<div class="response" id="analyze-response"></div>
|
|
400
53
|
</div>
|
|
401
54
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
<div class="card
|
|
405
|
-
<div class="
|
|
406
|
-
|
|
407
|
-
<div class="
|
|
55
|
+
<div class="endpoints">
|
|
56
|
+
<!-- Analyze -->
|
|
57
|
+
<div class="endpoint-card">
|
|
58
|
+
<div class="endpoint-header">
|
|
59
|
+
<!-- Left side: Method + Path -->
|
|
60
|
+
<div class="endpoint-title-group">
|
|
61
|
+
<span class="method post">POST</span>
|
|
62
|
+
<span class="endpoint-path">/api/analyze</span>
|
|
63
|
+
</div>
|
|
64
|
+
<!-- Right side: Download Icon -->
|
|
65
|
+
<button class="btn-icon-head" onclick="testAnalyze()" title="Download Project Source">
|
|
66
|
+
📥
|
|
67
|
+
</button>
|
|
408
68
|
</div>
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
<
|
|
69
|
+
<p class="endpoint-desc">Phân tích dự án và lấy toàn bộ source code.</p>
|
|
70
|
+
<div class="form-group">
|
|
71
|
+
<label>Path</label>
|
|
72
|
+
<input type="text" id="analyze-path" value="." placeholder="Project path (e.g. .)">
|
|
73
|
+
</div>
|
|
74
|
+
<div class="btn-group">
|
|
75
|
+
<!-- Big Download button removed as it's now in the header -->
|
|
76
|
+
<button class="btn btn-copy" onclick="copyAnalyzeResult()">
|
|
77
|
+
<span id="analyze-copy-icon">📋</span>
|
|
78
|
+
<span id="analyze-copy-text">Copy Text</span>
|
|
79
|
+
</button>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="response" id="analyze-response"></div>
|
|
415
82
|
</div>
|
|
416
83
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
84
|
+
<!-- Execute Bash -->
|
|
85
|
+
<div class="endpoint-card">
|
|
86
|
+
<div class="endpoint-header">
|
|
87
|
+
<div class="endpoint-title-group">
|
|
88
|
+
<span class="method post">POST</span>
|
|
89
|
+
<span class="endpoint-path">/api/execute</span>
|
|
90
|
+
</div>
|
|
91
|
+
<!-- Could add an execute icon here later if needed -->
|
|
92
|
+
</div>
|
|
93
|
+
<p class="endpoint-desc">Thực thi bash script với syntax validation.</p>
|
|
94
|
+
<div class="form-group">
|
|
95
|
+
<label>Bash Script</label>
|
|
96
|
+
<textarea id="execute-bash"
|
|
97
|
+
placeholder="mkdir -p src/test echo 'Hello' > src/test/hello.txt"></textarea>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="btn-group">
|
|
100
|
+
<button class="btn" onclick="testExecute()">
|
|
101
|
+
<span>▶️</span>
|
|
102
|
+
<span>Run Script</span>
|
|
103
|
+
</button>
|
|
104
|
+
<button class="btn" onclick="executeFromClipboard()">
|
|
105
|
+
<span>📋</span>
|
|
106
|
+
<span>Paste & Run</span>
|
|
107
|
+
</button>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="response" id="execute-response"></div>
|
|
424
110
|
</div>
|
|
425
|
-
<div class="response" id="execute-response"></div>
|
|
426
111
|
</div>
|
|
427
112
|
</div>
|
|
428
113
|
|
|
429
|
-
<!-- iOS Toast -->
|
|
430
114
|
<div class="toast" id="toast"></div>
|
|
431
115
|
|
|
432
|
-
<script>
|
|
433
|
-
const API_BASE = window.location.origin;
|
|
434
|
-
let lastAnalyzeResult = null;
|
|
435
|
-
|
|
436
|
-
const SYSTEM_PROMPT = `# VG Coder AI System Prompt
|
|
437
|
-
|
|
438
|
-
## Command Prefixes
|
|
439
|
-
### /ask - Q&A Mode
|
|
440
|
-
### /plan - Planning Mode
|
|
441
|
-
### /fix - Bug Fix Mode
|
|
442
|
-
### /code - Code Generation Mode
|
|
443
|
-
|
|
444
|
-
## ⚠️ QUY TẮC BẮT BUỘC /code
|
|
445
|
-
|
|
446
|
-
1. **Format Script Chuẩn:**
|
|
447
|
-
\`\`\`bash
|
|
448
|
-
mkdir -p $(dirname "path/to/file.ext")
|
|
449
|
-
cat <<'EOF' > path/to/file.ext
|
|
450
|
-
... content ...
|
|
451
|
-
EOF
|
|
452
|
-
\`\`\`
|
|
453
|
-
|
|
454
|
-
2. **Quy tắc:**
|
|
455
|
-
- Luôn có mkdir -p
|
|
456
|
-
- Dùng <<'EOF' (có quotes)
|
|
457
|
-
- Chỉ include files thay đổi
|
|
458
|
-
`;
|
|
459
|
-
|
|
460
|
-
document.getElementById('prompt-text').textContent = SYSTEM_PROMPT;
|
|
461
|
-
|
|
462
|
-
function toggleSystemPrompt() {
|
|
463
|
-
const content = document.getElementById('system-prompt-content');
|
|
464
|
-
const icon = document.getElementById('toggle-icon');
|
|
465
|
-
content.classList.toggle('open');
|
|
466
|
-
icon.style.transform = content.classList.contains('open') ? 'rotate(180deg)' : 'rotate(0deg)';
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function showResponse(elementId, data, isError = false) {
|
|
470
|
-
const el = document.getElementById(elementId);
|
|
471
|
-
el.className = 'response show ' + (isError ? 'error' : 'success');
|
|
472
|
-
|
|
473
|
-
// Format nice JSON
|
|
474
|
-
if (typeof data === 'object') {
|
|
475
|
-
if (data.stdout) data.stdout = data.stdout.trim();
|
|
476
|
-
if (data.stderr) data.stderr = data.stderr.trim();
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
el.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function showLoading(button, originalText) {
|
|
483
|
-
button.disabled = true;
|
|
484
|
-
button.innerHTML = '<div class="loading-spinner"></div>';
|
|
485
|
-
button.dataset.originalText = originalText;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function resetButton(button) {
|
|
489
|
-
button.disabled = false;
|
|
490
|
-
button.innerHTML = button.dataset.originalText;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function showToast(message, type = 'success') {
|
|
494
|
-
const toast = document.getElementById('toast');
|
|
495
|
-
let icon = type === 'success' ? '✅' : (type === 'error' ? '❌' : 'ℹ️');
|
|
496
|
-
toast.innerHTML = `${icon} ${message}`;
|
|
497
|
-
toast.classList.add('show');
|
|
498
|
-
setTimeout(() => toast.classList.remove('show'), 3000);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// --- ROBUST COPY FUNCTION (MODERN + FALLBACK) ---
|
|
502
|
-
function fallbackCopyTextToClipboard(text) {
|
|
503
|
-
var textArea = document.createElement("textarea");
|
|
504
|
-
textArea.value = text;
|
|
505
|
-
|
|
506
|
-
// Ensure textarea is not visible but part of DOM
|
|
507
|
-
textArea.style.position = "fixed";
|
|
508
|
-
textArea.style.left = "-9999px";
|
|
509
|
-
textArea.style.top = "0";
|
|
510
|
-
document.body.appendChild(textArea);
|
|
511
|
-
|
|
512
|
-
textArea.focus();
|
|
513
|
-
textArea.select();
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
var successful = document.execCommand('copy');
|
|
517
|
-
return successful;
|
|
518
|
-
} catch (err) {
|
|
519
|
-
console.error('Fallback: Oops, unable to copy', err);
|
|
520
|
-
return false;
|
|
521
|
-
} finally {
|
|
522
|
-
document.body.removeChild(textArea);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
async function copyToClipboard(text, btnElement, successIconId, successTextId) {
|
|
527
|
-
if (!text) return showToast('Nothing to copy', 'error');
|
|
528
|
-
|
|
529
|
-
const updateUI = () => {
|
|
530
|
-
if (btnElement) {
|
|
531
|
-
btnElement.classList.add('copied');
|
|
532
|
-
const icon = document.getElementById(successIconId);
|
|
533
|
-
const label = document.getElementById(successTextId);
|
|
534
|
-
|
|
535
|
-
const originalIcon = icon.textContent;
|
|
536
|
-
const originalLabel = label.textContent;
|
|
537
|
-
|
|
538
|
-
icon.textContent = '✓';
|
|
539
|
-
label.textContent = 'Copied';
|
|
540
|
-
|
|
541
|
-
setTimeout(() => {
|
|
542
|
-
btnElement.classList.remove('copied');
|
|
543
|
-
icon.textContent = originalIcon;
|
|
544
|
-
label.textContent = originalLabel;
|
|
545
|
-
}, 2000);
|
|
546
|
-
}
|
|
547
|
-
showToast('Copied to clipboard!');
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
// Try Modern API first
|
|
551
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
552
|
-
try {
|
|
553
|
-
await navigator.clipboard.writeText(text);
|
|
554
|
-
updateUI();
|
|
555
|
-
} catch (err) {
|
|
556
|
-
console.warn('Modern copy failed, trying fallback', err);
|
|
557
|
-
// Try fallback if modern API fails
|
|
558
|
-
if (fallbackCopyTextToClipboard(text)) {
|
|
559
|
-
updateUI();
|
|
560
|
-
} else {
|
|
561
|
-
showToast('Failed to copy', 'error');
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
} else {
|
|
565
|
-
// Use fallback immediately if API doesn't exist
|
|
566
|
-
if (fallbackCopyTextToClipboard(text)) {
|
|
567
|
-
updateUI();
|
|
568
|
-
} else {
|
|
569
|
-
showToast('Failed to copy', 'error');
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// --- Logic Handlers ---
|
|
575
|
-
|
|
576
|
-
function copySystemPrompt() {
|
|
577
|
-
copyToClipboard(SYSTEM_PROMPT, event.target.closest('.btn'), 'copy-icon', 'copy-text');
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
async function testAnalyze() {
|
|
581
|
-
const btn = event.target.closest('.btn');
|
|
582
|
-
const path = document.getElementById('analyze-path').value;
|
|
583
|
-
const originalHTML = btn.innerHTML;
|
|
584
|
-
|
|
585
|
-
showLoading(btn, originalHTML);
|
|
586
|
-
try {
|
|
587
|
-
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
588
|
-
method: 'POST',
|
|
589
|
-
headers: { 'Content-Type': 'application/json' },
|
|
590
|
-
body: JSON.stringify({ path })
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
if (res.ok) {
|
|
594
|
-
const text = await res.text();
|
|
595
|
-
lastAnalyzeResult = text;
|
|
596
|
-
|
|
597
|
-
// Trigger download
|
|
598
|
-
const blob = new Blob([text], { type: 'text/plain' });
|
|
599
|
-
const url = window.URL.createObjectURL(blob);
|
|
600
|
-
const a = document.createElement('a');
|
|
601
|
-
a.href = url;
|
|
602
|
-
a.download = 'project.txt';
|
|
603
|
-
a.click();
|
|
604
|
-
|
|
605
|
-
showResponse('analyze-response', { success: true, files: text.split('===== FILE:').length - 1 });
|
|
606
|
-
showToast('Analysis completed');
|
|
607
|
-
} else {
|
|
608
|
-
const data = await res.json();
|
|
609
|
-
showResponse('analyze-response', data, true);
|
|
610
|
-
showToast('Analysis failed', 'error');
|
|
611
|
-
}
|
|
612
|
-
} catch (err) {
|
|
613
|
-
showResponse('analyze-response', { error: err.message }, true);
|
|
614
|
-
showToast('Connection error', 'error');
|
|
615
|
-
}
|
|
616
|
-
resetButton(btn);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
async function copyAnalyzeResult() {
|
|
620
|
-
const btn = event.target.closest('.btn');
|
|
621
|
-
if (!lastAnalyzeResult) {
|
|
622
|
-
showToast('Please analyze/download first', 'error');
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
copyToClipboard(lastAnalyzeResult, btn, 'analyze-copy-icon', 'analyze-copy-text');
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
async function testExecute() {
|
|
629
|
-
const btn = event.target.closest('.btn');
|
|
630
|
-
const bash = document.getElementById('execute-bash').value;
|
|
631
|
-
if (!bash.trim()) return showToast('Script is empty', 'error');
|
|
632
|
-
|
|
633
|
-
showLoading(btn, btn.innerHTML);
|
|
634
|
-
try {
|
|
635
|
-
const res = await fetch(`${API_BASE}/api/execute`, {
|
|
636
|
-
method: 'POST',
|
|
637
|
-
headers: { 'Content-Type': 'application/json' },
|
|
638
|
-
body: JSON.stringify({ bash })
|
|
639
|
-
});
|
|
640
|
-
const data = await res.json();
|
|
641
|
-
showResponse('execute-response', data, !data.success);
|
|
642
|
-
showToast(data.success ? 'Executed successfully' : 'Execution failed', data.success ? 'success' : 'error');
|
|
643
|
-
} catch (err) {
|
|
644
|
-
showResponse('execute-response', { error: err.message }, true);
|
|
645
|
-
}
|
|
646
|
-
resetButton(btn);
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
async function executeFromClipboard() {
|
|
650
|
-
try {
|
|
651
|
-
const text = await navigator.clipboard.readText();
|
|
652
|
-
if (!text.trim()) return showToast('Clipboard is empty', 'error');
|
|
653
|
-
|
|
654
|
-
document.getElementById('execute-bash').value = text;
|
|
655
|
-
showToast('Pasted from clipboard');
|
|
656
|
-
} catch (err) {
|
|
657
|
-
showToast('Clipboard permission denied', 'error');
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// Init Check
|
|
662
|
-
(async () => {
|
|
663
|
-
const statusBadge = document.getElementById('status');
|
|
664
|
-
try {
|
|
665
|
-
const res = await fetch(`${API_BASE}/health`);
|
|
666
|
-
if (res.ok) {
|
|
667
|
-
statusBadge.textContent = '● Online';
|
|
668
|
-
statusBadge.style.backgroundColor = 'var(--ios-green)';
|
|
669
|
-
}
|
|
670
|
-
} catch {
|
|
671
|
-
statusBadge.textContent = '● Offline';
|
|
672
|
-
statusBadge.style.backgroundColor = 'var(--ios-text-secondary)';
|
|
673
|
-
}
|
|
674
|
-
})();
|
|
675
|
-
|
|
676
|
-
</script>
|
|
116
|
+
<script type="module" src="/js/main.js"></script>
|
|
677
117
|
</body>
|
|
678
118
|
|
|
679
|
-
</html>
|
|
119
|
+
</html>
|