vg-coder-cli 2.0.8 → 2.0.10
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/README.md +19 -0
- package/SYSTEM_PROMPT.md +157 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +206 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +206 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/detectors/index.html +116 -0
- package/coverage/lcov-report/src/detectors/project-detector.js.html +1084 -0
- package/coverage/lcov-report/src/exporter/html-exporter.js.html +2839 -0
- package/coverage/lcov-report/src/exporter/index.html +116 -0
- package/coverage/lcov-report/src/ignore/ignore-manager.js.html +979 -0
- package/coverage/lcov-report/src/ignore/index.html +116 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/index.js.html +928 -0
- package/coverage/lcov-report/src/scanner/file-scanner.js.html +1903 -0
- package/coverage/lcov-report/src/scanner/index.html +116 -0
- package/coverage/lcov-report/src/tokenizer/index.html +116 -0
- package/coverage/lcov-report/src/tokenizer/token-manager.js.html +1252 -0
- package/coverage/lcov-report/src/utils/helpers.js.html +469 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +1396 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/detectors/index.html +116 -0
- package/coverage/src/detectors/project-detector.js.html +1084 -0
- package/coverage/src/exporter/html-exporter.js.html +2839 -0
- package/coverage/src/exporter/index.html +116 -0
- package/coverage/src/ignore/ignore-manager.js.html +979 -0
- package/coverage/src/ignore/index.html +116 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/index.js.html +928 -0
- package/coverage/src/scanner/file-scanner.js.html +1903 -0
- package/coverage/src/scanner/index.html +116 -0
- package/coverage/src/tokenizer/index.html +116 -0
- package/coverage/src/tokenizer/token-manager.js.html +1252 -0
- package/coverage/src/utils/helpers.js.html +469 -0
- package/coverage/src/utils/index.html +116 -0
- package/jest.config.js +16 -0
- package/package.json +5 -3
- package/scripts/build.js +40 -0
- package/src/scanner/file-scanner.js +3 -104
- package/src/server/api-server.js +74 -18
- package/src/server/views/css/structure.css +148 -0
- package/src/server/views/dashboard.css +176 -312
- package/src/server/views/dashboard.html +153 -85
- package/src/server/views/js/api.js +19 -2
- package/src/server/views/js/features/structure.js +221 -0
- package/src/server/views/js/handlers.js +38 -70
- package/src/server/views/js/main.js +60 -0
- package/src/server/views/js/utils.js +10 -0
- package/src/server/views/vg-coder/assets/icon128.png +0 -0
- package/src/server/views/vg-coder/assets/icon16.png +0 -0
- package/src/server/views/vg-coder/assets/icon48.png +0 -0
- package/src/server/views/vg-coder/background.js +2 -0
- package/src/server/views/vg-coder/background.js.LICENSE.txt +118 -0
- package/src/server/views/vg-coder/controller.js +1 -0
- package/src/server/views/vg-coder/manifest.json +58 -0
- package/src/server/views/vg-coder/options.css +164 -0
- package/src/server/views/vg-coder/options.html +48 -0
- package/src/server/views/vg-coder/options.js +1 -0
- package/src/server/views/vg-coder/rules.json +23 -0
- package/src/tokenizer/token-manager.js +52 -2
- package/vg-coder-cli-2.0.10.tgz +0 -0
- package/vg-coder.zip +0 -0
- package/vg-coder-cli-2.0.8.tgz +0 -0
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
<meta charset="UTF-8">
|
|
6
6
|
<meta name="viewport"
|
|
7
7
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
8
|
-
<
|
|
9
|
-
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
10
|
-
<title>VG Coder API Dashboard</title>
|
|
8
|
+
<title>VG Coder - Split View</title>
|
|
11
9
|
<link rel="stylesheet" href="/dashboard.css">
|
|
10
|
+
<link rel="stylesheet" href="/css/structure.css">
|
|
12
11
|
<script>
|
|
13
|
-
|
|
14
|
-
(function() {
|
|
12
|
+
(function () {
|
|
15
13
|
const savedTheme = localStorage.getItem('theme') || 'light';
|
|
16
14
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
17
15
|
})();
|
|
@@ -19,100 +17,170 @@
|
|
|
19
17
|
</head>
|
|
20
18
|
|
|
21
19
|
<body>
|
|
22
|
-
<div class="
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<div
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
<!-- System Prompt Section -->
|
|
36
|
-
<div class="system-prompt-card">
|
|
37
|
-
<div class="system-prompt-header" onclick="toggleSystemPrompt()">
|
|
38
|
-
<div class="header-title-group">
|
|
39
|
-
<button class="btn-icon-head" onclick="copySystemPromptFromHeader(event)" title="Copy System Prompt">
|
|
40
|
-
📋
|
|
20
|
+
<div class="split-layout">
|
|
21
|
+
<!-- CỘT TRÁI: Giao diện VG Coder cũ -->
|
|
22
|
+
<div class="left-panel">
|
|
23
|
+
<div class="container">
|
|
24
|
+
<div class="header">
|
|
25
|
+
<div class="header-content">
|
|
26
|
+
<span class="status" id="status">● Server Starting...</span>
|
|
27
|
+
<div style="height: 5px;"></div>
|
|
28
|
+
</div>
|
|
29
|
+
<button class="theme-toggle" id="theme-toggle" title="Toggle Dark Mode">
|
|
30
|
+
<span id="theme-icon">🌙</span>
|
|
41
31
|
</button>
|
|
42
|
-
<h2>System Prompt</h2>
|
|
43
32
|
</div>
|
|
44
|
-
<span class="toggle-icon" id="toggle-icon">▼</span>
|
|
45
|
-
</div>
|
|
46
|
-
<div class="system-prompt-content" id="system-prompt-content">
|
|
47
|
-
<div class="prompt-text" id="prompt-text"></div>
|
|
48
|
-
<button class="btn btn-copy" onclick="copySystemPrompt()">
|
|
49
|
-
<span id="copy-icon">📋</span>
|
|
50
|
-
<span id="copy-text">Copy System Prompt</span>
|
|
51
|
-
</button>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
33
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
34
|
+
<!-- System Prompt Section -->
|
|
35
|
+
<div class="system-prompt-card">
|
|
36
|
+
<div class="system-prompt-header" onclick="toggleSystemPrompt()">
|
|
37
|
+
<div class="header-title-group">
|
|
38
|
+
<button class="btn-icon-head" onclick="copySystemPromptFromHeader(event)"
|
|
39
|
+
title="Copy System Prompt">
|
|
40
|
+
📋
|
|
41
|
+
</button>
|
|
42
|
+
<h2>System Prompt</h2>
|
|
43
|
+
</div>
|
|
44
|
+
<span class="toggle-icon" id="toggle-icon">▼</span>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="system-prompt-content" id="system-prompt-content">
|
|
47
|
+
<div class="prompt-text" id="prompt-text"></div>
|
|
48
|
+
<button class="btn btn-copy" onclick="copySystemPrompt(event)">
|
|
49
|
+
<span id="copy-icon">📋</span>
|
|
50
|
+
<span id="copy-text">Copy System Prompt</span>
|
|
51
|
+
</button>
|
|
63
52
|
</div>
|
|
64
|
-
<!-- Right side: Download Icon -->
|
|
65
|
-
<button class="btn-icon-head" onclick="testAnalyze()" title="Download Project Source">
|
|
66
|
-
📥
|
|
67
|
-
</button>
|
|
68
|
-
</div>
|
|
69
|
-
<p class="endpoint-desc">Phân tích dự án và lấy toàn bộ source code.</p>
|
|
70
|
-
<div class="form-group">
|
|
71
|
-
<label>Path</label>
|
|
72
|
-
<input type="text" id="analyze-path" value="." placeholder="Project path (e.g. .)">
|
|
73
|
-
</div>
|
|
74
|
-
<div class="btn-group">
|
|
75
|
-
<!-- Big Download button removed as it's now in the header -->
|
|
76
|
-
<button class="btn btn-copy" onclick="copyAnalyzeResult()">
|
|
77
|
-
<span id="analyze-copy-icon">📋</span>
|
|
78
|
-
<span id="analyze-copy-text">Copy Text</span>
|
|
79
|
-
</button>
|
|
80
53
|
</div>
|
|
81
|
-
<div class="response" id="analyze-response"></div>
|
|
82
|
-
</div>
|
|
83
54
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
55
|
+
<!-- Extension Installation Guide -->
|
|
56
|
+
<div class="system-prompt-card" id="extension-card">
|
|
57
|
+
<div class="system-prompt-header" onclick="toggleExtensionGuide()">
|
|
58
|
+
<div class="header-title-group">
|
|
59
|
+
<span style="font-size: 16px;">🧩</span>
|
|
60
|
+
<h2>Cài đặt Chrome Extension</h2>
|
|
61
|
+
</div>
|
|
62
|
+
<span class="toggle-icon" id="ext-toggle-icon">▼</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="system-prompt-content" id="extension-content">
|
|
65
|
+
<div class="extension-steps">
|
|
66
|
+
<ol>
|
|
67
|
+
<li style="margin-bottom: 8px;">
|
|
68
|
+
Copy link này và dán vào tab mới:
|
|
69
|
+
<div class="form-group" style="margin-top: 5px; margin-bottom: 0;">
|
|
70
|
+
<div style="display: flex; gap: 5px;">
|
|
71
|
+
<input type="text" id="chrome-url-input" readonly value="chrome://extensions" onclick="this.select()" style="font-family: monospace;">
|
|
72
|
+
<button class="btn btn-copy" style="flex: 0 0 40px; padding: 0;" onclick="copyChromeUrl(event)" title="Copy URL">
|
|
73
|
+
<span>📋</span>
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</li>
|
|
78
|
+
<li>Bật <b>Developer mode</b> (Góc phải trên cùng)</li>
|
|
79
|
+
<li>Chọn <b>Load unpacked</b></li>
|
|
80
|
+
<li>Dán đường dẫn bên dưới vào:</li>
|
|
81
|
+
</ol>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="form-group">
|
|
84
|
+
<input type="text" id="extension-path-input" readonly value="Loading path..." onclick="this.select()">
|
|
85
|
+
</div>
|
|
86
|
+
<button class="btn btn-copy" onclick="copyExtensionPath(event)">
|
|
87
|
+
<span id="ext-copy-icon">📋</span>
|
|
88
|
+
<span id="ext-copy-text">Copy Path</span>
|
|
89
|
+
</button>
|
|
90
90
|
</div>
|
|
91
|
-
<!-- Could add an execute icon here later if needed -->
|
|
92
|
-
</div>
|
|
93
|
-
<p class="endpoint-desc">Thực thi bash script với syntax validation.</p>
|
|
94
|
-
<div class="form-group">
|
|
95
|
-
<label>Bash Script</label>
|
|
96
|
-
<textarea id="execute-bash"
|
|
97
|
-
placeholder="mkdir -p src/test echo 'Hello' > src/test/hello.txt"></textarea>
|
|
98
91
|
</div>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
92
|
+
|
|
93
|
+
<div class="endpoints">
|
|
94
|
+
<!-- Analyze -->
|
|
95
|
+
<div class="endpoint-card">
|
|
96
|
+
<div class="endpoint-header">
|
|
97
|
+
<div class="endpoint-title-group">
|
|
98
|
+
<span class="method post">POST</span>
|
|
99
|
+
<span class="endpoint-path">/analyze</span>
|
|
100
|
+
</div>
|
|
101
|
+
<button class="btn-icon-head" onclick="testAnalyze(event)" title="Download Project Source">
|
|
102
|
+
📥
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="form-group">
|
|
106
|
+
<input type="text" id="analyze-path" value="." placeholder="Project path (e.g. .)">
|
|
107
|
+
</div>
|
|
108
|
+
<div class="btn-group">
|
|
109
|
+
<button class="btn btn-copy" onclick="copyAnalyzeResult(event)">
|
|
110
|
+
<span id="analyze-copy-icon">📋</span>
|
|
111
|
+
<span id="analyze-copy-text">Copy Text</span>
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="response" id="analyze-response"></div>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<!-- Execute Bash -->
|
|
118
|
+
<div class="endpoint-card">
|
|
119
|
+
<div class="endpoint-header">
|
|
120
|
+
<div class="endpoint-title-group">
|
|
121
|
+
<span class="method post">POST</span>
|
|
122
|
+
<span class="endpoint-path">/execute</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="form-group">
|
|
126
|
+
<textarea id="execute-bash"
|
|
127
|
+
placeholder="mkdir -p src/test echo 'Hello' > src/test/hello.txt"></textarea>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="btn-group">
|
|
130
|
+
<button class="btn" onclick="testExecute(event)">
|
|
131
|
+
<span>▶️</span> Run
|
|
132
|
+
</button>
|
|
133
|
+
<button class="btn" onclick="executeFromClipboard(event)">
|
|
134
|
+
<span>📋</span> Paste & Run
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
<div class="response" id="execute-response"></div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<!-- Structure -->
|
|
141
|
+
<div class="endpoint-card">
|
|
142
|
+
<div class="endpoint-header">
|
|
143
|
+
<div class="endpoint-title-group">
|
|
144
|
+
<span class="method get" style="background: var(--ios-blue); color: white;">GET</span>
|
|
145
|
+
<span class="endpoint-path">/structure</span>
|
|
146
|
+
</div>
|
|
147
|
+
<button class="btn-icon-head" onclick="copySelectedStructure(event)" title="Copy Selected">
|
|
148
|
+
📋
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="form-group">
|
|
152
|
+
<input type="text" id="structure-path" value="." placeholder="Project path">
|
|
153
|
+
</div>
|
|
154
|
+
<div class="btn-group">
|
|
155
|
+
<button class="btn" onclick="testStructure(event)">
|
|
156
|
+
<span>🌳</span> View
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="tree-container" id="structure-tree" style="display: none;">
|
|
160
|
+
<div class="tree-header">
|
|
161
|
+
<span>Tree</span>
|
|
162
|
+
<span class="tree-total-tokens" id="total-tokens-badge">0 tokens</span>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="tree-content" id="tree-content"></div>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="response" id="structure-response" style="display: none;"></div>
|
|
167
|
+
</div>
|
|
108
168
|
</div>
|
|
109
|
-
<div
|
|
169
|
+
<div style="height: 50px;"></div> <!-- Spacer for scrolling -->
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- CỘT PHẢI: Iframe ChatGPT -->
|
|
174
|
+
<div class="right-panel">
|
|
175
|
+
<div class="iframe-placeholder">
|
|
176
|
+
<p>⚠️ Nếu trang trắng, hãy cài Extension <b>"Ignore X-Frame-Options"</b></p>
|
|
177
|
+
<a href="https://chatgpt.com" target="_blank" class="link-fallback">Mở ChatGPT tab mới ↗</a>
|
|
110
178
|
</div>
|
|
179
|
+
<iframe src="https://chatgpt.com" title="ChatGPT Integration"></iframe>
|
|
111
180
|
</div>
|
|
112
181
|
</div>
|
|
113
182
|
|
|
114
183
|
<div class="toast" id="toast"></div>
|
|
115
|
-
|
|
116
184
|
<script type="module" src="/js/main.js"></script>
|
|
117
185
|
</body>
|
|
118
186
|
|
|
@@ -4,13 +4,14 @@ import { API_BASE } from './config.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Analyze project and get source code
|
|
6
6
|
* @param {string} path - Project path to analyze
|
|
7
|
+
* @param {Array<string>} specificFiles - Optional list of relative paths to filter
|
|
7
8
|
* @returns {Promise<string>} - Project content as text
|
|
8
9
|
*/
|
|
9
|
-
export async function analyzeProject(path) {
|
|
10
|
+
export async function analyzeProject(path, specificFiles = null) {
|
|
10
11
|
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
11
12
|
method: 'POST',
|
|
12
13
|
headers: { 'Content-Type': 'application/json' },
|
|
13
|
-
body: JSON.stringify({ path })
|
|
14
|
+
body: JSON.stringify({ path, specificFiles })
|
|
14
15
|
});
|
|
15
16
|
|
|
16
17
|
if (!res.ok) {
|
|
@@ -55,6 +56,22 @@ export async function checkHealth() {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Get project structure with tokens
|
|
61
|
+
* @param {string} path - Project path
|
|
62
|
+
* @returns {Promise<Object>} - Structure data
|
|
63
|
+
*/
|
|
64
|
+
export async function getStructure(path) {
|
|
65
|
+
const res = await fetch(`${API_BASE}/api/structure?path=${encodeURIComponent(path)}`);
|
|
66
|
+
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const data = await res.json();
|
|
69
|
+
throw new Error(data.error || 'Failed to get structure');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return await res.json();
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
/**
|
|
59
76
|
* Copy content as file to clipboard
|
|
60
77
|
* @param {string} filename - Filename for the clipboard item
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { getStructure, analyzeProject, copyToClipboard } from '../api.js';
|
|
2
|
+
import { showToast, showLoading, resetButton, showResponse, formatNumber, showCopiedState } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
// Global variable to store current structure data
|
|
5
|
+
let currentStructureData = null;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Handle Structure button click logic
|
|
9
|
+
*/
|
|
10
|
+
export async function handleStructureView(event) {
|
|
11
|
+
const btn = event.target.closest('.btn');
|
|
12
|
+
const pathInput = document.getElementById('structure-path');
|
|
13
|
+
const treeContainer = document.getElementById('structure-tree');
|
|
14
|
+
const treeContent = document.getElementById('tree-content');
|
|
15
|
+
const errorContainer = document.getElementById('structure-response');
|
|
16
|
+
|
|
17
|
+
const path = pathInput.value;
|
|
18
|
+
|
|
19
|
+
showLoading(btn, btn.innerHTML);
|
|
20
|
+
treeContainer.style.display = 'none';
|
|
21
|
+
errorContainer.style.display = 'none';
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const data = await getStructure(path);
|
|
25
|
+
currentStructureData = data.structure;
|
|
26
|
+
|
|
27
|
+
// Render Tree HTML using recursive function
|
|
28
|
+
treeContent.innerHTML = generateTreeHtml(data.structure);
|
|
29
|
+
|
|
30
|
+
// Initial token update
|
|
31
|
+
updateTotalTokens();
|
|
32
|
+
|
|
33
|
+
treeContainer.style.display = 'block';
|
|
34
|
+
showToast('Tải cấu trúc thành công', 'success');
|
|
35
|
+
|
|
36
|
+
} catch (err) {
|
|
37
|
+
showResponse('structure-response', { error: err.message }, true);
|
|
38
|
+
showToast('Lỗi: ' + err.message, 'error');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
resetButton(btn);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Toggle folder collapse/expand
|
|
46
|
+
* Only triggers if clicked on row but NOT on checkbox
|
|
47
|
+
*/
|
|
48
|
+
export function handleToggleFolder(event) {
|
|
49
|
+
if (event.target.type === 'checkbox') return;
|
|
50
|
+
|
|
51
|
+
// Find closest parent LI
|
|
52
|
+
const li = event.currentTarget.closest('.tree-li');
|
|
53
|
+
if (li && li.classList.contains('has-children')) {
|
|
54
|
+
li.classList.toggle('collapsed');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Handle Checkbox Logic (Parent <-> Child sync) & Update Token Total
|
|
60
|
+
*/
|
|
61
|
+
export function handleCheckboxChange(event) {
|
|
62
|
+
event.stopPropagation();
|
|
63
|
+
const checkbox = event.target;
|
|
64
|
+
const isChecked = checkbox.checked;
|
|
65
|
+
|
|
66
|
+
// 1. Sync Children: If this is a folder, update all children checkboxes
|
|
67
|
+
const li = checkbox.closest('.tree-li');
|
|
68
|
+
if (li) {
|
|
69
|
+
const childrenCheckboxes = li.querySelectorAll('.tree-checkbox');
|
|
70
|
+
childrenCheckboxes.forEach(child => {
|
|
71
|
+
child.checked = isChecked;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 2. Recalculate Tokens
|
|
76
|
+
updateTotalTokens();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Calculate total tokens of CHECKED files only
|
|
81
|
+
*/
|
|
82
|
+
function updateTotalTokens() {
|
|
83
|
+
// Select all checked checkboxes that are FILES (have data-tokens)
|
|
84
|
+
const checkedFiles = document.querySelectorAll('.tree-checkbox[data-type="file"]:checked');
|
|
85
|
+
|
|
86
|
+
let total = 0;
|
|
87
|
+
checkedFiles.forEach(box => {
|
|
88
|
+
const tokens = parseInt(box.dataset.tokens || '0');
|
|
89
|
+
total += tokens;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Update Badge
|
|
93
|
+
const badge = document.getElementById('total-tokens-badge');
|
|
94
|
+
badge.textContent = `${formatNumber(total)} tokens`;
|
|
95
|
+
|
|
96
|
+
// Optional: Visual styling if 0
|
|
97
|
+
if (total === 0) {
|
|
98
|
+
badge.style.color = 'var(--ios-gray)';
|
|
99
|
+
} else {
|
|
100
|
+
badge.style.color = ''; // reset to default
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Copy Content of Selected Files (via API)
|
|
106
|
+
*/
|
|
107
|
+
export async function handleCopySelected(event) {
|
|
108
|
+
const btn = event.target.closest('.btn-copy') || event.target.closest('.btn-icon-head');
|
|
109
|
+
const icon = document.getElementById('copy-structure-icon') || btn;
|
|
110
|
+
const text = document.getElementById('copy-structure-text') || { textContent: '' };
|
|
111
|
+
|
|
112
|
+
// 1. Get all checked FILE paths
|
|
113
|
+
const checkedBoxes = document.querySelectorAll('.tree-checkbox[data-type="file"]:checked');
|
|
114
|
+
const checkedPaths = [];
|
|
115
|
+
|
|
116
|
+
checkedBoxes.forEach(box => {
|
|
117
|
+
if (box.dataset.path) {
|
|
118
|
+
checkedPaths.push(box.dataset.path);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (checkedPaths.length === 0) {
|
|
123
|
+
showToast('Chưa chọn file nào', 'error');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Save original button state
|
|
128
|
+
const originalText = btn.innerHTML;
|
|
129
|
+
if (btn.classList.contains('btn-copy')) {
|
|
130
|
+
showLoading(btn, btn.innerHTML);
|
|
131
|
+
} else {
|
|
132
|
+
// For header icon, just show visual feedback
|
|
133
|
+
btn.style.opacity = '0.5';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const path = document.getElementById('structure-path').value;
|
|
138
|
+
|
|
139
|
+
// 2. Call Analyze API with specific files
|
|
140
|
+
const content = await analyzeProject(path, checkedPaths);
|
|
141
|
+
|
|
142
|
+
// 3. Copy to clipboard
|
|
143
|
+
await copyToClipboard(content);
|
|
144
|
+
|
|
145
|
+
// UI Feedback
|
|
146
|
+
if (btn.classList.contains('btn-copy')) {
|
|
147
|
+
showCopiedState(btn, icon, text, '📋', 'Copy Selected');
|
|
148
|
+
resetButton(btn);
|
|
149
|
+
} else {
|
|
150
|
+
// Header icon feedback
|
|
151
|
+
btn.style.opacity = '1';
|
|
152
|
+
const originalIcon = btn.textContent;
|
|
153
|
+
btn.textContent = '✓';
|
|
154
|
+
setTimeout(() => btn.textContent = originalIcon, 2000);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
showToast(`Đã copy nội dung ${checkedPaths.length} file!`, 'success');
|
|
158
|
+
|
|
159
|
+
} catch (err) {
|
|
160
|
+
resetButton(btn);
|
|
161
|
+
if (!btn.classList.contains('btn-copy')) btn.style.opacity = '1';
|
|
162
|
+
showToast('Lỗi copy: ' + err.message, 'error');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Recursive function to generate Tree HTML with Checkboxes
|
|
168
|
+
*/
|
|
169
|
+
function generateTreeHtml(node) {
|
|
170
|
+
if (!node) return '';
|
|
171
|
+
|
|
172
|
+
const isDir = node.type === 'directory';
|
|
173
|
+
const hasChildren = isDir && node.children && node.children.length > 0;
|
|
174
|
+
|
|
175
|
+
// Determine Token Color
|
|
176
|
+
const tokens = node.tokens || 0;
|
|
177
|
+
let tokenClass = 'token-low';
|
|
178
|
+
if (tokens > 5000) tokenClass = 'token-high';
|
|
179
|
+
else if (tokens > 2000) tokenClass = 'token-med';
|
|
180
|
+
|
|
181
|
+
// Icon
|
|
182
|
+
const icon = isDir ? (hasChildren ? '📁' : '📂') : '📄';
|
|
183
|
+
const arrow = hasChildren ? '▼' : '';
|
|
184
|
+
const liClass = `tree-li ${hasChildren ? 'has-children' : ''}`;
|
|
185
|
+
|
|
186
|
+
// Build HTML
|
|
187
|
+
let html = `<li class="${liClass}">`;
|
|
188
|
+
|
|
189
|
+
const clickAttr = hasChildren ? 'onclick="toggleFolder(event)"' : '';
|
|
190
|
+
|
|
191
|
+
// Add data-tokens and data-type for client-side calculation
|
|
192
|
+
|
|
193
|
+
html += `
|
|
194
|
+
<div class="tree-item-row" ${clickAttr}>
|
|
195
|
+
<span class="arrow">${arrow}</span>
|
|
196
|
+
<input type="checkbox" class="tree-checkbox"
|
|
197
|
+
data-path="${node.relativePath || node.path}"
|
|
198
|
+
data-tokens="${tokens}"
|
|
199
|
+
data-type="${node.type}"
|
|
200
|
+
checked
|
|
201
|
+
onclick="handleCheckboxChange(event)">
|
|
202
|
+
<span class="tree-icon">${icon}</span>
|
|
203
|
+
<span class="tree-name">${node.name}</span>
|
|
204
|
+
<span class="token-badge ${tokenClass}">${formatNumber(tokens)}</span>
|
|
205
|
+
</div>
|
|
206
|
+
`;
|
|
207
|
+
|
|
208
|
+
// Children recursion
|
|
209
|
+
if (hasChildren) {
|
|
210
|
+
html += '<ul class="tree-ul">';
|
|
211
|
+
// Sort: Folders first, then files
|
|
212
|
+
node.children.forEach(child => {
|
|
213
|
+
html += generateTreeHtml(child);
|
|
214
|
+
});
|
|
215
|
+
html += '</ul>';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
html += '</li>';
|
|
219
|
+
|
|
220
|
+
return isDir ? `<ul class="tree-ul">${html}</ul>` : html;
|
|
221
|
+
}
|