vg-coder-cli 2.0.10 → 2.0.11

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/DEVELOPMENT.md ADDED
@@ -0,0 +1,95 @@
1
+ # VG Coder - Development & Architecture Guide
2
+
3
+ Tài liệu này quy định kiến trúc Frontend (Dashboard) để đảm bảo tính dễ bảo trì, mở rộng và code sạch (Clean Code).
4
+
5
+ ## 1. Directory Structure (Cấu trúc thư mục)
6
+
7
+ Frontend nằm trong `src/server/views/`.
8
+
9
+ ```text
10
+ src/server/views/
11
+ ├── dashboard.html # File HTML chính (Layout & Markup)
12
+ ├── dashboard.css # CSS Global (Variables, Reset, Layout khung sườn)
13
+ ├── css/ # 📁 MODULE CSS (Chứa style riêng biệt cho từng feature)
14
+ │ ├── structure.css # Style cho cây thư mục
15
+ │ ├── iframe.css # Style cho Iframe/AI Panel
16
+ │ └── [feature].css # -> Style cho tính năng mới đặt tại đây
17
+ ├── js/ # 📁 JAVASCRIPT
18
+ │ ├── main.js # Entry Point (Khởi tạo, Import các feature)
19
+ │ ├── config.js # Constants & Config
20
+ │ ├── api.js # API Layer (Fetch requests)
21
+ │ ├── utils.js # Helper functions
22
+ │ ├── handlers.js # Global event handlers (để bind vào window)
23
+ │ └── features/ # 📁 MODULE JS (Logic riêng biệt cho từng feature)
24
+ │ ├── structure.js # Logic cây thư mục
25
+ │ ├── iframe-manager.js # Logic Iframe AI
26
+ │ └── [feature].js # -> Logic tính năng mới đặt tại đây
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 2. Quy trình thêm tính năng mới (Workflow)
32
+
33
+ Khi thêm một tính năng mới (ví dụ: `Settings`), tuân thủ 4 bước sau:
34
+
35
+ ### Bước 1: Tạo CSS Module
36
+ Tạo file `src/server/views/css/settings.css`.
37
+ * **Quy tắc:** Chỉ viết style liên quan đến settings.
38
+ * **Import:** Thêm thẻ `<link>` vào `dashboard.html`.
39
+
40
+ ### Bước 2: Tạo JS Module
41
+ Tạo file `src/server/views/js/features/settings.js`.
42
+ * **Quy tắc:** Export hàm khởi tạo (`initSettings`) hoặc các hàm xử lý logic.
43
+ * **Không** viết code chạy ngay lập tức (IIFE) trừ khi cần thiết.
44
+
45
+ ```javascript
46
+ // Example: src/server/views/js/features/settings.js
47
+ export function initSettings() {
48
+ console.log('Settings initialized');
49
+ // Logic here
50
+ }
51
+ ```
52
+
53
+ ### Bước 3: Cập nhật HTML
54
+ Thêm Markup vào `src/server/views/dashboard.html`.
55
+ * Thêm ID cụ thể để JS dễ query (ví dụ: `id="settings-panel"`).
56
+ * Thêm link CSS mới vào `<head>`.
57
+
58
+ ### Bước 4: Đăng ký (Register) trong `main.js`
59
+ Import và gọi hàm khởi tạo trong `src/server/views/js/main.js`.
60
+
61
+ ```javascript
62
+ // src/server/views/js/main.js
63
+ import { initSettings } from './features/settings.js';
64
+
65
+ document.addEventListener('DOMContentLoaded', async () => {
66
+ // ... các init khác
67
+ initSettings();
68
+ });
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 3. Coding Standards (Quy chuẩn Code)
74
+
75
+ ### CSS
76
+ * Sử dụng **CSS Variables** (`var(--ios-bg)`) định nghĩa trong `dashboard.css` để đồng bộ Dark/Light mode.
77
+ * Tránh sửa trực tiếp `dashboard.css` trừ khi thay đổi Layout toàn cục.
78
+
79
+ ### JavaScript
80
+ * **ES Modules:** Sử dụng `import/export`.
81
+ * **Global Scope:** Hạn chế gán biến vào `window`. Nếu cần dùng cho `onclick=""` trong HTML, hãy gán thông qua file `handlers.js` hoặc gán explicit trong `main.js`.
82
+ * **API Calls:** Mọi lệnh `fetch` gọi về server nên được viết trong `js/api.js`, sau đó feature import về dùng.
83
+
84
+ ---
85
+
86
+ ## 4. Prompt mẫu cho AI
87
+
88
+ Khi yêu cầu AI code tính năng mới, hãy dùng prompt sau để đảm bảo AI tuân thủ kiến trúc:
89
+
90
+ > "Hãy thêm tính năng [TÊN_TÍNH_NĂNG].
91
+ > Tuân thủ kiến trúc trong `DEVELOPMENT.md`:
92
+ > 1. Tạo file CSS riêng trong `views/css/`.
93
+ > 2. Tạo file JS logic riêng trong `views/js/features/`.
94
+ > 3. Cập nhật `dashboard.html` và `main.js`.
95
+ > 4. Sử dụng style từ biến CSS có sẵn."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vg-coder-cli",
3
- "version": "2.0.10",
3
+ "version": "2.0.11",
4
4
  "description": "🚀 CLI tool to analyze projects, concatenate source files, count tokens, and export HTML with syntax highlighting and copy functionality",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -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,7 @@
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">
11
12
  <script>
