diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx
index 8e0840052..fff114c09 100644
--- a/packages/app/src/app/_layout.tsx
+++ b/packages/app/src/app/_layout.tsx
@@ -616,8 +616,8 @@ export default function RootLayout() {
-
-
+
+
@@ -656,8 +656,8 @@ export default function RootLayout() {
-
-
+
+
diff --git a/packages/app/src/components/split-container.tsx b/packages/app/src/components/split-container.tsx
index 23215f177..9ecc93c84 100644
--- a/packages/app/src/components/split-container.tsx
+++ b/packages/app/src/components/split-container.tsx
@@ -67,9 +67,9 @@ interface SplitContainerProps {
onCloseTabsToLeft: (tabId: string, paneTabs: WorkspaceTabDescriptor[]) => Promise | void;
onCloseTabsToRight: (tabId: string, paneTabs: WorkspaceTabDescriptor[]) => Promise | void;
onCloseOtherTabs: (tabId: string, paneTabs: WorkspaceTabDescriptor[]) => Promise | void;
- onSelectNewTabOption: (selection: { optionId: "__new_tab_agent__"; paneId?: string }) => void;
+ onSelectNewTabOption: (selection: { optionId: "__new_tab_agent__" | "__new_tab_terminal__"; paneId?: string }) => void;
onNewTerminalTab: (input: { paneId?: string }) => void;
- newTabAgentOptionId?: "__new_tab_agent__";
+ newTabAgentOptionId?: "__new_tab_agent__" | "__new_tab_terminal__";
buildPaneContentModel: (input: {
paneId: string;
isPaneFocused: boolean;
diff --git a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx
index 6370cef31..2fd44bd6f 100644
--- a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx
+++ b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx
@@ -27,7 +27,7 @@ import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-
const DROPDOWN_WIDTH = 220;
const LOADING_TAB_LABEL_SKELETON_WIDTH = 80;
-type NewTabOptionId = "__new_tab_agent__";
+type NewTabOptionId = "__new_tab_agent__" | "__new_tab_terminal__";
type NewTabSelection = {
optionId: NewTabOptionId;
paneId?: string;
diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx
index fbecfc0f7..df1ef8540 100644
--- a/packages/app/src/screens/workspace/workspace-screen.tsx
+++ b/packages/app/src/screens/workspace/workspace-screen.tsx
@@ -22,6 +22,8 @@ import {
SquareTerminal,
} from "lucide-react-native";
import { GestureDetector } from "react-native-gesture-handler";
+import Animated from "react-native-reanimated";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles";
import invariant from "tiny-invariant";
import { SidebarMenuToggle } from "@/components/headers/menu-header";
@@ -115,6 +117,8 @@ import { findAdjacentPane } from "@/utils/split-navigation";
const TERMINALS_QUERY_STALE_TIME = 5_000;
const NEW_TAB_AGENT_OPTION_ID = "__new_tab_agent__";
+const NEW_TAB_TERMINAL_OPTION_ID = "__new_tab_terminal__";
+type NewTabOptionId = typeof NEW_TAB_AGENT_OPTION_ID | typeof NEW_TAB_TERMINAL_OPTION_ID;
const EMPTY_UI_TABS: WorkspaceTab[] = [];
type WorkspaceScreenProps = {
@@ -217,10 +221,6 @@ type MobileWorkspaceTabSwitcherProps = {
normalizedServerId: string;
normalizedWorkspaceId: string;
onSelectSwitcherTab: (key: string) => void;
- onSelectNewTabOption: (selection: {
- optionId: typeof NEW_TAB_AGENT_OPTION_ID;
- paneId?: string;
- }) => void;
onCopyResumeCommand: (agentId: string) => Promise | void;
onCopyAgentId: (agentId: string) => Promise | void;
onCloseTab: (tabId: string) => Promise | void;
@@ -415,7 +415,6 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
normalizedServerId,
normalizedWorkspaceId,
onSelectSwitcherTab,
- onSelectNewTabOption,
onCopyResumeCommand,
onCopyAgentId,
onCloseTab,
@@ -435,21 +434,18 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
}, [tabs]);
return (
-
+
[
+ accessibilityRole="button"
+ accessibilityLabel={`Switch tabs (${tabs.length} open)`}
+ style={({ pressed }) => [
styles.switcherTrigger,
- (hovered || pressed || isOpen) && styles.switcherTriggerActive,
- { borderWidth: 0, borderColor: "transparent" },
- Platform.OS === "web"
- ? {
- outlineStyle: "solid",
- outlineWidth: 0,
- outlineColor: "transparent",
- }
- : null,
+ pressed && styles.switcherTriggerPressed,
]}
onPress={() => setIsOpen(true)}
>
@@ -460,37 +456,9 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
normalizedWorkspaceId={normalizedWorkspaceId}
/>
-
-
-
-
- onSelectNewTabOption({
- optionId: NEW_TAB_AGENT_OPTION_ID,
- })
- }
- accessibilityRole="button"
- accessibilityLabel="New agent tab"
- style={({ hovered, pressed }) => [
- styles.newTabActionButton,
- (hovered || pressed) && styles.newTabActionButtonHovered,
- ]}
- >
-
-
-
-
- New agent tab
-
-
-
-
-
-
{
+ (selection: { optionId: NewTabOptionId; paneId?: string }) => {
+ if (selection.paneId && persistenceKey) {
+ focusWorkspacePane(persistenceKey, selection.paneId);
+ }
if (selection.optionId === NEW_TAB_AGENT_OPTION_ID) {
- if (selection.paneId && persistenceKey) {
- focusWorkspacePane(persistenceKey, selection.paneId);
- }
handleCreateDraftTab();
+ } else if (selection.optionId === NEW_TAB_TERMINAL_OPTION_ID) {
+ handleCreateTerminal({ paneId: selection.paneId });
}
},
- [focusWorkspacePane, handleCreateDraftTab, persistenceKey]
+ [focusWorkspacePane, handleCreateDraftTab, handleCreateTerminal, persistenceKey]
);
const handleCreateDraftSplit = useCallback(
@@ -1962,6 +1933,18 @@ function WorkspaceScreenContent({
width={220}
testID="workspace-header-menu"
>
+
+ }
+ onSelect={handleCreateDraftTab}
+ >
+ New agent
+
- ) : (
- null
- )}
+ ) : null}
{isMobile ? (
@@ -2323,35 +2303,19 @@ const styles = StyleSheet.create((theme) => ({
borderColor: theme.colors.borderAccent,
},
mobileTabsRow: {
- borderBottomWidth: 1,
- borderBottomColor: theme.colors.border,
backgroundColor: theme.colors.surface0,
- flexDirection: "row",
- alignItems: "center",
- gap: theme.spacing[2],
- paddingHorizontal: theme.spacing[2],
- paddingVertical: theme.spacing[1],
- },
- mobileTabsActions: {
- flexDirection: "row",
- alignItems: "center",
- gap: theme.spacing[1],
+ borderBottomWidth: theme.borderWidth[1],
+ borderBottomColor: theme.colors.border,
},
switcherTrigger: {
flexDirection: "row",
alignItems: "center",
- gap: theme.spacing[1],
- flex: 1,
- minWidth: 0,
- paddingHorizontal: theme.spacing[2],
- paddingVertical: theme.spacing[1],
- borderRadius: theme.borderRadius.md,
- borderWidth: theme.borderWidth[1],
- borderColor: theme.colors.border,
- justifyContent: "space-between",
+ gap: theme.spacing[2],
+ paddingHorizontal: theme.spacing[2] + theme.spacing[3],
+ paddingVertical: theme.spacing[2],
},
- switcherTriggerActive: {
- backgroundColor: theme.colors.surface2,
+ switcherTriggerPressed: {
+ backgroundColor: theme.colors.surface1,
},
switcherTriggerLeft: {
flexDirection: "row",