From 1502d6215eda7473ab5a327bbd86cf640b46c096 Mon Sep 17 00:00:00 2001 From: Taylor Ho Date: Mon, 24 Aug 2026 02:20:47 -0700 Subject: [PATCH] fix(desktop): keep activity companions pinned Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho --- desktop/src/app/companionWindow.test.mjs | 30 +++++++++++ desktop/src/app/companionWindow.ts | 36 +++++++++++++ .../src/app/navigation/useAppNavigation.ts | 17 ++----- .../activityRenderClasses/MessageActivity.tsx | 2 + .../ui/activityRenderClasses/PlanActivity.tsx | 2 + .../activityRenderClasses/ThoughtActivity.tsx | 2 + .../UserMessageBubble.tsx | 13 ++++- .../activityWindowPresentation.ts | 6 +++ .../src/shared/hooks/useHistorySearchState.ts | 6 ++- .../tests/e2e/agent-activity-window.spec.ts | 51 ++++++++++++------- 10 files changed, 132 insertions(+), 33 deletions(-) create mode 100644 desktop/src/features/agents/ui/activityRenderClasses/activityWindowPresentation.ts diff --git a/desktop/src/app/companionWindow.test.mjs b/desktop/src/app/companionWindow.test.mjs index f94d4a98b4..551faab101 100644 --- a/desktop/src/app/companionWindow.test.mjs +++ b/desktop/src/app/companionWindow.test.mjs @@ -3,6 +3,7 @@ import { describe, it } from "node:test"; import { acceptsNativeDeepLinks, + agentActivityCompanionCoordinates, companionCommunityBootstrap, companionCommunityIdForHash, companionWindowKindForLabel, @@ -23,6 +24,35 @@ describe("companionWindowKindForLabel", () => { }); }); +describe("agentActivityCompanionCoordinates", () => { + const coordinates = { + community: "community-one", + agentSession: "agent-one", + agentSessionChannel: "channel-one", + }; + + it("returns every immutable activity companion coordinate", () => { + assert.deepEqual( + agentActivityCompanionCoordinates("agent-activity", coordinates), + coordinates, + ); + }); + + it("rejects non-activity windows and incomplete coordinates", () => { + assert.equal( + agentActivityCompanionCoordinates("huddle", coordinates), + undefined, + ); + assert.equal( + agentActivityCompanionCoordinates("agent-activity", { + community: coordinates.community, + agentSession: coordinates.agentSession, + }), + undefined, + ); + }); +}); + describe("acceptsNativeDeepLinks", () => { it("reserves the pending-link queue for the main realm", () => { assert.equal(acceptsNativeDeepLinks(null), true); diff --git a/desktop/src/app/companionWindow.ts b/desktop/src/app/companionWindow.ts index 250f50d873..04a7426b2b 100644 --- a/desktop/src/app/companionWindow.ts +++ b/desktop/src/app/companionWindow.ts @@ -24,6 +24,42 @@ export function currentCompanionWindowKind(): CompanionWindowKind | null { } } +export type AgentActivityCompanionCoordinates = { + community: string; + agentSession: string; + agentSessionChannel: string; +}; + +export function agentActivityCompanionCoordinates( + companionKind: CompanionWindowKind | null, + search: Record, +): AgentActivityCompanionCoordinates | undefined { + if ( + companionKind !== "agent-activity" || + typeof search.community !== "string" || + typeof search.agentSession !== "string" || + typeof search.agentSessionChannel !== "string" + ) { + return undefined; + } + + return { + community: search.community, + agentSession: search.agentSession, + agentSessionChannel: search.agentSessionChannel, + }; +} + +/** Return the immutable route coordinates owned by this activity companion. */ +export function currentAgentActivityCompanionCoordinates( + search: Record, +): AgentActivityCompanionCoordinates | undefined { + return agentActivityCompanionCoordinates( + currentCompanionWindowKind(), + search, + ); +} + /** Community encoded into a companion bootstrap hash. */ export function companionCommunityIdForHash(hash: string): string | null { const query = hash.indexOf("?"); diff --git a/desktop/src/app/navigation/useAppNavigation.ts b/desktop/src/app/navigation/useAppNavigation.ts index b1659d9c49..9e572e4ae8 100644 --- a/desktop/src/app/navigation/useAppNavigation.ts +++ b/desktop/src/app/navigation/useAppNavigation.ts @@ -7,7 +7,7 @@ import { } from "@tanstack/react-router"; import { openSearchHitWithNavigation } from "@/app/navigation/searchHitNavigation"; -import { currentCompanionWindowKind } from "@/app/companionWindow"; +import { currentAgentActivityCompanionCoordinates } from "@/app/companionWindow"; import type { SearchHit } from "@/shared/api/types"; type NavigationBehavior = { @@ -21,18 +21,9 @@ export function useAppNavigation() { const navigate = useNavigate(); const location = useLocation(); const canGoBack = useCanGoBack(); - const companionSearch = location.search as Record; - const companionCoordinates = - currentCompanionWindowKind() === "agent-activity" && - typeof companionSearch.community === "string" && - typeof companionSearch.agentSession === "string" && - typeof companionSearch.agentSessionChannel === "string" - ? { - community: companionSearch.community, - agentSession: companionSearch.agentSession, - agentSessionChannel: companionSearch.agentSessionChannel, - } - : undefined; + const companionCoordinates = currentAgentActivityCompanionCoordinates( + location.search as Record, + ); const commitNavigation = React.useCallback( async ( diff --git a/desktop/src/features/agents/ui/activityRenderClasses/MessageActivity.tsx b/desktop/src/features/agents/ui/activityRenderClasses/MessageActivity.tsx index 00651b3525..1d3d38d014 100644 --- a/desktop/src/features/agents/ui/activityRenderClasses/MessageActivity.tsx +++ b/desktop/src/features/agents/ui/activityRenderClasses/MessageActivity.tsx @@ -1,5 +1,6 @@ import type { UserProfileLookup } from "@/features/profile/lib/identity"; import { Markdown } from "@/shared/ui/markdown"; +import { activityWindowMarkdownInteractive } from "./activityWindowPresentation"; import { useAgentSessionTranscriptVariant } from "../agentSessionTranscriptContext"; import { formatTranscriptTimestampTitle } from "../agentSessionUtils"; import type { TranscriptItem } from "../agentSessionTypes"; @@ -65,6 +66,7 @@ function MessageItem({ diff --git a/desktop/src/features/agents/ui/activityRenderClasses/PlanActivity.tsx b/desktop/src/features/agents/ui/activityRenderClasses/PlanActivity.tsx index 334a1b0807..0e1d862e8f 100644 --- a/desktop/src/features/agents/ui/activityRenderClasses/PlanActivity.tsx +++ b/desktop/src/features/agents/ui/activityRenderClasses/PlanActivity.tsx @@ -1,4 +1,5 @@ import { Markdown } from "@/shared/ui/markdown"; +import { activityWindowMarkdownInteractive } from "./activityWindowPresentation"; import { ActivityRow, ActivityRowContent, @@ -41,6 +42,7 @@ export function PlanActivity(props: ActivityRenderClassItemProps) { diff --git a/desktop/src/features/agents/ui/activityRenderClasses/ThoughtActivity.tsx b/desktop/src/features/agents/ui/activityRenderClasses/ThoughtActivity.tsx index 8dfaea86ef..2907cf2a2a 100644 --- a/desktop/src/features/agents/ui/activityRenderClasses/ThoughtActivity.tsx +++ b/desktop/src/features/agents/ui/activityRenderClasses/ThoughtActivity.tsx @@ -1,4 +1,5 @@ import { Markdown } from "@/shared/ui/markdown"; +import { activityWindowMarkdownInteractive } from "./activityWindowPresentation"; import { ActivityRow, ActivityRowContent, @@ -26,6 +27,7 @@ export function ThoughtActivity(props: ActivityRenderClassItemProps) { diff --git a/desktop/src/features/agents/ui/activityRenderClasses/UserMessageBubble.tsx b/desktop/src/features/agents/ui/activityRenderClasses/UserMessageBubble.tsx index 181e4febf5..fa6c8860bd 100644 --- a/desktop/src/features/agents/ui/activityRenderClasses/UserMessageBubble.tsx +++ b/desktop/src/features/agents/ui/activityRenderClasses/UserMessageBubble.tsx @@ -8,9 +8,11 @@ import { useAppNavigation } from "@/app/navigation/useAppNavigation"; import { cn } from "@/shared/lib/cn"; import { useProfilePanel } from "@/shared/context/ProfilePanelContext"; import { Markdown } from "@/shared/ui/markdown"; +import { activityWindowMarkdownInteractive } from "./activityWindowPresentation"; import { UserAvatar } from "@/shared/ui/UserAvatar"; import { useAgentSessionTranscriptVariant } from "../agentSessionTranscriptContext"; import type { TranscriptItem } from "../agentSessionTypes"; +import { isAgentActivityWindow } from "../../lib/agentActivityWindow"; import { MessageLinkHoverCue } from "./MessageLinkHoverCue"; import { useTranscriptBubbleOverflow } from "./useTranscriptBubbleOverflow"; @@ -33,12 +35,16 @@ export function UserMessageBubble({ const { goChannel } = useAppNavigation(); const { openProfilePanel } = useProfilePanel(); const isCompactPreview = variant === "compactPreview"; + const isDedicatedActivityWindow = isAgentActivityWindow(); const shouldClampBubble = !isCompactPreview; const [bubbleRef, hasBubbleOverflow] = useTranscriptBubbleOverflow(shouldClampBubble); const text = item.text.trim(); const messageLink = - shouldClampBubble && item.channelId && item.messageId + shouldClampBubble && + !isDedicatedActivityWindow && + item.channelId && + item.messageId ? { channelId: item.channelId, messageId: item.messageId } : null; const authorProfile = item.authorPubkey @@ -98,7 +104,9 @@ export function UserMessageBubble({ data-role="user-message" data-testid="transcript-user-message" > - {isCompactPreview ? null : item.authorPubkey && openProfilePanel ? ( + {isCompactPreview ? null : item.authorPubkey && + openProfilePanel && + !isDedicatedActivityWindow ? (