mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-11 10:57:07 +00:00
* feat(plugins): give plugins the agent's sidebar indicator and background flag Plugin UI re-derived agent state from status and attention fields and could not see background work, so it showed agents as done while a shell they left running was still live. Agent snapshots now carry the sidebar's own bucket as indicator/indicatorLabel, computed by the same function the sidebar uses so the two cannot drift. Lifecycle hooks and the agent.open.before request now carry `background`, so a hook can skip a hidden helper agent without a lookup. A hook cannot change it; an omitted value means unchanged, like metadata. `finished` attention still means the turn finished; background work is the indicator's concern. * fix(plugins): keep session-open hooks that omit background typechecking A hook that returns its own object instead of spreading the request has no background field; the result type now treats it as optional, like metadata, matching the runtime where an omitted value means unchanged. * refactor(plugins): give agent snapshots the background work count instead of an indicator * fix(plugins): keep legacy session-open hooks that omit background typechecking
150 lines
3.7 KiB
TypeScript
150 lines
3.7 KiB
TypeScript
import { useCallback, useMemo } from "react";
|
|
import { Pressable, Text, View } from "react-native";
|
|
import {
|
|
useWorkspace,
|
|
useAgent,
|
|
useProject,
|
|
type WorkspaceRef,
|
|
type AgentRef,
|
|
type ProjectRef,
|
|
type PluginSidebarViewProps,
|
|
} from "@getpaseo/plugin/client";
|
|
|
|
export function Heading({ title, view }: { title: string; view: PluginSidebarViewProps }) {
|
|
const style = useMemo(
|
|
() => ({
|
|
color: view.theme.colors.foregroundMuted,
|
|
fontSize: 11,
|
|
paddingHorizontal: 12,
|
|
paddingTop: 12,
|
|
paddingBottom: 4,
|
|
}),
|
|
[view.theme],
|
|
);
|
|
return (
|
|
<Text accessibilityRole="header" style={style}>
|
|
{title}
|
|
</Text>
|
|
);
|
|
}
|
|
export function WorkspaceRow({
|
|
reference,
|
|
view,
|
|
onLongPress,
|
|
}: {
|
|
reference: WorkspaceRef;
|
|
view: PluginSidebarViewProps;
|
|
onLongPress?: () => void;
|
|
}) {
|
|
const workspace = useWorkspace(reference);
|
|
const open = useCallback(
|
|
() => view.navigation.openWorkspace(reference),
|
|
[view.navigation, reference],
|
|
);
|
|
const style = useMemo(
|
|
() => ({ color: view.theme.colors.foreground, fontSize: 12, lineHeight: 16 }),
|
|
[view.theme],
|
|
);
|
|
const detail = useMemo(
|
|
() => ({ color: view.theme.colors.foregroundMuted, fontSize: 11 }),
|
|
[view.theme],
|
|
);
|
|
const background = useMemo(
|
|
() => ({
|
|
...rowStyle,
|
|
backgroundColor:
|
|
view.active.workspace?.key === reference.key ? view.theme.colors.surface2 : undefined,
|
|
}),
|
|
[view.active.workspace, view.theme, reference],
|
|
);
|
|
if (!workspace) return null;
|
|
return (
|
|
<Pressable
|
|
accessibilityRole="button"
|
|
accessibilityLabel={`Open ${workspace.title ?? workspace.name}`}
|
|
onPress={open}
|
|
onLongPress={onLongPress}
|
|
style={background}
|
|
>
|
|
<Text numberOfLines={1} style={style}>
|
|
{workspace.title ?? workspace.name}
|
|
</Text>
|
|
<Text numberOfLines={1} style={detail}>
|
|
{workspace.indicatorLabel}
|
|
</Text>
|
|
</Pressable>
|
|
);
|
|
}
|
|
export function AgentRow({
|
|
reference,
|
|
view,
|
|
}: {
|
|
reference: AgentRef;
|
|
view: PluginSidebarViewProps;
|
|
}) {
|
|
const agent = useAgent(reference);
|
|
const open = useCallback(
|
|
() => view.navigation.openAgent(reference),
|
|
[view.navigation, reference],
|
|
);
|
|
const style = useMemo(
|
|
() => ({ color: view.theme.colors.foreground, fontSize: 12 }),
|
|
[view.theme],
|
|
);
|
|
const detail = useMemo(
|
|
() => ({ color: view.theme.colors.foregroundMuted, fontSize: 11 }),
|
|
[view.theme],
|
|
);
|
|
if (!agent) return null;
|
|
const tasks = agent.backgroundWork.count;
|
|
return (
|
|
<Pressable
|
|
accessibilityRole="button"
|
|
accessibilityLabel={`Open agent ${agent.title ?? agent.provider}`}
|
|
onPress={open}
|
|
style={rowStyle}
|
|
>
|
|
<Text numberOfLines={1} style={style}>
|
|
{agent.title ?? agent.provider}
|
|
</Text>
|
|
{tasks > 0 ? (
|
|
<Text numberOfLines={1} style={detail}>
|
|
{tasks === 1 ? "1 background task" : `${tasks} background tasks`}
|
|
</Text>
|
|
) : null}
|
|
</Pressable>
|
|
);
|
|
}
|
|
export function ProjectHeading({
|
|
reference,
|
|
view,
|
|
}: {
|
|
reference: ProjectRef;
|
|
view: PluginSidebarViewProps;
|
|
}) {
|
|
const project = useProject(reference);
|
|
const open = useCallback(
|
|
() => view.navigation.openProject(reference),
|
|
[view.navigation, reference],
|
|
);
|
|
if (!project) return null;
|
|
return (
|
|
<View>
|
|
<Heading title={project.displayName} view={view} />
|
|
<Pressable
|
|
accessibilityRole="button"
|
|
accessibilityLabel={`New workspace in ${project.displayName}`}
|
|
onPress={open}
|
|
>
|
|
<Heading title="+ New workspace" view={view} />
|
|
</Pressable>
|
|
</View>
|
|
);
|
|
}
|
|
const rowStyle = {
|
|
minHeight: 28,
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 4,
|
|
justifyContent: "center",
|
|
} as const;
|