From 6219e03c355a249638bade6937c2f67038f5917c Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 22 Jul 2026 17:05:04 -0400 Subject: [PATCH] fix(core): refresh file listing when the device queue drains File operations are pushed onto the device's command queue and run asynchronously, but FileBrowser only re-listed the directory when dev, path, or updateIndex changed -- and updateIndex was only ever bumped by a breadcrumb click. The listing stayed stale after an upload, delete, rename, or directory creation. Watch the device's running flag and queue length, and re-list on the busy -> idle transition so a multi-file delete refreshes once at the end rather than per queued command. The refresh is silent: it skips the loading flag so the current icons stay on screen instead of flashing the spinner, and keeps the old listing if the re-fetch fails. Co-Authored-By: Claude Opus 4.8 --- n-link-core/components/FileBrowser.vue | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/n-link-core/components/FileBrowser.vue b/n-link-core/components/FileBrowser.vue index 254f1dc..7046089 100644 --- a/n-link-core/components/FileBrowser.vue +++ b/n-link-core/components/FileBrowser.vue @@ -52,8 +52,10 @@ const selected = ref([]); const files = ref(null); const loading = ref(false); -async function loadFiles() { - loading.value = true; +// `silent` keeps the current listing on screen while re-fetching, so the +// refresh after a queued operation doesn't flash the loading spinner. +async function loadFiles(silent = false) { + if (!silent) loading.value = true; try { const contents = await devices.listDir(props.dev, path.value); files.value = contents.map((file) => ({ @@ -62,13 +64,27 @@ async function loadFiles() { })); } catch (e) { console.error(e); - files.value = null; + if (!silent) files.value = null; } finally { loading.value = false; } } -watch([() => props.dev, path, updateIndex], loadFiles, {immediate: true}); +watch([() => props.dev, path, updateIndex], () => loadFiles(), {immediate: true}); + +// File operations (upload, delete, rename, create directory) are pushed onto +// the device's command queue and run asynchronously, so the listing on screen +// is stale by the time they finish. Re-list once the queue drains. +const busy = computed(() => { + const device = devices.devices[props.dev]; + if (!device) return false; + return !!device.running || !!device.queue?.length; +}); + +watch(busy, (isBusy, wasBusy) => { + // FileView resets its own selection when the new listing arrives. + if (wasBusy && !isBusy) loadFiles(true); +}); watch(path, () => { selected.value = [];