refactor(desktop): centralize auxiliary panel header controls

- Move auxiliary panel header padding into shared layout constants so profile, activity, thread, and channel management panels share the same right inset.
- Add shared auxiliary header back-button rendering with a scoped negative margin so leading icon placement is consistent without per-panel padding variants.
- Add a fixed auxiliary header actions wrapper to standardize icon-button spacing in header action clusters.
- Move the Activity raw feed toggle into a settings dropdown next to the close action, matching the profile panel settings ingress pattern.
- Normalize related Tailwind shorthand classes while touching the auxiliary panel headers.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-06-27 10:20:15 -07:00
parent bc3427389c
commit e07eefdcff
6 changed files with 164 additions and 122 deletions

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { ArrowLeft, CircleDot, Octagon, TerminalSquare, X } from "lucide-react";
import { CircleDot, Octagon, Settings, TerminalSquare, X } from "lucide-react";
import { toast } from "sonner";
import { ManagedAgentSessionPanel } from "@/features/agents/ui/ManagedAgentSessionPanel";
@@ -12,9 +12,11 @@ import { useStickToBottom } from "@/shared/hooks/useStickToBottom";
import { cn } from "@/shared/lib/cn";
import {
AuxiliaryPanelHeader,
AuxiliaryPanelHeaderActions,
AuxiliaryPanelHeaderGroup,
AuxiliaryPanelTitle,
auxiliaryPanelContentPaddingClass,
auxiliaryPanelHeaderPaddingClass,
} from "@/shared/layout/AuxiliaryPanelHeader";
import { Badge } from "@/shared/ui/badge";
import { Button } from "@/shared/ui/button";
@@ -28,6 +30,12 @@ import {
import { THREAD_PANEL_MIN_WIDTH_PX } from "@/shared/hooks/useThreadPanelWidth";
import { Switch } from "@/shared/ui/switch";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/shared/ui/dropdown-menu";
import type { ChannelAgentSessionAgent } from "./useChannelAgentSessions";
type AgentSessionThreadPanelProps = {
@@ -63,6 +71,7 @@ export function AgentSessionThreadPanel({
const { ref: scrollRef, onScroll } = useStickToBottom<HTMLDivElement>();
const rawFeedScopeKey = `${agent.pubkey}:${channel?.id ?? "all"}`;
const rawFeedSwitchId = React.useId();
const [rawFeedState, setRawFeedState] = React.useState(() => ({
scopeKey: rawFeedScopeKey,
show: false,
@@ -96,7 +105,7 @@ export function AgentSessionThreadPanel({
}
const agentHeaderActions = (
<div className="ml-auto flex shrink-0 items-center gap-2">
<AuxiliaryPanelHeaderActions>
{isLive && isWorking ? (
<Badge className="shrink-0 gap-1 px-2 py-0 text-2xs" variant="default">
<CircleDot className="h-2.5 w-2.5" />
@@ -104,32 +113,57 @@ export function AgentSessionThreadPanel({
</Badge>
) : null}
{isLive ? (
<div
className="flex min-w-19 shrink-0 items-center justify-end gap-2"
title={
showRawFeed
? "Hide raw JSON-RPC payloads."
: channel
? "Show raw JSON-RPC payloads for this channel."
: "Show raw JSON-RPC payloads for this agent."
}
>
<label
className="flex shrink-0 items-center gap-1.5 text-xs font-medium text-muted-foreground"
htmlFor="agent-session-raw-feed-switch"
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button
aria-label="Open activity settings"
data-testid="agent-session-settings-menu-trigger"
size="icon"
title="Activity settings"
type="button"
variant="ghost"
>
<Settings />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
className="min-w-56"
onCloseAutoFocus={(event) => event.preventDefault()}
>
<TerminalSquare className="h-3 w-3" />
Raw
</label>
<Switch
aria-label={showRawFeed ? "Hide raw feed" : "Show raw feed"}
checked={showRawFeed}
className="shrink-0 data-[state=unchecked]:bg-muted-foreground/45 [&>span]:bg-white"
data-testid="agent-session-toggle-raw-feed"
id="agent-session-raw-feed-switch"
onCheckedChange={handleRawFeedChange}
/>
</div>
<DropdownMenuItem
className="gap-3 pr-2"
onSelect={(event) => {
event.preventDefault();
handleRawFeedChange(!showRawFeed);
}}
title={
showRawFeed
? "Hide raw JSON-RPC payloads."
: channel
? "Show raw JSON-RPC payloads for this channel."
: "Show raw JSON-RPC payloads for this agent."
}
>
<TerminalSquare className="h-4 w-4 text-muted-foreground" />
<label
className="min-w-0 flex-1 text-sm font-medium"
htmlFor={rawFeedSwitchId}
>
Raw activity
</label>
<Switch
aria-label={showRawFeed ? "Hide raw feed" : "Show raw feed"}
checked={showRawFeed}
className="shrink-0 data-[state=unchecked]:bg-muted-foreground/45 [&>span]:bg-white"
data-testid="agent-session-toggle-raw-feed"
id={rawFeedSwitchId}
onCheckedChange={handleRawFeedChange}
onClick={(event) => event.stopPropagation()}
/>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : null}
{isLive && isWorking ? (
<Tooltip>
@@ -167,23 +201,16 @@ export function AgentSessionThreadPanel({
>
<X />
</Button>
</div>
</AuxiliaryPanelHeaderActions>
);
const agentHeaderContent = (
<>
<AuxiliaryPanelHeaderGroup>
<Button
aria-label="Back from activity"
className="shrink-0"
data-testid="agent-session-back"
onClick={onBackToProfile}
size="icon"
type="button"
variant="outline"
>
<ArrowLeft />
</Button>
<AuxiliaryPanelHeaderGroup
backButtonAriaLabel="Back from activity"
backButtonTestId="agent-session-back"
onBack={onBackToProfile}
>
<AuxiliaryPanelTitle>
{showRawFeed ? "Raw ACP Activity" : "Activity"}
</AuxiliaryPanelTitle>
@@ -199,7 +226,7 @@ export function AgentSessionThreadPanel({
className={cn(
"min-h-0 flex-1 overflow-y-auto px-3 pb-4",
isSplitLayout && auxiliaryPanelContentPaddingClass,
!isSplitLayout && (isFloatingOverlay ? "pt-4" : "pt-[3.25rem]"),
!isSplitLayout && (isFloatingOverlay ? "pt-4" : "pt-13"),
)}
>
<ManagedAgentSessionPanel
@@ -248,7 +275,7 @@ export function AgentSessionThreadPanel({
{!isOverlay ? (
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-[3.25rem] bg-background/75 backdrop-blur-md supports-[backdrop-filter]:bg-background/65 dark:bg-background/45 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/35"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-13 bg-background/75 backdrop-blur-md supports-backdrop-filter:bg-background/65 dark:bg-background/45 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/35"
/>
) : null}
@@ -256,8 +283,8 @@ export function AgentSessionThreadPanel({
className={cn(
"flex cursor-default select-none items-center",
isSinglePanelView
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-[3.25rem] min-h-[3.25rem] shrink-0 gap-2.5 bg-background/80 px-4 py-2 backdrop-blur-md supports-[backdrop-filter]:bg-background/70 sm:pl-6 sm:pr-3 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/55`
: "relative z-50 min-h-[3.25rem] shrink-0 gap-3 bg-background/80 px-5 py-2 backdrop-blur-md supports-[backdrop-filter]:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/55",
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-13 min-h-13 shrink-0 gap-2.5 bg-background/80 ${auxiliaryPanelHeaderPaddingClass} backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55`
: `relative z-50 min-h-13 shrink-0 gap-3 bg-background/80 ${auxiliaryPanelHeaderPaddingClass} backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55`,
)}
data-tauri-drag-region
>

View File

@@ -54,6 +54,7 @@ import { Input } from "@/shared/ui/input";
import { Textarea } from "@/shared/ui/textarea";
import {
AuxiliaryPanelHeader,
AuxiliaryPanelHeaderActions,
AuxiliaryPanelHeaderGroup,
AuxiliaryPanelTitle,
auxiliaryPanelContentPaddingClass,
@@ -320,7 +321,7 @@ export function ChannelManagementSheet({
"h-full w-full cursor-default overflow-hidden border-l-0 p-0",
animateSplitEnter && PANEL_ENTER_MOTION_CLASS,
isDark
? "bg-background/85 backdrop-blur-xl supports-[backdrop-filter]:bg-background/75"
? "bg-background/85 backdrop-blur-xl supports-backdrop-filter:bg-background/75"
: "bg-background",
)}
data-testid="channel-management-sheet"
@@ -368,7 +369,7 @@ export function ChannelManagementSheet({
PANEL_ENTER_MOTION_CLASS,
"w-[380px] cursor-default overflow-hidden p-0 data-[state=closed]:animate-out data-[state=closed]:slide-out-to-right data-[state=closed]:duration-200",
isDark
? "bg-background/85 backdrop-blur-xl supports-[backdrop-filter]:bg-background/75"
? "bg-background/85 backdrop-blur-xl supports-backdrop-filter:bg-background/75"
: "bg-background",
)}
data-testid="channel-management-sheet"
@@ -681,30 +682,25 @@ function ChannelManagementPanelContent({
<>
{isSplitLayout ? (
<AuxiliaryPanelHeader>
<AuxiliaryPanelHeaderGroup>
{activeView === "canvas" ? (
<Button
aria-label="Back to channel"
className="shrink-0"
data-testid="channel-management-back"
onClick={() => setActiveView("summary")}
size="icon"
type="button"
variant="outline"
>
<ChevronLeft />
</Button>
) : null}
<AuxiliaryPanelHeaderGroup
backButtonAriaLabel="Back to channel"
backButtonTestId="channel-management-back"
onBack={
activeView === "canvas"
? () => setActiveView("summary")
: undefined
}
>
<DialogPrimitive.Title asChild>
<AuxiliaryPanelTitle>
{activeView === "canvas" ? "Canvas" : "Channel"}
</AuxiliaryPanelTitle>
</DialogPrimitive.Title>
</AuxiliaryPanelHeaderGroup>
<div className="ml-auto flex shrink-0 items-center gap-2">
<AuxiliaryPanelHeaderActions>
<Button
aria-label="Close channel management"
className="relative z-[60]"
className="relative z-60"
data-testid="channel-management-close"
onClick={() => onOpenChange(false)}
onPointerDown={(event) => {
@@ -718,7 +714,7 @@ function ChannelManagementPanelContent({
>
<X />
</Button>
</div>
</AuxiliaryPanelHeaderActions>
<DialogPrimitive.Description className="sr-only">
Channel settings
</DialogPrimitive.Description>
@@ -728,8 +724,8 @@ function ChannelManagementPanelContent({
className={cn(
"relative z-10 flex min-h-11 flex-row items-center gap-3 space-y-0 border-b border-border/35 px-3 py-1.5 text-left shadow-none",
isDark
? "bg-background/70 backdrop-blur-xl supports-[backdrop-filter]:bg-background/55"
: "bg-background/80 backdrop-blur-md supports-[backdrop-filter]:bg-background/70",
? "bg-background/70 backdrop-blur-xl supports-backdrop-filter:bg-background/55"
: "bg-background/80 backdrop-blur-md supports-backdrop-filter:bg-background/70",
)}
>
<div className="flex min-w-0 flex-1 items-center gap-1.5">
@@ -751,7 +747,7 @@ function ChannelManagementPanelContent({
</div>
<Button
aria-label="Close channel management"
className="relative z-[60]"
className="relative z-60"
data-testid="channel-management-close"
onClick={() => onOpenChange(false)}
onPointerDown={(event) => {

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { ArrowDown, ArrowLeft, X } from "lucide-react";
import { ArrowDown, X } from "lucide-react";
import {
buildThreadSummaryFromVisibleEntries,
@@ -19,6 +19,7 @@ import {
AuxiliaryPanelHeaderGroup,
AuxiliaryPanelTitle,
auxiliaryPanelContentPaddingClass,
auxiliaryPanelHeaderPaddingClass,
} from "@/shared/layout/AuxiliaryPanelHeader";
import { Button } from "@/shared/ui/button";
import {
@@ -233,19 +234,10 @@ export function MessageThreadPanelSkeleton({
const threadHeaderContent = (
<>
<AuxiliaryPanelHeaderGroup>
{isSinglePanelView ? (
<Button
aria-label="Back to conversation"
className="shrink-0"
onClick={onClose}
size="icon"
type="button"
variant="outline"
>
<ArrowLeft />
</Button>
) : null}
<AuxiliaryPanelHeaderGroup
backButtonAriaLabel="Back to conversation"
onBack={isSinglePanelView ? onClose : undefined}
>
<AuxiliaryPanelTitle>Thread</AuxiliaryPanelTitle>
</AuxiliaryPanelHeaderGroup>
<Button
@@ -266,7 +258,7 @@ export function MessageThreadPanelSkeleton({
className={cn(
"min-h-0 flex-1 overflow-y-auto overflow-x-hidden overscroll-contain pb-24 [overflow-anchor:none]",
isSplitLayout && auxiliaryPanelContentPaddingClass,
!isSplitLayout && !isFloatingOverlay && "pt-[3.25rem]",
!isSplitLayout && !isFloatingOverlay && "pt-13",
)}
data-testid="message-thread-loading"
>
@@ -323,8 +315,8 @@ export function MessageThreadPanelSkeleton({
className={cn(
"flex cursor-default select-none items-center",
isSinglePanelView
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-[3.25rem] min-h-[3.25rem] shrink-0 gap-2.5 bg-background/80 px-4 py-2 backdrop-blur-md supports-[backdrop-filter]:bg-background/70 sm:pr-3 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/55`
: "relative z-50 min-h-[3.25rem] shrink-0 gap-3 bg-background/80 px-5 py-2 backdrop-blur-md supports-[backdrop-filter]:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/55",
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-13 min-h-13 shrink-0 gap-2.5 bg-background/80 ${auxiliaryPanelHeaderPaddingClass} backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55`
: `relative z-50 min-h-13 shrink-0 gap-3 bg-background/80 ${auxiliaryPanelHeaderPaddingClass} backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55`,
)}
data-tauri-drag-region
>
@@ -614,7 +606,7 @@ export function MessageThreadPanel({
className={cn(
"min-h-0 flex-1 overflow-y-auto overflow-x-hidden overscroll-contain pb-24 [overflow-anchor:none]",
isSplitLayout && auxiliaryPanelContentPaddingClass,
!isSplitLayout && !isFloatingOverlay && "pt-[3.25rem]",
!isSplitLayout && !isFloatingOverlay && "pt-13",
)}
data-testid="message-thread-body"
onScroll={onScroll}
@@ -910,20 +902,11 @@ export function MessageThreadPanel({
const threadHeaderContent = (
<>
<AuxiliaryPanelHeaderGroup>
{isSinglePanelView ? (
<Button
aria-label="Back to conversation"
className="shrink-0"
data-testid="message-thread-back"
onClick={onClose}
size="icon"
type="button"
variant="outline"
>
<ArrowLeft />
</Button>
) : null}
<AuxiliaryPanelHeaderGroup
backButtonAriaLabel="Back to conversation"
backButtonTestId="message-thread-back"
onBack={isSinglePanelView ? onClose : undefined}
>
<AuxiliaryPanelTitle>Thread</AuxiliaryPanelTitle>
</AuxiliaryPanelHeaderGroup>
<Button
@@ -970,8 +953,8 @@ export function MessageThreadPanel({
className={cn(
"flex cursor-default select-none items-center",
isSinglePanelView
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-[3.25rem] min-h-[3.25rem] shrink-0 gap-2.5 bg-background/80 px-4 py-2 backdrop-blur-md supports-[backdrop-filter]:bg-background/70 sm:pr-3 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/55`
: "relative z-50 min-h-[3.25rem] shrink-0 gap-3 bg-background/80 px-5 py-2 backdrop-blur-md supports-[backdrop-filter]:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/55",
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-13 min-h-13 shrink-0 gap-2.5 bg-background/80 ${auxiliaryPanelHeaderPaddingClass} backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55`
: `relative z-50 min-h-13 shrink-0 gap-3 bg-background/80 ${auxiliaryPanelHeaderPaddingClass} backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55`,
)}
data-tauri-drag-region
>

View File

@@ -1,7 +1,10 @@
import type * as React from "react";
import { THREAD_PANEL_MIN_WIDTH_PX } from "@/shared/hooks/useThreadPanelWidth";
import { AuxiliaryPanelHeader } from "@/shared/layout/AuxiliaryPanelHeader";
import {
AuxiliaryPanelHeader,
auxiliaryPanelHeaderPaddingClass,
} from "@/shared/layout/AuxiliaryPanelHeader";
import { cn } from "@/shared/lib/cn";
import {
OverlayPanelBackdrop,
@@ -111,10 +114,10 @@ export function UserProfilePanelFrame({
className={cn(
"flex cursor-default select-none items-center",
isSinglePanelView
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-13 min-h-13 shrink-0 gap-2.5 bg-transparent px-4 py-2 sm:pl-6 sm:pr-3`
? `relative ${PANEL_SINGLE_COLUMN_HEADER_LAYER_CLASS} -mb-13 min-h-13 shrink-0 gap-2.5 bg-transparent ${auxiliaryPanelHeaderPaddingClass}`
: isOverlay
? "relative z-50 min-h-13 shrink-0 gap-3 bg-background/80 px-5 py-2 backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55"
: "absolute inset-x-0 top-0 z-50 min-h-13 gap-3 bg-transparent px-3 py-2 after:absolute after:bottom-0 after:-left-px after:top-0 after:w-px after:bg-border/45 after:transition-colors peer-hover/profile-resize:after:bg-border/80 peer-focus-visible/profile-resize:after:bg-border/80",
? `relative z-50 min-h-13 shrink-0 gap-3 bg-background/80 ${auxiliaryPanelHeaderPaddingClass} backdrop-blur-md supports-backdrop-filter:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-backdrop-filter:bg-background/55`
: `absolute inset-x-0 top-0 z-50 min-h-13 gap-3 bg-transparent ${auxiliaryPanelHeaderPaddingClass} after:absolute after:bottom-0 after:-left-px after:top-0 after:w-px after:bg-border/45 after:transition-colors peer-hover/profile-resize:after:bg-border/80 peer-focus-visible/profile-resize:after:bg-border/80`,
)}
data-tauri-drag-region
>

View File

@@ -1,5 +1,5 @@
import type { ReactNode } from "react";
import { ArrowLeft, X } from "lucide-react";
import { X } from "lucide-react";
import { CopyButton } from "@/features/agents/ui/CopyButton";
import { MemoryRefreshButton } from "@/features/agent-memory/ui/MemorySection";
@@ -8,6 +8,7 @@ import {
type ProfilePanelView,
} from "@/features/profile/ui/UserProfilePanelUtils";
import {
AuxiliaryPanelHeaderActions,
AuxiliaryPanelHeaderGroup,
AuxiliaryPanelTitle,
} from "@/shared/layout/AuxiliaryPanelHeader";
@@ -37,21 +38,11 @@ export function getUserProfilePanelHeaderContent({
(view === "diagnostics" || view === "logs") && Boolean(logSubtitle);
const headerLeftContent = (
<AuxiliaryPanelHeaderGroup
backButtonAriaLabel="Back to profile"
backButtonTestId="user-profile-panel-back"
className={shouldShowLogDetails ? "items-start" : undefined}
onBack={view !== "summary" ? onBack : undefined}
>
{view !== "summary" ? (
<Button
aria-label="Back to profile"
className={shouldShowLogDetails ? "mt-0.5 shrink-0" : "shrink-0"}
data-testid="user-profile-panel-back"
onClick={onBack}
size="icon"
type="button"
variant="outline"
>
<ArrowLeft />
</Button>
) : null}
{shouldShowLogDetails ? (
<div className="min-w-0 flex-1">
<AuxiliaryPanelTitle className="translate-y-0 leading-5">
@@ -70,7 +61,7 @@ export function getUserProfilePanelHeaderContent({
</AuxiliaryPanelHeaderGroup>
);
const headerActions = (
<div className="ml-auto flex shrink-0 items-center gap-2">
<AuxiliaryPanelHeaderActions>
{view === "memories" && viewerIsOwner && effectivePubkey ? (
<MemoryRefreshButton
agentPubkey={effectivePubkey}
@@ -99,7 +90,7 @@ export function getUserProfilePanelHeaderContent({
>
<X />
</Button>
</div>
</AuxiliaryPanelHeaderActions>
);
return { headerActions, headerLeftContent };

View File

@@ -1,12 +1,24 @@
import type * as React from "react";
import { ArrowLeft } from "lucide-react";
import { channelChrome } from "@/shared/layout/chromeLayout";
import { cn } from "@/shared/lib/cn";
import { Button } from "@/shared/ui/button";
type AuxiliaryPanelHeaderProps = React.ComponentProps<"div">;
type AuxiliaryPanelHeaderGroupProps = React.ComponentProps<"div">;
type AuxiliaryPanelHeaderGroupProps = React.ComponentProps<"div"> & {
backButtonAriaLabel?: string;
backButtonTestId?: string;
onBack?: () => void;
};
type AuxiliaryPanelHeaderActionsProps = {
children: React.ReactNode;
};
type AuxiliaryPanelTitleProps = React.ComponentProps<"h2">;
export const auxiliaryPanelHeaderRightPaddingClass = "pr-2";
export const auxiliaryPanelHeaderPaddingClass = `pl-5 ${auxiliaryPanelHeaderRightPaddingClass} py-2`;
/** Compact title/action row for right auxiliary panels in split layouts. */
export function AuxiliaryPanelHeader({
className,
@@ -23,7 +35,10 @@ export function AuxiliaryPanelHeader({
{...props}
>
<div
className="pointer-events-auto relative z-30 shrink-0 cursor-default select-none px-5 py-2"
className={cn(
"pointer-events-auto relative z-30 shrink-0 cursor-default select-none",
auxiliaryPanelHeaderPaddingClass,
)}
data-tauri-drag-region
>
<div className="flex h-9 min-w-0 items-center gap-2.5">{children}</div>
@@ -35,8 +50,11 @@ export function AuxiliaryPanelHeader({
export const auxiliaryPanelContentPaddingClass = channelChrome.contentPadding;
export function AuxiliaryPanelHeaderGroup({
backButtonAriaLabel = "Back",
backButtonTestId,
className,
children,
onBack,
...props
}: AuxiliaryPanelHeaderGroupProps) {
return (
@@ -44,11 +62,35 @@ export function AuxiliaryPanelHeaderGroup({
className={cn("flex min-w-0 flex-1 items-center gap-1.5", className)}
{...props}
>
{onBack ? (
<Button
aria-label={backButtonAriaLabel}
// Header text needs a comfortable left inset, but a leading icon
// should visually sit closer to the panel edge. Keep the padding
// centralized on the header and pull only this shared button back.
className="-ml-2 shrink-0"
data-testid={backButtonTestId}
onClick={onBack}
size="icon"
type="button"
variant="outline"
>
<ArrowLeft />
</Button>
) : null}
{children}
</div>
);
}
export function AuxiliaryPanelHeaderActions({
children,
}: AuxiliaryPanelHeaderActionsProps) {
return (
<div className="ml-auto flex shrink-0 items-center gap-0.5">{children}</div>
);
}
export function AuxiliaryPanelTitle({
className,
children,