diff --git a/src/web/files.html b/src/web/files.html index f4dccde1b..0b941f205 100644 --- a/src/web/files.html +++ b/src/web/files.html @@ -11,22 +11,24 @@

Package File Browser

- +
- +
-
/
+
/
Loading files...
-
+
Files
-
-
-
+
File Content
@@ -46,7 +48,7 @@ /** @type {DB} */ let activeDb = {}; /** @type {string[]} */ - let pathStack = ["/"]; + let pathStack = [""]; /** @type {string[][]} */ let historyStack = []; let currentSearch = ""; @@ -77,18 +79,51 @@ let result = {}; let filesCount = 0; + const q = query.replace(/^\/|\/$/g, '').toLowerCase(); + const parts = q.split('/'); + const targetSegment = parts[0]; + for (const [key, value] of Object.entries(node)) { + const lowerKey = key.toLowerCase(); if (typeof value === 'object') { - const isMatch = parentMatched || key.toLowerCase().includes(query); - const subTree = filterDb(value, query, isMatch); - if (subTree !== null) { - result[key] = subTree; - filesCount++; + if (parentMatched) { + const subTree = filterDb(value, q, true); + if (subTree !== null) { + result[key] = subTree; + filesCount++; + } + } else { + let nextQuery = q; + let isMatch = false; + + if (lowerKey.startsWith(targetSegment)) { + if (parts.length === 1) { + isMatch = true; + } else { + nextQuery = parts.slice(1).join('/'); + } + } + + let subTree = filterDb(value, nextQuery, isMatch); + + if (subTree === null && nextQuery !== q) { + subTree = filterDb(value, q, false); + } + + if (subTree !== null) { + result[key] = subTree; + filesCount++; + } } } else { - if (parentMatched || key.toLowerCase().includes(query)) { + if (parentMatched) { result[key] = value; filesCount++; + } else { + if (parts.length === 1 && lowerKey.startsWith(targetSegment)) { + result[key] = value; + filesCount++; + } } } } @@ -120,14 +155,15 @@ for (const [dirKey, dirNode] of Object.entries(dirs)) { let childHasFiles = false; - for (const val of Object.values(dirNode)) { + let dirNodeValues = Object.values(dirNode); + for (const val of dirNodeValues) { if (typeof val !== 'object') { childHasFiles = true; break; } } - if (!childHasFiles && Object.keys(dirNode).length > 0) { + if (!childHasFiles && dirNodeValues.length > 0 && dirNodeValues.length < 5) { for (const [subKey, subValue] of Object.entries(dirNode)) { newNode[`${dirKey}/${subKey}`] = subValue; } @@ -172,13 +208,14 @@ filePanel.innerHTML = ''; if (isPeeking) { - const peekName = basePathArray[basePathArray.length - 1]; - fileHeader.innerHTML = `Files (Peeking: ${peekName})`; + const peekName = basePathArray.join('/'); + fileHeader.innerHTML = `Files (Peeking: ${peekName})`; } else if (lockedFileViewPath) { - const lockName = lockedFileViewPath[lockedFileViewPath.length - 1]; - fileHeader.innerHTML = `Files (Selected: ${lockName})`; + const lockName = lockedFileViewPath.join('/'); + fileHeader.innerHTML = `Files (Selected: ${lockName})`; } else { - fileHeader.innerHTML = `Files`; + const pathName = basePathArray.join('/') || '/'; + fileHeader.innerHTML = `Files (${pathName})`; } if (!node) { @@ -217,18 +254,21 @@ * @param {HTMLElement} container * @param {DB} node * @param {number} currentDepth - * @param {number} maxDepth + * @param {(depth: number, files: number) => boolean} maxDepthFn * @param {string[]} currentPathArray + * @returns {boolean} */ - function renderDirTree(container, node, currentDepth, maxDepth, currentPathArray) { - if (currentDepth >= maxDepth || !node) return; + function renderDirTree(container, node, currentDepth, maxDepthFn, currentPathArray) { + if (!node) return false; let dirs = []; for (const [key, value] of Object.entries(node)) { if (typeof value === 'object') dirs.push(key); } + if (!maxDepthFn(currentDepth, dirs.length)) { + return false; + } dirs.sort(); - dirs.forEach(dir => { const dirDiv = document.createElement('div'); dirDiv.className = 'list-item'; @@ -255,6 +295,7 @@ } dirDiv.innerHTML = `${dir}`; + dirDiv.setAttribute('data-path', nextPathArray.join('/')); dirDiv.addEventListener('mouseenter', () => { renderFiles(node[dir], nextPathArray, true); @@ -281,8 +322,12 @@ container.appendChild(dirDiv); - renderDirTree(container, node[dir], currentDepth + 1, maxDepth, nextPathArray); + let rendered = renderDirTree(container, node[dir], currentDepth + 1, maxDepthFn, nextPathArray); + if (hasSubDirs) { + dirDiv.innerText += rendered ? " .." : " ..."; + } }); + return true; } function render() { @@ -290,15 +335,16 @@ if (!node) { // reset to root - pathStack = ["/"]; + pathStack = []; historyStack = []; node = getCurrentNode(); lockedFileViewPath = null; if (!node) return; } - dirHeader.innerText = pathStack.join('/').replace('//', '/'); + dirHeader.innerText = pathStack.join('/') || '/'; + let lastScroll = dirPanel.scrollTop; dirPanel.innerHTML = ''; if (historyStack.length > 0) { @@ -306,7 +352,7 @@ backDiv.className = 'list-item up-icon'; backDiv.innerHTML = `Back`; backDiv.onclick = () => { - pathStack = historyStack.pop() || ['/']; + pathStack = historyStack.pop() || ['']; lockedFileViewPath = null; clearContentPanel(); render(); @@ -314,7 +360,14 @@ dirPanel.appendChild(backDiv); } - renderDirTree(dirPanel, node, 0, 3, pathStack); + renderDirTree(dirPanel, node, 0, (depth, files) => { + switch (depth) { + case 0: return true; + case 1: return files < 10; + case 2: return files < 5; + default: return false; + } + }, pathStack); if (dirPanel.children.length === 0 || (dirPanel.children.length === 1 && pathStack.length > 1)) { const emptyMsg = document.createElement('div'); @@ -322,6 +375,8 @@ emptyMsg.style.color = '#777'; emptyMsg.innerText = 'No subdirectories'; dirPanel.appendChild(emptyMsg); + } else { + dirPanel.scrollTop = lastScroll; } renderFiles(node, pathStack, false); @@ -334,7 +389,7 @@ * @param {string[]} basePathArray */ function showFileContent(fileName, packages, basePathArray) { - const fullPath = (basePathArray.join('/') + '/' + fileName).replace('//', '/'); + const fullPath = (basePathArray.join('/') + '/' + fileName); contentHeader.innerText = fileName; contentPanel.innerHTML = `
@@ -343,7 +398,7 @@
Provided by:
- ${packages.split(',').map(p => `${p}`).join(', ')} + ${packages.split(',').map(p => `${p}`).join(', ')}
`; } diff --git a/src/web/html.rs b/src/web/html.rs index e7905fe23..336a31701 100644 --- a/src/web/html.rs +++ b/src/web/html.rs @@ -260,6 +260,7 @@ pub fn generate_html_index(

Redox OS Package Repository

Repository for {target}

+

Search files in this Repository

diff --git a/src/web/style.css b/src/web/style.css index 87773f551..49bd29a8f 100644 --- a/src/web/style.css +++ b/src/web/style.css @@ -11,6 +11,10 @@ body { line-height: 1.6; } +p { + margin: 0.5em 0; +} + .container { max-width: 1280px; margin: 0 auto; @@ -110,6 +114,7 @@ code { padding: 0.2em 0.4em; border-radius: 3px; font-size: 0.9em; + font-weight: normal; } .card { @@ -290,6 +295,7 @@ th { padding: 8px 15px; cursor: pointer; display: flex; + flex-wrap: wrap; align-items: center; user-select: none; } @@ -320,6 +326,12 @@ th { margin-right: 0.2em; } +.nowrap { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + @media (prefers-color-scheme: dark) { body { background-color: #000;