Compare commits

..

1 commit

Author SHA1 Message Date
741a1b6429 interim commit 2026-03-01 15:24:59 +00:00
29 changed files with 578 additions and 611 deletions

53
@s Normal file
View file

@ -0,0 +1,53 @@
---
title: "Replacing garage guttering"
slug: /replacing-garage-guttering/
date: 2025-12-06
tags: ["projects", "DIY"]
---
I faced the following problems with the guttering on my garage:
- It had been bent out of shape by the wind
- The seals on the joins had worn away causing leaks
- There was sitting water that wasn't making it to the downpipe
- It was old and gross
Whenever it rained heavily, these problems would compound and lead to water
pouring over onto the garage brickwork.
The sitting water was caused by the lack of a sufficient drop from the union
join with my neighbour's gutter to the downpipe. A further impediment was that
the water had to turn a 90 degree angle, around the side of the garabe, before
reaching the downpipe. As a result, water was only making it to the downpipe
when there was very heavy rain and/or high winds. During normal drip-drainage of
the daily dew condensation on the roof, the water was just pooling in the
gutter.
I decided to redesign the passage to the downpipe. Instead of trying to make the
water turn a bend I thought it would be better to work _with_ gravity and have
the drop start at the end of the guttering, not around the corner. This way, the
water would have increased velocity at the beginning of its descent into the
downpipe.
By fashioning a "swans neck" sequence of joins, the downpipe now turns the
corner _during_ descent and is fed downwards along the wall to the water butt.
This has been working very well and the water no longer pools. I've noticed
however that condensation forms on the underside of the downpipe. This doesn't
look great and I worry about it wearing away the sealant I have applied at the
joins.
Overall, however I think it looks much neater as well as being more satisfying
from an engineering perspective. The white half-round gutters blend in nicely
with the neighbours' and look a lot cleaner.
I didn't keep track of costs for this project. I think in total it cost around
£80. This included the cost of the Floplast guttering and fixtures, the downpipe
and the protective mesh I applied to the top of the gutters to prevent blockages
from leaves and roof moss.
In order to drill the fixtures into the garage masonry I needed a more powerful
drill than my 18V battery-powered Erbauer. I bought this a few years ago before
I knew much about brands and power-tool quality. So I bought a wired Makita
hammer-drill for around £90. I think when I buy new power-tools in future I will
stick to Makita. The build quality and performance is excellent.

10
package-lock.json generated
View file

@ -80,6 +80,7 @@
"integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
@ -2262,6 +2263,7 @@
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.7.tgz",
"integrity": "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@tanstack/query-core": "5.90.7"
},
@ -2417,6 +2419,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -2529,6 +2532,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001716",
"electron-to-chromium": "^1.5.149",
@ -2947,6 +2951,7 @@
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@ -4381,6 +4386,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -4390,6 +4396,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
@ -4807,6 +4814,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -4996,6 +5004,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@ -5084,6 +5093,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},

View file

