Merge branch 'keyword-nav-plan'
This commit is contained in:
@@ -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,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
@@ -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,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user