From 6503d3b4082985e63bd3324b16486f8407f77356 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 24 Jan 2026 22:05:44 +0700 Subject: [PATCH] Update files --- packages/app/src/components/agent-list.tsx | 35 +--------------- .../src/components/headers/menu-header.tsx | 34 +++++++++++---- .../src/screens/agent/agent-ready-screen.tsx | 24 +++++++++++ packages/app/src/utils/agent-display-info.ts | 41 +++++++++++++++++++ scripts/dev.sh | 8 ++-- 5 files changed, 96 insertions(+), 46 deletions(-) create mode 100644 packages/app/src/utils/agent-display-info.ts diff --git a/packages/app/src/components/agent-list.tsx b/packages/app/src/components/agent-list.tsx index a444976d2..1ecbeaab3 100644 --- a/packages/app/src/components/agent-list.tsx +++ b/packages/app/src/components/agent-list.tsx @@ -14,12 +14,12 @@ import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { useQueryClient } from "@tanstack/react-query"; import { formatTimeAgo } from "@/utils/time"; import { shortenPath } from "@/utils/shorten-path"; +import { deriveBranchLabel, deriveProjectPath } from "@/utils/agent-display-info"; import { type AggregatedAgent } from "@/hooks/use-aggregated-agents"; import { useSessionStore } from "@/stores/session-store"; import { CHECKOUT_STATUS_STALE_TIME, checkoutStatusQueryKey, - type CheckoutStatusPayload, useCheckoutStatusCacheOnly, } from "@/hooks/use-checkout-status-query"; import { @@ -105,35 +105,6 @@ export function AgentList({ setActionAgent(null); }, [actionAgent, deleteAgent]); - const deriveBranchLabel = useCallback((checkout: CheckoutStatusPayload | null): string | null => { - if (!checkout || !checkout.isGit) { - return null; - } - const currentBranch: string | null = checkout.currentBranch ?? null; - const baseRef: string | null = checkout.baseRef ?? null; - if (!currentBranch) { - return null; - } - if (baseRef && currentBranch === baseRef) { - return null; - } - return currentBranch; - }, []); - - const deriveProjectPath = useCallback( - (agent: AggregatedAgent, checkout: CheckoutStatusPayload | null): string => { - const basePath = checkout?.isGit ? (checkout.repoRoot ?? agent.cwd) : agent.cwd; - const worktreeMarker = ".paseo/worktrees/"; - const idx = basePath.indexOf(worktreeMarker); - if (idx !== -1) { - const afterMarker = basePath.slice(idx + worktreeMarker.length); - return afterMarker; - } - return basePath; - }, - [] - ); - const viewabilityConfig = useMemo( () => ({ itemVisiblePercentThreshold: 30 }), [] @@ -190,7 +161,7 @@ export function AgentList({ agentId: agent.id, }); const checkout = checkoutQuery.data ?? null; - const projectPath = deriveProjectPath(agent, checkout); + const projectPath = deriveProjectPath(agent.cwd, checkout); const branchLabel = deriveBranchLabel(checkout); return ( @@ -233,8 +204,6 @@ export function AgentList({ ); }, [ - deriveBranchLabel, - deriveProjectPath, handleAgentLongPress, handleAgentPress, selectedAgentId, diff --git a/packages/app/src/components/headers/menu-header.tsx b/packages/app/src/components/headers/menu-header.tsx index c211e7072..b861cf4c5 100644 --- a/packages/app/src/components/headers/menu-header.tsx +++ b/packages/app/src/components/headers/menu-header.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from "react"; -import { Pressable, Text } from "react-native"; +import { Pressable, Text, View } from "react-native"; import { StyleSheet, UnistylesRuntime, useUnistyles } from "react-native-unistyles"; import { Menu, PanelLeft } from "lucide-react-native"; import { ScreenHeader } from "./screen-header"; @@ -7,10 +7,11 @@ import { usePanelStore } from "@/stores/panel-store"; interface MenuHeaderProps { title?: string; + subtitle?: string; rightContent?: ReactNode; } -export function MenuHeader({ title, rightContent }: MenuHeaderProps) { +export function MenuHeader({ title, subtitle, rightContent }: MenuHeaderProps) { const { theme } = useUnistyles(); const isMobile = UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm"; @@ -32,9 +33,16 @@ export function MenuHeader({ title, rightContent }: MenuHeaderProps) { {title && ( - - {title} - + + + {title} + + {subtitle && ( + + {subtitle} + + )} + )} } @@ -55,13 +63,21 @@ const styles = StyleSheet.create((theme) => ({ }, borderRadius: theme.borderRadius.lg, }, - title: { + titleContainer: { flex: 1, - fontSize: theme.fontSize.lg, + gap: theme.spacing[0], + }, + title: { + fontSize: theme.fontSize.base, fontWeight: { - xs: theme.fontWeight.semibold, - md: "400", + xs: "400", + md: "300", }, color: theme.colors.foreground, }, + subtitle: { + fontSize: theme.fontSize.sm, + fontWeight: "300", + color: theme.colors.foregroundMuted, + }, })); diff --git a/packages/app/src/screens/agent/agent-ready-screen.tsx b/packages/app/src/screens/agent/agent-ready-screen.tsx index 3c7b2148e..f3e007ab6 100644 --- a/packages/app/src/screens/agent/agent-ready-screen.tsx +++ b/packages/app/src/screens/agent/agent-ready-screen.tsx @@ -57,6 +57,8 @@ import { import { extractAgentModel } from "@/utils/extract-agent-model"; import { startPerfMonitor } from "@/utils/perf-monitor"; import { shortenPath } from "@/utils/shorten-path"; +import { deriveBranchLabel, deriveProjectPath } from "@/utils/agent-display-info"; +import { useCheckoutStatusQuery } from "@/hooks/use-checkout-status-query"; import { DropdownMenu, DropdownMenuContent, @@ -406,6 +408,13 @@ function AgentScreenContent({ ? branchError ?? "Unavailable" : branchLabel ?? "Unknown"; + // Checkout status for header subtitle + const checkoutStatusQuery = useCheckoutStatusQuery({ + serverId, + agentId: resolvedAgentId ?? "", + }); + const checkout = checkoutStatusQuery.status; + useEffect(() => { if (!resolvedAgentId) { setFocusedAgentId(null); @@ -490,6 +499,20 @@ function AgentScreenContent({ const effectiveAgent = agent ?? placeholderAgent; + // Header subtitle: project path + branch (matching agent list row format) + const headerProjectPath = effectiveAgent + ? deriveProjectPath(effectiveAgent.cwd, checkout) + : null; + const headerBranchLabel = deriveBranchLabel(checkout); + const headerSubtitle = useMemo(() => { + if (!headerProjectPath) return undefined; + const path = shortenPath(headerProjectPath); + if (headerBranchLabel) { + return `${path} · ${headerBranchLabel}`; + } + return path; + }, [headerProjectPath, headerBranchLabel]); + useEffect(() => { if (!isPendingCreateForRoute || !pendingCreate) { return; @@ -596,6 +619,7 @@ function AgentScreenContent({ {/* Header */} diff --git a/packages/app/src/utils/agent-display-info.ts b/packages/app/src/utils/agent-display-info.ts new file mode 100644 index 000000000..8fb4351b0 --- /dev/null +++ b/packages/app/src/utils/agent-display-info.ts @@ -0,0 +1,41 @@ +import type { CheckoutStatusPayload } from "@/hooks/use-checkout-status-query"; + +/** + * Derives the branch label to display for an agent. + * Returns null if there's no branch to show (not a git repo, or on the base branch). + */ +export function deriveBranchLabel( + checkout: CheckoutStatusPayload | null +): string | null { + if (!checkout || !checkout.isGit) { + return null; + } + const currentBranch: string | null = checkout.currentBranch ?? null; + const baseRef: string | null = checkout.baseRef ?? null; + if (!currentBranch) { + return null; + } + if (baseRef && currentBranch === baseRef) { + return null; + } + return currentBranch; +} + +/** + * Derives the project path to display for an agent. + * If inside a Paseo worktree, shows just the worktree-relative path. + * Otherwise uses the repo root or cwd. + */ +export function deriveProjectPath( + cwd: string, + checkout: CheckoutStatusPayload | null +): string { + const basePath = checkout?.isGit ? (checkout.repoRoot ?? cwd) : cwd; + const worktreeMarker = ".paseo/worktrees/"; + const idx = basePath.indexOf(worktreeMarker); + if (idx !== -1) { + const afterMarker = basePath.slice(idx + worktreeMarker.length); + return afterMarker; + } + return basePath; +} diff --git a/scripts/dev.sh b/scripts/dev.sh index 630f3e618..5ca49bec5 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -9,9 +9,9 @@ export PATH="$SCRIPT_DIR/../node_modules/.bin:$PATH" DAEMON_PORT=$(get-port 6767 6768 6769 6770 6771 6772 6773) METRO_PORT=$(get-port 8081 8082 8083 8084 8085 8086 8087) -# Use a temporary PASEO_HOME to avoid conflicts between dev instances -export PASEO_HOME=$(mktemp -d "${TMPDIR:-/tmp}/paseo-dev.XXXXXX") -trap "rm -rf '$PASEO_HOME'" EXIT +# # Use a temporary PASEO_HOME to avoid conflicts between dev instances +# export PASEO_HOME=$(mktemp -d "${TMPDIR:-/tmp}/paseo-dev.XXXXXX") +# trap "rm -rf '$PASEO_HOME'" EXIT # Build CORS origins for this Expo instance CORS_ORIGINS="http://localhost:${METRO_PORT},http://127.0.0.1:${METRO_PORT}" @@ -24,7 +24,7 @@ echo " Paseo Dev" echo "══════════════════════════════════════════════════════" echo " Daemon: http://localhost:${DAEMON_PORT}" echo " Metro: http://localhost:${METRO_PORT}" -echo " Home: ${PASEO_HOME}" +# echo " Home: ${PASEO_HOME}" echo "══════════════════════════════════════════════════════" # Export for child processes (overrides .env values)