diff --git a/src/types/host-sidebar-preferences.ts b/src/types/host-sidebar-preferences.ts index 3013559b2..3b455c5dd 100644 --- a/src/types/host-sidebar-preferences.ts +++ b/src/types/host-sidebar-preferences.ts @@ -49,6 +49,8 @@ export interface HostSidebarDisplayPreferences { statusColorScheme: StatusColorScheme; /** When true, a host row needs a double click to launch its session. */ openOnDoubleClick: boolean; + /** When false, nested folders hide the parent-path breadcrumb before their name. */ + showFolderPaths: boolean; } export interface HostSidebarPreferences { @@ -130,6 +132,7 @@ export function defaultHostSidebarPreferences(): HostSidebarPreferences { trayTrigger: "always", statusColorScheme: "accent", openOnDoubleClick: false, + showFolderPaths: true, }, }; } @@ -203,6 +206,10 @@ export function sanitizeHostSidebarPreferences( typeof displayObj.openOnDoubleClick === "boolean" ? displayObj.openOnDoubleClick : defaults.display.openOnDoubleClick, + showFolderPaths: + typeof displayObj.showFolderPaths === "boolean" + ? displayObj.showFolderPaths + : defaults.display.showFolderPaths, }; return { diff --git a/src/ui/locales/en.json b/src/ui/locales/en.json index dbb09e03f..5acff02c5 100644 --- a/src/ui/locales/en.json +++ b/src/ui/locales/en.json @@ -1404,6 +1404,8 @@ "actionsClick": "Click to expand/collapse", "openOnDoubleClick": "Open on double click", "openOnDoubleClickDesc": "Require a double click to open a host session instead of a single click", + "showFolderPaths": "Show folder paths", + "showFolderPathsDesc": "Show the parent folder path above nested subfolder names. Subfolders stay indented either way.", "deletedHost": "Deleted {{name}}", "copiedToClipboard": "Copied to clipboard", "terminalUrlCopied": "Terminal URL copied", diff --git a/src/ui/sidebar/CustomizeSidebarPanel.tsx b/src/ui/sidebar/CustomizeSidebarPanel.tsx index 56bad5c59..a1c5c8961 100644 --- a/src/ui/sidebar/CustomizeSidebarPanel.tsx +++ b/src/ui/sidebar/CustomizeSidebarPanel.tsx @@ -97,6 +97,20 @@ export function CustomizeSidebarPanel({ } /> + + + update((prev) => ({ + ...prev, + display: { ...prev.display, showFolderPaths: v }, + })) + } + /> +
diff --git a/src/ui/sidebar/HostsPanel.tsx b/src/ui/sidebar/HostsPanel.tsx index 5508d5dfe..5c49685f6 100644 --- a/src/ui/sidebar/HostsPanel.tsx +++ b/src/ui/sidebar/HostsPanel.tsx @@ -1076,6 +1076,7 @@ export function HostsPanel({ trayTrigger={sidebarPrefs.display.trayTrigger} showTags={sidebarPrefs.display.showTags} openOnDoubleClick={sidebarPrefs.display.openOnDoubleClick} + showFolderPaths={sidebarPrefs.display.showFolderPaths} />
)} diff --git a/src/ui/sidebar/tree/FolderItem/FolderItem.tsx b/src/ui/sidebar/tree/FolderItem/FolderItem.tsx index 55d55701c..1b52f010c 100644 --- a/src/ui/sidebar/tree/FolderItem/FolderItem.tsx +++ b/src/ui/sidebar/tree/FolderItem/FolderItem.tsx @@ -68,6 +68,7 @@ export function FolderItem({ trayTrigger = "hover", showTags = true, openOnDoubleClick = false, + showFolderPaths = true, arrangeMode = false, isDragging = false, onReorderDrop, @@ -115,6 +116,8 @@ export function FolderItem({ trayTrigger?: HostTrayTrigger; showTags?: boolean; openOnDoubleClick?: boolean; + /** When false, nested folders hide the parent-path breadcrumb before their name. */ + showFolderPaths?: boolean; /** When true (rearranging unlocked), the header can be dragged and its * top/bottom edges become reorder drop zones. The middle still accepts * hosts dropped into the folder. */ @@ -152,7 +155,8 @@ export function FolderItem({ // Nested folders show their parent path as a muted breadcrumb so depth stays // legible even when a folder is reached via search auto-expand rather than // by manually opening every ancestor. - const pathSegments = isGroup ? [] : folderPath.split(" / "); + const pathSegments = + isGroup || !showFolderPaths ? [] : folderPath.split(" / "); const breadcrumb = pathSegments.length > 1 ? pathSegments.slice(0, -1).join(" / ") : null; const folderHosts = collectAllHosts(folder.children); @@ -337,6 +341,7 @@ export function FolderItem({ trayTrigger={trayTrigger} showTags={showTags} openOnDoubleClick={openOnDoubleClick} + showFolderPaths={showFolderPaths} arrangeMode={arrangeMode} onReorderDrop={onReorderDrop} onFolderDragStart={onFolderDragStart} diff --git a/src/ui/sidebar/tree/SidebarTree.tsx b/src/ui/sidebar/tree/SidebarTree.tsx index b8c5cc5a8..59931a4ab 100644 --- a/src/ui/sidebar/tree/SidebarTree.tsx +++ b/src/ui/sidebar/tree/SidebarTree.tsx @@ -87,6 +87,7 @@ export function SidebarTree({ trayTrigger = "hover", showTags = true, openOnDoubleClick = false, + showFolderPaths = true, }: { children: (Host | HostFolder)[]; onOpenTab: ( @@ -108,6 +109,8 @@ export function SidebarTree({ trayTrigger?: HostTrayTrigger; showTags?: boolean; openOnDoubleClick?: boolean; + /** When false, nested folders hide the parent-path breadcrumb before their name. */ + showFolderPaths?: boolean; }) { const { t } = useTranslation(); // Knobs with no other owner come straight from the interface preset; the @@ -996,6 +999,7 @@ export function SidebarTree({ trayTrigger={trayTrigger} showTags={showTags} openOnDoubleClick={openOnDoubleClick} + showFolderPaths={showFolderPaths} arrangeMode={arrangeMode} isDragging={ draggedReorderKey === `folder:${item.path ?? item.name}` diff --git a/src/ui/tests/types/host-sidebar-preferences.test.ts b/src/ui/tests/types/host-sidebar-preferences.test.ts index 6e457ae44..ca468e9fc 100644 --- a/src/ui/tests/types/host-sidebar-preferences.test.ts +++ b/src/ui/tests/types/host-sidebar-preferences.test.ts @@ -26,6 +26,7 @@ describe("defaultHostSidebarPreferences", () => { trayTrigger: "always", statusColorScheme: "accent", openOnDoubleClick: false, + showFolderPaths: true, }, }); }); @@ -63,6 +64,7 @@ describe("sanitizeHostSidebarPreferences", () => { trayTrigger: "click", statusColorScheme: "status", openOnDoubleClick: true, + showFolderPaths: false, }, }; expect(sanitizeHostSidebarPreferences(valid)).toEqual(valid); @@ -78,6 +80,16 @@ describe("sanitizeHostSidebarPreferences", () => { ).toBe(false); }); + it("defaults showFolderPaths to true when missing or not a boolean", () => { + expect( + sanitizeHostSidebarPreferences({ display: {} }).display.showFolderPaths, + ).toBe(true); + expect( + sanitizeHostSidebarPreferences({ display: { showFolderPaths: "no" } }) + .display.showFolderPaths, + ).toBe(true); + }); + it("falls back to defaults for invalid enum values", () => { const result = sanitizeHostSidebarPreferences({ sort: { key: "not-a-real-key", pinnedFirst: "yes" },