adding base files
This commit is contained in:
commit
1ca7ec671d
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
content/blog/_drafts
|
||||
public
|
||||
17
config.toml
Normal file
17
config.toml
Normal file
@ -0,0 +1,17 @@
|
||||
# The URL the site will be built for
|
||||
base_url = "https://jakegoldsborough.com"
|
||||
title = "Jake Goldsborough"
|
||||
|
||||
# Whether to automatically compile all Sass files in the sass directory
|
||||
compile_sass = false
|
||||
|
||||
# Whether to build a search index to be used later on by a JavaScript library
|
||||
build_search_index = true
|
||||
|
||||
[markdown]
|
||||
# Whether to do syntax highlighting
|
||||
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
|
||||
highlight_code = true
|
||||
|
||||
[extra]
|
||||
# Put all your custom variables here
|
||||
85
content/_index.md
Normal file
85
content/_index.md
Normal file
@ -0,0 +1,85 @@
|
||||
+++
|
||||
title = "Home page"
|
||||
template = "home.html"
|
||||
+++
|
||||
|
||||
<article class="resume">
|
||||
|
||||
### Software Engineer | DevOps & Cloud Automation
|
||||
|
||||
Versatile Software Engineer with over a decade of experience in software
|
||||
development, DevOps, and cloud automation. Proven ability to design, develop,
|
||||
and optimize scalable systems, reducing operational overhead and improving
|
||||
performance. Passionate about problem-solving, automation, and infrastructure
|
||||
as code. Experienced in working remotely and collaborating with
|
||||
cross-functional teams to deliver efficient, maintainable solutions.
|
||||
|
||||
#### Experience
|
||||
|
||||
**Flashpoint - Software Engineer II (Mid-Level) (2022 - 2024)**
|
||||
|
||||
- **Reduced infrastructure setup time from days to hours by leading the Terraform
|
||||
and Kubernetes pipeline implementation**. Designed reusable Terraform modules
|
||||
and automated Kubernetes resource creation using Node.js. Initiated Helm
|
||||
chart development for better deployment management.
|
||||
|
||||
- **Eliminated hours of manual work across teams** by enhancing an AngularJS web
|
||||
application to efficiently manage and connect to cloud-based VMs.
|
||||
|
||||
- **Refactored a legacy Java-based API into a modern Node.js service**, reducing
|
||||
technical debt and simplifying maintenance for future feature requests.
|
||||
|
||||
- **Developed CI/CD pipelines and Dockerized applications to support an
|
||||
AngularJS-to-React migration**, ensuring seamless frontend transitions and
|
||||
improving local development environments.
|
||||
|
||||
- **Debugged and resolved mission-critical issues in cloud-based Linux VMs**,
|
||||
including optimizing RDP performance and fixing PulseAudio configurations,
|
||||
enhancing system stability.
|
||||
|
||||
- **Mentored junior engineers** and contributed to technical interviews,
|
||||
fostering a culture of continuous learning and collaboration.
|
||||
|
||||
**Infinity Interactive - Software Engineer (2010 - 2020)**
|
||||
|
||||
- **Contracted for projects with Fortune 100 and Fortune 500 companies**,
|
||||
including Shutterstock, Allegra, and Flashpoint.
|
||||
|
||||
- **Improved application performance by 75%** by implementing a Redis caching
|
||||
strategy for a high-traffic application.
|
||||
|
||||
- **Designed and deployed a GraphQL API using Node.js and Apollo**, optimizing
|
||||
backend data retrieval and improving response times.
|
||||
|
||||
- **Developed and maintained full-stack applications**, integrating modern
|
||||
JavaScript frameworks like React and Vue.js.
|
||||
|
||||
#### Skills & Technologies
|
||||
|
||||
- **Programming**: Node.js, TypeScript, Python, Rust
|
||||
|
||||
- **Cloud & Infrastructure**: Terraform, Kubernetes, Google Cloud
|
||||
|
||||
- **Frontend**: React, Vue.js, AngularJS
|
||||
|
||||
- **DevOps & CI/CD**: Docker, Git, NixOS
|
||||
|
||||
- **Databases & Caching**: Postgres, MySQL, Redis, GraphQL
|
||||
|
||||
- **Operating Systems**: Linux/Unix (Debian, Ubuntu, NixOS, macOS), BSD
|
||||
|
||||
#### Languages
|
||||
|
||||
- **English**: Native
|
||||
|
||||
- **Swedish**: Basic (Learning)
|
||||
|
||||
#### Additional Information
|
||||
|
||||
Open to relocation to Europe and remote-friendly opportunities.
|
||||
|
||||
Strong advocate for automation, scalable infrastructure, and developer tooling improvements.
|
||||
|
||||
Available for interviews and technical discussions upon request.
|
||||
|
||||
</article>
|
||||
3
content/blog/2025/_index.md
Normal file
3
content/blog/2025/_index.md
Normal file
@ -0,0 +1,3 @@
|
||||
+++
|
||||
title = "2025"
|
||||
+++
|
||||
90
content/blog/2025/nixos-daily-driver-1.md
Normal file
90
content/blog/2025/nixos-daily-driver-1.md
Normal file
@ -0,0 +1,90 @@
|
||||
+++
|
||||
title = "NixOS as a daily driver, part 1/? - What and why?"
|
||||
date = 2025-05-18
|
||||
+++
|
||||
|
||||
First, it's worth explaining what NixOS is and why I or anyone would want to
|
||||
run it as a daily driver.
|
||||
|
||||
But even before diving into NixOS, we have to talk about Nix.
|
||||
|
||||
### Nix
|
||||
Nix is a package manager that is purely functional and creates reproducible
|
||||
builds specified in the Nix Expression Language. Nix expressions are functions
|
||||
that take dependencies as arguments which creates a *derivation* that specifies
|
||||
a reproducible build environment. Nix then stores the results of the build at
|
||||
unique address specified by a hash of the complete dependency tree. This
|
||||
is known as the Nix store and it's immutable which allows atomic upgrades,
|
||||
rollbacks, and simultaneous installations of packages with different versions.
|
||||
|
||||
### NixOS
|
||||
NixOS is an operating system that is built on top of Nix and the idea of purely
|
||||
functional package management. Packages are never overwritten once built. If
|
||||
you change the Nix expression for a package, it will be rebuilt, and stored
|
||||
under a new address with a new hash, preventing interference with an old version.
|
||||
|
||||
NixOS takes this a step further and applies this to configuration. By building
|
||||
your entire system from a Nix expression, NixOS ensures that your old
|
||||
configuration is never overwritten which allows for easy rollbacks. One big
|
||||
caveat of this is the elimination of "global" directores such as `/bin`,
|
||||
`/lib`, `/usr`, etc. All packages are kept in `/nix/store` under a hashed
|
||||
address. (One exception is a symlink `/bin/sh` to Bash in the Nix store). There
|
||||
is a `/etc` for system-wide config but many of those files are symlinks to files
|
||||
in the Nix store.
|
||||
|
||||
Everything in NixOS is built by the Nix package manager. This includes the
|
||||
kernel, applications, system packages, and configuration.
|
||||
|
||||
To configure NixOS, you have a file at `/etc/nixos/configuration.nix`.
|
||||
You will setup everything from your boot devices to what services you want
|
||||
to run.
|
||||
|
||||
Here is a minimal config that enables sshd:
|
||||
|
||||
```
|
||||
{
|
||||
boot.loader.grub.device = "/dev/sda";
|
||||
fileSystems."/".device = "/dev/sda1";
|
||||
services.sshd.enable = true;
|
||||
}
|
||||
```
|
||||
|
||||
More on this later though.
|
||||
|
||||
|
||||
### Why?
|
||||
Now the whys. I think many of them speak for themselves but here are mine:
|
||||
|
||||
**Rollbacks**
|
||||
|
||||
This one is very big especially when learning about a new, drastically different
|
||||
OS like Nix. Because old config is never overwritten, you can easily cause a
|
||||
breaking change without being worried about how to fix it (unless maybe it's
|
||||
bootloader related). In fact, old configs are listed in the boot menu.
|
||||
|
||||
**Reproducible Build Configurations**
|
||||
|
||||
Kind of like a rollback but starting from scratch. You can take the
|
||||
`configuration.nix` file, copy to another machine, rebuild, and you will have
|
||||
the same applications, services, and configuration as before.
|
||||
|
||||
**Ad-hoc shell environments**
|
||||
|
||||
In a "Nix shell", you can use any program that is packaged with Nix without
|
||||
needing to install permanently.
|
||||
|
||||
For example, you can run `nix-shell -p git neovim node` and you will be dropped
|
||||
into a shell with those applications installed. This may take some time
|
||||
depending on the applications installed.
|
||||
|
||||
**It's a new way to think**
|
||||
|
||||
Honestly, I just like trying new stuff, especially when it's done in a new way.
|
||||
Linux has mostly been the same for a long time now, so it's refreshing to see
|
||||
a new way of doing it that also improves on an already solid OS. I also
|
||||
have really fallen in love with DevOps/IaC type of work, and NixOS definitely
|
||||
scratches that itch.
|
||||
|
||||
### Next time
|
||||
In the next post, I will go over how to install NixOS and maybe a bit of the
|
||||
configuration.
|
||||
27
content/blog/2025/weekly-summary-20.md
Normal file
27
content/blog/2025/weekly-summary-20.md
Normal file
@ -0,0 +1,27 @@
|
||||
+++
|
||||
title = "Weekly Summary - 20/52"
|
||||
date = 2025-05-16
|
||||
+++
|
||||
|
||||
I am starting a weekly summary/reflection series that will be a high level
|
||||
view of things I have worked on or fixed or just things I want to track or note.
|
||||
|
||||
Things I enjoyed:
|
||||
- deployed this site
|
||||
- got an old raspberry pi running again
|
||||
- started back into some rust learnings with blockchain, TUI, and MQTT hacking
|
||||
[^1] [^2] [^3]
|
||||
- started working on a walnut utensil rest
|
||||
- applied to 10 jobs
|
||||
|
||||
Things I did not enjoy:
|
||||
- seemingly bricked my RockPro64 as it won't boot past a black screen. ordered
|
||||
an adapater to help debug at a lower level.
|
||||
- rejected from multiple jobs
|
||||
- felt very confused by some of the rust learnings. makes sense in pieces but
|
||||
hard to see as the whole picture just yet
|
||||
- confirmed old fitbit can't be flashed with any open source/non-fitbit software
|
||||
|
||||
[^1]: [https://ratatui.rs/concepts/application-patterns/the-elm-architecture/](https://ratatui.rs/concepts/application-patterns/the-elm-architecture/)
|
||||
[^2]: [https://github.com/veeso/tui-realm/tree/main](https://github.com/veeso/tui-realm/tree/main)
|
||||
[^3]: [https://github.com/bytebeamio/rumqtt](https://github.com/bytebeamio/rumqtt)
|
||||
17
content/blog/2025/weekly-summary-21.md
Normal file
17
content/blog/2025/weekly-summary-21.md
Normal file
@ -0,0 +1,17 @@
|
||||
+++
|
||||
title = "Weekly Summary - 21/52"
|
||||
date = 2025-05-23
|
||||
+++
|
||||
|
||||
This has been a pretty productive week.
|
||||
|
||||
Things that were good:
|
||||
- RockPro64 boots again! Blog post coming...
|
||||
- continued Swedish language studies - 165 days
|
||||
- started work on some API inspection. Another blog post coming...
|
||||
- figured out nixos/hyprland boot issue
|
||||
|
||||
Things that could be better:
|
||||
- didn't work on much rust
|
||||
- can't get gitea ssh setup on VPS
|
||||
- broke wood router part so had to buy new one to work on utensil rest
|
||||
5
content/blog/_index.md
Normal file
5
content/blog/_index.md
Normal file
@ -0,0 +1,5 @@
|
||||
+++
|
||||
title = "Blog"
|
||||
sort_by = "date"
|
||||
template = "blog.html"
|
||||
+++
|
||||
85
content/resume.md
Normal file
85
content/resume.md
Normal file
@ -0,0 +1,85 @@
|
||||
+++
|
||||
title = "Resume"
|
||||
path = "resume"
|
||||
+++
|
||||
|
||||
<article class="resume">
|
||||
|
||||
### Software Engineer | DevOps & Cloud Automation
|
||||
|
||||
Versatile Software Engineer with over a decade of experience in software
|
||||
development, DevOps, and cloud automation. Proven ability to design, develop,
|
||||
and optimize scalable systems, reducing operational overhead and improving
|
||||
performance. Passionate about problem-solving, automation, and infrastructure
|
||||
as code. Experienced in working remotely and collaborating with
|
||||
cross-functional teams to deliver efficient, maintainable solutions.
|
||||
|
||||
#### Experience
|
||||
|
||||
**Flashpoint - Software Engineer II (Mid-Level) (2022 - 2024)**
|
||||
|
||||
- **Reduced infrastructure setup time from days to hours by leading the Terraform
|
||||
and Kubernetes pipeline implementation**. Designed reusable Terraform modules
|
||||
and automated Kubernetes resource creation using Node.js. Initiated Helm
|
||||
chart development for better deployment management.
|
||||
|
||||
- **Eliminated hours of manual work across teams** by enhancing an AngularJS web
|
||||
application to efficiently manage and connect to cloud-based VMs.
|
||||
|
||||
- **Refactored a legacy Java-based API into a modern Node.js service**, reducing
|
||||
technical debt and simplifying maintenance for future feature requests.
|
||||
|
||||
- **Developed CI/CD pipelines and Dockerized applications to support an
|
||||
AngularJS-to-React migration**, ensuring seamless frontend transitions and
|
||||
improving local development environments.
|
||||
|
||||
- **Debugged and resolved mission-critical issues in cloud-based Linux VMs**,
|
||||
including optimizing RDP performance and fixing PulseAudio configurations,
|
||||
enhancing system stability.
|
||||
|
||||
- **Mentored junior engineers** and contributed to technical interviews,
|
||||
fostering a culture of continuous learning and collaboration.
|
||||
|
||||
**Infinity Interactive - Software Engineer (2010 - 2020)**
|
||||
|
||||
- **Contracted for projects with Fortune 100 and Fortune 500 companies**,
|
||||
including Shutterstock, Allegra, and Flashpoint.
|
||||
|
||||
- **Improved application performance by 75%** by implementing a Redis caching
|
||||
strategy for a high-traffic application.
|
||||
|
||||
- **Designed and deployed a GraphQL API using Node.js and Apollo**, optimizing
|
||||
backend data retrieval and improving response times.
|
||||
|
||||
- **Developed and maintained full-stack applications**, integrating modern
|
||||
JavaScript frameworks like React and Vue.js.
|
||||
|
||||
#### Skills & Technologies
|
||||
|
||||
- **Programming**: Node.js, TypeScript, Python, Rust
|
||||
|
||||
- **Cloud & Infrastructure**: Terraform, Kubernetes, Google Cloud
|
||||
|
||||
- **Frontend**: React, Vue.js, AngularJS
|
||||
|
||||
- **DevOps & CI/CD**: Docker, Git, NixOS
|
||||
|
||||
- **Databases & Caching**: Postgres, MySQL, Redis, GraphQL
|
||||
|
||||
- **Operating Systems**: Linux/Unix (Debian, Ubuntu, NixOS, macOS), BSD
|
||||
|
||||
#### Languages
|
||||
|
||||
- **English**: Native
|
||||
|
||||
- **Swedish**: Basic (Learning)
|
||||
|
||||
#### Additional Information
|
||||
|
||||
Open to relocation to Europe and remote-friendly opportunities.
|
||||
|
||||
Strong advocate for automation, scalable infrastructure, and developer tooling improvements.
|
||||
|
||||
Available for interviews and technical discussions upon request.
|
||||
|
||||
</article>
|
||||
176
static/css/base.css
Normal file
176
static/css/base.css
Normal file
@ -0,0 +1,176 @@
|
||||
/* static/style.css */
|
||||
|
||||
@font-face {
|
||||
font-family: "BerkeleyMono";
|
||||
src: url("/fonts/berkeley-mono/BerkeleyMono-Regular.woff2")
|
||||
format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "BerkeleyMono";
|
||||
src: url("/fonts/berkeley-mono/BerkeleyMono-Bold.woff2")
|
||||
format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "BerkeleyMono";
|
||||
src: url("/fonts/berkeley-mono/BerkeleyMono-Italic.woff2")
|
||||
format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "BerkeleyMono";
|
||||
src: url("/fonts/berkeley-mono/BerkeleyMono-BoldItalic.woff2")
|
||||
format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: dark light;
|
||||
|
||||
--bg: #282828;
|
||||
--fg: #ebdbb2;
|
||||
--red: #fb4934;
|
||||
--green: #b8bb26;
|
||||
--yellow: #fabd2f;
|
||||
--blue: #83a598;
|
||||
--purple: #d3869b;
|
||||
--aqua: #8ec07c;
|
||||
--orange: #d65d0e;
|
||||
--gray: #a89984;
|
||||
|
||||
--font-main: BerkeleyMono, system-ui, sans-serif;
|
||||
--font-mono: monospace;
|
||||
|
||||
--spacing: 1rem;
|
||||
--max-width: 60rem;
|
||||
--accent-main: var(--purple);
|
||||
--accent-comp: var(--green);
|
||||
}
|
||||
|
||||
body.light {
|
||||
/* Gruvbox Light */
|
||||
--bg: #ebdbb2;
|
||||
--fg: #3c3836;
|
||||
|
||||
--red: #9d0006;
|
||||
--green: #79740e;
|
||||
--yellow: #b57614;
|
||||
--blue: #076678;
|
||||
--purple: #8f3f71;
|
||||
--aqua: #427b58;
|
||||
--orange: #af3a03;
|
||||
--gray: #928374;
|
||||
|
||||
--accent-main: var(--purple);
|
||||
--accent-comp: var(--green);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: var(--font-main);
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent-main);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, a.active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
header, footer {
|
||||
padding: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
color: var(--accent-main);
|
||||
font-size: clamp(2.25rem, 7vw, 5rem);
|
||||
margin-bottom: 0;
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid var(--gray);
|
||||
border-bottom: none;
|
||||
margin-top: 2rem;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: clamp(1.5rem, 5vw, 3rem);
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
flex: 0 auto;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 60ch;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--accent-comp);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.5rem, 5vw, 3rem);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: clamp(1.25rem, 4vw, 2rem);
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: clamp(1.25rem, 4vw, 2rem);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
background-color: #3c3836;
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre code {
|
||||
display: block;
|
||||
padding: 1em;
|
||||
overflow-x: auto;
|
||||
background-color: #3c3836;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid var(--aqua);
|
||||
padding-left: 1em;
|
||||
margin-left: 0;
|
||||
color: var(--gray);
|
||||
font-style: italic;
|
||||
}
|
||||
17
static/css/blog.css
Normal file
17
static/css/blog.css
Normal file
@ -0,0 +1,17 @@
|
||||
.blog-posts {
|
||||
padding-left: 0;
|
||||
|
||||
.published {
|
||||
color: var(--accent-main);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.post-item {
|
||||
margin-bottom: 2rem;
|
||||
list-style: none;
|
||||
}
|
||||
.post-item h3 {
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
}
|
||||
5
static/css/footnotes.css
Normal file
5
static/css/footnotes.css
Normal file
@ -0,0 +1,5 @@
|
||||
.footnote-definition {
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
5
static/css/resume.css
Normal file
5
static/css/resume.css
Normal file
@ -0,0 +1,5 @@
|
||||
article.resume {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--orange);
|
||||
}
|
||||
}
|
||||
5
static/css/style.css
Normal file
5
static/css/style.css
Normal file
@ -0,0 +1,5 @@
|
||||
@import url('base.css');
|
||||
@import url('theme-picker.css');
|
||||
@import url('resume.css');
|
||||
@import url('footnotes.css');
|
||||
@import url('blog.css');
|
||||
43
static/css/theme-picker.css
Normal file
43
static/css/theme-picker.css
Normal file
@ -0,0 +1,43 @@
|
||||
.theme-picker {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 1.5rem;
|
||||
color: var(--fg);
|
||||
padding-right: var(--spacing);
|
||||
|
||||
label {
|
||||
margin-left: 0.75rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: normal;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
input[value="light"] + span::before {
|
||||
content: "light";
|
||||
}
|
||||
|
||||
input[value="dark"] + span::before {
|
||||
content: "dark";
|
||||
}
|
||||
|
||||
input:not(:checked) + span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
input:checked + span {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.theme-picker noscript label {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
BIN
static/fonts/berkeley-mono/BerkeleyMono-Bold.woff2
Normal file
BIN
static/fonts/berkeley-mono/BerkeleyMono-Bold.woff2
Normal file
Binary file not shown.
BIN
static/fonts/berkeley-mono/BerkeleyMono-BoldItalic.woff2
Normal file
BIN
static/fonts/berkeley-mono/BerkeleyMono-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
static/fonts/berkeley-mono/BerkeleyMono-Italic.woff2
Normal file
BIN
static/fonts/berkeley-mono/BerkeleyMono-Italic.woff2
Normal file
Binary file not shown.
BIN
static/fonts/berkeley-mono/BerkeleyMono-Regular.woff2
Normal file
BIN
static/fonts/berkeley-mono/BerkeleyMono-Regular.woff2
Normal file
Binary file not shown.
18
static/js/main.js
Normal file
18
static/js/main.js
Normal file
@ -0,0 +1,18 @@
|
||||
const body = document.body;
|
||||
const radios = document.querySelectorAll('input[name="theme"]');
|
||||
|
||||
function applyTheme(theme) {
|
||||
body.className = theme;
|
||||
localStorage.setItem('theme', theme);
|
||||
}
|
||||
|
||||
const stored = localStorage.getItem('theme') || 'dark';
|
||||
applyTheme(stored);
|
||||
const selected = document.querySelector(`input[value="${stored}"]`);
|
||||
if (selected) selected.checked = true;
|
||||
|
||||
radios.forEach(radio => {
|
||||
radio.addEventListener('change', () => {
|
||||
applyTheme(radio.value);
|
||||
});
|
||||
});
|
||||
4
templates/_markup/render-link.html
Normal file
4
templates/_markup/render-link.html
Normal file
@ -0,0 +1,4 @@
|
||||
<a href="{{ href | safe }}"
|
||||
{% if href is starting_with("http") %} target="_blank" rel="noopener noreferrer"{% endif %}>
|
||||
{{ text | safe }}
|
||||
</a>
|
||||
62
templates/base.html
Normal file
62
templates/base.html
Normal file
@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>
|
||||
{% if page.title %}
|
||||
{{ page.title }} -
|
||||
{% endif %}
|
||||
|
||||
{{ config.title }}
|
||||
</title>
|
||||
|
||||
<link rel="stylesheet" href="{{ get_url(path='css/style.css') }}" />
|
||||
|
||||
{# Optional favicon #}
|
||||
<link rel="icon" href="{{ get_url(path='favicon.ico') }}" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="theme-picker">
|
||||
<label aria-label="Dark theme">
|
||||
<input type="radio" name="theme" value="dark" />
|
||||
<span aria-hidden="true"></span>
|
||||
</label>
|
||||
<label aria-label="Light theme">
|
||||
<input type="radio" name="theme" value="light" />
|
||||
<span aria-hidden="true"></span>
|
||||
</label>
|
||||
<noscript>
|
||||
<label aria-label="Dark theme"><span aria-hidden="true">dark</span></label>
|
||||
<label aria-label="Light theme"><span aria-hidden="true">light</span></label>
|
||||
</noscript>
|
||||
</div>
|
||||
<h1>
|
||||
<a href="{{ get_url(path='resume') }}">{{ config.title }}</a>
|
||||
</h1>
|
||||
<nav>
|
||||
<a
|
||||
href="{{ get_url(path='blog') }}"
|
||||
class="{% if 'blog' in current_path %}active{% endif %}"
|
||||
>Blog</a>
|
||||
<span>|</span>
|
||||
<a
|
||||
href="{{ get_url(path='resume') }}"
|
||||
class="{% if 'resume' in current_path %}active{% endif %}"
|
||||
>Resume</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© {{ now() | date(format="%Y") }} {{ config.title }}</p>
|
||||
</footer>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
11
templates/blog.html
Normal file
11
templates/blog.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "macros.html" as macros %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{ section.title }}</h2>
|
||||
|
||||
<ul class="blog-posts">
|
||||
{{ macros::list_pages_recursively(section=section) }}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
22
templates/blog/single.html
Normal file
22
templates/blog/single.html
Normal file
@ -0,0 +1,22 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<h1>{{ page.title }}</h1>
|
||||
<p>
|
||||
Published {{ page.date | date(format="%B %e, %Y") }} ·
|
||||
{{ page.reading_time }} min read
|
||||
</p>
|
||||
|
||||
{% if page.taxonomies.tags %}
|
||||
<p>
|
||||
Tags:
|
||||
{% for tag in page.taxonomies.tags %}
|
||||
<a href="{{ tag.permalink }}">{{ tag.name }}</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{{ page.content | safe }}
|
||||
</article>
|
||||
{% endblock %}
|
||||
7
templates/home.html
Normal file
7
templates/home.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="homepage">
|
||||
{{ section.content | safe }}
|
||||
</section>
|
||||
{% endblock %}
|
||||
1
templates/index.html
Normal file
1
templates/index.html
Normal file
@ -0,0 +1 @@
|
||||
{% extends "base.html" %}
|
||||
42
templates/macros.html
Normal file
42
templates/macros.html
Normal file
@ -0,0 +1,42 @@
|
||||
{% macro list_pages_recursively(section) %}
|
||||
{% set posts = [] %}
|
||||
{% set drafts = [] %}
|
||||
|
||||
{# Separate drafts from non-drafts #}
|
||||
{% for page in section.pages %}
|
||||
{% if page.draft %}
|
||||
{% set_global drafts = drafts | concat(with=page) %}
|
||||
{% else %}
|
||||
{% set_global posts = posts | concat(with=page) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{# Sort non drafts #}
|
||||
{% set sorted = posts | sort(attribute="date") | reverse %}
|
||||
|
||||
{% for page in sorted %}
|
||||
<li class="post-item">
|
||||
<h3><a href="{{ page.permalink }}">{{ page.title }}</a></h3>
|
||||
<div class="meta">
|
||||
{% if page.date %}
|
||||
<p>Published on {{ page.date | date(format="%Y-%m-%d") }}</p>
|
||||
{% endif %}
|
||||
<p>{{ page.reading_time }} min read</p>
|
||||
{% if page.description %}
|
||||
<p class="desc">{{ page.description }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
{% for subsection_path in section.subsections %}
|
||||
{% set subsection = get_section(path=subsection_path) %}
|
||||
{{ self::list_pages_recursively(section=subsection) }}
|
||||
{% endfor %}
|
||||
|
||||
{% for page in drafts %}
|
||||
<li class="post-item">
|
||||
<h3><a href="{{ page.permalink }}">{{ page.title }} (draft)</a></h3>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
31
templates/page.html
Normal file
31
templates/page.html
Normal file
@ -0,0 +1,31 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% if page.components | first == "blog" %}
|
||||
<article>
|
||||
<h1>{{ page.title }}</h1>
|
||||
{% if page.date %}
|
||||
<p>
|
||||
Published {{ page.date | date(format="%B %e, %Y") }}
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
{{ page.reading_time }} min read
|
||||
</p>
|
||||
|
||||
{% if page.taxonomies.tags %}
|
||||
<p>
|
||||
Tags:
|
||||
{% for tag in page.taxonomies.tags %}
|
||||
<a href="{{ tag.permalink }}">{{ tag.name }}</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{{ page.content | safe }}
|
||||
</article>
|
||||
{% else %}
|
||||
<h2>{{ page.title }}</h2>
|
||||
{{ page.content | safe }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user