recreate maplestory guild bbs layout
|
@ -2,10 +2,14 @@
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
import db from '@astrojs/db';
|
import db from '@astrojs/db';
|
||||||
import node from '@astrojs/node';
|
import node from '@astrojs/node';
|
||||||
|
import { modifiedTime } from './last-modified.mjs';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://haetae.gay",
|
site: "https://haetae.gay",
|
||||||
|
markdown: {
|
||||||
|
remarkPlugins: [modifiedTime],
|
||||||
|
},
|
||||||
integrations: [db()],
|
integrations: [db()],
|
||||||
adapter: node({
|
adapter: node({
|
||||||
mode: 'standalone'
|
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",
|
"@astrojs/rss": "^4.0.11",
|
||||||
"astro": "^5.1.7",
|
"astro": "^5.1.7",
|
||||||
"astro-breadcrumbs": "^3.3.1",
|
"astro-breadcrumbs": "^3.3.1",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"sanitize-html": "^2.14.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;
|
--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;
|
--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;
|
--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 {
|
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-face {
|
||||||
font-family: "Departure Mono";
|
font-family: "Departure Mono";
|
||||||
src: url("/fonts/DepartureMono-Regular.woff2") format("woff2");
|
src: url("/fonts/DepartureMono-Regular.woff2") format("woff2");
|
||||||
|
@ -10,6 +22,12 @@
|
||||||
font-weight: normal;
|
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-face {
|
||||||
font-family: "Redaction 35";
|
font-family: "Redaction 35";
|
||||||
src: url("/fonts/Redaction_35-Regular.woff2") format("woff2");
|
src: url("/fonts/Redaction_35-Regular.woff2") format("woff2");
|
||||||
|
|
|
@ -5,13 +5,14 @@ interface Props {
|
||||||
imagePath: string;
|
imagePath: string;
|
||||||
alt: string;
|
alt: string;
|
||||||
caption?: 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}");
|
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}"`);
|
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} />
|
<Image src={images[imagePath]()} {alt} />
|
||||||
<figcaption>{caption ?? alt}</figcaption>
|
{caption && <figcaption>{caption}</figcaption>}
|
||||||
</figure>
|
</figure>
|
|
@ -1,6 +1,11 @@
|
||||||
---
|
---
|
||||||
title: hey girl hey
|
title: hey girl hey
|
||||||
pubDate: 2024-02-03
|
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.
|
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 { defineCollection, z } from "astro:content";
|
||||||
import { glob } from "astro/loaders";
|
import { glob } from "astro/loaders";
|
||||||
import { rssSchema } from "@astrojs/rss";
|
import { rssSchema } from "@astrojs/rss";
|
||||||
|
import moods from "@/utils/moods";
|
||||||
|
|
||||||
const blog = defineCollection({
|
const blog = defineCollection({
|
||||||
loader: glob({ pattern: "*.md", base: "./src/content/blog" }),
|
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 {
|
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 Navbar from "@/components/Navbar.astro";
|
||||||
|
import Figure from "@/components/Figure.astro";
|
||||||
import Layout from "./Layout.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>
|
<Layout>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<slot />
|
{frontmatter.avatar && frontmatter.avatarText && (
|
||||||
|
<Figure className="avatar" imagePath={frontmatter.avatar} alt={frontmatter.avatarText} />
|
||||||
|
)}
|
||||||
|
<article>
|
||||||
|
<slot />
|
||||||
|
</article>
|
||||||
</main>
|
</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 { getCollection } from "astro:content";
|
||||||
import Layout from "./Layout.astro";
|
import Layout from "./Layout.astro";
|
||||||
import Navbar from "@/components/Navbar.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 {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
date: Date;
|
date: Date;
|
||||||
|
currently?: {
|
||||||
|
mood?: string;
|
||||||
|
reading?: string;
|
||||||
|
listening?: string;
|
||||||
|
watching?: string;
|
||||||
|
playing?: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const blog = await getCollection("blog");
|
const blog = await getCollection("blog");
|
||||||
blog.length = Math.min(blog.length, 5);
|
blog.length = Math.min(blog.length, 5);
|
||||||
blog.sort((a, b) => a.data.pubDate!.valueOf() - b.data.pubDate!.valueOf());
|
blog.sort((a, b) => a.data.pubDate!.valueOf() - b.data.pubDate!.valueOf());
|
||||||
const { title, date } = Astro.props;
|
const { title, date, currently } = Astro.props;
|
||||||
---
|
---
|
||||||
<Layout>
|
<Layout>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<section>
|
<main>
|
||||||
<nav id="blog-links">
|
<nav id="blog-links">
|
||||||
<h1>recent posts</h1>
|
<h1>recent posts</h1>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -36,33 +47,80 @@ const { title, date } = Astro.props;
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main>
|
<section>
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<div class="inner">
|
||||||
<h1>{title}</h1>
|
<header>
|
||||||
<time datetime={date.toISOString()}>
|
<h1>{title}</h1>
|
||||||
Posted on
|
<hr />
|
||||||
{date.toLocaleDateString(undefined, { dateStyle: "long" })}
|
<div class="info">
|
||||||
</time>
|
<time datetime={date.toISOString()}>
|
||||||
</header>
|
<span class="title">Date</span>
|
||||||
|
<span class="desc">{date.toLocaleDateString(undefined, { dateStyle: "long" })}</span>
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<slot />
|
<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>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<slot name="pagination" />
|
<slot name="pagination" />
|
||||||
</main>
|
</section>
|
||||||
</section>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style>
|
<style define:vars={{ outerBorder: `url(${outerBBS.src})`, innerBorder: `url(${innerBBS.src})` }}>
|
||||||
section {
|
main {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, auto);
|
grid-template-columns: repeat(2, auto);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
height: calc(100% - 2.5rem);
|
height: calc(100% - 2.5rem);
|
||||||
|
|
||||||
|
@media screen and (max-width: 1270px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#blog-links {
|
#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;
|
border-bottom: 2px solid black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
section {
|
||||||
width: 75ch;
|
width: 75ch;
|
||||||
|
|
||||||
@media screen and (max-width: 950px) {
|
@media screen and (max-width: 1270px) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 1rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
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 {
|
header {
|
||||||
padding: 1rem;
|
border-bottom: 2px solid #ccccdd;
|
||||||
padding-left: 2rem;
|
|
||||||
|
|
||||||
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) {
|
.info {
|
||||||
padding: 0.25rem;
|
display: flex;
|
||||||
padding-left: 2rem;
|
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 {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-image: linear-gradient(#d1d5db 2px, transparent 0px);
|
background-image: linear-gradient(to right, #fff 5px, transparent 2px), linear-gradient(#d1d5db 2px, transparent 2px);
|
||||||
background-size: 100% 1em;
|
background-size: 10px 2em;
|
||||||
background-position-y: 1.75rem;
|
background-position-y: 1.75rem;
|
||||||
|
margin: 0 2rem 2rem;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
font-size: 2rem;
|
line-height: 2em;
|
||||||
line-height: 1em;
|
margin-trim: block;
|
||||||
border: 2px solid black;
|
|
||||||
|
|
||||||
p { margin-block-end: 1em; }
|
p { margin-block: 1lh; }
|
||||||
&::before, &::after {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 100%;
|
|
||||||
left: 30px;
|
|
||||||
content: "";
|
|
||||||
border: 20px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before { border-bottom: 20px solid black; }
|
@supports not (margin-trim: block) {
|
||||||
&::after {
|
:first-child { margin-block-start: 0; }
|
||||||
border-bottom: 20px solid white;
|
:last-child { margin-block-end: 0; }
|
||||||
margin-bottom: -3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 950px) {
|
@media screen and (max-width: 950px) {
|
||||||
background-position-y: 0.8rem;
|
background-position-y: 0.8rem;
|
||||||
padding: 1rem;
|
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 Layout from "./Layout.astro";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import utc from "dayjs/plugin/utc";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
date: Date;
|
date: Date;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
lastModified?: string;
|
lastModified?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, date, notes, lastModified }: Props = Astro.props;
|
const { title, date, notes, lastModified }: Props = Astro.props;
|
||||||
|
dayjs.extend(utc);
|
||||||
---
|
---
|
||||||
<Layout>
|
<Layout>
|
||||||
<slot name="breadcrumbs" />
|
<slot name="breadcrumbs" />
|
||||||
|
@ -16,13 +19,13 @@ const { title, date, notes, lastModified }: Props = Astro.props;
|
||||||
<main>
|
<main>
|
||||||
<header>
|
<header>
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<time datetime={date.toISOString()}>
|
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||||
{date.toLocaleDateString(undefined, {
|
Published on {dayjs(date).utc(true).format("MMMM DD, YYYY")}
|
||||||
weekday: "long", year: "numeric", month: "long", day: "numeric",
|
|
||||||
})}
|
|
||||||
</time>
|
</time>
|
||||||
{lastModified && (
|
{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 && (
|
{notes && (
|
||||||
<blockquote><Fragment set:html={notes.split("\n").join("<br />")} /></blockquote>
|
<blockquote><Fragment set:html={notes.split("\n").join("<br />")} /></blockquote>
|
||||||
|
@ -45,15 +48,18 @@ const { title, date, notes, lastModified }: Props = Astro.props;
|
||||||
|
|
||||||
article, blockquote { font-family: var(--serif-font); }
|
article, blockquote { font-family: var(--serif-font); }
|
||||||
|
|
||||||
blockquote {
|
header {
|
||||||
margin: 1rem;
|
blockquote {
|
||||||
font-size: 1.125rem;
|
margin: 1rem;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
display: block;
|
display: block;
|
||||||
content: "Author’s Notes:";
|
content: "Author’s Notes:";
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
alt: "Author's Notes:";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +67,12 @@ const { title, date, notes, lastModified }: Props = Astro.props;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
line-height: 1.5;
|
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>
|
</style>
|
|
@ -16,6 +16,7 @@ const { title = "haetae" }: Props = Astro.props;
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<meta name="pinterest" content="nopin nohover" />
|
<meta name="pinterest" content="nopin nohover" />
|
||||||
|
<slot name="header" />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
---
|
---
|
||||||
title: about me
|
title: about me
|
||||||
layout: ../layouts/About.astro
|
layout: ../layouts/About.astro
|
||||||
|
avatar: /src/assets/images/portrait-0025.png
|
||||||
|
avatarText: pikachu from pokemon mystery dungeon
|
||||||
---
|
---
|
||||||
# hello!
|
# 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 previous = current + 1 === blog.length ? undefined : blog[current + 1];
|
||||||
const next = current === 0 ? 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 />
|
<Content />
|
||||||
|
|
||||||
{(previous || next) && (
|
{(previous || next) && (
|
||||||
|
|
|
@ -13,6 +13,7 @@ const getMonth = (id: number) => {
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
<Layout>
|
<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>
|
<h1>blog</h1>
|
||||||
<ul>
|
<ul>
|
||||||
{Object.entries(sorted).map(entry => (
|
{Object.entries(sorted).map(entry => (
|
||||||
|
@ -31,4 +32,5 @@ const getMonth = (id: number) => {
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
<a href="/blog/rss.xml">rss feed</a>
|
||||||
</Layout>
|
</Layout>
|
|
@ -3,6 +3,10 @@ import Chapter from "@/layouts/Chapter.astro";
|
||||||
import type { GetStaticPaths } from "astro";
|
import type { GetStaticPaths } from "astro";
|
||||||
import { getCollection, render } from "astro:content";
|
import { getCollection, render } from "astro:content";
|
||||||
import { Breadcrumbs } from "astro-breadcrumbs";
|
import { Breadcrumbs } from "astro-breadcrumbs";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import utc from "dayjs/plugin/utc";
|
||||||
|
|
||||||
|
dayjs.extend(utc);
|
||||||
|
|
||||||
export const getStaticPaths = (async () => {
|
export const getStaticPaths = (async () => {
|
||||||
const chapters = await getCollection("chapters");
|
const chapters = await getCollection("chapters");
|
||||||
|
@ -17,7 +21,7 @@ export const getStaticPaths = (async () => {
|
||||||
|
|
||||||
const { ficId, chapterId } = Astro.params;
|
const { ficId, chapterId } = Astro.params;
|
||||||
const { chapter } = Astro.props;
|
const { chapter } = Astro.props;
|
||||||
const { Content } = await render(chapter);
|
const { Content, remarkPluginFrontmatter } = await render(chapter);
|
||||||
|
|
||||||
const chapters = await getCollection("chapters", ({ id }) => {
|
const chapters = await getCollection("chapters", ({ id }) => {
|
||||||
return id.split("/")[0] === ficId;
|
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 current = chapters.findIndex(chapter => chapter.id === `${ficId}/${chapterId}`);
|
||||||
const next = current + 1 === chapters.length ? undefined : chapters[current + 1];
|
const next = current + 1 === chapters.length ? undefined : chapters[current + 1];
|
||||||
const previous = current === 0 ? undefined : chapters[current - 1];
|
const previous = current === 0 ? undefined : chapters[current - 1];
|
||||||
// lastmodified
|
const lastModified = new Date(remarkPluginFrontmatter.lastModified);
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
{ index: "last", text: chapter.data.title },
|
{ index: "last", text: chapter.data.title },
|
||||||
{ index: 2, text: fic[0].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">
|
<Fragment slot="breadcrumbs">
|
||||||
<Breadcrumbs id="breadcrumbs" customizeLinks={links} linkTextFormat="capitalized">
|
<Breadcrumbs id="breadcrumbs" customizeLinks={links} linkTextFormat="capitalized">
|
||||||
<Fragment slot="separator" set:text="/" />
|
<Fragment slot="separator" set:text="/" />
|
||||||
|
@ -99,6 +103,13 @@ const links = [
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#chapter-select {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
#previous {
|
#previous {
|
||||||
grid-area: 2 / 1 / 2 / 1;
|
grid-area: 2 / 1 / 2 / 1;
|
||||||
justify-self: left;
|
justify-self: left;
|
||||||
|
|
|
@ -19,6 +19,7 @@ chapters.length = Math.min(chapters.length, 5);
|
||||||
chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||||
---
|
---
|
||||||
<Layout>
|
<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>
|
<section>
|
||||||
<h1>{fic.data.title}</h1>
|
<h1>{fic.data.title}</h1>
|
||||||
<header class="info">
|
<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" })}
|
{post.data.publishedAt.toLocaleDateString(undefined, { dateStyle: "medium" })}
|
||||||
</time>
|
</time>
|
||||||
<a href={`/fics/${post.id}`}>{post.data.title}</a> in
|
<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>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</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;
|