<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>T4T5</title><description>I build things for the web</description><link>https://t4t5.com/</link><item><title>Running DaVinci Resolve on a Framework 13 with Omarchy</title><link>https://t4t5.com/davinci-resolve-on-linux/</link><guid isPermaLink="true">https://t4t5.com/davinci-resolve-on-linux/</guid><description>My painful journey of getting a decent video editor to work on my Linux laptop.</description><pubDate>Tue, 20 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I decided to install the &lt;a href=&quot;https://www.blackmagicdesign.com/products/davinciresolve&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;DaVinci Resolve&lt;/a&gt; video editor on my Framework laptop (running Omarchy btw), little did I know that I was about to embark on a journey full of obscure errors and edge cases. Apparently running it on Linux, while technically possible, is often so hard that &lt;a href=&quot;https://www.youtube.com/watch?v=DYZ6Mj0bNKo&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;it has almost become a meme at this point&lt;/a&gt;. I was, however, dead set on not giving up, and after a lot of tweaking with some help from Claude Code, I eventually got everything working as expected!&lt;/p&gt;
&lt;p&gt;I’m posting what I did here to save others from going through the same headaches as I did.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: Use davincibox to run DaVinci Resolve in a containerised Fedora instance. Use ROCm and override the GPU environment variable so that it thinks that you have an AMD Radeon RX 7900 (&lt;code&gt;gfx1100&lt;/code&gt;).&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id=&quot;1-install-container-dependencies&quot;&gt;1. Install container dependencies&lt;/h3&gt;
&lt;p&gt;First we download all the packages that our Fedora containerisation will use.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pacman&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -S&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; podman&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; distrobox&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; lshw&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; crun&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-change-user-permissions&quot;&gt;2. Change user permissions&lt;/h3&gt;
&lt;p&gt;DaVinci Resolve needs additional user permissions to access the GPU’s rendering capabilities.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; usermod&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -aG&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; render,video&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $USER&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Log out and back in for the changes to take effect (or restart your computer). You can verify that the permissions are set correctly by running the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;groups&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# &amp;gt; &amp;#39;render&amp;#39; and &amp;#39;video&amp;#39; should show up in the output!&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-download-davinci-resolve&quot;&gt;3. Download DaVinci Resolve&lt;/h3&gt;
&lt;p&gt;Get the installer from the &lt;a href=&quot;https://www.blackmagicdesign.com/products/davinciresolve&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;official website&lt;/a&gt; (yes, you will need to fill in a registration form, but it’s fairly quick). You’ll get a zip containing a &lt;code&gt;.run&lt;/code&gt; file.&lt;/p&gt;
&lt;h3 id=&quot;4-set-up-davincibox&quot;&gt;4. Set up davincibox&lt;/h3&gt;
&lt;p&gt;DaVinci Resolve’s Linux installer is &lt;a href=&quot;https://www.blackmagicdesign.com/event/davinciresolvedownload&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;built specifically for RHEL-compatible distros&lt;/a&gt; - Blackmagic doesn’t officially support Arch Linux. To work around this, we use &lt;a href=&quot;https://github.com/zelikos/davincibox&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;davincibox&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Clone the davincibox source code and move the &lt;code&gt;.run&lt;/code&gt; file from the previous step into the same
directory so that we can run the setup script:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://github.com/zelikos/davincibox.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; davincibox&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;./setup.sh&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; DaVinci_Resolve_&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.run&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;5-install-rocm&quot;&gt;5. Install ROCm&lt;/h3&gt;
&lt;p&gt;Without access to your GPU, DaVinci Resolve would be far too slow even for basic video editing. ROCm provides GPU acceleration for AMD graphics cards, so we install it inside the container:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;distrobox&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; enter&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; davincibox&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /bin/bash&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:#6A737D&quot;&gt;# once you&amp;#39;re in the container, run:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; sudo dnf install rocm-opencl&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; exit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;6-override-the-gpu-version&quot;&gt;6. Override the GPU version&lt;/h3&gt;
&lt;p&gt;If you have a Framework with an AMD chip, you will most likely see this error if you try to open the
app:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://t4t5.com/images/blog/davinci/unsupported-gpu.png&quot; alt=&quot;GPU not supported&quot;/&gt;&lt;/p&gt;
&lt;p&gt;My Framework 13 has a Radeon 840M (gfx1152), which isn’t officially supported. However, you &lt;em&gt;can&lt;/em&gt; still make it work if you trick ROCm into thinking that you have a Radeon RX 7900 (gfx1100).&lt;/p&gt;
&lt;p&gt;To do this, edit the file &lt;code&gt;~/.local/share/applications/DaVinciResolve.desktop&lt;/code&gt; in your favourite text editor and change the &lt;code&gt;Exec&lt;/code&gt; line to:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Exec=distrobox-enter -n davincibox -- env HSA_OVERRIDE_GFX_VERSION=11.0.0 /usr/bin/run-davinci /opt/resolve/bin/resolve %u&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;7-fix-the-scaled-down-ui&quot;&gt;7. Fix the scaled-down UI&lt;/h3&gt;
&lt;p&gt;DaVinci Resolve should now technically open without any errors, but you’ll notice when you launch it that the UI will be almost unusably small.&lt;/p&gt;
&lt;p&gt;To fix this, navigate through the small UI and create a dummy project - this will reveal the menu bar for the app.&lt;/p&gt;
&lt;p&gt;In the upper-left corner, click “DaVinci Resolve” -&amp;gt; “Preferences”. Then click the “User” tab, go to “UI Settings” and set the “UI Display Scale” to 200%.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://t4t5.com/images/blog/davinci/ui-display-scale.png&quot; alt=&quot;UI Display Scale&quot;/&gt;&lt;/p&gt;
&lt;p&gt;Save and restart the app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Congratulations, you are now ready to edit videos on your Framework laptop! 🎉&lt;/strong&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id=&quot;some-final-notes&quot;&gt;Some final notes:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The free version of DaVinci Resolve on Linux doesn’t support H.264/H.265 videos (&lt;a href=&quot;https://www.reddit.com/r/davinciresolve/comments/y4ytkl/why_no_mp4h264_support_on_linux/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;for weird licensing reasons&lt;/a&gt;). You’ll need to convert H.264/H.265 videos to MJPEG or DNxHR encoding before importing them:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;ffmpeg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; input.mp4&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -c:v&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; mjpeg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -q:v&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 3&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -c:a&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pcm_s16le&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; output.mov&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;The Radeon 840M has limited compute units, so this setup is best suited for lightweight video editing only. If timeline playback seems slow, right-click clips -&amp;gt; “Generate Optimized Media” or enable Playback -&amp;gt; “Proxy Mode”.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>The return of plaintext productivity</title><link>https://t4t5.com/plaintext-productivity/</link><guid isPermaLink="true">https://t4t5.com/plaintext-productivity/</guid><description>What happens when everyone starts optimising their software for LLMs over humans?</description><pubDate>Thu, 08 Jan 2026 20:20:00 GMT</pubDate><content:encoded>&lt;p&gt;The premise of “plaintext productivity” is simple: most software today is overengineered, and the real secret to productivity is to return to radically simpler tools.
Once you switch from whatever SaaS-based todo app you’re currently using to &lt;a href=&quot;https://jeffhuang.com/productivity_text_file/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;a single long todo.txt file&lt;/a&gt;, you’ll avoid a million distractions and actually get shit done.&lt;/p&gt;
&lt;p&gt;I’ll admit that this concept always seemed slightly performative to me. The enthusiasm from people promoting it often seemed more rooted in ideology than actual productivity (“screw big tech, own your data!”)&lt;/p&gt;
&lt;p&gt;But recently I’ve changed my mind and become a religious &lt;em&gt;promoter of plaintext productivity&lt;/em&gt; myself. That’s because there’s a new, incredibly compelling reason to adopt this workflow:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LLMs &lt;em&gt;love&lt;/em&gt; plaintext files.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This might seem obvious to many, but I don’t think we’ve fully realised all the implications.&lt;/p&gt;
&lt;p&gt;CLI tools like Claude Code and OpenCode keep showing people that when their data is organised as simple plaintext files in a logical directory structure, they perform
&lt;em&gt;extremely&lt;/em&gt; well at almost any task.&lt;/p&gt;
&lt;p&gt;Some recent examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://x.com/skirano/status/2007540021536993712&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Feed raw DNA data into Claude to analyse your health&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rohunvora/just-fucking-cancel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Export credit card statements as CSVs to cancel all your subscriptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=uBJdwRPO1QE&amp;list=LL&amp;index=1&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Use Obsidian and Claude Code to build a personal OS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are a thousand other stories like this all pointing to the same pattern: the &lt;a href=&quot;https://stephango.com/file-over-app&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;“file over app”&lt;/a&gt; philosophy is suddenly paying massive dividends in the age of AI agents.&lt;/p&gt;
&lt;p&gt;If a workflow is hard to automate today, it’s almost certainly because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The state lives on someone else’s computer, or&lt;/li&gt;
&lt;li&gt;it uses an obscure or proprietary file format&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Sure, sometimes you can still get the data through authenticated API requests and MCP, but it’s still an order of magnitude more complicated, restrictive, and slower than simply letting an agent go wild with &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;ls&lt;/code&gt; in a folder with raw text files.&lt;/p&gt;
&lt;p&gt;My own computer habits are changing rapidly because of this. I used to be more of a visual person: macOS, polished app UIs, everything had to feel intuitive. Now the visual interface has become secondary. The main thing I care about is that my data is readable by Claude. I write as much as I can in markdown, and spend an increasing amount of time migrating data from all my apps to plaintext formats like CSV, just to see what an agent can do with it.&lt;/p&gt;
&lt;p&gt;What happens when everyone starts optimising their software for LLMs over humans?&lt;/p&gt;
&lt;p&gt;My hunch is that we’ll all want to make our data a lot simpler, more readable, and more portable.&lt;/p&gt;
&lt;p&gt;Turns out the plaintext productivity guys were right all along.&lt;/p&gt;</content:encoded></item><item><title>The Bitcoin Halving will be reorged</title><link>https://t4t5.com/the-bitcoin-halving-will-be-reorged/</link><guid isPermaLink="true">https://t4t5.com/the-bitcoin-halving-will-be-reorged/</guid><description>Why block 840,000 will be the most valuable block in Bitcoin’s history and could lead to the mother of all reorgs.</description><pubDate>Fri, 26 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every four years (or every 210,000 blocks to be precise) Bitcoin’s issuance per block gets cut in half. So far, we’ve never really placed much importance on who actually mines the block that triggers the next halving cycle, we just have a short moment of celebration and then move on.&lt;/p&gt;
&lt;p&gt;But this year’s Bitcoin halving is not like other halvings. Block 840,000 will likely have the most valuable block reward in Bitcoin’s history by far.&lt;/p&gt;
&lt;p&gt;This is all because of one man—Casey Rodamor, the creator or the Ordinals protocol. He has almost single-handedly created the biggest MEV opportunity ever in Bitcoin, and it will be impossible for miners to resist.&lt;/p&gt;
&lt;p&gt;To see this more clearly, let’s do a quick analysis to estimate what the value of block 840,000 is.&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;the-epic-sat&quot;&gt;The Epic Sat&lt;/h2&gt;
&lt;p&gt;At the core of Casey’s &lt;a href=&quot;https://docs.ordinals.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ordinal theory&lt;/a&gt; is the concept of assigning serial numbers to individual satoshis and tracking them over time.&lt;/p&gt;
&lt;p&gt;Once satoshis become non-fungible, it naturally follows that some sats will be considered more valuable than others, maybe because its number is aesthetically pleasing, or because it was involved in a transaction of historic importance.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/halving/satributes.png&quot; width=&quot;600&quot;/&gt;
&lt;em&gt;A list of popular traits (or “satributes”) that an individual satoshi can have. From ord.io&lt;/em&gt;
&lt;p&gt;One of the most popular ways of categorising sats today is using the &lt;a href=&quot;https://docs.ordinals.com/overview.html#rarity&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Rodarmor Rarity&lt;/em&gt;&lt;/a&gt; system, which tracks the events triggered by Bitcoin’s protocol rules (i.e. new blocks, new difficulty adjustment periods, new halving periods) and assigns extra importance to the first sat that gets created in each one.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/halving/rodarmor-rarity.png&quot; width=&quot;700&quot;/&gt;
&lt;em&gt;The different levels of sat rarity, according to Casey Rodarmor&lt;/em&gt;
&lt;p&gt;While some consider this way of viewing sats nothing more than a frivolous hallucination, it’s impossible to deny that it has found its niche within a community of Bitcoin collectors and archeologists.&lt;/p&gt;
&lt;p&gt;The floor price of a single &lt;em&gt;Uncommon&lt;/em&gt; sat on MagicEden is currently $400 (0.01 BTC), and Sotheby’s &lt;a href=&quot;https://www.sothebys.com/en/buy/auction/2024/natively-digital-an-ordinals-curated-sale/rare-sat-1-543-080-000-000-000&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;recently sold a Rare sat for over $100,000 (2.71 BTC)&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;And yet, none of these previously sold sats will be nearly as valuable as the sat that is set to be mined in block 840,000 – the &lt;em&gt;Epic sat&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;An Epic sat is created once every halving, so the total supply currently stands at &lt;em&gt;just 3&lt;/em&gt;! As none have been sold yet, their market value is still a mystery. However, knowing that Rare sats are selling for $100k a pop, and that the supply of those is 100 times bigger than Epic sats (410 vs 3), we can safely say that the Epic sat will probably be valued at least 10 times higher than a Rare sat. That puts its price at around 1 million dollars (or roughly 25 BTC at the time of writing).&lt;/p&gt;
&lt;p&gt;Note that this price is what you could reasonably ask for if you decided to just sell it straight away. The smarter move would probably be to keep the sat and capitalise on the range of opportunities that it opens up, potentially generating a large income stream for years to come. &lt;a href=&quot;https://dune.com/dataalways/ordinals&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;As the number of inscriptions keeps increasing&lt;/a&gt;, the simplest and most obvious way to stand out from the noise is to inscribe your collection on a special sat.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/halving/pizza-ninjas.png&quot; width=&quot;700&quot;/&gt;
&lt;em&gt;The inscriptions in the Pizza Ninjas collection were all inscribed on “Pizza sats”, i.e. sats that were involved in the infamous 2010 Pizza transaction. The first inscription is on an Uncommon Pizza sat. The collection has a market cap of ~100 BTC.&lt;/em&gt;
&lt;p&gt;Remember – not a single inscription has been made on an Epic sat yet. Whoever makes the first one could easily create a whole collection &lt;a href=&quot;https://docs.ordinals.com/inscriptions/provenance.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;with that inscription as its parent&lt;/a&gt; in order for every piece to accrue value.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/halving/seize-ctrl.png&quot; width=&quot;700&quot;/&gt;
&lt;em&gt;“Seize Ctrl” made the first ever inscription on a Rare sat, and then turned it into a collection of 405 pieces. The collection now has a market cap of ~30 BTC.&lt;/em&gt;
&lt;p&gt;&lt;strong&gt;Estimated value of Epic sat in the Halving block: $1M+ (25 BTC)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So the existence of the Epic sat already makes this block extremely valuable. However, it pales in comparison to the next thing that block 840,000 will also contain.&lt;/p&gt;
&lt;h2 id=&quot;the-first-rune-token&quot;&gt;The First Rune Token&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://rodarmor.com/blog/runes/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Runes protocol&lt;/a&gt; is a new token standard by Casey Rodamor that’s set to launch as soon as block 840,000 kicks in. As one would expect, we’ve already started to see multiple teams on X market their upcoming token as “The First Rune”.&lt;/p&gt;
&lt;div class=&quot;flex gap-3 sm:flex-col&quot;&gt;&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/RuneX_Tech/status/1749381493745418289?ref_src=twsrc%5Etfw&quot;&gt;January 22, 2024&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;a href=&quot;https://twitter.com/rune_coin/status/1749841536630575123?ref_src=twsrc%5Etfw&quot;&gt;January 23, 2024&lt;/a&gt;&lt;/blockquote&gt;&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;&lt;/div&gt;
&lt;p&gt;The truth of course is that we won’t know for sure who gets to claim that title until the block is actually mined. But why is being first even important?&lt;/p&gt;
&lt;p&gt;We can look at the BRC-20 ecosystem for a clue. The first ever BRC-20 token, &lt;a href=&quot;https://ordiscan.com/inscription/348020&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ORDI&lt;/a&gt;, was created by &lt;a href=&quot;https://twitter.com/domodata&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;domo&lt;/a&gt; in March 2023. The token has no utility or special attributes other than being the first of its kind, and yet it is still the most valuable BRC-20 token today, with a market cap of over 1 billion dollars.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/halving/brc-20.png&quot; width=&quot;1200&quot;/&gt;
&lt;em&gt;Top BRC-20 tokens by market cap. From coingecko.com&lt;/em&gt;
&lt;p&gt;By pretty much any standard, Runes is a much better designed protocol for fungible tokens than BRC-20 is, with longer potential staying power. A cautious projection suggests that the first Rune token, even if it has a ridiculous name and no utility whatsoever, will quickly amass a market cap of $1B+.&lt;/p&gt;
&lt;p&gt;One very important difference between Runes and BRC-20 is that Runes don’t necessarily have to have a fair launch where the initial supply is zero and anyone can mint them. Teams that launch a Rune token have the ability to allocate (i.e. premine) it to themselves in the &lt;a href=&quot;https://github.com/ordinals/ord/blob/master/src/index/updater/rune_updater.rs#L85&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;etching transaction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This by itself makes it insanely attractive to be the team that launches the first Rune token. We don’t know what the premined team allocation for that first token will be (maybe there won’t even be one!), but it would seem like a huge wasted opportunity not to allocate at least &lt;em&gt;a little bit&lt;/em&gt; to the creators, in order to secure their financial future forever.&lt;/p&gt;
&lt;p&gt;In the world of smart contract platforms like Ethereum and Solana, it’s very common for the founders’ token allocation to be around 25% of the total supply. Again, let’s be extremely conservative and estimate that &lt;em&gt;only 5%&lt;/em&gt; of the first Rune token’s supply is allocated to the team. At a 1 billion dollar valuation, that would still leave its value at a whopping 50 million dollars!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Estimated value of team allocation of first Rune token: $50M&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;so-what-will-happen&quot;&gt;So what will happen?&lt;/h2&gt;
&lt;p&gt;By conservative estimates, the data in block 840,000 is easily worth at least $50M dollars (or well over 1000 BTC). This is more than 200 times higher than the standard block reward, which at the time of writing is averaging ~6.5 BTC. In other words, block 840,000 is worth at least &lt;em&gt;2 orders of magnitude&lt;/em&gt; more than any other block before it.&lt;/p&gt;
&lt;p&gt;This has never happened before, and dangling that huge money pot in front of miners will make it extremely hard for them to play by the usual rules. So what can we reasonably expect to happen in April this year?&lt;/p&gt;
&lt;p&gt;Well, we know that mining pools like &lt;a href=&quot;https://mempool.space/block/0000000000000000000201fdbd732a2424f16272b6e718deab0139e8350943e1&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AntPool&lt;/a&gt;, &lt;a href=&quot;https://mempool.space/block/0000000000000000000124536bfca7b89af57a8c789d163b80e65a775c0303ad&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SECPool&lt;/a&gt; and &lt;a href=&quot;https://mempool.space/block/000000000000000000034d8965307fad41d843f424862fdcc9371a689dc60423&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;F2Pool&lt;/a&gt; have already started experimenting with Ordinal MEV by extracting rare sats from the blocks they mine.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/halving/rare-sat-extraction.jpeg&quot; width=&quot;1200&quot;/&gt;
&lt;em&gt;AntPool extracting a Rare sat. Source: mempool.space&lt;/em&gt;
&lt;p&gt;Similarly, the mining company Luxor made headlines last year by skipping the mempool and filling &lt;a href=&quot;https://mempool.space/block/0000000000000000000515e202c8ae73c8155fc472422d7593af87aa74f2cf3d&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;a whole block&lt;/a&gt; with a &lt;a href=&quot;https://www.coindesk.com/tech/2023/02/02/giant-bitcoin-taproot-wizard-nft-minted-in-collaboration-with-luxor-mining-pool/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;single inscription of a 4MB Taproot Wizard JPEG&lt;/a&gt;. Other pools like Terra Pool have since followed and turned the process of inscribing large files into a &lt;a href=&quot;https://financialpost.com/globe-newswire/dmg-announces-expanding-business-for-large-ordinal-inscriptions-petra-patent-grant&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;service they offer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When you think about it, the cost to privately buy a block from a miner is really just the cost to cover the sum of transaction fees that they’re missing out on, plus a small premium. Those transaction fees currently average around ~$10,000, which really isn’t much at all.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/halving/block-fees.png&quot; width=&quot;1200&quot;/&gt;
&lt;p&gt;The result is inevitable: block 840,000 will almost certainly not be a “normal” block where a miner just snaps up the highest paying transactions from the mempool. The prospect of a collaboration between a capital-rich team in the Ordinals space and a mining pool presents an overwhelmingly attractive opportunity. In fact, it would be foolish for the miner to leave that money on the table!&lt;/p&gt;
&lt;p&gt;Consider a hypothetical situation where a project proposes to pay a miner 5 million dollars for the exclusive right to design a block:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The miner gets 500 times their usual fee income with virtually no extra effort.&lt;/li&gt;
&lt;li&gt;Since the content of the block is arguably worth $50M+, the team that bribed them can easily capitalise on both the Rune and the Epic sat to earn at least 10 times the $5M that they spent, probably also with relatively little effort.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given this situation, the biggest mining pools are likely being approached as we speak with very lucrative deals in preparation for the halving.&lt;/p&gt;
&lt;p&gt;But what happens if two independent Ordinal teams strike deals with competing miners? This is the scenario where we’ll potentially see &lt;em&gt;the mother of all reorgs&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If the miner is paid $5M to mine the halving block, and the next few blocks only have rewards of ~$125,000 each, they should theoretically be okay with missing out on the revenue of ~40 blocks. In other words, even if one miner gets there first, and their version of block 840,000 already has multiple confirmations, there will still be strong incentives for another miner to keep placing their bet on building on top of &lt;em&gt;their&lt;/em&gt; version of block 840,000 instead. There is always that small chance that they find multiple blocks in a row and win the race.&lt;/p&gt;
&lt;p&gt;So it seems almost inevitable to me that we’ll see the mother of all reorgs on Bitcoin this year. While I don’t know exactly how it will play out, one thing we know is that it will certainly be interesting, and eventually the dust will settle and new blocks will keep coming in as normal.&lt;/p&gt;
&lt;p&gt;It also makes it clearer than ever why Bitcoin’s transaction fees need to go up over time. It’s currently too cheap for private parties to come in and bribe the operators of mining pools. If an NFT or token company can make miners dismiss a bunch of transaction in their favour, you can be sure that a state actor could do the same. The number of high-fee transactions in the mempool needs to go up to become more attractive to miners than private arrangements, or we risk eventually having a big problem.&lt;/p&gt;</content:encoded></item><item><title>How to connect your Umbrel node to Joule</title><link>https://t4t5.com/how-to-connect-your-umbrel-node-to-joule/</link><guid isPermaLink="true">https://t4t5.com/how-to-connect-your-umbrel-node-to-joule/</guid><description>With Umbrel, it&apos;s already much easier for average Bitcoin users to set up their own node and get started with Lightning. But connecting it to Joule takes this experience to the next level!</description><pubDate>Sat, 15 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ethereum has a popular browser extension called &lt;a href=&quot;https://metamask.io/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;MetaMask&lt;/a&gt; that makes it trivial for users to interact with DeFi apps from their browsers.&lt;/p&gt;
&lt;p&gt;Fewer people are aware of the fact that Bitcoin has a great equivalent for its Lightning apps called &lt;a href=&quot;https://lightningjoule.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Joule&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*FiZiUOkaHyFZ5jBBAMAu5g.png&quot; alt/&gt;
&lt;em&gt;The Joule website&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://getumbrel.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Umbrel&lt;/a&gt;, it’s already much easier for average Bitcoin users to set up their own node and get started with Lightning. Connecting it to Joule takes this experience even further and makes it a joy to interact with LApps in your browser!&lt;/p&gt;
&lt;p&gt;Curiously, there doesn’t seem to be any guides explaining how to use the two together (although there is a &lt;a href=&quot;https://github.com/getumbrel/umbrel-dashboard/pull/315&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;pull request&lt;/a&gt; aiming at make this pairing easier in the future).&lt;/p&gt;
&lt;p&gt;Until then, here’s a step-by-step guide on how to make it work!&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;This guide assumes that you already have an Umbrel node up and running. We will be using the Chrome browser in this guide, but the Joule extension also works with Firefox, Opera and Brave.&lt;/p&gt;
&lt;h2 id=&quot;step-1&quot;&gt;Step 1:&lt;/h2&gt;
&lt;p&gt;Go to the &lt;a href=&quot;https://lightningjoule.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Joule website&lt;/a&gt; and download the browser extension.&lt;/p&gt;
&lt;p&gt;If you’re using Google Chrome, you will be redirected to the Chrome web store, and you’ll have to click “Add to Chrome”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*vyT_UukUWG3_13O67AjZsw.png&quot; alt/&gt;&lt;/p&gt;
&lt;h2 id=&quot;step-2&quot;&gt;Step 2:&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*wdcI_vh-lB53eNi7FS4IbA.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;Open the newly added extension and click “Get started”.&lt;/p&gt;
&lt;p&gt;You will be redirect to a page asking you what kind of node you have. Select “Local node”.&lt;/p&gt;
&lt;p&gt;Joule will request additional permissions to read and change data on localhost. Click “Allow”.&lt;/p&gt;
&lt;h2 id=&quot;step-3&quot;&gt;Step 3:&lt;/h2&gt;
&lt;p&gt;You will now be redirected to a page where you need to type in the URL for your node (the prefilled default is &lt;a href=&quot;https://localhost:8080./&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;https://localhost:8080&lt;/em&gt;&lt;/a&gt;). Change the value to &lt;a href=&quot;https://umbrel.local:8080/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://umbrel.local:8080&lt;/a&gt; and click “Connect”:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*vZIOrctXxn2KO0Oc-uQxwg.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;After clicking “Allow” on the next Chrome prompt, you will probably still get an error message saying that it failed to connect to the node.&lt;/p&gt;
&lt;p&gt;In that case, click on “clicking this link” in the error message. A new tab will open with a Chrome security warning. Click on “Advanced” and ignore the certificate warning.&lt;/p&gt;
&lt;p&gt;You should now see an error message like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*KFxi6qle6RFxf07zWnN7kQ.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;This is actually a good sign. You can now go back to the original tab, click “Connect” once more and you will be able to proceed!&lt;/p&gt;
&lt;p&gt;You should now see a page asking you to upload your macaroon files. These are used to verify that Joule has the right permissions to perform actions on your Umbrel node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*msmGl3ucABpKT_ddsDLjDQ.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;Getting these files from Umbrel is slightly tricky; you will have to use the command line to download them through SSH.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*_1DaW3iY4eUGVLR_onhoiA.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;Open your computer’s terminal application.&lt;/p&gt;
&lt;p&gt;If you’re on Mac or Linux, there’s a built-in command line tool in your terminal called &lt;a href=&quot;https://en.wikipedia.org/wiki/Secure_copy_protocol&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SCP&lt;/a&gt; that makes this relatively easy.&lt;/p&gt;
&lt;p&gt;We’ll start with the &lt;em&gt;admin.macaroon&lt;/em&gt; file. Open the terminal app and run the following command to download the file from your Umbrel node to your &lt;em&gt;Downloads&lt;/em&gt; folder (assuming that you’re using a Mac).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;scp&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; umbrel@umbrel.local:/home/umbrel/umbrel/lnd/data/chain/bitcoin/mainnet/admin.macaroon&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/Downloads/admin.macaroon&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be asked for a password. This is the same password that you use to log in to your Umbrel node (the default is &lt;em&gt;moneyprintergobrrr&lt;/em&gt;). Note that the terminal will not give you any feedback when you type in your password (so it might look as if you’re not really typing), but don’t worry, just type it as usual and hit enter.&lt;/p&gt;
&lt;p&gt;After that, you should see a percentage progress while the file gets downloaded to your computer:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*x5NBOCaAyvQYe0LSxTSx5A.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*DbgDBZsQFD2ndkNg0bG3cw.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;There it is!&lt;/p&gt;
&lt;p&gt;Now that we have the file, let’s repeat the same process for the &lt;em&gt;readonly.macaroon&lt;/em&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#B392F0&quot;&gt;scp&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; umbrel@umbrel.local:/home/umbrel/umbrel/lnd/data/chain/bitcoin/mainnet/readonly.macaroon&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/Downloads/readonly.macaroon&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now have both files downloaded to your computer.&lt;/p&gt;
&lt;p&gt;Now head back to the extension and drag and drop both files to their placeholders and click “Continue”:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*4V1uII1s4KRdDWPd0cF8HQ.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;Boom! You should now see your node; all that’s left now is to confirm it. You will also be asked to create a new password to log in to your Joule account.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*cBfP2C-yXjeAfsjdN0B35w.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*0icDGhStPQJE-cdGyUpyMQ.png&quot; alt/&gt;&lt;/p&gt;
&lt;p&gt;That’s it, you are now ready to start using your Umbrel node through Joule! 🎉&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://miro.medium.com/v2/resize:fit:1400/1*EIIrWDT5RLQ85PTaZTG1_Q.png&quot; alt/&gt;&lt;/p&gt;</content:encoded></item><item><title>3 Things Web Developers Should Know When Building Electron Apps</title><link>https://t4t5.com/3-things-web-developers-should-know-when-building-electron-apps/</link><guid isPermaLink="true">https://t4t5.com/3-things-web-developers-should-know-when-building-electron-apps/</guid><description>Before I started working on Codespace, I had never really built anything in Electron. The only thing I knew was that apps like Slack and Notion were using it to create cross-platform desktop apps, and that everyone on Hacker News hated it because it consumes a lot of RAM. 🤷‍♀️</description><pubDate>Wed, 20 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Before I started working on &lt;a href=&quot;https://codespace.app&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Codespace&lt;/a&gt;, I had never really built anything in Electron. The only thing I knew was that apps like Slack and Notion were using it to create cross-platform desktop apps, and that everyone on Hacker News hated it because it consumes a lot of RAM. 🤷‍♀️&lt;/p&gt;
&lt;p&gt;Even though I’ve built apps on the web for 10 years, it was quite a journey discovering all the quirks of Electron. Here are 3 small things I learned that can hopefully help out someone who’s just getting started:&lt;/p&gt;
&lt;h2 id=&quot;1-quitting-logic-is-not-trivial&quot;&gt;1. Quitting logic is not trivial&lt;/h2&gt;
&lt;p&gt;Quitting an app is easy right? Surely there’s just some built-in behaviour that handles all of that for us? Wrong. Although you might not think about it as a user, when you start developing native apps, you realise that there’s often no “one-size-fits-all” for how and when apps should quit – it all depends on their use case.&lt;/p&gt;
&lt;p&gt;For starters, the &lt;em&gt;default behaviour&lt;/em&gt; is different based on what OS you’re using. On Windows and Linux, users have been taught that “closing the window also quits the app”. On macOS however, that is not the case: clicking the red traffic light button merely closes the window, but the app itself usually stays in the dock and keeps running in the background until you actively choose to quit it.&lt;/p&gt;
&lt;p&gt;The difference is also not just based on the user’s OS, it also varies across apps themselves. Most apps are happy with the default OS behaviour, but some applications like 1Password will quit as soon as you close the window for security reasons. More commonly, there are cases where you want to make it &lt;em&gt;appear&lt;/em&gt; as if the app has been quit, but in reality you’re simply &lt;em&gt;hiding the dock icon&lt;/em&gt; while the app keeps running in the background. This is typically the case for apps that appear in your menu bar as soon as you turn on your computer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://t4t5.com/images/blog/electron/chrome-quit.png&quot; alt=&quot;Quitting in Chrome&quot;/&gt;
&lt;em&gt;Some apps like Google Chrome go even further and ask you to hold down cmd + Q to quit.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So to sum it up – think about how and when your app should quit. To help you out, here are some snippets of logic that you can use in your own app:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#6A737D&quot;&gt;// Hide window instead of quitting when closing it:&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:#E1E4E8&quot;&gt;win &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BrowserWindow&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ width: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;500&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, height: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;500&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&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:#E1E4E8&quot;&gt;win.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#39;close&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  e.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;preventDefault&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Prevents quit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  win.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;hide&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#6A737D&quot;&gt;// Quit app on macOS ONLY if using command + Q or some other force quit option:&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:#E1E4E8&quot;&gt;win &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BrowserWindow&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ width: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;500&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, height: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;500&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&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:#F97583&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; forceQuit &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; false&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:#6A737D&quot;&gt;// Triggered before close event, i.e. when pressing command + Q&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;app.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#39;before-quit&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  forceQuit &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&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:#E1E4E8&quot;&gt;win.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#39;close&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;forceQuit &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; process.platform &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &amp;#39;darwin&amp;#39;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) { &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// &amp;quot;darwin&amp;quot; is macOS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    e.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;preventDefault&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Prevents quit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    win.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;hide&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&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:#6A737D&quot;&gt;  // Let the app quit normally...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;2-write-thrice-run-everywhere&quot;&gt;2. Write &lt;em&gt;thrice&lt;/em&gt;, run everywhere&lt;/h2&gt;
&lt;p&gt;One of the expectations developers might initially have when building apps with Electron is that you can happily hack around on your Mac, make sure it runs there, and then ship the code assuming that it will work just as well on Windows and Linux.&lt;/p&gt;
&lt;p&gt;Woah, not so fast, cowboy! Again, there are some core differences in how the OS works, and you definitely need to test your app extensively on all three platforms if you’re going to market it as a cross-platform app. We’ve already talked about quitting logic, but here are some other examples of often overlooked quirks that you need to think about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Short commands are different. Mac users are used to ”⌘ + &lt;em&gt;key&lt;/em&gt;” to trigger an action within an app, whereas Linux and Windows users use “CTRL + &lt;em&gt;key&lt;/em&gt;”.&lt;/li&gt;
&lt;li&gt;Scrollbar visibility. If you’re a web designer, you probably already know the pain of shipping a website that looks great on your Mac, only to find that, when you open it on Windows, there are these horrible-looking scrollbars everywhere. Electron apps are no different, so you will probably want to &lt;a href=&quot;https://css-tricks.com/the-current-state-of-styling-scrollbars/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;style your scrollbars&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Differences in layouts and menus. Most well-designed apps will use so-called “frameless” styling, to avoid having an ugly grey bar on top of their app’s window. This is trivial to achieve in Electron, using &lt;code&gt;BrowserWindow({ frame: false })&lt;/code&gt;, however, you’ll most likely want to tweak your layout slightly for each platform after setting it. Below is an example of how I had to add some top padding – but &lt;em&gt;only&lt;/em&gt; to the macOS version of Codespace – in order to take into account the area occupied by the traffic lights:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://t4t5.com/images/blog/electron/top-padding.png&quot; alt=&quot;Top padding on macOS&quot;/&gt;&lt;/p&gt;
&lt;p&gt;This is of course not everything, there are also differences when it comes to installation, auto-updates, emoji support… etc. In short, you &lt;em&gt;need&lt;/em&gt; to test your app on all three platforms. I personally found that paying for something like &lt;a href=&quot;https://www.parallels.com/products/desktop/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Parallels Desktop&lt;/a&gt; was probably the best investment I ever made while building my app. There are also free alternatives like &lt;a href=&quot;https://www.virtualbox.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;VirtualBox&lt;/a&gt;, but from what I understand, there’s a considerable difference in performance, which could affect your developer happiness.&lt;/p&gt;
&lt;h2 id=&quot;3-you-dont-need-the-app-store&quot;&gt;3. You don’t need the App Store&lt;/h2&gt;
&lt;p&gt;As web developers, we’re used to deploying our sites to a server and have them live instantly, but as we know, native apps work a bit differently. One of the dilemmas I faced when Codespace was getting close to launching was whether to use the Mac App Store or not. After all, it’s an extra distribution channel that might bring in some more customers, but I was also nervous about unwarranted app rejections and the &lt;a href=&quot;https://9to5mac.com/2019/11/04/electron-app-rejections/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;various stories of Electron apps being booted from the store&lt;/a&gt;. On top of that, 30% is a pretty hefty fee for what Apple is providing if you ask me.&lt;/p&gt;
&lt;p&gt;In the end, I decided to distribute the app myself, and I haven’t regretted it one bit. Sure, it’s a little bit of extra work setting up your own payment and licensing system, but if you’re going cross-platform, you will have to do that anyway. It also pushed me to take 100% control of the marketing of the app, and I discovered that spending some extra time creating a nice-looking landing page is more likely to yield great results than just uploading some screenshots to the store.&lt;/p&gt;
&lt;p&gt;In short, if your app is cross-platform, I don’t see any great benefit to using the App Store as your main distribution channel. Skipping it also frees you from going through a potentially long review process every time you push an update to your app. You will however still need to pay for an Apple Developer Account in order to sign and notarise your app, otherwise it won’t run on anyone else’s Mac.&lt;/p&gt;</content:encoded></item><item><title>Announcing SweetAlert 2.0 🎉</title><link>https://t4t5.com/announcing-sweetalert-2-0/</link><guid isPermaLink="true">https://t4t5.com/announcing-sweetalert-2-0/</guid><description>SweetAlert is a little side project that I released in 2014 in order to help developers implement dialogs that don’t suck in their web apps. When I first released it, I had no expectations whatsoever, and frankly did it mostly to spice up my boring GitHub profile.</description><pubDate>Thu, 07 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://sweetalert.js.org&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SweetAlert&lt;/a&gt; is a little side project that I released in 2014 in order to help developers implement dialogs that don’t suck in their web apps. When I first released it, I had no expectations whatsoever, and frankly did it mostly to spice up my boring GitHub profile.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://t4t5.com/images/blog/sweetalert/1-0.gif&quot; alt=&quot;SweetAlert 1.0 animation&quot;/&gt;
&lt;em&gt;The famous animation that caught a lot of people’s attention&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Three years and over 16 000 GitHub stars later, SweetAlert is no longer a little pet project, but has grown to become an indispensable tool for thousands of websites all around the world.&lt;/p&gt;
&lt;p&gt;Things have changed dramatically in the JavaScript ecosystem since its first release however. Bower and templating languages no longer rule the world, and ES2015/ES2016/ES2017 is becoming mainstream. A major revamp of the library is long overdue.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://t4t5.com/images/blog/sweetalert/logo.png&quot; alt=&quot;SweetAlert Logo&quot;/&gt;&lt;/p&gt;
&lt;p&gt;With SweetAlert 2.0, I hope that the library will continue to be useful for another 3 years. Not only has the library been rewritten from the ground up using &lt;a href=&quot;https://www.typescriptlang.org&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TypeScript&lt;/a&gt; and &lt;a href=&quot;https://github.com/postcss/postcss&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PostCSS&lt;/a&gt;, but it also comes with some awesome new functionality.&lt;/p&gt;
&lt;h2 id=&quot;so-whats-new&quot;&gt;So what’s new?&lt;/h2&gt;
&lt;p&gt;By far the most requested feature in the original library was the the ability to cutomise SweetAlerts with extra buttons and content. For this, I looked into the most common alerts used on major websites like Facebook and Pinterest for inspiration.&lt;/p&gt;
&lt;img src=&quot;https://t4t5.com/images/blog/sweetalert/alts.png&quot; class=&quot;wide&quot; alt=&quot;SweetAlert alternatives&quot;/&gt;
&lt;p&gt;Rather than bloating the standard library with lots of app-specific options, SweetAlert 2.0 can render all of these thanks to its new &lt;code&gt;content&lt;/code&gt; parameter, which lets you pass in a custom DOM node. This DOM node can be designed either from scratch in pure JavaScript, or with the help of one of your favourite rendering libraries, like React or Vue.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#6A737D&quot;&gt;// A React component...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; List&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;Item 1&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;Item 2&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&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:#6A737D&quot;&gt;// Render it as a DOM node...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; wrapper&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createElement&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;ReactDOM.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&amp;gt;, wrapper)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; listEl&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; wrapper.firstChild&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:#6A737D&quot;&gt;// And pass the DOM node to SweetAlert!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;swal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  text: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Here&amp;#39;s a list:&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  content: listEl,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SweetAlert 2.0 is also completely &lt;em&gt;promise-based&lt;/em&gt;. This means you can get some seriously cool flows, especially if you combine it with &lt;code&gt;async&lt;/code&gt;+&lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; deleteFile&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // Wait for the user to press a button...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; shouldDelete&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; swal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Delete file?&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Are you sure that you want to delete this file?&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;warning&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&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:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (shouldDelete) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // Code to actually delete file goes here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    swal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Poof!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Your file has been deleted!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;success&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Overall, the API has been revamped to be as simple as possible and gracefully upgradeable. Take a look at this example of how you can change the number of buttons in an alert:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word&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:#6A737D&quot;&gt;// No buttons:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;swal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  text: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;This modal has no buttons!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  buttons: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&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:#6A737D&quot;&gt;// 1 button:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;swal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  text: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Here’s a modal with one button!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  button: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Click me!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&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:#6A737D&quot;&gt;// 2 buttons:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;swal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  text: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Two buttons…&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  buttons: [&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Cancel&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Click me!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&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:#6A737D&quot;&gt;// 3 buttons:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;swal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  text: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Even three buttons, each with their own promise value!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  buttons: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    cancel: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Cancel&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    confirm: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Click me!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    hello: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;quot;Say hello!&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s a lot more stuff packed in, but rather than go through it all here, I suggest you head over to the &lt;a href=&quot;https://sweetalert.js.org&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;shiny new SweetAlert website&lt;/a&gt;.
A big thank you to &lt;a href=&quot;https://github.com/t4t5/sweetalert/graphs/contributors&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;everyone who’s helped contribute&lt;/a&gt; to the library! Here’s to the next three years! 🍻&lt;/p&gt;</content:encoded></item></channel></rss>