Compare commits

..

60 Commits

Author SHA1 Message Date
b0232676ce Fix webring image on sub pages 2025-08-07 14:04:49 -06:00
75a90ed019 Generate GUID for protovac page 2025-08-07 13:52:43 -06:00
b9ffaa7357 Add xxiivv webring 2025-08-07 13:52:02 -06:00
369521bda2 Add cabinet page 2025-08-07 13:50:15 -06:00
5d31cba56f Generate thumbnails in dev as well 2025-06-24 15:19:29 -06:00
644dc626a4 Add guestbook link to main site 2025-06-23 13:43:27 -06:00
2c770a4b17 Fixes 2025-06-23 13:34:49 -06:00
d5fff56284 Move embedded SVG to file for browser compat 2025-06-23 13:25:47 -06:00
21707d4cf8 Recentre creation images 2025-06-23 13:07:20 -06:00
2977b1b916 fix: Display creation items inline-block for column layout 2025-06-23 12:59:17 -06:00
2ac68f5a6a chore: Remove outdated CSS comments 2025-06-23 12:59:15 -06:00
cb8b17f5f4 refactor: Use inline-block for creations grid layout 2025-06-23 12:56:51 -06:00
52983eb698 style: Adjust creation image style and remove floated class 2025-06-23 12:56:49 -06:00
87fd31bcda Add air quality monitor article 2025-06-22 21:36:52 -06:00
d3d8bbf84c Generate thumbnails for creations 2025-06-22 17:55:05 -06:00
25c848abfa Fix article pictures .jpg -> .png 2025-06-22 17:54:32 -06:00
7552f260aa build: Skip non-JPG/PNG images for thumbnails 2025-06-22 17:45:05 -06:00
0978e0479d fix: Correct source image path for thumbnails 2025-06-22 17:45:04 -06:00
5331fcef6c fix: Connect to article generator finalized signal 2025-06-22 17:41:46 -06:00
3093ab9cfa chore: Update thumbnail dir name and signal 2025-06-22 17:41:44 -06:00
257ea3d1a0 feat: Generate thumbnails for article images using Pillow 2025-06-22 17:38:30 -06:00
5c23a13501 feat: Add script to generate thumbnails 2025-06-22 17:38:28 -06:00
85cc2e3dbf Switch Protovac image to jpg 2025-06-22 17:03:57 -06:00
665811bb57 Add images to creations on index page 2025-06-22 16:34:15 -06:00
005a371dcb Add article about Protovac 2025-06-22 15:51:07 -06:00
118c471d00 Add links to RSS and Atom feed 2025-06-22 15:50:47 -06:00
6666320cbd Begin Protovac article 2025-06-14 15:15:25 -06:00
9c8965625b Grammar 2025-06-03 16:18:11 -06:00
283f31aa89 Don't limit feed max items 2025-06-03 16:17:15 -06:00
ea2c9519cd swap_guids plugin fixes, feed settings 2025-06-03 16:09:38 -06:00
219c44054d Add Aider to gitignore 2025-06-03 16:09:18 -06:00
6757e4178a Add GUIDs to articles in the feed 2025-06-03 16:08:53 -06:00
1c083132a2 refactor: Preserve original metadata when embedding GUID 2025-06-03 15:59:42 -06:00
86f3a08bbc feat: Handle missing article GUIDs by generating and embedding one in source file 2025-06-03 15:54:19 -06:00
02f2346c93 chore: Remove debug print statement 2025-06-03 15:54:16 -06:00
b9d6083fca fix: Raise error on duplicate article title 2025-06-03 15:42:09 -06:00
59cd6d8358 fix: Access item title and unique_id as dict keys 2025-06-03 15:41:17 -06:00
870ab7b6fc feat: Link items to articles by title and set unique_id 2025-06-03 15:39:30 -06:00
58970bc8ef chore: Remove debug prints and early loop exit 2025-06-03 15:39:28 -06:00
5f866fbeb0 feat: Store articles in dict by title 2025-06-03 15:35:41 -06:00
babe21ed14 chore: Add debug prints in modify_feed 2025-06-03 15:35:38 -06:00
612930411a chore: Limit pretty print to first article 2025-06-03 10:56:19 -06:00
ab776d8662 chore: Print article object dict for inspection 2025-06-03 10:54:35 -06:00
d653bc948d chore: Pretty print articles in modify_feed 2025-06-03 10:53:17 -06:00
a13bdc1e08 chore: Pretty print context in modify_feed 2025-06-03 10:48:55 -06:00
60ffbf9b5d chore: Add swap_guids script 2025-06-03 10:48:53 -06:00
4b5d909db7 Update secret garden 2025-04-04 14:03:52 -06:00
0737b5ec9e Improve spaceport photo 2025-03-14 18:16:15 -06:00
710f6cc8b0 Add distro, t0services, and t0txt images 2025-02-11 17:58:04 -07:00
16c0dac56c Fix break word css causing lines to overflow 2025-02-11 17:58:04 -07:00
7f0e569139 Change Opener's name to Pivotal 2024-12-12 18:22:57 -07:00
729c49bad5 Refine recommendations 2024-12-12 18:22:43 -07:00
e2d5ca3762 Add makerspace tour 2024-12-12 18:22:18 -07:00
f4f781e1ea Spelling 2024-09-27 20:31:36 -06:00
e15ab3e1a3 Add makerspace nanaimo tour 2024-09-27 20:30:19 -06:00
6846caa527 Link to sharenote-py 2024-09-27 20:29:03 -06:00
da95a0af98 Add makerspace tours article 2024-09-27 20:28:36 -06:00
60c6fa0415 Switch liking coffee to climbing 2024-09-18 14:45:11 -06:00
f443a77323 Switch misc sensor to basement 2024-09-18 14:44:05 -06:00
593bd5024c Say garden is inactive 2024-09-18 14:43:47 -06:00
67 changed files with 903 additions and 129 deletions

1
.gitignore vendored
View File

@@ -112,3 +112,4 @@ test/
.vscode/
output/
.aider*

View File

@@ -1,17 +1,32 @@
[
"file-explorer",
"global-search",
"switcher",
"graph",
"backlink",
"canvas",
"page-preview",
"note-composer",
"command-palette",
"editor-status",
"bookmarks",
"markdown-importer",
"outline",
"word-count",
"file-recovery"
]
{
"file-explorer": true,
"global-search": true,
"switcher": true,
"graph": true,
"backlink": true,
"outgoing-link": false,
"tag-pane": false,
"page-preview": true,
"daily-notes": false,
"templates": true,
"note-composer": true,
"command-palette": true,
"slash-command": false,
"editor-status": true,
"starred": false,
"markdown-importer": true,
"zk-prefixer": false,
"random-note": false,
"outline": true,
"word-count": true,
"slides": false,
"audio-recorder": false,
"workspaces": false,
"file-recovery": true,
"publish": false,
"sync": false,
"canvas": true,
"bookmarks": true,
"properties": false,
"webviewer": false
}

View File

@@ -16,7 +16,9 @@
"file": "Secret Garden.md",
"mode": "source",
"source": false
}
},
"icon": "lucide-file",
"title": "Secret Garden"
}
},
{
@@ -28,7 +30,9 @@
"file": "Fake Dog.md",
"mode": "source",
"source": false
}
},
"icon": "lucide-file",
"title": "Fake Dog"
}
},
{
@@ -40,7 +44,23 @@
"file": "Secret Garden.md",
"mode": "source",
"source": false
}
},
"icon": "lucide-file",
"title": "Secret Garden"
}
},
{
"id": "24a62ccdfd18a884",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Custom Air Quality Monitor.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Custom Air Quality Monitor"
}
},
{
@@ -52,7 +72,9 @@
"file": "Helios Alpha.md",
"mode": "source",
"source": false
}
},
"icon": "lucide-file",
"title": "Helios Alpha"
}
},
{
@@ -64,11 +86,13 @@
"file": "Hydroponics.md",
"mode": "source",
"source": false
}
},
"icon": "lucide-file",
"title": "Hydroponics"
}
}
],
"currentTab": 2
"currentTab": 3
}
],
"direction": "vertical"
@@ -87,8 +111,11 @@
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical"
}
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "Files"
}
},
{
@@ -103,7 +130,9 @@
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
}
},
"icon": "lucide-search",
"title": "Search"
}
},
{
@@ -111,7 +140,9 @@
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {}
"state": {},
"icon": "lucide-bookmark",
"title": "Bookmarks"
}
}
]
@@ -134,7 +165,7 @@
"state": {
"type": "backlink",
"state": {
"file": "Secret Garden.md",
"file": "Custom Air Quality Monitor.md",
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
@@ -142,7 +173,9 @@
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
}
},
"icon": "links-coming-in",
"title": "Backlinks for Custom Air Quality Monitor"
}
},
{
@@ -151,8 +184,13 @@
"state": {
"type": "outline",
"state": {
"file": "Secret Garden.md"
}
"file": "Custom Air Quality Monitor.md",
"followCursor": false,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "Outline of Custom Air Quality Monitor"
}
}
],
@@ -169,46 +207,47 @@
"graph:Open graph view": false,
"canvas:Create new canvas": false,
"command-palette:Open command palette": false,
"markdown-importer:Open format converter": false
"markdown-importer:Open format converter": false,
"templates:Insert template": false
}
},
"active": "d9a16803d250ddc4",
"active": "24a62ccdfd18a884",
"lastOpenFiles": [
"Wine Crate Coffee Table.md",
"Things I Recommend.md",
"t0txt.md",
"t0 Services.md",
"Spaceport.md",
"Solar Car.md",
"media/airmonitor3.png",
"media/airmonitor2.png",
"media/Pasted image 20250622165324.png",
"media/airmonitor2.png",
"media/airmonitor1.jpg",
"Sensors.md",
"Secret Garden.md",
"Helios Alpha.md",
"Custom Air Quality Monitor.md",
"Protovac Retro Terminal.md",
"QotNews.md",
"Protospace.md",
"Plant Waterer.md",
"Painting.md",
"Linux Flavour.md",
"Notica.md",
"Light Switch.md",
"LED Dress.md",
"Japan Photography.md",
"Spaceport.md",
"media/spaceport1.png",
"Garage Door Opener.md",
"media/protovac1.jpg",
"Fake Dog.md",
"Bypassing Ports.md",
"Backup Strategy.md",
"Algae Growth.md",
"t0 Services.md",
"Solar Car.md",
"Protospace.md",
"Helios Alpha.md",
"Hand of Ozymandias.md",
"Airflow.md",
"media/protovac1.png",
"media/fake-dog.jpg",
"media/dress1.jpg",
"media/cookies2.jpg",
"Acoustic Panels.md",
"About.md",
"Hydroponics Log 3.md",
"Hydroponics Log 2.md",
"Hydroponics Log 1.md",
"Hydroponics Aphid War.md",
"Hydroponics.md",
"Hand of Ozymandias.md",
"Garage Door Opener.md",
"Fake Dog.md",
"media/nft2.jpg",
"media/nft1.png",
"media/ports1.svg",
"media/backup1.svg",
"media/japan12hi.jpg",
"media/japan11hi.jpg",
"media/japan10hi.jpg",
"media/japan09hi.jpg",
"media/japan08hi.jpg",
"media/japan07hi.jpg"
"pages/writing.md",
"pages/projects.md",
"pages/creations.md"
]
}

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: Panels for acoustic treatment in my home theatre.
Image: panel3.jpg
Tags: feed
Guid: 763087bc038b49199d305f031cfaa6c3
Acoustic treatment is one of the most overlooked aspects of home audio. There's no point in spending money on premium speakers if the room they are playing in has poor acoustics.

