diff --git a/src/components/dm/DMView.tsx b/src/components/dm/DMView.tsx
index 2a6a917..afc131a 100644
--- a/src/components/dm/DMView.tsx
+++ b/src/components/dm/DMView.tsx
@@ -1,5 +1,6 @@
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
import { NDKEvent, NDKKind, nip19 } from "@nostr-dev-kit/ndk";
+import { openUrl } from "@tauri-apps/plugin-opener";
import { useUserStore } from "../../stores/user";
import { useUIStore } from "../../stores/ui";
import { useNotificationsStore } from "../../stores/notifications";
@@ -7,6 +8,8 @@ import { fetchDMConversations, fetchDMThread, sendDM, decryptDM, getNDK } from "
import { useProfile } from "../../hooks/useProfile";
import { timeAgo, shortenPubkey, profileName } from "../../lib/utils";
import { debug } from "../../lib/debug";
+import { parseContent } from "../../lib/parsing";
+import { tryHandleUrlInternally, tryOpenNostrEntity } from "../feed/TextSegments";
// ── Helpers ──────────────────────────────────────────────────────────────────
@@ -77,6 +80,75 @@ function ConvRow({
);
}
+// ── DM text renderer ─────────────────────────────────────────────────────────
+
+function DMText({ text }: { text: string }) {
+ const segments = useMemo(() => parseContent(text), [text]);
+ return (
+
+ {segments.map((seg, i) => {
+ if (seg.type === "link") {
+ return (
+ {
+ e.preventDefault();
+ if (!tryHandleUrlInternally(seg.value)) openUrl(seg.value).catch(() => {});
+ }}
+ >
+ {seg.display || seg.value}
+
+ );
+ }
+ if (seg.type === "image") {
+ return (
+ { (e.target as HTMLImageElement).style.display = "none"; }}
+ />
+ );
+ }
+ if (seg.type === "naddr" || seg.type === "mention") {
+ return (
+ tryOpenNostrEntity(seg.value)}
+ >
+ {seg.type === "naddr" ? "🔗 " : "@"}{String(seg.display ?? seg.value).slice(0, 20)}…
+
+ );
+ }
+ if (seg.type === "quote") {
+ return (
+
+ ↩ quoted note
+
+ );
+ }
+ // video/audio/youtube/etc. — show URL as a plain link
+ if (["video", "audio", "youtube", "vimeo", "spotify", "tidal", "fountain"].includes(seg.type)) {
+ return (
+ { e.preventDefault(); openUrl(seg.value).catch(() => {}); }}
+ >
+ {seg.value}
+
+ );
+ }
+ return {seg.value};
+ })}
+
+ );
+}
+
// ── Message bubble ────────────────────────────────────────────────────────────
function MessageBubble({ event, myPubkey }: { event: NDKEvent; myPubkey: string }) {
@@ -106,7 +178,7 @@ function MessageBubble({ event, myPubkey }: { event: NDKEvent; myPubkey: string
) : text === null ? (
…
) : (
- text
+