vg-coder-cli 2.0.10 → 2.0.12

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,84 @@
1
+ /* Right Panel Layout */
2
+ .right-panel {
3
+ flex: 1;
4
+ height: 100%;
5
+ background: #fff;
6
+ position: relative;
7
+ display: flex;
8
+ flex-direction: column;
9
+ }
10
+
11
+ /* AI Header Styles */
12
+ .ai-header {
13
+ height: 50px;
14
+ background: var(--ios-bg);
15
+ border-bottom: 1px solid var(--ios-separator);
16
+ display: flex;
17
+ align-items: center;
18
+ padding: 0 15px;
19
+ gap: 10px;
20
+ justify-content: space-between;
21
+ }
22
+
23
+ .ai-select-group {
24
+ display: flex;
25
+ align-items: center;
26
+ gap: 10px;
27
+ flex: 1;
28
+ }
29
+
30
+ .ai-select {
31
+ padding: 6px 12px;
32
+ border-radius: 8px;
33
+ border: 1px solid var(--ios-separator);
34
+ background: var(--ios-card);
35
+ color: var(--text-primary);
36
+ font-size: 13px;
37
+ font-weight: 500;
38
+ min-width: 200px;
39
+ outline: none;
40
+ cursor: pointer;
41
+ }
42
+
43
+ /* Iframe Container */
44
+ .ai-iframe-container {
45
+ flex: 1;
46
+ position: relative;
47
+ width: 100%;
48
+ background: #fff;
49
+ }
50
+
51
+ .right-panel iframe {
52
+ width: 100%;
53
+ height: 100%;
54
+ border: none;
55
+ position: relative;
56
+ z-index: 2;
57
+ }
58
+
59
+ /* Placeholder underneath iframe */
60
+ .iframe-placeholder {
61
+ position: absolute;
62
+ top: 50%;
63
+ left: 50%;
64
+ transform: translate(-50%, -50%);
65
+ text-align: center;
66
+ color: var(--text-secondary);
67
+ z-index: 1;
68
+ width: 100%;
69
+ }
70
+
71
+ .link-fallback {
72
+ color: var(--ios-blue);
73
+ text-decoration: none;
74
+ margin-top: 10px;
75
+ display: inline-block;
76
+ }
77
+
78
+ /* Mobile Responsive adjustment for right panel */
79
+ @media (max-width: 768px) {
80
+ .right-panel {
81
+ flex: 1;
82
+ height: 50vh;
83
+ }
84
+ }
@@ -43,9 +43,7 @@ body {
43
43
  background-color: var(--ios-bg);
44
44
  color: var(--text-primary);
45
45
  height: 100vh;
46
- /* Full viewport height */
47
46
  overflow: hidden;
48
- /* Disable body scroll */
49
47
  font-size: 13px;
50
48
  }
51
49
 