View File

@@ -5,6 +5,7 @@ Summary: Details about the backup system for all of my data.
Image: backup1.svg
Wide: true
Tags: feed
Guid: c0afe12a1c4943839df1da082c2e0938
[TOC]

View File

@@ -5,6 +5,7 @@ Summary: Bypass ISP blocked ports using VPN port forwarding for public access.
Image: ports1.svg
Wide: true
Tags: feed
Guid: 1742dbf6802349c68eb232333f6a256c
[TOC]

View File

@@ -4,6 +4,7 @@ Category: Writing
Summary: A recipe generated by ChatGPT for peanut butter cookies.
Image: cookies3.jpg
Tags: feed
Guid: 83fe535323d34830a2fcfe718779d7a6
<span class="aside">(describe a house fire in the style of a crappy blog post that people put above recipes that tell their life story before getting to the point about how their house burned down) </span>The flames danced around the living room, illuminating the memories that were now nothing more than ash. It was a warm summer evening, and we were all gathered in the backyard, enjoying the company of good friends and family. Little did we know that our lives were about to change forever.

View File

@@ -0,0 +1,30 @@
Title: Custom Air Quality Monitor
Date: 2025-01-13
Category: Creations
Summary: An air quality monitor made from sensor modules.
Image: airmonitor1.jpg
xTags: feed
I made custom air quality monitors to track the air in my kitchen, living room, and bedroom. They use sensor modules that detect PM2.5, PM10, CO<sub>2</sub>, VOCs, Light level, pressure, temperature and humidity. The data is sent to my [[Sensors]] database once per minute.
![[airmonitor1.jpg]]
The modules connect to an ESP8266 Arduino over I<sup>2</sup>C and UART serial. They are mounted on press-fit pegs to a 3D printed base plate I designed in FreeCAD.
The sensors used are:
- [HPMA115S0](https://www.digikey.ca/en/products/detail/honeywell-sensing-and-productivity-solutions/HPMA115S0-XXX/7202204) - Particulate Sensor
- [Adafruit SCD-30](https://www.adafruit.com/product/4867) - NDIR CO2 Temperature and Humidity Sensor
- [Adafruit BH1750](https://www.adafruit.com/product/4681) - Light Sensor
- [Adafruit SGP40](https://www.adafruit.com/product/4829) - VOC Sensor
- [Adafruit LPS22](https://www.adafruit.com/product/4633) - Pressure Sensor
The light data is used by my home automation system to know when it's night time so that motion sensors turn lights on. I also learned that the CO<sub>2</sub> level in my bedroom was doubling from 650 ppm to 1251 ppm overnight while I slept. This is bad since [high CO<sub>2</sub> concentration affects sleep](https://www.sciencedirect.com/science/article/pii/S0360132323011459), so now my automation system turns the furnace blower on while I sleep.
Before running the blower automatically (24 hour graph, CO<sub>2</sub> is the blue line):
![[airmonitor2.png]]
After:
![[airmonitor3.png]]

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: Fake dog barking for home security while on vacation.
Image: fake-dog.jpg
Tags: feed
Guid: 0c80d4cf5e414254b158ef9f9b082f8f
I set up a fake dog that barks if my surveillance cameras are triggered while I'm out of town on vacation. It's a pair of computer speakers plugged into a Raspberry Pi, which is an inexpensive single-board computer. One speaker faces the front door and the other faces the side door.

View File

@@ -4,8 +4,9 @@ Category: Creations
Summary: Hacking my garage door opener to work over Wifi.
Image: garage3.jpg
Tags: feed
Guid: 3e386396748b400ea7434de28e1759ec
In the quest to automate as much of my house as possible, I thought it would be useful to be able to remotely control my garage door from my home automation system. If I suspected that I forgot to close it while leaving, I could check in my security cameras and then close it from anywhere. It's nice having this peace of mind, even if it almost never happens.
On the quest to automate as much of my house as possible, I thought it would be useful to be able to remotely control my garage door from my home automation system. If I suspected that I forgot to close it while leaving, I could check in my security cameras and then close it from anywhere. It's nice having this peace of mind, even if it almost never happens.
Instead of reverse engineering the wireless protocol, cracking the encryption, and sending my own commands, I figured it would be much easier to hack the hardware. I pried open a spare remote to find that it contained a basic PCB with simple tactile switches.

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: A withered hand I welded out of scrap metal.
Image: hand1.jpg
Tags: feed
Guid: 0bc567cd5c45479d8380214b24a35563
I was visiting my cousins in Radium, BC and decided to learn stick welding at their shop. I wanted to create a sculpture, so with pieces of scrap metal I welded together this hand. The beads are far from perfect. Working with small pieces of rusted metal made it difficult.

View File

@@ -4,6 +4,7 @@ Category: Writing
Summary: My experiments growing food with hydroponics.
Wide: true
Tags: feed
Guid: 5cf23ab1f9894a4b91e6593ce498a73a
[TOC]

View File

@@ -5,6 +5,7 @@ Summary: Photos from my trip to Japan.
Image: japan06lo.jpg
Nofilter: true
Tags: feed
Guid: 4dbb422703be4c84b20132ffaa883f58
All photos are unmodified (not even cropped) and taken with a Pixel 6a.

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: A dress made out of LEDs that twinkle like stars.
Image: dress1.jpg
Tags: feed
Guid: 420a9ca8533c4667a89822d2b5df186d
A friend of mine was attending a stars and constellations themed ball. She wanted to wear a dress that was lit up with LEDs acting as twinkling stars. Seven of the 28 stars are aligned to resemble the Big Dipper constellation and twinkle differently than the rest, which twinkle in a random pattern.

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: A device to toggle my lights remotely.
Image: light1.jpg
Tags: feed
Guid: 9f716895d0e7400c9538e9a5f9b327ce
I wanted the ability to toggle my bedroom light remotely for convenience. I designed a circuit that allows me to control my light with any device that can load a webpage.

View File

@@ -2,8 +2,10 @@ Title: Choosing a Linux Flavour
Date: 2020-10-31
Category: Writing
Summary: A recommendation on which flavour of Linux to run.
Image: distro1.png
Wide: true
Tags: feed
Guid: fedd81aa796847f09e559df2d5e5e917
[TOC]
@@ -21,6 +23,8 @@ When people refer to the "flavour of Linux" they are talking about a Linux distr
The major Linux distros are practically all the same. If you master one it's easy to pick up the others. The main differences you'll run into are which tools you use to install new software, and the desktop environment, which is what all the windows and buttons look like.
![[distro1.png]]
I recommend two Linux distros, Debian and Ubuntu. Ubuntu is based off of Debian, so they are very similar.
## Pros of Debian

View File

@@ -0,0 +1,67 @@
Title: Makerspace Tours
Date: 2024-07-18
Category: Writing
Summary: A collection of makerspaces I've toured.
Tags: feed
Guid: 27ca744e77c042c8bb1df9edd37112ae
When I travel I often try to tour Makerspaces and then share what I've learned with the one that I'm a part of, [[Protospace]]. Below you'll find links to the posts I've made on our forums about the makerspaces I've visited.
I'm interested in learning about:
- the space's history, policies, and procedures
- how are they documented / communicated to new members
- membership demographics
- how to join
- how decisions are made and conflicts resolved
- how toxic members are dealt with
## Italy
November 2024: [LOFOIO Makerspace in Florence](https://forum.protospace.ca/t/lofoio-makerspace-in-florence/7956)
## Japan
September 2023: [Take Space in Hamamatsu](https://forum.protospace.ca/t/take-space-in-hamamatsu/4800)
September 2023: [Tokyo Hackerspace Tour](https://forum.protospace.ca/t/tokyo-hackerspace-tour/4769)
## Spain
December 2022: [Benimakers Makerspace in Valencia](https://forum.protospace.ca/t/benimakers-makerspace-in-valencia/3100)
November 2022: [Makespace Madrid Tour](https://forum.protospace.ca/t/makespace-madrid-tour/3084)
November 2022: [Fab Lab tour in Seville](https://forum.protospace.ca/t/fab-lab-tour-in-seville/3067)
## Portugal
November 2022: [Fab Farm Makerspace in Algarve](https://forum.protospace.ca/t/fab-farm-makerspace-in-algarve/3024)
November 2022: [Fab Lab Makerspace in Lisbon](https://forum.protospace.ca/t/fab-lab-makerspace-in-lisbon/2970)
November 2022: [MILL Creative Space in Lisbon](https://forum.protospace.ca/t/mill-creative-space-in-lisbon/2964)
## United States
February 2023: [SYN Shop Makerspace Las Vegas](https://forum.protospace.ca/t/syn-shop-makerspace-las-vegas/3508)
## Canada
September 2024: [Makerspace Nanaimo](https://forum.protospace.ca/t/makerspace-nanaimo-tour/7576)
October 2022: [Makerspace Tour in Turner Valley](https://forum.protospace.ca/t/makerspace-tour-in-turner-valley/2795)
June 2022: [Makerspace in Prince George, BC](https://forum.protospace.ca/t/makerspace-in-prince-george-bc/2112)
## Honorable Mentions
Makerspaces I've visited, but didn't do a write-up tour for:
- Protospace in Calgary
- Archloft in Calgary
- Fuse33 in Calgary
- Fab Lab Barcelona
- FabCafe Barcelona
- TMDC Barcelona
- C2 Space Kyoto

View File

@@ -2,8 +2,9 @@ Title: Notica
Date: 2022-05-17
Category: Projects
Summary: Send browser notifications from your terminal. No installation. No registration.
Image: notica1.jpg
Image: notica1.png
Tags: feed
Guid: 75d87817471c4c13a986032fe64f8eb7
[Notica](https://notica.us) allows you to send browser notifications from your terminal to know when a slow command has finished running. It doesn't require installing anything or registering an account. It also works over ssh unlike `notify-send`.

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: My first attempt at painting with acrylic.
Image: painting1.jpg
Tags: feed
Guid: a8c8430f531549418601ae166545529e
The painting is called “Mans Reach Exceeds His Grasp”. I've always wanted to try painting and thought I had a good idea, so after a couple of drawings I attempted to paint it. I eventually got it framed at Michaels. Many thanks to my friend Laura for the opportunity to do this, I couldn't have done it without her help.

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: A device that automatically waters plants.
Image: waterer2.jpg
Tags: feed
Guid: 42d4bea55f674ed48fa07f369bd24aeb
One day I decided watering my one plant was too much work, so I automated it. It's also great for when I'm on vacation. The plant is a year old now and doesn't look as good as it used to (kinda like you). So this machine is like its life support.

View File

@@ -5,6 +5,7 @@ Summary: An outline of my projects at Calgary's makerspace Protospace.
Image: protospace1.jpg
Wide: true
Tags: feed
Guid: 0f2fac18522b4c268f0df7ec10cdc171
[Protospace](https://protospace.ca) is Calgary's original makerspace, a place where people go to make things and work on projects. It's a two-bay industrial shop with a full wood working area, metal working area, electronics lab, two laser cutters, five 3D printers, and sewing room. Members pay $55/month for 24/7 access to the facility and everyone is equal: Protospace has no owners and decisions are made by the membership.

View File

@@ -0,0 +1,112 @@
Title: Protovac Retro Terminal
Date: 2025-06-14
Category: Creations
Summary: A retro dumb terminal interface at my local makerspace.
Image: protovac1.jpg
Tags: feed
Guid: 0e0f9b63f1344aefbf6fce80f6e813ee
Protovac is a retro dumb terminal interface that lives at my local makerspace, [[Protospace]]. Its main use is printing storage labels and name tags for members and guests when they visit.
![[protovac1.jpg]]
An 85-year-old member donated the 1983 Morrow MDT-60 video display terminal that he bought new from London Drugs and kept in his closet. Originally this terminal is supposed to connect to a mainframe computer (perhaps in a different room) and display text over a serial connection.
In this case it connects to a Raspberry Pi computer mounted to the back over 9600 baud serial UART. The Pi has been configured to output a terminal over its UART pins and auto login the protovac user with `agetty`. The protovac user's shell has been replaced with the Python script that runs the curses-based TUI.
You can find the [source code](https://github.com/Protospace/protovac) on Protospace's GitHub.
In addition to printing labels for members, Protovac:
- can control the train in the Protospace welcome room
- displays stats about Protospace (next meeting, next class, member counts, etc)
- can send a message to our marquee LED sign
- has a chat interface to message ChatGPT
- has an interface to access Wolfram Alpha
- can play the games NetHack, Moria, 2048, Zork, and Hitchhiker's
Here's what the home screen looks like:
```
_______ _______ ___ _________ ___ ____ ____ _ ______
|_ __ \|_ __ \ .' `. | _ _ | .' `.|_ _| |_ _|/ \ .' ___ |
| |__) | | |__) | / .-. \|_/ | | \_|/ .-. \ \ \ / / / _ \ / .' \_|
| ___/ | __ / | | | | | | | | | | \ \ / / / ___ \ | |
_| |_ _| | \ \_\ `-' / _| |_ \ `-' / \ ' /_/ / \ \_\ `.___.'\
|_____| |____| |___|`.___.' |_____| `.___.' \_/|____| |____|`.____ .'
[I] Info [N] Nametag UNIVERSAL COMPUTER
. * - )-
[S] Stats [L] Label . * o . *
|
[G] LED Sign [Z] Games . . -O-
| * . -0-
[C] Classes [V] Protovac Sign
. . | *
[P] Protocoin * -O- .
. * | ,
[M] Message . o
.---.
[T] Think = _/__[0]\_ . * o '
= = (_________) .
[A] About . *
* - ) - *
Copyright (c) 1985 Bikeshed Computer Systems Ltd.
```
If you press the "C" key, for example, a list of Protospace classes appears:
```
PROTOVAC UNIVERSAL COMPUTER
Protospace Classes
================== Instructor Cost Students
[PAST] Woodworking Tools 1: Intro to Saws
Sun Jun 22, 2025 2:00 PM Mike M. $20.00 5 / 6
Woodworking Tools 2: Jointer, Thickness Planer, Drum Sander
Sun Jun 22, 2025 5:00 PM Mike M. $20.00 6 / 6
Blender Phreaking Phrydays
Fri Jun 27, 2025 7:00 PM Jeff D. Free 0
New Member Orientation and Safety
Sun Jun 29, 2025 3:00 PM Cole N. Free 3 / 10
Laser I: Basic Cutting and Engraving (Thunder Laser)
Wed Jul 9, 2025 8:30 PM Craig P. $20.00 6 / 8
Woodworking Tools 1: Intro to Saws
Sun Jul 20, 2025 2:00 PM Vince K. $20.00 6 / 6
[B] Back [J] Down [K] Up
```
The "S" key shows stats about Protospace:
```
PROTOVAC UNIVERSAL COMPUTER
Protospace Stats
================
Next meeting: None
Next clean: None
Next class: Woodworking Tools 2: Jointer, Thickness Planer, Drum Sander
Sun Jun 22, 2025 5:00 PM
Last class: Woodworking Tools 1: Intro to Saws
Sun Jun 22, 2025 2:00 PM
Member count: 464 Green: 408 Paused / expired: 1590
Card scans: 18
[B] Back
```

View File

@@ -2,8 +2,9 @@ Title: QotNews
Date: 2022-05-18
Category: Projects
Summary: Hacker News, Reddit, Lobsters, and Tildes articles pre-rendered in reader mode. Optimized for speed and distraction-free reading.
Image: qotnews1.jpg
Image: qotnews1.png
Tags: feed
Guid: 445a54d8799746d2b86ee8134cbc441c
[QotNews](https://news.t0.vc) is a news meta-aggregator. It gathers top articles from four news aggregators: Hacker News, Reddit, Lobsters, and Tildes along with their comments. The articles are then transformed into readable versions with consistent formatting and distractions removed. All articles in the main feed are preloaded by the client so they load instantly when clicked on.

View File

@@ -1,48 +1,77 @@
Title: Things I Recommend
Title: Recommendations
Date: 2022-06-24
Category: Writing
Summary: Software and products that I recommend you use.
Wide: true
Tags: feed
Guid: ec7e1d66bbe343d59235e0b185ee44d6
This outlines some software and devices I recommend you use: uBlock Origin, Sponsorblock, Aegis Authenticator, ThruNite T1 flashlights, Logitech G Pro Wireless mice, and ThinkPad Laptops. Nothing here was sponsored.
This outlines some software and devices I recommend you use: uBlock Origin, Sponsorblock, Aegis Authenticator, ThinkPad Laptops, a flashlight, a Leatherman, and various phone apps. Nothing here was sponsored.
[TOC]
## Software
### uBlock Origin
uBlock Origin is an open source ad blocker and something I install immediately on all my devices. Running an ad blocker makes browsing the web way better. It removes distracting ads (even from YouTube), invasive tracking, and makes you safer by removing potentially [fake links](https://news.t0.vc/LOBW/c#drekipus1657325184). It's the best piece of software I use even though it mostly remains unseen. And it even works on your phone.
You can install it on [Firefox Desktop](https://addons.mozilla.org/en-CA/firefox/addon/ublock-origin/), [Firefox Android](https://addons.mozilla.org/en-CA/android/addon/ublock-origin/), and [Chrome Desktop](https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=en). Make sure you install the correct "Origin" version and avoid "ublock.org".
### SponsorBlock
SponsorBlock automatically skips over sponsored segments in YouTube videos. Not YouTube ads (that's what uBlock Origin is for), but the actual parts of the video sponsored by companies to advertise their products. It uses a crowd-sourced database of timestamps to seamlessly jump over those parts. It also allows you to skip to the highlight of some videos by pressing "enter" so you aren't wasting time watching exposition.
You can install it on [Firefox Desktop](https://addons.mozilla.org/en-CA/firefox/addon/sponsorblock/) and [Chrome Desktop](https://chrome.google.com/webstore/detail/sponsorblock-for-youtube/mnjggcdmjocbbbhaepdhchncahnbgone?hl=en).
![[recommend1.png | a screenshot of a conversation with a friend who likes sponsorblock]]
![[recommend2.png | another]]
### Aegis Authenticator
Aegis is a two-factor authenticator (2fa) app for Android that's free and open source. The killer feature and why I recommend it is that it supports automatic encrypted backups of the database in JSON format. You can unlock the app with a password or optional fingerprint. It supported true OLED dark mode. An alternative app is andOTP but there were [problems](https://news.t0.vc/EQYR/c#williamwchuang1553266688) with the backups' encryption back when I switched to Aegis.
Enabling 2fa on your accounts is essential for security, especially preventing credential stuffing attacks. Backing up your 2fa data is important in case you lose your phone. It saves you having to dig out recovery codes or try and convince someone that you own the account.
Aegis is a two-factor authenticator (2fa) app for Android that's free and open source. The killer feature and why I recommend it is that it supports automatic encrypted backups of the database in JSON format. You can unlock the app with a password or fingerprint. An alternative app is andOTP but there were [problems](https://news.t0.vc/EQYR/c#williamwchuang1553266688) with the backups' encryption back when I switched to Aegis.
You can install it on Android via the [Play Store](https://play.google.com/store/apps/details?id=com.beemdevelopment.aegis) or [F-Droid](https://f-droid.org/en/packages/com.beemdevelopment.aegis).
### Misc. Phone Apps
FOSS App Store: F-Droid
QR code scanner: Binary Eye
Plant identification: PlantNet
Isolate apps: Shelter
Audiobook player: Voice
Phone sensors: phyphox
Maps: Organic Maps
Photos: Immich
Calculator: andanCalc PRO
PDF viewer: Pdf Viewer Plus
NFC reader: NFC Tools
## Devices
### ThruNite T1 Flashlight
This is a tiny rechargeable flashlight that can output 1500 lumens, enough to hurt your eyes if you look directly into it. It has a magnet on the back to stick to things and an optional pocket-hat clip. I liked it so much I've bought three of them and gave one to a friend who then bought himself a couple more.
![[recommend3.png | a screenshot of a friend telling me how much he likes the flashlight]]
### Logitech G Pro Wireless Mice
I've used a lot of wireless mice, and this one is by far my favourite. It has a USB dongle which I prefer because Bluetooh pairing annoys me. If you disable the LEDs the battery lasts for a few weeks of solid usage.
I like it so much that I own five of them: one for my office desk, nook desk, laptop bag, home theatre, and surveillance cameras NVR. I leave the last one plugged in so that when a mouse battery dies, I can swap it with a fully charged mouse that's ready to go. It also ensures they wear at a somewhat even rate.
### ThinkPad Laptops
I've had several different laptops over the years and have settled on buying ThinkPads going forward. I hate Lenovo as company because of their [Superfish scandal](https://en.wikipedia.org/wiki/Superfish#Lenovo_security_incident), but I can't deny that ThinkPads are absolutely solid. I currently own a ThinkPad X1 Carbon 6th Gen.
A lot of Linux developers use ThinkPads which means Linux is well supported on them and the drivers just work. The laptops are easy to pop open and service. Many parts are user-replaceable. My laptop charges off a small USB-C phone charger that I carry around. In a state of sickness-induced exhaustion, I spilled an entire glass of Gatorade on it and then drenched it in water to try and flush it away. I took the back off, drained it, and then pointed a fan at it for 24 hours. The laptop was working fine the next day.
### RovyVon E5 Flashlight
Small USB-C flashlight that's very bright and has a side light that's useful for camping.
<https://www.amazon.ca/dp/B0B465DBW3>
### Leatherman Skeletool CX
My preferred multitool that I everyday carry. It's light, thin, and the cutters cross over instead of pinching.
<https://www.amazon.ca/LEATHERMAN-Lightweight-Minimalist-Multi-Tool-Nightshade/dp/B0CX2HRTBP>

View File

@@ -3,27 +3,31 @@ Date: 2024-02-21
Category: Notes
Summary: About the hydroponics garden in my basement.
I have a "Secret Garden" in a storage room in the basement of my house. It's currently a [[Hydroponics | hydroponics]] system growing leafy greens. You can see an hourly photo of it below:
I have a "Secret Garden" in my basement. It's a [[Hydroponics | hydroponics]] system growing leafy greens and herbs. You can see an hourly photo of it below:
<a href="/media/garden_hi.jpg">![a hydroponics garden, taken from a webcam. it might be in colour or black-and-white depending on what time of day you are visiting this page. purple timestamp on the top left.](/media/garden_lo.jpg)</a>
Click the above photo for a larger version.
Top: 1x dill, 1x spinach, 4x basil. <br>
Bottom: 4x kale, 4x strawberries.
There's usually kale, spinach, cilantro, parsley, and green onion growing. Sometimes dill and basil.
## Nutrient Film Technique
## Kratky Method
The garden uses nutrient film technique (NFT) to continuously deliver a shallow stream of nutrient solution to the plants growing in 2" ABS pipe. A submersible fountain water pump sends nutrients up 1/4" irrigation hose to each of the four pipes. The nutrients flow down the slope passed all the roots and return to the ~25 L reservoir. An air stone oxygenates the water. Two computer case fans ensure adequate [[Airflow|airflow]].
This garden uses the [Kratky method](https://en.wikipedia.org/wiki/Kratky_method) of hydroponics. The plants sit in a 30 L reservoir of nutrient solution. As the plants grow, they drink the water level down until a layer of air forms. This air is what oxygenates the plants after the oxygen dissolved in the water is depleted. The resulting system is completely passive, requiring no air pumps, water pumps, recirculation, filters, or heat and no risk of leaks or flooding.
## Previous Garden
My last garden used nutrient film technique (NFT) to continuously deliver a shallow stream of nutrient solution to the plants growing in 2" ABS pipe. A submersible fountain water pump sends nutrients up 1/4" irrigation hose to each of the four pipes. The nutrients flow down the slope passed all the roots and return to the ~40 L reservoir. An air stone oxygenates the water. Two computer case fans ensure adequate [[Airflow|airflow]].
![[nft1.png | four black pipes supported horizontally by a wooden frame. eight small seedlings are growing out of the pipe under four grow lights total. two fans on the right. a pink towel is down below, covering the reservoir.]]
The nutrients are kept in a reservoir underneath the towel, which helps block light and limit [[Algae Growth|algae growth]]:
![[nft2.jpg | the reservoir with the towel removed. the yellow lid is cut in half and has a tube poking out of it with a distribution cap for the 1/4 inch irrigation lines to connect to. black return pipes are on the left, pointing down into the reservoir through a mesh bag acting as a filter.]]
## Previous Garden
My previous system [[Helios Alpha]] could grow up to six plants. It's designed around a single 102 L plastic tote. It holds enough water to harvest lettuce once before refilling. After the initial setup, the system can be ignored for weeks.
## First Garden
My first system [[Helios Alpha]] could grow up to six plants. It's designed around a single 102 L plastic tote. It holds enough water to harvest lettuce a few times before refilling. After the initial setup, the system can be ignored for weeks.
![[heliosalpha1.jpg | the hydroponics system from two angles. a black tub with yellow lid, covered in tin foil with six holes for plants. grow lights above suspended by metal shelving.]]

View File

@@ -27,7 +27,7 @@ Black: temperature (°C), blue: humidity (%)
Black: temperature (°C), blue: humidity (%)
![a graph](https://sensor-pics.dns.t0.vc/Misc_Temperature.png)
![a graph](https://sensor-pics.dns.t0.vc/Basement_Temperature.png)
Black: temperature (°C), blue: humidity (%)
@@ -61,6 +61,6 @@ The data gets collected by a central Python script that process and stores it in
## InfluxDB Regrets
My biggest regret was using InfluxDB. It's a stupid database that I wouldn't recommend it to anyone. I ran into timezone issues with `group by time()`. It assumes the column data type is an integer if your sensor happens to send it a whole number at first and it won't let you change it. Their docs are a confusing mess. They dropped the SQL-like InfluxQL syntax for querying with a pipeline-like syntax called Flux in version 2.0. Debian's repos seem to be staying with version 1.x though. You can only delete data by time ranges, not values. It also logs every single thing to `/var/log/syslog` and there's no easy way to disable it (completely). They shut down InfluxDB cloud in Belgium and [didn't warn customers](https://community.influxdata.com/t/getting-weird-results-from-gcp-europe-west1/30615/7) before deleting all their data.
My biggest regret was using InfluxDB. It's a stupid database that I wouldn't recommend it to anyone. I ran into timezone issues with `group by time()`. It assumes the column data type is an integer if your sensor happens to send it a whole number at first and it won't let you change it. Their docs are a confusing mess. They dropped the SQL-like InfluxQL syntax for querying with a pipeline-like syntax called Flux in version 2.0. Debian's repos seem to be staying with version 1.x though. You can only delete data by time ranges, not values. It also logs every single thing to `/var/log/syslog` and there's no easy way to disable it (completely). They shut down InfluxDB cloud in Belgium and [didn't warn customers](https://community.influxdata.com/t/getting-weird-results-from-gcp-europe-west1/30615/7) before deleting all their data. They changed schemas again in version 3.x and made useful features closed-source. Read the comments [here](https://news.t0.vc/TUTF/c#doctoboggan1750217574).
Just stick to SQLite or Postgres.

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: About my time volunteering with the University of Calgary Solar Car Team, where I designed a maximum power point tracker.
Image: solar2.jpg
Tags: feed
Guid: 7259d46cfc0440acba56d43d1749314b
I joined the University of Calgary Solar Car Team in my first semester for a chance to learn things, gain practical experience, and meet people that share my interests. The car was the top Canadian team in a 3000 km race from Darwin to Adelaide, Australia in 2011. We met up at a shop on campus every Saturday morning to work on the new Generation IV of the solar car.

View File

@@ -2,8 +2,9 @@ Title: Spaceport
Date: 2022-05-16
Category: Projects
Summary: Member portal for Calgary Protospace. It tracks dues, courses, training, access cards, and more.
Image: spaceport1.jpg
Image: spaceport1.png
Tags: feed
Guid: 3a7b5606eefc45cbad523d7548a864a5
[Spaceport](https://my.protospace.ca) is the member portal that I wrote for [[Protospace]], a makerspace that I frequent in Calgary. It is by far my largest project and the one I've spent the most time on. It has a database of all our members and tracks their transactions like dues and training fees. It allows members to sign up for classes and our instructors to teach courses. It also manages the access cards that members use to get into the building.

View File

@@ -0,0 +1,16 @@
Title: Three Drawer Cabinet
Date: 2025-03-18
Category: Creations
Summary: A three-drawer cabinet with a laser etched design.
Image: cabinet1.jpg
xTags: feed
I built a three drawer cabinet for storing embroidery machine supplies at my local makerspace, [[Protospace]]. It was built to fit under the machine, inside its metal stand.
![[cabinet1.jpg]]
The side features a laser etched flower design I found online. The cabinet is assembled using pocket screws because I wanted to learn how to use them.
Here's what it looks like installed:
![[cabinet2.jpg]]

View File

@@ -4,6 +4,7 @@ Category: Creations
Summary: A coffee table made out of wooden wine creates.
Image: wine3.jpg
Tags: feed
Guid: f0e36fec844d422eb7e0d626788c7b0a
My close friend Odai saw a simple coffee table design online that was built out of four wooden wine crates. They are quite cheap and available at any hardware store. We each wanted to make one so went and bought eight crates and some plywood to use as a base.

BIN
content/extra/button.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg fill="none" height="300" stroke="#000" stroke-linecap="square" stroke-width="28" width="300" xmlns="http://www.w3.org/2000/svg"><path d="M201.962 210a60 60 0 10-103.924-60l-50 86.603M98.038 210a60 60 0 10103.924-60l-50-86.603M150 120a60 60 0 100 120h100"/></svg>

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
content/media/cabinet1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 KiB

BIN
content/media/cabinet2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

BIN
content/media/distro1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

BIN
content/media/protovac1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 209 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 88 KiB

BIN
content/media/t0txt1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -2,9 +2,10 @@ Title: t-zero Services
Date: 2022-05-27
Category: Writing
Summary: A list of minimal microservices on my t0.vc domain.
Image: t0services1.svg
Wide: true
Short: 6
Tags: feed
Guid: 0440222b638144d5a6c376e1aaf43755
The t-zero Services are a collection of minimalist microservices that I host on my t0.vc domain. The letter "t" meaning me, and "0" meaning small. They're all meant to do exactly one thing reliably and stay online for as long as I can host them.
@@ -13,6 +14,8 @@ The smallest t-zero is the main domain itself at [t0.vc](https://t0.vc) and it s
The rest of the t-zero services are hosted on its subdomains.
![[t0services1.svg]]
## t0txt
The second t-zero I wrote was [[t0txt]], a pastebin that is compatible with the command line and `curl`. This allows me to very easily pipe text data into it and immediately get a URL that I can share. I copied the idea from [sprunge.us](http://sprunge.us/) which kept going down because he'd forget to pay his Google Cloud bill.

View File

@@ -2,12 +2,16 @@ Title: t0txt
Date: 2022-05-15
Category: Projects
Summary: Minimal command line pastebin. Allows you to upload text notes from a bash pipe or web browser.
Image: t0txt1.png
Tags: feed
Guid: a5fd74baa289491e9c4e931cdfcd2170
[t0txt](https://txt.t0.vc) is a minimalist pastebin. You can upload text notes from the command line by using a bash alias or by submitting text through the web form.
You can find the [source code](https://github.com/tannercollin/t0txt) on Github.
![[t0txt1.png]]
The pastes you upload take the form of [txt.t0.vc/IMLV](https://txt.t0.vc/IMLV), where they are identified by four unique capital letters. This makes it easy to memorize the URL while moving it between devices.
I wrote t0txt in July 2019 and plan to continue hosting it indefinitely. I use it quite often for sysadmin and automation work, so I'm committed to keeping it alive. Here's an example use case:

93
generate_thumbnails.py Normal file
View File

@@ -0,0 +1,93 @@
import logging
import os
import pprint
from pelican import signals
from PIL import Image
log = logging.getLogger(__name__)
THUMBNAIL_MAX_SIZE = 448
def generator_finalized(generator):
"""
Generates thumbnails for images specified in article metadata.
"""
output_path = generator.settings['OUTPUT_PATH']
content_path = generator.settings['PATH']
media_path = os.path.join(content_path, 'media')
thumb_dir = os.path.join(output_path, 'media', 'thumbnails')
if not os.path.exists(thumb_dir):
try:
os.makedirs(thumb_dir)
log.info(f"Created thumbnail directory: {thumb_dir}")
except OSError as e:
log.error(f"Could not create thumbnail directory {thumb_dir}: {e}")
return
for article in generator.articles:
if hasattr(article, 'image'):
image_path_rel_to_content = article.image
# image_path_rel_to_content is often like 'media/imagename.jpg'
# or just 'imagename.jpg' if it's directly in 'content/media/'
# and STATIC_PATHS includes 'media'.
# We assume article.image is a path relative to the 'content' folder.
source_image_full_path = os.path.join(media_path, image_path_rel_to_content)
if not os.path.exists(source_image_full_path):
log.warning(f"Source image not found for article '{article.slug}': {source_image_full_path}")
continue
image_filename = os.path.basename(image_path_rel_to_content)
thumb_path = os.path.join(thumb_dir, image_filename)
_, ext = os.path.splitext(image_filename)
ext_lower = ext.lower()
if ext_lower not in ['.jpg', '.jpeg', '.png']:
log.info(f"Skipping non-JPG/PNG image for article '{article.slug}': {image_filename}")
continue
try:
log.debug(f"Processing image: {source_image_full_path}")
img = Image.open(source_image_full_path)
# Preserve original format, handle potential conversion issues for some modes
original_format = img.format
if img.mode == 'P' and 'transparency' in img.info: # Palette mode with transparency
img = img.convert('RGBA')
elif img.mode not in ('RGB', 'RGBA', 'L'): # L is grayscale
log.info(f"Converting image {image_filename} from mode {img.mode} to RGB for thumbnailing.")
img = img.convert('RGB')
img.thumbnail((THUMBNAIL_MAX_SIZE, THUMBNAIL_MAX_SIZE))
save_kwargs = {}
if original_format:
save_kwargs['format'] = original_format
if original_format == 'JPEG':
save_kwargs['quality'] = 95 # Adjust quality for JPEGs
save_kwargs['optimize'] = True
elif original_format == 'PNG':
save_kwargs['optimize'] = True
img.save(thumb_path, **save_kwargs)
log.info(f"Generated thumbnail for '{article.slug}': {thumb_path}")
# Optionally, add thumbnail URL to article metadata if needed by templates
# This depends on how SITEURL and paths are structured.
# For now, just creating the file.
# article.thumbnail_url = f"{generator.settings.get('SITEURL', '')}/media/thumbs/{image_filename}"
except FileNotFoundError:
log.error(f"Image file not found: {source_image_full_path}")
except IOError as e:
log.error(f"Could not open or process image {source_image_full_path}: {e}")
except Exception as e:
log.error(f"An unexpected error occurred while processing {source_image_full_path}: {e}")
def register():
signals.article_generator_finalized.connect(generator_finalized)

View File

@@ -50,7 +50,7 @@
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#000000;stroke-width:0.264999"
style="fill:#1a1a1a;stroke-width:0.264999"
id="rect909"
width="212.19583"
height="121.17917"

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -47,7 +47,7 @@
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#000000;stroke-width:0.264999"
style="fill:#1a1a1a;stroke-width:0.264999"
id="rect909"
width="212.19583"
height="121.17917"

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -47,7 +47,7 @@
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#000000;stroke-width:0.264999"
style="fill:#1a1a1a;stroke-width:0.264999"
id="rect909"
width="212.19583"
height="121.17917"

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,31 @@
<mxfile host="app.diagrams.net" modified="2024-01-27T01:41:03.200Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0" etag="OL6IpkjM6-81aN4SoHv_" version="23.0.2" type="device">
<diagram name="Page-1" id="5-Wfztd7QZj7HGqn7hWo">
<mxGraphModel dx="928" dy="525" 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="xaIvDc9CACh0lx6NfuLJ-1" value="" style="shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;image=https://upload.wikimedia.org/wikipedia/commons/4/4a/Debian-OpenLogo.svg;" vertex="1" parent="1">
<mxGeometry x="210" y="210" width="109" height="144" as="geometry" />
</mxCell>
<mxCell id="xaIvDc9CACh0lx6NfuLJ-2" value="" style="shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;image=https://upload.wikimedia.org/wikipedia/commons/9/9e/UbuntuCoF.svg;" vertex="1" parent="1">
<mxGeometry x="580" y="212" width="140" height="140" as="geometry" />
</mxCell>
<mxCell id="xaIvDc9CACh0lx6NfuLJ-3" value="" style="shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;image=https://upload.wikimedia.org/wikipedia/commons/3/35/Tux.svg;" vertex="1" parent="1">
<mxGeometry x="380" y="212" width="134" height="134" as="geometry" />
</mxCell>
<mxCell id="xaIvDc9CACh0lx6NfuLJ-4" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=5;strokeColor=#808080;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="307" as="sourcePoint" />
<mxPoint x="370" y="257" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="xaIvDc9CACh0lx6NfuLJ-5" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=5;strokeColor=#808080;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="520" y="304" as="sourcePoint" />
<mxPoint x="570" y="254" as="targetPoint" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -0,0 +1,31 @@
<mxfile host="app.diagrams.net" modified="2024-01-27T01:25:37.000Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0" etag="WvQ24rCeBP-gvlG-6t5J" version="23.0.2" type="device">
<diagram name="Page-1" id="dd472eb7-4b8b-5cd9-a60b-b15522922e76">
<mxGraphModel dx="1114" dy="630" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="374e34682ed331ee-1" value="&lt;font style=&quot;font-size: 36px;&quot;&gt;t0.vc&lt;/font&gt;" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;dashed=0;comic=0;fontFamily=Verdana;fontSize=22;fontColor=default;fillColor=#f5f5f5;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="290" y="180" width="450" height="180" as="geometry" />
</mxCell>
<mxCell id="45236fa5f4b8e91a-1" value="t0txt" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;dashed=0;comic=0;fontFamily=Verdana;fontSize=22;fontColor=default;fillColor=#f5f5f5;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="430" y="120" width="160" height="100" as="geometry" />
</mxCell>
<mxCell id="45236fa5f4b8e91a-2" value="&lt;div&gt;t0url&lt;/div&gt;" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;dashed=0;comic=0;fontFamily=Verdana;fontSize=22;fontColor=default;fillColor=#f5f5f5;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="240" y="285" width="160" height="100" as="geometry" />
</mxCell>
<mxCell id="45236fa5f4b8e91a-3" value="t0reg" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;dashed=0;comic=0;fontFamily=Verdana;fontSize=22;fontColor=default;fillColor=#f5f5f5;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="620" y="280" width="160" height="110" as="geometry" />
</mxCell>
<mxCell id="45236fa5f4b8e91a-6" value="t0pic" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;dashed=0;comic=0;fontFamily=Verdana;fontSize=22;fontColor=default;fillColor=#f5f5f5;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="620" y="150" width="160" height="100" as="geometry" />
</mxCell>
<mxCell id="45236fa5f4b8e91a-7" value="t0sig" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;dashed=0;comic=0;fontFamily=Verdana;fontSize=22;fontColor=default;fillColor=#f5f5f5;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="435" y="310" width="160" height="100" as="geometry" />
</mxCell>
<mxCell id="45236fa5f4b8e91a-8" value="t0dns" style="ellipse;whiteSpace=wrap;html=1;rounded=0;shadow=0;dashed=0;comic=0;fontFamily=Verdana;fontSize=22;fontColor=default;fillColor=#f5f5f5;strokeColor=#666666;" parent="1" vertex="1">
<mxGeometry x="240" y="160" width="160" height="100" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -2,6 +2,11 @@
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
import sys
sys.path.append('.')
import generate_thumbnails
PATH = 'content'
TIMEZONE = 'Canada/Mountain'
@@ -33,6 +38,7 @@ MARKDOWN = {
PLUGINS = [
'obsidian',
'linkclass',
'generate_thumbnails',
]
STATIC_PATHS = ['media', 'extra']

View File

@@ -2,6 +2,11 @@
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
import sys
sys.path.append('.')
import generate_thumbnails
PATH = 'content'
TIMEZONE = 'Canada/Mountain'
@@ -33,6 +38,7 @@ MARKDOWN = {
PLUGINS = [
'obsidian',
'linkclass',
'generate_thumbnails',
]
STATIC_PATHS = ['media', 'extra']

View File

@@ -3,6 +3,11 @@
from __future__ import unicode_literals
import os
import sys
sys.path.append('.')
import swap_guids
AUTHOR = 'Tanner'
SITENAME = 'Tanner\'s Site (t0.vc)'
SITEURL = 'https://t0.vc'
@@ -13,7 +18,7 @@ TIMEZONE = 'Canada/Mountain'
DEFAULT_LANG = 'en'
# Feed generation is usually not desired when developing
#FEED_MAX_ITEMS = 15
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
@@ -21,8 +26,8 @@ AUTHOR_FEED_ATOM = None
AUTHOR_FEED_RSS = None
#TAG_FEED_ATOM = 'feeds/{slug}/atom.xml'
#TAG_FEED_RSS = 'feeds/{slug}/rss.xml'
TAG_FEED_ATOM = 'test-atom.xml'
TAG_FEED_RSS = 'test-rss.xml'
TAG_FEED_ATOM = 'atom.xml'
TAG_FEED_RSS = 'rss.xml'
RSS_FEED_SUMMARY_ONLY = False # include full content
DEFAULT_PAGINATION = False
@@ -43,6 +48,7 @@ MARKDOWN = {
PLUGINS = [
'obsidian',
'linkclass',
'swap_guids',
]
STATIC_PATHS = ['media', 'extra', 'text']

77
swap_guids.py Normal file
View File

@@ -0,0 +1,77 @@
import logging
import pprint
import uuid
from pelican import signals
log = logging.getLogger(__name__)
def modify_feed(context, feed):
articles = {}
for article in context['articles']:
if article.title in articles:
raise Exception(f"Duplicate article title found: {article.title}")
articles[article.title] = article
for item in feed.items:
item_title = item['title']
article = articles.get(item_title)
if not article:
raise Exception(f"Article not found for title: {item_title}")
if not hasattr(article, 'guid') or not article.guid:
log.info(f"Article '{article.title}' ({article.source_path}) is missing a guid. Generating and embedding one.")
new_guid_str = uuid.uuid4().hex
guid_text_to_embed = f"Guid: {new_guid_str}"
source_path = article.source_path
# Ensure article object has the _content attribute
if not hasattr(article, '_content'):
log.error(f"Article '{article.title}' does not have '_content' attribute. Cannot embed Guid into source file.")
raise Exception(f"Cannot find raw content for article '{article.title}' to embed Guid.")
# Read the original file content.
# Python's open() in text mode uses universal newlines by default, converting \r\n and \r to \n.
# Pelican's MarkdownReader also provides article._content with \n newlines.
try:
with open(source_path, 'r', encoding='utf-8') as f:
current_body_content = f.read()
except Exception as e:
log.error(f"Failed to read original content from '{source_path}': {e}")
raise
# Split this body content to find its first paragraph.
# Paragraphs in Markdown are separated by one or more blank lines (\n\n).
body_parts = current_body_content.split('\n\n', 1)
first_paragraph_of_body = body_parts[0]
rest_of_body_content = body_parts[1] if len(body_parts) > 1 else ""
# Append the Guid text to the end of the first paragraph of the body.
# .rstrip() removes any trailing whitespace/newlines from the paragraph itself before appending.
modified_first_paragraph_of_body = first_paragraph_of_body.rstrip() + '\n' + guid_text_to_embed
# Construct the full new file content by combining the original metadata part and the new body.
# This preserves the original metadata block verbatim (including comments, formatting, and original newline characters if any within it,
# as metadata_part_from_file is a direct slice from original_file_content_universal_newlines which has \n newlines).
full_new_content = modified_first_paragraph_of_body + '\n\n' + rest_of_body_content
try:
with open(source_path, 'w', encoding='utf-8') as f:
f.write(full_new_content)
log.info(f"Successfully wrote updated content with embedded Guid to '{source_path}'.")
except Exception as e:
log.error(f"Failed to write updated content to '{source_path}': {e}")
raise # Re-raise the exception to halt processing if file write fails
# Set article.guid for the current Pelican run, so it's used for the feed item
article.guid = new_guid_str
log.debug(f"Set in-memory article.guid = '{new_guid_str}' for '{article.title}'.")
item['unique_id'] = article.guid
def register():
signals.feed_generated.connect(modify_feed)

View File

@@ -7,6 +7,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
*, *::before, *::after {
box-sizing: border-box;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
a.external {
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath fill='%23fff' stroke='%23000' d='M1.5 4.518h5.982V10.5H1.5z'/%3E%3Cpath fill='%23000' d='M5.765 1H11v5.39L9.427 7.937l-1.31-1.31L5.393 9.35l-2.69-2.688 2.81-2.808L4.2 2.544z'/%3E%3Cpath fill='%23fff' d='m9.995 2.004.022 4.885L8.2 5.07 5.32 7.95 4.09 6.723l2.882-2.88-1.85-1.852z'/%3E%3C/svg%3E%0A");
background-position: center right;
@@ -66,7 +72,8 @@
<body>
<div class="content">
<p><a href="/">Home | t0.vc</a></p>
<p><a href="/">Home (t0.vc)</a></p>
<p><a href="/rss.xml">RSS Feed</a> | <a href="/atom.xml">Atom Feed</a></p>
{% block content %}
{% endblock %}
</div>

View File

@@ -1,7 +1,7 @@
<link rel=icon href=data:><meta name=viewport content=width=1%><pre style=font:unset>Hi, I'm <a href=a>Tanner</a>! I like <a href=r>plants</a>, bots, Python,
Debian, coffee, <a href=s>sensors</a>, and makerspaces
Debian, climbing, <a href=s>sensors</a>, and makerspaces
Please sign my <a href=g>Guest Book</a>
Please sign my <a href=g>Guestbook</a>
site@t0.vc
@@ -9,14 +9,14 @@ CV
Firmware Engineer - Cabana Blockchain, '18-
Lead Hardware Engineer - Critical Control, '16-'18
Electrical Engineer - Opener Aero, '16-'16
Electrical Engineer - Pivotal Aero, '16-'16
BSc Electrical Engineering - University of Calgary
Blog
<a href=6>Makerspace Tours
<a href=p>Bypassing ISP Blocked Ports
<a href=j>Japan Photography
<a href=2>ChatGPT Peanut Butter Cookies
<a href=h>Hydroponics
<a href=x>[more]</a>

View File

@@ -16,6 +16,11 @@
{% endfor %}
<p>
<a class="external" href="https://github.com/tannercollin/sharenote-py" target="_blank" rel="noreferrer noopener">sharenote-py</a><br/>
An implementation of the Obsidian Share Note API server in Python.
</p>
<p>
<a class="external" href="https://github.com/tannercollin/standardnotes-fs" target="_blank" rel="noreferrer noopener">standardnotes-fs</a><br/>
A filesystem that mounts your Standard Notes account as a directory of text files that you can edit.

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="802"
height="458"
viewBox="0 0 212.19583 121.17917"
version="1.1"
id="svg5"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="logo-path.svg"
inkscape:export-filename="logo-export.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.52964612"
inkscape:cx="769.38164"
inkscape:cy="262.43938"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="text911" />
<defs
id="defs2">
<rect
x="27.741714"
y="556.36462"
width="592.50952"
height="240.91045"
id="rect913" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#1a1a1a;stroke-width:0.264999"
id="rect909"
width="212.19583"
height="121.17917"
x="0"
y="0" />
<g
aria-label="tanner█"
transform="matrix(0.54302647,0,0,0.54302647,-10.487989,-292.51606)"
id="text911"
style="font-size:76.3777px;line-height:1.25;white-space:pre;shape-inside:url(#rect913);display:inline;fill:#ffffff">
<path
d="m 50.64058,570.31312 v 11.85942 h 15.588808 v 5.33302 H 50.64058 v 22.67463 q 0,4.62443 1.752809,6.45182 1.752808,1.8274 6.116183,1.8274 h 7.719816 v 5.48219 h -8.391105 q -7.719816,0 -10.889789,-3.09539 -3.169973,-3.09538 -3.169973,-10.66602 V 587.50556 H 32.627675 v -5.33302 h 11.150846 v -11.85942 z"
style="font-family:monospace;-inkscape-font-specification:monospace"
id="path287" />
<path
d="m 99.905688,602.94519 h -2.274922 q -6.004301,0 -9.062393,2.12575 -3.020798,2.08845 -3.020798,6.26535 0,3.76668 2.274922,5.85513 2.274922,2.08845 6.302652,2.08845 5.668658,0 8.913221,-3.91585 3.24456,-3.95314 3.28185,-10.88978 v -1.52905 z m 13.313882,-2.83433 v 23.83074 h -6.89935 v -6.19077 q -2.20033,3.72938 -5.55677,5.51948 -3.319153,1.75281 -8.092759,1.75281 -6.37724,0 -10.181207,-3.58021 -3.803968,-3.61749 -3.803968,-9.65909 0,-6.97394 4.661725,-10.59144 4.699019,-3.6175 13.761412,-3.6175 h 9.211567 v -1.08152 q -0.0373,-4.99737 -2.53598,-7.23499 -2.49868,-2.27493 -7.98087,-2.27493 -3.505617,0 -7.085822,1.00694 -3.580204,1.00693 -6.97394,2.94621 v -6.86206 q 3.803967,-1.45446 7.272291,-2.16304 3.505617,-0.74588 6.787471,-0.74588 5.18384,0 8.83863,1.52905 3.69209,1.52904 5.96701,4.58713 1.41716,1.86469 2.01386,4.62444 0.5967,2.72244 0.5967,8.20463 z"
style="font-family:monospace;-inkscape-font-specification:monospace"
id="path289" />
<path
d="m 158.90448,598.0597 v 25.8819 h -6.89936 v -25.8819 q 0,-5.63136 -1.97657,-8.27922 -1.97657,-2.64786 -6.19077,-2.64786 -4.8109,0 -7.42146,3.43103 -2.57328,3.39373 -2.57328,9.77097 v 23.60698 h -6.86205 v -41.76906 h 6.86205 v 6.26536 q 1.8274,-3.5802 4.96008,-5.4076 3.13268,-1.86469 7.42147,-1.86469 6.37724,0 9.50991,4.2142 3.16998,4.17691 3.16998,12.67989 z"
style="font-family:monospace;-inkscape-font-specification:monospace"
id="path291" />
<path
d="m 204.88774,598.0597 v 25.8819 h -6.89935 v -25.8819 q 0,-5.63136 -1.97658,-8.27922 -1.97657,-2.64786 -6.19077,-2.64786 -4.8109,0 -7.42146,3.43103 -2.57327,3.39373 -2.57327,9.77097 v 23.60698 h -6.86206 v -41.76906 h 6.86206 v 6.26536 q 1.82739,-3.5802 4.96007,-5.4076 3.13268,-1.86469 7.42147,-1.86469 6.37724,0 9.50992,4.2142 3.16997,4.17691 3.16997,12.67989 z"
style="font-family:monospace;-inkscape-font-specification:monospace"
id="path293" />
<path
d="m 253.14591,601.34156 v 3.35644 h -29.72316 v 0.22376 q 0,6.82477 3.54291,10.55415 3.5802,3.72938 10.06932,3.72938 3.28186,0 6.86206,-1.04423 3.58021,-1.04423 7.64523,-3.16997 v 6.82476 q -3.91585,1.60364 -7.57064,2.38681 -3.6175,0.82046 -7.01123,0.82046 -9.73368,0 -15.21587,-5.81783 -5.48219,-5.85513 -5.48219,-16.11093 0,-9.99473 5.37031,-15.96174 5.3703,-5.96701 14.32081,-5.96701 7.98088,0 12.56802,5.4076 4.62443,5.4076 4.62443,14.76835 z m -6.86206,-2.01387 q -0.14918,-6.04159 -2.87163,-9.17427 -2.68515,-3.16998 -7.75711,-3.16998 -4.96007,0 -8.16734,3.28186 -3.20726,3.28185 -3.80397,9.09968 z"
style="font-family:monospace;-inkscape-font-specification:monospace"
id="path295" />
<path
d="m 300.73279,590.78741 q -2.20034,-1.71551 -4.47526,-2.49868 -2.27492,-0.78317 -4.99737,-0.78317 -6.41453,0 -9.80827,4.02773 -3.39373,4.02773 -3.39373,11.63566 v 20.77265 h -6.89936 v -41.76906 h 6.89936 v 8.16735 q 1.71551,-4.43797 5.25842,-6.78748 3.58021,-2.3868 8.46569,-2.3868 2.53598,0 4.73632,0.634 2.20033,0.63399 4.2142,1.97657 z"
style="font-family:monospace;-inkscape-font-specification:monospace"
id="path297" />
<path
d="m 302.89585,643.03602 v -90.73581 h 47.475 v 90.73581 z"
style="font-family:monospace;-inkscape-font-specification:monospace"
id="path299" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -21,30 +21,28 @@
<div class="container">
<div class="logo">
<a href="/" aria-label="Return home">
<svg version="1.1" viewBox="0 0 212.2 121.18" xmlns="http://www.w3.org/2000/svg">
<rect width="212.2" height="121.18" stroke-width=".265" style="fill:#1a1a1a" />
<g transform="matrix(.54303 0 0 .54303 -10.488 -292.52)" fill="#fff" style="shape-inside:url(#rect913);white-space:pre">
<path d="m50.641 570.31v11.859h15.589v5.333h-15.589v22.675q0 4.6244 1.7528 6.4518 1.7528 1.8274 6.1162 1.8274h7.7198v5.4822h-8.3911q-7.7198 0-10.89-3.0954-3.17-3.0954-3.17-10.666v-22.675h-11.151v-5.333h11.151v-11.859z"/>
<path d="m99.906 602.95h-2.2749q-6.0043 0-9.0624 2.1258-3.0208 2.0884-3.0208 6.2654 0 3.7667 2.2749 5.8551t6.3027 2.0884q5.6687 0 8.9132-3.9158 3.2446-3.9531 3.2818-10.89v-1.529zm13.314-2.8343v23.831h-6.8994v-6.1908q-2.2003 3.7294-5.5568 5.5195-3.3192 1.7528-8.0928 1.7528-6.3772 0-10.181-3.5802-3.804-3.6175-3.804-9.6591 0-6.9739 4.6617-10.591 4.699-3.6175 13.761-3.6175h9.2116v-1.0815q-0.0373-4.9974-2.536-7.235-2.4987-2.2749-7.9809-2.2749-3.5056 0-7.0858 1.0069-3.5802 1.0069-6.9739 2.9462v-6.8621q3.804-1.4545 7.2723-2.163 3.5056-0.74588 6.7875-0.74588 5.1838 0 8.8386 1.529 3.6921 1.529 5.967 4.5871 1.4172 1.8647 2.0139 4.6244 0.5967 2.7224 0.5967 8.2046z"/>
<path d="m158.9 598.06v25.882h-6.8994v-25.882q0-5.6314-1.9766-8.2792t-6.1908-2.6479q-4.8109 0-7.4215 3.431-2.5733 3.3937-2.5733 9.771v23.607h-6.862v-41.769h6.862v6.2654q1.8274-3.5802 4.9601-5.4076 3.1327-1.8647 7.4215-1.8647 6.3772 0 9.5099 4.2142 3.17 4.1769 3.17 12.68z"/>
<path d="m204.89 598.06v25.882h-6.8994v-25.882q0-5.6314-1.9766-8.2792-1.9766-2.6479-6.1908-2.6479-4.8109 0-7.4215 3.431-2.5733 3.3937-2.5733 9.771v23.607h-6.8621v-41.769h6.8621v6.2654q1.8274-3.5802 4.9601-5.4076 3.1327-1.8647 7.4215-1.8647 6.3772 0 9.5099 4.2142 3.17 4.1769 3.17 12.68z"/>
<path d="m253.15 601.34v3.3564h-29.723v0.22376q0 6.8248 3.5429 10.554 3.5802 3.7294 10.069 3.7294 3.2819 0 6.8621-1.0442 3.5802-1.0442 7.6452-3.17v6.8248q-3.9158 1.6036-7.5706 2.3868-3.6175 0.82046-7.0112 0.82046-9.7337 0-15.216-5.8178-5.4822-5.8551-5.4822-16.111 0-9.9947 5.3703-15.962 5.3703-5.967 14.321-5.967 7.9809 0 12.568 5.4076 4.6244 5.4076 4.6244 14.768zm-6.8621-2.0139q-0.14918-6.0416-2.8716-9.1743-2.6852-3.17-7.7571-3.17-4.9601 0-8.1673 3.2819-3.2073 3.2818-3.804 9.0997z"/>
<path d="m300.73 590.79q-2.2003-1.7155-4.4753-2.4987t-4.9974-0.78317q-6.4145 0-9.8083 4.0277-3.3937 4.0277-3.3937 11.636v20.773h-6.8994v-41.769h6.8994v8.1674q1.7155-4.438 5.2584-6.7875 3.5802-2.3868 8.4657-2.3868 2.536 0 4.7363 0.634 2.2003 0.63399 4.2142 1.9766z"/>
<path d="m302.9 643.04v-90.736h47.475v90.736z"/>
</g>
</svg>
<img width="160" height="91" src="/theme/logo-path-export.svg" />
</a>
</div>
{% block content %}
{% endblock %}
<p>&nbsp;</p>
</div>
<p class="footer">
<span class="wname">Webring:</span> <a href="https://nice42q.de/" class="wprev">&lt; Previous</a> | <a href="https://webring.t0.vc" class="windex">Index</a> | <a href="https://udia.ca" class="wnext">Next &gt;</a>
</p>
<p class="xxiivv">
<a href="https://webring.xxiivv.com/#tanner" target="_blank" rel="noopener">
<img src="/extra/xxiivv-icon.black.svg" alt="XXIIVV webring"/>
</a>
</p>
<p class="footer">
© 20122022 Tanner
© 20122025 Tanner
</p>
</body>
</html>

View File

@@ -2,14 +2,18 @@
{% block head %}
{{ super() }}
<meta name="description" content="Hi, I'm Tanner! I like home automation, sensors, bots, Python, Debian, coffee, and makerspaces." />
<meta name="summary" content="Hi, I'm Tanner! I like home automation, sensors, bots, Python, Debian, coffee, and makerspaces." />
<meta name="description" content="Hi, I'm Tanner! I like home automation, sensors, bots, Python, Debian, climbing, and makerspaces." />
<meta name="summary" content="Hi, I'm Tanner! I like home automation, sensors, bots, Python, Debian, climbing, and makerspaces." />
{% endblock %}
{% block content %}
<div class="content index">
<p>
Hi, I'm <a href="/about">Tanner</a>! I like <a href="/secret-garden">growing plants</a>, home automation, <a href="/sensors">sensors</a>, bots, Python, Debian, coffee, and makerspaces.
Hi, I'm <a href="/about">Tanner</a>! I like <a href="/secret-garden">growing plants</a>, home automation, <a href="/sensors">sensors</a>, bots, Python, Debian, climbing, and makerspaces.
</p>
<p>
Please sign my <a href=g>Guestbook</a>!
</p>
<p>
@@ -31,6 +35,13 @@
{% endfor %}
<h3>
<a class="external" href="https://github.com/tannercollin/sharenote-py" target="_blank" rel="noreferrer noopener">sharenote-py</a>
</h3>
<div class="summary">
<p>An implementation of the Obsidian Share Note API server in Python.</p>
</div>
<h3>
<a class="external" href="https://github.com/tannercollin/standardnotes-fs" target="_blank" rel="noreferrer noopener">standardnotes-fs</a>
</h3>
@@ -42,10 +53,17 @@
<p>Sometimes I create art or interactive tech.</p>
<div class="creations">
{% for article in articles_page.object_list if article.category.name == 'Creations' %}
<h3><a href="{{ article.url }}">{{ article.title }}</a></h3>
<div class="creation">
<a href="{{ article.url }}">
<img src="media/thumbnails/{{ article.image }}" alt="{{ article.summary|striptags }}" />
{{ article.title }}
</a>
</div>
{% endfor %}
</div>
<h2>Writing</h2>
@@ -61,7 +79,7 @@
<ul>
<li>Firmware Engineer at <a class="external" href="https://cabanablockchain.com" target="_blank" rel="noreferrer noopener">Cabana Blockchain</a>, 2018</li>
<li>Lead Hardware Engineer at <a class="external" href="https://criticalcontrol.com/" target="_blank" rel="noreferrer noopener">Critical Control</a>, 20162018</li>
<li>Electrical Engineer at <a class="external" href="https://www.opener.aero/" target="_blank" rel="noreferrer noopener">Opener Aero</a>, 20162016</li>
<li>Electrical Engineer at <a class="external" href="https://pivotal.aero/" target="_blank" rel="noreferrer noopener">Pivotal Aero</a>, 20162016</li>
<li>Electrical Engineer Intern at <a class="external" href="https://www.pason.com/" target="_blank" rel="noreferrer noopener">Pason Systems</a>, 20142015</li>
<li>BSc. Electrical Engineering from University of Calgary</li>
</ul>

View File

@@ -1,8 +1,16 @@
*, *::before, *::after {
box-sizing: border-box;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
html {
overflow-y: scroll;
}
body {
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: serif;
}
@@ -27,6 +35,40 @@
font: 1.1rem/1.5 serif;
}
.index .creations {
text-align: center;
}
.index .creations .creation {
display: inline-block;
vertical-align: top;
}
.index .creations .creation a {
border-bottom: none;
}
.creation > a {
display: inline-block;
vertical-align: top;
margin: 1rem;
margin-bottom: 1.5rem;
font: 1.1rem/1.5 serif;
width: 14rem;
text-align: center;
}
.creations .creation img {
max-width: 14rem;
max-height: 14rem;
width: auto;
margin: 0 auto;
display: block;
}
.source {
font: 0.9rem/1.5 serif;
}
@@ -62,6 +104,19 @@
text-align: center;
}
.xxiivv {
text-align: center;
}
.xxiivv img {
width: 30px;
height: 30px;
}
.xxiivv a {
border-bottom: none;
}
.toc {
float: right;
padding: 0.75rem;
@@ -100,7 +155,7 @@
font: 1.1rem/1.5 serif;
}
.content img:not(.floated) {
.content img {
width: 100%;
max-width: 36rem;
height: auto;
@@ -108,11 +163,6 @@
margin: 0 auto;
}
.content .floated {
float: left;
margin-right: 1rem;
}
.content.index {
margin-top: 3rem;
}
@@ -220,4 +270,8 @@
.nofilter img {
filter: initial !important;
}
.xxiivv img {
filter: invert(1);
}
}