Final touches to the facelift

This commit is contained in:
Bad Manners 2024-09-07 18:02:53 -03:00
parent 379287af36
commit a2fbf908aa
Signed by: badmanners
GPG key ID: 8C88292CCB075609
19 changed files with 194 additions and 87 deletions

98
package-lock.json generated
View file

@ -18,6 +18,7 @@
"astro-htaccess": "^0.1.2", "astro-htaccess": "^0.1.2",
"astro-pagefind": "^1.6.0", "astro-pagefind": "^1.6.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"fluid-tailwind": "^1.0.3",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
"marked": "^14.0.0", "marked": "^14.0.0",
"pagefind": "^1.1.0", "pagefind": "^1.1.0",
@ -1601,65 +1602,71 @@
} }
}, },
"node_modules/@pagefind/darwin-arm64": { "node_modules/@pagefind/darwin-arm64": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.1.1.tgz",
"integrity": "sha512-SLsXNLtSilGZjvqis8sX42fBWsWAVkcDh1oerxwqbac84HbiwxpxOC2jm8hRwcR0Z55HPZPWO77XeRix/8GwTg==", "integrity": "sha512-tZ9tysUmQpFs2EqWG2+E1gc+opDAhSyZSsgKmFzhnWfkK02YHZhvL5XJXEZDqYy3s1FAKhwjTg8XDxneuBlDZQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
] ]
}, },
"node_modules/@pagefind/darwin-x64": { "node_modules/@pagefind/darwin-x64": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.1.1.tgz",
"integrity": "sha512-QjQSE/L5oS1C8N8GdljGaWtjCBMgMtfrPAoiCmINTu9Y9dp0ggAyXvF8K7Qg3VyIMYJ6v8vg2PN7Z3b+AaAqUA==", "integrity": "sha512-ChohLQ39dLwaxQv0jIQB/SavP3TM5K5ENfDTqIdzLkmfs3+JlzSDyQKcJFjTHYcCzQOZVeieeGq8PdqvLJxJxQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"darwin" "darwin"
] ]
}, },
"node_modules/@pagefind/default-ui": { "node_modules/@pagefind/default-ui": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.1.1.tgz",
"integrity": "sha512-+XiAJAK++C64nQcD7s3Prdmd5S92lT05fwjOxm0L1jj80jbL+tmvcqkkFnPpoqhnicIPgcAX/Y5W0HRZnBt35w==" "integrity": "sha512-ZM0zDatWDnac/VGHhQCiM7UgA4ca8jpjA+VfuTJyHJBaxGqZMQnm4WoTz9E0KFcue1Bh9kxpu7uWFZfwpZZk0A==",
"license": "MIT"
}, },
"node_modules/@pagefind/linux-arm64": { "node_modules/@pagefind/linux-arm64": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.1.1.tgz",
"integrity": "sha512-8zjYCa2BtNEL7KnXtysPtBELCyv5DSQ4yHeK/nsEq6w4ToAMTBl0K06khqxdSGgjMSwwrxvLzq3so0LC5Q14dA==", "integrity": "sha512-H5P6wDoCoAbdsWp0Zx0DxnLUrwTGWGLu/VI1rcN2CyFdY2EGSvPQsbGBMrseKRNuIrJDFtxHHHyjZ7UbzaM9EA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@pagefind/linux-x64": { "node_modules/@pagefind/linux-x64": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.1.1.tgz",
"integrity": "sha512-4lsg6VB7A6PWTwaP8oSmXV4O9H0IHX7AlwTDcfyT+YJo/sPXOVjqycD5cdBgqNLfUk8B9bkWcTDCRmJbHrKeCw==", "integrity": "sha512-yJs7tTYbL2MI3HT+ngs9E1BfUbY9M4/YzA0yEM5xBo4Xl8Yu8Qg2xZTOQ1/F6gwvMrjCUFo8EoACs6LRDhtMrQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
] ]
}, },
"node_modules/@pagefind/windows-x64": { "node_modules/@pagefind/windows-x64": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.1.1.tgz",
"integrity": "sha512-OboCM76BcMKT9IoSfZuFhiqMRgTde8x4qDDvKulFmycgiJrlL5WnIqBHJLQxZq+o2KyZpoHF97iwsGAm8c32sQ==", "integrity": "sha512-b7/qPqgIl+lMzkQ8fJt51SfguB396xbIIR+VZ3YrL2tLuyifDJ1wL5mEm+ddmHxJ2Fki340paPcDan9en5OmAw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
"license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
"win32" "win32"
@ -3370,6 +3377,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/filter-obj": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz",
"integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/find-up": { "node_modules/find-up": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@ -3413,6 +3432,20 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/fluid-tailwind": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fluid-tailwind/-/fluid-tailwind-1.0.3.tgz",
"integrity": "sha512-JhSklHiXUcKnb/PIru1TbyorLD+k0rCIgeAbFwzu1QyQHYDKmKKsKzN7xPisSeGGr7FtYY9S63t+tzddUlxxrQ==",
"license": "MIT",
"dependencies": {
"filter-obj": "^5.1.0",
"map-obj": "^5.0.2",
"picocolors": "^1.0.0"
},
"peerDependencies": {
"tailwindcss": "^3.2.0"
}
},
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@ -4266,6 +4299,18 @@
"optional": true, "optional": true,
"peer": true "peer": true
}, },
"node_modules/map-obj": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.2.tgz",
"integrity": "sha512-K6K2NgKnTXimT3779/4KxSvobxOtMmx1LBZ3NwRxT/MDIR3Br/fQ4Q+WCX5QxjyUR8zg5+RV9Tbf2c5pAWTD2A==",
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/markdown-table": { "node_modules/markdown-table": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
@ -5361,18 +5406,19 @@
} }
}, },
"node_modules/pagefind": { "node_modules/pagefind": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.1.0.tgz", "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.1.1.tgz",
"integrity": "sha512-1nmj0/vfYcMxNEQj0YDRp6bTVv9hI7HLdPhK/vBBYlrnwjATndQvHyicj5Y7pUHrpCFZpFnLVQXIF829tpFmaw==", "integrity": "sha512-U2YR0dQN5B2fbIXrLtt/UXNS0yWSSYfePaad1KcBPTi0p+zRtsVjwmoPaMQgTks5DnHNbmDxyJUL5TGaLljK3A==",
"license": "MIT",
"bin": { "bin": {
"pagefind": "lib/runner/bin.cjs" "pagefind": "lib/runner/bin.cjs"
}, },
"optionalDependencies": { "optionalDependencies": {
"@pagefind/darwin-arm64": "1.1.0", "@pagefind/darwin-arm64": "1.1.1",
"@pagefind/darwin-x64": "1.1.0", "@pagefind/darwin-x64": "1.1.1",
"@pagefind/linux-arm64": "1.1.0", "@pagefind/linux-arm64": "1.1.1",
"@pagefind/linux-x64": "1.1.0", "@pagefind/linux-x64": "1.1.1",
"@pagefind/windows-x64": "1.1.0" "@pagefind/windows-x64": "1.1.1"
} }
}, },
"node_modules/parse-latin": { "node_modules/parse-latin": {

View file

@ -25,6 +25,7 @@
"astro-htaccess": "^0.1.2", "astro-htaccess": "^0.1.2",
"astro-pagefind": "^1.6.0", "astro-pagefind": "^1.6.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"fluid-tailwind": "^1.0.3",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
"marked": "^14.0.0", "marked": "^14.0.0",
"pagefind": "^1.1.0", "pagefind": "^1.1.0",

View file

@ -168,10 +168,16 @@ async function exportStory(slug: string, options: { outputDir: string }) {
); );
const rtfText = await readFile(join(tempDir, "temp.rtf"), "utf-8"); const rtfText = await readFile(join(tempDir, "temp.rtf"), "utf-8");
const rtfStyles = getRTFStyles(rtfText); const rtfStyles = getRTFStyles(rtfText);
await writeFile( if (!rtfStyles["Preformatted Text"]) {
join(outputDir, `${slug}.rtf`), console.warn(`Missing RTF style "Preformatted Text"! Skipping RTF file generation.`);
rtfText.replaceAll(rtfStyles["Preformatted Text"], rtfStyles["Normal"]), } else if (!rtfStyles["Normal"]) {
); console.warn(`Missing RTF style "Normal"! Skipping RTF file generation.`);
} else {
await writeFile(
join(outputDir, `${slug}.rtf`),
rtfText.replaceAll(rtfStyles["Preformatted Text"], rtfStyles["Normal"]),
);
}
})(), })(),
]); ]);
console.log("Success!"); console.log("Success!");

