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.
@@ -0,0 +1,154 @@
1
+ import { getById, qsa } from '../utils.js';
2
+
3
+ let activePanel = null;
4
+
5
+ /**
6
+ * Initialize Tool Window system
7
+ */
8
+ export function initToolWindow() {
9
+ const toolWindowBar = getById('tool-window-bar');
10
+ if (!toolWindowBar) {
11
+ console.warn('[ToolWindow] Tool window bar not found');
12
+ return;
13
+ }
14
+
15
+ // Attach event listeners to all tool window icons
16
+ const icons = qsa('.tool-window-icon');
17
+ icons.forEach(icon => {
18
+ icon.addEventListener('click', () => {
19
+ const panelId = icon.dataset.panel;
20
+ if (panelId) {
21
+ togglePanel(panelId);
22
+ }
23
+ });
24
+ });
25
+
26
+ // Open Project panel by default after a short delay
27
+ // This ensures all panel listeners are registered first
28
+ setTimeout(() => {
29
+ togglePanel('project');
30
+ }, 100);
31
+
32
+ console.log('[ToolWindow] Initialized with Project panel active');
33
+ }
34
+
35
+ /**
36
+ * Toggle a specific panel
37
+ * @param {string} panelId - Panel ID to toggle (e.g., 'project', 'git')
38
+ */
39
+ export function togglePanel(panelId) {
40
+ const panel = getById(`tool-panel-${panelId}`);
41
+ const container = getById('tool-panel-container');
42
+ const icon = qsa(`.tool-window-icon[data-panel="${panelId}"]`)[0];
43
+
44
+ if (!panel || !container) {
45
+ console.error('[ToolWindow] Panel or container not found:', panelId);
46
+ return;
47
+ }
48
+
49
+ // If clicking the same active panel, close it
50
+ if (activePanel === panelId) {
51
+ closeAllPanels();
52
+ return;
53
+ }
54
+
55
+ // Close all panels first
56
+ closeAllPanels();
57
+
58
+ // Open the new panel
59
+ setActivePanel(panelId);
60
+ container.classList.add('expanded');
61
+ panel.classList.add('active');
62
+ if (icon) icon.classList.add('active');
63
+
64
+ activePanel = panelId;
65
+
66
+ // Trigger panel-specific initialization if needed
67
+ triggerPanelInit(panelId);
68
+
69
+ console.log('[ToolWindow] Opened panel:', panelId);
70
+ }
71
+
72
+ /**
73
+ * Close all open panels
74
+ */
75
+ export function closeAllPanels() {
76
+ const container = getById('tool-panel-container');
77
+ const panels = qsa('.tool-panel');
78
+ const icons = qsa('.tool-window-icon');
79
+
80
+ if (container) {
81
+ container.classList.remove('expanded');
82
+ }
83
+
84
+ panels.forEach(panel => {
85
+ panel.classList.remove('active');
86
+ });
87
+
88
+ icons.forEach(icon => {
89
+ icon.classList.remove('active');
90
+ });
91
+
92
+ activePanel = null;
93
+
94
+ console.log('[ToolWindow] Closed all panels');
95
+ }
96
+
97
+ /**
98
+ * Set a panel as active (internal use)
99
+ * @param {string} panelId - Panel ID
100
+ */
101
+ function setActivePanel(panelId) {
102
+ const panels = qsa('.tool-panel');
103
+ panels.forEach(panel => {
104
+ if (panel.id === `tool-panel-${panelId}`) {
105
+ panel.classList.add('active');
106
+ } else {
107
+ panel.classList.remove('active');
108
+ }
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Trigger initialization for panel-specific features
114
+ * @param {string} panelId - Panel ID
115
+ */
116
+ function triggerPanelInit(panelId) {
117
+ // Dispatch custom event that panel modules can listen to
118
+ const event = new CustomEvent('tool-panel-opened', {
119
+ detail: { panelId }
120
+ });
121
+ document.dispatchEvent(event);
122
+ }
123
+
124
+ /**
125
+ * Get the currently active panel ID
126
+ * @returns {string|null} Active panel ID or null
127
+ */
128
+ export function getActivePanel() {
129
+ return activePanel;
130
+ }
131
+
132
+ /**
133
+ * Register a callback for when a panel is closed
134
+ * @param {Function} callback - Callback function
135
+ */
136
+ export function onPanelClose(callback) {
137
+ const container = getById('tool-panel-container');
138
+ if (container) {
139
+ const observer = new MutationObserver((mutations) => {
140
+ mutations.forEach((mutation) => {
141
+ if (mutation.attributeName === 'class') {
142
+ if (!container.classList.contains('expanded')) {
143
+ callback();
144
+ }
145
+ }
146
+ });
147
+ });
148
+ observer.observe(container, { attributes: true });
149
+ }
150
+ }
151
+
152
+ // Expose to window for HTML onclick if needed
153
+ window.toggleToolPanel = togglePanel;
154
+ window.closeToolPanels = closeAllPanels;
@@ -36,6 +36,17 @@ export function initEventHandlers() {
36
36
  }
37
37
  });
38
38
 
39
+ // Copy System Prompt
40
+ globalDispatcher.on(EVENT_TYPES.COPY_PROMPT, async (event) => {
41
+ console.log('[Handlers] Copy Prompt event received:', event);
42
+ try {
43
+ await navigator.clipboard.writeText(SYSTEM_PROMPT);
44
+ showToast('📋 Copied System Prompt', 'success');
45
+ } catch (err) {
46
+ showToast('Failed to copy: ' + err.message, 'error');
47
+ }
48
+ });
49
+
39
50
  console.log('[Handlers] Event handlers initialized');
40
51
  }
