vg-coder-cli 2.0.34 → 2.0.36

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.
@@ -1,335 +1,92 @@
1
- import { getGitStatus, getGitDiff, stageFile, unstageFile, commitChanges, discardChange } from '../api.js';
2
- import { showToast, getById, qsa } from '../utils.js';
3
- // FIX: Import Diff2HtmlUI from npm package
1
+ import { getById, qsa, showToast } from '../utils.js';
2
+ import { getGitDiff } from '../api.js';
4
3
  import { Diff2HtmlUI } from 'diff2html/lib/ui/js/diff2html-ui';
5
4
 
6
5
  let isGitMode = false;
7
- let currentStaged = [];
8
- let currentChanges = [];
9
6
 
7
+ /**
8
+ * Initialize Git View (Diff Viewer Only)
9
+ * This module only handles displaying git diffs in an overlay
10
+ * Commit/Stage/Unstage features are in git-panel.js
11
+ */
10
12
  export function initGitView() {
11
13
  const toggleBtn = getById('git-view-toggle');
12
- const refreshBtn = getById('git-refresh-btn');
13
-
14
- if (toggleBtn) toggleBtn.addEventListener('click', toggleGitMode);
15
- if (refreshBtn) refreshBtn.addEventListener('click', loadGitData);
14
+ if (toggleBtn) {
15
+ toggleBtn.addEventListener('click', toggleGitMode);
16
+ }
17
+
18
+ // Auto-close diff view when user switches to a tool panel
19
+ document.addEventListener('tool-panel-opened', (event) => {
20
+ console.log('[GitView] Tool panel opened:', event.detail.panelId);
21
+ if (isGitMode) {
22
+ console.log('[GitView] Auto-closing diff view due to panel switch');
23
+ closeGitView();
24
+ }
25
+ });
16
26
  }
17
27
 
