Move tab row pins and default-pin terminal and browser

This commit is contained in:
Mohamed Boudra
2026-06-16 14:12:18 +07:00
parent 9966e49329
commit c464c2bcb4
4 changed files with 79 additions and 41 deletions

View File

@@ -5,6 +5,7 @@ import { togglePinFromMenu, tabRowPin } from "./helpers/pins";
import { expectTerminalTabOpen } from "./helpers/workspace-tabs"; import { expectTerminalTabOpen } from "./helpers/workspace-tabs";
import type { PinnedTabTarget } from "../src/workspace-pins/target"; import type { PinnedTabTarget } from "../src/workspace-pins/target";
const DRAFT_TARGET: PinnedTabTarget = { kind: "draft" };
const TERMINAL_TARGET: PinnedTabTarget = { kind: "terminal" }; const TERMINAL_TARGET: PinnedTabTarget = { kind: "terminal" };
let workspace: SeededWorkspace; let workspace: SeededWorkspace;
@@ -23,13 +24,13 @@ test.describe("Pinned tab targets", () => {
}) => { }) => {
await gotoWorkspace(page, workspace.workspaceId); await gotoWorkspace(page, workspace.workspaceId);
await expect(tabRowPin(page, TERMINAL_TARGET)).toHaveCount(0); await expect(tabRowPin(page, DRAFT_TARGET)).toHaveCount(0);
await togglePinFromMenu(page, TERMINAL_TARGET); await togglePinFromMenu(page, DRAFT_TARGET);
await expect(tabRowPin(page, TERMINAL_TARGET)).toBeVisible({ timeout: 10_000 }); await expect(tabRowPin(page, DRAFT_TARGET)).toBeVisible({ timeout: 10_000 });
await togglePinFromMenu(page, TERMINAL_TARGET); await togglePinFromMenu(page, DRAFT_TARGET);
await expect(tabRowPin(page, TERMINAL_TARGET)).toHaveCount(0, { timeout: 10_000 }); await expect(tabRowPin(page, DRAFT_TARGET)).toHaveCount(0, { timeout: 10_000 });
}); });
test("clicking the pinned quick-launch button in the tab row opens a terminal tab", async ({ test("clicking the pinned quick-launch button in the tab row opens a terminal tab", async ({
@@ -38,7 +39,7 @@ test.describe("Pinned tab targets", () => {
test.setTimeout(45_000); test.setTimeout(45_000);
await gotoWorkspace(page, workspace.workspaceId); await gotoWorkspace(page, workspace.workspaceId);
await togglePinFromMenu(page, TERMINAL_TARGET); await expect(tabRowPin(page, TERMINAL_TARGET)).toBeVisible({ timeout: 10_000 });
await tabRowPin(page, TERMINAL_TARGET).click(); await tabRowPin(page, TERMINAL_TARGET).click();
await expectTerminalTabOpen(page); await expectTerminalTabOpen(page);

View File

@@ -171,6 +171,44 @@ function PinnableProfileMenuItem({ profile, disabled, onLaunch }: PinnableProfil
interface WorkspaceInlineAddTabButtonProps { interface WorkspaceInlineAddTabButtonProps {
shortcutKeys: ShortcutKey[][] | null; shortcutKeys: ShortcutKey[][] | null;
onCreateAgentTab: () => void;
onLayout: (event: LayoutChangeEvent) => void;
}
function WorkspaceInlineAddTabButton({
shortcutKeys,
onCreateAgentTab,
onLayout,
}: WorkspaceInlineAddTabButtonProps) {
const { t } = useTranslation();
const tooltipText = t("workspace.tabs.actions.newAgent");
return (
<View style={styles.inlineAddButton} onLayout={onLayout}>
<Tooltip delayDuration={0} enabledOnDesktop enabledOnMobile={false}>
<TooltipTrigger
testID="workspace-new-agent-tab-inline"
onPress={onCreateAgentTab}
accessibilityRole="button"
accessibilityLabel={tooltipText}
style={inlineAddActionButtonStyle}
>
<ThemedPlus size={14} uniProps={mutedColorMapping} />
</TooltipTrigger>
<TooltipContent side="bottom" align="center" offset={8}>
<View style={styles.newTabTooltipRow}>
<Text style={styles.newTabTooltipText}>{tooltipText}</Text>
{shortcutKeys ? (
<Shortcut chord={shortcutKeys} style={styles.newTabTooltipShortcut} />
) : null}
</View>
</TooltipContent>
</Tooltip>
</View>
);
}
interface WorkspaceTabRowExtrasProps {
onCreateAgentTab: () => void; onCreateAgentTab: () => void;
onCreateTerminal: () => void; onCreateTerminal: () => void;
onCreateBrowser: () => void; onCreateBrowser: () => void;
@@ -179,11 +217,9 @@ interface WorkspaceInlineAddTabButtonProps {
normalizedServerId: string; normalizedServerId: string;
showCreateBrowserTab: boolean; showCreateBrowserTab: boolean;
terminalDisabled: boolean; terminalDisabled: boolean;
onLayout: (event: LayoutChangeEvent) => void;
} }
function WorkspaceInlineAddTabButton({ function WorkspaceTabRowExtras({
shortcutKeys,
onCreateAgentTab, onCreateAgentTab,
onCreateTerminal, onCreateTerminal,
onCreateBrowser, onCreateBrowser,
@@ -192,8 +228,7 @@ function WorkspaceInlineAddTabButton({
normalizedServerId, normalizedServerId,
showCreateBrowserTab, showCreateBrowserTab,
terminalDisabled, terminalDisabled,
onLayout, }: WorkspaceTabRowExtrasProps) {
}: WorkspaceInlineAddTabButtonProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { config } = useDaemonConfig(normalizedServerId); const { config } = useDaemonConfig(normalizedServerId);
const profiles = useMemo( const profiles = useMemo(
@@ -220,30 +255,8 @@ function WorkspaceInlineAddTabButton({
const launchers = usePinnedLaunchers({ serverId: normalizedServerId, onLaunch }); const launchers = usePinnedLaunchers({ serverId: normalizedServerId, onLaunch });
const tooltipText = t("workspace.tabs.actions.newAgent");
return ( return (
<View style={styles.inlineAddButton} onLayout={onLayout}> <>
<Tooltip delayDuration={0} enabledOnDesktop enabledOnMobile={false}>
<TooltipTrigger
testID="workspace-new-agent-tab-inline"
onPress={onCreateAgentTab}
accessibilityRole="button"
accessibilityLabel={tooltipText}
style={inlineAddActionButtonStyle}
>
<ThemedPlus size={14} uniProps={mutedColorMapping} />
</TooltipTrigger>
<TooltipContent side="bottom" align="center" offset={8}>
<View style={styles.newTabTooltipRow}>
<Text style={styles.newTabTooltipText}>{tooltipText}</Text>
{shortcutKeys ? (
<Shortcut chord={shortcutKeys} style={styles.newTabTooltipShortcut} />
) : null}
</View>
</TooltipContent>
</Tooltip>
<PinnedTargetsRow launchers={launchers} testIdPrefix="workspace-pinned-target" />
<DropdownMenu> <DropdownMenu>
<Tooltip delayDuration={0} enabledOnDesktop enabledOnMobile={false}> <Tooltip delayDuration={0} enabledOnDesktop enabledOnMobile={false}>
<TooltipTrigger asChild triggerRefProp="triggerRef"> <TooltipTrigger asChild triggerRefProp="triggerRef">
@@ -251,7 +264,7 @@ function WorkspaceInlineAddTabButton({
testID="workspace-new-tab-menu-trigger" testID="workspace-new-tab-menu-trigger"
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={t("workspace.tabs.actions.moreActions")} accessibilityLabel={t("workspace.tabs.actions.moreActions")}
style={inlineAddActionButtonStyle} style={newTabActionButtonStyle}
> >
<ThemedChevronDown size={14} uniProps={mutedColorMapping} /> <ThemedChevronDown size={14} uniProps={mutedColorMapping} />
</DropdownMenuTrigger> </DropdownMenuTrigger>
@@ -301,7 +314,8 @@ function WorkspaceInlineAddTabButton({
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</View> <PinnedTargetsRow launchers={launchers} testIdPrefix="workspace-pinned-target" />
</>
); );
} }
@@ -975,6 +989,12 @@ export function WorkspaceDesktopTabsRow({
/> />
<WorkspaceInlineAddTabButton <WorkspaceInlineAddTabButton
shortcutKeys={newTabKeys} shortcutKeys={newTabKeys}
onCreateAgentTab={handleCreateAgentTab}
onLayout={handleInlineAddButtonLayout}
/>
</ScrollView>
<View style={styles.tabsActions} onLayout={handleTabsActionsLayout}>
<WorkspaceTabRowExtras
onCreateAgentTab={handleCreateAgentTab} onCreateAgentTab={handleCreateAgentTab}
onCreateTerminal={handleCreateTerminal} onCreateTerminal={handleCreateTerminal}
onCreateBrowser={handleCreateBrowser} onCreateBrowser={handleCreateBrowser}
@@ -983,10 +1003,7 @@ export function WorkspaceDesktopTabsRow({
normalizedServerId={normalizedServerId} normalizedServerId={normalizedServerId}
showCreateBrowserTab={showCreateBrowserTab} showCreateBrowserTab={showCreateBrowserTab}
terminalDisabled={terminalDisabled} terminalDisabled={terminalDisabled}
onLayout={handleInlineAddButtonLayout}
/> />
</ScrollView>
<View style={styles.tabsActions} onLayout={handleTabsActionsLayout}>
{showPaneSplitActions ? ( {showPaneSplitActions ? (
<> <>
<SplitActionButton <SplitActionButton

View File

@@ -42,8 +42,8 @@ const styles = StyleSheet.create((theme) => ({
alignItems: "center", alignItems: "center",
}, },
pinButton: { pinButton: {
width: 28, width: 22,
height: 28, height: 22,
borderRadius: theme.borderRadius.md, borderRadius: theme.borderRadius.md,
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",

View File

@@ -9,6 +9,18 @@ interface PinnedTargetsState {
isPinned: (target: PinnedTabTarget) => boolean; isPinned: (target: PinnedTabTarget) => boolean;
} }
const DEFAULT_PINNED_TARGETS: PinnedTabTarget[] = [{ kind: "terminal" }, { kind: "browser" }];
function applyDefaultPinnedTargets(pinned: PinnedTabTarget[]): PinnedTabTarget[] {
const next = [...DEFAULT_PINNED_TARGETS];
for (const target of pinned) {
if (!isTargetPinned(next, target)) {
next.push(target);
}
}
return next;
}
export const usePinnedTargetsStore = create<PinnedTargetsState>()( export const usePinnedTargetsStore = create<PinnedTargetsState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
@@ -18,8 +30,16 @@ export const usePinnedTargetsStore = create<PinnedTargetsState>()(
}), }),
{ {
name: "pinned-tab-targets", name: "pinned-tab-targets",
version: 1,
storage: createJSONStorage(() => AsyncStorage), storage: createJSONStorage(() => AsyncStorage),
partialize: (state) => ({ pinned: state.pinned }), partialize: (state) => ({ pinned: state.pinned }),
migrate: (persistedState, version) => {
if (version === 0) {
const pinned = (persistedState as { pinned?: PinnedTabTarget[] } | null)?.pinned ?? [];
return { pinned: applyDefaultPinnedTargets(pinned) };
}
return persistedState as PinnedTargetsState;
},
}, },
), ),
); );