// Display directory structure function displayDirectoryStructure(tree) { console.log("--- displayDirectoryStructure called ---"); console.log("Received tree:", JSON.parse(JSON.stringify(tree))); const container = document.getElementById('directoryStructure'); container.innerHTML = ''; const rootUl = document.createElement('ul'); container.appendChild(rootUl); const commonExtensions = ['.js', '.py', '.java', '.cpp', '.html', '.css', '.ts', '.jsx', '.tsx']; const directoryStructure = {}; const extensionCheckboxes = {}; // Sort the tree before processing tree.sort(sortContents); console.log("Sorted tree:", JSON.parse(JSON.stringify(tree))); // Build directory structure tree.forEach(item => { // This was the problematic line. HF API only returns files. // We build the directory structure from file paths. // if (item.type !== 'blob') return; console.log("Processing item:", item); item.path = item.path.startsWith('/') ? item.path : '/' + item.path; const pathParts = item.path.split('/'); console.log("Path parts:", pathParts); let currentLevel = directoryStructure; pathParts.forEach((part, index) => { part = part === '' ? './' : part; if (!currentLevel[part]) { currentLevel[part] = index === pathParts.length - 1 ? item : {}; } currentLevel = currentLevel[part]; }); }); console.log("Built directory structure object:", directoryStructure); function createTreeNode(name, item, parentUl) { console.log(`Creating tree node for: ${name}`, "Item:", item); const li = document.createElement('li'); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'mr-2'; if (typeof item === 'object' && item !== null && (!item.type || typeof item.type !== 'string')) { // Directory node console.log(` -> Identified as DIRECTORY`); createDirectoryNode(li, checkbox, name, item, parentUl); } else { // File node console.log(` -> Identified as FILE`); createFileNode(li, checkbox, name, item); } li.className = 'my-2'; parentUl.appendChild(li); updateParentCheckbox(checkbox); updateExtensionCheckboxes(); } function createDirectoryNode(li, checkbox, name, item, parentUl) { checkbox.classList.add('directory-checkbox'); li.appendChild(checkbox); const collapseButton = createCollapseButton(); li.appendChild(collapseButton); appendIcon(li, 'folder'); li.appendChild(document.createTextNode(name)); const ul = document.createElement('ul'); ul.className = 'ml-6 mt-2'; li.appendChild(ul); // Sort children: directories first, then files, then alphabetically const sortedEntries = Object.entries(item).sort(([nameA, itemA], [nameB, itemB]) => { const isDirA = typeof itemA === 'object' && itemA !== null && !itemA.type; const isDirB = typeof itemB === 'object' && itemB !== null && !itemB.type; if (isDirA && !isDirB) return -1; if (!isDirA && isDirB) return 1; return nameA.localeCompare(nameB); }); for (const [childName, childItem] of sortedEntries) { createTreeNode(childName, childItem, ul); } addDirectoryCheckboxListener(checkbox, li); addCollapseButtonListener(collapseButton, ul); } function createFileNode(li, checkbox, name, item) { checkbox.value = JSON.stringify({ url: item.url, path: item.path, urlType: item.urlType }); const extension = name.split('.').pop().toLowerCase(); const isCommonFile = commonExtensions.includes('.' + extension); checkbox.checked = isCommonFile; if (!(extension in extensionCheckboxes)) { extensionCheckboxes[extension] = { checkbox: createExtensionCheckbox(extension), children: [] }; } extensionCheckboxes[extension].children.push(checkbox); li.appendChild(checkbox); appendIcon(li, 'file'); li.appendChild(document.createTextNode(name)); } function createCollapseButton() { const collapseButton = document.createElement('button'); collapseButton.innerHTML = ''; collapseButton.className = 'mr-1 focus:outline-none'; return collapseButton; } function appendIcon(element, iconName) { const icon = document.createElement('i'); icon.setAttribute('data-lucide', iconName); icon.className = 'inline-block w-4 h-4 mr-1'; element.appendChild(icon); } function addDirectoryCheckboxListener(checkbox, li) { checkbox.addEventListener('change', function() { const childCheckboxes = li.querySelectorAll('input[type="checkbox"]'); childCheckboxes.forEach(childBox => { childBox.checked = this.checked; childBox.indeterminate = false; }); }); } function addCollapseButtonListener(collapseButton, ul) { collapseButton.addEventListener('click', function() { ul.classList.toggle('hidden'); const icon = this.querySelector('[data-lucide]'); if (ul.classList.contains('hidden')) { icon.setAttribute('data-lucide', 'chevron-right'); } else { icon.setAttribute('data-lucide', 'chevron-down'); } lucide.createIcons(); }); } function createExtensionCheckbox(extension) { const extCheckbox = document.createElement('input'); extCheckbox.type = 'checkbox'; extCheckbox.className = 'mr-1'; extCheckbox.value = extension; return extCheckbox; } for (const [name, item] of Object.entries(directoryStructure)) { createTreeNode(name, item, rootUl); } createExtensionCheckboxesContainer(); // Add event listener to container for checkbox changes container.addEventListener('change', function(event) { if (event.target.type === 'checkbox') { updateParentCheckbox(event.target); updateExtensionCheckboxes(); } }); function updateParentCheckbox(checkbox) { if (!checkbox) return; const li = checkbox.closest('li'); if (!li) return; if (!li.parentElement) return; const parentLi = li.parentElement.closest('li'); if (!parentLi) return; const parentCheckbox = parentLi.querySelector(':scope > input[type="checkbox"]'); const siblingCheckboxes = parentLi.querySelectorAll(':scope > ul > li > input[type="checkbox"]'); const checkedCount = Array.from(siblingCheckboxes).filter(cb => cb.checked).length; const indeterminateCount = Array.from(siblingCheckboxes).filter(cb => cb.indeterminate).length; if (indeterminateCount !== 0) { parentCheckbox.checked = false; parentCheckbox.indeterminate = true; } else if (checkedCount === 0) { parentCheckbox.checked = false; parentCheckbox.indeterminate = false; } else if (checkedCount === siblingCheckboxes.length) { parentCheckbox.checked = true; parentCheckbox.indeterminate = false; } else { parentCheckbox.checked = false; parentCheckbox.indeterminate = true; } // Recursively update parent checkboxes updateParentCheckbox(parentCheckbox); } function updateExtensionCheckboxes() { for (const [extension, checkbox] of Object.entries(extensionCheckboxes)) { const children = checkbox.children; const checkedCount = Array.from(children).filter(cb => cb.checked).length; if (checkedCount === 0) { checkbox.checkbox.checked = false; checkbox.checkbox.indeterminate = false; } else if (checkedCount === children.length) { checkbox.checkbox.checked = true; checkbox.checkbox.indeterminate = false; } else { checkbox.checkbox.checked = false; checkbox.checkbox.indeterminate = true; } } } function createExtensionCheckboxesContainer() { const extentionCheckboxesContainer = document.getElementById('extentionCheckboxes'); extentionCheckboxesContainer.innerHTML = ''; extentionCheckboxesContainer.className = 'mt-4'; const extentionCheckboxesContainerLabel = document.createElement('label'); extentionCheckboxesContainerLabel.innerHTML = 'Filter by file extensions:'; extentionCheckboxesContainerLabel.className = 'block text-sm font-medium text-gray-600'; extentionCheckboxesContainer.appendChild(extentionCheckboxesContainerLabel); const extentionCheckboxesContainerUl = document.createElement('ul'); extentionCheckboxesContainer.appendChild(extentionCheckboxesContainerUl); extentionCheckboxesContainerUl.className = 'mt-1'; const sortedExtensions = Object.entries(extensionCheckboxes).sort((a, b) => b[1].children.length - a[1].children.length); for (const [extension, checkbox] of sortedExtensions) { const extCheckbox = checkbox.checkbox; const extCheckboxLi = document.createElement('li'); extCheckboxLi.className = 'inline-block mr-4'; extCheckboxLi.appendChild(extCheckbox); extCheckboxLi.appendChild(document.createTextNode('.' + extension)); extentionCheckboxesContainerUl.appendChild(extCheckboxLi); extCheckbox.addEventListener('change', function() { const children = checkbox.children; children.forEach(child => { child.checked = this.checked; child.indeterminate = false; updateParentCheckbox(child); }); }); } } lucide.createIcons(); } // Sort contents alphabetically and by directory/file function sortContents(a, b) { if (!a || !b || !a.path || !b.path) return 0; const aPath = a.path.split('/'); const bPath = b.path.split('/'); const minLength = Math.min(aPath.length, bPath.length); for (let i = 0; i < minLength; i++) { if (aPath[i] !== bPath[i]) { if (i === aPath.length - 1 && i < bPath.length - 1) return 1; // a is a directory, b is a file or subdirectory if (i === bPath.length - 1 && i < aPath.length - 1) return -1; // b is a directory, a is a file or subdirectory return aPath[i].localeCompare(bPath[i]); } } return aPath.length - bPath.length; } // Get selected files from the directory structure function getSelectedFiles() { const checkboxes = document.querySelectorAll('#directoryStructure input[type="checkbox"]:checked:not(.directory-checkbox)'); return Array.from(checkboxes).map(checkbox => JSON.parse(checkbox.value)); } // Format repository contents into a single text function formatRepoContents(contents) { let text = ''; let index = ''; // Ensure contents is an array before sorting contents = Array.isArray(contents) ? contents.sort(sortContents) : [contents]; // Create a directory tree structure const tree = {}; contents.forEach(item => { const parts = item.path.split('/'); let currentLevel = tree; parts.forEach((part, i) => { if (!currentLevel[part]) { currentLevel[part] = i === parts.length - 1 ? null : {}; } currentLevel = currentLevel[part]; }); }); // Build the index recursively function buildIndex(node, prefix = '') { let result = ''; const entries = Object.entries(node); entries.forEach(([name, subNode], index) => { const isLastItem = index === entries.length - 1; const linePrefix = isLastItem ? '└── ' : '├── '; const childPrefix = isLastItem ? ' ' : '│ '; name = name === '' ? './' : name; result += `${prefix}${linePrefix}${name}\n`; if (subNode) { result += buildIndex(subNode, `${prefix}${childPrefix}`); } }); return result; } index = buildIndex(tree); contents.forEach((item) => { text += `\n\n---\nFile: ${item.path}\n---\n\n${item.text}\n`; }); const formattedText = `Directory Structure:\n\n${index}\n${text}`; try { const { encode, decode } = GPTTokenizer_cl100k_base; const tokensCount = encode(formattedText).length; document.getElementById('tokenCount').innerHTML = `Approximate Token Count: ${tokensCount} (Using cl100k_base tokenizer)`; } catch (error) { document.getElementById('tokenCount').innerHTML = ''; console.log(error); } return formattedText; } export { displayDirectoryStructure, sortContents, getSelectedFiles, formatRepoContents };