28
+ /**
29
+ * Toggle Git view overlay
30
+ */
18
31
  async function toggleGitMode() {
19
32
  const gitContainer = getById('git-view-container');
20
33
  const toggleBtn = getById('git-view-toggle');
21
- const refreshBtn = getById('git-refresh-btn');
22
34
  const toggleText = getById('git-toggle-text');
23
35
 
24
36
  isGitMode = !isGitMode;
25
-
37
+
26
38
  if (isGitMode) {
27
39
  gitContainer.classList.add('active');
28
40
  toggleBtn.classList.add('active');
29
- if (toggleText) toggleText.textContent = 'Close Changes';
30
- if (refreshBtn) refreshBtn.style.display = 'flex';
31
- await loadGitData();
32
- } else {
33
- gitContainer.classList.remove('active');
34
- toggleBtn.classList.remove('active');
35
- if (toggleText) toggleText.textContent = 'View Changes';
36
- if (refreshBtn) refreshBtn.style.display = 'none';
37
- }
38
- }
39
-
40
- async function loadGitData() {
41
- const container = getById('git-view-container');
42
-
43
- if (!container.querySelector('.git-sidebar')) {
44
- container.innerHTML = `
45
- <div class="git-sidebar">
46
- <div class="git-commit-section">
47
- <textarea id="git-commit-message" class="git-commit-input" placeholder="Message (⌘Enter to commit)" rows="1"></textarea>
48
- <button id="git-commit-btn" class="git-commit-btn">
49
- <span>✓</span> Commit
50
- </button>
51
- </div>
52
- <div class="git-section">
53
- <div class="git-section-header" id="header-staged">
54
- <div style="display:flex;align-items:center;gap:5px;">
55
- <span>STAGED CHANGES</span>
56
- <span class="git-badge" id="badge-staged">0</span>
57
- </div>
58
- <span class="git-btn-action" id="unstage-all" title="Unstage All" style="display:none;">-</span>
59
- </div>
60
- <ul class="git-tree-root" id="tree-staged"></ul>
61
- </div>
62
- <div class="git-section">
63
- <div class="git-section-header" id="header-changes">
64
- <div style="display:flex;align-items:center;gap:5px;">
65
- <span>CHANGES</span>
66
- <span class="git-badge" id="badge-changes">0</span>
67
- </div>
68
- <div style="display:flex;gap:5px;">
69
- <span class="git-btn-action destructive" id="discard-all" title="Discard All Changes">↺</span>
70
- <span class="git-btn-action" id="stage-all" title="Stage All">+</span>
71
- </div>
72
- </div>
73
- <ul class="git-tree-root" id="tree-changes"></ul>
74
- </div>
75
- </div>
76
- <div class="git-diff-area" id="git-diff-viewer">
77
- <div class="git-empty-state">Select a file to view changes</div>
78
- </div>
79
- `;
41
+ if (toggleText) toggleText.textContent = 'Close Diff';
80
42
 
81
- setTimeout(() => {
82
- const stageAll = getById('stage-all');
83
- const unstageAll = getById('unstage-all');
84
- const discardAll = getById('discard-all');
85
- const commitBtn = getById('git-commit-btn');
86
- const commitInput = getById('git-commit-message');
87
-
88
- if(stageAll) stageAll.addEventListener('click', async (e) => { e.stopPropagation(); await handleStage('*'); });
89
- if(unstageAll) unstageAll.addEventListener('click', async (e) => { e.stopPropagation(); await handleUnstage('*'); });
90
- if(discardAll) discardAll.addEventListener('click', async (e) => { e.stopPropagation(); await handleDiscard('*'); });
91
-
92
- if(commitBtn) commitBtn.addEventListener('click', handleCommit);
93
- if(commitInput) commitInput.addEventListener('keydown', (e) => {
94
- if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
95
- e.preventDefault();
96
- handleCommit();
97
- }
98
- });
99
- }, 0);
100
- }
101
-
102
- const refreshBtn = getById('git-refresh-btn');
103
- if(refreshBtn) refreshBtn.disabled = true;
104
-
105
- try {
106
- const { staged, changes } = await getGitStatus();
107
- currentStaged = staged;
108
- currentChanges = changes;
109
- renderTrees();
110
- } catch (err) {
111
- showToast('Error loading git status: ' + err.message, 'error');
112
- } finally {
113
- if(refreshBtn) refreshBtn.disabled = false;
114
- }
115
- }
116
-
117
- async function handleCommit() {
118
- const input = getById('git-commit-message');
119
- const message = input.value.trim();
120
- const btn = getById('git-commit-btn');
121
-
122
- if (!message) {
123
- showToast('Please enter a commit message', 'error');
124
- input.focus();
125
- return;
126
- }
127
-
128
- if (currentStaged.length === 0) {
129
- showToast('Nothing to commit. Stage changes first.', 'error');
130
- return;
131
- }
132
-
133
- btn.disabled = true;
134
- btn.innerHTML = '<span>...</span> Committing...';
135
-
136
- try {
137
- await commitChanges(message);
138
- showToast('Commit successful', 'success');
139
- input.value = '';
140
- await loadGitData();
141
- } catch (err) {
142
- showToast('Commit failed: ' + err.message, 'error');
143
- } finally {
144
- btn.disabled = false;
145
- btn.innerHTML = '<span>✓</span> Commit';
43
+ // Render simple diff viewer
44
+ renderDiffViewer();
45
+ } else {
46
+ closeGitView();
146
47
  }
147
48
  }
148
49
 
