From 80f4428f3733f44758d8b5d6ef4f04a80a190d15 Mon Sep 17 00:00:00 2001 From: cyb3rRunn3rr <308935272+cyb3rRunn3rr@users.noreply.github.com> Date: Tue, 29 Sep 2026 17:17:40 -0300 Subject: [PATCH 1/3] feat(webui): add file browser refresh action --- tests/webui_file_browser_state.test.cjs | 13 +++++ web_ui/static/js/app.js | 68 ++++++++++++++++++++----- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/tests/webui_file_browser_state.test.cjs b/tests/webui_file_browser_state.test.cjs index f37c65806..efb72d4b3 100644 --- a/tests/webui_file_browser_state.test.cjs +++ b/tests/webui_file_browser_state.test.cjs @@ -125,6 +125,19 @@ test("file-browser persistence, restoration, refresh and execution outcomes", as assert.deepEqual([...context.expandedFoldersRef.current], ["/data/a/b"]); context.expandedFoldersRef.current = context.getStoredExpandedFolders(); + const refreshStates = []; + const manualSearches = []; + context.fileBrowserRefreshing = false; + context.setFileBrowserRefreshing = (value) => refreshStates.push(value); + context.fileBrowserSearchQuery = { current: "new download" }; + context.handleFileBrowserSearch = (query) => manualSearches.push(query); + load("refreshFileBrowser"); + requests.length = 0; + await context.refreshFileBrowser(); + assert.deepEqual(refreshStates, [true, false]); + assert.deepEqual(requests, Object.keys(responses)); + assert.deepEqual(manualSearches, ["new download"]); + let delayResolve; let delay; context.setTimeout = (fn, ms) => { diff --git a/web_ui/static/js/app.js b/web_ui/static/js/app.js index cac1b0f74..349194de5 100644 --- a/web_ui/static/js/app.js +++ b/web_ui/static/js/app.js @@ -2163,6 +2163,7 @@ function AudionutsUAGUI() { useState(null); const [fileBrowserSearchLoading, setFileBrowserSearchLoading] = useState(false); + const [fileBrowserRefreshing, setFileBrowserRefreshing] = useState(false); const fileBrowserSearchTimer = useRef(null); const fileBrowserSearchQuery = useRef(""); const fileBrowserSearchId = useRef(0); @@ -3376,6 +3377,19 @@ function AudionutsUAGUI() { } }; + const refreshFileBrowser = async () => { + if (fileBrowserRefreshing) return; + setFileBrowserRefreshing(true); + try { + await loadBrowseRoots(); + if (fileBrowserSearchQuery.current) { + handleFileBrowserSearch(fileBrowserSearchQuery.current); + } + } finally { + setFileBrowserRefreshing(false); + } + }; + // Load description file browser roots const loadDescBrowseRoots = async () => { try { @@ -6131,12 +6145,27 @@ function AudionutsUAGUI() { ) : (
-

- - File Browser -

+
+

+ + File Browser +

+ +
-

- - File Browser -

+
+

+ + File Browser +

+ +
Date: Tue, 29 Sep 2026 17:37:35 -0300 Subject: [PATCH 2/3] fix(webui): await refreshed file search --- tests/webui_file_browser_state.test.cjs | 22 +++- web_ui/static/js/app.js | 132 ++++++++++++++---------- 2 files changed, 97 insertions(+), 57 deletions(-) diff --git a/tests/webui_file_browser_state.test.cjs b/tests/webui_file_browser_state.test.cjs index efb72d4b3..bd8f4a233 100644 --- a/tests/webui_file_browser_state.test.cjs +++ b/tests/webui_file_browser_state.test.cjs @@ -130,13 +130,28 @@ test("file-browser persistence, restoration, refresh and execution outcomes", as context.fileBrowserRefreshing = false; context.setFileBrowserRefreshing = (value) => refreshStates.push(value); context.fileBrowserSearchQuery = { current: "new download" }; - context.handleFileBrowserSearch = (query) => manualSearches.push(query); + let finishManualSearch; + let manualSearchStarted; + const searchStarted = new Promise((resolve) => { + manualSearchStarted = resolve; + }); + context.handleFileBrowserSearch = (query) => { + manualSearches.push(query); + manualSearchStarted(); + return new Promise((resolve) => { + finishManualSearch = resolve; + }); + }; load("refreshFileBrowser"); requests.length = 0; - await context.refreshFileBrowser(); - assert.deepEqual(refreshStates, [true, false]); + const manualRefresh = context.refreshFileBrowser(); + await searchStarted; + assert.deepEqual(refreshStates, [true]); assert.deepEqual(requests, Object.keys(responses)); assert.deepEqual(manualSearches, ["new download"]); + finishManualSearch(); + await manualRefresh; + assert.deepEqual(refreshStates, [true, false]); let delayResolve; let delay; @@ -168,6 +183,7 @@ test("file-browser persistence, restoration, refresh and execution outcomes", as assert.equal(searches.length, 1); context.fileBrowserSearchTimer = { current: null }; + context.fileBrowserSearchCompletion = { current: null }; context.fileBrowserSearchId = { current: 0 }; context.setFileBrowserSearch = () => {}; let searchLoading = false; diff --git a/web_ui/static/js/app.js b/web_ui/static/js/app.js index 349194de5..1c8c37770 100644 --- a/web_ui/static/js/app.js +++ b/web_ui/static/js/app.js @@ -2165,6 +2165,7 @@ function AudionutsUAGUI() { useState(false); const [fileBrowserRefreshing, setFileBrowserRefreshing] = useState(false); const fileBrowserSearchTimer = useRef(null); + const fileBrowserSearchCompletion = useRef(null); const fileBrowserSearchQuery = useRef(""); const fileBrowserSearchId = useRef(0); @@ -3377,13 +3378,14 @@ function AudionutsUAGUI() { } }; + /** Reload visible file-browser data without resetting navigation state. */ const refreshFileBrowser = async () => { if (fileBrowserRefreshing) return; setFileBrowserRefreshing(true); try { await loadBrowseRoots(); if (fileBrowserSearchQuery.current) { - handleFileBrowserSearch(fileBrowserSearchQuery.current); + await handleFileBrowserSearch(fileBrowserSearchQuery.current); } } finally { setFileBrowserRefreshing(false); @@ -3552,6 +3554,8 @@ function AudionutsUAGUI() { if (fileBrowserSearchTimer.current) { clearTimeout(fileBrowserSearchTimer.current); } + fileBrowserSearchCompletion.current?.(); + fileBrowserSearchCompletion.current = null; }; }, []); @@ -4093,68 +4097,88 @@ function AudionutsUAGUI() { // File Browser search const handleFileBrowserSearch = (value, signal) => { - if (signal?.aborted) return; - const searchId = ++fileBrowserSearchId.current; - setFileBrowserSearch(value); - const searchQuery = value.trim(); - fileBrowserSearchQuery.current = searchQuery; + if (signal?.aborted) return Promise.resolve(); + fileBrowserSearchCompletion.current?.(); if (fileBrowserSearchTimer.current) { clearTimeout(fileBrowserSearchTimer.current); } - if (!searchQuery) { - setFileBrowserSearchResults(null); - setFileBrowserSearchLoading(false); - return; - } - setFileBrowserSearchLoading(true); - const onAbort = () => { - if (fileBrowserSearchId.current === searchId) { - clearTimeout(fileBrowserSearchTimer.current); + return new Promise((resolve) => { + const searchId = ++fileBrowserSearchId.current; + let settled = false; + const settle = () => { + if (settled) return; + settled = true; + if (fileBrowserSearchCompletion.current === settle) { + fileBrowserSearchCompletion.current = null; + } + resolve(); + }; + fileBrowserSearchCompletion.current = settle; + setFileBrowserSearch(value); + const searchQuery = value.trim(); + fileBrowserSearchQuery.current = searchQuery; + if (!searchQuery) { + setFileBrowserSearchResults(null); setFileBrowserSearchLoading(false); + settle(); + return; } - }; - signal?.addEventListener("abort", onAbort, { once: true }); - fileBrowserSearchTimer.current = setTimeout(async () => { - if (signal?.aborted) return; - try { - const response = await apiFetch( - `${API_BASE}/browse_search?q=${encodeURIComponent(searchQuery)}`, - { signal }, - ); - if (signal?.aborted) return; - if (!response.ok) { - throw new Error(`Search request failed (${response.status})`); - } - const data = await response.json(); - if (signal?.aborted) return; - // Early return if the search has changed since this request - if (fileBrowserSearchId.current !== searchId) return; - if (data.success) { - setFileBrowserSearchResults(data); - } else { - setFileBrowserSearchResults({ - items: [], - query: searchQuery, - count: 0, - }); - } - } catch (error) { - if (signal?.aborted) return; - console.error("File browser search failed:", error); + setFileBrowserSearchLoading(true); + const onAbort = () => { if (fileBrowserSearchId.current === searchId) { - setFileBrowserSearchResults({ - items: [], - query: searchQuery, - count: 0, - }); + clearTimeout(fileBrowserSearchTimer.current); + setFileBrowserSearchLoading(false); } - } finally { signal?.removeEventListener("abort", onAbort); - if (!signal?.aborted && fileBrowserSearchId.current === searchId) { - setFileBrowserSearchLoading(false); + settle(); + }; + signal?.addEventListener("abort", onAbort, { once: true }); + fileBrowserSearchTimer.current = setTimeout(async () => { + if (signal?.aborted) { + settle(); + return; } - } - }, 300); //300ms debounce so we dont spam requests for every keystroke + try { + const response = await apiFetch( + `${API_BASE}/browse_search?q=${encodeURIComponent(searchQuery)}`, + { signal }, + ); + if (signal?.aborted) return; + if (!response.ok) { + throw new Error(`Search request failed (${response.status})`); + } + const data = await response.json(); + if (signal?.aborted) return; + // Early return if the search has changed since this request + if (fileBrowserSearchId.current !== searchId) return; + if (data.success) { + setFileBrowserSearchResults(data); + } else { + setFileBrowserSearchResults({ + items: [], + query: searchQuery, + count: 0, + }); + } + } catch (error) { + if (signal?.aborted) return; + console.error("File browser search failed:", error); + if (fileBrowserSearchId.current === searchId) { + setFileBrowserSearchResults({ + items: [], + query: searchQuery, + count: 0, + }); + } + } finally { + signal?.removeEventListener("abort", onAbort); + if (!signal?.aborted && fileBrowserSearchId.current === searchId) { + setFileBrowserSearchLoading(false); + } + settle(); + } + }, 300); //300ms debounce so we dont spam requests for every keystroke + }); }; const refreshFileBrowserAfterUpload = async (signal) => { From fc22596d48a850d300b8ce012d515620400ca9f0 Mon Sep 17 00:00:00 2001 From: wastaken7 <46319635+wastaken7@users.noreply.github.com> Date: Thu, 1 Oct 2026 11:54:59 -0300 Subject: [PATCH 3/3] Update web_ui/static/js/app.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- web_ui/static/js/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web_ui/static/js/app.js b/web_ui/static/js/app.js index 1c8c37770..74862b9c0 100644 --- a/web_ui/static/js/app.js +++ b/web_ui/static/js/app.js @@ -3383,6 +3383,7 @@ function AudionutsUAGUI() { if (fileBrowserRefreshing) return; setFileBrowserRefreshing(true); try { + setFileBrowserRestoring(true); await loadBrowseRoots(); if (fileBrowserSearchQuery.current) { await handleFileBrowserSearch(fileBrowserSearchQuery.current);