add shippy and restructure index page

This commit is contained in:
haetae 2025-09-03 03:24:55 -04:00
parent 79a1b42269
commit f12810a424
17 changed files with 369 additions and 156 deletions

9
shippy.config.ts Normal file
View File

@ -0,0 +1,9 @@
export default {
host: {
host: "137.184.37.162",
username: "haetae",
remoteProjectPath: "/var/www/fujohost/haetae/",
remoteDbPath: "/var/www/fujohost/haetae/guestbook.db",
dbDriver: "astro:db"
}
};

View File

@ -7,11 +7,19 @@ export const guestbook = {
addEntry: defineAction({
accept: "form",
input: z.object({
username: z.string().min(1, "You should have a name!"),
username: z.string().nonempty("You should have a name!"),
website: z.string().url().optional(),
message: z.string().min(1, "Can't be that short..."),
message: z.string().nonempty("Can't be that short..."),
challenge: z.string().nonempty("Can't be empty!"),
}),
handler: async ({ username, website, message }) => {
handler: async ({ username, website, message, challenge }) => {
if (challenge !== "haetae") {
throw new ActionError({
code: "UNAUTHORIZED",
message: "Check the challenge question again!",
});
}
const addLine = message.replaceAll(/\r?\n/g, "<br />");
const sanitized = DOMPurify.sanitize(addLine);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

After

Width:  |  Height:  |  Size: 715 B

View File

@ -17,6 +17,7 @@ const { notes, lastModified }: Props = Astro.props;
)}
{notes && (
<blockquote>
<p class="author-note">Author's Notes:</p>
<Fragment set:html={notes} />
</blockquote>
)}
@ -39,12 +40,9 @@ const { notes, lastModified }: Props = Astro.props;
margin: 1rem;
font-size: calc(35px / 2);
&::before {
display: block;
content: "Authors Notes:";
font-weight: bold;
.author-note {
line-height: 1;
alt: "Author's Notes:";
font-weight: bold;
}
}

View File

@ -44,6 +44,7 @@ const entries = await db.select().from(Guestbook).orderBy(desc(Guestbook.publish
</article>}
</>
))}
{entries.length === 0 && (
<article>
<h1>Huh...</h1>

View File

@ -17,7 +17,7 @@ const links = [
))}
</ul>
<ThemeSwitch />
<!-- <ThemeSwitch /> -->
</nav>
<style>
@ -31,6 +31,7 @@ const links = [
padding: 0;
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
}
</style>

View File

@ -0,0 +1,7 @@
---
const api_key = "";
const username = "";
const url = "";
---
<span class="refresh" onclick="window.location.reload()">&#128472;</span>

View File

@ -0,0 +1,244 @@
<section id="updates">
<header>
<h1>Updates</h1>
</header>
<div class="content">
<div class="inner">
<h2>
<div class="title">
<span class="segment">Subject</span>
<hr>
<span class="segment">Date</span>
</div>
</h2>
<article class="update card">
<header>
<h3><span>update title</span></h3>
<time datetime="">05/01/25</time>
</header>
<div class="content inner">
<p>some stuff happened</p>
</div>
</article>
<footer id="updates-pagination">
<a id="previous" aria-label="go to previous post" href="javascript:void(0)">
<div class="arrow" />
<div class="title">prev</div>
</a>
<a id="next" aria-label="go to next post" href="javascript:void(0)">
<div class="title">next</div>
<div class="arrow" />
</a>
</footer>
</div>
</div>
</section>
<style>
/* UPDATES */
#updates {
& > header {
background-color: var(--border-6);
border: 2px solid var(--border-0);
border-image: var(--headerBorder) 18 9 15 9 fill / 36px 18px 30px;
padding: 36px 18px 30px;
h1 {
font-family: var(--arial-font);
font-weight: bold;
font-size: 1rem;
margin: 0.25rem;
}
}
& > .content {
background-color: var(--bg-0);
border: 2px solid var(--border-0);
border-image: var(--bottomBorder) 0 8 40 7 fill / 0px 16px 80px 14px;
/* padding: 0px 8px 40px 7px; */
padding: 0 10px 78px;
& > .inner {
background-color: var(--bg-2);
padding: 16px 6px;
margin: 18px 20px;
border: 2px solid var(--border-6);
}
}
h2 {
font-family: var(--arial-font);
font-size: 1rem;
padding: 6px 2px;
border-block: 2px solid var(--border-3);
background-color: var(--bg-0);
.title {
display: grid;
grid-template-columns: 2fr 2px 1fr;
background-color: var(--bg-8);
color: var(--bg-0);
font-weight: normal;
text-transform: uppercase;
padding: 5px 2px;
text-align: center;
}
hr {
background-color: var(--border-7);
border: none;
}
}
/* UPDATE CARDS */
.update {
padding: 4px 2px;
background-color: var(--bg-0);
border-bottom: 2px solid var(--bg-4);
header {
display: grid;
grid-template-columns: 2fr 1fr;
text-align: center;
& > * {
font: normal 1.375rem var(--dotum-11-font);
padding: 6px;
}
h3 {
max-width: 100%;
background-color: var(--bg-1);
span {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@media screen and (max-width: 560px) {
margin: 0 auto;
max-width: 15ch;
}
}
}
/* 15ch */
time { background-color: var(--bg-3); }
}
.inner {
border: 2px solid var(--border-2);
border-image: var(--innerBorder) 3 2 2 fill / 6px 4px 4px;
padding: 4px;
background-color: var(--bg-0);
}
.content {
margin: 4px 2px 2px;
padding: 6px;
}
&:last-of-type {
border-bottom: 2px solid var(--border-2);
}
}
}
/* UPDATES PAGINATION */
#updates-pagination {
margin-top: 1em;
display: grid;
grid-template-columns: repeat(2, 1fr);
.arrow {
width: 36px;
height: 50px;
background: var(--buttons) no-repeat top left;
}
#previous {
grid-column: 1 / 2;
justify-self: left;
.arrow { background-position: 0 0; }
&:hover .arrow { background-position: -36px 0; }
&:active .arrow, &:focus .arrow { background-position: -72px 0; }
.title {
border-left: none;
margin-left: -2px;
padding-left: calc(0.5em + 2px);
}
}
#next {
grid-column: 2 / -1;
justify-self: right;
.arrow { background-position: -108px 0; }
&:hover .arrow { background-position: -144px 0; }
&:active .arrow, &:focus .arrow { background-position: -180px 0; }
.title {
position: relative;
z-index: 1;
border-right: none;
margin-right: -2px;
padding-right: calc(0.5em + 2px);
}
}
/* BASE STYLING FOR ARROW BUTTONS */
a {
display: flex;
flex-flow: row wrap;
align-items: stretch;
color: var(--normal-color);
text-decoration: none;
.title {
display: grid;
place-content: center;
padding: 0 0.5em;
background-color: var(--bg-7);
border: 2px solid var(--border-1);
box-shadow:
inset 2px 2px 0 0 var(--bg-0),
inset 4px 4px 0 0 var(--bg-5),
inset -2px -2px 0 0 var(--border-5),
inset -4px -4px 0 0 var(--border-6);
@media screen and (468px > width) {
> span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: calc(12ch + 1em);
}
}
}
&:hover {
color: var(--active-color);
.title { border-color: var(--active-border); }
}
&:active, &:focus {
color: var(--normal-color);
.title {
border-color: var(--border-1);
box-shadow:
inset -2px -2px 0 0 var(--bg-0),
inset -4px -4px 0 0 var(--bg-5),
inset 2px 2px 0 0 var(--border-5),
inset 4px 4px 0 0 var(--border-6);
}
}
}
}
</style>

