" (the
// quote/reply prefix Misskey/Akkoma and some reply federation prepend) and any other
// leading bare URL, so the title shows the actual prose, not link noise.
function tidySnippet(text) {
return String(text || '')
.replace(/^RE:\s*https?:\/\/\S+\s*/i, '')
.replace(/^https?:\/\/\S+\s*/i, '')
.trim();
}
router.get('/cirkel', (req, res, next) => {
const site = res.locals.site;
if (!site || !apEnabled() || (ActivityPubService.autoBoostCount(site.slug) === 0 && ActivityPubService.boostedCount(site.slug) === 0)) return next();
const posts = ActivityPubService.getCirkelPosts(site.slug, 80).map((r) => {
const text = tidySnippet(htmlToText(r.content));
// Show ONLY the title (the bold first line a Klonkt note carries), not the whole
// body. Title-less notes (e.g. plain Mastodon) fall back to a short text snippet.
const titleM = (r.content || '').match(/^\s*\s*([\s\S]*?)<\/strong>/i);
const realTitle = titleM ? htmlToText(titleM[1]).trim() : '';
const cover = coverMedia(r.media_json);
const name = r.author_name || r.author_handle || 'Onbekend';
return {
id: 'ap-' + r.id,
slug: '',
title: realTitle
? (realTitle.length > 90 ? realTitle.slice(0, 90) + '…' : realTitle)
: (text ? (text.length > 90 ? text.slice(0, 90) + '…' : text) : name),
excerpt: '',
cover_image_url: cover.image ? cover.image.url : null,
cover_video_url: cover.video ? cover.video.url : null,
published_at: r.published,
created_at: r.published,
type: 'post',
tags: '',
pinned: 0,
isBoost: !!r.boosted, // a post YOU boosted → render in the pinned style with a Boost badge
nsfw: r.nsfw ? 1 : 0, // remote sensitive post → blur in the Cirkel (post-card/tile)
content_warning: r.cw || '',
status: 'published',
source_name: name,
external_url: safeUrl(r.url),
};
});
const sites = ActivityPubService.getCirkelMembers(site.slug)
.map((s) => ({ name: s.name || 'Onbekend', url: safeUrl(s.url), avatar: safeUrl(s.icon) }));
renderPage(req, res, 'pages/circle-feed', { pageTitle: 'Cirkel', bodyClass: 'on-cirkel', posts, sites });
});
export default router;