import { useMutation } from "@tanstack/react-query"; import { type PluginClientContext, type PluginButtonRegistration, type PluginWorkspacePanelProps, useRpc, useWorkspace, } from "@getpaseo/plugin/client"; import { useCallback, useMemo } from "react"; import { Pressable, Text, View } from "react-native"; import { ExternalLink, Markdown } from "@getpaseo/plugin/client/ui"; import { incrementRpc } from "../shared/increment"; export function contributeClient(client: PluginClientContext) { const pills = new Map(); let stopped = false; const lifetime = new AbortController(); const register = (agent: { id: string; workspaceId?: string | null }) => { if (stopped || !agent.workspaceId) return; pills.get(agent.id)?.remove(); const workspaceId = agent.workspaceId; const remove = client.addComposerPill({ id: "open-counter", workspaceId, agentId: agent.id, button: { title: "Open plugin counter", icon: "Blocks", label: "Counter", behavior: { kind: "action", onPress() { client.openPanel("counter", { workspaceId }); }, }, }, }); pills.set(agent.id, remove); }; const remove = (agentId: string) => { pills.get(agentId)?.remove(); pills.delete(agentId); }; void client.paseo.agents .list({ subscribe: {}, signal: lifetime.signal }) .then(({ subscription }) => { subscription.subscribe({ snapshot: ({ entries }) => { for (const pill of pills.values()) pill.remove(); pills.clear(); for (const { agent } of entries) register(agent); }, update: (message) => { if (message.type !== "agent_update") return; const update = message.payload; if (update.kind === "remove") remove(update.agentId); else register(update.agent); }, }); return undefined; }) .catch((error) => { if (!stopped) console.error("Agent observation failed", error); }); return () => { stopped = true; lifetime.abort(); for (const pill of pills.values()) pill.remove(); pills.clear(); }; } export function ExamplePanel({ theme, layout, workspaceId, navigation, }: PluginWorkspacePanelProps) { const workspace = useWorkspace(workspaceId, ({ name }) => ({ name })); const callIncrement = useRpc(incrementRpc); const { data, error, isPending, mutate } = useMutation({ mutationFn: callIncrement }); const value = data?.value ?? 0; const styles = useMemo( () => ({ screen: { flex: 1, padding: layout.compact ? 16 : 24, gap: 16, backgroundColor: theme.colors.surface0, }, title: { color: theme.colors.foreground, fontSize: layout.compact ? 20 : 24 }, detail: { color: theme.colors.foregroundMuted }, button: { padding: 14, borderRadius: 10, backgroundColor: theme.colors.accent }, buttonText: { color: theme.colors.accentForeground, textAlign: "center" as const }, error: { color: theme.colors.statusDanger }, }), [theme, layout.compact], ); const handleIncrement = useCallback(() => { mutate({ value }); }, [mutate, value]); const openBrowser = navigation?.openBrowser; const handleOpenBrowser = useCallback(() => { openBrowser?.({ url: "https://paseo.sh/docs/plugins", workspaceId }); }, [openBrowser, workspaceId]); return ( {"## Workspace plugin panel\n\nCall a **daemon RPC** from this panel."} Plugin documentation {openBrowser ? ( Read in workspace browser ) : null} {workspace?.name} {data?.handledBy ?? "The RPC has not run yet."} {isPending ? "Calling daemon…" : `RPC counter: ${value}`} {error ? {error.message} : null} ); }