diff --git a/src/web/files.html b/src/web/files.html index 0c9fdd39..2b15801b 100644 --- a/src/web/files.html +++ b/src/web/files.html @@ -13,11 +13,11 @@

Package File Browser

- +
-
/
+
/
Loading files...
@@ -65,28 +65,61 @@ */ /** - * @param {DB} node - * @param {string} query - * @param {boolean} parentMatched - * @returns {DB|null} - */ + * @param {DB} node + * @param {string} query + * @param {boolean} parentMatched + * @returns {DB|null} + */ function filterDb(node, query, parentMatched = false) { /** @type {DB} */ 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++; + } } } } @@ -172,12 +205,13 @@ if (isPeeking) { const peekName = basePathArray.join('/'); - fileHeader.innerHTML = `Files (Peeking: ${peekName})`; + fileHeader.innerHTML = `Files (Peeking: ${peekName})`; } else if (lockedFileViewPath) { const lockName = lockedFileViewPath.join('/'); - fileHeader.innerHTML = `Files (Selected: ${lockName})`; + fileHeader.innerHTML = `Files (Selected: ${lockName})`; } else { - fileHeader.innerHTML = `Files`; + const pathName = basePathArray.join('/') || '/'; + fileHeader.innerHTML = `Files (${pathName})`; } if (!node) { @@ -304,7 +338,7 @@ if (!node) return; } - dirHeader.innerText = pathStack.join('/'); + dirHeader.innerText = pathStack.join('/') || '/'; let lastScroll = dirPanel.scrollTop; dirPanel.innerHTML = ''; diff --git a/src/web/style.css b/src/web/style.css index 5fad81c5..a846eda3 100644 --- a/src/web/style.css +++ b/src/web/style.css @@ -110,6 +110,7 @@ code { padding: 0.2em 0.4em; border-radius: 3px; font-size: 0.9em; + font-weight: normal; } .card { @@ -321,6 +322,12 @@ th { margin-right: 0.2em; } +.nowrap { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + @media (prefers-color-scheme: dark) { body { background-color: #000;