diff --git a/README.md b/README.md index 9b59ce5..61896ba 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ These files should be located in the data directory or mounted as volumes. 1. **Build the Docker image:** ```sh - docker build -t icrn-kernel-webserver:latest -f web/Dockerfile . + docker build -t icrn-kernel-webserver:latest -f web/Dockerfile web/ ``` 2. **Run the container with kernel data:** diff --git a/web/Dockerfile b/web/Dockerfile index 6cc4baa..9e687d1 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -9,16 +9,16 @@ RUN apt-get update && \ WORKDIR /app # Copy requirements and install Python dependencies -COPY web/requirements.txt . +COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Copy application files -COPY web/kernel_service.py . -COPY web/nginx.conf /etc/nginx/nginx.conf -COPY web/start.sh /app/start.sh +COPY kernel_service.py . +COPY nginx.conf /etc/nginx/nginx.conf +COPY start.sh /app/start.sh # Copy static files -COPY web/static/ /app/static/ +COPY static/ /app/static/ # Create data directory and fix line endings RUN mkdir -p /app/data && \ diff --git a/web/static/index.html b/web/static/index.html index 876129c..c1f2dfe 100644 --- a/web/static/index.html +++ b/web/static/index.html @@ -386,6 +386,11 @@
Search Packages
let filteredPackages = []; let sortColumn = -1; let sortDirection = 1; // 1 for ascending, -1 for descending + let packageSearchRows = []; // Store package search results for sorting + let packageSearchSortColumn = -1; + let packageSearchSortDirection = 1; + let packageSearchTotalMatches = 0; // Store total package matches + let packageSearchQuery = ''; // Store search query const API_BASE = '/api'; // Initialize on page load @@ -768,64 +773,239 @@
Search Packages
No packages found matching "${escapeHtml(data.query)}" `; + packageSearchRows = []; + packageSearchTotalMatches = 0; + packageSearchQuery = data.query || ''; return; } - let html = ` -
- Found ${data.total_matches} package(s) matching "${escapeHtml(data.query)}" -
- `; + // Store total matches and query + packageSearchTotalMatches = data.total_matches || 0; + packageSearchQuery = data.query || ''; + // Flatten the data: create one row per package-kernel combination + packageSearchRows = []; data.packages.forEach(pkg => { - html += ` -
-
-
- ${escapeHtml(pkg.name)} -
- ${pkg.kernel_count} kernel(s) -
-
-
Available in the following kernels:
-
- - - - - - - - - - - `; - if (pkg.kernels && pkg.kernels.length > 0) { pkg.kernels.forEach(kernel => { - html += ` + packageSearchRows.push({ + packageName: pkg.name, + packageVersion: kernel.package_version || 'N/A', + kernelName: kernel.kernel_name || 'N/A', + kernelVersion: kernel.kernel_version || 'N/A', + language: kernel.kernel_language || kernel.language || 'N/A' + }); + }); + } else { + // If no kernels, still show the package + packageSearchRows.push({ + packageName: pkg.name, + packageVersion: 'N/A', + kernelName: 'N/A', + kernelVersion: 'N/A', + language: 'N/A' + }); + } + }); + + // Reset sort state + packageSearchSortColumn = -1; + packageSearchSortDirection = 1; + + // Render the table + renderPackageSearchTable(); + } + + // Render package search results table + function renderPackageSearchTable() { + const resultsDiv = document.getElementById('packageSearchResults'); + + if (packageSearchRows.length === 0) { + resultsDiv.innerHTML = '

No results to display

'; + return; + } + + let html = ` +
+ Found ${packageSearchTotalMatches} package(s) matching "${escapeHtml(packageSearchQuery)}" (${packageSearchRows.length} total result(s)) +
+
+
LanguageKernel NameVersionPackage Version
+ - - - - + + + + + - `; - }); + + + `; + + packageSearchRows.forEach((row, index) => { + // Create clickable kernel name link if kernel info is available + let kernelNameCell; + if (row.kernelName !== 'N/A' && row.kernelVersion !== 'N/A' && row.language !== 'N/A') { + kernelNameCell = `${escapeHtml(row.kernelName)}`; } else { - html += ''; + kernelNameCell = escapeHtml(row.kernelName); } html += ` - -
${escapeHtml(kernel.language || 'N/A')}${escapeHtml(kernel.kernel_name || 'N/A')}${escapeHtml(kernel.kernel_version || 'N/A')}${escapeHtml(kernel.package_version || 'N/A')} + Package Name + + + Package Version + + + Kernel Name + + + Kernel Version + + + Language + +
No kernel information available
-
-
-
+ + ${escapeHtml(row.packageName)} + ${escapeHtml(row.packageVersion)} + ${kernelNameCell} + ${escapeHtml(row.kernelVersion)} + ${escapeHtml(row.language)} + `; }); + html += ` + + + + `; + resultsDiv.innerHTML = html; + + // Update sort icons + for (let i = 0; i < 5; i++) { + const icon = document.getElementById(`packageSortIcon${i}`); + if (icon) { + if (packageSearchSortColumn === i) { + if (packageSearchSortDirection === 1) { + icon.className = 'bi bi-arrow-down sort-icon active'; + } else { + icon.className = 'bi bi-arrow-up sort-icon active'; + } + } else { + icon.className = 'bi bi-arrow-down-up sort-icon'; + } + } + } + + // Attach event listeners to kernel links + resultsDiv.querySelectorAll('.kernel-link').forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + const language = this.getAttribute('data-language'); + const kernelName = this.getAttribute('data-kernel-name'); + const kernelVersion = this.getAttribute('data-kernel-version'); + navigateToKernel(language, kernelName, kernelVersion); + }); + }); + } + + // Sort package search table + function sortPackageSearchTable(columnIndex) { + // Reset all sort icons + for (let i = 0; i < 5; i++) { + const icon = document.getElementById(`packageSortIcon${i}`); + if (icon) { + icon.className = 'bi bi-arrow-down-up sort-icon'; + } + } + + // If clicking same column, reverse direction + if (packageSearchSortColumn === columnIndex) { + packageSearchSortDirection *= -1; + } else { + packageSearchSortColumn = columnIndex; + packageSearchSortDirection = 1; + } + + // Update sort icon + const icon = document.getElementById(`packageSortIcon${columnIndex}`); + if (icon) { + if (packageSearchSortDirection === 1) { + icon.className = 'bi bi-arrow-down sort-icon active'; + } else { + icon.className = 'bi bi-arrow-up sort-icon active'; + } + } + + // Sort rows + packageSearchRows.sort((a, b) => { + let aVal, bVal; + switch(columnIndex) { + case 0: // Package Name + aVal = a.packageName || ''; + bVal = b.packageName || ''; + break; + case 1: // Package Version + aVal = a.packageVersion || ''; + bVal = b.packageVersion || ''; + break; + case 2: // Kernel Name + aVal = a.kernelName || ''; + bVal = b.kernelName || ''; + break; + case 3: // Kernel Version + aVal = a.kernelVersion || ''; + bVal = b.kernelVersion || ''; + break; + case 4: // Language + aVal = a.language || ''; + bVal = b.language || ''; + break; + } + + if (aVal < bVal) return -1 * packageSearchSortDirection; + if (aVal > bVal) return 1 * packageSearchSortDirection; + return 0; + }); + + // Re-render the table + renderPackageSearchTable(); + } + + // Function to navigate to kernel view + async function navigateToKernel(language, kernelName, version) { + // Switch to kernels view + showView('kernels'); + + // Set the language if needed + const languageSelect = document.getElementById('languageSelect'); + if (languageSelect.value !== language) { + languageSelect.value = language; + currentLanguage = language; + // Load kernels for the new language + await loadKernels(language); + } + + // Find the kernel item in the list to highlight it + const kernelItems = document.querySelectorAll('.kernel-item'); + let kernelElement = null; + + kernelItems.forEach(item => { + const itemKernelName = item.getAttribute('data-kernel-name'); + const itemVersion = item.getAttribute('data-version'); + + if (itemKernelName === kernelName && itemVersion === version) { + kernelElement = item; + } + }); + + // Select the kernel (with or without element for highlighting) + await selectKernel(language, kernelName, version, kernelElement); }