diff --git a/packages/app/src/components/git-actions-split-button.tsx b/packages/app/src/components/git-actions-split-button.tsx
index f510424ca..234d789d3 100644
--- a/packages/app/src/components/git-actions-split-button.tsx
+++ b/packages/app/src/components/git-actions-split-button.tsx
@@ -16,18 +16,25 @@ import type { GitAction, GitActions } from "@/components/git-actions-policy";
interface GitActionsSplitButtonProps {
gitActions: GitActions;
+ hideLabels?: boolean;
}
-export function GitActionsSplitButton({ gitActions }: GitActionsSplitButtonProps) {
+export function GitActionsSplitButton({
+ gitActions,
+ hideLabels,
+}: GitActionsSplitButtonProps) {
const { theme } = useUnistyles();
const toast = useToast();
const archiveShortcutKeys = useShortcutKeys("archive-worktree");
- const getActionDisplayLabel = useCallback((action: GitAction): string => {
- if (action.status === "pending") return action.pendingLabel;
- if (action.status === "success") return action.successLabel;
- return action.label;
- }, []);
+ const getActionDisplayLabel = useCallback(
+ (action: GitAction): string => {
+ if (action.status === "pending") return action.pendingLabel;
+ if (action.status === "success") return action.successLabel;
+ return action.label;
+ },
+ [],
+ );
const handleActionSelect = useCallback(
(action: GitAction) => {
@@ -68,9 +75,11 @@ export function GitActionsSplitButton({ gitActions }: GitActionsSplitButtonProps
) : (
{gitActions.primary.icon}
-
- {getActionDisplayLabel(gitActions.primary)}
-
+ {!hideLabels && (
+
+ {getActionDisplayLabel(gitActions.primary)}
+
+ )}
)}
diff --git a/packages/app/src/components/headers/screen-header.tsx b/packages/app/src/components/headers/screen-header.tsx
index f516934e9..cc5b3b58b 100644
--- a/packages/app/src/components/headers/screen-header.tsx
+++ b/packages/app/src/components/headers/screen-header.tsx
@@ -1,4 +1,5 @@
import type { ReactNode } from "react";
+import type { LayoutChangeEvent } from "react-native";
import { View, type StyleProp, type ViewStyle } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
@@ -17,6 +18,7 @@ interface ScreenHeaderProps {
leftStyle?: StyleProp;
rightStyle?: StyleProp;
borderless?: boolean;
+ onRowLayout?: (event: LayoutChangeEvent) => void;
}
/**
@@ -29,6 +31,7 @@ export function ScreenHeader({
leftStyle,
rightStyle,
borderless,
+ onRowLayout,
}: ScreenHeaderProps) {
const { theme } = useUnistyles();
const insets = useSafeAreaInsets();
@@ -42,6 +45,7 @@ export function ScreenHeader({
({
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
+ minWidth: 0,
},
right: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
+ flexShrink: 0,
},
borderless: {
borderBottomColor: "transparent",
diff --git a/packages/app/src/components/headers/screen-title.tsx b/packages/app/src/components/headers/screen-title.tsx
index 57a60f60d..de8b98449 100644
--- a/packages/app/src/components/headers/screen-title.tsx
+++ b/packages/app/src/components/headers/screen-title.tsx
@@ -25,6 +25,7 @@ export function ScreenTitle({ children, numberOfLines = 1, testID, style }: Scre
const styles = StyleSheet.create((theme) => ({
text: {
flexShrink: 1,
+ minWidth: 0,
fontSize: theme.fontSize.base,
fontWeight: {
xs: "400",
diff --git a/packages/app/src/hooks/use-container-width.ts b/packages/app/src/hooks/use-container-width.ts
new file mode 100644
index 000000000..35bb1b7b6
--- /dev/null
+++ b/packages/app/src/hooks/use-container-width.ts
@@ -0,0 +1,18 @@
+import { useCallback, useState } from "react";
+import type { LayoutChangeEvent } from "react-native";
+
+/**
+ * Tracks the width of a container via onLayout.
+ */
+export function useContainerWidth(): {
+ onLayout: (e: LayoutChangeEvent) => void;
+ width: number;
+} {
+ const [width, setWidth] = useState(0);
+ return {
+ onLayout: useCallback((e: LayoutChangeEvent) => {
+ setWidth(e.nativeEvent.layout.width);
+ }, []),
+ width,
+ };
+}
diff --git a/packages/app/src/screens/workspace/workspace-git-actions.tsx b/packages/app/src/screens/workspace/workspace-git-actions.tsx
index 86fb15d9c..b30fe7bf4 100644
--- a/packages/app/src/screens/workspace/workspace-git-actions.tsx
+++ b/packages/app/src/screens/workspace/workspace-git-actions.tsx
@@ -15,9 +15,14 @@ import { useGitActions } from "@/hooks/use-git-actions";
interface WorkspaceGitActionsProps {
serverId: string;
cwd: string;
+ hideLabels?: boolean;
}
-export function WorkspaceGitActions({ serverId, cwd }: WorkspaceGitActionsProps) {
+export function WorkspaceGitActions({
+ serverId,
+ cwd,
+ hideLabels,
+}: WorkspaceGitActionsProps) {
const { theme } = useUnistyles();
const icons = useMemo(
@@ -40,5 +45,5 @@ export function WorkspaceGitActions({ serverId, cwd }: WorkspaceGitActionsProps)
return null;
}
- return ;
+ return ;
}
diff --git a/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx b/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx
index 836c2469f..a414175df 100644
--- a/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx
+++ b/packages/app/src/screens/workspace/workspace-open-in-editor-button.tsx
@@ -20,9 +20,14 @@ import { isWeb } from "@/constants/platform";
interface WorkspaceOpenInEditorButtonProps {
serverId: string;
cwd: string;
+ hideLabels?: boolean;
}
-export function WorkspaceOpenInEditorButton({ serverId, cwd }: WorkspaceOpenInEditorButtonProps) {
+export function WorkspaceOpenInEditorButton({
+ serverId,
+ cwd,
+ hideLabels,
+}: WorkspaceOpenInEditorButtonProps) {
const { theme } = useUnistyles();
const toast = useToast();
const client = useHostRuntimeClient(serverId);
@@ -127,7 +132,7 @@ export function WorkspaceOpenInEditorButton({ serverId, cwd }: WorkspaceOpenInEd
size={16}
color={theme.colors.foregroundMuted}
/>
- Open
+ {!hideLabels && Open}
)}
@@ -196,7 +201,14 @@ const styles = StyleSheet.create((theme) => ({
},
splitButtonPrimary: {
paddingLeft: theme.spacing[3],
- paddingRight: 10,
+ paddingRight: theme.spacing[3],
+ paddingVertical: theme.spacing[1],
+ justifyContent: "center",
+ position: "relative",
+ },
+ splitButtonPrimaryIconOnly: {
+ paddingLeft: theme.spacing[2],
+ paddingRight: theme.spacing[2],
paddingVertical: theme.spacing[1],
justifyContent: "center",
position: "relative",
diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx
index a37667ef5..3b8ab6e4e 100644
--- a/packages/app/src/screens/workspace/workspace-screen.tsx
+++ b/packages/app/src/screens/workspace/workspace-screen.tsx
@@ -117,6 +117,7 @@ import { findAdjacentPane } from "@/utils/split-navigation";
import { isAbsolutePath } from "@/utils/path";
import { useIsCompactFormFactor, supportsDesktopPaneSplits } from "@/constants/layout";
import { isWeb, isNative } from "@/constants/platform";
+import { useContainerWidth } from "@/hooks/use-container-width";
const TERMINALS_QUERY_STALE_TIME = 5_000;
const WORKSPACE_SETUP_AUTO_OPEN_WINDOW_MS = 30_000;
@@ -939,6 +940,8 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
);
const pendingByDraftId = useCreateFlowStore((state) => state.pendingByDraftId);
const { closingTabIds, closeTab } = useCloseTabs();
+ const { onLayout: onHeaderLayout, width: headerWidth } = useContainerWidth();
+ const showCompactButtonLabels = headerWidth < 700;
const closeWorkspaceTabWithCleanup = useCallback(
function closeWorkspaceTabWithCleanup(input: {
tabId: string;
@@ -2044,6 +2047,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
{(!isFocusModeEnabled || isMobile) && (
@@ -2151,12 +2155,14 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
serverId={normalizedServerId}
workspaceId={normalizedWorkspaceId}
scripts={workspaceDescriptor.scripts}
+ hideLabels={showCompactButtonLabels}
/>
) : null}
{!isMobile ? (
) : null}
{!isMobile && isGitCheckout ? (
@@ -2164,6 +2170,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
@@ -2413,9 +2420,11 @@ const styles = StyleSheet.create((theme) => ({
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
+ overflow: "hidden",
},
headerTitleTextGroup: {
minWidth: 0,
+ overflow: "hidden",
flexShrink: 1,
flexGrow: {
xs: 1,
@@ -2442,6 +2451,8 @@ const styles = StyleSheet.create((theme) => ({
md: theme.fontSize.base,
},
flexShrink: 1,
+ minWidth: 0,
+ maxWidth: "60%",
},
headerTitleSkeleton: {
width: 190,
diff --git a/packages/app/src/screens/workspace/workspace-scripts-button.tsx b/packages/app/src/screens/workspace/workspace-scripts-button.tsx
index 054a619fe..512413b70 100644
--- a/packages/app/src/screens/workspace/workspace-scripts-button.tsx
+++ b/packages/app/src/screens/workspace/workspace-scripts-button.tsx
@@ -41,12 +41,14 @@ interface WorkspaceScriptsButtonProps {
serverId: string;
workspaceId: string;
scripts: WorkspaceDescriptor["scripts"];
+ hideLabels?: boolean;
}
export function WorkspaceScriptsButton({
serverId,
workspaceId,
scripts,
+ hideLabels,
}: WorkspaceScriptsButtonProps): ReactElement | null {
const { theme } = useUnistyles();
const toast = useToast();
@@ -98,7 +100,7 @@ export function WorkspaceScriptsButton({
}
fill="transparent"
/>
- Scripts
+ {!hideLabels && Scripts}
@@ -268,6 +270,11 @@ const styles = StyleSheet.create((theme) => ({
paddingVertical: theme.spacing[1],
justifyContent: "center",
},
+ splitButtonPrimaryIconOnly: {
+ paddingHorizontal: theme.spacing[2],
+ paddingVertical: theme.spacing[1],
+ justifyContent: "center",
+ },
splitButtonPrimaryHovered: {
backgroundColor: theme.colors.surface2,
},