fix(desktop): refine inbox preview rendering

Render inbox previews with markdown-safe inline references and mark unread items read when opened.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Thomas Petersen
2026-06-18 09:12:03 -04:00
parent df8855fa03
commit b8abaf2f68
3 changed files with 31 additions and 12 deletions

View File

@@ -414,6 +414,7 @@ export function HomeView({
onFilterChange={setFilter}
onSelect={(itemId) => {
handleUserSelectItem(itemId);
markItemRead(itemId);
}}
reminderPubkey={currentPubkey}
selectedId={selectedItemId}

View File

@@ -121,21 +121,18 @@ export function InboxListPane({
</span>
</div>
<div
<Markdown
className={cn(
"mt-0.5 line-clamp-2 text-sm leading-5 **:inline [&_a]:font-medium [&_a]:text-current [&_br]:hidden [&_p]:inline",
"mt-0.5 line-clamp-2 max-w-full text-sm! leading-5! **:inline [&_*]:text-inherit [&_a]:font-medium [&_a]:text-current [&_br]:hidden [&_p]:inline",
isDone
? "font-normal text-muted-foreground"
: "font-semibold text-foreground",
)}
>
<Markdown
className="inline max-w-full text-inherit"
content={item.preview}
interactive={false}
mentionNames={item.mentionNames}
/>
</div>
content={item.preview}
interactive={false}
mentionNames={item.mentionNames}
plainInlineReferences
/>
</div>
</button>
);

View File

@@ -238,6 +238,7 @@ type MarkdownProps = {
customEmoji?: CustomEmoji[];
imetaByUrl?: ImetaLookup;
interactive?: boolean;
plainInlineReferences?: boolean;
agentMentionPubkeysByName?: Record<string, string>;
mentionNames?: string[];
mentionPubkeysByName?: Record<string, string>;
@@ -1678,6 +1679,7 @@ function SpoilerInline({
function createMarkdownComponents(
runtimeRef: React.RefObject<MarkdownRuntime>,
interactive = true,
plainInlineReferences = false,
): Components {
const paragraphClassName = "leading-[inherit]";
const listItemClassName = "my-1 [&_p]:inline";
@@ -1799,6 +1801,14 @@ function createMarkdownComponents(
);
}
if (plainInlineReferences) {
return (
<code {...props} className="font-mono text-inherit">
{children}
</code>
);
}
return (
<code {...props} className={cn(INLINE_CODE_CHIP_CLASS, className)}>
{children}
@@ -1937,6 +1947,10 @@ function createMarkdownComponents(
const { agentMentionPubkeysByName, mentionPubkeysByName } =
runtimeRef.current;
const mentionText = String(children ?? "");
if (plainInlineReferences) {
return <span>{mentionText}</span>;
}
const mentionName = mentionText.replace(/^@/, "").trim().toLowerCase();
const pubkey = mentionPubkeysByName?.[mentionName];
const isAgentMention =
@@ -1988,6 +2002,10 @@ function createMarkdownComponents(
return <InlineEmojiPopover alt={alt} resolvedSrc={resolvedSrc} />;
},
"channel-link": ({ children }: { children?: React.ReactNode }) => {
if (plainInlineReferences) {
return <span>{children}</span>;
}
const { channels, onOpenChannel } = runtimeRef.current;
const text = String(children ?? "");
const channelName = text.startsWith("#") ? text.slice(1) : text;
@@ -2075,6 +2093,7 @@ function MarkdownInner({
customEmoji,
imetaByUrl,
interactive = true,
plainInlineReferences = false,
agentMentionPubkeysByName,
mentionNames,
mentionPubkeysByName,
@@ -2116,8 +2135,9 @@ function MarkdownInner({
});
const components = React.useMemo(
() => createMarkdownComponents(runtimeRef, interactive),
[runtimeRef, interactive],
() =>
createMarkdownComponents(runtimeRef, interactive, plainInlineReferences),
[runtimeRef, interactive, plainInlineReferences],
);
// biome-ignore lint/suspicious/noExplicitAny: PluggableList type not directly importable
@@ -2200,6 +2220,7 @@ export const Markdown = React.memo(
prev.className === next.className &&
prev.customEmoji === next.customEmoji &&
prev.interactive === next.interactive &&
prev.plainInlineReferences === next.plainInlineReferences &&
prev.agentMentionPubkeysByName === next.agentMentionPubkeysByName &&
prev.mentionPubkeysByName === next.mentionPubkeysByName &&
shallowArrayEqual(prev.mentionNames, next.mentionNames) &&