generate recipes into multiple partials to fit nice formatting

This commit is contained in:
len0rd 2022-06-21 21:02:32 -04:00
parent cc054470a7
commit b2b500c3a0
10 changed files with 252 additions and 68 deletions

View file

@ -62,6 +62,6 @@
padding-left: 2%;
}
.card {
.card-homepage {
background-color: rgba(0, 0, 0, 0.1) !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 MiB

90
package-lock.json generated
View file

@ -12,6 +12,7 @@
"dynamic-scrollspy": "^0.2.0",
"ejs": "^3.1.8",
"express": "^4.17.1",
"markdown-it": "^13.0.1",
"mkdirp": "^1.0.4",
"showdown": "^2.1.0",
"showdown-highlight": "^3.0.0"
@ -29,6 +30,11 @@
"node": ">= 0.6"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@ -224,6 +230,17 @@
"node": ">= 0.8"
}
},
"node_modules/entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -432,6 +449,34 @@
"node": ">=10"
}
},
"node_modules/linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"dependencies": {
"uc.micro": "^1.0.1"
}
},
"node_modules/markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"dependencies": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"bin": {
"markdown-it": "bin/markdown-it.js"
}
},
"node_modules/mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -710,6 +755,11 @@
"node": ">= 0.6"
}
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@ -745,6 +795,11 @@
"negotiator": "0.6.2"
}
},
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@ -897,6 +952,11 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -1065,6 +1125,31 @@
"minimatch": "^3.0.4"
}
},
"linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"requires": {
"uc.micro": "^1.0.1"
}
},
"markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"requires": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
}
},
"mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -1272,6 +1357,11 @@
"mime-types": "~2.1.24"
}
},
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",

View file

@ -21,6 +21,7 @@
"dynamic-scrollspy": "^0.2.0",
"ejs": "^3.1.8",
"express": "^4.17.1",
"markdown-it": "^13.0.1",
"mkdirp": "^1.0.4",
"showdown": "^2.1.0",
"showdown-highlight": "^3.0.0"

View file

