feat(sidebar): show host name on every row in multi-host setups (#1775)
The host name previously appeared only in project grouping mode, and only for projects shared across hosts. It now shows on every workspace row in both project and status grouping modes whenever the sidebar spans more than one host, so you can always tell which machine a workspace lives on. Status rows read "Project · Host". Single-host setups are unchanged.
This commit is contained in:
@@ -64,6 +64,7 @@ import {
|
|||||||
parseHostWorkspaceRouteFromPathname,
|
parseHostWorkspaceRouteFromPathname,
|
||||||
} from "@/utils/host-routes";
|
} from "@/utils/host-routes";
|
||||||
import {
|
import {
|
||||||
|
shouldShowSidebarHostLabels,
|
||||||
useSidebarWorkspaceEntry,
|
useSidebarWorkspaceEntry,
|
||||||
type SidebarProjectEntry,
|
type SidebarProjectEntry,
|
||||||
type SidebarStatusWorkspacePlacement,
|
type SidebarStatusWorkspacePlacement,
|
||||||
@@ -1879,6 +1880,7 @@ function ProjectBlock({
|
|||||||
creatingWorkspaceIds,
|
creatingWorkspaceIds,
|
||||||
activeWorkspaceSelection,
|
activeWorkspaceSelection,
|
||||||
hostLabelByServerId,
|
hostLabelByServerId,
|
||||||
|
showHostLabels,
|
||||||
}: {
|
}: {
|
||||||
project: SidebarProjectEntry;
|
project: SidebarProjectEntry;
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
@@ -1899,6 +1901,7 @@ function ProjectBlock({
|
|||||||
creatingWorkspaceIds: ReadonlySet<string>;
|
creatingWorkspaceIds: ReadonlySet<string>;
|
||||||
activeWorkspaceSelection: ActiveWorkspaceSelection | null;
|
activeWorkspaceSelection: ActiveWorkspaceSelection | null;
|
||||||
hostLabelByServerId: ReadonlyMap<string, string>;
|
hostLabelByServerId: ReadonlyMap<string, string>;
|
||||||
|
showHostLabels: boolean;
|
||||||
}) {
|
}) {
|
||||||
const rowModel = useMemo(
|
const rowModel = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -1928,9 +1931,7 @@ function ProjectBlock({
|
|||||||
<MemoWorkspaceRowItem
|
<MemoWorkspaceRowItem
|
||||||
workspace={item}
|
workspace={item}
|
||||||
subtitle={
|
subtitle={
|
||||||
project.hosts.length > 1
|
showHostLabels ? (hostLabelByServerId.get(item.serverId) ?? item.serverId) : null
|
||||||
? (hostLabelByServerId.get(item.serverId) ?? item.serverId)
|
|
||||||
: null
|
|
||||||
}
|
}
|
||||||
shortcutNumber={shortcutIndexByWorkspaceKey.get(item.workspaceKey) ?? null}
|
shortcutNumber={shortcutIndexByWorkspaceKey.get(item.workspaceKey) ?? null}
|
||||||
showShortcutBadge={showShortcutBadges}
|
showShortcutBadge={showShortcutBadges}
|
||||||
@@ -1947,7 +1948,7 @@ function ProjectBlock({
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
project.projectKind,
|
project.projectKind,
|
||||||
project.hosts.length,
|
showHostLabels,
|
||||||
activeWorkspaceSelection,
|
activeWorkspaceSelection,
|
||||||
creatingWorkspaceIds,
|
creatingWorkspaceIds,
|
||||||
hostLabelByServerId,
|
hostLabelByServerId,
|
||||||
@@ -2115,6 +2116,7 @@ function areProjectBlockPropsEqual(previous: ProjectBlockProps, next: ProjectBlo
|
|||||||
previous.showShortcutBadges === next.showShortcutBadges &&
|
previous.showShortcutBadges === next.showShortcutBadges &&
|
||||||
previous.shortcutIndexByWorkspaceKey === next.shortcutIndexByWorkspaceKey &&
|
previous.shortcutIndexByWorkspaceKey === next.shortcutIndexByWorkspaceKey &&
|
||||||
previous.hostLabelByServerId === next.hostLabelByServerId &&
|
previous.hostLabelByServerId === next.hostLabelByServerId &&
|
||||||
|
previous.showHostLabels === next.showHostLabels &&
|
||||||
previous.parentGestureRef === next.parentGestureRef &&
|
previous.parentGestureRef === next.parentGestureRef &&
|
||||||
previous.onToggleCollapsed === next.onToggleCollapsed &&
|
previous.onToggleCollapsed === next.onToggleCollapsed &&
|
||||||
previous.onWorkspacePress === next.onWorkspacePress &&
|
previous.onWorkspacePress === next.onWorkspacePress &&
|
||||||
@@ -2181,6 +2183,7 @@ export function SidebarWorkspaceList({
|
|||||||
}
|
}
|
||||||
return labels;
|
return labels;
|
||||||
}, [hosts]);
|
}, [hosts]);
|
||||||
|
const showHostLabels = useMemo(() => shouldShowSidebarHostLabels(projects), [projects]);
|
||||||
|
|
||||||
const content =
|
const content =
|
||||||
groupMode === "status" ? (
|
groupMode === "status" ? (
|
||||||
@@ -2189,6 +2192,8 @@ export function SidebarWorkspaceList({
|
|||||||
projectNamesByKey={projectNamesByKey}
|
projectNamesByKey={projectNamesByKey}
|
||||||
shortcutIndexByWorkspaceKey={shortcutIndexByWorkspaceKey}
|
shortcutIndexByWorkspaceKey={shortcutIndexByWorkspaceKey}
|
||||||
onWorkspacePress={onWorkspacePress}
|
onWorkspacePress={onWorkspacePress}
|
||||||
|
hostLabelByServerId={hostLabelByServerId}
|
||||||
|
showHostLabels={showHostLabels}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ProjectModeList
|
<ProjectModeList
|
||||||
@@ -2202,6 +2207,7 @@ export function SidebarWorkspaceList({
|
|||||||
parentGestureRef={parentGestureRef}
|
parentGestureRef={parentGestureRef}
|
||||||
pathname={pathname}
|
pathname={pathname}
|
||||||
hostLabelByServerId={hostLabelByServerId}
|
hostLabelByServerId={hostLabelByServerId}
|
||||||
|
showHostLabels={showHostLabels}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2213,11 +2219,15 @@ function SidebarStatusModeWrapper({
|
|||||||
projectNamesByKey,
|
projectNamesByKey,
|
||||||
shortcutIndexByWorkspaceKey: _projectShortcutIndex,
|
shortcutIndexByWorkspaceKey: _projectShortcutIndex,
|
||||||
onWorkspacePress,
|
onWorkspacePress,
|
||||||
|
hostLabelByServerId,
|
||||||
|
showHostLabels,
|
||||||
}: {
|
}: {
|
||||||
statusWorkspacePlacements: SidebarStatusWorkspacePlacement[];
|
statusWorkspacePlacements: SidebarStatusWorkspacePlacement[];
|
||||||
projectNamesByKey: Map<string, string>;
|
projectNamesByKey: Map<string, string>;
|
||||||
shortcutIndexByWorkspaceKey: Map<string, number>;
|
shortcutIndexByWorkspaceKey: Map<string, number>;
|
||||||
onWorkspacePress?: () => void;
|
onWorkspacePress?: () => void;
|
||||||
|
hostLabelByServerId: ReadonlyMap<string, string>;
|
||||||
|
showHostLabels: boolean;
|
||||||
}) {
|
}) {
|
||||||
const showShortcutBadges = useShowShortcutBadges();
|
const showShortcutBadges = useShowShortcutBadges();
|
||||||
|
|
||||||
@@ -2228,6 +2238,8 @@ function SidebarStatusModeWrapper({
|
|||||||
shortcutIndexByWorkspaceKey={_projectShortcutIndex}
|
shortcutIndexByWorkspaceKey={_projectShortcutIndex}
|
||||||
showShortcutBadges={showShortcutBadges}
|
showShortcutBadges={showShortcutBadges}
|
||||||
onWorkspacePress={onWorkspacePress}
|
onWorkspacePress={onWorkspacePress}
|
||||||
|
hostLabelByServerId={hostLabelByServerId}
|
||||||
|
showHostLabels={showHostLabels}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2243,12 +2255,14 @@ function ProjectModeList({
|
|||||||
parentGestureRef,
|
parentGestureRef,
|
||||||
pathname,
|
pathname,
|
||||||
hostLabelByServerId,
|
hostLabelByServerId,
|
||||||
|
showHostLabels,
|
||||||
}: Omit<
|
}: Omit<
|
||||||
SidebarWorkspaceListProps,
|
SidebarWorkspaceListProps,
|
||||||
"statusWorkspacePlacements" | "projectNamesByKey" | "groupMode" | "isRefreshing" | "onRefresh"
|
"statusWorkspacePlacements" | "projectNamesByKey" | "groupMode" | "isRefreshing" | "onRefresh"
|
||||||
> & {
|
> & {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
hostLabelByServerId: ReadonlyMap<string, string>;
|
hostLabelByServerId: ReadonlyMap<string, string>;
|
||||||
|
showHostLabels: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [creatingWorkspaceIds, setCreatingWorkspaceIds] = useState<Set<string>>(() => new Set());
|
const [creatingWorkspaceIds, setCreatingWorkspaceIds] = useState<Set<string>>(() => new Set());
|
||||||
@@ -2436,6 +2450,7 @@ function ProjectModeList({
|
|||||||
creatingWorkspaceIds={creatingWorkspaceIds}
|
creatingWorkspaceIds={creatingWorkspaceIds}
|
||||||
activeWorkspaceSelection={activeWorkspaceSelection}
|
activeWorkspaceSelection={activeWorkspaceSelection}
|
||||||
hostLabelByServerId={hostLabelByServerId}
|
hostLabelByServerId={hostLabelByServerId}
|
||||||
|
showHostLabels={showHostLabels}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -2445,6 +2460,7 @@ function ProjectModeList({
|
|||||||
handleWorktreeCreated,
|
handleWorktreeCreated,
|
||||||
handleWorkspaceReorder,
|
handleWorkspaceReorder,
|
||||||
hostLabelByServerId,
|
hostLabelByServerId,
|
||||||
|
showHostLabels,
|
||||||
onWorkspacePress,
|
onWorkspacePress,
|
||||||
onToggleProjectCollapsed,
|
onToggleProjectCollapsed,
|
||||||
parentGestureRef,
|
parentGestureRef,
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ interface StatusWorkspaceListProps {
|
|||||||
shortcutIndexByWorkspaceKey: Map<string, number>;
|
shortcutIndexByWorkspaceKey: Map<string, number>;
|
||||||
showShortcutBadges: boolean;
|
showShortcutBadges: boolean;
|
||||||
onWorkspacePress?: () => void;
|
onWorkspacePress?: () => void;
|
||||||
|
hostLabelByServerId: ReadonlyMap<string, string>;
|
||||||
|
showHostLabels: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SidebarStatusWorkspaceList({
|
export function SidebarStatusWorkspaceList({
|
||||||
@@ -103,6 +105,8 @@ export function SidebarStatusWorkspaceList({
|
|||||||
shortcutIndexByWorkspaceKey: _projectShortcutIndex,
|
shortcutIndexByWorkspaceKey: _projectShortcutIndex,
|
||||||
showShortcutBadges,
|
showShortcutBadges,
|
||||||
onWorkspacePress,
|
onWorkspacePress,
|
||||||
|
hostLabelByServerId,
|
||||||
|
showHostLabels,
|
||||||
}: StatusWorkspaceListProps) {
|
}: StatusWorkspaceListProps) {
|
||||||
const groups = useMemo(
|
const groups = useMemo(
|
||||||
() => buildStatusGroups(workspaces, projectNamesByKey),
|
() => buildStatusGroups(workspaces, projectNamesByKey),
|
||||||
@@ -138,6 +142,8 @@ export function SidebarStatusWorkspaceList({
|
|||||||
shortcutIndex={statusShortcutIndex}
|
shortcutIndex={statusShortcutIndex}
|
||||||
showShortcutBadges={showShortcutBadges}
|
showShortcutBadges={showShortcutBadges}
|
||||||
onWorkspacePress={onWorkspacePress}
|
onWorkspacePress={onWorkspacePress}
|
||||||
|
hostLabelByServerId={hostLabelByServerId}
|
||||||
|
showHostLabels={showHostLabels}
|
||||||
/>
|
/>
|
||||||
</NestableScrollContainer>
|
</NestableScrollContainer>
|
||||||
) : (
|
) : (
|
||||||
@@ -154,6 +160,8 @@ export function SidebarStatusWorkspaceList({
|
|||||||
shortcutIndex={statusShortcutIndex}
|
shortcutIndex={statusShortcutIndex}
|
||||||
showShortcutBadges={showShortcutBadges}
|
showShortcutBadges={showShortcutBadges}
|
||||||
onWorkspacePress={onWorkspacePress}
|
onWorkspacePress={onWorkspacePress}
|
||||||
|
hostLabelByServerId={hostLabelByServerId}
|
||||||
|
showHostLabels={showHostLabels}
|
||||||
/>
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)}
|
)}
|
||||||
@@ -168,6 +176,8 @@ function StatusGroupList({
|
|||||||
shortcutIndex,
|
shortcutIndex,
|
||||||
showShortcutBadges,
|
showShortcutBadges,
|
||||||
onWorkspacePress,
|
onWorkspacePress,
|
||||||
|
hostLabelByServerId,
|
||||||
|
showHostLabels,
|
||||||
}: {
|
}: {
|
||||||
groups: StatusGroup[];
|
groups: StatusGroup[];
|
||||||
collapsedStatusGroupKeys: ReadonlySet<string>;
|
collapsedStatusGroupKeys: ReadonlySet<string>;
|
||||||
@@ -175,6 +185,8 @@ function StatusGroupList({
|
|||||||
shortcutIndex: Map<string, number>;
|
shortcutIndex: Map<string, number>;
|
||||||
showShortcutBadges: boolean;
|
showShortcutBadges: boolean;
|
||||||
onWorkspacePress?: () => void;
|
onWorkspacePress?: () => void;
|
||||||
|
hostLabelByServerId: ReadonlyMap<string, string>;
|
||||||
|
showHostLabels: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -190,7 +202,12 @@ function StatusGroupList({
|
|||||||
<StatusWorkspaceRow
|
<StatusWorkspaceRow
|
||||||
key={workspace.workspaceKey}
|
key={workspace.workspaceKey}
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
projectName={projectNamesByKey.get(workspace.projectKey) ?? ""}
|
subtitle={buildStatusRowSubtitle({
|
||||||
|
projectName: projectNamesByKey.get(workspace.projectKey) ?? "",
|
||||||
|
hostLabel: showHostLabels
|
||||||
|
? (hostLabelByServerId.get(workspace.serverId) ?? workspace.serverId)
|
||||||
|
: null,
|
||||||
|
})}
|
||||||
shortcutNumber={shortcutIndex.get(workspace.workspaceKey) ?? null}
|
shortcutNumber={shortcutIndex.get(workspace.workspaceKey) ?? null}
|
||||||
showShortcutBadge={showShortcutBadges}
|
showShortcutBadge={showShortcutBadges}
|
||||||
onWorkspacePress={onWorkspacePress}
|
onWorkspacePress={onWorkspacePress}
|
||||||
@@ -204,6 +221,21 @@ function StatusGroupList({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status mode breaks the project grouping, so the row needs the project name to stay
|
||||||
|
// legible; the host is appended after a middle dot once labels are active.
|
||||||
|
function buildStatusRowSubtitle({
|
||||||
|
projectName,
|
||||||
|
hostLabel,
|
||||||
|
}: {
|
||||||
|
projectName: string;
|
||||||
|
hostLabel: string | null;
|
||||||
|
}): string {
|
||||||
|
if (!hostLabel) {
|
||||||
|
return projectName;
|
||||||
|
}
|
||||||
|
return projectName ? `${projectName} · ${hostLabel}` : hostLabel;
|
||||||
|
}
|
||||||
|
|
||||||
function StatusGroupHeader({ group, collapsed }: { group: StatusGroup; collapsed: boolean }) {
|
function StatusGroupHeader({ group, collapsed }: { group: StatusGroup; collapsed: boolean }) {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
const toggleStatusGroupCollapsed = useSidebarCollapsedSectionsStore(
|
const toggleStatusGroupCollapsed = useSidebarCollapsedSectionsStore(
|
||||||
@@ -288,13 +320,13 @@ function StatusGroupIcon({ bucket }: { bucket: StatusGroup["bucket"] }) {
|
|||||||
|
|
||||||
const StatusWorkspaceRow = memo(function StatusWorkspaceRow({
|
const StatusWorkspaceRow = memo(function StatusWorkspaceRow({
|
||||||
workspace,
|
workspace,
|
||||||
projectName,
|
subtitle,
|
||||||
shortcutNumber,
|
shortcutNumber,
|
||||||
showShortcutBadge,
|
showShortcutBadge,
|
||||||
onWorkspacePress,
|
onWorkspacePress,
|
||||||
}: {
|
}: {
|
||||||
workspace: SidebarStatusWorkspacePlacement;
|
workspace: SidebarStatusWorkspacePlacement;
|
||||||
projectName: string;
|
subtitle: string;
|
||||||
shortcutNumber: number | null;
|
shortcutNumber: number | null;
|
||||||
showShortcutBadge: boolean;
|
showShortcutBadge: boolean;
|
||||||
onWorkspacePress?: () => void;
|
onWorkspacePress?: () => void;
|
||||||
@@ -316,7 +348,7 @@ const StatusWorkspaceRow = memo(function StatusWorkspaceRow({
|
|||||||
return (
|
return (
|
||||||
<StatusWorkspaceRowWithMenu
|
<StatusWorkspaceRowWithMenu
|
||||||
workspace={workspaceEntry}
|
workspace={workspaceEntry}
|
||||||
projectName={projectName}
|
subtitle={subtitle}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
shortcutNumber={shortcutNumber}
|
shortcutNumber={shortcutNumber}
|
||||||
showShortcutBadge={showShortcutBadge}
|
showShortcutBadge={showShortcutBadge}
|
||||||
@@ -327,14 +359,14 @@ const StatusWorkspaceRow = memo(function StatusWorkspaceRow({
|
|||||||
|
|
||||||
function StatusWorkspaceRowWithMenu({
|
function StatusWorkspaceRowWithMenu({
|
||||||
workspace,
|
workspace,
|
||||||
projectName,
|
subtitle,
|
||||||
selected,
|
selected,
|
||||||
shortcutNumber,
|
shortcutNumber,
|
||||||
showShortcutBadge,
|
showShortcutBadge,
|
||||||
onPress,
|
onPress,
|
||||||
}: {
|
}: {
|
||||||
workspace: SidebarWorkspaceEntry;
|
workspace: SidebarWorkspaceEntry;
|
||||||
projectName: string;
|
subtitle: string;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
shortcutNumber: number | null;
|
shortcutNumber: number | null;
|
||||||
showShortcutBadge: boolean;
|
showShortcutBadge: boolean;
|
||||||
@@ -455,7 +487,7 @@ function StatusWorkspaceRowWithMenu({
|
|||||||
<>
|
<>
|
||||||
<StatusWorkspaceRowInner
|
<StatusWorkspaceRowInner
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
projectName={projectName}
|
subtitle={subtitle}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
shortcutNumber={shortcutNumber}
|
shortcutNumber={shortcutNumber}
|
||||||
showShortcutBadge={showShortcutBadge}
|
showShortcutBadge={showShortcutBadge}
|
||||||
@@ -487,7 +519,7 @@ function StatusWorkspaceRowWithMenu({
|
|||||||
|
|
||||||
function StatusWorkspaceRowInner({
|
function StatusWorkspaceRowInner({
|
||||||
workspace,
|
workspace,
|
||||||
projectName,
|
subtitle,
|
||||||
selected,
|
selected,
|
||||||
shortcutNumber,
|
shortcutNumber,
|
||||||
showShortcutBadge,
|
showShortcutBadge,
|
||||||
@@ -504,7 +536,7 @@ function StatusWorkspaceRowInner({
|
|||||||
archiveShortcutKeys,
|
archiveShortcutKeys,
|
||||||
}: {
|
}: {
|
||||||
workspace: SidebarWorkspaceEntry;
|
workspace: SidebarWorkspaceEntry;
|
||||||
projectName: string;
|
subtitle: string;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
shortcutNumber: number | null;
|
shortcutNumber: number | null;
|
||||||
showShortcutBadge: boolean;
|
showShortcutBadge: boolean;
|
||||||
@@ -554,7 +586,7 @@ function StatusWorkspaceRowInner({
|
|||||||
>
|
>
|
||||||
<SidebarWorkspaceRowContent
|
<SidebarWorkspaceRowContent
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
subtitle={projectName}
|
subtitle={subtitle}
|
||||||
scriptIconKind={scriptIconKind}
|
scriptIconKind={scriptIconKind}
|
||||||
isHovered={isHovered}
|
isHovered={isHovered}
|
||||||
isLoading={isArchiving}
|
isLoading={isArchiving}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
buildSidebarProjectsFromStructure,
|
buildSidebarProjectsFromStructure,
|
||||||
computeSidebarOrderUpdates,
|
computeSidebarOrderUpdates,
|
||||||
deriveSidebarLoadingState,
|
deriveSidebarLoadingState,
|
||||||
|
shouldShowSidebarHostLabels,
|
||||||
type SidebarProjectEntry,
|
type SidebarProjectEntry,
|
||||||
} from "./sidebar-workspaces-view-model";
|
} from "./sidebar-workspaces-view-model";
|
||||||
|
|
||||||
@@ -297,6 +298,63 @@ describe("shared sidebar workspace model", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("shouldShowSidebarHostLabels", () => {
|
||||||
|
it("is false with no visible projects", () => {
|
||||||
|
expect(shouldShowSidebarHostLabels([])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is false when every project lives on a single host", () => {
|
||||||
|
const projects = buildSidebarProjectsFromStructure({
|
||||||
|
projects: [
|
||||||
|
project({ projectKey: "project-a", workspaceKeys: ["ws-1"] }),
|
||||||
|
project({ projectKey: "project-b", workspaceKeys: ["ws-2"] }),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(shouldShowSidebarHostLabels(projects)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is true when projects span separate hosts", () => {
|
||||||
|
const projects = buildSidebarProjectsFromStructure({
|
||||||
|
projects: [
|
||||||
|
project({
|
||||||
|
projectKey: "project-a",
|
||||||
|
hosts: [
|
||||||
|
{ serverId: "host-a", iconWorkingDir: "/repo/project-a", canCreateWorktree: true },
|
||||||
|
],
|
||||||
|
workspaceKeys: ["host-a:ws-1"],
|
||||||
|
}),
|
||||||
|
project({
|
||||||
|
projectKey: "project-b",
|
||||||
|
hosts: [
|
||||||
|
{ serverId: "host-b", iconWorkingDir: "/repo/project-b", canCreateWorktree: true },
|
||||||
|
],
|
||||||
|
workspaceKeys: ["host-b:ws-2"],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(shouldShowSidebarHostLabels(projects)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is true for a single project shared across hosts", () => {
|
||||||
|
const projects = buildSidebarProjectsFromStructure({
|
||||||
|
projects: [
|
||||||
|
project({
|
||||||
|
projectKey: "getpaseo/paseo",
|
||||||
|
hosts: [
|
||||||
|
{ serverId: "host-a", iconWorkingDir: "/repo/paseo", canCreateWorktree: true },
|
||||||
|
{ serverId: "host-b", iconWorkingDir: "/repo/paseo", canCreateWorktree: true },
|
||||||
|
],
|
||||||
|
workspaceKeys: ["host-a:main", "host-b:feature"],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(shouldShowSidebarHostLabels(projects)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("computeSidebarOrderUpdates", () => {
|
describe("computeSidebarOrderUpdates", () => {
|
||||||
it("returns no updates when there are no visible projects", () => {
|
it("returns no updates when there are no visible projects", () => {
|
||||||
const updates = computeSidebarOrderUpdates({
|
const updates = computeSidebarOrderUpdates({
|
||||||
|
|||||||
@@ -351,6 +351,20 @@ export function buildSidebarProjectsFromHostProjects(input: {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Host labels disambiguate which machine a workspace lives on; they only earn their
|
||||||
|
// space once the visible sidebar spans more than one host. Counting distinct hosts
|
||||||
|
// across the visible projects (not all connected hosts) keeps labels off when a host
|
||||||
|
// filter pins the view to a single host.
|
||||||
|
export function shouldShowSidebarHostLabels(projects: SidebarProjectEntry[]): boolean {
|
||||||
|
const serverIds = new Set<string>();
|
||||||
|
for (const project of projects) {
|
||||||
|
for (const host of project.hosts) {
|
||||||
|
serverIds.add(host.serverId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serverIds.size >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
export function applyStoredOrdering<T>(input: {
|
export function applyStoredOrdering<T>(input: {
|
||||||
items: T[];
|
items: T[];
|
||||||
storedOrder: string[];
|
storedOrder: string[];
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export {
|
|||||||
computeSidebarOrderUpdates,
|
computeSidebarOrderUpdates,
|
||||||
createSidebarWorkspaceEntry,
|
createSidebarWorkspaceEntry,
|
||||||
deriveSidebarLoadingState,
|
deriveSidebarLoadingState,
|
||||||
|
shouldShowSidebarHostLabels,
|
||||||
type SidebarLoadingState,
|
type SidebarLoadingState,
|
||||||
type SidebarOrderUpdates,
|
type SidebarOrderUpdates,
|
||||||
type SidebarStatusWorkspacePlacement,
|
type SidebarStatusWorkspacePlacement,
|
||||||
|
|||||||
Reference in New Issue
Block a user