diff --git a/src/lib/utils/markdown/basicMarkdownParser.ts b/src/lib/utils/markdown/basicMarkdownParser.ts
index c06e840..68548ca 100644
--- a/src/lib/utils/markdown/basicMarkdownParser.ts
+++ b/src/lib/utils/markdown/basicMarkdownParser.ts
@@ -26,8 +26,41 @@ const VIDEO_URL_REGEX = /https?:\/\/[^\s<]+\.(?:mp4|webm|mov|avi)(?:[^\s<]*)?/i;
const AUDIO_URL_REGEX = /https?:\/\/[^\s<]+\.(?:mp3|wav|ogg|m4a)(?:[^\s<]*)?/i;
const YOUTUBE_URL_REGEX = /https?:\/\/(?:www\.)?(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/|youtube-nocookie\.com\/embed\/)([a-zA-Z0-9_-]{11})(?:[^\s<]*)?/i;
-// Emoji shortcut pattern
-const EMOJI_SHORTCUT_REGEX = /:([a-zA-Z0-9_+-]+):/g;
+// Utility to strip tracking parameters from URLs
+function stripTrackingParams(url: string): string {
+ // List of tracking params to remove
+ const trackingParams = [/^utm_/i, /^fbclid$/i, /^gclid$/i, /^tracking$/i, /^ref$/i];
+ try {
+ // Absolute URL
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) {
+ const parsed = new URL(url);
+ trackingParams.forEach(pattern => {
+ for (const key of Array.from(parsed.searchParams.keys())) {
+ if (pattern.test(key)) {
+ parsed.searchParams.delete(key);
+ }
+ }
+ });
+ parsed.search = parsed.searchParams.toString();
+ return parsed.origin + parsed.pathname + (parsed.search ? '?' + parsed.search : '') + (parsed.hash || '');
+ } else {
+ // Relative URL: parse query string manually
+ const [path, queryAndHash = ''] = url.split('?');
+ const [query = '', hash = ''] = queryAndHash.split('#');
+ if (!query) return url;
+ const params = query.split('&').filter(Boolean);
+ const filtered = params.filter(param => {
+ const [key] = param.split('=');
+ return !trackingParams.some(pattern => pattern.test(key));
+ });
+ const queryString = filtered.length ? '?' + filtered.join('&') : '';
+ const hashString = hash ? '#' + hash : '';
+ return path + queryString + hashString;
+ }
+ } catch {
+ return url;
+ }
+}
function processBasicFormatting(content: string): string {
if (!content) return '';
@@ -37,27 +70,30 @@ function processBasicFormatting(content: string): string {
try {
// Process Markdown images first
processedText = processedText.replace(MARKDOWN_IMAGE, (match, alt, url) => {
+ url = stripTrackingParams(url);
if (YOUTUBE_URL_REGEX.test(url)) {
const videoId = extractYouTubeVideoId(url);
if (videoId) {
return ``;
}
}
-
if (VIDEO_URL_REGEX.test(url)) {
return ``;
}
-
if (AUDIO_URL_REGEX.test(url)) {
return ``;
}
-
- return ``;
+ // Only render
if the url ends with a direct image extension
+ if (/\.(jpg|jpeg|gif|png|webp|svg)$/i.test(url.split('?')[0])) {
+ return `
`;
+ }
+ // Otherwise, render as a clickable link
+ return `${alt || url}`;
});
// Process Markdown links
processedText = processedText.replace(MARKDOWN_LINK, (match, text, url) =>
- `${text}`
+ `${text}`
);
// Process WebSocket URLs
@@ -67,28 +103,27 @@ function processBasicFormatting(content: string): string {
return `${match}`;
});
- // Process direct media URLs
+ // Process direct media URLs and auto-link all URLs
processedText = processedText.replace(DIRECT_LINK, match => {
- if (YOUTUBE_URL_REGEX.test(match)) {
- const videoId = extractYouTubeVideoId(match);
+ const clean = stripTrackingParams(match);
+ if (YOUTUBE_URL_REGEX.test(clean)) {
+ const videoId = extractYouTubeVideoId(clean);
if (videoId) {
return ``;
}
}
-
- if (VIDEO_URL_REGEX.test(match)) {
- return ``;
+ if (VIDEO_URL_REGEX.test(clean)) {
+ return ``;
}
-
- if (AUDIO_URL_REGEX.test(match)) {
- return ``;
+ if (AUDIO_URL_REGEX.test(clean)) {
+ return ``;
}
-
- if (IMAGE_URL_REGEX.test(match)) {
- return `
`;
+ // Only render
if the url ends with a direct image extension
+ if (/\.(jpg|jpeg|gif|png|webp|svg)$/i.test(clean.split('?')[0])) {
+ return `
`;
}
-
- return `${match}`;
+ // Otherwise, render as a clickable link
+ return `${clean}`;
});
// Process text formatting
diff --git a/src/lib/utils/markdown/markdownTestfile.md b/src/lib/utils/markdown/markdownTestfile.md
index 7ceb3b8..08f1d71 100644
--- a/src/lib/utils/markdown/markdownTestfile.md
+++ b/src/lib/utils/markdown/markdownTestfile.md
@@ -7,7 +7,7 @@ It is _only_ a test, for __sure__. I just wanted to see if the markdown renders
This file is full of ~errors~ opportunities to ~~mess up the formatting~~ check your markdown parser.
-npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z wrote this. That's the same person as nostr:npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z and nprofile1qydhwumn8ghj7argv4nx7un9wd6zumn0wd68yvfwvdhk6tcpr3mhxue69uhkx6rjd9ehgurfd3kzumn0wd68yvfwvdhk6tcqyr7jprhgeregx7q2j4fgjmjgy0xfm34l63pqvwyf2acsd9q0mynuzp4qva3. That is a different person from npub1s3ht77dq4zqnya8vjun5jp3p44pr794ru36d0ltxu65chljw8xjqd975wz.
+npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z wrote this. That's the same person as this one with a nostr prefix nostr:npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z and nprofile1qydhwumn8ghj7argv4nx7un9wd6zumn0wd68yvfwvdhk6tcpr3mhxue69uhkx6rjd9ehgurfd3kzumn0wd68yvfwvdhk6tcqyr7jprhgeregx7q2j4fgjmjgy0xfm34l63pqvwyf2acsd9q0mynuzp4qva3. That is a different person from npub1s3ht77dq4zqnya8vjun5jp3p44pr794ru36d0ltxu65chljw8xjqd975wz.
> This is important information
@@ -62,7 +62,7 @@ Try embedded a nostr note with nevent:
nostr:nevent1qvzqqqqqqypzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqydhwumn8ghj7argv4nx7un9wd6zumn0wd68yvfwvdhk6tcpr3mhxue69uhkx6rjd9ehgurfd3kzumn0wd68yvfwvdhk6tcqyrzdyycehfwyekef75z5wnnygqeps6a4qvc8dunvumzr08g06svgcptkske
-Here with note:
+Here a note with no prefix
note1cnfpxxd6t3xdk204q4r5uezqxgvxhdgrxpm0ym8xcsme6r75rzxqcj9lmz
@@ -74,12 +74,22 @@ Here's a nonsense one:
nevent123
+And a nonsense one with a prefix:
+
+nostr:naddrwhatever
+
And some Nostr addresses that should be ignored:
https://lumina.rocks/note/note1sd0hkhxr49jsetkcrjkvf2uls5m8frkue6f5huj8uv4964p2d8fs8dn68z
https://primal.net/e/nevent1qqsqum7j25p9z8vcyn93dsd7edx34w07eqav50qnde3vrfs466q558gdd02yr
+URL with a tracking parameter, no Markdown:
+https://example.com?utm_source=newsletter1&utm_medium=email&utm_campaign=sale
+
+Image without Markdown:
+https://upload.wikimedia.org/wikipedia/commons/f/f1/Heart_coraz%C3%B3n.svg
+
This is an implementation of [Nostr-flavored Markdown](https://github.com/nostrability/nostrability/issues/146) for #gitstuff issue notes.
You can even include `code inline`, like `