mirror of
https://github.com/len0rd/personal-website.git
synced 2025-03-01 03:51:57 -05:00
happy with the blog portion. Recipes needs to be re-integrated
This commit is contained in:
parent
932c17d274
commit
67fd3d91af
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
||||||
*.DS_STORE
|
*.DS_STORE
|
||||||
node_modules
|
_website/
|
||||||
views/partials/md/
|
|
||||||
views/partials/generated/
|
|
||||||
*.mp4
|
*.mp4
|
||||||
|
.doctrees/
|
||||||
|
__pycache__/
|
||||||
|
|
1
_templates/sitename.html
Normal file
1
_templates/sitename.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<a class="navbar-brand text-wrap" href="{{ pathto(master_doc) }}"><h1 class="site-logo" id="site-title">{{ html_title }}</h1></a>
|
|
@ -1,37 +0,0 @@
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
padding-top: 2vh;
|
|
||||||
padding-bottom: 2vh;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
width: 50%;
|
|
||||||
margin-top: 2vh;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
padding-left: 5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin-left: 5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
margin-left: 5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
li p {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sticky-offset {
|
|
||||||
top: 56px !important;
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
.icon-footer {
|
|
||||||
padding: 3%;
|
|
||||||
padding: 2vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bgimage {
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
background: url('../img/logo.svg');
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bgimage h1 {
|
|
||||||
color: white;
|
|
||||||
text-shadow: 2px 2px #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) translateY(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.cenMain {
|
|
||||||
padding-top: 40%;
|
|
||||||
padding-bottom: 40%;
|
|
||||||
padding-top: 40vh;
|
|
||||||
padding-bottom: 40vh;
|
|
||||||
width: 60%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-cenMain {
|
|
||||||
height: 100%;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topMargin {
|
|
||||||
padding-top: 5%;
|
|
||||||
padding-top: 5vh;
|
|
||||||
width: 60%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
background-color: #09003d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-soft-link {
|
|
||||||
color: #5AB2DA;
|
|
||||||
padding-left: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-homepage {
|
|
||||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
|
||||||
padding: 2%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
75
assets/diagrams/hal_concept.drawio
Normal file
75
assets/diagrams/hal_concept.drawio
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<mxfile host="65bd71144e">
|
||||||
|
<diagram id="SKV8PHPIui5c4Fc6kAOh" name="Page-1">
|
||||||
|
<mxGraphModel dx="889" dy="665" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
<mxCell id="11" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" parent="1" source="2" target="8" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=diamond;endFill=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="2" target="12" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="460" y="100"/>
|
||||||
|
<mxPoint x="425" y="100"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="2" value="Hardware Abstraction Layer (Interfaces of core hardware drivers)" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="260" width="360" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="3" value="ISPI" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="4" value="IGPIO" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="320" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="5" value="IUART" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="390" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="6" value="II2C" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="460" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="7" value="..." style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="530" y="290" width="80" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="13" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" parent="1" source="8" target="12" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="8" value="Device Drivers <br>(decoupled from underlying<br>&nbsp;hardware driver implementation)" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="120" width="180" height="100" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9" value="IMU" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="180" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="10" value="etc etc" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="320" y="180" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="12" value="Libraries<br>(Business logic that uses a combination of device and<br>hardware driver interfaces)" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="10" width="350" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="16" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=classicThin;endFill=0;" parent="1" source="15" target="5" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="15" value="Hardware-specific implementations of interfaces" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="245" y="370" width="360" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="17" value="MCU X<br>implementation" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="262.5" y="410" width="117.5" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="18" value="MCU Y<br>implementation" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="410" y="410" width="120" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="19" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="210" y="400" width="20" height="90" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="20" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;flipH=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="620" y="400" width="20" height="90" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="22" value="<h1><br></h1><div>Implementation is build-time selectable depending on your target environment</div>" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;align=right;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="20" y="370" width="190" height="110" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
112
assets/diagrams/hal_concept_with_board.drawio
Normal file
112
assets/diagrams/hal_concept_with_board.drawio
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<mxfile host="65bd71144e">
|
||||||
|
<diagram id="SKV8PHPIui5c4Fc6kAOh" name="Page-1">
|
||||||
|
<mxGraphModel dx="1739" dy="1765" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
<mxCell id="30" value="Application ABC<br>- top-level execution (main())<br>- initializes libraries required for app using<br>interface references from Board Data ABC" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="155" y="-200" width="270" height="160" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="11" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" parent="1" source="2" target="8" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;html=1;endArrow=diamond;endFill=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="2" target="12" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="460" y="90"/>
|
||||||
|
<mxPoint x="470" y="90"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="2" value="Hardware Abstraction Layer (Interfaces of core hardware drivers)" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="260" width="360" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="3" value="ISPI" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="4" value="IGPIO" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="320" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="5" value="IUART" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="390" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="6" value="II2C" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="460" y="290" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="7" value="..." style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="530" y="290" width="80" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="13" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" parent="1" source="8" target="12" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="28" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" edge="1" parent="1" source="8" target="27">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="340" y="90"/>
|
||||||
|
<mxPoint x="290" y="90"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="8" value="Device Drivers <br>(decoupled from underlying<br>&nbsp;hardware driver implementation)" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="120" width="180" height="100" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9" value="IMU" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="250" y="180" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="10" value="etc etc" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="320" y="180" width="70" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="31" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" edge="1" parent="1" source="12" target="27">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="12" value="Libraries<br>(Business logic that uses a combination of device and<br>hardware driver interfaces)" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="325" y="10" width="290" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="16" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=classicThin;endFill=0;" parent="1" source="15" target="5" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="26" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" edge="1" parent="1" source="15" target="23">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="15" value="Hardware-specific implementations of interfaces" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="245" y="370" width="360" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="17" value="MCU X<br>implementation" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="262.5" y="410" width="117.5" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="18" value="MCU Y<br>implementation" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="410" y="410" width="120" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="19" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="210" y="400" width="20" height="90" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="20" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;flipH=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="620" y="400" width="20" height="90" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="22" value="<h1><br></h1><div>Implementation is build-time selectable depending on your target environment</div>" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;align=right;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="20" y="370" width="190" height="110" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="29" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=diamond;endFill=0;" edge="1" parent="1" source="23" target="27">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<Array as="points">
|
||||||
|
<mxPoint x="105" y="90"/>
|
||||||
|
<mxPoint x="290" y="90"/>
|
||||||
|
</Array>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="23" value="Board Configuration info:<br>- gpio configuration<br>- clock configuration<br>- any special bootup specifics" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="-30" y="120" width="270" height="105" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="24" value="Board A<br>Has "MCU X"" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="-20" y="200" width="120" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="25" value="Board B<br>Has "MCU Y"" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="110" y="200" width="120" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="27" value="Board Data ABC<br>- Selects board<br>- initializes board-specific drivers that are<br>required by the application" style="html=1;align=center;verticalAlign=top;rounded=1;absoluteArcSize=1;arcSize=10;dashed=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="155" y="-120" width="270" height="80" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
59
conf.py
Normal file
59
conf.py
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Sphinx docs configuration for building project documentation
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
project = "lenordsNet"
|
||||||
|
author = "lenord"
|
||||||
|
copyright = f"{datetime.now().year}, lenordsNet"
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
"sphinxcontrib.youtube",
|
||||||
|
"ablog",
|
||||||
|
"sphinx.ext.intersphinx",
|
||||||
|
"sphinx_design",
|
||||||
|
]
|
||||||
|
|
||||||
|
html_static_path = ["_static"]
|
||||||
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
|
blog_baseurl = "https://lenord.me/"
|
||||||
|
# A path relative to the configuration directory for posts archive pages.
|
||||||
|
blog_path = "posts"
|
||||||
|
# The "title" for the posts, used in active pages. Default is ``'Blog'``.
|
||||||
|
blog_title = "lenordsNet"
|
||||||
|
|
||||||
|
fontawesome_included = True
|
||||||
|
|
||||||
|
html_baseurl = blog_baseurl
|
||||||
|
html_title = blog_title
|
||||||
|
|
||||||
|
html_theme = "sphinx_book_theme"
|
||||||
|
html_theme_options = {
|
||||||
|
"repository_url": "https://github.com/len0rd/",
|
||||||
|
"search_bar_text": "search ...",
|
||||||
|
"show_prev_next": False,
|
||||||
|
"navbar_center": [],
|
||||||
|
"use_fullscreen_button": False,
|
||||||
|
"use_repository_button": True,
|
||||||
|
# "footer_items": ["copyright", "sphinx-version", x"last-updated"],
|
||||||
|
}
|
||||||
|
html_favicon = "assets/img/favicon.ico"
|
||||||
|
|
||||||
|
html_sidebars = {
|
||||||
|
"*": [
|
||||||
|
"sitename.html",
|
||||||
|
"search-field.html",
|
||||||
|
"recentposts.html",
|
||||||
|
"archives.html",
|
||||||
|
],
|
||||||
|
"posts/**": [
|
||||||
|
"sitename.html",
|
||||||
|
"search-field.html",
|
||||||
|
"postcard.html",
|
||||||
|
"recentposts.html",
|
||||||
|
"archives.html",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
html_context = {"html_title": html_title}
|
||||||
|
|
||||||
|
pygments_style = "sas"
|
15
index.rst
Normal file
15
index.rst
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Recent Posts
|
||||||
|
============
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:name: mastertoc
|
||||||
|
:caption: Contents
|
||||||
|
:titlesonly:
|
||||||
|
:hidden:
|
||||||
|
|
||||||
|
.. postlist:: 10
|
||||||
|
:author: len0rd
|
||||||
|
:date: %Y-%m-%d
|
||||||
|
:format: {date} - {title}
|
||||||
|
:list-style: none
|
||||||
|
:excerpts:
|
1387
package-lock.json
generated
1387
package-lock.json
generated
File diff suppressed because it is too large
Load diff
27
package.json
27
package.json
|
@ -1,27 +0,0 @@
|
||||||
{
|
|
||||||
"name": "len0rd_net",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "src for lenords.net",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"prestart": "./server_startup.sh"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/len0rd/personal-website.git"
|
|
||||||
},
|
|
||||||
"author": "len0rd",
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/len0rd/personal-website/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/len0rd/personal-website",
|
|
||||||
"dependencies": {
|
|
||||||
"ejs": "^3.1.8",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"markdown-it": "^13.0.1",
|
|
||||||
"markdown-it-hashtag": "^0.4.0",
|
|
||||||
"mkdirp": "^1.0.4"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1,6 @@
|
||||||
sphinx
|
sphinx
|
||||||
sphinx_bootstrap_theme
|
ablog
|
||||||
|
sphinx-design
|
||||||
|
pydata_sphinx_theme
|
||||||
sphinxcontrib-youtube
|
sphinxcontrib-youtube
|
||||||
|
sphinx-book-theme
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
Darkstar Quadcopter
|
Darkstar Quadcopter
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
.. post:: 31, July 2018
|
||||||
|
:tags: drone, diy
|
||||||
|
:category: Projects
|
||||||
|
:author: len0rd
|
||||||
|
|
||||||
The Darkstar is a RC quadcopter with the ability to fly autonomously through pre-designated waypoints, using advanced estimation techniques and object avoidance.
|
The Darkstar is a RC quadcopter with the ability to fly autonomously through pre-designated waypoints, using advanced estimation techniques and object avoidance.
|
||||||
|
|
||||||
At least that is the end-goal. Getting there however requires resources I simply do not have as a college student (read: money). Given such constraints, building an advance copter on the cheap sounded like a good challenge. Perhaps you can learn from my mistakes if you're interested in such a venture.
|
At least that is the end-goal. Getting there however requires resources I simply do not have as a college student (read: money). Given such constraints, building an advance copter on the cheap sounded like a good challenge. Perhaps you can learn from my mistakes if you're interested in such a venture.
|
123
posts/embedded_dev_primer.rst
Normal file
123
posts/embedded_dev_primer.rst
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
.. embedded_dev_primer:
|
||||||
|
|
||||||
|
Embedded Development Primer
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. post:: 05, January 2023
|
||||||
|
:tags: diy, docker, embedded, development, toolchain, advice
|
||||||
|
:category: Projects
|
||||||
|
:author: len0rd
|
||||||
|
|
||||||
|
This post is meant to be an overview of some principles and practices I have found helpful for professional baremetal embedded development. This is meant for people who want to have total control over their embedded development environment and codebase.
|
||||||
|
|
||||||
|
Tools like `platformio <https://platformio.org/>`_ are excellent and can help us get projects off the ground but are perhaps a little opaque when it comes to toolchain and dependency management. In a professional setting, I often find I need total control over toolchains and dependencies down to the lowest-level. Learning how to intelligently manage these tools in a modern way has been a major effort in my career so far. This post will serve as a collection of my thoughts and lessons-learned from building multiple embedded projects from the ground up.
|
||||||
|
|
||||||
|
Lesson 1: Docker for toolchain management
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
Step in my shoes for a minute: You start a new job. As usual your first task is setting up a development machine for the project. How do you setup the machine? Ah of course, the project has a 10,000 word README with detailed instructions of how to set everything up. It requires you use a very specific linux distro/kernel, install packages in the 'correct' order, and adhere to a specific directory structure. You follow the steps. The project fails to build. You now spend a week combing through the README, comparing your machine to other developers, maybe even reinstalling and starting from scratch.
|
||||||
|
|
||||||
|
Does this scenario sound familiar? Its something I have encountered multiple times in my career. Obviously there are a lot of issues with the "installation README" approach. I've also worked projects where a monolithic bash script handles dependency setup. While slightly better, this often encounters the same problems as a README. The root of the issue is that existing developers do not have to consistently use the installation README/script. Therefore these helper utilities are not maintained and eventually fall out of date. They are only touched when a new developer is required to struggle through them. They also become a nightmare when larger upgrades (like a distro bump) become necessary.
|
||||||
|
|
||||||
|
These dependency-management issues extend far beyond just setting up a new environment. They also contribute to the core "it works on my machine" problems we so often encounter. In embedded development such problems are amplified since our dependencies (toolchains, debug packages) seemingly cant be neatly container-ized like other languages (python). `Docker <https://docs.docker.com/>`_ is the solution to all of these problems.
|
||||||
|
|
||||||
|
|
||||||
|
What does docker provide?
|
||||||
|
|
||||||
|
- **A documented, repeatable dependency installation method**
|
||||||
|
|
||||||
|
- **Dependency management that can be version-controlled with the project.** Your dockerfile and the dependencies it captures evolve with the project!
|
||||||
|
|
||||||
|
- **A consistent environment between developers and CI servers.** Greatly reducing the "it works on my machine" problem
|
||||||
|
|
||||||
|
- **Development environment freedom.** Developers can run any parent distro or IDE they want as long as they can use docker
|
||||||
|
|
||||||
|
- **Easy to experiment with and revert dependency changes.** This is huge when looking at bumping toolchains
|
||||||
|
|
||||||
|
- **Project environment isolation.** One project doesn't mess things up for another project
|
||||||
|
|
||||||
|
Truly I can not sing enough praises to how docker has streamlined embedded development in my career. Today, many IDEs support developing from within a docker container. For instance VSCodes `devcontainer extension <https://code.visualstudio.com/docs/devcontainers/create-dev-container>`_. But since docker is an independent tool, your free to use it with any ide/text-editor you want.
|
||||||
|
|
||||||
|
Some common questions:
|
||||||
|
|
||||||
|
What about debugging?
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If my toolchain (gcc, gdb) is all within a docker container, how do I flash or debug my target hardware? Easy, just mount your devices into the container. When you run a container in privileged mode (``--privileged``) you can bind mount pretty much anything. So I will bind-mount ``/dev`` which means all USB devices attached to my host are also accessible within the container:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
docker run --mount source=/dev,target=/dev,type=bind --privileged --mount<BIND MOUNT PROJECT DIR> <IMAGE_NAME>
|
||||||
|
|
||||||
|
As you would expect, your container will also need udev rules installed if you want to access devices as a non-root user. This configuration step can be easily captured in your dockerfile!
|
||||||
|
|
||||||
|
Sharing a development dockerfile with a CI server?
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Dependencies/requirements for development are not the same as CI/CD requirements. Typically development requirements are a super-set of CI/CD requirements. ie: developers need additional debug/helper tools in addition to the core toolchain dependencies. This is where `multi-stage docker builds <https://docs.docker.com/build/building/multi-stage/>`_ come in. They allow you to define separate stages in a dockerfile. Thus allowing you to avoid unnecessary installations on CI. For example:
|
||||||
|
|
||||||
|
.. code-block:: dockerfile
|
||||||
|
|
||||||
|
FROM ubuntu:latest AS project_builder_base
|
||||||
|
|
||||||
|
RUN apt update \
|
||||||
|
&& apt install <toolchain>
|
||||||
|
&& ...
|
||||||
|
|
||||||
|
FROM project_builder_base AS project_development
|
||||||
|
|
||||||
|
RUN apt update \
|
||||||
|
&& apt install openocd <debugTool2> ...
|
||||||
|
|
||||||
|
Then in CI you only build the ``project_builder_base`` stage with:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
docker build --target project_builder_base -t projectTag:latest
|
||||||
|
|
||||||
|
Multi-stage builds like this require using dockers new build backend `"buildkit" <https://docs.docker.com/build/buildkit/>`_
|
||||||
|
|
||||||
|
Lesson 2: Abstract ASAP
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Making unit-testable code in embedded development can be challenging. There is a good chunk of code that can only be run on your target and would be complex to either mock out or build an automated test-rig for. I'm talking about the low-level drivers that interact directly with a MCU's hardware peripherals. For this reason, I think its critical to create a decoupling abstraction layer as-soon-as-possible in modern embedded development.
|
||||||
|
|
||||||
|
What does that look like? Something like this:
|
||||||
|
|
||||||
|
.. image:: ../assets/img/writeup/embedded_dev_primer/hal_concept.svg
|
||||||
|
|
||||||
|
Notice all SOC/MCU-specific code is encapsulated down in a single library which we abstract out with an interface layer immediately.
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
- **Easy to build a project for multiple targets** This benefit is huge. Imagine being able to build the same application for both linux (to unit test/simulate) and for your embedded target. You will catch bugs sooner and easier
|
||||||
|
|
||||||
|
- **All layers above the Hardware Abstraction Layer can be unit tested.** This includes device drivers (ie: sensor or actuator drivers) which in old-style development are often coupled to a specific MCU peripheral.
|
||||||
|
|
||||||
|
- **Component modularity.** At a system level, it is now very simple to move drivers and libraries from one MCU to another. As long as you have a Hardware Abstraction Layer (HAL) implementation for the new MCU you can mix and match things around. This can be critical in the prototyping stage of product development
|
||||||
|
|
||||||
|
I plan to dive into these concepts further in a future post.
|
||||||
|
|
||||||
|
Lesson 3: Separate the concepts of MCU and board
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
This concept is going to add yet-another piece to the Hardware Abstraction Layer puzzle. Separating your HAL implementation from board-specifics is an important step to having a fully modular embedded development project.
|
||||||
|
|
||||||
|
What are "board-specifics"? This is the term I use to describe the configuration information of a MCU that is specific to a real piece of hardware. For instance: MCU's will have a set of GPIOs. These GPIOs can be used as basic GPIOs or they can be mapped to SPI clocks, UART data lines, etc etc. The functional mapping of these pins is a detail that is specific to the overall hardware (aka 'board') you run your application on. It makes sense that information like this that is specific to a PCB board layout of a particular MCU should be encapsulated in its own 'library'. This library is separate from your HAL implementation
|
||||||
|
|
||||||
|
Here's an extension of the earlier diagram with these concepts added:
|
||||||
|
|
||||||
|
.. image:: ../assets/img/writeup/embedded_dev_primer/hal_concept_with_board.svg
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
- **Easy to track hardware revisions.** Structuring your project this way allows you to simultaneously support multiple revisions of a single board, which can be valuable if hardware availability is limited
|
||||||
|
|
||||||
|
- **Hardware Abstraction Layer doesnt change over board revisions.** With MCU peripheral drivers decoupled from board hardware - they dont need to change with each board change
|
||||||
|
|
||||||
|
- **Applications only instantiate drivers/resources they need.** Having a abstracted board data at the application-level allows you to only instantiate the drivers/peripherals that are required by the application. This will save memory and compute.
|
||||||
|
|
||||||
|
Conclusion
|
||||||
|
----------
|
||||||
|
|
||||||
|
This was a very brief overview of some of my most valuable baremetal development lessons-learned. Following these lessons allows truly agile hardware and software development in the embedded space, which I dont think was possible with older development paradigms. I plan to expand on each of these topics in future posts.
|
|
@ -3,6 +3,11 @@
|
||||||
LS-1: Modular Synth
|
LS-1: Modular Synth
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
.. post:: 04, September 2018
|
||||||
|
:tags: audio, synthesizer, diy, breadboard
|
||||||
|
:category: Projects
|
||||||
|
:author: len0rd
|
||||||
|
|
||||||
The LS-1 is a modular oscillator and sequencer, and includes 2 LFO’s (low-frequency oscillators), one external oscillator, and the oscillator attached to the sequencer. The sequencer itself is made up of a counter which acts as a LFO/clock divider, and dual muxes to select the feedback resistance and led to display. The counter outputs and mux select inputs have ports on the front-panel allowing the user to mix and match LFO divisions with mux selects, thus creating custom sequences.
|
The LS-1 is a modular oscillator and sequencer, and includes 2 LFO’s (low-frequency oscillators), one external oscillator, and the oscillator attached to the sequencer. The sequencer itself is made up of a counter which acts as a LFO/clock divider, and dual muxes to select the feedback resistance and led to display. The counter outputs and mux select inputs have ports on the front-panel allowing the user to mix and match LFO divisions with mux selects, thus creating custom sequences.
|
||||||
|
|
||||||
This build included a lot of firsts for me. This is the first time I’ve used Eagle to create a PCB/schematic (which should honestly be considered an atrocity given I am a Computer Engineering student), my first in creating a metal case using a water jet, and in general this is my first large-scale hobby project.
|
This build included a lot of firsts for me. This is the first time I’ve used Eagle to create a PCB/schematic (which should honestly be considered an atrocity given I am a Computer Engineering student), my first in creating a metal case using a water jet, and in general this is my first large-scale hobby project.
|
|
@ -3,6 +3,11 @@
|
||||||
My Website
|
My Website
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
.. post:: 31, July 2018
|
||||||
|
:tags: coding, diy, old
|
||||||
|
:category: Projects
|
||||||
|
:author: len0rd
|
||||||
|
|
||||||
Starting out with this website, I had essentially no knowledge of modern web technologies. I knew that I wanted something modern but also easy to maintain that I could use well into the future.
|
Starting out with this website, I had essentially no knowledge of modern web technologies. I knew that I wanted something modern but also easy to maintain that I could use well into the future.
|
||||||
|
|
||||||
The end result is the site you see here. By no means perfect or beautiful, but functional and a place where I can store guides mainly for my benefit. But maybe for your benefit too? I certainly dont know who's reading this ¯\\\_(ツ)_/¯
|
The end result is the site you see here. By no means perfect or beautiful, but functional and a place where I can store guides mainly for my benefit. But maybe for your benefit too? I certainly dont know who's reading this ¯\\\_(ツ)_/¯
|
|
@ -3,6 +3,11 @@
|
||||||
Pallet Desk
|
Pallet Desk
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
.. post:: 02, September 2018
|
||||||
|
:tags: furniture, diy, amateur, pallets
|
||||||
|
:category: Projects
|
||||||
|
:author: len0rd
|
||||||
|
|
||||||
As I finished up my sophomore year at university, I realized that I needed my own desk. At that point I had lived in three different apartments. All 3 of them had desks provided, and all of those desks were mediocre at best, downright broken at worst. What's worse is at the time I worked from home, which meant I *needed* a reliable desk. With the summer about to hit, and plans to work full-time from home, I knew I needed something fast. Here's the end result:
|
As I finished up my sophomore year at university, I realized that I needed my own desk. At that point I had lived in three different apartments. All 3 of them had desks provided, and all of those desks were mediocre at best, downright broken at worst. What's worse is at the time I worked from home, which meant I *needed* a reliable desk. With the summer about to hit, and plans to work full-time from home, I knew I needed something fast. Here's the end result:
|
||||||
|
|
||||||
.. image:: ../assets/img/writeup/palletDesk/finished-1-sm.jpg
|
.. image:: ../assets/img/writeup/palletDesk/finished-1-sm.jpg
|
226
prestart.js
226
prestart.js
|
@ -1,226 +0,0 @@
|
||||||
// convert our markdown documentation files
|
|
||||||
// to 'static' html/ejs partials:
|
|
||||||
// while this is a bit inconvenient (you need to restart
|
|
||||||
// the server everytime you want to see
|
|
||||||
// md changes), it is more efficient in
|
|
||||||
// that we aren't converting MD -> ejs
|
|
||||||
// on EVERY request
|
|
||||||
const fs = require('fs'),
|
|
||||||
mkdirp = require('mkdirp'),
|
|
||||||
recipeInputDir = './recipes/',
|
|
||||||
recipeOutputDir = './views/partials/md/recipes/',
|
|
||||||
recipeListGeneratedOutputDir = './views/partials/generated/',
|
|
||||||
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 in project writeups
|
|
||||||
const projectsAddHeaderClass = {
|
|
||||||
type: 'output', // when it's triggered -> output is at the very end when text is html
|
|
||||||
filter: text => { return addClassToTag(text, projectClassMap); }
|
|
||||||
};
|
|
||||||
|
|
||||||
function convertRecipeMarkdown(inputDir, outputDir) {
|
|
||||||
var md = require('markdown-it')()
|
|
||||||
.use(require('markdown-it-hashtag'));
|
|
||||||
|
|
||||||
md.renderer.rules.hashtag_open = function (tokens, idx) {
|
|
||||||
var tagName = tokens[idx].content.toLowerCase();
|
|
||||||
return '<a href="/recipe_navigator?tag=' + tagName + '"><span class="badge bg-secondary">';
|
|
||||||
}
|
|
||||||
|
|
||||||
md.renderer.rules.hashtag_close = function () { return '</span></a>'; }
|
|
||||||
|
|
||||||
// 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')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let fileNameNoExtension = file.slice(0, -3);
|
|
||||||
console.log('converting: ' + fileNameNoExtension);
|
|
||||||
fs.readFile(inputDir + file, 'utf8', (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ingredientTableRegex = new RegExp(`^(\\|?.*?)\\|(.*?)(\\|.*?\\|.*?)\n`, `gm`);
|
|
||||||
var ingredientDashCheck = new RegExp("^\-+$");
|
|
||||||
ingredientTableMatcher = ingredientTableRegex.exec(data);
|
|
||||||
while (ingredientTableMatcher != null) {
|
|
||||||
meas = ingredientTableMatcher[1];
|
|
||||||
let unit = ingredientTableMatcher[2].toLowerCase().trim();
|
|
||||||
if (unit != "unit" && unit && !ingredientDashCheck.test(unit)) {
|
|
||||||
meas += unit + " ";
|
|
||||||
}
|
|
||||||
data = data.replace(ingredientTableMatcher[0], `${meas}${ingredientTableMatcher[3]}\n`);
|
|
||||||
ingredientTableMatcher = ingredientTableRegex.exec(data);
|
|
||||||
}
|
|
||||||
ingredientTableMatcher = ingredientTableRegex.exec(data);
|
|
||||||
while (ingredientTableMatcher != null) {
|
|
||||||
meas = ingredientTableMatcher[1];
|
|
||||||
let unit = ingredientTableMatcher[2].toLowerCase().trim();
|
|
||||||
if (unit != "unit" && unit && !ingredientDashCheck.test(unit)) {
|
|
||||||
meas += unit + " ";
|
|
||||||
}
|
|
||||||
data = data.replace(ingredientTableMatcher[0], `${meas}${ingredientTableMatcher[3]}\n`);
|
|
||||||
ingredientTableMatcher = ingredientTableRegex.exec(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRecipeNavigatorList(recipeSrcDir, generatedOutputDir) {
|
|
||||||
// generate a list of recipe links. While doing so generate an array
|
|
||||||
// of unique hashtags found in all recipes
|
|
||||||
mkdirp.sync(generatedOutputDir);
|
|
||||||
let recipeListPartialOut = "";
|
|
||||||
let allRecipeHashtags = [];
|
|
||||||
fs.readdir(recipeSrcDir, (err, files) => {
|
|
||||||
files.sort().forEach(file => {
|
|
||||||
if (!file.endsWith('.md')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let fileNameNoExtension = file.slice(0, -3);
|
|
||||||
|
|
||||||
const data = fs.readFileSync(recipeSrcDir + file, { encoding: 'utf8', flag: 'r' });
|
|
||||||
|
|
||||||
// find all hashtags in the file
|
|
||||||
var hashtagRegex = new RegExp(`#(\\w+)`, `g`);
|
|
||||||
hashtagMatcher = hashtagRegex.exec(data);
|
|
||||||
var recipeTags = []; // hashtags of the current recipe only
|
|
||||||
while (hashtagMatcher != null) {
|
|
||||||
var hashtag = hashtagMatcher[1].toLowerCase();
|
|
||||||
if (!allRecipeHashtags.includes(hashtag)) {
|
|
||||||
allRecipeHashtags.push(hashtag);
|
|
||||||
}
|
|
||||||
if (!recipeTags.includes(hashtag)) {
|
|
||||||
recipeTags.push(hashtag);
|
|
||||||
}
|
|
||||||
hashtagMatcher = hashtagRegex.exec(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
let combinedRecipeTags = "";
|
|
||||||
if (recipeTags.length > 0) {
|
|
||||||
combinedRecipeTags = recipeTags.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
// get first recipe title from document
|
|
||||||
var titleRegex = new RegExp(`#\\s+(.+)\\n`, `g`);
|
|
||||||
titleMatcher = titleRegex.exec(data);
|
|
||||||
var recipeTitle = fileNameNoExtension;
|
|
||||||
if (titleMatcher != null) {
|
|
||||||
recipeTitle = titleMatcher[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
recipeListPartialOut += `<a href="recipes/${fileNameNoExtension}" class="list-group-item list-group-item-action" tags="${combinedRecipeTags}">${recipeTitle}</a>\n`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// writeout the link list partial
|
|
||||||
fs.writeFileSync(generatedOutputDir + "recipe-links.ejs", recipeListPartialOut, "utf-8");
|
|
||||||
|
|
||||||
// now generate the hashtag button list partial
|
|
||||||
// TODO: in the future sort the list by number of hashtag hits (most -> least common)
|
|
||||||
// instead of alphabetically
|
|
||||||
let tagListPartialOut = "";
|
|
||||||
allRecipeHashtags.sort().forEach(hashtag => {
|
|
||||||
tagListPartialOut += `<button type="button" class="btn btn-light btn-sm">${hashtag}</button>\n`;
|
|
||||||
});
|
|
||||||
fs.writeFileSync(generatedOutputDir + "recipe-tags.ejs", tagListPartialOut, "utf-8");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
convertRecipeMarkdown(recipeInputDir, recipeOutputDir);
|
|
||||||
generateRecipeNavigatorList(recipeInputDir, recipeListGeneratedOutputDir);
|
|
|
@ -1,28 +0,0 @@
|
||||||
# Sphinx docs configuration for building project documentation
|
|
||||||
from datetime import datetime
|
|
||||||
import sphinx_bootstrap_theme
|
|
||||||
import os
|
|
||||||
|
|
||||||
project = "lenordsNet Projects"
|
|
||||||
author = "lenord"
|
|
||||||
copyright = f"{datetime.now().year}, lenordsNet"
|
|
||||||
|
|
||||||
extensions = [
|
|
||||||
"sphinxcontrib.youtube",
|
|
||||||
]
|
|
||||||
|
|
||||||
root_doc = "contents"
|
|
||||||
|
|
||||||
html_theme = "bootstrap"
|
|
||||||
html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
|
|
||||||
html_baseurl = "/"
|
|
||||||
html_use_index = False
|
|
||||||
html_theme_options = {
|
|
||||||
"navbar_title": "lenordsNet",
|
|
||||||
"navbar_sidebarrel": False,
|
|
||||||
"navbar_class": "navbar navbar-inverse navbar-dark",
|
|
||||||
"source_link_position": "footer",
|
|
||||||
"bootswatch_theme": "cyborg",
|
|
||||||
}
|
|
||||||
|
|
||||||
pygments_style = "sas"
|
|
|
@ -1,15 +0,0 @@
|
||||||
lenordsNet project documentation
|
|
||||||
================================
|
|
||||||
|
|
||||||
`Home <../../>`_
|
|
||||||
----------------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:caption: Contents:
|
|
||||||
|
|
||||||
Home <../../#http://>
|
|
||||||
ls1synth
|
|
||||||
myWebsite
|
|
||||||
palletDesk
|
|
||||||
darkstar
|
|
42
server.js
42
server.js
|
@ -1,42 +0,0 @@
|
||||||
const PORT = 8090;
|
|
||||||
var express = require('express');
|
|
||||||
const path = require("path");
|
|
||||||
var app = express();
|
|
||||||
|
|
||||||
console.log('Starting express server on port ' + PORT);
|
|
||||||
|
|
||||||
// set the view engine to ejs
|
|
||||||
app.set('view engine', 'ejs');
|
|
||||||
app.set("views", path.join(__dirname, "views"));
|
|
||||||
|
|
||||||
// add folder for static content:
|
|
||||||
app.use(express.static(path.join(__dirname, 'assets')));
|
|
||||||
|
|
||||||
function getRootPage(req, res) {
|
|
||||||
let pageName = req.params["pageName"];
|
|
||||||
if (pageName === null || pageName === undefined) {
|
|
||||||
pageName = "index";
|
|
||||||
}
|
|
||||||
// hardcoded check for favicon as it always comes in on /favicon.ico :/
|
|
||||||
if (req.path.includes("favicon")) {
|
|
||||||
res.sendFile(pageName, { root: path.join(__dirname, "assets", "img") });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res.render(path.join("pages", pageName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/:pageName", getRootPage);
|
|
||||||
app.get("/", getRootPage);
|
|
||||||
|
|
||||||
app.get("/projects/:projectName", (req, res) => {
|
|
||||||
res.sendFile(req.params["projectName"], { root: path.join(__dirname, "assets", "projects") })
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/recipes/:recipeName", (req, res) => {
|
|
||||||
let pathname = path.join("pages", "recipes", "recipe_template");
|
|
||||||
let page = path.join("partials", "md", "recipes", req.params["recipeName"])
|
|
||||||
res.render(pathname, { "page": page });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(PORT);
|
|
|
@ -1,4 +0,0 @@
|
||||||
# build project documentation
|
|
||||||
sphinx-build project_writeups/ assets/projects -b html
|
|
||||||
# generate recipe ejs partials
|
|
||||||
node prestart.js
|
|
|
@ -1,23 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<%- include('../partials/include') %>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="d-flex flex-column min-vh-100">
|
|
||||||
<header>
|
|
||||||
<%- include('../partials/nav') %>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="container mt-5 topMargin">
|
|
||||||
<h1>Contact me</h1>
|
|
||||||
<p>Questions? Comments? Spam? Please email me with the below address:</p>
|
|
||||||
<h4>len0rd"AT"fastmail.co.uk</h4>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%- include('../partials/footer') %>
|
|
||||||
<%- include('../partials/post_html_include') %>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,142 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<%- include('../partials/include') %>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<%- include('../partials/nav') %>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main role="main">
|
|
||||||
|
|
||||||
<section class="bgimage">
|
|
||||||
<div class="container center">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
|
||||||
<h1 class="display-1">welcome to lenordsNet</h1>
|
|
||||||
<h3 class="text-white">enjoy my ramblings</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="container-flex code">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row text-white pb-5 pt-5">
|
|
||||||
<div class="col-4 pr-5 d-flex">
|
|
||||||
<samp class="display-1 ml-auto">></samp>
|
|
||||||
</div>
|
|
||||||
<div class="col-8">
|
|
||||||
<samp>I'm an American embedded software engineer with diverse industry experience. From large
|
|
||||||
commercial Java projects to low-level C firmware development, I've done a little bit of
|
|
||||||
everything (except web development as will be evident with this website). Such experience
|
|
||||||
allows me to understand and solve new problems quickly. My hobbies are just as eclectic as
|
|
||||||
my professional experience. This website showcases many of my personal projects.</samp>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row row-cols-1 row-cols-md-3 g-2 p-5">
|
|
||||||
<div class="col">
|
|
||||||
<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>
|
|
||||||
<p class="card-text">Yama is a powerful web crawler/scraper based on Selenium and Java.
|
|
||||||
Built for and used in a commercial application by yours truly</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer pb-3">
|
|
||||||
<a href="https://github.com/len0rd/YamaCrawler" class="card-link card-soft-link">See
|
|
||||||
Code</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<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>
|
|
||||||
<p class="card-text">This is what happens when I get fed up with a protocols reference
|
|
||||||
implementation (after using it for over 4 years)</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer pb-3">
|
|
||||||
<a href="https://github.com/len0rd/mavlib_gen" class="card-link card-soft-link">See Code</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<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>
|
|
||||||
<p class="card-text">Darkstar was my first foray into building my own drone. I used a
|
|
||||||
mixture
|
|
||||||
of open source software and some of my own stuff to get the hunk of junk into the air
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer pb-3">
|
|
||||||
<a href="projects/darkstar.html" class="btn btn-outline-light">Read More</a>
|
|
||||||
<a href="https://github.com/len0rd/darkstar_copter" class="card-link card-soft-link">See
|
|
||||||
Code</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<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>
|
|
||||||
<p class="card-text">Building my dream desk on a poor-college-student budget. The end
|
|
||||||
result: a portable and rock solid desk with a couple of flaws</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer pb-3">
|
|
||||||
<a href="projects/palletDesk.html" class="btn btn-outline-light">Read More</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<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>
|
|
||||||
<p class="card-text">A post on my website about how I built my website? Yep. Turns out
|
|
||||||
things aren't quite plug 'n play when you're a stubborn engineer who wants something to
|
|
||||||
function in a very specific way</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer pb-3">
|
|
||||||
<a href="projects/myWebsite.html" class="btn btn-outline-light">Read More</a>
|
|
||||||
<a href="https://github.com/len0rd/personal-website" class="card-link card-soft-link">See
|
|
||||||
Code</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<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>
|
|
||||||
<p class="card-text">I find that I learn best by doing. So when I starting considering
|
|
||||||
getting into the <i>extremely</i> expensive hobby of analog modular synths, I figured
|
|
||||||
the best way to start would be to build my own. The results aren't terribly impressive,
|
|
||||||
but helped me learn a whole ton along the way</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer pb-3">
|
|
||||||
<a href="projects/ls1synth.html" class="btn btn-outline-light">Read More</a>
|
|
||||||
<a href="https://github.com/len0rd/LS1-sequencer" class="card-link card-soft-link">See
|
|
||||||
Code</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<%- include('../partials/footer') %>
|
|
||||||
<%- include('../partials/post_html_include') %>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,103 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<%- include('../partials/include' ) %>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/css/projects.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="d-flex flex-column min-vh-100">
|
|
||||||
<header>
|
|
||||||
<%- include('../partials/nav') %>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="container mt-5 topMargin">
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col">
|
|
||||||
<h1 class="display-1">Recipes</h1>
|
|
||||||
</div>
|
|
||||||
<div class="col-4 align-self-end">
|
|
||||||
<input class="form-control" id="recipeSearch" type="text" placeholder="search..">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-2">
|
|
||||||
<div class="col btn-container">
|
|
||||||
<%- include('../partials/generated/recipe-tags') %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="list-group list-group-flush" id="recipe-link-container">
|
|
||||||
<%- include('../partials/generated/recipe-links') %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%- include('../partials/footer') %>
|
|
||||||
<%- include('../partials/post_html_include') %>
|
|
||||||
<script>
|
|
||||||
const TAG_ACTIVE_CLASS = "btn-primary";
|
|
||||||
|
|
||||||
/// Returns a list of the current recipe hashtags that are enabled (enabled filters)
|
|
||||||
function getActiveTags() {
|
|
||||||
var activeTags = [];
|
|
||||||
// create a list of currently active tags
|
|
||||||
$(".btn-container button").filter(function() {
|
|
||||||
if ($(this).hasClass(TAG_ACTIVE_CLASS)){
|
|
||||||
activeTags.push($(this).text())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return activeTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// check if the given recipe item all the currently selected hashtag filters (an 'AND' search)
|
|
||||||
function recipeHasAllActiveTags(activeTags, recipe) {
|
|
||||||
const recipeTags = recipe.attr("tags").toLowerCase().split(",");
|
|
||||||
return activeTags.every(elem => recipeTags.includes(elem));
|
|
||||||
}
|
|
||||||
|
|
||||||
function recipeHasCurrentSearchPhrase(searchValue, recipe) {
|
|
||||||
return !searchValue || recipe.text().toLowerCase().indexOf(searchValue) > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setActiveRecipes() {
|
|
||||||
const activeTags = getActiveTags();
|
|
||||||
const searchValue = $("#recipeSearch").val().toLowerCase();
|
|
||||||
|
|
||||||
$("#recipe-link-container a").filter(function() {
|
|
||||||
$(this).toggle(recipeHasCurrentSearchPhrase(searchValue, $(this)) && recipeHasAllActiveTags(activeTags, $(this)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This script is responsible for filtering the recipe list
|
|
||||||
/// based on enabled/disabled hashtags. I'm certain theres more
|
|
||||||
/// efficient ways to do this but /shrug
|
|
||||||
$(".btn-container").on("click", "button", function() {
|
|
||||||
$(this).toggleClass("btn-light btn-primary");
|
|
||||||
setActiveRecipes();
|
|
||||||
});
|
|
||||||
|
|
||||||
/// This method is responsible for filtering the recipe list based on the search bar
|
|
||||||
$(document).ready(function() {
|
|
||||||
$("#recipeSearch").on("keyup", function() {
|
|
||||||
setActiveRecipes();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// apply a tag on page load if specified in the url
|
|
||||||
$(document).ready(function() {
|
|
||||||
let searchParams = new URLSearchParams(window.location.search);
|
|
||||||
if (searchParams.has("tag")) {
|
|
||||||
const tagParam = searchParams.get("tag").toLowerCase().trim();
|
|
||||||
$(".btn-container button").filter(function() {
|
|
||||||
if (tagParam == $(this).text().toLowerCase().trim()) {
|
|
||||||
$(this).toggleClass("btn-light btn-primary");
|
|
||||||
setActiveRecipes();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<% var rootPath = '../../'; %>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<%- include(rootPath + 'partials/include' ) %>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/css/projects.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<%- include(rootPath + 'partials/nav') %>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="container mt-5 topMargin">
|
|
||||||
<div class="row">
|
|
||||||
<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 + page + '-instructions') %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%- include(rootPath + 'partials/post_html_include') %>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,26 +0,0 @@
|
||||||
<!-- you may need to remove 'fixed-bottom' if the footer is persisting on scroll-->
|
|
||||||
<footer class="footer bg-black">
|
|
||||||
<div class="container-fluid text-center">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="flex-center py-1">
|
|
||||||
<a class="li-ic icon-footer" style="color:black" href="https://github.com/len0rd">
|
|
||||||
<i class="fab fa-github fa-lg" style="color:white"></i>
|
|
||||||
</a>
|
|
||||||
<a class="li-ic icon-footer" style="color:black" href="https://www.youtube.com/channel/UCwGDckTCkqqW5W8JcRrxELA">
|
|
||||||
<i class="fab fa-youtube fa-lg" style="color:white"></i>
|
|
||||||
</a>
|
|
||||||
<a class="li-ic icon-footer" href="/contact">
|
|
||||||
<i class="fas fa-at fa-lg" style="color:white"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12 mb-2">
|
|
||||||
<div class="footer-copyright text-center text-white">© lenordsNet <%= new Date().getFullYear();%></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</footer>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<meta charset="UTF-8">
|
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/img/favicon.png">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.5.2/animate.min.css">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/css/site.css">
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.16.2/build/styles/default.min.css">
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
|
|
@ -1,21 +0,0 @@
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-black fixed-top">
|
|
||||||
<div class="container-fluid">
|
|
||||||
|
|
||||||
<a class="navbar-brand" href="/#">lenordsNet</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
|
|
||||||
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarText">
|
|
||||||
<ul class="navbar-nav me-auto my-2 my-lg-0 navbar-nav-scroll" style="--bs-scroll-height: 100px;">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="https://home.lenord.me/">Home Assistant</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="/recipe_navigator">Recipes</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<a href="/contact" class="btn btn-outline-light">Contact</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
|
@ -1 +0,0 @@
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
|
Loading…
Reference in a new issue