vg-coder-cli 2.0.31 → 2.0.32

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.
Files changed (40) hide show
  1. package/ARCHITECTURE.md +255 -0
  2. package/README.md +0 -11
  3. package/change.sh +0 -0
  4. package/dist/vg-coder-bundle.js +42 -0
  5. package/gulpfile.js +111 -0
  6. package/package.json +19 -11
  7. package/src/index.js +28 -220
  8. package/src/server/api-server.js +120 -428
  9. package/src/server/views/css/bubble.css +81 -0
  10. package/src/server/views/css/code-viewer.css +58 -0
  11. package/src/server/views/css/terminal.css +59 -155
  12. package/src/server/views/dashboard.css +78 -678
  13. package/src/server/views/dashboard.html +39 -278
  14. package/src/server/views/js/api.js +2 -22
  15. package/src/server/views/js/config.js +27 -15
  16. package/src/server/views/js/event-protocol.js +263 -0
  17. package/src/server/views/js/features/bubble-features/index.js +125 -0
  18. package/src/server/views/js/features/bubble-features/paste-run-feature.js +16 -0
  19. package/src/server/views/js/features/bubble-features/terminal-feature.js +16 -0
  20. package/src/server/views/js/features/bubble.js +175 -0
  21. package/src/server/views/js/features/code-viewer.js +90 -0
  22. package/src/server/views/js/features/commands.js +34 -81
  23. package/src/server/views/js/features/editor-tabs.js +19 -46
  24. package/src/server/views/js/features/git-view.js +63 -81
  25. package/src/server/views/js/features/iframe-manager.js +3 -97
  26. package/src/server/views/js/features/monaco-manager.js +19 -39
  27. package/src/server/views/js/features/project-switcher.js +7 -63
  28. package/src/server/views/js/features/resize.js +5 -16
  29. package/src/server/views/js/features/structure.js +38 -106
  30. package/src/server/views/js/features/terminal.js +102 -418
  31. package/src/server/views/js/handlers.js +60 -43
  32. package/src/server/views/js/main.js +75 -179
  33. package/src/server/views/js/shadow-entry.js +21 -0
  34. package/src/server/views/js/utils.js +48 -28
  35. package/src/server/views/vg-coder/_metadata/generated_indexed_rulesets/_ruleset1 +0 -0
  36. package/src/server/views/vg-coder/controller.js +33 -258
  37. package/vetgo-auto/chrome/src/utils/injector-script.ts +33 -258
  38. package/vetgo-auto/vg-coder.zip +0 -0
  39. package/src/server/views/dashboard.js +0 -457
  40. package/test-pty.js +0 -31
@@ -1,99 +1,5 @@
1
- const AI_PROVIDERS = [
2
- { id: 'chatgpt', name: 'ChatGPT', url: 'https://chat.openai.com' },
3
- { id: 'kimi', name: 'Kimi AI', url: 'https://www.kimi.com' },
4
- { id: 'deepseek', name: 'DeepSeek', url: 'https://chat.deepseek.com' },
5
- { id: 'gemini', name: 'Google Gemini', url: 'https://gemini.google.com/app' },
6
- { id: 'aistudio', name: 'Google AI Studio', url: 'https://aistudio.google.com/prompts/new_chat' },
7
- { id: 'gork', name: 'Gork', url: 'https://grok.com' },
8
- ];
9
-
1
+ // Iframe Manager - Deprecated and Removed
2
+ // This file is kept as a placeholder to prevent build errors if referenced elsewhere
10
3
  export function initIframeManager() {
11
- const select = document.getElementById('ai-provider-select');
12
- const iframe = document.getElementById('ai-iframe');
13
- // Removed externalLink reference
14
- const placeholderLink = document.getElementById('ai-placeholder-link');
15
-
16
- // Guide elements
17
- const guideContainer = document.getElementById('iframe-placeholder');
18
- const guideToggleBtn = document.getElementById('guide-toggle-btn');
19
- const guideCloseBtn = document.getElementById('guide-close-btn');
20
- const guideDoneBtn = document.getElementById('guide-done-btn');
21
-
22
- if (!select || !iframe) return;
23
-
24
- // 1. Populate options
25
- AI_PROVIDERS.forEach(provider => {
26
- const option = document.createElement('option');
27
- option.value = provider.id;
28
- option.textContent = provider.name;
29
- select.appendChild(option);
30
- });
31
-
32
- // 2. Load saved selection
33
- const savedProviderId = localStorage.getItem('ai_provider') || 'chatgpt';
34
- const isValid = AI_PROVIDERS.some(p => p.id === savedProviderId);
35
- select.value = isValid ? savedProviderId : AI_PROVIDERS[0].id;
36
-
37
- const updateProvider = (providerId) => {
38
- const provider = AI_PROVIDERS.find(p => p.id === providerId) || AI_PROVIDERS[0];
39
-
40
- // Add VG Coder context parameter to prevent nested iframe injection
41
- const urlWithParam = new URL(provider.url);
42
- urlWithParam.searchParams.set('vg_coder_context', 'true');
43
-
44
- // Reset iframe source to trigger reload
45
- iframe.src = 'about:blank';
46
- setTimeout(() => {
47
- iframe.src = urlWithParam.toString();
48
- }, 50);
49
-
50
- // Update placeholder link (without the parameter for direct access)
51
- if (placeholderLink) {
52
- placeholderLink.href = provider.url;
53
- placeholderLink.textContent = `Mở ${provider.name} tab mới ↗`;
54
- }
55
-
56
- localStorage.setItem('ai_provider', providerId);
57
- };
58
-
59
- // Initial load
60
- updateProvider(select.value);
61
-
62
- // Event listeners
63
- select.addEventListener('change', (e) => {
64
- updateProvider(e.target.value);
65
- });
66
-
67
- // --- GUIDE TOGGLE LOGIC ---
68
-
69
- const showGuide = () => {
70
- if (guideContainer) guideContainer.classList.remove('hidden');
71
- };
72
-
73
- const hideGuide = () => {
74
- if (guideContainer) guideContainer.classList.add('hidden');
75
- };
76
-
77
- const reloadIframe = () => {
78
- const currentSrc = iframe.src;
79
- iframe.src = 'about:blank';
80
- setTimeout(() => {
81
- iframe.src = currentSrc;
82
- }, 100);
83
- };
84
-
85
- if (guideToggleBtn) {
86
- guideToggleBtn.addEventListener('click', showGuide);
87
- }
88
-
89
- if (guideCloseBtn) {
90
- guideCloseBtn.addEventListener('click', hideGuide);
91
- }
92
-
93
- if (guideDoneBtn) {
94
- guideDoneBtn.addEventListener('click', () => {
95
- hideGuide();
96
- reloadIframe();
97
- });
98
- }
4
+ // No-op
99
5
  }
@@ -1,40 +1,44 @@
1
1
  import { API_BASE } from '../config.js';
2
- import { showToast } from '../utils.js';
2
+ import { showToast, getById } from '../utils.js';
3
3
 
4
4
  let editor = null;
5
- let models = new Map(); // Map<filePath, { model: ITextModel, viewState: object }>
5
+ let models = new Map();
6
6
  let isMonacoLoaded = false;
7
7
 
8
- // Cấu hình đường dẫn cho AMD Loader của Monaco
9
8
  export function initMonaco() {
10
- require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs' }});
9
+ if (typeof require !== 'undefined' && require.config) {
10
+ require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs' }});
11
+ }
11
12
  }
12
13
 
13
- /**
14
- * Đảm bảo Editor đã được khởi tạo
15
- */
16
14
  async function ensureEditor() {
17
15
  if (editor) return editor;
18
16
 
19
17
  return new Promise((resolve) => {
20
- require(['vs/editor/editor.main'], function () {
21
- const container = document.getElementById('monaco-container');
18
+ const amdRequire = window.require;
19
+
20
+ if (!amdRequire) {
21
+ console.error('Monaco Loader not found');
22
+ return;
23
+ }
24
+
25
+ amdRequire(['vs/editor/editor.main'], function () {
26
+ const container = getById('monaco-container');
27
+ if (!container) return;
22
28
 
23
- // Xác định theme dựa trên theme hiện tại của web
24
29
  const currentTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'vs-dark' : 'vs';
25
30
 
26
31
  editor = monaco.editor.create(container, {
27
32
  value: '',
28
33
  language: 'plaintext',
29
34
  theme: currentTheme,
30
- automaticLayout: true, // Tự động resize
35
+ automaticLayout: true,
31
36
  minimap: { enabled: true },
32
37
  fontSize: 13,
33
38
  fontFamily: 'Menlo, Monaco, "Courier New", monospace',
34
39
  scrollBeyondLastLine: false,
35
40
  });
36
41
 
37
- // Lắng nghe phím tắt Ctrl+S / Cmd+S để lưu
38
42
  editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
39
43
  saveCurrentFile();
40
44
  });
@@ -45,13 +49,10 @@ async function ensureEditor() {
45
49
  });
46
50
  }
47
51
 
48
- /**
49
- * Mở file vào Editor
50
- */
51
52
  export async function openFileInMonaco(path) {
52
53
  const editorInstance = await ensureEditor();
54
+ if (!editorInstance) return;
53
55
 
54
- // 1. Nếu Model đã tồn tại trong bộ nhớ -> Dùng lại
55
56
  if (models.has(path)) {
56
57
  const stored = models.get(path);
57
58
  editorInstance.setModel(stored.model);
@@ -62,22 +63,14 @@ export async function openFileInMonaco(path) {
62
63
  return;
63
64
  }
64
65
 
65
- // 2. Nếu chưa -> Fetch nội dung từ Server
66
66
  try {
67
67
  const res = await fetch(`${API_BASE}/api/read-file?path=${encodeURIComponent(path)}`);
68
68
  const data = await res.json();
69
69
 
70
70
  if (res.ok) {
71
- // Xác định ngôn ngữ từ extension
72
71
  const language = getLanguageFromPath(path);
73
-
74
- // Tạo Model mới
75
72
  const newModel = monaco.editor.createModel(data.content, language, monaco.Uri.file(path));
76
-
77
- // Lưu vào cache
78
73
  models.set(path, { model: newModel, viewState: null });
79
-
80
- // Gán vào Editor
81
74
  editorInstance.setModel(newModel);
82
75
  } else {
83
76
  showToast(`Error opening file: ${data.error}`, 'error');
@@ -87,9 +80,6 @@ export async function openFileInMonaco(path) {
87
80
  }
88
81
  }
89
82
 
90
- /**
91
- * Lưu trạng thái View (Scroll, Cursor) trước khi chuyển tab
92
- */
93
83
  export function saveViewState(path) {
94
84
  if (editor && models.has(path)) {
95
85
  const viewState = editor.saveViewState();
@@ -99,35 +89,26 @@ export function saveViewState(path) {
99
89
  }
100
90
  }
101
91
 
102
- /**
103
- * Giải phóng Model khi đóng Tab
104
- */
105
92
  export function disposeModel(path) {
106
93
  if (models.has(path)) {
107
94
  const stored = models.get(path);
108
- stored.model.dispose(); // Quan trọng: Tránh memory leak
95
+ stored.model.dispose();
109
96
  models.delete(path);
110
97
  }
111
98
  }
112
99
 
113
- /**
114
- * Cập nhật Theme cho Monaco khi web đổi theme
115
- */
116
100
  export function updateMonacoTheme(theme) {
117
101
  if (editor) {
118
102
  monaco.editor.setTheme(theme === 'dark' ? 'vs-dark' : 'vs');
119
103
  }
120
104
  }
121
105
 
122
- /**
123
- * Lưu file hiện tại
124
- */
125
106
  async function saveCurrentFile() {
126
107
  const model = editor.getModel();
127
108
  if (!model) return;
128
109
 
129
110
  const content = model.getValue();
130
- const filePath = model.uri.fsPath; // Lấy path từ URI
111
+ const filePath = model.uri.fsPath;
131
112
 
132
113
  try {
133
114
  const res = await fetch(`${API_BASE}/api/save-file`, {
@@ -146,7 +127,6 @@ async function saveCurrentFile() {
146
127
  }
147
128
  }
148
129
 
149
- // Helper: Map extension to Monaco Language
150
130
  function getLanguageFromPath(path) {
151
131
  const ext = path.split('.').pop().toLowerCase();
152
132
  const map = {
@@ -1,25 +1,15 @@
1
- // Project Switcher - Multi-project management UI
1
+ import { API_BASE } from '../config.js';
2
+ import { getById, showToast } from '../utils.js';
2
3
 
3
- /**
4
- * Initialize project switcher
5
- */
6
4
  export async function initProjectSwitcher() {
7
- // Load initial project list
8
5
  await loadProjects();
9
-
10
- // Poll for project updates every 5 seconds
11
6
  setInterval(loadProjects, 5000);
12
-
13
- // Listen for socket events
14
7
  setupSocketListeners();
15
8
  }
16
9
 
17
- /**
18
- * Load and display all projects
19
- */
20
10
  async function loadProjects() {
21
11
  try {
22
- const response = await fetch('/api/projects');
12
+ const response = await fetch(`${API_BASE}/api/projects`);
23
13
  const data = await response.json();
24
14
 
25
15
  updateProjectSelector(data.projects, data.activeProjectId);
@@ -30,17 +20,12 @@ async function loadProjects() {
30
20
  }
31
21
  }
32
22
 
33
- /**
34
- * Update project selector dropdown
35
- */
36
23
  function updateProjectSelector(projects, activeProjectId) {
37
- const selector = document.getElementById('project-selector');
24
+ const selector = getById('project-selector');
38
25
  if (!selector) return;
39
26
 
40
- // Clear existing options
41
27
  selector.innerHTML = '';
42
28
 
43
- // Add projects
44
29
  projects.forEach(project => {
45
30
  const option = document.createElement('option');
46
31
  option.value = project.id;
@@ -50,22 +35,16 @@ function updateProjectSelector(projects, activeProjectId) {
50
35
  });
51
36
  }
52
37
 
53
- /**
54
- * Update project count badge
55
- */
56
38
  function updateProjectCount(count) {
57
- const badge = document.getElementById('project-count');
39
+ const badge = getById('project-count');
58
40
  if (!badge) return;
59
41
 
60
42
  badge.textContent = `${count} project${count !== 1 ? 's' : ''}`;
61
43
  }
62
44
 
63
- /**
64
- * Switch to a different project
65
- */
66
45
  export async function switchProject(projectId) {
67
46
  try {
68
- const response = await fetch('/api/projects/switch', {
47
+ const response = await fetch(`${API_BASE}/api/projects/switch`, {
69
48
  method: 'POST',
70
49
  headers: { 'Content-Type': 'application/json' },
71
50
  body: JSON.stringify({ projectId })
@@ -74,7 +53,6 @@ export async function switchProject(projectId) {
74
53
  const data = await response.json();
75
54
 
76
55
  if (data.success) {
77
- // Emit custom event for other components to react
78
56
  const event = new CustomEvent('project-switched', {
79
57
  detail: {
80
58
  projectId,
@@ -84,7 +62,6 @@ export async function switchProject(projectId) {
84
62
  });
85
63
  window.dispatchEvent(event);
86
64
 
87
- // Show toast
88
65
  showToast(`Switched to: ${data.project.name}`, 'success');
89
66
  } else {
90
67
  showToast('Failed to switch project', 'error');
@@ -95,59 +72,26 @@ export async function switchProject(projectId) {
95
72
  }
96
73
  }
97
74
 
98
- /**
99
- * Setup socket listeners for real-time updates
100
- */
101
75
  function setupSocketListeners() {
102
76
  if (typeof io === 'undefined') return;
103
77
 
104
- const socket = io();
78
+ const socket = io(API_BASE);
105
79
 
106
- // New project registered
107
80
  socket.on('project:registered', (data) => {
108
81
  console.log('New project registered:', data);
109
82
  loadProjects();
110
83
  showToast(`New project joined: ${data.name}`, 'info');
111
84
  });
112
85
 
113
- // Project switched
114
86
  socket.on('project:switched', (data) => {
115
87
  console.log('Project switched:', data);
116
88
  loadProjects();
117
89
  });
118
90
 
119
- // Project removed
120
91
  socket.on('project:removed', (data) => {
121
92
  console.log('Project removed:', data);
122
93
  loadProjects();
123
94
  });
124
95
  }
125
96
 
126
- /**
127
- * Show toast notification
128
- */
129
- function showToast(message, type = 'info') {
130
- const toast = document.getElementById('toast');
131
- if (!toast) return;
132
-
133
- toast.textContent = message;
134
- toast.className = 'toast show';
135
-
136
- if (type === 'success') {
137
- toast.style.background = '#28a745';
138
- } else if (type === 'error') {
139
- toast.style.background = '#dc3545';
140
- } else if (type === 'warning') {
141
- toast.style.background = '#ffc107';
142
- toast.style.color = '#000';
143
- } else {
144
- toast.style.background = '#007bff';
145
- }
146
-
147
- setTimeout(() => {
148
- toast.classList.remove('show');
149
- }, 3000);
150
- }
151
-
152
- // Export for global access
153
97
  window.switchProject = switchProject;
@@ -1,15 +1,10 @@
1
- /**
2
- * Feature: Left Panel Resize
3
- * Allows the user to drag the right edge of the left panel to resize it.
4
- */
1
+ import { qs, getById } from '../utils.js';
5
2
 
6
3
  export function initResizeHandler() {
7
- const leftPanel = document.querySelector('.left-panel');
8
- const handle = document.getElementById('resize-handler'); // Match the ID we will add in HTML
9
- const splitLayout = document.querySelector('.split-layout');
4
+ const leftPanel = qs('.left-panel');
5
+ const handle = getById('resize-handler');
10
6
 
11
7
  if (!leftPanel || !handle) {
12
- console.warn('Resize elements not found');
13
8
  return;
14
9
  }
15
10
 
@@ -17,28 +12,23 @@ export function initResizeHandler() {
17
12
  let startX = 0;
18
13
  let startWidth = 0;
19
14
 
20
- // Mouse Down
21
15
  handle.addEventListener('mousedown', (e) => {
22
16
  isResizing = true;
23
17
  startX = e.clientX;
24
18
  startWidth = leftPanel.getBoundingClientRect().width;
25
19
 
26
- // Add resizing class for styling/cursor
27
20
  document.body.classList.add('resizing');
28
-
29
- // Disable text selection during drag
30
21
  e.preventDefault();
31
22
  });
32
23
 
33
- // Mouse Move
34
24
  document.addEventListener('mousemove', (e) => {
35
25
  if (!isResizing) return;
36
26
 
37
27
  requestAnimationFrame(() => {
38
28
  const currentX = e.clientX;
39
29
  const diffX = currentX - startX;
40
- const newWidth = Math.max(250, startWidth + diffX); // Min width 250px
41
- const maxWidth = window.innerWidth - 300; // Leave space for right panel
30
+ const newWidth = Math.max(250, startWidth + diffX);
31
+ const maxWidth = window.innerWidth - 300;
42
32
 
43
33
  if (newWidth < maxWidth) {
44
34
  leftPanel.style.flex = `0 0 ${newWidth}px`;
@@ -47,7 +37,6 @@ export function initResizeHandler() {
47
37
  });
48
38
  });
49
39
 
50
- // Mouse Up
51
40
  document.addEventListener('mouseup', () => {
52
41
  if (isResizing) {
53
42
  isResizing = false;