diff --git a/desktop/src/features/channels/lib/threadFocusLayout.ts b/desktop/src/features/channels/lib/threadFocusLayout.ts index f14f3399bd..f0b40348d0 100644 --- a/desktop/src/features/channels/lib/threadFocusLayout.ts +++ b/desktop/src/features/channels/lib/threadFocusLayout.ts @@ -24,7 +24,7 @@ export const THREAD_FOCUS_SLIVER_WIDTH_PX = 72; * with auto horizontal margins so the reading measure stays comfortable no * matter how wide the window gets. */ -export const THREAD_FOCUS_COLUMN_MAX_WIDTH_PX = 880; +export const THREAD_FOCUS_COLUMN_MAX_WIDTH_PX = 820; /** * Horizontal distance the focus drawer travels on enter/exit. diff --git a/desktop/src/features/messages/lib/messageThreadPanelLayout.ts b/desktop/src/features/messages/lib/messageThreadPanelLayout.ts index a23510d887..b7671e226f 100644 --- a/desktop/src/features/messages/lib/messageThreadPanelLayout.ts +++ b/desktop/src/features/messages/lib/messageThreadPanelLayout.ts @@ -6,15 +6,13 @@ /** Inline gutter around thread message rows. */ export const THREAD_PANEL_MESSAGE_GUTTER_CLASS = "px-4"; -/** Restores the original message gutter inside the expanded focus overlay. */ -export const THREAD_FOCUS_MESSAGE_GUTTER_CLASS = "px-2"; - /** Inline gutter around the thread composer and its activity row. */ export const THREAD_PANEL_COMPOSER_GUTTER_CLASS = "px-5"; /** * Centers the reading column when a `columnMaxWidthPx` is supplied (focus-mode - * drawer). `px-10` is the original inline gutter between the column and drawer - * edges; the max-width itself is applied inline since it is caller-provided. + * drawer). The responsive gutter keeps compact focus drawers usable while + * preserving generous whitespace at desktop widths. The max-width itself is + * applied inline since it is a caller-provided value. */ -export const THREAD_PANEL_COLUMN_CLASS = "mx-auto w-full px-10"; +export const THREAD_PANEL_COLUMN_CLASS = "mx-auto w-full px-6 sm:px-10"; diff --git a/desktop/src/features/messages/ui/MessageRow.tsx b/desktop/src/features/messages/ui/MessageRow.tsx index 9e849ff22c..d1eb9a2c15 100644 --- a/desktop/src/features/messages/ui/MessageRow.tsx +++ b/desktop/src/features/messages/ui/MessageRow.tsx @@ -12,7 +12,10 @@ import { useKnownAgentPubkeys } from "@/features/agents/useKnownAgentPubkeys"; import { HuddleAttachment } from "@/features/huddle/components/HuddleAttachment"; import { MessageReactions } from "@/features/messages/ui/MessageReactions"; import { useReactionHandler } from "@/features/messages/ui/useReactionHandler"; -import type { UserProfileLookup } from "@/features/profile/lib/identity"; +import { + formatOwnerLabel, + type UserProfileLookup, +} from "@/features/profile/lib/identity"; import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover"; import { useRemindLater } from "@/features/reminders/ui/RemindMeLaterProvider"; import { @@ -81,6 +84,7 @@ export const MessageRow = React.memo( isUnread, layoutVariant = "default", message, + currentPubkey, onCollapseDepthGuide, onCollapseDepthGuideHoverChange, onCollapseDescendants, @@ -120,6 +124,7 @@ export const MessageRow = React.memo( isUnread?: boolean; layoutVariant?: "default" | "thread-reply"; message: TimelineMessage; + currentPubkey?: string; onCollapseDepthGuide?: (message: TimelineMessage) => void; onCollapseDepthGuideHoverChange?: ( message: TimelineMessage, @@ -209,11 +214,11 @@ export const MessageRow = React.memo( }, [knownAgentPubkeys, profiles], ); - const profilePopoverRole = + const displayedAuthorIsAgent = message.role === "bot" || - (message.pubkey && isKnownAgentPubkey(message.pubkey)) - ? "bot" - : message.role; + message.isAgent === true || + Boolean(message.pubkey && isKnownAgentPubkey(message.pubkey)); + const profilePopoverRole = displayedAuthorIsAgent ? "bot" : message.role; const agentMentionPubkeysByName = React.useMemo(() => { if (!mentionPubkeysByName) { return undefined; @@ -246,7 +251,8 @@ export const MessageRow = React.memo( message.body, message.tags, ); - const bodyOffsetClass = "mt-1"; + const bodyOffsetClass = + layoutVariant === "thread-reply" || emojiOnly ? "mt-1" : "-mt-0.5"; const { nonDmChannelNames: channelNames } = useChannelNavigation(); const openVideoReviewAt = useOpenVideoReviewAt(); @@ -367,7 +373,9 @@ export const MessageRow = React.memo( p+p]:mt-2 [&>ol]:space-y-1.5 [&>ul]:space-y-1.5", + "max-w-full text-sm", + layoutVariant === "thread-reply" && + "leading-relaxed [&>p+p]:mt-2 [&>ol]:space-y-1.5 [&>ul]:space-y-1.5", emojiOnly && "text-4xl leading-tight [&_p]:leading-tight [&_img[data-custom-emoji]]:h-[1.45em] [&_img[data-custom-emoji]]:align-middle [&_button:has(img[data-custom-emoji])]:align-middle", )} @@ -503,10 +511,18 @@ export const MessageRow = React.memo( ) : ( {message.author} ); - const agentOwnerNode = message.isAgent ? ( + const agentOwnerPubkey = + message.ownerPubkey ?? + (message.pubkey + ? profiles?.[normalizePubkey(message.pubkey)]?.ownerPubkey + : null); + const agentOwnerLabel = + message.ownerLabel ?? + formatOwnerLabel(agentOwnerPubkey, currentPubkey, profiles); + const agentOwnerNode = displayedAuthorIsAgent ? ( ) : null; @@ -823,7 +839,7 @@ export const MessageRow = React.memo( className={cn( "group/message relative z-10 rounded-2xl transition-colors", playEntrance && "motion-enter-conversation", - isThreadReplyLayout ? "py-2" : "py-1.5", + isThreadReplyLayout ? "py-2" : "py-1", hoverBackground ? "mx-1 px-2 hover:bg-muted/50 focus-within:bg-muted/50" : isThreadReplyLayout @@ -872,6 +888,7 @@ export const MessageRow = React.memo( prev.message.isAgent === next.message.isAgent && prev.message.ownerPubkey === next.message.ownerPubkey && prev.message.ownerLabel === next.message.ownerLabel && + prev.currentPubkey === next.currentPubkey && prev.message.avatarUrl === next.message.avatarUrl && prev.message.accent === next.message.accent && prev.message.time === next.message.time && diff --git a/desktop/src/features/messages/ui/MessageThreadPanel.tsx b/desktop/src/features/messages/ui/MessageThreadPanel.tsx index 63c599c56d..b7b3dcfe7a 100644 --- a/desktop/src/features/messages/ui/MessageThreadPanel.tsx +++ b/desktop/src/features/messages/ui/MessageThreadPanel.tsx @@ -34,7 +34,6 @@ import { AuxiliaryPanelTitle, } from "@/shared/layout/AuxiliaryPanel"; import { - THREAD_FOCUS_MESSAGE_GUTTER_CLASS, THREAD_PANEL_COLUMN_CLASS, THREAD_PANEL_COMPOSER_GUTTER_CLASS, THREAD_PANEL_MESSAGE_GUTTER_CLASS, @@ -256,9 +255,6 @@ export function MessageThreadPanel({ !isHuddleTranscript && (isOverlay || isSinglePanelView || isFocusMode), ); const hasConstrainedColumn = columnMaxWidthPx != null; - const messageGutterClass = hasConstrainedColumn - ? THREAD_FOCUS_MESSAGE_GUTTER_CLASS - : THREAD_PANEL_MESSAGE_GUTTER_CLASS; // Whether the composer dock trades its quiet-state spacer for the // conditional activity accessory (agent working and/or someone typing). const hasComposerBottomActivity = @@ -573,18 +569,19 @@ export function MessageThreadPanel({ } > {isHuddleTranscript ? ( -
+
) : (
@@ -643,7 +640,7 @@ export function MessageThreadPanel({ ) : null}
{threadRepliesPending && !isHuddleTranscript ? ( @@ -717,6 +714,7 @@ export function MessageThreadPanel({ {showUnreadDivider ? : null}
-
+
diff --git a/desktop/src/features/messages/ui/TimelineMessageRow.tsx b/desktop/src/features/messages/ui/TimelineMessageRow.tsx index 283760fb02..0f475bc605 100644 --- a/desktop/src/features/messages/ui/TimelineMessageRow.tsx +++ b/desktop/src/features/messages/ui/TimelineMessageRow.tsx @@ -143,6 +143,7 @@ export function MessageRowItem({ >