From 916f57891e520d58d9e4120961b313dd50e65c20 Mon Sep 17 00:00:00 2001 From: Thomas Petersen Date: Sat, 13 Jun 2026 13:35:48 -0400 Subject: [PATCH 1/3] fix(desktop): continuous blur across inbox header columns Render a single full-width backdrop strip behind the inbox pane headers instead of a per-pane backdrop-filter box. The blur now samples continuously across the column boundary rather than clipping at each pane edge. Inset headers become transparent and share a measured height via a new CSS variable so the strip spans both columns. Co-authored-by: Cursor --- desktop/src/features/home/ui/HomeView.tsx | 17 ++++++++++- .../src/features/home/ui/InboxDetailPane.tsx | 17 +++++++++-- .../src/features/home/ui/InboxListPane.tsx | 20 +++++++++++-- .../shared/layout/TopChromeInsetHeader.tsx | 15 ++++++++-- desktop/src/shared/layout/chromeLayout.ts | 30 ++++++++++++++++++- 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/desktop/src/features/home/ui/HomeView.tsx b/desktop/src/features/home/ui/HomeView.tsx index e8db2a5da..9461ef9c0 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 2104811b1..69499df96 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 f3f2af662..ed01793bc 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 66f554c88..a9eff76eb 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 (
Date: Wed, 17 Jun 2026 08:38:08 -0400 Subject: [PATCH 2/3] fix(desktop): remove header divider lines Keep the shared header blur treatment while removing the horizontal rules drawn by the global chrome, channel header, and top chrome backdrop. Co-authored-by: Cursor --- desktop/src/app/AppTopChrome.tsx | 17 ----------------- desktop/src/features/chat/ui/ChatHeader.tsx | 2 +- desktop/src/shared/layout/chromeLayout.ts | 7 +++---- desktop/src/shared/ui/TopChromeBackdrop.tsx | 3 +-- 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/desktop/src/app/AppTopChrome.tsx b/desktop/src/app/AppTopChrome.tsx index 17c68ab60..28370b4a4 100644 --- a/desktop/src/app/AppTopChrome.tsx +++ b/desktop/src/app/AppTopChrome.tsx @@ -26,22 +26,6 @@ type AppTopChromeProps = { searchLoading?: boolean; }; -function GlobalTopDivider() { - const sidebar = useOptionalSidebar(); - const state = sidebar?.state ?? "collapsed"; - - return ( -