feat(app): polish workspace tabs and changes

Size tabs from rendered labels and switch to horizontal overflow once minimum click targets are reached, keeping the add-tab action adjacent until it must remain fixed.

Make Changes an always-expanded document with an optional desktop navigation tree, keep Git actions in their platform-owned headers, remove pinned and inline split shortcuts, and place review tabs beside Changes.
This commit is contained in:
Mohamed Boudra
2026-08-17 21:14:37 +02:00
parent 6d7606160e
commit 75adf53893
41 changed files with 929 additions and 1901 deletions
@@ -94,7 +94,7 @@ export function BranchSwitcher({
accessibilityRole="button"
accessibilityLabel={t("branchSwitcher.currentBranch", { branchName: currentBranchName })}
>
<ThemedGitBranch size={14} uniProps={foregroundMutedIconColorMapping} />
<ThemedGitBranch size={12} uniProps={foregroundMutedIconColorMapping} />
<Text style={styles.branchLabel} numberOfLines={1}>
{currentBranchName}
</Text>
@@ -132,8 +132,7 @@ const styles = StyleSheet.create((theme) => ({
gap: theme.spacing[1],
minWidth: 0,
paddingVertical: theme.spacing[1],
paddingHorizontal: theme.spacing[2],
marginLeft: -theme.spacing[2],
paddingHorizontal: theme.spacing[1],
borderRadius: theme.borderRadius.md,
flexShrink: 1,
},
@@ -141,9 +140,9 @@ const styles = StyleSheet.create((theme) => ({
backgroundColor: theme.colors.surface1,
},
branchLabel: {
fontSize: theme.fontSize.sm,
color: theme.colors.foreground,
fontWeight: theme.fontWeight.medium,
fontSize: theme.fontSize.xs,
color: theme.colors.foregroundMuted,
fontWeight: theme.fontWeight.normal,
flexShrink: 1,
},
}));
@@ -18,7 +18,7 @@ import { usePanelStore, selectIsFileExplorerOpen, type ExplorerTab } from "@/sto
import { useCloseFileExplorerGesture } from "@/mobile-panels/gestures";
import { MobilePanelOverlay } from "@/mobile-panels/presentation";
import { HEADER_INNER_HEIGHT } from "@/constants/layout";
import { GitDiffPane } from "@/git/diff-pane";
import { ChangesSurface } from "@/git/diff-pane";
import { FileExplorerPane } from "./file-explorer-pane";
import { useKeyboardShiftStyle } from "@/hooks/use-keyboard-shift-style";
import { useHasOwnedWindowChromeObstruction, WindowChromeSafeArea } from "@/utils/desktop-window";
@@ -433,7 +433,7 @@ function ChangedFilesPane({
>) {
const { addFile, canAddToChat } = useAddFileToChat({ serverId, workspaceId });
return (
<GitDiffPane
<ChangesSurface
host="explorer"
serverId={serverId}
workspaceId={workspaceId}
@@ -1007,7 +1007,7 @@ function SplitPaneView({
buildPaneContentModel,
onFocusPane,
onSplitPane: _onSplitPane,
onSplitPaneEmpty,
onSplitPaneEmpty: _onSplitPaneEmpty,
onReorderTabsInPane,
renderPaneEmptyState,
activeDragTabId,
@@ -1114,15 +1114,6 @@ function SplitPaneView({
},
[onReorderTabsInPane, paneId],
);
const handleSplitRight = useCallback(
() => onSplitPaneEmpty({ targetPaneId: paneId, position: "right" }),
[onSplitPaneEmpty, paneId],
);
const handleSplitDown = useCallback(
() => onSplitPaneEmpty({ targetPaneId: paneId, position: "bottom" }),
[onSplitPaneEmpty, paneId],
);
return (
<RenderProfile id={`SplitPaneView:${pane.id}`}>
<View ref={paneRef} collapsable={false} style={styles.pane}>
@@ -1151,8 +1142,6 @@ function SplitPaneView({
onCreateBrowserTab={onCreateBrowserTab}
showCreateBrowserTab={showCreateBrowserTab}
onReorderTabs={handleReorderTabs}
onSplitRight={handleSplitRight}
onSplitDown={handleSplitDown}
externalDndContext
activeDragTabId={activeDragTabId}
tabDropPreviewIndex={
@@ -0,0 +1,31 @@
import type { ReactElement } from "react";
import { SquareTerminal } from "lucide-react-native";
import { withUnistyles } from "react-native-unistyles";
import { getProviderIcon } from "@/components/provider-icons";
import type { Theme } from "@/styles/theme";
const mutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
const ThemedSquareTerminal = withUnistyles(SquareTerminal);
function ProviderProfileIcon({
iconKey,
size,
color = "",
}: {
iconKey: string;
size: number;
color?: string;
}) {
const Icon = getProviderIcon(iconKey);
return <Icon size={size} color={color} />;
}
const ThemedProviderProfileIcon = withUnistyles(ProviderProfileIcon);
export function TerminalProfileIcon({ iconKey }: { iconKey: string | undefined }): ReactElement {
if (!iconKey) {
return <ThemedSquareTerminal size={14} uniProps={mutedColorMapping} />;
}
return <ThemedProviderProfileIcon iconKey={iconKey} size={14} uniProps={mutedColorMapping} />;
}