diff --git a/desktop/src/features/home/ui/HomeView.tsx b/desktop/src/features/home/ui/HomeView.tsx index e8db2a5daf..9461ef9c08 100644 --- a/desktop/src/features/home/ui/HomeView.tsx +++ b/desktop/src/features/home/ui/HomeView.tsx @@ -40,7 +40,12 @@ import { resolveUserLabel } from "@/features/profile/lib/identity"; import { deleteMessage, sendChannelMessage } from "@/shared/api/tauri"; import type { HomeFeedResponse } from "@/shared/api/types"; import { KIND_REACTION } from "@/shared/constants/kinds"; -import { topChromeInset } from "@/shared/layout/chromeLayout"; +import { + insetHeaderHeightMeasurement, + insetHeaderOverlay, + topChromeInset, +} from "@/shared/layout/chromeLayout"; +import { useMeasuredCssVariable } from "@/shared/layout/useMeasuredCssVariable"; import { cn } from "@/shared/lib/cn"; import { resolveMentionNames } from "@/shared/lib/resolveMentionNames"; import { useElementWidth } from "@/shared/hooks/use-mobile"; @@ -68,6 +73,12 @@ export function HomeView({ onRefresh, }: HomeViewProps) { const [homeInboxRef, homeInboxWidthPx] = useElementWidth(); + // One pane header drives the shared backdrop strip height so the blur is a + // single element spanning both columns instead of clipping per pane. + const headerChromeRef = useMeasuredCssVariable({ + targetRef: homeInboxRef, + ...insetHeaderHeightMeasurement, + }); const isNarrowHomeViewport = homeInboxWidthPx > 0 && homeInboxWidthPx < INBOX_SINGLE_COLUMN_BREAKPOINT_PX; @@ -348,10 +359,13 @@ export function HomeView({ } as React.CSSProperties } > +
+ {showListPane ? ( { @@ -398,6 +412,7 @@ export function HomeView({ contextChannelName={selectedChannel?.name ?? null} currentPubkey={currentPubkey} disabledReplyReason={disabledReplyReason} + headerChromeRef={showListPane ? undefined : headerChromeRef} isDeletingMessage={isDeletingMessage} isDone={ selectedItem ? effectiveDoneSet.has(selectedItem.id) : false diff --git a/desktop/src/features/home/ui/InboxDetailPane.tsx b/desktop/src/features/home/ui/InboxDetailPane.tsx index 2104811b1c..69499df962 100644 --- a/desktop/src/features/home/ui/InboxDetailPane.tsx +++ b/desktop/src/features/home/ui/InboxDetailPane.tsx @@ -24,6 +24,7 @@ import type { TimelineMessage } from "@/features/messages/types"; import { MessageComposer } from "@/features/messages/ui/MessageComposer"; import { UpdateIndicator } from "@/features/settings/UpdateIndicator"; import type { Channel } from "@/shared/api/types"; +import { insetHeaderOverlay } from "@/shared/layout/chromeLayout"; import { TopChromeInsetHeader } from "@/shared/layout/TopChromeInsetHeader"; import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; @@ -46,6 +47,8 @@ type InboxDetailPaneProps = { canOpenChannel: boolean; canReply: boolean; disabledReplyReason?: string | null; + /** Measured ref wiring the header height to the shared backdrop strip. */ + headerChromeRef?: React.Ref; isDone: boolean; isDeletingMessage?: boolean; isSendingReply?: boolean; @@ -80,6 +83,7 @@ export function InboxDetailPane({ canOpenChannel, canReply, disabledReplyReason, + headerChromeRef, isDone, isDeletingMessage = false, isSendingReply = false, @@ -234,7 +238,11 @@ export function InboxDetailPane({ ref={detailPaneRef} >
- +
-
+
{isThreadContextLoading ? (
diff --git a/desktop/src/features/home/ui/InboxListPane.tsx b/desktop/src/features/home/ui/InboxListPane.tsx index f3f2af6621..ed01793bc9 100644 --- a/desktop/src/features/home/ui/InboxListPane.tsx +++ b/desktop/src/features/home/ui/InboxListPane.tsx @@ -1,11 +1,15 @@ import { ChevronDown, Inbox } from "lucide-react"; +import type * as React from "react"; import { formatInboxTypeLabel, type InboxFilter, type InboxItem, } from "@/features/home/lib/inbox"; -import { topChromeInset } from "@/shared/layout/chromeLayout"; +import { + insetHeaderOverlay, + topChromeInset, +} from "@/shared/layout/chromeLayout"; import { TopChromeInsetHeader } from "@/shared/layout/TopChromeInsetHeader"; import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; @@ -30,6 +34,8 @@ const FILTER_OPTIONS: Array<{ label: string; value: InboxFilter }> = [ type InboxListPaneProps = { doneSet: ReadonlySet; filter: InboxFilter; + /** Measured ref wiring the header height to the shared backdrop strip. */ + headerChromeRef?: React.Ref; items: InboxItem[]; onFilterChange: (filter: InboxFilter) => void; onSelect: (itemId: string) => void; @@ -40,6 +46,7 @@ type InboxListPaneProps = { export function InboxListPane({ doneSet, filter, + headerChromeRef, items, onFilterChange, onSelect, @@ -55,7 +62,11 @@ export function InboxListPane({ showRightDivider && topChromeInset.verticalDivider, )} > - +
@@ -99,7 +110,10 @@ export function InboxListPane({
{items.length === 0 ? ( diff --git a/desktop/src/shared/layout/TopChromeInsetHeader.tsx b/desktop/src/shared/layout/TopChromeInsetHeader.tsx index 66f554c883..a9eff76ebf 100644 --- a/desktop/src/shared/layout/TopChromeInsetHeader.tsx +++ b/desktop/src/shared/layout/TopChromeInsetHeader.tsx @@ -3,7 +3,14 @@ import type * as React from "react"; import { topChromeInset } from "@/shared/layout/chromeLayout"; import { cn } from "@/shared/lib/cn"; -type TopChromeInsetHeaderProps = React.ComponentProps<"div">; +type TopChromeInsetHeaderProps = React.ComponentProps<"div"> & { + /** + * Render without its own backdrop, borders, and hairlines — for headers + * stacked on a shared `insetHeaderOverlay.backdrop` strip that spans + * multiple columns (keeps the blur continuous across pane boundaries). + */ + transparent?: boolean; +}; /** * Flowed header row that clears the global search/drag chrome and draws the @@ -12,14 +19,16 @@ type TopChromeInsetHeaderProps = React.ComponentProps<"div">; export function TopChromeInsetHeader({ className, children, + transparent = false, ...props }: TopChromeInsetHeaderProps) { return (