diff --git a/apps/web/components/new/document-cards/note-preview.tsx b/apps/web/components/new/document-cards/note-preview.tsx
index 14a77d34..ef4a0030 100644
--- a/apps/web/components/new/document-cards/note-preview.tsx
+++ b/apps/web/components/new/document-cards/note-preview.tsx
@@ -79,18 +79,28 @@ function NoteIcon() {
export function NotePreview({ document }: { document: DocumentWithMemories }) {
return (
-
+
+
+
+ {
+ document.title && (
+
+ {document.title}
+
+ )
+ }
{document.content && (
{document.content}
)}
+
)
}
diff --git a/apps/web/components/new/document-cards/youtube-preview.tsx b/apps/web/components/new/document-cards/youtube-preview.tsx
new file mode 100644
index 00000000..698a8faf
--- /dev/null
+++ b/apps/web/components/new/document-cards/youtube-preview.tsx
@@ -0,0 +1,49 @@
+"use client"
+
+import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
+import type { z } from "zod"
+import { dmSansClassName } from "@/utils/fonts"
+import { cn } from "@lib/utils"
+import { extractYouTubeVideoId } from "./youtube-utils"
+
+type DocumentsResponse = z.infer
+type DocumentWithMemories = DocumentsResponse["documents"][0]
+
+export function YoutubePreview({ document }: { document: DocumentWithMemories }) {
+ const videoId = extractYouTubeVideoId(document.url)
+
+ if (!videoId) {
+ return (
+
+ {document.title && (
+
+ {document.title}
+
+ )}
+ {document.content && (
+
+ {document.content}
+
+ )}
+
+ )
+ }
+
+ const embedUrl = `https://www.youtube.com/embed/${videoId}`
+
+ return (
+
+ )
+}
+
diff --git a/apps/web/components/new/document-cards/youtube-utils.ts b/apps/web/components/new/document-cards/youtube-utils.ts
new file mode 100644
index 00000000..9ac1e508
--- /dev/null
+++ b/apps/web/components/new/document-cards/youtube-utils.ts
@@ -0,0 +1,63 @@
+"use client"
+
+import { useQuery } from "@tanstack/react-query"
+
+export function isYouTubeUrl(url: string | undefined | null): boolean {
+ if (!url) return false
+ return (
+ url.includes("youtube.com") ||
+ url.includes("youtu.be") ||
+ url.includes("m.youtube.com")
+ )
+}
+
+export function extractYouTubeVideoId(url: string | undefined | null): string | null {
+ if (!url) return null
+
+ // Handle youtu.be format
+ const youtuBeMatch = url.match(/(?:youtu\.be\/)([a-zA-Z0-9_-]{11})/)
+ if (youtuBeMatch?.[1]) return youtuBeMatch[1]
+
+ // Handle youtube.com/watch?v= format
+ const watchMatch = url.match(/(?:youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/)
+ if (watchMatch?.[1]) return watchMatch[1]
+
+ // Handle youtube.com/embed/ format
+ const embedMatch = url.match(/(?:youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/)
+ if (embedMatch?.[1]) return embedMatch[1]
+
+ // Handle m.youtube.com format
+ const mobileMatch = url.match(/(?:m\.youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/)
+ if (mobileMatch?.[1]) return mobileMatch[1]
+
+ return null
+}
+
+export function useYouTubeChannelName(url: string | undefined | null) {
+ const videoId = extractYouTubeVideoId(url)
+ const videoUrl = videoId
+ ? `https://www.youtube.com/watch?v=${videoId}`
+ : url || ""
+
+ return useQuery({
+ queryKey: ["youtube-channel", videoUrl],
+ queryFn: async () => {
+ if (!videoUrl) return null
+
+ try {
+ const response = await fetch(
+ `https://www.youtube.com/oembed?url=${encodeURIComponent(videoUrl)}&format=json`,
+ )
+ if (!response.ok) return null
+ const data = (await response.json()) as { author_name?: string }
+ return data.author_name || null
+ } catch {
+ return null
+ }
+ },
+ enabled: !!videoUrl && isYouTubeUrl(url),
+ staleTime: 1000 * 60 * 60 * 24,
+ retry: 1,
+ })
+}
+
diff --git a/apps/web/components/new/memories-grid.tsx b/apps/web/components/new/memories-grid.tsx
index 9b3e9c2c..74116e4f 100644
--- a/apps/web/components/new/memories-grid.tsx
+++ b/apps/web/components/new/memories-grid.tsx
@@ -19,6 +19,11 @@ import { WebsitePreview } from "./document-cards/website-preview"
import { GoogleDocsPreview } from "./document-cards/google-docs-preview"
import { FilePreview } from "./document-cards/file-preview"
import { NotePreview } from "./document-cards/note-preview"
+import { YoutubePreview } from "./document-cards/youtube-preview"
+import {
+ isYouTubeUrl,
+ useYouTubeChannelName,
+} from "./document-cards/youtube-utils"
type DocumentsResponse = z.infer
type DocumentWithMemories = DocumentsResponse["documents"][0]
@@ -193,6 +198,37 @@ export function MemoriesGrid() {
)
}
+function DocumentUrlDisplay({ url }: { url: string }) {
+ const isYouTube = isYouTubeUrl(url)
+ const { data: channelName, isLoading } = useYouTubeChannelName(
+ isYouTube ? url : null,
+ )
+
+ if (isYouTube) {
+ return (
+
+ {isLoading ? "YouTube" : channelName || "YouTube"}
+
+ )
+ }
+
+ return (
+
+ {getAbsoluteUrl(url)}
+
+ )
+}
+
const DocumentCard = memo(
({
index: _index,
@@ -207,6 +243,7 @@ const DocumentCard = memo(
+ {document.url && !document.url.includes("x.com") && !document.url.includes("twitter.com") && !document.url.includes("files.supermemory.ai") && (
{document.title}
- {document.url && (
-
- {getAbsoluteUrl(document.url)}
-
- )}
+
+
+ )}
+ }
+
if (
document.type === "pdf" ||
document.type === "image" ||