diff options
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 10 | ||||
| -rw-r--r-- | src/components/note/Note.astro | 17 | ||||
| -rw-r--r-- | src/loaders/pleroma.ts | 27 |
4 files changed, 53 insertions, 2 deletions
diff --git a/package.json b/package.json index e0fc731..6bd0020 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "cssnano": "^7.0.7", "fast-xml-parser": "^5.2.5", "hastscript": "^9.0.0", + "marked": "^16.0.0", "mdast-util-directive": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "mdast-util-to-string": "^4.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10d65ea..90166a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: hastscript: specifier: ^9.0.0 version: 9.0.1 + marked: + specifier: ^16.0.0 + version: 16.0.0 mdast-util-directive: specifier: ^3.0.0 version: 3.1.0 @@ -2017,6 +2020,11 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@16.0.0: + resolution: {integrity: sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA==} + engines: {node: '>= 20'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -5416,6 +5424,8 @@ snapshots: markdown-table@3.0.4: {} + marked@16.0.0: {} + math-intrinsics@1.1.0: {} mdast-util-definitions@6.0.0: diff --git a/src/components/note/Note.astro b/src/components/note/Note.astro index 54d1fc0..a8cf205 100644 --- a/src/components/note/Note.astro +++ b/src/components/note/Note.astro @@ -44,5 +44,22 @@ const { Content } = await render(note); class:list={{ "line-clamp-6": isPreview }} > <Content /> + { + !isPreview && note.data.sourceUrl && ( + <> + <hr class="mt-6 mb-4 border-t border-gray-300 dark:border-gray-600" /> + <p class="text-sm text-gray-600 dark:text-gray-400"> + <a + href={note.data.sourceUrl} + class="cactus-link" + target="_blank" + rel="noopener noreferrer" + > + View original post → + </a> + </p> + </> + ) + } </div> </article> diff --git a/src/loaders/pleroma.ts b/src/loaders/pleroma.ts index dc6a05c..a0b169f 100644 --- a/src/loaders/pleroma.ts +++ b/src/loaders/pleroma.ts @@ -1,6 +1,7 @@ import type { Loader } from "astro/loaders"; import { XMLParser } from "fast-xml-parser"; import TurndownService from "turndown"; +import { marked } from "marked"; interface PleromaFeedConfig { instanceUrl: string; @@ -192,6 +193,20 @@ function cleanContent(htmlContent: string): string { return markdown.trim().replace(/\n\s*\n\s*\n/g, "\n\n"); } +function markdownToHtml(markdown: string): string { + // Configure marked options for safe rendering + marked.setOptions({ + breaks: true, // Convert line breaks to <br> + gfm: true, // GitHub flavored markdown + }); + + // Convert markdown to HTML + const html = marked.parse(markdown); + + // Return as string (marked.parse can return string or Promise<string>) + return typeof html === 'string' ? html : ''; +} + function extractTitle(content: string): string { // Extract first line or first sentence as title const firstLine = content.split("\n")[0]; @@ -296,6 +311,9 @@ export function pleromaLoader(config: PleromaFeedConfig): Loader { // Extract post ID from the entry ID const postId = entry.id.split("/").pop() || entry.id; + // Extract source URL from the entry + const sourceUrl = entry.link?.find(link => link["@_rel"] === "alternate")?.["@_href"] || entry.id; + // Create note entry store.set({ id: `pleroma-${postId}`, @@ -304,10 +322,11 @@ export function pleromaLoader(config: PleromaFeedConfig): Loader { description: cleanedContent.substring(0, 160) + (cleanedContent.length > 160 ? "..." : ""), publishDate: new Date(entry.published), + sourceUrl, }, body: cleanedContent, rendered: { - html: `<p>${cleanedContent.replace(/\n\n/g, "</p><p>")}</p>`, + html: markdownToHtml(cleanedContent), }, }); @@ -341,6 +360,9 @@ export function pleromaLoader(config: PleromaFeedConfig): Loader { (typeof item.link === "string" ? item.link.split("/").pop() : null) || Math.random().toString(36); + // Use the link as source URL + const sourceUrl = typeof item.link === "string" ? item.link : item.guid || ""; + // Create note entry store.set({ id: `pleroma-${postId}`, @@ -349,10 +371,11 @@ export function pleromaLoader(config: PleromaFeedConfig): Loader { description: cleanedContent.substring(0, 160) + (cleanedContent.length > 160 ? "..." : ""), publishDate: new Date(item.pubDate), + sourceUrl, }, body: cleanedContent, rendered: { - html: `<p>${cleanedContent.replace(/\n\n/g, "</p><p>")}</p>`, + html: markdownToHtml(cleanedContent), }, }); |
