summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2025-07-03 14:23:49 +0300
committerDawid Rycerz <dawid@rycerz.xyz>2025-07-03 14:23:49 +0300
commite28e2c961659f48177cf8ce39eeb39480b221535 (patch)
tree1138c3de714524db5eadab19df528402f42d8f67
parent4b9018b6d92ef8f1854d9dc44625295c2acd3fb3 (diff)
Fix markdown rendering of pleroma posts
-rw-r--r--package.json1
-rw-r--r--pnpm-lock.yaml10
-rw-r--r--src/components/note/Note.astro17
-rw-r--r--src/loaders/pleroma.ts27
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),
},
});