Skip to content

Commit 582cfba

Browse files
committed
Bugfix: Show "/" not a storage id in MTP-root tab titles
- The pane tab derived its title from the path basename, so at an MTP storage root the title surfaced the raw storage id (like `65537` = 0x10001 Internal Storage) instead of a sensible label. - New `deriveTabLabel(path)` (`tabs/tab-label.ts`) special-cases the `mtp://` scheme: it derives the label from the within-storage path via `getMtpDisplayPath`, so a storage root shows "/" (matching the breadcrumb and the local filesystem root) and a subfolder shows its folder name. Mounted-volume roots (`/Volumes/USB`) keep their basename — only MTP is special-cased. - Same disease the transfer-dialog header fix (`deriveTransferLabel`) already addressed; the tab is a separate consumer with the "/" convention the owner expects, so it gets its own helper rather than the dialog's display-name-at-root rule.
1 parent ede1a5d commit 582cfba

5 files changed

Lines changed: 66 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ The format is based on [keep a changelog](https://keepachangelog.com/en/1.1.0/),
5959
([f4a8b1cb](https://github.com/vdavid/cmdr/commit/f4a8b1cb))
6060
- Show the volume name instead of a raw storage id (like "65538") in the transfer dialog header
6161
([f4a8b1cb](https://github.com/vdavid/cmdr/commit/f4a8b1cb))
62+
- Show "/" instead of a raw storage id (like "65537") in the tab title at a phone or camera storage root
6263
- Fix file viewer settings (word wrap, text size, binary warning) silently resetting every session
6364
([51e127aa](https://github.com/vdavid/cmdr/commit/51e127aa))
6465
- Make the title bar draggable while a dialog is open, and in the file viewer window

apps/desktop/src/lib/file-explorer/tabs/CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Per-pane tab system for the dual-pane file explorer. Each pane side (left/right)
99
pin). Max 10 tabs per pane.
1010
- `TabBar.svelte`: Tab bar UI component. Always visible, Chrome-style shrinking tabs, pin icons, close buttons, context
1111
menu.
12+
- `tab-label.ts`: `deriveTabLabel(path)` — the tab title. Basename via `getFolderName` for normal paths, but for an MTP
13+
path (`mtp://…`) it derives from the within-storage path (`getMtpDisplayPath`) so the storage root shows "/" instead
14+
of the raw storage id (`65537`). Mounted-volume roots (`/Volumes/USB`) keep their basename; only the MTP scheme is
15+
special-cased. Pinned by `tab-label.test.ts`.
1216
- `tab-state-manager.test.ts`: Unit tests for state manager
1317

1418
## Key decisions

apps/desktop/src/lib/file-explorer/tabs/TabBar.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import type { TabState, TabId } from './tab-types'
33
import { tooltip } from '$lib/tooltip/tooltip'
4-
import { getFolderName } from '$lib/file-operations/transfer/transfer-dialog-utils'
4+
import { deriveTabLabel } from './tab-label'
55
import { getVolumes } from '$lib/stores/volume-store.svelte'
66
77
interface Props {
@@ -132,7 +132,7 @@
132132
</span>
133133
{/if}
134134
<span class="tab-label">
135-
{getFolderName(tab.path)}
135+
{deriveTabLabel(tab.path)}
136136
</span>
137137
{#if !isSingleTab}
138138
<span
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { deriveTabLabel } from './tab-label'
3+
4+
describe('deriveTabLabel', () => {
5+
it('shows "/" at an MTP storage root instead of the raw storage id', () => {
6+
// At the MTP storage root the last path segment is the raw storage id
7+
// (here 65537 = 0x10001 for Internal Storage), which used to surface as
8+
// the tab title "65537". A storage root carries no user-meaningful
9+
// basename, so the tab shows "/" — the same thing a local filesystem
10+
// root shows.
11+
expect(deriveTabLabel('mtp://0-5/65537')).toBe('/')
12+
})
13+
14+
it('shows the folder name for an MTP subfolder', () => {
15+
expect(deriveTabLabel('mtp://0-5/65537/DCIM/Camera')).toBe('Camera')
16+
expect(deriveTabLabel('mtp://0-5/65537/DCIM')).toBe('DCIM')
17+
})
18+
19+
it('shows "/" for the local filesystem root (convention pinned)', () => {
20+
expect(deriveTabLabel('/')).toBe('/')
21+
})
22+
23+
it('shows the basename for a local subfolder (convention pinned)', () => {
24+
expect(deriveTabLabel('/Users/john/Documents')).toBe('Documents')
25+
})
26+
27+
it('shows the volume folder name for a mounted-volume root (convention pinned)', () => {
28+
// A mounted volume root like /Volumes/USB keeps its basename ("USB"),
29+
// unchanged from the local convention — we only special-case the MTP
30+
// storage root, not every volume root.
31+
expect(deriveTabLabel('/Volumes/USB')).toBe('USB')
32+
})
33+
})
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { getFolderName } from '$lib/file-operations/transfer/transfer-dialog-utils'
2+
import { getMtpDisplayPath } from '$lib/mtp'
3+
4+
/**
5+
* Derives the user-facing label for a tab from its path.
6+
*
7+
* Normally the basename is the right thing to show ("Documents" for
8+
* `/Users/john/Documents`, "/" for the local filesystem root). But an MTP path
9+
* (`mtp://{deviceId}/{storageId}/inner/path`) puts the raw storage id as the
10+
* last segment at the storage root, which surfaced as the tab title "65537"
11+
* (0x10001 = Internal Storage). For MTP paths we derive the label from the
12+
* inner (within-storage) path instead: "/" at the storage root, the inner
13+
* folder basename below it — matching what the breadcrumb already shows.
14+
*
15+
* We special-case ONLY the MTP scheme. Every other path (including mounted
16+
* volume roots like `/Volumes/USB`, which keep their basename "USB") flows
17+
* through `getFolderName` unchanged.
18+
*/
19+
export function deriveTabLabel(path: string): string {
20+
if (path.startsWith('mtp://')) {
21+
// `getMtpDisplayPath` returns "/" at the storage root and `/DCIM/Camera`
22+
// for a subfolder; its basename is the tab label.
23+
return getFolderName(getMtpDisplayPath(path))
24+
}
25+
return getFolderName(path)
26+
}

0 commit comments

Comments
 (0)