Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ export function AgentSessionThreadPanel({
{!isOverlay ? (
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-[76px] bg-background/45 backdrop-blur-xl after:absolute after:bottom-0 after:left-0 after:top-10 after:w-px after:bg-border/80 supports-[backdrop-filter]:bg-background/35"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-[76px] bg-background/75 backdrop-blur-md after:absolute after:bottom-0 after:left-0 after:top-10 after:w-px after:bg-border/80 supports-[backdrop-filter]:bg-background/65 dark:bg-background/45 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/35"
/>
) : null}

<div
className={cn(
"z-50 flex cursor-default select-none items-center gap-3 px-4",
isOverlay
? "relative min-h-[44px] shrink-0 border-b border-border/70 bg-background/70 py-2.5 backdrop-blur-xl supports-[backdrop-filter]:bg-background/55"
? "relative min-h-[44px] shrink-0 border-b border-border/70 bg-background/80 py-2.5 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-11 min-h-[32px] py-[4px]",
)}
data-tauri-drag-region
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/features/chat/ui/ChatHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export function ChatHeader({
}

return (
<div className="relative z-30 h-[76px] -mb-[76px] bg-background/70 pt-[42px] backdrop-blur-xl supports-[backdrop-filter]:bg-background/55">
<div className="relative z-30 h-[76px] -mb-[76px] bg-background/80 pt-[42px] backdrop-blur-md supports-[backdrop-filter]:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:supports-[backdrop-filter]:bg-background/55">
{header}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/features/home/ui/InboxDetailPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export function InboxDetailPane({
<div className="relative min-h-0 flex-1 overflow-hidden">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-[76px] bg-background/45 backdrop-blur-xl supports-[backdrop-filter]:bg-background/35"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-[76px] 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"
/>
<div className="absolute inset-x-0 top-[38px] z-50 flex min-h-[32px] items-center justify-between gap-3 py-[4px] pl-6 pr-3">
<div className="min-w-0">
Expand Down
17 changes: 12 additions & 5 deletions desktop/src/features/home/ui/InboxListPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "@/features/home/lib/inbox";
import { cn } from "@/shared/lib/cn";
import { Button } from "@/shared/ui/button";
import { Markdown } from "@/shared/ui/markdown";
import {
DropdownMenu,
DropdownMenuContent,
Expand Down Expand Up @@ -47,7 +48,7 @@ export function InboxListPane({
<section className="relative flex min-h-0 min-w-0 flex-col overflow-hidden bg-background/60">
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-[76px] bg-background/45 backdrop-blur-xl supports-[backdrop-filter]:bg-background/35"
className="pointer-events-none absolute inset-x-0 top-0 z-40 h-[76px] 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"
/>
<div className="absolute inset-x-0 top-[42px] z-50 min-h-[32px] px-5 py-[4px]">
<div className="flex min-w-0 items-center justify-between gap-3">
Expand Down Expand Up @@ -159,16 +160,22 @@ export function InboxListPane({
</span>
</div>

<p
<div
className={cn(
"mt-0.5 line-clamp-2 text-sm leading-5",
"mt-0.5 line-clamp-2 text-sm leading-5 [&_*]:inline [&_a]:font-medium [&_a]:text-current [&_br]:hidden [&_p]:inline",
isDone
? "font-normal text-muted-foreground"
: "font-semibold text-foreground",
)}
>
{item.preview}
</p>
<Markdown
className="inline max-w-full text-inherit"
content={item.preview}
interactive={false}
Comment on lines +171 to +174
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Render media as plain preview content

For inbox items whose content contains image or video markdown, this new preview path sends the full content through Markdown with interactive={false}, but the markdown renderer only disables links/mentions/message buttons; the img renderer still creates the lightbox/context-menu/video UI. Since this preview is inside the row button, media-only messages can render clickable media controls and alter the row height instead of showing a compact two-line preview. Either strip media for previews or make media rendering honor interactive={false}.

Useful? React with 👍 / 👎.

mentionNames={item.mentionNames}
tight
/>
</div>

<div className="mt-1 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
<span
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/features/messages/ui/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ export function MessageComposer({
/>
<div className="relative flex w-full flex-col gap-3">
<form
className="relative isolate rounded-2xl border border-border/50 bg-background/70 px-3 pb-2 pt-3 shadow-[0_4px_24px_rgba(0,0,0,0.08)] backdrop-blur-xl supports-[backdrop-filter]:bg-background/55 dark:shadow-[0_4px_24px_rgba(0,0,0,0.35)] sm:px-4"
className="relative isolate rounded-2xl border border-border/50 bg-background/80 px-3 pb-2 pt-3 shadow-[0_4px_24px_rgba(0,0,0,0.08)] backdrop-blur-md supports-[backdrop-filter]:bg-background/70 dark:bg-background/70 dark:backdrop-blur-xl dark:shadow-[0_4px_24px_rgba(0,0,0,0.35)] dark:supports-[backdrop-filter]:bg-background/55 sm:px-4"
data-testid="message-composer"
onDragEnter={media.handleDragEnter}
onDragLeave={media.handleDragLeave}
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/features/messages/ui/MessageThreadPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export function MessageThreadPanel({
className={cn(
"z-50 flex cursor-default select-none items-center gap-3 px-3",
isOverlay
? "relative min-h-[44px] shrink-0 bg-background/70 py-[6px] backdrop-blur-xl supports-[backdrop-filter]:bg-background/55"
? "relative min-h-[44px] shrink-0 bg-background/80 py-[6px] 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-11 min-h-[32px] py-[4px]",
)}
data-tauri-drag-region
Expand Down
5 changes: 4 additions & 1 deletion desktop/src/features/messages/ui/MessageTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,10 @@ export const MessageTimeline = React.memo(function MessageTimeline({
onScroll={syncScrollState}
ref={scrollContainerRef}
>
<div className="flex w-full flex-col gap-2 pt-12" ref={contentRef}>
<div
className="flex w-full flex-col gap-2 pt-[76px]"
ref={contentRef}
>
<div ref={topSentinelRef} aria-hidden className="h-px" />

{isFetchingOlder ? (
Expand Down
155 changes: 78 additions & 77 deletions desktop/src/features/sidebar/ui/AppSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ export function AppSidebar({
</SidebarMenu>
</SidebarHeader>

<div className="relative flex min-h-0 flex-1 flex-col">
<div className="relative flex min-h-0 flex-1 flex-col overflow-hidden">
{unreadAboveCount > 0 ? (
<MoreUnreadButton
count={unreadAboveCount}
Expand All @@ -610,7 +610,7 @@ export function AppSidebar({
testId="sidebar-more-unread-above"
/>
) : null}
<SidebarContent ref={scrollRef}>
<SidebarContent className="pb-32" ref={scrollRef}>
{isLoading ? (
<SidebarGroup>
<SidebarGroupLabel>Channels</SidebarGroupLabel>
Expand Down Expand Up @@ -713,95 +713,96 @@ export function AppSidebar({

{unreadBelowCount > 0 ? (
<MoreUnreadButton
bottomClassName="bottom-28"
count={unreadBelowCount}
icon={<ArrowDown />}
onClick={scrollToNextBelow}
position="bottom"
testId="sidebar-more-unread-below"
/>
) : null}
</div>

<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<div
className="rounded-xl px-2 py-2 transition-colors hover:bg-sidebar-accent/70 focus-within:bg-sidebar-accent/70"
data-testid="sidebar-profile-card"
>
<div className="flex min-w-0 items-center gap-3">
<div className="relative shrink-0">
<ProfileAvatar
avatarUrl={profile?.avatarUrl ?? null}
className="h-10 w-10 rounded-2xl text-sm"
iconClassName="h-5 w-5"
label={resolvedDisplayName}
testId="sidebar-profile-avatar"
/>
<span
aria-label={getPresenceLabel(selfPresenceStatus)}
className="absolute -bottom-0.5 -right-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-sidebar"
data-testid="self-presence-badge"
role="img"
>
<PresenceDot
className="h-2.5 w-2.5"
status={selfPresenceStatus}
<SidebarFooter className="absolute inset-x-0 bottom-0 z-30 bg-sidebar/55 shadow-[0_-16px_40px_hsl(var(--sidebar)/0.55)] backdrop-blur-xl supports-[backdrop-filter]:bg-sidebar/45 dark:bg-sidebar/45 dark:shadow-[0_-16px_44px_rgba(0,0,0,0.42)] dark:supports-[backdrop-filter]:bg-sidebar/35">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep unread-below button above the footer

When there are unread channels below the current scroll position, MoreUnreadButton is still rendered at bottom-0 with z-10, but this newly absolute footer occupies the same bottom edge with z-30. In long channel lists the "more unread below" control is covered by the profile footer and its clicks are intercepted, so users lose the shortcut to jump to the next unread item. Offset the button above the footer or keep it in a higher/non-overlapping layer.

Useful? React with 👍 / 👎.

<SidebarMenu>
<SidebarMenuItem>
<div
className="rounded-xl px-2 py-2 transition-colors hover:bg-sidebar-accent/35 focus-within:bg-sidebar-accent/35 dark:hover:bg-sidebar-accent/25 dark:focus-within:bg-sidebar-accent/25"
data-testid="sidebar-profile-card"
>
<div className="flex min-w-0 items-center gap-3">
<div className="relative shrink-0">
<ProfileAvatar
avatarUrl={profile?.avatarUrl ?? null}
className="h-10 w-10 rounded-2xl text-sm"
iconClassName="h-5 w-5"
label={resolvedDisplayName}
testId="sidebar-profile-avatar"
/>
</span>
</div>
<div className="min-w-0 flex-1">
<ProfilePopover
open={profilePopoverOpen}
onOpenChange={setProfilePopoverOpen}
displayName={resolvedDisplayName}
nip05={profile?.nip05Handle}
avatarUrl={profile?.avatarUrl ?? null}
currentStatus={selfPresenceStatus}
isStatusPending={isPresencePending}
userStatusText={selfUserStatus?.text}
userStatusEmoji={selfUserStatus?.emoji}
onSetStatus={onSetPresenceStatus ?? (() => {})}
onSetUserStatus={onSetUserStatus}
onClearUserStatus={onClearUserStatus}
onOpenSettings={onSelectSettings}
>
<button
className="block w-full min-w-0 text-left text-sidebar-foreground"
data-testid="open-settings"
type="button"
<span
aria-label={getPresenceLabel(selfPresenceStatus)}
className="absolute -bottom-0.5 -right-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-sidebar"
data-testid="self-presence-badge"
role="img"
>
<p
className="truncate text-sm font-semibold text-current"
data-testid="sidebar-profile-name"
<PresenceDot
className="h-2.5 w-2.5"
status={selfPresenceStatus}
/>
</span>
</div>
<div className="min-w-0 flex-1">
<ProfilePopover
open={profilePopoverOpen}
onOpenChange={setProfilePopoverOpen}
displayName={resolvedDisplayName}
nip05={profile?.nip05Handle}
avatarUrl={profile?.avatarUrl ?? null}
currentStatus={selfPresenceStatus}
isStatusPending={isPresencePending}
userStatusText={selfUserStatus?.text}
userStatusEmoji={selfUserStatus?.emoji}
onSetStatus={onSetPresenceStatus ?? (() => {})}
onSetUserStatus={onSetUserStatus}
onClearUserStatus={onClearUserStatus}
onOpenSettings={onSelectSettings}
>
<button
className="block w-full min-w-0 text-left text-sidebar-foreground"
data-testid="open-settings"
type="button"
>
{resolvedDisplayName}
<p
className="truncate text-sm font-semibold text-current"
data-testid="sidebar-profile-name"
>
{resolvedDisplayName}
</p>
</button>
</ProfilePopover>
<WorkspaceSwitcher
activeWorkspace={activeWorkspace}
onAddWorkspace={onOpenAddWorkspace}
onRemoveWorkspace={onRemoveWorkspace}
onSwitchWorkspace={onSwitchWorkspace}
onUpdateWorkspace={onUpdateWorkspace}
variant="profile"
workspaces={workspaces}
/>
{selfUserStatus?.text || selfUserStatus?.emoji ? (
<p className="mt-0.5 truncate text-xs text-sidebar-foreground/50">
{selfUserStatus.emoji ? (
<span className="mr-1">{selfUserStatus.emoji}</span>
) : null}
{selfUserStatus.text}
</p>
</button>
</ProfilePopover>
<WorkspaceSwitcher
activeWorkspace={activeWorkspace}
onAddWorkspace={onOpenAddWorkspace}
onRemoveWorkspace={onRemoveWorkspace}
onSwitchWorkspace={onSwitchWorkspace}
onUpdateWorkspace={onUpdateWorkspace}
variant="profile"
workspaces={workspaces}
/>
{selfUserStatus?.text || selfUserStatus?.emoji ? (
<p className="mt-0.5 truncate text-xs text-sidebar-foreground/50">
{selfUserStatus.emoji ? (
<span className="mr-1">{selfUserStatus.emoji}</span>
) : null}
{selfUserStatus.text}
</p>
) : null}
) : null}
</div>
</div>
</div>
</div>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</div>

<CreateChannelDialog
channelKind={createDialogKind}
Expand Down
4 changes: 3 additions & 1 deletion desktop/src/features/sidebar/ui/MoreUnreadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const MORE_UNREAD_BUTTON_CLASS =
"h-7 min-h-7 gap-1.5 rounded-full border-0 bg-primary px-2.5 text-[11px] font-medium text-primary-foreground shadow-md hover:bg-primary/90 [&_svg]:size-3.5";

export function MoreUnreadButton({
bottomClassName = "bottom-0",
count,
icon,
onClick,
position,
testId,
}: {
bottomClassName?: string;
count: number;
icon: React.ReactNode;
onClick: () => void;
Expand All @@ -20,7 +22,7 @@ export function MoreUnreadButton({
}) {
return (
<div
className={`pointer-events-none absolute inset-x-0 z-10 flex justify-center py-1 ${position === "top" ? "top-0" : "bottom-0"}`}
className={`pointer-events-none absolute inset-x-0 z-10 flex justify-center py-1 ${position === "top" ? "top-0" : bottomClassName}`}
>
<Button
className={`pointer-events-auto ${MORE_UNREAD_BUTTON_CLASS}`}
Expand Down
Loading