mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Fix keyboard shortcuts and DRY agent status dot
This commit is contained in:
@@ -125,6 +125,7 @@ function AppContainer({ children, selectedAgentId }: AppContainerProps) {
|
||||
const desktopAgentListOpen = usePanelStore((state) => state.desktop.agentListOpen);
|
||||
const openAgentList = usePanelStore((state) => state.openAgentList);
|
||||
const toggleAgentList = usePanelStore((state) => state.toggleAgentList);
|
||||
const toggleFileExplorer = usePanelStore((state) => state.toggleFileExplorer);
|
||||
const horizontalScroll = useHorizontalScrollOptional();
|
||||
|
||||
const isMobile =
|
||||
@@ -140,6 +141,8 @@ function AppContainer({ children, selectedAgentId }: AppContainerProps) {
|
||||
enabled: chromeEnabled,
|
||||
isMobile,
|
||||
toggleAgentList,
|
||||
selectedAgentId,
|
||||
toggleFileExplorer,
|
||||
});
|
||||
const {
|
||||
translateX,
|
||||
|
||||
@@ -18,6 +18,7 @@ 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 { AgentStatusDot } from "@/components/agent-status-dot";
|
||||
import {
|
||||
CHECKOUT_STATUS_STALE_TIME,
|
||||
checkoutStatusQueryKey,
|
||||
@@ -181,12 +182,6 @@ export function AgentList({
|
||||
const timeAgo = formatTimeAgo(agent.lastActivityAt);
|
||||
const agentKey = `${agent.serverId}:${agent.id}`;
|
||||
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({
|
||||
serverId: agent.serverId,
|
||||
@@ -211,11 +206,7 @@ export function AgentList({
|
||||
{({ hovered }) => (
|
||||
<View style={styles.agentContent}>
|
||||
<View style={styles.row}>
|
||||
{statusColor && (
|
||||
<View
|
||||
style={[styles.statusDot, { backgroundColor: statusColor }]}
|
||||
/>
|
||||
)}
|
||||
<AgentStatusDot status={agent.status} requiresAttention={agent.requiresAttention} />
|
||||
<Text
|
||||
style={[
|
||||
styles.agentTitle,
|
||||
@@ -240,8 +231,6 @@ export function AgentList({
|
||||
handleAgentLongPress,
|
||||
handleAgentPress,
|
||||
selectedAgentId,
|
||||
theme.colors.palette.blue,
|
||||
theme.colors.success,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -429,11 +418,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
fontWeight: "300",
|
||||
color: theme.colors.foregroundMuted,
|
||||
},
|
||||
statusDot: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: theme.borderRadius.full,
|
||||
},
|
||||
sheetOverlay: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-end",
|
||||
|
||||
34
packages/app/src/components/agent-status-dot.tsx
Normal file
34
packages/app/src/components/agent-status-dot.tsx
Normal file
@@ -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,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -15,6 +15,7 @@ import { useAggregatedAgents, type AggregatedAgent } from "@/hooks/use-aggregate
|
||||
import { formatTimeAgo } from "@/utils/time";
|
||||
import { shortenPath } from "@/utils/shorten-path";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { AgentStatusDot } from "@/components/agent-status-dot";
|
||||
|
||||
function agentKey(agent: Pick<AggregatedAgent, "serverId" | "id">): string {
|
||||
return `${agent.serverId}:${agent.id}`;
|
||||
@@ -188,12 +189,15 @@ export function CommandCenter() {
|
||||
onPress={() => handleSelect(agent)}
|
||||
>
|
||||
<View style={styles.rowContent}>
|
||||
<View style={styles.rowTitle}>
|
||||
<AgentStatusDot status={agent.status} requiresAttention={agent.requiresAttention} />
|
||||
<Text
|
||||
style={[styles.title, { color: theme.colors.foreground }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{agent.title || "New agent"}
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={[styles.subtitle, { color: theme.colors.foregroundMuted }]}
|
||||
numberOfLines={1}
|
||||
@@ -258,9 +262,14 @@ const styles = StyleSheet.create((theme) => ({
|
||||
rowContent: {
|
||||
gap: 2,
|
||||
},
|
||||
rowTitle: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing[2],
|
||||
},
|
||||
title: {
|
||||
fontSize: theme.fontSize.base,
|
||||
fontWeight: theme.fontWeight.medium,
|
||||
fontWeight: theme.fontWeight.normal,
|
||||
},
|
||||
subtitle: {
|
||||
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 { useKeyboardNavStore } from "@/stores/keyboard-nav-store";
|
||||
import { getIsTauri } from "@/constants/layout";
|
||||
import { AgentStatusDot } from "@/components/agent-status-dot";
|
||||
|
||||
type SectionData = SidebarSectionData;
|
||||
|
||||
@@ -339,11 +340,6 @@ export function GroupedAgentList({
|
||||
const isRunning = agent.status === "running";
|
||||
const shortcutNumber =
|
||||
showShortcutBadges ? (shortcutIndexByAgentKey.get(agentKey) ?? null) : null;
|
||||
const statusColor = isRunning
|
||||
? theme.colors.palette.blue[500]
|
||||
: agent.requiresAttention
|
||||
? theme.colors.success
|
||||
: null;
|
||||
|
||||
const checkoutQuery = useCheckoutStatusCacheOnly({
|
||||
serverId: agent.serverId,
|
||||
@@ -377,11 +373,7 @@ export function GroupedAgentList({
|
||||
>
|
||||
<View style={styles.agentContent}>
|
||||
<View style={styles.row}>
|
||||
{statusColor && (
|
||||
<View
|
||||
style={[styles.statusDot, { backgroundColor: statusColor }]}
|
||||
/>
|
||||
)}
|
||||
<AgentStatusDot status={agent.status} requiresAttention={agent.requiresAttention} />
|
||||
<Text
|
||||
style={[
|
||||
styles.agentTitle,
|
||||
@@ -437,8 +429,6 @@ export function GroupedAgentList({
|
||||
shortcutIndexByAgentKey,
|
||||
theme.colors.foreground,
|
||||
theme.colors.foregroundMuted,
|
||||
theme.colors.palette.blue,
|
||||
theme.colors.success,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -691,11 +681,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
fontWeight: "300",
|
||||
color: theme.colors.foregroundMuted,
|
||||
},
|
||||
statusDot: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: theme.borderRadius.full,
|
||||
},
|
||||
sheetOverlay: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-end",
|
||||
|
||||
@@ -9,10 +9,14 @@ export function useGlobalKeyboardNav({
|
||||
enabled,
|
||||
isMobile,
|
||||
toggleAgentList,
|
||||
selectedAgentId,
|
||||
toggleFileExplorer,
|
||||
}: {
|
||||
enabled: boolean;
|
||||
isMobile: boolean;
|
||||
toggleAgentList: () => void;
|
||||
selectedAgentId?: string;
|
||||
toggleFileExplorer?: () => void;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
@@ -88,6 +92,18 @@ export function useGlobalKeyboardNav({
|
||||
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
|
||||
if ((event.metaKey || event.ctrlKey) && lowerKey === "k") {
|
||||
event.preventDefault();
|
||||
@@ -145,6 +161,14 @@ export function useGlobalKeyboardNav({
|
||||
window.removeEventListener("blur", handleBlurOrHide);
|
||||
document.removeEventListener("visibilitychange", handleBlurOrHide);
|
||||
};
|
||||
}, [enabled, isMobile, pathname, resetModifiers, router, toggleAgentList]);
|
||||
}, [
|
||||
enabled,
|
||||
isMobile,
|
||||
pathname,
|
||||
resetModifiers,
|
||||
router,
|
||||
selectedAgentId,
|
||||
toggleAgentList,
|
||||
toggleFileExplorer,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user