feat(route): add Inception Labs blog route (#21386)

* feat(routes): add Inception Labs blog route

Adds RSS feed for https://www.inceptionlabs.ai/blog/

The page is built with Framer and served as SSR HTML, so no Puppeteer
is required — a regular HTTP fetch with a browser User-Agent suffices.

Two distinct blog card layouts are handled:
- Standard card: date and category in [data-framer-name="Date"] elements
- Featured card: date inside a <mark> element, category in [data-framer-name="Category"]

Each item includes title, publication date, author, category, and the
full article body extracted from [data-framer-name="Content"].

Route: /inceptionlabs/blog
Example: /inceptionlabs/blog

* refactor(routes/inceptionlabs): address review feedback

- Fix categories: technology -> programming (valid Category type)
- Remove redundant radar source (inceptionlabs.ai redirects to www)
- Add maintainer GitHub ID
- Replace custom PostMeta interface with existing DataItem type
- Use [data-framer-name="Desktop"] selector to avoid SSR duplicates
- Refactor card collection from .each()/push to .map()/.filter()

---------

Co-authored-by: Zdenek <tvuj-email@example.com>
This commit is contained in:
Zdenek Stursa 2026-03-16 16:20:37 +01:00 committed by GitHub
parent c97a5a286f
commit d2d4955b18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 127 additions and 0 deletions

View File

@ -0,0 +1,118 @@
import { load } from 'cheerio';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
const ROOT_URL = 'https://www.inceptionlabs.ai';
export const route: Route = {
path: '/blog',
categories: ['programming'],
example: '/inceptionlabs/blog',
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['www.inceptionlabs.ai/blog'],
},
],
name: 'Blog',
maintainers: ['zdenek-stursa'],
handler,
url: 'inceptionlabs.ai/blog',
};
async function handler() {
const response = await ofetch(`${ROOT_URL}/blog`);
const $ = load(response);
// Use [data-framer-name="Desktop"] to target only desktop-variant cards,
// avoiding duplicate entries from SSR breakpoint rendering (desktop/mobile).
const posts = $('a[href^="./blog/"][data-framer-name="Desktop"]')
.toArray()
.map((el): DataItem | null => {
const $el = $(el);
const href = $el.attr('href') ?? '';
// Skip category filter links and anchor-only links
if (href.includes('categories') || href.includes('#')) {
return null;
}
// Get title: first h6.framer-text that is not a "Read story" navigation label
const title = $el
.find('h6.framer-text')
.toArray()
.map((h6) => $(h6).text().trim())
.find((text) => text && text !== 'Read story');
if (!title) {
return null;
}
const slug = href.replace('./blog/', '');
const link = `${ROOT_URL}/blog/${slug}`;
// Date/category extraction two card layouts exist:
// Standard card: [data-framer-name="Date"][0] = category (teal),
// [data-framer-name="Date"][1] = plain date text
// Featured card: [data-framer-name="Date"][0] = date inside <mark>,
// [data-framer-name="Category"] = category (teal, separate element)
const dateBlocks = $el
.find('[data-framer-name="Date"] p.framer-text')
.toArray()
.map((p) => $(p).text().trim());
// Category: featured cards use data-framer-name="Category"; standard cards use dateBlocks[0]
const categoryEl = $el.find('[data-framer-name="Category"]');
const category = categoryEl.length > 0 ? categoryEl.first().text().trim() : (dateBlocks[0] ?? '');
// Date: featured cards have a single Date block (the date itself); standard cards have it at index 1
const pubDate = dateBlocks.length >= 2 ? dateBlocks[1] : (dateBlocks[0] ?? '');
return {
title,
link,
category: category ? [category] : [],
pubDate,
};
})
.filter((post): post is DataItem => post !== null);
const items = await Promise.all(
posts.map((post) =>
cache.tryGet(post.link!, async () => {
const postHtml = await ofetch(post.link!);
const $post = load(postHtml);
// Full article content
const contentHtml = $post('[data-framer-name="Content"]').first().html() ?? '';
// Author name from the first [data-framer-name="Author"] RichTextContainer
const author = $post('[data-framer-name="Author"] p.framer-text').first().text().trim();
return {
...post,
description: contentHtml,
author,
pubDate: parseDate(post.pubDate as string),
} as DataItem;
})
)
);
return {
title: 'Inception Labs Blog',
link: `${ROOT_URL}/blog`,
description: 'Latest posts from the Inception Labs blog about diffusion LLMs and AI research',
item: items,
};
}

View File

@ -0,0 +1,9 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Inception Labs',
url: 'inceptionlabs.ai',
categories: ['programming'],
description: 'Inception Labs - AI research company developing diffusion-based LLMs',
lang: 'en',
};