12
13
  (function () {
13
14
  const savedTheme = localStorage.getItem('theme') || 'light';
@@ -18,7 +19,7 @@
18
19
 
19
20
  <body>
20
21
  <div class="split-layout">
21
- <!-- CỘT TRÁI: Giao diện VG Coder -->
22
+ <!-- CỘT TRÁI: Giao diện VG Coder -->
22
23
  <div class="left-panel">
23
24
  <div class="container">
24
25
  <div class="header">
@@ -68,8 +69,11 @@
68
69
  Copy link này và dán vào tab mới:
69
70
  <div class="form-group" style="margin-top: 5px; margin-bottom: 0;">
70
71
  <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">
72
+ <input type="text" id="chrome-url-input" readonly
73
+ value="chrome://extensions" onclick="this.select()"
74
+ style="font-family: monospace;">
75
+ <button class="btn btn-copy" style="flex: 0 0 40px; padding: 0;"
76
+ onclick="copyChromeUrl(event)" title="Copy URL">
73
77
  <span>📋</span>
74
78
  </button>
75
79
  </div>
@@ -81,7 +85,8 @@
81
85
  </ol>
82
86
  </div>
83
87
  <div class="form-group">
84
- <input type="text" id="extension-path-input" readonly value="Loading path..." onclick="this.select()">
88
+ <input type="text" id="extension-path-input" readonly value="Loading path..."
89
+ onclick="this.select()">
85
90
  </div>
86
91
  <button class="btn btn-copy" onclick="copyExtensionPath(event)">
87
92
  <span id="ext-copy-icon">📋</span>
@@ -91,29 +96,6 @@
91
96
  </div>
92
97
 
93
98
  <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
99
  <!-- Execute Bash -->
118
100
  <div class="endpoint-card">
119
101
  <div class="endpoint-header">
@@ -165,18 +147,53 @@
165
147
  </div>
166
148
  <div class="response" id="structure-response" style="display: none;"></div>
167
149
  </div>
150
+
151
+ <!-- Analyze -->
152
+ <div class="endpoint-card">
153
+ <div class="endpoint-header">
154
+ <div class="endpoint-title-group">
155
+ <span class="method post">POST</span>
156
+ <span class="endpoint-path">/analyze</span>
157
+ </div>
158
+ <button class="btn-icon-head" onclick="testAnalyze(event)" title="Download Project Source">
159
+ 📥
160
+ </button>
161
+ </div>
162
+ <div class="form-group">
163
+ <input type="text" id="analyze-path" value="." placeholder="Project path (e.g. .)">
164
+ </div>
165
+ <div class="btn-group">
166
+ <button class="btn btn-copy" onclick="copyAnalyzeResult(event)">
167
+ <span id="analyze-copy-icon">📋</span>
168
+ <span id="analyze-copy-text">Copy Text</span>
169
+ </button>
170
+ </div>
171
+ <div class="response" id="analyze-response"></div>
172
+ </div>
168
173
  </div>
169
174
  <div style="height: 50px;"></div> <!-- Spacer for scrolling -->
170
175
  </div>
171
176
  </div>
172
177
 
173
- <!-- CỘT PHẢI: Iframe ChatGPT -->
178
+ <!-- CỘT PHẢI: AI Iframe with Selector -->
174
179
  <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>
180
+ <div class="ai-header">
181
+ <div class="ai-select-group">
182
+ <span style="font-size: 16px;">🤖</span>
183
+ <select id="ai-provider-select" class="ai-select">
184
+ <!-- Options filled by JS -->
185
+ </select>
186
+ </div>
187
+ <a id="ai-external-link" href="#" target="_blank" class="btn-icon-head" title="Open in new tab">↗</a>
188
+ </div>
189
+
190
+ <div class="ai-iframe-container">
191
+ <div class="iframe-placeholder">
192
+ <p>⚠️ Nếu trang trắng, hãy cài Extension <b>"Ignore X-Frame-Options"</b></p>
193
+ <a id="ai-placeholder-link" href="#" target="_blank" class="link-fallback">Mở tab mới ↗</a>
194
+ </div>
195
+ <iframe id="ai-iframe" src="" title="AI Integration"></iframe>
178
196
  </div>
179
- <iframe src="https://chatgpt.com" title="ChatGPT Integration"></iframe>
180
197
  </div>
181
198
  </div>
182
199
 
@@ -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,7 @@ 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';
6
7
 
7
8
  /**
8
9
  * Initialize application on DOM ready
@@ -19,6 +20,9 @@ document.addEventListener('DOMContentLoaded', async () => {
19
20
 
20
21
  // Load Extension Path
21
22
  loadExtensionPath();
23
+
24
+ // Initialize Iframe Manager (New Feature)
25
+ initIframeManager();
22
26
  });
23
27
 
24
28
  /**