41
52
 
@@ -159,7 +170,8 @@ export async function testExecute(event) {
159
170
  export async function executeFromClipboard(event) {
160
171
  const btn = event?.target?.closest('.btn');
161
172
  const bashInput = getById('execute-bash');
162
- if (!bashInput) return;
173
+
174
+ // Don't return early if bashInput is missing - it's optional when called from bubble menu
163
175
 
164
176
  if (btn) showLoading(btn, btn.innerHTML);
165
177
  try {
@@ -169,13 +181,25 @@ export async function executeFromClipboard(event) {
169
181
  if (btn) resetButton(btn);
170
182
  return;
171
183
  }
172
- bashInput.value = clipboardText;
184
+
185
+ // Only populate bashInput if it exists (when called from dashboard)
186
+ if (bashInput) {
187
+ bashInput.value = clipboardText;
188
+ }
189
+
173
190
  const data = await executeScript(clipboardText);
174
- showResponse('execute-response', data, !data.success);
191
+
192
+ // Only show response in execute-response container if it exists
193
+ const responseContainer = getById('execute-response');
194
+ if (responseContainer) {
195
+ showResponse('execute-response', data, !data.success);
196
+ }
175
197
 
176
198
  if (data.success) {
177
199
  showToast('Thực thi OK', 'success');
178
- bashInput.value = '';
200
+ if (bashInput) {
201
+ bashInput.value = '';
202
+ }
179
203
  } else {
180
204
  data.syntaxError ? showToast('Lỗi syntax script', 'error') : showToast('Thực thi thất bại', 'error');
181
205
  }
@@ -183,7 +207,10 @@ export async function executeFromClipboard(event) {
183
207
  if (err.name === 'NotAllowedError') {
184
208
  showToast('Không có quyền clipboard', 'error');
185
209
  } else {
186
- showResponse('execute-response', { error: err.message }, true);
210
+ const responseContainer = getById('execute-response');
211
+ if (responseContainer) {
212
+ showResponse('execute-response', { error: err.message }, true);
213
+ }
187
214
  showToast('Lỗi: ' + err.message, 'error');
188
215
  }
189
216
  }
@@ -8,9 +8,15 @@ import { initEditorTabs } from './features/editor-tabs.js';
8
8
  // REMOVED: initMonaco
9
9
  import { initResizeHandler } from './features/resize.js';
10
10
  import { initSavedCommands } from './features/commands.js';
11
- import { initProjectSwitcher } from './features/project-switcher.js';
12
11
  import './features/structure.js';
13
12
  import { initBubble } from './features/bubble.js';
13
+ import { initKeyboardShortcuts } from './features/keyboard-shortcuts.js';
14
+
15
+ // NEW: Import Tool Window modules
16
+ import { initToolWindow } from './features/tool-window.js';
17
+ import { initProjectPanel } from './features/project-panel.js';
18
+ import { initGitPanel } from './features/git-panel.js';
19
+ import { initCommandsPanel } from './features/commands-panel.js';
14
20
 
15
21
  export async function initMain() {
16
22
  console.log('VG Coder: Starting Main Logic...');
@@ -19,8 +25,6 @@ export async function initMain() {
19
25
  const promptEl = getById('prompt-text');
20
26
  if (promptEl) promptEl.textContent = SYSTEM_PROMPT;
21
27
 
22
- await checkServerStatus();
23
- await loadProjectInfo();
24
28
 
25
29
  initTheme();
26
30
  loadExtensionPath();
@@ -28,15 +32,24 @@ export async function initMain() {
28
32
  // Initialize event handlers FIRST (before bubble which dispatches events)
29
33
  initEventHandlers();
30
34
 
35
+ // NEW: Initialize Tool Window system
36
+ initToolWindow();
37
+ initProjectPanel();
38
+ initGitPanel();
39
+ initCommandsPanel();
40
+
31
41
  initGitView();
32
42
  initTerminal();
33
43
  initEditorTabs();
34
44
  initResizeHandler();
35
45
  initSavedCommands();
36
- await initProjectSwitcher();
46
+ // initProjectSwitcher(); // REMOVED: project-panel.js now handles project polling
37
47
 
38
48
  // Init Bubble (will use event protocol)
39
49
  initBubble();
50
+
51
+ // Initialize keyboard shortcuts (global hotkeys for Shadow DOM)
52
+ initKeyboardShortcuts();
40
53
 
41
54
  console.log('✅ VG Coder: Initialization Complete');
42
55
  } catch (e) {
@@ -44,11 +57,9 @@ export async function initMain() {
44
57
  }
45
58
  }
46
59
 
47
- // ... (Giữ nguyên các hàm helper khác: switchProject, loadProjectInfo, initTheme...)
48
60
  // Để ngắn gọn, tôi copy lại phần còn lại của main.js
49
61
  window.addEventListener('project-switched', async (event) => {
50
62
  const { projectId, projectName } = event.detail;
51
- await loadProjectInfo();
52
63
  if (window.updateTerminalVisibility) window.updateTerminalVisibility(projectId);
53
64
  if (window.loadSavedCommands) await window.loadSavedCommands();
54
65
  const treeContainer = getById('structure-tree');
@@ -57,31 +68,6 @@ window.addEventListener('project-switched', async (event) => {
57
68
  if (treeContent) treeContent.innerHTML = '';
58
69
  });
59
70
 
60
- async function checkServerStatus() {
61
- const statusEl = getById('status');
62
- if (!statusEl) return;
63
- const isHealthy = await checkHealth();
64
- if (isHealthy) {
65
- statusEl.textContent = '●';
66
- statusEl.style.background = 'transparent';
67
- statusEl.style.color = 'var(--ios-green)';
68
- } else {
69
- statusEl.textContent = '●';
70
- statusEl.style.background = 'transparent';
71
- statusEl.style.color = 'var(--ios-red)';
72
- }
73
- }
74
-
75
- async function loadProjectInfo() {
76
- try {
77
- const res = await fetch(`${API_BASE}/api/info?path=.`);
78
- const data = await res.json();
79
- const projectNameEl = getById('project-name');
80
- const projectMetaEl = getById('project-meta');
81
- if (projectNameEl) projectNameEl.textContent = data.path.split(/[\\/]/).pop();
82
- if (projectMetaEl) projectMetaEl.textContent = `${data.primaryType} • ${data.path}`;
83
- } catch (err) {}
84
- }
85
71
 
86
72
  function initTheme() {
87
73
  const themeBtn = getById('theme-toggle');