<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/simple-atom.xslt"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">
  <title>joshua.seigler.net - software</title>
  <subtitle>Personal homepage of Joshua Seigler</subtitle>
  <link href="https://joshua.seigler.net/feeds/software.xml" rel="self" />
  <link href="https://joshua.seigler.net/" />
  <updated>2026-07-06T00:00:00Z</updated>
  <id>https://joshua.seigler.net/</id>
  <author>
    <name></name>
  </author>
    <entry>
      <title>I made myself a Recipe Label Generator</title>
      <link href="https://joshua.seigler.net/posts/i-made-myself-a-recipe-label-generator/" />
      <updated>2026-07-02T00:00:00Z</updated>
      <id>https://joshua.seigler.net/posts/i-made-myself-a-recipe-label-generator/</id>
      <content type="html">&lt;p&gt;I have been dissatisfied with some of the recipe label generators online (except for &lt;a href=&quot;https://tools.myfooddata.com/nutrition-facts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MyFoodData&lt;/a&gt;, which is fantastic, but I wanted something simpler, purpose-built for my site). I also wanted a small personal software project to preserve my familiarity with &lt;a href=&quot;https://www.solidjs.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SolidJS&lt;/a&gt;. So I made &lt;a href=&quot;https://recipelabels.pages.seigler.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;my own recipe label generator&lt;/a&gt;. (Since one purpose of this project is skill-building, of course I did this without an LLM.)&lt;/p&gt;
&lt;p&gt;I used &lt;a href=&quot;https://docs.solidjs.com/solid-start&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SolidStart&lt;/a&gt; to scaffold an empty SolidJS TypeScript project with Vite. I downloaded the “SR Legacy” data set from &lt;a href=&quot;https://fdc.nal.usda.gov/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;the USDA FoodData Central site&lt;/a&gt;. This data set is fairly complete, but it’s still small enough that I can serve and load a condensed version for fully client-side lookups. I used &lt;a href=&quot;https://www.fusejs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;FuseJS&lt;/a&gt; to search this data set - its tuneable fuzzy search and small bundle size were perfect for the project’s needs.&lt;/p&gt;
&lt;p&gt;This was my first experience with &lt;a href=&quot;https://docs.solidjs.com/concepts/stores&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SolidJS stores&lt;/a&gt;, and I found the developer experience wonderful. It turned out to be an efficient and simple way to reactively manage the dynamic form state I required.&lt;/p&gt;
&lt;p&gt;I adapted &lt;a href=&quot;https://stackoverflow.com/a/73848584&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;a fraction-parsing script&lt;/a&gt; from StackExchange user &lt;code&gt;machineghost&lt;/code&gt; to allow the quantity to be written as a mixed fraction. And that was it. You can see the results at the bottom of my &lt;a href=&quot;https://joshua.seigler.net/recipes/bread-pudding&quot;&gt;bread pudding&lt;/a&gt; and &lt;a href=&quot;https://joshua.seigler.net/recipes/breakfast-strata&quot;&gt;breakfast strata&lt;/a&gt; posts.&lt;/p&gt;
&lt;h3 id=&quot;recipe-label-maker&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/i-made-myself-a-recipe-label-generator/#recipe-label-maker&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://recipelabels.pages.seigler.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Recipe Label Maker&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://git.apps.seigler.net/joshua/recipelabels.pages.seigler.net&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Source code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It could be interesting to try to parse an entire pasted ingredients list. I would need to fabricate simplified alternatives to the FoodData food names (e.g. “milk” -&amp;gt; “Milk, whole, 3.25% milkfat, with added vitamin D”) and have a good way to disambiguate less obvious ingredients. Ideally this could be accomplished client-side as well, without integrating the Everything Tool (tokenization, vector embeddings). A task for another day.&lt;/p&gt;
</content>
    </entry>
    <entry>
      <title>Angular impressions update</title>
      <link href="https://joshua.seigler.net/posts/angular-impressions-update/" />
      <updated>2026-03-27T00:00:00Z</updated>
      <id>https://joshua.seigler.net/posts/angular-impressions-update/</id>
      <content type="html">&lt;p&gt;This is a six-months-later follow up to &lt;a href=&quot;https://joshua.seigler.net/posts/angular-impressions/&quot;&gt;Angular impressions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Angular is kinda good for me now that I’m used to it. Using modern Angular development practices helps a whole lot – dependency injection is less mysterious with &lt;code&gt;inject&lt;/code&gt;, state management is actually great since I can use signals, and it looks like in a version or two maybe even Observables will be basically optional. I’ve accepted that Angular does more for you than React, and that helps with the daily experience as well.&lt;/p&gt;
&lt;p&gt;I do miss being able to pass around components as values; in Angular if you want to make a component that accepts another component which it will use to do its rendering (maybe the user can provide a wrapper for table cells), you will be using templates. Not as elegant or simple.&lt;/p&gt;
&lt;p&gt;But Angular finally feels expressive, and I’m no longer spending effort fighting with it.&lt;/p&gt;
</content>
    </entry>
    <entry>
      <title>Angular impressions</title>
      <link href="https://joshua.seigler.net/posts/angular-impressions/" />
      <updated>2025-10-29T00:00:00Z</updated>
      <id>https://joshua.seigler.net/posts/angular-impressions/</id>
      <content type="html">&lt;p&gt;I’ve been working with Angular lately. Most of my experience is with React, starting back in 2018 just before the addition of hooks, and working with it heavily into 2025. I build web and mobile applications, and React or React Native was the tool of choice across a handful of companies small and large. I’ve also built some small personal projects with Vue and SolidJS as little learning experiments.&lt;/p&gt;
&lt;p&gt;When I try a tool that challenges my comfortable ways of doing things, the initial feeling is generally bad. That’s normal and expected. I’m using the tool, so I understand that in theory it can fit some need I have. But the way it goes about things often seems wrong because I’m not used to it. I haven’t learned to look at things from the tool’s particular perspective yet.&lt;/p&gt;
&lt;p&gt;A good example of this discovery journey is Typescript. When I first started using Typescript, it seemed like the amount of hassle involved in making everything properly typed was not worth the occasional benefit of catching a type error. But once I got used to the language I quickly appreciated the value of working with types, and I ended up with a very positive opinion of the tool.&lt;/p&gt;
&lt;p&gt;Getting “over the hump” and internalizing the tool’s paradigm doesn’t always mean liking the tool. Tailwind is a tool for organizing and applying CSS styles. It is the most popular example of “atomic styles”, a way of using CSS classes like inline styles, with some constraints applied to help with design consistency. With Tailwind and other Atomic CSS tools, you don’t write CSS, you apply utility classes. At first this struck me as nearly obscene. But a lot of smart people are huge fans, and I joined a project that was using it and didn’t have the social capital to rip it out, so I gave it a shot. It wasn’t as bad as I thought it would be, and I often enjoyed using it. But after becoming thoroughly familiar with Tailwind, I think that in most cases you can do better with CSS variables and scoped CSS. (I have a lot of thoughts about CSS architecture, but I’ll save them for their own post.)&lt;/p&gt;
&lt;p&gt;Since I’ve been using Angular for a few months now, I’m coming out of the “this feels bad because it’s unfamiliar” stage and starting to have a sense of the Angular paradigm. To use &lt;a href=&quot;https://joshua.seigler.net/posts/the-trivium-a-tool-for-learning-anything/&quot;&gt;Trivium&lt;/a&gt; terminology, I’m starting to shift my focus from Angular’s grammar and logic to its rhetoric.&lt;/p&gt;
&lt;p&gt;Here are some of my observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Angular &lt;em&gt;really&lt;/em&gt; loves dependency injection and decorators.&lt;/li&gt;
&lt;li&gt;Angular has evolved significantly over time, so there are a lot of old and new patterns coexisting: &lt;code&gt;*ngIf&lt;/code&gt; and &lt;code&gt;@if&lt;/code&gt;, &lt;code&gt;@Input&lt;/code&gt; and &lt;code&gt;input()&lt;/code&gt;,  &lt;code&gt;ngOnInit&lt;/code&gt; and &lt;code&gt;effect()&lt;/code&gt;. Since I’m just now learning it, I mostly use the modern patterns shown in Angular’s docs.&lt;/li&gt;
&lt;li&gt;Angular is compiled, which lets you do creative things with template languages if you want.&lt;/li&gt;
&lt;li&gt;Angular depends on the DOM in a way React doesn’t. Each Angular component is like a mini application with its own host element. The component’s code renders its template into the host element. In React, a JSX component is more like a persistent function invocation. It has a location in the DOM, but it doesn’t have its own host element. The component could easily emit nothing and be completely absent from the DOM. This is either difficult or impossible in Angular, it’s not even something you would try to do.&lt;/li&gt;
&lt;li&gt;Angular is complex. Maybe I will come to appreciate DI and the Angular way eventually. But I already know that with a starkly simpler tool like React or especially Solid, I can efficiently accomplish the same result, while being able to understand how my framework functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, I feel that Angular is certainly suitable for building web applications. But because of all the DI and decorators, it feels unnecessarily clever, which makes learning to use it more difficult, and makes deeply understanding how it works less likely. It is well tested and is as “batteries included” as it gets, which is nice for large projects.&lt;/p&gt;
&lt;p&gt;It will be interesting to see how my feelings change with time. Stay posted.&lt;/p&gt;
</content>
    </entry>
    <entry>
      <title>Tools of the trade</title>
      <link href="https://joshua.seigler.net/posts/tools-of-the-trade/" />
      <updated>2025-05-15T00:00:00Z</updated>
      <id>https://joshua.seigler.net/posts/tools-of-the-trade/</id>
      <content type="html">&lt;p&gt;Everyone has different tools that they find especially effective. Here are some I have found with a few words about why I like them.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: I made a &lt;a href=&quot;https://joshua.seigler.net/uses&quot;&gt;/uses&lt;/a&gt; page that lists these tools and many others I like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;mise-en-place&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/tools-of-the-trade/#mise-en-place&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://mise.jdx.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mise-en-place&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Universal dev tool version manager. Specify tool versions in a config file and this tool can ensure that they are installed and active when entering the project directory. Amazing for getting a new dev environment set up in seconds. Replaces &lt;code&gt;asdf&lt;/code&gt;, &lt;code&gt;nvm&lt;/code&gt;, &lt;code&gt;pyenv&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;rbenv&lt;/code&gt;, and many other tool-specific version managers. Supports an incredible number of tools thanks to compatibility with &lt;code&gt;asdf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It also supports installing specific global tools, like &lt;code&gt;angular-cli&lt;/code&gt; from &lt;code&gt;npm&lt;/code&gt;, or &lt;code&gt;stack-pr&lt;/code&gt; from &lt;code&gt;pipx&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;stack-pr&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/tools-of-the-trade/#stack-pr&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://github.com/modular/stack-pr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;stack-pr&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open source tool for &lt;a href=&quot;https://www.stacking.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;stacking PRs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;PR stacks are, as far as I can tell, the best way to manage large features in git. I first heard about this practice in a series of blog posts from Graphite, a company offering free PR-stacking software and related paid services. But you don’t need a custom CI flow or managed service for stacking to work - this CLI tool or one of the others at &lt;a href=&quot;https://www.stacking.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;stacking.dev&lt;/a&gt; can take care of this.&lt;/p&gt;
&lt;p&gt;If you start using PR stacks your whole company will start copying you.&lt;/p&gt;
&lt;h3 id=&quot;xc&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/tools-of-the-trade/#xc&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://xcfile.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;xc&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Markdown based task runner.&lt;/p&gt;
&lt;p&gt;Define tasks in code blocks in markdown, and call them from the CLI. Serves as both task definition and documentation.&lt;/p&gt;
&lt;h3 id=&quot;pd2slack&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/tools-of-the-trade/#pd2slack&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://github.com/sidpremkumar/pd2slack&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pd2slack&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Simple python script to update the members of a Slack group such as &lt;code&gt;@oncall&lt;/code&gt; to match the active member(s) of a PagerDuty schedule. This replaces several expensive SAAS services.&lt;/p&gt;
&lt;h3 id=&quot;syncthing&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/tools-of-the-trade/#syncthing&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://syncthing.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SyncThing&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;P2P alternative to Dropbox, supports mobile and desktop. Synchronize folders of content across all my devices.&lt;/p&gt;
&lt;h3 id=&quot;obsidian&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/tools-of-the-trade/#obsidian&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://obsidian.md/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Obsidian&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For notes and reference. Sync across devices with &lt;code&gt;syncthing&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;keepassxc&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://joshua.seigler.net/posts/tools-of-the-trade/#keepassxc&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt; &lt;a href=&quot;https://keepassxc.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;KeePassXC&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For passwords. Sync across devices with &lt;code&gt;syncthing&lt;/code&gt;.&lt;/p&gt;
</content>
    </entry>
</feed>