View file

@ -254,11 +254,11 @@ const { link, instance, user, postId } = Astro.props;
if (comment.in_reply_to_id === post.postId || !(comment.in_reply_to_id in commentMap)) { if (comment.in_reply_to_id === post.postId || !(comment.in_reply_to_id in commentMap)) {
commentMap[comment.id] = commentsList.length; commentMap[comment.id] = commentsList.length;
commentsList.push(commentBox); commentsList.push(commentBox);
} else { } else if (commentMap[comment.in_reply_to_id]) {
const commentsIndex = commentMap[comment.in_reply_to_id]; const commentsIndex = commentMap[comment.in_reply_to_id]!;
commentMap[comment.id] = commentsIndex; commentMap[comment.id] = commentsIndex;
const parentThreadDiv = const parentThreadDiv =
commentsList[commentsIndex].querySelector<HTMLElementTagNameMap["div"]>("div[data-comment-thread]")!; commentsList[commentsIndex]!.querySelector<HTMLElementTagNameMap["div"]>("div[data-comment-thread]")!;
parentThreadDiv.setAttribute("aria-hidden", "false"); parentThreadDiv.setAttribute("aria-hidden", "false");
parentThreadDiv.appendChild(commentBox); parentThreadDiv.appendChild(commentBox);
} }