149
- function renderTrees() {
150
- const treeStaged = getById('tree-staged');
151
- const badgeStaged = getById('badge-staged');
152
- const unstageAll = getById('unstage-all');
50
+ /**
51
+ * Close Git view overlay
52
+ */
53
+ function closeGitView() {
54
+ const gitContainer = getById('git-view-container');
55
+ const toggleBtn = getById('git-view-toggle');
56
+ const toggleText = getById('git-toggle-text');
153
57
 
154
- if (badgeStaged) badgeStaged.textContent = currentStaged.length;
155
- if (unstageAll) unstageAll.style.display = currentStaged.length > 0 ? 'block' : 'none';
156
- if (treeStaged) {
157
- treeStaged.innerHTML = '';
158
- if (currentStaged.length > 0) {
159
- const rootStaged = buildFileTree(currentStaged);
160
- renderTreeNodes(rootStaged, treeStaged, 'staged', 0);
161
- }
162
- }
163
-
164
- const treeChanges = getById('tree-changes');
165
- const badgeChanges = getById('badge-changes');
166
- const discardAll = getById('discard-all');
167
-
168
- if (badgeChanges) badgeChanges.textContent = currentChanges.length;
169
- if (discardAll) discardAll.style.display = currentChanges.length > 0 ? 'block' : 'none';
170
- if (treeChanges) {
171
- treeChanges.innerHTML = '';
172
- if (currentChanges.length > 0) {
173
- const rootChanges = buildFileTree(currentChanges);
174
- renderTreeNodes(rootChanges, treeChanges, 'changes', 0);
175
- }
176
- }
58
+ isGitMode = false;
59
+ gitContainer.classList.remove('active');
60
+ toggleBtn.classList.remove('active');
61
+ if (toggleText) toggleText.textContent = 'View Diff';
177
62
  }
178
63
 
179
- function buildFileTree(files) {
180
- const root = {};
181
- files.forEach(file => {
182
- const parts = file.path.split('/');
183
- let current = root;
184
- parts.forEach((part, index) => {
185
- if (!current[part]) {
186
- current[part] = { name: part, children: {}, fileData: null };
187
- }
188
- if (index === parts.length - 1) {
189
- current[part].fileData = file;
190
- }
191
- current = current[part].children;
192
- });
193
- });
194
- return root;
195
- }
196
-
197
- function renderTreeNodes(nodes, container, type, depth) {
198
- const items = Object.values(nodes);
199
- items.sort((a, b) => {
200
- const aIsFolder = Object.keys(a.children).length > 0;
201
- const bIsFolder = Object.keys(b.children).length > 0;
202
- if (aIsFolder && !bIsFolder) return -1;
203
- if (!aIsFolder && bIsFolder) return 1;
204
- return a.name.localeCompare(b.name);
205
- });
206
-
207
- items.forEach(node => {
208
- const isFolder = Object.keys(node.children).length > 0;
209
- const li = document.createElement('li');
210
- li.className = 'git-tree-node';
211
-
212
- const content = document.createElement('div');
213
- content.className = 'git-tree-content';
214
- content.style.paddingLeft = (depth * 16 + 8) + 'px';
215
-
216
- const arrow = document.createElement('span');
217
- arrow.className = 'git-arrow';
218
- arrow.textContent = isFolder ? '▼' : '';
219
- content.appendChild(arrow);
220
-
221
- const iconSpan = document.createElement('span');
222
- iconSpan.className = 'git-icon';
223
- if (isFolder) {
224
- iconSpan.textContent = '📂';
225
- } else {
226
- const status = node.fileData.status;
227
- iconSpan.textContent = getStatusIcon(status);
228
- iconSpan.className += ' git-status-' + status;
229
- }
230
- content.appendChild(iconSpan);
231
-
232
- const label = document.createElement('span');
233
- label.className = isFolder ? 'git-label git-dir-label' : 'git-label git-file-label';
234
- label.textContent = node.name;
235
- content.appendChild(label);
236
-
237
- if (!isFolder && node.fileData) {
238
- const actions = document.createElement('div');
239
- actions.className = 'git-actions';
240
-
241
- if (type === 'changes') {
242
- const discardBtn = document.createElement('button');
243
- discardBtn.className = 'git-btn-action destructive';
244
- discardBtn.textContent = '↺';
245
- discardBtn.title = 'Discard Changes';
246
- discardBtn.onclick = (e) => { e.stopPropagation(); handleDiscard(node.fileData.path); };
247
- actions.appendChild(discardBtn);
248
- }
249
-
250
- const btn = document.createElement('button');
251
- btn.className = 'git-btn-action';
252
- if (type === 'staged') {
253
- btn.textContent = '-';
254
- btn.title = 'Unstage';
255
- btn.onclick = (e) => { e.stopPropagation(); handleUnstage(node.fileData.path); };
256
- } else {
257
- btn.textContent = '+';
258
- btn.title = 'Stage';
259
- btn.onclick = (e) => { e.stopPropagation(); handleStage(node.fileData.path); };
260
- }
261
- actions.appendChild(btn);
262
- content.appendChild(actions);
263
- }
264
-
265
- content.addEventListener('click', (e) => {
266
- e.stopPropagation();
267
- if (isFolder) {
268
- li.classList.toggle('collapsed');
269
- } else {
270
- qsa('.git-tree-content').forEach(el => el.classList.remove('selected'));
271
- content.classList.add('selected');
272
- loadDiffView(node.fileData.path, type);
273
- }
274
- });
275
-
276
- li.appendChild(content);
277
-
278
- if (isFolder) {
279
- const ul = document.createElement('ul');
280
- renderTreeNodes(node.children, ul, type, depth + 1);
281
- li.appendChild(ul);
282
- }
283
-
284
- container.appendChild(li);
285
- });
286
- }
287
-
288
- function getStatusIcon(status) {
289
- if(status === 'M') return 'M';
290
- if(status === 'A' || status === 'U') return 'U';
291
- if(status === 'D') return 'D';
292
- if(status === 'R') return 'R';
293
- return '?';
294
- }
295
-
296
- async function handleStage(path) {
297
- try {
298
- await stageFile(path);
299
- await loadGitData();
300
- } catch (err) {
301
- showToast('Stage failed: ' + err.message, 'error');
302
- }
303
- }
304
-
305
- async function handleUnstage(path) {
306
- try {
307
- await unstageFile(path);
308
- await loadGitData();
309
- } catch (err) {
310
- showToast('Unstage failed: ' + err.message, 'error');
311
- }
312
- }
313
-
314
- async function handleDiscard(path) {
315
- const isAll = path === '*';
316
- const msg = isAll
317
- ? 'Are you sure you want to discard ALL changes? This is irreversible!'
318
- : `Discard changes to ${path}? This is irreversible!`;
319
-
320
- if (confirm(msg)) {
321
- try {
322
- await discardChange(path);
323
- await loadGitData();
324
- showToast('Changes discarded', 'success');
325
- } catch (err) {
326
- showToast('Discard failed: ' + err.message, 'error');
327
- }
328
- }
64
+ /**
65
+ * Render diff viewer container
66
+ */
67
+ function renderDiffViewer() {
68
+ const container = getById('git-view-container');
69
+ container.innerHTML = `
70
+ <div class="git-diff-area" id="git-diff-viewer">
71
+ <div class="git-empty-state">
72
+ <div style="font-size: 48px; margin-bottom: 16px;">📄</div>
73
+ <div style="font-size: 14px; color: #8b949e;">Select a file from Git panel to view changes</div>
74
+ </div>
75
+ </div>
76
+ `;
329
77
  }
330
78
 
79
+ /**
80
+ * Load and display diff for a specific file
81
+ * Called by git-panel.js when user clicks on a file
82
+ */
331
83
  async function loadDiffView(filePath, type) {
332
84
  const viewer = getById('git-diff-viewer');
85
+ if (!viewer) {
86
+ console.error('[GitView] Diff viewer not found');
87
+ return;
88
+ }
89
+
333
90
  viewer.innerHTML = '<div class="git-empty-state">Loading diff...</div>';
334
91
 
335
92
  // FIX: Using imported class
@@ -339,8 +96,14 @@ async function loadDiffView(filePath, type) {
339
96
  const diff = await getGitDiff(filePath, type === 'staged' ? 'staged' : 'working');
340
97
 
341
98
  if (!diff || !diff.trim()) {
342
- viewer.innerHTML = '<div class="git-empty-state">No text changes detected (binary file?)</div>';
343
- return;
99
+ viewer.innerHTML = `
100
+ <div class="git-empty-state">
101
+ <div style="font-size: 48px; margin-bottom: 16px;">📝</div>
102
+ <div style="font-size: 14px; color: #8b949e;">No text changes detected</div>
103
+ <div style="font-size: 12px; color: #6e7681; margin-top: 8px;">This might be a binary file or have no changes</div>
104
+ </div>
105
+ `;
106
+ return;
344
107
  }
345
108
 
346
109
  viewer.innerHTML = '';
@@ -357,6 +120,15 @@ async function loadDiffView(filePath, type) {
357
120
  ui.highlightCode();
358
121
 
359
122
  } catch (err) {
360
- viewer.innerHTML = `<div class="git-empty-state" style="color:#f85149">Error loading diff: ${err.message}</div>`;
123
+ viewer.innerHTML = `
124
+ <div class="git-empty-state" style="color:#f85149">
125
+ <div style="font-size: 48px; margin-bottom: 16px;">⚠️</div>
126
+ <div style="font-size: 14px;">Error loading diff</div>
127
+ <div style="font-size: 12px; margin-top: 8px;">${err.message}</div>
128
+ </div>
129
+ `;
361
130
  }
362
131
  }
132
+
133
+ // Export loadDiffView as global function so git-panel can call it
134
+ window.loadGitDiffView = loadDiffView;