Merge branch 'keyword-nav-plan'

This commit is contained in:
Mohamed Boudra
2026-02-04 11:08:41 +07:00
5 changed files with 74 additions and 38 deletions
+2 -18
View File
@@ -18,6 +18,7 @@ import { shortenPath } from "@/utils/shorten-path";
import { deriveBranchLabel, deriveProjectPath } from "@/utils/agent-display-info"; import { deriveBranchLabel, deriveProjectPath } from "@/utils/agent-display-info";
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 { import {
CHECKOUT_STATUS_STALE_TIME, CHECKOUT_STATUS_STALE_TIME,
checkoutStatusQueryKey, checkoutStatusQueryKey,
@@ -181,12 +182,6 @@ export function AgentList({
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;
const isRunning = agent.status === "running";
const statusColor = isRunning
? theme.colors.palette.blue[500]
: agent.requiresAttention
? theme.colors.success
: null;
const checkoutQuery = useCheckoutStatusCacheOnly({ const checkoutQuery = useCheckoutStatusCacheOnly({
serverId: agent.serverId, serverId: agent.serverId,
@@ -211,11 +206,7 @@ export function AgentList({
{({ hovered }) => ( {({ hovered }) => (
<View style={styles.agentContent}> <View style={styles.agentContent}>
<View style={styles.row}> <View style={styles.row}>
{statusColor && ( <AgentStatusDot status={agent.status} requiresAttention={agent.requiresAttention} />
<View
style={[styles.statusDot, { backgroundColor: statusColor }]}
/>
)}
<Text <Text
style={[ style={[
styles.agentTitle, styles.agentTitle,
@@ -240,8 +231,6 @@ export function AgentList({
handleAgentLongPress, handleAgentLongPress,
handleAgentPress, handleAgentPress,
selectedAgentId, selectedAgentId,
theme.colors.palette.blue,
theme.colors.success,
] ]
); );
@@ -429,11 +418,6 @@ const styles = StyleSheet.create((theme) => ({
fontWeight: "300", fontWeight: "300",
color: theme.colors.foregroundMuted, color: theme.colors.foregroundMuted,
}, },
statusDot: {
width: 8,
height: 8,
borderRadius: theme.borderRadius.full,
},
sheetOverlay: { sheetOverlay: {
flex: 1, flex: 1,
justifyContent: "flex-end", justifyContent: "flex-end",
@@ -0,0 +1,34 @@
import { View } from "react-native";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
export function AgentStatusDot({
status,
requiresAttention,
}: {
status: string | null | undefined;
requiresAttention: boolean | null | undefined;
}) {
const { theme } = useUnistyles();
const isRunning = status === "running";
const color = isRunning
? theme.colors.palette.blue[500]
: requiresAttention
? theme.colors.success
: null;
if (!color) {
return null;
}
return <View style={[styles.dot, { backgroundColor: color }]} />;
}
const styles = StyleSheet.create((theme) => ({
dot: {
width: 8,
height: 8,
borderRadius: theme.borderRadius.full,
},
}));
+10 -1
View File
@@ -15,6 +15,7 @@ import { useAggregatedAgents, type AggregatedAgent } from "@/hooks/use-aggregate
import { formatTimeAgo } from "@/utils/time"; import { formatTimeAgo } from "@/utils/time";
import { shortenPath } from "@/utils/shorten-path"; import { shortenPath } from "@/utils/shorten-path";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { AgentStatusDot } from "@/components/agent-status-dot";
function agentKey(agent: Pick<AggregatedAgent, "serverId" | "id">): string { function agentKey(agent: Pick<AggregatedAgent, "serverId" | "id">): string {
return `${agent.serverId}:${agent.id}`; return `${agent.serverId}:${agent.id}`;
@@ -188,12 +189,15 @@ export function CommandCenter() {
onPress={() => handleSelect(agent)} onPress={() => handleSelect(agent)}
> >
<View style={styles.rowContent}> <View style={styles.rowContent}>
<View style={styles.rowTitle}>
<AgentStatusDot status={agent.status} requiresAttention={agent.requiresAttention} />
<Text <Text
style={[styles.title, { color: theme.colors.foreground }]} style={[styles.title, { color: theme.colors.foreground }]}
numberOfLines={1} numberOfLines={1}
> >
{agent.title || "New agent"} {agent.title || "New agent"}
</Text> </Text>
</View>
<Text <Text
style={[styles.subtitle, { color: theme.colors.foregroundMuted }]} style={[styles.subtitle, { color: theme.colors.foregroundMuted }]}
numberOfLines={1} numberOfLines={1}
@@ -258,9 +262,14 @@ const styles = StyleSheet.create((theme) => ({
rowContent: { rowContent: {
gap: 2, gap: 2,
}, },
rowTitle: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
},
title: { title: {
fontSize: theme.fontSize.base, fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.medium, fontWeight: theme.fontWeight.normal,
}, },
subtitle: { subtitle: {
fontSize: theme.fontSize.sm, fontSize: theme.fontSize.sm,
@@ -44,6 +44,7 @@ import { useSidebarAgentSections, type SidebarSectionData } from "@/hooks/use-si
import { useSidebarCollapsedSectionsStore } from "@/stores/sidebar-collapsed-sections-store"; import { useSidebarCollapsedSectionsStore } from "@/stores/sidebar-collapsed-sections-store";
import { useKeyboardNavStore } from "@/stores/keyboard-nav-store"; import { useKeyboardNavStore } from "@/stores/keyboard-nav-store";
import { getIsTauri } from "@/constants/layout"; import { getIsTauri } from "@/constants/layout";
import { AgentStatusDot } from "@/components/agent-status-dot";
type SectionData = SidebarSectionData; type SectionData = SidebarSectionData;
@@ -339,11 +340,6 @@ export function GroupedAgentList({
const isRunning = agent.status === "running"; const isRunning = agent.status === "running";
const shortcutNumber = const shortcutNumber =
showShortcutBadges ? (shortcutIndexByAgentKey.get(agentKey) ?? null) : null; showShortcutBadges ? (shortcutIndexByAgentKey.get(agentKey) ?? null) : null;
const statusColor = isRunning
? theme.colors.palette.blue[500]
: agent.requiresAttention
? theme.colors.success
: null;
const checkoutQuery = useCheckoutStatusCacheOnly({ const checkoutQuery = useCheckoutStatusCacheOnly({
serverId: agent.serverId, serverId: agent.serverId,
@@ -377,11 +373,7 @@ export function GroupedAgentList({
> >
<View style={styles.agentContent}> <View style={styles.agentContent}>
<View style={styles.row}> <View style={styles.row}>
{statusColor && ( <AgentStatusDot status={agent.status} requiresAttention={agent.requiresAttention} />
<View
style={[styles.statusDot, { backgroundColor: statusColor }]}
/>
)}
<Text <Text
style={[ style={[
styles.agentTitle, styles.agentTitle,
@@ -437,8 +429,6 @@ export function GroupedAgentList({
shortcutIndexByAgentKey, shortcutIndexByAgentKey,
theme.colors.foreground, theme.colors.foreground,
theme.colors.foregroundMuted, theme.colors.foregroundMuted,
theme.colors.palette.blue,
theme.colors.success,
] ]
); );
@@ -691,11 +681,6 @@ const styles = StyleSheet.create((theme) => ({
fontWeight: "300", fontWeight: "300",
color: theme.colors.foregroundMuted, color: theme.colors.foregroundMuted,
}, },
statusDot: {
width: 8,
height: 8,
borderRadius: theme.borderRadius.full,
},
sheetOverlay: { sheetOverlay: {
flex: 1, flex: 1,
justifyContent: "flex-end", justifyContent: "flex-end",
@@ -9,10 +9,14 @@ export function useGlobalKeyboardNav({
enabled, enabled,
isMobile, isMobile,
toggleAgentList, toggleAgentList,
selectedAgentId,
toggleFileExplorer,
}: { }: {
enabled: boolean; enabled: boolean;
isMobile: boolean; isMobile: boolean;
toggleAgentList: () => void; toggleAgentList: () => void;
selectedAgentId?: string;
toggleFileExplorer?: () => void;
}) { }) {
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
@@ -88,6 +92,18 @@ export function useGlobalKeyboardNav({
return; return;
} }
// Cmd+E: toggle explorer sidebar (only when an agent is selected)
if (
selectedAgentId &&
toggleFileExplorer &&
(event.metaKey || event.ctrlKey) &&
lowerKey === "e"
) {
event.preventDefault();
toggleFileExplorer();
return;
}
// Cmd+K: command center // Cmd+K: command center
if ((event.metaKey || event.ctrlKey) && lowerKey === "k") { if ((event.metaKey || event.ctrlKey) && lowerKey === "k") {
event.preventDefault(); event.preventDefault();
@@ -145,6 +161,14 @@ export function useGlobalKeyboardNav({
window.removeEventListener("blur", handleBlurOrHide); window.removeEventListener("blur", handleBlurOrHide);
document.removeEventListener("visibilitychange", handleBlurOrHide); document.removeEventListener("visibilitychange", handleBlurOrHide);
}; };
}, [enabled, isMobile, pathname, resetModifiers, router, toggleAgentList]); }, [
enabled,
isMobile,
pathname,
resetModifiers,
router,
selectedAgentId,
toggleAgentList,
toggleFileExplorer,
]);
} }