Hide keyboard shortcut badges in workspace menu on native (#1964)

* Hide keyboard shortcut badges in workspace menu on native

The archive item's shortcut badge (e.g. Ctrl+Shift+⌫) rendered in the
workspace kebab/context menu on mobile, where there's no physical
keyboard to invoke it. Gate the trailing badge behind !isNative in both
kebab menu variants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(app): hide native shortcuts in every workspace menu

Workspace menus were duplicated across project and status views, so the status path missed the native gate. Centralize the menu so every sidebar view shares the same shortcut policy.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2026-07-10 00:42:36 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent a743a22496
commit cac71ee8f5
4 changed files with 160 additions and 395 deletions
@@ -38,17 +38,13 @@ import { type GestureType } from "react-native-gesture-handler";
import * as Clipboard from "expo-clipboard";
import { DiffStat } from "@/components/diff-stat";
import {
Archive,
CircleAlert,
CircleCheck,
ChevronDown,
ChevronRight,
Copy,
ExternalLink,
GitPullRequest,
Settings,
MoreVertical,
Pencil,
Plus,
Trash2,
} from "lucide-react-native";
@@ -93,6 +89,7 @@ import { isEmphasizedStatusDotBucket } from "@/utils/status-dot-color";
import type { SidebarStateBucket } from "@/utils/sidebar-agent-state";
import { SidebarStatusWorkspaceList } from "@/components/sidebar/sidebar-status-list";
import type { StatusGroup } from "@/hooks/sidebar-status-view-model";
import { SidebarWorkspaceMenu } from "@/components/sidebar/sidebar-workspace-menu";
import {
SidebarWorkspaceRowFrame,
SidebarWorkspaceRowContent,
@@ -142,15 +139,11 @@ const ThemedExternalLink = withUnistyles(ExternalLink);
const ThemedGitPullRequest = withUnistyles(GitPullRequest);
const ThemedActivityIndicator = withUnistyles(ActivityIndicator);
const ThemedCircleAlert = withUnistyles(CircleAlert);
const ThemedCircleCheck = withUnistyles(CircleCheck);
const ThemedSyncedLoader = withUnistyles(SyncedLoader);
const ThemedPlus = withUnistyles(Plus);
const ThemedMoreVertical = withUnistyles(MoreVertical);
const ThemedTrash2 = withUnistyles(Trash2);
const ThemedSettings = withUnistyles(Settings);
const ThemedCopy = withUnistyles(Copy);
const ThemedArchive = withUnistyles(Archive);
const ThemedPencil = withUnistyles(Pencil);
const foregroundColorMapping = (theme: Theme) => ({
color: theme.colors.foreground,
@@ -358,12 +351,6 @@ function projectKebabStyle({
return [styles.projectKebabButton, hovered && styles.projectKebabButtonHovered];
}
function workspaceKebabStyle({
hovered = false,
}: PressableStateCallbackType & { hovered?: boolean }) {
return [styles.kebabButton, hovered && styles.kebabButtonHovered];
}
function getProjectWorkspaceRowStyle({
isDragging,
selected,
@@ -540,12 +527,6 @@ function ProjectRowTrailingActions({
const trash2LeadingIcon = <ThemedTrash2 size={14} uniProps={foregroundMutedColorMapping} />;
const settingsLeadingIcon = <ThemedSettings size={14} uniProps={foregroundMutedColorMapping} />;
const copyLeadingIcon = <ThemedCopy size={14} uniProps={foregroundMutedColorMapping} />;
const markAsReadLeadingIcon = (
<ThemedCircleCheck size={14} uniProps={foregroundMutedColorMapping} />
);
const archiveLeadingIcon = <ThemedArchive size={14} uniProps={foregroundMutedColorMapping} />;
const renameLeadingIcon = <ThemedPencil size={14} uniProps={foregroundMutedColorMapping} />;
const openInNewWindowLeadingIcon = (
<ThemedExternalLink size={14} uniProps={foregroundMutedColorMapping} />
);
@@ -693,7 +674,7 @@ function WorkspaceRowRightGroup({
</SidebarWorkspaceTrailingActionBase>
<SidebarWorkspaceTrailingActionOverlay visible={showKebabInSlot}>
{onArchive ? (
<WorkspaceKebabMenu
<SidebarWorkspaceMenu
workspaceKey={workspace.workspaceKey}
onCopyPath={onCopyPath}
onCopyBranchName={onCopyBranchName}
@@ -713,97 +694,6 @@ function WorkspaceRowRightGroup({
);
}
function WorkspaceKebabMenu({
workspaceKey,
onCopyPath,
onCopyBranchName,
onRename,
onMarkAsRead,
onArchive,
archiveLabel,
archiveStatus,
archivePendingLabel,
archiveShortcutKeys,
}: {
workspaceKey: string;
onCopyPath?: () => void;
onCopyBranchName?: () => void;
onRename?: () => void;
onMarkAsRead?: () => void;
onArchive: () => void;
archiveLabel?: string;
archiveStatus?: "idle" | "pending" | "success";
archivePendingLabel?: string;
archiveShortcutKeys?: ShortcutKey[][] | null;
}) {
const { t } = useTranslation();
const archiveTrailing = useMemo(
() => (archiveShortcutKeys ? <Shortcut chord={archiveShortcutKeys} /> : null),
[archiveShortcutKeys],
);
return (
<DropdownMenu>
<DropdownMenuTrigger
hitSlop={8}
style={workspaceKebabStyle}
accessibilityRole={platformIsWeb ? undefined : "button"}
accessibilityLabel={t("sidebar.workspace.actions.menu")}
testID={`sidebar-workspace-kebab-${workspaceKey}`}
>
{renderKebabTriggerIcon}
</DropdownMenuTrigger>
<DropdownMenuContent align="end" width={260}>
{onCopyPath ? (
<DropdownMenuItem
testID={`sidebar-workspace-menu-copy-path-${workspaceKey}`}
leading={copyLeadingIcon}
onSelect={onCopyPath}
>
{t("sidebar.workspace.actions.copyPath")}
</DropdownMenuItem>
) : null}
{onCopyBranchName ? (
<DropdownMenuItem
testID={`sidebar-workspace-menu-copy-branch-name-${workspaceKey}`}
leading={copyLeadingIcon}
onSelect={onCopyBranchName}
>
{t("sidebar.workspace.actions.copyBranchName")}
</DropdownMenuItem>
) : null}
{onRename ? (
<DropdownMenuItem
testID={`sidebar-workspace-menu-rename-${workspaceKey}`}
leading={renameLeadingIcon}
onSelect={onRename}
>
{t("sidebar.workspace.actions.rename")}
</DropdownMenuItem>
) : null}
{onMarkAsRead ? (
<DropdownMenuItem
testID={`sidebar-workspace-menu-mark-as-read-${workspaceKey}`}
leading={markAsReadLeadingIcon}
onSelect={onMarkAsRead}
>
Mark as read
</DropdownMenuItem>
) : null}
<DropdownMenuItem
testID={`sidebar-workspace-menu-archive-${workspaceKey}`}
leading={archiveLeadingIcon}
trailing={archiveTrailing}
status={archiveStatus}
pendingLabel={archivePendingLabel}
onSelect={onArchive}
>
{archiveLabel ?? t("sidebar.workspace.actions.archive")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
function ProjectIcon({
iconDataUri,
placeholderInitial,
@@ -2872,14 +2762,6 @@ const styles = StyleSheet.create((theme) => ({
fontSize: theme.fontSize.xs,
flexShrink: 0,
},
kebabButton: {
padding: 2,
borderRadius: 4,
marginLeft: 2,
},
kebabButtonHovered: {
backgroundColor: theme.colors.surface2,
},
statusDotNeedsInput: {
backgroundColor: theme.colors.palette.amber[500],
borderColor: theme.colors.surface0,