Improve sidebar workspace readability and host identity (#2790)

* fix(app): align turn footer actions

Keep running-turn actions in the same position as completed-turn actions and use the shared small icon size for both copy and fork controls.

* feat(app): improve sidebar workspace identity

Make dense sidebar workspace rows easier to scan and let each device control how connected hosts are identified.

* fix(app): keep host badges readable

Use a compact display menu and keep identity color on the host icon so translated controls fit and badge text retains standard contrast.

* fix(app): announce selected sidebar badge mode

* fix(app): retain icon-only host names for screen readers

* fix(app): name hoisted workspace rows

* fix(app): retain host identity in workspace labels

* fix(app): retain workspace status in row labels

* fix(app): defer host appearance until identity resolves

* fix(app): serialize host appearance writes

* fix(app): serialize host registry writes

* fix(app): bound sidebar host badge labels

* test(app): match workspace status labels

* fix(app): consolidate sidebar identity behavior

* test(app): assert status grouping behavior

* fix(app): keep sidebar row actions accessible

* fix(app): stabilize optimistic message layout
This commit is contained in:
Mohamed Boudra
2026-08-02 16:58:20 +02:00
committed by GitHub
parent 553ac53643
commit ccb668b3b4
57 changed files with 3251 additions and 809 deletions
@@ -19,6 +19,7 @@ import {
CircleX,
Copy,
ExternalLink,
FileDiff,
Folder,
GitBranch,
Server,
@@ -94,12 +95,14 @@ interface WorkspaceHoverCardProps {
workspace: SidebarWorkspaceEntry;
prHint: PrHint | null;
isDragging: boolean;
disabled?: boolean;
}
export function WorkspaceHoverCard({
workspace,
prHint,
isDragging,
disabled = false,
children,
}: PropsWithChildren<WorkspaceHoverCardProps>): ReactNode {
const isCompact = useIsCompactFormFactor();
@@ -109,7 +112,12 @@ export function WorkspaceHoverCard({
}
return (
<WorkspaceHoverCardDesktop workspace={workspace} prHint={prHint} isDragging={isDragging}>
<WorkspaceHoverCardDesktop
workspace={workspace}
prHint={prHint}
isDragging={isDragging}
disabled={disabled}
>
{children}
</WorkspaceHoverCardDesktop>
);
@@ -119,6 +127,7 @@ function WorkspaceHoverCardDesktop({
workspace,
prHint,
isDragging,
disabled = false,
children,
}: PropsWithChildren<WorkspaceHoverCardProps>): ReactElement {
const triggerRef = useRef<View>(null);
@@ -145,10 +154,10 @@ function WorkspaceHoverCardDesktop({
const handleTriggerEnter = useCallback(() => {
triggerHoveredRef.current = true;
clearGraceTimer();
if (!isDragging) {
if (!isDragging && !disabled) {
setOpen(true);
}
}, [clearGraceTimer, isDragging]);
}, [clearGraceTimer, disabled, isDragging]);
const handleTriggerLeave = useCallback(() => {
triggerHoveredRef.current = false;
@@ -166,13 +175,13 @@ function WorkspaceHoverCardDesktop({
onLeaveSafeZone: scheduleClose,
});
// Close when drag starts
// Close while another row interaction owns attention.
useEffect(() => {
if (isDragging) {
if (isDragging || disabled) {
clearGraceTimer();
setOpen(false);
}
}, [isDragging, clearGraceTimer]);
}, [clearGraceTimer, disabled, isDragging]);
// Cleanup on unmount
useEffect(() => {
@@ -285,6 +294,16 @@ function WorkspaceHoverCardContent({
{workspace.name}
</Text>
</View>
{prHint ? <PrBadge hint={prHint} style={styles.cardInfoRow} /> : null}
{workspace.diffStat ? (
<View style={styles.cardInfoRow}>
<ThemedFileDiff size={12} uniProps={foregroundMutedColorMapping} />
<DiffStat
additions={workspace.diffStat.additions}
deletions={workspace.diffStat.deletions}
/>
</View>
) : null}
<HostRow serverId={workspace.serverId} />
{workspace.currentBranch ? (
<CopyableInfoRow
@@ -304,17 +323,6 @@ function WorkspaceHoverCardContent({
testID="hover-card-workspace-cwd"
/>
) : null}
{prHint || workspace.diffStat ? (
<View style={styles.cardMetaRow}>
{workspace.diffStat ? (
<DiffStat
additions={workspace.diffStat.additions}
deletions={workspace.diffStat.deletions}
/>
) : null}
{prHint ? <PrBadge hint={prHint} /> : null}
</View>
) : null}
{prHint?.checks && prHint.checks.length > 0 ? (
<>
<View style={styles.separator} />
@@ -334,6 +342,7 @@ function WorkspaceHoverCardContent({
const ThemedGitBranch = withUnistyles(GitBranch);
const ThemedFolder = withUnistyles(Folder);
const ThemedServer = withUnistyles(Server);
const ThemedFileDiff = withUnistyles(FileDiff);
type CardInfoIcon = React.ComponentType<React.ComponentProps<typeof ThemedGitBranch>>;
@@ -596,13 +605,6 @@ const styles = StyleSheet.create((theme) => ({
flex: 1,
minWidth: 0,
},
cardMetaRow: {
flexDirection: "row",
alignItems: "center",
gap: 6,
paddingHorizontal: theme.spacing[3],
paddingBottom: theme.spacing[2],
},
cardInfoRow: {
flexDirection: "row",
alignItems: "center",