mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 16:34:36 +00:00
Customize top-level sidebar items (#4203)
* feat(app): customize top-level sidebar items Let users reorder and hide built-in and plugin sidebar rows from Appearance settings while keeping the preference device-local. Add Search as a first-class row and standardize section explanations as header info tooltips. * fix(app): preserve consecutive sidebar preference edits Derive each sidebar change from the latest cached settings and retain unavailable plugin slots while editing the visible list. Keep E2E interaction selectors tied to accessible control names. * test(app): isolate provider settings unit boundary Keep the legacy provider-section test focused on provider behavior now that its shared section shell can render the settings info control.
This commit is contained in:
@@ -1,16 +1,5 @@
|
||||
import { router, usePathname } from "expo-router";
|
||||
import {
|
||||
CalendarClock,
|
||||
FolderPlus,
|
||||
GitBranch,
|
||||
History,
|
||||
Home,
|
||||
Plus,
|
||||
Search,
|
||||
Server,
|
||||
Settings,
|
||||
X,
|
||||
} from "lucide-react-native";
|
||||
import { router } from "expo-router";
|
||||
import { FolderPlus, GitBranch, Home, Server, Settings, X } from "lucide-react-native";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState, type RefObject } from "react";
|
||||
import {
|
||||
@@ -33,8 +22,8 @@ import {
|
||||
SIDEBAR_RESIZE_FAIL_OFFSET,
|
||||
} from "@/components/sidebar-resize-handle-layout";
|
||||
import { HostPicker } from "@/components/hosts/host-picker";
|
||||
import { SidebarHeaderRow } from "@/components/sidebar/sidebar-header-row";
|
||||
import { SidebarDisplayPreferencesMenu } from "@/components/sidebar/display-preferences/menu";
|
||||
import { SidebarNavRows } from "@/components/sidebar/sidebar-nav-rows";
|
||||
import { SidebarHelpMenu } from "@/components/sidebar/sidebar-help-menu";
|
||||
import { SidebarResizeHandle } from "@/components/sidebar-resize-handle";
|
||||
import { Shortcut } from "@/components/ui/shortcut";
|
||||
@@ -42,8 +31,6 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
|
||||
import { HEADER_INNER_HEIGHT, useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { useOpenAddProject } from "@/hooks/use-open-add-project";
|
||||
import { useShortcutKeys } from "@/hooks/use-shortcut-keys";
|
||||
import { canCreateWorktreeForProjectKind } from "@/projects/host-projects";
|
||||
import { useHostFeature } from "@/runtime/host-features";
|
||||
import {
|
||||
type SidebarProjectEntry,
|
||||
type SidebarWorkspaceEntry,
|
||||
@@ -54,28 +41,20 @@ import { RetainedPanelActivity } from "@/components/retained-panel";
|
||||
import type { SidebarWorkspaceGroup } from "@/components/sidebar/sidebar-labels";
|
||||
import type { SidebarProjectIconTarget } from "@/utils/sidebar-project-row-model";
|
||||
import { type SidebarGroupMode, useSidebarViewStore } from "@/stores/sidebar-view-store";
|
||||
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
|
||||
import { useHosts } from "@/runtime/host-runtime";
|
||||
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
|
||||
import { useWorkspace } from "@/stores/session-store-hooks";
|
||||
import { usePanelStore } from "@/stores/panel-store";
|
||||
import { useOwnsWindowChromeCorner, WindowChromeSafeArea } from "@/utils/desktop-window";
|
||||
import { useCloseAgentListGesture } from "@/mobile-panels/gestures";
|
||||
import { MobilePanelOverlay } from "@/mobile-panels/presentation";
|
||||
import {
|
||||
buildOpenProjectRoute,
|
||||
buildNewWorkspaceRoute,
|
||||
buildSchedulesRoute,
|
||||
buildSessionsRoute,
|
||||
buildSettingsAddHostRoute,
|
||||
buildSettingsRoute,
|
||||
} from "@/utils/host-routes";
|
||||
import { openHostOverview } from "@/navigation/settings-navigation";
|
||||
import type { ShortcutKey } from "@/utils/format-shortcut";
|
||||
import { SidebarAgentListSkeleton } from "./sidebar-agent-list-skeleton";
|
||||
import { SidebarCalloutSlot } from "./sidebar-callout-slot";
|
||||
import { SidebarWorkspaceList } from "./sidebar-workspace-list";
|
||||
import { PluginSidebarItems } from "@/plugins";
|
||||
|
||||
type SidebarTheme = ReturnType<typeof useUnistyles>["theme"];
|
||||
|
||||
@@ -102,20 +81,16 @@ interface SidebarSharedProps {
|
||||
handleHome: () => void;
|
||||
handleSettings: () => void;
|
||||
labels: SidebarLabels;
|
||||
newWorkspaceKeys: ShortcutKey[][] | null;
|
||||
handleAddHost: () => void;
|
||||
handleOpenHostSettings: (serverId: string) => void;
|
||||
}
|
||||
|
||||
interface SidebarLabels {
|
||||
addProject: string;
|
||||
newWorkspace: string;
|
||||
hosts: string;
|
||||
home: string;
|
||||
settings: string;
|
||||
searchHosts: string;
|
||||
sessions: string;
|
||||
schedules: string;
|
||||
closeSidebar: string;
|
||||
}
|
||||
|
||||
@@ -124,15 +99,11 @@ interface MobileSidebarProps extends SidebarSharedProps {
|
||||
insetsTop: number;
|
||||
insetsBottom: number;
|
||||
closeSidebar: () => void;
|
||||
handleViewMoreNavigate: () => void;
|
||||
handleViewSchedulesNavigate: () => void;
|
||||
}
|
||||
|
||||
interface DesktopSidebarProps extends SidebarSharedProps {
|
||||
insetsTop: number;
|
||||
active: boolean;
|
||||
handleViewMore: () => void;
|
||||
handleViewSchedules: () => void;
|
||||
}
|
||||
|
||||
export const LeftSidebar = memo(function LeftSidebar({ active }: { active: boolean }) {
|
||||
@@ -223,25 +194,13 @@ export const LeftSidebar = memo(function LeftSidebar({ active }: { active: boole
|
||||
router.push(buildOpenProjectRoute());
|
||||
}, []);
|
||||
|
||||
const handleViewMoreNavigate = useCallback(() => {
|
||||
router.push(buildSessionsRoute());
|
||||
}, []);
|
||||
|
||||
const handleViewSchedulesNavigate = useCallback(() => {
|
||||
router.push(buildSchedulesRoute());
|
||||
}, []);
|
||||
|
||||
const newWorkspaceKeys = useShortcutKeys("new-workspace");
|
||||
const labels = useMemo(
|
||||
(): SidebarLabels => ({
|
||||
addProject: t("sidebar.actions.addProject"),
|
||||
newWorkspace: t("sidebar.actions.newWorkspace"),
|
||||
hosts: t("sidebar.actions.hosts"),
|
||||
home: t("sidebar.actions.home"),
|
||||
settings: t("sidebar.actions.settings"),
|
||||
searchHosts: t("sidebar.host.searchPlaceholder"),
|
||||
sessions: t("sidebar.sections.sessions"),
|
||||
schedules: t("sidebar.sections.schedules"),
|
||||
closeSidebar: t("sidebar.actions.closeSidebar"),
|
||||
}),
|
||||
[t],
|
||||
@@ -265,7 +224,6 @@ export const LeftSidebar = memo(function LeftSidebar({ active }: { active: boole
|
||||
toggleProjectCollapsed,
|
||||
handleRefresh,
|
||||
labels,
|
||||
newWorkspaceKeys,
|
||||
};
|
||||
|
||||
if (isCompactLayout) {
|
||||
@@ -282,8 +240,6 @@ export const LeftSidebar = memo(function LeftSidebar({ active }: { active: boole
|
||||
handleSettings={handleSettingsMobile}
|
||||
handleAddHost={handleAddHostMobile}
|
||||
handleOpenHostSettings={handleOpenHostSettingsMobile}
|
||||
handleViewMoreNavigate={handleViewMoreNavigate}
|
||||
handleViewSchedulesNavigate={handleViewSchedulesNavigate}
|
||||
/>
|
||||
</RetainedPanelActivity>
|
||||
);
|
||||
@@ -300,8 +256,6 @@ export const LeftSidebar = memo(function LeftSidebar({ active }: { active: boole
|
||||
handleSettings={handleSettingsDesktop}
|
||||
handleAddHost={handleAddHostDesktop}
|
||||
handleOpenHostSettings={handleOpenHostSettingsDesktop}
|
||||
handleViewMore={handleViewMoreNavigate}
|
||||
handleViewSchedules={handleViewSchedulesNavigate}
|
||||
/>
|
||||
</RetainedPanelActivity>
|
||||
);
|
||||
@@ -487,61 +441,6 @@ function IconTooltipContent({
|
||||
);
|
||||
}
|
||||
|
||||
const SidebarNewWorkspaceHeaderRow = memo(function SidebarNewWorkspaceHeaderRow({
|
||||
label,
|
||||
testID,
|
||||
variant,
|
||||
shortcutKeys,
|
||||
onBeforeNavigate,
|
||||
}: {
|
||||
label: string;
|
||||
testID: string;
|
||||
variant: "header" | "compact";
|
||||
shortcutKeys: ShortcutKey[][] | null;
|
||||
onBeforeNavigate?: () => void;
|
||||
}) {
|
||||
const activeWorkspaceSelection = useActiveWorkspaceSelection();
|
||||
const activeWorkspaceServerId = activeWorkspaceSelection?.serverId ?? null;
|
||||
const activeWorkspaceId = activeWorkspaceSelection?.workspaceId ?? null;
|
||||
const activeWorkspace = useWorkspace(activeWorkspaceServerId, activeWorkspaceId);
|
||||
const supportsWorkspaceMultiplicity = useHostFeature(
|
||||
activeWorkspaceServerId,
|
||||
"workspaceMultiplicity",
|
||||
);
|
||||
const canUseActiveWorkspaceContext = Boolean(
|
||||
activeWorkspace &&
|
||||
(supportsWorkspaceMultiplicity || canCreateWorktreeForProjectKind(activeWorkspace.projectKind)),
|
||||
);
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
onBeforeNavigate?.();
|
||||
router.push(
|
||||
activeWorkspaceServerId
|
||||
? buildNewWorkspaceRoute(
|
||||
activeWorkspace && canUseActiveWorkspaceContext
|
||||
? {
|
||||
serverId: activeWorkspaceServerId,
|
||||
sourceDirectory: activeWorkspace.projectRootPath,
|
||||
projectId: activeWorkspace.projectId,
|
||||
}
|
||||
: { serverId: activeWorkspaceServerId },
|
||||
)
|
||||
: buildNewWorkspaceRoute(),
|
||||
);
|
||||
}, [activeWorkspace, activeWorkspaceServerId, canUseActiveWorkspaceContext, onBeforeNavigate]);
|
||||
|
||||
return (
|
||||
<SidebarHeaderRow
|
||||
icon={Plus}
|
||||
label={label}
|
||||
onPress={handlePress}
|
||||
testID={testID}
|
||||
variant={variant}
|
||||
shortcutKeys={shortcutKeys}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
function SidebarFooter({
|
||||
theme,
|
||||
handleOpenProject,
|
||||
@@ -622,7 +521,6 @@ function MobileSidebar({
|
||||
shortcutIndexByWorkspaceKey,
|
||||
toggleProjectCollapsed,
|
||||
handleRefresh,
|
||||
newWorkspaceKeys,
|
||||
handleOpenProject,
|
||||
handleHome,
|
||||
handleSettings,
|
||||
@@ -632,25 +530,10 @@ function MobileSidebar({
|
||||
insetsTop,
|
||||
insetsBottom,
|
||||
closeSidebar,
|
||||
handleViewMoreNavigate,
|
||||
handleViewSchedulesNavigate,
|
||||
}: MobileSidebarProps) {
|
||||
const pathname = usePathname();
|
||||
const hasActiveHostFilter = useSidebarViewStore((state) => state.hostFilters.length > 0);
|
||||
const isSessionsActive = pathname.includes("/sessions");
|
||||
const isSchedulesActive = pathname.includes("/schedules");
|
||||
const { gesture: closeGesture, gestureRef: closeGestureRef } = useCloseAgentListGesture();
|
||||
|
||||
const handleViewMore = useCallback(() => {
|
||||
closeSidebar();
|
||||
handleViewMoreNavigate();
|
||||
}, [closeSidebar, handleViewMoreNavigate]);
|
||||
|
||||
const handleViewSchedules = useCallback(() => {
|
||||
closeSidebar();
|
||||
handleViewSchedulesNavigate();
|
||||
}, [closeSidebar, handleViewSchedulesNavigate]);
|
||||
|
||||
const handleWorkspacePress = useCallback(() => {
|
||||
closeSidebar();
|
||||
}, [closeSidebar]);
|
||||
@@ -672,32 +555,7 @@ function MobileSidebar({
|
||||
>
|
||||
<View style={styles.sidebarContent} pointerEvents="auto">
|
||||
<WindowChromeSafeArea placement="below" />
|
||||
<View style={styles.sidebarHeaderGroup}>
|
||||
<SidebarNewWorkspaceHeaderRow
|
||||
label={labels.newWorkspace}
|
||||
testID="sidebar-global-new-workspace"
|
||||
variant="compact"
|
||||
shortcutKeys={newWorkspaceKeys}
|
||||
onBeforeNavigate={closeSidebar}
|
||||
/>
|
||||
<SidebarHeaderRow
|
||||
icon={History}
|
||||
label={labels.sessions}
|
||||
onPress={handleViewMore}
|
||||
isActive={isSessionsActive}
|
||||
testID="sidebar-sessions"
|
||||
variant="compact"
|
||||
/>
|
||||
<SidebarHeaderRow
|
||||
icon={CalendarClock}
|
||||
label={labels.schedules}
|
||||
onPress={handleViewSchedules}
|
||||
isActive={isSchedulesActive}
|
||||
testID="sidebar-schedules"
|
||||
variant="compact"
|
||||
/>
|
||||
<PluginSidebarItems onBeforeNavigate={closeSidebar} />
|
||||
</View>
|
||||
<SidebarNavRows style={styles.sidebarHeaderGroup} onBeforeNavigate={closeSidebar} />
|
||||
<WindowChromeSafeArea placement="inline" style={styles.mobileCloseButtonRow}>
|
||||
<Pressable
|
||||
style={styles.mobileCloseButton}
|
||||
@@ -774,7 +632,6 @@ function DesktopSidebar({
|
||||
shortcutIndexByWorkspaceKey,
|
||||
toggleProjectCollapsed,
|
||||
handleRefresh,
|
||||
newWorkspaceKeys,
|
||||
handleOpenProject,
|
||||
handleHome,
|
||||
handleSettings,
|
||||
@@ -783,14 +640,9 @@ function DesktopSidebar({
|
||||
handleOpenHostSettings,
|
||||
insetsTop,
|
||||
active,
|
||||
handleViewMore,
|
||||
handleViewSchedules,
|
||||
}: DesktopSidebarProps) {
|
||||
const ownsTopLeft = useOwnsWindowChromeCorner("top-left");
|
||||
const pathname = usePathname();
|
||||
const hasActiveHostFilter = useSidebarViewStore((state) => state.hostFilters.length > 0);
|
||||
const isSessionsActive = pathname.includes("/sessions");
|
||||
const isSchedulesActive = pathname.includes("/schedules");
|
||||
const sidebarWidth = usePanelStore((state) => state.sidebarWidth);
|
||||
const setSidebarWidth = usePanelStore((state) => state.setSidebarWidth);
|
||||
const { width: viewportWidth } = useWindowDimensions();
|
||||
@@ -898,31 +750,7 @@ function DesktopSidebar({
|
||||
) : (
|
||||
<TitlebarDragRegion />
|
||||
)}
|
||||
<View style={sidebarHeaderGroupStyle}>
|
||||
<SidebarNewWorkspaceHeaderRow
|
||||
label={labels.newWorkspace}
|
||||
testID="sidebar-global-new-workspace"
|
||||
variant="compact"
|
||||
shortcutKeys={newWorkspaceKeys}
|
||||
/>
|
||||
<SidebarHeaderRow
|
||||
icon={History}
|
||||
label={labels.sessions}
|
||||
onPress={handleViewMore}
|
||||
isActive={isSessionsActive}
|
||||
testID="sidebar-sessions"
|
||||
variant="compact"
|
||||
/>
|
||||
<SidebarHeaderRow
|
||||
icon={CalendarClock}
|
||||
label={labels.schedules}
|
||||
onPress={handleViewSchedules}
|
||||
isActive={isSchedulesActive}
|
||||
testID="sidebar-schedules"
|
||||
variant="compact"
|
||||
/>
|
||||
<PluginSidebarItems />
|
||||
</View>
|
||||
<SidebarNavRows style={sidebarHeaderGroupStyle} />
|
||||
</View>
|
||||
|
||||
{isInitialLoad && !hasActiveHostFilter ? (
|
||||
@@ -971,45 +799,10 @@ function DesktopSidebar({
|
||||
}
|
||||
|
||||
function WorkspacesSectionHeader() {
|
||||
const { theme } = useUnistyles();
|
||||
const setCommandCenterOpen = useKeyboardShortcutsStore((state) => state.setCommandCenterOpen);
|
||||
const commandCenterKeys = useShortcutKeys("toggle-command-center");
|
||||
const handleSearchPress = useCallback(() => setCommandCenterOpen(true), [setCommandCenterOpen]);
|
||||
const searchButtonStyle = useCallback(
|
||||
({ hovered = false, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [
|
||||
styles.workspacesHeaderIconButton,
|
||||
(hovered || pressed) && styles.workspacesHeaderIconButtonHovered,
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.workspacesSectionHeader}>
|
||||
<Text style={styles.workspacesSectionTitle}>Workspaces</Text>
|
||||
<View style={styles.workspacesSectionActions}>
|
||||
<Tooltip delayDuration={300}>
|
||||
<TooltipTrigger asChild>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Open command center"
|
||||
testID="sidebar-command-center-search"
|
||||
style={searchButtonStyle}
|
||||
onPress={handleSearchPress}
|
||||
>
|
||||
{({ hovered, pressed }) => (
|
||||
<Search
|
||||
size={14}
|
||||
color={
|
||||
hovered || pressed ? theme.colors.foreground : theme.colors.foregroundMuted
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Pressable>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" align="center" offset={8}>
|
||||
<IconTooltipContent label="Search" shortcutKeys={commandCenterKeys} />
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip delayDuration={300}>
|
||||
<TooltipTrigger asChild>
|
||||
<View>
|
||||
@@ -1075,16 +868,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
alignItems: "center",
|
||||
gap: theme.spacing[1],
|
||||
},
|
||||
workspacesHeaderIconButton: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: theme.borderRadius.md,
|
||||
},
|
||||
workspacesHeaderIconButtonHovered: {
|
||||
backgroundColor: theme.colors.surfaceSidebarHover,
|
||||
},
|
||||
sidebarContent: {
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
import { router, usePathname } from "expo-router";
|
||||
import { CalendarClock, History, Plus, Search } from "lucide-react-native";
|
||||
import { memo, useCallback, useMemo, type ComponentType } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View, type StyleProp, type ViewStyle } from "react-native";
|
||||
import { SidebarHeaderRow } from "@/components/sidebar/sidebar-header-row";
|
||||
import { useShortcutKeys } from "@/hooks/use-shortcut-keys";
|
||||
import { PluginSidebarItemRow } from "@/plugins/sidebar-items";
|
||||
import { canCreateWorktreeForProjectKind } from "@/projects/host-projects";
|
||||
import { useHostFeature } from "@/runtime/host-features";
|
||||
import {
|
||||
builtinSidebarNavLabelKey,
|
||||
builtinSidebarNavShortcutAction,
|
||||
type BuiltinSidebarNavId,
|
||||
} from "@/sidebar-nav/model";
|
||||
import { useSidebarNavItems } from "@/sidebar-nav/use-sidebar-nav-items";
|
||||
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
|
||||
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
|
||||
import { useWorkspace } from "@/stores/session-store-hooks";
|
||||
import {
|
||||
buildNewWorkspaceRoute,
|
||||
buildSchedulesRoute,
|
||||
buildSessionsRoute,
|
||||
} from "@/utils/host-routes";
|
||||
|
||||
interface SidebarNavRowProps {
|
||||
onBeforeNavigate?: () => void;
|
||||
}
|
||||
|
||||
interface SidebarNavRowsProps extends SidebarNavRowProps {
|
||||
/** Style for the group wrapper, which the sidebar owns. */
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top-level sidebar navigation, ordered and filtered by the user's
|
||||
* `sidebarNavItems` preference. Renders nothing — not even the bordered group
|
||||
* wrapper — when every item is hidden.
|
||||
*/
|
||||
export function SidebarNavRows({ style, onBeforeNavigate }: SidebarNavRowsProps) {
|
||||
const { items } = useSidebarNavItems();
|
||||
const visibleItems = useMemo(() => items.filter((item) => item.visible), [items]);
|
||||
|
||||
if (visibleItems.length === 0) return null;
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
{visibleItems.map((item) => {
|
||||
if (item.kind === "plugin") {
|
||||
return (
|
||||
<PluginSidebarItemRow
|
||||
key={item.key}
|
||||
group={item.group}
|
||||
onBeforeNavigate={onBeforeNavigate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const Row = BUILTIN_ROWS[item.id];
|
||||
return <Row key={item.key} onBeforeNavigate={onBeforeNavigate} />;
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const SidebarNewWorkspaceRow = memo(function SidebarNewWorkspaceRow({
|
||||
onBeforeNavigate,
|
||||
}: SidebarNavRowProps) {
|
||||
const { t } = useTranslation();
|
||||
const shortcutKeys = useShortcutKeys(builtinSidebarNavShortcutAction("new-workspace"));
|
||||
const activeWorkspaceSelection = useActiveWorkspaceSelection();
|
||||
const activeWorkspaceServerId = activeWorkspaceSelection?.serverId ?? null;
|
||||
const activeWorkspaceId = activeWorkspaceSelection?.workspaceId ?? null;
|
||||
const activeWorkspace = useWorkspace(activeWorkspaceServerId, activeWorkspaceId);
|
||||
const supportsWorkspaceMultiplicity = useHostFeature(
|
||||
activeWorkspaceServerId,
|
||||
"workspaceMultiplicity",
|
||||
);
|
||||
const canUseActiveWorkspaceContext = Boolean(
|
||||
activeWorkspace &&
|
||||
(supportsWorkspaceMultiplicity || canCreateWorktreeForProjectKind(activeWorkspace.projectKind)),
|
||||
);
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
onBeforeNavigate?.();
|
||||
router.push(
|
||||
activeWorkspaceServerId
|
||||
? buildNewWorkspaceRoute(
|
||||
activeWorkspace && canUseActiveWorkspaceContext
|
||||
? {
|
||||
serverId: activeWorkspaceServerId,
|
||||
sourceDirectory: activeWorkspace.projectRootPath,
|
||||
projectId: activeWorkspace.projectId,
|
||||
}
|
||||
: { serverId: activeWorkspaceServerId },
|
||||
)
|
||||
: buildNewWorkspaceRoute(),
|
||||
);
|
||||
}, [activeWorkspace, activeWorkspaceServerId, canUseActiveWorkspaceContext, onBeforeNavigate]);
|
||||
|
||||
return (
|
||||
<SidebarHeaderRow
|
||||
icon={Plus}
|
||||
label={t(builtinSidebarNavLabelKey("new-workspace"))}
|
||||
onPress={handlePress}
|
||||
testID="sidebar-global-new-workspace"
|
||||
variant="compact"
|
||||
shortcutKeys={shortcutKeys}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
function SidebarHistoryRow({ onBeforeNavigate }: SidebarNavRowProps) {
|
||||
const { t } = useTranslation();
|
||||
const pathname = usePathname();
|
||||
const handlePress = useCallback(() => {
|
||||
onBeforeNavigate?.();
|
||||
router.push(buildSessionsRoute());
|
||||
}, [onBeforeNavigate]);
|
||||
|
||||
return (
|
||||
<SidebarHeaderRow
|
||||
icon={History}
|
||||
label={t(builtinSidebarNavLabelKey("history"))}
|
||||
onPress={handlePress}
|
||||
isActive={pathname.includes("/sessions")}
|
||||
testID="sidebar-sessions"
|
||||
variant="compact"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SidebarSearchRow({ onBeforeNavigate }: SidebarNavRowProps) {
|
||||
const { t } = useTranslation();
|
||||
const shortcutKeys = useShortcutKeys(builtinSidebarNavShortcutAction("search"));
|
||||
const setCommandCenterOpen = useKeyboardShortcutsStore((state) => state.setCommandCenterOpen);
|
||||
const handlePress = useCallback(() => {
|
||||
onBeforeNavigate?.();
|
||||
setCommandCenterOpen(true);
|
||||
}, [onBeforeNavigate, setCommandCenterOpen]);
|
||||
|
||||
return (
|
||||
<SidebarHeaderRow
|
||||
icon={Search}
|
||||
label={t(builtinSidebarNavLabelKey("search"))}
|
||||
onPress={handlePress}
|
||||
testID="sidebar-search"
|
||||
variant="compact"
|
||||
shortcutKeys={shortcutKeys}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SidebarSchedulesRow({ onBeforeNavigate }: SidebarNavRowProps) {
|
||||
const { t } = useTranslation();
|
||||
const pathname = usePathname();
|
||||
const handlePress = useCallback(() => {
|
||||
onBeforeNavigate?.();
|
||||
router.push(buildSchedulesRoute());
|
||||
}, [onBeforeNavigate]);
|
||||
|
||||
return (
|
||||
<SidebarHeaderRow
|
||||
icon={CalendarClock}
|
||||
label={t(builtinSidebarNavLabelKey("schedules"))}
|
||||
onPress={handlePress}
|
||||
isActive={pathname.includes("/schedules")}
|
||||
testID="sidebar-schedules"
|
||||
variant="compact"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const BUILTIN_ROWS: Record<BuiltinSidebarNavId, ComponentType<SidebarNavRowProps>> = {
|
||||
"new-workspace": SidebarNewWorkspaceRow,
|
||||
history: SidebarHistoryRow,
|
||||
search: SidebarSearchRow,
|
||||
schedules: SidebarSchedulesRow,
|
||||
};
|
||||
Reference in New Issue
Block a user