add css, next post feature

change favicon, add more readme, add extra example post, dark/light mode toggle
This commit is contained in:
jetstream0
2023-07-31 22:54:05 -07:00
parent c041f77b88
commit db4f5fea22
16 changed files with 257 additions and 29 deletions

View File

@@ -1,26 +1,56 @@
# Hedgeblog # Hedgeblog
My [personal blog](https://www.prussiafan.club), because what the world needs is yet another semi-abandoned blog. My [personal blog](https://www.prussiafan.club), because what the world needs is yet another semi-abandoned blog. All the code in this repo is licensed under the AGPL license, with the exception of Makoto, which is licensed under the MIT license.
## Technical Goals ## Technical Goals
- Completely rewrite the blog, and get it working - Completely rewrite the blog, and get it working
- Be able to be served statically (so it can be deployed on Github Pages or Cloudflare Pages for no dinero) - Be able to be served statically (so it can be deployed on Github Pages or Cloudflare Pages for no dinero)
- No dependencies - or basically, I want to write every line of code - No dependencies - or basically, I want to write every line of code (builtin modules like `path`, `fs` are ok of course)
- No Javascript served to client - the web pages should be pure HTML and CSS - No Javascript served to client - the web pages should be pure HTML and CSS
These goals are accomplished!
## Non-Technical Goals ## Non-Technical Goals
- Make two things I can call "Ryuji" and "Saki" to go along with "Makoto" (those are the three main characters of one of the best manga series ever) - Make two things I can call "Ryuji" and "Saki" to go along with "Makoto" (those are the three main characters of one of the best manga series ever)
- Move over some of the old blog posts (only the stuff I like), after rewriting them - Move over some of the old blog posts (only the stuff I like), after rewriting them
- Start writing stuff on the blog again, at least semi-regularly - Start writing stuff on the blog again, at least semi-regularly
The third goal may never be accomplished.
## Makoto ## Makoto
Makoto is the markdown-to-html parser, made with no dependencies. It was made around two months before Ryuji and Saki, and is meant to be more of a standalone thing. This is the sole npm dependency of the project (because I published makoto to npm and wanted to make sure it worked). Makoto is the markdown-to-html parser, made with no dependencies. It was made around two months before Ryuji and Saki, and is meant to be more of a standalone thing. This is the sole npm dependency of the project. I `npm install`ed it instead of just copying the file over mostly because I published Makoto to npm and wanted to make sure it worked. Also it has different license, documentation and stuff.
It also has a very cool warnings feature, that isn't used in this project, but can be seen in action if you use the [Makoto Web Editor](https://makoto.prussia.dev). It also has a very cool warnings feature, that isn't used in this project, but can be seen in action if you use the [Makoto Web Editor](https://makoto.prussia.dev).
## Ryuji ## Ryuji
Ryuji is a simple templating system. It's Jinja/Nunjucks inspired but has less features. On the upside, Ryuji is around 200 lines of code and supports if statements, for loops, components and inserting variables. Ryuji is a simple, Jinja/Nunjucks inspired templating system that supports `if` statements, `for` loops, components, and inserting variables. It isn't quite as fully featured as Jinja/Nunjucks, but on the upside, Ryuji is around just 200 lines of code, and worked very well for my usecase. I think it's pretty cool.
I didn't write any docs for it, but you can see the syntax if you look in the `templates` directory or look in `tests.ts`. I didn't write any docs for it (yet), but you can see the syntax if you look in the `templates` directory or look in `tests.ts`.
## Saki ## Saki
Saki is the build system that puts it all together and outputs the blog's static html. Even more simple than Ryuji, it is just around 70 lines of code. Saki is the build system that puts it all together and outputs the blog's static html. Even more simple than Ryuji, it is just around 70 lines of code.
## Running
First, install the dependencies (well, dependency, since Makoto is the only one).
```bash
npm install
```
## Building
```bash
npm run build
```
## Previewing
```bash
npm run preview
```
This builds the project and then serves the `build` folder at [http://localhost:8042](http://localhost:8042). As you can see in `preview.ts`, this part also relies on no dependencies - only builtin module `http` is used.
## Tests for Ryuji (templating)
```bash
npm run test
```
Uses Endosulfan, my very basic <40 LOC test assertion thingy.

View File

@@ -1,4 +1,3 @@
export let total_tests: number = 0; export let total_tests: number = 0;
export let failed_tests: number = 0; export let failed_tests: number = 0;
export let passed_tests: number = 0; export let passed_tests: number = 0;

View File

@@ -50,7 +50,7 @@ let tags: string[] = []; //also get all the tags since we are iterating through
for (let i=0; i < posts_metadata.length; i++) { for (let i=0; i < posts_metadata.length; i++) {
let post_metadata: PostMetadata = posts_metadata[i]; let post_metadata: PostMetadata = posts_metadata[i];
posts_serve_paths.push(`/posts/${post_metadata.slug}`); posts_serve_paths.push(`/posts/${post_metadata.slug}`);
let post_md_path: string = path.join(__dirname, `/posts/${post_metadata.slug}.md`); let post_md_path: string = path.join(__dirname, `/posts/${post_metadata.filename}.md`);
let md: string = readFileSync(post_md_path, "utf-8").replaceAll("\r", ""); let md: string = readFileSync(post_md_path, "utf-8").replaceAll("\r", "");
let html: string = parse_md_to_html(md); let html: string = parse_md_to_html(md);
for (let j=0; j < post_metadata.tags.length; j++) { for (let j=0; j < post_metadata.tags.length; j++) {
@@ -65,9 +65,11 @@ for (let i=0; i < posts_metadata.length; i++) {
html, html,
tags_exist: post_metadata.tags.length !== 0, tags_exist: post_metadata.tags.length !== 0,
} }
let next_post: PostMetadata = posts_metadata[i+1] ? posts_metadata[i+1] : posts_metadata[0];
posts_vars.push( posts_vars.push(
{ {
post, post,
next_post,
author_expected: post.author.toLowerCase().startsWith("jetstream0") || post.author.toLowerCase().startsWith("prussia"), author_expected: post.author.toLowerCase().startsWith("jetstream0") || post.author.toLowerCase().startsWith("prussia"),
} }
); );

View File

@@ -6,5 +6,13 @@
"date": "30/12/1999", "date": "30/12/1999",
"author": "Prussia", "author": "Prussia",
"tags": ["example", "fake", "please remember to remove"] "tags": ["example", "fake", "please remember to remove"]
},
"another_example": {
"title": "Another Example!",
"slug": "another-example",
"filename": "another_example",
"date": "31/07/2023",
"author": "John Dough",
"tags": ["example", "jia", "please remember to remove"]
} }
} }

14
posts/another_example.md Normal file
View File

@@ -0,0 +1,14 @@
Have some lorem ipsum. On the house!
...
No, I **insist**. Don't be shy.
1. lorem
2. ipsum
Repellat et vel consequuntur et. Suscipit animi ipsam tempora consequatur hic ea tenetur. Culpa rerum quos eum vero ea.
Aperiam quia facilis doloremque ducimus. Amet quas similique officia quas et enim aut. Non ut vel sint distinctio consectetur ipsa.
Illum id sit laboriosam corrupti veritatis et quam. Ut ea quaerat omnis doloribus enim. Sed atque ad est doloribus esse.
Molestias omnis ut voluptatem. Eius vel deleniti quia quam odio. Adipisci voluptas dolor nulla voluptatem. Molestiae sunt veritatis qui ex atque molestiae nobis. Expedita repellat dolores adipisci.
Deleniti totam molestiae necessitatibus rem dolores. Natus ut quos beatae aut. Corrupti eum provident perferendis eum dolores maiores eos.

View File

@@ -2,7 +2,7 @@ Hello! This is an **example** post *not meant* to be actually served on the blog
[look its a link](https://example.com) [look its a link](https://example.com)
![and an image too??? no way](/favicon.ico) ![and an image too??? no way, this is a very safe hedgehog btw](/favicon.ico)
## Wow a header ## Wow a header

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

84
static/styles/global.css Normal file
View File

@@ -0,0 +1,84 @@
body, html {
margin: 0;
padding: 0;
}
#main {
box-sizing: border-box;
min-height: 100vh;
padding: 35px 10vw;
color: #222;
}
* {
font-family: 'Times New Roman', Times, serif;
}
*:not(h1, h2, h3, h4, h5, h6) {
font-size: 1.02rem;
}
#return {
position: absolute;
top: 5px;
left: 5px;
}
a {
text-decoration: underline;
}
a:link {
color: forestgreen;
}
a:visited {
color: orchid;
}
p {
margin: 7px 0px;
line-height: 1.4;
}
h1, h2, h3, h4, h5, h6 {
font-family: Verdana, sans-serif;
margin: 0;
margin-bottom: 5px;
}
ul {
margin: 0;
padding-left: 40px;
}
input[type="checkbox"] {
margin-left: 0;
}
#dark-mode {
position: fixed;
top: 5px;
right: 5px;
opacity: 0;
}
#dark-mode:checked ~ #main {
background-color: #222;
color: white;
}
label[for="dark-mode"] {
position: fixed;
top: 3px;
right: 3px;
font-size: 1.3rem;
}
#dark-mode + label[for="dark-mode"]::after {
content: "🌙";
}
#dark-mode:checked + label[for="dark-mode"]::after {
content: "☀️";
}

