vg-coder-cli 2.0.35 → 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.
- package/dist/vg-coder-bundle.js +3 -3
- package/package.json +1 -1
- package/src/server/api-server.js +19 -0
- package/src/server/views/css/shortcuts-help.css +165 -0
- package/src/server/views/dashboard.html +19 -0
- package/src/server/views/js/api.js +13 -0
- package/src/server/views/js/features/keyboard-shortcuts.js +333 -0
- package/src/server/views/js/features/terminal.js +74 -1
- package/src/server/views/js/features/tool-window.js +7 -1
- package/src/server/views/js/handlers.js +21 -5
- package/src/server/views/js/main.js +4 -0
package/package.json
CHANGED
package/src/server/api-server.js
CHANGED
|
@@ -136,6 +136,25 @@ class ApiServer {
|
|
|
136
136
|
res.send(content);
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
// Count tokens using TokenManager (tiktoken)
|
|
140
|
+
this.app.post('/api/count-tokens', async (req, res) => {
|
|
141
|
+
try {
|
|
142
|
+
const { text } = req.body;
|
|
143
|
+
|
|
144
|
+
if (!text) {
|
|
145
|
+
return res.json({ tokens: 0 });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const tokenManager = new TokenManager();
|
|
149
|
+
const tokens = tokenManager.countTokens(text);
|
|
150
|
+
|
|
151
|
+
res.json({ tokens });
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error('Token counting error:', error);
|
|
154
|
+
res.status(500).json({ error: error.message, tokens: 0 });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
139
158
|
// --- PROJECT MANAGEMENT APIS (FIXED) ---
|
|
140
159
|
|
|
141
160
|
// List all projects
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* Keyboard Shortcuts Help Panel */
|
|
2
|
+
.vg-shortcuts-help {
|
|
3
|
+
position: fixed;
|
|
4
|
+
top: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
background: rgba(0, 0, 0, 0.7);
|
|
9
|
+
backdrop-filter: blur(5px);
|
|
10
|
+
display: none;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
z-index: 2147483646; /* Just below bubble */
|
|
14
|
+
animation: fadeIn 0.2s ease;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.vg-shortcuts-help.visible {
|
|
18
|
+
display: flex;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.vg-shortcuts-panel {
|
|
22
|
+
background: var(--bg-secondary, #1e1e1e);
|
|
23
|
+
border-radius: 12px;
|
|
24
|
+
padding: 32px;
|
|
25
|
+
max-width: 500px;
|
|
26
|
+
width: 90%;
|
|
27
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
|
28
|
+
animation: slideUp 0.3s ease;
|
|
29
|
+
position: relative;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.vg-shortcuts-header {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
margin-bottom: 24px;
|
|
37
|
+
padding-bottom: 16px;
|
|
38
|
+
border-bottom: 2px solid var(--border-color, #333);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.vg-shortcuts-title {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 12px;
|
|
45
|
+
font-size: 24px;
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
color: var(--text-primary, #fff);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.vg-shortcuts-close {
|
|
51
|
+
background: transparent;
|
|
52
|
+
border: none;
|
|
53
|
+
font-size: 28px;
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
color: var(--text-secondary, #888);
|
|
56
|
+
transition: all 0.2s;
|
|
57
|
+
padding: 4px 8px;
|
|
58
|
+
border-radius: 6px;
|
|
59
|
+
line-height: 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.vg-shortcuts-close:hover {
|
|
63
|
+
background: var(--bg-hover, #333);
|
|
64
|
+
color: var(--text-primary, #fff);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.vg-shortcuts-list {
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
gap: 16px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.vg-shortcut-item {
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
justify-content: space-between;
|
|
77
|
+
padding: 12px 16px;
|
|
78
|
+
background: var(--bg-primary, #2a2a2a);
|
|
79
|
+
border-radius: 8px;
|
|
80
|
+
transition: all 0.2s;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.vg-shortcut-item:hover {
|
|
84
|
+
background: var(--bg-hover, #333);
|
|
85
|
+
transform: translateX(4px);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.vg-shortcut-desc {
|
|
89
|
+
color: var(--text-primary, #fff);
|
|
90
|
+
font-size: 15px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.vg-shortcut-keys {
|
|
94
|
+
display: flex;
|
|
95
|
+
gap: 4px;
|
|
96
|
+
align-items: center;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.vg-shortcut-key {
|
|
100
|
+
background: linear-gradient(to bottom, #4a4a4a, #2a2a2a);
|
|
101
|
+
border: 1px solid #555;
|
|
102
|
+
border-radius: 6px;
|
|
103
|
+
padding: 6px 12px;
|
|
104
|
+
font-family: 'SF Mono', 'Monaco', 'Courier New', monospace;
|
|
105
|
+
font-size: 13px;
|
|
106
|
+
color: var(--text-primary, #fff);
|
|
107
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
|
108
|
+
min-width: 32px;
|
|
109
|
+
text-align: center;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.vg-shortcut-plus {
|
|
113
|
+
color: var(--text-secondary, #888);
|
|
114
|
+
font-size: 12px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.vg-shortcuts-footer {
|
|
118
|
+
margin-top: 24px;
|
|
119
|
+
padding-top: 16px;
|
|
120
|
+
border-top: 1px solid var(--border-color, #333);
|
|
121
|
+
text-align: center;
|
|
122
|
+
color: var(--text-secondary, #888);
|
|
123
|
+
font-size: 13px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* Animations */
|
|
127
|
+
@keyframes fadeIn {
|
|
128
|
+
from {
|
|
129
|
+
opacity: 0;
|
|
130
|
+
}
|
|
131
|
+
to {
|
|
132
|
+
opacity: 1;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@keyframes slideUp {
|
|
137
|
+
from {
|
|
138
|
+
opacity: 0;
|
|
139
|
+
transform: translateY(20px);
|
|
140
|
+
}
|
|
141
|
+
to {
|
|
142
|
+
opacity: 1;
|
|
143
|
+
transform: translateY(0);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Dark theme compatibility */
|
|
148
|
+
[data-theme="dark"] .vg-shortcuts-help {
|
|
149
|
+
--bg-primary: #1e1e1e;
|
|
150
|
+
--bg-secondary: #2a2a2a;
|
|
151
|
+
--bg-hover: #333;
|
|
152
|
+
--text-primary: #fff;
|
|
153
|
+
--text-secondary: #888;
|
|
154
|
+
--border-color: #444;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Light theme */
|
|
158
|
+
[data-theme="light"] .vg-shortcuts-help {
|
|
159
|
+
--bg-primary: #f5f5f5;
|
|
160
|
+
--bg-secondary: #fff;
|
|
161
|
+
--bg-hover: #e8e8e8;
|
|
162
|
+
--text-primary: #333;
|
|
163
|
+
--text-secondary: #666;
|
|
164
|
+
--border-color: #ddd;
|
|
165
|
+
}
|
|
@@ -130,6 +130,25 @@
|
|
|
130
130
|
</div>
|
|
131
131
|
</div>
|
|
132
132
|
|
|
133
|
+
<!-- Keyboard Shortcuts Help Overlay -->
|
|
134
|
+
<div id="shortcuts-help" class="vg-shortcuts-help">
|
|
135
|
+
<div class="vg-shortcuts-panel">
|
|
136
|
+
<div class="vg-shortcuts-header">
|
|
137
|
+
<div class="vg-shortcuts-title">
|
|
138
|
+
<span>⌨️</span>
|
|
139
|
+
<span>Keyboard Shortcuts</span>
|
|
140
|
+
</div>
|
|
141
|
+
<button class="vg-shortcuts-close" onclick="window.toggleShortcutsHelp()">×</button>
|
|
142
|
+
</div>
|
|
143
|
+
<div id="shortcuts-list" class="vg-shortcuts-list">
|
|
144
|
+
<!-- Shortcuts will be rendered dynamically by keyboard-shortcuts.js -->
|
|
145
|
+
</div>
|
|
146
|
+
<div class="vg-shortcuts-footer">
|
|
147
|
+
Press <kbd class="vg-shortcut-key">Cmd</kbd> + <kbd class="vg-shortcut-key">/</kbd> to toggle this help
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
133
152
|
<!-- Scripts injected via Gulp -->
|
|
134
153
|
</body>
|
|
135
154
|
</html>
|
|
@@ -144,3 +144,16 @@ export async function copyToClipboard(text) {
|
|
|
144
144
|
export async function readFromClipboard() {
|
|
145
145
|
return await navigator.clipboard.readText();
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
export async function countTokens(text) {
|
|
149
|
+
const res = await fetch(`${API_BASE}/api/count-tokens`, {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
headers: { 'Content-Type': 'application/json' },
|
|
152
|
+
body: JSON.stringify({ text })
|
|
153
|
+
});
|
|
154
|
+
if (!res.ok) {
|
|
155
|
+
throw new Error('Failed to count tokens');
|
|
156
|
+
}
|
|
157
|
+
const data = await res.json();
|
|
158
|
+
return data.tokens || 0;
|
|
159
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VG Coder Keyboard Shortcuts Module (Shadow DOM Compatible)
|
|
3
|
+
*
|
|
4
|
+
* Global keyboard shortcuts that work when VG Coder is injected via extension.
|
|
5
|
+
* Uses document-level listeners and composedPath() to check Shadow DOM isolation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getById, getRoot } from '../utils.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Centralized keyboard shortcuts configuration
|
|
12
|
+
* Single source of truth for all shortcuts
|
|
13
|
+
*/
|
|
14
|
+
const SHORTCUTS_CONFIG = [
|
|
15
|
+
{
|
|
16
|
+
key: '3',
|
|
17
|
+
action: 'toggle-dashboard',
|
|
18
|
+
description: 'Toggle VG Coder Dashboard (Open/Close)',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: '4',
|
|
22
|
+
action: 'open-git-panel',
|
|
23
|
+
description: 'Open VG Coder & Activate Git Panel',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: '/',
|
|
27
|
+
action: 'help-shortcuts',
|
|
28
|
+
description: 'Show/Hide Keyboard Shortcuts Help',
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate shortcuts registry from config
|
|
34
|
+
* Automatically creates CMD+key and CTRL+key variants
|
|
35
|
+
*/
|
|
36
|
+
const SHORTCUTS = {};
|
|
37
|
+
SHORTCUTS_CONFIG.forEach(({ key, action }) => {
|
|
38
|
+
SHORTCUTS[`CMD+${key.toUpperCase()}`] = action;
|
|
39
|
+
SHORTCUTS[`CTRL+${key.toUpperCase()}`] = action;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Platform detection
|
|
43
|
+
const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPod|iPad/.test(navigator.platform);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Convert keyboard event to shortcut key string
|
|
47
|
+
* @param {KeyboardEvent} event - Keyboard event
|
|
48
|
+
* @returns {string|null} Shortcut key string (e.g., "CMD+4")
|
|
49
|
+
*/
|
|
50
|
+
function getShortcutKey(event) {
|
|
51
|
+
const parts = [];
|
|
52
|
+
|
|
53
|
+
// Add modifier keys
|
|
54
|
+
if (event.metaKey) parts.push('CMD');
|
|
55
|
+
if (event.ctrlKey) parts.push('CTRL');
|
|
56
|
+
if (event.altKey) parts.push('ALT');
|
|
57
|
+
if (event.shiftKey) parts.push('SHIFT');
|
|
58
|
+
|
|
59
|
+
// Add main key (use event.key for number keys)
|
|
60
|
+
const key = event.key;
|
|
61
|
+
|
|
62
|
+
// Skip if only modifiers were pressed
|
|
63
|
+
if (['Meta', 'Control', 'Alt', 'Shift'].includes(key)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
parts.push(key.toUpperCase());
|
|
68
|
+
|
|
69
|
+
return parts.join('+');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if event originated from VG Coder Shadow DOM
|
|
74
|
+
* @param {KeyboardEvent} event - Keyboard event
|
|
75
|
+
* @returns {boolean} True if event is from VG Coder
|
|
76
|
+
*/
|
|
77
|
+
function isFromVGCoder(event) {
|
|
78
|
+
try {
|
|
79
|
+
// SIMPLIFIED APPROACH: Since we're in shadow DOM context,
|
|
80
|
+
// just check if VG Coder elements exist (we're always "in" VG Coder)
|
|
81
|
+
// This works because the bundle is only loaded when VG Coder is active
|
|
82
|
+
const appRoot = getById('vg-app-root');
|
|
83
|
+
const bubble = getById('vg-bubble');
|
|
84
|
+
|
|
85
|
+
// If VG Coder is loaded, we should handle ALL keyboard events
|
|
86
|
+
// (We're in isolated shadow DOM, so we won't interfere with page events)
|
|
87
|
+
const vgCoderExists = !!(appRoot || bubble);
|
|
88
|
+
|
|
89
|
+
if (!vgCoderExists) {
|
|
90
|
+
console.warn('[Keyboard] VG Coder elements not found');
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return true;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.warn('[Keyboard] Error checking VG Coder:', err);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Toggle VG Coder dashboard visibility
|
|
103
|
+
*/
|
|
104
|
+
function toggleDashboard() {
|
|
105
|
+
const appRoot = getById('vg-app-root');
|
|
106
|
+
if (!appRoot) {
|
|
107
|
+
console.warn('[Keyboard] Cannot toggle dashboard: vg-app-root not found');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const isVisible = appRoot.classList.contains('visible');
|
|
112
|
+
|
|
113
|
+
if (isVisible) {
|
|
114
|
+
appRoot.classList.remove('visible');
|
|
115
|
+
console.log('[Keyboard] Dashboard hidden');
|
|
116
|
+
} else {
|
|
117
|
+
appRoot.classList.add('visible');
|
|
118
|
+
console.log('[Keyboard] Dashboard shown');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Open VG Coder and activate Git panel
|
|
124
|
+
*/
|
|
125
|
+
function openGitPanel() {
|
|
126
|
+
const appRoot = getById('vg-app-root');
|
|
127
|
+
if (!appRoot) {
|
|
128
|
+
console.warn('[Keyboard] Cannot open Git panel: vg-app-root not found');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// First, make sure dashboard is visible
|
|
133
|
+
if (!appRoot.classList.contains('visible')) {
|
|
134
|
+
appRoot.classList.add('visible');
|
|
135
|
+
console.log('[Keyboard] Dashboard opened');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Then activate Git panel using getRoot() to query in Shadow DOM
|
|
139
|
+
const root = getRoot();
|
|
140
|
+
const gitButton = root.querySelector('.tool-window-icon[data-panel="git"]');
|
|
141
|
+
if (gitButton) {
|
|
142
|
+
gitButton.click();
|
|
143
|
+
console.log('[Keyboard] Git panel activated');
|
|
144
|
+
} else {
|
|
145
|
+
console.warn('[Keyboard] Git panel button not found');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Create new terminal
|
|
151
|
+
*/
|
|
152
|
+
function createNewTerminal() {
|
|
153
|
+
if (typeof window.createNewTerminal === 'function') {
|
|
154
|
+
window.createNewTerminal();
|
|
155
|
+
console.log('[Keyboard] New terminal created');
|
|
156
|
+
} else {
|
|
157
|
+
console.warn('[Keyboard] Terminal feature not available');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Handle keyboard event
|
|
163
|
+
* @param {KeyboardEvent} event - Keyboard event
|
|
164
|
+
*/
|
|
165
|
+
function handleKeyDown(event) {
|
|
166
|
+
// Debug: Log all keyboard events
|
|
167
|
+
const key = event.key;
|
|
168
|
+
const meta = event.metaKey;
|
|
169
|
+
const ctrl = event.ctrlKey;
|
|
170
|
+
|
|
171
|
+
// Only log if Cmd or Ctrl is pressed (to reduce noise)
|
|
172
|
+
if (meta || ctrl) {
|
|
173
|
+
console.log('[Keyboard] Key event:', { key, meta, ctrl, alt: event.altKey, shift: event.shiftKey });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check if event is from VG Coder shadow DOM
|
|
177
|
+
const fromVGCoder = isFromVGCoder(event);
|
|
178
|
+
if (!fromVGCoder) {
|
|
179
|
+
if (meta || ctrl) {
|
|
180
|
+
console.log('[Keyboard] Event ignored - not from VG Coder');
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const shortcutKey = getShortcutKey(event);
|
|
186
|
+
console.log('[Keyboard] Shortcut key detected:', shortcutKey);
|
|
187
|
+
|
|
188
|
+
if (!shortcutKey) return;
|
|
189
|
+
|
|
190
|
+
// Check if shortcut is registered
|
|
191
|
+
const action = SHORTCUTS[shortcutKey];
|
|
192
|
+
|
|
193
|
+
if (!action) {
|
|
194
|
+
console.log('[Keyboard] No action registered for:', shortcutKey);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Prevent default browser behavior
|
|
199
|
+
event.preventDefault();
|
|
200
|
+
event.stopPropagation();
|
|
201
|
+
|
|
202
|
+
console.log(`[Keyboard] Shortcut triggered: ${shortcutKey} -> ${action}`);
|
|
203
|
+
|
|
204
|
+
// Execute action
|
|
205
|
+
switch (action) {
|
|
206
|
+
case 'toggle-dashboard':
|
|
207
|
+
toggleDashboard();
|
|
208
|
+
break;
|
|
209
|
+
case 'open-git-panel':
|
|
210
|
+
openGitPanel();
|
|
211
|
+
break;
|
|
212
|
+
case 'help-shortcuts':
|
|
213
|
+
toggleShortcutsHelp();
|
|
214
|
+
break;
|
|
215
|
+
default:
|
|
216
|
+
console.warn(`[Keyboard] Unknown action: ${action}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Toggle shortcuts help panel
|
|
222
|
+
*/
|
|
223
|
+
function toggleShortcutsHelp() {
|
|
224
|
+
const helpPanel = getById('shortcuts-help');
|
|
225
|
+
if (!helpPanel) {
|
|
226
|
+
console.warn('[Keyboard] Help panel not found');
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const isVisible = helpPanel.classList.contains('visible');
|
|
231
|
+
|
|
232
|
+
if (isVisible) {
|
|
233
|
+
helpPanel.classList.remove('visible');
|
|
234
|
+
console.log('[Keyboard] Help panel hidden');
|
|
235
|
+
} else {
|
|
236
|
+
// Render shortcuts before showing
|
|
237
|
+
renderHelpPanel();
|
|
238
|
+
helpPanel.classList.add('visible');
|
|
239
|
+
console.log('[Keyboard] Help panel shown');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Render shortcuts in help panel
|
|
245
|
+
*/
|
|
246
|
+
function renderHelpPanel() {
|
|
247
|
+
const container = getById('shortcuts-list');
|
|
248
|
+
if (!container) return;
|
|
249
|
+
|
|
250
|
+
// Auto-generate shortcuts list from config
|
|
251
|
+
const prefix = isMac ? 'Cmd' : 'Ctrl';
|
|
252
|
+
const shortcuts = SHORTCUTS_CONFIG.map(({ key, description }) => ({
|
|
253
|
+
key: `${prefix}+${key}`,
|
|
254
|
+
desc: description,
|
|
255
|
+
}));
|
|
256
|
+
|
|
257
|
+
container.innerHTML = shortcuts.map(({ key, desc }) => {
|
|
258
|
+
const keys = key.split('+').map(k => `<span class="vg-shortcut-key">${k}</span>`);
|
|
259
|
+
const keysHtml = keys.join('<span class="vg-shortcut-plus">+</span>');
|
|
260
|
+
|
|
261
|
+
return `
|
|
262
|
+
<div class="vg-shortcut-item">
|
|
263
|
+
<div class="vg-shortcut-desc">${desc}</div>
|
|
264
|
+
<div class="vg-shortcut-keys">${keysHtml}</div>
|
|
265
|
+
</div>
|
|
266
|
+
`;
|
|
267
|
+
}).join('');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Create new terminal (continued from above)
|
|
272
|
+
*/
|
|
273
|
+
function continueCreateNewTerminal() {
|
|
274
|
+
// This function placeholder ensures proper code flow
|
|
275
|
+
// The actual implementation is below
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Fix the switch statement continuation
|
|
279
|
+
function handleKeyDownContinued(action) {
|
|
280
|
+
switch (action) {
|
|
281
|
+
case 'terminal-new':
|
|
282
|
+
createNewTerminal();
|
|
283
|
+
break;
|
|
284
|
+
default:
|
|
285
|
+
console.warn(`[Keyboard] Unknown action: ${action}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Initialize keyboard shortcuts
|
|
291
|
+
*/
|
|
292
|
+
export function initKeyboardShortcuts() {
|
|
293
|
+
console.log('[Keyboard] Initializing keyboard shortcuts...');
|
|
294
|
+
console.log(`[Keyboard] Platform: ${isMac ? 'macOS' : 'Windows/Linux'}`);
|
|
295
|
+
|
|
296
|
+
// Register global keydown listener at document level
|
|
297
|
+
// Using capture phase (true) to catch events before they reach target
|
|
298
|
+
document.addEventListener('keydown', handleKeyDown, true);
|
|
299
|
+
|
|
300
|
+
// Log registered shortcuts
|
|
301
|
+
const shortcuts = Object.keys(SHORTCUTS)
|
|
302
|
+
.filter(key => isMac ? key.startsWith('CMD') : key.startsWith('CTRL'))
|
|
303
|
+
.map(key => `${key} → ${SHORTCUTS[key]}`);
|
|
304
|
+
|
|
305
|
+
console.log('[Keyboard] Registered shortcuts:', shortcuts);
|
|
306
|
+
console.log('[Keyboard] Keyboard shortcuts initialized ✓');
|
|
307
|
+
|
|
308
|
+
// Make available for debugging and HTML onclick handlers
|
|
309
|
+
if (typeof window !== 'undefined') {
|
|
310
|
+
window.__VG_KEYBOARD_SHORTCUTS__ = {
|
|
311
|
+
shortcuts: SHORTCUTS,
|
|
312
|
+
platform: isMac ? 'mac' : 'win',
|
|
313
|
+
toggle: toggleDashboard,
|
|
314
|
+
terminal: createNewTerminal,
|
|
315
|
+
help: toggleShortcutsHelp,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// Expose for HTML onclick handler
|
|
319
|
+
window.toggleShortcutsHelp = toggleShortcutsHelp;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get help text for shortcuts (for future help UI)
|
|
325
|
+
* @returns {Array} Array of shortcut descriptions
|
|
326
|
+
*/
|
|
327
|
+
export function getShortcutsHelp() {
|
|
328
|
+
const prefix = isMac ? 'Cmd' : 'Ctrl';
|
|
329
|
+
return [
|
|
330
|
+
{ key: `${prefix}+4`, description: 'Toggle VG Coder Dashboard (Open/Close)' },
|
|
331
|
+
{ key: `${prefix}+5`, description: 'Create New Terminal' },
|
|
332
|
+
];
|
|
333
|
+
}
|
|
@@ -2,6 +2,7 @@ import { io } from 'socket.io-client';
|
|
|
2
2
|
import { Terminal } from 'xterm';
|
|
3
3
|
import { FitAddon } from 'xterm-addon-fit';
|
|
4
4
|
import { getById, showToast } from '../utils.js';
|
|
5
|
+
import { countTokens as countTokensAPI } from '../api.js';
|
|
5
6
|
|
|
6
7
|
let socket;
|
|
7
8
|
const activeTerminals = new Map();
|
|
@@ -43,8 +44,12 @@ export function createNewTerminal() {
|
|
|
43
44
|
// --- HTML STRUCTURE: Header + Body + Separate Input ---
|
|
44
45
|
wrapper.innerHTML = `
|
|
45
46
|
<div class="terminal-header" id="header-${termId}">
|
|
46
|
-
<div class="terminal-title-group"
|
|
47
|
+
<div class="terminal-title-group">
|
|
48
|
+
<span>>_</span> Terminal (${activeTerminals.size + 1})
|
|
49
|
+
<span class="terminal-token-count" id="token-count-${termId}">0 tokens</span>
|
|
50
|
+
</div>
|
|
47
51
|
<div class="terminal-controls">
|
|
52
|
+
<button class="term-btn copy-logs" onclick="window.copyTerminalLogs('${termId}')" title="Copy Logs">📋</button>
|
|
48
53
|
<button class="term-btn minimize" onclick="window.toggleMinimize('${termId}')">-</button>
|
|
49
54
|
<button class="term-btn maximize" onclick="window.toggleMaximize('${termId}')">+</button>
|
|
50
55
|
<button class="term-btn close" onclick="window.closeTerminal('${termId}')">x</button>
|
|
@@ -173,6 +178,73 @@ export function toggleMaximize(termId) {
|
|
|
173
178
|
}
|
|
174
179
|
}
|
|
175
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Copy terminal logs to clipboard with token count
|
|
183
|
+
*/
|
|
184
|
+
export async function copyTerminalLogs(termId) {
|
|
185
|
+
const session = activeTerminals.get(termId);
|
|
186
|
+
if (!session) {
|
|
187
|
+
showToast('Terminal not found', 'error');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
// Get terminal buffer content
|
|
193
|
+
const term = session.term;
|
|
194
|
+
const buffer = term.buffer.active;
|
|
195
|
+
const lines = [];
|
|
196
|
+
|
|
197
|
+
// Extract all lines from buffer
|
|
198
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
199
|
+
const line = buffer.getLine(i);
|
|
200
|
+
if (line) {
|
|
201
|
+
lines.push(line.translateToString(true));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const logsText = lines.join('\n').trim();
|
|
206
|
+
|
|
207
|
+
if (!logsText) {
|
|
208
|
+
showToast('No logs to copy', 'info');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Copy to clipboard first (fast)
|
|
213
|
+
await navigator.clipboard.writeText(logsText);
|
|
214
|
+
|
|
215
|
+
// Then count tokens via API (accurate)
|
|
216
|
+
try {
|
|
217
|
+
const tokens = await countTokensAPI(logsText);
|
|
218
|
+
|
|
219
|
+
// Update token count display
|
|
220
|
+
updateTokenCount(termId, tokens);
|
|
221
|
+
|
|
222
|
+
// Show toast
|
|
223
|
+
showToast(`📋 Copied ${tokens.toLocaleString()} tokens`, 'success');
|
|
224
|
+
} catch (err) {
|
|
225
|
+
console.error('[Terminal] Token counting error:', err);
|
|
226
|
+
// Still copied to clipboard, just show basic message
|
|
227
|
+
showToast('📋 Copied to clipboard', 'success');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
} catch (err) {
|
|
231
|
+
console.error('[Terminal] Copy logs error:', err);
|
|
232
|
+
showToast('Error copying logs', 'error');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Update token count display in terminal header
|
|
238
|
+
*/
|
|
239
|
+
function updateTokenCount(termId, tokens) {
|
|
240
|
+
const tokenEl = getById(`token-count-${termId}`);
|
|
241
|
+
if (tokenEl) {
|
|
242
|
+
tokenEl.textContent = `${tokens.toLocaleString()} tokens`;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Remove old simple countTokens function - no longer needed
|
|
247
|
+
|
|
176
248
|
function makeDraggable(element, handle) {
|
|
177
249
|
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
|
178
250
|
handle.onmousedown = dragMouseDown;
|
|
@@ -206,3 +278,4 @@ window.createNewTerminal = createNewTerminal;
|
|
|
206
278
|
window.closeTerminal = closeTerminalUI;
|
|
207
279
|
window.toggleMinimize = toggleMinimize;
|
|
208
280
|
window.toggleMaximize = toggleMaximize;
|
|
281
|
+
window.copyTerminalLogs = copyTerminalLogs;
|
|
@@ -23,7 +23,13 @@ export function initToolWindow() {
|
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
|
|
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');
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/**
|