recreate maplestory guild bbs layout
|
@ -2,10 +2,14 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import db from '@astrojs/db';
|
||||
import node from '@astrojs/node';
|
||||
import { modifiedTime } from './last-modified.mjs';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: "https://haetae.gay",
|
||||
markdown: {
|
||||
remarkPlugins: [modifiedTime],
|
||||
},
|
||||
integrations: [db()],
|
||||
adapter: node({
|
||||
mode: 'standalone'
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { statSync } from "fs";
|
||||
|
||||
export function modifiedTime() {
|
||||
return function (tree, file) {
|
||||
const path = file.history[0];
|
||||
const result = statSync(path);
|
||||
file.data.astro.frontmatter.lastModified = result.mtime;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
"@astrojs/rss": "^4.0.11",
|
||||
"astro": "^5.1.7",
|
||||
"astro-breadcrumbs": "^3.3.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"markdown-it": "^14.1.0",
|
||||
"sanitize-html": "^2.14.0"
|
||||
},
|
||||
|
|
After Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
After Width: | Height: | Size: 301 B |
After Width: | Height: | Size: 583 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 302 B |
After Width: | Height: | Size: 585 B |
After Width: | Height: | Size: 532 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 865 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 945 B |
After Width: | Height: | Size: 916 B |
After Width: | Height: | Size: 790 B |
After Width: | Height: | Size: 785 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 948 B |
After Width: | Height: | Size: 541 B |
After Width: | Height: | Size: 825 B |
After Width: | Height: | Size: 853 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 559 B |
After Width: | Height: | Size: 544 B |
After Width: | Height: | Size: 796 B |
After Width: | Height: | Size: 831 B |
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 840 B |
After Width: | Height: | Size: 848 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 557 B |
After Width: | Height: | Size: 877 B |
After Width: | Height: | Size: 866 B |
After Width: | Height: | Size: 826 B |
After Width: | Height: | Size: 871 B |
After Width: | Height: | Size: 847 B |
After Width: | Height: | Size: 802 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 560 B |
After Width: | Height: | Size: 868 B |
|
@ -6,6 +6,8 @@
|
|||
--title-font: "Kiwi Soda", Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
|
||||
--mono-font: "Departure Mono", ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
|
||||
--serif-font: "Redaction 35", 'Iowan Old Style', 'Palatino Linotype', 'URW Palladio L', P052, serif;
|
||||
--sans-font: "MLSS", 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
|
||||
--arial-font: "Arial Pixel", Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
@font-face {
|
||||
font-family: "Arial Pixel";
|
||||
src: url("/fonts/PIXEAR.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Arial Pixel";
|
||||
src: url("/fonts/PIXEAB.woff2") format("woff2");
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Departure Mono";
|
||||
src: url("/fonts/DepartureMono-Regular.woff2") format("woff2");
|
||||
|
@ -10,6 +22,12 @@
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "MLSS";
|
||||
src: url("/fonts/mario-luigi-rpg-speech-text.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Redaction 35";
|
||||
src: url("/fonts/Redaction_35-Regular.woff2") format("woff2");
|
||||
|
|
|
@ -5,13 +5,14 @@ interface Props {
|
|||
imagePath: string;
|
||||
alt: string;
|
||||
caption?: string;
|
||||
className?: string[] | string;
|
||||
}
|
||||
|
||||
const { imagePath, alt, caption }: Props = Astro.props;
|
||||
const { imagePath, alt, caption, className }: Props = Astro.props;
|
||||
const images = import.meta.glob<{ default: ImageMetadata }>("/src/assets/**/*.{jpeg,jpg,png,webp,gif}");
|
||||
if (!images[imagePath]) throw new Error(`"${imagePath}" does not exist in glob: "src/assets/**/*.{jpeg,jpg,png,webp,gif}"`);
|
||||
---
|
||||
<figure>
|
||||
<figure class:list={className}>
|
||||
<Image src={images[imagePath]()} {alt} />
|
||||
<figcaption>{caption ?? alt}</figcaption>
|
||||
{caption && <figcaption>{caption}</figcaption>}
|
||||
</figure>
|
|
@ -1,6 +1,11 @@
|
|||
---
|
||||
title: hey girl hey
|
||||
pubDate: 2024-02-03
|
||||
currently:
|
||||
mood: happy
|
||||
reading: hella
|
||||
watching: stuff
|
||||
playing: balatro
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pretium augue elit, eget interdum massa lobortis ut. Praesent facilisis ornare aliquam. Donec sit amet volutpat ipsum, id ultricies urna. Donec vestibulum sagittis felis, tempor fermentum urna posuere at. Vestibulum cursus mauris eget bibendum blandit. Aenean at augue porttitor, bibendum massa ut, laoreet lacus. Phasellus fermentum tincidunt lectus vel volutpat. Proin sagittis vel sem sit amet consequat. Vestibulum ac laoreet quam. Mauris eu purus sit amet odio maximus dictum. Mauris quam tellus, tempus eu faucibus in, mollis quis velit. Phasellus nisl mauris, congue vel magna a, rutrum aliquet ante. Proin in ante pharetra, vestibulum nisi vel, fringilla tortor. Etiam mattis, mauris et mattis sagittis, orci eros ornare risus, vitae consequat ligula nulla eget quam.
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
import { defineCollection, z } from "astro:content";
|
||||
import { glob } from "astro/loaders";
|
||||
import { rssSchema } from "@astrojs/rss";
|
||||
import moods from "@/utils/moods";
|
||||
|
||||
const blog = defineCollection({
|
||||
loader: glob({ pattern: "*.md", base: "./src/content/blog" }),
|
||||
schema: rssSchema,
|
||||
schema: rssSchema.extend({
|
||||
currently: z.object({
|
||||
mood: z.enum(moods).optional(),
|
||||
reading: z.ostring(),
|
||||
watching: z.ostring(),
|
||||
playing: z.ostring(),
|
||||
listening: z.ostring(),
|
||||
}).optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
function generateFicSlug({ entry, data }: { entry: string, data: any }): string {
|
||||
|
|
|
@ -1,11 +1,63 @@
|
|||
---
|
||||
import type { MarkdownLayoutProps } from "astro";
|
||||
import Navbar from "@/components/Navbar.astro";
|
||||
import Figure from "@/components/Figure.astro";
|
||||
import Layout from "./Layout.astro";
|
||||
import border from "@/assets/images/border.png";
|
||||
import frame from "@/assets/images/frame.png";
|
||||
|
||||
type Props = MarkdownLayoutProps<{
|
||||
avatar?: string;
|
||||
avatarText?: string;
|
||||
}>;
|
||||
|
||||
const { frontmatter } = Astro.props;
|
||||
---
|
||||
<Layout>
|
||||
<Navbar />
|
||||
|
||||
|
||||
<main>
|
||||
<slot />
|
||||
{frontmatter.avatar && frontmatter.avatarText && (
|
||||
<Figure className="avatar" imagePath={frontmatter.avatar} alt={frontmatter.avatarText} />
|
||||
)}
|
||||
<article>
|
||||
<slot />
|
||||
</article>
|
||||
</main>
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
||||
<style define:vars={{ borderImage: `url(${border.src})`, frameImage: `url(${frame.src})` }}>
|
||||
main {
|
||||
margin: 3rem auto;
|
||||
max-width: clamp(50ch, 80ch, 100%);
|
||||
}
|
||||
|
||||
article {
|
||||
padding: 1rem 1.5rem;
|
||||
color: #f8f8f8;
|
||||
text-shadow: 2px 2px #000;
|
||||
background: #202020;
|
||||
border-image: var(--frameImage) 6 9 / 12px 18px / 10px repeat;
|
||||
image-rendering: pixelated;
|
||||
|
||||
h1, h2, h3 {
|
||||
font-family: var(--body-font);
|
||||
border-bottom: 2px solid #f8f8f8;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.avatar) {
|
||||
width: fit-content;
|
||||
margin: 0 auto 2rem;
|
||||
border-image: var(--borderImage) 5 4 / 10px 8px / 8px repeat;
|
||||
image-rendering: pixelated;
|
||||
|
||||
img {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
width: 80px;
|
||||
height: auto;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2,21 +2,32 @@
|
|||
import { getCollection } from "astro:content";
|
||||
import Layout from "./Layout.astro";
|
||||
import Navbar from "@/components/Navbar.astro";
|
||||
import moods from "@/utils/moods";
|
||||
import outerBBS from "@/assets/images/guild-bbs.png";
|
||||
import innerBBS from "@/assets/images/guild-bbs-content.png";
|
||||
import { Image } from "astro:assets";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
date: Date;
|
||||
currently?: {
|
||||
mood?: string;
|
||||
reading?: string;
|
||||
listening?: string;
|
||||
watching?: string;
|
||||
playing?: string;
|
||||
}
|
||||
}
|
||||
|
||||
const blog = await getCollection("blog");
|
||||
blog.length = Math.min(blog.length, 5);
|
||||
blog.sort((a, b) => a.data.pubDate!.valueOf() - b.data.pubDate!.valueOf());
|
||||
const { title, date } = Astro.props;
|
||||
const { title, date, currently } = Astro.props;
|
||||
---
|
||||
<Layout>
|
||||
<Navbar />
|
||||
|
||||
<section>
|
||||
<main>
|
||||
<nav id="blog-links">
|
||||
<h1>recent posts</h1>
|
||||
<ul>
|
||||
|
@ -36,33 +47,80 @@ const { title, date } = Astro.props;
|
|||
</ul>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
<section>
|
||||
<article>
|
||||
<header>
|
||||
<h1>{title}</h1>
|
||||
<time datetime={date.toISOString()}>
|
||||
Posted on
|
||||
{date.toLocaleDateString(undefined, { dateStyle: "long" })}
|
||||
</time>
|
||||
</header>
|
||||
<div class="inner">
|
||||
<header>
|
||||
<h1>{title}</h1>
|
||||
<hr />
|
||||
<div class="info">
|
||||
<time datetime={date.toISOString()}>
|
||||
<span class="title">Date</span>
|
||||
<span class="desc">{date.toLocaleDateString(undefined, { dateStyle: "long" })}</span>
|
||||
</time>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<slot />
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
{currently && (
|
||||
<aside>
|
||||
{currently?.mood && (
|
||||
<dl>
|
||||
<dt>Current mood</dt>
|
||||
<dd>
|
||||
<Image src={`/src/assets/moods/${moods.find(mood => mood === currently?.mood)}.png`} width="32" height="32" alt={currently.mood} />
|
||||
</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.playing && (
|
||||
<dl>
|
||||
<dt>Currently playing</dt>
|
||||
<dd>{currently.playing}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.watching && (
|
||||
<dl>
|
||||
<dt>Currently watching</dt>
|
||||
<dd>{currently.watching}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.reading && (
|
||||
<dl>
|
||||
<dt>Currently reading</dt>
|
||||
<dd>{currently.reading}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.listening && (
|
||||
<dl>
|
||||
<dt>Currently listening</dt>
|
||||
<dd>{currently.listening}</dd>
|
||||
</dl>
|
||||
)}
|
||||
</aside>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<slot name="pagination" />
|
||||
</main>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
section {
|
||||
<style define:vars={{ outerBorder: `url(${outerBBS.src})`, innerBorder: `url(${innerBBS.src})` }}>
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, auto);
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
height: calc(100% - 2.5rem);
|
||||
|
||||
@media screen and (max-width: 1270px) {
|
||||
grid-template-columns: 1fr;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
#blog-links {
|
||||
|
@ -92,61 +150,127 @@ const { title, date } = Astro.props;
|
|||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1251px) {
|
||||
@media screen and (max-width: 1270px) {
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
section {
|
||||
width: 75ch;
|
||||
|
||||
@media screen and (max-width: 950px) {
|
||||
@media screen and (max-width: 1270px) {
|
||||
width: 100%;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
font: 1rem var(--arial-font);
|
||||
letter-spacing: 1px;
|
||||
border-image: var(--outerBorder) 26 11 12 13 fill / 52px 22px 24px 26px;
|
||||
border-style: solid;
|
||||
image-rendering: pixelated;
|
||||
padding: calc(2rem + 2px) calc(1rem - 2px) calc(1rem - 4px) calc(1rem + 2px);
|
||||
|
||||
.inner {
|
||||
border-image: var(--innerBorder) 4 3 3 fill / 8px 6px 6px;
|
||||
border-style: solid;
|
||||
image-rendering: pixelated;
|
||||
margin: 14px 4px 8px;
|
||||
padding: 2px 1px 1px;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1rem;
|
||||
padding-left: 2rem;
|
||||
border-bottom: 2px solid #ccccdd;
|
||||
|
||||
time { padding-left: 3rem; }
|
||||
h1 {
|
||||
font: bold 1rem var(--arial-font);
|
||||
text-align: center;
|
||||
margin: 6px 2px;
|
||||
padding: 2px 0;
|
||||
background-color: #bbccdd;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 950px) {
|
||||
padding: 0.25rem;
|
||||
padding-left: 2rem;
|
||||
.info {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
border-top-color: #eeeeff;
|
||||
border-right-color: #eeeeff;
|
||||
border-left-color: #eeffff;
|
||||
border-bottom-color: #eeffff;
|
||||
padding: 2px 4px;
|
||||
|
||||
time, .section {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
background-color: #eeeeff;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
background-color: #ddddee;
|
||||
padding: 4px 6px;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: #99bbcc;
|
||||
border: none;
|
||||
height: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
background-image: linear-gradient(#d1d5db 2px, transparent 0px);
|
||||
background-size: 100% 1em;
|
||||
background-image: linear-gradient(to right, #fff 5px, transparent 2px), linear-gradient(#d1d5db 2px, transparent 2px);
|
||||
background-size: 10px 2em;
|
||||
background-position-y: 1.75rem;
|
||||
margin: 0 2rem 2rem;
|
||||
padding: 2rem;
|
||||
font-size: 2rem;
|
||||
line-height: 1em;
|
||||
border: 2px solid black;
|
||||
line-height: 2em;
|
||||
margin-trim: block;
|
||||
|
||||
p { margin-block-end: 1em; }
|
||||
&::before, &::after {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 30px;
|
||||
content: "";
|
||||
border: 20px solid transparent;
|
||||
}
|
||||
|
||||
&::before { border-bottom: 20px solid black; }
|
||||
&::after {
|
||||
border-bottom: 20px solid white;
|
||||
margin-bottom: -3px;
|
||||
p { margin-block: 1lh; }
|
||||
|
||||
@supports not (margin-trim: block) {
|
||||
:first-child { margin-block-start: 0; }
|
||||
:last-child { margin-block-end: 0; }
|
||||
}
|
||||
|
||||
@media screen and (max-width: 950px) {
|
||||
background-position-y: 0.8rem;
|
||||
padding: 1rem;
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
border-top: 2px solid #88aabb;
|
||||
|
||||
dl {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
border-bottom: 2px solid #88aabb;
|
||||
|
||||
dt {
|
||||
background-color: #eeeeff;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
dd {
|
||||
background-color: #ddddee;
|
||||
padding: 4px 6px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:first-child > dt ~ dd { padding: 0; }
|
||||
&:last-child { border: none; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
---
|
||||
import Layout from "./Layout.astro";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
date: Date;
|
||||
notes?: string;
|
||||
lastModified?: string;
|
||||
lastModified?: Date;
|
||||
}
|
||||
|
||||
const { title, date, notes, lastModified }: Props = Astro.props;
|
||||
dayjs.extend(utc);
|
||||
---
|
||||
<Layout>
|
||||
<slot name="breadcrumbs" />
|
||||
|
@ -16,13 +19,13 @@ const { title, date, notes, lastModified }: Props = Astro.props;
|
|||
<main>
|
||||
<header>
|
||||
<h1>{title}</h1>
|
||||
<time datetime={date.toISOString()}>
|
||||
{date.toLocaleDateString(undefined, {
|
||||
weekday: "long", year: "numeric", month: "long", day: "numeric",
|
||||
})}
|
||||
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||
Published on {dayjs(date).utc(true).format("MMMM DD, YYYY")}
|
||||
</time>
|
||||
{lastModified && (
|
||||
<time datetime={lastModified}>{lastModified}</time>
|
||||
<time datetime={dayjs(lastModified).utc(true).toISOString()}>
|
||||
Last edited on {dayjs(lastModified).utc(true).format("MMMM DD, YYYY")}
|
||||
</time>
|
||||
)}
|
||||
{notes && (
|
||||
<blockquote><Fragment set:html={notes.split("\n").join("<br />")} /></blockquote>
|
||||
|
@ -45,23 +48,31 @@ const { title, date, notes, lastModified }: Props = Astro.props;
|
|||
|
||||
article, blockquote { font-family: var(--serif-font); }
|
||||
|
||||
blockquote {
|
||||
margin: 1rem;
|
||||
font-size: 1.125rem;
|
||||
header {
|
||||
blockquote {
|
||||
margin: 1rem;
|
||||
font-size: 1.125rem;
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
content: "Author’s Notes:";
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
&::before {
|
||||
display: block;
|
||||
content: "Author’s Notes:";
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
alt: "Author's Notes:";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article {
|
||||
margin: 1rem 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
margin-trim: block;
|
||||
|
||||
p { margin-block-end: 1rem; }
|
||||
p { margin-block: 1lh; }
|
||||
@supports not (margin-trim: block) {
|
||||
:first-child { margin-block-start: 0; }
|
||||
:last-child { margin-block-end: 0; }
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -16,6 +16,7 @@ const { title = "haetae" }: Props = Astro.props;
|
|||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<meta name="pinterest" content="nopin nohover" />
|
||||
<slot name="header" />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
---
|
||||
title: about me
|
||||
layout: ../layouts/About.astro
|
||||
avatar: /src/assets/images/portrait-0025.png
|
||||
avatarText: pikachu from pokemon mystery dungeon
|
||||
---
|
||||
# hello!
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ const current = blog.findIndex(entry => entry.id === Astro.params.id);
|
|||
const previous = current + 1 === blog.length ? undefined : blog[current + 1];
|
||||
const next = current === 0 ? undefined : blog[current - 1];
|
||||
---
|
||||
<Blog title={entry.data.title!} date={entry.data.pubDate!}>
|
||||
<Blog title={entry.data.title!} date={entry.data.pubDate!} currently={entry.data.currently}>
|
||||
<Content />
|
||||
|
||||
{(previous || next) && (
|
||||
|
|
|
@ -13,6 +13,7 @@ const getMonth = (id: number) => {
|
|||
}
|
||||
---
|
||||
<Layout>
|
||||
<Fragment slot="header" set:html={`<link rel="alternate" type="application/rss+xml" title="haetae's blog" href=${new URL("/blog/rss.xml", Astro.site)} />`} />
|
||||
<h1>blog</h1>
|
||||
<ul>
|
||||
{Object.entries(sorted).map(entry => (
|
||||
|
@ -31,4 +32,5 @@ const getMonth = (id: number) => {
|
|||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<a href="/blog/rss.xml">rss feed</a>
|
||||
</Layout>
|
|
@ -3,6 +3,10 @@ import Chapter from "@/layouts/Chapter.astro";
|
|||
import type { GetStaticPaths } from "astro";
|
||||
import { getCollection, render } from "astro:content";
|
||||
import { Breadcrumbs } from "astro-breadcrumbs";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const chapters = await getCollection("chapters");
|
||||
|
@ -17,7 +21,7 @@ export const getStaticPaths = (async () => {
|
|||
|
||||
const { ficId, chapterId } = Astro.params;
|
||||
const { chapter } = Astro.props;
|
||||
const { Content } = await render(chapter);
|
||||
const { Content, remarkPluginFrontmatter } = await render(chapter);
|
||||
|
||||
const chapters = await getCollection("chapters", ({ id }) => {
|
||||
return id.split("/")[0] === ficId;
|
||||
|
@ -29,14 +33,14 @@ chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
|||
const current = chapters.findIndex(chapter => chapter.id === `${ficId}/${chapterId}`);
|
||||
const next = current + 1 === chapters.length ? undefined : chapters[current + 1];
|
||||
const previous = current === 0 ? undefined : chapters[current - 1];
|
||||
// lastmodified
|
||||
const lastModified = new Date(remarkPluginFrontmatter.lastModified);
|
||||
|
||||
const links = [
|
||||
{ index: "last", text: chapter.data.title },
|
||||
{ index: 2, text: fic[0].data.title },
|
||||
];
|
||||
---
|
||||
<Chapter title={chapter.data.title} date={chapter.data.publishedAt} notes={chapter.data.notes}>
|
||||
<Chapter title={chapter.data.title} date={chapter.data.publishedAt} {lastModified} notes={chapter.data.notes}>
|
||||
<Fragment slot="breadcrumbs">
|
||||
<Breadcrumbs id="breadcrumbs" customizeLinks={links} linkTextFormat="capitalized">
|
||||
<Fragment slot="separator" set:text="/" />
|
||||
|
@ -99,6 +103,13 @@ const links = [
|
|||
justify-self: center;
|
||||
}
|
||||
|
||||
#chapter-select {
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
#previous {
|
||||
grid-area: 2 / 1 / 2 / 1;
|
||||
justify-self: left;
|
||||
|
|
|
@ -19,6 +19,7 @@ chapters.length = Math.min(chapters.length, 5);
|
|||
chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
---
|
||||
<Layout>
|
||||
<Fragment slot="header" set:html={`<link rel="alternate" type="application/rss+xml" title="${fic.data.title}" href=${new URL(`/fics/${Astro.params.ficId}/rss.xml`, Astro.site)} />`} />
|
||||
<section>
|
||||
<h1>{fic.data.title}</h1>
|
||||
<header class="info">
|
||||
|
|
|
@ -18,7 +18,9 @@ chapters.sort((a, b) => a.data.publishedAt.valueOf() - b.data.publishedAt.valueO
|
|||
{post.data.publishedAt.toLocaleDateString(undefined, { dateStyle: "medium" })}
|
||||
</time>
|
||||
<a href={`/fics/${post.id}`}>{post.data.title}</a> in
|
||||
<a href={`/fics/${post.id.split("/")[0]}`}>{fics.find(({ id }) => post.id.startsWith(id))?.data.title}</a>
|
||||
<a href={`/fics/${post.id.split("/")[0]}`}>
|
||||
{fics.find(({ id }) => post.id.startsWith(id))?.data.title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
export default [
|
||||
"accomplished",
|
||||
"aggravated",
|
||||
"amused",
|
||||
"angry",
|
||||
"annoyed",
|
||||
"anxious",
|
||||
"apathetic",
|
||||
"artistic",
|
||||
"awake",
|
||||
"bitchy",
|
||||
"blah",
|
||||
"blank",
|
||||
"bored",
|
||||
"bouncy",
|
||||
"busy",
|
||||
"calm",
|
||||
"cheerful",
|
||||
"chipper",
|
||||
"cold",
|
||||
"complacent",
|
||||
"confused",
|
||||
"contemplative",
|
||||
"content",
|
||||
"cranky",
|
||||
"crappy",
|
||||
"crazy",
|
||||
"creative",
|
||||
"crushed",
|
||||
"curious",
|
||||
"cynical",
|
||||
"depressed",
|
||||
"determined",
|
||||
"devious",
|
||||
"dirty",
|
||||
"disappointed",
|
||||
"discontent",
|
||||
"distressed",
|
||||
"ditzy",
|
||||
"dorky",
|
||||
"drained",
|
||||
"drunk",
|
||||
"ecstatic",
|
||||
"embarrassed",
|
||||
"energetic",
|
||||
"enraged",
|
||||
"enthralled",
|
||||
"envious",
|
||||
"exanimate",
|
||||
"excited",
|
||||
"exhausted",
|
||||
"flirty",
|
||||
"frustrated",
|
||||
"full",
|
||||
"geeky",
|
||||
"giddy",
|
||||
"giggly",
|
||||
"gloomy",
|
||||
"good",
|
||||
"grateful",
|
||||
"groggy",
|
||||
"grumpy",
|
||||
"guilty",
|
||||
"happy",
|
||||
"high",
|
||||
"hopeful",
|
||||
"horny",
|
||||
"hot",
|
||||
"hungry",
|
||||
"hyper",
|
||||
"impressed",
|
||||
"indescribable",
|
||||
"indifferent",
|
||||
"infuriated",
|
||||
"intimidated",
|
||||
"irate",
|
||||
"irritated",
|
||||
"jealous",
|
||||
"jubilant",
|
||||
"lazy",
|
||||
"lethargic",
|
||||
"listless",
|
||||
"lonely",
|
||||
"loved",
|
||||
"melancholy",
|
||||
"mellow",
|
||||
"mischievous",
|
||||
"moody",
|
||||
"morose",
|
||||
"naughty",
|
||||
"nauseated",
|
||||
"nerdy",
|
||||
"nervous",
|
||||
"nostalgic",
|
||||
"numb",
|
||||
"okay",
|
||||
"optimistic",
|
||||
"peaceful",
|
||||
"pensive",
|
||||
"pessimistic",
|
||||
"pissed off",
|
||||
"pleased",
|
||||
"predatory",
|
||||
"productive",
|
||||
"quixotic",
|
||||
"recumbent",
|
||||
"refreshed",
|
||||
"rejected",
|
||||
"rejuvenated",
|
||||
"relaxed",
|
||||
"relieved",
|
||||
"restless",
|
||||
"rushed",
|
||||
"sad",
|
||||
"satisfied",
|
||||
"scared",
|
||||
"shocked",
|
||||
"sick",
|
||||
"silly",
|
||||
"sleepy",
|
||||
"sore",
|
||||
"stressed",
|
||||
"surprised",
|
||||
"sympathetic",
|
||||
"thankful",
|
||||
"thirsty",
|
||||
"thoughtful",
|
||||
"tired",
|
||||
"touched",
|
||||
"uncomfortable",
|
||||
"weird",
|
||||
"working",
|
||||
"worried",
|
||||
] as const;
|