View File

@@ -0,0 +1,16 @@
h2 {
display: inline-block;
}
#fancy-title:checked ~ h2 {
color: white;
padding-right: 0px 4px;
background-color: dodgerblue;
text-shadow: 1px 1px 1px gray, 4px 4px 1px blue;
}
#checkboxes {
position: relative;
top: 0;
left: 0;
}

View File

@@ -0,0 +1,16 @@
#post-md {
margin-top: 7px;
display: none;
}
#show-md:checked ~ #post-md {
display: block;
}
#show-md:checked ~ #post-html {
display: none;
}
#next-post-container {
padding-top: 10px;
}

View File

@@ -0,0 +1 @@
<input id="dark-mode" type="checkbox"/><label for="dark-mode"></label>

View File

@@ -0,0 +1,35 @@
<style>
blockquote {
margin: 0;
margin: 8px 0px;
padding: 4px 8px 4px 0px;
border-left: 8px solid #727272;
padding-left: 8px;
}
.code-block, code {
font-family: 'Courier New', Courier, monospace;
font-size: 1rem;
}
.code-block {
color: #222;
background-color: #d8d8d8;
padding: 2px 4px;
word-break: break-all;
border-radius: 3px;
}
img {
max-width: 85%;
}
table {
border-collapse: collapse;
}
th, td {
padding: 2px 5px;
border: 1px solid black;
}
</style>

