Standard.site landing page built in Next.js

Add OpenGraph metadata and atUri with pdsls.dev link to docs pages

aka.dad 35c878e5 a88514bf

verified
+40 -10
+13 -4
app/components/docs/DocsMarkdownContext.tsx
··· 2 2 3 3 import { createContext, useContext } from 'react' 4 4 5 - const DocsMarkdownContext = createContext<string | null>(null) 5 + interface DocsPageContext { 6 + markdown: string | null 7 + atUri: string | null 8 + } 9 + 10 + const DocsMarkdownContext = createContext<DocsPageContext>({ markdown: null, atUri: null }) 6 11 7 - export function DocsMarkdownProvider({ markdown, children }: { markdown: string; children: React.ReactNode }) { 12 + export function DocsMarkdownProvider({ markdown, atUri, children }: { markdown: string; atUri?: string; children: React.ReactNode }) { 8 13 return ( 9 - <DocsMarkdownContext.Provider value={markdown}> 14 + <DocsMarkdownContext.Provider value={{ markdown, atUri: atUri ?? null }}> 10 15 {children} 11 16 </DocsMarkdownContext.Provider> 12 17 ) 13 18 } 14 19 15 20 export function useDocsMarkdown() { 16 - return useContext(DocsMarkdownContext) 21 + return useContext(DocsMarkdownContext).markdown 22 + } 23 + 24 + export function useDocsAtUri() { 25 + return useContext(DocsMarkdownContext).atUri 17 26 }
+16 -1
app/components/docs/DocsPageMenu.tsx
··· 2 2 3 3 import { useState, useRef, useEffect } from 'react' 4 4 import { usePathname } from 'next/navigation' 5 - import { useDocsMarkdown } from './DocsMarkdownContext' 5 + import { useDocsMarkdown, useDocsAtUri } from './DocsMarkdownContext' 6 6 7 7 export function DocsPageMenu() { 8 8 const [open, setOpen] = useState(false) ··· 10 10 const menuRef = useRef<HTMLDivElement>(null) 11 11 const pathname = usePathname() 12 12 const markdown = useDocsMarkdown() 13 + const atUri = useDocsAtUri() 13 14 14 15 const slug = (() => { 15 16 const path = pathname.split(/[?#]/)[0] ··· 100 101 </svg> 101 102 View raw Markdown 102 103 </button> 104 + {atUri && ( 105 + <a 106 + href={`https://pdsls.dev/${atUri}`} 107 + target="_blank" 108 + rel="noopener noreferrer" 109 + onClick={() => setOpen(false)} 110 + className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted hover:text-base-content hover:bg-base-300 rounded-lg transition-colors" 111 + > 112 + <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 113 + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> 114 + </svg> 115 + View on pdsls.dev 116 + </a> 117 + )} 103 118 </div> 104 119 )} 105 120 </div>
+11 -5
app/docs/[...slug]/page.tsx
··· 20 20 'faq': () => import('@/content/docs/faq.mdx'), 21 21 } 22 22 23 - async function getFrontmatter(slugPath: string): Promise<{ title?: string; description?: string }> { 23 + async function getFrontmatter(slugPath: string): Promise<{ title?: string; description?: string; ogImage?: string; atUri?: string }> { 24 24 const filePath = join(process.cwd(), 'content', 'docs', `${slugPath}.mdx`) 25 25 const raw = await readFile(filePath, 'utf-8') 26 26 ··· 31 31 for (const line of match[1].split('\n')) { 32 32 const [key, ...rest] = line.split(':') 33 33 if (key && rest.length) { 34 - frontmatter[key.trim()] = rest.join(':').trim() 34 + frontmatter[key.trim()] = rest.join(':').trim().replace(/^["']|["']$/g, '') 35 35 } 36 36 } 37 37 ··· 53 53 54 54 if (!docsContent[slugPath]) return {} 55 55 56 - const { title, description } = await getFrontmatter(slugPath) 56 + const { title, description, ogImage } = await getFrontmatter(slugPath) 57 57 58 58 return { 59 59 title: title ? `${title} - Standard.site` : undefined, 60 60 description, 61 + openGraph: { 62 + title: title ? `${title} - Standard.site` : undefined, 63 + description, 64 + images: ogImage ? [`/media/${ogImage}`] : undefined, 65 + }, 61 66 } 62 67 } 63 68 ··· 74 79 notFound() 75 80 } 76 81 77 - const [{ default: Content }, markdown] = await Promise.all([ 82 + const [{ default: Content }, markdown, { atUri }] = await Promise.all([ 78 83 loader(), 79 84 getDocMarkdown(slugPath), 85 + getFrontmatter(slugPath), 80 86 ]) 81 87 82 88 return ( 83 - <DocsMarkdownProvider markdown={markdown}> 89 + <DocsMarkdownProvider markdown={markdown} atUri={atUri}> 84 90 <Content /> 85 91 </DocsMarkdownProvider> 86 92 )