feat: enhance file selection and prompt generation
- Add gitignore filtering to file tree and search - Introduce recursive directory listing API - Enable recursive directory selection in UI - Pass selected paths directly for prompt generation - Refactor API endpoint names and error handling
This commit is contained in:
@@ -141,14 +141,27 @@ class SimpleFileTree {
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.className = 'tree-checkbox';
|
||||
checkbox.checked = selected.has(path);
|
||||
checkbox.addEventListener('change', (e) => {
|
||||
checkbox.addEventListener('change', async (e) => {
|
||||
e.stopPropagation();
|
||||
if (checkbox.checked) {
|
||||
selected.add(path);
|
||||
// If this is a directory, also select all its children
|
||||
if (item.type === 'directory') {
|
||||
await this.selectDirectoryChildren(path, true);
|
||||
} else {
|
||||
// For files, update UI immediately since no async operation
|
||||
this.updateSelectionUI();
|
||||
}
|
||||
} else {
|
||||
selected.delete(path);
|
||||
// If this is a directory, also deselect all its children
|
||||
if (item.type === 'directory') {
|
||||
await this.selectDirectoryChildren(path, false);
|
||||
} else {
|
||||
// For files, update UI immediately since no async operation
|
||||
this.updateSelectionUI();
|
||||
}
|
||||
}
|
||||
this.updateSelectionUI();
|
||||
});
|
||||
|
||||
// Expand/collapse button for directories
|
||||
@@ -218,11 +231,20 @@ class SimpleFileTree {
|
||||
// Remove from loaded paths so it can be reloaded when expanded again
|
||||
this.loadedPaths.delete(dirPath);
|
||||
} else {
|
||||
// Expand
|
||||
// Expand - update UI optimistically but revert on error
|
||||
this.expandedDirs.add(dirPath);
|
||||
if (expandBtn) expandBtn.innerHTML = '▼';
|
||||
if (icon) icon.textContent = '📂';
|
||||
await this.loadChildren(dirPath);
|
||||
|
||||
// Try to load children
|
||||
const success = await this.loadChildren(dirPath);
|
||||
|
||||
// If loading failed, revert the UI state
|
||||
if (!success) {
|
||||
this.expandedDirs.delete(dirPath);
|
||||
if (expandBtn) expandBtn.innerHTML = '▶';
|
||||
if (icon) icon.textContent = '📁';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +278,7 @@ class SimpleFileTree {
|
||||
|
||||
if (r.error) {
|
||||
console.warn('Failed to load directory:', parentPath, r.error);
|
||||
return;
|
||||
return false; // Return false to indicate failure
|
||||
}
|
||||
|
||||
// Sort items: directories first, then files
|
||||
@@ -271,7 +293,7 @@ class SimpleFileTree {
|
||||
const parentElement = qs(`[data-path="${parentPath}"]`);
|
||||
if (!parentElement) {
|
||||
console.warn('Parent element not found for path:', parentPath);
|
||||
return;
|
||||
return false; // Return false to indicate failure
|
||||
}
|
||||
|
||||
const parentDepth = parseInt(parentElement.dataset.depth || '0');
|
||||
@@ -316,6 +338,7 @@ class SimpleFileTree {
|
||||
});
|
||||
|
||||
this.loadedPaths.add(parentPath);
|
||||
return true; // Return true to indicate success
|
||||
}
|
||||
|
||||
getDepth(path) {
|
||||
@@ -378,6 +401,58 @@ class SimpleFileTree {
|
||||
if (tokenCountEl) tokenCountEl.textContent = tokens.toString();
|
||||
}
|
||||
|
||||
// Select or deselect all children of a directory recursively
|
||||
async selectDirectoryChildren(dirPath, select) {
|
||||
// First, get all children from API to update the selection state
|
||||
await this.selectDirectoryChildrenFromAPI(dirPath, select);
|
||||
|
||||
// Then, update any currently visible children in the DOM
|
||||
this.updateVisibleCheckboxes();
|
||||
|
||||
// Update the selection UI once at the end
|
||||
this.updateSelectionUI();
|
||||
}
|
||||
|
||||
// Update all visible checkboxes to match the current selection state
|
||||
updateVisibleCheckboxes() {
|
||||
const treeItems = document.querySelectorAll('.tree-item');
|
||||
|
||||
treeItems.forEach(item => {
|
||||
const itemPath = item.dataset.path;
|
||||
const checkbox = item.querySelector('.tree-checkbox');
|
||||
if (checkbox && itemPath) {
|
||||
// Set checkbox state based on current selection
|
||||
checkbox.checked = selected.has(itemPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Select directory children using API to get complete recursive list
|
||||
async selectDirectoryChildrenFromAPI(dirPath, select) {
|
||||
try {
|
||||
const response = await fetch(`/api/heroprompt/workspaces/${encodeURIComponent(currentWs)}/list?path=${encodeURIComponent(dirPath)}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.children) {
|
||||
data.children.forEach(child => {
|
||||
const childPath = child.path;
|
||||
if (select) {
|
||||
selected.add(childPath);
|
||||
} else {
|
||||
selected.delete(childPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch directory children:', response.status, response.statusText);
|
||||
const errorText = await response.text();
|
||||
console.error('Error response:', errorText);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error selecting directory children:', error);
|
||||
}
|
||||
}
|
||||
|
||||
createFileCard(path) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'file-card';
|
||||
@@ -1026,19 +1101,15 @@ async function generatePrompt() {
|
||||
outputEl.innerHTML = '<div class="loading">Generating prompt...</div>';
|
||||
|
||||
try {
|
||||
// sync selection to backend before generating
|
||||
// Pass selections directly to prompt generation
|
||||
const paths = Array.from(selected);
|
||||
const syncResult = await post(`/api/heroprompt/workspaces/${encodeURIComponent(currentWs)}/selection`, {
|
||||
paths: JSON.stringify(paths)
|
||||
});
|
||||
|
||||
if (syncResult.error) {
|
||||
throw new Error(`Failed to sync selection: ${syncResult.error}`);
|
||||
}
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('text', promptText);
|
||||
formData.append('selected_paths', JSON.stringify(paths));
|
||||
|
||||
const r = await fetch(`/api/heroprompt/workspaces/${encodeURIComponent(currentWs)}/prompt`, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({ text: promptText })
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!r.ok) {
|
||||
|
||||
Reference in New Issue
Block a user