View File

@@ -1 +1 @@
<a href="/" style="position: absolute; left: 10px; top: 10px;"><- Get me outta here!</a> <a id="return" href="/"><= Get me outta here!</a>

View File

@@ -5,17 +5,26 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>prussia fan club</title> <title>prussia fan club</title>
<link rel="icon" type="image/png" href="/favicon.ico"> <link rel="icon" type="image/png" href="/favicon.ico">
<link rel="stylesheet" href="/styles/global.css">
<link rel="stylesheet" href="/styles/index.css">
</head> </head>
<body> <body>
<div> [[ component:dark-mode-checkbox ]]
<ul> <div id="main">
[[ for:posts:post ]] <div>
[[ component:post-listing ]] <input id="fancy-title" type="checkbox"/><label for="fancy-title">Fancy Title</label>
[[ endfor ]] <br>
</ul> <h2>prussiafan.club</h2>
</div> <br>
<div> <p>This is my blog. I also have a <a href="https://prussia.dev">portfolio</a>, <a href="https://prussia.dev/retro">retro style personal website</a>, and a place where you can <a href="https://prussia.dev/sample">hire me</a>.</p>
<p>This is my blog. I also have a <a href="https://prussia.dev">portfolio</a>, <a href="https://prussia.dev/retro">retro style personal website</a>, and a place where you can <a href="https://prussia.dev/sample">hire me</a>.</p> </div>
<div>
<ul>
[[ for:posts:post ]]
[[ component:post-listing ]]
[[ endfor ]]
</ul>
</div>
</div> </div>
</body> </body>
</html> </html>

