vg-coder-cli 2.0.11 → 2.0.14

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,155 @@
1
+ /* --- LAYOUT HEADER --- */
2
+ .git-toggle-group {
3
+ display: flex;
4
+ align-items: center;
5
+ gap: 8px;
6
+ margin-left: 10px;
7
+ padding-left: 10px;
8
+ border-left: 1px solid var(--ios-separator);
9
+ flex-shrink: 0;
10
+ }
11
+
12
+ .git-toggle-btn {
13
+ padding: 0 12px;
14
+ background: transparent;
15
+ border: 1px solid var(--ios-separator);
16
+ border-radius: 6px;
17
+ cursor: pointer;
18
+ font-size: 13px;
19
+ font-weight: 500;
20
+ color: var(--text-primary);
21
+ height: 32px;
22
+ display: flex;
23
+ align-items: center;
24
+ }
25
+
26
+ .git-toggle-btn.active {
27
+ background: var(--ios-blue);
28
+ color: #fff;
29
+ border-color: var(--ios-blue);
30
+ }
31
+
32
+ .git-refresh-btn {
33
+ width: 32px;
34
+ height: 32px;
35
+ border-radius: 6px;
36
+ border: 1px solid var(--ios-separator);
37
+ background: var(--ios-card);
38
+ color: var(--text-primary);
39
+ cursor: pointer;
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: center;
43
+ }
44
+
45
+ /* --- CONTAINER CHÍNH --- */
46
+ .right-panel {
47
+ position: relative;
48
+ /* Quan trọng */
49
+ }
50
+
51
+ .d2h-file-list {
52
+ position: fixed;
53
+ top: 0;
54
+ left: 0;
55
+ z-index: 99;
56
+ background-color: #0d1117;
57
+ overflow: scroll;
58
+ height: calc(100vh - 46px);
59
+ width: 446px;
60
+ top: 46px;
61
+ }
62
+
63
+ .git-view-container {
64
+ position: absolute;
65
+ top: 50px;
66
+ left: 0;
67
+ right: 0;
68
+ bottom: 0;
69
+ z-index: 9999;
70
+ /* Z-index cao nhất */
71
+ background: #0d1117;
72
+ /* Nền đen GitHub */
73
+ display: none;
74
+ flex-direction: column;
75
+ overflow: scroll;
76
+ }
77
+
78
+ .git-view-container.active {
79
+ display: flex;
80
+ }
81
+
82
+ /* --- DEBUG / SAFETY NET CSS --- */
83
+ /* Ép toàn bộ text trong vùng code phải có màu sáng */
84
+ .d2h-wrapper * {
85
+ box-sizing: border-box;
86
+ }
87
+
88
+ /* Force text color */
89
+ .d2h-code-line-ctn,
90
+ .d2h-code-line,
91
+ .hljs {
92
+ color: #e6edf3 !important;
93
+ /* Trắng xám */
94
+ background: transparent !important;
95
+ }
96
+
97
+ /* Force line number color */
98
+ .d2h-code-side-linenumber {
99
+ color: #6e7681 !important;
100
+ background: #0d1117 !important;
101
+ border-color: #30363d !important;
102
+ }
103
+
104
+ /* Fix background diff */
105
+ .d2h-ins {
106
+ background-color: rgba(46, 160, 67, 0.15) !important;
107
+ }
108
+
109
+ .d2h-del {
110
+ background-color: rgba(248, 81, 73, 0.15) !important;
111
+ }
112
+
113
+ /* Layout */
114
+ .d2h-files-wrapper {
115
+ flex: 1;
116
+ overflow: auto;
117
+ background: #0d1117;
118
+ }
119
+
120
+ .d2h-file-list-wrapper {
121
+ width: 250px;
122
+ flex-shrink: 0;
123
+ background: #0d1117;
124
+ border-right: 1px solid #30363d;
125
+ display: flex;
126
+ flex-direction: column;
127
+ }
128
+
129
+ .d2h-file-list-header,
130
+ .d2h-file-header {
131
+ background: #161b22 !important;
132
+ border-bottom: 1px solid #30363d !important;
133
+ color: #e6edf3 !important;
134
+ position: sticky;
135
+ top: 0;
136
+ z-index: 10;
137
+ }
138
+
139
+ .d2h-file-list a {
140
+ color: #8b949e !important;
141
+ text-decoration: none;
142
+ display: block;
143
+ padding: 5px 10px;
144
+ }
145
+
146
+ .d2h-file-list a:hover {
147
+ color: #58a6ff !important;
148
+ background: #161b22;
149
+ }
150
+
151
+ /* Ẩn rác */
152
+ .d2h-file-switch,
153
+ .d2h-tag {
154
+ display: none !important;
155
+ }
@@ -0,0 +1,98 @@
1
+ /* Layer chứa các terminal trôi nổi */
2
+ #floating-terminals-layer {
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ width: 0;
7
+ height: 0;
8
+ z-index: 10000; /* Cao hơn mọi thứ */
9
+ }
10
+
11
+ /* Style cho từng cửa sổ terminal */
12
+ .floating-terminal {
13
+ position: absolute;
14
+ width: 600px;
15
+ height: 400px;
16
+ background: #1e1e1e;
17
+ border-radius: 8px;
18
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
19
+ border: 1px solid #333;
20
+ display: flex;
21
+ flex-direction: column;
22
+ overflow: hidden;
23
+ resize: both; /* Cho phép resize cửa sổ */
24
+ min-width: 300px;
25
+ min-height: 200px;
26
+ }
27
+
28
+ /* Header dùng để drag */
29
+ .terminal-header {
30
+ background: #252526;
31
+ padding: 8px 12px;
32
+ display: flex;
33
+ justify-content: space-between;
34
+ align-items: center;
35
+ cursor: grab;
36
+ border-bottom: 1px solid #333;
37
+ user-select: none;
38
+ }
39
+
40
+ .terminal-header:active {
41
+ cursor: grabbing;
42
+ }
43
+
44
+ .terminal-title-group {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 8px;
48
+ color: #ccc;
49
+ font-family: monospace;
50
+ font-size: 12px;
51
+ font-weight: 600;
52
+ }
53
+
54
+ .terminal-controls {
55
+ display: flex;
56
+ gap: 6px;
57
+ }
58
+
59
+ .term-btn {
60
+ width: 12px;
61
+ height: 12px;
62
+ border-radius: 50%;
63
+ border: none;
64
+ cursor: pointer;
65
+ }
66
+
67
+ .term-btn.close {
68
+ background: #FF5F56;
69
+ }
70
+ .term-btn.close:hover {
71
+ background: #ff3b30;
72
+ }
73
+
74
+ .term-btn.minimize {
75
+ background: #FFBD2E;
76
+ }
77
+
78
+ .term-btn.maximize {
79
+ background: #27C93F;
80
+ }
81
+
82
+ /* Phần nội dung chứa xterm */
83
+ .terminal-body {
84
+ flex: 1;
85
+ position: relative;
86
+ padding: 5px;
87
+ background: #1e1e1e;
88
+ overflow: hidden;
89
+ }
90
+
91
+ /* Scrollbar customization */
92
+ .xterm-viewport::-webkit-scrollbar {
93
+ width: 8px;
94
+ }
95
+ .xterm-viewport::-webkit-scrollbar-thumb {
96
+ background: #424242;
97
+ border-radius: 4px;
98
+ }
@@ -9,6 +9,24 @@
9
9
  <link rel="stylesheet" href="/dashboard.css">
