<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nathan Long</title><description>A digital-garden/dumpster fire handcrafted by some ordinary web guy</description><link>https://nathan-long.com/</link><item><title>Git: Remove Dead Branches</title><link>https://nathan-long.com/blog/git-remove-dead-branches/</link><guid isPermaLink="true">https://nathan-long.com/blog/git-remove-dead-branches/</guid><description>A quick command to get rid of stale git branches.</description><pubDate>Tue, 10 Mar 2026 20:37:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s very common near the end of a project to have a hot pile of &lt;strong&gt;dead branches&lt;/strong&gt;: branches that originally tracked something upstream but have since been deleted.&lt;/p&gt;
&lt;p&gt;Your original &lt;code&gt;super-awesome-feature&lt;/code&gt; branch was merged via PR (pull request) on GitHub and deleted there after review, but you still have it locally, which means the branch is now considered dead. Manually cleaning up these branches can be tedious. I use a small script to clean up my local branches:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; branch&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --format&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;%(refname:short) %(upstream:track)&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;$2==&quot;[gone]&quot; {print $1}&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; xargs&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; branch&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -D&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To break it down, the script:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Updates your remote references&lt;/li&gt;
&lt;li&gt;List local branches and their upstream tracking status&lt;/li&gt;
&lt;li&gt;Filter branches whose upstream is &lt;code&gt;[gone]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Force-delete those branches&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And in more detail:&lt;/p&gt;
&lt;h2 id=&quot;fetch-and-prune&quot;&gt;Fetch and Prune&lt;/h2&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -p&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This downloads updates from the remote repository but does not merge them. The &lt;code&gt;-p&lt;/code&gt; flag removes stale remote-tracking references.&lt;/p&gt;
&lt;h2 id=&quot;chain-a-command&quot;&gt;Chain a command&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The double-ampersand is a bash control operator that only moves onto the next part if the previous command succeeds. (If it exits with a status of 0).&lt;/p&gt;
&lt;h2 id=&quot;list-local-branches&quot;&gt;List local branches&lt;/h2&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git branch --format &apos;%(refname:short) %(upstream:track)&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will return a list of local branches formatted with the short branch name and the upstream tracking status. Something like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;main [ahead 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;feature-login [behind 2]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;feature-api [gone]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;old-feature [gone]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;super-awesome-feature [gone]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;[gone]&lt;/code&gt; here means that at one point, the branch tracked something upstream, but that upstream branch no longer exists.&lt;/p&gt;
&lt;h2 id=&quot;filter-the-branch-list&quot;&gt;Filter the branch list&lt;/h2&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; awk&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;$2==&quot;[gone]&quot; {print $1}&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This uses the bash pipe operator (&lt;code&gt;|&lt;/code&gt;) to feed the branch list into &lt;code&gt;awk&lt;/code&gt;, which splits on whitespace and processes the input line by line.&lt;/p&gt;
&lt;p&gt;So an item like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;feature-api [gone]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;turns into:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$1 = feature-api&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$2 = [gone]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, we have a condition of &lt;code&gt;$2==&quot;[gone]&quot;&lt;/code&gt; which makes &lt;code&gt;awk&lt;/code&gt; only process lines with the second field of &lt;code&gt;[gone]&lt;/code&gt;. It discards the ones that don’t match.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{print $1}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We then only output the first field, in this case, &lt;code&gt;feature-api&lt;/code&gt;, to form a new list of just the dead branches.&lt;/p&gt;
&lt;h2 id=&quot;delete-dead-branches&quot;&gt;Delete dead branches&lt;/h2&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; xargs&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; branch&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -D&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The final part of the script pipes our new dead branch list into &lt;code&gt;xargs&lt;/code&gt;, which converts input into command arguments. The &lt;code&gt;-r&lt;/code&gt; flag here is a safety measure to not run the command if there’s no input.&lt;/p&gt;
&lt;p&gt;So &lt;code&gt;xargs&lt;/code&gt; will receive a list like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;feature-api&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;old-feature&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which gets turned into:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git branch -D feature-api old-feature&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And then &lt;code&gt;git&lt;/code&gt; force deletes those branches.&lt;/p&gt;
&lt;p&gt;Handy!&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>2026 Techo Kaigi</title><link>https://nathan-long.com/blog/2026-techo-kaigi/</link><guid isPermaLink="true">https://nathan-long.com/blog/2026-techo-kaigi/</guid><description>Running my own &apos;planner meeting&apos; for fun and emotional profit.</description><pubDate>Tue, 24 Feb 2026 15:41:22 GMT</pubDate><content:encoded>&lt;p&gt;Do you have a physical note system? Do you write anything down by hand? If not, you’re missing out. Digital notes are convenient, but there is something special about the tactile act of putting pen to paper. Perhaps it’s just the old artist in me, but it feels like an expansive idea space. There are no boundaries, no database or field restrictions, no rules; just you and the paper. It’s the ultimate fidget; it occupies both my hands and my brain. Some days it’s the only way I make it through marathon meetings.&lt;/p&gt;
&lt;p&gt;I’ve tried several products and approaches over the years, but in a haphazard way. I &lt;a href=&quot;https://vrklovespaper.substack.com/p/making-a-workshop-thats-also-a-zine&quot;&gt;recently learned&lt;/a&gt; about a Japanese practice called “techo kaigi,” which roughly translates to “Planner Meeting”. It’s a self-guided investigation of your stationary system to see what’s working and plan changes to improve it.&lt;/p&gt;
&lt;p&gt;There are a lot of videos of people going through this (check out the videos from the folks at &lt;a href=&quot;https://www.youtube.com/@Yoseka&quot;&gt;Yoseka Stationary&lt;/a&gt; up in NYC), and it’s fascinating to see what people are trying. I was struck by the love and joy embedded in these items—they weren’t just utilitarian objects, they were reflections of their owners who clearly took joy in using them.&lt;/p&gt;
&lt;p&gt;So I’m gonna try it. There are only two main requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It needs to be compact. I often work from different spaces, and the available table space can vary wildly (sometimes it’s just my own lap!).&lt;/li&gt;
&lt;li&gt;It needs to be enjoyable to use.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-old&quot;&gt;&lt;strong&gt;The Old&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;components&quot;&gt;&lt;strong&gt;Components&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Self-hosted personal wiki (&lt;a href=&quot;https://tiddlywiki.com/&quot;&gt;Tiddlywiki&lt;/a&gt;) - Work and writing&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/RETTACY-Dot-Grid-Notebook-Spiral/dp/B07SVTHR88/&quot;&gt;RETTACY A5 Spiral-bound dotgrid notebook&lt;/a&gt; - Notes, thoughts, and everything that doesn’t fit elsewhere&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Blue-Sky-Monthly-Planner-Calendar/dp/B0F5D4VNBX/&quot;&gt;2025-26 Bluesky Academic calendar A5 size&lt;/a&gt; - Planning and daily tasks&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Five-Star-Notebook-Personal-450048CG1-ECM/dp/B0CWS3HR31/&quot;&gt;Random spiral Fivestar notebook&lt;/a&gt; - Health and Fitness&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/BIC-Cristal-Smooth-Ballpoint-12-Count/dp/B00006IE6X/&quot;&gt;Bic Cristal pens&lt;/a&gt; - ‘ol reliable.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-good&quot;&gt;&lt;strong&gt;The Good&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wiki&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;The wiki is for work and for writing. It’s loosely based on the &lt;a href=&quot;https://en.wikipedia.org/wiki/Zettelkasten&quot;&gt;zettelkasen&lt;/a&gt; concept, snippets with interlinked ideas. II also keep project logs, writing drafts, and side-project ideas there..&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RETTACY A5 dotgrid notebook&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Dotgrid is my favorite paper format. It’s perfect for outlining, quick sketches, or simple wireframes. I often use it to map out logic flows or make sense of a problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bluesky Calendar&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;The month view is the main benefit for planning my personal life. I don’t use it for work meetings (as it’s futile to duplicate work calendars), but for events, objectives, family logistics, and goals. I also use it as a habit tracker to mark off the days I’ve completed my goals.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-neutral&quot;&gt;&lt;strong&gt;The Neutral&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bluesky Calendar&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;The week view is useful for planning discrete tasks in a week and creating time-bound todo lists, but once they’re done I never reference them again, meaning most of the pages in the calendar are either not relevant yet, or no longer relevant.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random spiral Fivestar notebook&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;This works fine for planning and logging my workouts. It’s durable and effective. Writing down my progress lets me see what’s working.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BIC Cristal Pens&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;These are the most reliable pens I’ve ever used, they take a beating and never clog… BUT the feeling of writing with them isn’t always the smoothest.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-bad&quot;&gt;&lt;strong&gt;The Bad&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wiki&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Inaccessible outside of my laptop, which means it’s context-locked to when I’m sitting down to officially work.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dotgrid notebook&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;All topics are mushed together. When I need to reference something, I have to scan all the entries to find what I’m looking for.&lt;/li&gt;
&lt;li&gt;When I feel overwhelmed, I write out to-do lists, but they get lost in the flow and are hard to reference quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Calendar&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;The calendar was too big. I like to work from different locations throughout the week, and sometimes I don’t have a lot of desk space. When I don’t review it, my planning suffers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-new&quot;&gt;&lt;strong&gt;The New&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;components-1&quot;&gt;&lt;strong&gt;Components&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Self-hosted personal wiki (&lt;a href=&quot;https://tiddlywiki.com/&quot;&gt;Tiddlywiki&lt;/a&gt;) - keep largely the same.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Leather-Notebook-Refillable-Hand-Crafted-Travelers/dp/B01C6MRKLG/&quot;&gt;Wanderer’s Traveler Notebook&lt;/a&gt;, 3 inserts - the biggest change!&lt;/li&gt;
&lt;li&gt;Self-printed 2026 calendar insert - Such compact. Much DIY.&lt;/li&gt;
&lt;li&gt;Index cards for floating lists - new way to track weekly tasks.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Five-Star-Notebook-Personal-450048CG1-ECM/dp/B0CWS3HR31/&quot;&gt;Random spiral Fivestar notebook&lt;/a&gt; - keep the same, it’s working fine.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Zebra-Sarasa-Pen-Assorted-10-Count/dp/B07SGGLW45/&quot;&gt;Zebra Sarasa Fineliners&lt;/a&gt;, all the colors of the rainbow!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;changes&quot;&gt;&lt;strong&gt;Changes&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wiki&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;No major changes. I’m pretty happy with how this one works; it’s been valuable to me as a reference and an idea generator.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Traveler’s Notebook&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;This form factor is new to me. The ‘notebook’ is a leather cover with two elastic bands and an elastic closure that keeps everything closed. It comes with 3 inserts that are slid through the elastic bands, forming three mini-notebooks in one!&lt;/li&gt;
&lt;li&gt;The replaceable inserts allow me to work through 3 categories (Work, Study, Journal/Prayer) while keeping everything together. When I finish a category, I can simply replace the insert with a new one and continue. This helps with referencing as I don’t have to flip and scan for entries as much.&lt;/li&gt;
&lt;li&gt;I can still use my dot grid paper, or whatever paper I want (lined, unlined…). There’s a large variety of inserts for this format!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self-printed 2026 calendar insert&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Because the Traveler’s Notebook is so insert friendly, I made my own 2026 year booklet, which gives me the month view I rely on, but allows me to keep it physically connected to the rest of my notes. No more excuses for not reviewing the calendar!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Index cards&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;I’ve always liked the idea of the &lt;a href=&quot;https://ugmonk.com/collections/analog&quot;&gt;Ugmonk Analog System&lt;/a&gt;, but really, it’s a bunch of overpriced index cards with a fancy stand. Index cards are readily available and fantastic for short lists. We have the power to make our own!&lt;/li&gt;
&lt;li&gt;Each index card serves a specific purpose. I have:
&lt;ul&gt;
&lt;li&gt;Two ‘Week’ cards for the current and upcoming week which I divide into 3 sections on each side (6 sections total) with a daily list on each section (grouping Saturday and Sunday) together&lt;/li&gt;
&lt;li&gt;A ‘House’ card with all the longer-term projects I have around the house.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;All the cards are clipped into my Travelers Notebook with paper clips where I can easily reference, access, and update them. I usually pull the current week card out as a reference for the day.&lt;/li&gt;
&lt;li&gt;If an item grows to where it needs its own list, I can just reach for another card and create a new one.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zebra Sarasa Fineliner pens&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;As a lefty, pens can be tricky. These pens are very ‘push’ friendly, meaning I don’t have to pull them in a specific way to get the flow of ink working correctly.&lt;/li&gt;
&lt;li&gt;The different colors help make different days easier to scan without drawing dividers everywhere. I pick a new color for each day (in rainbow order, of course, except for the yellow, which is impossible to read).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So there we have it, my first personal techo kaigi. I found a lot of value in reviewing and intentionally redesigning my systems, and I’ve been enjoying the changes so far. Check back in a year, and we’ll see how things are working!&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>How to Run FreshRSS Locally With Docker</title><link>https://nathan-long.com/blog/run-freshrss-locally-with-docker/</link><guid isPermaLink="true">https://nathan-long.com/blog/run-freshrss-locally-with-docker/</guid><description>A short guide on how to run FreshRSS locally for that self-hosted RSS joy.</description><pubDate>Tue, 20 Jan 2026 22:17:28 GMT</pubDate><content:encoded>&lt;p&gt;I love RSS.&lt;/p&gt;
&lt;p&gt;I love that it gives me a direct line to what people are publishing. No algorithm, no intermediary, just me and the voices I choose—and there are so many interesting voices out there!&lt;/p&gt;
&lt;p&gt;Managing the feeds is the real drag. Since they pulled the plug on Google Reader, there hasn’t been one app that I could really get behind. Fortunately, a group of folks who love RSS even more than I do have created an open-source RSS reader,  &lt;a href=&quot;https://freshrss.org/index.html&quot;&gt;FreshRSS&lt;/a&gt;. Which is great! But how do you run it?&lt;/p&gt;
&lt;p&gt;If you’re like me, you don’t need to host it publicly; you just want to run the app locally. Turns out the FreshRSS team has helped make this easy by hosting a &lt;a href=&quot;https://hub.docker.com/r/freshrss/freshrss&quot;&gt;well-maintained public Docker image&lt;/a&gt; of the application. All you will need is a local container service, like &lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot;&gt;Docker Desktop&lt;/a&gt; or &lt;a href=&quot;https://orbstack.dev/&quot;&gt;Orbstack&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;installing-freshrss-locally&quot;&gt;&lt;strong&gt;Installing FreshRSS locally&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Create a folder somewhere you can easily manage and navigate to in the terminal.&lt;/p&gt;
&lt;p&gt;Create a new &lt;code&gt;docker-compose.yml&lt;/code&gt; in that folder with the contents of:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;volumes&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  data&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  extensions&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  freshrss&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;    # Grab latest docker image from hub.docker.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; freshrss/freshrss:latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    container_name&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; freshrss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    hostname&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; freshrss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    restart&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; unless-stopped&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    logging&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      options&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;        max-size&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; 10m&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    ports&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;8080:80&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;    # set up location for data persistence&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    volumes&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;data:/var/www/FreshRSS/data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;extensions:/var/www/FreshRSS/extensions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      TZ&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; America/New_York&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;      # cron syntax for: update every 30 minutes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      CRON_MIN&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;*/30&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s break down what’s happening here.&lt;/p&gt;
&lt;p&gt;The first three lines define Docker volumes, which are persistent storage that live OUTSIDE the container. This is important as they’ll persist beyond the container starting and stopping (kinda important unless you want to re-index each feed!)&lt;/p&gt;
&lt;p&gt;We then define the &lt;code&gt;freshrss&lt;/code&gt; service, point it to that fancy Docker Hub image, name it, set some reasonable defaults, attach it to a port, and then point to where the named volumes will be attached.&lt;/p&gt;
&lt;p&gt;If you’re in a different timezone than East Coast US, change the &lt;code&gt;environment.TZ&lt;/code&gt; field to match your location. If you’re not sure of the specific name, &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_tz_database_time_zones&quot;&gt;find your timezone here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;starting-your-local-freshrss-container&quot;&gt;&lt;strong&gt;Starting your local FreshRSS container&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Now navigate to that folder you created and run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker compose up&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The container will spin up and bring you to the installation screen of FreshRSS. Follow the install instructions and PRESTO! Self-hosted RSS! (To stop the process, hit CTRL-C).&lt;/p&gt;
&lt;p&gt;The app should be available at &lt;code&gt;http://localhost:8080/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can also run it in ‘detached’ mode with &lt;code&gt;docker compose up -d&lt;/code&gt;, but you’ll need to remember to stop the container when you’re done. (I like keeping my containers attached in a terminal tab so I can remember what I have running.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; If you delete the container, you will also delete the data FreshRSS has been saving for you. Just start/stop, don’t destroy the container unless you also want to wipe your data. FreshRSS has lots of archiving options if you want to set up a backup system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;time-to-fill-it-with-some-voices&quot;&gt;&lt;strong&gt;Time to fill it with some voices!&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Now that you have a locally running RSS reader, you need voices to fill it with! I &lt;a href=&quot;https://nathan-long.com/voices&quot;&gt;publish a list of web industry voices&lt;/a&gt; that I continually update. You can scan the list and pick what you want, or you can &lt;a href=&quot;https://nathan-long.com/data/feed.opml&quot;&gt;download and import my OPML file&lt;/a&gt; to get your feeds started!&lt;/p&gt;
&lt;h2 id=&quot;a-few-rss-tips&quot;&gt;&lt;strong&gt;A few RSS Tips&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;As you add more and more voices, it can become overwhelming to keep track of them. To keep your feed from being stressful, I have found you have to &lt;strong&gt;become OK with the idea that you will not read every post in your feed&lt;/strong&gt;. Really! It’s OK! I used to struggle with the feeling that I would “miss” something important, but those pieces tend to have a reverb effect across the blogosphere—if something’s important, it will show up in other places as well!&lt;/p&gt;
&lt;p&gt;There are a few strategies I use when organizing my feed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a person writes under a site name (like Harry Roberts at CSS Wizardry), consider labeling the feed by the author’s name so you start to get a feel for who’s writing what.&lt;/li&gt;
&lt;li&gt;Group voices by interest or industry topic (Design, a11y, etc) so that you can get a feel for what topics are emerging in what category.&lt;/li&gt;
&lt;li&gt;FreshRSS has an ‘Important Feeds’ visibility category you can set for the people you’re most interested in hearing from to help cut through the noise.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then a few tips for reading my feed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scanning is your friend. Start with the titles and only move into the ones that interest you.&lt;/li&gt;
&lt;li&gt;If the unread numbers get really big, pick just one category or voice to start with. Scan the entries, read what’s interesting, and then mark everything else read. Rinse and repeat as time allows.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>tooling</category></item><item><title>Responsive Ambient Videos with AV1 and HEVC/H.265 Compression</title><link>https://nathan-long.com/blog/responsive-ambient-videos-av1-hevc/</link><guid isPermaLink="true">https://nathan-long.com/blog/responsive-ambient-videos-av1-hevc/</guid><description>Encode and compress videos like it&apos;s 2026</description><pubDate>Tue, 13 Jan 2026 15:04:09 GMT</pubDate><content:encoded>&lt;p&gt;Ambient videos, or “short looping videos as a background element,” are both common and tricky to get right. They can add a lot of visual texture to a design, but are often large, chunky elements that can slow down your performance.&lt;/p&gt;
&lt;p&gt;But this is 2026! We have some new tools to help with video compression! We’re going to utilize some of the techniques &lt;a href=&quot;https://evilmartians.com/chronicles/better-web-video-with-av1-codec&quot;&gt;Evil Martians demonstrated with AV1 encoding&lt;/a&gt; and combine them with responsive video techniques that &lt;a href=&quot;https://scottjehl.com/posts/using-responsive-video/&quot;&gt;Scott Jehl explains well&lt;/a&gt; over on his blog.&lt;/p&gt;
&lt;p&gt;There are a few assumptions I’m making: you will be processing the video manually, and you have &lt;code&gt;ffmpeg&lt;/code&gt; installed (I recommend &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; on a Mac).&lt;/p&gt;
&lt;p&gt;At the end of this, we’ll have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A vertically formatted AV1 (for mobile)&lt;/li&gt;
&lt;li&gt;A vertically formatted HEVC/H.265 (mobile fallback)&lt;/li&gt;
&lt;li&gt;720p AV1 (for tablet/small desktop)&lt;/li&gt;
&lt;li&gt;720p HEVC/H.265 (tablet/small desktop fallback)&lt;/li&gt;
&lt;li&gt;1080p AV1 (large desktop)&lt;/li&gt;
&lt;li&gt;1080p HEVC/H.265 (large desktop fallback)&lt;/li&gt;
&lt;li&gt;A poster image generated from the first frame of the video&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll tie it all together in an HTML &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element and implement some accessibility features.&lt;/p&gt;
&lt;h2 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;But first… a disclaimer:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No amount of compression is going to make a 180+ second video performant as an ambient video! Optimization starts at the communication level.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If an ambient video is being considered, consider the following guidelines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;20 seconds or less.&lt;/li&gt;
&lt;li&gt;Avoid wide-panning, rotating, or otherwise big movement shots (helps prevent folks from getting motion-sick or just being jarring in general).&lt;/li&gt;
&lt;li&gt;Avoid flashes if possible (3 or fewer per second per &lt;a href=&quot;https://www.digitala11y.com/understanding-sc-2-3-1-three-flashes-or-below-threshold/&quot;&gt;WCAG SC 2.3.1&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Start with the highest resolution output feasible (as it makes it easier to slice and dice).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once we have a reasonable starting point, we can work on smooshing these down to a nice size.&lt;/p&gt;
&lt;h2 id=&quot;av1&quot;&gt;AV1&lt;/h2&gt;
&lt;p&gt;AV1 is our champion video compression codec that does &lt;a href=&quot;https://people.xiph.org/~xiphmont/demo/av1/demo1.shtml&quot;&gt;some smart things&lt;/a&gt; to reduce filesize. But as with all nice, new things, it’s &lt;a href=&quot;https://caniuse.com/av1&quot;&gt;not fully supported yet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To make an AV1 from our reasonable starting point, we can run the following:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; INPUT.mov&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -map_metadata&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -an&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libsvtav1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -qp&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 40&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -tile-columns&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -tile-rows&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -pix_fmt&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; yuv420p&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -movflags&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; +faststart&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; scale=-1:1080&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.av1.1080.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here’s a breakdown of what’s happening:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-i INPUT.mov&lt;/code&gt; sets the source video file for input. FFmpeg will take video and audio streams from this file, convert them, and pack them into a new container.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-map_metadata -1&lt;/code&gt; will remove video metadata (like the name of a tool that was used initially to create a video). Sometimes metadata is useful, but for web development, this is rarely the case.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-an&lt;/code&gt; removes all audio. It will be muted to allow looping anyway.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-c:v libsvtav1&lt;/code&gt; selects the AV1 video codec.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-qp 40&lt;/code&gt; sets your size/quality balance for AV1. Lower numbers mean better quality and bigger size.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-tile-columns 2 -tile-rows 2&lt;/code&gt; is for speed enhancements at the cost of a small loss in compression efficiency.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-pix_fmt yuv420p&lt;/code&gt; sets a pixel format that helps reduce filesize by using full resolution for brightness and a smaller resolution for color. Read up on &lt;a href=&quot;https://en.wikipedia.org/wiki/Chroma_subsampling&quot;&gt;chroma subsampling&lt;/a&gt; to learn more. Pretty wild!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-movflags +faststart&lt;/code&gt; moves the important information to the beginning of the file. It allows the browser to start playing while the video is still downloading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-vf scale=-1:1080&lt;/code&gt; Scales down the image to whatever by 1080 pixels. If you want a different size, put in the desired height instead of 1080.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;output.av1.1080.mp4&lt;/code&gt; sets the name for the output file. Why an MP4? Aren’t we making AV1’s?! We are! But MP4 is a CONTAINER format, and it’s the most widely supported one. Just like in life, it’s what’s on the inside that matters.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will likely need to adjust the &lt;code&gt;-qp&lt;/code&gt; settings and your output resolutions to get the right mix.&lt;/p&gt;
&lt;h2 id=&quot;hevch265&quot;&gt;HEVC/H.265&lt;/h2&gt;
&lt;p&gt;We still need a fallback, but we don’t have to go all the way back to H.264. Safari supports the &lt;a href=&quot;https://caniuse.com/hevc&quot;&gt;HEVC/H.265 codec&lt;/a&gt;, which is like H.264 but … sleeker. 30-50% smaller than H.264 (with AV1 being 30% smaller than HEVC).&lt;/p&gt;
&lt;p&gt;This one is going to take much longer, so grab some coffee and run:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; INPUT.mov&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -map_metadata&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -an&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libx265&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -crf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 28&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -preset&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; veryslow&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -pix_fmt&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; yuv420p&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -movflags&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; +faststart&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -tag:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; hvc1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; scale=-1:1080&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; video.hevc.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Many of the same flags appear as before, but there are a few new ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-c:v libx265&lt;/code&gt; selects the HEVC/H.265 codec&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-preset veryslow&lt;/code&gt; forces HEVC/H.265 codec to generate a smaller video file even if it will be much longer.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-crf 28&lt;/code&gt; stands for ‘Constant Rate Factor’ and sets your size/quality balance. Lower numbers mean better quality and bigger size. The CRF scale goes from 0 to 51.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, you may need to adjust your &lt;code&gt;-crf&lt;/code&gt; rate to get the right amount of compression and quality. The output resolution should match the AV1 file.&lt;/p&gt;
&lt;h2 id=&quot;small-desktop--tablet-versions&quot;&gt;Small Desktop / Tablet Versions&lt;/h2&gt;
&lt;p&gt;To generate smaller videos, reuse the same commands from before, but change your scaling command. Something like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-vf scale=-1:720&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will output videos that are 720 pixels tall.&lt;/p&gt;
&lt;p&gt;If your video is a weird size, you may get errors if the numbers don’t divide cleanly when scaling down. If that happens, choose a new size or run the math on your own to find what numbers are going to work.&lt;/p&gt;
&lt;h2 id=&quot;mobile-versions&quot;&gt;Mobile Versions&lt;/h2&gt;
&lt;p&gt;Most likely, your starting video will be in a wide 16:9 format, but that’s a good amount of pixels you’ll never see on a mobile screen! We’re going to make a vertical crop of the video to minimize our wasted pixels.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; INPUT.mov&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;crop=ih*9/16:ih&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:a&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; copy&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.vertical.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-vf &quot;crop=ih*9/16:ih&quot;&lt;/code&gt; Calculates the new width based on the input height (ih) to maintain a 9:16 aspect ratio. Since x and y coordinates are not specified, ffmpeg defaults to cropping from the center of the frame (which is what we want). If you want a different ratio, enter your own numbers instead of &lt;code&gt;9/16&lt;/code&gt;, where the first number is the width and the second is the height.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c:a copy&lt;/code&gt; Not strictly necessary, it just copies any audio settings without modifying them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that we have a vertical file, we can make AV1 and HEVC/H.265 videos like the previous steps. Since we have less overall data, you can likely get away with something like &lt;code&gt;-vf scale=-1:1620&lt;/code&gt; to give those high-density pixel displays a nice video while still being small.&lt;/p&gt;
&lt;h2 id=&quot;making-a-poster-image&quot;&gt;Making a poster image&lt;/h2&gt;
&lt;p&gt;We also need a static poster image that will display if the video is loading over slow connections:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; INPUT.mov&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -ss&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 00:00:00.000&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vframes&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; thumb.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-ss 00:00:00.000&lt;/code&gt; seeks to a specific timestamp in the video. Here’s it at the very beginning.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-vframes 1&lt;/code&gt; tells ffmpeg output one frame.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;thumb.png&lt;/code&gt; saves that frame as a PNG.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You’ll want to size and compress that image into a reasonable format (WEBP or AVIF). Smaller is better here.&lt;/p&gt;
&lt;h2 id=&quot;rendering-the-video&quot;&gt;Rendering the video&lt;/h2&gt;
&lt;p&gt;Now that we have all the pieces, we can load them into a &lt;code&gt;video&lt;/code&gt; element:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; autoplay&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; playsinline&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; muted&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; loop&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  poster&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/assets/video/output.poster.avif&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    src&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/assets/video/output.vert.av1.1280.mp4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    media&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;(max-width: 769px)&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;video/mp4;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;    codecs=av01.0.05M.08,opus&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    src&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/assets/video/output.vert.hevc.1280.mp4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    media&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;(max-width: 769px)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;video/mp4; codecs=hvc1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    src&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/assets/video/output.av1.720.mp4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    media&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;(max-width:1441px)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;video/mp4; codecs=av01.0.05M.08,opus&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    src&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/assets/video/output.hevc.720.mp4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    media&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;(max-width:1441px)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;video/mp4; codecs=hvc1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    src&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/assets/video/output.av1.1080.mp4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;video/mp4; codecs=av01.0.05M.08,opus&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;source&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    src&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;/assets/video/output.hevc.1080.mp4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;video/mp4; codecs=hvc1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ensure these attributes are on your &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;autoplay&lt;/code&gt; starts the video as soon as there is enough data to do so.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;playsinline&lt;/code&gt; tells the browser that the video is to be played in the element’s playback area. Required for ambient videos in Safari.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;muted&lt;/code&gt; Insures no audio is output. Required by all browsers to autoplay.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loop&lt;/code&gt; Restarts the video when it reaches the end.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;poster&lt;/code&gt; points to the image to load while the video is loading or if no data can be provided.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And in addition to &lt;code&gt;src&lt;/code&gt; on our &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements, we set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;media&lt;/code&gt; which allows media queries, just like the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element. No match-y no load-y.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type=&quot;codecs=av01.0.05M.08,opus&quot;&lt;/code&gt; tells the browser this is an AV1 video&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type=&quot;video/mp4; codecs=hvc1&quot;&lt;/code&gt; tells the browser this is an HEVC/H.265 video.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that I used &lt;code&gt;max-width&lt;/code&gt; queries, but you could just as easily build them with &lt;code&gt;min-width&lt;/code&gt; queries; just be aware that the browser will use the first item that matches.&lt;/p&gt;
&lt;h2 id=&quot;a11y-considerations&quot;&gt;A11Y Considerations&lt;/h2&gt;
&lt;p&gt;We have an ambient video, but there’s still some accessibility we need to ensure. There are two features we need to enable.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.w3.org/TR/UNDERSTANDING-WCAG20/time-limits-pause.html&quot;&gt;WCAC SC 2.2.2 “Pause, Stop, Hide”&lt;/a&gt; criteria states that something longer than 5 seconds (which we likely qualify for) needs some sort of control to stop the motion. The simplest implementation is a play/pause toggle button.&lt;/p&gt;
&lt;p&gt;How you enable it will vary on how you’re handling interactivity on your project, but a very simple Vanilla JavaScript implementation could look like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;// the awesome button you made to toggle the video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; videoToggleButton&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; document&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.querySelector&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;your-button-selector-here&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;// the video itself&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; document&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.querySelector&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;your-video-selector-here&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;// toggle the play state of the video&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; handleVideoToggle&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;  video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;?.paused &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;?.play&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;?.pause&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  // also whatever you&apos;re doing to toggle the play/pause icons&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;// listen for clicks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;videoToggleButton&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;?.addEventListener&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; handleVideoToggle);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is over-simplified, but you get the idea. Change the selector to whatever you’re using to identify your elements.&lt;/p&gt;
&lt;p&gt;Also, unless your video is extremely subtle, we need to prevent it from autoplaying if users have &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-reduced-motion&quot;&gt;prefers-reduced-motion&lt;/a&gt; set. You COULD hide it with a CSS media query, but ‘reduced motion’ does not mean ‘no motion’. I prefer that users have the option to still engage with it as they want.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;// Check if the prefers-reduced-motion media query matches &apos;reduce&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; prefersReducedMotion&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.matchMedia&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;(prefers-reduced-motion: reduce)&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;).matches;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;// grab the video if you haven&apos;t already&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; document&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.querySelector&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;your-selector-here&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; (prefersReducedMotion &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; video) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;  this&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;?.pause&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;  this&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;video&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;?.removeAttribute&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;autoplay&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  // also whatever you&apos;re doing to toggle the play/pause icons&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And there we have it: a responsive, accessible, highly compressed ambient video!&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>Creating While Adulting</title><link>https://nathan-long.com/blog/creating-while-adulting/</link><guid isPermaLink="true">https://nathan-long.com/blog/creating-while-adulting/</guid><description>Why we stop creating, why we should create, what even is creativity, and some things we can do to nurture it, even while adulting.</description><pubDate>Wed, 10 Sep 2025 14:05:36 GMT</pubDate><content:encoded>&lt;p&gt;At Viget we have a tradition of weekly rotating &lt;a href=&quot;https://www.viget.com/articles/traditions-gratitude-one-way-we-stay-connected-at-viget/&quot;&gt;LabShares&lt;/a&gt;, which are a 10-12 minute presentations on ANYTHING the presenter is passionate about. We’ve talked about coffee, aloha shirts, sunscreen, adult autism, and many many other interesting topics.&lt;/p&gt;
&lt;p&gt;Recently, I was privileged to present my recent thinking on the nature of creativity and how we can continue to create even during the busiest seasons of our life.&lt;/p&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/af34aee8-0804-43d1-b820-9b6f5e34bcb2&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Images by Nikhil and Luis Villasmil (with modifications)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Hey friends,&lt;/p&gt;
&lt;p&gt;Today I want to talk about the curious problem of creating while adulting.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why we stop creating.&lt;/li&gt;
&lt;li&gt;Why we should create.&lt;/li&gt;
&lt;li&gt;What EVEN IS creativity.&lt;/li&gt;
&lt;li&gt;And some things we can do to nurture it, even while adulting.&lt;/li&gt;
&lt;/ol&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/64f0f9f7-ff13-4068-9d85-f1648942d20a&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But first, a disclaimer. The following content is Rated O for Opinion and contains questionable research mixed with my own experiences and speculation. It’s my opinion and how I’ve come to think about creativity.&lt;/p&gt;
&lt;p&gt;That said…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/385179be-6c62-4ec8-8c30-528e946bbdb4&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Let me introduce you to ‘Little Nathan’.&lt;/p&gt;
&lt;p&gt;Like many of us, when I was young I spent a lot of time creating and playing.&lt;/p&gt;
&lt;p&gt;Lego sculptures, play acting — usually as…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/4272e01c-6206-42b8-9d72-05a97688971b&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;BATMAN. (Batman was a big deal to me at the time.)&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/1e9ca58f-1c5d-44cf-9660-99664180db94&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;…but especially drawing. I loved to draw and I drew a LOT. These are some of my early drawings.&lt;/p&gt;
&lt;p&gt;Many of us can confirm that children are REALLY PROLIFIC artists.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/59412ad9-05a5-4146-9e15-afb5eeb33fb6&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;They fearlessly create from their inner selves.&lt;/p&gt;
&lt;p&gt;They’re not concerned with how it looks as much as the process: the smell of those Crayola markers, the feeling of pencil on paper.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/515e7ad0-3938-47c7-ac3d-eea6607a6d37&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;They create because they want to, for the joy of it.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/4e34887e-3d9c-40ce-be65-1732fbfdd471&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;As a kid, I created entire worlds (often ripped from the things I loved).&lt;/p&gt;
&lt;p&gt;I drew comics, wrote stories, and just kept creating.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/59e36cc9-5622-4912-8fea-0af64d121236&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Christine Nishiyama&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But at some point, we stop creating like this.&lt;/p&gt;
&lt;p&gt;We stop creating for the joy of it.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/d0300683-a8e0-4c66-b4ae-5073d65e59cc&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;I think two things happen as we get older:&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 11 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/4c816f3d-7b54-42e4-8fa5-0bc4994fbb19&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;We encounter ‘The Gap’. And…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/f697f787-4cf4-463c-9dd5-21e39c9b969e&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Life gets hard.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/5bcb035d-e96e-4918-98b9-5f45091d8087&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Chart by Kavya Chaturvedi (with modifications)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;“The Gap” is a term &lt;a href=&quot;https://vimeo.com/85040589&quot;&gt;defined by Ira Glass&lt;/a&gt; and it describes the discrepancy between your creative taste and your creative skill.&lt;/p&gt;
&lt;p&gt;Although typically when you first encounter it looks more like:&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/ffe165a6-002e-42d5-89fc-d9ab391b3202&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;A chasm.&lt;/p&gt;
&lt;p&gt;Through exposure to great work your inner sense of quality—your taste—has developed enough for the skill of your OWN work to become a disappointment to you.&lt;/p&gt;
&lt;p&gt;It may not have been one particular revelation but a slow confidence drain from critical review or rejection.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/f157b707-cf2c-4734-a8cc-5c6b5fc54d37&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But you’ve come face to face with “The Gap” and find a crossroads.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/d034b7be-6514-423d-b5fd-3e66ada23225&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Sarah J, &lt;a href=&quot;https://giphy.com/gifs/animation-i-give-up-cant-Jkz1tFg639alq&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Some people stop here. They feel their work will never be as good as their ambitions and they move on.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/8aa4897c-61b8-4cda-b1ac-6b45fdf3fe32&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Some will accept the challenge, double down, and work on closing that gap.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/2b530917-a750-420d-9ff8-b4e027842801&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;This was my experience when I saw the work of Todd McFarlane, Akira Toriyama, and many others. I was enthralled by their work and knew what I was producing wasn’t the same quality.&lt;/p&gt;
&lt;p&gt;So I copied, I traced, I riffed, and I kept going. These are some of my high school drawings.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/3e1279d5-aa6a-4337-842e-bc8176f86b5f&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But creating had changed for me, it was no longer JUST for joy of creating — I had something to prove now, an inner tension.&lt;/p&gt;
&lt;p&gt;Which… isn’t necessarily a bad thing, but it can be a consuming thing.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/bf638757-0fae-429b-a3d0-9820bc23b8f4&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image uncredited, &lt;a href=&quot;https://mid-nighttiger.tumblr.com/post/677016579141222400/ok-we-used-to-keep-this-diagram-in-the-artist-chat&quot;&gt;original source&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Our taste tends to improve faster than our skill leading to confidence spirals.&lt;/p&gt;
&lt;p&gt;When I look at something I’ve created I can VERY EASILY see everything that’s wrong with it, what I could have/should have done differently.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 21 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/cfceb7b8-1175-4084-8bac-f1c9450269e6&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;In fact, in one particular confidence spiral of my own I destroyed a lot of my art.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/022376d9-cffb-4ae4-93bd-9c3e57cf0af5&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;The only reason I have some of these pieces to show you today is because my Dad found a old copy of slides that I used to apply to the Art Program at Radford University.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/ca2a83f4-c4ab-4cb6-91b4-b1a9698a1534&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image uncredited, &lt;a href=&quot;https://mid-nighttiger.tumblr.com/post/677016579141222400/ok-we-used-to-keep-this-diagram-in-the-artist-chat&quot;&gt;original source&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;We’ll come back to confidence spirals in a bit.&lt;/p&gt;
&lt;p&gt;But for now let’s go on to another reason we stop creating… and that is…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/eece711e-7181-4eb3-b0a7-feab0c091af6&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by AFV, &lt;a href=&quot;https://giphy.com/gifs/afv-americas-funniest-home-videos-l0K4hqqqwgFijgVLa&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Life gets hard.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/bcb60a0d-4d3e-48dc-9915-04988316d5c4&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;When I graduated with a BFA and got a real job, things got busy fast.&lt;/p&gt;
&lt;p&gt;Housing, bills, budgets, repair and maintenance. Layer in in relationships, family, and kids, and things quickly reach capacity.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/4efc4654-6c4a-4caf-87af-82ac2ad8275c&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;I came to realize that I am finite, and my energy is finite.&lt;/p&gt;
&lt;p&gt;My life was so FULL that I thought I didn’t have the juice outside of my job and responsibilities to create.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/6982463e-815c-4ab4-8789-6da79d06fea3&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by National Gallery Singapore&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And I thought that by not creating “capital A” ART — that I wasn’t being creative.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/d1ecdc61-ad2f-40f9-8a63-b50e3458382a&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Alexandru Goman&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But I was wrong.&lt;/p&gt;
&lt;p&gt;And I believe we can recapture the JOY of creating, even during the busiest moments, because…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/6d5a3daf-b388-4109-b7a1-3a6f1927cc9d&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Alexandru Goman&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Creativity is not a talent, it’s a mode of thinking.&lt;/p&gt;
&lt;p&gt;It’s not something you’re born with.&lt;/p&gt;
&lt;p&gt;It’s about how you approach things — and it bleeds into all areas of our lives.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 31-40 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/5f78ff5c-9f04-4d88-8526-6ad78e85a523&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Viget&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;It’s a way we can relate to each other. We can show love through creativity and give back to the world and the people we encounter each time we create something new.&lt;/p&gt;
&lt;p&gt;It’s about what you have to say:&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/ebaa1642-dc0b-48dc-9dd8-a006cad5e20e&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;A thank you note says, “I cared enough to use time-consuming analog materials to convey my appreciation for what you did.”&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/bd304c33-8d5c-489a-b5af-5654d538bf83&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by King Arthur Baking Company&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;A homemade pie says, “I have created a majestic sensory experience for you of carbs and sugar.”&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/5c495089-a8f4-43b7-9db7-6534c2094307&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;A garden says “Look at how amazing nature is, these plants are cool and I want to share them with you.”&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/eaf2a399-8187-4104-b189-a0a9c0bbd406&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Barbara Pozzi, &lt;a href=&quot;https://giphy.com/gifs/black-and-white-talking-bird-26FfbM5bbhCdLANW0&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Everything we create says something big or small.&lt;/p&gt;
&lt;p&gt;And we all have something to say.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/481f64bc-887a-4c4a-be8f-f4d76987cccc&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But some people say, “Well, I’m just not creative.”&lt;/p&gt;
&lt;p&gt;To which —I— say…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/12f035bd-10f5-4b36-9190-d7d977218c80&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Zan Lazarevic&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;PHOOEY!&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/45dfd82b-5c8b-40ee-9a2f-e77f9c8be177&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;It takes a lot of creativity to keep little ones engaged in soccer practice.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/d5258355-0f2a-4a08-b107-7765f9fde321&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Parents of small children — it takes a lot of creativity to teach and provide experiences for them.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/afa9323b-de75-446f-bb1a-111fc59a778e&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Alexandru Goman&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But you have to see that creativity for what it is.&lt;/p&gt;
&lt;p&gt;Because creativity is not a talent, it is a mode of thinking.&lt;/p&gt;
&lt;p&gt;So let’s think about thinking for a bit&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 41-50 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/c74c269d-62da-4f1c-ab9f-aa996eb0124d&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Wikimedia Commons&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;John Cleese has a &lt;a href=&quot;https://www.youtube.com/watch?v=Pb5oIIPO62g&quot;&gt;famous talk on creativity&lt;/a&gt; that’s helpful here. (And I’ve already started referencing it).&lt;/p&gt;
&lt;p&gt;In it, Cleese describes two modes of thinking:&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/eb1e6c08-bd3f-47dd-b2bc-1fee502b6e72&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Marcel Strauss&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;The Open mode:&lt;/p&gt;
&lt;p&gt;Which is relaxed, expansive, humorous, curious. It’s not under pressure.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/bafc7b8e-a62d-47f6-824b-ca473e1dda81&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Solen Feyissa&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And the Closed mode:&lt;/p&gt;
&lt;p&gt;Which is a heads down, purposeful, it’s work-mode. It can be either pleasurable or stressful — sometimes both, depending&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/5660ac0f-ef51-410f-aa2a-8a607a92389b&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Alex Padurariu&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;You need both modes.&lt;/p&gt;
&lt;p&gt;If you only stay in the open you never get anything done.&lt;/p&gt;
&lt;p&gt;If you only stay in the closed mode means going through life with our heads down.&lt;/p&gt;
&lt;p&gt;But we need a few things to help move us into the open mode.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/84f08876-52b5-45d0-81a6-3cbb7e657c8b&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;You need space, the ability to seal yourself or you and your collaborators off.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/21d30cfb-cc21-43f7-98a5-07606214771b&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;You need time to create boundaries, To reserve that time for yourself, because it TAKES TIME to get into the open mode. It’s not a switch you can turn on and off.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/04935e26-5555-4d17-8ff4-80697402f039&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;You also need MORE time to stick with the problem, to tolerate the discomfort of an unsolved problem and not always take the first solution.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/21b48841-5987-44d5-87c1-955f8fcf5c56&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;You need confidence, because a fear of mistakes blocks creativity.&lt;/p&gt;
&lt;p&gt;Try to be open to anything in this mode.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/769ce9f1-1373-4cf9-a8a8-71785adbd576&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And then finally, we need humor. An attitude of humor allows play to happen, to ask “What if”, to explore silly ideas that you’d never normally consider.&lt;/p&gt;
&lt;p&gt;Humor is what brings us into the open mode faster than anything else.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/01eeba84-f254-4b68-bbc1-5fa362393766&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;However, when you’re also attempting to adult at the same time, I think we need three additional things to help us find this open mode:&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 51-60 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/fc4d6409-e91a-4ac9-95be-79d3a7c97e48&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;We need to prepare before we even try to be creative.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/7c83bf0a-0b59-4f7d-8a77-0e366db669e1&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;We need perseverance in the struggle, in the doing.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/b561965b-a1b8-41df-bd40-0000a6b3af1c&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And we need perspective in the after to keep ourselves ready for the next thing.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/905ed1bd-f3ea-41ed-86e4-722bf5f41097&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Better Noise Music, &lt;a href=&quot;https://giphy.com/gifs/betternoisemusic-house-party-solence-fuck-the-bad-vibes-nyqWfGvpAZPis1WOk2&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Let’s talk preparation, specifically energy and community.&lt;/p&gt;
&lt;p&gt;We’ve already talked about how our energy is finite.&lt;/p&gt;
&lt;p&gt;But some of us have energy tanks that look like this:&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/1d5f32b1-f31d-4186-93d6-a39a5cd730af&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Tony Feredo&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Full of holes. Mine included.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/56095ff0-128c-4594-9c53-3410d6cd8747&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Tony Feredo&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And when you look closely at some of the activities you THINK are FILLING your tank, they’re actually…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/dba3bd7c-df32-4b7f-831c-c45eac189ede&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Tony Feredo and Rajeshbhai Vaghasiya (vecteezy.com)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Energy parasites. They’re taking energy, not giving it.&lt;/p&gt;
&lt;p&gt;Doomscrolling, distractions, vapid entertainment, even negative people.&lt;/p&gt;
&lt;p&gt;For me personally, I found almost all social media was eating up a lot of energy just to engage with it. So I cut it out of my life.&lt;/p&gt;
&lt;p&gt;It’s going to be different for you, but if our energy is finite, what are we doing to protect it?&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/68886901-69b2-4803-81d1-e4a4a96b6eb3&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Nancy Hann&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;A lot of our energy also comes from community.&lt;/p&gt;
&lt;p&gt;Consider, if you will, the apple tree.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/0152f3f0-cfc0-4b79-bab9-4cc0cc5117f6&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Veronika Diegel&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Specifically a branch of an apple tree. On a functioning tree, the branch will produce fruit.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/77415d81-0a7d-4099-b4ab-684014264020&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Oregon State University&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;In periods of my life I’ve tried to be the branch without a tree.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 61-70 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/671174fb-a46f-466c-a16b-b59547cb22d3&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Oregon State University&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;I wanted to do it on my own, I’m an introvert after all, but the branch can’t produce fruit without the support of the rest of the tree.&lt;/p&gt;
&lt;p&gt;The water and nutrients from the sap and leaves. Even the bees that pollinate the flowers.&lt;/p&gt;
&lt;p&gt;Without a community we’re just a lone branch.&lt;/p&gt;
&lt;p&gt;We need people who encourage us, support us, help us along the way. We’re not designed to live life alone.&lt;/p&gt;
&lt;p&gt;So who or what are you connected to? What does your community look like?&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/94ab2556-b4b4-4053-b605-655b7f4d5e66&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Neom&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;So we’re preparing by hooking into community and guarding our energy, let’s talk about the doing.&lt;/p&gt;
&lt;p&gt;Sometimes when you’re working on a creative problem, you hit…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/c6ea43c0-ca4c-416c-8c29-2c8e5a9b17c9&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Puglie Pug, &lt;a href=&quot;https://giphy.com/gifs/pug-puglie-djB9ZJFrB8eEa9GcyX&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;THE SLUMP.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/3f51a8ff-52f3-49ae-ad0e-5884c9b26668&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Hannah Jacobs, &lt;a href=&quot;https://giphy.com/gifs/7HDGcyGY6ow4yFGagl&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;THE DOLDRUMS.&lt;/p&gt;
&lt;p&gt;This is the struggle section of creative effort. You’ve committed to creating but it’s just a slog. Nothing is coming.&lt;/p&gt;
&lt;p&gt;I have found a way to short circuit the slump is if you can find and harness your…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/6846091f-b49c-4b0f-a707-f8bbc74db271&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by William Warby&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;FREAK ENERGY!&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/83ac52cb-82a9-4b7b-ac98-54c9bd52f1d6&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Olivie Strauss (with modifications)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Freak energy is one part curiosity, one part humor, one part weird (and weirdness is subjective).&lt;/p&gt;
&lt;p&gt;Everyone has something you could go on forever about, you can’t read enough of it, you don’t tire of it, you can do it again and again and it remains enjoyable.&lt;/p&gt;
&lt;p&gt;And the joy of this can help pull you through a slump.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/5f8eda82-fe61-419a-928b-c2d09457d1b1&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://giphy.com/gifs/happy-Us9Ibqg4H0mTC&quot;&gt;Original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Some of you already know exactly what it is that you get your freak energy from.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/322b2351-7bc2-496b-8e01-74a3bab079ee&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But if not, ask, “What would you do for free?”&lt;/p&gt;
&lt;p&gt;Because that’s a good indication of the source of your freak energy.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/23692d4e-474c-4d89-b927-0bcfad926182&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;For me it’s games. Specifically game RULES. I can read rulebooks all day and learning a new game is an enticing brain puzzle that I can hardly put down.&lt;/p&gt;
&lt;p&gt;Now, that’s not particularly practical in itself. But harnessing freak energy doesn’t mean it has to be the MAIN thing.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/20f59395-eb2d-4847-9d24-245a50f45ac3&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Images from &apos;The Rules of the Red Rubber Ball&apos; by Kevin Carroll&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Kevin Carroll, the author of Rules of the Red Rubber Ball, his freak energy was from sports. But when he injured his knee and could no longer play, he pivoted to being an athletic trainer. He harnessed his freak energy to propel him into something else tangential.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 71-80 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/751def0c-9823-467b-b618-e05b613cc5f7&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;So if you find yourself in the slump, find the point of tangency, that point of connection, between your problem and your freak energy, and let it pull you along.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/82ac4088-74de-43bd-a1fc-c60cb9c010b2&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Charles Etoroma&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Now lets talk about keeping perspective afterwards.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/f108a99e-4620-4976-b49c-1601cfee3723&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image uncredited, &lt;a href=&quot;https://mid-nighttiger.tumblr.com/post/677016579141222400/ok-we-used-to-keep-this-diagram-in-the-artist-chat&quot;&gt;original source&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Remember those confidence spirals we talked about?&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/46faf80e-047d-4b47-988e-7a5c9974cb13&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Holler Studios, &lt;a href=&quot;https://giphy.com/gifs/HollerStudios-animation-reaction-gif-holler-studios-0tvLwQiNjT3G6bnTzl&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;There’s a tendency when you look back at your efforts to see the failures. An endless todo list that makes everything else disappear behind the flaws.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/5315e3b9-128f-4335-a8c4-2bc15463542a&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Erol Ahmed&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But if we let our work ONLY be a catalog of mistakes it will make us feel resentful and guilty.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/36857df5-34ed-479a-bb69-2bb1f7e87029&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Anthony Antonellis, &lt;a href=&quot;https://giphy.com/gifs/picture-gold-painting-l3vRaEt5RXuEKPwly&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;We have to work on reframing to ALSO see our successes. We don’t ignore the failures, but we look PAST them to find the successes too.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/6e747c7a-0b2e-42fd-aaba-b22727051f65&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://giphy.com/gifs/hamlet-reading-learning-osmosis-1hXY6iNdTFpTW4je85&quot;&gt;Original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;When we criticize our past MISTAKES, we criticize ourselves for not knowing as much THEN as we do NOW.&lt;/p&gt;
&lt;p&gt;It’s FROM the mistakes that we’ve learned what we know now — our mistakes make it OURS.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/d7d20718-9228-4d27-882b-69418b62c493&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Jared Norby with modifications, &lt;a href=&quot;https://giphy.com/gifs/no-stop-dont-EIbNk3GZnHYOa9Zfz5&quot;&gt;original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And we cannot let a fear of mistakes prevent us from doing or trying new things.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/92744d40-a58d-494a-96cc-3116e6527610&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;As an example, I will confess, I’m terrible at gardening and landscaping.&lt;/p&gt;
&lt;p&gt;This is the opposite of my yard.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/21c85399-fffd-462a-a323-16853b331074&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And when I look at my yard, I see where the grass failed to grow even after all that money I spent.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;!-- 81-89 --&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/27f880bb-b631-4a6f-af16-a945eb548621&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Where the kudzu is trying to take over.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/0c6b9e8c-b004-42e3-bd42-6b283df2298f&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;I see the holes where I’ve killed the plants.&lt;/p&gt;
&lt;p&gt;And if I stop there, it’s all failures.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/519c3b06-80df-4749-936d-51b5762f23ad&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://giphy.com/gifs/moodman-Q81NcsY6YxK7jxnr4v&quot;&gt;Original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;But I’ve also:&lt;/p&gt;
&lt;p&gt;Learned how to dethatch and properly seed a lawn, revived my gardenia bushes from near death, and already reclaimed a huge section of my yard from the kudzu.&lt;/p&gt;
&lt;p&gt;These successes help me each time I go back out there and we need to remember and draw on these successes.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/0e3dd673-710f-47b8-8c0c-e63a28d7b32b&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Alexandru Goman&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;So to wrap this up:&lt;/p&gt;
&lt;p&gt;We stop creating from discouragement and demands but…&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/5452c814-f29f-40bb-bed7-6425d247b6fe&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Image by Alexandru Goman&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Creativity is not a talent, it’s a mode of thinking that’s available to us at every stage of life so:&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/f05e0a8c-1122-437c-9cec-35aacc9a7edd&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Images by Nikhil and Luis Villasmil (with modifications)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Protect your juice.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/56cb1824-008d-49e4-8a44-127e55bd89ff&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Images by Nikhil and Luis Villasmil (with modifications)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Find your freak.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/35f01992-439e-4521-91ed-b4a1a9b5318c&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;Images by Nikhil and Luis Villasmil (with modifications)&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;And remember, the world doesn’t need flawless. It needs people brave enough to show up and make something.&lt;/p&gt;
&lt;p&gt;Because every time we create, big or small, we’re not just making things, we’re SAYING something. And we need more of that.&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;slide&quot;&gt;
  &lt;figure class=&quot;slide-image&quot;&gt;
    &lt;img src=&quot;https://github.com/user-attachments/assets/8f1ed452-47fc-49c8-843a-1959b237a28f&quot; loading=&quot;lazy&quot; alt=&quot;&quot; /&gt;
    &lt;figcaption&gt;&lt;a href=&quot;https://giphy.com/gifs/video-games-game-mario-13qctMBrrgbwJi&quot;&gt;Original gif&lt;/a&gt;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;div class=&quot;slide-text flow&quot;&gt;
&lt;p&gt;Thanks everyone for listening!&lt;/p&gt;
&lt;p&gt;Happy creating!&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
</content:encoded><category>talks</category></item><item><title>What I&apos;ve Learned From Writing 100 Newsletters</title><link>https://nathan-long.com/blog/what-ive-learned-from-writing-100-newsletters/</link><guid isPermaLink="true">https://nathan-long.com/blog/what-ive-learned-from-writing-100-newsletters/</guid><description>A look back over what I&apos;ve learned from writing 100 issues of my newsletter.</description><pubDate>Mon, 11 Aug 2025 21:05:47 GMT</pubDate><content:encoded>&lt;p&gt;This week marks a big milestone for me: &lt;strong&gt;100 newsletter issues!&lt;/strong&gt; 🙌🎉 (&lt;a href=&quot;https://nathan-long.com/newsletter/&quot;&gt;Go sign up&lt;/a&gt; if you haven’t already! Or go check out the &lt;a href=&quot;https://buttondown.com/nathanlong/archive&quot;&gt;archives&lt;/a&gt; of the last 100 issues!)&lt;/p&gt;
&lt;p&gt;Am I an expert at writing now? 𝔸𝔹𝕊𝕆𝕃𝕌𝕋𝔼𝕃𝕐 ℕ𝕆𝕋. But I have learned a few helpful things along the way:&lt;/p&gt;
&lt;h2 id=&quot;writing-happens-all-the-time-all-around-you&quot;&gt;Writing happens all the time, all around you&lt;/h2&gt;
&lt;p&gt;When I started &lt;a href=&quot;https://nathan-long.com/blog/owning-my-voice/&quot;&gt;trying to write more&lt;/a&gt;, I was under the impression that writing was Serious Business™ and happened mostly while looking suitably thoughtful in front of a keyboard. I felt I needed something like &lt;a href=&quot;https://en.wikipedia.org/wiki/Walden&quot;&gt;Thoreau’s cabin&lt;/a&gt; and &lt;a href=&quot;https://caroline-writes.com/i-tried-stephen-kings-writing-routine/&quot;&gt;King’s routine&lt;/a&gt; to produce real work.&lt;/p&gt;
&lt;p&gt;However, I’ve come to realize that a lot of my writing DOESN’T happen at a desk — it’s in the ideas that bounce around in my head all week and the gentle, intentional pursuit of them. It can be an idea that occurs during a walk, a stray thought in conversation with a friend, or a series of events that sparks something. If I observe where my thoughts go and press into them, THEN when I sit down at a keyboard, I have something to work with.&lt;/p&gt;
&lt;p&gt;There are still times I sit to write and there’s absolutely nothing there, but the more I intentionally observe my thoughts and patterns, the more my subconscious helps push ideas to the front.&lt;/p&gt;
&lt;h2 id=&quot;writing-helps-me-think&quot;&gt;Writing helps me think&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“If you’re thinking without writing, you only think you’re thinking.”&lt;/p&gt;
&lt;p&gt;— Leslie Lamport&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When I try to capture an idea with words, I begin to see the ‘shape’ of it. Is it full of holes? Is there a special seed, but a lot of cruft? Is it a strange and weird thing that should never see the light of day? (I have a whole drawer full of those…) Writing it out creates a more concrete concept than simply letting it float around—it forces me to be more structured about my thinking.&lt;/p&gt;
&lt;h2 id=&quot;consistency-really-is-key&quot;&gt;Consistency really is key&lt;/h2&gt;
&lt;p&gt;“Write consistently” is probably the #1 recommendation of writing advice, and well … they’re not wrong! The more I write, the easier it becomes. Publishing short thoughts every week creates an accountability contract with myself that forces me to write, even on the weeks it would be much easier not to.&lt;/p&gt;
&lt;p&gt;Yet it can still be a struggle to keep coming back, and I’ve had to give myself some grace on busy weeks. I’ve written before about &lt;a href=&quot;https://buttondown.com/nathanlong/archive/spirit-of-play-and-the-implication-of/&quot;&gt;struggling in the gap&lt;/a&gt;, but I like this advice from &lt;a href=&quot;https://www.jeetmehta.com/posts/thrive-in-obscurity&quot;&gt;Jeet Mehta&lt;/a&gt; about the WHY:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“If you’re in it purely for the promised land of love, praise, followers, and fame from millions of people - it’s impossible to sustain. In every field, it takes years of practice, repetition, and “failed performances” before the first hit.”&lt;/p&gt;
&lt;p&gt;— Jeet Mehta&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The WHY and FOR WHO of what you’re doing is very important. Approval and admiration of others, while very natural and tempting to fall into, is a horrible metric for… pretty much anything.&lt;/p&gt;
&lt;p&gt;Rather, I’ve found that when I write for myself, or perhaps that one person who has the same very specific problem, it’s  liberating and much easier to keep pressing through the struggle!&lt;/p&gt;
&lt;h2 id=&quot;the-bar-is-lower-than-you-think&quot;&gt;The bar is lower than you think&lt;/h2&gt;
&lt;p&gt;I was also concerned when I started writing that I wouldn’t have anything unique or worthwhile to say. I was encouraged by &lt;a href=&quot;https://adactio.com/journal/20996&quot;&gt;this post by Jeremy Keith&lt;/a&gt;, who implores us all to remember that the combination of our perspective and experience IS unique to us.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“In other words, while it seems like there’s never a good time to write about something, the truth is that there’s never a bad time to write about something”&lt;/p&gt;
&lt;p&gt;— Jeremy Keith&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And we need &lt;a href=&quot;https://buttondown.com/nathanlong/archive/rss-voices-and-the-future-of-webassembly/&quot;&gt;more voices&lt;/a&gt;, more perspectives, more &lt;a href=&quot;https://buttondown.com/nathanlong/archive/the-weirdnet-and-frog-corrals/&quot;&gt;experiences&lt;/a&gt;, because THAT’S what makes the web weird and wonderful!&lt;/p&gt;
&lt;p&gt;And also why I’ll keep on writing! And why you should too!&lt;/p&gt;
</content:encoded><category>thoughts</category></item><item><title>Git: Fixing Mistakes</title><link>https://nathan-long.com/blog/git-fixing-mistakes/</link><guid isPermaLink="true">https://nathan-long.com/blog/git-fixing-mistakes/</guid><description>Like knowing where the brakes are on a car, knowing how to fix Git mistakes lets you try things with confidence.</description><pubDate>Fri, 21 Feb 2025 18:58:30 GMT</pubDate><content:encoded>&lt;p&gt;Git—love it or hate it, you’ll be using it nearly every day as a developer. When I started using Git, I felt like I was one bad command from blowing up a project history. Now, I still blow things up regularly, but now I know how to fix them. 😎 (Usually.)&lt;/p&gt;
&lt;p&gt;Mistakes happen—pretty much all the time. Knowing how to back out of them is like knowing where the brakes are on a car. It lets you try things with confidence, knowing that if it goes sideways you can back out and start over.&lt;/p&gt;
&lt;p&gt;Here are some ways to fix your mistakes in Git.&lt;/p&gt;
&lt;h2 id=&quot;forgot-something-small-amend-the-commit&quot;&gt;Forgot something small? Amend the commit.&lt;/h2&gt;
&lt;p&gt;Sometimes you’ve forgotten about that one file, or there’s one small change you forgot to put in. Rather than creating a new commit you can stage the change as if you’re going to make a new commit and then run:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --amend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will bring up the last commit message and smoosh the change in, making only one commit instead of two.&lt;/p&gt;
&lt;p&gt;If you don’t need to rewrite the commit message, just add the file you can use:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --amend&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --no-edit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In both cases, because we’re rewriting history, you’ll need to run:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; push&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --force-with-lease&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The ‘force’ part tells Git that, yes, you meant to do this, and the ‘lease’ part checks to make sure that there are no additional commits on the branch that your system doesn’t know about.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: As a general rule, force push on your own stuff with confidence BUT if it’s a shared branch, MAKE SURE to check with your team first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;tangled-or-accidentally-deleted-a-single-file-revert-it&quot;&gt;Tangled or accidentally deleted a single file? Revert it.&lt;/h2&gt;
&lt;p&gt;Sometimes, you need to reset one file in particular. You can do that with:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; checkout&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; origin/main&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; path/to/file&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This resets the specified file to the current version on a branch on the remote. If you need to reset it to a different branch, swap out &lt;code&gt;origin/main&lt;/code&gt; to &lt;code&gt;origin/whatever-you-need&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If rather than a branch, you need the file from a different point in the timeline, you can replace &lt;code&gt;origin/main&lt;/code&gt; with a commit hash like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; checkout&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 345e8bb2&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; path/to/file&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;burn-it-all-down-&quot;&gt;Burn it all down 🔥&lt;/h2&gt;
&lt;p&gt;Sometimes, things go wrong, and you want to dump your work, find the exit, and start over. There are two levels: the minor nuke and the major nuke.&lt;/p&gt;
&lt;h3 id=&quot;minor-nuke&quot;&gt;Minor Nuke&lt;/h3&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; reset&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --hard&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; HEAD&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is your minor nuke. It resets your current branch to the last commit (the HEAD) and discards every change made since then. This is useful if there’s been a lot of accidental churn or something got updated that you didn’t mean to.&lt;/p&gt;
&lt;h3 id=&quot;major-nuke&quot;&gt;Major Nuke&lt;/h3&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# while on the branch to reset, pull the lastest from the remote&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; fetch&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; origin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# hard reset branch to remote branch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; reset&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --hard&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; origin/&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt;branc&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# remove untracked files&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; clean&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -df&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is your major nuke, where &lt;code&gt;&amp;lt;branch&amp;gt;&lt;/code&gt; is the name of your current branch. This discards any changes you have locally that aren’t on the remote. It also force cleans any untracked directories. If you have things go very poorly&lt;/p&gt;
&lt;p&gt;If you choose a different branch to reset to, your current branch will become an exact match of what you’re pulling from. This can be what you want if you need to start over or reset something like an environment branch.&lt;/p&gt;
&lt;h2 id=&quot;rewind-time&quot;&gt;Rewind Time&lt;/h2&gt;
&lt;p&gt;If you’ve committed to a branch you didn’t mean to or need to move backward in time:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# find the commit you need&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; log&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# reset to that commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; revert&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt;commit-has&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;h&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;timeline-surgery&quot;&gt;Timeline Surgery!&lt;/h2&gt;
&lt;p&gt;It doesn’t happen often, but if you ever need to delete a commit with a surgical judo chop that happened BEFORE or IN-BETWEEN commits, you can do some interactive rebasing:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; rebase&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; HEAD~4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will bring up a dialog of the last 4 commits. Delete (or comment out) the lines corresponding to the commits you wish to delete, like so:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;pick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 2f05aba&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt; #will be preserved&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;#pick 3371cec ... #will be deleted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;#pick daed25c ... #will be deleted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;pick&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; e2b2a84&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt; #will be preserved&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you’ve finished, the commits you’ve selected to be deleted will be wiped from the repository’s history. Because we’re modifying history again, you will need to force push (but remember our general rule of force pushing!)&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>Testing AI Value</title><link>https://nathan-long.com/blog/testing-ai-value/</link><guid isPermaLink="true">https://nathan-long.com/blog/testing-ai-value/</guid><description>I&apos;ve been an AI skeptic, but it&apos;s time to test AI for myself... with a few boundaries.</description><pubDate>Fri, 24 Jan 2025 22:36:19 GMT</pubDate><content:encoded>&lt;p&gt;I was disappointed when &lt;a href=&quot;https://en.wikipedia.org/wiki/AlphaGo_versus_Lee_Sedol&quot;&gt;AlphaGo beat Lee Sedol&lt;/a&gt; in 2016, but intrigued when it influenced opening strategies that had been set for decades (or even centuries). I was alarmed in 2022 by how confidently ChatGPT spouted hallucinations with no factual basis but amused that it enabled me to create a haiku about my love of cheese effortlessly.&lt;/p&gt;
&lt;p&gt;I’ve been an AI skeptic, but it’s frickin’ everywhere. The Pragmatic Engineer newsletter tracked the &lt;a href=&quot;https://newsletter.pragmaticengineer.com/p/are-llms-making-stackoverflow-irrelevant&quot;&gt;decline of StackOverflow with the rise of ChatGPT&lt;/a&gt;. Anthropic is raising capital at a &lt;a href=&quot;https://techcrunch.com/2025/01/07/anthropic-reportedly-in-talks-to-raise-2b-at-60b-valuation-led-by-lightspeed/&quot;&gt;valuation of $60B&lt;/a&gt;. AI even recently popped up as a way to help my kid &lt;a href=&quot;https://blog.duolingo.com/duolingo-max/&quot;&gt;learn Spanish&lt;/a&gt;… AI is everywhere, seemingly in everyTHING, and it does not look like it’s going away soon.&lt;/p&gt;
&lt;p&gt;I like Cal Newport’s concept of &lt;a href=&quot;https://calnewport.com/on-digital-minimalism/&quot;&gt;digital minimalism&lt;/a&gt; when dealing with disruptive technology: aggressively clearing away digital clutter and keeping only the tools that bring value to your life. I think of it as digital &lt;a href=&quot;https://konmari.com/about-the-konmari-method/&quot;&gt;KonMari&lt;/a&gt;. (Literally why I slid out of most social media, minimal value there for me, folks.)&lt;/p&gt;
&lt;p&gt;So what value does AI bring a web developer? Addy Osmani has a &lt;a href=&quot;https://newsletter.pragmaticengineer.com/p/how-ai-will-change-software-engineering&quot;&gt;few opinions&lt;/a&gt; on that. Addy defines two effective patterns of AI usage he’s seen engineers use:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Bootstrappers&lt;/strong&gt; - Use AI to rapidly develop the initial codebase for a working prototype of a rough concept — good enough for initial user feedback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterators&lt;/strong&gt; - Daily use for code completion, refactoring, generating tests and documentation, and as a virtual ‘pair programmer’.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s the second pattern that speaks to me.&lt;/p&gt;
&lt;p&gt;I’m not OK with AI doing serious writing FOR me, but I am OK with having it help check my grammar and sentence structure: &lt;a href=&quot;https://www.youtube.com/watch?v=3kcQzCzSDvc&quot;&gt;Clippy&lt;/a&gt; is reborn (but maybe… more useful this time).&lt;/p&gt;
&lt;p&gt;I’m not OK with AI coding FOR me, but I am OK with AI helping me fix flaws in my code and find efficiency gains: my coding robot &lt;a href=&quot;https://www.youtube.com/watch?v=4j2xEwEHbrE&quot;&gt;buddy and me&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Addy notes that for effective use, engineers need to identify when the model is making errors, making it more valuable to senior-level engineers who can validate, strengthen, and handle edge cases. AI becomes an accelerator, a proof-checker, a sounding board.&lt;/p&gt;
&lt;p&gt;This feels like enough value to test, but I still have some reservations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Working as a remote employee is isolating enough. Replacing human collaboration with AI collaboration could silently diminish connection in a space where it’s already at a premium.&lt;/li&gt;
&lt;li&gt;Obfuscating the actual workings with ‘magic’ feels dangerous. If we don’t know WHY it works, we will not be able to fix it when it breaks.&lt;/li&gt;
&lt;li&gt;Fear is a bad reason to adopt something: either to prevent being ‘left behind’ or to stay relevant in today’s job market. Some things are just OK to skip. (Remember how WEB3 was going to change the world?)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-test&quot;&gt;The Test&lt;/h2&gt;
&lt;p&gt;So we test it. But we set some boundaries:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I do the work. The AI must function as an assistant to counteract the temptation to rely on it too heavily.&lt;/li&gt;
&lt;li&gt;It remains opt-in. No inline recommendations. This interface must only come when called.&lt;/li&gt;
&lt;li&gt;I must understand everything I commit. If I don’t understand it, or can’t reach an understanding, the recommendation gets nuked.&lt;/li&gt;
&lt;li&gt;Data must remain reasonably protected and not be used for training data. This one comes from my peers at Viget and I wholeheartedly agree. This code is my work product and must be legally protected within the bounds of our contracts.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And then we review:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does it improve my work product like catching errors or improving clarity?&lt;/li&gt;
&lt;li&gt;Does it make me more efficient?&lt;/li&gt;
&lt;li&gt;Does it introduce errors?&lt;/li&gt;
&lt;li&gt;How expensive is it?&lt;/li&gt;
&lt;li&gt;Does the result justify the cost?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-implementation&quot;&gt;The Implementation&lt;/h2&gt;
&lt;p&gt;After reviewing several options I’ve settled on the &lt;a href=&quot;https://github.com/olimorris/codecompanion.nvim&quot;&gt;CodeCompanion&lt;/a&gt; plugin for NeoVim. In particular, its chat interface will let me send the contents of a buffer to the LLM, and I can control the level of access it has. This fits the pattern I’m looking for.&lt;/p&gt;
&lt;p&gt;This will also allow me to test different LLMs. I plan to start with &lt;a href=&quot;https://www.anthropic.com/claude/sonnet&quot;&gt;Claude 3.5 Sonnet&lt;/a&gt; as it’s aimed at the type of work I do and Anthropic honors my #4 boundary of not using my data for training purposes for commercial products.&lt;/p&gt;
&lt;p&gt;So, we’ll see. If it proves valuable, I’ll keep it around. If not, well then…&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>My Personal tmux Mystery</title><link>https://nathan-long.com/blog/my-personal-tmux-mystery/</link><guid isPermaLink="true">https://nathan-long.com/blog/my-personal-tmux-mystery/</guid><description>People laud it, but I can&apos;t find a use for it. Am I the weird one?</description><pubDate>Thu, 22 Aug 2024 15:23:23 GMT</pubDate><content:encoded>&lt;p&gt;Roll in enough Vim circles and you’ll hear people sing the praises of &lt;code&gt;tmux&lt;/code&gt;, the &lt;a href=&quot;https://github.com/tmux/tmux/wiki&quot;&gt;terminal multiplexer&lt;/a&gt;. And to be honest… I’ve never really quite gotten it.&lt;/p&gt;
&lt;p&gt;I tried it and didn’t get why it was great. Recently, Adam Stacoviak from the &lt;a href=&quot;https://changelog.com/&quot;&gt;Changelog&lt;/a&gt; mirrored my thoughts in this short clip:&lt;/p&gt;
&lt;div class=&quot;feature&quot;&gt;
&lt;iframe style=&quot;aspect-ratio: 560/315; width: 100%; height: auto;&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/nAc_kxlGXFY?si=RCzaLBzFXO6GYA4m&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;The selling points that Nick and Jared make for &lt;code&gt;tmux&lt;/code&gt; actually made me realize why it’s never quite fit for me. Their two main points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;tmux&lt;/code&gt; sessions are a great way to manage projects and window arrangements.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tmux&lt;/code&gt; allows persistent processes over an SSH connection.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It made me realize that my normal workflow differs from what they describe. Which made me start to wonder, “Am I the outlier here?”&lt;/p&gt;
&lt;p&gt;So here’s a quick overview of how my workflow differs.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I don’t use complex window arrangements and have other ways to manage projects.&lt;/li&gt;
&lt;li&gt;(The big one) Most of my development is local-only.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;projects-and-window-arrangement&quot;&gt;Projects and Window Arrangement&lt;/h2&gt;
&lt;p&gt;To open up a project I:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open up WezTerm&lt;/li&gt;
&lt;li&gt;Type the shortcut saved from my &lt;a href=&quot;https://github.com/huyng/bashmarks&quot;&gt;bashmarks script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;nvim&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use the amazing &lt;a href=&quot;https://github.com/nvim-telescope/telescope.nvim&quot;&gt;Telescope plugin&lt;/a&gt; to pull up any files I need.&lt;/li&gt;
&lt;li&gt;Start coding.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once I’m in the project I typically work with a single full-screen window. I use lots of window splits to compare files, put them side by side, keep one open as a reference, or whatever … but that’s all internal to NeoVim. I manage my git history through the &lt;a href=&quot;https://github.com/tpope/vim-fugitive&quot;&gt;Fugitive plugin&lt;/a&gt;. If I need to run a quick command I have a &lt;a href=&quot;https://github.com/voldikss/vim-floaterm&quot;&gt;floating terminal&lt;/a&gt; bound to a function key that I can quickly bring up and dismiss without leaving NeoVim. Once I’m IN NeoVim, I don’t need to leave it.&lt;/p&gt;
&lt;p&gt;Adam jokingly (seriously?) says halfway through the video, “What’s wrong with multiple tabs?” And … 😳 … that’s EXACTLY what I do when I need to run a separate process in the background, I kick open a new tab on WezTerm and then leave it running. Need to fire off a container that’s not detached? Open a new tab. Need to run my local &lt;a href=&quot;https://tiddlywiki.com/&quot;&gt;node-based wiki&lt;/a&gt; in the background? Open a new tab and fire off the process.&lt;/p&gt;
&lt;p&gt;The tabs act as my expander and window management. Because I start fresh each time with a simple arrangement, I don’t need to save the session and the project. I just need to get there in the command line and open NeoVim.&lt;/p&gt;
&lt;p&gt;I realize many people don’t work in this one-window-at-a-time-in-fullscreen mode like I do. If there was a lot of setup or tear down between projects I could see sessions being useful, I just don’t think it will help me much.&lt;/p&gt;
&lt;h2 id=&quot;local-only-development&quot;&gt;Local Only Development&lt;/h2&gt;
&lt;p&gt;This is the main reason of that makes &lt;code&gt;tmux&lt;/code&gt; not a real boost for me — I simply don’t often work through an SSH connection.&lt;/p&gt;
&lt;p&gt;Most of my development work happens on a local checkout of a project, running on local containers. Occasionally I’ll need to SSH into something and run a few commands, but typically they’re relatively minor and quick — I’ve never had a command disconnect on me.&lt;/p&gt;
&lt;p&gt;IF I did more work on remote boxes I could see this being a critical part, but it’s simply not a large part of my workflow.&lt;/p&gt;
&lt;p&gt;So I’m not crazy, I’m just not who &lt;code&gt;tmux&lt;/code&gt; is for.&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>Console Cowboys In Cyberspace</title><link>https://nathan-long.com/blog/console-cowboys-in-cyberspace/</link><guid isPermaLink="true">https://nathan-long.com/blog/console-cowboys-in-cyberspace/</guid><description>Do you know anything about hackers?</description><pubDate>Fri, 28 Jun 2024 20:55:09 GMT</pubDate><content:encoded>&lt;p&gt;In a blast of nostalgia I came across this old clip from the PBS show Ghostwriter:&lt;/p&gt;
&lt;div class=&quot;feature&quot;&gt;
&lt;iframe style=&quot;aspect-ratio: 560/315; width: 100%; height: auto;&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/bLlj_GeKniA?si=7ushXwH3ucHo3Sen&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;In addition to this encapsulation of a part of my early childhood, there’s some immediate takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sick &lt;a href=&quot;https://www.goodreads.com/book/show/6088007-neuromancer&quot;&gt;Neuromancer&lt;/a&gt; reference.&lt;/li&gt;
&lt;li&gt;We should all petition to change our titles to &lt;code&gt;Console Cowboys in Cyberspace&lt;/code&gt;. 🤠&lt;/li&gt;
&lt;li&gt;The idealistic representation of 90’s hacker culture…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think after decades of internet culture it’s much easier to be pessimistic about the future, but the things we talk about that made the beginning of the internet great are still there, just covered up by so much other noise. ‘Curiosity’ and ‘Imagination’ haven’t gone away, we just need to be more active in bringing them to the forefront!&lt;/p&gt;
</content:encoded><category>thoughts</category></item><item><title>Colorschemes for the Discerning Developer</title><link>https://nathan-long.com/blog/colorschemes-for-the-discerning-developer/</link><guid isPermaLink="true">https://nathan-long.com/blog/colorschemes-for-the-discerning-developer/</guid><description>Maybe it&apos;s time for a fresh coat of paint.</description><pubDate>Tue, 11 Jun 2024 19:50:21 GMT</pubDate><content:encoded>&lt;p&gt;That colorscheme in your editor…&lt;/p&gt;
&lt;p&gt;…how long have you been looking at it? When did you change it last?&lt;/p&gt;
&lt;p&gt;Years? Yeah, me too. 😅&lt;/p&gt;
&lt;p&gt;I recently took a spin to see what was new out there, and boy, are there some cool colorschemes!&lt;/p&gt;
&lt;p&gt;I eventually settled on a lovely theme called &lt;a href=&quot;https://catppuccin.com/&quot;&gt;Catppuccin&lt;/a&gt; (specifically the Mocha variant, the darkest one). But here are some of the others I came across. Maybe there’s something here to give your editor a nice new coat of paint as well!&lt;/p&gt;
&lt;h2 id=&quot;multi-editor-colorschemes&quot;&gt;Multi-Editor Colorschemes&lt;/h2&gt;
&lt;p&gt;These colorschemes have multiple ports, making them a nice way to extend it across all your tooling.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://catppuccin.com/&quot;&gt;Catppuccin&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Beautifully balanced pastels pop off a dark blue background. It also has one of the nicest light variants I’ve seen.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://rosepinetheme.com/&quot;&gt;Rosé Pine&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Another balanced theme with multiple variations… This palette trends towards warmer natural colors.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/folke/tokyonight.nvim&quot;&gt;Tokyo Night&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;This was my pick before Catppuccin. I liked it so much that I used some of the colors here on my site! It celebrates the neon of Tokyo nights.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://draculatheme.com/&quot;&gt;Dracula&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;A nice dark scheme with an amazing amount of ports and implementations. Slightly darker than Catppuccin and Rosé Pine. The purple and orange feel especially thematic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.nordtheme.com/&quot;&gt;Nord&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;The power of BLUE! It’s bold and STRONG.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://ethanschoonover.com/solarized/&quot;&gt;Solarized&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;This was the OG theme that felt like real consideration had been put into the colors. I consider this the predecessor of most of these themes and it’s STILL a pretty solid choice.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/voronianski/oceanic-next-color-scheme&quot;&gt;Oceanic Next&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;A slightly desaturated palette, this sits somewhere in between Solarized and Tomorrow Night.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/chriskempson/tomorrow-theme&quot;&gt;Tomorrow Night&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Bold color choices, and nice if you don’t want a blue background.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/cocopon/iceberg.vim&quot;&gt;Iceberg&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;High contrast, bluish theme. Feels like if you turned down the lights on Nord a bit.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/sonph/onehalf&quot;&gt;One Half&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Based on Atom’s ‘One’ colorscheme, a practical mix of colors on a gray (or light gray!) background.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;neovim-only&quot;&gt;NeoVim Only&lt;/h2&gt;
&lt;p&gt;These colorschemes are mostly for Vim/NeoVim, but many have other ports.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/rebelot/kanagawa.nvim&quot;&gt;Kanagawa&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Based on the Katsushika Hokusai painting of the same name, this theme has some unique colors giving it a different feel.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/navarasu/onedark.nvim&quot;&gt;OneDark&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Another Atom One Dark successor, with TreeSitter support.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/morhetz/gruvbox&quot;&gt;Gruvbox&lt;/a&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;One of the few themes that feels like distinctly its own thing. It has retro vibes that make me nostalgic for the green and amber-colored displays of the past.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;decorating-your-personalized-development-environment&quot;&gt;Decorating your ‘Personalized Development Environment’&lt;/h2&gt;
&lt;p&gt;I have always liked TJ DeVrie’s concept of the &lt;a href=&quot;https://www.youtube.com/watch?v=QMVIJhC9Veg&quot;&gt;PDE&lt;/a&gt;, of creating an ENVIRONMENT that’s JUST YOURS and a joy to use. One of the best ways I know of to personalize a space is with color. So what does YOUR colorscheme say about you? If it’s not speaking to YOU, maybe it’s time to change it up!&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>ffmpeg Basics and Recipes</title><link>https://nathan-long.com/blog/ffmpeg-basics-and-recipes/</link><guid isPermaLink="true">https://nathan-long.com/blog/ffmpeg-basics-and-recipes/</guid><description>A few recipes for common video tasks</description><pubDate>Wed, 27 Mar 2024 16:44:55 GMT</pubDate><content:encoded>&lt;p&gt;Recently I wrote about &lt;a href=&quot;https://www.ffmpeg.org/&quot;&gt;ffmpeg&lt;/a&gt;, a Swiss-army knife command-line utility for media. While I find it incredibly handy NOW, it took time to build up a working knowledge of the tool to handle certain tasks with confidence. The downside of having a versatile tool is that it can do an awful lot of things!&lt;/p&gt;
&lt;p&gt;So let’s break down how it works and I’ll share some of my most common recipes for converting media.&lt;/p&gt;
&lt;h2 id=&quot;installing&quot;&gt;Installing&lt;/h2&gt;
&lt;p&gt;The easiest way to install (on a Mac) is with &lt;a href=&quot;https://brew.sh/&quot;&gt;homebrew&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;brew install ffmpeg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You can also download directly from &lt;a href=&quot;https://ffmpeg.org/download.html&quot;&gt;their site&lt;/a&gt;, but that’s a tad more involved. Homebrew drops it into your path, ready to go.&lt;/p&gt;
&lt;h2 id=&quot;basics&quot;&gt;Basics&lt;/h2&gt;
&lt;p&gt;An &lt;code&gt;ffmpeg&lt;/code&gt; command will take:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An &lt;strong&gt;input file,&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;several sets of &lt;strong&gt;options&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;and an &lt;strong&gt;output file&lt;/strong&gt; to spit everything out into.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A basic command will look something like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input-file&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; {lots&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; options&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; here}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output-file&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;compression&quot;&gt;Compression&lt;/h2&gt;
&lt;p&gt;One of the handiest things &lt;code&gt;ffmpeg&lt;/code&gt; can do is give you fine-grained control over video compression. Not only can you pick the method of compression, you can choose what codec to use.&lt;/p&gt;
&lt;p&gt;There are several methods of compression, but two of the main ones you’ll see are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-b:v&lt;/code&gt; Sets a fixed &lt;strong&gt;bitrate&lt;/strong&gt; of the output, measured in kb (ex. 1200kb) or megabytes (ex. 2M). You’ll typically see values between 1200-6000kb. The lower the number, the less ‘bits’ will be used.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-crf&lt;/code&gt; Sets a &lt;strong&gt;Constant Rate Factor&lt;/strong&gt; (available for &lt;a href=&quot;http://www.videolan.org/developers/x264.html&quot;&gt;x264&lt;/a&gt;, &lt;a href=&quot;http://x265.org/&quot;&gt;x265&lt;/a&gt; and &lt;a href=&quot;https://www.webmproject.org/code/&quot;&gt;libvpx&lt;/a&gt;). This is a smart value between 0 and 51 that attempts to vary the bitrate per frame for better-looking results (less artifacts). For x264, sane values are between 18 (better quality, larger file) and 28 (lower quality, larger file). The default is 23, so you can use this as a starting point.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bitrates can get quite complicated but give you a lot of control. They’re useful in situations for streaming videos or where you need to specify a minimum, maximum, or ensure a constant bitrate. Because I use &lt;code&gt;ffmpeg&lt;/code&gt; to operate on smaller videos I gravitate towards &lt;code&gt;-crf&lt;/code&gt; values.&lt;/p&gt;
&lt;p&gt;Read more on &lt;a href=&quot;https://slhck.info/video/2017/02/24/crf-guide.html&quot;&gt;how CRF works&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;convert-to-h264-mp4&quot;&gt;Convert to h264 mp4&lt;/h3&gt;
&lt;p&gt;If you need to share a video, you can’t go wrong with an mp4. It’s widely supported and everything except ancient devices should be able to play it. Here’s a good default compression recipe:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mov&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libx264&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -crf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 28&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -preset&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; very&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; slow&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Option descriptions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-c:v&lt;/code&gt; selects the compression format to &lt;code&gt;libx264&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-crf 28&lt;/code&gt; sets a decent constant rate factor.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-preset&lt;/code&gt; sets a series of options that will provide a certain encoding speed to compression ratio, slower speeds mean more compression.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One benefit of H264 compression is it’s FAST and pretty efficient. I can usually get a &lt;code&gt;.mov&lt;/code&gt; file down to about 10% of its size with this recipe.&lt;/p&gt;
&lt;p&gt;Read more about &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/H.264&quot;&gt;H264 compression&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;convert-to-webm&quot;&gt;Convert to webm&lt;/h3&gt;
&lt;p&gt;webm is now &lt;a href=&quot;https://caniuse.com/webm&quot;&gt;fully supported in modern browsers&lt;/a&gt; but is a little more finicky to encode, it’s quite easy to set some options and wind up with large webm files. I find webm’s to be slightly larger than well-compressed mp4s.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libvpx-vp9&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -crf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 30&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -b:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-c:v&lt;/code&gt; selects the compression format to &lt;code&gt;libvpx-vp9&lt;/code&gt; which is the latest webm codec.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-b:v&lt;/code&gt; is set to 0 (required) to allow constant quality settings with:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-crf 30&lt;/code&gt; which follows the CRF numbers outlined above&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They recommend that you use a two-pass method on webm for the best results, but it is QUITE slow, so grab a coffee while you wait:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libvpx-vp9&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -b:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -crf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 30&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -pass&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -an&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; /dev/null&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libvpx-vp9&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -b:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -crf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 30&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -pass&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:a&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libopus&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.webm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-pass {number}&lt;/code&gt; lets it know that we’re doing some multipass business here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-f&lt;/code&gt; forces output, in this case &lt;code&gt;null&lt;/code&gt; to prevent the first pass from outputting anything&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; is a normal bash/zsh command to run the second command if the first command is successful.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read more on &lt;a href=&quot;https://trac.ffmpeg.org/wiki/Encode/VP9&quot;&gt;VP9 compression here&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Heads Up!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you do a two-pass encoding, the first pass will LOOK like nothing is happening, just be patient, it will eventually start showing progress and spit out your file.&lt;/p&gt;
&lt;p&gt;I’ll also state that I haven’t found much difference between the single pass and the double pass methods, but I also think I’m not operating at the size and length of video files to see the difference. If it’s a smaller file, the single pass is probably good enough for your needs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;common-recipes&quot;&gt;Common Recipes&lt;/h2&gt;
&lt;p&gt;Now we’ve talked about compression, what are some other things you can do? Here are a few of the tasks I find myself running.&lt;/p&gt;
&lt;h3 id=&quot;scale-video-by-height&quot;&gt;Scale Video by Height&lt;/h3&gt;
&lt;p&gt;One of the easiest ways to shrink file size is to reduce the size of the video, especially if you’ve got a mondo-sized source file. To produce a 1080p video:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; scale=-1:1080&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-vf&lt;/code&gt; is an alias for &lt;code&gt;-filter:v&lt;/code&gt; (video filter)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scale=-1:1080&lt;/code&gt;resizes the video to ’‘whatever’’ (the -1) by 1080 pixels high. If the video is 16:9, the width would then be 1920. If you ran &lt;code&gt;-1:720&lt;/code&gt; the output would be 1280x720 for a 16:9 video.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Common resolutions for 16:9 video are: 1024×576, 1152×648, 1280×720, 1366×768, 1600×900, 1920×1080, 2560×1440 and 3840×2160. You can use these as some numbers to start with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;trim-video-by-timecode&quot;&gt;Trim Video by Timecode&lt;/h3&gt;
&lt;p&gt;Sometimes you only need a slice of a video, or you need to just shave off the first few seconds of something. Here’s how you trim by timecode:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -ss&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 00:01:02.500&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 00:01:03.250&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; copy&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-ss&lt;/code&gt; indicates start time in seconds (with decimals for sub-second times)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-to&lt;/code&gt; is the end time in &lt;code&gt;hh:mm:ss&lt;/code&gt; format&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c&lt;/code&gt; copies existing formats&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -ss&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 00:00:03&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Not providing a &lt;code&gt;-to&lt;/code&gt; means it runs to the end of the file. This example will trim off the first 3 seconds of the file.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;remove-audio&quot;&gt;Remove Audio&lt;/h3&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; example.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; copy&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -an&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; example-nosound.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-an&lt;/code&gt; removes audio&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c&lt;/code&gt; copies existing formats&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;boost-volume&quot;&gt;Boost Volume&lt;/h3&gt;
&lt;p&gt;Note that this can work on audio files, not just videos.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.wav&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -af&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;volume=1.5&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.wav&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-af&lt;/code&gt; set audio filters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;volume=1.5&lt;/code&gt; and boost the volume to 150% of the original. This can be proportional or in decibels: &lt;code&gt;volume=-5dB&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;create-a-poster-image&quot;&gt;Create a Poster Image&lt;/h3&gt;
&lt;p&gt;Embedding an HTML video and don’t have a poster image? Rather than trying to awkwardly screengrab something, you can use this to create an image at a specific timestamp.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; example.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -ss&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 00:00:00.000&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vframes&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;thumb.png&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-ss&lt;/code&gt; seeks to where we’re pulling a frame (in this case, the start of the video)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-vframes&lt;/code&gt; sets the number of frames to output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*.png&lt;/code&gt; (in output file extension) converts it to a PNG image&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;advanced-recipes&quot;&gt;Advanced Recipes&lt;/h2&gt;
&lt;p&gt;Here are some of the more complicated recipes I use that string together some of the operations I’ve already covered.&lt;/p&gt;
&lt;h3 id=&quot;create-ambient-video-mp4&quot;&gt;Create Ambient Video MP4&lt;/h3&gt;
&lt;p&gt;Need to load up a looping ambient video, but you’re given a 200+MB file?&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libx264&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -crf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 28&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -preset&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; veryslow&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; scale=-1:720&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -movflags&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; +faststart&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -an&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Uses H264 compression&lt;/li&gt;
&lt;li&gt;Uses a Constant Rate of 28&lt;/li&gt;
&lt;li&gt;Uses the &lt;code&gt;veryslow&lt;/code&gt; preset for better compression&lt;/li&gt;
&lt;li&gt;Reduces to 1080x720 dimensions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-movflags +faststart&lt;/code&gt; moves the metadata to the front of the video allowing immediate streaming (rather than waiting for the whole file to load)&lt;/li&gt;
&lt;li&gt;Removes audio channels since it will be playing muted.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;gif-to-mp4&quot;&gt;GIF to MP4&lt;/h3&gt;
&lt;p&gt;Want a GIF, but not with the filesize overhead?&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; animated.gif&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -movflags&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; +faststart&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -pix_fmt&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; yuv420p&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;scale=trunc(iw/2)*2:trunc(ih/2)*2&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; video.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-movflags +faststart&lt;/code&gt; to move metadata to the front for streaming&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-pix_fmt yuv420p&lt;/code&gt; use the pixel format of &lt;code&gt;yuv420p&lt;/code&gt; which is a chroma subsampling scheme.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-vf&lt;/code&gt; starts a filter. The formula in the string will upscale the resolution by 2x.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mp4-to-gif&quot;&gt;MP4 to GIF&lt;/h3&gt;
&lt;p&gt;Going the other way is a bit more involved because we need to take a full-color video and restrict it to 256 colors for a GIF. First, we’ll need to generate a color palette from the video:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -y&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -ss&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -vf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; fps=10,scale=320:-1:flags=lanczos,palettegen&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; palette.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Generates a PNG color palette&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-ss&lt;/code&gt; seeks to timecode for what frame to sample the palette from&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, using that palette, we’ll convert the video into the best-fit gif.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; palette.png&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -filter_complex&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;fps=10,scale=-1:480:flags=lanczos[x];[x][1:v]paletteuse&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.gif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Notice the two inputs, we grab the video and the palette&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-filter-complex&lt;/code&gt; Sets up a filter that: sets the fps to 10, scales it to 480 pixels high, and uses the palette we specified earlier.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;max-compatibility&quot;&gt;Max Compatibility&lt;/h3&gt;
&lt;p&gt;I hardly use this recipe anymore since MOST devices can handle mp4’s just fine. But just in case you need to get something running on an older device:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; libx264&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -crf&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 23&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -profile:v&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; baseline&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -level&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 3.0&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -pix_fmt&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; yuv420p&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -c:a&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; aac&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -ac&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -b:a&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; 128k&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -movflags&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; +faststart&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; output.mp4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;H.264 video and AAC audio is the best combination for broad support. Ancient browsers that do not have H.264 decoders however will need a VP8/Vorbis video.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-profile:v&lt;/code&gt; baseline and &lt;code&gt;-level 3.0&lt;/code&gt; options are only needed for old mobile devices that cannot handle CPU-intensive features of H.264.&lt;/li&gt;
&lt;li&gt;Uses a constant rate of 23.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-movflags +faststart&lt;/code&gt; moves the metadata to the front of the video allowing immediate streaming&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;If you’ve come across other recipes, or you see a way how these could be improved, shoot me a note! I’m constantly improving and adding to this list.&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>These Are a Few of My Favorite CLI Things...</title><link>https://nathan-long.com/blog/a-few-of-my-favorite-cli-things/</link><guid isPermaLink="true">https://nathan-long.com/blog/a-few-of-my-favorite-cli-things/</guid><description>I take a look at a few of the CLI utilities I&apos;ve picked up along the way.</description><pubDate>Thu, 21 Mar 2024 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve recently been perusing the setups at &lt;a href=&quot;https://uses.tech/&quot;&gt;uses.tech&lt;/a&gt;. It’s interesting to see where people have landed on hardware and software options, but I started thinking about some of the miscellaneous CLI utilities I’ve tried and picked up along the way. My work LIVES inside the terminal (as a Vim user) and here are some of the small things I’ve found to make it easier on myself (in no particular order):&lt;/p&gt;
&lt;h2 id=&quot;tig&quot;&gt;tig&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Site:&lt;/strong&gt; &lt;a href=&quot;https://jonas.github.io/tig/&quot;&gt;https://jonas.github.io/tig/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Git can be weird sometimes. While I don’t use &lt;code&gt;tig&lt;/code&gt; to perform any git operations, I find the explorer view helpful to see what’s going on in there. Have you rebased correctly? What did your teammates just commit, and what files did it affect? I run this constantly on active projects to keep a mental picture of what’s going on.&lt;/p&gt;
&lt;p&gt;(So what DO I do most of my git actions with? I use Tim Pope’s excellent &lt;a href=&quot;https://github.com/tpope/vim-fugitive&quot;&gt;Fugitive&lt;/a&gt; Vim plugin, but that’s more Vim-based, so it doesn’t qualify for this article…)&lt;/p&gt;
&lt;div class=&quot;feature rounded-1 overflow-hidden lh-0&quot;&gt;
  &lt;video class=&quot;w-full h-auto&quot; autoplay muted loop playsinline&gt;
      &lt;source src=&quot;https://github.com/nathanlong/nathanlong.github.io/assets/623568/44491262-1f61-402d-b394-31c16aa65bcd&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
&lt;/div&gt;
&lt;h2 id=&quot;exa&quot;&gt;exa&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Site:&lt;/strong&gt; &lt;a href=&quot;https://the.exa.website/&quot;&gt;https://the.exa.website/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;exa&lt;/code&gt; is a more colorful way to list files and directories. It may seem simple, but when you’re navigating around, having color-coded items, as well as other pertinent information right at hand, is a nice quality-of-life improvement. This has completely replaced &lt;code&gt;ls -laF&lt;/code&gt; for me. I’ve aliased the two modes I use the most to:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; ll&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;exa --long --header --git --all&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt; # long form, everything&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;alias&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; lt&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;exa --tree --level=2 --long --all&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt; # tree view&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;feature rounded-1 overflow-hidden lh-0&quot;&gt;
  &lt;video class=&quot;w-full h-auto&quot; autoplay muted loop playsinline&gt;
    &lt;source src=&quot;https://github.com/nathanlong/nathanlong.github.io/assets/623568/4faafda2-4c14-49ad-a48f-7f21bbf289dd&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
&lt;/div&gt;
&lt;h2 id=&quot;bashmarks&quot;&gt;bashmarks&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt; &lt;a href=&quot;https://github.com/huyng/bashmarks&quot;&gt;https://github.com/huyng/bashmarks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I found this bash script about 12 years ago and I’ve used it every day since then. It’s a very simple bookmarking system for navigating directories with tab completion and very short commands. &lt;code&gt;s&lt;/code&gt; saves the current directory with the name you give it, and &lt;code&gt;g&lt;/code&gt; + &lt;code&gt;name&lt;/code&gt; takes you right back to that path.&lt;/p&gt;
&lt;p&gt;I’ve tried fuzzy completion and other fancier bookmarking options, but this one is so simple and does just what I need that I’ve wound up back at bashmarks several times. I’ll save the fancy fuzzy searching for INSIDE projects, I’d rather just teleport to where I need to go. I highly recommend this script if you need a simple navigation option.&lt;/p&gt;
&lt;h2 id=&quot;ffmpeg&quot;&gt;ffmpeg&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Site:&lt;/strong&gt; &lt;a href=&quot;https://ffmpeg.org/&quot;&gt;https://ffmpeg.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is an absolute monster of a video and audio tool. Need to compress a looping ambient hero video? Run it through &lt;code&gt;ffmpeg&lt;/code&gt;. Need to strip the audio from something? Run it through &lt;code&gt;ffmpeg&lt;/code&gt;. Need to quickly trim down a video? You guessed it, &lt;code&gt;ffmpeg&lt;/code&gt;. I have shaved innumerable megabytes of video data with this tool while processing assets for client projects.&lt;/p&gt;
&lt;p&gt;The only beef I have with it is that writing out the arguments for proper compression is like trying to write an arcane spell. You need special knowledge of &lt;code&gt;ffmpeg&lt;/code&gt;’s vast options and some knowledge of video codecs to make your way. Yet despite that, it remains an indispensable tool for front-end development.&lt;/p&gt;
&lt;p&gt;The video screencaps in this article were captured with a normal Quicktime screen recording, but I have a special quickfire mov-to-mp4 command that uses the absolute basic mp4 defaults and still manages to make them 5-10% of the original &lt;code&gt;.mov&lt;/code&gt; size:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# Quicktime mov files are so big!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; movtomp4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;	for&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; i &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; *.mov&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;		ffmpeg&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;$i&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &quot;${i}.mp4&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;	done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At some point, I’ll have to write up my cheatsheet for &lt;code&gt;ffmpeg&lt;/code&gt;…&lt;/p&gt;
&lt;h2 id=&quot;degit&quot;&gt;degit&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt; &lt;a href=&quot;https://github.com/Rich-Harris/degit&quot;&gt;https://github.com/Rich-Harris/degit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I learned about &lt;code&gt;degit&lt;/code&gt; from the Vite docs, and it’s become my go-to way for initializing a prototype kit. When I have an idea I want to try, I turn to &lt;code&gt;degit&lt;/code&gt; and let it grab all the configuration files for me. Need Tailwind? Grab a starter kit for that. Testing out something in React? There’s a starter kit you can pull for that too.&lt;/p&gt;
&lt;p&gt;In fact, I made a few &lt;a href=&quot;https://nathan-long.com/blog/vite-rapid-prototype-starter-kits/&quot;&gt;starter kits of my own&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;just&quot;&gt;just&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt; &lt;a href=&quot;https://github.com/casey/just&quot;&gt;https://github.com/casey/just&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Do you have a project that needs lots of arbitrary commands? Things you have to continually look up and paste back into your console? &lt;code&gt;just&lt;/code&gt; helps provide a unified layer on top of whatever system you’re using. Unlike a Rakefile or Makefile, it’s not bound to any specific language, you can just feed it whatever commands you’d like and it’ll run them for you.&lt;/p&gt;
&lt;p&gt;It even pairs nicely with the previous utility, &lt;code&gt;degit&lt;/code&gt; to help form a rapid-start prototyping area with &lt;a href=&quot;https://nathan-long.com/blog/vite-rapid-prototype-starter-kits/&quot;&gt;project templates&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;asdf&quot;&gt;asdf&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Site:&lt;/strong&gt; &lt;a href=&quot;https://asdf-vm.com/&quot;&gt;https://asdf-vm.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It can be tough to make sure that your environments are in sync across projects, versions, and even programming languages. For a while, I was using language-specific version managers (&lt;code&gt;nvm&lt;/code&gt;, &lt;code&gt;rvm&lt;/code&gt;, with all the shims that come with it) but Viget introduced me to &lt;code&gt;asdf&lt;/code&gt; as an all-in-one way to manage all these different languages in a way that didn’t have me pulling my hair out.&lt;/p&gt;
&lt;h2 id=&quot;btop&quot;&gt;btop&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt; &lt;a href=&quot;https://github.com/aristocratos/btop&quot;&gt;https://github.com/aristocratos/btop&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Think Activity Monitor, but way cooler looking and accessible without clicking a bunch of things. &lt;code&gt;btop&lt;/code&gt; is a pleasing way to see just what is making your computer slow down to a crawl (shakes fist at Docker).&lt;/p&gt;
&lt;div class=&quot;feature rounded-1 overflow-hidden lh-0&quot;&gt;
&lt;video class=&quot;w-full h-auto&quot; autoplay muted loop playsinline&gt;
      &lt;source src=&quot;https://github.com/nathanlong/nathanlong.github.io/assets/623568/15fc78f3-a1d3-4e30-acca-a070f4fea860&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;/div&gt;
</content:encoded><category>tooling</category></item><item><title>Talking about Motion for the Web</title><link>https://nathan-long.com/blog/talk-motion-2024/</link><guid isPermaLink="true">https://nathan-long.com/blog/talk-motion-2024/</guid><description>Ways to classify and plan for motion in websites.</description><pubDate>Wed, 06 Mar 2024 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I mentioned last year that I was working on a series on &lt;a href=&quot;https://nathan-long.com/blog/building-motion-for-the-web/&quot;&gt;building motion for the web&lt;/a&gt;. Well, that series turned into a talk, and we recorded it.&lt;/p&gt;
&lt;p&gt;In it, I talk about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ways to &lt;strong&gt;Classify&lt;/strong&gt; motion to help build shared language when talking about types of motion for the web.&lt;/li&gt;
&lt;li&gt;Building a ‘&lt;strong&gt;Complexity&lt;/strong&gt; tier list’ for motion to help assist in planning.&lt;/li&gt;
&lt;li&gt;Some recommended Collaboration methods for designing and implementing motion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(You see the 3 C’s in there? Alliteration FTW 😎)&lt;/p&gt;
&lt;p&gt;You can check the full thing out below.&lt;/p&gt;
&lt;div class=&quot;feature&quot;&gt;
  &lt;iframe style=&quot;width: 100%; height: auto; aspect-ratio: 16/9;&quot; src=&quot;https://player.vimeo.com/video/912728757?h=486c8974f8&quot; width=&quot;1280&quot; height=&quot;360&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; fullscreen; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
</content:encoded><category>motion</category><category>talks</category></item><item><title>Keeping a Project Log</title><link>https://nathan-long.com/blog/keeping-a-project-log/</link><guid isPermaLink="true">https://nathan-long.com/blog/keeping-a-project-log/</guid><description>What does a web developer log look like?</description><pubDate>Mon, 04 Mar 2024 17:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Captain’s Log, Stardate 41503.7. We have entered the Neutral Zone where a Talarian freighter has been severely damaged in a battle. I have sent an away team to investigate.&lt;/p&gt;
&lt;p&gt;— Captain Jean Luc Picard (S1E20: Heart of Glory)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I grew up watching Star Trek (specifically reruns of The Next Generation) and listening to Captain Jean Luc Picard (the BEST captain, &lt;code&gt;fight me&lt;/code&gt;…) record the events of their explorations and adventures.&lt;/p&gt;
&lt;p&gt;I used to wonder: who would listen to these logs? Were they for the captain’s benefit? Was some poor schmoe’s job at Starfleet to listen to long expositions on events? The internet tells me that the ship’s logs were (are?) used to document speed, location, who was onboard, significant events, or anything else regarding the ship’s operation. If something goes wrong, the log can help piece together the events leading up to the event.&lt;/p&gt;
&lt;p&gt;But captains aren’t the only folks to keep logs.&lt;/p&gt;
&lt;p&gt;Scientists keep highly detailed logs in their &lt;a href=&quot;https://en.wikipedia.org/wiki/Lab_notebook&quot;&gt;lab notebooks&lt;/a&gt;, documenting their hypotheses and experiment results so they can be repeated and breakthroughs can be proven (and protected in the case of intellectual property). Researchers in descriptive sciences keep &lt;a href=&quot;https://en.wikipedia.org/wiki/Fieldnotes&quot;&gt;field notes&lt;/a&gt; to record observations and evidence to help create an understanding of a phenomenon.&lt;/p&gt;
&lt;p&gt;As developers, should we keep logs? &lt;strong&gt;Short answer: Yes.&lt;/strong&gt; (And it’s not a new idea…) We should keep ‘project logs’.&lt;/p&gt;
&lt;p&gt;Great. Fantastic. I’m sure you’ll all get right on that and do fine. But if you’re like me, perhaps a little more structure into WHAT EXACTLY to log would be helpful.&lt;/p&gt;
&lt;h2 id=&quot;what-should-a-web-developers-log-look-like&quot;&gt;What should a web developer’s log look like?&lt;/h2&gt;
&lt;p&gt;We’re not scientists, nor are we space explorers, but as developers, we navigate through series of problems and solutions in projects. We plot a course at the beginning of a project and then observe how that approach fails or succeeds.&lt;/p&gt;
&lt;p&gt;If you’re like me, this is all very timely information. Ask me again in 3 months and I will struggle to recall all but the key details of what happened.&lt;/p&gt;
&lt;p&gt;A web developer project log should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be private, not publicly accessible.&lt;/li&gt;
&lt;li&gt;Be organized by project.&lt;/li&gt;
&lt;li&gt;Record goals and possible solutions upfront.&lt;/li&gt;
&lt;li&gt;Record any supporting links and materials (designs, workspaces, external notes, etc.)&lt;/li&gt;
&lt;li&gt;Contain a chronological log of quick updates on:
&lt;ul&gt;
&lt;li&gt;Decisions made, and the reasons for them.&lt;/li&gt;
&lt;li&gt;Events, either internal or external that affect the build process.&lt;/li&gt;
&lt;li&gt;Stray thoughts about future optimizations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Record final results and contrast them against the original goals and solutions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ll expand on each of these.&lt;/p&gt;
&lt;h3 id=&quot;keep-it-private&quot;&gt;Keep it Private&lt;/h3&gt;
&lt;p&gt;Some developers, especially game developers, keep a public devlog. They can range from things like &lt;a href=&quot;https://itch.io/devlogs&quot;&gt;patch notes&lt;/a&gt;, to something &lt;a href=&quot;https://www.gridsagegames.com/blog/&quot;&gt;with more behind-the-scenes thinking&lt;/a&gt;, to &lt;a href=&quot;https://www.youtube.com/watch?v=OU3B3D-52us&quot;&gt;lavishly produced videos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But game developers face different problems than we do as web developers. They have a game to sell in a saturated market. If they’re a small team or indie dev, they may not have folks devoted to evangelizing their game to their audiences. A public devlog increases accountability and gets existing users excited about upcoming updates. It can expose balance notes that might change the &lt;a href=&quot;https://en.wikipedia.org/wiki/Metagame&quot;&gt;game meta&lt;/a&gt;. It can serve as a marketing tool. Because of these uses, a game devlog exists more for the USERS than for the DEVELOPER.&lt;/p&gt;
&lt;p&gt;As web developers, we often sell our services by contract — we don’t need the same type of audience-building efforts. In fact, we might even breach contract if we share technical details publicly. So if you keep a log, it will be for YOUR benefit.&lt;/p&gt;
&lt;h3 id=&quot;organized-by-project&quot;&gt;Organized by Project&lt;/h3&gt;
&lt;p&gt;I want to make a distinction between a log and a journal. A journal is incredibly useful for stream-of-consciousness observations organized by day. However, using this approach for a project spreads information across a series of time-bound entries. If you’re working on multiple projects simultaneously it can be difficult to gather that information back up in a usable way. You can tag it, sort it, segment it, or whatever, but when you go looking for you’ll also be perusing entries that will cover a variety of topics in addition to the one you’re looking for.&lt;/p&gt;
&lt;p&gt;For maximum benefit, we’ll be using these logs both during and after the build. For that reason, we’ll make it easier for our future selves to organize these logs by project.&lt;/p&gt;
&lt;h3 id=&quot;record-goals-and-approach&quot;&gt;Record Goals and Approach&lt;/h3&gt;
&lt;p&gt;This is one of the most important parts of a developer project log; it’s the equivalent of our hypotheses. At the beginning of any project, the problem is explored and an approach is planned to solve it. But projects are not static, goals can change, and wrinkles are introduced by hidden requirements or obstacles that weren’t known at the outset. It’s much harder to see how far things have drifted if you don’t record them first.&lt;/p&gt;
&lt;p&gt;A project manager may record parts of this in shared documentation, but this also includes YOUR goals for the project – things only you can know. This project log exists for your improvement so it’s up to you to record these things yourself. PMs will move on, and you may not be paired again on the next project, so this is for you to develop a consistent practice in your approach.&lt;/p&gt;
&lt;h3 id=&quot;record-supporting-links-and-materials&quot;&gt;Record supporting links and materials&lt;/h3&gt;
&lt;p&gt;If you put something out of sight, you won’t use it. Part of increasing the use of this project log is to make it a handy dashboard. Stuff it full of the things you constantly have to look up: design files, meeting notes, links to specific tickets. Whatever you need. If you’ve recorded it, it’s all right there.&lt;/p&gt;
&lt;h3 id=&quot;chronologically-record-process&quot;&gt;Chronologically record process&lt;/h3&gt;
&lt;p&gt;Every so often, at a cadence that makes sense to you, record progress on the build. What’s happening? Did the client request something different? Was a problem found in the approach? Document decisions and events. Anything that influences the build should find its way in here. Give it a dated entry so you can piece together what happened when.&lt;/p&gt;
&lt;h3 id=&quot;contrast-results-and-goals&quot;&gt;Contrast results and goals&lt;/h3&gt;
&lt;p&gt;We built up our initial approach, we logged the changes and the path we wound up taking, and now, in the end, we can look back and see the difference between our start and end.&lt;/p&gt;
&lt;p&gt;We can use this to figure out what worked and what didn’t. Was there a particular event that helped or hindered the work? Did your collaboration with a teammate cause a new solution to present itself?&lt;/p&gt;
&lt;p&gt;By logging and recognizing these things, we can work to reinforce the good and avoid repeating the bad. If your team runs retrospectives, your log will give you the ammunition to propose changes, highlight difficulties, or give genuine praise to your teammates.&lt;/p&gt;
&lt;p&gt;It can also give you a moment to be proud of what you’ve accomplished. Did you come up with a new technique? Did the project unlock a mental model of something? Use these things to fill out your &lt;a href=&quot;https://jvns.ca/blog/brag-documents/&quot;&gt;Brag Sheet&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;format&quot;&gt;Format&lt;/h2&gt;
&lt;p&gt;This is the least important part of the log, and the easiest one to get trapped in. The tool must facilitate the work, not dominate your time. Several tools fit the bill:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tiddlywiki.com/&quot;&gt;TiddlyWiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.so/&quot;&gt;Notion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;… heck, even just some scripts and plaintext.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It just needs to be easy to call up and edit. I personally use TiddlyWiki as part of a larger personal wiki. I love being able to easily extend and format it with HTML/CSS/JS and all the data is stored in plaintext files (either as &lt;code&gt;.tid&lt;/code&gt; with WikiText syntax or as &lt;code&gt;.md&lt;/code&gt; with Markdown syntax).&lt;/p&gt;
&lt;p&gt;But pick something that works for you.&lt;/p&gt;
&lt;h2 id=&quot;warp-factor-two-engage&quot;&gt;Warp Factor Two… Engage!&lt;/h2&gt;
&lt;p&gt;There. A little structure, a little advice, and now we have a tool to help meaningfully progress our technical chops one project at a time.&lt;/p&gt;
</content:encoded><category>thoughts</category><category>process</category></item><item><title>Custom Class Regex for Tailwind IntelliSense in Neovim</title><link>https://nathan-long.com/blog/class-regex-for-tailwind-intellisense/</link><guid isPermaLink="true">https://nathan-long.com/blog/class-regex-for-tailwind-intellisense/</guid><description>For when your templating language expects something that looks nothing like HTML.</description><pubDate>Wed, 29 Nov 2023 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sometimes while using Tailwind, you need to place classes into things that aren’t structured like HTML elements. Take a look at this Twig snippet from a Craft site:&lt;/p&gt;
&lt;p&gt;{% raw %}&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;twig&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;{{ entry&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;primaryDescription &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; tag&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;div&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  class&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;text-body-lg text-rich&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  html&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; entry&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;primaryDescription&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}) }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;{% endraw %}&lt;/p&gt;
&lt;p&gt;This checks for the existence of a field and wraps it in a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; tag… But that little bit there as &lt;code&gt;class:&lt;/code&gt;… If you’re running Tailwind in Neovim like me, it doesn’t recognize that as a trigger for autocomplete because it wants that sweet, sweet HTML &lt;code&gt;class=&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;And without IntelliSense/autocomplete you’re missing a critical piece of the Tailwind authoring experience. You NEED autocomplete when working with Tailwind.&lt;/p&gt;
&lt;p&gt;The fine folks working on Tailwind realized this problem and have exposed some settings to allow custom triggers for whatever wacky format your templating language wants. In fact, they’ve exposed two methods for us:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-intellisense#tailwindcssclassattributes&quot;&gt;class attribute&lt;/a&gt; setting.&lt;/li&gt;
&lt;li&gt;The experimental &lt;a href=&quot;https://www.paolotiu.com/blog/get-tailwind-intellisense-anywhere&quot;&gt;class regex setting&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We’re looking at the second one. Most guides will tell you to plop some JSON in your VSCode settings and it’s not immediately clear where the equivalent of this is for Neovim. But hey, you’re here now, so I’ll show you. You stuff it into your LSP initialization settings for Tailwind, like this:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; lspconfig &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;lspconfig&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; capabilities &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;cmp_nvim_lsp&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;default_capabilities&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; on_attach &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt; --this is a custom function that sets several options&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- The initialization of TailwindCSS LSP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;lspconfig.tailwindcss.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;setup&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  on_attach &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; on_attach,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  capabilities &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; capabilities,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  settings &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    tailwindCSS &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      experimental &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        classRegex &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;          &quot;(?:class: ?)(?:&apos;|\&quot;|`)([^\&quot;&apos;`]*)(?:&apos;|\&quot;|`)&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Twig, looks for string preceded by &apos;class:&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This setup assumes a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You’re using &lt;a href=&quot;https://github.com/neovim/nvim-lspconfig&quot;&gt;nvim-lspconfig&lt;/a&gt; to connect to your LSPs.&lt;/li&gt;
&lt;li&gt;You’re using &lt;a href=&quot;https://github.com/hrsh7th/nvim-cmp&quot;&gt;nvim-cmp&lt;/a&gt; for autocomplete.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can check out my &lt;a href=&quot;https://nathan-long.com/blog/modern-javascript-tooling-in-neovim/&quot;&gt;earlier post&lt;/a&gt; on setting up LSP’s or look at my &lt;a href=&quot;https://github.com/nathanlong/dotfiles/blob/main/nvim/init.lua&quot;&gt;init.lua&lt;/a&gt; to see how I’ve set it up.&lt;/p&gt;
&lt;p&gt;The regex itself is looking for anything inside a string (either &lt;code&gt;&quot;&quot;&lt;/code&gt; or &lt;code&gt;&apos;&apos;&lt;/code&gt;) preceded by &lt;code&gt;class:&lt;/code&gt; and 0 to 1 spaces. Tailwind will now helpfully suggest classes once you kick open that string. If you need something different than what’s listed here you can check out &lt;a href=&quot;https://github.com/paolotiu/tailwind-intellisense-regex-list&quot;&gt;this repository of sample regexes you can use&lt;/a&gt; for different patterns.&lt;/p&gt;
&lt;p&gt;Happy autocompleting!&lt;/p&gt;
</content:encoded><category>neovim</category></item><item><title>Nathan Uses This: 2023 Edition</title><link>https://nathan-long.com/blog/nathan-uses-this-2023-edition/</link><guid isPermaLink="true">https://nathan-long.com/blog/nathan-uses-this-2023-edition/</guid><description>Some things I use</description><pubDate>Wed, 20 Sep 2023 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There’s been a &lt;a href=&quot;https://francescoimprota.com/2023/06/13/my-tech-stack/&quot;&gt;lot&lt;/a&gt; &lt;a href=&quot;https://chriscoyier.net/2023/09/14/tool-picks/&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;https://css-irl.info/cool-tools/&quot;&gt;folks&lt;/a&gt; &lt;a href=&quot;https://christianheilmann.com/2023/09/14/the-10-tools-i-install-on-every-new-mac-i-get/&quot;&gt;talking&lt;/a&gt; recently about the tools that they use so I thought this would be a good time pull a Marie Kondo on everything that’s rolling around in this machine to see if it still holds up to inspection. While several of these things won’t spark joy, they should at least spark VALUE.&lt;/p&gt;
&lt;h2 id=&quot;paper-planner-tasklife-management&quot;&gt;&lt;a href=&quot;https://www.amazon.com/Blue-Sky-2023-2024-Enterprise-144720/dp/B0BS4869YR/&quot;&gt;Paper Planner&lt;/a&gt;: Task/Life Management&lt;/h2&gt;
&lt;p&gt;Alright, stay with me. My first tool choice has nothing to do with computers, but everything to do with planning. I’ve tried so many different planning tools (Todoist, Wunderlist, Trello, Workflowy, Things, Habitica, Remember the Milk, and even building my own system) and none of them have worked for me.&lt;/p&gt;
&lt;p&gt;There’s too much input sprawl: emails, Slack, GitHub issues, JIRA issues, the one client that still insists on using BitBucket… And that’s just work. We also have school events, play dates, practices, trips — things that exist outside of the computer that ALSO need to be tracked.&lt;/p&gt;
&lt;p&gt;Maybe I’ll expand on this in the future, but my principle has become to manage the task at the source, but keep my goals, high-level items, and events in the planner.&lt;/p&gt;
&lt;h2 id=&quot;tiddlywiki-notes-writing-journal-build-logs&quot;&gt;&lt;a href=&quot;https://tiddlywiki.com/&quot;&gt;TiddlyWiki&lt;/a&gt;: Notes, Writing, Journal, Build Logs&lt;/h2&gt;
&lt;p&gt;Things that need to stick around longer than a week go in my personal wiki powered by TiddlyWiki. Everything gets stored as flat files for backup, version control, and storage and I run the node module in the background to stitch it all together in an interactive interface for me. It’s part &lt;a href=&quot;https://zettelkasten.de/posts/overview/#the-introduction-to-the-zettelkasten-method&quot;&gt;zettelkessen&lt;/a&gt;, part journal, part work dashboard. Because it’s web-based, it’s also easy to extend and style just how I  want it. It’s become my second brain, I’m not sure I could function without it anymore…&lt;/p&gt;
&lt;h2 id=&quot;wezterm-terminal&quot;&gt;&lt;a href=&quot;https://wezfurlong.org/wezterm/&quot;&gt;WezTerm&lt;/a&gt;: Terminal&lt;/h2&gt;
&lt;p&gt;Earlier this year I switched from iTerm2 to WezTerm, which says a lot because I spend ALLLLL day in the terminal. It’s fast, can be scripted with Lua (which is continuing the pattern of Lua-fication in my life), has an external config file I can take with me, and has a host of nice-to-haves like: baked-in support for my favorite code theme (&lt;a href=&quot;https://github.com/folke/tokyonight.nvim&quot;&gt;TokyoNight Storm&lt;/a&gt;) and my favorite code font (&lt;a href=&quot;https://www.jetbrains.com/lp/mono/&quot;&gt;JetBrains Mono&lt;/a&gt;) without the need of font-patching for those sweet, sweet icons.&lt;/p&gt;
&lt;h2 id=&quot;neovim-code-editor&quot;&gt;&lt;a href=&quot;https://neovim.io/&quot;&gt;NeoVim&lt;/a&gt;: Code Editor&lt;/h2&gt;
&lt;p&gt;I switched from Vim to NeoVim a few years ago and haven’t looked back. I was leery of Lua at first, but NeoVim introduced some killer features that made the learning curve worthwhile. LSP support topped the charts for me, and there are some fantastic plugins like &lt;a href=&quot;https://github.com/nvim-telescope/telescope.nvim&quot;&gt;Telescope&lt;/a&gt;, &lt;a href=&quot;https://github.com/nvim-treesitter/nvim-treesitter&quot;&gt;Treesitter&lt;/a&gt;, and others.&lt;/p&gt;
&lt;p&gt;One of the main things that bother me in Neovim right now is that some languages (I’m looking at YOU, &lt;a href=&quot;https://twig.symfony.com/&quot;&gt;Twig&lt;/a&gt;) have terrible indent support. The rise of Prettier has helped in certain cases, but embedded languages continue to be a problem.&lt;/p&gt;
&lt;h2 id=&quot;obs-visual-filters-for-remote-meetings&quot;&gt;&lt;a href=&quot;https://obsproject.com/&quot;&gt;OBS&lt;/a&gt;: Visual filters for remote meetings&lt;/h2&gt;
&lt;p&gt;Remember SnapCamera? Well, it’s not around anymore. The funny filters helped inject some humor into the suddenly 100% remote meetings during the pandemic. Now I run OBS and can use overlays, gifs—basically whatever I want as a video source for meetings. Is this overkill? Yes. Is it fun? Also yes.&lt;/p&gt;
&lt;h2 id=&quot;figma-and-the-affinity-suite-design&quot;&gt;&lt;a href=&quot;https://www.figma.com/&quot;&gt;Figma&lt;/a&gt; and the &lt;a href=&quot;https://affinity.serif.com/en-us/&quot;&gt;Affinity Suite&lt;/a&gt;: Design&lt;/h2&gt;
&lt;p&gt;When I first joined Viget in 2022 it was the first time I’d been without Adobe products since I was 15. It was equal parts shocking and liberating. Figma is one of the best interface design tools I’ve used. I fear for its future having been gobbled up by Adobe, but I’ll continue to use it until there’s a better option.&lt;/p&gt;
&lt;p&gt;Having started my career as a designer, I was used to being able to manipulate photos, documents, and layouts to do whatever I wanted. I have no desire to maintain a personal Adobe subscription so I turned to Affinity and their extremely reasonably priced suite of design tools. I’ve been happy with them and regularly reach for Designer for SVG work (Figma can be painful with complicated SVGs…)&lt;/p&gt;
&lt;h2 id=&quot;buttondown-email-newsletters&quot;&gt;&lt;a href=&quot;https://buttondown.email/&quot;&gt;Buttondown&lt;/a&gt;: Email newsletters&lt;/h2&gt;
&lt;p&gt;There’s a lot of choice when it comes to writing emails and maintaining a mailing list. I’ve used Mailchimp frequently for clients but I wasn’t keen on maintaining a personal subscription. I found Buttondown through &lt;a href=&quot;https://cassidoo.co/&quot;&gt;Cassidy Williams&lt;/a&gt;’s newsletter and really liked the interface and features. It was the Goldilocks fit for what I wanted in maintaining an email newsletter.&lt;/p&gt;
&lt;h2 id=&quot;feedly-rss-reader&quot;&gt;&lt;a href=&quot;https://feedly.com/&quot;&gt;Feedly&lt;/a&gt;: RSS reader&lt;/h2&gt;
&lt;p&gt;This is one I feel okay about but might change in the future. Feedly is the best of the free readers and does what I need it to right now which is following the 341 sites I’ve loaded in there. I have no real complaints about it other than it just doesn’t feel like it’s MINE. Some features I DO like are the bits of social proof that are scattered across the interface, such as loose counts of just how many people are following a source and what’s trending in different categories.&lt;/p&gt;
&lt;h2 id=&quot;just-universal-command-runner&quot;&gt;&lt;a href=&quot;https://github.com/casey/just&quot;&gt;Just&lt;/a&gt;: Universal command runner&lt;/h2&gt;
&lt;p&gt;I was introduced to this utility about a year ago and it quickly replaced the gnarled file of zsh aliases I kept on my local machine. I use it to simplify long project commands (without having to constantly look them up) and doing some basic command automation.&lt;/p&gt;
&lt;p&gt;But its most important feature is its ability to serve as a universal interface between different projects. I can create the same recipes to start up local dev from a Rails project that I can to a React, Elixir, SSG-flavor-of-the-day, or whatever. So rather than trying to remember how to start up the server, I can hit my custom &lt;code&gt;just run&lt;/code&gt; command, and BOOM—we’re up and running.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Welp, that’s a few of my most used tools. It’ll be interesting to check back in a year or so and see how these choices have played out.&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>Building Motion for the Web</title><link>https://nathan-long.com/blog/building-motion-for-the-web/</link><guid isPermaLink="true">https://nathan-long.com/blog/building-motion-for-the-web/</guid><description>Finding ways to improve motion process in building web interfaces.</description><pubDate>Fri, 14 Jul 2023 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve recently been noodling on motion and its use in web interfaces. I’ve been frustrated that most examples and material you find about motion fall into a few common categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Another rehash of Disney’s motion principles&lt;/li&gt;
&lt;li&gt;Explanations of easing and CSS transitions&lt;/li&gt;
&lt;li&gt;Imaginary app interactions built entirely in prototyping tools&lt;/li&gt;
&lt;li&gt;Wild and technically complex demos that can be inscrutable. (Have fun reading that 3D shader algorithm…)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It all seems to be very low-level, limited-scope implementations, or so high level you might as well be reading a Wikipedia article. Very rarely do you find material on how a team considered motion as part of their design ideation, how the designer and developers collaborated on bringing it to life, and how they convinced the stakeholders that the time spent was worth the effort…&lt;/p&gt;
&lt;p&gt;So I thought we should try to bridge that gap.&lt;/p&gt;
&lt;p&gt;What I’m most interested in is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creating &lt;strong&gt;shared language&lt;/strong&gt; around motion
&lt;ul&gt;
&lt;li&gt;(Have you tried having a conversation on how something should move?)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Empowering designers to be creative &lt;strong&gt;without&lt;/strong&gt; blowing the entire development budget.
&lt;ul&gt;
&lt;li&gt;(“Yes, all the things can move, but like THIS rather than like THAT…”)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create frameworks for &lt;strong&gt;talking to clients&lt;/strong&gt; about motion and setting &lt;strong&gt;expectations&lt;/strong&gt; around timing and cost.
&lt;ul&gt;
&lt;li&gt;(It never works if it’s just tacked on at the end of a project or has no money allocated to it.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing &lt;strong&gt;collaboration methods and deliverables&lt;/strong&gt; around motion to widen our process toolkit
&lt;ul&gt;
&lt;li&gt;(Spoiler: I don’t think high-fidelity motion prototypes are helpful to anyone but the client.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I feel like THESE are the social, financial, and collaboration barriers that have to be overcome for any successful technology integration, but we seem to currently lack these tools for motion on the web.&lt;/p&gt;
&lt;p&gt;We’ve started a series over at Viget that I’m excited about as we work to explore these topics. The first post is here: &lt;a href=&quot;https://www.viget.com/articles/building-motion-for-the-web-classifying/&quot;&gt;Building Motion for the Web: In the ‘Zone’&lt;/a&gt;. We explore classifications of motion characteristics in interfaces to start categorizing types of motion.&lt;/p&gt;
&lt;p&gt;Check it out, it’s only getting started…&lt;/p&gt;
</content:encoded><category>motion</category></item><item><title>Vite Rapid Prototyping Starter Kits</title><link>https://nathan-long.com/blog/vite-rapid-prototype-starter-kits/</link><guid isPermaLink="true">https://nathan-long.com/blog/vite-rapid-prototype-starter-kits/</guid><description>Creating quick one-line commands to get a local sandbox up and running.</description><pubDate>Fri, 16 Jun 2023 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the things I love about &lt;a href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt; is how FAST it is to spin up. When I had the idea for my  &lt;a href=&quot;https://nathan-long.com/sandclock/&quot;&gt;Sandclock&lt;/a&gt; toy app (which I’ll write about once I get a little further with it) I used the Vite React starter to quickly spin up a basic project. No configuration, no hunting for packages, just drop and go.&lt;/p&gt;
&lt;p&gt;This got me thinking about prototypes.&lt;/p&gt;
&lt;h2 id=&quot;prototypes&quot;&gt;Prototypes&lt;/h2&gt;
&lt;p&gt;Sometimes when building a user interface a concept comes up that’s a bit tricky to nail down. Our design tools are great at communicating static design but once you add motion or more complex functionality it can become harder to get collaborate.&lt;/p&gt;
&lt;p&gt;This is where I feel prototypes become valuable—getting buy-in on a difficult to explain concept. Splitting the concept from the codebase gives some freedom to get messy and iterate without causing a PR nesting tangle or a bunch of churn commits.&lt;/p&gt;
&lt;p&gt;I LOVE code playgrounds like &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;. They excel at sharing ideas, illustrating concepts, and exposing techniques — but as a rapid prototyping tool there’s some friction between the playground and your final code. Especially if you’re looking to port your code afterwards into a specific stack.&lt;/p&gt;
&lt;p&gt;Vite helps here in quickly spinning up a local dev environment with little to no config. You get your normal code editor and can plug in any stack you need. If you use one of their starter kits, it’s just a one line script to get a full local playground up and running. Vite has the bases covered pretty well in terms of JS frameworks (Vue, React, Svelte, and more) — BUT there are a few patterns we use at Viget that aren’t as widely covered like &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt; + &lt;a href=&quot;https://stimulus.hotwired.dev/&quot;&gt;Stimulus&lt;/a&gt; and Vanilla JS with &lt;a href=&quot;https://www.viget.com/articles/how-does-viget-javascript/&quot;&gt;Viget Modules&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;degit-ize-your-starter-kit&quot;&gt;Degit-ize your Starter Kit&lt;/h3&gt;
&lt;p&gt;In the Vite docs, they surface &lt;a href=&quot;https://github.com/Rich-Harris/degit&quot;&gt;degit&lt;/a&gt; (by Rich Harris) as a way to expand beyond the official Vite starters. It’s a command-line utility that nabs clean copies of git repos but leaves all the history behind. We’re looking forward, not backwards! It’s a nice little utility that opens up the field for one-line installs for UNOFFICIAL starter kits… like these next two.&lt;/p&gt;
&lt;h2 id=&quot;tailwind-and-stimulus&quot;&gt;Tailwind and Stimulus&lt;/h2&gt;
&lt;p&gt;At &lt;a href=&quot;https://www.viget.com/&quot;&gt;Viget&lt;/a&gt;, we’re big fans of &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt; and &lt;a href=&quot;https://stimulus.hotwired.dev/&quot;&gt;Stimulus&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;tailwind&quot;&gt;Tailwind&lt;/h3&gt;
&lt;p&gt;Maybe one day I’ll write an apologetics-style post about the reasons Tailwind is great, but for now I’ll keep it short. Tailwind is massively composable, completely sidesteps any need for naming methodologies, and flattens out the selector specificity so it’ll scale long-term on a project without gathering cruft. It’s also portable in that you can write Tailwind in React, Vue, Svelte, but also Craft, Wordpress, custom Ruby apps, or any other system you’d like to use. That means as a team it’s a common language that can we can spread on top of any stack we’ll need to use.&lt;/p&gt;
&lt;p&gt;Tailwind is also well suited for prototypes. You can lift the HTML markup and classes and just port it into whatever you’re working on — and it ought to just work. Even if you have custom classes and patterns, those are portable enough between projects, and if you need to RELY on project-specific setup you can just grab the project’s &lt;code&gt;tailwind.config.js&lt;/code&gt; into your prototype from the beginning!&lt;/p&gt;
&lt;h3 id=&quot;stimulus&quot;&gt;Stimulus&lt;/h3&gt;
&lt;p&gt;Sometimes you don’t need that much JavaScript. Rather than attempting to render the interface, Stimulus allows you to ‘sprinkle’ your interface with interactivity that loads only when needed. Like Tailwind, it augments HTML markup as the source of truth and when the two are paired together you can really jam on something without much file switching!&lt;/p&gt;
&lt;h3 id=&quot;a-new-tailwindstimulus-starter&quot;&gt;A new Tailwind/Stimulus starter&lt;/h3&gt;
&lt;p&gt;Given these team staples, I set about writing a Vite starter only to find my coworker, &lt;a href=&quot;https://github.com/jeremyfrank&quot;&gt;Jeremy Frank&lt;/a&gt;, had already created one!&lt;/p&gt;
&lt;p&gt;Check it out &lt;a href=&quot;https://github.com/jeremyfrank/vite-tailwind-stimulus-starter&quot;&gt;on Github here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And with degit, you can one-line it with:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;degit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; jeremyfrank/vite-tailwind-stimulus-starter&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; my-new-project&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;vanilla-all-the-things&quot;&gt;Vanilla All the Things&lt;/h2&gt;
&lt;p&gt;OK, so this isn’t really a tech stack as it is buy-in into the platform itself. CSS and JS have come a long way in the last several years.&lt;/p&gt;
&lt;h3 id=&quot;css&quot;&gt;CSS&lt;/h3&gt;
&lt;p&gt;In CSS we have variables, grid, powerful selectors like &lt;code&gt;has()&lt;/code&gt; (ALMOST here) and &lt;code&gt;not()&lt;/code&gt;, and amazing features like &lt;code&gt;clamp()&lt;/code&gt; that remove many of the original reasons we used a pre-processor like SASS. And just look at these &lt;a href=&quot;https://developer.chrome.com/blog/whats-new-css-ui-2023/&quot;&gt;new things for CSS in 2023&lt;/a&gt; like this &lt;code&gt;popover&lt;/code&gt; &lt;a href=&quot;https://developer.chrome.com/blog/introducing-popover-api/&quot;&gt;demo from Una Kravets&lt;/a&gt;. How can you not be excited about all this?!&lt;/p&gt;
&lt;h3 id=&quot;javascript&quot;&gt;JavaScript&lt;/h3&gt;
&lt;p&gt;A few years ago the Viget team developed a pattern called &lt;a href=&quot;https://www.viget.com/articles/how-does-viget-javascript/&quot;&gt;Viget Modules&lt;/a&gt; that tap into &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import&quot;&gt;dynamic imports&lt;/a&gt; to load only the modules needed on a page. Don’t need it? Don’t load it!&lt;/p&gt;
&lt;p&gt;Originally, Webpack worked it’s magic to chunk and bundle everything, but now with native ESM support in browsers we can ship these independent modules WITHOUT a bundler. Which means this approach could work without a build system at all!&lt;/p&gt;
&lt;p&gt;While we’ve largely replaced Viget Modules with Stimulus controllers in production sites, I have found that when learning new API’s or techniques it is easier to clear away all the magic to better understand what’s actually going on in the browser.&lt;/p&gt;
&lt;p&gt;This also ensures that whatever you’re writing isn’t going to be subject to a third-party’s opinions of how things ought to be. It’s just you and the browser.&lt;/p&gt;
&lt;p&gt;You can check out my &lt;a href=&quot;https://github.com/nathanlong/vite-vanilla&quot;&gt;vanilla starter kit here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And with degit, you can one-line it with:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;degit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; nathanlong/vite-vanilla&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; my-new-project&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;extra-credit-prototype-junk-drawer&quot;&gt;Extra Credit: Prototype Junk Drawer&lt;/h2&gt;
&lt;p&gt;We’ve now got several options on how we want to roll our quick prototype kits, but what if we took it one step further?&lt;/p&gt;
&lt;p&gt;We could transform all these one-line starter commands with &lt;code&gt;degit&lt;/code&gt; into aliases, OR… &lt;a href=&quot;https://just.systems/man/en/&quot;&gt;we could use &lt;code&gt;just&lt;/code&gt;&lt;/a&gt; to pull together a prototype directory where we can make those one lines EVEN SHORTER.&lt;/p&gt;
&lt;p&gt;Check out this &lt;code&gt;justfile&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;{% raw %}&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# react prototype from the official vite docs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;react&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; target:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  @echo&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;Creating React prototype in {{target}}…&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  npm&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; vite@latest&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; {{target}}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; --template&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; react&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  cd&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; {{target}}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  @echo&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;Start dev with: npm run dev&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# stimulus prototype (custom starter)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;stimulus&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; target:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  @echo&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;Creating Stimulus prototype in {{target}}…&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  degit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; jeremyfrank/vite-tailwind-stimulus-starter&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; {{target}}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  cd&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; {{target}}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  @echo&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;Start dev with: npm run dev&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;# vanilla prototype (custom starter)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;vanilla&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; target:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  @echo&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;Creating Vanilla prototype in {{target}}…&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  degit&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; nathanlong/vite-vanilla&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; {{target}}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  cd&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; {{target}}&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; npm&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  @echo&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;Start dev with: npm run dev&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;{% endraw %}&lt;/p&gt;
&lt;p&gt;Now instead of even having to remember the repo paths you can plop into your &lt;del&gt;junk drawer&lt;/del&gt; prototyping directory and start a prototype with something like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;just&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; stimulus&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; my-awesome-prototype&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Happy prototyping!&lt;/p&gt;
</content:encoded><category>tooling</category></item><item><title>Setting up LSPs for Modern JavaScript Tooling in Neovim</title><link>https://nathan-long.com/blog/modern-javascript-tooling-in-neovim/</link><guid isPermaLink="true">https://nathan-long.com/blog/modern-javascript-tooling-in-neovim/</guid><description>Installing LSPs, snippets, and Intellisense-style autocompletion</description><pubDate>Thu, 23 Feb 2023 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s no secret that I love Vim. I’ve been using it since I started in the industry, but rather than talking about WHY I love it so much (that feels like a separate post) let’s talk about adding some key modern features of a code editor for writing JavaScript.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These features are going to require us to run with &lt;a href=&quot;https://neovim.io/&quot;&gt;Neovim&lt;/a&gt;, a relatively recent retooling of &lt;a href=&quot;https://www.vim.org/&quot;&gt;Vim&lt;/a&gt;, the modal editor that’s been in active development since the 90’s! &lt;small&gt;(Such sleek, much wow, very powerful… 🐕)&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We’re going to nab:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Intellisense-style code-completion&lt;/strong&gt; and hinting for:
&lt;ul&gt;
&lt;li&gt;JavaScript, HTML, CSS, ESLint, and JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Powerful language actions&lt;/strong&gt; and methods for displaying info and jumping around in your code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linters&lt;/strong&gt; that will &lt;del&gt;tell you how wrong you are&lt;/del&gt; help you write better code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auto code-formatting&lt;/strong&gt; with Prettier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What you’ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://neovim.io/&quot;&gt;Neovim&lt;/a&gt;&lt;/strong&gt; (for LSP support).&lt;/li&gt;
&lt;li&gt;A little bit of &lt;strong&gt;Lua&lt;/strong&gt; code (don’t worry, if I can parse it you can too).&lt;/li&gt;
&lt;li&gt;Some familiarity with installing utilities via the &lt;strong&gt;command line&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;lsps-you-magical-beasties&quot;&gt;LSP’s, you magical beasties…&lt;/h2&gt;
&lt;p&gt;A lot of the features we’re interested in come from the &lt;a href=&quot;https://microsoft.github.io/language-server-protocol/&quot;&gt;Language Server Protocol&lt;/a&gt; or LSP developed by Microsoft. They act as language-specific ‘brains’ that an editor (like Visual Studio Code) can communicate with for all sorts of handy language features.&lt;/p&gt;
&lt;p&gt;This is the tech behind &lt;strong&gt;Intellisense&lt;/strong&gt; completion, and it’s also supported in Neovim. We’re going to be nabbing these brains for ourselves with the help of NPM.&lt;/p&gt;
&lt;p&gt;There are 4 LSP’s that we’re interested in today:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/typescript&quot;&gt;TypeScript&lt;/a&gt; - Why TypeScript? Well, it has inside of it many language features that work for vanilla JavaScript, not JUST TypeScript.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/typescript-language-server&quot;&gt;typescript-language-server&lt;/a&gt; - works with TypeScript to provide those language features for JavaScript&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/vscode-langservers-extracted&quot;&gt;vscode-langservers-extracted&lt;/a&gt; - This is a combo bundle. The fine folks at Microsoft wrote LSPs for HTML, CSS, JSON, and added ESLint support while they were at it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/@tailwindcss/language-server&quot;&gt;@tailwindcss/language-server&lt;/a&gt; - (optional) We use &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt; quite a bit at &lt;a href=&quot;https://www.viget.com/&quot;&gt;Viget&lt;/a&gt;, and this gives us powerful autocompletion which is a MUST with Tailwind, but if this isn’t your cup of tea you can leave this one off.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’re going to install everything globally with this command:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; typescript-language-server&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; typescript&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; vscode-langservers-extracted&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string)&quot;&gt; @tailwindcss/language-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(I’m using &lt;code&gt;npm&lt;/code&gt; to install modules in this example, but you can use whatever package manager you prefer. The point is you just need these globally installed so you can access them from Neovim. These LSP’s are just a small sampling of what’s available, see the docs at &lt;a href=&quot;https://github.com/neovim/nvim-lspconfig&quot;&gt;nvim-lspconfig&lt;/a&gt; for ways to install and configure more LSPs)&lt;/p&gt;
&lt;p&gt;Now that we have these magical language 🧠’s available globally we need to connect them to Neovim.&lt;/p&gt;
&lt;h2 id=&quot;connecting-neovim&quot;&gt;Connecting Neovim&lt;/h2&gt;
&lt;p&gt;We’ll be installing Neovim plugins to help attach everything. The plugin manager I’m using here is &lt;a href=&quot;https://github.com/wbthomason/packer.nvim&quot;&gt;Packer&lt;/a&gt;, but these will work with any other plugin manager as well.&lt;/p&gt;
&lt;p&gt;We’re also going to be writing the setup and configurations of these plugins in Lua because most of them are written in Lua themselves. However, there’s no need to swap out your &lt;code&gt;init.vim&lt;/code&gt; (vimscript-based config equivalent of &lt;code&gt;.vimrc&lt;/code&gt; for Neovim) for &lt;code&gt;init.lua&lt;/code&gt; just yet. Neovim gives you the option to write Lua blocks inside of Vimscript like:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;lua &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; EOF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  -- all your lua config here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So if you’re rocking Vimscript, make sure to wrap the following setup in the Lua markers.&lt;/p&gt;
&lt;h3 id=&quot;install-the-plugins&quot;&gt;Install the plugins&lt;/h3&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;packer&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;startup&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;({&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  -- ...other plugins before and after...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  --&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  -- LSP integration and autocomplete&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;neovim/nvim-lspconfig&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;hrsh7th/nvim-cmp&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;hrsh7th/cmp-nvim-lsp&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;hrsh7th/cmp-nvim-lsp-signature-help&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;hrsh7th/cmp-buffer&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;hrsh7th/cmp-path&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  -- Prettier&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;  use&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;    &apos;prettier/vim-prettier&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    run &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;yarn install --frozen-lockfile --production&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    ft &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;javascript&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;typescript&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;css&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;scss&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;json&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;graphql&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;markdown&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;vue&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;yaml&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;html&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align: left&quot;&gt;Plugin&lt;/th&gt;
&lt;th style=&quot;text-align: left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://github.com/neovim/nvim-lspconfig&quot;&gt;nvim-lspconfig&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left&quot;&gt;Officially supported LSP quickstart configs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://github.com/hrsh7th/nvim-cmp&quot;&gt;nvim-cmp&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left&quot;&gt;The completion engine we’ll be using for everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://github.com/hrsh7th/cmp-nvim-lsp&quot;&gt;cmp-nvim-lsp&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left&quot;&gt;This is a ‘source’ plugin for our complete engine, &lt;code&gt;nvim-cmp&lt;/code&gt;. This allows it to use the LSPs (required)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://github.com/hrsh7th/cmp-nvim-lsp-signature-help&quot;&gt;cmp-nvim-lsp-signature-help&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left&quot;&gt;A helper source that will auto-hint at function arguments for us (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://github.com/hrsh7th/cmp-buffer&quot;&gt;cmp-buffer&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left&quot;&gt;While we’re at it, another source plugin that allows autocompletion from the buffer itself (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://github.com/hrsh7th/cmp-path&quot;&gt;cmp-path&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left&quot;&gt;Another source plugin to help complete file system paths (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left&quot;&gt;&lt;a href=&quot;https://github.com/prettier/vim-prettier&quot;&gt;Prettier&lt;/a&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: left&quot;&gt;Officially maintained auto-formatting utility for a variety of languages, what we’re doing here is actually only activating it for certain filetypes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;One note about Prettier:&lt;/strong&gt; You may have noticed we did not install Prettier globally. TYPICALLY you’ll be running Prettier from a local project installation, and this setup will hook into a project that has Prettier installed. But if you want to use Prettier outside of a project config, you can also install a version globally that you can use without modifying project dependencies. We’ll come back to Prettier a little later on.&lt;/p&gt;
&lt;h3 id=&quot;activate-the-plugins&quot;&gt;Activate the plugins&lt;/h3&gt;
&lt;p&gt;If you haven’t worked with Lua plugins before they differ from traditional Vim plugins in the fact that they’re not active by default, you have to call them (and optionally pass configuration options) before they’ll attach themselves to your session. This allows for you to selectively load plugins per filetype or any other fancy options you might need.&lt;/p&gt;
&lt;p&gt;Inside of your &lt;code&gt;init.vim&lt;/code&gt; or &lt;code&gt;init.lua&lt;/code&gt; add the following:&lt;/p&gt;
&lt;h4 id=&quot;lsp-config&quot;&gt;LSP Config&lt;/h4&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- LSP Mappings + Settings -----------------------------------------------------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- modified from: https://github.com/neovim/nvim-lspconfig#suggested-configuration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; opts &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; { noremap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, silent&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Basic diagnostic mappings, these will navigate to or display diagnostics&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;space&amp;gt;d&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.diagnostic.open_float, opts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;[d&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.diagnostic.goto_prev, opts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;]d&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.diagnostic.goto_next, opts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;space&amp;gt;q&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.diagnostic.setloclist, opts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Use an on_attach function to only map the following keys&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- after the language server attaches to the current buffer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; on_attach&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt;client&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-punctuation)&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt; bufnr&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  -- Enable completion triggered by &amp;lt;c-x&amp;gt;&amp;lt;c-o&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.api.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;nvim_buf_set_option&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(bufnr, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;omnifunc&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;v:lua.vim.lsp.omnifunc&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;  -- Mappings to magical LSP functions!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; bufopts &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; { noremap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, silent&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, buffer&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;bufnr }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;gD&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.declaration, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;gd&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.definition, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;gk&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.hover, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;gi&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.implementation, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;gK&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.signature_help, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;space&amp;gt;D&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.type_definition, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;space&amp;gt;rn&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.rename, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;space&amp;gt;ca&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.code_action, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;gr&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, vim.lsp.buf.references, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  vim.keymap.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;n&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;space&amp;gt;f&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;() vim.lsp.buf.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; { async &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, bufopts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- The nvim-cmp almost supports LSP&apos;s capabilities so You should advertise it to LSP servers..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; capabilities &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;cmp_nvim_lsp&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;default_capabilities&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Capabilities required for the visualstudio lsps (css, html, etc)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;capabilities.textDocument.completion.completionItem.snippetSupport &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Activate LSPs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- All LSPs in this list need to be manually installed via NPM/PNPM/whatevs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; lspconfig &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;lspconfig&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; servers &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;tailwindcss&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;tsserver&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;jsonls&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;eslint&apos; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; _, lsp &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;in&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; pairs&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(servers) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  lspconfig[lsp].&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;setup&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    on_attach &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; on_attach,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    capabilites &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; capabilities,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- This is an interesting one, for some reason these two LSPs (CSS/HTML) need to&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- be activated separately outside of the above loop. If someone can tell me why,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- send me a note...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;lspconfig.cssls.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;setup&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  on_attach &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; on_attach,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  capabilities &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; capabilities&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;lspconfig.html.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;setup&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  on_attach &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; on_attach,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  capabilities &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; capabilities&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Awesome, that’s the LSP setup. We now have LSP’s activated and attached – but they’re not going to autocompleting yet. We need to set up &lt;code&gt;nvim-cmp&lt;/code&gt;. But to do that we also need to specify a snippet engine.&lt;/p&gt;
&lt;h4 id=&quot;snippet-setup&quot;&gt;Snippet Setup&lt;/h4&gt;
&lt;p&gt;Now, you don’t NEED &lt;code&gt;LuaSnip&lt;/code&gt; specifically for &lt;code&gt;nvim-cmp&lt;/code&gt;, but you do need A snippet engine (&lt;code&gt;nvim-cmp&lt;/code&gt; requires one 🤷). I’ve chosen &lt;code&gt;LuaSnip&lt;/code&gt; for it’s ease of use and power, which I may cover in a future article. For now let’s set it up:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Luasnip ---------------------------------------------------------------------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Load as needed by filetype by the luasnippets folder in the config dir&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; luasnip &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;luasnip&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;luasnip.loaders.from_lua&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;lazy_load&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- set keybinds for both INSERT and VISUAL.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.api.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;nvim_set_keymap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;C-n&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;Plug&amp;gt;luasnip-next-choice&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, {})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.api.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;nvim_set_keymap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;C-n&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;Plug&amp;gt;luasnip-next-choice&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, {})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.api.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;nvim_set_keymap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;C-p&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;Plug&amp;gt;luasnip-prev-choice&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, {})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;vim.api.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;nvim_set_keymap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;C-p&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;Plug&amp;gt;luasnip-prev-choice&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, {})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Set this check up for nvim-cmp tab mapping&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; has_words_before&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; line, col &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; unpack&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(vim.api.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;nvim_win_get_cursor&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; col &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;~=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; vim.api.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;nvim_buf_get_lines&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, line &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, line, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)[&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;]:&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;sub&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(col, col):&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;match&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;%s&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;autocomplete-setup&quot;&gt;Autocomplete Setup&lt;/h4&gt;
&lt;p&gt;Almost done! Now we just need to activate &lt;code&gt;nvim-cmp&lt;/code&gt; and feed it both our LSP and other sources, and create some keymappings:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- CMP - Autocompletion --------------------------------------------------------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;local&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;cmp&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;setup&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  snippet &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;    expand&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;       require&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;luasnip&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;lsp_expand&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(args.body) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- For `luasnip` users.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    end&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  mapping &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;C-p&amp;gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.mapping.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;select_prev_item&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;C-n&amp;gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.mapping.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;select_next_item&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;C-d&amp;gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.mapping.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;scroll_docs&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;C-f&amp;gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.mapping.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;scroll_docs&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;C-Space&amp;gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.mapping.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;complete&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;C-e&amp;gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.mapping.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;close&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&apos;&amp;lt;CR&amp;gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.mapping.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;confirm&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      behavior &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.ConfirmBehavior.Replace,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;      select &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;Tab&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;mapping&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt;fallback&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;visible&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;select_next_item&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      elseif&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; luasnip.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;expand_or_jumpable&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        luasnip.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;expand_or_jump&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      elseif&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt; has_words_before&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;complete&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;        fallback&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    end&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;s&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    [&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;S-Tab&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;mapping&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-parameter)&quot;&gt;fallback&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;visible&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        cmp.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;select_prev_item&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      elseif&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; luasnip.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;jumpable&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;        luasnip.&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;jump&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-constant)&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;        fallback&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;      end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;    end&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;i&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;s&quot; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  sources &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    { name &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;nvim_lsp&apos; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    { name &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;nvim_lsp_signature_help&apos; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    { name &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;luasnip&apos; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    { name &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;buffer&apos; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;    { name &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-keyword)&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt; &apos;path&apos; &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Aaaaay, now we have LSP-assisted autocomplete working! 😎 We’ve set up some smart mappings specifically around &lt;code&gt;&amp;lt;Tab&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;S-Tab&amp;gt;&lt;/code&gt; to allow &lt;code&gt;nvim-cmp&lt;/code&gt; to quickly cycle through options, and we’ve fed all of our sources into the completion engine.&lt;/p&gt;
&lt;h3 id=&quot;prettier-setup&quot;&gt;Prettier Setup&lt;/h3&gt;
&lt;p&gt;And lastly, lets activate and make some keybindings for Prettier:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;code-toggle&quot; hidden&gt;Expand&lt;/button&gt;&lt;pre class=&quot;astro-code dayparts&quot; style=&quot;background-color:var(--shiki-background);color:var(--shiki-foreground); overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-comment)&quot;&gt;-- Prettier&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;keymap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;leader&amp;gt;re&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;Plug&amp;gt;(Prettier)&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, opts)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--shiki-token-function)&quot;&gt;keymap&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;&amp;lt;leader&amp;gt;re&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:var(--shiki-token-string-expression)&quot;&gt;&quot;:PrettierFragment&amp;lt;cr&amp;gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:var(--shiki-foreground)&quot;&gt;, opts)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If it’s more helpful to see everything all together, you can take a peek at &lt;a href=&quot;https://github.com/nathanlong/dotfiles/blob/main/nvim/init.lua&quot;&gt;my personal Neovim config&lt;/a&gt;, just know that it may deviate slightly from these instructions as I’m always tinkering.&lt;/p&gt;
&lt;h2 id=&quot;thats-great-but-what-can-i-do-with-these-things&quot;&gt;That’s great, but what can I DO with these things?&lt;/h2&gt;
&lt;p&gt;Each one of these things we just installed and connected will allow to some truly magical actions.&lt;/p&gt;
&lt;h3 id=&quot;language-powered-extensible-autocompletion-nvim-cmp--lsp&quot;&gt;Language-Powered Extensible autocompletion (nvim-cmp + LSP)&lt;/h3&gt;
&lt;p&gt;If you’ve seen someone use VSCode, you have seen this in action. Language keywords are automatically provided for you as you type, and more than that: it KNOWS your code base. If you’ve correctly formatted your code, the autocomplete popups will be able to tell you what types of arguments the function requires, grab methods and functions from imported code, and just overall better autocomplete.&lt;/p&gt;
&lt;p&gt;But YOU get to pick what goes in there. If you need something like Elixir and Erlang support, there are &lt;a href=&quot;https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#elixirls&quot;&gt;LSP’s for that&lt;/a&gt;. This system will allow you to grab whatever completion brains you want and plug them into your new system.&lt;/p&gt;
&lt;h3 id=&quot;super-language-powers&quot;&gt;Super Language Powers&lt;/h3&gt;
&lt;p&gt;There are a lot of powerful features hidden in the LSPs that are not obvious at first. We bound a lot of these commands to key mappings in our config files, here are a few of them explained:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Buffer Hover&lt;/strong&gt; = Display a tooltip for what your cursor is on. Helpful for things like seeing the expanded Tailwind CSS definitions or a bit of info about a piece of code. Very handy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer Implementation&lt;/strong&gt; = Open a quickfix window of everywhere in the current file what your cursor is over has been implemented (not the same as referenced, there’s a different command for that 😆).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer Definition&lt;/strong&gt; = Jumps to the definition of what your cursor is currently over. You can jump back to a variable or function definition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer Type Definition&lt;/strong&gt; = Jumps to the definition of the type you’re on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer Rename&lt;/strong&gt; = Rename what’s under your cursor everywhere. Incredibly handy!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signature Help&lt;/strong&gt; = Displays function signatures with the current parameter emphasized. Can’t remember what arguments a function takes? Activate signature help and it’ll tell you!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code Action&lt;/strong&gt; = This one is interesting. It’s context sensitive command that will give you a list of things that can be done for a diagnostic error and can actually fix an issue for you or provide recommendations!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer Reference&lt;/strong&gt; = Create a quickfix window for all references for what your cursor is on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Buffer Format&lt;/strong&gt; = This one can also be pretty magical. This command can do some basic clean up and formatting for you. Think of it as Prettier-Lite™️&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;linting-in-context&quot;&gt;Linting In Context&lt;/h3&gt;
&lt;p&gt;We didn’t address it directly, but part of the &lt;code&gt;vscode-langservers-extracted&lt;/code&gt; package included &lt;code&gt;eslint&lt;/code&gt;. Through the LSP integration, diagnostics will output directly alongside your JavaScript so you can see &lt;del&gt;when you’ve made a terrible mistake&lt;/del&gt; when your code could be improved. This setup also gives you an easy way to navigate through diagnostic errors (We bound them to &lt;code&gt;[d&lt;/code&gt; and &lt;code&gt;]d&lt;/code&gt; in the config files) and even have some quick LSP-assisted resolution through Code Actions.&lt;/p&gt;
&lt;h3 id=&quot;auto-formatting-prettier&quot;&gt;Auto-formatting (Prettier)&lt;/h3&gt;
&lt;p&gt;Alright, this is more subjective than the other benefits, but stick with me here for a 🔥 &lt;strong&gt;hot take&lt;/strong&gt; 🔥.&lt;/p&gt;
&lt;p&gt;While &lt;a href=&quot;https://prettier.io/&quot;&gt;Prettier&lt;/a&gt; isn’t perfect, it does have a valuable function: it completely removes arbitrary and subjective formatting differences when working on a team.&lt;/p&gt;
&lt;p&gt;Tabs/Spaces? (Foh-get a-bout eet) Spaces around argument options? (Naht to woo-ray, hun). Entire PR’s littered with whitespace adjustments? (Ne-vah agaihns). Write the code how you want to while you’re hacking away, then, when you’re done, have the formatter clean it up and snap it into alignment with team standards (collected in the &lt;code&gt;.pretterrc&lt;/code&gt;). It’s like a robot butler for your code, and while it may not put everything back where YOU would put it you can also know that it’s doing the same thing for everyone else. And that’s a lot harder to get stuck on.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The support of LSP’s is one of the main reasons I hopped onboard the Neovim train. After seeing how nice the JavaScript experience is in Visual Studio Code and the power of what LSP’s allow you to do with your code I was sold. When you’re styling a user interface in TailwindCSS or working in a Next.js app, having the lightning fast autocomplete along with quick hopping, output previews, and contextual linting… It turns Neovim into a rocket ship for writing modern JavaScript!&lt;/p&gt;
</content:encoded><category>neovim</category></item><item><title>Owning My Voice</title><link>https://nathan-long.com/blog/owning-my-voice/</link><guid isPermaLink="true">https://nathan-long.com/blog/owning-my-voice/</guid><description>2023 is the year of the rabbit... and personal websites.</description><pubDate>Tue, 31 Jan 2023 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Take a quick nostalgia trip with me…&lt;/p&gt;
&lt;p&gt;Back when I started my career in this industry, the web was my starry-eyed, fairy-winged teacher:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Molly_Holzschlag&quot;&gt;Molly Holzschlag&lt;/a&gt; helped me take my first baby-steps with HTML and CSS.&lt;/li&gt;
&lt;li&gt;I learned responsive web design from &lt;a href=&quot;https://ethanmarcotte.com/&quot;&gt;Ethan Marcotte&lt;/a&gt;’s &lt;a href=&quot;https://alistapart.com/article/responsive-web-design/&quot;&gt;articles&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://adactio.com/&quot;&gt;Jeremy Keith&lt;/a&gt; taught me the new things in &lt;a href=&quot;https://html5forwebdesigners.com/&quot;&gt;HTML5&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lukew.com/&quot;&gt;Luke Wroblewski&lt;/a&gt; taught me how to build ‘&lt;a href=&quot;https://www.lukew.com/presos/preso.asp?26&quot;&gt;mobile first&lt;/a&gt;’.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://rachelnabors.com/&quot;&gt;Rachel Nabors’s&lt;/a&gt; continued efforts in animation inspired me to also see what could be done in the browser.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chriscoyier.net/&quot;&gt;Chris Coyier&lt;/a&gt; schooled me on the deeper parts of CSS with &lt;a href=&quot;https://css-tricks.com/&quot;&gt;CSS Tricks&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At some point (perhaps when Google Reader was shut down in 2013?) everything started to shift to social media. Particularly Twitter. In many ways it felt like an evolution—the barrier to communication was dropping. I could @mention &lt;a href=&quot;https://www.paulirish.com/&quot;&gt;Paul Irish&lt;/a&gt; or &lt;a href=&quot;https://www.sarasoueidan.com/&quot;&gt;Sarah Soueidan&lt;/a&gt; and they might ACTUALLY RESPOND. 😵‍💫&lt;/p&gt;
&lt;h2 id=&quot;waking-up-in-the-dark-forest&quot;&gt;Waking up in the Dark Forest&lt;/h2&gt;
&lt;p&gt;But things slowly changed while I wasn’t looking. If the web was once this weird and &lt;a href=&quot;http://www.csszengarden.com/&quot;&gt;magical garden&lt;/a&gt; it slowly turned into what &lt;a href=&quot;https://maggieappleton.com/&quot;&gt;Maggie Appleton&lt;/a&gt; (and &lt;a href=&quot;https://onezero.medium.com/the-dark-forest-theory-of-the-internet-7dc3e68a7cb1&quot;&gt;others&lt;/a&gt;) now describe as the &lt;strong&gt;&lt;a href=&quot;https://maggieappleton.com/cozy-web&quot;&gt;Dark Forest&lt;/a&gt;&lt;/strong&gt;. It’s not safe there. The tech giants prey on you and your personal data. Engaging in open debate is meaningless at BEST. Trolls are lurking in the comment sections. Being yourself out THERE can be like inviting the worst of the internet onto your back porch.&lt;/p&gt;
&lt;p&gt;At some point I was no longer reading updates from people I knew or cared about but getting lost endless amounts of shallow-yet-mildly-amusing content or &lt;a href=&quot;https://en.wikipedia.org/wiki/Doomscrolling&quot;&gt;doomscrolling&lt;/a&gt; through emotionally incendiary material.&lt;/p&gt;
&lt;h2 id=&quot;are-we-human-or-are-we-robot&quot;&gt;Are we human? Or are we &lt;code&gt;robot?&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;I was no longer in control what I was ingesting. The robots were feeding me what they were trained to surface. &lt;code&gt;And I liked it.&lt;/code&gt; Well, at first anyways.&lt;/p&gt;
&lt;p&gt;Unfortunately it &lt;a href=&quot;https://maggieappleton.com/ai-dark-forest&quot;&gt;seems like it’s going to get worse&lt;/a&gt;. The Dark Forest is about to be overrun with bots through the brilliant yet terrifying engines of &lt;a href=&quot;https://openai.com/blog/chatgpt/&quot;&gt;ChatGPT&lt;/a&gt;, &lt;a href=&quot;https://www.midjourney.com/home/&quot;&gt;Midjourney&lt;/a&gt;, &lt;a href=&quot;https://openai.com/dall-e-2/&quot;&gt;DALL-E&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Stable_Diffusion&quot;&gt;Stable Diffusion&lt;/a&gt; and others. It’s going to be hard to tell what is made by real humans, and what is bot-generated in a flood of unoriginal content.&lt;/p&gt;
&lt;p&gt;Combine that with the &lt;a href=&quot;https://twitter.com/garius/status/1588115310124539904?s=46&amp;amp;t=OH02ZwWUxJOQs9Z6MTQCug&quot;&gt;trust thermocline&lt;/a&gt; in our industry giants and it’s not looking so nice out there right now.&lt;/p&gt;
&lt;h2 id=&quot;owning-my-voice&quot;&gt;Owning My Voice&lt;/h2&gt;
&lt;p&gt;They say that &lt;a href=&quot;https://matthiasott.com/notes/the-year-of-the-personal-website&quot;&gt;2023 is the year of personal website&lt;/a&gt;. &lt;a href=&quot;https://bringback.blog/&quot;&gt;There is a call&lt;/a&gt; for individuals to start owning their spaces and &lt;a href=&quot;https://www.theverge.com/23513418/bring-back-personal-blogging&quot;&gt;owning their voices again&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To me that was part of the magic of the web when I first entered the industry: unique voices with unique ideas in highly personalized spaces. That’s why I’m dusting off this website and being more intentional about using it to own my voice on the web and create connections.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Don’t do it like me. Do it like you.”&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;&lt;a href=&quot;https://matthiasott.com/articles/into-the-personal-website-verse&quot;&gt;Matthias Ott&lt;/a&gt; - User Experience Designer&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This blog is hosted on &lt;a href=&quot;https://pages.github.com/&quot;&gt;Github Pages&lt;/a&gt; and built with &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;11ty&lt;/a&gt; but there are so many other ways to create your space, even if you’re not a developer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ghost.org/&quot;&gt;Ghost CMS&lt;/a&gt; - slick, content focused interface&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webflow.com/&quot;&gt;Webflow&lt;/a&gt; - a no-code solution with a free starter plan&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hotglue.me/&quot;&gt;Hotglue.me&lt;/a&gt; - charmingly wild no-code platform&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mmm.page/&quot;&gt;mmm.page&lt;/a&gt; - drag and drop website builder&lt;/li&gt;
&lt;li&gt;Wordpress - in either &lt;a href=&quot;https://wordpress.org/&quot;&gt;self-hosted&lt;/a&gt; or &lt;a href=&quot;https://wordpress.com/&quot;&gt;ready-to-go&lt;/a&gt; form&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I plan to use this space to experiment, get &lt;code&gt;weird&lt;/code&gt;, actually work on my improving my meme-damaged writing skills, and highlight unique ideas from other folks who are also working to own their voices.&lt;/p&gt;
&lt;p&gt;I hope to see more doing the same, because THAT’S the web I fell in love with. Who knows, maybe they’ll even bring Google Reader back… &lt;code&gt;:troll:&lt;/code&gt;&lt;/p&gt;
</content:encoded><category>thoughts</category></item></channel></rss>