refactor(desktop): centralize companion window ownership

Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-23 20:11:35 -07:00
committed by Rizz
parent 47907293ba
commit 42002efaa9
5 changed files with 66 additions and 31 deletions

View File

@@ -44,7 +44,7 @@ import {
} from "@/features/notifications/hooks";
import { PreventSleepProvider } from "@/features/agents/usePreventSleep";
import { requestOpenCreateAgent } from "@/features/agents/openCreateAgentEvent";
import { isAgentActivityWindow } from "@/features/agents/lib/agentActivityWindow";
import { currentCompanionWindowKind } from "@/app/companionWindow";
import { useAgentsDataRefresh } from "@/features/agents/lib/useAgentsDataRefresh";
import { useManagedAgentRuntimeReconciliation } from "@/features/agents/useManagedAgentRuntimeReconciliation";
import { useAutoRestartPolicy } from "@/features/agents/lib/useAutoRestartPolicy";
@@ -126,8 +126,9 @@ export function AppShell() {
showHuddleInMainApp,
viewHuddleChannel,
} = useHuddlePresentation();
const isActivityWindow = isAgentActivityWindow();
const isCompanionWindow = isHuddleRoom || isActivityWindow;
const companionWindowKind = currentCompanionWindowKind();
const isActivityWindow = companionWindowKind === "agent-activity";
const isCompanionWindow = companionWindowKind !== null;
const hasCommunityRail =
communitiesHook.communities.length > 1 && !isCompanionWindow;
const addCommunityDialog = useAddCommunityDialogState();
@@ -354,7 +355,7 @@ export function AppShell() {
handleThreadReplyDesktopNotification,
} = useAppShellDesktopNotifications({
channels,
enabled: !isHuddleRoom,
enabled: !isCompanionWindow,
goChannel,
goHome,
notificationSettings: notificationSettings.settings,
@@ -391,8 +392,8 @@ export function AppShell() {
muteThread,
unmuteThread,
} = useUnreadChannels(
isHuddleRoom ? EMPTY_CHANNELS : sidebarChannels,
isHuddleRoom ? null : activeChannel,
isCompanionWindow ? EMPTY_CHANNELS : sidebarChannels,
isCompanionWindow ? null : activeChannel,
{
pubkey: identityQuery.data?.pubkey,
relayClient,
@@ -454,7 +455,7 @@ export function AppShell() {
identityQuery.data?.pubkey,
notificationSettings.settings,
notificationSettings.setDesktopEnabled,
!isHuddleRoom,
!isCompanionWindow,
selectedView === "home" && !settingsOpen,
getChannelReadAt,
readStateVersion,
@@ -658,13 +659,13 @@ export function AppShell() {
[openSearchHit],
);
useAppShellLifecycleEffects({
desktopBadgeEnabled: !isHuddleRoom,
desktopBadgeEnabled: !isCompanionWindow,
homeBadgeCountExcludingHighPriority,
topLevelUnreadChannelIds,
unreadChannelNotificationCount,
});
// Dispatch `buzz://` deep links only from the main window; the companion is dedicated to its active Huddle route.
useAppDeepLinks(!isHuddleRoom);
// Dispatch `buzz://` deep links only from the primary window; companions own their focused route.
useAppDeepLinks(!isCompanionWindow);
const handleOpenCreateChannel = React.useCallback(
() => setIsCreateChannelOpen(true),
[],
@@ -673,7 +674,7 @@ export function AppShell() {
activeChannelId: selectedView === "channel" ? selectedChannelId : null,
canSearchCurrentChannel:
selectedView === "channel" && Boolean(activeChannel),
disabled: settingsOpen || isHuddleRoom,
disabled: settingsOpen || isCompanionWindow,
onBrowseChannels: handleOpenBrowseChannels,
onCreateChannel: handleOpenCreateChannel,
onGoHome: goHome,
@@ -684,7 +685,7 @@ export function AppShell() {
useSettingsShortcuts({
onClose: handleCloseSettings,
onOpenSettings: handleOpenSettings,
open: isHuddleRoom ? undefined : settingsOpen,
open: isCompanionWindow ? undefined : settingsOpen,
});
useMarkAsReadShortcuts({
activeChannelId: activeChannel?.id ?? null,

View File

@@ -0,0 +1,19 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { companionWindowKindForLabel } from "./companionWindow.ts";
describe("companionWindowKindForLabel", () => {
it("classifies focused companion labels", () => {
assert.equal(
companionWindowKindForLabel("agent-activity-deadbeef-channel"),
"agent-activity",
);
assert.equal(companionWindowKindForLabel("huddle-channel-id"), "huddle");
});
it("leaves primary and unrelated windows unclassified", () => {
assert.equal(companionWindowKindForLabel("main"), null);
assert.equal(companionWindowKindForLabel("reader-document"), null);
});
});

View File

@@ -0,0 +1,30 @@
import { isTauri } from "@tauri-apps/api/core";
import { getCurrentWindow } from "@tauri-apps/api/window";
export type CompanionWindowKind = "agent-activity" | "huddle";
/** Classify native companion surfaces that reuse the main application shell. */
export function companionWindowKindForLabel(
label: string,
): CompanionWindowKind | null {
if (label.startsWith("agent-activity-")) return "agent-activity";
if (label.startsWith("huddle-")) return "huddle";
return null;
}
/** Return this webview's companion kind, or null for the primary app window. */
export function currentCompanionWindowKind(): CompanionWindowKind | null {
if (!isTauri()) return null;
try {
return companionWindowKindForLabel(getCurrentWindow().label);
} catch {
// Browser previews can expose the Tauri IPC mock without window metadata.
return null;
}
}
/** Whether this webview is a focused companion rather than the primary app. */
export function isCompanionWindow(): boolean {
return currentCompanionWindowKind() !== null;
}

View File

@@ -1,19 +1,9 @@
import { isTauri } from "@tauri-apps/api/core";
import { currentCompanionWindowKind } from "@/app/companionWindow";
import { getCurrentWindow } from "@tauri-apps/api/window";
const AGENT_ACTIVITY_WINDOW_LABEL_PREFIX = "agent-activity-";
/** Whether this webview is dedicated to an agent activity feed. */
export function isAgentActivityWindow(): boolean {
if (!isTauri()) return false;
try {
return getCurrentWindow().label.startsWith(
AGENT_ACTIVITY_WINDOW_LABEL_PREFIX,
);
} catch {
return false;
}
return currentCompanionWindowKind() === "agent-activity";
}
/** Build the concise native title for a channel-scoped activity window. */

View File

@@ -11,8 +11,7 @@ import "@/shared/styles/globals.css";
import { UpdaterProvider } from "@/features/settings/hooks/UpdaterProvider";
import { migrateLegacyCommunityStorageBeforeRender } from "@/features/communities/legacyCommunityStorage";
import { CommunitiesProvider } from "@/features/communities/useCommunities";
import { huddleWindowChannelId } from "@/features/huddle/lib/huddleWindow";
import { isAgentActivityWindow } from "@/features/agents/lib/agentActivityWindow";
import { isCompanionWindow } from "@/app/companionWindow";
import { CommunityOnboardingProvider } from "@/features/onboarding/communityOnboarding";
import { ThemeProvider } from "@/shared/theme/ThemeProvider";
import { EmojiBurstProvider } from "@/shared/ui/EmojiBurstProvider";
@@ -86,11 +85,7 @@ function renderApp() {
SecurityError from localStorage can't blank the whole window. */}
<RootErrorBoundary>
<CommunitiesProvider>
<CommunityOnboardingProvider
enabled={
huddleWindowChannelId() === null && !isAgentActivityWindow()
}
>
<CommunityOnboardingProvider enabled={!isCompanionWindow()}>
<ThemeProvider defaultTheme="buzz">
<TooltipProvider>
<EmojiBurstProvider>