|
22 | 22 | DEFAULT_VOLUME_ID, |
23 | 23 | type UnlistenFn, |
24 | 24 | } from '$lib/tauri-commands' |
25 | | - import type { VolumeInfo, SortColumn, SortOrder } from './types' |
| 25 | + import type { VolumeInfo, SortColumn, SortOrder, NetworkHost } from './types' |
26 | 26 | import { defaultSortOrders, DEFAULT_SORT_BY } from './types' |
27 | 27 | import { ensureFontMetricsLoaded } from '$lib/font-metrics' |
28 | 28 | import { |
29 | 29 | createHistory, |
30 | 30 | push, |
| 31 | + pushPath, |
31 | 32 | back, |
32 | 33 | forward, |
33 | | - getCurrentPath, |
| 34 | + getCurrentEntry, |
34 | 35 | canGoBack, |
35 | 36 | canGoForward, |
36 | 37 | type NavigationHistory, |
|
64 | 65 | let unlistenNavigation: UnlistenFn | undefined |
65 | 66 |
|
66 | 67 | // Navigation history for each pane (per-pane, session-only) |
67 | | - let leftHistory = $state<NavigationHistory>(createHistory('~')) |
68 | | - let rightHistory = $state<NavigationHistory>(createHistory('~')) |
| 68 | + // Initialize with default volume - will be updated on mount with actual state |
| 69 | + let leftHistory = $state<NavigationHistory>(createHistory(DEFAULT_VOLUME_ID, '~')) |
| 70 | + let rightHistory = $state<NavigationHistory>(createHistory(DEFAULT_VOLUME_ID, '~')) |
69 | 71 |
|
70 | 72 | // Derived volume paths - handle 'network' virtual volume specially |
71 | 73 | const leftVolumePath = $derived( |
|
77 | 79 |
|
78 | 80 | function handleLeftPathChange(path: string) { |
79 | 81 | leftPath = path |
80 | | - leftHistory = push(leftHistory, path) |
| 82 | + // Use pushPath to keep current volumeId (directory navigation within volume) |
| 83 | + leftHistory = pushPath(leftHistory, path) |
81 | 84 | void saveAppStatus({ leftPath: path }) |
82 | 85 | void saveLastUsedPathForVolume(leftVolumeId, path) |
83 | 86 | // Re-focus to maintain keyboard handling after navigation |
|
86 | 89 |
|
87 | 90 | function handleRightPathChange(path: string) { |
88 | 91 | rightPath = path |
89 | | - rightHistory = push(rightHistory, path) |
| 92 | + // Use pushPath to keep current volumeId (directory navigation within volume) |
| 93 | + rightHistory = pushPath(rightHistory, path) |
90 | 94 | void saveAppStatus({ rightPath: path }) |
91 | 95 | void saveLastUsedPathForVolume(rightVolumeId, path) |
92 | 96 | // Re-focus to maintain keyboard handling after navigation |
93 | 97 | containerElement?.focus() |
94 | 98 | } |
95 | 99 |
|
| 100 | + // Handle network host selection changes (for history tracking) |
| 101 | + function handleLeftNetworkHostChange(host: NetworkHost | null) { |
| 102 | + // Push to history with network host state |
| 103 | + leftHistory = push(leftHistory, { |
| 104 | + volumeId: 'network', |
| 105 | + path: 'smb://', |
| 106 | + networkHost: host ?? undefined, |
| 107 | + }) |
| 108 | + containerElement?.focus() |
| 109 | + } |
| 110 | +
|
| 111 | + function handleRightNetworkHostChange(host: NetworkHost | null) { |
| 112 | + // Push to history with network host state |
| 113 | + rightHistory = push(rightHistory, { |
| 114 | + volumeId: 'network', |
| 115 | + path: 'smb://', |
| 116 | + networkHost: host ?? undefined, |
| 117 | + }) |
| 118 | + containerElement?.focus() |
| 119 | + } |
| 120 | +
|
96 | 121 | /** |
97 | 122 | * Handles sorting column click for left pane. |
98 | 123 | * If clicking the same column, toggles order. Otherwise, switches to new column with its default order. |
|
199 | 224 | leftVolumeId = volumeId |
200 | 225 | leftPath = pathToNavigate |
201 | 226 |
|
| 227 | + // Push volume change to history (this enables back/forward across volumes) |
| 228 | + leftHistory = push(leftHistory, { volumeId, path: pathToNavigate }) |
| 229 | +
|
202 | 230 | // Focus the left pane after successful volume selection |
203 | 231 | focusedPane = 'left' |
204 | 232 | void saveAppStatus({ leftVolumeId: volumeId, leftPath: pathToNavigate, focusedPane: 'left' }) |
|
222 | 250 | rightVolumeId = volumeId |
223 | 251 | rightPath = pathToNavigate |
224 | 252 |
|
| 253 | + // Push volume change to history (this enables back/forward across volumes) |
| 254 | + rightHistory = push(rightHistory, { volumeId, path: pathToNavigate }) |
| 255 | +
|
225 | 256 | // Focus the right pane after successful volume selection |
226 | 257 | focusedPane = 'right' |
227 | 258 | void saveAppStatus({ rightVolumeId: volumeId, rightPath: pathToNavigate, focusedPane: 'right' }) |
|
387 | 418 | rightVolumeId = rightContaining?.id ?? defaultId |
388 | 419 | } |
389 | 420 |
|
390 | | - // Initialize history with loaded paths |
391 | | - leftHistory = createHistory(status.leftPath) |
392 | | - rightHistory = createHistory(status.rightPath) |
| 421 | + // Initialize history with loaded paths and their volumes |
| 422 | + leftHistory = createHistory(leftVolumeId, status.leftPath) |
| 423 | + rightHistory = createHistory(rightVolumeId, status.rightPath) |
393 | 424 |
|
394 | 425 | initialized = true |
395 | 426 |
|
|
485 | 516 | } |
486 | 517 |
|
487 | 518 | /** |
488 | | - * Updates pane state after navigating back/forward (doesn't push to history). |
| 519 | + * Updates pane state after navigating back/forward (restores full state from history entry). |
| 520 | + * This includes both path AND volumeId changes - enabling back/forward across volumes. |
489 | 521 | */ |
490 | 522 | function updatePaneAfterHistoryNavigation(isLeft: boolean, newHistory: NavigationHistory, targetPath: string) { |
| 523 | + const entry = getCurrentEntry(newHistory) |
| 524 | + const paneRef = isLeft ? leftPaneRef : rightPaneRef |
| 525 | +
|
491 | 526 | if (isLeft) { |
492 | 527 | leftHistory = newHistory |
493 | 528 | leftPath = targetPath |
494 | | - void saveAppStatus({ leftPath: targetPath }) |
495 | | - void saveLastUsedPathForVolume(leftVolumeId, targetPath) |
| 529 | + // Restore volume context if it changed |
| 530 | + if (entry.volumeId !== leftVolumeId) { |
| 531 | + leftVolumeId = entry.volumeId |
| 532 | + void saveAppStatus({ leftVolumeId: entry.volumeId, leftPath: targetPath }) |
| 533 | + } else { |
| 534 | + void saveAppStatus({ leftPath: targetPath }) |
| 535 | + } |
| 536 | + void saveLastUsedPathForVolume(entry.volumeId, targetPath) |
496 | 537 | } else { |
497 | 538 | rightHistory = newHistory |
498 | 539 | rightPath = targetPath |
499 | | - void saveAppStatus({ rightPath: targetPath }) |
500 | | - void saveLastUsedPathForVolume(rightVolumeId, targetPath) |
| 540 | + // Restore volume context if it changed |
| 541 | + if (entry.volumeId !== rightVolumeId) { |
| 542 | + rightVolumeId = entry.volumeId |
| 543 | + void saveAppStatus({ rightVolumeId: entry.volumeId, rightPath: targetPath }) |
| 544 | + } else { |
| 545 | + void saveAppStatus({ rightPath: targetPath }) |
| 546 | + } |
| 547 | + void saveLastUsedPathForVolume(entry.volumeId, targetPath) |
| 548 | + } |
| 549 | +
|
| 550 | + // Restore network host state if navigating within network volume |
| 551 | + if (entry.volumeId === 'network') { |
| 552 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-call |
| 553 | + paneRef?.setNetworkHost?.(entry.networkHost ?? null) |
501 | 554 | } |
| 555 | +
|
502 | 556 | containerElement?.focus() |
503 | 557 | } |
504 | 558 |
|
|
526 | 580 | return |
527 | 581 | } |
528 | 582 |
|
529 | | - const targetPath = await resolveValidPath(getCurrentPath(newHistory)) |
530 | | - if (targetPath !== null) { |
531 | | - updatePaneAfterHistoryNavigation(isLeft, newHistory, targetPath) |
| 583 | + // Get the target entry (includes volumeId, path, and network state) |
| 584 | + const targetEntry = getCurrentEntry(newHistory) |
| 585 | +
|
| 586 | + // For network virtual volume, path resolution doesn't apply |
| 587 | + // (network browser handles its own state) |
| 588 | + if (targetEntry.volumeId === 'network') { |
| 589 | + updatePaneAfterHistoryNavigation(isLeft, newHistory, targetEntry.path) |
| 590 | + return |
| 591 | + } |
| 592 | +
|
| 593 | + // For real volumes, resolve path to handle deleted folders |
| 594 | + const resolvedPath = await resolveValidPath(targetEntry.path) |
| 595 | + if (resolvedPath !== null) { |
| 596 | + updatePaneAfterHistoryNavigation(isLeft, newHistory, resolvedPath) |
532 | 597 | } |
533 | 598 | } |
534 | 599 |
|
|
573 | 638 | onVolumeChange={handleLeftVolumeChange} |
574 | 639 | onRequestFocus={handleLeftFocus} |
575 | 640 | onSortChange={handleLeftSortChange} |
| 641 | + onNetworkHostChange={handleLeftNetworkHostChange} |
576 | 642 | /> |
577 | 643 | <FilePane |
578 | 644 | bind:this={rightPaneRef} |
|
588 | 654 | onVolumeChange={handleRightVolumeChange} |
589 | 655 | onRequestFocus={handleRightFocus} |
590 | 656 | onSortChange={handleRightSortChange} |
| 657 | + onNetworkHostChange={handleRightNetworkHostChange} |
591 | 658 | /> |
592 | 659 | {:else} |
593 | 660 | <LoadingIcon /> |
|
0 commit comments