View File

@@ -5,17 +5,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>[[ post.title ]]</title> <title>[[ post.title ]]</title>
<link rel="icon" type="image/png" href="/favicon.ico"> <link rel="icon" type="image/png" href="/favicon.ico">
<link rel="stylesheet" href="/styles/global.css">
<link rel="stylesheet" href="/styles/post.css">
</head> </head>
<body> <body>
[[ component:return ]] [[ component:return ]]
<div> [[ component:dark-mode-checkbox ]]
<div id="main">
<div id="post-info"> <div id="post-info">
<h1>[[ post.title ]]</h1> <h1>[[ post.title ]]</h1>
<div> <div>
<span><span [[ if:author_expected ]]title="(obviously)"[[ endif ]]>By [[ post.author ]]</span> | <span>[[ post.date ]]</span> [[ if:post.tags_exist ]]| <span>[[ for:post.tags:tag:index:max ]]<a href="/tags/[[ tag ]]">[[ tag ]]</a>[[ if:index:!max ]], [[ endif ]][[ endfor ]]</span>[[ endif ]]</span> <span><span [[ if:author_expected ]]title="(obviously)"[[ endif ]]>By [[ post.author ]]</span> | <span>[[ post.date ]]</span> [[ if:post.tags_exist ]]| <span>[[ for:post.tags:tag:index:max ]]<a href="/tags/[[ tag ]]">[[ tag ]]</a>[[ if:index:!max ]], [[ endif ]][[ endfor ]]</span>[[ endif ]]</span>
</div> </div>
</div> </div>
<input id="show-md" type="checkbox"/><label for="html">Show MD</label> <input id="show-md" type="checkbox"/><label for="show-md">Show MD</label>
<div id="post-html"> <div id="post-html">
[[ html:post.html ]] [[ html:post.html ]]
</div> </div>
@@ -27,6 +30,12 @@
[[ endif ]] [[ endif ]]
[[ endfor ]] [[ endfor ]]
</div> </div>
[[ if:post.slug:!next_post.slug ]]
<div id="next-post-container">
<a href="/posts/[[ next_post.slug ]]">Next Post: [[ next_post.title ]]</a>
</div>
[[ endif ]]
[[ component:makoto-styling ]]
</div> </div>
</body> </body>
</html> </html>

View File

@@ -5,17 +5,22 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>"[[ tag ]]" posts</title> <title>"[[ tag ]]" posts</title>
<link rel="icon" type="image/png" href="/favicon.ico"> <link rel="icon" type="image/png" href="/favicon.ico">
<link rel="stylesheet" href="/styles/global.css">
</head> </head>
<body> <body>
<div> [[ component:return ]]
<h2>Search for posts with tag "[[ tag ]]"</h2> [[ component:dark-mode-checkbox ]]
</div> <div id="main">
<div> <div>
<ul> <h2>Search for posts with tag "[[ tag ]]"</h2>
[[ for:posts:post ]] </div>
[[ component:post-listing ]] <div>
[[ endfor ]] <ul>
</ul> [[ for:posts:post ]]
[[ component:post-listing ]]
[[ endfor ]]
</ul>
</div>
</div> </div>
</body> </body>
</html> </html>