@cluesmith/codev 1.4.0 → 1.4.2
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/bin/af.js +0 -0
- package/bin/codev.js +0 -0
- package/bin/consult.js +0 -0
- package/bin/generate-image.js +0 -0
- package/dist/agent-farm/servers/dashboard-server.js +487 -5
- package/dist/agent-farm/servers/dashboard-server.js.map +1 -1
- package/dist/commands/adopt.d.ts.map +1 -1
- package/dist/commands/adopt.js +10 -0
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -0
- package/dist/commands/init.js.map +1 -1
- package/package.json +1 -1
- package/skeleton/templates/projectlist-archive.md +21 -0
- package/skeleton/templates/projectlist.md +17 -0
- package/templates/dashboard/css/activity.css +151 -0
- package/templates/dashboard/css/dialogs.css +149 -0
- package/templates/dashboard/css/files.css +530 -0
- package/templates/dashboard/css/layout.css +124 -0
- package/templates/dashboard/css/projects.css +501 -0
- package/templates/dashboard/css/statusbar.css +23 -0
- package/templates/dashboard/css/tabs.css +314 -0
- package/templates/dashboard/css/utilities.css +50 -0
- package/templates/dashboard/css/variables.css +45 -0
- package/templates/dashboard/index.html +158 -0
- package/templates/dashboard/js/activity.js +238 -0
- package/templates/dashboard/js/dialogs.js +328 -0
- package/templates/dashboard/js/files.js +436 -0
- package/templates/dashboard/js/main.js +487 -0
- package/templates/dashboard/js/projects.js +544 -0
- package/templates/dashboard/js/state.js +91 -0
- package/templates/dashboard/js/tabs.js +500 -0
- package/templates/dashboard/js/utils.js +57 -0
- package/templates/dashboard-split.html +1186 -171
- package/templates/open.html +7 -2
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// File Tree Browser and Search (Spec 0055, 0058)
|
|
2
|
+
|
|
3
|
+
// Load the file tree from the API
|
|
4
|
+
async function loadFilesTree() {
|
|
5
|
+
try {
|
|
6
|
+
const response = await fetch('/api/files');
|
|
7
|
+
if (!response.ok) {
|
|
8
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
9
|
+
}
|
|
10
|
+
filesTreeData = await response.json();
|
|
11
|
+
filesTreeError = null;
|
|
12
|
+
filesTreeLoaded = true;
|
|
13
|
+
filesTreeFlat = flattenFilesTree(filesTreeData);
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error('Failed to load files tree:', err);
|
|
16
|
+
filesTreeError = 'Could not load file tree: ' + err.message;
|
|
17
|
+
filesTreeData = [];
|
|
18
|
+
filesTreeFlat = [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Load files tree if not already loaded
|
|
23
|
+
async function loadFilesTreeIfNeeded() {
|
|
24
|
+
if (!filesTreeLoaded) {
|
|
25
|
+
await loadFilesTree();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Flatten the file tree into a searchable array
|
|
30
|
+
function flattenFilesTree(nodes, result = []) {
|
|
31
|
+
for (const node of nodes) {
|
|
32
|
+
if (node.type === 'file') {
|
|
33
|
+
result.push({ name: node.name, path: node.path });
|
|
34
|
+
} else if (node.children) {
|
|
35
|
+
flattenFilesTree(node.children, result);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Search files with relevance sorting
|
|
42
|
+
function searchFiles(query) {
|
|
43
|
+
if (!query) return [];
|
|
44
|
+
const q = query.toLowerCase();
|
|
45
|
+
|
|
46
|
+
const matches = filesTreeFlat.filter(f =>
|
|
47
|
+
f.path.toLowerCase().includes(q)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
matches.sort((a, b) => {
|
|
51
|
+
const aName = a.name.toLowerCase();
|
|
52
|
+
const bName = b.name.toLowerCase();
|
|
53
|
+
const aPath = a.path.toLowerCase();
|
|
54
|
+
const bPath = b.path.toLowerCase();
|
|
55
|
+
|
|
56
|
+
if (aName === q && bName !== q) return -1;
|
|
57
|
+
if (bName === q && aName !== q) return 1;
|
|
58
|
+
if (aName.startsWith(q) && !bName.startsWith(q)) return -1;
|
|
59
|
+
if (bName.startsWith(q) && !aName.startsWith(q)) return 1;
|
|
60
|
+
if (aName.includes(q) && !bName.includes(q)) return -1;
|
|
61
|
+
if (bName.includes(q) && !aName.includes(q)) return 1;
|
|
62
|
+
return aPath.localeCompare(bPath);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return matches.slice(0, 15);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Get file icon based on extension
|
|
69
|
+
function getFileIcon(filename) {
|
|
70
|
+
const ext = filename.split('.').pop().toLowerCase();
|
|
71
|
+
const iconMap = {
|
|
72
|
+
'js': '📜', 'ts': '📜', 'jsx': '⚛️', 'tsx': '⚛️',
|
|
73
|
+
'json': '{}', 'md': '📝', 'html': '🌐', 'css': '🎨',
|
|
74
|
+
'py': '🐍', 'sh': '⚙️', 'bash': '⚙️', 'yml': '⚙️', 'yaml': '⚙️',
|
|
75
|
+
'png': '🖼️', 'jpg': '🖼️', 'jpeg': '🖼️', 'gif': '🖼️', 'svg': '🖼️',
|
|
76
|
+
};
|
|
77
|
+
return iconMap[ext] || '📄';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Render tree nodes recursively
|
|
81
|
+
function renderTreeNodes(nodes, depth) {
|
|
82
|
+
if (!nodes || nodes.length === 0) return '';
|
|
83
|
+
|
|
84
|
+
return nodes.map(node => {
|
|
85
|
+
const indent = depth * 16;
|
|
86
|
+
const isExpanded = filesTreeExpanded.has(node.path);
|
|
87
|
+
const jsPath = escapeJsString(node.path);
|
|
88
|
+
|
|
89
|
+
if (node.type === 'dir') {
|
|
90
|
+
const icon = isExpanded ? '▼' : '▶';
|
|
91
|
+
const childrenHtml = node.children && node.children.length > 0
|
|
92
|
+
? `<div class="tree-children ${isExpanded ? '' : 'collapsed'}" data-path="${escapeHtml(node.path)}">${renderTreeNodes(node.children, depth + 1)}</div>`
|
|
93
|
+
: '';
|
|
94
|
+
|
|
95
|
+
return `
|
|
96
|
+
<div class="tree-item" data-type="dir" data-path="${escapeHtml(node.path)}" style="padding-left: ${indent + 8}px;" onclick="toggleFolder('${jsPath}')">
|
|
97
|
+
<span class="tree-item-icon folder-toggle">${icon}</span>
|
|
98
|
+
<span class="tree-item-name">${escapeHtml(node.name)}</span>
|
|
99
|
+
</div>
|
|
100
|
+
${childrenHtml}
|
|
101
|
+
`;
|
|
102
|
+
} else {
|
|
103
|
+
return `
|
|
104
|
+
<div class="tree-item" data-type="file" data-path="${escapeHtml(node.path)}" style="padding-left: ${indent + 8}px;" onclick="openFileFromTree('${jsPath}')">
|
|
105
|
+
<span class="tree-item-icon">${getFileIcon(node.name)}</span>
|
|
106
|
+
<span class="tree-item-name">${escapeHtml(node.name)}</span>
|
|
107
|
+
</div>
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
}).join('');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Toggle folder expanded/collapsed state
|
|
114
|
+
function toggleFolder(path) {
|
|
115
|
+
if (filesTreeExpanded.has(path)) {
|
|
116
|
+
filesTreeExpanded.delete(path);
|
|
117
|
+
} else {
|
|
118
|
+
filesTreeExpanded.add(path);
|
|
119
|
+
}
|
|
120
|
+
rerenderFilesBrowser();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Re-render file browser in current context
|
|
124
|
+
function rerenderFilesBrowser() {
|
|
125
|
+
if (activeTabId === 'dashboard') {
|
|
126
|
+
const filesContentEl = document.getElementById('dashboard-files-content');
|
|
127
|
+
if (filesContentEl) {
|
|
128
|
+
filesContentEl.innerHTML = filesSearchQuery
|
|
129
|
+
? renderFilesSearchResults()
|
|
130
|
+
: renderDashboardFilesBrowserWithWrapper();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Wrapper for file browser
|
|
136
|
+
function renderDashboardFilesBrowserWithWrapper() {
|
|
137
|
+
return `<div class="dashboard-files-list" id="dashboard-files-list">${renderDashboardFilesBrowser()}</div>`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Render compact file browser for dashboard
|
|
141
|
+
function renderDashboardFilesBrowser() {
|
|
142
|
+
if (filesTreeError) {
|
|
143
|
+
return `<div class="dashboard-empty-state">${escapeHtml(filesTreeError)}</div>`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!filesTreeLoaded || filesTreeData.length === 0) {
|
|
147
|
+
return '<div class="dashboard-empty-state">Loading files...</div>';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return renderTreeNodes(filesTreeData, 0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Collapse all folders
|
|
154
|
+
function collapseAllFolders() {
|
|
155
|
+
filesTreeExpanded.clear();
|
|
156
|
+
rerenderFilesBrowser();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Expand all folders
|
|
160
|
+
function expandAllFolders() {
|
|
161
|
+
function collectPaths(nodes) {
|
|
162
|
+
for (const node of nodes) {
|
|
163
|
+
if (node.type === 'dir') {
|
|
164
|
+
filesTreeExpanded.add(node.path);
|
|
165
|
+
if (node.children) {
|
|
166
|
+
collectPaths(node.children);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
collectPaths(filesTreeData);
|
|
172
|
+
rerenderFilesBrowser();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Refresh files tree
|
|
176
|
+
async function refreshFilesTree() {
|
|
177
|
+
await loadFilesTree();
|
|
178
|
+
rerenderFilesBrowser();
|
|
179
|
+
showToast('Files refreshed', 'success');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Open file from tree click
|
|
183
|
+
async function openFileFromTree(filePath) {
|
|
184
|
+
try {
|
|
185
|
+
const existingTab = tabs.find(t => t.type === 'file' && t.path === filePath);
|
|
186
|
+
if (existingTab) {
|
|
187
|
+
selectTab(existingTab.id);
|
|
188
|
+
refreshFileTab(existingTab.id);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const response = await fetch('/api/tabs/file', {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
headers: { 'Content-Type': 'application/json' },
|
|
195
|
+
body: JSON.stringify({ path: filePath })
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (!response.ok) {
|
|
199
|
+
throw new Error(await response.text());
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
await refresh();
|
|
203
|
+
|
|
204
|
+
const newTab = tabs.find(t => t.type === 'file' && t.path === filePath);
|
|
205
|
+
if (newTab) {
|
|
206
|
+
selectTab(newTab.id);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
showToast(`Opened ${getFileName(filePath)}`, 'success');
|
|
210
|
+
} catch (err) {
|
|
211
|
+
showToast('Failed to open file: ' + err.message, 'error');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ========================================
|
|
216
|
+
// File Search Functions (Spec 0058)
|
|
217
|
+
// ========================================
|
|
218
|
+
|
|
219
|
+
// Debounced search input handler for Files column
|
|
220
|
+
function onFilesSearchInput(value) {
|
|
221
|
+
clearTimeout(filesSearchDebounceTimer);
|
|
222
|
+
filesSearchDebounceTimer = setTimeout(() => {
|
|
223
|
+
filesSearchQuery = value;
|
|
224
|
+
filesSearchResults = searchFiles(value);
|
|
225
|
+
filesSearchIndex = 0;
|
|
226
|
+
rerenderFilesSearch();
|
|
227
|
+
}, 100);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Clear files search and restore tree view
|
|
231
|
+
function clearFilesSearch() {
|
|
232
|
+
filesSearchQuery = '';
|
|
233
|
+
filesSearchResults = [];
|
|
234
|
+
filesSearchIndex = 0;
|
|
235
|
+
const input = document.getElementById('files-search-input');
|
|
236
|
+
if (input) {
|
|
237
|
+
input.value = '';
|
|
238
|
+
}
|
|
239
|
+
rerenderFilesSearch();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Re-render the files search area
|
|
243
|
+
function rerenderFilesSearch() {
|
|
244
|
+
const filesContentEl = document.getElementById('dashboard-files-content');
|
|
245
|
+
if (filesContentEl) {
|
|
246
|
+
filesContentEl.innerHTML = filesSearchQuery
|
|
247
|
+
? renderFilesSearchResults()
|
|
248
|
+
: renderDashboardFilesBrowserWithWrapper();
|
|
249
|
+
}
|
|
250
|
+
const clearBtn = document.querySelector('.files-search-clear');
|
|
251
|
+
if (clearBtn) {
|
|
252
|
+
clearBtn.classList.toggle('hidden', !filesSearchQuery);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Render search results for Files column
|
|
257
|
+
function renderFilesSearchResults() {
|
|
258
|
+
if (!filesSearchResults.length) {
|
|
259
|
+
return '<div class="dashboard-empty-state">No files found</div>';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return `<div class="files-search-results">${filesSearchResults.map((file, index) =>
|
|
263
|
+
renderSearchResult(file, index, index === filesSearchIndex, filesSearchQuery, 'files')
|
|
264
|
+
).join('')}</div>`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Highlight matching text in search results
|
|
268
|
+
function highlightMatch(text, query) {
|
|
269
|
+
if (!query) return escapeHtml(text);
|
|
270
|
+
const q = query.toLowerCase();
|
|
271
|
+
const t = text.toLowerCase();
|
|
272
|
+
const idx = t.indexOf(q);
|
|
273
|
+
if (idx === -1) return escapeHtml(text);
|
|
274
|
+
|
|
275
|
+
return escapeHtml(text.substring(0, idx)) +
|
|
276
|
+
'<span class="files-search-highlight">' + escapeHtml(text.substring(idx, idx + query.length)) + '</span>' +
|
|
277
|
+
escapeHtml(text.substring(idx + query.length));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Render a single search result
|
|
281
|
+
function renderSearchResult(file, index, isSelected, query, context) {
|
|
282
|
+
const classPrefix = context === 'palette' ? 'file-palette' : 'files-search';
|
|
283
|
+
const jsPath = escapeJsString(file.path);
|
|
284
|
+
|
|
285
|
+
return `
|
|
286
|
+
<div class="${classPrefix}-result ${isSelected ? 'selected' : ''}"
|
|
287
|
+
data-index="${index}"
|
|
288
|
+
onclick="openFileFromSearch('${jsPath}', '${context}')">
|
|
289
|
+
<div class="${classPrefix}-result-name">${highlightMatch(file.name, query)}</div>
|
|
290
|
+
<div class="${classPrefix}-result-path">${highlightMatch(file.path, query)}</div>
|
|
291
|
+
</div>
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Keyboard handler for Files search input
|
|
296
|
+
function onFilesSearchKeydown(event) {
|
|
297
|
+
if (!filesSearchResults.length) {
|
|
298
|
+
if (event.key === 'Escape') {
|
|
299
|
+
clearFilesSearch();
|
|
300
|
+
event.target.blur();
|
|
301
|
+
}
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (event.key === 'ArrowDown') {
|
|
306
|
+
event.preventDefault();
|
|
307
|
+
filesSearchIndex = Math.min(filesSearchIndex + 1, filesSearchResults.length - 1);
|
|
308
|
+
rerenderFilesSearch();
|
|
309
|
+
scrollSelectedIntoView('files');
|
|
310
|
+
} else if (event.key === 'ArrowUp') {
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
filesSearchIndex = Math.max(filesSearchIndex - 1, 0);
|
|
313
|
+
rerenderFilesSearch();
|
|
314
|
+
scrollSelectedIntoView('files');
|
|
315
|
+
} else if (event.key === 'Enter') {
|
|
316
|
+
event.preventDefault();
|
|
317
|
+
if (filesSearchResults[filesSearchIndex]) {
|
|
318
|
+
openFileFromSearch(filesSearchResults[filesSearchIndex].path, 'files');
|
|
319
|
+
}
|
|
320
|
+
} else if (event.key === 'Escape') {
|
|
321
|
+
clearFilesSearch();
|
|
322
|
+
event.target.blur();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Scroll selected result into view
|
|
327
|
+
function scrollSelectedIntoView(context) {
|
|
328
|
+
const selector = context === 'palette'
|
|
329
|
+
? '.file-palette-result.selected'
|
|
330
|
+
: '.files-search-result.selected';
|
|
331
|
+
const selected = document.querySelector(selector);
|
|
332
|
+
if (selected) {
|
|
333
|
+
selected.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Open file from search result
|
|
338
|
+
function openFileFromSearch(filePath, context) {
|
|
339
|
+
const existingTab = tabs.find(t => t.type === 'file' && t.path === filePath);
|
|
340
|
+
if (existingTab) {
|
|
341
|
+
selectTab(existingTab.id);
|
|
342
|
+
refreshFileTab(existingTab.id);
|
|
343
|
+
} else {
|
|
344
|
+
openFileFromTree(filePath);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (context === 'palette') {
|
|
348
|
+
closePalette();
|
|
349
|
+
} else {
|
|
350
|
+
clearFilesSearch();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ========================================
|
|
355
|
+
// Cmd+P Palette Functions (Spec 0058)
|
|
356
|
+
// ========================================
|
|
357
|
+
|
|
358
|
+
// Open the file search palette
|
|
359
|
+
function openPalette() {
|
|
360
|
+
paletteOpen = true;
|
|
361
|
+
paletteQuery = '';
|
|
362
|
+
paletteResults = [];
|
|
363
|
+
paletteIndex = 0;
|
|
364
|
+
document.getElementById('file-palette').classList.remove('hidden');
|
|
365
|
+
const input = document.getElementById('palette-input');
|
|
366
|
+
input.value = '';
|
|
367
|
+
input.focus();
|
|
368
|
+
rerenderPaletteResults();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Close the file search palette
|
|
372
|
+
function closePalette() {
|
|
373
|
+
paletteOpen = false;
|
|
374
|
+
paletteQuery = '';
|
|
375
|
+
paletteResults = [];
|
|
376
|
+
paletteIndex = 0;
|
|
377
|
+
document.getElementById('file-palette').classList.add('hidden');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Debounced palette input handler
|
|
381
|
+
function onPaletteInput(value) {
|
|
382
|
+
clearTimeout(paletteDebounceTimer);
|
|
383
|
+
paletteDebounceTimer = setTimeout(() => {
|
|
384
|
+
paletteQuery = value;
|
|
385
|
+
paletteResults = searchFiles(value);
|
|
386
|
+
paletteIndex = 0;
|
|
387
|
+
rerenderPaletteResults();
|
|
388
|
+
}, 100);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Re-render palette results
|
|
392
|
+
function rerenderPaletteResults() {
|
|
393
|
+
const resultsEl = document.getElementById('palette-results');
|
|
394
|
+
if (!resultsEl) return;
|
|
395
|
+
|
|
396
|
+
if (!paletteQuery) {
|
|
397
|
+
resultsEl.innerHTML = '<div class="file-palette-empty">Type to search files...</div>';
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (!paletteResults.length) {
|
|
402
|
+
resultsEl.innerHTML = '<div class="file-palette-empty">No files found</div>';
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
resultsEl.innerHTML = paletteResults.map((file, index) =>
|
|
407
|
+
renderSearchResult(file, index, index === paletteIndex, paletteQuery, 'palette')
|
|
408
|
+
).join('');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Keyboard handler for palette input
|
|
412
|
+
function onPaletteKeydown(event) {
|
|
413
|
+
if (event.key === 'Escape') {
|
|
414
|
+
closePalette();
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (!paletteResults.length) return;
|
|
419
|
+
|
|
420
|
+
if (event.key === 'ArrowDown') {
|
|
421
|
+
event.preventDefault();
|
|
422
|
+
paletteIndex = Math.min(paletteIndex + 1, paletteResults.length - 1);
|
|
423
|
+
rerenderPaletteResults();
|
|
424
|
+
scrollSelectedIntoView('palette');
|
|
425
|
+
} else if (event.key === 'ArrowUp') {
|
|
426
|
+
event.preventDefault();
|
|
427
|
+
paletteIndex = Math.max(paletteIndex - 1, 0);
|
|
428
|
+
rerenderPaletteResults();
|
|
429
|
+
scrollSelectedIntoView('palette');
|
|
430
|
+
} else if (event.key === 'Enter') {
|
|
431
|
+
event.preventDefault();
|
|
432
|
+
if (paletteResults[paletteIndex]) {
|
|
433
|
+
openFileFromSearch(paletteResults[paletteIndex].path, 'palette');
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|