vg-coder-cli 2.0.10 → 2.0.12
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/DEVELOPMENT.md +95 -0
- package/package.json +1 -1
- package/src/server/api-server.js +60 -298
- package/src/server/views/css/git-view.css +155 -0
- package/src/server/views/css/iframe.css +84 -0
- package/src/server/views/dashboard.css +6 -53
- package/src/server/views/dashboard.html +66 -32
- package/src/server/views/js/api.js +15 -0
- package/src/server/views/js/features/git-view.js +117 -0
- package/src/server/views/js/features/iframe-manager.js +56 -0
- package/src/server/views/js/main.js +8 -0
- package/src/server/views/vg-coder/background.js +1 -1
- package/src/server/views/vg-coder/options.js +1 -1
- package/vg-coder-cli-2.0.12.tgz +0 -0
- package/vg-coder.zip +0 -0
- package/vg-coder-cli-2.0.10.tgz +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* Right Panel Layout */
|
|
2
|
+
.right-panel {
|
|
3
|
+
flex: 1;
|
|
4
|
+
height: 100%;
|
|
5
|
+
background: #fff;
|
|
6
|
+
position: relative;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* AI Header Styles */
|
|
12
|
+
.ai-header {
|
|
13
|
+
height: 50px;
|
|
14
|
+
background: var(--ios-bg);
|
|
15
|
+
border-bottom: 1px solid var(--ios-separator);
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
padding: 0 15px;
|
|
19
|
+
gap: 10px;
|
|
20
|
+
justify-content: space-between;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.ai-select-group {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 10px;
|
|
27
|
+
flex: 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.ai-select {
|
|
31
|
+
padding: 6px 12px;
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
border: 1px solid var(--ios-separator);
|
|
34
|
+
background: var(--ios-card);
|
|
35
|
+
color: var(--text-primary);
|
|
36
|
+
font-size: 13px;
|
|
37
|
+
font-weight: 500;
|
|
38
|
+
min-width: 200px;
|
|
39
|
+
outline: none;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Iframe Container */
|
|
44
|
+
.ai-iframe-container {
|
|
45
|
+
flex: 1;
|
|
46
|
+
position: relative;
|
|
47
|
+
width: 100%;
|
|
48
|
+
background: #fff;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.right-panel iframe {
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 100%;
|
|
54
|
+
border: none;
|
|
55
|
+
position: relative;
|
|
56
|
+
z-index: 2;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Placeholder underneath iframe */
|
|
60
|
+
.iframe-placeholder {
|
|
61
|
+
position: absolute;
|
|
62
|
+
top: 50%;
|
|
63
|
+
left: 50%;
|
|
64
|
+
transform: translate(-50%, -50%);
|
|
65
|
+
text-align: center;
|
|
66
|
+
color: var(--text-secondary);
|
|
67
|
+
z-index: 1;
|
|
68
|
+
width: 100%;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.link-fallback {
|
|
72
|
+
color: var(--ios-blue);
|
|
73
|
+
text-decoration: none;
|
|
74
|
+
margin-top: 10px;
|
|
75
|
+
display: inline-block;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Mobile Responsive adjustment for right panel */
|
|
79
|
+
@media (max-width: 768px) {
|
|
80
|
+
.right-panel {
|
|
81
|
+
flex: 1;
|
|
82
|
+
height: 50vh;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -43,9 +43,7 @@ body {
|
|
|
43
43
|
background-color: var(--ios-bg);
|
|
44
44
|
color: var(--text-primary);
|
|
45
45
|
height: 100vh;
|
|
46
|
-
/* Full viewport height */
|
|
47
46
|
overflow: hidden;
|
|
48
|
-
/* Disable body scroll */
|
|
49
47
|
font-size: 13px;
|
|
50
48
|
}
|
|
51
49
|
|
|
@@ -59,12 +57,9 @@ body {
|
|
|
59
57
|
/* Left Panel: VG Coder Tool */
|
|
60
58
|
.left-panel {
|
|
61
59
|
flex: 0 0 450px;
|
|
62
|
-
/* Fixed width: 450px */
|
|
63
60
|
max-width: 50vw;
|
|
64
|
-
/* Max 50% on small screens */
|
|
65
61
|
height: 100%;
|
|
66
62
|
overflow-y: auto;
|
|
67
|
-
/* Scrollable independently */
|
|
68
63
|
background: var(--ios-bg);
|
|
69
64
|
border-right: 1px solid var(--ios-separator);
|
|
70
65
|
position: relative;
|
|
@@ -74,48 +69,9 @@ body {
|
|
|
74
69
|
.container {
|
|
75
70
|
padding: 15px;
|
|
76
71
|
padding-top: 15px;
|
|
77
|
-
/* Removed max-width/margin centering since it's inside panel */
|
|
78
72
|
}
|
|
79
73
|
|
|
80
|
-
/*
|
|
81
|
-
.right-panel {
|
|
82
|
-
flex: 1;
|
|
83
|
-
/* Take remaining space */
|
|
84
|
-
height: 100%;
|
|
85
|
-
background: #fff;
|
|
86
|
-
position: relative;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.right-panel iframe {
|
|
90
|
-
width: 100%;
|
|
91
|
-
height: 100%;
|
|
92
|
-
border: none;
|
|
93
|
-
position: relative;
|
|
94
|
-
z-index: 2;
|
|
95
|
-
/* Sit above placeholder */
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/* Placeholder underneath iframe (visible if iframe fails to load) */
|
|
99
|
-
.iframe-placeholder {
|
|
100
|
-
position: absolute;
|
|
101
|
-
top: 50%;
|
|
102
|
-
left: 50%;
|
|
103
|
-
transform: translate(-50%, -50%);
|
|
104
|
-
text-align: center;
|
|
105
|
-
color: var(--text-secondary);
|
|
106
|
-
z-index: 1;
|
|
107
|
-
width: 100%;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.link-fallback {
|
|
111
|
-
color: var(--ios-blue);
|
|
112
|
-
text-decoration: none;
|
|
113
|
-
margin-top: 10px;
|
|
114
|
-
display: inline-block;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/* --- EXISTING COMPACT STYLES (Preserved & Adjusted) --- */
|
|
118
|
-
|
|
74
|
+
/* --- COMPONENT STYLES --- */
|
|
119
75
|
.header {
|
|
120
76
|
display: flex;
|
|
121
77
|
justify-content: space-between;
|
|
@@ -176,6 +132,11 @@ body {
|
|
|
176
132
|
border: none;
|
|
177
133
|
border-radius: 6px;
|
|
178
134
|
cursor: pointer;
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: center;
|
|
138
|
+
text-decoration: none;
|
|
139
|
+
color: var(--text-primary);
|
|
179
140
|
}
|
|
180
141
|
|
|
181
142
|
.system-prompt-content {
|
|
@@ -321,16 +282,13 @@ body {
|
|
|
321
282
|
position: fixed;
|
|
322
283
|
bottom: 20px;
|
|
323
284
|
left: 20px;
|
|
324
|
-
/* Move toast to bottom left */
|
|
325
285
|
transform: translateY(100px);
|
|
326
|
-
/* Adjust animation */
|
|
327
286
|
}
|
|
328
287
|
|
|
329
288
|
.toast.show {
|
|
330
289
|
transform: translateY(0);
|
|
331
290
|
}
|
|
332
291
|
|
|
333
|
-
/* Mobile Responsiveness */
|
|
334
292
|
@media (max-width: 768px) {
|
|
335
293
|
.split-layout {
|
|
336
294
|
flex-direction: column;
|
|
@@ -344,9 +302,4 @@ body {
|
|
|
344
302
|
border-right: none;
|
|
345
303
|
border-bottom: 1px solid var(--ios-separator);
|
|
346
304
|
}
|
|
347
|
-
|
|
348
|
-
.right-panel {
|
|
349
|
-
flex: 1;
|
|
350
|
-
height: 50vh;
|
|
351
|
-
}
|
|
352
305
|
}
|
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
<title>VG Coder - Split View</title>
|
|
9
9
|
<link rel="stylesheet" href="/dashboard.css">
|
|
10
10
|
<link rel="stylesheet" href="/css/structure.css">
|
|
11
|
+
<link rel="stylesheet" href="/css/iframe.css">
|
|
12
|
+
<link rel="stylesheet" href="/css/git-view.css">
|
|
13
|
+
|
|
14
|
+
<!-- QUAN TRỌNG: Đã đổi sang github-dark.min.css để chữ có màu sáng -->
|
|
15
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" />
|
|
16
|
+
|
|
17
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
|
|
18
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
|
|
19
|
+
|
|
11
20
|
<script>
|
|
12
21
|
(function () {
|
|
13
22
|
const savedTheme = localStorage.getItem('theme') || 'light';
|
|
@@ -18,7 +27,7 @@
|
|
|
18
27
|
|
|
19
28
|
<body>
|
|
20
29
|
<div class="split-layout">
|
|
21
|
-
<!-- CỘT TRÁI: Giao diện VG Coder
|
|
30
|
+
<!-- CỘT TRÁI: Giao diện VG Coder -->
|
|
22
31
|
<div class="left-panel">
|
|
23
32
|
<div class="container">
|
|
24
33
|
<div class="header">
|
|
@@ -68,8 +77,11 @@
|
|
|
68
77
|
Copy link này và dán vào tab mới:
|
|
69
78
|
<div class="form-group" style="margin-top: 5px; margin-bottom: 0;">
|
|
70
79
|
<div style="display: flex; gap: 5px;">
|
|
71
|
-
<input type="text" id="chrome-url-input" readonly
|
|
72
|
-
|
|
80
|
+
<input type="text" id="chrome-url-input" readonly
|
|
81
|
+
value="chrome://extensions" onclick="this.select()"
|
|
82
|
+
style="font-family: monospace;">
|
|
83
|
+
<button class="btn btn-copy" style="flex: 0 0 40px; padding: 0;"
|
|
84
|
+
onclick="copyChromeUrl(event)" title="Copy URL">
|
|
73
85
|
<span>📋</span>
|
|
74
86
|
</button>
|
|
75
87
|
</div>
|
|
@@ -81,7 +93,8 @@
|
|
|
81
93
|
</ol>
|
|
82
94
|
</div>
|
|
83
95
|
<div class="form-group">
|
|
84
|
-
<input type="text" id="extension-path-input" readonly value="Loading path..."
|
|
96
|
+
<input type="text" id="extension-path-input" readonly value="Loading path..."
|
|
97
|
+
onclick="this.select()">
|
|
85
98
|
</div>
|
|
86
99
|
<button class="btn btn-copy" onclick="copyExtensionPath(event)">
|
|
87
100
|
<span id="ext-copy-icon">📋</span>
|
|
@@ -91,29 +104,6 @@
|
|
|
91
104
|
</div>
|
|
92
105
|
|
|
93
106
|
<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
107
|
<!-- Execute Bash -->
|
|
118
108
|
<div class="endpoint-card">
|
|
119
109
|
<div class="endpoint-header">
|
|
@@ -165,18 +155,62 @@
|
|
|
165
155
|
</div>
|
|
166
156
|
<div class="response" id="structure-response" style="display: none;"></div>
|
|
167
157
|
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Analyze -->
|
|
160
|
+
<div class="endpoint-card">
|
|
161
|
+
<div class="endpoint-header">
|
|
162
|
+
<div class="endpoint-title-group">
|
|
163
|
+
<span class="method post">POST</span>
|
|
164
|
+
<span class="endpoint-path">/analyze</span>
|
|
165
|
+
</div>
|
|
166
|
+
<button class="btn-icon-head" onclick="testAnalyze(event)" title="Download Project Source">
|
|
167
|
+
📥
|
|
168
|
+
</button>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="form-group">
|
|
171
|
+
<input type="text" id="analyze-path" value="." placeholder="Project path (e.g. .)">
|
|
172
|
+
</div>
|
|
173
|
+
<div class="btn-group">
|
|
174
|
+
<button class="btn btn-copy" onclick="copyAnalyzeResult(event)">
|
|
175
|
+
<span id="analyze-copy-icon">📋</span>
|
|
176
|
+
<span id="analyze-copy-text">Copy Text</span>
|
|
177
|
+
</button>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="response" id="analyze-response"></div>
|
|
180
|
+
</div>
|
|
168
181
|
</div>
|
|
169
182
|
<div style="height: 50px;"></div> <!-- Spacer for scrolling -->
|
|
170
183
|
</div>
|
|
171
184
|
</div>
|
|
172
185
|
|
|
173
|
-
<!-- CỘT PHẢI: Iframe
|
|
186
|
+
<!-- CỘT PHẢI: AI Iframe with Selector & Git View -->
|
|
174
187
|
<div class="right-panel">
|
|
175
|
-
<div class="
|
|
176
|
-
<
|
|
177
|
-
|
|
188
|
+
<div class="ai-header">
|
|
189
|
+
<div class="ai-select-group">
|
|
190
|
+
<span style="font-size: 16px;">🤖</span>
|
|
191
|
+
<select id="ai-provider-select" class="ai-select">
|
|
192
|
+
<!-- Options filled by JS -->
|
|
193
|
+
</select>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<!-- NEW: Git Toggle Button -->
|
|
197
|
+
<button id="git-view-toggle" class="git-toggle-btn">View Changes</button>
|
|
198
|
+
<button id="git-refresh-btn" class="btn-icon-head" style="display:none; margin-left:5px;" title="Refresh">↻</button>
|
|
199
|
+
|
|
200
|
+
<a id="ai-external-link" href="#" target="_blank" class="btn-icon-head" title="Open in new tab">↗</a>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<!-- Git View Container (Hidden by default) -->
|
|
204
|
+
<div id="git-view-container" class="git-view-container"></div>
|
|
205
|
+
|
|
206
|
+
<!-- AI Iframe Container -->
|
|
207
|
+
<div class="ai-iframe-container">
|
|
208
|
+
<div class="iframe-placeholder">
|
|
209
|
+
<p>⚠️ Nếu trang trắng, hãy cài Extension <b>"Ignore X-Frame-Options"</b></p>
|
|
210
|
+
<a id="ai-placeholder-link" href="#" target="_blank" class="link-fallback">Mở tab mới ↗</a>
|
|
211
|
+
</div>
|
|
212
|
+
<iframe id="ai-iframe" src="" title="AI Integration"></iframe>
|
|
178
213
|
</div>
|
|
179
|
-
<iframe src="https://chatgpt.com" title="ChatGPT Integration"></iframe>
|
|
180
214
|
</div>
|
|
181
215
|
</div>
|
|
182
216
|
|
|
@@ -72,6 +72,21 @@ export async function getStructure(path) {
|
|
|
72
72
|
return await res.json();
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Get Git Diff
|
|
77
|
+
* @returns {Promise<string>} - Unified diff string
|
|
78
|
+
*/
|
|
79
|
+
export async function getGitDiff() {
|
|
80
|
+
const res = await fetch(`${API_BASE}/api/git/diff`);
|
|
81
|
+
const data = await res.json();
|
|
82
|
+
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
throw new Error(data.error || 'Failed to get git diff');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return data.diff;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
/**
|
|
76
91
|
* Copy content as file to clipboard
|
|
77
92
|
* @param {string} filename - Filename for the clipboard item
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { getGitDiff } from '../api.js';
|
|
2
|
+
import { showToast, showLoading } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
export function initGitView() {
|
|
5
|
+
console.log('[GitView] Initializing...');
|
|
6
|
+
const toggleBtn = document.getElementById('git-view-toggle');
|
|
7
|
+
const refreshBtn = document.getElementById('git-refresh-btn');
|
|
8
|
+
|
|
9
|
+
if (toggleBtn) toggleBtn.addEventListener('click', toggleGitMode);
|
|
10
|
+
if (refreshBtn) refreshBtn.addEventListener('click', loadGitChanges);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let isGitMode = false;
|
|
14
|
+
|
|
15
|
+
async function toggleGitMode() {
|
|
16
|
+
const gitContainer = document.getElementById('git-view-container');
|
|
17
|
+
const toggleBtn = document.getElementById('git-view-toggle');
|
|
18
|
+
const refreshBtn = document.getElementById('git-refresh-btn');
|
|
19
|
+
const toggleText = document.getElementById('git-toggle-text');
|
|
20
|
+
|
|
21
|
+
isGitMode = !isGitMode;
|
|
22
|
+
console.log('[GitView] Toggle Mode:', isGitMode);
|
|
23
|
+
|
|
24
|
+
if (isGitMode) {
|
|
25
|
+
gitContainer.classList.add('active');
|
|
26
|
+
toggleBtn.classList.add('active');
|
|
27
|
+
if (toggleText) toggleText.textContent = 'Close Changes';
|
|
28
|
+
if (refreshBtn) refreshBtn.style.display = 'flex';
|
|
29
|
+
|
|
30
|
+
// Debug kích thước container
|
|
31
|
+
const rect = gitContainer.getBoundingClientRect();
|
|
32
|
+
console.log('[GitView] Container Size:', rect.width, 'x', rect.height);
|
|
33
|
+
|
|
34
|
+
if (!gitContainer.innerHTML.trim()) {
|
|
35
|
+
await loadGitChanges();
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
gitContainer.classList.remove('active');
|
|
39
|
+
toggleBtn.classList.remove('active');
|
|
40
|
+
if (toggleText) toggleText.textContent = 'View Changes';
|
|
41
|
+
if (refreshBtn) refreshBtn.style.display = 'none';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function loadGitChanges() {
|
|
46
|
+
const gitContainer = document.getElementById('git-view-container');
|
|
47
|
+
const refreshBtn = document.getElementById('git-refresh-btn');
|
|
48
|
+
|
|
49
|
+
console.log('[GitView] Loading changes...');
|
|
50
|
+
|
|
51
|
+
if (refreshBtn) {
|
|
52
|
+
refreshBtn.style.opacity = '0.5';
|
|
53
|
+
refreshBtn.disabled = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
gitContainer.innerHTML = '<div class="git-loading-msg">Loading git changes... (Check Console if stuck)</div>';
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Kiểm tra thư viện
|
|
60
|
+
if (typeof Diff2HtmlUI === 'undefined') {
|
|
61
|
+
throw new Error('Thư viện Diff2HtmlUI chưa được load!');
|
|
62
|
+
}
|
|
63
|
+
if (typeof hljs === 'undefined') {
|
|
64
|
+
console.warn('⚠️ Highlight.js chưa được load!');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const diffString = await getGitDiff();
|
|
68
|
+
console.log('[GitView] Diff received, length:', diffString?.length);
|
|
69
|
+
|
|
70
|
+
if (!diffString || !diffString.trim()) {
|
|
71
|
+
gitContainer.innerHTML = `
|
|
72
|
+
<div class="git-loading-msg">
|
|
73
|
+
<div style="font-size:30px;">✨</div>
|
|
74
|
+
<p>No changes detected</p>
|
|
75
|
+
</div>`;
|
|
76
|
+
} else {
|
|
77
|
+
console.log('[GitView] Rendering diff...');
|
|
78
|
+
|
|
79
|
+
// Cấu hình Diff2Html
|
|
80
|
+
const configuration = {
|
|
81
|
+
drawFileList: true,
|
|
82
|
+
fileListToggle: false,
|
|
83
|
+
fileListStartVisible: true,
|
|
84
|
+
fileContentToggle: false,
|
|
85
|
+
matching: 'lines',
|
|
86
|
+
outputFormat: 'side-by-side',
|
|
87
|
+
synchronisedScroll: true,
|
|
88
|
+
highlight: true,
|
|
89
|
+
renderNothingWhenEmpty: true,
|
|
90
|
+
colorScheme: 'dark' // Ép buộc Dark Mode từ thư viện
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
gitContainer.innerHTML = ''; // Clear loading
|
|
94
|
+
|
|
95
|
+
const diff2htmlUi = new Diff2HtmlUI(gitContainer, diffString, configuration);
|
|
96
|
+
diff2htmlUi.draw();
|
|
97
|
+
diff2htmlUi.highlightCode();
|
|
98
|
+
|
|
99
|
+
console.log('[GitView] Render complete.');
|
|
100
|
+
}
|
|
101
|
+
showToast('Git changes loaded', 'success');
|
|
102
|
+
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error('[GitView] Error:', err);
|
|
105
|
+
gitContainer.innerHTML = `
|
|
106
|
+
<div class="git-loading-msg" style="color:var(--ios-red);">
|
|
107
|
+
<div>⚠️ Error</div>
|
|
108
|
+
<div style="font-size:12px;">${err.message}</div>
|
|
109
|
+
<div style="font-size:10px; margin-top:5px;">Check F12 Console for details</div>
|
|
110
|
+
</div>`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (refreshBtn) {
|
|
114
|
+
refreshBtn.style.opacity = '1';
|
|
115
|
+
refreshBtn.disabled = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quản lý tính năng Iframe AI Provider
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Danh sách các AI Providers
|
|
6
|
+
const AI_PROVIDERS = [
|
|
7
|
+
{ id: 'chatgpt', name: 'ChatGPT', url: 'https://chat.openai.com' },
|
|
8
|
+
{ id: 'kimi', name: 'Kimi AI', url: 'https://www.kimi.com' },
|
|
9
|
+
{ id: 'deepseek', name: 'DeepSeek', url: 'https://chat.deepseek.com' },
|
|
10
|
+
{ id: 'gemini', name: 'Google Gemini', url: 'https://gemini.google.com/app' },
|
|
11
|
+
{ id: 'aistudio', name: 'Google AI Studio', url: 'https://aistudio.google.com/prompts/new_chat' }
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export function initIframeManager() {
|
|
15
|
+
const select = document.getElementById('ai-provider-select');
|
|
16
|
+
const iframe = document.getElementById('ai-iframe');
|
|
17
|
+
const externalLink = document.getElementById('ai-external-link');
|
|
18
|
+
const placeholderLink = document.getElementById('ai-placeholder-link');
|
|
19
|
+
|
|
20
|
+
if (!select || !iframe) return;
|
|
21
|
+
|
|
22
|
+
// 1. Populate options
|
|
23
|
+
AI_PROVIDERS.forEach(provider => {
|
|
24
|
+
const option = document.createElement('option');
|
|
25
|
+
option.value = provider.id;
|
|
26
|
+
option.textContent = provider.name;
|
|
27
|
+
select.appendChild(option);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 2. Load saved selection or default
|
|
31
|
+
const savedProviderId = localStorage.getItem('ai_provider') || 'chatgpt';
|
|
32
|
+
|
|
33
|
+
// Validate if saved provider still exists
|
|
34
|
+
const isValid = AI_PROVIDERS.some(p => p.id === savedProviderId);
|
|
35
|
+
select.value = isValid ? savedProviderId : AI_PROVIDERS[0].id;
|
|
36
|
+
|
|
37
|
+
// 3. Function to update UI
|
|
38
|
+
const updateProvider = (providerId) => {
|
|
39
|
+
const provider = AI_PROVIDERS.find(p => p.id === providerId) || AI_PROVIDERS[0];
|
|
40
|
+
|
|
41
|
+
iframe.src = provider.url;
|
|
42
|
+
externalLink.href = provider.url;
|
|
43
|
+
placeholderLink.href = provider.url;
|
|
44
|
+
placeholderLink.textContent = `Mở ${provider.name} tab mới ↗`;
|
|
45
|
+
|
|
46
|
+
localStorage.setItem('ai_provider', providerId);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// 4. Initial update
|
|
50
|
+
updateProvider(select.value);
|
|
51
|
+
|
|
52
|
+
// 5. Event listener
|
|
53
|
+
select.addEventListener('change', (e) => {
|
|
54
|
+
updateProvider(e.target.value);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -3,6 +3,8 @@ import { SYSTEM_PROMPT } from './config.js';
|
|
|
3
3
|
import { checkHealth } from './api.js';
|
|
4
4
|
import './handlers.js'; // Import to register global functions
|
|
5
5
|
import { showToast, showCopiedState } from './utils.js';
|
|
6
|
+
import { initIframeManager } from './features/iframe-manager.js';
|
|
7
|
+
import { initGitView } from './features/git-view.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Initialize application on DOM ready
|
|
@@ -19,6 +21,12 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
19
21
|
|
|
20
22
|
// Load Extension Path
|
|
21
23
|
loadExtensionPath();
|
|
24
|
+
|
|
25
|
+
// Initialize Iframe Manager
|
|
26
|
+
initIframeManager();
|
|
27
|
+
|
|
28
|
+
// Initialize Git View
|
|
29
|
+
initGitView();
|
|
22
30
|
});
|
|
23
31
|
|
|
24
32
|
/**
|