View file

@ -222,6 +222,7 @@ export type GamePlatform = z.infer<typeof platform>;
export type CopyrightedCharacters = z.infer<typeof copyrightedCharacters>; export type CopyrightedCharacters = z.infer<typeof copyrightedCharacters>;
export type PublishedContent = z.infer<typeof publishedContent>; export type PublishedContent = z.infer<typeof publishedContent>;
export type PostWebsite = keyof PublishedContent["posts"]; export type PostWebsite = keyof PublishedContent["posts"];
export type Posts = PublishedContent["posts"];
// Content collections // Content collections

View file

@ -13,7 +13,7 @@ const UI_STRINGS = {
tok: (names: string[]) => names.join(" en "), tok: (names: string[]) => names.join(" en "),
}, },
"util/capitalize": { "util/capitalize": {
en: (text: string) => (text.length > 0 ? `${text[0].toUpperCase()}${text.slice(1)}` : ""), en: (text: string) => (text.length > 0 ? `${text[0]!.toUpperCase()}${text.slice(1)}` : ""),
}, },
"util/enumerate": { "util/enumerate": {
en: (count: number, nounSingular: string, nounPlural?: string) => { en: (count: number, nounSingular: string, nounPlural?: string) => {

View file

@ -37,30 +37,30 @@ const isCurrentRoute = (path: string) =>
<slot name="head" /> <slot name="head" />
</Fragment> </Fragment>
<div <div
class="flex min-h-screen flex-col bg-stone-200 text-stone-800 md:flex-row dark:bg-stone-800 dark:text-stone-200 print:bg-none" class="flex flex-col bg-stone-200 text-stone-800 md:flex-row dark:bg-stone-800 dark:text-stone-200 print:bg-none"
> >
<nav <nav
class="h-card static mb-4 flex flex-col items-center bg-bm-300 pb-10 pt-10 text-center text-stone-900 shadow-xl md:fixed md:inset-y-0 md:left-0 md:mb-0 md:w-60 md:pt-20 dark:bg-green-900 dark:text-stone-100 print:bg-none print:shadow-none" class="h-card mb-4 flex shrink-0 flex-col items-center border-b-4 border-bm-400 bg-bm-300 pb-10 pt-10 text-center text-stone-900 shadow-xl md:left-0 md:mb-0 md:min-h-screen md:w-72 md:justify-self-stretch md:border-b-0 md:border-r-4 md:pt-20 dark:border-green-950 dark:bg-green-900 dark:text-stone-100 print:bg-none print:shadow-none"
> >
<img <img
loading="eager" loading="eager"
src={logo.src} src={logo.src}
alt="A pixelated metal briefcase over a background with a green gradient." alt="A pixelated metal briefcase over a background with a green gradient."
class="u-logo my-4 w-full max-w-[192px] rounded-sm border-2 border-green-950 shadow-md" class="u-logo my-4 w-full max-w-[192px] rounded-sm border-2 border-green-800 shadow-md dark:border-green-950"
width={192} width={192}
/> />
<span class="p-name mb-4 mt-2 text-2xl font-semibold">Bad Manners</span> <span class="p-name mb-6 mt-4 text-3xl font-semibold">Bad Manners</span>
<ul class="flex flex-col gap-y-2 pr-8 text-left sm:pr-2"> <ul class="flex w-[80%] max-w-sm flex-col gap-y-2 px-4 pr-8 text-left sm:pr-2">
<li> <li>
<a class="u-url text-link group" href="https://badmanners.xyz/" data-age-restricted rel="me"> <a class="u-url text-link group" href="https://badmanners.xyz/" data-age-restricted rel="me">
<IconHome width="1.25rem" height="1.25rem" class="inline align-text-top" /> <IconHome width="1.25rem" height="1.25rem" class="order-1 inline align-text-top" />
<span class="group-hover:underline group-focus:underline">Main website</span> <span class="order-3 group-hover:underline group-focus:underline">Main website</span>
</a> </a>
</li> </li>
<li> <li>
<a class="u-url text-link group" href="/" aria-current={isCurrentRoute("/") ? "page" : undefined}> <a class="u-url text-link group" href="/" aria-current={isCurrentRoute("/") ? "page" : undefined}>
<IconBriefcase width="1.25rem" height="1.25rem" class="inline align-text-top" /> <IconBriefcase width="1.25rem" height="1.25rem" class="order-1 inline align-text-top" />
<span class="group-hover:underline group-focus:underline">Gallery</span> <span class="order-3 group-hover:underline group-focus:underline">Gallery</span>
</a> </a>
</li> </li>
<li> <li>
@ -69,20 +69,20 @@ const isCurrentRoute = (path: string) =>
href="/stories/1" href="/stories/1"
aria-current={isCurrentRoute("/stories/1") ? "page" : undefined} aria-current={isCurrentRoute("/stories/1") ? "page" : undefined}
> >
<IconBook width="1.25rem" height="1.25rem" class="inline align-text-top" /> <IconBook width="1.25rem" height="1.25rem" class="order-1 inline align-text-top" />
<span class="group-hover:underline group-focus:underline">Stories</span> <span class="order-3 group-hover:underline group-focus:underline">Stories</span>
</a> </a>
</li> </li>
<li> <li>
<a class="u-url text-link group" href="/games" aria-current={isCurrentRoute("/games") ? "page" : undefined}> <a class="u-url text-link group" href="/games" aria-current={isCurrentRoute("/games") ? "page" : undefined}>
<IconGamepad width="1.25rem" height="1.25rem" class="inline align-text-top" /> <IconGamepad width="1.25rem" height="1.25rem" class="order-1 inline align-text-top" />
<span class="group-hover:underline group-focus:underline">Games</span> <span class="order-3 group-hover:underline group-focus:underline">Games</span>
</a> </a>
</li> </li>
<li> <li>
<a class="u-url text-link group" href="/tags" aria-current={isCurrentRoute("/tags") ? "page" : undefined}> <a class="u-url text-link group" href="/tags" aria-current={isCurrentRoute("/tags") ? "page" : undefined}>
<IconTags width="1.25rem" height="1.25rem" class="inline align-text-top" /> <IconTags width="1.25rem" height="1.25rem" class="order-1 inline align-text-top" />
<span class="group-hover:underline group-focus:underline">Tags</span> <span class="order-3 group-hover:underline group-focus:underline">Tags</span>
</a> </a>
</li> </li>
<li> <li>
@ -92,27 +92,35 @@ const isCurrentRoute = (path: string) =>
rel="search" rel="search"
aria-current={isCurrentRoute("/search") ? "page" : undefined} aria-current={isCurrentRoute("/search") ? "page" : undefined}
> >
<IconMagnifyingGlass width="1.25rem" height="1.25rem" class="inline align-text-top" /> <IconMagnifyingGlass width="1.25rem" height="1.25rem" class="order-1 inline align-text-top" />
<span class="group-hover:underline group-focus:underline">Search</span> <span class="order-3 group-hover:underline group-focus:underline">Search</span>
</a> </a>
</li> </li>
<li> <li>
<a class="u-url text-link group" href="/feed.xml"> <a class="u-url text-link group" href="/feed.xml">
<IconSquareRSS width="1.25rem" height="1.25rem" class="inline align-text-top" /> <IconSquareRSS width="1.25rem" height="1.25rem" class="order-1 inline align-text-top" />
<span class="group-hover:underline group-focus:underline">RSS feed</span> <span class="order-3 group-hover:underline group-focus:underline">RSS feed</span>
</a> </a>
</li> </li>
<li> <li>
<button data-dark-mode style={{ display: "none" }} class="text-link group"> <button
<IconSun width="1.25rem" height="1.25rem" class="hidden align-middle dark:inline" /> data-dark-mode
<IconMoon width="1.25rem" height="1.25rem" class="inline align-text-top dark:hidden" /> style={{ display: "none" }}
<span class="group-hover:underline group-focus:underline" class="text-link group"
>{t("en", "published_content/toggle_dark_mode")}</span aria-label={t("en", "published_content/toggle_dark_mode")}
>
<IconSun width="1.25rem" height="1.25rem" class="order-1 hidden align-text-top dark:inline" />
<IconMoon width="1.25rem" height="1.25rem" class="order-1 inline align-text-top dark:hidden" />
<span class="order-3 hidden group-hover:underline group-focus:underline dark:block" aria-hidden="true"
>Light mode</span
>
<span class="order-3 block group-hover:underline group-focus:underline dark:hidden" aria-hidden="true"
>Dark mode</span
> >
</button> </button>
</li> </li>
</ul> </ul>
<div class="pt-4 text-center text-xs text-black dark:text-white"> <div class="py-6 text-center text-xs text-black dark:text-white">
<span <span
>&copy; { >&copy; {
currentYear === "2024" ? ( currentYear === "2024" ? (
@ -128,10 +136,26 @@ const isCurrentRoute = (path: string) =>
</div> </div>
</nav> </nav>
<main <main
class:list={[className, "ml-0 max-w-6xl px-2 pb-12 pt-4 md:ml-60 md:px-4 print:pb-0"]} class:list={[className, "ml-0 max-w-6xl px-2 pb-28 pt-4 md:px-4 lg:px-8 print:pb-0"]}
data-pagefind-body={enablePagefind ? "" : undefined} data-pagefind-body={enablePagefind ? "" : undefined}
> >
<slot /> <slot />
</main> </main>
</div> </div>
</BaseLayout> </BaseLayout>
<style>
nav ul li a,
nav ul li button {
@apply flex w-full items-baseline justify-between;
}
nav ul li a::after,
nav ul li button::after {
@apply order-2 flex-grow self-end bg-bottom;
background: radial-gradient(circle, currentcolor 1px, transparent 1px);
background-size: 1ex 4.5px;
background-repeat: space no-repeat;
content: "";
height: 1ex;
}
</style>

View file

@ -9,7 +9,7 @@ import BaseLayout from "./BaseLayout.astro";
import CopyrightedCharacters from "../components/CopyrightedCharacters.astro"; import CopyrightedCharacters from "../components/CopyrightedCharacters.astro";
import Prose from "../components/Prose.astro"; import Prose from "../components/Prose.astro";
import MastodonComments from "../components/MastodonComments.astro"; import MastodonComments from "../components/MastodonComments.astro";
import type { CopyrightedCharacters as CopyrightedCharactersType } from "../content/config"; import type { CopyrightedCharacters as CopyrightedCharactersType, Posts } from "../content/config";
import { qualifyLocalURLsInMarkdown } from "../utils/qualify_local_urls_in_markdown"; import { qualifyLocalURLsInMarkdown } from "../utils/qualify_local_urls_in_markdown";
import { import {
IconSun, IconSun,
@ -44,14 +44,7 @@ type Props = {
next?: RelatedContent; next?: RelatedContent;
relatedStories?: CollectionEntry<"stories">[]; relatedStories?: CollectionEntry<"stories">[];
relatedGames?: CollectionEntry<"games">[]; relatedGames?: CollectionEntry<"games">[];
posts: { posts: Posts;
mastodon?: {
link: string;
instance: string;
user: string;
postId: string;
};
};
/* Layout attributes */ /* Layout attributes */
publishedContentType: "story" | "game"; publishedContentType: "story" | "game";
@ -159,14 +152,14 @@ const thumbnail =
</div> </div>
</div> </div>
<main <main
class="h-entry mx-auto max-w-5xl rounded-lg bg-stone-50 px-2 pb-10 pt-12 shadow-sm sm:px-6 md:px-20 lg:px-36 dark:bg-stone-900 print:max-w-full print:bg-none print:shadow-none" class="h-entry mx-auto max-w-6xl rounded-lg bg-stone-50 px-2 pb-10 shadow-sm sm:px-6 md:px-32 lg:px-64 dark:bg-stone-900 print:max-w-full print:bg-none print:shadow-none"
data-pagefind-body={props.isDraft ? undefined : ""} data-pagefind-body={props.isDraft ? undefined : ""}
data-pagefind-meta={`type:${props.publishedContentType}`} data-pagefind-meta={`type:${props.publishedContentType}`}
> >
{ {
props.prev || props.next ? ( props.prev || props.next ? (
<div class="print:hidden"> <div class="print:hidden">
<div id="nav-top" class="my-4 grid grid-cols-2 justify-items-stretch gap-2 text-lg font-light"> <div id="nav-top" class="my-4 grid grid-cols-2 justify-items-stretch gap-2 pt-4 text-lg font-light">
{props.prev ? ( {props.prev ? (
<a <a
href={props.prev.link} href={props.prev.link}
@ -196,7 +189,9 @@ const thumbnail =
</div> </div>
<hr class="mx-auto mb-5 w-full max-w-2xl border-stone-400 dark:border-stone-600" /> <hr class="mx-auto mb-5 w-full max-w-2xl border-stone-400 dark:border-stone-600" />
</div> </div>
) : null ) : (
<div class="pt-5 sm:pt-7" aria-hidden="true" />
)
} }
<h1 <h1
id="section-title" id="section-title"
@ -205,6 +200,7 @@ const thumbnail =
> >
{props.title} {props.title}
</h1> </h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] w-1/2 rounded-sm bg-stone-800 dark:bg-stone-100" />
<section <section
id="section-information" id="section-information"
class="p-summary mt-1 space-y-2 px-2 font-serif font-light italic text-stone-600 dark:text-stone-200" class="p-summary mt-1 space-y-2 px-2 font-serif font-light italic text-stone-600 dark:text-stone-200"

View file

@ -4,6 +4,7 @@ import GalleryLayout from "../layouts/GalleryLayout.astro";
<GalleryLayout pageTitle="404"> <GalleryLayout pageTitle="404">
<meta slot="head" property="og:description" content="Not found" /> <meta slot="head" property="og:description" content="Not found" />
<h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">404 &ndash; Not Found</h1> <h1 class="m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">404 &ndash; Not Found</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
<p class="my-4">The requested link could not be found. Make sure that the URL is correct.</p> <p class="my-4">The requested link could not be found. Make sure that the URL is correct.</p>
</GalleryLayout> </GalleryLayout>

View file

@ -19,7 +19,8 @@ const games = await Promise.all(
<GalleryLayout pageTitle="Games" class="h-feed"> <GalleryLayout pageTitle="Games" class="h-feed">
<meta slot="head" property="og:description" content="Bad Manners || A game that I've gone and done." /> <meta slot="head" property="og:description" content="Bad Manners || A game that I've gone and done." />
<h1 class="p-name m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Games</h1> <h1 class="p-name m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">Games</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
<p class="p-summary my-4">A game that I've gone and done.</p> <p class="p-summary my-4">A game that I've gone and done.</p>
<ul class="my-6 flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal"> <ul class="my-6 flex flex-wrap items-start justify-center gap-4 text-center md:justify-normal">
{ {

View file

@ -75,11 +75,12 @@ const latestItems: LatestItemsEntry[] = await Promise.all(
<GalleryLayout pageTitle="Gallery" class="h-feed"> <GalleryLayout pageTitle="Gallery" class="h-feed">
<meta slot="head" property="og:description" content="Bad Manners || Welcome to my gallery!" /> <meta slot="head" property="og:description" content="Bad Manners || Welcome to my gallery!" />
<h1 class="p-name m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">My gallery</h1> <h1 class="p-name m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">Welcome to my gallery!</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
<div class="p-summary"> <div class="p-summary">
<p class="my-4"> <p class="my-4">
Welcome to my gallery! You can expect lots of safe vore/endosoma ahead. Use the navigation menu to navigate Glad to see you here. You can expect lots of safe vore/endosoma ahead. Use the navigation menu to navigate through
through my content. my content.
</p> </p>
<ul class="list-disc pl-8"> <ul class="list-disc pl-8">
<li><a class="text-link underline" href="/stories/1">Read my stories!</a></li> <li><a class="text-link underline" href="/stories/1">Read my stories!</a></li>

View file

@ -10,6 +10,7 @@ import GalleryLayout from "../layouts/GalleryLayout.astro";
<GalleryLayout pageTitle="Search"> <GalleryLayout pageTitle="Search">
<meta slot="head" property="og:description" content="Bad Manners || Search" /> <meta slot="head" property="og:description" content="Bad Manners || Search" />
<h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Search</h1> <h1 class="m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">Search</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
<SearchComponent id="search" className="pagefind-ui my-4" /> <SearchComponent id="search" className="pagefind-ui my-4" />
</GalleryLayout> </GalleryLayout>

View file

@ -30,7 +30,8 @@ const totalPages = Math.ceil(page.total / page.size);
<GalleryLayout pageTitle="Stories" class="h-feed"> <GalleryLayout pageTitle="Stories" class="h-feed">
<meta slot="head" property="og:description" content={`Bad Manners || ${page.total} stories.`} /> <meta slot="head" property="og:description" content={`Bad Manners || ${page.total} stories.`} />
<h1 class="p-name m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Stories</h1> <h1 class="p-name m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">Stories</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
<div class="p-summary"> <div class="p-summary">
<p class="my-4">The bulk of my content!</p> <p class="my-4">The bulk of my content!</p>
<p class="text-center font-light text-stone-950 dark:text-white"> <p class="text-center font-light text-stone-950 dark:text-white">

View file

@ -26,7 +26,8 @@ const mainChaptersWithSummaries = mainChapters.filter((story) => story.data.summ
property="og:description" property="og:description"
content="The Lost of the Marshes || The story of Quince, Nikili, and Suu." content="The Lost of the Marshes || The story of Quince, Nikili, and Suu."
/> />
<h1 class="p-name m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">{series.data.name}</h1> <h1 class="p-name m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">{series.data.name}</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
<p class="p-summary my-4"> <p class="p-summary my-4">
This is the main hub for the story of Quince, Nikili, and Suu, as well as all bonus content. This is the main hub for the story of Quince, Nikili, and Suu, as well as all bonus content.
</p> </p>
@ -43,7 +44,7 @@ const mainChaptersWithSummaries = mainChapters.filter((story) => story.data.summ
> >
<summary class="rounded-lg bg-stone-200 px-2 py-1 dark:bg-stone-800"> <summary class="rounded-lg bg-stone-200 px-2 py-1 dark:bg-stone-800">
Click to reveal spoilers up to { Click to reveal spoilers up to {
mainChaptersWithSummaries[mainChaptersWithSummaries.length - 1].data.title.match(/Chapter \d+\b/)?.[0] mainChaptersWithSummaries[mainChaptersWithSummaries.length - 1]!.data.title.match(/Chapter \d+\b/)?.[0]
} }
</summary> </summary>
<ul class="border-t border-stone-400 px-1 dark:border-stone-500"> <ul class="border-t border-stone-400 px-1 dark:border-stone-500">

View file

@ -71,7 +71,8 @@ if (uncategorizedTagsSet.size > 0) {
<GalleryLayout pageTitle="Tags"> <GalleryLayout pageTitle="Tags">
<meta property="og:description" slot="head" content="Bad Manners || Find all content with a specific tag." /> <meta property="og:description" slot="head" content="Bad Manners || Find all content with a specific tag." />
<h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">All available tags</h1> <h1 class="m-2 text-3xl font-semibold text-stone-800 dark:text-stone-100">All available tags</h1>
<hr class="mb-3 ml-[2px] mt-2 h-[4px] max-w-xs rounded-sm bg-stone-800 dark:bg-stone-100" />
<p class="my-4">You can find all content with a specific tag by selecting it below from the appropriate category.</p> <p class="my-4">You can find all content with a specific tag by selecting it below from the appropriate category.</p>
<section class="my-2" aria-labelledby="category-series"> <section class="my-2" aria-labelledby="category-series">
<h2 id="category-series" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">Series</h2> <h2 id="category-series" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100">Series</h2>

View file

@ -2,6 +2,23 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
/* Tippy tooltips */
.tippy-box[data-theme~="bm"] {
@apply bg-stone-800 font-sans text-sm text-stone-50 dark:bg-zinc-900 dark:text-zinc-100;
}
.tippy-box[data-theme~="bm"][data-placement^="top"] > .tippy-arrow::before {
@apply border-t-stone-800 dark:border-t-zinc-900;
}
.tippy-box[data-theme~="bm"][data-placement^="bottom"] > .tippy-arrow::before {
@apply border-b-stone-800 dark:border-b-zinc-900;
}
.tippy-box[data-theme~="bm"][data-placement^="left"] > .tippy-arrow::before {
@apply border-l-stone-800 dark:border-l-zinc-900;
}
.tippy-box[data-theme~="bm"][data-placement^="right"] > .tippy-arrow::before {
@apply border-r-stone-800 dark:border-r-zinc-900;
}
@layer components { @layer components {
.text-link, .text-link,
.pagefind-ui .pagefind-ui__result-link { .pagefind-ui .pagefind-ui__result-link {

View file

@ -6,7 +6,7 @@ export async function formatCopyrightedCharacters(copyrightedCharacters: Copyrig
Object.values( Object.values(
Object.keys(copyrightedCharacters).reduce( Object.keys(copyrightedCharacters).reduce(
(acc, character) => { (acc, character) => {
const user = copyrightedCharacters[character]; const user = copyrightedCharacters[character]!;
if (user.id in acc) { if (user.id in acc) {
acc[user.id][1].push(character); acc[user.id][1].push(character);
} else { } else {

View file

@ -18,7 +18,7 @@ export function parsePartialHTMLTag(text: string): ParsedHTMLTag {
const openTag = partialText.match(OPEN_TAG_START_REGEX); const openTag = partialText.match(OPEN_TAG_START_REGEX);
if (openTag) { if (openTag) {
const result: ParsedHTMLTag = { const result: ParsedHTMLTag = {
tag: openTag[1], tag: openTag[1]!,
type: "open", type: "open",
}; };
partialText = partialText.slice(openTag[0].length); partialText = partialText.slice(openTag[0].length);
@ -30,7 +30,7 @@ export function parsePartialHTMLTag(text: string): ParsedHTMLTag {
if (!result.attributes) { if (!result.attributes) {
result.attributes = {}; result.attributes = {};
} }
result.attributes[attribute[1]] = attribute[2] ? JSON.parse(attribute[2]) : null; result.attributes[attribute[1]!] = attribute[2] ? JSON.parse(attribute[2]) : null;
partialText = partialText.slice(attribute[0].length); partialText = partialText.slice(attribute[0].length);
} }
if (partialText.match(END_OPEN_REGEX)) { if (partialText.match(END_OPEN_REGEX)) {
@ -44,7 +44,7 @@ export function parsePartialHTMLTag(text: string): ParsedHTMLTag {
const closeTag = partialText.match(CLOSE_TAG_REGEX); const closeTag = partialText.match(CLOSE_TAG_REGEX);
if (closeTag) { if (closeTag) {
return { return {
tag: closeTag[1], tag: closeTag[1]!,
type: "close", type: "close",
}; };
} }

View file

@ -1,9 +1,18 @@
import defaultTheme from "tailwindcss/defaultTheme"; import defaultTheme from "tailwindcss/defaultTheme";
import typography from "@tailwindcss/typography";
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
darkMode: ["variant", "@media not print { .dark & }"], darkMode: ["variant", "@media not print { .dark & }"],
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"], content: {
files: [
"./src/components/**/*.astro",
"./src/content/games/**/*.{md,mdx}",
"./src/content/stories/**/*.{md,mdx}",
"./src/layouts/*.astro",
"./src/pages/**/*.{astro,ts}",
],
},
theme: { theme: {
extend: { extend: {
fontFamily: { fontFamily: {