diff --git a/AGENTS.md b/AGENTS.md
index c94ba3881e..358d579e53 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -112,6 +112,7 @@ Additional rules:
- No `unsafe` code
- Do not introduce new `unwrap()` or `expect()` in production paths — use `?` and proper error types
- New public API must have doc comments
+- **DCO Sign-off**: Always sign off your commits (e.g. `git commit -s`) to comply with the Developer Certificate of Origin (DCO) check.
---
diff --git a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
index a3a8a20231..3bd505aed2 100644
--- a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
+++ b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
@@ -4,6 +4,7 @@ import type * as React from "react";
import { ChatHeader } from "@/features/chat/ui/ChatHeader";
import type { EphemeralChannelDisplay } from "@/features/channels/lib/ephemeralChannel";
import type { ActiveDmHeaderParticipant } from "@/features/channels/useActiveChannelHeader";
+import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
import { getChannelDescription } from "@/features/channels/lib/channelDescription";
import { getDmParticipantPreview } from "@/features/channels/lib/dmParticipantDisplay";
import { ChannelHeaderStatusBadge } from "@/features/channels/ui/ChannelHeaderStatusBadge";
@@ -113,6 +114,30 @@ export function ChannelScreenHeader({
+ ) : activeDmHeaderParticipants.length === 1 ? (
+
+
+
) : (
);
}
@@ -170,12 +201,23 @@ function DmHeaderParticipantStack({
}),
}}
>
-
+
+
+
))}
{hiddenCount > 0 ? (
diff --git a/desktop/src/features/chat/ui/ChatHeader.tsx b/desktop/src/features/chat/ui/ChatHeader.tsx
index 9ced506751..5f1299a6c5 100644
--- a/desktop/src/features/chat/ui/ChatHeader.tsx
+++ b/desktop/src/features/chat/ui/ChatHeader.tsx
@@ -15,6 +15,7 @@ import { toast } from "sonner";
import type { ChannelType, ChannelVisibility } from "@/shared/api/types";
import { UpdateIndicator } from "@/features/settings/UpdateIndicator";
+import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
import { cn } from "@/shared/lib/cn";
import { channelChrome } from "@/shared/layout/chromeLayout";
import { Button } from "@/shared/ui/button";
@@ -35,6 +36,7 @@ type ChatHeaderProps = {
statusBadge?: React.ReactNode;
/** Render the chrome wrapper without an individual backdrop when a parent supplies shared blur. */
transparentChrome?: boolean;
+ dmUserPubkey?: string;
};
const HEADER_ICON_CLASS = "h-4 w-4 text-muted-foreground";
@@ -97,6 +99,7 @@ export function ChatHeader({
overlaysContent = false,
statusBadge,
transparentChrome = false,
+ dmUserPubkey,
}: ChatHeaderProps) {
const trimmedDescription = description?.trim() ?? "";
@@ -133,27 +136,51 @@ export function ChatHeader({
/>
)}
-
- {title}
-
-
+ {dmUserPubkey ? (
+
+
+
+ ) : (
+
+ {title}
+
+ )}
+ {channelType !== "dm" && (
+
+ )}
{statusBadge ? (
{statusBadge}
diff --git a/desktop/src/features/profile/ui/UserProfilePopover.tsx b/desktop/src/features/profile/ui/UserProfilePopover.tsx
index 9161bac302..fa567e7796 100644
--- a/desktop/src/features/profile/ui/UserProfilePopover.tsx
+++ b/desktop/src/features/profile/ui/UserProfilePopover.tsx
@@ -63,6 +63,7 @@ type UserProfilePopoverProps = {
role?: string;
/** Value used to generate the BotIdenticon glyph (typically the author name). */
botIdenticonValue?: string;
+ hideMessageAction?: boolean;
};
const HOVER_OPEN_DELAY_MS = 500;
@@ -175,6 +176,7 @@ export function UserProfilePopover({
enableProfilePanel = true,
role,
botIdenticonValue,
+ hideMessageAction = false,
}: UserProfilePopoverProps) {
const [open, setOpen] = React.useState(false);
const [pendingAction, setPendingAction] = React.useState<
@@ -260,6 +262,7 @@ export function UserProfilePopover({
const isCurrentUserOwner = ownsAuthorAgent(profile, currentPubkey);
const viewerIsOwner = isCurrentUserOwner || isOwner === true;
const showMessageAction =
+ !hideMessageAction &&
showProfileActions &&
!isAgentClassificationPending &&
(!isBotProfile || viewerIsOwner);