10
10
  <link rel="stylesheet" href="/css/structure.css">
11
11
  <link rel="stylesheet" href="/css/iframe.css">
12
+ <link rel="stylesheet" href="/css/git-view.css">
13
+ <link rel="stylesheet" href="/css/terminal.css">
14
+
15
+ <!-- Syntax Highlighting -->
16
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" />
17
+
18
+ <!-- Git Diff -->
19
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
20
+
21
+ <!-- Terminal Dependencies -->
22
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
23
+ <script src="/socket.io/socket.io.js"></script>
24
+ <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
25
+ <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
26
+
27
+ <!-- Other Libs -->
28
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
29
+
12
30
  <script>
13
31
  (function () {
14
32
  const savedTheme = localStorage.getItem('theme') || 'light';
@@ -31,6 +49,14 @@
31
49
  <span id="theme-icon">🌙</span>
32
50
  </button>
33
51
  </div>
52
+
53
+ <!-- Quick Actions -->
54
+ <div class="endpoint-card" style="display: flex; gap: 10px; align-items: center; justify-content: space-between;">
55
+ <span style="font-weight: 600;">Tools:</span>
56
+ <button class="btn" onclick="createNewTerminal()" style="background: #252526; border: 1px solid #444;">
57
+ <span>🖥️</span> New Terminal
58
+ </button>
59
+ </div>
34
60
 
35
61
  <!-- System Prompt Section -->
36
62
  <div class="system-prompt-card">
@@ -96,7 +122,7 @@
96
122
  </div>
97
123
 
98
124
  <div class="endpoints">
99
- <!-- Execute Bash -->
125
+ <!-- Execute Bash (RESTORED) -->
100
126
  <div class="endpoint-card">
101
127
  <div class="endpoint-header">
102
128
  <div class="endpoint-title-group">
@@ -175,7 +201,7 @@
175
201
  </div>
176
202
  </div>
177
203
 
178
- <!-- CỘT PHẢI: AI Iframe with Selector -->
204
+ <!-- CỘT PHẢI: AI Iframe with Selector & Git View -->
179
205
  <div class="right-panel">
180
206
  <div class="ai-header">
181
207
  <div class="ai-select-group">
@@ -184,9 +210,17 @@
184
210
  <!-- Options filled by JS -->
185
211
  </select>
186
212
  </div>
213
+
214
+ <button id="git-view-toggle" class="git-toggle-btn">View Changes</button>
215
+ <button id="git-refresh-btn" class="btn-icon-head" style="display:none; margin-left:5px;" title="Refresh">↻</button>
216
+
187
217
  <a id="ai-external-link" href="#" target="_blank" class="btn-icon-head" title="Open in new tab">↗</a>
188
218
  </div>
189
219
 
220
+ <!-- Git View Container (Hidden by default) -->
221
+ <div id="git-view-container" class="git-view-container"></div>
222
+
223
+ <!-- AI Iframe Container -->
190
224
  <div class="ai-iframe-container">
191
225
  <div class="iframe-placeholder">
192
226
  <p>⚠️ Nếu trang trắng, hãy cài Extension <b>"Ignore X-Frame-Options"</b></p>
@@ -196,9 +230,11 @@
196
230
  </div>
197
231
  </div>
198
232
  </div>
233
+
234
+ <!-- Floating Terminals Container (Fixed on top of everything) -->
235
+ <div id="floating-terminals-layer"></div>
199
236
 
200
237
  <div class="toast" id="toast"></div>
201
238
  <script type="module" src="/js/main.js"></script>
202
239
  </body>
203
-
204
240
  </html>
@@ -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,174 @@
1
+ // Terminal Logic: Multi-instance & Floating
2
+
3
+ let socket;
4
+ const activeTerminals = new Map(); // Map<termId, { term, fitAddon, element }>
5
+
6
+ // Z-Index Management
7
+ let maxZIndex = 10001;
8
+
9
+ export function initTerminal() {
10
+ if (typeof io === 'undefined' || typeof Terminal === 'undefined') {
11
+ console.error('Libraries missing for Terminal');
12
+ return;
13
+ }
14
+
15
+ // 1. Init Socket
16
+ socket = io();
17
+
18
+ // 2. Global Event Listeners
19
+ socket.on('terminal:data', ({ termId, data }) => {
20
+ const session = activeTerminals.get(termId);
21
+ if (session) {
22
+ session.term.write(data);
23
+ }
24
+ });
25
+
26
+ socket.on('terminal:exit', ({ termId }) => {
27
+ closeTerminalUI(termId);
28
+ });
29
+ }
30
+
31
+ /**
32
+ * Tạo một cửa sổ terminal mới
33
+ */
34
+ export function createNewTerminal() {
35
+ const termId = 'term_' + Date.now();
36
+ const layer = document.getElementById('floating-terminals-layer');
37
+
38
+ // 1. Create DOM Elements
39
+ const wrapper = document.createElement('div');
40
+ wrapper.className = 'floating-terminal';
41
+ wrapper.id = `wrapper-${termId}`;
42
+ wrapper.style.top = '100px';
43
+ wrapper.style.left = '100px';
44
+ // Offset vị trí một chút nếu mở nhiều cái
45
+ const offset = activeTerminals.size * 30;
46
+ wrapper.style.top = `${100 + offset}px`;
47
+ wrapper.style.left = `${400 + offset}px`;
48
+ wrapper.style.zIndex = ++maxZIndex;
49
+
50
+ wrapper.innerHTML = `
51
+ <div class="terminal-header" id="header-${termId}">
52
+ <div class="terminal-title-group">
53
+ <span>>_</span> Terminal (${activeTerminals.size + 1})
54
+ </div>
55
+ <div class="terminal-controls">
56
+ <button class="term-btn close" onclick="window.closeTerminal('${termId}')" title="Close"></button>
57
+ </div>
58
+ </div>
59
+ <div class="terminal-body" id="body-${termId}"></div>
60
+ `;
61
+
62
+ layer.appendChild(wrapper);
63
+
64
+ // 2. Init xterm.js
65
+ const term = new Terminal({
66
+ cursorBlink: true,
67
+ fontSize: 13,
68
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
69
+ theme: {
70
+ background: '#1e1e1e',
71
+ foreground: '#f0f0f0'
72
+ }
73
+ });
74
+
75
+ const fitAddon = new FitAddon.FitAddon();
76
+ term.loadAddon(fitAddon);
77
+
78
+ term.open(document.getElementById(`body-${termId}`));
79
+ fitAddon.fit();
80
+
81
+ // 3. Register Events
82
+
83
+ // Bring to front on click
84
+ wrapper.addEventListener('mousedown', () => {
85
+ wrapper.style.zIndex = ++maxZIndex;
86
+ });
87
+
88
+ // Send input
89
+ term.onData(data => {
90
+ socket.emit('terminal:input', { termId, data });
91
+ });
92
+
93
+ // Resize Observer to refit terminal when window is resized
94
+ const resizeObserver = new ResizeObserver(() => {
95
+ try {
96
+ fitAddon.fit();
97
+ socket.emit('terminal:resize', {
98
+ termId,
99
+ cols: term.cols,
100
+ rows: term.rows
101
+ });
102
+ } catch (e) {}
103
+ });
104
+ resizeObserver.observe(document.getElementById(`body-${termId}`));
105
+
106
+ // 4. Make Draggable
107
+ makeDraggable(wrapper, document.getElementById(`header-${termId}`));
108
+
109
+ // 5. Store Session
110
+ activeTerminals.set(termId, { term, fitAddon, element: wrapper });
111
+
112
+ // 6. Init Backend Process
113
+ socket.emit('terminal:init', {
114
+ termId,
115
+ cols: term.cols,
116
+ rows: term.rows
117
+ });
118
+ }
119
+
120
+ /**
121
+ * Đóng terminal UI & Process
122
+ */
123
+ export function closeTerminalUI(termId) {
124
+ const session = activeTerminals.get(termId);
125
+ if (session) {
126
+ // Remove from DOM
127
+ session.element.remove();
128
+ // Clean map
129
+ activeTerminals.delete(termId);
130
+ // Tell server to kill
131
+ socket.emit('terminal:kill', { termId });
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Logic Drag & Drop
137
+ */
138
+ function makeDraggable(element, handle) {
139
+ let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
140
+
141
+ handle.onmousedown = dragMouseDown;
142
+
143
+ function dragMouseDown(e) {
144
+ e.preventDefault();
145
+ // Get mouse cursor position at startup
146
+ pos3 = e.clientX;
147
+ pos4 = e.clientY;
148
+ document.onmouseup = closeDragElement;
149
+ // Call function whenever cursor moves
150
+ document.onmousemove = elementDrag;
151
+ }
152
+
153
+ function elementDrag(e) {
154
+ e.preventDefault();
155
+ // Calculate new cursor position
156
+ pos1 = pos3 - e.clientX;
157
+ pos2 = pos4 - e.clientY;
158
+ pos3 = e.clientX;
159
+ pos4 = e.clientY;
160
+ // Set element's new position
161
+ element.style.top = (element.offsetTop - pos2) + "px";
162
+ element.style.left = (element.offsetLeft - pos1) + "px";
163
+ }
164
+
165
+ function closeDragElement() {
166
+ // Stop moving when mouse button is released
167
+ document.onmouseup = null;
168
+ document.onmousemove = null;
169
+ }
170
+ }
171
+
172
+ // Global Exports for HTML onclick
173
+ window.createNewTerminal = createNewTerminal;
174
+ window.closeTerminal = closeTerminalUI;