@@ -59,12 +57,9 @@ body {
59
57
  /* Left Panel: VG Coder Tool */
60
58
  .left-panel {
61
59
  flex: 0 0 450px;
62
- /* Fixed width: 450px */
63
60
  max-width: 50vw;
64
- /* Max 50% on small screens */
65
61
  height: 100%;
66
62
  overflow-y: auto;
67
- /* Scrollable independently */
68
63
  background: var(--ios-bg);
69
64
  border-right: 1px solid var(--ios-separator);
70
65
  position: relative;
@@ -74,48 +69,9 @@ body {
74
69
  .container {
75
70
  padding: 15px;
76
71
  padding-top: 15px;
77
- /* Removed max-width/margin centering since it's inside panel */
78
72
  }
79
73
 
80
- /* Right Panel: ChatGPT Iframe */
81
- .right-panel {
82
- flex: 1;
83
- /* Take remaining space */
84
- height: 100%;
85
- background: #fff;
86
- position: relative;
87
- }
88
-
89
- .right-panel iframe {
90
- width: 100%;
91
- height: 100%;
92
- border: none;
93
- position: relative;
94
- z-index: 2;
95
- /* Sit above placeholder */
96
- }
97
-
98
- /* Placeholder underneath iframe (visible if iframe fails to load) */
99
- .iframe-placeholder {
100
- position: absolute;
101
- top: 50%;
102
- left: 50%;
103
- transform: translate(-50%, -50%);
104
- text-align: center;
105
- color: var(--text-secondary);
106
- z-index: 1;
107
- width: 100%;
108
- }
109
-
110
- .link-fallback {
111
- color: var(--ios-blue);
112
- text-decoration: none;
113
- margin-top: 10px;
114
- display: inline-block;
115
- }
116
-
117
- /* --- EXISTING COMPACT STYLES (Preserved & Adjusted) --- */
118
-
74
+ /* --- COMPONENT STYLES --- */
119
75
  .header {
120
76
  display: flex;
121
77
  justify-content: space-between;
@@ -176,6 +132,11 @@ body {
176
132
  border: none;
177
133
  border-radius: 6px;
178
134
  cursor: pointer;
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ text-decoration: none;
139
+ color: var(--text-primary);
179
140
  }
180
141
 
181
142
  .system-prompt-content {
@@ -321,16 +282,13 @@ body {
321
282
  position: fixed;
322
283
  bottom: 20px;
323
284
  left: 20px;
324
- /* Move toast to bottom left */
325
285
  transform: translateY(100px);
326
- /* Adjust animation */
327
286
  }
328
287
 
329
288
  .toast.show {
330
289
  transform: translateY(0);
331
290
  }
332
291
 
333
- /* Mobile Responsiveness */
334
292
  @media (max-width: 768px) {
335
293
  .split-layout {
336
294
  flex-direction: column;
@@ -344,9 +302,4 @@ body {
344
302
  border-right: none;
345
303
  border-bottom: 1px solid var(--ios-separator);
346
304
  }
347
-
348
- .right-panel {
349
- flex: 1;
350
- height: 50vh;
351
- }
352
305
  }
@@ -8,6 +8,15 @@
8
8
  <title>VG Coder - Split View</title>
9
9
  <link rel="stylesheet" href="/dashboard.css">
10
10
  <link rel="stylesheet" href="/css/structure.css">
11
+ <link rel="stylesheet" href="/css/iframe.css">
12
+ <link rel="stylesheet" href="/css/git-view.css">
13
+
14
+ <!-- QUAN TRỌNG: Đã đổi sang github-dark.min.css để chữ có màu sáng -->
15
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" />
16
+
17
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
18
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
19
+
11
20
  <script>
12
21
  (function () {
13
22
  const savedTheme = localStorage.getItem('theme') || 'light';
@@ -18,7 +27,7 @@
18
27
 
19
28
  <body>
20
29
  <div class="split-layout">
21
- <!-- CỘT TRÁI: Giao diện VG Coder -->
30
+ <!-- CỘT TRÁI: Giao diện VG Coder -->
22
31
  <div class="left-panel">
23
32
  <div class="container">
24
33
  <div class="header">
@@ -68,8 +77,11 @@
68
77
  Copy link này và dán vào tab mới:
69
78
  <div class="form-group" style="margin-top: 5px; margin-bottom: 0;">
70
79
  <div style="display: flex; gap: 5px;">
71
- <input type="text" id="chrome-url-input" readonly value="chrome://extensions" onclick="this.select()" style="font-family: monospace;">
72
- <button class="btn btn-copy" style="flex: 0 0 40px; padding: 0;" onclick="copyChromeUrl(event)" title="Copy URL">
80
+ <input type="text" id="chrome-url-input" readonly
81
+ value="chrome://extensions" onclick="this.select()"
82
+ style="font-family: monospace;">
83
+ <button class="btn btn-copy" style="flex: 0 0 40px; padding: 0;"
84
+ onclick="copyChromeUrl(event)" title="Copy URL">
73
85
  <span>📋</span>
74
86
  </button>
75
87
  </div>
@@ -81,7 +93,8 @@
81
93
  </ol>
82
94
  </div>
83
95
  <div class="form-group">
84
- <input type="text" id="extension-path-input" readonly value="Loading path..." onclick="this.select()">
96
+ <input type="text" id="extension-path-input" readonly value="Loading path..."
97
+ onclick="this.select()">
85
98
  </div>
86
99
  <button class="btn btn-copy" onclick="copyExtensionPath(event)">
87
100
  <span id="ext-copy-icon">📋</span>
@@ -91,29 +104,6 @@
91
104
  </div>
92
105
 
93
106
  <div class="endpoints">
94
- <!-- Analyze -->
95
- <div class="endpoint-card">
96
- <div class="endpoint-header">
97
- <div class="endpoint-title-group">
98
- <span class="method post">POST</span>
99
- <span class="endpoint-path">/analyze</span>
100
- </div>
101
- <button class="btn-icon-head" onclick="testAnalyze(event)" title="Download Project Source">
102
- 📥
103
- </button>
104
- </div>
105
- <div class="form-group">
106
- <input type="text" id="analyze-path" value="." placeholder="Project path (e.g. .)">
107
- </div>
108
- <div class="btn-group">
109
- <button class="btn btn-copy" onclick="copyAnalyzeResult(event)">
110
- <span id="analyze-copy-icon">📋</span>
111
- <span id="analyze-copy-text">Copy Text</span>
112
- </button>
113
- </div>
114
- <div class="response" id="analyze-response"></div>
115
- </div>
116
-
117
107
  <!-- Execute Bash -->
118
108
  <div class="endpoint-card">
119
109
  <div class="endpoint-header">
@@ -165,18 +155,62 @@
165
155
  </div>
166
156
  <div class="response" id="structure-response" style="display: none;"></div>
167
157
  </div>
158
+
159
+ <!-- Analyze -->
160
+ <div class="endpoint-card">
161
+ <div class="endpoint-header">
162
+ <div class="endpoint-title-group">
163
+ <span class="method post">POST</span>
164
+ <span class="endpoint-path">/analyze</span>
165
+ </div>
166
+ <button class="btn-icon-head" onclick="testAnalyze(event)" title="Download Project Source">
167
+ 📥
168
+ </button>
169
+ </div>
170
+ <div class="form-group">
171
+ <input type="text" id="analyze-path" value="." placeholder="Project path (e.g. .)">
172
+ </div>
173
+ <div class="btn-group">
174
+ <button class="btn btn-copy" onclick="copyAnalyzeResult(event)">
175
+ <span id="analyze-copy-icon">📋</span>
176
+ <span id="analyze-copy-text">Copy Text</span>
177
+ </button>
178
+ </div>
179
+ <div class="response" id="analyze-response"></div>
180
+ </div>
168
181
  </div>
169
182
  <div style="height: 50px;"></div> <!-- Spacer for scrolling -->
170
183
  </div>
171
184
  </div>
172
185
 
173
- <!-- CỘT PHẢI: Iframe ChatGPT -->
186
+ <!-- CỘT PHẢI: AI Iframe with Selector & Git View -->
174
187
  <div class="right-panel">
175
- <div class="iframe-placeholder">
176
- <p>⚠️ Nếu trang trắng, hãy cài Extension <b>"Ignore X-Frame-Options"</b></p>
177
- <a href="https://chatgpt.com" target="_blank" class="link-fallback">Mở ChatGPT tab mới ↗</a>
188
+ <div class="ai-header">
189
+ <div class="ai-select-group">
190
+ <span style="font-size: 16px;">🤖</span>
191
+ <select id="ai-provider-select" class="ai-select">
192
+ <!-- Options filled by JS -->
193
+ </select>
194
+ </div>
195
+
196
+ <!-- NEW: Git Toggle Button -->
197
+ <button id="git-view-toggle" class="git-toggle-btn">View Changes</button>
198
+ <button id="git-refresh-btn" class="btn-icon-head" style="display:none; margin-left:5px;" title="Refresh">↻</button>
199
+
200
+ <a id="ai-external-link" href="#" target="_blank" class="btn-icon-head" title="Open in new tab">↗</a>
201
+ </div>
202
+
203
+ <!-- Git View Container (Hidden by default) -->
204
+ <div id="git-view-container" class="git-view-container"></div>
205
+
206
+ <!-- AI Iframe Container -->
207
+ <div class="ai-iframe-container">
208
+ <div class="iframe-placeholder">
209
+ <p>⚠️ Nếu trang trắng, hãy cài Extension <b>"Ignore X-Frame-Options"</b></p>
210
+ <a id="ai-placeholder-link" href="#" target="_blank" class="link-fallback">Mở tab mới ↗</a>
211
+ </div>
212
+ <iframe id="ai-iframe" src="" title="AI Integration"></iframe>
178
213
  </div>
179
- <iframe src="https://chatgpt.com" title="ChatGPT Integration"></iframe>
180
214
  </div>
181
215
  </div>
182
216
 
@@ -72,6 +72,21 @@ export async function getStructure(path) {
72
72
  return await res.json();
73
73
  }
74
74
 
75
+ /**
76
+ * Get Git Diff
77
+ * @returns {Promise<string>} - Unified diff string
78
+ */
79
+ export async function getGitDiff() {
80
+ const res = await fetch(`${API_BASE}/api/git/diff`);
81
+ const data = await res.json();
82
+
83
+ if (!res.ok) {
84
+ throw new Error(data.error || 'Failed to get git diff');
85
+ }
86
+
87
+ return data.diff;
88
+ }
89
+
75
90
  /**
76
91
  * Copy content as file to clipboard
77
92
  * @param {string} filename - Filename for the clipboard item
@@ -0,0 +1,117 @@
1
+ import { getGitDiff } from '../api.js';
2
+ import { showToast, showLoading } from '../utils.js';
3
+
4
+ export function initGitView() {
5
+ console.log('[GitView] Initializing...');
6
+ const toggleBtn = document.getElementById('git-view-toggle');
7
+ const refreshBtn = document.getElementById('git-refresh-btn');
8
+
9
+ if (toggleBtn) toggleBtn.addEventListener('click', toggleGitMode);
10
+ if (refreshBtn) refreshBtn.addEventListener('click', loadGitChanges);
11
+ }
12
+
13
+ let isGitMode = false;
14
+
15
+ async function toggleGitMode() {
16
+ const gitContainer = document.getElementById('git-view-container');
17
+ const toggleBtn = document.getElementById('git-view-toggle');
18
+ const refreshBtn = document.getElementById('git-refresh-btn');
19
+ const toggleText = document.getElementById('git-toggle-text');
20
+
21
+ isGitMode = !isGitMode;
22
+ console.log('[GitView] Toggle Mode:', isGitMode);
23
+
24
+ if (isGitMode) {
25
+ gitContainer.classList.add('active');
26
+ toggleBtn.classList.add('active');
27
+ if (toggleText) toggleText.textContent = 'Close Changes';
28
+ if (refreshBtn) refreshBtn.style.display = 'flex';
29
+
30
+ // Debug kích thước container
31
+ const rect = gitContainer.getBoundingClientRect();
32
+ console.log('[GitView] Container Size:', rect.width, 'x', rect.height);
33
+
34
+ if (!gitContainer.innerHTML.trim()) {
35
+ await loadGitChanges();
36
+ }
37
+ } else {
38
+ gitContainer.classList.remove('active');
39
+ toggleBtn.classList.remove('active');
40
+ if (toggleText) toggleText.textContent = 'View Changes';
41
+ if (refreshBtn) refreshBtn.style.display = 'none';
42
+ }
43
+ }
44
+
45
+ async function loadGitChanges() {
46
+ const gitContainer = document.getElementById('git-view-container');
47
+ const refreshBtn = document.getElementById('git-refresh-btn');
48
+
49
+ console.log('[GitView] Loading changes...');
50
+
51
+ if (refreshBtn) {
52
+ refreshBtn.style.opacity = '0.5';
53
+ refreshBtn.disabled = true;
54
+ }
55
+
56
+ gitContainer.innerHTML = '<div class="git-loading-msg">Loading git changes... (Check Console if stuck)</div>';
57
+
58
+ try {
59
+ // Kiểm tra thư viện
60
+ if (typeof Diff2HtmlUI === 'undefined') {
61
+ throw new Error('Thư viện Diff2HtmlUI chưa được load!');
62
+ }
63
+ if (typeof hljs === 'undefined') {
64
+ console.warn('⚠️ Highlight.js chưa được load!');
65
+ }
66
+
67
+ const diffString = await getGitDiff();
68
+ console.log('[GitView] Diff received, length:', diffString?.length);
69
+
70
+ if (!diffString || !diffString.trim()) {
71
+ gitContainer.innerHTML = `
72
+ <div class="git-loading-msg">
73
+ <div style="font-size:30px;">✨</div>
74
+ <p>No changes detected</p>
75
+ </div>`;
76
+ } else {
77
+ console.log('[GitView] Rendering diff...');
78
+
79
+ // Cấu hình Diff2Html
80
+ const configuration = {
81
+ drawFileList: true,
82
+ fileListToggle: false,
83
+ fileListStartVisible: true,
84
+ fileContentToggle: false,
85
+ matching: 'lines',
86
+ outputFormat: 'side-by-side',
87
+ synchronisedScroll: true,
88
+ highlight: true,
89
+ renderNothingWhenEmpty: true,
90
+ colorScheme: 'dark' // Ép buộc Dark Mode từ thư viện
91
+ };
92
+
93
+ gitContainer.innerHTML = ''; // Clear loading
94
+
95
+ const diff2htmlUi = new Diff2HtmlUI(gitContainer, diffString, configuration);
96
+ diff2htmlUi.draw();
97
+ diff2htmlUi.highlightCode();
98
+
99
+ console.log('[GitView] Render complete.');
100
+ }
101
+ showToast('Git changes loaded', 'success');
102
+
103
+ } catch (err) {
104
+ console.error('[GitView] Error:', err);
105
+ gitContainer.innerHTML = `
106
+ <div class="git-loading-msg" style="color:var(--ios-red);">
107
+ <div>⚠️ Error</div>
108
+ <div style="font-size:12px;">${err.message}</div>
109
+ <div style="font-size:10px; margin-top:5px;">Check F12 Console for details</div>
110
+ </div>`;
111
+ }
112
+
113
+ if (refreshBtn) {
114
+ refreshBtn.style.opacity = '1';
115
+ refreshBtn.disabled = false;
116
+ }
117
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Quản lý tính năng Iframe AI Provider
3
+ */
4
+
5
+ // Danh sách các AI Providers
6
+ const AI_PROVIDERS = [
7
+ { id: 'chatgpt', name: 'ChatGPT', url: 'https://chat.openai.com' },
8
+ { id: 'kimi', name: 'Kimi AI', url: 'https://www.kimi.com' },
9
+ { id: 'deepseek', name: 'DeepSeek', url: 'https://chat.deepseek.com' },
10
+ { id: 'gemini', name: 'Google Gemini', url: 'https://gemini.google.com/app' },
11
+ { id: 'aistudio', name: 'Google AI Studio', url: 'https://aistudio.google.com/prompts/new_chat' }
12
+ ];
13
+
14
+ export function initIframeManager() {
15
+ const select = document.getElementById('ai-provider-select');
16
+ const iframe = document.getElementById('ai-iframe');
17
+ const externalLink = document.getElementById('ai-external-link');
18
+ const placeholderLink = document.getElementById('ai-placeholder-link');
19
+
20
+ if (!select || !iframe) return;
21
+
22
+ // 1. Populate options
23
+ AI_PROVIDERS.forEach(provider => {
24
+ const option = document.createElement('option');
25
+ option.value = provider.id;
26
+ option.textContent = provider.name;
27
+ select.appendChild(option);
28
+ });
29
+
30
+ // 2. Load saved selection or default
31
+ const savedProviderId = localStorage.getItem('ai_provider') || 'chatgpt';
32
+
33
+ // Validate if saved provider still exists
34
+ const isValid = AI_PROVIDERS.some(p => p.id === savedProviderId);
35
+ select.value = isValid ? savedProviderId : AI_PROVIDERS[0].id;
36
+
37
+ // 3. Function to update UI
38
+ const updateProvider = (providerId) => {
39
+ const provider = AI_PROVIDERS.find(p => p.id === providerId) || AI_PROVIDERS[0];
40
+
41
+ iframe.src = provider.url;
42
+ externalLink.href = provider.url;
43
+ placeholderLink.href = provider.url;
44
+ placeholderLink.textContent = `Mở ${provider.name} tab mới ↗`;
45
+
46
+ localStorage.setItem('ai_provider', providerId);
47
+ };
48
+
49
+ // 4. Initial update
50
+ updateProvider(select.value);
51
+
52
+ // 5. Event listener
53
+ select.addEventListener('change', (e) => {
54
+ updateProvider(e.target.value);
55
+ });
56
+ }
@@ -3,6 +3,8 @@ import { SYSTEM_PROMPT } from './config.js';
3
3
  import { checkHealth } from './api.js';
4
4
  import './handlers.js'; // Import to register global functions
5
5
  import { showToast, showCopiedState } from './utils.js';
6
+ import { initIframeManager } from './features/iframe-manager.js';
7
+ import { initGitView } from './features/git-view.js';
6
8
 
7
9
  /**
8
10
  * Initialize application on DOM ready
@@ -19,6 +21,12 @@ document.addEventListener('DOMContentLoaded', async () => {
19
21
 
20
22
  // Load Extension Path
21
23
  loadExtensionPath();
24
+
25
+ // Initialize Iframe Manager
26
+ initIframeManager();
27
+
28
+ // Initialize Git View
29
+ initGitView();
22
30
  });
23
31
 
24
32
  /**