mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 10:12:44 +00:00
sidebar history: status indicator on session rows
Sidebar Session rows now mirror the workspace tab descriptor: same provider icon, same loader spinner, same status dot, same attention badge. Reuses WorkspaceTabPresentationResolver + WorkspaceTabIcon directly — no parallel implementation. Status reactivity stays inside the tab descriptor's own per-agent selector, so selectSidebarSessionSlice is unchanged and the parent ids hook keeps its reference stability. Navigation test mocks tab presentation so it doesn't pull the full workspace stack just to assert click behavior; production uses the real shared component.
This commit is contained in:
@@ -29,13 +29,21 @@ const {
|
||||
navigateToPreparedWorkspaceTabMock: vi.fn(),
|
||||
prepareWorkspaceTabMock: vi.fn(),
|
||||
testTheme: {
|
||||
borderRadius: { lg: 8 },
|
||||
borderRadius: { full: 999, lg: 8 },
|
||||
colors: {
|
||||
foreground: "#111111",
|
||||
foregroundMuted: "#666666",
|
||||
palette: {
|
||||
amber: { 500: "#f59e0b", 700: "#b45309" },
|
||||
blue: { 500: "#3b82f6" },
|
||||
green: { 500: "#22c55e" },
|
||||
red: { 500: "#ef4444" },
|
||||
},
|
||||
surface0: "#ffffff",
|
||||
surface2: "#eeeeee",
|
||||
surfaceSidebarHover: "#f5f5f5",
|
||||
},
|
||||
colorScheme: "light",
|
||||
fontSize: { sm: 14, xs: 12 },
|
||||
fontWeight: { medium: "500" },
|
||||
iconSize: { md: 20, sm: 16 },
|
||||
@@ -61,10 +69,34 @@ vi.mock("@/components/provider-icons", async () => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/screens/workspace/workspace-tab-presentation", async () => {
|
||||
const ReactModule = await import("react");
|
||||
return {
|
||||
WorkspaceTabPresentationResolver: ({ children }: { children: (presentation: {}) => unknown }) =>
|
||||
children({}),
|
||||
WorkspaceTabIcon: () =>
|
||||
ReactModule.createElement("span", { "data-testid": "workspace-tab-icon" }),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-native-reanimated", () => ({
|
||||
default: {
|
||||
View: "div",
|
||||
},
|
||||
Easing: {
|
||||
linear: vi.fn(),
|
||||
},
|
||||
makeMutable: (value: unknown) => ({ value }),
|
||||
useAnimatedStyle: (factory: () => unknown) => factory(),
|
||||
withRepeat: (value: unknown) => value,
|
||||
withTiming: (value: unknown) => value,
|
||||
}));
|
||||
|
||||
vi.mock("react-native-unistyles", () => ({
|
||||
StyleSheet: {
|
||||
create: (styles: unknown) => (typeof styles === "function" ? styles(testTheme) : styles),
|
||||
},
|
||||
withUnistyles: (Component: unknown) => Component,
|
||||
useUnistyles: () => ({
|
||||
theme: testTheme,
|
||||
rt: {},
|
||||
|
||||
@@ -10,12 +10,16 @@ import {
|
||||
import { useShallow } from "zustand/shallow";
|
||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||
import { SidebarAgentListSkeleton } from "@/components/sidebar-agent-list-skeleton";
|
||||
import { getProviderIcon } from "@/components/provider-icons";
|
||||
import {
|
||||
type AggregatedAgentIdEntry,
|
||||
useAggregatedAgentIds,
|
||||
useAggregatedAgentsInitialLoad,
|
||||
} from "@/hooks/use-aggregated-agents";
|
||||
import {
|
||||
WorkspaceTabIcon,
|
||||
WorkspaceTabPresentationResolver,
|
||||
} from "@/screens/workspace/workspace-tab-presentation";
|
||||
import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-types";
|
||||
import type { SidebarProjectEntry } from "@/hooks/use-sidebar-workspaces-list";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { navigateToPreparedWorkspaceTab, prepareWorkspaceTab } from "@/utils/workspace-navigation";
|
||||
@@ -149,13 +153,20 @@ const SidebarSessionRow = memo(function SidebarSessionRow({
|
||||
);
|
||||
|
||||
const titleStyle = useMemo(() => [styles.title, isHovered && styles.titleHovered], [isHovered]);
|
||||
const tabDescriptor = useMemo<WorkspaceTabDescriptor>(
|
||||
() => ({
|
||||
key: `agent_${id}`,
|
||||
tabId: `agent_${id}`,
|
||||
kind: "agent",
|
||||
target: { kind: "agent", agentId: id },
|
||||
}),
|
||||
[id],
|
||||
);
|
||||
|
||||
if (!agent || agent.archivedAt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ProviderIcon = getProviderIcon(agent.provider);
|
||||
|
||||
return (
|
||||
<View onPointerEnter={handlePointerEnter} onPointerLeave={handlePointerLeave}>
|
||||
<Pressable
|
||||
@@ -166,7 +177,20 @@ const SidebarSessionRow = memo(function SidebarSessionRow({
|
||||
testID={`sidebar-session-row-${serverId}-${id}`}
|
||||
>
|
||||
<View style={styles.providerIconWrap}>
|
||||
<ProviderIcon size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
|
||||
<WorkspaceTabPresentationResolver
|
||||
tab={tabDescriptor}
|
||||
serverId={serverId}
|
||||
workspaceId="sidebar-sessions"
|
||||
>
|
||||
{(presentation) => (
|
||||
<WorkspaceTabIcon
|
||||
presentation={presentation}
|
||||
active={isHovered}
|
||||
size={theme.iconSize.sm}
|
||||
statusDotBorderColor={isHovered ? theme.colors.surfaceSidebarHover : undefined}
|
||||
/>
|
||||
)}
|
||||
</WorkspaceTabPresentationResolver>
|
||||
</View>
|
||||
<Text style={titleStyle} numberOfLines={1}>
|
||||
{agent.title || "New session"}
|
||||
|
||||
Reference in New Issue
Block a user