Compare commits
8 Commits
redesign-p
...
playground
| Author | SHA1 | Date | |
|---|---|---|---|
| c3e31bb7c9 | |||
| 49d3623c49 | |||
| f091733d29 | |||
| 98b041e056 | |||
| f5a02ae2f5 | |||
| 770675fe31 | |||
| 3e79dc9695 | |||
| 63a240a8fe |
@@ -19,23 +19,24 @@ export class FileLoader implements IPluginBuilder {
|
||||
const { menuManifest } = builderContext;
|
||||
const { files } = this.options;
|
||||
files.forEach((file) => {
|
||||
const fileName =
|
||||
file.nameOverride ?? file.path.split("/").pop() ?? file.path;
|
||||
const filePath = file.path.split("/").pop() ?? file.path;
|
||||
const fileName = file.nameOverride ?? filePath;
|
||||
const isImage = fileName.match(/\.(png|jpe?g|gif|webp)$/);
|
||||
if (isImage) {
|
||||
console.log("Processing image", file.path);
|
||||
ffmpeg(file.path)
|
||||
.output(`${builderContext.outputDirectory}/${fileName}`)
|
||||
.output(`${builderContext.outputDirectory}/${filePath}`)
|
||||
.size(`${this.options.imageWidth ?? 200}x?`)
|
||||
.run();
|
||||
} else {
|
||||
console.log("Copying file", file.path);
|
||||
copyFile(file.path, `${builderContext.outputDirectory}/${fileName}`);
|
||||
const destination = `${builderContext.outputDirectory}/${filePath}`;
|
||||
console.log("Copying file", file.path, "to", destination);
|
||||
copyFile(file.path, destination);
|
||||
}
|
||||
if (file.menuEntry) {
|
||||
menuManifest.push({
|
||||
name: fileName,
|
||||
link: file.path,
|
||||
link: filePath,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
53
src/assets/base.css
Normal file
53
src/assets/base.css
Normal file
@@ -0,0 +1,53 @@
|
||||
:root {
|
||||
--color-background: #1c1b28;
|
||||
--color-accent: #00FFFF;
|
||||
--color-text: #FFF;
|
||||
|
||||
--content-width: 800px;
|
||||
|
||||
--font-size-base: 18px;
|
||||
--line-height-base: 26px;
|
||||
--font-family: monospace;
|
||||
|
||||
--space-base: 1rem;
|
||||
}
|
||||
|
||||
.banner {
|
||||
margin-top: 1rem;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 1rem;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.banner {
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
|
||||
.name {
|
||||
color: #1c1b28;
|
||||
background-color: #00FFFF;
|
||||
white-space: nowrap;
|
||||
padding: 5px;
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title::after {
|
||||
background: repeating-linear-gradient(90deg, #00FFFF, #00FFFF 2px, transparent 0, transparent 10px);
|
||||
content: "";
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ new ToolchainBuilder(paths)
|
||||
files: [
|
||||
{
|
||||
path: "src/templates/404.html",
|
||||
nameOverride: "Not Found Page",
|
||||
menuEntry: false,
|
||||
},
|
||||
{
|
||||
@@ -32,6 +31,14 @@ new ToolchainBuilder(paths)
|
||||
},
|
||||
{
|
||||
path: "src/assets/favicon.ico",
|
||||
},
|
||||
{
|
||||
path: "src/assets/base.css"
|
||||
},
|
||||
{
|
||||
path: "src/playground/playground.html",
|
||||
menuEntry: true,
|
||||
nameOverride: "CSS/HTML playground"
|
||||
}
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Initial commit
|
||||
|
||||
_2023-09-15_
|
||||
_2023-09-15 - 1 minute read_
|
||||
|
||||
I've been thinking a long time about having a place to publicly publish things. I'm not really into doing that on social media since it would lock my content in their format and make it hard to move anywhere else so i thought i would just write my "things" in plain markdown and then find a way of hosting them online.
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Markdown parser
|
||||
|
||||
## Part 1
|
||||
_2023-09-16 - 5 minute read_
|
||||
|
||||
_2023-09-16_
|
||||
## Part 1
|
||||
|
||||
While traveling today i thought about ways to structure my markdown parser.
|
||||
|
||||
What I want is a function that takes a string containing markdown formatted text and outputs a string of html elements.
|
||||
|
||||
My plan is to solve this using a markdown line feed and symbol classes representing the different possible elements.
|
||||
These symbols are for example headings, list items, code snippets, paragraphs and links.
|
||||
Each symbol will know how to render itself as well as being able to tell if it's the right symbol for the current line feed line.
|
||||
These symbols are for example headings, list items, code snippets, paragraphs and links.
|
||||
Each symbol will know how to render itself as well as being able to tell if it's the right symbol for the current feed line.
|
||||
|
||||
Lets say we have a markdown file that looks something like this:
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Tunneling traffic through ssh
|
||||
|
||||
_2024-12-23 - 1 minute read_
|
||||
|
||||
This lets you tunnel port 80 from your machine, to port 80 on another machine, using some ssh server as jump node.
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Converting various document formats
|
||||
|
||||
_2025-01-10 - 1 minute read_
|
||||
|
||||
I was in need of a way to convert markdown documents + images into pdfs and came over the [pandoc](https://pandoc.org/) project which seems to be very powerful.
|
||||
|
||||
This is how i used it.
|
||||
|
||||
@@ -13,7 +13,7 @@ Esphome made the setup very easy. After defining the onewire hub component i cou
|
||||
|
||||

|
||||
|
||||
My esphome instance is running in my local homeassistant (HASS) instance which integrates neatly and makes all sensors available to both systems. I created a new view in HASS and added the sensors showing the current values as well as a histogram of the last 5 minutes thinking that is should be a reasonable timespan to have when comparing temperatures. To my suprise the 5 minute interval in HASS didn't show 5 minutes at all but rather 24h. It was possible to zoom in on the graph to see it in more detail but i wasn't happy with how it turned out. Ideally I wanted this view to be visible on one of my screens in my brewery and it should show what I want to see without me having to interact with it.
|
||||
My esphome instance is running in my local homeassistant (HASS) instance which integrates neatly and makes all sensors available to both systems. I created a new view in HASS and added the sensors showing the current values as well as a histogram of the last 5 minutes thinking that it should be a reasonable timespan to have when comparing temperatures. To my suprise the 5 minute interval in HASS didn't show 5 minutes at all but rather 24h. It was possible to zoom in on the graph to see it in more detail but i wasn't happy with how it turned out. Ideally I wanted this view to be visible on one of my screens in my brewery and it should show what I want to see without me having to interact with it.
|
||||
|
||||

|
||||
|
||||
@@ -27,7 +27,7 @@ We usually brew two batches the same day since it allows us to do utilize the ti
|
||||
|
||||

|
||||
|
||||
We did the first batch as usual, heating up the water to 67c before dumping in the malt pipe along with our 5KG grain bill of mostly pilsner malt and some wheat. Historically we have been going off of the bult in temperature sensor in the bottom of our brewzilla brewsystem, but one of its flaws is that the sensor is located right above the two heating elements which means that it is going to measure the warmest water in the pot. This works for turning the heater on and off while recirculating using a pump while the brewery has no equipment or ingredients in it, but as soon as you add anything it is no longer going do represent the temperature of the whole vessel. We put the first sensor below the malt pipe right above the false bottom of the brewery, then we put the second one in the middle of grains.
|
||||
We did the first batch as usual, heating up the water to 67c before dumping in the malt pipe along with our 5KG grain bill of mostly pilsner malt and some wheat. Historically we have been going off of the built-in temperature sensor in the bottom of our brewzilla brewsystem, but one of its flaws is that the sensor is located right above the two heating elements which means that it is going to measure the warmest water in the pot. This works for turning the heater on and off while recirculating using a pump while the brewery has no equipment or ingredients in it, but as soon as you add anything it is no longer going do represent the temperature of the whole vessel. We put the first sensor below the malt pipe right above the false bottom of the brewery, then we put the second one in the middle of grains.
|
||||
|
||||

|
||||
_Yellow: temperature of the grain bed. Green: temperature just below the malt pipe._
|
||||
@@ -46,4 +46,12 @@ For the next batch we heated up the strike water to 75c. After adding the malt p
|
||||
|
||||
Now we just had to maintain that temperature, something that we have done before with great success by setting the heaters to 67c and starting to pump.
|
||||
|
||||

|
||||

|
||||
|
||||
By the end of the brew day the last batch measured 1.050 so suprisingly we ended up 4 points below the last batch that was identical recipe-wise. That could of course be due to multiple factors. We use pre-milled grain so depending on if we use the malt from the top or the bottom of the bucket there might be more or less grain flour. Or perhaps the mash is just more efficient at a bit lower temperature. However from what I've read the most efficient mashing range is between 65-67 degrees celcius so I'm a bit confused.
|
||||
|
||||
I think we need to brew some more beers using this method and see if we if it increases our result to recipe accuracy.
|
||||
|
||||
In the end the only thing that matters is that we brew good tasting beer and i don't think this process will change much when it comes to that. It's just feels good to hit the target!
|
||||
|
||||
Cheers!
|
||||
|
||||
BIN
src/notes/7-second-hand-MTB/assets/bike-in-car.jpg
Normal file
BIN
src/notes/7-second-hand-MTB/assets/bike-in-car.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 MiB |
23
src/notes/7-second-hand-MTB/note.md
Normal file
23
src/notes/7-second-hand-MTB/note.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Second hand MTB
|
||||
|
||||
_2025-08-17 - 2 minute read_
|
||||
|
||||

|
||||
_Tadaa!_
|
||||
|
||||
I have wanted a hardtail for a while. I've been browsing the shops from time to time but never been ready to pull the trigger on buying a second full price bike. Some weeks ago my mother made a great local find on facebook marketplace. It was a modern 11-speed carbon fiber hardtail with SRAM x01 components and it got me determined to find one myself.
|
||||
|
||||
My musts were:
|
||||
- Correct frame size (XL), duh.
|
||||
- 1x drivetrain or being able to convert to 1x easily.
|
||||
- No obvious signs of complete neglect such as rust horizontal rule broken components.
|
||||
|
||||
I want to keep this bike simple. My focus is for it to just work and be as close to a workhorse as it can be when it comes to mountain biking. That means no extra linkages, suspension or dropper-post to service. When my FS-bike needs fixing or I just want to hit the trail for some stupid fun it should be there ready to go.
|
||||
|
||||
So I found the bike I in the picture. After some detective work I found out that it probably is a 2012 KHS. Carbon fiber frame, SRAM x01 2x10 drivetrain, and best of all the guy would throw in some of the stuff that he had for the bike, one of those things was a 1x drive gear. That means that I will be able to convert it into a 1x10 drivetrain with very little effort. It looked pretty good. The top tube has been professionally repaired 5 years ago but I figured that if it had survived for this long it will probably work for me as well. I also got 3 more wheels that need some work and a longer steering bar.
|
||||
|
||||
I got to ride it a bit before deciding on the purchase and it did not creek or make any other strange sounds. The drivetrain shifted well and the suspension seemed responsive. Of course things could use a little cleaning and service but a lot of bikes do. I was pretty stoked that i found a carbon fiber bike for a good price.
|
||||
|
||||
I know that I probably want to change out the front drive gear pretty soon but first I just want to try it out on the trail to see how it performs without any changes.
|
||||
|
||||
Cheerio!
|
||||
130
src/playground/playground.html
Normal file
130
src/playground/playground.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<meta>
|
||||
<link href="base.css" rel="stylesheet" />
|
||||
<style>
|
||||
html {
|
||||
line-height: var(--line-height-base);
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: var(--content-width);
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text);
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
#style-editor {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text);
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
outline: none;
|
||||
border: 1px solid var(--color-accent);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#html-editor {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text);
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
outline: none;
|
||||
border: 1px solid var(--color-accent);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#preview {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text);
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
outline: none;
|
||||
border: 1px solid var(--color-accent);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style id="style">
|
||||
.square {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
background-color: #553FF3;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 3rem;
|
||||
position: ;
|
||||
border-radius: 30px;
|
||||
border-top-left-radius: 5px;
|
||||
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 100px;
|
||||
left: 45px;
|
||||
}
|
||||
</style>
|
||||
<div id="markup" aria-hidden="true" hidden="true" style="display: none;">
|
||||
<div class="square"><span>CSS</span></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
window.onload = function () {
|
||||
const style = document.getElementById('style').innerHTML;
|
||||
const formattedStyle = style.split("\n").reduce((agg, curr) => {
|
||||
console.log(curr)
|
||||
if (curr === "") return agg;
|
||||
const line = curr.replace('\t', '').replaceAll('\t', ' ');
|
||||
if (agg.length === 0) return line;
|
||||
return agg + "\n" + line;;
|
||||
}, "");
|
||||
console.log(formattedStyle)
|
||||
document.getElementById('style-editor').value = formattedStyle;
|
||||
document.getElementById('style').innerHTML = formattedStyle;
|
||||
|
||||
const markup = document.getElementById('markup').innerHTML;
|
||||
const formattedMarkup = markup.replace('\t', '').replaceAll('\t', ' ').split('\n').filter(line => line !== '').join('\n');
|
||||
document.getElementById('html-editor').value = formattedMarkup;
|
||||
document.getElementById('preview').innerHTML = formattedMarkup;
|
||||
}
|
||||
</script>
|
||||
</meta>
|
||||
|
||||
<body>
|
||||
<div class="banner">
|
||||
<div class="title">
|
||||
<div class="name"><a href="/">Zackarias Montell</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>This is an interactive html and css playground built with minimal JS.</p>
|
||||
<div id="preview"></div>
|
||||
<textarea rows="15" autocomplete="false" autocorrect="false" id="style-editor"
|
||||
oninput="document.getElementById('style').innerHTML = this.value">
|
||||
</textarea>
|
||||
<textarea id="html-editor" oninput="document.getElementById('preview').innerHTML=this.value" rows="15"
|
||||
autocomplete="false" autocorrect="false" id="style-editor">
|
||||
</textarea>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,15 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Page Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="message">
|
||||
<h2>404</h2>
|
||||
<h1>Page Not Found</h1>
|
||||
<p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Page Not Found</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="message">
|
||||
<h2>404</h2>
|
||||
<h1>Page Not Found</h1>
|
||||
<p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link href="base.css" rel="stylesheet" />
|
||||
<style>
|
||||
html {
|
||||
font-size: 18px;
|
||||
@@ -23,7 +24,7 @@
|
||||
body {
|
||||
max-width: 800px;
|
||||
color: #fbfbfe;
|
||||
background-color: #1c1b28;
|
||||
background-color: var(--color-background);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -84,46 +85,6 @@
|
||||
}
|
||||
|
||||
.profile li {}
|
||||
|
||||
.banner {
|
||||
margin-top: 1rem;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 1rem;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.banner {
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
|
||||
.name {
|
||||
color: #1c1b28;
|
||||
background-color: #00FFFF;
|
||||
white-space: nowrap;
|
||||
padding: 5px;
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title::after {
|
||||
background: repeating-linear-gradient(90deg, #00FFFF, #00FFFF 2px, transparent 0, transparent 10px);
|
||||
content: "";
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link href="base.css" rel="stylesheet" />
|
||||
<style>
|
||||
html {
|
||||
font-family: monospace;
|
||||
@@ -82,46 +83,6 @@
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.banner {
|
||||
margin-top: 1rem;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 1rem;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.banner {
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
|
||||
.name {
|
||||
color: #1c1b28;
|
||||
background-color: #00FFFF;
|
||||
white-space: nowrap;
|
||||
padding: 5px;
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title::after {
|
||||
background: repeating-linear-gradient(90deg, #00FFFF, #00FFFF 2px, transparent 0, transparent 10px);
|
||||
content: "";
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00FFFF;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user