@ -1,78 +0,0 @@
---
title: "Amateur radio licence"
slug: /amateur-radio-licence/
date: 2026-03-12
tags: ["radio"]
---
In February I bit the bullet and booked the exam for the UK Amateur Radio
Licence (Foundation Level).
Having the exam in the calendar helped me concentrate and remain focused, as my
learning style is naturally quite slow and meandering. I really wanted to earn
my licence so that I could get on the air and begin to advance my practical
knowledge.
I used the official Radio Society of Great Britain (RSGB) textbook and a
simplified syllabus from [Essex Ham Train](https://www.essexham.co.uk/train/).
![Flash cards from my studying](./img/flash-cards.jpg)
In the mocks, I was averaging an 80% pass rate but in the exam itself I did
particularly well and scored 92%, getting only two questions wrong (one of which
didn't come up at all in the training materials). I completed the exam remotely
but there was an RSGB invigilator observing my screen and environment to check I
wasn't cheating! (Thank you Malcolm, G3ZNU.)
Having received my certificate I was able to apply to Ofcom for my callsign. I
chose **M7SYO**. The first two characters are mandatory and denote my
qualification level ("Foundation") and the last three are your personal choice,
based on what is available. Luckily I was able to choose letters reflecting
"Systems Obscure". When appropriate to do so, I can add "E" as my regional
secondary locator to indicate that I am operating from England (e.g. ME7SYO).
The Foundation level is pretty generous given that it is the entry-level. I can
transmit up to 25W and am permitted to use most of the amateur bands (10m, 12m,
15m, 17m, 20m, 30m, 40m, 80m) at HF, apart from 60m, and many of the VHF/UHF
bands.
I am lucky that my uncle, Greg, is an experienced amateur operator and secretary
of his local amateur radio club in Macclesfield. He has been helping me with my
practical knowledge and advising on what gear to start with. For now, I have
ordered the affordable and well-regarded
[Baofeng UV-5RM Plus](https://www.baofengradio.com/products/uv-5rm-plus-8w-multi-band-radio)
handheld along with a NA-771 antenna. This will enable me to connect to
repeaters and hopefully make my first QSOs (contacts). I also plan to construct
some home-made antennas that I can attach to the handheld and experiment with.
![Monitoring over SDR](./img/monitoring-sdr.jpg)
As I'm still waiting for the radio to arrive, I am currently limited to
monitoring transmissions via my
[SDR](https://en.wikipedia.org/wiki/Software-defined_radio#Amateur_and_home_use)
(RTL-SDR v.4). I've been able to pick up some Morse conversations and a faint
read of the strange - possibly Iranian - number station that is currently
[perplexing the amateur community](https://youtu.be/ErmbTpxAM7Q?si=auxTnY8HSSnu1xZ5).
In order to access the HF band and reach contacts further afield by exploiting
[ionospheric propagation](https://www.qsl.net/4x4xm/Propagation/Ionosphere-propagation-of-radio-waves.htm),
I will need a better and more powerful tranceiver. I am looking into getting a
second-hand portable Yaesu through one of Greg's contacts. My plan is to drive
or cycle to good spots locally and work in the field. I also eventually want to
communicate over CW using Morse code and experiment with some radio astronomy.
The possibilities are pretty limitless with this hobby!
![The new to me X1 and some light reading](./img/thinkpad-x1-carbon.jpg)
To facilitate my field work I wanted a small and robust laptop so I bought a
second hand ThinkPad X1 Carbon (Gen.9). It's very sleek compared to my T15 which
I mainly use as a desktop device. I'm running Fedora Workstation for a
hassle-free and easily maintainable Linux environment.
Finally, I joined the RSGB. This gives me access to lots of useful radio
resources, free access to the
[National Radio Center](https://rsgb.org/main/about-us/national-radio-centre-gb3rs/)
at Bletchley Park, and the print edition of the monthly _RadCom_ magazine which
is a really good read.
Once I start transmitting I will report here on my progress...

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -13,7 +13,7 @@ const renderer = {
}
const highlighter = await createHighlighter({
themes: ["light-plus"],
themes: ["gruvbox-dark-hard"],
langs: [
"javascript",
@ -49,7 +49,7 @@ const posts = files.map((file) => {
(match, lang, code) => {
return highlighter.codeToHtml(code.trim(), {
lang: lang || "text",
theme: "light-plus",
theme: "gruvbox-dark-hard",
transformers: [transformerColorizedBrackets()],
})

View file

@ -37,8 +37,8 @@ const EolasListing = () => {
<div className="container mx-auto py-4 px-1 md:p-4 grow">
<div className="space-my-8">
<section className="container">
<h2 className="text-2xl font-semibold mb-4 text-[#d65d0e]! h2-home scanlined px-2">
{`eolas recent`}
<h2 className="text-2xl font-semibold mb-4 text-[#d65d0e]! h2-home">
{`Recent learning`}
</h2>
{isLoading && <div>Loading...</div>}

View file

@ -2,15 +2,9 @@ import MetricBar from "./MetricBar"
const LanguagesChart = ({ chartData, error }) => {
return (
<div style={{ marginTop: "1rem" }}>
<div
className="code-stat-label"
style={{
paddingBottom: ".5rem",
marginBottom: ".5rem",
}}
>
Programming languages
<div className="bg-sidebar p-3 my-4 mx-2 rounded-2xl">
<div className="text-muted-foreground text-sm pb-2">
programming languages
</div>
{error ? (
@ -24,7 +18,7 @@ const LanguagesChart = ({ chartData, error }) => {
metric={x.language}
hours={x.hours}
percentage={x.percentage}
color="black"
color="#458588"
/>
))
)}

View file

@ -9,7 +9,7 @@ const MetricBar = ({ metric, hours, percentage, color }) => (
}}
>
<span style={{}}>{metric}</span>
<span style={{ color: "black" }}>
<span style={{ color: "#bdae93" }}>
{hours}h ({percentage}%)
</span>
</div>
@ -17,7 +17,8 @@ const MetricBar = ({ metric, hours, percentage, color }) => (
style={{
width: "100%",
height: "8px",
backgroundColor: "lightgrey",
backgroundColor: "#32302f",
borderRadius: "4px",
overflow: "hidden",
}}
>

View file

@ -2,16 +2,8 @@ import MetricBar from "./MetricBar"
const ProjectsChart = ({ chartData, error }) => {
return (
<div style={{ marginTop: "1rem" }}>
<div
className="code-stat-label"
style={{
paddingBottom: ".5rem",
marginBottom: ".5rem",
}}
>
Repos
</div>
<div className="bg-sidebar p-3 my-4">
<div className="text-muted-foreground text-sm pb-2">projects</div>
{error ? (
<div>Data could not be found!</div>
@ -24,11 +16,11 @@ const ProjectsChart = ({ chartData, error }) => {
metric={x.project}
hours={x.hours}
percentage={x.percentage}
color="black"
color="#fe8019"
/>
))
)}
<div className="code-stats-disclaimer">
<div className="text-sm text-muted-foreground">
Data excludes workplace repos.
</div>
</div>

View file

@ -1,8 +1,8 @@
const Scorecard = ({ title, metric }) => {
return (
<div className="code-stat-grid">
<div className="code-stat-label">{title}</div>
<div className="code-stat-metric">{metric}</div>
<div className="bg-sidebar p-3 rounded-2xl">
<div className="text-muted-foreground text-sm">{title}</div>
<div className="text-lg">{metric}</div>
</div>
)
}

View file

@ -1,5 +1,4 @@
import { useQuery } from "@tanstack/react-query"
import wakapiApi from "../api/wakapi-api"
import { convertDateFriendly } from "../utils/convertDate"
import Scorecard from "../components/Scorecard"
@ -33,9 +32,7 @@ const CodeStats = () => {
data &&
data?.projects.filter(
(project) =>
!project.key.includes("gp-") &&
!project.key.includes("unknown") &&
!project.key.includes("amber")
!project.key.includes("gp-") && !project.key.includes("unknown")
)
const personalProjectsSorted =
@ -70,52 +67,45 @@ const CodeStats = () => {
.slice(0, 4)
return (
<div className="code-stats-sect">
<div className="">
<section className="">
<div className="">
<h2 className="big-title">{`Code this month`}</h2>
{/*
<table
border="1"
width="100%"
style={{ borderCollapse: "collapse" }}
>
<tr>
<td style={{ paddingLeft: "4px" }}>Period:</td>
<td style={{ paddingLeft: "4px" }}>
<div className="container mx-auto py-4 px-1 md:p-4 grow ">
<div className="space-my-8">
<section className="container">
<div className="flex flex-col md:flex-row items-start md:items-center md:justify-between">
<h2 className="text-3xl font-semibold mb-4 text-[#458588]! h2-home">
{`code stats`}
</h2>
<div className="mb-4 text-sm text-muted">
{convertDateFriendly(data?.from)} -{" "}
{convertDateFriendly(data?.to)}
</td>
</tr>
<tr>
<td style={{ paddingLeft: "4px" }}>Total time:</td>
<td style={{ paddingLeft: "4px" }}>
{error
? "Error"
: isLoading
? "Loading..."
: grandTotalFormatted}
</td>
</tr>
<tr>
<td style={{ paddingLeft: "4px" }}>Main project:</td>
<td style={{ paddingLeft: "4px" }}>
{error ? "Error" : isLoading ? "Loading..." : mainProject}
</td>
</tr>
</table>
*/}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mx-2">
<Scorecard
title="time coding"
metric={
error ? "Error" : isLoading ? "Loading..." : grandTotalFormatted
}
/>
<Scorecard
title="main project"
metric={error ? "Error" : isLoading ? "Loading..." : mainProject}
/>
<Scorecard
title="OS"
metric={error ? "Error" : isLoading ? "Loading..." : osMetric}
/>
</div>
<LanguagesChart chartData={languagesChartData} error={error} />
<ProjectsChart chartData={projectsChartData} error={error} />
<div className="code-stats-disclaimer">
<div className="text-sm text-center text-muted-foreground">
Data sourced from my self-hosted{" "}
<a href="https://wakapi.dev/" target="__blank" className="">
<a
href="https://wakapi.dev/"
target="__blank"
className="underline decoration-1 hover:text-primary/80 underline-offset-4 text-primary"
>
Wakapi
</a>{" "}
instance.

View file

@ -5,18 +5,24 @@ import { convertDate } from "@/utils/convertDate"
const PostListing = ({ posts, title, showAllButton }) => {
return (
<div className="">
<div className="">
<section className="">
<h2 className="big-title">{`${title}`}</h2>
<div className="container mx-auto py-4 px-1 md:p-4 grow">
<div className="space-my-8">
<section className="container">
<h2 className="text-3xl font-semibold mb-4 block h2-home">
{`${title}`}
</h2>
{posts.map((post) => (
<ul className="no-bullets">
<li className="">
<div className="post-listing-item">
<span style={{ marginRight: "1rem" }}>
<ul>
<li className="mb-4">
<div className="flex justify-between flex-col relative hover:bg-[#504945] hover:rounded-2xl p-2">
<span className="overflow-hidden whitespace-nowrap text-muted-foreground shrink-0 condensed">
{convertDate(post.date)}
</span>
<Link to={`/posts/${post.slug}`} key={post.slug} className="">
<Link
to={`/posts/${post.slug}`}
key={post.slug}
className="overflow-hidden text-ellipsis whitespace-nowrap min-w-0"
>
{post.title}
</Link>
</div>

View file

@ -1,149 +1,100 @@
@media (min-width: 768px) {
main {
max-width: 768px;
margin: 0 1.5rem;
}
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap");
@import "./styles/_variables.css";
@import "tailwindcss";
@import "tw-animate-css";
.post-listing-item {
display: flex;
flex-direction: row;
}
}
@media (max-width: 768px) {
.post-listing-item {
display: flex;
flex-direction: column;
}
.title-link {
letter-spacing: 0 !important;
}
}
.post-listing-item {
display: flex;
* {
outline-color: color-mix(in srgb, var(--ring) 50%, transparent);
}
html {
font-family: "DejaVu Sans", sans-serif;
font-family: var(--font-sansserif);
}
.header-links {
display: flex;
list-style: none;
gap: 1rem;
margin: 0;
padding: 0;
body {
background-color: var(--background);
color: var(--foreground);
}
.plain-link {
color: inherit;
text-decoration: none;
}
.no-bullets {
list-style: none;
padding-left: 0;
}
blockquote {
font-style: italic;
.condensed {
font-family: "IBM Plex Sans";
}
figcaption {
text-align: center;
font-style: italic;
font-size: 14px;
font-weight: 500;
font-family: "IBM Plex Sans";
}
figure {
text-align: center;
h1 {
color: var(--color-orange-light);
font-family: "IBM Plex Sans";
}
figure img {
h2 {
font-family: "IBM Plex Sans";
color: var(--color-green-light);
}
.h2-home {
font-family: "IBM Plex Sans";
font-weight: 600;
}
h3 {
font-family: "IBM Plex Sans";
font-weight: 600 !important;
}
.monospaced-font {
font-family: "iA Writer Mono";
}
.scanlined {
position: relative;
/* Add this */
}
.scanlined::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
max-width: 500px;
min-width: 300px;
height: auto;
height: 100%;
background-image: linear-gradient(rgba(0, 0, 0, 0.4) 1px, transparent 1px);
background-size: 2px 2px;
background-repeat: repeat;
pointer-events: none;
z-index: 9999;
/* Might want to lower this too */
}
code {
font-family: var(--font-monospaced);
}
p,
li {
font-size: 1.125rem !important;
line-height: 1.8 !important;
}
p code {
color: var(--foreground);
background: #504945;
font-size: 14px;
padding: 0.2rem 0.3rem;
border-radius: var(--radius);
font-weight: 500;
}
.shiki {
padding: 1rem;
/* border: 1pt solid black; */
background-color: whitesmoke !important;
overflow: auto;
}
.code-stats-sect {
font-family: sans-serif !important;
}
.code-stat-grid {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.code-stat-label {
font-weight: bold;
font-family: sans-serif;
}
.code-stats-disclaimer {
font-style: italic;
font-size: 14px;
text-align: center;
}
.about-li-padding {
padding-right: 1rem;
}
h1.site-title,
h1.post-title {
margin-bottom: 0.75rem;
}
.big-title {
padding-bottom: 0.5rem;
margin-bottom: 0.5rem;
border-bottom: 1px solid black;
}
.blog-meta {
font-size: 14px;
}
@font-face {
font-family: "DejaVu Sans";
src: url("/fonts/DejaVuSans.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "DejaVu Sans";
src: url("/fonts/DejaVuSans-Bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "DejaVu Sans";
src: url("/fonts/DejaVuSans-Oblique.woff2") format("woff2");
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "DejaVu Sans";
src: url("/fonts/DejaVuSans-BoldOblique.woff2") format("woff2");
font-weight: 700;
font-style: italic;
font-display: swap;
padding: 1rem 1.2rem;
border-radius: 0;
overflow-x: auto;
margin: 1.5rem 0;
line-height: 1.3;
/* counter-reset: line; */
font-family: var(--font-monospaced) !important;
font-size: 14px !important;
}

View file

@ -4,16 +4,16 @@ import portrait from "../images/portrait-compressed.jpg"
const AboutPage = () => {
return (
<MainTemplate>
<div className="">
<figure className="">
<div className="">
<div className="container mx-auto py-4 px-1 md:p-4">
<figure className="w-full flex flex-col items-center mb-6">
<div className="scanlined inline-block">
<img
alt="A portrait of the blog author"
src={portrait}
style={{ width: '25%' }}
className="w-80 flex"
/>
</div>
<figcaption className="">
<figcaption className="text-sm text-muted-foreground mt-3 text-center">
Pictured with the WITCH computer at the{" "}
<a
href="https://www.tnmoc.org/"
@ -57,33 +57,29 @@ const AboutPage = () => {
>
Arria NLG
</a>{" "}
and in several web developer roles. Before software, I was a
and in several web developer roles. Before software I was a
teacher.{" "}
</p>
<p>I am licenced amateur radio operator. My callsign is ME7SYO.</p>
<p className="leading-[1.6] [&:not(:first-child)]:mt-6">
Some things I like:
<ul className="no-bullets">
<li className=""><span className="about-li-padding">🐶</span>Staffies and other bull-breeds</li>
<li className=""><span className="about-li-padding">🎼</span>Classical music (Haydn, Mozart, JSB)</li>
<li className=""><span className="about-li-padding">🛸</span>Science fiction </li>
<li className=""><span className="about-li-padding">🐦</span>Bird watching</li>
<ul className="pt-2">
<li className="mb-1">🐶 Staffies and other bull-breeds</li>
<li className="mb-1">🎼 Classical music (Haydn, Mozart, JSB)</li>
<li className="mb-1">🛸 Science fiction </li>
</ul>
</p>
<p className="">
<p className="leading-[1.6] [&:not(:first-child)]:mt-6">
Some things I'm interested in:
<ul className="no-bullets">
<li className=""><span className="about-li-padding">🧑💻</span>Self-hosting and digital resiliance</li>
<li className=""><span className="about-li-padding">🖥</span>The history of computing and networks</li>
<li className=""><span className="about-li-padding"></span>Buddhism</li>
<li className=""><span className="about-li-padding">📡</span>Amateur radio</li>
<li className=""><span className="about-li-padding">👽</span>SETI</li>
<ul className="pt-2">
<li className="mb-1">🧑💻 Self-hosting and digital resiliance</li>
<li className="mb-1">🖳 The history of computing and networks</li>
<li className="mb-1"> Buddhism</li>
{/*
<li className="mb-1">📡 Civil communications infrastructure</li>
*/}
</ul>
</p>
</div>

View file

@ -4,23 +4,70 @@ import { usePosts } from "@/hooks/usePosts"
import gruvboxComputer from "../images/gruvbox-computer.svg"
import EolasListing from "@/components/EolasListing"
import CodeStats from "../containers/CodeStats"
import { Link } from "react-router"
const HomePage = () => {
const { posts } = usePosts()
return (
<MainTemplate>
<p>
A wizard who goes to bed early. This is <Link to="/about">my</Link>{" "}
technical scrapbook and digital garden.
<div className="container mx-auto md:p-4 py-4 px-1 grow">
<div className="space-my-8">
<section className="space-y-4">
<div className="gap-6 flex flex-col items-center sm:flex-row">
<div className="">
<img
src={gruvboxComputer}
className="md:w-80 w-50 rounded-2xl"
/>
</div>
<div>
<h1 className="text-5xl font-bold py-3 text-center sm:text-left md:text-left">
<div className="">systems obscure</div>
</h1>
<p className="text-center sm:text-left md:text-left text-muted font-medium text-lg mt-1">
A wizard who goes to bed early. This is my technical scrapbook
and digital garden.
</p>
</div>
</div>
</section>
</div>
</div>
<PostListing title="Recent posts" posts={posts.slice(0, 5)} />
<PostListing title="recent posts" posts={posts.slice(0, 5)} />
<PostListing
title="Highlights"
title="highlights"
posts={posts.filter((post) => post.tags.includes("highlight"))}
/>
<div className="container mx-auto md:p-4 py-4 px-1 grow mb-4">
<div className="">
<section className="container">
<h2 className="text-3xl font-semibold mb-4 text-[#d3869b]! h2-home">
{`projects`}
</h2>
<ul className="p-2">
<li className="pb-2">
<a
className="underline underline-offset-4 text-primary hover:text-primary/80 font-medium text-xl"
href="https://eolas.systemsobscure.net"
target="_blank"
>
eolas
</a>
</li>
<li>
<p className="">
A public frontend for my local Zettelkasten created with
NodeJS, Python and React.
</p>
</li>
</ul>
</section>
</div>
</div>
<CodeStats />
<EolasListing />
</MainTemplate>
)
}

View file

@ -9,7 +9,7 @@ const PostsPage = () => {
return (
<MainTemplate>
<PostListing title="All posts" posts={posts} />
<PostListing title="all posts" posts={posts} />
</MainTemplate>
)
}

View file

@ -1,7 +1,7 @@
:root {
--radius: 0.3rem;
--background: #282828;
--foreground: #ebdbb2;
--foreground: #fbf1c7;
--sidebar: #3c3836;
--color-red-light: #fb4934;
--color-orange-light: #fe8019;
@ -37,7 +37,7 @@
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--font-monospaced: "Jetbrains Mono";
--font-sansserif: "Inter", sans-serif;
--font-sansserif: "IBM Plex Sans", sans-serif;
}
@theme inline {

View file

@ -3,7 +3,6 @@ import MainTemplate from "./MainTemplate"
import { Link, useParams } from "react-router"
import { convertDate } from "@/utils/convertDate"
import { usePosts } from "@/hooks/usePosts"
import { convertDateFriendly } from "../utils/convertDate"
const BlogTemplate = () => {
const { slug } = useParams()
@ -12,32 +11,23 @@ const BlogTemplate = () => {
return (
<MainTemplate>
<div className="">
<div className="container mx-auto py-4 px-1 md:p-4 grow">
{!post ? (
<div>Loading...</div>
) : (
<article className="">
<header
className="big-title"
style={{ paddingBottom: "1rem", marginBottom: "2rem" }}
>
<h1 className="post-title">{post?.title}</h1>
<div
className="blog-meta"
style={{
display: "flex",
flexDirection: "row",
gap: "1rem",
}}
>
<time datetime={convertDate(post?.date)} className="text-sm">
{convertDateFriendly(post?.date)}
<article className="prose prose-lg max-w-none">
<header className="mb-6 pb-4">
<h1 className="text-5xl font-bold mb-4 inline-block">
{post?.title}
</h1>
<div className="flex flex-wrap align-center gap-4 text-[#928374] condensed font-medium">
<time datetime={convertDate(post?.date)} className="">
{convertDate(post?.date)}
</time>
<div className="">
<span>Tag(s): </span>
<div className="flex flex-wrap gap-3 align-center">
{post?.tags?.map((tag, i) => (
<Link
style={{ marginRight: "0.5rem" }}
className="text-primary underline underline-offset-3 hover:text-[#689d6a]"
key={i}
to={`/tags/${tag}`}
>
@ -48,7 +38,35 @@ const BlogTemplate = () => {
</div>
</header>
<div dangerouslySetInnerHTML={{ __html: post?.html }} />
<div
className="
[&>h2]:text-2xl [&>h2]:font-bold [&>h2]:my-4 [&>h2]:text-[#ebdbb2]!
[&>h3]:text-xl [&>h3]:font-bold [&>h3]:my-4 [&>h3]:text-[#ebdbb2]!
[&>h4]:text-lg [&>h4]:font-bold [&>h4]:my-4 [&>h4]:text-[#ebdbb2]!
[&>p]:leading-7 [&>p:not(:first-child)]:mt-4
[&>p+:is(h1,h2,h3,h4,h5,h6)]:mt-6
[&>blockquote]:mt-4 [&>blockquote]:border-l-2 [&>blockquote]:pl-6 [&>blockquote]:text-muted-foreground
[&>ul]:my-4 [&>ul]:ml-6 [&>ul]:list-disc [&>ul>li]:mt-2
[&>pre]:mt-6 [&>pre]:mb-6
[&>p+pre]:mt-6
[&>pre+p]:mt-6
[&>ul+pre]:mt-6
[&>li]:leading-[1.6]
[&_li_code]:relative [&_li_code]:rounded [&_li_code]:bg-[#504945] [&_li_code]:px-[0.3rem] [&_li_code]:py-[0.2rem] [&_li_code]:font-mono [&_li_code]:text-sm [&_li_code]:font-normal
[&>code]:relative [&>code]:rounded [&>code]:bg-[#504945] [&>code]:px-[0.3rem] [&>code]:py-[0.2rem] [&>code]:font-mono [&>code]:text-sm [&>code]:font-normal
[&>figure]:w-full [&>figure]:max-w-2xl [&>figure]:flex [&>figure]:flex-col [&>figure]:items-center [&>figure]:justify-center [&>figure]:mb-6 [&>figure]:mx-auto [&>figure>img]:max-w-full [&>figure>img]:max-h-[700px] [&>figure>img]:w-auto [&>figure>img]:h-auto [&>figure>img]:object-contain
[&>figure>img]:max-w-2xl [&>figure>img]:max-h-[700px] [&>figure>img]:w-auto [&>figure>img]:h-auto [&>figure>img]:object-contain
[&>figure>figcaption]:text-sm [&>figure>figcaption]:text-[#bdae93] [&>figure>figcaption]:mt-3 [&>figure>figcaption]:text-center
[&>figure>figcaption>a]:text-primary [&>figure>figcaption>a:hover]:text-primary/80
[&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-[#689d6a] [&_a]:text-primary
[&>table]:w-full [&>table]:my-4
[&>table>thead>tr]:m-0 [&>table>thead>tr]:border-t [&>table>thead>tr]:p-0 [&>table>thead>tr:even]:bg-muted
[&>table>thead>tr>th]:border [&>table>thead>tr>th]:px-4 [&>table>thead>tr>th]:py-2 [&>table>thead>tr>th]:text-left [&>table>thead>tr>th]:font-bold [&>table>thead>tr>th[align=center]]:text-center [&>table>thead>tr>th[align=right]]:text-right
[&>table>tbody>tr]:m-0 [&>table>tbody>tr]:border-t [&>table>tbody>tr]:p-0 [&>table>tbody>tr:even]:bg-muted
[&>table>tbody>tr>td]:border [&>table>tbody>tr>td]:px-4 [&>table>tbody>tr>td]:py-2 [&>table>tbody>tr>td]:text-left [&>table>tbody>tr>td[align=center]]:text-center [&>table>tbody>tr>td[align=right]]:text-right
"
dangerouslySetInnerHTML={{ __html: post?.html }}
/>
</article>
)}
</div>

View file

@ -1,44 +1,32 @@
// @ts-nocheck
import gruvboxComputer from "../images/gruvbox-computer.svg"
import { Link } from "react-router"
const Header = () => {
return (
<header>
<nav className="">
<h1 className="site-title">
<Link className="title-link plain-link" to="/">
Systems Obscure
<header className="md:py-6 pb-4">
<nav className="bg-sidebar container mx-auto justify-between flex gap-1 rounded-r-2xl">
<Link to="/">
<div className="">
<img src={gruvboxComputer} className="w-12" />
</div>
</Link>
</h1>
<ul className="header-links">
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/posts">Posts</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li className="flex flex-col items-center justify-center">
<a
className=""
href="https://forgejo.systemsobscure.net/thomasabishop"
target="blank"
<ul class="flex space-x-4 px-4 py-2 text-sm">
<li class="flex flex-col items-center justify-center">
<Link
class="text-primary hover:text-[#689d6a] condensed font-semibold text-xl"
to="/posts"
>
Code
</a>
posts
</Link>
</li>
<li className="flex flex-col items-center justify-center">
<a
className=""
href="https://fosstodon.org/@systemsobscure"
rel="me"
target="blank"
<li class="flex flex-col items-center justify-center">
<Link
class="text-primary hover:text-[#689d6a] condensed font-semibold text-xl"
to="/about"
>
Fosstodon
</a>
about
</Link>
</li>
</ul>
</nav>
@ -48,16 +36,16 @@ const Header = () => {
const Footer = () => {
return (
<footer className="mx-auto">
<nav>
<ul className="">
<footer className="bg-sidebar container mx-auto px-4 mt-10 mb-8 rounded-2xl p-2">
<nav className="">
<ul className="flex flex-row justify-start gap-4">
<li className="flex flex-col items-center justify-center">
<a
className=""
className="text-primary underline underline-offset-3 hover:text-[#689d6a] font-semibold"
href="https://forgejo.systemsobscure.net/thomasabishop"
target="blank"
>
Forgejo
forgejo
</a>
</li>
</ul>
@ -68,14 +56,13 @@ const Footer = () => {
const MainTemplate = ({ children }) => {
return (
<div className="">
<main className="">
<div className="antialiased max-w-3xl mt-3 mx-auto bg-[#282828] no-scanlines wrapper">
<main className="flex-auto min-w-0 mt-0 flex flex-col px-2 md:px-0">
<Header />
<div>{children}</div>
<Footer />
</main>
</div>
)
}
export default MainTemplate
//antialiased max-w-3xl mt-3 mx-auto bg-[#282828] no-scanlines wrapper
//main: flex-auto min-w-0 mt-0 flex flex-col px-2 md:px-0