Improve tagging and toki pona support
This commit is contained in:
parent
d4a9dc9dbc
commit
18e98cdb3b
89 changed files with 971 additions and 956 deletions
|
|
@ -20,14 +20,13 @@ const games = (await getCollection("games", (game) => !game.data.isDraft)).sort(
|
|||
<a class="text-link hover:underline focus:underline" href={`/games/${game.slug}`}>
|
||||
{game.data.thumbnail ? (
|
||||
<Image
|
||||
class="max-w-72"
|
||||
class="max-w-[288px]"
|
||||
src={game.data.thumbnail}
|
||||
alt={`Thumbnail for ${game.data.title}`}
|
||||
width={game.data.thumbnailWidth}
|
||||
height={game.data.thumbnailHeight}
|
||||
width={288}
|
||||
/>
|
||||
) : null}
|
||||
<div class="max-w-72 text-sm">
|
||||
<div class="max-w-[288px] text-sm">
|
||||
<>
|
||||
<span>{game.data.title}</span>
|
||||
<br />
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const totalPages = Math.ceil(page.total / page.size);
|
|||
|
||||
<GalleryLayout pageTitle="Stories">
|
||||
<h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Stories</h1>
|
||||
<p class="my-4">My main collection of content so far.</p>
|
||||
<p class="my-4">The bulk of my content!</p>
|
||||
<p class="text-center font-light text-stone-950 dark:text-white">
|
||||
{
|
||||
page.start == page.end
|
||||
|
|
@ -68,19 +68,16 @@ const totalPages = Math.ceil(page.total / page.size);
|
|||
<a class="text-link hover:underline focus:underline" href={`/stories/${story.slug}`}>
|
||||
{story.data.thumbnail ? (
|
||||
<Image
|
||||
class="w-48"
|
||||
class="max-w-[192px]"
|
||||
src={story.data.thumbnail}
|
||||
alt={`Thumbnail for ${story.data.title}`}
|
||||
width={story.data.thumbnailWidth}
|
||||
height={story.data.thumbnailHeight}
|
||||
width={192}
|
||||
/>
|
||||
) : null}
|
||||
<div class="max-w-48 text-sm">
|
||||
<>
|
||||
<span>{story.data.title}</span>
|
||||
<br />
|
||||
<span class="italic">{formatDate(story.data.pubDate, "MMM d, yyyy", { locale: enUSLocale })}</span>
|
||||
</>
|
||||
<div class="max-w-[192px] text-sm">
|
||||
<span>{story.data.title}</span>
|
||||
<br />
|
||||
<span class="italic">{formatDate(story.data.pubDate, "MMM d, yyyy", { locale: enUSLocale })}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -92,6 +92,12 @@ function getUsernameForWebsite(user: CollectionEntry<"users">, website: Website)
|
|||
return bskyMatch[1];
|
||||
}
|
||||
break;
|
||||
case "itaku":
|
||||
const itakuMatch = link.match(/^.*\bitaku\.ee\/profile\/([^\/]+)\/?$/);
|
||||
if (itakuMatch && itakuMatch[1]) {
|
||||
return itakuMatch[1];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled website "${website}" in getUsernameForWebsite`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@ import { getCollection } from "astro:content";
|
|||
import { slug } from "github-slugger";
|
||||
import GalleryLayout from "../layouts/GalleryLayout.astro";
|
||||
|
||||
const [stories, games] = await Promise.all([
|
||||
getCollection("stories", (story) => !story.data.isDraft),
|
||||
getCollection("games", (game) => !game.data.isDraft),
|
||||
const [stories, games, tagCategories] = await Promise.all([
|
||||
getCollection("stories"),
|
||||
getCollection("games"),
|
||||
getCollection("tag-categories"),
|
||||
]);
|
||||
const tagsSet = new Set<string>();
|
||||
const draftOnlyTagsSet = new Set<string>();
|
||||
const seriesList: Record<string, string> = {};
|
||||
stories.forEach((story) => {
|
||||
stories.filter((story) => !story.data.isDraft).forEach((story) => {
|
||||
story.data.tags.forEach((tag) => {
|
||||
tagsSet.add(tag);
|
||||
});
|
||||
|
|
@ -26,7 +28,7 @@ stories.forEach((story) => {
|
|||
}
|
||||
}
|
||||
});
|
||||
games.forEach((game) => {
|
||||
games.filter((game) => !game.data.isDraft).forEach((game) => {
|
||||
game.data.tags.forEach((tag) => {
|
||||
tagsSet.add(tag);
|
||||
});
|
||||
|
|
@ -43,96 +45,53 @@ games.forEach((game) => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
const categorizedTags: Record<string, string[]> = {
|
||||
"Types of vore": [
|
||||
"oral vore",
|
||||
"anal vore",
|
||||
"cock vore",
|
||||
"unbirth",
|
||||
"tail vore",
|
||||
"slit vore",
|
||||
"sheath vore",
|
||||
"nipple vore",
|
||||
"chest maw vore",
|
||||
],
|
||||
"Body types": [
|
||||
"anthro predator",
|
||||
"feral predator",
|
||||
"taur predator",
|
||||
"ambiguous predator",
|
||||
"human prey",
|
||||
"anthro prey",
|
||||
"feral prey",
|
||||
"ambiguous prey",
|
||||
],
|
||||
Genders: [
|
||||
"male predator",
|
||||
"trans male predator",
|
||||
"female predator",
|
||||
"non-binary predator",
|
||||
"ambiguous gender predator",
|
||||
"male prey",
|
||||
"female prey",
|
||||
"trans female prey",
|
||||
"non-binary prey",
|
||||
"ambiguous gender prey",
|
||||
],
|
||||
"Relative size": ["macro predator", "micro prey", "size difference", "similar size", "same size", "smaller predator"],
|
||||
Willingness: [
|
||||
"willing predator",
|
||||
"semi-willing predator",
|
||||
"unwilling predator",
|
||||
"asleep predator",
|
||||
"willing prey",
|
||||
"semi-willing prey",
|
||||
"unwilling prey",
|
||||
"asleep prey",
|
||||
],
|
||||
"Vore-related scenarios": [
|
||||
"point of view",
|
||||
"regurgitation",
|
||||
"long-term endo",
|
||||
"perma endo",
|
||||
"implied perma endo",
|
||||
"full tour",
|
||||
"implied full tour",
|
||||
"prey transfer",
|
||||
"object vore",
|
||||
"role reversal",
|
||||
"nested vore",
|
||||
"multiple prey",
|
||||
"messy stomach",
|
||||
"bladder vore",
|
||||
"soul vore",
|
||||
],
|
||||
"Sexual content": ["nudity", "masturbation", "straight sex", "gay sex", "lesbian sex", "orgy"],
|
||||
"Other kinks": [
|
||||
"hyper",
|
||||
"egg play",
|
||||
"transformation",
|
||||
"netorare",
|
||||
"sizeplay",
|
||||
"inflation",
|
||||
"daddy play",
|
||||
"BDSM",
|
||||
"dubcon",
|
||||
],
|
||||
"Type of content": ["request", "commission", "flash fiction"],
|
||||
"Recurring characters": ["Sam Brendan", "Beetle", "Muno"],
|
||||
};
|
||||
|
||||
Object.entries(categorizedTags).forEach(([category, tagList]) => {
|
||||
tagList.forEach((tag) => {
|
||||
if (!tagsSet.delete(tag)) {
|
||||
throw new Error(`Tag "${tag}" was added to category "${category}" but isn't present in any content`);
|
||||
stories.filter((story) => story.data.isDraft).forEach((story) => {
|
||||
story.data.tags.forEach((tag) => {
|
||||
if (!tagsSet.has(tag)) {
|
||||
draftOnlyTagsSet.add(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
games.filter((game) => game.data.isDraft).forEach((game) => {
|
||||
game.data.tags.forEach((tag) => {
|
||||
if (!tagsSet.has(tag)) {
|
||||
draftOnlyTagsSet.add(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
const uncategorizedTagsSet = new Set(tagsSet);
|
||||
|
||||
if (tagsSet.size > 0) {
|
||||
console.log("The following tags have no category:", [...tagsSet]);
|
||||
categorizedTags["Uncategorized tags"] = [...tagsSet];
|
||||
const categorizedTags: Array<[string, string, string[]]> = tagCategories.sort((a, b) => a.data.index - b.data.index).map(category => {
|
||||
const tagList = category.data.tags.map(tag => {
|
||||
if (typeof tag === "string") {
|
||||
return tag
|
||||
}
|
||||
if (!("eng" in tag)) {
|
||||
throw new Error(`"{[lang]: text}" tag must have an "eng" key: ${tag}`)
|
||||
}
|
||||
return tag["eng"]!
|
||||
})
|
||||
tagList.forEach((tag, index) => {
|
||||
if (index !== tagList.findLastIndex((val) => tag == val)) {
|
||||
throw new Error(`Duplicated tag "${tag}" found in multiple tag-categories`)
|
||||
}
|
||||
})
|
||||
return [category.data.name, category.id, tagList.filter(tag => {
|
||||
if (draftOnlyTagsSet.has(tag)) {
|
||||
console.log(`Omitting draft-only tag "${tag}"`);
|
||||
return false;
|
||||
}
|
||||
if (tagsSet.has(tag)) {
|
||||
uncategorizedTagsSet.delete(tag);
|
||||
}
|
||||
return true;
|
||||
})];
|
||||
})
|
||||
|
||||
if (uncategorizedTagsSet.size > 0) {
|
||||
const tagList = [...uncategorizedTagsSet];
|
||||
console.log("The following tags have no category:", tagList);
|
||||
categorizedTags.push(["Uncategorized tags", "others", tagList]);
|
||||
}
|
||||
---
|
||||
|
||||
|
|
@ -154,21 +113,23 @@ if (tagsSet.size > 0) {
|
|||
</ul>
|
||||
</section>
|
||||
{
|
||||
Object.entries(categorizedTags).map(([category, tagList]) => (
|
||||
<section class="my-2" aria-labelledby={`category-${slug(category)}`}>
|
||||
<h2 id={`category-${slug(category)}`} class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">
|
||||
{category}
|
||||
</h2>
|
||||
<ul class="flex max-w-3xl flex-wrap gap-x-2 gap-y-2 px-2">
|
||||
{tagList.map((tag) => (
|
||||
<li class="rounded-full bg-bm-300 px-3 py-1 text-sm text-black shadow-sm dark:bg-bm-600 dark:text-white">
|
||||
<a class="hover:underline focus:underline" href={`/tags/${slug(tag)}`}>
|
||||
{tag}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
))
|
||||
categorizedTags.map(([category, categorySlug, tagList]) =>
|
||||
tagList.length > 0 ? (
|
||||
<section class="my-2" aria-labelledby={`category-${categorySlug}`}>
|
||||
<h2 id={`category-${categorySlug}`} class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">
|
||||
{category}
|
||||
</h2>
|
||||
<ul class="flex max-w-3xl flex-wrap gap-x-2 gap-y-2 px-2">
|
||||
{tagList.map((tag) => (
|
||||
<li class="rounded-full bg-bm-300 px-3 py-1 text-sm text-black shadow-sm dark:bg-bm-600 dark:text-white">
|
||||
<a class="hover:underline focus:underline" href={`/tags/${slug(tag)}`}>
|
||||
{tag}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
) : null,
|
||||
)
|
||||
}
|
||||
</GalleryLayout>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue