vg-coder-cli 2.0.7 → 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.
@@ -0,0 +1,214 @@
1
+ // Event Handlers & Business Logic
2
+ import { SYSTEM_PROMPT } from './config.js';
3
+ import { analyzeProject, executeScript, copyAsFile, copyToClipboard, readFromClipboard } from './api.js';
4
+ import { showToast, showLoading, resetButton, showResponse, showCopiedState } from './utils.js';
5
+
6
+ // State management
7
+ let lastAnalyzeResult = null;
8
+
9
+ /**
10
+ * Toggle system prompt section
11
+ */
12
+ export function toggleSystemPrompt() {
13
+ const content = document.getElementById('system-prompt-content');
14
+ const icon = document.getElementById('toggle-icon');
15
+ content.classList.toggle('open');
16
+ icon.classList.toggle('open');
17
+ }
18
+
19
+ /**
20
+ * Copy system prompt from the Header Button
21
+ * Stops propagation so the accordion doesn't toggle
22
+ */
23
+ export function copySystemPromptFromHeader(event) {
24
+ event.stopPropagation(); // Stop accordion from toggling
25
+
26
+ // Animate button
27
+ const btn = event.currentTarget;
28
+ btn.textContent = '✓';
29
+
30
+ navigator.clipboard.writeText(SYSTEM_PROMPT).then(() => {
31
+ showToast('Đã copy System Prompt', 'success');
32
+ setTimeout(() => {
33
+ btn.textContent = '📋';
34
+ }, 2000);
35
+ }).catch(err => {
36
+ showToast('Lỗi copy: ' + err.message, 'error');
37
+ btn.textContent = '📋';
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Copy system prompt to clipboard (Main content button)
43
+ */
44
+ export function copySystemPrompt() {
45
+ const copyBtn = event.target.closest('.btn-copy');
46
+ const copyIcon = document.getElementById('copy-icon');
47
+ const copyText = document.getElementById('copy-text');
48
+
49
+ navigator.clipboard.writeText(SYSTEM_PROMPT).then(() => {
50
+ showCopiedState(copyBtn, copyIcon, copyText, '📋', 'Copy System Prompt');
51
+ showToast('Đã copy System Prompt', 'success');
52
+ }).catch(err => {
53
+ showToast('Lỗi copy: ' + err.message, 'error');
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Handle analyze button click
59
+ */
60
+ export async function testAnalyze() {
61
+ // Support both the big button .btn and the header icon .btn-icon-head
62
+ const btn = event.target.closest('.btn') || event.target.closest('.btn-icon-head');
63
+ const path = document.getElementById('analyze-path').value;
64
+
65
+ showLoading(btn, btn.innerHTML);
66
+ try {
67
+ const text = await analyzeProject(path);
68
+ lastAnalyzeResult = text;
69
+
70
+ // Download file
71
+ const blob = new Blob([text], { type: 'text/plain' });
72
+ const url = window.URL.createObjectURL(blob);
73
+ const a = document.createElement('a');
74
+ a.href = url;
75
+ a.download = 'project.txt';
76
+ a.click();
77
+
78
+ showResponse('analyze-response', {
79
+ success: true,
80
+ message: 'File downloaded!',
81
+ files: text.split('\n').filter(l => l.includes('===== FILE:')).length,
82
+ size: (text.length / 1024).toFixed(2) + ' KB'
83
+ });
84
+ showToast('Đã download file', 'success');
85
+ } catch (err) {
86
+ showResponse('analyze-response', { error: err.message }, true);
87
+ showToast('Lỗi: ' + err.message, 'error');
88
+ }
89
+ resetButton(btn);
90
+ }
91
+
92
+ /**
93
+ * Copy analyze result as text
94
+ */
95
+ export async function copyAnalyzeResult() {
96
+ const copyBtn = event.target.closest('.btn-copy');
97
+ const copyIcon = document.getElementById('analyze-copy-icon');
98
+ const copyText = document.getElementById('analyze-copy-text');
99
+
100
+ if (!lastAnalyzeResult) {
101
+ // Fetch if not already analyzed
102
+ const path = document.getElementById('analyze-path').value;
103
+ showLoading(copyBtn, copyBtn.innerHTML);
104
+
105
+ try {
106
+ lastAnalyzeResult = await analyzeProject(path);
107
+ } catch (err) {
108
+ showToast('Lỗi: ' + err.message, 'error');
109
+ resetButton(copyBtn);
110
+ return;
111
+ }
112
+ resetButton(copyBtn);
113
+ }
114
+
115
+ // Copy to clipboard
116
+ try {
117
+ await copyToClipboard(lastAnalyzeResult);
118
+ showCopiedState(copyBtn, copyIcon, copyText, '📋', 'Copy Text');
119
+ showToast('Đã copy project.txt', 'success');
120
+ } catch (err) {
121
+ showToast('Lỗi copy: ' + err.message, 'error');
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Handle execute button click
127
+ */
128
+ export async function testExecute() {
129
+ const btn = event.target.closest('.btn');
130
+ const bashInput = document.getElementById('execute-bash');
131
+ const bash = bashInput.value;
132
+
133
+ if (!bash.trim()) {
134
+ showToast('Vui lòng nhập bash script', 'error');
135
+ return;
136
+ }
137
+
138
+ showLoading(btn, btn.innerHTML);
139
+ try {
140
+ const data = await executeScript(bash);
141
+ showResponse('execute-response', data, !data.success);
142
+
143
+ if (data.success) {
144
+ showToast('Thực thi thành công', 'success');
145
+ // Clear input on success
146
+ bashInput.value = '';
147
+ } else {
148
+ showToast('Thực thi thất bại', 'error');
149
+ }
150
+ } catch (err) {
151
+ showResponse('execute-response', { error: err.message }, true);
152
+ showToast('Lỗi: ' + err.message, 'error');
153
+ }
154
+ resetButton(btn);
155
+ }
156
+
157
+ /**
158
+ * Execute script from clipboard
159
+ */
160
+ export async function executeFromClipboard() {
161
+ const btn = event.target.closest('.btn');
162
+ const bashInput = document.getElementById('execute-bash');
163
+
164
+ showLoading(btn, btn.innerHTML);
165
+
166
+ try {
167
+ const clipboardText = await readFromClipboard();
168
+
169
+ if (!clipboardText || !clipboardText.trim()) {
170
+ showToast('Clipboard trống!', 'error');
171
+ showResponse('execute-response', {
172
+ error: 'Clipboard is empty',
173
+ message: 'Please copy a bash script to clipboard first'
174
+ }, true);
175
+ resetButton(btn);
176
+ return;
177
+ }
178
+
179
+ // Show what we are running
180
+ bashInput.value = clipboardText;
181
+
182
+ const data = await executeScript(clipboardText);
183
+ showResponse('execute-response', data, !data.success);
184
+
185
+ if (data.success) {
186
+ showToast('Thực thi từ clipboard OK', 'success');
187
+ // Clear input on success
188
+ bashInput.value = '';
189
+ } else {
190
+ if (data.syntaxError) {
191
+ showToast('Lỗi syntax script', 'error');
192
+ } else {
193
+ showToast('Thực thi thất bại', 'error');
194
+ }
195
+ }
196
+ } catch (err) {
197
+ if (err.name === 'NotAllowedError') {
198
+ showToast('Không có quyền clipboard', 'error');
199
+ } else {
200
+ showResponse('execute-response', { error: err.message }, true);
201
+ showToast('Lỗi: ' + err.message, 'error');
202
+ }
203
+ }
204
+ resetButton(btn);
205
+ }
206
+
207
+ // Make functions globally available for onclick handlers
208
+ window.toggleSystemPrompt = toggleSystemPrompt;
209
+ window.copySystemPrompt = copySystemPrompt;
210
+ window.copySystemPromptFromHeader = copySystemPromptFromHeader;
211
+ window.testAnalyze = testAnalyze;
212
+ window.copyAnalyzeResult = copyAnalyzeResult;
213
+ window.testExecute = testExecute;
214
+ window.executeFromClipboard = executeFromClipboard;
@@ -0,0 +1,72 @@
1
+ // Main entry point - Initialize application
2
+ import { SYSTEM_PROMPT } from './config.js';
3
+ import { checkHealth } from './api.js';
4
+ import './handlers.js'; // Import to register global functions
5
+
6
+ /**
7
+ * Initialize application on DOM ready
8
+ */
9
+ document.addEventListener('DOMContentLoaded', async () => {
10
+ // Load system prompt text
11
+ document.getElementById('prompt-text').textContent = SYSTEM_PROMPT;
12
+
13
+ // Check server status
14
+ await checkServerStatus();
15
+
16
+ // Initialize Theme
17
+ initTheme();
18
+ });
19
+
20
+ /**
21
+ * Check and update server status
22
+ */
23
+ async function checkServerStatus() {
24
+ const statusEl = document.getElementById('status');
25
+ const isHealthy = await checkHealth();
26
+
27
+ if (isHealthy) {
28
+ statusEl.textContent = '● Online';
29
+ statusEl.style.background = 'rgba(52, 199, 89, 0.15)';
30
+ statusEl.style.color = 'var(--ios-green)';
31
+ } else {
32
+ statusEl.textContent = '● Offline';
33
+ statusEl.style.background = 'rgba(255, 59, 48, 0.15)';
34
+ statusEl.style.color = 'var(--ios-red)';
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Initialize Theme Logic
40
+ */
41
+ function initTheme() {
42
+ const themeBtn = document.getElementById('theme-toggle');
43
+ const themeIcon = document.getElementById('theme-icon');
44
+
45
+ // Get current theme from DOM (set by inline script) or localStorage
46
+ let currentTheme = localStorage.getItem('theme') || 'light';
47
+
48
+ // Update icon initially
49
+ updateThemeIcon(currentTheme);
50
+
51
+ themeBtn.addEventListener('click', () => {
52
+ // Toggle theme
53
+ const newTheme = currentTheme === 'light' ? 'dark' : 'light';
54
+
55
+ // Update DOM
56
+ document.documentElement.setAttribute('data-theme', newTheme);
57
+ localStorage.setItem('theme', newTheme);
58
+
59
+ // Update local state
60
+ currentTheme = newTheme;
61
+ updateThemeIcon(newTheme);
62
+ });
63
+ }
64
+
65
+ function updateThemeIcon(theme) {
66
+ const themeIcon = document.getElementById('theme-icon');
67
+ if (theme === 'dark') {
68
+ themeIcon.textContent = '☀️';
69
+ } else {
70
+ themeIcon.textContent = '🌙';
71
+ }
72
+ }
@@ -0,0 +1,72 @@
1
+ // UI Utility Functions
2
+
3
+ /**
4
+ * Display a toast notification
5
+ * @param {string} message - Message to display
6
+ * @param {string} type - Type of toast: 'success', 'error', 'info'
7
+ */
8
+ export function showToast(message, type = 'success') {
9
+ const toast = document.getElementById('toast');
10
+ // Reset text content to remove potential icon junk
11
+ toast.textContent = message;
12
+ toast.className = `toast ${type}`;
13
+ toast.classList.add('show');
14
+
15
+ // Clear previous timeout if exists
16
+ if (toast.timeoutId) clearTimeout(toast.timeoutId);
17
+
18
+ toast.timeoutId = setTimeout(() => toast.classList.remove('show'), 3000);
19
+ }
20
+
21
+ /**
22
+ * Show loading state on button
23
+ * @param {HTMLElement} button - Button element
24
+ * @param {string} originalText - Original button HTML
25
+ */
26
+ export function showLoading(button, originalText) {
27
+ button.disabled = true;
28
+ button.innerHTML = '<span class="loading"></span>';
29
+ button.dataset.originalText = originalText;
30
+ }
31
+
32
+ /**
33
+ * Reset button to original state
34
+ * @param {HTMLElement} button - Button element
35
+ */
36
+ export function resetButton(button) {
37
+ button.disabled = false;
38
+ const originalText = button.dataset.originalText;
39
+ button.innerHTML = originalText;
40
+ }
41
+
42
+ /**
43
+ * Display API response in response area
44
+ * @param {string} elementId - ID of response element
45
+ * @param {Object} data - Response data
46
+ * @param {boolean} isError - Whether this is an error response
47
+ */
48
+ export function showResponse(elementId, data, isError = false) {
49
+ const el = document.getElementById(elementId);
50
+ el.className = 'response show ' + (isError ? 'error' : 'success');
51
+ el.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
52
+ }
53
+
54
+ /**
55
+ * Update button state to show copied status
56
+ * @param {HTMLElement} button - Button element
57
+ * @param {HTMLElement} icon - Icon element
58
+ * @param {HTMLElement} text - Text element
59
+ * @param {string} originalIcon - Original icon text
60
+ * @param {string} originalText - Original button text
61
+ */
62
+ export function showCopiedState(button, icon, text, originalIcon, originalText) {
63
+ button.classList.add('copied');
64
+ icon.textContent = '✓';
65
+ text.textContent = 'Copied';
66
+
67
+ setTimeout(() => {
68
+ button.classList.remove('copied');
69
+ icon.textContent = originalIcon;
70
+ text.textContent = originalText;
71
+ }, 2000);
72
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file