feat: convert #hashtags in post content to category links
Adds a markdown-it inline rule that transforms #tag text into links to /categories/tag/ on-site. Syndication targets (Bluesky, Mastodon, Bridgy) continue to receive raw #tag text, which their native facet/hashtag detection handles automatically. Edge cases handled: headings, hex colors, URL fragments, code blocks, pure numbers are all excluded from conversion. Confab-Link: http://localhost:8080/sessions/0ec83454-d346-4329-8aaf-6b12139bf596
This commit is contained in:
@@ -330,11 +330,19 @@
|
|||||||
@apply text-sm text-surface-600 dark:text-surface-400 flex flex-wrap gap-2 items-center;
|
@apply text-sm text-surface-600 dark:text-surface-400 flex flex-wrap gap-2 items-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Category tags */
|
/* Category tags (post metadata pills) */
|
||||||
.p-category {
|
.p-category {
|
||||||
@apply inline-block px-2 py-0.5 text-xs bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-300 rounded border border-surface-200 dark:border-surface-700 hover:border-surface-400 dark:hover:border-surface-500 transition-colors;
|
@apply inline-block px-2 py-0.5 text-xs bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-300 rounded border border-surface-200 dark:border-surface-700 hover:border-surface-400 dark:hover:border-surface-500 transition-colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inline hashtags in post content — styled as subtle links, not pills */
|
||||||
|
.e-content a.hashtag,
|
||||||
|
.prose a.hashtag {
|
||||||
|
@apply text-accent-600 dark:text-accent-400 no-underline hover:underline font-medium;
|
||||||
|
/* Override prose default link styling (no border-bottom, no color shift) */
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Webmention facepile - overlapping avatar display */
|
/* Webmention facepile - overlapping avatar display */
|
||||||
.facepile {
|
.facepile {
|
||||||
@apply flex flex-wrap items-center;
|
@apply flex flex-wrap items-center;
|
||||||
|
|||||||
@@ -77,6 +77,45 @@ export default function (eleventyConfig) {
|
|||||||
slugify: (s) => s.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, ""),
|
slugify: (s) => s.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, ""),
|
||||||
level: [2, 3, 4],
|
level: [2, 3, 4],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Hashtag plugin: converts #tag to category links on-site
|
||||||
|
// Syndication targets (Bluesky, Mastodon) handle raw #tag natively via facet detection
|
||||||
|
md.inline.ruler.push("hashtag", (state, silent) => {
|
||||||
|
const pos = state.pos;
|
||||||
|
if (state.src.charCodeAt(pos) !== 0x23 /* # */) return false;
|
||||||
|
|
||||||
|
// Must be at start of string or preceded by whitespace/punctuation (not part of a URL fragment or hex color)
|
||||||
|
if (pos > 0) {
|
||||||
|
const prevChar = state.src.charAt(pos - 1);
|
||||||
|
if (!/[\s()\[\]{},;:!?"'«»""'']/.test(prevChar)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match hashtag: # followed by letter/underscore, then word chars (letters, digits, underscores)
|
||||||
|
const tail = state.src.slice(pos + 1);
|
||||||
|
const match = tail.match(/^([a-zA-Z_]\w*)/);
|
||||||
|
if (!match) return false;
|
||||||
|
|
||||||
|
const tag = match[1];
|
||||||
|
|
||||||
|
// Skip pure hex color codes (3, 4, 6, or 8 hex digits with nothing else)
|
||||||
|
if (/^[0-9a-fA-F]{3,8}$/.test(tag)) return false;
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
const slug = tag.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
||||||
|
const tokenOpen = state.push("link_open", "a", 1);
|
||||||
|
tokenOpen.attrSet("href", `/categories/${slug}/`);
|
||||||
|
tokenOpen.attrSet("class", "p-category hashtag");
|
||||||
|
|
||||||
|
const tokenText = state.push("text", "", 0);
|
||||||
|
tokenText.content = `#${tag}`;
|
||||||
|
|
||||||
|
state.push("link_close", "a", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.pos = pos + 1 + tag.length;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
eleventyConfig.setLibrary("md", md);
|
eleventyConfig.setLibrary("md", md);
|
||||||
|
|
||||||
// Syntax highlighting for fenced code blocks (```lang)
|
// Syntax highlighting for fenced code blocks (```lang)
|
||||||
|
|||||||
Reference in New Issue
Block a user