@ -13,43 +13,46 @@ const showdown = require('showdown'),
projectOutputDir = './views/partials/md/projects/',
recipeInputDir = './recipes/',
recipeOutputDir = './views/partials/md/recipes/',
classMap = {
projectClassMap = {
h1: 'display-1' //tag type : class to add to all tags of that type (class="display-1" added to all <h1>)
};
const { assert } = require('console');
function addClassToTag(text, classMap) {
var modifiedText = text;
Object.keys(classMap).forEach(function (key) {
var regex = new RegExp(`<(${key})(.*?)>`, 'g');
matcher = regex.exec(modifiedText);
// only proceed if we found a match, and the class we add isn't already on the tag somehow
while (matcher != null && !matcher[2].includes(classMap[key])) {
// add the class content WHILE preserving any other properties already in the tag!
console.log("adding class content in: " + matcher[0]);
var restOfTag = matcher[2];
modifiedText = modifiedText.replace(matcher[0], `<${key} class="${classMap[key]}" ${restOfTag}>`);
matcher = regex.exec(modifiedText);
}
});
return modifiedText;
}
// handles adding classes to specific
// tag types automatically
const addClass = {
// tag types automatically in project writeups
const projectsAddHeaderClass = {
type: 'output', // when it's triggered -> output is at the very end when text is html
filter: text => {
var modifiedText = text;
Object.keys(classMap).forEach(function (key) {
var regex = new RegExp(`<${key}(.*?)>`, 'g');
matcher = regex.exec(modifiedText);
// only proceed if we found a match, and the class we add isn't already on the tag somehow
while (matcher != null && !matcher[0].includes(classMap[key])) {
// add the class content WHILE preserving any other properties already in the tag!
console.log("adding class content in: " + matcher[0]);
var restOfTag = matcher[1];
modifiedText = modifiedText.replace(matcher[0], `<${key} class="${classMap[key]}" ${restOfTag}>`);
matcher = regex.exec(modifiedText);
}
});
return modifiedText;
}
filter: text => { return addClassToTag(text, projectClassMap); }
};
// create our Showdown converter with our custom extension
const converter = new showdown.Converter({
extensions: [addClass, showdownHighlight],
// create Showdown converters
const projectsConverter = new showdown.Converter({
extensions: [projectsAddHeaderClass, showdownHighlight],
tables: true
});
function convertMarkdownInDir(inputDir, outputDir) {
function convertMarkdownInDirWithShowdown(inputDir, outputDir, converter) {
// make the directory for the html output if necessary
mkdirp.sync(outputDir);
fs.readdir(inputDir, (err, files) => {
@ -74,5 +77,93 @@ function convertMarkdownInDir(inputDir, outputDir) {
});
}
convertMarkdownInDir(projectInputDir, projectOutputDir);
convertMarkdownInDir(recipeInputDir, recipeOutputDir);
function convertRecipeMarkdown(inputDir, outputDir) {
var md = require('markdown-it')();
// This is a hardcoded markdown header section number to html file name
//
// Example.md:
// """
// ... maybe some other header info here -| - exported as filename-title.ejs
// # Delicious Recipe Name -|
// Catch phrase or yield -|
// | - exported as filename-subtitle
// image of the food |
// -|
// ## Ingredients -|
// ... ingredients table, etc | - exported as filename-ingredients.ejs
// -|
// ## Instructions
// """
//
// NOTE: these titles are HARDCODED in recipe_template.ejs!
const mdSectionHtmlTitles = [
'title',
// 'subtitle',
'ingredients',
'instructions',
]
mkdirp.sync(outputDir);
fs.readdir(inputDir, (err, files) => {
files.forEach(file => {
if (file.endsWith('.md')) {
let fileNameNoExtension = file.slice(0, -3);
console.log('converting: ' + fileNameNoExtension);
fs.readFile(inputDir + file, 'utf8', (err, data) => {
if (err) {
console.error(err);
} else {
let tokens = md.parse(data)
let sections = []
sections.push([]); // start off the array and put everything before and including the first header in title
let numSections = 0;
for (const token of tokens) {
if (token.type === 'heading_open') {
if (numSections == 0) {
numSections++;
}
else if (numSections < mdSectionHtmlTitles.length) {
console.log("found heading open. start new section arr");
numSections++;
sections.push([]);
}
}
sections[sections.length - 1].push(token)
}
assert(sections.length <= mdSectionHtmlTitles.length);
// hardcode bootstrap class attribute to add to <table> tag in ingredients
for (let ii = 0; ii < sections[1].length; ii++) {
if (sections[1][ii].type == 'table_open') {
sections[1][ii].attrs = [["class", "table table-striped table-sm table-hover"]];
break;
}
}
// console.log(sections[0]);
for (let ii = 0; ii < sections.length; ii++) {
let html = md.renderer.render(sections[ii], md.options);
// hardcode making images in the title section larger
if (ii == 0) {
var regex = new RegExp(`<img (.*?)>`, `g`);
matcher = regex.exec(html);
while (matcher != null && !matcher[1].includes("w-100")) {
var restOfTag = matcher[1];
html = html.replace(matcher[0], `<img class="w-100" ${restOfTag}>`);
matcher = regex.exec(html);
}
}
fs.writeFileSync(outputDir + fileNameNoExtension + '-' + mdSectionHtmlTitles[ii] + '.ejs', html, 'utf8');
}
}
});
}
});
});
}
convertMarkdownInDirWithShowdown(projectInputDir, projectOutputDir, projectsConverter);
convertRecipeMarkdown(recipeInputDir, recipeOutputDir);

View file

@ -28,15 +28,15 @@ With the requirements thought out, I started planning how I was going to build e
There were some really cool designs online, but I needed something simple. This was my first woodworking project, and I knew I *would* (hehe) be making a lot of mistakes. All of these thoughts culminated into a single whiteboard sketch:
![Whiteboard sketch of ititial design](/img/writeup/palletDesk/plans-1-sm.jpg)
![Whiteboard sketch of initial design](/img/writeup/palletDesk/plans-1-sm.jpg)
Yep. That's it. It was all a lot clearer in my head. Essentially the red 'rectangles' are 2x4 cross beams that would support the desktop and create a structure to build onto. The measurements were mainly based on what would fit in my Camry. Note: this only fits in the car when I put the back seats down so the desk can go through the trunk and into the back of the car. pics further down. I also measured a few desks nearby to see what an appropriate depth for a monitor + keyboard would be.
## Build
### Pallet Aquisition
### Pallet Acquisition
There are a lot of guides on how to get pallets online. If you're in the US, check the free section of Craigslist or your local classifieds. You can also just walk into local places and ask if they have any pallets that you can use. Alternatively, just drive behind businesses and see if they have pallets stacked near the dumpster.
There are a lot of guides on how to get pallets online. If you're in the US, check the free section of Craigslist or your local classifieds. You can also just walk into local places and ask if they have any pallets that you can use. Alternatively, just drive behind businesses and see if they have pallets stacked near the dumpster.
If you have a smaller car, be prepared to potentially tear down the pallet on site. Pallets were a bit larger than I expected and some of them would not fit in my car! Because of this, I had to pass up on some of the nicer pallets out there. In all, I think this project took 2-3 pallets. It could be done with less, but my teardown methods were less than ideal.
@ -44,9 +44,9 @@ The problem with pallet wood is that it's a bit of a mixed bag. Sometimes you ge
### Pallet Teardown
The best way to get usable wood from a pallet is to use a jig saw. Period. If you don't have one, buy one online or borrow a friends. It's worth it especially if you want to do a lot of projects like this. With a jig saw, it's very easy to align the blade between the pallet planks and cut through the nails holding them in place. If you have room to spare, you could also use a table saw and just cut off the pallet planks before they're nailed into the base.
The best way to get usable wood from a pallet is to use a jig saw. Period. If you don't have one, buy one online or borrow a friends. It's worth it especially if you want to do a lot of projects like this. With a jig saw, it's very easy to align the blade between the pallet planks and cut through the nails holding them in place. If you have room to spare, you could also use a table saw and just cut off the pallet planks before they're nailed into the base.
If you're a poor college student as I was, the other option is a crowbar, hammer and *a lot* of patience. With this method you need to jimmy the crow bar in between the pallet plank and base and slowely wedge out the nails holding the plank in place. Making sure you dont strain one end of the plank too much and crack it. This is a delicate process that takes a long time and often ends in breaking the pallet planks much to the despair of the builder.
If you're a poor college student as I was, the other option is a crowbar, hammer and *a lot* of patience. With this method you need to jimmy the crow bar in between the pallet plank and base and slowely wedge out the nails holding the plank in place. Making sure you dont strain one end of the plank too much and crack it. This is a delicate process that takes a long time and often ends in breaking the pallet planks much to the despair of the builder.
Because of this expense of learning, Perhaps only 50% of the 3 pallets I had turned out usable. Unfortunately I don't have any images of this stage of the process likely due to the rage-inducing nature of it all. I worked on this portion of the project at night after getting home from school across a couple of days.
@ -70,7 +70,7 @@ Also, for this entire project, wood was cut to size using my friends chop saw. N
![Closeup of desktop frame with crossbeams](/img/writeup/palletDesk/build-4-sm.jpg)
With the basic frame in place, it was time to start building the visible part of the desktop. I started by bordering the exterior with pallet plants to make something like a picture frame for the middle. The chop saw came in handy, helping make angles that match.
With the basic frame in place, it was time to start building the visible part of the desktop. I started by bordering the exterior with pallet plants to make something like a picture frame for the middle. The chop saw came in handy, helping make angles that match.
![Desktop border mostly in place](/img/writeup/palletDesk/build-5-sm.jpg)
@ -86,9 +86,9 @@ Once the crate paper was secured I started cutting and fitting pallet planks. I
Once a single board was pushed to the far right side, It would be secured on the 3 2x4 frame pieces with screws and glue. Once all the pieces were secured I went back and started sanding. The desktop was made from a combination of three different pallets, so the wood was vastly different in quality and thickness.
My **biggest** regret in this project was not spending enough time sanding the top. Especially the middle of the desk, I should've spend multiple hours sanding it, with multiple passes at different levels of courseness to make sure it was smooth and flat. A planar and/or a really good electric sander should be a must for this portion of the project. A single board in the middle of my destop is slightly thicker than the others, which means my keyboard can not lie flat without wobbling.
My **biggest** regret in this project was not spending enough time sanding the top. Especially the middle of the desk, I should've spend multiple hours sanding it, with multiple passes at different levels of courseness to make sure it was smooth and flat. A planar and/or a really good electric sander should be a must for this portion of the project. A single board in the middle of my destop is slightly thicker than the others, which means my keyboard can not lie flat without wobbling.
If you plan on covering the top in epoxy as I did, this is also a good time to make *sure* any and all holes are filled with wood filler (I made wood filler by mixing wood glue with some sawdust). You dont want anywhere for the epoxy to escape so make sure all the old nail holes on the pallet boards are properly filled.
If you plan on covering the top in epoxy as I did, this is also a good time to make *sure* any and all holes are filled with wood filler (I made wood filler by mixing wood glue with some sawdust). You dont want anywhere for the epoxy to escape so make sure all the old nail holes on the pallet boards are properly filled.
### Legs
@ -138,4 +138,4 @@ And, in case you doubted, here's the desk broken down and in the trunk of my Cam
The desk works great, and overall the design is very forgiving to rookie mistakes. Make sure you have the proper tools (Jigsaw) for pallet dissassembly. Dont skimp on sanding the desktop, and if you're going to use epoxy, make *absolutely* sure that you have adequately filled in all the nail holes in the pallet wood before application.
![Done! 4](/img/writeup/palletDesk/finished-1-lg.jpg)
![Done! 4](/img/writeup/palletDesk/finished-1-lg.jpg)

View file

@ -2,6 +2,8 @@
*yields: ~4 dozen*
![Cookies](/img/recipes/chocolateChipCookies.jpeg)
## Ingredients
Measure | Weight | Ingredient
@ -16,7 +18,7 @@ Measure | Weight | Ingredient
2 3/4c | 370g | Flour
1 tsp | 7g | Baking Soda
1 tsp | 7g | Salt
| 1g | Cinnamon
1/4 tsp | 1g | Cinnamon
2c | | Chocolate Chip
## Instructions

View file

@ -40,7 +40,7 @@
</div>
<div class="container pb-5 pt-5">
<div class="card-columns">
<div class="card bg-dark border-light text-white">
<div class="card bg-dark card-homepage border-light text-white">
<div class="card-body">
<h5 class="card-title">Yama Crawler</h5>
<h6 class="card-subtitle mb-2 text-muted">Selenium-Based Web Crawler</h6>
@ -52,7 +52,7 @@
Code</a>
</div>
</div>
<div class="card bg-dark border-light text-white">
<div class="card bg-dark card-homepage border-light text-white">
<div class="card-body">
<h5 class="card-title">Mavlib Gen</h5>
<h6 class="card-subtitle mb-2 text-muted">Modern Mavlink C generator</h6>
@ -63,7 +63,7 @@
<a href="https://github.com/len0rd/mavlib_gen" class="card-link card-soft-link">See Code</a>
</div>
</div>
<div class="card bg-dark border-light text-white">
<div class="card bg-dark card-homepage border-light text-white">
<div class="card-body">
<h5 class="card-title">Darkstar</h5>
<h6 class="card-subtitle mb-2 text-muted">Why buy a quad when you can build it</h6>
@ -78,7 +78,7 @@
Code</a>
</div>
</div>
<div class="card bg-dark border-light text-white">
<div class="card bg-dark card-homepage border-light text-white">
<div class="card-body">
<h5 class="card-title">Pallet Desk</h5>
<h6 class="card-subtitle mb-2 text-muted">Reliable and cheap desk</h6>
@ -89,7 +89,7 @@
<a href="projects/palletDesk" class="btn btn-outline-light">Read More</a>
</div>
</div>
<div class="card bg-dark border-light text-white">
<div class="card bg-dark card-homepage border-light text-white">
<div class="card-body">
<h5 class="card-title">My Website</h5>
<h6 class="card-subtitle mb-2 text-muted">Is this meta</h6>
@ -104,7 +104,7 @@
</div>
</div>
<div class="card bg-dark border-light text-white">
<div class="card bg-dark card-homepage border-light text-white">
<div class="card-body">
<h5 class="card-title">LS-1 Synth</h5>
<h6 class="card-subtitle mb-2 text-muted">Music to my ears</h6>

View file

@ -11,15 +11,13 @@
<%- include('../partials/nav') %>
</header>
<div class="container mt-5">
<div class="topMargin">
<h1>Recipes</h1>
<div class="list-group">
<a href="recipes/chocolateChipCookies" class="list-group-item list-group-item-action">Chocolate Chip Cookies</a>
<a href="#" class="list-group-item list-group-item-action">Morbi leo risus</a>
<a href="#" class="list-group-item list-group-item-action">Porta ac consectetur ac</a>
<a href="#" class="list-group-item list-group-item-action">Vestibulum at eros</a>
</div>
<div class="container mt-5 topMargin">
<h1>Recipes</h1>
<div class="list-group">
<a href="recipes/chocolateChipCookies" class="list-group-item list-group-item-action">Chocolate Chip Cookies</a>
<a href="#" class="list-group-item list-group-item-action">Morbi leo risus</a>
<a href="#" class="list-group-item list-group-item-action">Porta ac consectetur ac</a>
<a href="#" class="list-group-item list-group-item-action">Vestibulum at eros</a>
</div>
</div>

View file

@ -12,20 +12,22 @@
<%- include(rootPath + 'partials/nav') %>
</header>
<div class="container mt-5">
<div class="row">
<%- include(rootPath + page) %>
</div>
</div>
<div class="container mt-5 topMargin">
<div class="row g-0">
<div class="col-md-8">
<%- include(rootPath + page + '-title') %>
</div>
<div class="col">
<div class="card">
<div class="card-body">
<%- include(rootPath + page + '-ingredients') %>
</div>
</div>
</div>
</div>
<%- include(rootPath + 'partials/footer') %>
<%- include(rootPath + 'partials/post_html_include') %>
<script type="text/javascript" src="/script/dynamicscrollspy.min.js"></script>
<script>
$('#scrollBar').DynamicScrollspy({
ulClassNames: 'navbar navbar-light bg-light sticky-top sticky-offset'
});
</script>
<%- include(rootPath + page + '-instructions') %>
</div>
<%- include(rootPath + 'partials/post_html_include') %>
</body>