add more blog styles and fix fic styles
|
@ -1,4 +1,3 @@
|
||||||
{
|
{
|
||||||
"deno.enable": true,
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
import { defineAction, ActionError } from "astro:actions";
|
import { defineAction, ActionError } from "astro:actions";
|
||||||
import { db, Guestbook } from "astro:db";
|
import { db, Guestbook } from "astro:db";
|
||||||
import { z } from "astro:schema";
|
import { z } from "astro:schema";
|
||||||
import { checkProfanity } from "./utils";
|
|
||||||
|
|
||||||
export const server = {
|
export const server = {
|
||||||
guestbook: defineAction({
|
guestbook: defineAction({
|
||||||
|
@ -10,7 +9,7 @@ export const server = {
|
||||||
username: z.string(),
|
username: z.string(),
|
||||||
website: z.string().url().optional(),
|
website: z.string().url().optional(),
|
||||||
body: z.string(),
|
body: z.string(),
|
||||||
honeypot: z.string().max(0).nullish(),
|
password: z.string().regex(/\biliad/g),
|
||||||
}),
|
}),
|
||||||
handler: async (input) => {
|
handler: async (input) => {
|
||||||
if (input.username === "") {
|
if (input.username === "") {
|
||||||
|
@ -27,26 +26,18 @@ export const server = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.honeypot !== undefined) {
|
if (input.password !== "iliad") {
|
||||||
throw new ActionError({
|
throw new ActionError({
|
||||||
code: "UNPROCESSABLE_CONTENT",
|
code: "UNPROCESSABLE_CONTENT",
|
||||||
message: "Oh dear, something went wrong!",
|
message: "Whoops, something went wrong!",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter = await checkProfanity(input.body);
|
|
||||||
if (filter) {
|
|
||||||
return await db.insert(Guestbook).values({
|
return await db.insert(Guestbook).values({
|
||||||
username: input.username,
|
username: input.username,
|
||||||
website: input.website,
|
website: input.website,
|
||||||
body: input.body,
|
body: input.body,
|
||||||
}).returning();
|
}).returning();
|
||||||
} else {
|
|
||||||
throw new ActionError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "You can't curse!",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
|
@ -1,29 +0,0 @@
|
||||||
export async function checkProfanity(message: string) {
|
|
||||||
try {
|
|
||||||
const response = await fetch("https://vector.profanity.dev", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ message }),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`There was an error checking your message: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = await response.json();
|
|
||||||
|
|
||||||
if (json.isProfanity) {
|
|
||||||
throw new Error("Please don't cuss!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkSpam(form: FormData) {
|
|
||||||
const token = form.append("procaptcha-response", import.meta.env.SPAM_SITE_KEY);
|
|
||||||
|
|
||||||
}
|
|
Before Width: | Height: | Size: 585 B After Width: | Height: | Size: 429 B |
Before Width: | Height: | Size: 532 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 349 B |
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 310 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 945 B |
Before Width: | Height: | Size: 916 B After Width: | Height: | Size: 313 B |
Before Width: | Height: | Size: 790 B |
Before Width: | Height: | Size: 785 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 948 B After Width: | Height: | Size: 336 B |
Before Width: | Height: | Size: 541 B After Width: | Height: | Size: 319 B |
Before Width: | Height: | Size: 825 B |
Before Width: | Height: | Size: 853 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 559 B |
Before Width: | Height: | Size: 544 B |
Before Width: | Height: | Size: 796 B |
Before Width: | Height: | Size: 831 B |
Before Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 840 B |
Before Width: | Height: | Size: 848 B |
Before Width: | Height: | Size: 839 B |
Before Width: | Height: | Size: 557 B After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 877 B |
Before Width: | Height: | Size: 866 B |
Before Width: | Height: | Size: 826 B After Width: | Height: | Size: 302 B |
Before Width: | Height: | Size: 871 B |
Before Width: | Height: | Size: 847 B |
Before Width: | Height: | Size: 802 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 560 B After Width: | Height: | Size: 297 B |
Before Width: | Height: | Size: 868 B After Width: | Height: | Size: 331 B |
|
@ -10,6 +10,8 @@
|
||||||
--sans-font: "MLSS", 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-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;
|
--arial-font: "Arial Pixel", Arial, Helvetica, sans-serif;
|
||||||
--pmd-font: "Wonder Mail", Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
|
--pmd-font: "Wonder Mail", Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
|
||||||
|
--mplus-10-font: "PixelMPlus 10", "MS Gothic", system-ui-ja, system-ui-zh-cn, system-ui-zh-tw, system-ui-zh-hk, system-ui-ko, sans-serif;
|
||||||
|
--mplus-12-font: "PixelMPlus 12", "MS Gothic", system-ui-ja, system-ui-zh-cn, system-ui-zh-tw, system-ui-zh-hk, system-ui-ko, sans-serif;
|
||||||
|
|
||||||
@media screen and (prefers-color-scheme: light) {
|
@media screen and (prefers-color-scheme: light) {
|
||||||
--bg-color: #e6f2ef;
|
--bg-color: #e6f2ef;
|
||||||
|
|
|
@ -28,6 +28,30 @@
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "PixelMPlus 10";
|
||||||
|
src: url("/fonts/PixelMplus10-Regular.woff2") format("woff");
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "PixelMPlus 10";
|
||||||
|
src: url("/fonts/PixelMplus10-Bold.woff2") format("woff");
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "PixelMPlus 12";
|
||||||
|
src: url("/fonts/PixelMplus12-Regular.woff2") format("woff");
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "PixelMPlus 12";
|
||||||
|
src: url("/fonts/PixelMplus12-Bold.woff2") format("woff");
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
@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");
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
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 MarkdownIt from "markdown-it";
|
||||||
import moods from "@/utils/moods";
|
import moods from "@/utils/moods";
|
||||||
|
const parser = new MarkdownIt({
|
||||||
|
html: true,
|
||||||
|
breaks: true,
|
||||||
|
});
|
||||||
|
|
||||||
const blog = defineCollection({
|
const blog = defineCollection({
|
||||||
loader: glob({ pattern: "*.md", base: "./src/content/blog" }),
|
loader: glob({ pattern: "*.md", base: "./src/content/blog" }),
|
||||||
|
@ -16,32 +21,32 @@ const blog = defineCollection({
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
function generateFicSlug({ entry, data }: { entry: string, data: any }): string {
|
|
||||||
if (data.slug) {
|
|
||||||
return data.slug as string;
|
|
||||||
}
|
|
||||||
return entry.split("/")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const source = "./src/content/fics";
|
const source = "./src/content/fics";
|
||||||
const chapters = defineCollection({
|
const chapters = defineCollection({
|
||||||
loader: glob({ pattern: "**/*.{md,mdx,mdoc}", base: source }),
|
loader: glob({ pattern: "**/*.{md,mdx,mdoc}", base: source }),
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
publishedAt: z.coerce.date(),
|
publishedAt: z.coerce.date(),
|
||||||
notes: z.ostring(),
|
notes: z.ostring().transform(notes => parser.renderInline(notes ?? "", {})),
|
||||||
lastModified: z.coerce.date().optional(),
|
lastModified: z.coerce.date().optional(),
|
||||||
sortOrder: z.number(),
|
sortOrder: z.number(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const fics = defineCollection({
|
const fics = defineCollection({
|
||||||
loader: glob({ pattern: "**/*.{yml,yaml}", base: source, generateId: generateFicSlug }),
|
loader: glob({
|
||||||
|
pattern: "**/*.{yml,yaml}",
|
||||||
|
base: source,
|
||||||
|
generateId: ({entry, data}) => {
|
||||||
|
if (data.slug) return data.slug as string;
|
||||||
|
return entry.split("/")[0];
|
||||||
|
}
|
||||||
|
}),
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
series: z.array(z.string()),
|
series: z.array(z.string()),
|
||||||
publishedAt: z.coerce.date(),
|
publishedAt: z.coerce.date(),
|
||||||
summary: z.string(),
|
summary: z.string().transform(summary => parser.renderInline(summary, {})),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ title: not a sin
|
||||||
publishedAt: 2024-02-01
|
publishedAt: 2024-02-01
|
||||||
sortOrder: 1
|
sortOrder: 1
|
||||||
notes:
|
notes:
|
||||||
hello is this is a <em>test</em>, this is going to be a really long text! this is where all the author's notes will go.
|
hello is this is a *test*, this is going to be a really long text! this is where all the author's notes will go.
|
||||||
|
|
||||||
fhi
|
fhi
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,8 +3,9 @@ title: this IS a sin
|
||||||
publishedAt: 2024-03-02
|
publishedAt: 2024-03-02
|
||||||
sortOrder: 2
|
sortOrder: 2
|
||||||
notes:
|
notes:
|
||||||
<p>hey there this is just a test </p>
|
hey there this is just a test
|
||||||
<p>him!!! <em>lol!!!</em></p>
|
|
||||||
|
him!!! *lol!!!*
|
||||||
---
|
---
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis **reprehenderit** provident ullam sint *explicabo* quas esse velit, voluptatum eveniet, tempora illum expedita, eum voluptate! Odio excepturi similique ex quos tenetur.
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis **reprehenderit** provident ullam sint *explicabo* quas esse velit, voluptatum eveniet, tempora illum expedita, eum voluptate! Odio excepturi similique ex quos tenetur.
|
||||||
|
|
||||||
|
|
|
@ -81,30 +81,32 @@ dayjs.extend(utc);
|
||||||
|
|
||||||
{currently && (
|
{currently && (
|
||||||
<aside>
|
<aside>
|
||||||
|
<h2>Current</h2>
|
||||||
{currently?.mood && (
|
{currently?.mood && (
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Current mood</dt>
|
<dt class="title">Mood</dt>
|
||||||
<dd>
|
<dd class="desc">
|
||||||
<Image src={`/src/assets/moods/${moods.find(mood => mood === currently?.mood)}.png`} width="32" height="32" alt={currently.mood} />
|
<Image src={`/src/assets/moods/${moods.find(mood => mood === currently?.mood)}.png`} width={36} height={36} alt="" />
|
||||||
|
{currently.mood}
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
)}
|
)}
|
||||||
{currently.playing && (
|
{currently.playing && (
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Currently playing</dt>
|
<dt class="title">Game</dt>
|
||||||
<dd>{currently.playing}</dd>
|
<dd class="desc">{currently.playing}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
)}
|
)}
|
||||||
{currently.watching && (
|
{currently.watching && (
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Currently watching</dt>
|
<dt class="title">Show</dt>
|
||||||
<dd>{currently.watching}</dd>
|
<dd class="desc">{currently.watching}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
)}
|
)}
|
||||||
{currently.reading && (
|
{currently.reading && (
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Currently reading</dt>
|
<dt class="title">Book</dt>
|
||||||
<dd>{currently.reading}</dd>
|
<dd class="desc">{currently.reading}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
)}
|
)}
|
||||||
{currently.listening && (
|
{currently.listening && (
|
||||||
|
@ -135,13 +137,18 @@ dayjs.extend(utc);
|
||||||
--bg-7: #ccddee;
|
--bg-7: #ccddee;
|
||||||
--bg-8: #8888bb;
|
--bg-8: #8888bb;
|
||||||
--border-0: #000;
|
--border-0: #000;
|
||||||
--border-1: #6699aa;
|
--border-1: #334466;
|
||||||
--border-2: #7799bb;
|
--border-2: #6699aa;
|
||||||
--border-3: #88aabb;
|
--border-3: #7799bb;
|
||||||
--border-4: #99bbcc;
|
--border-4: #88aabb;
|
||||||
--border-5: #bbccdd;
|
--border-5: #99bbcc;
|
||||||
|
--border-6: #bbccdd;
|
||||||
}
|
}
|
||||||
h1 { font: bold 1rem var(--arial-font); }
|
h1 { font: bold 1rem var(--arial-font); }
|
||||||
|
h2 {
|
||||||
|
font: normal 1.5rem var(--mplus-12-font);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
main {
|
main {
|
||||||
font: 1rem var(--arial-font);
|
font: 1rem var(--arial-font);
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -172,17 +179,19 @@ dayjs.extend(utc);
|
||||||
background-color: var(--bg-3);
|
background-color: var(--bg-3);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
margin: 18px 6px;
|
margin: 18px 6px;
|
||||||
border: 2px solid var(--border-5);
|
border: 2px solid var(--border-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
padding: 6px 2px;
|
padding: 6px 2px;
|
||||||
border-block: 2px solid var(--border-2);
|
border-block: 2px solid var(--border-3);
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: var(--bg-8);
|
background-color: var(--bg-8);
|
||||||
color: var(--bg-0);
|
color: var(--bg-0);
|
||||||
|
font-weight: normal;
|
||||||
|
text-transform: uppercase;
|
||||||
padding: 5px 2px;
|
padding: 5px 2px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -200,6 +209,8 @@ dayjs.extend(utc);
|
||||||
border-bottom: 2px solid var(--bg-4);
|
border-bottom: 2px solid var(--bg-4);
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
|
font-family: var(--mplus-10-font);
|
||||||
|
font-size: 1.254rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -219,7 +230,7 @@ dayjs.extend(utc);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
a { background-color: var(--bg-7); }
|
a { background-color: var(--bg-7); }
|
||||||
time { background-color: var(--border-5); }
|
time { background-color: var(--border-6); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:last-child { border: none; }
|
&:last-child { border: none; }
|
||||||
|
@ -232,7 +243,7 @@ dayjs.extend(utc);
|
||||||
|
|
||||||
&:first-child { margin-top: 4px; }
|
&:first-child { margin-top: 4px; }
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: 2px solid var(--border-1);
|
border-bottom: 2px solid var(--border-2);
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,13 +262,25 @@ dayjs.extend(utc);
|
||||||
padding: 36px 16px 74px 20px;
|
padding: 36px 16px 74px 20px;
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
border: 2px solid var(--border-1);
|
border: 2px solid var(--border-2);
|
||||||
border-image: var(--innerBorder) 3 2 2 fill / 6px 4px 4px;
|
border-image: var(--innerBorder) 3 2 2 fill / 6px 4px 4px;
|
||||||
margin: 14px 4px 8px;
|
margin: 14px 4px 8px;
|
||||||
padding: 4px 2px 2px;
|
padding: 4px 2px 2px;
|
||||||
background-color: var(--bg-0);
|
background-color: var(--bg-0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font: normal 1.5rem var(--mplus-12-font);
|
||||||
|
background-color: var(--bg-2);
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
background-color: var(--bg-4);
|
||||||
|
padding: 4px 6px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
border-bottom: 2px solid var(--bg-6);
|
border-bottom: 2px solid var(--bg-6);
|
||||||
|
|
||||||
|
@ -265,39 +288,26 @@ dayjs.extend(utc);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 6px 2px;
|
margin: 6px 2px;
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
background-color: var(--border-5);
|
background-color: var(--border-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
border-top-color: var(--bg-2);
|
border-color: var(--bg-2) var(--bg-2) var(--bg-1) var(--bg-1);
|
||||||
border-right-color: var(--bg-2);
|
|
||||||
border-left-color: var(--bg-1);
|
|
||||||
border-bottom-color: var(--bg-1);
|
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
|
|
||||||
time, .section {
|
time, .section {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
|
||||||
background-color: var(--bg-2);
|
|
||||||
padding: 4px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
background-color: var(--bg-4);
|
|
||||||
padding: 4px 6px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
background-color: var(--border-4);
|
background-color: var(--border-5);
|
||||||
border: none;
|
border: none;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
}
|
}
|
||||||
|
@ -328,27 +338,39 @@ dayjs.extend(utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
border-top: 2px solid var(--border-3);
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
grid-template-rows: repeat(4, auto);
|
||||||
|
border-top: 2px solid var(--border-4);
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
grid-area: 1 / 1 / -1;
|
||||||
|
padding-top: -2px;
|
||||||
|
padding-left: 2px;
|
||||||
|
height: 100%;
|
||||||
|
border-top: 2px solid var(--border-1);
|
||||||
|
border-right: 2px solid var(--border-4);
|
||||||
|
background-color: var(--bg-7);
|
||||||
|
}
|
||||||
|
|
||||||
dl {
|
dl {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-bottom: 2px solid var(--border-3);
|
border-bottom: 2px solid var(--border-4);
|
||||||
|
border-top: 2px solid var(--border-1);
|
||||||
|
border-left: 2px solid var(--border-1);
|
||||||
|
|
||||||
dt {
|
&:first-of-type > dd {
|
||||||
background-color: var(--bg-2);
|
display: flex;
|
||||||
padding: 4px 6px;
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin: -2px;
|
||||||
|
padding: 0 6px;
|
||||||
}
|
}
|
||||||
|
&:last-child { border-bottom: none; }
|
||||||
dd {
|
|
||||||
background-color: var(--bg-4);
|
|
||||||
padding: 4px 6px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child > dt ~ dd { padding: 0 6px; }
|
|
||||||
&:last-child { border: none; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
ficTitle: string;
|
ficTitle: string;
|
||||||
date: Date;
|
date: Date;
|
||||||
notes?: string;
|
notes?: any;
|
||||||
lastModified?: Date;
|
lastModified?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ dayjs.extend(utc);
|
||||||
)}
|
)}
|
||||||
{notes && (
|
{notes && (
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<Fragment set:html={notes.split("\n").join("<br />")} />
|
<Fragment set:html={notes} />
|
||||||
</blockquote>
|
</blockquote>
|
||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -38,7 +38,7 @@ dayjs.extend(utc);
|
||||||
<div>
|
<div>
|
||||||
<span class="title">Summary</span>
|
<span class="title">Summary</span>
|
||||||
<blockquote class="data">
|
<blockquote class="data">
|
||||||
<Fragment set:html={fic.data.summary.split("\n").join("<br />")} />
|
<Fragment set:html={fic.data.summary} />
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
|
|
|
@ -14,7 +14,9 @@ export const GET: APIRoute = async (context) => {
|
||||||
const fic = fics.find(({ id }) => id === context.params.ficId);
|
const fic = fics.find(({ id }) => id === context.params.ficId);
|
||||||
return rss({
|
return rss({
|
||||||
title: `${fic?.data.title}`,
|
title: `${fic?.data.title}`,
|
||||||
description: `${fic?.data.summary}`,
|
description: sanitize(parser.render(fic?.data.summary!), {
|
||||||
|
allowedTags: sanitize.defaults.allowedTags.concat(["br"]),
|
||||||
|
}),
|
||||||
site: context.site!,
|
site: context.site!,
|
||||||
items: chapters.map(chapter => ({
|
items: chapters.map(chapter => ({
|
||||||
link: `/fics/${chapter.id}`,
|
link: `/fics/${chapter.id}`,
|
||||||
|
@ -23,7 +25,7 @@ export const GET: APIRoute = async (context) => {
|
||||||
content: sanitize(parser.render(chapter.body!), {
|
content: sanitize(parser.render(chapter.body!), {
|
||||||
allowedTags: sanitize.defaults.allowedTags.concat(["img"]),
|
allowedTags: sanitize.defaults.allowedTags.concat(["img"]),
|
||||||
}),
|
}),
|
||||||
categories: fic?.data.series,
|
categories: fic?.data.series.concat(fic.data.title),
|
||||||
})),
|
})),
|
||||||
stylesheet: "/pretty-feed-v3.xsl",
|
stylesheet: "/pretty-feed-v3.xsl",
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,10 +18,15 @@ entries.sort((a, b) => b.date.valueOf() - a.date.valueOf());
|
||||||
<ThemeSwitch />
|
<ThemeSwitch />
|
||||||
<main>
|
<main>
|
||||||
<h1>Guestbook</h1>
|
<h1>Guestbook</h1>
|
||||||
|
<details>
|
||||||
|
<summary>click me for a secret!</summary>
|
||||||
|
<noscript>you should put ILIAD, but in all lowercase, in the password field!</noscript>
|
||||||
|
<p id="cool-surprise"></p>
|
||||||
|
</details>
|
||||||
|
|
||||||
<form action={actions.guestbook} method="post">
|
<form action={actions.guestbook} method="post">
|
||||||
<label for="username">Username</label>
|
<label for="username">Username</label>
|
||||||
<input type="text" name="username" id="username" required aria-describedby="username-error" />
|
<input type="text" name="username" id="username" aria-describedby="username-error" required />
|
||||||
{errors.username && <p id="username-error">{errors.username.join(",")}</p>}
|
{errors.username && <p id="username-error">{errors.username.join(",")}</p>}
|
||||||
|
|
||||||
<label for="website">Website (optional)</label>
|
<label for="website">Website (optional)</label>
|
||||||
|
@ -29,23 +34,26 @@ entries.sort((a, b) => b.date.valueOf() - a.date.valueOf());
|
||||||
{errors.website && <p id="website-error">{errors.website.join(",")}</p>}
|
{errors.website && <p id="website-error">{errors.website.join(",")}</p>}
|
||||||
|
|
||||||
<label for="body">Message</label>
|
<label for="body">Message</label>
|
||||||
<textarea name="body" id="body" rows="5" required aria-describedby="body-error"></textarea>
|
<textarea name="body" id="body" rows="5" aria-describedby="body-error" required></textarea>
|
||||||
{errors.body && <p id="body-error">{errors.body.join(",")}</p>}
|
{errors.body && <p id="body-error">{errors.body.join(",")}</p>}
|
||||||
|
|
||||||
<input type="hidden" name="honeypot" tabindex="-1" autocomplete="off" style="display:none" />
|
<label for="password">Do you know the password?</label>
|
||||||
<button type="submit">Post</button>
|
<input type="text" name="password" id="password" value="yes!" aria-describedby="password-error" required />
|
||||||
|
{errors.password && <p id="password-error">{errors.password.join(",")}</p>}
|
||||||
|
|
||||||
{errors.honeypot && <p>{errors.honeypot.join(",")}</p>}
|
<button type="submit">Post</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{entries.map(({ username, website, body, date }) => (
|
{entries.map(({ username, website, body, date }) => (
|
||||||
<article class="entry">
|
<article class="entry">
|
||||||
|
<header>
|
||||||
<h1>{username}</h1>
|
<h1>{username}</h1>
|
||||||
{website && <p><a href={website} target="_blank" referrerpolicy="no-referrer">website</a></p>}
|
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||||
<time datetime={dayjs(date).utc(true).toISOString()}>{dayjs(date).utc(true).format("MMMM DD, YYYY")}</time>
|
Posted on {dayjs(date).utc(true).format("MMMM DD, YYYY")}
|
||||||
<div>
|
</time>
|
||||||
|
{website && <a href={website} target="_blank" referrerpolicy="no-referrer">website</a>}
|
||||||
|
</header>
|
||||||
{body}
|
{body}
|
||||||
</div>
|
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</main>
|
</main>
|
||||||
|
@ -81,4 +89,34 @@ entries.sort((a, b) => b.date.valueOf() - a.date.valueOf());
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
& > summary {
|
||||||
|
list-style: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "[+]";
|
||||||
|
}
|
||||||
|
|
||||||
|
[open] &::after {
|
||||||
|
content: "[_]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const details = document.getElementsByTagName("details").item(0);
|
||||||
|
const pw = document.getElementById("cool-surprise");
|
||||||
|
let initial = "vyvnq";
|
||||||
|
details?.addEventListener("toggle", e => {
|
||||||
|
if ((e.target as HTMLDetailsElement).open) {
|
||||||
|
pw!.innerHTML = `if you read this, put <strong>${initial.replace(/[a-zA-Z]/g, c => {
|
||||||
|
// @ts-expect-error the below code works
|
||||||
|
return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
|
||||||
|
})}</strong> for the password field!`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -1,3 +1,15 @@
|
||||||
|
function aliasMood(emotion: string) {
|
||||||
|
switch (emotion) {
|
||||||
|
case "aggravated":
|
||||||
|
case "annoyed":
|
||||||
|
case "devious":
|
||||||
|
return "angry";
|
||||||
|
case "apathetic":
|
||||||
|
|
||||||
|
return "blah";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
"accomplished",
|
"accomplished",
|
||||||
"aggravated",
|
"aggravated",
|
||||||
|
|