refactor(app): rename agents route to sessions with improved list layout
This commit is contained in:
@@ -645,7 +645,7 @@ export default function RootLayout() {
|
|||||||
options={{ gestureEnabled: false, freezeOnBlur: true }}
|
options={{ gestureEnabled: false, freezeOnBlur: true }}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen name="h/[serverId]/index" />
|
<Stack.Screen name="h/[serverId]/index" />
|
||||||
<Stack.Screen name="h/[serverId]/agents" />
|
<Stack.Screen name="h/[serverId]/sessions" />
|
||||||
<Stack.Screen name="h/[serverId]/open-project" />
|
<Stack.Screen name="h/[serverId]/open-project" />
|
||||||
<Stack.Screen name="h/[serverId]/settings" />
|
<Stack.Screen name="h/[serverId]/settings" />
|
||||||
<Stack.Screen name="pair-scan" />
|
<Stack.Screen name="pair-scan" />
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import { AgentsScreen } from "@/screens/agents-screen";
|
import { SessionsScreen } from "@/screens/sessions-screen";
|
||||||
|
|
||||||
export default function HostAgentsRoute() {
|
export default function HostAgentsRoute() {
|
||||||
const params = useLocalSearchParams<{ serverId?: string }>();
|
const params = useLocalSearchParams<{ serverId?: string }>();
|
||||||
const serverId = typeof params.serverId === "string" ? params.serverId : "";
|
const serverId = typeof params.serverId === "string" ? params.serverId : "";
|
||||||
|
|
||||||
return <AgentsScreen serverId={serverId} />;
|
return <SessionsScreen serverId={serverId} />;
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ import { formatTimeAgo } from '@/utils/time'
|
|||||||
import { shortenPath } from '@/utils/shorten-path'
|
import { shortenPath } from '@/utils/shorten-path'
|
||||||
import { type AggregatedAgent } from '@/hooks/use-aggregated-agents'
|
import { type AggregatedAgent } from '@/hooks/use-aggregated-agents'
|
||||||
import { useSessionStore } from '@/stores/session-store'
|
import { useSessionStore } from '@/stores/session-store'
|
||||||
import { AgentStatusDot } from '@/components/agent-status-dot'
|
import { Archive } from 'lucide-react-native'
|
||||||
import { prepareWorkspaceTab } from '@/utils/workspace-navigation'
|
import { prepareWorkspaceTab } from '@/utils/workspace-navigation'
|
||||||
|
|
||||||
interface AgentListProps {
|
interface AgentListProps {
|
||||||
@@ -29,11 +29,9 @@ interface AgentListProps {
|
|||||||
showAttentionIndicator?: boolean
|
showAttentionIndicator?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AgentListSection {
|
type FlatListItem =
|
||||||
key: string
|
| { type: 'header'; key: string; title: string }
|
||||||
title: string
|
| { type: 'agent'; key: string; agent: AggregatedAgent }
|
||||||
data: AggregatedAgent[]
|
|
||||||
}
|
|
||||||
|
|
||||||
function deriveDateSectionLabel(lastActivityAt: Date): string {
|
function deriveDateSectionLabel(lastActivityAt: Date): string {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
@@ -82,9 +80,11 @@ function formatStatusLabel(status: AggregatedAgent['status']): string {
|
|||||||
|
|
||||||
function SessionBadge({
|
function SessionBadge({
|
||||||
label,
|
label,
|
||||||
|
icon,
|
||||||
tone = 'neutral',
|
tone = 'neutral',
|
||||||
}: {
|
}: {
|
||||||
label: string
|
label: string
|
||||||
|
icon?: ReactElement
|
||||||
tone?: 'neutral' | 'warning' | 'danger'
|
tone?: 'neutral' | 'warning' | 'danger'
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@@ -95,6 +95,7 @@ function SessionBadge({
|
|||||||
tone === 'danger' && styles.badgeDanger,
|
tone === 'danger' && styles.badgeDanger,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
{icon}
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.badgeText,
|
styles.badgeText,
|
||||||
@@ -123,6 +124,7 @@ function SessionRow({
|
|||||||
onPress: (agent: AggregatedAgent) => void
|
onPress: (agent: AggregatedAgent) => void
|
||||||
onLongPress: (agent: AggregatedAgent) => void
|
onLongPress: (agent: AggregatedAgent) => void
|
||||||
}) {
|
}) {
|
||||||
|
const { theme } = useUnistyles()
|
||||||
const timeAgo = formatTimeAgo(agent.lastActivityAt)
|
const timeAgo = formatTimeAgo(agent.lastActivityAt)
|
||||||
const agentKey = `${agent.serverId}:${agent.id}`
|
const agentKey = `${agent.serverId}:${agent.id}`
|
||||||
const isSelected = selectedAgentId === agentKey
|
const isSelected = selectedAgentId === agentKey
|
||||||
@@ -141,9 +143,6 @@ function SessionRow({
|
|||||||
onLongPress={() => onLongPress(agent)}
|
onLongPress={() => onLongPress(agent)}
|
||||||
testID={`agent-row-${agent.serverId}-${agent.id}`}
|
testID={`agent-row-${agent.serverId}-${agent.id}`}
|
||||||
>
|
>
|
||||||
<View style={styles.rowLeading}>
|
|
||||||
<AgentStatusDot status={agent.status} requiresAttention={agent.requiresAttention} />
|
|
||||||
</View>
|
|
||||||
<View style={styles.rowContent}>
|
<View style={styles.rowContent}>
|
||||||
<View style={styles.rowTitleRow}>
|
<View style={styles.rowTitleRow}>
|
||||||
<Text
|
<Text
|
||||||
@@ -152,7 +151,12 @@ function SessionRow({
|
|||||||
>
|
>
|
||||||
{agent.title || 'New session'}
|
{agent.title || 'New session'}
|
||||||
</Text>
|
</Text>
|
||||||
{agent.archivedAt ? <SessionBadge label="Archived" /> : null}
|
{agent.archivedAt ? (
|
||||||
|
<SessionBadge
|
||||||
|
label="Archived"
|
||||||
|
icon={<Archive size={theme.fontSize.xs} color={theme.colors.foregroundMuted} />}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
{(agent.pendingPermissionCount ?? 0) > 0 ? (
|
{(agent.pendingPermissionCount ?? 0) > 0 ? (
|
||||||
<SessionBadge label={`${agent.pendingPermissionCount} pending`} tone="warning" />
|
<SessionBadge label={`${agent.pendingPermissionCount} pending`} tone="warning" />
|
||||||
) : null}
|
) : null}
|
||||||
@@ -198,47 +202,6 @@ function SessionRow({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SessionTableSection({
|
|
||||||
section,
|
|
||||||
isMobile,
|
|
||||||
selectedAgentId,
|
|
||||||
showAttentionIndicator,
|
|
||||||
onAgentPress,
|
|
||||||
onAgentLongPress,
|
|
||||||
}: {
|
|
||||||
section: AgentListSection
|
|
||||||
isMobile: boolean
|
|
||||||
selectedAgentId?: string
|
|
||||||
showAttentionIndicator: boolean
|
|
||||||
onAgentPress: (agent: AggregatedAgent) => void
|
|
||||||
onAgentLongPress: (agent: AggregatedAgent) => void
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<View style={styles.sectionBlock}>
|
|
||||||
<View style={styles.sectionHeading}>
|
|
||||||
<Text style={styles.sectionTitle}>{section.title}</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.listCard}>
|
|
||||||
{section.data.map((agent, index) => (
|
|
||||||
<View
|
|
||||||
key={`${agent.serverId}:${agent.id}`}
|
|
||||||
style={index > 0 ? styles.rowDivider : undefined}
|
|
||||||
>
|
|
||||||
<SessionRow
|
|
||||||
agent={agent}
|
|
||||||
isMobile={isMobile}
|
|
||||||
selectedAgentId={selectedAgentId}
|
|
||||||
showAttentionIndicator={showAttentionIndicator}
|
|
||||||
onPress={onAgentPress}
|
|
||||||
onLongPress={onAgentLongPress}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AgentList({
|
export function AgentList({
|
||||||
agents,
|
agents,
|
||||||
@@ -276,6 +239,7 @@ export function AgentList({
|
|||||||
serverId,
|
serverId,
|
||||||
workspaceId: agent.cwd,
|
workspaceId: agent.cwd,
|
||||||
target: { kind: 'agent', agentId },
|
target: { kind: 'agent', agentId },
|
||||||
|
pin: Boolean(agent.archivedAt),
|
||||||
})
|
})
|
||||||
router.navigate(route as any)
|
router.navigate(route as any)
|
||||||
},
|
},
|
||||||
@@ -298,7 +262,7 @@ export function AgentList({
|
|||||||
setActionAgent(null)
|
setActionAgent(null)
|
||||||
}, [actionAgent, actionClient])
|
}, [actionAgent, actionClient])
|
||||||
|
|
||||||
const sections = useMemo((): AgentListSection[] => {
|
const flatItems = useMemo((): FlatListItem[] => {
|
||||||
const order = ['Today', 'Yesterday', 'This week', 'This month', 'Older'] as const
|
const order = ['Today', 'Yesterday', 'This week', 'This month', 'Older'] as const
|
||||||
const buckets = new Map<string, AggregatedAgent[]>()
|
const buckets = new Map<string, AggregatedAgent[]>()
|
||||||
for (const agent of agents) {
|
for (const agent of agents) {
|
||||||
@@ -308,41 +272,53 @@ export function AgentList({
|
|||||||
buckets.set(label, existing)
|
buckets.set(label, existing)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: AgentListSection[] = []
|
const result: FlatListItem[] = []
|
||||||
for (const label of order) {
|
for (const label of order) {
|
||||||
const data = buckets.get(label)
|
const data = buckets.get(label)
|
||||||
if (!data || data.length === 0) {
|
if (!data || data.length === 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result.push({ key: `date:${label}`, title: label, data })
|
result.push({ type: 'header', key: `header:${label}`, title: label })
|
||||||
|
for (const agent of data) {
|
||||||
|
result.push({ type: 'agent', key: `${agent.serverId}:${agent.id}`, agent })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}, [agents])
|
}, [agents])
|
||||||
|
|
||||||
const renderSection: ListRenderItem<AgentListSection> = useCallback(
|
const renderItem: ListRenderItem<FlatListItem> = useCallback(
|
||||||
({ item: section }) => (
|
({ item }) => {
|
||||||
<SessionTableSection
|
if (item.type === 'header') {
|
||||||
section={section}
|
return (
|
||||||
isMobile={isMobile}
|
<View style={styles.sectionHeading}>
|
||||||
selectedAgentId={selectedAgentId}
|
<Text style={styles.sectionTitle}>{item.title}</Text>
|
||||||
showAttentionIndicator={showAttentionIndicator}
|
</View>
|
||||||
onAgentPress={handleAgentPress}
|
)
|
||||||
onAgentLongPress={handleAgentLongPress}
|
}
|
||||||
/>
|
return (
|
||||||
),
|
<SessionRow
|
||||||
|
agent={item.agent}
|
||||||
|
isMobile={isMobile}
|
||||||
|
selectedAgentId={selectedAgentId}
|
||||||
|
showAttentionIndicator={showAttentionIndicator}
|
||||||
|
onPress={handleAgentPress}
|
||||||
|
onLongPress={handleAgentLongPress}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
[handleAgentLongPress, handleAgentPress, isMobile, selectedAgentId, showAttentionIndicator]
|
[handleAgentLongPress, handleAgentPress, isMobile, selectedAgentId, showAttentionIndicator]
|
||||||
)
|
)
|
||||||
|
|
||||||
const keyExtractor = useCallback((section: AgentListSection) => section.key, [])
|
const keyExtractor = useCallback((item: FlatListItem) => item.key, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FlatList
|
<FlatList
|
||||||
data={sections}
|
data={flatItems}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
contentContainerStyle={styles.listContent}
|
contentContainerStyle={styles.listContent}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={keyExtractor}
|
||||||
renderItem={renderSection}
|
renderItem={renderItem}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
ListFooterComponent={listFooterComponent}
|
ListFooterComponent={listFooterComponent}
|
||||||
@@ -417,18 +393,16 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
xs: theme.spacing[3],
|
xs: theme.spacing[3],
|
||||||
md: theme.spacing[6],
|
md: theme.spacing[6],
|
||||||
},
|
},
|
||||||
paddingTop: theme.spacing[2],
|
paddingTop: theme.spacing[4],
|
||||||
paddingBottom: theme.spacing[6],
|
paddingBottom: theme.spacing[6],
|
||||||
gap: theme.spacing[1],
|
gap: theme.spacing[1],
|
||||||
},
|
},
|
||||||
sectionBlock: {
|
|
||||||
marginTop: theme.spacing[2],
|
|
||||||
},
|
|
||||||
sectionHeading: {
|
sectionHeading: {
|
||||||
|
marginTop: theme.spacing[2],
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: theme.spacing[3],
|
gap: theme.spacing[3],
|
||||||
paddingHorizontal: theme.spacing[1],
|
paddingHorizontal: theme.spacing[3],
|
||||||
marginBottom: theme.spacing[2],
|
marginBottom: theme.spacing[2],
|
||||||
},
|
},
|
||||||
sectionTitle: {
|
sectionTitle: {
|
||||||
@@ -436,23 +410,6 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
fontWeight: theme.fontWeight.medium,
|
fontWeight: theme.fontWeight.medium,
|
||||||
color: theme.colors.foregroundMuted,
|
color: theme.colors.foregroundMuted,
|
||||||
},
|
},
|
||||||
listCard: {
|
|
||||||
overflow: {
|
|
||||||
xs: 'hidden' as const,
|
|
||||||
md: 'visible' as const,
|
|
||||||
},
|
|
||||||
borderRadius: {
|
|
||||||
xs: theme.borderRadius.lg,
|
|
||||||
md: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
rowDivider: {
|
|
||||||
borderTopWidth: {
|
|
||||||
xs: StyleSheet.hairlineWidth,
|
|
||||||
md: 0,
|
|
||||||
},
|
|
||||||
borderTopColor: theme.colors.border,
|
|
||||||
},
|
|
||||||
row: {
|
row: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -467,9 +424,6 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
md: 0,
|
md: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rowLeading: {
|
|
||||||
marginRight: theme.spacing[3],
|
|
||||||
},
|
|
||||||
rowContent: {
|
rowContent: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
@@ -502,7 +456,7 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
sessionTitle: {
|
sessionTitle: {
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
fontSize: theme.fontSize.sm,
|
fontSize: theme.fontSize.sm,
|
||||||
fontWeight: '500',
|
fontWeight: '400',
|
||||||
color: theme.colors.foreground,
|
color: theme.colors.foreground,
|
||||||
opacity: 0.86,
|
opacity: 0.86,
|
||||||
},
|
},
|
||||||
@@ -535,6 +489,9 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
textAlign: 'right' as const,
|
textAlign: 'right' as const,
|
||||||
},
|
},
|
||||||
badge: {
|
badge: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing[1],
|
||||||
paddingHorizontal: theme.spacing[2],
|
paddingHorizontal: theme.spacing[2],
|
||||||
paddingVertical: theme.spacing[1],
|
paddingVertical: theme.spacing[1],
|
||||||
borderRadius: theme.borderRadius.full,
|
borderRadius: theme.borderRadius.full,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import { getHostRuntimeStore, useHosts } from '@/runtime/host-runtime'
|
|||||||
import { formatConnectionStatus } from '@/utils/daemons'
|
import { formatConnectionStatus } from '@/utils/daemons'
|
||||||
import { HEADER_INNER_HEIGHT, HEADER_INNER_HEIGHT_MOBILE } from '@/constants/layout'
|
import { HEADER_INNER_HEIGHT, HEADER_INNER_HEIGHT_MOBILE } from '@/constants/layout'
|
||||||
import {
|
import {
|
||||||
buildHostAgentsRoute,
|
buildHostSessionsRoute,
|
||||||
buildHostSettingsRoute,
|
buildHostSettingsRoute,
|
||||||
mapPathnameToServer,
|
mapPathnameToServer,
|
||||||
parseServerIdFromPathname,
|
parseServerIdFromPathname,
|
||||||
@@ -221,7 +221,7 @@ export const LeftSidebar = memo(function LeftSidebar({ selectedAgentId: _selecte
|
|||||||
if (!activeServerId) {
|
if (!activeServerId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
router.push(buildHostAgentsRoute(activeServerId) as any)
|
router.push(buildHostSessionsRoute(activeServerId) as any)
|
||||||
}, [activeServerId])
|
}, [activeServerId])
|
||||||
|
|
||||||
const handleHostSelect = useCallback(
|
const handleHostSelect = useCallback(
|
||||||
@@ -283,6 +283,41 @@ export const LeftSidebar = memo(function LeftSidebar({ selectedAgentId: _selecte
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function SessionsButton({ onPress }: { onPress: () => void }) {
|
||||||
|
const { theme } = useUnistyles()
|
||||||
|
const pathname = usePathname()
|
||||||
|
const isActive = pathname.includes('/sessions')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
style={({ hovered }) => [
|
||||||
|
styles.newAgentButton,
|
||||||
|
hovered && styles.newAgentButtonHovered,
|
||||||
|
isActive && styles.newAgentButtonActive,
|
||||||
|
]}
|
||||||
|
testID="sidebar-sessions"
|
||||||
|
accessible
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel="Sessions"
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
{({ hovered }) => (
|
||||||
|
<>
|
||||||
|
<MessagesSquare
|
||||||
|
size={theme.iconSize.md}
|
||||||
|
color={hovered || isActive ? theme.colors.foreground : theme.colors.foregroundMuted}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={[styles.newAgentButtonText, (hovered || isActive) && styles.newAgentButtonTextHovered]}
|
||||||
|
>
|
||||||
|
Sessions
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function MobileSidebar({
|
function MobileSidebar({
|
||||||
theme,
|
theme,
|
||||||
activeServerId,
|
activeServerId,
|
||||||
@@ -457,28 +492,7 @@ function MobileSidebar({
|
|||||||
<View style={styles.sidebarContent} pointerEvents="auto">
|
<View style={styles.sidebarContent} pointerEvents="auto">
|
||||||
<View style={styles.sidebarHeader}>
|
<View style={styles.sidebarHeader}>
|
||||||
<View style={styles.sidebarHeaderRow}>
|
<View style={styles.sidebarHeaderRow}>
|
||||||
<Pressable
|
<SessionsButton onPress={handleViewMore} />
|
||||||
style={styles.newAgentButton}
|
|
||||||
testID="sidebar-sessions"
|
|
||||||
accessible
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel="Sessions"
|
|
||||||
onPress={handleViewMore}
|
|
||||||
>
|
|
||||||
{({ hovered }) => (
|
|
||||||
<>
|
|
||||||
<MessagesSquare
|
|
||||||
size={theme.iconSize.md}
|
|
||||||
color={hovered ? theme.colors.foreground : theme.colors.foregroundMuted}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={[styles.newAgentButtonText, hovered && styles.newAgentButtonTextHovered]}
|
|
||||||
>
|
|
||||||
Sessions
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -623,28 +637,7 @@ function DesktopSidebar({
|
|||||||
) : null}
|
) : null}
|
||||||
<View style={styles.sidebarHeader} {...dragHandlers}>
|
<View style={styles.sidebarHeader} {...dragHandlers}>
|
||||||
<View style={styles.sidebarHeaderRow}>
|
<View style={styles.sidebarHeaderRow}>
|
||||||
<Pressable
|
<SessionsButton onPress={handleViewMore} />
|
||||||
style={styles.newAgentButton}
|
|
||||||
testID="sidebar-sessions"
|
|
||||||
accessible
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel="Sessions"
|
|
||||||
onPress={handleViewMore}
|
|
||||||
>
|
|
||||||
{({ hovered }) => (
|
|
||||||
<>
|
|
||||||
<MessagesSquare
|
|
||||||
size={theme.iconSize.md}
|
|
||||||
color={hovered ? theme.colors.foreground : theme.colors.foregroundMuted}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={[styles.newAgentButtonText, hovered && styles.newAgentButtonTextHovered]}
|
|
||||||
>
|
|
||||||
Sessions
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Pressable>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -786,15 +779,14 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
gap: theme.spacing[2],
|
gap: theme.spacing[2],
|
||||||
},
|
},
|
||||||
newAgentButton: {
|
newAgentButton: {
|
||||||
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: theme.spacing[2],
|
gap: theme.spacing[2],
|
||||||
paddingVertical: theme.spacing[1],
|
paddingVertical: theme.spacing[2],
|
||||||
paddingRight: theme.spacing[1],
|
paddingHorizontal: theme.spacing[3],
|
||||||
paddingLeft: theme.spacing[3],
|
borderRadius: theme.borderRadius.lg,
|
||||||
flexShrink: 0,
|
|
||||||
},
|
},
|
||||||
newAgentButtonHovered: {},
|
|
||||||
newAgentButtonText: {
|
newAgentButtonText: {
|
||||||
fontSize: theme.fontSize.base,
|
fontSize: theme.fontSize.base,
|
||||||
fontWeight: theme.fontWeight.normal,
|
fontWeight: theme.fontWeight.normal,
|
||||||
@@ -803,6 +795,12 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
newAgentButtonTextHovered: {
|
newAgentButtonTextHovered: {
|
||||||
color: theme.colors.foreground,
|
color: theme.colors.foreground,
|
||||||
},
|
},
|
||||||
|
newAgentButtonHovered: {
|
||||||
|
backgroundColor: theme.colors.surface1,
|
||||||
|
},
|
||||||
|
newAgentButtonActive: {
|
||||||
|
backgroundColor: theme.colors.surface1,
|
||||||
|
},
|
||||||
hostTrigger: {
|
hostTrigger: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export function useAggregatedAgents(options?: {
|
|||||||
attentionReason: agent.attentionReason,
|
attentionReason: agent.attentionReason,
|
||||||
attentionTimestamp: agent.attentionTimestamp,
|
attentionTimestamp: agent.attentionTimestamp,
|
||||||
archivedAt: agent.archivedAt,
|
archivedAt: agent.archivedAt,
|
||||||
|
createdAt: agent.createdAt,
|
||||||
labels: agent.labels,
|
labels: agent.labels,
|
||||||
};
|
};
|
||||||
allAgents.push(nextAgent);
|
allAgents.push(nextAgent);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ function toAggregatedAgent(params: {
|
|||||||
attentionReason: source.attentionReason,
|
attentionReason: source.attentionReason,
|
||||||
attentionTimestamp: source.attentionTimestamp ?? null,
|
attentionTimestamp: source.attentionTimestamp ?? null,
|
||||||
archivedAt: source.archivedAt ?? null,
|
archivedAt: source.archivedAt ?? null,
|
||||||
|
createdAt: source.createdAt,
|
||||||
labels: source.labels,
|
labels: source.labels,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-20
@@ -2,23 +2,21 @@ import { useMemo, useState, useCallback, useEffect } from "react";
|
|||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { useIsFocused } from "@react-navigation/native";
|
import { useIsFocused } from "@react-navigation/native";
|
||||||
import { StyleSheet } from "react-native-unistyles";
|
import { StyleSheet } from "react-native-unistyles";
|
||||||
import { BackHeader } from "@/components/headers/back-header";
|
import { MenuHeader } from "@/components/headers/menu-header";
|
||||||
import { AgentList } from "@/components/agent-list";
|
import { AgentList } from "@/components/agent-list";
|
||||||
import { useAllAgentsList } from "@/hooks/use-all-agents-list";
|
import { useAllAgentsList } from "@/hooks/use-all-agents-list";
|
||||||
import { router } from "expo-router";
|
|
||||||
import { buildHostRootRoute } from "@/utils/host-routes";
|
|
||||||
|
|
||||||
export function AgentsScreen({ serverId }: { serverId: string }) {
|
export function SessionsScreen({ serverId }: { serverId: string }) {
|
||||||
const isFocused = useIsFocused();
|
const isFocused = useIsFocused();
|
||||||
|
|
||||||
if (!isFocused) {
|
if (!isFocused) {
|
||||||
return <View style={styles.container} />;
|
return <View style={styles.container} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <AgentsScreenContent serverId={serverId} />;
|
return <SessionsScreenContent serverId={serverId} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AgentsScreenContent({ serverId }: { serverId: string }) {
|
function SessionsScreenContent({ serverId }: { serverId: string }) {
|
||||||
const { agents, isRevalidating, refreshAll } = useAllAgentsList({
|
const { agents, isRevalidating, refreshAll } = useAllAgentsList({
|
||||||
serverId,
|
serverId,
|
||||||
includeArchived: true,
|
includeArchived: true,
|
||||||
@@ -40,24 +38,14 @@ function AgentsScreenContent({ serverId }: { serverId: string }) {
|
|||||||
}, [isRevalidating, isManualRefresh]);
|
}, [isRevalidating, isManualRefresh]);
|
||||||
|
|
||||||
const sortedAgents = useMemo(() => {
|
const sortedAgents = useMemo(() => {
|
||||||
return [...agents].sort((a, b) => {
|
return [...agents].sort(
|
||||||
const aNeedsInput = (a.pendingPermissionCount ?? 0) > 0;
|
(a, b) => b.createdAt.getTime() - a.createdAt.getTime()
|
||||||
const bNeedsInput = (b.pendingPermissionCount ?? 0) > 0;
|
);
|
||||||
if (aNeedsInput && !bNeedsInput) return -1;
|
|
||||||
if (!aNeedsInput && bNeedsInput) return 1;
|
|
||||||
|
|
||||||
if (a.requiresAttention && !b.requiresAttention) return -1;
|
|
||||||
if (!a.requiresAttention && b.requiresAttention) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}, [agents]);
|
}, [agents]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<BackHeader
|
<MenuHeader title="Sessions" />
|
||||||
title="Sessions"
|
|
||||||
onBack={() => router.replace(buildHostRootRoute(serverId) as any)}
|
|
||||||
/>
|
|
||||||
<AgentList
|
<AgentList
|
||||||
agents={sortedAgents}
|
agents={sortedAgents}
|
||||||
showCheckoutInfo={false}
|
showCheckoutInfo={false}
|
||||||
@@ -1110,6 +1110,7 @@ export const useSessionStore = create<SessionStore>()(
|
|||||||
requiresAttention: agent.requiresAttention ?? false,
|
requiresAttention: agent.requiresAttention ?? false,
|
||||||
attentionReason: agent.attentionReason ?? null,
|
attentionReason: agent.attentionReason ?? null,
|
||||||
attentionTimestamp: agent.attentionTimestamp ?? null,
|
attentionTimestamp: agent.attentionTimestamp ?? null,
|
||||||
|
createdAt: agent.createdAt,
|
||||||
labels: agent.labels,
|
labels: agent.labels,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export type AgentDirectoryEntry = Pick<
|
|||||||
| "attentionReason"
|
| "attentionReason"
|
||||||
| "attentionTimestamp"
|
| "attentionTimestamp"
|
||||||
| "archivedAt"
|
| "archivedAt"
|
||||||
|
| "createdAt"
|
||||||
| "labels"
|
| "labels"
|
||||||
> & {
|
> & {
|
||||||
pendingPermissionCount?: number;
|
pendingPermissionCount?: number;
|
||||||
|
|||||||
@@ -323,12 +323,12 @@ export function buildHostRootRoute(serverId: string): string {
|
|||||||
return `/h/${encodeSegment(normalized)}`;
|
return `/h/${encodeSegment(normalized)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildHostAgentsRoute(serverId: string): string {
|
export function buildHostSessionsRoute(serverId: string): string {
|
||||||
const base = buildHostRootRoute(serverId);
|
const base = buildHostRootRoute(serverId);
|
||||||
if (base === "/") {
|
if (base === "/") {
|
||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
return `${base}/agents`;
|
return `${base}/sessions`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildHostOpenProjectRoute(serverId: string): string {
|
export function buildHostOpenProjectRoute(serverId: string): string {
|
||||||
@@ -361,8 +361,8 @@ export function mapPathnameToServer(
|
|||||||
if (suffix.startsWith("settings")) {
|
if (suffix.startsWith("settings")) {
|
||||||
return `${base}/settings`;
|
return `${base}/settings`;
|
||||||
}
|
}
|
||||||
if (suffix.startsWith("agents")) {
|
if (suffix.startsWith("sessions")) {
|
||||||
return `${base}/agents`;
|
return `${base}/sessions`;
|
||||||
}
|
}
|
||||||
if (suffix.startsWith("open-project")) {
|
if (suffix.startsWith("open-project")) {
|
||||||
return `${base}/open-project`;
|
return `${base}/open-project`;
|
||||||
|
|||||||
Reference in New Issue
Block a user