-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrss.xml
1454 lines (1066 loc) · 153 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>Dizzard's RSS Feed</title><link>http://dizzard.net/</link><image><url>http://dizzard.net/images/favicon.png</url><title>Dizzard's RSS Feed</title><link>http://dizzard.net/</link></image><description>Latest Articles from Max Omdal (dizzard.net)</description><lastBuildDate>Wed, 29 Jan 2025 00:00:00 -0000</lastBuildDate><item><title>I Made a Sign</title><link>http://dizzard.net/articles/signage/article.html</link><description><![CDATA[<h1>I Made a Sign</h1>
<p><img src="http://dizzard.net/images/sign.jpg" alt="" /></p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/cvlFr1VB9fk?si=gnbmLnTdTNfr9gea" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Wed, 29 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">33</guid></item><item><title>Oh Blogging, What a Bother!</title><link>http://dizzard.net/articles/oh_blogging_what_a_bother/article.html</link><description><![CDATA[<h1>Oh Blogging, What a Bother!</h1>
<p>Sometimes, I'm not so certain if I enjoy blogging, or if I enjoy the <em>idea</em> of blogging. I know I like making things, both physical and digital, I know I like reading blogs, so why would I <em>not</em> enjoy blogging? I'm not sure, but I'm starting to question it. Maybe what I really need is time away from screens after a day job spent in front of them. It's also been an awfully busy start to 2025, with some stressful national politics.</p>
<p>Maybe what I need is to get off the internet altogether.</p>
<p>I'm just not sure. And maybe that's OK too! I'm still here writing something, aren't I? What I really think I would like to have on this blog is documentation of the <em>in-real-life</em> things I've built. I see two great benefits to this. The first being a personal log of my creations, a way to reflect and consider how the quality and complexity of projects I take on changes and grows. The other is being able to hopefully inspire others to build things themselves! I see a lot of blogs about digital technology, photography, current events, and sometimes art. What about the makerspace movement? What about everyone's own personal <a href="https://hackaday.com/">Hackadays</a>. That's the type of blog I'd like to read more of. There's plenty of YouTube channels in that vein, but I don't need any more video in my life! I don't need to tell you, dear reader, that YouTube and blogging are far from the same thing.</p>
<p>Do you have any blogs to recommend to me?</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Wed, 29 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">32</guid></item><item><title>Blog Challenge</title><link>http://dizzard.net/articles/blog_challenge/article.html</link><description><![CDATA[<p>I was inspired by other bloggers to share my answers to the <a href="https://blog.avas.space/bear-blog-challenge/">bear blog questions</a> (<a href="https://kevquirk.com/blog/blog-questions-challenge">Adapted by Kev</a>). So here are my responses.</p>
<h2>Why did you start blogging in the first place?</h2>
<p>I started my website in my last year of college as a way to place all my work from university and my brief stint in research in one place, so I can look back at and remember what I did. <a href="https://www.youtube.com/watch?v=VQgD2DCBnjQ">Here</a> are <a href="https://dl.acm.org/doi/10.1145/3488542">some</a> <a href="https://dizzard.net/articles/particle_simulation/particle_simulation.html">examples</a>. I don't do as much technically interesting things anymore, but as I started reading more of other's blogs, and getting on the <a href="https://fosstodon.org/@mjomdal">fediverse</a>, I became interested in sharing the projects I make, experiences I've had and lessons I've learned. In 2025, I've made <a href="https://dizzard.net/articles/resolutions/article.html">a resolution</a> to really ramp up my blogging.</p>
<p>As another bonus, having blogs I can share with family and friends is really nice. If I want to <a href="https://dizzard.net/articles/chest_of_drawers/article.html">share a project</a>, or <a href="https://dizzard.net/articles/detergent/article.html">how to make detergent</a>, I just send a link to my blog post. It's a lot more fun than just sending a really long text.</p>
<h2>What platform are you using to manage your blog and why did you choose it?</h2>
<p>Everything is hosted on Github Pages. I've cut my teeth at <a href="https://sequoiafabrica.org">Sequoia Fabrica</a> self-hosting sites, and I also self-host a bunch of services behind Tailscale, So I'm sure I could figure out how to use a cloud provider to spin up my own hosting. But Github Pages is so damn easy, and it just works, so I don't see myself changing that for a while.</p>
<p>All posts are published to my blog through git. I push a new commit, and a Github action builds and pushes changes to another branch that holds just the static content for the site. Everything is in a public repository <a href="https://github.com/momja/momja.github.io">here</a>. There's a python script I wrote that will take all the blog posts, convert from Markdown to HTML, and copy them to a static directory. There are some improvements yet to be made, and I'll incrementally revise it. I'm not interested in an overhaul. Even though I know there's some issues, it works well enough. All the shiny cool tools like Hugo, 11ty, BearBlog, etc. seem like a lot of fun, but I kind of like knowing <em>all</em> the steps from start to finish of how my blog gets published. Since the site is built as a bit of a playground for me, I prefer running it all myself.</p>
<h2>Have you blogged on other platforms before?</h2>
<p>No. I published one article on Medium which is rather embarrassing and should be deleted. </p>
<h2>How do you write your posts? For example, in a local editing tool, or in a panel/dashboard that's part of your blog?</h2>
<p>I write either in Obsidian, or Vim. Usually, the first draft is in Obsidian, I'll copy and paste to my site's directory, and edit in Vim. It would be cool to build a custom web portal to publish to my site. My least favorite part of the workflow now is adding images because everything is placed in one resources folder, then I need to provide the markdown file with the correct relative path, and type the name of the image in correctly. It seems simple, but I always mess this part up. </p>
<h2>When do you feel most inspired to write?</h2>
<p>I don't have much written down on this site as of now. I'm trying to change that. I like writing in the morning before work sometimes, but usually it's just when I think of something to blog about. I really enjoy sharing projects big and small. When I finish a project, I like to share it.</p>
<h2>Do you publish immediately after writing, or do you let it simmer a bit as a draft?</h2>
<p>No editing, no simmering, just press GO.</p>
<h2>What's your favorite post on your blog?</h2>
<p><a href="https://dizzard.net/articles/favorite_things_2024/article.html">My Favorite Things of 2024</a>. I wish more people would publish content like this. Stuff that's not YouTube affiliate, but actual tools, books, software that you liked using over the last year or so.</p>
<h2>Any future plans for your blog? Maybe a redesign, a move to another platform, or adding a new feature?</h2>
<p>No redesigns in my future. If anything, I want to rip out some of the original stylistic decisions I've made so it is even more bare bones. The only future plan that I care about this year is publishing more content.</p>
<p>Oh! I suppose it <em>would</em> be nice to publish from mobile. Rigging a solution for that might be on my 2025 roadmap.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Mon, 06 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">31</guid></item><item><title>10 Minute Mini Corkboard Enamel Pin Display</title><link>http://dizzard.net/articles/10_minute_corkboard/article.html</link><description><![CDATA[<h1>10 Minute Mini Corkboard Enamel Pin Display</h1>
<p>I was waiting for paint to dry, so to keep my hands busy I made this little cork board to display some of my and my partner's pins (yeah I know, it's pretty empty as-is). It's just cork adhered to a hardboard backing. I cut it out on the bandsaw.</p>
<p><img src="http://dizzard.net/images/corkboard.jpg" alt="" /></p>
<p>Some related links I found:</p>
<ul>
<li>Cork, by all accounts I have found from 5 minutes of searching, is a pretty sustainable material <a href="https://earthfriendlytips.com/is-cork-eco-friendly-everything-you-need-to-know/">link</a></li>
<li>Cork is also derived from the bark of the <a href="https://en.wikipedia.org/wiki/Quercus_suber">Quercus Suber</a> tree, which is a type of Oak tree!</li>
<li>Harvesting the cork does not require cutting down the tree. The bark is removed, and the tree remains to continue growing</li>
<li>Interesting Business Insider <a href="https://www.businessinsider.com/how-the-worlds-largest-cork-company-makes-wine-stoppers-2024-9?op=1">video</a> on Cork Production in Portugal, the biggest producer of cork.</li>
</ul>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sun, 05 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">30</guid></item><item><title>A Sneak Peak at a Sign</title><link>http://dizzard.net/articles/sequoia_sign/article.html</link><description><![CDATA[<h1>A Sneap Peak at a Sign</h1>
<p>I've been working on an entrance sign for <a href="https://sequoiafabrica.org">Sequoia Fabrica</a>, and here is an early preview. There's some internals to this sign that will make the final project sing, but it's not quite ready yet. I think I'll do a full write up about it once it is complete.</p>
<p><img src="http://dizzard.net/images/sequoia_sign.jpg" alt="" /></p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sat, 04 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">29</guid></item><item><title>Using Style Transfer and CNC to Produce Printblocks</title><link>http://dizzard.net/articles/golden_gate_style_transfer/article.html</link><description><![CDATA[<h1>Using Style Transfer to CNC Produce Printblocks</h1>
<p>In college, I wrote a final paper about <a href="https://dizzard.net/images/Final__CycleGAN_on_Image_Transfer.pdf">Generative Adversarial Nets for Artistic Image-to-Image Translation</a> where I explored how GANs can be used for image style transfer. In my experiments, I never got great results.</p>
<p><img src="http://dizzard.net/images/gan_example.jpg" alt="../images/gan_example.png" /></p>
<p>That college paper was much more down in the dirt with the model than what I've tried recently. For an event, I needed to create a bunch of printmaking blocks very quickly, and they needed to be centered around San Francisco Landmarks. To accomplish this, I took a look at diffusion models. Using Dall-E 3, I was able to apply a woodblock print style transfer, and with some further processing, I could vectorize the output and send it to a CNC machine to manufacture the stamp. In the example below, it shows the original public domain image, the result of style transfer on the image, then the linoleum block, and the final print.</p>
<p><img src="http://dizzard.net/images/golden_gate_process.jpg" alt="" /></p>
<h2>The Tools</h2>
<p>I used OpenAI's ChatGPT interface to apply style transfer (I know, this is not to be confused with "Neural Style Transfer" which is a specific set of algorithms) There was quite a bit of clean up in Gimp, then Inkscape to remove blurry lines, clean up the fog. The stamp was carved on a Nomad 3 CNC with a 90º Vee Bit, with further cleanup by hand with some carving tools.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Fri, 03 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">28</guid></item><item><title>Syncing and Backups with Rsync and Borg</title><link>http://dizzard.net/articles/borg_rsync/article.html</link><description><![CDATA[<p><img src="http://dizzard.net/images/borg_and_rsync.jpg" alt="Borg And Rsync" /></p>
<h1>Syncing and Backups with Rsync and Borg</h1>
<p>I have a relatively simple self-hosting setup at home. My biggest and most important service is <a href="https://nextcloud.com">Nextcloud</a>. For quite some time, I've been running this with basically no backup, which is super risky, and I was starting to fear the day when the SSD fails. So to save myself from worrying, and save my previous photos and documents at some future date of failure, I decided to take some action while I was home for the holidays and had a little free time.</p>
<h2>Cloud vs. Local</h2>
<p>I considered a couple options for backups. One would be cloud provider solution, such as <a href="https://www.backblaze.com/">BackBlaze</a> or <a href="https://www.rsync.net/">Rsync.net</a>, but I didn't think that was a good fit for a couple reasons. First, it's expensive, BackBlaze would be $12/mo for 2TB, and then more money any time you need to recover your data. Micro Center was selling a <a href="https://www.microcenter.com/product/629580/toshiba-canvio-advance-2tb-usb-31-(gen-1-type-a)-25-portable-external-hard-drive-red">2TB Hard Drive for $80</a>, amortized over a year of use, that would come out to &lt; $6/mo. So if I can get more than 6 months from a drive at that price point, it would be a better deal than BackBlaze already.</p>
<p>So with that knowledge, I purchased a Toshiba Canvio 2TB Hard Drive at an even better deal of $70 at a Micro Center brick-and-morter store. One of the reasons I wanted to do this while I was home for the holidays is so I could setup my backup device in a different location than my actual server to satisfy the "1 copy off-site" part of the <a href="https://www.veeam.com/blog/321-backup-rule.html">3-2-1 Rule</a>. </p>
<p>I also picked up a Raspberry Pi 5 with 4GB of memory, and a couple accessories to get things fully set up.</p>
<h2>The Process</h2>
<p>After doing a little research on file synchronization and backups, I came up with a two-step process to backup all my docker volumes. This way if something were to fail on my local device, I could restore it from the offsite backup, or even roll back to an old backup. The nice thing about having it all containerized is that, as long as I have my docker volumes and docker compose files, restoring the server should be fairly straightforward.</p>
<p>I'm relying on two neat utilities called <a href="https://rsync.samba.org/">Rsync</a> and <a href="https://www.borgbackup.org/">Borg</a> which provide file synchronization and backup tools respectively. I use Rsync to transfer files over ssh to my offsite machine, then Borg to create a snapshot every week.</p>
<h2>Backup Script</h2>
<p>You can find the complete script <a href="https://gist.github.com/momja/83531e6af416d2afd207eaa5fca2572d">here</a>. Below is an illustrative version of the code.</p>
<div class="codehilite">
<pre><span></span><code><span class="c1"># ----------------------</span>
<span class="c1"># Abridged Backup Script</span>
<span class="c1"># ----------------------</span>
<span class="n">remote_backup_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;REMOTE_BACKUP_DIR&quot;</span><span class="p">)</span>
<span class="n">local_backup_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;LOCAL_BACKUP_DIR&quot;</span><span class="p">)</span>
<span class="n">borg_repo_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;BORG_REPO_DIR&quot;</span><span class="p">)</span>
<span class="n">backup_drive</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&quot;BACKUP_DRIVE&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">mount_drive</span><span class="p">():</span>
<span class="n">run_command</span><span class="p">(</span><span class="s2">&quot;mount /dev/sda1 /mnt/backup&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">sync_files</span><span class="p">():</span>
<span class="n">rsync_command</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;rsync -az --delete --exclude-from=&#39;/etc/backup-config/exclude-file.txt&#39; -e &#39;ssh -c aes128-ctr&#39; </span><span class="si">{</span><span class="n">remote_backup_dir</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">local_backup_dir</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="n">rsync_command</span><span class="p">,</span> <span class="kc">False</span><span class="p">)</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Rsync output:</span><span class="se">\n</span><span class="si">{</span><span class="n">output</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">backup_files</span><span class="p">():</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Backing up files...&quot;</span><span class="p">)</span>
<span class="n">borg_command</span> <span class="o">=</span> <span class="p">(</span>
<span class="sa">f</span><span class="s2">&quot;borg create --stats --progress &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">borg_repo_dir</span><span class="si">}</span><span class="s2">::dockerData-</span><span class="se">{{</span><span class="s2">now</span><span class="se">}}</span><span class="s2"> &quot;</span>
<span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">local_backup_dir</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="p">)</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="n">borg_command</span><span class="p">)</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Borg output:</span><span class="se">\n</span><span class="si">{</span><span class="n">output</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">unmount_drive</span><span class="p">():</span>
<span class="n">run_command</span><span class="p">(</span><span class="s2">&quot;umount /mnt/backup&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">parser</span> <span class="o">=</span> <span class="c1"># setup parser</span>
<span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="o">.</span><span class="n">sync</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">args</span><span class="o">.</span><span class="n">backup</span><span class="p">:</span>
<span class="n">logging</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s1">&#39;Either --sync, --backup, or both must be specified.&#39;</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">mount_drive</span><span class="p">()</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">sync</span><span class="p">:</span>
<span class="n">sync_files</span><span class="p">()</span>
<span class="k">if</span> <span class="n">args</span><span class="o">.</span><span class="n">backup</span><span class="p">:</span>
<span class="n">backup_files</span><span class="p">()</span>
<span class="k">finally</span><span class="p">:</span>
<span class="n">unmount_drive</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre>
</div>
<p>In order for the backup script to work, you need to add some environment variables. I provide these in a <code class="codeblock">.env</code> file, but you could also add to a <code class="codeblock">.bashrc</code> file or just provide on each run.</p>
<div class="codehilite">
<pre><span></span><code><span class="nv">REMOTE_BACKUP_DIR</span><span class="o">=</span>you@computer:/path/to/data/
<span class="nv">LOCAL_BACKUP_DIR</span><span class="o">=</span>/mnt/backup/data
<span class="nv">BORG_REPO_DIR</span><span class="o">=</span>/mnt/backup/data_borg
<span class="nv">BACKUP_DRIVE</span><span class="o">=</span>/dev/sda1
</code></pre>
</div>
<p>Before running, make sure you set correct permissions on both servers, and configure an ssh key for the offsite machine to access the main server.</p>
<p>To automate the execution of the sync and backup, I've set up two cron jobs. One that runs on Sundays and performs synchronizations and snapshots. Then another that runs every other day and just performs synchronization. My idea is that if I were to break something on my primary server while doing some configuration or testing, the Rsync mirror will allow me to recover to a valid state, or repair accidentally deleted data. If something worse happens, like my SSD gets corrupted, I can restore from the Borg Snapshots, which is a little slower and a little more work.</p>
<h2>Conclusion</h2>
<p>This is not a perfect solution, it's not even foolproof. But it seems to work OK now. I have a little more testing to do before I'm comfortable with it, but it does feel like I can breath a little easier knowing if I F**k something up, I'll <em>probably</em> be able to recover from it. The next logical step would be to buy another drive so the offsite backup and synchronized repositories are on separate disks, then set up alerts to monitor for a failure so I don't need to manually check it. I'm not going to mess around with RAID right now, but maybe some day.</p>
<p>Now, going back to the provider solution for a second. I still think it's a good idea to store your data with a secure cloud for another layer of redundancy, it's just not my first choice. Part of the reason I'm trying to better protect my data is because my family is running into issues with Amazon Photos and the <a href="https://www.reddit.com/r/gsuitelegacymigration/comments/uhzseu/comment/i79p2sn/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button">lack of control</a> they give their users. So it just didn't feel right to rely on another cloud provider for a backup solution.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Thu, 02 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">27</guid></item><item><title>My Favorite Things of 2024</title><link>http://dizzard.net/articles/favorite_things_2024/article.html</link><description><![CDATA[<p><img src="https://img.buzzfeed.com/buzzfeed-static/static/2015-09/29/22/enhanced/webdr14/anigif_original-5801-1443580997-15.gif" alt="" /></p>
<h1>My Favorite Things of 2024</h1>
<p>Every year, the Tested network publishes a series of videos with the staff about their favorite things of the year. Here's all the ones from 2024:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=12uvJkBL7mo">Norm</a></li>
<li><a href="https://www.youtube.com/watch?v=j_88zIuh_xY&pp=ygUOVGVzdGVkIGluIDIwMjQ%3D">Josh</a></li>
<li><a href="https://www.youtube.com/watch?v=qgrt7uoGm70&pp=ygUOVGVzdGVkIGluIDIwMjQ%3D">Adam</a></li>
<li><a href="https://www.youtube.com/watch?v=E2sBYsaW8h4&pp=ygUOVGVzdGVkIGluIDIwMjQ%3D">Kayte</a></li>
<li><a href="https://www.youtube.com/watch?v=Jhvq9M2TCjU&pp=ygUOVGVzdGVkIGluIDIwMjQ%3D">Sean</a></li>
<li><a href="https://www.youtube.com/watch?v=Z_J1wXFXBoo&pp=ygUOVGVzdGVkIGluIDIwMjQ%3D">Joey</a></li>
</ul>
<p>I really enjoy these videos. I'm a bit of a tool nerd, so it's a real treat seeing professionals describe some of the tools that have been the most satisfying and productive to use. I think the tools in someone's metaphorical tool belt also say a lot about that person's philosophy of work.</p>
<p>So I thought I should share some my favorite things from 2024! It's not all tools, but a lot of them are.</p>
<p>None of these are affiliate links BTW.</p>
<h2>My Favorite Things of 2024</h2>
<ul>
<li><a href="https://www.knipex.com/products/pipe-wrenches-and-water-pump-pliers/knipex-cobra-high-tech-water-pump-pliers/knipex-cobra-high-tech-water-pump-pliers/8703180?v=9365">Knipex Cobra Water Pump Pliers</a>. I actually received a pair of these last Christmas, used them for a bit, then I lost them :(. Halfway through 2024, I bought a replacement. I had the <a href="https://a.co/d/75JYw9D">4 inch</a>, then I replaced with the <a href="https://a.co/d/a1aPUr3">5 inch</a> ones. Both are great, I kind of wish I still had the 4 inchers, since they do not use a push button mechanism to release the jaws, and they are just a little more pocketable. I shuttle my stuff between home and <a href="https://sequoiafabrica.org">Sequoia Fabrica</a> by bike frequently, so having small tools is really convenient. The pliers themselves are fantastic, they can get a grip on anything, and their unique shape helps prevent rounding over bolt heads.</li>
<li><a href="https://www.banggood.com/12_7+25_4+67MM-Carbide-Lower-Bearing-Spiral-Trimming-CNC-Router-Bit-End-Mill-1-or-4-inch-6_35mm-Shank-for-Woodworking-p-2000463.html?cur_warehouse=CN&rmmds=search">Spiral Shank Flush Template Router Bit</a>. Template bits in general have become a new favorite item for me. Along with a laser cutter (or a steady hand at the bandsaw), it has become a total joy to make templates. I'm working on a sign (more on that later), where I had to route out every letter individually. This wouldn't have been possible freehand, but with a palm router, a template bit, and a template, it was a breeze. It's really unlocked new possibilities for me, especially in a small shop.</li>
<li><a href="https://a.co/d/3LE3lVz">Makita Cordless Track Saw</a>. I think any track saw would make me just as excited, but this is the one I got. I bought it used off Craigslist, and it took a little adjusting and getting used to, but this machine is fantastic. In a woodshop without a table saw, it's practically required. My biggest frustration is having to set it up and tear it down every time I use it. However, there are some cool things you can buy or <a href="https://www.youtube.com/watch?v=sHj5G6t1qvY&pp=ygUUdHJhY2sgc2F3IHJhaWwgaGluZ2U%3D">make</a> (or <a href="https://www.youtube.com/watch?v=59RAkriXEh0&pp=ygUUdHJhY2sgc2F3IHJhaWwgaGluZ2U%3D">this</a>) that allow for some easier setup and alignment with the track saw, which I hope to find some time to figure out in 2025.</li>
<li><a href="https://nextcloud.com/">Nextcloud</a>. Nextcloud is my self-hosted solution to file storage. It's like Google Drive, but I can host <em>everything</em> myself, so there's no question of data ownership, and I have complete control over who can see it, and how I access it.</li>
<li><a href="https://support.apple.com/guide/iphone/play-background-sounds-iphb2cfa052c/ios">IOS Background Sounds</a>. This one might seem a little weird, but always having white noise on my device, without having to fiddle with an app or make sure it's downloaded, is absolutely fantastic. I have <a href="https://en.wikipedia.org/wiki/Tinnitus">tinnitus</a>, so whether I'm home or away, having white noise is a necessity when I sleep. For whatever reason, I've been woefully unimpressed with the market of white noise apps available. <a href="https://portal.app/">Portal</a> is the best I've seen, but I don't want to pay for a subscription since I only use the most basic functions (But if you like it <em>you</em> should buy it! I met <a href="https://portal.app/about">Nick</a>, their founder once, and they are a great person making a great product).</li>
</ul>
<p>Alright so that's the cut for 2024. Maybe I'll do this again at the end of the year/start of next year.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Wed, 01 Jan 2025 00:00:00 -0000</pubDate><guid isPermaLink="false">26</guid></item><item><title>New Year's Resolutions</title><link>http://dizzard.net/articles/resolutions/article.html</link><description><![CDATA[<h1>New Year's Resolutions</h1>
<p>For the past few years, I've written up some resolutions for myself.</p>
<p>Last years resolutions and reflections:</p>
<ul>
<li><strong>Exercise more</strong>. I did pick up biking, which was a step in the right direction, but I walk less and seem to spend less time outside in general. I guess <a href="https://dizzard.net/articles/new_workshops/article.html">opening a makerspace</a> takes up a lot of free time.</li>
<li><strong>Join the 100 days to offload challenge</strong>. I blogged 8/100 times. 2023 was 8 times. I’m trending flat! Well it’s supposed to be a challenge, and I’d rather fail again at a challenge than not try at all so I’m keeping it on the list. I have a couple blog posts locked and loaded for 2025 already. Maybe that's cheating, but it still got me writing more!</li>
<li><strong>Read less news.</strong> Despite this being a horrible year for news, I escaped the doom scroll that I fell into during the pandemic. It took a few years to escape it's gravitational pull, but I think I'm doing better. We will see how things go in 2025 :/.</li>
<li><strong>Be more intentional with time</strong>. There's only so many hours in the day, and you have to make the most of it. I spent less time doom scrolling (positive), but more time scrolling overall (negative). I've deleted TikTok, YouTube and Facebook from my phone because I can't keep watching these 5 second videos at every otherwise quiet moment I have during the day.</li>
</ul>
<p>So here’s my list:</p>
<ul>
<li><strong>Join the <a href="">#100DaysToOffload challenge</a></strong> . This made the list last year and I only posted 8 times. I’m not too confident about it, but I want to try again. It's a stretch, I know, considering my blogging history. But what is there to lose? I tried last year and failure didn't make me run away from this blog.</li>
<li><strong>Be OK with the Silence</strong>. This is similar to last year's goal of <em>Be more intentional with my time</em>. But since that didn't work too well last, year, I'm going to try and narrow it down. What does the silence have to do with intentionality? Well, usually when there is any moment of quiet, a moment where I am not actively engaged in work or some variety of activity, I feel the need to pull out a screen and look at something. This is not good for my mental health or productivity. I need to be OK not always having something to do or something to look at. Relaxation is good. Enjoy the quiet times while you can.</li>
<li><strong>Eat Healthy more consistently</strong>. I like to think I eat a rather healthy diet, but in busy weeks, everything falls off the rails and I'll eat anything in front of me. Even when I don't have to, there's something in my brain when I'm stressed that tells me to just eat, even when it's not something I want or I'm not hungry. I'm better about it when life is more routine, but I can't always rely on routine to keep me healthy.</li>
</ul>
<p>What didn't make the list:</p>
<ul>
<li><strong>Make more stuff</strong>. This was on the list back in [[News Years Resolutions 2023|2023]], I always want to make more, and I think I will do this naturally, I don't feel the need to keep an eye on this.</li>
<li><strong>Read more</strong>. My reading habit has fallen off the rails since graduating College. I used to religiously track what I was reading, how much. That got me reading more, and consistently. Now I rarely pick up a book. I'll read my RSS feed, but that's the most I'll do. I'm currently Reading The Power Broker, but not on track to finish it in this century. In years past, I'd read 20-30 books, I'm not sure I've read more than 5 this year. I'm just telling myself - for now - that that is OK. Without diminishing the importance of reading, I feel like there are more important things I need to focus on right now.</li>
<li><strong>Exercise More</strong>. Exercise is critical to well-being. I acknowledge I fell short of my goals for 2024, and I'll be working on that going forward. I'm still prioritizing diet over exercise right now. Because of my daily habits, and tendency to walk/bike everywhere, I'm going to get exercise in without trying, even if it's not as much as I would like.</li>
</ul>
<p>What are your resolutions? Shoot me an <a href="mailto://mjomdal@gmail.com">email</a>.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Tue, 31 Dec 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">25</guid></item><item><title>AI For Development</title><link>http://dizzard.net/articles/ai_development/article.html</link><description><![CDATA[<h1>AI For Development</h1>
<p>I occasionally tinker on my computer after work hours on personal projects, albeit infrequently, especially when compared to the magnificent work done by some of the engineers I follow. Most of my time currently is wrapped in <a href="https://sequoiafabrica.org">building a community makerspace</a>, but there are some quiet evenings I find myself writing small utilities, or working on this blog. </p>
<p>Since the time I have and am willing to spend on computer-related projects outside of work is dwindling, I want to go from idea to product as quickly as possible. I love having the know-how to make things with just a computer terminal and a browser, but if I'm being honest, I'd rather spend my free time away from a desk. At my job, copilot-type assistants haven't been approved for use by engineering. But I have started incorporating them in my personal development workflow. I've found <a href="https://zed.dev">Zed</a> to be a compelling IDE focused on modern techniques and AI for development (but if anyone from Zed reads this, please improve your vim emulator!). I've tested out ChatGPT-4o quite a bit using the <a href="https://web.chatboxai.app/">Chatbox</a> GUI. Alfred also has a handy <a href="https://alfred.app/workflows/alfredapp/openai/">ChatGPT workflow</a> (this has fallen out of favor for me as my requests to these chatbots have become more verbose).</p>
<p>What I want out of LLMs is a way to work <em>faster</em>. I don't expect it to do my work for me, but if it can just get me started sometimes I'm able to get things done quicker. It's doing a bit more than covering boilerplate, and a bit less than implementing APIs. But ultimately, if I can just get things done faster, I can do more of the stuff I love. Sometimes that stuff is building more software projects, but a lot of the time, it's getting away from my desk. A lot of the programs I write are to make my life more fun or functional, like adding light strips with custom firmware to my room, or, to borrow a phrase from <a href="https://automatetheboringstuff.com/">Al</a>, automating the boring stuff. I've found LLM's to be <em>really poor</em> at implementing new code, but it can usually get a refactor about 80% correct. So, that's 80% less time I have to spend refactoring (minus maybe 10% for the time it takes to review the bot's changes). That's a win. That's a speed improvement that makes me more likely to attempt something. It also means when I run into speedbumps because I don't know how to do something in a particular language, or I can't debug a stack trace, I'm able to get through those quicker.</p>
<p>I think Simon Willison has one of the most realistic grasps on <a href="https://simonwillison.net/2023/Mar/27/ai-enhanced-development/">LLMs for software development</a>. He writes really great quality long-format and short-format articles on the topic. He also references others in the field, so in a way the blog is a bit of an aggregator for content. I'd also give <a href="https://www.scattered-thoughts.net/writing/speed-matters/">this article</a> from <a href="https://www.scattered-thoughts.net/">Jamie Brandon</a> a read on why speed matters in software engineering projects. It's one of the very few fields where throughout a career you can expect to improve your productivity by a factor of 10.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Tue, 17 Sep 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">24</guid></item><item><title>Finished a Workbench</title><link>http://dizzard.net/articles/workbench/article.html</link><description><![CDATA[<h1>Finished a Workbench</h1>
<p>I just finished constructing a workbench for <a href="https://sequoiafabrica.org">Sequoia Fabrica's</a> woodshop. I'm fairly happy with it, it's one of my first large-scale projects. The table top was a challenge to assemble because it was all construction lumber. Working around knots and laminating all the boards together was really tough, but I'm happy with how it turned out.</p>
<h2>Design</h2>
<p>The workbench is designed to hold 3 filing cabinets for tool storage, with a platform for holding the shop vac. It's entirely built out of construction lumber to keep the cost at a minimum.</p>
<p>A tool well is added to the back to make it easy to clear up your work surface while in the middle of a project. </p>
<p>10 dog-holes are placed in the front left of the bench for workholding. They are spaced every 6in, and I designed a <a href="../../images/laser_template.jpg">laser cut template</a> to handle this. There will also be a Twin vise designed and manufactured by <a href="inkleind.com">Andrew Klein</a> attached to the front face of the table, which will further augment workholding.</p>
<p>The table is just shy of 8', which might be a little long for a typical workbench. It is ~2' deep. The woodshop is very small, under 100 sqft. So going big here was a decision I perseverated over for a while, but ultimately decided having a large main workbench is the a more efficient use of space than several small tables.</p>
<h2>Conclusion</h2>
<p>Overall, I'm very satisfied with how sturdy the table is. The finish color is OK (used Danish oil), and I think it being made of Douglass Fir, it's going to get pretty dented up. We will see how it wears. At least I know how to repair it. I use a track saw a lot, and I really think the track saw begs for something that's not up against a wall. because the track needs some overhang. I'm looking forward to adding the vise, because I think it will make this a great bench for hand tool work, power carving, etc.</p>
<h2>What's Next</h2>
<p>I think the next thing is going to be two portable square tables that will be placed on quality casters. Then if you need more space, just roll them out of the room. If you need more worktop, bring 'em back in.
<img src="http://dizzard.net/images/workbench_1.jpg" alt="" /></p>
<p><img src="http://dizzard.net/images/workbench_2.jpg" alt="" /></p>
<p><img src="http://dizzard.net/images/workbench_3.jpg" alt="" /></p>
<p><img src="http://dizzard.net/images/workbench_4.jpg" alt="" /></p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sat, 15 Jun 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">23</guid></item><item><title>Boquets to Art at the De Young Museum</title><link>http://dizzard.net/articles/blooming/article.html</link><description><![CDATA[<h1>Boquets to Art at the De Young Museum</h1>
<p>This past weekend, my partner and I went to San Francisco's <a href="https://famsf.org">De Young Museum</a> to see the exhibition annual "Boquets to Art". It was a fun event that takes over the entire museum, pairing artwork to flower arrangements in whimsical and creative ways. Here are some pictures from the event.</p>
<p><img src="http://dizzard.net/images/blooming_1.jpg" alt="" />
<img src="http://dizzard.net/images/blooming_2.jpg" alt="" />
<img src="http://dizzard.net/images/blooming_3.jpg" alt="" />
<img src="http://dizzard.net/images/blooming_4.jpg" alt="" /></p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Wed, 12 Jun 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">22</guid></item><item><title>Refinishing a Chest of Drawers Found on the Street</title><link>http://dizzard.net/articles/chest_of_drawers/article.html</link><description><![CDATA[<h1>Refinishing a Chest of Drawers Found on the Street</h1>
<p>Recently, while walking to the neighborhood library, I found a chest of drawers, or dresser in an alley with a free sticker on it.
It was in rough shape.
The paint was falling off, it was scratched, and the drawers' tracks were worn thin.
San Francisco for whatever reason seems to be the land of setting stuff on the street and hoping someone takes it. I guess it's because a lot of the time people do take it.
Well, that day on my way <em>back</em> from the library, I became the person to take the street junk.
I shouldn't act like that is out of character for me.
Much of our apartment is furnished from stuff we've found on the street, and sometimes if we are feeling fancy, stuff we've purchased secondhand.
It's a bit of a game, kind of like <a href="https://www.reddit.com/r/DumpsterDiving/">dumpster diving</a>.
Sometimes, you go for a walk and there's some great find right around the corner!</p>
<p>One of my favorite finds was an antique dresser manufatured by <a href="http://www.furniturecityhistory.org/company/3824/sligh-furniture-co">Sligh</a> over 100 years ago.
It had seen better days, but the piece was still beautiful.
It had this warmth to it.
You could feel it's age in it's character.
Something about the way it had worn would speak to me when I opened it.
I'd think about all the hands that dresser had passed through, and I'd think how many more hands will it go through.</p>
<p>That last part is really important to me. This piece of furniture has been around for 100 years, how many more years does it have left?
The Sligh dresser was set on the street for trash pickup, so it had just about seen it's last trip around the Sun.
I feel purpose in saving these things and redressing them so they can continue to be loved.</p>
<p>We didn't end up keeping that dresser.
I spent a lot of time repairing the drawer slides, fixing up cracks and finishes, but it was ultimately just too big for our room.
However it was enjoyed thoroughly for the year and a half we had it.
And it makes me happy to know I was able to pass it on to someone else, who I hope is continuing to use it.
I enjoyed breathing life back into this centenarian object.</p>
<p>So when I saw the chest of drawers by the library, I felt ready for my next dresser restore.
This one was in worse shape than the last one, particularly the finish.</p>
<p>Someone had clearly tried painting over an old finish that was wearing.
Sometimes that works.
Most of the time I think without addressing the original finish properly you're going to run into problems.</p>
<p>Underneath the paint, it looked like some sort of surface finish, maybe a spar varnish.
Not sure.</p>
<p>I scraped off most the paint and finish by hand with a scraper, then in delicate areas I used Citristrip.
If you want to do furniture repair, avoid using chemical strippers as much as possible.
It doesn't make your life as easy as you'd hope.
They make huge messes, and they are just disgusting to work with, even the ones that are supposedly more "natural".
Try and get as much done by hand as possible.
It will take some elbow grease, but maybe that's just a way to save yourself from a gym membership.</p>
<p>So with most of the finish removed, The wood had some discoloration from stains and some difficult areas still needed to be cleaned up.
Luckily for me, the dresser was all solid wood. The construction of the chest is very simple solid wood and butted up joints nailed together.
A little utilitarian when compared to some fancy dovetail drawers, but whatever, I like it for what it is.
Since it was all solid, I could sand it to heck.
The top also needed some planing since it had cupped. It was kind of strange the way the top was adhered and warped.</p>
<p>I'm pretty sure it was replaced after the original construction.
The top appears to be Poplar.
I'm still learning to identify woods, but I think the rest of the body is either cherry or soft maple.
May also poplar :shrug:.</p>
<p>With everything all cleaned up and sanded to 180 grit, I applied some Total Boat Halcyon amber water-based polyurethane.
I really enjoy working with this stuff because it's so hard to mess it up.
It goes on real nice, with a cool gloss, and I don't have to worry so much about VOC's when working with it.
Clean up is also simple since it is water based and can be brushed on.
However, it's quite expensive. When I last bought it, a quart was $46 on Amazon!</p>
<p>Anyway, without further ado, here's the final product!
Notice some of the drawers are sagging, I think I can replace the drawer slides at a later date, but for now I'm happy to lubricate it with a little wax and call them good.</p>
<p><img src="http://dizzard.net/images/chest_incomplete.jpg" alt="Before" />
<img src="http://dizzard.net/images/chest_complete.jpg" alt="After" /></p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Tue, 04 Jun 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">21</guid></item><item><title>Building a New Workshop in San Francisco</title><link>http://dizzard.net/articles/new_workshops/article.html</link><description><![CDATA[<h1>Building A New Workshop in San Francisco</h1>
<p>San Francisco will soon be getting a new community workshop. As a part of an organization, myself and others are building <a href="sequoiafabrica.org">Sequoia Fabrica</a>. It will be a non-profit community workshop with the mission to provide access to tools and knowledge without the barriers to entry at many for-profit organizations. Since opening our doors in March, we've also distinguished ourselves by offering a wide variety of <a href="https://bookwhen.com/sequoiafabrica#focus=ev-sl1e-20240604190000">workshops and classes</a> which is all thanks to the members and volunteers who have founded and kept the space going.</p>
<p>I am the President and Woodshop <a href="https://wiki.sequoiafabrica.org/wiki/Groves">Grove</a> leader for the time being. I hope as Sequoia grows, we're able to add more members willing to take on leader and instructor roles. So far, it's been going well, although I'll say I think starting anything that requires money and legal establishment can be a bit stressful.</p>
<p>What I really look forward to doing is running classes and building an inspiring space. I've looked at other Bay Area woodshop classes, and the pricing is absurd. I don't fault the small businesses for charging $300 for a stool making class, because they are trying to make a living from it, and I think that's a great think. But I don't think that should be the only option. If you want to pay $300 to make a stool, or learn basic woodworking, I think you should do that, and you will probably get a more refined experience for it. What myself and the others at Sequoia are concerned about is the missing tier below that. We want to offer classes for cheap, led by volunteers who are doing it just because they care to, without the pressure of needing to do it for income. Many people have all these different hobbies they could talk about, or put to practice without end. Our space is for those people. It's for sharing, building, and learning. Long-term, I would like to be able to host classes that are completely free, including materials cost. We can do this if we're able to build a strong enough foundation of donors, and hopefully some grant money, and that will take time. To me, that time and effort will be worth it, because we'd be radically breaking down the barrier to entry for people to get into fine crafts and maker culture.</p>
<p>I don't think the "class/event" structure is for everyone. Some spaces like <a href="https://noisebridge.net">Noisebridge</a>, which I've mentioned <a href="http://dizzard.net/articles/shared_workshop/article.html">before</a> haven't made that the priority focus, and that works for them. Some makerspaces, or whatever you choose to call them aren't prioritizing events as a way of serving the community. I wish there was more of this across the board, but I also understand some spaces might prefer to harbor an environment where members can just make use of the equipment at their disposal. Sequoia does give members 24/7 access to use the space for their personal projects, but it is also made clear this is not a rental workshop. Members are as much volunteers as they are space users. So to be a member, you kind of have to buy in to the idea that what we're doing is of benefit to the community and you are down to help with that.</p>
<p>I like classes. I like taking them and I like teaching them. I'm not an expert craftsman, but you don't need to be an expert to teach a class, you just need to know a little more about the trade than what you will get to in the class. An elementary school math teach doesn't need to know Calculus to be a great teacher.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sun, 02 Jun 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">20</guid></item><item><title>Homemade Laundry Detergent Recipe for Lazy People</title><link>http://dizzard.net/articles/detergent/article.html</link><description><![CDATA[<h1>Homemade Laundry Detergent for Lazy People</h1>
<ul>
<li><strong>2</strong> parts Borax (400g)</li>
<li><strong>2</strong> parts Washing Soda (400g)</li>
<li><strong>1</strong> parts Fels-Naptha Soap (200g)
<h2>Steps</h2></li>
</ul>
<ol class="list-decimal">
<li>Grind 200g of Fels-Naptha soap with an old coffee grinder. Anything works really as long as you can get it down to powder.</li>
<li>Add 400g of Washin Soda, and 400g of Borax to a plastic container</li>
<li>Seal the container and shake to mix</li>
<li>Use about 2 tablespoons per load</li>
</ol>
<h2>Notes</h2>
<ul>
<li>I’ve tried using Zote soap, another popular laundry soap, but its way to hard to grind into a powder. The soap is not nearly as dry as fels naptha, the texture is slightly oily.</li>
</ul>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sat, 04 May 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">19</guid></item><item><title>This is now available on RSS!</title><link>http://dizzard.net/articles/rss_now/article.html</link><description><![CDATA[<h1>This Blog is now available via RSS!</h1>
<p>I took some time this weekend to add an RSS feed to this blog. I will eventually add a visible link, but for now, you can find it at <a href="https://dizzard.net/rss">dizzard.net/rss</a>. It may have some kinks to work out, but the solution was quick and dirty. I implemented it using <a href="https://pypi.org/project/feedgen/">Feedgen</a>. Like most of my personal projects these days, ChatGPT sped up the process significantly.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Mon, 22 Apr 2024 00:00:00 -0000</pubDate><guid isPermaLink="false">18</guid></item><item><title>How to make Beeswax Candles</title><link>http://dizzard.net/articles/beeswax_candles/article.html</link><description><![CDATA[<p><img src="http://dizzard.net/images/candlesticks.jpg" alt="" /></p>
<h1>Beeswax Candles (Dip)</h1>
<p>For <a href="https://sequoiafabrica.org">Sequoia Fabrica</a>, I'm running a candlestick making class. I'm not an expert at this, but like most things in life, you just need to be one page ahead in the book to explain to others what is going on. These are my notes on candlestick making.</p>
<h2>Prerequisites</h2>
<p>You will need:</p>
<ul>
<li>A burner (induction for portability)</li>
<li>Large boiler</li>
<li>4 Deep jars (alternatively, a double boiler and 2 jars)</li>
<li>Scissors</li>
<li>Cotton wicks</li>
<li>Beeswax (<em>2-3lbs</em>)</li>
<li>Cold water</li>
<li>Washers or bolts (any small scrap of metal you can tie a string around)</li>
</ul>
<h2>Steps</h2>
<ol class="list-decimal">
<li><p>Heat the wax in a double-boiler style.
If you don't have a double boiler, just fill jars with wax and place in a pot full of water.
Beeswax melts at <em><a href="https://en.wikipedia.org/wiki/Beeswax">144 to 147 °F</a></em>, keep this in mind while melting.
Low and slow is better, so try not to exceed the temperature significantly.
The water should never come to a boil.</p></li>
<li><p>Once the wax is fully melted, prepare a container, or a couple jars with cold water.
Don't make it <em>too</em> cold, because that will cause issues later.
Get some tap water, add one or two ice cubes.</p></li>
<li><p>Now, cut the string to be twice the depth of your melted beeswax, plus an extra <em>15-20cm</em>.
Tie to each end a couple washers. Since wax floats in water, this will help it sink.
The washers are optional.</p></li>
<li><p>Now you start dipping.
This is the repetitive part.
Dip in the hot wax, let it drip off, dip in the cool water, let it drip off.
The repeat.</p></li>
<li><p>Continue step 4 until you have your desired candle size.
The final candle should be between <em>18-20mm</em> in diameter to fit in most candlestick holders.
You can measure this by marking a string at a <em>56.5mm</em> ($pi times 18$), and wrap around the candle.
Sometimes it's easiest just to start with a candlestick holder and work until you've got the size you want.</p></li>
</ol>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Mon, 11 Dec 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">17</guid></item><item><title>What Does Apple Watch Do</title><link>http://dizzard.net/articles/apple_watch/article.html</link><description><![CDATA[<p>I’ve worn an Apple Watch on and off since the Series 2, which came out in 2016. I’ve owned two watches. One disappeared one day. I have no idea where it went. For a while, I wore two family members’ watches because they never used them. Then I bought my own. </p>
<p>Buying that watch was a really stupid move. I don’t think the Apple Watch is totally stupid, but I think for the vast majority of users, it’s just a glorified pager. </p>
<p>For myself, I found the most compelling use of the watch was paying for the bus. Something I could also do with my phone in my front pocket or the wallet in my back pocket. </p>
<p>It’s not that I didn’t try to love the Apple Watch. I wore it every day to see my health trends, I designed and 3D printed a custom dock to minimize the effort to charge it, I even bought a case I could pop on while I was woodworking so it could stay protected and remain on my wrist. I tried using it for Siri, controlling my smart home, sending messages, monitoring workouts (ok this use case is good).</p>
<p>The problem is that the watch never tried to love me back. The features just aren’t useful. And I think more people need to come to terms with this rather than just buying it because of the Apple hype train. Siri sucks on the wrist, especially if I’ve got a phone near me idk who’s gonna pick up my call to “hey Siri”. When it does pick up, it takes way longer than my phone to provide feedback. Same for controlling my smart home. It doesn’t update nodes in the home app until I actually open the app on the watch, so when i want to turn off my bedroom light, I need to wait 5 seconds for it to realize the bedroom light is currently on in order for me to turn it off. This is just a waste of time to sit there and wait. In fact I’m coming to think smart homes in general are still far from being useful. It’s health monitoring appears to be very detailed and accurate, but as someone who deals with health anxieties that stem from my whacky brain rather than actual diagnosed health issues, having all this data on myself doesn’t actually seem to be benefitting me in any way.</p>
<p>Regarding workouts, I find the watch makes me <em>less</em> likely to exercise because I have this pressure to wear the watch every time I leave the house for a run because if I’m not wearing it, then the run <em>never happened</em>. So if I don’t have a charged watch, or I forgot to load up some music or a podcast on it beforehand, count that run out. </p>
<p>My least favorite, yet most common use of the watch is for field notifications I don’t need to be looking at in the first place. Why do I need my wrist to buzz every time I get an automated email to my work inbox that I don’t read anyways? Why read “Mom just sent you a photo. Open iPhone to see”? What’s that doing for me? Is it anything positive, or is it just getting me to check my devices more.</p>
<p>If you use the watch a lot for tracking workouts, or you have some health issues you want to monitor, I think you should by all means go for it and use a watch. But for any other purpose, I’d say it’s not worth the expense, nor is it worth the mental stress the notification machine puts on your brain.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sat, 11 Nov 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">16</guid></item><item><title>Principles of Operating a Shared Workshop</title><link>http://dizzard.net/articles/shared_workshop/article.html</link><description><![CDATA[<h1>Principles of Operating a Shared Workshop</h1>
<p>These are some of the thoughts I have compiled over the last year volunteering my time helping maintain a wood-shop at my local hackerspace, <a href="noisebridge.net">Noisebridge</a>. Noisebridge is a very unique space in that it operates as an anarchist space with one guiding principle: <em>Be excellent to each other</em>. There can often be conflict between passionate people at the space doing their best to make things better, and not being able to agree on what "better" really is. I'm putting <em>my</em> thoughts down on what I've observed to work. I'm certain not everyone will agree.</p>
<h2>Everything must have exactly one place of belonging</h2>
<p>If the space is big enough to require redundant items, each duplicate should be clearly labelled as to where it belongs. I.E. if there are two solder stations, each soldering iron should be labelled with the station it belongs to. Keep in mind that Hackerspaces are rarely big enough to require redundancy. Noisebridge has some duplicate items because of accessibility issues with our multi-floor layout, but beyond that, we strive to be as explicit about where things belong as possible.</p>
<h2>Floor Space is sacred</h2>
<p>In a workshop, you are <em>constantly</em> moving around. For your safety and efficiency, floors and worktops should be cleared at the end of each use.</p>
<p>Keeping tools and supplies off the floor is also essential. Use vertical space on walls, add shelving, and remove unused tools if necessary. The floors should be the cleanest part of your shop.</p>
<h2>Maintainers must be ruthless in enforcing rules about keeping things clean**</h2>
<p>Again, I'm talking from experience at an anarchist hackerspace, where the doors are open and all are welcome. So based on how restricted access your space is, your mileage will vary. </p>
<p>There should be a zero tolerance policy for things left on work tops. If someone's project is not stored correctly, they must address the problem immediately or the project must be removed from the space. Workspace must be protected at all costs, otherwise the purpose of the space is brought into question. Is it really a workshop if there is no space to work?</p>
<h2>Agree upon limits to what tools are to be kept in the space</h2>
<p>Non-industrial spaces are extremely limited in square foot and resources. Having a couple accessible and well-maintained tools should always be prioritized over accumulation of tools in various states of usability. From my experience, the more tools added to the space, the harder it becomes to work because you are trading square footage for more tools. You are also trading time and resources maintaining those tools.</p>
<h2>Labels and rules should be explicit, but mutable</h2>
<p>Nothing is set in stone at an anarchist space, or any space for that matter. Assume you will have to make changes in the future, but you should try and mitigate excessive churn. Robustly structured environments should be able to support the activities people expect to use it for, and it should also be able to adapt if the community agrees we want to support new sorts of activities.</p>
<p>This might manifest itself as putting some of your otherwise stationary tools on casters so you can rearrange the shop easily. However, this should be done carefully. Increasing the flexibility of the space also makes it easier for rogue agents to act in ways that benefit their own interests rather than those of the community. This balancing act is often best addressed in practice by keeping a watchful eye over the space, and mentoring others to do the same. It is also important to define shared values as a community to help individuals know how to behave when working on their own.</p>
<h2>Project storage rules should be clear and upheld</h2>
<p>A workshop should not be treated like a U-haul storage locker. This is often the biggest source of accumulation and detrimental to the preservation of floor space. If you even have the space for project storage, with the community to outline how long a project should be allowed to be stored, and stick to the agreement by promptly removing projects that exceed the deadline.</p>
<p>This may seem harsh and upsetting to an individual who leaves their project longer than the allotted time, but there are sacrifices you make when committing to a project in a shared workshop. Try to work with people <em>before</em> they reach these deadlines to make sure they are aware of these expectations. This can be one of the hardest things to do because of the time and difficulty associated with contacting individuals for project updates, and even just figuring out who's project is who's.</p>
<h2>Closing thoughts</h2>
<p>You don't have the same freedoms as you do in your own space, but if you can give up these freedoms you gain the benefits of more square footage, bigger variety of tools and resources, and most importantly a community to build with. Living in San Francisco as a 23 year old, having access to a wood shop that is solely my own is fiscally impossible.</p>
<p>Yeah, a lot of the time working with a community is hard. Like, <em>rip out your hair hard</em>.</p>
<p>When I'm feeling that stress, I need to remind myself that working with a community doesn't just bring up the bad things. It also brings me tremendous joy to work with other people, teach, and learn alongside them. It's become rare that I will go to the wood shop and just work on whatever personal project I have in mind. Most of the time, I will be working on something for the space or chatting with someone about a project or something unrelated to woodworking altogether. I've come to realize shared workshops are not just about giving people the resources they need to build their own stuff. The community of makers and hackers is <em>itself</em> something that requires building.</p>
<h2>Notes</h2>
<p>This is an excerpt from a book I'm reading <a href="https://worldcat.org/title/429393294">about cohousing</a>, where it is common that several homes will share one workshop. I found some of the outlined principles interesting.</p>
<blockquote>
<h1>Making Workshops Successful</h1>
<p>It may be hard to imagine that a workshop used by 50 or 60 persons could possibly function smoothly. Potentially, a shared workshop can help residents save money and produce better work, and it can be more fun in the process. It can also be a chaotic free-for-all. In comparing the systems of different cohousing communities, we found that the following practices promoted the usability and safety, and decreased the frustration, of a shared workshop</p>
<p>A. <em>Accountability</em> Keeping the workshop locked and having two or three people "in charge" makes users feel accountable to the person they get the key from, who will be upset if a tool is missing or the shop is left messy.</p>
<p>B. <em>Buying In</em>, or varying degrees of investment. Some may want to have better power tools; they should buy and store them separately in a locker or tool box. Others may not even want to invest in a good set of hand tools (and should not plan on borrowing them). Thus, there can be two or three levels of investment. (Cohousers generally advise against various levels of investment for common facilities except when a small group wants to purchase something less than half of the residents are likely to use, such as a table saw or a sailboat.)</p>
<p>C. <em>Children</em>. Some feel that children should have full access to the workshop (except power tools), to encourage their spontaneous creativity. For safety and to prevent abandoned projects, others feel that children should be permitted only under adult supervision. Between the permissive and disciplinarian attitudes there is a practical middle ground. Adults should aggressively teach interested children "shop manners" - safety first, how to clean up, and how not to abuse tools. Once they have demonstrated competent shop manners, children should be given equal access. Until then, they might have a separate bench, with their own tools.</p>
<p>D. <em>Day-Glo</em>. Successful workshops paint their hand tools the most awful Day-Glo fluorescent colors available, 'so you can see those suckers 50 yards away lying in two feet of grass'. Besides, no self-respecting person would want tools that color in their house.</p>
</blockquote>
<p>I agree with most of this, but I think <em>Buying in</em> would need to be adjusted for a space that wants to welcome those who otherwise could not afford access to such a space, and I don't think the section in children should be just limited to kids. Everyone should have to show some basic competence with respect to safety and clean-up.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sun, 08 Oct 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">15</guid></item><item><title>The Green Imperative By Victor Papanek</title><link>http://dizzard.net/articles/the_green_imperative/article.html</link><description><![CDATA[<h1>The Green Imperative By Victor Papanek</h1>
<p>The book focuses much more around Design than I initially expected. It's not very in your face about what I thought would be the immediate demand to put the environment first. <a href="https://en.wikipedia.org/wiki/Victor_Papanek">Papanek</a> instead meanders around the influence of design on consumption, with a particularly strong distaste for Mid-Century Modern design and the Bauhaus movement. The connection he draws between simple, undecorated furniture and human consumption is a little unclear to me, but he's spending a lot of time talking about how humans have a long tradition of adding superfluous decorations to objects that would otherwise be purely utility.</p>
<p>It's actually a lot more related to <a href="https://eyeondesign.aiga.org/in-caps-lock-ruben-pater-untangles-the-relationship-between-graphic-design-and-capitalism/">Caps Lock</a> than I expected. Discussions of design collectives, the things that prevent designers from designing for just purposes, etc.</p>
<p>I just feel that too much has changed since it's original publishing in 1995. The Green Imperative, in my opinion, takes a rather lackadaisical approach to designing for reslilience in the face of climate change, but the world Papanek was living in was different than the world we live in now.</p>
<hr />
<blockquote>
<p>Our biogenetic heritage governs expectations of size, weight, distance, speed and time. 'Old' measurements like mile, pound, yard, foot, stone, reflect what we can lift or carry easily, and how we used our bodies as templates for measurement. As explained in Chapter 4, the eidetic image that we carry with us through life gives us another system for judging harmonious relationships and scale. 'Invented' measuring systems, like the metric system, are as meaningless to us as is the time displayed on a digital watch.</p>
</blockquote>
<p>What the hell is Papanek saying here? All units of measure are made up. How does 12” in a foot make it more harmonious? The page before this Papanek is talking about how the slums of Rio are positioned at the highest, most beautiful place in the city because that’s where people would go naturally. I’ve got a few more pages in me before I call bullshit on this goon.</p>
<hr />
<h2>Chapter 6: The lessons of vernacular architecture</h2>
<p>Honestly, I feel like this whole book so far has mostly been about Papanek’s obsession with vernacular design. </p>
<hr />
<h2>Chapter 7: Form Follows Fun</h2>
<blockquote>
<p>What do you do when the real place looks like the copy of a fake - Student</p>
</blockquote>
<p>This dude has some serious distaste for the young and new. He starts the chapter with two quotes from students that gives the impression that youth <em>cannot</em> appreciate something that is real, and not carefully curated.</p>
<hr />
<blockquote>
<p>Even though designers realize that the objects they bring into life will last only briefly in the marketplace, they still wish that - even in everyday use - their 'babies' might somehow remain unblemished and 'perfect'. At international conferences the design chief of a firm that made cigarette lighters would meet fellow designers who still carried their original black lighter. After years of being kept in a pocket with keys, a penknife or whatever, nicks and scratches on the black finish would cause the original brass casing to glint through. The designer would obligingly offer a free replacement then and there. I remember saying to him myself, 'No thank you, all the scratches are just beginning to give it character, making it unique!' In his search for perfection, he was never able to understand that things can't remain unchanged. In an age in which fortunes are spent on beauty creams and lotions to stretch out a semblance of youth, it is difficult to explain that material choices should be made so that objects can age gracefully once more.</p>
</blockquote>
<p>I vibe with this. I still have a hard time realizing this in my own thoughts though. Often I find myself disatisfied with blemishes and marks of wear.</p>
<hr />
<blockquote>
<p>One of my design clients in Japan is a subsidiary of a large automobile company. Visiting their design laboratory, I saw a photograph of former President Reagan pinned on a bulletin board. 'Reagan is one of the greatest Japanese car designers, I was told, 'He bullied us into exporting fewer cars to America. So now we produce much larger cars for the American market. We export fewer numbers, but make a much higher profit with these over-sized models.'</p>
</blockquote>
<p>Of course Reagan is to blame for this too! This belongs on <a href="https://www.reddit.com/r/fuckcars/">r/fuckcars</a>. And of course , when you start to think he’s gonna say rationally that importing larger cars to the US is bad because it contributes to visual, noise, and air pollution, without providing much, if any practical benefit, who is allegedly advocating for buying <em>less</em> because it is <em>green</em> says in the following paragraph:</p>
<blockquote>
<p>… tiny is not necessarily better.</p>
</blockquote>
<p>GODDAMMIT! I feel like this entire book wss written just to piss me off. He rounds this section off by saying washing dishes by hand is more water-efficient than dishwashers. I’ll give him the benefit of the doubt here since this book came out in the ‘90’s, but modern dishwashers use significantly less water than hand washing no matter how you cut it.</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Tue, 09 May 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">14</guid></item><item><title>How to capture code snippets from Obsidian and add to Alfred Snippets</title><link>http://dizzard.net/articles/obsidian_sniper/article.html</link><description><![CDATA[<h1>How to capture code snippets from Obsidian and add to Alfred Snippets</h1>
<p>At work, I'm often looking through my <a href="https://obsidian.md/">Obsidian</a> notes looking for a certain code snippet to run to update a build version, deploy my server, or some other mundane task I can't be bothered to memorize. And it's nice having obsidian because then not only do I have a home for these little snippets of code, but I can also provide some notes on the snippets, like what it does, why it's written a certain way, what to try if it fails, etc. Sometimes I need this context, but a lot of the times I just need the snippet of code.</p>
<p>Instead of having to take all these snippets, scrub through them to find what's important, and manually add to <a href="https://www.alfredapp.com/">Alfred</a>, I thought I could do it in an automated fashion. It seemed like a good opportunity to use <a href="https://en.wikipedia.org/wiki/AWK"><code class="codeblock">awk</code></a>, even though I didn't really understand how it was used. All I knew is that <code class="codeblock">awk</code> is good for parsing through files in a stream, and really that was all I needed to know.</p>
<p>I just had to come up with a way to identify the files I wanted to include as snippets with as little work as possible, and that is where my little scripts name comes from! Introducing <code class="codeblock">sniper</code>, the handy-dandy tool for cloning your obsidian code blocks in Alfred snippets. All you have to do is prefix a code block with the Sniper keyword "snipe" in a markdown comment. This isn't a "smart" utility, so you got to make sure the comment only contains the word "snipe", and nothing else. Here's an example:</p>
<p><img src="http://dizzard.net/images/obsidian_codeblock.jpg" alt="" /></p>
<p>To get your flagged snippets to appear in alfred, you will first want to create a new snippets group. Open up "Settings -> Features -> Snippets -> Collections" and click the little "+" button. Then type in the name of the collection. I named mine "Random Code Snippets", but you can name it whatever you want, just make sure to update the bash script variable <code class="codeblock">$ALFRED_SNIPPETS_DIR</code> so it matches your collection.</p>
<p><img src="http://dizzard.net/images/alfred_snippets_collection.jpg" alt="" /></p>
<p>You also need to update <code class="codeblock">$ALFRED_SNIPPETS_DIR</code> so it know where your Alfred config directory is. You can find where your alfred preferences file is by going to "Settings -> Advanced -> Syncing". I keep mine in <code class="codeblock">.config</code> with the rest of my dotfiles</p>
<p><img src="http://dizzard.net/images/alfred_preferences.jpg" alt="" /></p>
<p>My directory path looks like:</p>
<div class="codehilite">
<pre><span></span><code><span class="nv">ALFRED_SNIPPETS_DIR</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$HOME</span><span class="s2">/.config/Alfred.alfredpreferences/snippets/Random Code Snippets&quot;</span>
</code></pre>
</div>
<h2>The Code</h2>
<p>OK, so with the basic setup figured out, you just need this snippet of code (snippets all the way down)!</p>
<div class="codehilite">
<pre><span></span><code><span class="ch">#!/bin/bash</span>
<span class="c1"># Script for swiping snippets (sniping) from obsidian into alfred</span>
<span class="nv">ALFRED_SNIPPETS_DIR</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$HOME</span><span class="s2">/.config/Alfred.alfredpreferences/snippets/Random Code Snippets&quot;</span>
snipe <span class="o">()</span> <span class="o">{</span>
<span class="nv">MARKDOWN_FILE</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="nv">$0</span> <span class="p">|</span> cut -c3-<span class="k">)</span>
<span class="nv">FILENAME</span><span class="o">=</span><span class="k">$(</span>basename <span class="s2">&quot;</span><span class="nv">$0</span><span class="s2">&quot;</span><span class="k">)</span>
<span class="nv">UUID</span><span class="o">=</span><span class="k">$(</span>uuidgen<span class="k">)</span>
<span class="nv">SNIPPET_FILENAME</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">/</span><span class="nv">$FILENAME</span><span class="s2"> [</span><span class="nv">$UUID</span><span class="s2">].json&quot;</span>
<span class="nv">SNIPPET</span><span class="o">=</span><span class="k">$(</span>awk <span class="s1">&#39;BEGIN { start=0 } /%%snipe%%/ { start=1; next } start &amp;&amp; /^```/ { code=1; start=0; next } code &amp;&amp; /^```/ { code=0; next } code { printf &quot;%s\\n&quot;, $0 } END { }&#39;</span> <span class="s2">&quot;</span><span class="nv">$MARKDOWN_FILE</span><span class="s2">&quot;</span><span class="k">)</span>
<span class="k">if</span> <span class="o">[</span> -z <span class="s2">&quot;</span><span class="nv">$SNIPPET</span><span class="s2">&quot;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
<span class="k">return</span> <span class="m">1</span>
<span class="k">fi</span>
touch <span class="s2">&quot;</span><span class="nv">$SNIPPET_FILENAME</span><span class="s2">&quot;</span>
<span class="nv">ENCODED_SNIPPET</span><span class="o">=</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">SNIPPET</span><span class="p">//</span><span class="se">\&quot;</span><span class="p">/</span><span class="se">\\\&quot;</span><span class="si">}</span><span class="s2">&quot;</span> <span class="c1"># Replace &quot; with \&quot;</span>
<span class="nb">printf</span> <span class="s2">&quot;{\n&quot;</span> &gt;&gt; <span class="nv">$SNIPPET_FILENAME</span>
<span class="nb">printf</span> <span class="s2">&quot;\&quot;alfredsnippet\&quot; : {\n&quot;</span> &gt;&gt; <span class="nv">$SNIPPET_FILENAME</span>
<span class="nb">printf</span> <span class="s2">&quot;\&quot;snippet\&quot; : \&quot;%s\&quot;,\n&quot;</span> <span class="s2">&quot;</span><span class="nv">$ENCODED_SNIPPET</span><span class="s2">&quot;</span> &gt;&gt; <span class="nv">$SNIPPET_FILENAME</span>
<span class="nb">printf</span> <span class="s2">&quot;\&quot;uid\&quot; : \&quot;%s\&quot;,\n&quot;</span> <span class="nv">$UUID</span> &gt;&gt; <span class="nv">$SNIPPET_FILENAME</span>
<span class="nb">printf</span> <span class="s2">&quot;\&quot;name\&quot; : \&quot;%s\&quot;,\n&quot;</span> <span class="s2">&quot;</span><span class="nv">$FILENAME</span><span class="s2">&quot;</span> &gt;&gt; <span class="nv">$SNIPPET_FILENAME</span>
<span class="nb">printf</span> <span class="s2">&quot;\&quot;keyword\&quot; : \&quot;\&quot;\n&quot;</span> &gt;&gt; <span class="nv">$SNIPPET_FILENAME</span>
<span class="nb">printf</span> <span class="s2">&quot;}}\n&quot;</span> &gt;&gt; <span class="nv">$SNIPPET_FILENAME</span>
<span class="o">}</span>
<span class="c1"># Clear out anything already in directory</span>
rm <span class="s2">&quot;</span><span class="nv">$ALFRED_SNIPPETS_DIR</span><span class="s2">/&quot;</span>*.json
<span class="nv">FUNCDEFS</span><span class="o">=</span><span class="k">$(</span><span class="nb">typeset</span> -f snipe<span class="k">)</span> find . -name <span class="s2">&quot;*.md&quot;</span> -type f -exec sh -c <span class="s1">&#39;</span>
<span class="s1"> eval &quot;$FUNCDEFS&quot; &amp;&amp;</span>
<span class="s1"> unset -v FUNCDEFS &amp;&amp;</span>
<span class="s1"> snipe &quot;$@&quot;&#39;</span> <span class="o">{}</span> <span class="s2">&quot;</span><span class="nv">$ALFRED_SNIPPETS_DIR</span><span class="s2">&quot;</span> <span class="se">\;</span>
</code></pre>
</div>
<p>I added this to a directory on my path <code class="codeblock">~/bin</code> so I can easily execute it. To use it:</p>
<ol class="list-decimal">
<li>Navigate to your Obsidian vault directory</li>
<li>Run the script (e.g. <code class="codeblock">sniper</code>) without any arguments</li>
<li>That's all. Note that this <em>erases</em> anything already in the snippets collection, which is why I encourage you to create a new one.</li>
<li>Sometimes, I've noticed the snippets don't update immediately, so I had to clear Alfred's cache (Settings -> Advanced -> Clear Application Cache)</li>
</ol>
<h2>Notes</h2>
<ul>
<li>Yeah, this could probably be done with an Obsidian or Alfred extension, but this is one of the simplest possible options for me, and I like knowing what's going on.</li>
<li>This is not so much a tutorial, but if you have questions, let me know. ChatGPT helped me a lot with the scripting, but I learned a lot too.</li>
<li>I encourage you to take a look at this and play around with it. There are some limitations, like only allowing one snippet per file.
The filename is also used as the snippet name, so if the file name doesn't really correspond with the snippet, that might be confusing.
But hey if you keep atomic notes (which I <em>totally definitely</em> do (not)), it shouldn't be an issue.</li>
</ul>
<p>That's all for now. Happy hacking :P</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sun, 07 May 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">13</guid></item><item><title>Writing a Discord bot with Python and aiocron</title><link>http://dizzard.net/articles/discord_bot/discord_bot.html</link><description><![CDATA[<h1>Writing a Discord bot with Python and aiocron</h1>
<p>I wanted to write a Discord bot to provide simple scheduled alerts for the <a href="https://noisebridge.net">hackerspace</a> I am a part of. We have a channel for the woodshop, and every so often, we need to empty the dust collector and scrap bins.</p>
<p>After some basic research, I found it was pretty straightforward how to set up a bot in the discord developer portal, so I'm not going to go through that here, but if you need help, you can check out <a href="https://realpython.com/how-to-make-a-discord-bot-python/">this realpython article</a>.</p>
<p>Instead, I want to focus on a snag I ran into while trying to use <a href="https://github.com/gawel/aiocron">aiocron</a> with <a href="https://discordpy.readthedocs.io/en/stable/">discord.py</a>. The python discord library now offers some neat tricks for timing method runs with the annotation <code class="codeblock">@tasks.loop(...)</code>, but this doesn't let you <em>schedule</em> async method calls by day or time, so you have to provide some extra code to make sure you start the loop at the right moment in time for it to work correctly. aiocron is neat because it uses crontab format for scheduling method calls. </p>
<p>Now, when I first tried to get this running, I encountered issues with the event loop. My understanding is that the discord library made <a href="https://discordpy.readthedocs.io/en/stable/migrating.html?highlight=2%200#asyncio-event-loop-changes">some changes</a> in it's 2.0 version that allows you to create your own asynchronous loop for running the bot. If you <em>don't</em> use this loop, I'm not sure how you set up other asynchronous code.</p>
<div class="codehilite">
<pre><span></span><code><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">discord</span>
<span class="kn">import</span> <span class="nn">aiocron</span>
<span class="kn">import</span> <span class="nn">asyncio</span>
<span class="n">TOKEN</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s1">&#39;DISCORD_TOKEN&#39;</span><span class="p">)</span>
<span class="n">GUILD</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s1">&#39;DISCORD_GUILD&#39;</span><span class="p">)</span>
<span class="n">WOODSHOP_CHANNEL</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s1">&#39;WOODSHOP_CHANNEL&#39;</span><span class="p">)</span>
<span class="n">trash_message</span> <span class="o">=</span> <span class="s2">&quot;This is your friendly reminder to take out the shop bins and empty the dust collector!&quot;</span>
<span class="n">bot</span> <span class="o">=</span> <span class="n">commands</span><span class="o">.</span><span class="n">Bot</span><span class="p">(</span><span class="n">case_insensitive</span><span class="o">=</span><span class="n">TRUE</span><span class="p">,</span> <span class="n">intents</span><span class="o">=</span><span class="n">discord</span><span class="o">.</span><span class="n">Intents</span><span class="o">.</span><span class="n">all</span><span class="p">(),</span> <span class="n">help_command</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
<span class="n">loop</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">get_event_loop</span><span class="p">()</span>
<span class="nd">@aiocron</span><span class="o">.</span><span class="n">crontab</span><span class="p">(</span><span class="s1">&#39;0 12 * * 0&#39;</span><span class="p">,</span> <span class="n">start</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">loop</span><span class="o">=</span><span class="n">loop</span><span class="p">)</span> <span class="c1"># every sunday at noon</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">trash_reminder</span><span class="p">():</span>
<span class="n">woodshop_channel</span> <span class="o">=</span> <span class="n">bot</span><span class="o">.</span><span class="n">get_channel</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">WOODSHOP_CHANNEL</span><span class="p">))</span>
<span class="k">await</span> <span class="n">woodshop_channel</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">trash_message</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="k">async</span> <span class="k">with</span> <span class="n">bot</span><span class="p">:</span>
<span class="n">trash_reminder</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">await</span> <span class="n">bot</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="n">TOKEN</span><span class="p">)</span>
<span class="n">loop</span><span class="o">.</span><span class="n">run_until_complete</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></pre>
</div>
<p>By using the variable <code class="codeblock">loop</code> as my async loop, I can provide that to the crontab annotation, and voila! Things start working.</p>
<p>Now, if you are like me and don't know that format off the top of your head, what I did is ask old ChatGPT, who gave a prompt answer and description. Work smarter, not harder!</p>
<p>Not so much of a tutorial, but I'm hoping someone finds this code snippet helpful, because I couldn't find an example of this myself. I did, however find <a href="https://www.reddit.com/r/Discord_Bots/comments/vxknqz/aiocroncronjob_not_working_with_discordpy_v20/">some</a> <a href="https://github.com/Rapptz/discord.py/issues/7986">people</a> running into similar issues.</p>
<p>Anyways, happy hacking!</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sat, 22 Apr 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">12</guid></item><item><title>This blog post is automatically published</title><link>http://dizzard.net/articles/automated_blogging/automated_blogging.html</link><description><![CDATA[<h1>Automated Blog Publishing</h1>
<p>Previously, I wrote a blog post about deploying my blog with Github Pages. Now, I want to be able to write blog posts from my phone and publish them without touch git. So I thought it would be neat if I could edit the files on my iPhone by connecting to one of my always-on raspberry pi's, and automate the deployment of the blog from the raspberry pi. If this works, I'll write more about it. I'm still testing things out, and I'm trying to get <a href="https://github.com/gorakhargosh/watchdog">watchdog</a> to handle the watching for me. If this post is live though, the automated publish worked!</p>
<p>P.S. it also handles blog updates (this is an edit to the original post)</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Thu, 09 Mar 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">11</guid></item><item><title>Earth 🪐 to Localhost</title><link>http://dizzard.net/articles/apple_file_protocol/apple_file_protocol.html</link><description><![CDATA[<h1>Earth 🪐 to Localhost...</h1>
<p>This is the first note I've written on my locally-hosted network attached storage setup. This is currently running on the <a href="https://en.wikipedia.org/wiki/Apple_Filing_Protocol">Apple Filing Protocol</a>. This is possible thanks to the reimplementation of the AFP protocol by the <a href="https://netatalk.sourceforge.io/">Netatalk</a> project, which is a "free and open source AFP fileserver."</p>
<p>AFP I guess is deprecated on Mac's, so they cannot <em>serve</em> data via AFP, but they can still login to other networks running the protocol. I'm running this on my raspberry pi, which is awfully slow, but who cares? Once it's loaded, it's loaded. I've got access to my files, and they are mostly text, so it really doesn't matter.</p>
<p>There is a problem though. The iOS files app does <em>not</em> support the AFP protocol. Even though it's Apple's own protocol, as far as I can tell, it <em>was</em> never supported on mobile. So next step for me will be converting my notes file server to the Server Message Block (SMB) protocol. This was originally created by Microsoft, but an implementation called <a href="https://ubuntu.com/server/docs/samba-file-server">Samba</a> allows Unix-like devices to interface with Windows, and other Unix-like devices. Pretty neat! I don't use Window often, but I do have a PC, so I'd like it to be supported.</p>
<h2>An aside...</h2>
<p>Today is also the last day of January. One month down in 2023!</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Tue, 31 Jan 2023 00:00:00 -0000</pubDate><guid isPermaLink="false">10</guid></item><item><title>Interesting Blogs and Bloggers</title><link>http://dizzard.net/articles/interesting_blogs/Interesting%20Blogs.html</link><description><![CDATA[<h1>Some Interesting Blogs I've Stumbled Upon</h1>
<p>I will update this from time to time as I find more people I want to share. These are mostly technology focused.</p>
<h1>MacOS App Developers</h1>
<ul>
<li><a href="https://ryanhanson.dev/">Ryan Hansen</a>
<ul>
<li>maker of rectangle.app</li>
</ul></li>
<li><a href="https://evantravers.com/">Evan Travers</a>
<ul>
<li>makes some interesting apps for productivity</li>
</ul></li>
</ul>
<h1>Digital Gardens</h1>
<ul>
<li><a href="https://andymatuschak.org/">Andy Matuschak</a></li>
<li><a href="https://wiki.nikitavoloboev.xyz/">Nikita Voloboev</a></li>
<li><a href="https://Doubleloop.net">Double Loop</a></li>
<li><a href="https://rachsmith.com/">Rach Smith</a></li>
<li><a href="https://jzhao.xyz/">Jackie Zhao</a></li>
</ul>
<h1>Bloggers</h1>
<ul>
<li><a href="https://darekkay.com/">Derek Kay</a></li>
<li><a href="https://blog.codinghorror.com/">Jeff Atwood</a>
<ul>
<li>founder of stack overflow</li>
</ul></li>
<li><a href="https://ma.tt/">Matt Mulenwhegg</a>
<ul>
<li>The founder of Wordpress/Automattic</li>
</ul></li>
<li><a href="https://www.arp242.net">Martin Tournoij</a>
<ul>
<li>random little open source projects, like [[Goatcounter]]</li>
</ul></li>
<li><a href="https://tpo.pe/">Tim Pope</a>
<ul>
<li>Pope is a total nut and the king of vim plugins</li>
</ul></li>
</ul>
<h1>Game Developers</h1>
<ul>
<li><a href="http://www.stfj.net/">Zach Gage</a>
<ul>
<li>Made that iOS fishing game you liked so much</li>
</ul></li>
</ul>
<h1>Unix/Linux Developers and Ricers</h1>
<ul>
<li><a href="https://waylonwalker.com/">Waylon Walker</a>
<ul>
<li>My go to for #tmux keybinding help. He has some great guides for this. <em>Update ([[2022-12-17]])</em>: these days I'm not using tmux, I replaced it with kitty terminal, which fits pretty much all my needs.</li>
</ul></li>
</ul>
<h1>Obsidian</h1>
<ul>
<li><a href="https://kevinquinn.fun/">Kevin Quinn</a></li>
<li><a href="https://chris-grieser.de/ca0f246f224c457b8d641a665dde1f7d">Chris Grieser (PseudoMeta)</a>
<ul>
<li>Chris makes a lot of great plugins/addons for various apps like Obsidian and Alfred</li>
</ul></li>
</ul>
<h1>VR/AR</h1>
<ul>
<li><a href="https://kguttag.com/">Karl Guttag</a></li>
</ul>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Tue, 27 Dec 2022 00:00:00 -0000</pubDate><guid isPermaLink="false">9</guid></item><item><title>Thoughts on Hackerspaces</title><link>http://dizzard.net/articles/hackerspaces/hackerspaces_to_me.html</link><description><![CDATA[<h1>Thoughts on Hackerspaces</h1>
<p>What is a <a href="https://hackerspaces.org/">hackerspace</a> to me?</p>
<p>It's a free place for a community of individuals to gather, share knowledge and resources, and build the things they want to see in the world. It makes me think of co-housing in a lot of ways. Although there is no required <em>financial</em> obligation, there is a requirement that you treat others well and share your knowledge and skills when you can. <a href="https://noisebridge.net">Noisebridge</a>, the hackerspace in San Francisco I've been finding myself at recently, has only one formal rule: Be excellent to each other. It may sound a bit cheesy, but for the most part, it seems to be working. The space has been running for 15 years.</p>
<p>NB is officially a 501(c)(3) non-profit organization, but the word <em>organization</em> is loosely interpreted here. That is because it's also described as anarchist in its nature meaning there is no formal structure in the space. That's not to say there aren't regular meetings, but there is no hierarchy, no reports. People have self-assigned responsibilities, but there are no obligations. This I think is essential to any well functioning co-space, and I would think the same would be necessary for a healthy cohousing community (if you add a landlord, then the cohousing community just becomes an apartment building that happens to have laundry in the basement).</p>
<p>With any radically inclusive, shared space, you are bound to deal with something you don't like. Sometimes people will leave a mess, or you'll find something to be broken. Maybe someone doesn't put something back in the right place, or they might even steal it!</p>
<p>But with all this also comes radically wonderful things. People donate their time and resources to make it an amazing space. There is no other place in the city with access to as many resources as NB, paid or otherwise. With 3D printers, industrial laser cutters, full format laser and inkjet printers, an embroidery machine, screen-printing equipment, a <em>full</em> woodshop, metal shop, and so much more, it really is a place where <em>anyone</em> can come and build just about <em>anything</em>. <a href="https://www.humanmade.org/facility">Humanmade</a>, which is also a non-for-profit makerspace in <a href="https://en.wikipedia.org/wiki/South_of_Market,_San_Francisco">SF's SoMa district</a> doesn't even have as many tools as NB, <em>and</em> that place costs <em>$3000 a year</em>!</p>
<p>Since these hackerspaces are freely accessible to all, they are very much a reflection of the city they are in. That comes with all its benefits and its baggage, and I'm here for both. This space is unique, and since it is ran by the community for the community, you have an equal opportunity to make it the kind of space you want it to be. If there is something that you want to see changed, you can change it without going through a bureaucratic process. You <em>just do it</em>. And that sometimes leads to those conflicts where you find someone changed something you liked a certain way, but as long as the community continually communicates and tries to be excellent to each other, it works out. It's never going to be exactly how <em>you</em> want it because it's not just your space. But the thing is, for just about everyone at these spaces — it is infeasible to have a space like this to yourself. You give up some control, and you get an awful lot in return.</p>
<p>The hackerspace fits into a theme I've been exploring this past year and I have to write more about as I continue exploring it in 2023. I'm not yet sure how to group these thoughts together, but it circles around some of the ideas I see in socialism, anarchism, communism in a general sense and more specifically third spaces, cohousing, community centers, resource sharing, donation circles, free and open source software, decentralized networks, and the small web. I'll try to share more. I think I need to sort some of these thoughts out, but I just want to find ways for people to share more, because I think it's good for us psychologically and it reduces waste.</p>
<p>Happy Holidays, see you in the new year 🥳!</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Wed, 21 Dec 2022 00:00:00 -0000</pubDate><guid isPermaLink="false">8</guid></item><item><title>I'm Counting Goats!</title><link>http://dizzard.net/articles/goatcounter/goatcounter.html</link><description><![CDATA[<h1>I'm Counting Goats!</h1>
<p>Queue “I’ll be watching you” by The Police, because I’m now spying on all the viewers of this blog!</p>
<p>Just kidding. Well, sort of. I wanted to have some metric of how many people are viewing my blog, not because I’m going to sell ad space, or leverage that information for some other malicious reason. I just wanted to know if anyone reads it! And like I said, to respect my readers’ privacy, I wanted to take as little data as possible. That’s why I decided to use <a href="https://www.goatcounter.com/">GoatCounter</a>. It keeps a count of when someone’s checking out my site, but it’s not tracking all the personal data that google analytics is going to.</p>
<p>It was super easy to set up, you can inspect this page and see the script yourself. It’s just one line :).</p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Wed, 21 Dec 2022 00:00:00 -0000</pubDate><guid isPermaLink="false">7</guid></item><item><title>Deploy Github Pages with Jinja and Github Actions</title><link>http://dizzard.net/articles/gh-pages/Deploy%20Github%20Pages%20with%20Jinja%20and%20Github%20Actions.html</link><description><![CDATA[<h1>Deploy Github Pages with Jinja and GH Actions</h1>
<p>This blog, like many others is hosted on <a href="https://pages.github.com/">Github Pages</a>. Github Pages is a great (and free) way to post small static sites like personal websites. The annoying thing is that you have to push build code that would <em>usually</em> be included in your <code class="codeblock">.gitignore</code> file. So if you don't want to have to build your site locally, and include the build files in your repository, there is an option for you that leverages Github Actions.</p>
<p>Now, if you haven't heard of <a href="https://github.com/features/actions">Github Actions</a>, you should check it out. It's Github's platform for CI/CD, and it's easily configurable with .yaml files. There's nothing special you have to do for a repository, you just include the action files in the <code class="codeblock">.github/workflows</code> directory.</p>
<p><a href="https://github.com/momja/momja.github.io">My site repository</a> is currently being deployed with a <a href="https://github.com/JamesIves/github-pages-deploy-action">github action</a> written by <a href="https://github.com/JamesIves">@JamesIves</a>. The sample <code class="codeblock">.yaml</code> file looks like this:</p>
<div class="codehilite">
<pre><span></span><code><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Build and Deploy</span><span class="w"></span>
<span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="nv">push</span><span class="p p-Indicator">]</span><span class="w"></span>
<span class="nt">permissions</span><span class="p">:</span><span class="w"> </span>
<span class="w"> </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">write</span><span class="w"></span>
<span class="nt">jobs</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">build-and-deploy</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ubuntu-latest</span><span class="w"></span>
<span class="w"> </span><span class="nt">steps</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Checkout 🛎️</span><span class="w"></span>
<span class="w"> </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">actions/checkout@v3</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Install and Build 🔧</span><span class="w"></span>
<span class="w"> </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">|</span><span class="w"></span>
<span class="w"> </span><span class="no">npm ci</span><span class="w"></span>
<span class="w"> </span><span class="no">npm run build</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Deploy 🚀</span><span class="w"></span>
<span class="w"> </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">JamesIves/github-pages-deploy-action@v4.3.3</span><span class="w"></span>
<span class="w"> </span><span class="nt">with</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">branch</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gh-pages</span><span class="w"></span>
<span class="w"> </span><span class="nt">folder</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">build</span><span class="w"></span>
</code></pre>
</div>
<p>There's very little here we have to change to get this working for us.</p>
<ol class="list-decimal">
<li><p>First, make sure this section matches the branch your github site is hosted on. You can check which branch is currently used by opening your github pages repository then:</p>
<p>Settings -> Code and Automation -> Pages -> Source.</p>
<div class="codehilite">
<pre><span></span><code><span class="nt">with</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">branch</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gh-pages &lt;- make sure this matches &quot;branch&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">folder</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">build</span><span class="w"></span>
</code></pre>
</div>
<p><img src="http://dizzard.net/images/setting-gh-pages.jpg" alt="../../images/setting-gh-pages.jpg" /></p></li>
<li><p>Now we need to update the "Install and Build 🔧" section. There are a couple things we have to do for this to work, and it will vary for you depending on how your site is being built. I'm generating the static pages of my site using the Jinja web templating engine, and then I inject style with Postcss and Tailwind. This means I need to run a python script to generate the HTML files, then run a postcss injection to transform my css. I've got <a href="https://github.com/momja/momja.github.io/blob/master/package.json">all this configured</a> in my 'package.json' file, so whenever I run <code class="codeblock">npm run build</code>, all this is taken care of. At least that all happens smoothly locally. When running this on some ephemeral virtual machine, you have to actually make sure all the dependencies are downloaded first. For the postcss dependencies, that's as simple as running <code class="codeblock">npm install</code> assuming you've correctly set up your packages.</p>
<p>For Python, it is a little different. I'm not the biggest fan of dependency management in Python, but I recently came across a tool called <a href="https://github.com/bndr/pipreqs">pipreqs</a> which solves the challenges I associate with generating package requirements for Python. To build a requirements.txt file with pipreqs, just run <code class="codeblock">pipreqs</code> in your project directory. You will want to include this requirements file in your git commit, because we will then use it to install all the pip dependencies on the VM used by Github Actions!</p>
<div class="codehilite">
<pre><span></span><code><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Install and Build 🔧</span><span class="w"></span>
<span class="w"> </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">|</span><span class="w"></span>
<span class="w"> </span><span class="no">npm ci</span><span class="w"></span>
<span class="w"> </span><span class="no">npm run build</span><span class="w"></span>
</code></pre>
</div>
<p>becomes:</p>
<div class="codehilite">
<pre><span></span><code><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Install and Build 🔧</span><span class="w"></span>
<span class="w"> </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">|</span><span class="w"></span>
<span class="w"> </span><span class="no">pip install -r requirements.txt</span><span class="w"></span>
<span class="w"> </span><span class="no">npm install</span><span class="w"></span>
<span class="w"> </span><span class="no">npm run build</span><span class="w"></span>
</code></pre>
</div></li>
<li><p>Lastly, I didn't want the <code class="codeblock">gh-pages</code> branch to be updated each time a change is made to <em>any</em> branch, so I changed the yaml field at the top of the file for when the action should be triggered:</p>
<div class="codehilite">
<pre><span></span><code><span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="nv">push</span><span class="p p-Indicator">]</span><span class="w"></span>
</code></pre>
</div>
<p>becomes:</p>
<div class="codehilite">
<pre><span></span><code><span class="nt">on</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">push</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">branches</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">master</span><span class="w"></span>
</code></pre>
</div></li>
</ol>
<p>And there you have it! Check out the final file <a href="https://github.com/momja/momja.github.io/blob/38e1a2985c1983cbd35c07d970318d8743cb63ba/.github/workflows/deploy-to-gh-pages.yml">here</a></p>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Tue, 14 Jun 2022 00:00:00 -0000</pubDate><guid isPermaLink="false">6</guid></item><item><title>Migrating from tmux to Kitty</title><link>http://dizzard.net/articles/tmux-kitty/tmux-kitty.html</link><description><![CDATA[<h1>Migrating from Tmux to Kitty</h1>
<p>So sitting at my workdesk waiting for my local changes to compile and deploy, I often find my mind wandering towards the world of <a href="https://www.reddit.com/r/unixporn/">Unix ricing</a>. Some people do it for beauty, others do it for maximizing their productivity. Regardless of the reason, before I go into any of this, let me preface the article by saying that all this is completely unnecessary. Pretty much any terminal will do the trick. But once again, I'm twiddling my thumbs waiting for deployment, so I need <em>something</em> to do. Why not finally migrate my terminal work from <a href="https://github.com/tmux/tmux">tmux</a> in <a href="https://iterm2.com/index.html">iTerm2</a> to <a href="https://sw.kovidgoyal.net/kitty/">Kitty</a>?</p>
<p>If you haven't heard of Kitty, first of all, I commend you for not wasting as much time as I do reading about development tools instead of actually working! Kitty is a modern GPU-accelerated terminal emulator available for Linux and macOS. It's written in C and python, and it's pretty extensible. It has <a href="https://sw.kovidgoyal.net/kitty/kittens/custom/">support for plugins</a> called kittens, and comes with some default ones like a theme-switcher and ssh utility.</p>
<p>Kitty offers much of the functionality of tmux - things like pane management, custom keybindings, remote control. <em>But</em>, Kitty does have some distinct advantages in my mind. For one, Kitty is a terminal <em>emulator</em>. Tmux is only a terminal <em>multiplexer</em>, and thus also requires a terminal emulator. I'd prefer to simplify the number of tools I use for my work. Kitty can replace both tmux and iTerm2, and adds some features on top like the cool cat that it is. Already mentioned is plugin support, which iTerm2 does technically support, but I always got confused trying to configure it.</p>
<p>Next and most exciting is the inline native image support. Now some terminals support something called ==<a href="#TODO">sixel graphics</a>==, which I'll admit is pretty neat. Sixel to me is a bandage fix for graphic representation in the terminal because it's not representing imgages as native images, but rather performing some computation to build images out of special ASCII symbols, which means when you try to translate or transform the image in any way, things get weird. Why deal with that when kitty has your back? It can even render GIFs!</p>
<pre class="codewrapper"><code class="codeblock">kitty +kitten icat ~/Downloads/kitten_falling.gif
</code></pre>
<p><img src="http://dizzard.net/images/animation.gif" alt="Kitty terminal output with the icat plugin" /></p>
<p>Another reason I think Kitty is great is that it's text file configurable, which makes it <em>way</em> easier to share configurations between my work and personal computer. You can find an up-to-date copy of my kitten config file in <a href="https://github.com/momja/dotfiles/blob/master/kitty/kitty.conf">my dotfiles repository</a></p>
<h2>Reproducing my tmux configuration in Kitty</h2>
<p>I'd changed all the keybindings in tmux to match how I navigate everything else. And since I use vim keybindings in all my editors, I thought it would be nice to keep "HJKL" in use with tmux for navigating between panes and windows. I wanted to navigate between panes by simply pressing <code class="codeblock">ctrl-[HJKL]</code> to jump between windows vertically and horizontally, and if I wanted to create a new window, I'd press my tmux prefixer, followed by a directional key to specify where I wanted to add that new split. Kitty as far as I know doesn't have prefix keys like tmux, so I wasn't able to keep that around, but I'm OK with that. Instead, I just added another modifier key. So when I want to create a new pane, just press <code class="codeblock">ctrl-opt-[HJKL]</code>
<br></p>
<div class="codehilite">
<pre><span></span><code><span class="c1"># Create a new window splitting the space used by the existing one so that</span>
<span class="c1"># the two windows are placed one above the other</span>
map ctrl+alt+k launch --location<span class="o">=</span>hsplit --cwd<span class="o">=</span>current
<span class="c1"># Create a new window splitting the space used by the existing one so that</span>
<span class="c1"># the two windows are placed side by side</span>
map ctrl+alt+l launch --location<span class="o">=</span>vsplit --cwd<span class="o">=</span>current
<span class="c1"># Rotate the current split, chaging its split axis from vertical to</span>
<span class="c1"># horizontal or vice versa</span>
map ctrl+shift+r layout_action rotate
<span class="c1"># Move the active window in the indicated direction</span>
map ctrl+shift+h move_window left
map ctrl+shift+l move_window right
map ctrl+shift+k move_window up
map ctrl+shift+j move_window down
<span class="c1"># Switch focus to the neighboring window in the indicated direction</span>
map ctrl+h neighboring_window left
map ctrl+l neighboring_window right
map ctrl+k neighboring_window up
map ctrl+j neighboring_window down
<span class="c1"># Switch tabs</span>
map ctrl+cmd+l next_tab
map ctrl+cmd+h previous_tab
enabled_layouts splits:split_axis<span class="o">=</span>horizontal
</code></pre>
</div>
]]></description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Maxwell Omdal</dc:creator><pubDate>Sun, 12 Jun 2022 00:00:00 -0000</pubDate><guid isPermaLink="false">5</guid></item><item><title>Path Planning</title><link>http://dizzard.net/articles/path_planning/path_planning.html</link><description><![CDATA[<p><img src="../../images/header.jpg" style="width:100%; height: auto;" alt=""></p>
<h1>
Path Planning : Bat Guy Vs. The Clown
</h1>
<h2>
Max Omdal
</h2>
<p>
This Path Planning project is an interactive simulation with several interactive parts. Primarily a superhero
and supervillain play a game of cat and mouse. Batma... err.. "Bat Guy" and "The Clown" are caught in this
battle. As Bat Guy, you have the ability to control bats by dropping signaling devices that they will navigate
their way through a maze of water towers to reach. You also will need to make use of your other Super gadgets,
like explosive projectiles, and miniature self-driving probes. Read on to learn about all the features in this
application, as well as how it was implemented.
</p>
<div class="py-5 flex justify-center">
<a href="src/PathPlanningProcessing.zip"
class="scale-normal hover:scale-up bg-indigo-100 hover:bg-indigo-200 text-gray-800 font-bold py-2 px-4 rounded inline-flex">
<i class="fa fa-download fill-current w-4 h-4 mr-2 my-auto"></i>