(null);
@@ -236,6 +244,15 @@ export function AgentSessionThreadPanel({
profiles,
preferResolvedSelfLabel: true,
});
+ React.useEffect(() => {
+ if (!scopeChannelName) return;
+
+ void setAgentActivityWindowTitle(agentLabel, scopeChannelName).catch(
+ (error) => {
+ console.error("Failed to update agent activity window title:", error);
+ },
+ );
+ }, [agentLabel, scopeChannelName]);
const viewLabel = showRawFeed ? "Raw ACP activity" : "Activity";
const headerScopeLabel = `${viewLabel} · ${scopeLabel}`;
const animateActivity = useTranscriptAnimationEnabled();
@@ -469,6 +486,7 @@ export function AgentSessionThreadPanel({
backdrop={layout !== "split" && !isOverlay}
backdropSurface="soft"
inset={layout !== "split" ? "wide" : "default"}
+ showCloseAction={!isDedicatedActivityWindow}
>
{agentHeaderContent}
diff --git a/desktop/src/features/channels/ui/ChannelScreen.tsx b/desktop/src/features/channels/ui/ChannelScreen.tsx
index 2e0501d3b0..8c38e237ad 100644
--- a/desktop/src/features/channels/ui/ChannelScreen.tsx
+++ b/desktop/src/features/channels/ui/ChannelScreen.tsx
@@ -98,6 +98,7 @@ export function ChannelScreen({
targetMessageEvents,
targetMessageId,
}: ChannelScreenProps) {
+ const isDedicatedActivityWindow = isAgentActivityWindow();
const { goHome } = useAppNavigation();
const { activeCommunity } = useCommunities();
const {
@@ -713,10 +714,13 @@ export function ChannelScreen({
const isNarrowPanelViewport =
channelContentWidthPx > 0 &&
channelContentWidthPx < AUXILIARY_PANEL_SINGLE_COLUMN_BREAKPOINT_PX;
+ // A dedicated activity window is the panel itself, not a responsive channel
+ // view. Keep the channel timeline out of that window at every width.
const isSinglePanelView =
- isNarrowPanelViewport &&
- activeChannel?.channelType !== "forum" &&
- hasAuxiliaryPanel;
+ isDedicatedActivityWindow ||
+ (isNarrowPanelViewport &&
+ activeChannel?.channelType !== "forum" &&
+ hasAuxiliaryPanel);
const shouldCompactHeaderActions =
hasAuxiliaryPanel &&
channelContentWidthPx > 0 &&
@@ -853,6 +857,7 @@ export function ChannelScreen({
}
diff --git a/desktop/src/features/channels/ui/ChannelScreenLoadingFallback.tsx b/desktop/src/features/channels/ui/ChannelScreenLoadingFallback.tsx
index a566b71d90..461fb2d19a 100644
--- a/desktop/src/features/channels/ui/ChannelScreenLoadingFallback.tsx
+++ b/desktop/src/features/channels/ui/ChannelScreenLoadingFallback.tsx
@@ -2,13 +2,15 @@ import { HuddleStartingView } from "@/features/huddle/components/HuddleStartingV
import { ViewLoadingFallback } from "@/shared/ui/ViewLoadingFallback";
export function ChannelScreenLoadingFallback({
+ includeHeader = true,
isHuddleTranscript,
}: {
+ includeHeader?: boolean;
isHuddleTranscript: boolean;
}) {
return isHuddleTranscript ? (
) : (
-
+
);
}
diff --git a/desktop/src/shared/layout/AuxiliaryPanelHeader.tsx b/desktop/src/shared/layout/AuxiliaryPanelHeader.tsx
index 5140c4d8d5..547e7f81e0 100644
--- a/desktop/src/shared/layout/AuxiliaryPanelHeader.tsx
+++ b/desktop/src/shared/layout/AuxiliaryPanelHeader.tsx
@@ -19,6 +19,7 @@ type AuxiliaryPanelHeaderProps = Omit<
inset?: "default" | "wide";
mode?: AuxiliaryPanelMode;
resizeBorder?: boolean;
+ showCloseAction?: boolean;
surface?: AuxiliaryPanelSurface;
/** Render header content without its own backdrop for a shared parent chrome. */
transparent?: boolean;
@@ -113,6 +114,7 @@ export function AuxiliaryPanelHeader({
inset = "default",
mode,
resizeBorder = false,
+ showCloseAction = true,
surface = "default",
transparent,
...props
@@ -159,7 +161,7 @@ export function AuxiliaryPanelHeader({
data-tauri-drag-region
{...props}
>
- {renderAuxiliaryPanelHeaderContent(children)}
+ {renderAuxiliaryPanelHeaderContent(children, showCloseAction)}
>
);
@@ -181,15 +183,21 @@ export function AuxiliaryPanelHeader({
data-tauri-drag-region
>
- {renderAuxiliaryPanelHeaderContent(children)}
+ {renderAuxiliaryPanelHeaderContent(children, showCloseAction)}
);
}
-function renderAuxiliaryPanelHeaderContent(children: React.ReactNode) {
- const { foundActions, content } = attachCloseActionToHeaderActions(children);
+function renderAuxiliaryPanelHeaderContent(
+ children: React.ReactNode,
+ showCloseAction: boolean,
+) {
+ const { foundActions, content } = attachCloseActionToHeaderActions(
+ children,
+ showCloseAction,
+ );
if (foundActions) {
return content;
@@ -198,12 +206,15 @@ function renderAuxiliaryPanelHeaderContent(children: React.ReactNode) {
return (
<>
{children}
-
+
>
);
}
-function attachCloseActionToHeaderActions(children: React.ReactNode): {
+function attachCloseActionToHeaderActions(
+ children: React.ReactNode,
+ includeCloseAction: boolean,
+): {
content: React.ReactNode;
foundActions: boolean;
} {
@@ -216,11 +227,14 @@ function attachCloseActionToHeaderActions(children: React.ReactNode): {
if (child.type === AuxiliaryPanelHeaderActions) {
foundActions = true;
- return React.cloneElement(child, { includeCloseAction: true });
+ return React.cloneElement(child, { includeCloseAction });
}
if (child.type === React.Fragment) {
- const nested = attachCloseActionToHeaderActions(child.props.children);
+ const nested = attachCloseActionToHeaderActions(
+ child.props.children,
+ includeCloseAction,
+ );
if (!nested.foundActions) {
return child;
diff --git a/desktop/src/shared/layout/auxiliaryPanelContext.test.mjs b/desktop/src/shared/layout/auxiliaryPanelContext.test.mjs
index 69cea29974..7a06e9d661 100644
--- a/desktop/src/shared/layout/auxiliaryPanelContext.test.mjs
+++ b/desktop/src/shared/layout/auxiliaryPanelContext.test.mjs
@@ -8,6 +8,7 @@ import { AuxiliaryPanel } from "./AuxiliaryPanel/index.ts";
import { AuxiliaryPanelBody } from "./AuxiliaryPanel/index.ts";
import {
AuxiliaryPanelHeader,
+ AuxiliaryPanelHeaderActions,
AuxiliaryPanelHeaderGroup,
} from "./AuxiliaryPanel/index.ts";
import {
@@ -177,6 +178,33 @@ test("AuxiliaryPanelHeader renders a generic close action from context", () => {
assert.match(html, /data-testid="auxiliary-panel-close"/);
});
+test("AuxiliaryPanelHeader can suppress its close action", () => {
+ const html = render(
+ React.createElement(
+ AuxiliaryPanel,
+ {
+ header: React.createElement(
+ AuxiliaryPanelHeader,
+ { showCloseAction: false },
+ React.createElement(AuxiliaryPanelHeaderGroup, null, "Title"),
+ React.createElement(
+ AuxiliaryPanelHeaderActions,
+ null,
+ React.createElement("button", { type: "button" }, "Settings"),
+ ),
+ ),
+ onClose: () => {},
+ widthPx: 420,
+ },
+ "Panel",
+ ),
+ );
+
+ assert.match(html, />Settings);
+ assert.doesNotMatch(html, /aria-label="Close panel"/);
+ assert.doesNotMatch(html, /data-testid="auxiliary-panel-close"/);
+});
+
test("AuxiliaryPanelHeader keeps resize border in single-panel mode when requested", () => {
const html = render(
React.createElement(