Skip to content

Commit a1d1994

Browse files
committed
Pane state: skip FilePane MCP sync on Network volume
`FilePane.syncPaneStateToMcp` was pushing pane state even when the pane is on the virtual Network volume, where `NetworkMountView` / `NetworkBrowser` is the actual content owner. The push carried stale data: `files: []`, the old fixture `path`, and the leftover `totalFiles`/`loadedRange` from before the volume switch. Because `NetworkBrowser`'s own pane-state push (with the 14 host entries as `files`) raced FilePane's, and FilePane's typically pushed last, `cmdr://state` ended up frozen with empty `files`. That's why three SMB tests (`guest/auth host shows share count`, `50-share host shows correct share count`) consistently hit their 30s `pollUntil` deadline on Linux: the state YAML never contained the host entries NetworkBrowser was correctly putting there. The tests had no `expect(result).toBe(true)` so the silent timeout looked like a pass — three dead assertions hiding a real bug. Fix: - Short-circuit `syncPaneStateToMcp` when `isNetworkView`. `NetworkBrowser` owns the push for that view (path `smb://`, files = hosts). - Add the missing `expect(result).toBe(true)` to the three SMB tests so a real silent failure here fails the suite instead of disguising itself as a 30s pass. After the fix the three tests drop from ~30.4s each (deadline hit) to ~400-800ms (actual pass). MTP is unaffected (its file list comes from a normal `list_directory`, FilePane's sync is the right source of truth there).
1 parent 792bcff commit a1d1994

2 files changed

Lines changed: 22 additions & 3 deletions

File tree

apps/desktop/src/lib/file-explorer/pane/FilePane.svelte

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,8 +870,24 @@
870870
/**
871871
* Sync pane state to Rust for MCP context tools.
872872
* Called when files load, cursor position changes, or view mode changes.
873+
*
874+
* Skipped entirely on the Network virtual volume: `NetworkBrowser`
875+
* (mounted inside `NetworkMountView`) owns the pane-state push for that
876+
* view and writes the host list as `files`. Without this guard, FilePane's
877+
* own sync races NetworkBrowser's and overwrites it with stale local-pane
878+
* data (empty `files`, the old fixture `path`, and a leftover
879+
* `totalFiles`/`loadedRange`). That clobber is why three SMB tests
880+
* (`guest host shows share count`, `auth host shows share count`,
881+
* `50-share host shows correct share count`) used to time out at the 30s
882+
* pollUntil deadline — `cmdr://state` never contained the host entries
883+
* NetworkBrowser had just pushed.
884+
*
885+
* MTP volumes are not affected: their file list comes from a normal
886+
* `list_directory` against the volume, so FilePane's sync is the right
887+
* source of truth there.
873888
*/
874889
async function syncPaneStateToMcp() {
890+
if (isNetworkView) return
875891
try {
876892
const files = await buildMcpFileList()
877893
const effectiveTotal = hasParent ? totalCount + 1 : totalCount

apps/desktop/test/e2e-playwright/smb.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,15 @@ describeSmb('SMB host discovery', () => {
204204
await mcpSelectVolume('left', 'Network')
205205

206206
// Wait for the guest host to appear and its shares to be prefetched
207-
await pollUntil(
207+
const result = await pollUntil(
208208
tauriPage,
209209
async () => {
210210
const state = await mcpReadResource('cmdr://state')
211211
return state.includes('SMB Test (Guest)') && state.includes('shares=1')
212212
},
213213
30000,
214214
)
215+
expect(result).toBe(true)
215216
})
216217
})
217218

@@ -370,14 +371,15 @@ describeSmb('SMB authentication', () => {
370371
await mcpSelectVolume('left', 'Network')
371372

372373
// Wait for the auth host to appear and its shares to be prefetched
373-
await pollUntil(
374+
const result = await pollUntil(
374375
tauriPage,
375376
async () => {
376377
const state = await mcpReadResource('cmdr://state')
377378
return state.includes('SMB Test (Auth)') && state.includes('shares=1')
378379
},
379380
30000,
380381
)
382+
expect(result).toBe(true)
381383
})
382384

383385
test('listing shares with valid credentials returns private share', async ({ tauriPage }) => {
@@ -437,14 +439,15 @@ describeSmb('SMB 50-share server', () => {
437439
await mcpSelectVolume('left', 'Network')
438440

439441
// Wait for the 50-shares host to appear and prefetch shares
440-
await pollUntil(
442+
const result = await pollUntil(
441443
tauriPage,
442444
async () => {
443445
const state = await mcpReadResource('cmdr://state')
444446
return state.includes('SMB Test (50 Shares)') && state.includes('shares=50')
445447
},
446448
30000,
447449
)
450+
expect(result).toBe(true)
448451
})
449452
})
450453

0 commit comments

Comments
 (0)