move assets and implement meadow's guestbook
BIN
public/fonts/pixeab.woff2
Normal file
BIN
public/fonts/pixearg.woff2
Normal file
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 731 B After Width: | Height: | Size: 731 B |
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 699 B |
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 302 B |
@ -7,7 +7,7 @@
|
||||
--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;
|
||||
--sans-font: "Super Star", 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, 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;
|
||||
--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;
|
||||
|
@ -1,12 +1,12 @@
|
||||
@font-face {
|
||||
font-family: "Arial Pixel";
|
||||
src: url("/fonts/PIXEAR.woff2") format("woff2");
|
||||
src: url("/fonts/pixearg.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Arial Pixel";
|
||||
src: url("/fonts/PIXEAB.woff2") format("woff2");
|
||||
src: url("/fonts/pixeab.woff2") format("woff2");
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@ -23,33 +23,21 @@
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "MLSS";
|
||||
font-family: "Super Star";
|
||||
src: url("/fonts/mario-luigi-rpg-speech-text.woff2") format("woff2");
|
||||
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");
|
||||
src: url("/fonts/PixelMplus10-Regular.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "PixelMPlus 12";
|
||||
src: url("/fonts/PixelMplus12-Bold.woff2") format("woff");
|
||||
font-weight: bold;
|
||||
src: url("/fonts/PixelMplus12-Regular.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
@ -1,6 +1,9 @@
|
||||
---
|
||||
title: hmhmhm!
|
||||
pubDate: 2024-02-04
|
||||
currently:
|
||||
mood: sad
|
||||
reading: sad fanfic
|
||||
---
|
||||
|
||||
hey there this is a test
|
@ -1,11 +1,11 @@
|
||||
---
|
||||
import type { MarkdownLayoutProps } from "astro";
|
||||
import Layout from "./Layout.astro";
|
||||
import Navbar from "@/components/Navbar.astro";
|
||||
import Figure from "@/components/Figure.astro";
|
||||
import Navbar from "~/Navbar.astro";
|
||||
import Figure from "~/Figure.astro";
|
||||
|
||||
import border from "@/assets/border.png";
|
||||
import frame from "@/assets/frame.png";
|
||||
import border from "$/images/border.png";
|
||||
import frame from "$/images/frame.png";
|
||||
|
||||
type Props = MarkdownLayoutProps<{
|
||||
avatar?: string;
|
||||
|
@ -4,12 +4,12 @@ import { getCollection } from "astro:content";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import Layout from "./Layout.astro";
|
||||
import Navbar from "@/components/Navbar.astro";
|
||||
import Navbar from "~/Navbar.astro";
|
||||
import moods from "@/utils/moods";
|
||||
|
||||
import outerBBS from "@/assets/guild-bbs.png";
|
||||
import innerBBS from "@/assets/guild-bbs-content.png";
|
||||
import sideBBS from "@/assets/guild-bbs-list.png";
|
||||
import outerBBS from "$/images/guild-bbs.png";
|
||||
import innerBBS from "$/images/guild-bbs-content.png";
|
||||
import sideBBS from "$/images/guild-bbs-list.png";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@ -36,7 +36,7 @@ dayjs.extend(utc);
|
||||
<main>
|
||||
<nav id="blog-links">
|
||||
<div class="inner">
|
||||
<hgroup>
|
||||
<div class="nav-section">
|
||||
<h1><span class="title">recent posts</span></h1>
|
||||
<ul>
|
||||
{blog.map(entry => (
|
||||
@ -50,76 +50,74 @@ dayjs.extend(utc);
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</hgroup>
|
||||
<hgroup>
|
||||
</div>
|
||||
<div class="nav-section">
|
||||
<h1><span class="title">other links</span></h1>
|
||||
<ul>
|
||||
<li><span class="item"><a href="/blog">archive</a></span></li>
|
||||
<li><span class="item"><a href="/blog/rss.xml">rss feed</a></span></li>
|
||||
</ul>
|
||||
</hgroup>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<div class="inner">
|
||||
<header>
|
||||
<h1>{title}</h1>
|
||||
<hr />
|
||||
<div class="info">
|
||||
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||
<span class="title">Date</span>
|
||||
<span class="desc">{dayjs(date).utc(true).format("MMMM DD, YYYY")}</span>
|
||||
</time>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content" data-simplebar>
|
||||
<slot />
|
||||
<article>
|
||||
<div class="inner">
|
||||
<header>
|
||||
<h1>{title}</h1>
|
||||
<hr />
|
||||
<div class="info">
|
||||
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||
<span class="title">Date</span>
|
||||
<span class="desc">{dayjs(date).utc(true).format("MMMM DD, YYYY")}</span>
|
||||
</time>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{currently && (
|
||||
<aside>
|
||||
<h2>Current</h2>
|
||||
{currently?.mood && (
|
||||
<dl>
|
||||
<dt class="title">Mood</dt>
|
||||
<dd class="desc">
|
||||
<Image src={`/src/assets/moods/${moods.find(mood => mood === currently?.mood)}.png`} width={36} height={36} alt="" />
|
||||
{currently.mood}
|
||||
</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.playing && (
|
||||
<dl>
|
||||
<dt class="title">Game</dt>
|
||||
<dd class="desc">{currently.playing}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.watching && (
|
||||
<dl>
|
||||
<dt class="title">Show</dt>
|
||||
<dd class="desc">{currently.watching}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.reading && (
|
||||
<dl>
|
||||
<dt class="title">Book</dt>
|
||||
<dd class="desc">{currently.reading}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.listening && (
|
||||
<dl>
|
||||
<dt>Currently listening</dt>
|
||||
<dd>{currently.listening}</dd>
|
||||
</dl>
|
||||
)}
|
||||
</aside>
|
||||
)}
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
{currently && (
|
||||
<aside>
|
||||
<h2>Current</h2>
|
||||
{currently?.mood && (
|
||||
<dl>
|
||||
<dt class="title">Mood</dt>
|
||||
<dd class="desc">
|
||||
<Image src={`/src/assets/moods/${moods.find(mood => mood === currently?.mood)}.png`} width={36} height={36} alt="" />
|
||||
{currently.mood}
|
||||
</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.playing && (
|
||||
<dl>
|
||||
<dt class="title">Game</dt>
|
||||
<dd class="desc">{currently.playing}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.watching && (
|
||||
<dl>
|
||||
<dt class="title">Show</dt>
|
||||
<dd class="desc">{currently.watching}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.reading && (
|
||||
<dl>
|
||||
<dt class="title">Book</dt>
|
||||
<dd class="desc">{currently.reading}</dd>
|
||||
</dl>
|
||||
)}
|
||||
{currently.listening && (
|
||||
<dl>
|
||||
<dt>Currently listening</dt>
|
||||
<dd>{currently.listening}</dd>
|
||||
</dl>
|
||||
)}
|
||||
</aside>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
<slot name="pagination" />
|
||||
</main>
|
||||
</Layout>
|
||||
@ -238,7 +236,7 @@ dayjs.extend(utc);
|
||||
}
|
||||
}
|
||||
|
||||
hgroup {
|
||||
.nav-section {
|
||||
margin: 0 4px;
|
||||
background-color: var(--bg-0);
|
||||
|
||||
@ -317,7 +315,7 @@ dayjs.extend(utc);
|
||||
.content {
|
||||
position: relative;
|
||||
background-attachment: local;
|
||||
background-image: linear-gradient(to right, var(--bg-0) 5px, transparent 2px), linear-gradient(var(--bg-4) 2px, transparent 2px);
|
||||
background-image: linear-gradient(to right, var(--bg-0) 5px, transparent 2px), linear-gradient(var(--bg-4) 2px, transparent 2px);
|
||||
background-size: 10px 1lh;
|
||||
background-position-y: calc(2lh - 2px);
|
||||
margin: 2rem 1rem;
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
import "@/assets/styles/base.css";
|
||||
import "$/styles/base.css";
|
||||
|
||||
interface Props { title?: string; }
|
||||
|
||||
@ -16,9 +16,9 @@ const { title = "haetae" }: Props = Astro.props;
|
||||
<slot name="header" />
|
||||
<title>{title}</title>
|
||||
<script is:inline>
|
||||
localStorage.getItem("theme") ?
|
||||
document.documentElement.className = localStorage.getItem("theme") :
|
||||
document.documentElement.removeAttribute("class");
|
||||
localStorage.getItem("theme")
|
||||
? document.documentElement.className = localStorage.getItem("theme")
|
||||
: document.documentElement.removeAttribute("class");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -1,12 +1,28 @@
|
||||
---
|
||||
title: about me
|
||||
layout: ../layouts/About.astro
|
||||
avatar: /src/assets/portrait-0025.png
|
||||
avatar: /src/assets/images/portrait-0025.png
|
||||
avatarText: pikachu from pokemon mystery dungeon
|
||||
---
|
||||
# hello!
|
||||
|
||||
it's me, the weirdo. here's a bunch of my images:
|
||||
hey there, i go by haetae (or tae, for short) and i use he/him pronouns.
|
||||
|
||||
## badges...
|
||||
|
||||
# credits
|
||||
i used a bunch of assets that require attribution but i also wanted to link people to the authors because their works are great!
|
||||
|
||||
## images
|
||||
- [Pikachu portrait](https://sprites.pmdcollab.org/#/0025?form=0) by SPIKE CHUNSOFT, curated by the PMD Collab project
|
||||
- [This about page's frame images](https://www.spriters-resource.com/ds_dsi/pokemonmysterydungeonexplorersoftimedarkness/sheet/5986/) by redblueyellow
|
||||
- The blog's frame images and additional assets (such as the mood emoticons) were graciously provided by AymShade on the MapleLegends forums! I can't directly link because I'm scared of a Certain Company™ being evil
|
||||
- [The guestbook's speech bubble images](https://www.spriters-resource.com/game_boy_advance/mlss/sheet/7573/) by MajinPiccolo
|
||||
|
||||
## fonts
|
||||
- [Departure Mono](https://departuremono.com/) by [Helena Zhang](https://www.helenazhang.com/) is licensed under [OFL 1.1](https://www.tldrlegal.com/license/open-font-license-ofl-explained)
|
||||
- [Kiwi Soda](https://fontenddev.com/fonts/kiwi-soda/) from fontenddev.com is licensed under [CC by 4.0](https://creativecommons.org/licenses/by/4.0/)
|
||||
- [Mario & Luigi RPG Speech Text](https://fontstruct.com/fontstructions/show/1102228) by DarkMaxX is licensed under [CC by Share Alike 3.0](http://creativecommons.org/licenses/by-sa/3.0/)
|
||||
- [PixelMplus](https://itouhiro.hatenablog.com/entry/20130602/font) by itouhiro is licensed under [M+ font license](https://web.archive.org/web/20221024231351/http://mplus-fonts.osdn.jp/mplus-bitmap-fonts/#license)
|
||||
- [Redaction](https://www.redaction.us/) by [Forest Young](https://www.moma.org/interactives/exhibitions/2011/talktome/objects/140027/) and [Jeremy Mickel](https://mckltype.com/) is dual-licensed under [LGPL 2.1](https://www.tldrlegal.com/license/gnu-lesser-general-public-license-v2-1-lgpl-2-1) and [OFL 1.1](https://www.tldrlegal.com/license/open-font-license-ofl-explained)
|
||||
- [sq](https://github.com/leahneukirchen/sq) by [Leah Neukirchen](https://leahneukirchen.org/) is in public domain
|
||||
- [Wonder Mail](https://www.dafont.com/wonder-mail.font) by ShinxHijinx is free for personal use
|
@ -2,12 +2,12 @@
|
||||
import Blog from '@/layouts/Blog.astro';
|
||||
import type { GetStaticPaths } from 'astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import leftNormal from "@/assets/left-normal.png";
|
||||
import leftHover from "@/assets/left-hover.png";
|
||||
import leftActive from "@/assets/left-active.png";
|
||||
import rightNormal from "@/assets/right-normal.png";
|
||||
import rightHover from "@/assets/right-hover.png";
|
||||
import rightActive from "@/assets/right-active.png";
|
||||
import leftNormal from "$/images/left-normal.png";
|
||||
import leftHover from "$/images/left-hover.png";
|
||||
import leftActive from "$/images/left-active.png";
|
||||
import rightNormal from "$/images/right-normal.png";
|
||||
import rightHover from "$/images/right-hover.png";
|
||||
import rightActive from "$/images/right-active.png";
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const blog = await getCollection("blog");
|
||||
|
@ -1,65 +1,98 @@
|
||||
---
|
||||
import { actions, isInputError } from "astro:actions";
|
||||
import { db, Guestbook as table } from "astro:db";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import ThemeSwitch from "@/components/ThemeSwitch.astro";
|
||||
import ThemeSwitch from "~/ThemeSwitch.astro";
|
||||
import speech from "$/images/speech.png";
|
||||
|
||||
export const prerender = false;
|
||||
type GuestBookEntry = {
|
||||
ID: number;
|
||||
CreatedAt: string;
|
||||
UpdatedAt: string;
|
||||
DeletedAt: null | string;
|
||||
Name: string;
|
||||
Text: string;
|
||||
Website: null | string;
|
||||
Approved: true;
|
||||
GuestBookID: number;
|
||||
Guestbook: object;
|
||||
}
|
||||
|
||||
dayjs.extend(utc);
|
||||
const result = Astro.getActionResult(actions.guestbook);
|
||||
const errors = isInputError(result?.error) ? result.error.fields : {};
|
||||
const entries = await db.select().from(table);
|
||||
entries.sort((a, b) => b.date.valueOf() - a.date.valueOf());
|
||||
const response = await fetch("https://guestbooks.meadow.cafe/api/v1/get-guestbook-messages/500");
|
||||
const entries: Array<GuestBookEntry> = await response.json();
|
||||
entries.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valueOf());
|
||||
---
|
||||
<Layout title="haetae, guestbook">
|
||||
<ThemeSwitch />
|
||||
|
||||
<main>
|
||||
<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">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" name="username" id="username" aria-describedby="username-error" required />
|
||||
{errors.username && <p id="username-error">{errors.username.join(",")}</p>}
|
||||
<section id="form">
|
||||
<form action="https://guestbooks.meadow.cafe/guestbook/500/submit" method="post" aria-describedby="errors">
|
||||
<label for="name">Nickname</label>
|
||||
<input type="text" id="name" name="name" required />
|
||||
|
||||
<label for="website">Website (optional)</label>
|
||||
<input type="url" name="website" id="website" aria-describedby="website-error" />
|
||||
{errors.website && <p id="website-error">{errors.website.join(",")}</p>}
|
||||
<label for="website">Website (optional)</label>
|
||||
<input type="url" id="website" name="website" />
|
||||
|
||||
<label for="body">Message</label>
|
||||
<textarea name="body" id="body" rows="5" aria-describedby="body-error" required></textarea>
|
||||
{errors.body && <p id="body-error">{errors.body.join(",")}</p>}
|
||||
<label for="challengeQuestionAnswer">What's my name?</label>
|
||||
<input placeholder="read my about page!" type="text" id="challengeQuestionAnswer" name="challengeQuestionAnswer" required />
|
||||
|
||||
<label for="text">Message</label>
|
||||
<textarea placeholder="Message (plain text only)..." id="text" name="text" required></textarea>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
<div id="errors" role="alert"></div>
|
||||
|
||||
<label for="password">Do you know the password?</label>
|
||||
<input type="text" name="password" id="password" value="yes!" aria-describedby="password-error" required />
|
||||
{errors.password && <p id="password-error">{errors.password.join(",")}</p>}
|
||||
<aside>
|
||||
Lovingly made with <a href="https://guestbooks.meadow.cafe" target="_blank" referrerpolicy="no-referrer">Guestbooks</a>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<button type="submit">Post</button>
|
||||
</form>
|
||||
<section id="entries">
|
||||
<h2>Messages</h2>
|
||||
{entries.map(({ Name, Website, Text, CreatedAt}) => (
|
||||
<article class="entry">
|
||||
<header>
|
||||
<h1>
|
||||
{Website
|
||||
? <a href={Website} target="_blank" referrerpolicy="no-referrer">{Name}</a>
|
||||
: <span>{Name}</span>
|
||||
}
|
||||
</h1>
|
||||
<time datetime={dayjs(CreatedAt).utc(true).toISOString()}>
|
||||
Posted on {dayjs(CreatedAt).utc(true).format("MMMM DD, YYYY")}
|
||||
</time>
|
||||
</header>
|
||||
|
||||
{entries.map(({ username, website, body, date }) => (
|
||||
<article class="entry">
|
||||
<header>
|
||||
<h1>{username}</h1>
|
||||
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||
Posted on {dayjs(date).utc(true).format("MMMM DD, YYYY")}
|
||||
</time>
|
||||
{website && <a href={website} target="_blank" referrerpolicy="no-referrer">website</a>}
|
||||
</header>
|
||||
{body}
|
||||
</article>
|
||||
))}
|
||||
{Text}
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<button onclick="document.getElementById('notification').showModal()">open popup</button>
|
||||
<dialog id="notification">
|
||||
<form method="dialog">
|
||||
Successfully posted! Messages are manually approved. New entries should show up in a day or two! :)
|
||||
<menu>
|
||||
<button type="submit">Close</button>
|
||||
</menu>
|
||||
</form>
|
||||
</dialog>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
<style define:vars={{ border: `url(${speech.src})` }}>
|
||||
:root {
|
||||
--speech-bg-color: #f8f8f8;
|
||||
--speech-fg-color: #404040;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: clamp(75ch, 80ch, 100%);
|
||||
margin: 1rem auto;
|
||||
@ -80,43 +113,91 @@ entries.sort((a, b) => b.date.valueOf() - a.date.valueOf());
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
|
||||
input, textarea {
|
||||
margin-bottom: 1rem;
|
||||
padding: 2px 6px;
|
||||
#form {
|
||||
form {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
|
||||
input, textarea {
|
||||
margin-bottom: 1rem;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
aside {
|
||||
color: color-mix(in oklab, var(--fg-color) 80%, var(--bg-color));
|
||||
}
|
||||
}
|
||||
|
||||
details {
|
||||
& > summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
.entry {
|
||||
image-rendering: pixelated;
|
||||
font-family: var(--sans-font);
|
||||
font-size: calc(1rem * 2);
|
||||
letter-spacing: 1px;
|
||||
color: var(--speech-fg-color);
|
||||
background-color: var(--speech-bg-color);
|
||||
border-image: var(--border) 7 / 7px / 7px repeat;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "[+]";
|
||||
}
|
||||
|
||||
[open] &::after {
|
||||
content: "[_]";
|
||||
}
|
||||
#notification {
|
||||
margin: auto;
|
||||
max-width: 35ch;
|
||||
color: var(--fg-color);
|
||||
background: var(--bg-color);
|
||||
transition:
|
||||
display 1s allow-discrete,
|
||||
overlay 1s allow-discrete;
|
||||
animation: fadeOut 1s forwards;
|
||||
|
||||
menu {
|
||||
margin: 1em 0 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&[open] {
|
||||
animation: fadeIn 1.0s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
</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!`
|
||||
const form = document.forms[0];
|
||||
const challenge = form.elements["challengeQuestionAnswer" as any];
|
||||
const errors = document.getElementById("errors")!;
|
||||
const notification = document.getElementById("notification")! as HTMLDialogElement;
|
||||
|
||||
form.addEventListener("submit", async e => {
|
||||
e.preventDefault();
|
||||
let data = new FormData(form);
|
||||
const response = await fetch(form.action, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
});
|
||||
if (response.ok) {
|
||||
form.reset();
|
||||
notification.showModal();
|
||||
errors.innerHTML = "";
|
||||
} else {
|
||||
const err = await response.text();
|
||||
if (response.status === 401) {
|
||||
challenge.ariaInvalid = "true";
|
||||
errors.innerHTML = "The provided answer to the challenge question was invalid!";
|
||||
} else {
|
||||
errors.innerHTML = err;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -5,8 +5,9 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"~/*": ["src/components/*"],
|
||||
"$/*": ["src/assets/*"],
|
||||
"@/*": ["src/*"],
|
||||
}
|
||||
}
|
||||
}
|