<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Blog — Sam Zanca</title><description>Posts on development, tools, and systems.</description><link>https://zanca.dev/</link><language>en-us</language><item><title>Stop choosing Convenience</title><link>https://zanca.dev/blog/stop-choosing-convenience/</link><guid isPermaLink="true">https://zanca.dev/blog/stop-choosing-convenience/</guid><description>We&apos;ve optimized for developer convenience for years. AI changes the equation. Maybe it&apos;s time to build native desktop applications again.</description><pubDate>Sun, 05 Jul 2026 04:41:21 GMT</pubDate><content:encoded>&lt;p&gt;For years, I&amp;#39;ve been frustrated by the number of desktop apps companies ship using Electron. I understand why Electron became popular. It gave us a simple way to build cross-platform desktop applications, let frontend developers reuse their existing React skills, and kept the development experience familiar.&lt;/p&gt;
&lt;p&gt;But we&amp;#39;re in a different era now. We&amp;#39;re in the AI era.&lt;/p&gt;
&lt;p&gt;Companies are encouraging developers to use AI coding agents, and that&amp;#39;s changing the tradeoffs around software development.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ve noticed this trend outside of desktop development too. On Twitter, I&amp;#39;ve seen developers moving away from clever abstractions and compact libraries in favor of more explicit code because AI models are simply better at working with it.&lt;/p&gt;
&lt;p&gt;Take React as an example. We used Redux everywhere for years. Then the industry moved away from it because it was considered too verbose, replacing it with Zustand, custom hooks, and countless homegrown state management solutions. Every company seemed to reinvent the wheel a little differently.&lt;/p&gt;
&lt;p&gt;Today, Redux doesn&amp;#39;t seem quite so unreasonable anymore.&lt;/p&gt;
&lt;p&gt;Its patterns are well established, predictable, and explicit. Those are exactly the kinds of codebases AI thrives in. AI is effectively averaging millions of public GitHub repositories. If we write software using patterns that are consistent across companies and skill levels, AI has a much better chance of producing the correct result.&lt;/p&gt;
&lt;p&gt;I think desktop development is in a similar position.&lt;/p&gt;
&lt;p&gt;If we&amp;#39;re already relying on AI to generate large portions of our applications, why are we still optimizing for Electron&amp;#39;s biggest advantage? The original selling point was that frontend developers didn&amp;#39;t have to learn native desktop development. But if AI is doing most of the implementation work, that advantage becomes much less important.&lt;/p&gt;
&lt;p&gt;Instead, why not build better desktop applications?&lt;/p&gt;
&lt;p&gt;The framework I&amp;#39;ve become most interested in is GPUI from the Zed team. Yes, it&amp;#39;s Rust, and Rust isn&amp;#39;t an easy language. But GPUI&amp;#39;s architecture feels surprisingly familiar to anyone who&amp;#39;s written React before. Combined with modern AI models, it&amp;#39;s much more approachable than I expected.&lt;/p&gt;
&lt;p&gt;I&amp;#39;ve one-shotted several GPUI applications using DeepSeek V4 Pro, which is hardly the most expensive model available. The framework is surprisingly productive because it gives you a solid foundation without requiring a massive amount of code.&lt;/p&gt;
&lt;p&gt;The biggest thing holding GPUI back isn&amp;#39;t developer experience. It&amp;#39;s the ecosystem.&lt;/p&gt;
&lt;p&gt;For example, video support is still fairly immature. There isn&amp;#39;t a widely adopted package that solves it cleanly, so many developers end up building their own solutions. Elliott from Dreams of Code implemented an entire video pipeline for Kiru. There are plenty of similar examples on GitHub using GPUI alongside wgpu or other Rust multimedia libraries.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s normal for a young ecosystem. It will improve over time.&lt;/p&gt;
&lt;p&gt;When it does, I think we&amp;#39;ll have a compelling alternative to Electron.&lt;/p&gt;
&lt;p&gt;Electron has become the default answer for desktop applications because it&amp;#39;s convenient. But convenience comes with a cost. Every Electron application ships its own browser runtime, consumes more memory than it needs to, and contributes to the growing feeling that modern desktop software is becoming heavier every year.&lt;/p&gt;
&lt;p&gt;If more of these applications were built with frameworks like GPUI, we&amp;#39;d end up with software that&amp;#39;s faster, lighter, and makes much better use of system resources.&lt;/p&gt;
&lt;p&gt;The thing that finally pushed me to write this was installing Whisper Flow.&lt;/p&gt;
&lt;p&gt;Overall, I like it. The self-correction is genuinely impressive. I haven&amp;#39;t used another voice-to-text system that cleans up spoken mistakes nearly as well.&lt;/p&gt;
&lt;p&gt;For comparison, I currently use FUTO Keyboard on Android. It&amp;#39;s good, but it&amp;#39;s nowhere near Whisper Flow&amp;#39;s quality. If I stumble over my words, FUTO faithfully transcribes the mistake. Whisper Flow often understands what I meant and quietly fixes it. That&amp;#39;s a fantastic user experience.&lt;/p&gt;
&lt;p&gt;But then I looked at the application itself.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s an Electron app.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;At its core, it&amp;#39;s a push-to-talk application with speech processing. The heavy lifting is already happening inside Whisper. The UI is mostly a small settings window. It doesn&amp;#39;t feel like an application that needs an embedded Chromium instance.&lt;/p&gt;
&lt;p&gt;If anything, the performance-sensitive parts would benefit from being written in a lower-level language.&lt;/p&gt;
&lt;p&gt;This feels like the perfect kind of application for GPUI or another lightweight native framework. Keep the interface simple, run Whisper locally, work completely offline, and ship something that&amp;#39;s fast by default instead of carrying around an entire browser just to display a settings panel.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s what made me realize something.&lt;/p&gt;
&lt;p&gt;Electron solved a very real problem for the industry. But AI is changing what that problem looks like.&lt;/p&gt;
&lt;p&gt;Maybe it&amp;#39;s time to stop assuming Electron is the default choice and start building native desktop applications again.&lt;/p&gt;
</content:encoded><category>electron</category><category>rust</category><category>agents</category></item><item><title>How I setup my SolidStart apps</title><link>https://zanca.dev/blog/solidstart-setup/</link><guid isPermaLink="true">https://zanca.dev/blog/solidstart-setup/</guid><pubDate>Tue, 30 Dec 2025 19:40:21 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;As of writing this, running &lt;code&gt;bun create solid&lt;/code&gt; doesn&amp;#39;t use the latest version, so we&amp;#39;ll use &lt;code&gt;bunx create-solid@latest&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this quick guide I&amp;#39;ll setup my preferred way to use solidjs/solidstart. Preferring to use a guide such that I don&amp;#39;t have to maintain a template over time.&lt;/p&gt;
&lt;h2&gt;Starting with the drizzle template&lt;/h2&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bunx create-solid@latest -s --ts -t=&amp;quot;with-drizzle&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;-s&lt;/code&gt; to make this a solidstart project, &lt;code&gt;--ts&lt;/code&gt; to use typescript, &lt;code&gt;-t=&amp;quot;with-drizzle&amp;quot;&lt;/code&gt; to use the drizzle template&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Update the &lt;code&gt;.gitignore&lt;/code&gt; to ignore some files.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Other lock files (we&amp;#39;re using bun)
package-lock.json
yarn.lock
pnpm-lock.yaml

# sqlite file
drizzle/db.sqlite&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add this convenience dev script to the &lt;code&gt;package.json&lt;/code&gt;. This will wipe the db, push the schema and start the dev server.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;diff&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;&amp;quot;scripts&amp;quot;: {
+  &amp;quot;dev:fresh&amp;quot;: &amp;quot;rm drizzle/db.sqlite &amp;amp;&amp;amp; drizzle-kit push &amp;amp;&amp;amp; vinxi dev
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Change the Drizzle driver to &lt;code&gt;libsql&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;We&amp;#39;re swapping out drizzle&amp;#39;s built in &lt;code&gt;better-sqlite3&lt;/code&gt; driver, which requires a custom dockerfile when deploying to railway for &lt;code&gt;libsql&lt;/code&gt; from turso which is pure javascript. No idea what the trade-offs are but this makes my side projects easier to deploy.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bun rm @types/better-sqlite3
bun add @libsql/client&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;diff&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;// drizzle.config.ts
export default {
  dialect: &amp;quot;sqlite&amp;quot;,
  schema: &amp;quot;./drizzle/schema.ts&amp;quot;,
  out: &amp;quot;./drizzle/migrations/&amp;quot;,
- // driver: &amp;quot;better-sqlite&amp;quot;,
  dbCredentials: {
-   url: &amp;#39;./drizzle/db.sqlite&amp;#39;,
+   url: &amp;quot;file:./drizzle/db.sqlite&amp;quot;,
  },
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;ts&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// src/api/db.ts
import { drizzle } from &amp;quot;drizzle-orm/libsql&amp;quot;;
import { createClient } from &amp;quot;@libsql/client&amp;quot;;
import { dirname, resolve } from &amp;quot;path&amp;quot;;
import { existsSync, mkdirSync } from &amp;quot;fs&amp;quot;;

// Use environment variable for database path, or resolve relative to project root
const dbPath = process.env.DATABASE_PATH
  ? resolve(process.env.DATABASE_PATH)
  : resolve(process.cwd(), &amp;quot;drizzle&amp;quot;, &amp;quot;db.sqlite&amp;quot;);
const dbDir = dirname(dbPath);

// Ensure the directory exists before creating the client
if (!existsSync(dbDir)) {
  mkdirSync(dbDir, { recursive: true });
}

const client = createClient({
  url: `file:${dbPath}`,
});

export const db = drizzle(client);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Add tailwind&lt;/h2&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;bun add solid-motionone @tailwindcss/vite
bun add -D tailwindcss&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;ts&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// tailwind.config.js
/** @type {import(&amp;#39;tailwindcss&amp;#39;).Config} */
export default {
  content: [
    &amp;quot;./src/**/*.{ts,tsx}&amp;quot;,
  ],
  darkMode: &amp;#39;class&amp;#39;,
  theme: {
    extend: {},
  },
  plugins: [],
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;diff&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;# app.config.ts
import { defineConfig } from &amp;quot;@solidjs/start/config&amp;quot;;
+import tailwindcss from &amp;quot;@tailwindcss/vite&amp;quot;;

export default defineConfig({
  vite: {
    ssr: { external: [&amp;quot;drizzle-orm&amp;quot;] },
+    plugins: [tailwindcss()],
  },
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;p&gt;The server entry file &lt;code&gt;src/entry-server.tsx&lt;/code&gt; is a good place to start background tasks or server setup tasks. In dev, this runs once a route is hit but in prod this runs on server startup.&lt;/p&gt;
</content:encoded><category>solidjs</category><category>solidstart</category></item><item><title>Tail-Call Optimization (in gleam)</title><link>https://zanca.dev/blog/tail-call-optimization/</link><guid isPermaLink="true">https://zanca.dev/blog/tail-call-optimization/</guid><description>Tail-Call Optimization is crucial for recursive algorithms, here&apos;s how it works.</description><pubDate>Tue, 02 Sep 2025 16:15:33 GMT</pubDate><content:encoded>&lt;p&gt;Tail-Call Optimization is compiler optimization that allows a recursive function, where the last line of the function is the call to itself, to reuse the same memory stack, thus allowing the recursive function to use constant memory.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/assets/blog/hayleigh-on-tco.png&quot; alt=&quot;hayleigh on tco&quot; /&gt;&lt;/p&gt;
&lt;p&gt;proof: &lt;code&gt;Specification and Transformation of Programs by Helmut A. Partsch; Chapter 6&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Why does this matter?&lt;/h2&gt;
&lt;p&gt;Tail-Call Optimization is crucial for recursive algorithms, especially those that might involve deep recursion. It prevents &amp;quot;stack overflow&amp;quot; errors by reusing the call stack for tail calls, ensuring constant memory usage. This makes highly recursive functional code robust and performant, avoiding crashes and allowing elegant solutions that might otherwise require iterative rewrites.&lt;/p&gt;
&lt;h3&gt;Feels Like Mutation (But Isn&amp;#39;t)&lt;/h3&gt;
&lt;p&gt;In functional programming, mutation is typically avoided. However, by using accumulators in tail-recursive functions it can feel like to mutable state. The accumulator parameters effectively &amp;quot;store&amp;quot; the evolving state of the computation. With each recursive call, new values for these accumulators are passed, creating a new &amp;quot;snapshot&amp;quot; of the state rather than directly altering an existing variable. With TCO, we maintain immutability and get simpler code as a side effect.&lt;/p&gt;
&lt;p&gt;The only negative in Gleam is you&amp;#39;ll have to make a public facing wrapper function to hide the accumulator parameters since Gleam doesn&amp;#39;t support optional or default arguments.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Every recursive function can be turned into a tail-recursive one. So lets start with a classic example of Fibonacci, since its often used to explain recursion and we usually see it in its non tail-recursive form.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;rust&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fn fibonacci(n: Int) -&amp;gt; Int {
  case n {
    0 -&amp;gt; 0
    1 -&amp;gt; 1
    _ -&amp;gt; fibonacci(n - 1) + fibonacci(n - 2)
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To make it tail-recursive, we need to make sure the last call is a single call, not two. If you think about how a human would solve this, they would go in pairs of two: 0 + 1 is 1. 1 + 1, is 2, 1 + 2 is 3, 2 + 3 is 5. So lets add 2 accumulator values to keep track of our pairs.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;rust&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;// Private, actual tail recursive function
fn fibonacci_tail(n: Int, a: Int, b: Int) -&amp;gt; Int {
  case n {
    0 -&amp;gt; a
    1 -&amp;gt; b
    _ -&amp;gt; fibonacci_tail(n - 1, b, a + b)
  }
}

// Public interface that simplifies the parameters
pub fn fibonacci(n: Int) -&amp;gt; Int {
  fibonacci_tail(n, 0, 1)
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;rust&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;// Both function the same
pub fn main() {
  10
  |&amp;gt; fibonacci
  |&amp;gt; echo // 55
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here&amp;#39;s all the &lt;code&gt;fibonacci_tail&lt;/code&gt; calls. As you can see &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are basically holding all the fibonacci digits and slowly stepping thru the sequence.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;rust&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;fibonacci_tail(10, 0, 1)
fibonacci_tail(9, 1, 1)
fibonacci_tail(8, 1, 2)
fibonacci_tail(7, 2, 3)
fibonacci_tail(6, 3, 5)
fibonacci_tail(5, 5, 8)
fibonacci_tail(4, 8, 13)
fibonacci_tail(3, 13, 21)
fibonacci_tail(2, 21, 34)
fibonacci_tail(1, 34, 55)
55&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And here&amp;#39;s a javascript version using an iterative approach.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;js&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function fibonacciIterative(n) {
  if (n === 0 || n === 1) return n;

  let a = 0, b = 1;
  for (let i = 2; i &amp;lt;= n; i++) {
    let temp = a + b;
    a = b;
    b = temp;
  }

  return b;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;I don&amp;#39;t know about you, but the gleam version is a lot easier to read and understand.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;TODO add more examples&lt;/h3&gt;
&lt;hr&gt;
&lt;h3&gt;Compiler Support for TCO&lt;/h3&gt;
&lt;p&gt;TCO isn&amp;#39;t universally supported. Languages like Gleam, OCaml, and Haskell offer TCO guarantees.&lt;/p&gt;
&lt;p&gt;Conversely, many languages, including JavaScript, don&amp;#39;t optimize tail calls by default. This can lead to stack overflows for deep recursion, unlike in languages with TCO. Always confirm your language and compiler&amp;#39;s TCO support.&lt;/p&gt;
</content:encoded><category>gleam</category><category>functional-programming</category></item><item><title>How I use Fish Shell</title><link>https://zanca.dev/blog/fish/</link><guid isPermaLink="true">https://zanca.dev/blog/fish/</guid><description>Discover why the Fish shell offers an unparalleled out-of-box experience. Learn about its superior completions, navigation, and configuration, making it the best modern shell for developers.</description><pubDate>Fri, 29 Aug 2025 12:06:51 GMT</pubDate><content:encoded>&lt;p&gt;Tired of endlessly tweaking your shell configuration? Fish offers a refreshingly opinionated and powerful experience right out of the box.&lt;/p&gt;
&lt;p&gt;I love the Fish shell for several reasons, the first being the &lt;strong&gt;out-of-box experience is best in class&lt;/strong&gt;. You don&amp;#39;t need to go crazy with plugins or hell even a status line tool like &lt;code&gt;oh-my-zsh&lt;/code&gt;. You can just get to work. Fish comes with so many nice features that make moving around in the terminal so pleasant.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!example]- To name a few&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Caveat: Nushell is very compelling too as it has very comparable UX to Fish, but fish is a more familiar shell. Nushell completely changes the paradigm and offers features that might not interest you. Worth noting that both Nu and Fish have their own shell language.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;Another reason is &lt;strong&gt;configuration&lt;/strong&gt;. Yes, Fish has its own scripting language which makes it not POSIX compliant. Most make this out to be a bigger deal than it actually is. People complain about it being a poor choice for scripting and issues with portability. The solution to both of these, is to use fish-lang for only its designed purpose.&lt;/p&gt;
&lt;p&gt;System-level scripts should be written in sh/bash for portability. Your shell config is &lt;em&gt;personal&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As for integrating with tools most tools out there have fish integrations, so using fish is almost never actually an issue. Tools like python virtual environments create &lt;code&gt;activate&lt;/code&gt; scripts in fish if that&amp;#39;s the shell they detect you using. And if you&amp;#39;re using a tool written entirely in bash you have two options:&lt;/p&gt;
&lt;p&gt;Additionally, if you don&amp;#39;t want a complex config, you can use the command &lt;code&gt;fish_config&lt;/code&gt; which will give you a web-ui for customizing: themes, prompt, functions, and keybindings in a jiffy. This lets you get up and running with fish even faster.&lt;/p&gt;
&lt;h2&gt;Autocomplete&lt;/h2&gt;
&lt;p&gt;I just want to show a quick example of fish&amp;#39;s game changing auto-complete.&lt;/p&gt;
&lt;p&gt;Say you were to try this command in git:&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# First some setup
git checkout -b something-foo-bar-spike-feature-final;
git checkout main;
# Try this in bash, zsh, and fish
git checkout spike # press tab after spike&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Only fish will give you an autocomplete result (out of the box). Because unlike bash and zsh, fish will fuzzy find and has built-in git integration.&lt;/p&gt;
&lt;h2&gt;General practices&lt;/h2&gt;
&lt;p&gt;All my fish configs are written in such a way that I make no assumptions about what is installed. This is both so that each machine can be using all or none of my preferred apps and I still get access to my fish config. Before I use any command/app/tool, I check that it exists.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;if type -q some_command
    # Configure some_command
end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;My configuration&lt;/h2&gt;
&lt;p&gt;I got tired of having to be careful not to commit things that were appended to my fish config file, so I decided I&amp;#39;d move it to the &lt;code&gt;conf.d&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from: $HOME/.config/fish/config.fish
to:   $HOME/.config/fish/config.d/config.fish&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out &lt;code&gt;conf.d&lt;/code&gt; is loaded before the main config file anyways, so this changes basically nothing.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;├── conf.d/
│   └── config.fish
└── config.fish # This file is gitignored&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As a result now the default config file can remain as a local config file. Any tool that wants to append things can do so and I don&amp;#39;t have to care.&lt;/p&gt;
&lt;p&gt;At any time I can look at my local config and move some of those into a &lt;code&gt;conf.d&lt;/code&gt; file.&lt;/p&gt;
&lt;h3&gt;conf.d directory&lt;/h3&gt;
&lt;p&gt;This is a feature of Fish, so you don&amp;#39;t need to source this folder or anything. This directory contains specific tool/app configurations. This keeps my config file more specific to shell configurations.&lt;/p&gt;
&lt;p&gt;Generally, if a tool appends a blob of code to my config file, that&amp;#39;s a good candidate for something to go into a separate conf.d file. So another way of viewing files besides config.fish, is that they&amp;#39;re all code-gen&amp;#39;d. Where as config.fish is code I&amp;#39;ve written.&lt;/p&gt;
&lt;h3&gt;functions&lt;/h3&gt;
&lt;p&gt;Another great feature of fish, each file named after a command is a sort of lazy-evaluated script. All files here are only loaded when the function is called. So e.g. if I never uses &lt;code&gt;bass&lt;/code&gt;, it&amp;#39;ll never be evaluated.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Command line tools I use with Fish&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;More details &lt;a href=&quot;https://github.com/metruzanca/dotfiles/tree/master/home/.config/fish&quot;&gt;https://github.com/metruzanca/dotfiles/tree/master/home/.config/fish&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I use &lt;a href=&quot;https://starship.rs&quot;&gt;starship.rs&lt;/a&gt; as my status line. Starship has very similar opinions to fish. The out-of-box experience is superb and configuration is very simple.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://github.com/ajeetdsouza/zoxide&quot;&gt;zoxide&lt;/a&gt; as an improved &lt;code&gt;cd&lt;/code&gt; which lets me just teleport to places I&amp;#39;ve previously been to.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://github.com/lsd-rs/lsd&quot;&gt;lsd&lt;/a&gt; as a fancy &lt;code&gt;ls&lt;/code&gt; replacement. And I hook into fish&amp;#39;s &lt;code&gt;fish_prompt&lt;/code&gt; event to call ls after changing directories so I always get a list as I move (which eliminates most calls of lsd).&lt;/p&gt;
&lt;p&gt;My prompt starts up with &lt;code&gt;pfetch&lt;/code&gt; because its pretty.&lt;/p&gt;
</content:encoded><category>terminal</category><category>linux</category><category>fish</category></item><item><title>How to close the current window with JavaScript</title><link>https://zanca.dev/blog/close-windows-with-javascript/</link><guid isPermaLink="true">https://zanca.dev/blog/close-windows-with-javascript/</guid><description>Explaining the quirks and workaround for closing windows in Javascript</description><pubDate>Sun, 22 Sep 2024 16:03:18 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;[!example] tldr&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;js&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;window.open(location, &amp;#39;_self&amp;#39;).close();
// or
window.close(&amp;#39;&amp;#39;, &amp;#39;_parent&amp;#39;, &amp;#39;&amp;#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Working as of Chromium: &lt;code&gt;128.0.6613.138&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Today I learned a little quirk with closing the current tab/window with Javascript.&lt;/p&gt;
&lt;p&gt;So apparently, you cannot use &lt;code&gt;window.close();&lt;/code&gt; to close the current window, unless that window was programmatically opened. But you can close children windows if you kept a reference to them. As it turns out, the children can also close the parent window. And for some reason, if you call &lt;code&gt;window.close(&amp;#39;&amp;#39;, &amp;#39;_parent&amp;#39;, &amp;#39;&amp;#39;)&lt;/code&gt;, this actually works.... but it shouldn&amp;#39;t.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;close&lt;/code&gt; doesn&amp;#39;t actually take any arguments... (&lt;em&gt;also see &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/close&quot;&gt;mdn&lt;/a&gt;&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/blog/js-window-close-args.png&quot; alt=&quot;js window close args&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Reading the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-close-dev&quot;&gt;html spec&lt;/a&gt;, &lt;code&gt;close&lt;/code&gt; doesn&amp;#39;t have args either.&lt;/p&gt;
&lt;p&gt;Searching the internet, the only thing I could find was this &lt;a href=&quot;https://natclark.com/tutorials/javascript-close-tab/&quot;&gt;post&lt;/a&gt; by natclark.&lt;/p&gt;
&lt;p&gt;Apparently &lt;code&gt;_parent&lt;/code&gt;, much like the widely known &lt;code&gt;_blank&lt;/code&gt; has to do with &lt;code&gt;a&lt;/code&gt; tag targets. There&amp;#39;s actually more targets i.e.: &lt;code&gt;_self&lt;/code&gt;, and  &lt;code&gt;_top&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s an interesting graphic explaining the targets from &lt;a href=&quot;https://stackoverflow.com/questions/18470097/difference-between-self-top-and-parent-in-the-anchor-tag-target-attribute&quot;&gt;stackoverflow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/blog/js-browser-url-targets.png&quot; alt=&quot;js browser url targets&quot; /&gt;&lt;/p&gt;
&lt;p&gt;My best guess, is that this works purely for backwards compatibility. There&amp;#39;s even a &lt;a href=&quot;https://lonare.medium.com/window-close-not-working-how-to-redirect-the-parent-window-i-have-got-you-covered-solution-56c63a40f169&quot;&gt;post&lt;/a&gt; talking about a similar workaround/exploit where you can call: &lt;code&gt;window.open(location, &amp;#39;_self&amp;#39;).close()&lt;/code&gt; that seems like it worked the same way.&lt;/p&gt;
&lt;p&gt;I believe, that close takes arguments for backwards compatibility purposes, but the arguments are deprecated, so they&amp;#39;re not documented anymore. So if browsers lost this ability, you couldn&amp;#39;t blame them since its not in the docs.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;window.close(&amp;#39;&amp;#39;, &amp;#39;_parent&amp;#39;, &amp;#39;&amp;#39;)&lt;/code&gt; seems to work because close tries to get the parent of the current window, finds that it is the top parent, so it then closes it&amp;#39;s parent aka itself. Honestly, kind of strange still, the other workaround makes more sense.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;window.open(location, &amp;#39;_self&amp;#39;).close()&lt;/code&gt;: on the other hand, tries to open a window, but since &lt;code&gt;_self&lt;/code&gt; already exists, then it gets reused and then we can call close on it, but since it was &amp;quot;opened&amp;quot; via Javascript, it no longer is blocked.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h5&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/nav-history-apis.html#apis-for-creating-and-navigating-browsing-contexts-by-name&quot;&gt;7.2.2.1 Opening and closing windows&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt; [...] If a window already exists with that name, it is reused.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Both workarounds seem to work in Chromium: &lt;code&gt;128.0.6613.138&lt;/code&gt; (Official Build) (64-bit).&lt;/p&gt;
</content:encoded><category>javascript</category><category>til</category></item><item><title>Repeat yourself, dammit!</title><link>https://zanca.dev/blog/repeat-yourself-dammit/</link><guid isPermaLink="true">https://zanca.dev/blog/repeat-yourself-dammit/</guid><description>Dry is dead and we Killed it.</description><pubDate>Fri, 05 Apr 2024 15:13:00 GMT</pubDate><content:encoded>&lt;h2&gt;Dry is Dead and we Killed it.&lt;/h2&gt;
&lt;p&gt;Stop using DRY or making everything into a component/function. DRY leads to complexity. Sure, there might be less of it, but theres a higher density of complexity. I&amp;#39;d rather have more code, that is simple.&lt;/p&gt;
&lt;p&gt;One of my favorite scenarios: having like 3 different wrappers of 1 tool. What if instead we didn&amp;#39;t write a wrapper and just used the tool raw? Thats way less code, and way less complex since we didn&amp;#39;t have to constantly add features to the wrapper to re-expose features of the original tool. And if we need to know how to use the tool, you&amp;#39;ve got plenty of examples of that vs one really convoluted and generic-ified usage of it.&lt;/p&gt;
&lt;p&gt;We should instead use WET(write everything twice/thrice) or AHA(avoid hasty abstractions). Another way to put it is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Optimize for change first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;What other people say&lt;/h2&gt;
&lt;p&gt;Other people have said this before and likely did a better job.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kentcdodds.com/blog/aha-programming&quot;&gt;AHA Programming&lt;/a&gt; by Kent C. Dodds&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;The Wrong Abstraction&lt;/a&gt; by Sandi Metz&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/ralphcone/please-do-repeat-yourself-dry-is-dead-1jbg&quot;&gt;Please do repeat yourself (DRY is dead)&lt;/a&gt; by Rafal Stozek&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tedspence.com/do-repeat-yourself-528230cb92f0&quot;&gt;Do Repeat Yourself&lt;/a&gt; by Ted Spence&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://accu.org/journals/overload/27/151/teodorescu_2662/&quot;&gt;Do Repeat yourself&lt;/a&gt; by Lucian Radu Teodorescu&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
</content:encoded><category>programming</category><category>best-practice</category></item><item><title>Using Quartz as a Personal Blog</title><link>https://zanca.dev/blog/quartz/</link><guid isPermaLink="true">https://zanca.dev/blog/quartz/</guid><description>How I use Quartz, the static site generator for Obsidian Vaults</description><pubDate>Mon, 08 Jan 2024 15:02:53 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;[!tldr]
If you don&amp;#39;t want all the backstory and just want to skip to the technical bits on how to use quarts for blogging and note taking with obsidian as your editor, &lt;a href=&quot;https://zanca.dev/blog/quartz#setting-up-quartz&quot;&gt;click here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Prologue - Dealing with perfectionism&lt;/h2&gt;
&lt;p&gt;I&amp;#39;ve been wanting to write blog posts, for a while now. Posts about things I&amp;#39;m learning, things I&amp;#39;m building and most importantly mistakes I&amp;#39;ve made.&lt;/p&gt;
&lt;p&gt;The roadblock that&amp;#39;s always been in my way has been my stubbornness.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!quote]
&amp;quot;&lt;em&gt;I&amp;#39;m a web developer, I should build my own blog from &lt;strong&gt;scratch&lt;/strong&gt;!&lt;/em&gt;&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Having a highly customized site would be awesome, and there&amp;#39;s tons of great ones out there. Here&amp;#39;s just a few of my favorites:&lt;/p&gt;
&lt;p&gt;All those blogs are awesome. &lt;em&gt;Especially Amelia&amp;#39;s blog, who with just a glance will give all us mere mortals imposter syndrome no matter how long we&amp;#39;ve been in the industry.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The problem with all these developers sites, is they didn&amp;#39;t start blogging with a cool site. They started with some kind of blogging platform like: &lt;a href=&quot;https://dev.to/&quot;&gt;dev.to&lt;/a&gt;, &lt;a href=&quot;https://hashnode.com/&quot;&gt;hashnode.com&lt;/a&gt;, or the &lt;a href=&quot;http://og.urbanup.com/10118301&quot;&gt;OG&lt;/a&gt; &lt;a href=&quot;https://medium.com/&quot;&gt;Medium&lt;/a&gt;. Heck some developers happily use the default blog starter template of xyz framework.&lt;/p&gt;
&lt;p&gt;What better person to use as an example than Dan Abramov with his site &lt;a href=&quot;https://overreacted.io/&quot;&gt;overreacted.io&lt;/a&gt;. &lt;em&gt;In case you&amp;#39;ve been living under a rock, Dan created &lt;strong&gt;React.js&lt;/strong&gt;.. er I mean.. Redux and Create-React-App. Oh and he works on the react core team at facebook.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Another example, we&amp;#39;ve got Ryan Carnianto who still just &lt;a href=&quot;https://dev.to/ryansolid&quot;&gt;blogs to Devto&lt;/a&gt;. Who is Ryan? Creator of &lt;a href=&quot;https://www.solidjs.com/&quot;&gt;Solidjs&lt;/a&gt;, the framework that got everyone talking about Signals. If like me, you watch a lot of &amp;quot;Dev Youtube&amp;quot;, SolidJS is the framework beloved by &lt;strong&gt;Both&lt;/strong&gt; &lt;a href=&quot;https://www.youtube.com/@theprimeagen&quot;&gt;@ThePrimeagen&lt;/a&gt; and &lt;a href=&quot;https://www.youtube.com/@t3dotgg&quot;&gt;@t3dotgg&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Alright, we don&amp;#39;t need to make our own. So what am I using?&lt;/p&gt;
&lt;p&gt;Hold up, there&amp;#39;s still some more backstory. Another &amp;quot;problem&amp;quot;. Well, its not a problem per say, I just made it a problem, because I love getting in my own way.&lt;/p&gt;
&lt;p&gt;I want to be able to edit my blog post drafts from all my devices, including my phone. During 2021, I fell in Love with a little quarantine project by the name of &lt;a href=&quot;https://obsidian.md&quot;&gt;Obsidian&lt;/a&gt;. I loved the editor experience and wanted to use that for everything.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One thing to note with obsidian, while obsidian supports traditional markdown likes e.g. &lt;code&gt;[text](relative-url)&lt;/code&gt;, those can get cumbersome and obsidian defaults to using &amp;quot;wiki-links&amp;quot; e.g. &lt;code&gt;[[shortest-url]] or [[shortest-url|text]]&lt;/code&gt;. And I much prefer wiki-links. So whatever I use needed to support wiki-links.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, I tried making site after site:&lt;/p&gt;
&lt;p&gt;And every time I had something, I just wasn&amp;#39;t satisfied and I told myself &amp;quot;I couldn&amp;#39;t blog until it was &lt;strong&gt;Perfect&lt;/strong&gt;&amp;quot;.&lt;/p&gt;
&lt;h2&gt;Enter Quartz&lt;/h2&gt;
&lt;p&gt;I was looking for a tool to make some documentation/&lt;a href=&quot;https://convergence-wiki.vercel.app/&quot;&gt;wiki for a project&lt;/a&gt; and I stumbled upon &lt;a href=&quot;https://quartz.jzhao.xyz/&quot;&gt;Quartz&lt;/a&gt;. Quartz is a static site generator &lt;a href=&quot;https://www.urbandictionary.com/define.php?term=FOSS&quot;&gt;FOSS&lt;/a&gt; alternative to Obsidian Publish.&lt;/p&gt;
&lt;p&gt;I tried it out for that project of mine and I loved it and instantly knew I needed to use it for my blog. The default site is gorgeously styled and would allow me to use Obsidian for editing and git for publishing.&lt;/p&gt;
&lt;h2&gt;Setting up Quartz&lt;/h2&gt;
&lt;p&gt;All I did was follow the documentation to create the starter project, change a few strings (like site name) and following their docs, I adjusted the layout a bit. I didn&amp;#39;t spend much time on this step, most of my time was importing all my old blog posts.&lt;/p&gt;
&lt;p&gt;While importing them, I didn&amp;#39;t like how long my urls were, so I updated all my blog posts to have short urls. Maintaining canonical URLs is good for SEO, but none of my previews sites didn&amp;#39;t have sitemaps and lets be real, most of my old blog posts aren&amp;#39;t interesting or useful. I&amp;#39;ve archived a few really &lt;em&gt;bad&lt;/em&gt; ones but I&amp;#39;ll keep some for posterity.&lt;/p&gt;
&lt;p&gt;By default Quartz comes with &lt;code&gt;Plugin.RemoveDrafts()&lt;/code&gt; which is essentially an &amp;quot;opt-out&amp;quot; of being published. My plan was to unify my personal notes and blog into one Obsidian Vault, so this would be very tedious. Fortunately, Quartz has a second plugin &lt;code&gt;Plugin.ExplicitPublish()&lt;/code&gt; which is perfect for my needs.&lt;/p&gt;
&lt;p&gt;One additional benefit to the &lt;code&gt;ExplicitPublish&lt;/code&gt; is I can publish individual notes that aren&amp;#39;t in my blog folder. This way, say I&amp;#39;ve written up a personal note on how to use Postgres in docker, I can just publish that single note. (&lt;em&gt;If this file links to other non-published files, Quartz won&amp;#39;t publish those&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;Aside from this plugin swap, the only other two major changes to Quartz&amp;#39;s &lt;code&gt;quartz.config.ts&lt;/code&gt; &amp;amp; &lt;code&gt;quartz.layout.ts&lt;/code&gt; files was disable the Graph component and add a component to create a list of recent posts on the sidebar on desktop and above the footer on mobile.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;tsx&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;Component.RecentNotes({
  title: &amp;quot;Recent Blog Posts&amp;quot;,
  limit: 3,
  filter: (f) =&amp;gt; (
    f.slug!.startsWith(&amp;quot;blog/&amp;quot;)
    &amp;amp;&amp;amp; f.slug! !== &amp;quot;posts/index&amp;quot;
    &amp;amp;&amp;amp; !f.frontmatter?.noindex
  ),
  linkToMore: &amp;quot;blog/&amp;quot; as SimpleSlug,
}),&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Aside from that, at the moment, I&amp;#39;m pretty much using stock Quartz. Is my blog perfect? No. I&amp;#39;m itching to restyle it to match my colors in &lt;a href=&quot;https://zanca-dev-v4.vercel.app&quot;&gt;https://zanca-dev-v4.vercel.app&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;My landing page&lt;/h2&gt;
&lt;p&gt;None of my previous sites had a landing page. I always wanted to make something cool like what my good friend &lt;a href=&quot;https://federicoterzi.com/&quot;&gt;Federico Terzi&lt;/a&gt; made on his site. (&lt;em&gt;federico is the author of a super cool text-expander app called &lt;a href=&quot;https://espanso.org&quot;&gt;Espanso&lt;/a&gt;, you should go check it out!&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;But honestly, I&amp;#39;m not very creative. Give me a Figma and I&amp;#39;ll make it pixel perfect, responsive and give it that &amp;quot;feel good&amp;quot;/&amp;quot;power user touch&amp;quot; that apps like &lt;a href=&quot;https://linear.app&quot;&gt;linear&lt;/a&gt; have.&lt;/p&gt;
&lt;p&gt;Instead, while going to my &lt;a href=&quot;https://github.com/metruzanca&quot;&gt;Github profile&lt;/a&gt; looking for one of my old site repositories as inspiration, I had a realization.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/blog/github-profile.png&quot; alt=&quot;github profile&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I already have the perfect landing page.&lt;/p&gt;
&lt;p&gt;And now, with my site done, I can start writing! (&lt;em&gt;aka this article&lt;/em&gt;) Just note, when I say &amp;quot;Done&amp;quot;, I mean done. Not perfect. I set a goal this morning, and I got that goal Done.&lt;/p&gt;
&lt;p&gt;Actually I set up a few using &lt;a href=&quot;https://pomofocus.io/&quot;&gt;pomofocus.io&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/blog/pomodoro.io-quartz.png&quot; alt=&quot;pomodoro.io quartz&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Quartz on mobile&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Originally, I thought we could use &lt;code&gt;git sparse-checkout set content&lt;/code&gt; to reduce the amount of files that needed to be checked for updates to improve obsidian-git&amp;#39;s performance on mobile. But unfortunately, &lt;a href=&quot;https://isomorphic-git.org/&quot;&gt;Isomorphic-git&lt;/a&gt; which is what powers obsidian-git, doesn&amp;#39;t support sparse mode. If you try it, obsidian will throw an error telling you to create an issue on the isomorphic-git repository.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!abstract]- If you&amp;#39;re interested, here&amp;#39;s what I wrote thinking this would work. Oops
Since, we won&amp;#39;t be editing code on mobile and all we care about is the markdown files inside the &lt;code&gt;/content&lt;/code&gt; directory, we don&amp;#39;t need to clone anything else.&lt;/p&gt;
&lt;p&gt;What we&amp;#39;ll do next is clone the repository with some flags on our PC then move the cloned repository on to our Android device.&lt;/p&gt;
&lt;p&gt;TL;DR; here&amp;#39;s the code&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone --filter=blob:none --no-checkout https://github.com/user/repository.git;
cd repository
git sparse-checkout set content;
git checkout;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The magic happens in two parts:&lt;/p&gt;
&lt;p&gt;First we clone with the two flags: &lt;code&gt;--filter=blob:none --no-checkout&lt;/code&gt; which will give us an empty working tree and will set the repository to lazily load the history of all the files from the remote.&lt;/p&gt;
&lt;p&gt;Second we we use the &lt;code&gt;sparse-checkout&lt;/code&gt; command to set it up such that when we &lt;code&gt;checkout&lt;/code&gt; only the contents of the &lt;code&gt;content&lt;/code&gt; directory will be pulled.&lt;/p&gt;
&lt;p&gt;Lets see how that looks...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://asciinema.org/a/SoUCqbeVkHA36nXhNkvvdeVYz&quot;&gt;&lt;img src=&quot;https://asciinema.org/a/SoUCqbeVkHA36nXhNkvvdeVYz.svg&quot; alt=&quot;asciicast&quot; /&gt;&lt;/a&gt;
As you can see, when we clone the repository, its initially empty with just the hidden &lt;code&gt;.git/&lt;/code&gt; directory. Then after setting the &lt;code&gt;sparse-checkout&lt;/code&gt; directory and &lt;code&gt;checking&lt;/code&gt; out we end up with just the content folder.
There&amp;#39;s one last step that I can&amp;#39;t show I&amp;#39;m afraid, and thats because we need to create a personal_access_token on github and use that in our clone URL.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Following their documentation &lt;a href=&quot;https://publish.obsidian.md/git-doc/Getting+Started#Mobile&quot;&gt;https://publish.obsidian.md/git-doc/Getting+Started#Mobile&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I was able to clone the repo on my PC, then move the file to my android device. Once there, it all opens fine and &lt;em&gt;does work&lt;/em&gt;, though with pretty bad performance on the &lt;code&gt;git status&lt;/code&gt; checks. This is because of all the files in the quartz directory slowing things down.&lt;/p&gt;
&lt;p&gt;One idea is to use a submodule for the content, but getting the build on vercel to trigger when a submodule updates is going to be difficult. I can apparently get it to work with 2 github actions but its kind of spaghetti infrastructure at that point: &lt;a href=&quot;https://www.perplexity.ai/search/github-action-detect-QLw0F4PRR1iaPObfmpKG4A?s=c&quot;&gt;https://www.perplexity.ai/search/github-action-detect-QLw0F4PRR1iaPObfmpKG4A?s=c&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ideally, this would be solved in Quartz itself, by moving all &lt;code&gt;/quartz&lt;/code&gt; files to a npm package akin to how a framework like &lt;a href=&quot;https://start.solidjs.com/getting-started/what-is-solidstart&quot;&gt;Solidstart&lt;/a&gt; has a package &lt;a href=&quot;https://www.npmjs.com/package/@solidjs/start&quot;&gt;@solidjs/start&lt;/a&gt; that contains the framework and in the repository we&amp;#39;ve got just a few files that hook into the APIs exposed by the package.&lt;/p&gt;
&lt;p&gt;I might reach out to quartz and ask if that idea is in the cards or if they wanted help with figuring it out.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I&amp;#39;m calling that a day. Overall, I&amp;#39;m very satisfied with the outcome and I&amp;#39;m excited to start writing more posts. &lt;em&gt;Although, probably not using obsidian-git on mobile yet&lt;/em&gt;.&lt;/p&gt;
</content:encoded><category>quartz</category><category>obsidian</category><category>blog</category></item><item><title>Github Actions for automatic Github Releases</title><link>https://zanca.dev/blog/gh-automatic-releases/</link><guid isPermaLink="true">https://zanca.dev/blog/gh-automatic-releases/</guid><description>How to Automate Github Releases with a Github Actions pipeline for Continuous Deployment</description><pubDate>Wed, 21 Apr 2021 00:21:18 GMT</pubDate><content:encoded>&lt;p&gt;I&amp;#39;ve really fallen in love with github actions. They make allow you to play around with continuos integration and enable so much automation for your personal projects.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;ve ever wanted to make automatic releases on github, look no further.&lt;/p&gt;
&lt;h2&gt;Step 1: Create the workflow file&lt;/h2&gt;
&lt;p&gt;Create a new file at this path&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;.github/workflows/release.yml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you&amp;#39;re using vscode you can simply click new file and paste that path in. If you&amp;#39;re going bare with a terminal have this:&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p .github/workflows &amp;amp;&amp;amp; touch .github/workflows/release.yml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Step 2: The Workflow itself&lt;/h2&gt;
&lt;p&gt;But first TL;DR: We&amp;#39;re using &lt;a href=&quot;https://github.com/marketplace/actions/zip-release&quot;&gt;Zip Release&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And heres the code:&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;yaml&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;name: Release
on:
  push:
    tags:
      - &amp;#39;*&amp;#39;
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - name: Create Archive Release
        uses: thedoctor0/zip-release@master
        with:
          filename: &amp;#39;release.zip&amp;#39;
          exclusions: &amp;#39;*.git*&amp;#39;
      - name: Upload Release
        uses: ncipollo/release-action@v1
        with:
          artifacts: &amp;#39;release.zip&amp;#39;
          token: ${{ secrets.GITHUB_TOKEN }}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The long version. This action will only trigger when we push tags to the repository. Once triggered, it will first checkout the master(or main) branch using the &amp;quot;native&amp;quot; action for checkout. Afterwards it will zip using thedoctor0&amp;#39;s &lt;a href=&quot;https://github.com/marketplace/actions/zip-release&quot;&gt;Zip Release&lt;/a&gt; to zip up the contents of the repository and use the &lt;a href=&quot;https://github.com/ncipollo/release-action&quot;&gt;Release Action&lt;/a&gt; to create a new release using the newly created zip file as artifact.&lt;/p&gt;
&lt;p&gt;You could conceivably create multiple zips or other types of packages (e.g. jars) to use as artefacts.&lt;/p&gt;
&lt;p&gt;An example of artefacts is the releases of my personal minecraft [texture pack](&lt;a href=&quot;https://github.com/metruzanca/minecraft-xpd-texture-pack&quot;&gt;https://github.com/metruzanca/minecraft-xpd-texture-pack&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;) where the pack is distributed as a .zip file.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In case you&amp;#39;re also wondering what &lt;code&gt;secrets.GITHUB_TOKEN&lt;/code&gt; is, I&amp;#39;ve asked myself that too. Basically its a token github can use within the action to authenticate as &amp;quot;the repository&amp;quot; in order to perform actions within the repository e.g. creating new releases. You can read more over at the &lt;a href=&quot;https://docs.github.com/en/enterprise-server@3.0/actions/reference/authentication-in-a-workflow&quot;&gt;Docs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Step 3: Creating new releases&lt;/h2&gt;
&lt;p&gt;With the workflow created, unlike in my other guide for a &lt;a href=&quot;https://zanca.dev/blog/continuos-deployment-of-a-static-site-to-github-pages/&quot;&gt;gh-pages deploy pipeline&lt;/a&gt; which was triggered by pushing to master, meanwhile this workflow is triggered by pushing tags.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Whats a tag? Its basically a bookmark to a certain commit. Allowing you to easily say checkout tags/v1 or tags/v1.1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;TL;DR&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git tag -a v&amp;lt;semver&amp;gt; -m &amp;quot;&amp;lt;release message&amp;gt;&amp;quot; # Creates a tag
git push --tags # This triggers the release&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, theres a good chance that you&amp;#39;d never touched tags. So heres a quick guide:&lt;/p&gt;
&lt;p&gt;To view your tags (q to exit as per usual)&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git tag&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Creating Annotated tags:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tags come in two flavours, normal tags and annotated tags. Annotated tags are best practice as they contain a lot of information (such as tag author), however you can use normal tags as temporary tags.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git tag -a v&amp;lt;semver&amp;gt; -m &amp;quot;&amp;lt;release message&amp;gt;&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is akin to making a commit, it stays local until explicitly pushing to remote. To do that we simply need to push the tags. &lt;code&gt;git push&lt;/code&gt; will push everything but if you only want to publish tags theres a flag for that.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git push --tags&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The --tags flag is also available on fetch&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git fetch --tags&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Step 4: Profit&lt;/h2&gt;
&lt;p&gt;If everything is setup properly, pushing tags should trigger a github action and create your release package.&lt;/p&gt;
</content:encoded><category>til</category><category>devops</category><category>github</category></item><item><title>Installing Linux on a Powerbook G4</title><link>https://zanca.dev/blog/powerbook-linux/</link><guid isPermaLink="true">https://zanca.dev/blog/powerbook-linux/</guid><description>Reviving an old macbook with linux</description><pubDate>Fri, 02 Apr 2021 22:37:39 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/assets/blog/powerbook_g4.jpg&quot; alt=&quot;Apple PowerBook G4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;My dad has an old MacBook from 2001, a PowerBook G4 that&amp;#39;s been just sitting collecting dust for years. He wanted to try and revive it to use it for casual web browsing. So I told him he could probably pull it off by putting Linux on it.&lt;/p&gt;
&lt;p&gt;After some research, I found the best distribution for this would &lt;a href=&quot;https://lubuntu.net/&quot;&gt;Lubuntu&lt;/a&gt; as it has the following benefits:&lt;/p&gt;
&lt;h2&gt;Initial setup&lt;/h2&gt;
&lt;p&gt;First Download Lubuntu for PowerPC at &lt;a href=&quot;https://lubuntu.net/downloads/&quot;&gt;https://lubuntu.net/downloads/&lt;/a&gt;. The direct download link seems to be down so a torrent client is going to be needed. I recommend &lt;a href=&quot;http://webtorrent.io&quot;&gt;webtorrent&lt;/a&gt; as it&amp;#39;s very user-friendly.&lt;/p&gt;
&lt;p&gt;Next you&amp;#39;ll need something to flash a USB, &lt;a href=&quot;https://www.balena.io/etcher/&quot;&gt;balena etcher&lt;/a&gt; is cross-platform and will suit our needs.&lt;/p&gt;
&lt;p&gt;With balena installed you can select your drive, the ISO you downloaded via torrent and get flashing.&lt;/p&gt;
&lt;h2&gt;Getting into PowerPC BIOS/boot menu&lt;/h2&gt;
&lt;p&gt;To get into the powerbook&amp;#39;s bios you&amp;#39;ll need to hold down this long combination after you turn on your mac &lt;code&gt;Cmd⌘ + Option/alt + O + F&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After that you should be presented with a CLI with a white background.&lt;/p&gt;
&lt;h2&gt;Selecting the boot medium&lt;/h2&gt;
&lt;p&gt;PowerBooks are old, thus they don&amp;#39;t have a GUI based bios and instead use a CLI. Normally I love me a good CLI as they are much more powerful if you know what you&amp;#39;re doing. Sadly this bios is not very user friendly with obscure commands and no user manual. After some digging I found the correct command to boot from USB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NB&lt;/strong&gt;: if you have a non-american keyboard, while in this CLI your keyboard layout&lt;/p&gt;
&lt;p&gt;will default to the american layout so this might come in handy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/5/51/KB_United_States-NoAltGr.svg/400px-KB_United_States-NoAltGr.svg.png&quot; alt=&quot;US keyboard layout&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;if your keyboard has a fat enter key, the | and \ key will instead be to the left of the enter key instead of above.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For left usb port&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;boot usb0/disk@1:,\\yaboot&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For right usb port&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;boot usb1/disk@1:,\\yaboot&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Just to ring home how obscure these commands are, &lt;code&gt;yaboot&lt;/code&gt; is an argument thats needed if the OS is linux based. Meanwhile &lt;code&gt;tbxi&lt;/code&gt; is for OSX based. Why? Beats me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Setting up Lubuntu&lt;/h2&gt;
&lt;p&gt;After these steps, you should boot into the trial of Lubuntu. From here you can experiment with the distro or install it via the desktop icon.&lt;/p&gt;
&lt;p&gt;Following the install wizard should be easy enough.&lt;/p&gt;
&lt;p&gt;And we&amp;#39;re done! Enjoy your revived system.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Though if your G4 was like ours, 512mb of ram is going to be hard to work with, so be careful and setup a swap file or be prepared to reboot regularly when your system gets stuck when you run out of ram...&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><category>linux</category></item><item><title>Gatsby.js blog</title><link>https://zanca.dev/blog/gatsby-blog/</link><guid isPermaLink="true">https://zanca.dev/blog/gatsby-blog/</guid><description>How I created my blog and portfolio website with Gatsby.js and Netlify CMS</description><pubDate>Sun, 20 Dec 2020 20:36:56 GMT</pubDate><content:encoded>&lt;p&gt;One of my greatest weaknesses is perfectionism. This is most prevalent in my personal projects where I have no outside stimulus, such as a deadline, to keep me focussed on creating the most value for the least effort. In my personal projects I have a tendency to want to overthink (because it&amp;#39;s fun!) the problem and try to come up with creative solutions.&lt;/p&gt;
&lt;p&gt;Whiling making something, I often end up researching some topic that came up and soon I find myself deep on a random topic (like this week was cryptocurrency). While this does add to my personal knowledge base and in some cases experience (as I may start another experimental project to apply what I learned), I often find that I deviated far from the original goal I had set.&lt;/p&gt;
&lt;p&gt;This blog and portfolio, will hopefully be a step in the right direction to stop that bad habit of mine. Instead I will try to actually finish my personal projects and take note of topics that I would like to research further and schedule a specific time to explore and research those ideas.&lt;/p&gt;
&lt;p&gt;This specific blog post was written during the processes of creating this site, so this will serve as a step by step guide for anyone reading and more importantly my future self.&lt;/p&gt;
&lt;h2&gt;Setting up Gatsby&lt;/h2&gt;
&lt;p&gt;After initially downloading the full example (thats using Netlify CMS and Gatsby) in order to get a feel for how they worked together, I decided to start from scratch following &lt;a href=&quot;https://www.netlifycms.org/docs/gatsby/&quot;&gt;the Netlify Docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Following the guide on gatsby, I created a new gatsby site.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gatsby new blog https://github.com/gatsbyjs/gatsby-starter-blog&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After this, I knew I wanted my site to be built in Typescript and styled with Styled-Components. To configure these two, I made use of Gatsby&amp;#39;s Recipe feature. Running &lt;code&gt;gatsby recipes&lt;/code&gt; we get a list of all available recipes. From that list I found the ones I was interested in (and looked up a few others for future use)&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gatsby recipe typescript --install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To keep things neat, just in case, I actually decided to run the commands separately and make a commit for each of them.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gatsby recipe styled-components --install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Setting up Netlify CMS&lt;/h2&gt;
&lt;p&gt;Add the netlify cms dependencies&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install --save netlify-cms-app gatsby-plugin-netlify-cms&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add the config file (which is setup to work with how gatsby&amp;#39;s template is by default)&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;├── static
│   ├── admin
│   │   ├── config.yml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;yaml&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;backend:
  name: git-gateway
  branch: master

media_folder: static/img
public_folder: /img

collections:
  - name: &amp;quot;blog&amp;quot;
    label: &amp;quot;Blog&amp;quot;
    folder: &amp;quot;content/blog&amp;quot;
    create: true
    slug: &amp;quot;index&amp;quot;
    media_folder: &amp;quot;&amp;quot;
    public_folder: &amp;quot;&amp;quot;
    path: &amp;quot;{{title}}/index&amp;quot;
    editor:
      preview: false
    fields:
      - { label: &amp;quot;Title&amp;quot;, name: &amp;quot;title&amp;quot;, widget: &amp;quot;string&amp;quot; }
      - { label: &amp;quot;Publish Date&amp;quot;, name: &amp;quot;date&amp;quot;, widget: &amp;quot;datetime&amp;quot; }
      - { label: &amp;quot;Description&amp;quot;, name: &amp;quot;description&amp;quot;, widget: &amp;quot;string&amp;quot; }
      - { label: &amp;quot;Body&amp;quot;, name: &amp;quot;body&amp;quot;, widget: &amp;quot;markdown&amp;quot; }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This configuration allows placing the post assets along side the markdown files. Which I actually prefer to how Netlify&amp;#39;s example project was setup.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;content/
├── blog
│   ├── first-post-title
│   │   ├── index.md
│   │   └── post-image.jpg
└── └── second-post-title
        ├── index.md
        └── post-image.jpg&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The last step for Netlify CMS is to add the plugin to the &lt;code&gt;gatsby-config.js&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;js&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;plugins: [`gatsby-plugin-netlify-cms`]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With all that done, in theory Netlify CMS should be correctly configured and all that remains is to push to github and add the project to Netlify.&lt;/p&gt;
&lt;p&gt;Once added Identity Service and Git Gateway needs to be enabled.&lt;/p&gt;
&lt;h2&gt;The real coding begins!&lt;/h2&gt;
&lt;p&gt;Now that I&amp;#39;ve got everything working by following the guide from netlify&amp;#39;s docs, I can get started on making my own changes.&lt;/p&gt;
&lt;h3&gt;Getting /blog/ in my posts path&lt;/h3&gt;
&lt;p&gt;First and foremost, I need to get a /blog/ path before all my blog posts. If I don&amp;#39;t it&amp;#39;ll be messy when I add pages later and will absolutely screw up SEO ranking if I change tha page&amp;#39;s URL. So the earliest I get this done the better.&lt;/p&gt;
&lt;p&gt;To get this done I&amp;#39;m going to edit the &lt;code&gt;gatsby-node.js&lt;/code&gt; file and within the &lt;code&gt;createPages&lt;/code&gt; function where it makes a call to the &lt;code&gt;createPage&lt;/code&gt; callback I need to edit the path.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;diff&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;  createPage({
+   path: `/blog${post.fields.slug}`,
    component: blogPost,
    context: {
      id: post.id,
      previousPostId,
      nextPostId,
    },
  })&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;For code blocks that have both language and diffing highlighting (like github does), you can do that with prism.js. (Gatsby comes with it out of the box) Instead of setting the language to e.g. js, you can use diff-js&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Adding comments&lt;/h3&gt;
&lt;p&gt;Next up is adding support for comments. Theres this really nice project called &lt;a href=&quot;https://utteranc.es/&quot;&gt;utteranc.es&lt;/a&gt; which allows you to use GitHub issue comments for blog post comments. For anything but a developer blog, this would be inconvenient. However since this is indeed a developer blog, I feel like this is a great addition to the setup.&lt;/p&gt;
&lt;p&gt;Not only does it make it easy to handle comments but it also makes contributing to articles that much easier. Say my dream to be come a well known developer like &lt;a href=&quot;https://overreacted.io/&quot;&gt;Dan Abramov&lt;/a&gt; comes true and I want to allow people to contribute to my blog posts by adding translations, I think utterances will help with the whole process. Keeping the conversation around a blog post all in one place.&lt;/p&gt;
</content:encoded><category>react</category><category>netlify</category><category>gatsby</category></item><item><title>Deploy website to GitHub Pages</title><link>https://zanca.dev/blog/gh-static-site/</link><guid isPermaLink="true">https://zanca.dev/blog/gh-static-site/</guid><description>How to easily setup a CI pipeline with github actions to deploy a Static Site (e.g. React or Gatsby) to Github Pages</description><pubDate>Mon, 17 Aug 2020 23:35:00 GMT</pubDate><content:encoded>&lt;p&gt;I often create &lt;strong&gt;React&lt;/strong&gt; or &lt;strong&gt;Gatsby&lt;/strong&gt; apps and usually just opt to use github pages as a host.&lt;/p&gt;
&lt;p&gt;Up until now my process for getting the &lt;code&gt;build/&lt;/code&gt; directory to where it needs to be, has been... messy to say the least. Today I&amp;#39;ve gone ahead and automated that once and for all and I&amp;#39;ve found what I think is a nice and easy solution. One thats also easy to setup for new repository.&lt;/p&gt;
&lt;p&gt;Here&amp;#39;s how it works.&lt;/p&gt;
&lt;h2&gt;Step 1: Create the workflow&lt;/h2&gt;
&lt;p&gt;Go ahead and create a new file at &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; with the following contents:&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;yaml&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;name: Deployment
on:
  push:
    branches:
      - master
jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [12.x]
    steps:
      - uses: actions/checkout@v1
      - name: Use node ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install dependencies
        run: npm ci
      - name: Build site
        run: npm run build
      - name: Deploy to gh-pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
          publish_dir: ./build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The gist of this workflow is two fold:&lt;/p&gt;
&lt;p&gt;The first part works out of the box (assuming you&amp;#39;ve used either create-react-app or gatsby-cli to bootstrap a project and thus have a npm build script), to get the second part working we need to setup a token for deploying. We&amp;#39;ve got a couple of &lt;a href=&quot;https://github.com/peaceiris/actions-gh-pages#supported-tokens&quot;&gt;options&lt;/a&gt; however I think the best and most convenient one is using ssh deploy keys.&lt;/p&gt;
&lt;h2&gt;Step 2: Create a new ssh key pair&lt;/h2&gt;
&lt;p&gt;Very important that you create a &lt;strong&gt;new ssh key pair&lt;/strong&gt; and don&amp;#39;t use one you might already be using if say you&amp;#39;re using ssh with github. The reason behind this is that we&amp;#39;re creating a key pair that our workflow will use.&lt;/p&gt;
&lt;div class=&quot;code-block-wrapper&quot;&gt;&lt;div class=&quot;code-lang&quot;&gt;bash&lt;/div&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t rsa -b 4096 -C &amp;quot;$(git config user.email)&amp;quot; -f gh-pages -N &amp;quot;&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Step 3: Add the ssh keys to the repository&lt;/h2&gt;
&lt;p&gt;After generating the keys go over to your repository settings and add the following.&lt;/p&gt;
&lt;h2&gt;And We&amp;#39;re Done!&lt;/h2&gt;
&lt;p&gt;The next time you push to master, this workflow will be triggered and will install, build and then create a new branch for us.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The last optional step would be to play around with the settings for gh-pages e.g. Adding a custom domain / subdomain.&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>devops</category><category>til</category><category>github</category></item></channel></rss>