From 63668ca89f055b2233a8aa6caf4213853918d68d Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Sun, 15 Nov 2020 21:43:36 -0600 Subject: Switch to handlebars for templating --- src/css/mobile-styles.css | 23 ++++ src/css/styles.css | 31 +---- src/html/admin.html | 3 +- src/html/misc.html | 29 ----- src/html/projects.html | 286 ---------------------------------------- src/index.js | 3 +- src/server.js | 90 +++++++------ src/templates.js | 115 +++++------------ src/templates/blog-single.html | 23 ++++ src/templates/blog.html | 22 ++++ src/templates/bread.html | 23 ++++ src/templates/feed.html | 24 ++++ src/templates/footer.html | 14 ++ src/templates/index.html | 20 +++ src/templates/misc.html | 30 +++++ src/templates/navigation.html | 10 ++ src/templates/projects.html | 287 +++++++++++++++++++++++++++++++++++++++++ src/templates/tags.html | 22 ++++ 18 files changed, 584 insertions(+), 471 deletions(-) create mode 100644 src/css/mobile-styles.css delete mode 100644 src/html/misc.html delete mode 100644 src/html/projects.html create mode 100644 src/templates/blog-single.html create mode 100644 src/templates/blog.html create mode 100644 src/templates/bread.html create mode 100644 src/templates/feed.html create mode 100644 src/templates/footer.html create mode 100644 src/templates/index.html create mode 100644 src/templates/misc.html create mode 100644 src/templates/navigation.html create mode 100644 src/templates/projects.html create mode 100644 src/templates/tags.html (limited to 'src') diff --git a/src/css/mobile-styles.css b/src/css/mobile-styles.css new file mode 100644 index 0000000..36553cc --- /dev/null +++ b/src/css/mobile-styles.css @@ -0,0 +1,23 @@ +@media screen and (min-width: 900px) { + body { + width: 100%; + /* margin: auto; */ + } + .card-img { + display: flex; + flex-flow: row wrap; + align-content: flex-start; + } + .card img { + width: 400px; + } + form input[type=text], input[type=password], input[type=email] { + max-width: 600px; + } + form textarea { + max-width: 600px; + } + form input[type=submit] { + margin-left: 0px; + } +} \ No newline at end of file diff --git a/src/css/styles.css b/src/css/styles.css index 522b007..f406104 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -11,8 +11,9 @@ body { - width: 100%; - margin: 0px; + width: 80%; + margin: auto; + /* margin: 0px; */ background-color: var(--background); font-family: Arial, Helvetica, sans-serif; } @@ -93,7 +94,7 @@ a { width: 100%; } .feed { - width: 80%; + /* width: 80%; */ min-width: 200px; margin: auto; display: flex; @@ -109,30 +110,6 @@ p { margin-top: 0; } -@media screen and (min-width: 900px) { - body { - width: 100%; - /* margin: auto; */ - } - .card-img { - display: flex; - flex-flow: row wrap; - align-content: flex-start; - } - .card img { - width: 400px; - } - form input[type=text], input[type=password], input[type=email] { - max-width: 600px; - } - form textarea { - max-width: 600px; - } - form input[type=submit] { - margin-left: 0px; - } -} - .titlebar { padding: 5px; border-top: 1px solid var(--light-text); diff --git a/src/html/admin.html b/src/html/admin.html index c6683c3..3eaa60a 100644 --- a/src/html/admin.html +++ b/src/html/admin.html @@ -32,6 +32,7 @@ +

< Admin @@ -135,4 +136,4 @@ - \ No newline at end of file + diff --git a/src/html/misc.html b/src/html/misc.html deleted file mode 100644 index 6ab0a15..0000000 --- a/src/html/misc.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - Mark's Kitchen - - - - - - - - -

- < - Miscellany -

-
- Projects -
-
- Chess -
-
- Word Play - - - - \ No newline at end of file diff --git a/src/html/projects.html b/src/html/projects.html deleted file mode 100644 index fef817f..0000000 --- a/src/html/projects.html +++ /dev/null @@ -1,286 +0,0 @@ - - - - - Mark's Kitchen - Projects - - - - - - -
-

- < - < - Projects -

-

A collection of my projects, mostly programming

-
-
-

- wikijscmd -

-

- I use wiki.js for my personal wiki. I wanted a simple client I could use for it - and a command line one would also allow for me to script it. The wiki comes built - in with a GraphQL API, so I learned some GraphQL and created a python wrapper that - allows for managing and editing of wiki.js content over this API. -

-

- 2020-10 -

-
-
-

- Pi Weather Station -

-

- I set up a sensor on a raspberry pi zero to measure the temperature, pressure, and humidity. - This project is a web site that runs on the pi that plots the data over time. In the - future I'd like to make the plots nicer, and include outdoor temperature data as well. -

-

- 2020-09 -

-
-
-

- Local Podcast Generator -

-

- I had some audio files on my phone I wanted to listen to like a podcast. My podcast app is way - better for this type of content than a general purpose media player. So this app will locally - create a web server that serves an rss feed generated from a directory on your phone. This can - be added like any other podcast in your favorite app. -

-

- 2020-09 -

-
-
-

- File Feed -

-

- I found myself wanting to browse social feeds a lot, and wanting new content. - I have a lot of documents on my file of things I haven't explored yet. So this program - tries to merge the two: a generated feed of files on my computer. -

-

- 2020-08 -

-
-
-

- games.marks.kitchen (source code) -

-

- A collection of HTML5 games that I've made. When I have some fun idea, - I'll host it here. It has high scores for some games too, and a scoreboard. - I am pretty proud of how this website turned out, it is what I wanted - to have when I was a kid. -

-

- 2020-04 -

-
-
-

- Backdoor Learning on Models of Code -

-

- A class project where I studied how to poison training datasets of source - code to create backdoors in a model predicting a function name based on - a code body. A backdoor is an inocuous feature that will force a prediction - to a target label. By modifying a small proportion of training data, with - something like adding an unused variable declaration, the backdoor will be - successfully learned. I implemented the Sever algorithm which attempts to - find and remove poisoned data, but it was unsuccessful at doing so on the - code2seq model architecture. -

-

- 2020-04 -

-
-
-

- Multi-level Wavelet-CNN HDR Image Reconstruction from Single Exposures -

-

- A class project that used MWCNNs to reconstruct images. On HDR single exposure - images, white balancing often clips values. The novel wavelet approach was able - to reconstruct image detail in a realistic way. -

-

- 2020-04 -

-
-
-

- Budget -

-

- My website I made to host manage my budget. It functions a lot like a spreadsheet, - with some more advanced features and an easier interface to use. It is multiuser, - and if you are interested in using it send me an email. -

-

- 2019-12 (until current) -

-
-
-

- SynthesisRefactoring -

-

- For class, I did a project using program synthesis to refactor programs. The program tried to - find areas inside source code that could be extracted into methods. It sort of works, and - in the future it could really be improved by using compilers techniques. I also made - a web-frontend - for it at refactoring.marks.kitchen. -

-

- 2019-10 -

-
-
-

- Cosmic Cargo -

-

- A game I worked on with some friends for GBJAM 2019, and is - Oregon Trail, in space. - This game was made over the course of a week, and can be played at - this link. -

-

- 2019-08 -

-
- -
-

- marks.kitchen -

-

- The engine behind this website. I wanted to learn more about - back-end servers, and creating a way to me to update this - website dynamically. I wrote a blog post about the making - here. -

-

- 2019-01 (until current) -

-
- -
-

- marks.database -

-

- A personal database management system I wrote in order to keep track - of a lot of things. I use this as a bookmark manager, a recipe and pantry - organizer, and a way to store lots of other things too. I wrote some - about making this here. -

-

- 2019-03 -

-
- -
-

- Telegram Plugin Bot -

-

- A custom Telegram bot I wrote in python that allowed for custom plugins. - This made it super easy to add functionality to a bot, and it made - my group chat with friends lively. This project has now been suceeded by a - rewrite - in Java. It was difficult to manage loading and unloading in Python, and - wrapping the API by hand made it difficult to expand beyond basic messages. -

-

- 2017-11 (Java: 2019-10) -

-
- -
-

- Telegram arcade -

-

- A general purpose bot and web server to play Telegram HTML5 games. - I couldn't find any existing solutions that allowed me to deploy - these games easily, so I wrote my own. The API is not well documented, - and I wrote a bit about it here. -

-

- 2019-05 -

-
- -
-

- Read-Length -

-

- A firefox extension that adds the reading length of a website to the - title of a page. This was my first web extension, and its technique - for finding paragraph content doesn't work great. It works well in - firefox on Ubuntu, as the window bar will display the title with - the updated reading time. -

-

- 2019-02 -

-
- -
-

- Compiler -

-

- A compiler I worked on as an indepedent study during undergrad, - implementing techniques from "Compilers: Principles, Techniques, and Tools". - I also wrote a small stack based virtual - machine that runs the - generated code -

-

- 2019-10 -

-
- -
-

- streetcard -

-

- A general purpose card game engine. I wanted to test out - some strategies for card games, so I - tried to write an engine for it. I didn't finish it, but - you can play some basic card games in this engine. -

-

- 2017-10 -

-
- -
-

- Roguelike -

-

- A rogue-like demo game I made just for fun while I was on winter break one year. -

-

- 2017-01 -

-
-
-
- - - \ No newline at end of file diff --git a/src/index.js b/src/index.js index 62d8bb5..4b9a3c6 100644 --- a/src/index.js +++ b/src/index.js @@ -100,8 +100,9 @@ function setUpModels(){ } const models = setUpModels(); +const templates = require('./templates'); sync(); -server.setUpRoutes(models, jwtFunctions, database); +server.setUpRoutes(models, jwtFunctions, database, templates.setUpTemplates()); server.listen(config.port); diff --git a/src/server.js b/src/server.js index 7be1237..e82fc12 100644 --- a/src/server.js +++ b/src/server.js @@ -87,7 +87,7 @@ async function constructFeedFromType(models, postType){ return constructFeed(posts) } -async function constructSinglePost(models, postType, postId){ +async function formatPostsforSingle(models, postType, postId){ var posts = await models.posts.findAll({ where: { type: postType, @@ -96,11 +96,25 @@ async function constructSinglePost(models, postType, postId){ }); posts = posts.map(x => x.get({ plain: true })); await addImagesAndTagsToPosts(models, posts) - - return constructFeed(posts) + posts.forEach(post => { + post.createdAt = post.createdAt.toString().substring(0, 10) + }) + return posts +} + +async function formatPostsForType(models, postType){ + var posts = await models.posts.findAll({ + where: { type: postType }, order: [['createdAt', 'DESC']] + }); + posts = posts.map(x => x.get({ plain: true })); + await addImagesAndTagsToPosts(models, posts) + posts.forEach(post => { + post.createdAt = post.createdAt.toString().substring(0, 10) + }) + return posts; } -function setUpRoutes(models, jwtFunctions, database) { +function setUpRoutes(models, jwtFunctions, database, templates) { // Authentication routine server.use(function (req, res, next) { if (req.path.toLowerCase().startsWith("/admin")) { @@ -142,41 +156,25 @@ function setUpRoutes(models, jwtFunctions, database) { }) server.get('/', async (req, res) => { - var html = [] - html.push(templates["index"]["pre"]) - html.push(templates["titlebar"]) - html.push(await constructFeedFromType(models, "index")) - html.push(templates["footer"]) - html.push(templates["index"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsForType(models, "index") + let body = templates["index"]({posts}); + res.status(200).send(body) }) server.get('/bread', async (req, res) => { - var html = [] - html.push(templates["bread"]["pre"]) - html.push(await constructFeedFromType(models, "bread")) - html.push(templates["footer"]) - html.push(templates["bread"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsForType(models, "bread") + let body = templates["bread"]({posts}); + res.status(200).send(body) }) server.get('/blog', async (req, res) => { - var html = [] - html.push(templates["blog"]["pre"]) - html.push(await constructFeedFromType(models, "blog")) - html.push(templates["footer"]) - html.push(templates["blog"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsForType(models, "blog") + let body = templates["blog"]({posts}); + res.status(200).send(body) }) server.get('/post/:type/:id', async (req, res) => { - var html = [] - html.push(templates["blog"]["pre"]) - html.push(await constructSinglePost(models, req.params.type, req.params.id)) - html.push(templates["footer"]) - html.push(templates["blog"]["post"]) - - res.status(200).send(html.join("")) + let posts = await formatPostsforSingle(models, req.params.type, req.params.id) + let date = posts[0].createdAt; + let body = templates["blog-single"]({posts, date}); + res.status(200).send(body) }) server.get('/tags/:name', async (req, res) => { const { name } = req.params; @@ -189,15 +187,12 @@ function setUpRoutes(models, jwtFunctions, database) { }); posts = posts.map(x => x.get({ plain: true })); await addImagesAndTagsToPosts(models, posts) + posts.forEach(post => { + post.createdAt = post.createdAt.toString().substring(0, 10) + }) - var html = [] - html.push(templates["blog"]["pre"]) - html.push(`

#${name}

`) - html.push(await constructFeed(posts)) - html.push(templates["footer"]) - html.push(templates["blog"]["post"]) - - res.status(200).send(html.join("")) + let body = templates["tags"]({posts, name}) + res.status(200).send(body) }) server.get('/admin', (req, res) => res.sendFile(__dirname + "/html/admin.html")); @@ -206,14 +201,23 @@ function setUpRoutes(models, jwtFunctions, database) { server.get('/email-success', (req, res) => res.sendFile(__dirname + "/html/email-success.html")) server.get('/feed', (req, res) => res.sendFile(__dirname + "/html/feed.html")); server.get('/essay', (req, res) => res.sendFile(__dirname + "/html/essay.html")); - server.get('/misc', (req, res) => res.sendFile(__dirname + "/html/misc.html")); + // server.get('/misc', (req, res) => res.sendFile(__dirname + "/html/misc.html")); server.get('/word-square', (req, res) => res.sendFile(__dirname + "/html/word-square.html")); server.get('/chess', (req, res) => res.sendFile(__dirname + "/html/chess.html")); server.get('/admin/chess', async (req, res, next) => res.sendFile(__dirname + "/html/chess.html")); - server.get('/projects', (req, res) => res.sendFile(__dirname + "/html/projects.html")); + // server.get('/projects', (req, res) => res.sendFile(__dirname + "/html/projects.html")); server.get('/zines', (req, res) => res.sendFile(__dirname + "/public/zines.html")); server.use('/static', express.static(__dirname + '/public')) + server.get('/misc', async (req, res) => { + let body = templates["misc"](); + res.status(200).send(body) + }) + server.get('/projects', async (req, res) => { + let body = templates["projects"](); + res.status(200).send(body) + }) + server.get('/wordsquares/best', async (req, res, next) => { var best = await database.query("select words, name from wordsquares where best = 1", { type: database.QueryTypes.SELECT }) res.status(200).send({ best: best }); diff --git a/src/templates.js b/src/templates.js index b748853..293f0e4 100644 --- a/src/templates.js +++ b/src/templates.js @@ -1,86 +1,33 @@ -module.exports = { - titlebar: ``, - footer: ``, +const fs = require('fs'); +const path = require('path'); +const handlebars = require("handlebars"); + +function loadTemplate(templates, name, filepath){ + const templateContent = fs.readFileSync(filepath).toString() + templates[name] = handlebars.compile(templateContent); +} + +function loadPartial(name, filepath){ + handlebars.registerPartial(name, fs.readFileSync(filepath).toString()); +} + +function setUpTemplates(){ + loadPartial("navigation", path.join(__dirname, "templates/navigation.html")) + loadPartial("footer", path.join(__dirname, "templates/footer.html")) + loadPartial("feed", path.join(__dirname, "templates/feed.html")) - index: { - pre: ` - - - - Mark's Kitchen - - - - - - - - -

Welcome to Mark's Kitchen

`, - post: ` - ` - }, - bread: { - pre: ` - - - - Mark's Kitchen - Bread - - - - - - - -

- < - Bread -

- Some highlights (and lowlights) of breadmaking`, - post: ` - ` - }, - blog: { - pre: ` - - - - Mark's Kitchen - Blog - - - - - - - -

- < - Blog -

`, - post: ` - ` - } + let templates = {}; + loadTemplate(templates, "index", path.join(__dirname, 'templates/index.html')) + loadTemplate(templates, "bread", path.join(__dirname, 'templates/bread.html')) + loadTemplate(templates, "blog", path.join(__dirname, 'templates/blog.html')) + loadTemplate(templates, "blog-single", path.join(__dirname, 'templates/blog-single.html')) + loadTemplate(templates, "tags", path.join(__dirname, 'templates/tags.html')) + loadTemplate(templates, "misc", path.join(__dirname, 'templates/misc.html')) + loadTemplate(templates, "projects", path.join(__dirname, 'templates/projects.html')) + return templates +} + + +module.exports = { + setUpTemplates } \ No newline at end of file diff --git a/src/templates/blog-single.html b/src/templates/blog-single.html new file mode 100644 index 0000000..8622007 --- /dev/null +++ b/src/templates/blog-single.html @@ -0,0 +1,23 @@ + + + + + Mark's Kitchen - Blog + + + + + + + +

+ < + < + Post from {{date}} +

+ {{> navigation}} + {{> feed}} + {{> footer}} + + + \ No newline at end of file diff --git a/src/templates/blog.html b/src/templates/blog.html new file mode 100644 index 0000000..40b86f5 --- /dev/null +++ b/src/templates/blog.html @@ -0,0 +1,22 @@ + + + + + Mark's Kitchen - Blog + + + + + + + +

+ < + Blog +

+ {{> navigation}} + {{> feed}} + {{> footer}} + + + \ No newline at end of file diff --git a/src/templates/bread.html b/src/templates/bread.html new file mode 100644 index 0000000..b5d02ed --- /dev/null +++ b/src/templates/bread.html @@ -0,0 +1,23 @@ + + + + + Mark's Kitchen - Bread + + + + + + + +

+ < + Bread +

+

Some highlights (and lowlights) of breadmaking

+ {{> navigation}} + {{> feed}} + {{> footer}} + + + \ No newline at end of file diff --git a/src/templates/feed.html b/src/templates/feed.html new file mode 100644 index 0000000..2158f3e --- /dev/null +++ b/src/templates/feed.html @@ -0,0 +1,24 @@ +
+ {{#each posts}} +
+

{{{this.description}}}

+
+ {{#each this.images}} + + + + {{/each}} +
+

+ + {{this.createdAt}} + + + {{#each this.tags}} + {{this}} + {{/each}} + +

+
+ {{/each}} +
\ No newline at end of file diff --git a/src/templates/footer.html b/src/templates/footer.html new file mode 100644 index 0000000..617b5fa --- /dev/null +++ b/src/templates/footer.html @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/src/templates/index.html b/src/templates/index.html new file mode 100644 index 0000000..efb7369 --- /dev/null +++ b/src/templates/index.html @@ -0,0 +1,20 @@ + + + + + Mark's Kitchen + + + + + + + + +

Welcome to Mark's Kitchen

+ {{> navigation}} + {{> feed}} + {{> footer}} + + + \ No newline at end of file diff --git a/src/templates/misc.html b/src/templates/misc.html new file mode 100644 index 0000000..9fb376a --- /dev/null +++ b/src/templates/misc.html @@ -0,0 +1,30 @@ + + + + + Mark's Kitchen + + + + + + + + +

+ < + Miscellany +

+ {{> navigation}} +
+ Projects +
+
+ Chess +
+
+ Word Play + + + + \ No newline at end of file diff --git a/src/templates/navigation.html b/src/templates/navigation.html new file mode 100644 index 0000000..6bb10f1 --- /dev/null +++ b/src/templates/navigation.html @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/src/templates/projects.html b/src/templates/projects.html new file mode 100644 index 0000000..cf3361a --- /dev/null +++ b/src/templates/projects.html @@ -0,0 +1,287 @@ + + + + + Mark's Kitchen - Projects + + + + + + +
+

+ < + < + Projects +

+ {{> navigation}} +

A collection of my projects, mostly programming

+
+
+

+ wikijscmd +

+

+ I use wiki.js for my personal wiki. I wanted a simple client I could use for it + and a command line one would also allow for me to script it. The wiki comes built + in with a GraphQL API, so I learned some GraphQL and created a python wrapper that + allows for managing and editing of wiki.js content over this API. +

+

+ 2020-10 +

+
+
+

+ Pi Weather Station +

+

+ I set up a sensor on a raspberry pi zero to measure the temperature, pressure, and humidity. + This project is a web site that runs on the pi that plots the data over time. In the + future I'd like to make the plots nicer, and include outdoor temperature data as well. +

+

+ 2020-09 +

+
+
+

+ Local Podcast Generator +

+

+ I had some audio files on my phone I wanted to listen to like a podcast. My podcast app is way + better for this type of content than a general purpose media player. So this app will locally + create a web server that serves an rss feed generated from a directory on your phone. This can + be added like any other podcast in your favorite app. +

+

+ 2020-09 +

+
+
+

+ File Feed +

+

+ I found myself wanting to browse social feeds a lot, and wanting new content. + I have a lot of documents on my file of things I haven't explored yet. So this program + tries to merge the two: a generated feed of files on my computer. +

+

+ 2020-08 +

+
+
+

+ games.marks.kitchen (source code) +

+

+ A collection of HTML5 games that I've made. When I have some fun idea, + I'll host it here. It has high scores for some games too, and a scoreboard. + I am pretty proud of how this website turned out, it is what I wanted + to have when I was a kid. +

+

+ 2020-04 +

+
+
+

+ Backdoor Learning on Models of Code +

+

+ A class project where I studied how to poison training datasets of source + code to create backdoors in a model predicting a function name based on + a code body. A backdoor is an inocuous feature that will force a prediction + to a target label. By modifying a small proportion of training data, with + something like adding an unused variable declaration, the backdoor will be + successfully learned. I implemented the Sever algorithm which attempts to + find and remove poisoned data, but it was unsuccessful at doing so on the + code2seq model architecture. +

+

+ 2020-04 +

+
+
+

+ Multi-level Wavelet-CNN HDR Image Reconstruction from Single Exposures +

+

+ A class project that used MWCNNs to reconstruct images. On HDR single exposure + images, white balancing often clips values. The novel wavelet approach was able + to reconstruct image detail in a realistic way. +

+

+ 2020-04 +

+
+
+

+ Budget +

+

+ My website I made to host manage my budget. It functions a lot like a spreadsheet, + with some more advanced features and an easier interface to use. It is multiuser, + and if you are interested in using it send me an email. +

+

+ 2019-12 (until current) +

+
+
+

+ SynthesisRefactoring +

+

+ For class, I did a project using program synthesis to refactor programs. The program tried to + find areas inside source code that could be extracted into methods. It sort of works, and + in the future it could really be improved by using compilers techniques. I also made + a web-frontend + for it at refactoring.marks.kitchen. +

+

+ 2019-10 +

+
+
+

+ Cosmic Cargo +

+

+ A game I worked on with some friends for GBJAM 2019, and is + Oregon Trail, in space. + This game was made over the course of a week, and can be played at + this link. +

+

+ 2019-08 +

+
+ +
+

+ marks.kitchen +

+

+ The engine behind this website. I wanted to learn more about + back-end servers, and creating a way to me to update this + website dynamically. I wrote a blog post about the making + here. +

+

+ 2019-01 (until current) +

+
+ +
+

+ marks.database +

+

+ A personal database management system I wrote in order to keep track + of a lot of things. I use this as a bookmark manager, a recipe and pantry + organizer, and a way to store lots of other things too. I wrote some + about making this here. +

+

+ 2019-03 +

+
+ +
+

+ Telegram Plugin Bot +

+

+ A custom Telegram bot I wrote in python that allowed for custom plugins. + This made it super easy to add functionality to a bot, and it made + my group chat with friends lively. This project has now been suceeded by a + rewrite + in Java. It was difficult to manage loading and unloading in Python, and + wrapping the API by hand made it difficult to expand beyond basic messages. +

+

+ 2017-11 (Java: 2019-10) +

+
+ +
+

+ Telegram arcade +

+

+ A general purpose bot and web server to play Telegram HTML5 games. + I couldn't find any existing solutions that allowed me to deploy + these games easily, so I wrote my own. The API is not well documented, + and I wrote a bit about it here. +

+

+ 2019-05 +

+
+ +
+

+ Read-Length +

+

+ A firefox extension that adds the reading length of a website to the + title of a page. This was my first web extension, and its technique + for finding paragraph content doesn't work great. It works well in + firefox on Ubuntu, as the window bar will display the title with + the updated reading time. +

+

+ 2019-02 +

+
+ +
+

+ Compiler +

+

+ A compiler I worked on as an indepedent study during undergrad, + implementing techniques from "Compilers: Principles, Techniques, and Tools". + I also wrote a small stack based virtual + machine that runs the + generated code +

+

+ 2019-10 +

+
+ +
+

+ streetcard +

+

+ A general purpose card game engine. I wanted to test out + some strategies for card games, so I + tried to write an engine for it. I didn't finish it, but + you can play some basic card games in this engine. +

+

+ 2017-10 +

+
+ +
+

+ Roguelike +

+

+ A rogue-like demo game I made just for fun while I was on winter break one year. +

+

+ 2017-01 +

+
+
+
+ + + \ No newline at end of file diff --git a/src/templates/tags.html b/src/templates/tags.html new file mode 100644 index 0000000..31858e8 --- /dev/null +++ b/src/templates/tags.html @@ -0,0 +1,22 @@ + + + + + Mark's Kitchen - Tags + + + + + + + +

+ < + Tag {{name}} +

+ {{> navigation}} + {{> feed}} + {{> footer}} + + + \ No newline at end of file -- cgit v1.2.3