|
501 | 501 | volumePath: string, |
502 | 502 | targetPath: string, |
503 | 503 | ) { |
504 | | - const oldPath = getPanePath(pane) |
505 | | - void saveLastUsedPathForVolume(getPaneVolumeId(pane), oldPath) |
| 504 | + const mgr = getTabMgr(pane) |
| 505 | + const activeTab = getActiveTab(mgr) |
| 506 | + const oldPath = activeTab.path |
| 507 | + void saveLastUsedPathForVolume(activeTab.volumeId, oldPath) |
506 | 508 |
|
507 | 509 | if (!volumes.find((v) => v.id === volumeId)) { |
508 | 510 | volumes = (await listVolumes()).data |
509 | 511 | } |
510 | 512 |
|
511 | | - // Immediately navigate to the target path (optimistic — shows spinner instantly) |
| 513 | + // Pinned tab: open a new tab with the target volume instead of navigating in-place |
| 514 | + if (activeTab.pinned && (volumeId !== activeTab.volumeId || targetPath !== activeTab.path)) { |
| 515 | + if (mgr.tabs.length >= MAX_TABS_PER_PANE) { |
| 516 | + addToast('Tab limit reached') |
| 517 | + } else { |
| 518 | + const newTab: TabState = { |
| 519 | + id: crypto.randomUUID(), |
| 520 | + path: targetPath, |
| 521 | + volumeId, |
| 522 | + history: createHistory(volumeId, targetPath), |
| 523 | + sortBy: activeTab.sortBy, |
| 524 | + sortOrder: activeTab.sortOrder, |
| 525 | + viewMode: activeTab.viewMode, |
| 526 | + pinned: false, |
| 527 | + cursorFilename: null, |
| 528 | + unreachable: null, |
| 529 | + } |
| 530 | +
|
| 531 | + const activeIndex = mgr.tabs.findIndex((t) => t.id === activeTab.id) |
| 532 | + mgr.tabs.splice(activeIndex + 1, 0, newTab) |
| 533 | + mgr.activeTabId = newTab.id |
| 534 | +
|
| 535 | + saveTabsForPaneSide(pane) |
| 536 | + focusedPane = pane |
| 537 | + void cancelNavPriority(oldPath) |
| 538 | + void prioritizeDir(targetPath, 'current_dir') |
| 539 | + saveAppStatus({ |
| 540 | + [paneKey(pane, 'volumeId')]: volumeId, |
| 541 | + [paneKey(pane, 'path')]: targetPath, |
| 542 | + focusedPane: pane, |
| 543 | + }) |
| 544 | + containerElement?.focus() |
| 545 | + // Background path correction applies to the new active tab |
| 546 | + applyVolumePathCorrection(pane, volumeId, volumePath, targetPath) |
| 547 | + return |
| 548 | + } |
| 549 | + } |
| 550 | +
|
| 551 | + // In-place navigation (normal flow or pinned tab at cap) |
512 | 552 | setPaneVolumeId(pane, volumeId) |
513 | 553 | setPanePath(pane, targetPath) |
514 | 554 | setPaneHistory(pane, push(getPaneHistory(pane), { volumeId, path: targetPath })) |
|
523 | 563 | }) |
524 | 564 | saveTabsForPaneSide(pane) |
525 | 565 |
|
526 | | - // Resolve the "best" path in the background; correct if needed. |
527 | | - // Generation counter guards against stale corrections when the user navigates away. |
| 566 | + applyVolumePathCorrection(pane, volumeId, volumePath, targetPath) |
| 567 | + } |
| 568 | +
|
| 569 | + /** Resolves the "best" path for a volume change in the background; corrects the active tab if needed. */ |
| 570 | + function applyVolumePathCorrection( |
| 571 | + pane: 'left' | 'right', |
| 572 | + volumeId: string, |
| 573 | + volumePath: string, |
| 574 | + targetPath: string, |
| 575 | + ) { |
528 | 576 | const generation = ++volumeChangeGeneration |
529 | 577 | const other = otherPane(pane) |
530 | 578 | void determineNavigationPath(volumeId, volumePath, targetPath, { |
|
0 commit comments