vg-coder-cli 2.0.37 â 2.0.38
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 +58 -3
- package/package.json +4 -2
- package/src/server/views/css/code-viewer.css +215 -0
- package/src/server/views/js/features/code-viewer.js +110 -14
- package/src/server/views/js/features/keyboard-shortcuts.js +1 -9
- package/src/server/views/js/features/project-panel.js +2 -10
- package/src/server/views/vg-coder/background.js +11 -4
- package/src/server/views/vg-coder/controller.js +3 -57
- package/src/server/views/vg-coder/_metadata/generated_indexed_rulesets/_ruleset1 +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vg-coder-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.38",
|
|
4
4
|
"description": "đ CLI tool to analyze projects, concatenate source files, count tokens, and export HTML with syntax highlighting and copy functionality",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dev": "nodemon src/index.js",
|
|
17
17
|
"build:extension": "cd vetgo-auto && npm run build",
|
|
18
18
|
"build:copy": "node scripts/build.js",
|
|
19
|
-
"build": "npm run build:inject && npm run install:local",
|
|
19
|
+
"build": "npm run build:extension && npm run build:copy && npm run build:inject && npm run install:local",
|
|
20
20
|
"push": "npm run build && npm pack && npm publish",
|
|
21
21
|
"build:inject": "gulp"
|
|
22
22
|
},
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"fs-extra": "^11.3.3",
|
|
42
42
|
"highlight.js": "^11.9.0",
|
|
43
43
|
"ignore": "^5.3.0",
|
|
44
|
+
"markdown-it": "^14.0.0",
|
|
45
|
+
"mermaid": "^11.4.1",
|
|
44
46
|
"node-pty": "^1.1.0",
|
|
45
47
|
"ora": "^5.4.1",
|
|
46
48
|
"path": "^0.12.7",
|
|
@@ -56,3 +56,218 @@
|
|
|
56
56
|
color: var(--ios-red, #ff3b30);
|
|
57
57
|
font-family: monospace;
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
/* Markdown Styles */
|
|
61
|
+
.cv-markdown {
|
|
62
|
+
background: var(--ios-bg, #0d1117);
|
|
63
|
+
padding: 20px;
|
|
64
|
+
min-height: 100%;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.markdown-body {
|
|
68
|
+
color: var(--text-primary, #c9d1d9);
|
|
69
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
|
|
70
|
+
font-size: 14px;
|
|
71
|
+
line-height: 1.6;
|
|
72
|
+
max-width: 980px;
|
|
73
|
+
word-wrap: break-word;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Headers */
|
|
77
|
+
.markdown-body h1,
|
|
78
|
+
.markdown-body h2,
|
|
79
|
+
.markdown-body h3,
|
|
80
|
+
.markdown-body h4,
|
|
81
|
+
.markdown-body h5,
|
|
82
|
+
.markdown-body h6 {
|
|
83
|
+
margin-top: 24px;
|
|
84
|
+
margin-bottom: 16px;
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
line-height: 1.25;
|
|
87
|
+
color: var(--text-primary, #c9d1d9);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.markdown-body h1 {
|
|
91
|
+
font-size: 2em;
|
|
92
|
+
border-bottom: 1px solid var(--border-color, #30363d);
|
|
93
|
+
padding-bottom: 0.3em;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.markdown-body h2 {
|
|
97
|
+
font-size: 1.5em;
|
|
98
|
+
border-bottom: 1px solid var(--border-color, #30363d);
|
|
99
|
+
padding-bottom: 0.3em;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.markdown-body h3 { font-size: 1.25em; }
|
|
103
|
+
.markdown-body h4 { font-size: 1em; }
|
|
104
|
+
.markdown-body h5 { font-size: 0.875em; }
|
|
105
|
+
.markdown-body h6 { font-size: 0.85em; color: var(--text-secondary, #8b949e); }
|
|
106
|
+
|
|
107
|
+
/* Paragraphs */
|
|
108
|
+
.markdown-body p {
|
|
109
|
+
margin-top: 0;
|
|
110
|
+
margin-bottom: 16px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Lists */
|
|
114
|
+
.markdown-body ul,
|
|
115
|
+
.markdown-body ol {
|
|
116
|
+
margin-top: 0;
|
|
117
|
+
margin-bottom: 16px;
|
|
118
|
+
padding-left: 2em;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.markdown-body li {
|
|
122
|
+
margin-top: 0.25em;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.markdown-body li > p {
|
|
126
|
+
margin-top: 16px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.markdown-body li + li {
|
|
130
|
+
margin-top: 0.25em;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Code */
|
|
134
|
+
.markdown-body code {
|
|
135
|
+
padding: 0.2em 0.4em;
|
|
136
|
+
margin: 0;
|
|
137
|
+
font-size: 85%;
|
|
138
|
+
background-color: rgba(110, 118, 129, 0.4);
|
|
139
|
+
border-radius: 6px;
|
|
140
|
+
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.markdown-body pre {
|
|
144
|
+
margin-top: 0;
|
|
145
|
+
margin-bottom: 16px;
|
|
146
|
+
padding: 16px;
|
|
147
|
+
overflow: auto;
|
|
148
|
+
font-size: 85%;
|
|
149
|
+
line-height: 1.45;
|
|
150
|
+
background-color: var(--code-block-bg, #161b22);
|
|
151
|
+
border-radius: 6px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.markdown-body pre code {
|
|
155
|
+
display: block;
|
|
156
|
+
padding: 0;
|
|
157
|
+
margin: 0;
|
|
158
|
+
font-size: 100%;
|
|
159
|
+
word-break: normal;
|
|
160
|
+
white-space: pre;
|
|
161
|
+
background: transparent;
|
|
162
|
+
border: 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Links */
|
|
166
|
+
.markdown-body a {
|
|
167
|
+
color: var(--link-color, #58a6ff);
|
|
168
|
+
text-decoration: none;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.markdown-body a:hover {
|
|
172
|
+
text-decoration: underline;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Blockquotes */
|
|
176
|
+
.markdown-body blockquote {
|
|
177
|
+
margin: 0 0 16px 0;
|
|
178
|
+
padding: 0 1em;
|
|
179
|
+
color: var(--text-secondary, #8b949e);
|
|
180
|
+
border-left: 0.25em solid var(--border-color, #30363d);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.markdown-body blockquote > :first-child {
|
|
184
|
+
margin-top: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.markdown-body blockquote > :last-child {
|
|
188
|
+
margin-bottom: 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Tables */
|
|
192
|
+
.markdown-body table {
|
|
193
|
+
border-spacing: 0;
|
|
194
|
+
border-collapse: collapse;
|
|
195
|
+
margin-top: 0;
|
|
196
|
+
margin-bottom: 16px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.markdown-body table th,
|
|
200
|
+
.markdown-body table td {
|
|
201
|
+
padding: 6px 13px;
|
|
202
|
+
border: 1px solid var(--border-color, #30363d);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.markdown-body table th {
|
|
206
|
+
font-weight: 600;
|
|
207
|
+
background-color: var(--table-header-bg, #161b22);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.markdown-body table tr {
|
|
211
|
+
background-color: var(--table-row-bg, #0d1117);
|
|
212
|
+
border-top: 1px solid var(--border-color, #30363d);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.markdown-body table tr:nth-child(2n) {
|
|
216
|
+
background-color: var(--table-row-alt-bg, #161b22);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Horizontal Rule */
|
|
220
|
+
.markdown-body hr {
|
|
221
|
+
height: 0.25em;
|
|
222
|
+
padding: 0;
|
|
223
|
+
margin: 24px 0;
|
|
224
|
+
background-color: var(--border-color, #30363d);
|
|
225
|
+
border: 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Images */
|
|
229
|
+
.markdown-body img {
|
|
230
|
+
max-width: 100%;
|
|
231
|
+
box-sizing: content-box;
|
|
232
|
+
background-color: var(--ios-bg, #0d1117);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Strong/Bold */
|
|
236
|
+
.markdown-body strong {
|
|
237
|
+
font-weight: 600;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Emphasis/Italic */
|
|
241
|
+
.markdown-body em {
|
|
242
|
+
font-style: italic;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* Mermaid Diagrams */
|
|
246
|
+
.markdown-body .mermaid-diagram {
|
|
247
|
+
margin: 16px 0;
|
|
248
|
+
padding: 20px;
|
|
249
|
+
background-color: var(--code-block-bg, #161b22);
|
|
250
|
+
border-radius: 6px;
|
|
251
|
+
display: flex;
|
|
252
|
+
justify-content: center;
|
|
253
|
+
align-items: center;
|
|
254
|
+
overflow-x: auto;
|
|
255
|
+
min-height: 100px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.markdown-body .mermaid-diagram svg {
|
|
259
|
+
max-width: 100%;
|
|
260
|
+
height: auto;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Mermaid error state */
|
|
264
|
+
.markdown-body .mermaid-error {
|
|
265
|
+
margin: 16px 0;
|
|
266
|
+
padding: 16px;
|
|
267
|
+
background-color: rgba(255, 59, 48, 0.1);
|
|
268
|
+
border: 1px solid var(--ios-red, #ff3b30);
|
|
269
|
+
border-radius: 6px;
|
|
270
|
+
color: var(--ios-red, #ff3b30);
|
|
271
|
+
font-family: monospace;
|
|
272
|
+
font-size: 12px;
|
|
273
|
+
}
|
|
@@ -2,6 +2,10 @@ import { API_BASE } from '../config.js';
|
|
|
2
2
|
import { showToast, getById } from '../utils.js';
|
|
3
3
|
// Import Highlight.js core
|
|
4
4
|
import hljs from 'highlight.js/lib/core';
|
|
5
|
+
// Import markdown-it for rendering .md files
|
|
6
|
+
import markdownit from 'markdown-it';
|
|
7
|
+
// Import mermaid for rendering diagrams
|
|
8
|
+
import mermaid from 'mermaid';
|
|
5
9
|
|
|
6
10
|
// Import common languages to reduce bundle size
|
|
7
11
|
import javascript from 'highlight.js/lib/languages/javascript';
|
|
@@ -28,6 +32,79 @@ hljs.registerLanguage('python', python);
|
|
|
28
32
|
hljs.registerLanguage('sql', sql);
|
|
29
33
|
hljs.registerLanguage('markdown', markdown);
|
|
30
34
|
|
|
35
|
+
// Initialize markdown-it with safe settings
|
|
36
|
+
const md = markdownit({
|
|
37
|
+
html: false, // Disable HTML tags in source for security (prevents XSS)
|
|
38
|
+
breaks: true, // Convert line breaks to <br>
|
|
39
|
+
linkify: true, // Auto-convert URLs to links
|
|
40
|
+
typographer: true, // Enable smart quotes and other typographic replacements
|
|
41
|
+
highlight: function (str, lang) {
|
|
42
|
+
// Use highlight.js for code blocks within markdown
|
|
43
|
+
if (lang && hljs.getLanguage(lang)) {
|
|
44
|
+
try {
|
|
45
|
+
return hljs.highlight(str, { language: lang }).value;
|
|
46
|
+
} catch (__) {}
|
|
47
|
+
}
|
|
48
|
+
return ''; // Use default escaping
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Initialize mermaid with dark theme
|
|
53
|
+
mermaid.initialize({
|
|
54
|
+
startOnLoad: false, // We'll manually trigger rendering
|
|
55
|
+
theme: 'dark', // Match VG Coder's dark theme
|
|
56
|
+
securityLevel: 'loose', // Allow interaction in shadow DOM
|
|
57
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Render mermaid diagrams in the container
|
|
62
|
+
* Finds all code.language-mermaid blocks and replaces them with rendered SVG
|
|
63
|
+
*/
|
|
64
|
+
async function renderMermaidDiagrams(container) {
|
|
65
|
+
const mermaidBlocks = container.querySelectorAll('code.language-mermaid');
|
|
66
|
+
|
|
67
|
+
if (mermaidBlocks.length === 0) {
|
|
68
|
+
return; // No mermaid diagrams to render
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`[Mermaid] Found ${mermaidBlocks.length} diagram(s) to render`);
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < mermaidBlocks.length; i++) {
|
|
74
|
+
const block = mermaidBlocks[i];
|
|
75
|
+
const code = block.textContent;
|
|
76
|
+
const pre = block.parentElement;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// Use mermaid.render() which is more compatible with shadow DOM
|
|
80
|
+
const id = `mermaid-diagram-${Date.now()}-${i}`;
|
|
81
|
+
const { svg, bindFunctions } = await mermaid.render(id, code);
|
|
82
|
+
|
|
83
|
+
// Create wrapper div for mermaid
|
|
84
|
+
const wrapper = document.createElement('div');
|
|
85
|
+
wrapper.className = 'mermaid-diagram';
|
|
86
|
+
wrapper.innerHTML = svg;
|
|
87
|
+
|
|
88
|
+
// Replace pre/code with wrapper
|
|
89
|
+
pre.replaceWith(wrapper);
|
|
90
|
+
|
|
91
|
+
// Bind any interactive functions if present
|
|
92
|
+
if (bindFunctions) {
|
|
93
|
+
bindFunctions(wrapper);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`[Mermaid] Successfully rendered diagram ${i + 1}`);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error(`[Mermaid] Failed to render diagram ${i + 1}:`, err);
|
|
99
|
+
// On error, show error message instead of broken diagram
|
|
100
|
+
const errorDiv = document.createElement('div');
|
|
101
|
+
errorDiv.className = 'mermaid-error';
|
|
102
|
+
errorDiv.textContent = `Mermaid rendering error: ${err.message}`;
|
|
103
|
+
pre.replaceWith(errorDiv);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
31
108
|
export async function openFileInViewer(path) {
|
|
32
109
|
const container = getById('code-viewer-container');
|
|
33
110
|
if (!container) return;
|
|
@@ -41,22 +118,41 @@ export async function openFileInViewer(path) {
|
|
|
41
118
|
|
|
42
119
|
if (res.ok) {
|
|
43
120
|
const ext = path.split('.').pop().toLowerCase();
|
|
44
|
-
const language = getLanguageFromExt(ext);
|
|
45
|
-
|
|
46
|
-
// Escape HTML tags to prevent XSS and ensure correct rendering
|
|
47
|
-
const escapedCode = escapeHtml(data.content);
|
|
48
121
|
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
122
|
+
// Check if file is markdown
|
|
123
|
+
if (ext === 'md') {
|
|
124
|
+
// Render as markdown
|
|
125
|
+
const htmlContent = md.render(data.content);
|
|
126
|
+
|
|
127
|
+
container.innerHTML = `
|
|
128
|
+
<div class="cv-wrapper cv-markdown">
|
|
129
|
+
<div class="markdown-body">
|
|
130
|
+
${htmlContent}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
// Render mermaid diagrams (if any)
|
|
136
|
+
await renderMermaidDiagrams(container);
|
|
137
|
+
} else {
|
|
138
|
+
// Render as code with syntax highlighting
|
|
139
|
+
const language = getLanguageFromExt(ext);
|
|
140
|
+
|
|
141
|
+
// Escape HTML tags to prevent XSS and ensure correct rendering
|
|
142
|
+
const escapedCode = escapeHtml(data.content);
|
|
143
|
+
|
|
144
|
+
// Render Code Block
|
|
145
|
+
container.innerHTML = `
|
|
146
|
+
<div class="cv-wrapper">
|
|
147
|
+
<pre><code class="language-${language}">${escapedCode}</code></pre>
|
|
148
|
+
</div>
|
|
149
|
+
`;
|
|
55
150
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
151
|
+
// Apply Highlight.js
|
|
152
|
+
const codeBlock = container.querySelector('code');
|
|
153
|
+
if(codeBlock) {
|
|
154
|
+
hljs.highlightElement(codeBlock);
|
|
155
|
+
}
|
|
60
156
|
}
|
|
61
157
|
|
|
62
158
|
} else {
|
|
@@ -112,10 +112,8 @@ function toggleDashboard() {
|
|
|
112
112
|
|
|
113
113
|
if (isVisible) {
|
|
114
114
|
appRoot.classList.remove('visible');
|
|
115
|
-
console.log('[Keyboard] Dashboard hidden');
|
|
116
115
|
} else {
|
|
117
116
|
appRoot.classList.add('visible');
|
|
118
|
-
console.log('[Keyboard] Dashboard shown');
|
|
119
117
|
}
|
|
120
118
|
}
|
|
121
119
|
|
|
@@ -132,7 +130,6 @@ function openGitPanel() {
|
|
|
132
130
|
// First, make sure dashboard is visible
|
|
133
131
|
if (!appRoot.classList.contains('visible')) {
|
|
134
132
|
appRoot.classList.add('visible');
|
|
135
|
-
console.log('[Keyboard] Dashboard opened');
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
// Then activate Git panel using getRoot() to query in Shadow DOM
|
|
@@ -183,7 +180,6 @@ function handleKeyDown(event) {
|
|
|
183
180
|
}
|
|
184
181
|
|
|
185
182
|
const shortcutKey = getShortcutKey(event);
|
|
186
|
-
console.log('[Keyboard] Shortcut key detected:', shortcutKey);
|
|
187
183
|
|
|
188
184
|
if (!shortcutKey) return;
|
|
189
185
|
|
|
@@ -191,7 +187,6 @@ function handleKeyDown(event) {
|
|
|
191
187
|
const action = SHORTCUTS[shortcutKey];
|
|
192
188
|
|
|
193
189
|
if (!action) {
|
|
194
|
-
console.log('[Keyboard] No action registered for:', shortcutKey);
|
|
195
190
|
return;
|
|
196
191
|
}
|
|
197
192
|
|
|
@@ -199,7 +194,6 @@ function handleKeyDown(event) {
|
|
|
199
194
|
event.preventDefault();
|
|
200
195
|
event.stopPropagation();
|
|
201
196
|
|
|
202
|
-
console.log(`[Keyboard] Shortcut triggered: ${shortcutKey} -> ${action}`);
|
|
203
197
|
|
|
204
198
|
// Execute action
|
|
205
199
|
switch (action) {
|
|
@@ -213,7 +207,7 @@ function handleKeyDown(event) {
|
|
|
213
207
|
toggleShortcutsHelp();
|
|
214
208
|
break;
|
|
215
209
|
default:
|
|
216
|
-
|
|
210
|
+
return;
|
|
217
211
|
}
|
|
218
212
|
}
|
|
219
213
|
|
|
@@ -231,12 +225,10 @@ function toggleShortcutsHelp() {
|
|
|
231
225
|
|
|
232
226
|
if (isVisible) {
|
|
233
227
|
helpPanel.classList.remove('visible');
|
|
234
|
-
console.log('[Keyboard] Help panel hidden');
|
|
235
228
|
} else {
|
|
236
229
|
// Render shortcuts before showing
|
|
237
230
|
renderHelpPanel();
|
|
238
231
|
helpPanel.classList.add('visible');
|
|
239
|
-
console.log('[Keyboard] Help panel shown');
|
|
240
232
|
}
|
|
241
233
|
}
|
|
242
234
|
|
|
@@ -45,7 +45,7 @@ export function initProjectPanel() {
|
|
|
45
45
|
if (getById('project-panel-selector')) {
|
|
46
46
|
loadProjectsIntoSelector();
|
|
47
47
|
}
|
|
48
|
-
},
|
|
48
|
+
}, 30000);
|
|
49
49
|
|
|
50
50
|
console.log('[ProjectPanel] Initialized with polling (Socket.IO not available in shadow DOM)');
|
|
51
51
|
}
|
|
@@ -131,35 +131,27 @@ async function loadProjectsIntoSelector() {
|
|
|
131
131
|
|
|
132
132
|
// Save current selection to preserve it
|
|
133
133
|
const currentValue = selector.value;
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
|
|
136
135
|
try {
|
|
137
136
|
const apiBase = window.API_BASE || 'http://localhost:6868';
|
|
138
|
-
console.log('[ProjectPanel] Fetching projects from:', `${apiBase}/api/projects`);
|
|
139
137
|
|
|
140
138
|
const response = await fetch(`${apiBase}/api/projects`);
|
|
141
139
|
const data = await response.json();
|
|
142
140
|
|
|
143
|
-
console.log('[ProjectPanel] Received projects:', data);
|
|
144
|
-
|
|
145
141
|
if (data.projects && data.projects.length > 0) {
|
|
146
142
|
const options = data.projects.map(p =>
|
|
147
143
|
`<option value="${p.id}" ${p.isActive ? 'selected' : ''}>${p.name}</option>`
|
|
148
144
|
).join('');
|
|
149
145
|
|
|
150
|
-
console.log('[ProjectPanel] Updating selector with', data.projects.length, 'projects');
|
|
151
146
|
selector.innerHTML = options;
|
|
152
147
|
|
|
153
148
|
// IMPORTANT: Restore previous selection if it still exists
|
|
154
149
|
// This prevents auto-switching when new projects join
|
|
155
150
|
if (currentValue && data.projects.some(p => p.id === currentValue)) {
|
|
156
151
|
selector.value = currentValue;
|
|
157
|
-
console.log('[ProjectPanel] â
Restored previous selection:', currentValue);
|
|
158
152
|
} else {
|
|
159
|
-
console.log('[ProjectPanel] âšī¸ Using active project from API:', data.activeProjectId);
|
|
160
153
|
}
|
|
161
154
|
} else {
|
|
162
|
-
console.warn('[ProjectPanel] No projects returned from API');
|
|
163
155
|
selector.innerHTML = '<option value="">No projects</option>';
|
|
164
156
|
}
|
|
165
157
|
} catch (err) {
|