-
+
Raw ACP
JSON-RPC payloads
@@ -40,7 +40,7 @@ export function RawEventRail({ events }: { events: ObserverEvent[] }) {
#{event.seq} {" "}
{describeRawEvent(event)}
-
+
{JSON.stringify(event.payload, null, 2)}
diff --git a/desktop/src/features/agents/ui/RelayDirectorySection.tsx b/desktop/src/features/agents/ui/RelayDirectorySection.tsx
index 18e6ea358dd..4e91a455f63 100644
--- a/desktop/src/features/agents/ui/RelayDirectorySection.tsx
+++ b/desktop/src/features/agents/ui/RelayDirectorySection.tsx
@@ -95,7 +95,7 @@ export function RelayDirectorySection({
className="w-full border-collapse text-left text-sm"
data-testid="relay-directory-table"
>
-
+
Agent
Status
@@ -121,7 +121,7 @@ export function RelayDirectorySection({
diff --git a/desktop/src/features/agents/ui/RespondToField.tsx b/desktop/src/features/agents/ui/RespondToField.tsx
index 2e903bfebb1..bc73ca4e6ed 100644
--- a/desktop/src/features/agents/ui/RespondToField.tsx
+++ b/desktop/src/features/agents/ui/RespondToField.tsx
@@ -211,7 +211,7 @@ function AllowlistPicker({
>
Allowed pubkeys
-
+
{allowlist.length} selected
@@ -241,7 +241,7 @@ function AllowlistPicker({
{allowlist.map((pubkey) => (
diff --git a/desktop/src/features/agents/ui/TeamDialog.tsx b/desktop/src/features/agents/ui/TeamDialog.tsx
index 06421bc5170..8d765100d19 100644
--- a/desktop/src/features/agents/ui/TeamDialog.tsx
+++ b/desktop/src/features/agents/ui/TeamDialog.tsx
@@ -422,7 +422,7 @@ export function TeamDialog({
/>
{persona.displayName}
diff --git a/desktop/src/features/agents/ui/TeamsSection.tsx b/desktop/src/features/agents/ui/TeamsSection.tsx
index bcbcd7e104f..500d5a09e03 100644
--- a/desktop/src/features/agents/ui/TeamsSection.tsx
+++ b/desktop/src/features/agents/ui/TeamsSection.tsx
@@ -172,7 +172,7 @@ export function TeamsSection({
) : null}
{team.version ? (
-
+
v{team.version}
) : null}
@@ -199,13 +199,13 @@ export function TeamsSection({
{visible.map((persona) => (
))}
{overflow > 0 ? (
-
+
+{overflow}
) : null}
diff --git a/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx b/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx
index 35850ef3e88..ed7309f1d2a 100644
--- a/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx
+++ b/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx
@@ -49,7 +49,7 @@ function SelectionChipButton({
{persona.displayName}
{isInChannel ? (
-
+
✓ Already in this channel
) : null}
-
+
{promptPreview(persona.systemPrompt)}
diff --git a/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx b/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx
index acb12eb0c56..1fe45f9ae70 100644
--- a/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx
+++ b/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx
@@ -124,7 +124,7 @@ export function AddChannelBotTeamsSection({
{inChannelCount > 0 ? (
{team.name}
{team.description ? (
-
+
{team.description}
) : null}
@@ -158,10 +158,10 @@ export function AddChannelBotTeamsSection({
>
-
+
{persona.displayName}
{personaInChannel ? (
diff --git a/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx b/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx
index c35be56572d..6a8c508cae1 100644
--- a/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx
+++ b/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx
@@ -80,7 +80,7 @@ export function AgentSessionThreadPanel({
{isLive && isWorking ? (
@@ -92,7 +92,7 @@ export function AgentSessionThreadPanel({
{
diff --git a/desktop/src/features/channels/ui/BotActivityBar.tsx b/desktop/src/features/channels/ui/BotActivityBar.tsx
index d4ee6ff1e5a..8acf8a00568 100644
--- a/desktop/src/features/channels/ui/BotActivityBar.tsx
+++ b/desktop/src/features/channels/ui/BotActivityBar.tsx
@@ -184,8 +184,8 @@ export function BotActivityComposerAction({
className={cn(
"border border-background",
isInline
- ? "!h-[18px] !w-[18px] shadow-xs ring-1 ring-primary/25 text-[7px]"
- : "!h-5 !w-5 text-[8px]",
+ ? "!h-[18px] !w-[18px] shadow-xs ring-1 ring-primary/25 text-[0.4375rem]"
+ : "!h-5 !w-5 text-[0.5rem]",
)}
displayName={agent.name}
key={agent.pubkey}
@@ -193,7 +193,7 @@ export function BotActivityComposerAction({
))}
{typingAgents.length > 2 ? (
-
+
+{typingAgents.length - 2}
) : null}
@@ -240,7 +240,7 @@ export function BotActivityComposerAction({
>
{agent.name}
diff --git a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx
index 7f093294d72..a5689b483c6 100644
--- a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx
+++ b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx
@@ -184,7 +184,7 @@ export function ChannelMemberInviteCard({
Add members
{inviteTargets.length > 0 ? (
-
+
{inviteTargets.length} selected
) : null}
@@ -210,7 +210,7 @@ export function ChannelMemberInviteCard({
{selectedInvitees.map((invitee) => (
diff --git a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
index a873069f71e..59b39fdc281 100644
--- a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
+++ b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
@@ -92,7 +92,7 @@ export function ChannelScreenHeader({
activeChannel?.channelType === "dm" ? (
People
-
+
{people.length}
@@ -284,7 +284,7 @@ export function MembersSidebar({
Bots
-
+
{bots.length}
{hasControllableManagedBots ? (
@@ -329,7 +329,7 @@ export function MembersSidebar({
Archived
{archived.length}
diff --git a/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx b/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx
index 69974cb30f1..4cb083c30b5 100644
--- a/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx
+++ b/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx
@@ -119,7 +119,7 @@ export function MembersSidebarMemberCard({
@@ -159,7 +159,7 @@ export function MembersSidebarMemberCard({
>
) : null}
-
+
{truncatePubkey(member.pubkey)}
diff --git a/desktop/src/features/channels/ui/QuickBotBar.tsx b/desktop/src/features/channels/ui/QuickBotBar.tsx
index b4eec62c0fc..572811a26c4 100644
--- a/desktop/src/features/channels/ui/QuickBotBar.tsx
+++ b/desktop/src/features/channels/ui/QuickBotBar.tsx
@@ -75,7 +75,7 @@ export function QuickBotBar({ personas, pending, onAdd }: QuickBotBarProps) {
src={rewriteRelayUrl(persona.avatarUrl)}
/>
) : (
-
+
{initials}
)}
diff --git a/desktop/src/features/home/ui/FeedSection.tsx b/desktop/src/features/home/ui/FeedSection.tsx
index 51d871c32b7..a42d3d674a1 100644
--- a/desktop/src/features/home/ui/FeedSection.tsx
+++ b/desktop/src/features/home/ui/FeedSection.tsx
@@ -187,11 +187,11 @@ export function FeedSection({
{feedHeadline(item)}
-
+
{item.channelName ? (
-
+
#{item.channelName}
) : null}
-
+
{formatRelativeTime(item.createdAt)}
{isThreadContextLoading ? (
-
+
Loading context...
) : null}
diff --git a/desktop/src/features/home/ui/InboxListPane.tsx b/desktop/src/features/home/ui/InboxListPane.tsx
index f3f2af6621b..b45575c604b 100644
--- a/desktop/src/features/home/ui/InboxListPane.tsx
+++ b/desktop/src/features/home/ui/InboxListPane.tsx
@@ -67,7 +67,7 @@ export function InboxListPane({
{item.isActionRequired ? (
-
+
Needs action
) : null}
@@ -189,7 +189,7 @@ export function InboxListPane({
diff --git a/desktop/src/features/home/ui/RecentNotesSection.tsx b/desktop/src/features/home/ui/RecentNotesSection.tsx
index d786af37905..d142f503a2a 100644
--- a/desktop/src/features/home/ui/RecentNotesSection.tsx
+++ b/desktop/src/features/home/ui/RecentNotesSection.tsx
@@ -76,11 +76,11 @@ export function RecentNotesSection({
{displayName}
{isAgent ? (
-
+
bot
) : null}
-
+
{formatRelativeTime(note.createdAt)}
diff --git a/desktop/src/features/huddle/components/HuddleBar.tsx b/desktop/src/features/huddle/components/HuddleBar.tsx
index fb32276b725..d19a73fdd62 100644
--- a/desktop/src/features/huddle/components/HuddleBar.tsx
+++ b/desktop/src/features/huddle/components/HuddleBar.tsx
@@ -363,7 +363,7 @@ export function HuddleBar({ className }: HuddleBarProps) {
{isPttMode && (
-
+
{navigator.platform?.includes("Mac") ? "⌃Space" : "Ctrl+Space"}
)}
diff --git a/desktop/src/features/huddle/components/HuddleIndicator.tsx b/desktop/src/features/huddle/components/HuddleIndicator.tsx
index 1a8fb6ba6fe..53a8f184cda 100644
--- a/desktop/src/features/huddle/components/HuddleIndicator.tsx
+++ b/desktop/src/features/huddle/components/HuddleIndicator.tsx
@@ -321,7 +321,7 @@ export function HuddleIndicator({
{/* Participant count badge */}
{participantCount > 0 && (
-
+
{participantCount}
)}
diff --git a/desktop/src/features/huddle/components/MicControls.tsx b/desktop/src/features/huddle/components/MicControls.tsx
index 5730144f598..94f24912479 100644
--- a/desktop/src/features/huddle/components/MicControls.tsx
+++ b/desktop/src/features/huddle/components/MicControls.tsx
@@ -216,7 +216,7 @@ export function DeviceList({
})}
{showChangeHint && (
-
+
Change takes effect on next huddle
)}
diff --git a/desktop/src/features/huddle/components/ParticipantList.tsx b/desktop/src/features/huddle/components/ParticipantList.tsx
index 38514e2f3cb..a6239977fff 100644
--- a/desktop/src/features/huddle/components/ParticipantList.tsx
+++ b/desktop/src/features/huddle/components/ParticipantList.tsx
@@ -52,7 +52,7 @@ export function ParticipantList({
avatarUrl={profile.avatarUrl ?? null}
label={profile.displayName || pubkey.slice(0, 6)}
className={cn(
- "h-7 w-7 rounded-lg text-[9px]",
+ "h-7 w-7 rounded-lg text-[0.5625rem]",
isActive &&
"ring-2 ring-green-500 ring-offset-1 ring-offset-background",
)}
@@ -83,7 +83,7 @@ export function ParticipantList({
🤖
@@ -121,7 +121,7 @@ function HexAvatar({
aria-label={ariaLabel ?? `Participant ${pubkey.slice(0, 8)}`}
role="img"
className={cn(
- "flex h-7 w-7 items-center justify-center rounded-lg text-[9px] font-semibold shadow-xs",
+ "flex h-7 w-7 items-center justify-center rounded-lg text-[0.5625rem] font-semibold shadow-xs",
isActive &&
"ring-2 ring-green-500 ring-offset-1 ring-offset-background",
)}
diff --git a/desktop/src/features/messages/ui/ComposerAttachments.tsx b/desktop/src/features/messages/ui/ComposerAttachments.tsx
index 2444895a22b..0beb3f9e0cd 100644
--- a/desktop/src/features/messages/ui/ComposerAttachments.tsx
+++ b/desktop/src/features/messages/ui/ComposerAttachments.tsx
@@ -136,7 +136,7 @@ export const ComposerAttachments = React.memo(function ComposerAttachments({
>
-
+
{label}
diff --git a/desktop/src/features/messages/ui/DayDivider.tsx b/desktop/src/features/messages/ui/DayDivider.tsx
index e04fd0edbe7..8ba5c11e863 100644
--- a/desktop/src/features/messages/ui/DayDivider.tsx
+++ b/desktop/src/features/messages/ui/DayDivider.tsx
@@ -6,7 +6,7 @@ export function DayDivider({ label }: { label: string }) {
data-testid="message-timeline-day-divider"
data-day-label={label}
>
-
+
{label}
diff --git a/desktop/src/features/messages/ui/DiffViewer.tsx b/desktop/src/features/messages/ui/DiffViewer.tsx
index 4c9e501e456..147eb7aa348 100644
--- a/desktop/src/features/messages/ui/DiffViewer.tsx
+++ b/desktop/src/features/messages/ui/DiffViewer.tsx
@@ -36,7 +36,7 @@ function FileChangeBadge({
return (
{showFileHeader ? (
-
+
{label}
-
+
{DIFF_TYPE_LABELS[diffType]}
diff --git a/desktop/src/features/messages/ui/MessageComposer.tsx b/desktop/src/features/messages/ui/MessageComposer.tsx
index 36f5985a0e4..89f41b29eb0 100644
--- a/desktop/src/features/messages/ui/MessageComposer.tsx
+++ b/desktop/src/features/messages/ui/MessageComposer.tsx
@@ -773,7 +773,7 @@ export function MessageComposer({
data-testid="edit-target"
>
-
+
Editing message
@@ -796,7 +796,7 @@ export function MessageComposer({
data-testid="reply-target"
>
-
+
Replying to {replyTarget.author}
diff --git a/desktop/src/features/messages/ui/MessageThreadPanel.tsx b/desktop/src/features/messages/ui/MessageThreadPanel.tsx
index 5e8bad43d82..2b81dfb9b5f 100644
--- a/desktop/src/features/messages/ui/MessageThreadPanel.tsx
+++ b/desktop/src/features/messages/ui/MessageThreadPanel.tsx
@@ -286,7 +286,7 @@ export function MessageThreadPanel({
{!isAtBottom ? (
scrollToBottom("smooth")}
size="sm"
diff --git a/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx b/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx
index 6103bb403ca..411d243e829 100644
--- a/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx
+++ b/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx
@@ -32,7 +32,7 @@ function ParticipantAvatar({
>
diff --git a/desktop/src/features/messages/ui/MessageTimeline.tsx b/desktop/src/features/messages/ui/MessageTimeline.tsx
index 1fa347a2088..25756b002a0 100644
--- a/desktop/src/features/messages/ui/MessageTimeline.tsx
+++ b/desktop/src/features/messages/ui/MessageTimeline.tsx
@@ -400,7 +400,7 @@ export const MessageTimeline = React.memo(function MessageTimeline({
)}
>
{
scrollToBottom("smooth");
diff --git a/desktop/src/features/messages/ui/SystemMessageRow.tsx b/desktop/src/features/messages/ui/SystemMessageRow.tsx
index 7833fd0af4c..9b78c78b490 100644
--- a/desktop/src/features/messages/ui/SystemMessageRow.tsx
+++ b/desktop/src/features/messages/ui/SystemMessageRow.tsx
@@ -129,7 +129,7 @@ function SystemMessageAvatar({
const avatar = (
@@ -165,12 +165,12 @@ function SystemMessageAvatar({
>
diff --git a/desktop/src/features/messages/ui/TypingIndicatorRow.tsx b/desktop/src/features/messages/ui/TypingIndicatorRow.tsx
index bd68bb042a2..b0bfb01981d 100644
--- a/desktop/src/features/messages/ui/TypingIndicatorRow.tsx
+++ b/desktop/src/features/messages/ui/TypingIndicatorRow.tsx
@@ -113,8 +113,8 @@ export function TypingIndicatorRow({
label={label}
className={cn(
isActivityVariant
- ? "h-[18px] w-[18px] text-[7px]"
- : "h-5 w-5 text-[8px]",
+ ? "h-[18px] w-[18px] text-[0.4375rem]"
+ : "h-5 w-5 text-[0.5rem]",
)}
iconClassName={
isActivityVariant ? "h-2.5 w-2.5" : "h-3 w-3"
diff --git a/desktop/src/features/onboarding/ui/AvatarStep.tsx b/desktop/src/features/onboarding/ui/AvatarStep.tsx
index a99c69013e2..591b338a696 100644
--- a/desktop/src/features/onboarding/ui/AvatarStep.tsx
+++ b/desktop/src/features/onboarding/ui/AvatarStep.tsx
@@ -86,7 +86,7 @@ function AvatarPreview({
>
0 && "buzz-avatar-squish",
)}
data-testid="onboarding-avatar-preview-emoji"
diff --git a/desktop/src/features/onboarding/ui/MembershipDenied.tsx b/desktop/src/features/onboarding/ui/MembershipDenied.tsx
index 4f0618f94e7..1a20572f3d7 100644
--- a/desktop/src/features/onboarding/ui/MembershipDenied.tsx
+++ b/desktop/src/features/onboarding/ui/MembershipDenied.tsx
@@ -177,7 +177,7 @@ export function MembershipDenied({
This will use this Nostr identity:
-
+
{shortenNpub(previewNpub)}
diff --git a/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx b/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx
index b251fb9e3b8..cbff60e55b2 100644
--- a/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx
+++ b/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx
@@ -227,7 +227,7 @@ export function NostrKeyImportForm({
This will use this Nostr identity:
-
+
{shortenNpub(previewNpub)}
diff --git a/desktop/src/features/profile/ui/UserProfilePopover.tsx b/desktop/src/features/profile/ui/UserProfilePopover.tsx
index d750fa10538..9350b98b94d 100644
--- a/desktop/src/features/profile/ui/UserProfilePopover.tsx
+++ b/desktop/src/features/profile/ui/UserProfilePopover.tsx
@@ -216,7 +216,7 @@ export function UserProfilePopover({
) : null}
{profile?.displayName ? (
-
+
{truncatePubkey(pubkey)}
) : null}
diff --git a/desktop/src/features/pulse/ui/AgentActivityCard.tsx b/desktop/src/features/pulse/ui/AgentActivityCard.tsx
index 7fdc1097758..fc11f6554e6 100644
--- a/desktop/src/features/pulse/ui/AgentActivityCard.tsx
+++ b/desktop/src/features/pulse/ui/AgentActivityCard.tsx
@@ -75,7 +75,7 @@ export function AgentActivityCard({
{displayName}
{agentStatus ?
: null}
-
+
{formatRelativeTime(group.latestAt)}
@@ -113,7 +113,7 @@ export function AgentActivityCard({
-
+
{formatRelativeTime(note.createdAt)}
diff --git a/desktop/src/features/pulse/ui/NoteCard.tsx b/desktop/src/features/pulse/ui/NoteCard.tsx
index a3e71f89fe4..e329b1e4814 100644
--- a/desktop/src/features/pulse/ui/NoteCard.tsx
+++ b/desktop/src/features/pulse/ui/NoteCard.tsx
@@ -191,7 +191,7 @@ export function NoteCard({
{isAgent ? (
-
+
bot
) : null}
diff --git a/desktop/src/features/pulse/ui/PulseTabBar.tsx b/desktop/src/features/pulse/ui/PulseTabBar.tsx
index 937682c7edb..cc3793b9363 100644
--- a/desktop/src/features/pulse/ui/PulseTabBar.tsx
+++ b/desktop/src/features/pulse/ui/PulseTabBar.tsx
@@ -103,7 +103,7 @@ export function PulseTabBar({
>
Agents
{relayAgents.length > 0 ? (
-
+
{relayAgents.length}
) : null}
diff --git a/desktop/src/features/search/ui/TopbarSearch.tsx b/desktop/src/features/search/ui/TopbarSearch.tsx
index 3ee05c49c55..60bd1abc292 100644
--- a/desktop/src/features/search/ui/TopbarSearch.tsx
+++ b/desktop/src/features/search/ui/TopbarSearch.tsx
@@ -201,7 +201,7 @@ export function TopbarSearch({
placeholder="Search everything"
value={query}
/>
-
+
⌘K
@@ -212,7 +212,7 @@ export function TopbarSearch({
data-testid="search-results"
>
{debouncedQuery.length < MIN_SEARCH_QUERY_LENGTH ? (
-
+
Type at least two characters for live suggestions.
) : searchQuery.isLoading && results.length === 0 ? (
@@ -291,7 +291,7 @@ export function TopbarSearch({
: truncateResultText(result.hit.content)}
-
+
{result.kind === "channel"
? "Channel"
: `${describeSearchHit(result.hit)} · ${formatRelativeTime(result.hit.createdAt)}`}
diff --git a/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx b/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx
index 0181e19288a..ff01e693c5c 100644
--- a/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx
+++ b/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx
@@ -216,7 +216,10 @@ function TemplateRow({
{template.name}
{template.isBuiltin ? (
-
+
built-in
) : null}
@@ -756,7 +759,7 @@ function RuntimeRow({
) : (
)}
diff --git a/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx b/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx
index a6d3044cdc9..c5c27149b98 100644
--- a/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx
+++ b/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx
@@ -109,7 +109,7 @@ function RuntimeRow({
{runtime.label}
{runtime.command ? (
-
+
{runtime.command}
) : null}
@@ -134,21 +134,21 @@ function RuntimeRow({
{runtime.underlyingCliPath &&
runtime.underlyingCliPath !== runtime.binaryPath ? (
-
+
CLI: {" "}
{runtime.underlyingCliPath}
-
+
ACP adapter: {" "}
{runtime.binaryPath}
) : (
<>
-
+
{runtime.binaryPath}
-
+
ACP support built-in — no separate adapter needed.
>
@@ -158,7 +158,7 @@ function RuntimeRow({
<>
CLI detected at{" "}
-
+
{runtime.underlyingCliPath ?? "unknown path"}
{" "}
but ACP adapter not found.
@@ -176,7 +176,7 @@ function RuntimeRow({
<>
ACP adapter found at{" "}
-
+
{runtime.binaryPath ?? "unknown path"}
{" "}
but the {runtime.label} CLI is not installed.
diff --git a/desktop/src/features/settings/ui/NotificationSettingsCard.tsx b/desktop/src/features/settings/ui/NotificationSettingsCard.tsx
index 55a0c9ab770..be85a6bb67b 100644
--- a/desktop/src/features/settings/ui/NotificationSettingsCard.tsx
+++ b/desktop/src/features/settings/ui/NotificationSettingsCard.tsx
@@ -179,7 +179,7 @@ export function NotificationSettingsCard({
{SLOT_LABELS[slot]}
{comingSoon ? (
-
+
Coming soon
) : null}
diff --git a/desktop/src/features/settings/ui/ProfileSettingsCard.tsx b/desktop/src/features/settings/ui/ProfileSettingsCard.tsx
index cfe3c9bb20d..7c49aa0b1e9 100644
--- a/desktop/src/features/settings/ui/ProfileSettingsCard.tsx
+++ b/desktop/src/features/settings/ui/ProfileSettingsCard.tsx
@@ -429,7 +429,7 @@ export function ProfileSettingsCard({
>
0 && "buzz-avatar-squish",
)}
data-testid="profile-avatar-preview-emoji"
diff --git a/desktop/src/features/settings/ui/SoundPicker.tsx b/desktop/src/features/settings/ui/SoundPicker.tsx
index 5de4f450c6d..440fd39fcc3 100644
--- a/desktop/src/features/settings/ui/SoundPicker.tsx
+++ b/desktop/src/features/settings/ui/SoundPicker.tsx
@@ -113,7 +113,7 @@ export function SoundPicker({
{name}
{name === recommended ? (
-
+
rec.
) : null}
diff --git a/desktop/src/features/sidebar/ui/AppSidebar.tsx b/desktop/src/features/sidebar/ui/AppSidebar.tsx
index 00e8af524d8..0106121a713 100644
--- a/desktop/src/features/sidebar/ui/AppSidebar.tsx
+++ b/desktop/src/features/sidebar/ui/AppSidebar.tsx
@@ -444,7 +444,7 @@ export function AppSidebar({
{homeBadgeCount > 0 ? (
{Math.min(homeBadgeCount, 99)}
@@ -492,7 +492,7 @@ export function AppSidebar({
{shouldShowAgentCount ? (
{totalAgentCount}
diff --git a/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx b/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx
index 5ebca7e47ed..677ffe9b0a0 100644
--- a/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx
+++ b/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx
@@ -3,7 +3,7 @@ import type * as React from "react";
import { Button } from "@/shared/ui/button";
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";
+ "h-7 min-h-7 gap-1.5 rounded-full border-0 bg-primary px-2.5 text-[0.6875rem] font-medium text-primary-foreground shadow-md hover:bg-primary/90 [&_svg]:size-3.5";
export function MoreUnreadButton({
bottomClassName = "bottom-0",
diff --git a/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx b/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx
index 70b34d6a3fb..557ffcffd3f 100644
--- a/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx
+++ b/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx
@@ -180,7 +180,7 @@ export function NewDirectMessageDialog({
>
diff --git a/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx b/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx
index 97547a576e7..654403f20de 100644
--- a/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx
+++ b/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx
@@ -78,7 +78,10 @@ export function SidebarProfileCard({
const workspaceLabel = activeWorkspace?.name ?? "No workspace";
const readonlyWorkspaceLabel = (
-
+
🐝
{workspaceLabel}
diff --git a/desktop/src/features/sidebar/ui/SidebarSection.tsx b/desktop/src/features/sidebar/ui/SidebarSection.tsx
index 6d9210622dc..3db12f93e62 100644
--- a/desktop/src/features/sidebar/ui/SidebarSection.tsx
+++ b/desktop/src/features/sidebar/ui/SidebarSection.tsx
@@ -70,7 +70,7 @@ function DmChannelIcon({
@@ -91,18 +91,18 @@ function DmChannelIcon({
{participants && participants.length > 2 ? (
-
+
{participants.length}
) : null}
diff --git a/desktop/src/features/user-status/ui/SetStatusDialog.tsx b/desktop/src/features/user-status/ui/SetStatusDialog.tsx
index 337aedf232a..5f09276d2e0 100644
--- a/desktop/src/features/user-status/ui/SetStatusDialog.tsx
+++ b/desktop/src/features/user-status/ui/SetStatusDialog.tsx
@@ -120,7 +120,7 @@ export function SetStatusDialog({
{emoji ? (
{
event.stopPropagation();
setEmoji("");
diff --git a/desktop/src/features/workflows/ui/WorkflowCard.tsx b/desktop/src/features/workflows/ui/WorkflowCard.tsx
index 0a5526a4da4..be539995425 100644
--- a/desktop/src/features/workflows/ui/WorkflowCard.tsx
+++ b/desktop/src/features/workflows/ui/WorkflowCard.tsx
@@ -85,7 +85,7 @@ export function WorkflowCard({
-
+
{channelName ? {channelName} : null}
{triggerSummary ? {triggerSummary} : null}
diff --git a/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx b/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx
index faf22857f86..19ff1319997 100644
--- a/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx
+++ b/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx
@@ -186,7 +186,7 @@ export function WorkflowDetailPanel({
-
+
{new Date(
run.createdAt * 1000,
@@ -216,10 +216,10 @@ export function WorkflowDetailPanel({
{isSelected ? (
-
+
Execution Trace
{approvalsQuery.isFetching ? (
-
+
Refreshing approvals...
) : null}
diff --git a/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx b/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx
index a1e6b2775e2..c83743a6a83 100644
--- a/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx
+++ b/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx
@@ -94,7 +94,7 @@ export function WorkflowRunTrace({
{Object.keys(step.output).length > 0 ? (
-
+
Output
@@ -104,7 +104,7 @@ export function WorkflowRunTrace({
) : null}
{step.error ? (
-
+
Error
@@ -114,7 +114,7 @@ export function WorkflowRunTrace({
) : null}
{pendingApproval ? (
-
+
Pending approval
diff --git a/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx b/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx
index 2e7e14d045a..39c61f91c2c 100644
--- a/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx
+++ b/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx
@@ -132,7 +132,7 @@ export function WorkspaceSwitcher({
diff --git a/desktop/src/shared/ui/UserAvatar.tsx b/desktop/src/shared/ui/UserAvatar.tsx
index cab6a8fd66c..08463675579 100644
--- a/desktop/src/shared/ui/UserAvatar.tsx
+++ b/desktop/src/shared/ui/UserAvatar.tsx
@@ -6,8 +6,8 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/shared/ui/avatar";
type UserAvatarSize = "xs" | "sm" | "md";
const sizeClasses: Record = {
- xs: "h-5 w-5 text-[8px]",
- sm: "h-6 w-6 text-[9px]",
+ xs: "h-5 w-5 text-[0.5rem]",
+ sm: "h-6 w-6 text-[0.5625rem]",
md: "h-10 w-10 text-xs",
};
diff --git a/desktop/src/shared/ui/VideoPlayer.tsx b/desktop/src/shared/ui/VideoPlayer.tsx
index b967b00e954..db8a78b7533 100644
--- a/desktop/src/shared/ui/VideoPlayer.tsx
+++ b/desktop/src/shared/ui/VideoPlayer.tsx
@@ -531,7 +531,7 @@ function VideoScrubber({
style={{ left: `${hoverRatio * 100}%` }}
>
-
+
{formatTimecode(hoverRatio * duration)} / {formatTimecode(duration)}
@@ -918,7 +918,7 @@ export function VideoPlayer({
)}
{formatTimecode(currentTime)}
@@ -932,7 +932,7 @@ export function VideoPlayer({
testIdPrefix="video-inline"
/>
{formatTimecode(duration)}
@@ -1716,12 +1716,12 @@ function VideoReviewDialog({
{formatTimecode(currentTime)}
{replyTarget ? (
-
+
Replying to {replyTarget.comment.author}
) : (
onSeek(item.seconds ?? 0)}
@@ -1892,7 +1892,7 @@ function VideoReviewCommentBody({
displayName={item.comment.author}
size="xs"
/>
-
+
{item.comment.author}
diff --git a/desktop/src/shared/ui/badge.tsx b/desktop/src/shared/ui/badge.tsx
index fdb5d91b9c3..9112e67149e 100644
--- a/desktop/src/shared/ui/badge.tsx
+++ b/desktop/src/shared/ui/badge.tsx
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/shared/lib/cn";
const badgeVariants = cva(
- "inline-flex items-center rounded-full px-2 pb-[3px] pt-[5px] text-[10px] font-semibold uppercase leading-none tracking-[0.18em]",
+ "inline-flex items-center rounded-full px-2 pb-[3px] pt-[5px] text-[0.625rem] font-semibold uppercase leading-none tracking-[0.18em]",
{
variants: {
variant: {
From 8ca67e0af4ae39699eff4138eaa399072c652d28 Mon Sep 17 00:00:00 2001
From: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w
<52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@sprout-oss.stage.blox.sqprod.co>
Date: Sun, 14 Jun 2026 23:47:26 -0700
Subject: [PATCH 04/20] fix(desktop): convert DiffViewer.css font-size px to
rem for zoom
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
DiffViewer renders in the message/timeline path via DiffMessage.tsx, so
its 3 px font-size rules (.diff, .buzz-diff-gutter,
.diff-decoration-content) stayed frozen against Cmd +/- zoom — the same
bug class the rem sweep targets. These sat outside the text-[Npx]
Tailwind-literal scope (plain CSS) and aren't caught by #1052's px-guard,
which scopes CSS to globals.css only.
Convert 1:1 with zero pixel shift: 12px -> 0.75rem, 11px -> 0.6875rem.
Keeps the app-wide sweep honest.
Co-authored-by: Taylor Ho
Signed-off-by: Taylor Ho
---
desktop/src/features/messages/ui/DiffViewer.css | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/desktop/src/features/messages/ui/DiffViewer.css b/desktop/src/features/messages/ui/DiffViewer.css
index d6747173dd4..dcc97fb7885 100644
--- a/desktop/src/features/messages/ui/DiffViewer.css
+++ b/desktop/src/features/messages/ui/DiffViewer.css
@@ -52,7 +52,7 @@
}
.buzz-diff-theme .diff {
- font-size: 12px;
+ font-size: 0.75rem;
}
.buzz-diff-theme .diff td {
@@ -68,7 +68,7 @@
padding: 0.125rem 0.5rem;
border-right: 1px solid hsl(var(--border) / 0.65);
color: hsl(var(--muted-foreground));
- font-size: 11px;
+ font-size: 0.6875rem;
}
.buzz-diff-theme .buzz-diff-code {
@@ -86,7 +86,7 @@
padding: 0.2rem 0.75rem;
background: hsl(var(--muted) / 0.35);
color: hsl(var(--muted-foreground));
- font-size: 11px;
+ font-size: 0.6875rem;
}
.buzz-diff-theme .diff-gutter-omit::before {
From e42e70e20ece1de67198cb8cb7289f82221cb68e Mon Sep 17 00:00:00 2001
From: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w
<52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@sprout-oss.stage.blox.sqprod.co>
Date: Mon, 15 Jun 2026 00:13:04 -0700
Subject: [PATCH 05/20] fix(composer): scale mention highlight with zoom, align
to timeline chip
The composer's Tiptap .mention-highlight pill used a hardcoded
font-size: 15px, freezing it against Cmd +/- zoom (which scales the
root rem font-size). Convert to 1rem so it scales, and align to the
timeline mention chip's text-base (1rem) sizing per #1052's chat-base
type scale. Emoji-mart picker --font-size left as-is per scope.
Co-authored-by: Taylor Ho
Signed-off-by: Taylor Ho
---
desktop/src/shared/styles/globals.css | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index 69961f3fb8c..4b7b701d519 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -615,7 +615,8 @@
border-radius: 4px;
background: hsl(var(--primary) / 0.15);
padding: 3px 4px 2px;
- /* 1rem === 16px (chat base); rem so it scales with the root-font-size zoom. */
+ /* 1rem === 16px (chat base); rem so it scales with the root-font-size zoom.
+ Matches the timeline mention chip (text-base) so composer + timeline align. */
font-size: 1rem;
line-height: 1;
color: hsl(var(--primary));
From e5b10ce5183facdd16cdf47b268b59530fe7a6b5 Mon Sep 17 00:00:00 2001
From: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w
<52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@sprout-oss.stage.blox.sqprod.co>
Date: Mon, 15 Jun 2026 01:33:31 -0700
Subject: [PATCH 06/20] refactor(desktop): consolidate sub-xs text onto rem
tokens, enforce app-wide
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Arbitrary `text-[…rem]` literals had drifted across the desktop app
pixel-by-pixel (11/10/9/8/7px), and the px-text guard only covered the
message-timeline render path. This mints two named meta-text tokens and
sweeps every arbitrary text literal onto the rem-based scale so Cmd +/-
zoom keeps scaling and the sizes stay on one consolidated ramp.
- Add `text-2xs` (0.6875rem/11px) and `text-3xs` (0.5rem/8px) to
`tailwind.config.js` under `theme.extend.fontSize`.
- Swap arbitrary literals to named tokens (round-up merges):
0.6875/0.625/0.5625rem -> `text-2xs`, 0.5/0.4375rem -> `text-3xs`.
- Odd ducks: 0.8125rem (13px) -> stock `text-sm`, 10.5px -> `text-2xs`.
- Leave the `text-[6rem]` avatar emoji glyph (decorative, allowlisted).
- Widen the px-text guard: scan all of `desktop/src` and reject any
arbitrary text-size literal (px **or** rem/em), not just px in the
render path. Allowlist the avatar glyph by `path:line`.
- Document the tokens + guard in AGENTS.md.
Co-authored-by: Taylor Ho
Signed-off-by: Taylor Ho
---
AGENTS.md | 24 +++++++----
desktop/scripts/check-px-text.mjs | 40 ++++++++-----------
.../agent-memory/ui/MemorySection.tsx | 4 +-
.../agents/ui/AddTeamToChannelDialog.tsx | 2 +-
.../agents/ui/AgentSessionToolItem.tsx | 6 +--
.../agents/ui/AgentSessionTranscriptList.tsx | 6 +--
.../agents/ui/ManagedAgentLogPanel.tsx | 2 +-
.../features/agents/ui/ManagedAgentRow.tsx | 4 +-
.../src/features/agents/ui/ModelPicker.tsx | 2 +-
.../ui/PersonaCatalogSelectionBadge.tsx | 2 +-
.../src/features/agents/ui/RawEventRail.tsx | 4 +-
.../agents/ui/RelayDirectorySection.tsx | 4 +-
.../src/features/agents/ui/RespondToField.tsx | 4 +-
desktop/src/features/agents/ui/TeamDialog.tsx | 2 +-
.../src/features/agents/ui/TeamsSection.tsx | 6 +--
.../ui/AddChannelBotPersonasSection.tsx | 12 +++---
.../channels/ui/AddChannelBotTeamsSection.tsx | 8 ++--
.../channels/ui/AgentSessionThreadPanel.tsx | 7 +---
.../features/channels/ui/BotActivityBar.tsx | 8 ++--
.../channels/ui/ChannelMemberInviteCard.tsx | 4 +-
.../channels/ui/ChannelScreenHeader.tsx | 2 +-
.../features/channels/ui/MembersSidebar.tsx | 6 +--
.../channels/ui/MembersSidebarMemberCard.tsx | 4 +-
.../src/features/channels/ui/QuickBotBar.tsx | 2 +-
desktop/src/features/home/ui/FeedSection.tsx | 10 ++---
.../src/features/home/ui/InboxDetailPane.tsx | 2 +-
.../src/features/home/ui/InboxListPane.tsx | 6 +--
.../features/home/ui/RecentNotesSection.tsx | 4 +-
.../features/huddle/components/HuddleBar.tsx | 2 +-
.../huddle/components/HuddleIndicator.tsx | 2 +-
.../huddle/components/MicControls.tsx | 2 +-
.../huddle/components/ParticipantList.tsx | 6 +--
.../messages/ui/ComposerAttachments.tsx | 2 +-
.../src/features/messages/ui/DayDivider.tsx | 2 +-
.../src/features/messages/ui/DiffViewer.tsx | 6 +--
.../features/messages/ui/MessageComposer.tsx | 4 +-
.../messages/ui/MessageThreadPanel.tsx | 2 +-
.../messages/ui/MessageThreadSummaryRow.tsx | 2 +-
.../features/messages/ui/MessageTimeline.tsx | 2 +-
.../features/messages/ui/SystemMessageRow.tsx | 6 +--
.../messages/ui/TypingIndicatorRow.tsx | 4 +-
.../onboarding/ui/MembershipDenied.tsx | 2 +-
.../onboarding/ui/NostrKeyImportForm.tsx | 2 +-
.../profile/ui/UserProfilePopover.tsx | 2 +-
.../features/pulse/ui/AgentActivityCard.tsx | 4 +-
desktop/src/features/pulse/ui/NoteCard.tsx | 2 +-
desktop/src/features/pulse/ui/PulseTabBar.tsx | 4 +-
.../src/features/search/ui/TopbarSearch.tsx | 6 +--
.../ui/ChannelTemplatesSettingsCard.tsx | 7 +---
.../settings/ui/DoctorSettingsPanel.tsx | 14 +++----
.../settings/ui/NotificationSettingsCard.tsx | 2 +-
.../src/features/settings/ui/SoundPicker.tsx | 2 +-
.../src/features/sidebar/ui/AppSidebar.tsx | 4 +-
.../features/sidebar/ui/MoreUnreadButton.tsx | 2 +-
.../sidebar/ui/NewDirectMessageDialog.tsx | 2 +-
.../sidebar/ui/SidebarProfileCard.tsx | 5 +--
.../features/sidebar/ui/SidebarSection.tsx | 8 ++--
.../user-status/ui/SetStatusDialog.tsx | 2 +-
.../features/workflows/ui/WorkflowCard.tsx | 2 +-
.../workflows/ui/WorkflowDetailPanel.tsx | 6 +--
.../workflows/ui/WorkflowRunTrace.tsx | 6 +--
.../workspaces/ui/WorkspaceSwitcher.tsx | 2 +-
desktop/src/shared/ui/UserAvatar.tsx | 4 +-
desktop/src/shared/ui/VideoPlayer.tsx | 14 +++----
desktop/src/shared/ui/badge.tsx | 2 +-
desktop/tailwind.config.js | 9 +++++
scripts/check-px-text-core.mjs | 21 +++++++---
67 files changed, 188 insertions(+), 177 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 1cda517b624..7b56c359d5f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -426,15 +426,25 @@ px:
text === `text-base` (16px) — chat is the app's base type size**, and the
surrounding timeline elements (timestamps, system rows, code, reactions) are
deliberate steps on that same stock ramp.
-- ❌ `text-[15px]`, `text-[13px]`, or CSS `font-size: 15px`. These opted out of
- zoom and caused the message-timeline regression (PR #891).
+- ✅ The `text-2xs` (0.6875rem / 11px) and `text-3xs` (0.5rem / 8px) meta-text
+ tokens (in `desktop/tailwind.config.js` under `theme.extend.fontSize`) for the
+ sub-`text-xs` ramp — timestamps, count badges, tracking labels, tiny glyphs.
+ These replaced the dozens of arbitrary `text-[…rem]` literals that had drifted
+ apart pixel-by-pixel; keep meta text on these two tokens, not new arbitrary
+ values.
+- ❌ `text-[15px]`, `text-[13px]`, CSS `font-size: 15px` — px froze against zoom
+ and caused the message-timeline regression (PR #891).
+- ❌ Arbitrary rem literals too: `text-[0.6875rem]`, `text-[0.9rem]`, etc. They
+ zoom fine but re-fragment the scale we consolidated. Use a named token.
Prefer stock tokens — they're rem and zoom-safe. Only if a design genuinely
-needs a size the stock scale can't express should you **add a rem-based token**
-(in `desktop/tailwind.config.js` under `theme.extend.fontSize`) rather than an
-arbitrary px value. A CI guard (`pnpm check:px-text`, in
-`desktop/scripts/check-px-text.mjs`) fails on new px text in the
-message-timeline / thread render path.
+needs a size the stock/`2xs`/`3xs` scale can't express should you **add a
+rem-based token** (in `desktop/tailwind.config.js` under `theme.extend.fontSize`)
+rather than an arbitrary literal. A CI guard (`pnpm check:px-text`, in
+`desktop/scripts/check-px-text.mjs`) scans all of `desktop/src` and fails on any
+new arbitrary text-size literal — px **or** rem/em. Genuinely decorative glyphs
+(e.g. the `text-[6rem]` avatar emoji) are allowlisted by `path:line` in that
+script.
### Workspace Switching
diff --git a/desktop/scripts/check-px-text.mjs b/desktop/scripts/check-px-text.mjs
index 519ec7815d2..668514b9bb7 100644
--- a/desktop/scripts/check-px-text.mjs
+++ b/desktop/scripts/check-px-text.mjs
@@ -5,35 +5,27 @@ import { runPxTextCheck } from "../../scripts/check-px-text-core.mjs";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const projectRoot = path.resolve(__dirname, "..");
-// Scoped to the message-timeline / thread render path — the surface where the
-// rem→px zoom regression (PR #891) landed. Readable message text here MUST use
-// rem-based tokens (the stock `text-base` / `text-sm` scale, chat === base)
-// so Cmd +/- zoom scales it. We intentionally do NOT sweep the whole app yet
-// (decorative chrome — avatar initials, day dividers, diff-viewer labels —
-// still uses px); widen these roots when that sweep happens.
+// Enforces the rem-token text scale app-wide. The rem→px zoom regression
+// (PR #891) landed in the message-timeline render path, but arbitrary text
+// literals (`text-[…px]`, `text-[…rem]`) had drifted across the whole desktop
+// app — so the guard now scans all of `src`. Readable text MUST use a rem-based
+// token (the stock `text-base`/`text-sm`/`text-xs` scale, or the `text-2xs` /
+// `text-3xs` meta-text tokens) so Cmd +/- zoom scales it and the size stays on
+// one consolidated scale. Genuine decorative glyphs are allowlisted below.
const rules = [
{
- root: "src/shared/ui",
- extensions: new Set([".ts", ".tsx"]),
- files: new Set(["markdown.tsx", "mentionChip.ts"]),
- },
- {
- root: "src/features/messages/ui",
- extensions: new Set([".tsx"]),
- files: new Set(["MessageRow.tsx"]),
- },
- {
- // `.mention-highlight` lives here and was part of the #891 px regression —
- // guard the `font-size: NNpx` form too, not just the Tailwind utility.
- root: "src/shared/styles",
- extensions: new Set([".css"]),
- files: new Set(["globals.css"]),
+ root: "src",
+ extensions: new Set([".ts", ".tsx", ".css"]),
},
];
-// Decorative / chrome px-text exceptions: `relativePath:lineNumber`. Empty for
-// now — the regression footprint is fully on rem tokens.
-const overrides = new Set();
+// Decorative / chrome exceptions: `relativePath:lineNumber`. The avatar emoji
+// glyph is a fixed display size sized to its avatar box (not readable message
+// text), so it stays as the lone documented `text-[6rem]` literal.
+const overrides = new Set([
+ "src/features/settings/ui/ProfileSettingsCard.tsx:432",
+ "src/features/onboarding/ui/AvatarStep.tsx:89",
+]);
await runPxTextCheck({
projectRoot,
diff --git a/desktop/src/features/agent-memory/ui/MemorySection.tsx b/desktop/src/features/agent-memory/ui/MemorySection.tsx
index df8025159b8..8d729e80886 100644
--- a/desktop/src/features/agent-memory/ui/MemorySection.tsx
+++ b/desktop/src/features/agent-memory/ui/MemorySection.tsx
@@ -253,8 +253,8 @@ function MemoryGraphView({
className="text-xs italic text-muted-foreground"
data-testid="agent-memory-no-core"
>
- No core memory yet
- — agent identity is unrooted.
+ No core memory yet — agent
+ identity is unrooted.
) : null}
diff --git a/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx b/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx
index 693c0e5cf0b..c6f9f5eb7d2 100644
--- a/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx
+++ b/desktop/src/features/agents/ui/AddTeamToChannelDialog.tsx
@@ -178,7 +178,7 @@ export function AddTeamToChannelDialog({
>
diff --git a/desktop/src/features/agents/ui/AgentSessionToolItem.tsx b/desktop/src/features/agents/ui/AgentSessionToolItem.tsx
index 4c443ba62a3..23cead9f816 100644
--- a/desktop/src/features/agents/ui/AgentSessionToolItem.tsx
+++ b/desktop/src/features/agents/ui/AgentSessionToolItem.tsx
@@ -198,7 +198,7 @@ function ToolTimestamp({
return (
-
+
{time}
{duration ? ` · ${duration}` : null}
@@ -260,7 +260,7 @@ function BuzzToolInlineAction({
if (action.onClick) {
return (
{
event.preventDefault();
event.stopPropagation();
@@ -279,7 +279,7 @@ function BuzzToolInlineAction({
return (
{action.avatar}
diff --git a/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx b/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx
index ff90b998554..21c75d9a9ae 100644
--- a/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx
+++ b/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx
@@ -112,7 +112,7 @@ function MessageItem({
{!isAssistant ? (
@@ -198,7 +198,7 @@ function MetadataItem({
{section.title}
-
+
{section.body.trim() || "No metadata."}
@@ -248,7 +248,7 @@ function TranscriptTimestamp({ timestamp }: { timestamp: string }) {
return (
-
+
{formatted}
diff --git a/desktop/src/features/agents/ui/ManagedAgentLogPanel.tsx b/desktop/src/features/agents/ui/ManagedAgentLogPanel.tsx
index d73ece8822e..86fdc05cf23 100644
--- a/desktop/src/features/agents/ui/ManagedAgentLogPanel.tsx
+++ b/desktop/src/features/agents/ui/ManagedAgentLogPanel.tsx
@@ -65,7 +65,7 @@ export function ManagedAgentLogPanel({
) : (
-
+
{selectedAgent.name}
{selectedAgent.status}
diff --git a/desktop/src/features/agents/ui/ManagedAgentRow.tsx b/desktop/src/features/agents/ui/ManagedAgentRow.tsx
index ba06918e9e7..e08f20e296f 100644
--- a/desktop/src/features/agents/ui/ManagedAgentRow.tsx
+++ b/desktop/src/features/agents/ui/ManagedAgentRow.tsx
@@ -349,7 +349,7 @@ function StatusBlock({
}) {
return (
-
+
Status
-
+
Runtime
diff --git a/desktop/src/features/agents/ui/ModelPicker.tsx b/desktop/src/features/agents/ui/ModelPicker.tsx
index 193bc83d6aa..4ac9bb39a3a 100644
--- a/desktop/src/features/agents/ui/ModelPicker.tsx
+++ b/desktop/src/features/agents/ui/ModelPicker.tsx
@@ -152,7 +152,7 @@ export function ModelPicker({
{needsRestart ? (
- restart to apply
+ restart to apply
) : null}
);
diff --git a/desktop/src/features/agents/ui/PersonaCatalogSelectionBadge.tsx b/desktop/src/features/agents/ui/PersonaCatalogSelectionBadge.tsx
index 000b9075c5b..0329b6c807b 100644
--- a/desktop/src/features/agents/ui/PersonaCatalogSelectionBadge.tsx
+++ b/desktop/src/features/agents/ui/PersonaCatalogSelectionBadge.tsx
@@ -14,7 +14,7 @@ export function PersonaCatalogSelectionBadge({
return (
-
+
Raw ACP
JSON-RPC payloads
@@ -40,7 +40,7 @@ export function RawEventRail({ events }: { events: ObserverEvent[] }) {
#{event.seq} {" "}
{describeRawEvent(event)}
-
+
{JSON.stringify(event.payload, null, 2)}
diff --git a/desktop/src/features/agents/ui/RelayDirectorySection.tsx b/desktop/src/features/agents/ui/RelayDirectorySection.tsx
index 4e91a455f63..d1ac88c620f 100644
--- a/desktop/src/features/agents/ui/RelayDirectorySection.tsx
+++ b/desktop/src/features/agents/ui/RelayDirectorySection.tsx
@@ -95,7 +95,7 @@ export function RelayDirectorySection({
className="w-full border-collapse text-left text-sm"
data-testid="relay-directory-table"
>
-
+
Agent
Status
@@ -121,7 +121,7 @@ export function RelayDirectorySection({
diff --git a/desktop/src/features/agents/ui/RespondToField.tsx b/desktop/src/features/agents/ui/RespondToField.tsx
index bc73ca4e6ed..3f7b675aa48 100644
--- a/desktop/src/features/agents/ui/RespondToField.tsx
+++ b/desktop/src/features/agents/ui/RespondToField.tsx
@@ -211,7 +211,7 @@ function AllowlistPicker({
>
Allowed pubkeys
-
+
{allowlist.length} selected
@@ -241,7 +241,7 @@ function AllowlistPicker({
{allowlist.map((pubkey) => (
diff --git a/desktop/src/features/agents/ui/TeamDialog.tsx b/desktop/src/features/agents/ui/TeamDialog.tsx
index 8d765100d19..504f6acf305 100644
--- a/desktop/src/features/agents/ui/TeamDialog.tsx
+++ b/desktop/src/features/agents/ui/TeamDialog.tsx
@@ -422,7 +422,7 @@ export function TeamDialog({
/>
{persona.displayName}
diff --git a/desktop/src/features/agents/ui/TeamsSection.tsx b/desktop/src/features/agents/ui/TeamsSection.tsx
index 500d5a09e03..9be5a9bb8b9 100644
--- a/desktop/src/features/agents/ui/TeamsSection.tsx
+++ b/desktop/src/features/agents/ui/TeamsSection.tsx
@@ -172,7 +172,7 @@ export function TeamsSection({
) : null}
{team.version ? (
-
+
v{team.version}
) : null}
@@ -199,13 +199,13 @@ export function TeamsSection({
{visible.map((persona) => (
))}
{overflow > 0 ? (
-
+
+{overflow}
) : null}
diff --git a/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx b/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx
index ed7309f1d2a..16b0806a4cd 100644
--- a/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx
+++ b/desktop/src/features/channels/ui/AddChannelBotPersonasSection.tsx
@@ -49,7 +49,7 @@ function SelectionChipButton({
{persona.displayName}
{isInChannel ? (
-
+
✓ Already in this channel
) : null}
-
+
{promptPreview(persona.systemPrompt)}
diff --git a/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx b/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx
index 1fe45f9ae70..2879902c733 100644
--- a/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx
+++ b/desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx
@@ -124,7 +124,7 @@ export function AddChannelBotTeamsSection({
{inChannelCount > 0 ? (
{team.name}
{team.description ? (
-
+
{team.description}
) : null}
@@ -158,10 +158,10 @@ export function AddChannelBotTeamsSection({
>
-
+
{persona.displayName}
{personaInChannel ? (
diff --git a/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx b/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx
index 6a8c508cae1..174de971434 100644
--- a/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx
+++ b/desktop/src/features/channels/ui/AgentSessionThreadPanel.tsx
@@ -79,10 +79,7 @@ export function AgentSessionThreadPanel({
const agentHeaderActions = (
{isLive && isWorking ? (
-
+
Live
@@ -92,7 +89,7 @@ export function AgentSessionThreadPanel({
{
diff --git a/desktop/src/features/channels/ui/BotActivityBar.tsx b/desktop/src/features/channels/ui/BotActivityBar.tsx
index 8acf8a00568..ad273d0725e 100644
--- a/desktop/src/features/channels/ui/BotActivityBar.tsx
+++ b/desktop/src/features/channels/ui/BotActivityBar.tsx
@@ -184,8 +184,8 @@ export function BotActivityComposerAction({
className={cn(
"border border-background",
isInline
- ? "!h-[18px] !w-[18px] shadow-xs ring-1 ring-primary/25 text-[0.4375rem]"
- : "!h-5 !w-5 text-[0.5rem]",
+ ? "!h-[18px] !w-[18px] shadow-xs ring-1 ring-primary/25 text-3xs"
+ : "!h-5 !w-5 text-3xs",
)}
displayName={agent.name}
key={agent.pubkey}
@@ -193,7 +193,7 @@ export function BotActivityComposerAction({
))}
{typingAgents.length > 2 ? (
-
+
+{typingAgents.length - 2}
) : null}
@@ -240,7 +240,7 @@ export function BotActivityComposerAction({
>
{agent.name}
diff --git a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx
index a5689b483c6..6e96b91e49a 100644
--- a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx
+++ b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx
@@ -184,7 +184,7 @@ export function ChannelMemberInviteCard({
Add members
{inviteTargets.length > 0 ? (
-
+
{inviteTargets.length} selected
) : null}
@@ -210,7 +210,7 @@ export function ChannelMemberInviteCard({
{selectedInvitees.map((invitee) => (
diff --git a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
index 59b39fdc281..e2e752924a7 100644
--- a/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
+++ b/desktop/src/features/channels/ui/ChannelScreenHeader.tsx
@@ -92,7 +92,7 @@ export function ChannelScreenHeader({
activeChannel?.channelType === "dm" ? (
People
-
+
{people.length}
@@ -284,7 +284,7 @@ export function MembersSidebar({
Bots
-
+
{bots.length}
{hasControllableManagedBots ? (
@@ -329,7 +329,7 @@ export function MembersSidebar({
Archived
{archived.length}
diff --git a/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx b/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx
index 4cb083c30b5..cc09eea86f0 100644
--- a/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx
+++ b/desktop/src/features/channels/ui/MembersSidebarMemberCard.tsx
@@ -119,7 +119,7 @@ export function MembersSidebarMemberCard({
@@ -159,7 +159,7 @@ export function MembersSidebarMemberCard({
>
) : null}
-
+
{truncatePubkey(member.pubkey)}
diff --git a/desktop/src/features/channels/ui/QuickBotBar.tsx b/desktop/src/features/channels/ui/QuickBotBar.tsx
index 572811a26c4..06a53e3622a 100644
--- a/desktop/src/features/channels/ui/QuickBotBar.tsx
+++ b/desktop/src/features/channels/ui/QuickBotBar.tsx
@@ -75,7 +75,7 @@ export function QuickBotBar({ personas, pending, onAdd }: QuickBotBarProps) {
src={rewriteRelayUrl(persona.avatarUrl)}
/>
) : (
-
+
{initials}
)}
diff --git a/desktop/src/features/home/ui/FeedSection.tsx b/desktop/src/features/home/ui/FeedSection.tsx
index a42d3d674a1..4b71f6eddeb 100644
--- a/desktop/src/features/home/ui/FeedSection.tsx
+++ b/desktop/src/features/home/ui/FeedSection.tsx
@@ -187,11 +187,11 @@ export function FeedSection({
{feedHeadline(item)}
-
+
{item.channelName ? (
-
+
#{item.channelName}
) : null}
-
+
{formatRelativeTime(item.createdAt)}
{isThreadContextLoading ? (
-
+
Loading context...
) : null}
diff --git a/desktop/src/features/home/ui/InboxListPane.tsx b/desktop/src/features/home/ui/InboxListPane.tsx
index b45575c604b..2ff283d0b48 100644
--- a/desktop/src/features/home/ui/InboxListPane.tsx
+++ b/desktop/src/features/home/ui/InboxListPane.tsx
@@ -67,7 +67,7 @@ export function InboxListPane({
{item.isActionRequired ? (
-
+
Needs action
) : null}
@@ -189,7 +189,7 @@ export function InboxListPane({
diff --git a/desktop/src/features/home/ui/RecentNotesSection.tsx b/desktop/src/features/home/ui/RecentNotesSection.tsx
index d142f503a2a..45c9f49d9ec 100644
--- a/desktop/src/features/home/ui/RecentNotesSection.tsx
+++ b/desktop/src/features/home/ui/RecentNotesSection.tsx
@@ -76,11 +76,11 @@ export function RecentNotesSection({
{displayName}
{isAgent ? (
-
+
bot
) : null}
-
+
{formatRelativeTime(note.createdAt)}
diff --git a/desktop/src/features/huddle/components/HuddleBar.tsx b/desktop/src/features/huddle/components/HuddleBar.tsx
index d19a73fdd62..79e779800d1 100644
--- a/desktop/src/features/huddle/components/HuddleBar.tsx
+++ b/desktop/src/features/huddle/components/HuddleBar.tsx
@@ -363,7 +363,7 @@ export function HuddleBar({ className }: HuddleBarProps) {
{isPttMode && (
-
+
{navigator.platform?.includes("Mac") ? "⌃Space" : "Ctrl+Space"}
)}
diff --git a/desktop/src/features/huddle/components/HuddleIndicator.tsx b/desktop/src/features/huddle/components/HuddleIndicator.tsx
index 53a8f184cda..464f5ee27ae 100644
--- a/desktop/src/features/huddle/components/HuddleIndicator.tsx
+++ b/desktop/src/features/huddle/components/HuddleIndicator.tsx
@@ -321,7 +321,7 @@ export function HuddleIndicator({
{/* Participant count badge */}
{participantCount > 0 && (
-
+
{participantCount}
)}
diff --git a/desktop/src/features/huddle/components/MicControls.tsx b/desktop/src/features/huddle/components/MicControls.tsx
index 94f24912479..757315497a0 100644
--- a/desktop/src/features/huddle/components/MicControls.tsx
+++ b/desktop/src/features/huddle/components/MicControls.tsx
@@ -216,7 +216,7 @@ export function DeviceList({
})}
{showChangeHint && (
-
+
Change takes effect on next huddle
)}
diff --git a/desktop/src/features/huddle/components/ParticipantList.tsx b/desktop/src/features/huddle/components/ParticipantList.tsx
index a6239977fff..eb4b8c40c56 100644
--- a/desktop/src/features/huddle/components/ParticipantList.tsx
+++ b/desktop/src/features/huddle/components/ParticipantList.tsx
@@ -52,7 +52,7 @@ export function ParticipantList({
avatarUrl={profile.avatarUrl ?? null}
label={profile.displayName || pubkey.slice(0, 6)}
className={cn(
- "h-7 w-7 rounded-lg text-[0.5625rem]",
+ "h-7 w-7 rounded-lg text-2xs",
isActive &&
"ring-2 ring-green-500 ring-offset-1 ring-offset-background",
)}
@@ -83,7 +83,7 @@ export function ParticipantList({
🤖
@@ -121,7 +121,7 @@ function HexAvatar({
aria-label={ariaLabel ?? `Participant ${pubkey.slice(0, 8)}`}
role="img"
className={cn(
- "flex h-7 w-7 items-center justify-center rounded-lg text-[0.5625rem] font-semibold shadow-xs",
+ "flex h-7 w-7 items-center justify-center rounded-lg text-2xs font-semibold shadow-xs",
isActive &&
"ring-2 ring-green-500 ring-offset-1 ring-offset-background",
)}
diff --git a/desktop/src/features/messages/ui/ComposerAttachments.tsx b/desktop/src/features/messages/ui/ComposerAttachments.tsx
index 0beb3f9e0cd..994b4479777 100644
--- a/desktop/src/features/messages/ui/ComposerAttachments.tsx
+++ b/desktop/src/features/messages/ui/ComposerAttachments.tsx
@@ -136,7 +136,7 @@ export const ComposerAttachments = React.memo(function ComposerAttachments({
>
-
+
{label}
diff --git a/desktop/src/features/messages/ui/DayDivider.tsx b/desktop/src/features/messages/ui/DayDivider.tsx
index 8ba5c11e863..a76b8a7f1e0 100644
--- a/desktop/src/features/messages/ui/DayDivider.tsx
+++ b/desktop/src/features/messages/ui/DayDivider.tsx
@@ -6,7 +6,7 @@ export function DayDivider({ label }: { label: string }) {
data-testid="message-timeline-day-divider"
data-day-label={label}
>
-
+
{label}
diff --git a/desktop/src/features/messages/ui/DiffViewer.tsx b/desktop/src/features/messages/ui/DiffViewer.tsx
index 147eb7aa348..239fd02110e 100644
--- a/desktop/src/features/messages/ui/DiffViewer.tsx
+++ b/desktop/src/features/messages/ui/DiffViewer.tsx
@@ -36,7 +36,7 @@ function FileChangeBadge({
return (
{showFileHeader ? (
-
+
{label}
-
+
{DIFF_TYPE_LABELS[diffType]}
diff --git a/desktop/src/features/messages/ui/MessageComposer.tsx b/desktop/src/features/messages/ui/MessageComposer.tsx
index 89f41b29eb0..75e5e9b2e49 100644
--- a/desktop/src/features/messages/ui/MessageComposer.tsx
+++ b/desktop/src/features/messages/ui/MessageComposer.tsx
@@ -773,7 +773,7 @@ export function MessageComposer({
data-testid="edit-target"
>
-
+
Editing message
@@ -796,7 +796,7 @@ export function MessageComposer({
data-testid="reply-target"
>
-
+
Replying to {replyTarget.author}
diff --git a/desktop/src/features/messages/ui/MessageThreadPanel.tsx b/desktop/src/features/messages/ui/MessageThreadPanel.tsx
index 2b81dfb9b5f..b3baf31cafe 100644
--- a/desktop/src/features/messages/ui/MessageThreadPanel.tsx
+++ b/desktop/src/features/messages/ui/MessageThreadPanel.tsx
@@ -286,7 +286,7 @@ export function MessageThreadPanel({
{!isAtBottom ? (
scrollToBottom("smooth")}
size="sm"
diff --git a/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx b/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx
index 411d243e829..a99a8367047 100644
--- a/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx
+++ b/desktop/src/features/messages/ui/MessageThreadSummaryRow.tsx
@@ -32,7 +32,7 @@ function ParticipantAvatar({
>
diff --git a/desktop/src/features/messages/ui/MessageTimeline.tsx b/desktop/src/features/messages/ui/MessageTimeline.tsx
index 25756b002a0..5ed4950660f 100644
--- a/desktop/src/features/messages/ui/MessageTimeline.tsx
+++ b/desktop/src/features/messages/ui/MessageTimeline.tsx
@@ -400,7 +400,7 @@ export const MessageTimeline = React.memo(function MessageTimeline({
)}
>
{
scrollToBottom("smooth");
diff --git a/desktop/src/features/messages/ui/SystemMessageRow.tsx b/desktop/src/features/messages/ui/SystemMessageRow.tsx
index 9b78c78b490..69ac8519900 100644
--- a/desktop/src/features/messages/ui/SystemMessageRow.tsx
+++ b/desktop/src/features/messages/ui/SystemMessageRow.tsx
@@ -129,7 +129,7 @@ function SystemMessageAvatar({
const avatar = (
@@ -165,12 +165,12 @@ function SystemMessageAvatar({
>
diff --git a/desktop/src/features/messages/ui/TypingIndicatorRow.tsx b/desktop/src/features/messages/ui/TypingIndicatorRow.tsx
index b0bfb01981d..67e2a4f5514 100644
--- a/desktop/src/features/messages/ui/TypingIndicatorRow.tsx
+++ b/desktop/src/features/messages/ui/TypingIndicatorRow.tsx
@@ -113,8 +113,8 @@ export function TypingIndicatorRow({
label={label}
className={cn(
isActivityVariant
- ? "h-[18px] w-[18px] text-[0.4375rem]"
- : "h-5 w-5 text-[0.5rem]",
+ ? "h-[18px] w-[18px] text-3xs"
+ : "h-5 w-5 text-3xs",
)}
iconClassName={
isActivityVariant ? "h-2.5 w-2.5" : "h-3 w-3"
diff --git a/desktop/src/features/onboarding/ui/MembershipDenied.tsx b/desktop/src/features/onboarding/ui/MembershipDenied.tsx
index 1a20572f3d7..4f253e8c5d1 100644
--- a/desktop/src/features/onboarding/ui/MembershipDenied.tsx
+++ b/desktop/src/features/onboarding/ui/MembershipDenied.tsx
@@ -177,7 +177,7 @@ export function MembershipDenied({
This will use this Nostr identity:
-
+
{shortenNpub(previewNpub)}
diff --git a/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx b/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx
index cbff60e55b2..d2ae1e7c829 100644
--- a/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx
+++ b/desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx
@@ -227,7 +227,7 @@ export function NostrKeyImportForm({
This will use this Nostr identity:
-
+
{shortenNpub(previewNpub)}
diff --git a/desktop/src/features/profile/ui/UserProfilePopover.tsx b/desktop/src/features/profile/ui/UserProfilePopover.tsx
index 9350b98b94d..b5146a5e97b 100644
--- a/desktop/src/features/profile/ui/UserProfilePopover.tsx
+++ b/desktop/src/features/profile/ui/UserProfilePopover.tsx
@@ -216,7 +216,7 @@ export function UserProfilePopover({
) : null}
{profile?.displayName ? (
-
+
{truncatePubkey(pubkey)}
) : null}
diff --git a/desktop/src/features/pulse/ui/AgentActivityCard.tsx b/desktop/src/features/pulse/ui/AgentActivityCard.tsx
index fc11f6554e6..07f38364503 100644
--- a/desktop/src/features/pulse/ui/AgentActivityCard.tsx
+++ b/desktop/src/features/pulse/ui/AgentActivityCard.tsx
@@ -75,7 +75,7 @@ export function AgentActivityCard({
{displayName}
{agentStatus ?
: null}
-
+
{formatRelativeTime(group.latestAt)}
@@ -113,7 +113,7 @@ export function AgentActivityCard({
-
+
{formatRelativeTime(note.createdAt)}
diff --git a/desktop/src/features/pulse/ui/NoteCard.tsx b/desktop/src/features/pulse/ui/NoteCard.tsx
index e329b1e4814..e3ba40ae7a3 100644
--- a/desktop/src/features/pulse/ui/NoteCard.tsx
+++ b/desktop/src/features/pulse/ui/NoteCard.tsx
@@ -191,7 +191,7 @@ export function NoteCard({
{isAgent ? (
-
+
bot
) : null}
diff --git a/desktop/src/features/pulse/ui/PulseTabBar.tsx b/desktop/src/features/pulse/ui/PulseTabBar.tsx
index cc3793b9363..3ba3b0777ac 100644
--- a/desktop/src/features/pulse/ui/PulseTabBar.tsx
+++ b/desktop/src/features/pulse/ui/PulseTabBar.tsx
@@ -13,7 +13,7 @@ type PulseTabBarProps = {
};
const tabButtonClassName =
- "h-7 rounded-full border border-transparent px-1.5 text-[10.5px] font-medium text-muted-foreground data-[active=true]:border-border/70 data-[active=true]:bg-background/80 data-[active=true]:text-foreground data-[active=true]:shadow-xs data-[active=true]:backdrop-blur-sm";
+ "h-7 rounded-full border border-transparent px-1.5 text-2xs font-medium text-muted-foreground data-[active=true]:border-border/70 data-[active=true]:bg-background/80 data-[active=true]:text-foreground data-[active=true]:shadow-xs data-[active=true]:backdrop-blur-sm";
export function PulseTabBar({
activeTab,
@@ -103,7 +103,7 @@ export function PulseTabBar({
>
Agents
{relayAgents.length > 0 ? (
-
+
{relayAgents.length}
) : null}
diff --git a/desktop/src/features/search/ui/TopbarSearch.tsx b/desktop/src/features/search/ui/TopbarSearch.tsx
index 60bd1abc292..bea3f62dcb9 100644
--- a/desktop/src/features/search/ui/TopbarSearch.tsx
+++ b/desktop/src/features/search/ui/TopbarSearch.tsx
@@ -201,7 +201,7 @@ export function TopbarSearch({
placeholder="Search everything"
value={query}
/>
-
+
⌘K
@@ -212,7 +212,7 @@ export function TopbarSearch({
data-testid="search-results"
>
{debouncedQuery.length < MIN_SEARCH_QUERY_LENGTH ? (
-
+
Type at least two characters for live suggestions.
) : searchQuery.isLoading && results.length === 0 ? (
@@ -291,7 +291,7 @@ export function TopbarSearch({
: truncateResultText(result.hit.content)}
-
+
{result.kind === "channel"
? "Channel"
: `${describeSearchHit(result.hit)} · ${formatRelativeTime(result.hit.createdAt)}`}
diff --git a/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx b/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx
index ff01e693c5c..bfae506c5ed 100644
--- a/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx
+++ b/desktop/src/features/settings/ui/ChannelTemplatesSettingsCard.tsx
@@ -216,10 +216,7 @@ function TemplateRow({
{template.name}
{template.isBuiltin ? (
-
+
built-in
) : null}
@@ -759,7 +756,7 @@ function RuntimeRow({
) : (
)}
diff --git a/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx b/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx
index c5c27149b98..a457ecb062a 100644
--- a/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx
+++ b/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx
@@ -109,7 +109,7 @@ function RuntimeRow({
{runtime.label}
{runtime.command ? (
-
+
{runtime.command}
) : null}
@@ -134,21 +134,21 @@ function RuntimeRow({
{runtime.underlyingCliPath &&
runtime.underlyingCliPath !== runtime.binaryPath ? (
-
+
CLI: {" "}
{runtime.underlyingCliPath}
-
+
ACP adapter: {" "}
{runtime.binaryPath}
) : (
<>
-
+
{runtime.binaryPath}
-
+
ACP support built-in — no separate adapter needed.
>
@@ -158,7 +158,7 @@ function RuntimeRow({
<>
CLI detected at{" "}
-
+
{runtime.underlyingCliPath ?? "unknown path"}
{" "}
but ACP adapter not found.
@@ -176,7 +176,7 @@ function RuntimeRow({
<>
ACP adapter found at{" "}
-
+
{runtime.binaryPath ?? "unknown path"}
{" "}
but the {runtime.label} CLI is not installed.
diff --git a/desktop/src/features/settings/ui/NotificationSettingsCard.tsx b/desktop/src/features/settings/ui/NotificationSettingsCard.tsx
index be85a6bb67b..af7246ca9b0 100644
--- a/desktop/src/features/settings/ui/NotificationSettingsCard.tsx
+++ b/desktop/src/features/settings/ui/NotificationSettingsCard.tsx
@@ -179,7 +179,7 @@ export function NotificationSettingsCard({
{SLOT_LABELS[slot]}
{comingSoon ? (
-
+
Coming soon
) : null}
diff --git a/desktop/src/features/settings/ui/SoundPicker.tsx b/desktop/src/features/settings/ui/SoundPicker.tsx
index 440fd39fcc3..95d7a0835e3 100644
--- a/desktop/src/features/settings/ui/SoundPicker.tsx
+++ b/desktop/src/features/settings/ui/SoundPicker.tsx
@@ -113,7 +113,7 @@ export function SoundPicker({
{name}
{name === recommended ? (
-
+
rec.
) : null}
diff --git a/desktop/src/features/sidebar/ui/AppSidebar.tsx b/desktop/src/features/sidebar/ui/AppSidebar.tsx
index 0106121a713..0b0e21bfc16 100644
--- a/desktop/src/features/sidebar/ui/AppSidebar.tsx
+++ b/desktop/src/features/sidebar/ui/AppSidebar.tsx
@@ -444,7 +444,7 @@ export function AppSidebar({
{homeBadgeCount > 0 ? (
{Math.min(homeBadgeCount, 99)}
@@ -492,7 +492,7 @@ export function AppSidebar({
{shouldShowAgentCount ? (
{totalAgentCount}
diff --git a/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx b/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx
index 677ffe9b0a0..e7769d2d6f2 100644
--- a/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx
+++ b/desktop/src/features/sidebar/ui/MoreUnreadButton.tsx
@@ -3,7 +3,7 @@ import type * as React from "react";
import { Button } from "@/shared/ui/button";
const MORE_UNREAD_BUTTON_CLASS =
- "h-7 min-h-7 gap-1.5 rounded-full border-0 bg-primary px-2.5 text-[0.6875rem] font-medium text-primary-foreground shadow-md hover:bg-primary/90 [&_svg]:size-3.5";
+ "h-7 min-h-7 gap-1.5 rounded-full border-0 bg-primary px-2.5 text-2xs font-medium text-primary-foreground shadow-md hover:bg-primary/90 [&_svg]:size-3.5";
export function MoreUnreadButton({
bottomClassName = "bottom-0",
diff --git a/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx b/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx
index 557ffcffd3f..6aaf42bd916 100644
--- a/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx
+++ b/desktop/src/features/sidebar/ui/NewDirectMessageDialog.tsx
@@ -180,7 +180,7 @@ export function NewDirectMessageDialog({
>
diff --git a/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx b/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx
index 654403f20de..910e9904eea 100644
--- a/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx
+++ b/desktop/src/features/sidebar/ui/SidebarProfileCard.tsx
@@ -78,10 +78,7 @@ export function SidebarProfileCard({
const workspaceLabel = activeWorkspace?.name ?? "No workspace";
const readonlyWorkspaceLabel = (
-
+
🐝
{workspaceLabel}
diff --git a/desktop/src/features/sidebar/ui/SidebarSection.tsx b/desktop/src/features/sidebar/ui/SidebarSection.tsx
index 3db12f93e62..fa180feaf46 100644
--- a/desktop/src/features/sidebar/ui/SidebarSection.tsx
+++ b/desktop/src/features/sidebar/ui/SidebarSection.tsx
@@ -70,7 +70,7 @@ function DmChannelIcon({
@@ -91,18 +91,18 @@ function DmChannelIcon({
{participants && participants.length > 2 ? (
-
+
{participants.length}
) : null}
diff --git a/desktop/src/features/user-status/ui/SetStatusDialog.tsx b/desktop/src/features/user-status/ui/SetStatusDialog.tsx
index 5f09276d2e0..2eb9027cde0 100644
--- a/desktop/src/features/user-status/ui/SetStatusDialog.tsx
+++ b/desktop/src/features/user-status/ui/SetStatusDialog.tsx
@@ -120,7 +120,7 @@ export function SetStatusDialog({
{emoji ? (
{
event.stopPropagation();
setEmoji("");
diff --git a/desktop/src/features/workflows/ui/WorkflowCard.tsx b/desktop/src/features/workflows/ui/WorkflowCard.tsx
index be539995425..6b14dea27b0 100644
--- a/desktop/src/features/workflows/ui/WorkflowCard.tsx
+++ b/desktop/src/features/workflows/ui/WorkflowCard.tsx
@@ -85,7 +85,7 @@ export function WorkflowCard({
-
+
{channelName ? {channelName} : null}
{triggerSummary ? {triggerSummary} : null}
diff --git a/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx b/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx
index 19ff1319997..9660f5f8d44 100644
--- a/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx
+++ b/desktop/src/features/workflows/ui/WorkflowDetailPanel.tsx
@@ -186,7 +186,7 @@ export function WorkflowDetailPanel({
-
+
{new Date(
run.createdAt * 1000,
@@ -216,10 +216,10 @@ export function WorkflowDetailPanel({
{isSelected ? (
-
+
Execution Trace
{approvalsQuery.isFetching ? (
-
+
Refreshing approvals...
) : null}
diff --git a/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx b/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx
index c83743a6a83..76fc9ecf886 100644
--- a/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx
+++ b/desktop/src/features/workflows/ui/WorkflowRunTrace.tsx
@@ -94,7 +94,7 @@ export function WorkflowRunTrace({
{Object.keys(step.output).length > 0 ? (
-
+
Output
@@ -104,7 +104,7 @@ export function WorkflowRunTrace({
) : null}
{step.error ? (
-
+
Error
@@ -114,7 +114,7 @@ export function WorkflowRunTrace({
) : null}
{pendingApproval ? (
-
+
Pending approval
diff --git a/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx b/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx
index 39c61f91c2c..2703087d843 100644
--- a/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx
+++ b/desktop/src/features/workspaces/ui/WorkspaceSwitcher.tsx
@@ -132,7 +132,7 @@ export function WorkspaceSwitcher({
diff --git a/desktop/src/shared/ui/UserAvatar.tsx b/desktop/src/shared/ui/UserAvatar.tsx
index 08463675579..2f52436ff8c 100644
--- a/desktop/src/shared/ui/UserAvatar.tsx
+++ b/desktop/src/shared/ui/UserAvatar.tsx
@@ -6,8 +6,8 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/shared/ui/avatar";
type UserAvatarSize = "xs" | "sm" | "md";
const sizeClasses: Record = {
- xs: "h-5 w-5 text-[0.5rem]",
- sm: "h-6 w-6 text-[0.5625rem]",
+ xs: "h-5 w-5 text-3xs",
+ sm: "h-6 w-6 text-2xs",
md: "h-10 w-10 text-xs",
};
diff --git a/desktop/src/shared/ui/VideoPlayer.tsx b/desktop/src/shared/ui/VideoPlayer.tsx
index db8a78b7533..d011be3adcb 100644
--- a/desktop/src/shared/ui/VideoPlayer.tsx
+++ b/desktop/src/shared/ui/VideoPlayer.tsx
@@ -531,7 +531,7 @@ function VideoScrubber({
style={{ left: `${hoverRatio * 100}%` }}
>
-
+
{formatTimecode(hoverRatio * duration)} / {formatTimecode(duration)}
@@ -918,7 +918,7 @@ export function VideoPlayer({
)}
{formatTimecode(currentTime)}
@@ -932,7 +932,7 @@ export function VideoPlayer({
testIdPrefix="video-inline"
/>
{formatTimecode(duration)}
@@ -1716,12 +1716,12 @@ function VideoReviewDialog({
{formatTimecode(currentTime)}
{replyTarget ? (
-
+
Replying to {replyTarget.comment.author}
) : (
onSeek(item.seconds ?? 0)}
@@ -1892,7 +1892,7 @@ function VideoReviewCommentBody({
displayName={item.comment.author}
size="xs"
/>
-
+
{item.comment.author}
diff --git a/desktop/src/shared/ui/badge.tsx b/desktop/src/shared/ui/badge.tsx
index 9112e67149e..d7318f5e199 100644
--- a/desktop/src/shared/ui/badge.tsx
+++ b/desktop/src/shared/ui/badge.tsx
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/shared/lib/cn";
const badgeVariants = cva(
- "inline-flex items-center rounded-full px-2 pb-[3px] pt-[5px] text-[0.625rem] font-semibold uppercase leading-none tracking-[0.18em]",
+ "inline-flex items-center rounded-full px-2 pb-[3px] pt-[5px] text-2xs font-semibold uppercase leading-none tracking-[0.18em]",
{
variants: {
variant: {
diff --git a/desktop/tailwind.config.js b/desktop/tailwind.config.js
index 6422fc81acb..221cbe8ed5e 100644
--- a/desktop/tailwind.config.js
+++ b/desktop/tailwind.config.js
@@ -2,6 +2,15 @@
export default {
theme: {
extend: {
+ // Sub-`text-xs` ramp for meta text (timestamps, count badges, tracking
+ // labels) and tiny glyphs. Defined in rem so Cmd +/- zoom — which scales
+ // the root font-size — keeps scaling them. Do NOT reintroduce
+ // arbitrary `text-[…rem]` / `text-[…px]` literals; the px-text guard
+ // rejects them. Stock scale picks up from here: xs (12px), sm (14px)…
+ fontSize: {
+ "2xs": "0.6875rem", // 11px — meta-text workhorse (timestamps, badges)
+ "3xs": "0.5rem", // 8px — tiny glyphs / micro labels
+ },
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
diff --git a/scripts/check-px-text-core.mjs b/scripts/check-px-text-core.mjs
index 16e7dd6e65e..2c122763eb2 100644
--- a/scripts/check-px-text-core.mjs
+++ b/scripts/check-px-text-core.mjs
@@ -11,14 +11,22 @@ import path from "node:path";
* (e.g. the stock `text-base`, `text-sm`, `text-xs` scale — chat === base).
*
* It flags:
- * - Tailwind arbitrary px text utilities: `text-[NNpx]`
+ * - Tailwind arbitrary text-size utilities: `text-[NNpx]`, `text-[N.NNrem]`,
+ * `text-[N.NNem]` — any arbitrary font-size literal. Use a named token
+ * (`text-2xs`, `text-3xs`, or the stock `text-base`/`text-sm`/`text-xs`
+ * scale) so the size lives in `tailwind.config.js` as rem and stays
+ * consistent. px literals freeze against zoom; arbitrary rem literals
+ * re-fragment the scale we just consolidated.
* - CSS px font sizes: `font-size: NNpx`
*
* Decorative/chrome exceptions (avatar initials sized to a fixed avatar box,
- * etc.) live in the `overrides` allowlist supplied by each app.
+ * the `text-[6rem]` emoji glyph, etc.) live in the `overrides` allowlist
+ * supplied by each app.
*/
-const TEXT_PX_RE = /\btext-\[\d+(?:\.\d+)?px\]/g;
+// Any arbitrary Tailwind text-size literal — px, rem, or em. Color literals
+// like `text-[#fff]` or `text-[var(--x)]` don't match (no unit-bearing number).
+const TEXT_ARBITRARY_RE = /\btext-\[\d+(?:\.\d+)?(?:px|rem|em)\]/g;
// Match the CSS `font-size` property, but NOT custom properties like
// `--font-size:` (third-party widget vars) which merely contain the substring.
const FONT_SIZE_PX_RE = /(?
Date: Mon, 15 Jun 2026 11:24:10 -0700
Subject: [PATCH 07/20] fix(desktop): scale composer chips with editor text
- Update rich-text composer mention highlight styling in desktop/src/shared/styles/globals.css to inherit the editor font size instead of forcing text-base
- Reduce chip padding to rem-based values so mention, agent mention, and channel highlights align with the composer line height
- Remove the stale comment that tied composer chips to timeline chip sizing, preserving the larger rendered timeline chip style separately
---
desktop/src/shared/styles/globals.css | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index 4b7b701d519..3fb1ef7eb3a 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -614,11 +614,9 @@
display: inline-block;
border-radius: 4px;
background: hsl(var(--primary) / 0.15);
- padding: 3px 4px 2px;
- /* 1rem === 16px (chat base); rem so it scales with the root-font-size zoom.
- Matches the timeline mention chip (text-base) so composer + timeline align. */
- font-size: 1rem;
- line-height: 1;
+ padding: 0.125rem 0.25rem;
+ font-size: inherit;
+ line-height: 1.1;
color: hsl(var(--primary));
font-weight: 500;
}
From bfabdfe484299b8eff250eeb2442ac7bc140f895 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Mon, 15 Jun 2026 13:46:16 -0700
Subject: [PATCH 08/20] refactor(desktop): remove markdown display variants
- Remove the compact and tight props from the shared Markdown component API
- Collapse MarkdownVariant handling into one default text rhythm with leading-6 body text
- Update message, inbox, pulse, recent note, and agent transcript callers to stop passing compact/tight display flags
- Simplify Markdown memo comparisons now that sizing variants are no longer part of the public surface
---
.../agents/ui/AgentSessionTranscriptList.tsx | 4 +-
.../src/features/home/ui/InboxListPane.tsx | 1 -
.../src/features/home/ui/InboxMessageRow.tsx | 1 -
.../features/home/ui/RecentNotesSection.tsx | 2 +-
.../src/features/messages/ui/MessageRow.tsx | 1 -
desktop/src/features/pulse/ui/NoteCard.tsx | 2 +-
desktop/src/shared/ui/markdown.tsx | 88 ++++---------------
7 files changed, 19 insertions(+), 80 deletions(-)
diff --git a/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx b/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx
index 21c75d9a9ae..0e3e92434d6 100644
--- a/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx
+++ b/desktop/src/features/agents/ui/AgentSessionTranscriptList.tsx
@@ -139,7 +139,7 @@ function MessageItem({
)}
>
{isAssistant ? (
-
+
) : (
<>
{text}
@@ -166,7 +166,7 @@ function ThoughtItem({
-
+
);
diff --git a/desktop/src/features/home/ui/InboxListPane.tsx b/desktop/src/features/home/ui/InboxListPane.tsx
index 2ff283d0b48..db4b6ea4ebd 100644
--- a/desktop/src/features/home/ui/InboxListPane.tsx
+++ b/desktop/src/features/home/ui/InboxListPane.tsx
@@ -182,7 +182,6 @@ export function InboxListPane({
content={item.preview}
interactive={false}
mentionNames={item.mentionNames}
- tight
/>
diff --git a/desktop/src/features/home/ui/InboxMessageRow.tsx b/desktop/src/features/home/ui/InboxMessageRow.tsx
index 8852488b944..81062f109f4 100644
--- a/desktop/src/features/home/ui/InboxMessageRow.tsx
+++ b/desktop/src/features/home/ui/InboxMessageRow.tsx
@@ -131,7 +131,6 @@ export function InboxMessageRow({
className="max-w-full text-left text-sm text-foreground"
content={message.content}
mentionNames={message.mentionNames}
- tight
/>
-
+
diff --git a/desktop/src/features/messages/ui/MessageRow.tsx b/desktop/src/features/messages/ui/MessageRow.tsx
index 424410cdc3b..18de77a941a 100644
--- a/desktop/src/features/messages/ui/MessageRow.tsx
+++ b/desktop/src/features/messages/ui/MessageRow.tsx
@@ -199,7 +199,6 @@ export const MessageRow = React.memo(
mentionNames={mentionNames}
mentionPubkeysByName={mentionPubkeysByName}
searchQuery={searchQuery}
- tight
videoReviewContext={videoReviewContext}
/>
);
diff --git a/desktop/src/features/pulse/ui/NoteCard.tsx b/desktop/src/features/pulse/ui/NoteCard.tsx
index e3ba40ae7a3..4b906cb44b0 100644
--- a/desktop/src/features/pulse/ui/NoteCard.tsx
+++ b/desktop/src/features/pulse/ui/NoteCard.tsx
@@ -213,7 +213,7 @@ export function NoteCard({
) : null}
-
+
diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx
index ab298d58175..2e6f84a198b 100644
--- a/desktop/src/shared/ui/markdown.tsx
+++ b/desktop/src/shared/ui/markdown.tsx
@@ -201,7 +201,6 @@ function messageLinkUrlTransform(value: string, key: string): string {
type MarkdownProps = {
channelNames?: string[];
className?: string;
- compact?: boolean;
content: string;
customEmoji?: CustomEmoji[];
imetaByUrl?: ImetaLookup;
@@ -210,12 +209,9 @@ type MarkdownProps = {
mentionNames?: string[];
mentionPubkeysByName?: Record
;
searchQuery?: string;
- tight?: boolean;
videoReviewContext?: VideoReviewContext;
};
-type MarkdownVariant = "default" | "compact" | "tight";
-
/**
* Inline image embed with click-to-zoom lightbox and right-click download.
*
@@ -730,7 +726,6 @@ function SyntaxHighlightedCode({
);
}
function createMarkdownComponents(
- variant: MarkdownVariant,
channels: Channel[],
onOpenChannel: (channelId: string) => void,
onOpenMessageLink: (link: ParsedMessageLink) => void,
@@ -739,18 +734,10 @@ function createMarkdownComponents(
agentMentionPubkeysByName?: Record,
interactive = true,
): Components {
- const paragraphClassName =
- variant === "tight"
- ? "leading-5"
- : variant === "compact"
- ? "leading-6"
- : "leading-7";
- const listItemClassName =
- variant === "tight" ? "my-0.5 [&_p]:inline" : "my-1 [&_p]:inline";
+ const paragraphClassName = "leading-6";
+ const listItemClassName = "my-1 [&_p]:inline";
const listClassName =
- variant === "tight"
- ? "space-y-0.5 pl-6 marker:text-muted-foreground"
- : "space-y-1 pl-6 marker:text-muted-foreground";
+ "space-y-1 pl-6 marker:text-muted-foreground";
return {
a: ({ children, href, ...props }) => {
@@ -1093,7 +1080,6 @@ function createMarkdownComponents(
function MarkdownInner({
channelNames,
className,
- compact = false,
content,
customEmoji,
imetaByUrl,
@@ -1102,14 +1088,8 @@ function MarkdownInner({
mentionNames,
mentionPubkeysByName,
searchQuery,
- tight = false,
videoReviewContext,
}: MarkdownProps) {
- const variant: MarkdownVariant = tight
- ? "tight"
- : compact
- ? "compact"
- : "default";
const { channels: rawChannels } = useChannelNavigation();
const channels = useStableArray(rawChannels);
const { goChannel } = useAppNavigation();
@@ -1117,7 +1097,6 @@ function MarkdownInner({
const components = React.useMemo(
() =>
createMarkdownComponents(
- variant,
channels,
(channelId) => {
void goChannel(channelId);
@@ -1142,7 +1121,6 @@ function MarkdownInner({
),
[
goChannel,
- variant,
channels,
imetaByUrl,
mentionPubkeysByName,
@@ -1198,52 +1176,18 @@ function MarkdownInner({
return (
*:first-child]:mt-0 [&>*:last-child]:mb-0",
- // Base owl: p+p, list+p, etc.
- "[&>*+*]:mt-2",
- // Headings: flat push/pull — size does the hierarchy work
- "[&>*+h1]:mt-2.5 [&>*+h2]:mt-2.5 [&>*+h3]:mt-2.5",
- "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
- // Blockquotes: breathe above and below
- "[&>*+blockquote]:mt-3 [&>blockquote+*]:mt-3",
- // Code blocks: breathe above and below
- "[&>*+[data-code-block]]:mt-3 [&>[data-code-block]+*]:mt-3",
- // Tables: breathe above and below
- "[&>*+[data-table-block]]:mt-3 [&>[data-table-block]+*]:mt-3",
- // hr: clear section divider
- "[&>*+hr]:mt-3.5 [&>hr+*]:mt-3.5",
- // Lists after paragraphs: tighter to feel related
- "[&>p+ul]:mt-1 [&>p+ol]:mt-1 [&>div+ul]:mt-1 [&>div+ol]:mt-1",
- ].join(" ")
- : compact
- ? [
- "max-w-none break-words text-base text-foreground/90",
- "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
- "[&>*+*]:mt-2",
- "[&>*+h1]:mt-3 [&>*+h2]:mt-3 [&>*+h3]:mt-3",
- "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
- "[&>*+blockquote]:mt-3 [&>blockquote+*]:mt-3",
- "[&>*+[data-code-block]]:mt-3 [&>[data-code-block]+*]:mt-3",
- "[&>*+[data-table-block]]:mt-3 [&>[data-table-block]+*]:mt-3",
- "[&>*+hr]:mt-3.5 [&>hr+*]:mt-3.5",
- "[&>p+ul]:mt-1 [&>p+ol]:mt-1 [&>div+ul]:mt-1 [&>div+ol]:mt-1",
- ].join(" ")
- : [
- "max-w-none break-words text-sm leading-7 text-foreground/90",
- "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
- "[&>*+*]:mt-3",
- "[&>*+h1]:mt-3.5 [&>*+h2]:mt-3.5 [&>*+h3]:mt-3.5",
- "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
- "[&>*+blockquote]:mt-3.5 [&>blockquote+*]:mt-3.5",
- "[&>*+[data-code-block]]:mt-3.5 [&>[data-code-block]+*]:mt-3.5",
- "[&>*+[data-table-block]]:mt-3.5 [&>[data-table-block]+*]:mt-3.5",
- "[&>*+hr]:mt-4 [&>hr+*]:mt-4",
- "[&>p+ul]:mt-1.5 [&>p+ol]:mt-1.5 [&>div+ul]:mt-1.5 [&>div+ol]:mt-1.5",
- ].join(" "),
+ [
+ "max-w-none break-words text-sm leading-6 text-foreground/90",
+ "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
+ "[&>*+*]:mt-3",
+ "[&>*+h1]:mt-3.5 [&>*+h2]:mt-3.5 [&>*+h3]:mt-3.5",
+ "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
+ "[&>*+blockquote]:mt-3.5 [&>blockquote+*]:mt-3.5",
+ "[&>*+[data-code-block]]:mt-3.5 [&>[data-code-block]+*]:mt-3.5",
+ "[&>*+[data-table-block]]:mt-3.5 [&>[data-table-block]+*]:mt-3.5",
+ "[&>*+hr]:mt-4 [&>hr+*]:mt-4",
+ "[&>p+ul]:mt-1.5 [&>p+ol]:mt-1.5 [&>div+ul]:mt-1.5 [&>div+ol]:mt-1.5",
+ ].join(" "),
className,
)}
>
@@ -1259,10 +1203,8 @@ export const Markdown = React.memo(
(prev, next) =>
prev.content === next.content &&
prev.className === next.className &&
- prev.compact === next.compact &&
prev.customEmoji === next.customEmoji &&
prev.interactive === next.interactive &&
- prev.tight === next.tight &&
prev.agentMentionPubkeysByName === next.agentMentionPubkeysByName &&
prev.mentionPubkeysByName === next.mentionPubkeysByName &&
shallowArrayEqual(prev.mentionNames, next.mentionNames) &&
From 8d11f7ff205d34e3f594be3673f116ef2d8f179f Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Mon, 15 Jun 2026 13:46:39 -0700
Subject: [PATCH 09/20] fix(desktop): unify mention chip and code styling
- Move mention chip sizing and hover treatment into shared CSS classes consumed by timeline markdown and composer decorations
- Apply shared chip styling to channel links and message links so inline semantic chips use one visual treatment
- Keep bot mentions optically balanced with agent-specific right padding and a slightly larger robot mask icon
- Align composer text with the chat base type size and reduce mention/code radii by one token for a tighter inline appearance
---
.../messages/lib/mentionHighlightExtension.ts | 6 ++--
desktop/src/shared/styles/globals.css | 32 +++++++++++++------
desktop/src/shared/ui/markdown.tsx | 18 ++++++++---
desktop/src/shared/ui/mentionChip.ts | 6 ++--
4 files changed, 41 insertions(+), 21 deletions(-)
diff --git a/desktop/src/features/messages/lib/mentionHighlightExtension.ts b/desktop/src/features/messages/lib/mentionHighlightExtension.ts
index 53f7636744d..63ebfba407f 100644
--- a/desktop/src/features/messages/lib/mentionHighlightExtension.ts
+++ b/desktop/src/features/messages/lib/mentionHighlightExtension.ts
@@ -264,14 +264,14 @@ function buildDecorations(
node.text,
pos,
mentionPatterns,
- "mention-highlight",
+ "mention-highlight mention-chip",
);
addMatchesForPatterns(
decorations,
node.text,
pos,
agentMentionPatterns,
- "mention-highlight agent-mention-highlight",
+ "mention-highlight mention-chip agent-mention-highlight",
{ hideMentionPrefix: true },
);
addMatchesForPatterns(
@@ -279,7 +279,7 @@ function buildDecorations(
node.text,
pos,
channelPatterns,
- "mention-highlight",
+ "mention-highlight mention-chip",
);
});
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index 3fb1ef7eb3a..34511ecb31f 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -503,7 +503,7 @@
.rich-text-composer .tiptap {
outline: none;
min-height: 1.5rem; /* single line height */
- font-size: 0.875rem;
+ font-size: 1rem;
line-height: 1.5rem;
}
@@ -546,7 +546,7 @@
}
.rich-text-composer .tiptap code {
- border-radius: 0.375rem;
+ border-radius: calc(var(--radius) - 4px);
background: hsl(var(--muted));
padding: 0.125rem 0.375rem;
font-family: ui-monospace, monospace;
@@ -554,7 +554,7 @@
}
.rich-text-composer .tiptap pre {
- border-radius: 0.75rem;
+ border-radius: var(--radius);
border: 1px solid hsl(var(--border) / 0.7);
background: hsl(var(--muted) / 0.6);
padding: 0.375rem 0.75rem;
@@ -610,17 +610,30 @@
margin: 0.5rem 0;
}
+.mention-chip,
.rich-text-composer .tiptap .mention-highlight {
display: inline-block;
- border-radius: 4px;
+ border-radius: calc(var(--radius) - 4px);
background: hsl(var(--primary) / 0.15);
- padding: 0.125rem 0.25rem;
- font-size: inherit;
- line-height: 1.1;
+ padding: 0 0.25rem;
+ font-size: 1rem;
+ line-height: 1.25rem;
color: hsl(var(--primary));
font-weight: 500;
}
+.mention-chip-hover {
+ transition-property: color, background-color, border-color, outline-color,
+ text-decoration-color, fill, stroke;
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ transition-duration: 150ms;
+}
+
+.mention-chip-hover:hover {
+ background: hsl(var(--primary) / 0.25);
+ color: hsl(var(--primary) / 0.9);
+}
+
.rich-text-composer .tiptap .agent-mention-at-hidden {
color: transparent;
font-size: 0;
@@ -633,6 +646,7 @@
align-items: center;
gap: 2px;
background: hsl(var(--primary) / 0.18);
+ padding-right: 0.375rem;
color: hsl(var(--primary) / 0.92);
}
@@ -644,8 +658,8 @@
[data-mention].agent-mention-highlight::before {
content: "";
display: inline-block;
- width: 0.85em;
- height: 0.85em;
+ width: 0.9em;
+ height: 0.9em;
flex: 0 0 auto;
background: currentColor;
transform: translateY(-0.25px);
diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx
index 2e6f84a198b..ba30a727a05 100644
--- a/desktop/src/shared/ui/markdown.tsx
+++ b/desktop/src/shared/ui/markdown.tsx
@@ -497,7 +497,7 @@ function MarkdownCodeBlock({
return (
-
+
{language && (
{language}
@@ -837,7 +837,7 @@ function createMarkdownComponents(
@@ -1018,7 +1018,11 @@ function createMarkdownComponents(
type="button"
data-channel-link=""
aria-label={`Open channel ${channelName}`}
- className="rounded-md bg-primary/15 px-1 py-0.5 text-sm font-medium text-primary cursor-pointer hover:bg-primary/25 transition-colors"
+ className={cn(
+ "cursor-pointer",
+ MENTION_CHIP_BASE_CLASSES,
+ MENTION_CHIP_HOVER_CLASSES,
+ )}
onClick={() => {
onOpenChannel(channel.id);
}}
@@ -1031,7 +1035,7 @@ function createMarkdownComponents(
return (
{children}
@@ -1065,7 +1069,11 @@ function createMarkdownComponents(
data-message-link=""
aria-label={`Open message in ${channelLabel}`}
title={href}
- className="rounded-md bg-primary/15 px-1 py-0.5 text-sm font-medium text-primary cursor-pointer hover:bg-primary/25 transition-colors"
+ className={cn(
+ "cursor-pointer",
+ MENTION_CHIP_BASE_CLASSES,
+ MENTION_CHIP_HOVER_CLASSES,
+ )}
onClick={() => {
onOpenMessageLink(parsed.value);
}}
diff --git a/desktop/src/shared/ui/mentionChip.ts b/desktop/src/shared/ui/mentionChip.ts
index 583ee3ff3db..e63abb3ed62 100644
--- a/desktop/src/shared/ui/mentionChip.ts
+++ b/desktop/src/shared/ui/mentionChip.ts
@@ -1,5 +1,3 @@
-export const MENTION_CHIP_BASE_CLASSES =
- "inline-block rounded-[4px] bg-primary/15 px-1 pt-[3px] pb-[2px] text-base font-medium leading-none text-primary";
+export const MENTION_CHIP_BASE_CLASSES = "mention-chip";
-export const MENTION_CHIP_HOVER_CLASSES =
- "transition-colors hover:bg-primary/25 hover:text-primary/90";
+export const MENTION_CHIP_HOVER_CLASSES = "mention-chip-hover";
From 7366ebdc4de9da11f895cc3742b0494dcf1d9457 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Mon, 15 Jun 2026 14:09:06 -0700
Subject: [PATCH 10/20] fix(desktop): align message header typography
- Add shared MessageHeaderRow and MessageAuthorText primitives for timeline header spacing and author/title typography
- Use the shared header primitives in normal and system message rows so usernames, system titles, and timestamps align consistently
- Reduce main timeline username size to match surrounding message surfaces and remove the extra body offset now that header line boxes align
- Render system-event participants as profile names instead of mention chips and drop unused agent/persona props from system row plumbing
---
.../features/messages/ui/MessageHeader.tsx | 50 ++++++++++++++++++
.../src/features/messages/ui/MessageRow.tsx | 25 ++++-----
.../features/messages/ui/SystemMessageRow.tsx | 51 ++++---------------
.../messages/ui/TimelineMessageList.tsx | 2 -
desktop/src/shared/ui/markdown.tsx | 8 +--
5 files changed, 76 insertions(+), 60 deletions(-)
create mode 100644 desktop/src/features/messages/ui/MessageHeader.tsx
diff --git a/desktop/src/features/messages/ui/MessageHeader.tsx b/desktop/src/features/messages/ui/MessageHeader.tsx
new file mode 100644
index 00000000000..9439f39be67
--- /dev/null
+++ b/desktop/src/features/messages/ui/MessageHeader.tsx
@@ -0,0 +1,50 @@
+import type * as React from "react";
+
+import { cn } from "@/shared/lib/cn";
+
+type MessageHeaderRowProps = {
+ children: React.ReactNode;
+ className?: string;
+};
+
+export function MessageHeaderRow({
+ children,
+ className,
+}: MessageHeaderRowProps) {
+ return (
+
+ {children}
+
+ );
+}
+
+type MessageAuthorTextProps = {
+ as?: "div" | "h3" | "span";
+ children: React.ReactNode;
+ className?: string;
+ hoverUnderline?: boolean;
+};
+
+export function MessageAuthorText({
+ as: Component = "span",
+ children,
+ className,
+ hoverUnderline = false,
+}: MessageAuthorTextProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/desktop/src/features/messages/ui/MessageRow.tsx b/desktop/src/features/messages/ui/MessageRow.tsx
index 18de77a941a..c20e40c049b 100644
--- a/desktop/src/features/messages/ui/MessageRow.tsx
+++ b/desktop/src/features/messages/ui/MessageRow.tsx
@@ -20,6 +20,7 @@ import {
import { Markdown } from "@/shared/ui/markdown";
import type { VideoReviewContext } from "@/shared/ui/VideoPlayer";
import { MessageActionBar } from "./MessageActionBar";
+import { MessageAuthorText, MessageHeaderRow } from "./MessageHeader";
import { MessageTimestamp } from "./MessageTimestamp";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
@@ -244,13 +245,13 @@ export const MessageRow = React.memo(
);
const authorNode = message.pubkey ? (
-
+
{message.author}
-
+
) : (
-
+
{message.author}
-
+
);
const actionBarNode = (
@@ -400,7 +401,7 @@ export const MessageRow = React.memo(
{avatarNode}
)}
-
+
{message.pubkey ? (
{authorNode}
@@ -424,8 +425,8 @@ export const MessageRow = React.memo(
{message.personaDisplayName}
) : null}
-
-
{messageBodyNode}
+
+
{messageBodyNode}
>
) : (
@@ -450,7 +451,7 @@ export const MessageRow = React.memo(
{avatarNode}
)}
-
+
{message.pubkey ? (
{authorNode}
@@ -474,8 +475,8 @@ export const MessageRow = React.memo(
{message.personaDisplayName}
) : null}
-
-
{messageBodyNode}
+
+
{messageBodyNode}
>
)}
diff --git a/desktop/src/features/messages/ui/SystemMessageRow.tsx b/desktop/src/features/messages/ui/SystemMessageRow.tsx
index 69ac8519900..9a79974e73e 100644
--- a/desktop/src/features/messages/ui/SystemMessageRow.tsx
+++ b/desktop/src/features/messages/ui/SystemMessageRow.tsx
@@ -9,16 +9,12 @@ import type { UserProfileLookup } from "@/features/profile/lib/identity";
import { resolveUserLabel } from "@/features/profile/lib/identity";
import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
import { cn } from "@/shared/lib/cn";
-import { normalizePubkey } from "@/shared/lib/pubkey";
import { Button } from "@/shared/ui/button";
import { isPositiveEmojiParticle } from "@/shared/ui/EmojiBurstProvider";
-import {
- MENTION_CHIP_BASE_CLASSES,
- MENTION_CHIP_HOVER_CLASSES,
-} from "@/shared/ui/mentionChip";
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
import { UserAvatar } from "@/shared/ui/UserAvatar";
+import { MessageAuthorText, MessageHeaderRow } from "./MessageHeader";
import { MessageTimestamp } from "./MessageTimestamp";
type SystemMessagePayload = {
@@ -63,31 +59,18 @@ function resolveDisplayLabel(
function ProfileName({
children,
- highlight = false,
- isAgent = false,
pubkey,
}: {
children: React.ReactNode;
- highlight?: boolean;
- isAgent?: boolean;
pubkey: string | undefined;
}) {
- const isAgentMention = highlight && isAgent;
const node = (
- {highlight && !isAgentMention ? "@" : null}
{children}
);
@@ -192,14 +175,7 @@ function describeSystemEvent(
payload: SystemMessagePayload,
currentPubkey: string | undefined,
profiles: UserProfileLookup | undefined,
- personaLookup?: Map,
- agentPubkeys?: ReadonlySet,
): SystemMessageDescription | null {
- const isTargetAgent =
- payload.target !== undefined &&
- (agentPubkeys?.has(normalizePubkey(payload.target)) === true ||
- profiles?.[normalizePubkey(payload.target)]?.isAgent === true ||
- personaLookup?.has(normalizePubkey(payload.target)) === true);
const actorLabel = resolveDisplayLabel(
payload.actor,
currentPubkey,
@@ -214,7 +190,7 @@ function describeSystemEvent(
{actorLabel}
);
const targetName = (
-
+
{targetLabel}
);
@@ -275,17 +251,12 @@ function describeSystemEvent(
export const SystemMessageRow = React.memo(function SystemMessageRow({
message,
currentPubkey,
- agentPubkeys,
profiles,
- personaLookup,
onToggleReaction,
}: {
message: TimelineMessage;
currentPubkey?: string;
- agentPubkeys?: ReadonlySet;
profiles?: UserProfileLookup;
- /** Map from lowercase pubkey → persona display name for bot members. */
- personaLookup?: Map;
onToggleReaction?: (
message: TimelineMessage,
emoji: string,
@@ -315,8 +286,6 @@ export const SystemMessageRow = React.memo(function SystemMessageRow({
payload,
currentPubkey,
profiles,
- personaLookup,
- agentPubkeys,
);
if (!description) {
return null;
@@ -340,18 +309,20 @@ export const SystemMessageRow = React.memo(function SystemMessageRow({
targetPubkey={payload.target}
/>
-
-
+
+
{description.title}
-
+
+
+
+
+ {description.action}
+
-
- {description.action}
-
{footer}
diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx
index ba30a727a05..aaa551879fa 100644
--- a/desktop/src/shared/ui/markdown.tsx
+++ b/desktop/src/shared/ui/markdown.tsx
@@ -736,8 +736,7 @@ function createMarkdownComponents(
): Components {
const paragraphClassName = "leading-6";
const listItemClassName = "my-1 [&_p]:inline";
- const listClassName =
- "space-y-1 pl-6 marker:text-muted-foreground";
+ const listClassName = "space-y-1 pl-6 marker:text-muted-foreground";
return {
a: ({ children, href, ...props }) => {
@@ -1033,10 +1032,7 @@ function createMarkdownComponents(
}
return (
-
+
{children}
);
From 30c3b14f82ef438bde57d6696375fb036f7d2596 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Mon, 15 Jun 2026 14:39:13 -0700
Subject: [PATCH 11/20] fix(desktop): align message text stack with avatar
height
- Increase timeline message avatars to the 10 spacing token so one-line messages have a clearer visual anchor
- Replace the previous negative text-column offset with an explicit gap between the header and body
- Set header author and timestamp line-height to leading-4 so the header plus one body line aligns with the avatar height
- Apply the same header/body gap treatment to system message rows for consistent vertical rhythm
---
desktop/src/features/messages/ui/MessageHeader.tsx | 4 ++--
desktop/src/features/messages/ui/MessageRow.tsx | 10 +++++-----
desktop/src/features/messages/ui/MessageTimestamp.tsx | 2 +-
desktop/src/features/messages/ui/SystemMessageRow.tsx | 2 +-
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/desktop/src/features/messages/ui/MessageHeader.tsx b/desktop/src/features/messages/ui/MessageHeader.tsx
index 9439f39be67..c9a5f219ba3 100644
--- a/desktop/src/features/messages/ui/MessageHeader.tsx
+++ b/desktop/src/features/messages/ui/MessageHeader.tsx
@@ -14,7 +14,7 @@ export function MessageHeaderRow({
return (
@@ -39,7 +39,7 @@ export function MessageAuthorText({
return (
{avatarNode}
)}
-
+
{message.pubkey ? (
{authorNode}
@@ -450,7 +450,7 @@ export const MessageRow = React.memo(
) : (
{avatarNode}
)}
-
+
{message.pubkey ? (
{authorNode}
diff --git a/desktop/src/features/messages/ui/MessageTimestamp.tsx b/desktop/src/features/messages/ui/MessageTimestamp.tsx
index e8285d173ff..3a28508f7c8 100644
--- a/desktop/src/features/messages/ui/MessageTimestamp.tsx
+++ b/desktop/src/features/messages/ui/MessageTimestamp.tsx
@@ -11,7 +11,7 @@ export function MessageTimestamp({
return (
-
+
{time}
diff --git a/desktop/src/features/messages/ui/SystemMessageRow.tsx b/desktop/src/features/messages/ui/SystemMessageRow.tsx
index 9a79974e73e..b73ab946e0c 100644
--- a/desktop/src/features/messages/ui/SystemMessageRow.tsx
+++ b/desktop/src/features/messages/ui/SystemMessageRow.tsx
@@ -308,7 +308,7 @@ export const SystemMessageRow = React.memo(function SystemMessageRow({
profiles={profiles}
targetPubkey={payload.target}
/>
-
+
{description.title}
From 0714b6593b11435eaef1efc75a947930bcc20c52 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Mon, 15 Jun 2026 21:48:19 -0700
Subject: [PATCH 12/20] fix(desktop): restore markdown and system row cleanup
after merge
- Reapply the removal of Markdown compact/tight display variants while preserving the merged runtime-ref markdown implementation
- Switch markdown body copy to unitless leading-relaxed so timeline text scales proportionally with app zoom
- Remove compact Markdown props from forum, feed, and channel canvas callers introduced by the main-branch merge
- Restore system-event participant rendering as plain profile names and remove unused agent/persona props from SystemMessageRow plumbing
---
.../features/channels/ui/ChannelCanvas.tsx | 1 -
.../src/features/forum/ui/ForumPostCard.tsx | 1 -
.../features/forum/ui/ForumThreadPanel.tsx | 1 -
desktop/src/features/home/ui/FeedSection.tsx | 1 -
.../features/messages/ui/SystemMessageRow.tsx | 36 +-------
.../messages/ui/TimelineMessageList.tsx | 2 -
desktop/src/shared/ui/markdown.tsx | 91 ++++---------------
7 files changed, 19 insertions(+), 114 deletions(-)
diff --git a/desktop/src/features/channels/ui/ChannelCanvas.tsx b/desktop/src/features/channels/ui/ChannelCanvas.tsx
index 8b379f3674e..ecdb36cb8d4 100644
--- a/desktop/src/features/channels/ui/ChannelCanvas.tsx
+++ b/desktop/src/features/channels/ui/ChannelCanvas.tsx
@@ -123,7 +123,6 @@ export function ChannelCanvas({
>
diff --git a/desktop/src/features/forum/ui/ForumPostCard.tsx b/desktop/src/features/forum/ui/ForumPostCard.tsx
index 2ce878aee75..7655a42a488 100644
--- a/desktop/src/features/forum/ui/ForumPostCard.tsx
+++ b/desktop/src/features/forum/ui/ForumPostCard.tsx
@@ -116,7 +116,6 @@ export function ForumPostCard({
diff --git a/desktop/src/features/messages/ui/SystemMessageRow.tsx b/desktop/src/features/messages/ui/SystemMessageRow.tsx
index cfe704e60f7..82a85bf03ec 100644
--- a/desktop/src/features/messages/ui/SystemMessageRow.tsx
+++ b/desktop/src/features/messages/ui/SystemMessageRow.tsx
@@ -10,13 +10,8 @@ import type { UserProfileLookup } from "@/features/profile/lib/identity";
import { resolveUserLabel } from "@/features/profile/lib/identity";
import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
import { cn } from "@/shared/lib/cn";
-import { normalizePubkey } from "@/shared/lib/pubkey";
import { Button } from "@/shared/ui/button";
import { isPositiveEmojiParticle } from "@/shared/ui/EmojiBurstProvider";
-import {
- MENTION_CHIP_BASE_CLASSES,
- MENTION_CHIP_HOVER_CLASSES,
-} from "@/shared/ui/mentionChip";
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
import { UserAvatar } from "@/shared/ui/UserAvatar";
@@ -67,31 +62,18 @@ function resolveDisplayLabel(
function ProfileName({
children,
- highlight = false,
- isAgent = false,
pubkey,
}: {
children: React.ReactNode;
- highlight?: boolean;
- isAgent?: boolean;
pubkey: string | undefined;
}) {
- const isAgentMention = highlight && isAgent;
const node = (
- {highlight && !isAgentMention ? "@" : null}
{children}
);
@@ -196,14 +178,7 @@ function describeSystemEvent(
payload: SystemMessagePayload,
currentPubkey: string | undefined,
profiles: UserProfileLookup | undefined,
- personaLookup?: Map,
- agentPubkeys?: ReadonlySet,
): SystemMessageDescription | null {
- const isTargetAgent =
- payload.target !== undefined &&
- (agentPubkeys?.has(normalizePubkey(payload.target)) === true ||
- profiles?.[normalizePubkey(payload.target)]?.isAgent === true ||
- personaLookup?.has(normalizePubkey(payload.target)) === true);
const actorLabel = resolveDisplayLabel(
payload.actor,
currentPubkey,
@@ -218,7 +193,7 @@ function describeSystemEvent(
{actorLabel}
);
const targetName = (
-
+
{targetLabel}
);
@@ -279,17 +254,12 @@ function describeSystemEvent(
export const SystemMessageRow = React.memo(function SystemMessageRow({
message,
currentPubkey,
- agentPubkeys,
profiles,
- personaLookup,
onToggleReaction,
}: {
message: TimelineMessage;
currentPubkey?: string;
- agentPubkeys?: ReadonlySet;
profiles?: UserProfileLookup;
- /** Map from lowercase pubkey → persona display name for bot members. */
- personaLookup?: Map;
onToggleReaction?: (
message: TimelineMessage,
emoji: string,
@@ -319,8 +289,6 @@ export const SystemMessageRow = React.memo(function SystemMessageRow({
payload,
currentPubkey,
profiles,
- personaLookup,
- agentPubkeys,
);
if (!description) {
return null;
diff --git a/desktop/src/features/messages/ui/TimelineMessageList.tsx b/desktop/src/features/messages/ui/TimelineMessageList.tsx
index 46b1f5eaf32..4bc590c76f6 100644
--- a/desktop/src/features/messages/ui/TimelineMessageList.tsx
+++ b/desktop/src/features/messages/ui/TimelineMessageList.tsx
@@ -157,10 +157,8 @@ export const TimelineMessageList = React.memo(function TimelineMessageList({
{footer}
diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx
index 583c2819067..03ae1d4a7a3 100644
--- a/desktop/src/shared/ui/markdown.tsx
+++ b/desktop/src/shared/ui/markdown.tsx
@@ -216,7 +216,6 @@ function messageLinkUrlTransform(value: string, key: string): string {
type MarkdownProps = {
channelNames?: string[];
className?: string;
- compact?: boolean;
content: string;
customEmoji?: CustomEmoji[];
imetaByUrl?: ImetaLookup;
@@ -225,12 +224,9 @@ type MarkdownProps = {
mentionNames?: string[];
mentionPubkeysByName?: Record
;
searchQuery?: string;
- tight?: boolean;
videoReviewContext?: VideoReviewContext;
};
-type MarkdownVariant = "default" | "compact" | "tight";
-
/**
* Inline image embed with click-to-zoom lightbox and right-click download.
*
@@ -745,22 +741,12 @@ function SyntaxHighlightedCode({
);
}
function createMarkdownComponents(
- variant: MarkdownVariant,
runtimeRef: React.RefObject,
interactive = true,
): Components {
- const paragraphClassName =
- variant === "tight"
- ? "leading-5"
- : variant === "compact"
- ? "leading-6"
- : "leading-7";
- const listItemClassName =
- variant === "tight" ? "my-0.5 [&_p]:inline" : "my-1 [&_p]:inline";
- const listClassName =
- variant === "tight"
- ? "space-y-0.5 pl-6 marker:text-muted-foreground"
- : "space-y-1 pl-6 marker:text-muted-foreground";
+ const paragraphClassName = "leading-relaxed";
+ const listItemClassName = "my-1 [&_p]:inline";
+ const listClassName = "space-y-1 pl-6 marker:text-muted-foreground";
return {
a: ({ children, href, ...props }) => {
@@ -1114,7 +1100,6 @@ function createMarkdownComponents(
function MarkdownInner({
channelNames,
className,
- compact = false,
content,
customEmoji,
imetaByUrl,
@@ -1123,14 +1108,8 @@ function MarkdownInner({
mentionNames,
mentionPubkeysByName,
searchQuery,
- tight = false,
videoReviewContext,
}: MarkdownProps) {
- const variant: MarkdownVariant = tight
- ? "tight"
- : compact
- ? "compact"
- : "default";
const { channels: rawChannels } = useChannelNavigation();
const channels = useStableArray(rawChannels);
const { goChannel } = useAppNavigation();
@@ -1166,8 +1145,8 @@ function MarkdownInner({
});
const components = React.useMemo(
- () => createMarkdownComponents(variant, runtimeRef, interactive),
- [variant, runtimeRef, interactive],
+ () => createMarkdownComponents(runtimeRef, interactive),
+ [runtimeRef, interactive],
);
// biome-ignore lint/suspicious/noExplicitAny: PluggableList type not directly importable
@@ -1217,52 +1196,18 @@ function MarkdownInner({
return (
*:first-child]:mt-0 [&>*:last-child]:mb-0",
- // Base owl: p+p, list+p, etc.
- "[&>*+*]:mt-2",
- // Headings: flat push/pull — size does the hierarchy work
- "[&>*+h1]:mt-2.5 [&>*+h2]:mt-2.5 [&>*+h3]:mt-2.5",
- "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
- // Blockquotes: breathe above and below
- "[&>*+blockquote]:mt-3 [&>blockquote+*]:mt-3",
- // Code blocks: breathe above and below
- "[&>*+[data-code-block]]:mt-3 [&>[data-code-block]+*]:mt-3",
- // Tables: breathe above and below
- "[&>*+[data-table-block]]:mt-3 [&>[data-table-block]+*]:mt-3",
- // hr: clear section divider
- "[&>*+hr]:mt-3.5 [&>hr+*]:mt-3.5",
- // Lists after paragraphs: tighter to feel related
- "[&>p+ul]:mt-1 [&>p+ol]:mt-1 [&>div+ul]:mt-1 [&>div+ol]:mt-1",
- ].join(" ")
- : compact
- ? [
- "max-w-none break-words text-sm leading-6 text-foreground/90",
- "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
- "[&>*+*]:mt-2",
- "[&>*+h1]:mt-3 [&>*+h2]:mt-3 [&>*+h3]:mt-3",
- "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
- "[&>*+blockquote]:mt-3 [&>blockquote+*]:mt-3",
- "[&>*+[data-code-block]]:mt-3 [&>[data-code-block]+*]:mt-3",
- "[&>*+[data-table-block]]:mt-3 [&>[data-table-block]+*]:mt-3",
- "[&>*+hr]:mt-3.5 [&>hr+*]:mt-3.5",
- "[&>p+ul]:mt-1 [&>p+ol]:mt-1 [&>div+ul]:mt-1 [&>div+ol]:mt-1",
- ].join(" ")
- : [
- "max-w-none break-words text-sm leading-7 text-foreground/90",
- "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
- "[&>*+*]:mt-3",
- "[&>*+h1]:mt-3.5 [&>*+h2]:mt-3.5 [&>*+h3]:mt-3.5",
- "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
- "[&>*+blockquote]:mt-3.5 [&>blockquote+*]:mt-3.5",
- "[&>*+[data-code-block]]:mt-3.5 [&>[data-code-block]+*]:mt-3.5",
- "[&>*+[data-table-block]]:mt-3.5 [&>[data-table-block]+*]:mt-3.5",
- "[&>*+hr]:mt-4 [&>hr+*]:mt-4",
- "[&>p+ul]:mt-1.5 [&>p+ol]:mt-1.5 [&>div+ul]:mt-1.5 [&>div+ol]:mt-1.5",
- ].join(" "),
+ [
+ "max-w-none break-words text-sm leading-relaxed text-foreground/90",
+ "[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
+ "[&>*+*]:mt-3",
+ "[&>*+h1]:mt-3.5 [&>*+h2]:mt-3.5 [&>*+h3]:mt-3.5",
+ "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
+ "[&>*+blockquote]:mt-3.5 [&>blockquote+*]:mt-3.5",
+ "[&>*+[data-code-block]]:mt-3.5 [&>[data-code-block]+*]:mt-3.5",
+ "[&>*+[data-table-block]]:mt-3.5 [&>[data-table-block]+*]:mt-3.5",
+ "[&>*+hr]:mt-4 [&>hr+*]:mt-4",
+ "[&>p+ul]:mt-1.5 [&>p+ol]:mt-1.5 [&>div+ul]:mt-1.5 [&>div+ol]:mt-1.5",
+ ].join(" "),
className,
)}
>
@@ -1278,10 +1223,8 @@ export const Markdown = React.memo(
(prev, next) =>
prev.content === next.content &&
prev.className === next.className &&
- prev.compact === next.compact &&
prev.customEmoji === next.customEmoji &&
prev.interactive === next.interactive &&
- prev.tight === next.tight &&
prev.agentMentionPubkeysByName === next.agentMentionPubkeysByName &&
prev.mentionPubkeysByName === next.mentionPubkeysByName &&
shallowArrayEqual(prev.mentionNames, next.mentionNames) &&
From cf71af120f47f333157ae6943b1e32fcd3cdab9d Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Tue, 16 Jun 2026 14:05:15 -0700
Subject: [PATCH 13/20] fix(desktop): inherit mention chip text rhythm
- Update shared mention chip CSS to inherit both font-size and line-height from its surrounding text context
- Avoid future drift between global chip styling and timeline/forum/body copy wrappers as typography changes
---
desktop/src/shared/styles/globals.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index befe195abcd..14193b3414b 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -626,8 +626,8 @@
border-radius: calc(var(--radius) - 4px);
background: hsl(var(--primary) / 0.15);
padding: 0 0.25rem;
- font-size: 1rem;
- line-height: 1.25rem;
+ font-size: inherit;
+ line-height: inherit;
color: hsl(var(--primary));
font-weight: 500;
}
From a185823380fd6cd3549d46926c24a21323683556 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Tue, 16 Jun 2026 15:54:46 -0700
Subject: [PATCH 14/20] fix(desktop): downshift chat and forum message bodies
to text-sm
Message bodies felt too heavy at text-base after the type-scale cleanup.
Restore stock text-sm on the primary Markdown surfaces so chat and forum
content matches the shared Markdown default and surrounding chrome.
- MessageRow: change body Markdown override from text-base to text-sm
- ForumPostCard: set explicit text-sm on post preview Markdown
- ForumThreadPanel: set explicit text-sm on post body and reply Markdown
- TimelineMessageList: drop unused personaLookup destructure (build fix)
---
desktop/src/features/forum/ui/ForumPostCard.tsx | 1 +
desktop/src/features/forum/ui/ForumThreadPanel.tsx | 2 ++
desktop/src/features/messages/ui/MessageRow.tsx | 2 +-
desktop/src/features/messages/ui/TimelineMessageList.tsx | 1 -
4 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/desktop/src/features/forum/ui/ForumPostCard.tsx b/desktop/src/features/forum/ui/ForumPostCard.tsx
index 7655a42a488..4bd5f0f0873 100644
--- a/desktop/src/features/forum/ui/ForumPostCard.tsx
+++ b/desktop/src/features/forum/ui/ForumPostCard.tsx
@@ -116,6 +116,7 @@ export function ForumPostCard({
Date: Tue, 16 Jun 2026 16:00:59 -0700
Subject: [PATCH 15/20] fix(desktop): use full foreground on system events and
body text
System event actions were too dim at muted-foreground/70, and Markdown
bodies inherited text-foreground/90 from the shared wrapper. Use default
foreground for readable copy while timestamps stay muted.
- SystemMessageRow: title and action text use text-foreground
- markdown.tsx: drop /90 opacity on shared Markdown body wrapper
- VideoPlayer: video review comment body uses text-foreground
---
desktop/src/features/messages/ui/SystemMessageRow.tsx | 4 ++--
desktop/src/shared/ui/VideoPlayer.tsx | 2 +-
desktop/src/shared/ui/markdown.tsx | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/desktop/src/features/messages/ui/SystemMessageRow.tsx b/desktop/src/features/messages/ui/SystemMessageRow.tsx
index 82a85bf03ec..b63e0122e33 100644
--- a/desktop/src/features/messages/ui/SystemMessageRow.tsx
+++ b/desktop/src/features/messages/ui/SystemMessageRow.tsx
@@ -313,7 +313,7 @@ export const SystemMessageRow = React.memo(function SystemMessageRow({
/>
-
-
+
{description.action}
diff --git a/desktop/src/shared/ui/VideoPlayer.tsx b/desktop/src/shared/ui/VideoPlayer.tsx
index 3e591874066..c879d9cd84c 100644
--- a/desktop/src/shared/ui/VideoPlayer.tsx
+++ b/desktop/src/shared/ui/VideoPlayer.tsx
@@ -1876,7 +1876,7 @@ function VideoReviewCommentCard({
replies: TimecodedComment[];
}) {
return (
-
+
*:first-child]:mt-0 [&>*:last-child]:mb-0",
"[&>*+*]:mt-3",
"[&>*+h1]:mt-3.5 [&>*+h2]:mt-3.5 [&>*+h3]:mt-3.5",
From 97d80e1e68d959e6fd0818126a5abb06f2ebcfe2 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Tue, 16 Jun 2026 16:28:55 -0700
Subject: [PATCH 16/20] fix(desktop): match composer text scale to timeline
text-sm
The Tiptap composer still rendered at 1rem because globals.css overrode
the editor's text-sm classes. Align composer body and code with timeline
Markdown at text-sm and leading-relaxed.
- globals.css: set .rich-text-composer .tiptap to 0.875rem / 1.625 lh
- globals.css: bump composer inline and fenced code to 0.875rem
- useRichTextEditor: use leading-relaxed on editor attributes
---
.../src/features/messages/lib/useRichTextEditor.ts | 2 +-
desktop/src/shared/styles/globals.css | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/desktop/src/features/messages/lib/useRichTextEditor.ts b/desktop/src/features/messages/lib/useRichTextEditor.ts
index bc2f442768a..8a0d6e0c0f5 100644
--- a/desktop/src/features/messages/lib/useRichTextEditor.ts
+++ b/desktop/src/features/messages/lib/useRichTextEditor.ts
@@ -324,7 +324,7 @@ export function useRichTextEditor({
autocapitalize: "none",
autocorrect: "off",
class:
- "min-h-0 resize-none overflow-y-hidden border-0 bg-transparent px-0 py-0 text-sm leading-6 text-foreground md:leading-6 shadow-none focus-visible:ring-0 caret-foreground outline-hidden prose-sm max-w-none",
+ "min-h-0 resize-none overflow-y-hidden border-0 bg-transparent px-0 py-0 text-sm leading-relaxed text-foreground shadow-none focus-visible:ring-0 caret-foreground outline-hidden prose-sm max-w-none",
"data-testid": "message-input",
spellcheck: "false",
},
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index 14193b3414b..8e8485fdc39 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -512,9 +512,9 @@
/* ── Tiptap rich-text composer ──────────────────────────────────────── */
.rich-text-composer .tiptap {
outline: none;
- min-height: 1.5rem; /* single line height */
- font-size: 1rem;
- line-height: 1.5rem;
+ min-height: 1.4375rem; /* one line at text-sm + leading-relaxed */
+ font-size: 0.875rem;
+ line-height: 1.625;
}
.rich-text-composer .tiptap p {
@@ -560,7 +560,7 @@
background: hsl(var(--muted));
padding: 0.125rem 0.375rem;
font-family: ui-monospace, monospace;
- font-size: 0.8125rem;
+ font-size: 0.875rem;
}
.rich-text-composer .tiptap pre {
@@ -576,8 +576,8 @@
background: none;
padding: 0;
border-radius: 0;
- font-size: 0.8125rem;
- line-height: 1.5rem;
+ font-size: 0.875rem;
+ line-height: 1.625;
}
.rich-text-composer .tiptap blockquote {
From 2c2fc22c7c746f3542436475d3c79f38b66624b6 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Tue, 16 Jun 2026 17:14:48 -0700
Subject: [PATCH 17/20] fix(desktop): unify timeline and composer inline chip
styling
- Scope mention, agent, and inline-code chip CSS under `.message-markdown`
so timeline markdown and the TipTap composer render identical chips
- Add `MESSAGE_MARKDOWN_CLASS` to the composer editor and drop the legacy
`mention-highlight` decoration class in favor of shared `mention-chip`
- Restore compact vertical padding with baseline alignment; agent bot icon
is absolutely positioned in a left-padded gutter so it no longer affects
chip height
- Migrate composer typography to Tailwind v4 CSS vars (`--text-sm`,
`--text-xs`, `--leading-relaxed`) and route inline code through
`INLINE_CODE_CHIP_CLASS` with token-based mono sizing
- Update mentions e2e selectors/assertions for the new chip class names
---
.../messages/lib/mentionHighlightExtension.ts | 8 +-
.../messages/lib/useRichTextEditor.ts | 5 +-
desktop/src/shared/styles/globals.css | 96 +++++++++++--------
desktop/src/shared/ui/markdown.tsx | 13 +--
desktop/src/shared/ui/mentionChip.ts | 6 ++
desktop/tests/e2e/mentions.spec.ts | 10 +-
6 files changed, 77 insertions(+), 61 deletions(-)
diff --git a/desktop/src/features/messages/lib/mentionHighlightExtension.ts b/desktop/src/features/messages/lib/mentionHighlightExtension.ts
index 63ebfba407f..cee06b6a4a3 100644
--- a/desktop/src/features/messages/lib/mentionHighlightExtension.ts
+++ b/desktop/src/features/messages/lib/mentionHighlightExtension.ts
@@ -5,7 +5,7 @@ import { Decoration, DecorationSet } from "@tiptap/pm/view";
export const mentionHighlightKey = new PluginKey("mentionHighlight");
/**
- * TipTap extension that applies inline `mention-highlight` decorations
+ * TipTap extension that applies inline `mention-chip` decorations
* to `@Name` and `#channel-name` patterns in the document.
*
* Accepts `names` (display names) and `channelNames` storage options.
@@ -264,14 +264,14 @@ function buildDecorations(
node.text,
pos,
mentionPatterns,
- "mention-highlight mention-chip",
+ "mention-chip",
);
addMatchesForPatterns(
decorations,
node.text,
pos,
agentMentionPatterns,
- "mention-highlight mention-chip agent-mention-highlight",
+ "mention-chip agent-mention-highlight",
{ hideMentionPrefix: true },
);
addMatchesForPatterns(
@@ -279,7 +279,7 @@ function buildDecorations(
node.text,
pos,
channelPatterns,
- "mention-highlight mention-chip",
+ "mention-chip",
);
});
diff --git a/desktop/src/features/messages/lib/useRichTextEditor.ts b/desktop/src/features/messages/lib/useRichTextEditor.ts
index 8a0d6e0c0f5..c856fd7a154 100644
--- a/desktop/src/features/messages/lib/useRichTextEditor.ts
+++ b/desktop/src/features/messages/lib/useRichTextEditor.ts
@@ -11,6 +11,8 @@ import { Selection, TextSelection } from "@tiptap/pm/state";
import { isMacPlatform } from "@/shared/lib/platform";
import type { CustomEmoji } from "@/shared/lib/remarkCustomEmoji";
+import { MESSAGE_MARKDOWN_CLASS } from "@/shared/ui/mentionChip";
+
import {
MentionHighlightExtension,
mentionHighlightKey,
@@ -323,8 +325,7 @@ export function useRichTextEditor({
attributes: {
autocapitalize: "none",
autocorrect: "off",
- class:
- "min-h-0 resize-none overflow-y-hidden border-0 bg-transparent px-0 py-0 text-sm leading-relaxed text-foreground shadow-none focus-visible:ring-0 caret-foreground outline-hidden prose-sm max-w-none",
+ class: `${MESSAGE_MARKDOWN_CLASS} min-h-0 resize-none overflow-y-hidden border-0 bg-transparent px-0 py-0 text-sm leading-relaxed text-foreground shadow-none focus-visible:ring-0 caret-foreground outline-hidden max-w-none`,
"data-testid": "message-input",
spellcheck: "false",
},
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index 8e8485fdc39..f35ae0d8393 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -512,9 +512,9 @@
/* ── Tiptap rich-text composer ──────────────────────────────────────── */
.rich-text-composer .tiptap {
outline: none;
- min-height: 1.4375rem; /* one line at text-sm + leading-relaxed */
- font-size: 0.875rem;
- line-height: 1.625;
+ min-height: calc(var(--text-sm) * var(--leading-relaxed));
+ font-size: var(--text-sm);
+ line-height: var(--leading-relaxed);
}
.rich-text-composer .tiptap p {
@@ -555,14 +555,6 @@
text-decoration: line-through;
}
-.rich-text-composer .tiptap code {
- border-radius: calc(var(--radius) - 4px);
- background: hsl(var(--muted));
- padding: 0.125rem 0.375rem;
- font-family: ui-monospace, monospace;
- font-size: 0.875rem;
-}
-
.rich-text-composer .tiptap pre {
border-radius: var(--radius);
border: 1px solid hsl(var(--border) / 0.7);
@@ -576,8 +568,8 @@
background: none;
padding: 0;
border-radius: 0;
- font-size: 0.875rem;
- line-height: 1.625;
+ font-size: var(--text-xs);
+ line-height: var(--text-xs--line-height);
}
.rich-text-composer .tiptap blockquote {
@@ -620,16 +612,37 @@
margin: 0.5rem 0;
}
-.mention-chip,
-.rich-text-composer .tiptap .mention-highlight {
+/* Inline chips — timeline markdown + composer share `.message-markdown` scope */
+.message-markdown .mention-chip,
+.message-markdown .inline-code-chip,
+.message-markdown :not(pre) > code {
+ box-sizing: border-box;
display: inline-block;
border-radius: calc(var(--radius) - 4px);
- background: hsl(var(--primary) / 0.15);
- padding: 0 0.25rem;
+ font-weight: 500;
+ vertical-align: baseline;
+ box-decoration-break: clone;
+ -webkit-box-decoration-break: clone;
+}
+
+.message-markdown .mention-chip {
+ padding: 0.1875rem 0.25rem 0.125rem;
+ line-height: 1;
font-size: inherit;
- line-height: inherit;
+ background: hsl(var(--primary) / 0.15);
color: hsl(var(--primary));
- font-weight: 500;
+}
+
+.message-markdown .inline-code-chip,
+.message-markdown :not(pre) > code {
+ min-height: calc(var(--text-sm) + 0.375rem);
+ padding: 0.1875rem 0.25rem 0.125rem;
+ line-height: 1;
+ font-family: ui-monospace, monospace;
+ background: hsl(var(--muted));
+ color: inherit;
+ /* Mono cap height ≈ sans text-xs on text-sm body */
+ font-size: calc(var(--text-xs) / var(--text-sm) * 1em);
}
.mention-chip-hover {
@@ -645,35 +658,34 @@
color: hsl(var(--primary) / 0.9);
}
-.rich-text-composer .tiptap .agent-mention-at-hidden {
- color: transparent;
- font-size: 0;
- line-height: 0;
-}
-
-.rich-text-composer .tiptap .agent-mention-highlight,
-[data-mention].agent-mention-highlight {
- display: inline-flex;
- align-items: center;
- gap: 2px;
- background: hsl(var(--primary) / 0.18);
- padding-right: 0.375rem;
- color: hsl(var(--primary) / 0.92);
+.message-markdown .agent-mention-at-hidden {
+ display: inline-block;
+ width: 0;
+ max-width: 0;
+ overflow: hidden;
+ opacity: 0;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ vertical-align: baseline;
+ line-height: 1;
}
-[data-mention].agent-mention-highlight {
- transform: translateY(2px);
+.message-markdown .agent-mention-highlight {
+ position: relative;
+ padding-left: calc(0.25rem + 0.95em + 0.125rem);
}
-.rich-text-composer .tiptap .agent-mention-highlight::before,
-[data-mention].agent-mention-highlight::before {
+.message-markdown .agent-mention-highlight::before {
content: "";
- display: inline-block;
- width: 0.9em;
- height: 0.9em;
- flex: 0 0 auto;
+ position: absolute;
+ /* 0.1875rem chip padding-top − 0.0625rem (1px at 16px root) */
+ top: 0.125rem;
+ left: 0.25rem;
+ width: 0.95em;
+ height: 0.95em;
background: currentColor;
- transform: translateY(-0.25px);
+ pointer-events: none;
-webkit-mask:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 8V4H8'/%3E%3Crect width='16' height='12' x='4' y='8' rx='2'/%3E%3Cpath d='M2 14h2'/%3E%3Cpath d='M20 14h2'/%3E%3Cpath d='M15 13v2'/%3E%3Cpath d='M9 13v2'/%3E%3C/svg%3E")
center / contain no-repeat;
diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx
index 171b95e9340..5850dd131aa 100644
--- a/desktop/src/shared/ui/markdown.tsx
+++ b/desktop/src/shared/ui/markdown.tsx
@@ -41,8 +41,10 @@ import remarkMentions from "@/shared/lib/remarkMentions";
import remarkMessageLinks from "@/features/messages/lib/remarkMessageLinks";
import { Button } from "@/shared/ui/button";
import {
+ INLINE_CODE_CHIP_CLASS,
MENTION_CHIP_BASE_CLASSES,
MENTION_CHIP_HOVER_CLASSES,
+ MESSAGE_MARKDOWN_CLASS,
} from "@/shared/ui/mentionChip";
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
@@ -78,7 +80,7 @@ const MAX_CACHE_ENTRIES = 100;
const MAX_LOADED_LANGUAGES = 30;
const MAX_HIGHLIGHT_LINES = 150;
const CODE_BLOCK_CLASS =
- "code-block-lines block min-w-full whitespace-pre font-mono text-sm text-foreground";
+ "code-block-lines block min-w-full whitespace-pre font-mono text-xs text-foreground";
const DIFF_ADD_RE = /\s*\/\/\s*\[!code\s*\+\+\]\s*$/;
const DIFF_REMOVE_RE = /\s*\/\/\s*\[!code\s*--\]\s*$/;
@@ -844,13 +846,7 @@ function createMarkdownComponents(
}
return (
-
+
{children}
);
@@ -1196,6 +1192,7 @@ function MarkdownInner({
return (
*:first-child]:mt-0 [&>*:last-child]:mb-0",
diff --git a/desktop/src/shared/ui/mentionChip.ts b/desktop/src/shared/ui/mentionChip.ts
index e63abb3ed62..f980367dc33 100644
--- a/desktop/src/shared/ui/mentionChip.ts
+++ b/desktop/src/shared/ui/mentionChip.ts
@@ -1,3 +1,9 @@
export const MENTION_CHIP_BASE_CLASSES = "mention-chip";
export const MENTION_CHIP_HOVER_CLASSES = "mention-chip-hover";
+
+/** Wrapper on rendered message Markdown — scopes inline chip CSS. */
+export const MESSAGE_MARKDOWN_CLASS = "message-markdown";
+
+/** Inline `` `code` `` chip — matches mention chip rhythm in message bodies. */
+export const INLINE_CODE_CHIP_CLASS = "inline-code-chip";
diff --git a/desktop/tests/e2e/mentions.spec.ts b/desktop/tests/e2e/mentions.spec.ts
index bf403352add..a1a7f276868 100644
--- a/desktop/tests/e2e/mentions.spec.ts
+++ b/desktop/tests/e2e/mentions.spec.ts
@@ -188,7 +188,7 @@ test("selecting a person mention inserts @Name into input", async ({
await dropdown.getByText("bob").click();
await expect(input).toHaveText("Hey @bob ");
- const mentionChip = input.locator(".mention-highlight", {
+ const mentionChip = input.locator(".mention-chip", {
hasText: "@bob",
});
await expect(mentionChip).toBeVisible();
@@ -214,7 +214,7 @@ test("selecting an agent mention inserts @Name into input", async ({
});
await expect(agentMentionChip).toBeVisible();
await expect(agentMentionChip).toHaveText("alice");
- await expect(agentMentionChip).toHaveCSS("display", "inline-flex");
+ await expect(agentMentionChip).toHaveCSS("display", "inline-block");
await expect(agentMentionChip).toHaveCSS("border-top-width", "0px");
});
@@ -406,7 +406,7 @@ test("profile-only agents without public respond-to are hidden from mentions", a
const dropdown = autocomplete(page);
await expect(dropdown).not.toBeVisible();
- await expect(input.locator(".mention-highlight")).toHaveCount(0);
+ await expect(input.locator(".mention-chip")).toHaveCount(0);
});
test("mentioning an in-channel stopped managed agent starts it before sending", async ({
@@ -703,11 +703,11 @@ test("selecting a non-member agent from a DM inserts @Name into input", async ({
const dropdown = autocomplete(page);
await expect(dropdown.getByText("charlie")).toBeVisible();
await expect(autocomplete(page)).toHaveCount(1);
- await expect(input.locator(".mention-highlight")).toHaveCount(0);
+ await expect(input.locator(".mention-chip")).toHaveCount(0);
await input.press("Enter");
await expect(input).toHaveText("@charlie ");
- await expect(input.locator(".mention-highlight")).toBeVisible();
+ await expect(input.locator(".mention-chip")).toBeVisible();
});
test("do nothing sends a non-member mention without inviting", async ({
From e07b1efdaf0cfbacfbb5b64b60ce2767e1163458 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Tue, 16 Jun 2026 17:24:07 -0700
Subject: [PATCH 18/20] fix(desktop): tokenize inline chips and restore code
block scale
- Replace ad-hoc inline-code sizing with `.message-markdown` CSS vars: one
shared chip box (padding + min-height), text-xs labels for inline mono,
and named agent-icon offsets
- Use inline-flex centering so smaller code labels sit in the same box as
mention chips without em-ratio hacks
- Bump fenced code blocks and composer pre code to text-sm font-medium (500)
so block code matches body weight while inline code stays one step down
- Update mentions e2e to expect inline-flex agent chips
---
desktop/src/shared/styles/globals.css | 52 ++++++++++++++++++---------
desktop/src/shared/ui/markdown.tsx | 2 +-
desktop/tests/e2e/mentions.spec.ts | 2 +-
3 files changed, 37 insertions(+), 19 deletions(-)
diff --git a/desktop/src/shared/styles/globals.css b/desktop/src/shared/styles/globals.css
index f35ae0d8393..44047e416a4 100644
--- a/desktop/src/shared/styles/globals.css
+++ b/desktop/src/shared/styles/globals.css
@@ -568,8 +568,9 @@
background: none;
padding: 0;
border-radius: 0;
- font-size: var(--text-xs);
- line-height: var(--text-xs--line-height);
+ font-size: var(--text-sm);
+ line-height: var(--text-sm--line-height);
+ font-weight: 500;
}
.rich-text-composer .tiptap blockquote {
@@ -612,22 +613,41 @@
margin: 0.5rem 0;
}
-/* Inline chips — timeline markdown + composer share `.message-markdown` scope */
+/* Inline chips — timeline markdown + composer share `.message-markdown` scope.
+ Body copy is text-sm; inline code labels sit one step down (text-xs).
+ All chip variants share one box height so mono labels stay balanced. */
+.message-markdown {
+ --inline-chip-padding-block-start: 0.1875rem;
+ --inline-chip-padding-block-end: 0.125rem;
+ --inline-chip-padding-inline: 0.25rem;
+ --inline-chip-min-height: calc(
+ var(--text-sm) + var(--inline-chip-padding-block-start) +
+ var(--inline-chip-padding-block-end)
+ );
+ --inline-code-font-size: var(--text-xs);
+ --agent-icon-size: 0.95em;
+ --agent-icon-gap: 0.125rem;
+ --agent-icon-top: 0.125rem;
+}
+
.message-markdown .mention-chip,
.message-markdown .inline-code-chip,
.message-markdown :not(pre) > code {
box-sizing: border-box;
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
+ min-height: var(--inline-chip-min-height);
+ padding: var(--inline-chip-padding-block-start)
+ var(--inline-chip-padding-inline) var(--inline-chip-padding-block-end);
border-radius: calc(var(--radius) - 4px);
font-weight: 500;
+ line-height: 1;
vertical-align: baseline;
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
.message-markdown .mention-chip {
- padding: 0.1875rem 0.25rem 0.125rem;
- line-height: 1;
font-size: inherit;
background: hsl(var(--primary) / 0.15);
color: hsl(var(--primary));
@@ -635,14 +655,10 @@
.message-markdown .inline-code-chip,
.message-markdown :not(pre) > code {
- min-height: calc(var(--text-sm) + 0.375rem);
- padding: 0.1875rem 0.25rem 0.125rem;
- line-height: 1;
+ font-size: var(--inline-code-font-size);
font-family: ui-monospace, monospace;
background: hsl(var(--muted));
color: inherit;
- /* Mono cap height ≈ sans text-xs on text-sm body */
- font-size: calc(var(--text-xs) / var(--text-sm) * 1em);
}
.mention-chip-hover {
@@ -673,17 +689,19 @@
.message-markdown .agent-mention-highlight {
position: relative;
- padding-left: calc(0.25rem + 0.95em + 0.125rem);
+ padding-left: calc(
+ var(--inline-chip-padding-inline) + var(--agent-icon-size) +
+ var(--agent-icon-gap)
+ );
}
.message-markdown .agent-mention-highlight::before {
content: "";
position: absolute;
- /* 0.1875rem chip padding-top − 0.0625rem (1px at 16px root) */
- top: 0.125rem;
- left: 0.25rem;
- width: 0.95em;
- height: 0.95em;
+ top: var(--agent-icon-top);
+ left: var(--inline-chip-padding-inline);
+ width: var(--agent-icon-size);
+ height: var(--agent-icon-size);
background: currentColor;
pointer-events: none;
-webkit-mask:
diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx
index 5850dd131aa..072abd8b0bc 100644
--- a/desktop/src/shared/ui/markdown.tsx
+++ b/desktop/src/shared/ui/markdown.tsx
@@ -80,7 +80,7 @@ const MAX_CACHE_ENTRIES = 100;
const MAX_LOADED_LANGUAGES = 30;
const MAX_HIGHLIGHT_LINES = 150;
const CODE_BLOCK_CLASS =
- "code-block-lines block min-w-full whitespace-pre font-mono text-xs text-foreground";
+ "code-block-lines block min-w-full whitespace-pre font-mono text-sm font-medium text-foreground";
const DIFF_ADD_RE = /\s*\/\/\s*\[!code\s*\+\+\]\s*$/;
const DIFF_REMOVE_RE = /\s*\/\/\s*\[!code\s*--\]\s*$/;
diff --git a/desktop/tests/e2e/mentions.spec.ts b/desktop/tests/e2e/mentions.spec.ts
index a1a7f276868..51d976db1d3 100644
--- a/desktop/tests/e2e/mentions.spec.ts
+++ b/desktop/tests/e2e/mentions.spec.ts
@@ -214,7 +214,7 @@ test("selecting an agent mention inserts @Name into input", async ({
});
await expect(agentMentionChip).toBeVisible();
await expect(agentMentionChip).toHaveText("alice");
- await expect(agentMentionChip).toHaveCSS("display", "inline-block");
+ await expect(agentMentionChip).toHaveCSS("display", "inline-flex");
await expect(agentMentionChip).toHaveCSS("border-top-width", "0px");
});
From bba9ce3c782f05bebd340b9e77b4e5d057c1f059 Mon Sep 17 00:00:00 2001
From: Taylor Ho
Date: Tue, 16 Jun 2026 21:25:41 -0700
Subject: [PATCH 19/20] fix(desktop): tighten markdown block spacing
- Add explicit markdown renderers for h4, h5, and h6 so all heading levels use app-controlled typography instead of browser defaults
- Reduce adjacent paragraph spacing to mt-1.5 while preserving larger spacing around other block types
- Extend heading spacing rules across h1-h6 and tighten consecutive heading stacks for cleaner markdown previews
---
desktop/src/shared/ui/markdown.tsx | 21 +++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/desktop/src/shared/ui/markdown.tsx b/desktop/src/shared/ui/markdown.tsx
index b66642e3adc..c2c06a2581e 100644
--- a/desktop/src/shared/ui/markdown.tsx
+++ b/desktop/src/shared/ui/markdown.tsx
@@ -1088,6 +1088,21 @@ function createMarkdownComponents(
{children}
),
+ h4: ({ children }) => (
+
+ {children}
+
+ ),
+ h5: ({ children }) => (
+
+ {children}
+
+ ),
+ h6: ({ children }) => (
+
+ {children}
+
+ ),
hr: () => ,
img: ({ alt, src }) => {
const { imetaByUrl } = runtimeRef.current;
@@ -1420,8 +1435,10 @@ function MarkdownInner({
"max-w-none break-words text-sm leading-relaxed text-foreground",
"[&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
"[&>*+*]:mt-3",
- "[&>*+h1]:mt-3.5 [&>*+h2]:mt-3.5 [&>*+h3]:mt-3.5",
- "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5",
+ "[&>p+p]:mt-1.5",
+ "[&>*+h1]:mt-3.5 [&>*+h2]:mt-3.5 [&>*+h3]:mt-3.5 [&>*+h4]:mt-3.5 [&>*+h5]:mt-3.5 [&>*+h6]:mt-3.5",
+ "[&>h1+*]:mt-0.5 [&>h2+*]:mt-0.5 [&>h3+*]:mt-0.5 [&>h4+*]:mt-0.5 [&>h5+*]:mt-0.5 [&>h6+*]:mt-0.5",
+ "[&>h1+h2]:mt-1.5! [&>h2+h3]:mt-1.5! [&>h3+h4]:mt-1.5! [&>h4+h5]:mt-1.5! [&>h5+h6]:mt-1.5!",
"[&>*+blockquote]:mt-3.5 [&>blockquote+*]:mt-3.5",
"[&>*+[data-code-block]]:mt-3.5 [&>[data-code-block]+*]:mt-3.5",
"[&>*+[data-table-block]]:mt-3.5 [&>[data-table-block]+*]:mt-3.5",
From b4fc1bdc3d0cc4cd23286bb1a26459a1a78ba0e9 Mon Sep 17 00:00:00 2001
From: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w
<52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@sprout-oss.stage.blox.sqprod.co>
Date: Tue, 16 Jun 2026 21:55:18 -0700
Subject: [PATCH 20/20] Restore agent-mention styling in SystemMessageRow after
merge
The main merge accidentally dropped main's agent-aware rendering from
SystemMessageRow.tsx, breaking the mentions.spec.ts smoke test that
expects managed-agent names in system add/remove rows to render with
[data-mention].agent-mention-highlight.
Combined main's agent-mention logic back in (ProfileName highlight/isAgent
props + mention-chip classes, describeSystemEvent isTargetAgent detection,
agentPubkeys/personaLookup props + pass-through) while keeping the
type-scale tokenizations that landed on this branch (avatar text-2xs swaps
and system-row text-foreground color tightening).
Co-authored-by: Taylor Ho
Signed-off-by: Taylor Ho
---
.../features/messages/ui/SystemMessageRow.tsx | 44 +++++++++++++++++--
1 file changed, 41 insertions(+), 3 deletions(-)
diff --git a/desktop/src/features/messages/ui/SystemMessageRow.tsx b/desktop/src/features/messages/ui/SystemMessageRow.tsx
index 298fb16f3ba..d0ca23b7965 100644
--- a/desktop/src/features/messages/ui/SystemMessageRow.tsx
+++ b/desktop/src/features/messages/ui/SystemMessageRow.tsx
@@ -10,8 +10,13 @@ import type { UserProfileLookup } from "@/features/profile/lib/identity";
import { resolveUserLabel } from "@/features/profile/lib/identity";
import { UserProfilePopover } from "@/features/profile/ui/UserProfilePopover";
import { cn } from "@/shared/lib/cn";
+import { normalizePubkey } from "@/shared/lib/pubkey";
import { Button } from "@/shared/ui/button";
import { isPositiveEmojiParticle } from "@/shared/ui/EmojiBurstProvider";
+import {
+ MENTION_CHIP_BASE_CLASSES,
+ MENTION_CHIP_HOVER_CLASSES,
+} from "@/shared/ui/mentionChip";
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
import { UserAvatar } from "@/shared/ui/UserAvatar";
@@ -62,18 +67,31 @@ function resolveDisplayLabel(
function ProfileName({
children,
+ highlight = false,
+ isAgent = false,
pubkey,
}: {
children: React.ReactNode;
+ highlight?: boolean;
+ isAgent?: boolean;
pubkey: string | undefined;
}) {
+ const isAgentMention = highlight && isAgent;
const node = (
+ {highlight && !isAgentMention ? "@" : null}
{children}
);
@@ -178,7 +196,14 @@ function describeSystemEvent(
payload: SystemMessagePayload,
currentPubkey: string | undefined,
profiles: UserProfileLookup | undefined,
+ personaLookup?: Map,
+ agentPubkeys?: ReadonlySet,
): SystemMessageDescription | null {
+ const isTargetAgent =
+ payload.target !== undefined &&
+ (agentPubkeys?.has(normalizePubkey(payload.target)) === true ||
+ profiles?.[normalizePubkey(payload.target)]?.isAgent === true ||
+ personaLookup?.has(normalizePubkey(payload.target)) === true);
const actorLabel = resolveDisplayLabel(
payload.actor,
currentPubkey,
@@ -193,7 +218,9 @@ function describeSystemEvent(
{actorLabel}
);
const targetName = (
- {targetLabel}
+
+ {targetLabel}
+
);
switch (payload.type) {
@@ -252,12 +279,17 @@ function describeSystemEvent(
export const SystemMessageRow = React.memo(function SystemMessageRow({
message,
currentPubkey,
+ agentPubkeys,
profiles,
+ personaLookup,
onToggleReaction,
}: {
message: TimelineMessage;
currentPubkey?: string;
+ agentPubkeys?: ReadonlySet;
profiles?: UserProfileLookup;
+ /** Map from lowercase pubkey → persona display name for bot members. */
+ personaLookup?: Map;
onToggleReaction?: (
message: TimelineMessage,
emoji: string,
@@ -283,7 +315,13 @@ export const SystemMessageRow = React.memo(function SystemMessageRow({
return null;
}
- const description = describeSystemEvent(payload, currentPubkey, profiles);
+ const description = describeSystemEvent(
+ payload,
+ currentPubkey,
+ profiles,
+ personaLookup,
+ agentPubkeys,
+ );
if (!description) {
return null;
}