View File

@ -2,7 +2,6 @@
import type { MarkdownLayoutProps } from "astro";
import { Font } from "astro:assets";
import Layout from "./Layout.astro";
import Navbar from "~/Navbar.astro";
import Figure from "~/Figure.astro";
import border from "$/pmd-border.png";
@ -20,8 +19,6 @@ const { frontmatter } = Astro.props;
<Font cssVariable="--wondermail" preload />
</Fragment>
<Navbar />
<main>
{frontmatter.avatar && frontmatter.avatarText && (
<Figure class="avatar" imagePath={frontmatter.avatar} alt={frontmatter.avatarText} />

View File

@ -2,7 +2,6 @@
import { Font, Image } from "astro:assets";
import { getCollection } from "astro:content";
import Layout from "./Layout.astro";
import Navbar from "~/Navbar.astro";
import formatDate from "@/utils/formatDate";
import moods from "@/utils/moods";
@ -29,7 +28,6 @@ blog.sort((a, b) => b.data.pubDate!.valueOf() - a.data.pubDate!.valueOf());
<Fragment slot="head">
<slot name="head" />
</Fragment>
<Navbar />
<main>
<article id={id}>

View File

@ -2,6 +2,7 @@
import { Font } from "astro:assets";
import "$/styles/base.css";
import "$/styles/themes.css";
import Navbar from "~/Navbar.astro";
interface Props { title?: string; }
@ -25,6 +26,7 @@ const { title = "haetae" }: Props = Astro.props;
</script>
</head>
<body>
<Navbar />
<slot />
</body>
</html>

View File

@ -59,14 +59,14 @@ const notes = fic.rendered && await parser.parse((fic.rendered.metadata!.frontma
</dl>
<div class="links">
{chapters && <>
{/* <a class="button" href={`/fics/${chapters[0].id}`}>start reading</a> */}
{chapters.length > 0 && <>
<a class="button" href={`/fics/${chapters[0].id}`}>start reading</a>
<a class="button" href={`/fics/${Astro.params.ficId}/rss.xml`}>rss feed</a>
</>}
</div>
</header>
{chapters && (
{chapters.length > 0 && (
<h2>chapters</h2>
<ul>
{chapters.map(chapter => (
@ -75,6 +75,7 @@ const notes = fic.rendered && await parser.parse((fic.rendered.metadata!.frontma
</ul>
)}
</section>
{fic.body && (
<section id="oneshot">
<ChapterContent lastModified={lastMod} notes={notes}>

View File

@ -30,8 +30,9 @@ const inputErrors = isInputError(result?.error) ? result.error.fields : {};
<input type="url" id="website" name="website" aria-describedby="website-error" />
{inputErrors.website && <p id="website-error" class="error">{inputErrors.website}</p>}
<!-- <label for="challengeQuestionAnswer">What's my name?</label>
<input placeholder="read my about page!" type="text" id="challengeQuestionAnswer" name="challengeQuestionAnswer" required /> -->
<label for="challenge">Challenge: What's my name?</label>
<input placeholder="read my about page!" type="text" id="challenge" name="challenge" required aria-describedby="challenge-error" />
{inputErrors.challenge && <p id="challenge-error" class="error">{inputErrors.challenge}</p>}
<label for="message">Message</label>
<textarea placeholder="Message (plain text only)..." id="message" name="message" required aria-describedby="message-error"></textarea>

View File

@ -1,11 +1,13 @@
---
import { Font } from 'astro:assets';
import Layout from '@/layouts/Layout.astro';
import Navbar from '~/Navbar.astro';
import Updates from '~/Updates.astro';
import outerBBS from "$/guild-bbs.png";
import innerBBS from "$/guild-bbs-content.png";
import sideBBS from "$/guild-bbs-list.png";
import headerBBS from "$/guild-bbs-header.png";
import bottomBBS from "$/guild-bbs-bottom.png";
import buttons from "$/guild-bbs-buttons.png";
---
<Layout>
@ -13,59 +15,59 @@ import buttons from "$/guild-bbs-buttons.png";
<link rel="preload" href={outerBBS.src} as="image" />
<link rel="preload" href={innerBBS.src} as="image" />
<link rel="preload" href={sideBBS.src} as="image" />
<link rel="preload" href={headerBBS.src} as="image" />
<link rel="preload" href={bottomBBS.src} as="image" />
<link rel="preload" href={buttons.src} as="image" />
<Font cssVariable="--arial" preload />
<Font cssVariable="--dotum-11" preload />
<Font cssVariable="--dotumche-11" preload />
</Fragment>
<Navbar />
<main>
<section id="updates">
<article class="update card">
<h1>update title</h1>
<time datetime="">05/01/25</time>
<div class="content">
<p>some stuff happened</p>
</div>
</article>
<footer id="updates-pagination">
<a id="previous" aria-label="go to previous post" href="javascript:void(0)">
<div class="arrow" />
<div class="title">prev</div>
</a>
<a id="next" aria-label="go to next post" href="javascript:void(0)">
<div class="title">next</div>
<div class="arrow" />
</a>
</footer>
</section>
<section id="welcome">
<div class="card">
<div class="inner">
<h1>welcome!</h1>
<article>
this is
<article class="card">
<h2>waddup</h2>
<div class="content">
hi
</div>
</article>
</div>
</section>
<Updates />
<section id="wall">
<h1>stuff!</h1>
<p>i keep a bunch of links here, including the webrings i've joined and some badges i like</p>
<div class="web">
<div class="inner">
<h1>stuff!</h1>
<p>i keep a bunch of links here, including the webrings i've joined and some badges i like</p>
<div class="web">
</div>
</div>
</section>
<section id="scrobbler">
</section>
</main>
</Layout>
<style define:vars={{ outerBorder: `url(${outerBBS.src})`, innerBorder: `url(${innerBBS.src})`, sideBorder: `url(${sideBBS.src})`, buttons: `url(${buttons.src})` }}>
<style define:vars={{
outerBorder: `url(${outerBBS.src})`,
innerBorder: `url(${innerBBS.src})`,
sideBorder: `url(${sideBBS.src})`,
headerBorder: `url(${headerBBS.src})`,
bottomBorder: `url(${bottomBBS.src})`,
buttons: `url(${buttons.src})`
}}>
main {
image-rendering: pixelated;
--bg-0: #fff;
--bg-1: #eeffff;
--bg-2: #eeeeff;
--bg-3: #eee;
--bg-1: #f5fafc;
--bg-2: #e8eef3;
--bg-3: #e6e9eb;
--bg-4: #ddddee;
--bg-5: #ddeeee;
--bg-6: #ccccdd;
@ -78,6 +80,7 @@ import buttons from "$/guild-bbs-buttons.png";
--border-4: #88aabb;
--border-5: #99bbcc;
--border-6: #bbccdd;
--border-7: #aaaacc;
--normal-color: #335577;
--active-color: #aa0033;
--active-border: #442266;
@ -88,117 +91,60 @@ import buttons from "$/guild-bbs-buttons.png";
display: grid;
grid-template-columns: 1fr 1fr;
margin: 1em;
@media screen and (max-width: 1080px) {
grid-template-columns: 1fr;
}
}
h1 { font: bold 1rem var(--arial-font); }
/* UPDATES */
#updates {
border-image: var(--sideBorder) 22 9 40 8 fill / 44px 18px 80px 16px;
padding: 44px 18px 80px 16px;
margin-right: -2px;
}
#updates-pagination {
margin: 1rem 0;
display: grid;
grid-template-columns: repeat(2, 1fr);
.arrow {
width: 36px;
height: 50px;
background: var(--buttons) no-repeat top left;
}
#previous {
grid-column: 1 / 2;
justify-self: left;
.arrow { background-position: 0 0; }
&:hover .arrow { background-position: -36px 0; }
&:active .arrow, &:focus .arrow { background-position: -72px 0; }
.title {
border-left: none;
margin-left: -2px;
padding-left: calc(0.5em + 2px);
}
}
#next {
grid-column: 2 / -1;
justify-self: right;
.arrow { background-position: -108px 0; }
&:hover .arrow { background-position: -144px 0; }
&:active .arrow, &:focus .arrow { background-position: -180px 0; }
.title {
position: relative;
z-index: 1;
border-right: none;
margin-right: -2px;
padding-right: calc(0.5em + 2px);
}
}
a {
display: flex;
flex-flow: row wrap;
align-items: stretch;
color: var(--normal-color);
text-decoration: none;
.title {
display: grid;
place-content: center;
padding: 0 0.5em;
background-color: var(--bg-7);
border: 2px solid var(--border-1);
box-shadow:
inset 2px 2px 0 0 var(--bg-0),
inset 4px 4px 0 0 var(--bg-5),
inset -2px -2px 0 0 var(--border-5),
inset -4px -4px 0 0 var(--border-6);
@media screen and (468px > width) {
> span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: calc(12ch + 1em);
}
}
}
&:hover {
color: var(--active-color);
.title { border-color: var(--active-border); }
}
&:active, &:focus {
color: var(--normal-color);
.title {
border-color: var(--border-1);
box-shadow:
inset -2px -2px 0 0 var(--bg-0),
inset -4px -4px 0 0 var(--bg-5),
inset 2px 2px 0 0 var(--border-5),
inset 4px 4px 0 0 var(--border-6);
}
}
}
.inner {
border: 2px solid var(--border-2);
border-image: var(--innerBorder) 3 2 2 fill / 6px 4px 4px;
padding: 4px;
background-color: var(--bg-0);
}
/* WELCOME */
#welcome {
border-image: var(--outerBorder) 20 9 39 11 fill / 40px 18px 78px 22px;
padding: 40px 18px 78px 22px;
display: flex;
flex-wrap: wrap;
border: 2px solid var(--border-0);
border-image: var(--outerBorder) 19 9 38 11 fill / 38px 18px 76px 22px;
background-color: var(--bg-2);
padding: 36px 16px 74px 20px;
margin-right: -2px;
.inner {
flex: 1;
margin: 14px 4px 8px;
/* padding: 1rem; */
}
.card {
h2 {
font: normal 1.375rem var(--dotum-11-font);
}
}
@media screen and (max-width: 1080px) {
margin-right: 0;
margin-bottom: -2px;
}
}
/* INNER BORDERS */
.inner {
border-image: var(--innerBorder) 3 2 2 2 fill / 6px 4px 4px;
padding: 6px 4px 4px;
#wall {
border-image: var(--sideBorder) 22 9 40 8 fill / 44px 18px 80px 16px;
background-color: var(--bg-0);
padding: 42px 10px 78px;
margin-top: -2px;
grid-column: 1 / -1;
& > .inner {
background-color: var(--bg-2);
padding: 16px 6px;
margin: 18px 20px 16px;
border: 2px solid var(--border-6);
}
}
</style>